aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/ast.cc33
-rw-r--r--boot/lexer.ll6
-rw-r--r--boot/parser.yy16
-rw-r--r--boot/semantic.cc80
-rw-r--r--doc/appendix.tex13
-rw-r--r--doc/type-system.tex4
-rw-r--r--gcc/gcc/elna-generic.cc24
-rw-r--r--include/elna/boot/ast.h25
-rw-r--r--include/elna/boot/semantic.h6
-rw-r--r--include/elna/gcc/elna-generic.h1
-rw-r--r--testsuite/compilable/record_construction.elna12
11 files changed, 171 insertions, 49 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 =
diff --git a/doc/appendix.tex b/doc/appendix.tex
index e98da0c..71b0cc4 100644
--- a/doc/appendix.tex
+++ b/doc/appendix.tex
@@ -73,8 +73,13 @@
\alt{} <trait>
\alt{} <cast>
\alt{} <procedure-call>
+ \alt{} <record-constructor>
\alt{} `(' <expression> `)'.
+<record-constructor> = <identifier> `\{' [<field-initializer> \{`,' <field-initializer>\}] `\}'.
+
+<field-initializer> = <identifier> `:' <expression>.
+
<factor> = <unary-operator> <factor> | <reference>.
<term> = <factor> \{<multiplication-operator> <factor>\}.
@@ -142,9 +147,7 @@
| <label-declaration> | <goto-statement> | <while-statement>
| <if-statement> | <case-statement> | $\varepsilon{}$.
-<statement-part> = [`begin' <statements>
- \alt{} `return' <expression>
- \alt{} `begin' <statements> `;' `return' <expression>].
+<statement-part> = [`begin' <statements>].
<constant-declaration> = <identifier-definition> `:=' <expression>.
@@ -167,7 +170,7 @@
<procedure-heading> = `(' [<parameter> \{`;' <parameter>\}] `)' <return-declaration>.
-<procedure-body> = <constant-part> <variable-part> <statement-part> `end'.
+<procedure-body> = <constant-part> <variable-part> <statement-part> [`return' <expression>] `end'.
<procedure-declaration> = `proc' <identifier-definition> <procedure-heading> \\
(<procedure-body> | `extern').
@@ -175,5 +178,5 @@
<program> = <import-part>
<constant-part> <type-part> <variable-part>
\{<procedure-declaration>\}
- <statement-part> `end' `.\@'.
+ <statement-part> [`return' <expression>] `end' `.\@'.
\end{grammar}
diff --git a/doc/type-system.tex b/doc/type-system.tex
index e1497be..102b0e4 100644
--- a/doc/type-system.tex
+++ b/doc/type-system.tex
@@ -65,7 +65,7 @@ end.
<procedure-heading> = `(' [<parameter> \{`;' <parameter>\}] `)' <return-declaration>.
-<procedure-body> = <constant-part> <variable-part> <statement-part> `end'.
+<procedure-body> = <constant-part> <variable-part> <statement-part> [`return' <expression>] `end'.
<procedure-declaration> = `proc' <identifier-definition> <procedure-heading> \\
(<procedure-body> | `extern').
@@ -109,7 +109,7 @@ type
var
u: U;
begin
- u := U(0, 1, 2);
+ u := U{x: 0, y: 1, z: 2};
u.x := 3
end.
\end{lstlisting}
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index 30dd68b..5749bbc 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -178,6 +178,30 @@ namespace elna::gcc
}
}
+ void generic_visitor::visit(boot::record_constructor_expression *expression)
+ {
+ tree type_decl = this->symbols->lookup(expression->type_name);
+
+ if (type_decl == NULL_TREE)
+ {
+ error_at(get_location(&expression->position()), "Type '%s' not declared",
+ expression->type_name.c_str());
+ this->current_expression = error_mark_node;
+ return;
+ }
+ tree record_type = TREE_TYPE(type_decl);
+ vec<constructor_elt, va_gc> *tree_arguments = nullptr;
+
+ for (const boot::field_initializer& initializer : expression->field_initializers)
+ {
+ tree field_decl = find_field_by_name(get_location(&expression->position()),
+ record_type, initializer.name);
+ initializer.value->accept(this);
+ CONSTRUCTOR_APPEND_ELT(tree_arguments, field_decl, this->current_expression);
+ }
+ this->current_expression = build_constructor(record_type, tree_arguments);
+ }
+
void generic_visitor::visit(boot::unit *unit)
{
for (boot::import_declaration *const declaration : unit->imports)
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index ab79c30..67e9fbe 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -60,6 +60,8 @@ namespace elna::boot
class type_declaration;
class procedure_call;
class cast_expression;
+ class record_constructor_expression;
+ struct field_initializer;
class assign_statement;
class if_statement;
class import_declaration;
@@ -97,6 +99,7 @@ namespace elna::boot
virtual void visit(type_declaration *) = 0;
virtual void visit(procedure_call *) = 0;
virtual void visit(cast_expression *) = 0;
+ virtual void visit(record_constructor_expression *) = 0;
virtual void visit(traits_expression *) = 0;
virtual void visit(assign_statement *) = 0;
virtual void visit(if_statement *) = 0;
@@ -152,6 +155,7 @@ namespace elna::boot
[[noreturn]] virtual void visit(procedure_call *) override;
[[noreturn]] virtual void visit(unit *) override;
[[noreturn]] virtual void visit(cast_expression *) override;
+ [[noreturn]] virtual void visit(record_constructor_expression *) override;
[[noreturn]] virtual void visit(traits_expression *) override;
[[noreturn]] virtual void visit(binary_expression *) override;
[[noreturn]] virtual void visit(unary_expression *) override;
@@ -194,6 +198,7 @@ namespace elna::boot
virtual void visit(procedure_call *call) override;
virtual void visit(unit *unit) override;
virtual void visit(cast_expression *expression) override;
+ virtual void visit(record_constructor_expression *expression) override;
virtual void visit(traits_expression *trait) override;
virtual void visit(binary_expression *expression) override;
virtual void visit(unary_expression *expression) override;
@@ -325,6 +330,26 @@ namespace elna::boot
record_type_expression *is_record() override;
};
+ struct field_initializer
+ {
+ std::string name;
+ expression *value;
+ };
+
+ class record_constructor_expression : public expression
+ {
+ public:
+ const std::string type_name;
+ const std::vector<field_initializer> field_initializers;
+
+ record_constructor_expression(const struct position position,
+ std::string&& type_name,
+ std::vector<field_initializer>&& field_initializers);
+ void accept(parser_visitor *visitor) override;
+
+ virtual ~record_constructor_expression() override;
+ };
+
/**
* Enumeration type.
*/
diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h
index 1e81ebb..5ef4bc8 100644
--- a/include/elna/boot/semantic.h
+++ b/include/elna/boot/semantic.h
@@ -150,10 +150,6 @@ namespace elna::boot
bool check_unresolved_symbol(std::shared_ptr<alias_type> alias,
std::vector<std::string>& path);
- std::size_t 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);
-
public:
explicit type_analysis_visitor(symbol_bag bag);
@@ -165,6 +161,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(record_constructor_expression *expression) override;
};
/**
@@ -195,6 +192,7 @@ namespace elna::boot
void visit(pointer_type_expression *expression) override;
void visit(type_declaration *declaration) override;
void visit(record_type_expression *expression) override;
+ void visit(record_constructor_expression *expression) override;
void visit(procedure_type_expression *expression) override;
void visit(enumeration_type_expression *expression) override;
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index 2fe4d48..f30f9d6 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -83,6 +83,7 @@ namespace elna::gcc
void visit(boot::field_access_expression *expression) override;
void visit(boot::dereference_expression *expression) override;
void visit(boot::unit *unit) override;
+ void visit(boot::record_constructor_expression *expression) override;
void visit(boot::assign_statement *statement) override;
void visit(boot::if_statement *statement) override;
void visit(boot::import_declaration *) override;
diff --git a/testsuite/compilable/record_construction.elna b/testsuite/compilable/record_construction.elna
new file mode 100644
index 0000000..00e60db
--- /dev/null
+++ b/testsuite/compilable/record_construction.elna
@@ -0,0 +1,12 @@
+type
+ R = record
+ x, y: Int
+ end
+
+var
+ r: R := R{x: 1, y: 2}
+
+begin
+ assert(r.x = 1 & r.y = 2)
+return 0
+end.