Correctly parse ambiguities like A.B[10] x and x.y[10] = 3.

This commit is contained in:
chriseth 2015-10-16 16:12:14 +02:00
parent 452d473293
commit 87079bd3fd
4 changed files with 99 additions and 27 deletions

View File

@ -734,6 +734,9 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
// These two cases are very hard to distinguish: // These two cases are very hard to distinguish:
// x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9; // x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9;
// In the first case, x is a type name, in the second it is the name of a variable. // In the first case, x is a type name, in the second it is the name of a variable.
// As an extension, we can even have:
// `x.y.z[1][2] a;` and `x.y.z[1][2] = 10;`
// Where in the first, x.y.z leads to a type name where in the second, it accesses structs.
switch (peekStatementType()) switch (peekStatementType())
{ {
case LookAheadInfo::VariableDeclarationStatement: case LookAheadInfo::VariableDeclarationStatement:
@ -744,36 +747,43 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
break; break;
} }
// At this point, we have '(Identifier|ElementaryTypeName) "["'. // At this point, we have 'Identifier "["' or 'Identifier "." Identifier' or 'ElementoryTypeName "["'.
// We parse '(Identifier|ElementaryTypeName) ( "[" Expression "]" )+' and then decide whether to hand this over // We parse '(Identifier ("." Identifier)* |ElementaryTypeName) ( "[" Expression "]" )+'
// to ExpressionStatement or create a VariableDeclarationStatement out of it. // until we can decide whether to hand this over to ExpressionStatement or create a
ASTPointer<PrimaryExpression> primary; // VariableDeclarationStatement out of it.
vector<ASTPointer<PrimaryExpression>> path;
bool startedWithElementary = false;
if (m_scanner->currentToken() == Token::Identifier) if (m_scanner->currentToken() == Token::Identifier)
primary = parseIdentifier(); path.push_back(parseIdentifier());
else else
{ {
primary = ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(m_scanner->currentToken()); startedWithElementary = true;
path.push_back(ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(m_scanner->currentToken()));
m_scanner->next(); m_scanner->next();
} }
while (!startedWithElementary && m_scanner->currentToken() == Token::Period)
{
m_scanner->next();
path.push_back(parseIdentifier());
}
vector<pair<ASTPointer<Expression>, SourceLocation>> indices; vector<pair<ASTPointer<Expression>, SourceLocation>> indices;
solAssert(m_scanner->currentToken() == Token::LBrack, ""); while (m_scanner->currentToken() == Token::LBrack)
SourceLocation indexLocation = primary->location();
do
{ {
expectToken(Token::LBrack); expectToken(Token::LBrack);
ASTPointer<Expression> index; ASTPointer<Expression> index;
if (m_scanner->currentToken() != Token::RBrack) if (m_scanner->currentToken() != Token::RBrack)
index = parseExpression(); index = parseExpression();
SourceLocation indexLocation = path.front()->location();
indexLocation.end = endPosition(); indexLocation.end = endPosition();
indices.push_back(make_pair(index, indexLocation)); indices.push_back(make_pair(index, indexLocation));
expectToken(Token::RBrack); expectToken(Token::RBrack);
} }
while (m_scanner->currentToken() == Token::LBrack);
if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken())) if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken()))
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices)); return parseVariableDeclarationStatement(typeNameIndexAccessStructure(path, indices));
else else
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices)); return parseExpressionStatement(expressionFromIndexAccessStructure(path, indices));
} }
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement( ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
@ -1090,7 +1100,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const
// We have a variable declaration if we get a keyword that specifies a type name. // We have a variable declaration if we get a keyword that specifies a type name.
// If it is an identifier or an elementary type name followed by an identifier, we also have // If it is an identifier or an elementary type name followed by an identifier, we also have
// a variable declaration. // a variable declaration.
// If we get an identifier followed by a "[", it can be both ("type[9] a;" or "arr[9] = 7;"). // If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;").
// In all other cases, we have an expression statement. // In all other cases, we have an expression statement.
Token::Value token(m_scanner->currentToken()); Token::Value token(m_scanner->currentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
@ -1102,25 +1112,36 @@ Parser::LookAheadInfo Parser::peekStatementType() const
Token::Value next = m_scanner->peekNextToken(); Token::Value next = m_scanner->peekNextToken();
if (next == Token::Identifier || Token::isLocationSpecifier(next)) if (next == Token::Identifier || Token::isLocationSpecifier(next))
return LookAheadInfo::VariableDeclarationStatement; return LookAheadInfo::VariableDeclarationStatement;
if (m_scanner->peekNextToken() == Token::LBrack) if (next == Token::LBrack || next == Token::Period)
return LookAheadInfo::IndexAccessStructure; return LookAheadInfo::IndexAccessStructure;
} }
return LookAheadInfo::ExpressionStatement; return LookAheadInfo::ExpressionStatement;
} }
ASTPointer<TypeName> Parser::typeNameIndexAccessStructure( ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<ASTPointer<PrimaryExpression>> const& _path,
vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
) )
{ {
ASTNodeFactory nodeFactory(*this, _primary); solAssert(!_path.empty(), "");
ASTNodeFactory nodeFactory(*this);
SourceLocation location = _path.front()->location();
location.end = _path.back()->location().end;
nodeFactory.setLocation(location);
ASTPointer<TypeName> type; ASTPointer<TypeName> type;
if (auto identifier = dynamic_cast<Identifier const*>(_primary.get())) if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_path.front().get()))
type = nodeFactory.createNode<UserDefinedTypeName>(vector<ASTString>{identifier->name()}); {
else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get())) solAssert(_path.size() == 1, "");
type = nodeFactory.createNode<ElementaryTypeName>(typeName->typeToken()); type = nodeFactory.createNode<ElementaryTypeName>(typeName->typeToken());
}
else else
solAssert(false, "Invalid type name for array look-ahead."); {
vector<ASTString> path;
for (auto const& el: _path)
path.push_back(dynamic_cast<Identifier const&>(*el).name());
type = nodeFactory.createNode<UserDefinedTypeName>(path);
}
for (auto const& lengthExpression: _indices) for (auto const& lengthExpression: _indices)
{ {
nodeFactory.setLocation(lengthExpression.second); nodeFactory.setLocation(lengthExpression.second);
@ -1130,12 +1151,24 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
} }
ASTPointer<Expression> Parser::expressionFromIndexAccessStructure( ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<ASTPointer<PrimaryExpression>> const& _path,
vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
) )
{ {
ASTNodeFactory nodeFactory(*this, _primary); solAssert(!_path.empty(), "");
ASTPointer<Expression> expression(_primary); ASTNodeFactory nodeFactory(*this, _path.front());
ASTPointer<Expression> expression(_path.front());
for (size_t i = 1; i < _path.size(); ++i)
{
SourceLocation location(_path.front()->location());
location.end = _path[i]->location().end;
nodeFactory.setLocation(location);
Identifier const& identifier = dynamic_cast<Identifier const&>(*_path[i]);
expression = nodeFactory.createNode<MemberAccess>(
expression,
make_shared<ASTString>(identifier.name())
);
}
for (auto const& index: _indices) for (auto const& index: _indices)
{ {
nodeFactory.setLocation(index.second); nodeFactory.setLocation(index.second);

View File

@ -125,14 +125,14 @@ private:
/// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to /// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to
/// decide with constant look-ahead. /// decide with constant look-ahead.
LookAheadInfo peekStatementType() const; LookAheadInfo peekStatementType() const;
/// Returns a typename parsed in look-ahead fashion from something like "a[8][2**70]". /// Returns a typename parsed in look-ahead fashion from something like "a.b[8][2**70]".
ASTPointer<TypeName> typeNameIndexAccessStructure( ASTPointer<TypeName> typeNameIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, std::vector<ASTPointer<PrimaryExpression>> const& _path,
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
); );
/// Returns an expression parsed in look-ahead fashion from something like "a[8][2**70]". /// Returns an expression parsed in look-ahead fashion from something like "a.b[8][2**70]".
ASTPointer<Expression> expressionFromIndexAccessStructure( ASTPointer<Expression> expressionFromIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, std::vector<ASTPointer<PrimaryExpression>> const& _path,
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
); );
/// If current token value is not _value, throw exception otherwise advance token. /// If current token value is not _value, throw exception otherwise advance token.

View File

@ -2505,6 +2505,27 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6)
BOOST_CHECK(expectError(text) == Error::Type::TypeError); BOOST_CHECK(expectError(text) == Error::Type::TypeError);
} }
BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
{
char const* text = R"(
contract C {
struct R { uint[10][10] y; }
struct S { uint a; uint b; uint[20][20][20] c; R d; }
S data;
function f() {
C.S x = data;
C.S memory y;
C.S[10] memory z;
C.S[10];
y.a = 2;
x.c[1][2][3] = 9;
x.d.y[2][2] = 3;
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -1015,6 +1015,24 @@ BOOST_AUTO_TEST_CASE(tuples)
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
} }
BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
{
char const* text = R"(
contract C {
struct S { uint a; uint b; uint[][][] c; }
function f() {
C.S x;
C.S memory y;
C.S[10] memory z;
C.S[10](x);
x.a = 2;
x.c[1][2][3] = 9;
}
}
)";
BOOST_CHECK(successParse(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }