aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/ast.cc54
-rw-r--r--boot/evaluator.cc2
-rw-r--r--boot/lexer.ll6
-rw-r--r--boot/name_analysis.cc259
-rw-r--r--boot/parser.yy55
-rw-r--r--boot/type_check.cc50
-rw-r--r--gcc/gcc/elna-builtins.cc2
-rw-r--r--gcc/gcc/elna-generic.cc103
-rw-r--r--gcc/gcc/elna-tree.cc9
-rw-r--r--include/elna/boot/ast.h32
-rw-r--r--include/elna/boot/name_analysis.h98
-rw-r--r--include/elna/boot/type_check.h14
-rw-r--r--include/elna/gcc/elna-generic.h2
-rw-r--r--include/elna/gcc/elna-tree.h1
-rw-r--r--include/elna/gcc/elna1.h7
-rw-r--r--testsuite/runnable/for_by.elna9
-rw-r--r--testsuite/runnable/for_loop.elna9
-rw-r--r--testsuite/runnable/slice_equality.elna10
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.