/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ /** * @author Christian * @date 2014 * Solidity parser. */ #include #include #include #include #include #include #include using namespace std; namespace dev { namespace solidity { /// AST node factory that also tracks the begin and end position of an AST node /// while it is being parsed class Parser::ASTNodeFactory { public: explicit ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.position(), -1, _parser.sourceName()) {} ASTNodeFactory(Parser const& _parser, ASTPointer const& _childNode): m_parser(_parser), m_location(_childNode->location()) {} void markEndPosition() { m_location.end = m_parser.endPosition(); } void setLocation(SourceLocation const& _location) { m_location = _location; } void setLocationEmpty() { m_location.end = m_location.start; } /// Set the end position to the one of the given node. void setEndPositionFromNode(ASTPointer const& _node) { m_location.end = _node->location().end; } template ASTPointer createNode(Args&& ... _args) { if (m_location.end < 0) markEndPosition(); return make_shared(m_location, forward(_args)...); } private: Parser const& m_parser; SourceLocation m_location; }; ASTPointer Parser::parse(shared_ptr const& _scanner) { try { m_recursionDepth = 0; m_scanner = _scanner; ASTNodeFactory nodeFactory(*this); vector> nodes; while (m_scanner->currentToken() != Token::EOS) { switch (auto token = m_scanner->currentToken()) { case Token::Pragma: nodes.push_back(parsePragmaDirective()); break; case Token::Import: nodes.push_back(parseImportDirective()); break; case Token::Interface: case Token::Contract: case Token::Library: nodes.push_back(parseContractDefinition(token)); break; default: fatalParserError(string("Expected pragma, import directive or contract/interface/library definition.")); } } solAssert(m_recursionDepth == 0, ""); return nodeFactory.createNode(nodes); } catch (FatalError const&) { if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. return nullptr; } } ASTPointer Parser::parsePragmaDirective() { RecursionGuard recursionGuard(*this); // pragma anything* ; // Currently supported: // pragma solidity ^0.4.0 || ^0.3.0; ASTNodeFactory nodeFactory(*this); expectToken(Token::Pragma); vector literals; vector tokens; do { Token::Value 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); literals.push_back(literal); tokens.push_back(token); } m_scanner->next(); } while (m_scanner->currentToken() != Token::Semicolon && m_scanner->currentToken() != Token::EOS); nodeFactory.markEndPosition(); expectToken(Token::Semicolon); return nodeFactory.createNode(tokens, literals); } ASTPointer Parser::parseImportDirective() { RecursionGuard recursionGuard(*this); // import "abc" [as x]; // import * as x from "abc"; // import {a as b, c} from "abc"; ASTNodeFactory nodeFactory(*this); expectToken(Token::Import); ASTPointer path; ASTPointer unitAlias = make_shared(); vector, ASTPointer>> symbolAliases; if (m_scanner->currentToken() == Token::StringLiteral) { path = getLiteralAndAdvance(); if (m_scanner->currentToken() == Token::As) { m_scanner->next(); unitAlias = expectIdentifierToken(); } } else { if (m_scanner->currentToken() == Token::LBrace) { m_scanner->next(); while (true) { ASTPointer id = parseIdentifier(); ASTPointer alias; if (m_scanner->currentToken() == Token::As) { expectToken(Token::As); alias = expectIdentifierToken(); } symbolAliases.push_back(make_pair(move(id), move(alias))); if (m_scanner->currentToken() != Token::Comma) break; m_scanner->next(); } expectToken(Token::RBrace); } else if (m_scanner->currentToken() == Token::Mul) { m_scanner->next(); expectToken(Token::As); unitAlias = expectIdentifierToken(); } else fatalParserError("Expected string literal (path), \"*\" or alias list."); // "from" is not a keyword but parsed as an identifier because of backwards // compatibility and because it is a really common word. if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from") fatalParserError("Expected \"from\"."); m_scanner->next(); if (m_scanner->currentToken() != Token::StringLiteral) fatalParserError("Expected import path."); path = getLiteralAndAdvance(); } nodeFactory.markEndPosition(); expectToken(Token::Semicolon); return nodeFactory.createNode(path, unitAlias, move(symbolAliases)); } ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token) { switch(_token) { case Token::Interface: return ContractDefinition::ContractKind::Interface; case Token::Contract: return ContractDefinition::ContractKind::Contract; case Token::Library: return ContractDefinition::ContractKind::Library; default: fatalParserError("Unsupported contract type."); } // FIXME: fatalParserError is not considered as throwing here return ContractDefinition::ContractKind::Contract; } ASTPointer Parser::parseContractDefinition(Token::Value _expectedKind) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared(m_scanner->currentCommentLiteral()); expectToken(_expectedKind); ASTPointer name = expectIdentifierToken(); vector> baseContracts; if (m_scanner->currentToken() == Token::Is) do { m_scanner->next(); baseContracts.push_back(parseInheritanceSpecifier()); } while (m_scanner->currentToken() == Token::Comma); vector> subNodes; expectToken(Token::LBrace); while (true) { Token::Value currentTokenValue = m_scanner->currentToken(); if (currentTokenValue == Token::RBrace) break; else if ( currentTokenValue == Token::Function || (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "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())); else if (currentTokenValue == Token::Struct) subNodes.push_back(parseStructDefinition()); else if (currentTokenValue == Token::Enum) subNodes.push_back(parseEnumDefinition()); else if ( currentTokenValue == Token::Identifier || currentTokenValue == Token::Mapping || Token::isElementaryTypeName(currentTokenValue) ) { VarDeclParserOptions options; options.isStateVariable = true; options.allowInitialValue = true; subNodes.push_back(parseVariableDeclaration(options)); expectToken(Token::Semicolon); } else if (currentTokenValue == Token::Modifier) subNodes.push_back(parseModifierDefinition()); else if (currentTokenValue == Token::Event) subNodes.push_back(parseEventDefinition()); else if (currentTokenValue == Token::Using) subNodes.push_back(parseUsingDirective()); else fatalParserError(string("Function, variable, struct or modifier declaration expected.")); } nodeFactory.markEndPosition(); expectToken(Token::RBrace); return nodeFactory.createNode( name, docString, baseContracts, subNodes, tokenToContractKind(_expectedKind) ); } ASTPointer Parser::parseInheritanceSpecifier() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name(parseUserDefinedTypeName()); unique_ptr>> arguments; if (m_scanner->currentToken() == Token::LParen) { m_scanner->next(); arguments.reset(new vector>(parseFunctionCallListArguments())); nodeFactory.markEndPosition(); expectToken(Token::RParen); } else nodeFactory.setEndPositionFromNode(name); return nodeFactory.createNode(name, std::move(arguments)); } Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) { 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."); m_scanner->next(); return visibility; } StateMutability Parser::parseStateMutability(Token::Value _token) { 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."); m_scanner->next(); return stateMutability; } Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( bool _forceEmptyName, bool _allowModifiers, ASTString const* _contractName ) { RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; result.isConstructor = false; if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "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) result.name = make_shared(); 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(); if (_allowModifiers && token == Token::Identifier) { // If the name is empty (and this is not a constructor), // then this can either be a modifier (fallback function declaration) // or the name of the state variable (function type name plus variable). if ((result.name->empty() && !result.isConstructor) && ( m_scanner->peekNextToken() == Token::Semicolon || m_scanner->peekNextToken() == Token::Assign )) // Variable declaration, break here. break; else result.modifiers.push_back(parseModifierInvocation()); } else if (Token::isVisibilitySpecifier(token)) { if (result.visibility != Declaration::Visibility::Default) { // There is the special case of a public state variable of function type. // Detect this and return early. if ( (result.visibility == Declaration::Visibility::External || result.visibility == Declaration::Visibility::Internal) && result.modifiers.empty() && (result.name->empty() && !result.isConstructor) ) break; parserError(string( "Visibility already specified as \"" + Declaration::visibilityToString(result.visibility) + "\"." )); m_scanner->next(); } else result.visibility = parseVisibilitySpecifier(token); } else if (Token::isStateMutabilitySpecifier(token)) { if (result.stateMutability != StateMutability::NonPayable) { parserError(string( "State mutability already specified as \"" + stateMutabilityToString(result.stateMutability) + "\"." )); m_scanner->next(); } else result.stateMutability = parseStateMutability(token); } else break; } if (m_scanner->currentToken() == Token::Returns) { bool const permitEmptyParameterList = false; m_scanner->next(); result.returnParameters = parseParameterList(options, permitEmptyParameterList); } else result.returnParameters = createEmptyParameterList(); return result; } ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->currentCommentLiteral() != "") docstring = make_shared(m_scanner->currentCommentLiteral()); FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName); if ( header.isConstructor || !header.modifiers.empty() || !header.name->empty() || m_scanner->currentToken() == Token::Semicolon || m_scanner->currentToken() == Token::LBrace ) { // this has to be a function ASTPointer block = ASTPointer(); nodeFactory.markEndPosition(); if (m_scanner->currentToken() != Token::Semicolon) { block = parseBlock(); nodeFactory.setEndPositionFromNode(block); } else m_scanner->next(); // just consume the ';' return nodeFactory.createNode( header.name, header.visibility, header.stateMutability, header.isConstructor, docstring, header.parameters, header.modifiers, header.returnParameters, block ); } else { // this has to be a state variable ASTPointer type = nodeFactory.createNode( header.parameters, header.returnParameters, header.visibility, header.stateMutability ); type = parseTypeNameSuffix(type, nodeFactory); VarDeclParserOptions options; options.isStateVariable = true; options.allowInitialValue = true; auto node = parseVariableDeclaration(options, type); expectToken(Token::Semicolon); return node; } } ASTPointer Parser::parseStructDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Struct); ASTPointer name = expectIdentifierToken(); vector> members; expectToken(Token::LBrace); while (m_scanner->currentToken() != Token::RBrace) { members.push_back(parseVariableDeclaration()); expectToken(Token::Semicolon); } nodeFactory.markEndPosition(); expectToken(Token::RBrace); return nodeFactory.createNode(name, members); } ASTPointer Parser::parseEnumValue() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); return nodeFactory.createNode(expectIdentifierToken()); } ASTPointer Parser::parseEnumDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Enum); ASTPointer name = expectIdentifierToken(); vector> members; expectToken(Token::LBrace); while (m_scanner->currentToken() != Token::RBrace) { members.push_back(parseEnumValue()); if (m_scanner->currentToken() == Token::RBrace) break; expectToken(Token::Comma); if (m_scanner->currentToken() != Token::Identifier) fatalParserError(string("Expected Identifier after ','")); } if (members.size() == 0) parserError({"enum with no members is not allowed."}); nodeFactory.markEndPosition(); expectToken(Token::RBrace); return nodeFactory.createNode(name, members); } ASTPointer Parser::parseVariableDeclaration( VarDeclParserOptions const& _options, ASTPointer const& _lookAheadArrayType ) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadArrayType ? ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this); ASTPointer type; if (_lookAheadArrayType) type = _lookAheadArrayType; else { type = parseTypeName(_options.allowVar); if (type != nullptr) nodeFactory.setEndPositionFromNode(type); } bool isIndexed = false; bool isDeclaredConst = false; Declaration::Visibility visibility(Declaration::Visibility::Default); VariableDeclaration::Location location = VariableDeclaration::Location::Default; ASTPointer identifier; while (true) { Token::Value token = m_scanner->currentToken(); if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token)) { if (visibility != Declaration::Visibility::Default) { parserError(string( "Visibility already specified as \"" + Declaration::visibilityToString(visibility) + "\"." )); m_scanner->next(); } else visibility = parseVisibilitySpecifier(token); } else { if (_options.allowIndexed && token == Token::Indexed) isIndexed = true; else if (token == Token::Constant) isDeclaredConst = true; else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token)) { if (location != VariableDeclaration::Location::Default) 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 ); } else break; m_scanner->next(); } } nodeFactory.markEndPosition(); if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier) { identifier = make_shared(""); solAssert(!_options.allowVar, ""); // allowEmptyName && allowVar makes no sense if (type) nodeFactory.setEndPositionFromNode(type); // if type is null this has already caused an error } else identifier = expectIdentifierToken(); ASTPointer value; if (_options.allowInitialValue) { if (m_scanner->currentToken() == Token::Assign) { m_scanner->next(); value = parseExpression(); nodeFactory.setEndPositionFromNode(value); } } return nodeFactory.createNode( type, identifier, value, visibility, _options.isStateVariable, isIndexed, isDeclaredConst, location ); } ASTPointer Parser::parseModifierDefinition() { RecursionGuard recursionGuard(*this); ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; }); m_insideModifier = true; ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->currentCommentLiteral() != "") docstring = make_shared(m_scanner->currentCommentLiteral()); expectToken(Token::Modifier); ASTPointer name(expectIdentifierToken()); ASTPointer parameters; if (m_scanner->currentToken() == Token::LParen) { VarDeclParserOptions options; options.allowIndexed = true; options.allowLocationSpecifier = true; parameters = parseParameterList(options); } else parameters = createEmptyParameterList(); ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); return nodeFactory.createNode(name, docstring, parameters, block); } ASTPointer Parser::parseEventDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer docstring; if (m_scanner->currentCommentLiteral() != "") docstring = make_shared(m_scanner->currentCommentLiteral()); expectToken(Token::Event); ASTPointer name(expectIdentifierToken()); VarDeclParserOptions options; options.allowIndexed = true; ASTPointer parameters = parseParameterList(options); bool anonymous = false; if (m_scanner->currentToken() == Token::Anonymous) { anonymous = true; m_scanner->next(); } nodeFactory.markEndPosition(); expectToken(Token::Semicolon); return nodeFactory.createNode(name, docstring, parameters, anonymous); } ASTPointer Parser::parseUsingDirective() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Using); ASTPointer library(parseUserDefinedTypeName()); ASTPointer typeName; expectToken(Token::For); if (m_scanner->currentToken() == Token::Mul) m_scanner->next(); else typeName = parseTypeName(false); nodeFactory.markEndPosition(); expectToken(Token::Semicolon); return nodeFactory.createNode(library, typeName); } ASTPointer Parser::parseModifierInvocation() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name(parseIdentifier()); unique_ptr>> arguments; if (m_scanner->currentToken() == Token::LParen) { m_scanner->next(); arguments.reset(new vector>(parseFunctionCallListArguments())); nodeFactory.markEndPosition(); expectToken(Token::RParen); } else nodeFactory.setEndPositionFromNode(name); return nodeFactory.createNode(name, move(arguments)); } ASTPointer Parser::parseIdentifier() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); return nodeFactory.createNode(expectIdentifierToken()); } ASTPointer Parser::parseUserDefinedTypeName() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); vector identifierPath{*expectIdentifierToken()}; while (m_scanner->currentToken() == Token::Period) { m_scanner->next(); nodeFactory.markEndPosition(); identifierPath.push_back(*expectIdentifierToken()); } return nodeFactory.createNode(identifierPath); } ASTPointer Parser::parseTypeNameSuffix(ASTPointer type, ASTNodeFactory& nodeFactory) { RecursionGuard recursionGuard(*this); while (m_scanner->currentToken() == Token::LBrack) { m_scanner->next(); ASTPointer length; if (m_scanner->currentToken() != Token::RBrack) length = parseExpression(); nodeFactory.markEndPosition(); expectToken(Token::RBrack); type = nodeFactory.createNode(type, length); } return type; } ASTPointer Parser::parseTypeName(bool _allowVar) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer type; Token::Value token = m_scanner->currentToken(); if (Token::isElementaryTypeName(token)) { unsigned firstSize; unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize); type = ASTNodeFactory(*this).createNode(elemTypeName); m_scanner->next(); } else if (token == Token::Var) { if (!_allowVar) parserError(string("Expected explicit type name.")); m_scanner->next(); } else if (token == Token::Function) type = parseFunctionType(); else if (token == Token::Mapping) type = parseMapping(); else if (token == Token::Identifier) type = parseUserDefinedTypeName(); else fatalParserError(string("Expected type name")); if (type) // Parse "[...]" postfixes for arrays. type = parseTypeNameSuffix(type, nodeFactory); return type; } ASTPointer Parser::parseFunctionType() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); FunctionHeaderParserResult header = parseFunctionHeader(true, false); solAssert(!header.isConstructor, "Tried to parse type as constructor."); return nodeFactory.createNode( header.parameters, header.returnParameters, header.visibility, header.stateMutability ); } ASTPointer Parser::parseMapping() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Mapping); expectToken(Token::LParen); ASTPointer keyType; Token::Value token = m_scanner->currentToken(); if (!Token::isElementaryTypeName(token)) fatalParserError(string("Expected elementary type name for mapping key type")); unsigned firstSize; unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize); keyType = ASTNodeFactory(*this).createNode(elemTypeName); m_scanner->next(); expectToken(Token::Arrow); bool const allowVar = false; ASTPointer valueType = parseTypeName(allowVar); nodeFactory.markEndPosition(); expectToken(Token::RParen); return nodeFactory.createNode(keyType, valueType); } ASTPointer Parser::parseParameterList( VarDeclParserOptions const& _options, bool _allowEmpty ) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); vector> parameters; VarDeclParserOptions options(_options); options.allowEmptyName = true; expectToken(Token::LParen); if (!_allowEmpty || m_scanner->currentToken() != Token::RParen) { parameters.push_back(parseVariableDeclaration(options)); while (m_scanner->currentToken() != Token::RParen) { if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen) fatalParserError("Unexpected trailing comma in parameter list."); expectToken(Token::Comma); parameters.push_back(parseVariableDeclaration(options)); } } nodeFactory.markEndPosition(); m_scanner->next(); return nodeFactory.createNode(parameters); } ASTPointer Parser::parseBlock(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::LBrace); vector> statements; while (m_scanner->currentToken() != Token::RBrace) statements.push_back(parseStatement()); nodeFactory.markEndPosition(); expectToken(Token::RBrace); return nodeFactory.createNode(_docString, statements); } ASTPointer Parser::parseStatement() { RecursionGuard recursionGuard(*this); ASTPointer docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared(m_scanner->currentCommentLiteral()); ASTPointer statement; switch (m_scanner->currentToken()) { case Token::If: return parseIfStatement(docString); case Token::While: return parseWhileStatement(docString); case Token::Do: return parseDoWhileStatement(docString); case Token::For: return parseForStatement(docString); case Token::LBrace: return parseBlock(docString); // starting from here, all statements must be terminated by a semicolon case Token::Continue: statement = ASTNodeFactory(*this).createNode(docString); m_scanner->next(); break; case Token::Break: statement = ASTNodeFactory(*this).createNode(docString); m_scanner->next(); break; case Token::Return: { ASTNodeFactory nodeFactory(*this); ASTPointer expression; if (m_scanner->next() != Token::Semicolon) { expression = parseExpression(); nodeFactory.setEndPositionFromNode(expression); } statement = nodeFactory.createNode(docString, expression); break; } case Token::Throw: { statement = ASTNodeFactory(*this).createNode(docString); m_scanner->next(); break; } case Token::Assembly: return parseInlineAssembly(docString); case Token::Identifier: if (m_scanner->currentLiteral() == "emit") statement = parseEmitStatement(docString); else if (m_insideModifier && m_scanner->currentLiteral() == "_") { statement = ASTNodeFactory(*this).createNode(docString); m_scanner->next(); } else statement = parseSimpleStatement(docString); break; default: statement = parseSimpleStatement(docString); break; } expectToken(Token::Semicolon); return statement; } ASTPointer Parser::parseInlineAssembly(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Assembly); if (m_scanner->currentToken() == Token::StringLiteral) { if (m_scanner->currentLiteral() != "evmasm") fatalParserError("Only \"evmasm\" supported."); m_scanner->next(); } assembly::Parser asmParser(m_errorReporter); shared_ptr block = asmParser.parse(m_scanner, true); nodeFactory.markEndPosition(); return nodeFactory.createNode(_docString, block); } ASTPointer Parser::parseIfStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::If); expectToken(Token::LParen); ASTPointer condition = parseExpression(); expectToken(Token::RParen); ASTPointer trueBody = parseStatement(); ASTPointer falseBody; if (m_scanner->currentToken() == Token::Else) { m_scanner->next(); falseBody = parseStatement(); nodeFactory.setEndPositionFromNode(falseBody); } else nodeFactory.setEndPositionFromNode(trueBody); return nodeFactory.createNode(_docString, condition, trueBody, falseBody); } ASTPointer Parser::parseWhileStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::While); expectToken(Token::LParen); ASTPointer condition = parseExpression(); expectToken(Token::RParen); ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); return nodeFactory.createNode(_docString, condition, body, false); } ASTPointer Parser::parseDoWhileStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Do); ASTPointer body = parseStatement(); expectToken(Token::While); expectToken(Token::LParen); ASTPointer condition = parseExpression(); expectToken(Token::RParen); nodeFactory.markEndPosition(); expectToken(Token::Semicolon); return nodeFactory.createNode(_docString, condition, body, true); } ASTPointer Parser::parseForStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer initExpression; ASTPointer conditionExpression; ASTPointer loopExpression; expectToken(Token::For); expectToken(Token::LParen); // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen? if (m_scanner->currentToken() != Token::Semicolon) initExpression = parseSimpleStatement(ASTPointer()); expectToken(Token::Semicolon); if (m_scanner->currentToken() != Token::Semicolon) conditionExpression = parseExpression(); expectToken(Token::Semicolon); if (m_scanner->currentToken() != Token::RParen) loopExpression = parseExpressionStatement(ASTPointer()); expectToken(Token::RParen); ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); return nodeFactory.createNode( _docString, initExpression, conditionExpression, loopExpression, body ); } ASTPointer Parser::parseEmitStatement(ASTPointer const& _docString) { ASTNodeFactory nodeFactory(*this); m_scanner->next(); ASTNodeFactory eventCallNodeFactory(*this); if (m_scanner->currentToken() != Token::Identifier) fatalParserError("Expected event name or path."); IndexAccessedPath iap; while (true) { iap.path.push_back(parseIdentifier()); if (m_scanner->currentToken() != Token::Period) break; m_scanner->next(); }; auto eventName = expressionFromIndexAccessStructure(iap); expectToken(Token::LParen); vector> arguments; vector> names; std::tie(arguments, names) = parseFunctionCallArguments(); eventCallNodeFactory.markEndPosition(); nodeFactory.markEndPosition(); expectToken(Token::RParen); auto eventCall = eventCallNodeFactory.createNode(eventName, arguments, names); auto statement = nodeFactory.createNode(_docString, eventCall); return statement; } ASTPointer Parser::parseSimpleStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); // These two cases are very hard to distinguish: // x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9; // In the first case, x is a type name, in the second it is the name of a variable. // As an extension, we can even have: // `x.y.z[1][2] a;` and `x.y.z[1][2] = 10;` // Where in the first, x.y.z leads to a type name where in the second, it accesses structs. switch (peekStatementType()) { case LookAheadInfo::VariableDeclarationStatement: return parseVariableDeclarationStatement(_docString); case LookAheadInfo::ExpressionStatement: return parseExpressionStatement(_docString); default: break; } // At this point, we have 'Identifier "["' or 'Identifier "." Identifier' or 'ElementoryTypeName "["'. // We parse '(Identifier ("." Identifier)* |ElementaryTypeName) ( "[" Expression "]" )*' // until we can decide whether to hand this over to ExpressionStatement or create a // VariableDeclarationStatement out of it. IndexAccessedPath iap = parseIndexAccessedPath(); if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken())) return parseVariableDeclarationStatement(_docString, typeNameFromIndexAccessStructure(iap)); else return parseExpressionStatement(_docString, expressionFromIndexAccessStructure(iap)); } ASTPointer Parser::parseVariableDeclarationStatement( ASTPointer const& _docString, ASTPointer const& _lookAheadArrayType ) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); if (_lookAheadArrayType) nodeFactory.setLocation(_lookAheadArrayType->location()); vector> variables; ASTPointer value; if ( !_lookAheadArrayType && m_scanner->currentToken() == Token::Var && m_scanner->peekNextToken() == Token::LParen ) { // Parse `var (a, b, ,, c) = ...` into a single VariableDeclarationStatement with multiple variables. m_scanner->next(); m_scanner->next(); if (m_scanner->currentToken() != Token::RParen) while (true) { ASTPointer var; if ( m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != Token::RParen ) { ASTNodeFactory varDeclNodeFactory(*this); varDeclNodeFactory.markEndPosition(); ASTPointer name = expectIdentifierToken(); var = varDeclNodeFactory.createNode( ASTPointer(), name, ASTPointer(), VariableDeclaration::Visibility::Default ); } variables.push_back(var); if (m_scanner->currentToken() == Token::RParen) break; else expectToken(Token::Comma); } nodeFactory.markEndPosition(); m_scanner->next(); } else { VarDeclParserOptions options; options.allowVar = true; options.allowLocationSpecifier = true; variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType)); nodeFactory.setEndPositionFromNode(variables.back()); } if (m_scanner->currentToken() == Token::Assign) { m_scanner->next(); value = parseExpression(); nodeFactory.setEndPositionFromNode(value); } return nodeFactory.createNode(_docString, variables, value); } ASTPointer Parser::parseExpressionStatement( ASTPointer const& _docString, ASTPointer const& _lookAheadIndexAccessStructure ) { RecursionGuard recursionGuard(*this); ASTPointer expression = parseExpression(_lookAheadIndexAccessStructure); return ASTNodeFactory(*this, expression).createNode(_docString, expression); } ASTPointer Parser::parseExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { RecursionGuard recursionGuard(*this); ASTPointer expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure); if (Token::isAssignmentOp(m_scanner->currentToken())) { Token::Value assignmentOperator = expectAssignmentOperator(); ASTPointer rightHandSide = parseExpression(); ASTNodeFactory nodeFactory(*this, expression); nodeFactory.setEndPositionFromNode(rightHandSide); return nodeFactory.createNode(expression, assignmentOperator, rightHandSide); } else if (m_scanner->currentToken() == Token::Value::Conditional) { m_scanner->next(); ASTPointer trueExpression = parseExpression(); expectToken(Token::Colon); ASTPointer falseExpression = parseExpression(); ASTNodeFactory nodeFactory(*this, expression); nodeFactory.setEndPositionFromNode(falseExpression); return nodeFactory.createNode(expression, trueExpression, falseExpression); } else return expression; } ASTPointer Parser::parseBinaryExpression( int _minPrecedence, ASTPointer const& _lookAheadIndexAccessStructure ) { RecursionGuard recursionGuard(*this); ASTPointer expression = parseUnaryExpression(_lookAheadIndexAccessStructure); ASTNodeFactory nodeFactory(*this, expression); int precedence = Token::precedence(m_scanner->currentToken()); for (; precedence >= _minPrecedence; --precedence) while (Token::precedence(m_scanner->currentToken()) == precedence) { Token::Value op = m_scanner->currentToken(); m_scanner->next(); ASTPointer right = parseBinaryExpression(precedence + 1); nodeFactory.setEndPositionFromNode(right); expression = nodeFactory.createNode(expression, op, right); } return expression; } ASTPointer Parser::parseUnaryExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ? ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this); Token::Value token = m_scanner->currentToken(); if (!_lookAheadIndexAccessStructure && (Token::isUnaryOp(token) || Token::isCountOp(token))) { // prefix expression m_scanner->next(); ASTPointer subExpression = parseUnaryExpression(); nodeFactory.setEndPositionFromNode(subExpression); return nodeFactory.createNode(token, subExpression, true); } else { // potential postfix expression ASTPointer subExpression = parseLeftHandSideExpression(_lookAheadIndexAccessStructure); token = m_scanner->currentToken(); if (!Token::isCountOp(token)) return subExpression; nodeFactory.markEndPosition(); m_scanner->next(); return nodeFactory.createNode(token, subExpression, false); } } ASTPointer Parser::parseLeftHandSideExpression( ASTPointer const& _lookAheadIndexAccessStructure ) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ? ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this); ASTPointer expression; if (_lookAheadIndexAccessStructure) expression = _lookAheadIndexAccessStructure; else if (m_scanner->currentToken() == Token::New) { expectToken(Token::New); ASTPointer typeName(parseTypeName(false)); if (typeName) nodeFactory.setEndPositionFromNode(typeName); else nodeFactory.markEndPosition(); expression = nodeFactory.createNode(typeName); } else expression = parsePrimaryExpression(); while (true) { switch (m_scanner->currentToken()) { case Token::LBrack: { m_scanner->next(); ASTPointer index; if (m_scanner->currentToken() != Token::RBrack) index = parseExpression(); nodeFactory.markEndPosition(); expectToken(Token::RBrack); expression = nodeFactory.createNode(expression, index); break; } case Token::Period: { m_scanner->next(); nodeFactory.markEndPosition(); expression = nodeFactory.createNode(expression, expectIdentifierToken()); break; } case Token::LParen: { m_scanner->next(); vector> arguments; vector> names; std::tie(arguments, names) = parseFunctionCallArguments(); nodeFactory.markEndPosition(); expectToken(Token::RParen); expression = nodeFactory.createNode(expression, arguments, names); break; } default: return expression; } } } ASTPointer Parser::parsePrimaryExpression() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->currentToken(); ASTPointer expression; switch (token) { case Token::TrueLiteral: case Token::FalseLiteral: nodeFactory.markEndPosition(); expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; case Token::Number: if (Token::isEtherSubdenomination(m_scanner->peekNextToken())) { ASTPointer literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); Literal::SubDenomination subdenomination = static_cast(m_scanner->currentToken()); m_scanner->next(); expression = nodeFactory.createNode(token, literal, subdenomination); } else if (Token::isTimeSubdenomination(m_scanner->peekNextToken())) { ASTPointer literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); Literal::SubDenomination subdenomination = static_cast(m_scanner->currentToken()); m_scanner->next(); expression = nodeFactory.createNode(token, literal, subdenomination); } else { nodeFactory.markEndPosition(); expression = nodeFactory.createNode(token, getLiteralAndAdvance()); } break; case Token::StringLiteral: nodeFactory.markEndPosition(); expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; case Token::Identifier: nodeFactory.markEndPosition(); expression = nodeFactory.createNode(getLiteralAndAdvance()); break; case Token::LParen: case Token::LBrack: { // Tuple/parenthesized expression or inline array/bracketed expression. // Special cases: ()/[] is empty tuple/array type, (x) is not a real tuple, // (x,) is one-dimensional tuple, elements in arrays cannot be left out, only in tuples. m_scanner->next(); vector> components; Token::Value oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack); bool isArray = (token == Token::LBrack); if (m_scanner->currentToken() != oppositeToken) while (true) { if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken) components.push_back(parseExpression()); else if (isArray) parserError("Expected expression (inline array elements cannot be omitted)."); else components.push_back(ASTPointer()); if (m_scanner->currentToken() == oppositeToken) break; expectToken(Token::Comma); } nodeFactory.markEndPosition(); expectToken(oppositeToken); expression = nodeFactory.createNode(components, isArray); break; } default: if (Token::isElementaryTypeName(token)) { //used for casts unsigned firstSize; unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); ElementaryTypeNameToken elementaryExpression(m_scanner->currentToken(), firstSize, secondSize); expression = nodeFactory.createNode(elementaryExpression); m_scanner->next(); } else fatalParserError(string("Expected primary expression.")); break; } return expression; } vector> Parser::parseFunctionCallListArguments() { RecursionGuard recursionGuard(*this); vector> arguments; if (m_scanner->currentToken() != Token::RParen) { arguments.push_back(parseExpression()); while (m_scanner->currentToken() != Token::RParen) { expectToken(Token::Comma); arguments.push_back(parseExpression()); } } return arguments; } pair>, vector>> Parser::parseFunctionCallArguments() { RecursionGuard recursionGuard(*this); pair>, vector>> ret; Token::Value token = m_scanner->currentToken(); if (token == Token::LBrace) { // call({arg1 : 1, arg2 : 2 }) expectToken(Token::LBrace); bool first = true; while (m_scanner->currentToken() != Token::RBrace) { if (!first) expectToken(Token::Comma); ret.second.push_back(expectIdentifierToken()); expectToken(Token::Colon); ret.first.push_back(parseExpression()); if ( m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RBrace ) { parserError("Unexpected trailing comma."); m_scanner->next(); } first = false; } expectToken(Token::RBrace); } else ret.first = parseFunctionCallListArguments(); return ret; } 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 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); if (token == Token::Mapping || token == Token::Function || token == Token::Var) return LookAheadInfo::VariableDeclarationStatement; if (mightBeTypeName) { Token::Value next = m_scanner->peekNextToken(); if (next == Token::Identifier || Token::isLocationSpecifier(next)) return LookAheadInfo::VariableDeclarationStatement; if (next == Token::LBrack || next == Token::Period) return LookAheadInfo::IndexAccessStructure; } return LookAheadInfo::ExpressionStatement; } Parser::IndexAccessedPath Parser::parseIndexAccessedPath() { IndexAccessedPath iap; if (m_scanner->currentToken() == Token::Identifier) { iap.path.push_back(parseIdentifier()); while (m_scanner->currentToken() == Token::Period) { m_scanner->next(); iap.path.push_back(parseIdentifier()); } } else { unsigned firstNum; unsigned secondNum; tie(firstNum, secondNum) = m_scanner->currentTokenInfo(); ElementaryTypeNameToken elemToken(m_scanner->currentToken(), firstNum, secondNum); iap.path.push_back(ASTNodeFactory(*this).createNode(elemToken)); m_scanner->next(); } while (m_scanner->currentToken() == Token::LBrack) { expectToken(Token::LBrack); ASTPointer index; if (m_scanner->currentToken() != Token::RBrack) index = parseExpression(); SourceLocation indexLocation = iap.path.front()->location(); indexLocation.end = endPosition(); iap.indices.push_back(make_pair(index, indexLocation)); expectToken(Token::RBrack); } return iap; } ASTPointer Parser::typeNameFromIndexAccessStructure(Parser::IndexAccessedPath const& _iap) { solAssert(!_iap.path.empty(), ""); RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); SourceLocation location = _iap.path.front()->location(); location.end = _iap.path.back()->location().end; nodeFactory.setLocation(location); ASTPointer type; if (auto typeName = dynamic_cast(_iap.path.front().get())) { solAssert(_iap.path.size() == 1, ""); type = nodeFactory.createNode(typeName->typeName()); } else { vector path; for (auto const& el: _iap.path) path.push_back(dynamic_cast(*el).name()); type = nodeFactory.createNode(path); } for (auto const& lengthExpression: _iap.indices) { nodeFactory.setLocation(lengthExpression.second); type = nodeFactory.createNode(type, lengthExpression.first); } return type; } ASTPointer Parser::expressionFromIndexAccessStructure( Parser::IndexAccessedPath const& _iap ) { solAssert(!_iap.path.empty(), ""); RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this, _iap.path.front()); ASTPointer expression(_iap.path.front()); for (size_t i = 1; i < _iap.path.size(); ++i) { SourceLocation location(_iap.path.front()->location()); location.end = _iap.path[i]->location().end; nodeFactory.setLocation(location); Identifier const& identifier = dynamic_cast(*_iap.path[i]); expression = nodeFactory.createNode( expression, make_shared(identifier.name()) ); } for (auto const& index: _iap.indices) { nodeFactory.setLocation(index.second); expression = nodeFactory.createNode(expression, index.first); } return expression; } ASTPointer Parser::createEmptyParameterList() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); return nodeFactory.createNode(vector>()); } string Parser::currentTokenName() { Token::Value token = m_scanner->currentToken(); if (Token::isElementaryTypeName(token)) //for the sake of accuracy in reporting { ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken(); return elemTypeName.toString(); } else return Token::name(token); } Token::Value Parser::expectAssignmentOperator() { Token::Value op = m_scanner->currentToken(); if (!Token::isAssignmentOp(op)) fatalParserError( string("Expected assignment operator, got '") + currentTokenName() + string("'") ); m_scanner->next(); return op; } ASTPointer Parser::expectIdentifierToken() { Token::Value id = m_scanner->currentToken(); if (id != Token::Identifier) fatalParserError( string("Expected identifier, got '") + currentTokenName() + string("'") ); return getLiteralAndAdvance(); } ASTPointer Parser::getLiteralAndAdvance() { ASTPointer identifier = make_shared(m_scanner->currentLiteral()); m_scanner->next(); return identifier; } } }