mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #304 from chriseth/imp
Simple aliasing during import
This commit is contained in:
commit
938ed70935
@ -46,7 +46,8 @@ NameAndTypeResolver::NameAndTypeResolver(
|
|||||||
|
|
||||||
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
|
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
|
||||||
{
|
{
|
||||||
solAssert(!m_scopes[&_sourceUnit], "");
|
if (!m_scopes[&_sourceUnit])
|
||||||
|
// By importing, it is possible that the container already exists.
|
||||||
m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
|
m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
|
||||||
m_currentScope = m_scopes[&_sourceUnit].get();
|
m_currentScope = m_scopes[&_sourceUnit].get();
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors);
|
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors);
|
||||||
|
_sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
catch (FatalError const&)
|
||||||
{
|
{
|
||||||
@ -78,12 +80,12 @@ 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;
|
||||||
}
|
}
|
||||||
else
|
else if (imp->name().empty())
|
||||||
{
|
{
|
||||||
auto scope = m_scopes.find(_sourceUnits.at(path));
|
auto scope = m_scopes.find(_sourceUnits.at(path));
|
||||||
solAssert(scope != end(m_scopes), "");
|
solAssert(scope != end(m_scopes), "");
|
||||||
@ -380,7 +382,7 @@ void NameAndTypeResolver::reportFatalTypeError(Error const& _e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
||||||
map<ASTNode const*, unique_ptr<DeclarationContainer>>& _scopes,
|
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
|
||||||
ASTNode& _astRoot,
|
ASTNode& _astRoot,
|
||||||
ErrorList& _errors
|
ErrorList& _errors
|
||||||
):
|
):
|
||||||
@ -392,6 +394,17 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
|||||||
_astRoot.accept(*this);
|
_astRoot.accept(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
|
||||||
|
{
|
||||||
|
SourceUnit const* importee = _import.annotation().sourceUnit;
|
||||||
|
solAssert(!!importee, "");
|
||||||
|
if (!m_scopes[importee])
|
||||||
|
m_scopes[importee].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
|
||||||
|
m_scopes[&_import] = m_scopes[importee];
|
||||||
|
registerDeclaration(_import, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
|
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
|
||||||
{
|
{
|
||||||
registerDeclaration(_contract, true);
|
registerDeclaration(_contract, true);
|
||||||
@ -489,9 +502,9 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&)
|
|||||||
|
|
||||||
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
|
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
|
||||||
{
|
{
|
||||||
map<ASTNode const*, unique_ptr<DeclarationContainer>>::iterator iter;
|
map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
|
||||||
bool newlyAdded;
|
bool newlyAdded;
|
||||||
unique_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
|
shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
|
||||||
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container));
|
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container));
|
||||||
solAssert(newlyAdded, "Unable to add new scope.");
|
solAssert(newlyAdded, "Unable to add new scope.");
|
||||||
m_currentScope = &_declaration;
|
m_currentScope = &_declaration;
|
||||||
|
@ -111,7 +111,8 @@ private:
|
|||||||
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
|
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
|
||||||
/// where nullptr denotes the global scope. Note that structs are not scope since they do
|
/// where nullptr denotes the global scope. Note that structs are not scope since they do
|
||||||
/// not contain code.
|
/// not contain code.
|
||||||
std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>> m_scopes;
|
/// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
|
||||||
|
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
|
||||||
|
|
||||||
DeclarationContainer* m_currentScope = nullptr;
|
DeclarationContainer* m_currentScope = nullptr;
|
||||||
ErrorList& m_errors;
|
ErrorList& m_errors;
|
||||||
@ -125,12 +126,13 @@ class DeclarationRegistrationHelper: private ASTVisitor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DeclarationRegistrationHelper(
|
DeclarationRegistrationHelper(
|
||||||
std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>>& _scopes,
|
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
|
||||||
ASTNode& _astRoot,
|
ASTNode& _astRoot,
|
||||||
ErrorList& _errors
|
ErrorList& _errors
|
||||||
);
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool visit(ImportDirective& _declaration) override;
|
||||||
bool visit(ContractDefinition& _contract) override;
|
bool visit(ContractDefinition& _contract) override;
|
||||||
void endVisit(ContractDefinition& _contract) override;
|
void endVisit(ContractDefinition& _contract) override;
|
||||||
bool visit(StructDefinition& _struct) override;
|
bool visit(StructDefinition& _struct) override;
|
||||||
@ -166,7 +168,7 @@ private:
|
|||||||
// creates the Declaration error and adds it in the errors list and throws FatalError
|
// creates the Declaration error and adds it in the errors list and throws FatalError
|
||||||
void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
|
void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
|
||||||
|
|
||||||
std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>>& m_scopes;
|
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
|
||||||
ASTNode const* m_currentScope = nullptr;
|
ASTNode const* m_currentScope = nullptr;
|
||||||
VariableScope* m_currentFunction = nullptr;
|
VariableScope* m_currentFunction = nullptr;
|
||||||
ErrorList& m_errors;
|
ErrorList& m_errors;
|
||||||
|
@ -56,6 +56,13 @@ Error ASTNode::createTypeError(string const& _description) const
|
|||||||
return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description);
|
return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SourceUnitAnnotation& SourceUnit::annotation() const
|
||||||
|
{
|
||||||
|
if (!m_annotation)
|
||||||
|
m_annotation = new SourceUnitAnnotation();
|
||||||
|
return static_cast<SourceUnitAnnotation&>(*m_annotation);
|
||||||
|
}
|
||||||
|
|
||||||
ImportAnnotation& ImportDirective::annotation() const
|
ImportAnnotation& ImportDirective::annotation() const
|
||||||
{
|
{
|
||||||
if (!m_annotation)
|
if (!m_annotation)
|
||||||
@ -63,6 +70,12 @@ ImportAnnotation& ImportDirective::annotation() const
|
|||||||
return static_cast<ImportAnnotation&>(*m_annotation);
|
return static_cast<ImportAnnotation&>(*m_annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer ImportDirective::type() const
|
||||||
|
{
|
||||||
|
solAssert(!!annotation().sourceUnit, "");
|
||||||
|
return make_shared<ModuleType>(*annotation().sourceUnit);
|
||||||
|
}
|
||||||
|
|
||||||
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
|
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
|
||||||
{
|
{
|
||||||
auto exportedFunctionList = interfaceFunctionList();
|
auto exportedFunctionList = interfaceFunctionList();
|
||||||
|
@ -120,6 +120,7 @@ public:
|
|||||||
|
|
||||||
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;
|
||||||
|
virtual SourceUnitAnnotation& annotation() const override;
|
||||||
|
|
||||||
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
|
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
|
||||||
|
|
||||||
@ -128,28 +129,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import directive for referencing other files / source objects.
|
* Abstract AST class for a declaration (contract, function, struct, variable, import directive).
|
||||||
* Example: import "abc.sol"
|
|
||||||
* Source objects are identified by a string which can be a file name but does not have to be.
|
|
||||||
*/
|
|
||||||
class ImportDirective: public ASTNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ImportDirective(SourceLocation const& _location, ASTPointer<ASTString> const& _identifier):
|
|
||||||
ASTNode(_location), m_identifier(_identifier) {}
|
|
||||||
|
|
||||||
virtual void accept(ASTVisitor& _visitor) override;
|
|
||||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
|
||||||
|
|
||||||
ASTString const& identifier() const { return *m_identifier; }
|
|
||||||
virtual ImportAnnotation& annotation() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ASTPointer<ASTString> m_identifier;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract AST class for a declaration (contract, function, struct, variable).
|
|
||||||
*/
|
*/
|
||||||
class Declaration: public ASTNode
|
class Declaration: public ASTNode
|
||||||
{
|
{
|
||||||
@ -194,6 +174,45 @@ private:
|
|||||||
ASTNode const* m_scope;
|
ASTNode const* m_scope;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import directive for referencing other files / source objects.
|
||||||
|
* 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.
|
||||||
|
* 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 Declaration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ImportDirective(
|
||||||
|
SourceLocation const& _location,
|
||||||
|
ASTPointer<ASTString> const& _path,
|
||||||
|
ASTPointer<ASTString> const& _unitAlias,
|
||||||
|
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>>&& _symbolAliases
|
||||||
|
):
|
||||||
|
Declaration(_location, _unitAlias),
|
||||||
|
m_path(_path),
|
||||||
|
m_symbolAliases(_symbolAliases)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
|
ASTString const& path() const { return *m_path; }
|
||||||
|
virtual ImportAnnotation& annotation() const override;
|
||||||
|
|
||||||
|
virtual TypePointer type() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ASTPointer<ASTString> m_path;
|
||||||
|
/// 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;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class that is added to each AST node that can store local variables.
|
* Abstract class that is added to each AST node that can store local variables.
|
||||||
*/
|
*/
|
||||||
|
@ -54,10 +54,20 @@ struct DocumentedAnnotation
|
|||||||
std::multimap<std::string, DocTag> docTags;
|
std::multimap<std::string, DocTag> docTags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SourceUnitAnnotation: ASTAnnotation
|
||||||
|
{
|
||||||
|
/// The "absolute" (in the compiler sense) path of this source unit.
|
||||||
|
std::string path;
|
||||||
|
/// The exported symbols (all global symbols).
|
||||||
|
std::map<ASTString, std::vector<Declaration const*>> exportedSymbols;
|
||||||
|
};
|
||||||
|
|
||||||
struct ImportAnnotation: ASTAnnotation
|
struct ImportAnnotation: ASTAnnotation
|
||||||
{
|
{
|
||||||
/// The absolute path of the source unit to import.
|
/// The absolute path of the source unit to import.
|
||||||
std::string absolutePath;
|
std::string absolutePath;
|
||||||
|
/// The actual source unit.
|
||||||
|
SourceUnit const* sourceUnit = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeDeclarationAnnotation: ASTAnnotation
|
struct TypeDeclarationAnnotation: ASTAnnotation
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -1925,9 +1925,25 @@ string ModifierType::toString(bool _short) const
|
|||||||
return name + ")";
|
return name + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
MagicType::MagicType(MagicType::Kind _kind):
|
bool ModuleType::operator==(Type const& _other) const
|
||||||
m_kind(_kind)
|
|
||||||
{
|
{
|
||||||
|
if (_other.category() != category())
|
||||||
|
return false;
|
||||||
|
return &m_sourceUnit == &dynamic_cast<ModuleType const&>(_other).m_sourceUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
|
||||||
|
{
|
||||||
|
MemberList::MemberMap symbols;
|
||||||
|
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
|
||||||
|
for (Declaration const* symbol: symbolName.second)
|
||||||
|
symbols.push_back(MemberList::Member(symbolName.first, symbol->type(), symbol));
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ModuleType::toString(bool) const
|
||||||
|
{
|
||||||
|
return string("module \"") + m_sourceUnit.annotation().path + string("\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagicType::operator==(Type const& _other) const
|
bool MagicType::operator==(Type const& _other) const
|
||||||
|
@ -134,7 +134,7 @@ public:
|
|||||||
{
|
{
|
||||||
Integer, IntegerConstant, StringLiteral, Bool, Real, Array,
|
Integer, IntegerConstant, StringLiteral, Bool, Real, Array,
|
||||||
FixedBytes, Contract, Struct, Function, Enum, Tuple,
|
FixedBytes, Contract, Struct, Function, Enum, Tuple,
|
||||||
Mapping, TypeType, Modifier, Magic
|
Mapping, TypeType, Modifier, Magic, Module
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @{
|
/// @{
|
||||||
@ -969,6 +969,34 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special type for imported modules. These mainly give access to their scope via members.
|
||||||
|
*/
|
||||||
|
class ModuleType: public Type
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Category category() const override { return Category::Module; }
|
||||||
|
|
||||||
|
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
|
||||||
|
|
||||||
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
|
||||||
|
{
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
virtual bool canBeStored() const override { return false; }
|
||||||
|
virtual bool canLiveOutsideStorage() const override { return true; }
|
||||||
|
virtual unsigned sizeOnStack() const override { return 0; }
|
||||||
|
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
|
||||||
|
|
||||||
|
virtual std::string toString(bool _short) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SourceUnit const& m_sourceUnit;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
|
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
|
||||||
* (it always references a global singleton by name).
|
* (it always references a global singleton by name).
|
||||||
@ -979,7 +1007,7 @@ public:
|
|||||||
enum class Kind { Block, Message, Transaction };
|
enum class Kind { Block, Message, Transaction };
|
||||||
virtual Category category() const override { return Category::Magic; }
|
virtual Category category() const override { return Category::Magic; }
|
||||||
|
|
||||||
explicit MagicType(Kind _kind);
|
explicit MagicType(Kind _kind): m_kind(_kind) {}
|
||||||
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
|
||||||
{
|
{
|
||||||
|
@ -111,6 +111,8 @@ bool CompilerStack::parse()
|
|||||||
sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner);
|
sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner);
|
||||||
if (!sourcePair.second.ast)
|
if (!sourcePair.second.ast)
|
||||||
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
|
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
|
||||||
|
else
|
||||||
|
sourcePair.second.ast->annotation().path = sourcePair.first;
|
||||||
sourceUnitsByName[sourcePair.first] = sourcePair.second.ast.get();
|
sourceUnitsByName[sourcePair.first] = sourcePair.second.ast.get();
|
||||||
}
|
}
|
||||||
if (!Error::containsOnlyWarnings(m_errors))
|
if (!Error::containsOnlyWarnings(m_errors))
|
||||||
@ -376,7 +378,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(
|
||||||
@ -384,6 +386,7 @@ void CompilerStack::resolveImports()
|
|||||||
<< errinfo_sourceLocation(import->location())
|
<< errinfo_sourceLocation(import->location())
|
||||||
<< errinfo_comment("Source not found.")
|
<< errinfo_comment("Source not found.")
|
||||||
);
|
);
|
||||||
|
import->annotation().sourceUnit = m_sources.at(path).ast.get();
|
||||||
|
|
||||||
toposort(path, &m_sources[path]);
|
toposort(path, &m_sources[path]);
|
||||||
}
|
}
|
||||||
|
@ -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 = make_shared<string>();
|
||||||
|
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("'")
|
||||||
);
|
);
|
||||||
|
@ -93,6 +93,14 @@ BOOST_AUTO_TEST_CASE(relative_import_multiplex)
|
|||||||
BOOST_CHECK(c.compile());
|
BOOST_CHECK(c.compile());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(simple_alias)
|
||||||
|
{
|
||||||
|
CompilerStack c;
|
||||||
|
c.addSource("a", "contract A {}");
|
||||||
|
c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B { function() { x.A r = x.A(20); } }");
|
||||||
|
BOOST_CHECK(c.compile());
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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"(
|
||||||
|
Loading…
Reference in New Issue
Block a user