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
@@ -522,6 +522,28 @@ function literal(value, opts = {}) {
522
522
  isTrivia: false
523
523
  };
524
524
  const canMatchNewline = meta.canMatchNewline;
525
+ const parseScalar = !caseInsensitive && value.length === 1 ? (() => {
526
+ const code = value.charCodeAt(0);
527
+ return (input, pos, ctx) => {
528
+ if (input.charCodeAt(pos) === code) {
529
+ ctx._sv = value;
530
+ return pos + 1;
531
+ }
532
+ ctx._fx = expected;
533
+ return ~pos;
534
+ };
535
+ })() : (input, pos, ctx) => {
536
+ const end = pos + value.length;
537
+ if (end <= input.length) {
538
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
539
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
540
+ ctx._sv = matchedValue;
541
+ return end;
542
+ }
543
+ }
544
+ ctx._fx = expected;
545
+ return ~pos;
546
+ };
525
547
  if (caseInsensitive) {
526
548
  const upper = value.toUpperCase();
527
549
  const lower = value.toLowerCase();
@@ -562,6 +584,7 @@ function literal(value, opts = {}) {
562
584
  _tag: "literal",
563
585
  _meta: meta,
564
586
  _def: { tag: "literal", value, caseInsensitive },
587
+ _parseScalar: parseScalar,
565
588
  parse
566
589
  };
567
590
  }
