aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-07-13 23:59:36 +0200
committerEugen Wissner <belka@caraus.de>2026-07-13 23:59:36 +0200
commit8ceb48a4e60b8ff89f28372da4f66cd152e2e0fe (patch)
treeb5e5522c47de3a7f62cd51a3258bfec7e99c062e /boot
parent500c0676b3f6cd5a2297987d5b0dc7ccf34a28d9 (diff)
downloadelna-8ceb48a4e60b8ff89f28372da4f66cd152e2e0fe.tar.gz
Change record constructor syntax
Diffstat (limited to 'boot')
-rw-r--r--boot/ast.cc33
-rw-r--r--boot/lexer.ll6
-rw-r--r--boot/parser.yy16
-rw-r--r--boot/semantic.cc80
4 files changed, 97 insertions, 38 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index 118d92c..36215c1 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -114,6 +114,11 @@ namespace elna::boot
__builtin_unreachable();
}
+ void empty_visitor::visit(record_constructor_expression *)
+ {
+ __builtin_unreachable();
+ }
+
void empty_visitor::visit(traits_expression *)
{
__builtin_unreachable();
@@ -403,6 +408,14 @@ namespace elna::boot
expression->target().accept(this);
}
+ void walking_visitor::visit(record_constructor_expression *expression)
+ {
+ for (const field_initializer& initializer : expression->field_initializers)
+ {
+ initializer.value->accept(this);
+ }
+ }
+
void walking_visitor::visit(traits_expression *trait)
{
if (!trait->parameters.empty())
@@ -623,6 +636,26 @@ namespace elna::boot
return this;
}
+ record_constructor_expression::record_constructor_expression(const struct position position,
+ std::string&& type_name,
+ std::vector<field_initializer>&& field_initializers)
+ : node(position), type_name(std::move(type_name)), field_initializers(std::move(field_initializers))
+ {
+ }
+
+ void record_constructor_expression::accept(parser_visitor *visitor)
+ {
+ visitor->visit(this);
+ }
+
+ record_constructor_expression::~record_constructor_expression()
+ {
+ for (const field_initializer& initializer : field_initializers)
+ {
+ delete initializer.value;
+ }
+ }
+
variable_declaration::variable_declaration(const struct position position,
std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type,
expression *initializer)
diff --git a/boot/lexer.ll b/boot/lexer.ll
index 05015f6..4367caa 100644
--- a/boot/lexer.ll
+++ b/boot/lexer.ll
@@ -236,6 +236,12 @@ of {
\] {
return yy::parser::make_RIGHT_SQUARE(this->location);
}
+\{ {
+ return yy::parser::make_LEFT_BRACE(this->location);
+}
+\} {
+ return yy::parser::make_RIGHT_BRACE(this->location);
+}
\<\< {
return yy::parser::make_SHIFT_LEFT(this->location);
}
diff --git a/boot/parser.yy b/boot/parser.yy
index f1bf915..ae2e5a8 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -81,6 +81,7 @@ along with GCC; see the file COPYING3. If not see
%token <std::string> STRING
%token <bool> BOOLEAN
%token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]"
+%token LEFT_BRACE "{" RIGHT_BRACE "}"
%token ASSIGNMENT ":="
EXCLAMATION "!"
AT "@" HAT "^"
@@ -148,6 +149,8 @@ along with GCC; see the file COPYING3. If not see
%type <std::unique_ptr<elna::boot::procedure_body>> procedure_body;
%type <elna::boot::field_declaration> field_declaration;
%type <std::vector<elna::boot::field_declaration>> optional_fields required_fields;
+%type <elna::boot::field_initializer> field_initializer;
+%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;
@@ -258,6 +261,10 @@ simple_expression:
| cast_expression { $$ = $1; }
| call_expression { $$ = $1; }
| "(" expression ")" { $$ = $2; }
+ | IDENTIFIER "{" field_initializers "}"
+ {
+ $$ = new boot::record_constructor_expression(boot::make_position(@1), std::move($1), std::move($3));
+ }
operand:
unary_expression { $$ = $1; }
| simple_expression { $$ = $1; }
@@ -421,6 +428,15 @@ required_fields:
optional_fields:
required_fields { std::swap($$, $1); }
| /* no fields */ {}
+field_initializer:
+ IDENTIFIER ":" expression { $$.name = std::move($1); $$.value = $3; }
+field_initializers:
+ field_initializer "," field_initializers
+ {
+ std::swap($$, $3);
+ $$.emplace($$.cbegin(), $1);
+ }
+ | field_initializer { $$.push_back($1); }
type_expression:
"[" INTEGER "]" type_expression
{
diff --git a/boot/semantic.cc b/boot/semantic.cc
index 97c316a..f46dce9 100644
--- a/boot/semantic.cc
+++ b/boot/semantic.cc
@@ -288,37 +288,6 @@ namespace elna::boot
walking_visitor::visit(expression);
}
- std::size_t type_analysis_visitor::match_record_fields(
- const std::shared_ptr<record_type>& record,
- std::vector<expression *>::const_iterator& argument_it,
- const std::vector<expression *>::const_iterator& argument_end)
- {
- std::size_t expected = 0;
-
- if (!record->base.empty())
- {
- if (auto base = inner_aliased_type(record->base).get<record_type>())
- {
- expected += match_record_fields(base, argument_it, argument_end);
- }
- }
- for (auto& field : record->fields)
- {
- ++expected;
- if (argument_it != argument_end)
- {
- (*argument_it)->accept(this);
- if (!is_assignable_from(field.second, (*argument_it)->type_decoration))
- {
- add_error<type_expectation_error>(type_expectation_error::kind::argument,
- (*argument_it)->position());
- }
- ++argument_it;
- }
- }
- return expected;
- }
-
void type_analysis_visitor::visit(procedure_call *call)
{
call->callable().accept(this);
@@ -346,16 +315,30 @@ namespace elna::boot
call->arguments.size(), call->position());
}
}
- else if (auto callable_record = inner_aliased_type(call->type_decoration).get<record_type>())
- {
- auto argument_it = std::cbegin(call->arguments);
- auto argument_end = std::cend(call->arguments);
+ }
- std::size_t expected = match_record_fields(callable_record, argument_it, argument_end);
+ void type_analysis_visitor::visit(record_constructor_expression *expression)
+ {
+ auto record = inner_aliased_type(expression->type_decoration).get<record_type>();
- if (call->arguments.size() != expected)
+ if (record == nullptr)
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::result, expression->position());
+ return;
+ }
+ for (const field_initializer& initializer : expression->field_initializers)
+ {
+ for (const type_field& field : record->fields)
{
- add_error<argument_count_error>(expected, call->arguments.size(), call->position());
+ if (field.first == initializer.name)
+ {
+ if (!is_assignable_from(field.second, initializer.value->type_decoration))
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::argument,
+ initializer.value->position());
+ }
+ break;
+ }
}
}
}
@@ -584,6 +567,27 @@ namespace elna::boot
this->current_type = type(result_type);
}
+ void name_analysis_visitor::visit(record_constructor_expression *expression)
+ {
+ if (auto type_symbol = this->bag.lookup(expression->type_name))
+ {
+ if (auto type_info = type_symbol->is_type())
+ {
+ expression->type_decoration = type_info->symbol;
+ }
+ }
+ for (const field_initializer& initializer : expression->field_initializers)
+ {
+ initializer.value->accept(this);
+ if (!expression->type_decoration.empty()
+ && lookup_field(expression->type_decoration, initializer.name).empty())
+ {
+ add_error<declaration_error>(declaration_error::kind::undeclared,
+ initializer.name, expression->position());
+ }
+ }
+ }
+
void name_analysis_visitor::visit(procedure_type_expression *expression)
{
std::shared_ptr<procedure_type> result_type =