codemirror-lang-raku 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,59 +1,97 @@
1
1
  import { ContextTracker, ExternalTokenizer, LRParser } from '@lezer/lr';
2
- import { Tree } from '@lezer/common';
3
- import { syntaxTree, LRLanguage, indentNodeProp, delimitedIndent, foldNodeProp, foldInside, LanguageSupport } from '@codemirror/language';
2
+ import { NodeWeakMap, parseMixed } from '@lezer/common';
3
+ import { syntaxTree, delimitedIndent, LRLanguage, indentNodeProp, foldNodeProp, foldInside, LanguageSupport } from '@codemirror/language';
4
4
  import { tags, styleTags } from '@lezer/highlight';
5
+ import { Prec, EditorState } from '@codemirror/state';
5
6
 
6
7
  // This file was generated by lezer-generator. You probably shouldn't edit it.
7
- const BlockComment = 1,
8
- Pod = 2,
9
- Heredoc = 3,
10
- methodDot = 4,
11
- MethodName = 5,
12
- declaredName = 172,
13
- declaredMethodName = 173,
14
- smiley = 174,
15
- Version = 6,
16
- regexBody = 7,
17
- quoteContent = 175,
18
- quoteNestOpen = 176,
19
- quoteNestClose = 177,
20
- quoteEnd = 178,
21
- rawString = 179,
22
- quoteStart = 180,
23
- Regex = 8,
24
- fatArrowKey = 181,
25
- wordOperator = 9,
26
- LineComment = 10,
27
- DocComment = 11,
28
- radixNumber = 13,
29
- PackageName = 26,
30
- RoutineName = 33,
31
- methodRoutineName = 36,
32
- RegexName = 41,
33
- EnumName = 45,
34
- SubsetName = 48,
35
- ConstantName = 51,
36
- VariableName = 53,
37
- AttributeName = 54,
38
- SpecialVariable = 55,
39
- Identifier = 56,
40
- TypeName = 57,
41
- Number = 58,
42
- StringLiteral = 59,
43
- Interpolation = 61,
44
- PairKey = 62,
45
- self = 142,
46
- True = 143,
47
- False = 144,
48
- Nil = 145,
49
- Operator = 164;
8
+ const smiley = 199,
9
+ noSmiley = 200,
10
+ BlockComment = 1,
11
+ podStart = 2,
12
+ podEnd = 201,
13
+ podEndDirective = 3,
14
+ podText = 202,
15
+ PodDirective = 4,
16
+ PodHeading = 5,
17
+ PodStrong = 6,
18
+ PodEmphasis = 7,
19
+ PodCode = 8,
20
+ PodLink = 9,
21
+ PodFormat = 10,
22
+ rawHeredoc = 11,
23
+ interpolatingHeredoc = 12,
24
+ heredocStart = 203,
25
+ methodDot = 13,
26
+ MethodName = 14,
27
+ declaredName = 204,
28
+ declaredMethodName = 205,
29
+ Version = 15,
30
+ regexStart = 206,
31
+ regexBodyEnd = 207,
32
+ regexLitOpen = 208,
33
+ regexLitClose = 209,
34
+ regexText = 210,
35
+ CharacterClass = 16,
36
+ Assertion = 17,
37
+ regexQuote = 18,
38
+ regexOperator = 19,
39
+ regexComment = 20,
40
+ regexBlockComment = 21,
41
+ regexCapture = 22,
42
+ quoteContent = 211,
43
+ quoteNestOpen = 212,
44
+ quoteNestClose = 213,
45
+ quoteEnd = 214,
46
+ rawString = 215,
47
+ quoteStart = 216,
48
+ interpolatingWordList = 23,
49
+ regexLiteral = 24,
50
+ transliteration = 25,
51
+ plainName = 217,
52
+ wordOperator = 26,
53
+ multiName = 218,
54
+ operatorVariable = 27,
55
+ LineComment = 28,
56
+ DocComment = 29,
57
+ Pod = 31,
58
+ radixNumber = 32,
59
+ PackageName = 45,
60
+ RoutineName = 52,
61
+ methodRoutineName = 55,
62
+ multiRoutineName = 56,
63
+ RegexName = 61,
64
+ regexBody = 63,
65
+ VariableName = 65,
66
+ AttributeName = 66,
67
+ SpecialVariable = 67,
68
+ EnumName = 71,
69
+ SubsetName = 74,
70
+ ConstantName = 77,
71
+ Identifier = 79,
72
+ TypeName = 80,
73
+ Number = 81,
74
+ quotedString = 82,
75
+ Interpolation = 83,
76
+ PairKey = 84,
77
+ multi = 95,
78
+ proto = 96,
79
+ only = 97,
80
+ self = 164,
81
+ True = 165,
82
+ False = 166,
83
+ Nil = 167,
84
+ Operator = 186,
85
+ Regex = 190,
86
+ Heredoc = 191,
87
+ StringLiteral = 192;
50
88
 
51
89
  // Opening bracket -> closing bracket, for the delimiters of quotes and
52
90
  // embedded comments. Raku accepts any Unicode bracket pair; this lists
53
91
  // the ones that are likely to be typed.
54
- const brackets = {};
92
+ const brackets$1 = {};
55
93
  for (let pair of ["()", "[]", "{}", "<>", "«»", "「」", "『』", "()", "[]", "{}", "【】", "〈〉", "《》", "〔〕"])
56
- brackets[pair.charCodeAt(0)] = pair.charCodeAt(1);
94
+ brackets$1[pair.charCodeAt(0)] = pair.charCodeAt(1);
57
95
  function isAsciiLetter(ch) { return ch >= 97 /* Ch.a */ && ch <= 122 /* Ch.z */ || ch >= 65 /* Ch.A */ && ch <= 90 /* Ch.Z */; }
58
96
  function isDigit(ch) { return ch >= 48 /* Ch._0 */ && ch <= 57 /* Ch._9 */; }
59
97
  // The characters of the grammar's whitespace token.
@@ -118,39 +156,130 @@ function word(input, from, to) {
118
156
  class Quote {
119
157
  constructor(parent, open, close, count,
120
158
  // How many nested opening delimiters are unclosed.
121
- depth) {
159
+ depth, interpolates,
160
+ // Whether a backslash that does not start an escape still
161
+ // takes the delimiter after it out of play: q:c[a \] b]
162
+ escapes) {
122
163
  this.parent = parent;
123
164
  this.open = open;
124
165
  this.close = close;
125
166
  this.count = count;
126
167
  this.depth = depth;
127
- 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, depth, interpolates, escapes ? 1 : 0])
172
+ hash = (hash * 31 + part) | 0;
173
+ this.hash = hash;
174
+ }
175
+ withDepth(depth) {
176
+ return new Quote(this.parent, this.open, this.close, this.count, depth, this.interpolates, this.escapes);
177
+ }
178
+ }
179
+ const noHeredocs = [];
180
+ // The quote that the text of a heredoc is read as. It has no
181
+ // delimiters, and ends where the text does.
182
+ const heredocQuote = new Quote(null, -1, -1, 1, 0, 63 /* Interpolate.All */, true);
183
+ // The body of a regex declaration, or the inside of a regex literal,
184
+ // that is being read piece by piece.
185
+ class RegexBody {
186
+ constructor(// The body around the block of code that this one is in.
187
+ parent,
188
+ // How many braces are open in a block of code in the
189
+ // regex. Zero in the regex itself.
190
+ braces,
191
+ // For a regex literal, the position in the document of
192
+ // the delimiter that closes it. It is not part of the
193
+ // hash, which is to be the same before and after an edit
194
+ // that moves the literal.
195
+ literalEnd = -1) {
196
+ this.parent = parent;
197
+ this.braces = braces;
198
+ this.literalEnd = literalEnd;
199
+ this.hash = ((parent ? parent.hash * 31 : 0) + 7919 * (braces + 1) + (literalEnd < 0 ? 0 : 104729)) | 0;
200
+ }
201
+ withBraces(braces) { return new RegexBody(this.parent, braces, this.literalEnd); }
202
+ }
203
+ // A Pod block that is being read piece by piece.
204
+ class PodBlock {
205
+ constructor(kind,
206
+ // What follows `=begin`, which `=end` has to repeat.
207
+ name,
208
+ // Whether it holds code, in which formatting codes are not read.
209
+ code) {
210
+ this.kind = kind;
211
+ this.name = name;
212
+ this.code = code;
213
+ let hash = 101 + kind * 7 + (code ? 3 : 0);
214
+ for (let i = 0; i < name.length; i++)
215
+ hash = (hash * 31 + name.charCodeAt(i)) | 0;
216
+ this.hash = hash;
128
217
  }
129
218
  }
