diff options
Diffstat (limited to 'frontend/semantic.cc')
| -rw-r--r-- | frontend/semantic.cc | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/frontend/semantic.cc b/frontend/semantic.cc new file mode 100644 index 0000000..36c75b8 --- /dev/null +++ b/frontend/semantic.cc @@ -0,0 +1,644 @@ +/* Name analysis. + 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 +<http://www.gnu.org/licenses/>. */ + +#include "elna/frontend/semantic.h" + +#include <algorithm> +#include <set> + +namespace elna::frontend +{ + undeclared_error::undeclared_error(const std::string& identifier, const char *path, const struct position position) + : error(path, position), identifier(identifier) + { + } + + std::string undeclared_error::what() const + { + return "Type '" + identifier + "' not declared"; + } + + already_declared_error::already_declared_error(const std::string& identifier, + const char *path, const struct position position) + : error(path, position), identifier(identifier) + { + } + + std::string already_declared_error::what() const + { + return "Symbol '" + identifier + "' has been already declared"; + } + + field_duplication_error::field_duplication_error(const std::string& field_name, + const char *path, const struct position position) + : error(path, position), field_name(field_name) + { + } + + std::string field_duplication_error::what() const + { + return "Repeated field name '" + field_name + "'"; + } + + cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle, + const char *path, const struct position position) + : error(path, position), cycle(cycle) + { + } + + std::string cyclic_declaration_error::what() const + { + auto segment = std::cbegin(this->cycle); + std::string message = "Type declaration forms a cycle: " + *segment; + + ++segment; + for (; segment != std::cend(this->cycle); ++segment) + { + message += " -> " + *segment; + } + return message; + } + + return_error::return_error(const std::string& identifier, const char *path, const struct position position) + : error(path, position), identifier(identifier) + { + } + + std::string return_error::what() const + { + return "Procedure '" + identifier + "' is expected to return, but does not have a return statement"; + } + + variable_initializer_error::variable_initializer_error(const char *path, const struct position position) + : error(path, position) + { + } + + std::string variable_initializer_error::what() const + { + return "Only one variable can be initialized"; + } + + type_analysis_visitor::type_analysis_visitor(const char *path, symbol_bag bag) + : error_container(path), bag(bag) + { + } + + void type_analysis_visitor::visit(program *program) + { + visit(static_cast<unit *>(program)); + } + + void type_analysis_visitor::visit(procedure_declaration *definition) + { + if (definition->body.has_value() && definition->heading().return_type.proper_type != nullptr) + { + for (statement *const statement : definition->body.value().body()) + { + statement->accept(this); + } + if (!this->returns) + { + add_error<return_error>(definition->identifier.name, this->input_file, definition->position()); + } + } + } + + void type_analysis_visitor::visit(assign_statement *) + { + } + + void type_analysis_visitor::visit(if_statement *) + { + } + + void type_analysis_visitor::visit(while_statement *) + { + } + + void type_analysis_visitor::visit(return_statement *) + { + this->returns = true; + } + + void type_analysis_visitor::visit(defer_statement *) + { + } + + void type_analysis_visitor::visit(case_statement *) + { + } + + void type_analysis_visitor::visit(procedure_call *) + { + } + + 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; + procedure->accept(this); + } + } + + 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) + { + } + + procedure_type name_analysis_visitor::build_procedure(procedure_type_expression& type_expression) + { + procedure_type::return_t result_return; + + if (type_expression.return_type.no_return) + { + result_return = procedure_type::return_t(std::monostate{}); + } + else if (type_expression.return_type.proper_type != nullptr) + { + type_expression.return_type.proper_type->accept(this); + result_return = procedure_type::return_t(this->current_type); + } + else + { + result_return = procedure_type::return_t(); + } + procedure_type result_type = procedure_type(result_return); + + for (struct type_expression *parameter : type_expression.parameters) + { + parameter->accept(this); + result_type.parameters.push_back(this->current_type); + } + return result_type; + } + + void name_analysis_visitor::visit(program *program) + { + visit(static_cast<unit *>(program)); + + for (statement *const statement : program->body) + { + statement->accept(this); + } + } + + void name_analysis_visitor::visit(type_declaration *definition) + { + definition->body().accept(this); + auto resolved = this->bag.resolve(definition->identifier.name, this->current_type); + auto info = std::make_shared<type_info>(type(resolved)); + + 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.declared(type_expression->name); + + if (unresolved_alias != nullptr) + { + this->current_type = type(unresolved_alias); + } + else if (auto from_symbol_table = this->bag.lookup(type_expression->name)) + { + this->current_type = from_symbol_table->is_type()->symbol; + } + else + { + add_error<undeclared_error>(type_expression->name, this->input_file, type_expression->position()); + this->current_type = type(); + } + } + + void name_analysis_visitor::visit(pointer_type_expression *type_expression) + { + type_expression->base().accept(this); + this->current_type = type(std::make_shared<pointer_type>(this->current_type)); + } + + void name_analysis_visitor::visit(array_type_expression *type_expression) + { + type_expression->base().accept(this); + this->current_type = type(std::make_shared<array_type>(this->current_type, type_expression->size)); + } + + std::vector<type_field> name_analysis_visitor::build_composite_type(const std::vector<field_declaration>& fields) + { + std::vector<type_field> result; + std::set<std::string> field_names; + + for (auto& field : fields) + { + if (field_names.find(field.first) != field_names.cend()) + { + add_error<field_duplication_error>(field.first, this->input_file, field.second->position()); + } + else + { + field_names.insert(field.first); + field.second->accept(this); + result.push_back(std::make_pair(field.first, this->current_type)); + } + } + return result; + } + + void name_analysis_visitor::visit(record_type_expression *type_expression) + { + auto result_type = std::make_shared<record_type>(); + + result_type->fields = build_composite_type(type_expression->fields); + + this->current_type = type(result_type); + } + + void name_analysis_visitor::visit(union_type_expression *type_expression) + { + auto result_type = std::make_shared<union_type>(); + + result_type->fields = build_composite_type(type_expression->fields); + + this->current_type = type(result_type); + } + + void name_analysis_visitor::visit(procedure_type_expression *type_expression) + { + std::shared_ptr<procedure_type> result_type = + std::make_shared<procedure_type>(std::move(build_procedure(*type_expression))); + + this->current_type = type(result_type); + } + + void name_analysis_visitor::visit(enumeration_type_expression *type_expression) + { + std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>(type_expression->members); + + this->current_type = type(result_type); + } + + void name_analysis_visitor::visit(variable_declaration *declaration) + { + declaration->variable_type().accept(this); + + for (const auto& variable_identifier : declaration->identifiers) + { + 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); + + constant_symbol->exported = definition->identifier.exported; + this->bag.enter(definition->identifier.name, constant_symbol); + } + + void name_analysis_visitor::visit(procedure_declaration *definition) + { + std::shared_ptr<procedure_info> info; + auto heading = build_procedure(definition->heading()); + + if (definition->body.has_value()) + { + info = std::make_shared<procedure_info>(heading, definition->parameter_names, this->bag.enter()); + + for (constant_declaration *const constant : definition->body.value().constants()) + { + constant->accept(this); + } + for (variable_declaration *const variable : definition->body.value().variables()) + { + variable->accept(this); + } + for (statement *const statement : definition->body.value().body()) + { + statement->accept(this); + } + this->bag.leave(); + } + else + { + info = std::make_shared<procedure_info>(heading, definition->parameter_names); + } + info->exported = definition->identifier.exported; + this->bag.enter(definition->identifier.name, info); + } + + void name_analysis_visitor::visit(assign_statement *statement) + { + statement->lvalue().accept(this); + statement->rvalue().accept(this); + } + + void name_analysis_visitor::visit(if_statement *statement) + { + statement->body().prerequisite().accept(this); + for (struct statement *const statement : statement->body().statements) + { + statement->accept(this); + } + for (const auto branch : statement->branches) + { + branch->prerequisite().accept(this); + + for (struct statement *const statement : branch->statements) + { + statement->accept(this); + } + } + if (statement->alternative != nullptr) + { + for (struct statement *const statement : *statement->alternative) + { + statement->accept(this); + } + } + } + + void name_analysis_visitor::visit(import_declaration *) + { + } + + void name_analysis_visitor::visit(while_statement *statement) + { + statement->body().prerequisite().accept(this); + for (struct statement *const statement : statement->body().statements) + { + statement->accept(this); + } + for (const auto branch : statement->branches) + { + branch->prerequisite().accept(this); + + for (struct statement *const statement : branch->statements) + { + statement->accept(this); + } + } + } + + void name_analysis_visitor::visit(return_statement *statement) + { + statement->return_expression().accept(this); + } + + void name_analysis_visitor::visit(defer_statement *statement) + { + for (struct statement *const statement : statement->statements) + { + statement->accept(this); + } + } + + void name_analysis_visitor::visit(case_statement *statement) + { + statement->condition().accept(this); + for (const switch_case& case_block : statement->cases) + { + for (expression *const case_label : case_block.labels) + { + case_label->accept(this); + } + for (struct statement *const statement : case_block.statements) + { + statement->accept(this); + } + } + if (statement->alternative != nullptr) + { + for (struct statement *const statement : *statement->alternative) + { + statement->accept(this); + } + } + } + + void name_analysis_visitor::visit(procedure_call *call) + { + call->callable().accept(this); + for (expression *const argument: call->arguments) + { + argument->accept(this); + } + } + + void name_analysis_visitor::visit(unit *unit) + { + for (type_declaration *const type : unit->types) + { + type->accept(this); + } + for (variable_declaration *const variable : unit->variables) + { + variable->accept(this); + } + for (procedure_declaration *const procedure : unit->procedures) + { + procedure->accept(this); + } + } + + void name_analysis_visitor::visit(traits_expression *trait) + { + if (!trait->parameters.empty()) + { + trait->parameters.front()->accept(this); + trait->types.push_back(this->current_type); + } + } + + void name_analysis_visitor::visit(cast_expression *expression) + { + expression->value().accept(this); + expression->target().accept(this); + expression->expression_type = this->current_type; + } + + void name_analysis_visitor::visit(binary_expression *expression) + { + expression->lhs().accept(this); + expression->rhs().accept(this); + } + + void name_analysis_visitor::visit(unary_expression *expression) + { + expression->operand().accept(this); + } + + void name_analysis_visitor::visit(variable_expression *) + { + } + + void name_analysis_visitor::visit(array_access_expression *expression) + { + expression->base().accept(this); + expression->index().accept(this); + } + + void name_analysis_visitor::visit(field_access_expression *expression) + { + expression->base().accept(this); + } + + void name_analysis_visitor::visit(dereference_expression *expression) + { + expression->base().accept(this); + } + + void name_analysis_visitor::visit(literal<std::int32_t> *literal) + { + this->current_literal = literal->value; + } + + void name_analysis_visitor::visit(literal<std::uint32_t> *literal) + { + this->current_literal = literal->value; + } + + void name_analysis_visitor::visit(literal<double> *literal) + { + this->current_literal = literal->value; + } + + void name_analysis_visitor::visit(literal<bool> *literal) + { + this->current_literal = literal->value; + } + + void name_analysis_visitor::visit(literal<unsigned char> *literal) + { + this->current_literal = literal->value; + } + + void name_analysis_visitor::visit(literal<std::nullptr_t> *literal) + { + this->current_literal = literal->value; + } + + void name_analysis_visitor::visit(literal<std::string> *literal) + { + this->current_literal = literal->value; + } + + declaration_visitor::declaration_visitor(const char *path) + : error_container(path) + { + } + + void declaration_visitor::visit(program *program) + { + visit(static_cast<unit *>(program)); + } + + void declaration_visitor::visit(import_declaration *) + { + } + + void declaration_visitor::visit(unit *unit) + { + for (import_declaration *const _import : unit->imports) + { + _import->accept(this); + } + for (type_declaration *const type : unit->types) + { + type->accept(this); + } + for (variable_declaration *const variable : unit->variables) + { + variable->accept(this); + } + for (procedure_declaration *const procedure : unit->procedures) + { + procedure->accept(this); + } + } + + 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) + { + add_error<variable_initializer_error>(this->input_file, declaration->position()); + } + } + + void declaration_visitor::visit(procedure_declaration *definition) + { + if (!definition->body.has_value()) + { + return; + } + for (variable_declaration *const variable : definition->body.value().variables()) + { + variable->accept(this); + } + } +} |
