tree-sitter-glsl-spec 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/grammar.js ADDED
@@ -0,0 +1,2932 @@
1
+ /**
2
+ * @file GLSL grammar for tree-sitter
3
+ * @license MIT
4
+ */
5
+
6
+ // @ts-check
7
+
8
+ import {
9
+ INTERPOLATION_QUALIFIER_KEYWORDS,
10
+ PRECISION_QUALIFIER_KEYWORDS,
11
+ STORAGE_QUALIFIER_KEYWORDS,
12
+ TYPE_KEYWORDS,
13
+ flattenKeywordGroups,
14
+ } from './keywords.js';
15
+
16
+ import EXTENSIONS from './extensions.js';
17
+
18
+ /**
19
+ * Collect all `grammarExtension` rule builders from the extension registry
20
+ * into a single flat object suitable for `Object.assign()` into rules.
21
+ *
22
+ * @returns {RuleBuilders<string, never>}
23
+ */
24
+ function collectExtensionRules() {
25
+ /** @type {RuleBuilders<string, never>} */
26
+ const rules = {};
27
+ for (const ext of Object.values(EXTENSIONS)) {
28
+ if (ext.grammarExtension) {
29
+ Object.assign(rules, ext.grammarExtension);
30
+ }
31
+ }
32
+ return rules;
33
+ }
34
+
35
+ const EXTENSION_RULES = collectExtensionRules();
36
+
37
+ /**
38
+ * Conditionally include an extension rule in a `choice()`.
39
+ * Returns `[$.rule_name]` if the extension defines it, `[]` otherwise.
40
+ *
41
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
42
+ * @param {string} ruleName Name of the extension-defined rule.
43
+ * @returns {RuleOrLiteral[]}
44
+ */
45
+ function extRule($, ruleName) {
46
+ return ruleName in EXTENSION_RULES ? [$[ruleName]] : [];
47
+ }
48
+
49
+ /**
50
+ * Parser configuration flags.
51
+ *
52
+ * The first three extend the core GLSL grammar to handle real-world
53
+ * shader code (preprocessor structure, unexpanded macros, shared
54
+ * C++/GLSL headers). `ESSL` restricts it to the OpenGL ES subset.
55
+ *
56
+ * Document any rule drift caused by these flags at the affected rules.
57
+ */
58
+ const OPT = {
59
+ /**
60
+ * Parse preprocessor directives (`#define`, `#if`, `#pragma`, etc.)
61
+ * into structured nodes instead of opaque `preproc_call`.
62
+ */
63
+ MACRO_PARSING: true,
64
+ /**
65
+ * Accept unexpanded macros as qualifiers, types, and expressions.
66
+ * E.g. `COMPAT_PRECISION float x;`, `CONCAT(vec, 3) value;`.
67
+ */
68
+ MACRO_EXPANSION: true,
69
+ /**
70
+ * Recognize `#ifdef __cplusplus` / `#ifndef __STDC__` language
71
+ * guards and preserve non-GLSL branches for language injection.
72
+ */
73
+ MULTILINGUAL: true,
74
+ /**
75
+ * Restrict to the ESSL (OpenGL ES) subset: no aggregate
76
+ * initializers (`{ ... }`), no standalone `;` declarations.
77
+ */
78
+ ESSL: false,
79
+ };
80
+
81
+ /**
82
+ * Expression precedence levels (specification/chapters/operators.adoc).
83
+ * Higher number = higher precedence (binds tighter).
84
+ * Used for prec.left/prec.right within each production.
85
+ */
86
+ const PRECEDENCE = {
87
+ COMMA: 1, // spec 17 (lowest)
88
+ ASSIGNMENT: 2, // spec 16
89
+ CONDITIONAL: 3, // spec 15
90
+ LOGICAL_OR: 4, // spec 14
91
+ LOGICAL_XOR: 5, // spec 13
92
+ LOGICAL_AND: 6, // spec 12
93
+ INCLUSIVE_OR: 7, // spec 11
94
+ EXCLUSIVE_OR: 8, // spec 10
95
+ AND: 9, // spec 9
96
+ EQUALITY: 10, // spec 8
97
+ RELATIONAL: 11, // spec 7
98
+ SHIFT: 12, // spec 6
99
+ ADDITIVE: 13, // spec 5
100
+ MULTIPLICATIVE: 14, // spec 4
101
+ UNARY: 15, // spec 3
102
+ POSTFIX: 16, // spec 2
103
+ };
104
+
105
+ // ── Helpers ──────────────────────────────────────────────────────────────
106
+
107
+ /**
108
+ * Creates a preprocessor regex rule.
109
+ * Based on: ref/c_grammar.js preprocessor()
110
+ *
111
+ * @param {string} command Preprocessor directive name without `#`.
112
+ * @returns {RuleOrLiteral} Tree-sitter alias rule for the directive token.
113
+ */
114
+ function preprocessor(command) {
115
+ return alias(token(prec(1, new RegExp('#[ \\t]*' + command))), '#' + command);
116
+ }
117
+
118
+ /**
119
+ * Creates preproc_if / preproc_ifdef / preproc_else / preproc_elif rules
120
+ * parameterized by a suffix and content rule, so they can appear in different
121
+ * contexts (top-level, inside structs, inside statement lists).
122
+ * Based on: ref/c_grammar.js preprocIf()
123
+ *
124
+ * @param {string} suffix Rule-name suffix used for specialized variants.
125
+ * @param {($: any) => *} content Content builder for the directive body.
126
+ * @param {number} precedence Precedence used for the generated rules. Defaults to `0`.
127
+ * @returns {{[ruleName: string]: *}} Generated preprocessor rule builders.
128
+ */
129
+ function preprocIf(suffix, content, precedence = 0) {
130
+ /**
131
+ * Builds the `#else` / `#elif` alternative branch for a generated rule.
132
+ *
133
+ * @param {*} $ Grammar symbols.
134
+ * @returns {RuleOrLiteral} Alternative branch rule.
135
+ */
136
+ function alternativeBlock($) {
137
+ return choice(
138
+ suffix ?
139
+ alias($['preproc_else' + suffix], $.preproc_else) :
140
+ $.preproc_else,
141
+ suffix ?
142
+ alias($['preproc_elif' + suffix], $.preproc_elif) :
143
+ $.preproc_elif,
144
+ );
145
+ }
146
+
147
+ return {
148
+ /**
149
+ * @param {*} $ Grammar symbols.
150
+ * @returns {RuleOrLiteral} Rule definition.
151
+ */
152
+ ['preproc_if' + suffix]: ($) =>
153
+ prec(
154
+ precedence,
155
+ seq(
156
+ preprocessor('if'),
157
+ field('condition', $._preproc_expression),
158
+ token.immediate(/\r?\n/),
159
+ repeat(content($)),
160
+ field('alternative', optional(alternativeBlock($))),
161
+ preprocessor('endif'),
162
+ ),
163
+ ),
164
+
165
+ /**
166
+ * @param {*} $ Grammar symbols.
167
+ * @returns {RuleOrLiteral} Rule definition.
168
+ */
169
+ ['preproc_ifdef' + suffix]: ($) =>
170
+ prec(
171
+ precedence,
172
+ seq(
173
+ choice(preprocessor('ifdef'), preprocessor('ifndef')),
174
+ field('name', $.identifier),
175
+ repeat(content($)),
176
+ field('alternative', optional(alternativeBlock($))),
177
+ preprocessor('endif'),
178
+ ),
179
+ ),
180
+
181
+ /**
182
+ * @param {*} $ Grammar symbols.
183
+ * @returns {RuleOrLiteral} Rule definition.
184
+ */
185
+ ['preproc_else' + suffix]: ($) =>
186
+ prec(precedence, seq(preprocessor('else'), repeat(content($)))),
187
+
188
+ /**
189
+ * @param {*} $ Grammar symbols.
190
+ * @returns {RuleOrLiteral} Rule definition.
191
+ */
192
+ ['preproc_elif' + suffix]: ($) =>
193
+ prec(
194
+ precedence,
195
+ seq(
196
+ preprocessor('elif'),
197
+ field('condition', $._preproc_expression),
198
+ token.immediate(/\r?\n/),
199
+ repeat(content($)),
200
+ field('alternative', optional(alternativeBlock($))),
201
+ ),
202
+ ),
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Creates a rule to optionally match one or more of the rules
208
+ * separated by a comma.
209
+ *
210
+ * @param {*} rule Rule to repeat.
211
+ * @returns {RuleOrLiteral} Optional comma-separated rule sequence.
212
+ */
213
+ function commaSep(rule) {
214
+ return optional(commaSep1(rule));
215
+ }
216
+
217
+ /**
218
+ * Creates a rule to match one or more of the rules separated by a comma.
219
+ *
220
+ * @param {*} rule Rule to repeat.
221
+ * @returns {RuleOrLiteral} Comma-separated rule sequence.
222
+ */
223
+ function commaSep1(rule) {
224
+ return seq(rule, repeat(seq(',', rule)));
225
+ }
226
+
227
+ /**
228
+ * Builds a logical negation wrapper around a rule fragment.
229
+ *
230
+ * @param {*} expr Rule fragment to negate.
231
+ * @returns {RuleOrLiteral} Rule definition.
232
+ */
233
+ function not(expr) {
234
+ return seq('!', expr);
235
+ }
236
+
237
+ // ── OPT.MULTILINGUAL Helpers ─────────────────────────────────────────────
238
+
239
+ /**
240
+ * Language check: `MACRO`, `defined MACRO`, or `defined(MACRO)`.
241
+ *
242
+ * @param {*} macro The language macro rule.
243
+ * @returns {RuleOrLiteral} Rule definition.
244
+ */
245
+ function langCheck(macro) {
246
+ return choice(macro, seq('defined', macro), seq('defined', '(', macro, ')'));
247
+ }
248
+
249
+ /**
250
+ * Compound condition patterns: `A || B`, `(A || B)`, `A && !B`, etc.
251
+ *
252
+ * @param {*} primary The primary language check (e.g. cpp).
253
+ * @param {*} secondary The secondary language check (e.g. c).
254
+ * @returns {Array} Alternatives to spread into a `choice()`.
255
+ */
256
+ function langCompound(primary, secondary) {
257
+ return [
258
+ seq('(', primary, '||', secondary, ')'),
259
+ seq('(', secondary, '||', primary, ')'),
260
+ seq('(', primary, '&&', not(secondary), ')'),
261
+ seq('(', not(secondary), '&&', primary, ')'),
262
+ seq(primary, '||', secondary),
263
+ seq(secondary, '||', primary),
264
+ seq(primary, '&&', not(secondary)),
265
+ seq(not(secondary), '&&', primary),
266
+ ];
267
+ }
268
+
269
+ /**
270
+ * Positive language guard body: condition selects foreign code.
271
+ *
272
+ * @param {*} condition The positive condition rule.
273
+ * @param {*} codeBlock The foreign code block rule.
274
+ * @returns {RuleOrLiteral} Rule definition.
275
+ */
276
+ function langForeignBody(condition, codeBlock) {
277
+ return seq(
278
+ field('condition', condition),
279
+ token.immediate(/\r?\n/),
280
+ field('consequence', codeBlock),
281
+ );
282
+ }
283
+
284
+ /**
285
+ * Negated language guard body: condition excludes foreign code → body is GLSL.
286
+ *
287
+ * @param {*} $ Grammar symbols.
288
+ * @param {*} condition The negated condition rule.
289
+ * @param {*} foreignElse The foreign-language else rule.
290
+ * @returns {RuleOrLiteral} Rule definition.
291
+ */
292
+ function langGlslBody($, condition, foreignElse) {
293
+ return seq(
294
+ field('condition', condition),
295
+ repeat($._top_level_item),
296
+ field('alternative', optional(alias(foreignElse, $.preproc_else))),
297
+ );
298
+ }
299
+
300
+ /**
301
+ * `#ifdef` arm: foreign code in body, optional GLSL in `#else`.
302
+ *
303
+ * @param {*} $ Grammar symbols.
304
+ * @param {*} macro The language macro rule.
305
+ * @param {*} codeBlock The foreign code block rule.
306
+ * @param {*} glslElse The GLSL else rule.
307
+ * @returns {RuleOrLiteral} Rule definition.
308
+ */
309
+ function langIfdefArm($, macro, codeBlock, glslElse) {
310
+ return seq(
311
+ field('name', macro),
312
+ field('consequence', codeBlock),
313
+ field('alternative', optional(alias(glslElse, $.preproc_else))),
314
+ );
315
+ }
316
+
317
+ /**
318
+ * `#ifndef` arm: GLSL in body, optional foreign code in `#else`.
319
+ *
320
+ * @param {*} $ Grammar symbols.
321
+ * @param {*} macro The language macro rule.
322
+ * @param {*} foreignElse The foreign-language else rule.
323
+ * @returns {RuleOrLiteral} Rule definition.
324
+ */
325
+ function langIfndefArm($, macro, foreignElse) {
326
+ return seq(
327
+ field('name', macro),
328
+ repeat($._top_level_item),
329
+ field('alternative', optional(alias(foreignElse, $.preproc_else))),
330
+ );
331
+ }
332
+
333
+ export default grammar({
334
+ name: 'glsl',
335
+
336
+ conflicts: ($) => [
337
+ // `const foo;` — GLSL allows bare type declarations like `const vec3;`
338
+ // where `foo` is a _type_identifier starting a declarator_list, but
339
+ // also `type_qualifier identifier_list SEMICOLON` where `foo` is a
340
+ // plain identifier in identifier_list. Without a symbol table we
341
+ // can't tell which.
342
+ [$.identifier_list, $._type_identifier],
343
+
344
+ ...(OPT.MACRO_EXPANSION ?
345
+ [
346
+ // `MYMACRO vec3 x;` — `MYMACRO` could be a type name
347
+ // (constructor) or a macro_invocation standing in for a qualifier.
348
+ // Resolved by context after the next token.
349
+ [$._type_identifier, $.macro_invocation],
350
+
351
+ // `MYMACRO(args) x;` — `MYMACRO(args)` could be a function-like
352
+ // type constructor or a macro_invocation in type position.
353
+ [$._type_specifier_nonarray, $.macro_invocation],
354
+
355
+ // `foo[0]` — `foo` could be a variable (primary_expression) for
356
+ // subscript or a type name for an array constructor. GLSL has no
357
+ // way to distinguish TYPE_NAME from IDENTIFIER without a symbol
358
+ // table.
359
+ // `foo(` — `foo` could be a variable starting a function_call,
360
+ // a macro_invocation, a type identifier, or a macro_function_call.
361
+ [$._primary_expression, $._type_identifier, $.macro_invocation, $._macro_function_call],
362
+
363
+ // `foo(` — identifier could start a macro_invocation or be a
364
+ // primary_expression in various contexts.
365
+ [$._primary_expression, $.macro_invocation],
366
+ [$._primary_expression, $._type_identifier, $.macro_invocation],
367
+
368
+ // `QUAL1 QUAL2 type x;` — when multiple identifiers appear as
369
+ // qualifiers, the repeat in type_qualifier can't tell where
370
+ // qualifiers end and the type begins without lookahead.
371
+ [$.type_qualifier],
372
+
373
+ // `MYMACRO;` at statement level — could be an expression
374
+ // statement (identifier followed by `;`) or a macro_invocation
375
+ // used as a statement-like construct.
376
+ [$.expression_statement, $.macro_invocation],
377
+
378
+ // Combination of the above two: `MYMACRO(args)` at statement
379
+ // level is three-way ambiguous between expression statement,
380
+ // type specifier starting a declaration, and macro_invocation.
381
+ [
382
+ $.expression_statement,
383
+ $._type_specifier_nonarray,
384
+ $.macro_invocation,
385
+ ],
386
+ ] :
387
+ []),
388
+ ],
389
+
390
+ extras: ($) => [/\s|\\\r?\n/, $.comment],
391
+
392
+ inline: (_) => [],
393
+
394
+ supertypes: ($) => [$.statement, $.simple_statement, $.single_type_qualifier],
395
+
396
+ word: ($) => $.identifier,
397
+
398
+ rules: Object.assign(
399
+ {
400
+ // ── Top-level ── grammar.adoc ──────────────────────────────────────
401
+
402
+ /**
403
+ * ```bnf
404
+ * translation_unit :
405
+ * external_declaration
406
+ * translation_unit external_declaration
407
+ * ```
408
+ *
409
+ * Tree-sitter compromise: uses repeat() instead of left-recursive list.
410
+ *
411
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
412
+ * @returns {RuleOrLiteral} Rule definition.
413
+ */
414
+ translation_unit: ($) => repeat($._top_level_item),
415
+
416
+ /**
417
+ * ```bnf
418
+ * external_declaration :
419
+ * function_definition
420
+ * declaration
421
+ * SEMICOLON
422
+ * ```
423
+ *
424
+ * Hidden: transparent pass-through so top-level items appear
425
+ * directly under translation_unit.
426
+ *
427
+ * @param {GrammarSymbols<string>} $
428
+ * @returns {RuleOrLiteral} Rule definition.
429
+ */
430
+ _external_declaration: ($) =>
431
+ choice(
432
+ $.function_definition,
433
+ $.declaration,
434
+ ...(OPT.ESSL ? [] : [';']),
435
+ ),
436
+
437
+ /**
438
+ * ```bnf
439
+ * function_definition :
440
+ * function_prototype compound_statement_no_new_scope
441
+ * ```
442
+ *
443
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
444
+ * @returns {RuleOrLiteral} Rule definition.
445
+ */
446
+ function_definition: ($) =>
447
+ seq($.function_declarator, $.compound_statement),
448
+
449
+ // ── Expressions ── grammar.adoc ────────────────────────────────────
450
+
451
+ /**
452
+ * ```bnf
453
+ * primary_expression :
454
+ * variable_identifier
455
+ * INTCONSTANT
456
+ * UINTCONSTANT
457
+ * FLOATCONSTANT
458
+ * BOOLCONSTANT
459
+ * DOUBLECONSTANT
460
+ * LEFT_PAREN expression RIGHT_PAREN
461
+ * ```
462
+ *
463
+ * Hidden: transparent pass-through so bare identifiers/literals
464
+ * appear directly without a wrapper node.
465
+ * variable_identifier is inlined (just IDENTIFIER).
466
+ * BOOLCONSTANT is `true` or `false`.
467
+ *
468
+ * @param {GrammarSymbols<string>} $
469
+ */
470
+ _primary_expression: ($) =>
471
+ choice(
472
+ $.identifier,
473
+ $.number_literal,
474
+ $.bool_literal,
475
+ $.parenthesized_expression,
476
+ ...(OPT.MACRO_EXPANSION ?
477
+ [prec.dynamic(-1, $.macro_invocation)] :
478
+ []),
479
+ ),
480
+
481
+ /**
482
+ * ```bnf
483
+ * primary_expression : BOOLCONSTANT
484
+ * ```
485
+ *
486
+ * @param {GrammarSymbols<string>} _
487
+ */
488
+ bool_literal: (_) => choice('true', 'false'),
489
+
490
+ /**
491
+ * ```bnf
492
+ * primary_expression : LEFT_PAREN expression RIGHT_PAREN
493
+ * ```
494
+ *
495
+ * Tree-sitter compromise: extracted from _primary_expression as
496
+ * a named node for editor fold/highlight support.
497
+ *
498
+ * @param {GrammarSymbols<string>} $
499
+ */
500
+ parenthesized_expression: ($) => seq('(', $._expression, ')'),
501
+
502
+ /**
503
+ * ```bnf
504
+ * postfix_expression :
505
+ * primary_expression
506
+ * postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET
507
+ * function_call
508
+ * postfix_expression DOT FIELD_SELECTION
509
+ * postfix_expression INC_OP
510
+ * postfix_expression DEC_OP
511
+ * ```
512
+ *
513
+ * Hidden: transparent pass-through. Public sub-rules
514
+ * (subscript_expression, function_call, field_expression,
515
+ * update_expression) are extracted as named nodes.
516
+ * integer_expression is inlined (just expression).
517
+ *
518
+ * @param {GrammarSymbols<string>} $
519
+ */
520
+ _postfix_expression: ($) =>
521
+ choice(
522
+ $._primary_expression,
523
+ $.subscript_expression,
524
+ $.function_call,
525
+ $.field_expression,
526
+ alias($._postfix_update, $.update_expression),
527
+ ),
528
+
529
+ /**
530
+ * Hidden helper: postfix increment/decrement, aliased to
531
+ * update_expression in _postfix_expression.
532
+ *
533
+ * @param {GrammarSymbols<string>} $
534
+ */
535
+ _postfix_update: ($) =>
536
+ prec.left(
537
+ PRECEDENCE.POSTFIX,
538
+ seq(
539
+ field('argument', $._postfix_expression),
540
+ field('operator', choice('++', '--')),
541
+ ),
542
+ ),
543
+
544
+ /**
545
+ * ```bnf
546
+ * postfix_expression :
547
+ * postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET
548
+ * ```
549
+ *
550
+ * Tree-sitter compromise: extracted from _postfix_expression as a
551
+ * named node. integer_expression is just expression.
552
+ *
553
+ * @param {GrammarSymbols<string>} $
554
+ */
555
+ subscript_expression: ($) =>
556
+ prec(
557
+ PRECEDENCE.POSTFIX,
558
+ seq(
559
+ field('argument', $._postfix_expression),
560
+ '[',
561
+ field('index', $._expression),
562
+ ']',
563
+ ),
564
+ ),
565
+
566
+ /**
567
+ * ```bnf
568
+ * postfix_expression : postfix_expression DOT FIELD_SELECTION
569
+ * ```
570
+ *
571
+ * Tree-sitter compromise: extracted from _postfix_expression as a
572
+ * named node. FIELD_SELECTION is aliased to field_identifier.
573
+ *
574
+ * @param {GrammarSymbols<string>} $
575
+ */
576
+ field_expression: ($) =>
577
+ prec(
578
+ PRECEDENCE.POSTFIX,
579
+ seq(
580
+ field('argument', $._postfix_expression),
581
+ '.',
582
+ field('field', alias($.identifier, $.field_identifier)),
583
+ ),
584
+ ),
585
+
586
+ /**
587
+ * ```bnf
588
+ * function_call : function_call_or_method
589
+ *
590
+ * function_call_or_method : function_call_generic
591
+ *
592
+ * function_call_generic :
593
+ * function_call_header_with_parameters RIGHT_PAREN
594
+ * function_call_header_no_parameters RIGHT_PAREN
595
+ *
596
+ * function_call_header_no_parameters :
597
+ * function_call_header VOID
598
+ * function_call_header
599
+ *
600
+ * function_call_header_with_parameters :
601
+ * function_call_header assignment_expression
602
+ * function_call_header_with_parameters COMMA assignment_expression
603
+ *
604
+ * function_call_header :
605
+ * function_identifier LEFT_PAREN
606
+ *
607
+ * function_identifier :
608
+ * type_specifier
609
+ * postfix_expression
610
+ * ```
611
+ *
612
+ * Tree-sitter compromise: the 6-level spec chain
613
+ * (function_call → function_call_or_method → function_call_generic →
614
+ * function_call_header_* → function_call_header → function_identifier)
615
+ * is collapsed into a single function_call node.
616
+ *
617
+ * @param {GrammarSymbols<string>} $
618
+ */
619
+ function_call: ($) =>
620
+ prec.dynamic(
621
+ 1,
622
+ prec(
623
+ PRECEDENCE.POSTFIX,
624
+ seq(
625
+ field(
626
+ 'function',
627
+ choice($.type_specifier, $._postfix_expression),
628
+ ),
629
+ '(',
630
+ optional(field('arguments', $.argument_list)),
631
+ ')',
632
+ ),
633
+ ),
634
+ ),
635
+
636
+ /**
637
+ * Tree-sitter compromise: preserves the spec's `void` no-argument form.
638
+ *
639
+ * @param {GrammarSymbols<string>} $
640
+ */
641
+ argument_list: ($) => choice('void', commaSep1($._assignment_expression)),
642
+
643
+ /**
644
+ * ```bnf
645
+ * unary_expression :
646
+ * postfix_expression
647
+ * INC_OP unary_expression
648
+ * DEC_OP unary_expression
649
+ * unary_operator unary_expression
650
+ *
651
+ * unary_operator :
652
+ * PLUS | DASH | BANG | TILDE
653
+ * ```
654
+ *
655
+ * Hidden: transparent pass-through. Public sub-rules
656
+ * (unary_expression for operators, update_expression for ++/--)
657
+ * are extracted as named nodes.
658
+ *
659
+ * @param {GrammarSymbols<string>} $
660
+ */
661
+ _unary_expression: ($) =>
662
+ choice(
663
+ $._postfix_expression,
664
+ alias($._prefix_update, $.update_expression),
665
+ $.unary_expression,
666
+ ),
667
+
668
+ /**
669
+ * Hidden helper: prefix increment/decrement, aliased to
670
+ * update_expression in _unary_expression.
671
+ *
672
+ * @param {GrammarSymbols<string>} $
673
+ */
674
+ _prefix_update: ($) =>
675
+ prec.right(
676
+ PRECEDENCE.UNARY,
677
+ seq(
678
+ field('operator', choice('++', '--')),
679
+ field('argument', $._unary_expression),
680
+ ),
681
+ ),
682
+
683
+ /**
684
+ * Public sub-rule of _unary_expression: only emitted when a
685
+ * unary operator (+, -, !, ~) is present.
686
+ *
687
+ * @param {GrammarSymbols<string>} $
688
+ */
689
+ unary_expression: ($) =>
690
+ prec.right(
691
+ PRECEDENCE.UNARY,
692
+ seq(
693
+ field('operator', choice('+', '-', '!', '~')),
694
+ field('argument', $._unary_expression),
695
+ ),
696
+ ),
697
+
698
+ /**
699
+ * Public alias target for both prefix and postfix ++/--
700
+ * (via _postfix_update and _prefix_update).
701
+ *
702
+ * @param {GrammarSymbols<string>} $
703
+ */
704
+ update_expression: ($) => choice($._postfix_update, $._prefix_update),
705
+
706
+ /**
707
+ * ```bnf
708
+ * multiplicative_expression :
709
+ * unary_expression
710
+ * multiplicative_expression STAR unary_expression
711
+ * multiplicative_expression SLASH unary_expression
712
+ * multiplicative_expression PERCENT unary_expression
713
+ * ```
714
+ *
715
+ * @param {GrammarSymbols<string>} $
716
+ */
717
+ _multiplicative_expression: ($) =>
718
+ choice(
719
+ $._unary_expression,
720
+ alias($._multiplicative_operation, $.binary_expression),
721
+ ),
722
+
723
+ /**
724
+ * Hidden helper for multiplicative binary operations.
725
+ *
726
+ * @param {GrammarSymbols<string>} $
727
+ */
728
+ _multiplicative_operation: ($) =>
729
+ prec.left(
730
+ PRECEDENCE.MULTIPLICATIVE,
731
+ seq(
732
+ field('left', $._multiplicative_expression),
733
+ field('operator', choice('*', '/', '%')),
734
+ field('right', $._unary_expression),
735
+ ),
736
+ ),
737
+
738
+ /**
739
+ * ```bnf
740
+ * additive_expression :
741
+ * multiplicative_expression
742
+ * additive_expression PLUS multiplicative_expression
743
+ * additive_expression DASH multiplicative_expression
744
+ * ```
745
+ *
746
+ * @param {GrammarSymbols<string>} $
747
+ */
748
+ _additive_expression: ($) =>
749
+ choice(
750
+ $._multiplicative_expression,
751
+ alias($._additive_operation, $.binary_expression),
752
+ ),
753
+
754
+ /**
755
+ * Hidden helper for additive binary operations.
756
+ *
757
+ * @param {GrammarSymbols<string>} $
758
+ */
759
+ _additive_operation: ($) =>
760
+ prec.left(
761
+ PRECEDENCE.ADDITIVE,
762
+ seq(
763
+ field('left', $._additive_expression),
764
+ field('operator', choice('+', '-')),
765
+ field('right', $._multiplicative_expression),
766
+ ),
767
+ ),
768
+
769
+ /**
770
+ * ```bnf
771
+ * shift_expression :
772
+ * additive_expression
773
+ * shift_expression LEFT_OP additive_expression
774
+ * shift_expression RIGHT_OP additive_expression
775
+ * ```
776
+ *
777
+ * @param {GrammarSymbols<string>} $
778
+ */
779
+ _shift_expression: ($) =>
780
+ choice(
781
+ $._additive_expression,
782
+ alias($._shift_operation, $.binary_expression),
783
+ ),
784
+
785
+ /**
786
+ * Hidden helper for shift binary operations.
787
+ *
788
+ * @param {GrammarSymbols<string>} $
789
+ */
790
+ _shift_operation: ($) =>
791
+ prec.left(
792
+ PRECEDENCE.SHIFT,
793
+ seq(
794
+ field('left', $._shift_expression),
795
+ field('operator', choice('<<', '>>')),
796
+ field('right', $._additive_expression),
797
+ ),
798
+ ),
799
+
800
+ /**
801
+ * ```bnf
802
+ * relational_expression :
803
+ * shift_expression
804
+ * relational_expression LEFT_ANGLE shift_expression
805
+ * relational_expression RIGHT_ANGLE shift_expression
806
+ * relational_expression LE_OP shift_expression
807
+ * relational_expression GE_OP shift_expression
808
+ * ```
809
+ *
810
+ * @param {GrammarSymbols<string>} $
811
+ */
812
+ _relational_expression: ($) =>
813
+ choice(
814
+ $._shift_expression,
815
+ alias($._relational_operation, $.binary_expression),
816
+ ),
817
+
818
+ /**
819
+ * Hidden helper for relational binary operations.
820
+ *
821
+ * @param {GrammarSymbols<string>} $
822
+ */
823
+ _relational_operation: ($) =>
824
+ prec.left(
825
+ PRECEDENCE.RELATIONAL,
826
+ seq(
827
+ field('left', $._relational_expression),
828
+ field('operator', choice('<', '>', '<=', '>=')),
829
+ field('right', $._shift_expression),
830
+ ),
831
+ ),
832
+
833
+ /**
834
+ * ```bnf
835
+ * equality_expression :
836
+ * relational_expression
837
+ * equality_expression EQ_OP relational_expression
838
+ * equality_expression NE_OP relational_expression
839
+ * ```
840
+ *
841
+ * @param {GrammarSymbols<string>} $
842
+ */
843
+ _equality_expression: ($) =>
844
+ choice(
845
+ $._relational_expression,
846
+ alias($._equality_operation, $.binary_expression),
847
+ ),
848
+
849
+ /**
850
+ * Hidden helper for equality binary operations.
851
+ *
852
+ * @param {GrammarSymbols<string>} $
853
+ */
854
+ _equality_operation: ($) =>
855
+ prec.left(
856
+ PRECEDENCE.EQUALITY,
857
+ seq(
858
+ field('left', $._equality_expression),
859
+ field('operator', choice('==', '!=')),
860
+ field('right', $._relational_expression),
861
+ ),
862
+ ),
863
+
864
+ /**
865
+ * ```bnf
866
+ * and_expression :
867
+ * equality_expression
868
+ * and_expression AMPERSAND equality_expression
869
+ * ```
870
+ *
871
+ * @param {GrammarSymbols<string>} $
872
+ */
873
+ _and_expression: ($) =>
874
+ choice(
875
+ $._equality_expression,
876
+ alias($._and_operation, $.binary_expression),
877
+ ),
878
+
879
+ /**
880
+ * Hidden helper for bitwise-and operations.
881
+ *
882
+ * @param {GrammarSymbols<string>} $
883
+ */
884
+ _and_operation: ($) =>
885
+ prec.left(
886
+ PRECEDENCE.AND,
887
+ seq(
888
+ field('left', $._and_expression),
889
+ field('operator', '&'),
890
+ field('right', $._equality_expression),
891
+ ),
892
+ ),
893
+
894
+ /**
895
+ * ```bnf
896
+ * exclusive_or_expression :
897
+ * and_expression
898
+ * exclusive_or_expression CARET and_expression
899
+ * ```
900
+ *
901
+ * @param {GrammarSymbols<string>} $
902
+ */
903
+ _exclusive_or_expression: ($) =>
904
+ choice(
905
+ $._and_expression,
906
+ alias($._exclusive_or_operation, $.binary_expression),
907
+ ),
908
+
909
+ /**
910
+ * Hidden helper for bitwise-xor operations.
911
+ *
912
+ * @param {GrammarSymbols<string>} $
913
+ */
914
+ _exclusive_or_operation: ($) =>
915
+ prec.left(
916
+ PRECEDENCE.EXCLUSIVE_OR,
917
+ seq(
918
+ field('left', $._exclusive_or_expression),
919
+ field('operator', '^'),
920
+ field('right', $._and_expression),
921
+ ),
922
+ ),
923
+
924
+ /**
925
+ * ```bnf
926
+ * inclusive_or_expression :
927
+ * exclusive_or_expression
928
+ * inclusive_or_expression VERTICAL_BAR exclusive_or_expression
929
+ * ```
930
+ *
931
+ * @param {GrammarSymbols<string>} $
932
+ */
933
+ _inclusive_or_expression: ($) =>
934
+ choice(
935
+ $._exclusive_or_expression,
936
+ alias($._inclusive_or_operation, $.binary_expression),
937
+ ),
938
+
939
+ /**
940
+ * Hidden helper for bitwise-or operations.
941
+ *
942
+ * @param {GrammarSymbols<string>} $
943
+ */
944
+ _inclusive_or_operation: ($) =>
945
+ prec.left(
946
+ PRECEDENCE.INCLUSIVE_OR,
947
+ seq(
948
+ field('left', $._inclusive_or_expression),
949
+ field('operator', '|'),
950
+ field('right', $._exclusive_or_expression),
951
+ ),
952
+ ),
953
+
954
+ /**
955
+ * ```bnf
956
+ * logical_and_expression :
957
+ * inclusive_or_expression
958
+ * logical_and_expression AND_OP inclusive_or_expression
959
+ * ```
960
+ *
961
+ * @param {GrammarSymbols<string>} $
962
+ */
963
+ _logical_and_expression: ($) =>
964
+ choice(
965
+ $._inclusive_or_expression,
966
+ alias($._logical_and_operation, $.binary_expression),
967
+ ),
968
+
969
+ /**
970
+ * Hidden helper for logical-and operations.
971
+ *
972
+ * @param {GrammarSymbols<string>} $
973
+ */
974
+ _logical_and_operation: ($) =>
975
+ prec.left(
976
+ PRECEDENCE.LOGICAL_AND,
977
+ seq(
978
+ field('left', $._logical_and_expression),
979
+ field('operator', '&&'),
980
+ field('right', $._inclusive_or_expression),
981
+ ),
982
+ ),
983
+
984
+ /**
985
+ * ```bnf
986
+ * logical_xor_expression :
987
+ * logical_and_expression
988
+ * logical_xor_expression XOR_OP logical_and_expression
989
+ * ```
990
+ *
991
+ * @param {GrammarSymbols<string>} $
992
+ */
993
+ _logical_xor_expression: ($) =>
994
+ choice(
995
+ $._logical_and_expression,
996
+ alias($._logical_xor_operation, $.binary_expression),
997
+ ),
998
+
999
+ /**
1000
+ * Hidden helper for logical-xor operations.
1001
+ *
1002
+ * @param {GrammarSymbols<string>} $
1003
+ */
1004
+ _logical_xor_operation: ($) =>
1005
+ prec.left(
1006
+ PRECEDENCE.LOGICAL_XOR,
1007
+ seq(
1008
+ field('left', $._logical_xor_expression),
1009
+ field('operator', '^^'),
1010
+ field('right', $._logical_and_expression),
1011
+ ),
1012
+ ),
1013
+
1014
+ /**
1015
+ * ```bnf
1016
+ * logical_or_expression :
1017
+ * logical_xor_expression
1018
+ * logical_or_expression OR_OP logical_xor_expression
1019
+ * ```
1020
+ *
1021
+ * @param {GrammarSymbols<string>} $
1022
+ */
1023
+ _logical_or_expression: ($) =>
1024
+ choice(
1025
+ $._logical_xor_expression,
1026
+ alias($._logical_or_operation, $.binary_expression),
1027
+ ),
1028
+
1029
+ /**
1030
+ * Hidden helper for logical-or operations.
1031
+ *
1032
+ * @param {GrammarSymbols<string>} $
1033
+ */
1034
+ _logical_or_operation: ($) =>
1035
+ prec.left(
1036
+ PRECEDENCE.LOGICAL_OR,
1037
+ seq(
1038
+ field('left', $._logical_or_expression),
1039
+ field('operator', '||'),
1040
+ field('right', $._logical_xor_expression),
1041
+ ),
1042
+ ),
1043
+
1044
+ /**
1045
+ * Tree-sitter compromise: the spec's binary precedence ladder is parsed
1046
+ * via hidden rules but emitted as one consumer-facing node.
1047
+ *
1048
+ * @param {GrammarSymbols<string>} $
1049
+ */
1050
+ binary_expression: ($) =>
1051
+ choice(
1052
+ $._multiplicative_operation,
1053
+ $._additive_operation,
1054
+ $._shift_operation,
1055
+ $._relational_operation,
1056
+ $._equality_operation,
1057
+ $._and_operation,
1058
+ $._exclusive_or_operation,
1059
+ $._inclusive_or_operation,
1060
+ $._logical_and_operation,
1061
+ $._logical_xor_operation,
1062
+ $._logical_or_operation,
1063
+ ),
1064
+
1065
+ /**
1066
+ * ```bnf
1067
+ * conditional_expression :
1068
+ * logical_or_expression
1069
+ * logical_or_expression QUESTION expression COLON
1070
+ * assignment_expression
1071
+ * ```
1072
+ *
1073
+ * @param {GrammarSymbols<string>} $
1074
+ */
1075
+ _conditional_expression: ($) =>
1076
+ choice($._logical_or_expression, $.conditional_expression),
1077
+
1078
+ /**
1079
+ * Tree-sitter compromise: visible conditional node only when `?:`
1080
+ * is present; the spec base case stays hidden in _conditional_expression.
1081
+ *
1082
+ * @param {GrammarSymbols<string>} $
1083
+ */
1084
+ conditional_expression: ($) =>
1085
+ prec.right(
1086
+ PRECEDENCE.CONDITIONAL,
1087
+ seq(
1088
+ field('condition', $._logical_or_expression),
1089
+ '?',
1090
+ field('consequence', $._expression),
1091
+ ':',
1092
+ field('alternative', $._assignment_expression),
1093
+ ),
1094
+ ),
1095
+
1096
+ /**
1097
+ * ```bnf
1098
+ * assignment_expression :
1099
+ * conditional_expression
1100
+ * unary_expression assignment_operator assignment_expression
1101
+ * ```
1102
+ *
1103
+ * ```bnf
1104
+ * assignment_operator :
1105
+ * EQUAL | MUL_ASSIGN | DIV_ASSIGN | MOD_ASSIGN |
1106
+ * ADD_ASSIGN | SUB_ASSIGN | LEFT_ASSIGN | RIGHT_ASSIGN |
1107
+ * AND_ASSIGN | XOR_ASSIGN | OR_ASSIGN
1108
+ * ```
1109
+ *
1110
+ * @param {GrammarSymbols<string>} $
1111
+ */
1112
+ _assignment_expression: ($) =>
1113
+ choice($._conditional_expression, $.assignment_expression),
1114
+
1115
+ /**
1116
+ * Tree-sitter compromise: visible assignment node only when an
1117
+ * assignment operator is present; the spec base case stays hidden
1118
+ * in _assignment_expression.
1119
+ *
1120
+ * @param {GrammarSymbols<string>} $
1121
+ */
1122
+ assignment_expression: ($) =>
1123
+ prec.right(
1124
+ PRECEDENCE.ASSIGNMENT,
1125
+ seq(
1126
+ field('left', $._unary_expression),
1127
+ field('operator', $.assignment_operator),
1128
+ field('right', $._assignment_expression),
1129
+ ),
1130
+ ),
1131
+
1132
+ /**
1133
+ * ```bnf
1134
+ * assignment_operator :
1135
+ * EQUAL | MUL_ASSIGN | DIV_ASSIGN | MOD_ASSIGN |
1136
+ * ADD_ASSIGN | SUB_ASSIGN | LEFT_ASSIGN | RIGHT_ASSIGN |
1137
+ * AND_ASSIGN | XOR_ASSIGN | OR_ASSIGN
1138
+ * ```
1139
+ *
1140
+ * @param {GrammarSymbols<string>} _
1141
+ */
1142
+ assignment_operator: (_) =>
1143
+ choice(
1144
+ '=',
1145
+ '*=',
1146
+ '/=',
1147
+ '%=',
1148
+ '+=',
1149
+ '-=',
1150
+ '<<=',
1151
+ '>>=',
1152
+ '&=',
1153
+ '^=',
1154
+ '|=',
1155
+ ),
1156
+
1157
+ /**
1158
+ * ```bnf
1159
+ * expression :
1160
+ * assignment_expression
1161
+ * expression COMMA assignment_expression
1162
+ * ```
1163
+ *
1164
+ * Tree-sitter compromise: _expression is hidden; the comma-operator
1165
+ * form surfaces as comma_expression (visible).
1166
+ *
1167
+ * @param {GrammarSymbols<string>} $
1168
+ */
1169
+ _expression: ($) => choice($._assignment_expression, $.comma_expression),
1170
+
1171
+ /**
1172
+ * ```bnf
1173
+ * expression : expression COMMA assignment_expression
1174
+ * ```
1175
+ *
1176
+ * Tree-sitter compromise: the comma-operator form is a separate
1177
+ * visible node; the single-assignment form is hidden in _expression.
1178
+ *
1179
+ * @param {GrammarSymbols<string>} $
1180
+ */
1181
+ comma_expression: ($) =>
1182
+ prec.left(
1183
+ PRECEDENCE.COMMA,
1184
+ seq(
1185
+ field('left', $._expression),
1186
+ ',',
1187
+ field('right', $._assignment_expression),
1188
+ ),
1189
+ ),
1190
+
1191
+ /**
1192
+ * ```bnf
1193
+ * constant_expression : conditional_expression
1194
+ * ```
1195
+ *
1196
+ * Hidden: 1:1 pass-through so expressions appear directly in
1197
+ * layout arguments and array sizes.
1198
+ *
1199
+ * @param {GrammarSymbols<string>} $
1200
+ */
1201
+ _constant_expression: ($) => $._conditional_expression,
1202
+
1203
+ // ── Types ── grammar.adoc ──────────────────────────────────────────
1204
+
1205
+ /**
1206
+ * ```bnf
1207
+ * fully_specified_type :
1208
+ * type_specifier
1209
+ * type_qualifier type_specifier
1210
+ * ```
1211
+ *
1212
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1213
+ * @returns {RuleOrLiteral} Rule definition.
1214
+ */
1215
+ type: ($) =>
1216
+ choice(seq($.type_qualifier, $.type_specifier), $.type_specifier),
1217
+
1218
+ /**
1219
+ * ```bnf
1220
+ * type_qualifier :
1221
+ * single_type_qualifier
1222
+ * type_qualifier single_type_qualifier
1223
+ * ```
1224
+ *
1225
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1226
+ * @returns {RuleOrLiteral} Rule definition.
1227
+ */
1228
+ type_qualifier: ($) => repeat1($.single_type_qualifier),
1229
+
1230
+ /**
1231
+ * ```bnf
1232
+ * single_type_qualifier :
1233
+ * storage_qualifier
1234
+ * layout_qualifier
1235
+ * precision_qualifier
1236
+ * interpolation_qualifier
1237
+ * invariant_qualifier
1238
+ * precise_qualifier
1239
+ * ```
1240
+ *
1241
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1242
+ * @returns {RuleOrLiteral} Rule definition.
1243
+ */
1244
+ single_type_qualifier: ($) =>
1245
+ choice(
1246
+ $.storage_qualifier,
1247
+ $.layout_qualifier,
1248
+ $.precision_qualifier,
1249
+ $.interpolation_qualifier,
1250
+ $.invariant_qualifier,
1251
+ $.precise_qualifier,
1252
+ ...(OPT.MACRO_EXPANSION ?
1253
+ [prec.dynamic(-1, $.macro_invocation)] :
1254
+ []),
1255
+ ),
1256
+
1257
+ /**
1258
+ * ```bnf
1259
+ * storage_qualifier :
1260
+ * CONST | IN | OUT | INOUT | CENTROID | PATCH | SAMPLE |
1261
+ * UNIFORM | BUFFER | SHARED | COHERENT | VOLATILE |
1262
+ * RESTRICT | READONLY | WRITEONLY |
1263
+ * SUBROUTINE | SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN
1264
+ * ```
1265
+ *
1266
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1267
+ * @returns {RuleOrLiteral} Rule definition.
1268
+ */
1269
+ storage_qualifier: ($) =>
1270
+ choice(
1271
+ ...flattenKeywordGroups(STORAGE_QUALIFIER_KEYWORDS),
1272
+ seq('subroutine', '(', $.type_name_list, ')'),
1273
+ ),
1274
+
1275
+ /**
1276
+ * ```bnf
1277
+ * type_name_list :
1278
+ * TYPE_NAME
1279
+ * type_name_list COMMA TYPE_NAME
1280
+ * ```
1281
+ *
1282
+ * GLSL-only (subroutine).
1283
+ *
1284
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1285
+ * @returns {RuleOrLiteral} Rule definition.
1286
+ */
1287
+ type_name_list: ($) => commaSep1($.identifier),
1288
+
1289
+ /**
1290
+ * ```bnf
1291
+ * layout_qualifier :
1292
+ * LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN
1293
+ * ```
1294
+ *
1295
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1296
+ * @returns {RuleOrLiteral} Rule definition.
1297
+ */
1298
+ layout_qualifier: ($) => seq('layout', '(', $._layout_arguments, ')'),
1299
+
1300
+ /**
1301
+ * ```bnf
1302
+ * layout_qualifier_id_list :
1303
+ * layout_qualifier_id
1304
+ * layout_qualifier_id_list COMMA layout_qualifier_id
1305
+ * ```
1306
+ *
1307
+ * Hidden: transparent pass-through so layout_argument nodes
1308
+ * appear directly inside layout_qualifier.
1309
+ *
1310
+ * @param {GrammarSymbols<string>} $
1311
+ */
1312
+ _layout_arguments: ($) => commaSep1($.layout_argument),
1313
+
1314
+ /**
1315
+ * ```bnf
1316
+ * layout_qualifier_id :
1317
+ * IDENTIFIER
1318
+ * IDENTIFIER EQUAL constant_expression
1319
+ * SHARED
1320
+ * ```
1321
+ *
1322
+ * @param {GrammarSymbols<string>} $
1323
+ */
1324
+ layout_argument: ($) =>
1325
+ choice(
1326
+ seq(
1327
+ field('name', $.identifier),
1328
+ '=',
1329
+ field('value', $._constant_expression),
1330
+ ),
1331
+ $.identifier,
1332
+ 'shared',
1333
+ ),
1334
+
1335
+ /**
1336
+ * ```bnf
1337
+ * precision_qualifier :
1338
+ * HIGH_PRECISION | MEDIUM_PRECISION | LOW_PRECISION
1339
+ * ```
1340
+ *
1341
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
1342
+ * @returns {RuleOrLiteral} Rule definition.
1343
+ */
1344
+ precision_qualifier: (_) =>
1345
+ choice(...flattenKeywordGroups(PRECISION_QUALIFIER_KEYWORDS)),
1346
+
1347
+ /**
1348
+ * ```bnf
1349
+ * interpolation_qualifier :
1350
+ * SMOOTH | FLAT | NOPERSPECTIVE
1351
+ * ```
1352
+ *
1353
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
1354
+ * @returns {RuleOrLiteral} Rule definition.
1355
+ */
1356
+ interpolation_qualifier: (_) =>
1357
+ choice(...flattenKeywordGroups(INTERPOLATION_QUALIFIER_KEYWORDS)),
1358
+
1359
+ /**
1360
+ * ```bnf
1361
+ * invariant_qualifier : INVARIANT
1362
+ * ```
1363
+ *
1364
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
1365
+ * @returns {RuleOrLiteral} Rule definition.
1366
+ */
1367
+ invariant_qualifier: (_) => 'invariant',
1368
+
1369
+ /**
1370
+ * ```bnf
1371
+ * precise_qualifier : PRECISE
1372
+ * ```
1373
+ *
1374
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
1375
+ * @returns {RuleOrLiteral} Rule definition.
1376
+ */
1377
+ precise_qualifier: (_) => 'precise',
1378
+
1379
+ /**
1380
+ * ```bnf
1381
+ * type_specifier :
1382
+ * type_specifier_nonarray
1383
+ * type_specifier_nonarray array_specifier
1384
+ * ```
1385
+ *
1386
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1387
+ * @returns {RuleOrLiteral} Rule definition.
1388
+ */
1389
+ type_specifier: ($) =>
1390
+ seq($._type_specifier_nonarray, optional($.array_specifier)),
1391
+
1392
+ /**
1393
+ * ```bnf
1394
+ * array_specifier :
1395
+ * LEFT_BRACKET RIGHT_BRACKET
1396
+ * LEFT_BRACKET conditional_expression RIGHT_BRACKET
1397
+ * array_specifier LEFT_BRACKET RIGHT_BRACKET
1398
+ * array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET
1399
+ * ```
1400
+ *
1401
+ * Tree-sitter compromise: uses repeat1() instead of left-recursion.
1402
+ *
1403
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1404
+ * @returns {RuleOrLiteral} Rule definition.
1405
+ */
1406
+ array_specifier: ($) =>
1407
+ repeat1(seq('[', optional($._conditional_expression), ']')),
1408
+
1409
+ /**
1410
+ * ```bnf
1411
+ * type_specifier_nonarray :
1412
+ * VOID | FLOAT | DOUBLE | INT | UINT | BOOL |
1413
+ * VEC2..4 | DVEC2..4 | BVEC2..4 | IVEC2..4 | UVEC2..4 |
1414
+ * MAT2..4 | MAT2X2..MAT4X4 | DMAT2..4 | DMAT2X2..DMAT4X4 |
1415
+ * ATOMIC_UINT | <sampler types> | <image types> |
1416
+ * struct_specifier | TYPE_NAME
1417
+ * ```
1418
+ *
1419
+ * TYPE_NAME is an identifier that names a user-defined type.
1420
+ * Tree-sitter cannot distinguish TYPE_NAME from IDENTIFIER
1421
+ * semantically; here it is accepted as $._type_identifier.
1422
+ *
1423
+ * Extension: when OPT.MACRO_EXPANSION is enabled, a function-like macro
1424
+ * invocation is also accepted in type position so unexpanded forms like
1425
+ * `CONCAT(vec, 3)` can parse structurally. Bare identifiers remain
1426
+ * `type_identifier` because they are indistinguishable from user-defined
1427
+ * type names at parse time.
1428
+ *
1429
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1430
+ * @returns {RuleOrLiteral} Rule definition.
1431
+ */
1432
+ _type_specifier_nonarray: ($) =>
1433
+ choice(
1434
+ ...flattenKeywordGroups(TYPE_KEYWORDS),
1435
+ $.struct_specifier,
1436
+ prec.dynamic(-1, $._type_identifier),
1437
+ ...(OPT.MACRO_EXPANSION ?
1438
+ [
1439
+ prec.dynamic(
1440
+ -3,
1441
+ alias($._macro_function_call, $.macro_invocation),
1442
+ ),
1443
+ ] :
1444
+ []),
1445
+ ),
1446
+
1447
+ // ── Structs ── grammar.adoc ────────────────────────────────────────
1448
+
1449
+ /**
1450
+ * ```bnf
1451
+ * struct_specifier :
1452
+ * STRUCT IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE
1453
+ * STRUCT LEFT_BRACE struct_declaration_list RIGHT_BRACE
1454
+ * ```
1455
+ *
1456
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1457
+ * @returns {RuleOrLiteral} Rule definition.
1458
+ */
1459
+ struct_specifier: ($) =>
1460
+ seq(
1461
+ 'struct',
1462
+ optional(field('name', $.identifier)),
1463
+ '{',
1464
+ $.field_declaration_list,
1465
+ '}',
1466
+ ),
1467
+
1468
+ /**
1469
+ * ```bnf
1470
+ * struct_declaration_list :
1471
+ * struct_declaration
1472
+ * struct_declaration_list struct_declaration
1473
+ * ```
1474
+ *
1475
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1476
+ * @returns {RuleOrLiteral} Rule definition.
1477
+ */
1478
+ field_declaration_list: ($) =>
1479
+ repeat1(
1480
+ choice(
1481
+ $.field_declaration,
1482
+ // Extension: preprocessor directives inside struct bodies
1483
+ $.preproc_call,
1484
+ ...(OPT.MACRO_PARSING ?
1485
+ [
1486
+ $.preproc_def,
1487
+ $.preproc_function_def,
1488
+ $.preproc_undef,
1489
+ alias($.preproc_if_in_struct_declaration, $.preproc_if),
1490
+ alias($.preproc_ifdef_in_struct_declaration, $.preproc_ifdef),
1491
+ ] :
1492
+ []),
1493
+ ),
1494
+ ),
1495
+
1496
+ /**
1497
+ * ```bnf
1498
+ * struct_declaration :
1499
+ * type_specifier struct_declarator_list SEMICOLON
1500
+ * type_qualifier type_specifier struct_declarator_list SEMICOLON
1501
+ * ```
1502
+ *
1503
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1504
+ * @returns {RuleOrLiteral} Rule definition.
1505
+ */
1506
+ field_declaration: ($) =>
1507
+ seq(
1508
+ optional($.type_qualifier),
1509
+ $.type_specifier,
1510
+ $.field_declarator_list,
1511
+ ';',
1512
+ ),
1513
+
1514
+ /**
1515
+ * ```bnf
1516
+ * struct_declarator_list :
1517
+ * struct_declarator
1518
+ * struct_declarator_list COMMA struct_declarator
1519
+ * ```
1520
+ *
1521
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1522
+ * @returns {RuleOrLiteral} Rule definition.
1523
+ */
1524
+ field_declarator_list: ($) => commaSep1($.field_declarator),
1525
+
1526
+ /**
1527
+ * ```bnf
1528
+ * struct_declarator :
1529
+ * IDENTIFIER
1530
+ * IDENTIFIER array_specifier
1531
+ * ```
1532
+ *
1533
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1534
+ * @returns {RuleOrLiteral} Rule definition.
1535
+ */
1536
+ field_declarator: ($) =>
1537
+ seq(field('name', $.identifier), optional($.array_specifier)),
1538
+
1539
+ // ── Declarations ── grammar.adoc ───────────────────────────────────
1540
+
1541
+ /**
1542
+ * ```bnf
1543
+ * declaration :
1544
+ * function_prototype SEMICOLON
1545
+ * init_declarator_list SEMICOLON
1546
+ * PRECISION precision_qualifier type_specifier SEMICOLON
1547
+ * type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list
1548
+ * RIGHT_BRACE SEMICOLON
1549
+ * type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list
1550
+ * RIGHT_BRACE IDENTIFIER SEMICOLON
1551
+ * type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list
1552
+ * RIGHT_BRACE IDENTIFIER array_specifier SEMICOLON
1553
+ * type_qualifier SEMICOLON
1554
+ * type_qualifier identifier_list SEMICOLON
1555
+ * ```
1556
+ *
1557
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1558
+ * @returns {RuleOrLiteral} Rule definition.
1559
+ */
1560
+ declaration: ($) =>
1561
+ choice(
1562
+ seq($.function_declarator, ';'),
1563
+ seq($.declarator_list, ';'),
1564
+ seq('precision', $.precision_qualifier, $.type_specifier, ';'),
1565
+ // Interface blocks
1566
+ seq(
1567
+ $.type_qualifier,
1568
+ field('name', $.identifier),
1569
+ '{',
1570
+ $.field_declaration_list,
1571
+ '}',
1572
+ optional(
1573
+ seq(
1574
+ field('instance_name', $.identifier),
1575
+ optional($.array_specifier),
1576
+ ),
1577
+ ),
1578
+ ';',
1579
+ ),
1580
+ seq($.type_qualifier, ';'),
1581
+ seq($.type_qualifier, $.identifier_list, ';'),
1582
+ ),
1583
+
1584
+ /**
1585
+ * ```bnf
1586
+ * identifier_list :
1587
+ * IDENTIFIER
1588
+ * identifier_list COMMA IDENTIFIER
1589
+ * ```
1590
+ *
1591
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1592
+ * @returns {RuleOrLiteral} Rule definition.
1593
+ */
1594
+ identifier_list: ($) => commaSep1($.identifier),
1595
+
1596
+ /**
1597
+ * ```bnf
1598
+ * function_prototype : function_declarator RIGHT_PAREN
1599
+ *
1600
+ * function_declarator :
1601
+ * function_header
1602
+ * function_header_with_parameters
1603
+ *
1604
+ * function_header_with_parameters :
1605
+ * function_header parameter_declaration
1606
+ * function_header_with_parameters COMMA parameter_declaration
1607
+ *
1608
+ * function_header :
1609
+ * fully_specified_type IDENTIFIER LEFT_PAREN
1610
+ * ```
1611
+ *
1612
+ * Tree-sitter compromise: function_prototype is inlined and its
1613
+ * sub-production (closing RIGHT_PAREN) collapsed here. The spec splits
1614
+ * LEFT_PAREN and RIGHT_PAREN across function_header and
1615
+ * function_prototype. They're merged here so the delimiter pair lives in
1616
+ * one node.
1617
+ *
1618
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1619
+ * @returns {RuleOrLiteral} Rule definition.
1620
+ */
1621
+ function_declarator: ($) =>
1622
+ seq(
1623
+ field('return_type', $.type),
1624
+ field('name', $.identifier),
1625
+ '(',
1626
+ optional(field('parameters', $.parameter_list)),
1627
+ ')',
1628
+ ),
1629
+
1630
+ /**
1631
+ * Tree-sitter compromise: replaces the spec's
1632
+ * function_header_with_parameters left-recursive chain.
1633
+ *
1634
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1635
+ * @returns {RuleOrLiteral} Rule definition.
1636
+ */
1637
+ parameter_list: ($) => commaSep1($.parameter_declaration),
1638
+
1639
+ /**
1640
+ * ```bnf
1641
+ * parameter_declaration :
1642
+ * type_qualifier parameter_declarator
1643
+ * parameter_declarator
1644
+ * type_qualifier parameter_type_specifier
1645
+ * parameter_type_specifier
1646
+ * ```
1647
+ *
1648
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1649
+ * @returns {RuleOrLiteral} Rule definition.
1650
+ */
1651
+ parameter_declaration: ($) =>
1652
+ choice(
1653
+ seq(
1654
+ optional($.type_qualifier),
1655
+ field('type', $.type_specifier),
1656
+ field('name', $.identifier),
1657
+ optional($.array_specifier),
1658
+ ),
1659
+ seq(optional($.type_qualifier), field('type', $.type_specifier)),
1660
+ ),
1661
+
1662
+ /**
1663
+ * ```bnf
1664
+ * init_declarator_list :
1665
+ * single_declaration
1666
+ * init_declarator_list COMMA IDENTIFIER
1667
+ * init_declarator_list COMMA IDENTIFIER array_specifier
1668
+ * init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer
1669
+ * init_declarator_list COMMA IDENTIFIER EQUAL initializer
1670
+ * ```
1671
+ *
1672
+ * Tree-sitter compromise: subsequent declarators (which omit the
1673
+ * type in the BNF) are wrapped in `declarator` nodes without a
1674
+ * `type` child, so every name/value pair is uniformly grouped.
1675
+ *
1676
+ * @param {GrammarSymbols<string>} $
1677
+ */
1678
+ declarator_list: ($) =>
1679
+ seq(
1680
+ $.declarator,
1681
+ repeat(
1682
+ seq(',', alias($._subsequent_declarator, $.declarator)),
1683
+ ),
1684
+ ),
1685
+
1686
+ /**
1687
+ * ```bnf
1688
+ * single_declaration :
1689
+ * fully_specified_type
1690
+ * fully_specified_type IDENTIFIER
1691
+ * fully_specified_type IDENTIFIER array_specifier
1692
+ * fully_specified_type IDENTIFIER array_specifier EQUAL initializer
1693
+ * fully_specified_type IDENTIFIER EQUAL initializer
1694
+ * ```
1695
+ *
1696
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1697
+ * @returns {RuleOrLiteral} Rule definition.
1698
+ */
1699
+ declarator: ($) =>
1700
+ seq(
1701
+ $.type,
1702
+ optional(
1703
+ seq(
1704
+ field('name', $.identifier),
1705
+ optional($.array_specifier),
1706
+ optional(seq('=', field('value', $._initializer))),
1707
+ ),
1708
+ ),
1709
+ ),
1710
+
1711
+ /**
1712
+ * Subsequent declarator in a comma-separated list (no type).
1713
+ *
1714
+ * @param {GrammarSymbols<string>} $
1715
+ */
1716
+ _subsequent_declarator: ($) =>
1717
+ seq(
1718
+ field('name', $.identifier),
1719
+ optional($.array_specifier),
1720
+ optional(seq('=', field('value', $._initializer))),
1721
+ ),
1722
+
1723
+ // ── Initializers ── grammar.adoc ───────────────────────────────────
1724
+
1725
+ /**
1726
+ * ```bnf
1727
+ * initializer :
1728
+ * assignment_expression
1729
+ * LEFT_BRACE initializer_list RIGHT_BRACE
1730
+ * LEFT_BRACE initializer_list COMMA RIGHT_BRACE
1731
+ * ```
1732
+ *
1733
+ * Hidden: transparent pass-through for expressions; the aggregate
1734
+ * `{ ... }` form is extracted as the visible `initializer` node.
1735
+ *
1736
+ * @param {GrammarSymbols<string>} $
1737
+ */
1738
+ _initializer: ($) =>
1739
+ OPT.ESSL ?
1740
+ $._assignment_expression :
1741
+ choice($._assignment_expression, $.initializer),
1742
+
1743
+ ...(OPT.ESSL ? {} : {
1744
+ /**
1745
+ * Aggregate initializer: `{ initializer_list }`. GLSL-only.
1746
+ *
1747
+ * @param {GrammarSymbols<string>} $
1748
+ */
1749
+ initializer: ($) => seq('{', $.initializer_list, '}'),
1750
+ }),
1751
+
1752
+ /**
1753
+ * ```bnf
1754
+ * initializer_list :
1755
+ * initializer
1756
+ * initializer_list COMMA initializer
1757
+ * ```
1758
+ *
1759
+ * Trailing comma allowed per spec (`{ initializer_list COMMA }`).
1760
+ * GLSL-only (ESSL has no aggregate initializers).
1761
+ *
1762
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1763
+ * @returns {RuleOrLiteral} Rule definition.
1764
+ */
1765
+ ...(OPT.ESSL ? {} : {
1766
+ initializer_list: ($) => seq(commaSep1($._initializer), optional(',')),
1767
+ }),
1768
+
1769
+ // ── Statements ── grammar.adoc ────────────────────────────────────
1770
+
1771
+ /**
1772
+ * ```bnf
1773
+ * declaration_statement : declaration
1774
+ * ```
1775
+ *
1776
+ * Hidden: 1:1 pass-through so local_declaration appears directly.
1777
+ *
1778
+ * @param {GrammarSymbols<string>} $
1779
+ */
1780
+ _declaration_statement: ($) => prec.dynamic(-1, $.local_declaration),
1781
+
1782
+ /**
1783
+ * Block-scope declaration form.
1784
+ *
1785
+ * This reuses the ordinary declarator list instead of splitting block
1786
+ * declarations into a separate macro-filtered tree.
1787
+ *
1788
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1789
+ * @returns {RuleOrLiteral} Rule definition.
1790
+ */
1791
+ local_declaration: ($) => seq($.declarator_list, ';'),
1792
+
1793
+ /**
1794
+ * ```bnf
1795
+ * statement :
1796
+ * compound_statement
1797
+ * simple_statement
1798
+ * ```
1799
+ *
1800
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1801
+ * @returns {RuleOrLiteral} Rule definition.
1802
+ */
1803
+ statement: ($) => choice($.compound_statement, $.simple_statement),
1804
+
1805
+ /**
1806
+ * ```bnf
1807
+ * simple_statement :
1808
+ * declaration_statement
1809
+ * expression_statement
1810
+ * selection_statement
1811
+ * switch_statement
1812
+ * case_label
1813
+ * iteration_statement
1814
+ * jump_statement
1815
+ * ```
1816
+ *
1817
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1818
+ * @returns {RuleOrLiteral} Rule definition.
1819
+ */
1820
+ simple_statement: ($) =>
1821
+ choice(
1822
+ $.expression_statement,
1823
+ $._declaration_statement,
1824
+ $.if_statement,
1825
+ $.switch_statement,
1826
+ $.case_label,
1827
+ $._iteration_statement,
1828
+ $._jump_statement,
1829
+ ...extRule($, 'demote_statement'),
1830
+ // Extension: preprocessor directives in statement position
1831
+ $.preproc_call,
1832
+ ...(OPT.MACRO_PARSING ?
1833
+ [
1834
+ $.preproc_def,
1835
+ $.preproc_function_def,
1836
+ $.preproc_undef,
1837
+ alias($.preproc_if_in_statement, $.preproc_if),
1838
+ alias($.preproc_ifdef_in_statement, $.preproc_ifdef),
1839
+ ] :
1840
+ []),
1841
+ ),
1842
+
1843
+ /**
1844
+ * ```bnf
1845
+ * compound_statement :
1846
+ * LEFT_BRACE RIGHT_BRACE
1847
+ * LEFT_BRACE statement_list RIGHT_BRACE
1848
+ * ```
1849
+ *
1850
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1851
+ * @returns {RuleOrLiteral} Rule definition.
1852
+ */
1853
+ compound_statement: ($) => seq('{', optional($.statement_list), '}'),
1854
+
1855
+ /**
1856
+ * ```bnf
1857
+ * statement_no_new_scope :
1858
+ * compound_statement_no_new_scope
1859
+ * simple_statement
1860
+ * ```
1861
+ *
1862
+ * Hidden: identical to `statement` (a supertype); tree-sitter does
1863
+ * not enforce scoping so the distinction adds no value.
1864
+ *
1865
+ * @param {GrammarSymbols<string>} $
1866
+ */
1867
+ _statement_no_new_scope: ($) =>
1868
+ choice($.compound_statement, $.simple_statement),
1869
+
1870
+ /**
1871
+ * ```bnf
1872
+ * statement_list :
1873
+ * statement
1874
+ * statement_list statement
1875
+ * ```
1876
+ *
1877
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1878
+ * @returns {RuleOrLiteral} Rule definition.
1879
+ */
1880
+ statement_list: ($) => repeat1($.statement),
1881
+
1882
+ /**
1883
+ * ```bnf
1884
+ * expression_statement :
1885
+ * SEMICOLON
1886
+ * expression SEMICOLON
1887
+ * ```
1888
+ *
1889
+ * Extension: when OPT.MACRO_EXPANSION is enabled, a function-like
1890
+ * macro invocation may also stand alone as a semicolonless line.
1891
+ * This stays low-priority so real GLSL expression statements win.
1892
+ *
1893
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1894
+ * @returns {RuleOrLiteral} Rule definition.
1895
+ */
1896
+ expression_statement: ($) =>
1897
+ choice(
1898
+ prec.dynamic(1, seq(optional($._expression), ';')),
1899
+ ...(OPT.MACRO_EXPANSION ?
1900
+ [prec.dynamic(-2, alias($._macro_function_call, $.function_call))] :
1901
+ []),
1902
+ ),
1903
+
1904
+ /**
1905
+ * ```bnf
1906
+ * selection_statement :
1907
+ * IF LEFT_PAREN expression RIGHT_PAREN
1908
+ * selection_rest_statement
1909
+ *
1910
+ * selection_rest_statement :
1911
+ * statement ELSE statement
1912
+ * statement
1913
+ * ```
1914
+ *
1915
+ * Tree-sitter compromise: selection_rest_statement is inlined.
1916
+ *
1917
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1918
+ * @returns {RuleOrLiteral} Rule definition.
1919
+ */
1920
+ if_statement: ($) =>
1921
+ prec.right(
1922
+ seq(
1923
+ 'if',
1924
+ '(',
1925
+ field('condition', $._expression),
1926
+ ')',
1927
+ field('consequence', $.statement),
1928
+ optional(field('alternative', $.else_clause)),
1929
+ ),
1930
+ ),
1931
+
1932
+ /**
1933
+ * ```bnf
1934
+ * selection_rest_statement : statement ELSE statement | statement
1935
+ * ```
1936
+ *
1937
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1938
+ * @returns {RuleOrLiteral} Rule definition.
1939
+ */
1940
+ else_clause: ($) => seq('else', $.statement),
1941
+
1942
+ /**
1943
+ * ```bnf
1944
+ * condition :
1945
+ * expression
1946
+ * fully_specified_type IDENTIFIER EQUAL initializer
1947
+ * ```
1948
+ *
1949
+ * Hidden: transparent pass-through for expressions; the
1950
+ * declaration form is extracted as the visible `condition`
1951
+ * node (e.g. `for (int i = 0; ...)`).
1952
+ *
1953
+ * @param {GrammarSymbols<string>} $
1954
+ */
1955
+ _condition: ($) =>
1956
+ choice(
1957
+ $._expression,
1958
+ $.condition,
1959
+ ),
1960
+
1961
+ /**
1962
+ * Declaration inside a for/while condition: `type name = init`.
1963
+ *
1964
+ * @param {GrammarSymbols<string>} $
1965
+ */
1966
+ condition: ($) =>
1967
+ seq($.type, field('name', $.identifier), '=', $._initializer),
1968
+
1969
+ /**
1970
+ * ```bnf
1971
+ * switch_statement :
1972
+ * SWITCH LEFT_PAREN expression RIGHT_PAREN
1973
+ * LEFT_BRACE switch_statement_list RIGHT_BRACE
1974
+ *
1975
+ * switch_statement_list :
1976
+ * /* empty * /
1977
+ * statement_list
1978
+ * ```
1979
+ *
1980
+ * Tree-sitter compromise: switch_statement_list is inlined
1981
+ * as optional(statement_list).
1982
+ *
1983
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
1984
+ * @returns {RuleOrLiteral} Rule definition.
1985
+ */
1986
+ switch_statement: ($) =>
1987
+ seq(
1988
+ 'switch',
1989
+ '(',
1990
+ field('condition', $._expression),
1991
+ ')',
1992
+ '{',
1993
+ optional($.statement_list),
1994
+ '}',
1995
+ ),
1996
+
1997
+ /**
1998
+ * ```bnf
1999
+ * case_label :
2000
+ * CASE expression COLON
2001
+ * DEFAULT COLON
2002
+ * ```
2003
+ *
2004
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2005
+ * @returns {RuleOrLiteral} Rule definition.
2006
+ */
2007
+ case_label: ($) =>
2008
+ choice(
2009
+ seq('case', field('value', $._expression), ':'),
2010
+ seq('default', ':'),
2011
+ ),
2012
+
2013
+ /**
2014
+ * ```bnf
2015
+ * iteration_statement :
2016
+ * WHILE LEFT_PAREN condition RIGHT_PAREN
2017
+ * statement_no_new_scope
2018
+ * DO statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON
2019
+ * FOR LEFT_PAREN for_init_statement for_rest_statement
2020
+ * RIGHT_PAREN statement_no_new_scope
2021
+ * ```
2022
+ *
2023
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2024
+ * @returns {RuleOrLiteral} Rule definition.
2025
+ */
2026
+ _iteration_statement: ($) =>
2027
+ choice($.while_statement, $.do_statement, $.for_statement),
2028
+
2029
+ /**
2030
+ * ```bnf
2031
+ * iteration_statement : WHILE LEFT_PAREN condition RIGHT_PAREN
2032
+ * statement_no_new_scope
2033
+ * ```
2034
+ *
2035
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2036
+ * @returns {RuleOrLiteral} Rule definition.
2037
+ */
2038
+ while_statement: ($) =>
2039
+ seq(
2040
+ 'while',
2041
+ '(',
2042
+ field('condition', $._condition),
2043
+ ')',
2044
+ field('body', $._statement_no_new_scope),
2045
+ ),
2046
+
2047
+ /**
2048
+ * ```bnf
2049
+ * iteration_statement : DO statement WHILE LEFT_PAREN expression
2050
+ * RIGHT_PAREN SEMICOLON
2051
+ * ```
2052
+ *
2053
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2054
+ * @returns {RuleOrLiteral} Rule definition.
2055
+ */
2056
+ do_statement: ($) =>
2057
+ seq(
2058
+ 'do',
2059
+ field('body', $.statement),
2060
+ 'while',
2061
+ '(',
2062
+ field('condition', $._expression),
2063
+ ')',
2064
+ ';',
2065
+ ),
2066
+
2067
+ /**
2068
+ * ```bnf
2069
+ * iteration_statement : FOR LEFT_PAREN for_init_statement
2070
+ * for_rest_statement RIGHT_PAREN statement_no_new_scope
2071
+ *
2072
+ * for_init_statement :
2073
+ * expression_statement | declaration_statement
2074
+ *
2075
+ * for_rest_statement :
2076
+ * conditionopt SEMICOLON
2077
+ * conditionopt SEMICOLON expression
2078
+ *
2079
+ * conditionopt : /* empty * / | condition
2080
+ * ```
2081
+ *
2082
+ * Tree-sitter compromise: for_init_statement, for_rest_statement,
2083
+ * and conditionopt are inlined.
2084
+ *
2085
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2086
+ * @returns {RuleOrLiteral} Rule definition.
2087
+ */
2088
+ for_statement: ($) =>
2089
+ seq(
2090
+ 'for',
2091
+ '(',
2092
+ field(
2093
+ 'initializer',
2094
+ choice($.expression_statement, $._declaration_statement),
2095
+ ),
2096
+ field('condition', optional($._condition)),
2097
+ ';',
2098
+ field('update', optional($._expression)),
2099
+ ')',
2100
+ field('body', $._statement_no_new_scope),
2101
+ ),
2102
+
2103
+ /**
2104
+ * ```bnf
2105
+ * jump_statement :
2106
+ * CONTINUE SEMICOLON
2107
+ * BREAK SEMICOLON
2108
+ * RETURN SEMICOLON
2109
+ * RETURN expression SEMICOLON
2110
+ * DISCARD SEMICOLON
2111
+ * ```
2112
+ *
2113
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2114
+ * @returns {RuleOrLiteral} Rule definition.
2115
+ */
2116
+ _jump_statement: ($) =>
2117
+ choice(
2118
+ $.continue_statement,
2119
+ $.break_statement,
2120
+ $.return_statement,
2121
+ $.discard_statement,
2122
+ ...extRule($, 'terminate_invocation_statement'),
2123
+ ),
2124
+
2125
+ /**
2126
+ * ```bnf
2127
+ * jump_statement : CONTINUE SEMICOLON
2128
+ * ```
2129
+ *
2130
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2131
+ * @returns {RuleOrLiteral} Rule definition.
2132
+ */
2133
+ continue_statement: (_) => seq('continue', ';'),
2134
+
2135
+ /**
2136
+ * ```bnf
2137
+ * jump_statement : BREAK SEMICOLON
2138
+ * ```
2139
+ *
2140
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2141
+ * @returns {RuleOrLiteral} Rule definition.
2142
+ */
2143
+ break_statement: (_) => seq('break', ';'),
2144
+
2145
+ /**
2146
+ * ```bnf
2147
+ * jump_statement : RETURN SEMICOLON | RETURN expression SEMICOLON
2148
+ * ```
2149
+ *
2150
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2151
+ * @returns {RuleOrLiteral} Rule definition.
2152
+ */
2153
+ return_statement: ($) => seq('return', optional($._expression), ';'),
2154
+
2155
+ /**
2156
+ * ```bnf
2157
+ * jump_statement : DISCARD SEMICOLON
2158
+ * ```
2159
+ *
2160
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2161
+ * @returns {RuleOrLiteral} Rule definition.
2162
+ */
2163
+ discard_statement: (_) => seq('discard', ';'),
2164
+
2165
+ // ── Lexical ── basics.adoc ─────────────────────────────────────────
2166
+
2167
+ /**
2168
+ * ```bnf
2169
+ * identifier :
2170
+ * nondigit
2171
+ * identifier nondigit
2172
+ * identifier digit
2173
+ * nondigit : one of
2174
+ * _ a-z A-Z
2175
+ * digit : one of
2176
+ * 0-9
2177
+ * ```
2178
+ *
2179
+ * Source: specification/chapters/basics.adoc §Identifiers
2180
+ *
2181
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2182
+ * @returns {RuleOrLiteral} Rule definition.
2183
+ */
2184
+ identifier: (_) => /[a-zA-Z_][a-zA-Z0-9_]*/,
2185
+
2186
+ /**
2187
+ * Extension of spec TYPE_NAME handling:
2188
+ * Tree-sitter cannot know whether an identifier denotes a user-defined
2189
+ * type or an ordinary variable/function name, so type-name uses are
2190
+ * accepted structurally via an identifier alias.
2191
+ *
2192
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2193
+ * @returns {RuleOrLiteral} Rule definition.
2194
+ */
2195
+ _type_identifier: ($) => alias($.identifier, $.type_identifier),
2196
+
2197
+ /**
2198
+ * Comments (specification/chapters/basics.adoc §Comments):
2199
+ * Line comments: // to end of line
2200
+ * Block comments: /* to * /
2201
+ * Comments cannot be nested.
2202
+ *
2203
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2204
+ * @returns {RuleOrLiteral} Rule definition.
2205
+ */
2206
+ comment: (_) =>
2207
+ token(
2208
+ choice(
2209
+ seq('//', /(\\+(.|\r?\n)|[^\\\n])*/),
2210
+ seq('/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/'),
2211
+ ),
2212
+ ),
2213
+
2214
+ /**
2215
+ * ```bnf
2216
+ * integer-constant :
2217
+ * decimal-constant integer-suffix_opt
2218
+ * octal-constant integer-suffix_opt
2219
+ * hexadecimal-constant integer-suffix_opt
2220
+ * integer-suffix : one of
2221
+ * u U
2222
+ * decimal-constant :
2223
+ * nonzero-digit
2224
+ * decimal-constant digit
2225
+ * octal-constant :
2226
+ * 0
2227
+ * octal-constant octal-digit
2228
+ * hexadecimal-constant :
2229
+ * 0x hexadecimal-digit
2230
+ * 0X hexadecimal-digit
2231
+ * hexadecimal-constant hexadecimal-digit
2232
+ * ```
2233
+ *
2234
+ * ```bnf
2235
+ * floating-constant :
2236
+ * fractional-constant exponent-part_opt floating-suffix_opt
2237
+ * digit-sequence exponent-part floating-suffix_opt
2238
+ * fractional-constant :
2239
+ * digit-sequence . digit-sequence
2240
+ * digit-sequence .
2241
+ * . digit-sequence
2242
+ * exponent-part :
2243
+ * e sign_opt digit-sequence
2244
+ * E sign_opt digit-sequence
2245
+ * sign : one of
2246
+ * + -
2247
+ * floating-suffix : one of
2248
+ * f F lf LF
2249
+ * ```
2250
+ *
2251
+ * Source: specification/chapters/variables.adoc §Integers, §Floats
2252
+ *
2253
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2254
+ * @returns {RuleOrLiteral} Rule definition.
2255
+ */
2256
+ number_literal: (_) => {
2257
+ const decimalDigits = /[0-9]+/;
2258
+ const octalDigits = /[0-7]+/;
2259
+ const hexDigits = /[0-9a-fA-F]+/;
2260
+
2261
+ const integerConstant = choice(
2262
+ seq(/[1-9]/, optional(decimalDigits)), // decimal-constant
2263
+ seq('0', optional(octalDigits)), // octal-constant (includes bare 0)
2264
+ seq(choice('0x', '0X'), hexDigits), // hexadecimal-constant
2265
+ );
2266
+
2267
+ const exponentPart = seq(
2268
+ choice('e', 'E'),
2269
+ optional(choice('+', '-')),
2270
+ decimalDigits,
2271
+ );
2272
+
2273
+ const fractionalConstant = choice(
2274
+ seq(decimalDigits, '.', optional(decimalDigits)),
2275
+ seq('.', decimalDigits),
2276
+ );
2277
+
2278
+ const floatingConstant = choice(
2279
+ seq(fractionalConstant, optional(exponentPart)),
2280
+ seq(decimalDigits, exponentPart),
2281
+ );
2282
+
2283
+ return token(
2284
+ choice(
2285
+ seq(integerConstant, optional(choice('u', 'U'))),
2286
+ seq(floatingConstant, optional(choice('f', 'F', 'lf', 'LF'))),
2287
+ ),
2288
+ );
2289
+ },
2290
+
2291
+ // ── Preprocessor ── basics.adoc §Preprocessor ──────────────────────
2292
+
2293
+ /**
2294
+ * Extension: #version directive.
2295
+ *
2296
+ * Not a formal BNF production in grammar.adoc, but defined in
2297
+ * basics.adoc §Preprocessor / §Version Declaration.
2298
+ * Form: #version <number> [profile]
2299
+ * where profile is one of: core, compatibility, es
2300
+ *
2301
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2302
+ * @returns {RuleOrLiteral} Rule definition.
2303
+ */
2304
+ preproc_version: (_) => seq(/#[ \t]*version/, /[^\n]*/),
2305
+
2306
+ /**
2307
+ * Extension: catch-all for unknown preprocessor directives.
2308
+ * When OPT.MACRO_PARSING is off, all directives (except #version
2309
+ * and bare #) fall through to this rule.
2310
+ *
2311
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2312
+ * @returns {RuleOrLiteral} Rule definition.
2313
+ */
2314
+ preproc_call: ($) =>
2315
+ seq(
2316
+ field('directive', $.preproc_directive),
2317
+ field('argument', optional($.preproc_arg)),
2318
+ token.immediate(/\r?\n/),
2319
+ ),
2320
+
2321
+ /**
2322
+ * basics.adoc §Preprocessor:
2323
+ * "The number sign (#) on a line by itself is ignored."
2324
+ *
2325
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2326
+ * @returns {RuleOrLiteral} Rule definition.
2327
+ */
2328
+ preproc_nothing: (_) => seq(/#[ \t]*/, token.immediate(/\r?\n/)),
2329
+
2330
+ /**
2331
+ * Extension: opaque text payload for preprocessor directives.
2332
+ * Low precedence so structured tokens are preferred when possible.
2333
+ *
2334
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2335
+ * @returns {RuleOrLiteral} Rule definition.
2336
+ */
2337
+ preproc_arg: (_) => token(prec(-1, /\S([^/\n]|\/[^*]|\\\r?\n)*/)),
2338
+
2339
+ /**
2340
+ * Extension: directive token for preproc_call catch-all.
2341
+ * Matches `#` followed by an identifier (e.g. `#include`, `#pragma`).
2342
+ *
2343
+ * @param {GrammarSymbols<string>} _ Grammar symbols.
2344
+ * @returns {RuleOrLiteral} Rule definition.
2345
+ */
2346
+ preproc_directive: (_) => /#[ \t]*[a-zA-Z0-9]\w*/,
2347
+
2348
+ /**
2349
+ * Top-level translation-unit items, including preprocessor directives.
2350
+ *
2351
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2352
+ * @returns {RuleOrLiteral} Rule definition.
2353
+ */
2354
+ _top_level_item: ($) =>
2355
+ choice(
2356
+ $._external_declaration,
2357
+ $.preproc_version,
2358
+ $.preproc_call,
2359
+ $.preproc_nothing,
2360
+ ...(OPT.MACRO_PARSING ?
2361
+ [
2362
+ ...(OPT.MULTILINGUAL ?
2363
+ [
2364
+ $.preproc_language_if,
2365
+ $.preproc_language_ifdef,
2366
+ $.preproc_language_ifndef,
2367
+ ] :
2368
+ []),
2369
+ $.preproc_def,
2370
+ $.preproc_function_def,
2371
+ $.preproc_undef,
2372
+ $.preproc_error,
2373
+ $.preproc_pragma,
2374
+ $.preproc_extension,
2375
+ $.preproc_line,
2376
+ $.preproc_if,
2377
+ $.preproc_ifdef,
2378
+ ] :
2379
+ []),
2380
+ ),
2381
+ },
2382
+
2383
+ // ── OPT.MACRO_PARSING ──────────────────────────────────────────────
2384
+ // Structured preprocessor directive parsing: specific rules for
2385
+ // #define, #undef, #if/#ifdef/#else/#endif, #error, #pragma,
2386
+ // #extension, #line, and preprocessor condition expressions.
2387
+ // When disabled, all directives (except #version and bare #)
2388
+ // fall through to the opaque preproc_call catch-all.
2389
+
2390
+ OPT.MACRO_PARSING ?
2391
+ {
2392
+ /**
2393
+ * Extension: #define directive.
2394
+ *
2395
+ * basics.adoc §Preprocessor: "#define and #undef functionality are
2396
+ * defined as is standard for C++ preprocessors for macro definitions
2397
+ * both with and without macro parameters."
2398
+ *
2399
+ * @param {*} $ Grammar symbols.
2400
+ * @returns {RuleOrLiteral} Rule definition.
2401
+ */
2402
+ preproc_def: ($) =>
2403
+ seq(
2404
+ preprocessor('define'),
2405
+ field('name', $.identifier),
2406
+ field('value', optional($.preproc_arg)),
2407
+ token.immediate(/\r?\n/),
2408
+ ),
2409
+
2410
+ /**
2411
+ * Extension: #define with parameters (function-like macro).
2412
+ *
2413
+ * @param {*} $ Grammar symbols.
2414
+ * @returns {RuleOrLiteral} Rule definition.
2415
+ */
2416
+ preproc_function_def: ($) =>
2417
+ seq(
2418
+ preprocessor('define'),
2419
+ field('name', $.identifier),
2420
+ field('parameters', $.preproc_params),
2421
+ field('value', optional($.preproc_arg)),
2422
+ token.immediate(/\r?\n/),
2423
+ ),
2424
+
2425
+ /**
2426
+ * Extension: parameter list for function-like #define.
2427
+ * token.immediate('(') ensures no space between macro name and '('.
2428
+ *
2429
+ * @param {*} $ Grammar symbols.
2430
+ * @returns {RuleOrLiteral} Rule definition.
2431
+ */
2432
+ preproc_params: ($) =>
2433
+ seq(
2434
+ token.immediate('('),
2435
+ commaSep(choice($.identifier, '...')),
2436
+ ')',
2437
+ ),
2438
+
2439
+ /**
2440
+ * Extension: #undef directive.
2441
+ *
2442
+ * @param {*} $ Grammar symbols.
2443
+ * @returns {RuleOrLiteral} Rule definition.
2444
+ */
2445
+ preproc_undef: ($) =>
2446
+ seq(
2447
+ preprocessor('undef'),
2448
+ field('name', $.identifier),
2449
+ token.immediate(/\r?\n/),
2450
+ ),
2451
+
2452
+ /**
2453
+ * Extension: #error directive.
2454
+ *
2455
+ * basics.adoc §Preprocessor: "The message will be the tokens
2456
+ * following the #error directive, up to the first new-line."
2457
+ *
2458
+ * @param {*} $ Grammar symbols.
2459
+ * @returns {RuleOrLiteral} Rule definition.
2460
+ */
2461
+ preproc_error: ($) =>
2462
+ seq(
2463
+ preprocessor('error'),
2464
+ field('message', optional($.preproc_arg)),
2465
+ token.immediate(/\r?\n/),
2466
+ ),
2467
+
2468
+ /**
2469
+ * Extension: #pragma directive.
2470
+ *
2471
+ * basics.adoc §Preprocessor: "Tokens following #pragma are not
2472
+ * subject to preprocessor macro expansion."
2473
+ *
2474
+ * @param {*} $ Grammar symbols.
2475
+ * @returns {RuleOrLiteral} Rule definition.
2476
+ */
2477
+ preproc_pragma: ($) =>
2478
+ seq(
2479
+ preprocessor('pragma'),
2480
+ field('argument', optional($.preproc_arg)),
2481
+ token.immediate(/\r?\n/),
2482
+ ),
2483
+
2484
+ /**
2485
+ * Extension: #extension directive.
2486
+ *
2487
+ * basics.adoc §Preprocessor:
2488
+ * Form: #extension <extension_name> : <behavior>
2489
+ * #extension all : <behavior>
2490
+ *
2491
+ * @param {*} $ Grammar symbols.
2492
+ * @returns {RuleOrLiteral} Rule definition.
2493
+ */
2494
+ preproc_extension: ($) =>
2495
+ seq(
2496
+ preprocessor('extension'),
2497
+ field('argument', optional($.preproc_arg)),
2498
+ token.immediate(/\r?\n/),
2499
+ ),
2500
+
2501
+ /**
2502
+ * Extension: #line directive.
2503
+ *
2504
+ * basics.adoc §Preprocessor:
2505
+ * Form: #line <line>
2506
+ * #line <line> <source-string-number>
2507
+ *
2508
+ * @param {*} $ Grammar symbols.
2509
+ * @returns {RuleOrLiteral} Rule definition.
2510
+ */
2511
+ preproc_line: ($) =>
2512
+ seq(
2513
+ preprocessor('line'),
2514
+ field('argument', optional($.preproc_arg)),
2515
+ token.immediate(/\r?\n/),
2516
+ ),
2517
+
2518
+ /**
2519
+ * Preprocessor expressions for #if / #elif conditions.
2520
+ *
2521
+ * basics.adoc §Preprocessor: "Expressions following #if and #elif
2522
+ * are further restricted to expressions operating on literal integer
2523
+ * constants, plus identifiers consumed by the defined operator."
2524
+ *
2525
+ * @param {*} $ Grammar symbols.
2526
+ * @returns {RuleOrLiteral} Rule definition.
2527
+ */
2528
+ _preproc_expression: ($) =>
2529
+ choice(
2530
+ $.identifier,
2531
+ $.number_literal,
2532
+ $.preproc_defined,
2533
+ alias($.preproc_unary_expression, $.unary_expression),
2534
+ alias($.preproc_binary_expression, $.binary_expression),
2535
+ alias(
2536
+ $.preproc_parenthesized_expression,
2537
+ $.parenthesized_expression,
2538
+ ),
2539
+ ),
2540
+
2541
+ /**
2542
+ * Extension: parenthesized grouping in preprocessor expressions.
2543
+ * Aliased to parenthesized_expression in the tree.
2544
+ *
2545
+ * @param {*} $ Grammar symbols.
2546
+ * @returns {RuleOrLiteral} Rule definition.
2547
+ */
2548
+ preproc_parenthesized_expression: ($) =>
2549
+ seq('(', $._preproc_expression, ')'),
2550
+
2551
+ /**
2552
+ * ```bnf
2553
+ * defined identifier
2554
+ * defined ( identifier )
2555
+ * ```
2556
+ *
2557
+ * Source: basics.adoc §Preprocessor
2558
+ *
2559
+ * @param {GrammarSymbols<string>} $ Grammar symbols.
2560
+ * @returns {RuleOrLiteral} Rule definition.
2561
+ */
2562
+ preproc_defined: ($) =>
2563
+ choice(
2564
+ prec(1, seq('defined', '(', $.identifier, ')')),
2565
+ seq('defined', $.identifier),
2566
+ ),
2567
+
2568
+ /**
2569
+ * Extension: unary operators in preprocessor expressions.
2570
+ * basics.adoc §Preprocessor allows !, ~, -, + on integer constants.
2571
+ * Aliased to unary_expression in the tree.
2572
+ *
2573
+ * @param {*} $ Grammar symbols.
2574
+ * @returns {RuleOrLiteral} Rule definition.
2575
+ */
2576
+ preproc_unary_expression: ($) =>
2577
+ prec.left(
2578
+ 14,
2579
+ seq(
2580
+ field('operator', choice('!', '~', '-', '+')),
2581
+ field('argument', $._preproc_expression),
2582
+ ),
2583
+ ),
2584
+
2585
+ /**
2586
+ * Preprocessor binary operators (basics.adoc §Preprocessor, precedence table):
2587
+ * 3 multiplicative * / % LTR
2588
+ * 4 additive + - LTR
2589
+ * 5 bit-wise shift << >> LTR
2590
+ * 6 relational < > <= >= LTR
2591
+ * 7 equality == != LTR
2592
+ * 8 bit-wise and & LTR
2593
+ * 9 bit-wise xor ^ LTR
2594
+ * 10 bit-wise or | LTR
2595
+ * 11 logical and && LTR
2596
+ * 12 logical or || LTR
2597
+ *
2598
+ * @param {*} $ Grammar symbols.
2599
+ * @returns {RuleOrLiteral} Rule definition.
2600
+ */
2601
+ preproc_binary_expression: ($) => {
2602
+ /** @type {[number, RuleOrLiteral][]} */
2603
+ const table = [
2604
+ [11, choice('*', '/', '%')],
2605
+ [10, choice('+', '-')],
2606
+ [9, choice('<<', '>>')],
2607
+ [8, choice('<', '>', '<=', '>=')],
2608
+ [7, choice('==', '!=')],
2609
+ [6, '&'],
2610
+ [5, '^'],
2611
+ [4, '|'],
2612
+ [3, '&&'],
2613
+ [2, '||'],
2614
+ ];
2615
+ return choice(
2616
+ ...table.map(([precedence, operator]) =>
2617
+ prec.left(
2618
+ precedence,
2619
+ seq(
2620
+ field('left', $._preproc_expression),
2621
+ field('operator', operator),
2622
+ field('right', $._preproc_expression),
2623
+ ),
2624
+ ),
2625
+ ),
2626
+ );
2627
+ },
2628
+
2629
+ // Conditional compilation: context-dependent variants.
2630
+ ...preprocIf('', ($) => $._top_level_item),
2631
+ ...preprocIf('_in_statement', ($) => $.statement),
2632
+ ...preprocIf('_in_struct_declaration', ($) => $.field_declaration),
2633
+ } :
2634
+ {},
2635
+
2636
+ // ── OPT.MACRO_EXPANSION ────────────────────────────────────────────
2637
+ // Syntactic placeholders for unexpanded macro output. This accepts
2638
+ // identifiers or function-like macro invocations in positions where
2639
+ // macro expansion would yield standard GLSL qualifiers or type names.
2640
+
2641
+ OPT.MACRO_EXPANSION ?
2642
+ {
2643
+ /**
2644
+ * Extension: unexpanded macro used in ordinary GLSL syntax.
2645
+ *
2646
+ * Object-like macros appear as `(macro_invocation (identifier))`.
2647
+ * Function-like macros appear as `(macro_invocation (function_call ...))`.
2648
+ * The same node is reused in qualifier, type, and expression position.
2649
+ *
2650
+ * @param {*} $ Grammar symbols.
2651
+ * @returns {RuleOrLiteral} Rule definition.
2652
+ */
2653
+ macro_invocation: ($) =>
2654
+ choice(
2655
+ alias($._macro_function_call, $.function_call),
2656
+ $.identifier,
2657
+ ),
2658
+
2659
+ /**
2660
+ * Extension: internal function-like macro invocation.
2661
+ *
2662
+ * Unlike regular function_call which requires valid GLSL
2663
+ * expressions as arguments, macro arguments are arbitrary
2664
+ * token sequences separated by commas, with balanced
2665
+ * parentheses. This allows constructs like:
2666
+ * MACRO(.field = 1, .other = 2)
2667
+ * CONCAT(vec, 3)
2668
+ *
2669
+ * @param {GrammarSymbols<string>} $
2670
+ */
2671
+ _macro_function_call: ($) =>
2672
+ seq(
2673
+ field('function', $.identifier),
2674
+ '(',
2675
+ optional(field('arguments', $.macro_argument_list)),
2676
+ ')',
2677
+ ),
2678
+
2679
+ /**
2680
+ * Comma-separated list of macro arguments.
2681
+ *
2682
+ * @param {GrammarSymbols<string>} $
2683
+ */
2684
+ macro_argument_list: ($) =>
2685
+ seq($.macro_argument, repeat(seq(',', $.macro_argument))),
2686
+
2687
+ /**
2688
+ * A single macro argument: any token sequence without bare
2689
+ * `,` or unmatched `)`. Parenthesized sub-groups are allowed
2690
+ * and preserve commas inside them.
2691
+ *
2692
+ * @param {GrammarSymbols<string>} $
2693
+ */
2694
+ macro_argument: ($) =>
2695
+ repeat1(choice(
2696
+ $._macro_paren_group,
2697
+ $._macro_token,
2698
+ )),
2699
+
2700
+ /**
2701
+ * Balanced parentheses inside a macro argument.
2702
+ *
2703
+ * @param {GrammarSymbols<string>} $
2704
+ */
2705
+ _macro_paren_group: ($) =>
2706
+ seq('(', optional($._macro_paren_content), ')'),
2707
+
2708
+ /**
2709
+ * Content inside balanced parens: tokens and commas (no separation).
2710
+ *
2711
+ * @param {GrammarSymbols<string>} $
2712
+ */
2713
+ _macro_paren_content: ($) =>
2714
+ repeat1(choice(
2715
+ $._macro_paren_group,
2716
+ $._macro_token,
2717
+ ',',
2718
+ )),
2719
+
2720
+ /**
2721
+ * Any single token that isn't `,`, `(`, or `)`.
2722
+ *
2723
+ * @param {GrammarSymbols<string>} _
2724
+ */
2725
+ _macro_token: (_) => token(prec(-1, /[^\s,()]+/)),
2726
+ } :
2727
+ {},
2728
+
2729
+ // ── OPT.MULTILINGUAL ──────────────────────────────────────────────
2730
+ // Mixed-language branch support for shared GLSL/C++ utility headers.
2731
+ // This keeps the outer preprocessor structure in GLSL while exposing the
2732
+ // host-language span for query-based injection.
2733
+ //
2734
+ // Two language families are recognized:
2735
+ // cpp: `__cplusplus`
2736
+ // c: `__STDC__`, `__GNUC__`, `__clang__`, `_MSC_VER`
2737
+ //
2738
+ // Each family generates a parallel set of rules (macro, check, condition,
2739
+ // negated condition, code block, else branch, markers). The helpers below
2740
+ // eliminate the duplication.
2741
+
2742
+ OPT.MULTILINGUAL ?
2743
+ {
2744
+ // ── Language macros ──
2745
+
2746
+ /** @param {GrammarSymbols<string>} _ @returns {RuleOrLiteral} */
2747
+ preproc_cpp_language_macro: (_) => '__cplusplus',
2748
+ /** @param {GrammarSymbols<string>} _ @returns {RuleOrLiteral} */
2749
+ preproc_c_language_macro: (_) =>
2750
+ choice('__STDC__', '__GNUC__', '__clang__', '_MSC_VER'),
2751
+
2752
+ // ── Language checks: `MACRO`, `defined MACRO`, `defined(MACRO)` ──
2753
+
2754
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2755
+ preproc_cpp_language_check: ($) => langCheck($.preproc_cpp_language_macro),
2756
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2757
+ preproc_c_language_check: ($) => langCheck($.preproc_c_language_macro),
2758
+
2759
+ // ── Positive conditions ──
2760
+
2761
+ /**
2762
+ * Condition selecting a C++ branch. Matches simple checks and
2763
+ * compound forms like `defined(__cplusplus) || defined(__STDC__)`.
2764
+ * If `__cplusplus` appears anywhere, the branch is treated as C++
2765
+ * since C++ is a usable superset for injected C syntax.
2766
+ *
2767
+ * @param {GrammarSymbols<string>} $
2768
+ */
2769
+ preproc_cpp_condition: ($) =>
2770
+ choice(
2771
+ $.preproc_cpp_language_check,
2772
+ ...langCompound(
2773
+ $.preproc_cpp_language_check,
2774
+ $.preproc_c_language_check,
2775
+ ),
2776
+ ),
2777
+
2778
+ /**
2779
+ * Condition selecting a C branch (simple check only).
2780
+ *
2781
+ * @param {GrammarSymbols<string>} $
2782
+ */
2783
+ preproc_c_condition: ($) => $.preproc_c_language_check,
2784
+
2785
+ // ── Negated conditions ──
2786
+
2787
+ /**
2788
+ * `!defined(__cplusplus)` — body is GLSL, else is C++.
2789
+ *
2790
+ * @param {GrammarSymbols<string>} $
2791
+ */
2792
+ preproc_not_cpp_condition: ($) => not($.preproc_cpp_language_check),
2793
+
2794
+ /**
2795
+ * `!defined(__STDC__)` — body is GLSL, else is C.
2796
+ *
2797
+ * @param {GrammarSymbols<string>} $
2798
+ */
2799
+ preproc_not_c_condition: ($) => not($.preproc_c_language_check),
2800
+
2801
+ // ── Foreign-language code blocks ──
2802
+
2803
+ /**
2804
+ * Raw non-GLSL line. Stops before `#else`, `#elif`, `#endif`.
2805
+ *
2806
+ * @param {GrammarSymbols<string>} _
2807
+ */
2808
+ multilingual_code_line: (_) =>
2809
+ token(prec(-1, /(?:[^\n]+\r?\n?|\r?\n)/)),
2810
+
2811
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2812
+ multilingual_cpp_code_block: ($) => repeat1($.multilingual_code_line),
2813
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2814
+ multilingual_c_code_block: ($) => repeat1($.multilingual_code_line),
2815
+
2816
+ // ── Language-aware #else branches ──
2817
+
2818
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2819
+ preproc_cpp_else: ($) =>
2820
+ seq(preprocessor('else'), field('body', $.multilingual_cpp_code_block)),
2821
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2822
+ preproc_c_else: ($) =>
2823
+ seq(preprocessor('else'), field('body', $.multilingual_c_code_block)),
2824
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2825
+ preproc_glsl_else: ($) =>
2826
+ seq(preprocessor('else'), repeat($._top_level_item)),
2827
+
2828
+ // ── Internal markers (condition → body pairs) ──
2829
+
2830
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2831
+ _found_cpp_marker: ($) =>
2832
+ langForeignBody($.preproc_cpp_condition, $.multilingual_cpp_code_block),
2833
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2834
+ _found_c_marker: ($) =>
2835
+ langForeignBody($.preproc_c_condition, $.multilingual_c_code_block),
2836
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2837
+ _found_not_cpp_marker: ($) =>
2838
+ langGlslBody($, $.preproc_not_cpp_condition, $.preproc_cpp_else),
2839
+ /** @param {GrammarSymbols<string>} $ @returns {RuleOrLiteral} */
2840
+ _found_not_c_marker: ($) =>
2841
+ langGlslBody($, $.preproc_not_c_condition, $.preproc_c_else),
2842
+
2843
+ // ── Top-level language guard directives ──
2844
+
2845
+ /**
2846
+ * `#ifdef __cplusplus` / `#ifdef __STDC__`
2847
+ *
2848
+ * @param {GrammarSymbols<string>} $
2849
+ */
2850
+ preproc_language_ifdef: ($) =>
2851
+ seq(
2852
+ preprocessor('ifdef'),
2853
+ choice(
2854
+ langIfdefArm($, $.preproc_cpp_language_macro, $.multilingual_cpp_code_block, $.preproc_glsl_else),
2855
+ langIfdefArm($, $.preproc_c_language_macro, $.multilingual_c_code_block, $.preproc_glsl_else),
2856
+ ),
2857
+ preprocessor('endif'),
2858
+ ),
2859
+
2860
+ /**
2861
+ * `#ifndef __cplusplus` / `#ifndef __STDC__`
2862
+ *
2863
+ * @param {GrammarSymbols<string>} $
2864
+ */
2865
+ preproc_language_ifndef: ($) =>
2866
+ seq(
2867
+ preprocessor('ifndef'),
2868
+ choice(
2869
+ langIfndefArm($, $.preproc_cpp_language_macro, $.preproc_cpp_else),
2870
+ langIfndefArm($, $.preproc_c_language_macro, $.preproc_c_else),
2871
+ ),
2872
+ preprocessor('endif'),
2873
+ ),
2874
+
2875
+ /**
2876
+ * `#elif defined(__cplusplus)` / `#elif defined(__STDC__)` —
2877
+ * recursive chain for multi-language guards.
2878
+ *
2879
+ * @param {GrammarSymbols<string>} $
2880
+ */
2881
+ preproc_language_elif: ($) =>
2882
+ seq(
2883
+ preprocessor('elif'),
2884
+ choice($._found_cpp_marker, $._found_c_marker),
2885
+ field(
2886
+ 'alternative',
2887
+ optional(
2888
+ choice(
2889
+ alias($.preproc_language_elif, $.preproc_elif),
2890
+ alias($.preproc_glsl_else, $.preproc_else),
2891
+ ),
2892
+ ),
2893
+ ),
2894
+ ),
2895
+
2896
+ /**
2897
+ * `#if defined(__cplusplus)` / `#if !defined(__cplusplus)` —
2898
+ * top-level language guard with positive or negated condition.
2899
+ *
2900
+ * @param {GrammarSymbols<string>} $
2901
+ */
2902
+ preproc_language_if: ($) =>
2903
+ seq(
2904
+ preprocessor('if'),
2905
+ choice(
2906
+ // Positive: body is foreign code
2907
+ seq(
2908
+ choice($._found_cpp_marker, $._found_c_marker),
2909
+ field(
2910
+ 'alternative',
2911
+ optional(
2912
+ choice(
2913
+ alias($.preproc_language_elif, $.preproc_elif),
2914
+ alias($.preproc_glsl_else, $.preproc_else),
2915
+ ),
2916
+ ),
2917
+ ),
2918
+ ),
2919
+ // Negated: body is GLSL
2920
+ choice($._found_not_cpp_marker, $._found_not_c_marker),
2921
+ ),
2922
+ preprocessor('endif'),
2923
+ ),
2924
+ } :
2925
+ {},
2926
+
2927
+ // ── Extension grammar rules ──────────────────────────────────────────
2928
+ EXTENSION_RULES,
2929
+ ),
2930
+ });
2931
+
2932
+ export {PRECEDENCE as PREC, preprocIf, preprocessor, commaSep, commaSep1};