130
219
  class Context {
131
- constructor(mode, quote) {
220
+ constructor(mode, quote,
221
+ // The heredocs that were opened on the current line, whose
222
+ // text starts on the next one.
223
+ heredocs, pod,
224
+ // The body of a regex declaration that this is in.
225
+ regex, hash = -1) {
132
226
  this.mode = mode;
133
227
  this.quote = quote;
228
+ this.heredocs = heredocs;
229
+ this.pod = pod;
230
+ this.regex = regex;
231
+ if (hash < 0) {
232
+ hash = (quote ? quote.hash : 0) ^ (pod ? pod.hash : 0) ^ (regex ? regex.hash : 0);
233
+ for (let { terminator, interpolates } of heredocs) {
234
+ hash = (hash * 31 + (interpolates ? 19 : 17)) | 0;
235
+ for (let i = 0; i < terminator.length; i++)
236
+ hash = (hash * 31 + terminator.charCodeAt(i)) | 0;
237
+ }
238
+ // Negative values stand for "not worked out yet".
239
+ hash &= 0x7fffffff;
240
+ }
241
+ this.hash = hash;
242
+ }
243
+ withMode(mode) {
244
+ return mode == this.mode ? this : new Context(mode, this.quote, this.heredocs, this.pod, this.regex, this.hash);
245
+ }
246
+ withHeredocs(heredocs) {
247
+ return heredocs == this.heredocs ? this : new Context(this.mode, this.quote, heredocs, this.pod, this.regex);
248
+ }
249
+ withQuote(mode, quote, heredocs = this.heredocs) {
250
+ return new Context(mode, quote, heredocs, this.pod, this.regex);
251
+ }
252
+ withPod(pod) { return new Context(this.mode, this.quote, this.heredocs, pod, this.regex); }
253
+ withRegex(regex) {
254
+ return new Context(this.mode, this.quote, this.heredocs, this.pod, regex);
134
255
  }
135
- // An old node is only reused where this is the same as where it was
136
- // made. The mode is left out: it changes with every token, which
137
- // would rule out nearly all reuse, and `reuse` works it out again.
138
- get hash() { return this.quote ? this.quote.hash : 0; }
139
- withMode(mode) { return mode == this.mode ? this : new Context(mode, this.quote); }
140
256
  }
257
+ // The nodes of a string: a quote, the token of a word list that
258
+ // interpolates, and the tree of its inside, which takes the place of
259
+ // that token.
260
+ const stringTerms = new Set([quotedString, interpolatingWordList, StringLiteral]);
141
261
  // The mode after a token or a node, for those where the term settles
142
262
  // it. Shifting a token and reusing a node both look here, so that they
143
263
  // agree.
144
264
  const termModes = new Map();
145
- for (let term of [VariableName, AttributeName, SpecialVariable, Number, radixNumber, Version, MethodName,
146
- PairKey, rawString, quoteEnd, StringLiteral, Regex, regexBody, self, True, False, Nil])
265
+ for (let term of [VariableName, AttributeName, SpecialVariable, operatorVariable, Number, radixNumber, Version, MethodName,
266
+ PairKey, rawString, quoteEnd, ...stringTerms, regexLiteral, transliteration, regexBody,
267
+ // The tree of the inside of a regex literal, which takes the
268
+ // place of the literal's token in a node that is reused.
269
+ Regex, self, True, False, Nil,
270
+ // The pieces of a regex. What follows them is more of
271
+ // the regex, or the brace that closes it.
272
+ regexText, CharacterClass, Assertion, regexQuote, regexOperator, regexComment,
273
+ regexBlockComment, regexCapture])
147
274
  termModes.set(term, 1 /* Mode.AfterTerm */);
148
- for (let term of [Identifier, TypeName, smiley, declaredName, declaredMethodName, PackageName, RoutineName,
149
- methodRoutineName, RegexName, EnumName, SubsetName, ConstantName])
275
+ for (let term of [Identifier, TypeName, smiley, noSmiley, declaredName, declaredMethodName, PackageName, RoutineName,
276
+ methodRoutineName, multiName, multiRoutineName, RegexName, EnumName, SubsetName, ConstantName])
150
277
  termModes.set(term, 2 /* Mode.AfterName */);
151
278
  // The `}` of "{...}" in a string does not take a subscript.
152
279
  for (let term of [quoteContent, Interpolation])
153
280
  termModes.set(term, 0 /* Mode.Term */);
281
+ for (let term of [multi, proto, only])
282
+ termModes.set(term, 4 /* Mode.AfterMulti */);
154
283
  // The mode after an operator, from its first three characters. `after`
155
284
  // is the mode before it.
156
285
  function modeAfterOperator(first, second, third, after) {
@@ -165,7 +294,8 @@ function modeAfterOperator(first, second, third, after) {
165
294
  }
166
295
  // The mode after a token that the terms file has no name for, from its
167
296
  // first character. Keywords and identifiers come through here alike;
168
- // an identifier gets its mode when the Identifier node is reduced.
297
+ // an identifier gets its mode when the Identifier node is reduced,
298
+ // from modeAfter, which also looks at how it is spelled.
169
299
  function modeAfterChar(first) {
170
300
  if (first == 41 /* Ch.ParenClose */ || first == 93 /* Ch.BracketClose */ || first == 36 /* Ch.Dollar */ || first == 64 /* Ch.At */)
171
301
  return 1 /* Mode.AfterTerm */;
@@ -174,9 +304,20 @@ function modeAfterChar(first) {
174
304
  return 2 /* Mode.AfterName */;
175
305
  return first == 125 /* Ch.BraceClose */ ? 3 /* Mode.AfterBlock */ : 0 /* Mode.Term */;
176
306
  }
307
+ // The built-in names that stand for a value and take no arguments. The
308
+ // constant `e` is left out: a routine or a variable of one's own is too
309
+ // often named that.
310
+ const termWords = /^(pi|π|tau|τ|now|time|rand)$/;
177
311
  // The mode after the token of the given term at the input's position.
178
312
  // `before` is the mode before it.
179
313
  function modeAfter(term, input, offset, before) {
314
+ // A term that takes no arguments is followed by an operator: pi /2
315
+ if (term == Identifier) {
316
+ let end = nameEnd(input, offset);
317
+ // Most names are longer than these, and are not looked at further.
318
+ if (end - offset <= 4 && termWords.test(word(input, offset, end)))
319
+ return 1 /* Mode.AfterTerm */;
320
+ }
180
321
  let mode = termModes.get(term);
181
322
  if (mode != null)
182
323
  return mode;
@@ -185,17 +326,159 @@ function modeAfter(term, input, offset, before) {
185
326
  return modeAfterOperator(first, input.peek(offset + 1), input.peek(offset + 2), before);
186
327
  return modeAfterChar(first);
187
328
  }
188
- const skippedTerms = new Set([LineComment, DocComment, BlockComment, Pod, Heredoc]);
329
+ // The context at the start of a parse. The tokens that start the parse
330
+ // of the inside of another token are only looked for while the context
331
+ // is still this one, which spares asking the parser whether they can
332
+ // be shifted at every other token.
333
+ const startContext = new Context(0 /* Mode.Term */, null, noHeredocs, null, null);
334
+ // The tokens of the text of a heredoc, and the tree of its inside,
335
+ // which takes the place of the token of one that interpolates.
336
+ const heredocTerms = new Set([rawHeredoc, interpolatingHeredoc, Heredoc]);
337
+ const skippedTerms = new Set([LineComment, DocComment, BlockComment, ...heredocTerms, Pod, podStart, podEnd, podEndDirective,
338
+ podText, PodDirective, PodHeading, PodStrong, PodEmphasis, PodCode, PodLink, PodFormat]);
339
+ // The heredoc that the quote at offset `at` opens, when it opens one.
340
+ // Its terminator is what the quote holds: END for q:to/END/
341
+ function heredocAt(input, at) {
342
+ let first = input.peek(at);
343
+ if (first != 113 /* Ch.q */ && first != 81 /* Ch.Q */)
344
+ return null;
345
+ let opening = readOpening(input, at);
346
+ if (!opening || !opening.heredoc)
347
+ return null;
348
+ let end = opening.start;
349
+ while (input.peek(end) != opening.close && input.peek(end) != 10 /* Ch.Newline */ && input.peek(end) >= 0)
350
+ end++;
351
+ // An empty one, as in the `q:to//` of a quote that is being typed,
352
+ // would end the heredoc at the next blank line.
353
+ let terminator = word(input, opening.start, end).trim();
354
+ return terminator ? { terminator, interpolates: opening.interpolates == 63 /* Interpolate.All */ } : null;
355
+ }
356
+ // `heredocs`, with the heredoc that the quote at offset `at` opens, if
357
+ // it opens one.
358
+ function withHeredocAt(heredocs, input, at) {
359
+ let heredoc = heredocAt(input, at);
360
+ return heredoc ? heredocs.concat(heredoc) : heredocs;
361
+ }
362
+ function hasNewline(input, from, to) {
363
+ for (let i = from; i < to; i++)
364
+ if (input.peek(i) == 10 /* Ch.Newline */)
365
+ return true;
366
+ return false;
367
+ }
368
+ // The heredocs that are open after a reused node, of which the input is
369
+ // at the start. As when tokens are shifted, a line break between tokens
370
+ // and the text of a heredoc close the ones before them.
371
+ function heredocsAfter(node, input, before) {
372
+ // Most reused nodes are single tokens.
373
+ if (!node.children.length && !stringTerms.has(node.type.id) && !heredocTerms.has(node.type.id))
374
+ return before;
375
+ let cursor = node.cursor(), found = [], closedAt = -1;
376
+ // Goes over the node at the cursor from its end, and tells whether
377
+ // the heredocs before some point in it are closed.
378
+ function scan() {
379
+ let type = cursor.type.id, { from, to } = cursor;
380
+ if (heredocTerms.has(type)) {
381
+ closedAt = to;
382
+ return true;
383
+ }
384
+ let isString = stringTerms.has(type);
385
+ if (isString) {
386
+ let heredoc = heredocAt(input, from);
387
+ if (heredoc)
388
+ found.push({ pos: from, heredoc });
389
+ }
390
+ // The tree of the inside of a regex literal or of a word list. It
391
+ // was one token when it was shifted, which opens no heredoc.
392
+ if (type == Regex || type == StringLiteral)
393
+ return false;
394
+ if (!cursor.lastChild())
395
+ return false;
396
+ // Between the pieces of a string lies its text, not whitespace.
397
+ let gaps = !isString;
398
+ for (let gapEnd = to;;) {
399
+ if (gaps && hasNewline(input, cursor.to, gapEnd)) {
400
+ closedAt = gapEnd;
401
+ break;
402
+ }
403
+ if (scan())
404
+ break;
405
+ gapEnd = cursor.from;
406
+ if (!cursor.prevSibling()) {
407
+ if (gaps && hasNewline(input, from, gapEnd))
408
+ closedAt = gapEnd;
409
+ break;
410
+ }
411
+ }
412
+ cursor.parent();
413
+ return closedAt >= 0;
414
+ }
415
+ scan();
416
+ if (!found.length && closedAt < 0)
417
+ return before;
418
+ let after = found.filter(opener => opener.pos >= closedAt).sort((a, b) => a.pos - b.pos).map(opener => opener.heredoc);
419
+ return closedAt < 0 ? before.concat(after) : after.length ? after : noHeredocs;
420
+ }
421
+ // The last token in `node` that says what the mode after it is: one
422
+ // that is not a comment or Pod, which leave the mode as it was. A node
423
+ // that settles the mode by itself counts as a token. Null when the
424
+ // node holds nothing but comments.
425
+ function lastToken$1(node) {
426
+ if (termModes.has(node.type.id) || !node.lastChild)
427
+ return skippedTerms.has(node.type.id) ? null : node;
428
+ for (let child = node.lastChild; child; child = child.prevSibling) {
429
+ let found = skippedTerms.has(child.type.id) ? null : lastToken$1(child);
430
+ if (found)
431
+ return found;
432
+ }
433
+ return null;
434
+ }
189
435
  const trackContext = new ContextTracker({
190
- start: new Context(0 /* Mode.Term */, null),
436
+ start: startContext,
191
437
  shift(context, term, _stack, input) {
438
+ if (term == rawHeredoc || term == interpolatingHeredoc)
439
+ return context.withHeredocs(noHeredocs);
440
+ // The text of a heredoc that interpolates is read by a parser of
441
+ // its own, as a quote without delimiters.
442
+ if (term == heredocStart)
443
+ return context.withQuote(0 /* Mode.Term */, heredocQuote);
444
+ if (term == podStart)
445
+ return context.withPod(readPodStart(input));
446
+ if (term == podEnd || term == podEndDirective)
447
+ return context.withPod(null);
192
448
  if (skippedTerms.has(term))
193
449
  return context;
450
+ let { regex } = context;
451
+ if (term == regexStart)
452
+ return context.withRegex(new RegexBody(regex, 0));
453
+ if (term == regexBodyEnd)
454
+ return context.withRegex(regex && regex.parent).withMode(termModes.get(regexBody));
455
+ // The inside of a regex literal is read by a parser of its own, of
456
+ // which these are the first and the last token. The closing
457
+ // delimiter is found the way termToken found it when it made the
458
+ // literal one token, so that the two agree on where the regex ends.
459
+ if (term == regexLitOpen) {
460
+ let opening = regexOpening(input), end = opening ? regexEnd(input, opening) : -1;
461
+ // Without one, which takes a parse that does not start from a
462
+ // literal, the regex runs to the end of what is parsed.
463
+ return context.withRegex(new RegexBody(null, 0, end < 0 ? Infinity : input.pos + end));
464
+ }
465
+ if (term == regexLitClose)
466
+ return context.withRegex(null);
467
+ // The braces of a block of code in a regex, and of the blocks in it.
468
+ // The tokens `{` and `}` have no term to go by.
469
+ if (regex && !termModes.has(term)) {
470
+ if (input.next == 123 /* Ch.BraceOpen */)
471
+ context = context.withRegex(regex.withBraces(regex.braces + 1));
472
+ else if (input.next == 125 /* Ch.BraceClose */ && regex.braces > 0)
473
+ context = context.withRegex(regex.withBraces(regex.braces - 1));
474
+ }
194
475
  switch (term) {
195
476
  case quoteStart: {
196
- let opening = readOpening(input);
197
- return opening ? new Context(0 /* Mode.Term */, new Quote(context.quote, opening.open, opening.close, opening.count, 0))
198
- : context;
477
+ let opening = wordListOpening(input, 0) || readOpening(input);
478
+ if (!opening)
479
+ return context;
480
+ let quote = new Quote(context.quote, opening.open, opening.close, opening.count, 0, opening.interpolates, opening.escapes);
481
+ return context.withQuote(0 /* Mode.Term */, quote, withHeredocAt(context.heredocs, input, 0));
199
482
  }
200
483
  case quoteNestOpen:
201
484
  case quoteNestClose: {
@@ -203,53 +486,37 @@ const trackContext = new ContextTracker({
203
486
  if (!quote)
204
487
  return context;
205
488
  let depth = quote.depth + (term == quoteNestOpen ? 1 : -1);
206
- return new Context(0 /* Mode.Term */, new Quote(quote.parent, quote.open, quote.close, quote.count, depth));
489
+ return context.withQuote(0 /* Mode.Term */, quote.withDepth(depth));
207
490
  }
208
491
  case quoteEnd:
209
- return new Context(1 /* Mode.AfterTerm */, context.quote && context.quote.parent);
492
+ return context.withQuote(1 /* Mode.AfterTerm */, context.quote && context.quote.parent);
210
493
  }
211
494
  // The whitespace token has no term to go by. It is the only token
212
- // that starts with a space.
213
- if (!termModes.has(term) && isSpace(input.next))
495
+ // that starts with a space. A line break in it leaves the heredocs
496
+ // that found no terminator behind.
497
+ if (!termModes.has(term) && isSpace(input.next)) {
498
+ if (!context.heredocs.length)
499
+ return context;
500
+ for (let pos = 0; isSpace(input.peek(pos)); pos++)
501
+ if (input.peek(pos) == 10 /* Ch.Newline */)
502
+ return context.withHeredocs(noHeredocs);
214
503
  return context;
504
+ }
505
+ if (term == rawString)
506
+ context = context.withHeredocs(withHeredocAt(context.heredocs, input, 0));
215
507
  return context.withMode(modeAfter(term, input, 0, context.mode));
216
508
  },
217
- reduce(context, term) {
218
- return term == Identifier || term == Interpolation ? context.withMode(termModes.get(term)) : context;
509
+ reduce(context, term, _stack, input) {
510
+ // The input is at the start of the node that is reduced.
511
+ return term == Identifier || term == Interpolation ? context.withMode(modeAfter(term, input, 0, context.mode))
512
+ : context;
219
513
  },
220
514
  reuse(context, node, _stack, input) {
221
- // Walk down to the last token, keeping its offset from the node's
222
- // start, where the input is positioned. Stop at a node that settles
223
- // the mode by itself.
224
- let last = node, offset = 0;
225
- while (last instanceof Tree && last.children.length && !termModes.has(last.type.id)) {
226
- offset += last.positions[last.children.length - 1];
227
- last = last.children[last.children.length - 1];
228
- }
229
- let term;
230
- if (last instanceof Tree) {
231
- term = last.type.id;
232
- }
233
- else {
234
- // A buffer holds its nodes in pre-order, as type, start, end and
235
- // the index after the node's last descendant.
236
- let { buffer } = last, index = 0;
237
- for (let from = 0, to = buffer.length;;) {
238
- index = from;
239
- while (buffer[index + 3] < to)
240
- index = buffer[index + 3];
241
- from = index + 4;
242
- to = buffer[index + 3];
243
- if (from == to || termModes.has(buffer[index]))
244
- break;
245
- }
246
- term = buffer[index];
247
- offset += buffer[index + 1];
248
- }
249
- if (skippedTerms.has(term))
250
- return context;
515
+ context = context.withHeredocs(heredocsAfter(node, input, context.heredocs));
516
+ // The node's positions count from its start, where the input is.
517
+ let last = lastToken$1(node.topNode);
251
518
  // What comes before a trailing `++` in a reused node is a term.
252
- return context.withMode(modeAfter(term, input, offset, 1 /* Mode.AfterTerm */));
519
+ return last ? context.withMode(modeAfter(last.type.id, input, last.from, 1 /* Mode.AfterTerm */)) : context;
253
520
  },
254
521
  hash: context => context.hash
255
522
  });
@@ -257,7 +524,7 @@ function context(stack) { return stack.context; }
257
524
  // Whether a token of `size` characters at the current position is where
258
525
  // a term starts, as opposed to an infix operator.
259
526
  function inTermPosition(input, mode, size) {
260
- if (mode == 0 /* Mode.Term */)
527
+ if (mode == 0 /* Mode.Term */ || mode == 4 /* Mode.AfterMulti */)
261
528
  return true;
262
529
  if (mode == 1 /* Mode.AfterTerm */)
263
530
  return false;
@@ -270,21 +537,28 @@ function inTermPosition(input, mode, size) {
270
537
  // #`(( ... )), needs the same number of closing ones. Brackets of the
271
538
  // same kind nest. An unclosed comment runs to the end of the input.
272
539
  const blockComment = new ExternalTokenizer(input => {
273
- if (input.next != 35 /* Ch.Hash */ || input.peek(1) != 96 /* Ch.Backtick */)
540
+ let end = embeddedCommentEnd(input, 0);
541
+ if (!end)
274
542
  return;
275
- let open = input.peek(2), close = brackets[open];
276
- if (close == null)
277
- return;
278
- let count = 1;
279
- while (input.peek(2 + count) == open)
280
- count++;
281
- let end = rawEnd(input, 2 + count, { open, close, count, escapes: false });
282
543
  if (end < 0)
283
544
  skipToEnd(input);
284
545
  else
285
546
  input.advance(end);
286
547
  input.acceptToken(BlockComment);
287
548
  });
549
+ // The offset after the embedded comment that starts at offset `at`.
550
+ // Zero when none starts there, and -1 when it is not closed.
551
+ function embeddedCommentEnd(input, at) {
552
+ if (input.peek(at) != 35 /* Ch.Hash */ || input.peek(at + 1) != 96 /* Ch.Backtick */)
553
+ return 0;
554
+ let open = input.peek(at + 2), close = brackets$1[open];
555
+ if (close == null)
556
+ return 0;
557
+ let count = 1;
558
+ while (input.peek(at + 2 + count) == open)
559
+ count++;
560
+ return rawEnd(input, at + 2 + count, { open, close, count, escapes: false });
561
+ }
288
562
  function isBlank(ch) { return ch == 32 /* Ch.Space */ || ch == 9 /* Ch.Tab */ || ch == 13 /* Ch.Return */; }
289
563
  // The offset of the first character at or after `pos` that is not a blank.
290
564
  function blanksEnd(input, pos) {
@@ -292,6 +566,18 @@ function blanksEnd(input, pos) {
292
566
  pos++;
293
567
  return pos;
294
568
  }
569
+ // How far a token looks over the whitespace after it, to see what
570
+ // follows. A token must not look far past its end: see the note on
571
+ // lookahead in AGENTS.md.
572
+ const maxGap = 20;
573
+ // The offset of the first character at or after `pos` that is not a
574
+ // blank, or not whitespace when `lines` is set, going over at most
575
+ // maxGap characters.
576
+ function gapEnd(input, pos, lines = false) {
577
+ for (let end = pos + maxGap; pos < end && (lines ? isSpace : isBlank)(input.peek(pos));)
578
+ pos++;
579
+ return pos;
580
+ }
295
581
  // Whether only blanks separate the current position from the start of its line.
296
582
  function atLineStart(input) {
297
583
  for (let i = -1;; i--) {
@@ -321,121 +607,263 @@ function skipBlanks(input) {
321
607
  // Skips blanks, and tells whether the rest of the line starts with `text`.
322
608
  function lineStartsWith(input, text) {
323
609
  skipBlanks(input);
610
+ return startsWith(input, 0, text);
611
+ }
612
+ // Whether the text from offset `pos` starts with `text`, as a whole name.
613
+ function startsWith(input, pos, text) {
324
614
  for (let i = 0; i < text.length; i++)
325
- if (input.peek(i) != text.charCodeAt(i))
615
+ if (input.peek(pos + i) != text.charCodeAt(i))
326
616
  return false;
327
- return !continuesName(input, text.length);
617
+ return !continuesName(input, pos + text.length);
328
618
  }
329
619
  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,})$/;
330
- // A Pod block, from its `=directive` at the start of a line.
331
- // =begin name ... =end name delimited, can hold blocks of other names
332
- // =finish the rest of the file
333
- // =for name, =head1, ... up to the next blank line
334
- const podToken = new ExternalTokenizer(input => {
620
+ const podCodeBlock = /^(code|input|output)$/;
621
+ // The offset after the directive that starts at the current position,
622
+ // as in `=begin` or `=head1`, or -1 when there is none.
623
+ function podDirectiveEnd(input) {
335
624
  if (input.next != 61 /* Ch.Equals */ || !isAsciiLetter(input.peek(1)) || !atLineStart(input))
336
- return;
625
+ return -1;
337
626
  let end = 1;
338
627
  while (isAsciiLetter(input.peek(end)) || isDigit(input.peek(end)))
339
628
  end++;
340
629
  let directive = word(input, 1, end);
341
630
  if (!podDirective.test(directive) || continuesName(input, end))
342
- return;
631
+ return -1;
343
632
  // A semantic block such as =TITLE has to start in the first column.
344
633
  // An indented `=FOO + 1` continues an assignment.
345
634
  if (directive.charCodeAt(0) <= 90 /* Ch.Z */ && input.peek(-1) >= 0 && input.peek(-1) != 10 /* Ch.Newline */)
635
+ return -1;
636
+ return end;
637
+ }
638
+ // The offset after the name that goes with a directive which ends at
639
+ // offset `end`: `=begin pod`, `=for comment`, `=end code`. The other
640
+ // directives have none.
641
+ function podNameEnd(input, end) {
642
+ if (!/^(begin|for|end)$/.test(word(input, 1, end)))
643
+ return end;
644
+ let name = nameEnd(input, blanksEnd(input, end));
645
+ return name < 0 ? end : name;
646
+ }
647
+ // The block that the directive at the current position starts.
648
+ // =begin name ... =end name delimited, can hold blocks of other names
649
+ // =finish the rest of the file
650
+ // =for name, =head1, ... up to the next blank line
651
+ function readPodStart(input) {
652
+ let end = podDirectiveEnd(input), directive = word(input, 1, end);
653
+ if (directive == "finish")
654
+ return new PodBlock(2 /* PodKind.Finish */, "", false);
655
+ let nameStart = blanksEnd(input, end), name = word(input, nameStart, Math.max(nameStart, nameEnd(input, nameStart)));
656
+ if (directive == "begin")
657
+ return new PodBlock(0 /* PodKind.Delimited */, name, podCodeBlock.test(name));
658
+ return new PodBlock(1 /* PodKind.Paragraph */, "", podCodeBlock.test(directive == "for" ? name : directive));
659
+ }
660
+ // Whether the current position is right after a `=head` directive,
661
+ // where the text of the heading starts.
662
+ function atHeadingText(input) {
663
+ let pos = -1;
664
+ while (isDigit(input.peek(pos)))
665
+ pos--;
666
+ for (let i = 0; i < 5; i++)
667
+ if (input.peek(pos - i) != "=head".charCodeAt(4 - i))
668
+ return false;
669
+ for (pos -= 5; isBlank(input.peek(pos)); pos--) { }
670
+ return input.peek(pos) < 0 || input.peek(pos) == 10 /* Ch.Newline */;
671
+ }
672
+ // Whether a formatting code starts at offset `pos`: B<, C<<, L«
673
+ function isFormattingCode(input, pos) {
674
+ let ch = input.peek(pos), after = input.peek(pos + 1), before = input.peek(pos - 1);
675
+ return ch >= 65 /* Ch.A */ && ch <= 90 /* Ch.Z */ && (after == 60 /* Ch.Less */ || after == 171 /* Ch.GuillemetOpen */) &&
676
+ !isAsciiLetter(before) && !isDigit(before);
677
+ }
678
+ // The offset after the formatting code at the current position. When
679
+ // the code is not closed in its paragraph, this is the negated offset
680
+ // of the end of the text that was gone over to find that out.
681
+ function formattingCodeEnd(input) {
682
+ let open = input.peek(1), close = brackets$1[open], count = 1;
683
+ while (open == 60 /* Ch.Less */ && input.peek(1 + count) == open)
684
+ count++;
685
+ for (let pos = 1 + count, depth = 1;; pos++) {
686
+ let ch = input.peek(pos);
687
+ if (ch < 0)
688
+ return -pos;
689
+ if (ch == 10 /* Ch.Newline */) {
690
+ // A blank line or a directive ends the paragraph.
691
+ let next = blanksEnd(input, pos + 1), after = input.peek(next);
692
+ if (after < 0 || after == 10 /* Ch.Newline */ || after == 61 /* Ch.Equals */ && isAsciiLetter(input.peek(next + 1)))
693
+ return -pos;
694
+ }
695
+ else if (ch == close && repeats(input, pos, close, count)) {
696
+ pos += count - 1;
697
+ if (--depth == 0)
698
+ return pos + 1;
699
+ }
700
+ else if (ch == open && count == 1) {
701
+ // C<a < b> is not closed by the first `>` when another `<` came before it.
702
+ depth++;
703
+ }
704
+ }
705
+ }
706
+ const formattingTerms = { 66: PodStrong, 73: PodEmphasis, 67: PodCode, 76: PodLink };
707
+ // The pieces of a Pod block, which starts with a `=directive` at the
708
+ // start of a line. What it is, and with that where it ends, is kept in
709
+ // the context.
710
+ const podToken = new ExternalTokenizer((input, stack) => {
711
+ let { pod } = context(stack);
712
+ if (!pod) {
713
+ let end = podDirectiveEnd(input);
714
+ if (end > 0)
715
+ input.acceptToken(podStart, podNameEnd(input, end));
346
716
  return;
347
- input.advance(end);
348
- if (directive == "finish") {
349
- skipToEnd(input);
350
717
  }
351
- else if (directive == "begin") {
352
- skipBlanks(input);
353
- let name = word(input, 0, Math.max(0, nameEnd(input, 0)));
354
- for (;;) {
355
- skipLine(input);
356
- if (input.next < 0)
357
- break;
358
- input.advance();
359
- if (!lineStartsWith(input, "=end"))
360
- continue;
361
- input.advance(4);
362
- if (lineStartsWith(input, name)) {
363
- skipLine(input);
364
- break;
365
- }
366
- }
718
+ let next = input.next;
719
+ if (next < 0)
720
+ return input.acceptToken(podEnd);
721
+ if (next == 10 /* Ch.Newline */ && pod.kind == 1 /* PodKind.Paragraph */) {
722
+ // Ends before a blank line or another directive.
723
+ let pos = blanksEnd(input, 1), ch = input.peek(pos);
724
+ if (ch == 10 /* Ch.Newline */ || ch < 0 || ch == 61 /* Ch.Equals */ && isAsciiLetter(input.peek(pos + 1)))
725
+ return input.acceptToken(podEnd);
367
726
  }
368
- else {
369
- for (;;) {
727
+ let directive = pod.kind == 0 /* PodKind.Delimited */ ? podDirectiveEnd(input) : -1;
728
+ if (directive > 0) {
729
+ if (directive == 4 && startsWith(input, 1, "end") && startsWith(input, blanksEnd(input, 4), pod.name)) {
370
730
  skipLine(input);
371
- if (input.next < 0)
372
- break;
373
- // Stop before a blank line or another directive.
374
- let i = blanksEnd(input, 1), ch = input.peek(i);
375
- if (ch == 10 /* Ch.Newline */ || ch < 0 || ch == 61 /* Ch.Equals */ && isAsciiLetter(input.peek(i + 1)))
376
- break;
377
- input.advance();
731
+ return input.acceptToken(podEndDirective);
378
732
  }
733
+ return input.acceptToken(PodDirective, podNameEnd(input, directive));
379
734
  }
380
- input.acceptToken(Pod);
735
+ if (atHeadingText(input) && !isLineEnd(input, 0)) {
736
+ skipLine(input);
737
+ return input.acceptToken(PodHeading);
738
+ }
739
+ if (!pod.code && isFormattingCode(input, 0)) {
740
+ let end = formattingCodeEnd(input);
741
+ if (end > 0)
742
+ return input.acceptToken(formattingTerms[next] || PodFormat, end);
743
+ // What looked like a code is text. The text that was gone over is
744
+ // made one token, so that no token has looked beyond its own end.
745
+ // A token that does keeps the tokens before it from being reused.
746
+ return input.acceptToken(podText, -end);
747
+ }
748
+ // Text, up to where one of the above may apply.
749
+ for (;;) {
750
+ input.advance();
751
+ let ch = input.next;
752
+ if (ch < 0 || ch == 10 /* Ch.Newline */ || !pod.code && isFormattingCode(input, 0))
753
+ break;
754
+ // A directive can only follow the blanks at the start of a line.
755
+ if (ch == 61 /* Ch.Equals */ && atLineStart(input))
756
+ break;
757
+ }
758
+ input.acceptToken(podText);
381
759
  });
382
760
  const quoteWords = {
383
761
  q: "raw", Q: "raw", qw: "raw", qww: "raw", qx: "raw", Qw: "raw", Qww: "raw", Qx: "raw",
384
762
  qq: "interpolating", qqw: "interpolating", qqww: "interpolating", qqx: "interpolating",
385
763
  rx: "regex", m: "regex", ms: "regex",
386
- s: "substitution", ss: "substitution", S: "substitution", tr: "substitution", TR: "substitution"
764
+ s: "substitution", ss: "substitution", S: "substitution",
765
+ // Like a substitution, but its first part is not a regex.
766
+ tr: "transliteration", TR: "transliteration"
387
767
  };
768
+ // The kinds of interpolation that the adverbs of a quote switch.
769
+ const interpolationAdverbs = new Map([
770
+ ["c", 1 /* Interpolate.Closure */], ["closure", 1 /* Interpolate.Closure */],
771
+ ["s", 2 /* Interpolate.Scalar */], ["scalar", 2 /* Interpolate.Scalar */],
772
+ ["a", 4 /* Interpolate.Array */], ["array", 4 /* Interpolate.Array */],
773
+ ["h", 8 /* Interpolate.Hash */], ["hash", 8 /* Interpolate.Hash */],
774
+ ["f", 16 /* Interpolate.Function */], ["function", 16 /* Interpolate.Function */],
775
+ ["b", 32 /* Interpolate.Backslash */], ["backslash", 32 /* Interpolate.Backslash */],
776
+ ["qq", 63 /* Interpolate.All */], ["double", 63 /* Interpolate.All */]
777
+ ]);
778
+ // The opening of a word list that interpolates, at offset `at`: << or «
779
+ function wordListOpening(input, at) {
780
+ let open = input.peek(at), count = open == 60 /* Ch.Less */ ? 2 : 1;
781
+ if (open != 171 /* Ch.GuillemetOpen */ && !(open == 60 /* Ch.Less */ && input.peek(at + 1) == 60 /* Ch.Less */))
782
+ return null;
783
+ return { open, close: brackets$1[open], count, start: at + count, kind: "interpolating",
784
+ interpolates: 63 /* Interpolate.All */, escapes: true };
785
+ }
786
+ // The opening of a quote that starts with a quote character at offset `at`.
787
+ function quoteCharOpening(open, close, at, interpolates, escapes = true) {
788
+ return { open, close, count: 1, start: at + 1, kind: interpolates ? "interpolating" : "raw", interpolates, escapes };
789
+ }
388
790
  // The delimiters, other than brackets, that are accepted after a quote word.
389
791
  function isQuoteDelimiter(ch) {
390
792
  return ch == 47 /* Ch.Slash */ || ch == 33 /* Ch.Bang */ || ch == 124 /* Ch.Pipe */ || ch == 126 /* Ch.Tilde */ || ch == 94 /* Ch.Caret */ ||
391
793
  ch == 37 /* Ch.Percent */ || ch == 64 /* Ch.At */ || ch == 34 /* Ch.DoubleQuote */ || ch == 39 /* Ch.Apostrophe */;
392
794
  }
393
- // Reads the start of a quote at the current position: a quote character,
394
- // or a quote word (q, qq, m, s, ...) with its adverbs and its delimiter.
395
- function readOpening(input) {
396
- let next = input.next;
795
+ // Reads the start of a quote at offset `at`: a quote character, or a
796
+ // quote word (q, qq, m, s, ...) with its adverbs and its delimiter.
797
+ // The offsets in the result count from the current position.
798
+ function readOpening(input, at = 0) {
799
+ let next = input.peek(at);
397
800
  if (next == 34 /* Ch.DoubleQuote */)
398
- return { open: next, close: next, count: 1, start: 1, kind: "interpolating", escapes: true };
801
+ return quoteCharOpening(next, next, at, 63 /* Interpolate.All */);
399
802
  if (next == 39 /* Ch.Apostrophe */)
400
- return { open: next, close: next, count: 1, start: 1, kind: "raw", escapes: true };
803
+ return quoteCharOpening(next, next, at, 0);
401
804
  if (next == 0x201c /* “ */)
402
- return { open: next, close: 0x201d, count: 1, start: 1, kind: "interpolating", escapes: true };
805
+ return quoteCharOpening(next, 0x201d, at, 63 /* Interpolate.All */);
403
806
  if (next == 0x2018 /* ‘ */)
404
- return { open: next, close: 0x2019, count: 1, start: 1, kind: "raw", escapes: true };
807
+ return quoteCharOpening(next, 0x2019, at, 0);
405
808
  if (next == 0x300c /* 「 */)
406
- return { open: next, close: 0x300d, count: 1, start: 1, kind: "raw", escapes: false };
809
+ return quoteCharOpening(next, 0x300d, at, 0, false);
407
810
  if (!isAsciiLetter(next))
408
811
  return null;
409
- let pos = 1;
812
+ let pos = at + 1;
410
813
  while (isAsciiLetter(input.peek(pos)))
411
814
  pos++;
412
- if (pos > 4)
815
+ if (pos - at > 4)
413
816
  return null;
414
- let name = word(input, 0, pos), kind = quoteWords[name];
817
+ let name = word(input, at, pos), kind = quoteWords[name];
415
818
  if (!kind)
416
819
  return null;
417
- // Adverbs: :w, :to, :g, :x(2)
418
- let adverbs = false;
820
+ // Adverbs: :w, :to, :g, :x(2). Those of a regex mean other things
821
+ // than those of a quote: m:s/a b/
822
+ let adverbs = false, heredoc = false, isQuote = kind == "raw" || kind == "interpolating";
823
+ let interpolates = kind == "interpolating" ? 63 /* Interpolate.All */ : 0;
824
+ // Whether a backslash escapes the delimiter, apart from the escapes
825
+ // of :b. That comes with q and its :q adverb: Q and qq:!b have none.
826
+ let escapes = isQuote ? kind == "raw" && next == 113 /* Ch.q */ : true;
419
827
  while (input.peek(pos) == 58 /* Ch.Colon */ && (isAsciiLetter(input.peek(pos + 1)) || input.peek(pos + 1) == 33 /* Ch.Bang */)) {
420
828
  adverbs = true;
829
+ let negated = input.peek(pos + 1) == 33 /* Ch.Bang */, adverbStart = pos + (negated ? 2 : 1);
421
830
  pos += 2;
422
831
  while (isAsciiLetter(input.peek(pos)) || isDigit(input.peek(pos)))
423
832
  pos++;
833
+ let adverb = word(input, adverbStart, pos);
834
+ // An argument that is False or 0 switches the adverb off: q:c(False)
424
835
  if (input.peek(pos) == 40 /* Ch.ParenOpen */) {
836
+ let argStart = pos + 1;
425
837
  while (input.peek(pos) != 41 /* Ch.ParenClose */) {
426
838
  if (input.peek(pos) < 0 || input.peek(pos) == 10 /* Ch.Newline */)
427
839
  return null;
428
840
  pos++;
429
841
  }
842
+ if (/^\s*(False|0)\s*$/.test(word(input, argStart, pos)))
843
+ negated = true;
430
844
  pos++;
431
845
  }
846
+ if (isQuote) {
847
+ let bits = interpolationAdverbs.get(adverb);
848
+ if (bits != null)
849
+ interpolates = negated ? interpolates & ~bits : interpolates | bits;
850
+ else if (!negated && (adverb == "q" || adverb == "single"))
851
+ escapes = true;
852
+ else if (!negated && (adverb == "to" || adverb == "heredoc"))
853
+ heredoc = true;
854
+ }
432
855
  }
433
856
  if (!adverbs && continuesName(input, pos))
434
857
  return null;
435
- // The delimiter has to follow directly. Raku also takes `q {...}`, but
436
- // then `S { ... }` and `m ($x)` on a name that is not a quote word
437
- // would turn into quotes.
438
- let open = input.peek(pos), close = brackets[open];
858
+ // The delimiter follows directly, or, for q, qq and Q, after blanks
859
+ // on the same line: q {...}. Not for m, s and the others: `s { ... }`
860
+ // and `m ($x)` are more often calls of a routine of one's own.
861
+ if (isQuote && isBlank(input.peek(pos))) {
862
+ let bracket = gapEnd(input, pos);
863
+ if (brackets$1[input.peek(bracket)] != null && input.peek(bracket) != 40 /* Ch.ParenOpen */)
864
+ pos = bracket;
865
+ }
866
+ let open = input.peek(pos), close = brackets$1[open];
439
867
  if (close == null) {
440
868
  if (!isQuoteDelimiter(open))
441
869
  return null;
@@ -448,7 +876,11 @@ function readOpening(input) {
448
876
  if (close != open)
449
877
  while (input.peek(pos + count) == open)
450
878
  count++;
451
- return { open, close, count, start: pos + count, kind, escapes: name.charCodeAt(0) != 81 /* Ch.Q */ };
879
+ if (isQuote)
880
+ kind = interpolates ? "interpolating" : "raw";
881
+ if (interpolates & 32 /* Interpolate.Backslash */)
882
+ escapes = true;
883
+ return { open, close, count, start: pos + count, kind, interpolates, escapes, heredoc };
452
884
  }
453
885
  // The offset after the delimiter that closes a quote whose content
454
886
  // starts at `pos`, or -1 when it is not closed.
@@ -491,7 +923,7 @@ function quotedEnd(input, pos) {
491
923
  // The offset of the delimiter that closes a regex whose content starts
492
924
  // at `pos`, or -1. Delimiters do not count inside quotes, character
493
925
  // classes, comments and code blocks.
494
- function regexEnd(input, pos, open, close, count) {
926
+ function regexEnd(input, { start: pos, open, close, count }) {
495
927
  for (let depth = 1;;) {
496
928
  let ch = input.peek(pos);
497
929
  if (ch < 0)
@@ -539,13 +971,20 @@ function regexEnd(input, pos, open, close, count) {
539
971
  break;
540
972
  pos = more;
541
973
  }
974
+ // The bracket that closes the class is not one of the regex: rx[ <[a]> ]
975
+ pos++;
542
976
  }
543
977
  else if (ch == 35 /* Ch.Hash */) {
544
- while (input.peek(pos) >= 0 && input.peek(pos) != 10 /* Ch.Newline */)
545
- pos++;
978
+ // An embedded comment, or one that runs to the end of the line.
979
+ let end = embeddedCommentEnd(input, pos);
980
+ if (end > 0)
981
+ pos = end;
982
+ else
983
+ while (input.peek(pos) >= 0 && input.peek(pos) != 10 /* Ch.Newline */)
984
+ pos++;
546
985
  }
547
986
  else if (ch == 123 /* Ch.BraceOpen */ && close != 125 /* Ch.BraceClose */) {
548
- let end = regexEnd(input, pos + 1, 123 /* Ch.BraceOpen */, 125 /* Ch.BraceClose */, 1);
987
+ let end = regexEnd(input, { start: pos + 1, open: 123 /* Ch.BraceOpen */, close: 125 /* Ch.BraceClose */, count: 1 });
549
988
  pos = end < 0 ? pos + 1 : end + 1;
550
989
  }
551
990
  else {
@@ -555,25 +994,41 @@ function regexEnd(input, pos, open, close, count) {
555
994
  }
556
995
  // The offset after a regex or substitution, given its opening, or -1.
557
996
  function regexTokenEnd(input, opening) {
558
- let { open, close, count } = opening;
559
- let end = regexEnd(input, opening.start, open, close, count);
997
+ let end = regexEnd(input, opening);
560
998
  if (end < 0)
561
999
  return -1;
562
- end += count;
1000
+ end += opening.count;
563
1001
  // s/a/b/ has a second part. s{a} = 'b' does not.
564
- if (opening.kind == "substitution" && open == close)
1002
+ if (opening.kind != "regex" && opening.open == opening.close)
565
1003
  end = rawEnd(input, end, opening);
566
1004
  return end;
567
1005
  }
1006
+ // The opening of a bare regex: /a/
1007
+ const slashOpening = { open: 47 /* Ch.Slash */, close: 47 /* Ch.Slash */, count: 1, start: 1, kind: "regex",
1008
+ interpolates: 0, escapes: true };
1009
+ // The opening of the regex literal at the current position. The token
1010
+ // that makes a literal one token, and the parse of its inside, both
1011
+ // find its closing delimiter from this, so that they agree.
1012
+ function regexOpening(input) {
1013
+ if (input.next == 47 /* Ch.Slash */)
1014
+ return slashOpening;
1015
+ let opening = readOpening(input);
1016
+ return opening && (opening.kind == "regex" || opening.kind == "substitution") ? opening : null;
1017
+ }
568
1018
  // The offset after a word list that opens at the current position, or
569
1019
  // -1. A list that spans lines has to hold plain words, so that a stray
570
1020
  // `<` does not swallow the code up to some later `>`. `strict` is for
571
1021
  // a subscript, which has to be on one line, and cannot hold what
572
1022
  // would make it a comparison: `$a<5 && $b>3`.
1023
+ //
1024
+ // A list with double angles is rarely a stray operator, and may hold
1025
+ // code over a few lines. Not over more: one that is being typed would
1026
+ // swallow the code up to some later `>>`.
573
1027
  function wordListEnd(input, strict) {
574
1028
  let open = input.next, close = open == 171 /* Ch.GuillemetOpen */ ? 187 /* Ch.GuillemetClose */ : 62 /* Ch.Greater */;
575
1029
  let count = open == 60 /* Ch.Less */ && input.peek(1) == 60 /* Ch.Less */ ? 2 : 1;
576
- for (let pos = count, lines = 0, code = false;; pos++) {
1030
+ let codeLines = open == 171 /* Ch.GuillemetOpen */ || count == 2 ? maxCodeWordListLines : 1;
1031
+ for (let pos = count, lines = 1, code = false;; pos++) {
577
1032
  let ch = input.peek(pos);
578
1033
  if (ch == close && repeats(input, pos, close, count))
579
1034
  return pos + count;
@@ -584,7 +1039,7 @@ function wordListEnd(input, strict) {
584
1039
  else if (ch == 59 /* Ch.Semicolon */ || ch == 123 /* Ch.BraceOpen */ || ch == 125 /* Ch.BraceClose */ ||
585
1040
  ch == 40 /* Ch.ParenOpen */ || ch == 41 /* Ch.ParenClose */)
586
1041
  code = true;
587
- if (lines && (code || strict))
1042
+ if (lines > 1 && (code && lines > codeLines || strict))
588
1043
  return -1;
589
1044
  if (strict && (code || ch == 36 /* Ch.Dollar */ || ch == 64 /* Ch.At */ || ch == 37 /* Ch.Percent */ || ch == 38 /* Ch.Amp */ ||
590
1045
  ch == 124 /* Ch.Pipe */ || ch == 61 /* Ch.Equals */ || ch == 44 /* Ch.Comma */ || ch == 63 /* Ch.Question */ ||
@@ -592,19 +1047,77 @@ function wordListEnd(input, strict) {
592
1047
  return -1;
593
1048
  }
594
1049
  }
1050
+ // How many lines a <<...>> list that holds code may span.
1051
+ const maxCodeWordListLines = 10;
1052
+ // How long a <<...>> subscript can be. Whether it is closed is found
1053
+ // out from its start, and a token must not look far past its end.
1054
+ const maxDoubleSubscript = 24;
1055
+ // Whether a character can start or end the content of a <<...>>
1056
+ // subscript: part of a word, a variable or a quote. An operator
1057
+ // character cannot, which tells the hyper operators apart: @a<<+>>@b,
1058
+ // @a«R-»@b
1059
+ function isSubscriptEdge(ch) {
1060
+ return isIdentifierStart(ch) || isDigit(ch) || ch == 39 /* Ch.Apostrophe */ || ch == 34 /* Ch.DoubleQuote */;
1061
+ }
1062
+ // The offset after the <<...>> subscript at the current position, with
1063
+ // the given opening, or -1. It is short, on one line, and holds no
1064
+ // punctuation of statements.
1065
+ function doubleSubscriptEnd(input, { start, close, count }) {
1066
+ let first = input.peek(start);
1067
+ if (!isSubscriptEdge(first) && first != 36 /* Ch.Dollar */ && first != 64 /* Ch.At */ && first != 37 /* Ch.Percent */ && first != 38 /* Ch.Amp */)
1068
+ return -1;
1069
+ for (let pos = start; pos < maxDoubleSubscript; pos++) {
1070
+ let ch = input.peek(pos);
1071
+ if (ch == close && repeats(input, pos, close, count))
1072
+ return isSubscriptEdge(input.peek(pos - 1)) ? pos + count : -1;
1073
+ if (ch < 0 || ch == 10 /* Ch.Newline */ || ch == 59 /* Ch.Semicolon */ || ch == 123 /* Ch.BraceOpen */ || ch == 125 /* Ch.BraceClose */ ||
1074
+ ch == 40 /* Ch.ParenOpen */ || ch == 41 /* Ch.ParenClose */)
1075
+ return -1;
1076
+ }
1077
+ return -1;
1078
+ }
1079
+ // Whether every `{` between two offsets has its `}` there, and the
1080
+ // other way around.
1081
+ function hasBalancedBraces(input, from, to) {
1082
+ let depth = 0;
1083
+ for (let pos = from; pos < to; pos++) {
1084
+ let ch = input.peek(pos);
1085
+ if (ch == 123 /* Ch.BraceOpen */)
1086
+ depth++;
1087
+ else if (ch == 125 /* Ch.BraceClose */ && --depth < 0)
1088
+ return false;
1089
+ }
1090
+ return depth == 0;
1091
+ }
595
1092
  // A `<` subscript right after a term: %h<key>
596
1093
  function subscriptEnd(input) {
597
1094
  if (input.next != 60 /* Ch.Less */ || isSpace(input.peek(-1)) || input.peek(1) == 60 /* Ch.Less */)
598
1095
  return -1;
599
1096
  return wordListEnd(input, true);
600
1097
  }
1098
+ // The keywords that are routines or prefix operators, and can be called
1099
+ // with parentheses. `if(1)` and `my($x)` are not calls of this kind.
1100
+ const callableKeywords = /^(take|return|emit|next|last|redo|proceed|succeed|so|not)$/;
1101
+ // The words that declare what follows `multi`, `proto` or `only`.
1102
+ const declarators$1 = /^(sub|method|submethod|token|rule|regex)$/;
1103
+ // Whether a declarator starts with the given word, as `su` starts `sub`.
1104
+ function startsDeclarator(name) {
1105
+ return ["sub", "method", "submethod", "token", "rule", "regex"].some(declarator => declarator.startsWith(name));
1106
+ }
1107
+ const operatorCategory = /^(infix|prefix|postfix|circumfix|postcircumfix|term)$/;
601
1108
  function isWordOperator(name) {
602
1109
  return name == "x" || name == "xx" || name == "min" || name == "max" || name == "Z" || name == "X";
603
1110
  }
604
1111
  // The tokens that depend on whether a term or an operator is expected.
605
1112
  const termToken = new ExternalTokenizer((input, stack) => {
606
- if (!stack.canShift(Regex))
1113
+ if (!stack.canShift(regexLiteral)) {
1114
+ // At the start of the parse of the inside of a word list that
1115
+ // interpolates, its opening delimiter.
1116
+ let opening = context(stack) == startContext && stack.canShift(quoteStart) ? wordListOpening(input, 0) : null;
1117
+ if (opening)
1118
+ input.acceptToken(quoteStart, opening.start);
607
1119
  return;
1120
+ }
608
1121
  let next = input.next, { mode } = context(stack);
609
1122
  if (isIdentifierStart(next)) {
610
1123
  // The Z, X and R meta operators: Z+, X~, R-, Z=>
@@ -616,14 +1129,29 @@ const termToken = new ExternalTokenizer((input, stack) => {
616
1129
  return input.acceptToken(wordOperator, pos);
617
1130
  }
618
1131
  // A word before `=>` is a key, whatever it spells.
619
- let end = nameEnd(input, 0), after = end;
620
- while (input.peek(after) == 32 /* Ch.Space */ || input.peek(after) == 9 /* Ch.Tab */)
621
- after++;
1132
+ let end = nameEnd(input, 0), after = gapEnd(input, end);
622
1133
  if (input.peek(after) == 61 /* Ch.Equals */ && input.peek(after + 1) == 62 /* Ch.Greater */)
623
- return input.acceptToken(fatArrowKey, end);
1134
+ return input.acceptToken(plainName, end);
1135
+ // After `multi`, `proto` or `only`, a name that is not a declarator
1136
+ // is that of a sub: multi foo($x) { }
1137
+ // Only with a signature or a body right after it. A name alone may
1138
+ // be a declarator that is still being typed (`multi su`), and
1139
+ // `only` and `proto` can be routines of one's own (`only foo, 1`).
1140
+ // The signature may start on the next line, unless the name can
1141
+ // still become a declarator: `multi su` above a line with a `(`.
1142
+ if (mode == 4 /* Mode.AfterMulti */ && !(end <= 9 && declarators$1.test(word(input, 0, end)))) {
1143
+ let nameTo = categoryEnd(input, 0, end), opens = (ch) => ch == 40 /* Ch.ParenOpen */ || ch == 123 /* Ch.BraceOpen */;
1144
+ if (opens(input.peek(gapEnd(input, nameTo))) ||
1145
+ opens(input.peek(gapEnd(input, nameTo, true))) && !startsDeclarator(word(input, 0, end)))
1146
+ return input.acceptToken(multiName, nameTo);
1147
+ }
1148
+ // So is one of the keywords that are routines, when it is called
1149
+ // with parentheses: take(1). They are 2 to 7 characters long.
1150
+ if (input.peek(end) == 40 /* Ch.ParenOpen */ && end <= 7 && callableKeywords.test(word(input, 0, end)))
1151
+ return input.acceptToken(plainName, end);
624
1152
  if (mode == 1 /* Mode.AfterTerm */) {
625
1153
  // x, xx, min, max, Z and X are operators where one is expected.
626
- if (isWordOperator(word(input, 0, end)))
1154
+ if (end <= 3 && isWordOperator(word(input, 0, end)))
627
1155
  input.acceptToken(wordOperator, end);
628
1156
  return;
629
1157
  }
@@ -632,11 +1160,20 @@ const termToken = new ExternalTokenizer((input, stack) => {
632
1160
  // Squeezed between two terms, a sigil is the operator: $a%b. With a
633
1161
  // space before it, it is a variable: `my Array[Int] %h`.
634
1162
  if (mode == 1 /* Mode.AfterTerm */ && isIdentifierStart(input.peek(1)) && !isSpace(input.peek(-1)))
635
- input.acceptToken(wordOperator, 1);
1163
+ return input.acceptToken(wordOperator, 1);
1164
+ // An operator as a routine: &infix:<+>
1165
+ if (next == 38 /* Ch.Amp */) {
1166
+ let end = nameEnd(input, 1);
1167
+ if (end > 0 && operatorCategory.test(word(input, 1, end))) {
1168
+ let categoryTo = categoryEnd(input, 1, end);
1169
+ if (categoryTo > end)
1170
+ input.acceptToken(operatorVariable, categoryTo);
1171
+ }
1172
+ }
636
1173
  return;
637
1174
  }
638
1175
  else if (next == 60 /* Ch.Less */ || next == 171 /* Ch.GuillemetOpen */) {
639
- let end;
1176
+ let end, opening = wordListOpening(input, 0);
640
1177
  if (inTermPosition(input, mode, 1)) {
641
1178
  // [<] and [<=] are reductions, <-> starts a pointy block.
642
1179
  let after = input.peek(1);
@@ -644,19 +1181,31 @@ const termToken = new ExternalTokenizer((input, stack) => {
644
1181
  return;
645
1182
  end = wordListEnd(input, false);
646
1183
  }
1184
+ else if (opening && !isSpace(input.peek(-1))) {
1185
+ // A subscript with double angles, which touches its term:
1186
+ // %h«$key», :a<<b $c>>
1187
+ end = doubleSubscriptEnd(input, opening);
1188
+ }
647
1189
  else {
648
1190
  end = subscriptEnd(input);
649
1191
  }
650
- if (end > 0)
651
- input.acceptToken(rawString, end);
652
- return;
1192
+ if (end <= 0)
1193
+ return;
1194
+ // <<a $b>> and «a $b» interpolate. The list is one token here:
1195
+ // that it is closed has to be known where it starts. Its inside
1196
+ // is parsed again, piece by piece (see index.ts), by rules that
1197
+ // count nested delimiters, escapes and the braces of blocks. A
1198
+ // list that those rules would end elsewhere is not read inside.
1199
+ let interpolates = opening && rawEnd(input, opening.start, opening) == end &&
1200
+ hasBalancedBraces(input, opening.start, end);
1201
+ return input.acceptToken(interpolates ? interpolatingWordList : rawString, end);
653
1202
  }
654
1203
  else if (next == 47 /* Ch.Slash */) {
655
1204
  if (input.peek(1) == 47 /* Ch.Slash */ || !inTermPosition(input, mode, 1))
656
1205
  return;
657
- let end = regexEnd(input, 1, 47 /* Ch.Slash */, 47 /* Ch.Slash */, 1);
1206
+ let end = regexTokenEnd(input, slashOpening);
658
1207
  if (end > 0)
659
- input.acceptToken(Regex, end + 1);
1208
+ input.acceptToken(regexLiteral, end);
660
1209
  return;
661
1210
  }
662
1211
  let opening = readOpening(input);
@@ -664,9 +1213,15 @@ const termToken = new ExternalTokenizer((input, stack) => {
664
1213
  return;
665
1214
  if (opening.kind == "interpolating")
666
1215
  return input.acceptToken(quoteStart, opening.start);
667
- let end = opening.kind == "raw" ? rawEnd(input, opening.start, opening) : regexTokenEnd(input, opening);
1216
+ if (opening.kind != "raw") {
1217
+ let end = regexTokenEnd(input, opening);
1218
+ if (end > 0)
1219
+ input.acceptToken(opening.kind == "transliteration" ? transliteration : regexLiteral, end);
1220
+ return;
1221
+ }
1222
+ let end = rawEnd(input, opening.start, opening);
668
1223
  if (end > 0)
669
- input.acceptToken(opening.kind == "raw" ? rawString : Regex, end);
1224
+ input.acceptToken(rawString, end);
670
1225
  });
671
1226
  // Whether a sigil other than `$` interpolates: only when a subscript or
672
1227
  // a call follows its name. `end` is the offset after the name.
@@ -686,7 +1241,7 @@ const quoteToken = new ExternalTokenizer((input, stack) => {
686
1241
  let { quote, mode } = context(stack);
687
1242
  if (!quote)
688
1243
  return;
689
- let { open, close, count } = quote, nests = open != close;
1244
+ let { open, close, count, interpolates } = quote, nests = open != close;
690
1245
  // After a variable, a subscript or a call continues the interpolation.
691
1246
  // `[` and `(` are left to the grammar's own tokens.
692
1247
  if (mode == 1 /* Mode.AfterTerm */) {
@@ -697,6 +1252,9 @@ const quoteToken = new ExternalTokenizer((input, stack) => {
697
1252
  return input.acceptToken(rawString, end);
698
1253
  }
699
1254
  let start = input.pos;
1255
+ // The text of a heredoc ends where the input does.
1256
+ if (input.next < 0 && quote == heredocQuote)
1257
+ return input.acceptToken(quoteEnd);
700
1258
  for (;;) {
701
1259
  let next = input.next;
702
1260
  if (next < 0)
@@ -708,9 +1266,24 @@ const quoteToken = new ExternalTokenizer((input, stack) => {
708
1266
  input.advance(count);
709
1267
  return input.acceptToken(!atClose ? quoteNestOpen : quote.depth ? quoteNestClose : quoteEnd);
710
1268
  }
711
- if (next == 92 /* Ch.Backslash */ || next == 36 /* Ch.Dollar */ || next == 123 /* Ch.BraceOpen */)
712
- break;
713
- if (next == 64 /* Ch.At */ || next == 37 /* Ch.Percent */ || next == 38 /* Ch.Amp */) {
1269
+ if (next == 92 /* Ch.Backslash */) {
1270
+ if (interpolates & 32 /* Interpolate.Backslash */)
1271
+ break;
1272
+ // Still keeps a delimiter after it from closing the quote.
1273
+ if (quote.escapes && input.peek(1) >= 0)
1274
+ input.advance();
1275
+ }
1276
+ else if (next == 36 /* Ch.Dollar */) {
1277
+ if (interpolates & 2 /* Interpolate.Scalar */)
1278
+ break;
1279
+ }
1280
+ else if (next == 123 /* Ch.BraceOpen */) {
1281
+ if (interpolates & 1 /* Interpolate.Closure */)
1282
+ break;
1283
+ }
1284
+ else if (next == 64 /* Ch.At */ && interpolates & 4 /* Interpolate.Array */ ||
1285
+ next == 37 /* Ch.Percent */ && interpolates & 8 /* Interpolate.Hash */ ||
1286
+ next == 38 /* Ch.Amp */ && interpolates & 16 /* Interpolate.Function */) {
714
1287
  let end = nameEnd(input, 1);
715
1288
  if (end > 0 && hasPostfix(input, end))
716
1289
  break;
@@ -723,37 +1296,27 @@ const quoteToken = new ExternalTokenizer((input, stack) => {
723
1296
  // The text of a heredoc: the lines after the one that holds its opener,
724
1297
  // up to the line that holds only its terminator. As a skipped token, it
725
1298
  // leaves the rest of the opener's line to be parsed as usual. The
726
- // opener is looked up in the text of that line rather than kept in the
727
- // context, so that it is also found when the line's nodes were reused
728
- // from an earlier parse.
729
- const heredocToken = new ExternalTokenizer(input => {
1299
+ // context holds the terminators of the heredocs that the line opened.
1300
+ //
1301
+ // The text is one token, also when it interpolates: that it has a
1302
+ // terminator has to be known where it starts. A text in which
1303
+ // everything interpolates is a token of its own kind, the inside of
1304
+ // which is parsed again (see index.ts), from an empty token that puts
1305
+ // the quote in the context. When a line opens several heredocs, their
1306
+ // texts are one token, which interpolates if all of them do.
1307
+ const heredocToken = new ExternalTokenizer((input, stack) => {
1308
+ if (context(stack) == startContext && stack.canShift(heredocStart))
1309
+ return input.acceptToken(heredocStart);
1310
+ let { heredocs } = context(stack);
1311
+ if (!heredocs.length)
1312
+ return;
730
1313
  let pos = blanksEnd(input, 0);
731
1314
  if (input.peek(pos) != 10 /* Ch.Newline */)
732
1315
  return;
733
- // Read the line that just ended, and look for heredoc openers in it.
734
- let lineStart = 0;
735
- while (lineStart > -500 && input.peek(lineStart - 1) >= 0 && input.peek(lineStart - 1) != 10 /* Ch.Newline */)
736
- lineStart--;
737
- let line = word(input, lineStart, 0);
738
- if (line.indexOf(":to") < 0 && line.indexOf(":heredoc") < 0 || /^\s*#/.test(line))
739
- return;
740
- let opener = /(?:^|[^\w$@%&.'-])[qQ]\w*(?::\w+)*:(?:to|heredoc)(?::\w+)*\s*(\S)/g, found;
741
- let terminators = [];
742
- while (found = opener.exec(line)) {
743
- let open = found[1], close = String.fromCharCode(brackets[open.charCodeAt(0)] || open.charCodeAt(0));
744
- let end = line.indexOf(close, opener.lastIndex);
745
- if (end < 0)
746
- continue;
747
- terminators.push(line.slice(opener.lastIndex, end).trim());
748
- opener.lastIndex = end + 1;
749
- }
750
- if (!terminators.length)
751
- return;
752
1316
  input.advance(pos);
753
- for (let terminator of terminators) {
1317
+ for (let { terminator } of heredocs) {
754
1318
  for (;;) {
755
- // Without its terminator, this is not taken to be a heredoc. The
756
- // opener is found by its text, and may be in a comment or a string.
1319
+ // Without its terminator, this is not taken to be a heredoc.
757
1320
  if (input.next < 0)
758
1321
  return;
759
1322
  input.advance();
@@ -764,22 +1327,218 @@ const heredocToken = new ExternalTokenizer(input => {
764
1327
  skipLine(input);
765
1328
  }
766
1329
  }
767
- input.acceptToken(Heredoc);
1330
+ input.acceptToken(heredocs.every(heredoc => heredoc.interpolates) ? interpolatingHeredoc : rawHeredoc);
768
1331
  });
769
- // The body of a token, rule or regex declaration, up to its closing brace.
770
- const regexBodyToken = new ExternalTokenizer((input, stack) => {
771
- if (!stack.canShift(regexBody))
1332
+ const regexDeclarator = /^(token|rule|regex)$/;
1333
+ const regexDeclaratorPrefix = /^(proto|multi|my|our)$/;
1334
+ // Whether the line that starts at offset `pos` starts a regex
1335
+ // declaration: a declarator, a name, and further on a brace, as in
1336
+ // `token name {`. In a regex, the words alone can be literals:
1337
+ // `token | rule`
1338
+ function startsRegexDeclaration(input, pos) {
1339
+ let start = blanksEnd(input, pos), end = nameEnd(input, start);
1340
+ // proto token, my regex
1341
+ if (end > 0 && isBlank(input.peek(end)) && regexDeclaratorPrefix.test(word(input, start, end))) {
1342
+ start = blanksEnd(input, end);
1343
+ end = nameEnd(input, start);
1344
+ }
1345
+ if (end < 0 || end - start > 5 || !isBlank(input.peek(end)) || !regexDeclarator.test(word(input, start, end)))
1346
+ return false;
1347
+ let name = blanksEnd(input, end), after = nameEnd(input, name);
1348
+ if (after < 0)
1349
+ return false;
1350
+ for (after = categoryEnd(input, name, after);; after++) {
1351
+ let next = input.peek(after);
1352
+ if (next == 123 /* Ch.BraceOpen */)
1353
+ return true;
1354
+ if (next < 0 || next == 10 /* Ch.Newline */)
1355
+ return false;
1356
+ }
1357
+ }
1358
+ // The characters that quantify, combine and anchor in a regex.
1359
+ function isRegexOperator(ch) {
1360
+ 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 */ ||
1361
+ ch == 126 /* Ch.Tilde */ || ch == 37 /* Ch.Percent */;
1362
+ }
1363
+ // The offset after the `<...>` at the current position in a regex.
1364
+ // When it is not closed on its line, this is the negated offset of the
1365
+ // end of the text that was gone over to find that out.
1366
+ function assertionEnd(input) {
1367
+ for (let pos = 1, depth = 1;;) {
1368
+ let ch = input.peek(pos);
1369
+ if (ch < 0 || ch == 10 /* Ch.Newline */)
1370
+ return -pos;
1371
+ if (ch == 92 /* Ch.Backslash */) {
1372
+ pos += input.peek(pos + 1) == 10 /* Ch.Newline */ || input.peek(pos + 1) < 0 ? 1 : 2;
1373
+ }
1374
+ else if (ch == 39 /* Ch.Apostrophe */ || ch == 34 /* Ch.DoubleQuote */) {
1375
+ let end = quotedEnd(input, pos);
1376
+ pos = end < 0 ? pos + 1 : end;
1377
+ }
1378
+ else if (ch == 91 /* Ch.BracketOpen */) {
1379
+ // The brackets of a character class can hold a `>` and a `}`,
1380
+ // and can span lines.
1381
+ let end = pos + 1;
1382
+ while (input.peek(end) != 93 /* Ch.BracketClose */ && input.peek(end) >= 0)
1383
+ end += input.peek(end) == 92 /* Ch.Backslash */ ? 2 : 1;
1384
+ pos = input.peek(end) == 93 /* Ch.BracketClose */ ? end + 1 : pos + 1;
1385
+ }
1386
+ else if (ch == 123 /* Ch.BraceOpen */) {
1387
+ // So can a block of code: <?{ $x > 1 }>
1388
+ let end = pos + 1;
1389
+ for (let braces = 1; braces > 0; end++) {
1390
+ let inner = input.peek(end);
1391
+ if (inner < 0 || inner == 10 /* Ch.Newline */)
1392
+ return -pos;
1393
+ if (inner == 123 /* Ch.BraceOpen */)
1394
+ braces++;
1395
+ else if (inner == 125 /* Ch.BraceClose */)
1396
+ braces--;
1397
+ }
1398
+ pos = end;
1399
+ }
1400
+ else if (ch == 60 /* Ch.Less */) {
1401
+ depth++;
1402
+ pos++;
1403
+ }
1404
+ else if (ch == 62 /* Ch.Greater */) {
1405
+ pos++;
1406
+ if (--depth == 0)
1407
+ return pos;
1408
+ }
1409
+ else {
1410
+ pos++;
1411
+ }
1412
+ }
1413
+ }
1414
+ // The pieces of the body of a token, rule or regex declaration. The
1415
+ // context tells whether the position is in such a body, and whether it
1416
+ // is in a block of code there, which is left to the other tokenizers.
1417
+ const regexToken = new ExternalTokenizer((input, stack) => {
1418
+ let current = context(stack), { regex } = current, next = input.next;
1419
+ if (!regex || regex.braces > 0) {
1420
+ // At the start of the parse of the inside of a regex literal, the
1421
+ // opening delimiter: `/`, or a quote word with its adverbs: rx:i/
1422
+ if (current == startContext && stack.canShift(regexLitOpen)) {
1423
+ let opening = regexOpening(input);
1424
+ if (opening)
1425
+ input.acceptToken(regexLitOpen, opening.start);
1426
+ return;
1427
+ }
1428
+ // Outside of a regex, or in a block of code in one, where another
1429
+ // regex can be declared. An empty body, `{}`, has no regex.
1430
+ if (next >= 0 && next != 125 /* Ch.BraceClose */ && stack.canShift(regexStart))
1431
+ input.acceptToken(regexStart);
772
1432
  return;
773
- let end = regexEnd(input, 0, 123 /* Ch.BraceOpen */, 125 /* Ch.BraceClose */, 1);
774
- if (end < 0) {
775
- // Not closed: the rest of the input.
776
- skipToEnd(input);
777
- if (input.pos > stack.pos)
778
- input.acceptToken(regexBody);
779
1433
  }
780
- else if (end > 0) {
781
- input.acceptToken(regexBody, end);
1434
+ let { literalEnd } = regex;
1435
+ if (literalEnd >= 0) {
1436
+ // A literal ends at its closing delimiter. The rest of what is
1437
+ // parsed, which for s/a/b/ includes the replacement, is one token.
1438
+ if (next < 0 || input.pos >= literalEnd) {
1439
+ skipToEnd(input);
1440
+ return input.acceptToken(regexLitClose);
1441
+ }
1442
+ }
1443
+ else if (next < 0 || next == 125 /* Ch.BraceClose */ || next == 10 /* Ch.Newline */ && startsRegexDeclaration(input, 1)) {
1444
+ // The body ends at its closing brace. One that is not closed ends
1445
+ // before the next regex declaration, which a body does not hold, so
1446
+ // that the declarations after it are still found.
1447
+ return input.acceptToken(regexBodyEnd);
782
1448
  }
1449
+ // A block of code, and an escape, are tokens of the grammar. An
1450
+ // escape does not take a line break, where the check above is made.
1451
+ if (next == 123 /* Ch.BraceOpen */)
1452
+ return;
1453
+ if (next == 92 /* Ch.Backslash */ && input.peek(1) >= 0 && input.peek(1) != 10 /* Ch.Newline */)
1454
+ return;
1455
+ // How far a piece of a literal can go: up to its closing delimiter.
1456
+ // The pieces are read by other rules than the ones that found that
1457
+ // delimiter, and where the two disagree, the delimiter wins. A piece
1458
+ // that would go over it is text.
1459
+ let limit = literalEnd < 0 ? Infinity : literalEnd - input.pos;
1460
+ if (next == 35 /* Ch.Hash */) {
1461
+ // An embedded comment, #`( ... ), or one that runs to the end of the line.
1462
+ let end = embeddedCommentEnd(input, 0);
1463
+ if (end > 0)
1464
+ return input.acceptToken(regexBlockComment, end);
1465
+ skipLine(input);
1466
+ return input.acceptToken(regexComment);
1467
+ }
1468
+ if (next == 39 /* Ch.Apostrophe */ || next == 34 /* Ch.DoubleQuote */) {
1469
+ let end = quotedEnd(input, 0);
1470
+ if (end > 0 && end <= limit)
1471
+ return input.acceptToken(regexQuote, end);
1472
+ }
1473
+ else if (next == 60 /* Ch.Less */ || next == 62 /* Ch.Greater */ || next == 171 /* Ch.GuillemetOpen */ || next == 187 /* Ch.GuillemetClose */ ||
1474
+ next == 41 /* Ch.ParenClose */ && input.peek(1) == 62 /* Ch.Greater */) {
1475
+ // Word boundaries and capture markers stand alone: << >> « » <( )>
1476
+ let after = input.peek(1);
1477
+ if (next != 60 /* Ch.Less */ || after == 60 /* Ch.Less */ || after == 40 /* Ch.ParenOpen */) {
1478
+ let size = next == 171 /* Ch.GuillemetOpen */ || next == 187 /* Ch.GuillemetClose */ ? 1 : 2;
1479
+ if (next != 62 /* Ch.Greater */ || after == 62 /* Ch.Greater */)
1480
+ return input.acceptToken(regexOperator, size);
1481
+ }
1482
+ else {
1483
+ let end = assertionEnd(input);
1484
+ if (end > limit)
1485
+ return input.acceptToken(regexText, 1);
1486
+ if (end > 0) {
1487
+ // <[a..z]>, <-[a]>, <+alpha-[b]>, <?[c]>
1488
+ let kind = after == 63 /* Ch.Question */ || after == 33 /* Ch.Bang */ ? input.peek(2) : after;
1489
+ let isClass = kind == 91 /* Ch.BracketOpen */ || kind == 45 /* Ch.Hyphen */ || kind == 43 /* Ch.Plus */;
1490
+ return input.acceptToken(isClass ? CharacterClass : Assertion, end);
1491
+ }
1492
+ // What is not closed is text, up to the brace that closes the
1493
+ // body if there is one on the line.
1494
+ let stop = 1;
1495
+ while (stop < -end && input.peek(stop) != 125 /* Ch.BraceClose */)
1496
+ stop++;
1497
+ return input.acceptToken(regexText, stop);
1498
+ }
1499
+ }
1500
+ else if (next == 36 /* Ch.Dollar */ || next == 64 /* Ch.At */) {
1501
+ let after = input.peek(1);
1502
+ // A named capture: $<name>
1503
+ if (next == 36 /* Ch.Dollar */ && after == 60 /* Ch.Less */) {
1504
+ let end = nameEnd(input, 2);
1505
+ if (end > 0 && input.peek(end) == 62 /* Ch.Greater */)
1506
+ return input.acceptToken(regexCapture, end + 1);
1507
+ }
1508
+ // A variable is a token of the grammar: $x, $0, $/, @list, $!attr, $*dynamic
1509
+ // Not the `$` before the delimiter that closes a literal: /a$/
1510
+ if (limit != 1) {
1511
+ if (isIdentifierStart(after) || next == 36 /* Ch.Dollar */ && (isDigit(after) || after == 47 /* Ch.Slash */))
1512
+ return;
1513
+ if ((after == 33 /* Ch.Bang */ || after == 46 /* Ch.Dot */ || after == 42 /* Ch.Star */ || after == 63 /* Ch.Question */) &&
1514
+ isIdentifierStart(input.peek(2)))
1515
+ return;
1516
+ }
1517
+ // Otherwise `$` and `$$` anchor.
1518
+ if (next == 36 /* Ch.Dollar */) {
1519
+ while (input.next == 36 /* Ch.Dollar */ && input.pos != literalEnd)
1520
+ input.advance();
1521
+ return input.acceptToken(regexOperator);
1522
+ }
1523
+ }
1524
+ else if (isRegexOperator(next)) {
1525
+ while (isRegexOperator(input.next) && input.pos != literalEnd)
1526
+ input.advance();
1527
+ return input.acceptToken(regexOperator);
1528
+ }
1529
+ // Text, up to where one of the above may apply.
1530
+ for (;;) {
1531
+ input.advance();
1532
+ let ch = input.next;
1533
+ if (ch < 0 || input.pos == literalEnd ||
1534
+ ch == 10 /* Ch.Newline */ || ch == 92 /* Ch.Backslash */ || ch == 39 /* Ch.Apostrophe */ || ch == 34 /* Ch.DoubleQuote */ ||
1535
+ ch == 60 /* Ch.Less */ || ch == 62 /* Ch.Greater */ || ch == 171 /* Ch.GuillemetOpen */ || ch == 187 /* Ch.GuillemetClose */ ||
1536
+ ch == 41 /* Ch.ParenClose */ && input.peek(1) == 62 /* Ch.Greater */ ||
1537
+ ch == 123 /* Ch.BraceOpen */ || ch == 125 /* Ch.BraceClose */ || ch == 35 /* Ch.Hash */ || ch == 36 /* Ch.Dollar */ ||
1538
+ ch == 64 /* Ch.At */ || isRegexOperator(ch))
1539
+ break;
1540
+ }
1541
+ input.acceptToken(regexText);
783
1542
  });
784
1543
  // ---- Names ----
785
1544
  // The dot of a method call, with its optional `?`, `+`, `*`, `^` or `&`
@@ -800,20 +1559,32 @@ const methodDotToken = new ExternalTokenizer((input, stack) => {
800
1559
  return;
801
1560
  input.acceptToken(methodDot, size);
802
1561
  });
803
- // The offset after an operator name's `:<...>` part, as in infix:<+>
804
- // and term:sym<x>, or `pos` when there is none there.
805
- function categoryEnd(input, pos) {
1562
+ // The offset after an operator name's `:<...>` part, as in infix:<+>,
1563
+ // circumfix:<[ ]>, infix:['+'] and term:sym<x>, or `pos` when there is
1564
+ // none there. The name before it runs from `nameStart` to `pos`.
1565
+ function categoryEnd(input, nameStart, pos) {
806
1566
  if (input.peek(pos) != 58 /* Ch.Colon */)
807
1567
  return pos;
808
1568
  let open = pos + 1;
809
1569
  if (word(input, open, open + 3) == "sym")
810
1570
  open += 3;
811
- let close = input.peek(open) == 60 /* Ch.Less */ ? 62 /* Ch.Greater */ : input.peek(open) == 171 /* Ch.GuillemetOpen */ ? 187 /* Ch.GuillemetClose */ : -1;
1571
+ let first = input.peek(open);
1572
+ if (first == 91 /* Ch.BracketOpen */) {
1573
+ // Only a quoted operator: ['+']
1574
+ let quote = input.peek(open + 1);
1575
+ if (quote != 39 /* Ch.Apostrophe */ && quote != 34 /* Ch.DoubleQuote */)
1576
+ return pos;
1577
+ let end = quotedEnd(input, open + 1);
1578
+ return end > 0 && input.peek(end) == 93 /* Ch.BracketClose */ ? end + 1 : pos;
1579
+ }
1580
+ let close = first == 60 /* Ch.Less */ ? 62 /* Ch.Greater */ : first == 171 /* Ch.GuillemetOpen */ ? 187 /* Ch.GuillemetClose */ : -1;
812
1581
  if (close < 0)
813
1582
  return pos;
1583
+ // The two halves of a circumfix are separated by one space.
1584
+ let spaces = /circumfix$/.test(word(input, nameStart, pos)) ? 1 : 0;
814
1585
  for (let i = open + 1;; i++) {
815
1586
  let ch = input.peek(i);
816
- if (ch < 0 || isSpace(ch))
1587
+ if (ch < 0 || isSpace(ch) && (ch != 32 /* Ch.Space */ || i == open + 1 || --spaces < 0))
817
1588
  return pos;
818
1589
  // The operator itself can be `>`, so the last closer before a break counts.
819
1590
  if (ch == close && !(input.peek(i + 1) == close))
@@ -834,7 +1605,7 @@ const nameToken = new ExternalTokenizer((input, stack) => {
834
1605
  input.acceptToken(MethodName, end);
835
1606
  return;
836
1607
  }
837
- end = categoryEnd(input, end);
1608
+ end = categoryEnd(input, mark, end);
838
1609
  if (!mark && stack.canShift(declaredName))
839
1610
  input.acceptToken(declaredName, end);
840
1611
  else if (stack.canShift(declaredMethodName))
@@ -842,12 +1613,20 @@ const nameToken = new ExternalTokenizer((input, stack) => {
842
1613
  });
843
1614
  // The type smiley in Int:D, Str:U and Any:_. It has to touch the type
844
1615
  // name, and must not be the start of a longer pair such as :Default.
1616
+ //
1617
+ // A type name without a smiley ends in an empty token. Without it, the
1618
+ // parser has to read the token after the name to know that the name is
1619
+ // complete, and when a long comment is in between, it records that as
1620
+ // a lookahead, which keeps the tokens before it from being reused.
845
1621
  const smileyToken = new ExternalTokenizer((input, stack) => {
846
- if (input.next != 58 /* Ch.Colon */ || !stack.canShift(smiley) || !continuesName(input, -1))
1622
+ if (!stack.canShift(noSmiley))
847
1623
  return;
848
- let kind = input.peek(1);
849
- if ((kind == 68 /* Ch.D */ || kind == 85 /* Ch.U */ || kind == 95 /* Ch.Underscore */) && !continuesName(input, 2))
850
- input.acceptToken(smiley, 2);
1624
+ if (input.next == 58 /* Ch.Colon */) {
1625
+ let kind = input.peek(1);
1626
+ if ((kind == 68 /* Ch.D */ || kind == 85 /* Ch.U */ || kind == 95 /* Ch.Underscore */) && !continuesName(input, 2))
1627
+ return input.acceptToken(smiley, 2);
1628
+ }
1629
+ input.acceptToken(noSmiley, 0);
851
1630
  });
852
1631
  // v6, v6.d, v1.2.3, v1.2+, v1.*. A name such as v8-engine is not a version.
853
1632
  const versionToken = new ExternalTokenizer((input, stack) => {
@@ -882,27 +1661,27 @@ const versionToken = new ExternalTokenizer((input, stack) => {
882
1661
  });
883
1662
 
884
1663
  // This file was generated by lezer-generator. You probably shouldn't edit it.
885
- 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};
886
- 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};
1664
+ 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:170, our:172, has:174, state:176, temp:178, let:180, anon:182, augment:184, supersede:186, unit:188, multi:190, proto:192, only:194, if:196, elsif:198, else:200, unless:202, with:204, orwith:206, without:208, for:210, while:212, until:214, loop:216, repeat:218, given:220, when:222, default:224, do:226, gather:228, take:230, try:232, return:234, next:236, last:238, redo:240, proceed:242, succeed:244, react:246, whenever:248, supply:250, emit:252, start:254, use:256, need:258, import:260, require:262, no:264, is:266, does:268, of:270, returns:272, handles:274, where:276, will:278, trusts:280, hides:282, and:284, or:286, not:288, xor:290, so:292, andthen:294, orelse:296, notandthen:298, div:300, mod:302, gcd:304, lcm:306, eq:308, ne:310, lt:312, gt:314, le:316, ge:318, cmp:320, leg:322, eqv:324, but:326, self:328};
1665
+ const spec_capIdentifier = {__proto__:null,True:330, False:332, Nil:334, BEGIN:336, CHECK:338, INIT:340, END:342, ENTER:344, LEAVE:346, KEEP:348, UNDO:350, FIRST:352, NEXT:354, LAST:356, PRE:358, POST:360, CATCH:362, CONTROL:364, QUIT:366, CLOSE:368, DOC:370};
887
1666
  const parser$1 = LRParser.deserialize({
888
1667
  version: 14,
889
- 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",
890
- 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~",
891
- 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",
892
- 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 , : .",
893
- maxTerm: 190,
1668
+ states: ".WQfQDdOOO*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'#EQO7SO!0jO'#EQOOQ.W'#HS'#HSOOQ.W'#HR'#HROOQ.W'#Ge'#GeQfQDdOOO7ZQEUO'#DTO7bQEUO'#D`O7iQEUO'#D`OARQEUO'#DgOA]QEUO'#DsOKQQEUO'#DvO!&uQEUO'#DyOOQ.W'#HZ'#HZO!0jO!!^OOO!1hO!0jOOOOOS'#Gf'#GfO!1oOSO,59gOOQ.W,59g,59gOOQ.W,59k,59kO!1yQDdO,59kOOQ.W,59n,59nO!2QQDdO,59nOOQ.W,59x,59xO!2XQDdO,59xOOQ.W'#Gg'#GgO!;qQDdO'#HUOOQ.W'#HU'#HUOOQ.W,59z,59zOOQ.W,5:h,5:hOOQ.W,5:j,5:jO!;xQDdO'#EROOO2Q'#HX'#HXOOO2Q'#Gi'#GiO!<PO!0jO,5:lOOQ.W,5:l,5:lOOQ.W-E:c-E:cOOQ.W'#DZ'#DZOOQ.W,59o,59oO,fQDdO,59oOOQ.W'#Db'#DbO,fQDdO,59zOOQ.W'#De'#DeO!<WQDdO,5:ROOQ.W'#Dk'#DkO!<_Q:[O'#DlOOQ.W,5:R,5:RO!<WQDdO,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!<gQDdO'#DrOOO#t'#HV'#HVOOO#t'#Gh'#GhO!0jO!!^OOQOOOOOO!1hO!0jOOOOOS-E:d-E:dOOQ.W1G/R1G/ROOQ.W1G/V1G/VOOQ.W1G/Y1G/YOOQ.W1G/d1G/dOOQ.W-E:e-E:eOOQ.W,5=p,5=pOOO2Q,5:m,5:mO!<nQDdO,5:mOOO2Q-E:g-E:gOOQ.W1G0W1G0WOOQ.W1G/Z1G/ZOOQ.W1G/f1G/fOOQ.W1G/m1G/mO!=pO!!^O'#DmOOQ.W,5:W,5:WO!=wQ7fO,5:WO!=|QDdO1G/mOOO#t,5:^,5:^O!>TQDdO,5:^OOO#t-E:f-E:fOOO2Q1G0X1G0XO!>[O!!^O,5:XOOQY,5:X,5:XOOQ.W1G/r1G/rOOQ.W7+%X7+%XOOO#t1G/x1G/xOOQY1G/s1G/s",
1669
+ stateData: "!>o~O%tOSlOSmOSPOSZOS[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!v_O!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$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%Q_O%R_O%S_O%T_O%o]O%p^O%qZO%rWO%wZO%z[O%|_O~O%hkO~O%clO~O%plO~OSmOTmOUmOVmOWmOXmOYmO%bmO~ORoO%aoO~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!v_O!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$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%Q_O%R_O%S_O%T_O%o]O%p^O%qZO%rWO%wZO%z[O%|_O~O^zO~O%_{O%`{O~O]YOrTOuUO!P|O!b}O!c}O!d}O!e}O%k}O%l}O%m}O%o}O%|}O~O%n!QO~P6XO%d!SO~P,fO%d!VO~P,fO%e!XO~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!v_O!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$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%Q_O%R_O%S_O%T_O%o]O%p^O%qZO%rWO%wZO%z[O%|_O~O!R!]O%d!ZO~P7pO%d!_OQ!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!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$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%^!gX%o!gX%p!gX%q!gX%r!gX%w!gX%z!gX%|!gXq!gXt!gX!O!gX~O%d!aOQ!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!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$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%^!jX%o!jX%p!jX%q!jX%r!jX%w!jX%z!jX%|!jXq!jXt!jX!O!jX~O%d!cOQ!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!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$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%^!mX%o!mX%p!mX%q!mX%r!mX%w!mX%z!mX%|!mXq!mXt!mX!O!mX~O`!fOa!fOb!fOc!fOd!fOe!fOf!fO!P!eO!b!fO!c!fO!d!fO!e!fO%i!iO%j!fO~O%n!iO~P6XOR!lO%a!lO~P+ZOq!mO~PfOt!nO~PfO!O!oO~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!v_O!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$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%Q_O%R_O%S_O%T_O%o]O%p^O%qZO%rWO%wZO%z[O%|_O~O!R!qO~P!2`O!O!rO~PfO%n!uO~P6XO!R!xO~P7pO!O!zO%f!yO~O!O!}O~PfO!O#QO~PfO`!fOa!fOb!fOc!fOd!fOe!fOf!fO!P!eO!b!fO!c!fO!d!fO!e!fO%j!fO~O%g#SO~P!<uO!O#TO~O!R#UO~P7pO!O#VO~PfO%g#WO~P!<uOml!d!e!cp!v%Q%|%S%T~",
1670
+ goto: ",X&OPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP&PPPP&jPP&j&PPPPPP'ZPP'^P&PP'wPP'z'}&PPPP(h(k(uPPPP(x&PP)O&PP)R&PP)U&j&P&PP&P'TPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP)X)s)y*^*hPPPPPPPPPPPPPPPPPPPPPPP*r+QP+m+{P,RP&P!P_OTUVXbcdefqsuw|!U!W!Y!^!e!s!|#O!O_OTUVXbcdefqsuw|!U!W!Y!^!e!s!|#OX}^l!P!jR!Uch`OTUVbqsu|!e!s#O[xXcde!U!WR!qwR!WdR!We!PXOTUVXbcdefqsuw|!U!W!Y!^!e!s!|#OR!^fQ!]fS!x!Y!^R#U!|R!{![X!fk!h!y#RR!`gR!bhR!diQbOQqTQsUQuV[!Rbqsu!s#OQ!s|R#O!eQnSR!kn[wXcde!U!WQ!YfU!pw!Y!|R!|!^Q!hkS#P!h#RR#R!yQ!P^Q!jlT!t!P!jiaOTUVbqsu|!e!s#Oh`OTUVbqsu|!e!s#OgvXcdefw!U!W!Y!^!|UyXdeQ!TcQ!v!UR!w!WX!gk!h!y#RX!O^l!P!j",
1671
+ 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 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",
1672
+ maxTerm: 229,
894
1673
  context: trackContext,
895
1674
  nodeProps: [
896
- ["openedBy", 14,"(",17,"[",27,"{"],
897
- ["closedBy", 15,")",18,"]",28,"}"]
1675
+ ["openedBy", 33,"(",36,"[",46,"{"],
1676
+ ["closedBy", 34,")",37,"]",47,"}"]
898
1677
  ],
899
- skippedNodes: [0,1,2,3,10,11],
900
- repeatNodeCount: 3,
901
- 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~",
902
- tokenizers: [blockComment, podToken, heredocToken, methodDotToken, nameToken, smileyToken, versionToken, regexBodyToken, quoteToken, termToken, 0, 1],
903
- topRules: {"Program":[0,12]},
904
- specialized: [{term: 186, get: (value) => spec_identifier[value] || -1},{term: 188, get: (value) => spec_capIdentifier[value] || -1}],
905
- tokenPrec: 3609
1678
+ skippedNodes: [0,1,11,12,28,29],
1679
+ repeatNodeCount: 5,
1680
+ 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%t~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%QPqr%{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%|~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%QPqr-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%QP!_!`7QP7VO%QP~7[a%QP!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%QPqr-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%R~~:lQ%T~!O!P:r!_!`7QP:wQ%QP!O!P:}#Q#R7QP;SP%QP#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%S~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!v~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%|~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%z~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%QPOqMdqrMirzMdz{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%QPqr%{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%w~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~",
1681
+ tokenizers: [smileyToken, blockComment, podToken, heredocToken, methodDotToken, nameToken, versionToken, regexToken, quoteToken, termToken, 0, 1],
1682
+ topRules: {"Program":[0,30],"Regex":[1,190],"Heredoc":[2,191],"StringLiteral":[3,192]},
1683
+ specialized: [{term: 223, get: (value) => spec_identifier[value] || -1},{term: 226, get: (value) => spec_capIdentifier[value] || -1}],
1684
+ tokenPrec: 3469
906
1685
  });
907
1686
 
908
1687
  // The keywords of the grammar, with the tag each is highlighted with.
@@ -921,6 +1700,279 @@ const keywordTags = {
921
1700
  self: tags.self
922
1701
  };
923
1702
 
1703
+ const scopes = new NodeWeakMap();
1704
+ const scopeNodes = /^(Block|Interpolation|Program)$/;
1705
+ const variableNodes = /^(VariableName|AttributeName)$/;
1706
+ const declarators = /^(my|our|has|state)$/;
1707
+ /// Whether a variable is a placeholder, which is declared by being
1708
+ /// used: $^a, and the named $:a
1709
+ function isPlaceholder(name) { return /^[$@%&][\^:]/.test(name); }
1710
+ // The name of the variable without a sigil that the backslash `node`
1711
+ // comes right before, as in `\x`. A capitalized one reads as a type.
1712
+ function sigillessName(state, node) {
1713
+ let next = node.nextSibling;
1714
+ return node.name == "Operator" && next && (next.name == "Identifier" || next.name == "TypeName") &&
1715
+ next.from == node.to && state.sliceDoc(node.from, node.to) == "\\" ? next : null;
1716
+ }
1717
+ // Whether a block is a subscript, as in %h{$key}: it touches the term
1718
+ // before it. It is not a scope for placeholder variables.
1719
+ function isSubscript(block) {
1720
+ let prev = block.prevSibling;
1721
+ return block.name == "Block" && prev != null && prev.to == block.from && /^[A-Z]/.test(prev.name) && prev.name != "Operator";
1722
+ }
1723
+ /// Whether a completion is for a name with a sigil.
1724
+ function hasSigil(completion) { return /^[$@%&]/.test(completion.label); }
1725
+ function variable(label) {
1726
+ return { label, type: label[0] == "&" ? "function" : /^.[!.]/.test(label) ? "property" : "variable", boost: 1 };
1727
+ }
1728
+ function isArrow(text) { return text == "->" || text == "<->"; }
1729
+ class Gatherer {
1730
+ constructor(state) {
1731
+ this.state = state;
1732
+ this.names = [];
1733
+ this.methods = [];
1734
+ }
1735
+ text(node) { return this.state.sliceDoc(node.from, node.to); }
1736
+ declare(node) {
1737
+ let label = this.text(node);
1738
+ // A name without a sigil: my \x
1739
+ if (!variableNodes.test(node.name))
1740
+ return this.names.push({ label, type: "variable", boost: 1 });
1741
+ this.names.push(variable(label));
1742
+ // `has $.x` also makes the private `$!x`, and `$^a` can be written `$a`.
1743
+ if (label[1] == ".")
1744
+ this.names.push(variable(label[0] + "!" + label.slice(2)));
1745
+ else if (isPlaceholder(label))
1746
+ this.names.push(variable(label[0] + label.slice(2)));
1747
+ }
1748
+ // Declares the name after `node`, when `node` is the backslash of a
1749
+ // variable without a sigil: my \x = 1, sub f(\x) { }
1750
+ declareSigilless(node) {
1751
+ let name = sigillessName(this.state, node);
1752
+ if (name)
1753
+ this.declare(name);
1754
+ return name != null;
1755
+ }
1756
+ // The variables of a signature, or of the parentheses of `my (...)`.
1757
+ // What follows a `=` or a `where`, up to the next comma, is a default
1758
+ // value or a constraint, and uses variables rather than declaring them.
1759
+ declareAll(node) {
1760
+ let uses = false;
1761
+ for (let child = node.firstChild; child; child = child.nextSibling) {
1762
+ let { name } = child;
1763
+ if (name == ",")
1764
+ uses = false;
1765
+ else if (name == "where" || name == "Operator" && this.text(child) == "=")
1766
+ uses = true;
1767
+ else if (uses)
1768
+ continue;
1769
+ else if (this.declareSigilless(child))
1770
+ continue;
1771
+ else if (variableNodes.test(name))
1772
+ this.declare(child);
1773
+ else if (name == "Parens" || name == "Brackets")
1774
+ this.declareAll(child);
1775
+ }
1776
+ }
1777
+ addMethods(scope) {
1778
+ for (let method of scopeOf(scope, this.state).methods)
1779
+ this.methods.push(method);
1780
+ }
1781
+ // Goes over the content of a node that is not a scope of its own.
1782
+ content(node) {
1783
+ // Whether a declarator was seen, and what it declares is yet to come.
1784
+ let declaring = false;
1785
+ for (let child = node.firstChild; child; child = child.nextSibling) {
1786
+ let { name } = child;
1787
+ if (declarators.test(name)) {
1788
+ declaring = true;
1789
+ continue;
1790
+ }
1791
+ // my Int $x, my Array[Int] @y
1792
+ if (declaring && (name == "TypeName" || name == "Brackets"))
1793
+ continue;
1794
+ // A placeholder variable declares itself where it is used: { $^a + $^b }
1795
+ if (name == "VariableName" && isPlaceholder(this.text(child)))
1796
+ this.declare(child);
1797
+ else if (declaring && this.declareSigilless(child))
1798
+ continue;
1799
+ else if (declaring && variableNodes.test(name))
1800
+ this.declare(child);
1801
+ else if (declaring && name == "Parens")
1802
+ this.declareAll(child);
1803
+ else if (scopeNodes.test(name)) {
1804
+ // The placeholders in a subscript are those of the block around it.
1805
+ if (isSubscript(child))
1806
+ this.placeholders(child);
1807
+ this.addMethods(child);
1808
+ }
1809
+ else if (/Declaration$/.test(name))
1810
+ this.declaration(child);
1811
+ else if (child.firstChild)
1812
+ this.content(child);
1813
+ // The name of a constant with a sigil is a variable after the
1814
+ // declaration node: constant $LIMIT = 3
1815
+ declaring = name == "ConstantDeclaration" && child.lastChild.name == "constant";
1816
+ }
1817
+ }
1818
+ // The placeholder variables in a node, and in the subscripts in it.
1819
+ placeholders(node) {
1820
+ for (let child = node.firstChild; child; child = child.nextSibling) {
1821
+ if (child.name == "VariableName") {
1822
+ if (isPlaceholder(this.text(child)))
1823
+ this.declare(child);
1824
+ }
1825
+ else if (child.firstChild && (!scopeNodes.test(child.name) || isSubscript(child)))
1826
+ this.placeholders(child);
1827
+ }
1828
+ }
1829
+ declaration(node) {
1830
+ let kind = node.firstChild.name;
1831
+ for (let child = node.firstChild; child; child = child.nextSibling) {
1832
+ let { name } = child;
1833
+ if (name == "RoutineName") {
1834
+ let label = this.text(child);
1835
+ // An operator is not called by its name: infix:<+>
1836
+ if (/:[<«\[]/.test(label))
1837
+ continue;
1838
+ // `multi foo` has no declarator, and declares a sub.
1839
+ if (kind == "sub" || kind == "RoutineName")
1840
+ this.names.push({ label, type: "function", boost: 1 }, variable("&" + label));
1841
+ // A private method is called as self!name, where nothing is completed.
1842
+ else if (/^[^!^]/.test(label))
1843
+ this.methods.push({ label, type: "method", boost: 1 });
1844
+ }
1845
+ else if (name == "PackageName") {
1846
+ this.names.push({ label: this.text(child), type: "class", boost: 1 });
1847
+ }
1848
+ else if (name == "EnumName" || name == "SubsetName") {
1849
+ this.names.push({ label: this.text(child), type: "type", boost: 1 });
1850
+ }
1851
+ else if (name == "ConstantName") {
1852
+ let label = this.text(child);
1853
+ this.names.push(/^[$@%&]/.test(label) ? variable(label) : { label, type: "constant", boost: 1 });
1854
+ }
1855
+ else if (scopeNodes.test(name)) {
1856
+ this.addMethods(child);
1857
+ }
1858
+ else if (name != "Parens" && child.firstChild) {
1859
+ // The signature belongs to the scope of the body.
1860
+ this.content(child);
1861
+ }
1862
+ }
1863
+ }
1864
+ // The parameters of a block: those of the routine that it is the body
1865
+ // of, or the ones after the arrow of a pointy block.
1866
+ parameters(block) {
1867
+ let parent = block.parent;
1868
+ if (parent && parent.name == "RoutineDeclaration") {
1869
+ for (let child = parent.firstChild; child; child = child.nextSibling)
1870
+ if (child.name == "Parens")
1871
+ this.declareAll(child);
1872
+ return;
1873
+ }
1874
+ // Going backwards, the variables of a parameter come before the
1875
+ // `=` of its default value.
1876
+ let found = [], parameter = [];
1877
+ for (let prev = block.prevSibling; prev; prev = prev.prevSibling) {
1878
+ let { name } = prev;
1879
+ if (name == ";" || name == "Block" || /Declaration$/.test(name))
1880
+ return;
1881
+ if (name == "VariableName") {
1882
+ parameter.push(prev);
1883
+ }
1884
+ else if (prev.prevSibling && sigillessName(this.state, prev.prevSibling)) {
1885
+ // -> \item { }
1886
+ parameter.push(prev);
1887
+ }
1888
+ else if (name == ",") {
1889
+ found = found.concat(parameter);
1890
+ parameter = [];
1891
+ }
1892
+ else if (name == "Operator") {
1893
+ let text = this.text(prev);
1894
+ if (text == "=")
1895
+ parameter = [];
1896
+ else if (isArrow(text)) {
1897
+ for (let node of found.concat(parameter))
1898
+ this.declare(node);
1899
+ return;
1900
+ }
1901
+ }
1902
+ }
1903
+ }
1904
+ }
1905
+ // The names that the scope node `node` declares in its content. The
1906
+ // parameters of a block are not part of its node, and are left out.
1907
+ function scopeOf(node, state) {
1908
+ let scope = scopes.get(node);
1909
+ if (scope)
1910
+ return scope;
1911
+ let gatherer = new Gatherer(state);
1912
+ gatherer.content(node);
1913
+ scope = { names: gatherer.names, methods: gatherer.methods };
1914
+ scopes.set(node, scope);
1915
+ return scope;
1916
+ }
1917
+ /// The names that are in scope at `node`, innermost scope first, and
1918
+ /// the methods of the whole document.
1919
+ function localNames(node, state) {
1920
+ let names = [], methods = [];
1921
+ for (let cur = node; cur; cur = cur.parent) {
1922
+ if (!scopeNodes.test(cur.name))
1923
+ continue;
1924
+ let scope = scopeOf(cur, state);
1925
+ // An edit to the parameters does not replace the block's node, so
1926
+ // these are read every time.
1927
+ let parameters = new Gatherer(state);
1928
+ parameters.parameters(cur);
1929
+ for (let name of parameters.names)
1930
+ names.push(name);
1931
+ for (let name of scope.names)
1932
+ names.push(name);
1933
+ methods = scope.methods;
1934
+ }
1935
+ return { names, methods };
1936
+ }
1937
+ /// Whether a variable is in a place where variables are declared, so
1938
+ /// that its name is a new one. The variable is a child of `parent`, and
1939
+ /// comes after the sibling `prev`.
1940
+ function declaresVariable(state, parent, prev) {
1941
+ let text = (node) => state.sliceDoc(node.from, node.to);
1942
+ // A signature, or the list of `my ($a, $b)`, unless the variable is
1943
+ // in a default value or in a constraint.
1944
+ if (parent.name == "Parens") {
1945
+ let before = parent.prevSibling;
1946
+ if (parent.parent && parent.parent.name == "RoutineDeclaration" || before && declarators.test(before.name)) {
1947
+ for (let node = prev; node && node.name != ","; node = node.prevSibling)
1948
+ if (node.name == "where" || node.name == "Operator" && text(node) == "=")
1949
+ return false;
1950
+ return true;
1951
+ }
1952
+ }
1953
+ // After a declarator, or after the arrow of a pointy block.
1954
+ let first = true;
1955
+ for (let node = prev; node; node = node.prevSibling) {
1956
+ let { name } = node;
1957
+ // `my $a, $b` declares the first one only.
1958
+ if (declarators.test(name))
1959
+ return first;
1960
+ if (name == ";" || name == "Block" || name == "{" || /Declaration$/.test(name))
1961
+ return false;
1962
+ if (name == "Operator") {
1963
+ let operator = text(node);
1964
+ if (isArrow(operator))
1965
+ return true;
1966
+ if (operator == "=")
1967
+ return false;
1968
+ }
1969
+ // Only a type can stand between a declarator and its variable.
1970
+ if (name != "TypeName" && name != "Brackets")
1971
+ first = false;
1972
+ }
1973
+ return false;
1974
+ }
1975
+
924
1976
  function completions(type, ...labels) {
925
1977
  return labels.join(" ").split(" ").map(label => ({ label, type }));
926
1978
  }
@@ -937,6 +1989,7 @@ const globals = [...keywords, ...constants, ...types, ...routines];
937
1989
  // `:key` is not a name from the list.
938
1990
  const nameTail = /^(\w[\w:'\-]*)?$/;
939
1991
  const specialVariableTail = /^[$@%&][*?][\w\-]*$/;
1992
+ const variableTail = /^[$@%&][!.^:]?[\w'\-]*$/;
940
1993
  // Whether the position of `node` holds code, as opposed to the text of
941
1994
  // a string, a regex, a comment or Pod. An interpolation is code again.
942
1995
  function inCode(node) {
@@ -963,8 +2016,26 @@ function isPrivateMethodName(tree, state, node) {
963
2016
  return invocant.to == mark.from &&
964
2017
  /^(self|VariableName|AttributeName|SpecialVariable|Identifier|TypeName|Parens|Brackets|[)\]])$/.test(invocant.name);
965
2018
  }
966
- /// A completion source for Raku keywords and for the commonly used
967
- /// built-in types, routines, methods and special variables.
2019
+ // The names declared in the document, followed by the built-in ones
2020
+ // that they do not hide.
2021
+ function withLocal(local, builtin) {
2022
+ if (!local.length)
2023
+ return builtin;
2024
+ let labels = new Set(), result = [];
2025
+ for (let completion of local) {
2026
+ if (labels.has(completion.label))
2027
+ continue;
2028
+ labels.add(completion.label);
2029
+ result.push(completion);
2030
+ }
2031
+ for (let completion of builtin)
2032
+ if (!labels.has(completion.label))
2033
+ result.push(completion);
2034
+ return result;
2035
+ }
2036
+ /// A completion source for Raku keywords, for the commonly used
2037
+ /// built-in types, routines, methods and special variables, and for the
2038
+ /// names that the document declares.
968
2039
  const rakuCompletionSource = context => {
969
2040
  let { state, pos } = context, tree = syntaxTree(state), node = tree.resolveInner(pos, -1);
970
2041
  if (!inCode(node))
@@ -973,46 +2044,337 @@ const rakuCompletionSource = context => {
973
2044
  let special = node.name == "SpecialVariable" ? node : context.matchBefore(/[$@%&][*?]$/);
974
2045
  if (special)
975
2046
  return { from: special.from, options: specialVariables, validFor: specialVariableTail };
2047
+ // A variable, or a sigil that is about to be one. A lone `%` or `&`
2048
+ // is as often an operator, and has to be followed by a name first.
2049
+ let isVariableNode = node.name == "VariableName" || node.name == "AttributeName";
2050
+ let sigil = isVariableNode ? null : context.matchBefore(/[$@]$|[$@%&][!.^:]$/);
2051
+ if (isVariableNode || sigil) {
2052
+ let from = isVariableNode ? node.from : sigil.from;
2053
+ // A lone sigil is not a node. It lies in the node around it.
2054
+ let parent = isVariableNode ? node.parent : tree.resolveInner(from, 0);
2055
+ let prev = isVariableNode ? node.prevSibling : parent.childBefore(from);
2056
+ if (declaresVariable(state, parent, prev))
2057
+ return null;
2058
+ // A placeholder variable is declared by being used, also by the
2059
+ // one that is being typed, which is not offered for itself.
2060
+ let typed = isVariableNode ? state.sliceDoc(from, node.to) : "";
2061
+ let options = withLocal(localNames(parent, state).names.filter(hasSigil), []);
2062
+ if (isPlaceholder(typed))
2063
+ options = options.filter(completion => completion.label != typed);
2064
+ return options.length ? { from, options, validFor: variableTail } : null;
2065
+ }
2066
+ let local = localNames(node, state), localMethods = () => withLocal(local.methods, methods);
976
2067
  if (node.name == "MethodName")
977
- return { from: node.from, options: methods, validFor: nameTail };
2068
+ return { from: node.from, options: localMethods(), validFor: nameTail };
978
2069
  // A dot or `.=` with no name after it yet. Two dots are a range operator.
979
2070
  let before = state.sliceDoc(Math.max(0, pos - 2), pos);
980
2071
  if (before == ".=")
981
- return { from: pos, options: methods, validFor: nameTail };
2072
+ return { from: pos, options: localMethods(), validFor: nameTail };
982
2073
  if (/(^|[^.])\.$/.test(before)) {
983
2074
  // The dot of `1.5` and of `v6.d` is far more often typed than a
984
2075
  // method call on a number.
985
2076
  let numeric = /^(Number|Version)$/.test(tree.resolveInner(pos - 1, -1).name);
986
- return numeric && !context.explicit ? null : { from: pos, options: methods, validFor: nameTail };
2077
+ return numeric && !context.explicit ? null : { from: pos, options: localMethods(), validFor: nameTail };
987
2078
  }
2079
+ let localGlobals = () => withLocal(local.names.filter(completion => !hasSigil(completion)), globals);
988
2080
  // A keyword's node is named after the keyword, as are those of `,` and `;`.
989
2081
  let isKeyword = /^\w/.test(node.name) && node.name == state.sliceDoc(node.from, node.to);
990
2082
  if (isKeyword || node.name == "Identifier" || node.name == "TypeName") {
991
2083
  if (isPrivateMethodName(tree, state, node))
992
2084
  return null;
993
- // `$x .= trim` calls a method.
994
2085
  let prev = node.prevSibling;
2086
+ // The name of a variable without a sigil, where it is declared: my \x
2087
+ if (prev && prev.to == node.from && state.sliceDoc(prev.from, prev.to) == "\\" &&
2088
+ declaresVariable(state, node.parent, prev.prevSibling))
2089
+ return null;
2090
+ // `$x .= trim` calls a method.
995
2091
  let afterDotAssign = prev != null && prev.name == "Operator" && state.sliceDoc(prev.from, prev.to) == ".=";
996
- return { from: node.from, options: afterDotAssign ? methods : globals, validFor: nameTail };
2092
+ return { from: node.from, options: afterDotAssign ? localMethods() : localGlobals(), validFor: nameTail };
997
2093
  }
998
- // Other names (variables, pair keys, names being declared) are made up
999
- // by the user, so only a position where no token has been started
1000
- // gets the whole list, and only on request.
2094
+ // Other names (pair keys, names being declared) are made up by the
2095
+ // user, so only a position where no token has been started gets the
2096
+ // whole list, and only on request.
1001
2097
  if (context.explicit && /^[\s(\[{,;=]?$/.test(state.sliceDoc(Math.max(0, pos - 1), pos)))
1002
- return { from: pos, options: globals, validFor: nameTail };
2098
+ return { from: pos, options: localGlobals(), validFor: nameTail };
1003
2099
  return null;
1004
2100
  };
1005
2101
 
2102
+ // The grammar has no statement nodes, so whether a line continues the
2103
+ // statement before it is worked out here, from the token that precedes
2104
+ // the line and from what the line starts with.
2105
+ const skipped = /^(LineComment|DocComment|BlockComment|Pod|Heredoc)$/;
2106
+ const brackets = /^(Block|Parens|Brackets|Interpolation|Program)$/;
2107
+ const wordOperators = /^(and|or|xor|andthen|orelse|notandthen|div|mod|gcd|lcm|eq|ne|lt|gt|le|ge|cmp|leg|eqv|but)$/;
2108
+ // Operators that end a term or stand for a whole statement.
2109
+ const closingOperators = /^(\+\+|--|\.\.\.|!!!|\?\?\?)$/;
2110
+ // The words that start a trait. `of` is left out: it is as often a
2111
+ // name of one's own.
2112
+ const traits = "is|does|returns|handles|where|will";
2113
+ /// The lines that are indented again when they are typed: one that
2114
+ /// closes a bracket, and one that starts with a trait, which is only
2115
+ /// known to continue a statement once the trait's word is there.
2116
+ const indentOnInput = new RegExp("^\\s*(?:[\\}\\]\\)]|(?:" + traits + ")\\s)$");
2117
+ // A line that starts with an infix: an operator that has a space after
2118
+ // it and more on the line, an operator that is a word, a statement
2119
+ // modifier, or a trait (`is export`). A prefix operator touches its
2120
+ // operand, and `...` stands alone.
2121
+ const startsWithInfix = new RegExp("^\\s*(?:[~+\\-*/%<>=|&^?!]+[ \\t]+\\S|(?:" + wordOperators.source.slice(2, -2) +
2122
+ "|if|unless|for|while|until|given|when|with|without" +
2123
+ "|" + traits + ")(?![\\w'-]))");
2124
+ const startsWithMethod = /^\s*\.[^\s\d.]/;
2125
+ // The last token of `node`. A string, a regex, a heredoc and a Pod
2126
+ // block count as one token: what they end in is not code.
2127
+ const wholeTokens = /^(StringLiteral|Regex|Heredoc|Pod)$/;
2128
+ function lastToken(node) {
2129
+ while (node.lastChild && !wholeTokens.test(node.name))
2130
+ node = node.lastChild;
2131
+ return node;
2132
+ }
2133
+ // The last token that ends at or before `pos` in `bracket`, not
2134
+ // counting comments.
2135
+ function tokenBefore(bracket, pos) {
2136
+ let node = bracket.resolveInner(pos, -1);
2137
+ // Either the node that ends there, or the one around the position.
2138
+ if (node.to > pos)
2139
+ node = node.childBefore(pos);
2140
+ while (node) {
2141
+ let token = lastToken(node);
2142
+ if (!skipped.test(token.name))
2143
+ return token;
2144
+ // Go to what comes before the comment, in this node or around it.
2145
+ while (node && !node.prevSibling)
2146
+ node = node.parent;
2147
+ node = node && node.prevSibling;
2148
+ }
2149
+ return null;
2150
+ }
2151
+ function bracketAround(node) {
2152
+ for (let parent = node.parent; parent; parent = parent.parent)
2153
+ if (brackets.test(parent.name))
2154
+ return parent;
2155
+ return null;
2156
+ }
2157
+ // The bracket node whose content a token is part of. The brackets of a
2158
+ // node are part of the content around that node.
2159
+ function bracketOf(token) {
2160
+ let around = bracketAround(token);
2161
+ return around && around == token.parent && /^[()\[\]{}]$/.test(token.name) ? bracketAround(around) : around;
2162
+ }
2163
+ // The first node after `node` that is not a comment.
2164
+ function nextCode(node) {
2165
+ do
2166
+ node = node && node.nextSibling;
2167
+ while (node && skipped.test(node.name));
2168
+ return node;
2169
+ }
2170
+ // Whether a block in braces holds the pairs of a hash, in which a comma
2171
+ // separates items that line up.
2172
+ function holdsPairs(block, state) {
2173
+ let first = block.name == "Block" ? nextCode(block.firstChild) : null;
2174
+ if (!first)
2175
+ return false;
2176
+ if (first.name == "PairKey")
2177
+ return true;
2178
+ let second = nextCode(first);
2179
+ return second != null && second.name == "Operator" && state.sliceDoc(second.from, second.to) == "=>";
2180
+ }
2181
+ // Whether a line continues a statement of `bracket`. The line starts at
2182
+ // `pos` with the text `after`.
2183
+ function continuesStatement(state, bracket, pos, after) {
2184
+ // The lines of a regex are not statements: its `+` and `|` are not
2185
+ // the operators of an expression.
2186
+ for (let node = bracket.resolveInner(pos, -1); node && node.from >= bracket.from; node = node.parent) {
2187
+ if (node.name == "Regex")
2188
+ return false;
2189
+ if (brackets.test(node.name))
2190
+ break;
2191
+ }
2192
+ let before = tokenBefore(bracket, pos);
2193
+ if (!before || bracketOf(before) != bracket)
2194
+ return false;
2195
+ let name = before.name, text = state.sliceDoc(before.from, before.to);
2196
+ if (name == "Operator")
2197
+ return !closingOperators.test(text);
2198
+ if (wordOperators.test(name) && name == text)
2199
+ return true;
2200
+ if (name == ",")
2201
+ return !holdsPairs(bracket, state);
2202
+ if (name == ";" || name == "{")
2203
+ return false;
2204
+ if (name == "}") {
2205
+ // A block ends its statement. A subscript, which touches the term
2206
+ // that it follows, does not: %h{$key}
2207
+ let block = before.parent;
2208
+ return block.from > 0 && !/\s/.test(state.sliceDoc(block.from - 1, block.from)) && startsWithInfix.test(after);
2209
+ }
2210
+ // After the end of a statement, a `.` calls a method on the topic.
2211
+ return startsWithInfix.test(after) || startsWithMethod.test(after);
2212
+ }
2213
+ // Whether the content of a bracket node starts on the line of its
2214
+ // opening bracket, which `delimitedIndent` then aligns the rest with.
2215
+ function isAligned(node, state) {
2216
+ let open = node.firstChild, next = nextCode(open);
2217
+ return open != null && next != null && next != node.lastChild && next.from < state.doc.lineAt(open.from).to;
2218
+ }
2219
+ const bodyKeywords = /^(if|elsif|else|unless|with|orwith|without|for|while|until|loop|repeat|given|when|default|whenever|react|supply|gather|do|try|start)$/;
2220
+ const declarationPrefixes = /^(my|our|has|state|anon|augment|supersede|unit|multi|proto|only)$/;
2221
+ // The first node of the statement that a block is the body of, when it
2222
+ // is the body of a control statement or of a declaration. A block that
2223
+ // is a term in an expression has none: `my $f = sub { ... }`
2224
+ function statementOfBody(block, state) {
2225
+ let declaration = block.parent && /Declaration$/.test(block.parent.name) ? block.parent : null;
2226
+ let first = declaration || block;
2227
+ for (let prev = first.prevSibling; prev; prev = prev.prevSibling) {
2228
+ if (skipped.test(prev.name))
2229
+ continue;
2230
+ if (prev.name == ";" || prev.name == "{")
2231
+ break;
2232
+ // A closing brace that is followed by a line break ends a statement.
2233
+ if (lastToken(prev).name == "}" && /\n/.test(state.sliceDoc(prev.to, first.from)))
2234
+ break;
2235
+ if (declaration && !declarationPrefixes.test(prev.name))
2236
+ return null;
2237
+ first = prev;
2238
+ }
2239
+ return declaration || bodyKeywords.test(first.name) ? first : null;
2240
+ }
2241
+ /// The indentation of the lines in a node that holds statements between
2242
+ /// braces: that of `delimitedIndent`, and one unit more for a line that
2243
+ /// continues a statement.
2244
+ function statementIndent(closing) {
2245
+ let delimited = delimitedIndent({ closing });
2246
+ return (context) => {
2247
+ let { node, state, unit } = context, after = context.textAfter;
2248
+ let closed = after.slice(/^\s*/.exec(after)[0].length).startsWith(closing);
2249
+ // The body of a statement is indented from the line where the
2250
+ // statement starts, which need not be the line of its brace.
2251
+ // if $a
2252
+ // && $b {
2253
+ // say 1;
2254
+ // }
2255
+ let statement = isAligned(node, state) ? null : statementOfBody(node, state);
2256
+ let indent = statement ? context.lineIndent(statement.from) + (closed ? 0 : unit) : delimited(context);
2257
+ return !closed && continuesStatement(state, node, context.pos, after) ? indent + unit : indent;
2258
+ };
2259
+ }
2260
+ /// The indentation of the lines at the top level.
2261
+ function topIndent(context) {
2262
+ return continuesStatement(context.state, context.node, context.pos, context.textAfter) ? context.unit : 0;
2263
+ }
2264
+
2265
+ function closing(open) { return String.fromCharCode(brackets$1[open.charCodeAt(0)]); }
2266
+ // The brackets to try, in this order. A bracket that is repeated has
2267
+ // to be closed as many times.
2268
+ const pairs = ["(", "[", "{", "<", "«", "「", "((", "[[", "{{", "(((", "[[[", "{{{"].map(open => {
2269
+ return { open, close: closing(open).repeat(open.length) };
2270
+ });
2271
+ // Whether `text` can stand between the delimiters: none of them is
2272
+ // left open in it, and none closes more than were opened.
2273
+ function fits(text, { open, close }) {
2274
+ let depth = 0;
2275
+ for (let pos = 0; pos < text.length;) {
2276
+ if (text.startsWith(close, pos)) {
2277
+ if (--depth < 0)
2278
+ return false;
2279
+ pos += close.length;
2280
+ }
2281
+ else if (text.startsWith(open, pos)) {
2282
+ depth++;
2283
+ pos += open.length;
2284
+ }
2285
+ else {
2286
+ pos++;
2287
+ }
2288
+ }
2289
+ return depth == 0;
2290
+ }
2291
+ // The delimiters of the comment that a block comment command would
2292
+ // remove for the range, if there is one: the range is that comment, or
2293
+ // all of its text, give or take whitespace.
2294
+ function commentAt(state, from, to) {
2295
+ let start = from + /^\s*/.exec(state.sliceDoc(from, to))[0].length;
2296
+ let node = syntaxTree(state).resolveInner(start, 1);
2297
+ if (node.name != "BlockComment")
2298
+ return null;
2299
+ let text = state.sliceDoc(node.from, node.to), open = /^#`(.)\1*/.exec(text);
2300
+ if (!open || brackets$1[open[1].charCodeAt(0)] == null)
2301
+ return null;
2302
+ let close = closing(open[1]).repeat(open[0].length - 2);
2303
+ if (!text.endsWith(close))
2304
+ return null;
2305
+ let blank = (a, b) => a >= b || !/\S/.test(state.sliceDoc(a, b));
2306
+ let isComment = start == node.from && blank(node.to, to);
2307
+ let isContent = blank(node.from + open[0].length, from) && blank(to, node.to - close.length) && to <= node.to;
2308
+ return isComment || isContent ? { open: open[0], close } : null;
2309
+ }
2310
+ function blockTokens(state, pos) {
2311
+ // The command asks at the start of what it works on, which is a
2312
+ // selection range, or the first text of a selected line.
2313
+ let range = state.selection.ranges.find(range => range.from == pos);
2314
+ let existing = range && commentAt(state, range.from, range.to);
2315
+ if (existing)
2316
+ return existing;
2317
+ // The command that goes by lines wraps up to the end of the line, so
2318
+ // the brackets have to fit that text as well.
2319
+ let texts = [], line = state.doc.lineAt(pos);
2320
+ if (range) {
2321
+ texts.push(state.sliceDoc(range.from, range.to));
2322
+ line = state.doc.lineAt(range.to);
2323
+ }
2324
+ else {
2325
+ let onLine = state.selection.ranges.find(range => range.from <= pos && range.from >= line.from);
2326
+ if (onLine)
2327
+ line = state.doc.lineAt(onLine.to);
2328
+ }
2329
+ if (!range || range.to < line.to)
2330
+ texts.push(state.sliceDoc(pos, line.to));
2331
+ // The spaces keep a bracket at the edge of the text from joining the delimiters.
2332
+ let pair = pairs.find(pair => texts.every(text => fits(" " + text + " ", pair))) || pairs[0];
2333
+ return { open: "#`" + pair.open, close: pair.close };
2334
+ }
2335
+ /// Language data that gives `language` block comment tokens which fit
2336
+ /// the selection. It takes precedence over the fixed tokens in the
2337
+ /// language's own data.
2338
+ function embeddedCommentTokens(language) {
2339
+ return Prec.high(EditorState.languageData.of((state, pos, side) => {
2340
+ if (!language.isActiveAt(state, pos, side))
2341
+ return [];
2342
+ // Only worked out when asked for: this is called for every kind of
2343
+ // language data, and the line comment commands do not read `block`.
2344
+ return [{ get commentTokens() {
2345
+ return { line: "#", get block() { return blockTokens(state, pos); } };
2346
+ } }];
2347
+ }));
2348
+ }
2349
+
2350
+ // A regex literal is one token to the parser of a program: whether it
2351
+ // is closed has to be known where it starts, and a token that is read
2352
+ // piece by piece cannot look that far ahead (see AGENTS.md). Its inside
2353
+ // is parsed by the same grammar, from the top rule for a regex literal,
2354
+ // and that tree takes the place of the token's node. The inner parser
2355
+ // is made from `base` so that it has the same props, among them the
2356
+ // language data that LRLanguage.define puts on top nodes.
2357
+ function nestedParsers(base) {
2358
+ // From the term of a token to the parser of its inside. The
2359
+ // generator registers a top rule under its node name.
2360
+ let parsers = new Map();
2361
+ let wrap = parseMixed(node => parsers.get(node.type.id) || null);
2362
+ // The inner parsers are wrapped as well: a regex can hold a block of
2363
+ // code, and that can hold a regex.
2364
+ parsers.set(regexLiteral, { parser: base.configure({ top: "Regex", wrap }) });
2365
+ parsers.set(interpolatingHeredoc, { parser: base.configure({ top: "Heredoc", wrap }) });
2366
+ parsers.set(interpolatingWordList, { parser: base.configure({ top: "StringLiteral", wrap }) });
2367
+ return wrap;
2368
+ }
1006
2369
  /// The Lezer parser for Raku, without the editor-specific node props.
1007
- const parser = parser$1;
1008
- /// A language provider based on the Lezer Raku parser, extended with
1009
- /// highlighting and indentation information.
1010
- const rakuLanguage = LRLanguage.define({
2370
+ const parser = parser$1.configure({ wrap: nestedParsers(parser$1) });
2371
+ const baseLanguage = LRLanguage.define({
1011
2372
  name: "raku",
1012
- parser: parser.configure({
2373
+ parser: parser$1.configure({
1013
2374
  props: [
1014
2375
  indentNodeProp.add({
1015
- "Block Interpolation": delimitedIndent({ closing: "}" }),
2376
+ Program: topIndent,
2377
+ "Block Interpolation": statementIndent("}"),
1016
2378
  Parens: delimitedIndent({ closing: ")" }),
1017
2379
  Brackets: delimitedIndent({ closing: "]" }),
1018
2380
  // The lines of these are text, which keeps its indentation.
@@ -1026,22 +2388,30 @@ const rakuLanguage = LRLanguage.define({
1026
2388
  // A heredoc starts with the line break before its first line.
1027
2389
  Heredoc(tree) { return { from: tree.from, to: tree.to }; }
1028
2390
  }),
1029
- styleTags(Object.assign(Object.assign({}, keywordTags), { "True False": tags.bool, Nil: tags.null, Identifier: tags.function(tags.variableName), TypeName: tags.typeName, PackageName: tags.definition(tags.className), "EnumName SubsetName": tags.definition(tags.typeName), ConstantName: tags.definition(tags.constant(tags.variableName)), "RoutineName RegexName": tags.function(tags.definition(tags.variableName)), MethodName: tags.function(tags.propertyName), VariableName: tags.variableName, AttributeName: tags.propertyName, SpecialVariable: tags.special(tags.variableName), PairKey: tags.attributeName, Number: tags.number, Version: tags.literal, "StringLiteral Heredoc": tags.string, Regex: tags.regexp, Pod: tags.docComment, Escape: tags.escape, "Interpolation/{ Interpolation/}": tags.special(tags.brace), LineComment: tags.lineComment, BlockComment: tags.blockComment, DocComment: tags.docComment, Operator: tags.operator, ".": tags.derefOperator, ", ;": tags.separator, ":": tags.punctuation, "( )": tags.paren, "[ ]": tags.squareBracket, "{ }": tags.brace }))
2391
+ styleTags(Object.assign(Object.assign({}, keywordTags), { "True False": tags.bool, Nil: tags.null, Identifier: tags.function(tags.variableName), TypeName: tags.typeName, PackageName: tags.definition(tags.className), "EnumName SubsetName": tags.definition(tags.typeName), ConstantName: tags.definition(tags.constant(tags.variableName)), "RoutineName RegexName": tags.function(tags.definition(tags.variableName)), MethodName: tags.function(tags.propertyName), VariableName: tags.variableName, AttributeName: tags.propertyName, SpecialVariable: tags.special(tags.variableName), PairKey: tags.attributeName, Number: tags.number, Version: tags.literal, "StringLiteral Heredoc": tags.string, Regex: tags.regexp, CharacterClass: tags.character, Assertion: tags.function(tags.variableName), "Pod PodFormat": tags.docComment, PodDirective: tags.meta,
2392
+ // These are still part of a comment, and are styled as one where
2393
+ // a theme has no style for the second tag.
2394
+ PodHeading: [tags.docComment, tags.heading], PodStrong: [tags.docComment, tags.strong], PodEmphasis: [tags.docComment, tags.emphasis], PodCode: [tags.docComment, tags.monospace], PodLink: [tags.docComment, tags.link], Escape: tags.escape, "Interpolation/{ Interpolation/}": tags.special(tags.brace), LineComment: tags.lineComment, BlockComment: tags.blockComment, DocComment: tags.docComment, Operator: tags.operator, ".": tags.derefOperator, ", ;": tags.separator, ":": tags.punctuation, "( )": tags.paren, "[ ]": tags.squareBracket, "{ }": tags.brace }))
1030
2395
  ]
1031
2396
  }),
1032
2397
  languageData: {
1033
2398
  // An embedded comment can use any bracket. Parentheses are the usual choice.
1034
2399
  commentTokens: { line: "#", block: { open: "#`(", close: ")" } },
1035
2400
  closeBrackets: { brackets: ["(", "[", "{", "'", '"', "「"] },
1036
- indentOnInput: /^\s*[\}\]\)]$/
2401
+ indentOnInput
1037
2402
  }
1038
2403
  });
1039
- /// Completion of Raku keywords and of the commonly used built-in types,
1040
- /// routines, methods and special variables.
2404
+ /// A language provider based on the Lezer Raku parser, extended with
2405
+ /// highlighting and indentation information.
2406
+ const rakuLanguage = baseLanguage.configure({ wrap: nestedParsers(baseLanguage.parser) });
2407
+ /// Completion of Raku keywords, of the commonly used built-in types,
2408
+ /// routines, methods and special variables, and of the names that the
2409
+ /// document declares.
1041
2410
  const rakuCompletion = rakuLanguage.data.of({ autocomplete: rakuCompletionSource });
1042
- /// Raku language support, with completion.
2411
+ /// Raku language support, with completion, and with block comment
2412
+ /// tokens that fit the selection.
1043
2413
  function raku() {
1044
- return new LanguageSupport(rakuLanguage, [rakuCompletion]);
2414
+ return new LanguageSupport(rakuLanguage, [rakuCompletion, embeddedCommentTokens(rakuLanguage)]);
1045
2415
  }
1046
2416
 
1047
2417
  export { parser, raku, rakuCompletion, rakuLanguage };