diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/ast.cc | 12 | ||||
| -rw-r--r-- | boot/dependency.cc | 18 | ||||
| -rw-r--r-- | boot/lexer.ll | 1 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 15 | ||||
| -rw-r--r-- | boot/parser.yy | 18 | ||||
| -rw-r--r-- | boot/type_check.cc | 80 |
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); |
