summaryrefslogtreecommitdiff
path: root/frontend/semantic.cc
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/semantic.cc')
-rw-r--r--frontend/semantic.cc644
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);
+ }
+ }
+}