mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #1627 from ethereum/asmfunctions
Parsing assembly function definitions and calls.
This commit is contained in:
commit
7bdc4ddab2
@ -190,6 +190,10 @@ public:
|
||||
}
|
||||
(*this)(_instr.instruction);
|
||||
}
|
||||
void operator()(assembly::FunctionCall const&)
|
||||
{
|
||||
solAssert(false, "Function call not removed during desugaring phase.");
|
||||
}
|
||||
void operator()(Label const& _label)
|
||||
{
|
||||
m_state.assembly.setSourceLocation(_label.location);
|
||||
@ -249,7 +253,10 @@ public:
|
||||
_block.location
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
void operator()(assembly::FunctionDefinition const&)
|
||||
{
|
||||
solAssert(false, "Function definition not removed during desugaring phase.");
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -48,17 +48,22 @@ struct Label { SourceLocation location; std::string name; };
|
||||
struct Assignment { SourceLocation location; Identifier variableName; };
|
||||
struct FunctionalAssignment;
|
||||
struct VariableDeclaration;
|
||||
struct FunctionDefinition;
|
||||
struct FunctionCall;
|
||||
struct Block;
|
||||
using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionalInstruction, VariableDeclaration, Block>;
|
||||
using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>;
|
||||
/// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand
|
||||
/// side and requires x to occupy exactly one stack slot.
|
||||
struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; };
|
||||
/// Functional instruction, e.g. "mul(mload(20), add(2, x))"
|
||||
struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; };
|
||||
struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; };
|
||||
/// Block-scope variable declaration ("let x := mload(20)"), non-hoisted
|
||||
struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> 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; std::vector<std::string> arguments; std::vector<std::string> returns; Block body; };
|
||||
|
||||
struct LocationExtractor: boost::static_visitor<SourceLocation>
|
||||
{
|
||||
|
@ -62,6 +62,8 @@ assembly::Statement Parser::parseStatement()
|
||||
{
|
||||
case Token::Let:
|
||||
return parseVariableDeclaration();
|
||||
case Token::Function:
|
||||
return parseFunctionDefinition();
|
||||
case Token::LBrace:
|
||||
return parseBlock();
|
||||
case Token::Assign:
|
||||
@ -214,10 +216,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
|
||||
{
|
||||
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
|
||||
expectToken(Token::Let);
|
||||
varDecl.name = m_scanner->currentLiteral();
|
||||
if (instructions().count(varDecl.name))
|
||||
fatalParserError("Cannot use instruction names for identifier names.");
|
||||
expectToken(Token::Identifier);
|
||||
varDecl.name = expectAsmIdentifier();
|
||||
expectToken(Token::Colon);
|
||||
expectToken(Token::Assign);
|
||||
varDecl.value.reset(new Statement(parseExpression()));
|
||||
@ -225,44 +224,107 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
|
||||
return varDecl;
|
||||
}
|
||||
|
||||
FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
|
||||
assembly::FunctionDefinition Parser::parseFunctionDefinition()
|
||||
{
|
||||
if (_instruction.type() != typeid(Instruction))
|
||||
fatalParserError("Assembly instruction required in front of \"(\")");
|
||||
FunctionalInstruction ret;
|
||||
ret.instruction = std::move(boost::get<Instruction>(_instruction));
|
||||
ret.location = ret.instruction.location;
|
||||
solidity::Instruction instr = ret.instruction.instruction;
|
||||
InstructionInfo instrInfo = instructionInfo(instr);
|
||||
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
|
||||
fatalParserError("DUPi instructions not allowed for functional notation");
|
||||
if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
|
||||
fatalParserError("SWAPi instructions not allowed for functional notation");
|
||||
|
||||
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
|
||||
expectToken(Token::Function);
|
||||
funDef.name = expectAsmIdentifier();
|
||||
expectToken(Token::LParen);
|
||||
unsigned args = unsigned(instrInfo.args);
|
||||
for (unsigned i = 0; i < args; ++i)
|
||||
while (m_scanner->currentToken() != Token::RParen)
|
||||
{
|
||||
ret.arguments.emplace_back(parseExpression());
|
||||
if (i != args - 1)
|
||||
{
|
||||
if (m_scanner->currentToken() != Token::Comma)
|
||||
fatalParserError(string(
|
||||
"Expected comma (" +
|
||||
instrInfo.name +
|
||||
" expects " +
|
||||
boost::lexical_cast<string>(args) +
|
||||
" arguments)"
|
||||
));
|
||||
else
|
||||
m_scanner->next();
|
||||
}
|
||||
funDef.arguments.push_back(expectAsmIdentifier());
|
||||
if (m_scanner->currentToken() == Token::RParen)
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
}
|
||||
ret.location.end = endPosition();
|
||||
if (m_scanner->currentToken() == Token::Comma)
|
||||
fatalParserError(
|
||||
string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
|
||||
);
|
||||
expectToken(Token::RParen);
|
||||
return ret;
|
||||
if (m_scanner->currentToken() == Token::Sub)
|
||||
{
|
||||
expectToken(Token::Sub);
|
||||
expectToken(Token::GreaterThan);
|
||||
expectToken(Token::LParen);
|
||||
while (true)
|
||||
{
|
||||
funDef.returns.push_back(expectAsmIdentifier());
|
||||
if (m_scanner->currentToken() == Token::RParen)
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
}
|
||||
expectToken(Token::RParen);
|
||||
}
|
||||
funDef.body = parseBlock();
|
||||
funDef.location.end = funDef.body.location.end;
|
||||
return funDef;
|
||||
}
|
||||
|
||||
assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
|
||||
{
|
||||
if (_instruction.type() == typeid(Instruction))
|
||||
{
|
||||
FunctionalInstruction ret;
|
||||
ret.instruction = std::move(boost::get<Instruction>(_instruction));
|
||||
ret.location = ret.instruction.location;
|
||||
solidity::Instruction instr = ret.instruction.instruction;
|
||||
InstructionInfo instrInfo = instructionInfo(instr);
|
||||
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
|
||||
fatalParserError("DUPi instructions not allowed for functional notation");
|
||||
if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
|
||||
fatalParserError("SWAPi instructions not allowed for functional notation");
|
||||
expectToken(Token::LParen);
|
||||
unsigned args = unsigned(instrInfo.args);
|
||||
for (unsigned i = 0; i < args; ++i)
|
||||
{
|
||||
ret.arguments.emplace_back(parseExpression());
|
||||
if (i != args - 1)
|
||||
{
|
||||
if (m_scanner->currentToken() != Token::Comma)
|
||||
fatalParserError(string(
|
||||
"Expected comma (" +
|
||||
instrInfo.name +
|
||||
" expects " +
|
||||
boost::lexical_cast<string>(args) +
|
||||
" arguments)"
|
||||
));
|
||||
else
|
||||
m_scanner->next();
|
||||
}
|
||||
}
|
||||
ret.location.end = endPosition();
|
||||
if (m_scanner->currentToken() == Token::Comma)
|
||||
fatalParserError(
|
||||
string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
|
||||
);
|
||||
expectToken(Token::RParen);
|
||||
return ret;
|
||||
}
|
||||
else if (_instruction.type() == typeid(Identifier))
|
||||
{
|
||||
FunctionCall ret;
|
||||
ret.functionName = std::move(boost::get<Identifier>(_instruction));
|
||||
ret.location = ret.functionName.location;
|
||||
expectToken(Token::LParen);
|
||||
while (m_scanner->currentToken() != Token::RParen)
|
||||
{
|
||||
ret.arguments.emplace_back(parseExpression());
|
||||
if (m_scanner->currentToken() == Token::RParen)
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
}
|
||||
ret.location.end = endPosition();
|
||||
expectToken(Token::RParen);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
fatalParserError("Assembly instruction or function name required in front of \"(\")");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
string Parser::expectAsmIdentifier()
|
||||
{
|
||||
string name = m_scanner->currentLiteral();
|
||||
if (instructions().count(name))
|
||||
fatalParserError("Cannot use instruction names for identifier names.");
|
||||
expectToken(Token::Identifier);
|
||||
return name;
|
||||
}
|
||||
|
@ -67,7 +67,9 @@ protected:
|
||||
std::map<std::string, dev::solidity::Instruction> const& instructions();
|
||||
Statement parseElementaryOperation(bool _onlySinglePusher = false);
|
||||
VariableDeclaration parseVariableDeclaration();
|
||||
FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction);
|
||||
FunctionDefinition parseFunctionDefinition();
|
||||
Statement parseFunctionalInstruction(Statement&& _instruction);
|
||||
std::string expectAsmIdentifier();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -112,6 +112,24 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl
|
||||
return "let " + _variableDeclaration.name + " := " + boost::apply_visitor(*this, *_variableDeclaration.value);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")";
|
||||
if (!_functionDefinition.returns.empty())
|
||||
out += " -> (" + boost::algorithm::join(_functionDefinition.returns, ", ") + ")";
|
||||
return out + "\n" + (*this)(_functionDefinition.body);
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall)
|
||||
{
|
||||
return
|
||||
(*this)(_functionCall.functionName) + "(" +
|
||||
boost::algorithm::join(
|
||||
_functionCall.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
|
||||
", " ) +
|
||||
")";
|
||||
}
|
||||
|
||||
string AsmPrinter::operator()(Block const& _block)
|
||||
{
|
||||
if (_block.statements.empty())
|
||||
|
@ -53,6 +53,8 @@ public:
|
||||
std::string operator()(assembly::Assignment const& _assignment);
|
||||
std::string operator()(assembly::FunctionalAssignment const& _functionalAssignment);
|
||||
std::string operator()(assembly::VariableDeclaration const& _variableDeclaration);
|
||||
std::string operator()(assembly::FunctionDefinition const& _functionDefinition);
|
||||
std::string operator()(assembly::FunctionCall const& _functionCall);
|
||||
std::string operator()(assembly::Block const& _block);
|
||||
};
|
||||
|
||||
|
@ -159,6 +159,21 @@ BOOST_AUTO_TEST_CASE(blocks)
|
||||
BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_definitions)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ function f() { } function g(a) -> (x) { } }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ function f(a, d) { } function g(a, d) -> (x, y) { } }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_calls)
|
||||
{
|
||||
BOOST_CHECK(successParse("{ g(1, 2, f(mul(2, 3))) x() }"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Printing)
|
||||
@ -209,6 +224,16 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
|
||||
parsePrintCompare(parsed);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
|
||||
{
|
||||
parsePrintCompare("{\n function f(a, d)\n {\n mstore(a, d)\n }\n function g(a, d) -> (x, y)\n {\n }\n}");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_calls)
|
||||
{
|
||||
parsePrintCompare("{\n g(1, mul(2, x), f(mul(2, 3)))\n x()\n}");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Analysis)
|
||||
|
Loading…
Reference in New Issue
Block a user