Merge pull request #3297 from ethereum/separate_expression_and_statement

Separate expression and statement
This commit is contained in:
Alex Beregszaszi 2018-01-04 12:04:19 +00:00 committed by GitHub
commit 2cdd789b5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 201 additions and 85 deletions

View File

@ -42,11 +42,13 @@ using If = solidity::assembly::If;
using Case = solidity::assembly::Case; using Case = solidity::assembly::Case;
using Switch = solidity::assembly::Switch; using Switch = solidity::assembly::Switch;
using ForLoop = solidity::assembly::ForLoop; using ForLoop = solidity::assembly::ForLoop;
using ExpressionStatement = solidity::assembly::ExpressionStatement;
using Block = solidity::assembly::Block; using Block = solidity::assembly::Block;
using TypedName = solidity::assembly::TypedName; using TypedName = solidity::assembly::TypedName;
using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
} }
} }

View File

@ -77,6 +77,13 @@ void CodeTransform::operator()(StackAssignment const& _assignment)
checkStackHeight(&_assignment); checkStackHeight(&_assignment);
} }
void CodeTransform::operator()(ExpressionStatement const& _statement)
{
m_assembly.setSourceLocation(_statement.location);
boost::apply_visitor(*this, _statement.expression);
checkStackHeight(&_statement);
}
void CodeTransform::operator()(Label const& _label) void CodeTransform::operator()(Label const& _label)
{ {
m_assembly.setSourceLocation(_label.location); m_assembly.setSourceLocation(_label.location);
@ -460,7 +467,7 @@ AbstractAssembly::LabelID CodeTransform::functionEntryID(string const& _name, Sc
return m_context->functionEntryIDs[&_function]; return m_context->functionEntryIDs[&_function];
} }
void CodeTransform::visitExpression(Statement const& _expression) void CodeTransform::visitExpression(Expression const& _expression)
{ {
int height = m_assembly.stackHeight(); int height = m_assembly.stackHeight();
boost::apply_visitor(*this, _expression); boost::apply_visitor(*this, _expression);

View File

@ -101,6 +101,7 @@ public:
void operator()(Identifier const& _identifier); void operator()(Identifier const& _identifier);
void operator()(FunctionalInstruction const& _instr); void operator()(FunctionalInstruction const& _instr);
void operator()(FunctionCall const&); void operator()(FunctionCall const&);
void operator()(ExpressionStatement const& _statement);
void operator()(Label const& _label); void operator()(Label const& _label);
void operator()(StackAssignment const& _assignment); void operator()(StackAssignment const& _assignment);
void operator()(Assignment const& _assignment); void operator()(Assignment const& _assignment);
@ -118,7 +119,7 @@ private:
AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label);
AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function); AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function);
/// Generates code for an expression that is supposed to return a single value. /// Generates code for an expression that is supposed to return a single value.
void visitExpression(Statement const& _expression); void visitExpression(Expression const& _expression);
void visitStatements(std::vector<Statement> const& _statements); void visitStatements(std::vector<Statement> const& _statements);

View File

@ -37,6 +37,11 @@ Statement ASTCopier::operator()(Instruction const&)
return {}; return {};
} }
Statement ASTCopier::operator()(ExpressionStatement const& _statement)
{
return ExpressionStatement{ _statement.location, translate(_statement.expression) };
}
Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) Statement ASTCopier::operator()(VariableDeclaration const& _varDecl)
{ {
return VariableDeclaration{ return VariableDeclaration{
@ -67,7 +72,7 @@ Statement ASTCopier::operator()(Label const&)
return {}; return {};
} }
Statement ASTCopier::operator()(FunctionCall const& _call) Expression ASTCopier::operator()(FunctionCall const& _call)
{ {
return FunctionCall{ return FunctionCall{
_call.location, _call.location,
@ -76,7 +81,7 @@ Statement ASTCopier::operator()(FunctionCall const& _call)
}; };
} }
Statement ASTCopier::operator()(FunctionalInstruction const& _instruction) Expression ASTCopier::operator()(FunctionalInstruction const& _instruction)
{ {
return FunctionalInstruction{ return FunctionalInstruction{
_instruction.location, _instruction.location,
@ -85,12 +90,12 @@ Statement ASTCopier::operator()(FunctionalInstruction const& _instruction)
}; };
} }
Statement ASTCopier::operator()(Identifier const& _identifier) Expression ASTCopier::operator()(Identifier const& _identifier)
{ {
return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; return Identifier{_identifier.location, translateIdentifier(_identifier.name)};
} }
Statement ASTCopier::operator()(Literal const& _literal) Expression ASTCopier::operator()(Literal const& _literal)
{ {
return translate(_literal); return translate(_literal);
} }
@ -140,9 +145,14 @@ Statement ASTCopier::operator ()(Block const& _block)
return translate(_block); return translate(_block);
} }
Expression ASTCopier::translate(Expression const& _expression)
{
return _expression.apply_visitor(static_cast<ExpressionCopier&>(*this));
}
Statement ASTCopier::translate(Statement const& _statement) Statement ASTCopier::translate(Statement const& _statement)
{ {
return boost::apply_visitor(*this, _statement); return _statement.apply_visitor(static_cast<StatementCopier&>(*this));
} }
Block ASTCopier::translate(Block const& _block) Block ASTCopier::translate(Block const& _block)

