Parse complex import directives.

This commit is contained in:
chriseth 2015-12-14 18:01:40 +01:00
parent fe23cc8226
commit d3c459b5a9
7 changed files with 114 additions and 28 deletions

View File

@ -78,7 +78,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
"Import \"" + "Import \"" +
path + path +
"\" (referenced as \"" + "\" (referenced as \"" +
imp->identifier() + imp->path() +
"\") not found." "\") not found."
); );
error = true; error = true;

View File

@ -129,23 +129,39 @@ private:
/** /**
* Import directive for referencing other files / source objects. * Import directive for referencing other files / source objects.
* Example: import "abc.sol" * Example: import "abc.sol" // imports all symbols of "abc.sol" into current scope
* Source objects are identified by a string which can be a file name but does not have to be. * Source objects are identified by a string which can be a file name but does not have to be.
* Other ways to use it:
* import "abc" as x; // creates symbol "x" that contains all symbols in "abc"
* import * as x from "abc"; // same as above
* import {a as b, c} from "abc"; // creates new symbols "b" and "c" referencing "a" and "c" in "abc", respectively.
*/ */
class ImportDirective: public ASTNode class ImportDirective: public ASTNode
{ {
public: public:
ImportDirective(SourceLocation const& _location, ASTPointer<ASTString> const& _identifier): ImportDirective(
ASTNode(_location), m_identifier(_identifier) {} SourceLocation const& _location,
ASTPointer<ASTString> const& _path,
ASTPointer<ASTString> const& _unitAlias,
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>>&& _symbolAliases
):
ASTNode(_location), m_path(_path), m_unitAlias(_unitAlias), m_symbolAliases(_symbolAliases) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
ASTString const& identifier() const { return *m_identifier; } ASTString const& path() const { return *m_path; }
virtual ImportAnnotation& annotation() const override; virtual ImportAnnotation& annotation() const override;
private: private:
ASTPointer<ASTString> m_identifier; ASTPointer<ASTString> m_path;
/// The alias for the module itself. If present, import the whole unit under that name and
/// ignore m_symbolAlias.
ASTPointer<ASTString> m_unitAlias;
/// The aliases for the specific symbols to import. If non-empty import the specific symbols.
/// If the second component is empty, import the identifier unchanged.
/// If both m_unitAlias and m_symbolAlias are empty, import all symbols into the current scope.
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> m_symbolAliases;
}; };
/** /**

View File

@ -91,7 +91,7 @@ Json::Value const& ASTJsonConverter::json()
bool ASTJsonConverter::visit(ImportDirective const& _node) bool ASTJsonConverter::visit(ImportDirective const& _node)
{ {
addJsonNode("Import", { make_pair("file", _node.identifier())}); addJsonNode("Import", { make_pair("file", _node.path())});
return true; return true;
} }

View File

@ -49,7 +49,7 @@ void ASTPrinter::print(ostream& _stream)
bool ASTPrinter::visit(ImportDirective const& _node) bool ASTPrinter::visit(ImportDirective const& _node)
{ {
writeLine("ImportDirective \"" + _node.identifier() + "\""); writeLine("ImportDirective \"" + _node.path() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }

View File

@ -376,7 +376,7 @@ void CompilerStack::resolveImports()
for (ASTPointer<ASTNode> const& node: _source->ast->nodes()) for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
{ {
string path = absolutePath(import->identifier(), _sourceName); string path = absolutePath(import->path(), _sourceName);
import->annotation().absolutePath = path; import->annotation().absolutePath = path;
if (!m_sources.count(path)) if (!m_sources.count(path))
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(

View File

@ -83,7 +83,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
nodes.push_back(parseContractDefinition(token == Token::Library)); nodes.push_back(parseContractDefinition(token == Token::Library));
break; break;
default: default:
fatalParserError(std::string("Expected import directive or contract definition.")); fatalParserError(string("Expected import directive or contract definition."));
} }
} }
return nodeFactory.createNode<SourceUnit>(nodes); return nodeFactory.createNode<SourceUnit>(nodes);
@ -113,14 +113,65 @@ int Parser::endPosition() const
ASTPointer<ImportDirective> Parser::parseImportDirective() ASTPointer<ImportDirective> Parser::parseImportDirective()
{ {
// import "abc" [as x];
// import * as x from "abc";
// import {a as b, c} from "abc";
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::Import); expectToken(Token::Import);
ASTPointer<ASTString> path;
ASTPointer<ASTString> unitAlias;
vector<pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> symbolAliases;
if (m_scanner->currentToken() == Token::StringLiteral)
{
path = getLiteralAndAdvance();
if (m_scanner->currentToken() == Token::As)
{
m_scanner->next();
unitAlias = expectIdentifierToken();
}
}
else
{
if (m_scanner->currentToken() == Token::LBrace)
{
m_scanner->next();
while (true)
{
ASTPointer<Identifier> id = parseIdentifier();
ASTPointer<ASTString> alias;
if (m_scanner->currentToken() == Token::As)
{
expectToken(Token::As);
alias = expectIdentifierToken();
}
symbolAliases.push_back(move(make_pair(move(id), move(alias))));
if (m_scanner->currentToken() != Token::Comma)
break;
m_scanner->next();
}
expectToken(Token::RBrace);
}
else if (m_scanner->currentToken() == Token::Mul)
{
m_scanner->next();
expectToken(Token::As);
unitAlias = expectIdentifierToken();
}
else
fatalParserError("Expected string literal (path), \"*\" or alias list.");
// "from" is not a keyword but parsed as an identifier because of backwards
// compatibility and because it is a really common word.
if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
fatalParserError("Expected \"from\".");
m_scanner->next();
if (m_scanner->currentToken() != Token::StringLiteral) if (m_scanner->currentToken() != Token::StringLiteral)
fatalParserError(std::string("Expected string literal (URL).")); fatalParserError("Expected import path.");
ASTPointer<ASTString> url = getLiteralAndAdvance(); path = getLiteralAndAdvance();
}
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
return nodeFactory.createNode<ImportDirective>(url); return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
} }
ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary) ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
@ -171,7 +222,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
else if (currentTokenValue == Token::Using) else if (currentTokenValue == Token::Using)
subNodes.push_back(parseUsingDirective()); subNodes.push_back(parseUsingDirective());
else else
fatalParserError(std::string("Function, variable, struct or modifier declaration expected.")); fatalParserError(string("Function, variable, struct or modifier declaration expected."));
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBrace); expectToken(Token::RBrace);
@ -250,7 +301,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
else if (Token::isVisibilitySpecifier(token)) else if (Token::isVisibilitySpecifier(token))
{ {
if (visibility != Declaration::Visibility::Default) if (visibility != Declaration::Visibility::Default)
fatalParserError(std::string("Multiple visibility specifiers.")); fatalParserError(string("Multiple visibility specifiers."));
visibility = parseVisibilitySpecifier(token); visibility = parseVisibilitySpecifier(token);
} }
else else
@ -327,7 +378,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
break; break;
expectToken(Token::Comma); expectToken(Token::Comma);
if (m_scanner->currentToken() != Token::Identifier) if (m_scanner->currentToken() != Token::Identifier)
fatalParserError(std::string("Expected Identifier after ','")); fatalParserError(string("Expected Identifier after ','"));
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -363,7 +414,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token)) if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
{ {
if (visibility != Declaration::Visibility::Default) if (visibility != Declaration::Visibility::Default)
fatalParserError(std::string("Visibility already specified.")); fatalParserError(string("Visibility already specified."));
visibility = parseVisibilitySpecifier(token); visibility = parseVisibilitySpecifier(token);
} }
else else
@ -375,9 +426,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token)) else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{ {
if (location != VariableDeclaration::Location::Default) if (location != VariableDeclaration::Location::Default)
fatalParserError(std::string("Location already specified.")); fatalParserError(string("Location already specified."));
if (!type) if (!type)
fatalParserError(std::string("Location specifier needs explicit type name.")); fatalParserError(string("Location specifier needs explicit type name."));
location = ( location = (
token == Token::Memory ? token == Token::Memory ?
VariableDeclaration::Location::Memory : VariableDeclaration::Location::Memory :
@ -532,7 +583,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
else if (token == Token::Var) else if (token == Token::Var)
{ {
if (!_allowVar) if (!_allowVar)
fatalParserError(std::string("Expected explicit type name.")); fatalParserError(string("Expected explicit type name."));
m_scanner->next(); m_scanner->next();
} }
else if (token == Token::Mapping) else if (token == Token::Mapping)
@ -551,7 +602,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
type = nodeFactory.createNode<UserDefinedTypeName>(identifierPath); type = nodeFactory.createNode<UserDefinedTypeName>(identifierPath);
} }
else else
fatalParserError(std::string("Expected type name")); fatalParserError(string("Expected type name"));
if (type) if (type)
// Parse "[...]" postfixes for arrays. // Parse "[...]" postfixes for arrays.
@ -574,7 +625,7 @@ ASTPointer<Mapping> Parser::parseMapping()
expectToken(Token::Mapping); expectToken(Token::Mapping);
expectToken(Token::LParen); expectToken(Token::LParen);
if (!Token::isElementaryTypeName(m_scanner->currentToken())) if (!Token::isElementaryTypeName(m_scanner->currentToken()))
fatalParserError(std::string("Expected elementary type name for mapping key type")); fatalParserError(string("Expected elementary type name for mapping key type"));
ASTPointer<ElementaryTypeName> keyType; ASTPointer<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->currentToken()); keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->currentToken());
m_scanner->next(); m_scanner->next();
@ -1072,7 +1123,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
m_scanner->next(); m_scanner->next();
} }
else else
fatalParserError(std::string("Expected primary expression.")); fatalParserError(string("Expected primary expression."));
break; break;
} }
return expression; return expression;
@ -1221,8 +1272,7 @@ Token::Value Parser::expectAssignmentOperator()
Token::Value op = m_scanner->currentToken(); Token::Value op = m_scanner->currentToken();
if (!Token::isAssignmentOp(op)) if (!Token::isAssignmentOp(op))
fatalParserError( fatalParserError(
std::string("Expected assignment operator ") + string("Expected assignment operator, got '") +
string(" got '") +
string(Token::name(m_scanner->currentToken())) + string(Token::name(m_scanner->currentToken())) +
string("'") string("'")
); );
@ -1234,8 +1284,7 @@ ASTPointer<ASTString> Parser::expectIdentifierToken()
{ {
if (m_scanner->currentToken() != Token::Identifier) if (m_scanner->currentToken() != Token::Identifier)
fatalParserError( fatalParserError(
std::string("Expected identifier ") + string("Expected identifier, got '") +
string(" got '") +
string(Token::name(m_scanner->currentToken())) + string(Token::name(m_scanner->currentToken())) +
string("'") string("'")
); );

View File

@ -1047,6 +1047,27 @@ BOOST_AUTO_TEST_CASE(using_for)
BOOST_CHECK(successParse(text)); BOOST_CHECK(successParse(text));
} }
BOOST_AUTO_TEST_CASE(complex_import)
{
char const* text = R"(
import "abc" as x;
import * as x from "abc";
import {a as b, c as d, f} from "def";
contract x {}
)";
BOOST_CHECK(successParse(text));
}
BOOST_AUTO_TEST_CASE(from_is_not_keyword)
{
// "from" is not a keyword although it is used as a keyword in import directives.
char const* text = R"(
contract from {
}
)";
BOOST_CHECK(successParse(text));
}
BOOST_AUTO_TEST_CASE(inline_array_declaration) BOOST_AUTO_TEST_CASE(inline_array_declaration)
{ {
char const* text = R"( char const* text = R"(