mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Yul] Adds break/continue statements and some general tests for for-loop syntax.
This commit is contained in:
parent
4704ef843d
commit
05e2d362c8
@ -94,6 +94,12 @@ public:
|
|||||||
(*this)(_for.body);
|
(*this)(_for.body);
|
||||||
(*this)(_for.post);
|
(*this)(_for.post);
|
||||||
}
|
}
|
||||||
|
void operator()(yul::Break const&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void operator()(yul::Continue const&)
|
||||||
|
{
|
||||||
|
}
|
||||||
void operator()(yul::Block const& _block)
|
void operator()(yul::Block const& _block)
|
||||||
{
|
{
|
||||||
for (auto const& s: _block.statements)
|
for (auto const& s: _block.statements)
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <libyul/AsmScope.h>
|
#include <libyul/AsmScope.h>
|
||||||
#include <libyul/AsmAnalysisInfo.h>
|
#include <libyul/AsmAnalysisInfo.h>
|
||||||
#include <libyul/Utilities.h>
|
#include <libyul/Utilities.h>
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
@ -33,6 +34,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
@ -472,7 +474,7 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
|
|||||||
{
|
{
|
||||||
solAssert(_for.condition, "");
|
solAssert(_for.condition, "");
|
||||||
|
|
||||||
Scope* originalScope = m_currentScope;
|
Scope* outerScope = m_currentScope;
|
||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
if (!(*this)(_for.pre))
|
if (!(*this)(_for.pre))
|
||||||
@ -485,18 +487,37 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
|
|||||||
if (!expectExpression(*_for.condition))
|
if (!expectExpression(*_for.condition))
|
||||||
success = false;
|
success = false;
|
||||||
m_stackHeight--;
|
m_stackHeight--;
|
||||||
|
|
||||||
|
// backup outer for-loop & create new state
|
||||||
|
auto outerForLoop = m_currentForLoop;
|
||||||
|
m_currentForLoop = &_for;
|
||||||
|
|
||||||
if (!(*this)(_for.body))
|
if (!(*this)(_for.body))
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
if (!(*this)(_for.post))
|
if (!(*this)(_for.post))
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
m_stackHeight -= scope(&_for.pre).numberOfVariables();
|
m_stackHeight -= scope(&_for.pre).numberOfVariables();
|
||||||
m_info.stackHeightInfo[&_for] = m_stackHeight;
|
m_info.stackHeightInfo[&_for] = m_stackHeight;
|
||||||
m_currentScope = originalScope;
|
m_currentScope = outerScope;
|
||||||
|
m_currentForLoop = outerForLoop;
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(Break const& _break)
|
||||||
|
{
|
||||||
|
m_info.stackHeightInfo[&_break] = m_stackHeight;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(Continue const& _continue)
|
||||||
|
{
|
||||||
|
m_info.stackHeightInfo[&_continue] = m_stackHeight;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool AsmAnalyzer::operator()(Block const& _block)
|
bool AsmAnalyzer::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
@ -93,6 +94,8 @@ public:
|
|||||||
bool operator()(If const& _if);
|
bool operator()(If const& _if);
|
||||||
bool operator()(Switch const& _switch);
|
bool operator()(Switch const& _switch);
|
||||||
bool operator()(ForLoop const& _forLoop);
|
bool operator()(ForLoop const& _forLoop);
|
||||||
|
bool operator()(Break const&);
|
||||||
|
bool operator()(Continue const&);
|
||||||
bool operator()(Block const& _block);
|
bool operator()(Block const& _block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -124,6 +127,7 @@ private:
|
|||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
std::shared_ptr<Dialect> m_dialect;
|
std::shared_ptr<Dialect> m_dialect;
|
||||||
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
|
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
|
||||||
|
ForLoop const* m_currentForLoop = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,10 @@ struct Case { langutil::SourceLocation location; std::unique_ptr<Literal> value;
|
|||||||
/// Switch statement
|
/// Switch statement
|
||||||
struct Switch { langutil::SourceLocation location; std::unique_ptr<Expression> expression; std::vector<Case> cases; };
|
struct Switch { langutil::SourceLocation location; std::unique_ptr<Expression> expression; std::vector<Case> cases; };
|
||||||
struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<Expression> condition; Block post; Block body; };
|
struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<Expression> condition; Block post; Block body; };
|
||||||
|
/// Break statement (valid within for loop)
|
||||||
|
struct Break { langutil::SourceLocation location; };
|
||||||
|
/// Continue statement (valid within for loop)
|
||||||
|
struct Continue { langutil::SourceLocation location; };
|
||||||
|
|
||||||
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
|
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
|
||||||
{
|
{
|
||||||
|
@ -41,12 +41,14 @@ struct If;
|
|||||||
struct Switch;
|
struct Switch;
|
||||||
struct Case;
|
struct Case;
|
||||||
struct ForLoop;
|
struct ForLoop;
|
||||||
|
struct Break;
|
||||||
|
struct Continue;
|
||||||
struct ExpressionStatement;
|
struct ExpressionStatement;
|
||||||
struct Block;
|
struct Block;
|
||||||
|
|
||||||
struct TypedName;
|
struct TypedName;
|
||||||
|
|
||||||
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
|
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
|
||||||
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
|
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <libyul/AsmParser.h>
|
#include <libyul/AsmParser.h>
|
||||||
#include <liblangutil/Scanner.h>
|
#include <liblangutil/Scanner.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
@ -104,6 +105,32 @@ Statement Parser::parseStatement()
|
|||||||
}
|
}
|
||||||
case Token::For:
|
case Token::For:
|
||||||
return parseForLoop();
|
return parseForLoop();
|
||||||
|
case Token::Break:
|
||||||
|
if (m_insideForLoopBody)
|
||||||
|
{
|
||||||
|
auto stmt = Statement{ createWithLocation<Break>() };
|
||||||
|
m_scanner->next();
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_errorReporter.syntaxError(location(), "Keyword break outside for-loop body is not allowed.");
|
||||||
|
m_scanner->next();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case Token::Continue:
|
||||||
|
if (m_insideForLoopBody)
|
||||||
|
{
|
||||||
|
auto stmt = Statement{ createWithLocation<Continue>() };
|
||||||
|
m_scanner->next();
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_errorReporter.syntaxError(location(), "Keyword continue outside for-loop body is not allowed.");
|
||||||
|
m_scanner->next();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
case Token::Assign:
|
case Token::Assign:
|
||||||
{
|
{
|
||||||
if (m_dialect->flavour != AsmFlavour::Loose)
|
if (m_dialect->flavour != AsmFlavour::Loose)
|
||||||
@ -243,13 +270,19 @@ Case Parser::parseCase()
|
|||||||
|
|
||||||
ForLoop Parser::parseForLoop()
|
ForLoop Parser::parseForLoop()
|
||||||
{
|
{
|
||||||
|
bool outerForLoopBody = m_insideForLoopBody;
|
||||||
|
m_insideForLoopBody = false;
|
||||||
|
|
||||||
RecursionGuard recursionGuard(*this);
|
RecursionGuard recursionGuard(*this);
|
||||||
ForLoop forLoop = createWithLocation<ForLoop>();
|
ForLoop forLoop = createWithLocation<ForLoop>();
|
||||||
expectToken(Token::For);
|
expectToken(Token::For);
|
||||||
forLoop.pre = parseBlock();
|
forLoop.pre = parseBlock();
|
||||||
forLoop.condition = make_unique<Expression>(parseExpression());
|
forLoop.condition = make_unique<Expression>(parseExpression());
|
||||||
forLoop.post = parseBlock();
|
forLoop.post = parseBlock();
|
||||||
|
|
||||||
|
m_insideForLoopBody = true;
|
||||||
forLoop.body = parseBlock();
|
forLoop.body = parseBlock();
|
||||||
|
m_insideForLoopBody = outerForLoopBody;
|
||||||
forLoop.location.end = forLoop.body.location.end;
|
forLoop.location.end = forLoop.body.location.end;
|
||||||
return forLoop;
|
return forLoop;
|
||||||
}
|
}
|
||||||
@ -455,6 +488,9 @@ VariableDeclaration Parser::parseVariableDeclaration()
|
|||||||
FunctionDefinition Parser::parseFunctionDefinition()
|
FunctionDefinition Parser::parseFunctionDefinition()
|
||||||
{
|
{
|
||||||
RecursionGuard recursionGuard(*this);
|
RecursionGuard recursionGuard(*this);
|
||||||
|
auto outerForLoopBody = m_insideForLoopBody;
|
||||||
|
m_insideForLoopBody = false;
|
||||||
|
ScopeGuard restoreInsideForLoopBody{[&]() { m_insideForLoopBody = outerForLoopBody; }};
|
||||||
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
|
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
|
||||||
expectToken(Token::Function);
|
expectToken(Token::Function);
|
||||||
funDef.name = expectAsmIdentifier();
|
funDef.name = expectAsmIdentifier();
|
||||||
|
@ -39,7 +39,7 @@ class Parser: public langutil::ParserBase
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Parser(langutil::ErrorReporter& _errorReporter, std::shared_ptr<Dialect> _dialect):
|
explicit Parser(langutil::ErrorReporter& _errorReporter, std::shared_ptr<Dialect> _dialect):
|
||||||
ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {}
|
ParserBase(_errorReporter), m_dialect(std::move(_dialect)), m_insideForLoopBody{false} {}
|
||||||
|
|
||||||
/// Parses an inline assembly block starting with `{` and ending with `}`.
|
/// Parses an inline assembly block starting with `{` and ending with `}`.
|
||||||
/// @param _reuseScanner if true, do check for end of input after the `}`.
|
/// @param _reuseScanner if true, do check for end of input after the `}`.
|
||||||
@ -87,6 +87,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Dialect> m_dialect;
|
std::shared_ptr<Dialect> m_dialect;
|
||||||
|
bool m_insideForLoopBody;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -224,6 +224,16 @@ string AsmPrinter::operator()(ForLoop const& _forLoop) const
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string AsmPrinter::operator()(Break const&) const
|
||||||
|
{
|
||||||
|
return "break";
|
||||||
|
}
|
||||||
|
|
||||||
|
string AsmPrinter::operator()(Continue const&) const
|
||||||
|
{
|
||||||
|
return "continue";
|
||||||
|
}
|
||||||
|
|
||||||
string AsmPrinter::operator()(Block const& _block) const
|
string AsmPrinter::operator()(Block const& _block) const
|
||||||
{
|
{
|
||||||
if (_block.statements.empty())
|
if (_block.statements.empty())
|
||||||
|
@ -50,6 +50,8 @@ public:
|
|||||||
std::string operator()(If const& _if) const;
|
std::string operator()(If const& _if) const;
|
||||||
std::string operator()(Switch const& _switch) const;
|
std::string operator()(Switch const& _switch) const;
|
||||||
std::string operator()(ForLoop const& _forLoop) const;
|
std::string operator()(ForLoop const& _forLoop) const;
|
||||||
|
std::string operator()(Break const& _break) const;
|
||||||
|
std::string operator()(Continue const& _continue) const;
|
||||||
std::string operator()(Block const& _block) const;
|
std::string operator()(Block const& _block) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -63,6 +63,8 @@ public:
|
|||||||
bool operator()(If const& _if);
|
bool operator()(If const& _if);
|
||||||
bool operator()(Switch const& _switch);
|
bool operator()(Switch const& _switch);
|
||||||
bool operator()(ForLoop const& _forLoop);
|
bool operator()(ForLoop const& _forLoop);
|
||||||
|
bool operator()(Break const&) { return true; }
|
||||||
|
bool operator()(Continue const&) { return true; }
|
||||||
bool operator()(Block const& _block);
|
bool operator()(Block const& _block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -69,7 +69,6 @@ void VariableReferenceCounter::operator()(ForLoop const& _forLoop)
|
|||||||
m_scope = originalScope;
|
m_scope = originalScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VariableReferenceCounter::operator()(Block const& _block)
|
void VariableReferenceCounter::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
Scope* originalScope = m_scope;
|
Scope* originalScope = m_scope;
|
||||||
@ -92,7 +91,6 @@ void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CodeTransform::CodeTransform(
|
CodeTransform::CodeTransform(
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
AsmAnalysisInfo& _analysisInfo,
|
AsmAnalysisInfo& _analysisInfo,
|
||||||
@ -605,11 +603,9 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
|
|||||||
|
|
||||||
visitStatements(_forLoop.pre.statements);
|
visitStatements(_forLoop.pre.statements);
|
||||||
|
|
||||||
// TODO: When we implement break and continue, the labels and the stack heights at that point
|
|
||||||
// have to be stored in a stack.
|
|
||||||
AbstractAssembly::LabelID loopStart = m_assembly.newLabelId();
|
AbstractAssembly::LabelID loopStart = m_assembly.newLabelId();
|
||||||
AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId();
|
|
||||||
AbstractAssembly::LabelID postPart = m_assembly.newLabelId();
|
AbstractAssembly::LabelID postPart = m_assembly.newLabelId();
|
||||||
|
AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId();
|
||||||
|
|
||||||
m_assembly.setSourceLocation(_forLoop.location);
|
m_assembly.setSourceLocation(_forLoop.location);
|
||||||
m_assembly.appendLabel(loopStart);
|
m_assembly.appendLabel(loopStart);
|
||||||
@ -619,6 +615,8 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
|
|||||||
m_assembly.appendInstruction(solidity::Instruction::ISZERO);
|
m_assembly.appendInstruction(solidity::Instruction::ISZERO);
|
||||||
m_assembly.appendJumpToIf(loopEnd);
|
m_assembly.appendJumpToIf(loopEnd);
|
||||||
|
|
||||||
|
int const stackHeightBody = m_assembly.stackHeight();
|
||||||
|
m_context->forLoopStack.emplace(Context::ForLoopLabels{ {postPart, stackHeightBody}, {loopEnd, stackHeightBody} });
|
||||||
(*this)(_forLoop.body);
|
(*this)(_forLoop.body);
|
||||||
|
|
||||||
m_assembly.setSourceLocation(_forLoop.location);
|
m_assembly.setSourceLocation(_forLoop.location);
|
||||||
@ -631,7 +629,19 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
|
|||||||
m_assembly.appendLabel(loopEnd);
|
m_assembly.appendLabel(loopEnd);
|
||||||
|
|
||||||
finalizeBlock(_forLoop.pre, stackStartHeight);
|
finalizeBlock(_forLoop.pre, stackStartHeight);
|
||||||
|
m_context->forLoopStack.pop();
|
||||||
m_scope = originalScope;
|
m_scope = originalScope;
|
||||||
|
checkStackHeight(&_forLoop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeTransform::operator()(Break const&)
|
||||||
|
{
|
||||||
|
yulAssert(false, "Code generation for break statement in Yul is not implemented yet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeTransform::operator()(Continue const&)
|
||||||
|
{
|
||||||
|
yulAssert(false, "Code generation for continue statement in Yul is not implemented yet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeTransform::operator()(Block const& _block)
|
void CodeTransform::operator()(Block const& _block)
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
class ErrorReporter;
|
class ErrorReporter;
|
||||||
@ -57,6 +59,20 @@ struct CodeTransformContext
|
|||||||
std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
|
std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
|
||||||
std::map<Scope::Variable const*, int> variableStackHeights;
|
std::map<Scope::Variable const*, int> variableStackHeights;
|
||||||
std::map<Scope::Variable const*, unsigned> variableReferences;
|
std::map<Scope::Variable const*, unsigned> variableReferences;
|
||||||
|
|
||||||
|
struct JumpInfo
|
||||||
|
{
|
||||||
|
AbstractAssembly::LabelID label; ///< Jump's LabelID to jump to.
|
||||||
|
int targetStackHeight; ///< Stack height after the jump.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ForLoopLabels
|
||||||
|
{
|
||||||
|
JumpInfo post; ///< Jump info for jumping to post branch.
|
||||||
|
JumpInfo done; ///< Jump info for jumping to done branch.
|
||||||
|
};
|
||||||
|
|
||||||
|
std::stack<ForLoopLabels> forLoopStack;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,6 +182,8 @@ public:
|
|||||||
void operator()(Switch const& _switch);
|
void operator()(Switch const& _switch);
|
||||||
void operator()(FunctionDefinition const&);
|
void operator()(FunctionDefinition const&);
|
||||||
void operator()(ForLoop const&);
|
void operator()(ForLoop const&);
|
||||||
|
void operator()(Break const&);
|
||||||
|
void operator()(Continue const&);
|
||||||
void operator()(Block const& _block);
|
void operator()(Block const& _block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -138,6 +138,15 @@ Statement ASTCopier::operator()(ForLoop const& _forLoop)
|
|||||||
translate(_forLoop.body)
|
translate(_forLoop.body)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Statement ASTCopier::operator()(Break const& _break)
|
||||||
|
{
|
||||||
|
return Break{ _break };
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement ASTCopier::operator()(Continue const& _continue)
|
||||||
|
{
|
||||||
|
return Continue{ _continue };
|
||||||
|
}
|
||||||
|
|
||||||
Statement ASTCopier::operator ()(Block const& _block)
|
Statement ASTCopier::operator ()(Block const& _block)
|
||||||
{
|
{
|
||||||
|
@ -58,6 +58,8 @@ public:
|
|||||||
virtual Statement operator()(Switch const& _switch) = 0;
|
virtual Statement operator()(Switch const& _switch) = 0;
|
||||||
virtual Statement operator()(FunctionDefinition const&) = 0;
|
virtual Statement operator()(FunctionDefinition const&) = 0;
|
||||||
virtual Statement operator()(ForLoop const&) = 0;
|
virtual Statement operator()(ForLoop const&) = 0;
|
||||||
|
virtual Statement operator()(Break const&) = 0;
|
||||||
|
virtual Statement operator()(Continue const&) = 0;
|
||||||
virtual Statement operator()(Block const& _block) = 0;
|
virtual Statement operator()(Block const& _block) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,6 +85,8 @@ public:
|
|||||||
Statement operator()(Switch const& _switch) override;
|
Statement operator()(Switch const& _switch) override;
|
||||||
Statement operator()(FunctionDefinition const&) override;
|
Statement operator()(FunctionDefinition const&) override;
|
||||||
Statement operator()(ForLoop const&) override;
|
Statement operator()(ForLoop const&) override;
|
||||||
|
Statement operator()(Break const&) override;
|
||||||
|
Statement operator()(Continue const&) override;
|
||||||
Statement operator()(Block const& _block) override;
|
Statement operator()(Block const& _block) override;
|
||||||
|
|
||||||
virtual Expression translate(Expression const& _expression);
|
virtual Expression translate(Expression const& _expression);
|
||||||
|
@ -161,6 +161,14 @@ void ASTModifier::operator()(ForLoop& _for)
|
|||||||
(*this)(_for.body);
|
(*this)(_for.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTModifier::operator()(Break&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ASTModifier::operator()(Continue&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void ASTModifier::operator()(Block& _block)
|
void ASTModifier::operator()(Block& _block)
|
||||||
{
|
{
|
||||||
walkVector(_block.statements);
|
walkVector(_block.statements);
|
||||||
|
@ -56,6 +56,8 @@ public:
|
|||||||
virtual void operator()(Switch const& _switch);
|
virtual void operator()(Switch const& _switch);
|
||||||
virtual void operator()(FunctionDefinition const&);
|
virtual void operator()(FunctionDefinition const&);
|
||||||
virtual void operator()(ForLoop const&);
|
virtual void operator()(ForLoop const&);
|
||||||
|
virtual void operator()(Break const&) {}
|
||||||
|
virtual void operator()(Continue const&) {}
|
||||||
virtual void operator()(Block const& _block);
|
virtual void operator()(Block const& _block);
|
||||||
|
|
||||||
virtual void visit(Statement const& _st);
|
virtual void visit(Statement const& _st);
|
||||||
@ -91,6 +93,8 @@ public:
|
|||||||
virtual void operator()(Switch& _switch);
|
virtual void operator()(Switch& _switch);
|
||||||
virtual void operator()(FunctionDefinition&);
|
virtual void operator()(FunctionDefinition&);
|
||||||
virtual void operator()(ForLoop&);
|
virtual void operator()(ForLoop&);
|
||||||
|
virtual void operator()(Break&);
|
||||||
|
virtual void operator()(Continue&);
|
||||||
virtual void operator()(Block& _block);
|
virtual void operator()(Block& _block);
|
||||||
|
|
||||||
virtual void visit(Statement& _st);
|
virtual void visit(Statement& _st);
|
||||||
|
@ -130,6 +130,16 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
|
|||||||
popScope();
|
popScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DataFlowAnalyzer::operator()(Break&)
|
||||||
|
{
|
||||||
|
yulAssert(false, "Not implemented yet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFlowAnalyzer::operator()(Continue&)
|
||||||
|
{
|
||||||
|
yulAssert(false, "Not implemented yet.");
|
||||||
|
}
|
||||||
|
|
||||||
void DataFlowAnalyzer::operator()(Block& _block)
|
void DataFlowAnalyzer::operator()(Block& _block)
|
||||||
{
|
{
|
||||||
size_t numScopes = m_variableScopes.size();
|
size_t numScopes = m_variableScopes.size();
|
||||||
|
@ -53,6 +53,8 @@ public:
|
|||||||
void operator()(Switch& _switch) override;
|
void operator()(Switch& _switch) override;
|
||||||
void operator()(FunctionDefinition&) override;
|
void operator()(FunctionDefinition&) override;
|
||||||
void operator()(ForLoop&) override;
|
void operator()(ForLoop&) override;
|
||||||
|
void operator()(Break& _continue) override;
|
||||||
|
void operator()(Continue& _continue) override;
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -149,6 +149,16 @@ void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
|
|||||||
join(zeroRuns);
|
join(zeroRuns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RedundantAssignEliminator::operator()(Break const&)
|
||||||
|
{
|
||||||
|
yulAssert(false, "Not implemented yet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RedundantAssignEliminator::operator()(Continue const&)
|
||||||
|
{
|
||||||
|
yulAssert(false, "Not implemented yet.");
|
||||||
|
}
|
||||||
|
|
||||||
void RedundantAssignEliminator::operator()(Block const& _block)
|
void RedundantAssignEliminator::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
// This will set all variables that are declared in this
|
// This will set all variables that are declared in this
|
||||||
|
@ -112,6 +112,8 @@ public:
|
|||||||
void operator()(Switch const& _switch) override;
|
void operator()(Switch const& _switch) override;
|
||||||
void operator()(FunctionDefinition const&) override;
|
void operator()(FunctionDefinition const&) override;
|
||||||
void operator()(ForLoop const&) override;
|
void operator()(ForLoop const&) override;
|
||||||
|
void operator()(Break const&) override;
|
||||||
|
void operator()(Continue const&) override;
|
||||||
void operator()(Block const& _block) override;
|
void operator()(Block const& _block) override;
|
||||||
|
|
||||||
static void run(Dialect const& _dialect, Block& _ast);
|
static void run(Dialect const& _dialect, Block& _ast);
|
||||||
|
@ -55,6 +55,8 @@ public:
|
|||||||
bool statementEqual(Switch const& _lhs, Switch const& _rhs);
|
bool statementEqual(Switch const& _lhs, Switch const& _rhs);
|
||||||
bool switchCaseEqual(Case const& _lhs, Case const& _rhs);
|
bool switchCaseEqual(Case const& _lhs, Case const& _rhs);
|
||||||
bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs);
|
bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs);
|
||||||
|
bool statementEqual(Break const&, Break const&) { return true; }
|
||||||
|
bool statementEqual(Continue const&, Continue const&) { return true; }
|
||||||
bool statementEqual(Block const& _lhs, Block const& _rhs);
|
bool statementEqual(Block const& _lhs, Block const& _rhs);
|
||||||
private:
|
private:
|
||||||
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
|
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
|
||||||
|
@ -295,6 +295,74 @@ BOOST_AUTO_TEST_CASE(if_statement)
|
|||||||
BOOST_CHECK(successParse("{ function f() -> x:bool {} if f() { let b:bool := f() } }"));
|
BOOST_CHECK(successParse("{ function f() -> x:bool {} if f() { let b:bool := f() } }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(for_statement)
|
||||||
|
{
|
||||||
|
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||||
|
BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {} }", dialect));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(for_statement_break)
|
||||||
|
{
|
||||||
|
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||||
|
BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {break} }", dialect));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(for_statement_break_init)
|
||||||
|
{
|
||||||
|
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||||
|
CHECK_ERROR_DIALECT(
|
||||||
|
"{ for {let i := 0 break} iszero(eq(i, 10)) {i := add(i, 1)} {} }",
|
||||||
|
SyntaxError,
|
||||||
|
"Keyword break outside for-loop body is not allowed.",
|
||||||
|
dialect);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(for_statement_break_post)
|
||||||
|
{
|
||||||
|
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||||
|
CHECK_ERROR_DIALECT(
|
||||||
|
"{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) break} {} }",
|
||||||
|
SyntaxError,
|
||||||
|
"Keyword break outside for-loop body is not allowed.",
|
||||||
|
dialect);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(for_statement_nested_break)
|
||||||
|
{
|
||||||
|
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||||
|
CHECK_ERROR_DIALECT(
|
||||||
|
"{ for {let i := 0} iszero(eq(i, 10)) {} { function f() { break } } }",
|
||||||
|
SyntaxError,
|
||||||
|
"Keyword break outside for-loop body is not allowed.",
|
||||||
|
dialect);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(for_statement_continue)
|
||||||
|
{
|
||||||
|
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||||
|
BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {continue} }", dialect));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(for_statement_continue_fail_init)
|
||||||
|
{
|
||||||
|
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||||
|
CHECK_ERROR_DIALECT(
|
||||||
|
"{ for {let i := 0 continue} iszero(eq(i, 10)) {i := add(i, 1)} {} }",
|
||||||
|
SyntaxError,
|
||||||
|
"Keyword continue outside for-loop body is not allowed.",
|
||||||
|
dialect);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(for_statement_continue_fail_post)
|
||||||
|
{
|
||||||
|
auto dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople());
|
||||||
|
CHECK_ERROR_DIALECT(
|
||||||
|
"{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) continue} {} }",
|
||||||
|
SyntaxError,
|
||||||
|
"Keyword continue outside for-loop body is not allowed.",
|
||||||
|
dialect);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(if_statement_invalid)
|
BOOST_AUTO_TEST_CASE(if_statement_invalid)
|
||||||
{
|
{
|
||||||
CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected.");
|
CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected.");
|
||||||
|
@ -105,12 +105,27 @@ void Interpreter::operator()(ForLoop const& _forLoop)
|
|||||||
visit(statement);
|
visit(statement);
|
||||||
while (evaluate(*_forLoop.condition) != 0)
|
while (evaluate(*_forLoop.condition) != 0)
|
||||||
{
|
{
|
||||||
|
m_state.loopState = LoopState::Default;
|
||||||
(*this)(_forLoop.body);
|
(*this)(_forLoop.body);
|
||||||
|
if (m_state.loopState == LoopState::Break)
|
||||||
|
break;
|
||||||
|
|
||||||
(*this)(_forLoop.post);
|
(*this)(_forLoop.post);
|
||||||
}
|
}
|
||||||
|
m_state.loopState = LoopState::Default;
|
||||||
closeScope();
|
closeScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Interpreter::operator()(Break const&)
|
||||||
|
{
|
||||||
|
m_state.loopState = LoopState::Break;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interpreter::operator()(Continue const&)
|
||||||
|
{
|
||||||
|
m_state.loopState = LoopState::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
void Interpreter::operator()(Block const& _block)
|
void Interpreter::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
openScope();
|
openScope();
|
||||||
@ -122,7 +137,14 @@ void Interpreter::operator()(Block const& _block)
|
|||||||
m_functions[funDef.name] = &funDef;
|
m_functions[funDef.name] = &funDef;
|
||||||
m_scopes.back().insert(funDef.name);
|
m_scopes.back().insert(funDef.name);
|
||||||
}
|
}
|
||||||
ASTWalker::operator()(_block);
|
|
||||||
|
for (auto const& statement: _block.statements)
|
||||||
|
{
|
||||||
|
visit(statement);
|
||||||
|
if (m_state.loopState != LoopState::Default)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
closeScope();
|
closeScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,13 @@ class InterpreterTerminated: dev::Exception
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class LoopState
|
||||||
|
{
|
||||||
|
Default,
|
||||||
|
Continue,
|
||||||
|
Break,
|
||||||
|
};
|
||||||
|
|
||||||
struct InterpreterState
|
struct InterpreterState
|
||||||
{
|
{
|
||||||
dev::bytes calldata;
|
dev::bytes calldata;
|
||||||
@ -65,6 +72,7 @@ struct InterpreterState
|
|||||||
std::vector<std::string> trace;
|
std::vector<std::string> trace;
|
||||||
/// This is actually an input parameter that more or less limits the runtime.
|
/// This is actually an input parameter that more or less limits the runtime.
|
||||||
size_t maxTraceSize = 0;
|
size_t maxTraceSize = 0;
|
||||||
|
LoopState loopState = LoopState::Default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,6 +98,8 @@ public:
|
|||||||
void operator()(Switch const& _switch) override;
|
void operator()(Switch const& _switch) override;
|
||||||
void operator()(FunctionDefinition const&) override;
|
void operator()(FunctionDefinition const&) override;
|
||||||
void operator()(ForLoop const&) override;
|
void operator()(ForLoop const&) override;
|
||||||
|
void operator()(Break const&) override;
|
||||||
|
void operator()(Continue const&) override;
|
||||||
void operator()(Block const& _block) override;
|
void operator()(Block const& _block) override;
|
||||||
|
|
||||||
std::vector<std::string> const& trace() const { return m_state.trace; }
|
std::vector<std::string> const& trace() const { return m_state.trace; }
|
||||||
|
Loading…
Reference in New Issue
Block a user