diff options
| -rw-r--r-- | boot/ast.cc | 54 | ||||
| -rw-r--r-- | boot/evaluator.cc | 2 | ||||
| -rw-r--r-- | boot/lexer.ll | 6 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 259 | ||||
| -rw-r--r-- | boot/parser.yy | 55 | ||||
| -rw-r--r-- | boot/type_check.cc | 50 | ||||
| -rw-r--r-- | gcc/gcc/elna-builtins.cc | 2 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 103 | ||||
| -rw-r--r-- | gcc/gcc/elna-tree.cc | 9 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 32 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 98 | ||||
| -rw-r--r-- | include/elna/boot/type_check.h | 14 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 2 | ||||
| -rw-r--r-- | include/elna/gcc/elna-tree.h | 1 | ||||
| -rw-r--r-- | include/elna/gcc/elna1.h | 7 | ||||
| -rw-r--r-- | testsuite/runnable/for_by.elna | 9 | ||||
| -rw-r--r-- | testsuite/runnable/for_loop.elna | 9 | ||||
| -rw-r--r-- | testsuite/runnable/slice_equality.elna | 10 |
18 files changed, 473 insertions, 249 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index ec5b27d..a50d3d3 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -91,6 +91,11 @@ namespace elna::boot __builtin_unreachable(); } + void empty_visitor::visit(for_statement *) + { + __builtin_unreachable(); + } + void empty_visitor::visit(defer_statement *) { __builtin_unreachable(); @@ -286,6 +291,20 @@ namespace elna::boot } } + void walking_visitor::visit(for_statement *statement) + { + statement->initial_value().accept(this); + statement->final_value().accept(this); + if (statement->step != nullptr) + { + statement->step->accept(this); + } + for (auto *body_statement : statement->body) + { + body_statement->accept(this); + } + } + void walking_visitor::visit(defer_statement *statement) { for (auto *block_statement : statement->statements) @@ -1522,6 +1541,41 @@ namespace elna::boot } } + 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) + { + } + + for_statement::~for_statement() + { + delete this->m_initial_value; + delete this->m_final_value; + delete this->step; + + for (const statement *body_statement : this->body) + { + delete body_statement; + } + } + + void for_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& for_statement::initial_value() + { + return *this->m_initial_value; + } + + expression& for_statement::final_value() + { + return *this->m_final_value; + } + const char *print_binary_operator(const binary_operator operation) { switch (operation) diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 77a56ad..4b37668 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -186,7 +186,7 @@ namespace elna::boot return std::visit([](auto value) -> std::optional<constant_value> { using T = std::decay_t<decltype(value)>; - if constexpr (std::is_integral_v<T>) + if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) { return constant_value{ ~value }; } diff --git a/boot/lexer.ll b/boot/lexer.ll index 9fe0398..c363ddd 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -83,6 +83,12 @@ elsif { while { return yy::parser::make_WHILE(this->location); } +for { + return yy::parser::make_FOR(this->location); +} +by { + return yy::parser::make_BY(this->location); +} do { return yy::parser::make_DO(this->location); } diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 907f318..f932a5d 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -21,127 +21,120 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - declaration_error::declaration_error(const kind error_kind, - const boot::identifier& identifier) - : error(identifier.position()), identifier(identifier.name()), error_kind(error_kind) + symbol_error::symbol_error(const source_position position, payload_type payload) + : error(position), payload(std::move(payload)) { } - std::string declaration_error::what() const + std::string symbol_error::what() const { - switch (this->error_kind) - { - case kind::undeclared: - return "Type '" + identifier + "' not declared"; - case kind::local_export: - return "Local symbol '" + this->identifier + "' cannot be exported"; - default: - __builtin_unreachable(); - } - } - - redefinition_error::redefinition_error(const boot::identifier& identifier, - std::optional<source_position> original) - : error(identifier.position()), identifier(identifier.name()), original(original) - { - } - - std::string redefinition_error::what() const - { - return "Symbol '" + identifier + "' has been already defined"; + return std::visit([](const auto& pay) -> std::string { + using T = std::decay_t<decltype(pay)>; + if constexpr (std::is_same_v<T, undeclared>) + { + return "Type '" + pay.name + "' not declared"; + } + else if constexpr (std::is_same_v<T, local_export>) + { + return "Local symbol '" + pay.name + "' cannot be exported"; + } + else if constexpr (std::is_same_v<T, redefinition>) + { + return "Symbol '" + pay.name + "' has been already defined"; + } + }, payload); } - std::optional<std::pair<std::string, source_position>> redefinition_error::note() const + std::optional<std::pair<std::string, source_position>> symbol_error::note() const { - if (original.has_value() && original->start().available()) + if (const auto *redef = std::get_if<redefinition>(&payload)) { - return std::make_pair("previously declared here", *original); + if (redef->original.has_value() && redef->original->start().available()) + { + return std::make_pair("previously declared here", *redef->original); + } } return std::nullopt; } - const_array_error::const_array_error(const source_position position) - : error(position) - { - } - - std::string const_array_error::what() const - { - return "const must be written before the array size, not after"; - } - - double_const_error::double_const_error(const source_position position) - : error(position) - { - } - - std::string double_const_error::what() const - { - return "Duplicate 'const' qualifier is not allowed"; - } - - field_not_found_error::field_not_found_error(const identifier& field_name, - type composite_type) - : error(field_name.position()), field_name(field_name.name()), composite_type(std::move(composite_type)) + const_qualifier_error::const_qualifier_error(const source_position position, kind error_kind) + : error(position), error_kind(error_kind) { } - std::string field_not_found_error::what() const + std::string const_qualifier_error::what() const { - type const resolved = resolve_underlying_type(composite_type); - bool const is_enum = resolved.get<enumeration_type>() != nullptr; - bool const is_record = resolved.get<record_type>() != nullptr; - - if (is_enum || is_record) + switch (error_kind) { - std::string message = is_enum ? "Enumeration" : "Record"; - - if (auto alias = composite_type.get<alias_type>()) - { - message += " '" + alias->name + "'"; - } - message += " does not have a "; - message += is_enum ? "member" : "field"; - message += " named '" + field_name + "'"; - return message; + case kind::array_position: + return "const must be written before the array size, not after"; + case kind::duplicate: + return "Duplicate 'const' qualifier is not allowed"; + default: + __builtin_unreachable(); } - return "Type '" + composite_type.to_string() - + "' does not have a field named '" + field_name + "'"; } - duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, - type aggregate, std::optional<source_position> original, - std::optional<std::string> base_name) - : error(member_name.position()), member_name(member_name.name()), aggregate(std::move(aggregate)), - original(original), base_name(std::move(base_name)) + member_error::member_error(const source_position position, payload_type payload) + : error(position), payload(std::move(payload)) { } - std::string duplicate_member_error::what() const + std::string member_error::what() const { - type const resolved = resolve_underlying_type(aggregate); - bool const is_enum = resolved.get<enumeration_type>() != nullptr; - std::string const kind = is_enum ? "member" : "field"; - std::string message = is_enum ? "Enumeration" : "Record"; + return std::visit([](const auto& pay) -> std::string { + using T = std::decay_t<decltype(pay)>; + if constexpr (std::is_same_v<T, not_found>) + { + const type resolved = resolve_underlying_type(pay.composite); + const bool is_enum = resolved.get<enumeration_type>() != nullptr; + const bool is_record = resolved.get<record_type>() != nullptr; - if (auto alias = aggregate.get<alias_type>()) - { - message += " '" + alias->name + "'"; - } - message += " already has a " + kind + " named '" + member_name + "'"; + if (is_enum || is_record) + { + std::string message = is_enum ? "Enumeration" : "Record"; + if (auto alias = pay.composite.template get<alias_type>()) + { + message += " '" + alias->name + "'"; + } + message += " does not have a "; + message += is_enum ? "member" : "field"; + message += " named '" + pay.name + "'"; + return message; + } + return "Type '" + pay.composite.to_string() + + "' does not have a field named '" + pay.name + "'"; + } + else if constexpr (std::is_same_v<T, duplicate>) + { + const type resolved = resolve_underlying_type(pay.aggregate); + const bool is_enum = resolved.get<enumeration_type>() != nullptr; + const std::string kind = is_enum ? "member" : "field"; + std::string message = is_enum ? "Enumeration" : "Record"; - if (base_name.has_value()) - { - message += " (defined in base type '" + *base_name + "')"; - } - return message; + if (auto alias = pay.aggregate.template get<alias_type>()) + { + message += " '" + alias->name + "'"; + } + message += " already has a " + kind + " named '" + pay.name + "'"; + + if (pay.base.has_value()) + { + message += " (defined in base type '" + *pay.base + "')"; + } + return message; + } + }, payload); } - std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const + std::optional<std::pair<std::string, source_position>> member_error::note() const { - if (original.has_value() && original->start().available()) + if (const auto *dup = std::get_if<duplicate>(&payload)) { - return std::make_pair("previously declared here", *original); + if (dup->original.has_value() && dup->original->start().available()) + { + return std::make_pair("previously declared here", *dup->original); + } } return std::nullopt; } @@ -231,7 +224,7 @@ namespace elna::boot type name_analysis_visitor::lookup_field(const type& composite_type, const std::string& field_name) { - type const resolved_type = resolve_underlying_type(composite_type); + const type resolved_type = resolve_underlying_type(composite_type); if (auto record = resolved_type.get<record_type>()) { @@ -287,7 +280,8 @@ namespace elna::boot if (this->current_type.get<constant_type>() != nullptr) { - add_error<double_const_error>(expression->position()); + add_error<const_qualifier_error>(expression->position(), + const_qualifier_error::kind::duplicate); } this->current_type = type(std::make_shared<constant_type>(this->current_type)); } @@ -298,7 +292,8 @@ namespace elna::boot if (this->current_type.get<constant_type>() != nullptr) { - add_error<const_array_error>(expression->position()); + add_error<const_qualifier_error>(expression->position(), + const_qualifier_error::kind::array_position); } this->current_type = type(std::make_shared<array_type>(this->current_type, expression->size)); } @@ -355,8 +350,9 @@ namespace elna::boot base_name = alias->name; } } - add_error<duplicate_member_error>(field_name, aggregate, - existing->second.declaration, base_name); + add_error<member_error>(field_name.position(), + member_error::duplicate{.name = field_name.name(), .aggregate = aggregate, + .original = existing->second.declaration, .base = base_name}); } else { @@ -392,8 +388,8 @@ namespace elna::boot } else { - add_error<declaration_error>(declaration_error::kind::undeclared, - expression->base.value()); + add_error<symbol_error>(expression->base.value().position(), + symbol_error::undeclared{.name = expression->base.value().name()}); this->current_type = type(); return; } @@ -421,8 +417,8 @@ namespace elna::boot } else { - add_error<declaration_error>(declaration_error::kind::undeclared, - expression->type_name); + add_error<symbol_error>(expression->type_name.position(), + symbol_error::undeclared{.name = expression->type_name.name()}); } for (const field_initializer& initializer : expression->field_initializers) { @@ -430,8 +426,8 @@ namespace elna::boot if (!expression->type_decoration.empty() && lookup_field(expression->type_decoration, initializer.name()).empty()) { - add_error<declaration_error>(declaration_error::kind::undeclared, - initializer.id()); + add_error<symbol_error>(initializer.id().position(), + symbol_error::undeclared{.name = initializer.id().name()}); } } } @@ -485,14 +481,16 @@ for (const auto& member : expression->members) std::shared_ptr<enumeration_type> const result_type = std::make_shared<enumeration_type>( member_names); std::map<std::string, source_position> seen; - type const aggregate(result_type); + const type aggregate(result_type); for (const auto& member : expression->members) { auto existing = seen.find(member.name()); if (existing != seen.end()) { - add_error<duplicate_member_error>(member, aggregate, existing->second); + add_error<member_error>(member.position(), + member_error::duplicate{.name = member.name(), .aggregate = aggregate, + .original = existing->second, .base = std::nullopt}); } else { @@ -511,8 +509,8 @@ for (const auto& member : expression->members) if (!this->bag.enter(name, variable_symbol)) { auto original = this->bag.lookup(name); - add_error<redefinition_error>(boot::identifier(name, position), - original->position); + add_error<symbol_error>(position, + symbol_error::redefinition{.name = name, .original = original->position}); } return variable_symbol; } @@ -642,7 +640,7 @@ for (const auto& member : expression->members) if (!trait->type_decoration.empty()) { - type const resolved = resolve_underlying_type(trait->type_decoration); + const type resolved = resolve_underlying_type(trait->type_decoration); if (resolved.get<enumeration_type>() == nullptr && !is_primitive_type(resolved, "Float") @@ -656,8 +654,8 @@ for (const auto& member : expression->members) } else { - add_error<declaration_error>(declaration_error::kind::undeclared, - trait->name); + add_error<symbol_error>(trait->name.position(), + symbol_error::undeclared{.name = trait->name.name()}); } } @@ -742,7 +740,9 @@ for (const auto& member : expression->members) } if (expression->type_decoration.empty()) { - add_error<field_not_found_error>(expression->field(), expression->base().type_decoration); + add_error<member_error>(expression->field().position(), + member_error::not_found{.name = expression->field().name(), + .composite = expression->base().type_decoration}); } else { @@ -761,6 +761,27 @@ 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->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); + 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); @@ -792,29 +813,33 @@ for (const auto& member : expression->members) } else { - add_error<declaration_error>(declaration_error::kind::undeclared, - boot::identifier(expression->name, expression->position())); + add_error<symbol_error>(expression->position(), + symbol_error::undeclared{.name = expression->name}); } } void name_analysis_visitor::visit(literal<std::int32_t> *literal) { literal->type_decoration = lookup_primitive_type("Int"); + this->current_type = literal->type_decoration; } void name_analysis_visitor::visit(literal<std::uint32_t> *literal) { literal->type_decoration = lookup_primitive_type("Word"); + this->current_type = literal->type_decoration; } void name_analysis_visitor::visit(literal<double> *literal) { literal->type_decoration = lookup_primitive_type("Float"); + this->current_type = literal->type_decoration; } void name_analysis_visitor::visit(literal<bool> *literal) { literal->type_decoration = lookup_primitive_type("Bool"); + this->current_type = literal->type_decoration; } void name_analysis_visitor::visit(literal<unsigned char> *literal) @@ -831,6 +856,7 @@ for (const auto& member : expression->members) { literal->type_decoration = type(std::make_shared<slice_type>( type(std::make_shared<constant_type>(lookup_primitive_type("Char"))))); + this->current_type = literal->type_decoration; } declaration_visitor::declaration_visitor() @@ -864,8 +890,9 @@ for (const auto& member : expression->members) if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second) { - add_error<redefinition_error>(declaration->identifier.id(), - declaration->position()); + add_error<symbol_error>(declaration->identifier.id().position(), + symbol_error::redefinition{.name = declaration->identifier.id().name(), + .original = declaration->position()}); } } @@ -887,8 +914,8 @@ for (const auto& member : expression->members) { if (variable_identifier.exported()) { - add_error<declaration_error>(declaration_error::kind::local_export, - variable_identifier.id()); + add_error<symbol_error>(variable_identifier.id().position(), + symbol_error::local_export{.name = variable_identifier.id().name()}); } } } diff --git a/boot/parser.yy b/boot/parser.yy index bc2ddb5..7d93dde 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -99,12 +99,8 @@ along with GCC; see the file COPYING3. If not see TYPE "type" RECORD "record" EXTERN "extern" - IF "if" - WHILE "while" - DO "do" - THEN "then" - ELSE "else" - ELSIF "elsif" + IF "if" THEN "then" ELSE "else" ELSIF "elsif" + WHILE "while" DO "do" FOR "for" BY "by" RETURN "return" IMPORT "import" BEGIN_BLOCK "begin" @@ -135,7 +131,7 @@ along with GCC; see the file COPYING3. If not see %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; +%type <elna::boot::expression *> expression operand simple_expression procedure_return by_step; %type <elna::boot::unary_expression *> unary_expression; %type <elna::boot::binary_expression *> binary_expression; %type <std::vector<elna::boot::expression *>> expressions actual_parameter_list; @@ -156,7 +152,6 @@ 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 <elna::boot::cast_expression *> cast_expression; %type <std::unique_ptr<elna::boot::identifier>> identifier; %type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition; %type <std::vector<elna::boot::identifier_definition>> identifier_definitions; @@ -175,7 +170,7 @@ procedure_body: { $$ = std::make_unique<boot::procedure_body>($1, $2, $3); } statement_part: - /* no statements */ {} + %empty {} | "begin" statements { $$ = $2; } identifier: IDENTIFIER { $$ = std::make_unique<boot::identifier>($1, boot::make_position(@1)); } @@ -190,7 +185,7 @@ identifier_definitions: } | identifier_definition { $$.emplace_back(std::move(*$1)); } return_declaration: - /* proper procedure */ {} + %empty {} | ":" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); } | ":" type_expression { $$ = boot::procedure_type_expression::return_t($2); } procedure_heading: "(" optional_fields ")" return_declaration @@ -205,7 +200,7 @@ procedure_declaration: $$ = new boot::procedure_declaration(boot::make_position(@$), std::move(*$2), $3); } procedure_part: - /* no procedure declarations */ {} + %empty {} | procedure_declaration procedure_part { $$ = $2; @@ -215,8 +210,9 @@ call_expression: designator_expression actual_parameter_list { $$ = new boot::procedure_call(boot::make_position(@$), $1, $2); } -cast_expression: "cast" "(" expression ":" type_expression ")" - { $$ = new boot::cast_expression(boot::make_position(@$), $5, $3); } +by_step: + "by" expression { $$ = $2; } + | %empty { $$ = nullptr; } elsif_do_statements: "elsif" expression "do" statements elsif_do_statements { @@ -224,10 +220,10 @@ elsif_do_statements: $$ = $5; $$.emplace($$.begin(), branch); } - | /* no branches */ {} + | %empty {} else_statements: "else" statements { $$ = new std::vector<boot::statement *>($2); } - | { $$ = nullptr; } + | %empty { $$ = nullptr; } elsif_then_statements: "elsif" expression "then" statements elsif_then_statements { @@ -235,7 +231,7 @@ elsif_then_statements: $$ = $5; $$.emplace($$.begin(), branch); } - | /* no branches */ {} + | %empty {} procedure_return: "return" expression { $$ = $2; } | "return" { $$ = nullptr; } @@ -257,7 +253,10 @@ simple_expression: literal { $$ = $1; } | designator_expression { $$ = $1; } | traits_expression { $$ = $1; } - | cast_expression { $$ = $1; } + | "cast" "(" expression ":" type_expression ")" + { + $$ = new boot::cast_expression(boot::make_position(@$), $5, $3); + } | call_expression { $$ = $1; } | "(" expression ")" { $$ = $2; } | identifier "{" field_initializers "}" @@ -363,6 +362,7 @@ expressions: $$.emplace($$.cbegin(), $1); } | expression { $$.push_back($1); } + | %empty { $$ = std::vector<elna::boot::expression *>(); } type_expressions: type_expression "," type_expressions { @@ -389,6 +389,10 @@ 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" + { + $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, $6, $9, $7); + } | "if" expression "then" statements elsif_then_statements else_statements "end" { boot::conditional_statements *then = new boot::conditional_statements($2, $4); @@ -399,7 +403,7 @@ statement: { $$ = new boot::defer_statement(boot::make_position(@$), $2); } | "case" expression "of" switch_cases else_statements "end" { $$ = new boot::case_statement(boot::make_position(@$), $2, $4, $5); } - | { $$ = new boot::empty_statement(boot::make_position(@$)); } + | %empty { $$ = new boot::empty_statement(boot::make_position(@$)); } switch_case: case_labels ":" statements { $$ = { .labels = $1, .statements = $3 }; } switch_cases: @@ -434,7 +438,7 @@ required_fields: | field_declaration { $$.emplace_back($1); } optional_fields: required_fields { $$ = $1; } - | /* no fields */ {} + | %empty {} field_initializer: identifier ":" expression { $$ = std::make_unique<boot::field_initializer>(std::move(*$1), $3); } field_initializers: @@ -505,14 +509,14 @@ variable_declaration: $$ = new boot::variable_declaration( boot::make_position(@$), $1, shared_type, $5); } variable_declarations: - /* no variable declarations */ {} + %empty {} | variable_declaration variable_declarations { $$ = $2; $$.insert(std::cbegin($$), $1); } variable_part: - /* no variable declarations */ {} + %empty {} | "var" variable_declarations { $$ = $2; } import_declaration: IDENTIFIER "." import_declaration @@ -532,7 +536,7 @@ import_declarations: $$.emplace_back(new boot::import_declaration(boot::make_position(@$), $1)); } import_part: - /* no import declarations */ {} + %empty {} | "import" import_declarations { $$ = $2; } type_declaration: identifier_definition "=" type_expression { @@ -544,13 +548,12 @@ type_declarations: $$ = $2; $$.insert($$.cbegin(), $1); } - | /* no type definitions */ {} + | %empty {} type_part: - /* no type definitions */ {} + %empty {} | "type" type_declarations { $$ = $2; } actual_parameter_list: - "(" ")" {} - | "(" expressions ")" { $$ = $2; } + "(" expressions ")" { $$ = $2; } %% void yy::parser::error(const location_type& loc, const std::string& message) diff --git a/boot/type_check.cc b/boot/type_check.cc index 7989925..1689e65 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -465,6 +465,42 @@ 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); + + 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) + { + 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); + } + } + this->bag.enter(statement->symbols); + for (auto *body_statement : statement->body) + { + body_statement->accept(this); + } + this->bag.leave(); + } + void type_analysis_visitor::visit(type_declaration *declaration) { std::vector<std::string> alias_path; @@ -649,6 +685,18 @@ 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) @@ -749,6 +797,8 @@ namespace elna::boot valid = is_integral_type(lhs_resolved) && is_primitive_type(rhs_resolved, "Word"); break; + default: + __builtin_unreachable(); } if (!valid) { diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 77ffb1c..2790251 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -237,7 +237,7 @@ namespace elna::gcc tree declare_variable(const std::string& name, const boot::variable_info& info, std::shared_ptr<symbol_table> symbols) { - auto variable_type = get_inner_alias(info.symbol, symbols); + auto *variable_type = get_inner_alias(info.symbol, symbols); tree declaration_tree = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(name.c_str()), variable_type); TREE_ADDRESSABLE(declaration_tree) = 1; diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index c2c59aa..6fe3da9 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -228,7 +228,7 @@ namespace elna::gcc if (TREE_CODE(base_type) == ARRAY_TYPE) { // Elna arrays are 1-indexed in TYPE_DOMAIN. Pass the raw - // (1-based) start index — GCC handles the 1→0 conversion + // (1-based) start index since GCC handles the 1→0 conversion // via the domain lower bound. No -1 adjustment needed here. slice_ptr = build4_loc(location, ARRAY_REF, TREE_TYPE(base_type), base, start_index, size_one_node, NULL_TREE); @@ -256,10 +256,6 @@ namespace elna::gcc void generic_visitor::visit(boot::unit *unit) { - for (boot::import_declaration *const declaration : unit->imports) - { - declaration->accept(this); - } for (boot::variable_declaration *const variable : unit->variables) { variable->accept(this); @@ -363,15 +359,13 @@ namespace elna::gcc tree generic_visitor::leave_scope() { - // Variables are only defined in the top function scope. - tree variables = f_binding_level->level_chain == nullptr ? f_names : NULL_TREE; - tree new_block = build_block(variables, f_binding_level->blocks, NULL_TREE, NULL_TREE); + tree new_block = build_block(f_binding_level->names, f_binding_level->blocks, NULL_TREE, NULL_TREE); for (tree it = f_binding_level->blocks; it != NULL_TREE; it = BLOCK_CHAIN(it)) { BLOCK_SUPERCONTEXT(it) = new_block; } - tree bind_expr = build3(BIND_EXPR, void_type_node, variables, chain_defer(), new_block); + tree bind_expr = build3(BIND_EXPR, void_type_node, f_binding_level->names, chain_defer(), new_block); this->symbols = this->symbols->scope(); f_binding_level = f_binding_level->level_chain; @@ -639,27 +633,27 @@ namespace elna::gcc case exclusive_disjunction: gcc_unreachable(); case logical_conjunction: - this->current_expression = build2_loc(expression_location, + this->current_expression = fold_build2_loc(expression_location, TRUTH_ANDIF_EXPR, elna_bool_type_node, left, right); break; case logical_disjunction: - this->current_expression = build2_loc(expression_location, + this->current_expression = fold_build2_loc(expression_location, TRUTH_ORIF_EXPR, elna_bool_type_node, left, right); break; case logical_exclusive_disjunction: - this->current_expression = build2_loc(expression_location, + this->current_expression = fold_build2_loc(expression_location, TRUTH_XOR_EXPR, elna_bool_type_node, left, right); break; case bitwise_conjunction: - this->current_expression = build2_loc(expression_location, + this->current_expression = fold_build2_loc(expression_location, BIT_AND_EXPR, left_type, left, right); break; case bitwise_disjunction: - this->current_expression = build2_loc(expression_location, + this->current_expression = fold_build2_loc(expression_location, BIT_IOR_EXPR, left_type, left, right); break; case bitwise_exclusive_disjunction: - this->current_expression = build2_loc(expression_location, + this->current_expression = fold_build2_loc(expression_location, BIT_XOR_EXPR, left_type, left, right); break; case equals: @@ -780,7 +774,7 @@ namespace elna::gcc else { DECL_CONTEXT(declaration_tree) = current_function_decl; - f_names = chainon(f_names, declaration_tree); + f_binding_level->names = chainon(f_binding_level->names, declaration_tree); auto *declaration_statement = build1_loc(declaration_location, DECL_EXPR, void_type_node, declaration_tree); @@ -1031,7 +1025,7 @@ namespace elna::gcc } else { - tree assignment = build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, rvalue); + tree assignment = fold_build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, rvalue); append_statement(assignment); } @@ -1088,21 +1082,86 @@ namespace elna::gcc return build3(COND_EXPR, void_type_node, condition, then_body, next); } - void generic_visitor::visit(boot::import_declaration *) + void generic_visitor::visit(boot::for_statement *statement) { + 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); + append_statement(declaration_statement); + + tree control_type = TREE_TYPE(control_variable_declaration); + if (statement->step == nullptr) + { + step = build_int_cst_type(control_type, 1); + } + + // 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 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); + + 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); + + tree for_binding = leave_scope(); + append_statement(for_binding); + + this->current_expression = NULL_TREE; } void generic_visitor::visit(boot::while_statement *statement) { location_t prerequisite_location = get_location(&statement->branch().prerequisite().position()); - tree while_check_label = build_label_decl("while_do", prerequisite_location); - tree while_end_label = build_label_decl("while_end", UNKNOWN_LOCATION); + tree while_check_label = create_artificial_label(prerequisite_location); + tree while_end_label = create_artificial_label(UNKNOWN_LOCATION); tree goto_check = build1(GOTO_EXPR, void_type_node, while_check_label); tree result = NULL_TREE; - auto& branches = statement->branches; - for (auto& branch : branches | std::views::reverse) + for (boot::conditional_statements *branch : statement->branches | std::views::reverse) { result = make_if_branch(*branch, result, goto_check); } diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc index ab7363f..2568430 100644 --- a/gcc/gcc/elna-tree.cc +++ b/gcc/gcc/elna-tree.cc @@ -249,15 +249,6 @@ namespace elna::gcc return composite_type_node; } - tree build_label_decl(const char *name, location_t loc) - { - auto label_decl = build_decl(loc, LABEL_DECL, get_identifier(name), void_type_node); - - DECL_CONTEXT(label_decl) = current_function_decl; - - return label_decl; - } - tree extract_constant(tree expression) { int code = TREE_CODE(expression); diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 6080155..b50eb75 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 for_statement; class case_statement; class traits_expression; class unit; @@ -117,6 +118,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(for_statement *) = 0; virtual void visit(defer_statement *) = 0; virtual void visit(case_statement *) = 0; virtual void visit(empty_statement *) = 0; @@ -164,6 +166,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(for_statement *) override; [[noreturn]] void visit(defer_statement *) override; [[noreturn]] void visit(empty_statement *) override; [[noreturn]] void visit(case_statement *) override; @@ -210,6 +213,7 @@ namespace elna::boot void visit(if_statement *) override; void visit(import_declaration *) override; void visit(while_statement *statement) override; + void visit(for_statement *statement) override; void visit(defer_statement *statement) override; void visit(empty_statement *) override; void visit(case_statement *statement) override; @@ -819,6 +823,34 @@ namespace elna::boot ~while_statement() override; }; + /** + * for-statement. + */ + class for_statement : public statement + { + expression *m_initial_value; + expression *m_final_value; + + public: + const identifier control_variable; + 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); + ~for_statement() override; + + void accept(parser_visitor *visitor) override; + + expression& initial_value(); + expression& final_value(); + }; + + /** + * Stores module-level definitions. + */ class unit : public node, public procedure_body { public: diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index 5afe102..d18b813 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include <memory> #include <map> #include <optional> +#include <variant> #include "elna/boot/ast.h" #include "elna/boot/result.h" @@ -29,102 +30,60 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { /** - * Error declaring a symbol. + * Error declaring or using a symbol (undeclared, redefinition, + * local export). */ - class declaration_error : public error + class symbol_error : public error { public: - enum class kind - { - undeclared, - local_export - }; + 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>; - declaration_error(const kind error_kind, - const boot::identifier& identifier); + symbol_error(const source_position position, payload_type payload); std::string what() const override; + std::optional<std::pair<std::string, source_position>> note() const override; private: - std::string identifier; - kind error_kind; + payload_type payload; }; /** - * Attempted to redefine a name that is already in use in the - * current scope. + * \c const qualifier used incorrectly — wrong position or + * duplicate. */ - class redefinition_error : public error + class const_qualifier_error : public error { - std::string identifier; - std::optional<source_position> original; - public: - redefinition_error(const boot::identifier& identifier, - std::optional<source_position> original); - - std::string what() const override; + enum class kind { array_position, duplicate }; - std::optional<std::pair<std::string, source_position>> note() const override; - }; - - /** - * Array size before \c const is not valid — only \c const [n]T is - * accepted, not \c [n]const T. - */ - class const_array_error : public error - { - public: - explicit const_array_error(const source_position position); + const_qualifier_error(const source_position position, kind error_kind); std::string what() const override; - }; - - /** - * Direct \c const \c const T is not valid — write it once. - * Merging through an alias is fine (C++ rule). - */ - class double_const_error : public error - { - public: - explicit double_const_error(const source_position position); - std::string what() const override; + private: + kind error_kind; }; /** - * Access to a field that does not exist in the given type. + * Error accessing or defining a member of a record or enumeration. */ - class field_not_found_error : public error + class member_error : public error { - std::string field_name; - type composite_type; - public: - field_not_found_error(const identifier& field_name, - type composite_type); + struct not_found { std::string name; type composite; }; + struct duplicate { std::string name; type aggregate; std::optional<source_position> original; std::optional<std::string> base; }; + using payload_type = std::variant<not_found, duplicate>; - std::string what() const override; - }; - - /** - * Field with the same name is already declared in this type. - */ - class duplicate_member_error : public error - { - std::string member_name; - type aggregate; - std::optional<source_position> original; - std::optional<std::string> base_name; - - public: - duplicate_member_error(const boot::identifier& member_name, - type aggregate, std::optional<source_position> original = std::nullopt, - std::optional<std::string> base_name = std::nullopt); + member_error(const source_position position, payload_type payload); std::string what() const override; - std::optional<std::pair<std::string, source_position>> note() const override; + + private: + payload_type payload; }; /** @@ -200,6 +159,9 @@ namespace elna::boot void visit(array_access_expression *expression) override; void visit(field_access_expression *expression) override; void visit(dereference_expression *expression) override; + + void visit(for_statement *statement) override; + void visit(literal<std::int32_t> *literal) override; void visit(literal<std::uint32_t> *literal) override; void visit(literal<double> *literal) override; diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h index 1680077..5fdc5cb 100644 --- a/include/elna/boot/type_check.h +++ b/include/elna/boot/type_check.h @@ -165,6 +165,19 @@ 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(). @@ -231,6 +244,7 @@ namespace elna::boot void visit(record_type_expression *expression) override; void visit(procedure_call *call) override; void visit(case_statement *statement) override; + void visit(for_statement *statement) override; void visit(record_constructor_expression *expression) override; void visit(array_constructor_expression *expression) override; void visit(slicing_expression *expression) override; diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 04b91fb..de95a7e 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -89,7 +89,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::import_declaration *) override; + void visit(boot::for_statement *statement) override; void visit(boot::while_statement *statement) override; void visit(boot::defer_statement *statement) override; void visit(boot::empty_statement *) override; diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index d826f25..1d48b00 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -66,7 +66,6 @@ namespace elna::gcc tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name); tree build_static_array_type(tree type, const std::uint64_t size); tree build_enumeration_type(const std::vector<std::string>& members); - tree build_label_decl(const char *name, location_t loc); tree extract_constant(tree expression); diff --git a/include/elna/gcc/elna1.h b/include/elna/gcc/elna1.h index b05cdf2..34cf091 100644 --- a/include/elna/gcc/elna1.h +++ b/include/elna/gcc/elna1.h @@ -73,16 +73,15 @@ struct GTY ((chain_next ("%h.level_chain"))) binding_level // Defer statement coupled with statements following it. vec<defer_scope, va_gc> *defers; + + // Variables local to a block. + tree names; }; struct GTY (()) language_function { - // Local variables and constants. - tree names; - // Lexical scope. struct binding_level *binding_level; }; #define f_binding_level DECL_STRUCT_FUNCTION(current_function_decl)->language->binding_level -#define f_names DECL_STRUCT_FUNCTION(current_function_decl)->language->names diff --git a/testsuite/runnable/for_by.elna b/testsuite/runnable/for_by.elna new file mode 100644 index 0000000..b219dc6 --- /dev/null +++ b/testsuite/runnable/for_by.elna @@ -0,0 +1,9 @@ +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_loop.elna b/testsuite/runnable/for_loop.elna new file mode 100644 index 0000000..02db679 --- /dev/null +++ b/testsuite/runnable/for_loop.elna @@ -0,0 +1,9 @@ +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/slice_equality.elna b/testsuite/runnable/slice_equality.elna new file mode 100644 index 0000000..1969088 --- /dev/null +++ b/testsuite/runnable/slice_equality.elna @@ -0,0 +1,10 @@ +var + lhs_payload, rhs_payload: [3]Int := [3]Int{ 2, 4, 6 } + lhs, rhs: []Int + +begin + lhs := lhs_payload.ptr[1 to 3]; + rhs := rhs_payload.ptr[1 to 3]; + + assert(lhs = rhs) +end. |
