diff options
Diffstat (limited to 'libsolidity/parsing')
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 281 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.h | 16 | ||||
-rw-r--r-- | libsolidity/parsing/ParserBase.cpp | 20 | ||||
-rw-r--r-- | libsolidity/parsing/ParserBase.h | 8 | ||||
-rw-r--r-- | libsolidity/parsing/Scanner.cpp | 89 | ||||
-rw-r--r-- | libsolidity/parsing/Scanner.h | 39 | ||||
-rw-r--r-- | libsolidity/parsing/Token.cpp | 102 | ||||
-rw-r--r-- | libsolidity/parsing/Token.h | 168 |
8 files changed, 423 insertions, 300 deletions
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e2e1eebc..b17dad9a 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -20,7 +20,7 @@ * Solidity parser. */ -#include <ctype.h> +#include <cctype> #include <vector> #include <libevmasm/SourceLocation.h> #include <libsolidity/parsing/Parser.h> @@ -57,7 +57,7 @@ public: solAssert(m_location.sourceName, ""); if (m_location.end < 0) markEndPosition(); - return make_shared<NodeType>(m_location, forward<Args>(_args)...); + return make_shared<NodeType>(m_location, std::forward<Args>(_args)...); } private: @@ -75,7 +75,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) vector<ASTPointer<ASTNode>> nodes; while (m_scanner->currentToken() != Token::EOS) { - switch (auto token = m_scanner->currentToken()) + switch (m_scanner->currentToken()) { case Token::Pragma: nodes.push_back(parsePragmaDirective()); @@ -86,7 +86,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) case Token::Interface: case Token::Contract: case Token::Library: - nodes.push_back(parseContractDefinition(token)); + nodes.push_back(parseContractDefinition()); break; default: fatalParserError(string("Expected pragma, import directive or contract/interface/library definition.")); @@ -112,17 +112,17 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective() ASTNodeFactory nodeFactory(*this); expectToken(Token::Pragma); vector<string> literals; - vector<Token::Value> tokens; + vector<Token> tokens; do { - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (token == Token::Illegal) parserError("Token incompatible with Solidity parser as part of pragma directive."); else { string literal = m_scanner->currentLiteral(); - if (literal.empty() && Token::toString(token)) - literal = Token::toString(token); + if (literal.empty() && TokenTraits::toString(token)) + literal = TokenTraits::toString(token); literals.push_back(literal); tokens.push_back(token); } @@ -198,31 +198,35 @@ ASTPointer<ImportDirective> Parser::parseImportDirective() return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases)); } -ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token) +ContractDefinition::ContractKind Parser::parseContractKind() { - switch(_token) + ContractDefinition::ContractKind kind; + switch(m_scanner->currentToken()) { case Token::Interface: - return ContractDefinition::ContractKind::Interface; + kind = ContractDefinition::ContractKind::Interface; + break; case Token::Contract: - return ContractDefinition::ContractKind::Contract; + kind = ContractDefinition::ContractKind::Contract; + break; case Token::Library: - return ContractDefinition::ContractKind::Library; + kind = ContractDefinition::ContractKind::Library; + break; default: - fatalParserError("Unsupported contract type."); + solAssert(false, "Invalid contract kind."); } - // FIXME: fatalParserError is not considered as throwing here - return ContractDefinition::ContractKind::Contract; + m_scanner->next(); + return kind; } -ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _expectedKind) +ASTPointer<ContractDefinition> Parser::parseContractDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer<ASTString> docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); - expectToken(_expectedKind); + ContractDefinition::ContractKind contractKind = parseContractKind(); ASTPointer<ASTString> name = expectIdentifierToken(); vector<ASTPointer<InheritanceSpecifier>> baseContracts; if (m_scanner->currentToken() == Token::Is) @@ -236,16 +240,13 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp expectToken(Token::LBrace); while (true) { - Token::Value currentTokenValue = m_scanner->currentToken(); + Token currentTokenValue = m_scanner->currentToken(); if (currentTokenValue == Token::RBrace) break; - else if ( - currentTokenValue == Token::Function || - (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "constructor") - ) + else if (currentTokenValue == Token::Function || currentTokenValue == Token::Constructor) // This can be a function or a state variable of function type (especially // complicated to distinguish fallback function from function type state variable) - subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get())); + subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable()); else if (currentTokenValue == Token::Struct) subNodes.push_back(parseStructDefinition()); else if (currentTokenValue == Token::Enum) @@ -253,7 +254,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp else if ( currentTokenValue == Token::Identifier || currentTokenValue == Token::Mapping || - Token::isElementaryTypeName(currentTokenValue) + TokenTraits::isElementaryTypeName(currentTokenValue) ) { VarDeclParserOptions options; @@ -278,7 +279,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp docString, baseContracts, subNodes, - tokenToContractKind(_expectedKind) + contractKind ); } @@ -300,70 +301,92 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() return nodeFactory.createNode<InheritanceSpecifier>(name, std::move(arguments)); } -Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) +Declaration::Visibility Parser::parseVisibilitySpecifier() { Declaration::Visibility visibility(Declaration::Visibility::Default); - if (_token == Token::Public) - visibility = Declaration::Visibility::Public; - else if (_token == Token::Internal) - visibility = Declaration::Visibility::Internal; - else if (_token == Token::Private) - visibility = Declaration::Visibility::Private; - else if (_token == Token::External) - visibility = Declaration::Visibility::External; - else - solAssert(false, "Invalid visibility specifier."); + Token token = m_scanner->currentToken(); + switch (token) + { + case Token::Public: + visibility = Declaration::Visibility::Public; + break; + case Token::Internal: + visibility = Declaration::Visibility::Internal; + break; + case Token::Private: + visibility = Declaration::Visibility::Private; + break; + case Token::External: + visibility = Declaration::Visibility::External; + break; + default: + solAssert(false, "Invalid visibility specifier."); + } m_scanner->next(); return visibility; } -StateMutability Parser::parseStateMutability(Token::Value _token) +StateMutability Parser::parseStateMutability() { StateMutability stateMutability(StateMutability::NonPayable); - if (_token == Token::Payable) - stateMutability = StateMutability::Payable; - // FIXME: constant should be removed at the next breaking release - else if (_token == Token::View || _token == Token::Constant) - stateMutability = StateMutability::View; - else if (_token == Token::Pure) - stateMutability = StateMutability::Pure; - else - solAssert(false, "Invalid state mutability specifier."); + Token token = m_scanner->currentToken(); + switch(token) + { + case Token::Payable: + stateMutability = StateMutability::Payable; + break; + case Token::View: + stateMutability = StateMutability::View; + break; + case Token::Pure: + stateMutability = StateMutability::Pure; + break; + case Token::Constant: + stateMutability = StateMutability::View; + parserError( + "The state mutability modifier \"constant\" was removed in version 0.5.0. " + "Use \"view\" or \"pure\" instead." + ); + break; + default: + solAssert(false, "Invalid state mutability specifier."); + } m_scanner->next(); return stateMutability; } -Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( - bool _forceEmptyName, - bool _allowModifiers, - ASTString const* _contractName -) +Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) { RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; result.isConstructor = false; - if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") + if (m_scanner->currentToken() == Token::Constructor) result.isConstructor = true; else if (m_scanner->currentToken() != Token::Function) solAssert(false, "Function or constructor expected."); m_scanner->next(); - if (result.isConstructor || _forceEmptyName || m_scanner->currentToken() == Token::LParen) + if (result.isConstructor) + result.name = make_shared<ASTString>(); + else if (_forceEmptyName || m_scanner->currentToken() == Token::LParen) result.name = make_shared<ASTString>(); + else if (m_scanner->currentToken() == Token::Constructor) + fatalParserError(string( + "This function is named \"constructor\" but is not the constructor of the contract. " + "If you intend this to be a constructor, use \"constructor(...) { ... }\" without the \"function\" keyword to define it." + )); else result.name = expectIdentifierToken(); - if (!result.name->empty() && _contractName && *result.name == *_contractName) - result.isConstructor = true; VarDeclParserOptions options; options.allowLocationSpecifier = true; result.parameters = parseParameterList(options); while (true) { - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (_allowModifiers && token == Token::Identifier) { // If the name is empty (and this is not a constructor), @@ -378,7 +401,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( else result.modifiers.push_back(parseModifierInvocation()); } - else if (Token::isVisibilitySpecifier(token)) + else if (TokenTraits::isVisibilitySpecifier(token)) { if (result.visibility != Declaration::Visibility::Default) { @@ -398,9 +421,9 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( m_scanner->next(); } else - result.visibility = parseVisibilitySpecifier(token); + result.visibility = parseVisibilitySpecifier(); } - else if (Token::isStateMutabilitySpecifier(token)) + else if (TokenTraits::isStateMutabilitySpecifier(token)) { if (result.stateMutability != StateMutability::NonPayable) { @@ -412,7 +435,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( m_scanner->next(); } else - result.stateMutability = parseStateMutability(token); + result.stateMutability = parseStateMutability(); } else break; @@ -428,7 +451,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( return result; } -ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName) +ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -436,7 +459,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A if (m_scanner->currentCommentLiteral() != "") docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral()); - FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName); + FunctionHeaderParserResult header = parseFunctionHeader(false, true); if ( header.isConstructor || @@ -531,7 +554,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition() if (m_scanner->currentToken() != Token::Identifier) fatalParserError(string("Expected identifier after ','")); } - if (members.size() == 0) + if (members.empty()) parserError({"enum with no members is not allowed."}); nodeFactory.markEndPosition(); @@ -559,14 +582,15 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( bool isIndexed = false; bool isDeclaredConst = false; Declaration::Visibility visibility(Declaration::Visibility::Default); - VariableDeclaration::Location location = VariableDeclaration::Location::Default; + VariableDeclaration::Location location = VariableDeclaration::Location::Unspecified; ASTPointer<ASTString> identifier; while (true) { - Token::Value token = m_scanner->currentToken(); - if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token)) + Token token = m_scanner->currentToken(); + if (_options.isStateVariable && TokenTraits::isVariableVisibilitySpecifier(token)) { + nodeFactory.markEndPosition(); if (visibility != Declaration::Visibility::Default) { parserError(string( @@ -577,7 +601,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( m_scanner->next(); } else - visibility = parseVisibilitySpecifier(token); + visibility = parseVisibilitySpecifier(); } else { @@ -585,36 +609,47 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( isIndexed = true; else if (token == Token::Constant) isDeclaredConst = true; - else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token)) + else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { - if (location != VariableDeclaration::Location::Default) + if (location != VariableDeclaration::Location::Unspecified) parserError(string("Location already specified.")); else if (!type) parserError(string("Location specifier needs explicit type name.")); else - location = ( - token == Token::Memory ? - VariableDeclaration::Location::Memory : - VariableDeclaration::Location::Storage - ); + { + switch (token) + { + case Token::Storage: + location = VariableDeclaration::Location::Storage; + break; + case Token::Memory: + location = VariableDeclaration::Location::Memory; + break; + case Token::CallData: + location = VariableDeclaration::Location::CallData; + break; + default: + solAssert(false, "Unknown data location."); + } + } } else break; + nodeFactory.markEndPosition(); m_scanner->next(); } } - nodeFactory.markEndPosition(); if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier) { identifier = make_shared<ASTString>(""); solAssert(!_options.allowVar, ""); // allowEmptyName && allowVar makes no sense - if (type) - nodeFactory.setEndPositionFromNode(type); - // if type is null this has already caused an error } else + { + nodeFactory.markEndPosition(); identifier = expectIdentifierToken(); + } ASTPointer<Expression> value; if (_options.allowInitialValue) { @@ -771,15 +806,31 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer<TypeName> type; - Token::Value token = m_scanner->currentToken(); - if (Token::isElementaryTypeName(token)) + Token token = m_scanner->currentToken(); + if (TokenTraits::isElementaryTypeName(token)) { unsigned firstSize; unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize); - type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(elemTypeName); + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); m_scanner->next(); + auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable); + if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) + { + if (elemTypeName.token() == Token::Address) + { + nodeFactory.markEndPosition(); + stateMutability = parseStateMutability(); + } + else + { + parserError("State mutability can only be specified for address types."); + m_scanner->next(); + } + } + type = nodeFactory.createNode<ElementaryTypeName>(elemTypeName, stateMutability); } else if (token == Token::Var) { @@ -823,8 +874,8 @@ ASTPointer<Mapping> Parser::parseMapping() expectToken(Token::Mapping); expectToken(Token::LParen); ASTPointer<ElementaryTypeName> keyType; - Token::Value token = m_scanner->currentToken(); - if (!Token::isElementaryTypeName(token)) + Token token = m_scanner->currentToken(); + if (!TokenTraits::isElementaryTypeName(token)) fatalParserError(string("Expected elementary type name for mapping key type")); unsigned firstSize; unsigned secondSize; @@ -928,10 +979,11 @@ ASTPointer<Statement> Parser::parseStatement() } case Token::Assembly: return parseInlineAssembly(docString); + case Token::Emit: + statement = parseEmitStatement(docString); + break; case Token::Identifier: - if (m_scanner->currentLiteral() == "emit") - statement = parseEmitStatement(docString); - else if (m_insideModifier && m_scanner->currentLiteral() == "_") + if (m_insideModifier && m_scanner->currentLiteral() == "_") { statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString); m_scanner->next(); @@ -1051,6 +1103,8 @@ ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const& _docString) { + expectToken(Token::Emit, false); + ASTNodeFactory nodeFactory(*this); m_scanner->next(); ASTNodeFactory eventCallNodeFactory(*this); @@ -1198,7 +1252,7 @@ pair<Parser::LookAheadInfo, Parser::IndexAccessedPath> Parser::tryParseIndexAcce // VariableDeclarationStatement out of it. IndexAccessedPath iap = parseIndexAccessedPath(); - if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken())) + if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken())) return make_pair(LookAheadInfo::VariableDeclaration, move(iap)); else return make_pair(LookAheadInfo::Expression, move(iap)); @@ -1288,16 +1342,16 @@ ASTPointer<Expression> Parser::parseExpression( { RecursionGuard recursionGuard(*this); ASTPointer<Expression> expression = parseBinaryExpression(4, _partiallyParsedExpression); - if (Token::isAssignmentOp(m_scanner->currentToken())) + if (TokenTraits::isAssignmentOp(m_scanner->currentToken())) { - Token::Value assignmentOperator = m_scanner->currentToken(); + Token assignmentOperator = m_scanner->currentToken(); m_scanner->next(); ASTPointer<Expression> rightHandSide = parseExpression(); ASTNodeFactory nodeFactory(*this, expression); nodeFactory.setEndPositionFromNode(rightHandSide); return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); } - else if (m_scanner->currentToken() == Token::Value::Conditional) + else if (m_scanner->currentToken() == Token::Conditional) { m_scanner->next(); ASTPointer<Expression> trueExpression = parseExpression(); @@ -1319,11 +1373,11 @@ ASTPointer<Expression> Parser::parseBinaryExpression( RecursionGuard recursionGuard(*this); ASTPointer<Expression> expression = parseUnaryExpression(_partiallyParsedExpression); ASTNodeFactory nodeFactory(*this, expression); - int precedence = Token::precedence(m_scanner->currentToken()); + int precedence = TokenTraits::precedence(m_scanner->currentToken()); for (; precedence >= _minPrecedence; --precedence) - while (Token::precedence(m_scanner->currentToken()) == precedence) + while (TokenTraits::precedence(m_scanner->currentToken()) == precedence) { - Token::Value op = m_scanner->currentToken(); + Token op = m_scanner->currentToken(); m_scanner->next(); ASTPointer<Expression> right = parseBinaryExpression(precedence + 1); nodeFactory.setEndPositionFromNode(right); @@ -1339,8 +1393,8 @@ ASTPointer<Expression> Parser::parseUnaryExpression( RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _partiallyParsedExpression ? ASTNodeFactory(*this, _partiallyParsedExpression) : ASTNodeFactory(*this); - Token::Value token = m_scanner->currentToken(); - if (!_partiallyParsedExpression && (Token::isUnaryOp(token) || Token::isCountOp(token))) + Token token = m_scanner->currentToken(); + if (!_partiallyParsedExpression && (TokenTraits::isUnaryOp(token) || TokenTraits::isCountOp(token))) { // prefix expression m_scanner->next(); @@ -1353,7 +1407,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression( // potential postfix expression ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_partiallyParsedExpression); token = m_scanner->currentToken(); - if (!Token::isCountOp(token)) + if (!TokenTraits::isCountOp(token)) return subExpression; nodeFactory.markEndPosition(); m_scanner->next(); @@ -1428,7 +1482,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); ASTPointer<Expression> expression; switch (token) @@ -1439,7 +1493,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); break; case Token::Number: - if (Token::isEtherSubdenomination(m_scanner->peekNextToken())) + if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken())) { ASTPointer<ASTString> literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); @@ -1447,7 +1501,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() m_scanner->next(); expression = nodeFactory.createNode<Literal>(token, literal, subdenomination); } - else if (Token::isTimeSubdenomination(m_scanner->peekNextToken())) + else if (TokenTraits::isTimeSubdenomination(m_scanner->peekNextToken())) { ASTPointer<ASTString> literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); @@ -1477,7 +1531,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() // (x,) is one-dimensional tuple, elements in arrays cannot be left out, only in tuples. m_scanner->next(); vector<ASTPointer<Expression>> components; - Token::Value oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack); + Token oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack); bool isArray = (token == Token::LBrack); if (m_scanner->currentToken() != oppositeToken) @@ -1500,8 +1554,11 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() expression = nodeFactory.createNode<TupleExpression>(components, isArray); break; } + case Token::IllegalHex: + fatalParserError("Expected even number of hex-nibbles within double-quotes."); + break; default: - if (Token::isElementaryTypeName(token)) + if (TokenTraits::isElementaryTypeName(token)) { //used for casts unsigned firstSize; @@ -1538,7 +1595,7 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars { RecursionGuard recursionGuard(*this); pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret; - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (token == Token::LBrace) { // call({arg1 : 1, arg2 : 2 }) @@ -1577,19 +1634,25 @@ Parser::LookAheadInfo Parser::peekStatementType() const // Distinguish between variable declaration (and potentially assignment) and expression statement // (which include assignments to other expressions and pre-declared variables). // We have a variable declaration if we get a keyword that specifies a type name. - // If it is an identifier or an elementary type name followed by an identifier, we also have - // a variable declaration. + // If it is an identifier or an elementary type name followed by an identifier + // or a mutability specifier, we also have a variable declaration. // If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;"). // In all other cases, we have an expression statement. - Token::Value token(m_scanner->currentToken()); - bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); + Token token(m_scanner->currentToken()); + bool mightBeTypeName = (TokenTraits::isElementaryTypeName(token) || token == Token::Identifier); if (token == Token::Mapping || token == Token::Function || token == Token::Var) return LookAheadInfo::VariableDeclaration; if (mightBeTypeName) { - Token::Value next = m_scanner->peekNextToken(); - if (next == Token::Identifier || Token::isLocationSpecifier(next)) + Token next = m_scanner->peekNextToken(); + // So far we only allow ``address payable`` in variable declaration statements and in no other + // kind of statement. This means, for example, that we do not allow type expressions of the form + // ``address payable;``. + // If we want to change this in the future, we need to consider another scanner token here. + if (TokenTraits::isElementaryTypeName(token) && TokenTraits::isStateMutabilitySpecifier(next, false)) + return LookAheadInfo::VariableDeclaration; + if (next == Token::Identifier || TokenTraits::isLocationSpecifier(next)) return LookAheadInfo::VariableDeclaration; if (next == Token::LBrack || next == Token::Period) return LookAheadInfo::IndexAccessStructure; diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 08653364..fa974171 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -69,17 +69,13 @@ private: ///@name Parsing functions for the AST nodes ASTPointer<PragmaDirective> parsePragmaDirective(); ASTPointer<ImportDirective> parseImportDirective(); - ContractDefinition::ContractKind tokenToContractKind(Token::Value _token); - ASTPointer<ContractDefinition> parseContractDefinition(Token::Value _expectedKind); + ContractDefinition::ContractKind parseContractKind(); + ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); - Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); - StateMutability parseStateMutability(Token::Value _token); - FunctionHeaderParserResult parseFunctionHeader( - bool _forceEmptyName, - bool _allowModifiers, - ASTString const* _contractName = nullptr - ); - ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); + Declaration::Visibility parseVisibilitySpecifier(); + StateMutability parseStateMutability(); + FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); + ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(); ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName); ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition(); diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp index 71133746..1d4cb1e2 100644 --- a/libsolidity/parsing/ParserBase.cpp +++ b/libsolidity/parsing/ParserBase.cpp @@ -43,12 +43,12 @@ int ParserBase::endPosition() const return m_scanner->currentLocation().end; } -Token::Value ParserBase::currentToken() const +Token ParserBase::currentToken() const { return m_scanner->currentToken(); } -Token::Value ParserBase::peekNextToken() const +Token ParserBase::peekNextToken() const { return m_scanner->peekNextToken(); } @@ -58,31 +58,31 @@ std::string ParserBase::currentLiteral() const return m_scanner->currentLiteral(); } -Token::Value ParserBase::advance() +Token ParserBase::advance() { return m_scanner->next(); } -void ParserBase::expectToken(Token::Value _value, bool _advance) +void ParserBase::expectToken(Token _value, bool _advance) { - Token::Value tok = m_scanner->currentToken(); + Token tok = m_scanner->currentToken(); if (tok != _value) { - auto tokenName = [this](Token::Value _token) + auto tokenName = [this](Token _token) { if (_token == Token::Identifier) return string("identifier"); else if (_token == Token::EOS) return string("end of source"); - else if (Token::isReservedKeyword(_token)) - return string("reserved keyword '") + Token::friendlyName(_token) + "'"; - else if (Token::isElementaryTypeName(_token)) //for the sake of accuracy in reporting + else if (TokenTraits::isReservedKeyword(_token)) + return string("reserved keyword '") + TokenTraits::friendlyName(_token) + "'"; + else if (TokenTraits::isElementaryTypeName(_token)) //for the sake of accuracy in reporting { ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken(); return string("'") + elemTypeName.toString() + "'"; } else - return string("'") + Token::friendlyName(_token) + "'"; + return string("'") + TokenTraits::friendlyName(_token) + "'"; }; fatalParserError(string("Expected ") + tokenName(_value) + string(" but got ") + tokenName(tok)); diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h index b28e1b1b..e01f37d8 100644 --- a/libsolidity/parsing/ParserBase.h +++ b/libsolidity/parsing/ParserBase.h @@ -63,11 +63,11 @@ protected: ///@{ ///@name Helper functions /// If current token value is not _value, throw exception otherwise advance token. - void expectToken(Token::Value _value, bool _advance = true); - Token::Value currentToken() const; - Token::Value peekNextToken() const; + void expectToken(Token _value, bool _advance = true); + Token currentToken() const; + Token peekNextToken() const; std::string currentLiteral() const; - Token::Value advance(); + Token advance(); ///@} /// Increases the recursion depth and throws an exception if it is too deep. diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index dbe1f389..e9dad2ad 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -214,9 +214,9 @@ void Scanner::addUnicodeAsUTF8(unsigned codepoint) } // Ensure that tokens can be stored in a byte. -BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); +BOOST_STATIC_ASSERT(TokenTraits::count() <= 0x100); -Token::Value Scanner::next() +Token Scanner::next() { m_currentToken = m_nextToken; m_skippedComment = m_nextSkippedComment; @@ -225,7 +225,7 @@ Token::Value Scanner::next() return m_currentToken.token; } -Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else) +Token Scanner::selectToken(char _next, Token _then, Token _else) { advance(); if (m_char == _next) @@ -249,7 +249,7 @@ void Scanner::skipWhitespaceExceptUnicodeLinebreak() advance(); } -Token::Value Scanner::skipSingleLineComment() +Token Scanner::skipSingleLineComment() { // Line terminator is not part of the comment. If it is a // non-ascii line terminator, it will result in a parser error. @@ -259,7 +259,7 @@ Token::Value Scanner::skipSingleLineComment() return Token::Whitespace; } -Token::Value Scanner::scanSingleLineDocComment() +Token Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); advance(); //consume the last '/' at /// @@ -295,7 +295,7 @@ Token::Value Scanner::scanSingleLineDocComment() return Token::CommentLiteral; } -Token::Value Scanner::skipMultiLineComment() +Token Scanner::skipMultiLineComment() { advance(); while (!isSourcePastEndOfInput()) @@ -316,7 +316,7 @@ Token::Value Scanner::skipMultiLineComment() return Token::Illegal; } -Token::Value Scanner::scanMultiLineDocComment() +Token Scanner::scanMultiLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); bool endFound = false; @@ -369,7 +369,7 @@ Token::Value Scanner::scanMultiLineDocComment() return Token::CommentLiteral; } -Token::Value Scanner::scanSlash() +Token Scanner::scanSlash() { int firstSlashPosition = sourcePos(); advance(); @@ -380,7 +380,7 @@ Token::Value Scanner::scanSlash() else if (m_char == '/') { // doxygen style /// comment - Token::Value comment; + Token comment; m_nextSkippedComment.location.start = firstSlashPosition; comment = scanSingleLineDocComment(); m_nextSkippedComment.location.end = sourcePos(); @@ -406,7 +406,7 @@ Token::Value Scanner::scanSlash() return Token::Whitespace; } // we actually have a multiline documentation comment - Token::Value comment; + Token comment; m_nextSkippedComment.location.start = firstSlashPosition; comment = scanMultiLineDocComment(); m_nextSkippedComment.location.end = sourcePos(); @@ -432,7 +432,7 @@ void Scanner::scanToken() m_nextSkippedComment.literal.clear(); m_nextSkippedComment.extendedTokenInfo = make_tuple(0, 0); - Token::Value token; + Token token; // M and N are for the purposes of grabbing different type sizes unsigned m; unsigned n; @@ -601,7 +601,7 @@ void Scanner::scanToken() { tie(token, m, n) = scanIdentifierOrKeyword(); - // Special case for hexademical literals + // Special case for hexadecimal literals if (token == Token::Hex) { // reset @@ -612,7 +612,7 @@ void Scanner::scanToken() if (m_char == '"' || m_char == '\'') token = scanHexString(); else - token = Token::Illegal; + token = Token::IllegalHex; } } else if (isDecimalDigit(m_char)) @@ -703,7 +703,7 @@ bool Scanner::isUnicodeLinebreak() return false; } -Token::Value Scanner::scanString() +Token Scanner::scanString() { char const quote = m_char; advance(); // consume quote @@ -727,7 +727,7 @@ Token::Value Scanner::scanString() return Token::StringLiteral; } -Token::Value Scanner::scanHexString() +Token Scanner::scanHexString() { char const quote = m_char; advance(); // consume quote @@ -736,23 +736,31 @@ Token::Value Scanner::scanHexString() { char c = m_char; if (!scanHexByte(c)) - return Token::Illegal; + return Token::IllegalHex; addLiteralChar(c); } if (m_char != quote) - return Token::Illegal; + return Token::IllegalHex; literal.complete(); advance(); // consume quote return Token::StringLiteral; } +// Parse for regex [:digit:]+(_[:digit:]+)* void Scanner::scanDecimalDigits() { - while (isDecimalDigit(m_char)) - addLiteralCharAndAdvance(); + // MUST begin with a decimal digit. + if (!isDecimalDigit(m_char)) + return; + + // May continue with decimal digit or underscore for grouping. + do addLiteralCharAndAdvance(); + while (!m_source.isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_')); + + // Defer further validation of underscore to SyntaxChecker. } -Token::Value Scanner::scanNumber(char _charSeen) +Token Scanner::scanNumber(char _charSeen) { enum { DECIMAL, HEX, BINARY } kind = DECIMAL; LiteralScope literal(this, LITERAL_TYPE_NUMBER); @@ -760,6 +768,8 @@ Token::Value Scanner::scanNumber(char _charSeen) { // we have already seen a decimal point of the float addLiteralChar('.'); + if (m_char == '_') + return Token::Illegal; scanDecimalDigits(); // we know we have at least one digit } else @@ -770,14 +780,15 @@ Token::Value Scanner::scanNumber(char _charSeen) { addLiteralCharAndAdvance(); // either 0, 0exxx, 0Exxx, 0.xxx or a hex number - if (m_char == 'x' || m_char == 'X') + if (m_char == 'x') { // hex number kind = HEX; addLiteralCharAndAdvance(); if (!isHexDigit(m_char)) - return Token::Illegal; // we must have at least one hex digit after 'x'/'X' - while (isHexDigit(m_char)) + return Token::Illegal; // we must have at least one hex digit after 'x' + + while (isHexDigit(m_char) || m_char == '_') // We keep the underscores for later validation addLiteralCharAndAdvance(); } else if (isDecimalDigit(m_char)) @@ -790,8 +801,22 @@ Token::Value Scanner::scanNumber(char _charSeen) scanDecimalDigits(); // optional if (m_char == '.') { + if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') + { + // Assume the input may be a floating point number with leading '_' in fraction part. + // Recover by consuming it all but returning `Illegal` right away. + addLiteralCharAndAdvance(); // '.' + addLiteralCharAndAdvance(); // '_' + scanDecimalDigits(); + } + if (m_source.isPastEndOfInput() || !isDecimalDigit(m_source.get(1))) + { + // A '.' has to be followed by a number. + literal.complete(); + return Token::Number; + } addLiteralCharAndAdvance(); - scanDecimalDigits(); // optional + scanDecimalDigits(); } } } @@ -801,8 +826,18 @@ Token::Value Scanner::scanNumber(char _charSeen) solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); if (kind != DECIMAL) return Token::Illegal; + else if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') + { + // Recover from wrongly placed underscore as delimiter in literal with scientific + // notation by consuming until the end. + addLiteralCharAndAdvance(); // 'e' + addLiteralCharAndAdvance(); // '_' + scanDecimalDigits(); + literal.complete(); + return Token::Number; + } // scan exponent - addLiteralCharAndAdvance(); + addLiteralCharAndAdvance(); // 'e' | 'E' if (m_char == '+' || m_char == '-') addLiteralCharAndAdvance(); if (!isDecimalDigit(m_char)) @@ -819,7 +854,7 @@ Token::Value Scanner::scanNumber(char _charSeen) return Token::Number; } -tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() +tuple<Token, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() { solAssert(isIdentifierStart(m_char), ""); LiteralScope literal(this, LITERAL_TYPE_STRING); @@ -828,7 +863,7 @@ tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() while (isIdentifierPart(m_char)) //get full literal addLiteralCharAndAdvance(); literal.complete(); - return Token::fromIdentifierOrKeyword(m_nextToken.literal); + return TokenTraits::fromIdentifierOrKeyword(m_nextToken.literal); } char CharStream::advanceAndGet(size_t _chars) diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index 602532e4..14eeb66e 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -112,13 +112,13 @@ public: void reset(); /// @returns the next token and advances input - Token::Value next(); + Token next(); ///@{ ///@name Information about the current token /// @returns the current token - Token::Value currentToken() const + Token currentToken() const { return m_currentToken.token; } @@ -149,7 +149,7 @@ public: ///@name Information about the next token /// @returns the next token without advancing input. - Token::Value peekNextToken() const { return m_nextToken.token; } + Token peekNextToken() const { return m_nextToken.token; } SourceLocation peekLocation() const { return m_nextToken.location; } std::string const& peekLiteral() const { return m_nextToken.literal; } ///@} @@ -162,13 +162,20 @@ public: /// Do only use in error cases, they are quite expensive. std::string lineAtPosition(int _position) const { return m_source.lineAtPosition(_position); } std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); } + std::string sourceAt(SourceLocation const& _location) const + { + solAssert(!_location.isEmpty(), ""); + solAssert(m_sourceName && _location.sourceName, ""); + solAssert(*m_sourceName == *_location.sourceName, ""); + return m_source.source().substr(_location.start, _location.end - _location.start); + } ///@} private: /// Used for the current and look-ahead token and comments struct TokenDesc { - Token::Value token; + Token token; SourceLocation location; std::string literal; std::tuple<unsigned, unsigned> extendedTokenInfo; @@ -185,9 +192,9 @@ private: bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } void rollback(int _amount) { m_char = m_source.rollback(_amount); } - inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; } + inline Token selectToken(Token _tok) { advance(); return _tok; } /// If the next character is _next, advance and return _then, otherwise return _else. - inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else); + inline Token selectToken(char _next, Token _then, Token _else); bool scanHexByte(char& o_scannedByte); bool scanUnicode(unsigned& o_codepoint); @@ -199,19 +206,19 @@ private: bool skipWhitespace(); /// Skips all whitespace that are neither '\r' nor '\n'. void skipWhitespaceExceptUnicodeLinebreak(); - Token::Value skipSingleLineComment(); - Token::Value skipMultiLineComment(); + Token skipSingleLineComment(); + Token skipMultiLineComment(); void scanDecimalDigits(); - Token::Value scanNumber(char _charSeen = 0); - std::tuple<Token::Value, unsigned, unsigned> scanIdentifierOrKeyword(); + Token scanNumber(char _charSeen = 0); + std::tuple<Token, unsigned, unsigned> scanIdentifierOrKeyword(); - Token::Value scanString(); - Token::Value scanHexString(); - Token::Value scanSingleLineDocComment(); - Token::Value scanMultiLineDocComment(); + Token scanString(); + Token scanHexString(); + Token scanSingleLineDocComment(); + Token scanMultiLineDocComment(); /// Scans a slash '/' and depending on the characters returns the appropriate token - Token::Value scanSlash(); + Token scanSlash(); /// Scans an escape-sequence which is part of a string and adds the /// decoded character to the current literal. Returns true if a pattern @@ -226,7 +233,7 @@ private: bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } TokenDesc m_skippedComment; // desc for current skipped comment - TokenDesc m_nextSkippedComment; // desc for next skiped comment + TokenDesc m_nextSkippedComment; // desc for next skipped comment TokenDesc m_currentToken; // desc for current token (as returned by Next()) TokenDesc m_nextToken; // desc for next token (one token look-ahead) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 5ce74316..dccd9037 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -51,9 +51,9 @@ namespace dev namespace solidity { -void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second) +void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second) { - solAssert(Token::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(Token::toString(_baseType))); + solAssert(TokenTraits::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(TokenTraits::toString(_baseType))); if (_baseType == Token::BytesM) { solAssert(_second == 0, "There should not be a second size argument to type bytesM."); @@ -61,17 +61,17 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con } else if (_baseType == Token::UIntM || _baseType == Token::IntM) { - solAssert(_second == 0, "There should not be a second size argument to type " + string(Token::toString(_baseType)) + "."); + solAssert(_second == 0, "There should not be a second size argument to type " + string(TokenTraits::toString(_baseType)) + "."); solAssert( - _first <= 256 && _first % 8 == 0, - "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "." + _first <= 256 && _first % 8 == 0, + "No elementary type " + string(TokenTraits::toString(_baseType)) + to_string(_first) + "." ); } else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN) { solAssert( _first >= 8 && _first <= 256 && _first % 8 == 0 && _second <= 80, - "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "." + "No elementary type " + string(TokenTraits::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "." ); } m_token = _baseType; @@ -79,38 +79,54 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con m_secondNumber = _second; } -#define T(name, string, precedence) #name, -char const* const Token::m_name[NUM_TOKENS] = +namespace TokenTraits { - TOKEN_LIST(T, T) -}; -#undef T +char const* toString(Token tok) +{ + switch (tok) + { +#define T(name, string, precedence) case Token::name: return string; + TOKEN_LIST(T, T) +#undef T + default: // Token::NUM_TOKENS: + return ""; + } +} -#define T(name, string, precedence) string, -char const* const Token::m_string[NUM_TOKENS] = +char const* name(Token tok) { - TOKEN_LIST(T, T) -}; +#define T(name, string, precedence) #name, + static char const* const names[TokenTraits::count()] = { TOKEN_LIST(T, T) }; #undef T + solAssert(static_cast<size_t>(tok) < TokenTraits::count(), ""); + return names[static_cast<size_t>(tok)]; +} -#define T(name, string, precedence) precedence, -int8_t const Token::m_precedence[NUM_TOKENS] = +std::string friendlyName(Token tok) { - TOKEN_LIST(T, T) -}; -#undef T + char const* ret = toString(tok); + if (ret) + return std::string(ret); + ret = name(tok); + solAssert(ret != nullptr, ""); + return std::string(ret); +} -#define KT(a, b, c) 'T', -#define KK(a, b, c) 'K', -char const Token::m_tokenType[] = +#define T(name, string, precedence) precedence, +int precedence(Token tok) { - TOKEN_LIST(KT, KK) -}; + int8_t const static precs[TokenTraits::count()] = + { + TOKEN_LIST(T, T) + }; + return precs[static_cast<size_t>(tok)]; +} +#undef T -int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) +int parseSize(string::const_iterator _begin, string::const_iterator _end) { try { @@ -123,7 +139,20 @@ int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) } } -tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(string const& _literal) +static Token keywordByName(string const& _name) +{ + // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored + // and keywords to be put inside the keywords variable. +#define KEYWORD(name, string, precedence) {string, Token::name}, +#define TOKEN(name, string, precedence) + static const map<string, Token> keywords({TOKEN_LIST(TOKEN, KEYWORD)}); +#undef KEYWORD +#undef TOKEN + auto it = keywords.find(_name); + return it == keywords.end() ? Token::Identifier : it->second; +} + +tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); if (positionM != _literal.end()) @@ -131,7 +160,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s string baseType(_literal.begin(), positionM); auto positionX = find_if_not(positionM, _literal.end(), ::isdigit); int m = parseSize(positionM, positionX); - Token::Value keyword = keywordByName(baseType); + Token keyword = keywordByName(baseType); if (keyword == Token::Bytes) { if (0 < m && m <= 32 && positionX == _literal.end()) @@ -165,27 +194,14 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s else return make_tuple(Token::FixedMxN, m, n); } - } + } } return make_tuple(Token::Identifier, 0, 0); } return make_tuple(keywordByName(_literal), 0, 0); } -Token::Value Token::keywordByName(string const& _name) -{ - // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored - // and keywords to be put inside the keywords variable. -#define KEYWORD(name, string, precedence) {string, Token::name}, -#define TOKEN(name, string, precedence) - static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)}); -#undef KEYWORD -#undef TOKEN - auto it = keywords.find(_name); - return it == keywords.end() ? Token::Identifier : it->second; -} -#undef KT -#undef KK +} } } diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 4d7a7bc6..81e8dd98 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -45,6 +45,7 @@ #include <libdevcore/Common.h> #include <libsolidity/interface/Exceptions.h> #include <libsolidity/parsing/UndefMacros.h> +#include <iosfwd> namespace dev { @@ -144,11 +145,13 @@ namespace solidity K(Assembly, "assembly", 0) \ K(Break, "break", 0) \ K(Constant, "constant", 0) \ + K(Constructor, "constructor", 0) \ K(Continue, "continue", 0) \ K(Contract, "contract", 0) \ K(Do, "do", 0) \ K(Else, "else", 0) \ K(Enum, "enum", 0) \ + K(Emit, "emit", 0) \ K(Event, "event", 0) \ K(External, "external", 0) \ K(For, "for", 0) \ @@ -173,6 +176,7 @@ namespace solidity K(Return, "return", 0) \ K(Returns, "returns", 0) \ K(Storage, "storage", 0) \ + K(CallData, "calldata", 0) \ K(Struct, "struct", 0) \ K(Throw, "throw", 0) \ K(Using, "using", 0) \ @@ -221,136 +225,138 @@ namespace solidity /* Keywords reserved for future use. */ \ K(Abstract, "abstract", 0) \ K(After, "after", 0) \ + K(Alias, "alias", 0) \ + K(Apply, "apply", 0) \ + K(Auto, "auto", 0) \ K(Case, "case", 0) \ K(Catch, "catch", 0) \ + K(CopyOf, "copyof", 0) \ K(Default, "default", 0) \ + K(Define, "define", 0) \ K(Final, "final", 0) \ + K(Immutable, "immutable", 0) \ + K(Implements, "implements", 0) \ K(In, "in", 0) \ K(Inline, "inline", 0) \ K(Let, "let", 0) \ + K(Macro, "macro", 0) \ K(Match, "match", 0) \ + K(Mutable, "mutable", 0) \ K(NullLiteral, "null", 0) \ K(Of, "of", 0) \ + K(Override, "override", 0) \ + K(Partial, "partial", 0) \ + K(Promise, "promise", 0) \ + K(Reference, "reference", 0) \ K(Relocatable, "relocatable", 0) \ + K(Sealed, "sealed", 0) \ + K(Sizeof, "sizeof", 0) \ K(Static, "static", 0) \ + K(Supports, "supports", 0) \ K(Switch, "switch", 0) \ K(Try, "try", 0) \ K(Type, "type", 0) \ + K(Typedef, "typedef", 0) \ K(TypeOf, "typeof", 0) \ + K(Unchecked, "unchecked", 0) \ + \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ + /* Illegal hex token */ \ + T(IllegalHex, "ILLEGAL_HEX", 0) \ \ /* Scanner-internal use only. */ \ T(Whitespace, NULL, 0) - -class Token -{ -public: - // All token values. - // attention! msvc issue: - // http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 - // @todo: avoid TOKEN_LIST macro +// All token values. +// attention! msvc issue: +// http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 +// @todo: avoid TOKEN_LIST macro +enum class Token : unsigned int { #define T(name, string, precedence) name, - enum Value - { - TOKEN_LIST(T, T) - NUM_TOKENS - }; + TOKEN_LIST(T, T) + NUM_TOKENS #undef T +}; - // @returns a string corresponding to the C++ token name - // (e.g. "LT" for the token LT). - static char const* name(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_name[tok]; - } +namespace TokenTraits +{ + constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); } // Predicates - static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; } - static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; } - static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; } - static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd || - op == Add || op == Mul || op == Equal || op == NotEqual; } - static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; } - static bool isCompareOp(Value op) { return Equal <= op && op <= GreaterThanOrEqual; } - - static Value AssignmentToBinaryOp(Value op) - { - solAssert(isAssignmentOp(op) && op != Assign, ""); - return Value(op + (BitOr - AssignBitOr)); - } + constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; } + constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; } + constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } + constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd || + op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; } + constexpr bool isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; } + constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; } - static bool isBitOp(Value op) { return (BitOr <= op && op <= BitAnd) || op == BitNot; } - static bool isBooleanOp(Value op) { return (Or <= op && op <= And) || op == Not; } - static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; } - static bool isCountOp(Value op) { return op == Inc || op == Dec; } - static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } - static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } - static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } - static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; } - static bool isStateMutabilitySpecifier(Value op) { return op == Pure || op == Constant || op == View || op == Payable; } - static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } - static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } - static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); } + constexpr bool isBitOp(Token op) { return (Token::BitOr <= op && op <= Token::BitAnd) || op == Token::BitNot; } + constexpr bool isBooleanOp(Token op) { return (Token::Or <= op && op <= Token::And) || op == Token::Not; } + constexpr bool isUnaryOp(Token op) { return (Token::Not <= op && op <= Token::Delete) || op == Token::Add || op == Token::Sub; } + constexpr bool isCountOp(Token op) { return op == Token::Inc || op == Token::Dec; } + constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); } + constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; } + constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; } + constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; } - // @returns a string corresponding to the JS token string - // (.e., "<" for the token LT) or NULL if the token doesn't - // have a (unique) string (e.g. an IDENTIFIER). - static char const* toString(Value tok) + constexpr bool isStateMutabilitySpecifier(Token op, bool _allowConstant = true) { - solAssert(tok < NUM_TOKENS, ""); - return m_string[tok]; + return (op == Token::Constant && _allowConstant) + || op == Token::Pure || op == Token::View || op == Token::Payable; } - static std::string friendlyName(Value tok) + constexpr bool isEtherSubdenomination(Token op) { return op == Token::SubWei || op == Token::SubSzabo || op == Token::SubFinney || op == Token::SubEther; } + constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; } + constexpr bool isReservedKeyword(Token op) { return (Token::Abstract <= op && op <= Token::Unchecked); } + + inline Token AssignmentToBinaryOp(Token op) { - char const* ret = toString(tok); - if (ret == nullptr) - { - ret = name(tok); - solAssert(ret != nullptr, ""); - } - return std::string(ret); + solAssert(isAssignmentOp(op) && op != Token::Assign, ""); + return static_cast<Token>(static_cast<int>(op) + (static_cast<int>(Token::BitOr) - static_cast<int>(Token::AssignBitOr))); } // @returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. - static int precedence(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_precedence[tok]; - } + int precedence(Token tok); - static std::tuple<Token::Value, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal); + std::tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal); -private: - // @returns -1 on error (invalid digit or number too large) - static int parseSize(std::string::const_iterator _begin, std::string::const_iterator _end); - // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. - static Token::Value keywordByName(std::string const& _name); - static char const* const m_name[NUM_TOKENS]; - static char const* const m_string[NUM_TOKENS]; - static int8_t const m_precedence[NUM_TOKENS]; - static char const m_tokenType[NUM_TOKENS]; -}; + // @returns a string corresponding to the C++ token name + // (e.g. "LT" for the token LT). + char const* name(Token tok); + + // @returns a string corresponding to the JS token string + // (.e., "<" for the token LT) or NULL if the token doesn't + // have a (unique) string (e.g. an IDENTIFIER). + char const* toString(Token tok); + + std::string friendlyName(Token tok); +} + +inline std::ostream& operator<<(std::ostream& os, Token token) +{ + os << TokenTraits::friendlyName(token); + return os; +} class ElementaryTypeNameToken { public: - ElementaryTypeNameToken(Token::Value _token, unsigned const& _firstNumber, unsigned const& _secondNumber) + ElementaryTypeNameToken(Token _token, unsigned const& _firstNumber, unsigned const& _secondNumber) { assertDetails(_token, _firstNumber, _secondNumber); } unsigned int firstNumber() const { return m_firstNumber; } unsigned int secondNumber() const { return m_secondNumber; } - Token::Value token() const { return m_token; } + Token token() const { return m_token; } + ///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type - std::string toString(bool const& tokenValue = false) const + std::string toString(bool const& tokenValue = false) const { - std::string name = Token::toString(m_token); + std::string name = TokenTraits::toString(m_token); if (tokenValue || (firstNumber() == 0 && secondNumber() == 0)) return name; solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here."); @@ -361,11 +367,11 @@ public: } private: - Token::Value m_token; + Token m_token; unsigned int m_firstNumber; unsigned int m_secondNumber; /// throws if type is not properly sized - void assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second); + void assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second); }; } |