View File

@ -34,28 +34,55 @@ namespace dev
namespace julia namespace julia
{ {
class ExpressionCopier: public boost::static_visitor<Expression>
{
public:
virtual Expression operator()(Literal const& _literal) = 0;
virtual Expression operator()(Identifier const& _identifier) = 0;
virtual Expression operator()(FunctionalInstruction const& _instr) = 0;
virtual Expression operator()(FunctionCall const&) = 0;
};
class StatementCopier: public boost::static_visitor<Statement>
{
public:
virtual Statement operator()(ExpressionStatement const& _statement) = 0;
virtual Statement operator()(Instruction const& _instruction) = 0;
virtual Statement operator()(Label const& _label) = 0;
virtual Statement operator()(StackAssignment const& _assignment) = 0;
virtual Statement operator()(Assignment const& _assignment) = 0;
virtual Statement operator()(VariableDeclaration const& _varDecl) = 0;
virtual Statement operator()(If const& _if) = 0;
virtual Statement operator()(Switch const& _switch) = 0;
virtual Statement operator()(FunctionDefinition const&) = 0;
virtual Statement operator()(ForLoop const&) = 0;
virtual Statement operator()(Block const& _block) = 0;
};
/** /**
* Creates a copy of a iulia AST potentially replacing identifier names. * Creates a copy of a iulia AST potentially replacing identifier names.
* Base class to be extended. * Base class to be extended.
*/ */
class ASTCopier: public boost::static_visitor<Statement> class ASTCopier: public ExpressionCopier, public StatementCopier
{ {
public: public:
Statement operator()(Literal const& _literal); virtual Expression operator()(Literal const& _literal) override;
Statement operator()(Instruction const& _instruction); virtual Statement operator()(Instruction const& _instruction) override;
Statement operator()(Identifier const& _identifier); virtual Expression operator()(Identifier const& _identifier) override;
Statement operator()(FunctionalInstruction const& _instr); virtual Expression operator()(FunctionalInstruction const& _instr) override;
Statement operator()(FunctionCall const&); virtual Expression operator()(FunctionCall const&) override;
Statement operator()(Label const& _label); virtual Statement operator()(ExpressionStatement const& _statement) override;
Statement operator()(StackAssignment const& _assignment); virtual Statement operator()(Label const& _label) override;
Statement operator()(Assignment const& _assignment); virtual Statement operator()(StackAssignment const& _assignment) override;
Statement operator()(VariableDeclaration const& _varDecl); virtual Statement operator()(Assignment const& _assignment) override;
Statement operator()(If const& _if); virtual Statement operator()(VariableDeclaration const& _varDecl) override;
Statement operator()(Switch const& _switch); virtual Statement operator()(If const& _if) override;
Statement operator()(FunctionDefinition const&); virtual Statement operator()(Switch const& _switch) override;
Statement operator()(ForLoop const&); virtual Statement operator()(FunctionDefinition const&) override;
Statement operator()(Block const& _block); virtual Statement operator()(ForLoop const&) override;
virtual Statement operator()(Block const& _block) override;
virtual Expression translate(Expression const& _expression);
virtual Statement translate(Statement const& _statement); virtual Statement translate(Statement const& _statement);
protected: protected:

View File

@ -42,6 +42,11 @@ void ASTWalker::operator()(FunctionCall const& _funCall)
walkVector(_funCall.arguments | boost::adaptors::reversed); walkVector(_funCall.arguments | boost::adaptors::reversed);
} }
void ASTWalker::operator()(ExpressionStatement const& _statement)
{
boost::apply_visitor(*this, _statement.expression);
}
void ASTWalker::operator()(Assignment const& _assignment) void ASTWalker::operator()(Assignment const& _assignment)
{ {
for (auto const& name: _assignment.variableNames) for (auto const& name: _assignment.variableNames)
@ -100,6 +105,11 @@ void ASTModifier::operator()(FunctionCall& _funCall)
walkVector(_funCall.arguments | boost::adaptors::reversed); walkVector(_funCall.arguments | boost::adaptors::reversed);
} }
void ASTModifier::operator()(ExpressionStatement& _statement)
{
boost::apply_visitor(*this, _statement.expression);
}
void ASTModifier::operator()(Assignment& _assignment) void ASTModifier::operator()(Assignment& _assignment)
{ {
for (auto& name: _assignment.variableNames) for (auto& name: _assignment.variableNames)

View File

@ -47,6 +47,7 @@ public:
virtual void operator()(Identifier const&) {} virtual void operator()(Identifier const&) {}
virtual void operator()(FunctionalInstruction const& _instr); virtual void operator()(FunctionalInstruction const& _instr);
virtual void operator()(FunctionCall const& _funCall); virtual void operator()(FunctionCall const& _funCall);
virtual void operator()(ExpressionStatement const& _statement);
virtual void operator()(Label const&) { solAssert(false, ""); } virtual void operator()(Label const&) { solAssert(false, ""); }
virtual void operator()(StackAssignment const&) { solAssert(false, ""); } virtual void operator()(StackAssignment const&) { solAssert(false, ""); }
virtual void operator()(Assignment const& _assignment); virtual void operator()(Assignment const& _assignment);
@ -77,6 +78,7 @@ public:
virtual void operator()(Identifier&) {} virtual void operator()(Identifier&) {}
virtual void operator()(FunctionalInstruction& _instr); virtual void operator()(FunctionalInstruction& _instr);
virtual void operator()(FunctionCall& _funCall); virtual void operator()(FunctionCall& _funCall);
virtual void operator()(ExpressionStatement& _statement);
virtual void operator()(Label&) { solAssert(false, ""); } virtual void operator()(Label&) { solAssert(false, ""); }
virtual void operator()(StackAssignment&) { solAssert(false, ""); } virtual void operator()(StackAssignment&) { solAssert(false, ""); }
virtual void operator()(Assignment& _assignment); virtual void operator()(Assignment& _assignment);
@ -98,6 +100,10 @@ protected:
{ {
boost::apply_visitor(*this, _st); boost::apply_visitor(*this, _st);
} }
virtual void visit(Expression& _e)
{
boost::apply_visitor(*this, _e);
}
}; };
} }