@@ -1018,10 +1041,32 @@ function regex(pattern, flags = "") {
1018
1041
  const canMatchNewline = raw.canMatchNewline;
1019
1042
  const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
1020
1043
  const def = { tag: "regex", source, flags: resolvedFlags };
1044
+ const expected = directTerminalFailureExpected(def);
1045
+ const parseScalar = (input, pos, ctx) => {
1046
+ const scanEnd = scan?.(input, pos);
1047
+ if (scanEnd !== void 0) {
1048
+ if (scanEnd === null) {
1049
+ ctx._fx = expected;
1050
+ return ~pos;
1051
+ }
1052
+ ctx._sv = input.slice(pos, scanEnd);
1053
+ return scanEnd;
1054
+ }
1055
+ anchored.lastIndex = pos;
1056
+ const match = anchored.exec(input);
1057
+ if (match === null) {
1058
+ ctx._fx = expected;
1059
+ return ~pos;
1060
+ }
1061
+ ctx._sv = match[0];
1062
+ return pos + match[0].length;
1063
+ };
1021
1064
  return {
1022
1065
  _tag: "regex",
1023
1066
  _meta: meta,
1024
1067
  _def: def,
1068
+ _stickyRegex: anchored,
1069
+ _parseScalar: parseScalar,
1025
1070
  parse(input, pos, ctx) {
1026
1071
  const scanEnd = scan?.(input, pos);
1027
1072
  if (scanEnd !== void 0) {
@@ -1063,6 +1108,7 @@ function createParseContext() {
1063
1108
  _fe: void 0,
1064
1109
  _fx: void 0,
1065
1110
  _fc: void 0,
1111
+ _sv: void 0,
1066
1112
  _probe: void 0,
1067
1113
  _cstChildren: void 0,
1068
1114
  _cstLeaves: void 0,
@@ -1754,6 +1800,9 @@ var fastScanner3;
1754
1800
  function needsDeferredTriviaCommit(ctx) {
1755
1801
  return ctx._triviaLog !== void 0 || ctx._rootTriviaLog !== void 0 || ctx._cstBuf !== void 0 || ctx._cstTriviaLog !== void 0;
1756
1802
  }
1803
+ function needsTriviaRollback(ctx) {
1804
+ 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;
1805
+ }
1757
1806
  function saveTriviaMark(ctx) {
1758
1807
  const m = saveCstMark(ctx);
1759
1808
  return {
@@ -1863,7 +1912,8 @@ function advanceTrivia(input, cur, ctx) {
1863
1912
  const triviaP = ctx.trivia;
1864
1913
  if (!triviaP) return cur;
1865
1914
  if (!ctx.trackLines) {
1866
- const fast2 = fastTriviaScanner(triviaP);
1915
+ const classified = triviaP._meta.triviaScanner;
1916
+ const fast2 = classified === void 0 ? fastTriviaScanner(triviaP) : classified;
1867
1917
  if (fast2) return fast2(input, cur);
1868
1918
  if (ctx.triviaKindLabels) return skipWithLabels(input, cur, ctx);
1869
1919
  const tr2 = triviaP.parse(input, cur, createDetachedParseContext(ctx.trackLines, ctx.state));
@@ -2416,6 +2466,11 @@ function adjacencyMisuse(polarity) {
2416
2466
  return new TypeError(`${polarity}(): ${NO_STANDALONE_PARSE}`);
2417
2467
  }
2418
2468
 
2469
+ // src/combinators/scalar.ts
2470
+ function scalarOf(combinator) {
2471
+ return combinator._parseScalar;
2472
+ }
2473
+
2419
2474
  // src/combinators/sequence.ts
2420
2475
  function sequence(...parsers) {
2421
2476
  let adjacency = null;
@@ -2431,6 +2486,7 @@ function sequence(...parsers) {
2431
2486
  (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
2432
2487
  }
2433
2488
  const hasAdjacency = adjacency !== null;
2489
+ const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
2434
2490
  const meta = {
2435
2491
  // Union through the nullable prefix — a leading optional()/many() lets a later
2436
2492
  // term's first char start the sequence. Just `parsers[0]` under-approximates.
@@ -2439,165 +2495,96 @@ function sequence(...parsers) {
2439
2495
  isTrivia: false
2440
2496
  };
2441
2497
  const def = { tag: "sequence", parsers };
2442
- let followSentinels;
2443
- function parseTolerant(input, pos, ctx) {
2444
- followSentinels ??= parsers.map((_, i) => {
2445
- const fs = parsers.slice(i + 1).reduce(
2446
- (acc, p) => union(acc, firstSetOf(p)),
2447
- { kind: "empty" }
2448
- );
2449
- return firstSetSentinel(fs);
2450
- });
2451
- const values = def.valueUnused ? void 0 : [];
2498
+ const scalarParsers = parsers.map(scalarOf);
2499
+ const parseScalar = (input, pos, ctx) => {
2452
2500
  let cur = pos;
2453
- const inheritedSync = ctx._sync;
2454
- try {
2455
- for (let i = 0; i < parsers.length; i++) {
2456
- ctx._sync = followSentinels[i] ?? inheritedSync;
2457
- const adj = adjacency === null ? null : adjacency[i];
2458
- if (adj) {
2459
- if (!holdsAdjacency(input, cur, ctx, adj)) return adjacencyFail(cur, adj);
2460
- if (values !== void 0) values.push(null);
2461
- continue;
2462
- }
2463
- if (ctx.trivia && i > 0) {
2464
- const mTlog = cstTlLen(ctx);
2465
- const mLog = ctx._triviaLog?.length ?? 0;
2466
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
2467
- let scanEnd;
2468
- if (needsDeferredTriviaCommit(ctx)) {
2469
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
2470
- } else {
2471
- scanEnd = advanceTrivia(input, cur, ctx);
2472
- }
2473
- const scanTlog = cstTlLen(ctx);
2474
- const scanLog = ctx._triviaLog?.length ?? 0;
2475
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
2476
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2477
- if (!result2.ok) return result2;
2478
- if (result2.span.end > scanEnd) cur = result2.span.end;
2479
- else rollbackScannedTriviaAt(
2480
- ctx,
2481
- mTlog,
2482
- scanTlog,
2483
- mLog,
2484
- scanLog,
2485
- mRootLog,
2486
- scanRootLog
2487
- );
2488
- if (values !== void 0) values.push(result2.value);
2489
- continue;
2490
- }
2491
- const result = parsers[i].parse(input, cur, ctx);
2492
- if (!result.ok) return result;
2493
- if (values !== void 0) values.push(result.value);
2494
- cur = result.span.end;
2501
+ if (strictArity !== 0) {
2502
+ cur = scalarParsers[0](input, cur, ctx);
2503
+ if (cur < 0) return cur;
2504
+ const first = ctx._sv;
2505
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
2506
+ cur = scalarParsers[1](input, cur, ctx);
2507
+ if (cur < 0) return cur;
2508
+ const second = ctx._sv;
2509
+ if (strictArity === 2) {
2510
+ ctx._sv = def.valueUnused ? void 0 : [first, second];
2511
+ return cur;
2495
2512
  }
2496
- } finally {
2497
- ctx._sync = inheritedSync;
2513
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
2514
+ cur = scalarParsers[2](input, cur, ctx);
2515
+ if (cur < 0) return cur;
2516
+ ctx._sv = def.valueUnused ? void 0 : [first, second, ctx._sv];
2517
+ return cur;
2498
2518
  }
2499
- return {
2500
- ok: true,
2501
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2502
- value: values ?? void 0,
2503
- span: { start: pos, end: cur }
2504
- };
2505
- }
2506
- function parseAdjacent(input, pos, ctx) {
2507
2519
  const values = def.valueUnused ? void 0 : [];
2508
- let cur = pos;
2509
- for (let i = 0; i < parsers.length; i++) {
2510
- const adj = adjacency[i];
2511
- if (adj) {
2512
- if (!holdsAdjacency(input, cur, ctx, adj)) return adjacencyFail(cur, adj);
2513
- if (values !== void 0) values.push(null);
2514
- continue;
2515
- }
2516
- if (ctx.trivia && i > 0) {
2517
- let scanEnd;
2518
- const mTlog = cstTlLen(ctx);
2519
- const mLog = ctx._triviaLog?.length ?? 0;
2520
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
2521
- if (needsDeferredTriviaCommit(ctx)) {
2522
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
2523
- } else {
2524
- scanEnd = advanceTrivia(input, cur, ctx);
2525
- }
2526
- const scanTlog = cstTlLen(ctx);
2527
- const scanLog = ctx._triviaLog?.length ?? 0;
2528
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
2529
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2530
- if (!result2.ok) return result2;
2531
- if (result2.span.end > scanEnd) cur = result2.span.end;
2532
- else rollbackScannedTriviaAt(
2533
- ctx,
2534
- mTlog,
2535
- scanTlog,
2536
- mLog,
2537
- scanLog,
2538
- mRootLog,
2539
- scanRootLog
2540
- );
2541
- if (values !== void 0) values.push(result2.value);
2542
- continue;
2543
- }
2544
- const result = parsers[i].parse(input, cur, ctx);
2545
- if (!result.ok) return result;
2546
- if (values !== void 0) values.push(result.value);
2547
- cur = result.span.end;
2548
- }
2549
- return {
2550
- ok: true,
2551
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2552
- value: values ?? void 0,
2553
- span: { start: pos, end: cur }
2554
- };
2555
- }
2520
+ for (let i = 0; i < scalarParsers.length; i++) {
2521
+ let termPos = cur;
2522
+ if (i > 0 && ctx.trivia) termPos = advanceTrivia(input, cur, ctx);
2523
+ const end = scalarParsers[i](input, termPos, ctx);
2524
+ if (end < 0) return end;
2525
+ if (values !== void 0) values.push(ctx._sv);
2526
+ if (end > termPos || termPos === cur) cur = end;
2527
+ }
2528
+ ctx._sv = values;
2529
+ return cur;
2530
+ };
2531
+ let followSentinels;
2556
2532
  return {
2557
2533
  _tag: "sequence",
2558
2534
  _meta: meta,
2559
2535
  _def: def,
2536
+ _parseScalar: hasAdjacency ? void 0 : parseScalar,
2560
2537
  parse(input, pos, ctx) {
2561
- if (ctx._tolerant) return parseTolerant(input, pos, ctx);
2562
- if (hasAdjacency) return parseAdjacent(input, pos, ctx);
2538
+ const tolerant = ctx._tolerant === true;
2539
+ if (tolerant) {
2540
+ followSentinels ??= parsers.map((_, i) => firstSetSentinel(
2541
+ parsers.slice(i + 1).reduce(
2542
+ (set, parser) => union(set, firstSetOf(parser)),
2543
+ { kind: "empty" }
2544
+ )
2545
+ ));
2546
+ }
2547
+ const inheritedSync = ctx._sync;
2548
+ const deferredTrivia = needsDeferredTriviaCommit(ctx);
2563
2549
  const values = def.valueUnused ? void 0 : [];
2564
2550
  let cur = pos;
2565
- for (let i = 0; i < parsers.length; i++) {
2566
- if (ctx.trivia && i > 0) {
2567
- let scanEnd;
2568
- const mTlog = cstTlLen(ctx);
2569
- const mLog = ctx._triviaLog?.length ?? 0;
2570
- const mRootLog = ctx._rootTriviaLog?.length ?? 0;
2571
- if (needsDeferredTriviaCommit(ctx)) {
2572
- scanEnd = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
2573
- } else {
2574
- scanEnd = advanceTrivia(input, cur, ctx);
2551
+ try {
2552
+ for (let i = 0; i < parsers.length; i++) {
2553
+ if (tolerant) ctx._sync = followSentinels[i] ?? inheritedSync;
2554
+ const assertion = adjacency?.[i];
2555
+ if (assertion) {
2556
+ if (!holdsAdjacency(input, cur, ctx, assertion)) return adjacencyFail(cur, assertion);
2557
+ if (values !== void 0) values.push(null);
2558
+ continue;
2575
2559
  }
2576
- const scanTlog = cstTlLen(ctx);
2577
- const scanLog = ctx._triviaLog?.length ?? 0;
2578
- const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
2579
- const result2 = parsers[i].parse(input, scanEnd, ctx);
2580
- if (!result2.ok) return result2;
2581
- if (result2.span.end > scanEnd) {
2582
- cur = result2.span.end;
2583
- } else {
2560
+ let termPos = cur;
2561
+ const markTlog = deferredTrivia ? cstTlLen(ctx) : 0;
2562
+ const markLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
2563
+ const markRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
2564
+ if (ctx.trivia && i > 0) {
2565
+ termPos = deferredTrivia ? commitTriviaScan(scanTriviaCompact(input, cur, ctx)) : advanceTrivia(input, cur, ctx);
2566
+ }
2567
+ const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
2568
+ const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
2569
+ const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
2570
+ const result = parsers[i].parse(input, termPos, ctx);
2571
+ if (!result.ok) return result;
2572
+ if (values !== void 0) values.push(result.value);
2573
+ if (result.span.end > termPos || termPos === cur) cur = result.span.end;
2574
+ else if (deferredTrivia) {
2584
2575
  rollbackScannedTriviaAt(
2585
2576
  ctx,
2586
- mTlog,
2577
+ markTlog,
2587
2578
  scanTlog,
2588
- mLog,
2579
+ markLog,
2589
2580
  scanLog,
2590
- mRootLog,
2581
+ markRootLog,
2591
2582
  scanRootLog
2592
2583
  );
2593
2584
  }
2594
- if (values !== void 0) values.push(result2.value);
2595
- continue;
2596
2585
  }
2597
- const result = parsers[i].parse(input, cur, ctx);
2598
- if (!result.ok) return result;
2599
- if (values !== void 0) values.push(result.value);
2600
- cur = result.span.end;
2586
+ } finally {
2587
+ if (tolerant) ctx._sync = inheritedSync;
2601
2588
  }
2602
2589
  return {
2603
2590
  ok: true,
@@ -2729,7 +2716,8 @@ function choice(...args) {
2729
2716
  disjoint
2730
2717
  };
2731
2718
  const strategy = disjoint || hasGates ? null : detectStrategy(parsers);
2732
- const autoNot = !disjoint && !hasGates && (strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix") ? computeAutoNot(parsers) : parsers.map(() => null);
2719
+ const ordered = strategy?.tag === "firstMatch" || strategy?.tag === "sharedPrefix";
2720
+ const autoNot = !disjoint && !hasGates && ordered ? computeAutoNot(parsers) : parsers.map(() => null);
2733
2721
  let greedyLitMap = null;
2734
2722
  let sortedParsers = null;
2735
2723
  const asciiDispatch = disjoint ? buildAsciiDispatch(parsers) : null;
@@ -2743,6 +2731,37 @@ function choice(...args) {
2743
2731
  } else if (strategy?.tag === "literalsLongestFirst") {
2744
2732
  sortedParsers = strategy.sortedIndices.map((i) => parsers[i]);
2745
2733
  }
2734
+ const scalarParsers = parsers.map(scalarOf);
2735
+ const scalarEligible = !hasGates && parsers.every((p) => p._parseScalar !== void 0) && (disjoint || ordered);
2736
+ const scalarExpected = parsers.flatMap(deriveExpected);
2737
+ const parseScalar = (input, pos, ctx) => {
2738
+ if (disjoint) {
2739
+ const code = pos < input.length ? input.codePointAt(pos) : -1;
2740
+ let idx = code >= 0 && code < 128 ? asciiDispatch[code] : -1;
2741
+ if (idx < 0 && code >= 0) {
2742
+ for (let i = 0; i < parsers.length; i++) {
2743
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
2744
+ idx = i;
2745
+ break;
2746
+ }
2747
+ }
2748
+ }
2749
+ if (idx >= 0) {
2750
+ const gate = gates[idx];
2751
+ if (!gate || gate(ctx.state)) return scalarParsers[idx](input, pos, ctx);
2752
+ ctx._fx = deriveExpected(parsers[idx]);
2753
+ return ~pos;
2754
+ }
2755
+ } else {
2756
+ for (let i = 0; i < scalarParsers.length; i++) {
2757
+ if (gates[i] && !gates[i](ctx.state)) continue;
2758
+ const end = scalarParsers[i](input, pos, ctx);
2759
+ if (end >= 0 && (!autoNot[i] || !autoNotFires(input, end, autoNot[i]))) return end;
2760
+ }
2761
+ }
2762
+ ctx._fx = scalarExpected;
2763
+ return ~pos;
2764
+ };
2746
2765
  return {
2747
2766
  _tag: "choice",
2748
2767
  _meta: meta,
@@ -2754,6 +2773,7 @@ function choice(...args) {
2754
2773
  strategy: strategy ?? { tag: "firstMatch" },
2755
2774
  autoNot
2756
2775
  },
2776
+ _parseScalar: scalarEligible ? parseScalar : void 0,
2757
2777
  parse(input, pos, ctx) {
2758
2778
  const expected = [];
2759
2779
  let expectedAt = pos;
@@ -3014,8 +3034,8 @@ function buildAsciiDispatch(parsers) {
3014
3034
  }
3015
3035
 
3016
3036
  // src/combinators/repeat.ts
3017
- function repItem(combinator, input, cur, ctx, guardable, mandatory) {
3018
- const mark = saveTriviaMark(ctx);
3037
+ function repItem(combinator, input, cur, ctx, guardable, mandatory, rollbackNeeded) {
3038
+ const mark = rollbackNeeded ? saveTriviaMark(ctx) : void 0;
3019
3039
  let pos = cur;
3020
3040
  if (ctx.trivia) {
3021
3041
  if (needsDeferredTriviaCommit(ctx)) {
@@ -3025,24 +3045,24 @@ function repItem(combinator, input, cur, ctx, guardable, mandatory) {
3025
3045
  }
3026
3046
  }
3027
3047
  if (pos >= input.length && !mandatory) {
3028
- rollbackTrivia(ctx, mark);
3048
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
3029
3049
  return "stop";
3030
3050
  }
3031
3051
  if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
3032
- rollbackTrivia(ctx, mark);
3052
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
3033
3053
  return "stop";
3034
3054
  }
3035
3055
  const result = combinator.parse(input, pos, ctx);
3036
3056
  if (!result.ok) {
3037
- rollbackTrivia(ctx, mark);
3057
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
3038
3058
  if (result.committed) return { fail: result, failPos: pos };
3039
3059
  return { fail: result, failPos: pos };
3040
3060
  }
3041
3061
  if (result.span.end === pos && !mandatory) {
3042
- rollbackTrivia(ctx, mark);
3062
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
3043
3063
  return "stop";
3044
3064
  }
3045
- return { value: result.value, end: result.span.end };
3065
+ return result;
3046
3066
  }
3047
3067
  function resolveBounds(what, opts) {
3048
3068
  const min = opts.min ?? 0;
@@ -3055,6 +3075,21 @@ function resolveBounds(what, opts) {
3055
3075
  if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
3056
3076
  return { min, max };
3057
3077
  }
3078
+ function repeatScalarTail(combinator, child, input, cur, ctx, guardable, values, remaining) {
3079
+ while (cur < input.length && remaining > 0) {
3080
+ let itemPos = cur;
3081
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
3082
+ if (itemPos >= input.length) break;
3083
+ if (guardable && !startsFirstSet(combinator, input, itemPos)) break;
3084
+ const end = child(input, itemPos, ctx);
3085
+ if (end <= itemPos) break;
3086
+ if (values !== void 0) values.push(ctx._sv);
3087
+ cur = end;
3088
+ remaining--;
3089
+ }
3090
+ ctx._sv = values;
3091
+ return cur;
3092
+ }
3058
3093
  function many(combinator, opts = {}) {
3059
3094
  const { min, max } = resolveBounds("many()", opts);
3060
3095
  if (min >= 1) return atLeast(combinator, min, max);
@@ -3066,17 +3101,21 @@ function many(combinator, opts = {}) {
3066
3101
  const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
3067
3102
  let expected;
3068
3103
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
3104
+ const scalarChild = scalarOf(combinator);
3105
+ const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
3069
3106
  return {
3070
3107
  _tag: "many",
3071
3108
  _meta: meta,
3072
3109
  _def: def,
3110
+ _parseScalar: parseScalar,
3073
3111
  parse(input, pos, ctx) {
3074
3112
  const values = def.valueUnused ? void 0 : [];
3113
+ const rollbackNeeded = needsTriviaRollback(ctx);
3075
3114
  let cur = pos;
3076
3115
  let count = 0;
3077
3116
  while (cur < input.length) {
3078
3117
  if (count >= max) break;
3079
- const item = repItem(combinator, input, cur, ctx, guardable, false);
3118
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
3080
3119
  if (item === "stop") break;
3081
3120
  if ("fail" in item) {
3082
3121
  if (!item.fail.ok && item.fail.committed) return item.fail;
@@ -3093,7 +3132,7 @@ function many(combinator, opts = {}) {
3093
3132
  }
3094
3133
  if (values !== void 0) values.push(item.value);
3095
3134
  count++;
3096
- cur = item.end;
3135
+ cur = item.span.end;
3097
3136
  }
3098
3137
  return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
3099
3138
  }
@@ -3111,30 +3150,47 @@ function atLeast(combinator, min, max) {
3111
3150
  const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
3112
3151
  let expected;
3113
3152
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
3153
+ const scalarChild = scalarOf(combinator);
3154
+ const parseScalar = (input, pos, ctx) => {
3155
+ let cur = scalarChild(input, pos, ctx);
3156
+ if (cur < 0) return cur;
3157
+ const values = def.valueUnused ? void 0 : [ctx._sv];
3158
+ for (let count = 1; count < min; count++) {
3159
+ let itemPos = cur;
3160
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
3161
+ const end = scalarChild(input, itemPos, ctx);
3162
+ if (end < 0) return ~cur;
3163
+ cur = end;
3164
+ if (values !== void 0) values.push(ctx._sv);
3165
+ }
3166
+ return repeatScalarTail(combinator, scalarChild, input, cur, ctx, guardable, values, max - min);
3167
+ };
3114
3168
  return {
3115
3169
  _tag: "oneOrMore",
3116
3170
  _meta: meta,
3117
3171
  _def: def,
3172
+ _parseScalar: parseScalar,
3118
3173
  parse(input, pos, ctx) {
3119
3174
  const first = combinator.parse(input, pos, ctx);
3120
3175
  if (!first.ok) return first;
3121
3176
  const values = def.valueUnused ? void 0 : [first.value];
3177
+ const rollbackNeeded = needsTriviaRollback(ctx);
3122
3178
  let cur = first.span.end;
3123
3179
  let count = 1;
3124
3180
  while (count < min) {
3125
- const item = repItem(combinator, input, cur, ctx, guardable, true);
3181
+ const item = repItem(combinator, input, cur, ctx, guardable, true, rollbackNeeded);
3126
3182
  if (item === "stop" || "fail" in item) {
3127
3183
  if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
3128
3184
  expected ??= deriveExpected(combinator);
3129
3185
  return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
3130
3186
  }
3131
3187
  if (values !== void 0) values.push(item.value);
3132
- cur = item.end;
3188
+ cur = item.span.end;
3133
3189
  count++;
3134
3190
  }
3135
3191
  while (cur < input.length) {
3136
3192
  if (count >= max) break;
3137
- const item = repItem(combinator, input, cur, ctx, guardable, false);
3193
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
3138
3194
  if (item === "stop") break;
3139
3195
  if ("fail" in item) {
3140
3196
  if (!item.fail.ok && item.fail.committed) return item.fail;
@@ -3151,7 +3207,7 @@ function atLeast(combinator, min, max) {
3151
3207
  }
3152
3208
  if (values !== void 0) values.push(item.value);
3153
3209
  count++;
3154
- cur = item.end;
3210
+ cur = item.span.end;
3155
3211
  }
3156
3212
  return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
3157
3213
  }
@@ -3159,11 +3215,54 @@ function atLeast(combinator, min, max) {
3159
3215
  }
3160
3216
 
3161
3217
  // src/combinators/map.ts
3218
+ var JSON_STRING_BODY = String.raw`(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*`;
3162
3219
  function transform(combinator, fn) {
3220
+ const observesSpan = fn.length > 1;
3221
+ const sequenceDef = combinator._def.tag === "sequence" ? combinator._def : void 0;
3222
+ const terms = sequenceDef?.parsers;
3223
+ const inner = terms?.[1];
3224
+ 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;
3225
+ const child = scalarOf(combinator);
3226
+ const parseScalar = fused ? (() => {
3227
+ const body = inner._stickyRegex;
3228
+ const tuple = ['"', "", '"'];
3229
+ const expected = [JSON.stringify('"')];
3230
+ return (input, pos, ctx) => {
3231
+ if (ctx.trivia !== void 0) {
3232
+ const end2 = child(input, pos, ctx);
3233
+ if (end2 < 0) return end2;
3234
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end: end2 }) : fn(ctx._sv);
3235
+ return end2;
3236
+ }
3237
+ if (input.charCodeAt(pos) !== 34) {
3238
+ ctx._fx = expected;
3239
+ return ~pos;
3240
+ }
3241
+ body.lastIndex = pos + 1;
3242
+ const match = body.exec(input);
3243
+ const close = pos + 1 + match[0].length;
3244
+ if (input.charCodeAt(close) !== 34) {
3245
+ ctx._fx = expected;
3246
+ return ~close;
3247
+ }
3248
+ tuple[1] = match[0];
3249
+ const end = close + 1;
3250
+ ctx._sv = observesSpan ? fn(tuple, { start: pos, end }) : fn(tuple);
3251
+ return end;
3252
+ };
3253
+ })() : /* @__PURE__ */ (() => {
3254
+ return (input, pos, ctx) => {
3255
+ const end = child(input, pos, ctx);
3256
+ if (end < 0) return end;
3257
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end }) : fn(ctx._sv);
3258
+ return end;
3259
+ };
3260
+ })();
3163
3261
  return {
3164
3262
  _tag: "transform",
3165
3263
  _meta: combinator._meta,
3166
3264
  _def: { tag: "transform", parser: combinator, fn },
3265
+ _parseScalar: parseScalar,
3167
3266
  parse(input, pos, ctx) {
3168
3267
  const result = combinator.parse(input, pos, ctx);
3169
3268
  if (!result.ok) return result;
@@ -3178,9 +3277,11 @@ function trivia(combinator) {
3178
3277
  _meta: {
3179
3278
  ...combinator._meta,
3180
3279
  isTrivia: true,
3280
+ triviaScanner: fastTriviaScanner(combinator),
3181
3281
  ...kindLabels ? { triviaKindLabels: kindLabels } : {}
3182
3282
  },
3183
3283
  _def: { tag: "trivia", parser: combinator },
3284
+ _parseScalar: scalarOf(combinator),
3184
3285
  parse: combinator.parse.bind(combinator)
3185
3286
  };
3186
3287
  }
@@ -3253,8 +3354,20 @@ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
3253
3354
  }
3254
3355
 
3255
3356
  // src/combinators/ref.ts
3357
+ function withRuleTrivia(trivia2, ctx, run) {
3358
+ const savedLabels = ctx.triviaKindLabels;
3359
+ ctx.trivia = trivia2;
3360
+ ctx.triviaKindLabels = trivia2._meta.triviaKindLabels;
3361
+ try {
3362
+ return run();
3363
+ } finally {
3364
+ ctx.trivia = void 0;
3365
+ ctx.triviaKindLabels = savedLabels;
3366
+ }
3367
+ }
3256
3368
  function ref() {
3257
3369
  let resolved = null;
3370
+ let resolvedScalar = null;
3258
3371
  let ownBoundary = false;
3259
3372
  const meta = {
3260
3373
  firstSet: any(),
@@ -3271,25 +3384,28 @@ function ref() {
3271
3384
  return resolved;
3272
3385
  }
3273
3386
  },
3387
+ _parseScalar(input, pos, ctx) {
3388
+ const scalar = resolvedScalar;
3389
+ if (!scalar) throw new Error("ref<T>() used before .define() was called");
3390
+ const gt = meta.grammarTrivia;
3391
+ if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
3392
+ return withRuleTrivia(gt, ctx, () => scalar(input, pos, ctx));
3393
+ }
3394
+ return scalar(input, pos, ctx);
3395
+ },
3274
3396
  parse(input, pos, ctx) {
3275
- if (!resolved) throw new Error("ref<T>() used before .define() was called");
3397
+ const parser = resolved;
3398
+ if (!parser) throw new Error("ref<T>() used before .define() was called");
3276
3399
  const gt = meta.grammarTrivia;
3277
3400
  if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
3278
- const savedLabels = ctx.triviaKindLabels;
3279
- ctx.trivia = gt;
3280
- ctx.triviaKindLabels = gt._meta.triviaKindLabels;
3281
- try {
3282
- return resolved.parse(input, pos, ctx);
3283
- } finally {
3284
- ctx.trivia = void 0;
3285
- ctx.triviaKindLabels = savedLabels;
3286
- }
3401
+ return withRuleTrivia(gt, ctx, () => parser.parse(input, pos, ctx));
3287
3402
  }
3288
- return resolved.parse(input, pos, ctx);
3403
+ return parser.parse(input, pos, ctx);
3289
3404
  },
3290
3405
  define(p) {
3291
3406
  if (resolved) throw new Error("ref<T>() already defined");
3292
3407
  resolved = p;
3408
+ resolvedScalar = scalarOf(p);
3293
3409
  ownBoundary = hasOwnTriviaBoundary(p);
3294
3410
  meta.firstSet = p._meta.firstSet;
3295
3411
  meta.canMatchNewline = p._meta.canMatchNewline;
@@ -6332,6 +6448,9 @@ if(ctx._rootTriviaLog!==undefined&&ctx._rootTriviaLog.length!==rt)ctx._rootTrivi
6332
6448
  function q(s) {
6333
6449
  return JSON.stringify(s);
6334
6450
  }
6451
+ function isAsciiOnlyClass(cls) {
6452
+ return cls !== null && cls !== void 0 && cls.hi.length === 0;
6453
+ }
6335
6454
  var NO_SINKS = { fd: false, er: false };
6336
6455
  function sinkSlots(t, s) {
6337
6456
  return `${s.fd ? `,${t}fd=0` : ""}${s.er ? `,${t}er=0` : ""}`;
@@ -6878,12 +6997,13 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
6878
6997
  return `${head}EC.e=pos;return ''}`;
6879
6998
  case OP_GATE: {
6880
6999
  const child = link(code[ip + 2]);
6881
- const ci = classes.push([t.cc[code[ip + 1]]]) - 1;
7000
+ const resolved = t.cc[code[ip + 1]];
7001
+ const ci = classes.push([resolved]) - 1;
6882
7002
  classPlan.push([code[ip + 1]]);
6883
7003
  const cls = hoist("cl", `CLS[${ci}][0]`);
6884
7004
  const xf = fxRef(code[ip + 3]);
6885
7005
  return `${head}
6886
- if(!classHas(${cls},lead(input,pos))){ctx._fe=pos;ctx._fx=${xf};return FAIL}
7006
+ if(${staticBuild && isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
6887
7007
  return ${child}(input,pos,ctx)
6888
7008
  }`;
6889
7009
  }
@@ -7226,10 +7346,11 @@ return v
7226
7346
  const asc = hoist("as", `DISP[${di}].ascii`);
7227
7347
  const hiArr = table.hi;
7228
7348
  const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join("\n");
7349
+ const directAscii = staticBuild && table.hi.length === 0;
7229
7350
  return `${head}
7230
- const c=lead(input,pos)
7351
+ const c=${directAscii ? "input.charCodeAt(pos)" : "lead(input,pos)"}
7231
7352
  let arm=-1
7232
- if(c>=0&&c<128){const a=${asc}[c];if(a!==0)arm=a-1}
7353
+ if(${directAscii ? "c<128" : "c>=0&&c<128"}){const a=${asc}[c];if(a!==0)arm=a-1}
7233
7354
  ${hiArr.length === 0 ? "" : `else if(c>=128){const h=${hoist("hi", `DISP[${di}].hi`)}
7234
7355
  for(let i=0;i<h.length;i+=3){if(c>=h[i]&&c<=h[i+1]){arm=h[i+2];break}}}`}
7235
7356
  if(arm>=0){
@@ -7251,7 +7372,9 @@ return FAIL
7251
7372
  const di = code[ip + 1];
7252
7373
  const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]));
7253
7374
  const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null);
7254
- const gateRefs = Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
7375
+ const maskable = n <= 32;
7376
+ const directAsciiMask = staticBuild && maskable && gates.every(isAsciiOnlyClass);
7377
+ const gateRefs = directAsciiMask ? [] : Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
7255
7378
  const tokenCandidate = tokenChoiceCandidates.get(ip);
7256
7379
  const pretests = Array.from({ length: n }, (_, i) => {
7257
7380
  if (tokenCandidate?.arm === i) {
@@ -7271,7 +7394,6 @@ return FAIL
7271
7394
  const sequence6 = largeChoiceScalarSequences.get(code[base + i]);
7272
7395
  return sequence6 === void 0 ? void 0 : { sequence: scalarSequenceRef(sequence6) };
7273
7396
  });
7274
- const maskable = n <= 32;
7275
7397
  const maskName = maskable ? hoist("mk", `MASK[${masks.push(maskForClassRow(gates)) - 1}]`) : "";
7276
7398
  if (maskable) maskPlan.push(~di);
7277
7399
  const p = tmp();
@@ -7333,16 +7455,16 @@ ${merge}
7333
7455
  ctx._fe=pos;ctx._fx=acc??${choiceFx}
7334
7456
  return FAIL`;
7335
7457
  return `${head}
7336
- const c=lead(input,pos)
7458
+ const c=${directAsciiMask ? "input.charCodeAt(pos)" : "lead(input,pos)"}
7337
7459
  ${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ""}
7338
7460
  ${deepAt.map((at, i) => `let ${at},${deepFx[i]}`).join("\n")}
7339
7461
  ${maskable ? `if(c<128){
7340
- const bits=${maskName}[c<0?128:c]
7462
+ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
7341
7463
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
7342
7464
  ${lazyArms(true)}
7343
- }else{
7465
+ }${directAsciiMask ? "" : `else{
7344
7466
  ${lazyArms(false)}
7345
- }
7467
+ }`}
7346
7468
  ` : lazyArms(false)}
7347
7469
  ${finish}
7348
7470
  }`;
@@ -7393,7 +7515,7 @@ if(ctx._fc===true){${startFailureExact ? `if(best===pos){acc=${catchName}(${i},0
7393
7515
  ${rollbackFor(i)}
7394
7516
  }${routeMiss}${sequenceMiss}`;
7395
7517
  }).join("\n") : "";
7396
- const generalArms = arms.map((arm, i) => {
7518
+ const generalArms = directAsciiMask ? "" : arms.map((arm, i) => {
7397
7519
  const pretest = pretests[i];
7398
7520
  const decision = pretest?.token === void 0 && pretest?.sequence === void 0 ? "" : tmp();
7399
7521
  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("||")})`;
@@ -7423,12 +7545,12 @@ ${rollbackFor(i)}
7423
7545
  }${miss}`;
7424
7546
  }).join("\n");
7425
7547
  return `${head}
7426
- const c=lead(input,pos)
7548
+ const c=${directAsciiMask ? "input.charCodeAt(pos)" : "lead(input,pos)"}
7427
7549
  ${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ""}
7428
7550
  let acc
7429
7551
  let best=pos
7430
7552
  ${maskable ? `if(c<128){
7431
- const bits=${maskName}[c<0?128:c]
7553
+ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
7432
7554
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
7433
7555
  ${startFailureExact ? "" : "let prev=0"}
7434
7556
  ${maskArms}
@@ -7523,14 +7645,18 @@ return [key,v]
7523
7645
  const itemFx = reportItem ? fxRef(code[ip + 6]) : "EMPTY_FX";
7524
7646
  const collect = op === OP_REP;
7525
7647
  let itemCls;
7648
+ let itemAscii = false;
7526
7649
  if (!REC) {
7527
7650
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
7528
7651
  if (itemClassIndex >= 0) {
7529
- const ci = classes.push([t.cc[itemClassIndex]]) - 1;
7652
+ const resolved = t.cc[itemClassIndex];
7653
+ const ci = classes.push([resolved]) - 1;
7530
7654
  classPlan.push([itemClassIndex]);
7531
7655
  itemCls = hoist("ri", `CLS[${ci}][0]`);
7656
+ itemAscii = staticBuild && isAsciiOnlyClass(resolved);
7532
7657
  }
7533
7658
  }
7659
+ const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
7534
7660
  const skipBeforeFirst = sepIp < 0 && min === 0;
7535
7661
  const p = tmp();
7536
7662
  const via = sep !== void 0 ? "false" : min === 0 ? skipBeforeFirst ? "true" : "count>0" : `count>=${min}&&count>0`;
@@ -7590,7 +7716,7 @@ let count=0
7590
7716
  for(;;){
7591
7717
  ${max >= 0 ? `if(count>=${max})break
7592
7718
  ` : ""}${sep !== void 0 ? `if(count>0&&count>=${min}&&cur>=input.length)break
7593
- ` : ""}${itemCls !== void 0 && sep === void 0 ? `if(count>=${min}&&${p}gate&&${hasTrivia === "false" ? "true" : hasTrivia === "true" ? "false" : "!hasTrivia"}&&!classHas(${itemCls},lead(input,cur)))break
7719
+ ` : ""}${itemCls !== void 0 && sep === void 0 ? `if(count>=${min}&&${p}gate&&${hasTrivia === "false" ? "true" : hasTrivia === "true" ? "false" : "!hasTrivia"}&&${itemMiss("cur")})break
7594
7720
  ` : ""}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` : ""}
7595
7721
  ${markBody}
7596
7722
  let itemStart=cur
@@ -7615,7 +7741,7 @@ itemStart=${hasTrivia === "false" ? "EC.e" : hasTrivia === "true" ? `${skip}(inp
7615
7741
  ${rb}
7616
7742
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
7617
7743
  }
7618
- ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&!classHas(${itemCls},lead(input,itemStart))){
7744
+ ${itemCls === void 0 ? "" : `if(count>=${min}&&${p}gate&&${itemMiss("itemStart")}){
7619
7745
  ${rb}
7620
7746
  ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
7621
7747
  }
@@ -11991,6 +12117,22 @@ var Encoder = class {
11991
12117
  choiceRollbackMasks = /* @__PURE__ */ new Map();
11992
12118
  nonCommittingChoiceSites = /* @__PURE__ */ new Set();
11993
12119
  failureRollbackCleanSites = /* @__PURE__ */ new Set();
12120
+ /**
12121
+ * `hasSideWriter` memo for `failureNeedsRollback`, shared across every call
12122
+ * for the lifetime of this `Encoder`. A cached entry is written only when
12123
+ * `complete` is true, which means the answer is a path-independent fact
12124
+ * about that combinator node — it does not depend on which root's traversal
12125
+ * asked, only on the node's own reachable subgraph. Scoping the memo per call
12126
+ * (as a local inside `failureNeedsRollback`) was correct but discarded that
12127
+ * fact after every arm, so a grammar with many ordered-choice arms / optional
12128
+ * / attempt sites re-walked the SAME shared rule graph from scratch for each
12129
+ * site — O(sites × graph size) instead of O(graph size). Measured on the
12130
+ * scss-parser grammar (composed CSS-base superset, the deepest/largest of the
12131
+ * four grammars): 47-50s for one `compileRuleMap` with the per-call memo,
12132
+ * versus the whole macro transform completing in under a second once this
12133
+ * memo is hoisted here.
12134
+ */
12135
+ sideWriterMemo = /* @__PURE__ */ new Map();
11994
12136
  labels = void 0;
11995
12137
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
11996
12138
  activeTrivia = void 0;
@@ -12416,13 +12558,13 @@ var Encoder = class {
12416
12558
  const refName = p._ruleName;
12417
12559
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
12418
12560
  const root = winner !== void 0 && !winnerWrapsReference2(winner, p) ? winner : p;
12419
- const sideWriterMemo = /* @__PURE__ */ new Map();
12420
- const hasSideWriter = (current, seen = /* @__PURE__ */ new Set()) => {
12561
+ const sideWriterMemo = this.sideWriterMemo;
12562
+ const seen = /* @__PURE__ */ new Set();
12563
+ const hasSideWriter = (current) => {
12421
12564
  const cached = sideWriterMemo.get(current);
12422
12565
  if (cached !== void 0) return { found: cached, complete: true };
12423
12566
  if (seen.has(current)) return { found: false, complete: false };
12424
- const next = new Set(seen);
12425
- next.add(current);
12567
+ seen.add(current);
12426
12568
  const def = current._def;
12427
12569
  let result;
12428
12570
  switch (def.tag) {
@@ -12437,11 +12579,11 @@ var Encoder = class {
12437
12579
  const name = current._ruleName;
12438
12580
  const resolvedWinner = name === void 0 ? void 0 : this.winners?.[name];
12439
12581
  if (resolvedWinner !== void 0 && !winnerWrapsReference2(resolvedWinner, current)) {
12440
- result = hasSideWriter(resolvedWinner, next);
12582
+ result = hasSideWriter(resolvedWinner);
12441
12583
  break;
12442
12584
  }
12443
12585
  try {
12444
- result = hasSideWriter(def.thunk(), next);
12586
+ result = hasSideWriter(def.thunk());
12445
12587
  } catch {
12446
12588
  result = { found: true, complete: true };
12447
12589
  }
@@ -12451,7 +12593,7 @@ var Encoder = class {
12451
12593
  let complete = true;
12452
12594
  result = { found: false, complete: true };
12453
12595
  for (const child of childrenOf(def)) {
12454
- const childResult = hasSideWriter(child, next);
12596
+ const childResult = hasSideWriter(child);
12455
12597
  if (childResult.found) {
12456
12598
  result = { found: true, complete: true };
12457
12599
  break;
@@ -12461,6 +12603,7 @@ var Encoder = class {
12461
12603
  if (!result.found) result = { found: false, complete };
12462
12604
  }
12463
12605
  }
12606
+ seen.delete(current);
12464
12607
  if (result.found || result.complete) sideWriterMemo.set(current, result.found);
12465
12608
  return result;
12466
12609
  };