codemirror-lang-raku 0.1.0 → 0.3.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/dist/index.cjs CHANGED
@@ -4,58 +4,97 @@ var lr = require('@lezer/lr');
4
4
  var common = require('@lezer/common');
5
5
  var language = require('@codemirror/language');
6
6
  var highlight = require('@lezer/highlight');
7
+ var state = require('@codemirror/state');
7
8
 
8
9
  // This file was generated by lezer-generator. You probably shouldn't edit it.
9
- const BlockComment = 1,
10
- Pod = 2,
11
- Heredoc = 3,
12
- methodDot = 4,
13
- MethodName = 5,
14
- declaredName = 172,
15
- declaredMethodName = 173,
16
- smiley = 174,
17
- Version = 6,
18
- regexBody = 7,
19
- quoteContent = 175,
20
- quoteNestOpen = 176,
21
- quoteNestClose = 177,
22
- quoteEnd = 178,
23
- rawString = 179,
24
- quoteStart = 180,
25
- Regex = 8,
26
- fatArrowKey = 181,
27
- wordOperator = 9,
28
- LineComment = 10,
29
- DocComment = 11,
30
- radixNumber = 13,
31
- PackageName = 26,
32
- RoutineName = 33,
33
- methodRoutineName = 36,
34
- RegexName = 41,
35
- EnumName = 45,
36
- SubsetName = 48,
37
- ConstantName = 51,
38
- VariableName = 53,
39
- AttributeName = 54,
40
- SpecialVariable = 55,
41
- Identifier = 56,
42
- TypeName = 57,
43
- Number = 58,
44
- StringLiteral = 59,
45
- Interpolation = 61,
46
- PairKey = 62,
47
- self = 142,
48
- True = 143,
49
- False = 144,
50
- Nil = 145,
51
- Operator = 164;
10
+ const smiley = 201,
11
+ noSmiley = 202,
12
+ BlockComment = 1,
13
+ podStart = 2,
14
+ podEnd = 203,
15
+ podEndDirective = 3,
16
+ podText = 204,
17
+ PodDirective = 4,
18
+ PodHeading = 5,
19
+ PodStrong = 6,
20
+ PodEmphasis = 7,
21
+ PodCode = 8,
22
+ PodLink = 9,
23
+ PodFormat = 10,
24
+ rawHeredoc = 11,
25
+ interpolatingHeredoc = 12,
26
+ heredocStart = 205,
27
+ methodDot = 13,
28
+ MethodName = 14,
29
+ declaredName = 206,
30
+ declaredMethodName = 207,
31
+ Version = 15,
32
+ regexStart = 208,
33
+ regexBodyEnd = 209,
34
+ regexLitOpen = 210,
35
+ regexLitClose = 211,
36
+ regexText = 212,
37
+ CharacterClass = 16,
38
+ Assertion = 17,
39
+ regexQuote = 18,
40
+ regexOperator = 19,
41
+ regexComment = 20,
42
+ regexBlockComment = 21,
43
+ regexCapture = 22,
44
+ quoteContent = 213,
45
+ quoteNestOpen = 214,
46
+ quoteNestClose = 215,
47
+ quoteEnd = 216,
48
+ rawString = 217,
49
+ quoteStart = 218,
50
+ interpolatingWordList = 23,
51
+ regexLiteral = 24,
52
+ transliteration = 25,
53
+ plainName = 219,
54
+ wordOperator = 26,
55
+ multiName = 220,
56
+ operatorVariable = 27,
57
+ LineComment = 28,
58
+ DocComment = 29,
59
+ Pod = 31,
60
+ radixNumber = 32,
61
+ PackageName = 45,
62
+ RoutineName = 52,
63
+ methodRoutineName = 55,
64
+ multiRoutineName = 56,
65
+ RegexName = 61,
66
+ regexBody = 63,
67
+ VariableName = 65,
68
+ AttributeName = 66,
69
+ SpecialVariable = 67,
70
+ EnumName = 71,
71
+ SubsetName = 74,
72
+ ConstantName = 77,
73
+ Identifier = 79,
74
+ TypeName = 80,
75
+ Number = 81,
76
+ quotedString = 82,
77
+ NestedDelimiters = 83,
78
+ Interpolation = 84,
79
+ PairKey = 85,
80
+ multi = 96,
81
+ proto = 97,
82
+ only = 98,
83
+ self = 165,
84
+ True = 166,
85
+ False = 167,
86
+ Nil = 168,
87
+ Operator = 187,
88
+ Regex = 191,
89
+ Heredoc = 192,
90
+ StringLiteral = 193;
52
91
 
53
92
  // Opening bracket -> closing bracket, for the delimiters of quotes and
54
93
  // embedded comments. Raku accepts any Unicode bracket pair; this lists
55
94
  // the ones that are likely to be typed.
56
- const brackets = {};
95
+ const brackets$1 = {};
57
96
  for (let pair of ["()", "[]", "{}", "<>", "«»", "「」", "『』", "()", "[]", "{}", "【】", "〈〉", "《》", "〔〕"])
58
- brackets[pair.charCodeAt(0)] = pair.charCodeAt(1);
97
+ brackets$1[pair.charCodeAt(0)] = pair.charCodeAt(1);
59
98
  function isAsciiLetter(ch) { return ch >= 97 /* Ch.a */ && ch <= 122 /* Ch.z */ || ch >= 65 /* Ch.A */ && ch <= 90 /* Ch.Z */; }
60
99
  function isDigit(ch) { return ch >= 48 /* Ch._0 */ && ch <= 57 /* Ch._9 */; }
61
100
  // The characters of the grammar's whitespace token.
@@ -118,41 +157,126 @@ function word(input, from, to) {
118
157
  }
119
158
  // A quote that is being read piece by piece because it interpolates.
