From 8d8e771af944796eba10cfa9568d284daaab93a1 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 9 Jul 2026 09:21:57 +0200 Subject: Implement record extensions --- boot/ast.cc | 4 +- boot/parser.yy | 6 ++- boot/semantic.cc | 57 +++++++++++++++++++++----- boot/symbol.cc | 5 +++ gcc/gcc/elna-builtins.cc | 12 ++++++ include/elna/boot/ast.h | 4 +- include/elna/boot/semantic.h | 12 +++++- include/elna/boot/symbol.h | 5 ++- testsuite/runnable/record_base_assignment.elna | 22 ++++++++++ 9 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 testsuite/runnable/record_base_assignment.elna diff --git a/boot/ast.cc b/boot/ast.cc index 5885cff..3599308 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -383,8 +383,8 @@ namespace elna::boot } record_type_expression::record_type_expression(const struct position position, - std::vector&& fields) - : node(position), fields(std::move(fields)) + std::vector&& fields, std::optional base) + : node(position), fields(std::move(fields)), base(base) { } diff --git a/boot/parser.yy b/boot/parser.yy index b05b544..7797dd1 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -460,7 +460,11 @@ type_expression: } | "record" optional_fields "end" { - $$ = new boot::record_type_expression(boot::make_position(@1), std::move($2)); + $$ = new boot::record_type_expression(boot::make_position(@1), std::move($2), std::nullopt); + } + | "record" "(" IDENTIFIER ")" optional_fields "end" + { + $$ = new boot::record_type_expression(boot::make_position(@1), std::move($5), std::move($3)); } | "union" required_fields "end" { diff --git a/boot/semantic.cc b/boot/semantic.cc index bc7c2d2..a48226b 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -78,7 +78,17 @@ namespace elna::boot std::string return_error::what() const { - return "Procedure '" + identifier + "' is expected to return, but does not have a return statement"; + return "Procedure '" + this->identifier + "' is expected to return, but does not have a return statement"; + } + + base_type_error::base_type_error(const std::string& base, const struct position position) + : error(position), base(base) + { + } + + std::string base_type_error::what() const + { + return "Record base type '" + this->base + "' is not a record."; } type_analysis_visitor::type_analysis_visitor(symbol_bag bag) @@ -291,8 +301,37 @@ namespace elna::boot void name_analysis_visitor::visit(record_type_expression *type_expression) { - auto result_type = std::make_shared(); - + std::shared_ptr result_type; + if (type_expression->base.has_value()) + { + if (auto unresolved_alias = this->bag.declared(type_expression->base.value())) + { + result_type = std::make_shared(type(unresolved_alias)); + } + else if (auto base_symbol = this->bag.lookup(type_expression->base.value())) + { + if (auto base_type_info = base_symbol->is_type()) + { + result_type = std::make_shared(base_type_info->symbol); + } + else + { + add_error(type_expression->base.value(), type_expression->position()); + this->current_type = type(); + return; + } + } + else + { + add_error(type_expression->base.value(), type_expression->position()); + this->current_type = type(); + return; + } + } + else + { + result_type = std::make_shared(); + } result_type->fields = build_composite_type(type_expression->fields); this->current_type = type(result_type); @@ -498,7 +537,7 @@ namespace elna::boot { call->callable().accept(this); } - for (expression *const argument: call->arguments) + for (expression *const argument : call->arguments) { argument->accept(this); } @@ -551,15 +590,13 @@ namespace elna::boot expression->operand().accept(this); } - void name_analysis_visitor::visit(named_expression *type_expression) + void name_analysis_visitor::visit(named_expression *expression) { - auto unresolved_alias = this->bag.declared(type_expression->name); - - if (unresolved_alias != nullptr) + if (auto unresolved_alias = this->bag.declared(expression->name)) { this->current_type = type(unresolved_alias); } - else if (auto from_symbol_table = this->bag.lookup(type_expression->name)) + else if (auto from_symbol_table = this->bag.lookup(expression->name)) { if (auto type_symbol = from_symbol_table->is_type()) { @@ -568,7 +605,7 @@ namespace elna::boot } else { - add_error(type_expression->name, type_expression->position()); + add_error(expression->name, expression->position()); this->current_type = type(); } } diff --git a/boot/symbol.cc b/boot/symbol.cc index 902d331..d01eb8e 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -268,6 +268,11 @@ namespace elna::boot { } + record_type::record_type(type base) + : base(base) + { + } + procedure_type::procedure_type(return_t return_type) : return_type(return_type) { diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 7c97027..7817c7e 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -124,6 +124,18 @@ namespace elna::gcc { tree composite_type_node = make_node(RECORD_TYPE); + if (!reference->base.empty()) + { + tree base_type_node = get_inner_alias(reference->base, symbols); + + for (tree base_field = TYPE_FIELDS(base_type_node); base_field != NULL_TREE; + base_field = TREE_CHAIN(base_field)) + { + tree cloned_field = build_field(UNKNOWN_LOCATION, composite_type_node, + IDENTIFIER_POINTER(DECL_NAME(base_field)), TREE_TYPE(base_field)); + TYPE_FIELDS(composite_type_node) = chainon(TYPE_FIELDS(composite_type_node), cloned_field); + } + } build_composite_type(reference->fields, composite_type_node, symbols); return composite_type_node; diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 0642d44..6942ca5 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -287,8 +287,10 @@ namespace elna::boot { public: const std::vector fields; + const std::optional base; - record_type_expression(const struct position position, std::vector&& fields); + record_type_expression(const struct position position, + std::vector&& fields, std::optional base); void accept(parser_visitor *visitor) override; record_type_expression *is_record() override; diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index a0a4a73..6bc7fe0 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -78,6 +78,16 @@ namespace elna::boot std::string what() const override; }; + class base_type_error : public error + { + const std::string base; + + public: + base_type_error(const std::string& base, const struct position position); + + std::string what() const override; + }; + /** * Checks types. */ @@ -153,7 +163,7 @@ namespace elna::boot void visit(traits_expression *trait) override; void visit(binary_expression *expression) override; void visit(unary_expression *expression) override; - void visit(named_expression *type_expression) override; + void visit(named_expression *expression) override; void visit(array_access_expression *expression) override; void visit(field_access_expression *expression) override; void visit(dereference_expression *expression) override; diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index 5ef917e..beb44e2 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -129,6 +129,9 @@ namespace elna::boot struct record_type { std::vector fields; + const type base; + + explicit record_type(type base = type()); }; struct union_type @@ -143,7 +146,7 @@ namespace elna::boot std::vector parameters; const return_t return_type; - procedure_type(return_t return_type = return_t()); + explicit procedure_type(return_t return_type = return_t()); }; struct enumeration_type diff --git a/testsuite/runnable/record_base_assignment.elna b/testsuite/runnable/record_base_assignment.elna new file mode 100644 index 0000000..c91383d --- /dev/null +++ b/testsuite/runnable/record_base_assignment.elna @@ -0,0 +1,22 @@ +type + B = record + x: Word + end + R = record(B) + y: Word + end + +proc f(): Int +var + r: R +begin + r.x := 3u; + r.y := 2u; + + assert(r.x = 3u & r.y = 2u); + + return 0 +end + + return f() +end. -- cgit v1.2.3