diff options
author | Christian <c@ethdev.com> | 2014-10-09 02:53:50 +0800 |
---|---|---|
committer | Christian <c@ethdev.com> | 2014-10-09 02:53:50 +0800 |
commit | 0a1ebe4f516a5c1e8ebc12798a94529bdda9b6df (patch) | |
tree | dcc068f33caaa625872b1c1e612f6c362b13145f | |
parent | 56e9cc8db71f8af949123e13e6a97cc056cf766d (diff) | |
download | dexon-solidity-0a1ebe4f516a5c1e8ebc12798a94529bdda9b6df.tar.gz dexon-solidity-0a1ebe4f516a5c1e8ebc12798a94529bdda9b6df.tar.zst dexon-solidity-0a1ebe4f516a5c1e8ebc12798a94529bdda9b6df.zip |
Parse everything up to function bodies and report parser errors with location.
-rw-r--r-- | AST.h | 91 | ||||
-rw-r--r-- | BaseTypes.h | 17 | ||||
-rw-r--r-- | Parser.cpp | 137 | ||||
-rw-r--r-- | Parser.h | 8 | ||||
-rw-r--r-- | Scanner.cpp | 48 | ||||
-rw-r--r-- | Scanner.h | 16 | ||||
-rw-r--r-- | Token.h | 1 | ||||
-rw-r--r-- | grammar.txt | 14 |
8 files changed, 264 insertions, 68 deletions
@@ -32,6 +32,9 @@ namespace dev { namespace solidity { +// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do +// not do reference counting but point to a special memory area that is completely released +// explicitly. template <class T> using ptr = std::shared_ptr<T>; template <class T> @@ -47,9 +50,11 @@ class Expression; class ASTNode { public: - explicit ASTNode(const Location& _location) + explicit ASTNode(Location const& _location) : m_location(_location) {} + + Location getLocation() const { return m_location; } private: Location m_location; }; @@ -57,13 +62,12 @@ private: class ContractDefinition : public ASTNode { public: - ContractDefinition(const Location& _location, - const std::string& _name, - const vecptr<StructDefinition>& _definedStructs, - const vecptr<VariableDeclaration>& _stateVariables, - const vecptr<FunctionDefinition>& _definedFunctions) - : ASTNode(_location), - m_name(_name), + ContractDefinition(Location const& _location, + std::string const& _name, + vecptr<StructDefinition> const& _definedStructs, + vecptr<VariableDeclaration> const& _stateVariables, + vecptr<FunctionDefinition> const& _definedFunctions) + : ASTNode(_location), m_name(_name), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions) @@ -78,33 +82,61 @@ private: class StructDefinition : public ASTNode { +public: + StructDefinition(Location const& _location, + std::string const& _name, + vecptr<VariableDeclaration> const& _members) + : ASTNode(_location), m_name(_name), m_members(_members) + {} private: std::string m_name; vecptr<VariableDeclaration> m_members; }; +/// Used as function parameter list and return list +/// None of the parameters is allowed to contain mappings (not even recursively +/// inside structs) +class ParameterList : public ASTNode +{ +public: + ParameterList(Location const& _location, vecptr<VariableDeclaration> const& _parameters) + : ASTNode(_location), m_parameters(_parameters) + {} +private: + vecptr<VariableDeclaration> m_parameters; +}; + class FunctionDefinition : public ASTNode { +public: + FunctionDefinition(Location const& _location, std::string const& _name, bool _isPublic, + ptr<ParameterList> const& _parameters, + bool _isDeclaredConst, + ptr<ParameterList> const& _returnParameters, + ptr<Block> const& _body) + : ASTNode(_location), m_name(_name), m_isPublic(_isPublic), m_parameters(_parameters), + m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), + m_body(_body) + {} private: std::string m_name; - vecptr<VariableDeclaration> m_arguments; + bool m_isPublic; + ptr<ParameterList> m_parameters; bool m_isDeclaredConst; - vecptr<VariableDeclaration> m_returns; + ptr<ParameterList> m_returnParameters; ptr<Block> m_body; }; class VariableDeclaration : public ASTNode { public: - VariableDeclaration(const Location& _location, - const ptr<TypeName>& _type, - const std::string& _name) - : ASTNode(_location), - m_type(_type), - m_name(_name) + VariableDeclaration(Location const& _location, + ptr<TypeName> const& _type, + std::string const& _name) + : ASTNode(_location), m_type(_type), m_name(_name) {} private: - ptr<TypeName> m_type; ///<s can be empty ("var") + ptr<TypeName> m_type; ///< can be empty ("var") std::string m_name; }; @@ -114,7 +146,7 @@ private: class TypeName : public ASTNode { public: - explicit TypeName(const Location& _location) + explicit TypeName(Location const& _location) : ASTNode(_location) {} }; @@ -123,7 +155,7 @@ public: class ElementaryTypeName : public TypeName { public: - explicit ElementaryTypeName(const Location& _location, Token::Value _type) + explicit ElementaryTypeName(Location const& _location, Token::Value _type) : TypeName(_location), m_type(_type) {} private: @@ -133,18 +165,19 @@ private: class UserDefinedTypeName : public TypeName { public: - UserDefinedTypeName(const Location& _location, const std::string& _name) + UserDefinedTypeName(Location const& _location, std::string const& _name) : TypeName(_location), m_name(_name) {} private: std::string m_name; }; -class MappingTypeName : public TypeName +class Mapping : public TypeName { public: - explicit MappingTypeName(const Location& _location) - : TypeName(_location) + Mapping(Location const& _location, ptr<ElementaryTypeName> const& _keyType, + ptr<TypeName> const& _valueType) + : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} private: ptr<ElementaryTypeName> m_keyType; @@ -158,10 +191,18 @@ private: class Statement : public ASTNode { +public: + explicit Statement(Location const& _location) + : ASTNode(_location) + {} }; class Block : public Statement { +public: + explicit Block(Location const& _location) + : Statement(_location) + {} private: vecptr<Statement> m_statements; }; @@ -245,10 +286,12 @@ private: Token::Value m_operator; }; +/// Can be ordinary function call, type cast or struct construction. class FunctionCall : public Expression { private: - std::string m_functionName; // TODO only calls to fixed, named functions for now + // if m_functionName is the name of a type, store the token directly + std::string m_functionName; // "in place" calls of return values are not possible for now vecptr<Expression> m_arguments; }; diff --git a/BaseTypes.h b/BaseTypes.h index 0cc7f853..2e92b07e 100644 --- a/BaseTypes.h +++ b/BaseTypes.h @@ -4,19 +4,16 @@ namespace dev { namespace solidity { -// Representation of an interval of source positions. +/// Representation of an interval of source positions. +/// The interval includes start and excludes end. struct Location { - Location(int b, int e) : beg_pos(b), end_pos(e) { } - Location() : beg_pos(0), end_pos(0) { } + Location(int _start, int _end) : start(_start), end(_end) { } + Location() : start(-1), end(-1) { } - bool IsValid() const { - return beg_pos >= 0 && end_pos >= beg_pos; - } + bool IsValid() const { return start >= 0 && end >= start; } - static Location invalid() { return Location(-1, -1); } - - int beg_pos; - int end_pos; + int start; + int end; }; } } @@ -20,6 +20,7 @@ * Solidity parser. */ +#include "libdevcore/Log.h" #include "libsolidity/BaseTypes.h" #include "libsolidity/Parser.h" #include "libsolidity/Scanner.h" @@ -27,9 +28,9 @@ namespace dev { namespace solidity { -ptr<ASTNode> Parser::parse(Scanner& _scanner) +ptr<ASTNode> Parser::parse(std::shared_ptr<Scanner> const& _scanner) { - m_scanner = &_scanner; + m_scanner = _scanner; return parseContractDefinition(); } @@ -41,20 +42,22 @@ class Parser::ASTNodeFactory { public: ASTNodeFactory(const Parser& _parser) - : m_parser(_parser), - m_location(_parser.getPosition(), -1) + : m_parser(_parser), m_location(_parser.getPosition(), -1) {} - void markEndPosition() + void markEndPosition() { m_location.end = m_parser.getEndPosition(); } + + /// Set the end position to the one of the given node. + void setEndPositionFromNode(const ptr<ASTNode>& _node) { - m_location.end_pos = m_parser.getEndPosition(); + m_location.end = _node->getLocation().end; } /// @todo: check that this actually uses perfect forwarding template <class NodeType, typename... Args> ptr<NodeType> createNode(Args&&... _args) { - if (m_location.end_pos < 0) markEndPosition(); + if (m_location.end < 0) markEndPosition(); return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...); } @@ -65,12 +68,12 @@ private: int Parser::getPosition() const { - return m_scanner->getCurrentLocation().beg_pos; + return m_scanner->getCurrentLocation().start; } int Parser::getEndPosition() const { - return m_scanner->getCurrentLocation().end_pos; + return m_scanner->getCurrentLocation().end; } @@ -98,7 +101,6 @@ ptr<ContractDefinition> Parser::parseContractDefinition() functions.push_back(parseFunctionDefinition(visibilityIsPublic)); } else if (currentToken == Token::STRUCT) { structs.push_back(parseStructDefinition()); - expectToken(Token::SEMICOLON); } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || Token::IsElementaryTypeName(currentToken)) { stateVariables.push_back(parseVariableDeclaration()); @@ -117,19 +119,57 @@ ptr<ContractDefinition> Parser::parseContractDefinition() ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) { - (void) _isPublic; - throwExpectationError("Function parsing is not yet implemented."); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::FUNCTION); + std::string name(expectIdentifier()); + ptr<ParameterList> parameters(parseParameterList()); + bool isDeclaredConst = false; + if (m_scanner->getCurrentToken() == Token::CONST) { + isDeclaredConst = true; + m_scanner->next(); + } + ptr<ParameterList> returnParameters; + if (m_scanner->getCurrentToken() == Token::RETURNS) { + m_scanner->next(); + returnParameters = parseParameterList(); + } + ptr<Block> block = parseBlock(); + nodeFactory.setEndPositionFromNode(block); + return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, parameters, + isDeclaredConst, returnParameters, block); } ptr<StructDefinition> Parser::parseStructDefinition() { - throwExpectationError("Struct definition parsing is not yet implemented."); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::STRUCT); + std::string name = expectIdentifier(); + vecptr<VariableDeclaration> members; + expectToken(Token::LBRACE); + while (m_scanner->getCurrentToken() != Token::RBRACE) { + members.push_back(parseVariableDeclaration()); + expectToken(Token::SEMICOLON); + } + nodeFactory.markEndPosition(); + expectToken(Token::RBRACE); + + return nodeFactory.createNode<StructDefinition>(name, members); } ptr<VariableDeclaration> Parser::parseVariableDeclaration() { ASTNodeFactory nodeFactory(*this); + ptr<TypeName> type = parseTypeName(); + nodeFactory.markEndPosition(); + std::string name = expectIdentifier(); + return nodeFactory.createNode<VariableDeclaration>(type, name); +} + +ptr<TypeName> Parser::parseTypeName() +{ ptr<TypeName> type; Token::Value token = m_scanner->getCurrentToken(); if (Token::IsElementaryTypeName(token)) { @@ -139,17 +179,67 @@ ptr<VariableDeclaration> Parser::parseVariableDeclaration() type = ASTNodeFactory(*this).createNode<TypeName>(); m_scanner->next(); } else if (token == Token::MAPPING) { - // TODO - throwExpectationError("mappings are not yet implemented"); + type = parseMapping(); } else if (token == Token::IDENTIFIER) { type = ASTNodeFactory(*this).createNode<UserDefinedTypeName>(m_scanner->getCurrentLiteral()); m_scanner->next(); } else { - throwExpectationError("Expected variable declaration"); + throwExpectationError("Expected type name"); } + + return type; +} + +ptr<Mapping> Parser::parseMapping() +{ + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::MAPPING); + expectToken(Token::LPAREN); + + if (!Token::IsElementaryTypeName(m_scanner->getCurrentToken())) + throwExpectationError("Expected elementary type name for mapping key type"); + ptr<ElementaryTypeName> keyType; + keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken()); + m_scanner->next(); + + expectToken(Token::ARROW); + ptr<TypeName> valueType = parseTypeName(); nodeFactory.markEndPosition(); - std::string name = expectIdentifier(); - return nodeFactory.createNode<VariableDeclaration>(type, name); + expectToken(Token::RPAREN); + + return nodeFactory.createNode<Mapping>(keyType, valueType); +} + +ptr<ParameterList> Parser::parseParameterList() +{ + ASTNodeFactory nodeFactory(*this); + + vecptr<VariableDeclaration> parameters; + expectToken(Token::LPAREN); + if (m_scanner->getCurrentToken() != Token::RPAREN) { + parameters.push_back(parseVariableDeclaration()); + while (m_scanner->getCurrentToken() != Token::RPAREN) { + expectToken(Token::COMMA); + parameters.push_back(parseVariableDeclaration()); + } + } + nodeFactory.markEndPosition(); + m_scanner->next(); + return nodeFactory.createNode<ParameterList>(parameters); +} + +ptr<Block> Parser::parseBlock() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::LBRACE); + while (m_scanner->getCurrentToken() != Token::RBRACE) { + m_scanner->next(); + // @todo + } + nodeFactory.markEndPosition(); + expectToken(Token::RBRACE); + return nodeFactory.createNode<Block>(); } void Parser::expectToken(Token::Value _value) @@ -171,9 +261,16 @@ std::string Parser::expectIdentifier() void Parser::throwExpectationError(const std::string& _description) { - (void) _description; + int line, column; + std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition()); + cwarn << "Solidity parser error: " << _description + << "at line " << (line + 1) + << ", column " << (column + 1); + cwarn << m_scanner->getLineAtPosition(getPosition()); + cwarn << std::string(column, ' ') << "^"; + /// @todo make a proper exception hierarchy - throw std::exception();//_description); + throw std::exception(); } @@ -32,7 +32,7 @@ class Scanner; class Parser { public: - ptr<ASTNode> parse(Scanner& _scanner); + ptr<ASTNode> parse(std::shared_ptr<Scanner> const& _scanner); private: class ASTNodeFactory; @@ -48,6 +48,10 @@ private: ptr<FunctionDefinition> parseFunctionDefinition(bool _isPublic); ptr<StructDefinition> parseStructDefinition(); ptr<VariableDeclaration> parseVariableDeclaration(); + ptr<TypeName> parseTypeName(); + ptr<Mapping> parseMapping(); + ptr<ParameterList> parseParameterList(); + ptr<Block> parseBlock(); /// @} /// Helper functions @@ -58,7 +62,7 @@ private: void throwExpectationError(const std::string& _description); /// @} - Scanner* m_scanner; + std::shared_ptr<Scanner> m_scanner; }; } } diff --git a/Scanner.cpp b/Scanner.cpp index a936e24f..836e0d09 100644 --- a/Scanner.cpp +++ b/Scanner.cpp @@ -40,6 +40,9 @@ // You should have received a copy of the GNU General Public License // along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +#include <algorithm> +#include <tuple> + #include <libsolidity/Scanner.h> namespace dev { @@ -121,6 +124,7 @@ Token::Value Scanner::next() return m_current_token.token; } + bool Scanner::skipWhitespace() { const int start_position = getSourcePos(); @@ -182,7 +186,7 @@ void Scanner::scanToken() Token::Value token; do { // Remember the position of the next token - m_next_token.location.beg_pos = getSourcePos(); + m_next_token.location.start = getSourcePos(); switch (m_char) { case '\n': @@ -401,7 +405,7 @@ void Scanner::scanToken() // whitespace. } while (token == Token::WHITESPACE); - m_next_token.location.end_pos = getSourcePos(); + m_next_token.location.end = getSourcePos(); m_next_token.token = token; } @@ -546,7 +550,7 @@ Token::Value Scanner::scanNumber(bool _periodSeen) #define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ KEYWORD_GROUP('a') \ - KEYWORD("address", Token::BREAK) \ + KEYWORD("address", Token::ADDRESS) \ KEYWORD_GROUP('b') \ KEYWORD("break", Token::BREAK) \ KEYWORD("bool", Token::BOOL) \ @@ -588,8 +592,8 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD_GROUP('l') \ KEYWORD_GROUP('m') \ - KEYWORD_GROUP('n') \ KEYWORD("mapping", Token::MAPPING) \ + KEYWORD_GROUP('n') \ KEYWORD("new", Token::NEW) \ KEYWORD("null", Token::NULL_LITERAL) \ KEYWORD_GROUP('p') \ @@ -600,8 +604,9 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD_GROUP('r') \ KEYWORD("real", Token::REAL) \ KEYWORD("return", Token::RETURN) \ + KEYWORD("returns", Token::RETURNS) \ KEYWORD_GROUP('s') \ - KEYWORD("string", Token::STRING_TYPE) \ + KEYWORD("string", Token::STRING_TYPE) \ KEYWORD("struct", Token::STRUCT) \ KEYWORD("switch", Token::SWITCH) \ KEYWORD_GROUP('t') \ @@ -671,5 +676,38 @@ Token::Value Scanner::scanIdentifierOrKeyword() return KeywordOrIdentifierToken(m_next_token.literal); } +std::string CharStream::getLineAtPosition(int _position) const +{ + // if _position points to \n, it returns the line before the \n + using size_type = std::string::size_type; + size_type searchStart = std::min<size_type>(m_source.size(), _position); + if (searchStart > 0) searchStart--; + size_type lineStart = m_source.rfind('\n', searchStart); + if (lineStart == std::string::npos) + lineStart = 0; + else + lineStart++; + return m_source.substr(lineStart, + std::min(m_source.find('\n', lineStart), + m_source.size()) - lineStart); +} + +std::tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const +{ + using size_type = std::string::size_type; + size_type searchPosition = std::min<size_type>(m_source.size(), _position); + int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n'); + + size_type lineStart; + if (searchPosition == 0) { + lineStart = 0; + } else { + lineStart = m_source.rfind('\n', searchPosition - 1); + lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; + } + + return std::tuple<int, int>(lineNumber, searchPosition - lineStart); +} + } } @@ -82,6 +82,12 @@ public: return get(); } + /// Functions that help pretty-printing parse errors + /// Do only use in error cases, they are quite expensive. + /// @{ + std::string getLineAtPosition(int _position) const; + std::tuple<int, int> translatePositionToLineColumn(int _position) const; + /// @} private: std::string m_source; size_t m_pos; @@ -134,6 +140,16 @@ public: Location peekLocation() const { return m_next_token.location; } const std::string& peekLiteral() const { return m_next_token.literal; } + /// Functions that help pretty-printing parse errors. + /// Do only use in error cases, they are quite expensive. + /// @{ + std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } + std::tuple<int, int> translatePositionToLineColumn(int _position) const + { + return m_source.translatePositionToLineColumn(_position); + } + /// @} + // Returns true if there was a line terminator before the peek'ed token, // possibly inside a multi-line comment. bool hasAnyLineTerminatorBeforeNext() const { @@ -169,6 +169,7 @@ namespace solidity { K(PUBLIC, "public", 0) \ K(PRIVATE, "private", 0) \ K(RETURN, "return", 0) \ + K(RETURNS, "returns", 0) \ K(STRUCT, "struct", 0) \ K(SWITCH, "switch", 0) \ K(THIS, "this", 0) \ diff --git a/grammar.txt b/grammar.txt index aec02489..1946325f 100644 --- a/grammar.txt +++ b/grammar.txt @@ -1,18 +1,18 @@ ContractDefinition = 'contract' Identifier '{' ContractPart* '}' -ContractPart = VariableDeclaration ';' | StructDefinition ';' | - FunctionDefinition ';' | 'public:' | 'private:' +ContractPart = VariableDeclaration ';' | StructDefinition | + FunctionDefinition | 'public:' | 'private:' StructDefinition = 'struct' Identifier '{' ( VariableDeclaration (';' VariableDeclaration)* )? '} -FunctionDefinition = 'function' Identifier ArgumentList 'const'? - 'returns' ArgumentList Block -ArgumentList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' +FunctionDefinition = 'function' Identifier ParameterList 'const'? + ( 'returns' ParameterList )? Block +ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' // semantic restriction: mappings and structs (recursively) containing mappings // are not allowed in argument lists VariableDeclaration = TypeName Identifier -TypeName = PredefinedType | Identifier | MappingType -MappingType = 'mapping' '(' SimplePredefinedType '=>' TypeName ')' +TypeName = ElementaryTypeName | Identifier | Mapping +Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | Continue | Break | Return | VariableAssignment | Expression ';' | Block |