diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/ast.cc | 65 | ||||
| -rw-r--r-- | boot/lexer.ll | 10 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 98 | ||||
| -rw-r--r-- | boot/parser.yy | 29 | ||||
| -rw-r--r-- | boot/symbol.cc | 43 | ||||
| -rw-r--r-- | boot/type_check.cc | 63 |
6 files changed, 162 insertions, 146 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index a50d3d3..69d72fb 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -91,6 +91,11 @@ namespace elna::boot __builtin_unreachable(); } + void empty_visitor::visit(repeat_statement *) + { + __builtin_unreachable(); + } + void empty_visitor::visit(for_statement *) { __builtin_unreachable(); @@ -291,14 +296,18 @@ namespace elna::boot } } - void walking_visitor::visit(for_statement *statement) + void walking_visitor::visit(repeat_statement *statement) { - statement->initial_value().accept(this); - statement->final_value().accept(this); - if (statement->step != nullptr) + statement->condition().accept(this); + for (auto *body_statement : statement->body) { - statement->step->accept(this); + body_statement->accept(this); } + } + + void walking_visitor::visit(for_statement *statement) + { + statement->range().accept(this); for (auto *body_statement : statement->body) { body_statement->accept(this); @@ -1541,19 +1550,42 @@ namespace elna::boot } } + repeat_statement::repeat_statement(const source_position position, std::vector<statement *>&& body, + expression *condition) + : node(position), m_condition(condition), body(std::move(body)) + { + } + + repeat_statement::~repeat_statement() + { + delete this->m_condition; + for (const statement *body_statement : this->body) + { + delete body_statement; + } + } + + void repeat_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& repeat_statement::condition() + { + return *this->m_condition; + } + for_statement::for_statement(const source_position position, identifier&& control_variable, - expression *initial_value, expression *final_value, - std::vector<statement *>&& body, expression *step) - : node(position), m_initial_value(initial_value), m_final_value(final_value), - control_variable(std::move(control_variable)), body(std::move(body)), step(step) + expression *range, std::vector<statement *>&& body, identifier *const counter = nullptr) + : node(position), m_range(range), control_variable(std::move(control_variable)), + counter(counter), body(std::move(body)) { } for_statement::~for_statement() { - delete this->m_initial_value; - delete this->m_final_value; - delete this->step; + delete this->m_range; + delete this->counter; for (const statement *body_statement : this->body) { @@ -1566,14 +1598,9 @@ namespace elna::boot visitor->visit(this); } - expression& for_statement::initial_value() - { - return *this->m_initial_value; - } - - expression& for_statement::final_value() + expression& for_statement::range() { - return *this->m_final_value; + return *this->m_range; } const char *print_binary_operator(const binary_operator operation) diff --git a/boot/lexer.ll b/boot/lexer.ll index c363ddd..89fe85c 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -86,8 +86,14 @@ while { for { return yy::parser::make_FOR(this->location); } -by { - return yy::parser::make_BY(this->location); +repeat { + return yy::parser::make_REPEAT(this->location); +} +until { + return yy::parser::make_UNTIL(this->location); +} +with { + return yy::parser::make_WITH(this->location); } do { return yy::parser::make_DO(this->location); diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index f932a5d..0d25039 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -21,31 +21,32 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - symbol_error::symbol_error(const source_position position, payload_type payload) + declaration_error::declaration_error(const source_position position, payload_type payload) : error(position), payload(std::move(payload)) { } - std::string symbol_error::what() const + std::string declaration_error::what() const { - return std::visit([](const auto& pay) -> std::string { - using T = std::decay_t<decltype(pay)>; + return std::visit([](const auto& payload) -> std::string { + using T = std::decay_t<decltype(payload)>; + if constexpr (std::is_same_v<T, undeclared>) { - return "Type '" + pay.name + "' not declared"; + return "Type '" + payload.name + "' not declared"; } else if constexpr (std::is_same_v<T, local_export>) { - return "Local symbol '" + pay.name + "' cannot be exported"; + return "Local symbol '" + payload.name + "' cannot be exported"; } else if constexpr (std::is_same_v<T, redefinition>) { - return "Symbol '" + pay.name + "' has been already defined"; + return "Symbol '" + payload.name + "' has been already defined"; } - }, payload); + }, this->payload); } - std::optional<std::pair<std::string, source_position>> symbol_error::note() const + std::optional<std::pair<std::string, source_position>> declaration_error::note() const { if (const auto *redef = std::get_if<redefinition>(&payload)) { @@ -240,16 +241,9 @@ namespace elna::boot return lookup_field(record->base, field_name); } } - else if (auto array = resolved_type.get<array_type>()) - { - if (auto field = lookup_pointer_like_field(field_name, array->base)) - { - return field.value(); - } - } - else if (auto slice = resolved_type.get<slice_type>()) + else if (auto range_base = get_range_base_type(resolved_type)) { - if (auto field = lookup_pointer_like_field(field_name, slice->base)) + if (auto field = lookup_pointer_like_field(field_name, range_base)) { return field.value(); } @@ -388,8 +382,8 @@ namespace elna::boot } else { - add_error<symbol_error>(expression->base.value().position(), - symbol_error::undeclared{.name = expression->base.value().name()}); + add_error<declaration_error>(expression->base.value().position(), + declaration_error::undeclared{.name = expression->base.value().name()}); this->current_type = type(); return; } @@ -417,8 +411,8 @@ namespace elna::boot } else { - add_error<symbol_error>(expression->type_name.position(), - symbol_error::undeclared{.name = expression->type_name.name()}); + add_error<declaration_error>(expression->type_name.position(), + declaration_error::undeclared{.name = expression->type_name.name()}); } for (const field_initializer& initializer : expression->field_initializers) { @@ -426,8 +420,8 @@ namespace elna::boot if (!expression->type_decoration.empty() && lookup_field(expression->type_decoration, initializer.name()).empty()) { - add_error<symbol_error>(initializer.id().position(), - symbol_error::undeclared{.name = initializer.id().name()}); + add_error<declaration_error>(initializer.id().position(), + declaration_error::undeclared{.name = initializer.id().name()}); } } } @@ -509,8 +503,8 @@ for (const auto& member : expression->members) if (!this->bag.enter(name, variable_symbol)) { auto original = this->bag.lookup(name); - add_error<symbol_error>(position, - symbol_error::redefinition{.name = name, .original = original->position}); + add_error<declaration_error>(position, + declaration_error::redefinition{.name = name, .original = original->position}); } return variable_symbol; } @@ -654,8 +648,8 @@ for (const auto& member : expression->members) } else { - add_error<symbol_error>(trait->name.position(), - symbol_error::undeclared{.name = trait->name.name()}); + add_error<declaration_error>(trait->name.position(), + declaration_error::undeclared{.name = trait->name.name()}); } } @@ -711,18 +705,12 @@ for (const auto& member : expression->members) walking_visitor::visit(expression); auto resolved_base = resolve_underlying_type(expression->base().type_decoration); - if (auto array = resolved_base.get<array_type>()) - { - expression->type_decoration = array->base; - } - else if (auto slice = resolved_base.get<slice_type>()) - { - expression->type_decoration = slice->base; - } - // Elements of a constant array are constant themselves since a static - // array is a holistic type. - if (!expression->type_decoration.empty()) + if (auto range_base = get_range_base_type(resolved_base)) { + expression->type_decoration = range_base; + + // Elements of a constant array are constant themselves since a static + // array is a holistic type. expression->type_decoration = qualify_member_type(expression->type_decoration, expression->base().type_decoration); } @@ -763,18 +751,20 @@ for (const auto& member : expression->members) void name_analysis_visitor::visit(for_statement *statement) { - statement->initial_value().accept(this); - const type control_variable_type = type(std::make_shared<constant_type>(this->current_type)); - auto initial_value_info = std::make_shared<variable_info>(control_variable_type, false); + 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->final_value().accept(this); - if (statement->step != nullptr) - { - statement->step->accept(this); - } statement->symbols = this->bag.enter(); - this->bag.enter(statement->control_variable.name(), initial_value_info); + 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); @@ -813,8 +803,8 @@ for (const auto& member : expression->members) } else { - add_error<symbol_error>(expression->position(), - symbol_error::undeclared{.name = expression->name}); + add_error<declaration_error>(expression->position(), + declaration_error::undeclared{.name = expression->name}); } } @@ -890,8 +880,8 @@ for (const auto& member : expression->members) if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second) { - add_error<symbol_error>(declaration->identifier.id().position(), - symbol_error::redefinition{.name = declaration->identifier.id().name(), + add_error<declaration_error>(declaration->identifier.id().position(), + declaration_error::redefinition{.name = declaration->identifier.id().name(), .original = declaration->position()}); } } @@ -914,8 +904,8 @@ for (const auto& member : expression->members) { if (variable_identifier.exported()) { - add_error<symbol_error>(variable_identifier.id().position(), - symbol_error::local_export{.name = variable_identifier.id().name()}); + add_error<declaration_error>(variable_identifier.id().position(), + declaration_error::local_export{.name = variable_identifier.id().name()}); } } } diff --git a/boot/parser.yy b/boot/parser.yy index 7d93dde..4fdbf84 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -100,7 +100,7 @@ along with GCC; see the file COPYING3. If not see RECORD "record" EXTERN "extern" IF "if" THEN "then" ELSE "else" ELSIF "elsif" - WHILE "while" DO "do" FOR "for" BY "by" + WHILE "while" DO "do" FOR "for" REPEAT "repeat" UNTIL "until" WITH "with" RETURN "return" IMPORT "import" BEGIN_BLOCK "begin" @@ -130,8 +130,7 @@ along with GCC; see the file COPYING3. If not see %type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part; %type <elna::boot::type_expression *> type_expression; %type <std::vector<elna::boot::type_expression *>> type_expressions; -%type <elna::boot::traits_expression *> traits_expression; -%type <elna::boot::expression *> expression operand simple_expression procedure_return by_step; +%type <elna::boot::expression *> expression operand simple_expression procedure_return; %type <elna::boot::unary_expression *> unary_expression; %type <elna::boot::binary_expression *> binary_expression; %type <std::vector<elna::boot::expression *>> expressions actual_parameter_list; @@ -152,7 +151,7 @@ along with GCC; see the file COPYING3. If not see %type <std::vector<elna::boot::field_initializer>> field_initializers; %type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements; %type <std::vector<elna::boot::statement *> *> else_statements; -%type <std::unique_ptr<elna::boot::identifier>> identifier; +%type <std::unique_ptr<elna::boot::identifier>> identifier with_counter; %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; @@ -210,8 +209,8 @@ call_expression: designator_expression actual_parameter_list { $$ = new boot::procedure_call(boot::make_position(@$), $1, $2); } -by_step: - "by" expression { $$ = $2; } +with_counter: + "with" identifier { $$ = $2; } | %empty { $$ = nullptr; } elsif_do_statements: "elsif" expression "do" statements elsif_do_statements @@ -243,16 +242,14 @@ literal: | CHARACTER { $$ = new boot::literal<unsigned char>(boot::make_position(@$), $1.at(0)); } | "nil" { $$ = new boot::literal<std::nullptr_t>(boot::make_position(@$), nullptr); } | STRING { $$ = new boot::literal<std::string>(boot::make_position(@$), $1); } -traits_expression: - TRAIT "(" type_expressions ")" +simple_expression: + literal { $$ = $1; } + | designator_expression { $$ = $1; } + | TRAIT "(" type_expressions ")" { $$ = new boot::traits_expression(boot::make_position(@$), boot::identifier($1, boot::make_position(@1)), $3); } -simple_expression: - literal { $$ = $1; } - | designator_expression { $$ = $1; } - | traits_expression { $$ = $1; } | "cast" "(" expression ":" type_expression ")" { $$ = new boot::cast_expression(boot::make_position(@$), $5, $3); @@ -389,9 +386,13 @@ statement: boot::conditional_statements *body = new boot::conditional_statements($2, $4); $$ = new boot::while_statement(boot::make_position(@$), body, $5); } - | "for" identifier ":=" expression "to" expression by_step "do" statements "end" + | "repeat" statements "until" expression + { + $$ = new boot::repeat_statement(boot::make_position(@$), $2, $4); + } + | "for" identifier "of" expression with_counter "do" statements "end" { - $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, $6, $9, $7); + $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, $7, $5.release()); } | "if" expression "then" statements elsif_then_statements else_statements "end" { diff --git a/boot/symbol.cc b/boot/symbol.cc index da948fd..5e303fb 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -155,6 +155,11 @@ namespace elna::boot return resolved_this.empty() && resolved_that.empty(); } + type::operator bool() const + { + return !empty(); + } + bool type::empty() const { return std::holds_alternative<std::monostate>(payload); @@ -458,35 +463,41 @@ namespace elna::boot bool is_numeric_type(const type& checked) { - type const resolved = resolve_underlying_type(checked); - - return is_primitive_type(resolved, "Int") - || is_primitive_type(resolved, "Word") - || is_primitive_type(resolved, "Float"); + return is_primitive_type(checked, "Int") + || is_primitive_type(checked, "Word") + || is_primitive_type(checked, "Float"); } bool is_integral_type(const type& checked) { - type const resolved = resolve_underlying_type(checked); - - return is_primitive_type(resolved, "Int") - || is_primitive_type(resolved, "Word"); + return is_primitive_type(checked, "Int") + || is_primitive_type(checked, "Word"); } bool is_discrete_type(const type& checked) { - type const resolved = resolve_underlying_type(checked); - - return is_primitive_type(resolved, "Int") - || is_primitive_type(resolved, "Word") - || is_primitive_type(resolved, "Bool") - || is_primitive_type(resolved, "Char"); + return is_integral_type(checked) + || is_primitive_type(checked, "Bool") + || is_primitive_type(checked, "Char"); } bool is_any_pointer_type(const type& checked) { return checked.get<pointer_type>() != nullptr || checked.get<procedure_type>() != nullptr - || is_primitive_type(resolve_underlying_type(checked), "Pointer"); + || is_primitive_type(checked, "Pointer"); + } + + type get_range_base_type(const type& range) + { + if (auto array = range.get<array_type>()) + { + return array->base; + } + else if (auto slice = range.get<slice_type>()) + { + return slice->base; + } + return type(); } } diff --git a/boot/type_check.cc b/boot/type_check.cc index 1689e65..761c4b2 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -93,14 +93,23 @@ namespace elna::boot + "' is expected to return, but does not have a return statement"; } - base_type_error::base_type_error(type actual, const source_position position) - : error(position), actual(std::move(actual)) + type_kind_error::type_kind_error(const source_position position, kind type_kind, const type& actual) + : error(position), type_kind(type_kind), actual(actual) { } - std::string base_type_error::what() const + std::string type_kind_error::what() const { - return "'" + actual.to_string() + "' is not a record type"; + switch (this->type_kind) + { + case kind::record_base: + return "'" + this->actual.to_string() + "' is not a record type"; + case kind::for_loop: + return "for-loop variable must be an array or a slice, got '" + + this->actual.to_string() + "'"; + default: + __builtin_unreachable(); + } } argument_count_error::argument_count_error(std::size_t expected, std::size_t actual, @@ -467,31 +476,13 @@ namespace elna::boot void type_analysis_visitor::visit(for_statement *statement) { - statement->initial_value().accept(this); - type const initial_type = resolve_underlying_type(statement->initial_value().type_decoration); + statement->range().accept(this); + auto resolved_range = resolve_underlying_type(statement->range().type_decoration); - if (!is_integral_type(initial_type)) - { - add_error<for_loop_type_error>( - statement->initial_value().position(), - statement->initial_value().type_decoration); - } - statement->final_value().accept(this); - if (!is_assignable_from(initial_type, statement->final_value().type_decoration)) - { - add_error<type_mismatch_error>( - statement->final_value().position(), - initial_type, statement->final_value().type_decoration); - } - if (statement->step != nullptr) + if (!get_range_base_type(resolved_range)) { - statement->step->accept(this); - if (!is_assignable_from(initial_type, statement->step->type_decoration)) - { - add_error<type_mismatch_error>( - statement->step->position(), - initial_type, statement->step->type_decoration); - } + add_error<type_kind_error>(statement->range().position(), type_kind_error::kind::for_loop, + statement->range().type_decoration); } this->bag.enter(statement->symbols); for (auto *body_statement : statement->body) @@ -523,14 +514,16 @@ namespace elna::boot auto const base_symbol = this->bag.lookup(expression->base.value().name()); if (base_symbol == nullptr || base_symbol->is_type() == nullptr) { - add_error<base_type_error>(type(), expression->position()); + add_error<type_kind_error>(expression->position(), + type_kind_error::kind::record_base, type()); } else { type const base_type = resolve_underlying_type(base_symbol->is_type()->symbol); if (base_type.get<record_type>() == nullptr) { - add_error<base_type_error>(base_type, expression->position()); + add_error<type_kind_error>(expression->position(), + type_kind_error::kind::record_base, base_type); } } } @@ -685,18 +678,6 @@ namespace elna::boot } } - for_loop_type_error::for_loop_type_error(const source_position position, - type actual) - : error(position), actual(std::move(actual)) - { - } - - std::string for_loop_type_error::what() const - { - return "for-loop variable must be Int or Word, but got '" - + actual.to_string() + "'"; - } - binary_operation_error::binary_operation_error(const source_position position, type left, type right, binary_operator operation) : error(position), left(std::move(left)), right(std::move(right)), op(operation) |
