aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-09-18 12:19:13 +0200
committerEugen Wissner <belka@caraus.de>2026-09-18 12:19:13 +0200
commitdc9d03915c4b169fec749081b45d56f722105813 (patch)
tree0a0719bc95f942b675fb52865d9c5bac9e105767
parentb575036fe804f14b78da74897a74b010cc94f730 (diff)
downloadelna-dc9d03915c4b169fec749081b45d56f722105813.tar.gz
Allow block-local variables
-rw-r--r--boot/ast.cc157
-rw-r--r--boot/name_analysis.cc86
-rw-r--r--boot/parser.yy67
-rw-r--r--boot/type_check.cc45
-rw-r--r--boot/validation.cc29
-rw-r--r--gcc/gcc/elna-generic.cc39
-rw-r--r--include/elna/boot/ast.h86
-rw-r--r--include/elna/boot/name_analysis.h10
-rw-r--r--include/elna/boot/type_check.h1
-rw-r--r--include/elna/boot/validation.h2
-rw-r--r--include/elna/gcc/elna-generic.h5
-rw-r--r--testsuite/runnable/block_local_variable.elna14
12 files changed, 270 insertions, 271 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index 4a596e5..b38e8d1 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -253,81 +253,66 @@ namespace elna::boot
statement->rvalue().accept(this);
}
- void walking_visitor::visit(if_statement *statement)
+ void walking_visitor::traverse_block(block& body)
{
- statement->branch().prerequisite().accept(this);
- for (auto *branch_statement : statement->branch().statements)
+ for (variable_declaration *const variable : body.variables)
{
- branch_statement->accept(this);
+ variable->accept(this);
+ }
+ for (statement *const body_statement : body.statements)
+ {
+ body_statement->accept(this);
}
+ }
+
+ void walking_visitor::visit(if_statement *statement)
+ {
+ statement->branch().prerequisite().accept(this);
+ traverse_block(statement->branch().body);
+
for (conditional_statements *branch : statement->branches)
{
branch->prerequisite().accept(this);
-
- for (auto *branch_statement : branch->statements)
- {
- branch_statement->accept(this);
- }
+ traverse_block(branch->body);
}
if (statement->alternative != nullptr)
{
- for (auto *branch_statement : *statement->alternative)
- {
- branch_statement->accept(this);
- }
+ traverse_block(*statement->alternative);
}
}
void walking_visitor::visit(while_statement *statement)
{
statement->branch().prerequisite().accept(this);
- for (auto *branch_statement : statement->branch().statements)
- {
- branch_statement->accept(this);
- }
+ traverse_block(statement->branch().body);
+
for (conditional_statements *branch : statement->branches)
{
branch->prerequisite().accept(this);
-
- for (auto *branch_statement : branch->statements)
- {
- branch_statement->accept(this);
- }
+ traverse_block(branch->body);
}
}
void walking_visitor::visit(repeat_statement *statement)
{
statement->condition().accept(this);
- for (auto *body_statement : statement->body)
- {
- body_statement->accept(this);
- }
+ traverse_block(statement->body);
}
void walking_visitor::visit(for_statement *statement)
{
statement->range().accept(this);
- for (auto *body_statement : statement->body)
- {
- body_statement->accept(this);
- }
+ traverse_block(statement->body);
}
void walking_visitor::visit(defer_statement *statement)
{
- for (auto *body_statement : statement->statements)
- {
- body_statement->accept(this);
- }
+ traverse_block(statement->body);
}
void walking_visitor::visit(block_statement *statement)
{
- for (auto *body_statement : statement->statements)
- {
- body_statement->accept(this);
- }
+ traverse_block(statement->body);
}
void walking_visitor::visit(break_statement *)
@@ -341,23 +326,17 @@ namespace elna::boot
void walking_visitor::visit(case_statement *statement)
{
statement->condition().accept(this);
- for (const switch_case& case_block : statement->cases)
+ for (switch_case& case_block : statement->cases)
{
for (expression *case_label : case_block.labels)
{
case_label->accept(this);
}
- for (auto *body_statement : case_block.statements)
- {
- body_statement->accept(this);
- }
+ traverse_block(case_block.body);
}
if (statement->alternative != nullptr)
{
- for (auto *body_statement : *statement->alternative)
- {
- body_statement->accept(this);
- }
+ traverse_block(*statement->alternative);
}
}
@@ -1091,21 +1070,12 @@ namespace elna::boot
return *m_underlying_type;
}
- procedure_body::procedure_body(std::vector<variable_declaration *>&& variables,
- std::vector<statement *>&& entry_point, expression *return_expression)
- : variables(std::move(variables)),
- statements(std::move(entry_point)), return_expression(return_expression)
+ block::block(std::vector<variable_declaration *>&& variables, std::vector<statement *>&& statements)
+ : variables(std::move(variables)), statements(std::move(statements))
{
}
- procedure_body::procedure_body(procedure_body&& that) noexcept
- : variables(std::move(const_cast<std::vector<variable_declaration *>&>(that.variables))),
- statements(std::move(const_cast<std::vector<statement *>&>(that.statements))),
- return_expression(that.return_expression)
- {
- }
-
- procedure_body::~procedure_body()
+ block::~block()
{
for (const statement *body_statement : this->statements)
{
@@ -1117,6 +1087,11 @@ namespace elna::boot
}
}
+ procedure_body::procedure_body(block&& body, expression *return_expression)
+ : block(std::move(body)), return_expression(return_expression)
+ {
+ }
+
void traverse_body(parser_visitor *visitor, const procedure_body& body)
{
for (variable_declaration *variable : body.variables)
@@ -1175,8 +1150,8 @@ namespace elna::boot
return this;
}
- defer_statement::defer_statement(const source_position position, std::vector<statement *>&& statements)
- : node(position), statements(std::move(statements))
+ defer_statement::defer_statement(const source_position position, block&& body)
+ : node(position), body(std::move(body))
{
}
@@ -1185,17 +1160,8 @@ namespace elna::boot
visitor->visit(this);
}
- defer_statement::~defer_statement()
- {
- for (const statement *body_statement : statements)
- {
- delete body_statement;
- }
- }
-
- block_statement::block_statement(const source_position position, identifier&& name,
- std::vector<statement *>&& statements)
- : node(position), name(std::move(name)), statements(std::move(statements))
+ block_statement::block_statement(const source_position position, identifier&& name, block&& body)
+ : node(position), name(std::move(name)), body(std::move(body))
{
}
@@ -1204,14 +1170,6 @@ namespace elna::boot
visitor->visit(this);
}
- block_statement::~block_statement()
- {
- for (const statement *body_statement : statements)
- {
- delete body_statement;
- }
- }
-
break_statement::break_statement(const source_position position, identifier&& label)
: node(position), label(std::move(label))
{
@@ -1618,8 +1576,8 @@ namespace elna::boot
return this;
}
- conditional_statements::conditional_statements(expression *prerequisite, std::vector<statement *>&& statements)
- : m_prerequisite(prerequisite), statements(std::move(statements))
+ conditional_statements::conditional_statements(expression *prerequisite, block&& body)
+ : m_prerequisite(prerequisite), body(std::move(body))
{
}
@@ -1631,14 +1589,10 @@ namespace elna::boot
conditional_statements::~conditional_statements()
{
delete m_prerequisite;
- for (auto *statement : statements)
- {
- delete statement;
- }
}
case_statement::case_statement(const source_position position,
- expression *condition, std::vector<switch_case>&& cases, std::vector<statement *> *alternative)
+ expression *condition, std::vector<switch_case>&& cases, block *alternative)
: node(position), m_condition(condition), cases(std::move(cases)), alternative(alternative)
{
}
@@ -1653,6 +1607,19 @@ namespace elna::boot
return *m_condition;
}
+ case_statement::~case_statement()
+ {
+ delete m_condition;
+ for (const switch_case& case_block : this->cases)
+ {
+ for (const expression *case_label : case_block.labels)
+ {
+ delete case_label;
+ }
+ }
+ delete this->alternative;
+ }
+
assign_statement::assign_statement(const source_position position, designator_expression *lvalue,
expression *rvalue)
: node(position), m_lvalue(lvalue), m_rvalue(rvalue)
@@ -1705,8 +1672,7 @@ namespace elna::boot
}
if_statement::if_statement(const source_position position, conditional_statements *branch,
- std::vector<conditional_statements *>&& branches,
- std::vector<statement *> *alternative)
+ std::vector<conditional_statements *>&& branches, block *alternative)
: node(position), m_branch(branch), branches(std::move(branches)), alternative(alternative)
{
}
@@ -1766,7 +1732,7 @@ namespace elna::boot
}
}
- repeat_statement::repeat_statement(const source_position position, std::vector<statement *>&& body,
+ repeat_statement::repeat_statement(const source_position position, block&& body,
expression *condition)
: node(position), m_condition(condition), body(std::move(body))
{
@@ -1775,10 +1741,6 @@ namespace elna::boot
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)
@@ -1792,7 +1754,7 @@ namespace elna::boot
}
for_statement::for_statement(const source_position position, identifier&& control_variable,
- expression *range, std::vector<statement *>&& body, identifier *const counter = nullptr)
+ expression *range, block&& body, identifier *const counter = nullptr)
: node(position), m_range(range), control_variable(std::move(control_variable)),
counter(counter), body(std::move(body))
{
@@ -1802,11 +1764,6 @@ namespace elna::boot
{
delete this->m_range;
delete this->counter;
-
- for (const statement *body_statement : this->body)
- {
- delete body_statement;
- }
}
void for_statement::accept(parser_visitor *visitor)
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 9ba030c..18aaca2 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -733,65 +733,63 @@ namespace elna::boot
statement->rvalue().accept(this);
}
- void resolving_visitor::visit(if_statement *statement)
+ void resolving_visitor::traverse_block(block& body)
{
- statement->branch().prerequisite().accept(this);
- for (auto *branch_statement : statement->branch().statements)
+ for (variable_declaration *const variable : body.variables)
+ {
+ variable->accept(this);
+ }
+ for (statement *const body_statement : body.statements)
{
- branch_statement->accept(this);
+ body_statement->accept(this);
}
+ }
+
+ void resolving_visitor::visit_block(block& body)
+ {
+ body.symbols = this->bag.enter();
+ traverse_block(body);
+ this->bag.leave();
+ }
+
+ void resolving_visitor::visit(if_statement *statement)
+ {
+ statement->branch().prerequisite().accept(this);
+ visit_block(statement->branch().body);
+
for (conditional_statements *branch : statement->branches)
{
branch->prerequisite().accept(this);
-
- for (auto *branch_statement : branch->statements)
- {
- branch_statement->accept(this);
- }
+ visit_block(branch->body);
}
if (statement->alternative != nullptr)
{
- for (auto *branch_statement : *statement->alternative)
- {
- branch_statement->accept(this);
- }
+ visit_block(*statement->alternative);
}
}
void resolving_visitor::visit(while_statement *statement)
{
statement->branch().prerequisite().accept(this);
- for (auto *branch_statement : statement->branch().statements)
- {
- branch_statement->accept(this);
- }
+ visit_block(statement->branch().body);
+
for (conditional_statements *branch : statement->branches)
{
branch->prerequisite().accept(this);
-
- for (auto *branch_statement : branch->statements)
- {
- branch_statement->accept(this);
- }
+ visit_block(branch->body);
}
}
void resolving_visitor::visit(repeat_statement *statement)
{
statement->condition().accept(this);
- for (auto *body_statement : statement->body)
- {
- body_statement->accept(this);
- }
+ visit_block(statement->body);
}
void resolving_visitor::visit(defer_statement *statement)
{
++this->defer_depth;
- for (auto *body_statement : statement->statements)
- {
- body_statement->accept(this);
- }
+ visit_block(statement->body);
--this->defer_depth;
}
@@ -799,7 +797,7 @@ namespace elna::boot
{
const std::string& label_name = statement->name.name();
- this->bag.enter();
+ statement->body.symbols = this->bag.enter();
auto label_symbol = std::make_shared<label_info>(this->defer_depth);
label_symbol->position.emplace(statement->name.position());
@@ -815,10 +813,7 @@ namespace elna::boot
add_error<symbol_declaration_error>(statement->name.position(),
label_name, original_definition);
}
- for (auto *body_statement : statement->statements)
- {
- body_statement->accept(this);
- }
+ traverse_block(statement->body);
this->bag.leave();
}
@@ -855,23 +850,17 @@ namespace elna::boot
void resolving_visitor::visit(case_statement *statement)
{
statement->condition().accept(this);
- for (const switch_case& case_block : statement->cases)
+ for (switch_case& case_block : statement->cases)
{
for (expression *case_label : case_block.labels)
{
case_label->accept(this);
}
- for (auto *body_statement : case_block.statements)
- {
- body_statement->accept(this);
- }
+ visit_block(case_block.body);
}
if (statement->alternative != nullptr)
{
- for (auto *body_statement : *statement->alternative)
- {
- body_statement->accept(this);
- }
+ visit_block(*statement->alternative);
}
}
@@ -1552,6 +1541,7 @@ namespace elna::boot
{
const std::shared_ptr<procedure_info> info = this->bag.lookup("")->is_procedure();
+ unit->entry_point->symbols = info->scope;
this->bag.enter(info->scope);
traverse_body(this, unit->entry_point.value());
this->bag.leave();
@@ -1598,6 +1588,7 @@ namespace elna::boot
const std::shared_ptr<procedure_info> info =
this->bag.lookup(declaration->identifier.name())->is_procedure();
+ declaration->body->symbols = info->scope;
this->bag.enter(info->scope);
traverse_body(this, declaration->body.value());
this->bag.leave();
@@ -1612,7 +1603,7 @@ namespace elna::boot
const type control_variable_pointer_type = type(std::make_shared<pointer_type>(control_variable_base_type));
const type control_variable_const_type = type(std::make_shared<constant_type>(control_variable_pointer_type));
- statement->symbols = this->bag.enter();
+ statement->body.symbols = this->bag.enter();
register_variable(statement->control_variable.name(), control_variable_const_type,
statement->control_variable.position());
@@ -1621,10 +1612,7 @@ namespace elna::boot
register_variable(statement->counter->name(), lookup_primitive_type("Word"),
statement->counter->position());
}
- for (auto *body_statement : statement->body)
- {
- body_statement->accept(this);
- }
+ traverse_block(statement->body);
this->bag.leave();
}
}
diff --git a/boot/parser.yy b/boot/parser.yy
index eaf1a7c..01dfa80 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -135,7 +135,7 @@ along with GCC; see the file COPYING3. If not see
%type <elna::boot::switch_case> switch_case;
%type <std::vector<elna::boot::switch_case>> switch_cases;
%type <elna::boot::variable_declaration *> variable_declaration;
-%type <std::vector<elna::boot::variable_declaration *>> variable_declarations;
+%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::expression *> expression operand simple_expression procedure_return unary_expression;
@@ -145,6 +145,7 @@ along with GCC; see the file COPYING3. If not see
%type <std::unique_ptr<elna::boot::procedure_call>> call_expression;
%type <elna::boot::statement *> statement;
%type <std::vector<elna::boot::statement *>> statements statement_part;
+%type <elna::boot::block> block;
%type <elna::boot::procedure_declaration *> procedure_declaration;
%type <std::unique_ptr<elna::boot::procedure_type_expression>> procedure_heading;
%type <elna::boot::procedure_type_expression::return_t> return_declaration;
@@ -157,7 +158,7 @@ along with GCC; see the file COPYING3. If not see
%type <std::unique_ptr<elna::boot::field_initializer>> field_initializer;
%type <std::vector<elna::boot::field_initializer>> field_initializers;
%type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements;
-%type <std::vector<elna::boot::statement *> *> else_statements;
+%type <elna::boot::block *> else_statements;
%type <std::unique_ptr<elna::boot::identifier>> identifier with_counter;
%type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition attributed_identifier;
%type <std::vector<elna::boot::identifier_definition>> identifier_definitions;
@@ -188,9 +189,9 @@ program:
}
module_declaration:
"type" type_declarations { $$ = $2; }
- | "var" variable_declarations
+ | variable_part
{
- std::vector<boot::variable_declaration *> variables = $2;
+ std::vector<boot::variable_declaration *> variables = $1;
$$.insert($$.end(), variables.begin(), variables.end());
}
@@ -215,12 +216,19 @@ module_declarations:
$$.insert($$.end(), appendix.begin(), appendix.end());
}
procedure_body:
- "var" variable_declarations statement_part procedure_return
- { $$ = std::make_unique<boot::procedure_body>($2, $3, $4); }
- | statement_part procedure_return
- {
- $$ = std::make_unique<boot::procedure_body>(std::vector<boot::variable_declaration *>(), $1, $2);
- }
+ block procedure_return
+ { $$ = std::make_unique<boot::procedure_body>(std::move($1), $2); }
+
+variable_part:
+ "var" variable_declarations { $$ = $2; }
+
+block:
+ variable_part statement_part
+ { $$ = boot::block(std::move($1), std::move($2)); }
+ | "begin" statements
+ { $$ = boot::block(std::vector<boot::variable_declaration *>(), std::move($2)); }
+ | statements
+ { $$ = boot::block(std::vector<boot::variable_declaration *>(), std::move($1)); }
statement_part:
%empty {}
@@ -284,20 +292,20 @@ with_counter:
"with" identifier { $$ = $2; }
| %empty { $$ = nullptr; }
elsif_do_statements:
- "elsif" expression "do" statements elsif_do_statements
+ "elsif" expression "do" block elsif_do_statements
{
- boot::conditional_statements *branch = new boot::conditional_statements($2, $4);
+ boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4));
$$ = $5;
$$.emplace($$.begin(), branch);
}
| %empty {}
else_statements:
- "else" statements { $$ = new std::vector<boot::statement *>($2); }
+ "else" block { $$ = new boot::block(std::move($2)); }
| %empty { $$ = nullptr; }
elsif_then_statements:
- "elsif" expression "then" statements elsif_then_statements
+ "elsif" expression "then" block elsif_then_statements
{
- boot::conditional_statements *branch = new boot::conditional_statements($2, $4);
+ boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4));
$$ = $5;
$$.emplace($$.begin(), branch);
}
@@ -563,36 +571,37 @@ name_reference:
statement:
designator_expression ":=" expression
{ $$ = new boot::assign_statement(boot::make_position(@$), $1, $3); }
- | "while" expression "do" statements elsif_do_statements "end"
+ | "while" expression "do" block elsif_do_statements "end"
{
- boot::conditional_statements *body = new boot::conditional_statements($2, $4);
+ boot::conditional_statements *body = new boot::conditional_statements($2, std::move($4));
$$ = new boot::while_statement(boot::make_position(@$), body, $5);
}
- | "repeat" statements "until" expression
+ | "repeat" block "until" expression
{
- $$ = new boot::repeat_statement(boot::make_position(@$), $2, $4);
+ $$ = new boot::repeat_statement(boot::make_position(@$), std::move($2), $4);
}
- | "for" identifier "of" expression with_counter "do" statements "end"
+ | "for" identifier "of" expression with_counter "do" block "end"
{
- $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, $7, $5.release());
+ $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4,
+ std::move($7), $5.release());
}
- | "if" expression "then" statements elsif_then_statements else_statements "end"
+ | "if" expression "then" block elsif_then_statements else_statements "end"
{
- boot::conditional_statements *then = new boot::conditional_statements($2, $4);
+ boot::conditional_statements *then = new boot::conditional_statements($2, std::move($4));
$$ = new boot::if_statement(boot::make_position(@$), then, $5, $6);
}
| call_expression { $$ = $1.release(); }
- | "defer" statements "end"
- { $$ = new boot::defer_statement(boot::make_position(@$), $2); }
- | "block" identifier statements "end"
- { $$ = new boot::block_statement(boot::make_position(@$), std::move(*$2), $3); }
+ | "defer" block "end"
+ { $$ = new boot::defer_statement(boot::make_position(@$), std::move($2)); }
+ | "block" identifier block "end"
+ { $$ = new boot::block_statement(boot::make_position(@$), std::move(*$2), std::move($3)); }
| "break" identifier
{ $$ = new boot::break_statement(boot::make_position(@$), std::move(*$2)); }
| "case" expression "of" switch_cases else_statements "end"
{ $$ = new boot::case_statement(boot::make_position(@$), $2, $4, $5); }
| %empty { $$ = new boot::empty_statement(boot::make_position(@$)); }
-switch_case: case_labels ":" statements
- { $$ = { .labels = $1, .statements = $3 }; }
+switch_case: case_labels ":" block
+ { $$ = { .labels = $1, .body = std::move($3) }; }
switch_cases:
switch_case "|" switch_cases
{
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 2b82bec..5d5bc59 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -876,66 +876,47 @@ namespace elna::boot
add_error<type_requirement_error>(statement->range().position(),
statement->range().type_decoration, type_requirement_error::kind::for_range);
}
- this->bag.enter(statement->symbols);
- for (auto *body_statement : statement->body)
- {
- body_statement->accept(this);
- }
+ traverse_block(statement->body);
+ }
+
+ void type_analysis_visitor::traverse_block(block& body)
+ {
+ this->bag.enter(body.symbols);
+ walking_visitor::traverse_block(body);
this->bag.leave();
}
void type_analysis_visitor::visit(repeat_statement *statement)
{
visit_and_validate_condition(statement->condition());
-
- for (auto *body_statement : statement->body)
- {
- body_statement->accept(this);
- }
+ traverse_block(statement->body);
}
void type_analysis_visitor::visit(while_statement *statement)
{
visit_and_validate_condition(statement->branch().prerequisite());
+ traverse_block(statement->branch().body);
- for (auto *branch_statement : statement->branch().statements)
- {
- branch_statement->accept(this);
- }
for (conditional_statements *branch : statement->branches)
{
visit_and_validate_condition(branch->prerequisite());
-
- for (auto *branch_statement : branch->statements)
- {
- branch_statement->accept(this);
- }
+ traverse_block(branch->body);
}
}
void type_analysis_visitor::visit(if_statement *statement)
{
visit_and_validate_condition(statement->branch().prerequisite());
+ traverse_block(statement->branch().body);
- for (auto *branch_statement : statement->branch().statements)
- {
- branch_statement->accept(this);
- }
for (conditional_statements *branch : statement->branches)
{
visit_and_validate_condition(branch->prerequisite());
-
- for (auto *branch_statement : branch->statements)
- {
- branch_statement->accept(this);
- }
+ traverse_block(branch->body);
}
if (statement->alternative != nullptr)
{
- for (auto *branch_statement : *statement->alternative)
- {
- branch_statement->accept(this);
- }
+ traverse_block(*statement->alternative);
}
}
diff --git a/boot/validation.cc b/boot/validation.cc
index e628cd8..facef04 100644
--- a/boot/validation.cc
+++ b/boot/validation.cc
@@ -49,6 +49,13 @@ namespace elna::boot
}
}
+ void validation_visitor::visit_block(const block& body)
+ {
+ this->bag.enter(body.symbols);
+ visit_statements(body.statements);
+ this->bag.leave();
+ }
+
void validation_visitor::visit(unit *unit)
{
for (declaration *unit_declaration : unit->declarations)
@@ -88,11 +95,11 @@ namespace elna::boot
{
for (const switch_case& case_block : statement->cases)
{
- visit_statements(case_block.statements);
+ visit_block(case_block.body);
}
if (statement->alternative != nullptr)
{
- visit_statements(*statement->alternative);
+ visit_block(*statement->alternative);
}
std::unordered_map<constant_value, source_position, constant_value_hash> seen;
@@ -122,44 +129,44 @@ namespace elna::boot
void validation_visitor::visit(if_statement *statement)
{
- visit_statements(statement->branch().statements);
+ visit_block(statement->branch().body);
for (const conditional_statements *branch : statement->branches)
{
- visit_statements(branch->statements);
+ visit_block(branch->body);
}
if (statement->alternative != nullptr)
{
- visit_statements(*statement->alternative);
+ visit_block(*statement->alternative);
}
}
void validation_visitor::visit(while_statement *statement)
{
- visit_statements(statement->branch().statements);
+ visit_block(statement->branch().body);
for (const conditional_statements *branch : statement->branches)
{
- visit_statements(branch->statements);
+ visit_block(branch->body);
}
}
void validation_visitor::visit(repeat_statement *statement)
{
- visit_statements(statement->body);
+ visit_block(statement->body);
}
void validation_visitor::visit(for_statement *statement)
{
- visit_statements(statement->body);
+ visit_block(statement->body);
}
void validation_visitor::visit(defer_statement *statement)
{
- visit_statements(statement->statements);
+ visit_block(statement->body);
}
void validation_visitor::visit(block_statement *statement)
{
- visit_statements(statement->statements);
+ visit_block(statement->body);
}
void validation_visitor::visit(break_statement *)
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index 60ed82b..f4cb3c3 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -980,7 +980,7 @@ namespace elna::gcc
if (statement->alternative != nullptr)
{
enter_scope();
- visit_statements(*statement->alternative);
+ visit_block(*statement->alternative);
result = leave_scope();
}
@@ -1001,7 +1001,7 @@ namespace elna::gcc
tree condition = this->current_expression;
enter_scope();
- visit_statements(branch.statements);
+ visit_block(branch.body);
if (goto_append != NULL_TREE)
{
append_statement(goto_append);
@@ -1032,7 +1032,7 @@ namespace elna::gcc
append_statement(build1(LABEL_EXPR, void_type_node, loop_label));
enter_scope();
- visit_statements(statement->body);
+ visit_block(statement->body);
tree repeat_binding = leave_scope();
append_statement(repeat_binding);
@@ -1077,7 +1077,7 @@ namespace elna::gcc
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());
+ auto control_variable_info = statement->body.symbols->lookup(statement->control_variable.name());
const boot::variable_info unqualified_info(
boot::resolve_underlying_type(control_variable_info->is_variable()->symbol),
control_variable_info->is_variable()->is_extern);
@@ -1087,7 +1087,7 @@ namespace elna::gcc
tree counter_declaration{ NULL_TREE };
if (statement->counter != nullptr)
{
- auto counter_info = statement->symbols->lookup(statement->counter->name());
+ auto counter_info = statement->body.symbols->lookup(statement->counter->name());
counter_declaration = declare_local_variable(*statement->counter,
*counter_info->is_variable(), elna_word_one_node);
}
@@ -1097,7 +1097,7 @@ namespace elna::gcc
// The body gets its own scope, so that defer statements in it are run on
// every iteration and not once for the whole loop.
enter_scope();
- visit_statements(statement->body);
+ visit_block(statement->body);
tree body_binding = leave_scope();
append_statement(body_binding);
@@ -1158,10 +1158,21 @@ namespace elna::gcc
}
}
+ void generic_visitor::visit_block(boot::block& body)
+ {
+ this->bag.enter(body.symbols);
+ for (boot::variable_declaration *const variable : body.variables)
+ {
+ variable->accept(this);
+ }
+ visit_statements(body.statements);
+ this->bag.leave();
+ }
+
void generic_visitor::visit(boot::defer_statement *statement)
{
enter_scope();
- visit_statements(statement->statements);
+ visit_block(statement->body);
defer(leave_scope());
}
@@ -1172,7 +1183,7 @@ namespace elna::gcc
enter_scope();
this->symbols->enter(statement->name.name(), end_label);
- visit_statements(statement->statements);
+ visit_block(statement->body);
append_statement(leave_scope());
// The label sits outside the binding, so that a break leaving the block
@@ -1216,7 +1227,7 @@ namespace elna::gcc
tree end_label_declaration = create_artificial_label(get_location(&statement->position()));
tree switch_statements = alloc_stmt_list();
- for (const boot::switch_case& case_block : statement->cases)
+ for (boot::switch_case& case_block : statement->cases)
{
for (boot::expression *const case_label : case_block.labels)
{
@@ -1230,7 +1241,7 @@ namespace elna::gcc
append_to_statement_list(case_expression, &switch_statements);
}
enter_scope();
- visit_statements(case_block.statements);
+ visit_block(case_block.body);
append_to_statement_list(leave_scope(), &switch_statements);
tree goto_end = build1(GOTO_EXPR, void_type_node, end_label_declaration);
@@ -1245,7 +1256,7 @@ namespace elna::gcc
append_to_statement_list(case_expression, &switch_statements);
enter_scope();
- visit_statements(*statement->alternative);
+ visit_block(*statement->alternative);
append_to_statement_list(leave_scope(), &switch_statements);
TREE_USED(end_label_declaration) = 1;
@@ -1267,10 +1278,10 @@ namespace elna::gcc
if (statement->alternative != nullptr)
{
enter_scope();
- visit_statements(*statement->alternative);
+ visit_block(*statement->alternative);
result = leave_scope();
}
- for (const boot::switch_case& case_block : statement->cases | std::views::reverse)
+ for (boot::switch_case& case_block : statement->cases | std::views::reverse)
{
tree case_condition = boolean_false_node;
for (boot::expression *const case_label : case_block.labels)
@@ -1285,7 +1296,7 @@ namespace elna::gcc
}
enter_scope();
- visit_statements(case_block.statements);
+ visit_block(case_block.body);
tree then_body = leave_scope();
result = build3(COND_EXPR, void_type_node, case_condition, then_body, result);
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index 4eea412..52461e6 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -111,6 +111,7 @@ namespace elna::boot
template<typename T>
concept literal_type = requires { literal_type_id<T>::value; };
+ class statement;
class variable_declaration;
class procedure_declaration;
class type_declaration;
@@ -153,6 +154,27 @@ namespace elna::boot
class empty_statement;
/**
+ * Variable declarations and the statements they are visible in.
+ */
+ struct block
+ {
+ std::vector<variable_declaration *> variables;
+ std::vector<statement *> statements;
+ /// Scope containing the variables, entered by the name analysis.
+ std::shared_ptr<symbol_table> symbols;
+
+ block() = default;
+ block(std::vector<variable_declaration *>&& variables, std::vector<statement *>&& statements);
+ block(const block&) = delete;
+ block(block&& that) noexcept = default;
+
+ block& operator=(const block&) = delete;
+ block& operator=(block&& that) noexcept = default;
+
+ ~block();
+ };
+
+ /**
* Interface for AST visitors.
*/
struct parser_visitor
@@ -304,6 +326,8 @@ namespace elna::boot
protected:
/// Passes needing the entry point's scope override this.
virtual void visit_entry_point(unit *unit);
+ /// Passes needing the scope of a block override this.
+ virtual void traverse_block(block& body);
};
/**
@@ -659,21 +683,14 @@ namespace elna::boot
procedure_type_expression *is_procedure() override;
};
- struct procedure_body
+ struct procedure_body : block
{
- const std::vector<variable_declaration *> variables;
- const std::vector<statement *> statements;
- expression *const return_expression{ nullptr };
+ expression *return_expression{ nullptr };
- procedure_body(std::vector<variable_declaration *>&& variables,
- std::vector<statement *>&& entry_point,
- expression *return_expression = nullptr);
- procedure_body(const procedure_body&) = delete;
- procedure_body(procedure_body&& that) noexcept;
+ procedure_body(block&& body, expression *return_expression = nullptr);
+ procedure_body(procedure_body&& that) noexcept = default;
- procedure_body& operator=(const procedure_body&) = delete;
-
- virtual ~procedure_body();
+ virtual ~procedure_body() = default;
};
void traverse_body(parser_visitor *visitor, const procedure_body& body);
@@ -687,7 +704,7 @@ namespace elna::boot
public:
const identifier_definition identifier;
- const std::optional<procedure_body> body;
+ std::optional<procedure_body> body;
/**
* Type parameters bound by this declaration. Empty for a plain
@@ -771,9 +788,9 @@ namespace elna::boot
expression *m_prerequisite;
public:
- const std::vector<statement *> statements;
+ block body;
- conditional_statements(expression *prerequisite, std::vector<statement *>&& statements);
+ conditional_statements(expression *prerequisite, block&& body);
expression& prerequisite();
@@ -783,7 +800,7 @@ namespace elna::boot
struct switch_case
{
std::vector<expression *> labels;
- std::vector<statement *> statements;
+ block body;
};
class case_statement : public statement
@@ -791,13 +808,15 @@ namespace elna::boot
expression *m_condition;
public:
- const std::vector<switch_case> cases;
- const std::vector<statement *> *alternative;
+ std::vector<switch_case> cases;
+ block *alternative;
case_statement(const source_position position, expression *condition,
- std::vector<switch_case>&& cases, std::vector<statement *> *alternative = nullptr);
+ std::vector<switch_case>&& cases, block *alternative = nullptr);
void accept(parser_visitor *visitor) override;
expression& condition();
+
+ ~case_statement() override;
};
class designator_expression : public expression
@@ -965,11 +984,11 @@ namespace elna::boot
public:
const std::vector<conditional_statements *> branches;
- const std::vector<statement *> *alternative;
+ block *alternative;
if_statement(const source_position position, conditional_statements *branch,
std::vector<conditional_statements *>&& branches,
- std::vector<statement *> *alternative = nullptr);
+ block *alternative = nullptr);
void accept(parser_visitor *visitor) override;
conditional_statements& branch();
@@ -1016,9 +1035,9 @@ namespace elna::boot
expression *m_condition;
public:
- const std::vector<statement *> body;
+ block body;
- repeat_statement(const source_position position, std::vector<statement *>&& body,
+ repeat_statement(const source_position position, block&& body,
expression *condition);
~repeat_statement() override;
@@ -1037,11 +1056,11 @@ namespace elna::boot
public:
const identifier control_variable;
identifier *const counter;
- const std::vector<statement *> body;
- std::shared_ptr<symbol_table> symbols;
+ /// The control variable and the counter live in the body's scope.
+ block body;
for_statement(const source_position position, identifier&& control_variable,
- expression *range, std::vector<statement *>&& body, identifier *const counter);
+ expression *range, block&& body, identifier *const counter);
~for_statement() override;
void accept(parser_visitor *visitor) override;
@@ -1057,7 +1076,7 @@ namespace elna::boot
public:
const std::vector<import_declaration *> imports;
const std::vector<declaration *> declarations;
- const std::optional<procedure_body> entry_point;
+ std::optional<procedure_body> entry_point;
const std::vector<identifier> parameters;
const std::optional<source_position> entry_position;
@@ -1104,12 +1123,10 @@ namespace elna::boot
class defer_statement : public statement
{
public:
- const std::vector<statement *> statements;
+ block body;
- defer_statement(const source_position position, std::vector<statement *>&& statements);
+ defer_statement(const source_position position, block&& body);
void accept(parser_visitor *visitor) override;
-
- ~defer_statement() override;
};
/**
@@ -1119,13 +1136,10 @@ namespace elna::boot
{
public:
const identifier name;
- const std::vector<statement *> statements;
+ block body;
- block_statement(const source_position position, identifier&& name,
- std::vector<statement *>&& statements);
+ block_statement(const source_position position, identifier&& name, block&& body);
void accept(parser_visitor *visitor) override;
-
- ~block_statement() override;
};
/**
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index 67cd713..004aeae 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -298,6 +298,16 @@ namespace elna::boot
std::optional<type> lookup_pointer_like_field(const std::string& field_name,
const type& element_type);
type lookup_field(const type& composite_type, const std::string& field_name);
+ /*
+ * Visits the declarations and the statements of a block in the scope
+ * the caller has opened for it.
+ */
+ void traverse_block(block& body);
+ /*
+ * Opens the scope of a block, remembers it on the block for the later
+ * passes and visits the block in it.
+ */
+ void visit_block(block& body);
public:
void visit(array_type_expression *expression) override;
diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h
index 582b887..94f8961 100644
--- a/include/elna/boot/type_check.h
+++ b/include/elna/boot/type_check.h
@@ -300,6 +300,7 @@ namespace elna::boot
protected:
void visit_entry_point(unit *unit) override;
+ void traverse_block(block& body) override;
public:
explicit type_analysis_visitor(symbol_bag bag, const target_info& target);
diff --git a/include/elna/boot/validation.h b/include/elna/boot/validation.h
index 87e0b18..d3a22d1 100644
--- a/include/elna/boot/validation.h
+++ b/include/elna/boot/validation.h
@@ -49,6 +49,8 @@ namespace elna::boot
evaluator constant_evaluator;
void visit_statements(const std::vector<statement *>& statements);
+ /// Visits the statements of a block in its own scope.
+ void visit_block(const block& body);
public:
validation_visitor(symbol_bag& bag, const target_info& target);
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index b05a8f9..54f320e 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -67,6 +67,11 @@ namespace elna::gcc
void build_volatile_store_builtin(location_t call_location, boot::procedure_call *call);
void visit_statements(const std::vector<boot::statement *>& statements);
+ /*
+ * Declares the variables of a block and generates its statements. The
+ * caller opens the binding level the variables are declared in.
+ */
+ void visit_block(boot::block& body);
void assert_constant();
tree declare_local_variable(const boot::identifier& name,
const boot::variable_info& info, tree initial_value);
diff --git a/testsuite/runnable/block_local_variable.elna b/testsuite/runnable/block_local_variable.elna
new file mode 100644
index 0000000..6287eec
--- /dev/null
+++ b/testsuite/runnable/block_local_variable.elna
@@ -0,0 +1,14 @@
+program()
+var
+ total: Int := 0
+begin
+ if total = 0 then
+ var
+ inner: Int := 7
+ begin
+ total := total + inner
+ end;
+ assert(total = 7)
+return 0u8
+
+end.