diff --git a/boot/dependency.cc b/boot/dependency.cc index 88e9bd5..6b10611 100644 --- a/boot/dependency.cc +++ b/boot/dependency.cc @@ -69,7 +69,7 @@ namespace elna::boot { return std::move(name_analyser.errors()); } - type_analysis_visitor type_analyzer(path); + type_analysis_visitor type_analyzer(path, bag); tree->accept(&type_analyzer); if (type_analyzer.has_errors()) diff --git a/boot/result.cc b/boot/result.cc index fe55410..dab82f4 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -48,4 +48,20 @@ namespace elna::boot { return !m_errors.empty(); } + + bool identifier_definition::operator==(const identifier_definition& that) const + { + return *this == that.name; + } + + bool identifier_definition::operator==(const std::string& that) const + { + return this->name == that; + } +} + +std::size_t std::hash::operator()( + const elna::boot::identifier_definition& key) const +{ + return std::hash{}(key.name); } diff --git a/boot/semantic.cc b/boot/semantic.cc index e6dd248..b343f56 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -93,8 +93,8 @@ namespace elna::boot return "Only one variable can be initialized"; } - type_analysis_visitor::type_analysis_visitor(const char *path) - : error_container(path) + type_analysis_visitor::type_analysis_visitor(const char *path, symbol_bag bag) + : error_container(path), bag(bag) { } @@ -113,7 +113,7 @@ namespace elna::boot } if (!this->returns) { - add_error(definition->identifier.identifier, this->input_file, definition->position()); + add_error(definition->identifier.name, this->input_file, definition->position()); } } } @@ -126,10 +126,6 @@ namespace elna::boot { } - void type_analysis_visitor::visit(import_declaration *) - { - } - void type_analysis_visitor::visit(while_statement *) { } @@ -151,8 +147,28 @@ namespace elna::boot { } + bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr alias, + std::vector& alias_path) + { + if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path)) + { + return false; + } + alias_path.push_back(alias->name); + + if (auto another_alias = alias->reference.get()) + { + return check_unresolved_symbol(another_alias, alias_path); + } + return true; + } + void type_analysis_visitor::visit(unit *unit) { + for (type_declaration *const type : unit->types) + { + type->accept(this); + } for (procedure_declaration *const procedure : unit->procedures) { this->returns = false; @@ -160,6 +176,17 @@ namespace elna::boot } } + void type_analysis_visitor::visit(type_declaration *definition) + { + std::vector alias_path; + auto unresolved_type = this->bag.lookup(definition->identifier.name)->is_type()->symbol.get(); + + if (!check_unresolved_symbol(unresolved_type, alias_path)) + { + add_error(alias_path, this->input_file, definition->position()); + } + } + name_analysis_visitor::name_analysis_visitor(const char *path, symbol_bag bag) : error_container(path), bag(bag) { @@ -205,18 +232,20 @@ namespace elna::boot void name_analysis_visitor::visit(type_declaration *definition) { definition->body().accept(this); - auto unresolved_declaration = this->bag.unresolved.at(definition->identifier.identifier); + auto resolved = this->bag.resolve(definition->identifier.name, this->current_type); + auto info = std::make_shared(type(resolved)); - unresolved_declaration->reference = this->current_type; + info->exported = definition->identifier.exported; + this->bag.enter(definition->identifier.name, info); } void name_analysis_visitor::visit(named_type_expression *type_expression) { - auto unresolved_alias = this->bag.unresolved.find(type_expression->name); + auto unresolved_alias = this->bag.declared(type_expression->name); - if (unresolved_alias != this->bag.unresolved.end()) + if (unresolved_alias != nullptr) { - this->current_type = type(unresolved_alias->second); + this->current_type = type(unresolved_alias); } else if (auto from_symbol_table = this->bag.lookup(type_expression->name)) { @@ -301,15 +330,24 @@ namespace elna::boot for (const auto& variable_identifier : declaration->identifiers) { - this->bag.enter(variable_identifier.identifier, std::make_shared(this->current_type)); + auto variable_symbol = std::make_shared(this->current_type, declaration->is_extern); + + variable_symbol->exported = variable_identifier.exported; + if (!this->bag.enter(variable_identifier.name, variable_symbol)) + { + add_error(variable_identifier.name, this->input_file, + declaration->position()); + } } } void name_analysis_visitor::visit(constant_declaration *definition) { definition->body().accept(this); + auto constant_symbol = std::make_shared(this->current_literal); - this->bag.enter(definition->identifier.identifier, std::make_shared(this->current_literal)); + constant_symbol->exported = definition->identifier.exported; + this->bag.enter(definition->identifier.name, constant_symbol); } void name_analysis_visitor::visit(procedure_declaration *definition) @@ -339,7 +377,8 @@ namespace elna::boot { info = std::make_shared(heading, definition->parameter_names); } - this->bag.enter(definition->identifier.identifier, info); + info->exported = definition->identifier.exported; + this->bag.enter(definition->identifier.name, info); } void name_analysis_visitor::visit(assign_statement *statement) @@ -440,42 +479,12 @@ namespace elna::boot } } - bool name_analysis_visitor::check_unresolved_symbol(std::shared_ptr alias, - std::vector& path) - { - if (std::find(std::cbegin(path), std::cend(path), alias->name) != std::cend(path)) - { - return false; - } - path.push_back(alias->name); - - if (auto another_alias = alias->reference.get()) - { - return check_unresolved_symbol(another_alias, path); - } - return true; - } - void name_analysis_visitor::visit(unit *unit) { for (type_declaration *const type : unit->types) { type->accept(this); } - for (auto& unresolved : this->bag.unresolved) - { - std::vector path; - - if (check_unresolved_symbol(unresolved.second, path)) - { - auto info = std::make_shared(type_info(type(unresolved.second))); - this->bag.enter(unresolved.first, info); - } - else - { - add_error(path, this->input_file, position{ 0, 0 }); - } - } for (variable_declaration *const variable : unit->variables) { variable->accept(this); @@ -590,12 +599,7 @@ namespace elna::boot } for (type_declaration *const type : unit->types) { - const std::string& type_identifier = type->identifier.identifier; - - if (!this->unresolved.insert({ type_identifier, std::make_shared(type_identifier) }).second) - { - add_error(type->identifier.identifier, this->input_file, type->position()); - } + type->accept(this); } for (variable_declaration *const variable : unit->variables) { @@ -607,6 +611,17 @@ namespace elna::boot } } + void declaration_visitor::visit(type_declaration *definition) + { + const std::string& type_identifier = definition->identifier.name; + + if (!this->unresolved.insert({ type_identifier, std::make_shared(type_identifier) }).second) + { + add_error(definition->identifier.name, this->input_file, + definition->position()); + } + } + void declaration_visitor::visit(variable_declaration *declaration) { if (declaration->has_initializer() && declaration->identifiers.size() > 1) diff --git a/boot/symbol.cc b/boot/symbol.cc index 1651cbb..902d331 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -314,7 +314,7 @@ namespace elna::boot procedure_info::procedure_info(const procedure_type symbol, const std::vector names, std::shared_ptr scope) - : symbol(symbol), names(names), symbols(scope) + : symbol(symbol), names(names), scope(scope) { } @@ -323,6 +323,11 @@ namespace elna::boot return std::static_pointer_cast(shared_from_this()); } + bool procedure_info::is_extern() const + { + return this->scope == nullptr; + } + constant_info::constant_info(const variant& symbol) : symbol(symbol) { @@ -333,8 +338,8 @@ namespace elna::boot return std::static_pointer_cast(shared_from_this()); } - variable_info::variable_info(const type symbol) - : symbol(symbol) + variable_info::variable_info(const type symbol, bool is_extern) + : symbol(symbol), is_extern(is_extern) { } @@ -358,14 +363,10 @@ namespace elna::boot return result; } - symbol_bag::symbol_bag() - { - this->symbols = std::make_shared(); - } - - symbol_bag::symbol_bag(forward_table&& unresolved, std::shared_ptr symbols) - : symbols(symbols), unresolved(unresolved) + symbol_bag::symbol_bag(forward_table&& unresolved, std::shared_ptr global_table) + : unresolved(unresolved) { + this->symbols = std::make_shared(global_table); } std::shared_ptr symbol_bag::lookup(const std::string& name) @@ -396,18 +397,31 @@ namespace elna::boot this->symbols = child; } - void symbol_bag::leave() + std::shared_ptr symbol_bag::leave() { - this->symbols = this->symbols->scope(); + std::shared_ptr result = this->symbols; + + this->symbols = result->scope(); + return result; } - void symbol_bag::add_import(std::shared_ptr table) + std::shared_ptr symbol_bag::declared(const std::string& symbol_name) { - this->imports.push_front(table); + auto unresolved_alias = this->unresolved.find(symbol_name); + + return unresolved_alias == this->unresolved.end() ? std::shared_ptr() : unresolved_alias->second; + } + + std::shared_ptr symbol_bag::resolve(const std::string& symbol_name, type& resolution) + { + auto unresolved_declaration = this->unresolved.at(symbol_name); + + unresolved_declaration->reference = resolution; + return unresolved_declaration; } void symbol_bag::add_import(const symbol_bag& bag) { - add_import(bag.symbols); + this->imports.push_front(bag.symbols); } } diff --git a/gcc/elna-builtins.cc b/gcc/elna-builtins.cc index 232cf28..a121bdc 100644 --- a/gcc/elna-builtins.cc +++ b/gcc/elna-builtins.cc @@ -87,7 +87,7 @@ namespace elna::gcc tree definition_tree = build_decl(UNKNOWN_LOCATION, TYPE_DECL, get_identifier(identifier.c_str()), type); - TREE_PUBLIC(definition_tree) = true; + TREE_PUBLIC(definition_tree) = 1; if (is_unique_type(type)) { TYPE_NAME(type) = DECL_NAME(definition_tree); @@ -235,7 +235,21 @@ namespace elna::gcc } DECL_ARGUMENTS(fndecl) = argument_chain; TREE_ADDRESSABLE(fndecl) = 1; - DECL_EXTERNAL(fndecl) = info.symbols == nullptr; + DECL_EXTERNAL(fndecl) = info.is_extern(); + } + + tree declare_variable(const std::string& name, const boot::variable_info& info, + std::shared_ptr symbols) + { + auto variable_type = get_inner_alias(info.symbol, symbols); + tree declaration_tree = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(name.c_str()), variable_type); + + TREE_ADDRESSABLE(declaration_tree) = 1; + DECL_EXTERNAL(declaration_tree) = info.is_extern; + + symbols->enter(name, declaration_tree); + + return declaration_tree; } void rewrite_symbol_table(std::shared_ptr info_table, std::shared_ptr symbols) @@ -250,6 +264,10 @@ namespace elna::gcc handle_symbol(symbol_name, alias_type, symbols); } } + else if (auto variable_info = symbol_info->is_variable()) + { + declare_variable(symbol_name, *variable_info, symbols); + } else if (auto procedure_info = symbol_info->is_procedure()) { declare_procedure(symbol_name, *procedure_info, symbols); diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index 5472061..72f67eb 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -311,7 +311,7 @@ namespace elna::gcc void generic_visitor::visit(boot::procedure_declaration *definition) { - tree fndecl = this->symbols->lookup(definition->identifier.identifier); + tree fndecl = this->symbols->lookup(definition->identifier.name); TREE_PUBLIC(fndecl) = definition->identifier.exported; if (!definition->body.has_value()) @@ -323,7 +323,7 @@ namespace elna::gcc DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc(); enter_scope(); - this->bag.enter(this->bag.lookup(definition->identifier.identifier)->is_procedure()->symbols); + this->bag.enter(this->bag.lookup(definition->identifier.name)->is_procedure()->scope); tree argument_chain = DECL_ARGUMENTS(fndecl); for (; argument_chain != NULL_TREE; argument_chain = TREE_CHAIN(argument_chain)) @@ -713,8 +713,8 @@ namespace elna::gcc return; } tree definition_tree = build_decl(definition_location, CONST_DECL, - get_identifier(definition->identifier.identifier.c_str()), TREE_TYPE(this->current_expression)); - auto result = this->symbols->enter(definition->identifier.identifier, definition_tree); + get_identifier(definition->identifier.name.c_str()), TREE_TYPE(this->current_expression)); + auto result = this->symbols->enter(definition->identifier.name, definition_tree); if (result) { @@ -733,29 +733,29 @@ namespace elna::gcc else { error_at(definition_location, "Variable '%s' already declared in this scope", - definition->identifier.identifier.c_str()); + definition->identifier.name.c_str()); } this->current_expression = NULL_TREE; } void generic_visitor::visit(boot::type_declaration *declaration) { - TREE_PUBLIC(this->symbols->lookup(declaration->identifier.identifier)) = declaration->identifier.exported; + TREE_PUBLIC(this->symbols->lookup(declaration->identifier.name)) = declaration->identifier.exported; } void generic_visitor::visit(boot::variable_declaration *declaration) { for (const auto& variable_identifier : declaration->identifiers) { - this->current_expression = get_inner_alias( - this->bag.lookup(variable_identifier.identifier)->is_variable()->symbol, - this->symbols); - location_t declaration_location = get_location(&declaration->position()); - tree declaration_tree = build_decl(declaration_location, VAR_DECL, - get_identifier(variable_identifier.identifier.c_str()), this->current_expression); - bool result = this->symbols->enter(variable_identifier.identifier, declaration_tree); + tree declaration_tree = this->symbols->lookup(variable_identifier.name); + if (declaration_tree == NULL_TREE) + { + auto variable_symbol = this->bag.lookup(variable_identifier.name)->is_variable(); + + declaration_tree = declare_variable(variable_identifier.name, *variable_symbol, this->symbols); + } // Set initializer if given. if (declaration->body != nullptr) { @@ -771,19 +771,14 @@ namespace elna::gcc print_type(TREE_TYPE(this->current_expression)).c_str()); } } - else if (POINTER_TYPE_P(this->current_expression)) + else if (!declaration->is_extern && POINTER_TYPE_P(TREE_TYPE(declaration_tree))) { DECL_INITIAL(declaration_tree) = elna_pointer_nil_node; } - DECL_EXTERNAL(declaration_tree) = declaration->is_extern; TREE_PUBLIC(declaration_tree) = variable_identifier.exported; this->current_expression = NULL_TREE; - if (!result) - { - error_at(declaration_location, "Variable '%s' already declared in this scope", - variable_identifier.identifier.c_str()); - } - else if (lang_hooks.decls.global_bindings_p()) + + if (lang_hooks.decls.global_bindings_p()) { TREE_STATIC(declaration_tree) = 1; varpool_node::get_create(declaration_tree); diff --git a/gcc/elna1.cc b/gcc/elna1.cc index 017f30a..cb643c3 100644 --- a/gcc/elna1.cc +++ b/gcc/elna1.cc @@ -66,7 +66,6 @@ using dependency_state = elna::boot::dependency_state(state.globals); std::ifstream entry_point{ filename, std::ios::in }; if (!entry_point) @@ -80,7 +79,7 @@ static elna::boot::dependency elna_parse_file(dependency_state& state, const cha { elna::gcc::report_errors(outcome.errors()); } - elna::boot::symbol_bag outcome_bag = elna::boot::symbol_bag{ std::move(outcome.unresolved), module_table }; + elna::boot::symbol_bag outcome_bag = elna::boot::symbol_bag{ std::move(outcome.unresolved), state.globals }; for (const auto& sub_tree : outcome.tree->imports) { @@ -102,7 +101,7 @@ static elna::boot::dependency elna_parse_file(dependency_state& state, const cha elna::gcc::report_errors(semantic_errors); } state.cache.insert({ filename, outcome_bag }); - elna::gcc::rewrite_symbol_table(module_table, state.custom); + elna::gcc::rewrite_symbol_table(outcome_bag.leave(), state.custom); linemap_add(line_table, LC_LEAVE, 0, NULL, 0); return outcome; diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index ad531f4..f67575a 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -217,12 +217,6 @@ namespace elna::boot virtual literal_expression *is_literal(); }; - struct identifier_definition - { - std::string identifier; - bool exported; - }; - /** * Symbol definition. */ diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h index 962578f..98d65c5 100644 --- a/include/elna/boot/dependency.h +++ b/include/elna/boot/dependency.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include #include "elna/boot/result.h" #include "elna/boot/ast.h" +#include "elna/boot/symbol.h" namespace elna::boot { diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index 3b2c26d..9fc1849 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -106,4 +106,19 @@ namespace elna::boot T proper_type{}; bool no_return{ false }; }; + + struct identifier_definition + { + std::string name; + bool exported; + + bool operator==(const identifier_definition& that) const; + bool operator==(const std::string& that) const; + }; } + +template<> +struct std::hash +{ + std::size_t operator()(const elna::boot::identifier_definition& key) const noexcept; +}; diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index 6648d60..c2a485f 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -93,22 +93,26 @@ namespace elna::boot class type_analysis_visitor final : public empty_visitor, public error_container { bool returns; + symbol_bag bag; + + bool check_unresolved_symbol(std::shared_ptr alias, + std::vector& path); public: - explicit type_analysis_visitor(const char *path); + explicit type_analysis_visitor(const char *path, symbol_bag bag); void visit(program *program) override; void visit(procedure_declaration *definition) override; void visit(assign_statement *) override; void visit(if_statement *) override; - void visit(import_declaration *) override; void visit(while_statement *) override; void visit(return_statement *) override; void visit(defer_statement *) override; void visit(case_statement *) override; void visit(procedure_call *) override; void visit(unit *unit) override; + void visit(type_declaration *definition) override; }; /** @@ -124,9 +128,6 @@ namespace elna::boot procedure_type build_procedure(procedure_type_expression& type_expression); std::vector build_composite_type(const std::vector& fields); - bool check_unresolved_symbol(std::shared_ptr alias, - std::vector& path); - public: name_analysis_visitor(const char *path, symbol_bag bag); @@ -175,13 +176,14 @@ namespace elna::boot class declaration_visitor final : public empty_visitor, public error_container { public: - std::unordered_map> unresolved; + forward_table unresolved; explicit declaration_visitor(const char *path); void visit(program *program) override; void visit(import_declaration *) override; void visit(unit *unit) override; + void visit(type_declaration *definition) override; void visit(variable_declaration *declaration) override; void visit(procedure_declaration *definition) override; }; diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index 9a7b24c..5ef917e 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -161,6 +161,8 @@ namespace elna::boot class info : public std::enable_shared_from_this { public: + bool exported{ false }; + virtual ~info() = 0; virtual std::shared_ptr is_type(); @@ -231,6 +233,7 @@ namespace elna::boot * can not be found. * * \param name Symbol name. + * * \return Symbol from the table if found. */ symbol_ptr lookup(const std::string& name) @@ -250,6 +253,7 @@ namespace elna::boot /** * \param name Symbol name. + * * \return Whether the table contains a symbol with the given name. */ bool contains(const std::string& name) @@ -293,16 +297,37 @@ namespace elna::boot std::shared_ptr is_type() override; }; + /** + * Procedure symbol information. + */ class procedure_info : public info { public: + /// Procedure type. const procedure_type symbol; - const std::vector names; - std::shared_ptr symbols; + /// Parameter names. + const std::vector names; + + /// Local definitions. + std::shared_ptr scope; + + /** + * Constructs procedure symbol information. + * + * \param symbol Procedure type. + * \param names Parameter names. + * \param scope Local definition (is `nullptr` for extern symbols). + */ procedure_info(const procedure_type symbol, const std::vector names, std::shared_ptr scope = nullptr); + std::shared_ptr is_procedure() override; + + /** + * \return Whether this is an extern symbol. + */ + bool is_extern() const; }; class constant_info : public info @@ -317,35 +342,116 @@ namespace elna::boot std::shared_ptr is_constant() override; }; + /** + * Variable symbol information. + */ class variable_info : public info { public: + /// Variable type. const type symbol; - variable_info(const type symbol); + /// Whether this is an extern symbol. + const bool is_extern; + + /** + * Constructs a variable symbol information. + * + * \param symbol Variable type. + * \param is_extern Whether this is an extern symbol. + */ + variable_info(const type symbol, bool is_extern); + std::shared_ptr is_variable() override; }; std::shared_ptr builtin_symbol_table(); + /** + * Symbol bag contains: + * + * - the symbol table of a module itself + * - symbol tables of imported modules + * - forward declarations + */ class symbol_bag { std::shared_ptr symbols; std::forward_list> imports; - - public: forward_table unresolved; - symbol_bag(); - symbol_bag(forward_table&& unresolved, std::shared_ptr symbols); + public: + /** + * \param unresolved Forward declarations collected in the previous step. + * \param global_table Global symbols. + */ + symbol_bag(forward_table&& unresolved, std::shared_ptr global_table); + + /** + * Looks up a symbol in the current and imported modules. + * + * \param name Symbol name to look up. + * + * \return Symbol from one of the symbol tables if found. + */ std::shared_ptr lookup(const std::string& name); - bool enter(const std::string& name, std::shared_ptr entry); - std::shared_ptr enter(); - void enter(std::shared_ptr child); - void leave(); - void add_import(std::shared_ptr table); + /** + * Inserts a symbol into the current scope. + * + * \param name Symbol name. + * \param entry Symbol info. + * + * \return Whether the insertion took place. + */ + bool enter(const std::string& name, std::shared_ptr entry); + + /** + * Enters a new scope. + * + * \return Reference to the allocated scope. + */ + std::shared_ptr enter(); + + /** + * Sets the current scope to \a child. + * + * \param child New scope. + */ + void enter(std::shared_ptr child); + + /** + * Leave the current scope. + * + * \return Left scope. + */ + std::shared_ptr leave(); + + /** + * Checks whether there is a forward declaration \a symbol_name and + * returns it if so. + * + * \param symbol_name Type name to look up. + * \return Forward declaration or `nullptr` if the symbol is not declared. + */ + std::shared_ptr declared(const std::string& symbol_name); + + /** + * Completes the forward-declared type \a symbol_name and defines it to + * be \a resolution. + * + * \param symbol_name Type name. + * \param resolution Type definition. + * \return Alias to the defined type. + */ + std::shared_ptr resolve(const std::string& symbol_name, type& resolution); + + /** + * Add imported symbols to the scope. + * + * \param bag Symbol bag of another module. + */ void add_import(const symbol_bag& bag); }; } diff --git a/include/elna/gcc/elna-builtins.h b/include/elna/gcc/elna-builtins.h index 1fe11e4..0cdf519 100644 --- a/include/elna/gcc/elna-builtins.h +++ b/include/elna/gcc/elna-builtins.h @@ -36,4 +36,6 @@ namespace elna::gcc tree get_inner_alias(const boot::type& type, std::shared_ptr symbols); void declare_procedure(const std::string& name, const boot::procedure_info& info, std::shared_ptr symbols); + tree declare_variable(const std::string& name, const boot::variable_info& info, + std::shared_ptr symbols); }