mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #768 from roadriverrail/do_while_loops
Add support for do/while loops
This commit is contained in:
commit
a40dcfef12
@ -1,5 +1,8 @@
|
|||||||
### 0.4.5 (unreleased)
|
### 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)
|
### 0.4.4 (2016-10-31)
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
Expressions and Control Structures
|
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
|
Control Structures
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Most of the control structures from C or JavaScript are available in Solidity
|
Most of the control structures from C or JavaScript are available in Solidity
|
||||||
except for ``switch`` and ``goto``. So
|
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.
|
the usual semantics known from C or JavaScript.
|
||||||
|
|
||||||
Parentheses can *not* be omitted for conditionals, but curly brances can be omitted
|
Parentheses can *not* be omitted for conditionals, but curly brances can be omitted
|
||||||
|
@ -1005,18 +1005,22 @@ public:
|
|||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
ASTPointer<ASTString> const& _docString,
|
ASTPointer<ASTString> const& _docString,
|
||||||
ASTPointer<Expression> const& _condition,
|
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(ASTVisitor& _visitor) override;
|
||||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
Expression const& condition() const { return *m_condition; }
|
Expression const& condition() const { return *m_condition; }
|
||||||
Statement const& body() const { return *m_body; }
|
Statement const& body() const { return *m_body; }
|
||||||
|
bool isDoWhile() const { return m_isDoWhile; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ASTPointer<Expression> m_condition;
|
ASTPointer<Expression> m_condition;
|
||||||
ASTPointer<Statement> m_body;
|
ASTPointer<Statement> m_body;
|
||||||
|
bool m_isDoWhile;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -264,7 +264,11 @@ bool ASTJsonConverter::visit(IfStatement const& _node)
|
|||||||
|
|
||||||
bool ASTJsonConverter::visit(WhileStatement const& _node)
|
bool ASTJsonConverter::visit(WhileStatement const& _node)
|
||||||
{
|
{
|
||||||
addJsonNode(_node, "WhileStatement", {}, true);
|
addJsonNode(
|
||||||
|
_node,
|
||||||
|
_node.isDoWhile() ? "DoWhileStatement" : "WhileStatement",
|
||||||
|
{},
|
||||||
|
true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ bool ASTPrinter::visit(IfStatement const& _node)
|
|||||||
|
|
||||||
bool ASTPrinter::visit(WhileStatement const& _node)
|
bool ASTPrinter::visit(WhileStatement const& _node)
|
||||||
{
|
{
|
||||||
writeLine("WhileStatement");
|
writeLine(_node.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
|
||||||
printSourcePart(_node);
|
printSourcePart(_node);
|
||||||
return goDeeper();
|
return goDeeper();
|
||||||
}
|
}
|
||||||
|
@ -611,12 +611,25 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement)
|
|||||||
m_breakTags.push_back(loopEnd);
|
m_breakTags.push_back(loopEnd);
|
||||||
|
|
||||||
m_context << loopStart;
|
m_context << loopStart;
|
||||||
|
|
||||||
|
// While loops have the condition prepended
|
||||||
|
if (!_whileStatement.isDoWhile())
|
||||||
|
{
|
||||||
compileExpression(_whileStatement.condition());
|
compileExpression(_whileStatement.condition());
|
||||||
m_context << Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
m_context.appendConditionalJumpTo(loopEnd);
|
m_context.appendConditionalJumpTo(loopEnd);
|
||||||
|
}
|
||||||
|
|
||||||
_whileStatement.body().accept(*this);
|
_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.appendJumpTo(loopStart);
|
||||||
m_context << loopEnd;
|
m_context << loopEnd;
|
||||||
|
|
||||||
|
@ -410,6 +410,16 @@ bool Why3Translator::visit(WhileStatement const& _node)
|
|||||||
{
|
{
|
||||||
addSourceFromDocStrings(_node.annotation());
|
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 ");
|
add("while ");
|
||||||
_node.condition().accept(*this);
|
_node.condition().accept(*this);
|
||||||
newLine();
|
newLine();
|
||||||
|
@ -41,7 +41,7 @@ ArrayTypeName = TypeName StorageLocation? '[' Expression? ']'
|
|||||||
StorageLocation = 'memory' | 'storage'
|
StorageLocation = 'memory' | 'storage'
|
||||||
|
|
||||||
Block = '{' Statement* '}'
|
Block = '{' Statement* '}'
|
||||||
Statement = IfStatement | WhileStatement | ForStatement | Block |
|
Statement = IfStatement | WhileStatement | DoWhileStatement | ForStatement | Block |
|
||||||
( PlaceholderStatement | Continue | Break | Return |
|
( PlaceholderStatement | Continue | Break | Return |
|
||||||
Throw | SimpleStatement ) ';'
|
Throw | SimpleStatement ) ';'
|
||||||
|
|
||||||
@ -51,6 +51,7 @@ WhileStatement = 'while' '(' Expression ')' Statement
|
|||||||
PlaceholderStatement = '_'
|
PlaceholderStatement = '_'
|
||||||
SimpleStatement = VariableDefinition | ExpressionStatement
|
SimpleStatement = VariableDefinition | ExpressionStatement
|
||||||
ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
|
ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
|
||||||
|
DoWhileStatement = 'do' Statement 'while' '(' Expression ')' ';'
|
||||||
Continue = 'continue'
|
Continue = 'continue'
|
||||||
Break = 'break'
|
Break = 'break'
|
||||||
Return = 'return' Expression?
|
Return = 'return' Expression?
|
||||||
|
@ -722,6 +722,8 @@ ASTPointer<Statement> Parser::parseStatement()
|
|||||||
return parseIfStatement(docString);
|
return parseIfStatement(docString);
|
||||||
case Token::While:
|
case Token::While:
|
||||||
return parseWhileStatement(docString);
|
return parseWhileStatement(docString);
|
||||||
|
case Token::Do:
|
||||||
|
return parseDoWhileStatement(docString);
|
||||||
case Token::For:
|
case Token::For:
|
||||||
return parseForStatement(docString);
|
return parseForStatement(docString);
|
||||||
case Token::LBrace:
|
case Token::LBrace:
|
||||||
@ -816,9 +818,24 @@ ASTPointer<WhileStatement> Parser::parseWhileStatement(ASTPointer<ASTString> con
|
|||||||
expectToken(Token::RParen);
|
expectToken(Token::RParen);
|
||||||
ASTPointer<Statement> body = parseStatement();
|
ASTPointer<Statement> body = parseStatement();
|
||||||
nodeFactory.setEndPositionFromNode(body);
|
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)
|
ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& _docString)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
|
@ -85,6 +85,7 @@ private:
|
|||||||
ASTPointer<InlineAssembly> parseInlineAssembly(ASTPointer<ASTString> const& _docString = {});
|
ASTPointer<InlineAssembly> parseInlineAssembly(ASTPointer<ASTString> const& _docString = {});
|
||||||
ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString);
|
||||||
ASTPointer<WhileStatement> parseWhileStatement(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);
|
ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
|
||||||
/// A "simple statement" can be a variable declaration statement or an expression statement.
|
/// A "simple statement" can be a variable declaration statement or an expression statement.
|
||||||
ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString);
|
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);
|
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)
|
BOOST_AUTO_TEST_CASE(nested_loops)
|
||||||
{
|
{
|
||||||
// tests that break and continue statements in nested loops jump to the correct place
|
// tests that break and continue statements in nested loops jump to the correct place
|
||||||
|
Loading…
Reference in New Issue
Block a user