diff options
-rw-r--r-- | Changelog.md | 3 | ||||
-rw-r--r-- | docs/control-structures.rst | 4 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 8 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 6 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.cpp | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 19 | ||||
-rw-r--r-- | libsolidity/formal/Why3Translator.cpp | 10 | ||||
-rw-r--r-- | libsolidity/grammar.txt | 3 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 19 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.h | 1 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 28 |
11 files changed, 92 insertions, 11 deletions
diff --git a/Changelog.md b/Changelog.md index ee106047..1ae2f57e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### 0.4.5 (unreleased) +Features: + * Do-while loops: support for a C-style do{<block>}while(<expr>); control structure + ### 0.4.4 (2016-10-31) Bugfixes: diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 597829d3..51f43015 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -2,14 +2,14 @@ Expressions and Control Structures ################################## -.. index:: if, else, while, for, break, continue, return, switch, goto +.. index:: if, else, while, do/while, for, break, continue, return, switch, goto Control Structures =================== Most of the control structures from C or JavaScript are available in Solidity except for ``switch`` and ``goto``. So -there is: ``if``, ``else``, ``while``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with +there is: ``if``, ``else``, ``while``, ``do``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with the usual semantics known from C or JavaScript. Parentheses can *not* be omitted for conditionals, but curly brances can be omitted diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 7ed4ddce..6c3f52bc 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1005,18 +1005,22 @@ public: SourceLocation const& _location, ASTPointer<ASTString> const& _docString, ASTPointer<Expression> const& _condition, - ASTPointer<Statement> const& _body + ASTPointer<Statement> const& _body, + bool _isDoWhile ): - BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body) {} + BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body), + m_isDoWhile(_isDoWhile) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& condition() const { return *m_condition; } Statement const& body() const { return *m_body; } + bool isDoWhile() const { return m_isDoWhile; } private: ASTPointer<Expression> m_condition; ASTPointer<Statement> m_body; + bool m_isDoWhile; }; /** diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index b573feda..3fce1180 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -264,7 +264,11 @@ bool ASTJsonConverter::visit(IfStatement const& _node) bool ASTJsonConverter::visit(WhileStatement const& _node) { - addJsonNode(_node, "WhileStatement", {}, true); + addJsonNode( + _node, + _node.isDoWhile() ? "DoWhileStatement" : "WhileStatement", + {}, + true); return true; } diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index a9de457a..27266968 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -208,7 +208,7 @@ bool ASTPrinter::visit(IfStatement const& _node) bool ASTPrinter::visit(WhileStatement const& _node) { - writeLine("WhileStatement"); + writeLine(_node.isDoWhile() ? "DoWhileStatement" : "WhileStatement"); printSourcePart(_node); return goDeeper(); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index ebb84784..1404963f 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -611,12 +611,25 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement) m_breakTags.push_back(loopEnd); m_context << loopStart; - compileExpression(_whileStatement.condition()); - m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(loopEnd); + + // While loops have the condition prepended + if (!_whileStatement.isDoWhile()) + { + compileExpression(_whileStatement.condition()); + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(loopEnd); + } _whileStatement.body().accept(*this); + // Do-while loops have the condition appended + if (_whileStatement.isDoWhile()) + { + compileExpression(_whileStatement.condition()); + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(loopEnd); + } + m_context.appendJumpTo(loopStart); m_context << loopEnd; diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index 813fa3ab..5934d593 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -410,6 +410,16 @@ bool Why3Translator::visit(WhileStatement const& _node) { addSourceFromDocStrings(_node.annotation()); + // Why3 does not appear to support do-while loops, + // so we will simulate them by performing a while + // loop with the body prepended once. + + if (_node.isDoWhile()) + { + visitIndentedUnlessBlock(_node.body()); + newLine(); + } + add("while "); _node.condition().accept(*this); newLine(); diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index d84ee10c..586c41e9 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -41,7 +41,7 @@ ArrayTypeName = TypeName StorageLocation? '[' Expression? ']' StorageLocation = 'memory' | 'storage' Block = '{' Statement* '}' -Statement = IfStatement | WhileStatement | ForStatement | Block | +Statement = IfStatement | WhileStatement | DoWhileStatement | ForStatement | Block | ( PlaceholderStatement | Continue | Break | Return | Throw | SimpleStatement ) ';' @@ -51,6 +51,7 @@ WhileStatement = 'while' '(' Expression ')' Statement PlaceholderStatement = '_' SimpleStatement = VariableDefinition | ExpressionStatement ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement +DoWhileStatement = 'do' Statement 'while' '(' Expression ')' ';' Continue = 'continue' Break = 'break' Return = 'return' Expression? diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 0e99d1e7..52b53619 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -722,6 +722,8 @@ ASTPointer<Statement> Parser::parseStatement() return parseIfStatement(docString); case Token::While: return parseWhileStatement(docString); + case Token::Do: + return parseDoWhileStatement(docString); case Token::For: return parseForStatement(docString); case Token::LBrace: @@ -816,9 +818,24 @@ ASTPointer<WhileStatement> Parser::parseWhileStatement(ASTPointer<ASTString> con expectToken(Token::RParen); ASTPointer<Statement> body = parseStatement(); nodeFactory.setEndPositionFromNode(body); - return nodeFactory.createNode<WhileStatement>(_docString, condition, body); + return nodeFactory.createNode<WhileStatement>(_docString, condition, body, false); } +ASTPointer<WhileStatement> Parser::parseDoWhileStatement(ASTPointer<ASTString> const& _docString) +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Do); + ASTPointer<Statement> body = parseStatement(); + expectToken(Token::While); + expectToken(Token::LParen); + ASTPointer<Expression> condition = parseExpression(); + expectToken(Token::RParen); + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode<WhileStatement>(_docString, condition, body, true); +} + + ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& _docString) { ASTNodeFactory nodeFactory(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 9c30cf60..26f347cb 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -85,6 +85,7 @@ private: ASTPointer<InlineAssembly> parseInlineAssembly(ASTPointer<ASTString> const& _docString = {}); ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString); ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString); + ASTPointer<WhileStatement> parseDoWhileStatement(ASTPointer<ASTString> const& _docString); ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString); /// A "simple statement" can be a variable declaration statement or an expression statement. ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8600443d..a1430b02 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -353,6 +353,34 @@ BOOST_AUTO_TEST_CASE(while_loop) testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5); } + +BOOST_AUTO_TEST_CASE(do_while_loop) +{ + char const* sourceCode = "contract test {\n" + " function f(uint n) returns(uint nfac) {\n" + " nfac = 1;\n" + " var i = 2;\n" + " do { nfac *= i++; } while (i <= n);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + auto do_while_loop_cpp = [](u256 const& n) -> u256 + { + u256 nfac = 1; + u256 i = 2; + do + { + nfac *= i++; + } + while (i <= n); + + return nfac; + }; + + testSolidityAgainstCppOnRange("f(uint256)", do_while_loop_cpp, 0, 5); +} + BOOST_AUTO_TEST_CASE(nested_loops) { // tests that break and continue statements in nested loops jump to the correct place |