diff options
-rw-r--r-- | AST.cpp | 23 | ||||
-rw-r--r-- | AST.h | 287 | ||||
-rw-r--r-- | BaseTypes.h | 22 | ||||
-rw-r--r-- | Parser.cpp | 180 | ||||
-rw-r--r-- | Parser.h | 64 | ||||
-rw-r--r-- | Scanner.cpp | 42 | ||||
-rw-r--r-- | Scanner.h | 18 | ||||
-rw-r--r-- | Token.h | 35 | ||||
-rw-r--r-- | grammar.txt | 32 |
9 files changed, 675 insertions, 28 deletions
diff --git a/AST.cpp b/AST.cpp new file mode 100644 index 00000000..ba50b7c6 --- /dev/null +++ b/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 <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity abstract syntax tree. + */ + +#include <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 <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity abstract syntax tree. + */ + +#pragma once + +#include <string> +#include <vector> +#include <memory> + +#include <libsolidity/BaseTypes.h> +#include <libsolidity/Token.h> + +namespace dev { +namespace solidity { + +template <class T> +using ptr = std::shared_ptr<T>; +template <class T> +using vecptr = std::vector<ptr<T>>; + +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<StructDefinition>& _definedStructs, + const vecptr<VariableDeclaration>& _stateVariables, + const vecptr<FunctionDefinition>& _definedFunctions) + : ASTNode(_location), + m_name(_name), + m_definedStructs(_definedStructs), + m_stateVariables(_stateVariables), + m_definedFunctions(_definedFunctions) + {} + +private: + std::string m_name; + vecptr<StructDefinition> m_definedStructs; + vecptr<VariableDeclaration> m_stateVariables; + vecptr<FunctionDefinition> m_definedFunctions; +}; + +class StructDefinition : public ASTNode +{ +private: + std::string m_name; + vecptr<VariableDeclaration> m_members; +}; + +class FunctionDefinition : public ASTNode +{ +private: + std::string m_name; + vecptr<VariableDeclaration> m_arguments; + bool m_isDeclaredConst; + vecptr<VariableDeclaration> m_returns; + 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) + {} +private: + ptr<TypeName> m_type; ///<s can be empty ("var") + std::string m_name; +}; + +/// types +/// @{ + +class TypeName : public ASTNode +{ +public: + explicit TypeName(const Location& _location) + : ASTNode(_location) + {} +}; + +/// any pre-defined type that is not a mapping +class ElementaryTypeName : public TypeName +{ +public: + explicit ElementaryTypeName(const Location& _location, Token::Value _type) + : TypeName(_location), m_type(_type) + {} +private: + Token::Value m_type; +}; + +class UserDefinedTypeName : public TypeName +{ +public: + UserDefinedTypeName(const Location& _location, const std::string& _name) + : TypeName(_location), m_name(_name) + {} +private: + std::string m_name; +}; + +class MappingTypeName : public TypeName +{ +public: + explicit MappingTypeName(const Location& _location) + : TypeName(_location) + {} +private: + ptr<ElementaryTypeName> m_keyType; + ptr<TypeName> m_valueType; +}; + +/// @} + +/// Statements +/// @{ + +class Statement : public ASTNode +{ +}; + +class Block : public Statement +{ +private: + vecptr<Statement> m_statements; +}; + +class IfStatement : public Statement +{ + +private: + ptr<Expression> m_condition; + ptr<Statement> m_trueBody; + ptr<Statement> m_falseBody; +}; + +class BreakableStatement : public Statement +{ + +}; + +class WhileStatement : public BreakableStatement +{ +private: + ptr<Expression> m_condition; + ptr<Statement> m_body; +}; + +class Continue : public Statement +{ + +}; + +class Break : public Statement +{ + +}; + +class Return : public Statement +{ +private: + ptr<Expression> m_expression; +}; + +class VariableAssignment : public Statement +{ +private: + ptr<VariableDeclaration> m_variable; + Token::Value m_assigmentOperator; + ptr<Expression> m_rightHandSide; ///< can be missing +}; + +class Expression : public Statement +{ +private: +}; + +/// @} + +/// Expressions +/// @{ + +class Assignment : public Expression +{ +private: + ptr<Expression> m_leftHandSide; + Token::Value m_assigmentOperator; + ptr<Expression> m_rightHandSide; +}; + +class UnaryOperation : public Expression +{ +private: + Token::Value m_operator; + ptr<Expression> m_subExpression; + bool isPrefix; +}; + +class BinaryOperation : public Expression +{ +private: + ptr<Expression> m_left; + ptr<Expression> 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<Expression> m_arguments; +}; + +class MemberAccess : public Expression +{ +private: + ptr<Expression> m_expression; + std::string m_memberName; +}; + +class IndexAccess : public Expression +{ + ptr<Expression> m_base; + ptr<Expression> 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/BaseTypes.h b/BaseTypes.h new file mode 100644 index 00000000..0cc7f853 --- /dev/null +++ b/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/Parser.cpp b/Parser.cpp new file mode 100644 index 00000000..09ea8604 --- /dev/null +++ b/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 <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity parser. + */ + +#include "libsolidity/BaseTypes.h" +#include "libsolidity/Parser.h" +#include "libsolidity/Scanner.h" + +namespace dev { +namespace solidity { + +ptr<ASTNode> 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 <class NodeType, typename... Args> + ptr<NodeType> createNode(Args&&... _args) + { + if (m_location.end_pos < 0) markEndPosition(); + return std::make_shared<NodeType>(m_location, std::forward<Args>(_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<ContractDefinition> Parser::parseContractDefinition() +{ + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::CONTRACT); + std::string name = expectIdentifier(); + expectToken(Token::LBRACE); + + vecptr<StructDefinition> structs; + vecptr<VariableDeclaration> stateVariables; + vecptr<FunctionDefinition> 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<ContractDefinition>(name, structs, stateVariables, functions); +} + +ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) +{ + (void) _isPublic; + throwExpectationError("Function parsing is not yet implemented."); +} + +ptr<StructDefinition> Parser::parseStructDefinition() +{ + throwExpectationError("Struct definition parsing is not yet implemented."); +} + +ptr<VariableDeclaration> Parser::parseVariableDeclaration() +{ + ASTNodeFactory nodeFactory(*this); + + ptr<TypeName> type; + Token::Value token = m_scanner->getCurrentToken(); + if (Token::IsElementaryTypeName(token)) { + type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token); + m_scanner->next(); + } else if (token == Token::VAR) { + type = ASTNodeFactory(*this).createNode<TypeName>(); + m_scanner->next(); + } else if (token == Token::MAPPING) { + // TODO + throwExpectationError("mappings are not yet implemented"); + } else if (token == Token::IDENTIFIER) { + type = ASTNodeFactory(*this).createNode<UserDefinedTypeName>(m_scanner->getCurrentLiteral()); + m_scanner->next(); + } else { + throwExpectationError("Expected variable declaration"); + } + nodeFactory.markEndPosition(); + std::string name = expectIdentifier(); + return nodeFactory.createNode<VariableDeclaration>(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/Parser.h b/Parser.h new file mode 100644 index 00000000..4a48dace --- /dev/null +++ b/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 <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity parser. + */ + +#pragma once + +#include "libsolidity/AST.h" + +namespace dev { +namespace solidity { + +class Scanner; + +class Parser +{ +public: + ptr<ASTNode> 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<ContractDefinition> parseContractDefinition(); + ptr<FunctionDefinition> parseFunctionDefinition(bool _isPublic); + ptr<StructDefinition> parseStructDefinition(); + ptr<VariableDeclaration> 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/Scanner.cpp b/Scanner.cpp index 101b4a1a..a936e24f 100644 --- a/Scanner.cpp +++ b/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) \ @@ -47,6 +47,7 @@ #include <libdevcore/Common.h> #include <libdevcore/Log.h> #include <libdevcore/CommonData.h> +#include <libsolidity/BaseTypes.h> #include <libsolidity/Token.h> 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(); @@ -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/grammar.txt b/grammar.txt new file mode 100644 index 00000000..aec02489 --- /dev/null +++ b/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 ')' |