diff options
-rw-r--r-- | AST.cpp | 11 | ||||
-rw-r--r-- | AST.h | 36 | ||||
-rw-r--r-- | ASTForward.h | 1 | ||||
-rw-r--r-- | ASTPrinter.cpp | 12 | ||||
-rw-r--r-- | ASTPrinter.h | 2 | ||||
-rw-r--r-- | ASTVisitor.h | 4 | ||||
-rw-r--r-- | AST_accept.h | 30 | ||||
-rw-r--r-- | Compiler.cpp | 34 | ||||
-rw-r--r-- | Compiler.h | 1 | ||||
-rw-r--r-- | CompilerStack.cpp | 9 | ||||
-rw-r--r-- | NameAndTypeResolver.cpp | 13 | ||||
-rw-r--r-- | NameAndTypeResolver.h | 2 | ||||
-rw-r--r-- | Parser.cpp | 67 | ||||
-rw-r--r-- | Parser.h | 5 | ||||
-rw-r--r-- | grammar.txt | 5 |
15 files changed, 213 insertions, 19 deletions
@@ -130,6 +130,17 @@ void WhileStatement::checkTypeRequirements() m_body->checkTypeRequirements(); } +void ForStatement::checkTypeRequirements() +{ + if (m_initExpression) + m_initExpression->checkTypeRequirements(); + if (m_condExpression) + m_condExpression->expectType(BoolType()); + if (m_loopExpression) + m_loopExpression->checkTypeRequirements(); + m_body->checkTypeRequirements(); +} + void Return::checkTypeRequirements() { if (!m_expression) @@ -509,6 +509,42 @@ private: ASTPointer<Statement> m_body; }; +/** + * For loop statement + */ +class ForStatement: public BreakableStatement +{ +public: + ForStatement(Location const& _location, + ASTPointer<Statement> const& _initExpression, + ASTPointer<Expression> const& _conditionExpression, + ASTPointer<ExpressionStatement> const& _loopExpression, + ASTPointer<Statement> const& _body): + BreakableStatement(_location), + m_initExpression(_initExpression), + m_condExpression(_conditionExpression), + m_loopExpression(_loopExpression), + m_body(_body) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + virtual void checkTypeRequirements() override; + + Statement const* getInitializationExpression() const { return m_initExpression.get(); } + Expression const* getCondition() const { return m_condExpression.get(); } + ExpressionStatement const* getLoopExpression() const { return m_loopExpression.get(); } + Statement const& getBody() const { return *m_body; } + +private: + /// For statement's initialization expresion. for(XXX; ; ). Can be empty + ASTPointer<Statement> m_initExpression; + /// For statement's condition expresion. for(; XXX ; ). Can be empty + ASTPointer<Expression> m_condExpression; + /// For statement's loop expresion. for(;;XXX). Can be empty + ASTPointer<ExpressionStatement> m_loopExpression; + /// The body of the loop + ASTPointer<Statement> m_body; +}; + class Continue: public Statement { public: diff --git a/ASTForward.h b/ASTForward.h index d6c4c9f4..c960fc8f 100644 --- a/ASTForward.h +++ b/ASTForward.h @@ -52,6 +52,7 @@ class Block; class IfStatement; class BreakableStatement; class WhileStatement; +class ForStatement; class Continue; class Break; class Return; diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp index 970822a9..916fca1e 100644 --- a/ASTPrinter.cpp +++ b/ASTPrinter.cpp @@ -150,6 +150,13 @@ bool ASTPrinter::visit(WhileStatement const& _node) return goDeeper(); } +bool ASTPrinter::visit(ForStatement const& _node) +{ + writeLine("ForStatement"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(Continue const& _node) { writeLine("Continue"); @@ -360,6 +367,11 @@ void ASTPrinter::endVisit(WhileStatement const&) m_indentation--; } +void ASTPrinter::endVisit(ForStatement const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(Continue const&) { m_indentation--; diff --git a/ASTPrinter.h b/ASTPrinter.h index 15b65e3f..fc5fb4ac 100644 --- a/ASTPrinter.h +++ b/ASTPrinter.h @@ -57,6 +57,7 @@ public: bool visit(IfStatement const& _node) override; bool visit(BreakableStatement const& _node) override; bool visit(WhileStatement const& _node) override; + bool visit(ForStatement const& _node) override; bool visit(Continue const& _node) override; bool visit(Break const& _node) override; bool visit(Return const& _node) override; @@ -90,6 +91,7 @@ public: void endVisit(IfStatement const&) override; void endVisit(BreakableStatement const&) override; void endVisit(WhileStatement const&) override; + void endVisit(ForStatement const&) override; void endVisit(Continue const&) override; void endVisit(Break const&) override; void endVisit(Return const&) override; diff --git a/ASTVisitor.h b/ASTVisitor.h index 5728cd3d..33a8a338 100644 --- a/ASTVisitor.h +++ b/ASTVisitor.h @@ -58,6 +58,7 @@ public: virtual bool visit(IfStatement&) { return true; } virtual bool visit(BreakableStatement&) { return true; } virtual bool visit(WhileStatement&) { return true; } + virtual bool visit(ForStatement&) { return true; } virtual bool visit(Continue&) { return true; } virtual bool visit(Break&) { return true; } virtual bool visit(Return&) { return true; } @@ -93,6 +94,7 @@ public: virtual void endVisit(IfStatement&) { } virtual void endVisit(BreakableStatement&) { } virtual void endVisit(WhileStatement&) { } + virtual void endVisit(ForStatement&) { } virtual void endVisit(Continue&) { } virtual void endVisit(Break&) { } virtual void endVisit(Return&) { } @@ -132,6 +134,7 @@ public: virtual bool visit(IfStatement const&) { return true; } virtual bool visit(BreakableStatement const&) { return true; } virtual bool visit(WhileStatement const&) { return true; } + virtual bool visit(ForStatement const&) { return true; } virtual bool visit(Continue const&) { return true; } virtual bool visit(Break const&) { return true; } virtual bool visit(Return const&) { return true; } @@ -167,6 +170,7 @@ public: virtual void endVisit(IfStatement const&) { } virtual void endVisit(BreakableStatement const&) { } virtual void endVisit(WhileStatement const&) { } + virtual void endVisit(ForStatement const&) { } virtual void endVisit(Continue const&) { } virtual void endVisit(Break const&) { } virtual void endVisit(Return const&) { } diff --git a/AST_accept.h b/AST_accept.h index e0454d33..0e5a71b6 100644 --- a/AST_accept.h +++ b/AST_accept.h @@ -267,6 +267,36 @@ void WhileStatement::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void ForStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (m_initExpression) + m_initExpression->accept(_visitor); + if (m_condExpression) + m_condExpression->accept(_visitor); + if (m_loopExpression) + m_loopExpression->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ForStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (m_initExpression) + m_initExpression->accept(_visitor); + if (m_condExpression) + m_condExpression->accept(_visitor); + if (m_loopExpression) + m_loopExpression->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + void Continue::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/Compiler.cpp b/Compiler.cpp index 0f85cda3..394ae5f8 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -302,6 +302,40 @@ bool Compiler::visit(WhileStatement const& _whileStatement) return false; } +bool Compiler::visit(ForStatement const& _forStatement) +{ + eth::AssemblyItem loopStart = m_context.newTag(); + eth::AssemblyItem loopEnd = m_context.newTag(); + m_continueTags.push_back(loopStart); + m_breakTags.push_back(loopEnd); + + if (_forStatement.getInitializationExpression()) + _forStatement.getInitializationExpression()->accept(*this); + + m_context << loopStart; + + // if there is no terminating condition in for, default is to always be true + if (_forStatement.getCondition()) + { + compileExpression(*_forStatement.getCondition()); + m_context << eth::Instruction::ISZERO; + m_context.appendConditionalJumpTo(loopEnd); + } + + _forStatement.getBody().accept(*this); + + // for's loop expression if existing + if (_forStatement.getLoopExpression()) + _forStatement.getLoopExpression()->accept(*this); + + m_context.appendJumpTo(loopStart); + m_context << loopEnd; + + m_continueTags.pop_back(); + m_breakTags.pop_back(); + return false; +} + bool Compiler::visit(Continue const&) { if (!m_continueTags.empty()) @@ -59,6 +59,7 @@ private: virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(IfStatement const& _ifStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override; + virtual bool visit(ForStatement const& _forStatement) override; virtual bool visit(Continue const& _continue) override; virtual bool visit(Break const& _break) override; virtual bool visit(Return const& _return) override; diff --git a/CompilerStack.cpp b/CompilerStack.cpp index 23f5fd68..1242c0ab 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -73,6 +73,15 @@ void CompilerStack::parse() resolver.resolveNamesAndTypes(*contract); m_contracts[contract->getName()].contract = contract; } + for (Source const* source: m_sourceOrder) + for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) + if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) + { + m_globalContext->setCurrentContract(*contract); + resolver.updateDeclaration(*m_globalContext->getCurrentThis()); + resolver.checkTypeRequirements(*contract); + m_contracts[contract->getName()].contract = contract; + } m_parseSuccessful = true; } diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp index 2ad27680..80732615 100644 --- a/NameAndTypeResolver.cpp +++ b/NameAndTypeResolver.cpp @@ -49,8 +49,6 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) m_currentScope = &m_scopes[&_contract]; for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, nullptr); - for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) - structDef->checkValidityOfMembers(); for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, nullptr); for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) @@ -59,13 +57,16 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) ReferencesResolver referencesResolver(*function, *this, function->getReturnParameterList().get()); } - // First, the parameter types of all functions need to be resolved before we can check - // the types, since it is possible to call functions that are only defined later - // in the source. - _contract.checkTypeRequirements(); m_currentScope = &m_scopes[nullptr]; } +void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) +{ + for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) + structDef->checkValidityOfMembers(); + _contract.checkTypeRequirements(); +} + void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) { m_scopes[nullptr].registerDeclaration(_declaration, true); diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h index 1ff9febf..23ac5fe7 100644 --- a/NameAndTypeResolver.h +++ b/NameAndTypeResolver.h @@ -46,6 +46,8 @@ public: void registerDeclarations(SourceUnit& _sourceUnit); /// Resolves all names and types referenced from the given contract. void resolveNamesAndTypes(ContractDefinition& _contract); + /// Check all type requirements in the given contract. + void checkTypeRequirements(ContractDefinition& _contract); /// Updates the given global declaration (used for "this"). Not to be used with declarations /// that create their own scope. void updateDeclaration(Declaration const& _declaration); @@ -304,6 +304,8 @@ ASTPointer<Statement> Parser::parseStatement() return parseIfStatement(); case Token::WHILE: return parseWhileStatement(); + case Token::FOR: + return parseForStatement(); case Token::LBRACE: return parseBlock(); // starting from here, all statements must be terminated by a semicolon @@ -328,18 +330,7 @@ ASTPointer<Statement> Parser::parseStatement() } break; default: - // distinguish between variable definition (and potentially assignment) and expression statement - // (which include assignments to other expressions and pre-declared variables) - // We have a variable definition if we get a keyword that specifies a type name, or - // in the case of a user-defined type, we have two identifiers following each other. - if (m_scanner->getCurrentToken() == Token::MAPPING || - m_scanner->getCurrentToken() == Token::VAR || - ((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || - m_scanner->getCurrentToken() == Token::IDENTIFIER) && - m_scanner->peekNextToken() == Token::IDENTIFIER)) - statement = parseVariableDefinition(); - else // "ordinary" expression statement - statement = parseExpressionStatement(); + statement = parseVarDefOrExprStmt(); } expectToken(Token::SEMICOLON); return statement; @@ -377,6 +368,44 @@ ASTPointer<WhileStatement> Parser::parseWhileStatement() return nodeFactory.createNode<WhileStatement>(condition, body); } +ASTPointer<ForStatement> Parser::parseForStatement() +{ + ASTNodeFactory nodeFactory(*this); + ASTPointer<Statement> initExpression; + ASTPointer<Expression> conditionExpression; + ASTPointer<ExpressionStatement> 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->getCurrentToken() != Token::SEMICOLON) + initExpression = parseVarDefOrExprStmt(); + expectToken(Token::SEMICOLON); + + if (m_scanner->getCurrentToken() != Token::SEMICOLON) + conditionExpression = parseExpression(); + expectToken(Token::SEMICOLON); + + if (m_scanner->getCurrentToken() != Token::RPAREN) + loopExpression = parseExpressionStatement(); + expectToken(Token::RPAREN); + + ASTPointer<Statement> body = parseStatement(); + nodeFactory.setEndPositionFromNode(body); + return nodeFactory.createNode<ForStatement>(initExpression, + conditionExpression, + loopExpression, + body); +} + +ASTPointer<Statement> Parser::parseVarDefOrExprStmt() +{ + if (peekVariableDefinition()) + return parseVariableDefinition(); + else + return parseExpressionStatement(); +} + ASTPointer<VariableDefinition> Parser::parseVariableDefinition() { ASTNodeFactory nodeFactory(*this); @@ -566,6 +595,20 @@ vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments() return arguments; } + +bool Parser::peekVariableDefinition() +{ + // distinguish between variable definition (and potentially assignment) and expression statement + // (which include assignments to other expressions and pre-declared variables) + // We have a variable definition if we get a keyword that specifies a type name, or + // in the case of a user-defined type, we have two identifiers following each other. + return (m_scanner->getCurrentToken() == Token::MAPPING || + m_scanner->getCurrentToken() == Token::VAR || + ((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || + m_scanner->getCurrentToken() == Token::IDENTIFIER) && + m_scanner->peekNextToken() == Token::IDENTIFIER)); +} + void Parser::expectToken(Token::Value _value) { if (m_scanner->getCurrentToken() != _value) @@ -59,6 +59,8 @@ private: ASTPointer<Statement> parseStatement(); ASTPointer<IfStatement> parseIfStatement(); ASTPointer<WhileStatement> parseWhileStatement(); + ASTPointer<ForStatement> parseForStatement(); + ASTPointer<Statement> parseVarDefOrExprStmt(); ASTPointer<VariableDefinition> parseVariableDefinition(); ASTPointer<ExpressionStatement> parseExpressionStatement(); ASTPointer<Expression> parseExpression(); @@ -72,6 +74,9 @@ private: ///@{ ///@name Helper functions + /// Peeks ahead in the scanner to determine if a variable definition is going to follow + bool peekVariableDefinition(); + /// If current token value is not _value, throw exception otherwise advance token. void expectToken(Token::Value _value); Token::Value expectAssignmentOperator(); diff --git a/grammar.txt b/grammar.txt index a26f717a..8c34997b 100644 --- a/grammar.txt +++ b/grammar.txt @@ -16,10 +16,13 @@ Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | Block | - ( Continue | Break | Return | VariableDefinition | Expression ) ';' + ( Continue | Break | Return | VariableDefinition | ExpressionStatement ) ';' +ExpressionStatement = Expression IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement +VardefOrExprStmt = Variabledefinition | ExpressionStatement +ForStatement = 'for' '(' (VardefOrExprStmt)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' |