mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add extra token for assembly assignment
Adding an extra token for := prevents whitespace between : = being valid
This commit is contained in:
parent
5bf8af8004
commit
f395d5bab4
@ -596,7 +596,12 @@ void Scanner::scanToken()
|
|||||||
token = Token::Period;
|
token = Token::Period;
|
||||||
break;
|
break;
|
||||||
case ':':
|
case ':':
|
||||||
token = selectToken(Token::Colon);
|
// : :=
|
||||||
|
advance();
|
||||||
|
if (m_char == '=')
|
||||||
|
token = selectToken(Token::AssemblyAssign);
|
||||||
|
else
|
||||||
|
token = Token::Colon;
|
||||||
break;
|
break;
|
||||||
case ';':
|
case ';':
|
||||||
token = selectToken(Token::Semicolon);
|
token = selectToken(Token::Semicolon);
|
||||||
|
@ -140,6 +140,8 @@ namespace langutil
|
|||||||
T(Dec, "--", 0) \
|
T(Dec, "--", 0) \
|
||||||
K(Delete, "delete", 0) \
|
K(Delete, "delete", 0) \
|
||||||
\
|
\
|
||||||
|
/* Inline Assembly Operators */ \
|
||||||
|
T(AssemblyAssign, ":=", 2) \
|
||||||
/* Keywords */ \
|
/* Keywords */ \
|
||||||
K(Anonymous, "anonymous", 0) \
|
K(Anonymous, "anonymous", 0) \
|
||||||
K(As, "as", 0) \
|
K(As, "as", 0) \
|
||||||
|
@ -138,63 +138,66 @@ Statement Parser::parseStatement()
|
|||||||
return ExpressionStatement{locationOf(expr), expr};
|
return ExpressionStatement{locationOf(expr), expr};
|
||||||
}
|
}
|
||||||
case Token::Comma:
|
case Token::Comma:
|
||||||
|
case Token::AssemblyAssign:
|
||||||
{
|
{
|
||||||
// if a comma follows, a multiple assignment is assumed
|
std::vector<Identifier> variableNames;
|
||||||
|
|
||||||
if (elementary.type() != typeid(Identifier))
|
while (true)
|
||||||
fatalParserError("Label name / variable name must precede \",\" (multiple assignment).");
|
|
||||||
Identifier const& identifier = boost::get<Identifier>(elementary);
|
|
||||||
|
|
||||||
Assignment assignment = createWithLocation<Assignment>(identifier.location);
|
|
||||||
assignment.variableNames.emplace_back(identifier);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
expectToken(Token::Comma);
|
|
||||||
elementary = parseElementaryOperation();
|
|
||||||
if (elementary.type() != typeid(Identifier))
|
if (elementary.type() != typeid(Identifier))
|
||||||
fatalParserError("Variable name expected in multiple assignment.");
|
{
|
||||||
assignment.variableNames.emplace_back(boost::get<Identifier>(elementary));
|
auto const token = currentToken() == Token::Comma ? "," : ":=";
|
||||||
}
|
|
||||||
while (currentToken() == Token::Comma);
|
|
||||||
|
|
||||||
expectToken(Token::Colon);
|
fatalParserError(
|
||||||
expectToken(Token::Assign);
|
std::string("Variable name must precede \"") +
|
||||||
|
token +
|
||||||
|
"\"" +
|
||||||
|
(currentToken() == Token::Comma ? " in multiple assignment." : " in assignment.")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const& identifier = boost::get<Identifier>(elementary);
|
||||||
|
|
||||||
|
if (m_dialect->builtin(identifier.name))
|
||||||
|
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
|
||||||
|
|
||||||
|
variableNames.emplace_back(identifier);
|
||||||
|
|
||||||
|
if (currentToken() != Token::Comma)
|
||||||
|
break;
|
||||||
|
|
||||||
|
expectToken(Token::Comma);
|
||||||
|
|
||||||
|
elementary = parseElementaryOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
Assignment assignment =
|
||||||
|
createWithLocation<Assignment>(boost::get<Identifier>(elementary).location);
|
||||||
|
assignment.variableNames = std::move(variableNames);
|
||||||
|
|
||||||
|
expectToken(Token::AssemblyAssign);
|
||||||
|
|
||||||
assignment.value.reset(new Expression(parseExpression()));
|
assignment.value.reset(new Expression(parseExpression()));
|
||||||
assignment.location.end = locationOf(*assignment.value).end;
|
assignment.location.end = locationOf(*assignment.value).end;
|
||||||
|
|
||||||
return Statement{std::move(assignment)};
|
return Statement{std::move(assignment)};
|
||||||
}
|
}
|
||||||
case Token::Colon:
|
case Token::Colon:
|
||||||
{
|
{
|
||||||
if (elementary.type() != typeid(Identifier))
|
if (elementary.type() != typeid(Identifier))
|
||||||
fatalParserError("Label name / variable name must precede \":\".");
|
fatalParserError("Label name must precede \":\".");
|
||||||
|
|
||||||
Identifier const& identifier = boost::get<Identifier>(elementary);
|
Identifier const& identifier = boost::get<Identifier>(elementary);
|
||||||
|
|
||||||
advance();
|
advance();
|
||||||
// identifier:=: should be parsed as identifier: =: (i.e. a label),
|
|
||||||
// while identifier:= (being followed by a non-colon) as identifier := (assignment).
|
// label
|
||||||
if (currentToken() == Token::Assign && peekNextToken() != Token::Colon)
|
if (m_dialect->flavour != AsmFlavour::Loose)
|
||||||
{
|
fatalParserError("Labels are not supported.");
|
||||||
Assignment assignment = createWithLocation<Assignment>(identifier.location);
|
|
||||||
if (m_dialect->builtin(identifier.name))
|
Label label = createWithLocation<Label>(identifier.location);
|
||||||
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
|
label.name = identifier.name;
|
||||||
else if (m_dialect->flavour != AsmFlavour::Yul && instructions().count(identifier.name.str()))
|
return label;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (m_dialect->flavour != AsmFlavour::Loose)
|
if (m_dialect->flavour != AsmFlavour::Loose)
|
||||||
@ -439,10 +442,9 @@ VariableDeclaration Parser::parseVariableDeclaration()
|
|||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (currentToken() == Token::Colon)
|
if (currentToken() == Token::AssemblyAssign)
|
||||||
{
|
{
|
||||||
expectToken(Token::Colon);
|
expectToken(Token::AssemblyAssign);
|
||||||
expectToken(Token::Assign);
|
|
||||||
varDecl.value = make_unique<Expression>(parseExpression());
|
varDecl.value = make_unique<Expression>(parseExpression());
|
||||||
varDecl.location.end = locationOf(*varDecl.value).end;
|
varDecl.location.end = locationOf(*varDecl.value).end;
|
||||||
}
|
}
|
||||||
|
@ -490,8 +490,8 @@ BOOST_AUTO_TEST_CASE(recursion_depth)
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(multiple_assignment)
|
BOOST_AUTO_TEST_CASE(multiple_assignment)
|
||||||
{
|
{
|
||||||
CHECK_PARSE_ERROR("{ let x function f() -> a, b {} 123, x := f() }", ParserError, "Label name / variable name must precede \",\" (multiple assignment).");
|
CHECK_PARSE_ERROR("{ let x function f() -> a, b {} 123, x := f() }", ParserError, "Variable name must precede \",\" in multiple assignment.");
|
||||||
CHECK_PARSE_ERROR("{ let x function f() -> a, b {} x, 123 := f() }", ParserError, "Variable name expected in multiple assignment.");
|
CHECK_PARSE_ERROR("{ let x function f() -> a, b {} x, 123 := f() }", ParserError, "Variable name must precede \":=\" in assignment.");
|
||||||
|
|
||||||
/// NOTE: Travis hiccups if not having a variable
|
/// NOTE: Travis hiccups if not having a variable
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
@ -706,7 +706,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_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)
|
BOOST_AUTO_TEST_CASE(revert)
|
||||||
|
@ -59,6 +59,32 @@ BOOST_AUTO_TEST_CASE(smoke_test)
|
|||||||
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
|
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(assembly_assign)
|
||||||
|
{
|
||||||
|
Scanner scanner(CharStream("let a := 1", ""));
|
||||||
|
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Let);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1");
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(assembly_multiple_assign)
|
||||||
|
{
|
||||||
|
Scanner scanner(CharStream("let a, b, c := 1", ""));
|
||||||
|
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Let);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::Comma);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::Comma);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
|
||||||
|
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1");
|
||||||
|
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(string_escapes)
|
BOOST_AUTO_TEST_CASE(string_escapes)
|
||||||
{
|
{
|
||||||
Scanner scanner(CharStream(" { \"a\\x61\"", ""));
|
Scanner scanner(CharStream(" { \"a\\x61\"", ""));
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
assembly {
|
||||||
|
let mod := 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (67-70): Cannot use instruction names for identifier names.
|
||||||
|
// ParserError: (71-73): Expected ';' but got ':='
|
@ -7,5 +7,5 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// ParserError: (87-88): Literal, identifier or instruction expected.
|
// ParserError: (87-89): Literal, identifier or instruction expected.
|
||||||
// ParserError: (87-88): Expected primary expression.
|
// ParserError: (87-89): Expected primary expression.
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
assembly {
|
||||||
|
function g() -> a,b, c {}
|
||||||
|
let a, sub, mov := g()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (102-105): Cannot use instruction names for identifier names.
|
||||||
|
// ParserError: (105-106): Expected ';' but got ','
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
assembly {
|
||||||
|
return := 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (70-72): Variable name must precede ":=" in assignment.
|
||||||
|
// ParserError: (70-72): Expected primary expression.
|
@ -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.
|
@ -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.
|
@ -0,0 +1,11 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
assembly {
|
||||||
|
function g() -> a,b, c {}
|
||||||
|
let x, y ,z : = g()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (107-108): Literal, identifier or instruction expected.
|
||||||
|
// ParserError: (107-108): Expected primary expression.
|
@ -217,7 +217,7 @@ BOOST_AUTO_TEST_CASE(tokens_as_identifers)
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(lacking_types)
|
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("{ let x:u256 := 1 }", ParserError, "Expected ':' but got '}'");
|
||||||
CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected ':' but got ')'");
|
CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected ':' but got ')'");
|
||||||
CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected ':' but got '{'");
|
CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected ':' but got '{'");
|
||||||
@ -271,8 +271,8 @@ BOOST_AUTO_TEST_CASE(recursion_depth)
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(multiple_assignment)
|
BOOST_AUTO_TEST_CASE(multiple_assignment)
|
||||||
{
|
{
|
||||||
CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} 123:u256, x := f() }", ParserError, "Label name / variable name must precede \",\" (multiple assignment).");
|
CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} 123:u256, x := f() }", ParserError, "Variable name must precede \",\" in multiple assignment.");
|
||||||
CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} x, 123:u256 := f() }", ParserError, "Variable name expected in multiple assignment.");
|
CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} x, 123:u256 := f() }", ParserError, "Variable name must precede \":=\" in assignment.");
|
||||||
|
|
||||||
/// NOTE: Travis hiccups if not having a variable
|
/// NOTE: Travis hiccups if not having a variable
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
@ -333,6 +333,7 @@ BOOST_AUTO_TEST_CASE(builtins_parser)
|
|||||||
CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
||||||
CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
|
||||||
CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect);
|
CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect);
|
||||||
|
CHECK_ERROR_DIALECT("{ function g() -> a,b {} builtin, builtin2 := g() }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(builtins_analysis)
|
BOOST_AUTO_TEST_CASE(builtins_analysis)
|
||||||
|
Loading…
Reference in New Issue
Block a user