View File

@ -26,14 +26,14 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::julia; using namespace dev::julia;
Statement Substitution::translate(Statement const& _statement) Expression Substitution::translate(Expression const& _expression)
{ {
if (_statement.type() == typeid(Identifier)) if (_expression.type() == typeid(Identifier))
{ {
string const& name = boost::get<Identifier>(_statement).name; string const& name = boost::get<Identifier>(_expression).name;
if (m_substitutions.count(name)) if (m_substitutions.count(name))
// No recursive substitution // No recursive substitution
return ASTCopier().translate(*m_substitutions.at(name)); return ASTCopier().translate(*m_substitutions.at(name));
} }
return ASTCopier::translate(_statement); return ASTCopier::translate(_expression);
} }

View File

@ -33,18 +33,17 @@ namespace julia
/** /**
* Specific AST copier that replaces certain identifiers with expressions. * Specific AST copier that replaces certain identifiers with expressions.
* Only works on ASTs that are expressions.
*/ */
class Substitution: public ASTCopier class Substitution: public ASTCopier
{ {
public: public:
Substitution(std::map<std::string, Statement const*> const& _substitutions): Substitution(std::map<std::string, Expression const*> const& _substitutions):
m_substitutions(_substitutions) m_substitutions(_substitutions)
{} {}
virtual Statement translate(Statement const& _statement) override; virtual Expression translate(Expression const& _expression) override;
private: private:
std::map<std::string, Statement const*> const& m_substitutions; std::map<std::string, Expression const*> const& m_substitutions;
}; };
} }

