parseman 0.18.0 → 0.18.2

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
@@ -349,13 +349,12 @@ function literal(value, opts = {}) {
349
349
  if (end > input.length) {
350
350
  return failAt(ctx, [JSON.stringify(value)], pos);
351
351
  }
352
- const slice = input.slice(pos, end);
353
- const matched = caseInsensitive ? asciiFoldEq(slice, value) : slice === value;
354
- if (matched) {
352
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
353
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
355
354
  const span = { start: pos, end };
356
- const leaf = { _tag: "leaf", value: slice, span };
355
+ const leaf = { _tag: "leaf", value: matchedValue, span };
357
356
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
358
- return { ok: true, value: slice, span };
357
+ return { ok: true, value: matchedValue, span };
359
358
  }
360
359
  return failAt(ctx, [JSON.stringify(value)], pos);
361
360
  }
@@ -368,10 +367,102 @@ function registerRegexAnalyzer(analyzer) {
368
367
  firstSetAnalyzer = analyzer;
369
368
  }
370
369
  var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
370
+ var SCAN_BAIL_AT = 64;
371
+ var SPACE_RANGES = [
372
+ [9, 13],
373
+ [32, 32],
374
+ [160, 160],
375
+ [5760, 5760],
376
+ [8192, 8202],
377
+ [8232, 8233],
378
+ [8239, 8239],
379
+ [8287, 8287],
380
+ [12288, 12288],
381
+ [65279, 65279]
382
+ ];
383
+ var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
384
+ function shorthandRanges(ch) {
385
+ if (ch === "d") return [[48, 57]];
386
+ if (ch === "s") return SPACE_RANGES;
387
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
388
+ }
389
+ function inRanges(cp, ranges) {
390
+ for (let i = 0; i < ranges.length; i++) {
391
+ const [lo, hi] = ranges[i];
392
+ if (cp >= lo && cp <= hi) return true;
393
+ }
394
+ return false;
395
+ }
396
+ function readClassRanges(body) {
397
+ if (body.startsWith("^")) return null;
398
+ const ranges = [];
399
+ let i = 0;
400
+ const readAtom = () => {
401
+ const ch = body[i];
402
+ if (ch === void 0) return null;
403
+ if (ch === "\\") {
404
+ const esc = body[i + 1];
405
+ if (esc === void 0) return null;
406
+ i += 2;
407
+ if (esc === "d" || esc === "w" || esc === "s") return { ranges: shorthandRanges(esc) };
408
+ if (esc in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[esc] };
409
+ if (esc >= "a" && esc <= "z" || esc >= "A" && esc <= "Z") return null;
410
+ return { cp: esc.charCodeAt(0) };
411
+ }
412
+ i++;
413
+ return { cp: ch.charCodeAt(0) };
414
+ };
415
+ while (i < body.length) {
416
+ const lo = readAtom();
417
+ if (!lo) return null;
418
+ if ("ranges" in lo) {
419
+ ranges.push(...lo.ranges);
420
+ continue;
421
+ }
422
+ if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
423
+ i++;
424
+ const hi = readAtom();
425
+ if (!hi || "ranges" in hi) return null;
426
+ ranges.push([lo.cp, hi.cp]);
427
+ } else {
428
+ ranges.push([lo.cp, lo.cp]);
429
+ }
430
+ }
431
+ return ranges.length ? ranges : null;
432
+ }
433
+ function shortScanner(source, flags) {
434
+ if (/[imsuvy]/.test(flags)) return null;
435
+ let ranges = null;
436
+ let quant = "";
437
+ if (source[0] === "[") {
438
+ let end = 1;
439
+ while (end < source.length && source[end] !== "]") {
440
+ if (source[end] === "\\") end += 2;
441
+ else end++;
442
+ }
443
+ if (source[end] !== "]") return null;
444
+ ranges = readClassRanges(source.slice(1, end));
445
+ quant = source.slice(end + 1);
446
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
447
+ ranges = shorthandRanges(source[1]);
448
+ quant = source.slice(2);
449
+ }
450
+ if (!ranges || quant !== "+" && quant !== "*") return null;
451
+ const minOne = quant === "+";
452
+ return (input, pos) => {
453
+ let end = pos;
454
+ while (end < input.length && inRanges(input.charCodeAt(end), ranges)) {
455
+ end++;
456
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
457
+ }
458
+ return minOne && end === pos ? null : end;
459
+ };
460
+ }
371
461
  function regex(pattern, flags = "") {
372
462
  const source = typeof pattern === "string" ? pattern : pattern.source;
373
463
  const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
374
464
  const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
465
+ const scan = shortScanner(source, resolvedFlags);
375
466
  const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
376
467
  const meta = { firstSet, canMatchNewline, isTrivia: false };
377
468
  return {
@@ -379,6 +470,15 @@ function regex(pattern, flags = "") {
379
470
  _meta: meta,
380
471
  _def: { tag: "regex", source, flags: resolvedFlags },
381
472
  parse(input, pos, ctx) {
473
+ const scanEnd = scan?.(input, pos);
474
+ if (scanEnd !== void 0) {
475
+ if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
476
+ const value = input.slice(pos, scanEnd);
477
+ const span2 = { start: pos, end: scanEnd };
478
+ const leaf2 = { _tag: "leaf", value, span: span2 };
479
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf2);
480
+ return { ok: true, value, span: span2 };
481
+ }
382
482
  anchored.lastIndex = pos;
383
483
  const m = anchored.exec(input);
384
484
  if (m === null) {
@@ -562,13 +662,10 @@ function choice(...args) {
562
662
  };
563
663
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
564
664
  const autoNot = !disjoint && !hasGates && strategy?.tag === "firstMatch" ? computeAutoNot(parsers) : parsers.map(() => null);
565
- let greedyRe = null;
566
665
  let greedyLitMap = null;
567
666
  let sortedParsers = null;
667
+ const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
568
668
  if (strategy?.tag === "greedyClassify") {
569
- const regexDef = getCoreRegexDef(parsers[strategy.superIndex]);
570
- const flags = "y" + regexDef.flags.replace(/[gy]/g, "");
571
- greedyRe = new RegExp(regexDef.source, flags);
572
669
  greedyLitMap = /* @__PURE__ */ new Map();
573
670
  for (let i = 0; i < parsers.length; i++) {
574
671
  if (i === strategy.superIndex) continue;
@@ -593,14 +690,21 @@ function choice(...args) {
593
690
  const expected = [];
594
691
  if (disjoint && pos < input.length) {
595
692
  const code = input.codePointAt(pos);
596
- for (const parser2 of parsers) {
597
- if (inFirstSet(code, parser2._meta.firstSet)) {
598
- const result = parser2.parse(input, pos, ctx);
599
- if (result.ok) return result;
600
- expected.push(...result.expected);
601
- return { ok: false, expected, span: { start: pos, end: pos } };
693
+ let parser2 = code < 128 ? asciiDispatch?.[code] ?? null : null;
694
+ if (!parser2) {
695
+ for (const p of parsers) {
696
+ if (inFirstSet(code, p._meta.firstSet)) {
697
+ parser2 = p;
698
+ break;
699
+ }
602
700
  }
603
701
  }
702
+ if (parser2) {
703
+ const result = parser2.parse(input, pos, ctx);
704
+ if (result.ok) return result;
705
+ expected.push(...result.expected);
706
+ return { ok: false, expected, span: { start: pos, end: pos } };
707
+ }
604
708
  return {
605
709
  ok: false,
606
710
  expected: parsers.flatMap((p) => {
@@ -776,6 +880,19 @@ function areDisjoint(sets) {
776
880
  if (intersects(sets[i], sets[j])) return false;
777
881
  return true;
778
882
  }
883
+ function buildAsciiDispatch(parsers) {
884
+ const table = Array(128).fill(null);
885
+ for (const parser2 of parsers) {
886
+ const fs = parser2._meta.firstSet;
887
+ if (fs.kind !== "ranges") continue;
888
+ for (const { lo, hi } of fs.ranges) {
889
+ for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
890
+ table[code] = parser2;
891
+ }
892
+ }
893
+ }
894
+ return table;
895
+ }
779
896
 
780
897
  // src/cst/trivia-kinds.ts
781
898
  function unwrapTrivia(p) {
@@ -897,6 +1014,7 @@ function recordTriviaChunks(ctx, chunks) {
897
1014
  // src/combinators/trivia-skip.ts
898
1015
  var NOOP_COMMIT = () => {
899
1016
  };
1017
+ var fastTriviaCache = /* @__PURE__ */ new WeakMap();
900
1018
  function needsDeferredTriviaCommit(ctx) {
901
1019
  return ctx._triviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
902
1020
  }
@@ -923,6 +1041,8 @@ function scanWithLabels(input, cur, ctx) {
923
1041
  function advanceTrivia(input, cur, ctx) {
924
1042
  const triviaP = ctx.trivia;
925
1043
  if (!triviaP) return cur;
1044
+ const fast = fastTriviaScanner(triviaP);
1045
+ if (fast) return fast(input, cur);
926
1046
  if (ctx.triviaKindLabels) {
927
1047
  const scan = scanWithLabels(input, cur, ctx);
928
1048
  return scan.end;
@@ -935,6 +1055,10 @@ function scanTrivia(input, cur, ctx) {
935
1055
  if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
936
1056
  const log = ctx._triviaLog;
937
1057
  const captureTl = ctx.captureTrivia && (ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0);
1058
+ const fast = !ctx.triviaKindLabels ? fastTriviaScanner(triviaP) : null;
1059
+ if (fast && log === void 0 && !captureTl) {
1060
+ return { end: fast(input, cur), commit: NOOP_COMMIT };
1061
+ }
938
1062
  if (ctx.triviaKindLabels && (log !== void 0 || captureTl)) {
939
1063
  return scanWithLabels(input, cur, ctx);
940
1064
  }
@@ -956,6 +1080,79 @@ function scanTrivia(input, cur, ctx) {
956
1080
  const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
957
1081
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
958
1082
  }
1083
+ function fastTriviaScanner(trivia2) {
1084
+ const cached = fastTriviaCache.get(trivia2);
1085
+ if (cached !== void 0) return cached;
1086
+ const scanner = buildFastTriviaScanner(trivia2);
1087
+ fastTriviaCache.set(trivia2, scanner);
1088
+ return scanner;
1089
+ }
1090
+ function buildFastTriviaScanner(trivia2) {
1091
+ const core = trivia2._def.tag === "trivia" ? trivia2._def.parser : trivia2;
1092
+ const direct = regexTriviaScanner(core);
1093
+ if (direct) return direct;
1094
+ const repeat = core._def.tag === "oneOrMore" || core._def.tag === "many" && core._def.min >= 1 ? core._def.parser : null;
1095
+ if (!repeat) return null;
1096
+ const one = regexTriviaScanner(repeat);
1097
+ if (one) return loopScanner([one]);
1098
+ if (repeat._def.tag !== "choice") return null;
1099
+ const arms = repeat._def.parsers.map(regexTriviaScanner);
1100
+ if (arms.some((s) => s === null)) return null;
1101
+ return loopScanner(arms);
1102
+ }
1103
+ function loopScanner(arms) {
1104
+ return (input, cur) => {
1105
+ let pos = cur;
1106
+ scan: while (pos < input.length) {
1107
+ for (const arm of arms) {
1108
+ const end = arm(input, pos);
1109
+ if (end > pos) {
1110
+ pos = end;
1111
+ continue scan;
1112
+ }
1113
+ }
1114
+ break;
1115
+ }
1116
+ return pos;
1117
+ };
1118
+ }
1119
+ function regexTriviaScanner(parser2) {
1120
+ if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1121
+ const ws = whitespaceSource(parser2._def.source);
1122
+ if (ws) return ws;
1123
+ return blockCommentSource(parser2._def.source) ? scanBlockComment : null;
1124
+ }
1125
+ function whitespaceSource(source) {
1126
+ const m = /^\[([ \\tnrf]+)\][*+]$/.exec(source);
1127
+ if (!m) return null;
1128
+ const chars = /* @__PURE__ */ new Set();
1129
+ for (let i = 0; i < m[1].length; i++) {
1130
+ const ch = m[1][i];
1131
+ if (ch !== "\\") {
1132
+ chars.add(ch.charCodeAt(0));
1133
+ continue;
1134
+ }
1135
+ const esc = m[1][++i];
1136
+ if (esc === "t") chars.add(9);
1137
+ else if (esc === "n") chars.add(10);
1138
+ else if (esc === "r") chars.add(13);
1139
+ else if (esc === "f") chars.add(12);
1140
+ else return null;
1141
+ }
1142
+ return (input, cur) => {
1143
+ let pos = cur;
1144
+ while (pos < input.length && chars.has(input.charCodeAt(pos))) pos++;
1145
+ return pos;
1146
+ };
1147
+ }
1148
+ function blockCommentSource(source) {
1149
+ return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
1150
+ }
1151
+ function scanBlockComment(input, cur) {
1152
+ if (input.charCodeAt(cur) !== 47 || input.charCodeAt(cur + 1) !== 42) return cur;
1153
+ const close = input.indexOf("*/", cur + 2);
1154
+ return close === -1 ? cur : close + 2;
1155
+ }
959
1156
 
960
1157
  // src/combinators/sequence.ts
961
1158
  function sequence(...parsers) {
@@ -1094,11 +1291,15 @@ function optional(combinator) {
1094
1291
  canMatchNewline: combinator._meta.canMatchNewline,
1095
1292
  isTrivia: false
1096
1293
  };
1294
+ const firstSetSkippable = !matchesEmpty(combinator);
1097
1295
  return {
1098
1296
  _tag: "optional",
1099
1297
  _meta: meta,
1100
1298
  _def: { tag: "optional", parser: combinator },
1101
1299
  parse(input, pos, ctx) {
1300
+ if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
1301
+ return { ok: true, value: null, span: { start: pos, end: pos } };
1302
+ }
1102
1303
  const mark = saveTriviaMark(ctx);
1103
1304
  const result = combinator.parse(input, pos, ctx);
1104
1305
  if (result.ok) return result;
@@ -1107,6 +1308,15 @@ function optional(combinator) {
1107
1308
  }
1108
1309
  };
1109
1310
  }
1311
+ function startsFirstSet(combinator, input, pos) {
1312
+ const fs = combinator._meta.firstSet;
1313
+ if (fs.kind === "any") return true;
1314
+ if (fs.kind === "empty") return false;
1315
+ const code = input.codePointAt(pos);
1316
+ if (code === void 0) return false;
1317
+ for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
1318
+ return false;
1319
+ }
1110
1320
  function sepBy(combinator, separator) {
1111
1321
  const meta = {
1112
1322
  firstSet: combinator._meta.firstSet,
@@ -1414,7 +1624,7 @@ function transform(combinator, fn) {
1414
1624
  parse(input, pos, ctx) {
1415
1625
  const result = combinator.parse(input, pos, ctx);
1416
1626
  if (!result.ok) return result;
1417
- return { ...result, value: fn(result.value, result.span) };
1627
+ return { ok: true, value: fn(result.value, result.span), span: result.span };
1418
1628
  }
1419
1629
  };
1420
1630
  }
@@ -1428,7 +1638,7 @@ function skip(main, skipped) {
1428
1638
  if (!result.ok) return result;
1429
1639
  const s = skipped.parse(input, result.span.end, ctx);
1430
1640
  if (!s.ok) return result;
1431
- return { ...result, span: { start: result.span.start, end: s.span.end } };
1641
+ return { ok: true, value: result.value, span: { start: result.span.start, end: s.span.end } };
1432
1642
  }
1433
1643
  };
1434
1644
  }
@@ -1450,7 +1660,11 @@ function label(name, combinator) {
1450
1660
  _tag: combinator._tag,
1451
1661
  _meta: combinator._meta,
1452
1662
  _def: { tag: "label", label: name, parser: combinator },
1453
- parse: combinator.parse.bind(combinator)
1663
+ parse(input, pos, ctx) {
1664
+ const result = combinator.parse(input, pos, ctx);
1665
+ if (!result.ok) return { ok: false, expected: [name], span: result.span };
1666
+ return result;
1667
+ }
1454
1668
  };
1455
1669
  }
1456
1670
 
@@ -1662,9 +1876,9 @@ function expect(combinator, label2) {
1662
1876
 
1663
1877
  // src/compiler/scannable-run.ts
1664
1878
  var BACKSLASH = 92;
1665
- var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1879
+ var CLASS_ESCAPES2 = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1666
1880
  var META = new Set("()[]{}*+?|^$.".split(""));
1667
- var SPACE_RANGES = [
1881
+ var SPACE_RANGES2 = [
1668
1882
  [9, 13],
1669
1883
  [32, 32],
1670
1884
  [160, 160],
@@ -1677,9 +1891,9 @@ var SPACE_RANGES = [
1677
1891
  [12288, 12288],
1678
1892
  [65279, 65279]
1679
1893
  ];
1680
- function shorthandRanges(ch) {
1894
+ function shorthandRanges2(ch) {
1681
1895
  if (ch === "d") return [[48, 57]];
1682
- if (ch === "s") return SPACE_RANGES;
1896
+ if (ch === "s") return SPACE_RANGES2;
1683
1897
  return [[48, 57], [65, 90], [97, 122], [95, 95]];
1684
1898
  }
1685
1899
  function readUnicodeEscape(body, i) {
@@ -1703,8 +1917,8 @@ function parseClassRanges(body) {
1703
1917
  const e = body[i + 1];
1704
1918
  if (e === void 0) return null;
1705
1919
  i += 2;
1706
- if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1707
- if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1920
+ if (e in CLASS_ESCAPES2) return { cp: CLASS_ESCAPES2[e] };
1921
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges2(e) };
1708
1922
  if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1709
1923
  return { cp: e.codePointAt(0) };
1710
1924
  }
@@ -1730,9 +1944,9 @@ function parseClassRanges(body) {
1730
1944
  return ranges.length ? ranges : null;
1731
1945
  }
1732
1946
  function classToRanges(cls) {
1733
- if (cls === "\\d") return shorthandRanges("d");
1734
- if (cls === "\\w") return shorthandRanges("w");
1735
- if (cls === "\\s") return shorthandRanges("s");
1947
+ if (cls === "\\d") return shorthandRanges2("d");
1948
+ if (cls === "\\w") return shorthandRanges2("w");
1949
+ if (cls === "\\s") return shorthandRanges2("s");
1736
1950
  const body = cls.slice(1, -1);
1737
1951
  if (body.startsWith("^")) return null;
1738
1952
  return parseClassRanges(body);
@@ -1750,7 +1964,7 @@ function literalCodePoints(frag) {
1750
1964
  if (ch === "\\") {
1751
1965
  const e = frag[i + 1];
1752
1966
  if (e === void 0) return null;
1753
- out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
1967
+ out.push(e in CLASS_ESCAPES2 ? CLASS_ESCAPES2[e] : e.codePointAt(0));
1754
1968
  i += 2;
1755
1969
  continue;
1756
1970
  }
@@ -1843,7 +2057,7 @@ function parseSeqParts(source) {
1843
2057
  if (e === "d" || e === "w" || e === "s") {
1844
2058
  flush();
1845
2059
  i += 2;
1846
- i += runFrom(shorthandRanges(e), false, source[i]);
2060
+ i += runFrom(shorthandRanges2(e), false, source[i]);
1847
2061
  continue;
1848
2062
  }
1849
2063
  const uni = readUnicodeEscape(source, i);
@@ -1852,8 +2066,8 @@ function parseSeqParts(source) {
1852
2066
  cp2 = uni.cp;
1853
2067
  i = uni.next;
1854
2068
  } else if (e === void 0) return null;
1855
- else if (e in CLASS_ESCAPES) {
1856
- cp2 = CLASS_ESCAPES[e];
2069
+ else if (e in CLASS_ESCAPES2) {
2070
+ cp2 = CLASS_ESCAPES2[e];
1857
2071
  i += 2;
1858
2072
  } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1859
2073
  else {
@@ -1960,7 +2174,7 @@ function rangesSubset(sub, sup) {
1960
2174
  }
1961
2175
  function parseClassOperand(body) {
1962
2176
  if (body === "\\d" || body === "\\w" || body === "\\s") {
1963
- return { ranges: shorthandRanges(body[1]), negated: false };
2177
+ return { ranges: shorthandRanges2(body[1]), negated: false };
1964
2178
  }
1965
2179
  if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1966
2180
  let inner = body.slice(1, -1);
@@ -2393,7 +2607,7 @@ function parseScanShapeCore(source) {
2393
2607
  }
2394
2608
  m = /^\\([dws])([+*])$/.exec(source);
2395
2609
  if (m) {
2396
- return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
2610
+ return { kind: "chars", ranges: shorthandRanges2(m[1]), minOne: m[2] === "+" };
2397
2611
  }
2398
2612
  m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
2399
2613
  if (m) {
@@ -4508,8 +4722,17 @@ function emitDispatch(p, ctx, pos) {
4508
4722
  return emit(def.parser, ctx, pos);
4509
4723
  case "token":
4510
4724
  return emitToken(def, ctx, pos);
4511
- case "label":
4512
- return emit(def.parser, ctx, pos);
4725
+ case "label": {
4726
+ const inner = emitFallible(def.parser, ctx, pos);
4727
+ return {
4728
+ stmts: [
4729
+ ...inner.stmts,
4730
+ ...emitIfFail(ctx, `!${inner.okVar}`, failBody(ctx, JSON.stringify(def.label), "_ctx._fe"))
4731
+ ],
4732
+ valueVar: inner.valVar,
4733
+ endVar: inner.endVar
4734
+ };
4735
+ }
4513
4736
  case "grammar": {
4514
4737
  const savedTrivia = ctx.activeTrivia;
4515
4738
  const savedKindLabels = ctx.triviaKindLabels;