Skip to content

Commit

Permalink
Add extra token for assembly assignment
Browse files Browse the repository at this point in the history
Adding an extra token for := prevents whitespace between : = being valid
  • Loading branch information
Marenz committed Feb 19, 2019
1 parent 2f0926c commit 6b19c0a
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 35 deletions.
7 changes: 6 additions & 1 deletion liblangutil/Scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,12 @@ void Scanner::scanToken()
token = Token::Period;
break;
case ':':
token = selectToken(Token::Colon);
// : :=
advance();
if (m_char == '=')
token = selectToken(Token::AssemblyAssign);
else
token = Token::Colon;
break;
case ';':
token = selectToken(Token::Semicolon);
Expand Down
2 changes: 2 additions & 0 deletions liblangutil/Token.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ namespace langutil
T(Dec, "--", 0) \
K(Delete, "delete", 0) \
\
/* Inline Assembly Operators */ \
T(AssemblyAssign, ":=", 2) \
/* Keywords */ \
K(Anonymous, "anonymous", 0) \
K(As, "as", 0) \
Expand Down
65 changes: 35 additions & 30 deletions libyul/AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,43 +158,49 @@ Statement Parser::parseStatement()
}
while (currentToken() == Token::Comma);

expectToken(Token::Colon);
expectToken(Token::Assign);
expectToken(Token::AssemblyAssign);

assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end;
return Statement{std::move(assignment)};
}
case Token::AssemblyAssign:
{
if (elementary.type() != typeid(Identifier))
fatalParserError("Variable name must precede \":=\".");

Identifier const& identifier = boost::get<Identifier>(elementary);

Assignment assignment = createWithLocation<Assignment>(identifier.location);

if (m_dialect->builtin(identifier.name))
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
else if (m_dialect->flavour != AsmFlavour::Yul && instructions().count(identifier.name.str()))
fatalParserError("Cannot use instruction names for identifier names.");

advance();
assignment.variableNames.emplace_back(identifier);
assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end;
return Statement{std::move(assignment)};

}
case Token::Colon:
{
if (elementary.type() != typeid(Identifier))
fatalParserError("Label name / variable name must precede \":\".");
fatalParserError("Label name must precede \":\".");

Identifier const& identifier = boost::get<Identifier>(elementary);

advance();
// identifier:=: should be parsed as identifier: =: (i.e. a label),
// while identifier:= (being followed by a non-colon) as identifier := (assignment).
if (currentToken() == Token::Assign && peekNextToken() != Token::Colon)
{
Assignment assignment = createWithLocation<Assignment>(identifier.location);
if (m_dialect->builtin(identifier.name))
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
else if (m_dialect->flavour != AsmFlavour::Yul && instructions().count(identifier.name.str()))
fatalParserError("Cannot use instruction names for identifier names.");
advance();
assignment.variableNames.emplace_back(identifier);
assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end;
return Statement{std::move(assignment)};
}
else
{
// label
if (m_dialect->flavour != AsmFlavour::Loose)
fatalParserError("Labels are not supported.");
Label label = createWithLocation<Label>(identifier.location);
label.name = identifier.name;
return label;
}

// label
if (m_dialect->flavour != AsmFlavour::Loose)
fatalParserError("Labels are not supported.");

Label label = createWithLocation<Label>(identifier.location);
label.name = identifier.name;
return label;
}
default:
if (m_dialect->flavour != AsmFlavour::Loose)
Expand Down Expand Up @@ -439,10 +445,9 @@ VariableDeclaration Parser::parseVariableDeclaration()
else
break;
}
if (currentToken() == Token::Colon)
if (currentToken() == Token::AssemblyAssign)
{
expectToken(Token::Colon);
expectToken(Token::Assign);
expectToken(Token::AssemblyAssign);
varDecl.value = make_unique<Expression>(parseExpression());
varDecl.location.end = locationOf(*varDecl.value).end;
}
Expand Down
2 changes: 1 addition & 1 deletion test/libsolidity/InlineAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)

BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
{
CHECK_ASSEMBLE_ERROR("{ gas := 2 }", ParserError, "Label name / variable name must precede \":\"");
CHECK_ASSEMBLE_ERROR("{ gas := 2 }", ParserError, "Variable name must precede \":=\"");
}

BOOST_AUTO_TEST_CASE(revert)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ contract C {
}
}
// ----
// ParserError: (87-88): Literal, identifier or instruction expected.
// ParserError: (87-88): Expected primary expression.
// ParserError: (87-89): Literal, identifier or instruction expected.
// ParserError: (87-89): Expected primary expression.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C {
function f() public pure {
assembly {
return := 1
}
}
}
// ----
// ParserError: (70-72): Variable name must precede ":=".
// ParserError: (70-72): Expected primary expression.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C {
function f() public pure {
assembly {
return : 1
}
}
}
// ----
// ParserError: (70-71): Label name must precede ":".
// ParserError: (70-71): Expected primary expression.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C {
function f() public pure {
assembly {
let x : = mload(0)
}
}
}
// ----
// ParserError: (69-70): Literal, identifier or instruction expected.
// ParserError: (69-70): Expected primary expression.
2 changes: 1 addition & 1 deletion test/libyul/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ BOOST_AUTO_TEST_CASE(tokens_as_identifers)

BOOST_AUTO_TEST_CASE(lacking_types)
{
CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected identifier but got '='");
CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected ':' but got ':='");
CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected ':' but got '}'");
CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected ':' but got ')'");
CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected ':' but got '{'");
Expand Down

0 comments on commit 6b19c0a

Please sign in to comment.