aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/ast.cc65
-rw-r--r--boot/lexer.ll10
-rw-r--r--boot/name_analysis.cc98
-rw-r--r--boot/parser.yy29
-rw-r--r--boot/symbol.cc43
-rw-r--r--boot/type_check.cc63
-rw-r--r--gcc/gcc/elna-generic.cc139
-rw-r--r--gcc/gcc/elna-tree.cc5
-rw-r--r--include/elna/boot/ast.h40
-rw-r--r--include/elna/boot/name_analysis.h20
-rw-r--r--include/elna/boot/symbol.h10
-rw-r--r--include/elna/boot/type_check.h30
-rw-r--r--include/elna/gcc/elna-generic.h5
-rw-r--r--include/elna/gcc/elna-tree.h1
-rw-r--r--testsuite/runnable/for_by.elna9
-rw-r--r--testsuite/runnable/for_each_array.elna12
-rw-r--r--testsuite/runnable/for_each_slice.elna15
-rw-r--r--testsuite/runnable/for_loop.elna9
-rw-r--r--testsuite/runnable/for_with.elna9
-rw-r--r--testsuite/runnable/repeat_loop.elna11
20 files changed, 377 insertions, 246 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index a50d3d3..69d72fb 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -91,6 +91,11 @@ namespace elna::boot
__builtin_unreachable();
}
+ void empty_visitor::visit(repeat_statement *)
+ {
+ __builtin_unreachable();
+ }
+
void empty_visitor::visit(for_statement *)
{
__builtin_unreachable();
@@ -291,14 +296,18 @@ namespace elna::boot
}
}
- void walking_visitor::visit(for_statement *statement)
+ void walking_visitor::visit(repeat_statement *statement)
{
- statement->initial_value().accept(this);
- statement->final_value().accept(this);
- if (statement->step != nullptr)
+ statement->condition().accept(this);
+ for (auto *body_statement : statement->body)
{
- statement->step->accept(this);
+ body_statement->accept(this);
}
+ }
+
+ void walking_visitor::visit(for_statement *statement)
+ {
+ statement->range().accept(this);
for (auto *body_statement : statement->body)
{
body_statement->accept(this);
@@ -1541,19 +1550,42 @@ namespace elna::boot
}
}
+ repeat_statement::repeat_statement(const source_position position, std::vector<statement *>&& body,
+ expression *condition)
+ : node(position), m_condition(condition), body(std::move(body))
+ {
+ }
+
+ repeat_statement::~repeat_statement()
+ {
+ delete this->m_condition;
+ for (const statement *body_statement : this->body)
+ {
+ delete body_statement;
+ }
+ }
+
+ void repeat_statement::accept(parser_visitor *visitor)
+ {
+ visitor->visit(this);
+ }
+
+ expression& repeat_statement::condition()
+ {
+ return *this->m_condition;
+ }
+
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)
+ expression *range, std::vector<statement *>&& body, identifier *const counter = nullptr)
+ : node(position), m_range(range), control_variable(std::move(control_variable)),
+ counter(counter), body(std::move(body))
{
}
for_statement::~for_statement()
{
- delete this->m_initial_value;
- delete this->m_final_value;
- delete this->step;
+ delete this->m_range;
+ delete this->counter;
for (const statement *body_statement : this->body)
{
@@ -1566,14 +1598,9 @@ namespace elna::boot
visitor->visit(this);
}
- expression& for_statement::initial_value()
- {
- return *this->m_initial_value;
- }
-
- expression& for_statement::final_value()
+ expression& for_statement::range()
{
- return *this->m_final_value;
+ return *this->m_range;
}
const char *print_binary_operator(const binary_operator operation)
diff --git a/boot/lexer.ll b/boot/lexer.ll
index c363ddd..89fe85c 100644
--- a/boot/lexer.ll
+++ b/boot/lexer.ll
@@ -86,8 +86,14 @@ while {
for {
return yy::parser::make_FOR(this->location);
}
-by {
- return yy::parser::make_BY(this->location);
+repeat {
+ return yy::parser::make_REPEAT(this->location);
+}
+until {
+ return yy::parser::make_UNTIL(this->location);
+}
+with {
+ return yy::parser::make_WITH(this->location);
}
do {
return yy::parser::make_DO(this->location);
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index f932a5d..0d25039 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -21,31 +21,32 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
- symbol_error::symbol_error(const source_position position, payload_type payload)
+ declaration_error::declaration_error(const source_position position, payload_type payload)
: error(position), payload(std::move(payload))
{
}
- std::string symbol_error::what() const
+ std::string declaration_error::what() const
{
- return std::visit([](const auto& pay) -> std::string {
- using T = std::decay_t<decltype(pay)>;
+ return std::visit([](const auto& payload) -> std::string {
+ using T = std::decay_t<decltype(payload)>;
+
if constexpr (std::is_same_v<T, undeclared>)
{
- return "Type '" + pay.name + "' not declared";
+ return "Type '" + payload.name + "' not declared";
}
else if constexpr (std::is_same_v<T, local_export>)
{
- return "Local symbol '" + pay.name + "' cannot be exported";
+ return "Local symbol '" + payload.name + "' cannot be exported";
}
else if constexpr (std::is_same_v<T, redefinition>)
{
- return "Symbol '" + pay.name + "' has been already defined";
+ return "Symbol '" + payload.name + "' has been already defined";
}
- }, payload);
+ }, this->payload);
}
- std::optional<std::pair<std::string, source_position>> symbol_error::note() const
+ std::optional<std::pair<std::string, source_position>> declaration_error::note() const
{
if (const auto *redef = std::get_if<redefinition>(&payload))
{
@@ -240,16 +241,9 @@ namespace elna::boot
return lookup_field(record->base, field_name);
}
}
- else if (auto array = resolved_type.get<array_type>())
- {
- if (auto field = lookup_pointer_like_field(field_name, array->base))
- {
- return field.value();
- }
- }
- else if (auto slice = resolved_type.get<slice_type>())
+ else if (auto range_base = get_range_base_type(resolved_type))
{
- if (auto field = lookup_pointer_like_field(field_name, slice->base))
+ if (auto field = lookup_pointer_like_field(field_name, range_base))
{
return field.value();
}
@@ -388,8 +382,8 @@ namespace elna::boot
}
else
{
- add_error<symbol_error>(expression->base.value().position(),
- symbol_error::undeclared{.name = expression->base.value().name()});
+ add_error<declaration_error>(expression->base.value().position(),
+ declaration_error::undeclared{.name = expression->base.value().name()});
this->current_type = type();
return;
}
@@ -417,8 +411,8 @@ namespace elna::boot
}
else
{
- add_error<symbol_error>(expression->type_name.position(),
- symbol_error::undeclared{.name = expression->type_name.name()});
+ add_error<declaration_error>(expression->type_name.position(),
+ declaration_error::undeclared{.name = expression->type_name.name()});
}
for (const field_initializer& initializer : expression->field_initializers)
{
@@ -426,8 +420,8 @@ namespace elna::boot
if (!expression->type_decoration.empty()
&& lookup_field(expression->type_decoration, initializer.name()).empty())
{
- add_error<symbol_error>(initializer.id().position(),
- symbol_error::undeclared{.name = initializer.id().name()});
+ add_error<declaration_error>(initializer.id().position(),
+ declaration_error::undeclared{.name = initializer.id().name()});
}
}
}
@@ -509,8 +503,8 @@ for (const auto& member : expression->members)
if (!this->bag.enter(name, variable_symbol))
{
auto original = this->bag.lookup(name);
- add_error<symbol_error>(position,
- symbol_error::redefinition{.name = name, .original = original->position});
+ add_error<declaration_error>(position,
+ declaration_error::redefinition{.name = name, .original = original->position});
}
return variable_symbol;
}
@@ -654,8 +648,8 @@ for (const auto& member : expression->members)
}
else
{
- add_error<symbol_error>(trait->name.position(),
- symbol_error::undeclared{.name = trait->name.name()});
+ add_error<declaration_error>(trait->name.position(),
+ declaration_error::undeclared{.name = trait->name.name()});
}
}
@@ -711,18 +705,12 @@ for (const auto& member : expression->members)
walking_visitor::visit(expression);
auto resolved_base = resolve_underlying_type(expression->base().type_decoration);
- if (auto array = resolved_base.get<array_type>())
- {
- expression->type_decoration = array->base;
- }
- else if (auto slice = resolved_base.get<slice_type>())
- {
- expression->type_decoration = slice->base;
- }
- // Elements of a constant array are constant themselves since a static
- // array is a holistic type.
- if (!expression->type_decoration.empty())
+ if (auto range_base = get_range_base_type(resolved_base))
{
+ expression->type_decoration = range_base;
+
+ // Elements of a constant array are constant themselves since a static
+ // array is a holistic type.
expression->type_decoration = qualify_member_type(expression->type_decoration,
expression->base().type_decoration);
}
@@ -763,18 +751,20 @@ 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->range().accept(this);
+ auto resolved_range = resolve_underlying_type(statement->range().type_decoration);
+ const type control_variable_base_type = get_range_base_type(resolved_range);
+ const type control_variable_pointer_type = type(std::make_shared<pointer_type>(control_variable_base_type));
+ this->current_type = type(std::make_shared<constant_type>(control_variable_pointer_type));
- 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);
+ register_variable(statement->control_variable.name(), false, statement->control_variable.position());
+ if (statement->counter != nullptr)
+ {
+ this->current_type = lookup_primitive_type("Word");
+ register_variable(statement->counter->name(), false, statement->counter->position());
+ }
for (auto *body_statement : statement->body)
{
body_statement->accept(this);
@@ -813,8 +803,8 @@ for (const auto& member : expression->members)
}
else
{
- add_error<symbol_error>(expression->position(),
- symbol_error::undeclared{.name = expression->name});
+ add_error<declaration_error>(expression->position(),
+ declaration_error::undeclared{.name = expression->name});
}
}
@@ -890,8 +880,8 @@ for (const auto& member : expression->members)
if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second)
{
- add_error<symbol_error>(declaration->identifier.id().position(),
- symbol_error::redefinition{.name = declaration->identifier.id().name(),
+ add_error<declaration_error>(declaration->identifier.id().position(),
+ declaration_error::redefinition{.name = declaration->identifier.id().name(),
.original = declaration->position()});
}
}
@@ -914,8 +904,8 @@ for (const auto& member : expression->members)
{
if (variable_identifier.exported())
{
- add_error<symbol_error>(variable_identifier.id().position(),
- symbol_error::local_export{.name = variable_identifier.id().name()});
+ add_error<declaration_error>(variable_identifier.id().position(),
+ declaration_error::local_export{.name = variable_identifier.id().name()});
}
}
}
diff --git a/boot/parser.yy b/boot/parser.yy
index 7d93dde..4fdbf84 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -100,7 +100,7 @@ along with GCC; see the file COPYING3. If not see
RECORD "record"
EXTERN "extern"
IF "if" THEN "then" ELSE "else" ELSIF "elsif"
- WHILE "while" DO "do" FOR "for" BY "by"
+ WHILE "while" DO "do" FOR "for" REPEAT "repeat" UNTIL "until" WITH "with"
RETURN "return"
IMPORT "import"
BEGIN_BLOCK "begin"
@@ -130,8 +130,7 @@ along with GCC; see the file COPYING3. If not see
%type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part;
%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 by_step;
+%type <elna::boot::expression *> expression operand simple_expression procedure_return;
%type <elna::boot::unary_expression *> unary_expression;
%type <elna::boot::binary_expression *> binary_expression;
%type <std::vector<elna::boot::expression *>> expressions actual_parameter_list;
@@ -152,7 +151,7 @@ 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 <std::unique_ptr<elna::boot::identifier>> identifier;
+%type <std::unique_ptr<elna::boot::identifier>> identifier with_counter;
%type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition;
%type <std::vector<elna::boot::identifier_definition>> identifier_definitions;
%type <std::vector<std::string>> import_declaration;
@@ -210,8 +209,8 @@ call_expression: designator_expression actual_parameter_list
{
$$ = new boot::procedure_call(boot::make_position(@$), $1, $2);
}
-by_step:
- "by" expression { $$ = $2; }
+with_counter:
+ "with" identifier { $$ = $2; }
| %empty { $$ = nullptr; }
elsif_do_statements:
"elsif" expression "do" statements elsif_do_statements
@@ -243,16 +242,14 @@ literal:
| CHARACTER { $$ = new boot::literal<unsigned char>(boot::make_position(@$), $1.at(0)); }
| "nil" { $$ = new boot::literal<std::nullptr_t>(boot::make_position(@$), nullptr); }
| STRING { $$ = new boot::literal<std::string>(boot::make_position(@$), $1); }
-traits_expression:
- TRAIT "(" type_expressions ")"
+simple_expression:
+ literal { $$ = $1; }
+ | designator_expression { $$ = $1; }
+ | TRAIT "(" type_expressions ")"
{
$$ = new boot::traits_expression(boot::make_position(@$),
boot::identifier($1, boot::make_position(@1)), $3);
}
-simple_expression:
- literal { $$ = $1; }
- | designator_expression { $$ = $1; }
- | traits_expression { $$ = $1; }
| "cast" "(" expression ":" type_expression ")"
{
$$ = new boot::cast_expression(boot::make_position(@$), $5, $3);
@@ -389,9 +386,13 @@ 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"
+ | "repeat" statements "until" expression
+ {
+ $$ = new boot::repeat_statement(boot::make_position(@$), $2, $4);
+ }
+ | "for" identifier "of" expression with_counter "do" statements "end"
{
- $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, $6, $9, $7);
+ $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, $7, $5.release());
}
| "if" expression "then" statements elsif_then_statements else_statements "end"
{
diff --git a/boot/symbol.cc b/boot/symbol.cc
index da948fd..5e303fb 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -155,6 +155,11 @@ namespace elna::boot
return resolved_this.empty() && resolved_that.empty();
}
+ type::operator bool() const
+ {
+ return !empty();
+ }
+
bool type::empty() const
{
return std::holds_alternative<std::monostate>(payload);
@@ -458,35 +463,41 @@ namespace elna::boot
bool is_numeric_type(const type& checked)
{
- type const resolved = resolve_underlying_type(checked);
-
- return is_primitive_type(resolved, "Int")
- || is_primitive_type(resolved, "Word")
- || is_primitive_type(resolved, "Float");
+ return is_primitive_type(checked, "Int")
+ || is_primitive_type(checked, "Word")
+ || is_primitive_type(checked, "Float");
}
bool is_integral_type(const type& checked)
{
- type const resolved = resolve_underlying_type(checked);
-
- return is_primitive_type(resolved, "Int")
- || is_primitive_type(resolved, "Word");
+ return is_primitive_type(checked, "Int")
+ || is_primitive_type(checked, "Word");
}
bool is_discrete_type(const type& checked)
{
- type const resolved = resolve_underlying_type(checked);
-
- return is_primitive_type(resolved, "Int")
- || is_primitive_type(resolved, "Word")
- || is_primitive_type(resolved, "Bool")
- || is_primitive_type(resolved, "Char");
+ return is_integral_type(checked)
+ || is_primitive_type(checked, "Bool")
+ || is_primitive_type(checked, "Char");
}
bool is_any_pointer_type(const type& checked)
{
return checked.get<pointer_type>() != nullptr
|| checked.get<procedure_type>() != nullptr
- || is_primitive_type(resolve_underlying_type(checked), "Pointer");
+ || is_primitive_type(checked, "Pointer");
+ }
+
+ type get_range_base_type(const type& range)
+ {
+ if (auto array = range.get<array_type>())
+ {
+ return array->base;
+ }
+ else if (auto slice = range.get<slice_type>())
+ {
+ return slice->base;
+ }
+ return type();
}
}
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 1689e65..761c4b2 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -93,14 +93,23 @@ namespace elna::boot
+ "' is expected to return, but does not have a return statement";
}
- base_type_error::base_type_error(type actual, const source_position position)
- : error(position), actual(std::move(actual))
+ type_kind_error::type_kind_error(const source_position position, kind type_kind, const type& actual)
+ : error(position), type_kind(type_kind), actual(actual)
{
}
- std::string base_type_error::what() const
+ std::string type_kind_error::what() const
{
- return "'" + actual.to_string() + "' is not a record type";
+ switch (this->type_kind)
+ {
+ case kind::record_base:
+ return "'" + this->actual.to_string() + "' is not a record type";
+ case kind::for_loop:
+ return "for-loop variable must be an array or a slice, got '"
+ + this->actual.to_string() + "'";
+ default:
+ __builtin_unreachable();
+ }
}
argument_count_error::argument_count_error(std::size_t expected, std::size_t actual,
@@ -467,31 +476,13 @@ 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);
+ statement->range().accept(this);
+ auto resolved_range = resolve_underlying_type(statement->range().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)
+ if (!get_range_base_type(resolved_range))
{
- 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);
- }
+ add_error<type_kind_error>(statement->range().position(), type_kind_error::kind::for_loop,
+ statement->range().type_decoration);
}
this->bag.enter(statement->symbols);
for (auto *body_statement : statement->body)
@@ -523,14 +514,16 @@ namespace elna::boot
auto const base_symbol = this->bag.lookup(expression->base.value().name());
if (base_symbol == nullptr || base_symbol->is_type() == nullptr)
{
- add_error<base_type_error>(type(), expression->position());
+ add_error<type_kind_error>(expression->position(),
+ type_kind_error::kind::record_base, type());
}
else
{
type const base_type = resolve_underlying_type(base_symbol->is_type()->symbol);
if (base_type.get<record_type>() == nullptr)
{
- add_error<base_type_error>(base_type, expression->position());
+ add_error<type_kind_error>(expression->position(),
+ type_kind_error::kind::record_base, base_type);
}
}
}
@@ -685,18 +678,6 @@ 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)
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index 6fe3da9..ebf4f8f 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -947,7 +947,7 @@ namespace elna::gcc
if (TREE_CODE(aggregate_type) == ARRAY_TYPE && expression->field() == "length")
{
- this->current_expression = convert(build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST),
+ this->current_expression = fold_convert(elna_word_type_node,
TYPE_MAX_VALUE(TYPE_DOMAIN(aggregate_type)));
}
else if (TREE_CODE(aggregate_type) == ARRAY_TYPE && expression->field() == "ptr")
@@ -1082,69 +1082,120 @@ namespace elna::gcc
return build3(COND_EXPR, void_type_node, condition, then_body, next);
}
- void generic_visitor::visit(boot::for_statement *statement)
+ tree generic_visitor::declare_local_variable(const boot::identifier& name,
+ const boot::variable_info& info, tree initial_value)
{
- 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);
+ tree decl = declare_variable(name.name(), info, this->symbols);
+ DECL_CONTEXT(decl) = current_function_decl;
+ f_binding_level->names = chainon(f_binding_level->names, decl);
+ DECL_INITIAL(decl) = initial_value;
+ tree declaration_statement = build1_loc(get_location(&name.position()),
+ DECL_EXPR, void_type_node, decl);
append_statement(declaration_statement);
- tree control_type = TREE_TYPE(control_variable_declaration);
- if (statement->step == nullptr)
- {
- step = build_int_cst_type(control_type, 1);
- }
+ return decl;
+ }
+ std::pair<tree, tree> generic_visitor::build_loop_head(tree control_variable_declaration, tree limit,
+ tree_code comparison, location_t check_location)
+ {
// 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 condition = fold_build2(comparison, elna_bool_type_node, control_variable_declaration, limit);
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);
+ return { check_label, end_label };
+ }
+
+ void generic_visitor::visit(boot::repeat_statement *statement)
+ {
+ tree loop_label = create_artificial_label(get_location(&statement->position()));
+ tree goto_loop = build1(GOTO_EXPR, void_type_node, loop_label);
+ append_statement(build1(LABEL_EXPR, void_type_node, loop_label));
+
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);
+ statement->condition().accept(this);
+ tree condition_statement = build3(COND_EXPR, void_type_node,
+ this->current_expression, NULL_TREE, goto_loop);
+ append_statement(condition_statement);
+ }
+
+ void generic_visitor::visit(boot::for_statement *statement)
+ {
+ statement->range().accept(this);
+ tree range_expression = this->current_expression;
+ location_t location = get_location(&statement->position());
+ tree start_pointer;
+ tree length;
+
+ if (TREE_CODE(TREE_TYPE(range_expression)) == ARRAY_TYPE)
+ {
+ tree element_type = TREE_TYPE(TREE_TYPE(range_expression));
+ tree array_reference = build4_loc(location, ARRAY_REF, element_type,
+ range_expression, size_one_node, NULL_TREE, NULL_TREE);
+
+ start_pointer = build1(ADDR_EXPR, build_pointer_type(element_type), array_reference);
+ length = TYPE_MAX_VALUE(TYPE_DOMAIN(TREE_TYPE(range_expression)));
+ }
+ else
+ {
+ tree ptr_field = TYPE_FIELDS(TREE_TYPE(range_expression));
+ tree length_field = TREE_CHAIN(ptr_field);
+
+ start_pointer = build3_loc(location, COMPONENT_REF, TREE_TYPE(ptr_field),
+ range_expression, ptr_field, NULL_TREE);
+ length = build3_loc(location, COMPONENT_REF, TREE_TYPE(length_field),
+ range_expression, length_field, NULL_TREE);
+ }
+ tree end_pointer = do_pointer_arithmetic(boot::binary_operator::sum, start_pointer, length, location);
+
+ 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(
+ boot::resolve_underlying_type(control_variable_info->is_variable()->symbol),
+ control_variable_info->is_variable()->is_extern);
+ tree control_variable_declaration = declare_local_variable(
+ statement->control_variable, unqualified_info, start_pointer);
+
+ tree elna_one = build_int_cst_type(elna_word_type_node, 1);
+ tree counter_declaration{ NULL_TREE };
+ if (statement->counter != nullptr)
+ {
+ auto counter_info = statement->symbols->lookup(statement->counter->name());
+ counter_declaration = declare_local_variable(*statement->counter,
+ *counter_info->is_variable(), elna_one);
+ }
+ auto [check_label, end_label] = build_loop_head(control_variable_declaration,
+ end_pointer, LT_EXPR, location);
+
+ for (auto *body_statement : statement->body)
+ {
+ body_statement->accept(this);
+ }
+ tree pointer_plus = do_pointer_arithmetic(boot::binary_operator::sum,
+ control_variable_declaration, elna_one, location);
+ append_statement(build2(MODIFY_EXPR, void_type_node, control_variable_declaration, pointer_plus));
+
+ if (counter_declaration != NULL_TREE)
+ {
+ tree counter_plus = fold_build2(PLUS_EXPR, elna_word_type_node, counter_declaration, elna_one);
+ append_statement(build2(MODIFY_EXPR, void_type_node, counter_declaration, counter_plus));
+ }
+ append_statement(build1(GOTO_EXPR, void_type_node, check_label));
+ append_statement(build1(LABEL_EXPR, void_type_node, end_label));
tree for_binding = leave_scope();
append_statement(for_binding);
diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc
index 2568430..7466f24 100644
--- a/gcc/gcc/elna-tree.cc
+++ b/gcc/gcc/elna-tree.cc
@@ -31,11 +31,6 @@ namespace elna::gcc
return TREE_CODE(type) == INTEGER_TYPE && type != elna_char_type_node;
}
- bool is_numeric_type(tree type)
- {
- return is_integral_type(type) || type == elna_float_type_node;
- }
-
bool is_unique_type(tree type)
{
gcc_assert(TYPE_P(type));
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index b50eb75..d6804df 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 repeat_statement;
class for_statement;
class case_statement;
class traits_expression;
@@ -118,6 +119,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(repeat_statement *) = 0;
virtual void visit(for_statement *) = 0;
virtual void visit(defer_statement *) = 0;
virtual void visit(case_statement *) = 0;
@@ -146,7 +148,9 @@ namespace elna::boot
};
/**
- * Abstract visitor that doesn't visit any nodes by default.
+ * Abstract visitor that asserts on any node visitation. Child visitors
+ * override only the node types they care about, with the guarantee that
+ * unimplemented nodes are never silently ignored.
*/
class empty_visitor : public parser_visitor
{
@@ -166,6 +170,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(repeat_statement *) override;
[[noreturn]] void visit(for_statement *) override;
[[noreturn]] void visit(defer_statement *) override;
[[noreturn]] void visit(empty_statement *) override;
@@ -213,6 +218,7 @@ namespace elna::boot
void visit(if_statement *) override;
void visit(import_declaration *) override;
void visit(while_statement *statement) override;
+ void visit(repeat_statement *statement) override;
void visit(for_statement *statement) override;
void visit(defer_statement *statement) override;
void visit(empty_statement *) override;
@@ -824,28 +830,44 @@ namespace elna::boot
};
/**
- * for-statement.
+ * for-to-statement.
+ */
+ class repeat_statement : public statement
+ {
+ expression *m_condition;
+
+ public:
+ const std::vector<statement *> body;
+
+ repeat_statement(const source_position position, std::vector<statement *>&& body,
+ expression *condition);
+ ~repeat_statement() override;
+
+ void accept(parser_visitor *visitor) override;
+
+ expression& condition();
+ };
+
+ /**
+ * for-of-statement.
*/
class for_statement : public statement
{
- expression *m_initial_value;
- expression *m_final_value;
+ expression *m_range;
public:
const identifier control_variable;
+ identifier *const counter;
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);
+ expression *range, std::vector<statement *>&& body, identifier *const counter);
~for_statement() override;
void accept(parser_visitor *visitor) override;
- expression& initial_value();
- expression& final_value();
+ expression& range();
};
/**
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index d18b813..68fe6da 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -33,15 +33,25 @@ namespace elna::boot
* Error declaring or using a symbol (undeclared, redefinition,
* local export).
*/
- class symbol_error : public error
+ class declaration_error : public error
{
public:
- struct undeclared { std::string name; };
- struct local_export { std::string name; };
- struct redefinition { std::string name; std::optional<source_position> original; };
+ 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>;
- symbol_error(const source_position position, payload_type payload);
+ declaration_error(const source_position position, payload_type payload);
std::string what() const override;
std::optional<std::pair<std::string, source_position>> note() const override;
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index ea418b1..4e97205 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -73,6 +73,7 @@ namespace elna::boot
bool operator==(const std::nullptr_t&) const;
bool operator==(const type& other) const;
+ explicit operator bool() const;
bool empty() const;
@@ -523,4 +524,13 @@ namespace elna::boot
* \return Whether the type is a pointer type.
*/
bool is_any_pointer_type(const type& checked);
+
+ /**
+ * If \a range is an array or a slice gives its base type, otherwise
+ * returns an empty type.
+ *
+ * \param range The type to check.
+ * \return The base type, or an empty type.
+ */
+ type get_range_base_type(const type& range);
}
diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h
index 5fdc5cb..e500007 100644
--- a/include/elna/boot/type_check.h
+++ b/include/elna/boot/type_check.h
@@ -88,16 +88,23 @@ namespace elna::boot
};
/**
- * Base type of a record is not a record.
+ * Unexpected kind of a type at specific position.
*/
- class base_type_error : public error
+ class type_kind_error : public error
{
- type actual;
-
public:
- base_type_error(type actual, const source_position position);
+ enum class kind
+ {
+ record_base,
+ for_loop
+ };
+ type_kind_error(const source_position position, kind type_kind, const type& actual);
std::string what() const override;
+
+ private:
+ kind type_kind;
+ type actual;
};
/**
@@ -165,19 +172,6 @@ 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().
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index de95a7e..1ac829f 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -60,6 +60,10 @@ namespace elna::gcc
bool expect_trait_type_only(boot::traits_expression *trait);
void visit_statements(const std::vector<boot::statement *>& statements);
bool assert_constant(location_t expression_location);
+ tree declare_local_variable(const boot::identifier& name,
+ const boot::variable_info& info, tree initial_value);
+ std::pair<tree, tree> build_loop_head(tree control_variable_declaration, tree limit,
+ tree_code comparison, location_t check_location);
public:
generic_visitor(std::shared_ptr<symbol_table> symbol_table,
@@ -89,6 +93,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::repeat_statement *statement) override;
void visit(boot::for_statement *statement) override;
void visit(boot::while_statement *statement) override;
void visit(boot::defer_statement *statement) override;
diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h
index 1d48b00..ac97e41 100644
--- a/include/elna/gcc/elna-tree.h
+++ b/include/elna/gcc/elna-tree.h
@@ -36,7 +36,6 @@ namespace elna::gcc
using symbol_table = boot::symbol_map<tree, tree, NULL_TREE>;
bool is_integral_type(tree type);
- bool is_numeric_type(tree type);
bool is_unique_type(tree type);
bool is_void_type(tree type);
diff --git a/testsuite/runnable/for_by.elna b/testsuite/runnable/for_by.elna
deleted file mode 100644
index b219dc6..0000000
--- a/testsuite/runnable/for_by.elna
+++ /dev/null
@@ -1,9 +0,0 @@
-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_each_array.elna b/testsuite/runnable/for_each_array.elna
new file mode 100644
index 0000000..bf5733d
--- /dev/null
+++ b/testsuite/runnable/for_each_array.elna
@@ -0,0 +1,12 @@
+var
+ actual: [4]Word := [4]Word{}
+ input: [4]Word := [4]Word{ 2u, 4u, 6u, 8u }
+ i: Word := 1u
+
+begin
+ for element of input do
+ actual[i] := element^ * 2u;
+ i := i + 1u
+ end;
+ assert(actual = [4]Word{ 4u, 8u, 12u, 16u })
+end.
diff --git a/testsuite/runnable/for_each_slice.elna b/testsuite/runnable/for_each_slice.elna
new file mode 100644
index 0000000..efdf961
--- /dev/null
+++ b/testsuite/runnable/for_each_slice.elna
@@ -0,0 +1,15 @@
+var
+ actual: [4]Word := [4]Word{}
+ input: [4]Word := [4]Word{ 2u, 4u, 6u, 8u }
+ slice: []Word
+ i: Word := 1u
+
+begin
+ slice := input[1u to input.length];
+
+ for element of slice do
+ actual[i] := element^ * 2u;
+ i := i + 1u
+ end;
+ assert(actual = [4]Word{ 4u, 8u, 12u, 16u })
+end.
diff --git a/testsuite/runnable/for_loop.elna b/testsuite/runnable/for_loop.elna
deleted file mode 100644
index 02db679..0000000
--- a/testsuite/runnable/for_loop.elna
+++ /dev/null
@@ -1,9 +0,0 @@
-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/for_with.elna b/testsuite/runnable/for_with.elna
new file mode 100644
index 0000000..cfec5c0
--- /dev/null
+++ b/testsuite/runnable/for_with.elna
@@ -0,0 +1,9 @@
+var
+ actual: [3]Word := [3]Word{}
+
+begin
+ for element of [3]Word{ 1u, 2u, 3u } with i do
+ actual[i] := i * element^
+ end;
+ assert(actual = [3]Word{ 1u, 4u, 9u })
+end.
diff --git a/testsuite/runnable/repeat_loop.elna b/testsuite/runnable/repeat_loop.elna
new file mode 100644
index 0000000..5faaeab
--- /dev/null
+++ b/testsuite/runnable/repeat_loop.elna
@@ -0,0 +1,11 @@
+var
+ actual: [4]Word := [4]Word{}
+ i: Word := 0u
+
+begin
+ repeat
+ i := i + 1u;
+ actual[i] := i
+ until i = 4u;
+ assert(actual = [4]Word{ 1u, 2u, 3u, 4u })
+end.