Add support for do/while loops

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.
This commit is contained in:
Rhett Aultman 2016-07-30 00:13:05 -07:00
parent dc8a5f4ef5
commit 4524ad0870
11 changed files with 92 additions and 11 deletions

View File

@ -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:

View File

@ -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

View File

@ -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;
};
/**

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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();

View File

@ -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?

View File

@ -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);

View File

@ -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);

View File

@ -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