diff options
| -rw-r--r-- | boot/ast.cc | 41 | ||||
| -rw-r--r-- | boot/evaluator.cc | 4 | ||||
| -rw-r--r-- | boot/materialization.cc | 17 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 69 | ||||
| -rw-r--r-- | boot/parser.yy | 56 | ||||
| -rw-r--r-- | boot/symbol.cc | 15 | ||||
| -rw-r--r-- | boot/validation.cc | 4 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 12 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 24 | ||||
| -rw-r--r-- | include/elna/boot/materialization.h | 4 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 5 | ||||
| -rw-r--r-- | include/elna/boot/symbol.h | 25 | ||||
| -rw-r--r-- | include/elna/boot/validation.h | 1 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 1 | ||||
| -rw-r--r-- | testsuite/runnable/const_section.elna | 16 | ||||
| -rw-r--r-- | tools/tree-sitter/grammar.js | 19 |
16 files changed, 308 insertions, 5 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index edc10a7..15e4928 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -72,6 +72,11 @@ namespace elna::boot __builtin_unreachable(); } + void empty_visitor::visit(constant_declaration *) + { + __builtin_unreachable(); + } + void empty_visitor::visit(procedure_declaration *) { __builtin_unreachable(); @@ -357,6 +362,11 @@ namespace elna::boot } } + void walking_visitor::visit(constant_declaration *declaration) + { + declaration->initializer->accept(this); + } + void walking_visitor::visit(array_type_expression *expression) { expression->base().accept(this); @@ -1007,6 +1017,31 @@ namespace elna::boot return *m_underlying_type; } + constant_declaration::constant_declaration(const source_position position, + identifier_definition identifier, expression *const initializer) + : node(position), identifier(std::move(identifier)), initializer(initializer) + { + } + + constant_declaration::~constant_declaration() + { + delete this->initializer; + } + + void constant_declaration::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + block::block(std::vector<constant_declaration *>&& constants, + std::vector<variable_declaration *>&& variables, + std::vector<statement *>&& statements) + : constants(std::move(constants)), + variables(std::move(variables)), + statements(std::move(statements)) + { + } + block::block(std::vector<variable_declaration *>&& variables, std::vector<statement *>&& statements) : variables(std::move(variables)), statements(std::move(statements)) { @@ -1014,6 +1049,7 @@ namespace elna::boot block& block::operator=(block&& that) noexcept { + std::swap(this->constants, that.constants); std::swap(this->variables, that.variables); std::swap(this->statements, that.statements); std::swap(this->symbols, that.symbols); @@ -1031,6 +1067,10 @@ namespace elna::boot { delete variable; } + for (const constant_declaration *constant : this->constants) + { + delete constant; + } } procedure_body::procedure_body(block&& body, expression *return_expression) @@ -1059,6 +1099,7 @@ namespace elna::boot void traverse_block(parser_visitor *visitor, const block& body) { + visit_all(visitor, body.constants); visit_all(visitor, body.variables); visit_all(visitor, body.statements); } diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 78eb4cd..a96d8ab 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -295,6 +295,10 @@ namespace elna::boot { return std::nullopt; } + if (const std::shared_ptr<constant_info> constant = symbol->is_constant()) + { + return constant->value; + } const std::shared_ptr<variable_info> variable = symbol->is_variable(); if (variable == nullptr diff --git a/boot/materialization.cc b/boot/materialization.cc index c7296f9..fd2558b 100644 --- a/boot/materialization.cc +++ b/boot/materialization.cc @@ -39,6 +39,8 @@ namespace elna::boot return "Local symbols cannot be extern"; case variable_generic: return "Variables cannot have generic parameters"; + case constant_generic: + return "Constants cannot have generic parameters"; } __builtin_unreachable(); } @@ -91,6 +93,21 @@ namespace elna::boot } } + void materialization_visitor::visit(constant_declaration *declaration) + { + walking_visitor::visit(declaration); + if (!this->in_global_scope && declaration->identifier.exported()) + { + add_error<materialization_error>(declaration->identifier.id().position(), + materialization_error::kind::local_export); + } + if (!declaration->identifier.parameters.empty()) + { + add_error<materialization_error>(declaration->identifier.id().position(), + materialization_error::kind::constant_generic); + } + } + void materialization_visitor::visit(literal<integer_literal> *literal) { switch (literal->wants_signed) diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 9b3dec4..36faee1 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -317,6 +317,10 @@ namespace elna::boot { } + void forward_declaration_visitor::visit(constant_declaration *) + { + } + void forward_declaration_visitor::visit(procedure_declaration *) { } @@ -449,6 +453,26 @@ namespace elna::boot return variable_symbol; } + std::shared_ptr<constant_info> resolving_visitor::register_constant(const std::string& name, + const type& constant_type, const constant_value& value, const source_position position) + { + const std::shared_ptr<constant_info> constant_symbol = + std::make_shared<constant_info>(constant_type, value); + constant_symbol->position.emplace(position); + constant_symbol->file = this->module_file; + + if (!this->bag.enter(name, constant_symbol)) + { + const std::shared_ptr<info> original = this->bag.lookup(name); + symbol_declaration_error::redefinition original_definition{ + .original = original->position, + .file = this->redefinition_file(original) + }; + add_error<symbol_declaration_error>(position, name, original_definition); + } + return constant_symbol; + } + std::optional<type> resolving_visitor::lookup_pointer_like_field( const std::string& field_name, const type& element_type) { @@ -1063,6 +1087,10 @@ namespace elna::boot { expression->type_decoration = variable_symbol->symbol; } + else if (const auto constant_symbol = from_symbol_table->is_constant()) + { + expression->type_decoration = constant_symbol->symbol; + } else if (const auto procedure_symbol = from_symbol_table->is_procedure()) { type procedure = type(std::make_shared<procedure_type>(procedure_symbol->symbol)); @@ -1502,6 +1530,25 @@ namespace elna::boot } } + void declaration_visitor::visit(constant_declaration *declaration) + { + declaration->initializer->accept(this); + const std::optional<constant_value> computed = + this->constant_evaluator.evaluate(*declaration->initializer); + if (!computed.has_value()) + { + const non_constant_expression_error::initializer payload{ { declaration->identifier.id() } }; + add_error<non_constant_expression_error>(declaration->initializer->position(), payload); + return; + } + const type constant_type = declaration->initializer->type_decoration; + const source_position position = declaration->position(); + const std::string name = declaration->identifier.name(); + const std::shared_ptr<constant_info> constant_symbol = + register_constant(name, constant_type, computed.value(), position); + constant_symbol->exported = declaration->identifier.exported(); + } + void declaration_visitor::visit(procedure_declaration *declaration) { // The scope is opened before the heading is built so that the type @@ -1633,6 +1680,28 @@ namespace elna::boot } } + void name_analysis_visitor::visit(constant_declaration *declaration) + { + if (!this->bag.is_global()) + { + declaration->initializer->accept(this); + const std::optional<constant_value> computed = + this->constant_evaluator.evaluate(*declaration->initializer); + if (!computed.has_value()) + { + const non_constant_expression_error::initializer payload{ { declaration->identifier.id() } }; + add_error<non_constant_expression_error>(declaration->initializer->position(), payload); + return; + } + const type constant_type = declaration->initializer->type_decoration; + const source_position position = declaration->position(); + const std::string name = declaration->identifier.name(); + const std::shared_ptr<constant_info> constant_symbol = + register_constant(name, constant_type, computed.value(), position); + constant_symbol->exported = declaration->identifier.exported(); + } + } + void name_analysis_visitor::visit(procedure_declaration *declaration) { if (declaration->body.has_value()) diff --git a/boot/parser.yy b/boot/parser.yy index 9628443..fa701ec 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -132,6 +132,10 @@ along with GCC; see the file COPYING3. If not see %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 <elna::boot::constant_declaration *> const_declaration; +%type <std::vector<elna::boot::constant_declaration *>> const_declarations; +%type <std::pair<std::vector<elna::boot::constant_declaration *>, std::vector<elna::boot::variable_declaration *>>> + block_declaration block_declarations; %type <elna::boot::type_expression *> type_expression return_declaration; %type <std::vector<elna::boot::type_expression *>> type_expressions; %type <elna::boot::expression *> expression operand simple_expression procedure_return unary_expression; @@ -201,6 +205,12 @@ section: $$.insert($$.end(), variables.begin(), variables.end()); } + | "const" const_declarations + { + std::vector<boot::constant_declaration *> constants = $2; + + $$.insert($$.end(), constants.begin(), constants.end()); + } | "threadvar" variable_declarations { std::vector<boot::variable_declaration *> variables = $2; @@ -231,11 +241,39 @@ sections: procedure_body: block procedure_return { $$ = std::make_unique<boot::procedure_body>(std::move(*$1), $2); } +block_declaration: + "const" const_declarations + { + $$ = { std::move($2), {} }; + } + | "var" variable_declarations + { + $$ = { {}, std::move($2) }; + } +block_declarations: + block_declaration block_declarations + { + auto [constants, variables] = std::move($1); + auto [more_constants, more_variables] = std::move($2); + + constants.insert(constants.end(), more_constants.begin(), more_constants.end()); + variables.insert(variables.end(), more_variables.begin(), more_variables.end()); + $$ = { std::move(constants), std::move(variables) }; + } + | block_declaration + { + $$ = std::move($1); + } block: - "var" variable_declarations statement_part - { $$ = std::make_unique<boot::block>($2, $3); } + block_declarations statement_part + { + auto [constants, variables] = std::move($1); + $$ = std::make_unique<boot::block>(std::move(constants), std::move(variables), $2); + } | optional_begin_statements - { $$ = std::make_unique<boot::block>(std::vector<boot::variable_declaration *>(), $1); } + { + $$ = std::make_unique<boot::block>(std::vector<boot::variable_declaration *>(), $1); + } statement_part: %empty {} | "begin" statements { $$ = $2; } @@ -749,6 +787,18 @@ variable_declarations: $$ = $2; $$.insert(std::cbegin($$), $1); } +const_declaration: + identifier_definition "=" expression + { + $$ = new boot::constant_declaration(boot::make_position(@$), std::move(*$1), $3); + } +const_declarations: + %empty {} + | const_declaration const_declarations + { + $$ = $2; + $$.insert(std::cbegin($$), $1); + } import_path: IDENTIFIER "." import_path { diff --git a/boot/symbol.cc b/boot/symbol.cc index ffefea4..8feba4a 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -326,6 +326,11 @@ namespace elna::boot return nullptr; } + std::shared_ptr<constant_info> info::is_constant() + { + return nullptr; + } + type_info::type_info(const type& symbol) : symbol(symbol), owner(symbol.get<alias_type>()) { @@ -362,6 +367,16 @@ namespace elna::boot return std::static_pointer_cast<variable_info>(shared_from_this()); } + constant_info::constant_info(const type& symbol, const constant_value& value) + : symbol(symbol), value(value) + { + } + + std::shared_ptr<constant_info> constant_info::is_constant() + { + return std::static_pointer_cast<constant_info>(shared_from_this()); + } + label_info::label_info(std::size_t defer_depth) : defer_depth(defer_depth) { diff --git a/boot/validation.cc b/boot/validation.cc index 4539633..aa47557 100644 --- a/boot/validation.cc +++ b/boot/validation.cc @@ -73,6 +73,10 @@ namespace elna::boot { } + void validation_visitor::visit(constant_declaration *) + { + } + void validation_visitor::visit(procedure_declaration *declaration) { if (declaration->body.has_value()) diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index bf4784b..57c8c22 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -454,6 +454,16 @@ namespace elna::gcc } } + void generic_visitor::visit(boot::constant_declaration *declaration) + { + const std::string& name = declaration->identifier.name(); + const std::shared_ptr<boot::info> symbol = this->bag.lookup(name); + const std::shared_ptr<boot::constant_info> constant_symbol = symbol->is_constant(); + tree type = get_inner_alias(constant_symbol->symbol, this->symbols); + tree constant_tree = constant_to_tree(constant_symbol->value, this->symbols, type); + this->symbols->enter(name, constant_tree); + } + void generic_visitor::visit(boot::procedure_declaration *declaration) { tree fndecl = this->symbols->lookup(declaration->identifier.name()); @@ -470,6 +480,7 @@ namespace elna::gcc { this->symbols->enter(IDENTIFIER_POINTER(DECL_NAME(argument_chain)), argument_chain); } + visit_all(this, declaration->body.value().constants); visit_all(this, declaration->body.value().variables); visit_statements(declaration->body.value().statements); @@ -1134,6 +1145,7 @@ namespace elna::gcc void generic_visitor::visit_block(boot::block& body) { this->bag.enter(body.symbols); + visit_all(this, body.constants); visit_all(this, body.variables); visit_statements(body.statements); this->bag.leave(); diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 8f8da13..c12d8e8 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -111,6 +111,7 @@ namespace elna::boot class variable_declaration; class procedure_declaration; class type_declaration; + class constant_declaration; class procedure_call; class cast_expression; class record_constructor_expression; @@ -154,11 +155,15 @@ namespace elna::boot */ struct block { + std::vector<constant_declaration *> constants; 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(std::vector<constant_declaration *>&& constants, + std::vector<variable_declaration *>&& variables, + std::vector<statement *>&& statements); block(std::vector<variable_declaration *>&& variables, std::vector<statement *>&& statements); block(const block&) = delete; block(block&& that) noexcept = default; @@ -175,6 +180,7 @@ namespace elna::boot struct parser_visitor { virtual void visit(variable_declaration *) = 0; + virtual void visit(constant_declaration *) = 0; virtual void visit(procedure_declaration *) = 0; virtual void visit(type_declaration *) = 0; virtual void visit(procedure_call *) = 0; @@ -235,6 +241,7 @@ namespace elna::boot [[noreturn]] void visit(extern_type_expression *) override; [[noreturn]] void visit(variable_declaration *) override; + [[noreturn]] void visit(constant_declaration *) override; [[noreturn]] void visit(procedure_declaration *) override; [[noreturn]] void visit(assign_statement *) override; [[noreturn]] void visit(if_statement *) override; @@ -284,6 +291,7 @@ namespace elna::boot void visit(extern_type_expression *) override; void visit(variable_declaration *) override; + void visit(constant_declaration *) override; void visit(procedure_declaration *) override; void visit(assign_statement *statement) override; void visit(if_statement *statement) override; @@ -748,6 +756,22 @@ namespace elna::boot }; /** + * Constant definition. + */ + class constant_declaration : public declaration + { + public: + const identifier_definition identifier; + expression *const initializer; + + constant_declaration(const source_position position, identifier_definition identifier, + expression *initializer); + ~constant_declaration() override; + + void accept(parser_visitor *visitor) override; + }; + + /** * Cast expression. */ class cast_expression : public expression diff --git a/include/elna/boot/materialization.h b/include/elna/boot/materialization.h index 74b4133..7436c45 100644 --- a/include/elna/boot/materialization.h +++ b/include/elna/boot/materialization.h @@ -30,7 +30,8 @@ namespace elna::boot real_overflow, local_export, local_extern, - variable_generic + variable_generic, + constant_generic }; materialization_error(const source_position position, kind error_kind); @@ -52,6 +53,7 @@ namespace elna::boot void visit(unit *unit) override; void visit(procedure_declaration *declaration) override; void visit(variable_declaration *declaration) override; + void visit(constant_declaration *declaration) override; void visit(literal<integer_literal> *literal) override; void visit(literal<float_literal> *literal) override; diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index 62b91b4..1d9676a 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -200,6 +200,7 @@ namespace elna::boot void visit(unit *unit) override; void visit(type_declaration *declaration) override; void visit(variable_declaration *) override; + void visit(constant_declaration *) override; void visit(procedure_declaration *) override; }; @@ -351,6 +352,8 @@ namespace elna::boot std::filesystem::path redefinition_file(const std::shared_ptr<info>& original) const; std::shared_ptr<variable_info> register_variable(const std::string& name, const type& variable_type, const source_position position, const bool is_extern = false); + std::shared_ptr<constant_info> register_constant(const std::string& name, + const type& constant_type, const constant_value& value, const source_position position); 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); @@ -436,6 +439,7 @@ namespace elna::boot void visit(unit *unit) override; void visit(type_declaration *declaration) override; void visit(variable_declaration *declaration) override; + void visit(constant_declaration *declaration) override; void visit(procedure_declaration *declaration) override; [[noreturn]] void visit(for_statement *) override; @@ -456,6 +460,7 @@ namespace elna::boot void visit(unit *unit) override; void visit(type_declaration *) override; void visit(variable_declaration *declaration) override; + void visit(constant_declaration *declaration) override; void visit(procedure_declaration *declaration) override; void visit(for_statement *statement) override; diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index bdac269..190a297 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -277,6 +277,7 @@ namespace elna::boot class procedure_info; class variable_info; class label_info; + class constant_info; class info : public std::enable_shared_from_this<info> { @@ -292,6 +293,7 @@ namespace elna::boot virtual std::shared_ptr<procedure_info> is_procedure(); virtual std::shared_ptr<variable_info> is_variable(); virtual std::shared_ptr<label_info> is_label(); + virtual std::shared_ptr<constant_info> is_constant(); }; /** @@ -514,6 +516,29 @@ namespace elna::boot }; /** + * Constant symbol information. + */ + class constant_info : public info + { + public: + /// Constant type. + const type symbol; + + /// Evaluated constant value. + const constant_value value; + + /** + * Constructs constant symbol information. + * + * \param symbol Constant type. + * \param value Evaluated constant value. + */ + constant_info(const type& symbol, const constant_value& value); + + std::shared_ptr<constant_info> is_constant() override; + }; + + /** * Named block a break-statement can jump to. */ class label_info : public info diff --git a/include/elna/boot/validation.h b/include/elna/boot/validation.h index 2af68e0..028b0dd 100644 --- a/include/elna/boot/validation.h +++ b/include/elna/boot/validation.h @@ -59,6 +59,7 @@ namespace elna::boot void visit(unit *unit) override; void visit(type_declaration *declaration) override; void visit(variable_declaration *) override; + void visit(constant_declaration *) override; void visit(procedure_declaration *declaration) override; void visit(case_statement *statement) override; diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index f942f81..6fc395e 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -82,6 +82,7 @@ namespace elna::gcc void visit(boot::type_declaration *) override; void visit(boot::variable_declaration *declaration) override; + void visit(boot::constant_declaration *declaration) override; void visit(boot::procedure_declaration *) override; void visit(boot::procedure_call *call) override; void visit(boot::cast_expression *expression) override; diff --git a/testsuite/runnable/const_section.elna b/testsuite/runnable/const_section.elna new file mode 100644 index 0000000..a4fde8f --- /dev/null +++ b/testsuite/runnable/const_section.elna @@ -0,0 +1,16 @@ +program() +const + BUFFER_SIZE = 3 +var + arr: [BUFFER_SIZE]Int +begin + arr[1] := 10; + arr[BUFFER_SIZE] := 30; + assert(arr[1] = 10); + assert(arr[3] = 30); + block my_block + const LOCAL_VAL = 42 + begin + assert(LOCAL_VAL = 42) + end +end. diff --git a/tools/tree-sitter/grammar.js b/tools/tree-sitter/grammar.js index 9e62a78..ca0e7d5 100644 --- a/tools/tree-sitter/grammar.js +++ b/tools/tree-sitter/grammar.js @@ -79,6 +79,7 @@ module.exports = grammar({ _declaration: $ => choice( $.import_declaration, $.type_section, + $.const_section, $.var_section, $.threadvar_section, $.procedure_declaration, @@ -106,6 +107,17 @@ module.exports = grammar({ field('type', choice($._type_expression, 'extern')), ), + const_section: $ => seq( + 'const', + repeat1(seq($.const_declaration, optional(';'))), + ), + + const_declaration: $ => seq( + field('name', $.identifier_definition), + '=', + field('value', $._expression), + ), + identifier_definition: $ => seq( field('name', $.identifier), optional(field('type_parameters', $.type_parameters)), @@ -189,9 +201,14 @@ module.exports = grammar({ optional(field('value', $._expression)), ), + _block_declaration: $ => choice( + $.const_section, + $.var_section, + ), + block: $ => choice( seq( - $.var_section, + repeat1($._block_declaration), optional(seq('begin', optional($._statements))), ), seq( |
