mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
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:
parent
dc8a5f4ef5
commit
4524ad0870
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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?
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user