diff options
| -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 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 139 | ||||
| -rw-r--r-- | gcc/gcc/elna-tree.cc | 5 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 40 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 20 | ||||
| -rw-r--r-- | include/elna/boot/symbol.h | 10 | ||||
| -rw-r--r-- | include/elna/boot/type_check.h | 30 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 5 | ||||
| -rw-r--r-- | include/elna/gcc/elna-tree.h | 1 | ||||
| -rw-r--r-- | testsuite/runnable/for_by.elna | 9 | ||||
| -rw-r--r-- | testsuite/runnable/for_each_array.elna | 12 | ||||
| -rw-r--r-- | testsuite/runnable/for_each_slice.elna | 15 | ||||
| -rw-r--r-- | testsuite/runnable/for_loop.elna | 9 | ||||
| -rw-r--r-- | testsuite/runnable/for_with.elna | 9 | ||||
| -rw-r--r-- | testsuite/runnable/repeat_loop.elna | 11 |
20 files changed, 377 insertions, 246 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) diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 6fe3da9..ebf4f8f 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -947,7 +947,7 @@ namespace elna::gcc if (TREE_CODE(aggregate_type) == ARRAY_TYPE && expression->field() == "length") { - this->current_expression = convert(build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST), + this->current_expression = fold_convert(elna_word_type_node, TYPE_MAX_VALUE(TYPE_DOMAIN(aggregate_type))); } else if (TREE_CODE(aggregate_type) == ARRAY_TYPE && expression->field() == "ptr") @@ -1082,69 +1082,120 @@ namespace elna::gcc return build3(COND_EXPR, void_type_node, condition, then_body, next); } - void generic_visitor::visit(boot::for_statement *statement) + tree generic_visitor::declare_local_variable(const boot::identifier& name, + const boot::variable_info& info, tree initial_value) { - statement->initial_value().accept(this); - tree initial_value = this->current_expression; - - statement->final_value().accept(this); - tree final_value = this->current_expression; - - tree step; - location_t step_location = UNKNOWN_LOCATION; - if (statement->step != nullptr) - { - statement->step->accept(this); - step = this->current_expression; - step_location = get_location(&statement->step->position()); - } - enter_scope(); - // Declare control variable with the unqualified type. The constant_type wrapper - // is for semantic checking only, because GENERIC needs to modify the control variable. - auto control_variable_info = statement->symbols->lookup(statement->control_variable.name()); - boot::variable_info unqualified_info(resolve_underlying_type(control_variable_info->is_variable()->symbol), - control_variable_info->is_variable()->is_extern); - tree control_variable_declaration = declare_variable(statement->control_variable.name(), - unqualified_info, this->symbols); - DECL_CONTEXT(control_variable_declaration) = current_function_decl; - f_binding_level->names = chainon(f_binding_level->names, control_variable_declaration); - DECL_INITIAL(control_variable_declaration) = initial_value; - tree declaration_statement = build1_loc(get_location(&statement->control_variable.position()), - DECL_EXPR, void_type_node, control_variable_declaration); + tree decl = declare_variable(name.name(), info, this->symbols); + DECL_CONTEXT(decl) = current_function_decl; + f_binding_level->names = chainon(f_binding_level->names, decl); + DECL_INITIAL(decl) = initial_value; + tree declaration_statement = build1_loc(get_location(&name.position()), + DECL_EXPR, void_type_node, decl); append_statement(declaration_statement); - tree control_type = TREE_TYPE(control_variable_declaration); - if (statement->step == nullptr) - { - step = build_int_cst_type(control_type, 1); - } + return decl; + } + std::pair<tree, tree> generic_visitor::build_loop_head(tree control_variable_declaration, tree limit, + tree_code comparison, location_t check_location) + { // Put a label in front of the loop condition. - location_t check_location = get_location(&statement->position()); tree check_label = create_artificial_label(check_location); tree check_statement = build1_loc(check_location, LABEL_EXPR, void_type_node, check_label); append_statement(check_statement); // Build the condition and jump if the condition isn't met. - tree condition = fold_build2(LE_EXPR, elna_bool_type_node, control_variable_declaration, final_value); + tree condition = fold_build2(comparison, elna_bool_type_node, control_variable_declaration, limit); tree end_label = create_artificial_label(UNKNOWN_LOCATION); tree goto_end = build1(GOTO_EXPR, void_type_node, end_label); tree condition_statement = build3_loc(check_location, COND_EXPR, void_type_node, condition, NULL_TREE, goto_end); append_statement(condition_statement); + return { check_label, end_label }; + } + + void generic_visitor::visit(boot::repeat_statement *statement) + { + tree loop_label = create_artificial_label(get_location(&statement->position())); + tree goto_loop = build1(GOTO_EXPR, void_type_node, loop_label); + append_statement(build1(LABEL_EXPR, void_type_node, loop_label)); + for (auto *body_statement : statement->body) { body_statement->accept(this); } - // Adjust the control variable by step and jump to check the condition. - tree step_expression = fold_build2_loc(step_location, PLUS_EXPR, control_type, - control_variable_declaration, step); - append_statement(build2(MODIFY_EXPR, void_type_node, control_variable_declaration, step_expression)); - tree goto_check = build1(GOTO_EXPR, void_type_node, check_label); - append_statement(goto_check); - tree end_statement = build1(LABEL_EXPR, void_type_node, end_label); - append_statement(end_statement); + statement->condition().accept(this); + tree condition_statement = build3(COND_EXPR, void_type_node, + this->current_expression, NULL_TREE, goto_loop); + append_statement(condition_statement); + } + + void generic_visitor::visit(boot::for_statement *statement) + { + statement->range().accept(this); + tree range_expression = this->current_expression; + location_t location = get_location(&statement->position()); + tree start_pointer; + tree length; + + if (TREE_CODE(TREE_TYPE(range_expression)) == ARRAY_TYPE) + { + tree element_type = TREE_TYPE(TREE_TYPE(range_expression)); + tree array_reference = build4_loc(location, ARRAY_REF, element_type, + range_expression, size_one_node, NULL_TREE, NULL_TREE); + + start_pointer = build1(ADDR_EXPR, build_pointer_type(element_type), array_reference); + length = TYPE_MAX_VALUE(TYPE_DOMAIN(TREE_TYPE(range_expression))); + } + else + { + tree ptr_field = TYPE_FIELDS(TREE_TYPE(range_expression)); + tree length_field = TREE_CHAIN(ptr_field); + + start_pointer = build3_loc(location, COMPONENT_REF, TREE_TYPE(ptr_field), + range_expression, ptr_field, NULL_TREE); + length = build3_loc(location, COMPONENT_REF, TREE_TYPE(length_field), + range_expression, length_field, NULL_TREE); + } + tree end_pointer = do_pointer_arithmetic(boot::binary_operator::sum, start_pointer, length, location); + + enter_scope(); + // Declare control variable with the unqualified type. The constant_type wrapper + // is for semantic checking only, because GENERIC needs to modify the control variable. + auto control_variable_info = statement->symbols->lookup(statement->control_variable.name()); + boot::variable_info unqualified_info( + boot::resolve_underlying_type(control_variable_info->is_variable()->symbol), + control_variable_info->is_variable()->is_extern); + tree control_variable_declaration = declare_local_variable( + statement->control_variable, unqualified_info, start_pointer); + + tree elna_one = build_int_cst_type(elna_word_type_node, 1); + tree counter_declaration{ NULL_TREE }; + if (statement->counter != nullptr) + { + auto counter_info = statement->symbols->lookup(statement->counter->name()); + counter_declaration = declare_local_variable(*statement->counter, + *counter_info->is_variable(), elna_one); + } + auto [check_label, end_label] = build_loop_head(control_variable_declaration, + end_pointer, LT_EXPR, location); + + for (auto *body_statement : statement->body) + { + body_statement->accept(this); + } + tree pointer_plus = do_pointer_arithmetic(boot::binary_operator::sum, + control_variable_declaration, elna_one, location); + append_statement(build2(MODIFY_EXPR, void_type_node, control_variable_declaration, pointer_plus)); + + if (counter_declaration != NULL_TREE) + { + tree counter_plus = fold_build2(PLUS_EXPR, elna_word_type_node, counter_declaration, elna_one); + append_statement(build2(MODIFY_EXPR, void_type_node, counter_declaration, counter_plus)); + } + append_statement(build1(GOTO_EXPR, void_type_node, check_label)); + append_statement(build1(LABEL_EXPR, void_type_node, end_label)); tree for_binding = leave_scope(); append_statement(for_binding); diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc index 2568430..7466f24 100644 --- a/gcc/gcc/elna-tree.cc +++ b/gcc/gcc/elna-tree.cc @@ -31,11 +31,6 @@ namespace elna::gcc return TREE_CODE(type) == INTEGER_TYPE && type != elna_char_type_node; } - bool is_numeric_type(tree type) - { - return is_integral_type(type) || type == elna_float_type_node; - } - bool is_unique_type(tree type) { gcc_assert(TYPE_P(type)); diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index b50eb75..d6804df 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -75,6 +75,7 @@ namespace elna::boot class if_statement; class import_declaration; class while_statement; + class repeat_statement; class for_statement; class case_statement; class traits_expression; @@ -118,6 +119,7 @@ namespace elna::boot virtual void visit(if_statement *) = 0; virtual void visit(import_declaration *) = 0; virtual void visit(while_statement *) = 0; + virtual void visit(repeat_statement *) = 0; virtual void visit(for_statement *) = 0; virtual void visit(defer_statement *) = 0; virtual void visit(case_statement *) = 0; @@ -146,7 +148,9 @@ namespace elna::boot }; /** - * Abstract visitor that doesn't visit any nodes by default. + * Abstract visitor that asserts on any node visitation. Child visitors + * override only the node types they care about, with the guarantee that + * unimplemented nodes are never silently ignored. */ class empty_visitor : public parser_visitor { @@ -166,6 +170,7 @@ namespace elna::boot [[noreturn]] void visit(if_statement *) override; [[noreturn]] void visit(import_declaration *) override; [[noreturn]] void visit(while_statement *) override; + [[noreturn]] void visit(repeat_statement *) override; [[noreturn]] void visit(for_statement *) override; [[noreturn]] void visit(defer_statement *) override; [[noreturn]] void visit(empty_statement *) override; @@ -213,6 +218,7 @@ namespace elna::boot void visit(if_statement *) override; void visit(import_declaration *) override; void visit(while_statement *statement) override; + void visit(repeat_statement *statement) override; void visit(for_statement *statement) override; void visit(defer_statement *statement) override; void visit(empty_statement *) override; @@ -824,28 +830,44 @@ namespace elna::boot }; /** - * for-statement. + * for-to-statement. + */ + class repeat_statement : public statement + { + expression *m_condition; + + public: + const std::vector<statement *> body; + + repeat_statement(const source_position position, std::vector<statement *>&& body, + expression *condition); + ~repeat_statement() override; + + void accept(parser_visitor *visitor) override; + + expression& condition(); + }; + + /** + * for-of-statement. */ class for_statement : public statement { - expression *m_initial_value; - expression *m_final_value; + expression *m_range; public: const identifier control_variable; + identifier *const counter; const std::vector<statement *> body; - expression *const step; std::shared_ptr<symbol_table> symbols; for_statement(const source_position position, identifier&& control_variable, - expression *initial_value, expression *final_value, - std::vector<statement *>&& body, expression *step = nullptr); + expression *range, std::vector<statement *>&& body, identifier *const counter); ~for_statement() override; void accept(parser_visitor *visitor) override; - expression& initial_value(); - expression& final_value(); + expression& range(); }; /** diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index d18b813..68fe6da 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -33,15 +33,25 @@ namespace elna::boot * Error declaring or using a symbol (undeclared, redefinition, * local export). */ - class symbol_error : public error + class declaration_error : public error { public: - struct undeclared { std::string name; }; - struct local_export { std::string name; }; - struct redefinition { std::string name; std::optional<source_position> original; }; + struct undeclared + { + std::string name; + }; + struct local_export + { + std::string name; + }; + struct redefinition + { + std::string name; + std::optional<source_position> original; + }; using payload_type = std::variant<undeclared, local_export, redefinition>; - symbol_error(const source_position position, payload_type payload); + declaration_error(const source_position position, payload_type payload); std::string what() const override; std::optional<std::pair<std::string, source_position>> note() const override; diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index ea418b1..4e97205 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -73,6 +73,7 @@ namespace elna::boot bool operator==(const std::nullptr_t&) const; bool operator==(const type& other) const; + explicit operator bool() const; bool empty() const; @@ -523,4 +524,13 @@ namespace elna::boot * \return Whether the type is a pointer type. */ bool is_any_pointer_type(const type& checked); + + /** + * If \a range is an array or a slice gives its base type, otherwise + * returns an empty type. + * + * \param range The type to check. + * \return The base type, or an empty type. + */ + type get_range_base_type(const type& range); } diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h index 5fdc5cb..e500007 100644 --- a/include/elna/boot/type_check.h +++ b/include/elna/boot/type_check.h @@ -88,16 +88,23 @@ namespace elna::boot }; /** - * Base type of a record is not a record. + * Unexpected kind of a type at specific position. */ - class base_type_error : public error + class type_kind_error : public error { - type actual; - public: - base_type_error(type actual, const source_position position); + enum class kind + { + record_base, + for_loop + }; + type_kind_error(const source_position position, kind type_kind, const type& actual); std::string what() const override; + + private: + kind type_kind; + type actual; }; /** @@ -165,19 +172,6 @@ namespace elna::boot }; /** - * FOR loop variable must be \c Int or \c Word. - */ - class for_loop_type_error : public error - { - type actual; - - public: - for_loop_type_error(const source_position position, type actual); - - std::string what() const override; - }; - - /** * Chain of responsibility for type compatibility checks. * * Populate \c ctx with pre-resolved types, then call \c run(). diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index de95a7e..1ac829f 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -60,6 +60,10 @@ namespace elna::gcc bool expect_trait_type_only(boot::traits_expression *trait); void visit_statements(const std::vector<boot::statement *>& statements); bool assert_constant(location_t expression_location); + tree declare_local_variable(const boot::identifier& name, + const boot::variable_info& info, tree initial_value); + std::pair<tree, tree> build_loop_head(tree control_variable_declaration, tree limit, + tree_code comparison, location_t check_location); public: generic_visitor(std::shared_ptr<symbol_table> symbol_table, @@ -89,6 +93,7 @@ namespace elna::gcc void visit(boot::slicing_expression *expression) override; void visit(boot::assign_statement *statement) override; void visit(boot::if_statement *statement) override; + void visit(boot::repeat_statement *statement) override; void visit(boot::for_statement *statement) override; void visit(boot::while_statement *statement) override; void visit(boot::defer_statement *statement) override; diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index 1d48b00..ac97e41 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -36,7 +36,6 @@ namespace elna::gcc using symbol_table = boot::symbol_map<tree, tree, NULL_TREE>; bool is_integral_type(tree type); - bool is_numeric_type(tree type); bool is_unique_type(tree type); bool is_void_type(tree type); diff --git a/testsuite/runnable/for_by.elna b/testsuite/runnable/for_by.elna deleted file mode 100644 index b219dc6..0000000 --- a/testsuite/runnable/for_by.elna +++ /dev/null @@ -1,9 +0,0 @@ -var - actual: [4]Word := [4]Word{} - -begin - for i := 1u to 4u by 2u do - actual[i] := i - end; - assert(actual = [4]Word{ 1u, 0u, 3u, 0u }) -end. diff --git a/testsuite/runnable/for_each_array.elna b/testsuite/runnable/for_each_array.elna new file mode 100644 index 0000000..bf5733d --- /dev/null +++ b/testsuite/runnable/for_each_array.elna @@ -0,0 +1,12 @@ +var + actual: [4]Word := [4]Word{} + input: [4]Word := [4]Word{ 2u, 4u, 6u, 8u } + i: Word := 1u + +begin + for element of input do + actual[i] := element^ * 2u; + i := i + 1u + end; + assert(actual = [4]Word{ 4u, 8u, 12u, 16u }) +end. diff --git a/testsuite/runnable/for_each_slice.elna b/testsuite/runnable/for_each_slice.elna new file mode 100644 index 0000000..efdf961 --- /dev/null +++ b/testsuite/runnable/for_each_slice.elna @@ -0,0 +1,15 @@ +var + actual: [4]Word := [4]Word{} + input: [4]Word := [4]Word{ 2u, 4u, 6u, 8u } + slice: []Word + i: Word := 1u + +begin + slice := input[1u to input.length]; + + for element of slice do + actual[i] := element^ * 2u; + i := i + 1u + end; + assert(actual = [4]Word{ 4u, 8u, 12u, 16u }) +end. diff --git a/testsuite/runnable/for_loop.elna b/testsuite/runnable/for_loop.elna deleted file mode 100644 index 02db679..0000000 --- a/testsuite/runnable/for_loop.elna +++ /dev/null @@ -1,9 +0,0 @@ -var - actual: [4]Word := [4]Word{} - -begin - for i := 1u to 4u do - actual[i] := i * 2u - end; - assert(actual = [4]Word{ 2u, 4u, 6u, 8u }) -end. diff --git a/testsuite/runnable/for_with.elna b/testsuite/runnable/for_with.elna new file mode 100644 index 0000000..cfec5c0 --- /dev/null +++ b/testsuite/runnable/for_with.elna @@ -0,0 +1,9 @@ +var + actual: [3]Word := [3]Word{} + +begin + for element of [3]Word{ 1u, 2u, 3u } with i do + actual[i] := i * element^ + end; + assert(actual = [3]Word{ 1u, 4u, 9u }) +end. diff --git a/testsuite/runnable/repeat_loop.elna b/testsuite/runnable/repeat_loop.elna new file mode 100644 index 0000000..5faaeab --- /dev/null +++ b/testsuite/runnable/repeat_loop.elna @@ -0,0 +1,11 @@ +var + actual: [4]Word := [4]Word{} + i: Word := 0u + +begin + repeat + i := i + 1u; + actual[i] := i + until i = 4u; + assert(actual = [4]Word{ 1u, 2u, 3u, 4u }) +end. |
