From dc9d03915c4b169fec749081b45d56f722105813 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 18 Sep 2026 12:19:13 +0200 Subject: Allow block-local variables --- include/elna/boot/ast.h | 86 +++++++++++++++++++++++---------------- include/elna/boot/name_analysis.h | 10 +++++ include/elna/boot/type_check.h | 1 + include/elna/boot/validation.h | 2 + include/elna/gcc/elna-generic.h | 5 +++ 5 files changed, 68 insertions(+), 36 deletions(-) (limited to 'include') 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 concept literal_type = requires { literal_type_id::value; }; + class statement; class variable_declaration; class procedure_declaration; class type_declaration; @@ -152,6 +153,27 @@ namespace elna::boot class break_statement; class empty_statement; + /** + * Variable declarations and the statements they are visible in. + */ + struct block + { + std::vector variables; + std::vector statements; + /// Scope containing the variables, entered by the name analysis. + std::shared_ptr symbols; + + block() = default; + block(std::vector&& variables, std::vector&& 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. */ @@ -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 variables; - const std::vector statements; - expression *const return_expression{ nullptr }; + expression *return_expression{ nullptr }; - procedure_body(std::vector&& variables, - std::vector&& 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 body; + std::optional 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 statements; + block body; - conditional_statements(expression *prerequisite, std::vector&& statements); + conditional_statements(expression *prerequisite, block&& body); expression& prerequisite(); @@ -783,7 +800,7 @@ namespace elna::boot struct switch_case { std::vector labels; - std::vector statements; + block body; }; class case_statement : public statement @@ -791,13 +808,15 @@ namespace elna::boot expression *m_condition; public: - const std::vector cases; - const std::vector *alternative; + std::vector cases; + block *alternative; case_statement(const source_position position, expression *condition, - std::vector&& cases, std::vector *alternative = nullptr); + std::vector&& 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 branches; - const std::vector *alternative; + block *alternative; if_statement(const source_position position, conditional_statements *branch, std::vector&& branches, - std::vector *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 body; + block body; - repeat_statement(const source_position position, std::vector&& 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 body; - std::shared_ptr 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&& 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 imports; const std::vector declarations; - const std::optional entry_point; + std::optional entry_point; const std::vector parameters; const std::optional entry_position; @@ -1104,12 +1123,10 @@ namespace elna::boot class defer_statement : public statement { public: - const std::vector statements; + block body; - defer_statement(const source_position position, std::vector&& 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 statements; + block body; - block_statement(const source_position position, identifier&& name, - std::vector&& 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 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& 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& 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); -- cgit v1.2.3