mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #3297 from ethereum/separate_expression_and_statement
Separate expression and statement
This commit is contained in:
		
						commit
						2cdd789b5d
					
				| @ -42,11 +42,13 @@ using If = solidity::assembly::If; | ||||
| using Case = solidity::assembly::Case; | ||||
| using Switch = solidity::assembly::Switch; | ||||
| using ForLoop = solidity::assembly::ForLoop; | ||||
| using ExpressionStatement = solidity::assembly::ExpressionStatement; | ||||
| using Block = solidity::assembly::Block; | ||||
| 
 | ||||
| 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>; | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -77,6 +77,13 @@ void CodeTransform::operator()(StackAssignment const& _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) | ||||
| { | ||||
| 	m_assembly.setSourceLocation(_label.location); | ||||
| @ -460,7 +467,7 @@ AbstractAssembly::LabelID CodeTransform::functionEntryID(string const& _name, Sc | ||||
| 	return m_context->functionEntryIDs[&_function]; | ||||
| } | ||||
| 
 | ||||
| void CodeTransform::visitExpression(Statement const& _expression) | ||||
| void CodeTransform::visitExpression(Expression const& _expression) | ||||
| { | ||||
| 	int height = m_assembly.stackHeight(); | ||||
| 	boost::apply_visitor(*this, _expression); | ||||
|  | ||||
| @ -101,6 +101,7 @@ public: | ||||
| 	void operator()(Identifier const& _identifier); | ||||
| 	void operator()(FunctionalInstruction const& _instr); | ||||
| 	void operator()(FunctionCall const&); | ||||
| 	void operator()(ExpressionStatement const& _statement); | ||||
| 	void operator()(Label const& _label); | ||||
| 	void operator()(StackAssignment const& _assignment); | ||||
| 	void operator()(Assignment const& _assignment); | ||||
| @ -118,7 +119,7 @@ private: | ||||
| 	AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); | ||||
| 	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.
 | ||||
| 	void visitExpression(Statement const& _expression); | ||||
| 	void visitExpression(Expression const& _expression); | ||||
| 
 | ||||
| 	void visitStatements(std::vector<Statement> const& _statements); | ||||
| 
 | ||||
|  | ||||
| @ -37,6 +37,11 @@ Statement ASTCopier::operator()(Instruction const&) | ||||
| 	return {}; | ||||
| } | ||||
| 
 | ||||
| Statement ASTCopier::operator()(ExpressionStatement const& _statement) | ||||
| { | ||||
| 	return ExpressionStatement{ _statement.location, translate(_statement.expression) }; | ||||
| } | ||||
| 
 | ||||
| Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) | ||||
| { | ||||
| 	return VariableDeclaration{ | ||||
| @ -67,7 +72,7 @@ Statement ASTCopier::operator()(Label const&) | ||||
| 	return {}; | ||||
| } | ||||
| 
 | ||||
| Statement ASTCopier::operator()(FunctionCall const& _call) | ||||
| Expression ASTCopier::operator()(FunctionCall const& _call) | ||||
| { | ||||
| 	return FunctionCall{ | ||||
| 		_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{ | ||||
| 		_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)}; | ||||
| } | ||||
| 
 | ||||
| Statement ASTCopier::operator()(Literal const& _literal) | ||||
| Expression ASTCopier::operator()(Literal const& _literal) | ||||
| { | ||||
| 	return translate(_literal); | ||||
| } | ||||
| @ -140,9 +145,14 @@ Statement ASTCopier::operator ()(Block const& _block) | ||||
| 	return translate(_block); | ||||
| } | ||||
| 
 | ||||
