/* Symbol definitions. Copyright (C) 2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "elna/boot/symbol.h" #include namespace elna::boot { type::type(std::shared_ptr alias) : payload(alias) { } type::type(std::shared_ptr primitive) : payload(primitive) { } type::type(std::shared_ptr record) : payload(record) { } type::type(std::shared_ptr pointer) : payload(pointer) { } type::type(std::shared_ptr constant) : payload(constant) { } type::type(std::shared_ptr array) : payload(array) { } type::type(std::shared_ptr slice) : payload(slice) { } type::type(std::shared_ptr procedure) : payload(procedure) { } type::type(std::shared_ptr enumeration) : payload(enumeration) { } template std::shared_ptr type::get() const { if constexpr (std::is_same_v) { if (const auto *weak_pointer = std::get_if>(&payload)) { return weak_pointer->lock(); } return nullptr; } else { if (auto *shared_pointer = std::get_if>(&payload)) { return *shared_pointer; } return nullptr; } } // Explicit instantiation for all types used by callers. template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; bool type::operator==(const std::nullptr_t&) const { return empty(); } bool type::operator==(const type& other) const { type const resolved_this = resolve_aliases(*this); type const resolved_that = resolve_aliases(other); if (auto left_record = resolved_this.get()) { return left_record == resolved_that.get(); } if (auto left_primitive = resolved_this.get()) { auto right_primitive = resolved_that.get(); return right_primitive != nullptr && left_primitive->identifier == right_primitive->identifier; } if (auto left_enumeration = resolved_this.get()) { return left_enumeration == resolved_that.get(); } if (auto left_pointer = resolved_this.get()) { auto right_pointer = resolved_that.get(); return right_pointer != nullptr && left_pointer->base == right_pointer->base; } if (auto left_const = resolved_this.get()) { auto right_const = resolved_that.get(); return right_const != nullptr && left_const->unqualified == right_const->unqualified; } if (auto left_array = resolved_this.get()) { auto right_array = resolved_that.get(); return right_array != nullptr && left_array->size == right_array->size && left_array->base == right_array->base; } if (auto left_slice = resolved_this.get()) { auto right_slice = resolved_that.get(); return right_slice != nullptr && left_slice->base == right_slice->base; } if (auto left_procedure = resolved_this.get()) { auto right_procedure = resolved_that.get(); return right_procedure != nullptr && left_procedure->return_type == right_procedure->return_type && left_procedure->parameters == right_procedure->parameters; } return resolved_this.empty() && resolved_that.empty(); } bool type::empty() const { return std::holds_alternative(payload); } std::string type::to_string() const { return std::visit([](const auto& payload) -> std::string { using T = std::decay_t; if constexpr (std::is_same_v) { return ""; } else if constexpr (std::is_same_v>) { return payload.lock()->name; } else if constexpr (std::is_same_v>) { return payload->identifier; } else if constexpr (std::is_same_v>) { return "^" + payload->base.to_string(); } else if constexpr (std::is_same_v>) { return "const " + payload->unqualified.to_string(); } else if constexpr (std::is_same_v>) { return "[" + std::to_string(payload->size) + "]" + payload->base.to_string(); } else if constexpr (std::is_same_v>) { return "[]" + payload->base.to_string(); } else if constexpr (std::is_same_v>) { return payload->base.empty() ? "record ... end" : "record(" + payload->base.to_string() + ") ... end"; } else if constexpr (std::is_same_v>) { std::string result = "proc("; for (std::size_t i = 0; i < payload->parameters.size(); ++i) { if (i > 0) { result += ", "; } result += payload->parameters[i].to_string(); } result += ")"; if (payload->return_type.no_return) { result += ": !"; } else if (!payload->return_type.proper_type.empty()) { result += ": " + payload->return_type.proper_type.to_string(); } return result; } else if constexpr (std::is_same_v>) { return "(enumeration)"; } }, payload); } alias_type::alias_type(const std::string& name) : name(name) { } pointer_type::pointer_type(type base) : base(std::move(base)) { } constant_type::constant_type(type unqualified) : unqualified(std::move(unqualified)) { } array_type::array_type(type base, std::uint64_t size) : base(std::move(base)), size(size) { } slice_type::slice_type(type base) : base(std::move(base)) { } primitive_type::primitive_type(const std::string& identifier) : identifier(identifier) { } record_type::record_type(type base) : base(std::move(base)) { } procedure_type::procedure_type(return_t return_type) : return_type(std::move(return_type)) { } enumeration_type::enumeration_type(const std::vector& members) : members(members) { } info::~info() = default; std::shared_ptr info::is_type() { return nullptr; } std::shared_ptr info::is_procedure() { return nullptr; } std::shared_ptr info::is_variable() { return nullptr; } type_info::type_info(const type& symbol) : symbol(symbol) { } std::shared_ptr type_info::is_type() { return std::static_pointer_cast(shared_from_this()); } procedure_info::procedure_info(const procedure_type& symbol, const std::vector& names, std::shared_ptr scope) : symbol(symbol), names(names), scope(std::move(scope)) { } std::shared_ptr procedure_info::is_procedure() { return std::static_pointer_cast(shared_from_this()); } bool procedure_info::is_extern() const { return this->scope == nullptr; } variable_info::variable_info(const type& symbol, bool is_extern) : symbol(symbol), is_extern(is_extern) { } std::shared_ptr variable_info::is_variable() { return std::static_pointer_cast(shared_from_this()); } std::shared_ptr builtin_symbol_table() { auto result = std::make_shared(); result->enter("Int", std::make_shared(type(std::make_shared("Int")))); result->enter("Word", std::make_shared(type(std::make_shared("Word")))); result->enter("Char", std::make_shared(type(std::make_shared("Char")))); result->enter("Pointer", std::make_shared(type(std::make_shared("Pointer")))); result->enter("Float", std::make_shared(type(std::make_shared("Float")))); result->enter("String", std::make_shared(type(std::make_shared("String")))); type const boolean = type(std::make_shared("Bool")); result->enter("Bool", std::make_shared(boolean)); procedure_type assert_symbol{ procedure_type::return_t() }; assert_symbol.parameters.push_back(boolean); std::shared_ptr const assert_info = std::make_shared(assert_symbol, std::vector{ "condition" }); result->enter("assert", assert_info); return result; } symbol_bag::symbol_bag(forward_table&& unresolved, const std::shared_ptr& global_table) : unresolved(unresolved) { this->symbols = std::make_shared(global_table); } std::shared_ptr symbol_bag::lookup(const std::string& name) { for (const auto& import_bag : this->imports) { if (auto result = import_bag->lookup(name)) { return result; } } return this->symbols->lookup(name); } bool symbol_bag::enter(const std::string& name, const std::shared_ptr& entry) { return this->symbols->enter(name, entry); } std::shared_ptr symbol_bag::enter() { this->symbols = std::make_shared(this->symbols); return this->symbols; } void symbol_bag::enter(std::shared_ptr child) { this->symbols = std::move(child); } std::shared_ptr symbol_bag::leave() { std::shared_ptr result = this->symbols; this->symbols = result->scope(); return result; } std::shared_ptr symbol_bag::declared(const std::string& symbol_name) { 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->referent = resolution; return unresolved_declaration; } void symbol_bag::add_import(const symbol_bag& bag) { this->imports.push_front(bag.exported_symbols()); } std::shared_ptr symbol_bag::exported_symbols() const { if (!m_exported) { m_exported = std::make_shared(symbols->scope()); for (const auto& [name, info] : *symbols) { if (info->exported) { m_exported->enter(name, info); } } } return m_exported; } type resolve_underlying_type(const std::shared_ptr& alias) { return resolve_underlying_type(alias->referent); } type resolve_underlying_type(const type& alias) { type resolved_alias = resolve_aliases(alias); if (auto qualified = resolved_alias.get()) { return resolve_underlying_type(qualified->unqualified); } else { return resolved_alias; } } type resolve_aliases(const type& checked) { if (auto alias = checked.get()) { return resolve_aliases(alias->referent); } return checked; } bool is_primitive_type(const type& checked, const std::string& name) { if (auto primitive_checked = checked.get()) { return primitive_checked->identifier == name; } return false; } bool is_numeric_type(const type& checked) { type const resolved = resolve_underlying_type(checked); return is_primitive_type(resolved, "Int") || is_primitive_type(resolved, "Word") || is_primitive_type(resolved, "Float"); } bool is_integral_type(const type& checked) { type const resolved = resolve_underlying_type(checked); return is_primitive_type(resolved, "Int") || is_primitive_type(resolved, "Word"); } bool is_discrete_type(const type& checked) { type const resolved = resolve_underlying_type(checked); return is_primitive_type(resolved, "Int") || is_primitive_type(resolved, "Word") || is_primitive_type(resolved, "Bool") || is_primitive_type(resolved, "Char"); } bool is_any_pointer_type(const type& checked) { return checked.get() != nullptr || checked.get() != nullptr || is_primitive_type(resolve_underlying_type(checked), "Pointer"); } }