aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/dependency.cc21
-rw-r--r--boot/name_analysis.cc815
-rw-r--r--boot/symbol.cc5
-rw-r--r--include/elna/boot/name_analysis.h107
-rw-r--r--include/elna/boot/symbol.h11
-rw-r--r--testsuite/compilable/procedure_forward_reference.elna7
6 files changed, 582 insertions, 384 deletions
diff --git a/boot/dependency.cc b/boot/dependency.cc
index 7e103aa..9b52ce9 100644
--- a/boot/dependency.cc
+++ b/boot/dependency.cc
@@ -68,19 +68,30 @@ namespace elna::boot
const std::shared_ptr<symbol_table>& globals, const target_info& target,
const std::filesystem::path& module_path)
{
- declaration_visitor declarations{};
- tree->accept(&declarations);
- analysis_result result{ .value = { std::move(declarations.unresolved), globals }, .errors = {} };
+ forward_declaration_visitor forward_declarer;
- if (declarations.has_errors())
+ tree->accept(&forward_declarer);
+
+ analysis_result result{
+ .value = symbol_bag(std::move(forward_declarer.unresolved), globals),
+ .errors = std::move(forward_declarer.errors())
+ };
+ if (!result.errors.empty())
{
- std::swap(result.errors, declarations.errors());
return result;
}
for (const auto& import : imports)
{
result.value.add_import(import);
}
+ declaration_visitor declarations(result.value, target, module_path);
+ tree->accept(&declarations);
+
+ if (declarations.has_errors())
+ {
+ std::swap(result.errors, declarations.errors());
+ return result;
+ }
name_analysis_visitor name_analyser(result.value, target, module_path);
tree->accept(&name_analyser);
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 10b8a8d..cf90e3d 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -202,13 +202,58 @@ namespace elna::boot
}
}
- name_analysis_visitor::name_analysis_visitor(symbol_bag bag, const target_info& target,
- std::filesystem::path module_file)
- : bag(std::move(bag)), constant_evaluator(this->bag, target), module_file(std::move(module_file))
+ /**
+ * Collects field names from a record type recursively, base first.
+ */
+ static void collect_field_names(const type& composite_type,
+ ordered_map<field_origin>& names)
+ {
+ auto record = resolve_underlying_type(composite_type).get<record_type>();
+ if (record == nullptr)
+ {
+ return;
+ }
+ if (!record->base.empty())
+ {
+ collect_field_names(record->base, names);
+ }
+ for (auto& field : record->fields)
+ {
+ names.insert(field.first, field_origin{ .position = std::nullopt, .base_type = composite_type });
+ }
+ }
+
+ void forward_declaration_visitor::visit(unit *unit)
+ {
+ for (type_declaration *const type : unit->types)
+ {
+ type->accept(this);
+ }
+ }
+
+ void forward_declaration_visitor::visit(type_declaration *declaration)
+ {
+ const std::string& type_identifier = declaration->identifier.name();
+
+ if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second)
+ {
+ add_error<declaration_error>(declaration->identifier.id().position(),
+ declaration->identifier.id().name(),
+ declaration_error::redefinition{ .original = declaration->position(), .file = {} });
+ }
+ }
+
+ resolving_visitor::resolving_visitor(symbol_bag& bag, const target_info& target,
+ const std::filesystem::path& module_path)
+ : bag(bag), constant_evaluator(this->bag, target), module_file(module_path)
+ {
+ }
+
+ void resolving_visitor::visit(import_declaration *)
{
}
- std::pair<procedure_type, std::vector<std::string>> name_analysis_visitor::build_procedure(
+ std::pair<procedure_type, std::vector<std::string>> resolving_visitor::build_procedure(
procedure_type_expression& expression)
{
procedure_type::return_t result_return;
@@ -241,7 +286,70 @@ namespace elna::boot
return result_type;
}
- std::optional<type> name_analysis_visitor::lookup_pointer_like_field(
+ ordered_map<type> resolving_visitor::build_composite_type(const std::vector<field_declaration>& fields,
+ ordered_map<field_origin>& field_names, const type& aggregate)
+ {
+ ordered_map<type> result;
+
+ for (const auto& field : fields)
+ {
+ field.second->accept(this);
+ for (const auto& field_name : field.first)
+ {
+ auto [existing, inserted] = field_names.insert(field_name.name(),
+ field_origin{ .position = field.second->position(), .base_type = type() });
+ if (!inserted)
+ {
+ std::optional<std::string> base_name;
+
+ if (!existing->second.position.has_value()
+ && !existing->second.base_type.empty())
+ {
+ if (auto alias = existing->second.base_type.get<alias_type>())
+ {
+ base_name = alias->name;
+ }
+ }
+ add_error<member_error>(field_name.position(), field_name.name(), aggregate,
+ member_error::duplicate{ .original = existing->second.position, .base = base_name });
+ }
+ else
+ {
+ result.insert(field_name.name(), this->current_type);
+ }
+ }
+ }
+ return result;
+ }
+
+ type resolving_visitor::lookup_primitive_type(const std::string& name)
+ {
+ return this->bag.lookup(name)->is_type()->symbol;
+ }
+
+ std::filesystem::path resolving_visitor::redefinition_file(const std::shared_ptr<info>& original) const
+ {
+ return original->file != this->module_file ? original->file : std::filesystem::path{};
+ }
+
+ std::shared_ptr<variable_info> resolving_visitor::register_variable(const std::string& name,
+ const type& variable_type, const source_position position, const bool is_extern)
+ {
+ auto variable_symbol = std::make_shared<variable_info>(variable_type, is_extern);
+ variable_symbol->position.emplace(position);
+ variable_symbol->file = this->module_file;
+
+ if (!this->bag.enter(name, variable_symbol))
+ {
+ auto original = this->bag.lookup(name);
+ add_error<declaration_error>(position, name,
+ declaration_error::redefinition{ .original = original->position,
+ .file = this->redefinition_file(original) });
+ }
+ return variable_symbol;
+ }
+
+ std::optional<type> resolving_visitor::lookup_pointer_like_field(
const std::string& field_name, const type& element_type)
{
if (field_name == "length")
@@ -256,12 +364,7 @@ namespace elna::boot
return std::nullopt;
}
- type name_analysis_visitor::lookup_primitive_type(const std::string& name)
- {
- return this->bag.lookup(name)->is_type()->symbol;
- }
-
- type name_analysis_visitor::lookup_field(const type& composite_type, const std::string& field_name)
+ type resolving_visitor::lookup_field(const type& composite_type, const std::string& field_name)
{
const type resolved_type = resolve_underlying_type(composite_type);
@@ -289,44 +392,40 @@ namespace elna::boot
return type();
}
- void name_analysis_visitor::visit(type_declaration *declaration)
+ void resolving_visitor::visit(variable_declaration *declaration)
{
- walking_visitor::visit(declaration);
- auto resolved = this->bag.resolve(declaration->identifier.name(), this->current_type);
- auto info = std::make_shared<type_info>(type(resolved));
+ declaration->variable_type().accept(this);
+ auto variable_type = this->current_type;
+ std::optional<constant_value> computed;
- info->exported = declaration->identifier.exported();
- info->position.emplace(declaration->position());
- info->file = this->module_file;
+ if (declaration->initializer != nullptr)
+ {
+ declaration->initializer->accept(this);
+ computed = this->constant_evaluator.evaluate(*declaration->initializer);
- if (!this->bag.enter(declaration->identifier.name(), info))
+ if (this->bag.is_global() && !computed.has_value())
+ {
+ add_error<non_constant_expression_error>(declaration->initializer->position(),
+ non_constant_expression_error::initializer{ extract_identifiers(declaration->identifiers) });
+ }
+ }
+ else if (resolve_aliases(variable_type).get<constant_type>() != nullptr)
{
- auto original = this->bag.lookup(declaration->identifier.name());
- add_error<declaration_error>(declaration->identifier.id().position(), declaration->identifier.name(),
- declaration_error::redefinition{ .original = original->position,
- .file = this->redefinition_file(original) });
+ auto position_span = source_position(declaration->identifiers.front().id().position().start(),
+ declaration->identifiers.back().id().position().end());
+ add_error<const_qualifier_error>(position_span,
+ const_qualifier_error::not_initialized{ extract_identifiers(declaration->identifiers) });
}
- }
-
- void name_analysis_visitor::visit(pointer_type_expression *expression)
- {
- walking_visitor::visit(expression);
- this->current_type = type(std::make_shared<pointer_type>(this->current_type));
- }
-
- void name_analysis_visitor::visit(constant_type_expression *expression)
- {
- walking_visitor::visit(expression);
-
- if (this->current_type.get<constant_type>() != nullptr)
+ for (const identifier_definition& variable_identifier : declaration->identifiers)
{
- add_error<const_qualifier_error>(expression->position(),
- const_qualifier_error::kind::duplicate);
+ auto variable_symbol = register_variable(variable_identifier.name(),
+ variable_type, declaration->position(), declaration->is_extern);
+ variable_symbol->exported = variable_identifier.exported();
+ variable_symbol->value = computed;
}
- this->current_type = type(std::make_shared<constant_type>(this->current_type));
}
- void name_analysis_visitor::visit(array_type_expression *expression)
+ void resolving_visitor::visit(array_type_expression *expression)
{
expression->base().accept(this);
auto array_base = this->current_type;
@@ -347,70 +446,30 @@ namespace elna::boot
this->current_type = type(std::make_shared<array_type>(array_base, size_constant.value()));
}
- void name_analysis_visitor::visit(slice_type_expression *expression)
+ void resolving_visitor::visit(slice_type_expression *expression)
{
- walking_visitor::visit(expression);
+ expression->base().accept(this);
this->current_type = type(std::make_shared<slice_type>(this->current_type));
}
- /**
- * Collects field names from a record type recursively, base first.
- */
- static void collect_field_names(const type& composite_type,
- ordered_map<field_origin>& names)
+ void resolving_visitor::visit(pointer_type_expression *expression)
{
- auto record = resolve_underlying_type(composite_type).get<record_type>();
- if (record == nullptr)
- {
- return;
- }
- if (!record->base.empty())
- {
- collect_field_names(record->base, names);
- }
- for (auto& field : record->fields)
- {
- names.insert(field.first, field_origin{ .position = std::nullopt, .base_type = composite_type });
- }
+ expression->base().accept(this);
+ this->current_type = type(std::make_shared<pointer_type>(this->current_type));
}
- ordered_map<type> name_analysis_visitor::build_composite_type(const std::vector<field_declaration>& fields,
- ordered_map<field_origin>& field_names, const type& aggregate)
+ void resolving_visitor::visit(constant_type_expression *expression)
{
- ordered_map<type> result;
-
- for (const auto& field : fields)
+ expression->base().accept(this);
+ if (this->current_type.get<constant_type>() != nullptr)
{
- field.second->accept(this);
- for (const auto& field_name : field.first)
- {
- auto [existing, inserted] = field_names.insert(field_name.name(),
- field_origin{ .position = field.second->position(), .base_type = type() });
- if (!inserted)
- {
- std::optional<std::string> base_name;
-
- if (!existing->second.position.has_value()
- && !existing->second.base_type.empty())
- {
- if (auto alias = existing->second.base_type.get<alias_type>())
- {
- base_name = alias->name;
- }
- }
- add_error<member_error>(field_name.position(), field_name.name(), aggregate,
- member_error::duplicate{ .original = existing->second.position, .base = base_name });
- }
- else
- {
- result.insert(field_name.name(), this->current_type);
- }
- }
+ add_error<const_qualifier_error>(expression->position(),
+ const_qualifier_error::kind::duplicate);
}
- return result;
+ this->current_type = type(std::make_shared<constant_type>(this->current_type));
}
- void name_analysis_visitor::visit(record_type_expression *expression)
+ void resolving_visitor::visit(record_type_expression *expression)
{
std::shared_ptr<record_type> result_type;
if (expression->base.has_value())
@@ -451,66 +510,7 @@ namespace elna::boot
this->current_type = type(result_type);
}
- void name_analysis_visitor::visit(record_constructor_expression *expression)
- {
- if (auto type_symbol = this->bag.lookup(expression->type_name.name()))
- {
- if (auto type_info = type_symbol->is_type())
- {
- expression->type_decoration = type_info->symbol;
- }
- }
- else
- {
- add_error<declaration_error>(expression->type_name.position(),
- expression->type_name.name(), declaration_error::kind::undeclared_type);
- }
- for (const field_initializer& initializer : expression->field_initializers)
- {
- initializer.value().accept(this);
- if (!expression->type_decoration.empty()
- && lookup_field(expression->type_decoration, initializer.name()).empty())
- {
- add_error<member_error>(initializer.id().position(), initializer.id().name(),
- expression->type_decoration, member_error::kind::not_found);
- }
- }
- this->current_type = type();
- }
-
- void name_analysis_visitor::visit(array_constructor_expression *expression)
- {
- for (auto *element : expression->elements)
- {
- element->accept(this);
- }
- const type element_type = expression->elements.front()->type_decoration;
- expression->type_decoration = type(std::make_shared<array_type>(element_type,
- expression->elements.size()));
- this->current_type = type();
- }
-
- void name_analysis_visitor::visit(slicing_expression *expression)
- {
- walking_visitor::visit(expression);
- auto resolved_base = resolve_underlying_type(expression->base().type_decoration);
-
- if (auto pointer = resolved_base.get<pointer_type>())
- {
- expression->type_decoration = type(std::make_shared<slice_type>(pointer->base));
- }
- else if (auto array = resolved_base.get<array_type>())
- {
- expression->type_decoration = type(std::make_shared<slice_type>(array->base));
- }
- else if (auto slice = resolved_base.get<slice_type>())
- {
- expression->type_decoration = type(slice);
- }
- this->current_type = type();
- }
-
- void name_analysis_visitor::visit(procedure_type_expression *expression)
+ void resolving_visitor::visit(procedure_type_expression *expression)
{
std::shared_ptr<procedure_type> const result_type =
std::make_shared<procedure_type>(std::move(build_procedure(*expression).first));
@@ -518,12 +518,7 @@ namespace elna::boot
this->current_type = type(result_type);
}
- void name_analysis_visitor::visit(extern_type_expression *)
- {
- this->current_type = type(std::make_shared<extern_type>());
- }
-
- void name_analysis_visitor::visit(enumeration_type_expression *expression)
+ void resolving_visitor::visit(enumeration_type_expression *expression)
{
std::vector<std::string> member_names;
member_names.reserve(expression->members.size());
@@ -552,114 +547,105 @@ namespace elna::boot
this->current_type = type(result_type);
}
- std::filesystem::path name_analysis_visitor::redefinition_file(
- const std::shared_ptr<info>& original) const
+ void resolving_visitor::visit(extern_type_expression *)
{
- return original->file != this->module_file ? original->file : std::filesystem::path{};
+ this->current_type = type(std::make_shared<extern_type>());
}
- std::shared_ptr<variable_info> name_analysis_visitor::register_variable(const std::string& name,
- const bool is_extern, const source_position position)
+ void resolving_visitor::visit(assign_statement *statement)
{
- auto variable_symbol = std::make_shared<variable_info>(this->current_type, is_extern);
- variable_symbol->position.emplace(position);
- variable_symbol->file = this->module_file;
+ statement->lvalue().accept(this);
+ statement->rvalue().accept(this);
+ }
- if (!this->bag.enter(name, variable_symbol))
+ void resolving_visitor::visit(if_statement *statement)
+ {
+ statement->branch().prerequisite().accept(this);
+ for (auto *branch_statement : statement->branch().statements)
{
- auto original = this->bag.lookup(name);
- add_error<declaration_error>(position, name,
- declaration_error::redefinition{ .original = original->position,
- .file = this->redefinition_file(original) });
+ branch_statement->accept(this);
+ }
+ for (conditional_statements *branch : statement->branches)
+ {
+ branch->prerequisite().accept(this);
+
+ for (auto *branch_statement : branch->statements)
+ {
+ branch_statement->accept(this);
+ }
+ }
+ if (statement->alternative != nullptr)
+ {
+ for (auto *branch_statement : *statement->alternative)
+ {
+ branch_statement->accept(this);
+ }
}
- return variable_symbol;
}
- void name_analysis_visitor::visit(variable_declaration *declaration)
+ void resolving_visitor::visit(while_statement *statement)
{
- declaration->variable_type().accept(this);
- auto variable_type = this->current_type;
- std::optional<constant_value> computed;
-
- if (declaration->initializer != nullptr)
+ statement->branch().prerequisite().accept(this);
+ for (auto *branch_statement : statement->branch().statements)
{
- declaration->initializer->accept(this);
- this->current_type = variable_type;
- computed = this->constant_evaluator.evaluate(*declaration->initializer);
+ branch_statement->accept(this);
+ }
+ for (conditional_statements *branch : statement->branches)
+ {
+ branch->prerequisite().accept(this);
- if (this->bag.is_global() && !computed.has_value())
+ for (auto *branch_statement : branch->statements)
{
- add_error<non_constant_expression_error>(declaration->initializer->position(),
- non_constant_expression_error::initializer{ extract_identifiers(declaration->identifiers) });
+ branch_statement->accept(this);
}
}
- else if (resolve_aliases(variable_type).get<constant_type>() != nullptr)
+ }
+
+ void resolving_visitor::visit(repeat_statement *statement)
+ {
+ statement->condition().accept(this);
+ for (auto *body_statement : statement->body)
{
- auto position_span = source_position(declaration->identifiers.front().id().position().start(),
- declaration->identifiers.back().id().position().end());
- add_error<const_qualifier_error>(position_span,
- const_qualifier_error::not_initialized{ extract_identifiers(declaration->identifiers) });
+ body_statement->accept(this);
}
- for (const identifier_definition& variable_identifier : declaration->identifiers)
+ }
+
+ void resolving_visitor::visit(defer_statement *statement)
+ {
+ for (auto *block_statement : statement->statements)
{
- auto variable_symbol = register_variable(variable_identifier.name(), declaration->is_extern,
- declaration->position());
- variable_symbol->exported = variable_identifier.exported();
- variable_symbol->value = computed;
+ block_statement->accept(this);
}
}
- void name_analysis_visitor::visit(procedure_declaration *declaration)
+ void resolving_visitor::visit(empty_statement *)
{
- std::shared_ptr<procedure_info> info;
- auto [heading, parameter_names] = build_procedure(declaration->heading());
+ }
- if (declaration->body.has_value())
+ void resolving_visitor::visit(case_statement *statement)
+ {
+ statement->condition().accept(this);
+ for (const switch_case& case_block : statement->cases)
{
- info = std::make_shared<procedure_info>(heading, std::move(parameter_names), this->bag.enter());
- auto name_iterator = std::cbegin(info->names);
- auto type_iterator = std::cbegin(heading.parameters);
-
- while (name_iterator != std::cend(info->names) && type_iterator != std::cend(heading.parameters))
- {
- this->current_type = *type_iterator;
- auto variable_symbol = register_variable(*name_iterator, false, declaration->heading().position());
- variable_symbol->exported = false;
-
- ++name_iterator;
- ++type_iterator;
- }
- for (variable_declaration *const variable : declaration->body.value().variables)
- {
- variable->accept(this);
- }
- for (statement *const statement : declaration->body.value().entry_point)
+ for (expression *case_label : case_block.labels)
{
- statement->accept(this);
+ case_label->accept(this);
}
- if (declaration->body.value().return_expression != nullptr)
+ for (auto *block_statement : case_block.statements)
{
- declaration->body.value().return_expression->accept(this);
+ block_statement->accept(this);
}
- this->bag.leave();
- }
- else
- {
- info = std::make_shared<procedure_info>(heading, std::move(parameter_names));
}
- info->exported = declaration->identifier.exported();
- info->position.emplace(declaration->position());
- info->file = this->module_file;
- if (!this->bag.enter(declaration->identifier.name(), info))
+ if (statement->alternative != nullptr)
{
- auto original = this->bag.lookup(declaration->identifier.name());
- add_error<declaration_error>(declaration->identifier.id().position(), declaration->identifier.name(),
- declaration_error::redefinition{ .original = original->position,
- .file = this->redefinition_file(original) });
+ for (auto *block_statement : *statement->alternative)
+ {
+ block_statement->accept(this);
+ }
}
}
- void name_analysis_visitor::visit(procedure_call *call)
+ void resolving_visitor::visit(procedure_call *call)
{
call->callable().accept(this);
if (auto procedure = call->callable().type_decoration.get<procedure_type>())
@@ -673,44 +659,76 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(unit *unit)
+ void resolving_visitor::visit(cast_expression *expression)
{
- for (type_declaration *const type : unit->types)
+ expression->value().accept(this);
+ expression->target().accept(this);
+ expression->type_decoration = this->current_type;
+ this->current_type = type();
+ }
+
+ void resolving_visitor::visit(record_constructor_expression *expression)
+ {
+ if (auto type_symbol = this->bag.lookup(expression->type_name.name()))
{
- type->accept(this);
+ if (auto type_info = type_symbol->is_type())
+ {
+ expression->type_decoration = type_info->symbol;
+ }
}
- for (variable_declaration *const variable : unit->variables)
+ else
{
- variable->accept(this);
+ add_error<declaration_error>(expression->type_name.position(),
+ expression->type_name.name(), declaration_error::kind::undeclared_type);
}
- for (procedure_declaration *const procedure : unit->procedures)
+ for (const field_initializer& initializer : expression->field_initializers)
{
- procedure->accept(this);
+ initializer.value().accept(this);
+ if (!expression->type_decoration.empty()
+ && lookup_field(expression->type_decoration, initializer.name()).empty())
+ {
+ add_error<member_error>(initializer.id().position(), initializer.id().name(),
+ expression->type_decoration, member_error::kind::not_found);
+ }
}
- if (unit->has_body())
+ this->current_type = type();
+ }
+
+ void resolving_visitor::visit(array_constructor_expression *expression)
+ {
+ for (auto *element : expression->elements)
{
- this->bag.enter();
- auto variable_type = lookup_primitive_type("Int");
- auto count_symbol = std::make_shared<variable_info>(variable_type, false);
- count_symbol->file = this->module_file;
- this->bag.enter("count", count_symbol);
+ element->accept(this);
+ }
+ const type element_type = expression->elements.front()->type_decoration;
+ expression->type_decoration = type(std::make_shared<array_type>(element_type,
+ expression->elements.size()));
+ this->current_type = type();
+ }
- variable_type = lookup_primitive_type("Word8");
- variable_type = type(std::make_shared<pointer_type>(variable_type));
- variable_type = type(std::make_shared<pointer_type>(variable_type));
- auto parameters_symbol = std::make_shared<variable_info>(variable_type, false);
- parameters_symbol->file = this->module_file;
- this->bag.enter("parameters", parameters_symbol);
+ void resolving_visitor::visit(slicing_expression *expression)
+ {
+ expression->base().accept(this);
+ auto resolved_base = resolve_underlying_type(expression->base().type_decoration);
- for (statement *const statement : unit->entry_point)
- {
- statement->accept(this);
- }
- this->bag.leave();
+ if (auto pointer = resolved_base.get<pointer_type>())
+ {
+ expression->type_decoration = type(std::make_shared<slice_type>(pointer->base));
+ }
+ else if (auto array = resolved_base.get<array_type>())
+ {
+ expression->type_decoration = type(std::make_shared<slice_type>(array->base));
+ }
+ else if (auto slice = resolved_base.get<slice_type>())
+ {
+ expression->type_decoration = type(slice);
}
+ expression->start().accept(this);
+ expression->end().accept(this);
+ this->current_type = type();
}
- void name_analysis_visitor::visit(traits_expression *trait)
+ void resolving_visitor::visit(traits_expression *trait)
{
if (!trait->arguments.empty())
{
@@ -734,9 +752,10 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(binary_expression *expression)
+ void resolving_visitor::visit(binary_expression *expression)
{
- walking_visitor::visit(expression);
+ expression->lhs().accept(this);
+ expression->rhs().accept(this);
switch (expression->operation())
{
@@ -767,9 +786,9 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(unary_expression *expression)
+ void resolving_visitor::visit(unary_expression *expression)
{
- walking_visitor::visit(expression);
+ expression->operand().accept(this);
if (expression->operation() == unary_operator::reference)
{
@@ -783,9 +802,40 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(array_access_expression *expression)
+ void resolving_visitor::visit(named_expression *expression)
{
- walking_visitor::visit(expression);
+ this->current_type = type();
+
+ if (auto unresolved_alias = this->bag.declared(expression->name))
+ {
+ this->current_type = type(unresolved_alias);
+ }
+ else if (auto from_symbol_table = this->bag.lookup(expression->name))
+ {
+ if (auto type_symbol = from_symbol_table->is_type())
+ {
+ this->current_type = type_symbol->symbol;
+ }
+ else if (auto variable_symbol = from_symbol_table->is_variable())
+ {
+ expression->type_decoration = variable_symbol->symbol;
+ }
+ else if (auto procedure_symbol = from_symbol_table->is_procedure())
+ {
+ expression->type_decoration = type(std::make_shared<procedure_type>(procedure_symbol->symbol));
+ }
+ }
+ else
+ {
+ add_error<declaration_error>(expression->position(),
+ expression->name, declaration_error::kind::undeclared_symbol);
+ }
+ }
+
+ void resolving_visitor::visit(array_access_expression *expression)
+ {
+ expression->base().accept(this);
+ expression->index().accept(this);
auto resolved_base = resolve_underlying_type(expression->base().type_decoration);
if (auto range_base = get_range_base_type(resolved_base))
@@ -800,9 +850,9 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(field_access_expression *expression)
+ void resolving_visitor::visit(field_access_expression *expression)
{
- walking_visitor::visit(expression);
+ expression->base().accept(this);
// Handling field access on a type (only valid for enumerations).
if (!this->current_type.empty())
@@ -845,11 +895,12 @@ namespace elna::boot
expression->base().type_decoration);
}
}
+ this->current_type = type();
}
- void name_analysis_visitor::visit(dereference_expression *expression)
+ void resolving_visitor::visit(dereference_expression *expression)
{
- walking_visitor::visit(expression);
+ expression->base().accept(this);
if (auto pointer = resolve_underlying_type(expression->base().type_decoration).get<pointer_type>())
{
@@ -858,67 +909,7 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(for_statement *statement)
- {
- statement->range().accept(this);
- auto resolved_range = resolve_underlying_type(statement->range().type_decoration);
- const type control_variable_base_type = get_range_base_type(resolved_range);
- const type control_variable_pointer_type = type(std::make_shared<pointer_type>(control_variable_base_type));
- this->current_type = type(std::make_shared<constant_type>(control_variable_pointer_type));
-
- statement->symbols = this->bag.enter();
-
- register_variable(statement->control_variable.name(), false, statement->control_variable.position());
- if (statement->counter != nullptr)
- {
- this->current_type = lookup_primitive_type("Word");
- register_variable(statement->counter->name(), false, statement->counter->position());
- }
- for (auto *body_statement : statement->body)
- {
- body_statement->accept(this);
- }
- this->bag.leave();
- }
-
- void name_analysis_visitor::visit(cast_expression *expression)
- {
- walking_visitor::visit(expression);
- expression->type_decoration = this->current_type;
- this->current_type = type();
- }
-
- void name_analysis_visitor::visit(named_expression *expression)
- {
- this->current_type = type();
-
- if (auto unresolved_alias = this->bag.declared(expression->name))
- {
- this->current_type = type(unresolved_alias);
- }
- else if (auto from_symbol_table = this->bag.lookup(expression->name))
- {
- if (auto type_symbol = from_symbol_table->is_type())
- {
- this->current_type = type_symbol->symbol;
- }
- else if (auto variable_symbol = from_symbol_table->is_variable())
- {
- expression->type_decoration = variable_symbol->symbol;
- }
- else if (auto procedure_symbol = from_symbol_table->is_procedure())
- {
- expression->type_decoration = type(std::make_shared<procedure_type>(procedure_symbol->symbol));
- }
- }
- else
- {
- add_error<declaration_error>(expression->position(),
- expression->name, declaration_error::kind::undeclared_symbol);
- }
- }
-
- void name_analysis_visitor::visit(literal<integer_literal> *literal)
+ void resolving_visitor::visit(literal<integer_literal> *literal)
{
if (literal->value.is_signed())
{
@@ -947,7 +938,7 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(literal<float_literal> *literal)
+ void resolving_visitor::visit(literal<float_literal> *literal)
{
literal->type_decoration = literal->value.format() == float_literal::format_kind::binary32
? lookup_primitive_type("Single")
@@ -955,37 +946,47 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(literal<bool> *literal)
+ void resolving_visitor::visit(literal<bool> *literal)
{
literal->type_decoration = lookup_primitive_type("Bool");
this->current_type = type();
}
- void name_analysis_visitor::visit(literal<std::uint32_t> *literal)
+ void resolving_visitor::visit(literal<std::uint32_t> *literal)
{
literal->type_decoration = lookup_primitive_type("Char");
this->current_type = type();
}
- void name_analysis_visitor::visit(literal<std::nullptr_t> *literal)
+ void resolving_visitor::visit(literal<std::nullptr_t> *literal)
{
literal->type_decoration = lookup_primitive_type("Pointer");
this->current_type = type();
}
- void name_analysis_visitor::visit(literal<std::string> *literal)
+ void resolving_visitor::visit(literal<std::string> *literal)
{
literal->type_decoration = type(std::make_shared<slice_type>(
type(std::make_shared<constant_type>(lookup_primitive_type("Word8")))));
this->current_type = type();
}
+ declaration_visitor::declaration_visitor(symbol_bag& bag, const target_info& target,
+ const std::filesystem::path& module_path)
+ : resolving_visitor(bag, target, module_path)
+ {
+ }
+
void declaration_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);
@@ -994,30 +995,109 @@ namespace elna::boot
void declaration_visitor::visit(type_declaration *declaration)
{
- const std::string& type_identifier = declaration->identifier.name();
+ declaration->underlying_type().accept(this);
+ auto resolved = this->bag.resolve(declaration->identifier.name(), this->current_type);
+ auto info = std::make_shared<type_info>(type(resolved));
- if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second)
+ info->exported = declaration->identifier.exported();
+ info->position.emplace(declaration->position());
+ info->file = this->module_file;
+
+ if (!this->bag.enter(declaration->identifier.name(), info))
{
- add_error<declaration_error>(declaration->identifier.id().position(),
- declaration->identifier.id().name(),
- declaration_error::redefinition{ .original = declaration->position(), .file = {} });
+ auto original = this->bag.lookup(declaration->identifier.name());
+ add_error<declaration_error>(declaration->identifier.id().position(), declaration->identifier.name(),
+ declaration_error::redefinition{ .original = original->position,
+ .file = this->redefinition_file(original) });
}
}
void declaration_visitor::visit(procedure_declaration *declaration)
{
- if (!declaration->body.has_value())
+ auto [heading, parameter_names] = build_procedure(declaration->heading());
+ std::shared_ptr<procedure_info> info;
+
+ if (declaration->body.has_value())
{
- return;
+ info = std::make_shared<procedure_info>(heading, std::move(parameter_names), this->bag.enter());
+ auto name_iterator = std::cbegin(info->names);
+ auto type_iterator = std::cbegin(info->symbol.parameters);
+
+ while (name_iterator != std::cend(info->names) && type_iterator != std::cend(info->symbol.parameters))
+ {
+ auto variable_symbol = register_variable(*name_iterator, *type_iterator,
+ declaration->heading().position());
+ variable_symbol->exported = false;
+
+ ++name_iterator;
+ ++type_iterator;
+ }
+ this->bag.leave();
}
- for (variable_declaration *const variable : declaration->body.value().variables)
+ else
{
- variable->accept(this);
+ info = std::make_shared<procedure_info>(heading, std::move(parameter_names));
+ }
+ info->exported = declaration->identifier.exported();
+ info->position.emplace(declaration->position());
+ info->file = this->module_file;
+ if (!this->bag.enter(declaration->identifier.name(), info))
+ {
+ auto original = this->bag.lookup(declaration->identifier.name());
+ add_error<declaration_error>(declaration->identifier.id().position(), declaration->identifier.name(),
+ declaration_error::redefinition{ .original = original->position,
+ .file = this->redefinition_file(original) });
}
}
- void declaration_visitor::visit(variable_declaration *declaration)
+ void declaration_visitor::visit(for_statement *)
{
+ __builtin_unreachable();
+ }
+
+ name_analysis_visitor::name_analysis_visitor(symbol_bag& bag, const target_info& target,
+ const std::filesystem::path& module_path)
+ : resolving_visitor(bag, target, module_path)
+ {
+ }
+
+ void name_analysis_visitor::visit(unit *unit)
+ {
+ for (procedure_declaration *const procedure : unit->procedures)
+ {
+ procedure->accept(this);
+ }
+ if (unit->has_body())
+ {
+ this->bag.enter();
+ auto variable_type = lookup_primitive_type("Int");
+ auto count_symbol = std::make_shared<variable_info>(variable_type, false);
+ count_symbol->file = this->module_file;
+ this->bag.enter("count", count_symbol);
+
+ variable_type = lookup_primitive_type("Word8");
+ variable_type = type(std::make_shared<pointer_type>(variable_type));
+ variable_type = type(std::make_shared<pointer_type>(variable_type));
+ auto parameters_symbol = std::make_shared<variable_info>(variable_type, false);
+ parameters_symbol->file = this->module_file;
+ this->bag.enter("parameters", parameters_symbol);
+
+ for (statement *const statement : unit->entry_point)
+ {
+ statement->accept(this);
+ }
+ this->bag.leave();
+ }
+ }
+
+ void name_analysis_visitor::visit(type_declaration *)
+ {
+ __builtin_unreachable();
+ }
+
+ void name_analysis_visitor::visit(variable_declaration *declaration)
+ {
+ resolving_visitor::visit(declaration);
for (const identifier_definition& variable_identifier : declaration->identifiers)
{
if (variable_identifier.exported())
@@ -1027,4 +1107,51 @@ namespace elna::boot
}
}
}
+
+ void name_analysis_visitor::visit(procedure_declaration *declaration)
+ {
+ const std::shared_ptr<procedure_info> info = this->bag.lookup(declaration->identifier.name())->is_procedure();
+
+ if (declaration->body.has_value())
+ {
+ this->bag.enter(info->scope);
+ for (variable_declaration *const variable : declaration->body.value().variables)
+ {
+ variable->accept(this);
+ }
+ for (statement *const statement : declaration->body.value().entry_point)
+ {
+ statement->accept(this);
+ }
+ if (declaration->body.value().return_expression != nullptr)
+ {
+ declaration->body.value().return_expression->accept(this);
+ }
+ this->bag.leave();
+ }
+ }
+
+ void name_analysis_visitor::visit(for_statement *statement)
+ {
+ statement->range().accept(this);
+ auto resolved_range = resolve_underlying_type(statement->range().type_decoration);
+ const type control_variable_base_type = get_range_base_type(resolved_range);
+ const type control_variable_pointer_type = type(std::make_shared<pointer_type>(control_variable_base_type));
+ const type control_variable_const_type = type(std::make_shared<constant_type>(control_variable_pointer_type));
+
+ statement->symbols = this->bag.enter();
+
+ register_variable(statement->control_variable.name(), control_variable_const_type,
+ statement->control_variable.position());
+ if (statement->counter != nullptr)
+ {
+ register_variable(statement->counter->name(), lookup_primitive_type("Word"),
+ statement->counter->position());
+ }
+ for (auto *body_statement : statement->body)
+ {
+ body_statement->accept(this);
+ }
+ this->bag.leave();
+ }
}
diff --git a/boot/symbol.cc b/boot/symbol.cc
index 7ef09c9..4d6832d 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -420,6 +420,11 @@ namespace elna::boot
return unresolved_declaration;
}
+ bool symbol_bag::forward_declare(const std::string& symbol_name, std::shared_ptr<alias_type> forward_declaration)
+ {
+ return this->unresolved.insert({ symbol_name, std::move(forward_declaration) }).second;
+ }
+
void symbol_bag::add_import(const std::shared_ptr<symbol_table>& symbols)
{
this->imports.push_front(symbols);
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index a2e9437..cd3c4f7 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -66,8 +66,7 @@ namespace elna::boot
};
/**
- * \c const qualifier used incorrectly — wrong position or
- * duplicate.
+ * \c const qualifier used incorrectly — wrong position or duplicate.
*/
class const_qualifier_error final : public diagnostic
{
@@ -126,62 +125,78 @@ namespace elna::boot
type base_type;
};
+ /**
+ * Makes every type name of the module referencable before any underlying
+ * type is resolved.
+ *
+ * Enters an unresolved alias placeholder for each type declaration, so
+ * declarations within the type section can reference each other regardless
+ * of order.
+ */
+ class forward_declaration_visitor final : public empty_visitor, public diagnostic_container
+ {
+ public:
+ forward_table unresolved;
+
+ void visit(unit *unit) override;
+ void visit(type_declaration *declaration) override;
+ };
+
/**
- * Performs name analysis.
+ * Shared name resolution machinery for the declaration and the body passes.
+ *
+ * Resolves type expressions into semantic types, decorates expressions with
+ * their type and evaluates constant expressions.
*/
- class name_analysis_visitor final : public walking_visitor, public diagnostic_container
+ class resolving_visitor : public parser_visitor, public diagnostic_container
{
+ protected:
type current_type;
- symbol_bag bag;
+ symbol_bag& bag;
evaluator constant_evaluator;
- /// Path of the module being analyzed, recorded in the symbol infos.
std::filesystem::path module_file;
+ resolving_visitor(symbol_bag& bag, const target_info& target,
+ const std::filesystem::path& module_path);
+
std::pair<procedure_type, std::vector<std::string>> build_procedure(
procedure_type_expression& expression);
ordered_map<type> build_composite_type(const std::vector<field_declaration>& fields,
ordered_map<field_origin>& field_names, const type& aggregate);
- std::shared_ptr<variable_info> register_variable(const std::string& name,
- const bool is_extern, const source_position position);
-
- /**
- * File of \p original for a redefinition note.
- *
- * \param original Symbol the error redefines.
- * \return Module of the original declaration; empty when it is in the
- * module being analyzed, so the note renders under the
- * error's own file.
- */
- std::filesystem::path redefinition_file(const std::shared_ptr<info>& original) const;
-
type lookup_primitive_type(const std::string& name);
- type lookup_field(const type& composite_type, const std::string& field_name);
+ std::filesystem::path redefinition_file(const std::shared_ptr<info>& original) const;
+ std::shared_ptr<variable_info> register_variable(const std::string& name,
+ const type& variable_type, const source_position position, const bool is_extern = false);
std::optional<type> lookup_pointer_like_field(const std::string& field_name,
const type& element_type);
+ type lookup_field(const type& composite_type, const std::string& field_name);
public:
- name_analysis_visitor(symbol_bag bag, const target_info& target,
- std::filesystem::path module_file);
+ void visit(variable_declaration *declaration) override;
void visit(array_type_expression *expression) override;
void visit(slice_type_expression *expression) override;
void visit(pointer_type_expression *expression) override;
void visit(constant_type_expression *expression) override;
- void visit(type_declaration *declaration) override;
void visit(record_type_expression *expression) override;
- void visit(record_constructor_expression *expression) override;
- void visit(array_constructor_expression *expression) override;
- void visit(slicing_expression *expression) override;
void visit(procedure_type_expression *expression) override;
void visit(enumeration_type_expression *expression) override;
void visit(extern_type_expression *) override;
- void visit(variable_declaration *declaration) override;
- void visit(procedure_declaration *declaration) override;
+ void visit(assign_statement *statement) override;
+ void visit(if_statement *statement) override;
+ void visit(import_declaration *) override;
+ void visit(while_statement *statement) override;
+ void visit(repeat_statement *statement) override;
+ void visit(defer_statement *statement) override;
+ void visit(empty_statement *) override;
+ void visit(case_statement *statement) override;
void visit(procedure_call *call) override;
- void visit(unit *unit) override;
void visit(cast_expression *expression) override;
+ void visit(record_constructor_expression *expression) override;
+ void visit(array_constructor_expression *expression) override;
+ void visit(slicing_expression *expression) override;
void visit(traits_expression *trait) override;
void visit(binary_expression *expression) override;
void visit(unary_expression *expression) override;
@@ -190,8 +205,6 @@ namespace elna::boot
void visit(field_access_expression *expression) override;
void visit(dereference_expression *expression) override;
- void visit(for_statement *statement) override;
-
void visit(literal<integer_literal> *literal) override;
void visit(literal<float_literal> *literal) override;
void visit(literal<bool> *literal) override;
@@ -201,16 +214,42 @@ namespace elna::boot
};
/**
- * Collects global declarations without resolving any symbols.
+ * Resolves declarations: fills type underlyings, registers global variables
+ * with their evaluated constant values and registers procedure headings
+ * together with their parameter scopes.
*/
- class declaration_visitor final : public empty_visitor, public diagnostic_container
+ class declaration_visitor final : public resolving_visitor
{
+ using resolving_visitor::visit;
+
public:
- forward_table unresolved;
+ declaration_visitor(symbol_bag& bag, const target_info& target,
+ const std::filesystem::path& module_path);
void visit(unit *unit) override;
void visit(type_declaration *declaration) override;
void visit(procedure_declaration *declaration) override;
+
+ [[noreturn]] void visit(for_statement *) override;
+ };
+
+ /**
+ * Analyzes procedure bodies and the module entry point against the complete
+ * symbol table.
+ */
+ class name_analysis_visitor final : public resolving_visitor
+ {
+ using resolving_visitor::visit;
+
+ public:
+ name_analysis_visitor(symbol_bag& bag, const target_info& target,
+ const std::filesystem::path& module_path);
+
+ void visit(unit *unit) override;
+ [[noreturn]] void visit(type_declaration *) override;
void visit(variable_declaration *declaration) override;
+ void visit(procedure_declaration *declaration) override;
+
+ void visit(for_statement *statement) override;
};
}
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index 5f0de28..2c47fcc 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -492,7 +492,7 @@ namespace elna::boot
* returns it if so.
*
* \param symbol_name Type name to look up.
- * \return Forward declaration or `nullptr` if the symbol is not declared.
+ * \return Forward declaration or \c nullptr if the symbol is not declared.
*/
std::shared_ptr<alias_type> declared(const std::string& symbol_name);
@@ -507,6 +507,15 @@ namespace elna::boot
std::shared_ptr<alias_type> resolve(const std::string& symbol_name, type& resolution);
/**
+ * Forward declares a type registering its stub.
+ *
+ * \param symbol_name Type name.
+ * \param forward_declaration Type alias to store.
+ * \return Whether the forward declaration took place.
+ */
+ bool forward_declare(const std::string& symbol_name, std::shared_ptr<alias_type> forward_declaration);
+
+ /**
* Add imported symbols to the scope.
*
* \param symbols Exported symbol table of another module.
diff --git a/testsuite/compilable/procedure_forward_reference.elna b/testsuite/compilable/procedure_forward_reference.elna
new file mode 100644
index 0000000..bbffc73
--- /dev/null
+++ b/testsuite/compilable/procedure_forward_reference.elna
@@ -0,0 +1,7 @@
+proc first(): Int
+return second() + 1
+
+proc second(): Int
+return 41
+
+end.