Document symbol bag class and methods
This commit is contained in:
		@@ -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())
 | 
			
		||||
 
 | 
			
		||||
@@ -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<elna::boot::identifier_definition>::operator()(
 | 
			
		||||
        const elna::boot::identifier_definition& key) const
 | 
			
		||||
{
 | 
			
		||||
    return std::hash<std::string>{}(key.name);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										117
									
								
								boot/semantic.cc
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								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<return_error>(definition->identifier.identifier, this->input_file, definition->position());
 | 
			
		||||
                add_error<return_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_type> alias,
 | 
			
		||||
            std::vector<std::string>& 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<alias_type>())
 | 
			
		||||
        {
 | 
			
		||||
            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<std::string> alias_path;
 | 
			
		||||
        auto unresolved_type = this->bag.lookup(definition->identifier.name)->is_type()->symbol.get<alias_type>();
 | 
			
		||||
 | 
			
		||||
        if (!check_unresolved_symbol(unresolved_type, alias_path))
 | 
			
		||||
        {
 | 
			
		||||
            add_error<cyclic_declaration_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_info>(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<variable_info>(this->current_type));
 | 
			
		||||
            auto variable_symbol = std::make_shared<variable_info>(this->current_type, declaration->is_extern);
 | 
			
		||||
 | 
			
		||||
            variable_symbol->exported = variable_identifier.exported;
 | 
			
		||||
            if (!this->bag.enter(variable_identifier.name, variable_symbol))
 | 
			
		||||
            {
 | 
			
		||||
                add_error<already_declared_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<constant_info>(this->current_literal);
 | 
			
		||||
 | 
			
		||||
        this->bag.enter(definition->identifier.identifier, std::make_shared<constant_info>(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<procedure_info>(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_type> alias,
 | 
			
		||||
            std::vector<std::string>& 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<alias_type>())
 | 
			
		||||
        {
 | 
			
		||||
            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<std::string> path;
 | 
			
		||||
 | 
			
		||||
            if (check_unresolved_symbol(unresolved.second, path))
 | 
			
		||||
            {
 | 
			
		||||
                auto info = std::make_shared<type_info>(type_info(type(unresolved.second)));
 | 
			
		||||
                this->bag.enter(unresolved.first, info);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                add_error<cyclic_declaration_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<alias_type>(type_identifier) }).second)
 | 
			
		||||
            {
 | 
			
		||||
                add_error<already_declared_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<alias_type>(type_identifier) }).second)
 | 
			
		||||
        {
 | 
			
		||||
            add_error<already_declared_error>(definition->identifier.name, this->input_file,
 | 
			
		||||
                    definition->position());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void declaration_visitor::visit(variable_declaration *declaration)
 | 
			
		||||
    {
 | 
			
		||||
        if (declaration->has_initializer() && declaration->identifiers.size() > 1)
 | 
			
		||||
 
 | 
			
		||||
@@ -314,7 +314,7 @@ namespace elna::boot
 | 
			
		||||
 | 
			
		||||
    procedure_info::procedure_info(const procedure_type symbol, const std::vector<std::string> names,
 | 
			
		||||
            std::shared_ptr<symbol_table> 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<procedure_info>(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<constant_info>(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_table>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    symbol_bag::symbol_bag(forward_table&& unresolved, std::shared_ptr<symbol_table> symbols)
 | 
			
		||||
        : symbols(symbols), unresolved(unresolved)
 | 
			
		||||
    symbol_bag::symbol_bag(forward_table&& unresolved, std::shared_ptr<symbol_table> global_table)
 | 
			
		||||
        : unresolved(unresolved)
 | 
			
		||||
    {
 | 
			
		||||
        this->symbols = std::make_shared<symbol_table>(global_table);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<info> 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_table> symbol_bag::leave()
 | 
			
		||||
    {
 | 
			
		||||
        this->symbols = this->symbols->scope();
 | 
			
		||||
        std::shared_ptr<symbol_table> result = this->symbols;
 | 
			
		||||
 | 
			
		||||
        this->symbols = result->scope();
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void symbol_bag::add_import(std::shared_ptr<symbol_table> table)
 | 
			
		||||
    std::shared_ptr<alias_type> 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<alias_type>() : unresolved_alias->second;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<alias_type> 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<symbol_table> 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<boot::symbol_table> info_table, std::shared_ptr<symbol_table> 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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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<language_function>();
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,6 @@ using dependency_state = elna::boot::dependency_state<std::shared_ptr<elna::gcc:
 | 
			
		||||
 | 
			
		||||
static elna::boot::dependency elna_parse_file(dependency_state& state, const char *filename)
 | 
			
		||||
{
 | 
			
		||||
    auto module_table = std::make_shared<elna::boot::symbol_table>(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;
 | 
			
		||||
 
 | 
			
		||||
@@ -217,12 +217,6 @@ namespace elna::boot
 | 
			
		||||
        virtual literal_expression *is_literal();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct identifier_definition
 | 
			
		||||
    {
 | 
			
		||||
        std::string identifier;
 | 
			
		||||
        bool exported;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Symbol definition.
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include "elna/boot/result.h"
 | 
			
		||||
#include "elna/boot/ast.h"
 | 
			
		||||
#include "elna/boot/symbol.h"
 | 
			
		||||
 | 
			
		||||
namespace elna::boot
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -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<elna::boot::identifier_definition>
 | 
			
		||||
{
 | 
			
		||||
    std::size_t operator()(const elna::boot::identifier_definition& key) const noexcept;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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_type> alias,
 | 
			
		||||
                std::vector<std::string>& 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<type_field> build_composite_type(const std::vector<field_declaration>& fields);
 | 
			
		||||
 | 
			
		||||
        bool check_unresolved_symbol(std::shared_ptr<alias_type> alias,
 | 
			
		||||
                std::vector<std::string>& 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<std::string, std::shared_ptr<alias_type>> 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;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -161,6 +161,8 @@ namespace elna::boot
 | 
			
		||||
    class info : public std::enable_shared_from_this<info>
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        bool exported{ false };
 | 
			
		||||
 | 
			
		||||
        virtual ~info() = 0;
 | 
			
		||||
 | 
			
		||||
        virtual std::shared_ptr<type_info> 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<type_info> is_type() override;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Procedure symbol information.
 | 
			
		||||
     */
 | 
			
		||||
    class procedure_info : public info
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        /// Procedure type.
 | 
			
		||||
        const procedure_type symbol;
 | 
			
		||||
        const std::vector<std::string> names;
 | 
			
		||||
        std::shared_ptr<symbol_table> symbols;
 | 
			
		||||
 | 
			
		||||
        /// Parameter names.
 | 
			
		||||
        const std::vector<std::string> names;
 | 
			
		||||
 | 
			
		||||
        /// Local definitions.
 | 
			
		||||
        std::shared_ptr<symbol_table> 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<std::string> names,
 | 
			
		||||
                std::shared_ptr<symbol_table> scope = nullptr);
 | 
			
		||||
 | 
			
		||||
        std::shared_ptr<procedure_info> 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<constant_info> 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<variable_info> is_variable() override;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<symbol_table> 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<symbol_table> symbols;
 | 
			
		||||
        std::forward_list<std::shared_ptr<symbol_table>> imports;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        forward_table unresolved;
 | 
			
		||||
 | 
			
		||||
        symbol_bag();
 | 
			
		||||
        symbol_bag(forward_table&& unresolved, std::shared_ptr<symbol_table> symbols);
 | 
			
		||||
    public:
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * \param unresolved Forward declarations collected in the previous step.
 | 
			
		||||
         * \param global_table Global symbols.
 | 
			
		||||
         */
 | 
			
		||||
        symbol_bag(forward_table&& unresolved, std::shared_ptr<symbol_table> 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<info> lookup(const std::string& name);
 | 
			
		||||
        bool enter(const std::string& name, std::shared_ptr<info> entry);
 | 
			
		||||
        std::shared_ptr<symbol_table> enter();
 | 
			
		||||
        void enter(std::shared_ptr<symbol_table> child);
 | 
			
		||||
        void leave();
 | 
			
		||||
 | 
			
		||||
        void add_import(std::shared_ptr<symbol_table> 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<info> entry);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Enters a new scope.
 | 
			
		||||
         *
 | 
			
		||||
         * \return Reference to the allocated scope.
 | 
			
		||||
         */
 | 
			
		||||
        std::shared_ptr<symbol_table> enter();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Sets the current scope to \a child.
 | 
			
		||||
         *
 | 
			
		||||
         * \param child New scope.
 | 
			
		||||
         */
 | 
			
		||||
        void enter(std::shared_ptr<symbol_table> child);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Leave the current scope.
 | 
			
		||||
         *
 | 
			
		||||
         * \return Left scope.
 | 
			
		||||
         */
 | 
			
		||||
        std::shared_ptr<symbol_table> 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<alias_type> 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<alias_type> 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);
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,4 +36,6 @@ namespace elna::gcc
 | 
			
		||||
    tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols);
 | 
			
		||||
    void declare_procedure(const std::string& name, const boot::procedure_info& info,
 | 
			
		||||
            std::shared_ptr<symbol_table> symbols);
 | 
			
		||||
    tree declare_variable(const std::string& name, const boot::variable_info& info,
 | 
			
		||||
            std::shared_ptr<symbol_table> symbols);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user