/* 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" namespace elna::boot { type::type() { } type::type(std::shared_ptr alias) : tag(type_tag::alias), alias(alias) { } type::type(std::shared_ptr primitive) : tag(type_tag::primitive), primitive(primitive) { } type::type(std::shared_ptr record) : tag(type_tag::record), record(record) { } type::type(std::shared_ptr pointer) : tag(type_tag::pointer), pointer(pointer) { } type::type(std::shared_ptr constant) : tag(type_tag::constant), constant(constant) { } type::type(std::shared_ptr array) : tag(type_tag::array), array(array) { } type::type(std::shared_ptr procedure) : tag(type_tag::procedure), procedure(procedure) { } type::type(std::shared_ptr enumeration) : tag(type_tag::enumeration), enumeration(enumeration) { } void type::copy(const type& other) { switch (other.tag) { case type_tag::empty: break; case type_tag::alias: new (&alias) std::weak_ptr(other.alias); break; case type_tag::primitive: new (&primitive) std::shared_ptr(other.primitive); break; case type_tag::record: new (&record) std::shared_ptr(other.record); break; case type_tag::pointer: new (&pointer) std::shared_ptr(other.pointer); break; case type_tag::constant: new (&constant) std::shared_ptr(other.constant); break; case type_tag::array: new (&array) std::shared_ptr(other.array); break; case type_tag::procedure: new (&procedure) std::shared_ptr(other.procedure); break; case type_tag::enumeration: new (&enumeration) std::shared_ptr(other.enumeration); break; } } type::type(const type& other) : tag(other.tag) { copy(other); } void type::move(type&& other) { switch (other.tag) { case type_tag::empty: break; case type_tag::alias: new (&alias) std::weak_ptr(std::move(other.alias)); break; case type_tag::primitive: new (&primitive) std::shared_ptr(std::move(other.primitive)); break; case type_tag::record: new (&record) std::shared_ptr(std::move(other.record)); break; case type_tag::pointer: new (&pointer) std::shared_ptr(std::move(other.pointer)); break; case type_tag::constant: new (&constant) std::shared_ptr(std::move(other.constant)); break; case type_tag::array: new (&array) std::shared_ptr(std::move(other.array)); break; case type_tag::procedure: new (&procedure) std::shared_ptr(std::move(other.procedure)); break; case type_tag::enumeration: new (&enumeration) std::shared_ptr(std::move(other.enumeration)); break; } } type& type::operator=(const type& other) { this->~type(); this->tag = other.tag; copy(other); return *this; } type::type(type&& other) : tag(other.tag) { move(std::move(other)); } type& type::operator=(type&& other) { this->~type(); this->tag = other.tag; move(std::move(other)); return *this; } type::~type() { switch (tag) { case type_tag::empty: break; case type_tag::alias: this->alias.~weak_ptr(); break; case type_tag::primitive: this->primitive.~shared_ptr(); break; case type_tag::record: this->record.~shared_ptr(); break; case type_tag::pointer: this->pointer.~shared_ptr(); break; case type_tag::constant: this->constant.~shared_ptr(); break; case type_tag::array: this->array.~shared_ptr(); break; case type_tag::procedure: this->procedure.~shared_ptr(); break; case type_tag::enumeration: this->enumeration.~shared_ptr(); break; } } template<> std::shared_ptr type::get() const { return tag == type_tag::alias ? this->alias.lock() : nullptr; } template<> std::shared_ptr type::get() const { return tag == type_tag::primitive ? this->primitive : nullptr; } template<> std::shared_ptr type::get() const { return tag == type_tag::record ? this->record : nullptr; } template<> std::shared_ptr type::get() const { return tag == type_tag::pointer ? this->pointer : nullptr; } template<> std::shared_ptr type::get() const { return tag == type_tag::array ? this->array : nullptr; } template<> std::shared_ptr type::get() const { return tag == type_tag::constant ? this->constant : nullptr; } template<> std::shared_ptr type::get() const { return tag == type_tag::procedure ? this->procedure : nullptr; } template<> std::shared_ptr type::get() const { return tag == type_tag::enumeration ? this->enumeration : nullptr; } bool type::operator==(const std::nullptr_t&) const { return empty(); } bool type::operator==(const type& other) const { type resolved_this = resolve_aliases(*this); type 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_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 tag == type_tag::empty; } std::string type::to_string() const { switch (tag) { case type_tag::empty: return ""; case type_tag::alias: return alias.lock()->name; case type_tag::primitive: return primitive->identifier; case type_tag::pointer: return "^" + pointer->base.to_string(); case type_tag::constant: return "const " + constant->unqualified.to_string(); case type_tag::array: return "[" + std::to_string(array->size) + "]" + array->base.to_string(); case type_tag::record: if (record->base.empty()) { return "record ... end"; } else { return "record(" + record->base.to_string() + ") ... end"; } case type_tag::procedure: { std::string result = "proc("; for (std::size_t i = 0; i < procedure->parameters.size(); ++i) { if (i > 0) result += ", "; result += procedure->parameters[i].to_string(); } result += ")"; if (procedure->return_type.no_return) { result += ": !"; } else if (!procedure->return_type.proper_type.empty()) { result += ": " + procedure->return_type.proper_type.to_string(); } return result; } case type_tag::enumeration: return "(enumeration)"; } __builtin_unreachable(); } alias_type::alias_type(const std::string& name) : name(name), reference() { } pointer_type::pointer_type(type base) : base(base) { } constant_type::constant_type(type unqualified) : unqualified(unqualified) { } array_type::array_type(type base, std::uint64_t size) : base(base), size(size) { } primitive_type::primitive_type(const std::string& identifier) : identifier(identifier) { } record_type::record_type(type base) : base(base) { } procedure_type::procedure_type(return_t return_type) : return_type(return_type) { } enumeration_type::enumeration_type(const std::vector& members) : members(members) { } info::~info() { } 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(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 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 assert_info = std::make_shared(assert_symbol, std::vector{ "condition" }); result->enter("assert", assert_info); return result; } 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) { for (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, 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 = 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->reference = 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(std::shared_ptr alias) { return resolve_underlying_type(alias->reference); } 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->reference); } 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_any_pointer_type(const type& checked) { return checked.get() != nullptr || checked.get() != nullptr || is_primitive_type(checked, "Pointer"); } }