@dekobon/tree-sitter-groovy 0.2.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/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +187 -0
- package/binding.gyp +21 -0
- package/bindings/node/binding.cc +19 -0
- package/bindings/node/binding_test.js +24 -0
- package/bindings/node/index.d.ts +56 -0
- package/bindings/node/index.js +27 -0
- package/grammar.js +1607 -0
- package/package.json +88 -0
- package/prebuilds/darwin-arm64/@dekobon+tree-sitter-groovy.node +0 -0
- package/prebuilds/linux-x64/@dekobon+tree-sitter-groovy.node +0 -0
- package/prebuilds/win32-x64/@dekobon+tree-sitter-groovy.node +0 -0
- package/queries/groovy/folds.scm +18 -0
- package/queries/groovy/highlights.scm +235 -0
- package/queries/groovy/indents.scm +35 -0
- package/queries/groovy/injections.scm +38 -0
- package/queries/groovy/locals.scm +32 -0
- package/queries/groovy/tags.scm +67 -0
- package/src/grammar.json +6527 -0
- package/src/node-types.json +16643 -0
- package/src/parser.c +147303 -0
- package/src/scanner.c +213 -0
- package/src/tree_sitter/alloc.h +54 -0
- package/src/tree_sitter/array.h +291 -0
- package/src/tree_sitter/parser.h +286 -0
- package/tree-sitter.json +38 -0
package/grammar.js
ADDED
|
@@ -0,0 +1,1607 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Apache Groovy grammar for tree-sitter
|
|
3
|
+
* @author Elijah Zupancic <elijah@zupancic.name>
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* The authoritative design document for this grammar is
|
|
7
|
+
* SPECIFICATION.md at the repository root. Every rule below maps to
|
|
8
|
+
* a section there. Read the spec before making non-trivial changes.
|
|
9
|
+
*
|
|
10
|
+
* The external scanner (src/scanner.c) handles four context-sensitive
|
|
11
|
+
* lexing situations that pure grammar rules cannot disambiguate:
|
|
12
|
+
* - Slashy-string-start vs. division
|
|
13
|
+
* - Automatic-semicolon (newline-as-terminator)
|
|
14
|
+
* - Labelled-statement colon
|
|
15
|
+
* - Block-comment / Groovydoc single-token emit
|
|
16
|
+
* See SPECIFICATION.md §6.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/// <reference types="tree-sitter-cli/dsl" />
|
|
20
|
+
// @ts-check
|
|
21
|
+
|
|
22
|
+
/* eslint-disable no-multi-spaces --
|
|
23
|
+
* The aligned trailing comments make the Apache-mirror precedence
|
|
24
|
+
* ladder readable at a glance.
|
|
25
|
+
*/
|
|
26
|
+
const PREC = {
|
|
27
|
+
// Higher number = tighter binding in tree-sitter.
|
|
28
|
+
// Mirrors apache/groovy:GroovyParser.g4 `expression` rule.
|
|
29
|
+
// See SPECIFICATION.md §3.1.
|
|
30
|
+
ASSIGN: 1, // = += -= *= /= %= **= <<= >>= >>>= &= ^= |= ?=
|
|
31
|
+
CONDITIONAL: 2, // ?: (Elvis) and ? : (ternary) — same level
|
|
32
|
+
IMPLICATION: 3, // ==>
|
|
33
|
+
LOGICAL_OR: 4, // ||
|
|
34
|
+
LOGICAL_AND: 5, // &&
|
|
35
|
+
BITWISE_OR: 6, // |
|
|
36
|
+
BITWISE_XOR: 7, // ^
|
|
37
|
+
BITWISE_AND: 8, // &
|
|
38
|
+
EQUALITY: 9, // == != <=> === !== =~ ==~
|
|
39
|
+
RELATIONAL: 10, // < <= > >= in !in instanceof !instanceof as
|
|
40
|
+
SHIFT_OR_RANGE: 11, // << >> >>> .. ..< <.. <..<
|
|
41
|
+
ADDITIVE: 12, // + -
|
|
42
|
+
MULTIPLICATIVE: 13, // * / %
|
|
43
|
+
UNARY: 14, // prefix ++ -- + - (Apache level 3 UNARY_ADD)
|
|
44
|
+
POWER: 15, // ** (Apache level 2)
|
|
45
|
+
UNARY_NOT: 16, // ! ~ (Apache level 1, tighter than POWER)
|
|
46
|
+
POSTFIX: 17, // postfix ++ --, []
|
|
47
|
+
ACCESS: 18, // . ?. ??. *. .& .@ ::
|
|
48
|
+
PRIMARY: 19, // literals, identifiers, ()
|
|
49
|
+
// Type-context only — never competes with the expression ladder
|
|
50
|
+
// above. Shares CONDITIONAL's numeric value by accident; the two
|
|
51
|
+
// do not collide because `_type` and `_expression` reach the
|
|
52
|
+
// resolver through disjoint parse stacks.
|
|
53
|
+
ARRAY_TYPE: 2,
|
|
54
|
+
};
|
|
55
|
+
/* eslint-enable no-multi-spaces */
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* GString body segment choice shared between `"..."` and `"""..."""`.
|
|
59
|
+
* The text rule differs (single-quoted bans newlines, triple-quoted
|
|
60
|
+
* allows them) but every other alternative is identical.
|
|
61
|
+
*
|
|
62
|
+
* @param {GrammarSymbols<string>} $ Grammar reference symbols.
|
|
63
|
+
* @param {RuleOrLiteral} textRule The flavour-specific literal-text rule.
|
|
64
|
+
* @returns {ChoiceRule} The shared body-segment choice.
|
|
65
|
+
*/
|
|
66
|
+
function gstringPart($, textRule) {
|
|
67
|
+
return choice(
|
|
68
|
+
alias(textRule, $.string_fragment),
|
|
69
|
+
$.gstring_dollar_interpolation,
|
|
70
|
+
$.gstring_brace_interpolation,
|
|
71
|
+
alias($._gstring_literal_dollar, $.string_fragment),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = grammar({
|
|
76
|
+
name: 'groovy',
|
|
77
|
+
|
|
78
|
+
// Placeholder for the real implementation. SPECIFICATION.md §3-§7
|
|
79
|
+
// is the contract this rule set will fulfil.
|
|
80
|
+
word: $ => $.identifier,
|
|
81
|
+
|
|
82
|
+
extras: $ => [
|
|
83
|
+
/\s/,
|
|
84
|
+
$.line_comment,
|
|
85
|
+
$.block_comment,
|
|
86
|
+
$.groovydoc_comment,
|
|
87
|
+
],
|
|
88
|
+
|
|
89
|
+
// Externals listed here must have a matching branch in
|
|
90
|
+
// `src/scanner.c` AND be referenced by at least one rule below.
|
|
91
|
+
// Tokens the spec §6 proposes as external scanner branches but
|
|
92
|
+
// that this grammar implements in-grammar (GString interpolation
|
|
93
|
+
// via regex tokens, slashy body via in-grammar `_slashy_text`,
|
|
94
|
+
// statement boundaries via `repeat($._statement)`, label colon
|
|
95
|
+
// via grammar precedence) are intentionally NOT listed — listing
|
|
96
|
+
// an unwired external token would let dormant scanner branches
|
|
97
|
+
// mis-fire in error-recovery states.
|
|
98
|
+
externals: $ => [
|
|
99
|
+
$._slashy_string_start,
|
|
100
|
+
$.line_comment,
|
|
101
|
+
$.block_comment,
|
|
102
|
+
$.groovydoc_comment,
|
|
103
|
+
],
|
|
104
|
+
|
|
105
|
+
conflicts: $ => [
|
|
106
|
+
// See SPECIFICATION.md §7. Populated as rules accumulate.
|
|
107
|
+
// (list_literal vs map_literal — tree-sitter resolves without
|
|
108
|
+
// an explicit conflict because `:` after the first element
|
|
109
|
+
// always disambiguates within one token of lookahead.)
|
|
110
|
+
//
|
|
111
|
+
// §5.15 — inside `( … )` an identifier could be either an
|
|
112
|
+
// _expression (parenthesized_expression) or a _type
|
|
113
|
+
// (parenthesized_type_cast). Tree-sitter explores both via
|
|
114
|
+
// this conflict; the `)` plus the next token disambiguates.
|
|
115
|
+
[$._expression, $._type],
|
|
116
|
+
|
|
117
|
+
// §4 — `a.b.c` could be a field_access chain (expression
|
|
118
|
+
// context) or a qualified_type (type context). Same family
|
|
119
|
+
// as the _expression / _type conflict above but the implicit
|
|
120
|
+
// qualified_type_repeat1 aux symbol forces a separate
|
|
121
|
+
// declaration.
|
|
122
|
+
[$._expression, $.qualified_type],
|
|
123
|
+
|
|
124
|
+
// §3.2.2 — `_cast_type` overlaps `_type` on identifier / array /
|
|
125
|
+
// generic alternatives; only the dotted path diverges. The
|
|
126
|
+
// companion three-way conflict pulls in `_dotted_type` so the
|
|
127
|
+
// dotted shape can win the trailing `.identifier` from the
|
|
128
|
+
// surrounding `field_access` reduction.
|
|
129
|
+
[$._cast_type, $._type],
|
|
130
|
+
[$._cast_type, $._dotted_type, $.qualified_type],
|
|
131
|
+
|
|
132
|
+
// §4 — `for ( identifier …` could continue as a for_statement
|
|
133
|
+
// (identifier is part of the init expression) or as a
|
|
134
|
+
// for_in_statement (identifier is the loop variable). The
|
|
135
|
+
// token after the identifier (`;` / operator vs `in`)
|
|
136
|
+
// disambiguates.
|
|
137
|
+
[$.for_in_statement, $._expression],
|
|
138
|
+
|
|
139
|
+
// §5.2 — `def identifier` could either be a
|
|
140
|
+
// local_variable_declaration (variable_declarator with no
|
|
141
|
+
// initializer) or the start of a method_declaration
|
|
142
|
+
// (_property_name + parameters). The `(` after the name
|
|
143
|
+
// decides; the conflict keeps both alternatives alive until
|
|
144
|
+
// then.
|
|
145
|
+
[$.variable_declarator, $._property_name],
|
|
146
|
+
],
|
|
147
|
+
|
|
148
|
+
rules: {
|
|
149
|
+
// Top-level. Real rule set lands incrementally per
|
|
150
|
+
// SPECIFICATION.md §4 (statement and declaration coverage).
|
|
151
|
+
source_file: $ => seq(
|
|
152
|
+
optional($.shebang),
|
|
153
|
+
repeat($._statement),
|
|
154
|
+
),
|
|
155
|
+
|
|
156
|
+
// §4 — `#!/usr/bin/env groovy` line at file start. Single
|
|
157
|
+
// token consumed by the in-grammar lexer.
|
|
158
|
+
shebang: _ => token(seq('#!', /[^\n]*/)),
|
|
159
|
+
|
|
160
|
+
// §4 — statement-level alternatives. Declarations and the rest
|
|
161
|
+
// of control flow fill out this choice in subsequent iterations.
|
|
162
|
+
// `block` is intentionally NOT here — a top-level `{ ... }`
|
|
163
|
+
// continues to parse as an expression_statement-wrapped closure
|
|
164
|
+
// (the historically intended form for Groovy scripts). Blocks
|
|
165
|
+
// appear explicitly in the body slot of `if` / `while` / etc.
|
|
166
|
+
_statement: $ => choice(
|
|
167
|
+
$.expression_statement,
|
|
168
|
+
$.if_statement,
|
|
169
|
+
$.while_statement,
|
|
170
|
+
$.do_while_statement,
|
|
171
|
+
$.for_statement,
|
|
172
|
+
$.for_in_statement,
|
|
173
|
+
$.return_statement,
|
|
174
|
+
$.break_statement,
|
|
175
|
+
$.continue_statement,
|
|
176
|
+
$.throw_statement,
|
|
177
|
+
$.assert_statement,
|
|
178
|
+
$.yield_statement,
|
|
179
|
+
$.try_statement,
|
|
180
|
+
$.class_declaration,
|
|
181
|
+
$.trait_declaration,
|
|
182
|
+
$.interface_declaration,
|
|
183
|
+
$.annotation_type_declaration,
|
|
184
|
+
$.enum_declaration,
|
|
185
|
+
$.record_declaration,
|
|
186
|
+
$.package_declaration,
|
|
187
|
+
$.import_declaration,
|
|
188
|
+
$.local_variable_declaration,
|
|
189
|
+
$.multi_assignment_declaration,
|
|
190
|
+
$.pipeline_statement,
|
|
191
|
+
$.method_declaration,
|
|
192
|
+
$.labeled_statement,
|
|
193
|
+
$.command_chain,
|
|
194
|
+
),
|
|
195
|
+
|
|
196
|
+
// §5.5 — command_chain is the parenthesis-free method-call
|
|
197
|
+
// form (`println 'hello'`, `events 'a', 'b', 'c'`). v1 is
|
|
198
|
+
// intentionally conservative: receiver is a bare identifier
|
|
199
|
+
// and arguments are restricted to literal-shaped expressions
|
|
200
|
+
// and closures (no risk of mis-parsing `foo bar` as a command
|
|
201
|
+
// call on a bare identifier). Multi-argument lists are supported
|
|
202
|
+
// (comma-separated literal arguments per Gradle / Jenkins DSL
|
|
203
|
+
// idioms). Chained `foo bar baz` and complex argument shapes
|
|
204
|
+
// land later. Closes a subset of murtaza64 #5.
|
|
205
|
+
command_chain: $ => prec(2, seq(
|
|
206
|
+
field('receiver', $.identifier),
|
|
207
|
+
field('argument', $._command_argument),
|
|
208
|
+
repeat(seq(',', field('argument', $._command_argument))),
|
|
209
|
+
)),
|
|
210
|
+
|
|
211
|
+
_command_argument: $ => choice(
|
|
212
|
+
$.string_literal,
|
|
213
|
+
$.number_literal,
|
|
214
|
+
$.boolean_literal,
|
|
215
|
+
$.null_literal,
|
|
216
|
+
$.closure,
|
|
217
|
+
),
|
|
218
|
+
|
|
219
|
+
// §5.3 — `label: stmt`. Required only at statement-start
|
|
220
|
+
// position; map_entries (which share `:`) are inside `[…]`
|
|
221
|
+
// and don't collide here. `prec(1, ...)` keeps both
|
|
222
|
+
// labeled_statement and expression_statement alive after
|
|
223
|
+
// `identifier`; the `:` decides. A scanner-token form
|
|
224
|
+
// (`_label_colon`) was attempted but interacted with
|
|
225
|
+
// not-yet-implemented named-argument command chains
|
|
226
|
+
// (`archiveArtifacts artifacts: 'x'`); see divergence #5.
|
|
227
|
+
labeled_statement: $ => prec(1, seq(
|
|
228
|
+
field('label', $.identifier),
|
|
229
|
+
':',
|
|
230
|
+
field('statement', $._statement),
|
|
231
|
+
)),
|
|
232
|
+
|
|
233
|
+
// §4 — block of statements, used in control-flow bodies and
|
|
234
|
+
// (eventually) method bodies. Shares `{ ... }` syntax with
|
|
235
|
+
// closure (§5.8). A `prec(1, …)` keeps block preferred at use
|
|
236
|
+
// sites where both `block` and a closure-bearing `_statement`
|
|
237
|
+
// are valid alternatives (e.g. the body slot of `if` / `while`).
|
|
238
|
+
block: $ => prec(1, seq(
|
|
239
|
+
'{',
|
|
240
|
+
repeat($._statement),
|
|
241
|
+
'}',
|
|
242
|
+
)),
|
|
243
|
+
|
|
244
|
+
// §4 — if / else. `prec.right` resolves the dangling-else
|
|
245
|
+
// ambiguity in favour of the closest `if`. Body accepts either
|
|
246
|
+
// an explicit block (preferred) or a single statement.
|
|
247
|
+
if_statement: $ => prec.right(seq(
|
|
248
|
+
'if',
|
|
249
|
+
'(',
|
|
250
|
+
field('condition', $._expression),
|
|
251
|
+
')',
|
|
252
|
+
field('consequence', choice($.block, $._statement)),
|
|
253
|
+
optional(seq(
|
|
254
|
+
'else',
|
|
255
|
+
field('alternative', choice($.block, $._statement)),
|
|
256
|
+
)),
|
|
257
|
+
)),
|
|
258
|
+
|
|
259
|
+
// §4 — while loop. do-while lands separately.
|
|
260
|
+
while_statement: $ => seq(
|
|
261
|
+
'while',
|
|
262
|
+
'(',
|
|
263
|
+
field('condition', $._expression),
|
|
264
|
+
')',
|
|
265
|
+
field('body', choice($.block, $._statement)),
|
|
266
|
+
),
|
|
267
|
+
|
|
268
|
+
// §4 — C-style for. init / condition / update slots all
|
|
269
|
+
// optional; bare `for (;;)` is the infinite-loop idiom.
|
|
270
|
+
// Typed variable declarations in init (`int i = 0`) land with
|
|
271
|
+
// local_variable_declaration in a later iteration.
|
|
272
|
+
for_statement: $ => seq(
|
|
273
|
+
'for',
|
|
274
|
+
'(',
|
|
275
|
+
optional(field('init', $._expression)),
|
|
276
|
+
';',
|
|
277
|
+
optional(field('condition', $._expression)),
|
|
278
|
+
';',
|
|
279
|
+
optional(field('update', $._expression)),
|
|
280
|
+
')',
|
|
281
|
+
field('body', choice($.block, $._statement)),
|
|
282
|
+
),
|
|
283
|
+
|
|
284
|
+
// §4 — do-while. Body is required; condition follows the body.
|
|
285
|
+
do_while_statement: $ => seq(
|
|
286
|
+
'do',
|
|
287
|
+
field('body', choice($.block, $._statement)),
|
|
288
|
+
'while',
|
|
289
|
+
'(',
|
|
290
|
+
field('condition', $._expression),
|
|
291
|
+
')',
|
|
292
|
+
),
|
|
293
|
+
|
|
294
|
+
// §4 — return / break / continue / throw / assert. Until
|
|
295
|
+
// AUTOMATIC_SEMICOLON lands the parser will greedily consume
|
|
296
|
+
// a trailing expression onto `return` / `throw` / `assert`;
|
|
297
|
+
// explicit `;` or statement boundary is recommended in source.
|
|
298
|
+
return_statement: $ => prec.right(seq(
|
|
299
|
+
'return',
|
|
300
|
+
optional($._expression),
|
|
301
|
+
)),
|
|
302
|
+
|
|
303
|
+
break_statement: $ => prec.right(seq(
|
|
304
|
+
'break',
|
|
305
|
+
optional(field('label', $.identifier)),
|
|
306
|
+
)),
|
|
307
|
+
|
|
308
|
+
continue_statement: $ => prec.right(seq(
|
|
309
|
+
'continue',
|
|
310
|
+
optional(field('label', $.identifier)),
|
|
311
|
+
)),
|
|
312
|
+
|
|
313
|
+
throw_statement: $ => seq('throw', $._expression),
|
|
314
|
+
|
|
315
|
+
// §4 — `yield expr` in a Groovy 4 switch expression's case
|
|
316
|
+
// body. `yield` is a contextual keyword (§5.2) so it remains
|
|
317
|
+
// a usable identifier elsewhere — tree-sitter's word:
|
|
318
|
+
// directive resolves the ambiguity.
|
|
319
|
+
yield_statement: $ => prec.right(seq(
|
|
320
|
+
'yield',
|
|
321
|
+
$._expression,
|
|
322
|
+
)),
|
|
323
|
+
|
|
324
|
+
// §4 — power-assert: `assert e` or `assert e : msg`. The colon
|
|
325
|
+
// separator may collide with map_entry in later iterations, but
|
|
326
|
+
// assert is a statement-only construct so the context resolves
|
|
327
|
+
// it.
|
|
328
|
+
assert_statement: $ => prec.right(seq(
|
|
329
|
+
'assert',
|
|
330
|
+
$._expression,
|
|
331
|
+
optional(seq(':', field('message', $._expression))),
|
|
332
|
+
)),
|
|
333
|
+
|
|
334
|
+
// §4 — try / catch / finally. At least one of catch/finally is
|
|
335
|
+
// required per Java/Groovy semantics; the grammar relaxes that
|
|
336
|
+
// to allow a bare `try { … }` and lets downstream tooling
|
|
337
|
+
// surface the error if needed. Try-with-resources lands later
|
|
338
|
+
// with local variable declarations.
|
|
339
|
+
try_statement: $ => seq(
|
|
340
|
+
'try',
|
|
341
|
+
optional(field('resources', $.resource_specification)),
|
|
342
|
+
field('body', $.block),
|
|
343
|
+
repeat($.catch_clause),
|
|
344
|
+
optional($.finally_clause),
|
|
345
|
+
),
|
|
346
|
+
|
|
347
|
+
// §4 — `try (Foo f = expr; Bar b = expr) { … }`. Resources
|
|
348
|
+
// are typed or `def`-style declarators with required `=`
|
|
349
|
+
// initializer (per Java-style try-with-resources). Multiple
|
|
350
|
+
// resources separated by `;`.
|
|
351
|
+
resource_specification: $ => seq(
|
|
352
|
+
'(',
|
|
353
|
+
$.resource,
|
|
354
|
+
repeat(seq(';', $.resource)),
|
|
355
|
+
optional(';'),
|
|
356
|
+
')',
|
|
357
|
+
),
|
|
358
|
+
|
|
359
|
+
resource: $ => seq(
|
|
360
|
+
optional(field('type', $._type)),
|
|
361
|
+
field('name', $.identifier),
|
|
362
|
+
'=',
|
|
363
|
+
field('value', $._expression),
|
|
364
|
+
),
|
|
365
|
+
|
|
366
|
+
catch_clause: $ => seq(
|
|
367
|
+
'catch',
|
|
368
|
+
'(',
|
|
369
|
+
field('parameter', $.catch_formal_parameter),
|
|
370
|
+
')',
|
|
371
|
+
field('body', $.block),
|
|
372
|
+
),
|
|
373
|
+
|
|
374
|
+
// §4 — `catch_formal_parameter` accepts either a single _type
|
|
375
|
+
// or a multi_type for Java 7+ / Groovy multi-catch. Closes
|
|
376
|
+
// murtaza64 #39.
|
|
377
|
+
catch_formal_parameter: $ => seq(
|
|
378
|
+
field('type', choice($._type, $.multi_type)),
|
|
379
|
+
field('name', $.identifier),
|
|
380
|
+
),
|
|
381
|
+
|
|
382
|
+
multi_type: $ => seq(
|
|
383
|
+
$._type,
|
|
384
|
+
repeat1(seq('|', $._type)),
|
|
385
|
+
),
|
|
386
|
+
|
|
387
|
+
finally_clause: $ => seq(
|
|
388
|
+
'finally',
|
|
389
|
+
field('body', $.block),
|
|
390
|
+
),
|
|
391
|
+
|
|
392
|
+
// §4 — class / trait / interface declarations. v1 supports the
|
|
393
|
+
// shape `[keyword] Name { body }`. extends, implements, generics,
|
|
394
|
+
// modifiers, and sealed / permits clauses land in later
|
|
395
|
+
// iterations. trait closes the dekobon #247 trait row.
|
|
396
|
+
// §4 — class declaration with optional Groovy 4 sealed modifier
|
|
397
|
+
// and `permits` clause. The `non-sealed` form is a single
|
|
398
|
+
// anonymous token (token('non-sealed')) so the dash doesn't
|
|
399
|
+
// get lexed as subtraction.
|
|
400
|
+
class_declaration: $ => seq(
|
|
401
|
+
repeat($.annotation),
|
|
402
|
+
repeat($._modifier),
|
|
403
|
+
optional(choice('sealed', token('non-sealed'))),
|
|
404
|
+
'class',
|
|
405
|
+
field('name', $.identifier),
|
|
406
|
+
optional(field('type_parameters', $.type_parameters)),
|
|
407
|
+
optional($.superclass),
|
|
408
|
+
optional($.super_interfaces),
|
|
409
|
+
optional($.permits_clause),
|
|
410
|
+
field('body', $.class_body),
|
|
411
|
+
),
|
|
412
|
+
|
|
413
|
+
trait_declaration: $ => seq(
|
|
414
|
+
repeat($.annotation),
|
|
415
|
+
repeat($._modifier),
|
|
416
|
+
optional(choice('sealed', token('non-sealed'))),
|
|
417
|
+
'trait',
|
|
418
|
+
field('name', $.identifier),
|
|
419
|
+
optional(field('type_parameters', $.type_parameters)),
|
|
420
|
+
optional($.extends_interfaces),
|
|
421
|
+
optional($.permits_clause),
|
|
422
|
+
field('body', $.class_body),
|
|
423
|
+
),
|
|
424
|
+
|
|
425
|
+
interface_declaration: $ => seq(
|
|
426
|
+
repeat($.annotation),
|
|
427
|
+
repeat($._modifier),
|
|
428
|
+
optional(choice('sealed', token('non-sealed'))),
|
|
429
|
+
'interface',
|
|
430
|
+
field('name', $.identifier),
|
|
431
|
+
optional(field('type_parameters', $.type_parameters)),
|
|
432
|
+
optional($.extends_interfaces),
|
|
433
|
+
optional($.permits_clause),
|
|
434
|
+
field('body', $.class_body),
|
|
435
|
+
),
|
|
436
|
+
|
|
437
|
+
// §4 / §5.14 — generic type parameter list on a class /
|
|
438
|
+
// interface / trait / method declaration. The `<` is
|
|
439
|
+
// `token.immediate` so the parser only treats it as a type
|
|
440
|
+
// parameter opener when it follows the declaration name with
|
|
441
|
+
// no intervening whitespace.
|
|
442
|
+
type_parameters: $ => seq(
|
|
443
|
+
token.immediate('<'),
|
|
444
|
+
$.type_parameter,
|
|
445
|
+
repeat(seq(',', $.type_parameter)),
|
|
446
|
+
'>',
|
|
447
|
+
),
|
|
448
|
+
|
|
449
|
+
type_parameter: $ => seq(
|
|
450
|
+
field('name', alias($.identifier, $.type_identifier)),
|
|
451
|
+
optional(seq(
|
|
452
|
+
'extends',
|
|
453
|
+
$._type,
|
|
454
|
+
repeat(seq('&', $._type)),
|
|
455
|
+
)),
|
|
456
|
+
),
|
|
457
|
+
|
|
458
|
+
// §4 — class has a single super and zero-or-more interfaces.
|
|
459
|
+
superclass: $ => seq('extends', $._type),
|
|
460
|
+
|
|
461
|
+
super_interfaces: $ => seq(
|
|
462
|
+
'implements',
|
|
463
|
+
$._type,
|
|
464
|
+
repeat(seq(',', $._type)),
|
|
465
|
+
),
|
|
466
|
+
|
|
467
|
+
// §4 — trait / interface extend multiple interfaces (no
|
|
468
|
+
// `implements` keyword in that position).
|
|
469
|
+
extends_interfaces: $ => seq(
|
|
470
|
+
'extends',
|
|
471
|
+
$._type,
|
|
472
|
+
repeat(seq(',', $._type)),
|
|
473
|
+
),
|
|
474
|
+
|
|
475
|
+
// §4 — Java-style modifiers. Each one is a hard reserved
|
|
476
|
+
// keyword (§5.2) so the `word: $.identifier` directive keeps
|
|
477
|
+
// them from accidentally being treated as identifiers.
|
|
478
|
+
_modifier: _ => choice(
|
|
479
|
+
'public',
|
|
480
|
+
'private',
|
|
481
|
+
'protected',
|
|
482
|
+
'static',
|
|
483
|
+
'final',
|
|
484
|
+
'abstract',
|
|
485
|
+
'synchronized',
|
|
486
|
+
'native',
|
|
487
|
+
'transient',
|
|
488
|
+
'volatile',
|
|
489
|
+
'strictfp',
|
|
490
|
+
),
|
|
491
|
+
|
|
492
|
+
permits_clause: $ => seq(
|
|
493
|
+
'permits',
|
|
494
|
+
$._type,
|
|
495
|
+
repeat(seq(',', $._type)),
|
|
496
|
+
),
|
|
497
|
+
|
|
498
|
+
// §4 — `@interface Foo { … }` declares an annotation type.
|
|
499
|
+
// `@` followed by the `interface` keyword is unambiguous —
|
|
500
|
+
// `interface` is a hard reserved keyword (§5.2) so the
|
|
501
|
+
// alternative parse `annotation` (which needs identifier)
|
|
502
|
+
// cannot fire here.
|
|
503
|
+
annotation_type_declaration: $ => seq(
|
|
504
|
+
repeat($.annotation),
|
|
505
|
+
'@',
|
|
506
|
+
'interface',
|
|
507
|
+
field('name', $.identifier),
|
|
508
|
+
field('body', $.class_body),
|
|
509
|
+
),
|
|
510
|
+
|
|
511
|
+
// §4 — `{ method* }` for v1. Field declarations, static
|
|
512
|
+
// initialisers, and inner classes land later.
|
|
513
|
+
class_body: $ => seq(
|
|
514
|
+
'{',
|
|
515
|
+
repeat($._class_member),
|
|
516
|
+
'}',
|
|
517
|
+
),
|
|
518
|
+
|
|
519
|
+
_class_member: $ => choice(
|
|
520
|
+
$.method_declaration,
|
|
521
|
+
$.field_declaration,
|
|
522
|
+
$.constructor_declaration,
|
|
523
|
+
$.static_initializer,
|
|
524
|
+
),
|
|
525
|
+
|
|
526
|
+
// §4 — constructors share the method_declaration shape but have
|
|
527
|
+
// no return type and the name matches the enclosing class. We
|
|
528
|
+
// don't enforce the name match (downstream tooling can), but we
|
|
529
|
+
// emit a distinct node kind so IDE features (rename, jump) can
|
|
530
|
+
// tell constructors apart from methods. `prec(3, …)` wins over
|
|
531
|
+
// both field and method when the identifier is followed by `(`
|
|
532
|
+
// and no return type precedes it.
|
|
533
|
+
constructor_declaration: $ => prec.right(3, seq(
|
|
534
|
+
repeat($.annotation),
|
|
535
|
+
repeat($._modifier),
|
|
536
|
+
field('name', $.identifier),
|
|
537
|
+
field('parameters', $.formal_parameters),
|
|
538
|
+
optional($.throws_clause),
|
|
539
|
+
field('body', $.block),
|
|
540
|
+
)),
|
|
541
|
+
|
|
542
|
+
// §4 — `static { … }` class-body initializer block. Runs once
|
|
543
|
+
// when the class is loaded. Distinct node kind so downstream
|
|
544
|
+
// tools can index it.
|
|
545
|
+
static_initializer: $ => seq(
|
|
546
|
+
'static',
|
|
547
|
+
field('body', $.block),
|
|
548
|
+
),
|
|
549
|
+
|
|
550
|
+
// §4 — class / interface / trait field. Java-style shape: optional
|
|
551
|
+
// annotations, optional modifiers, `def` or a `_type`, then one or
|
|
552
|
+
// more declarators (each with optional initializer). Method
|
|
553
|
+
// declarations share the same prefix; `prec(2, …)` on
|
|
554
|
+
// `method_declaration` and the trailing `(` discriminate.
|
|
555
|
+
field_declaration: $ => seq(
|
|
556
|
+
repeat($.annotation),
|
|
557
|
+
repeat($._modifier),
|
|
558
|
+
choice('def', field('type', $._type)),
|
|
559
|
+
$.variable_declarator,
|
|
560
|
+
repeat(seq(',', $.variable_declarator)),
|
|
561
|
+
),
|
|
562
|
+
|
|
563
|
+
// §4 — `def name(params) body`. Typed return type (`String foo()
|
|
564
|
+
// { … }`) lands once we can disambiguate from local variable
|
|
565
|
+
// declaration. Modifiers and throws clause similarly defer.
|
|
566
|
+
// prec(1) wins over local_variable_declaration when `(` follows
|
|
567
|
+
// the name, so `def f() { … }` at script-top-level parses as a
|
|
568
|
+
// method rather than a `def f` variable followed by `(…) { … }`.
|
|
569
|
+
method_declaration: $ => prec.right(2, seq(
|
|
570
|
+
repeat($.annotation),
|
|
571
|
+
repeat($._modifier),
|
|
572
|
+
optional(field('type_parameters', $.method_type_parameters)),
|
|
573
|
+
choice('def', field('return_type', $._type)),
|
|
574
|
+
field('name', $._property_name),
|
|
575
|
+
field('parameters', $.formal_parameters),
|
|
576
|
+
optional($.throws_clause),
|
|
577
|
+
optional(field('body', $.block)),
|
|
578
|
+
)),
|
|
579
|
+
|
|
580
|
+
// §4 / §5.14 — method-level generic type parameters
|
|
581
|
+
// (`<T> T identity(T x) { x }`). Distinct from `type_parameters`
|
|
582
|
+
// (used on class/trait/interface) because here the `<` cannot be
|
|
583
|
+
// `token.immediate` — it appears at the start of the declaration
|
|
584
|
+
// with optional whitespace from preceding modifiers / annotations.
|
|
585
|
+
method_type_parameters: $ => seq(
|
|
586
|
+
'<',
|
|
587
|
+
$.type_parameter,
|
|
588
|
+
repeat(seq(',', $.type_parameter)),
|
|
589
|
+
'>',
|
|
590
|
+
),
|
|
591
|
+
|
|
592
|
+
// §4 — `throws E1, E2, …` after a method signature.
|
|
593
|
+
throws_clause: $ => prec.left(seq(
|
|
594
|
+
'throws',
|
|
595
|
+
$._type,
|
|
596
|
+
repeat(seq(',', $._type)),
|
|
597
|
+
)),
|
|
598
|
+
|
|
599
|
+
formal_parameters: $ => seq(
|
|
600
|
+
'(',
|
|
601
|
+
optional(seq(
|
|
602
|
+
$.formal_parameter,
|
|
603
|
+
repeat(seq(',', $.formal_parameter)),
|
|
604
|
+
)),
|
|
605
|
+
')',
|
|
606
|
+
),
|
|
607
|
+
|
|
608
|
+
// §5.13 — formal parameter with optional type, optional
|
|
609
|
+
// varargs marker (`Type... name`), and optional default value
|
|
610
|
+
// (`name = expr`).
|
|
611
|
+
formal_parameter: $ => seq(
|
|
612
|
+
optional(field('type', choice($._type, $.varargs_type))),
|
|
613
|
+
field('name', $.identifier),
|
|
614
|
+
optional(seq('=', field('default', $._expression))),
|
|
615
|
+
),
|
|
616
|
+
|
|
617
|
+
varargs_type: $ => seq($._type, '...'),
|
|
618
|
+
|
|
619
|
+
// §4 — enum. Closes murtaza64 #36 first half. Constants list
|
|
620
|
+
// (optionally trailing-comma) optionally followed by `;` and
|
|
621
|
+
// further class members. Constants may carry constructor
|
|
622
|
+
// arguments (`RED(0xff0000)`); v1 supports that via the
|
|
623
|
+
// optional argument_list.
|
|
624
|
+
enum_declaration: $ => seq(
|
|
625
|
+
repeat($.annotation),
|
|
626
|
+
'enum',
|
|
627
|
+
field('name', $.identifier),
|
|
628
|
+
field('body', $.enum_body),
|
|
629
|
+
),
|
|
630
|
+
|
|
631
|
+
enum_body: $ => seq(
|
|
632
|
+
'{',
|
|
633
|
+
optional(seq(
|
|
634
|
+
$.enum_constant,
|
|
635
|
+
repeat(seq(',', $.enum_constant)),
|
|
636
|
+
optional(','),
|
|
637
|
+
)),
|
|
638
|
+
optional(seq(';', repeat($._class_member))),
|
|
639
|
+
'}',
|
|
640
|
+
),
|
|
641
|
+
|
|
642
|
+
enum_constant: $ => seq(
|
|
643
|
+
field('name', $.identifier),
|
|
644
|
+
optional(field('arguments', $.argument_list)),
|
|
645
|
+
),
|
|
646
|
+
|
|
647
|
+
// §4 — record (Groovy 4+). `record Name(Type a, Type b) [{ body }]`.
|
|
648
|
+
// `record` is a contextual keyword — tree-sitter's word: directive
|
|
649
|
+
// keeps `record` usable as a plain identifier elsewhere.
|
|
650
|
+
record_declaration: $ => prec.right(seq(
|
|
651
|
+
repeat($.annotation),
|
|
652
|
+
'record',
|
|
653
|
+
field('name', $.identifier),
|
|
654
|
+
field('components', $.record_components),
|
|
655
|
+
optional(field('body', $.class_body)),
|
|
656
|
+
)),
|
|
657
|
+
|
|
658
|
+
record_components: $ => seq(
|
|
659
|
+
'(',
|
|
660
|
+
optional(seq(
|
|
661
|
+
$.record_component,
|
|
662
|
+
repeat(seq(',', $.record_component)),
|
|
663
|
+
)),
|
|
664
|
+
')',
|
|
665
|
+
),
|
|
666
|
+
|
|
667
|
+
record_component: $ => seq(
|
|
668
|
+
field('type', $._type),
|
|
669
|
+
field('name', $.identifier),
|
|
670
|
+
),
|
|
671
|
+
|
|
672
|
+
// §4 — package and import. Trailing `;` is optional in Groovy;
|
|
673
|
+
// we accept both forms by not requiring it.
|
|
674
|
+
package_declaration: $ => seq(
|
|
675
|
+
'package',
|
|
676
|
+
field('name', $.qualified_name),
|
|
677
|
+
),
|
|
678
|
+
|
|
679
|
+
// §4 — `import [static] qualified.Name [.*] [as Alias]`.
|
|
680
|
+
// `.*` is a single anonymous token so it lexes distinctly from
|
|
681
|
+
// a continuation of qualified_name (`.identifier`). Wildcard
|
|
682
|
+
// imports only appear in this position, so the lexer-wide
|
|
683
|
+
// longest-match is safe.
|
|
684
|
+
import_declaration: $ => seq(
|
|
685
|
+
'import',
|
|
686
|
+
optional('static'),
|
|
687
|
+
field('name', $.qualified_name),
|
|
688
|
+
optional(token('.*')),
|
|
689
|
+
optional(seq('as', field('alias', $.identifier))),
|
|
690
|
+
),
|
|
691
|
+
|
|
692
|
+
qualified_name: $ => seq(
|
|
693
|
+
$.identifier,
|
|
694
|
+
repeat(seq('.', $.identifier)),
|
|
695
|
+
),
|
|
696
|
+
|
|
697
|
+
// §4 / §5.12 — `def x = …`, `var x = …`, and `Type x = …`.
|
|
698
|
+
// The typed form requires every declarator to carry an
|
|
699
|
+
// `= value` initializer; bare `Type x` without initializer
|
|
700
|
+
// would conflict with method_invocation `Type(x)` and is
|
|
701
|
+
// disallowed in v1. Multiple declarators per statement
|
|
702
|
+
// (`def a = 1, b = 2`) are supported in all three forms.
|
|
703
|
+
local_variable_declaration: $ => prec.right(choice(
|
|
704
|
+
seq(
|
|
705
|
+
choice('def', 'var'),
|
|
706
|
+
$.variable_declarator,
|
|
707
|
+
repeat(seq(',', $.variable_declarator)),
|
|
708
|
+
),
|
|
709
|
+
seq(
|
|
710
|
+
field('type', $._type),
|
|
711
|
+
$._initialized_declarator,
|
|
712
|
+
repeat(seq(',', $._initialized_declarator)),
|
|
713
|
+
),
|
|
714
|
+
)),
|
|
715
|
+
|
|
716
|
+
_initialized_declarator: $ => seq(
|
|
717
|
+
field('name', $.identifier),
|
|
718
|
+
'=',
|
|
719
|
+
field('value', $._expression),
|
|
720
|
+
),
|
|
721
|
+
|
|
722
|
+
variable_declarator: $ => seq(
|
|
723
|
+
field('name', $.identifier),
|
|
724
|
+
optional(seq('=', field('value', $._expression))),
|
|
725
|
+
),
|
|
726
|
+
|
|
727
|
+
// §4 — `def (a, b) = expr`. Closes murtaza64 #22. v1 requires
|
|
728
|
+
// the leading `def`; the no-`def` form `(a, b) = expr` lands
|
|
729
|
+
// later (it shares a prefix with parenthesized_expression and
|
|
730
|
+
// needs a conflict declaration).
|
|
731
|
+
multi_assignment_declaration: $ => seq(
|
|
732
|
+
'def',
|
|
733
|
+
'(',
|
|
734
|
+
$.variable_declarator,
|
|
735
|
+
repeat(seq(',', $.variable_declarator)),
|
|
736
|
+
')',
|
|
737
|
+
'=',
|
|
738
|
+
field('value', $._expression),
|
|
739
|
+
),
|
|
740
|
+
|
|
741
|
+
// §4 / §10 row #37 — Jenkins-style `pipeline { … }` block.
|
|
742
|
+
// Closes murtaza64 #37 (pipeline as a regular statement, not
|
|
743
|
+
// a file-trailer-only construct). The body is a closure, since
|
|
744
|
+
// that's how Jenkins's DSL is shaped — `pipeline` is followed
|
|
745
|
+
// by a closure literal with nested stages, steps, etc.
|
|
746
|
+
pipeline_statement: $ => seq(
|
|
747
|
+
'pipeline',
|
|
748
|
+
field('body', $.closure),
|
|
749
|
+
),
|
|
750
|
+
|
|
751
|
+
// §5.9 — annotation usage. Lands on its own here; class /
|
|
752
|
+
// method / record / enum declarations gain an optional
|
|
753
|
+
// leading `repeat($.annotation)` so a `@Foo @Bar class X { }`
|
|
754
|
+
// attaches the annotations to the class node.
|
|
755
|
+
annotation: $ => seq(
|
|
756
|
+
'@',
|
|
757
|
+
field('name', $.qualified_name),
|
|
758
|
+
optional(field('arguments', $.argument_list)),
|
|
759
|
+
),
|
|
760
|
+
|
|
761
|
+
// §4 — `for (x in xs)`. Untyped variable for v1. Typed
|
|
762
|
+
// (`for (Type x in xs)`) lands later. The `in` keyword here
|
|
763
|
+
// is a structural delimiter, not the membership_expression
|
|
764
|
+
// operator — context disambiguates because the for-header is
|
|
765
|
+
// not an expression position.
|
|
766
|
+
for_in_statement: $ => seq(
|
|
767
|
+
'for',
|
|
768
|
+
'(',
|
|
769
|
+
field('variable', $.identifier),
|
|
770
|
+
'in',
|
|
771
|
+
field('value', $._expression),
|
|
772
|
+
')',
|
|
773
|
+
field('body', choice($.block, $._statement)),
|
|
774
|
+
),
|
|
775
|
+
|
|
776
|
+
expression_statement: $ => $._expression,
|
|
777
|
+
|
|
778
|
+
// §3.2 PRIMARY — minimal alternative set for now. Every primary
|
|
779
|
+
// expression form from §3.2 / §5 will land in this choice over
|
|
780
|
+
// subsequent iterations.
|
|
781
|
+
_expression: $ => choice(
|
|
782
|
+
$.identifier,
|
|
783
|
+
$.number_literal,
|
|
784
|
+
$.string_literal,
|
|
785
|
+
$.boolean_literal,
|
|
786
|
+
$.null_literal,
|
|
787
|
+
$.parenthesized_expression,
|
|
788
|
+
$.unary_expression,
|
|
789
|
+
$.list_literal,
|
|
790
|
+
$.map_literal,
|
|
791
|
+
$.closure,
|
|
792
|
+
$.binary_expression,
|
|
793
|
+
$.range_expression,
|
|
794
|
+
$.identity_expression,
|
|
795
|
+
$.spaceship_expression,
|
|
796
|
+
$.regex_find_expression,
|
|
797
|
+
$.regex_match_expression,
|
|
798
|
+
$.ternary_expression,
|
|
799
|
+
$.elvis_expression,
|
|
800
|
+
$.logical_implication_expression,
|
|
801
|
+
$.assignment_expression,
|
|
802
|
+
$.power_expression,
|
|
803
|
+
$.subscript_expression,
|
|
804
|
+
$.safe_subscript_expression,
|
|
805
|
+
$.method_invocation,
|
|
806
|
+
$.update_expression,
|
|
807
|
+
$.unary_update_expression,
|
|
808
|
+
$.field_access,
|
|
809
|
+
$.safe_navigation_expression,
|
|
810
|
+
$.safe_chain_dot_expression,
|
|
811
|
+
$.spread_dot_expression,
|
|
812
|
+
$.method_pointer_expression,
|
|
813
|
+
$.direct_field_access_expression,
|
|
814
|
+
$.method_reference_expression,
|
|
815
|
+
$.object_creation_expression,
|
|
816
|
+
$.parenthesized_type_cast,
|
|
817
|
+
$.cast_expression,
|
|
818
|
+
$.membership_expression,
|
|
819
|
+
$.instanceof_expression,
|
|
820
|
+
$.switch_expression,
|
|
821
|
+
),
|
|
822
|
+
|
|
823
|
+
// §4 — Groovy 4 switch-expression. Single rule for both classic
|
|
824
|
+
// `case … :` and arrow `case … -> …` branches; arrow form
|
|
825
|
+
// closes murtaza64 #36 (second half). Used as a statement via
|
|
826
|
+
// expression_statement, or as an expression directly per the
|
|
827
|
+
// §8.2 #36 regression.
|
|
828
|
+
switch_expression: $ => seq(
|
|
829
|
+
'switch',
|
|
830
|
+
'(',
|
|
831
|
+
field('value', $._expression),
|
|
832
|
+
')',
|
|
833
|
+
field('body', $.switch_block),
|
|
834
|
+
),
|
|
835
|
+
|
|
836
|
+
switch_block: $ => seq(
|
|
837
|
+
'{',
|
|
838
|
+
repeat(choice(
|
|
839
|
+
$.switch_case,
|
|
840
|
+
$.switch_arrow_case,
|
|
841
|
+
$.switch_default,
|
|
842
|
+
)),
|
|
843
|
+
'}',
|
|
844
|
+
),
|
|
845
|
+
|
|
846
|
+
switch_case: $ => seq(
|
|
847
|
+
'case',
|
|
848
|
+
field('value', $._expression),
|
|
849
|
+
':',
|
|
850
|
+
repeat($._statement),
|
|
851
|
+
),
|
|
852
|
+
|
|
853
|
+
switch_arrow_case: $ => seq(
|
|
854
|
+
'case',
|
|
855
|
+
field('value', $._expression),
|
|
856
|
+
'->',
|
|
857
|
+
field('body', choice($.block, $._statement)),
|
|
858
|
+
),
|
|
859
|
+
|
|
860
|
+
switch_default: $ => seq(
|
|
861
|
+
'default',
|
|
862
|
+
choice(
|
|
863
|
+
seq(':', repeat($._statement)),
|
|
864
|
+
seq('->', field('body', choice($.block, $._statement))),
|
|
865
|
+
),
|
|
866
|
+
),
|
|
867
|
+
|
|
868
|
+
// §3.2 — `x as Type`. RELATIONAL level (10), left-associative.
|
|
869
|
+
// RHS is `_cast_type`, a `_type`-shaped sibling that swaps in
|
|
870
|
+
// `_dotted_type` for `qualified_type` so the trailing
|
|
871
|
+
// `.identifier` of `x as java.util.Map` extends the type
|
|
872
|
+
// through every dot segment (instead of being claimed by
|
|
873
|
+
// `field_access` at higher precedence than `cast_expression`).
|
|
874
|
+
cast_expression: $ => prec.left(PREC.RELATIONAL, seq(
|
|
875
|
+
field('value', $._expression),
|
|
876
|
+
'as',
|
|
877
|
+
field('type', $._cast_type),
|
|
878
|
+
)),
|
|
879
|
+
|
|
880
|
+
// §3.2.2 — RHS type of `as`, `instanceof`, and `!instanceof`.
|
|
881
|
+
// Same alternatives as `_type` except `qualified_type` is
|
|
882
|
+
// replaced by `_dotted_type`, whose tail shifts each dot at a
|
|
883
|
+
// precedence tighter than `field_access`.
|
|
884
|
+
_cast_type: $ => choice(
|
|
885
|
+
alias($.identifier, $.type_identifier),
|
|
886
|
+
$.array_type,
|
|
887
|
+
alias($._dotted_type, $.qualified_type),
|
|
888
|
+
$.generic_type,
|
|
889
|
+
),
|
|
890
|
+
|
|
891
|
+
_dotted_type: $ => seq(
|
|
892
|
+
alias($.identifier, $.type_identifier),
|
|
893
|
+
$._dotted_type_tail,
|
|
894
|
+
),
|
|
895
|
+
|
|
896
|
+
_dotted_type_tail: $ => prec.right(PREC.ACCESS + 1, seq(
|
|
897
|
+
'.',
|
|
898
|
+
alias($.identifier, $.type_identifier),
|
|
899
|
+
optional($._dotted_type_tail),
|
|
900
|
+
)),
|
|
901
|
+
|
|
902
|
+
// §3.2 — `x in xs` and `x !in xs`. RELATIONAL level. Both
|
|
903
|
+
// operators share one node kind; the `operator` field
|
|
904
|
+
// distinguishes them. `!in` is wrapped in `token(prec(1, …))`
|
|
905
|
+
// so it lexes as a single token despite the embedded space
|
|
906
|
+
// requirement implied by §3.2.2's trailing-context rule —
|
|
907
|
+
// Apache's `NOT_IN` predicate is approximated by requiring at
|
|
908
|
+
// least one trailing whitespace, `[`, `(`, or `{` after `!in`.
|
|
909
|
+
// §3.2 / §3.2.2 — `x in xs` and `x !in xs`. `!in` is a single
|
|
910
|
+
// token whose match requires a trailing whitespace / bracket
|
|
911
|
+
// character (Apache `NOT_IN` predicate) so `a !inXs` does not
|
|
912
|
+
// mis-tokenise as `!in` followed by `Xs`.
|
|
913
|
+
membership_expression: $ => {
|
|
914
|
+
const ops = [
|
|
915
|
+
'in',
|
|
916
|
+
token(prec(1, seq('!in', /[ \t\r\n\[({]/))),
|
|
917
|
+
];
|
|
918
|
+
return choice(...ops.map(op => prec.left(PREC.RELATIONAL, seq(
|
|
919
|
+
field('element', $._expression),
|
|
920
|
+
field('operator', op),
|
|
921
|
+
field('collection', $._expression),
|
|
922
|
+
))));
|
|
923
|
+
},
|
|
924
|
+
|
|
925
|
+
// §3.2 / §3.2.2 — `x instanceof T` and `x !instanceof T`.
|
|
926
|
+
// `!instanceof` is a single token whose match requires a
|
|
927
|
+
// trailing whitespace (Apache `NOT_INSTANCEOF` predicate) so
|
|
928
|
+
// `a !instanceofX` does not mis-tokenise as `!instanceof`
|
|
929
|
+
// followed by identifier `X`.
|
|
930
|
+
instanceof_expression: $ => {
|
|
931
|
+
const ops = [
|
|
932
|
+
'instanceof',
|
|
933
|
+
token(prec(1, seq('!instanceof', /[ \t\r\n]/))),
|
|
934
|
+
];
|
|
935
|
+
return choice(...ops.map(op => prec.left(PREC.RELATIONAL, seq(
|
|
936
|
+
field('value', $._expression),
|
|
937
|
+
field('operator', op),
|
|
938
|
+
field('type', $._cast_type),
|
|
939
|
+
))));
|
|
940
|
+
},
|
|
941
|
+
|
|
942
|
+
// §3.2 / §5.15 — `new Foo(args)`. We only handle the plain
|
|
943
|
+
// constructor-call form here; array creation `new Foo[10]`,
|
|
944
|
+
// generic types, and array initialisers land later. PREC.PRIMARY
|
|
945
|
+
// so it composes with the access tier from the left
|
|
946
|
+
// (`new Foo().bar`).
|
|
947
|
+
object_creation_expression: $ => prec.right(PREC.PRIMARY, seq(
|
|
948
|
+
'new',
|
|
949
|
+
field('type', $._type),
|
|
950
|
+
field('arguments', $.argument_list),
|
|
951
|
+
)),
|
|
952
|
+
|
|
953
|
+
// §5.15 — C-style cast `(Foo) x`. Distinct from
|
|
954
|
+
// `parenthesized_expression` followed by something (resolved
|
|
955
|
+
// by tree-sitter's GLR; declared as a conflict if needed).
|
|
956
|
+
// PREC.UNARY right-associative so `(Foo) -x` parses as
|
|
957
|
+
// `(Foo)(-x)` and the type binds to the rightward expression.
|
|
958
|
+
parenthesized_type_cast: $ => prec.right(PREC.UNARY, seq(
|
|
959
|
+
'(',
|
|
960
|
+
field('type', $._type),
|
|
961
|
+
')',
|
|
962
|
+
field('value', $._expression),
|
|
963
|
+
)),
|
|
964
|
+
|
|
965
|
+
// §4 — `_type`. Supports unqualified type identifiers,
|
|
966
|
+
// qualified types (`java.util.List`), array types
|
|
967
|
+
// (`int[]`, `String[][]`), and generic types (`List<String>`,
|
|
968
|
+
// `Map<String, Integer>`). `generic_type` always carries explicit
|
|
969
|
+
// angle brackets, so it never competes with comparison operators
|
|
970
|
+
// in expression context (which only reach `_type` through a
|
|
971
|
+
// separate parse stack).
|
|
972
|
+
_type: $ => choice(
|
|
973
|
+
alias($.identifier, $.type_identifier),
|
|
974
|
+
$.array_type,
|
|
975
|
+
$.qualified_type,
|
|
976
|
+
$.generic_type,
|
|
977
|
+
),
|
|
978
|
+
|
|
979
|
+
// Near-twin of `_dotted_type` below; the two cannot be merged
|
|
980
|
+
// without breaking either field-access (which needs
|
|
981
|
+
// `qualified_type` to NOT outrank `field_access`) or
|
|
982
|
+
// cast / instanceof RHS (which needs `_dotted_type` to outrank
|
|
983
|
+
// `field_access`). Edits here should be mirrored in
|
|
984
|
+
// `_dotted_type` / `_dotted_type_tail`.
|
|
985
|
+
qualified_type: $ => prec.left(seq(
|
|
986
|
+
alias($.identifier, $.type_identifier),
|
|
987
|
+
repeat1(seq('.', alias($.identifier, $.type_identifier))),
|
|
988
|
+
)),
|
|
989
|
+
|
|
990
|
+
array_type: $ => prec(PREC.ARRAY_TYPE, seq($._type, '[', ']')),
|
|
991
|
+
|
|
992
|
+
// §4 / §5.14 — Java-style generic type arguments. Allows nested
|
|
993
|
+
// generics (`Map<String, List<Integer>>`) and bounded wildcards
|
|
994
|
+
// (`? extends Foo`, `? super Bar`). The `<` is `token.immediate`
|
|
995
|
+
// so it only matches when the angle bracket directly follows the
|
|
996
|
+
// base name with no intervening whitespace — `a < b` still parses
|
|
997
|
+
// as a binary expression because of the space before `<`.
|
|
998
|
+
generic_type: $ => prec.right(PREC.ARRAY_TYPE, seq(
|
|
999
|
+
field('base', choice(
|
|
1000
|
+
alias($.identifier, $.type_identifier),
|
|
1001
|
+
$.qualified_type,
|
|
1002
|
+
)),
|
|
1003
|
+
field('arguments', $.type_arguments),
|
|
1004
|
+
)),
|
|
1005
|
+
|
|
1006
|
+
type_arguments: $ => seq(
|
|
1007
|
+
token.immediate('<'),
|
|
1008
|
+
$._type_argument,
|
|
1009
|
+
repeat(seq(',', $._type_argument)),
|
|
1010
|
+
'>',
|
|
1011
|
+
),
|
|
1012
|
+
|
|
1013
|
+
_type_argument: $ => choice(
|
|
1014
|
+
$._type,
|
|
1015
|
+
$.wildcard,
|
|
1016
|
+
),
|
|
1017
|
+
|
|
1018
|
+
wildcard: $ => seq(
|
|
1019
|
+
'?',
|
|
1020
|
+
optional(seq(
|
|
1021
|
+
choice('extends', 'super'),
|
|
1022
|
+
$._type,
|
|
1023
|
+
)),
|
|
1024
|
+
),
|
|
1025
|
+
|
|
1026
|
+
// §3.1 ACCESS tier / §3.2 — seven distinct node kinds for the
|
|
1027
|
+
// dot family. All at PREC.ACCESS left-associative so chains like
|
|
1028
|
+
// `a.b.c` parse `(a.b).c`. Right operand is the property /
|
|
1029
|
+
// field / method name (an identifier; method_reference also
|
|
1030
|
+
// accepts the `new` keyword per §5.14). Each operator emits a
|
|
1031
|
+
// unique node kind so downstream tooling (`get_op_type()`) can
|
|
1032
|
+
// discriminate without tree-shape heuristics.
|
|
1033
|
+
|
|
1034
|
+
field_access: $ => prec.left(PREC.ACCESS, seq(
|
|
1035
|
+
field('object', $._expression),
|
|
1036
|
+
'.',
|
|
1037
|
+
field('field', $._property_name),
|
|
1038
|
+
)),
|
|
1039
|
+
|
|
1040
|
+
safe_navigation_expression: $ => prec.left(PREC.ACCESS, seq(
|
|
1041
|
+
field('object', $._expression),
|
|
1042
|
+
'?.',
|
|
1043
|
+
field('property', $._property_name),
|
|
1044
|
+
)),
|
|
1045
|
+
|
|
1046
|
+
// Groovy 4+ — see SPECIFICATION.md §3.2. Unlike `?.`, which only
|
|
1047
|
+
// short-circuits when the immediate receiver is null, `??.`
|
|
1048
|
+
// propagates null through the whole chain.
|
|
1049
|
+
safe_chain_dot_expression: $ => prec.left(PREC.ACCESS, seq(
|
|
1050
|
+
field('object', $._expression),
|
|
1051
|
+
'??.',
|
|
1052
|
+
field('property', $._property_name),
|
|
1053
|
+
)),
|
|
1054
|
+
|
|
1055
|
+
spread_dot_expression: $ => prec.left(PREC.ACCESS, seq(
|
|
1056
|
+
field('object', $._expression),
|
|
1057
|
+
'*.',
|
|
1058
|
+
field('property', $._property_name),
|
|
1059
|
+
)),
|
|
1060
|
+
|
|
1061
|
+
method_pointer_expression: $ => prec.left(PREC.ACCESS, seq(
|
|
1062
|
+
field('object', $._expression),
|
|
1063
|
+
'.&',
|
|
1064
|
+
field('method', $._property_name),
|
|
1065
|
+
)),
|
|
1066
|
+
|
|
1067
|
+
direct_field_access_expression: $ => prec.left(PREC.ACCESS, seq(
|
|
1068
|
+
field('object', $._expression),
|
|
1069
|
+
'.@',
|
|
1070
|
+
field('field', $._property_name),
|
|
1071
|
+
)),
|
|
1072
|
+
|
|
1073
|
+
// §5.2 — property / method names after a dot operator (or as a
|
|
1074
|
+
// method declaration name) can be either an identifier or a
|
|
1075
|
+
// *quoted identifier*: a string literal that escapes spaces,
|
|
1076
|
+
// keywords, or special chars. `map."key with spaces"` and
|
|
1077
|
+
// `def "abstract"() { … }` both rely on this.
|
|
1078
|
+
_property_name: $ => choice(
|
|
1079
|
+
$.identifier,
|
|
1080
|
+
alias($.string_literal, $.quoted_identifier),
|
|
1081
|
+
),
|
|
1082
|
+
|
|
1083
|
+
// §5.14 — method reference. Groovy 3+. The RHS is either an
|
|
1084
|
+
// identifier (`String::length`) or the `new` keyword
|
|
1085
|
+
// (`Foo::new`).
|
|
1086
|
+
method_reference_expression: $ => prec.left(PREC.ACCESS, seq(
|
|
1087
|
+
field('target', $._expression),
|
|
1088
|
+
'::',
|
|
1089
|
+
field('name', choice($.identifier, 'new')),
|
|
1090
|
+
)),
|
|
1091
|
+
|
|
1092
|
+
// §3.2 — postfix `++` / `--`. Distinct node kind from the
|
|
1093
|
+
// prefix form (`unary_update_expression`) per the spec table.
|
|
1094
|
+
// PREC.POSTFIX, left-associative.
|
|
1095
|
+
update_expression: $ => prec.left(PREC.POSTFIX, seq(
|
|
1096
|
+
field('operand', $._expression),
|
|
1097
|
+
field('operator', choice('++', '--')),
|
|
1098
|
+
)),
|
|
1099
|
+
|
|
1100
|
+
// §3.2 — prefix `++` / `--`. Apache groups these with UNARY_ADD
|
|
1101
|
+
// (level 3); we use PREC.UNARY to match.
|
|
1102
|
+
unary_update_expression: $ => prec.right(PREC.UNARY, seq(
|
|
1103
|
+
field('operator', choice('++', '--')),
|
|
1104
|
+
field('operand', $._expression),
|
|
1105
|
+
)),
|
|
1106
|
+
|
|
1107
|
+
// §3.2 POSTFIX / §3.2.1 — `f(args)` and the subscript-yielding-
|
|
1108
|
+
// callable composition `arr[i](args)`. The callee slot is any
|
|
1109
|
+
// expression so subscript / future field_access / future
|
|
1110
|
+
// safe_navigation chains compose naturally. PREC.POSTFIX
|
|
1111
|
+
// matches subscript so chains like `obj[i](x)[j]` work.
|
|
1112
|
+
method_invocation: $ => prec.left(PREC.POSTFIX, seq(
|
|
1113
|
+
field('function', $._expression),
|
|
1114
|
+
field('arguments', $.argument_list),
|
|
1115
|
+
)),
|
|
1116
|
+
|
|
1117
|
+
// §3.2 / §5.11 — argument list. Positional args are `_expression`;
|
|
1118
|
+
// named args (Groovy's `k: v` in call position) are `map_entry`
|
|
1119
|
+
// aliased to `named_argument` so downstream tools can tell them
|
|
1120
|
+
// apart from positional values. Spread (`*expr`) lands later.
|
|
1121
|
+
argument_list: $ => seq(
|
|
1122
|
+
'(',
|
|
1123
|
+
optional(seq(
|
|
1124
|
+
$._argument,
|
|
1125
|
+
repeat(seq(',', $._argument)),
|
|
1126
|
+
optional(','),
|
|
1127
|
+
)),
|
|
1128
|
+
')',
|
|
1129
|
+
),
|
|
1130
|
+
|
|
1131
|
+
_argument: $ => choice(
|
|
1132
|
+
$._expression,
|
|
1133
|
+
alias($.map_entry, $.named_argument),
|
|
1134
|
+
$.spread_arguments,
|
|
1135
|
+
),
|
|
1136
|
+
|
|
1137
|
+
// §5.11 — `*expr` only inside argument list / list literal.
|
|
1138
|
+
// Distinct from a binary `*` because the LHS is absent.
|
|
1139
|
+
spread_arguments: $ => seq(
|
|
1140
|
+
'*',
|
|
1141
|
+
field('value', $._expression),
|
|
1142
|
+
),
|
|
1143
|
+
|
|
1144
|
+
// §3.1 POSTFIX / §3.2 — subscript and safe-subscript. The
|
|
1145
|
+
// distinction `arr?[k]` (safe) vs `arr[k]` (regular) is the
|
|
1146
|
+
// explicit ask of dekobon #247 for the `?[]` row. Both at
|
|
1147
|
+
// PREC.POSTFIX, left-associative — `a[b][c]` parses as
|
|
1148
|
+
// `(a[b])[c]`.
|
|
1149
|
+
subscript_expression: $ => prec.left(PREC.POSTFIX, seq(
|
|
1150
|
+
field('object', $._expression),
|
|
1151
|
+
'[',
|
|
1152
|
+
field('index', $._expression),
|
|
1153
|
+
']',
|
|
1154
|
+
)),
|
|
1155
|
+
|
|
1156
|
+
// `?[` is one token. `a ? [b] : c` (with space) still parses
|
|
1157
|
+
// as ternary plus list because tree-sitter's lexer never
|
|
1158
|
+
// crosses a whitespace boundary to extend a multi-char token.
|
|
1159
|
+
safe_subscript_expression: $ => prec.left(PREC.POSTFIX, seq(
|
|
1160
|
+
field('object', $._expression),
|
|
1161
|
+
'?[',
|
|
1162
|
+
field('index', $._expression),
|
|
1163
|
+
']',
|
|
1164
|
+
)),
|
|
1165
|
+
|
|
1166
|
+
// §3.1 level 2 / §3.2 — `**`. Distinct node kind so metric tools
|
|
1167
|
+
// count power separately from multiplicative ops. Right-assoc
|
|
1168
|
+
// per Apache: `1 ** 2 ** 3` parses as `1 ** (2 ** 3)`. POWER
|
|
1169
|
+
// binds tighter than UNARY (+ -) but looser than UNARY_NOT
|
|
1170
|
+
// (! ~) — see PREC table comments.
|
|
1171
|
+
power_expression: $ => prec.right(PREC.POWER, seq(
|
|
1172
|
+
field('left', $._expression),
|
|
1173
|
+
'**',
|
|
1174
|
+
field('right', $._expression),
|
|
1175
|
+
)),
|
|
1176
|
+
|
|
1177
|
+
// §3.1 level 13.5 / §3.2 — `==>`. Right-associative per Apache
|
|
1178
|
+
// (`<assoc=right>`), distinct node kind so downstream tools can
|
|
1179
|
+
// discriminate from `=>` (lambda arrow, not yet wired).
|
|
1180
|
+
logical_implication_expression: $ => prec.right(PREC.IMPLICATION, seq(
|
|
1181
|
+
field('left', $._expression),
|
|
1182
|
+
'==>',
|
|
1183
|
+
field('right', $._expression),
|
|
1184
|
+
)),
|
|
1185
|
+
|
|
1186
|
+
// §3.1 level 15 / §3.2 — single `assignment_expression` node
|
|
1187
|
+
// covers every augmented form (`+=`, `-=`, `*=`, `/=`, `%=`,
|
|
1188
|
+
// `**=`, `<<=`, `>>=`, `>>>=`, `&=`, `^=`, `|=`) plus plain `=`
|
|
1189
|
+
// and Elvis-assign `?=`. The operator field distinguishes them.
|
|
1190
|
+
// Right-associative per Apache. ASSIGN is the loosest precedence
|
|
1191
|
+
// tier and binds looser than CONDITIONAL above it.
|
|
1192
|
+
assignment_expression: $ => {
|
|
1193
|
+
const ops = [
|
|
1194
|
+
'=',
|
|
1195
|
+
'+=', '-=', '*=', '/=', '%=', '**=',
|
|
1196
|
+
'<<=', '>>=', '>>>=',
|
|
1197
|
+
'&=', '^=', '|=',
|
|
1198
|
+
'?=',
|
|
1199
|
+
];
|
|
1200
|
+
return choice(...ops.map(op => prec.right(PREC.ASSIGN, seq(
|
|
1201
|
+
field('left', $._expression),
|
|
1202
|
+
field('operator', op),
|
|
1203
|
+
field('right', $._expression),
|
|
1204
|
+
))));
|
|
1205
|
+
},
|
|
1206
|
+
|
|
1207
|
+
// §3.1 level 14 / §3.2 — ternary and Elvis share the CONDITIONAL
|
|
1208
|
+
// precedence level and are both right-associative. Apache groups
|
|
1209
|
+
// them in one `conditionalExprAlt` so `a ? b ?: c : d` parses as
|
|
1210
|
+
// `a ? (b ?: c) : d` because the middle slot recursively re-enters
|
|
1211
|
+
// the same precedence level. We replicate this with two distinct
|
|
1212
|
+
// rules at PREC.CONDITIONAL; right-associativity + recursion via
|
|
1213
|
+
// `$._expression` give the same result.
|
|
1214
|
+
//
|
|
1215
|
+
// elvis_expression is the headline ask of
|
|
1216
|
+
// dekobon/big-code-analysis #246: downstream tools need to
|
|
1217
|
+
// discriminate elvis from ternary by node kind alone.
|
|
1218
|
+
ternary_expression: $ => prec.right(PREC.CONDITIONAL, seq(
|
|
1219
|
+
field('condition', $._expression),
|
|
1220
|
+
'?',
|
|
1221
|
+
field('consequence', $._expression),
|
|
1222
|
+
':',
|
|
1223
|
+
field('alternative', $._expression),
|
|
1224
|
+
)),
|
|
1225
|
+
|
|
1226
|
+
elvis_expression: $ => prec.right(PREC.CONDITIONAL, seq(
|
|
1227
|
+
field('value', $._expression),
|
|
1228
|
+
'?:',
|
|
1229
|
+
field('default', $._expression),
|
|
1230
|
+
)),
|
|
1231
|
+
|
|
1232
|
+
// §3.2 — distinct-node ops that share precedence with existing
|
|
1233
|
+
// binary_expression tiers but emit unique node kinds per the
|
|
1234
|
+
// operator-coverage matrix. The integration target (`get_op_type()`
|
|
1235
|
+
// in dekobon/big-code-analysis) needs to discriminate these by
|
|
1236
|
+
// node kind alone, not by tree-shape heuristics.
|
|
1237
|
+
|
|
1238
|
+
// §3.2 range_expression — inclusive `..`, exclusive `..<` (right),
|
|
1239
|
+
// exclusive `<..` (left), exclusive `<..<` (both). SHIFT_OR_RANGE
|
|
1240
|
+
// precedence level, left-associative.
|
|
1241
|
+
range_expression: $ => {
|
|
1242
|
+
const ranges = ['..', '..<', '<..', '<..<'];
|
|
1243
|
+
return choice(...ranges.map(op => prec.left(PREC.SHIFT_OR_RANGE, seq(
|
|
1244
|
+
field('start', $._expression),
|
|
1245
|
+
field('operator', op),
|
|
1246
|
+
field('end', $._expression),
|
|
1247
|
+
))));
|
|
1248
|
+
},
|
|
1249
|
+
|
|
1250
|
+
// §3.2 identity_expression — `===` (identity) and `!==`
|
|
1251
|
+
// (non-identity), EQUALITY level. Per Apache, these live in
|
|
1252
|
+
// the equality alternative.
|
|
1253
|
+
identity_expression: $ => {
|
|
1254
|
+
const ops = ['===', '!=='];
|
|
1255
|
+
return choice(...ops.map(op => prec.left(PREC.EQUALITY, seq(
|
|
1256
|
+
field('left', $._expression),
|
|
1257
|
+
field('operator', op),
|
|
1258
|
+
field('right', $._expression),
|
|
1259
|
+
))));
|
|
1260
|
+
},
|
|
1261
|
+
|
|
1262
|
+
// §3.2 spaceship_expression — `<=>`, EQUALITY level. Three-way
|
|
1263
|
+
// comparison; no `operator` field since there is only one form.
|
|
1264
|
+
spaceship_expression: $ => prec.left(PREC.EQUALITY, seq(
|
|
1265
|
+
field('left', $._expression),
|
|
1266
|
+
'<=>',
|
|
1267
|
+
field('right', $._expression),
|
|
1268
|
+
)),
|
|
1269
|
+
|
|
1270
|
+
// §3.2 regex_find_expression — `=~`, EQUALITY level. Matches a
|
|
1271
|
+
// pattern at any position. Fields are `subject` / `pattern` so
|
|
1272
|
+
// downstream tools can tell the regex-bearing operand from the
|
|
1273
|
+
// input expression.
|
|
1274
|
+
regex_find_expression: $ => prec.left(PREC.EQUALITY, seq(
|
|
1275
|
+
field('subject', $._expression),
|
|
1276
|
+
'=~',
|
|
1277
|
+
field('pattern', $._expression),
|
|
1278
|
+
)),
|
|
1279
|
+
|
|
1280
|
+
// §3.2 regex_match_expression — `==~`, EQUALITY level. Anchored
|
|
1281
|
+
// whole-string match. Same field shape as regex_find_expression.
|
|
1282
|
+
regex_match_expression: $ => prec.left(PREC.EQUALITY, seq(
|
|
1283
|
+
field('subject', $._expression),
|
|
1284
|
+
'==~',
|
|
1285
|
+
field('pattern', $._expression),
|
|
1286
|
+
)),
|
|
1287
|
+
|
|
1288
|
+
// §3.2 — single binary_expression node with an `operator` field
|
|
1289
|
+
// for every Java-shaped binary operator (per §3.2 table row 1).
|
|
1290
|
+
// Apache groups arithmetic operators across two precedence
|
|
1291
|
+
// levels (MUL: * / %, ADD: + -); we keep them on one rule with
|
|
1292
|
+
// distinct precedence numbers so the *node kind* stays uniform
|
|
1293
|
+
// while the precedence ladder matches Apache exactly.
|
|
1294
|
+
//
|
|
1295
|
+
// Currently covers ADDITIVE and MULTIPLICATIVE. Shift/range,
|
|
1296
|
+
// relational, equality, bitwise, and logical levels join this
|
|
1297
|
+
// rule in later iterations — each is a single line addition.
|
|
1298
|
+
binary_expression: $ => {
|
|
1299
|
+
/* eslint-disable no-multi-spaces --
|
|
1300
|
+
* Aligned columns make the precedence ladder readable.
|
|
1301
|
+
*/
|
|
1302
|
+
const ops = [
|
|
1303
|
+
// ADDITIVE — §3.1 level 5
|
|
1304
|
+
['+', PREC.ADDITIVE],
|
|
1305
|
+
['-', PREC.ADDITIVE],
|
|
1306
|
+
// MULTIPLICATIVE — §3.1 level 4
|
|
1307
|
+
['*', PREC.MULTIPLICATIVE],
|
|
1308
|
+
['/', PREC.MULTIPLICATIVE],
|
|
1309
|
+
['%', PREC.MULTIPLICATIVE],
|
|
1310
|
+
// SHIFT — §3.1 level 6. Ranges (`..`, `..<`, etc.) share
|
|
1311
|
+
// this precedence level but use distinct node kinds, so
|
|
1312
|
+
// they live in their own rule (lands later).
|
|
1313
|
+
['<<', PREC.SHIFT_OR_RANGE],
|
|
1314
|
+
['>>', PREC.SHIFT_OR_RANGE],
|
|
1315
|
+
['>>>', PREC.SHIFT_OR_RANGE],
|
|
1316
|
+
// RELATIONAL — §3.1 level 7. `in`, `!in`, `instanceof`,
|
|
1317
|
+
// `!instanceof`, and `as` share this level but emit
|
|
1318
|
+
// distinct node kinds (`membership_expression`,
|
|
1319
|
+
// `instanceof_expression`, `cast_expression`), so they
|
|
1320
|
+
// land in their own rules later.
|
|
1321
|
+
['<', PREC.RELATIONAL],
|
|
1322
|
+
['<=', PREC.RELATIONAL],
|
|
1323
|
+
['>', PREC.RELATIONAL],
|
|
1324
|
+
['>=', PREC.RELATIONAL],
|
|
1325
|
+
// EQUALITY — §3.1 level 8. `===`, `!==`, `<=>`, `=~`,
|
|
1326
|
+
// `==~` share this level but emit distinct node kinds.
|
|
1327
|
+
['==', PREC.EQUALITY],
|
|
1328
|
+
['!=', PREC.EQUALITY],
|
|
1329
|
+
// BITWISE — §3.1 levels 9, 10, 11 (each tighter than the
|
|
1330
|
+
// next: & < ^ < | by precedence).
|
|
1331
|
+
['&', PREC.BITWISE_AND],
|
|
1332
|
+
['^', PREC.BITWISE_XOR],
|
|
1333
|
+
['|', PREC.BITWISE_OR],
|
|
1334
|
+
// LOGICAL — §3.1 levels 12, 13 (&& tighter than ||).
|
|
1335
|
+
['&&', PREC.LOGICAL_AND],
|
|
1336
|
+
['||', PREC.LOGICAL_OR],
|
|
1337
|
+
];
|
|
1338
|
+
/* eslint-enable no-multi-spaces */
|
|
1339
|
+
return choice(...ops.map(([op, p]) => prec.left(p, seq(
|
|
1340
|
+
field('left', $._expression),
|
|
1341
|
+
field('operator', op),
|
|
1342
|
+
field('right', $._expression),
|
|
1343
|
+
))));
|
|
1344
|
+
},
|
|
1345
|
+
|
|
1346
|
+
// §5.8 — closure expression. `{ -> }` is the explicit no-parameter
|
|
1347
|
+
// form; `{ a, b -> body }` has identifier parameters;
|
|
1348
|
+
// `{ String a, int b -> body }` has typed parameters; `{ body }`
|
|
1349
|
+
// has no `->` and an implicit `it` parameter (we do NOT emit a
|
|
1350
|
+
// synthetic `it` per §5.8). Default values and varargs land later.
|
|
1351
|
+
closure: $ => seq(
|
|
1352
|
+
'{',
|
|
1353
|
+
optional($.closure_parameters),
|
|
1354
|
+
repeat($._statement),
|
|
1355
|
+
'}',
|
|
1356
|
+
),
|
|
1357
|
+
|
|
1358
|
+
closure_parameters: $ => seq(
|
|
1359
|
+
optional(seq(
|
|
1360
|
+
$.closure_parameter,
|
|
1361
|
+
repeat(seq(',', $.closure_parameter)),
|
|
1362
|
+
)),
|
|
1363
|
+
'->',
|
|
1364
|
+
),
|
|
1365
|
+
|
|
1366
|
+
closure_parameter: $ => seq(
|
|
1367
|
+
optional(field('type', $._type)),
|
|
1368
|
+
field('name', $.identifier),
|
|
1369
|
+
),
|
|
1370
|
+
|
|
1371
|
+
// §3.2 PRIMARY / §5.13 — list literal with trailing-comma support.
|
|
1372
|
+
// Empty list is `[]`; the empty map literal is the distinct `[:]`
|
|
1373
|
+
// form (see map_literal). Tree-sitter splits the parse between
|
|
1374
|
+
// list_literal and map_literal until a `:` or `]` disambiguates;
|
|
1375
|
+
// the conflict declaration above keeps both alternatives alive.
|
|
1376
|
+
list_literal: $ => seq(
|
|
1377
|
+
'[',
|
|
1378
|
+
optional(seq(
|
|
1379
|
+
$._expression,
|
|
1380
|
+
repeat(seq(',', $._expression)),
|
|
1381
|
+
optional(','),
|
|
1382
|
+
)),
|
|
1383
|
+
']',
|
|
1384
|
+
),
|
|
1385
|
+
|
|
1386
|
+
// §3.2 PRIMARY / §5.3 — map literal. `[:]` is the empty map;
|
|
1387
|
+
// anything else is one-or-more `key: value` entries. The
|
|
1388
|
+
// distinction between map_entry and a labelled statement is
|
|
1389
|
+
// contextual — labels are statement-start-only (see §5.3 and
|
|
1390
|
+
// the LABEL_COLON scanner branch), so inside `[ … ]` a `:` after
|
|
1391
|
+
// an identifier is always a map key separator.
|
|
1392
|
+
map_literal: $ => {
|
|
1393
|
+
const entry = choice($.map_entry, $.spread_map_entry);
|
|
1394
|
+
return seq(
|
|
1395
|
+
'[',
|
|
1396
|
+
choice(
|
|
1397
|
+
':',
|
|
1398
|
+
seq(
|
|
1399
|
+
entry,
|
|
1400
|
+
repeat(seq(',', entry)),
|
|
1401
|
+
optional(','),
|
|
1402
|
+
),
|
|
1403
|
+
),
|
|
1404
|
+
']',
|
|
1405
|
+
);
|
|
1406
|
+
},
|
|
1407
|
+
|
|
1408
|
+
map_entry: $ => seq(
|
|
1409
|
+
field('key', $._expression),
|
|
1410
|
+
':',
|
|
1411
|
+
field('value', $._expression),
|
|
1412
|
+
),
|
|
1413
|
+
|
|
1414
|
+
// §5.11 — `*: expr` only inside a map literal. `*:` is a single
|
|
1415
|
+
// anonymous token (longest-match keeps it intact); a bare `*`
|
|
1416
|
+
// followed by a `:` colon outside this context would have to be
|
|
1417
|
+
// syntactically impossible by the surrounding rules.
|
|
1418
|
+
spread_map_entry: $ => seq(
|
|
1419
|
+
'*:',
|
|
1420
|
+
field('value', $._expression),
|
|
1421
|
+
),
|
|
1422
|
+
|
|
1423
|
+
// §3.2 PRIMARY — `( expr )`. Distinct from `parenthesized_type_cast`
|
|
1424
|
+
// (§5.15), which lands once we have `_type`. The parser disambiguates
|
|
1425
|
+
// by what's inside the parens.
|
|
1426
|
+
parenthesized_expression: $ => seq('(', $._expression, ')'),
|
|
1427
|
+
|
|
1428
|
+
// §3.2 — single `unary_expression` node kind but two precedence
|
|
1429
|
+
// alternatives, matching Apache §3.1:
|
|
1430
|
+
// UNARY_NOT (`!` `~`) — level 16, tighter than POWER (15),
|
|
1431
|
+
// so `~2 ** 3` parses as `(~2) ** 3`.
|
|
1432
|
+
// UNARY (`+` `-`) — level 14, looser than POWER (15),
|
|
1433
|
+
// so `-2 ** 3` parses as `-(2 ** 3)`.
|
|
1434
|
+
// Both alternatives produce the same node kind, so downstream
|
|
1435
|
+
// tools see one consistent shape and discriminate by operator.
|
|
1436
|
+
unary_expression: $ => choice(
|
|
1437
|
+
prec.right(PREC.UNARY_NOT, seq(
|
|
1438
|
+
field('operator', choice('!', '~')),
|
|
1439
|
+
field('operand', $._expression),
|
|
1440
|
+
)),
|
|
1441
|
+
prec.right(PREC.UNARY, seq(
|
|
1442
|
+
field('operator', choice('+', '-')),
|
|
1443
|
+
field('operand', $._expression),
|
|
1444
|
+
)),
|
|
1445
|
+
),
|
|
1446
|
+
|
|
1447
|
+
// §3.2 PRIMARY — boolean and null are keywords in Groovy. The
|
|
1448
|
+
// `word: $.identifier` directive above lets tree-sitter resolve
|
|
1449
|
+
// the keyword-vs-identifier ambiguity automatically: a literal
|
|
1450
|
+
// 'true' / 'false' / 'null' that matches the identifier regex
|
|
1451
|
+
// is reclassified as the keyword token when one of these rules
|
|
1452
|
+
// is the only legal next reduction.
|
|
1453
|
+
boolean_literal: _ => choice('true', 'false'),
|
|
1454
|
+
|
|
1455
|
+
null_literal: _ => 'null',
|
|
1456
|
+
|
|
1457
|
+
// §5.7 — string flavours. Single and triple-single never
|
|
1458
|
+
// interpolate. Double-quoted and triple-double interpolate
|
|
1459
|
+
// `$identifier(.identifier)*` and `${expression}` as structured
|
|
1460
|
+
// children, so consumers can walk into the GString's expression
|
|
1461
|
+
// segments. Slashy bodies and dollar-slashy bodies remain flat
|
|
1462
|
+
// tokens (their internal `$` is rare in practice and the
|
|
1463
|
+
// scanner cannot interrupt a slashy mid-body without per-flavor
|
|
1464
|
+
// state).
|
|
1465
|
+
string_literal: $ => choice(
|
|
1466
|
+
$._single_quoted_string,
|
|
1467
|
+
$._triple_single_quoted_string,
|
|
1468
|
+
$._double_quoted_string,
|
|
1469
|
+
$._triple_double_quoted_string,
|
|
1470
|
+
$._dollar_slashy_string,
|
|
1471
|
+
$._slashy_string,
|
|
1472
|
+
),
|
|
1473
|
+
|
|
1474
|
+
// §5.7 — slashy regex `/.../`. The opening `/` is emitted by
|
|
1475
|
+
// the external scanner (after the context guard from §5.4 / §6.2
|
|
1476
|
+
// and a lookahead that confirms a closing `/` exists on the same
|
|
1477
|
+
// line). The body is parsed as `gstringPart` so `$identifier` and
|
|
1478
|
+
// `${expr}` segments expose the same structured children as
|
|
1479
|
+
// double-quoted GStrings. The closing `/` is `token.immediate`
|
|
1480
|
+
// so it cannot skip whitespace between the body and the closer.
|
|
1481
|
+
_slashy_string: $ => seq(
|
|
1482
|
+
$._slashy_string_start,
|
|
1483
|
+
repeat(gstringPart($, $._slashy_text)),
|
|
1484
|
+
token.immediate('/'),
|
|
1485
|
+
),
|
|
1486
|
+
|
|
1487
|
+
_slashy_text: _ => token.immediate(repeat1(choice(
|
|
1488
|
+
/[^\/\\$\n\r]/,
|
|
1489
|
+
/\\[\s\S]/,
|
|
1490
|
+
))),
|
|
1491
|
+
|
|
1492
|
+
_single_quoted_string: _ => token(seq(
|
|
1493
|
+
'\'',
|
|
1494
|
+
repeat(choice(
|
|
1495
|
+
/[^'\\\n\r]/,
|
|
1496
|
+
/\\[\s\S]/,
|
|
1497
|
+
)),
|
|
1498
|
+
'\'',
|
|
1499
|
+
)),
|
|
1500
|
+
|
|
1501
|
+
_triple_single_quoted_string: _ => token(seq(
|
|
1502
|
+
'\'\'\'',
|
|
1503
|
+
repeat(choice(
|
|
1504
|
+
/[^'\\]/,
|
|
1505
|
+
/\\[\s\S]/,
|
|
1506
|
+
/'[^']/,
|
|
1507
|
+
/''[^']/,
|
|
1508
|
+
)),
|
|
1509
|
+
'\'\'\'',
|
|
1510
|
+
)),
|
|
1511
|
+
|
|
1512
|
+
// §5.7 / §6.3 — double-quoted GString. The opening `"` is
|
|
1513
|
+
// tokenised separately from the body so that `$identifier` and
|
|
1514
|
+
// `${expr}` segments parse as proper child nodes. The text
|
|
1515
|
+
// fragment stops before any `$`, so `$identifier` and `${expr}`
|
|
1516
|
+
// get a chance to match; literal `$` (followed by `"` or a
|
|
1517
|
+
// digit etc.) is recovered as `$` plus the fallback shape.
|
|
1518
|
+
_double_quoted_string: $ => seq(
|
|
1519
|
+
'"',
|
|
1520
|
+
repeat(gstringPart($, $._gstring_text)),
|
|
1521
|
+
token.immediate('"'),
|
|
1522
|
+
),
|
|
1523
|
+
|
|
1524
|
+
_gstring_text: _ => token.immediate(repeat1(choice(
|
|
1525
|
+
/[^"\\$\n\r]/,
|
|
1526
|
+
/\\[\s\S]/,
|
|
1527
|
+
))),
|
|
1528
|
+
|
|
1529
|
+
// Bare `$` inside a GString body that does not start a valid
|
|
1530
|
+
// interpolation (`$"`, `$5`, `$$`, `$ `) is matched as a one-
|
|
1531
|
+
// char literal fragment. This rule must have lower precedence
|
|
1532
|
+
// than `_gstring_dollar_path` so `$name` still wins.
|
|
1533
|
+
_gstring_literal_dollar: _ => token.immediate(prec(-1, /\$/)),
|
|
1534
|
+
|
|
1535
|
+
// §5.7 — triple-double GString. Like `_double_quoted_string`
|
|
1536
|
+
// but newlines and embedded `"` / `""` are part of the body.
|
|
1537
|
+
_triple_double_quoted_string: $ => seq(
|
|
1538
|
+
'"""',
|
|
1539
|
+
repeat(gstringPart($, $._triple_gstring_text)),
|
|
1540
|
+
token.immediate('"""'),
|
|
1541
|
+
),
|
|
1542
|
+
|
|
1543
|
+
_triple_gstring_text: _ => token.immediate(repeat1(choice(
|
|
1544
|
+
/[^"\\$]/,
|
|
1545
|
+
/\\[\s\S]/,
|
|
1546
|
+
/"[^"]/,
|
|
1547
|
+
/""[^"]/,
|
|
1548
|
+
))),
|
|
1549
|
+
|
|
1550
|
+
// §5.7 — `$identifier(.identifier)*` interpolation. The whole
|
|
1551
|
+
// `$identifier...` shape is matched as a single immediate token
|
|
1552
|
+
// so the `$` only consumes when followed by an identifier start;
|
|
1553
|
+
// `_gstring_literal_dollar` handles the lone-`$` fallback above.
|
|
1554
|
+
gstring_dollar_interpolation: $ => seq(
|
|
1555
|
+
field('value', alias(
|
|
1556
|
+
$._gstring_dollar_path,
|
|
1557
|
+
$.identifier,
|
|
1558
|
+
)),
|
|
1559
|
+
),
|
|
1560
|
+
|
|
1561
|
+
_gstring_dollar_path: _ => token.immediate(
|
|
1562
|
+
/\$[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*/,
|
|
1563
|
+
),
|
|
1564
|
+
|
|
1565
|
+
// §5.7 / §6.3 — `${expression}` interpolation.
|
|
1566
|
+
gstring_brace_interpolation: $ => seq(
|
|
1567
|
+
token.immediate('${'),
|
|
1568
|
+
field('value', $._expression),
|
|
1569
|
+
'}',
|
|
1570
|
+
),
|
|
1571
|
+
|
|
1572
|
+
// §5.7 — `$/...$/` dollar-slashy strings. The body is parsed
|
|
1573
|
+
// through `gstringPart` so `$identifier` and `${expr}` segments
|
|
1574
|
+
// expose structured children. `_dollar_slashy_text` consumes
|
|
1575
|
+
// every literal-text alternative: `/X` where X is not `$`,
|
|
1576
|
+
// `$$` (literal `$`), `$/` (literal `/`), and any other
|
|
1577
|
+
// non-`/` non-`$` char. The closer `/$` is `token.immediate`
|
|
1578
|
+
// so it always wins over text when `/` is followed by `$`.
|
|
1579
|
+
_dollar_slashy_string: $ => seq(
|
|
1580
|
+
'$/',
|
|
1581
|
+
repeat(gstringPart($, $._dollar_slashy_text)),
|
|
1582
|
+
token.immediate('/$'),
|
|
1583
|
+
),
|
|
1584
|
+
|
|
1585
|
+
_dollar_slashy_text: _ => token.immediate(repeat1(choice(
|
|
1586
|
+
/[^/$]/,
|
|
1587
|
+
/\/[^$]/,
|
|
1588
|
+
/\$\$/,
|
|
1589
|
+
/\$\//,
|
|
1590
|
+
))),
|
|
1591
|
+
|
|
1592
|
+
// §5.1 — hex (0x…), binary (0b…), decimal int / float / scientific,
|
|
1593
|
+
// each with optional type suffix from { G g L l I i D d F f }.
|
|
1594
|
+
// Underscores between digits are accepted permissively (the spec
|
|
1595
|
+
// forbids leading/trailing `_`, but that is a semantic check,
|
|
1596
|
+
// not a grammar one — see §1.2).
|
|
1597
|
+
number_literal: _ => token(choice(
|
|
1598
|
+
/0[xX][0-9a-fA-F_]+[GgLlIi]?/,
|
|
1599
|
+
/0[bB][01_]+[GgLlIi]?/,
|
|
1600
|
+
/[0-9][0-9_]*\.[0-9][0-9_]*([eE][+-]?[0-9][0-9_]*)?[GgFfDd]?/,
|
|
1601
|
+
/[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*[GgFfDd]?/,
|
|
1602
|
+
/[0-9][0-9_]*[GgLlIiDdFf]?/,
|
|
1603
|
+
)),
|
|
1604
|
+
|
|
1605
|
+
identifier: _ => /[A-Za-z_$][A-Za-z0-9_$]*/,
|
|
1606
|
+
},
|
|
1607
|
+
});
|