Skip to content

Commit

Permalink
Add support for do/while loops
Browse files Browse the repository at this point in the history
This commit adds support for a standard do <statement> while <expr>;
form of statement.  While loops were already being supported; supporting
a do/while loop mostly involves reusing code from while loops but putting
the conditional checking last.
  • Loading branch information
roadriverrail committed Nov 10, 2016
1 parent dc8a5f4 commit 4524ad0
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 11 deletions.
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
4 changes: 2 additions & 2 deletions docs/control-structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions libsolidity/ast/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -1005,18 +1005,22 @@ class WhileStatement: public BreakableStatement
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;
};

/**
Expand Down
6 changes: 5 additions & 1 deletion libsolidity/ast/ASTJsonConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion libsolidity/ast/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
19 changes: 16 additions & 3 deletions libsolidity/codegen/ContractCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
10 changes: 10 additions & 0 deletions libsolidity/formal/Why3Translator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 2 additions & 1 deletion libsolidity/grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) ';'

Expand All @@ -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?
Expand Down
19 changes: 18 additions & 1 deletion libsolidity/parsing/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions libsolidity/parsing/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class Parser: public ParserBase
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);
Expand Down
28 changes: 28 additions & 0 deletions test/libsolidity/SolidityEndToEndTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 4524ad0

Please sign in to comment.