@dekobon/tree-sitter-groovy 0.5.0 → 0.6.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 CHANGED
@@ -1,19 +1,24 @@
1
1
  /**
2
2
  * @file Apache Groovy grammar for tree-sitter
3
3
  * @author Elijah Zupancic <elijah@zupancic.name>
4
- * @license MIT
4
+ * @license Apache-2.0 OR MIT
5
5
  *
6
6
  * The authoritative design document for this grammar is
7
7
  * SPECIFICATION.md at the repository root. Every rule below maps to
8
8
  * a section there. Read the spec before making non-trivial changes.
9
9
  *
10
- * The external scanner (src/scanner.c) handles three context-sensitive
10
+ * The external scanner (src/scanner.c) handles the context-sensitive
11
11
  * lexing situations that pure grammar rules cannot disambiguate:
12
12
  * - Slashy-string-start vs. division
13
13
  * - Line / block / Groovydoc comment single-token emit
14
14
  * - The contextual `record` keyword, emitted only before a record
15
15
  * header so the word stays an identifier everywhere else
16
- * See SPECIFICATION.md §6.
16
+ * - Three decisions that depend on what shares a line: the key of
17
+ * a named argument in a parenthesis-free call, a subscript `[`,
18
+ * and the `)` of a cast
19
+ * - The `!in` operator, which depends on the character after it
20
+ * Inside a string body it emits nothing. See SPECIFICATION.md §6 and
21
+ * the `externals` list below.
17
22
  */
18
23
 
19
24
  /// <reference types="tree-sitter-cli/dsl" />
@@ -54,11 +59,50 @@ const PREC = {
54
59
  };
55
60
  /* eslint-enable no-multi-spaces */
56
61
 
62
+ /*
63
+ * Identifier characters, after GroovyLexer.g4 `JavaLetter` and
64
+ * `JavaLetterOrDigit`: Java's `Character.isJavaIdentifierStart` and
65
+ * `isJavaIdentifierPart` without the identifier-ignorable characters.
66
+ * In Unicode general categories that is L, Nl, Sc and Pc to start a
67
+ * name, and Nd, Mn and Mc as well to continue it. `$` is in Sc and
68
+ * `_` is in Pc.
69
+ */
70
+ const ID_START = String.raw`\p{L}\p{Nl}\p{Sc}\p{Pc}`;
71
+ const ID_PART = ID_START + String.raw`\p{Nd}\p{Mn}\p{Mc}`;
72
+
73
+ /**
74
+ * The source of an identifier pattern, in Rust regex syntax.
75
+ *
76
+ * The ASCII characters are spelled out and come first, so that the
77
+ * lexer has a state that loops on them with plain comparisons. The
78
+ * Unicode classes compile to a binary search over several hundred
79
+ * ranges; written as one class, every character of every ASCII name
80
+ * went through that search, which made the lexer about 2.5 times
81
+ * slower per identifier character.
82
+ *
83
+ * @param {boolean} dollar Whether `$` is an identifier character. It
84
+ * is not in a GString `$name` path (GroovyLexer.g4
85
+ * `IdentifierInGString`).
86
+ * @returns {string} The pattern source.
87
+ */
88
+ function identifierPattern(dollar) {
89
+ const asciiStart = dollar ? 'A-Za-z_$' : 'A-Za-z_';
90
+ const asciiPart = asciiStart + '0-9';
91
+ const otherStart = String.raw`[${ID_START}--[\x00-\x7F]]`;
92
+ const otherPart = String.raw`[${ID_PART}--[\x00-\x7F]]`;
93
+ const anyPart = `[${asciiPart}${otherPart}]`;
94
+ return `(?:[${asciiStart}][${asciiPart}]*(?:${otherPart}${anyPart}*)?|${otherStart}${anyPart}*)`;
95
+ }
96
+
57
97
  /**
58
98
  * GString body segment choice shared between `"..."` and `"""..."""`.
59
99
  * The text rule differs (single-quoted bans newlines, triple-quoted
60
100
  * allows them) but every other alternative is identical.
61
101
  *
102
+ * `_string_body` is never emitted. Listing it here makes it valid
103
+ * wherever a body segment may start, which is how the external scanner
104
+ * learns that it is inside a string (SPECIFICATION.md §6.10).
105
+ *
62
106
  * @param {GrammarSymbols<string>} $ Grammar reference symbols.
63
107
  * @param {RuleOrLiteral} textRule The flavour-specific literal-text rule.
64
108
  * @returns {ChoiceRule} The shared body-segment choice.
@@ -69,14 +113,112 @@ function gstringPart($, textRule) {
69
113
  $.gstring_dollar_interpolation,
70
114
  $.gstring_brace_interpolation,
71
115
  alias($._gstring_literal_dollar, $.string_fragment),
116
+ $._string_body,
117
+ );
118
+ }
119
+
120
+ /**
121
+ * The members of a type body, listed once. `enum_body` repeats the
122
+ * list for the member directly after a trailing comma, where a plain
123
+ * constructor is not allowed (see `enum_declaration`), and the two
124
+ * lists must not drift apart.
125
+ *
126
+ * @param {GrammarSymbols<string>} $ Grammar reference symbols.
127
+ * @param {RuleOrLiteral | null} constructor The constructor rule for
128
+ * this position, or null where a constructor cannot appear.
129
+ * @returns {RuleOrLiteral[]} The member rules, in a fixed order.
130
+ */
131
+ function classMembers($, constructor) {
132
+ return [
133
+ $.method_declaration,
134
+ $.field_declaration,
135
+ ...(constructor ? [constructor] : []),
136
+ $.static_initializer,
137
+ $.enum_declaration,
138
+ $.class_declaration,
139
+ $.interface_declaration,
140
+ $.trait_declaration,
141
+ $.record_declaration,
142
+ $.annotation_type_declaration,
143
+ ];
144
+ }
145
+
146
+ /**
147
+ * A constructor. The enum position directly after a trailing comma
148
+ * requires an access modifier (see `enum_declaration`); everywhere
149
+ * else any modifiers are optional.
150
+ *
151
+ * @param {GrammarSymbols<string>} $ Grammar reference symbols.
152
+ * @param {RuleOrLiteral} modifiers The modifier list for this position.
153
+ * @returns {PrecRightRule} The constructor rule body.
154
+ */
155
+ function constructorDeclaration($, modifiers) {
156
+ return prec.right(3, seq(
157
+ repeat($.annotation),
158
+ modifiers,
159
+ field('name', $.identifier),
160
+ field('parameters', $.formal_parameters),
161
+ optional($.throws_clause),
162
+ field('body', $.block),
163
+ ));
164
+ }
165
+
166
+ /**
167
+ * A method parameter or a closure parameter. Apache's `closure` takes
168
+ * the `formalParameterList` of a method, so the two have one shape:
169
+ * annotations, `final`, then `def`, `var` or a type (a `varargs_type`
170
+ * included), the name, and a default value.
171
+ *
172
+ * Apache's `variableModifiersOpt` is wider: annotations and modifiers
173
+ * in any order, `def` or `var` together with a type, and `...` with no
174
+ * type. Those are not accepted (divergence 14).
175
+ *
176
+ * @param {GrammarSymbols<string>} $ Grammar reference symbols.
177
+ * @returns {ChoiceRule} The parameter rule body.
178
+ */
179
+ function parameter($) {
180
+ const defaultValue = optional(seq('=', field('default', $._expression)));
181
+ return choice(
182
+ seq(
183
+ repeat($.annotation),
184
+ optional('final'),
185
+ choice(
186
+ seq(
187
+ optional(choice('def', 'var', field('type', choice($._type, $.varargs_type)))),
188
+ field('name', $.identifier),
189
+ ),
190
+ // `var` is a contextual keyword and a valid name: `def f(var)`.
191
+ // With no name after it, it is the name.
192
+ field('name', alias('var', $.identifier)),
193
+ ),
194
+ defaultValue,
195
+ ),
196
+ // `_` is a literal directly after an annotation where a statement
197
+ // can start (see `_placeholder_declarator`), and an annotated
198
+ // closure parameter starts there too: `{ @A _ -> }`, or with `_`
199
+ // as the type, `{ @A _ x -> }`. Anywhere else in a parameter `_`
200
+ // is an ordinary identifier.
201
+ seq(
202
+ repeat1($.annotation),
203
+ choice(
204
+ field('name', alias('_', $.identifier)),
205
+ seq(
206
+ field('type', alias('_', $.type_identifier)),
207
+ field('name', $.identifier),
208
+ ),
209
+ ),
210
+ defaultValue,
211
+ ),
72
212
  );
73
213
  }
