@dekobon/tree-sitter-groovy 0.2.2 → 0.4.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
@@ -109,7 +109,7 @@ module.exports = grammar({
109
109
  // (list_literal vs map_literal — tree-sitter resolves without
110
110
  // an explicit conflict because `:` after the first element
111
111
  // always disambiguates within one token of lookahead.)
112
- //
112
+
113
113
  // §5.15 — inside `( … )` an identifier could be either an
114
114
  // _expression (parenthesized_expression) or a _type
115
115
  // (parenthesized_type_cast). Tree-sitter explores both via
@@ -145,6 +145,25 @@ module.exports = grammar({
145
145
  // decides; the conflict keeps both alternatives alive until
146
146
  // then.
147
147
  [$.variable_declarator, $._property_name],
148
+
149
+ // §4 — `static NAME` inside a class body: after one or more
150
+ // modifiers and one identifier, that identifier could be the
151
+ // modifier-only field_declaration's variable_declarator name
152
+ // (`static MEDIUM_TTL = 900`) or the start of a typed
153
+ // field_declaration/method_declaration's `_type` (`static Foo
154
+ // bar`). The next token (`=`/`,`/`;` vs a second identifier)
155
+ // disambiguates.
156
+ [$.variable_declarator, $._type],
157
+
158
+ // §4 — `enum E { E(v, w) { } }`: after `E(` an identifier could be
159
+ // a constant's argument (`_expression`) or a constructor's untyped
160
+ // parameter (`formal_parameter`); the `{` after `)` decides. The
161
+ // conflict is global by necessity — it is between the two
162
+ // reductions, not their containers — and was checked inert
163
+ // outside an enum body: declaring it on the v0.3.0 grammar makes
164
+ // tree-sitter report it unnecessary. Regenerate without it to
165
+ // confirm that still holds.
166
+ [$.formal_parameter, $._expression],
148
167
  ],
149
168
 
150
169
  rules: {
@@ -390,9 +409,10 @@ module.exports = grammar({
390
409
 
391
410
  // §4 — `catch_formal_parameter` accepts either a single _type
392
411
  // or a multi_type for Java 7+ / Groovy multi-catch. Closes
393
- // murtaza64 #39.
412
+ // murtaza64 #39. The type itself is optional — Groovy allows a
413
+ // bare `catch (e) { … }`, implicitly typed as Exception.
394
414
  catch_formal_parameter: $ => seq(
395
- field('type', choice($._type, $.multi_type)),
415
+ optional(field('type', choice($._type, $.multi_type))),
396
416
  field('name', $.identifier),
397
417
  ),
398
418
 
@@ -537,11 +557,17 @@ module.exports = grammar({
537
557
  '}',
538
558
  ),
539
559
 
560
+ // A nested enum (`static enum EventType { … }` inside a domain
561
+ // class) is common enough in real Groovy that leaving it out
562
+ // silently misparses `enum` as a field_declaration's type name.
563
+ // Nested class/interface/trait land separately (bigger surface,
564
+ // own conflict set) — out of scope here.
540
565
  _class_member: $ => choice(
541
566
  $.method_declaration,
542
567
  $.field_declaration,
543
568
  $.constructor_declaration,
544
569
  $.static_initializer,
570
+ $.enum_declaration,
545
571
  ),
546
572
 
547
573
  // §4 — constructors share the method_declaration shape but have
@@ -573,12 +599,28 @@ module.exports = grammar({
573
599
  // more declarators (each with optional initializer). Method
574
600
  // declarations share the same prefix; `prec(2, …)` on
575
601
  // `method_declaration` and the trailing `(` discriminate.
576
- field_declaration: $ => seq(
577
- repeat($.annotation),
578
- repeat($._modifier),
579
- choice('def', field('type', $._type)),
580
- $.variable_declarator,
581
- repeat(seq(',', $.variable_declarator)),
602
+ //
603
+ // A modifier alone (no `def`, no type) also introduces a field —
604
+ // `static MEDIUM_TTL = 900` infers Object, a common constant-field
605
+ // idiom. Gated behind `repeat1($._modifier)` (not `repeat`) so this
606
+ // alternative can't match a bare, modifier-less identifier, which
607
+ // would collide with a plain expression_statement.
608
+ field_declaration: $ => choice(
609
+ // Prefer the typed field when both parses succeed, rather than
610
+ // splitting `private final int v\n C() {}` after `int`.
611
+ prec.dynamic(1, seq(
612
+ repeat($.annotation),
613
+ repeat($._modifier),
614
+ choice('def', field('type', $._type)),
615
+ $.variable_declarator,
616
+ repeat(seq(',', $.variable_declarator)),
617
+ )),
618
+ seq(
619
+ repeat($.annotation),
620
+ repeat1($._modifier),
621
+ $.variable_declarator,
622
+ repeat(seq(',', $.variable_declarator)),
623
+ ),
582
624
  ),
583
625
 
584
626
  // §4 — `def name(params) body`. Typed return type (`String foo()
@@ -626,10 +668,13 @@ module.exports = grammar({
626
668
  ')',
627
669
  ),
628
670
 
629
- // §5.13 — formal parameter with optional type, optional
630
- // varargs marker (`Type... name`), and optional default value
671
+ // §5.13 — formal parameter with optional annotations (Spring's
672
+ // `@PathVariable`, `@RequestBody`, JPA's `@Param`, … — ubiquitous
673
+ // on framework-facing methods), optional type, optional varargs
674
+ // marker (`Type... name`), and optional default value
631
675
  // (`name = expr`).
632
676
  formal_parameter: $ => seq(
677
+ repeat($.annotation),
633
678
  optional(field('type', choice($._type, $.varargs_type))),
634
679
  field('name', $.identifier),
635
680
  optional(seq('=', field('default', $._expression))),
@@ -637,13 +682,24 @@ module.exports = grammar({
637
682
 
638
683
  varargs_type: $ => seq($._type, '...'),
639
684
 
640
- // §4 — enum. Closes murtaza64 #36 first half. Constants list
641
- // (optionally trailing-comma) optionally followed by `;` and
642
- // further class members. Constants may carry constructor
643
- // arguments (`RED(0xff0000)`); v1 supports that via the
644
- // optional argument_list.
685
+ // §4 — enum. Closes murtaza64 #36 first half. Constants may
686
+ // carry constructor arguments (`RED(0xff0000)`) via the optional
687
+ // argument_list.
688
+ //
689
+ // The body follows GroovyParser.g4 `classBody` for `t == 2`:
690
+ // after the constants either a trailing `,` closes the list, or
691
+ // members follow after `;`, `,;`, or nothing at all — Groovy's
692
+ // own parser treats the newline as the separator (GROOVY-7773,
693
+ // GROOVY-9306), and newlines are extras here, so the member
694
+ // simply follows the last constant. `A,` directly before a
695
+ // member is rejected, as in Groovy. With no constants the body
696
+ // is an ordinary member list. The member list is spelled three
697
+ // times rather than shared with `class_body` through a hidden
698
+ // rule: a new rule renumbers the aux symbols and ~100 symbol ids,
699
+ // which a patch release must not do.
645
700
  enum_declaration: $ => seq(
646
701
  repeat($.annotation),
702
+ repeat($._modifier),
647
703
  'enum',
648
704
  field('name', $.identifier),
649
705
  field('body', $.enum_body),
@@ -651,19 +707,37 @@ module.exports = grammar({
651
707
 
652
708
  enum_body: $ => seq(
653
709
  '{',
654
- optional(seq(
655
- $.enum_constant,
656
- repeat(seq(',', $.enum_constant)),
657
- optional(','),
658
- )),
659
- optional(seq(';', repeat(choice($._class_member, ';')))),
710
+ choice(
711
+ seq(
712
+ $.enum_constant,
713
+ repeat(seq(',', $.enum_constant)),
714
+ optional(choice(
715
+ ',',
716
+ seq(optional(','), ';', repeat(choice($._class_member, ';'))),
717
+ seq($._class_member, repeat(choice($._class_member, ';'))),
718
+ )),
719
+ ),
720
+ repeat(choice($._class_member, ';')),
721
+ ),
660
722
  '}',
661
723
  ),
662
724
 
663
- enum_constant: $ => seq(
725
+ // §4 — `prec(1)` makes a bare identifier at the start of the body
726
+ // a constant rather than the type of the first member: `A\n E() { }`
727
+ // is a constant followed by a constructor, not a method `E`
728
+ // returning `A`. The mirror case with no constants, `int f() { }`
729
+ // as the first thing in the body, therefore reads as constant `int`
730
+ // plus constructor `f` — a newline is whitespace here and nothing
731
+ // else tells the two apart; a modifier, `def`, or a leading `;`
732
+ // makes it a member. Static precedence rather than `prec.dynamic`
733
+ // with an `[enum_constant, _type]` conflict: both yield the same
734
+ // tree on every valid shape, but the GLR form silently accepts
735
+ // `A\n B` (constants missing their comma, which Groovy rejects) as
736
+ // a field `A B`, where this form reports the ERROR.
737
+ enum_constant: $ => prec(1, seq(
664
738
  field('name', $.identifier),
665
739
  optional(field('arguments', $.argument_list)),
666
- ),
740
+ )),
667
741
 
668
742
  // §4 — record (Groovy 4+). `record Name(Type a, Type b) [{ body }]`.
669
743
  // `record` is a contextual keyword — tree-sitter's word: directive
@@ -779,16 +853,18 @@ module.exports = grammar({
779
853
  optional(field('arguments', $.argument_list)),
780
854
  ),
781
855
 
782
- // §4 — `for (x in xs)`. Untyped variable for v1. Typed
783
- // (`for (Type x in xs)`) lands later. The `in` keyword here
784
- // is a structural delimiter, not the membership_expression
785
- // operator — context disambiguates because the for-header is
786
- // not an expression position.
856
+ // §4 — `for (x in xs)`, plus the Java-compatible enhanced-for
857
+ // form Groovy accepts side by side: `for (Type x : xs)` / `for
858
+ // (def x : xs)`. The `in` / `:` keyword here is a structural
859
+ // delimiter, not the membership_expression operator or a
860
+ // labelled_statement colon — context disambiguates because the
861
+ // for-header is not an expression position.
787
862
  for_in_statement: $ => seq(
788
863
  'for',
789
864
  '(',
865
+ optional(choice('def', field('type', $._type))),
790
866
  field('variable', $.identifier),
791
- 'in',
867
+ choice('in', ':'),
792
868
  field('value', $._expression),
793
869
  ')',
794
870
  field('body', choice($.block, $._statement)),
@@ -972,10 +1048,22 @@ module.exports = grammar({
972
1048
  // (`new Foo().bar`).
973
1049
  object_creation_expression: $ => prec.right(PREC.PRIMARY, seq(
974
1050
  'new',
975
- field('type', $._type),
1051
+ field('type', choice($._type, alias($._diamond_type, $.generic_type))),
976
1052
  field('arguments', $.argument_list),
977
1053
  )),
978
1054
 
1055
+ // §3.2 — Apache's createdName permits diamond, but ordinary types
1056
+ // (including nested type arguments) do not. Keep the public shape.
1057
+ _diamond_type: $ => prec.right(PREC.ARRAY_TYPE, seq(
1058
+ field('base', choice(
1059
+ alias($.identifier, $.type_identifier),
1060
+ $.qualified_type,
1061
+ )),
1062
+ field('arguments', alias($._diamond_type_arguments, $.type_arguments)),
1063
+ )),
1064
+
1065
+ _diamond_type_arguments: _ => seq(token.immediate('<'), '>'),
1066
+
979
1067
  // §5.15 — C-style cast `(Foo) x`. Distinct from
980
1068
  // `parenthesized_expression` followed by something (resolved
981
1069
  // by tree-sitter's GLR; declared as a conflict if needed).
@@ -1135,10 +1223,35 @@ module.exports = grammar({
1135
1223
  // expression so subscript / future field_access / future
1136
1224
  // safe_navigation chains compose naturally. PREC.POSTFIX
1137
1225
  // matches subscript so chains like `obj[i](x)[j]` work.
1138
- method_invocation: $ => prec.left(PREC.POSTFIX, seq(
1139
- field('function', $._expression),
1140
- field('arguments', $.argument_list),
1141
- )),
1226
+ //
1227
+ // Trailing closure (§5.5's command_chain covers the bare-
1228
+ // identifier statement form; this covers the general expression
1229
+ // form used constantly on property-access chains: `list.find {
1230
+ // it > 5 }`, `list.inject(0) { acc, x -> acc + x }`). Groovy
1231
+ // treats a closure literal as the call's last argument whether
1232
+ // or not parens are present, so `argument_list` becomes optional
1233
+ // once a trailing closure is there to anchor the call.
1234
+ //
1235
+ // The args-and-closure alternative sits one precedence level
1236
+ // above plain POSTFIX so `f(x) { … }` reduces to a single
1237
+ // method_invocation node (arguments AND closure fields on the
1238
+ // same node) instead of GLR instead reaching the same span by
1239
+ // nesting a closure-only method_invocation around a plain call.
1240
+ method_invocation: $ => choice(
1241
+ prec.left(PREC.POSTFIX + 1, seq(
1242
+ field('function', $._expression),
1243
+ field('arguments', $.argument_list),
1244
+ field('closure', $.closure),
1245
+ )),
1246
+ prec.left(PREC.POSTFIX, seq(
1247
+ field('function', $._expression),
1248
+ field('arguments', $.argument_list),
1249
+ )),
1250
+ prec.left(PREC.POSTFIX, seq(
1251
+ field('function', $._expression),
1252
+ field('closure', $.closure),
1253
+ )),
1254
+ ),
1142
1255
 
1143
1256
  // §3.2 / §5.11 — argument list. Positional args are `_expression`;
1144
1257
  // named args (Groovy's `k: v` in call position) are `map_entry`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dekobon/tree-sitter-groovy",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "description": "Apache Groovy grammar for tree-sitter (dekobon fork)",
5
5
  "main": "bindings/node",
6
6
  "types": "bindings/node",
@@ -67,10 +67,16 @@
67
67
  }
68
68
  },
69
69
  "devDependencies": {
70
- "eslint": "^9.39.4",
70
+ "eslint": "^10.14.0",
71
71
  "eslint-config-treesitter": "^1.0.2",
72
+ "globals": "^17.14.0",
72
73
  "prebuildify": "^6.0.1",
73
- "tree-sitter-cli": "^0.26.9"
74
+ "tree-sitter-cli": "^0.27.2"
75
+ },
76
+ "overrides": {
77
+ "eslint-config-treesitter": {
78
+ "eslint-plugin-jsdoc": "^62.9.0"
79
+ }
74
80
  },
75
81
  "tree-sitter": [
76
82
  {