120
159
  class Quote {
121
- constructor(parent, open, close, count,
122
- // How many nested opening delimiters are unclosed.
123
- depth) {
160
+ constructor(parent, open, close, count, interpolates,
161
+ // Whether a backslash that does not start an escape still
162
+ // takes the delimiter after it out of play: q:c[a \] b]
163
+ escapes) {
124
164
  this.parent = parent;
125
165
  this.open = open;
126
166
  this.close = close;
127
167
  this.count = count;
128
- this.depth = depth;
129
- this.hash = ((parent ? parent.hash * 31 : 7) + open * 5 + count * 3 + depth) | 0;
168
+ this.interpolates = interpolates;
169
+ this.escapes = escapes;
170
+ let hash = parent ? parent.hash : 7;
171
+ for (let part of [open, count, interpolates, escapes ? 1 : 0])
172
+ hash = (hash * 31 + part) | 0;
173
+ this.hash = hash;
174
+ }
175
+ }
176
+ const noHeredocs = [];
177
+ // The quote that the text of a heredoc is read as. It has no
178
+ // delimiters, and ends where the text does.
179
+ const heredocQuote = new Quote(null, -1, -1, 1, 63 /* Interpolate.All */, true);
180
+ // The body of a regex declaration, or the inside of a regex literal,
181
+ // that is being read piece by piece.
182
+ class RegexBody {
183
+ constructor(// The body around the block of code that this one is in.
184
+ parent,
185
+ // How many braces are open in a block of code in the
186
+ // regex. Zero in the regex itself.
187
+ braces,
188
+ // For a regex literal, the position in the document of
189
+ // the delimiter that closes it. It is not part of the
190
+ // hash, which is to be the same before and after an edit
191
+ // that moves the literal.
192
+ literalEnd = -1) {
193
+ this.parent = parent;
194
+ this.braces = braces;
195
+ this.literalEnd = literalEnd;
196
+ this.hash = ((parent ? parent.hash * 31 : 0) + 7919 * (braces + 1) + (literalEnd < 0 ? 0 : 104729)) | 0;
197
+ }
198
+ withBraces(braces) { return new RegexBody(this.parent, braces, this.literalEnd); }
199
+ }
200
+ // A Pod block that is being read piece by piece.
201
+ class PodBlock {
202
+ constructor(kind,
203
+ // What follows `=begin`, which `=end` has to repeat.
204
+ name,
205
+ // Whether it holds code, in which formatting codes are not read.
206
+ code) {
207
+ this.kind = kind;
208
+ this.name = name;
209
+ this.code = code;
210
+ let hash = 101 + kind * 7 + (code ? 3 : 0);
211
+ for (let i = 0; i < name.length; i++)
212
+ hash = (hash * 31 + name.charCodeAt(i)) | 0;
213
+ this.hash = hash;
130
214
  }
131
215
  }
132
216
  class Context {
133
- constructor(mode, quote) {
217
+ constructor(mode, quote,
218
+ // The heredocs that were opened on the current line, whose
219
+ // text starts on the next one.
220
+ heredocs, pod,
221
+ // The body of a regex declaration that this is in.
222
+ regex, hash = -1) {
134
223
  this.mode = mode;
135
224
  this.quote = quote;
225
+ this.heredocs = heredocs;
226
+ this.pod = pod;
227
+ this.regex = regex;
228
+ if (hash < 0) {
229
+ hash = (quote ? quote.hash : 0) ^ (pod ? pod.hash : 0) ^ (regex ? regex.hash : 0);
230
+ for (let { terminator, interpolates } of heredocs) {
231
+ hash = (hash * 31 + (interpolates ? 19 : 17)) | 0;
232
+ for (let i = 0; i < terminator.length; i++)
233
+ hash = (hash * 31 + terminator.charCodeAt(i)) | 0;
234
+ }
235
+ // Negative values stand for "not worked out yet".
236
+ hash &= 0x7fffffff;
237
+ }
238
+ this.hash = hash;
239
+ }
240
+ withMode(mode) {
241
+ return mode == this.mode ? this : new Context(mode, this.quote, this.heredocs, this.pod, this.regex, this.hash);
242
+ }
243
+ withHeredocs(heredocs) {
244
+ return heredocs == this.heredocs ? this : new Context(this.mode, this.quote, heredocs, this.pod, this.regex);
245
+ }
246
+ withQuote(mode, quote, heredocs = this.heredocs) {
247
+ return new Context(mode, quote, heredocs, this.pod, this.regex);
248
+ }
249
+ withPod(pod) { return new Context(this.mode, this.quote, this.heredocs, pod, this.regex); }
250
+ withRegex(regex) {
251
+ return new Context(this.mode, this.quote, this.heredocs, this.pod, regex);
136
252
  }
137
- // An old node is only reused where this is the same as where it was
138
- // made. The mode is left out: it changes with every token, which
139
- // would rule out nearly all reuse, and `reuse` works it out again.
140
- get hash() { return this.quote ? this.quote.hash : 0; }
141
- withMode(mode) { return mode == this.mode ? this : new Context(mode, this.quote); }
142
253
  }
254
+ // The nodes of a string: a quote, the token of a word list that
255
+ // interpolates, and the tree of its inside, which takes the place of
256
+ // that token.
257
+ const stringTerms = new Set([quotedString, interpolatingWordList, StringLiteral]);
143
258
  // The mode after a token or a node, for those where the term settles
144
259
  // it. Shifting a token and reusing a node both look here, so that they
145
260
  // agree.
146
261
  const termModes = new Map();
147
- for (let term of [VariableName, AttributeName, SpecialVariable, Number, radixNumber, Version, MethodName,
148
- PairKey, rawString, quoteEnd, StringLiteral, Regex, regexBody, self, True, False, Nil])
262
+ for (let term of [VariableName, AttributeName, SpecialVariable, operatorVariable, Number, radixNumber, Version, MethodName,
263
+ PairKey, rawString, quoteEnd, ...stringTerms, regexLiteral, transliteration, regexBody,
264
+ // The tree of the inside of a regex literal, which takes the
265
+ // place of the literal's token in a node that is reused.
266
+ Regex, self, True, False, Nil,
267
+ // The pieces of a regex. What follows them is more of
268
+ // the regex, or the brace that closes it.
269
+ regexText, CharacterClass, Assertion, regexQuote, regexOperator, regexComment,
270
+ regexBlockComment, regexCapture])
149
271
  termModes.set(term, 1 /* Mode.AfterTerm */);
150
- for (let term of [Identifier, TypeName, smiley, declaredName, declaredMethodName, PackageName, RoutineName,
151
- methodRoutineName, RegexName, EnumName, SubsetName, ConstantName])
272
+ for (let term of [Identifier, TypeName, smiley, noSmiley, declaredName, declaredMethodName, PackageName, RoutineName,
273
+ methodRoutineName, multiName, multiRoutineName, RegexName, EnumName, SubsetName, ConstantName])
152
274
  termModes.set(term, 2 /* Mode.AfterName */);
153
275
  // The `}` of "{...}" in a string does not take a subscript.
154
- for (let term of [quoteContent, Interpolation])
276
+ for (let term of [quoteContent, quoteNestOpen, quoteNestClose, NestedDelimiters, Interpolation])
155
277
  termModes.set(term, 0 /* Mode.Term */);
278
+ for (let term of [multi, proto, only])
279
+ termModes.set(term, 4 /* Mode.AfterMulti */);
156
280
  // The mode after an operator, from its first three characters. `after`
157
281
  // is the mode before it.
158
282
  function modeAfterOperator(first, second, third, after) {
@@ -167,7 +291,8 @@ function modeAfterOperator(first, second, third, after) {
167
291
  }
168
292
  // The mode after a token that the terms file has no name for, from its
169
293
  // first character. Keywords and identifiers come through here alike;
170
- // an identifier gets its mode when the Identifier node is reduced.
294
+ // an identifier gets its mode when the Identifier node is reduced,
295
+ // from modeAfter, which also looks at how it is spelled.
171
296
  function modeAfterChar(first) {
172
297
  if (first == 41 /* Ch.ParenClose */ || first == 93 /* Ch.BracketClose */ || first == 36 /* Ch.Dollar */ || first == 64 /* Ch.At */)
173
298
  return 1 /* Mode.AfterTerm */;
@@ -176,9 +301,20 @@ function modeAfterChar(first) {
176
301
  return 2 /* Mode.AfterName */;
177
302
  return first == 125 /* Ch.BraceClose */ ? 3 /* Mode.AfterBlock */ : 0 /* Mode.Term */;
178
303
  }
304
+ // The built-in names that stand for a value and take no arguments. The
305
+ // constant `e` is left out: a routine or a variable of one's own is too
306
+ // often named that.
307
+ const termWords = /^(pi|π|tau|τ|now|time|rand)$/;
179
308
  // The mode after the token of the given term at the input's position.
180
309
  // `before` is the mode before it.
181
310
  function modeAfter(term, input, offset, before) {
311
+ // A term that takes no arguments is followed by an operator: pi /2
312
+ if (term == Identifier) {
313
+ let end = nameEnd(input, offset);
314
+ // Most names are longer than these, and are not looked at further.
315
+ if (end - offset <= 4 && termWords.test(word(input, offset, end)))
316
+ return 1 /* Mode.AfterTerm */;
317
+ }
182
318
  let mode = termModes.get(term);
183
319
  if (mode != null)
184
320
  return mode;
@@ -187,71 +323,196 @@ function modeAfter(term, input, offset, before) {
187
323
  return modeAfterOperator(first, input.peek(offset + 1), input.peek(offset + 2), before);
188
324
  return modeAfterChar(first);
189
325
  }
190
- const skippedTerms = new Set([LineComment, DocComment, BlockComment, Pod, Heredoc]);
326
+ // The context at the start of a parse. The tokens that start the parse
327
+ // of the inside of another token are only looked for while the context
328
+ // is still this one, which spares asking the parser whether they can
329
+ // be shifted at every other token.
330
+ const startContext = new Context(0 /* Mode.Term */, null, noHeredocs, null, null);
331
+ // The tokens of the text of a heredoc, and the tree of its inside,
332
+ // which takes the place of the token of one that interpolates.
333
+ const heredocTerms = new Set([rawHeredoc, interpolatingHeredoc, Heredoc]);
334
+ const skippedTerms = new Set([LineComment, DocComment, BlockComment, ...heredocTerms, Pod, podStart, podEnd, podEndDirective,
335
+ podText, PodDirective, PodHeading, PodStrong, PodEmphasis, PodCode, PodLink, PodFormat]);
336
+ // The heredoc that the quote at offset `at` opens, when it opens one.
337
+ // Its terminator is what the quote holds: END for q:to/END/
338
+ function heredocAt(input, at) {
339
+ let first = input.peek(at);
340
+ if (first != 113 /* Ch.q */ && first != 81 /* Ch.Q */)
341
+ return null;
342
+ let opening = readOpening(input, at);
343
+ if (!opening || !opening.heredoc)
344
+ return null;
345
+ let end = opening.start;
346
+ while (input.peek(end) != opening.close && input.peek(end) != 10 /* Ch.Newline */ && input.peek(end) >= 0)
347
+ end++;
348
+ // An empty one, as in the `q:to//` of a quote that is being typed,
349
+ // would end the heredoc at the next blank line.
350
+ let terminator = word(input, opening.start, end).trim();
351
+ return terminator ? { terminator, interpolates: opening.interpolates == 63 /* Interpolate.All */ } : null;
352
+ }
353
+ // `heredocs`, with the heredoc that the quote at offset `at` opens, if
354
+ // it opens one.
355
+ function withHeredocAt(heredocs, input, at) {
356
+ let heredoc = heredocAt(input, at);
357
+ return heredoc ? heredocs.concat(heredoc) : heredocs;
358
+ }
359
+ function hasNewline(input, from, to) {
360
+ for (let i = from; i < to; i++)
361
+ if (input.peek(i) == 10 /* Ch.Newline */)
362
+ return true;
363
+ return false;
364
+ }
365
+ // The heredocs that are open after a reused node, of which the input is
366
+ // at the start. As when tokens are shifted, a line break between tokens
367
+ // and the text of a heredoc close the ones before them. `inQuote` tells
368
+ // that the node is a run of pieces of a quote.
369
+ function heredocsAfter(node, input, before, inQuote) {
370
+ // Most reused nodes are single tokens.
371
+ if (!node.children.length && !stringTerms.has(node.type.id) && !heredocTerms.has(node.type.id))
372
+ return before;
373
+ let cursor = node.cursor(), found = [], closedAt = -1;
374
+ // Goes over the node at the cursor from its end, and tells whether
375
+ // the heredocs before some point in it are closed.
376
+ function scan(text = false) {
377
+ let type = cursor.type.id, { from, to } = cursor;
378
+ if (heredocTerms.has(type)) {
379
+ closedAt = to;
380
+ return true;
381
+ }
382
+ let isString = stringTerms.has(type);
383
+ if (isString) {
384
+ let heredoc = heredocAt(input, from);
385
+ if (heredoc)
386
+ found.push({ pos: from, heredoc });
387
+ }
388
+ // The tree of the inside of a regex literal or of a word list. It
389
+ // was one token when it was shifted, which opens no heredoc.
390
+ if (type == Regex || type == StringLiteral)
391
+ return false;
392
+ if (!cursor.lastChild())
393
+ return false;
394
+ // Between the pieces of a string lies its text, not whitespace.
395
+ let gaps = !isString && !text && type != NestedDelimiters;
396
+ for (let gapEnd = to;;) {
397
+ if (gaps && hasNewline(input, cursor.to, gapEnd)) {
398
+ closedAt = gapEnd;
399
+ break;
400
+ }
401
+ if (scan())
402
+ break;
403
+ gapEnd = cursor.from;
404
+ if (!cursor.prevSibling()) {
405
+ if (gaps && hasNewline(input, from, gapEnd))
406
+ closedAt = gapEnd;
407
+ break;
408
+ }
409
+ }
410
+ cursor.parent();
411
+ return closedAt >= 0;
412
+ }
413
+ // A piece on its own, such as an Interpolation, is a named node.
414
+ scan(inQuote && node.type.isAnonymous);
415
+ if (!found.length && closedAt < 0)
416
+ return before;
417
+ let after = found.filter(opener => opener.pos >= closedAt).sort((a, b) => a.pos - b.pos).map(opener => opener.heredoc);
418
+ return closedAt < 0 ? before.concat(after) : after.length ? after : noHeredocs;
419
+ }
420
+ // The last token in `node` that says what the mode after it is: one
421
+ // that is not a comment or Pod, which leave the mode as it was. A node
422
+ // that settles the mode by itself counts as a token. Null when the
423
+ // node holds nothing but comments.
424
+ function lastToken$1(node) {
425
+ if (termModes.has(node.type.id) || !node.lastChild)
426
+ return skippedTerms.has(node.type.id) ? null : node;
427
+ for (let child = node.lastChild; child; child = child.prevSibling) {
428
+ let found = skippedTerms.has(child.type.id) ? null : lastToken$1(child);
429
+ if (found)
430
+ return found;
431
+ }
432
+ return null;
433
+ }
191
434
  const trackContext = new lr.ContextTracker({
192
- start: new Context(0 /* Mode.Term */, null),
435
+ start: startContext,
193
436
  shift(context, term, _stack, input) {
437
+ if (term == rawHeredoc || term == interpolatingHeredoc)
438
+ return context.withHeredocs(noHeredocs);
439
+ // The text of a heredoc that interpolates is read by a parser of
440
+ // its own, as a quote without delimiters.
441
+ if (term == heredocStart)
442
+ return context.withQuote(0 /* Mode.Term */, heredocQuote);
443
+ if (term == podStart)
444
+ return context.withPod(readPodStart(input));
445
+ if (term == podEnd || term == podEndDirective)
446
+ return context.withPod(null);
194
447
  if (skippedTerms.has(term))
195
448
  return context;
449
+ let { regex } = context;
450
+ if (term == regexStart)
451
+ return context.withRegex(new RegexBody(regex, 0));
452
+ if (term == regexBodyEnd)
453
+ return context.withRegex(regex && regex.parent).withMode(termModes.get(regexBody));
454
+ // The inside of a regex literal is read by a parser of its own, of
455
+ // which these are the first and the last token. The closing
456
+ // delimiter is found the way termToken found it when it made the
457
+ // literal one token, so that the two agree on where the regex ends.
458
+ if (term == regexLitOpen) {
459
+ let opening = regexOpening(input), end = opening ? regexEnd(input, opening) : -1;
460
+ // Without one, which takes a parse that does not start from a
461
+ // literal, the regex runs to the end of what is parsed.
462
+ return context.withRegex(new RegexBody(null, 0, end < 0 ? Infinity : input.pos + end));
463
+ }
464
+ if (term == regexLitClose)
465
+ return context.withRegex(null);
466
+ // The braces of a block of code in a regex, and of the blocks in it.
467
+ // The tokens `{` and `}` have no term to go by.
468
+ if (regex && !termModes.has(term)) {
469
+ if (input.next == 123 /* Ch.BraceOpen */)
470
+ context = context.withRegex(regex.withBraces(regex.braces + 1));
471
+ else if (input.next == 125 /* Ch.BraceClose */ && regex.braces > 0)
472
+ context = context.withRegex(regex.withBraces(regex.braces - 1));
473
+ }
196
474
  switch (term) {
197
475
  case quoteStart: {
198
- let opening = readOpening(input);
199
- return opening ? new Context(0 /* Mode.Term */, new Quote(context.quote, opening.open, opening.close, opening.count, 0))
200
- : context;
201
- }
202
- case quoteNestOpen:
203
- case quoteNestClose: {
204
- let quote = context.quote;
205
- if (!quote)
476
+ let opening = wordListOpening(input, 0) || readOpening(input);
477
+ if (!opening)
206
478
  return context;
207
- let depth = quote.depth + (term == quoteNestOpen ? 1 : -1);
208
- return new Context(0 /* Mode.Term */, new Quote(quote.parent, quote.open, quote.close, quote.count, depth));
479
+ let quote = new Quote(context.quote, opening.open, opening.close, opening.count, opening.interpolates, opening.escapes);
480
+ return context.withQuote(0 /* Mode.Term */, quote, withHeredocAt(context.heredocs, input, 0));
209
481
  }
210
482
  case quoteEnd:
211
- return new Context(1 /* Mode.AfterTerm */, context.quote && context.quote.parent);
483
+ return context.withQuote(1 /* Mode.AfterTerm */, context.quote && context.quote.parent);
212
484
  }
213
485
  // The whitespace token has no term to go by. It is the only token
214
- // that starts with a space.
215
- if (!termModes.has(term) && isSpace(input.next))
486
+ // that starts with a space. A line break in it leaves the heredocs
487
+ // that found no terminator behind.
488
+ if (!termModes.has(term) && isSpace(input.next)) {
489
+ if (!context.heredocs.length)
490
+ return context;
491
+ for (let pos = 0; isSpace(input.peek(pos)); pos++)
492
+ if (input.peek(pos) == 10 /* Ch.Newline */)
493
+ return context.withHeredocs(noHeredocs);
216
494
  return context;
495
+ }
496
+ if (term == rawString)
497
+ context = context.withHeredocs(withHeredocAt(context.heredocs, input, 0));
217
498
  return context.withMode(modeAfter(term, input, 0, context.mode));
218
499
  },
219
- reduce(context, term) {
220
- return term == Identifier || term == Interpolation ? context.withMode(termModes.get(term)) : context;
500
+ reduce(context, term, _stack, input) {
501
+ // The input is at the start of the node that is reduced.
502
+ return term == Identifier || term == Interpolation ? context.withMode(modeAfter(term, input, 0, context.mode))
503
+ : context;
221
504
  },
222
- reuse(context, node, _stack, input) {
223
- // Walk down to the last token, keeping its offset from the node's
224
- // start, where the input is positioned. Stop at a node that settles
225
- // the mode by itself.
226
- let last = node, offset = 0;
227
- while (last instanceof common.Tree && last.children.length && !termModes.has(last.type.id)) {
228
- offset += last.positions[last.children.length - 1];
229
- last = last.children[last.children.length - 1];
230
- }
231
- let term;
232
- if (last instanceof common.Tree) {
233
- term = last.type.id;
234
- }
235
- else {
236
- // A buffer holds its nodes in pre-order, as type, start, end and
237
- // the index after the node's last descendant.
238
- let { buffer } = last, index = 0;
239
- for (let from = 0, to = buffer.length;;) {
240
- index = from;
241
- while (buffer[index + 3] < to)
242
- index = buffer[index + 3];
243
- from = index + 4;
244
- to = buffer[index + 3];
245
- if (from == to || termModes.has(buffer[index]))
246
- break;
247
- }
248
- term = buffer[index];
249
- offset += buffer[index + 1];
250
- }
251
- if (skippedTerms.has(term))
252
- return context;
505
+ // A node that is reused leaves the quote and the regex of the context
506
+ // as they are: the grammar pairs the delimiters that nest in a quote
507
+ // (NestedDelimiters) and the braces of a block, so a node closes what
508
+ // it opens.
509
+ reuse(context, node, stack, input) {
510
+ let inQuote = context.quote != null && inQuoteText(stack);
511
+ context = context.withHeredocs(heredocsAfter(node, input, context.heredocs, inQuote));
512
+ // The node's positions count from its start, where the input is.
513
+ let last = lastToken$1(node.topNode);
253
514
  // What comes before a trailing `++` in a reused node is a term.
254
- return context.withMode(modeAfter(term, input, offset, 1 /* Mode.AfterTerm */));
515
+ return last ? context.withMode(modeAfter(last.type.id, input, last.from, 1 /* Mode.AfterTerm */)) : context;
255
516
  },
256
517
  hash: context => context.hash
257
518
  });
@@ -259,7 +520,7 @@ function context(stack) { return stack.context; }
259
520
  // Whether a token of `size` characters at the current position is where
260
521
  // a term starts, as opposed to an infix operator.
261
522
  function inTermPosition(input, mode, size) {
262
- if (mode == 0 /* Mode.Term */)
523
+ if (mode == 0 /* Mode.Term */ || mode == 4 /* Mode.AfterMulti */)
263
524
  return true;
264
525
  if (mode == 1 /* Mode.AfterTerm */)
265
526
  return false;
@@ -272,21 +533,28 @@ function inTermPosition(input, mode, size) {
272
533
  // #`(( ... )), needs the same number of closing ones. Brackets of the
273
534
  // same kind nest. An unclosed comment runs to the end of the input.
274
535
  const blockComment = new lr.ExternalTokenizer(input => {
275
- if (input.next != 35 /* Ch.Hash */ || input.peek(1) != 96 /* Ch.Backtick */)
276
- return;
277
- let open = input.peek(2), close = brackets[open];
278
- if (close == null)
536
+ let end = embeddedCommentEnd(input, 0);
537
+ if (!end)
279
538
  return;
280
- let count = 1;
281
- while (input.peek(2 + count) == open)
282
- count++;
283
- let end = rawEnd(input, 2 + count, { open, close, count, escapes: false });
284
539
  if (end < 0)
285
540
  skipToEnd(input);
286
541
  else
287
542
  input.advance(end);
288
543
  input.acceptToken(BlockComment);
289
544
  });
545
+ // The offset after the embedded comment that starts at offset `at`.
546
+ // Zero when none starts there, and -1 when it is not closed.
547
+ function embeddedCommentEnd(input, at) {
548
+ if (input.peek(at) != 35 /* Ch.Hash */ || input.peek(at + 1) != 96 /* Ch.Backtick */)
549
+ return 0;
550
+ let open = input.peek(at + 2), close = brackets$1[open];
551
+ if (close == null)
552
+ return 0;
553
+ let count = 1;
554
+ while (input.peek(at + 2 + count) == open)
555
+ count++;
556
+ return rawEnd(input, at + 2 + count, { open, close, count, escapes: false });
557
+ }
290
558
  function isBlank(ch) { return ch == 32 /* Ch.Space */ || ch == 9 /* Ch.Tab */ || ch == 13 /* Ch.Return */; }
291
559
  // The offset of the first character at or after `pos` that is not a blank.
292
560
  function blanksEnd(input, pos) {
@@ -294,6 +562,18 @@ function blanksEnd(input, pos) {
294
562
  pos++;
295
563
  return pos;
296
564
  }
565
+ // How far a token looks over the whitespace after it, to see what
566
+ // follows. A token must not look far past its end: see the note on
567
+ // lookahead in AGENTS.md.
568
+ const maxGap = 20;
569
+ // The offset of the first character at or after `pos` that is not a
570
+ // blank, or not whitespace when `lines` is set, going over at most
571
+ // maxGap characters.
572
+ function gapEnd(input, pos, lines = false) {
573
+ for (let end = pos + maxGap; pos < end && (lines ? isSpace : isBlank)(input.peek(pos));)
574
+ pos++;
575
+ return pos;
576
+ }
297
577
  // Whether only blanks separate the current position from the start of its line.
298
578
  function atLineStart(input) {
299
579
  for (let i = -1;; i--) {
@@ -323,121 +603,263 @@ function skipBlanks(input) {
323
603
  // Skips blanks, and tells whether the rest of the line starts with `text`.
324
604
  function lineStartsWith(input, text) {
325
605
  skipBlanks(input);
606
+ return startsWith(input, 0, text);
607
+ }
608
+ // Whether the text from offset `pos` starts with `text`, as a whole name.
609
+ function startsWith(input, pos, text) {
326
610
  for (let i = 0; i < text.length; i++)
327
- if (input.peek(i) != text.charCodeAt(i))
611
+ if (input.peek(pos + i) != text.charCodeAt(i))
328
612
  return false;
329
- return !continuesName(input, text.length);
613
+ return !continuesName(input, pos + text.length);
330
614
  }
331
615
  const podDirective = /^(begin|for|end|finish|head\d*|item\d*|para|code|input|output|defn|comment|table|pod|rakudoc|config|alias|nested|data|[A-Z]{2,})$/;
332
- // A Pod block, from its `=directive` at the start of a line.
333
- // =begin name ... =end name delimited, can hold blocks of other names
334
- // =finish the rest of the file
335
- // =for name, =head1, ... up to the next blank line
336
- const podToken = new lr.ExternalTokenizer(input => {
616
+ const podCodeBlock = /^(code|input|output)$/;
617
+ // The offset after the directive that starts at the current position,
618
+ // as in `=begin` or `=head1`, or -1 when there is none.
619
+ function podDirectiveEnd(input) {
337
620
  if (input.next != 61 /* Ch.Equals */ || !isAsciiLetter(input.peek(1)) || !atLineStart(input))
338
- return;
621
+ return -1;
339
622
  let end = 1;
340
623
  while (isAsciiLetter(input.peek(end)) || isDigit(input.peek(end)))
341
624
  end++;
342
625
  let directive = word(input, 1, end);
343
626
  if (!podDirective.test(directive) || continuesName(input, end))
344
- return;
627
+ return -1;
345
628
  // A semantic block such as =TITLE has to start in the first column.
346
629
  // An indented `=FOO + 1` continues an assignment.
347
630
  if (directive.charCodeAt(0) <= 90 /* Ch.Z */ && input.peek(-1) >= 0 && input.peek(-1) != 10 /* Ch.Newline */)
631
+ return -1;
632
+ return end;
633
+ }
634
+ // The offset after the name that goes with a directive which ends at
635
+ // offset `end`: `=begin pod`, `=for comment`, `=end code`. The other
636
+ // directives have none.
637
+ function podNameEnd(input, end) {
638
+ if (!/^(begin|for|end)$/.test(word(input, 1, end)))
639
+ return end;
640
+ let name = nameEnd(input, blanksEnd(input, end));
641
+ return name < 0 ? end : name;
642
+ }
643
+ // The block that the directive at the current position starts.
644
+ // =begin name ... =end name delimited, can hold blocks of other names
645
+ // =finish the rest of the file
646
+ // =for name, =head1, ... up to the next blank line
647
+ function readPodStart(input) {
648
+ let end = podDirectiveEnd(input), directive = word(input, 1, end);
649
+ if (directive == "finish")
650
+ return new PodBlock(2 /* PodKind.Finish */, "", false);
651
+ let nameStart = blanksEnd(input, end), name = word(input, nameStart, Math.max(nameStart, nameEnd(input, nameStart)));
652
+ if (directive == "begin")
653
+ return new PodBlock(0 /* PodKind.Delimited */, name, podCodeBlock.test(name));
654
+ return new PodBlock(1 /* PodKind.Paragraph */, "", podCodeBlock.test(directive == "for" ? name : directive));
655
+ }
656
+ // Whether the current position is right after a `=head` directive,
657
+ // where the text of the heading starts.
658
+ function atHeadingText(input) {
659
+ let pos = -1;
660
+ while (isDigit(input.peek(pos)))
661
+ pos--;
662
+ for (let i = 0; i < 5; i++)
663
+ if (input.peek(pos - i) != "=head".charCodeAt(4 - i))
664
+ return false;
665
+ for (pos -= 5; isBlank(input.peek(pos)); pos--) { }
666
+ return input.peek(pos) < 0 || input.peek(pos) == 10 /* Ch.Newline */;
667
+ }
668
+ // Whether a formatting code starts at offset `pos`: B<, C<<, L«
669
+ function isFormattingCode(input, pos) {
670
+ let ch = input.peek(pos), after = input.peek(pos + 1), before = input.peek(pos - 1);
671
+ return ch >= 65 /* Ch.A */ && ch <= 90 /* Ch.Z */ && (after == 60 /* Ch.Less */ || after == 171 /* Ch.GuillemetOpen */) &&
672
+ !isAsciiLetter(before) && !isDigit(before);
673
+ }
674
+ // The offset after the formatting code at the current position. When
675
+ // the code is not closed in its paragraph, this is the negated offset
676
+ // of the end of the text that was gone over to find that out.
677
+ function formattingCodeEnd(input) {
678
+ let open = input.peek(1), close = brackets$1[open], count = 1;
679
+ while (open == 60 /* Ch.Less */ && input.peek(1 + count) == open)
680
+ count++;
681
+ for (let pos = 1 + count, depth = 1;; pos++) {
682
+ let ch = input.peek(pos);
683
+ if (ch < 0)
684
+ return -pos;
685
+ if (ch == 10 /* Ch.Newline */) {
686
+ // A blank line or a directive ends the paragraph.
687
+ let next = blanksEnd(input, pos + 1), after = input.peek(next);
688
+ if (after < 0 || after == 10 /* Ch.Newline */ || after == 61 /* Ch.Equals */ && isAsciiLetter(input.peek(next + 1)))
689
+ return -pos;
690
+ }
691
+ else if (ch == close && repeats(input, pos, close, count)) {
692
+ pos += count - 1;
693
+ if (--depth == 0)
694
+ return pos + 1;
695
+ }
696
+ else if (ch == open && count == 1) {
697
+ // C<a < b> is not closed by the first `>` when another `<` came before it.
698
+ depth++;
699
+ }
700
+ }
701
+ }
702
+ const formattingTerms = { 66: PodStrong, 73: PodEmphasis, 67: PodCode, 76: PodLink };
703
+ // The pieces of a Pod block, which starts with a `=directive` at the
704
+ // start of a line. What it is, and with that where it ends, is kept in
705
+ // the context.
706
+ const podToken = new lr.ExternalTokenizer((input, stack) => {
707
+ let { pod } = context(stack);
708
+ if (!pod) {
709
+ let end = podDirectiveEnd(input);
710
+ if (end > 0)
711
+ input.acceptToken(podStart, podNameEnd(input, end));
348
712
  return;
349
- input.advance(end);
350
- if (directive == "finish") {
351
- skipToEnd(input);
352
713
  }
353
- else if (directive == "begin") {
354
- skipBlanks(input);
355
- let name = word(input, 0, Math.max(0, nameEnd(input, 0)));
356
- for (;;) {
357
- skipLine(input);
358
- if (input.next < 0)
359
- break;
360
- input.advance();
361
- if (!lineStartsWith(input, "=end"))
362
- continue;
363
- input.advance(4);
364
- if (lineStartsWith(input, name)) {
365
- skipLine(input);
366
- break;
367
- }
368
- }
714
+ let next = input.next;
715
+ if (next < 0)
716
+ return input.acceptToken(podEnd);
717
+ if (next == 10 /* Ch.Newline */ && pod.kind == 1 /* PodKind.Paragraph */) {
718
+ // Ends before a blank line or another directive.
719
+ let pos = blanksEnd(input, 1), ch = input.peek(pos);
720
+ if (ch == 10 /* Ch.Newline */ || ch < 0 || ch == 61 /* Ch.Equals */ && isAsciiLetter(input.peek(pos + 1)))
721
+ return input.acceptToken(podEnd);
369
722
  }
370
- else {
371
- for (;;) {
723
+ let directive = pod.kind == 0 /* PodKind.Delimited */ ? podDirectiveEnd(input) : -1;
724
+ if (directive > 0) {
725
+ if (directive == 4 && startsWith(input, 1, "end") && startsWith(input, blanksEnd(input, 4), pod.name)) {
372
726
  skipLine(input);
373
- if (input.next < 0)
374
- break;
375
- // Stop before a blank line or another directive.
376
- let i = blanksEnd(input, 1), ch = input.peek(i);
377
- if (ch == 10 /* Ch.Newline */ || ch < 0 || ch == 61 /* Ch.Equals */ && isAsciiLetter(input.peek(i + 1)))
378
- break;
379
- input.advance();
727
+ return input.acceptToken(podEndDirective);
380
728
  }
729
+ return input.acceptToken(PodDirective, podNameEnd(input, directive));
730
+ }
731
+ if (atHeadingText(input) && !isLineEnd(input, 0)) {
732
+ skipLine(input);
733
+ return input.acceptToken(PodHeading);
381
734
  }
382
- input.acceptToken(Pod);
735
+ if (!pod.code && isFormattingCode(input, 0)) {
736
+ let end = formattingCodeEnd(input);
737
+ if (end > 0)
738
+ return input.acceptToken(formattingTerms[next] || PodFormat, end);
739
+ // What looked like a code is text. The text that was gone over is
740
+ // made one token, so that no token has looked beyond its own end.
741
+ // A token that does keeps the tokens before it from being reused.
742
+ return input.acceptToken(podText, -end);
743
+ }
744
+ // Text, up to where one of the above may apply.
745
+ for (;;) {
746
+ input.advance();
747
+ let ch = input.next;
748
+ if (ch < 0 || ch == 10 /* Ch.Newline */ || !pod.code && isFormattingCode(input, 0))
749
+ break;
750
+ // A directive can only follow the blanks at the start of a line.
751
+ if (ch == 61 /* Ch.Equals */ && atLineStart(input))
752
+ break;
753
+ }
754
+ input.acceptToken(podText);
383
755
  });
384
756
  const quoteWords = {
385
757
  q: "raw", Q: "raw", qw: "raw", qww: "raw", qx: "raw", Qw: "raw", Qww: "raw", Qx: "raw",
386
758
  qq: "interpolating", qqw: "interpolating", qqww: "interpolating", qqx: "interpolating",
387
759
  rx: "regex", m: "regex", ms: "regex",
388
- s: "substitution", ss: "substitution", S: "substitution", tr: "substitution", TR: "substitution"
760
+ s: "substitution", ss: "substitution", S: "substitution",
761
+ // Like a substitution, but its first part is not a regex.
762
+ tr: "transliteration", TR: "transliteration"
389
763
  };
764
+ // The kinds of interpolation that the adverbs of a quote switch.
765
+ const interpolationAdverbs = new Map([
766
+ ["c", 1 /* Interpolate.Closure */], ["closure", 1 /* Interpolate.Closure */],
767
+ ["s", 2 /* Interpolate.Scalar */], ["scalar", 2 /* Interpolate.Scalar */],
768
+ ["a", 4 /* Interpolate.Array */], ["array", 4 /* Interpolate.Array */],
769
+ ["h", 8 /* Interpolate.Hash */], ["hash", 8 /* Interpolate.Hash */],
770
+ ["f", 16 /* Interpolate.Function */], ["function", 16 /* Interpolate.Function */],
771
+ ["b", 32 /* Interpolate.Backslash */], ["backslash", 32 /* Interpolate.Backslash */],
772
+ ["qq", 63 /* Interpolate.All */], ["double", 63 /* Interpolate.All */]
773
+ ]);
774
+ // The opening of a word list that interpolates, at offset `at`: << or «
775
+ function wordListOpening(input, at) {
776
+ let open = input.peek(at), count = open == 60 /* Ch.Less */ ? 2 : 1;
777
+ if (open != 171 /* Ch.GuillemetOpen */ && !(open == 60 /* Ch.Less */ && input.peek(at + 1) == 60 /* Ch.Less */))
778
+ return null;
779
+ return { open, close: brackets$1[open], count, start: at + count, kind: "interpolating",
780
+ interpolates: 63 /* Interpolate.All */, escapes: true };
781
+ }
782
+ // The opening of a quote that starts with a quote character at offset `at`.
783
+ function quoteCharOpening(open, close, at, interpolates, escapes = true) {
784
+ return { open, close, count: 1, start: at + 1, kind: interpolates ? "interpolating" : "raw", interpolates, escapes };
785
+ }
390
786
  // The delimiters, other than brackets, that are accepted after a quote word.
391
787
  function isQuoteDelimiter(ch) {
392
788
  return ch == 47 /* Ch.Slash */ || ch == 33 /* Ch.Bang */ || ch == 124 /* Ch.Pipe */ || ch == 126 /* Ch.Tilde */ || ch == 94 /* Ch.Caret */ ||
393
789
  ch == 37 /* Ch.Percent */ || ch == 64 /* Ch.At */ || ch == 34 /* Ch.DoubleQuote */ || ch == 39 /* Ch.Apostrophe */;
394
790
  }
395
- // Reads the start of a quote at the current position: a quote character,
396
- // or a quote word (q, qq, m, s, ...) with its adverbs and its delimiter.
397
- function readOpening(input) {
398
- let next = input.next;
791
+ // Reads the start of a quote at offset `at`: a quote character, or a
792
+ // quote word (q, qq, m, s, ...) with its adverbs and its delimiter.
793
+ // The offsets in the result count from the current position.
794
+ function readOpening(input, at = 0) {
795
+ let next = input.peek(at);
399
796
  if (next == 34 /* Ch.DoubleQuote */)
400
- return { open: next, close: next, count: 1, start: 1, kind: "interpolating", escapes: true };
797
+ return quoteCharOpening(next, next, at, 63 /* Interpolate.All */);
401
798
  if (next == 39 /* Ch.Apostrophe */)
402
- return { open: next, close: next, count: 1, start: 1, kind: "raw", escapes: true };
799
+ return quoteCharOpening(next, next, at, 0);
403
800
  if (next == 0x201c /* “ */)
404
- return { open: next, close: 0x201d, count: 1, start: 1, kind: "interpolating", escapes: true };
801
+ return quoteCharOpening(next, 0x201d, at, 63 /* Interpolate.All */);
405
802
  if (next == 0x2018 /* ‘ */)
406
- return { open: next, close: 0x2019, count: 1, start: 1, kind: "raw", escapes: true };
803
+ return quoteCharOpening(next, 0x2019, at, 0);
407
804
  if (next == 0x300c /* 「 */)
408
- return { open: next, close: 0x300d, count: 1, start: 1, kind: "raw", escapes: false };
805
+ return quoteCharOpening(next, 0x300d, at, 0, false);
409
806
  if (!isAsciiLetter(next))
410
807
  return null;
411
- let pos = 1;
808
+ let pos = at + 1;
412
809
  while (isAsciiLetter(input.peek(pos)))
413
810
  pos++;
414
- if (pos > 4)
811
+ if (pos - at > 4)
415
812
  return null;
416
- let name = word(input, 0, pos), kind = quoteWords[name];
813
+ let name = word(input, at, pos), kind = quoteWords[name];
417
814
  if (!kind)
418
815
  return null;
419
- // Adverbs: :w, :to, :g, :x(2)
420
- let adverbs = false;
816
+ // Adverbs: :w, :to, :g, :x(2). Those of a regex mean other things
817
+ // than those of a quote: m:s/a b/
818
+ let adverbs = false, heredoc = false, isQuote = kind == "raw" || kind == "interpolating";
819
+ let interpolates = kind == "interpolating" ? 63 /* Interpolate.All */ : 0;
820
+ // Whether a backslash escapes the delimiter, apart from the escapes
821
+ // of :b. That comes with q and its :q adverb: Q and qq:!b have none.
822
+ let escapes = isQuote ? kind == "raw" && next == 113 /* Ch.q */ : true;
421
823
  while (input.peek(pos) == 58 /* Ch.Colon */ && (isAsciiLetter(input.peek(pos + 1)) || input.peek(pos + 1) == 33 /* Ch.Bang */)) {
422
824
  adverbs = true;
825
+ let negated = input.peek(pos + 1) == 33 /* Ch.Bang */, adverbStart = pos + (negated ? 2 : 1);
423
826
  pos += 2;
424
827
  while (isAsciiLetter(input.peek(pos)) || isDigit(input.peek(pos)))
425
828
  pos++;
829
+ let adverb = word(input, adverbStart, pos);
830
+ // An argument that is False or 0 switches the adverb off: q:c(False)
426
831
  if (input.peek(pos) == 40 /* Ch.ParenOpen */) {
832
+ let argStart = pos + 1;
427
833
  while (input.peek(pos) != 41 /* Ch.ParenClose */) {
428
834
  if (input.peek(pos) < 0 || input.peek(pos) == 10 /* Ch.Newline */)
429
835
  return null;
430
836
  pos++;
431
837
  }
838
+ if (/^\s*(False|0)\s*$/.test(word(input, argStart, pos)))
839
+ negated = true;
432
840
  pos++;
433
841
  }
842
+ if (isQuote) {
843
+ let bits = interpolationAdverbs.get(adverb);
844
+ if (bits != null)
845
+ interpolates = negated ? interpolates & ~bits : interpolates | bits;
846
+ else if (!negated && (adverb == "q" || adverb == "single"))
847
+ escapes = true;
848
+ else if (!negated && (adverb == "to" || adverb == "heredoc"))
849
+ heredoc = true;
850
+ }
434
851
  }
435
852
  if (!adverbs && continuesName(input, pos))
436
853
  return null;
437
- // The delimiter has to follow directly. Raku also takes `q {...}`, but
438
- // then `S { ... }` and `m ($x)` on a name that is not a quote word
439
- // would turn into quotes.
440
- let open = input.peek(pos), close = brackets[open];
854
+ // The delimiter follows directly, or, for q, qq and Q, after blanks
855
+ // on the same line: q {...}. Not for m, s and the others: `s { ... }`
856
+ // and `m ($x)` are more often calls of a routine of one's own.
857
+ if (isQuote && isBlank(input.peek(pos))) {
858
+ let bracket = gapEnd(input, pos);
859
+ if (brackets$1[input.peek(bracket)] != null && input.peek(bracket) != 40 /* Ch.ParenOpen */)
860
+ pos = bracket;
861
+ }
862
+ let open = input.peek(pos), close = brackets$1[open];
441
863
  if (close == null) {
442
864
  if (!isQuoteDelimiter(open))
443
865
  return null;
@@ -450,7 +872,11 @@ function readOpening(input) {
450
872
  if (close != open)
451
873
  while (input.peek(pos + count) == open)
452
874
  count++;
453
- return { open, close, count, start: pos + count, kind, escapes: name.charCodeAt(0) != 81 /* Ch.Q */ };
875
+ if (isQuote)
876
+ kind = interpolates ? "interpolating" : "raw";
877
+ if (interpolates & 32 /* Interpolate.Backslash */)
878
+ escapes = true;
879
+ return { open, close, count, start: pos + count, kind, interpolates, escapes, heredoc };
454
880
  }
455
881
  // The offset after the delimiter that closes a quote whose content
456
882
  // starts at `pos`, or -1 when it is not closed.
@@ -493,7 +919,7 @@ function quotedEnd(input, pos) {
493
919
  // The offset of the delimiter that closes a regex whose content starts
494
920
  // at `pos`, or -1. Delimiters do not count inside quotes, character
495
921
  // classes, comments and code blocks.
496
- function regexEnd(input, pos, open, close, count) {
922
+ function regexEnd(input, { start: pos, open, close, count }) {
497
923
  for (let depth = 1;;) {
498
924
  let ch = input.peek(pos);
499
925
  if (ch < 0)
@@ -541,13 +967,20 @@ function regexEnd(input, pos, open, close, count) {
541
967
  break;
542
968
  pos = more;
543
969
  }
970
+ // The bracket that closes the class is not one of the regex: rx[ <[a]> ]
971
+ pos++;
544
972
  }
545
973
  else if (ch == 35 /* Ch.Hash */) {
546
- while (input.peek(pos) >= 0 && input.peek(pos) != 10 /* Ch.Newline */)
547
- pos++;
974
+ // An embedded comment, or one that runs to the end of the line.
975
+ let end = embeddedCommentEnd(input, pos);
976
+ if (end > 0)
977
+ pos = end;
978
+ else
979
+ while (input.peek(pos) >= 0 && input.peek(pos) != 10 /* Ch.Newline */)
980
+ pos++;
548
981
  }
549
982
  else if (ch == 123 /* Ch.BraceOpen */ && close != 125 /* Ch.BraceClose */) {
550
- let end = regexEnd(input, pos + 1, 123 /* Ch.BraceOpen */, 125 /* Ch.BraceClose */, 1);
983
+ let end = regexEnd(input, { start: pos + 1, open: 123 /* Ch.BraceOpen */, close: 125 /* Ch.BraceClose */, count: 1 });
551
984
  pos = end < 0 ? pos + 1 : end + 1;
552
985
  }
553
986
  else {
@@ -557,25 +990,41 @@ function regexEnd(input, pos, open, close, count) {
557
990
  }
558
991
  // The offset after a regex or substitution, given its opening, or -1.
559
992
  function regexTokenEnd(input, opening) {
560
- let { open, close, count } = opening;
561
- let end = regexEnd(input, opening.start, open, close, count);
993
+ let end = regexEnd(input, opening);
562
994
  if (end < 0)
563
995
  return -1;
564
- end += count;
996
+ end += opening.count;
565
997
  // s/a/b/ has a second part. s{a} = 'b' does not.
566
- if (opening.kind == "substitution" && open == close)
998
+ if (opening.kind != "regex" && opening.open == opening.close)
567
999
  end = rawEnd(input, end, opening);
568
1000
  return end;
569
1001
  }
1002
+ // The opening of a bare regex: /a/
1003
+ const slashOpening = { open: 47 /* Ch.Slash */, close: 47 /* Ch.Slash */, count: 1, start: 1, kind: "regex",
1004
+ interpolates: 0, escapes: true };
1005
+ // The opening of the regex literal at the current position. The token
1006
+ // that makes a literal one token, and the parse of its inside, both
1007
+ // find its closing delimiter from this, so that they agree.
1008
+ function regexOpening(input) {
1009
+ if (input.next == 47 /* Ch.Slash */)
1010
+ return slashOpening;
1011
+ let opening = readOpening(input);
1012
+ return opening && (opening.kind == "regex" || opening.kind == "substitution") ? opening : null;
1013
+ }
570
1014
  // The offset after a word list that opens at the current position, or
571
1015
  // -1. A list that spans lines has to hold plain words, so that a stray
572
1016
  // `<` does not swallow the code up to some later `>`. `strict` is for
573
1017
  // a subscript, which has to be on one line, and cannot hold what
574
1018
  // would make it a comparison: `$a<5 && $b>3`.
1019
+ //
1020
+ // A list with double angles is rarely a stray operator, and may hold
1021
+ // code over a few lines. Not over more: one that is being typed would
1022
+ // swallow the code up to some later `>>`.
575
1023
  function wordListEnd(input, strict) {
576
1024
  let open = input.next, close = open == 171 /* Ch.GuillemetOpen */ ? 187 /* Ch.GuillemetClose */ : 62 /* Ch.Greater */;
577
1025
  let count = open == 60 /* Ch.Less */ && input.peek(1) == 60 /* Ch.Less */ ? 2 : 1;
578
- for (let pos = count, lines = 0, code = false;; pos++) {
1026
+ let codeLines = open == 171 /* Ch.GuillemetOpen */ || count == 2 ? maxCodeWordListLines : 1;
1027
+ for (let pos = count, lines = 1, code = false;; pos++) {
579
1028
  let ch = input.peek(pos);
580
1029
  if (ch == close && repeats(input, pos, close, count))
581
1030
  return pos + count;
@@ -586,7 +1035,7 @@ function wordListEnd(input, strict) {
586
1035
  else if (ch == 59 /* Ch.Semicolon */ || ch == 123 /* Ch.BraceOpen */ || ch == 125 /* Ch.BraceClose */ ||
587
1036
  ch == 40 /* Ch.ParenOpen */ || ch == 41 /* Ch.ParenClose */)
588
1037
  code = true;
589
- if (lines && (code || strict))
1038
+ if (lines > 1 && (code && lines > codeLines || strict))
590
1039
  return -1;
591
1040
  if (strict && (code || ch == 36 /* Ch.Dollar */ || ch == 64 /* Ch.At */ || ch == 37 /* Ch.Percent */ || ch == 38 /* Ch.Amp */ ||
592
1041
  ch == 124 /* Ch.Pipe */ || ch == 61 /* Ch.Equals */ || ch == 44 /* Ch.Comma */ || ch == 63 /* Ch.Question */ ||
@@ -594,19 +1043,77 @@ function wordListEnd(input, strict) {
594
1043
  return -1;
595
1044
  }
596
1045
  }
1046
+ // How many lines a <<...>> list that holds code may span.
1047
+ const maxCodeWordListLines = 10;
1048
+ // How long a <<...>> subscript can be. Whether it is closed is found
1049
+ // out from its start, and a token must not look far past its end.
1050
+ const maxDoubleSubscript = 24;
1051
+ // Whether a character can start or end the content of a <<...>>
1052
+ // subscript: part of a word, a variable or a quote. An operator
1053
+ // character cannot, which tells the hyper operators apart: @a<<+>>@b,
1054
+ // @a«R-»@b
1055
+ function isSubscriptEdge(ch) {
1056
+ return isIdentifierStart(ch) || isDigit(ch) || ch == 39 /* Ch.Apostrophe */ || ch == 34 /* Ch.DoubleQuote */;
1057
+ }
1058
+ // The offset after the <<...>> subscript at the current position, with
1059
+ // the given opening, or -1. It is short, on one line, and holds no
1060
+ // punctuation of statements.
1061
+ function doubleSubscriptEnd(input, { start, close, count }) {
1062
+ let first = input.peek(start);
1063
+ if (!isSubscriptEdge(first) && first != 36 /* Ch.Dollar */ && first != 64 /* Ch.At */ && first != 37 /* Ch.Percent */ && first != 38 /* Ch.Amp */)
1064
+ return -1;
1065
+ for (let pos = start; pos < maxDoubleSubscript; pos++) {
1066
+ let ch = input.peek(pos);
1067
+ if (ch == close && repeats(input, pos, close, count))
1068
+ return isSubscriptEdge(input.peek(pos - 1)) ? pos + count : -1;
1069
+ if (ch < 0 || ch == 10 /* Ch.Newline */ || ch == 59 /* Ch.Semicolon */ || ch == 123 /* Ch.BraceOpen */ || ch == 125 /* Ch.BraceClose */ ||
1070
+ ch == 40 /* Ch.ParenOpen */ || ch == 41 /* Ch.ParenClose */)
1071
+ return -1;
1072
+ }
1073
+ return -1;
1074
+ }
1075
+ // Whether every `{` between two offsets has its `}` there, and the
1076
+ // other way around.
1077
+ function hasBalancedBraces(input, from, to) {
1078
+ let depth = 0;
1079
+ for (let pos = from; pos < to; pos++) {
1080
+ let ch = input.peek(pos);
1081
+ if (ch == 123 /* Ch.BraceOpen */)
1082
+ depth++;
1083
+ else if (ch == 125 /* Ch.BraceClose */ && --depth < 0)
1084
+ return false;
1085
+ }
1086
+ return depth == 0;
1087
+ }
597
1088
  // A `<` subscript right after a term: %h<key>
598
1089
  function subscriptEnd(input) {
599
1090
  if (input.next != 60 /* Ch.Less */ || isSpace(input.peek(-1)) || input.peek(1) == 60 /* Ch.Less */)
600
1091
  return -1;
601
1092
  return wordListEnd(input, true);
602
1093
  }
1094
+ // The keywords that are routines or prefix operators, and can be called
1095
+ // with parentheses. `if(1)` and `my($x)` are not calls of this kind.
1096
+ const callableKeywords = /^(take|return|emit|next|last|redo|proceed|succeed|so|not)$/;
1097
+ // The words that declare what follows `multi`, `proto` or `only`.
1098
+ const declarators$1 = /^(sub|method|submethod|token|rule|regex)$/;
1099
+ // Whether a declarator starts with the given word, as `su` starts `sub`.
1100
+ function startsDeclarator(name) {
1101
+ return ["sub", "method", "submethod", "token", "rule", "regex"].some(declarator => declarator.startsWith(name));
1102
+ }
1103
+ const operatorCategory = /^(infix|prefix|postfix|circumfix|postcircumfix|term)$/;
603
1104
  function isWordOperator(name) {
604
1105
  return name == "x" || name == "xx" || name == "min" || name == "max" || name == "Z" || name == "X";
605
1106
  }
606
1107
  // The tokens that depend on whether a term or an operator is expected.
607
1108
  const termToken = new lr.ExternalTokenizer((input, stack) => {
608
- if (!stack.canShift(Regex))
1109
+ if (!stack.canShift(regexLiteral)) {
1110
+ // At the start of the parse of the inside of a word list that
1111
+ // interpolates, its opening delimiter.
1112
+ let opening = context(stack) == startContext && stack.canShift(quoteStart) ? wordListOpening(input, 0) : null;
1113
+ if (opening)
1114
+ input.acceptToken(quoteStart, opening.start);
609
1115
  return;
1116
+ }
610
1117
  let next = input.next, { mode } = context(stack);
611
1118
  if (isIdentifierStart(next)) {
612
1119
  // The Z, X and R meta operators: Z+, X~, R-, Z=>
@@ -618,14 +1125,29 @@ const termToken = new lr.ExternalTokenizer((input, stack) => {
618
1125
  return input.acceptToken(wordOperator, pos);
619
1126
  }
620
1127
  // A word before `=>` is a key, whatever it spells.
621
- let end = nameEnd(input, 0), after = end;
622
- while (input.peek(after) == 32 /* Ch.Space */ || input.peek(after) == 9 /* Ch.Tab */)
623
- after++;
1128
+ let end = nameEnd(input, 0), after = gapEnd(input, end);
624
1129
  if (input.peek(after) == 61 /* Ch.Equals */ && input.peek(after + 1) == 62 /* Ch.Greater */)
625
- return input.acceptToken(fatArrowKey, end);
1130
+ return input.acceptToken(plainName, end);
1131
+ // After `multi`, `proto` or `only`, a name that is not a declarator
1132
+ // is that of a sub: multi foo($x) { }
1133
+ // Only with a signature or a body right after it. A name alone may
1134
+ // be a declarator that is still being typed (`multi su`), and
1135
+ // `only` and `proto` can be routines of one's own (`only foo, 1`).
1136
+ // The signature may start on the next line, unless the name can
1137
+ // still become a declarator: `multi su` above a line with a `(`.
1138
+ if (mode == 4 /* Mode.AfterMulti */ && !(end <= 9 && declarators$1.test(word(input, 0, end)))) {
1139
+ let nameTo = categoryEnd(input, 0, end), opens = (ch) => ch == 40 /* Ch.ParenOpen */ || ch == 123 /* Ch.BraceOpen */;
1140
+ if (opens(input.peek(gapEnd(input, nameTo))) ||
1141
+ opens(input.peek(gapEnd(input, nameTo, true))) && !startsDeclarator(word(input, 0, end)))
1142
+ return input.acceptToken(multiName, nameTo);
1143
+ }
1144
+ // So is one of the keywords that are routines, when it is called
1145
+ // with parentheses: take(1). They are 2 to 7 characters long.
1146
+ if (input.peek(end) == 40 /* Ch.ParenOpen */ && end <= 7 && callableKeywords.test(word(input, 0, end)))
1147
+ return input.acceptToken(plainName, end);
626
1148
  if (mode == 1 /* Mode.AfterTerm */) {
627
1149
  // x, xx, min, max, Z and X are operators where one is expected.
628
- if (isWordOperator(word(input, 0, end)))
1150
+ if (end <= 3 && isWordOperator(word(input, 0, end)))
629
1151
  input.acceptToken(wordOperator, end);
630
1152
  return;
631
1153
  }
@@ -634,11 +1156,20 @@ const termToken = new lr.ExternalTokenizer((input, stack) => {
634
1156
  // Squeezed between two terms, a sigil is the operator: $a%b. With a
635
1157
  // space before it, it is a variable: `my Array[Int] %h`.
636
1158
  if (mode == 1 /* Mode.AfterTerm */ && isIdentifierStart(input.peek(1)) && !isSpace(input.peek(-1)))
637
- input.acceptToken(wordOperator, 1);
1159
+ return input.acceptToken(wordOperator, 1);
1160
+ // An operator as a routine: &infix:<+>
1161
+ if (next == 38 /* Ch.Amp */) {
1162
+ let end = nameEnd(input, 1);
1163
+ if (end > 0 && operatorCategory.test(word(input, 1, end))) {
1164
+ let categoryTo = categoryEnd(input, 1, end);
1165
+ if (categoryTo > end)
1166
+ input.acceptToken(operatorVariable, categoryTo);
1167
+ }
1168
+ }
638
1169
  return;
639
1170
  }
640
1171
  else if (next == 60 /* Ch.Less */ || next == 171 /* Ch.GuillemetOpen */) {
641
- let end;
1172
+ let end, opening = wordListOpening(input, 0);
642
1173
  if (inTermPosition(input, mode, 1)) {
643
1174
  // [<] and [<=] are reductions, <-> starts a pointy block.
644
1175
  let after = input.peek(1);
@@ -646,19 +1177,31 @@ const termToken = new lr.ExternalTokenizer((input, stack) => {
646
1177
  return;
647
1178
  end = wordListEnd(input, false);
648
1179
  }
1180
+ else if (opening && !isSpace(input.peek(-1))) {
1181
+ // A subscript with double angles, which touches its term:
1182
+ // %h«$key», :a<<b $c>>
1183
+ end = doubleSubscriptEnd(input, opening);
1184
+ }
649
1185
  else {
650
1186
  end = subscriptEnd(input);
651
1187
  }
652
- if (end > 0)
653
- input.acceptToken(rawString, end);
654
- return;
1188
+ if (end <= 0)
1189
+ return;
1190
+ // <<a $b>> and «a $b» interpolate. The list is one token here:
1191
+ // that it is closed has to be known where it starts. Its inside
1192
+ // is parsed again, piece by piece (see index.ts), by rules that
1193
+ // count nested delimiters, escapes and the braces of blocks. A
1194
+ // list that those rules would end elsewhere is not read inside.
1195
+ let interpolates = opening && rawEnd(input, opening.start, opening) == end &&
1196
+ hasBalancedBraces(input, opening.start, end);
1197
+ return input.acceptToken(interpolates ? interpolatingWordList : rawString, end);
655
1198
  }
656
1199
  else if (next == 47 /* Ch.Slash */) {
657
1200
  if (input.peek(1) == 47 /* Ch.Slash */ || !inTermPosition(input, mode, 1))
658
1201
  return;
659
- let end = regexEnd(input, 1, 47 /* Ch.Slash */, 47 /* Ch.Slash */, 1);
1202
+ let end = regexTokenEnd(input, slashOpening);
660
1203
  if (end > 0)
661
- input.acceptToken(Regex, end + 1);
1204
+ input.acceptToken(regexLiteral, end);
662
1205
  return;
663
1206
  }
664
1207
  let opening = readOpening(input);
@@ -666,9 +1209,15 @@ const termToken = new lr.ExternalTokenizer((input, stack) => {
666
1209
  return;
667
1210
  if (opening.kind == "interpolating")
668
1211
  return input.acceptToken(quoteStart, opening.start);
669
- let end = opening.kind == "raw" ? rawEnd(input, opening.start, opening) : regexTokenEnd(input, opening);
1212
+ if (opening.kind != "raw") {
1213
+ let end = regexTokenEnd(input, opening);
1214
+ if (end > 0)
1215
+ input.acceptToken(opening.kind == "transliteration" ? transliteration : regexLiteral, end);
1216
+ return;
1217
+ }
1218
+ let end = rawEnd(input, opening.start, opening);
670
1219
  if (end > 0)
671
- input.acceptToken(opening.kind == "raw" ? rawString : Regex, end);
1220
+ input.acceptToken(rawString, end);
672
1221
  });
673
1222
  // Whether a sigil other than `$` interpolates: only when a subscript or
674
1223
  // a call follows its name. `end` is the offset after the name.
@@ -681,17 +1230,25 @@ function hasPostfix(input, end) {
681
1230
  let methodEnd = nameEnd(input, end + 1);
682
1231
  return methodEnd > 0 && input.peek(methodEnd) == 40 /* Ch.ParenOpen */;
683
1232
  }
1233
+ // Whether the parser is between the pieces of a quote, as opposed to
1234
+ // in the code of a block in it.
1235
+ function inQuoteText(stack) { return stack.canShift(quoteContent); }
1236
+ // Whether the parser is, in a quote, right after a variable or after a
1237
+ // subscript or a call on one: where the grammar takes a quotePostfix.
1238
+ // This is asked of the parser and not of the mode, which is not known
1239
+ // to this precision after a reused node that ends in text of the quote.
1240
+ function afterQuotedVariable(stack) { return stack.canShift(rawString); }
684
1241
  // The pieces of a quote that interpolates.
685
1242
  const quoteToken = new lr.ExternalTokenizer((input, stack) => {
686
- if (!stack.canShift(quoteEnd))
1243
+ if (!inQuoteText(stack))
687
1244
  return;
688
- let { quote, mode } = context(stack);
1245
+ let { quote } = context(stack);
689
1246
  if (!quote)
690
1247
  return;
691
- let { open, close, count } = quote, nests = open != close;
1248
+ let { open, close, count, interpolates } = quote, nests = open != close;
692
1249
  // After a variable, a subscript or a call continues the interpolation.
693
1250
  // `[` and `(` are left to the grammar's own tokens.
694
- if (mode == 1 /* Mode.AfterTerm */) {
1251
+ if (afterQuotedVariable(stack)) {
695
1252
  if (input.next == 91 /* Ch.BracketOpen */ || input.next == 40 /* Ch.ParenOpen */)
696
1253
  return;
697
1254
  let end = subscriptEnd(input);
@@ -699,6 +1256,9 @@ const quoteToken = new lr.ExternalTokenizer((input, stack) => {
699
1256
  return input.acceptToken(rawString, end);
700
1257
  }
701
1258
  let start = input.pos;
1259
+ // The text of a heredoc ends where the input does.
1260
+ if (input.next < 0 && quote == heredocQuote)
1261
+ return input.acceptToken(quoteEnd);
702
1262
  for (;;) {
703
1263
  let next = input.next;
704
1264
  if (next < 0)
@@ -708,11 +1268,27 @@ const quoteToken = new lr.ExternalTokenizer((input, stack) => {
708
1268
  if (input.pos > start)
709
1269
  break;
710
1270
  input.advance(count);
711
- return input.acceptToken(!atClose ? quoteNestOpen : quote.depth ? quoteNestClose : quoteEnd);
1271
+ // Inside a nested pair, a closing delimiter closes that pair.
1272
+ return input.acceptToken(!atClose ? quoteNestOpen : stack.canShift(quoteNestClose) ? quoteNestClose : quoteEnd);
712
1273
  }
713
- if (next == 92 /* Ch.Backslash */ || next == 36 /* Ch.Dollar */ || next == 123 /* Ch.BraceOpen */)
714
- break;
715
- if (next == 64 /* Ch.At */ || next == 37 /* Ch.Percent */ || next == 38 /* Ch.Amp */) {
1274
+ if (next == 92 /* Ch.Backslash */) {
1275
+ if (interpolates & 32 /* Interpolate.Backslash */)
1276
+ break;
1277
+ // Still keeps a delimiter after it from closing the quote.
1278
+ if (quote.escapes && input.peek(1) >= 0)
1279
+ input.advance();
1280
+ }
1281
+ else if (next == 36 /* Ch.Dollar */) {
1282
+ if (interpolates & 2 /* Interpolate.Scalar */)
1283
+ break;
1284
+ }
1285
+ else if (next == 123 /* Ch.BraceOpen */) {
1286
+ if (interpolates & 1 /* Interpolate.Closure */)
1287
+ break;
1288
+ }
1289
+ else if (next == 64 /* Ch.At */ && interpolates & 4 /* Interpolate.Array */ ||
1290
+ next == 37 /* Ch.Percent */ && interpolates & 8 /* Interpolate.Hash */ ||
1291
+ next == 38 /* Ch.Amp */ && interpolates & 16 /* Interpolate.Function */) {
716
1292
  let end = nameEnd(input, 1);
717
1293
  if (end > 0 && hasPostfix(input, end))
718
1294
  break;
@@ -725,37 +1301,27 @@ const quoteToken = new lr.ExternalTokenizer((input, stack) => {
725
1301
  // The text of a heredoc: the lines after the one that holds its opener,
726
1302
  // up to the line that holds only its terminator. As a skipped token, it
727
1303
  // leaves the rest of the opener's line to be parsed as usual. The
728
- // opener is looked up in the text of that line rather than kept in the
729
- // context, so that it is also found when the line's nodes were reused
730
- // from an earlier parse.
731
- const heredocToken = new lr.ExternalTokenizer(input => {
1304
+ // context holds the terminators of the heredocs that the line opened.
1305
+ //
1306
+ // The text is one token, also when it interpolates: that it has a
1307
+ // terminator has to be known where it starts. A text in which
1308
+ // everything interpolates is a token of its own kind, the inside of
1309
+ // which is parsed again (see index.ts), from an empty token that puts
1310
+ // the quote in the context. When a line opens several heredocs, their
1311
+ // texts are one token, which interpolates if all of them do.
1312
+ const heredocToken = new lr.ExternalTokenizer((input, stack) => {
1313
+ if (context(stack) == startContext && stack.canShift(heredocStart))
1314
+ return input.acceptToken(heredocStart);
1315
+ let { heredocs } = context(stack);
1316
+ if (!heredocs.length)
1317
+ return;
732
1318
  let pos = blanksEnd(input, 0);
733
1319
  if (input.peek(pos) != 10 /* Ch.Newline */)
734
1320
  return;
735
- // Read the line that just ended, and look for heredoc openers in it.
736
- let lineStart = 0;
737
- while (lineStart > -500 && input.peek(lineStart - 1) >= 0 && input.peek(lineStart - 1) != 10 /* Ch.Newline */)
738
- lineStart--;
739
- let line = word(input, lineStart, 0);
740
- if (line.indexOf(":to") < 0 && line.indexOf(":heredoc") < 0 || /^\s*#/.test(line))
741
- return;
742
- let opener = /(?:^|[^\w$@%&.'-])[qQ]\w*(?::\w+)*:(?:to|heredoc)(?::\w+)*\s*(\S)/g, found;
743
- let terminators = [];
744
- while (found = opener.exec(line)) {
745
- let open = found[1], close = String.fromCharCode(brackets[open.charCodeAt(0)] || open.charCodeAt(0));
746
- let end = line.indexOf(close, opener.lastIndex);
747
- if (end < 0)
748
- continue;
749
- terminators.push(line.slice(opener.lastIndex, end).trim());
750
- opener.lastIndex = end + 1;
751
- }
752
- if (!terminators.length)
753
- return;
754
1321
  input.advance(pos);
755
- for (let terminator of terminators) {
1322
+ for (let { terminator } of heredocs) {
756
1323
  for (;;) {
757
- // Without its terminator, this is not taken to be a heredoc. The
758
- // opener is found by its text, and may be in a comment or a string.
1324
+ // Without its terminator, this is not taken to be a heredoc.
759
1325
  if (input.next < 0)
760
1326
  return;
761
1327
  input.advance();
@@ -766,22 +1332,218 @@ const heredocToken = new lr.ExternalTokenizer(input => {
766
1332
  skipLine(input);
767
1333
  }
768
1334
  }
769
- input.acceptToken(Heredoc);
1335
+ input.acceptToken(heredocs.every(heredoc => heredoc.interpolates) ? interpolatingHeredoc : rawHeredoc);
770
1336
  });
771
- // The body of a token, rule or regex declaration, up to its closing brace.
772
- const regexBodyToken = new lr.ExternalTokenizer((input, stack) => {
773
- if (!stack.canShift(regexBody))
1337
+ const regexDeclarator = /^(token|rule|regex)$/;
1338
+ const regexDeclaratorPrefix = /^(proto|multi|my|our)$/;
1339
+ // Whether the line that starts at offset `pos` starts a regex
1340
+ // declaration: a declarator, a name, and further on a brace, as in
1341
+ // `token name {`. In a regex, the words alone can be literals:
1342
+ // `token | rule`
1343
+ function startsRegexDeclaration(input, pos) {
1344
+ let start = blanksEnd(input, pos), end = nameEnd(input, start);
1345
+ // proto token, my regex
1346
+ if (end > 0 && isBlank(input.peek(end)) && regexDeclaratorPrefix.test(word(input, start, end))) {
1347
+ start = blanksEnd(input, end);
1348
+ end = nameEnd(input, start);
1349
+ }
1350
+ if (end < 0 || end - start > 5 || !isBlank(input.peek(end)) || !regexDeclarator.test(word(input, start, end)))
1351
+ return false;
1352
+ let name = blanksEnd(input, end), after = nameEnd(input, name);
1353
+ if (after < 0)
1354
+ return false;
1355
+ for (after = categoryEnd(input, name, after);; after++) {
1356
+ let next = input.peek(after);
1357
+ if (next == 123 /* Ch.BraceOpen */)
1358
+ return true;
1359
+ if (next < 0 || next == 10 /* Ch.Newline */)
1360
+ return false;
1361
+ }
1362
+ }
1363
+ // The characters that quantify, combine and anchor in a regex.
1364
+ function isRegexOperator(ch) {
1365
+ return ch == 43 /* Ch.Plus */ || ch == 42 /* Ch.Star */ || ch == 63 /* Ch.Question */ || ch == 124 /* Ch.Pipe */ || ch == 38 /* Ch.Amp */ || ch == 94 /* Ch.Caret */ ||
1366
+ ch == 126 /* Ch.Tilde */ || ch == 37 /* Ch.Percent */;
1367
+ }
1368
+ // The offset after the `<...>` at the current position in a regex.
1369
+ // When it is not closed on its line, this is the negated offset of the
1370
+ // end of the text that was gone over to find that out.
1371
+ function assertionEnd(input) {
1372
+ for (let pos = 1, depth = 1;;) {
1373
+ let ch = input.peek(pos);
1374
+ if (ch < 0 || ch == 10 /* Ch.Newline */)
1375
+ return -pos;
1376
+ if (ch == 92 /* Ch.Backslash */) {
1377
+ pos += input.peek(pos + 1) == 10 /* Ch.Newline */ || input.peek(pos + 1) < 0 ? 1 : 2;
1378
+ }
1379
+ else if (ch == 39 /* Ch.Apostrophe */ || ch == 34 /* Ch.DoubleQuote */) {
1380
+ let end = quotedEnd(input, pos);
1381
+ pos = end < 0 ? pos + 1 : end;
1382
+ }
1383
+ else if (ch == 91 /* Ch.BracketOpen */) {
1384
+ // The brackets of a character class can hold a `>` and a `}`,
1385
+ // and can span lines.
1386
+ let end = pos + 1;
1387
+ while (input.peek(end) != 93 /* Ch.BracketClose */ && input.peek(end) >= 0)
1388
+ end += input.peek(end) == 92 /* Ch.Backslash */ ? 2 : 1;
1389
+ pos = input.peek(end) == 93 /* Ch.BracketClose */ ? end + 1 : pos + 1;
1390
+ }
1391
+ else if (ch == 123 /* Ch.BraceOpen */) {
1392
+ // So can a block of code: <?{ $x > 1 }>
1393
+ let end = pos + 1;
1394
+ for (let braces = 1; braces > 0; end++) {
1395
+ let inner = input.peek(end);
1396
+ if (inner < 0 || inner == 10 /* Ch.Newline */)
1397
+ return -pos;
1398
+ if (inner == 123 /* Ch.BraceOpen */)
1399
+ braces++;
1400
+ else if (inner == 125 /* Ch.BraceClose */)
1401
+ braces--;
1402
+ }
1403
+ pos = end;
1404
+ }
1405
+ else if (ch == 60 /* Ch.Less */) {
1406
+ depth++;
1407
+ pos++;
1408
+ }
1409
+ else if (ch == 62 /* Ch.Greater */) {
1410
+ pos++;
1411
+ if (--depth == 0)
1412
+ return pos;
1413
+ }
1414
+ else {
1415
+ pos++;
1416
+ }
1417
+ }
1418
+ }
1419
+ // The pieces of the body of a token, rule or regex declaration. The
1420
+ // context tells whether the position is in such a body, and whether it
1421
+ // is in a block of code there, which is left to the other tokenizers.
1422
+ const regexToken = new lr.ExternalTokenizer((input, stack) => {
1423
+ let current = context(stack), { regex } = current, next = input.next;
1424
+ if (!regex || regex.braces > 0) {
1425
+ // At the start of the parse of the inside of a regex literal, the
1426
+ // opening delimiter: `/`, or a quote word with its adverbs: rx:i/
1427
+ if (current == startContext && stack.canShift(regexLitOpen)) {
1428
+ let opening = regexOpening(input);
1429
+ if (opening)
1430
+ input.acceptToken(regexLitOpen, opening.start);
1431
+ return;
1432
+ }
1433
+ // Outside of a regex, or in a block of code in one, where another
1434
+ // regex can be declared. An empty body, `{}`, has no regex.
1435
+ if (next >= 0 && next != 125 /* Ch.BraceClose */ && stack.canShift(regexStart))
1436
+ input.acceptToken(regexStart);
774
1437
  return;
775
- let end = regexEnd(input, 0, 123 /* Ch.BraceOpen */, 125 /* Ch.BraceClose */, 1);
776
- if (end < 0) {
777
- // Not closed: the rest of the input.
778
- skipToEnd(input);
779
- if (input.pos > stack.pos)
780
- input.acceptToken(regexBody);
781
1438
  }
782
- else if (end > 0) {
783
- input.acceptToken(regexBody, end);
1439
+ let { literalEnd } = regex;
1440
+ if (literalEnd >= 0) {
1441
+ // A literal ends at its closing delimiter. The rest of what is
1442
+ // parsed, which for s/a/b/ includes the replacement, is one token.
1443
+ if (next < 0 || input.pos >= literalEnd) {
1444
+ skipToEnd(input);
1445
+ return input.acceptToken(regexLitClose);
1446
+ }
1447
+ }
1448
+ else if (next < 0 || next == 125 /* Ch.BraceClose */ || next == 10 /* Ch.Newline */ && startsRegexDeclaration(input, 1)) {
1449
+ // The body ends at its closing brace. One that is not closed ends
1450
+ // before the next regex declaration, which a body does not hold, so
1451
+ // that the declarations after it are still found.
1452
+ return input.acceptToken(regexBodyEnd);
784
1453
  }
1454
+ // A block of code, and an escape, are tokens of the grammar. An
1455
+ // escape does not take a line break, where the check above is made.
1456
+ if (next == 123 /* Ch.BraceOpen */)
1457
+ return;
1458
+ if (next == 92 /* Ch.Backslash */ && input.peek(1) >= 0 && input.peek(1) != 10 /* Ch.Newline */)
1459
+ return;
1460
+ // How far a piece of a literal can go: up to its closing delimiter.
1461
+ // The pieces are read by other rules than the ones that found that
1462
+ // delimiter, and where the two disagree, the delimiter wins. A piece
1463
+ // that would go over it is text.
1464
+ let limit = literalEnd < 0 ? Infinity : literalEnd - input.pos;
1465
+ if (next == 35 /* Ch.Hash */) {
1466
+ // An embedded comment, #`( ... ), or one that runs to the end of the line.
1467
+ let end = embeddedCommentEnd(input, 0);
1468
+ if (end > 0)
1469
+ return input.acceptToken(regexBlockComment, end);
1470
+ skipLine(input);
1471
+ return input.acceptToken(regexComment);
1472
+ }
1473
+ if (next == 39 /* Ch.Apostrophe */ || next == 34 /* Ch.DoubleQuote */) {
1474
+ let end = quotedEnd(input, 0);
1475
+ if (end > 0 && end <= limit)
1476
+ return input.acceptToken(regexQuote, end);
1477
+ }
1478
+ else if (next == 60 /* Ch.Less */ || next == 62 /* Ch.Greater */ || next == 171 /* Ch.GuillemetOpen */ || next == 187 /* Ch.GuillemetClose */ ||
1479
+ next == 41 /* Ch.ParenClose */ && input.peek(1) == 62 /* Ch.Greater */) {
1480
+ // Word boundaries and capture markers stand alone: << >> « » <( )>
1481
+ let after = input.peek(1);
1482
+ if (next != 60 /* Ch.Less */ || after == 60 /* Ch.Less */ || after == 40 /* Ch.ParenOpen */) {
1483
+ let size = next == 171 /* Ch.GuillemetOpen */ || next == 187 /* Ch.GuillemetClose */ ? 1 : 2;
1484
+ if (next != 62 /* Ch.Greater */ || after == 62 /* Ch.Greater */)
1485
+ return input.acceptToken(regexOperator, size);
1486
+ }
1487
+ else {
1488
+ let end = assertionEnd(input);
1489
+ if (end > limit)
1490
+ return input.acceptToken(regexText, 1);
1491
+ if (end > 0) {
1492
+ // <[a..z]>, <-[a]>, <+alpha-[b]>, <?[c]>
1493
+ let kind = after == 63 /* Ch.Question */ || after == 33 /* Ch.Bang */ ? input.peek(2) : after;
1494
+ let isClass = kind == 91 /* Ch.BracketOpen */ || kind == 45 /* Ch.Hyphen */ || kind == 43 /* Ch.Plus */;
1495
+ return input.acceptToken(isClass ? CharacterClass : Assertion, end);
1496
+ }
1497
+ // What is not closed is text, up to the brace that closes the
1498
+ // body if there is one on the line.
1499
+ let stop = 1;
1500
+ while (stop < -end && input.peek(stop) != 125 /* Ch.BraceClose */)
1501
+ stop++;
1502
+ return input.acceptToken(regexText, stop);
1503
+ }
1504
+ }
1505
+ else if (next == 36 /* Ch.Dollar */ || next == 64 /* Ch.At */) {
1506
+ let after = input.peek(1);
1507
+ // A named capture: $<name>
1508
+ if (next == 36 /* Ch.Dollar */ && after == 60 /* Ch.Less */) {
1509
+ let end = nameEnd(input, 2);
1510
+ if (end > 0 && input.peek(end) == 62 /* Ch.Greater */)
1511
+ return input.acceptToken(regexCapture, end + 1);
1512
+ }
1513
+ // A variable is a token of the grammar: $x, $0, $/, @list, $!attr, $*dynamic
1514
+ // Not the `$` before the delimiter that closes a literal: /a$/
1515
+ if (limit != 1) {
1516
+ if (isIdentifierStart(after) || next == 36 /* Ch.Dollar */ && (isDigit(after) || after == 47 /* Ch.Slash */))
1517
+ return;
1518
+ if ((after == 33 /* Ch.Bang */ || after == 46 /* Ch.Dot */ || after == 42 /* Ch.Star */ || after == 63 /* Ch.Question */) &&
1519
+ isIdentifierStart(input.peek(2)))
1520
+ return;
1521
+ }
1522
+ // Otherwise `$` and `$$` anchor.
1523
+ if (next == 36 /* Ch.Dollar */) {
1524
+ while (input.next == 36 /* Ch.Dollar */ && input.pos != literalEnd)
1525
+ input.advance();
1526
+ return input.acceptToken(regexOperator);
1527
+ }
1528
+ }
1529
+ else if (isRegexOperator(next)) {
1530
+ while (isRegexOperator(input.next) && input.pos != literalEnd)
1531
+ input.advance();
1532
+ return input.acceptToken(regexOperator);
1533
+ }
1534
+ // Text, up to where one of the above may apply.
1535
+ for (;;) {
1536
+ input.advance();
1537
+ let ch = input.next;
1538
+ if (ch < 0 || input.pos == literalEnd ||
1539
+ ch == 10 /* Ch.Newline */ || ch == 92 /* Ch.Backslash */ || ch == 39 /* Ch.Apostrophe */ || ch == 34 /* Ch.DoubleQuote */ ||
1540
+ ch == 60 /* Ch.Less */ || ch == 62 /* Ch.Greater */ || ch == 171 /* Ch.GuillemetOpen */ || ch == 187 /* Ch.GuillemetClose */ ||
1541
+ ch == 41 /* Ch.ParenClose */ && input.peek(1) == 62 /* Ch.Greater */ ||
1542
+ ch == 123 /* Ch.BraceOpen */ || ch == 125 /* Ch.BraceClose */ || ch == 35 /* Ch.Hash */ || ch == 36 /* Ch.Dollar */ ||
1543
+ ch == 64 /* Ch.At */ || isRegexOperator(ch))
1544
+ break;
1545
+ }
1546
+ input.acceptToken(regexText);
785
1547
  });
786
1548
  // ---- Names ----
787
1549
  // The dot of a method call, with its optional `?`, `+`, `*`, `^` or `&`
@@ -796,26 +1558,37 @@ const methodDotToken = new lr.ExternalTokenizer((input, stack) => {
796
1558
  next = input.peek(++size);
797
1559
  if (!isIdentifierStart(next))
798
1560
  return;
799
- let { quote, mode } = context(stack);
800
- if (quote && stack.canShift(quoteEnd) &&
801
- (mode != 1 /* Mode.AfterTerm */ || input.peek(nameEnd(input, size)) != 40 /* Ch.ParenOpen */))
1561
+ if (context(stack).quote && inQuoteText(stack) &&
1562
+ (!afterQuotedVariable(stack) || input.peek(nameEnd(input, size)) != 40 /* Ch.ParenOpen */))
802
1563
  return;
803
1564
  input.acceptToken(methodDot, size);
804
1565
  });
805
- // The offset after an operator name's `:<...>` part, as in infix:<+>
806
- // and term:sym<x>, or `pos` when there is none there.
807
- function categoryEnd(input, pos) {
1566
+ // The offset after an operator name's `:<...>` part, as in infix:<+>,
1567
+ // circumfix:<[ ]>, infix:['+'] and term:sym<x>, or `pos` when there is
1568
+ // none there. The name before it runs from `nameStart` to `pos`.
1569
+ function categoryEnd(input, nameStart, pos) {
808
1570
  if (input.peek(pos) != 58 /* Ch.Colon */)
809
1571
  return pos;
810
1572
  let open = pos + 1;
811
1573
  if (word(input, open, open + 3) == "sym")
812
1574
  open += 3;
813
- let close = input.peek(open) == 60 /* Ch.Less */ ? 62 /* Ch.Greater */ : input.peek(open) == 171 /* Ch.GuillemetOpen */ ? 187 /* Ch.GuillemetClose */ : -1;
1575
+ let first = input.peek(open);
1576
+ if (first == 91 /* Ch.BracketOpen */) {
1577
+ // Only a quoted operator: ['+']
1578
+ let quote = input.peek(open + 1);
1579
+ if (quote != 39 /* Ch.Apostrophe */ && quote != 34 /* Ch.DoubleQuote */)
1580
+ return pos;
1581
+ let end = quotedEnd(input, open + 1);
1582
+ return end > 0 && input.peek(end) == 93 /* Ch.BracketClose */ ? end + 1 : pos;
1583
+ }
1584
+ let close = first == 60 /* Ch.Less */ ? 62 /* Ch.Greater */ : first == 171 /* Ch.GuillemetOpen */ ? 187 /* Ch.GuillemetClose */ : -1;
814
1585
  if (close < 0)
815
1586
  return pos;
1587
+ // The two halves of a circumfix are separated by one space.
1588
+ let spaces = /circumfix$/.test(word(input, nameStart, pos)) ? 1 : 0;
816
1589
  for (let i = open + 1;; i++) {
817
1590
  let ch = input.peek(i);
818
- if (ch < 0 || isSpace(ch))
1591
+ if (ch < 0 || isSpace(ch) && (ch != 32 /* Ch.Space */ || i == open + 1 || --spaces < 0))
819
1592
  return pos;
820
1593
  // The operator itself can be `>`, so the last closer before a break counts.
821
1594
  if (ch == close && !(input.peek(i + 1) == close))
@@ -836,7 +1609,7 @@ const nameToken = new lr.ExternalTokenizer((input, stack) => {
836
1609
  input.acceptToken(MethodName, end);
837
1610
  return;
838
1611
  }
839
- end = categoryEnd(input, end);
1612
+ end = categoryEnd(input, mark, end);
840
1613
  if (!mark && stack.canShift(declaredName))
841
1614
  input.acceptToken(declaredName, end);
842
1615
  else if (stack.canShift(declaredMethodName))
@@ -844,12 +1617,20 @@ const nameToken = new lr.ExternalTokenizer((input, stack) => {
844
1617
  });
845
1618
  // The type smiley in Int:D, Str:U and Any:_. It has to touch the type
846
1619
  // name, and must not be the start of a longer pair such as :Default.
1620
+ //
1621
+ // A type name without a smiley ends in an empty token. Without it, the
1622
+ // parser has to read the token after the name to know that the name is
1623
+ // complete, and when a long comment is in between, it records that as
1624
+ // a lookahead, which keeps the tokens before it from being reused.
847
1625
  const smileyToken = new lr.ExternalTokenizer((input, stack) => {
848
- if (input.next != 58 /* Ch.Colon */ || !stack.canShift(smiley) || !continuesName(input, -1))
1626
+ if (!stack.canShift(noSmiley))
849
1627
  return;
850
- let kind = input.peek(1);
851
- if ((kind == 68 /* Ch.D */ || kind == 85 /* Ch.U */ || kind == 95 /* Ch.Underscore */) && !continuesName(input, 2))
852
- input.acceptToken(smiley, 2);
1628
+ if (input.next == 58 /* Ch.Colon */) {
1629
+ let kind = input.peek(1);
1630
+ if ((kind == 68 /* Ch.D */ || kind == 85 /* Ch.U */ || kind == 95 /* Ch.Underscore */) && !continuesName(input, 2))
1631
+ return input.acceptToken(smiley, 2);
1632
+ }
1633
+ input.acceptToken(noSmiley, 0);
853
1634
  });
854
1635
  // v6, v6.d, v1.2.3, v1.2+, v1.*. A name such as v8-engine is not a version.
855
1636
  const versionToken = new lr.ExternalTokenizer((input, stack) => {
@@ -884,27 +1665,27 @@ const versionToken = new lr.ExternalTokenizer((input, stack) => {
884
1665
  });
885
1666
 
886
1667
  // This file was generated by lezer-generator. You probably shouldn't edit it.
887
- const spec_identifier = {__proto__:null,class:42, role:44, grammar:46, module:48, package:50, sub:64, method:68, submethod:70, token:76, rule:78, regex:80, enum:88, subset:94, constant:100, my:126, our:128, has:130, state:132, temp:134, let:136, anon:138, augment:140, supersede:142, unit:144, multi:146, proto:148, only:150, if:152, elsif:154, else:156, unless:158, with:160, orwith:162, without:164, for:166, while:168, until:170, loop:172, repeat:174, given:176, when:178, default:180, do:182, gather:184, take:186, try:188, return:190, next:192, last:194, redo:196, proceed:198, succeed:200, react:202, whenever:204, supply:206, emit:208, start:210, use:212, need:214, import:216, require:218, no:220, is:222, does:224, of:226, returns:228, handles:230, where:232, will:234, trusts:236, hides:238, and:240, or:242, not:244, xor:246, so:248, andthen:250, orelse:252, notandthen:254, div:256, mod:258, gcd:260, lcm:262, eq:264, ne:266, lt:268, gt:270, le:272, ge:274, cmp:276, leg:278, eqv:280, but:282, self:284};
888
- const spec_capIdentifier = {__proto__:null,True:286, False:288, Nil:290, BEGIN:292, CHECK:294, INIT:296, END:298, ENTER:300, LEAVE:302, KEEP:304, UNDO:306, FIRST:308, NEXT:310, LAST:312, PRE:314, POST:316, CATCH:318, CONTROL:320, QUIT:322, CLOSE:324, DOC:326};
1668
+ const spec_identifier = {__proto__:null,class:80, role:82, grammar:84, module:86, package:88, sub:102, method:106, submethod:108, token:116, rule:118, regex:120, enum:140, subset:146, constant:152, my:172, our:174, has:176, state:178, temp:180, let:182, anon:184, augment:186, supersede:188, unit:190, multi:192, proto:194, only:196, if:198, elsif:200, else:202, unless:204, with:206, orwith:208, without:210, for:212, while:214, until:216, loop:218, repeat:220, given:222, when:224, default:226, do:228, gather:230, take:232, try:234, return:236, next:238, last:240, redo:242, proceed:244, succeed:246, react:248, whenever:250, supply:252, emit:254, start:256, use:258, need:260, import:262, require:264, no:266, is:268, does:270, of:272, returns:274, handles:276, where:278, will:280, trusts:282, hides:284, and:286, or:288, not:290, xor:292, so:294, andthen:296, orelse:298, notandthen:300, div:302, mod:304, gcd:306, lcm:308, eq:310, ne:312, lt:314, gt:316, le:318, ge:320, cmp:322, leg:324, eqv:326, but:328, self:330};
1669
+ const spec_capIdentifier = {__proto__:null,True:332, False:334, Nil:336, BEGIN:338, CHECK:340, INIT:342, END:344, ENTER:346, LEAVE:348, KEEP:350, UNDO:352, FIRST:354, NEXT:356, LAST:358, PRE:360, POST:362, CATCH:364, CONTROL:366, QUIT:368, CLOSE:370, DOC:372};
889
1670
  const parser$1 = lr.LRParser.deserialize({
890
1671
  version: 14,
891
- states: "*hQfQDTOOO*kQDTO'#ClO*rQDTO'#CoO*yQDTO'#CyO+QQ7sO'#DbOOQ-w'#Df'#DfO+VQDuO'#DgOOQ-w'#Di'#DiO5fO!0bO'#DiOOQ-w'#G]'#G]OOQ-w'#G['#G[OOQ-w'#Fz'#FzQfQDTOOO>rQDeO'#CpO>yQDeO'#C{O?QQDeO'#C{OHZQDeO'#DROHeQDeO'#DXO!#yQDeO'#D[O!-_QDeO'#D_OOQ-w'#Gb'#GbOOQ-w,59W,59WO!6sQDTO,59WOOQ-w,59Z,59ZO!6zQDTO,59ZOOQ-w,59e,59eO!7RQDTO,59eOOQ-w,59|,59|OOQ-w,5:R,5:RO!7YQDTO'#DkOOO1x'#F|'#F|O!7aO!0bO,5:TOOQ-w,5:T,5:TOOQ-w-E9x-E9xOOQ-w'#Cv'#CvOOQ-w'#F{'#F{O!@jQDTO'#G_OOQ-w'#G_'#G_OOQ-w,59[,59[O5mQDTO,59[OOQ-w'#C}'#C}OOQ-w,59g,59gO5mQDTO,59gOOQ-w'#DQ'#DQO!@qQDTO,59mOOQ-w'#DV'#DVO!@xQ:XO'#DWOOQ-w,59m,59mO!@qQDTO,59mOOQ-w'#DZ'#DZOOQ-w,59s,59sOOQ-w'#D^'#D^OOQ-w,59v,59vOOQ-w'#Da'#DaOOQ-w,59y,59yOOQ-w1G.r1G.rOOQ-w1G.u1G.uOOQ-w1G/P1G/POOO1x,5:V,5:VO!AQQDTO,5:VOOO1x-E9z-E9zOOQ-w1G/o1G/oOOQ-w-E9y-E9yOOQ-w,5<y,5<yOOQ-w1G.v1G.vOOQ-w1G/R1G/ROOQ-w1G/X1G/XOOQ-w,59r,59rO!AXQ7cO,59rO!A^QDTO1G/XOOO1x1G/q1G/qOOQ-w1G/^1G/^OOQ-w7+$s7+$s",
892
- stateData: "!Aq~O$}OSYOSZOSPOSQOSROS~OSSOUXOWXOXXO]XO_PObQOe]Of]Og]Oh]Oi]OlROnYOp^Or_Os_Ov`Ow`Ox`O|aO!PbO!ScO!VXO!WXO!XXO![XO!`XO!adO!bdO!cdO!ddO!edO!fdO!gdO!hdO!idO!jdO!kdO!ldO!mdO!ndO!odO!pdO!qdO!rdO!sdO!tdO!udO!vdO!wdO!xdO!ydO!zdO!{dO!|dO!}dO#OdO#PdO#QdO#RdO#SdO#TdO#UdO#VdO#WdO#XdO#YdO#ZdO#[dO#]dO#^dO#_dO#`dO#adO#bdO#cdO#ddO#edO#fdO#gdO#hdO#idO#jdO#kdO#ldO#mdO#ndO#odO#pdO#qdO#rdO#sdO#tdO#udO#vdO#wdO#xdO#ydO#zdO#{dO#|dO#}dO$OdO$PdO$QdO$RdO$SdO$TdO$UdO$VdO$WdO$XdO$YdO$ZdO$[dO$]dO$^dO$_dO$`dO$adO$bdO$cdO$ddO$edO$fdO$gdO$hdO$idO$jXO$kXO$lXO$mXO$yVO$zWO${TO%QTO%SUO%TXO~O^eO~PfOagO~PfOkiO~PfOTkO~O$tlOS!ZXU!ZXW!ZXX!ZX]!ZX_!ZXb!ZXe!ZXf!ZXg!ZXh!ZXi!ZXl!ZXn!ZXp!ZXr!ZXs!ZXv!ZXw!ZXx!ZX|!ZX!P!ZX!S!ZX!V!ZX!W!ZX!X!ZX![!ZX!`!ZX!a!ZX!b!ZX!c!ZX!d!ZX!e!ZX!f!ZX!g!ZX!h!ZX!i!ZX!j!ZX!k!ZX!l!ZX!m!ZX!n!ZX!o!ZX!p!ZX!q!ZX!r!ZX!s!ZX!t!ZX!u!ZX!v!ZX!w!ZX!x!ZX!y!ZX!z!ZX!{!ZX!|!ZX!}!ZX#O!ZX#P!ZX#Q!ZX#R!ZX#S!ZX#T!ZX#U!ZX#V!ZX#W!ZX#X!ZX#Y!ZX#Z!ZX#[!ZX#]!ZX#^!ZX#_!ZX#`!ZX#a!ZX#b!ZX#c!ZX#d!ZX#e!ZX#f!ZX#g!ZX#h!ZX#i!ZX#j!ZX#k!ZX#l!ZX#m!ZX#n!ZX#o!ZX#p!ZX#q!ZX#r!ZX#s!ZX#t!ZX#u!ZX#v!ZX#w!ZX#x!ZX#y!ZX#z!ZX#{!ZX#|!ZX#}!ZX$O!ZX$P!ZX$Q!ZX$R!ZX$S!ZX$T!ZX$U!ZX$V!ZX$W!ZX$X!ZX$Y!ZX$Z!ZX$[!ZX$]!ZX$^!ZX$_!ZX$`!ZX$a!ZX$b!ZX$c!ZX$d!ZX$e!ZX$f!ZX$g!ZX$h!ZX$i!ZX$j!ZX$k!ZX$l!ZX$m!ZX$q!ZX$y!ZX$z!ZX${!ZX%Q!ZX%S!ZX%T!ZX^!ZXa!ZXk!ZX~OSSO_PObQOlmO!VnO!WnO!XnO!^nO$unO$vnO$wnO$ynO%TnO~O$xpO~P4kOSSOUXOWXOXXO]XO_PObQOe]Of]Og]Oh]Oi]OlROnuOp^Or_Os_Ov`Ow`Ox`O|aO!PbO!ScO!VXO!WXO!XXO![XO!`XO!adO!bdO!cdO!ddO!edO!fdO!gdO!hdO!idO!jdO!kdO!ldO!mdO!ndO!odO!pdO!qdO!rdO!sdO!tdO!udO!vdO!wdO!xdO!ydO!zdO!{dO!|dO!}dO#OdO#PdO#QdO#RdO#SdO#TdO#UdO#VdO#WdO#XdO#YdO#ZdO#[dO#]dO#^dO#_dO#`dO#adO#bdO#cdO#ddO#edO#fdO#gdO#hdO#idO#jdO#kdO#ldO#mdO#ndO#odO#pdO#qdO#rdO#sdO#tdO#udO#vdO#wdO#xdO#ydO#zdO#{dO#|dO#}dO$OdO$PdO$QdO$RdO$SdO$TdO$UdO$VdO$WdO$XdO$YdO$ZdO$[dO$]dO$^dO$_dO$`dO$adO$bdO$cdO$ddO$edO$fdO$gdO$hdO$idO$jXO$kXO$lXO$mXO$yVO$zWO${TO%QTO%SUO%TXO~O$rrO~P5mO$rxO~P5mO$s{O~P5mOSSOUXOWXOXXO]XO_PObQOe]Of]Og]Oh]Oi]Ol!OOp^Or_Os_Ov`Ow`Ox`O|aO!PbO!ScO!VXO!WXO!XXO![XO!`XO!adO!bdO!cdO!ddO!edO!fdO!gdO!hdO!idO!jdO!kdO!ldO!mdO!ndO!odO!pdO!qdO!rdO!sdO!tdO!udO!vdO!wdO!xdO!ydO!zdO!{dO!|dO!}dO#OdO#PdO#QdO#RdO#SdO#TdO#UdO#VdO#WdO#XdO#YdO#ZdO#[dO#]dO#^dO#_dO#`dO#adO#bdO#cdO#ddO#edO#fdO#gdO#hdO#idO#jdO#kdO#ldO#mdO#ndO#odO#pdO#qdO#rdO#sdO#tdO#udO#vdO#wdO#xdO#ydO#zdO#{dO#|dO#}dO$OdO$PdO$QdO$RdO$SdO$TdO$UdO$VdO$WdO$XdO$YdO$ZdO$[dO$]dO$^dO$_dO$`dO$adO$bdO$cdO$ddO$edO$fdO$gdO$hdO$idO$jXO$kXO$lXO$mXO$yVO$zWO${TO%QTO%SUO%TXO~On!PO$r}O~P?XO$r!ROS{XU{XW{XX{X]{X_{Xb{Xe{Xf{Xg{Xh{Xi{Xl{Xn{Xp{Xr{Xs{Xv{Xw{Xx{X|{X!P{X!S{X!V{X!W{X!X{X![{X!`{X!a{X!b{X!c{X!d{X!e{X!f{X!g{X!h{X!i{X!j{X!k{X!l{X!m{X!n{X!o{X!p{X!q{X!r{X!s{X!t{X!u{X!v{X!w{X!x{X!y{X!z{X!{{X!|{X!}{X#O{X#P{X#Q{X#R{X#S{X#T{X#U{X#V{X#W{X#X{X#Y{X#Z{X#[{X#]{X#^{X#_{X#`{X#a{X#b{X#c{X#d{X#e{X#f{X#g{X#h{X#i{X#j{X#k{X#l{X#m{X#n{X#o{X#p{X#q{X#r{X#s{X#t{X#u{X#v{X#w{X#x{X#y{X#z{X#{{X#|{X#}{X$O{X$P{X$Q{X$R{X$S{X$T{X$U{X$V{X$W{X$X{X$Y{X$Z{X$[{X$]{X$^{X$_{X$`{X$a{X$b{X$c{X$d{X$e{X$f{X$g{X$h{X$i{X$j{X$k{X$l{X$m{X$q{X$y{X$z{X${{X%Q{X%S{X%T{X^{Xa{Xk{X~O$r!TOS!OXU!OXW!OXX!OX]!OX_!OXb!OXe!OXf!OXg!OXh!OXi!OXl!OXn!OXp!OXr!OXs!OXv!OXw!OXx!OX|!OX!P!OX!S!OX!V!OX!W!OX!X!OX![!OX!`!OX!a!OX!b!OX!c!OX!d!OX!e!OX!f!OX!g!OX!h!OX!i!OX!j!OX!k!OX!l!OX!m!OX!n!OX!o!OX!p!OX!q!OX!r!OX!s!OX!t!OX!u!OX!v!OX!w!OX!x!OX!y!OX!z!OX!{!OX!|!OX!}!OX#O!OX#P!OX#Q!OX#R!OX#S!OX#T!OX#U!OX#V!OX#W!OX#X!OX#Y!OX#Z!OX#[!OX#]!OX#^!OX#_!OX#`!OX#a!OX#b!OX#c!OX#d!OX#e!OX#f!OX#g!OX#h!OX#i!OX#j!OX#k!OX#l!OX#m!OX#n!OX#o!OX#p!OX#q!OX#r!OX#s!OX#t!OX#u!OX#v!OX#w!OX#x!OX#y!OX#z!OX#{!OX#|!OX#}!OX$O!OX$P!OX$Q!OX$R!OX$S!OX$T!OX$U!OX$V!OX$W!OX$X!OX$Y!OX$Z!OX$[!OX$]!OX$^!OX$_!OX$`!OX$a!OX$b!OX$c!OX$d!OX$e!OX$f!OX$g!OX$h!OX$i!OX$j!OX$k!OX$l!OX$m!OX$q!OX$y!OX$z!OX${!OX%Q!OX%S!OX%T!OX^!OXa!OXk!OX~O$r!VOS!RXU!RXW!RXX!RX]!RX_!RXb!RXe!RXf!RXg!RXh!RXi!RXl!RXn!RXp!RXr!RXs!RXv!RXw!RXx!RX|!RX!P!RX!S!RX!V!RX!W!RX!X!RX![!RX!`!RX!a!RX!b!RX!c!RX!d!RX!e!RX!f!RX!g!RX!h!RX!i!RX!j!RX!k!RX!l!RX!m!RX!n!RX!o!RX!p!RX!q!RX!r!RX!s!RX!t!RX!u!RX!v!RX!w!RX!x!RX!y!RX!z!RX!{!RX!|!RX!}!RX#O!RX#P!RX#Q!RX#R!RX#S!RX#T!RX#U!RX#V!RX#W!RX#X!RX#Y!RX#Z!RX#[!RX#]!RX#^!RX#_!RX#`!RX#a!RX#b!RX#c!RX#d!RX#e!RX#f!RX#g!RX#h!RX#i!RX#j!RX#k!RX#l!RX#m!RX#n!RX#o!RX#p!RX#q!RX#r!RX#s!RX#t!RX#u!RX#v!RX#w!RX#x!RX#y!RX#z!RX#{!RX#|!RX#}!RX$O!RX$P!RX$Q!RX$R!RX$S!RX$T!RX$U!RX$V!RX$W!RX$X!RX$Y!RX$Z!RX$[!RX$]!RX$^!RX$_!RX$`!RX$a!RX$b!RX$c!RX$d!RX$e!RX$f!RX$g!RX$h!RX$i!RX$j!RX$k!RX$l!RX$m!RX$q!RX$y!RX$z!RX${!RX%Q!RX%S!RX%T!RX^!RXa!RXk!RX~O^!XO~PfOa!YO~PfOk!ZO~PfOk![O~PfO$x!_O~P4kOSSOUXOWXOXXO]XO_PObQOe]Of]Og]Oh]Oi]OlROp^Or_Os_Ov`Ow`Ox`O|aO!PbO!ScO!VXO!WXO!XXO![XO!`XO!adO!bdO!cdO!ddO!edO!fdO!gdO!hdO!idO!jdO!kdO!ldO!mdO!ndO!odO!pdO!qdO!rdO!sdO!tdO!udO!vdO!wdO!xdO!ydO!zdO!{dO!|dO!}dO#OdO#PdO#QdO#RdO#SdO#TdO#UdO#VdO#WdO#XdO#YdO#ZdO#[dO#]dO#^dO#_dO#`dO#adO#bdO#cdO#ddO#edO#fdO#gdO#hdO#idO#jdO#kdO#ldO#mdO#ndO#odO#pdO#qdO#rdO#sdO#tdO#udO#vdO#wdO#xdO#ydO#zdO#{dO#|dO#}dO$OdO$PdO$QdO$RdO$SdO$TdO$UdO$VdO$WdO$XdO$YdO$ZdO$[dO$]dO$^dO$_dO$`dO$adO$bdO$cdO$ddO$edO$fdO$gdO$hdO$idO$jXO$kXO$lXO$mXO$yVO$zWO${TO%QTO%SUO%TXO~On!aO~P!7hOn!dO~P?XOV!fOk!eO~Ok!hO~PfOk!iO~On!jO~P?XOZY!W!X!V]!`$j%T$l$m~",
893
- goto: ")d%VPPPPPPPPPPPPPPPP%WPP%W%rPPPPP&YPP&]P%rP&sPP&v%rPPP&y&|%rP'W%rP'Z%rP'^%WPPP%r%rP%rP%nPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP'a'w(ZPPPPPPPPPPPPP(a(mP)VPP%rxXOPQR[]^_`fhjmtwz|!Q!]!gTnWoyXOPQR[]^_`fhjmtwz|!Q!]!gRw]dYOPQR[fhjm!]Yu]^_wzR!atRz^Rz_R!Q`Q!P`S!d|!QR!j!gR!SaR!UbR!WcQ[OQfPQhQQjRYq[fhj!]R!]mYt]^_wzQ|`U!`t|!gR!g!QQoWR!^oeZOPQR[fhjm!]dYOPQR[fhjm!]es]^_`twz|!Q!gQv]Sy^_Q!bwR!cz",
894
- nodeNames: "⚠ BlockComment Pod Heredoc . MethodName Version Regex Regex Operator LineComment DocComment Program Number ) ( Parens ] [ Brackets PackageDeclaration class role grammar module package PackageName } { Block ; RoutineDeclaration sub RoutineName method submethod RoutineName RegexDeclaration token rule regex RegexName Block EnumDeclaration enum EnumName SubsetDeclaration subset SubsetName ConstantDeclaration constant ConstantName MethodCall VariableName AttributeName SpecialVariable Identifier TypeName Number StringLiteral Escape Interpolation PairKey my our has state temp let anon augment supersede unit multi proto only if elsif else unless with orwith without for while until loop repeat given when default do gather take try return next last redo proceed succeed react whenever supply emit start use need import require no is does of returns handles where will trusts hides and or not xor so andthen orelse notandthen div mod gcd lcm eq ne lt gt le ge cmp leg eqv but self True False Nil BEGIN CHECK INIT END ENTER LEAVE KEEP UNDO FIRST NEXT LAST PRE POST CATCH CONTROL QUIT CLOSE DOC Operator , : .",
895
- maxTerm: 190,
1672
+ states: "/`QfQDdOOO*zO#tOOO+POWOOO+UO,UOOO+uOSO'#C{O,PQDdO'#DPO,WQDdO'#DSO,_QDdO'#D^OOQ.W'#Df'#DfO,fQDdO'#D`O5zQ8WO'#D|OOQ.W'#D}'#D}O6PQ7gO'#EOOOQ.W'#EQ'#EQO6sO!%SO'#EQOOQ.W'#HU'#HUOOQ.W'#HT'#HTOOQ.W'#Gf'#GfQfQDdOOO6zQEUO'#DTO7RQEUO'#D`O7YQEUO'#D`O@rQEUO'#DgO@|QEUO'#DsOJqQEUO'#DvO!&fQEUO'#DyOOQ.W'#H^'#H^O!0ZO!!^OOO!1XO!%SOOOOOS'#Gg'#GgO!1`OSO,59gOOQ.W,59g,59gOOQ.W,59k,59kO!1jQDdO,59kOOQ.W,59n,59nO!1qQDdO,59nOOQ.W,59x,59xO!1xQDdO,59xOOQ.W'#Gh'#GhO!;bQDdO'#HWOOQ.W'#HW'#HWOOQ.W,59z,59zOOQ.W,5:h,5:hOOQ.W,5:j,5:jO!;iO!%SO'#ERO!;pQDdO'#ESOOO&j'#HZ'#HZO!;wO!0jO'#HZOOO&j'#Gj'#GjO!<uO!%SO,5:lOOQ.W,5:l,5:lOOQ.W-E:d-E:dOOQ.W'#DZ'#DZOOQ.W,59o,59oO,fQDdO,59oOOQ.W'#Db'#DbO,fQDdO,59zOOQ.W'#De'#DeO!<|QDdO,5:ROOQ.W'#Dk'#DkO!=TQ:[O'#DlOOQ.W,5:R,5:RO!<|QDdO,5:ROOQ.W'#Du'#DuOOQ.W,5:_,5:_OOQ.W'#Dx'#DxOOQ.W,5:b,5:bOOQ.W'#D{'#D{OOQ.W,5:e,5:eO!=]QDdO'#DrOOO#t'#HX'#HXOOO#t'#Gi'#GiO!0ZO!!^OOQOOOOOO!1XO!%SOOOOOS-E:e-E:eOOQ.W1G/R1G/ROOQ.W1G/V1G/VOOQ.W1G/Y1G/YOOQ.W1G/d1G/dOOQ.W-E:f-E:fOOQ.W,5=r,5=rOOO&j,5:m,5:mO!=dO!%SO,5:mOOO&j,5:n,5:nO!=kQDdO,5:nOOO2Q'#H]'#H]OOO2Q'#Gk'#GkO!=rO!0jO,5=uOOO&j-E:h-E:hOOQ.W1G0W1G0WOOQ.W1G/Z1G/ZOOQ.W1G/f1G/fOOQ.W1G/m1G/mO!?kO!!^O'#DmOOQ.W,5:W,5:WO!?rQ7fO,5:WO!?wQDdO1G/mOOO#t,5:^,5:^O!@OQDdO,5:^OOO#t-E:g-E:gOOO&j1G0X1G0XOOO&j1G0Y1G0YOOO2Q-E:i-E:iO!@VO!!^O,5:XOOQY,5:X,5:XOOQ.W1G/r1G/rOOQ.W7+%X7+%XOOO#t1G/x1G/xOOQY1G/s1G/s",
1673
+ stateData: "!@j~O%vOSlOSmOSPOSZOS[OS~OQSO]YO__Og_Oh_Oi_Oj_Ok_Op_OrTOuUOxcOycOzcO{cO|cO!PVO!R`O!TdO!VeO!WeO![fO!]fO!^fO!c_O!d_O!e_O!hgO!khO!niO!s_O!w_O!xjO!yjO!zjO!{jO!|jO!}jO#OjO#PjO#QjO#RjO#SjO#TjO#UjO#VjO#WjO#XjO#YjO#ZjO#[jO#]jO#^jO#_jO#`jO#ajO#bjO#cjO#djO#ejO#fjO#gjO#hjO#ijO#jjO#kjO#ljO#mjO#njO#ojO#pjO#qjO#rjO#sjO#tjO#ujO#vjO#wjO#xjO#yjO#zjO#{jO#|jO#}jO$OjO$PjO$QjO$RjO$SjO$TjO$UjO$VjO$WjO$XjO$YjO$ZjO$[jO$]jO$^jO$_jO$`jO$ajO$bjO$cjO$djO$ejO$fjO$gjO$hjO$ijO$jjO$kjO$ljO$mjO$njO$ojO$pjO$qjO$rjO$sjO$tjO$ujO$vjO$wjO$xjO$yjO$zjO${jO$|jO$}jO%OjO%PjO%QjO%R_O%S_O%T_O%U_O%q]O%r^O%sZO%tWO%yZO%|[O&O_O~O%jkO~O%elO~O%rlO~OSmOTmOUmOVmOWmOXmOYmO%dmO~ORoO%coO~P+ZOqpO~PfOtrO~PfO!OtO~PfOQSO]YO__Og_Oh_Oi_Oj_Ok_Op_OrTOuUOxcOycOzcO{cO|cO!PVO!RxO!TdO!VeO!WeO![fO!]fO!^fO!c_O!d_O!e_O!hgO!khO!niO!s_O!w_O!xjO!yjO!zjO!{jO!|jO!}jO#OjO#PjO#QjO#RjO#SjO#TjO#UjO#VjO#WjO#XjO#YjO#ZjO#[jO#]jO#^jO#_jO#`jO#ajO#bjO#cjO#djO#ejO#fjO#gjO#hjO#ijO#jjO#kjO#ljO#mjO#njO#ojO#pjO#qjO#rjO#sjO#tjO#ujO#vjO#wjO#xjO#yjO#zjO#{jO#|jO#}jO$OjO$PjO$QjO$RjO$SjO$TjO$UjO$VjO$WjO$XjO$YjO$ZjO$[jO$]jO$^jO$_jO$`jO$ajO$bjO$cjO$djO$ejO$fjO$gjO$hjO$ijO$jjO$kjO$ljO$mjO$njO$ojO$pjO$qjO$rjO$sjO$tjO$ujO$vjO$wjO$xjO$yjO$zjO${jO$|jO$}jO%OjO%PjO%QjO%R_O%S_O%T_O%U_O%q]O%r^O%sZO%tWO%yZO%|[O&O_O~O^zO~O%a{O%b{O~O!P}O!b!OO!c!PO!d!PO!e!PO%m!OO%n|O&O!PO~O%p!SO~P6XO%f!UO~P,fO%f!XO~P,fO%g!ZO~P,fOQSO]YO__Og_Oh_Oi_Oj_Ok_Op_OrTOuUOxcOycOzcO{cO|cO!P!^O!TdO!VeO!WeO![fO!]fO!^fO!c_O!d_O!e_O!hgO!khO!niO!s_O!w_O!xjO!yjO!zjO!{jO!|jO!}jO#OjO#PjO#QjO#RjO#SjO#TjO#UjO#VjO#WjO#XjO#YjO#ZjO#[jO#]jO#^jO#_jO#`jO#ajO#bjO#cjO#djO#ejO#fjO#gjO#hjO#ijO#jjO#kjO#ljO#mjO#njO#ojO#pjO#qjO#rjO#sjO#tjO#ujO#vjO#wjO#xjO#yjO#zjO#{jO#|jO#}jO$OjO$PjO$QjO$RjO$SjO$TjO$UjO$VjO$WjO$XjO$YjO$ZjO$[jO$]jO$^jO$_jO$`jO$ajO$bjO$cjO$djO$ejO$fjO$gjO$hjO$ijO$jjO$kjO$ljO$mjO$njO$ojO$pjO$qjO$rjO$sjO$tjO$ujO$vjO$wjO$xjO$yjO$zjO${jO$|jO$}jO%OjO%PjO%QjO%R_O%S_O%T_O%U_O%q]O%r^O%sZO%tWO%yZO%|[O&O_O~O!R!_O%f!]O~P7aO%f!aOQ!gX]!gX_!gXg!gXh!gXi!gXj!gXk!gXp!gXr!gXu!gXx!gXy!gXz!gX{!gX|!gX!P!gX!R!gX!T!gX!V!gX!W!gX![!gX!]!gX!^!gX!c!gX!d!gX!e!gX!h!gX!k!gX!n!gX!s!gX!w!gX!x!gX!y!gX!z!gX!{!gX!|!gX!}!gX#O!gX#P!gX#Q!gX#R!gX#S!gX#T!gX#U!gX#V!gX#W!gX#X!gX#Y!gX#Z!gX#[!gX#]!gX#^!gX#_!gX#`!gX#a!gX#b!gX#c!gX#d!gX#e!gX#f!gX#g!gX#h!gX#i!gX#j!gX#k!gX#l!gX#m!gX#n!gX#o!gX#p!gX#q!gX#r!gX#s!gX#t!gX#u!gX#v!gX#w!gX#x!gX#y!gX#z!gX#{!gX#|!gX#}!gX$O!gX$P!gX$Q!gX$R!gX$S!gX$T!gX$U!gX$V!gX$W!gX$X!gX$Y!gX$Z!gX$[!gX$]!gX$^!gX$_!gX$`!gX$a!gX$b!gX$c!gX$d!gX$e!gX$f!gX$g!gX$h!gX$i!gX$j!gX$k!gX$l!gX$m!gX$n!gX$o!gX$p!gX$q!gX$r!gX$s!gX$t!gX$u!gX$v!gX$w!gX$x!gX$y!gX$z!gX${!gX$|!gX$}!gX%O!gX%P!gX%Q!gX%R!gX%S!gX%T!gX%U!gX%`!gX%q!gX%r!gX%s!gX%t!gX%y!gX%|!gX&O!gXq!gXt!gX!O!gX~O%f!cOQ!jX]!jX_!jXg!jXh!jXi!jXj!jXk!jXp!jXr!jXu!jXx!jXy!jXz!jX{!jX|!jX!P!jX!R!jX!T!jX!V!jX!W!jX![!jX!]!jX!^!jX!c!jX!d!jX!e!jX!h!jX!k!jX!n!jX!s!jX!w!jX!x!jX!y!jX!z!jX!{!jX!|!jX!}!jX#O!jX#P!jX#Q!jX#R!jX#S!jX#T!jX#U!jX#V!jX#W!jX#X!jX#Y!jX#Z!jX#[!jX#]!jX#^!jX#_!jX#`!jX#a!jX#b!jX#c!jX#d!jX#e!jX#f!jX#g!jX#h!jX#i!jX#j!jX#k!jX#l!jX#m!jX#n!jX#o!jX#p!jX#q!jX#r!jX#s!jX#t!jX#u!jX#v!jX#w!jX#x!jX#y!jX#z!jX#{!jX#|!jX#}!jX$O!jX$P!jX$Q!jX$R!jX$S!jX$T!jX$U!jX$V!jX$W!jX$X!jX$Y!jX$Z!jX$[!jX$]!jX$^!jX$_!jX$`!jX$a!jX$b!jX$c!jX$d!jX$e!jX$f!jX$g!jX$h!jX$i!jX$j!jX$k!jX$l!jX$m!jX$n!jX$o!jX$p!jX$q!jX$r!jX$s!jX$t!jX$u!jX$v!jX$w!jX$x!jX$y!jX$z!jX${!jX$|!jX$}!jX%O!jX%P!jX%Q!jX%R!jX%S!jX%T!jX%U!jX%`!jX%q!jX%r!jX%s!jX%t!jX%y!jX%|!jX&O!jXq!jXt!jX!O!jX~O%f!eOQ!mX]!mX_!mXg!mXh!mXi!mXj!mXk!mXp!mXr!mXu!mXx!mXy!mXz!mX{!mX|!mX!P!mX!R!mX!T!mX!V!mX!W!mX![!mX!]!mX!^!mX!c!mX!d!mX!e!mX!h!mX!k!mX!n!mX!s!mX!w!mX!x!mX!y!mX!z!mX!{!mX!|!mX!}!mX#O!mX#P!mX#Q!mX#R!mX#S!mX#T!mX#U!mX#V!mX#W!mX#X!mX#Y!mX#Z!mX#[!mX#]!mX#^!mX#_!mX#`!mX#a!mX#b!mX#c!mX#d!mX#e!mX#f!mX#g!mX#h!mX#i!mX#j!mX#k!mX#l!mX#m!mX#n!mX#o!mX#p!mX#q!mX#r!mX#s!mX#t!mX#u!mX#v!mX#w!mX#x!mX#y!mX#z!mX#{!mX#|!mX#}!mX$O!mX$P!mX$Q!mX$R!mX$S!mX$T!mX$U!mX$V!mX$W!mX$X!mX$Y!mX$Z!mX$[!mX$]!mX$^!mX$_!mX$`!mX$a!mX$b!mX$c!mX$d!mX$e!mX$f!mX$g!mX$h!mX$i!mX$j!mX$k!mX$l!mX$m!mX$n!mX$o!mX$p!mX$q!mX$r!mX$s!mX$t!mX$u!mX$v!mX$w!mX$x!mX$y!mX$z!mX${!mX$|!mX$}!mX%O!mX%P!mX%Q!mX%R!mX%S!mX%T!mX%U!mX%`!mX%q!mX%r!mX%s!mX%t!mX%y!mX%|!mX&O!mXq!mXt!mX!O!mX~O`!hOa!hOb!hOc!hOd!hOe!hOf!hO!P!gO!b!hO!c!hO!d!hO!e!hO%k!kO%l!hO~O%p!kO~P6XOR!nO%c!nO~P+ZOq!oO~PfOt!pO~PfO!O!qO~PfOQSO]YO__Og_Oh_Oi_Oj_Ok_Op_OrTOuUOxcOycOzcO{cO|cO!PVO!TdO!VeO!WeO![fO!]fO!^fO!c_O!d_O!e_O!hgO!khO!niO!s_O!w_O!xjO!yjO!zjO!{jO!|jO!}jO#OjO#PjO#QjO#RjO#SjO#TjO#UjO#VjO#WjO#XjO#YjO#ZjO#[jO#]jO#^jO#_jO#`jO#ajO#bjO#cjO#djO#ejO#fjO#gjO#hjO#ijO#jjO#kjO#ljO#mjO#njO#ojO#pjO#qjO#rjO#sjO#tjO#ujO#vjO#wjO#xjO#yjO#zjO#{jO#|jO#}jO$OjO$PjO$QjO$RjO$SjO$TjO$UjO$VjO$WjO$XjO$YjO$ZjO$[jO$]jO$^jO$_jO$`jO$ajO$bjO$cjO$djO$ejO$fjO$gjO$hjO$ijO$jjO$kjO$ljO$mjO$njO$ojO$pjO$qjO$rjO$sjO$tjO$ujO$vjO$wjO$xjO$yjO$zjO${jO$|jO$}jO%OjO%PjO%QjO%R_O%S_O%T_O%U_O%q]O%r^O%sZO%tWO%yZO%|[O&O_O~O!R!sO~P!2PO%o!tO~P6XO!O!vO~PfO]YOrTOuUO%q!xO!P%}X!b%}X!c%}X!d%}X!e%}X%m%}X%n%}X%p%}X&O%}X%o%}X~O%p!|O~P6XO!R#PO~P7aO!O#RO%h#QO~O!O#UO~PfO%o#XO~P6XO!O#YO~PfO]YOrTOuUO%q!xO!P%}a!b%}a!c%}a!d%}a!e%}a%m%}a%n%}a%p%}a&O%}a%o%}a~O`!hOa!hOb!hOc!hOd!hOe!hOf!hO!P!gO!b!hO!c!hO!d!hO!e!hO%l!hO~O%i#]O~P!>pO!O#^O~O!R#_O~P7aO!O#`O~PfO%i#aO~P!>pOml!d!e!cp!w%R&O%T%U~",
1674
+ goto: ",r&RPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP&SPPP&mPP&m&SPPPPP'[PP'_P&SP'xPP'{(O&SPPP(i(l(vPPPP(y&SP)P&SP)S&SP)V&m&S&SP&S)Y)YPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP)b)|*S*g*q+PPPPPPPPPPPPPPPPPPPPPPPP+V+eP,Q,`P,fP,n&S!P_OTUVXbcdefqsuw}!W!Y![!`!g!w#T#V!O_OTUVXbcdefqsuw}!W!Y![!`!g!w#T#VT!x!P!zR!Wch`OTUVbqsu}!g!w#V[xXcde!W!YR!swR!YdR!Ye!PXOTUVXbcdefqsuw}!W!Y![!`!g!w#T#VR!`fQ!_fS#P![!`R#_#TR#S!^X!hk!j#Q#[R!bgR!dhR!fi]!O^l|!R!l!uQbOQqTQsUQuV[!Tbqsu!w#VQ!w}R#V!gQnSR!mn[wXcde!W!YQ![fU!rw![#TR#T!`Q!jkS#W!j#[R#[#QQ!R^Q!llQ!u|V!{!R!l!uQ!z!PR#Z!ziaOTUVbqsu}!g!w#Vh`OTUVbqsu}!g!w#VgvXcdefw!W!Y![!`#TUyXdeQ!VcQ!}!WR#O!YX!ik!j#Q#[]!Q^l|!R!l!uT!y!P!z",
1675
+ nodeNames: "⚠ BlockComment PodDirective PodDirective PodDirective PodHeading PodStrong PodEmphasis PodCode PodLink PodFormat Heredoc Heredoc . MethodName Version CharacterClass Assertion StringLiteral Operator LineComment BlockComment VariableName StringLiteral Regex Regex Operator VariableName LineComment DocComment Program Pod Number ) ( Parens ] [ Brackets PackageDeclaration class role grammar module package PackageName } { Block ; RoutineDeclaration sub RoutineName method submethod RoutineName RoutineName RegexDeclaration token rule regex RegexName Block Regex Escape VariableName AttributeName SpecialVariable Block EnumDeclaration enum EnumName SubsetDeclaration subset SubsetName ConstantDeclaration constant ConstantName MethodCall Identifier TypeName Number StringLiteral NestedDelimiters Interpolation PairKey my our has state temp let anon augment supersede unit multi proto only if elsif else unless with orwith without for while until loop repeat given when default do gather take try return next last redo proceed succeed react whenever supply emit start use need import require no is does of returns handles where will trusts hides and or not xor so andthen orelse notandthen div mod gcd lcm eq ne lt gt le ge cmp leg eqv but self True False Nil BEGIN CHECK INIT END ENTER LEAVE KEEP UNDO FIRST NEXT LAST PRE POST CATCH CONTROL QUIT CLOSE DOC Operator , : . Regex Heredoc StringLiteral",
1676
+ maxTerm: 232,
896
1677
  context: trackContext,
897
1678
  nodeProps: [
898
- ["openedBy", 14,"(",17,"[",27,"{"],
899
- ["closedBy", 15,")",18,"]",28,"}"]
1679
+ ["openedBy", 33,"(",36,"[",46,"{"],
1680
+ ["closedBy", 34,")",37,"]",47,"}"]
900
1681
  ],
901
- skippedNodes: [0,1,2,3,10,11],
902
- repeatNodeCount: 3,
903
- tokenData: "!$`~R!aX^%Wpq%Wqr%{st']tu(|uv5Rvw8axy:Wyz:]z{%{{|%{|}:b}!O%{!O!P:g!P!Q%{!Q!R;V!R![=f![!]@Z!]!^FQ!^!_%{!_!`%{!`!a%{!a!b%{!b!cFV!c!}Gy!}#OJu#O#PJz#P#Q! T#Q#R%{#R#S! Y#T#o! Y#o#p!$U#p#q%{#q#r!$Z#r#s%{#y#z%W$f$g%W$p$q! Y$q$r%{${$|! Y%Q%R! Y%R%S%{%W%o! Y%o%p%{%p&a! Y&a&b%{&b#BY! Y#BY#BZ%W#BZ$IS! Y$IS$I_%W$Iz$I{%{$I|$JO%W$JT$JU%W$KV$KW%W%&Y%+t%{&/x&Et! Y&FU&FV%W&Gh;'S! Y;'S;=`!#x?&r?Bj! Y?Hw?Ic! Y?Ii?JT! Y?J`?L{! Y~%]Y$}~X^%Wpq%W#y#z%W$f$g%W#BY#BZ%W$IS$I_%W$I|$JO%W$JT$JU%W$KV$KW%W&FU&FV%WP&Qc$jPqr%{z{%{{|%{}!O%{!P!Q%{!^!_%{!_!`%{!`!a%{!a!b%{#O#P%{#Q#R%{#p#q%{#r#s%{$q$r%{%R%S%{%o%p%{&a&b%{$Iz$I{%{%&Y%+t%{~'bWY~OY'zZ!_'z!_!`(c!`#p'z#p#q(c#q;'S'z;'S;=`(]<%lO'z~(PSY~OY'zZ;'S'z;'S;=`(]<%lO'z~(`P;=`<%l'z~(jSZ~Y~OY(cZ;'S(c;'S;=`(v<%lO(c~(yP;=`<%l(c~)Rk%T~qr*vz{.|!O!P-h!P!Q1x!Q![1}![!]2V!_!`.|!a!b.|!c!}3_#Q#R2V#R#S3_#T#o3_#r#s.|$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~*{a!V~!c!},Q#R#S,Q#T#o,Q$p$q,Q${$|,Q%Q%R,Q%W%o,Q%p&a,Q&b#BY,Q#BZ$IS,Q&/x&Et,Q&Gh;'S,Q;'S;=`.p?&r?Bj,Q?Hw?Ic,Q?Ii?JT,Q?J`?L{,Q~,Ve!W~wx-h}!O-h!Q![,Q![!].v!c!},Q#R#S,Q#T#o,Q$p$q,Q${$|,Q%Q%R,Q%W%o,Q%p&a,Q&b#BY,Q#BZ$IS,Q&/x&Et,Q&Gh;'S,Q;'S;=`.p?&r?Bj,Q?Hw?Ic,Q?Ii?JT,Q?J`?L{,Q~-ka!c!},Q#R#S,Q#T#o,Q$p$q,Q${$|,Q%Q%R,Q%W%o,Q%p&a,Q&b#BY,Q#BZ$IS,Q&/x&Et,Q&Gh;'S,Q;'S;=`.p?&r?Bj,Q?Hw?Ic,Q?Ii?JT,Q?J`?L{,Q~.sP;=`<%l,Q~.yP![!]-h~/Pa!c!}0U#R#S0U#T#o0U$p$q0U${$|0U%Q%R0U%W%o0U%p&a0U&b#BY0U#BZ$IS0U&/x&Et0U&Gh;'S0U;'S;=`1r?&r?Bj0U?Hw?Ic0U?Ii?JT0U?J`?L{0U~0Ze!X~wx.|}!O.|!Q![0U![!]1l!c!}0U#R#S0U#T#o0U$p$q0U${$|0U%Q%R0U%W%o0U%p&a0U&b#BY0U#BZ$IS0U&/x&Et0U&Gh;'S0U;'S;=`1r?&r?Bj0U?Hw?Ic0U?Ii?JT0U?J`?L{0U~1oP![!].|~1uP;=`<%l0U~1}O!V~~2SP!V~!Q![1}~2Ya!c!}3_#R#S3_#T#o3_$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~3de!V~wx2V}!O2V!Q![3_![!]4u!c!}3_#R#S3_#T#o3_$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~4xP![!]2V~5OP;=`<%l3_~5Wj$jPqr-huv6xz{.|!O!P-h![!]2V!_!`7V!a!b.|!c!}3_#Q#R2V#R#S3_#T#o3_#r#s.|$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_P6}P$jP!_!`7QP7VO$jP~7[a$jP!c!}0U#R#S0U#T#o0U$p$q0U${$|0U%Q%R0U%W%o0U%p&a0U&b#BY0U#BZ$IS0U&/x&Et0U&Gh;'S0U;'S;=`1r?&r?Bj0U?Hw?Ic0U?Ii?JT0U?J`?L{0U~8fj$jPqr-hvw6xz{.|!O!P-h![!]2V!_!`7V!a!b.|!c!}3_#Q#R2V#R#S3_#T#o3_#r#s.|$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~:]O_~~:bO^~~:gO$k~~:lQ$m~!O!P:r!_!`7QP:wQ$jP!O!P:}#Q#R7QP;SP$jP#Q#R7Q~;[^![~!O!P<W!Q![=f!d!e>T!f!g>o!g!h<r!q!r?T!z!{?i#R#S=}#U#V>T#W#X>o#X#Y<r#]#^=a#c#d?T#l#m?i~<ZP!Q![<^~<cT![~!Q![<^!g!h<r#R#S<W#X#Y<r#]#^=a~<uR{|=O}!O=O!Q![=U~=RP!Q![=U~=ZQ![~!Q![=U#]#^=a~=fO![~~=kU![~!O!P<W!Q![=f!g!h<r#R#S=}#X#Y<r#]#^=a~>QP!Q![=f~>WR!Q!R>a!R!S>a#R#S>a~>fR![~!Q!R>a!R!S>a#R#S>a~>rQ!Q![>x#R#S>x~>}Q![~!Q![>x#R#S>x~?WQ!Q!Y?^#R#S?^~?cQ![~!Q!Y?^#R#S?^~?lS!Q![?x!c!i?x#R#S?x#T#Z?x~?}S![~!Q![?x!c!i?x#R#S?x#T#Z?x~@`e$l~qrAq!Q![Dm![!]Et!_!`7Q!c!}By#R#SBy#T#oBy$p$qBy${$|By%Q%RBy%W%oBy%p&aBy&b#BYBy#BZ$ISBy&/x&EtBy&Gh;'SBy;'S;=`Dg?&r?BjBy?Hw?IcBy?Ii?JTBy?J`?L{By~Ata!c!}By#R#SBy#T#oBy$p$qBy${$|By%Q%RBy%W%oBy%p&aBy&b#BYBy#BZ$ISBy&/x&EtBy&Gh;'SBy;'S;=`Dg?&r?BjBy?Hw?IcBy?Ii?JTBy?J`?L{By~COe!`~wxAq}!OAq!Q![By![!]Da!c!}By#R#SBy#T#oBy$p$qBy${$|By%Q%RBy%W%oBy%p&aBy&b#BYBy#BZ$ISBy&/x&EtBy&Gh;'SBy;'S;=`Dg?&r?BjBy?Hw?IcBy?Ii?JTBy?J`?L{By~DdP![!]Aq~DjP;=`<%lBy~DpQ!Q![Dm!^!_Dv~DyT!O!PEY!Q![EY!c!}EY#R#SEY#T#oEY~E]U!O!PEY!Q![EY!`!aEo!c!}EY#R#SEY#T#oEY~EtO]~PEwP![!]EzPE}P!_!`7Q~FVOn~~F[i%T~qr-hz{.|!O!P-h![!]2V!_!`.|!a!b.|!c!}3_#Q#R2V#R#S3_#T#o3_#r#s.|$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~HOe%S~wxIa}!OIa!Q![Gy![!]Jo!c!}Gy#R#SGy#T#oGy$p$qGy${$|Gy%Q%RGy%W%oGy%p&aGy&b#BYGy#BZ$ISGy&/x&EtGy&Gh;'SGy;'S;=`Ji?&r?BjGy?Hw?IcGy?Ii?JTGy?J`?L{Gy~Ida!c!}Gy#R#SGy#T#oGy$p$qGy${$|Gy%Q%RGy%W%oGy%p&aGy&b#BYGy#BZ$ISGy&/x&EtGy&Gh;'SGy;'S;=`Ji?&r?BjGy?Hw?IcGy?Ii?JTGy?J`?L{Gy~JlP;=`<%lGy~JrP![!]Ia~JzOb~RKPu$jPOqMdqrMirzMdz{Mi{|Mi|}Md}!OMi!O!PMd!P!QMi!Q!^Md!^!_Mi!_!`Mi!`!aMi!a!bMi!b#OMd#O#PMi#P#QMd#Q#RMi#R#pMd#p#qMi#q#rMd#r#sMi#s$qMd$q$rMi$r%RMd%R%SMi%S%oMd%o%pMi%p&aMd&a&bMi&b$IzMd$Iz$I{Mi$I{%&YMd%&Y%+tMi%+t;'SMd;'S;=`N{;=`OMdQMiO!^QRMpc!^Q$jPqr%{z{%{{|%{}!O%{!P!Q%{!^!_%{!_!`%{!`!a%{!a!b%{#O#P%{#Q#R%{#p#q%{#r#s%{$q$r%{%R%S%{%o%p%{&a&b%{$Iz$I{%{%&Y%+t%{Q! QP!^Q;=`<%lMd~! YOa~~! _e%Q~wx!!p}!O!!p!Q![! Y![!]!$O!c!}! Y#R#S! Y#T#o! Y$p$q! Y${$|! Y%Q%R! Y%W%o! Y%p&a! Y&b#BY! Y#BZ$IS! Y&/x&Et! Y&Gh;'S! Y;'S;=`!#x?&r?Bj! Y?Hw?Ic! Y?Ii?JT! Y?J`?L{! Y~!!sa!c!}! Y#R#S! Y#T#o! Y$p$q! Y${$|! Y%Q%R! Y%W%o! Y%p&a! Y&b#BY! Y#BZ$IS! Y&/x&Et! Y&Gh;'S! Y;'S;=`!#x?&r?Bj! Y?Hw?Ic! Y?Ii?JT! Y?J`?L{! Y~!#{P;=`<%l! Y~!$RP![!]!!p~!$ZOl~~!$`Ok~",
904
- tokenizers: [blockComment, podToken, heredocToken, methodDotToken, nameToken, smileyToken, versionToken, regexBodyToken, quoteToken, termToken, 0, 1],
905
- topRules: {"Program":[0,12]},
906
- specialized: [{term: 186, get: (value) => spec_identifier[value] || -1},{term: 188, get: (value) => spec_capIdentifier[value] || -1}],
907
- tokenPrec: 3609
1682
+ skippedNodes: [0,1,11,12,28,29],
1683
+ repeatNodeCount: 6,
1684
+ tokenData: "!$`~R!aX^%Wpq%Wqr%{st']tu(|uv5Rvw8axy:Wyz:]z{%{{|%{|}:b}!O%{!O!P:g!P!Q%{!Q!R;V!R![=f![!]@Z!]!^FQ!^!_%{!_!`%{!`!a%{!a!b%{!b!cFV!c!}Gy!}#OJu#O#PJz#P#Q! T#Q#R%{#R#S! Y#T#o! Y#o#p!$U#p#q%{#q#r!$Z#r#s%{#y#z%W$f$g%W$p$q! Y$q$r%{${$|! Y%Q%R! Y%R%S%{%W%o! Y%o%p%{%p&a! Y&a&b%{&b#BY! Y#BY#BZ%W#BZ$IS! Y$IS$I_%W$Iz$I{%{$I|$JO%W$JT$JU%W$KV$KW%W%&Y%+t%{&/x&Et! Y&FU&FV%W&Gh;'S! Y;'S;=`!#x?&r?Bj! Y?Hw?Ic! Y?Ii?JT! Y?J`?L{! Y~%]Y%v~X^%Wpq%W#y#z%W$f$g%W#BY#BZ%W$IS$I_%W$I|$JO%W$JT$JU%W$KV$KW%W&FU&FV%WP&Qc%RPqr%{z{%{{|%{}!O%{!P!Q%{!^!_%{!_!`%{!`!a%{!a!b%{#O#P%{#Q#R%{#p#q%{#r#s%{$q$r%{%R%S%{%o%p%{&a&b%{$Iz$I{%{%&Y%+t%{~'bWl~OY'zZ!_'z!_!`(c!`#p'z#p#q(c#q;'S'z;'S;=`(]<%lO'z~(PSl~OY'zZ;'S'z;'S;=`(]<%lO'z~(`P;=`<%l'z~(jSm~l~OY(cZ;'S(c;'S;=`(v<%lO(c~(yP;=`<%l(c~)Rk&O~qr*vz{.|!O!P-h!P!Q1x!Q![1}![!]2V!_!`.|!a!b.|!c!}3_#Q#R2V#R#S3_#T#o3_#r#s.|$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~*{a!c~!c!},Q#R#S,Q#T#o,Q$p$q,Q${$|,Q%Q%R,Q%W%o,Q%p&a,Q&b#BY,Q#BZ$IS,Q&/x&Et,Q&Gh;'S,Q;'S;=`.p?&r?Bj,Q?Hw?Ic,Q?Ii?JT,Q?J`?L{,Q~,Ve!d~wx-h}!O-h!Q![,Q![!].v!c!},Q#R#S,Q#T#o,Q$p$q,Q${$|,Q%Q%R,Q%W%o,Q%p&a,Q&b#BY,Q#BZ$IS,Q&/x&Et,Q&Gh;'S,Q;'S;=`.p?&r?Bj,Q?Hw?Ic,Q?Ii?JT,Q?J`?L{,Q~-ka!c!},Q#R#S,Q#T#o,Q$p$q,Q${$|,Q%Q%R,Q%W%o,Q%p&a,Q&b#BY,Q#BZ$IS,Q&/x&Et,Q&Gh;'S,Q;'S;=`.p?&r?Bj,Q?Hw?Ic,Q?Ii?JT,Q?J`?L{,Q~.sP;=`<%l,Q~.yP![!]-h~/Pa!c!}0U#R#S0U#T#o0U$p$q0U${$|0U%Q%R0U%W%o0U%p&a0U&b#BY0U#BZ$IS0U&/x&Et0U&Gh;'S0U;'S;=`1r?&r?Bj0U?Hw?Ic0U?Ii?JT0U?J`?L{0U~0Ze!e~wx.|}!O.|!Q![0U![!]1l!c!}0U#R#S0U#T#o0U$p$q0U${$|0U%Q%R0U%W%o0U%p&a0U&b#BY0U#BZ$IS0U&/x&Et0U&Gh;'S0U;'S;=`1r?&r?Bj0U?Hw?Ic0U?Ii?JT0U?J`?L{0U~1oP![!].|~1uP;=`<%l0U~1}O!c~~2SP!c~!Q![1}~2Ya!c!}3_#R#S3_#T#o3_$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~3de!c~wx2V}!O2V!Q![3_![!]4u!c!}3_#R#S3_#T#o3_$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~4xP![!]2V~5OP;=`<%l3_~5Wj%RPqr-huv6xz{.|!O!P-h![!]2V!_!`7V!a!b.|!c!}3_#Q#R2V#R#S3_#T#o3_#r#s.|$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_P6}P%RP!_!`7QP7VO%RP~7[a%RP!c!}0U#R#S0U#T#o0U$p$q0U${$|0U%Q%R0U%W%o0U%p&a0U&b#BY0U#BZ$IS0U&/x&Et0U&Gh;'S0U;'S;=`1r?&r?Bj0U?Hw?Ic0U?Ii?JT0U?J`?L{0U~8fj%RPqr-hvw6xz{.|!O!P-h![!]2V!_!`7V!a!b.|!c!}3_#Q#R2V#R#S3_#T#o3_#r#s.|$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~:]Or~~:bOq~~:gO%S~~:lQ%U~!O!P:r!_!`7QP:wQ%RP!O!P:}#Q#R7QP;SP%RP#Q#R7Q~;[^!s~!O!P<W!Q![=f!d!e>T!f!g>o!g!h<r!q!r?T!z!{?i#R#S=}#U#V>T#W#X>o#X#Y<r#]#^=a#c#d?T#l#m?i~<ZP!Q![<^~<cT!s~!Q![<^!g!h<r#R#S<W#X#Y<r#]#^=a~<uR{|=O}!O=O!Q![=U~=RP!Q![=U~=ZQ!s~!Q![=U#]#^=a~=fO!s~~=kU!s~!O!P<W!Q![=f!g!h<r#R#S=}#X#Y<r#]#^=a~>QP!Q![=f~>WR!Q!R>a!R!S>a#R#S>a~>fR!s~!Q!R>a!R!S>a#R#S>a~>rQ!Q![>x#R#S>x~>}Q!s~!Q![>x#R#S>x~?WQ!Q!Y?^#R#S?^~?cQ!s~!Q!Y?^#R#S?^~?lS!Q![?x!c!i?x#R#S?x#T#Z?x~?}S!s~!Q![?x!c!i?x#R#S?x#T#Z?x~@`e%T~qrAq!Q![Dm![!]Et!_!`7Q!c!}By#R#SBy#T#oBy$p$qBy${$|By%Q%RBy%W%oBy%p&aBy&b#BYBy#BZ$ISBy&/x&EtBy&Gh;'SBy;'S;=`Dg?&r?BjBy?Hw?IcBy?Ii?JTBy?J`?L{By~Ata!c!}By#R#SBy#T#oBy$p$qBy${$|By%Q%RBy%W%oBy%p&aBy&b#BYBy#BZ$ISBy&/x&EtBy&Gh;'SBy;'S;=`Dg?&r?BjBy?Hw?IcBy?Ii?JTBy?J`?L{By~COe!w~wxAq}!OAq!Q![By![!]Da!c!}By#R#SBy#T#oBy$p$qBy${$|By%Q%RBy%W%oBy%p&aBy&b#BYBy#BZ$ISBy&/x&EtBy&Gh;'SBy;'S;=`Dg?&r?BjBy?Hw?IcBy?Ii?JTBy?J`?L{By~DdP![!]Aq~DjP;=`<%lBy~DpQ!Q![Dm!^!_Dv~DyT!O!PEY!Q![EY!c!}EY#R#SEY#T#oEY~E]U!O!PEY!Q![EY!`!aEo!c!}EY#R#SEY#T#oEY~EtOp~PEwP![!]EzPE}P!_!`7Q~FVO!R~~F[i&O~qr-hz{.|!O!P-h![!]2V!_!`.|!a!b.|!c!}3_#Q#R2V#R#S3_#T#o3_#r#s.|$p$q3_${$|3_%Q%R3_%W%o3_%p&a3_&b#BY3_#BZ$IS3_&/x&Et3_&Gh;'S3_;'S;=`4{?&r?Bj3_?Hw?Ic3_?Ii?JT3_?J`?L{3_~HOe%|~wxIa}!OIa!Q![Gy![!]Jo!c!}Gy#R#SGy#T#oGy$p$qGy${$|Gy%Q%RGy%W%oGy%p&aGy&b#BYGy#BZ$ISGy&/x&EtGy&Gh;'SGy;'S;=`Ji?&r?BjGy?Hw?IcGy?Ii?JTGy?J`?L{Gy~Ida!c!}Gy#R#SGy#T#oGy$p$qGy${$|Gy%Q%RGy%W%oGy%p&aGy&b#BYGy#BZ$ISGy&/x&EtGy&Gh;'SGy;'S;=`Ji?&r?BjGy?Hw?IcGy?Ii?JTGy?J`?L{Gy~JlP;=`<%lGy~JrP![!]Ia~JzOu~RKPu%RPOqMdqrMirzMdz{Mi{|Mi|}Md}!OMi!O!PMd!P!QMi!Q!^Md!^!_Mi!_!`Mi!`!aMi!a!bMi!b#OMd#O#PMi#P#QMd#Q#RMi#R#pMd#p#qMi#q#rMd#r#sMi#s$qMd$q$rMi$r%RMd%R%SMi%S%oMd%o%pMi%p&aMd&a&bMi&b$IzMd$Iz$I{Mi$I{%&YMd%&Y%+tMi%+t;'SMd;'S;=`N{;=`OMdQMiO!bQRMpc!bQ%RPqr%{z{%{{|%{}!O%{!P!Q%{!^!_%{!_!`%{!`!a%{!a!b%{#O#P%{#Q#R%{#p#q%{#r#s%{$q$r%{%R%S%{%o%p%{&a&b%{$Iz$I{%{%&Y%+t%{Q! QP!bQ;=`<%lMd~! YOt~~! _e%y~wx!!p}!O!!p!Q![! Y![!]!$O!c!}! Y#R#S! Y#T#o! Y$p$q! Y${$|! Y%Q%R! Y%W%o! Y%p&a! Y&b#BY! Y#BZ$IS! Y&/x&Et! Y&Gh;'S! Y;'S;=`!#x?&r?Bj! Y?Hw?Ic! Y?Ii?JT! Y?J`?L{! Y~!!sa!c!}! Y#R#S! Y#T#o! Y$p$q! Y${$|! Y%Q%R! Y%W%o! Y%p&a! Y&b#BY! Y#BZ$IS! Y&/x&Et! Y&Gh;'S! Y;'S;=`!#x?&r?Bj! Y?Hw?Ic! Y?Ii?JT! Y?J`?L{! Y~!#{P;=`<%l! Y~!$RP![!]!!p~!$ZO!P~~!$`O!O~",
1685
+ tokenizers: [smileyToken, blockComment, podToken, heredocToken, methodDotToken, nameToken, versionToken, regexToken, quoteToken, termToken, 0, 1],
1686
+ topRules: {"Program":[0,30],"Regex":[1,191],"Heredoc":[2,192],"StringLiteral":[3,193]},
1687
+ specialized: [{term: 225, get: (value) => spec_identifier[value] || -1},{term: 228, get: (value) => spec_capIdentifier[value] || -1}],
1688
+ tokenPrec: 3556
908
1689
  });
909
1690
 
910
1691
  // The keywords of the grammar, with the tag each is highlighted with.
@@ -923,6 +1704,279 @@ const keywordTags = {
923
1704
  self: highlight.tags.self
924
1705
  };
925
1706
 
1707
+ const scopes = new common.NodeWeakMap();
1708
+ const scopeNodes = /^(Block|Interpolation|Program)$/;
1709
+ const variableNodes = /^(VariableName|AttributeName)$/;
1710
+ const declarators = /^(my|our|has|state)$/;
1711
+ /// Whether a variable is a placeholder, which is declared by being
1712
+ /// used: $^a, and the named $:a
1713
+ function isPlaceholder(name) { return /^[$@%&][\^:]/.test(name); }
1714
+ // The name of the variable without a sigil that the backslash `node`
1715
+ // comes right before, as in `\x`. A capitalized one reads as a type.
1716
+ function sigillessName(state, node) {
1717
+ let next = node.nextSibling;
1718
+ return node.name == "Operator" && next && (next.name == "Identifier" || next.name == "TypeName") &&
1719
+ next.from == node.to && state.sliceDoc(node.from, node.to) == "\\" ? next : null;
1720
+ }
1721
+ // Whether a block is a subscript, as in %h{$key}: it touches the term
1722
+ // before it. It is not a scope for placeholder variables.
1723
+ function isSubscript(block) {
1724
+ let prev = block.prevSibling;
1725
+ return block.name == "Block" && prev != null && prev.to == block.from && /^[A-Z]/.test(prev.name) && prev.name != "Operator";
1726
+ }
1727
+ /// Whether a completion is for a name with a sigil.
1728
+ function hasSigil(completion) { return /^[$@%&]/.test(completion.label); }
1729
+ function variable(label) {
1730
+ return { label, type: label[0] == "&" ? "function" : /^.[!.]/.test(label) ? "property" : "variable", boost: 1 };
1731
+ }
1732
+ function isArrow(text) { return text == "->" || text == "<->"; }
1733
+ class Gatherer {
1734
+ constructor(state) {
1735
+ this.state = state;
1736
+ this.names = [];
1737
+ this.methods = [];
1738
+ }
1739
+ text(node) { return this.state.sliceDoc(node.from, node.to); }
1740
+ declare(node) {
1741
+ let label = this.text(node);
1742
+ // A name without a sigil: my \x
1743
+ if (!variableNodes.test(node.name))
1744
+ return this.names.push({ label, type: "variable", boost: 1 });
1745
+ this.names.push(variable(label));
1746
+ // `has $.x` also makes the private `$!x`, and `$^a` can be written `$a`.
1747
+ if (label[1] == ".")
1748
+ this.names.push(variable(label[0] + "!" + label.slice(2)));
1749
+ else if (isPlaceholder(label))
1750
+ this.names.push(variable(label[0] + label.slice(2)));
1751
+ }
1752
+ // Declares the name after `node`, when `node` is the backslash of a
1753
+ // variable without a sigil: my \x = 1, sub f(\x) { }
1754
+ declareSigilless(node) {
1755
+ let name = sigillessName(this.state, node);
1756
+ if (name)
1757
+ this.declare(name);
1758
+ return name != null;
1759
+ }
1760
+ // The variables of a signature, or of the parentheses of `my (...)`.
1761
+ // What follows a `=` or a `where`, up to the next comma, is a default
1762
+ // value or a constraint, and uses variables rather than declaring them.
1763
+ declareAll(node) {
1764
+ let uses = false;
1765
+ for (let child = node.firstChild; child; child = child.nextSibling) {
1766
+ let { name } = child;
1767
+ if (name == ",")
1768
+ uses = false;
1769
+ else if (name == "where" || name == "Operator" && this.text(child) == "=")
1770
+ uses = true;
1771
+ else if (uses)
1772
+ continue;
1773
+ else if (this.declareSigilless(child))
1774
+ continue;
1775
+ else if (variableNodes.test(name))
1776
+ this.declare(child);
1777
+ else if (name == "Parens" || name == "Brackets")
1778
+ this.declareAll(child);
1779
+ }
1780
+ }
1781
+ addMethods(scope) {
1782
+ for (let method of scopeOf(scope, this.state).methods)
1783
+ this.methods.push(method);
1784
+ }
1785
+ // Goes over the content of a node that is not a scope of its own.
1786
+ content(node) {
1787
+ // Whether a declarator was seen, and what it declares is yet to come.
1788
+ let declaring = false;
1789
+ for (let child = node.firstChild; child; child = child.nextSibling) {
1790
+ let { name } = child;
1791
+ if (declarators.test(name)) {
1792
+ declaring = true;
1793
+ continue;
1794
+ }
1795
+ // my Int $x, my Array[Int] @y
1796
+ if (declaring && (name == "TypeName" || name == "Brackets"))
1797
+ continue;
1798
+ // A placeholder variable declares itself where it is used: { $^a + $^b }
1799
+ if (name == "VariableName" && isPlaceholder(this.text(child)))
1800
+ this.declare(child);
1801
+ else if (declaring && this.declareSigilless(child))
1802
+ continue;
1803
+ else if (declaring && variableNodes.test(name))
1804
+ this.declare(child);
1805
+ else if (declaring && name == "Parens")
1806
+ this.declareAll(child);
1807
+ else if (scopeNodes.test(name)) {
1808
+ // The placeholders in a subscript are those of the block around it.
1809
+ if (isSubscript(child))
1810
+ this.placeholders(child);
1811
+ this.addMethods(child);
1812
+ }
1813
+ else if (/Declaration$/.test(name))
1814
+ this.declaration(child);
1815
+ else if (child.firstChild)
1816
+ this.content(child);
1817
+ // The name of a constant with a sigil is a variable after the
1818
+ // declaration node: constant $LIMIT = 3
1819
+ declaring = name == "ConstantDeclaration" && child.lastChild.name == "constant";
1820
+ }
1821
+ }
1822
+ // The placeholder variables in a node, and in the subscripts in it.
1823
+ placeholders(node) {
1824
+ for (let child = node.firstChild; child; child = child.nextSibling) {
1825
+ if (child.name == "VariableName") {
1826
+ if (isPlaceholder(this.text(child)))
1827
+ this.declare(child);
1828
+ }
1829
+ else if (child.firstChild && (!scopeNodes.test(child.name) || isSubscript(child)))
1830
+ this.placeholders(child);
1831
+ }
1832
+ }
1833
+ declaration(node) {
1834
+ let kind = node.firstChild.name;
1835
+ for (let child = node.firstChild; child; child = child.nextSibling) {
1836
+ let { name } = child;
1837
+ if (name == "RoutineName") {
1838
+ let label = this.text(child);
1839
+ // An operator is not called by its name: infix:<+>
1840
+ if (/:[<«\[]/.test(label))
1841
+ continue;
1842
+ // `multi foo` has no declarator, and declares a sub.
1843
+ if (kind == "sub" || kind == "RoutineName")
1844
+ this.names.push({ label, type: "function", boost: 1 }, variable("&" + label));
1845
+ // A private method is called as self!name, where nothing is completed.
1846
+ else if (/^[^!^]/.test(label))
1847
+ this.methods.push({ label, type: "method", boost: 1 });
1848
+ }
1849
+ else if (name == "PackageName") {
1850
+ this.names.push({ label: this.text(child), type: "class", boost: 1 });
1851
+ }
1852
+ else if (name == "EnumName" || name == "SubsetName") {
1853
+ this.names.push({ label: this.text(child), type: "type", boost: 1 });
1854
+ }
1855
+ else if (name == "ConstantName") {
1856
+ let label = this.text(child);
1857
+ this.names.push(/^[$@%&]/.test(label) ? variable(label) : { label, type: "constant", boost: 1 });
1858
+ }
1859
+ else if (scopeNodes.test(name)) {
1860
+ this.addMethods(child);
1861
+ }
1862
+ else if (name != "Parens" && child.firstChild) {
1863
+ // The signature belongs to the scope of the body.
1864
+ this.content(child);
1865
+ }
1866
+ }
1867
+ }
1868
+ // The parameters of a block: those of the routine that it is the body
1869
+ // of, or the ones after the arrow of a pointy block.
1870
+ parameters(block) {
1871
+ let parent = block.parent;
1872
+ if (parent && parent.name == "RoutineDeclaration") {
1873
+ for (let child = parent.firstChild; child; child = child.nextSibling)
1874
+ if (child.name == "Parens")
1875
+ this.declareAll(child);
1876
+ return;
1877
+ }
1878
+ // Going backwards, the variables of a parameter come before the
1879
+ // `=` of its default value.
1880
+ let found = [], parameter = [];
1881
+ for (let prev = block.prevSibling; prev; prev = prev.prevSibling) {
1882
+ let { name } = prev;
1883
+ if (name == ";" || name == "Block" || /Declaration$/.test(name))
1884
+ return;
1885
+ if (name == "VariableName") {
1886
+ parameter.push(prev);
1887
+ }
1888
+ else if (prev.prevSibling && sigillessName(this.state, prev.prevSibling)) {
1889
+ // -> \item { }
1890
+ parameter.push(prev);
1891
+ }
1892
+ else if (name == ",") {
1893
+ found = found.concat(parameter);
1894
+ parameter = [];
1895
+ }
1896
+ else if (name == "Operator") {
1897
+ let text = this.text(prev);
1898
+ if (text == "=")
1899
+ parameter = [];
1900
+ else if (isArrow(text)) {
1901
+ for (let node of found.concat(parameter))
1902
+ this.declare(node);
1903
+ return;
1904
+ }
1905
+ }
1906
+ }
1907
+ }
1908
+ }
1909
+ // The names that the scope node `node` declares in its content. The
1910
+ // parameters of a block are not part of its node, and are left out.
1911
+ function scopeOf(node, state) {
1912
+ let scope = scopes.get(node);
1913
+ if (scope)
1914
+ return scope;
1915
+ let gatherer = new Gatherer(state);
1916
+ gatherer.content(node);
1917
+ scope = { names: gatherer.names, methods: gatherer.methods };
1918
+ scopes.set(node, scope);
1919
+ return scope;
1920
+ }
1921
+ /// The names that are in scope at `node`, innermost scope first, and
1922
+ /// the methods of the whole document.
1923
+ function localNames(node, state) {
1924
+ let names = [], methods = [];
1925
+ for (let cur = node; cur; cur = cur.parent) {
1926
+ if (!scopeNodes.test(cur.name))
1927
+ continue;
1928
+ let scope = scopeOf(cur, state);
1929
+ // An edit to the parameters does not replace the block's node, so
1930
+ // these are read every time.
1931
+ let parameters = new Gatherer(state);
1932
+ parameters.parameters(cur);
1933
+ for (let name of parameters.names)
1934
+ names.push(name);
1935
+ for (let name of scope.names)
1936
+ names.push(name);
1937
+ methods = scope.methods;
1938
+ }
1939
+ return { names, methods };
1940
+ }
1941
+ /// Whether a variable is in a place where variables are declared, so
1942
+ /// that its name is a new one. The variable is a child of `parent`, and
1943
+ /// comes after the sibling `prev`.
1944
+ function declaresVariable(state, parent, prev) {
1945
+ let text = (node) => state.sliceDoc(node.from, node.to);
1946
+ // A signature, or the list of `my ($a, $b)`, unless the variable is
1947
+ // in a default value or in a constraint.
1948
+ if (parent.name == "Parens") {
1949
+ let before = parent.prevSibling;
1950
+ if (parent.parent && parent.parent.name == "RoutineDeclaration" || before && declarators.test(before.name)) {
1951
+ for (let node = prev; node && node.name != ","; node = node.prevSibling)
1952
+ if (node.name == "where" || node.name == "Operator" && text(node) == "=")
1953
+ return false;
1954
+ return true;
1955
+ }
1956
+ }
1957
+ // After a declarator, or after the arrow of a pointy block.
1958
+ let first = true;
1959
+ for (let node = prev; node; node = node.prevSibling) {
1960
+ let { name } = node;
1961
+ // `my $a, $b` declares the first one only.
1962
+ if (declarators.test(name))
1963
+ return first;
1964
+ if (name == ";" || name == "Block" || name == "{" || /Declaration$/.test(name))
1965
+ return false;
1966
+ if (name == "Operator") {
1967
+ let operator = text(node);
1968
+ if (isArrow(operator))
1969
+ return true;
1970
+ if (operator == "=")
1971
+ return false;
1972
+ }
1973
+ // Only a type can stand between a declarator and its variable.
1974
+ if (name != "TypeName" && name != "Brackets")
1975
+ first = false;
1976
+ }
1977
+ return false;
1978
+ }
1979
+
926
1980
  function completions(type, ...labels) {
927
1981
  return labels.join(" ").split(" ").map(label => ({ label, type }));
928
1982
  }
@@ -939,6 +1993,7 @@ const globals = [...keywords, ...constants, ...types, ...routines];
939
1993
  // `:key` is not a name from the list.
940
1994
  const nameTail = /^(\w[\w:'\-]*)?$/;
941
1995
  const specialVariableTail = /^[$@%&][*?][\w\-]*$/;
1996
+ const variableTail = /^[$@%&][!.^:]?[\w'\-]*$/;
942
1997
  // Whether the position of `node` holds code, as opposed to the text of
943
1998
  // a string, a regex, a comment or Pod. An interpolation is code again.
944
1999
  function inCode(node) {
@@ -965,8 +2020,26 @@ function isPrivateMethodName(tree, state, node) {
965
2020
  return invocant.to == mark.from &&
966
2021
  /^(self|VariableName|AttributeName|SpecialVariable|Identifier|TypeName|Parens|Brackets|[)\]])$/.test(invocant.name);
967
2022
  }
968
- /// A completion source for Raku keywords and for the commonly used
969
- /// built-in types, routines, methods and special variables.
2023
+ // The names declared in the document, followed by the built-in ones
2024
+ // that they do not hide.
2025
+ function withLocal(local, builtin) {
2026
+ if (!local.length)
2027
+ return builtin;
2028
+ let labels = new Set(), result = [];
2029
+ for (let completion of local) {
2030
+ if (labels.has(completion.label))
2031
+ continue;
2032
+ labels.add(completion.label);
2033
+ result.push(completion);
2034
+ }
2035
+ for (let completion of builtin)
2036
+ if (!labels.has(completion.label))
2037
+ result.push(completion);
2038
+ return result;
2039
+ }
2040
+ /// A completion source for Raku keywords, for the commonly used
2041
+ /// built-in types, routines, methods and special variables, and for the
2042
+ /// names that the document declares.
970
2043
  const rakuCompletionSource = context => {
971
2044
  let { state, pos } = context, tree = language.syntaxTree(state), node = tree.resolveInner(pos, -1);
972
2045
  if (!inCode(node))
@@ -975,46 +2048,337 @@ const rakuCompletionSource = context => {
975
2048
  let special = node.name == "SpecialVariable" ? node : context.matchBefore(/[$@%&][*?]$/);
976
2049
  if (special)
977
2050
  return { from: special.from, options: specialVariables, validFor: specialVariableTail };
2051
+ // A variable, or a sigil that is about to be one. A lone `%` or `&`
2052
+ // is as often an operator, and has to be followed by a name first.
2053
+ let isVariableNode = node.name == "VariableName" || node.name == "AttributeName";
2054
+ let sigil = isVariableNode ? null : context.matchBefore(/[$@]$|[$@%&][!.^:]$/);
2055
+ if (isVariableNode || sigil) {
2056
+ let from = isVariableNode ? node.from : sigil.from;
2057
+ // A lone sigil is not a node. It lies in the node around it.
2058
+ let parent = isVariableNode ? node.parent : tree.resolveInner(from, 0);
2059
+ let prev = isVariableNode ? node.prevSibling : parent.childBefore(from);
2060
+ if (declaresVariable(state, parent, prev))
2061
+ return null;
2062
+ // A placeholder variable is declared by being used, also by the
2063
+ // one that is being typed, which is not offered for itself.
2064
+ let typed = isVariableNode ? state.sliceDoc(from, node.to) : "";
2065
+ let options = withLocal(localNames(parent, state).names.filter(hasSigil), []);
2066
+ if (isPlaceholder(typed))
2067
+ options = options.filter(completion => completion.label != typed);
2068
+ return options.length ? { from, options, validFor: variableTail } : null;
2069
+ }
2070
+ let local = localNames(node, state), localMethods = () => withLocal(local.methods, methods);
978
2071
  if (node.name == "MethodName")
979
- return { from: node.from, options: methods, validFor: nameTail };
2072
+ return { from: node.from, options: localMethods(), validFor: nameTail };
980
2073
  // A dot or `.=` with no name after it yet. Two dots are a range operator.
981
2074
  let before = state.sliceDoc(Math.max(0, pos - 2), pos);
982
2075
  if (before == ".=")
983
- return { from: pos, options: methods, validFor: nameTail };
2076
+ return { from: pos, options: localMethods(), validFor: nameTail };
984
2077
  if (/(^|[^.])\.$/.test(before)) {
985
2078
  // The dot of `1.5` and of `v6.d` is far more often typed than a
986
2079
  // method call on a number.
987
2080
  let numeric = /^(Number|Version)$/.test(tree.resolveInner(pos - 1, -1).name);
988
- return numeric && !context.explicit ? null : { from: pos, options: methods, validFor: nameTail };
2081
+ return numeric && !context.explicit ? null : { from: pos, options: localMethods(), validFor: nameTail };
989
2082
  }
2083
+ let localGlobals = () => withLocal(local.names.filter(completion => !hasSigil(completion)), globals);
990
2084
  // A keyword's node is named after the keyword, as are those of `,` and `;`.
991
2085
  let isKeyword = /^\w/.test(node.name) && node.name == state.sliceDoc(node.from, node.to);
992
2086
  if (isKeyword || node.name == "Identifier" || node.name == "TypeName") {
993
2087
  if (isPrivateMethodName(tree, state, node))
994
2088
  return null;
995
- // `$x .= trim` calls a method.
996
2089
  let prev = node.prevSibling;
2090
+ // The name of a variable without a sigil, where it is declared: my \x
2091
+ if (prev && prev.to == node.from && state.sliceDoc(prev.from, prev.to) == "\\" &&
2092
+ declaresVariable(state, node.parent, prev.prevSibling))
2093
+ return null;
2094
+ // `$x .= trim` calls a method.
997
2095
  let afterDotAssign = prev != null && prev.name == "Operator" && state.sliceDoc(prev.from, prev.to) == ".=";
998
- return { from: node.from, options: afterDotAssign ? methods : globals, validFor: nameTail };
2096
+ return { from: node.from, options: afterDotAssign ? localMethods() : localGlobals(), validFor: nameTail };
999
2097
  }
1000
- // Other names (variables, pair keys, names being declared) are made up
1001
- // by the user, so only a position where no token has been started
1002
- // gets the whole list, and only on request.
2098
+ // Other names (pair keys, names being declared) are made up by the
2099
+ // user, so only a position where no token has been started gets the
2100
+ // whole list, and only on request.
1003
2101
  if (context.explicit && /^[\s(\[{,;=]?$/.test(state.sliceDoc(Math.max(0, pos - 1), pos)))
1004
- return { from: pos, options: globals, validFor: nameTail };
2102
+ return { from: pos, options: localGlobals(), validFor: nameTail };
1005
2103
  return null;
1006
2104
  };
1007
2105
 
2106
+ // The grammar has no statement nodes, so whether a line continues the
2107
+ // statement before it is worked out here, from the token that precedes
2108
+ // the line and from what the line starts with.
2109
+ const skipped = /^(LineComment|DocComment|BlockComment|Pod|Heredoc)$/;
2110
+ const brackets = /^(Block|Parens|Brackets|Interpolation|Program)$/;
2111
+ const wordOperators = /^(and|or|xor|andthen|orelse|notandthen|div|mod|gcd|lcm|eq|ne|lt|gt|le|ge|cmp|leg|eqv|but)$/;
2112
+ // Operators that end a term or stand for a whole statement.
2113
+ const closingOperators = /^(\+\+|--|\.\.\.|!!!|\?\?\?)$/;
2114
+ // The words that start a trait. `of` is left out: it is as often a
2115
+ // name of one's own.
2116
+ const traits = "is|does|returns|handles|where|will";
2117
+ /// The lines that are indented again when they are typed: one that
2118
+ /// closes a bracket, and one that starts with a trait, which is only
2119
+ /// known to continue a statement once the trait's word is there.
2120
+ const indentOnInput = new RegExp("^\\s*(?:[\\}\\]\\)]|(?:" + traits + ")\\s)$");
2121
+ // A line that starts with an infix: an operator that has a space after
2122
+ // it and more on the line, an operator that is a word, a statement
2123
+ // modifier, or a trait (`is export`). A prefix operator touches its
2124
+ // operand, and `...` stands alone.
2125
+ const startsWithInfix = new RegExp("^\\s*(?:[~+\\-*/%<>=|&^?!]+[ \\t]+\\S|(?:" + wordOperators.source.slice(2, -2) +
2126
+ "|if|unless|for|while|until|given|when|with|without" +
2127
+ "|" + traits + ")(?![\\w'-]))");
2128
+ const startsWithMethod = /^\s*\.[^\s\d.]/;
2129
+ // The last token of `node`. A string, a regex, a heredoc and a Pod
2130
+ // block count as one token: what they end in is not code.
2131
+ const wholeTokens = /^(StringLiteral|Regex|Heredoc|Pod)$/;
2132
+ function lastToken(node) {
2133
+ while (node.lastChild && !wholeTokens.test(node.name))
2134
+ node = node.lastChild;
2135
+ return node;
2136
+ }
2137
+ // The last token that ends at or before `pos` in `bracket`, not
2138
+ // counting comments.
2139
+ function tokenBefore(bracket, pos) {
2140
+ let node = bracket.resolveInner(pos, -1);
2141
+ // Either the node that ends there, or the one around the position.
2142
+ if (node.to > pos)
2143
+ node = node.childBefore(pos);
2144
+ while (node) {
2145
+ let token = lastToken(node);
2146
+ if (!skipped.test(token.name))
2147
+ return token;
2148
+ // Go to what comes before the comment, in this node or around it.
2149
+ while (node && !node.prevSibling)
2150
+ node = node.parent;
2151
+ node = node && node.prevSibling;
2152
+ }
2153
+ return null;
2154
+ }
2155
+ function bracketAround(node) {
2156
+ for (let parent = node.parent; parent; parent = parent.parent)
2157
+ if (brackets.test(parent.name))
2158
+ return parent;
2159
+ return null;
2160
+ }
2161
+ // The bracket node whose content a token is part of. The brackets of a
2162
+ // node are part of the content around that node.
2163
+ function bracketOf(token) {
2164
+ let around = bracketAround(token);
2165
+ return around && around == token.parent && /^[()\[\]{}]$/.test(token.name) ? bracketAround(around) : around;
2166
+ }
2167
+ // The first node after `node` that is not a comment.
2168
+ function nextCode(node) {
2169
+ do
2170
+ node = node && node.nextSibling;
2171
+ while (node && skipped.test(node.name));
2172
+ return node;
2173
+ }
2174
+ // Whether a block in braces holds the pairs of a hash, in which a comma
2175
+ // separates items that line up.
2176
+ function holdsPairs(block, state) {
2177
+ let first = block.name == "Block" ? nextCode(block.firstChild) : null;
2178
+ if (!first)
2179
+ return false;
2180
+ if (first.name == "PairKey")
2181
+ return true;
2182
+ let second = nextCode(first);
2183
+ return second != null && second.name == "Operator" && state.sliceDoc(second.from, second.to) == "=>";
2184
+ }
2185
+ // Whether a line continues a statement of `bracket`. The line starts at
2186
+ // `pos` with the text `after`.
2187
+ function continuesStatement(state, bracket, pos, after) {
2188
+ // The lines of a regex are not statements: its `+` and `|` are not
2189
+ // the operators of an expression.
2190
+ for (let node = bracket.resolveInner(pos, -1); node && node.from >= bracket.from; node = node.parent) {
2191
+ if (node.name == "Regex")
2192
+ return false;
2193
+ if (brackets.test(node.name))
2194
+ break;
2195
+ }
2196
+ let before = tokenBefore(bracket, pos);
2197
+ if (!before || bracketOf(before) != bracket)
2198
+ return false;
2199
+ let name = before.name, text = state.sliceDoc(before.from, before.to);
2200
+ if (name == "Operator")
2201
+ return !closingOperators.test(text);
2202
+ if (wordOperators.test(name) && name == text)
2203
+ return true;
2204
+ if (name == ",")
2205
+ return !holdsPairs(bracket, state);
2206
+ if (name == ";" || name == "{")
2207
+ return false;
2208
+ if (name == "}") {
2209
+ // A block ends its statement. A subscript, which touches the term
2210
+ // that it follows, does not: %h{$key}
2211
+ let block = before.parent;
2212
+ return block.from > 0 && !/\s/.test(state.sliceDoc(block.from - 1, block.from)) && startsWithInfix.test(after);
2213
+ }
2214
+ // After the end of a statement, a `.` calls a method on the topic.
2215
+ return startsWithInfix.test(after) || startsWithMethod.test(after);
2216
+ }
2217
+ // Whether the content of a bracket node starts on the line of its
2218
+ // opening bracket, which `delimitedIndent` then aligns the rest with.
2219
+ function isAligned(node, state) {
2220
+ let open = node.firstChild, next = nextCode(open);
2221
+ return open != null && next != null && next != node.lastChild && next.from < state.doc.lineAt(open.from).to;
2222
+ }
2223
+ const bodyKeywords = /^(if|elsif|else|unless|with|orwith|without|for|while|until|loop|repeat|given|when|default|whenever|react|supply|gather|do|try|start)$/;
2224
+ const declarationPrefixes = /^(my|our|has|state|anon|augment|supersede|unit|multi|proto|only)$/;
2225
+ // The first node of the statement that a block is the body of, when it
2226
+ // is the body of a control statement or of a declaration. A block that
2227
+ // is a term in an expression has none: `my $f = sub { ... }`
2228
+ function statementOfBody(block, state) {
2229
+ let declaration = block.parent && /Declaration$/.test(block.parent.name) ? block.parent : null;
2230
+ let first = declaration || block;
2231
+ for (let prev = first.prevSibling; prev; prev = prev.prevSibling) {
2232
+ if (skipped.test(prev.name))
2233
+ continue;
2234
+ if (prev.name == ";" || prev.name == "{")
2235
+ break;
2236
+ // A closing brace that is followed by a line break ends a statement.
2237
+ if (lastToken(prev).name == "}" && /\n/.test(state.sliceDoc(prev.to, first.from)))
2238
+ break;
2239
+ if (declaration && !declarationPrefixes.test(prev.name))
2240
+ return null;
2241
+ first = prev;
2242
+ }
2243
+ return declaration || bodyKeywords.test(first.name) ? first : null;
2244
+ }
2245
+ /// The indentation of the lines in a node that holds statements between
2246
+ /// braces: that of `delimitedIndent`, and one unit more for a line that
2247
+ /// continues a statement.
2248
+ function statementIndent(closing) {
2249
+ let delimited = language.delimitedIndent({ closing });
2250
+ return (context) => {
2251
+ let { node, state, unit } = context, after = context.textAfter;
2252
+ let closed = after.slice(/^\s*/.exec(after)[0].length).startsWith(closing);
2253
+ // The body of a statement is indented from the line where the
2254
+ // statement starts, which need not be the line of its brace.
2255
+ // if $a
2256
+ // && $b {
2257
+ // say 1;
2258
+ // }
2259
+ let statement = isAligned(node, state) ? null : statementOfBody(node, state);
2260
+ let indent = statement ? context.lineIndent(statement.from) + (closed ? 0 : unit) : delimited(context);
2261
+ return !closed && continuesStatement(state, node, context.pos, after) ? indent + unit : indent;
2262
+ };
2263
+ }
2264
+ /// The indentation of the lines at the top level.
2265
+ function topIndent(context) {
2266
+ return continuesStatement(context.state, context.node, context.pos, context.textAfter) ? context.unit : 0;
2267
+ }
2268
+
2269
+ function closing(open) { return String.fromCharCode(brackets$1[open.charCodeAt(0)]); }
2270
+ // The brackets to try, in this order. A bracket that is repeated has
2271
+ // to be closed as many times.
2272
+ const pairs = ["(", "[", "{", "<", "«", "「", "((", "[[", "{{", "(((", "[[[", "{{{"].map(open => {
2273
+ return { open, close: closing(open).repeat(open.length) };
2274
+ });
2275
+ // Whether `text` can stand between the delimiters: none of them is
2276
+ // left open in it, and none closes more than were opened.
2277
+ function fits(text, { open, close }) {
2278
+ let depth = 0;
2279
+ for (let pos = 0; pos < text.length;) {
2280
+ if (text.startsWith(close, pos)) {
2281
+ if (--depth < 0)
2282
+ return false;
2283
+ pos += close.length;
2284
+ }
2285
+ else if (text.startsWith(open, pos)) {
2286
+ depth++;
2287
+ pos += open.length;
2288
+ }
2289
+ else {
2290
+ pos++;
2291
+ }
2292
+ }
2293
+ return depth == 0;
2294
+ }
2295
+ // The delimiters of the comment that a block comment command would
2296
+ // remove for the range, if there is one: the range is that comment, or
2297
+ // all of its text, give or take whitespace.
2298
+ function commentAt(state, from, to) {
2299
+ let start = from + /^\s*/.exec(state.sliceDoc(from, to))[0].length;
2300
+ let node = language.syntaxTree(state).resolveInner(start, 1);
2301
+ if (node.name != "BlockComment")
2302
+ return null;
2303
+ let text = state.sliceDoc(node.from, node.to), open = /^#`(.)\1*/.exec(text);
2304
+ if (!open || brackets$1[open[1].charCodeAt(0)] == null)
2305
+ return null;
2306
+ let close = closing(open[1]).repeat(open[0].length - 2);
2307
+ if (!text.endsWith(close))
2308
+ return null;
2309
+ let blank = (a, b) => a >= b || !/\S/.test(state.sliceDoc(a, b));
2310
+ let isComment = start == node.from && blank(node.to, to);
2311
+ let isContent = blank(node.from + open[0].length, from) && blank(to, node.to - close.length) && to <= node.to;
2312
+ return isComment || isContent ? { open: open[0], close } : null;
2313
+ }
2314
+ function blockTokens(state, pos) {
2315
+ // The command asks at the start of what it works on, which is a
2316
+ // selection range, or the first text of a selected line.
2317
+ let range = state.selection.ranges.find(range => range.from == pos);
2318
+ let existing = range && commentAt(state, range.from, range.to);
2319
+ if (existing)
2320
+ return existing;
2321
+ // The command that goes by lines wraps up to the end of the line, so
2322
+ // the brackets have to fit that text as well.
2323
+ let texts = [], line = state.doc.lineAt(pos);
2324
+ if (range) {
2325
+ texts.push(state.sliceDoc(range.from, range.to));
2326
+ line = state.doc.lineAt(range.to);
2327
+ }
2328
+ else {
2329
+ let onLine = state.selection.ranges.find(range => range.from <= pos && range.from >= line.from);
2330
+ if (onLine)
2331
+ line = state.doc.lineAt(onLine.to);
2332
+ }
2333
+ if (!range || range.to < line.to)
2334
+ texts.push(state.sliceDoc(pos, line.to));
2335
+ // The spaces keep a bracket at the edge of the text from joining the delimiters.
2336
+ let pair = pairs.find(pair => texts.every(text => fits(" " + text + " ", pair))) || pairs[0];
2337
+ return { open: "#`" + pair.open, close: pair.close };
2338
+ }
2339
+ /// Language data that gives `language` block comment tokens which fit
2340
+ /// the selection. It takes precedence over the fixed tokens in the
2341
+ /// language's own data.
2342
+ function embeddedCommentTokens(language) {
2343
+ return state.Prec.high(state.EditorState.languageData.of((state, pos, side) => {
2344
+ if (!language.isActiveAt(state, pos, side))
2345
+ return [];
2346
+ // Only worked out when asked for: this is called for every kind of
2347
+ // language data, and the line comment commands do not read `block`.
2348
+ return [{ get commentTokens() {
2349
+ return { line: "#", get block() { return blockTokens(state, pos); } };
2350
+ } }];
2351
+ }));
2352
+ }
2353
+
2354
+ // A regex literal is one token to the parser of a program: whether it
2355
+ // is closed has to be known where it starts, and a token that is read
2356
+ // piece by piece cannot look that far ahead (see AGENTS.md). Its inside
2357
+ // is parsed by the same grammar, from the top rule for a regex literal,
2358
+ // and that tree takes the place of the token's node. The inner parser
2359
+ // is made from `base` so that it has the same props, among them the
2360
+ // language data that LRLanguage.define puts on top nodes.
2361
+ function nestedParsers(base) {
2362
+ // From the term of a token to the parser of its inside. The
2363
+ // generator registers a top rule under its node name.
2364
+ let parsers = new Map();
2365
+ let wrap = common.parseMixed(node => parsers.get(node.type.id) || null);
2366
+ // The inner parsers are wrapped as well: a regex can hold a block of
2367
+ // code, and that can hold a regex.
2368
+ parsers.set(regexLiteral, { parser: base.configure({ top: "Regex", wrap }) });
2369
+ parsers.set(interpolatingHeredoc, { parser: base.configure({ top: "Heredoc", wrap }) });
2370
+ parsers.set(interpolatingWordList, { parser: base.configure({ top: "StringLiteral", wrap }) });
2371
+ return wrap;
2372
+ }
1008
2373
  /// The Lezer parser for Raku, without the editor-specific node props.
1009
- const parser = parser$1;
1010
- /// A language provider based on the Lezer Raku parser, extended with
1011
- /// highlighting and indentation information.
1012
- const rakuLanguage = language.LRLanguage.define({
2374
+ const parser = parser$1.configure({ wrap: nestedParsers(parser$1) });
2375
+ const baseLanguage = language.LRLanguage.define({
1013
2376
  name: "raku",
1014
- parser: parser.configure({
2377
+ parser: parser$1.configure({
1015
2378
  props: [
1016
2379
  language.indentNodeProp.add({
1017
- "Block Interpolation": language.delimitedIndent({ closing: "}" }),
2380
+ Program: topIndent,
2381
+ "Block Interpolation": statementIndent("}"),
1018
2382
  Parens: language.delimitedIndent({ closing: ")" }),
1019
2383
  Brackets: language.delimitedIndent({ closing: "]" }),
1020
2384
  // The lines of these are text, which keeps its indentation.
@@ -1028,22 +2392,33 @@ const rakuLanguage = language.LRLanguage.define({
1028
2392
  // A heredoc starts with the line break before its first line.
1029
2393
  Heredoc(tree) { return { from: tree.from, to: tree.to }; }
1030
2394
  }),
1031
- highlight.styleTags(Object.assign(Object.assign({}, keywordTags), { "True False": highlight.tags.bool, Nil: highlight.tags.null, Identifier: highlight.tags.function(highlight.tags.variableName), TypeName: highlight.tags.typeName, PackageName: highlight.tags.definition(highlight.tags.className), "EnumName SubsetName": highlight.tags.definition(highlight.tags.typeName), ConstantName: highlight.tags.definition(highlight.tags.constant(highlight.tags.variableName)), "RoutineName RegexName": highlight.tags.function(highlight.tags.definition(highlight.tags.variableName)), MethodName: highlight.tags.function(highlight.tags.propertyName), VariableName: highlight.tags.variableName, AttributeName: highlight.tags.propertyName, SpecialVariable: highlight.tags.special(highlight.tags.variableName), PairKey: highlight.tags.attributeName, Number: highlight.tags.number, Version: highlight.tags.literal, "StringLiteral Heredoc": highlight.tags.string, Regex: highlight.tags.regexp, Pod: highlight.tags.docComment, Escape: highlight.tags.escape, "Interpolation/{ Interpolation/}": highlight.tags.special(highlight.tags.brace), LineComment: highlight.tags.lineComment, BlockComment: highlight.tags.blockComment, DocComment: highlight.tags.docComment, Operator: highlight.tags.operator, ".": highlight.tags.derefOperator, ", ;": highlight.tags.separator, ":": highlight.tags.punctuation, "( )": highlight.tags.paren, "[ ]": highlight.tags.squareBracket, "{ }": highlight.tags.brace }))
2395
+ highlight.styleTags(Object.assign(Object.assign({}, keywordTags), { "True False": highlight.tags.bool, Nil: highlight.tags.null, Identifier: highlight.tags.function(highlight.tags.variableName), TypeName: highlight.tags.typeName, PackageName: highlight.tags.definition(highlight.tags.className), "EnumName SubsetName": highlight.tags.definition(highlight.tags.typeName), ConstantName: highlight.tags.definition(highlight.tags.constant(highlight.tags.variableName)), "RoutineName RegexName": highlight.tags.function(highlight.tags.definition(highlight.tags.variableName)), MethodName: highlight.tags.function(highlight.tags.propertyName), VariableName: highlight.tags.variableName, AttributeName: highlight.tags.propertyName, SpecialVariable: highlight.tags.special(highlight.tags.variableName), PairKey: highlight.tags.attributeName, Number: highlight.tags.number, Version: highlight.tags.literal,
2396
+ // A pair of delimiters in a quote is text of it, and a tag
2397
+ // does not reach the text in a child node.
2398
+ "StringLiteral Heredoc NestedDelimiters": highlight.tags.string, Regex: highlight.tags.regexp, CharacterClass: highlight.tags.character, Assertion: highlight.tags.function(highlight.tags.variableName), "Pod PodFormat": highlight.tags.docComment, PodDirective: highlight.tags.meta,
2399
+ // These are still part of a comment, and are styled as one where
2400
+ // a theme has no style for the second tag.
2401
+ PodHeading: [highlight.tags.docComment, highlight.tags.heading], PodStrong: [highlight.tags.docComment, highlight.tags.strong], PodEmphasis: [highlight.tags.docComment, highlight.tags.emphasis], PodCode: [highlight.tags.docComment, highlight.tags.monospace], PodLink: [highlight.tags.docComment, highlight.tags.link], Escape: highlight.tags.escape, "Interpolation/{ Interpolation/}": highlight.tags.special(highlight.tags.brace), LineComment: highlight.tags.lineComment, BlockComment: highlight.tags.blockComment, DocComment: highlight.tags.docComment, Operator: highlight.tags.operator, ".": highlight.tags.derefOperator, ", ;": highlight.tags.separator, ":": highlight.tags.punctuation, "( )": highlight.tags.paren, "[ ]": highlight.tags.squareBracket, "{ }": highlight.tags.brace }))
1032
2402
  ]
1033
2403
  }),
1034
2404
  languageData: {
1035
2405
  // An embedded comment can use any bracket. Parentheses are the usual choice.
1036
2406
  commentTokens: { line: "#", block: { open: "#`(", close: ")" } },
1037
2407
  closeBrackets: { brackets: ["(", "[", "{", "'", '"', "「"] },
1038
- indentOnInput: /^\s*[\}\]\)]$/
2408
+ indentOnInput
1039
2409
  }
1040
2410
  });
1041
- /// Completion of Raku keywords and of the commonly used built-in types,
1042
- /// routines, methods and special variables.
2411
+ /// A language provider based on the Lezer Raku parser, extended with
2412
+ /// highlighting and indentation information.
2413
+ const rakuLanguage = baseLanguage.configure({ wrap: nestedParsers(baseLanguage.parser) });
2414
+ /// Completion of Raku keywords, of the commonly used built-in types,
2415
+ /// routines, methods and special variables, and of the names that the
2416
+ /// document declares.
1043
2417
  const rakuCompletion = rakuLanguage.data.of({ autocomplete: rakuCompletionSource });
1044
- /// Raku language support, with completion.
2418
+ /// Raku language support, with completion, and with block comment
2419
+ /// tokens that fit the selection.
1045
2420
  function raku() {
1046
- return new language.LanguageSupport(rakuLanguage, [rakuCompletion]);
2421
+ return new language.LanguageSupport(rakuLanguage, [rakuCompletion, embeddedCommentTokens(rakuLanguage)]);
1047
2422
  }
1048
2423
 
1049
2424
  exports.parser = parser;