From 961327ee7df11f86886e1b05d1e57f6201de9fc2 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 7 Oct 2014 18:25:04 +0200 Subject: [PATCH] Solidity parser, can not parse much yet. --- libsolidity/AST.cpp | 23 ++++ libsolidity/AST.h | 287 +++++++++++++++++++++++++++++++++++++++ libsolidity/BaseTypes.h | 22 +++ libsolidity/Parser.cpp | 180 ++++++++++++++++++++++++ libsolidity/Parser.h | 64 +++++++++ libsolidity/Scanner.cpp | 42 ++++-- libsolidity/Scanner.h | 18 +-- libsolidity/Token.h | 35 ++++- libsolidity/grammar.txt | 32 +++++ test/solidityParser.cpp | 52 +++++++ test/solidityScanner.cpp | 37 +++-- 11 files changed, 745 insertions(+), 47 deletions(-) create mode 100644 libsolidity/AST.cpp create mode 100644 libsolidity/AST.h create mode 100644 libsolidity/BaseTypes.h create mode 100644 libsolidity/Parser.cpp create mode 100644 libsolidity/Parser.h create mode 100644 libsolidity/grammar.txt create mode 100644 test/solidityParser.cpp diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp new file mode 100644 index 000000000..ba50b7c6f --- /dev/null +++ b/libsolidity/AST.cpp @@ -0,0 +1,23 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity abstract syntax tree. + */ + +#include diff --git a/libsolidity/AST.h b/libsolidity/AST.h new file mode 100644 index 000000000..f71b5b346 --- /dev/null +++ b/libsolidity/AST.h @@ -0,0 +1,287 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity abstract syntax tree. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace dev { +namespace solidity { + +template +using ptr = std::shared_ptr; +template +using vecptr = std::vector>; + +class VariableDeclaration; +class StructDefinition; +class FunctionDefinition; +class TypeName; +class Block; +class Expression; + +class ASTNode +{ +public: + explicit ASTNode(const Location& _location) + : m_location(_location) + {} +private: + Location m_location; +}; + +class ContractDefinition : public ASTNode +{ +public: + ContractDefinition(const Location& _location, + const std::string& _name, + const vecptr& _definedStructs, + const vecptr& _stateVariables, + const vecptr& _definedFunctions) + : ASTNode(_location), + m_name(_name), + m_definedStructs(_definedStructs), + m_stateVariables(_stateVariables), + m_definedFunctions(_definedFunctions) + {} + +private: + std::string m_name; + vecptr m_definedStructs; + vecptr m_stateVariables; + vecptr m_definedFunctions; +}; + +class StructDefinition : public ASTNode +{ +private: + std::string m_name; + vecptr m_members; +}; + +class FunctionDefinition : public ASTNode +{ +private: + std::string m_name; + vecptr m_arguments; + bool m_isDeclaredConst; + vecptr m_returns; + ptr m_body; +}; + +class VariableDeclaration : public ASTNode +{ +public: + VariableDeclaration(const Location& _location, + const ptr& _type, + const std::string& _name) + : ASTNode(_location), + m_type(_type), + m_name(_name) + {} +private: + ptr m_type; /// m_keyType; + ptr m_valueType; +}; + +/// @} + +/// Statements +/// @{ + +class Statement : public ASTNode +{ +}; + +class Block : public Statement +{ +private: + vecptr m_statements; +}; + +class IfStatement : public Statement +{ + +private: + ptr m_condition; + ptr m_trueBody; + ptr m_falseBody; +}; + +class BreakableStatement : public Statement +{ + +}; + +class WhileStatement : public BreakableStatement +{ +private: + ptr m_condition; + ptr m_body; +}; + +class Continue : public Statement +{ + +}; + +class Break : public Statement +{ + +}; + +class Return : public Statement +{ +private: + ptr m_expression; +}; + +class VariableAssignment : public Statement +{ +private: + ptr m_variable; + Token::Value m_assigmentOperator; + ptr m_rightHandSide; ///< can be missing +}; + +class Expression : public Statement +{ +private: +}; + +/// @} + +/// Expressions +/// @{ + +class Assignment : public Expression +{ +private: + ptr m_leftHandSide; + Token::Value m_assigmentOperator; + ptr m_rightHandSide; +}; + +class UnaryOperation : public Expression +{ +private: + Token::Value m_operator; + ptr m_subExpression; + bool isPrefix; +}; + +class BinaryOperation : public Expression +{ +private: + ptr m_left; + ptr m_right; + Token::Value m_operator; +}; + +class FunctionCall : public Expression +{ +private: + std::string m_functionName; // TODO only calls to fixed, named functions for now + vecptr m_arguments; +}; + +class MemberAccess : public Expression +{ +private: + ptr m_expression; + std::string m_memberName; +}; + +class IndexAccess : public Expression +{ + ptr m_base; + ptr m_index; +}; + +class PrimaryExpression : public Expression +{ +}; + +class Identifier : public PrimaryExpression +{ +private: + std::string m_name; +}; + +class Literal : public PrimaryExpression +{ +private: + std::string m_value; +}; + +/// @} + + +} } diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h new file mode 100644 index 000000000..0cc7f8534 --- /dev/null +++ b/libsolidity/BaseTypes.h @@ -0,0 +1,22 @@ +#pragma once + + +namespace dev { +namespace solidity { + +// Representation of an interval of source positions. +struct Location { + Location(int b, int e) : beg_pos(b), end_pos(e) { } + Location() : beg_pos(0), end_pos(0) { } + + bool IsValid() const { + return beg_pos >= 0 && end_pos >= beg_pos; + } + + static Location invalid() { return Location(-1, -1); } + + int beg_pos; + int end_pos; +}; + +} } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp new file mode 100644 index 000000000..09ea86045 --- /dev/null +++ b/libsolidity/Parser.cpp @@ -0,0 +1,180 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity parser. + */ + +#include "libsolidity/BaseTypes.h" +#include "libsolidity/Parser.h" +#include "libsolidity/Scanner.h" + +namespace dev { +namespace solidity { + +ptr Parser::parse(Scanner& _scanner) +{ + m_scanner = &_scanner; + + return parseContractDefinition(); +} + + +/// AST node factory that also tracks the begin and end position of an AST node +/// while it is being parsed +class Parser::ASTNodeFactory +{ +public: + ASTNodeFactory(const Parser& _parser) + : m_parser(_parser), + m_location(_parser.getPosition(), -1) + {} + + void markEndPosition() + { + m_location.end_pos = m_parser.getEndPosition(); + } + + /// @todo: check that this actually uses perfect forwarding + template + ptr createNode(Args&&... _args) + { + if (m_location.end_pos < 0) markEndPosition(); + return std::make_shared(m_location, std::forward(_args)...); + } + +private: + const Parser& m_parser; + Location m_location; +}; + +int Parser::getPosition() const +{ + return m_scanner->getCurrentLocation().beg_pos; +} + +int Parser::getEndPosition() const +{ + return m_scanner->getCurrentLocation().end_pos; +} + + +ptr Parser::parseContractDefinition() +{ + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::CONTRACT); + std::string name = expectIdentifier(); + expectToken(Token::LBRACE); + + vecptr structs; + vecptr stateVariables; + vecptr functions; + bool visibilityIsPublic = true; + while (true) { + Token::Value currentToken = m_scanner->getCurrentToken(); + if (currentToken == Token::RBRACE) { + break; + } else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) { + visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); + m_scanner->next(); + expectToken(Token::COLON); + } else if (currentToken == Token::FUNCTION) { + 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()); + expectToken(Token::SEMICOLON); + } else { + throwExpectationError("Function, variable or struct declaration expected."); + } + } + nodeFactory.markEndPosition(); + + m_scanner->next(); + expectToken(Token::EOS); + + return nodeFactory.createNode(name, structs, stateVariables, functions); +} + +ptr Parser::parseFunctionDefinition(bool _isPublic) +{ + (void) _isPublic; + throwExpectationError("Function parsing is not yet implemented."); +} + +ptr Parser::parseStructDefinition() +{ + throwExpectationError("Struct definition parsing is not yet implemented."); +} + +ptr Parser::parseVariableDeclaration() +{ + ASTNodeFactory nodeFactory(*this); + + ptr type; + Token::Value token = m_scanner->getCurrentToken(); + if (Token::IsElementaryTypeName(token)) { + type = ASTNodeFactory(*this).createNode(token); + m_scanner->next(); + } else if (token == Token::VAR) { + type = ASTNodeFactory(*this).createNode(); + m_scanner->next(); + } else if (token == Token::MAPPING) { + // TODO + throwExpectationError("mappings are not yet implemented"); + } else if (token == Token::IDENTIFIER) { + type = ASTNodeFactory(*this).createNode(m_scanner->getCurrentLiteral()); + m_scanner->next(); + } else { + throwExpectationError("Expected variable declaration"); + } + nodeFactory.markEndPosition(); + std::string name = expectIdentifier(); + return nodeFactory.createNode(type, name); +} + +void Parser::expectToken(Token::Value _value) +{ + if (m_scanner->getCurrentToken() != _value) + throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value))); + m_scanner->next(); +} + +std::string Parser::expectIdentifier() +{ + if (m_scanner->getCurrentToken() != Token::IDENTIFIER) + throwExpectationError("Expected identifier"); + + std::string literal = m_scanner->getCurrentLiteral(); + m_scanner->next(); + return literal; +} + +void Parser::throwExpectationError(const std::string& _description) +{ + (void) _description; + /// @todo make a proper exception hierarchy + throw std::exception();//_description); +} + + +} } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h new file mode 100644 index 000000000..4a48dace5 --- /dev/null +++ b/libsolidity/Parser.h @@ -0,0 +1,64 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity parser. + */ + +#pragma once + +#include "libsolidity/AST.h" + +namespace dev { +namespace solidity { + +class Scanner; + +class Parser +{ +public: + ptr parse(Scanner& _scanner); + +private: + class ASTNodeFactory; + + /// Start position of the current token + int getPosition() const; + /// End position of the current token + int getEndPosition() const; + + /// Parsing functions for the AST nodes + /// @{ + ptr parseContractDefinition(); + ptr parseFunctionDefinition(bool _isPublic); + ptr parseStructDefinition(); + ptr parseVariableDeclaration(); + /// @} + + /// Helper functions + /// @{ + /// If current token value is not _value, throw exception otherwise advance token. + void expectToken(Token::Value _value); + std::string expectIdentifier(); + void throwExpectationError(const std::string& _description); + /// @} + + Scanner* m_scanner; +}; + +} } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 101b4a1ab..a936e24f5 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -82,16 +82,10 @@ void Scanner::reset(const CharStream& _source) { m_source = _source; - // Initialize current_ to not refer to a literal. - m_current_token.token = Token::ILLEGAL; - m_current_token.literal.clear(); - - m_hasLineTerminatorBeforeNext = true; - m_hasMultilineCommentBeforeNext = false; - m_char = m_source.get(); skipWhitespace(); scanToken(); + next(); } @@ -466,7 +460,7 @@ Token::Value Scanner::scanString() literal.Complete(); advance(); // consume quote - return Token::STRING; + return Token::STRING_LITERAL; } @@ -551,13 +545,17 @@ Token::Value Scanner::scanNumber(bool _periodSeen) // Keyword Matcher #define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ + KEYWORD_GROUP('a') \ + KEYWORD("address", Token::BREAK) \ KEYWORD_GROUP('b') \ KEYWORD("break", Token::BREAK) \ + KEYWORD("bool", Token::BOOL) \ KEYWORD_GROUP('c') \ KEYWORD("case", Token::CASE) \ KEYWORD("catch", Token::CATCH) \ KEYWORD("const", Token::CONST) \ KEYWORD("continue", Token::CONTINUE) \ + KEYWORD("contract", Token::CONTRACT) \ KEYWORD_GROUP('d') \ KEYWORD("debugger", Token::DEBUGGER) \ KEYWORD("default", Token::DEFAULT) \ @@ -571,31 +569,55 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("finally", Token::FINALLY) \ KEYWORD("for", Token::FOR) \ KEYWORD("function", Token::FUNCTION) \ + KEYWORD_GROUP('h') \ + KEYWORD("hash", Token::HASH) \ + KEYWORD("hash32", Token::HASH32) \ + KEYWORD("hash64", Token::HASH64) \ + KEYWORD("hash128", Token::HASH128) \ + KEYWORD("hash256", Token::HASH256) \ KEYWORD_GROUP('i') \ KEYWORD("if", Token::IF) \ KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD("in", Token::IN) \ KEYWORD("instanceof", Token::INSTANCEOF) \ + KEYWORD("int", Token::INT) \ + KEYWORD("int32", Token::INT32) \ + KEYWORD("int64", Token::INT64) \ + KEYWORD("int128", Token::INT128) \ + KEYWORD("int256", Token::INT256) \ KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD_GROUP('l') \ + KEYWORD_GROUP('m') \ KEYWORD_GROUP('n') \ + KEYWORD("mapping", Token::MAPPING) \ KEYWORD("new", Token::NEW) \ KEYWORD("null", Token::NULL_LITERAL) \ KEYWORD_GROUP('p') \ KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("private", Token::PRIVATE) \ KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("public", Token::PUBLIC) \ KEYWORD_GROUP('r') \ + KEYWORD("real", Token::REAL) \ KEYWORD("return", Token::RETURN) \ KEYWORD_GROUP('s') \ + KEYWORD("string", Token::STRING_TYPE) \ + KEYWORD("struct", Token::STRUCT) \ KEYWORD("switch", Token::SWITCH) \ KEYWORD_GROUP('t') \ + KEYWORD("text", Token::TEXT) \ KEYWORD("this", Token::THIS) \ KEYWORD("throw", Token::THROW) \ KEYWORD("true", Token::TRUE_LITERAL) \ KEYWORD("try", Token::TRY) \ KEYWORD("typeof", Token::TYPEOF) \ + KEYWORD_GROUP('u') \ + KEYWORD("uint", Token::UINT) \ + KEYWORD("uint32", Token::UINT32) \ + KEYWORD("uint64", Token::UINT64) \ + KEYWORD("uint128", Token::UINT128) \ + KEYWORD("uint256", Token::UINT256) \ + KEYWORD("ureal", Token::UREAL) \ KEYWORD_GROUP('v') \ KEYWORD("var", Token::VAR) \ KEYWORD("void", Token::VOID) \ diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 3cf52fbc6..d2dcad29d 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -47,6 +47,7 @@ #include #include #include +#include #include namespace dev { @@ -111,21 +112,6 @@ public: bool complete_; }; - // Representation of an interval of source positions. - struct Location { - Location(int b, int e) : beg_pos(b), end_pos(e) { } - Location() : beg_pos(0), end_pos(0) { } - - bool IsValid() const { - return beg_pos >= 0 && end_pos >= beg_pos; - } - - static Location invalid() { return Location(-1, -1); } - - int beg_pos; - int end_pos; - }; - explicit Scanner(const CharStream& _source); // Resets the scanner as if newly constructed with _input as input. @@ -163,8 +149,6 @@ private: std::string literal; }; - static const int kCharacterLookaheadBufferSize = 1; - // Literal buffer support inline void startNewLiteral() { m_next_token.literal.clear(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 4f5ec1943..7a39b989f 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -152,6 +152,7 @@ namespace solidity { K(CASE, "case", 0) \ K(CATCH, "catch", 0) \ K(CONTINUE, "continue", 0) \ + K(CONTRACT, "contract", 0) \ K(DEBUGGER, "debugger", 0) \ K(DEFAULT, "default", 0) \ /* DELETE */ \ @@ -163,8 +164,12 @@ namespace solidity { K(IF, "if", 0) \ /* IN */ \ /* INSTANCEOF */ \ + K(MAPPING, "mapping", 0) \ K(NEW, "new", 0) \ + K(PUBLIC, "public", 0) \ + K(PRIVATE, "private", 0) \ K(RETURN, "return", 0) \ + K(STRUCT, "struct", 0) \ K(SWITCH, "switch", 0) \ K(THIS, "this", 0) \ K(THROW, "throw", 0) \ @@ -175,12 +180,36 @@ namespace solidity { K(WHILE, "while", 0) \ K(WITH, "with", 0) \ \ + /* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \ + K(INT, "int", 0) \ + K(INT32, "int32", 0) \ + K(INT64, "int64", 0) \ + K(INT128, "int128", 0) \ + K(INT256, "int256", 0) \ + K(UINT, "uint", 0) \ + K(UINT32, "uint32", 0) \ + K(UINT64, "uint64", 0) \ + K(UINT128, "uint128", 0) \ + K(UINT256, "uint256", 0) \ + K(HASH, "hash", 0) \ + K(HASH32, "hash32", 0) \ + K(HASH64, "hash64", 0) \ + K(HASH128, "hash128", 0) \ + K(HASH256, "hash256", 0) \ + K(ADDRESS, "address", 0) \ + K(BOOL, "bool", 0) \ + K(STRING_TYPE, "string", 0) \ + K(TEXT, "text", 0) \ + K(REAL, "real", 0) \ + K(UREAL, "ureal", 0) \ + T(TYPES_END, NULL, 0) /* used as type enum end marker */ \ + \ /* Literals (ECMA-262, section 7.8, page 16). */ \ K(NULL_LITERAL, "null", 0) \ K(TRUE_LITERAL, "true", 0) \ K(FALSE_LITERAL, "false", 0) \ T(NUMBER, NULL, 0) \ - T(STRING, NULL, 0) \ + T(STRING_LITERAL, NULL, 0) \ \ /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, NULL, 0) \ @@ -231,6 +260,10 @@ class Token { return tok == IDENTIFIER; } + static bool IsElementaryTypeName(Value tok) { + return INT <= tok && tok < TYPES_END; + } + static bool IsAssignmentOp(Value tok) { return INIT_VAR <= tok && tok <= ASSIGN_MOD; } diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt new file mode 100644 index 000000000..aec02489e --- /dev/null +++ b/libsolidity/grammar.txt @@ -0,0 +1,32 @@ +ContractDefinition = 'contract' Identifier '{' ContractPart* '}' +ContractPart = VariableDeclaration ';' | StructDefinition ';' | + FunctionDefinition ';' | 'public:' | 'private:' + +StructDefinition = 'struct' Identifier '{' + ( VariableDeclaration (';' VariableDeclaration)* )? '} + +FunctionDefinition = 'function' Identifier ArgumentList 'const'? + 'returns' ArgumentList Block +ArgumentList = '(' ( 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 ')' + +Block = '{' Statement* '}' +Statement = IfStatement | WhileStatement | Continue | Break | Return | VariableAssignment | Expression ';' | Block + +IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? +WhileStatement = 'while' '(' Expression ')' Statement +Continue = 'continue' ';' +Break = 'break' ';' +Return = 'return' Expression? ';' +VariableAssignment = VariableDeclaration ( AssignmentOp Expression )? ';' + +Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | MemberAccess | PrimaryExpression +Assignment = Expression (AssignmentOp Expression) +FunctionCall = Identifier '(' ( Expression ( ',' Expression )* ) ')' +MemberAccess = Expression '.' Identifier +IndexAccess = Expression '[' Expresison ']' +PrimaryExpression = Identifier | NumberLiteral | StringLiteral | '(' Expression ')' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp new file mode 100644 index 000000000..f42506767 --- /dev/null +++ b/test/solidityParser.cpp @@ -0,0 +1,52 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Unit tests for the solidity parser. + */ + +#include + +#include +#include +#include + +namespace dev { +namespace solidity { +namespace test { + +BOOST_AUTO_TEST_SUITE(SolidityParser) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + std::string text = "contract test123 {\n" + "\tuint256 stateVariable1;\n" + "}\n"; + Parser parser; + CharStream str(text); + // @todo: figure out why this does not compile + //Scanner scanner(CharStream(text)); + Scanner scanner(str); + BOOST_CHECK_NO_THROW(parser.parse(scanner)); +} + + +BOOST_AUTO_TEST_SUITE_END() + +} } } // end namespaces + diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index afbcdffae..7f84146a3 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -27,21 +27,26 @@ namespace dev { namespace solidity { namespace test { -BOOST_AUTO_TEST_SUITE(solidity) +BOOST_AUTO_TEST_SUITE(SolidityScanner) + +BOOST_AUTO_TEST_CASE(test_empty) +{ + Scanner scanner(CharStream("")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); +} BOOST_AUTO_TEST_CASE(smoke_test) { Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::FUNCTION); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::FUNCTION); BOOST_CHECK_EQUAL(scanner.next(), Token::BREAK); BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "765"); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string1"); BOOST_CHECK_EQUAL(scanner.next(), Token::COMMA); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string2"); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "identifier1"); @@ -51,26 +56,23 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_AUTO_TEST_CASE(string_escapes) { Scanner scanner(CharStream(" { \"a\\x61\"")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::LBRACE); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "aa"); } BOOST_AUTO_TEST_CASE(string_escapes_with_zero) { Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::LBRACE); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), std::string("aa\0abc", 6)); } BOOST_AUTO_TEST_CASE(string_escape_illegal) { Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); // TODO recovery from illegal tokens should be improved @@ -83,8 +85,7 @@ BOOST_AUTO_TEST_CASE(string_escape_illegal) BOOST_AUTO_TEST_CASE(hex_numbers) { Scanner scanner(CharStream("var x = 0x765432536763762734623472346;")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::VAR); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::VAR); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); @@ -96,8 +97,7 @@ BOOST_AUTO_TEST_CASE(hex_numbers) BOOST_AUTO_TEST_CASE(locations) { Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 0); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 19); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); @@ -122,8 +122,7 @@ BOOST_AUTO_TEST_CASE(ambiguities) { // test scanning of some operators which need look-ahead Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::LTE); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LTE); BOOST_CHECK_EQUAL(scanner.next(), Token::LT); BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN_ADD);