parseman 0.50.0 → 0.50.3

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.
Files changed (55) hide show
  1. package/README.md +14 -10
  2. package/dist/analysis/diagnostics.js +455 -226
  3. package/dist/analysis/diagnostics.js.map +3 -3
  4. package/dist/cli/index.js +445 -191
  5. package/dist/cli/index.js.map +3 -3
  6. package/dist/combinators/choice.d.ts.map +1 -1
  7. package/dist/combinators/grammar.d.ts.map +1 -1
  8. package/dist/combinators/keywords.d.ts.map +1 -1
  9. package/dist/combinators/literal.d.ts.map +1 -1
  10. package/dist/combinators/map.d.ts.map +1 -1
  11. package/dist/combinators/ref.d.ts.map +1 -1
  12. package/dist/combinators/regex.d.ts.map +1 -1
  13. package/dist/combinators/repeat.d.ts.map +1 -1
  14. package/dist/combinators/scalar.d.ts +6 -0
  15. package/dist/combinators/scalar.d.ts.map +1 -0
  16. package/dist/combinators/sequence.d.ts.map +1 -1
  17. package/dist/combinators/trivia-skip.d.ts +2 -0
  18. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  19. package/dist/compiler/token-capability.js +0 -3
  20. package/dist/compiler/token-capability.js.map +2 -2
  21. package/dist/index.js +446 -192
  22. package/dist/index.js.map +3 -3
  23. package/dist/language-service/index.js +68 -172
  24. package/dist/language-service/index.js.map +3 -3
  25. package/dist/parse-context.d.ts.map +1 -1
  26. package/dist/plugin/index.js +491 -306
  27. package/dist/plugin/index.js.map +3 -3
  28. package/dist/run/index.js +1 -0
  29. package/dist/run/index.js.map +1 -1
  30. package/dist/spec/index.js +399 -277
  31. package/dist/spec/index.js.map +3 -3
  32. package/dist/table/emit-assembly.d.ts.map +1 -1
  33. package/dist/table/encode.d.ts.map +1 -1
  34. package/dist/table/index.js +331 -188
  35. package/dist/table/index.js.map +3 -3
  36. package/dist/types.d.ts +8 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/version.d.ts +1 -1
  39. package/package.json +4 -2
  40. package/src/combinators/choice.ts +34 -1
  41. package/src/combinators/grammar.ts +39 -0
  42. package/src/combinators/keywords.ts +14 -3
  43. package/src/combinators/literal.ts +26 -0
  44. package/src/combinators/map.ts +60 -0
  45. package/src/combinators/ref.ts +26 -7
  46. package/src/combinators/regex.ts +23 -0
  47. package/src/combinators/repeat.ts +109 -16
  48. package/src/combinators/scalar.ts +30 -0
  49. package/src/combinators/sequence.ts +76 -161
  50. package/src/combinators/trivia-skip.ts +14 -1
  51. package/src/parse-context.ts +1 -0
  52. package/src/table/emit-assembly.ts +39 -16
  53. package/src/table/encode.ts +30 -10
  54. package/src/types.ts +8 -0
  55. package/src/version.ts +1 -1
package/dist/cli/index.js CHANGED
@@ -689,9 +689,43 @@ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
689
689
  }
690
690
  }
691
691
 
692
+ // src/combinators/scalar.ts
693
+ var SCALAR_TAG = /^(?:literal|regex|keywords|sequence|choice|many|oneOrMore|optional|transform|trivia|grammar|sepBy|lazy)$/;
694
+ function scalarOf(combinator) {
695
+ return combinator._parseScalar;
696
+ }
697
+ function scalarTree(parser2, seen) {
698
+ if (seen.has(parser2)) return true;
699
+ if (parser2._parseScalar === void 0) return false;
700
+ seen.add(parser2);
701
+ const def = parser2._def;
702
+ if (!SCALAR_TAG.test(def.tag)) return false;
703
+ if ("parsers" in def && !def.parsers.every((child) => scalarTree(child, seen))) return false;
704
+ if ("parser" in def && !scalarTree(def.parser, seen)) return false;
705
+ if ("separator" in def && !scalarTree(def.separator, seen)) return false;
706
+ return def.tag !== "lazy" || scalarTree(def.thunk(), seen);
707
+ }
708
+ function scalarRootOf(combinator) {
709
+ const scalar = combinator._parseScalar;
710
+ const def = combinator._def;
711
+ return scalar !== void 0 && def.tag === "grammar" && scalarTree(def.parser, /* @__PURE__ */ new Set()) ? scalar : void 0;
712
+ }
713
+
692
714
  // src/combinators/ref.ts
715
+ function withRuleTrivia(trivia2, ctx, run) {
716
+ const savedLabels = ctx.triviaKindLabels;
717
+ ctx.trivia = trivia2;
718
+ ctx.triviaKindLabels = trivia2._meta.triviaKindLabels;
719
+ try {
720
+ return run();
721
+ } finally {
722
+ ctx.trivia = void 0;
723
+ ctx.triviaKindLabels = savedLabels;
724
+ }
725
+ }
693
726
  function ref() {
694
727
  let resolved = null;
728
+ let resolvedScalar = null;
695
729
  let ownBoundary = false;
696
730
  const meta = {
697
731
  firstSet: any(),
@@ -708,25 +742,28 @@ function ref() {
708
742
  return resolved;
709
743
  }
710
744
  },
745
+ _parseScalar(input, pos, ctx) {
746
+ const scalar = resolvedScalar;
747
+ if (!scalar) throw new Error("ref<T>() used before .define() was called");
748
+ const gt = meta.grammarTrivia;
749
+ if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
750
+ return withRuleTrivia(gt, ctx, () => scalar(input, pos, ctx));
751
+ }
752
+ return scalar(input, pos, ctx);
753
+ },
711
754
  parse(input, pos, ctx) {
712
- if (!resolved) throw new Error("ref<T>() used before .define() was called");
755
+ const parser2 = resolved;
756
+ if (!parser2) throw new Error("ref<T>() used before .define() was called");
713
757
  const gt = meta.grammarTrivia;
714
758
  if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
715
- const savedLabels = ctx.triviaKindLabels;
716
- ctx.trivia = gt;
717
- ctx.triviaKindLabels = gt._meta.triviaKindLabels;
718
- try {
719
- return resolved.parse(input, pos, ctx);
720
- } finally {
721
- ctx.trivia = void 0;
722
- ctx.triviaKindLabels = savedLabels;
723
- }
759
+ return withRuleTrivia(gt, ctx, () => parser2.parse(input, pos, ctx));
724
760
  }
725
- return resolved.parse(input, pos, ctx);
761
+ return parser2.parse(input, pos, ctx);
726
762
  },