View File

@ -50,6 +50,10 @@ public:
for (auto const& arg: _instr.arguments) for (auto const& arg: _instr.arguments)
boost::apply_visitor(*this, arg); boost::apply_visitor(*this, arg);
} }
void operator()(assembly::ExpressionStatement const& _expr)
{
boost::apply_visitor(*this, _expr.expression);
}
void operator()(assembly::StackAssignment const&) {} void operator()(assembly::StackAssignment const&) {}
void operator()(assembly::Assignment const& _assignment) void operator()(assembly::Assignment const& _assignment)
{ {

View File

@ -155,6 +155,16 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
return success; return success;
} }
bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement)
{
// size_t initialStackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, _statement.expression);
// if (!expectDeposit(0, initialStackHeight, _statement.location))
// success = false;
m_info.stackHeightInfo[&_statement] = m_stackHeight;
return success;
}
bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment)
{ {
solAssert(!m_julia, ""); solAssert(!m_julia, "");
@ -407,13 +417,13 @@ bool AsmAnalyzer::operator()(Block const& _block)
return success; return success;
} }
bool AsmAnalyzer::expectExpression(Statement const& _statement) bool AsmAnalyzer::expectExpression(Expression const& _expr)
{ {
bool success = true; bool success = true;
int const initialHeight = m_stackHeight; int const initialHeight = m_stackHeight;
if (!boost::apply_visitor(*this, _statement)) if (!boost::apply_visitor(*this, _expr))
success = false; success = false;
if (!expectDeposit(1, initialHeight, locationOf(_statement))) if (!expectDeposit(1, initialHeight, locationOf(_expr)))
success = false; success = false;
return success; return success;
} }

View File

@ -65,6 +65,7 @@ public:
bool operator()(assembly::Identifier const&); bool operator()(assembly::Identifier const&);
bool operator()(assembly::FunctionalInstruction const& _functionalInstruction); bool operator()(assembly::FunctionalInstruction const& _functionalInstruction);
bool operator()(assembly::Label const& _label); bool operator()(assembly::Label const& _label);
bool operator()(assembly::ExpressionStatement const&);
bool operator()(assembly::StackAssignment const&); bool operator()(assembly::StackAssignment const&);
bool operator()(assembly::Assignment const& _assignment); bool operator()(assembly::Assignment const& _assignment);
bool operator()(assembly::VariableDeclaration const& _variableDeclaration); bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
@ -77,7 +78,7 @@ public:
private: private:
/// Visits the statement and expects it to deposit one item onto the stack. /// Visits the statement and expects it to deposit one item onto the stack.
bool expectExpression(Statement const& _statement); bool expectExpression(Expression const& _expr);
bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location); bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location);
/// Verifies that a variable to be assigned to exists and has the same size /// Verifies that a variable to be assigned to exists and has the same size

View File

@ -58,23 +58,25 @@ struct StackAssignment { SourceLocation location; Identifier variableName; };
/// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy
/// a single stack slot and expects a single expression on the right hand returning /// a single stack slot and expects a single expression on the right hand returning
/// the same amount of items as the number of variables. /// the same amount of items as the number of variables.
struct Assignment { SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Statement> value; }; struct Assignment { SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; };
/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))"
struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector<Statement> arguments; }; struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector<Expression> arguments; };
struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; }; struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Expression> arguments; };
/// Statement that contains only a single expression
struct ExpressionStatement { SourceLocation location; Expression expression; };
/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted
struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Statement> value; }; struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> value; };
/// Block that creates a scope (frees declared stack variables) /// Block that creates a scope (frees declared stack variables)
struct Block { SourceLocation location; std::vector<Statement> statements; }; struct Block { SourceLocation location; std::vector<Statement> statements; };
/// Function definition ("function f(a, b) -> (d, e) { ... }") /// Function definition ("function f(a, b) -> (d, e) { ... }")
struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; };
/// Conditional execution without "else" part. /// Conditional execution without "else" part.
struct If { SourceLocation location; std::shared_ptr<Statement> condition; Block body; }; struct If { SourceLocation location; std::shared_ptr<Expression> condition; Block body; };
/// Switch case or default case /// Switch case or default case
struct Case { SourceLocation location; std::shared_ptr<Literal> value; Block body; }; struct Case { SourceLocation location; std::shared_ptr<Literal> value; Block body; };
/// Switch statement /// Switch statement
struct Switch { SourceLocation location; std::shared_ptr<Statement> expression; std::vector<Case> cases; }; struct Switch { SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; };
struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Statement> condition; Block post; Block body; }; struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; };
struct LocationExtractor: boost::static_visitor<SourceLocation> struct LocationExtractor: boost::static_visitor<SourceLocation>
{ {

View File

@ -45,11 +45,13 @@ struct If;
struct Switch; struct Switch;
struct Case; struct Case;
struct ForLoop; struct ForLoop;
struct ExpressionStatement;
struct Block; struct Block;
struct TypedName; struct TypedName;
using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
} }
} }