| Expression ASTCopier::translate(Expression const& _expression) | ||||
| { | ||||
| 	return _expression.apply_visitor(static_cast<ExpressionCopier&>(*this)); | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
|  | ||||
| @ -34,28 +34,55 @@ namespace dev | ||||
| 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. | ||||
|  * Base class to be extended. | ||||
|  */ | ||||
| class ASTCopier: public boost::static_visitor<Statement> | ||||
| class ASTCopier: public ExpressionCopier, public StatementCopier | ||||
| { | ||||
| public: | ||||
| 	Statement operator()(Literal const& _literal); | ||||
| 	Statement operator()(Instruction const& _instruction); | ||||
| 	Statement operator()(Identifier const& _identifier); | ||||
| 	Statement operator()(FunctionalInstruction const& _instr); | ||||
| 	Statement operator()(FunctionCall const&); | ||||
| 	Statement operator()(Label const& _label); | ||||
| 	Statement operator()(StackAssignment const& _assignment); | ||||
| 	Statement operator()(Assignment const& _assignment); | ||||
| 	Statement operator()(VariableDeclaration const& _varDecl); | ||||
| 	Statement operator()(If const& _if); | ||||
| 	Statement operator()(Switch const& _switch); | ||||
| 	Statement operator()(FunctionDefinition const&); | ||||
| 	Statement operator()(ForLoop const&); | ||||
| 	Statement operator()(Block const& _block); | ||||
| 	virtual Expression operator()(Literal const& _literal) override; | ||||
| 	virtual Statement operator()(Instruction const& _instruction) override; | ||||
| 	virtual Expression operator()(Identifier const& _identifier) override; | ||||
| 	virtual Expression operator()(FunctionalInstruction const& _instr) override; | ||||
| 	virtual Expression operator()(FunctionCall const&) override; | ||||
| 	virtual Statement operator()(ExpressionStatement const& _statement) override; | ||||
| 	virtual Statement operator()(Label const& _label) override; | ||||
| 	virtual Statement operator()(StackAssignment const& _assignment) override; | ||||
| 	virtual Statement operator()(Assignment const& _assignment) override; | ||||
| 	virtual Statement operator()(VariableDeclaration const& _varDecl) override; | ||||
| 	virtual Statement operator()(If const& _if) override; | ||||
| 	virtual Statement operator()(Switch const& _switch) override; | ||||
| 	virtual Statement operator()(FunctionDefinition const&) override; | ||||
| 	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); | ||||
| 
 | ||||
| protected: | ||||
|  | ||||
| @ -42,6 +42,11 @@ void ASTWalker::operator()(FunctionCall const& _funCall) | ||||
| 	walkVector(_funCall.arguments | boost::adaptors::reversed); | ||||
| } | ||||
| 
 | ||||
| void ASTWalker::operator()(ExpressionStatement const& _statement) | ||||
| { | ||||
| 	boost::apply_visitor(*this, _statement.expression); | ||||
| } | ||||
| 
 | ||||
| void ASTWalker::operator()(Assignment const& _assignment) | ||||
| { | ||||
| 	for (auto const& name: _assignment.variableNames) | ||||
| @ -100,6 +105,11 @@ void ASTModifier::operator()(FunctionCall& _funCall) | ||||
| 	walkVector(_funCall.arguments | boost::adaptors::reversed); | ||||
| } | ||||
| 
 | ||||
| void ASTModifier::operator()(ExpressionStatement& _statement) | ||||
| { | ||||
| 	boost::apply_visitor(*this, _statement.expression); | ||||
| } | ||||
| 
 | ||||
| void ASTModifier::operator()(Assignment& _assignment) | ||||
| { | ||||
| 	for (auto& name: _assignment.variableNames) | ||||
|  | ||||
| @ -47,6 +47,7 @@ public: | ||||
| 	virtual void operator()(Identifier const&) {} | ||||
| 	virtual void operator()(FunctionalInstruction const& _instr); | ||||
| 	virtual void operator()(FunctionCall const& _funCall); | ||||
| 	virtual void operator()(ExpressionStatement const& _statement); | ||||
| 	virtual void operator()(Label const&) { solAssert(false, ""); } | ||||
| 	virtual void operator()(StackAssignment const&) { solAssert(false, ""); } | ||||
| 	virtual void operator()(Assignment const& _assignment); | ||||
| @ -77,6 +78,7 @@ public: | ||||
| 	virtual void operator()(Identifier&) {} | ||||
| 	virtual void operator()(FunctionalInstruction& _instr); | ||||
| 	virtual void operator()(FunctionCall& _funCall); | ||||
| 	virtual void operator()(ExpressionStatement& _statement); | ||||
| 	virtual void operator()(Label&) { solAssert(false, ""); } | ||||
| 	virtual void operator()(StackAssignment&) { solAssert(false, ""); } | ||||
| 	virtual void operator()(Assignment& _assignment); | ||||
| @ -98,6 +100,10 @@ protected: | ||||
| 	{ | ||||
| 		boost::apply_visitor(*this, _st); | ||||
| 	} | ||||
| 	virtual void visit(Expression& _e) | ||||
| 	{ | ||||
| 		boost::apply_visitor(*this, _e); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -26,14 +26,14 @@ using namespace std; | ||||
| using namespace dev; | ||||
| 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)) | ||||
| 			// No recursive substitution
 | ||||
