aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-09-07 22:02:15 +0200
committerEugen Wissner <belka@caraus.de>2026-09-07 23:39:53 +0200
commit72f5e82196d95008ed12c12de6bdccf24361e106 (patch)
tree8319324de8e2a5182dc3c1ddca00a837c2489ad2 /boot
parent47521ad6d85f9bd6caee39696ce40dff82cfa50d (diff)
downloadelna-72f5e82196d95008ed12c12de6bdccf24361e106.tar.gz
Make module entry point more procedure like
Diffstat (limited to 'boot')
-rw-r--r--boot/ast.cc12
-rw-r--r--boot/dependency.cc18
-rw-r--r--boot/lexer.ll1
-rw-r--r--boot/name_analysis.cc15
-rw-r--r--boot/parser.yy18
-rw-r--r--boot/type_check.cc80
6 files changed, 91 insertions, 53 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index b8647f4..edf1237 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -367,10 +367,15 @@ namespace elna::boot
}
if (unit->entry_point.has_value())
{
- traverse_body(this, unit->entry_point.value());
+ visit_entry_point(unit);
}
}
+ void walking_visitor::visit_entry_point(unit *unit)
+ {
+ traverse_body(this, unit->entry_point.value());
+ }
+
void walking_visitor::visit(type_declaration *declaration)
{
declaration->underlying_type().accept(this);
@@ -1033,10 +1038,13 @@ namespace elna::boot
std::vector<type_declaration *>&& types,
std::vector<variable_declaration *>&& variables,
std::vector<procedure_declaration *>&& procedures,
+ std::vector<identifier>&& parameters,
+ const source_position entry_position,
std::optional<procedure_body>&& body)
: node(position),
imports(std::move(imports)), types(std::move(types)), procedures(std::move(procedures)),
- variables(std::move(variables)), entry_point(std::move(body))
+ variables(std::move(variables)), entry_point(std::move(body)),
+ parameters(std::move(parameters)), entry_position(entry_position)
{
}
diff --git a/boot/dependency.cc b/boot/dependency.cc
index 9b52ce9..1eb5953 100644
--- a/boot/dependency.cc
+++ b/boot/dependency.cc
@@ -26,15 +26,23 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
- circular_import_error::circular_import_error(const source_position position,
- const std::string& module_name)
- : diagnostic(position), module_name(module_name)
+ import_error::import_error(const source_position position,
+ const std::string& module_name, const kind payload)
+ : diagnostic(position), module_name(module_name), m_payload(payload)
{
}
- std::string circular_import_error::what() const
+ std::string import_error::what() const
{
- return "Circular import of module '" + this->module_name + "'";
+ switch (this->m_payload)
+ {
+ case kind::circular:
+ return "Circular import of module '" + this->module_name + "'";
+ case kind::program:
+ return "Module '" + this->module_name + "' is a program and cannot be imported";
+ default:
+ __builtin_unreachable();
+ }
}
read_result read_source(std::istream& entry_point, const target_info& target)
diff --git a/boot/lexer.ll b/boot/lexer.ll
index bebfa67..5da70e5 100644
--- a/boot/lexer.ll
+++ b/boot/lexer.ll
@@ -71,6 +71,7 @@ EXPONENT [eE][+-]?[[:digit:]]+
this->location.step();
}
\n+ {
+ this->location.step();
}
if {
return yy::parser::make_IF(this->location);
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 77335d1..37c7ced 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -1131,6 +1131,7 @@ namespace elna::boot
{
add_error<declaration_format_error>(unit->position(),
declaration_format_error::kind::module_entry);
+ return;
}
else if (unit->parameters.size() == 1)
{
@@ -1150,18 +1151,12 @@ namespace elna::boot
}
this->bag.leave();
- info->position.emplace(unit->position());
+ info->position = unit->entry_position;
info->file = this->module_file;
- if (!this->bag.enter("", info))
- {
- auto original = this->bag.lookup("");
- symbol_declaration_error::redefinition original_definition{
- .original = original->position,
- .file = this->redefinition_file(original)
- };
- add_error<symbol_declaration_error>(unit->position(), "program", original_definition);
- }
+ // A unit has at most one entry point and an imported program is
+ // rejected before its symbols are merged, so this cannot collide.
+ this->bag.enter("", info);
}
}
diff --git a/boot/parser.yy b/boot/parser.yy
index dc1d4e8..ba611b5 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -157,7 +157,7 @@ along with GCC; see the file COPYING3. If not see
%type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition;
%type <std::vector<elna::boot::identifier_definition>> identifier_definitions;
%type <std::vector<std::string>> import_declaration;
-%type <std::vector<elna::boot::identifier>> identifiers;
+%type <std::vector<elna::boot::identifier>> required_identifiers optional_identifiers;
%type <std::vector<elna::boot::import_declaration *>> import_declarations import_part;
%type <std::unique_ptr<elna::boot::array_type_expression>> array_type_expression;
%%
@@ -167,10 +167,11 @@ program:
boot::unit *tree = new boot::unit(boot::make_position(@$), $1, $2, $3, $4);
driver.tree.reset(tree);
}
- | import_part type_part variable_part procedure_part "program" "(" ")" statement_part "end" "."
+ | import_part type_part variable_part procedure_part
+ "program" "(" optional_identifiers ")" procedure_body "end" "."
{
boot::unit *tree = new boot::unit(boot::make_position(@$), $1, $2, $3, $4,
- boot::procedure_body({}, std::move($8)));
+ std::move($7), boot::make_position(@5), std::move(*$9));
driver.tree.reset(tree);
}
procedure_body:
@@ -541,7 +542,7 @@ statements:
}
| statement { $$.push_back($1); }
field_declaration:
- identifiers ":" type_expression { $$ = std::make_pair($1, std::shared_ptr<boot::type_expression>($3)); }
+ required_identifiers ":" type_expression { $$ = std::make_pair($1, std::shared_ptr<boot::type_expression>($3)); }
required_fields:
field_declaration ";" required_fields
{
@@ -589,7 +590,7 @@ type_expression:
{
$$ = $2.release();
}
- | "(" identifiers ")"
+ | "(" required_identifiers ")"
{
$$ = new boot::enumeration_type_expression(boot::make_position(@$), $2);
}
@@ -597,13 +598,16 @@ type_expression:
{
$$ = new boot::named_expression(boot::make_position(@$), $1);
}
-identifiers:
- identifier "," identifiers
+required_identifiers:
+ identifier "," required_identifiers
{
$$ = $3;
$$.emplace($$.cbegin(), std::move(*$1));
}
| identifier { $$.emplace_back(std::move(*$1)); }
+optional_identifiers:
+ required_identifiers { $$ = $1; }
+ | %empty {}
variable_declaration:
identifier_definitions ":" type_expression
{
diff --git a/boot/type_check.cc b/boot/type_check.cc
index da60a21..cdc78df 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -114,6 +114,12 @@ namespace elna::boot
}, this->payload);
}
+ /// The program body is the only callable without a name.
+ static std::string describe_callable(const std::string& identifier)
+ {
+ return identifier.empty() ? "Program body" : "Procedure '" + identifier + "'";
+ }
+
type_mismatch_error::type_mismatch_error(const source_position position,
type actual, payload_type payload)
: diagnostic(position), actual(std::move(actual)), payload(std::move(payload))
@@ -140,12 +146,12 @@ namespace elna::boot
{
if (!this->actual.empty())
{
- return "Procedure '" + payload.identifier
- + "' does not return a value, but return expression has type '"
+ return describe_callable(payload.identifier)
+ + " does not return a value, but return expression has type '"
+ this->actual.to_string() + "'";
}
- return "Procedure '" + payload.identifier
- + "' is expected to return, but does not have a return statement";
+ return describe_callable(payload.identifier)
+ + " is expected to return, but does not have a return statement";
}
else if constexpr (std::is_same_v<T, unary>)
@@ -660,35 +666,41 @@ namespace elna::boot
}
if (declaration->body.has_value())
{
- if (declaration->body.value().return_expression != nullptr)
- {
- const expression *return_expr = declaration->body.value().return_expression;
- type const return_type = this->current_procedure->symbol.return_type.proper_type;
+ check_return(declaration->body.value(), declaration->position(),
+ declaration->identifier.name());
+ this->bag.leave();
+ }
+ this->current_procedure.reset();
+ }
- if (!return_type.empty())
- {
- if (!is_assignable_from(return_type, return_expr->type_decoration))
- {
- add_error<type_mismatch_error>(return_expr->position(),
- return_expr->type_decoration,
- type_mismatch_error::expected_type{ return_type });
- }
- }
- else
- {
- add_error<type_mismatch_error>(return_expr->position(),
- return_expr->type_decoration,
- type_mismatch_error::return_type{ .identifier = declaration->identifier.name() });
- }
- }
- else if (declaration->heading().return_type.proper_type != nullptr)
+ void type_analysis_visitor::check_return(const procedure_body& body,
+ const source_position position, const std::string& name)
+ {
+ const type return_type = this->current_procedure->symbol.return_type.proper_type;
+
+ if (body.return_expression == nullptr)
+ {
+ if (!return_type.empty())
{
- add_error<type_mismatch_error>(declaration->position(), type(),
- type_mismatch_error::return_type{ .identifier = declaration->identifier.name() });
+ add_error<type_mismatch_error>(position, type(),
+ type_mismatch_error::return_type{ .identifier = name });
}
- this->bag.leave();
+ return;
+ }
+ const expression *return_expr = body.return_expression;
+
+ if (return_type.empty())
+ {
+ add_error<type_mismatch_error>(return_expr->position(),
+ return_expr->type_decoration,
+ type_mismatch_error::return_type{ .identifier = name });
+ }
+ else if (!is_assignable_from(return_type, return_expr->type_decoration))
+ {
+ add_error<type_mismatch_error>(return_expr->position(),
+ return_expr->type_decoration,
+ type_mismatch_error::expected_type{ return_type });
}
- this->current_procedure.reset();
}
void type_analysis_visitor::visit(assign_statement *statement)
@@ -714,6 +726,16 @@ namespace elna::boot
}
}
+ void type_analysis_visitor::visit_entry_point(unit *unit)
+ {
+ this->current_procedure = this->bag.lookup("")->is_procedure();
+ this->bag.enter(this->current_procedure->scope);
+ walking_visitor::visit_entry_point(unit);
+ check_return(unit->entry_point.value(), unit->entry_position.value(), "");
+ this->bag.leave();
+ this->current_procedure.reset();
+ }
+
void type_analysis_visitor::visit(variable_declaration *declaration)
{
walking_visitor::visit(declaration);