View File

@ -77,9 +77,7 @@ assembly::Statement Parser::parseStatement()
{ {
assembly::If _if = createWithLocation<assembly::If>(); assembly::If _if = createWithLocation<assembly::If>();
m_scanner->next(); m_scanner->next();
_if.condition = make_shared<Statement>(parseExpression()); _if.condition = make_shared<Expression>(parseExpression());
if (_if.condition->type() == typeid(assembly::Instruction))
fatalParserError("Instructions are not supported as conditions for if - try to append \"()\".");
_if.body = parseBlock(); _if.body = parseBlock();
return _if; return _if;
} }
@ -87,9 +85,7 @@ assembly::Statement Parser::parseStatement()
{ {
assembly::Switch _switch = createWithLocation<assembly::Switch>(); assembly::Switch _switch = createWithLocation<assembly::Switch>();
m_scanner->next(); m_scanner->next();
_switch.expression = make_shared<Statement>(parseExpression()); _switch.expression = make_shared<Expression>(parseExpression());
if (_switch.expression->type() == typeid(assembly::Instruction))
fatalParserError("Instructions are not supported as expressions for switch - try to append \"()\".");
while (m_scanner->currentToken() == Token::Case) while (m_scanner->currentToken() == Token::Case)
_switch.cases.emplace_back(parseCase()); _switch.cases.emplace_back(parseCase());
if (m_scanner->currentToken() == Token::Default) if (m_scanner->currentToken() == Token::Default)
@ -127,18 +123,21 @@ assembly::Statement Parser::parseStatement()
// Simple instruction (might turn into functional), // Simple instruction (might turn into functional),
// literal, // literal,
// identifier (might turn into label or functional assignment) // identifier (might turn into label or functional assignment)
Statement statement(parseElementaryOperation(false)); ElementaryOperation elementary(parseElementaryOperation(false));
switch (currentToken()) switch (currentToken())
{ {
case Token::LParen: case Token::LParen:
return parseCall(std::move(statement)); {
Expression expr = parseCall(std::move(elementary));
return ExpressionStatement{locationOf(expr), expr};
}
case Token::Comma: case Token::Comma:
{ {
// if a comma follows, a multiple assignment is assumed // if a comma follows, a multiple assignment is assumed
if (statement.type() != typeid(assembly::Identifier)) if (elementary.type() != typeid(assembly::Identifier))
fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); fatalParserError("Label name / variable name must precede \",\" (multiple assignment).");
assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement); assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary);
Assignment assignment = createWithLocation<Assignment>(identifier.location); Assignment assignment = createWithLocation<Assignment>(identifier.location);
assignment.variableNames.emplace_back(identifier); assignment.variableNames.emplace_back(identifier);
@ -146,25 +145,25 @@ assembly::Statement Parser::parseStatement()
do do
{ {
expectToken(Token::Comma); expectToken(Token::Comma);
statement = parseElementaryOperation(false); elementary = parseElementaryOperation(false);
if (statement.type() != typeid(assembly::Identifier)) if (elementary.type() != typeid(assembly::Identifier))
fatalParserError("Variable name expected in multiple assignemnt."); fatalParserError("Variable name expected in multiple assignemnt.");
assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(statement)); assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(elementary));
} }
while (currentToken() == Token::Comma); while (currentToken() == Token::Comma);
expectToken(Token::Colon); expectToken(Token::Colon);
expectToken(Token::Assign); expectToken(Token::Assign);
assignment.value.reset(new Statement(parseExpression())); assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end; assignment.location.end = locationOf(*assignment.value).end;
return assignment; return assignment;
} }
case Token::Colon: case Token::Colon:
{ {
if (statement.type() != typeid(assembly::Identifier)) if (elementary.type() != typeid(assembly::Identifier))
fatalParserError("Label name / variable name must precede \":\"."); fatalParserError("Label name / variable name must precede \":\".");
assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement); assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary);
advance(); advance();
// identifier:=: should be parsed as identifier: =: (i.e. a label), // identifier:=: should be parsed as identifier: =: (i.e. a label),
// while identifier:= (being followed by a non-colon) as identifier := (assignment). // while identifier:= (being followed by a non-colon) as identifier := (assignment).
@ -175,7 +174,7 @@ assembly::Statement Parser::parseStatement()
fatalParserError("Cannot use instruction names for identifier names."); fatalParserError("Cannot use instruction names for identifier names.");
advance(); advance();
assignment.variableNames.emplace_back(identifier); assignment.variableNames.emplace_back(identifier);
assignment.value.reset(new Statement(parseExpression())); assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end; assignment.location.end = locationOf(*assignment.value).end;
return assignment; return assignment;
} }
@ -194,7 +193,21 @@ assembly::Statement Parser::parseStatement()
fatalParserError("Call or assignment expected."); fatalParserError("Call or assignment expected.");
break; break;
} }
return statement; if (elementary.type() == typeid(assembly::Identifier))
{
Expression expr = boost::get<assembly::Identifier>(elementary);
return ExpressionStatement{locationOf(expr), expr};
}
else if (elementary.type() == typeid(assembly::Literal))
{
Expression expr = boost::get<assembly::Literal>(elementary);
return ExpressionStatement{locationOf(expr), expr};
}
else
{
solAssert(elementary.type() == typeid(assembly::Instruction), "Invalid elementary operation.");
return boost::get<assembly::Instruction>(elementary);
}
} }
assembly::Case Parser::parseCase() assembly::Case Parser::parseCase()
@ -206,10 +219,10 @@ assembly::Case Parser::parseCase()
else if (m_scanner->currentToken() == Token::Case) else if (m_scanner->currentToken() == Token::Case)
{ {
m_scanner->next(); m_scanner->next();
assembly::Statement statement = parseElementaryOperation(); ElementaryOperation literal = parseElementaryOperation();
if (statement.type() != typeid(assembly::Literal)) if (literal.type() != typeid(assembly::Literal))
fatalParserError("Literal expected."); fatalParserError("Literal expected.");
_case.value = make_shared<Literal>(std::move(boost::get<assembly::Literal>(statement))); _case.value = make_shared<Literal>(boost::get<assembly::Literal>(std::move(literal)));
} }
else else
fatalParserError("Case or default case expected."); fatalParserError("Case or default case expected.");
@ -224,19 +237,17 @@ assembly::ForLoop Parser::parseForLoop()
ForLoop forLoop = createWithLocation<ForLoop>(); ForLoop forLoop = createWithLocation<ForLoop>();
expectToken(Token::For); expectToken(Token::For);
forLoop.pre = parseBlock(); forLoop.pre = parseBlock();
forLoop.condition = make_shared<Statement>(parseExpression()); forLoop.condition = make_shared<Expression>(parseExpression());
if (forLoop.condition->type() == typeid(assembly::Instruction))
fatalParserError("Instructions are not supported as conditions for the for statement.");
forLoop.post = parseBlock(); forLoop.post = parseBlock();
forLoop.body = parseBlock(); forLoop.body = parseBlock();
forLoop.location.end = forLoop.body.location.end; forLoop.location.end = forLoop.body.location.end;
return forLoop; return forLoop;
} }
assembly::Statement Parser::parseExpression() assembly::Expression Parser::parseExpression()
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
Statement operation = parseElementaryOperation(true); ElementaryOperation operation = parseElementaryOperation(true);
if (operation.type() == typeid(Instruction)) if (operation.type() == typeid(Instruction))
{ {
Instruction const& instr = boost::get<Instruction>(operation); Instruction const& instr = boost::get<Instruction>(operation);
@ -252,8 +263,18 @@ assembly::Statement Parser::parseExpression()
} }
if (currentToken() == Token::LParen) if (currentToken() == Token::LParen)
return parseCall(std::move(operation)); return parseCall(std::move(operation));
else if (operation.type() == typeid(Instruction))
{
Instruction& instr = boost::get<Instruction>(operation);
return FunctionalInstruction{std::move(instr.location), instr.instruction, {}};
}
else if (operation.type() == typeid(assembly::Identifier))
return boost::get<assembly::Identifier>(operation);
else else
return operation; {
solAssert(operation.type() == typeid(assembly::Literal), "");
return boost::get<assembly::Literal>(operation);
}
} }
std::map<string, dev::solidity::Instruction> const& Parser::instructions() std::map<string, dev::solidity::Instruction> const& Parser::instructions()
@ -296,10 +317,10 @@ std::map<dev::solidity::Instruction, string> const& Parser::instructionNames()
return s_instructionNames; return s_instructionNames;
} }
assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) Parser::ElementaryOperation Parser::parseElementaryOperation(bool _onlySinglePusher)
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
Statement ret; ElementaryOperation ret;
switch (currentToken()) switch (currentToken())
{ {
case Token::Identifier: case Token::Identifier:
@ -402,7 +423,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
{ {
expectToken(Token::Colon); expectToken(Token::Colon);
expectToken(Token::Assign); expectToken(Token::Assign);
varDecl.value.reset(new Statement(parseExpression())); varDecl.value.reset(new Expression(parseExpression()));
varDecl.location.end = locationOf(*varDecl.value).end; varDecl.location.end = locationOf(*varDecl.value).end;
} }
else else
@ -442,13 +463,13 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
return funDef; return funDef;
} }
assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
if (_instruction.type() == typeid(Instruction)) if (_initialOp.type() == typeid(Instruction))
{ {
solAssert(!m_julia, "Instructions are invalid in JULIA"); solAssert(!m_julia, "Instructions are invalid in JULIA");
Instruction const& instruction = std::move(boost::get<Instruction>(_instruction)); Instruction& instruction = boost::get<Instruction>(_initialOp);
FunctionalInstruction ret; FunctionalInstruction ret;
ret.instruction = instruction.instruction; ret.instruction = instruction.instruction;
ret.location = std::move(instruction.location); ret.location = std::move(instruction.location);
@ -499,10 +520,10 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
expectToken(Token::RParen); expectToken(Token::RParen);
return ret; return ret;
} }
else if (_instruction.type() == typeid(Identifier)) else if (_initialOp.type() == typeid(Identifier))
{ {
FunctionCall ret; FunctionCall ret;
ret.functionName = std::move(boost::get<Identifier>(_instruction)); ret.functionName = std::move(boost::get<Identifier>(_initialOp));
ret.location = ret.functionName.location; ret.location = ret.functionName.location;
expectToken(Token::LParen); expectToken(Token::LParen);
while (currentToken() != Token::RParen) while (currentToken() != Token::RParen)

View File

@ -44,6 +44,8 @@ public:
std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner); std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner);
protected: protected:
using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>;
/// Creates an inline assembly node with the given source location. /// Creates an inline assembly node with the given source location.
template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) const template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) const
{ {
@ -65,13 +67,13 @@ protected:
Case parseCase(); Case parseCase();
ForLoop parseForLoop(); ForLoop parseForLoop();
/// Parses a functional expression that has to push exactly one stack element /// Parses a functional expression that has to push exactly one stack element
Statement parseExpression(); assembly::Expression parseExpression();
static std::map<std::string, dev::solidity::Instruction> const& instructions(); static std::map<std::string, dev::solidity::Instruction> const& instructions();
static std::map<dev::solidity::Instruction, std::string> const& instructionNames(); static std::map<dev::solidity::Instruction, std::string> const& instructionNames();
Statement parseElementaryOperation(bool _onlySinglePusher = false); ElementaryOperation parseElementaryOperation(bool _onlySinglePusher = false);
VariableDeclaration parseVariableDeclaration(); VariableDeclaration parseVariableDeclaration();
FunctionDefinition parseFunctionDefinition(); FunctionDefinition parseFunctionDefinition();
Statement parseCall(Statement&& _instruction); assembly::Expression parseCall(ElementaryOperation&& _initialOp);
TypedName parseTypedName(); TypedName parseTypedName();
std::string expectAsmIdentifier(); std::string expectAsmIdentifier();

View File

@ -102,6 +102,11 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional
")"; ")";
} }
string AsmPrinter::operator()(ExpressionStatement const& _statement)
{
return boost::apply_visitor(*this, _statement.expression);
}
string AsmPrinter::operator()(assembly::Label const& _label) string AsmPrinter::operator()(assembly::Label const& _label)
{ {
solAssert(!m_julia, ""); solAssert(!m_julia, "");

View File

@ -42,6 +42,7 @@ public:
std::string operator()(assembly::Literal const& _literal); std::string operator()(assembly::Literal const& _literal);
std::string operator()(assembly::Identifier const& _identifier); std::string operator()(assembly::Identifier const& _identifier);
std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction); std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction);
std::string operator()(assembly::ExpressionStatement const& _expr);
std::string operator()(assembly::Label const& _label); std::string operator()(assembly::Label const& _label);
std::string operator()(assembly::StackAssignment const& _assignment); std::string operator()(assembly::StackAssignment const& _assignment);
std::string operator()(assembly::Assignment const& _assignment); std::string operator()(assembly::Assignment const& _assignment);

