From 30c000d121dc0132a6c5abb9d6e3da96b4dfda4a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Dec 2014 17:45:18 +0100 Subject: [PATCH 1/8] Adding a ForStatement solidity AST Node. - Adding ForStatement node - Implemented Parsing for ForStatement - A simple parsing test for the ForStatement - Work in progress --- libsolidity/AST.cpp | 7 +++++ libsolidity/AST.h | 24 +++++++++++++++++ libsolidity/ASTForward.h | 1 + libsolidity/ASTPrinter.cpp | 12 +++++++++ libsolidity/ASTPrinter.h | 2 ++ libsolidity/ASTVisitor.h | 4 +++ libsolidity/AST_accept.h | 24 +++++++++++++++++ libsolidity/Compiler.cpp | 7 +++++ libsolidity/Compiler.h | 1 + libsolidity/Parser.cpp | 54 +++++++++++++++++++++++++++++++------- libsolidity/Parser.h | 6 +++++ libsolidity/grammar.txt | 1 + test/solidityParser.cpp | 28 ++++++++++++++++++++ 13 files changed, 162 insertions(+), 9 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 5e344eadb..c286c412b 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -130,6 +130,13 @@ void WhileStatement::checkTypeRequirements() m_body->checkTypeRequirements(); } +void ForStatement::checkTypeRequirements() +{ + // LTODO + m_condExpression->expectType(BoolType()); + m_body->checkTypeRequirements(); +} + void Return::checkTypeRequirements() { if (!m_expression) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 4bb623b49..297b7f4e9 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -509,6 +509,30 @@ private: ASTPointer m_body; }; +class ForStatement: public BreakableStatement +{ +public: + ForStatement(Location const& _location, + ASTPointer const& _initExpression, + ASTPointer const& _conditionExpression, + ASTPointer const& _loopExpression, + ASTPointer 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; + +private: + ASTPointer m_initExpression; + ASTPointer m_condExpression; + ASTPointer m_loopExpression; + ASTPointer m_body; +}; + class Continue: public Statement { public: diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index d6c4c9f44..c960fc8f0 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/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/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 970822a9a..916fca1ef 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/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/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 15b65e3f0..fc5fb4acb 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/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/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index 5728cd3d0..33a8a3383 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/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/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index e0454d33e..ffef6f8b2 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -267,6 +267,30 @@ void WhileStatement::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void ForStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_initExpression->accept(_visitor); + m_condExpression->accept(_visitor); + m_loopExpression->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ForStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_initExpression->accept(_visitor); + m_condExpression->accept(_visitor); + m_loopExpression->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + void Continue::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index f2877c1ce..a0cad5374 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -287,6 +287,13 @@ bool Compiler::visit(WhileStatement const& _whileStatement) return false; } +bool Compiler::visit(ForStatement const& _forStatement) +{ + // LTODO + (void) _forStatement; + return false; +} + bool Compiler::visit(Continue const&) { if (!m_continueTags.empty()) diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 57e40cdae..c4f46d10f 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -53,6 +53,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/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 21651eb14..9941fbf43 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -304,6 +304,8 @@ ASTPointer 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,15 +330,7 @@ ASTPointer 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)) + if (peekVariableDefinition()) statement = parseVariableDefinition(); else // "ordinary" expression statement statement = parseExpressionStatement(); @@ -377,6 +371,34 @@ ASTPointer Parser::parseWhileStatement() return nodeFactory.createNode(condition, body); } +ASTPointer Parser::parseForStatement() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::FOR); + expectToken(Token::LPAREN); + ASTPointer initExpression = parseVardefOrExprstatement(); + expectToken(Token::SEMICOLON); + ASTPointer conditionExpression = parseExpression(); + expectToken(Token::SEMICOLON); + ASTPointer loopExpression = parseExpressionStatement(); + expectToken(Token::SEMICOLON); + expectToken(Token::RPAREN); + ASTPointer body = parseStatement(); + nodeFactory.setEndPositionFromNode(body); + return nodeFactory.createNode(initExpression, + conditionExpression, + loopExpression, + body); +} + +ASTPointer Parser::parseVardefOrExprstatement() +{ + if (peekVariableDefinition()) + return parseVariableDefinition(); + else + return parseExpressionStatement(); +} + ASTPointer Parser::parseVariableDefinition() { ASTNodeFactory nodeFactory(*this); @@ -566,6 +588,20 @@ vector> Parser::parseFunctionCallArguments() return arguments; } + +// 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. +bool Parser::peekVariableDefinition() +{ + 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) diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 52a374e03..343a85c0e 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -59,6 +59,8 @@ private: ASTPointer parseStatement(); ASTPointer parseIfStatement(); ASTPointer parseWhileStatement(); + ASTPointer parseForStatement(); + ASTPointer parseVardefOrExprstatement(); ASTPointer parseVariableDefinition(); ASTPointer parseExpressionStatement(); ASTPointer parseExpression(); @@ -72,6 +74,10 @@ private: ///@{ ///@name Helper functions + /// Peeks ahead in the scanner to determine if an expression statement + /// could be a variable definition + 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/libsolidity/grammar.txt b/libsolidity/grammar.txt index a26f717a1..99a590014 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -20,6 +20,7 @@ Statement = IfStatement | WhileStatement | Block | IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement +ForStatement = 'for' '(' Expressionstatement Expression Expressionstatement ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index a9e2d531f..8268901be 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -49,6 +49,23 @@ ASTPointer parseText(std::string const& _source) BOOST_FAIL("No contract found in source."); return ASTPointer(); } + +ASTPointer parseTextExplainError(std::string const& _source) +{ + try + { + return parseText(_source); + } + catch (Exception const& exception) + { + // LTODO: Print the error in a kind of a better way? + // In absence of CompilerStack we can't use SourceReferenceFormatter + cout << "Exception while parsing: " << diagnostic_information(exception); + // rethrow to signal test failure + throw exception; + } +} + } @@ -357,6 +374,17 @@ BOOST_AUTO_TEST_CASE(while_loop) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(for_loop) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " for (uint256 i = 0; i < 10; i++;)\n" + " { uint256 x = i; break; continue; }\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + BOOST_AUTO_TEST_CASE(if_statement) { char const* text = "contract test {\n" From e934aa0befb759ae3fafb545dd9398833a49bbe7 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 15 Dec 2014 17:54:29 +0100 Subject: [PATCH 2/8] new ForStatement parsing test and small grammar fix --- libsolidity/Parser.cpp | 1 - libsolidity/grammar.txt | 2 +- test/solidityParser.cpp | 16 ++++++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 9941fbf43..d936cfc72 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -381,7 +381,6 @@ ASTPointer Parser::parseForStatement() ASTPointer conditionExpression = parseExpression(); expectToken(Token::SEMICOLON); ASTPointer loopExpression = parseExpressionStatement(); - expectToken(Token::SEMICOLON); expectToken(Token::RPAREN); ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 99a590014..76e7bb6b6 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -20,7 +20,7 @@ Statement = IfStatement | WhileStatement | Block | IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement -ForStatement = 'for' '(' Expressionstatement Expression Expressionstatement ')' Statement +ForStatement = 'for' '(' VardefOrExpressionstatement ';' Expression ';' Expressionstatement ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 8268901be..53bd3f163 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -374,11 +374,23 @@ BOOST_AUTO_TEST_CASE(while_loop) BOOST_CHECK_NO_THROW(parseText(text)); } -BOOST_AUTO_TEST_CASE(for_loop) +BOOST_AUTO_TEST_CASE(for_loop_vardef_initexpr) { char const* text = "contract test {\n" " function fun(uint256 a) {\n" - " for (uint256 i = 0; i < 10; i++;)\n" + " for (uint256 i = 0; i < 10; i++)\n" + " { uint256 x = i; break; continue; }\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + +BOOST_AUTO_TEST_CASE(for_loop_simple_initexpr) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 i =0;\n" + " for (i = 0; i < 10; i++)\n" " { uint256 x = i; break; continue; }\n" " }\n" "}\n"; From b2992bd659768b0ee8f33ea24bf4e308efea3891 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 09:51:51 +0100 Subject: [PATCH 3/8] Solidity ForStatements expressions are now optional --- libsolidity/AST_accept.h | 18 ++++++++++++------ libsolidity/Parser.cpp | 17 ++++++++++++++--- libsolidity/grammar.txt | 2 +- test/solidityParser.cpp | 12 ++++++++++++ 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index ffef6f8b2..0e5a71b62 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -271,9 +271,12 @@ void ForStatement::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { - m_initExpression->accept(_visitor); - m_condExpression->accept(_visitor); - m_loopExpression->accept(_visitor); + 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); @@ -283,9 +286,12 @@ void ForStatement::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { - m_initExpression->accept(_visitor); - m_condExpression->accept(_visitor); - m_loopExpression->accept(_visitor); + 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); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index d936cfc72..598f5ad47 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -374,14 +374,25 @@ ASTPointer Parser::parseWhileStatement() ASTPointer Parser::parseForStatement() { ASTNodeFactory nodeFactory(*this); + ASTPointer initExpression; + ASTPointer conditionExpression; + ASTPointer loopExpression; expectToken(Token::FOR); expectToken(Token::LPAREN); - ASTPointer initExpression = parseVardefOrExprstatement(); + + // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN? + if (m_scanner->getCurrentToken() != Token::SEMICOLON) + initExpression = parseVardefOrExprstatement(); expectToken(Token::SEMICOLON); - ASTPointer conditionExpression = parseExpression(); + + if (m_scanner->getCurrentToken() != Token::SEMICOLON) + conditionExpression = parseExpression(); expectToken(Token::SEMICOLON); - ASTPointer loopExpression = parseExpressionStatement(); + + if (m_scanner->getCurrentToken() != Token::RPAREN) + loopExpression = parseExpressionStatement(); expectToken(Token::RPAREN); + ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); return nodeFactory.createNode(initExpression, diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 76e7bb6b6..7c0ac3a50 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -20,7 +20,7 @@ Statement = IfStatement | WhileStatement | Block | IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement -ForStatement = 'for' '(' VardefOrExpressionstatement ';' Expression ';' Expressionstatement ')' Statement +ForStatement = 'for' '(' (VardefOrExpressionstatement)? ';' (Expression)? ';' (Expressionstatement)? ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 53bd3f163..d57754b61 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -397,6 +397,18 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_initexpr) BOOST_CHECK_NO_THROW(parseTextExplainError(text)); } +BOOST_AUTO_TEST_CASE(for_loop_simple_noexpr) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 i =0;\n" + " for (;;)\n" + " { uint256 x = i; break; continue; }\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + BOOST_AUTO_TEST_CASE(if_statement) { char const* text = "contract test {\n" From 5eec2c5ac6d5a7f543a9eb33bdb3365653146fa1 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 13:20:50 +0100 Subject: [PATCH 4/8] ForStatement typecheck and initExpression is a Statement --- libsolidity/AST.cpp | 8 ++++++-- libsolidity/AST.h | 4 ++-- libsolidity/Parser.cpp | 4 ++-- libsolidity/Parser.h | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index c286c412b..4c042f2cf 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -132,8 +132,12 @@ void WhileStatement::checkTypeRequirements() void ForStatement::checkTypeRequirements() { - // LTODO - m_condExpression->expectType(BoolType()); + if (m_initExpression) + m_initExpression->checkTypeRequirements(); + if (m_condExpression) + m_condExpression->expectType(BoolType()); + if (m_loopExpression) + m_loopExpression->checkTypeRequirements(); m_body->checkTypeRequirements(); } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 297b7f4e9..2fb6a2e32 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -513,7 +513,7 @@ class ForStatement: public BreakableStatement { public: ForStatement(Location const& _location, - ASTPointer const& _initExpression, + ASTPointer const& _initExpression, ASTPointer const& _conditionExpression, ASTPointer const& _loopExpression, ASTPointer const& _body): @@ -527,7 +527,7 @@ public: virtual void checkTypeRequirements() override; private: - ASTPointer m_initExpression; + ASTPointer m_initExpression; ASTPointer m_condExpression; ASTPointer m_loopExpression; ASTPointer m_body; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 598f5ad47..2669ce209 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -374,7 +374,7 @@ ASTPointer Parser::parseWhileStatement() ASTPointer Parser::parseForStatement() { ASTNodeFactory nodeFactory(*this); - ASTPointer initExpression; + ASTPointer initExpression; ASTPointer conditionExpression; ASTPointer loopExpression; expectToken(Token::FOR); @@ -401,7 +401,7 @@ ASTPointer Parser::parseForStatement() body); } -ASTPointer Parser::parseVardefOrExprstatement() +ASTPointer Parser::parseVardefOrExprstatement() { if (peekVariableDefinition()) return parseVariableDefinition(); diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 343a85c0e..cd3d5bab6 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -60,7 +60,7 @@ private: ASTPointer parseIfStatement(); ASTPointer parseWhileStatement(); ASTPointer parseForStatement(); - ASTPointer parseVardefOrExprstatement(); + ASTPointer parseVardefOrExprstatement(); ASTPointer parseVariableDefinition(); ASTPointer parseExpressionStatement(); ASTPointer parseExpression(); From 1f0346396ae307aa87e184f3ac8f0d7f9a97c832 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 14:46:17 +0100 Subject: [PATCH 5/8] Minor issues, grammar update, new ForStatement test --- libsolidity/AST.h | 7 +++++++ libsolidity/Parser.cpp | 9 +++------ libsolidity/Parser.h | 5 ++--- libsolidity/grammar.txt | 6 ++++-- test/solidityParser.cpp | 12 ++++++++++++ 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 2fb6a2e32..dcbca1ee8 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -509,6 +509,9 @@ private: ASTPointer m_body; }; +/** + * For loop statement + */ class ForStatement: public BreakableStatement { public: @@ -527,9 +530,13 @@ public: virtual void checkTypeRequirements() override; private: + /// For statement's initialization expresion. for(XXX; ; ). Can be empty ASTPointer m_initExpression; + /// For statement's condition expresion. for(; XXX ; ). Can be empty ASTPointer m_condExpression; + /// For statement's loop expresion. for(;;XXX). Can be empty ASTPointer m_loopExpression; + /// The body of the loop ASTPointer m_body; }; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 2669ce209..732356751 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -330,10 +330,7 @@ ASTPointer Parser::parseStatement() } break; default: - if (peekVariableDefinition()) - statement = parseVariableDefinition(); - else // "ordinary" expression statement - statement = parseExpressionStatement(); + statement = parseVarDefOrExprStmt(); } expectToken(Token::SEMICOLON); return statement; @@ -382,7 +379,7 @@ ASTPointer Parser::parseForStatement() // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN? if (m_scanner->getCurrentToken() != Token::SEMICOLON) - initExpression = parseVardefOrExprstatement(); + initExpression = parseVarDefOrExprStmt(); expectToken(Token::SEMICOLON); if (m_scanner->getCurrentToken() != Token::SEMICOLON) @@ -401,7 +398,7 @@ ASTPointer Parser::parseForStatement() body); } -ASTPointer Parser::parseVardefOrExprstatement() +ASTPointer Parser::parseVarDefOrExprStmt() { if (peekVariableDefinition()) return parseVariableDefinition(); diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index cd3d5bab6..bf3a6beac 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -60,7 +60,7 @@ private: ASTPointer parseIfStatement(); ASTPointer parseWhileStatement(); ASTPointer parseForStatement(); - ASTPointer parseVardefOrExprstatement(); + ASTPointer parseVarDefOrExprStmt(); ASTPointer parseVariableDefinition(); ASTPointer parseExpressionStatement(); ASTPointer parseExpression(); @@ -74,8 +74,7 @@ private: ///@{ ///@name Helper functions - /// Peeks ahead in the scanner to determine if an expression statement - /// could be a variable definition + /// 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. diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 7c0ac3a50..8c34997b9 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -16,11 +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 -ForStatement = 'for' '(' (VardefOrExpressionstatement)? ';' (Expression)? ';' (Expressionstatement)? ')' Statement +VardefOrExprStmt = Variabledefinition | ExpressionStatement +ForStatement = 'for' '(' (VardefOrExprStmt)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index d57754b61..f978cdd9b 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -409,6 +409,18 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_noexpr) BOOST_CHECK_NO_THROW(parseTextExplainError(text)); } +BOOST_AUTO_TEST_CASE(for_loop_single_stmt_body) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 i =0;\n" + " for (i = 0; i < 10; i++)\n" + " continue;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + BOOST_AUTO_TEST_CASE(if_statement) { char const* text = "contract test {\n" From 3c6e966160d828882d243ee37b52b23992891404 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 16:52:47 +0100 Subject: [PATCH 6/8] Solidity ForStatement Compiler part - Work in progress --- libsolidity/AST.h | 5 +++++ libsolidity/Compiler.cpp | 31 +++++++++++++++++++++++++++++-- test/solidityEndToEndTest.cpp | 22 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index dcbca1ee8..c83690443 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -529,6 +529,11 @@ public: 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 m_initExpression; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index a0cad5374..8c70b2718 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -289,8 +289,35 @@ bool Compiler::visit(WhileStatement const& _whileStatement) bool Compiler::visit(ForStatement const& _forStatement) { - // LTODO - (void) _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; } diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index b6f63aa72..32de9af46 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -176,6 +176,28 @@ BOOST_AUTO_TEST_CASE(nested_loops) testSolidityAgainstCppOnRange(0, nested_loops_cpp, 0, 12); } +BOOST_AUTO_TEST_CASE(for_loop) +{ + char const* sourceCode = "contract test {\n" + " function f(uint n) returns(uint nfac) {\n" + " nfac = 1;\n" + " for (var i = 2; i <= n; i++)\n" + " nfac *= 1;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + auto for_loop_cpp = [](u256 const& n) -> u256 + { + u256 nfac = 1; + for (auto i = 2; i <= n; i++) + nfac *= i; + return nfac; + }; + + testSolidityAgainstCppOnRange(0, for_loop_cpp, 0, 5); +} + BOOST_AUTO_TEST_CASE(calling_other_functions) { // note that the index of a function is its index in the sorted sequence of functions From 82117dc7dab431b2de6cfb73a6b17343ff71b57b Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 16 Dec 2014 17:35:00 +0100 Subject: [PATCH 7/8] Solidity: More tests for the ForStatement --- test/solidityEndToEndTest.cpp | 54 ++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 32de9af46..ecd3b63a3 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -182,7 +182,7 @@ BOOST_AUTO_TEST_CASE(for_loop) " function f(uint n) returns(uint nfac) {\n" " nfac = 1;\n" " for (var i = 2; i <= n; i++)\n" - " nfac *= 1;\n" + " nfac *= i;\n" " }\n" "}\n"; compileAndRun(sourceCode); @@ -198,6 +198,58 @@ BOOST_AUTO_TEST_CASE(for_loop) testSolidityAgainstCppOnRange(0, for_loop_cpp, 0, 5); } +BOOST_AUTO_TEST_CASE(for_loop_empty) +{ + char const* sourceCode = "contract test {\n" + " function f() returns(uint ret) {\n" + " ret = 1;\n" + " for (;;)\n" + " {\n" + " ret += 1;\n" + " if (ret >= 10) break;\n" + " }\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + auto for_loop_empty_cpp = []() -> u256 + { + u256 ret = 1; + for (;;) + { + ret += 1; + if (ret >= 10) break; + } + return ret; + }; + + testSolidityAgainstCpp(0, for_loop_empty_cpp); +} + +BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) +{ + char const* sourceCode = "contract test {\n" + " function f(uint n) returns(uint nfac) {\n" + " nfac = 1;\n" + " uint256 i;\n" + " for (i = 2; i <= n; i++)\n" + " nfac *= i;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256 + { + u256 nfac = 1; + u256 i; + for (i = 2; i <= n; i++) + nfac *= i; + return nfac; + }; + + testSolidityAgainstCppOnRange(0, for_loop_simple_init_expr_cpp, 0, 5); +} + BOOST_AUTO_TEST_CASE(calling_other_functions) { // note that the index of a function is its index in the sorted sequence of functions From bcc263070b5851dfa432272d18b06ec32a6fb59c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 17 Dec 2014 00:28:26 +0100 Subject: [PATCH 8/8] Moving comment to function body --- libsolidity/Parser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 732356751..e287319d2 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -596,12 +596,12 @@ vector> Parser::parseFunctionCallArguments() } -// 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. 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()) ||