aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/ast.cc41
-rw-r--r--boot/evaluator.cc54
-rw-r--r--boot/name_analysis.cc98
-rw-r--r--boot/parser.yy21
-rw-r--r--boot/symbol.cc15
-rw-r--r--gcc/gcc/elna-builtins.cc2
-rw-r--r--gcc/gcc/elna-tree.cc21
-rw-r--r--include/elna/boot/ast.h21
-rw-r--r--include/elna/boot/evaluator.h7
-rw-r--r--include/elna/boot/name_analysis.h5
-rw-r--r--include/elna/boot/symbol.h9
-rw-r--r--include/elna/gcc/elna-tree.h2
-rw-r--r--testsuite/runnable/enumeration_members.elna19
-rw-r--r--tools/tree-sitter/grammar.js7
-rw-r--r--tools/tree-sitter/queries/elna/highlights.scm4
15 files changed, 234 insertions, 92 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index bdc9089..dc643fe 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -400,8 +400,15 @@ namespace elna::boot
}
}
- void walking_visitor::visit(enumeration_type_expression *)
+ void walking_visitor::visit(enumeration_type_expression *expression)
{
+ for (const enumeration_type_expression::member_declaration& member : expression->members)
+ {
+ if (member.second != nullptr)
+ {
+ member.second->accept(this);
+ }
+ }
}
void walking_visitor::visit(extern_type_expression *)
@@ -816,26 +823,9 @@ namespace elna::boot
return this;
}
- field_initializer::field_initializer(identifier name, expression *value)
- : m_name(std::move(name)), m_value(value)
- {
- }
-
- field_initializer::~field_initializer()
- {
- delete this->m_value;
- }
-
- field_initializer::field_initializer(field_initializer&& other) noexcept
- : m_name(std::move(other.m_name)), m_value(std::exchange(other.m_value, nullptr))
- {
- }
-
- field_initializer& field_initializer::operator=(field_initializer&& other) noexcept
+ field_initializer::field_initializer(identifier&& name, expression *value)
+ : m_name(std::move(name)), m_value(std::unique_ptr<expression>(value))
{
- std::swap(this->m_value, other.m_value);
- this->m_name = std::move(other.m_name);
- return *this;
}
const std::string& field_initializer::name() const
@@ -853,15 +843,6 @@ namespace elna::boot
return *this->m_value;
}
- void field_initializer::value(expression& value)
- {
- if (this->m_value != &value)
- {
- delete this->m_value;
- this->m_value = &value;
- }
- }
-
record_constructor_expression::record_constructor_expression(const source_position position,
identifier&& type_name,
std::vector<field_initializer>&& field_initializers)
@@ -970,7 +951,7 @@ namespace elna::boot
}
enumeration_type_expression::enumeration_type_expression(const source_position position,
- std::vector<identifier>&& members)
+ std::vector<enumeration_type_expression::member_declaration>&& members)
: node(position), members(std::move(members))
{
}
diff --git a/boot/evaluator.cc b/boot/evaluator.cc
index 67fc42a..18633ba 100644
--- a/boot/evaluator.cc
+++ b/boot/evaluator.cc
@@ -48,6 +48,10 @@ namespace elna::boot
return "Array dimensions for type '" + payload.array_type.to_string()
+ "' should be constant";
}
+ else if constexpr (std::is_same_v<T, enumeration_value>)
+ {
+ return "Enumeration member value must be an integer constant expression";
+ }
}, this->payload);
}
@@ -63,6 +67,21 @@ namespace elna::boot
}
}
+ /**
+ * Builds the constant for an enumeration value.
+ *
+ * Goes through the bit pattern because \c integer_literal::from imports
+ * the magnitude of a negative value, not its two's complement.
+ *
+ * \param value Member value.
+ * \param size Size of the enumeration type in bytes.
+ * \return Signed constant of the given size.
+ */
+ static integer_literal enumeration_constant(const std::ptrdiff_t value, const std::size_t size)
+ {
+ return integer_literal::from(static_cast<std::size_t>(value)).cast_to(true, size);
+ }
+
std::optional<type_properties> get_type_properties(const type& subject, const target_info& target)
{
const type resolved = resolve_underlying_type(subject);
@@ -330,10 +349,12 @@ namespace elna::boot
{
return std::nullopt;
}
- const std::size_t enumeration_position = static_cast<std::size_t>(
- std::distance(enumeration->members.begin(), member_iterator));
+ const std::ptrdiff_t enumeration_position =
+ std::distance(enumeration->members.begin(), member_iterator);
- return constant_value{ integer_literal::from(enumeration_position + 1U) };
+ return constant_value{ enumeration_constant(
+ enumeration->values.at(static_cast<std::size_t>(enumeration_position)),
+ this->target.word_properties.front().size) };
}
}
}
@@ -897,21 +918,21 @@ namespace elna::boot
}
std::optional<constant_value> evaluator::cast_to_enumeration(const constant_value& source_expression,
- const std::size_t member_count, const std::size_t target_size)
+ const enumeration_type& enumeration, const std::size_t target_size)
{
- std::optional<constant_value> position = cast_to_integer(source_expression, false, target_size);
+ std::optional<constant_value> value = cast_to_integer(source_expression, true, target_size);
- if (!position.has_value())
+ if (!value.has_value())
{
return std::nullopt;
}
- const auto *converted = std::get_if<integer_literal>(&position.value());
+ const auto *converted = std::get_if<integer_literal>(&value.value());
- if (converted == nullptr || *converted < std::size_t{ 1 } || *converted > member_count)
+ if (converted == nullptr || *converted < enumeration.minimum() || *converted > enumeration.maximum())
{
return std::nullopt;
}
- return position;
+ return value;
}
std::optional<constant_value> evaluator::cast_to_pointer(const constant_value& source_expression)
@@ -952,7 +973,7 @@ namespace elna::boot
{
return std::nullopt;
}
- return cast_to_enumeration(value.value(), enumeration->members.size(), properties->size);
+ return cast_to_enumeration(value.value(), *enumeration, properties->size);
}
if (is_integral_type(target_type))
{
@@ -1055,10 +1076,8 @@ namespace elna::boot
}
if (const auto enumeration = resolved.get<enumeration_type>())
{
- return constant_value{
- integer_literal::from<std::size_t>(
- this->target.word_properties.front().size, 1U).value()
- };
+ return constant_value{ enumeration_constant(enumeration->minimum(),
+ this->target.word_properties.front().size) };
}
}
else if (subject.name.name() == "max")
@@ -1104,11 +1123,8 @@ namespace elna::boot
}
if (const auto enumeration = resolved.get<enumeration_type>())
{
- return constant_value{
- integer_literal::from<std::size_t>(
- this->target.word_properties.front().size,
- enumeration->members.size()).value()
- };
+ return constant_value{ enumeration_constant(enumeration->maximum(),
+ this->target.word_properties.front().size) };
}
}
else if (subject.name.name() == "offset")
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 12a14c3..ff5f6bc 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -17,6 +17,7 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/name_analysis.h"
+#include <limits>
#include <map>
#include <utility>
#include <algorithm>
@@ -156,6 +157,9 @@ namespace elna::boot
return "Procedure types are always constant, 'const' is not allowed";
case module_entry:
return "Program entry point should have no or one argument";
+ case enumeration_values:
+ return "Enumeration values should be omitted, given for the first member only,"
+ " or given for all members";
default:
__builtin_unreachable();
}
@@ -213,6 +217,10 @@ namespace elna::boot
case field_on_type:
return "Cannot access field '" + this->name + "' on type '"
+ this->composite.to_string() + "'";
+ case duplicate_value:
+ return "Enumeration member '" + this->name + "' has a value that is already used";
+ case value_out_of_range:
+ return "Value of the enumeration member '" + this->name + "' does not fit into 'Int'";
default:
__builtin_unreachable();
}
@@ -657,28 +665,104 @@ namespace elna::boot
void resolving_visitor::visit(enumeration_type_expression *expression)
{
+ const auto written = std::ranges::count_if(expression->members,
+ [](const enumeration_type_expression::member_declaration& member) {
+ return member.second != nullptr;
+ });
+ const bool first_written = expression->members.front().second != nullptr;
+ const bool all_written = static_cast<std::size_t>(written) == expression->members.size();
+
+ if (written > 0 && !all_written && (written != 1 || !first_written))
+ {
+ add_error<declaration_format_error>(expression->position(),
+ declaration_format_error::kind::enumeration_values);
+ this->current_type = type();
+ return;
+ }
std::vector<std::string> member_names;
+ std::vector<std::ptrdiff_t> member_values;
+ std::map<std::ptrdiff_t, std::string> used_values;
+ bool values_valid = true;
+
member_names.reserve(expression->members.size());
- for (const auto& member : expression->members)
+ member_values.reserve(expression->members.size());
+ for (const enumeration_type_expression::member_declaration& member : expression->members)
{
- member_names.emplace_back(member.name());
+ std::optional<std::ptrdiff_t> value;
+
+ if (member.second != nullptr)
+ {
+ member.second->accept(this);
+ if (member.second->type_decoration.empty())
+ {
+ // The value expression failed to resolve and reported its own error already.
+ values_valid = false;
+ continue;
+ }
+ const std::optional<constant_value> evaluated = this->constant_evaluator.evaluate(*member.second);
+ const integer_literal *literal = evaluated.has_value()
+ ? std::get_if<integer_literal>(&evaluated.value())
+ : nullptr;
+
+ if (literal == nullptr)
+ {
+ add_error<non_constant_expression_error>(member.second->position(),
+ non_constant_expression_error::enumeration_value{});
+ values_valid = false;
+ continue;
+ }
+ value = literal->try_to<std::ptrdiff_t>();
+ }
+ else if (member_values.empty())
+ {
+ value = std::ptrdiff_t{ 1 };
+ }
+ else if (member_values.back() < std::numeric_limits<std::ptrdiff_t>::max())
+ {
+ value = member_values.back() + 1;
+ }
+
+ if (!value.has_value())
+ {
+ add_error<member_error>(member.first.position(), member.first.name(), type(),
+ member_error::kind::value_out_of_range);
+ values_valid = false;
+ continue;
+ }
+ const std::map<std::ptrdiff_t, std::string>::const_iterator existing = used_values.find(value.value());
+
+ if (existing != used_values.end())
+ {
+ add_error<member_error>(member.first.position(), member.first.name(), type(),
+ member_error::kind::duplicate_value);
+ values_valid = false;
+ continue;
+ }
+ used_values.insert({ value.value(), member.first.name() });
+ member_names.emplace_back(member.first.name());
+ member_values.emplace_back(value.value());
+ }
+ if (!values_valid)
+ {
+ this->current_type = type();
+ return;
}
std::shared_ptr<enumeration_type> const result_type = std::make_shared<enumeration_type>(
- member_names);
+ member_names, member_values);
std::map<std::string, source_position> seen;
const type aggregate(result_type);
- for (const auto& member : expression->members)
+ for (const enumeration_type_expression::member_declaration& member : expression->members)
{
- const std::map<std::string, source_position>::const_iterator existing = seen.find(member.name());
+ const std::map<std::string, source_position>::const_iterator existing = seen.find(member.first.name());
if (existing != seen.end())
{
- add_error<member_error>(member.position(), member.name(), aggregate,
+ add_error<member_error>(member.first.position(), member.first.name(), aggregate,
member_error::duplicate{ .original = existing->second, .base = std::nullopt });
}
else
{
- seen.insert({ member.name(), member.position() });
+ seen.insert({ member.first.name(), member.first.position() });
}
}
this->current_type = type(result_type);
diff --git a/boot/parser.yy b/boot/parser.yy
index 04c07a8..e8c3590 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -162,6 +162,8 @@ along with GCC; see the file COPYING3. If not see
%type <std::vector<elna::boot::attribute>> attributes;
%type <std::vector<std::string>> import_declaration;
%type <std::vector<elna::boot::identifier>> required_identifiers optional_identifiers generic_parameters;
+%type <std::unique_ptr<elna::boot::enumeration_type_expression::member_declaration>> enumeration_member;
+%type <std::vector<elna::boot::enumeration_type_expression::member_declaration>> enumeration_members;
%type <elna::boot::named_expression *> name_reference;
%type <std::vector<elna::boot::import_declaration *>> import_declarations;
%type <elna::boot::array_type_expression *> array_type_expression;
@@ -679,9 +681,24 @@ type_expression:
}
| "proc" procedure_heading
{ $$ = $2; }
- | "(" required_identifiers ")"
- { $$ = new boot::enumeration_type_expression(boot::make_position(@$), $2); }
+ | "(" enumeration_members ")"
+ { $$ = new boot::enumeration_type_expression(boot::make_position(@$), std::move($2)); }
| name_reference { $$ = $1; }
+enumeration_members:
+ enumeration_member "," enumeration_members
+ {
+ $$ = std::move($3);
+ $$.emplace($$.cbegin(), std::move(*$1));
+ }
+ | enumeration_member { $$.emplace_back(std::move(*$1)); }
+enumeration_member:
+ identifier
+ { $$ = std::make_unique<boot::enumeration_type_expression::member_declaration>(std::move(*$1), nullptr); }
+ | identifier "=" expression
+ {
+ $$ = std::make_unique<boot::enumeration_type_expression::member_declaration>(std::move(*$1),
+ std::unique_ptr<boot::expression>($3));
+ }
generic_parameters:
%empty {}
| "#[" required_identifiers "]" { $$ = $2; }
diff --git a/boot/symbol.cc b/boot/symbol.cc
index fa423e0..375cbfd 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -273,11 +273,22 @@ namespace elna::boot
{
}
- enumeration_type::enumeration_type(const std::vector<std::string>& members)
- : members(members)
+ enumeration_type::enumeration_type(const std::vector<std::string>& members,
+ const std::vector<std::ptrdiff_t>& values)
+ : members(members), values(values)
{
}
+ std::ptrdiff_t enumeration_type::minimum() const
+ {
+ return *std::ranges::min_element(this->values);
+ }
+
+ std::ptrdiff_t enumeration_type::maximum() const
+ {
+ return *std::ranges::max_element(this->values);
+ }
+
parameter_type::parameter_type(const std::string& name)
: name(name)
{
diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc
index 56950e3..8eb5ce1 100644
--- a/gcc/gcc/elna-builtins.cc
+++ b/gcc/gcc/elna-builtins.cc
@@ -253,7 +253,7 @@ namespace elna::gcc
}
else if (const auto reference = type.get<boot::enumeration_type>())
{
- return build_enumeration_type(reference->members);
+ return build_enumeration_type(*reference);
}
else if (const auto reference = type.get<boot::extern_type>())
{
diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc
index b6384f8..0f94864 100644
--- a/gcc/gcc/elna-tree.cc
+++ b/gcc/gcc/elna-tree.cc
@@ -186,38 +186,39 @@ namespace elna::gcc
return build_constructor(slice_type, elements);
}
- tree build_enumeration_type(const std::vector<std::string>& members)
+ tree build_enumeration_type(const boot::enumeration_type& enumeration)
{
tree composite_type_node = make_node(ENUMERAL_TYPE);
- tree base_type = elna_word_type_node;
+ tree base_type = elna_int_type_node;
TREE_TYPE(composite_type_node) = base_type;
ENUM_IS_SCOPED(composite_type_node) = 1;
+ // The precision and signedness are needed to build the member
+ // constants, so they are set before the members.
+ TYPE_UNSIGNED(composite_type_node) = TYPE_UNSIGNED(base_type);
+ TYPE_PRECISION(composite_type_node) = TYPE_PRECISION(base_type);
tree *members_pointer = &TYPE_VALUES(composite_type_node);
- std::size_t order{ 1 };
+ std::vector<std::ptrdiff_t>::const_iterator value_iterator = enumeration.values.begin();
- for (const std::string& member : members)
+ for (const std::string& member : enumeration.members)
{
tree member_name = get_identifier(member.c_str());
tree member_declaration = build_decl(UNKNOWN_LOCATION, CONST_DECL, member_name, composite_type_node);
DECL_CONTEXT(member_declaration) = composite_type_node;
- DECL_INITIAL(member_declaration) = build_int_cst_type(composite_type_node, order++);
+ DECL_INITIAL(member_declaration) = build_int_cst_type(composite_type_node, *value_iterator++);
TREE_CONSTANT(member_declaration) = 1;
TREE_READONLY(member_declaration) = 1;
- TYPE_MAX_VALUE(composite_type_node) = DECL_INITIAL(member_declaration);
-
*members_pointer = build_tree_list(member_name, member_declaration);
members_pointer = &TREE_CHAIN(*members_pointer);
}
- TYPE_MIN_VALUE(composite_type_node) = DECL_INITIAL(TREE_VALUE(TYPE_VALUES(composite_type_node)));
- TYPE_UNSIGNED(composite_type_node) = TYPE_UNSIGNED(base_type);
+ TYPE_MIN_VALUE(composite_type_node) = build_int_cst_type(composite_type_node, enumeration.minimum());
+ TYPE_MAX_VALUE(composite_type_node) = build_int_cst_type(composite_type_node, enumeration.maximum());
// NOLINTNEXTLINE(bugprone-signed-bitwise)
SET_TYPE_ALIGN(composite_type_node, TYPE_ALIGN(base_type));
TYPE_SIZE(composite_type_node) = NULL_TREE;
- TYPE_PRECISION(composite_type_node) = TYPE_PRECISION(base_type);
layout_type(composite_type_node);
return composite_type_node;
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index fba8ad4..c3b43bb 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -546,23 +546,15 @@ namespace elna::boot
struct field_initializer
{
- field_initializer(identifier name, expression *value);
- field_initializer(const field_initializer&) = delete;
- field_initializer(field_initializer&& other) noexcept;
-
- field_initializer& operator=(const field_initializer&) = delete;
- field_initializer& operator=(field_initializer&& other) noexcept;
-
- ~field_initializer();
+ field_initializer(identifier&& name, expression *value);
const std::string& name() const;
const identifier& id() const;
expression& value() const;
- void value(expression& value);
private:
identifier m_name;
- expression *m_value;
+ std::unique_ptr<expression> m_value;
};
class record_constructor_expression : public expression
@@ -606,10 +598,15 @@ namespace elna::boot
class enumeration_type_expression : public type_expression
{
public:
- const std::vector<identifier> members;
+ /**
+ * Member of an enumeration with an optional explicitly written value.
+ */
+ using member_declaration = std::pair<identifier, std::unique_ptr<expression>>;
+
+ const std::vector<member_declaration> members;
enumeration_type_expression(const source_position,
- std::vector<identifier>&& members);
+ std::vector<member_declaration>&& members);
void accept(parser_visitor *visitor) override;
enumeration_type_expression *is_enumeration() override;
diff --git a/include/elna/boot/evaluator.h b/include/elna/boot/evaluator.h
index dc6fd71..6dc204f 100644
--- a/include/elna/boot/evaluator.h
+++ b/include/elna/boot/evaluator.h
@@ -40,7 +40,10 @@ namespace elna::boot
{
type array_type;
};
- using payload_type = std::variant<initializer, case_label, array_dimensions>;
+ struct enumeration_value
+ {
+ };
+ using payload_type = std::variant<initializer, case_label, array_dimensions, enumeration_value>;
non_constant_expression_error(const source_position position, payload_type payload);
@@ -93,7 +96,7 @@ namespace elna::boot
static std::optional<constant_value> cast_to_bool(const constant_value& source_expression);
static std::optional<constant_value> cast_to_char(const constant_value& source_expression);
static std::optional<constant_value> cast_to_enumeration(const constant_value& source_expression,
- std::size_t member_count, std::size_t target_size);
+ const enumeration_type& enumeration, std::size_t target_size);
static std::optional<constant_value> cast_to_pointer(const constant_value& source_expression);
public:
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index d3f6f10..5cf5d43 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -131,7 +131,8 @@ namespace elna::boot
array_position,
duplicate,
constant_procedure,
- module_entry
+ module_entry,
+ enumeration_values
};
struct not_initialized
{
@@ -154,7 +155,7 @@ namespace elna::boot
class member_error final : public diagnostic
{
public:
- enum class kind { not_found, field_on_type };
+ enum class kind { not_found, field_on_type, duplicate_value, value_out_of_range };
struct duplicate {
std::optional<source_position> original;
std::optional<std::string> base;
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index 0d07b88..07bcf80 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -209,8 +209,15 @@ namespace elna::boot
struct enumeration_type
{
std::vector<std::string> members;
+ /// Value of each member, in the order of \c members.
+ std::vector<std::ptrdiff_t> values;
- explicit enumeration_type(const std::vector<std::string>& members);
+ enumeration_type(const std::vector<std::string>& members, const std::vector<std::ptrdiff_t>& values);
+
+ /// \return The smallest member value.
+ std::ptrdiff_t minimum() const;
+ /// \return The largest member value.
+ std::ptrdiff_t maximum() const;
};
/**
diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h
index 6dd3616..c160a9b 100644
--- a/include/elna/gcc/elna-tree.h
+++ b/include/elna/gcc/elna-tree.h
@@ -49,7 +49,7 @@ namespace elna::gcc
tree find_field_by_name(tree type, const std::string& field_name);
tree build_static_array_type(tree type, const std::uint64_t size);
tree build_slice(tree slice_type, tree ptr, tree length);
- tree build_enumeration_type(const std::vector<std::string>& members);
+ tree build_enumeration_type(const boot::enumeration_type& enumeration);
tree extract_constant(tree expression);
tree constant_to_tree(const boot::constant_value& value,
diff --git a/testsuite/runnable/enumeration_members.elna b/testsuite/runnable/enumeration_members.elna
new file mode 100644
index 0000000..b865f98
--- /dev/null
+++ b/testsuite/runnable/enumeration_members.elna
@@ -0,0 +1,19 @@
+program()
+type
+ E1 = (one, two, three, four)
+ E2 = (zero = 0, one, two, three)
+ E3 = (minus = -5, two = 2, ten = 10)
+
+begin
+ assert(cast(E1.one: Int) = 1);
+ assert(cast(E1.two: Int) = 2);
+ assert(cast(E1.four: Int) = 4);
+
+ assert(cast(E2.zero: Int) = 0);
+ assert(cast(E2.one: Int) = 1);
+ assert(cast(E2.three: Int) = 3);
+
+ assert(cast(E3.minus: Int) = -5);
+ assert(cast(E3.two: Int) = 2);
+ assert(cast(E3.ten: Int) = 10)
+end.
diff --git a/tools/tree-sitter/grammar.js b/tools/tree-sitter/grammar.js
index a43c947..bfb4c26 100644
--- a/tools/tree-sitter/grammar.js
+++ b/tools/tree-sitter/grammar.js
@@ -360,10 +360,15 @@ module.exports = grammar({
enum_type: $ => seq(
'(',
- commaSep1(field('member', $.identifier)),
+ commaSep1(field('member', $.enum_member)),
')',
),
+ enum_member: $ => seq(
+ field('name', $.identifier),
+ optional(seq('=', field('value', $._expression))),
+ ),
+
generic_type: $ => seq(
field('name', $.identifier),
field('type_arguments', $.type_arguments),
diff --git a/tools/tree-sitter/queries/elna/highlights.scm b/tools/tree-sitter/queries/elna/highlights.scm
index b5e6d10..4cb3f7c 100644
--- a/tools/tree-sitter/queries/elna/highlights.scm
+++ b/tools/tree-sitter/queries/elna/highlights.scm
@@ -101,8 +101,8 @@
(traits_expression
arguments: (identifier) @type)
-(enum_type
- member: (identifier) @constant)
+(enum_member
+ name: (identifier) @constant)
(procedure_declaration
name: (identifier_definition