727
763
  define(p) {
728
764
  if (resolved) throw new Error("ref<T>() already defined");
729
765
  resolved = p;
766
+ resolvedScalar = scalarOf(p);
730
767
  ownBoundary = hasOwnTriviaBoundary(p);
731
768
  meta.firstSet = p._meta.firstSet;
732
769
  meta.canMatchNewline = p._meta.canMatchNewline;
@@ -1112,6 +1149,7 @@ function createParseContext() {
1112
1149
  _fe: void 0,
1113
1150
  _fx: void 0,
1114
1151
  _fc: void 0,
1152
+ _sv: void 0,
1115
1153
  _probe: void 0,
1116
1154
  _cstChildren: void 0,
1117
1155
  _cstLeaves: void 0,
@@ -1949,6 +1987,24 @@ function parser(opts, root) {
1949
1987
  const captureKinds = clearTrivia ? void 0 : opts.captureTriviaKinds;
1950
1988
  const ownCaptureMask = captureKinds === void 0 ? void 0 : scopeLabels !== void 0 ? triviaKindMask(scopeLabels, captureKinds) : void 0;
1951
1989
  const inheritCaptureMask = captureKinds !== void 0 && scopeLabels === void 0;
1990
+ const rootScalar = scalarOf(root);
1991
+ const scalarEligible = opts.trackLines !== true && opts.recover !== true && !opaqueRootCapture && !forceCaptureTrivia && captureKinds === void 0;
1992
+ const parseScalar = scalarEligible ? (input, pos, ctx) => {
1993
+ const savedTrivia = ctx.trivia;
1994
+ const savedLabels = ctx.triviaKindLabels;
1995
+ if (clearTrivia) {
1996
+ ctx.trivia = void 0;
1997
+ ctx.triviaKindLabels = void 0;
1998
+ } else if (scopeTrivia !== void 0) {
1999
+ ctx.trivia = scopeTrivia;
2000
+ ctx.triviaKindLabels = scopeLabels;
2001
+ }
2002
+ const end = rootScalar(input, pos, ctx);
2003
+ ctx.trivia = savedTrivia;
2004
+ ctx.triviaKindLabels = savedLabels;
2005
+ return end;
2006
+ } : void 0;
2007
+ let publicScalar;
1952
2008
  const grammar = {
1953
2009
  _tag: "grammar",
1954
2010
  _meta: {
@@ -1972,7 +2028,20 @@ function parser(opts, root) {
1972
2028
  constructionTrackLines: trackLinesPolicy,
1973
2029
  ...captureKinds === void 0 ? {} : { constructionCaptureTriviaKinds: captureKinds }
1974
2030
  },
2031
+ _parseScalar: parseScalar,
1975
2032
  parse(input, pos, _ctx) {
2033
+ if (pos === void 0 && _ctx === void 0) {
2034
+ publicScalar ??= scalarRootOf(grammar) ?? null;
2035
+ if (publicScalar) {
2036
+ const ctx2 = createParseContext();
2037
+ const end = publicScalar(input, 0, ctx2);
2038
+ if (end < 0) {
2039
+ const at = ~end;
2040
+ return { ok: false, expected: ctx2._fx ?? [], span: { start: at, end: at } };
2041
+ }
2042
+ return { ok: true, value: ctx2._sv, span: { start: 0, end } };
2043
+ }
2044
+ }
1976
2045
  if (refuseUnclassified) refuseUnclassifiedRootScope(_ctx?._rootTriviaStrictScopes);
1977
2046
  const trackLines = trackLinesOf(_ctx);
1978
2047
  const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
@@ -2007,6 +2076,9 @@ function parse(combinator, input, opts = {}) {
2007
2076
  _analyzed.add(combinator);
2008
2077
  markUnusedValues(combinator);
2009
2078
  }
2079
+ if (!opts.trackLines && !opts.recover && combinator._def.tag === "grammar" && combinator._meta.grammarScanSkip === void 0) {
2080
+ return combinator.parse(input);
2081
+ }
2010
2082
  const trackLines = opts.trackLines ?? combinator._meta.grammarTrackLines ?? false;
2011
2083
  const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
2012
2084
  const lineIndex = lineContext?.lineIndex;
@@ -2601,10 +2673,32 @@ function regex(pattern, flags = "") {
2601
2673
  const canMatchNewline = raw.canMatchNewline;
2602
2674
  const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
2603
2675
  const def = { tag: "regex", source, flags: resolvedFlags };
2676
+ const expected = directTerminalFailureExpected(def);
2677
+ const parseScalar = (input, pos, ctx) => {
2678
+ const scanEnd = scan?.(input, pos);
2679
+ if (scanEnd !== void 0) {
2680
+ if (scanEnd === null) {
2681
+ ctx._fx = expected;
2682
+ return ~pos;
2683
+ }
2684
+ ctx._sv = input.slice(pos, scanEnd);
2685
+ return scanEnd;
2686
+ }
2687
+ anchored.lastIndex = pos;
2688
+ const match = anchored.exec(input);
2689
+ if (match === null) {
2690
+ ctx._fx = expected;
2691
+ return ~pos;
2692
+ }
2693
+ ctx._sv = match[0];
2694
+ return pos + match[0].length;
2695
+ };
2604
2696
  return {
2605
2697
  _tag: "regex",
2606
2698
  _meta: meta,
2607
2699
  _def: def,
2700
+ _stickyRegex: anchored,
2701
+ _parseScalar: parseScalar,
2608
2702
  parse(input, pos, ctx) {
2609
2703
  const scanEnd = scan?.(input, pos);
2610
2704
  if (scanEnd !== void 0) {
@@ -2649,6 +2743,28 @@ function literal(value, opts = {}) {
2649
2743
  isTrivia: false
2650
2744
  };
2651
2745
  const canMatchNewline = meta.canMatchNewline;
2746
+ const parseScalar = !caseInsensitive && value.length === 1 ? (() => {
2747
+ const code = value.charCodeAt(0);
2748
+ return (input, pos, ctx) => {
2749
+ if (input.charCodeAt(pos) === code) {
2750
+ ctx._sv = value;
2751
+ return pos + 1;
2752
+ }
2753
+ ctx._fx = expected;
2754
+ return ~pos;
2755
+ };
2756
+ })() : (input, pos, ctx) => {
2757
+ const end = pos + value.length;
2758
+ if (end <= input.length) {
2759
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
2760
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
2761
+ ctx._sv = matchedValue;
2762
+ return end;
2763
+ }
2764
+ }
2765
+ ctx._fx = expected;
2766
+ return ~pos;
2767
+ };
2652
2768
  if (caseInsensitive) {
2653
2769
  const upper = value.toUpperCase();
2654
2770
  const lower = value.toLowerCase();
@@ -2689,6 +2805,7 @@ function literal(value, opts = {}) {
2689
2805
  _tag: "literal",
2690
2806
  _meta: meta,
2691
2807
  _def: { tag: "literal", value, caseInsensitive },
2808
+ _parseScalar: parseScalar,
2692
2809
  parse: parse2
2693
2810
  };
2694
2811
  }
@@ -2756,16 +2873,26 @@ function keywords(words, opts = {}) {
2756
2873
  caseInsensitive: opts.caseInsensitive ?? false,
2757
2874
  boundary: opts.boundary
2758
2875
  };
2876
+ const expected = directTerminalFailureExpected(def);
2877
+ const parseScalar = (input, pos, ctx) => {
2878
+ re.lastIndex = pos;
2879
+ const m = re.exec(input);
2880
+ if (m === null) {
2881
+ ctx._fx = expected;
2882
+ return ~pos;
2883
+ }
2884
+ ctx._sv = m[0];
2885
+ return pos + m[0].length;
2886
+ };
2759
2887
  return {
2760
2888
  _tag: "keywords",
2761
2889
  _meta: meta,
2762
2890
  _def: def,
2891
+ _parseScalar: parseScalar,
2763
2892
  parse(input, pos, ctx) {
2764
2893
  re.lastIndex = pos;
2765
2894
  const m = re.exec(input);
2766
- if (m === null || m.index !== pos) {
2767
- return failAt(ctx, directTerminalFailureExpected(def), pos);
2768
- }
2895
+ if (m === null) return failAt(ctx, directTerminalFailureExpected(def), pos);
2769
2896
  const value = m[0];
2770
2897
  const span = { start: pos, end: pos + value.length };
2771
2898
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
@@ -2796,6 +2923,9 @@ var fastScanner3;
2796
2923
  function needsDeferredTriviaCommit(ctx) {
2797
2924
  return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
2798
2925
  }
2926
+ function needsTriviaRollback(ctx) {
2927
+ return ctx._cstBuf !== void 0 || ctx._cstLeaves !== void 0 || ctx._cstRawChildren !== void 0 || ctx._cstTriviaLog !== void 0 || ctx._fields !== void 0 || ctx._errors !== void 0 || ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0;
2928
+ }
2799
2929
  function saveTriviaMark(ctx) {
2800
2930
  const m = saveCstMark(ctx);
2801
2931
  return {
@@ -2912,7 +3042,8 @@ function advanceTrivia(input, cur, ctx) {
2912
3042
  const triviaP = ctx.trivia;
2913
3043
  if (!triviaP) return cur;
2914
3044
  if (!ctx.trackLines) {
2915
- const fast2 = fastTriviaScanner(triviaP);
3045
+ const classified = triviaP._meta.triviaScanner;
3046
+ const fast2 = classified === void 0 ? fastTriviaScanner(triviaP) : classified;
2916
3047
  if (fast2) return fast2(input, cur);
2917
3048
  if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
2918
3049
  const tr2 = triviaP.parse(input, cur, createDetachedParseContext(ctx.trackLines, ctx.state));
@@ -3517,6 +3648,7 @@ function sequence(...parsers) {
3517
3648
  (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
3518
3649
  }
3519
3650
  const hasAdjacency = adjacency !== null;
3651
+ const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
3520
3652
  const meta = {
3521
3653
  // Union through the nullable prefix — a leading optional()/many() lets a later
3522
3654
  // term's first char start the sequence. Just `parsers[0]` under-approximates.
@@ -3525,165 +3657,96 @@ function sequence(...parsers) {
3525
3657
  isTrivia: false
3526
3658
  };
3527
3659
  const def = { tag: "sequence", parsers };
3528
- let followSentinels;
3529
- function parseTolerant(input, pos, ctx) {
3530
- followSentinels ??= parsers.map((_, i) => {
3531
- const fs = parsers.slice(i + 1).reduce(
3532
- (acc, p) => union(acc, firstSetOf(p)),
3533
- { kind: "empty" }
3534
- );
3535
- return firstSetSentinel(fs);
3536
- });
3537
- const values = def.valueUnused ? void 0 : [];
3660
+ const scalarParsers = parsers.map(scalarOf);
3661
+ const parseScalar = (input, pos, ctx) => {
3538
3662
  let cur = pos;
3539
- const inheritedSync = ctx._sync;
3540
- try {
3541
- for (let i = 0; i < parsers.length; i++) {
3542
- ctx._sync = followSentinels[i] ?? inheritedSync;
3543
- const adj = adjacency === null ? null : adjacency[i];
3544
- if (adj) {
3545
- if (!holdsAdjacency(input, cur, ctx, adj)) return adjacencyFail(cur, adj);
3546
- if (values !== void 0) values.push(null);
3547
- continue;
3548
- }
3549
- if (ctx.trivia && i > 0) {
3550
- const mTlog = cstTlLen(ctx);
3551
- const mLog = ctx._triviaLog?.length ?? 0;
3552
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
3553
- let scanEnd;
3554
- if (needsDeferredTriviaCommit(ctx)) {
3555
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
3556
- } else {
3557
- scanEnd = advanceTrivia(input, cur, ctx);
3558
- }
3559
- const scanTlog = cstTlLen(ctx);
3560
- const scanLog = ctx._triviaLog?.length ?? 0;
3561
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
3562
- const result2 = parsers[i].parse(input, scanEnd, ctx);
3563
- if (!result2.ok) return result2;
3564
- if (result2.span.end > scanEnd) cur = result2.span.end;
3565
- else rollbackScannedTriviaAt(
3566
- ctx,
3567
- mTlog,
3568
- scanTlog,
3569
- mLog,
3570
- scanLog,
3571
- mRootLog,
3572
- scanRootLog
3573
- );
3574
- if (values !== void 0) values.push(result2.value);
3575
- continue;
3576
- }
3577
- const result = parsers[i].parse(input, cur, ctx);
3578
- if (!result.ok) return result;
3579
- if (values !== void 0) values.push(result.value);
3580
- cur = result.span.end;
3663
+ if (strictArity !== 0) {
3664
+ cur = scalarParsers[0](input, cur, ctx);
3665
+ if (cur < 0) return cur;
3666
+ const first = ctx._sv;
3667
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
3668
+ cur = scalarParsers[1](input, cur, ctx);
3669
+ if (cur < 0) return cur;
3670
+ const second = ctx._sv;
3671
+ if (strictArity === 2) {
3672
+ ctx._sv = def.valueUnused ? void 0 : [first, second];
3673
+ return cur;
3581
3674
  }
3582
- } finally {
3583
- ctx._sync = inheritedSync;
3675
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
3676
+ cur = scalarParsers[2](input, cur, ctx);
3677
+ if (cur < 0) return cur;
3678
+ ctx._sv = def.valueUnused ? void 0 : [first, second, ctx._sv];
3679
+ return cur;
3584
3680
  }
3585
- return {
3586
- ok: true,
3587
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
3588
- value: values ?? void 0,
3589
- span: { start: pos, end: cur }
3590
- };
3591
- }
3592
- function parseAdjacent(input, pos, ctx) {
3593
3681
  const values = def.valueUnused ? void 0 : [];
3594
- let cur = pos;
3595
- for (let i = 0; i < parsers.length; i++) {
3596
- const adj = adjacency[i];
3597
- if (adj) {
3598
- if (!holdsAdjacency(input, cur, ctx, adj)) return adjacencyFail(cur, adj);
3599
- if (values !== void 0) values.push(null);
3600
- continue;
3601
- }
3602
- if (ctx.trivia && i > 0) {
3603
- let scanEnd;
3604
- const mTlog = cstTlLen(ctx);
3605
- const mLog = ctx._triviaLog?.length ?? 0;
3606
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
3607
- if (needsDeferredTriviaCommit(ctx)) {
3608
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
3609
- } else {
3610
- scanEnd = advanceTrivia(input, cur, ctx);
3611
- }
3612
- const scanTlog = cstTlLen(ctx);
3613
- const scanLog = ctx._triviaLog?.length ?? 0;
3614
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
3615
- const result2 = parsers[i].parse(input, scanEnd, ctx);
3616
- if (!result2.ok) return result2;
3617
- if (result2.span.end > scanEnd) cur = result2.span.end;
3618
- else rollbackScannedTriviaAt(
3619
- ctx,
3620
- mTlog,
3621
- scanTlog,
3622
- mLog,
3623
- scanLog,
3624
- mRootLog,
3625
- scanRootLog
3626
- );
3627
- if (values !== void 0) values.push(result2.value);
3628
- continue;
3629
- }
3630
- const result = parsers[i].parse(input, cur, ctx);
3631
- if (!result.ok) return result;
3632
- if (values !== void 0) values.push(result.value);
3633
- cur = result.span.end;
3634
- }
3635
- return {
3636
- ok: true,
3637
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
3638
- value: values ?? void 0,
3639
- span: { start: pos, end: cur }
3640
- };
3641
- }
3682
+ for (let i = 0; i < scalarParsers.length; i++) {
3683
+ let termPos = cur;
3684
+ if (i > 0 && ctx.trivia) termPos = advanceTrivia(input, cur, ctx);
3685
+ const end = scalarParsers[i](input, termPos, ctx);
3686
+ if (end < 0) return end;
3687
+ if (values !== void 0) values.push(ctx._sv);
3688
+ if (end > termPos || termPos === cur) cur = end;
3689
+ }
3690
+ ctx._sv = values;
3691
+ return cur;
3692
+ };
3693
+ let followSentinels;
3642
3694
  return {
3643
3695
  _tag: "sequence",
3644
3696
  _meta: meta,
3645
3697
  _def: def,
3698
+ _parseScalar: hasAdjacency ? void 0 : parseScalar,
3646
3699
  parse(input, pos, ctx) {
3647
- if (ctx._tolerant) return parseTolerant(input, pos, ctx);
3648
- if (hasAdjacency) return parseAdjacent(input, pos, ctx);
3700
+ const tolerant = ctx._tolerant === true;
3701
+ if (tolerant) {
3702
+ followSentinels ??= parsers.map((_, i) => firstSetSentinel(
3703
+ parsers.slice(i + 1).reduce(
3704
+ (set, parser2) => union(set, firstSetOf(parser2)),
3705
+ { kind: "empty" }
3706
+ )
3707
+ ));
3708
+ }
3709
+ const inheritedSync = ctx._sync;
3710
+ const deferredTrivia = needsDeferredTriviaCommit(ctx);
3649
3711
  const values = def.valueUnused ? void 0 : [];
3650
3712
  let cur = pos;
3651
- for (let i = 0; i < parsers.length; i++) {
3652
- if (ctx.trivia && i > 0) {
3653
- let scanEnd;
3654
- const mTlog = cstTlLen(ctx);
3655
- const mLog = ctx._triviaLog?.length ?? 0;
3656
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
3657
- if (needsDeferredTriviaCommit(ctx)) {
3658
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
3659
- } else {
3660
- scanEnd = advanceTrivia(input, cur, ctx);
3713
+ try {
3714
+ for (let i = 0; i < parsers.length; i++) {
3715
+ if (tolerant) ctx._sync = followSentinels[i] ?? inheritedSync;
3716
+ const assertion = adjacency?.[i];
3717
+ if (assertion) {
3718
+ if (!holdsAdjacency(input, cur, ctx, assertion)) return adjacencyFail(cur, assertion);
3719
+ if (values !== void 0) values.push(null);
3720
+ continue;
3661
3721
  }
3662
- const scanTlog = cstTlLen(ctx);
3663
- const scanLog = ctx._triviaLog?.length ?? 0;
3664
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
3665
- const result2 = parsers[i].parse(input, scanEnd, ctx);
3666
- if (!result2.ok) return result2;
3667
- if (result2.span.end > scanEnd) {
3668
- cur = result2.span.end;
3669
- } else {
3722
+ let termPos = cur;
3723
+ const markTlog = deferredTrivia ? cstTlLen(ctx) : 0;
3724
+ const markLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
3725
+ const markRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
3726
+ if (ctx.trivia && i > 0) {
3727
+ termPos = deferredTrivia ? commitTriviaScan(scanTriviaCompact(input, cur, ctx)) : advanceTrivia(input, cur, ctx);
3728
+ }
3729
+ const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
3730
+ const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
3731
+ const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
3732
+ const result = parsers[i].parse(input, termPos, ctx);
3733
+ if (!result.ok) return result;
3734
+ if (values !== void 0) values.push(result.value);
3735
+ if (result.span.end > termPos || termPos === cur) cur = result.span.end;
3736
+ else if (deferredTrivia) {
3670
3737
  rollbackScannedTriviaAt(
3671
3738
  ctx,
3672
- mTlog,
3739
+ markTlog,
3673
3740
  scanTlog,
3674
- mLog,
3741
+ markLog,
3675
3742
  scanLog,
3676
- mRootLog,
3743
+ markRootLog,
3677
3744
  scanRootLog
3678
3745
  );
3679
3746
  }
3680
- if (values !== void 0) values.push(result2.value);
3681
- continue;
3682
3747
  }
3683
- const result = parsers[i].parse(input, cur, ctx);
3684
- if (!result.ok) return result;
3685
- if (values !== void 0) values.push(result.value);
3686
- cur = result.span.end;
3748
+ } finally {
3749
+ if (tolerant) ctx._sync = inheritedSync;
3687
3750
  }
3688
3751
  return {
3689
3752
  ok: true,
@@ -3815,7 +3878,8 @@ function choice(...args) {
3815
3878
  disjoint
3816
3879
  };
3817
3880
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
3818
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
3881
+ const ordered = strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix";
3882
+ const autoNot = !disjoint && !hasGates && ordered ? computeAutoNot(parsers) : parsers.map(() => null);
3819
3883
  let greedyLitMap = null;
3820
3884
  let sortedParsers = null;
3821
3885
  const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
@@ -3829,6 +3893,37 @@ function choice(...args) {
3829
3893
  } else if (strategy?.tag === "literalsLongestFirst") {
3830
3894
  sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
3831
3895
  }
3896
+ const scalarParsers = parsers.map(scalarOf);
3897
+ const scalarEligible = !hasGates && parsers.every((p) => p._parseScalar !== void 0) && (disjoint || ordered);
3898
+ const scalarExpected = parsers.flatMap(deriveExpected);
3899
+ const parseScalar = (input, pos, ctx) => {
3900
+ if (disjoint) {
3901
+ const code = pos < input.length ? input.codePointAt(pos) : -1;
3902
+ let idx = code >= 0 && code < 128 ? asciiDispatch[code] : -1;
3903
+ if (idx < 0 && code >= 0) {
3904
+ for (let i = 0; i < parsers.length; i++) {
3905
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
3906
+ idx = i;
3907
+ break;
3908
+ }
3909
+ }
3910
+ }
3911
+ if (idx >= 0) {
3912
+ const gate = gates[idx];
3913
+ if (!gate || gate(ctx.state)) return scalarParsers[idx](input, pos, ctx);
3914
+ ctx._fx = deriveExpected(parsers[idx]);
3915
+ return ~pos;
3916
+ }
3917
+ } else {
3918
+ for (let i = 0; i < scalarParsers.length; i++) {
3919
+ if (gates[i] && !gates[i](ctx.state)) continue;
3920
+ const end = scalarParsers[i](input, pos, ctx);
3921
+ if (end >= 0 && (!autoNot[i] || !autoNotFires(input, end, autoNot[i]))) return end;
3922
+ }
3923
+ }
3924
+ ctx._fx = scalarExpected;
3925
+ return ~pos;
3926
+ };
3832
3927
  return {
3833
3928
  _tag: "choice",
3834
3929
  _meta: meta,
@@ -3840,6 +3935,7 @@ function choice(...args) {
3840
3935
  strategy: strategy ?? { tag: "firstMatch" },
3841
3936
  autoNot
3842
3937
  },
3938
+ _parseScalar: scalarEligible ? parseScalar : void 0,
3843
3939
  parse(input, pos, ctx) {
3844
3940
  const expected = [];
3845
3941
  let expectedAt = pos;
@@ -4371,8 +4467,8 @@ function attempt(parser2) {
4371
4467
  }
4372
4468
 
4373
4469
  // src/combinators/repeat.ts
4374
- function repItem(combinator, input, cur, ctx, guardable, mandatory) {
4375
- const mark = saveTriviaMark(ctx);
4470
+ function repItem(combinator, input, cur, ctx, guardable, mandatory, rollbackNeeded) {
4471
+ const mark = rollbackNeeded ? saveTriviaMark(ctx) : void 0;
4376
4472
  let pos = cur;
4377
4473
  if (ctx.trivia) {
4378
4474
  if (needsDeferredTriviaCommit(ctx)) {
@@ -4382,24 +4478,24 @@ function repItem(combinator, input, cur, ctx, guardable, mandatory) {
4382
4478
  }
4383
4479
  }
4384
4480
  if (pos >= input.length && !mandatory) {
4385
- rollbackTrivia(ctx, mark);
4481
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4386
4482
  return "stop";
4387
4483
  }
4388
4484
  if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
4389
- rollbackTrivia(ctx, mark);
4485
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4390
4486
  return "stop";
4391
4487
  }
4392
4488
  const result = combinator.parse(input, pos, ctx);
4393
4489
  if (!result.ok) {
4394
- rollbackTrivia(ctx, mark);
4490
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4395
4491
  if (result.committed) return { fail: result, failPos: pos };
4396
4492
  return { fail: result, failPos: pos };
4397
4493
  }
4398
4494
  if (result.span.end === pos && !mandatory) {
4399
- rollbackTrivia(ctx, mark);
4495
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4400
4496
  return "stop";
4401
4497
  }
4402
- return { value: result.value, end: result.span.end };
4498
+ return result;
4403
4499
  }
4404
4500
  function resolveBounds(what, opts) {
4405
4501
  const min = opts.min ?? 0;
@@ -4412,6 +4508,21 @@ function resolveBounds(what, opts) {
4412
4508
  if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
4413
4509
  return { min, max };
4414
4510
  }
4511
+ function repeatScalarTail(combinator, child, input, cur, ctx, guardable, values, remaining) {
4512
+ while (cur < input.length && remaining > 0) {
4513
+ let itemPos = cur;
4514
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
4515
+ if (itemPos >= input.length) break;
4516
+ if (guardable && !startsFirstSet(combinator, input, itemPos)) break;
4517
+ const end = child(input, itemPos, ctx);
4518
+ if (end <= itemPos) break;
4519
+ if (values !== void 0) values.push(ctx._sv);
4520
+ cur = end;
4521
+ remaining--;
4522
+ }
4523
+ ctx._sv = values;
4524
+ return cur;
4525
+ }
4415
4526
  function many(combinator, opts = {}) {
4416
4527
  const { min, max } = resolveBounds("many()", opts);
4417
4528
  if (min >= 1) return atLeast(combinator, min, max);
@@ -4423,17 +4534,21 @@ function many(combinator, opts = {}) {
4423
4534
  const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
4424
4535
  let expected;
4425
4536
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4537
+ const scalarChild = scalarOf(combinator);
4538
+ const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
4426
4539
  return {
4427
4540
  _tag: "many",
4428
4541
  _meta: meta,
4429
4542
  _def: def,
4543
+ _parseScalar: parseScalar,
4430
4544
  parse(input, pos, ctx) {
4431
4545
  const values = def.valueUnused ? void 0 : [];
4546
+ const rollbackNeeded = needsTriviaRollback(ctx);
4432
4547
  let cur = pos;
4433
4548
  let count = 0;
4434
4549
  while (cur < input.length) {
4435
4550
  if (count >= max) break;
4436
- const item = repItem(combinator, input, cur, ctx, guardable, false);
4551
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
4437
4552
  if (item === "stop") break;
4438
4553
  if ("fail" in item) {
4439
4554
  if (!item.fail.ok && item.fail.committed) return item.fail;
@@ -4450,7 +4565,7 @@ function many(combinator, opts = {}) {
4450
4565
  }
4451
4566
  if (values !== void 0) values.push(item.value);
4452
4567
  count++;
4453
- cur = item.end;
4568
+ cur = item.span.end;
4454
4569
  }
4455
4570
  return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
4456
4571
  }
@@ -4468,30 +4583,47 @@ function atLeast(combinator, min, max) {
4468
4583
  const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
4469
4584
  let expected;
4470
4585
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4586
+ const scalarChild = scalarOf(combinator);
4587
+ const parseScalar = (input, pos, ctx) => {
4588
+ let cur = scalarChild(input, pos, ctx);
4589
+ if (cur < 0) return cur;
4590
+ const values = def.valueUnused ? void 0 : [ctx._sv];
4591
+ for (let count = 1; count < min; count++) {
4592
+ let itemPos = cur;
4593
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
4594
+ const end = scalarChild(input, itemPos, ctx);
4595
+ if (end < 0) return ~cur;
4596
+ cur = end;
4597
+ if (values !== void 0) values.push(ctx._sv);
4598
+ }
4599
+ return repeatScalarTail(combinator, scalarChild, input, cur, ctx, guardable, values, max - min);
4600
+ };
4471
4601
  return {
4472
4602
  _tag: "oneOrMore",
4473
4603
  _meta: meta,
4474
4604
  _def: def,
4605
+ _parseScalar: parseScalar,
4475
4606
  parse(input, pos, ctx) {
4476
4607
  const first = combinator.parse(input, pos, ctx);
4477
4608
  if (!first.ok) return first;
4478
4609
  const values = def.valueUnused ? void 0 : [first.value];
4610
+ const rollbackNeeded = needsTriviaRollback(ctx);
4479
4611
  let cur = first.span.end;
4480
4612
  let count = 1;
4481
4613
  while (count < min) {
4482
- const item = repItem(combinator, input, cur, ctx, guardable, true);
4614
+ const item = repItem(combinator, input, cur, ctx, guardable, true, rollbackNeeded);
4483
4615
  if (item === "stop" || "fail" in item) {
4484
4616
  if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
4485
4617
  expected ??= deriveExpected(combinator);
4486
4618
  return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
4487
4619
  }
4488
4620
  if (values !== void 0) values.push(item.value);
4489
- cur = item.end;
4621
+ cur = item.span.end;
4490
4622
  count++;
4491
4623
  }
4492
4624
  while (cur < input.length) {
4493
4625
  if (count >= max) break;
4494
- const item = repItem(combinator, input, cur, ctx, guardable, false);
4626
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
4495
4627
  if (item === "stop") break;
4496
4628
  if ("fail" in item) {
4497
4629
  if (!item.fail.ok && item.fail.committed) return item.fail;
@@ -4508,7 +4640,7 @@ function atLeast(combinator, min, max) {
4508
4640
  }
4509
4641
  if (values !== void 0) values.push(item.value);
4510
4642
  count++;
4511
- cur = item.end;
4643
+ cur = item.span.end;
4512
4644
  }
4513
4645
  return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
4514
4646
  }
@@ -4521,10 +4653,22 @@ function optional(combinator) {
4521
4653
  isTrivia: false
4522
4654
  };
4523
4655
  const firstSetSkippable = !matchesEmpty(combinator);
4656
+ const scalarChild = scalarOf(combinator);
4657
+ const parseScalar = (input, pos, ctx) => {
4658
+ if (firstSetSkippable && !startsFirstSet(combinator, input, pos)) {
4659
+ ctx._sv = null;
4660
+ return pos;
4661
+ }
4662
+ const end = scalarChild(input, pos, ctx);
4663
+ if (end >= 0) return end;
4664
+ ctx._sv = null;
4665
+ return pos;
4666
+ };
4524
4667
  return {
4525
4668
  _tag: "optional",
4526
4669
  _meta: meta,
4527
4670
  _def: { tag: "optional", parser: combinator },
4671
+ _parseScalar: parseScalar,
4528
4672
  parse(input, pos, ctx) {
4529
4673
  if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
4530
4674
  return { ok: true, value: null, span: { start: pos, end: pos } };
@@ -4558,6 +4702,43 @@ function sepBy(combinator, separatorArg, opts = {}) {
4558
4702
  expected ??= deriveExpected(combinator);
4559
4703
  return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
4560
4704
  };
4705
+ const scalarExpected = deriveExpected(combinator);
4706
+ const scalarChild = scalarOf(combinator);
4707
+ const scalarSeparator = scalarOf(separator);
4708
+ const parseScalar = (input, pos, ctx) => {
4709
+ let cur = scalarChild(input, pos, ctx);
4710
+ if (cur < 0) {
4711
+ if (min >= 1) {
4712
+ ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
4713
+ return ~pos;
4714
+ }
4715
+ ctx._sv = [];
4716
+ return pos;
4717
+ }
4718
+ const values = [ctx._sv];
4719
+ while (values.length < max && (cur < input.length || values.length < min)) {
4720
+ const beforeSeparator = cur;
4721
+ let separatorPos = cur;
4722
+ if (ctx.trivia) separatorPos = advanceTrivia(input, cur, ctx);
4723
+ const separatorEnd = scalarSeparator(input, separatorPos, ctx);
4724
+ if (separatorEnd < 0) break;
4725
+ let itemPos = separatorEnd;
4726
+ if (ctx.trivia) itemPos = advanceTrivia(input, itemPos, ctx);
4727
+ const itemEnd = scalarChild(input, itemPos, ctx);
4728
+ if (itemEnd < 0) {
4729
+ cur = trailing === "allow" ? separatorEnd : beforeSeparator;
4730
+ break;
4731
+ }
4732
+ values.push(ctx._sv);
4733
+ cur = itemEnd;
4734
+ }
4735
+ if (values.length < min) {
4736
+ ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
4737
+ return ~cur;
4738
+ }
4739
+ ctx._sv = values;
4740
+ return cur;
4741
+ };
4561
4742
  return {
4562
4743
  _tag: "sepBy",
4563
4744
  _meta: meta,
@@ -4573,6 +4754,7 @@ function sepBy(combinator, separatorArg, opts = {}) {
4573
4754
  // this option surface already; parity is held by test/parity, not by hope.
4574
4755
  ...keepSeparators ? { keepSeparators: true } : {}
4575
4756
  },
4757
+ _parseScalar: keepSeparators ? void 0 : parseScalar,
4576
4758
  parse(input, pos, ctx) {
4577
4759
  const first = combinator.parse(input, pos, ctx);
4578
4760
  const values = [];
@@ -5155,11 +5337,54 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
5155
5337
  }
5156
5338
 
5157
5339
  // src/combinators/map.ts
5340
+ var JSON_STRING_BODY = String.raw`(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*`;
5158
5341
  function transform(combinator, fn) {
5342
+ const observesSpan = fn.length > 1;
5343
+ const sequenceDef = combinator._def.tag === "sequence" ? combinator._def : void 0;
5344
+ const terms = sequenceDef?.parsers;
5345
+ const inner = terms?.[1];
5346
+ const fused = terms?.length === 3 && terms[0]._def.tag === "literal" && terms[0]._def.value === '"' && !terms[0]._def.caseInsensitive && inner._def.tag === "regex" && inner._def.source === JSON_STRING_BODY && inner._def.flags === "" && terms[2]._def.tag === "literal" && terms[2]._def.value === '"' && !terms[2]._def.caseInsensitive && inner._stickyRegex !== void 0;
5347
+ const child = scalarOf(combinator);
5348
+ const parseScalar = fused ? (() => {
5349
+ const body = inner._stickyRegex;
5350
+ const tuple = ['"', "", '"'];
5351
+ const expected = [JSON.stringify('"')];
5352
+ return (input, pos, ctx) => {
5353
+ if (ctx.trivia !== void 0) {
5354
+ const end2 = child(input, pos, ctx);
5355
+ if (end2 < 0) return end2;
5356
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end: end2 }) : fn(ctx._sv);
5357
+ return end2;
5358
+ }
5359
+ if (input.charCodeAt(pos) !== 34) {
5360
+ ctx._fx = expected;
5361
+ return ~pos;
5362
+ }
5363
+ body.lastIndex = pos + 1;
5364
+ const match = body.exec(input);
5365
+ const close = pos + 1 + match[0].length;
5366
+ if (input.charCodeAt(close) !== 34) {
5367
+ ctx._fx = expected;
5368
+ return ~close;
5369
+ }
5370
+ tuple[1] = match[0];
5371
+ const end = close + 1;
5372
+ ctx._sv = observesSpan ? fn(tuple, { start: pos, end }) : fn(tuple);
5373
+ return end;
5374
+ };
5375
+ })() : /* @__PURE__ */ (() => {
5376
+ return (input, pos, ctx) => {
5377
+ const end = child(input, pos, ctx);
5378
+ if (end < 0) return end;
5379
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end }) : fn(ctx._sv);
5380
+ return end;
5381
+ };
5382
+ })();
5159
5383
  return {
5160
5384
  _tag: "transform",
5161
5385
  _meta: combinator._meta,
5162
5386
  _def: { tag: "transform", parser: combinator, fn },
5387
+ _parseScalar: parseScalar,
5163
5388
  parse(input, pos, ctx) {
5164
5389
  const result = combinator.parse(input, pos, ctx);
5165
5390
  if (!result.ok) return result;
@@ -5174,9 +5399,11 @@ function trivia(combinator) {
5174
5399
  _meta: {
5175
5400
  ...combinator._meta,
5176
5401
  isTrivia: true,
5402
+ triviaScanner: fastTriviaScanner(combinator),
5177
5403
  ...kindLabels ? { triviaKindLabels: kindLabels } : {}
5178
5404
  },
5179
5405
  _def: { tag: "trivia", parser: combinator },
5406
+ _parseScalar: scalarOf(combinator),
5180
5407
  parse: combinator.parse.bind(combinator)
5181
5408
  };
5182
5409
  }
@@ -7078,6 +7305,22 @@ var Encoder = class {
7078
7305
  choiceRollbackMasks = /* @__PURE__ */ new Map();
7079
7306
  nonCommittingChoiceSites = /* @__PURE__ */ new Set();
7080
7307
  failureRollbackCleanSites = /* @__PURE__ */ new Set();
7308
+ /**
7309
+ * `hasSideWriter` memo for `failureNeedsRollback`, shared across every call
7310
+ * for the lifetime of this `Encoder`. A cached entry is written only when
7311
+ * `complete` is true, which means the answer is a path-independent fact
7312
+ * about that combinator node — it does not depend on which root's traversal
7313
+ * asked, only on the node's own reachable subgraph. Scoping the memo per call
7314
+ * (as a local inside `failureNeedsRollback`) was correct but discarded that
7315
+ * fact after every arm, so a grammar with many ordered-choice arms / optional
7316
+ * / attempt sites re-walked the SAME shared rule graph from scratch for each
7317
+ * site — O(sites × graph size) instead of O(graph size). Measured on the
7318
+ * scss-parser grammar (composed CSS-base superset, the deepest/largest of the
7319
+ * four grammars): 47-50s for one `compileRuleMap` with the per-call memo,
7320
+ * versus the whole macro transform completing in under a second once this
7321
+ * memo is hoisted here.
7322
+ */
7323
+ sideWriterMemo = /* @__PURE__ */ new Map();
7081
7324
  labels = void 0;
7082
7325
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
7083
7326
  activeTrivia = void 0;
@@ -7503,13 +7746,13 @@ var Encoder = class {
7503
7746
  const refName = p._ruleName;
7504
7747
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
7505
7748
  const root = winner !== void 0 && !winnerWrapsReference2(winner, p) ? winner : p;
7506
- const sideWriterMemo = /* @__PURE__ */ new Map();
7507
- const hasSideWriter = (current, seen = /* @__PURE__ */ new Set()) => {
7749
+ const sideWriterMemo = this.sideWriterMemo;
7750
+ const seen = /* @__PURE__ */ new Set();
7751
+ const hasSideWriter = (current) => {
7508
7752
  const cached = sideWriterMemo.get(current);
7509
7753
  if (cached !== void 0) return { found: cached, complete: true };
7510
7754
  if (seen.has(current)) return { found: false, complete: false };
7511
- const next = new Set(seen);
7512
- next.add(current);
7755
+ seen.add(current);
7513
7756
  const def = current._def;
7514
7757
  let result;
7515
7758
  switch (def.tag) {
@@ -7524,11 +7767,11 @@ var Encoder = class {
7524
7767
  const name = current._ruleName;
7525
7768
  const resolvedWinner = name === void 0 ? void 0 : this.winners?.[name];
7526
7769
  if (resolvedWinner !== void 0 && !winnerWrapsReference2(resolvedWinner, current)) {
7527
- result = hasSideWriter(resolvedWinner, next);
7770
+ result = hasSideWriter(resolvedWinner);
7528
7771
  break;
7529
7772
  }
7530
7773
  try {
7531
- result = hasSideWriter(def.thunk(), next);
7774
+ result = hasSideWriter(def.thunk());
7532
7775
  } catch {
7533
7776
  result = { found: true, complete: true };
7534
7777
  }
@@ -7538,7 +7781,7 @@ var Encoder = class {
7538
7781
  let complete = true;
7539
7782
  result = { found: false, complete: true };
7540
7783
  for (const child of childrenOf(def)) {
7541
- const childResult = hasSideWriter(child, next);
7784
+ const childResult = hasSideWriter(child);
7542
7785
  if (childResult.found) {
7543
7786
  result = { found: true, complete: true };
7544
7787
  break;
@@ -7548,6 +7791,7 @@ var Encoder = class {
7548
7791
  if (!result.found) result = { found: false, complete };
7549
7792
  }
7550
7793
  }
7794
+ seen.delete(current);
7551
7795
  if (result.found || result.complete) sideWriterMemo.set(current, result.found);
7552
7796
  return result;
7553
7797
  };
@@ -9892,6 +10136,9 @@ if(ctx._rootTriviaLog!==undefined&&ctx._rootTriviaLog.length!==rt)ctx._rootTrivi
9892
10136
  function q(s) {
9893
10137
  return JSON.stringify(s);
9894
10138
  }
10139
+ function isAsciiOnlyClass(cls) {
10140
+ return cls !== null && cls !== void 0 && cls.hi.length === 0;
10141
+ }
9895
10142
  var NO_SINKS = { fd: false, er: false };
9896
10143
  function sinkSlots(t2, s) {
9897
10144
  return `${s.fd ? `,${t2}fd=0` : ""}${s.er ? `,${t2}er=0` : ""}`;
@@ -10438,12 +10685,13 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
10438
10685
  return `${head}EC.e=pos;return ''}`;
10439
10686
  case OP_GATE: {
10440
10687
  const child = link(code[ip + 2]);
10441
- const ci = classes.push([t2.cc[code[ip + 1]]]) - 1;
10688
+ const resolved = t2.cc[code[ip + 1]];
10689
+ const ci = classes.push([resolved]) - 1;
10442
10690
  classPlan.push([code[ip + 1]]);
10443
10691
  const cls = hoist("cl", `CLS[${ci}][0]`);
10444
10692
  const xf = fxRef(code[ip + 3]);
10445
10693
  return `${head}
10446
- if(!classHas(${cls},lead(input,pos))){ctx._fe=pos;ctx._fx=${xf};return FAIL}
10694
+ if(${staticBuild && isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
10447
10695
  return ${child}(input,pos,ctx)
10448
10696
  }`;
10449
10697
  }
@@ -10786,10 +11034,11 @@ return v
10786
11034
  const asc = hoist("as", `DISP[${di}].ascii`);
10787
11035
  const hiArr = table.hi;
10788
11036
  const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join("\n");
11037
+ const directAscii = staticBuild && table.hi.length === 0;
10789
11038
  return `${head}
10790
- const c=lead(input,pos)
11039
+ const c=${directAscii ? "input.charCodeAt(pos)" : "lead(input,pos)"}
10791
11040
  let arm=-1
10792
- if(c>=0&&c<128){const a=${asc}[c];if(a!==0)arm=a-1}
11041
+ if(${directAscii ? "c<128" : "c>=0&&c<128"}){const a=${asc}[c];if(a!==0)arm=a-1}
10793
11042
  ${hiArr.length === 0 ? "" : `else if(c>=128){const h=${hoist("hi", `DISP[${di}].hi`)}
10794
11043
  for(let i=0;i<h.length;i+=3){if(c>=h[i]&&c<=h[i+1]){arm=h[i+2];break}}}`}
10795
11044
  if(arm>=0){
@@ -10811,7 +11060,9 @@ return FAIL
10811
11060
  const di = code[ip + 1];
10812
11061
  const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]));
10813
11062
  const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null);
10814
- const gateRefs = Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
11063
+ const maskable = n <= 32;
11064
+ const directAsciiMask = staticBuild && maskable && gates.every(isAsciiOnlyClass);
11065
+ const gateRefs = directAsciiMask ? [] : Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
10815
11066
  const tokenCandidate = tokenChoiceCandidates.get(ip);
10816
11067
  const pretests = Array.from({ length: n }, (_, i) => {
10817
11068
  if (tokenCandidate?.arm === i) {
@@ -10831,7 +11082,6 @@ return FAIL
10831
11082
  const sequence6 = largeChoiceScalarSequences.get(code[base + i]);
10832
11083
  return sequence6 === void 0 ? void 0 : { sequence: scalarSequenceRef(sequence6) };
10833
11084
  });
10834
- const maskable = n <= 32;
10835
11085
  const maskName = maskable ? hoist("mk", `MASK[${masks.push(maskForClassRow(gates)) - 1}]`) : "";
10836
11086
  if (maskable) maskPlan.push(~di);
10837
11087
  const p = tmp();
@@ -10893,16 +11143,16 @@ ${merge}
10893
11143
  ctx._fe=pos;ctx._fx=acc??${choiceFx}
10894
11144
  return FAIL`;
10895
11145
  return `${head}
10896
- const c=lead(input,pos)
11146
+ const c=${directAsciiMask ? "input.charCodeAt(pos)" : "lead(input,pos)"}
10897
11147
  ${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ""}
10898
11148
  ${deepAt.map((at, i) => `let ${at},${deepFx[i]}`).join("\n")}
10899
11149
  ${maskable ? `if(c<128){
10900
- const bits=${maskName}[c<0?128:c]
11150
+ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
10901
11151
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
10902
11152
  ${lazyArms(true)}
10903
- }else{
11153
+ }${directAsciiMask ? "" : `else{
10904
11154
  ${lazyArms(false)}
10905
- }
11155
+ }`}
10906
11156
  ` : lazyArms(false)}
10907
11157
  ${finish}
10908
11158
  }`;
@@ -10953,7 +11203,7 @@ if(ctx._fc===true){${startFailureExact ? `if(best===pos){acc=${catchName}(${i},0
10953
11203
  ${rollbackFor(i)}
10954
11204
  }${routeMiss}${sequenceMiss}`;
10955
11205
  }).join("\n") : "";
10956
- const generalArms = arms.map((arm, i) => {
11206
+ const generalArms = directAsciiMask ? "" : arms.map((arm, i) => {
10957
11207
  const pretest = pretests[i];
10958
11208
  const decision = pretest?.token === void 0 && pretest?.sequence === void 0 ? "" : tmp();
10959
11209
  const scalarCondition = pretest?.scalar === void 0 ? "" : pretest.scalar.length === 1 ? `&&${pretest.scalar[0]}(input,pos)>=0` : `&&(${pretest.scalar.map((recognize) => `${recognize}(input,pos)>=0`).join("||")})`;
@@ -10983,12 +11233,12 @@ ${rollbackFor(i)}
10983
11233
  }${miss}`;
10984
11234
  }).join("\n");
10985
11235
  return `${head}
10986
- const c=lead(input,pos)
11236
+ const c=${directAsciiMask ? "input.charCodeAt(pos)" : "lead(input,pos)"}
10987
11237
  ${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ""}
10988
11238
  let acc
10989
11239
  let best=pos
10990
11240
  ${maskable ? `if(c<128){
10991
- const bits=${maskName}[c<0?128:c]
11241
+ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
10992
11242
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
10993
11243
  ${startFailureExact ? "" : "let prev=0"}
10994
11244
  ${maskArms}
@@ -11083,14 +11333,18 @@ return [key,v]
11083
11333
  const itemFx = reportItem ? fxRef(code[ip + 6]) : "EMPTY_FX";
11084
11334
  const collect = op === OP_REP;
11085
11335
  let itemCls;
11336
+ let itemAscii = false;
11086
11337
  if (!REC) {
11087
11338
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
11088
11339
  if (itemClassIndex >= 0) {
11089
- const ci = classes.push([t2.cc[itemClassIndex]]) - 1;
11340
+ const resolved = t2.cc[itemClassIndex];
11341
+ const ci = classes.push([resolved]) - 1;
11090
11342
  classPlan.push([itemClassIndex]);
11091
11343
  itemCls = hoist("ri", `CLS[${ci}][0]`);
11344
+ itemAscii = staticBuild && isAsciiOnlyClass(resolved);
11092
11345
  }
11093
11346
  }
11347
+ const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
11094
11348
  const skipBeforeFirst = sepIp < 0 && min === 0;
11095
11349
  const p = tmp();
11096
11350
  const via = sep !== void 0 ? "false" : min === 0 ? skipBeforeFirst ? "true" : "count>0" : `count>=${min}&&count>0`;
@@ -11150,7 +11404,7 @@ let count=0
11150
11404
  for(;;){
11151
11405
  ${max >= 0 ? `if(count>=${max})break
11152
11406
  ` : ""}${sep !== void 0 ? `if(count>0&&count>=${min}&&cur>=input.length)break
11153
- ` : ""}${itemCls !== void 0 && sep === void 0 ? `if(count>=${min}&&${p}gate&&${hasTrivia === "false" ? "true" : hasTrivia === "true" ? "false" : "!hasTrivia"}&&!classHas(${itemCls},lead(input,cur)))break
11407
+ ` : ""}${itemCls !== void 0 && sep === void 0 ? `if(count>=${min}&&${p}gate&&${hasTrivia === "false" ? "true" : hasTrivia === "true" ? "false" : "!hasTrivia"}&&${itemMiss("cur")})break
11154
11408
  ` : ""}let ${p}raw=0,${p}tl=0,${p}lv=0,${p}lg=0,${p}rt=0${sinks.fd ? `,${p}fd=0` : ""}${sinks.er ? `,${p}er=0` : ""}
11155
11409
  ${markBody}
11156
11410
  let itemStart=cur
@@ -11175,7 +11429,7 @@ itemStart=${hasTrivia === "false" ? "EC.e" : hasTrivia === "true" ? `${skip}(inp
11175
11429
  ${rb}
11176
11430
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
11177
11431
  }
11178
- ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&!classHas(${itemCls},lead(input,itemStart))){
11432
+ ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${itemMiss("itemStart")}){
11179
11433
  ${rb}
11180
11434
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
11181
11435
  }