View File

@ -45,6 +45,11 @@ ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter):
m_currentScope = &scope(nullptr); m_currentScope = &scope(nullptr);
} }
bool ScopeFiller::operator()(ExpressionStatement const& _expr)
{
return boost::apply_visitor(*this, _expr.expression);
}
bool ScopeFiller::operator()(Label const& _item) bool ScopeFiller::operator()(Label const& _item)
{ {
if (!m_currentScope->registerLabel(_item.name)) if (!m_currentScope->registerLabel(_item.name))

View File

@ -53,6 +53,7 @@ public:
bool operator()(assembly::Literal const&) { return true; } bool operator()(assembly::Literal const&) { return true; }
bool operator()(assembly::Identifier const&) { return true; } bool operator()(assembly::Identifier const&) { return true; }
bool operator()(assembly::FunctionalInstruction const&) { return true; } bool operator()(assembly::FunctionalInstruction const&) { return true; }
bool operator()(assembly::ExpressionStatement const& _expr);
bool operator()(assembly::Label const& _label); bool operator()(assembly::Label const& _label);
bool operator()(assembly::StackAssignment const&) { return true; } bool operator()(assembly::StackAssignment const&) { return true; }
bool operator()(assembly::Assignment const&) { return true; } bool operator()(assembly::Assignment const&) { return true; }

View File

@ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(if_statement_scope)
BOOST_AUTO_TEST_CASE(if_statement_invalid) BOOST_AUTO_TEST_CASE(if_statement_invalid)
{ {
CHECK_PARSE_ERROR("{ if calldatasize {}", ParserError, "Instructions are not supported as conditions for if"); CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected token \"(\"");
BOOST_CHECK("{ if calldatasize() {}"); BOOST_CHECK("{ if calldatasize() {}");
CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected token LBrace"); CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected token LBrace");
@ -296,7 +296,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case)
BOOST_AUTO_TEST_CASE(switch_invalid_expression) BOOST_AUTO_TEST_CASE(switch_invalid_expression)
{ {
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected."); CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected.");
CHECK_PARSE_ERROR("{ switch calldatasize default {} }", ParserError, "Instructions are not supported as expressions for switch"); CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected token \"(\"");
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
} }
@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE(for_invalid_expression)
CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected token LBrace got 'Number'"); CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected token LBrace got 'Number'");
CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected token LBrace got 'Number'"); CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected token LBrace got 'Number'");
CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected token LBrace got 'Number'"); CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected token LBrace got 'Number'");
CHECK_PARSE_ERROR("{ for {} calldatasize {} {} }", ParserError, "Instructions are not supported as conditions for the for statement."); CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected token \"(\"");
CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
} }
@ -540,7 +540,7 @@ BOOST_AUTO_TEST_CASE(function_calls)
function g(a, b, c) function g(a, b, c)
{ {
} }
g(1, mul(2, address), f(mul(2, caller))) g(1, mul(2, address()), f(mul(2, caller())))
y() y()
})"; })";
boost::replace_all(source, "\t", " "); boost::replace_all(source, "\t", " ");