74
214
 
75
215
  module.exports = grammar({
76
216
  name: 'groovy',
77
217
 
78
- // Placeholder for the real implementation. SPECIFICATION.md §3-§7
79
- // is the contract this rule set will fulfil.
218
+ // Keywords are extracted from this token. A keyword is lexed as
219
+ // such only in a state where it has a parse action, or where it is
220
+ // reserved (`reserved` below); anywhere else the same text is an
221
+ // `identifier`. Several comments in this file depend on that.
80
222
  word: $ => $.identifier,
81
223
 
82
224
  // §5.2 — `new` is a hard keyword. Keyword extraction alone still
@@ -93,13 +235,23 @@ module.exports = grammar({
93
235
 
94
236
  extras: $ => [
95
237
  /\s/,
238
+ // §5.10 — a line escape, `\` at the end of a line. Groovy's lexer
239
+ // skips it as it skips spaces and tabs.
240
+ /\\\r?\n/,
96
241
  $.line_comment,
97
242
  $.block_comment,
98
243
  $.groovydoc_comment,
99
244
  ],
100
245
 
101
246
  // Externals listed here must have a matching branch in
102
- // `src/scanner.c` AND be referenced by at least one rule below.
247
+ // `src/scanner.c` AND be referenced by at least one rule below
248
+ // (`_command_named_key` and `_subscript_open` included). There are
249
+ // two exceptions. No rule references `_error_sentinel`: it exists so
250
+ // the scanner can detect error recovery, when tree-sitter marks
251
+ // every external valid. It is kept last here and in the scanner's
252
+ // `TokenType` enum, and the two orders must match. The scanner never
253
+ // emits `_string_body`: a string body references it so that the
254
+ // scanner can tell it is inside one.
103
255
  // Tokens the spec §6 proposes as external scanner branches but
104
256
  // that this grammar implements in-grammar (GString interpolation
105
257
  // via regex tokens, slashy body via in-grammar `_slashy_text`,
@@ -115,10 +267,39 @@ module.exports = grammar({
115
267
  $.block_comment,
116
268
  $.groovydoc_comment,
117
269
  $._record_keyword,
270
+ // §5.5 — empty token before the `name:` key of a named argument in
271
+ // a parenthesis-free call. Scanner-side so the newline before a Spock
272
+ // label (`result` then `where:`) is not swallowed, and so
273
+ // `Type name =` stays a declaration.
274
+ $._command_named_key,
275
+ // §5.10 — `[` on the same line as the token before it (spaces, tabs and
276
+ // line escapes allowed). A `[` after a newline isn't one and can only
277
+ // start a list or map literal: a statement, not a subscript of the line
278
+ // above.
279
+ $._subscript_open,
280
+ // §6.11 — a `)` with something after it on the same line. A cast
281
+ // that closes with it outranks a parenthesized expression; a cast
282
+ // whose operand is on a later line ranks below one, so `x = (a)`
283
+ // followed by `foo()` is two statements.
284
+ $._cast_close,
285
+ // §6.12 — the `!in` operator, which needs whitespace or an opening
286
+ // bracket after it. The scanner reads that character and leaves it.
287
+ $._not_in,
288
+ // §6.10 — never emitted: valid only between the quotes of a string
289
+ // whose body is lexed in pieces. Comments are extras, so the parser
290
+ // offers them there too; the scanner must emit nothing, or `//` and
291
+ // `/*` at a piece boundary become comments.
292
+ $._string_body,
293
+ // Never referenced by a rule: valid only during error recovery, when
294
+ // the scanner must not emit context-sensitive tokens.
295
+ $._error_sentinel,
118
296
  ],
119
297
 
120
298
  conflicts: $ => [
121
- // See SPECIFICATION.md §7. Populated as rules accumulate.
299
+ // `final def x` / `final x = 1` in a block vs. a modified member at top level.
300
+ [$._modifier, $.local_variable_declaration],
301
+ // This array is the authoritative list; SPECIFICATION.md §7 is
302
+ // the list predicted before the grammar existed.
122
303
  // (list_literal vs map_literal — tree-sitter resolves without
123
304
  // an explicit conflict because `:` after the first element
124
305
  // always disambiguates within one token of lookahead.)
@@ -177,6 +358,39 @@ module.exports = grammar({
177
358
  // tree-sitter report it unnecessary. Regenerate without it to
178
359
  // confirm that still holds.
179
360
  [$.formal_parameter, $._expression],
361
+ // §4 — after an enum's trailing comma, `private` may open a
362
+ // constructor (`private E() { }`) or any other member.
363
+ [$._modifier, $._modified_constructor_declaration],
364
+ // §5.8 — a closure's parameter list and a closure body that opens
365
+ // with a declaration share their first tokens, and so does a
366
+ // block, whose `{` may also open a closure. The `->` decides, or
367
+ // the first token that only one reading accepts.
368
+ //
369
+ // `{ final x -> }` vs. a body opening with a final local (#56) or
370
+ // with a modified declaration; all three stay alive at `final`.
371
+ [$._modifier, $.local_variable_declaration, $.closure_parameter],
372
+ // `{ String a = 'x', b -> }` vs. `{ String a = 'x', b = 'y' }`, and
373
+ // `{ def a, b -> }` vs. `{ def a, b }`. These two fork at every
374
+ // `,` of a declaration that opens a closure or block, and both
375
+ // stacks run to the end of the list: 15% to 60% more work for a
376
+ // list of two to eight names, about twice for a long one. A list
377
+ // of one name does not fork.
378
+ [$._initialized_declarator, $.closure_parameter],
379
+ [$.variable_declarator, $.closure_parameter],
380
+ // `{ @A _ x -> }`, a parameter of type `_`, vs. `{ @A _` as the
381
+ // declaration of the placeholder followed by a statement `x`.
382
+ [$._placeholder_declarator, $.closure_parameter],
383
+ // An untyped default, `{ a = 1 -> }` vs. `{ a = 1 }`, needs no
384
+ // entry: see `expression_statement`.
385
+ //
386
+ // Two things keep these forks from changing a valid parse, and a
387
+ // new rule must keep both. `closure_parameter` carries no dynamic
388
+ // precedence, so a reading that survives to the `->` wins by being
389
+ // the only one left. And `->` appears nowhere at the top level of
390
+ // a closure body: only in `closure_parameters` and in the arrow
391
+ // forms of `switch`. On invalid input the parameter reading can
392
+ // win, because a missing `->` is a cheaper repair than an ERROR:
393
+ // `{ x = 1, y }` recovers as two parameters.
180
394
  ],
181
395
 
182
396
  rules: {
@@ -211,6 +425,7 @@ module.exports = grammar({
211
425
  $.assert_statement,
212
426
  $.yield_statement,
213
427
  $.try_statement,
428
+ $.synchronized_statement,
214
429
  $.class_declaration,
215
430
  $.trait_declaration,
216
431
  $.interface_declaration,
@@ -230,18 +445,51 @@ module.exports = grammar({
230
445
  // §5.5 — command_chain is the parenthesis-free method-call
231
446
  // form (`println 'hello'`, `events 'a', 'b', 'c'`). v1 is
232
447
  // intentionally conservative: receiver is a bare identifier
233
- // and arguments are restricted to literal-shaped expressions
234
- // and closures (no risk of mis-parsing `foo bar` as a command
235
- // call on a bare identifier). Multi-argument lists are supported
236
- // (comma-separated literal arguments per Gradle / Jenkins DSL
237
- // idioms). Chained `foo bar baz` and complex argument shapes
238
- // land later. Closes a subset of murtaza64 #5.
448
+ // and the first argument is restricted to literal-shaped
449
+ // expressions, closures and named arguments (no risk of
450
+ // mis-parsing `foo bar` as a command call on a bare identifier).
451
+ // After a comma an argument is a named argument or any
452
+ // expression: Apache's `argumentListElement` without spread
453
+ // arguments (`foo 'a', *args` is an ERROR). Chained
454
+ // `foo bar baz` lands later. Closes a subset of murtaza64 #5.
239
455
  command_chain: $ => prec(2, seq(
240
456
  field('receiver', $.identifier),
241
- field('argument', $._command_argument),
242
- repeat(seq(',', field('argument', $._command_argument))),
457
+ field('argument', choice($._command_argument, alias($._command_named_argument, $.named_argument))),
458
+ repeat(seq(',', field('argument', choice(
459
+ alias($._command_named_argument, $.named_argument),
460
+ alias($._command_continued_named_argument, $.named_argument),
461
+ $._expression,
462
+ )))),
243
463
  )),
244
464
 
465
+ // §5.5 item 3 — named arguments in a parenthesis-free call:
466
+ // `exclude group: 'a', module: 'b'`, `archiveArtifacts artifacts: 'x'`,
467
+ // GORM's `name blank: false, size: 1..100`. `_command_named_key`
468
+ // is an empty token that the external scanner emits before a name
469
+ // only when `:` follows it on the same line, so `Type name = …` and
470
+ // a bare statement followed by a Spock label on the next line are
471
+ // unaffected. The name itself is an ordinary `identifier`. A
472
+ // keyword is a valid key (Apache `namedArgLabel`): it has no
473
+ // action after the empty token, so it lexes as an identifier, and
474
+ // the empty `property` set does the same for the reserved `new`.
475
+ _command_named_argument: $ => seq(
476
+ $._command_named_key,
477
+ field('key', reserved('property', $.identifier)),
478
+ ':',
479
+ field('value', $._expression),
480
+ ),
481
+
482
+ // After a comma a newline cannot end the statement, so a key on a
483
+ // continuation line (`sh script: 'make',` then `returnStdout: true`)
484
+ // needs no scanner token. Same-line keys normally come from the
485
+ // scanner; `case` and `default`, which the scanner rejects, reach
486
+ // this rule on the same line too (`foo 'a', default: 1`).
487
+ _command_continued_named_argument: $ => seq(
488
+ field('key', $.identifier),
489
+ ':',
490
+ field('value', $._expression),
491
+ ),
492
+
245
493
  _command_argument: $ => choice(
246
494
  $.string_literal,
247
495
  $.number_literal,
@@ -256,8 +504,9 @@ module.exports = grammar({
256
504
  // labeled_statement and expression_statement alive after
257
505
  // `identifier`; the `:` decides. A scanner-token form
258
506
  // (`_label_colon`) was attempted but interacted with
259
- // not-yet-implemented named-argument command chains
260
- // (`archiveArtifacts artifacts: 'x'`); see divergence #5.
507
+ // named-argument command chains (`archiveArtifacts artifacts: 'x'`),
508
+ // which now have their key decided by `_command_named_key`
509
+ // instead; see the §6.5 row in `docs/divergences-from-spec.md`.
261
510
  labeled_statement: $ => prec(1, seq(
262
511
  field('label', $.identifier),
263
512
  ':',
@@ -383,8 +632,7 @@ module.exports = grammar({
383
632
  // §4 — try / catch / finally. At least one of catch/finally is
384
633
  // required per Java/Groovy semantics; the grammar relaxes that
385
634
  // to allow a bare `try { … }` and lets downstream tooling
386
- // surface the error if needed. Try-with-resources lands later
387
- // with local variable declarations.
635
+ // surface the error if needed.
388
636
  try_statement: $ => seq(
389
637
  'try',
390
638
  optional(field('resources', $.resource_specification)),
@@ -393,6 +641,16 @@ module.exports = grammar({
393
641
  optional($.finally_clause),
394
642
  ),
395
643
 
644
+ // §4 — `synchronized (lock) { … }` (Apache `#synchronizedStmtAlt`).
645
+ // The body must be a block.
646
+ synchronized_statement: $ => seq(
647
+ 'synchronized',
648
+ '(',
649
+ field('lock', $._expression),
650
+ ')',
651
+ field('body', $.block),
652
+ ),
653
+
396
654
  // §4 — `try (Foo f = expr; Bar b = expr) { … }`. Resources
397
655
  // are typed or `def`-style declarators with required `=`
398
656
  // initializer (per Java-style try-with-resources). Multiple
@@ -406,8 +664,15 @@ module.exports = grammar({
406
664
  ),
407
665
 
408
666
  resource: $ => seq(
409
- optional(field('type', $._type)),
410
- field('name', $.identifier),
667
+ optional('final'),
668
+ choice(
669
+ seq(
670
+ optional(choice('def', 'var', field('type', $._type))),
671
+ field('name', $.identifier),
672
+ ),
673
+ // `var` alone is the name: `try (var = open())`.
674
+ field('name', alias('var', $.identifier)),
675
+ ),
411
676
  '=',
412
677
  field('value', $._expression),
413
678
  ),
@@ -425,8 +690,15 @@ module.exports = grammar({
425
690
  // murtaza64 #39. The type itself is optional — Groovy allows a
426
691
  // bare `catch (e) { … }`, implicitly typed as Exception.
427
692
  catch_formal_parameter: $ => seq(
428
- optional(field('type', choice($._type, $.multi_type))),
429
- field('name', $.identifier),
693
+ optional('final'),
694
+ choice(
695
+ seq(
696
+ optional(choice('def', 'var', field('type', choice($._type, $.multi_type)))),
697
+ field('name', $.identifier),
698
+ ),
699
+ // `var` alone is the name: `catch (var)`.
700
+ field('name', alias('var', $.identifier)),
701
+ ),
430
702
  ),
431
703
 
432
704
  multi_type: $ => seq(
@@ -468,6 +740,7 @@ module.exports = grammar({
468
740
  field('name', $.identifier),
469
741
  optional(field('type_parameters', $.type_parameters)),
470
742
  optional($.extends_interfaces),
743
+ optional($.super_interfaces),
471
744
  optional($.permits_clause),
472
745
  field('body', $.class_body),
473
746
  ),
@@ -514,17 +787,17 @@ module.exports = grammar({
514
787
  repeat(seq(',', $._type)),
515
788
  ),
516
789
 
517
- // §4 — trait / interface extend multiple interfaces (no
518
- // `implements` keyword in that position).
790
+ // §4 — trait / interface list their super-interfaces with `extends`.
519
791
  extends_interfaces: $ => seq(
520
792
  'extends',
521
793
  $._type,
522
794
  repeat(seq(',', $._type)),
523
795
  ),
524
796
 
525
- // §4 — Java-style modifiers. Each one is a hard reserved
526
- // keyword (§5.2) so the `word: $.identifier` directive keeps
527
- // them from accidentally being treated as identifiers.
797
+ // §4 — Java-style modifiers. Each one is a hard reserved keyword
798
+ // in Groovy (§5.2). Here it is a keyword only where a modifier can
799
+ // stand; anywhere else the word lexes as an identifier, and input
800
+ // such as `foo(private)` parses with no ERROR (#93).
528
801
  _modifier: _ => choice(
529
802
  'public',
530
803
  'private',
@@ -567,7 +840,7 @@ module.exports = grammar({
567
840
  // a no-op member. `;` is anonymous, so it never appears in the AST.
568
841
  //
569
842
  // A bare `block` is an instance initializer. It sits in the member
570
- // repeat — here and in the three identical repeats of `enum_body` —
843
+ // repeat — here and in the four identical repeats of `enum_body` —
571
844
  // rather than in `_class_member`, because `enum_body` also places
572
845
  // a lone `_class_member` directly after its constants. A block in
573
846
  // that slot is an LR conflict on `A() {` (constructor, or constant
@@ -597,18 +870,7 @@ module.exports = grammar({
597
870
  // is already a complete field (`private boolean sealed` is an
598
871
  // ERROR; `boolean sealed` is not). See divergence 12. No
599
872
  // `conflicts` entry is needed.
600
- _class_member: $ => choice(
601
- $.method_declaration,
602
- $.field_declaration,
603
- $.constructor_declaration,
604
- $.static_initializer,
605
- $.enum_declaration,
606
- $.class_declaration,
607
- $.interface_declaration,
608
- $.trait_declaration,
609
- $.record_declaration,
610
- $.annotation_type_declaration,
611
- ),
873
+ _class_member: $ => choice(...classMembers($, $.constructor_declaration)),
612
874
 
613
875
  // §4 — constructors share the method_declaration shape but have
614
876
  // no return type and the name matches the enclosing class. We
@@ -617,13 +879,16 @@ module.exports = grammar({
617
879
  // tell constructors apart from methods. `prec(3, …)` wins over
618
880
  // both field and method when the identifier is followed by `(`
619
881
  // and no return type precedes it.
620
- constructor_declaration: $ => prec.right(3, seq(
621
- repeat($.annotation),
622
- repeat($._modifier),
623
- field('name', $.identifier),
624
- field('parameters', $.formal_parameters),
625
- optional($.throws_clause),
626
- field('body', $.block),
882
+ constructor_declaration: $ => constructorDeclaration($, repeat($._modifier)),
883
+
884
+ // The constructor form for the member directly after an enum's
885
+ // trailing comma (see `enum_declaration`). Only access modifiers
886
+ // qualify: `static f() { }` there is a method. The weight is above
887
+ // a method with a body, which matches the same text through its
888
+ // modifier-only form.
889
+ _modified_constructor_declaration: $ => prec.dynamic(4, constructorDeclaration(
890
+ $,
891
+ repeat1(choice('public', 'private', 'protected')),
627
892
  )),
628
893
 
629
894
  // §4 — `static { … }` class-body initializer block. Runs once
@@ -647,38 +912,74 @@ module.exports = grammar({
647
912
  // would collide with a plain expression_statement.
648
913
  field_declaration: $ => choice(
649
914
  // Prefer the typed field when both parses succeed, rather than
650
- // splitting `private final int v\n C() {}` after `int`.
651
- prec.dynamic(1, seq(
915
+ // splitting `private final int v\n C() {}` after `int`. The
916
+ // weight is above a method body's: the split reading holds a
917
+ // method (`v C() {}`), which would otherwise tie.
918
+ prec.dynamic(4, seq(
652
919
  repeat($.annotation),
653
920
  repeat($._modifier),
654
921
  choice('def', field('type', $._type)),
655
922
  $.variable_declarator,
656
923
  repeat(seq(',', $.variable_declarator)),
657
924
  )),
658
- seq(
925
+ // The negative weight settles the split that the typed weight
926
+ // cannot. `private final int v` followed by `public A() { }` is
927
+ // also `private final int`, a field `v public` and a constructor
928
+ // `A() { }`: a keyword is an identifier wherever it has no
929
+ // action. Both readings hold one typed field, so they tied, and
930
+ // a comment between the two members made the split win.
931
+ prec.dynamic(-1, seq(
659
932
  repeat($.annotation),
660
933
  repeat1($._modifier),
661
934
  $.variable_declarator,
662
935
  repeat(seq(',', $.variable_declarator)),
663
- ),
936
+ )),
664
937
  ),
665
938
 
666
- // §4 — `def name(params) body`. Typed return type (`String foo()
667
- // { … }`) lands once we can disambiguate from local variable
668
- // declaration. Modifiers and throws clause similarly defer.
669
- // prec(1) wins over local_variable_declaration when `(` follows
670
- // the name, so `def f() { … }` at script-top-level parses as a
671
- // method rather than a `def f` variable followed by `(…) { … }`.
672
- method_declaration: $ => prec.right(2, seq(
673
- repeat($.annotation),
674
- repeat($._modifier),
675
- optional(field('type_parameters', $.method_type_parameters)),
676
- choice('def', field('return_type', $._type)),
677
- field('name', $._property_name),
678
- field('parameters', $.formal_parameters),
679
- optional($.throws_clause),
680
- optional(field('body', $.block)),
681
- )),
939
+ // §4 — `def name(params) body`, or a return type in place of
940
+ // `def`, with modifiers and a throws clause. The static precedence
941
+ // wins over local_variable_declaration when `(` follows the name,
942
+ // so `def f() { … }` at script-top-level parses as a method
943
+ // rather than a `def f` variable followed by `(…) { … }`.
944
+ // Modifiers alone (no `def`, no return type) also introduce a
945
+ // method outside a class body: `final f() { }`. In a class body
946
+ // that shape stays a constructor_declaration.
947
+ //
948
+ // A method with a body carries a dynamic precedence because its
949
+ // tokens also parse as statements: `String f() { }` as the
950
+ // expression `String` followed by the call `f() { }`, and
951
+ // `def f(a) { }` as a variable `f` followed by the cast
952
+ // `(a) { }`. Without a weight, a comment before the method made
953
+ // the split win. The weight is above the blank final local's
954
+ // (`final Type name`, see local_variable_declaration):
955
+ // `final String f(a) { }` is also that local followed by the cast
956
+ // `(a) { }`.
957
+ //
958
+ // A method without a body carries a negative one. Its tokens are
959
+ // also `x` followed by the call `foo()`, and outside a class body
960
+ // only that reading is valid Groovy: a script method needs a
961
+ // body. In a class body nothing competes, so abstract and
962
+ // interface methods are unaffected.
963
+ method_declaration: $ => {
964
+ const signature = seq(
965
+ repeat($.annotation),
966
+ choice(
967
+ seq(
968
+ repeat($._modifier),
969
+ optional(field('type_parameters', $.method_type_parameters)),
970
+ choice('def', field('return_type', $._type)),
971
+ ),
972
+ repeat1($._modifier),
973
+ ),
974
+ field('name', $._property_name),
975
+ field('parameters', $.formal_parameters),
976
+ optional($.throws_clause),
977
+ );
978
+ return prec.right(2, choice(
979
+ prec.dynamic(-1, signature),
980
+ prec.dynamic(3, seq(signature, field('body', $.block))),
981
+ ));
982
+ },
682
983
 
683
984
  // §4 / §5.14 — method-level generic type parameters
684
985
  // (`<T> T identity(T x) { x }`). Distinct from `type_parameters`
@@ -712,13 +1013,11 @@ module.exports = grammar({
712
1013
  // `@PathVariable`, `@RequestBody`, JPA's `@Param`, … — ubiquitous
713
1014
  // on framework-facing methods), optional type, optional varargs
714
1015
  // marker (`Type... name`), and optional default value
715
- // (`name = expr`).
716
- formal_parameter: $ => seq(
717
- repeat($.annotation),
718
- optional(field('type', choice($._type, $.varargs_type))),
719
- field('name', $.identifier),
720
- optional(seq('=', field('default', $._expression))),
721
- ),
1016
+ // (`name = expr`). `final` is the only modifier accepted here.
1017
+ // `def` and `var` stand in place of the type and are keywords, so
1018
+ // `def f(def a)` has no `type`. The shape is `parameter()`, shared
1019
+ // with `closure_parameter`.
1020
+ formal_parameter: $ => parameter($),
722
1021
 
723
1022
  varargs_type: $ => seq($._type, '...'),
724
1023
 
@@ -731,10 +1030,31 @@ module.exports = grammar({
731
1030
  // members follow after `;`, `,;`, or nothing at all — Groovy's
732
1031
  // own parser treats the newline as the separator (GROOVY-7773,
733
1032
  // GROOVY-9306), and newlines are extras here, so the member
734
- // simply follows the last constant. `A,` directly before a
735
- // member is rejected, as in Groovy. With no constants the body
1033
+ // simply follows the last constant. With no constants the body
736
1034
  // is an ordinary member list.
737
1035
  //
1036
+ // Groovy up to 4.x also accepts a member directly after a trailing
1037
+ // `,` (`A, B,\n private int x`): GroovyParser.g4 on those branches
1038
+ // has `enumConstants (nls COMMA)? sep?` before the member list.
1039
+ // Groovy 5 requires a `;`. Without this alternative the modifier
1040
+ // had no action after the `,`, lexed as an identifier, and became
1041
+ // a constant named `private` with no ERROR.
1042
+ //
1043
+ // The alternative is narrower than Groovy, which takes any member
1044
+ // there (divergence 13). `prec(1)` on `enum_constant` keeps a bare
1045
+ // identifier after the `,` a constant, so the member has to start
1046
+ // with something that cannot be one: a modifier, `def`, a
1047
+ // declaration keyword, or an annotation followed by one of those.
1048
+ // `A,\n String x` is an ERROR, and so is an instance initializer.
1049
+ // A constructor needs an access modifier. `B(x) { … }` after a
1050
+ // `,`, with or without annotations, reads as a constant with an
1051
+ // anonymous class body (unsupported, so an ERROR). With an access
1052
+ // modifier it cannot be a constant, and with no constructor form
1053
+ // here `private B(x) { }` parsed, with no ERROR, as a method named
1054
+ // `B`: the modifier-only form of `method_declaration` took it.
1055
+ // Other modifiers do not make a constructor, so `static f() { }`
1056
+ // is a method.
1057
+ //
738
1058
  // An instance initializer (`block`) is accepted wherever the
739
1059
  // member list repeats — after `;`, after a first member, or with
740
1060
  // no constants — but not directly after a constant. Newlines are
@@ -742,9 +1062,9 @@ module.exports = grammar({
742
1062
  // initializer) cannot be told from `A { … }` (a constant with an
743
1063
  // anonymous class body, unsupported); both stay errors.
744
1064
  //
745
- // The three member repeats are spelled out, and must stay
1065
+ // The four member repeats are spelled out, and must stay
746
1066
  // identical to the one in `class_body`, so that the generator
747
- // folds all four into one aux symbol (`aux_sym_class_body_repeat1`).
1067
+ // folds all five into one aux symbol (`aux_sym_class_body_repeat1`).
748
1068
  // A differing copy, or a hidden rule that is not inlined, adds a
749
1069
  // symbol and renumbers every id after it — the alias kinds
750
1070
  // `named_argument`, `quoted_identifier` and `type_identifier`
@@ -754,6 +1074,7 @@ module.exports = grammar({
754
1074
  repeat($._modifier),
755
1075
  'enum',
756
1076
  field('name', $.identifier),
1077
+ optional($.super_interfaces),
757
1078
  field('body', $.enum_body),
758
1079
  ),
759
1080
 
@@ -767,6 +1088,14 @@ module.exports = grammar({
767
1088
  ',',
768
1089
  seq(optional(','), ';', repeat(choice($._class_member, $.block, ';'))),
769
1090
  seq($._class_member, repeat(choice($._class_member, $.block, ';'))),
1091
+ seq(
1092
+ ',',
1093
+ choice(...classMembers(
1094
+ $,
1095
+ alias($._modified_constructor_declaration, $.constructor_declaration),
1096
+ )),
1097
+ repeat(choice($._class_member, $.block, ';')),
1098
+ ),
770
1099
  )),
771
1100
  ),
772
1101
  repeat(choice($._class_member, $.block, ';')),
@@ -789,11 +1118,23 @@ module.exports = grammar({
789
1118
  // share the prefix with an annotated member; the same precedence
790
1119
  // decides after them, so `@Foo A\n int x` is a constant and a
791
1120
  // field.
792
- enum_constant: $ => prec(1, seq(
793
- repeat($.annotation),
794
- field('name', $.identifier),
795
- optional(field('arguments', $.argument_list)),
796
- )),
1121
+ enum_constant: $ => choice(
1122
+ prec(1, seq(
1123
+ repeat($.annotation),
1124
+ field('name', $.identifier),
1125
+ optional(field('arguments', $.argument_list)),
1126
+ )),
1127
+ // `sealed` and `trait` are keywords wherever a member can start
1128
+ // (divergence 12): at the first constant and, since #89, after
1129
+ // every `,`. A constant may still have either name, so the
1130
+ // keyword is accepted as one. The negative precedence gives way
1131
+ // to a declaration when one follows: `sealed class`, `trait T {`.
1132
+ prec(-1, seq(
1133
+ repeat($.annotation),
1134
+ field('name', alias(choice('sealed', 'trait'), $.identifier)),
1135
+ optional(field('arguments', $.argument_list)),
1136
+ )),
1137
+ ),
797
1138
 
798
1139
  // §4 — record (Groovy 4+), after Apache's `classDeclaration`:
799
1140
  // `[modifiers] record Name[<T>](Type a, Type b) [implements I] [{ body }]`.
@@ -848,6 +1189,7 @@ module.exports = grammar({
848
1189
  // §4 — package and import. Trailing `;` is optional in Groovy;
849
1190
  // we accept both forms by not requiring it.
850
1191
  package_declaration: $ => seq(
1192
+ repeat($.annotation),
851
1193
  'package',
852
1194
  field('name', $.qualified_name),
853
1195
  ),
@@ -858,6 +1200,7 @@ module.exports = grammar({
858
1200
  // imports only appear in this position, so the lexer-wide
859
1201
  // longest-match is safe.
860
1202
  import_declaration: $ => seq(
1203
+ repeat($.annotation),
861
1204
  'import',
862
1205
  optional('static'),
863
1206
  field('name', $.qualified_name),
@@ -876,19 +1219,80 @@ module.exports = grammar({
876
1219
  // would conflict with method_invocation `Type(x)` and is
877
1220
  // disallowed in v1. Multiple declarators per statement
878
1221
  // (`def a = 1, b = 2`) are supported in all three forms.
1222
+ // `final` is the only modifier accepted here.
1223
+ // `final x = 1` with no type or `def` is the modifier-only form;
1224
+ // the variable is dynamically typed, as with `def`. `final` also
1225
+ // allows the typed form without an initializer (`final String
1226
+ // name`, a blank final), with a single declarator.
1227
+ //
1228
+ // The typed forms carry a dynamic precedence because their tokens
1229
+ // also parse another way: `String s = 'a'` as the expression
1230
+ // `String` followed by `s = 'a'`, and `final String s = 'a'` as
1231
+ // the modifier-only `final String` followed by `s = 'a'`. Without
1232
+ // a weight, which tree wins depends on tree shape, and a comment
1233
+ // before the declaration flipped it. The blank form ranks above
1234
+ // the initialized one, or `final String name` followed by
1235
+ // `name = 'a'` would tie with `final String` followed by
1236
+ // `name name = 'a'`.
1237
+ //
1238
+ // Newlines do not end a declaration, so these read wrongly with
1239
+ // no ERROR node:
1240
+ // - `final x` followed by a line that starts with an identifier
1241
+ // reads `x` as the type and that identifier as the name. `y = 2`
1242
+ // becomes `final x y = 2`, the same resolution modifier-only
1243
+ // fields get (`static x` then `y = 1`), and `String s = 'a'`
1244
+ // becomes `final x String` followed by `s = 'a'`.
1245
+ // - `final String name` followed by a line reading `foo() { }` is
1246
+ // `final String` followed by the method `name foo() { }`. A
1247
+ // method with a body outranks the blank form, so that
1248
+ // `final String f(a) { }` stays a method (see method_declaration).
1249
+ // - A bare `x` followed by a line reading `y = 1` is the typed
1250
+ // declaration `x y = 1`.
1251
+ //
1252
+ // Annotations may come first on every form (Apache
1253
+ // `variableModifier`): `@Field def x = 1` in a script,
1254
+ // `@SuppressWarnings('unchecked') List<String> xs = []` in a body.
879
1255
  local_variable_declaration: $ => prec.right(choice(
880
1256
  seq(
1257
+ repeat($.annotation),
1258
+ optional('final'),
881
1259
  choice('def', 'var'),
882
1260
  $.variable_declarator,
883
1261
  repeat(seq(',', $.variable_declarator)),
884
1262
  ),
885
- seq(
1263
+ prec.dynamic(1, seq(
1264
+ repeat($.annotation),
1265
+ optional('final'),
886
1266
  field('type', $._type),
887
1267
  $._initialized_declarator,
888
1268
  repeat(seq(',', $._initialized_declarator)),
1269
+ )),
1270
+ seq(
1271
+ repeat($.annotation),
1272
+ 'final',
1273
+ $.variable_declarator,
1274
+ repeat(seq(',', $.variable_declarator)),
889
1275
  ),
1276
+ prec.dynamic(2, seq(
1277
+ repeat($.annotation),
1278
+ 'final',
1279
+ field('type', $._type),
1280
+ field('name', $.identifier),
1281
+ )),
1282
+ prec.dynamic(2, seq(
1283
+ repeat1($.annotation),
1284
+ alias($._placeholder_declarator, $.variable_declarator),
1285
+ )),
890
1286
  )),
891
1287
 
1288
+ // `@Library('shared') _`, the Jenkins idiom: an annotation needs a
1289
+ // declaration to stand on, and `_` is the name used for it. Apache
1290
+ // takes any name there (`variableModifiers variableDeclarators`).
1291
+ // Only `_` is accepted here, as a literal: with any identifier,
1292
+ // `@A x` followed by `y = 1` on the next line would also be a typed
1293
+ // local `x y = 1`, and nothing but the line break tells them apart.
1294
+ _placeholder_declarator: $ => field('name', alias('_', $.identifier)),
1295
+
892
1296
  _initialized_declarator: $ => seq(
893
1297
  field('name', $.identifier),
894
1298
  '=',
@@ -943,6 +1347,7 @@ module.exports = grammar({
943
1347
  for_in_statement: $ => seq(
944
1348
  'for',
945
1349
  '(',
1350
+ optional('final'),
946
1351
  optional(choice('def', field('type', $._type))),
947
1352
  field('variable', $.identifier),
948
1353
  choice('in', ':'),
@@ -951,7 +1356,29 @@ module.exports = grammar({
951
1356
  field('body', choice($.block, $._statement)),
952
1357
  ),
953
1358
 
954
- expression_statement: $ => $._expression,
1359
+ // A statement that is `name = value` keeps the name unreduced.
1360
+ // A closure can open with the same tokens as a parameter with a
1361
+ // default (`{ a = 1 -> a }`), and so can an `if` / `while` / `for`
1362
+ // block, because its `{` may also open a closure. With `name`
1363
+ // already reduced to an expression, the two readings needed a
1364
+ // fork that ran to the end of the value: 14% to 32% more work for
1365
+ // `ext { version = '1.0' }` or `if (c) { a = 1 }`, and twice the
1366
+ // work for a long value. Here both readings shift the `=` and one
1367
+ // stack parses the value; the token after it decides.
1368
+ //
1369
+ // The form is limited to the start of a statement. Inside an
1370
+ // expression it would take `b = 1` out of `a + b = 1`, which the
1371
+ // operator precedence reads as `(a + b) = 1`.
1372
+ expression_statement: $ => choice(
1373
+ $._expression,
1374
+ alias($._statement_assignment, $.assignment_expression),
1375
+ ),
1376
+
1377
+ _statement_assignment: $ => prec.right(PREC.ASSIGN, seq(
1378
+ field('left', $.identifier),
1379
+ field('operator', '='),
1380
+ field('right', $._expression),
1381
+ )),
955
1382
 
956
1383
  // §3.2 PRIMARY — minimal alternative set for now. Every primary
957
1384
  // expression form from §3.2 / §5 will land in this choice over
@@ -1082,21 +1509,18 @@ module.exports = grammar({
1082
1509
  optional($._dotted_type_tail),
1083
1510
  )),
1084
1511
 
1085
- // §3.2 — `x in xs` and `x !in xs`. RELATIONAL level. Both
1512
+ // §3.2 / §3.2.2 — `x in xs` and `x !in xs`. RELATIONAL level. Both
1086
1513
  // operators share one node kind; the `operator` field
1087
- // distinguishes them. `!in` is wrapped in `token(prec(1, …))`
1088
- // so it lexes as a single token despite the embedded space
1089
- // requirement implied by §3.2.2's trailing-context rule —
1090
- // Apache's `NOT_IN` predicate is approximated by requiring at
1091
- // least one trailing whitespace, `[`, `(`, or `{` after `!in`.
1092
- // §3.2 / §3.2.2 — `x in xs` and `x !in xs`. `!in` is a single
1093
- // token whose match requires a trailing whitespace / bracket
1094
- // character (Apache `NOT_IN` predicate) so `a !inXs` does not
1095
- // mis-tokenise as `!in` followed by `Xs`.
1514
+ // distinguishes them. `!in` is an operator only before whitespace
1515
+ // or `[`, `(`, `{` (Apache's `NOT_IN` predicate), so that
1516
+ // `a !inXs` is not `!in` followed by `Xs`. The external scanner
1517
+ // makes that check (§6.12) and leaves the character where it is.
1518
+ // As a regex the token had to include it, which made
1519
+ // `a !in[1, 2]` an ERROR: the `[` was part of the operator.
1096
1520
  membership_expression: $ => {
1097
1521
  const ops = [
1098
1522
  'in',
1099
- token(prec(1, seq('!in', /[ \t\r\n\[({]/))),
1523
+ alias($._not_in, '!in'),
1100
1524
  ];
1101
1525
  return choice(...ops.map(op => prec.left(PREC.RELATIONAL, seq(
1102
1526
  field('element', $._expression),
@@ -1169,12 +1593,47 @@ module.exports = grammar({
1169
1593
  // by tree-sitter's GLR; declared as a conflict if needed).
1170
1594
  // PREC.UNARY right-associative so `(Foo) -x` parses as
1171
1595
  // `(Foo)(-x)` and the type binds to the rightward expression.
1172
- parenthesized_type_cast: $ => prec.right(PREC.UNARY, seq(
1173
- '(',
1174
- field('type', $._type),
1175
- ')',
1176
- field('value', $._expression),
1177
- )),
1596
+ //
1597
+ // Two weights rank the cast against the other reading of the same
1598
+ // tokens, a parenthesized expression followed by something. Both
1599
+ // follow Apache's `castParExpression castOperandExpression`.
1600
+ //
1601
+ // With the operand on the line of the `)`, the cast wins wherever
1602
+ // it parses, as the cast alternative comes first in Apache's
1603
+ // `expression`: `(a) + b`, `(a) [0]` and `(a) (b)` are casts.
1604
+ // `_cast_close` (§6.11) is the `)` that has something after it on
1605
+ // its line.
1606
+ //
1607
+ // With the operand on a later line, the cast loses to any other
1608
+ // complete reading. Apache's rule has no `nls` between the two
1609
+ // parts, so where a line break can end a statement, `x = (a)`
1610
+ // followed by `foo()` is two statements. Inside `(…)` and `[…]`
1611
+ // Groovy ignores line breaks and reads a cast. Rejecting the cast
1612
+ // outright would lose every such case: the scanner cannot tell
1613
+ // which brackets enclose it. Ranking it last keeps the ones where
1614
+ // nothing else parses, `foo((String)` followed by `x)`. It loses
1615
+ // the ones where the later line can also continue the expression:
1616
+ // `foo((a)` followed by `- b)` is a subtraction here and a cast
1617
+ // in Groovy (divergence 15, #99).
1618
+ //
1619
+ // Without the weights nothing ranked the two readings and the
1620
+ // stack order decided. Inside a closure or block, `a = (int) y`
1621
+ // followed by a line that starts with a literal lost the cast
1622
+ // once #83 added its forks.
1623
+ parenthesized_type_cast: $ => choice(
1624
+ prec.dynamic(1, prec.right(PREC.UNARY, seq(
1625
+ '(',
1626
+ field('type', $._type),
1627
+ alias($._cast_close, ')'),
1628
+ field('value', $._expression),
1629
+ ))),
1630
+ prec.dynamic(-1, prec.right(PREC.UNARY, seq(
1631
+ '(',
1632
+ field('type', $._type),
1633
+ ')',
1634
+ field('value', $._expression),
1635
+ ))),
1636
+ ),
1178
1637
 
1179
1638
  // §4 — `_type`. Supports unqualified type identifiers,
1180
1639
  // qualified types (`java.util.List`), array types
@@ -1201,7 +1660,7 @@ module.exports = grammar({
1201
1660
  repeat1(seq('.', alias($.identifier, $.type_identifier))),
1202
1661
  )),
1203
1662
 
1204
- array_type: $ => prec(PREC.ARRAY_TYPE, seq($._type, '[', ']')),
1663
+ array_type: $ => prec(PREC.ARRAY_TYPE, seq($._type, alias($._subscript_open, '['), ']')),
1205
1664
 
1206
1665
  // §4 / §5.14 — Java-style generic type arguments. Allows nested
1207
1666
  // generics (`Map<String, List<Integer>>`) and bounded wildcards
@@ -1356,7 +1815,7 @@ module.exports = grammar({
1356
1815
  // §3.2 / §5.11 — argument list. Positional args are `_expression`;
1357
1816
  // named args (Groovy's `k: v` in call position) are `map_entry`
1358
1817
  // aliased to `named_argument` so downstream tools can tell them
1359
- // apart from positional values. Spread (`*expr`) lands later.
1818
+ // apart from positional values.
1360
1819
  argument_list: $ => seq(
1361
1820
  '(',
1362
1821
  optional(seq(
@@ -1385,9 +1844,12 @@ module.exports = grammar({
1385
1844
  // explicit ask of dekobon #247 for the `?[]` row. Both at
1386
1845
  // PREC.POSTFIX, left-associative — `a[b][c]` parses as
1387
1846
  // `(a[b])[c]`.
1847
+ // `[` must be on the object's line (see `_subscript_open`): a Grails
1848
+ // controller action that ends in `[model: value]` on its own line returns
1849
+ // a map; it isn't a subscript of the statement before it.
1388
1850
  subscript_expression: $ => prec.left(PREC.POSTFIX, seq(
1389
1851
  field('object', $._expression),
1390
- '[',
1852
+ alias($._subscript_open, '['),
1391
1853
  field('index', $._expression),
1392
1854
  ']',
1393
1855
  )),
@@ -1395,6 +1857,9 @@ module.exports = grammar({
1395
1857
  // `?[` is one token. `a ? [b] : c` (with space) still parses
1396
1858
  // as ternary plus list because tree-sitter's lexer never
1397
1859
  // crosses a whitespace boundary to extend a multi-char token.
1860
+ // Unlike `[` (see `_subscript_open`), `?[` may still follow a
1861
+ // newline: no statement starts with `?[`, so there is nothing
1862
+ // to split off.
1398
1863
  safe_subscript_expression: $ => prec.left(PREC.POSTFIX, seq(
1399
1864
  field('object', $._expression),
1400
1865
  '?[',
@@ -1548,7 +2013,7 @@ module.exports = grammar({
1548
2013
  ['%', PREC.MULTIPLICATIVE],
1549
2014
  // SHIFT — §3.1 level 6. Ranges (`..`, `..<`, etc.) share
1550
2015
  // this precedence level but use distinct node kinds, so
1551
- // they live in their own rule (lands later).
2016
+ // they live in their own rule.
1552
2017
  ['<<', PREC.SHIFT_OR_RANGE],
1553
2018
  ['>>', PREC.SHIFT_OR_RANGE],
1554
2019
  ['>>>', PREC.SHIFT_OR_RANGE],
@@ -1586,7 +2051,12 @@ module.exports = grammar({
1586
2051
  // form; `{ a, b -> body }` has identifier parameters;
1587
2052
  // `{ String a, int b -> body }` has typed parameters; `{ body }`
1588
2053
  // has no `->` and an implicit `it` parameter (we do NOT emit a
1589
- // synthetic `it` per §5.8). Default values and varargs land later.
2054
+ // synthetic `it` per §5.8). A parameter takes what a method
2055
+ // parameter does (Apache's closure uses `formalParameterList`):
2056
+ // annotations, `final`, then `def`, `var` or a type (varargs
2057
+ // included), and a default; both rules are `parameter()`. The
2058
+ // body may open with the same words, so the `->` decides between
2059
+ // the two readings.
1590
2060
  closure: $ => seq(
1591
2061
  '{',
1592
2062
  optional($.closure_parameters),
@@ -1602,18 +2072,18 @@ module.exports = grammar({
1602
2072
  '->',
1603
2073
  ),
1604
2074
 
1605
- closure_parameter: $ => seq(
1606
- optional(field('type', $._type)),
1607
- field('name', $.identifier),
1608
- ),
2075
+ closure_parameter: $ => parameter($),
1609
2076
 
1610
2077
  // §3.2 PRIMARY / §5.13 — list literal with trailing-comma support.
1611
2078
  // Empty list is `[]`; the empty map literal is the distinct `[:]`
1612
- // form (see map_literal). Tree-sitter splits the parse between
1613
- // list_literal and map_literal until a `:` or `]` disambiguates;
1614
- // the conflict declaration above keeps both alternatives alive.
2079
+ // form (see map_literal). After the first element, a `:` or its
2080
+ // absence tells a list from a map within one token of lookahead,
2081
+ // so no conflict entry is needed.
2082
+ // Either `[` token opens a literal, so a same-line `[` resolves as
2083
+ // it did with one token: `if (a) { } [1].each { }` is still a block
2084
+ // followed by a list, not a subscripted closure.
1615
2085
  list_literal: $ => seq(
1616
- '[',
2086
+ choice('[', alias($._subscript_open, '[')),
1617
2087
  optional(seq(
1618
2088
  $._expression,
1619
2089
  repeat(seq(',', $._expression)),
@@ -1631,7 +2101,7 @@ module.exports = grammar({
1631
2101
  map_literal: $ => {
1632
2102
  const entry = choice($.map_entry, $.spread_map_entry);
1633
2103
  return seq(
1634
- '[',
2104
+ choice('[', alias($._subscript_open, '[')),
1635
2105
  choice(
1636
2106
  ':',
1637
2107
  seq(
@@ -1660,9 +2130,15 @@ module.exports = grammar({
1660
2130
  ),
1661
2131
 
1662
2132
  // §3.2 PRIMARY — `( expr )`. Distinct from `parenthesized_type_cast`
1663
- // (§5.15), which lands once we have `_type`. The parser disambiguates
1664
- // by what's inside the parens.
1665
- parenthesized_expression: $ => seq('(', $._expression, ')'),
2133
+ // (§5.15); the parser disambiguates by what's inside the parens
2134
+ // and after them. `_cast_close` is the `)` that has something
2135
+ // after it on its line (§6.11). It closes this rule too, so that
2136
+ // `(a) * b` and `((a))` stay open to both readings.
2137
+ parenthesized_expression: $ => seq(
2138
+ '(',
2139
+ $._expression,
2140
+ choice(')', alias($._cast_close, ')')),
2141
+ ),
1666
2142
 
1667
2143
  // §3.2 — single `unary_expression` node kind but two precedence
1668
2144
  // alternatives, matching Apache §3.1:
@@ -1773,17 +2249,27 @@ module.exports = grammar({
1773
2249
 
1774
2250
  // §5.7 — triple-double GString. Like `_double_quoted_string`
1775
2251
  // but newlines and embedded `"` / `""` are part of the body.
2252
+ //
2253
+ // One or two quotes are text when an ordinary character or an
2254
+ // escape follows them. Before `$` they are lexed one at a time, as
2255
+ // fragments of their own: the text token must not take a `$` that
2256
+ // starts an interpolation (`"""say "$name"""`), and a regex
2257
+ // cannot look ahead. The closer is longer than one quote, so it
2258
+ // still wins at `"""`.
1776
2259
  _triple_double_quoted_string: $ => seq(
1777
2260
  '"""',
1778
- repeat(gstringPart($, $._triple_gstring_text)),
2261
+ repeat(choice(
2262
+ gstringPart($, $._triple_gstring_text),
2263
+ alias(token.immediate('"'), $.string_fragment),
2264
+ )),
1779
2265
  token.immediate('"""'),
1780
2266
  ),
1781
2267
 
1782
2268
  _triple_gstring_text: _ => token.immediate(repeat1(choice(
1783
2269
  /[^"\\$]/,
1784
2270
  /\\[\s\S]/,
1785
- /"[^"]/,
1786
- /""[^"]/,
2271
+ /"(\\[\s\S]|[^"\\$])/,
2272
+ /""(\\[\s\S]|[^"\\$])/,
1787
2273
  ))),
1788
2274
 
1789
2275
  // §5.7 — `$identifier(.identifier)*` interpolation. The whole
@@ -1797,9 +2283,9 @@ module.exports = grammar({
1797
2283
  )),
1798
2284
  ),
1799
2285
 
1800
- _gstring_dollar_path: _ => token.immediate(
1801
- /\$[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*/,
1802
- ),
2286
+ _gstring_dollar_path: _ => token.immediate(new RustRegex(
2287
+ String.raw`\$${identifierPattern(false)}(?:\.${identifierPattern(false)})*`,
2288
+ )),
1803
2289
 
1804
2290
  // §5.7 / §6.3 — `${expression}` interpolation.
1805
2291
  gstring_brace_interpolation: $ => seq(
@@ -1815,15 +2301,25 @@ module.exports = grammar({
1815
2301
  // `$$` (literal `$`), `$/` (literal `/`), and any other
1816
2302
  // non-`/` non-`$` char. The closer `/$` is `token.immediate`
1817
2303
  // so it always wins over text when `/` is followed by `$`.
2304
+ //
2305
+ // A run of slashes is text only up to an ordinary character
2306
+ // (`a//b`). The text token must not take a `/` that the closer
2307
+ // needs, and a regex cannot look ahead, so the slashes of a run
2308
+ // that ends at `$` are lexed one at a time: for `//$` the single
2309
+ // `/` matches first, then `/$` is the longer match and closes
2310
+ // the string. Each such slash is its own `string_fragment`.
1818
2311
  _dollar_slashy_string: $ => seq(
1819
2312
  '$/',
1820
- repeat(gstringPart($, $._dollar_slashy_text)),
2313
+ repeat(choice(
2314
+ gstringPart($, $._dollar_slashy_text),
2315
+ alias(token.immediate('/'), $.string_fragment),
2316
+ )),
1821
2317
  token.immediate('/$'),
1822
2318
  ),
1823
2319
 
1824
2320
  _dollar_slashy_text: _ => token.immediate(repeat1(choice(
1825
2321
  /[^/$]/,
1826
- /\/[^$]/,
2322
+ /\/+[^/$]/,
1827
2323
  /\$\$/,
1828
2324
  /\$\//,
1829
2325
  ))),
@@ -1841,6 +2337,8 @@ module.exports = grammar({
1841
2337
  /[0-9][0-9_]*[GgLlIiDdFf]?/,
1842
2338
  )),
1843
2339
 
1844
- identifier: _ => /[A-Za-z_$][A-Za-z0-9_$]*/,
2340
+ // Groovy's lexer takes any Java letter, not only ASCII; see
2341
+ // `identifierPattern` above for the classes.
2342
+ identifier: _ => new RustRegex(identifierPattern(true)),
1845
2343
  },
1846
2344
  });