aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/ast.cc41
-rw-r--r--boot/evaluator.cc4
-rw-r--r--boot/materialization.cc17
-rw-r--r--boot/name_analysis.cc69
-rw-r--r--boot/parser.yy56
-rw-r--r--boot/symbol.cc15
-rw-r--r--boot/validation.cc4
-rw-r--r--gcc/gcc/elna-generic.cc12
-rw-r--r--include/elna/boot/ast.h24
-rw-r--r--include/elna/boot/materialization.h4
-rw-r--r--include/elna/boot/name_analysis.h5
-rw-r--r--include/elna/boot/symbol.h25
-rw-r--r--include/elna/boot/validation.h1
-rw-r--r--include/elna/gcc/elna-generic.h1
-rw-r--r--testsuite/runnable/const_section.elna16
-rw-r--r--tools/tree-sitter/grammar.js19
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(