| 			return ASTCopier().translate(*m_substitutions.at(name)); | ||||
| 	} | ||||
| 	return ASTCopier::translate(_statement); | ||||
| 	return ASTCopier::translate(_expression); | ||||
| } | ||||
|  | ||||
| @ -33,18 +33,17 @@ namespace julia | ||||
| 
 | ||||
| /**
 | ||||
|  * Specific AST copier that replaces certain identifiers with expressions. | ||||
|  * Only works on ASTs that are expressions. | ||||
|  */ | ||||
| class Substitution: public ASTCopier | ||||
| { | ||||
| public: | ||||
| 	Substitution(std::map<std::string, Statement const*> const& _substitutions): | ||||
| 	Substitution(std::map<std::string, Expression const*> const& _substitutions): | ||||
| 		m_substitutions(_substitutions) | ||||
| 	{} | ||||
| 	virtual Statement translate(Statement const& _statement) override; | ||||
| 	virtual Expression translate(Expression const& _expression) override; | ||||
| 
 | ||||
| private: | ||||
| 	std::map<std::string, Statement const*> const& m_substitutions; | ||||
| 	std::map<std::string, Expression const*> const& m_substitutions; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -50,6 +50,10 @@ public: | ||||
| 		for (auto const& arg: _instr.arguments) | ||||
| 			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::Assignment const& _assignment) | ||||
| 	{ | ||||
|  | ||||
| @ -155,6 +155,16 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) | ||||
| 	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) | ||||
| { | ||||
| 	solAssert(!m_julia, ""); | ||||
| @ -407,13 +417,13 @@ bool AsmAnalyzer::operator()(Block const& _block) | ||||
| 	return success; | ||||
| } | ||||
| 
 | ||||
| bool AsmAnalyzer::expectExpression(Statement const& _statement) | ||||
| bool AsmAnalyzer::expectExpression(Expression const& _expr) | ||||
| { | ||||
| 	bool success = true; | ||||
| 	int const initialHeight = m_stackHeight; | ||||
| 	if (!boost::apply_visitor(*this, _statement)) | ||||
| 	if (!boost::apply_visitor(*this, _expr)) | ||||
| 		success = false; | ||||
| 	if (!expectDeposit(1, initialHeight, locationOf(_statement))) | ||||
| 	if (!expectDeposit(1, initialHeight, locationOf(_expr))) | ||||
| 		success = false; | ||||
| 	return success; | ||||
| } | ||||
|  | ||||
| @ -65,6 +65,7 @@ public: | ||||
| 	bool operator()(assembly::Identifier const&); | ||||
| 	bool operator()(assembly::FunctionalInstruction const& _functionalInstruction); | ||||
| 	bool operator()(assembly::Label const& _label); | ||||
| 	bool operator()(assembly::ExpressionStatement const&); | ||||
| 	bool operator()(assembly::StackAssignment const&); | ||||
| 	bool operator()(assembly::Assignment const& _assignment); | ||||
| 	bool operator()(assembly::VariableDeclaration const& _variableDeclaration); | ||||
| @ -77,7 +78,7 @@ public: | ||||
| 
 | ||||
| private: | ||||
| 	/// 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); | ||||
| 
 | ||||
| 	/// Verifies that a variable to be assigned to exists and has the same size
 | ||||
|  | ||||
| @ -58,23 +58,25 @@ struct StackAssignment { SourceLocation location; Identifier variableName; }; | ||||
| /// 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
 | ||||
| /// 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))"
 | ||||
| struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector<Statement> arguments; }; | ||||
| struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; }; | ||||
| struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector<Expression> 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
 | ||||
| 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)
 | ||||
| struct Block { SourceLocation location; std::vector<Statement> statements; }; | ||||
| /// Function definition ("function f(a, b) -> (d, e) { ... }")
 | ||||
| struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; | ||||
| /// 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
 | ||||
| struct Case { SourceLocation location; std::shared_ptr<Literal> value; Block body; }; | ||||
| /// Switch statement
 | ||||
| struct Switch { SourceLocation location; std::shared_ptr<Statement> expression; std::vector<Case> cases; }; | ||||
| struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Statement> condition; Block post; Block body; }; | ||||
| struct Switch { SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; }; | ||||
| struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; }; | ||||
| 
 | ||||
| struct LocationExtractor: boost::static_visitor<SourceLocation> | ||||
| { | ||||
|  | ||||
| @ -45,11 +45,13 @@ struct If; | ||||
| struct Switch; | ||||
| struct Case; | ||||
| struct ForLoop; | ||||
| struct ExpressionStatement; | ||||
| struct Block; | ||||
| 
 | ||||
| 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>; | ||||
| 
 | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -77,9 +77,7 @@ assembly::Statement Parser::parseStatement() | ||||
| 	{ | ||||
| 		assembly::If _if = createWithLocation<assembly::If>(); | ||||
| 		m_scanner->next(); | ||||
| 		_if.condition = make_shared<Statement>(parseExpression()); | ||||
| 		if (_if.condition->type() == typeid(assembly::Instruction)) | ||||
| 			fatalParserError("Instructions are not supported as conditions for if - try to append \"()\"."); | ||||
| 		_if.condition = make_shared<Expression>(parseExpression()); | ||||
| 		_if.body = parseBlock(); | ||||
| 		return _if; | ||||
| 	} | ||||
| @ -87,9 +85,7 @@ assembly::Statement Parser::parseStatement() | ||||
| 	{ | ||||
| 		assembly::Switch _switch = createWithLocation<assembly::Switch>(); | ||||
| 		m_scanner->next(); | ||||
| 		_switch.expression = make_shared<Statement>(parseExpression()); | ||||
| 		if (_switch.expression->type() == typeid(assembly::Instruction)) | ||||
| 			fatalParserError("Instructions are not supported as expressions for switch - try to append \"()\"."); | ||||
| 		_switch.expression = make_shared<Expression>(parseExpression()); | ||||
| 		while (m_scanner->currentToken() == Token::Case) | ||||
| 			_switch.cases.emplace_back(parseCase()); | ||||
| 		if (m_scanner->currentToken() == Token::Default) | ||||
| @ -127,18 +123,21 @@ assembly::Statement Parser::parseStatement() | ||||
| 	// Simple instruction (might turn into functional),
 | ||||
| 	// literal,
 | ||||
| 	// identifier (might turn into label or functional assignment)
 | ||||
| 	Statement statement(parseElementaryOperation(false)); | ||||
| 	ElementaryOperation elementary(parseElementaryOperation(false)); | ||||
| 	switch (currentToken()) | ||||
| 	{ | ||||
| 	case Token::LParen: | ||||
| 		return parseCall(std::move(statement)); | ||||
| 	{ | ||||
| 		Expression expr = parseCall(std::move(elementary)); | ||||
| 		return ExpressionStatement{locationOf(expr), expr}; | ||||
| 	} | ||||
| 	case Token::Comma: | ||||
| 	{ | ||||
| 		// 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)."); | ||||
| 		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.variableNames.emplace_back(identifier); | ||||
| @ -146,25 +145,25 @@ assembly::Statement Parser::parseStatement() | ||||
| 		do | ||||
| 		{ | ||||
| 			expectToken(Token::Comma); | ||||
| 			statement = parseElementaryOperation(false); | ||||
| 			if (statement.type() != typeid(assembly::Identifier)) | ||||
| 			elementary = parseElementaryOperation(false); | ||||
| 			if (elementary.type() != typeid(assembly::Identifier)) | ||||
| 				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); | ||||
| 
 | ||||
| 		expectToken(Token::Colon); | ||||
| 		expectToken(Token::Assign); | ||||
| 
 | ||||
| 		assignment.value.reset(new Statement(parseExpression())); | ||||
| 		assignment.value.reset(new Expression(parseExpression())); | ||||
| 		assignment.location.end = locationOf(*assignment.value).end; | ||||
| 		return assignment; | ||||
| 	} | ||||
| 	case Token::Colon: | ||||
| 	{ | ||||
| 		if (statement.type() != typeid(assembly::Identifier)) | ||||
| 		if (elementary.type() != typeid(assembly::Identifier)) | ||||
| 			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(); | ||||
| 		// identifier:=: should be parsed as identifier: =: (i.e. a label),
 | ||||
| 		// 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."); | ||||
| 			advance(); | ||||
| 			assignment.variableNames.emplace_back(identifier); | ||||
| 			assignment.value.reset(new Statement(parseExpression())); | ||||
| 			assignment.value.reset(new Expression(parseExpression())); | ||||
| 			assignment.location.end = locationOf(*assignment.value).end; | ||||
| 			return assignment; | ||||
| 		} | ||||
| @ -194,7 +193,21 @@ assembly::Statement Parser::parseStatement() | ||||
| 			fatalParserError("Call or assignment expected."); | ||||
| 		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() | ||||
| @ -206,10 +219,10 @@ assembly::Case Parser::parseCase() | ||||
| 	else if (m_scanner->currentToken() == Token::Case) | ||||
| 	{ | ||||
| 		m_scanner->next(); | ||||
| 		assembly::Statement statement = parseElementaryOperation(); | ||||
| 		if (statement.type() != typeid(assembly::Literal)) | ||||
| 		ElementaryOperation literal = parseElementaryOperation(); | ||||
| 		if (literal.type() != typeid(assembly::Literal)) | ||||
| 			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 | ||||
| 		fatalParserError("Case or default case expected."); | ||||
| @ -224,19 +237,17 @@ assembly::ForLoop Parser::parseForLoop() | ||||
| 	ForLoop forLoop = createWithLocation<ForLoop>(); | ||||
| 	expectToken(Token::For); | ||||
| 	forLoop.pre = parseBlock(); | ||||
| 	forLoop.condition = make_shared<Statement>(parseExpression()); | ||||
| 	if (forLoop.condition->type() == typeid(assembly::Instruction)) | ||||
| 		fatalParserError("Instructions are not supported as conditions for the for statement."); | ||||
| 	forLoop.condition = make_shared<Expression>(parseExpression()); | ||||
| 	forLoop.post = parseBlock(); | ||||
| 	forLoop.body = parseBlock(); | ||||
| 	forLoop.location.end = forLoop.body.location.end; | ||||
| 	return forLoop; | ||||
| } | ||||
| 
 | ||||
| assembly::Statement Parser::parseExpression() | ||||
| assembly::Expression Parser::parseExpression() | ||||
| { | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	Statement operation = parseElementaryOperation(true); | ||||
| 	ElementaryOperation operation = parseElementaryOperation(true); | ||||
| 	if (operation.type() == typeid(Instruction)) | ||||
| 	{ | ||||
| 		Instruction const& instr = boost::get<Instruction>(operation); | ||||
| @ -252,8 +263,18 @@ assembly::Statement Parser::parseExpression() | ||||
| 	} | ||||
| 	if (currentToken() == Token::LParen) | ||||
| 		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 | ||||
| 		return operation; | ||||
| 	{ | ||||
| 		solAssert(operation.type() == typeid(assembly::Literal), ""); | ||||
| 		return boost::get<assembly::Literal>(operation); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) | ||||
| Parser::ElementaryOperation Parser::parseElementaryOperation(bool _onlySinglePusher) | ||||
| { | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	Statement ret; | ||||
| 	ElementaryOperation ret; | ||||
| 	switch (currentToken()) | ||||
| 	{ | ||||
| 	case Token::Identifier: | ||||
| @ -402,7 +423,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() | ||||
| 	{ | ||||
| 		expectToken(Token::Colon); | ||||
| 		expectToken(Token::Assign); | ||||
| 		varDecl.value.reset(new Statement(parseExpression())); | ||||
| 		varDecl.value.reset(new Expression(parseExpression())); | ||||
| 		varDecl.location.end = locationOf(*varDecl.value).end; | ||||
| 	} | ||||
| 	else | ||||
| @ -442,13 +463,13 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() | ||||
| 	return funDef; | ||||
| } | ||||
| 
 | ||||
| assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) | ||||
| assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) | ||||
| { | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	if (_instruction.type() == typeid(Instruction)) | ||||
| 	if (_initialOp.type() == typeid(Instruction)) | ||||
| 	{ | ||||
| 		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; | ||||
| 		ret.instruction = instruction.instruction; | ||||
| 		ret.location = std::move(instruction.location); | ||||
| @ -499,10 +520,10 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) | ||||
| 		expectToken(Token::RParen); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	else if (_instruction.type() == typeid(Identifier)) | ||||
| 	else if (_initialOp.type() == typeid(Identifier)) | ||||
| 	{ | ||||
| 		FunctionCall ret; | ||||
| 		ret.functionName = std::move(boost::get<Identifier>(_instruction)); | ||||
| 		ret.functionName = std::move(boost::get<Identifier>(_initialOp)); | ||||
| 		ret.location = ret.functionName.location; | ||||
| 		expectToken(Token::LParen); | ||||
| 		while (currentToken() != Token::RParen) | ||||
|  | ||||
| @ -44,6 +44,8 @@ public: | ||||
| 	std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner); | ||||
| 
 | ||||
| protected: | ||||
| 	using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>; | ||||
| 
 | ||||
| 	/// Creates an inline assembly node with the given source location.
 | ||||
| 	template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) const | ||||
| 	{ | ||||
| @ -65,13 +67,13 @@ protected: | ||||
| 	Case parseCase(); | ||||
| 	ForLoop parseForLoop(); | ||||
| 	/// 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<dev::solidity::Instruction, std::string> const& instructionNames(); | ||||
| 	Statement parseElementaryOperation(bool _onlySinglePusher = false); | ||||
| 	ElementaryOperation parseElementaryOperation(bool _onlySinglePusher = false); | ||||
| 	VariableDeclaration parseVariableDeclaration(); | ||||
| 	FunctionDefinition parseFunctionDefinition(); | ||||
| 	Statement parseCall(Statement&& _instruction); | ||||
| 	assembly::Expression parseCall(ElementaryOperation&& _initialOp); | ||||
| 	TypedName parseTypedName(); | ||||
| 	std::string expectAsmIdentifier(); | ||||
| 
 | ||||
|  | ||||
| @ -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) | ||||
| { | ||||
| 	solAssert(!m_julia, ""); | ||||
|  | ||||
| @ -42,6 +42,7 @@ public: | ||||
| 	std::string operator()(assembly::Literal const& _literal); | ||||
| 	std::string operator()(assembly::Identifier const& _identifier); | ||||
| 	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::StackAssignment const& _assignment); | ||||
| 	std::string operator()(assembly::Assignment const& _assignment); | ||||
|  | ||||
| @ -45,6 +45,11 @@ ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): | ||||
| 	m_currentScope = &scope(nullptr); | ||||
| } | ||||
| 
 | ||||
| bool ScopeFiller::operator()(ExpressionStatement const& _expr) | ||||
| { | ||||
| 	return boost::apply_visitor(*this, _expr.expression); | ||||
| } | ||||
| 
 | ||||
| bool ScopeFiller::operator()(Label const& _item) | ||||
| { | ||||
| 	if (!m_currentScope->registerLabel(_item.name)) | ||||
|  | ||||
| @ -53,6 +53,7 @@ public: | ||||
| 	bool operator()(assembly::Literal const&) { return true; } | ||||
| 	bool operator()(assembly::Identifier 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::StackAssignment const&) { return true; } | ||||
| 	bool operator()(assembly::Assignment const&) { return true; } | ||||
|  | ||||
| @ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(if_statement_scope) | ||||
| 
 | ||||
| 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() {}"); | ||||
| 	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"); | ||||
| @ -296,7 +296,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case) | ||||
| BOOST_AUTO_TEST_CASE(switch_invalid_expression) | ||||
| { | ||||
| 	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"); | ||||
| } | ||||
| 
 | ||||
| @ -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 {} 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"); | ||||
| } | ||||
| 
 | ||||
| @ -540,7 +540,7 @@ BOOST_AUTO_TEST_CASE(function_calls) | ||||
| 	function g(a, b, c) | ||||
| 	{ | ||||
| 	} | ||||
| 	g(1, mul(2, address), f(mul(2, caller))) | ||||
| 	g(1, mul(2, address()), f(mul(2, caller()))) | ||||
| 	y() | ||||
| })"; | ||||
| 	boost::replace_all(source, "\t", "    "); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user