aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-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
6 files changed, 162 insertions, 146 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)