@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/README.md +25 -4
- package/grammar.js +627 -129
- package/package.json +1 -1
- 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/highlights.scm +10 -4
- package/queries/groovy/locals.scm +7 -0
- package/queries/groovy/tags.scm +32 -0
- package/src/grammar.json +1765 -442
- package/src/node-types.json +593 -3
- package/src/parser.c +194884 -146231
- package/src/scanner.c +275 -24
- package/tree-sitter.json +2 -1
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
|
|
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
|
-
*
|
|
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
|
-
//
|
|
79
|
-
//
|
|
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
|
-
//
|
|
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
|
|
234
|
-
//
|
|
235
|
-
// call on a bare identifier).
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
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',
|
|
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
|
-
//
|
|
260
|
-
//
|
|
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.
|
|
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(
|
|
410
|
-
|
|
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(
|
|
429
|
-
|
|
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
|
|
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
|
-
//
|
|
527
|
-
//
|
|
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
|
|
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: $ =>
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
667
|
-
//
|
|
668
|
-
//
|
|
669
|
-
//
|
|
670
|
-
//
|
|
671
|
-
//
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
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.
|
|
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
|
|
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
|
|
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: $ =>
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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
|
-
|
|
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
|
|
1088
|
-
//
|
|
1089
|
-
//
|
|
1090
|
-
//
|
|
1091
|
-
//
|
|
1092
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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.
|
|
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
|
|
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).
|
|
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: $ =>
|
|
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).
|
|
1613
|
-
//
|
|
1614
|
-
//
|
|
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)
|
|
1664
|
-
//
|
|
1665
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
});
|