mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
User-defined literal suffixes.
This commit is contained in:
parent
3c0a7355d0
commit
216df46ff4
@ -378,6 +378,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
||||
|
||||
void ConstantEvaluator::endVisit(Literal const& _literal)
|
||||
{
|
||||
// TODO handle user suffix
|
||||
if (Type const* literalType = TypeProvider::forLiteral(_literal))
|
||||
m_values[&_literal] = constantToTypedValue(*literalType);
|
||||
}
|
||||
|
@ -141,6 +141,8 @@ bool FunctionCallGraphBuilder::visit(EmitStatement const& _emitStatement)
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO why don't we handle IdentifierPath here?
|
||||
|
||||
bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
|
||||
{
|
||||
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||
@ -230,6 +232,16 @@ bool FunctionCallGraphBuilder::visit(NewExpression const& _newExpression)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FunctionCallGraphBuilder::visit(Literal const& _literal)
|
||||
{
|
||||
if (auto const* identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||
functionReferenced(
|
||||
dynamic_cast<FunctionDefinition const&>(*(*identifierPath)->annotation().referencedDeclaration)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FunctionCallGraphBuilder::enqueueCallable(CallableDeclaration const& _callable)
|
||||
{
|
||||
if (!m_graph.edges.count(&_callable))
|
||||
|
@ -74,6 +74,7 @@ private:
|
||||
bool visit(MemberAccess const& _memberAccess) override;
|
||||
bool visit(ModifierInvocation const& _modifierInvocation) override;
|
||||
bool visit(NewExpression const& _newExpression) override;
|
||||
bool visit(Literal const& _literal) override;
|
||||
|
||||
void enqueueCallable(CallableDeclaration const& _callable);
|
||||
void processQueue();
|
||||
|
@ -3678,10 +3678,11 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
|
||||
|
||||
void TypeChecker::endVisit(Literal const& _literal)
|
||||
{
|
||||
Type const* type = nullptr;
|
||||
if (_literal.looksLikeAddress())
|
||||
{
|
||||
// Assign type here if it even looks like an address. This prevents double errors for invalid addresses
|
||||
_literal.annotation().type = TypeProvider::address();
|
||||
type = TypeProvider::address();
|
||||
|
||||
string msg;
|
||||
if (_literal.valueWithoutUnderscores().length() != 42) // "0x" + 40 hex digits
|
||||
@ -3707,7 +3708,8 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
);
|
||||
}
|
||||
|
||||
if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None)
|
||||
Literal::SubDenomination const* subDenomination = get_if<Literal::SubDenomination>(&_literal.suffix());
|
||||
if (_literal.isHexNumber() && subDenomination && *subDenomination != Literal::SubDenomination::None)
|
||||
m_errorReporter.fatalTypeError(
|
||||
5145_error,
|
||||
_literal.location(),
|
||||
@ -3715,20 +3717,83 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
"You can use an expression of the form \"0x1234 * 1 day\" instead."
|
||||
);
|
||||
|
||||
if (_literal.subDenomination() == Literal::SubDenomination::Year)
|
||||
if (subDenomination && *subDenomination == Literal::SubDenomination::Year)
|
||||
m_errorReporter.typeError(
|
||||
4820_error,
|
||||
_literal.location(),
|
||||
"Using \"years\" as a unit denomination is deprecated."
|
||||
);
|
||||
|
||||
if (!_literal.annotation().type)
|
||||
_literal.annotation().type = TypeProvider::forLiteral(_literal);
|
||||
if (!type)
|
||||
type = TypeProvider::forLiteral(_literal);
|
||||
|
||||
if (!_literal.annotation().type)
|
||||
if (!type)
|
||||
m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value.");
|
||||
|
||||
_literal.annotation().isPure = true;
|
||||
// TODO at this point 'type' needs to be stored for code generation.
|
||||
|
||||
bool isPure = true;
|
||||
if (auto const* identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||
{
|
||||
Declaration const* declaration = (*identifierPath)->annotation().referencedDeclaration;
|
||||
if (
|
||||
!dynamic_cast<FunctionDefinition const*>(declaration) ||
|
||||
!dynamic_cast<FunctionDefinition const*>(declaration)->isFree()
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
4438_error,
|
||||
_literal.location(),
|
||||
"The literal suffix needs to be a pre-defined suffix or a file-level function."
|
||||
);
|
||||
else
|
||||
{
|
||||
FunctionType const& functionType = dynamic_cast<FunctionType const&>(*declaration->type());
|
||||
if (
|
||||
dynamic_cast<RationalNumberType const*>(type) &&
|
||||
dynamic_cast<RationalNumberType const*>(type)->isFractional()
|
||||
)
|
||||
{
|
||||
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
|
||||
solAssert((mantissa && exponent) || (!mantissa && !exponent));
|
||||
if (!mantissa)
|
||||
m_errorReporter.typeError(
|
||||
5503_error,
|
||||
_literal.location(),
|
||||
"This fractional number cannot be decomposed into a mantissa and decimal exponent "
|
||||
"that fit the range of parameters of the suffix function."
|
||||
);
|
||||
else if (
|
||||
functionType.parameterTypes().size() != 2 ||
|
||||
!mantissa->isImplicitlyConvertibleTo(*functionType.parameterTypes().at(0)) ||
|
||||
!exponent->isImplicitlyConvertibleTo(*functionType.parameterTypes().at(1))
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
4778_error,
|
||||
_literal.location(),
|
||||
"TODO Fractional number, types to do match."
|
||||
);
|
||||
}
|
||||
else
|
||||
if (
|
||||
functionType.parameterTypes().size() != 1 ||
|
||||
!type->isImplicitlyConvertibleTo(*functionType.parameterTypes().front())
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
8838_error,
|
||||
_literal.location(),
|
||||
"The type of the literal cannot be converted to the parameter of the suffix function."
|
||||
);
|
||||
|
||||
isPure = functionType.isPure();
|
||||
if (functionType.returnParameterTypes().size() == 1)
|
||||
type = functionType.returnParameterTypes().front();
|
||||
else
|
||||
type = TypeProvider::tuple(functionType.returnParameterTypes());
|
||||
}
|
||||
}
|
||||
|
||||
_literal.annotation().isPure = isPure;
|
||||
_literal.annotation().type = type;
|
||||
_literal.annotation().isLValue = false;
|
||||
_literal.annotation().isConstant = false;
|
||||
}
|
||||
|
@ -949,8 +949,10 @@ bool Literal::isHexNumber() const
|
||||
|
||||
bool Literal::looksLikeAddress() const
|
||||
{
|
||||
if (subDenomination() != SubDenomination::None)
|
||||
return false;
|
||||
// User suffixes are fine.
|
||||
if (auto subDenomination = get_if<Literal::SubDenomination>(&suffix()))
|
||||
if (*subDenomination != SubDenomination::None)
|
||||
return false;
|
||||
|
||||
if (!isHexNumber())
|
||||
return false;
|
||||
|
@ -2332,6 +2332,8 @@ private:
|
||||
|
||||
/**
|
||||
* A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value.
|
||||
*
|
||||
* It can have a suffix that can lead to a function call.
|
||||
*/
|
||||
class Literal: public PrimaryExpression
|
||||
{
|
||||
@ -2349,14 +2351,15 @@ public:
|
||||
Week = static_cast<int>(Token::SubWeek),
|
||||
Year = static_cast<int>(Token::SubYear)
|
||||
};
|
||||
using Suffix = std::variant<SubDenomination, ASTPointer<IdentifierPath>>;
|
||||
Literal(
|
||||
int64_t _id,
|
||||
SourceLocation const& _location,
|
||||
Token _token,
|
||||
ASTPointer<ASTString> _value,
|
||||
SubDenomination _sub = SubDenomination::None
|
||||
Suffix _suffix = SubDenomination::None
|
||||
):
|
||||
PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_subDenomination(_sub) {}
|
||||
PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_suffix(std::move(_suffix)) {}
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
@ -2366,7 +2369,8 @@ public:
|
||||
|
||||
ASTString valueWithoutUnderscores() const;
|
||||
|
||||
SubDenomination subDenomination() const { return m_subDenomination; }
|
||||
//SubDenomination subDenomination() const { return m_suffix; }
|
||||
Suffix const& suffix() const { return m_suffix; }
|
||||
|
||||
/// @returns true if this is a number with a hex prefix.
|
||||
bool isHexNumber() const;
|
||||
@ -2381,7 +2385,7 @@ public:
|
||||
private:
|
||||
Token m_token;
|
||||
ASTPointer<ASTString> m_value;
|
||||
SubDenomination m_subDenomination;
|
||||
Suffix m_suffix;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
@ -962,17 +962,15 @@ bool ASTJsonExporter::visit(Literal const& _node)
|
||||
Json::Value value{_node.value()};
|
||||
if (!util::validateUTF8(_node.value()))
|
||||
value = Json::nullValue;
|
||||
Token subdenomination = Token(_node.subDenomination());
|
||||
Json::Value subdenomination = Json::nullValue;
|
||||
// if (auto subden = get_if<Literal::SubDenomination>(&_node.suffix()))
|
||||
// subdenomination = Json::Value{TokenTraits::toString(*subden)};
|
||||
// TODO suffix
|
||||
std::vector<pair<string, Json::Value>> attributes = {
|
||||
make_pair("kind", literalTokenKind(_node.token())),
|
||||
make_pair("value", value),
|
||||
make_pair("hexValue", util::toHex(util::asBytes(_node.value()))),
|
||||
make_pair(
|
||||
"subdenomination",
|
||||
subdenomination == Token::Illegal ?
|
||||
Json::nullValue :
|
||||
Json::Value{TokenTraits::toString(subdenomination)}
|
||||
)
|
||||
make_pair("subdenomination", subdenomination)
|
||||
};
|
||||
appendExpressionAttributes(attributes, _node.annotation());
|
||||
setJsonNode(_node, "Literal", std::move(attributes));
|
||||
|
@ -1014,13 +1014,17 @@ void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const
|
||||
|
||||
void Literal::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
_visitor.visit(*this);
|
||||
if (_visitor.visit(*this))
|
||||
if (auto identifierPath = std::get_if<ASTPointer<IdentifierPath>>(&m_suffix))
|
||||
(*identifierPath)->accept(_visitor);
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void Literal::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
_visitor.visit(*this);
|
||||
if (_visitor.visit(*this))
|
||||
if (auto identifierPath = std::get_if<ASTPointer<IdentifierPath>>(&m_suffix))
|
||||
(*identifierPath)->accept(_visitor);
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,7 @@ public:
|
||||
|
||||
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
|
||||
/// not fit any type.
|
||||
/// Ignores user-defined suffixes.
|
||||
static Type const* forLiteral(Literal const& _literal);
|
||||
static RationalNumberType const* rationalNumber(Literal const& _literal);
|
||||
|
||||
|
@ -925,8 +925,10 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
||||
{
|
||||
return make_tuple(false, rational(0));
|
||||
}
|
||||
switch (_literal.subDenomination())
|
||||
{
|
||||
|
||||
if (auto subDenomination = get_if<Literal::SubDenomination>(&_literal.suffix()))
|
||||
switch (*subDenomination)
|
||||
{
|
||||
case Literal::SubDenomination::None:
|
||||
case Literal::SubDenomination::Wei:
|
||||
case Literal::SubDenomination::Second:
|
||||
@ -952,7 +954,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
|
||||
case Literal::SubDenomination::Year:
|
||||
value *= bigint("31536000");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return make_tuple(true, value);
|
||||
@ -1220,6 +1222,33 @@ FixedPointType const* RationalNumberType::fixedPointType() const
|
||||
);
|
||||
}
|
||||
|
||||
pair<RationalNumberType const*, RationalNumberType const*> RationalNumberType::mantissaExponent() const
|
||||
{
|
||||
bool negative = (m_value < 0);
|
||||
int exponent = 0;
|
||||
rational value = abs(m_value); // We care about the sign later.
|
||||
rational maxValue = negative ?
|
||||
rational(bigint(1) << 255, 1):
|
||||
rational((bigint(1) << 256) - 1, 1);
|
||||
|
||||
while (value.denominator() != 1)
|
||||
{
|
||||
value *= 10;
|
||||
exponent--;
|
||||
if (
|
||||
value > rational((bigint(1) << 256) - 1) ||
|
||||
value < rational(-(bigint(1) << 255)) ||
|
||||
exponent < -255 // TODO sane vale?
|
||||
)
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
return {
|
||||
TypeProvider::rationalNumber(value),
|
||||
TypeProvider::rationalNumber(-exponent),
|
||||
};
|
||||
}
|
||||
|
||||
StringLiteralType::StringLiteralType(Literal const& _literal):
|
||||
m_value(_literal.value())
|
||||
{
|
||||
|
@ -587,6 +587,9 @@ public:
|
||||
/// @returns true if the value is not an integer.
|
||||
bool isFractional() const { return m_value.denominator() != 1; }
|
||||
|
||||
/// TODO document
|
||||
std::pair<RationalNumberType const*, RationalNumberType const*> mantissaExponent() const;
|
||||
|
||||
/// @returns true if the value is negative.
|
||||
bool isNegative() const { return m_value < 0; }
|
||||
|
||||
|
@ -2270,19 +2270,62 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
||||
void ExpressionCompiler::endVisit(Literal const& _literal)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _literal);
|
||||
Type const* type = _literal.annotation().type;
|
||||
|
||||
switch (type->category())
|
||||
if (auto identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||
{
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Bool:
|
||||
case Type::Category::Address:
|
||||
m_context << type->literalValue(&_literal);
|
||||
break;
|
||||
case Type::Category::StringLiteral:
|
||||
break; // will be done during conversion
|
||||
default:
|
||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
|
||||
*(*identifierPath)->annotation().referencedDeclaration
|
||||
);
|
||||
FunctionType const& functionType = *function.functionType(true);
|
||||
|
||||
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
|
||||
|
||||
// TODO this is actually not always the right one.
|
||||
auto type = TypeProvider::forLiteral(_literal);
|
||||
|
||||
if (dynamic_cast<RationalNumberType const*>(type) && dynamic_cast<RationalNumberType const*>(type)->isFractional())
|
||||
{
|
||||
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
|
||||
m_context << mantissa->literalValue(nullptr);
|
||||
utils().convertType(*mantissa, *functionType.parameterTypes().at(0));
|
||||
m_context << exponent->literalValue(nullptr);
|
||||
utils().convertType(*exponent, *functionType.parameterTypes().at(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO reuse the code below
|
||||
if (type->category() != Type::Category::StringLiteral)
|
||||
m_context << type->literalValue(&_literal);
|
||||
utils().convertType(*type, *functionType.parameterTypes().at(0));
|
||||
}
|
||||
m_context << m_context.functionEntryLabel(function).pushTag();
|
||||
m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction);
|
||||
m_context << returnLabel;
|
||||
|
||||
// callee adds return parameters, but removes arguments and return label
|
||||
m_context.adjustStackOffset(static_cast<int>(
|
||||
CompilerUtils::sizeOnStack(functionType.returnParameterTypes()) -
|
||||
CompilerUtils::sizeOnStack(functionType.parameterTypes()) -
|
||||
1
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
Type const* type = _literal.annotation().type;
|
||||
|
||||
|
||||
switch (type->category())
|
||||
{
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Bool:
|
||||
case Type::Category::Address:
|
||||
m_context << type->literalValue(&_literal);
|
||||
break;
|
||||
case Type::Category::StringLiteral:
|
||||
break; // will be done during conversion
|
||||
default:
|
||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2437,19 +2437,60 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
bool IRGeneratorForStatements::visit(Literal const& _literal)
|
||||
{
|
||||
setLocation(_literal);
|
||||
Type const& literalType = type(_literal);
|
||||
|
||||
switch (literalType.category())
|
||||
if (auto identifierPath = get_if<ASTPointer<IdentifierPath>>(&_literal.suffix()))
|
||||
{
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Bool:
|
||||
case Type::Category::Address:
|
||||
define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n";
|
||||
break;
|
||||
case Type::Category::StringLiteral:
|
||||
break; // will be done during conversion
|
||||
default:
|
||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||
FunctionDefinition const& function = dynamic_cast<FunctionDefinition const&>(
|
||||
*(*identifierPath)->annotation().referencedDeclaration
|
||||
);
|
||||
FunctionType const& functionType = *function.functionType(true);
|
||||
|
||||
// TODO this is actually not always the right one.
|
||||
auto type = TypeProvider::forLiteral(_literal);
|
||||
|
||||
vector<string> args;
|
||||
if (dynamic_cast<RationalNumberType const*>(type) && dynamic_cast<RationalNumberType const*>(type)->isFractional())
|
||||
{
|
||||
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
|
||||
IRVariable mantissaVar(m_context.newYulVariable(), *mantissa);
|
||||
define(mantissaVar) << toCompactHexWithPrefix(mantissa->literalValue(&_literal)) << "\n";
|
||||
args = convert(mantissaVar, *functionType.parameterTypes().at(0)).stackSlots();
|
||||
|
||||
IRVariable exponentVar(m_context.newYulVariable(), *exponent);
|
||||
define(exponentVar) << toCompactHexWithPrefix(exponent->literalValue(&_literal)) << "\n";
|
||||
args += convert(exponentVar, *functionType.parameterTypes().at(1)).stackSlots();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO reuse the code below
|
||||
if (type->category() != Type::Category::StringLiteral)
|
||||
{
|
||||
IRVariable value(m_context.newYulVariable(), *type);
|
||||
define(value) << toCompactHexWithPrefix(type->literalValue(&_literal)) << "\n";
|
||||
args = convert(value, *functionType.parameterTypes().at(0)).stackSlots();
|
||||
}
|
||||
// TODO what about string?
|
||||
}
|
||||
define(_literal) <<
|
||||
m_context.enqueueFunctionForCodeGeneration(function) <<
|
||||
"(" + joinHumanReadable(args) + ")\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
Type const& literalType = type(_literal);
|
||||
|
||||
switch (literalType.category())
|
||||
{
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Bool:
|
||||
case Type::Category::Address:
|
||||
define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n";
|
||||
break;
|
||||
case Type::Category::StringLiteral:
|
||||
break; // will be done during conversion
|
||||
default:
|
||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1947,6 +1947,61 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
|
||||
}
|
||||
}
|
||||
|
||||
ASTPointer<Expression> Parser::parseLiteral()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
Token token = m_scanner->currentToken();
|
||||
ASTPointer<ASTString> value = make_shared<string>(m_scanner->currentLiteral());
|
||||
Literal::Suffix suffix = Literal::SubDenomination::None;
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case Token::TrueLiteral:
|
||||
case Token::FalseLiteral:
|
||||
case Token::Number:
|
||||
break;
|
||||
case Token::StringLiteral:
|
||||
case Token::UnicodeStringLiteral:
|
||||
case Token::HexStringLiteral:
|
||||
{
|
||||
Token firstToken = token;
|
||||
while (m_scanner->peekNextToken() == firstToken)
|
||||
{
|
||||
advance();
|
||||
*value += m_scanner->currentLiteral();
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
advance();
|
||||
if (m_scanner->currentToken() == Token::Illegal)
|
||||
fatalParserError(5428_error, to_string(m_scanner->currentError()));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solAssert(false);
|
||||
break;
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
advance();
|
||||
|
||||
if (token == Token::Number && (
|
||||
TokenTraits::isEtherSubdenomination(m_scanner->currentToken()) ||
|
||||
TokenTraits::isTimeSubdenomination(m_scanner->currentToken())
|
||||
))
|
||||
{
|
||||
nodeFactory.markEndPosition();
|
||||
suffix = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
|
||||
advance();
|
||||
}
|
||||
else if (m_scanner->currentToken() == Token::Identifier)
|
||||
{
|
||||
auto identifierPath = parseIdentifierPath();
|
||||
nodeFactory.setEndPositionFromNode(identifierPath);
|
||||
suffix = move(identifierPath);
|
||||
}
|
||||
return nodeFactory.createNode<Literal>(token, move(value), move(suffix));
|
||||
}
|
||||
|
||||
ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
@ -1958,50 +2013,12 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
{
|
||||
case Token::TrueLiteral:
|
||||
case Token::FalseLiteral:
|
||||
nodeFactory.markEndPosition();
|
||||
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
|
||||
break;
|
||||
case Token::Number:
|
||||
if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken()))
|
||||
{
|
||||
ASTPointer<ASTString> literal = getLiteralAndAdvance();
|
||||
nodeFactory.markEndPosition();
|
||||
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
|
||||
advance();
|
||||
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
|
||||
}
|
||||
else if (TokenTraits::isTimeSubdenomination(m_scanner->peekNextToken()))
|
||||
{
|
||||
ASTPointer<ASTString> literal = getLiteralAndAdvance();
|
||||
nodeFactory.markEndPosition();
|
||||
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
|
||||
advance();
|
||||
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeFactory.markEndPosition();
|
||||
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
|
||||
}
|
||||
break;
|
||||
case Token::StringLiteral:
|
||||
case Token::UnicodeStringLiteral:
|
||||
case Token::HexStringLiteral:
|
||||
{
|
||||
string literal = m_scanner->currentLiteral();
|
||||
Token firstToken = m_scanner->currentToken();
|
||||
while (m_scanner->peekNextToken() == firstToken)
|
||||
{
|
||||
advance();
|
||||
literal += m_scanner->currentLiteral();
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
advance();
|
||||
if (m_scanner->currentToken() == Token::Illegal)
|
||||
fatalParserError(5428_error, to_string(m_scanner->currentError()));
|
||||
expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal));
|
||||
expression = parseLiteral();
|
||||
break;
|
||||
}
|
||||
case Token::Identifier:
|
||||
nodeFactory.markEndPosition();
|
||||
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
|
||||
|
@ -159,6 +159,7 @@ private:
|
||||
ASTPointer<Expression> parseLeftHandSideExpression(
|
||||
ASTPointer<Expression> const& _partiallyParsedExpression = ASTPointer<Expression>()
|
||||
);
|
||||
ASTPointer<Expression> parseLiteral();
|
||||
ASTPointer<Expression> parsePrimaryExpression();
|
||||
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
type Length is uint;
|
||||
|
||||
function km(uint meters) pure returns (Length) {
|
||||
return Length.wrap(meters * 1000);
|
||||
}
|
||||
|
||||
struct Float {
|
||||
uint mantissa;
|
||||
int exponent;
|
||||
}
|
||||
|
||||
function f(uint mantissa, int exponent) pure returns (Float memory) {
|
||||
return Float(mantissa, exponent);
|
||||
}
|
||||
|
||||
contract C {
|
||||
Length public length = 1000 km;
|
||||
Float public factor = 1.23 f;
|
||||
}
|
||||
// ----
|
||||
// length() -> 1000000
|
||||
// factor() -> 0x7b, 2
|
Loading…
Reference in New Issue
Block a user