aboutsummaryrefslogtreecommitdiff
path: root/tools/tree-sitter
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-10-05 00:26:05 +0200
committerEugen Wissner <belka@caraus.de>2026-10-05 01:00:50 +0200
commita9f6cfb6a8b6d38057faca067c015da1f72cf62d (patch)
treedb4e740db52df8be62bda5d2101603910107b7d4 /tools/tree-sitter
parent5b7f983b3912a8d0fbeaf22f7fe0e2c5f1e0ff01 (diff)
downloadelna-a9f6cfb6a8b6d38057faca067c015da1f72cf62d.tar.gz
Add a treesitter grammar
Diffstat (limited to 'tools/tree-sitter')
-rw-r--r--tools/tree-sitter/grammar.js572
-rw-r--r--tools/tree-sitter/queries/elna/folds.scm20
-rw-r--r--tools/tree-sitter/queries/elna/highlights.scm201
-rw-r--r--tools/tree-sitter/queries/elna/indents.scm22
-rw-r--r--tools/tree-sitter/queries/elna/locals.scm33
-rw-r--r--tools/tree-sitter/test/corpus/aliased_type_constant.txt50
-rw-r--r--tools/tree-sitter/test/corpus/assign_record_to_base.txt61
-rw-r--r--tools/tree-sitter/test/corpus/compile_time_array_length.txt39
-rw-r--r--tools/tree-sitter/test/corpus/const_alias.txt45
-rw-r--r--tools/tree-sitter/test/corpus/const_element_array.txt47
-rw-r--r--tools/tree-sitter/test/corpus/const_extern.txt21
-rw-r--r--tools/tree-sitter/test/corpus/const_negation.txt49
-rw-r--r--tools/tree-sitter/test/corpus/const_var_chain.txt37
-rw-r--r--tools/tree-sitter/test/corpus/double_literals.txt56
-rw-r--r--tools/tree-sitter/test/corpus/empty_proc.txt21
-rw-r--r--tools/tree-sitter/test/corpus/empty_proc_type_expression.txt21
-rw-r--r--tools/tree-sitter/test/corpus/empty_statements_in_a_row.txt24
-rw-r--r--tools/tree-sitter/test/corpus/generic_alias.txt132
-rw-r--r--tools/tree-sitter/test/corpus/generic_arguments.txt167
-rw-r--r--tools/tree-sitter/test/corpus/generic_base_chain.txt144
-rw-r--r--tools/tree-sitter/test/corpus/generic_const_inference.txt204
-rw-r--r--tools/tree-sitter/test/corpus/generic_inference.txt157
-rw-r--r--tools/tree-sitter/test/corpus/generic_pointee.txt71
-rw-r--r--tools/tree-sitter/test/corpus/noreturn_procedure.txt23
-rw-r--r--tools/tree-sitter/test/corpus/opaque_type.txt65
-rw-r--r--tools/tree-sitter/test/corpus/pointer_cast.txt38
-rw-r--r--tools/tree-sitter/test/corpus/pointer_const_conversion.txt32
-rw-r--r--tools/tree-sitter/test/corpus/pointer_subtraction.txt32
-rw-r--r--tools/tree-sitter/test/corpus/procedure_forward_reference.txt40
-rw-r--r--tools/tree-sitter/test/corpus/record_const_base.txt76
-rw-r--r--tools/tree-sitter/test/corpus/self-referencing-record.txt27
-rw-r--r--tools/tree-sitter/test/corpus/semicolon_parameter_separator.txt29
-rw-r--r--tools/tree-sitter/test/corpus/single_literals.txt56
-rw-r--r--tools/tree-sitter/test/corpus/slice_index.txt58
-rw-r--r--tools/tree-sitter/test/corpus/take_const_address.txt35
-rw-r--r--tools/tree-sitter/test/corpus/trailing_text.txt16
-rw-r--r--tools/tree-sitter/test/corpus/traits_size.txt93
-rw-r--r--tools/tree-sitter/tree-sitter.json18
38 files changed, 2832 insertions, 0 deletions
diff --git a/tools/tree-sitter/grammar.js b/tools/tree-sitter/grammar.js
new file mode 100644
index 0000000..b7129df
--- /dev/null
+++ b/tools/tree-sitter/grammar.js
@@ -0,0 +1,572 @@
+const PREC = {
+ ASSIGNMENT: 1,
+ LOGICAL: 2, // or, &, xor
+ RELATIONAL: 3, // =, <>, <, >, <=, >=
+ SHIFT: 4, // <<, >>
+ ADD: 5, // +, -
+ MULTIPLY: 6, // *, /, %
+ UNARY: 7, // @, ~, -, +
+ CALL: 8, // function calls
+ POSTFIX: 9, // ., [], [to], ^
+};
+
+function commaSep1(rule) {
+ return seq(rule, repeat(seq(',', rule)));
+}
+
+function sep1(rule, separator) {
+ return seq(rule, repeat(seq(separator, rule)));
+}
+
+module.exports = grammar({
+ name: 'elna',
+
+ extras: $ => [
+ /\s/,
+ $.comment,
+ ],
+
+ word: $ => $.identifier,
+
+ conflicts: $ => [
+ [$._statement, $._expression],
+ ],
+
+ rules: {
+ source_file: $ => seq(
+ choice(
+ $.module_declaration,
+ $.program_declaration,
+ ),
+ optional($.trailing_content),
+ ),
+
+ trailing_content: $ => token(seq(/[^\s]/, repeat(/[\s\S]/))),
+
+ comment: $ => token(seq('(*', /[^*]*\*+([^)*][^*]*\*+)*/, ')')),
+
+ identifier: $ => /[A-Za-z_][A-Za-z0-9_]*/,
+
+ trait: $ => token(seq('#', /[A-Za-z_][A-Za-z0-9_]*/)),
+
+ module_declaration: $ => seq(
+ 'module',
+ repeat($._declaration),
+ 'end',
+ '.',
+ ),
+
+ program_declaration: $ => seq(
+ 'program',
+ '(',
+ optional(field('parameters', commaSep1($.identifier))),
+ ')',
+ choice(
+ seq(
+ repeat1($._declaration),
+ optional(seq('begin', optional($._statements))),
+ ),
+ seq('begin', optional($._statements)),
+ optional($._statements),
+ ),
+ 'end',
+ '.',
+ ),
+
+ _declaration: $ => choice(
+ $.import_declaration,
+ $.type_section,
+ $.var_section,
+ $.threadvar_section,
+ $.procedure_declaration,
+ ),
+
+ import_declaration: $ => seq(
+ 'import',
+ commaSep1($.import_path),
+ ),
+
+ import_path: $ => seq(
+ $.identifier,
+ repeat(seq('.', $.identifier)),
+ ),
+
+ type_section: $ => seq(
+ 'type',
+ repeat1(seq($.type_declaration, optional(';'))),
+ ),
+
+ type_declaration: $ => seq(
+ field('name', $.identifier_definition),
+ optional(field('type_parameters', $.type_parameters)),
+ '=',
+ field('type', choice($._type_expression, 'extern')),
+ ),
+
+ identifier_definition: $ => seq(
+ field('name', $.identifier),
+ optional(field('exported', '*')),
+ repeat($.attribute),
+ ),
+
+ attribute: $ => seq(
+ '#{',
+ field('name', $.identifier),
+ optional(field('arguments', commaSep1($._expression))),
+ '}',
+ ),
+
+ type_parameters: $ => seq(
+ '#[',
+ commaSep1($.identifier),
+ ']',
+ ),
+
+ var_section: $ => seq(
+ 'var',
+ repeat1(seq($.variable_declaration, optional(';'))),
+ ),
+
+ threadvar_section: $ => seq(
+ 'threadvar',
+ repeat1(seq($.variable_declaration, optional(';'))),
+ ),
+
+ variable_declaration: $ => seq(
+ field('names', commaSep1($.identifier_definition)),
+ ':',
+ field('type', $._type_expression),
+ optional(seq(
+ ':=',
+ field('value', choice($._expression, 'extern')),
+ )),
+ ),
+
+ procedure_declaration: $ => seq(
+ 'proc',
+ field('name', $.identifier_definition),
+ optional(field('type_parameters', $.type_parameters)),
+ field('parameters', $.parameter_list),
+ repeat($.attribute),
+ optional(seq('->', field('return_type', $._type_expression))),
+ field('body', choice(
+ $.procedure_body,
+ 'extern',
+ )),
+ ),
+
+ parameter_list: $ => seq(
+ '(',
+ optional(seq(
+ $.field_declaration,
+ repeat(seq(';', $.field_declaration)),
+ optional(';'),
+ )),
+ ')',
+ ),
+
+ field_declaration: $ => seq(
+ field('names', commaSep1($.identifier_definition)),
+ ':',
+ field('type', $._type_expression),
+ ),
+
+ procedure_body: $ => seq(
+ optional($.block),
+ $.return_clause,
+ ),
+
+ return_clause: $ => seq(
+ 'return',
+ optional(field('value', $._expression)),
+ ),
+
+ block: $ => choice(
+ seq(
+ $.var_section,
+ optional(seq('begin', optional($._statements))),
+ ),
+ seq(
+ 'begin',
+ optional($._statements),
+ ),
+ $._statements,
+ ),
+
+ _statements: $ => repeat1(choice($._statement, ';')),
+
+ _statement: $ => choice(
+ $.assignment_statement,
+ $.call_expression,
+ $.if_statement,
+ $.while_statement,
+ $.repeat_statement,
+ $.for_statement,
+ $.defer_statement,
+ $.block_statement,
+ $.break_statement,
+ $.case_statement,
+ ),
+
+ assignment_statement: $ => prec.right(PREC.ASSIGNMENT, seq(
+ field('left', $._expression),
+ ':=',
+ field('right', $._expression),
+ )),
+
+ if_statement: $ => seq(
+ 'if',
+ field('condition', $._expression),
+ 'then',
+ field('consequence', optional($.block)),
+ repeat($.elsif_clause),
+ optional($.else_clause),
+ 'end',
+ ),
+
+ elsif_clause: $ => seq(
+ 'elsif',
+ field('condition', $._expression),
+ 'then',
+ field('consequence', optional($.block)),
+ ),
+
+ else_clause: $ => seq(
+ 'else',
+ field('body', optional($.block)),
+ ),
+
+ while_statement: $ => seq(
+ 'while',
+ field('condition', $._expression),
+ 'do',
+ field('body', optional($.block)),
+ repeat($.while_elsif_clause),
+ 'end',
+ ),
+
+ while_elsif_clause: $ => seq(
+ 'elsif',
+ field('condition', $._expression),
+ 'do',
+ field('body', optional($.block)),
+ ),
+
+ repeat_statement: $ => seq(
+ 'repeat',
+ field('body', optional($.block)),
+ 'until',
+ field('condition', $._expression),
+ ),
+
+ for_statement: $ => seq(
+ 'for',
+ field('variable', $.identifier),
+ 'of',
+ field('iterable', $._expression),
+ optional(seq('with', field('counter', $.identifier))),
+ 'do',
+ field('body', optional($.block)),
+ 'end',
+ ),
+
+ defer_statement: $ => seq(
+ 'defer',
+ field('body', optional($.block)),
+ 'end',
+ ),
+
+ block_statement: $ => seq(
+ 'block',
+ field('name', $.identifier),
+ field('body', optional($.block)),
+ 'end',
+ ),
+
+ break_statement: $ => seq(
+ 'break',
+ field('label', $.identifier),
+ ),
+
+ case_statement: $ => seq(
+ 'case',
+ field('value', $._expression),
+ 'of',
+ optional('|'),
+ sep1($.case_arm, '|'),
+ optional($.else_clause),
+ 'end',
+ ),
+
+ case_arm: $ => seq(
+ field('patterns', commaSep1($._expression)),
+ ':',
+ field('body', optional($.block)),
+ ),
+
+ // Type expressions
+ _type_expression: $ => choice(
+ $.array_type,
+ $.slice_type,
+ $.const_type,
+ $.pointer_type,
+ $.record_type,
+ $.procedure_type,
+ $.enum_type,
+ $.generic_type,
+ $.identifier,
+ ),
+
+ array_type: $ => prec(1, seq(
+ '[',
+ field('length', $._expression),
+ ']',
+ field('element_type', $._type_expression),
+ )),
+
+ slice_type: $ => prec(1, seq(
+ '[',
+ ']',
+ field('element_type', $._type_expression),
+ )),
+
+ const_type: $ => seq(
+ 'const',
+ field('type', $._type_expression),
+ ),
+
+ pointer_type: $ => prec.right(seq(
+ '^',
+ field('type', $._type_expression),
+ )),
+
+ record_type: $ => seq(
+ 'record',
+ optional(seq('(', field('base', choice($.identifier, $.generic_type)), ')')),
+ repeat($.attribute),
+ repeat(seq($.field_declaration, optional(';'))),
+ 'end',
+ ),
+
+ procedure_type: $ => seq(
+ 'proc',
+ field('parameters', $.parameter_list),
+ repeat($.attribute),
+ optional(seq('->', field('return_type', $._type_expression))),
+ ),
+
+ enum_type: $ => seq(
+ '(',
+ commaSep1(field('member', $.identifier)),
+ ')',
+ ),
+
+ generic_type: $ => seq(
+ field('name', $.identifier),
+ field('type_arguments', $.type_arguments),
+ ),
+
+ type_arguments: $ => seq(
+ '#[',
+ commaSep1($._type_expression),
+ ']',
+ ),
+
+ // Expressions
+ _expression: $ => choice(
+ $.literal,
+ $.identifier,
+ $.generic_type,
+ $.unary_expression,
+ $.binary_expression,
+ $.call_expression,
+ $.index_expression,
+ $.slice_expression,
+ $.field_expression,
+ $.dereference_expression,
+ $.cast_expression,
+ $.traits_expression,
+ $.record_constructor,
+ $.array_constructor,
+ $.parenthesized_expression,
+ ),
+
+ unary_expression: $ => prec.right(PREC.UNARY, seq(
+ field('operator', choice('@', '~', '-', '+')),
+ field('argument', $._expression),
+ )),
+
+ binary_expression: $ => choice(
+ ...[
+ ['or', PREC.LOGICAL],
+ ['&', PREC.LOGICAL],
+ ['xor', PREC.LOGICAL],
+ ['=', PREC.RELATIONAL],
+ ['<>', PREC.RELATIONAL],
+ ['<', PREC.RELATIONAL],
+ ['>', PREC.RELATIONAL],
+ ['<=', PREC.RELATIONAL],
+ ['>=', PREC.RELATIONAL],
+ ['<<', PREC.SHIFT],
+ ['>>', PREC.SHIFT],
+ ['+', PREC.ADD],
+ ['-', PREC.ADD],
+ ['*', PREC.MULTIPLY],
+ ['/', PREC.MULTIPLY],
+ ['%', PREC.MULTIPLY],
+ ].map(([operator, precedence]) =>
+ prec.left(precedence, seq(
+ field('left', $._expression),
+ field('operator', operator),
+ field('right', $._expression),
+ ))
+ )
+ ),
+
+ call_expression: $ => prec(PREC.CALL, seq(
+ field('function', $._expression),
+ field('arguments', $.argument_list),
+ )),
+
+ argument_list: $ => seq(
+ '(',
+ optional(commaSep1($._expression)),
+ ')',
+ ),
+
+ index_expression: $ => prec(PREC.POSTFIX, seq(
+ field('array', $._expression),
+ '[',
+ field('index', $._expression),
+ ']',
+ )),
+
+ slice_expression: $ => prec(PREC.POSTFIX, seq(
+ field('array', $._expression),
+ '[',
+ field('start', $._expression),
+ 'to',
+ field('end', $._expression),
+ ']',
+ )),
+
+ field_expression: $ => prec(PREC.POSTFIX, seq(
+ field('operand', $._expression),
+ '.',
+ field('field', $.identifier),
+ )),
+
+ dereference_expression: $ => prec(PREC.POSTFIX, seq(
+ field('operand', $._expression),
+ '^',
+ )),
+
+ cast_expression: $ => seq(
+ 'cast',
+ '(',
+ field('value', $._expression),
+ ':',
+ field('type', $._type_expression),
+ ')',
+ ),
+
+ traits_expression: $ => seq(
+ field('trait', $.trait),
+ '(',
+ field('arguments', commaSep1($._type_expression)),
+ ')',
+ ),
+
+ record_constructor: $ => prec(PREC.CALL, seq(
+ field('type', choice($.identifier, $.generic_type)),
+ '{',
+ optional(commaSep1($.field_initializer)),
+ '}',
+ )),
+
+ field_initializer: $ => seq(
+ field('name', $.identifier),
+ ':',
+ field('value', $._expression),
+ ),
+
+ array_constructor: $ => seq(
+ '[',
+ commaSep1($._expression),
+ ']',
+ ),
+
+ parenthesized_expression: $ => seq(
+ '(',
+ $._expression,
+ ')',
+ ),
+
+ // Literals
+ literal: $ => choice(
+ $.integer,
+ $.float,
+ $.string,
+ $.byte,
+ $.character,
+ $.boolean,
+ $.nil,
+ ),
+
+ boolean: $ => choice('true', 'false'),
+ nil: $ => 'nil',
+
+ integer: $ => token(choice(
+ seq(/0[xX][0-9a-fA-F]+/, optional(choice('u8', 'u16', 'u32', 'u64'))),
+ seq(/0[bB][01]+/, optional(choice('u8', 'u16', 'u32', 'u64'))),
+ seq(/[0-9]+/, optional(choice('u', 'u8', 'u16', 'u32', 'u64', 'i8', 'i16', 'i32', 'i64'))),
+ )),
+
+ float: $ => token(seq(
+ /[0-9]+/,
+ choice(
+ seq('.', /[0-9]+/, optional(/[eE][+-]?[0-9]+/)),
+ /[eE][+-]?[0-9]+/,
+ ),
+ optional('f'),
+ )),
+
+ string: $ => seq(
+ '"',
+ repeat(choice(
+ token.immediate(prec(1, /[^"\\\n]+/)),
+ $.escape_sequence,
+ '\\\n',
+ )),
+ '"',
+ ),
+
+ byte: $ => seq(
+ "'",
+ repeat1(choice(
+ token.immediate(prec(1, /[^'\\\n]+/)),
+ $.escape_sequence,
+ '\\\n',
+ )),
+ "'",
+ ),
+
+ character: $ => seq(
+ '`',
+ repeat1(choice(
+ token.immediate(prec(1, /[^`\\\n]+/)),
+ $.escape_sequence,
+ '\\\n',
+ )),
+ '`',
+ ),
+
+ escape_sequence: $ => token.immediate(seq(
+ '\\',
+ choice(
+ /[^0-9]/,
+ /[0-9]{1,3}/,
+ ),
+ )),
+ },
+});
diff --git a/tools/tree-sitter/queries/elna/folds.scm b/tools/tree-sitter/queries/elna/folds.scm
new file mode 100644
index 0000000..0387513
--- /dev/null
+++ b/tools/tree-sitter/queries/elna/folds.scm
@@ -0,0 +1,20 @@
+[
+ (module_declaration)
+ (program_declaration)
+ (procedure_declaration)
+ (record_type)
+ (enum_type)
+ (block)
+ (if_statement)
+ (elsif_clause)
+ (else_clause)
+ (while_statement)
+ (while_elsif_clause)
+ (repeat_statement)
+ (for_statement)
+ (defer_statement)
+ (block_statement)
+ (case_statement)
+ (case_arm)
+ (comment)
+] @fold
diff --git a/tools/tree-sitter/queries/elna/highlights.scm b/tools/tree-sitter/queries/elna/highlights.scm
new file mode 100644
index 0000000..b5e6d10
--- /dev/null
+++ b/tools/tree-sitter/queries/elna/highlights.scm
@@ -0,0 +1,201 @@
+; Keywords
+[
+ "program"
+ "module"
+ "type"
+ "var"
+ "threadvar"
+ "record"
+ "extern"
+ "defer"
+ "block"
+ "break"
+ "with"
+ "to"
+ "cast"
+ "begin"
+ "end"
+] @keyword
+
+"proc" @keyword.function
+"return" @keyword.return
+"import" @keyword.import
+
+[
+ "while"
+ "do"
+ "for"
+ "repeat"
+ "until"
+] @keyword.repeat
+
+[
+ "if"
+ "then"
+ "else"
+ "elsif"
+ "case"
+ "of"
+] @keyword.conditional
+
+"const" @keyword.modifier
+
+[
+ "or"
+ "xor"
+] @keyword.operator
+
+; Comments
+(comment) @comment @spell
+
+; Literals
+(integer) @number
+(float) @number.float
+(boolean) @boolean
+(nil) @constant.builtin
+
+(string) @string
+(character) @character
+(byte) @character
+(escape_sequence) @string.escape
+
+; Traits and Attributes
+(trait) @attribute
+(attribute
+ "#{" @punctuation.special
+ name: (identifier) @attribute
+ "}" @punctuation.special)
+
+; Builtin Types
+((identifier) @type.builtin
+ (#any-of? @type.builtin
+ "Int" "Word"
+ "Int8" "Int16" "Int32" "Int64"
+ "Word8" "Word16" "Word32" "Word64"
+ "Single" "Double"
+ "Char" "Pointer" "Bool"))
+
+; Declarations and Definitions
+(type_declaration
+ name: (identifier_definition
+ name: (identifier) @type.definition))
+
+(type_parameters
+ (identifier) @type.parameter)
+
+(type_arguments
+ (identifier) @type)
+
+(record_type
+ base: (identifier) @type)
+
+(record_constructor
+ type: (identifier) @type)
+
+(generic_type
+ name: (identifier) @type)
+
+(cast_expression
+ type: (identifier) @type)
+
+(traits_expression
+ arguments: (identifier) @type)
+
+(enum_type
+ member: (identifier) @constant)
+
+(procedure_declaration
+ name: (identifier_definition
+ name: (identifier) @function))
+
+(identifier_definition
+ exported: "*" @punctuation.special)
+
+(field_declaration
+ names: (identifier_definition
+ name: (identifier) @variable.member))
+
+(parameter_list
+ (field_declaration
+ names: (identifier_definition
+ name: (identifier) @variable.parameter)))
+
+(variable_declaration
+ names: (identifier_definition
+ name: (identifier) @variable))
+
+(for_statement
+ variable: (identifier) @variable
+ counter: (identifier)? @variable)
+
+(block_statement
+ name: (identifier) @label)
+
+(break_statement
+ label: (identifier) @label)
+
+(import_path
+ (identifier) @module)
+
+; Calls and Member Access
+(call_expression
+ function: (identifier) @function.call)
+
+(call_expression
+ function: (field_expression
+ field: (identifier) @function.method.call))
+
+(field_expression
+ field: (identifier) @variable.member)
+
+(field_initializer
+ name: (identifier) @variable.member)
+
+; Fallback identifiers
+(identifier) @variable
+
+; Operators
+[
+ ":="
+ "="
+ "<>"
+ "<"
+ ">"
+ "<="
+ ">="
+ "+"
+ "-"
+ "*"
+ "/"
+ "%"
+ "<<"
+ ">>"
+ "&"
+ "|"
+ "~"
+ "->"
+ "^"
+ "@"
+] @operator
+
+; Delimiters and Punctuation
+[
+ ";"
+ ","
+ ":"
+ "."
+] @punctuation.delimiter
+
+[
+ "("
+ ")"
+ "["
+ "]"
+ "{"
+ "}"
+] @punctuation.bracket
+
+[
+ "#["
+ "#{"
+] @punctuation.special
diff --git a/tools/tree-sitter/queries/elna/indents.scm b/tools/tree-sitter/queries/elna/indents.scm
new file mode 100644
index 0000000..0e807d0
--- /dev/null
+++ b/tools/tree-sitter/queries/elna/indents.scm
@@ -0,0 +1,22 @@
+[
+ (type_section)
+ (var_section)
+ (threadvar_section)
+ (procedure_body)
+ (block)
+ (record_type)
+ (case_arm)
+] @indent.begin
+
+[
+ "end"
+ "until"
+ (return_clause)
+] @indent.end
+
+[
+ "begin"
+ (elsif_clause)
+ (else_clause)
+ (while_elsif_clause)
+] @indent.branch
diff --git a/tools/tree-sitter/queries/elna/locals.scm b/tools/tree-sitter/queries/elna/locals.scm
new file mode 100644
index 0000000..1e9bb70
--- /dev/null
+++ b/tools/tree-sitter/queries/elna/locals.scm
@@ -0,0 +1,33 @@
+[
+ (source_file)
+ (module_declaration)
+ (program_declaration)
+ (procedure_declaration)
+ (procedure_body)
+ (block)
+ (block_statement)
+ (if_statement)
+ (while_statement)
+ (for_statement)
+ (repeat_statement)
+ (case_statement)
+] @local.scope
+
+(variable_declaration
+ names: (identifier_definition
+ name: (identifier) @local.definition))
+
+(parameter_list
+ (field_declaration
+ names: (identifier_definition
+ name: (identifier) @local.definition)))
+
+(for_statement
+ variable: (identifier) @local.definition
+ counter: (identifier)? @local.definition)
+
+(procedure_declaration
+ name: (identifier_definition
+ name: (identifier) @local.definition))
+
+(identifier) @local.reference
diff --git a/tools/tree-sitter/test/corpus/aliased_type_constant.txt b/tools/tree-sitter/test/corpus/aliased_type_constant.txt
new file mode 100644
index 0000000..589c756
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/aliased_type_constant.txt
@@ -0,0 +1,50 @@
+==================
+aliased_type_constant
+==================
+
+module
+
+type
+ Handle = Int
+
+var
+ limit: const Int := 1
+
+proc check()
+begin
+ assert(limit = 1)
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (integer))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (procedure_body
+ (block
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (identifier)
+ (literal
+ (integer))))))
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/assign_record_to_base.txt b/tools/tree-sitter/test/corpus/assign_record_to_base.txt
new file mode 100644
index 0000000..6488889
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/assign_record_to_base.txt
@@ -0,0 +1,61 @@
+==================
+assign_record_to_base
+==================
+
+program()
+type
+ B = record
+ x: Int
+ end
+ R = record(B)
+ y: Int
+ end
+
+var
+ x: ^B := nil
+ y: ^R := nil
+
+begin
+ x := y
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (identifier)
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))
+ (literal
+ (nil)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))
+ (literal
+ (nil))))
+ (assignment_statement
+ (identifier)
+ (identifier))))
diff --git a/tools/tree-sitter/test/corpus/compile_time_array_length.txt b/tools/tree-sitter/test/corpus/compile_time_array_length.txt
new file mode 100644
index 0000000..0e250f2
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/compile_time_array_length.txt
@@ -0,0 +1,39 @@
+==================
+compile_time_array_length
+==================
+
+module
+
+var
+ source: const [3]Int := [1, 2, 3]
+ target: Word := source.length
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (array_type
+ (literal
+ (integer))
+ (identifier)))
+ (array_constructor
+ (literal
+ (integer))
+ (literal
+ (integer))
+ (literal
+ (integer))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (field_expression
+ (identifier)
+ (identifier))))))
diff --git a/tools/tree-sitter/test/corpus/const_alias.txt b/tools/tree-sitter/test/corpus/const_alias.txt
new file mode 100644
index 0000000..ae19588
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/const_alias.txt
@@ -0,0 +1,45 @@
+==================
+const_alias
+==================
+
+module
+
+type
+ CI = const Int
+ CCI = const CI
+
+var
+ x: CCI := 8
+ y: const CI := 9
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier)))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (literal
+ (integer)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (integer))))))
diff --git a/tools/tree-sitter/test/corpus/const_element_array.txt b/tools/tree-sitter/test/corpus/const_element_array.txt
new file mode 100644
index 0000000..e43c2f1
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/const_element_array.txt
@@ -0,0 +1,47 @@
+==================
+const_element_array
+==================
+
+module
+
+type
+ CI = const Int
+
+var
+ a: [3]CI := [1, 2, 3]
+ b: [3]CI
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (array_type
+ (literal
+ (integer))
+ (identifier))
+ (array_constructor
+ (literal
+ (integer))
+ (literal
+ (integer))
+ (literal
+ (integer))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (array_type
+ (literal
+ (integer))
+ (identifier))))))
diff --git a/tools/tree-sitter/test/corpus/const_extern.txt b/tools/tree-sitter/test/corpus/const_extern.txt
new file mode 100644
index 0000000..0fb71bc
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/const_extern.txt
@@ -0,0 +1,21 @@
+==================
+const_extern
+==================
+
+module
+
+var
+ errno_like: const Int := extern
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))))))
diff --git a/tools/tree-sitter/test/corpus/const_negation.txt b/tools/tree-sitter/test/corpus/const_negation.txt
new file mode 100644
index 0000000..89e801c
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/const_negation.txt
@@ -0,0 +1,49 @@
+==================
+const_negation
+==================
+
+program()
+var
+ x: const Int := -5
+ y: const Int := -x
+
+begin
+ assert(x = -5);
+ assert(y = 5);
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (unary_expression
+ (literal
+ (integer))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (unary_expression
+ (identifier))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (identifier)
+ (unary_expression
+ (literal
+ (integer))))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (identifier)
+ (literal
+ (integer)))))))
diff --git a/tools/tree-sitter/test/corpus/const_var_chain.txt b/tools/tree-sitter/test/corpus/const_var_chain.txt
new file mode 100644
index 0000000..fca40c9
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/const_var_chain.txt
@@ -0,0 +1,37 @@
+==================
+const_var_chain
+==================
+
+program()
+var
+ x: const Int := 42
+ y: Int := x
+
+begin
+ assert(y = 42)
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (integer)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (identifier)))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (identifier)
+ (literal
+ (integer)))))))
diff --git a/tools/tree-sitter/test/corpus/double_literals.txt b/tools/tree-sitter/test/corpus/double_literals.txt
new file mode 100644
index 0000000..9dc7cc1
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/double_literals.txt
@@ -0,0 +1,56 @@
+==================
+double_literals
+==================
+
+module
+
+proc f()
+var
+ x: const Double := 3.14
+ y: const Double := 1e10
+ z: const Double := 4.567e8
+ t: const Double := 2.5E-3
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (procedure_body
+ (block
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (float)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (float)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (float)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (float)))))
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/empty_proc.txt b/tools/tree-sitter/test/corpus/empty_proc.txt
new file mode 100644
index 0000000..338c2fb
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/empty_proc.txt
@@ -0,0 +1,21 @@
+==================
+empty_proc
+==================
+
+module
+
+proc f()
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (procedure_body
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/empty_proc_type_expression.txt b/tools/tree-sitter/test/corpus/empty_proc_type_expression.txt
new file mode 100644
index 0000000..3d259a4
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/empty_proc_type_expression.txt
@@ -0,0 +1,21 @@
+==================
+empty_proc_type_expression
+==================
+
+module
+
+type
+ P = proc()
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (procedure_type
+ (parameter_list))))))
diff --git a/tools/tree-sitter/test/corpus/empty_statements_in_a_row.txt b/tools/tree-sitter/test/corpus/empty_statements_in_a_row.txt
new file mode 100644
index 0000000..98700ae
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/empty_statements_in_a_row.txt
@@ -0,0 +1,24 @@
+==================
+empty_statements_in_a_row
+==================
+
+module
+
+proc f()
+begin
+ ;
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (procedure_body
+ (block)
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/generic_alias.txt b/tools/tree-sitter/test/corpus/generic_alias.txt
new file mode 100644
index 0000000..54a8ea2
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/generic_alias.txt
@@ -0,0 +1,132 @@
+==================
+generic_alias
+==================
+
+program()
+type
+ File = record
+ fd: Int
+ end
+
+ Node#[T] = record
+ next: ^Node#[T];
+ value: ^T
+ end
+
+ Ref#[T] = ^T
+
+ Boxed#[V] = record
+ inner: ^Node#[V]
+ end
+
+var
+ chain: Node#[File]
+ aliased: Ref#[File]
+ raw: ^File
+ boxed: Boxed#[File]
+
+begin
+ chain.next := nil;
+ aliased := raw;
+ raw := aliased;
+ boxed.inner := @chain
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)))))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier)))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (pointer_type
+ (identifier)))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier))))))))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)))))
+ (assignment_statement
+ (field_expression
+ (identifier)
+ (identifier))
+ (literal
+ (nil)))
+ (assignment_statement
+ (identifier)
+ (identifier))
+ (assignment_statement
+ (identifier)
+ (identifier))
+ (assignment_statement
+ (field_expression
+ (identifier)
+ (identifier))
+ (unary_expression
+ (identifier)))))
diff --git a/tools/tree-sitter/test/corpus/generic_arguments.txt b/tools/tree-sitter/test/corpus/generic_arguments.txt
new file mode 100644
index 0000000..3f325de
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/generic_arguments.txt
@@ -0,0 +1,167 @@
+==================
+generic_arguments
+==================
+
+program()
+type
+ File = record
+ fd: Int
+ end
+
+ Pair#[K, V] = record
+ key: ^K;
+ value: ^V
+ end
+
+var
+ entry: Pair#[File, Word8]
+
+proc allocate#[T](size: Word) -> ^T
+extern
+
+proc first#[K, V](entry_ptr: ^Pair#[K, V]) -> ^K
+return entry_ptr^.key
+
+begin
+ entry.key := allocate#[File](#size(File));
+ assert(#size(Pair#[File, Word8]) = #size(Pointer) * 2u);
+ assert(#offset(Pair#[File, Word8], value) = #size(Pointer));
+ assert(first#[File, Word8](@entry) = entry.key)
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier)
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier)))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier)))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (pointer_type
+ (identifier)))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier)
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier))))))
+ (pointer_type
+ (identifier))
+ (procedure_body
+ (return_clause
+ (field_expression
+ (dereference_expression
+ (identifier))
+ (identifier)))))
+ (assignment_statement
+ (field_expression
+ (identifier)
+ (identifier))
+ (call_expression
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)))
+ (argument_list
+ (traits_expression
+ (trait)
+ (identifier)))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (traits_expression
+ (trait)
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier))))
+ (binary_expression
+ (traits_expression
+ (trait)
+ (identifier))
+ (literal
+ (integer))))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (traits_expression
+ (trait)
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier)))
+ (identifier))
+ (traits_expression
+ (trait)
+ (identifier)))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (call_expression
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier)))
+ (argument_list
+ (unary_expression
+ (identifier))))
+ (field_expression
+ (identifier)
+ (identifier)))))))
diff --git a/tools/tree-sitter/test/corpus/generic_base_chain.txt b/tools/tree-sitter/test/corpus/generic_base_chain.txt
new file mode 100644
index 0000000..770b7db
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/generic_base_chain.txt
@@ -0,0 +1,144 @@
+==================
+generic_base_chain
+==================
+
+program()
+type
+ File = record
+ fd: Int
+ end
+
+ Map#[K, V] = record
+ key: ^K;
+ value: ^V
+ end
+
+ Cache#[K, V] = record(Map#[Word8, V])
+ newest: ^K
+ end
+
+var
+ store: Cache#[Int, File]
+ byte_key: ^Word8
+ upcast: ^Map#[Word8, File]
+
+proc put#[K2, V2](target: ^Map#[K2, V2]; key: ^K2)
+begin
+ target^.key := key
+return
+
+begin
+ upcast := @store;
+ put(@store, byte_key)
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier)
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier)))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier)))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier)
+ (identifier))
+ (record_type
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier)))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier))))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier)
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)
+ (identifier)))))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))
+ (procedure_body
+ (block
+ (assignment_statement
+ (field_expression
+ (dereference_expression
+ (identifier))
+ (identifier))
+ (identifier)))
+ (return_clause)))
+ (assignment_statement
+ (identifier)
+ (unary_expression
+ (identifier)))
+ (call_expression
+ (identifier)
+ (argument_list
+ (unary_expression
+ (identifier))
+ (identifier)))))
diff --git a/tools/tree-sitter/test/corpus/generic_const_inference.txt b/tools/tree-sitter/test/corpus/generic_const_inference.txt
new file mode 100644
index 0000000..e4b5bf9
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/generic_const_inference.txt
@@ -0,0 +1,204 @@
+==================
+generic_const_inference
+==================
+
+program()
+type
+ ConstAlias = const ^Word8
+ BasePtr = ^Word8
+ NestedConst = const BasePtr
+
+var
+ slice: []const Word8
+ aliased: ConstAlias := nil
+ nested: NestedConst := nil
+ direct_const: const ^Word8 := nil
+
+proc write#[T](fd: Int; buf: ^T; count: Int) -> Int
+return 0
+
+proc write_const#[T](fd: Int; buf: ^const T; count: Int) -> Int
+return 0
+
+begin
+ write(1, slice.ptr, cast(slice.length: Int));
+ write(1, aliased, 0);
+ write(1, nested, 0);
+ write(1, direct_const, 0);
+ write_const(1, slice.ptr, cast(slice.length: Int));
+ write_const(1, aliased, 0);
+ write_const(1, nested, 0);
+ write_const(1, direct_const, 0)
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (pointer_type
+ (identifier))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier)))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (slice_type
+ (const_type
+ (identifier))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (literal
+ (nil)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (literal
+ (nil)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (pointer_type
+ (identifier)))
+ (literal
+ (nil))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier)))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (identifier)
+ (procedure_body
+ (return_clause
+ (literal
+ (integer)))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (const_type
+ (identifier))))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (identifier)
+ (procedure_body
+ (return_clause
+ (literal
+ (integer)))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (literal
+ (integer))
+ (field_expression
+ (identifier)
+ (identifier))
+ (cast_expression
+ (field_expression
+ (identifier)
+ (identifier))
+ (identifier))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (literal
+ (integer))
+ (identifier)
+ (literal
+ (integer))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (literal
+ (integer))
+ (identifier)
+ (literal
+ (integer))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (literal
+ (integer))
+ (identifier)
+ (literal
+ (integer))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (literal
+ (integer))
+ (field_expression
+ (identifier)
+ (identifier))
+ (cast_expression
+ (field_expression
+ (identifier)
+ (identifier))
+ (identifier))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (literal
+ (integer))
+ (identifier)
+ (literal
+ (integer))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (literal
+ (integer))
+ (identifier)
+ (literal
+ (integer))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (literal
+ (integer))
+ (identifier)
+ (literal
+ (integer))))))
diff --git a/tools/tree-sitter/test/corpus/generic_inference.txt b/tools/tree-sitter/test/corpus/generic_inference.txt
new file mode 100644
index 0000000..ccac55c
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/generic_inference.txt
@@ -0,0 +1,157 @@
+==================
+generic_inference
+==================
+
+program()
+type
+ File = record
+ fd: Int
+ end
+
+ Cell#[T] = record
+ held: ^T
+ end
+
+var
+ files: Cell#[File]
+ bytes: Cell#[Word8]
+ f: File
+ b: Word8
+
+proc store#[T](target: ^Cell#[T]; value: ^T)
+ target^.held := value
+return
+
+proc relay#[T](target: ^Cell#[T]; value: ^T)
+ store(target, value)
+return
+
+begin
+ store(@files, @f);
+ relay(@bytes, @b);
+ store(@files, nil)
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)))))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))
+ (procedure_body
+ (block
+ (assignment_statement
+ (field_expression
+ (dereference_expression
+ (identifier))
+ (identifier))
+ (identifier)))
+ (return_clause)))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)))))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))
+ (procedure_body
+ (block
+ (call_expression
+ (identifier)
+ (argument_list
+ (identifier)
+ (identifier))))
+ (return_clause)))
+ (call_expression
+ (identifier)
+ (argument_list
+ (unary_expression
+ (identifier))
+ (unary_expression
+ (identifier))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (unary_expression
+ (identifier))
+ (unary_expression
+ (identifier))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (unary_expression
+ (identifier))
+ (literal
+ (nil))))))
diff --git a/tools/tree-sitter/test/corpus/generic_pointee.txt b/tools/tree-sitter/test/corpus/generic_pointee.txt
new file mode 100644
index 0000000..55f5a1a
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/generic_pointee.txt
@@ -0,0 +1,71 @@
+==================
+generic_pointee
+==================
+
+program()
+
+type
+ File = record
+ fd: Int
+ end
+
+proc allocate#[T](size: Word) -> ^T
+extern
+
+var
+ f: ^File
+
+begin
+ f := allocate#[File](#size(File));
+ assert(f = nil)
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (type_parameters
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (pointer_type
+ (identifier)))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))
+ (assignment_statement
+ (identifier)
+ (call_expression
+ (generic_type
+ (identifier)
+ (type_arguments
+ (identifier)))
+ (argument_list
+ (traits_expression
+ (trait)
+ (identifier)))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (identifier)
+ (literal
+ (nil)))))))
diff --git a/tools/tree-sitter/test/corpus/noreturn_procedure.txt b/tools/tree-sitter/test/corpus/noreturn_procedure.txt
new file mode 100644
index 0000000..8f8466b
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/noreturn_procedure.txt
@@ -0,0 +1,23 @@
+==================
+noreturn_procedure
+==================
+
+module
+
+proc exit() #{noreturn}
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (attribute
+ (identifier))
+ (procedure_body
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/opaque_type.txt b/tools/tree-sitter/test/corpus/opaque_type.txt
new file mode 100644
index 0000000..7182bb9
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/opaque_type.txt
@@ -0,0 +1,65 @@
+==================
+opaque_type
+==================
+
+program()
+type
+ Handle = extern
+ Alias = Handle
+
+var
+ handle: ^Handle := nil
+ alias: ^Alias
+
+proc take(h: ^Handle) -> ^Handle
+return h
+
+begin
+ alias := take(handle)
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier)))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))
+ (literal
+ (nil)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))
+ (pointer_type
+ (identifier))
+ (procedure_body
+ (return_clause
+ (identifier))))
+ (assignment_statement
+ (identifier)
+ (call_expression
+ (identifier)
+ (argument_list
+ (identifier))))))
diff --git a/tools/tree-sitter/test/corpus/pointer_cast.txt b/tools/tree-sitter/test/corpus/pointer_cast.txt
new file mode 100644
index 0000000..63a28ab
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/pointer_cast.txt
@@ -0,0 +1,38 @@
+==================
+pointer_cast
+==================
+
+program()
+var
+ c: ^Char := nil
+ p: Pointer := nil
+
+begin
+ p := c;
+ c := p
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))
+ (literal
+ (nil)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (literal
+ (nil))))
+ (assignment_statement
+ (identifier)
+ (identifier))
+ (assignment_statement
+ (identifier)
+ (identifier))))
diff --git a/tools/tree-sitter/test/corpus/pointer_const_conversion.txt b/tools/tree-sitter/test/corpus/pointer_const_conversion.txt
new file mode 100644
index 0000000..f134f00
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/pointer_const_conversion.txt
@@ -0,0 +1,32 @@
+==================
+pointer_const_conversion
+==================
+
+program()
+var
+ x: Int
+ p: ^const Int
+
+begin
+ p := @x
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (const_type
+ (identifier)))))
+ (assignment_statement
+ (identifier)
+ (unary_expression
+ (identifier)))))
diff --git a/tools/tree-sitter/test/corpus/pointer_subtraction.txt b/tools/tree-sitter/test/corpus/pointer_subtraction.txt
new file mode 100644
index 0000000..3e28de8
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/pointer_subtraction.txt
@@ -0,0 +1,32 @@
+==================
+pointer_subtraction
+==================
+
+module
+
+proc test(a, b: ^Char) -> Int
+return a - b
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))
+ (identifier)
+ (procedure_body
+ (return_clause
+ (binary_expression
+ (identifier)
+ (identifier)))))))
diff --git a/tools/tree-sitter/test/corpus/procedure_forward_reference.txt b/tools/tree-sitter/test/corpus/procedure_forward_reference.txt
new file mode 100644
index 0000000..2b8627f
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/procedure_forward_reference.txt
@@ -0,0 +1,40 @@
+==================
+procedure_forward_reference
+==================
+
+module
+
+proc first() -> Int
+return second() + 1
+
+proc second() -> Int
+return 41
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (identifier)
+ (procedure_body
+ (return_clause
+ (binary_expression
+ (call_expression
+ (identifier)
+ (argument_list))
+ (literal
+ (integer))))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (identifier)
+ (procedure_body
+ (return_clause
+ (literal
+ (integer)))))))
diff --git a/tools/tree-sitter/test/corpus/record_const_base.txt b/tools/tree-sitter/test/corpus/record_const_base.txt
new file mode 100644
index 0000000..e6f7cc6
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/record_const_base.txt
@@ -0,0 +1,76 @@
+==================
+record_const_base
+==================
+
+module
+
+type
+ B = record
+ x: Int
+ end
+ CB = const B
+ D = record(CB)
+ y: Int
+ end
+
+proc f()
+var
+ d: D
+begin
+ d.x := 1;
+ d.y := 2
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier)))
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (identifier)
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))))
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (procedure_body
+ (block
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (assignment_statement
+ (field_expression
+ (identifier)
+ (identifier))
+ (literal
+ (integer)))
+ (assignment_statement
+ (field_expression
+ (identifier)
+ (identifier))
+ (literal
+ (integer))))
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/self-referencing-record.txt b/tools/tree-sitter/test/corpus/self-referencing-record.txt
new file mode 100644
index 0000000..02948f3
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/self-referencing-record.txt
@@ -0,0 +1,27 @@
+==================
+self-referencing-record
+==================
+
+module
+
+type
+ R = record
+ field: ^R
+ end
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (type_section
+ (type_declaration
+ (identifier_definition
+ (identifier))
+ (record_type
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))))))
diff --git a/tools/tree-sitter/test/corpus/semicolon_parameter_separator.txt b/tools/tree-sitter/test/corpus/semicolon_parameter_separator.txt
new file mode 100644
index 0000000..f9c1f14
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/semicolon_parameter_separator.txt
@@ -0,0 +1,29 @@
+==================
+semicolon_parameter_separator
+==================
+
+module
+
+proc f(a: Int; b: Word)
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))
+ (field_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)))
+ (procedure_body
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/single_literals.txt b/tools/tree-sitter/test/corpus/single_literals.txt
new file mode 100644
index 0000000..0c7faab
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/single_literals.txt
@@ -0,0 +1,56 @@
+==================
+single_literals
+==================
+
+module
+
+proc f()
+var
+ x: const Single := 3.14f
+ y: const Single := 1e10f
+ z: const Single := 4.567e8f
+ t: const Single := 2.5E-3f
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (procedure_body
+ (block
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (float)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (float)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (float)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (float)))))
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/slice_index.txt b/tools/tree-sitter/test/corpus/slice_index.txt
new file mode 100644
index 0000000..5480038
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/slice_index.txt
@@ -0,0 +1,58 @@
+==================
+slice_index
+==================
+
+module
+
+proc f()
+var
+ a: []Int
+ x: Int
+ p: ^Int
+begin
+ x := a[1];
+ a := p[1u to 10u]
+return
+
+end.
+
+---
+
+(source_file
+ (module_declaration
+ (procedure_declaration
+ (identifier_definition
+ (identifier))
+ (parameter_list)
+ (procedure_body
+ (block
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (slice_type
+ (identifier)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (identifier))))
+ (assignment_statement
+ (identifier)
+ (index_expression
+ (identifier)
+ (literal
+ (integer))))
+ (assignment_statement
+ (identifier)
+ (slice_expression
+ (identifier)
+ (literal
+ (integer))
+ (literal
+ (integer)))))
+ (return_clause)))))
diff --git a/tools/tree-sitter/test/corpus/take_const_address.txt b/tools/tree-sitter/test/corpus/take_const_address.txt
new file mode 100644
index 0000000..d84e20f
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/take_const_address.txt
@@ -0,0 +1,35 @@
+==================
+take_const_address
+==================
+
+program()
+var
+ x: ^const Int
+ y: const Int := 1
+
+begin
+ x := @y
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (pointer_type
+ (const_type
+ (identifier))))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (const_type
+ (identifier))
+ (literal
+ (integer))))
+ (assignment_statement
+ (identifier)
+ (unary_expression
+ (identifier)))))
diff --git a/tools/tree-sitter/test/corpus/trailing_text.txt b/tools/tree-sitter/test/corpus/trailing_text.txt
new file mode 100644
index 0000000..bb39c86
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/trailing_text.txt
@@ -0,0 +1,16 @@
+==================
+trailing_text
+==================
+
+module
+
+end.
+
+Everything after the module terminator is ignored and is never scanned, so it
+does not have to be Elna: proc 1 2 3 "unterminated
+
+---
+
+(source_file
+ (module_declaration)
+ (trailing_content))
diff --git a/tools/tree-sitter/test/corpus/traits_size.txt b/tools/tree-sitter/test/corpus/traits_size.txt
new file mode 100644
index 0000000..f566e0c
--- /dev/null
+++ b/tools/tree-sitter/test/corpus/traits_size.txt
@@ -0,0 +1,93 @@
+==================
+traits_size
+==================
+
+program()
+var
+ i: Int := 1
+ s: Int := cast(#size(Int): Int)
+ a: [10]Int
+ as: Int := cast(#size([10]Int): Int)
+ p: Int := cast(#size(Pointer): Int)
+
+begin
+ assert(s > 0);
+ assert(as = 10 * cast(#size(Int): Int));
+ assert(p > 0)
+end.
+
+---
+
+(source_file
+ (program_declaration
+ (var_section
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (literal
+ (integer)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (cast_expression
+ (traits_expression
+ (trait)
+ (identifier))
+ (identifier)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (array_type
+ (literal
+ (integer))
+ (identifier)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (cast_expression
+ (traits_expression
+ (trait)
+ (array_type
+ (literal
+ (integer))
+ (identifier)))
+ (identifier)))
+ (variable_declaration
+ (identifier_definition
+ (identifier))
+ (identifier)
+ (cast_expression
+ (traits_expression
+ (trait)
+ (identifier))
+ (identifier))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (identifier)
+ (literal
+ (integer)))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (identifier)
+ (binary_expression
+ (literal
+ (integer))
+ (cast_expression
+ (traits_expression
+ (trait)
+ (identifier))
+ (identifier))))))
+ (call_expression
+ (identifier)
+ (argument_list
+ (binary_expression
+ (identifier)
+ (literal
+ (integer)))))))
diff --git a/tools/tree-sitter/tree-sitter.json b/tools/tree-sitter/tree-sitter.json
new file mode 100644
index 0000000..cfbe469
--- /dev/null
+++ b/tools/tree-sitter/tree-sitter.json
@@ -0,0 +1,18 @@
+{
+ "grammars": [
+ {
+ "name": "elna",
+ "camelcase": "Elna",
+ "scope": "source.elna",
+ "file-types": ["elna"],
+ "highlights": ["queries/elna/highlights.scm"],
+ "folds": ["queries/elna/folds.scm"],
+ "locals": ["queries/elna/locals.scm"],
+ "indents": ["queries/elna/indents.scm"]
+ }
+ ],
+ "metadata": {
+ "version": "0.1.0",
+ "license": "MPL-2.0"
+ }
+}