parseman 0.55.4 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/analysis/choice-cost.d.ts.map +1 -1
  2. package/dist/analysis/commitment.d.ts.map +1 -1
  3. package/dist/analysis/diagnostics.js +3745 -3180
  4. package/dist/analysis/diagnostics.js.map +3 -3
  5. package/dist/analysis/duplication.d.ts.map +1 -1
  6. package/dist/analysis/gating.d.ts.map +1 -1
  7. package/dist/cli/index.js +2182 -1617
  8. package/dist/cli/index.js.map +3 -3
  9. package/dist/combinators/attempt.d.ts.map +1 -1
  10. package/dist/combinators/choice.d.ts.map +1 -1
  11. package/dist/combinators/dispatch.d.ts.map +1 -1
  12. package/dist/combinators/expect.d.ts.map +1 -1
  13. package/dist/combinators/first-set.d.ts.map +1 -1
  14. package/dist/combinators/lines.d.ts +79 -0
  15. package/dist/combinators/lines.d.ts.map +1 -0
  16. package/dist/combinators/node.d.ts +1 -1
  17. package/dist/combinators/trivia-skip.d.ts +1 -0
  18. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  19. package/dist/compiler/build-arity.d.ts +1 -1
  20. package/dist/compiler/build-arity.d.ts.map +1 -1
  21. package/dist/compiler/fields.d.ts.map +1 -1
  22. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  23. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  24. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  25. package/dist/compiler/token-capability.js +25 -0
  26. package/dist/compiler/token-capability.js.map +1 -1
  27. package/dist/coverage.d.ts.map +1 -1
  28. package/dist/cst/reflection.d.ts.map +1 -1
  29. package/dist/cst/trivia-kinds.d.ts +9 -1
  30. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2348 -1697
  34. package/dist/index.js.map +3 -3
  35. package/dist/language-service/index.js +3 -0
  36. package/dist/language-service/index.js.map +1 -1
  37. package/dist/parse-context.d.ts.map +1 -1
  38. package/dist/plugin/entry-supercompiler.d.ts +1 -1
  39. package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
  40. package/dist/plugin/evaluator.d.ts.map +1 -1
  41. package/dist/plugin/index.d.ts +27 -1
  42. package/dist/plugin/index.d.ts.map +1 -1
  43. package/dist/plugin/index.js +2360 -1314
  44. package/dist/plugin/index.js.map +3 -3
  45. package/dist/plugin/ir-eval.d.ts +2 -4
  46. package/dist/plugin/ir-eval.d.ts.map +1 -1
  47. package/dist/regex/classes.d.ts.map +1 -1
  48. package/dist/run/index.js +3 -0
  49. package/dist/run/index.js.map +1 -1
  50. package/dist/spec/index.js +15 -0
  51. package/dist/spec/index.js.map +1 -1
  52. package/dist/spec/model.d.ts.map +1 -1
  53. package/dist/table/assemble.d.ts +9 -65
  54. package/dist/table/assemble.d.ts.map +1 -1
  55. package/dist/table/child-slots.d.ts +4 -6
  56. package/dist/table/child-slots.d.ts.map +1 -1
  57. package/dist/table/compile-rule-map.d.ts +14 -2
  58. package/dist/table/compile-rule-map.d.ts.map +1 -1
  59. package/dist/table/emit-assembly.d.ts +17 -2
  60. package/dist/table/emit-assembly.d.ts.map +1 -1
  61. package/dist/table/emit.d.ts +5 -1
  62. package/dist/table/emit.d.ts.map +1 -1
  63. package/dist/table/encode.d.ts.map +1 -1
  64. package/dist/table/exec-baseline.d.ts.map +1 -1
  65. package/dist/table/exec.d.ts.map +1 -1
  66. package/dist/table/index.js +3884 -3262
  67. package/dist/table/index.js.map +3 -3
  68. package/dist/table/line-scan.d.ts +9 -0
  69. package/dist/table/line-scan.d.ts.map +1 -0
  70. package/dist/table/ops.d.ts +9 -6
  71. package/dist/table/ops.d.ts.map +1 -1
  72. package/dist/table/program.d.ts +24 -2
  73. package/dist/table/program.d.ts.map +1 -1
  74. package/dist/table/select-hot.d.ts +6 -0
  75. package/dist/table/select-hot.d.ts.map +1 -0
  76. package/dist/table/trivia-gap.d.ts +31 -0
  77. package/dist/table/trivia-gap.d.ts.map +1 -0
  78. package/dist/types.d.ts +38 -0
  79. package/dist/types.d.ts.map +1 -1
  80. package/dist/version.d.ts +1 -1
  81. package/package.json +4 -1
  82. package/src/analysis/choice-cost.ts +1 -0
  83. package/src/analysis/commitment.ts +13 -0
  84. package/src/analysis/duplication.ts +3 -0
  85. package/src/analysis/gating.ts +5 -0
  86. package/src/analysis/rebuild.ts +2 -2
  87. package/src/combinators/attempt.ts +2 -0
  88. package/src/combinators/choice.ts +3 -0
  89. package/src/combinators/dispatch.ts +3 -0
  90. package/src/combinators/expect.ts +5 -0
  91. package/src/combinators/first-set.ts +9 -1
  92. package/src/combinators/lines.ts +244 -0
  93. package/src/combinators/node.ts +6 -13
  94. package/src/combinators/trivia-skip.ts +48 -3
  95. package/src/compiler/build-arity.ts +3 -3
  96. package/src/compiler/fields.ts +3 -0
  97. package/src/compiler/grammar-coverage-ids.ts +1 -0
  98. package/src/compiler/ir-serialize.ts +10 -0
  99. package/src/compiler/token-alphabet.ts +3 -1
  100. package/src/coverage.ts +6 -0
  101. package/src/cst/reflection.ts +3 -0
  102. package/src/cst/trivia-kinds.ts +12 -1
  103. package/src/index.ts +2 -0
  104. package/src/parse-context.ts +3 -0
  105. package/src/plugin/entry-supercompiler.ts +8 -9
  106. package/src/plugin/evaluator.ts +4 -0
  107. package/src/plugin/index.ts +138 -13
  108. package/src/plugin/ir-eval.ts +10 -1
  109. package/src/regex/classes.ts +3 -0
  110. package/src/spec/model.ts +15 -0
  111. package/src/table/assemble.ts +125 -33
  112. package/src/table/child-slots.ts +4 -6
  113. package/src/table/compile-rule-map.ts +55 -15
  114. package/src/table/emit-assembly.ts +139 -26
  115. package/src/table/emit.ts +13 -11
  116. package/src/table/encode.ts +87 -27
  117. package/src/table/exec-baseline.ts +1 -3
  118. package/src/table/exec.ts +10 -6
  119. package/src/table/line-scan.ts +19 -0
  120. package/src/table/ops.ts +9 -6
  121. package/src/table/program.ts +41 -2
  122. package/src/table/select-hot.ts +70 -0
  123. package/src/table/trivia-gap.ts +109 -0
  124. package/src/types.ts +32 -0
  125. package/src/version.ts +1 -1
@@ -163,6 +163,11 @@ function matchesEmptyBody(p, seen, resolve3) {
163
163
  return true;
164
164
  }
165
165
  }
166
+ // A run consumes at least its first line's prefix, and its body after that.
167
+ case "lines":
168
+ return me(d.first ?? d.prefix) && me(d.body);
169
+ case "columns":
170
+ return d.n === 0;
166
171
  default:
167
172
  return true;
168
173
  }
@@ -277,6 +282,11 @@ function firstSetBody(p, seen, resolve3, activeTrivia) {
277
282
  resolve3,
278
283
  d.clearTrivia ? null : d.triviaParser ?? activeTrivia
279
284
  );
285
+ // A run starts with its first line's prefix; a nullable prefix starts with anything.
286
+ case "lines": {
287
+ const opening = d.first ?? d.prefix;
288
+ return empties(opening) ? any() : fs3(opening);
289
+ }
280
290
  default:
281
291
  return p._meta.firstSet;
282
292
  }
@@ -356,12 +366,12 @@ function scalarRootOf(combinator) {
356
366
  }
357
367
 
358
368
  // src/combinators/ref.ts
359
- function withRuleTrivia(trivia2, ctx, run) {
369
+ function withRuleTrivia(trivia2, ctx, run2) {
360
370
  const savedLabels = ctx.triviaKindLabels;
361
371
  ctx.trivia = trivia2;
362
372
  ctx.triviaKindLabels = trivia2._meta.triviaKindLabels;
363
373
  try {
364
- return run();
374
+ return run2();
365
375
  } finally {
366
376
  ctx.trivia = void 0;
367
377
  ctx.triviaKindLabels = savedLabels;
@@ -786,6 +796,8 @@ function createParseContext() {
786
796
  _triviaCaptureMask: void 0,
787
797
  trackLines: false,
788
798
  state: void 0,
799
+ lineView: void 0,
800
+ _columnsSplit: void 0,
789
801
  build: void 0,
790
802
  _errors: void 0,
791
803
  _tolerant: void 0,
@@ -804,6 +816,7 @@ function createParseContext() {
804
816
  _rootTriviaKindIndex: void 0,
805
817
  _rootTriviaStrictScopes: void 0,
806
818
  _rootTriviaCapture: void 0,
819
+ _gap: void 0,
807
820
  _cstTriviaLog: void 0,
808
821
  _fields: void 0,
809
822
  _cstBuf: void 0,
@@ -857,6 +870,7 @@ function literalCodePoints(frag) {
857
870
  if (ch2 === "\\") {
858
871
  const e = frag[i + 1];
859
872
  if (e === void 0) return null;
873
+ if (!(e in CLASS_ESCAPES) && /\w/.test(e)) return null;
860
874
  out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
861
875
  i += 2;
862
876
  continue;
@@ -1403,7 +1417,10 @@ function labeledTriviaSpec(arms, minRepeats) {
1403
1417
  arms,
1404
1418
  minRepeats,
1405
1419
  visitor: commonLabeledTriviaVisitor({ arms, minRepeats }),
1406
- charArms: charArmsFor({ arms, minRepeats })
1420
+ charArms: charArmsFor({ arms, minRepeats }),
1421
+ rootKinds: void 0,
1422
+ rootLeaders: [],
1423
+ rootScan: null
1407
1424
  };
1408
1425
  }
1409
1426
  function matchArmAt(input, pos, arm, state) {
@@ -1726,6 +1743,9 @@ function childrenOf(def) {
1726
1743
  ];
1727
1744
  case "routed":
1728
1745
  return def.fallback ? [def.fallback] : [];
1746
+ case "lines":
1747
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
1748
+ case "columns":
1729
1749
  case "literal":
1730
1750
  case "regex":
1731
1751
  case "keywords":
@@ -2577,11 +2597,12 @@ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
2577
2597
  return result;
2578
2598
  }
2579
2599
  function saveLookaheadMark(ctx) {
2580
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
2600
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null, split: ctx._columnsSplit };
2581
2601
  }
2582
2602
  function rollbackLookahead(ctx, mark) {
2583
2603
  rollbackTrivia(ctx, mark);
2584
2604
  if (ctx._probe) ctx._probe.best = mark.probeBest;
2605
+ ctx._columnsSplit = mark.split;
2585
2606
  }
2586
2607
  function scanWithLabels(input, cur, ctx) {
2587
2608
  const triviaP = ctx.trivia;
@@ -2815,8 +2836,50 @@ function loopScanner(arms) {
2815
2836
  function regexTriviaScanner(parser2) {
2816
2837
  const d = parser2._def.tag === "label" ? parser2._def.parser._def : parser2._def;
2817
2838
  if (d.tag !== "regex" || d.flags) return null;
2818
- const source = d.source;
2819
- return classRunSource(source) ?? altStarSource(source) ?? prefixRunSource(source) ?? delimitedSource(source);
2839
+ return sourceScanner(d.source);
2840
+ }
2841
+ function sourceScanner(source) {
2842
+ return classRunSource(source) ?? altStarSource(source) ?? prefixRunSource(source) ?? delimitedSource(source) ?? sharedPrefixAltSource(source);
2843
+ }
2844
+ function sharedPrefixAltSource(source) {
2845
+ const m = /^((?:\\.|[^\\[\]()*+?|^$.{}])+)\(\?:(.*)\)$/.exec(source);
2846
+ if (!m) return null;
2847
+ const alts = [];
2848
+ const body = m[2];
2849
+ let depth = 0;
2850
+ let start = 0;
2851
+ for (let i = 0; i < body.length; i++) {
2852
+ const c = body[i];
2853
+ if (c === "\\") {
2854
+ i++;
2855
+ continue;
2856
+ }
2857
+ if (c === "[") {
2858
+ while (i < body.length && body[i] !== "]") i += body[i] === "\\" ? 2 : 1;
2859
+ continue;
2860
+ }
2861
+ if (c === "(") depth++;
2862
+ else if (c === ")" && --depth < 0) return null;
2863
+ else if (c === "|" && depth === 0) {
2864
+ alts.push(body.slice(start, i));
2865
+ start = i + 1;
2866
+ }
2867
+ }
2868
+ if (depth !== 0 || alts.length === 0) return null;
2869
+ alts.push(body.slice(start));
2870
+ const scanners = [];
2871
+ for (const alt of alts) {
2872
+ const scanner = sourceScanner(m[1] + alt);
2873
+ if (scanner === null) return null;
2874
+ scanners.push(scanner);
2875
+ }
2876
+ return (input, cur) => {
2877
+ for (const scanner of scanners) {
2878
+ const end = scanner(input, cur);
2879
+ if (end > cur) return end;
2880
+ }
2881
+ return cur;
2882
+ };
2820
2883
  }
2821
2884
  function bmpString(cps) {
2822
2885
  if (!cps) return null;
@@ -3169,6 +3232,7 @@ function matchesAt(sentinel, input, pos, ctx) {
3169
3232
  function captureError(ctx, error) {
3170
3233
  if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
3171
3234
  }
3235
+ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
3172
3236
 
3173
3237
  // src/combinators/adjacency.ts
3174
3238
  function adjacencyOf(p) {
@@ -3427,6 +3491,12 @@ function derive(c, seen) {
3427
3491
  return deriveExpected2(def.parser);
3428
3492
  case "expect":
3429
3493
  return def.expected;
3494
+ case "lines": {
3495
+ const opening = def.first ?? def.prefix;
3496
+ return matchesEmpty(opening) ? [...deriveExpected2(opening), ...deriveExpected2(def.body)] : deriveExpected2(opening);
3497
+ }
3498
+ case "columns":
3499
+ return [`columns(${def.n})`];
3430
3500
  case "node":
3431
3501
  case "grammar":
3432
3502
  case "trivia":
@@ -3611,9 +3681,11 @@ function choice(...args) {
3611
3681
  for (let i = 0; i < parsers.length; i++) {
3612
3682
  if (gates[i] && !gates[i](ctx.state)) continue;
3613
3683
  const mark = saveTriviaMark(ctx);
3684
+ const split = ctx._columnsSplit;
3614
3685
  const result = parsers[i].parse(input, pos, ctx);
3615
3686
  if (!result.ok) {
3616
3687
  rollbackTrivia(ctx, mark);
3688
+ ctx._columnsSplit = split;
3617
3689
  const at = result.span.start;
3618
3690
  if (at > expectedAt) {
3619
3691
  expectedAt = at;
@@ -3834,6 +3906,9 @@ function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
3834
3906
  return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen)) || def.recoverAt !== void 0 && parserUsesRouted(def.recoverAt, seen) || def.stopAt !== void 0 && parserUsesRouted(def.stopAt, seen);
3835
3907
  case "recover":
3836
3908
  return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
3909
+ // The body parses its own input, so only the checks on this one can reuse a routed head.
3910
+ case "lines":
3911
+ return [def.prefix, def.first, def.lazy].some((entry) => entry !== void 0 && parserUsesRouted(entry, seen));
3837
3912
  case "lazy":
3838
3913
  try {
3839
3914
  return parserUsesRouted(def.thunk(), seen);
@@ -4084,9 +4159,11 @@ function attempt(parser2, options) {
4084
4159
  const mark = saveCstMark(ctx);
4085
4160
  const log = ctx._triviaLog?.length;
4086
4161
  const rootLog = ctx._rootTriviaLog?.length;
4162
+ const split = ctx._columnsSplit;
4087
4163
  const result = parser2.parse(input, pos, ctx);
4088
4164
  if (result.ok) return result;
4089
4165
  rollbackCstCapture(ctx, mark);
4166
+ ctx._columnsSplit = split;
4090
4167
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
4091
4168
  if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
4092
4169
  if (result.committed && !contain) return result;
@@ -4601,12 +4678,12 @@ function beginCompileDegradationDrain() {
4601
4678
  return true;
4602
4679
  });
4603
4680
  if (fresh.length === 0) return;
4604
- const lines = formatDegradations(fresh);
4681
+ const lines2 = formatDegradations(fresh);
4605
4682
  if (resolveDegradationLevel() === "error") {
4606
4683
  throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
4607
- ${lines.join("\n")}`);
4684
+ ${lines2.join("\n")}`);
4608
4685
  }
4609
- for (const l of lines) console.warn(l);
4686
+ for (const l of lines2) console.warn(l);
4610
4687
  };
4611
4688
  }
4612
4689
 
@@ -4792,7 +4869,7 @@ function confirmedArityForDef(def) {
4792
4869
  severity: "warn",
4793
4870
  where: `node("${def.type ?? "<inferred>"}")`,
4794
4871
  subject,
4795
- fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and clones \`_ctx.state\` on every match`,
4872
+ fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and hands it \`_ctx.state\` on every match`,
4796
4873
  otherwise: "only the tiers the reducer actually declares would be captured (arity >= 1 children, >= 2 fields, >= 4 raw, >= 5 trivia, >= 6 state). Declare it with `node(..., { buildArity: n })` if it cannot be derived"
4797
4874
  });
4798
4875
  return null;
@@ -4853,6 +4930,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
4853
4930
  return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen)) || d.recoverAt !== void 0 && parserHasOwnFields(d.recoverAt, seen) || d.stopAt !== void 0 && parserHasOwnFields(d.stopAt, seen);
4854
4931
  case "recover":
4855
4932
  return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
4933
+ case "lines":
4934
+ return parserHasOwnFields(d.body, seen);
4856
4935
  case "routed":
4857
4936
  return d.fallback ? parserHasOwnFields(d.fallback, seen) : false;
4858
4937
  case "many":
@@ -4947,7 +5026,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
4947
5026
  }
4948
5027
  const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || buildArity !== void 0 || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...buildArity !== void 0 ? { buildArity } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
4949
5028
  const capturesTrivia = captureTrivia || (build ? buildReadsTrivia(def) : project === void 0);
4950
- const clonesState = build ? buildReadsState(def) : project === void 0;
5029
+ const readsState = build ? buildReadsState(def) : project === void 0;
4951
5030
  const hasOwnFields = parserHasOwnFields(combinator);
4952
5031
  const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
4953
5032
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
@@ -4988,12 +5067,11 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
4988
5067
  ctx._triviaCaptureMask = savedMask;
4989
5068
  const { children: children3, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
4990
5069
  if (!r.ok) return r;
4991
- const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
5070
+ const st = readsState ? ctx.state : void 0;
4992
5071
  const nodeType = def.type ?? missingInferredType();
4993
5072
  const cstOutput = cstOutputHost(ctx.build);
4994
5073
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
4995
- const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
4996
- const built = unwrap && children3.length === 1 ? unwrapChild(children3[0]) : collapse && children3.length === 1 ? children3[0] : (cstOutput || !build && project === void 0) && ctx.build?._parsemanCstCollapse && children3.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children3[0], children3, rawChildren) ? children3[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children3, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children3, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children3, fields, span, rawChildren, triviaLog, hostState, tags) : build(children3, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children3, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children3 };
5074
+ const built = unwrap && children3.length === 1 ? unwrapChild(children3[0]) : collapse && children3.length === 1 ? children3[0] : (cstOutput || !build && project === void 0) && ctx.build?._parsemanCstCollapse && children3.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children3[0], children3, rawChildren) ? children3[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children3, fields, span, rawChildren, triviaLog, ctx.state, tags) : projectChild(children3, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children3, fields, span, rawChildren, triviaLog, ctx.state, tags) : build(children3, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children3, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children3 };
4997
5075
  const rawEntry2 = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
4998
5076
  if (saved.buf !== void 0 || saved.ch !== void 0) {
4999
5077
  pushCstChild(ctx, built, rawEntry2);
@@ -5266,1100 +5344,521 @@ function sourceLeaf(root, fn) {
5266
5344
  return makeLeaf(root, fn, true);
5267
5345
  }
5268
5346
 
5269
- // src/analysis/gating.ts
5270
- function childrenOf2(d) {
5271
- switch (d.tag) {
5272
- case "sequence":
5273
- case "choice":
5274
- return d.parsers;
5275
- case "dispatch":
5276
- return [
5277
- d.selector,
5278
- ...d.cases.map((c) => c.parser),
5279
- ...d.matchers ? d.matchers.map((c) => c.parser) : [],
5280
- ...d.otherwise === void 0 ? [] : [d.otherwise]
5281
- ];
5282
- case "sepBy":
5283
- return [d.parser, d.separator];
5284
- case "recover":
5285
- return [d.parser, d.sentinel];
5286
- case "scanTo":
5287
- return [
5288
- d.sentinel,
5289
- ...d.skip,
5290
- ...d.recoverAt === void 0 ? [] : [d.recoverAt],
5291
- ...d.stopAt === void 0 ? [] : [d.stopAt]
5292
- ];
5293
- case "grammar":
5294
- return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
5295
- case "routed":
5296
- return d.fallback ? [d.fallback] : [];
5297
- // A `lazy` is a REFERENCE, not a subtree. Descending through it would make
5298
- // every rule's walk cover the whole reachable grammar — site paths become
5299
- // nonsense, and a structural hash includes half the grammar. Treated as a
5300
- // leaf, keyed by the rule name it refers to, which is also the right
5301
- // semantics: two productions referencing `g.Ident` really do fill that slot
5302
- // the same way.
5303
- case "lazy":
5304
- case "literal":
5305
- case "regex":
5306
- case "keywords":
5307
- case "guard":
5308
- case "adjacency":
5309
- case "unknown":
5310
- return [];
5311
- default: {
5312
- const rec = d;
5313
- return rec.parser ? [rec.parser] : [];
5314
- }
5347
+ // src/table/lex-body.ts
5348
+ function buildLexBodyRecognizer(spec, constants) {
5349
+ const raw = constants[spec[0]];
5350
+ if (!(raw instanceof RegExp)) {
5351
+ throw new TypeError("table lexical body does not reference a RegExp constant");
5315
5352
  }
5316
- }
5317
- var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
5318
- function firstSetToString(fs3) {
5319
- if (fs3.kind === "any") return "ANY";
5320
- if (fs3.kind === "empty") return "(empty)";
5321
- return fs3.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
5353
+ if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
5354
+ throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
5355
+ }
5356
+ const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
5357
+ const suffix = spec[1];
5358
+ return (input, pos) => {
5359
+ re.lastIndex = pos;
5360
+ if (!re.test(input)) return -1;
5361
+ const end = re.lastIndex;
5362
+ if (suffix < 0) return end * 2;
5363
+ return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
5364
+ };
5322
5365
  }
5323
5366
 
5324
- // src/analysis/commitment.ts
5325
- function mayFail(p, seen = /* @__PURE__ */ new Set()) {
5326
- if (seen.has(p)) return true;
5327
- seen.add(p);
5328
- const d = p._def;
5329
- switch (d.tag) {
5330
- case "literal":
5331
- return d.value.length > 0;
5332
- case "trivia":
5333
- return false;
5334
- // `optional`/`many`/a min-0 `sepBy` swallow an ORDINARY failure — zero
5335
- // repetitions is a match. They do NOT swallow a COMMITTED one: a dispatch
5336
- // that has selected an arm and then failed inside it breaks out past every
5337
- // enclosing repeat, straight to the fallible boundary. So these are total
5338
- // exactly when their body cannot commit. (Measured: treating them as
5339
- // unconditionally total left an extra zero-width leaf in
5340
- // `BareInterpolatedSelector` on benchmark.less — a rollback the legacy
5341
- // build fired and this one had deleted.)
5342
- case "optional":
5343
- case "many":
5344
- return mayCommitFailure(d.parser);
5345
- case "sepBy":
5346
- return d.min >= 1 ? mayFail(d.parser, seen) : mayCommitFailure(d.parser) || mayCommitFailure(d.separator);
5347
- case "oneOrMore":
5348
- return mayFail(d.parser, seen);
5349
- case "sequence":
5350
- return d.parsers.some((x) => mayFail(x, seen));
5351
- // Delegating wrappers: as fallible as what they wrap.
5352
- case "node":
5353
- case "transform":
5354
- case "label":
5355
- case "field":
5356
- case "expect":
5357
- case "withCtx":
5358
- case "grammar":
5359
- case "token":
5360
- case "leaf":
5361
- return mayFail(d.parser, seen);
5362
- case "lazy": {
5363
- try {
5364
- return mayFail(d.thunk(), seen);
5365
- } catch {
5366
- return true;
5367
+ // src/table/lex-program.ts
5368
+ var LEX_NODE_TERMINAL = 0;
5369
+ var LEX_NODE_SEQUENCE2 = 1;
5370
+ var LEX_NODE_SEQUENCE3 = 2;
5371
+ var LEX_NODE_SEQUENCE4 = 3;
5372
+ var LEX_NODE_SEQUENCE5 = 4;
5373
+ var LEX_NODE_CHOICE2 = 5;
5374
+ var LEX_NODE_CHOICE4 = 6;
5375
+ var LEX_NODE_CHOICE8 = 7;
5376
+ var LEX_NODE_NOT = 8;
5377
+ var LEX_NODE_OPTIONAL = 9;
5378
+ function lexProgramDigest(words) {
5379
+ let hash = 2166136261;
5380
+ for (const word2 of words) {
5381
+ hash ^= word2 | 0;
5382
+ hash = Math.imul(hash, 16777619);
5383
+ }
5384
+ return hash | 0;
5385
+ }
5386
+ function classHas(cls, code) {
5387
+ if (code < 0) return false;
5388
+ if (code < 128) return cls.ascii[code] === 1;
5389
+ for (let i = 0; i < cls.hi.length; i += 2) {
5390
+ if (code >= cls.hi[i] && code <= cls.hi[i + 1]) return true;
5391
+ }
5392
+ return false;
5393
+ }
5394
+ var committed = (ctx) => ctx._fc === true;
5395
+ function trackLexLines(ctx, input, end) {
5396
+ let at = ctx._lineScannedTo ?? 0;
5397
+ const starts = ctx._lineStarts;
5398
+ if (starts === void 0 || end <= at) return;
5399
+ while (at < end) {
5400
+ if (input.charCodeAt(at) === 10) starts.push(at + 1);
5401
+ at++;
5402
+ }
5403
+ ctx._lineScannedTo = end;
5404
+ }
5405
+ function terminalNode(matcher, failure, tracksLines) {
5406
+ const match = (input, pos) => {
5407
+ const result = matcher(input, pos);
5408
+ return result < 0 ? -1 : result / 2;
5409
+ };
5410
+ return {
5411
+ match,
5412
+ run: (input, pos, ctx) => {
5413
+ const end = match(input, pos);
5414
+ if (end < 0) {
5415
+ ctx._fe = pos;
5416
+ ctx._fx = failure;
5417
+ if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
5418
+ return -1;
5367
5419
  }
5420
+ if (tracksLines) trackLexLines(ctx, input, end);
5421
+ return end;
5368
5422
  }
5369
- // An adjacency assertion is a TEST — failing is its whole job.
5370
- case "adjacency":
5371
- return true;
5372
- default:
5373
- return true;
5374
- }
5423
+ };
5375
5424
  }
5376
- function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set(), triviaActive = true) {
5377
- if (seen.has(p)) return false;
5378
- seen.add(p);
5379
- const d = p._def;
5380
- switch (d.tag) {
5381
- // Atomic / non-capturing-then-failing: a failure happens before any push.
5382
- case "literal":
5383
- case "regex":
5384
- case "keywords":
5385
- case "guard":
5386
- // Adjacency probes with `_cap = 0` / no `commit()`: it cannot push anything.
5387
- case "adjacency":
5388
- case "not":
5389
- // peek(): emitted under a non-capturing probe ctx and zero-width on both
5390
- // outcomes, so it can never leave a partial capture behind.
5391
- case "peek":
5392
- case "trivia":
5393
- case "token":
5394
- case "leaf":
5395
- case "scanTo":
5396
- case "unknown":
5397
- return false;
5398
- // node() captures into its own private buffers and rolls them back on
5399
- // failure (emitNode restores _ctx.* and never pushes on the !ok path).
5400
- case "node":
5401
- return false;
5402
- // choice/firstMatch already roll back each failed arm; on overall failure
5403
- // nothing committed remains.
5404
- case "choice":
5405
- return false;
5406
- case "dispatch":
5407
- return capturesLeaf(d.selector, new Set(seen)) || d.cases.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) || (d.matchers ? d.matchers.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) : false) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, new Set(seen), triviaActive) || capturesLeaf(d.otherwise, new Set(seen)) : false);
5408
- case "attempt":
5409
- return false;
5410
- // optional never fails; many/oneOrMore only "fail" with zero captured items.
5411
- case "optional":
5412
- case "many":
5413
- case "oneOrMore":
5414
- return false;
5415
- // sepBy emits its own per-iteration rollback in emitSepBy.
5416
- case "sepBy":
5417
- return false;
5418
- // A sequence is the real case: an earlier capturing term followed by a term
5419
- // that can fail leaves the earlier captures buffered.
5420
- //
5421
- // BOTH halves of that sentence are load-bearing, and the second half is what
5422
- // this walk exists to exploit. "Some earlier term captures" alone was the old
5423
- // test, and it is true of very nearly every sequence in a real grammar — a
5424
- // sequence almost always opens with a literal or a node. The rollback is only
5425
- // reachable if something AFTER the capture can still fail; once the remainder
5426
- // is total (`optional`/`many`/trivia/a min-0 `sepBy`), the sequence has no
5427
- // failure path left to roll back from, and the save+restore is dead code.
5428
- //
5429
- // `captured` tracks whether anything is in the buffers yet as the scan moves
5430
- // left to right. Two things put it there:
5431
- // - a term that captures (`capturesLeaf`/`hasNodeDef`), and
5432
- // - the inter-term TRIVIA scan, which under `capturing` pushes into
5433
- // `_cstRawChildren`/`_cstTriviaLog` BEFORE the term at each boundary
5434
- // i >= 1. `emitSeqValues` only rewinds that push on the term's SUCCESS
5435
- // path (the "matched empty, so leave the whitespace out of the span"
5436
- // branch); a term that FAILS breaks straight out with the trivia still
5437
- // buffered, so from boundary 1 onward the buffers are dirty regardless of
5438
- // what the earlier terms did.
5439
- // Each term is also asked about itself: a nested sequence can leave its own
5440
- // partial capture behind, and its terms are emitted inline into this same
5441
- // block with no boundary of their own to roll them back.
5442
- case "sequence": {
5443
- const parts = d.parsers;
5444
- let captured = false;
5445
- for (let i = 0; i < parts.length; i++) {
5446
- const part = parts[i];
5447
- if (i > 0 && triviaActive) captured = true;
5448
- if (captured && mayFail(part)) return true;
5449
- if (mayLeavePartialCapture(part, seen, triviaActive)) return true;
5450
- if (capturesLeaf(part) || hasNodeDef(part)) captured = true;
5451
- }
5452
- return false;
5453
- }
5454
- // Delegating wrappers: defer to the wrapped parser.
5455
- case "transform":
5456
- case "label":
5457
- case "field":
5458
- case "expect":
5459
- case "withCtx":
5460
- case "grammar":
5461
- return mayLeavePartialCapture(d.parser, seen, triviaActive);
5462
- case "recover":
5463
- return mayLeavePartialCapture(d.parser, seen, triviaActive);
5464
- case "lazy": {
5465
- try {
5466
- return mayLeavePartialCapture(d.thunk(), seen, triviaActive);
5467
- } catch {
5468
- return true;
5469
- }
5425
+ function sequence2(a, b) {
5426
+ return {
5427
+ match: (input, pos) => {
5428
+ const e0 = a.match(input, pos);
5429
+ return e0 < 0 ? -1 : b.match(input, e0);
5430
+ },
5431
+ run: (input, pos, ctx) => {
5432
+ const e0 = a.run(input, pos, ctx);
5433
+ if (e0 < 0) return -1;
5434
+ ctx._fc = false;
5435
+ return b.run(input, e0, ctx);
5470
5436
  }
5471
- // Unknown shapes: be safe and keep the rollback.
5472
- default:
5473
- return true;
5474
- }
5437
+ };
5475
5438
  }
5476
- function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
5477
- if (seen.has(p)) return false;
5478
- seen.add(p);
5479
- const d = p._def;
5480
- switch (d.tag) {
5481
- case "literal":
5482
- case "regex":
5483
- case "keywords":
5484
- case "node":
5485
- case "token":
5486
- case "routed":
5487
- case "leaf":
5488
- return true;
5489
- case "not":
5490
- case "peek":
5491
- case "guard":
5492
- case "adjacency":
5493
- case "trivia":
5494
- case "unknown":
5495
- return false;
5496
- case "sequence":
5497
- case "choice":
5498
- return d.parsers.some((x) => capturesLeaf(x, seen));
5499
- case "dispatch":
5500
- return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => capturesLeaf(entry.parser, seen)) : false) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
5501
- case "sepBy":
5502
- return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
5503
- case "many":
5504
- case "oneOrMore":
5505
- case "optional":
5506
- case "attempt":
5507
- case "transform":
5508
- case "label":
5509
- case "field":
5510
- case "expect":
5511
- case "withCtx":
5512
- case "grammar":
5513
- case "recover":
5514
- return capturesLeaf(d.parser, seen);
5515
- case "scanTo":
5516
- return true;
5517
- case "lazy": {
5518
- try {
5519
- return capturesLeaf(d.thunk(), seen);
5520
- } catch {
5521
- return true;
5522
- }
5439
+ function sequence3(a, b, c) {
5440
+ return {
5441
+ match: (input, pos) => {
5442
+ const e0 = a.match(input, pos);
5443
+ if (e0 < 0) return -1;
5444
+ const e1 = b.match(input, e0);
5445
+ return e1 < 0 ? -1 : c.match(input, e1);
5446
+ },
5447
+ run: (input, pos, ctx) => {
5448
+ const e0 = a.run(input, pos, ctx);
5449
+ if (e0 < 0) return -1;
5450
+ ctx._fc = false;
5451
+ const e1 = b.run(input, e0, ctx);
5452
+ if (e1 < 0) return -1;
5453
+ ctx._fc = false;
5454
+ return c.run(input, e1, ctx);
5523
5455
  }
5524
- default:
5525
- return true;
5526
- }
5456
+ };
5527
5457
  }
5528
- function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5529
- if (seen.has(p)) return false;
5530
- seen.add(p);
5531
- const d = p._def;
5532
- switch (d.tag) {
5533
- case "node":
5534
- return true;
5535
- case "lazy": {
5536
- try {
5537
- return hasNodeDef(d.thunk(), seen);
5538
- } catch {
5539
- return false;
5540
- }
5458
+ function sequence4(a, b, c, d) {
5459
+ const abc = sequence3(a, b, c);
5460
+ return {
5461
+ match: (input, pos) => {
5462
+ const end = abc.match(input, pos);
5463
+ return end < 0 ? -1 : d.match(input, end);
5464
+ },
5465
+ run: (input, pos, ctx) => {
5466
+ const end = abc.run(input, pos, ctx);
5467
+ if (end < 0) return -1;
5468
+ ctx._fc = false;
5469
+ return d.run(input, end, ctx);
5541
5470
  }
5542
- case "grammar":
5543
- case "trivia":
5544
- case "token":
5545
- case "leaf":
5546
- case "label":
5547
- case "field":
5548
- case "optional":
5549
- case "many":
5550
- case "oneOrMore":
5551
- case "not":
5552
- case "peek":
5553
- case "transform":
5554
- return hasNodeDef(d.parser, seen);
5555
- case "sequence":
5556
- case "choice":
5557
- return d.parsers.some((x) => hasNodeDef(x, seen));
5558
- case "dispatch":
5559
- return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => hasNodeDef(entry.parser, seen)) : false) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
5560
- case "sepBy":
5561
- return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
5562
- case "scanTo":
5563
- return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen)) || d.recoverAt !== void 0 && hasNodeDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasNodeDef(d.stopAt, seen);
5564
- case "recover":
5565
- return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
5566
- case "expect":
5567
- return hasNodeDef(d.parser, seen);
5568
- case "withCtx":
5569
- return hasNodeDef(d.parser, seen);
5570
- default:
5571
- return false;
5572
- }
5471
+ };
5573
5472
  }
5574
- function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
5575
- if (seen.has(p)) return false;
5576
- seen.add(p);
5577
- const d = p._def;
5578
- switch (d.tag) {
5579
- case "node":
5580
- return d.build !== void 0 || hasDirectBuildDef(d.parser, seen);
5581
- case "lazy": {
5582
- try {
5583
- return hasDirectBuildDef(d.thunk(), seen);
5584
- } catch {
5585
- return false;
5586
- }
5473
+ function sequence5(a, b, c, d, e) {
5474
+ const abcd = sequence4(a, b, c, d);
5475
+ return {
5476
+ match: (input, pos) => {
5477
+ const end = abcd.match(input, pos);
5478
+ return end < 0 ? -1 : e.match(input, end);
5479
+ },
5480
+ run: (input, pos, ctx) => {
5481
+ const end = abcd.run(input, pos, ctx);
5482
+ if (end < 0) return -1;
5483
+ ctx._fc = false;
5484
+ return e.run(input, end, ctx);
5587
5485
  }
5588
- case "grammar":
5589
- case "trivia":
5590
- case "token":
5591
- case "leaf":
5592
- case "label":
5593
- case "field":
5594
- case "optional":
5595
- case "many":
5596
- case "oneOrMore":
5597
- case "attempt":
5598
- case "not":
5599
- case "peek":
5600
- case "withCtx":
5601
- case "expect":
5602
- case "transform":
5603
- return hasDirectBuildDef(d.parser, seen);
5604
- case "sequence":
5605
- case "choice":
5606
- return d.parsers.some((x) => hasDirectBuildDef(x, seen));
5607
- case "dispatch":
5608
- return hasDirectBuildDef(d.selector, seen) || d.cases.some((x) => hasDirectBuildDef(x.parser, seen)) || (d.matchers ? d.matchers.some((x) => hasDirectBuildDef(x.parser, seen)) : false) || (d.otherwise ? hasDirectBuildDef(d.otherwise, seen) : false);
5609
- case "sepBy":
5610
- return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
5611
- case "scanTo":
5612
- return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen)) || d.recoverAt !== void 0 && hasDirectBuildDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasDirectBuildDef(d.stopAt, seen);
5613
- case "recover":
5614
- return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
5615
- case "routed":
5616
- return d.fallback ? hasDirectBuildDef(d.fallback, seen) : false;
5617
- default:
5618
- return false;
5619
- }
5486
+ };
5620
5487
  }
5621
- function mayCommitFailure(p, resolve3, memo = /* @__PURE__ */ new Map()) {
5622
- return reachesMarked(p, (node2) => commitFailureSuccessors(node2, resolve3), memo);
5488
+ function appendExpected(acc, values) {
5489
+ if (values === void 0 || values.length === 0) return acc;
5490
+ if (acc === void 0) return values.slice();
5491
+ acc.push(...values);
5492
+ return acc;
5623
5493
  }
5624
- var NO_EDGES = [];
5625
- function commitFailureSuccessors(p, resolve3) {
5626
- const d = p._def;
5627
- switch (d.tag) {
5628
- case "dispatch":
5629
- case "unknown":
5630
- return true;
5631
- case "choice":
5632
- case "sequence":
5633
- return d.parsers;
5634
- // A contained transaction reports every failure uncommitted.
5635
- case "attempt":
5636
- return d.contain === true ? NO_EDGES : [d.parser];
5637
- case "many":
5638
- case "oneOrMore":
5639
- case "optional":
5640
- case "transform":
5641
- case "label":
5642
- case "field":
5643
- case "grammar":
5644
- case "node":
5645
- case "token":
5646
- case "leaf":
5647
- case "withCtx":
5648
- return [d.parser];
5649
- case "sepBy":
5650
- return [d.parser, d.separator];
5651
- case "scanTo":
5652
- return [
5653
- d.sentinel,
5654
- ...d.skip,
5655
- ...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
5656
- ...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
5657
- ];
5658
- case "lazy": {
5659
- try {
5660
- return [d.thunk()];
5661
- } catch {
5662
- const name = p._ruleName;
5663
- const target = name === void 0 ? void 0 : resolve3?.(name);
5664
- return target === void 0 ? true : [target];
5494
+ function choiceNode(arms, classes, armExpected, choiceExpected) {
5495
+ let matchTail = () => -1;
5496
+ for (let i = arms.length - 1; i >= 0; i--) {
5497
+ const arm = arms[i], next = matchTail;
5498
+ matchTail = (input, pos) => {
5499
+ const end = arm.match(input, pos);
5500
+ return end < 0 ? next(input, pos) : end;
5501
+ };
5502
+ }
5503
+ let tail = (_input, pos, ctx, _lead, acc) => {
5504
+ ctx._fe = pos;
5505
+ ctx._fx = acc ?? choiceExpected;
5506
+ return -1;
5507
+ };
5508
+ for (let i = arms.length - 1; i >= 0; i--) {
5509
+ const arm = arms[i], cls = classes[i], staticExpected = armExpected[i], next = tail;
5510
+ tail = (input, pos, ctx, lead2, acc, best) => {
5511
+ if (cls !== void 0 && !classHas(cls, lead2)) {
5512
+ return next(
5513
+ input,
5514
+ pos,
5515
+ ctx,
5516
+ lead2,
5517
+ best === pos ? appendExpected(acc, staticExpected) : acc,
5518
+ best
5519
+ );
5665
5520
  }
5666
- }
5667
- default:
5668
- return NO_EDGES;
5521
+ ctx._fc = false;
5522
+ const end = arm.run(input, pos, ctx);
5523
+ if (end >= 0) return end;
5524
+ const at = ctx._fe ?? pos;
5525
+ if (at > best) {
5526
+ best = at;
5527
+ acc = void 0;
5528
+ }
5529
+ const nextAcc = at === best ? appendExpected(acc, ctx._fx) : acc;
5530
+ if (committed(ctx)) {
5531
+ if (nextAcc !== void 0) ctx._fx = nextAcc;
5532
+ return -1;
5533
+ }
5534
+ return next(input, pos, ctx, lead2, nextAcc, best);
5535
+ };
5669
5536
  }
5537
+ return {
5538
+ match: matchTail,
5539
+ run: (input, pos, ctx) => tail(
5540
+ input,
5541
+ pos,
5542
+ ctx,
5543
+ pos < input.length ? input.codePointAt(pos) : -1,
5544
+ void 0,
5545
+ pos
5546
+ )
5547
+ };
5670
5548
  }
5671
- function reachesMarked(root, successors, memo) {
5672
- const known = memo.get(root);
5673
- if (known !== void 0) return known;
5674
- const order = /* @__PURE__ */ new Map();
5675
- const low = [];
5676
- const found = [];
5677
- const stack = [];
5678
- const visit = (node2) => {
5679
- const at = low.length;
5680
- order.set(node2, at);
5681
- low.push(at);
5682
- found.push(false);
5683
- stack.push(node2);
5684
- const next = successors(node2);
5685
- if (next === true) found[at] = true;
5686
- else {
5687
- for (const child of next) {
5688
- if (found[at]) break;
5689
- const hit = memo.get(child);
5690
- if (hit !== void 0) {
5691
- if (hit) found[at] = true;
5692
- continue;
5693
- }
5694
- const seen = order.get(child);
5695
- if (seen !== void 0) {
5696
- if (seen < low[at]) low[at] = seen;
5697
- continue;
5698
- }
5699
- const k = visit(child);
5700
- if (low[k] < low[at]) low[at] = low[k];
5701
- if (found[k]) found[at] = true;
5702
- }
5703
- }
5704
- if (low[at] === at) {
5705
- const answer = found[at];
5706
- let member;
5707
- do {
5708
- member = stack.pop();
5709
- memo.set(member, answer);
5710
- } while (member !== node2);
5549
+ function notNode(body, failure) {
5550
+ return {
5551
+ match: (input, pos) => body.match(input, pos) < 0 ? pos : -1,
5552
+ run: (input, pos, ctx) => {
5553
+ if (body.match(input, pos) < 0) return pos;
5554
+ ctx._fe = pos;
5555
+ ctx._fx = failure;
5556
+ return -1;
5711
5557
  }
5712
- return at;
5713
5558
  };
5714
- return found[visit(root)];
5715
5559
  }
5716
- function hasSemanticReduction(roots, externalRefs) {
5717
- const seen = /* @__PURE__ */ new Set();
5718
- const visit = (parser2) => {
5719
- if (seen.has(parser2)) return false;
5720
- seen.add(parser2);
5721
- const def = parser2._def;
5722
- if (def.tag === "transform" && !def.recognitionOnly) return true;
5723
- if (def.tag === "choice" && def.gates.some(Boolean)) return true;
5724
- if (def.tag === "guard" || def.tag === "withCtx") return true;
5725
- if (def.tag === "node" && def.build !== void 0) return true;
5726
- if (def.tag === "lazy") {
5727
- if (externalRefs?.has(parser2)) return false;
5728
- try {
5729
- return visit(def.thunk());
5730
- } catch {
5731
- return true;
5732
- }
5560
+ function optionalNode(body) {
5561
+ return {
5562
+ match: (input, pos) => {
5563
+ const end = body.match(input, pos);
5564
+ return end < 0 ? pos : end;
5565
+ },
5566
+ run: (input, pos, ctx) => {
5567
+ ctx._fc = false;
5568
+ const end = body.run(input, pos, ctx);
5569
+ return end < 0 && !committed(ctx) ? pos : end;
5733
5570
  }
5734
- return childrenOf2(def).some(visit);
5735
5571
  };
5736
- return roots.some(visit);
5737
5572
  }
5738
- function classifyRuleMap(ruleMap) {
5739
- const externalRefs = /* @__PURE__ */ new Set();
5740
- const scanned = /* @__PURE__ */ new Set();
5741
- const scanExternal = (p) => {
5742
- if (scanned.has(p)) return;
5743
- scanned.add(p);
5744
- const def = p._def;
5745
- if (def.tag === "lazy") {
5746
- let resolved;
5747
- try {
5748
- resolved = def.thunk();
5749
- } catch {
5750
- resolved = void 0;
5573
+ function fixedTreeRunner(words, matchers, classes, expected) {
5574
+ const stack = [];
5575
+ const pop = () => {
5576
+ const value = stack.pop();
5577
+ if (value === void 0) throw new TypeError("table lexical tree has an incomplete fixed body");
5578
+ return value;
5579
+ };
5580
+ for (let at = 0; at < words.length; ) {
5581
+ const op = words[at++];
5582
+ if (op === LEX_NODE_TERMINAL) {
5583
+ const matcher = matchers[words[at++]], failure = expected[words[at++]];
5584
+ const tracks = words[at++];
5585
+ if (matcher === void 0 || failure === void 0 || tracks !== 0 && tracks !== 1) {
5586
+ throw new TypeError("table lexical tree terminal references an invalid pool entry");
5751
5587
  }
5752
- if (resolved === void 0) {
5753
- if (p._ruleName) externalRefs.add(p);
5754
- return;
5588
+ stack.push(terminalNode(matcher, failure, tracks === 1));
5589
+ continue;
5590
+ }
5591
+ if (op >= LEX_NODE_SEQUENCE2 && op <= LEX_NODE_SEQUENCE5) {
5592
+ const count = op - LEX_NODE_SEQUENCE2 + 2;
5593
+ const nodes2 = Array.from({ length: count }, pop).reverse();
5594
+ if (count === 2) stack.push(sequence2(nodes2[0], nodes2[1]));
5595
+ else if (count === 3) stack.push(sequence3(nodes2[0], nodes2[1], nodes2[2]));
5596
+ else if (count === 4) stack.push(sequence4(nodes2[0], nodes2[1], nodes2[2], nodes2[3]));
5597
+ else stack.push(sequence5(nodes2[0], nodes2[1], nodes2[2], nodes2[3], nodes2[4]));
5598
+ continue;
5599
+ }
5600
+ if (op >= LEX_NODE_CHOICE2 && op <= LEX_NODE_CHOICE8) {
5601
+ const count = op === LEX_NODE_CHOICE2 ? 2 : op === LEX_NODE_CHOICE4 ? 4 : 8;
5602
+ const choiceFx = expected[words[at++]];
5603
+ if (choiceFx === void 0) throw new TypeError("table lexical tree choice has an invalid expected set");
5604
+ const armClasses = [];
5605
+ const armFx = [];
5606
+ for (let i = 0; i < count; i++) {
5607
+ const cls = words[at++], fx = expected[words[at++]];
5608
+ if (cls < -1 || cls >= 0 && classes[cls] === void 0 || fx === void 0) {
5609
+ throw new TypeError("table lexical tree choice references an invalid pool entry");
5610
+ }
5611
+ armClasses.push(cls < 0 ? void 0 : classes[cls]);
5612
+ armFx.push(fx);
5755
5613
  }
5756
- scanExternal(resolved);
5757
- return;
5614
+ const arms = Array.from({ length: count }, pop).reverse();
5615
+ stack.push(choiceNode(arms, armClasses, armFx, choiceFx));
5616
+ continue;
5758
5617
  }
5759
- for (const child of childrenOf2(def)) scanExternal(child);
5760
- };
5761
- for (const [, rule] of ruleMap) scanExternal(rule);
5762
- return {
5763
- hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
5764
- isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs)
5765
- };
5618
+ if (op === LEX_NODE_NOT) {
5619
+ const failure = expected[words[at++]];
5620
+ if (failure === void 0) throw new TypeError("table lexical tree assertion has an invalid expected set");
5621
+ stack.push(notNode(pop(), failure));
5622
+ continue;
5623
+ }
5624
+ if (op === LEX_NODE_OPTIONAL) {
5625
+ stack.push(optionalNode(pop()));
5626
+ continue;
5627
+ }
5628
+ throw new TypeError("table lexical tree has an unknown fixed body");
5629
+ }
5630
+ if (stack.length !== 1) throw new TypeError("table lexical tree does not resolve to one root body");
5631
+ return stack[0].run;
5766
5632
  }
5767
-
5768
- // src/table/ops.ts
5769
- var OP_LIT = 1;
5770
- var OP_RX = 2;
5771
- var OP_SEQ = 3;
5772
- var OP_SEQV = 4;
5773
- var OP_CHOICE = 5;
5774
- var OP_REP = 6;
5775
- var OP_REPV = 7;
5776
- var OP_OPT = 8;
5777
- var OP_XFORM = 9;
5778
- var OP_NODE = 10;
5779
- var OP_RULE = 11;
5780
- var OP_GATE = 12;
5781
- var OP_NOT = 13;
5782
- var OP_PEEK = 14;
5783
- var OP_LEAF = 15;
5784
- var OP_EMPTY = 16;
5785
- var OP_LIT_TRACK = 17;
5786
- var OP_RX_TRACK = 18;
5787
- var OP_NODE_TRACK = 19;
5788
- var OP_SCOPE = 20;
5789
- var OP_EXPECT = 21;
5790
- var OP_SEQX = 22;
5791
- var OP_SCAN = 23;
5792
- var OP_FIELD = 24;
5793
- var OP_LIT_CI = 27;
5794
- var OP_LIT_CI_TRACK = 28;
5795
- var OP_TOKEN = 29;
5796
- var OP_SCOPE_CAP = 30;
5797
- var OP_WITHCTX = 31;
5798
- var OP_GUARD = 32;
5799
- var OP_ADJ = 33;
5800
- var OP_GREEDY = 34;
5801
- var OP_REJECT = 35;
5802
- var OP_ARMGATE = 36;
5803
- var OP_LIVE = 37;
5804
- var OP_ATTEMPT = 38;
5805
- var OP_LABEL = 39;
5806
- var OP_COV = 40;
5807
- var OP_SCOPE_PLAIN = 41;
5808
- var OP_LEX_BODY = 42;
5809
- var OP_LEX_PROGRAM = 43;
5810
- var OP_SOURCE_LEAF = 44;
5811
- var OP_DISPATCH = 25;
5812
- var OP_ROUTED = 26;
5813
- var OP_NAMES = {
5814
- [OP_LIT]: "LIT",
5815
- [OP_RX]: "RX",
5816
- [OP_SEQ]: "SEQ",
5817
- [OP_SEQV]: "SEQV",
5818
- [OP_CHOICE]: "CHOICE",
5819
- [OP_REP]: "REP",
5820
- [OP_REPV]: "REPV",
5821
- [OP_OPT]: "OPT",
5822
- [OP_XFORM]: "XFORM",
5823
- [OP_NODE]: "NODE",
5824
- [OP_RULE]: "RULE",
5825
- [OP_GATE]: "GATE",
5826
- [OP_NOT]: "NOT",
5827
- [OP_PEEK]: "PEEK",
5828
- [OP_LEAF]: "LEAF",
5829
- [OP_SOURCE_LEAF]: "SOURCE_LEAF",
5830
- [OP_EMPTY]: "EMPTY",
5831
- [OP_LIT_TRACK]: "LIT_TRACK",
5832
- [OP_RX_TRACK]: "RX_TRACK",
5833
- [OP_NODE_TRACK]: "NODE_TRACK",
5834
- [OP_SCOPE]: "SCOPE",
5835
- [OP_EXPECT]: "EXPECT",
5836
- [OP_SEQX]: "SEQX",
5837
- [OP_SCAN]: "SCAN",
5838
- [OP_FIELD]: "FIELD",
5839
- [OP_LIT_CI]: "LIT_CI",
5840
- [OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
5841
- [OP_DISPATCH]: "DISPATCH",
5842
- [OP_ROUTED]: "ROUTED",
5843
- [OP_TOKEN]: "TOKEN",
5844
- [OP_SCOPE_CAP]: "SCOPE_CAP",
5845
- [OP_WITHCTX]: "WITHCTX",
5846
- [OP_GUARD]: "GUARD",
5847
- [OP_ADJ]: "ADJ",
5848
- [OP_GREEDY]: "GREEDY",
5849
- [OP_REJECT]: "REJECT",
5850
- [OP_ARMGATE]: "ARMGATE",
5851
- [OP_LIVE]: "LIVE",
5852
- [OP_ATTEMPT]: "ATTEMPT",
5853
- [OP_LABEL]: "LABEL",
5854
- [OP_COV]: "COV",
5855
- [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
5856
- [OP_LEX_BODY]: "LEX_BODY",
5857
- [OP_LEX_PROGRAM]: "LEX_PROGRAM"
5858
- };
5859
-
5860
- // src/table/lex-program.ts
5861
- var LEX_NODE_TERMINAL = 0;
5862
- var LEX_NODE_SEQUENCE2 = 1;
5863
- var LEX_NODE_SEQUENCE3 = 2;
5864
- var LEX_NODE_SEQUENCE4 = 3;
5865
- var LEX_NODE_SEQUENCE5 = 4;
5866
- var LEX_NODE_CHOICE2 = 5;
5867
- var LEX_NODE_CHOICE4 = 6;
5868
- var LEX_NODE_CHOICE8 = 7;
5869
- var LEX_NODE_NOT = 8;
5870
- var LEX_NODE_OPTIONAL = 9;
5871
- function lexProgramDigest(words) {
5872
- let hash = 2166136261;
5873
- for (const word2 of words) {
5874
- hash ^= word2 | 0;
5875
- hash = Math.imul(hash, 16777619);
5633
+ function buildLexProgramRunner(spec, matchers, classes, expected) {
5634
+ if (spec[0] !== 0 && spec[0] !== 1 && spec[0] !== 2 && spec[0] !== 3 || spec[0] === 0 && spec.length !== 13 || spec[0] === 1 && spec.length !== 12 || spec[0] === 2 && spec.length < 6 || spec[0] === 3 && spec.length !== 3) {
5635
+ throw new TypeError("table lexical program has an invalid fixed body width");
5876
5636
  }
5877
- return hash | 0;
5878
- }
5879
- function classHas(cls, code) {
5880
- if (code < 0) return false;
5881
- if (code < 128) return cls.ascii[code] === 1;
5882
- for (let i = 0; i < cls.hi.length; i += 2) {
5883
- if (code >= cls.hi[i] && code <= cls.hi[i + 1]) return true;
5637
+ if (spec[1] !== lexProgramDigest(spec.slice(2))) {
5638
+ throw new TypeError("table lexical program semantic digest is inconsistent");
5884
5639
  }
5885
- return false;
5886
- }
5887
- var committed = (ctx) => ctx._fc === true;
5888
- function trackLexLines(ctx, input, end) {
5889
- let at = ctx._lineScannedTo ?? 0;
5890
- const starts = ctx._lineStarts;
5891
- if (starts === void 0 || end <= at) return;
5892
- while (at < end) {
5893
- if (input.charCodeAt(at) === 10) starts.push(at + 1);
5894
- at++;
5640
+ if (spec[0] === 3) {
5641
+ if (!Number.isInteger(spec[2]) || spec[2] < 0) {
5642
+ throw new TypeError("table balanced lexical program has an invalid scan reference");
5643
+ }
5644
+ const run2 = (_input, _pos, ctx, scan) => {
5645
+ if (scan === void 0) throw new TypeError("table balanced lexical program lost its scan body");
5646
+ const result = scan.parse(_input, _pos, ctx);
5647
+ if (!result.ok) {
5648
+ ctx._fe = result.span.start;
5649
+ ctx._fx = result.expected ?? [];
5650
+ return -1;
5651
+ }
5652
+ return result.span.end;
5653
+ };
5654
+ return Object.assign(run2, { scan: spec[2] });
5895
5655
  }
5896
- ctx._lineScannedTo = end;
5897
- }
5898
- function terminalNode(matcher, failure, tracksLines) {
5899
- const match = (input, pos) => {
5900
- const result = matcher(input, pos);
5901
- return result < 0 ? -1 : result / 2;
5902
- };
5903
- return {
5904
- match,
5905
- run: (input, pos, ctx) => {
5906
- const end = match(input, pos);
5907
- if (end < 0) {
5656
+ if (spec[0] === 2) return fixedTreeRunner(spec.slice(2), matchers, classes, expected);
5657
+ if (spec[0] === 1) {
5658
+ const g00 = matchers[spec[2]], g01 = matchers[spec[3]];
5659
+ const g02 = matchers[spec[4]], g03 = matchers[spec[5]];
5660
+ const guard0Fx = expected[spec[6]];
5661
+ const g1 = matchers[spec[7]], guard1Fx = expected[spec[8]];
5662
+ const terminal = matchers[spec[9]], terminalFx = expected[spec[10]];
5663
+ const tracks2 = spec[11];
5664
+ if (g00 === void 0 || g01 === void 0 || g02 === void 0 || g03 === void 0 || guard0Fx === void 0 || g1 === void 0 || guard1Fx === void 0 || terminal === void 0 || terminalFx === void 0 || tracks2 !== 0 && tracks2 !== 1) {
5665
+ throw new TypeError("table lexical program references an invalid pool entry");
5666
+ }
5667
+ return (input, pos, ctx) => {
5668
+ ctx._fc = false;
5669
+ if (g00(input, pos) >= 0 || g01(input, pos) >= 0 || g02(input, pos) >= 0 || g03(input, pos) >= 0) {
5908
5670
  ctx._fe = pos;
5909
- ctx._fx = failure;
5671
+ ctx._fx = guard0Fx;
5672
+ return -1;
5673
+ }
5674
+ ctx._fc = false;
5675
+ if (g1(input, pos) >= 0) {
5676
+ ctx._fe = pos;
5677
+ ctx._fx = guard1Fx;
5678
+ return -1;
5679
+ }
5680
+ ctx._fc = false;
5681
+ const result = terminal(input, pos);
5682
+ if (result < 0) {
5683
+ ctx._fe = pos;
5684
+ ctx._fx = terminalFx;
5910
5685
  if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
5911
5686
  return -1;
5912
5687
  }
5913
- if (tracksLines) trackLexLines(ctx, input, end);
5688
+ const end = result / 2;
5689
+ if (tracks2 === 1) trackLexLines(ctx, input, end);
5914
5690
  return end;
5691
+ };
5692
+ }
5693
+ const cls = classes[spec[2]];
5694
+ const choiceFx = expected[spec[3]];
5695
+ const m0 = matchers[spec[4]], x0 = expected[spec[5]];
5696
+ const m1 = matchers[spec[6]], x1 = expected[spec[7]];
5697
+ const m2 = matchers[spec[8]], x2 = expected[spec[9]];
5698
+ const m3 = matchers[spec[10]], x3 = expected[spec[11]];
5699
+ const tracks = spec[12];
5700
+ if (cls === void 0 || choiceFx === void 0 || m0 === void 0 || x0 === void 0 || m1 === void 0 || x1 === void 0 || m2 === void 0 || x2 === void 0 || m3 === void 0 || x3 === void 0 || tracks !== 0 && tracks !== 1) {
5701
+ throw new TypeError("table lexical program references an invalid pool entry");
5702
+ }
5703
+ return (input, pos, ctx) => {
5704
+ const lead2 = pos < input.length ? input.codePointAt(pos) : -1;
5705
+ if (!classHas(cls, lead2)) {
5706
+ ctx._fe = pos;
5707
+ ctx._fx = choiceFx.slice();
5708
+ return -1;
5915
5709
  }
5710
+ let acc;
5711
+ const failed = (fx) => {
5712
+ ctx._fe = pos;
5713
+ ctx._fx = fx;
5714
+ if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
5715
+ if (acc === void 0) acc = fx.slice();
5716
+ else acc.push(...fx);
5717
+ };
5718
+ ctx._fc = false;
5719
+ const success = (result2) => {
5720
+ const end = result2 / 2;
5721
+ if (tracks === 1) trackLexLines(ctx, input, end);
5722
+ return end;
5723
+ };
5724
+ let result = m0(input, pos);
5725
+ if (result >= 0) return success(result);
5726
+ failed(x0);
5727
+ ctx._fc = false;
5728
+ result = m1(input, pos);
5729
+ if (result >= 0) return success(result);
5730
+ failed(x1);
5731
+ ctx._fc = false;
5732
+ result = m2(input, pos);
5733
+ if (result >= 0) return success(result);
5734
+ failed(x2);
5735
+ ctx._fc = false;
5736
+ result = m3(input, pos);
5737
+ if (result >= 0) return success(result);
5738
+ failed(x3);
5739
+ ctx._fe = pos;
5740
+ ctx._fx = acc ?? choiceFx;
5741
+ return -1;
5916
5742
  };
5917
5743
  }
5918
- function sequence2(a, b) {
5919
- return {
5920
- match: (input, pos) => {
5921
- const e0 = a.match(input, pos);
5922
- return e0 < 0 ? -1 : b.match(input, e0);
5923
- },
5924
- run: (input, pos, ctx) => {
5925
- const e0 = a.run(input, pos, ctx);
5926
- if (e0 < 0) return -1;
5927
- ctx._fc = false;
5928
- return b.run(input, e0, ctx);
5929
- }
5930
- };
5744
+
5745
+ // src/table/program.ts
5746
+ function closureArtifact(prog) {
5747
+ return { ...prog, asm: [] };
5931
5748
  }
5932
- function sequence3(a, b, c) {
5933
- return {
5934
- match: (input, pos) => {
5935
- const e0 = a.match(input, pos);
5936
- if (e0 < 0) return -1;
5937
- const e1 = b.match(input, e0);
5938
- return e1 < 0 ? -1 : c.match(input, e1);
5939
- },
5940
- run: (input, pos, ctx) => {
5941
- const e0 = a.run(input, pos, ctx);
5942
- if (e0 < 0) return -1;
5943
- ctx._fc = false;
5944
- const e1 = b.run(input, e0, ctx);
5945
- if (e1 < 0) return -1;
5946
- ctx._fc = false;
5947
- return c.run(input, e1, ctx);
5948
- }
5949
- };
5749
+ function covKindCode(kind) {
5750
+ switch (kind) {
5751
+ case "rule":
5752
+ return 0;
5753
+ case "choice-arm":
5754
+ return 1;
5755
+ case "dispatch-arm":
5756
+ return 2;
5757
+ case "label":
5758
+ return 3;
5759
+ }
5950
5760
  }
5951
- function sequence4(a, b, c, d) {
5952
- const abc = sequence3(a, b, c);
5953
- return {
5954
- match: (input, pos) => {
5955
- const end = abc.match(input, pos);
5956
- return end < 0 ? -1 : d.match(input, end);
5957
- },
5958
- run: (input, pos, ctx) => {
5959
- const end = abc.run(input, pos, ctx);
5960
- if (end < 0) return -1;
5961
- ctx._fc = false;
5962
- return d.run(input, end, ctx);
5963
- }
5964
- };
5761
+ function covKindName(code) {
5762
+ switch (code) {
5763
+ case 0:
5764
+ return "rule";
5765
+ case 1:
5766
+ return "choice-arm";
5767
+ case 2:
5768
+ return "dispatch-arm";
5769
+ case 3:
5770
+ return "label";
5771
+ }
5965
5772
  }
5966
- function sequence5(a, b, c, d, e) {
5967
- const abcd = sequence4(a, b, c, d);
5968
- return {
5969
- match: (input, pos) => {
5970
- const end = abcd.match(input, pos);
5971
- return end < 0 ? -1 : e.match(input, end);
5972
- },
5973
- run: (input, pos, ctx) => {
5974
- const end = abcd.run(input, pos, ctx);
5975
- if (end < 0) return -1;
5976
- ctx._fc = false;
5977
- return e.run(input, end, ctx);
5978
- }
5979
- };
5773
+ function covDefinitions(prog) {
5774
+ return (prog.cov ?? []).map(([id, kind]) => ({ id, kind: covKindName(kind) }));
5980
5775
  }
5981
- function appendExpected(acc, values) {
5982
- if (values === void 0 || values.length === 0) return acc;
5983
- if (acc === void 0) return values.slice();
5984
- acc.push(...values);
5985
- return acc;
5776
+ function scanRefs(s) {
5777
+ const refs = [];
5778
+ for (const r of [s.sentinel, s.recoverAt, s.stopAt, s.prefix, s.body, s.first, s.lazy]) if (r !== void 0) refs.push(r);
5779
+ refs.push(...s.skip);
5780
+ return refs;
5986
5781
  }
5987
- function choiceNode(arms, classes, armExpected, choiceExpected) {
5988
- let matchTail = () => -1;
5989
- for (let i = arms.length - 1; i >= 0; i--) {
5990
- const arm = arms[i], next = matchTail;
5991
- matchTail = (input, pos) => {
5992
- const end = arm.match(input, pos);
5993
- return end < 0 ? next(input, pos) : end;
5994
- };
5995
- }
5996
- let tail = (_input, pos, ctx, _lead, acc) => {
5997
- ctx._fe = pos;
5998
- ctx._fx = acc ?? choiceExpected;
5999
- return -1;
5782
+ function assemblyExtraIps(prog) {
5783
+ const ips = [];
5784
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) ips.push(r[0]);
5785
+ for (const set of prog.scanSkip ?? []) for (const r of set) ips.push(r[0]);
5786
+ return ips;
5787
+ }
5788
+ function expandCompact(p) {
5789
+ if ("code" in p) return p;
5790
+ return ownTableProgram({
5791
+ code: p.c,
5792
+ k: p.k,
5793
+ cc: p.x,
5794
+ fx: p.e,
5795
+ disp: p.d,
5796
+ rules: p.r,
5797
+ fns: p.f,
5798
+ lines: p.l ?? 0,
5799
+ dsp: p.p ?? [],
5800
+ ...p.lx === void 0 ? {} : { lex: p.lx },
5801
+ ...p.lp === void 0 ? {} : { lexPrograms: p.lp },
5802
+ ...p.lb === void 0 ? {} : { labels: p.lb },
5803
+ ...p.rc === void 0 ? {} : { classified: p.rc },
5804
+ ...p.h === void 0 ? {} : { hostMode: p.h },
5805
+ ...p.tv === void 0 ? {} : { triviaSpecs: p.tv },
5806
+ ...p.sc === void 0 ? {} : { scans: p.sc },
5807
+ ...p.ss === void 0 ? {} : { scanSkip: p.ss },
5808
+ ...p.so === void 0 ? {} : { scanSkipOf: p.so },
5809
+ ...p.rv === void 0 ? {} : { rec: p.rv },
5810
+ ...p.cv === void 0 ? {} : { cov: p.cv },
5811
+ ...p.a === void 0 ? {} : { asm: p.a }
5812
+ });
5813
+ }
5814
+ function validateDispatchSpec(spec, arity, fallbackRouted) {
5815
+ const invalid = (arms) => {
5816
+ for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
5817
+ return false;
6000
5818
  };
6001
- for (let i = arms.length - 1; i >= 0; i--) {
6002
- const arm = arms[i], cls = classes[i], staticExpected = armExpected[i], next = tail;
6003
- tail = (input, pos, ctx, lead2, acc, best) => {
6004
- if (cls !== void 0 && !classHas(cls, lead2)) {
6005
- return next(
6006
- input,
6007
- pos,
6008
- ctx,
6009
- lead2,
6010
- best === pos ? appendExpected(acc, staticExpected) : acc,
6011
- best
6012
- );
6013
- }
6014
- ctx._fc = false;
6015
- const end = arm.run(input, pos, ctx);
6016
- if (end >= 0) return end;
6017
- const at = ctx._fe ?? pos;
6018
- if (at > best) {
6019
- best = at;
6020
- acc = void 0;
6021
- }
6022
- const nextAcc = at === best ? appendExpected(acc, ctx._fx) : acc;
6023
- if (committed(ctx)) {
6024
- if (nextAcc !== void 0) ctx._fx = nextAcc;
6025
- return -1;
6026
- }
6027
- return next(input, pos, ctx, lead2, nextAcc, best);
6028
- };
5819
+ if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
5820
+ throw new TypeError("table: malformed dispatch");
6029
5821
  }
6030
- return {
6031
- match: matchTail,
6032
- run: (input, pos, ctx) => tail(
6033
- input,
6034
- pos,
6035
- ctx,
6036
- pos < input.length ? input.codePointAt(pos) : -1,
6037
- void 0,
6038
- pos
6039
- )
6040
- };
6041
5822
  }
6042
- function notNode(body, failure) {
5823
+ var TABLE_RUNTIME = /* @__PURE__ */ Symbol("parseman.tableRuntime");
5824
+ function ownTableProgram(prog, resolved, caches = {}) {
5825
+ const runtime = prog[TABLE_RUNTIME];
6043
5826
  return {
6044
- match: (input, pos) => body.match(input, pos) < 0 ? pos : -1,
6045
- run: (input, pos, ctx) => {
6046
- if (body.match(input, pos) < 0) return pos;
6047
- ctx._fe = pos;
6048
- ctx._fx = failure;
6049
- return -1;
5827
+ ...prog,
5828
+ [TABLE_RUNTIME]: {
5829
+ resolved,
5830
+ choiceRollbackMasks: caches.choiceRollbackMasks ?? runtime?.choiceRollbackMasks,
5831
+ nonCommittingChoiceSites: caches.nonCommittingChoiceSites ?? runtime?.nonCommittingChoiceSites,
5832
+ failureRollbackCleanSites: caches.failureRollbackCleanSites ?? runtime?.failureRollbackCleanSites
6050
5833
  }
6051
5834
  };
6052
5835
  }
6053
- function optionalNode(body) {
6054
- return {
6055
- match: (input, pos) => {
6056
- const end = body.match(input, pos);
6057
- return end < 0 ? pos : end;
6058
- },
6059
- run: (input, pos, ctx) => {
6060
- ctx._fc = false;
6061
- const end = body.run(input, pos, ctx);
6062
- return end < 0 && !committed(ctx) ? pos : end;
6063
- }
6064
- };
5836
+ function choiceRollbackMask(prog, ip) {
5837
+ return prog[TABLE_RUNTIME]?.choiceRollbackMasks?.get(ip);
6065
5838
  }
6066
- function fixedTreeRunner(words, matchers, classes, expected) {
6067
- const stack = [];
6068
- const pop = () => {
6069
- const value = stack.pop();
6070
- if (value === void 0) throw new TypeError("table lexical tree has an incomplete fixed body");
6071
- return value;
6072
- };
6073
- for (let at = 0; at < words.length; ) {
6074
- const op = words[at++];
6075
- if (op === LEX_NODE_TERMINAL) {
6076
- const matcher = matchers[words[at++]], failure = expected[words[at++]];
6077
- const tracks = words[at++];
6078
- if (matcher === void 0 || failure === void 0 || tracks !== 0 && tracks !== 1) {
6079
- throw new TypeError("table lexical tree terminal references an invalid pool entry");
6080
- }
6081
- stack.push(terminalNode(matcher, failure, tracks === 1));
6082
- continue;
6083
- }
6084
- if (op >= LEX_NODE_SEQUENCE2 && op <= LEX_NODE_SEQUENCE5) {
6085
- const count = op - LEX_NODE_SEQUENCE2 + 2;
6086
- const nodes2 = Array.from({ length: count }, pop).reverse();
6087
- if (count === 2) stack.push(sequence2(nodes2[0], nodes2[1]));
6088
- else if (count === 3) stack.push(sequence3(nodes2[0], nodes2[1], nodes2[2]));
6089
- else if (count === 4) stack.push(sequence4(nodes2[0], nodes2[1], nodes2[2], nodes2[3]));
6090
- else stack.push(sequence5(nodes2[0], nodes2[1], nodes2[2], nodes2[3], nodes2[4]));
6091
- continue;
6092
- }
6093
- if (op >= LEX_NODE_CHOICE2 && op <= LEX_NODE_CHOICE8) {
6094
- const count = op === LEX_NODE_CHOICE2 ? 2 : op === LEX_NODE_CHOICE4 ? 4 : 8;
6095
- const choiceFx = expected[words[at++]];
6096
- if (choiceFx === void 0) throw new TypeError("table lexical tree choice has an invalid expected set");
6097
- const armClasses = [];
6098
- const armFx = [];
6099
- for (let i = 0; i < count; i++) {
6100
- const cls = words[at++], fx = expected[words[at++]];
6101
- if (cls < -1 || cls >= 0 && classes[cls] === void 0 || fx === void 0) {
6102
- throw new TypeError("table lexical tree choice references an invalid pool entry");
6103
- }
6104
- armClasses.push(cls < 0 ? void 0 : classes[cls]);
6105
- armFx.push(fx);
6106
- }
6107
- const arms = Array.from({ length: count }, pop).reverse();
6108
- stack.push(choiceNode(arms, armClasses, armFx, choiceFx));
6109
- continue;
6110
- }
6111
- if (op === LEX_NODE_NOT) {
6112
- const failure = expected[words[at++]];
6113
- if (failure === void 0) throw new TypeError("table lexical tree assertion has an invalid expected set");
6114
- stack.push(notNode(pop(), failure));
6115
- continue;
6116
- }
6117
- if (op === LEX_NODE_OPTIONAL) {
6118
- stack.push(optionalNode(pop()));
6119
- continue;
5839
+ function choiceCannotCommit(prog, ip) {
5840
+ return prog[TABLE_RUNTIME]?.nonCommittingChoiceSites?.has(ip) === true;
5841
+ }
5842
+ function failureRollbackClean(prog, ip) {
5843
+ return prog[TABLE_RUNTIME]?.failureRollbackCleanSites?.has(ip) === true;
5844
+ }
5845
+ var WIDE_CLASS_PREFIX_LO = 65535;
5846
+ var WIDE_CLASS_PREFIX_HI = 0;
5847
+ function validateClassRange(lo, hi) {
5848
+ for (const cp of [lo, hi]) {
5849
+ if (!Number.isInteger(cp) || cp < 0 || cp > 1114111) {
5850
+ throw new TypeError(`table char-class endpoint is outside Unicode: ${cp}`);
6120
5851
  }
6121
- throw new TypeError("table lexical tree has an unknown fixed body");
6122
5852
  }
6123
- if (stack.length !== 1) throw new TypeError("table lexical tree does not resolve to one root body");
6124
- return stack[0].run;
5853
+ if (lo > hi) {
5854
+ throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
5855
+ }
6125
5856
  }
6126
- function buildLexProgramRunner(spec, matchers, classes, expected) {
6127
- if (spec[0] !== 0 && spec[0] !== 1 && spec[0] !== 2 && spec[0] !== 3 || spec[0] === 0 && spec.length !== 13 || spec[0] === 1 && spec.length !== 12 || spec[0] === 2 && spec.length < 6 || spec[0] === 3 && spec.length !== 3) {
6128
- throw new TypeError("table lexical program has an invalid fixed body width");
6129
- }
6130
- if (spec[1] !== lexProgramDigest(spec.slice(2))) {
6131
- throw new TypeError("table lexical program semantic digest is inconsistent");
6132
- }
6133
- if (spec[0] === 3) {
6134
- if (!Number.isInteger(spec[2]) || spec[2] < 0) {
6135
- throw new TypeError("table balanced lexical program has an invalid scan reference");
6136
- }
6137
- const run = (_input, _pos, ctx, scan) => {
6138
- if (scan === void 0) throw new TypeError("table balanced lexical program lost its scan body");
6139
- const result = scan.parse(_input, _pos, ctx);
6140
- if (!result.ok) {
6141
- ctx._fe = result.span.start;
6142
- ctx._fx = result.expected ?? [];
6143
- return -1;
6144
- }
6145
- return result.span.end;
6146
- };
6147
- return Object.assign(run, { scan: spec[2] });
6148
- }
6149
- if (spec[0] === 2) return fixedTreeRunner(spec.slice(2), matchers, classes, expected);
6150
- if (spec[0] === 1) {
6151
- const g00 = matchers[spec[2]], g01 = matchers[spec[3]];
6152
- const g02 = matchers[spec[4]], g03 = matchers[spec[5]];
6153
- const guard0Fx = expected[spec[6]];
6154
- const g1 = matchers[spec[7]], guard1Fx = expected[spec[8]];
6155
- const terminal = matchers[spec[9]], terminalFx = expected[spec[10]];
6156
- const tracks2 = spec[11];
6157
- if (g00 === void 0 || g01 === void 0 || g02 === void 0 || g03 === void 0 || guard0Fx === void 0 || g1 === void 0 || guard1Fx === void 0 || terminal === void 0 || terminalFx === void 0 || tracks2 !== 0 && tracks2 !== 1) {
6158
- throw new TypeError("table lexical program references an invalid pool entry");
6159
- }
6160
- return (input, pos, ctx) => {
6161
- ctx._fc = false;
6162
- if (g00(input, pos) >= 0 || g01(input, pos) >= 0 || g02(input, pos) >= 0 || g03(input, pos) >= 0) {
6163
- ctx._fe = pos;
6164
- ctx._fx = guard0Fx;
6165
- return -1;
6166
- }
6167
- ctx._fc = false;
6168
- if (g1(input, pos) >= 0) {
6169
- ctx._fe = pos;
6170
- ctx._fx = guard1Fx;
6171
- return -1;
6172
- }
6173
- ctx._fc = false;
6174
- const result = terminal(input, pos);
6175
- if (result < 0) {
6176
- ctx._fe = pos;
6177
- ctx._fx = terminalFx;
6178
- if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
6179
- return -1;
6180
- }
6181
- const end = result / 2;
6182
- if (tracks2 === 1) trackLexLines(ctx, input, end);
6183
- return end;
6184
- };
6185
- }
6186
- const cls = classes[spec[2]];
6187
- const choiceFx = expected[spec[3]];
6188
- const m0 = matchers[spec[4]], x0 = expected[spec[5]];
6189
- const m1 = matchers[spec[6]], x1 = expected[spec[7]];
6190
- const m2 = matchers[spec[8]], x2 = expected[spec[9]];
6191
- const m3 = matchers[spec[10]], x3 = expected[spec[11]];
6192
- const tracks = spec[12];
6193
- if (cls === void 0 || choiceFx === void 0 || m0 === void 0 || x0 === void 0 || m1 === void 0 || x1 === void 0 || m2 === void 0 || x2 === void 0 || m3 === void 0 || x3 === void 0 || tracks !== 0 && tracks !== 1) {
6194
- throw new TypeError("table lexical program references an invalid pool entry");
6195
- }
6196
- return (input, pos, ctx) => {
6197
- const lead2 = pos < input.length ? input.codePointAt(pos) : -1;
6198
- if (!classHas(cls, lead2)) {
6199
- ctx._fe = pos;
6200
- ctx._fx = choiceFx.slice();
6201
- return -1;
6202
- }
6203
- let acc;
6204
- const failed = (fx) => {
6205
- ctx._fe = pos;
6206
- ctx._fx = fx;
6207
- if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
6208
- if (acc === void 0) acc = fx.slice();
6209
- else acc.push(...fx);
6210
- };
6211
- ctx._fc = false;
6212
- const success = (result2) => {
6213
- const end = result2 / 2;
6214
- if (tracks === 1) trackLexLines(ctx, input, end);
6215
- return end;
6216
- };
6217
- let result = m0(input, pos);
6218
- if (result >= 0) return success(result);
6219
- failed(x0);
6220
- ctx._fc = false;
6221
- result = m1(input, pos);
6222
- if (result >= 0) return success(result);
6223
- failed(x1);
6224
- ctx._fc = false;
6225
- result = m2(input, pos);
6226
- if (result >= 0) return success(result);
6227
- failed(x2);
6228
- ctx._fc = false;
6229
- result = m3(input, pos);
6230
- if (result >= 0) return success(result);
6231
- failed(x3);
6232
- ctx._fe = pos;
6233
- ctx._fx = acc ?? choiceFx;
6234
- return -1;
6235
- };
6236
- }
6237
-
6238
- // src/table/lex-body.ts
6239
- function buildLexBodyRecognizer(spec, constants) {
6240
- const raw = constants[spec[0]];
6241
- if (!(raw instanceof RegExp)) {
6242
- throw new TypeError("table lexical body does not reference a RegExp constant");
6243
- }
6244
- if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
6245
- throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
6246
- }
6247
- const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
6248
- const suffix = spec[1];
6249
- return (input, pos) => {
6250
- re.lastIndex = pos;
6251
- if (!re.test(input)) return -1;
6252
- const end = re.lastIndex;
6253
- if (suffix < 0) return end * 2;
6254
- return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
6255
- };
6256
- }
6257
-
6258
- // src/table/program.ts
6259
- function closureArtifact(prog) {
6260
- return { ...prog, asm: [] };
6261
- }
6262
- function covKindCode(kind) {
6263
- switch (kind) {
6264
- case "rule":
6265
- return 0;
6266
- case "choice-arm":
6267
- return 1;
6268
- case "dispatch-arm":
6269
- return 2;
6270
- case "label":
6271
- return 3;
6272
- }
6273
- }
6274
- function covKindName(code) {
6275
- switch (code) {
6276
- case 0:
6277
- return "rule";
6278
- case 1:
6279
- return "choice-arm";
6280
- case 2:
6281
- return "dispatch-arm";
6282
- case 3:
6283
- return "label";
6284
- }
6285
- }
6286
- function covDefinitions(prog) {
6287
- return (prog.cov ?? []).map(([id, kind]) => ({ id, kind: covKindName(kind) }));
6288
- }
6289
- function expandCompact(p) {
6290
- if ("code" in p) return p;
6291
- return ownTableProgram({
6292
- code: p.c,
6293
- k: p.k,
6294
- cc: p.x,
6295
- fx: p.e,
6296
- disp: p.d,
6297
- rules: p.r,
6298
- fns: p.f,
6299
- lines: p.l ?? 0,
6300
- dsp: p.p ?? [],
6301
- ...p.lx === void 0 ? {} : { lex: p.lx },
6302
- ...p.lp === void 0 ? {} : { lexPrograms: p.lp },
6303
- ...p.lb === void 0 ? {} : { labels: p.lb },
6304
- ...p.rc === void 0 ? {} : { classified: p.rc },
6305
- ...p.h === void 0 ? {} : { hostMode: p.h },
6306
- ...p.tv === void 0 ? {} : { triviaSpecs: p.tv },
6307
- ...p.sc === void 0 ? {} : { scans: p.sc },
6308
- ...p.ss === void 0 ? {} : { scanSkip: p.ss },
6309
- ...p.so === void 0 ? {} : { scanSkipOf: p.so },
6310
- ...p.rv === void 0 ? {} : { rec: p.rv },
6311
- ...p.cv === void 0 ? {} : { cov: p.cv },
6312
- ...p.a === void 0 ? {} : { asm: p.a }
6313
- });
6314
- }
6315
- function validateDispatchSpec(spec, arity, fallbackRouted) {
6316
- const invalid = (arms) => {
6317
- for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
6318
- return false;
6319
- };
6320
- if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
6321
- throw new TypeError("table: malformed dispatch");
6322
- }
6323
- }
6324
- var TABLE_RUNTIME = /* @__PURE__ */ Symbol("parseman.tableRuntime");
6325
- function ownTableProgram(prog, resolved, caches = {}) {
6326
- const runtime = prog[TABLE_RUNTIME];
6327
- return {
6328
- ...prog,
6329
- [TABLE_RUNTIME]: {
6330
- resolved,
6331
- choiceRollbackMasks: caches.choiceRollbackMasks ?? runtime?.choiceRollbackMasks,
6332
- nonCommittingChoiceSites: caches.nonCommittingChoiceSites ?? runtime?.nonCommittingChoiceSites,
6333
- failureRollbackCleanSites: caches.failureRollbackCleanSites ?? runtime?.failureRollbackCleanSites
6334
- }
6335
- };
6336
- }
6337
- function choiceRollbackMask(prog, ip) {
6338
- return prog[TABLE_RUNTIME]?.choiceRollbackMasks?.get(ip);
6339
- }
6340
- function choiceCannotCommit(prog, ip) {
6341
- return prog[TABLE_RUNTIME]?.nonCommittingChoiceSites?.has(ip) === true;
6342
- }
6343
- function failureRollbackClean(prog, ip) {
6344
- return prog[TABLE_RUNTIME]?.failureRollbackCleanSites?.has(ip) === true;
6345
- }
6346
- var WIDE_CLASS_PREFIX_LO = 65535;
6347
- var WIDE_CLASS_PREFIX_HI = 0;
6348
- function validateClassRange(lo, hi) {
6349
- for (const cp of [lo, hi]) {
6350
- if (!Number.isInteger(cp) || cp < 0 || cp > 1114111) {
6351
- throw new TypeError(`table char-class endpoint is outside Unicode: ${cp}`);
6352
- }
6353
- }
6354
- if (lo > hi) {
6355
- throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
6356
- }
6357
- }
6358
- function encodeClassSpec(ranges) {
6359
- let wide = false;
6360
- for (const range2 of ranges) {
6361
- validateClassRange(range2.lo, range2.hi);
6362
- if (range2.hi > 65535) wide = true;
5857
+ function encodeClassSpec(ranges) {
5858
+ let wide = false;
5859
+ for (const range2 of ranges) {
5860
+ validateClassRange(range2.lo, range2.hi);
5861
+ if (range2.hi > 65535) wide = true;
6363
5862
  }
6364
5863
  if (!wide) {
6365
5864
  let spec2 = "";
@@ -6416,6 +5915,11 @@ function classHas2(cls, code) {
6416
5915
  for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
6417
5916
  return false;
6418
5917
  }
5918
+ function dispatchIsExclusive(arms, cc) {
5919
+ const resolved = [];
5920
+ for (const ci of arms) if (ci >= 0) resolved[ci] = resolveClass(cc[ci]);
5921
+ return resolveDispatch(arms, resolved).exclusive;
5922
+ }
6419
5923
  function resolveDispatch(arms, cc) {
6420
5924
  const ascii = new Uint8Array(128);
6421
5925
  const hi = [];
@@ -6489,6 +5993,616 @@ function resolveTable(prog) {
6489
5993
  return built;
6490
5994
  }
6491
5995
 
5996
+ // src/analysis/gating.ts
5997
+ function childrenOf2(d) {
5998
+ switch (d.tag) {
5999
+ case "sequence":
6000
+ case "choice":
6001
+ return d.parsers;
6002
+ case "dispatch":
6003
+ return [
6004
+ d.selector,
6005
+ ...d.cases.map((c) => c.parser),
6006
+ ...d.matchers ? d.matchers.map((c) => c.parser) : [],
6007
+ ...d.otherwise === void 0 ? [] : [d.otherwise]
6008
+ ];
6009
+ case "sepBy":
6010
+ return [d.parser, d.separator];
6011
+ case "recover":
6012
+ return [d.parser, d.sentinel];
6013
+ case "scanTo":
6014
+ return [
6015
+ d.sentinel,
6016
+ ...d.skip,
6017
+ ...d.recoverAt === void 0 ? [] : [d.recoverAt],
6018
+ ...d.stopAt === void 0 ? [] : [d.stopAt]
6019
+ ];
6020
+ case "grammar":
6021
+ return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
6022
+ case "routed":
6023
+ return d.fallback ? [d.fallback] : [];
6024
+ case "lines":
6025
+ return [d.prefix, d.body, ...d.first === void 0 ? [] : [d.first], ...d.lazy === void 0 ? [] : [d.lazy]];
6026
+ case "columns":
6027
+ return [];
6028
+ // A `lazy` is a REFERENCE, not a subtree. Descending through it would make
6029
+ // every rule's walk cover the whole reachable grammar — site paths become
6030
+ // nonsense, and a structural hash includes half the grammar. Treated as a
6031
+ // leaf, keyed by the rule name it refers to, which is also the right
6032
+ // semantics: two productions referencing `g.Ident` really do fill that slot
6033
+ // the same way.
6034
+ case "lazy":
6035
+ case "literal":
6036
+ case "regex":
6037
+ case "keywords":
6038
+ case "guard":
6039
+ case "adjacency":
6040
+ case "unknown":
6041
+ return [];
6042
+ default: {
6043
+ const rec = d;
6044
+ return rec.parser ? [rec.parser] : [];
6045
+ }
6046
+ }
6047
+ }
6048
+ var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
6049
+ function firstSetToString(fs3) {
6050
+ if (fs3.kind === "any") return "ANY";
6051
+ if (fs3.kind === "empty") return "(empty)";
6052
+ return fs3.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
6053
+ }
6054
+
6055
+ // src/analysis/commitment.ts
6056
+ function mayFail(p, seen = /* @__PURE__ */ new Set()) {
6057
+ if (seen.has(p)) return true;
6058
+ seen.add(p);
6059
+ const d = p._def;
6060
+ switch (d.tag) {
6061
+ case "literal":
6062
+ return d.value.length > 0;
6063
+ case "trivia":
6064
+ return false;
6065
+ // `optional`/`many`/a min-0 `sepBy` swallow an ORDINARY failure — zero
6066
+ // repetitions is a match. They do NOT swallow a COMMITTED one: a dispatch
6067
+ // that has selected an arm and then failed inside it breaks out past every
6068
+ // enclosing repeat, straight to the fallible boundary. So these are total
6069
+ // exactly when their body cannot commit. (Measured: treating them as
6070
+ // unconditionally total left an extra zero-width leaf in
6071
+ // `BareInterpolatedSelector` on benchmark.less — a rollback the legacy
6072
+ // build fired and this one had deleted.)
6073
+ case "optional":
6074
+ case "many":
6075
+ return mayCommitFailure(d.parser);
6076
+ case "sepBy":
6077
+ return d.min >= 1 ? mayFail(d.parser, seen) : mayCommitFailure(d.parser) || mayCommitFailure(d.separator);
6078
+ case "oneOrMore":
6079
+ return mayFail(d.parser, seen);
6080
+ case "sequence":
6081
+ return d.parsers.some((x) => mayFail(x, seen));
6082
+ // Delegating wrappers: as fallible as what they wrap.
6083
+ case "node":
6084
+ case "transform":
6085
+ case "label":
6086
+ case "field":
6087
+ case "expect":
6088
+ case "withCtx":
6089
+ case "grammar":
6090
+ case "token":
6091
+ case "leaf":
6092
+ return mayFail(d.parser, seen);
6093
+ case "lazy": {
6094
+ try {
6095
+ return mayFail(d.thunk(), seen);
6096
+ } catch {
6097
+ return true;
6098
+ }
6099
+ }
6100
+ // An adjacency assertion is a TEST — failing is its whole job.
6101
+ case "adjacency":
6102
+ return true;
6103
+ default:
6104
+ return true;
6105
+ }
6106
+ }
6107
+ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set(), triviaActive = true) {
6108
+ if (seen.has(p)) return false;
6109
+ seen.add(p);
6110
+ const d = p._def;
6111
+ switch (d.tag) {
6112
+ // Atomic / non-capturing-then-failing: a failure happens before any push.
6113
+ case "literal":
6114
+ case "regex":
6115
+ case "keywords":
6116
+ case "guard":
6117
+ // Adjacency probes with `_cap = 0` / no `commit()`: it cannot push anything.
6118
+ case "adjacency":
6119
+ case "not":
6120
+ // peek(): emitted under a non-capturing probe ctx and zero-width on both
6121
+ // outcomes, so it can never leave a partial capture behind.
6122
+ case "peek":
6123
+ case "trivia":
6124
+ case "token":
6125
+ case "leaf":
6126
+ case "scanTo":
6127
+ case "unknown":
6128
+ case "columns":
6129
+ return false;
6130
+ // A run's prefixes are probed detached; only its body can capture.
6131
+ case "lines":
6132
+ return mayLeavePartialCapture(d.body, seen, triviaActive);
6133
+ // node() captures into its own private buffers and rolls them back on
6134
+ // failure (emitNode restores _ctx.* and never pushes on the !ok path).
6135
+ case "node":
6136
+ return false;
6137
+ // choice/firstMatch already roll back each failed arm; on overall failure
6138
+ // nothing committed remains.
6139
+ case "choice":
6140
+ return false;
6141
+ case "dispatch":
6142
+ return capturesLeaf(d.selector, new Set(seen)) || d.cases.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) || (d.matchers ? d.matchers.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) : false) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, new Set(seen), triviaActive) || capturesLeaf(d.otherwise, new Set(seen)) : false);
6143
+ case "attempt":
6144
+ return false;
6145
+ // optional never fails; many/oneOrMore only "fail" with zero captured items.
6146
+ case "optional":
6147
+ case "many":
6148
+ case "oneOrMore":
6149
+ return false;
6150
+ // sepBy emits its own per-iteration rollback in emitSepBy.
6151
+ case "sepBy":
6152
+ return false;
6153
+ // A sequence is the real case: an earlier capturing term followed by a term
6154
+ // that can fail leaves the earlier captures buffered.
6155
+ //
6156
+ // BOTH halves of that sentence are load-bearing, and the second half is what
6157
+ // this walk exists to exploit. "Some earlier term captures" alone was the old
6158
+ // test, and it is true of very nearly every sequence in a real grammar — a
6159
+ // sequence almost always opens with a literal or a node. The rollback is only
6160
+ // reachable if something AFTER the capture can still fail; once the remainder
6161
+ // is total (`optional`/`many`/trivia/a min-0 `sepBy`), the sequence has no
6162
+ // failure path left to roll back from, and the save+restore is dead code.
6163
+ //
6164
+ // `captured` tracks whether anything is in the buffers yet as the scan moves
6165
+ // left to right. Two things put it there:
6166
+ // - a term that captures (`capturesLeaf`/`hasNodeDef`), and
6167
+ // - the inter-term TRIVIA scan, which under `capturing` pushes into
6168
+ // `_cstRawChildren`/`_cstTriviaLog` BEFORE the term at each boundary
6169
+ // i >= 1. `emitSeqValues` only rewinds that push on the term's SUCCESS
6170
+ // path (the "matched empty, so leave the whitespace out of the span"
6171
+ // branch); a term that FAILS breaks straight out with the trivia still
6172
+ // buffered, so from boundary 1 onward the buffers are dirty regardless of
6173
+ // what the earlier terms did.
6174
+ // Each term is also asked about itself: a nested sequence can leave its own
6175
+ // partial capture behind, and its terms are emitted inline into this same
6176
+ // block with no boundary of their own to roll them back.
6177
+ case "sequence": {
6178
+ const parts = d.parsers;
6179
+ let captured = false;
6180
+ for (let i = 0; i < parts.length; i++) {
6181
+ const part = parts[i];
6182
+ if (i > 0 && triviaActive) captured = true;
6183
+ if (captured && mayFail(part)) return true;
6184
+ if (mayLeavePartialCapture(part, seen, triviaActive)) return true;
6185
+ if (capturesLeaf(part) || hasNodeDef(part)) captured = true;
6186
+ }
6187
+ return false;
6188
+ }
6189
+ // Delegating wrappers: defer to the wrapped parser.
6190
+ case "transform":
6191
+ case "label":
6192
+ case "field":
6193
+ case "expect":
6194
+ case "withCtx":
6195
+ case "grammar":
6196
+ return mayLeavePartialCapture(d.parser, seen, triviaActive);
6197
+ case "recover":
6198
+ return mayLeavePartialCapture(d.parser, seen, triviaActive);
6199
+ case "lazy": {
6200
+ try {
6201
+ return mayLeavePartialCapture(d.thunk(), seen, triviaActive);
6202
+ } catch {
6203
+ return true;
6204
+ }
6205
+ }
6206
+ // Unknown shapes: be safe and keep the rollback.
6207
+ default:
6208
+ return true;
6209
+ }
6210
+ }
6211
+ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
6212
+ if (seen.has(p)) return false;
6213
+ seen.add(p);
6214
+ const d = p._def;
6215
+ switch (d.tag) {
6216
+ case "literal":
6217
+ case "regex":
6218
+ case "keywords":
6219
+ case "node":
6220
+ case "token":
6221
+ case "routed":
6222
+ case "leaf":
6223
+ return true;
6224
+ case "not":
6225
+ case "peek":
6226
+ case "guard":
6227
+ case "adjacency":
6228
+ case "trivia":
6229
+ case "unknown":
6230
+ return false;
6231
+ case "sequence":
6232
+ case "choice":
6233
+ return d.parsers.some((x) => capturesLeaf(x, seen));
6234
+ case "dispatch":
6235
+ return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => capturesLeaf(entry.parser, seen)) : false) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
6236
+ case "sepBy":
6237
+ return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
6238
+ case "many":
6239
+ case "oneOrMore":
6240
+ case "optional":
6241
+ case "attempt":
6242
+ case "transform":
6243
+ case "label":
6244
+ case "field":
6245
+ case "expect":
6246
+ case "withCtx":
6247
+ case "grammar":
6248
+ case "recover":
6249
+ return capturesLeaf(d.parser, seen);
6250
+ case "scanTo":
6251
+ return true;
6252
+ case "lines":
6253
+ return capturesLeaf(d.body, seen);
6254
+ case "columns":
6255
+ return true;
6256
+ case "lazy": {
6257
+ try {
6258
+ return capturesLeaf(d.thunk(), seen);
6259
+ } catch {
6260
+ return true;
6261
+ }
6262
+ }
6263
+ default:
6264
+ return true;
6265
+ }
6266
+ }
6267
+ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
6268
+ if (seen.has(p)) return false;
6269
+ seen.add(p);
6270
+ const d = p._def;
6271
+ switch (d.tag) {
6272
+ case "node":
6273
+ return true;
6274
+ case "lazy": {
6275
+ try {
6276
+ return hasNodeDef(d.thunk(), seen);
6277
+ } catch {
6278
+ return false;
6279
+ }
6280
+ }
6281
+ case "grammar":
6282
+ case "trivia":
6283
+ case "token":
6284
+ case "leaf":
6285
+ case "label":
6286
+ case "field":
6287
+ case "optional":
6288
+ case "many":
6289
+ case "oneOrMore":
6290
+ case "not":
6291
+ case "peek":
6292
+ case "transform":
6293
+ return hasNodeDef(d.parser, seen);
6294
+ case "sequence":
6295
+ case "choice":
6296
+ return d.parsers.some((x) => hasNodeDef(x, seen));
6297
+ case "dispatch":
6298
+ return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => hasNodeDef(entry.parser, seen)) : false) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
6299
+ case "sepBy":
6300
+ return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
6301
+ case "scanTo":
6302
+ return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen)) || d.recoverAt !== void 0 && hasNodeDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasNodeDef(d.stopAt, seen);
6303
+ case "lines":
6304
+ return hasNodeDef(d.body, seen);
6305
+ case "recover":
6306
+ return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
6307
+ case "expect":
6308
+ return hasNodeDef(d.parser, seen);
6309
+ case "withCtx":
6310
+ return hasNodeDef(d.parser, seen);
6311
+ default:
6312
+ return false;
6313
+ }
6314
+ }
6315
+ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
6316
+ if (seen.has(p)) return false;
6317
+ seen.add(p);
6318
+ const d = p._def;
6319
+ switch (d.tag) {
6320
+ case "node":
6321
+ return d.build !== void 0 || hasDirectBuildDef(d.parser, seen);
6322
+ case "lazy": {
6323
+ try {
6324
+ return hasDirectBuildDef(d.thunk(), seen);
6325
+ } catch {
6326
+ return false;
6327
+ }
6328
+ }
6329
+ case "grammar":
6330
+ case "trivia":
6331
+ case "token":
6332
+ case "leaf":
6333
+ case "label":
6334
+ case "field":
6335
+ case "optional":
6336
+ case "many":
6337
+ case "oneOrMore":
6338
+ case "attempt":
6339
+ case "not":
6340
+ case "peek":
6341
+ case "withCtx":
6342
+ case "expect":
6343
+ case "transform":
6344
+ return hasDirectBuildDef(d.parser, seen);
6345
+ case "sequence":
6346
+ case "choice":
6347
+ return d.parsers.some((x) => hasDirectBuildDef(x, seen));
6348
+ case "dispatch":
6349
+ return hasDirectBuildDef(d.selector, seen) || d.cases.some((x) => hasDirectBuildDef(x.parser, seen)) || (d.matchers ? d.matchers.some((x) => hasDirectBuildDef(x.parser, seen)) : false) || (d.otherwise ? hasDirectBuildDef(d.otherwise, seen) : false);
6350
+ case "sepBy":
6351
+ return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
6352
+ case "scanTo":
6353
+ return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen)) || d.recoverAt !== void 0 && hasDirectBuildDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasDirectBuildDef(d.stopAt, seen);
6354
+ case "lines":
6355
+ return hasDirectBuildDef(d.body, seen);
6356
+ case "recover":
6357
+ return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
6358
+ case "routed":
6359
+ return d.fallback ? hasDirectBuildDef(d.fallback, seen) : false;
6360
+ default:
6361
+ return false;
6362
+ }
6363
+ }
6364
+ function mayCommitFailure(p, resolve3, memo = /* @__PURE__ */ new Map()) {
6365
+ return reachesMarked(p, (node2) => commitFailureSuccessors(node2, resolve3), memo);
6366
+ }
6367
+ var NO_EDGES = [];
6368
+ function commitFailureSuccessors(p, resolve3) {
6369
+ const d = p._def;
6370
+ switch (d.tag) {
6371
+ case "dispatch":
6372
+ case "unknown":
6373
+ return true;
6374
+ case "choice":
6375
+ case "sequence":
6376
+ return d.parsers;
6377
+ // A contained transaction reports every failure uncommitted.
6378
+ case "attempt":
6379
+ return d.contain === true ? NO_EDGES : [d.parser];
6380
+ case "many":
6381
+ case "oneOrMore":
6382
+ case "optional":
6383
+ case "transform":
6384
+ case "label":
6385
+ case "field":
6386
+ case "grammar":
6387
+ case "node":
6388
+ case "token":
6389
+ case "leaf":
6390
+ case "withCtx":
6391
+ return [d.parser];
6392
+ case "sepBy":
6393
+ return [d.parser, d.separator];
6394
+ case "scanTo":
6395
+ return [
6396
+ d.sentinel,
6397
+ ...d.skip,
6398
+ ...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
6399
+ ...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
6400
+ ];
6401
+ // A run's prefixes are probed detached; a committed failure reaches it only from its body.
6402
+ case "lines":
6403
+ return [d.body];
6404
+ case "lazy": {
6405
+ try {
6406
+ return [d.thunk()];
6407
+ } catch {
6408
+ const name = p._ruleName;
6409
+ const target = name === void 0 ? void 0 : resolve3?.(name);
6410
+ return target === void 0 ? true : [target];
6411
+ }
6412
+ }
6413
+ default:
6414
+ return NO_EDGES;
6415
+ }
6416
+ }
6417
+ function reachesMarked(root, successors, memo) {
6418
+ const known = memo.get(root);
6419
+ if (known !== void 0) return known;
6420
+ const order = /* @__PURE__ */ new Map();
6421
+ const low = [];
6422
+ const found = [];
6423
+ const stack = [];
6424
+ const visit = (node2) => {
6425
+ const at = low.length;
6426
+ order.set(node2, at);
6427
+ low.push(at);
6428
+ found.push(false);
6429
+ stack.push(node2);
6430
+ const next = successors(node2);
6431
+ if (next === true) found[at] = true;
6432
+ else {
6433
+ for (const child of next) {
6434
+ if (found[at]) break;
6435
+ const hit = memo.get(child);
6436
+ if (hit !== void 0) {
6437
+ if (hit) found[at] = true;
6438
+ continue;
6439
+ }
6440
+ const seen = order.get(child);
6441
+ if (seen !== void 0) {
6442
+ if (seen < low[at]) low[at] = seen;
6443
+ continue;
6444
+ }
6445
+ const k = visit(child);
6446
+ if (low[k] < low[at]) low[at] = low[k];
6447
+ if (found[k]) found[at] = true;
6448
+ }
6449
+ }
6450
+ if (low[at] === at) {
6451
+ const answer = found[at];
6452
+ let member;
6453
+ do {
6454
+ member = stack.pop();
6455
+ memo.set(member, answer);
6456
+ } while (member !== node2);
6457
+ }
6458
+ return at;
6459
+ };
6460
+ return found[visit(root)];
6461
+ }
6462
+ function hasSemanticReduction(roots, externalRefs) {
6463
+ const seen = /* @__PURE__ */ new Set();
6464
+ const visit = (parser2) => {
6465
+ if (seen.has(parser2)) return false;
6466
+ seen.add(parser2);
6467
+ const def = parser2._def;
6468
+ if (def.tag === "transform" && !def.recognitionOnly) return true;
6469
+ if (def.tag === "choice" && def.gates.some(Boolean)) return true;
6470
+ if (def.tag === "guard" || def.tag === "withCtx") return true;
6471
+ if (def.tag === "node" && def.build !== void 0) return true;
6472
+ if (def.tag === "lazy") {
6473
+ if (externalRefs?.has(parser2)) return false;
6474
+ try {
6475
+ return visit(def.thunk());
6476
+ } catch {
6477
+ return true;
6478
+ }
6479
+ }
6480
+ return childrenOf2(def).some(visit);
6481
+ };
6482
+ return roots.some(visit);
6483
+ }
6484
+ function classifyRuleMap(ruleMap) {
6485
+ const externalRefs = /* @__PURE__ */ new Set();
6486
+ const scanned = /* @__PURE__ */ new Set();
6487
+ const scanExternal = (p) => {
6488
+ if (scanned.has(p)) return;
6489
+ scanned.add(p);
6490
+ const def = p._def;
6491
+ if (def.tag === "lazy") {
6492
+ let resolved;
6493
+ try {
6494
+ resolved = def.thunk();
6495
+ } catch {
6496
+ resolved = void 0;
6497
+ }
6498
+ if (resolved === void 0) {
6499
+ if (p._ruleName) externalRefs.add(p);
6500
+ return;
6501
+ }
6502
+ scanExternal(resolved);
6503
+ return;
6504
+ }
6505
+ for (const child of childrenOf2(def)) scanExternal(child);
6506
+ };
6507
+ for (const [, rule] of ruleMap) scanExternal(rule);
6508
+ return {
6509
+ hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
6510
+ isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs)
6511
+ };
6512
+ }
6513
+
6514
+ // src/table/ops.ts
6515
+ var OP_LIT = 1;
6516
+ var OP_RX = 2;
6517
+ var OP_SEQ = 3;
6518
+ var OP_SEQV = 4;
6519
+ var OP_CHOICE = 5;
6520
+ var OP_REP = 6;
6521
+ var OP_REPV = 7;
6522
+ var OP_OPT = 8;
6523
+ var OP_XFORM = 9;
6524
+ var OP_NODE = 10;
6525
+ var OP_RULE = 11;
6526
+ var OP_GATE = 12;
6527
+ var OP_NOT = 13;
6528
+ var OP_PEEK = 14;
6529
+ var OP_LEAF = 15;
6530
+ var OP_EMPTY = 16;
6531
+ var OP_LIT_TRACK = 17;
6532
+ var OP_RX_TRACK = 18;
6533
+ var OP_NODE_TRACK = 19;
6534
+ var OP_SCOPE = 20;
6535
+ var OP_EXPECT = 21;
6536
+ var OP_SEQX = 22;
6537
+ var OP_SCAN = 23;
6538
+ var OP_FIELD = 24;
6539
+ var OP_LIT_CI = 27;
6540
+ var OP_LIT_CI_TRACK = 28;
6541
+ var OP_TOKEN = 29;
6542
+ var OP_SCOPE_CAP = 30;
6543
+ var OP_WITHCTX = 31;
6544
+ var OP_GUARD = 32;
6545
+ var OP_ADJ = 33;
6546
+ var OP_GREEDY = 34;
6547
+ var OP_REJECT = 35;
6548
+ var OP_ARMGATE = 36;
6549
+ var OP_LIVE = 37;
6550
+ var OP_ATTEMPT = 38;
6551
+ var OP_LABEL = 39;
6552
+ var OP_COV = 40;
6553
+ var OP_SCOPE_PLAIN = 41;
6554
+ var OP_LEX_BODY = 42;
6555
+ var OP_LEX_PROGRAM = 43;
6556
+ var OP_SOURCE_LEAF = 44;
6557
+ var OP_DISPATCH = 25;
6558
+ var OP_ROUTED = 26;
6559
+ var OP_NAMES = {
6560
+ [OP_LIT]: "LIT",
6561
+ [OP_RX]: "RX",
6562
+ [OP_SEQ]: "SEQ",
6563
+ [OP_SEQV]: "SEQV",
6564
+ [OP_CHOICE]: "CHOICE",
6565
+ [OP_REP]: "REP",
6566
+ [OP_REPV]: "REPV",
6567
+ [OP_OPT]: "OPT",
6568
+ [OP_XFORM]: "XFORM",
6569
+ [OP_NODE]: "NODE",
6570
+ [OP_RULE]: "RULE",
6571
+ [OP_GATE]: "GATE",
6572
+ [OP_NOT]: "NOT",
6573
+ [OP_PEEK]: "PEEK",
6574
+ [OP_LEAF]: "LEAF",
6575
+ [OP_SOURCE_LEAF]: "SOURCE_LEAF",
6576
+ [OP_EMPTY]: "EMPTY",
6577
+ [OP_LIT_TRACK]: "LIT_TRACK",
6578
+ [OP_RX_TRACK]: "RX_TRACK",
6579
+ [OP_NODE_TRACK]: "NODE_TRACK",
6580
+ [OP_SCOPE]: "SCOPE",
6581
+ [OP_EXPECT]: "EXPECT",
6582
+ [OP_SEQX]: "SEQX",
6583
+ [OP_SCAN]: "SCAN",
6584
+ [OP_FIELD]: "FIELD",
6585
+ [OP_LIT_CI]: "LIT_CI",
6586
+ [OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
6587
+ [OP_DISPATCH]: "DISPATCH",
6588
+ [OP_ROUTED]: "ROUTED",
6589
+ [OP_TOKEN]: "TOKEN",
6590
+ [OP_SCOPE_CAP]: "SCOPE_CAP",
6591
+ [OP_WITHCTX]: "WITHCTX",
6592
+ [OP_GUARD]: "GUARD",
6593
+ [OP_ADJ]: "ADJ",
6594
+ [OP_GREEDY]: "GREEDY",
6595
+ [OP_REJECT]: "REJECT",
6596
+ [OP_ARMGATE]: "ARMGATE",
6597
+ [OP_LIVE]: "LIVE",
6598
+ [OP_ATTEMPT]: "ATTEMPT",
6599
+ [OP_LABEL]: "LABEL",
6600
+ [OP_COV]: "COV",
6601
+ [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
6602
+ [OP_LEX_BODY]: "LEX_BODY",
6603
+ [OP_LEX_PROGRAM]: "LEX_PROGRAM"
6604
+ };
6605
+
6492
6606
  // src/compiler/direct-projection.ts
6493
6607
  function directArrayProjection(fn, fnSrc, arity) {
6494
6608
  let source = fnSrc;
@@ -7369,6 +7483,7 @@ var Encoder = class {
7369
7483
  case "expect":
7370
7484
  case "recover":
7371
7485
  case "scanTo":
7486
+ case "lines":
7372
7487
  case "unknown":
7373
7488
  return true;
7374
7489
  case "lazy": {
@@ -7385,6 +7500,33 @@ var Encoder = class {
7385
7500
  return childrenOf2(def);
7386
7501
  }
7387
7502
  }
7503
+ /**
7504
+ * THE FIRST-CHARACTER GATE OF AN `optional`, `not` OR `peek` CHILD.
7505
+ *
7506
+ * A non-nullable child cannot match at a character outside its first set, so
7507
+ * the wrapper's outcome there is already decided: `optional` yields `null`,
7508
+ * `not` succeeds, `peek` fails with its own set. The interpreter's `optional`
7509
+ * makes that test itself (`repeat.ts` `startsFirstSet`); the table entered the
7510
+ * child — a rollback mark, the call, the child's own failure — to learn it.
7511
+ *
7512
+ * The gate is an `OP_GATE` row around the child, so every engine runs it
7513
+ * unchanged, a probe or tolerant parse forwards through it as for a node's
7514
+ * gate, and the wrapper's row keeps its layout. The engines test the class
7515
+ * before taking the wrapper's mark. The first set is resolved through
7516
+ * `winners`, the analysis a choice arm's gate uses, so a composed rule's set
7517
+ * is the merged one. A scalar `not` child stays bare: `scalarTerminalNotChild`
7518
+ * already reduces it to one recognizer call.
7519
+ */
7520
+ lookaheadGate(p, child) {
7521
+ const op = this.code[child];
7522
+ if (op === OP_GATE || op === OP_LIT || op === OP_LIT_CI || op === OP_LIT_TRACK || op === OP_LIT_CI_TRACK || op === OP_RX || op === OP_RX_TRACK) return child;
7523
+ const rr = this.refResolver();
7524
+ if (matchesEmpty(p, /* @__PURE__ */ new Set(), rr)) return child;
7525
+ const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
7526
+ if (cls < 0) return child;
7527
+ const e = deriveExpected(p);
7528
+ return this.emit(OP_GATE, cls, child, this.expected(e.length > 0 ? e : [p._tag]));
7529
+ }
7388
7530
  /** A first set becomes a char class string, or −1 for `any`. */
7389
7531
  charClass(fs3) {
7390
7532
  if (fs3.kind !== "ranges" || fs3.ranges.length === 0) return -1;
@@ -7396,12 +7538,19 @@ var Encoder = class {
7396
7538
  this.ccIndex.set(spec, i);
7397
7539
  return i;
7398
7540
  }
7399
- /** Bind a guard only when its class is already part of the program. Repeat
7400
- * gating must not grow the compact artifact merely to avoid a failed call. */
7401
- existingCharClass(fs3) {
7402
- if (fs3.kind !== "ranges" || fs3.ranges.length === 0) return -1;
7403
- const hit = this.ccIndex.get(encodeClassSpec(fs3.ranges));
7404
- return hit !== void 0 && hit < 100 ? hit : -1;
7541
+ /**
7542
+ * The first set a node's or an attempt's fail-fast gate tests. A bare
7543
+ * cross-rule ref resolves through `winners` (a compose OVERRIDE may widen
7544
+ * it). Any other body keeps its construction-time `_meta.firstSet`, except
7545
+ * where that is `any`: under `compose()` a body that reaches a rule of
7546
+ * another piece is built before that rule exists, so its cached set is
7547
+ * `any` and the node got NO gate (jess's `CompoundSelector` was entered at
7548
+ * `{`, `Call` at `}`). The resolved set is the one a choice arm's gate uses,
7549
+ * over the final merged map.
7550
+ */
7551
+ gateFirstSet(p) {
7552
+ const cached = p._meta.firstSet;
7553
+ return p._def.tag === "lazy" || cached.kind === "any" ? firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : cached;
7405
7554
  }
7406
7555
  /**
7407
7556
  * Wrap one choice arm in its `autoNot` checks (`OP_REJECT`).
@@ -7589,6 +7738,18 @@ var Encoder = class {
7589
7738
  open: bal.open,
7590
7739
  close: bal.close
7591
7740
  }));
7741
+ if (d.tag === "lines") {
7742
+ return this.emit(OP_SCAN, this.scanSlot({
7743
+ kind: 2,
7744
+ flags: 0,
7745
+ skip: [],
7746
+ prefix: this.subtree(d.prefix),
7747
+ body: this.subtree(d.body),
7748
+ ...d.first === void 0 ? {} : { first: this.subtree(d.first) },
7749
+ ...d.lazy === void 0 ? {} : { lazy: this.subtree(d.lazy) }
7750
+ }));
7751
+ }
7752
+ if (d.tag === "columns") return this.emit(OP_SCAN, this.scanSlot({ kind: 3, flags: 0, skip: [], n: d.n }));
7592
7753
  if (d.tag === "scanTo") {
7593
7754
  const sentDef = d.sentinel._def;
7594
7755
  return this.emit(OP_SCAN, this.scanSlot({
@@ -7694,6 +7855,12 @@ var Encoder = class {
7694
7855
  if (transactionalFirst[i] !== void 0) classes[i] = -1;
7695
7856
  }
7696
7857
  }
7858
+ if (d.gates.some((g) => g !== null && g !== void 0) && !dispatchIsExclusive(classes, this.cc)) {
7859
+ for (let k = 0; k < classes.length; k++) {
7860
+ const gate2 = d.gates[order[k]];
7861
+ if (gate2 !== null && gate2 !== void 0) classes[k] = -1;
7862
+ }
7863
+ }
7697
7864
  const dispIdx = this.disp.length;
7698
7865
  this.disp.push(classes);
7699
7866
  let rollbackMask = kids.length > 31 ? -1 : 0;
@@ -7723,7 +7890,7 @@ var Encoder = class {
7723
7890
  case "oneOrMore": {
7724
7891
  const child = this.node(d.parser).ip;
7725
7892
  const rr = this.refResolver();
7726
- const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.existingCharClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
7893
+ const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
7727
7894
  const op = d.valueUnused ? OP_REPV : OP_REP;
7728
7895
  const min = d.tag === "many" ? 0 : d.min;
7729
7896
  return this.rec ? this.emit(op, child, min, d.max ?? -1, -1, 0, this.expected(deriveExpected(d.parser)), itemClass) : this.emit(op, child, min, d.max ?? -1, -1, 0);
@@ -7747,7 +7914,7 @@ var Encoder = class {
7747
7914
  return d.min >= 2 ? this.emit(OP_REP, child, d.min, d.max ?? -1, sep2, flags, this.expected(deriveExpected(d.parser))) : this.emit(OP_REP, child, d.min, d.max ?? -1, sep2, flags);
7748
7915
  }
7749
7916
  case "optional": {
7750
- const head = this.emit(OP_OPT, this.node(d.parser).ip);
7917
+ const head = this.emit(OP_OPT, this.lookaheadGate(d.parser, this.node(d.parser).ip));
7751
7918
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(head);
7752
7919
  return head;
7753
7920
  }
@@ -7795,8 +7962,8 @@ var Encoder = class {
7795
7962
  // every CST node, so this is load-bearing, not reflection trivia.
7796
7963
  d.tags === void 0 ? -1 : this.constant(d.tags)
7797
7964
  );
7798
- if (matchesEmpty(d.parser)) return body;
7799
- const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
7965
+ if (matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return body;
7966
+ const fs3 = this.gateFirstSet(d.parser);
7800
7967
  if (fs3.kind === "any") return body;
7801
7968
  const cls = this.charClass(fs3);
7802
7969
  if (cls < 0) return body;
@@ -7888,7 +8055,7 @@ var Encoder = class {
7888
8055
  // other engine reported `['not(literal)']`. It hid because `expected` is a
7889
8056
  // TOP-LEVEL field on `RunResult` and is NOT part of the identity digest.
7890
8057
  case "not":
7891
- return this.emit(OP_NOT, this.node(d.parser).ip, this.expected(assertionFailureExpected(false, d.parser._tag)));
8058
+ return this.emit(OP_NOT, this.lookaheadGate(d.parser, this.node(d.parser).ip), this.expected(assertionFailureExpected(false, d.parser._tag)));
7892
8059
  case "guard":
7893
8060
  return this.emit(OP_GUARD, this.fn(d.predicate, d.predSrc ?? null), this.expected(["gate"]));
7894
8061
  case "adjacency":
@@ -7909,14 +8076,14 @@ var Encoder = class {
7909
8076
  return this.emit(OP_WITHCTX, this.constant(d.extra), this.node(d.parser).ip);
7910
8077
  }
7911
8078
  case "peek":
7912
- return this.emit(OP_PEEK, this.node(d.parser).ip, this.expected(assertionFailureExpected(true, d.parser._tag)));
8079
+ return this.emit(OP_PEEK, this.lookaheadGate(d.parser, this.node(d.parser).ip), this.expected(assertionFailureExpected(true, d.parser._tag)));
7913
8080
  // A TRANSACTION IS A ROW. See `OP_ATTEMPT` for why the transparent
7914
8081
  // lowering was correct only for a choice arm.
7915
8082
  case "attempt": {
7916
8083
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
7917
8084
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
7918
- const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
7919
- if (fs3.kind === "any" || matchesEmpty(d.parser)) return inner;
8085
+ const fs3 = this.gateFirstSet(d.parser);
8086
+ if (fs3.kind === "any" || matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return inner;
7920
8087
  const cls = this.charClass(fs3);
7921
8088
  if (cls < 0) return inner;
7922
8089
  const e = deriveExpected(d.parser);
@@ -8035,12 +8202,7 @@ var Encoder = class {
8035
8202
  };
8036
8203
  const seen = /* @__PURE__ */ new Set();
8037
8204
  const refs = [];
8038
- for (const s of this.scans) {
8039
- if (s.sentinel !== void 0) refs.push(s.sentinel);
8040
- if (s.recoverAt !== void 0) refs.push(s.recoverAt);
8041
- if (s.stopAt !== void 0) refs.push(s.stopAt);
8042
- refs.push(...s.skip);
8043
- }
8205
+ for (const s of this.scans) refs.push(...scanRefs(s));
8044
8206
  for (const set of this.scanSkipSets) refs.push(...set);
8045
8207
  const stack = [...Object.values(this.rules), ...refs.map((r) => r[0])];
8046
8208
  while (stack.length > 0) {
@@ -8060,7 +8222,11 @@ var Encoder = class {
8060
8222
  skip: s.skip.map(res),
8061
8223
  ...s.sentinel === void 0 ? {} : { sentinel: res(s.sentinel) },
8062
8224
  ...s.recoverAt === void 0 ? {} : { recoverAt: res(s.recoverAt) },
8063
- ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) }
8225
+ ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) },
8226
+ ...s.prefix === void 0 ? {} : { prefix: res(s.prefix) },
8227
+ ...s.body === void 0 ? {} : { body: res(s.body) },
8228
+ ...s.first === void 0 ? {} : { first: res(s.first) },
8229
+ ...s.lazy === void 0 ? {} : { lazy: res(s.lazy) }
8064
8230
  }));
8065
8231
  this.scanSkipSets = this.scanSkipSets.map((set) => set.map(res));
8066
8232
  }
@@ -8902,7 +9068,7 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8902
9068
  if (shape.kind === "alt") {
8903
9069
  const okV = mint("_ok");
8904
9070
  const endV2 = mint("_end");
8905
- const lines = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
9071
+ const lines2 = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
8906
9072
  if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
8907
9073
  const groups = /* @__PURE__ */ new Map();
8908
9074
  for (const a of shape.arms) {
@@ -8911,60 +9077,60 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8911
9077
  const lower = f >= 65 && f <= 90 ? f + 32 : f;
8912
9078
  (groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
8913
9079
  }
8914
- lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
9080
+ lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8915
9081
  for (const [lower, arms] of groups) {
8916
9082
  const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
8917
- lines.push(`${ind} ${labels} {`);
9083
+ lines2.push(`${ind} ${labels} {`);
8918
9084
  for (const open of arms) {
8919
9085
  const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
8920
9086
  const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
8921
- lines.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
9087
+ lines2.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
8922
9088
  }
8923
- lines.push(`${ind} break`);
8924
- lines.push(`${ind} }`);
9089
+ lines2.push(`${ind} break`);
9090
+ lines2.push(`${ind} }`);
8925
9091
  }
8926
- lines.push(`${ind}}`);
8927
- return { setup: lines, ok: okV, end: endV2 };
9092
+ lines2.push(`${ind}}`);
9093
+ return { setup: lines2, ok: okV, end: endV2 };
8928
9094
  }
8929
9095
  if (shape.disjoint) {
8930
9096
  const plan = planAltSwitch(shape.firsts);
8931
9097
  if (plan.kind === "switch") {
8932
- lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
9098
+ lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8933
9099
  shape.arms.forEach((arm, k) => {
8934
9100
  const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
8935
9101
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8936
- lines.push(`${ind} ${labels} {`);
8937
- lines.push(...m.setup);
8938
- lines.push(`${ind} ${okV} = ${m.ok}`);
8939
- lines.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8940
- lines.push(`${ind} break`);
8941
- lines.push(`${ind} }`);
9102
+ lines2.push(`${ind} ${labels} {`);
9103
+ lines2.push(...m.setup);
9104
+ lines2.push(`${ind} ${okV} = ${m.ok}`);
9105
+ lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
9106
+ lines2.push(`${ind} break`);
9107
+ lines2.push(`${ind} }`);
8942
9108
  });
8943
- lines.push(`${ind}}`);
9109
+ lines2.push(`${ind}}`);
8944
9110
  } else {
8945
9111
  const c0 = codeAt(start, firstChar);
8946
9112
  shape.arms.forEach((arm, k) => {
8947
9113
  const fs3 = shape.firsts[k];
8948
9114
  const cond = fs3.negated ? `!(${classCond(c0, fs3.ranges)})` : `(${classCond(c0, fs3.ranges)})`;
8949
9115
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8950
- lines.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
8951
- lines.push(...m.setup);
8952
- lines.push(`${ind} ${okV} = ${m.ok}`);
8953
- lines.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8954
- lines.push(`${ind}}`);
9116
+ lines2.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
9117
+ lines2.push(...m.setup);
9118
+ lines2.push(`${ind} ${okV} = ${m.ok}`);
9119
+ lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
9120
+ lines2.push(`${ind}}`);
8955
9121
  });
8956
9122
  }
8957
9123
  } else {
8958
9124
  const lbl = mint("_alt");
8959
- lines.push(`${ind}${lbl}: {`);
9125
+ lines2.push(`${ind}${lbl}: {`);
8960
9126
  for (const arm of shape.arms) {
8961
9127
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8962
- lines.push(...m.setup);
8963
- lines.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
9128
+ lines2.push(...m.setup);
9129
+ lines2.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
8964
9130
  }
8965
- lines.push(`${ind}}`);
9131
+ lines2.push(`${ind}}`);
8966
9132
  }
8967
- return { setup: lines, ok: okV, end: endV2 };
9133
+ return { setup: lines2, ok: okV, end: endV2 };
8968
9134
  }
8969
9135
  if (shape.kind === "chars") {
8970
9136
  const cur = mint("_e");
@@ -8999,64 +9165,64 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8999
9165
  const c = classCond(`input.charCodeAt(${at})`, p.ranges);
9000
9166
  return p.negated ? `!(${c})` : `(${c})`;
9001
9167
  };
9002
- const lines = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
9168
+ const lines2 = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
9003
9169
  let first = true;
9004
9170
  for (const p of shape.parts) {
9005
9171
  if (p.part === "lit") {
9006
9172
  const L = p.cps.length;
9007
9173
  const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
9008
9174
  if (p.optional) {
9009
- lines.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
9175
+ lines2.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
9010
9176
  } else {
9011
- lines.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
9012
- lines.push(`${ind} ${endV2} += ${L}`);
9177
+ lines2.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
9178
+ lines2.push(`${ind} ${endV2} += ${L}`);
9013
9179
  }
9014
9180
  } else if (p.part === "run" && p.max === Infinity) {
9015
9181
  const s = mint("_s");
9016
- lines.push(`${ind} const ${s} = ${endV2}`);
9017
- lines.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9018
- if (p.min === 1) lines.push(`${ind} if (${endV2} === ${s}) break`);
9019
- else if (p.min > 1) lines.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
9182
+ lines2.push(`${ind} const ${s} = ${endV2}`);
9183
+ lines2.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9184
+ if (p.min === 1) lines2.push(`${ind} if (${endV2} === ${s}) break`);
9185
+ else if (p.min > 1) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
9020
9186
  } else if (p.part === "run" && p.max === 1) {
9021
9187
  if (p.min === 1) {
9022
- lines.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
9023
- lines.push(`${ind} ${endV2}++`);
9188
+ lines2.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
9189
+ lines2.push(`${ind} ${endV2}++`);
9024
9190
  } else {
9025
- lines.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9191
+ lines2.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9026
9192
  }
9027
9193
  } else if (p.part === "run") {
9028
9194
  const s = mint("_s");
9029
- lines.push(`${ind} const ${s} = ${endV2}`);
9030
- lines.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9031
- if (p.min > 0) lines.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
9195
+ lines2.push(`${ind} const ${s} = ${endV2}`);
9196
+ lines2.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9197
+ if (p.min > 0) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
9032
9198
  } else {
9033
9199
  const inner = emitShapeMatch(p.inner, endV2, mint, `${ind} `, first ? firstChar : void 0);
9034
9200
  if (!p.unbounded) {
9035
- lines.push(...inner.setup);
9201
+ lines2.push(...inner.setup);
9036
9202
  if (p.min === 1) {
9037
- lines.push(`${ind} if (!(${inner.ok})) break`);
9038
- lines.push(`${ind} ${endV2} = ${inner.end}`);
9203
+ lines2.push(`${ind} if (!(${inner.ok})) break`);
9204
+ lines2.push(`${ind} ${endV2} = ${inner.end}`);
9039
9205
  } else {
9040
- lines.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
9206
+ lines2.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
9041
9207
  }
9042
9208
  } else {
9043
9209
  const progV = mint("_prog");
9044
- lines.push(`${ind} let ${progV} = false`);
9045
- lines.push(`${ind} while (true) {`);
9046
- lines.push(...inner.setup);
9047
- lines.push(`${ind} if (!(${inner.ok})) break`);
9048
- lines.push(`${ind} ${endV2} = ${inner.end}`);
9049
- lines.push(`${ind} ${progV} = true`);
9050
- lines.push(`${ind} }`);
9051
- if (p.min === 1) lines.push(`${ind} if (!${progV}) break`);
9210
+ lines2.push(`${ind} let ${progV} = false`);
9211
+ lines2.push(`${ind} while (true) {`);
9212
+ lines2.push(...inner.setup);
9213
+ lines2.push(`${ind} if (!(${inner.ok})) break`);
9214
+ lines2.push(`${ind} ${endV2} = ${inner.end}`);
9215
+ lines2.push(`${ind} ${progV} = true`);
9216
+ lines2.push(`${ind} }`);
9217
+ if (p.min === 1) lines2.push(`${ind} if (!${progV}) break`);
9052
9218
  }
9053
9219
  }
9054
9220
  first = false;
9055
9221
  }
9056
- lines.push(`${ind} ${okV} = true`);
9057
- lines.push(`${ind}} while (false)`);
9058
- lines.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
9059
- return { setup: lines, ok: okV, end: endV2 };
9222
+ lines2.push(`${ind} ${okV} = true`);
9223
+ lines2.push(`${ind}} while (false)`);
9224
+ lines2.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
9225
+ return { setup: lines2, ok: okV, end: endV2 };
9060
9226
  }
9061
9227
  if (shape.kind === "litFold") {
9062
9228
  const openLen2 = shape.open.length;
@@ -9642,7 +9808,10 @@ var EMITTED_PARAMS = [
9642
9808
  // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
9643
9809
  // old factories ignore them.
9644
9810
  "LEAD",
9645
- "triviaFieldCapture"
9811
+ "triviaFieldCapture",
9812
+ // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
9813
+ // factories ignore it.
9814
+ "skipDeferredTrivia"
9646
9815
  ];
9647
9816
  var RUNTIME_PRELUDE = `
9648
9817
  let _pfScan=null
@@ -9663,7 +9832,7 @@ function _skipTrivia(input,cur,ctx){
9663
9832
  const s=_pfScan
9664
9833
  if(s!==null&&ctx._triviaLog===undefined&&!(ctx.captureTrivia===true&&(ctx._cstBuf!==undefined||ctx._cstTriviaLog!==undefined)))return s(input,cur)
9665
9834
  if(s!==null)return skipTriviaScanned(s,input,cur,ctx)
9666
- if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
9835
+ if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
9667
9836
  return advanceTrivia(input,cur,ctx)
9668
9837
  }
9669
9838
  function _pushLeaf(ctx,value,s,e){pushCstLeaf(ctx,{_tag:'leaf',value,span:{start:s,end:e}})}
@@ -9870,11 +10039,11 @@ function rebuildPools(cc, fx, disp, plan) {
9870
10039
  })
9871
10040
  };
9872
10041
  }
9873
- function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
10042
+ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select) {
9874
10043
  const { code, k, fx, disp, dsp, triviaLabelled } = t;
9875
10044
  const swapLegal = !cfg.trackLines;
9876
10045
  const hostCst = cfg.hostCst;
9877
- const REC = prog.rec === 1 && cfg.tolerant;
10046
+ const REC2 = prog.rec === 1 && cfg.tolerant;
9878
10047
  if (cfg.coverage) throw new Unemittable("a coverage assembly");
9879
10048
  const roots = [...Object.values(prog.rules), ...extraIps];
9880
10049
  const reachable = [...reachableSites(code, roots)];
@@ -9945,7 +10114,7 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
9945
10114
  const op = code[ip];
9946
10115
  sinkBits.set(
9947
10116
  ip,
9948
- op === OP_SCAN ? 3 : (op === OP_FIELD ? 1 : 0) | (op === OP_EXPECT || REC && (op === OP_REP || op === OP_REPV) ? 2 : 0)
10117
+ op === OP_SCAN ? 3 : (op === OP_FIELD ? 1 : 0) | (op === OP_EXPECT || REC2 && (op === OP_REP || op === OP_REPV) ? 2 : 0)
9949
10118
  );
9950
10119
  }
9951
10120
  const sinkKids = [];
@@ -9969,6 +10138,8 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
9969
10138
  };
9970
10139
  const bodies = [];
9971
10140
  const byIp = /* @__PURE__ */ new Map();
10141
+ const externals = [];
10142
+ const siteBytes = /* @__PURE__ */ new Map();
9972
10143
  const reached = /* @__PURE__ */ new Set();
9973
10144
  const prelude = [];
9974
10145
  const skipDefs = [];
@@ -10104,6 +10275,18 @@ return 1
10104
10275
  tokenDecisionRefs.set(dispatchIp, made);
10105
10276
  return made;
10106
10277
  }
10278
+ function gateMissTest(gip) {
10279
+ const resolved = t.cc[code[gip + 1]];
10280
+ const ci = classes.push([resolved]) - 1;
10281
+ classPlan.push([code[gip + 1]]);
10282
+ const cls = hoist("cl", `CLS[${ci}][0]`);
10283
+ return isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`;
10284
+ }
10285
+ function lookaheadMiss(child, miss) {
10286
+ if (code[child] !== OP_GATE || cfg.tolerant || cfg.probe) return "";
10287
+ return `if(${gateMissTest(child)}){${miss}}
10288
+ `;
10289
+ }
10107
10290
  function skipFor(l) {
10108
10291
  if (l.tri < 0) return "_skipTrivia";
10109
10292
  const ki = l.tri;
@@ -10114,8 +10297,8 @@ return 1
10114
10297
  const nm = `_sk${skipPool.size}`;
10115
10298
  skipPool.set(key, nm);
10116
10299
  if (!hasScan) {
10117
- skipDefs.push(l.buf ? `function ${nm}(input,cur,ctx){return commitTriviaScan(scanTriviaCompact(input,cur,ctx))}` : `function ${nm}(input,cur,ctx){
10118
- if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
10300
+ skipDefs.push(l.buf ? `function ${nm}(input,cur,ctx){return skipDeferredTrivia(input,cur,ctx)}` : `function ${nm}(input,cur,ctx){
10301
+ if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
10119
10302
  return advanceTrivia(input,cur,ctx)}`);
10120
10303
  return nm;
10121
10304
  }
@@ -10160,9 +10343,17 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
10160
10343
  const target = resolveAlias(ip);
10161
10344
  const done = byIp.get(target);
10162
10345
  if (done !== void 0) return done;
10346
+ if (select !== void 0 && !select.has(target)) {
10347
+ const ext = `_px${target}`;
10348
+ byIp.set(target, ext);
10349
+ externals.push(target);
10350
+ return ext;
10351
+ }
10163
10352
  const fname = `_pf${target}`;
10164
10353
  byIp.set(target, fname);
10165
- bodies.push(lower(target, fname));
10354
+ const body = lower(target, fname);
10355
+ siteBytes.set(target, body.length);
10356
+ bodies.push(body);
10166
10357
  return fname;
10167
10358
  }
10168
10359
  function resolveAlias(ip) {
@@ -10302,14 +10493,28 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
10302
10493
  return `${head}EC.e=pos;return ''}`;
10303
10494
  case OP_GATE: {
10304
10495
  const child = link(code[ip + 2]);
10305
- const resolved = t.cc[code[ip + 1]];
10306
- const ci = classes.push([resolved]) - 1;
10307
- classPlan.push([code[ip + 1]]);
10308
- const cls = hoist("cl", `CLS[${ci}][0]`);
10309
10496
  const xf = fxRef(code[ip + 3]);
10310
10497
  return `${head}
10311
- if(${staticBuild && isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
10498
+ if(${gateMissTest(ip)}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
10499
+ return ${child}(input,pos,ctx)
10500
+ }`;
10501
+ }
10502
+ case OP_ARMGATE: {
10503
+ const pred = fnRef(code[ip + 1]);
10504
+ const child = link(code[ip + 2]);
10505
+ const xf = fxRef(code[ip + 3]);
10506
+ return `${head}
10507
+ if(!${pred}(ctx.state)){ctx._fc=false;ctx._fe=-1;ctx._fx=${xf};return FAIL}
10312
10508
  return ${child}(input,pos,ctx)
10509
+ }`;
10510
+ }
10511
+ case OP_WITHCTX: {
10512
+ const ki = code[ip + 1];
10513
+ const child = link(code[ip + 2]);
10514
+ return `${head}
10515
+ const saved=ctx.state
10516
+ ctx.state=${ki < 0 ? `${fnRef(~ki)}(saved)` : kRef(ki)}
10517
+ try{return ${child}(input,pos,ctx)}finally{ctx.state=saved}
10313
10518
  }`;
10314
10519
  }
10315
10520
  case OP_XFORM: {
@@ -10367,7 +10572,7 @@ if(v!==FAIL)return v
10367
10572
  const err={_tag:'parseError',span:${prog.lines === 1 ? "spanLines(ctx,pos,pos)" : "{start:pos,end:pos}"},expected:${xf}}
10368
10573
  const es=ctx._errors
10369
10574
  if(es!==undefined)es.push(err)
10370
- ${REC ? "if(ctx._tolerant===true)captureError(ctx,err)\n" : ""}ctx._fc=false
10575
+ ${REC2 ? "if(ctx._tolerant===true)captureError(ctx,err)\n" : ""}ctx._fc=false
10371
10576
  EC.e=pos
10372
10577
  return err
10373
10578
  }`;
@@ -10488,7 +10693,7 @@ return v
10488
10693
  }
10489
10694
  case OP_LEX_PROGRAM: {
10490
10695
  const programId = code[ip + 1];
10491
- const run = hoist("lexProgram", `LEXPROG[${programId}]`);
10696
+ const run2 = hoist("lexProgram", `LEXPROG[${programId}]`);
10492
10697
  const scanId = t.lexPrograms[programId].scan;
10493
10698
  if (scanId !== void 0) return `${head}
10494
10699
  const sTri=ctx.trivia,sKinds=ctx.triviaKindLabels,sScan=_pfScan
@@ -10506,7 +10711,7 @@ ctx._cstTriviaLog=undefined
10506
10711
  ctx._triviaLog=undefined
10507
10712
  ctx._rootTriviaLog=undefined
10508
10713
  let e
10509
- try{e=${run}(input,pos,ctx,SCANS[${scanId}])}finally{
10714
+ try{e=${run2}(input,pos,ctx,SCANS[${scanId}])}finally{
10510
10715
  _pfScan=sScan
10511
10716
  ctx.trivia=sTri
10512
10717
  ctx.triviaKindLabels=sKinds
@@ -10525,7 +10730,7 @@ EC.e=e
10525
10730
  return v
10526
10731
  }`;
10527
10732
  return `${head}
10528
- const e=${run}(input,pos,ctx)
10733
+ const e=${run2}(input,pos,ctx)
10529
10734
  if(e<0)return FAIL
10530
10735
  const v=input.slice(pos,e)
10531
10736
  if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,{_tag:'leaf',value:v,span:{start:pos,end:e}})
@@ -10536,7 +10741,7 @@ return v
10536
10741
  /* ── transaction ─────────────────────────────────────────────────────── */
10537
10742
  case OP_ATTEMPT: {
10538
10743
  const child = link(code[ip + 1]);
10539
- const clean = !REC && failureRollbackClean(prog, ip);
10744
+ const clean = !REC2 && failureRollbackClean(prog, ip);
10540
10745
  const p = tmp();
10541
10746
  return `${head}
10542
10747
  ${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
@@ -10565,7 +10770,7 @@ return FAIL
10565
10770
  const xf = fxRef(code[ip + 2]);
10566
10771
  const p = tmp();
10567
10772
  return `${head}
10568
- ${emitMark(p, L.buf, L.raw, sinks)}
10773
+ ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${emitMark(p, L.buf, L.raw, sinks)}
10569
10774
  const v=${child}(input,pos,ctx)
10570
10775
  ${emitRollback(p, L.buf, L.raw, sinks)}
10571
10776
  if(v===FAIL){EC.e=pos;return null}
@@ -10579,7 +10784,7 @@ return FAIL
10579
10784
  const xf = fxRef(code[ip + 2]);
10580
10785
  const p = tmp();
10581
10786
  return `${head}
10582
- ${emitMark(p, L.buf, L.raw, sinks)}
10787
+ ${lookaheadMiss(code[ip + 1], `ctx._fe=pos;ctx._fx=${xf};return FAIL`)}${emitMark(p, L.buf, L.raw, sinks)}
10583
10788
  const v=${child}(input,pos,ctx)
10584
10789
  ${emitRollback(p, L.buf, L.raw, sinks)}
10585
10790
  if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
@@ -10589,10 +10794,10 @@ return null
10589
10794
  }
10590
10795
  case OP_OPT: {
10591
10796
  const child = link(code[ip + 1]);
10592
- const clean = !REC && failureRollbackClean(prog, ip);
10797
+ const clean = !REC2 && failureRollbackClean(prog, ip);
10593
10798
  const p = tmp();
10594
10799
  return `${head}
10595
- ${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
10800
+ ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
10596
10801
  ctx._fc=false
10597
10802
  const v=${child}(input,pos,ctx)
10598
10803
  if(v===FAIL){
@@ -10619,15 +10824,15 @@ return v
10619
10824
  const childIp = code[base + i];
10620
10825
  kids.push(code[childIp] === OP_ADJ ? "" : link(childIp));
10621
10826
  }
10622
- const sy = REC ? `${tmp()}sy` : "";
10827
+ const sy = REC2 ? `${tmp()}sy` : "";
10623
10828
  const pub = (i) => {
10624
10829
  const s = sentRef(code[base + n + i]);
10625
10830
  return s === "undefined" ? `ctx._sync=${sy}` : `ctx._sync=${s}??${sy}`;
10626
10831
  };
10627
10832
  const parts = [head];
10628
- if (REC) parts.push(`const ${sy}=ctx._sync`, "try{", pub(0));
10833
+ if (REC2) parts.push(`const ${sy}=ctx._sync`, "try{", pub(0));
10629
10834
  parts.push(`const v0=${kids[0]}(input,pos,ctx)`, "if(v0===FAIL)return FAIL");
10630
- const close = () => REC ? `${parts.join("\n")}
10835
+ const close = () => REC2 ? `${parts.join("\n")}
10631
10836
  }finally{ctx._sync=${sy}}
10632
10837
  }` : `${parts.join("\n")}
10633
10838
  }`;
@@ -10640,7 +10845,7 @@ return v
10640
10845
  for (let i = 1; i < n; i++) {
10641
10846
  const vn = `v${i}`;
10642
10847
  parts.push(`let ${vn}`);
10643
- if (REC) parts.push(pub(i));
10848
+ if (REC2) parts.push(pub(i));
10644
10849
  const childIp = code[base + i];
10645
10850
  if (code[childIp] === OP_ADJ) {
10646
10851
  const negated = code[childIp + 1] === 1;
@@ -10673,7 +10878,7 @@ return v
10673
10878
  const asc = hoist("as", `DISP[${di}].ascii`);
10674
10879
  const hiArr = table.hi;
10675
10880
  const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join("\n");
10676
- const directAscii = staticBuild && table.hi.length === 0;
10881
+ const directAscii = table.hi.length === 0;
10677
10882
  return `${head}
10678
10883
  const c=${directAscii ? "input.charCodeAt(pos)" : "lead(input,pos)"}
10679
10884
  let arm=-1
@@ -10700,7 +10905,7 @@ return FAIL
10700
10905
  const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]));
10701
10906
  const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null);
10702
10907
  const maskable = n <= 32;
10703
- const directAsciiMask = staticBuild && maskable && gates.every(isAsciiOnlyClass);
10908
+ const directAsciiMask = maskable && gates.every(isAsciiOnlyClass);
10704
10909
  const gateRefs = directAsciiMask ? [] : Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
10705
10910
  const tokenCandidate = tokenChoiceCandidates.get(ip);
10706
10911
  const pretests = Array.from({ length: n }, (_, i) => {
@@ -10725,7 +10930,7 @@ return FAIL
10725
10930
  if (maskable) maskPlan.push(~di);
10726
10931
  const p = tmp();
10727
10932
  const encodedRollbackMask = choiceRollbackMask(prog, ip) ?? -1;
10728
- const rollbackMask = REC ? -1 : encodedRollbackMask;
10933
+ const rollbackMask = REC2 ? -1 : encodedRollbackMask;
10729
10934
  const hasRollback = rollbackMask !== 0;
10730
10935
  const startFailureExact = arms.every((_arm, i) => {
10731
10936
  const terminal = leadingScalarTerminal(code, code[base + i], 0, false);
@@ -10742,7 +10947,7 @@ return FAIL
10742
10947
  if (choiceCannotCommit(prog, ip) && !startFailureExact) {
10743
10948
  const deepAt = Array.from({ length: n }, () => tmp());
10744
10949
  const deepFx = Array.from({ length: n }, () => tmp());
10745
- const remember = (i, at, expectedSet) => `${deepAt[i]}=${at};${deepFx[i]}=${expectedSet}`;
10950
+ const remember2 = (i, at, expectedSet) => `${deepAt[i]}=${at};${deepFx[i]}=${expectedSet}`;
10746
10951
  const lazyArms = (ascii) => arms.map((arm, i) => {
10747
10952
  const pretest = pretests[i];
10748
10953
  const decision = pretest?.token === void 0 && pretest?.sequence === void 0 ? "" : tmp();
@@ -10754,13 +10959,13 @@ return FAIL
10754
10959
  if(${decision}===0){
10755
10960
  ctx._fc=false
10756
10961
  {const at=_pfTokEnd
10757
- ${remember(i, "at", pretest.token.expected)}}
10962
+ ${remember2(i, "at", pretest.token.expected)}}
10758
10963
  }`;
10759
10964
  const sequenceMiss = pretest?.sequence === void 0 ? "" : `
10760
10965
  else if(${ascii ? `(bits&${1 << i})!==0&&` : ""}${decision}<0){
10761
10966
  ctx._fc=false
10762
10967
  {const at=_pfSeqEnd
10763
- ${remember(i, "at", pretest.sequence.expected.map((value, at) => `${decision}===${~at}?${value}:`).join("") + pretest.sequence.expected.at(-1))}}
10968
+ ${remember2(i, "at", pretest.sequence.expected.map((value, at) => `${decision}===${~at}?${value}:`).join("") + pretest.sequence.expected.at(-1))}}
10764
10969
  }`;
10765
10970
  return `${decision === "" ? "" : `let ${decision}=${pretest?.sequence === void 0 ? -1 : 0}
10766
10971
  `}if(${gate2}){
@@ -10768,7 +10973,7 @@ ctx._fc=false
10768
10973
  {const v=${arm}(input,pos,ctx)
10769
10974
  if(v!==FAIL)return v}
10770
10975
  {const at=ctx._fe??pos
10771
- ${remember(i, "at", "ctx._fx")}}
10976
+ ${remember2(i, "at", "ctx._fx")}}
10772
10977
  ${rollbackFor(i)}
10773
10978
  }${routeMiss}${sequenceMiss}`;
10774
10979
  }).join("\n");
@@ -10881,7 +11086,7 @@ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
10881
11086
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
10882
11087
  ${startFailureExact ? "" : "let prev=0"}
10883
11088
  ${maskArms}
10884
- ${startFailureExact ? "" : `if(best===pos)acc=${catchName}(${n},prev,acc)`}
11089
+ ${startFailureExact ? "" : `if(best===pos&&prev!==0)acc=${catchName}(${n},prev,acc)`}
10885
11090
  ctx._fe=pos;ctx._fx=${startFailureExact ? `best===pos?${choiceFx}:acc??${choiceFx}` : `acc??${choiceFx}`}
10886
11091
  return FAIL
10887
11092
  }
@@ -10973,14 +11178,14 @@ return [key,v]
10973
11178
  const collect = op === OP_REP;
10974
11179
  let itemCls;
10975
11180
  let itemAscii = false;
10976
- if (!REC) {
11181
+ if (!REC2) {
10977
11182
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
10978
11183
  if (itemClassIndex >= 0) {
10979
11184
  const resolved = t.cc[itemClassIndex];
10980
11185
  const ci = classes.push([resolved]) - 1;
10981
11186
  classPlan.push([itemClassIndex]);
10982
11187
  itemCls = hoist("ri", `CLS[${ci}][0]`);
10983
- itemAscii = staticBuild && isAsciiOnlyClass(resolved);
11188
+ itemAscii = isAsciiOnlyClass(resolved);
10984
11189
  }
10985
11190
  }
10986
11191
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
@@ -11021,11 +11226,11 @@ ${p}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
11021
11226
  ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
11022
11227
  }`;
11023
11228
  const my = `${p}my`;
11024
- const recFx = REC ? fxRef(code[ip + 6]) : "";
11025
- const sepSent = REC ? sentRef(code[ip + 7]) : "undefined";
11229
+ const recFx = REC2 ? fxRef(code[ip + 6]) : "";
11230
+ const sepSent = REC2 ? sentRef(code[ip + 7]) : "undefined";
11026
11231
  const recSent = sep2 === void 0 ? my : sepSent === "undefined" ? `orSentinel(${my},undefined)` : `orSentinel(${sepSent}??${my},${sepSent}===undefined?undefined:${my})`;
11027
11232
  const recGate = sep2 !== void 0 || min === 0 ? "" : `count>=${min}&&`;
11028
- const recBranch = !REC ? "" : `if(ctx._tolerant===true&&${my}!==undefined&&${recGate}!matchesAt(${my},input,itemStart,ctx)){
11233
+ const recBranch = !REC2 ? "" : `if(ctx._tolerant===true&&${my}!==undefined&&${recGate}!matchesAt(${my},input,itemStart,ctx)){
11029
11234
  ${sep2 === void 0 ? `${rb}
11030
11235
  ` : ""}const rr=recoverScan(input,itemStart,ctx,${recSent},${recFx})
11031
11236
  ${collect ? "out.push(rr.error)\n" : ""}captureError(ctx,rr.error)
@@ -11036,7 +11241,7 @@ continue
11036
11241
  `;
11037
11242
  return `${head}
11038
11243
  const out=${collect ? "[]" : "undefined"}
11039
- ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC ? `const ${my}=ctx._sync
11244
+ ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC2 ? `const ${my}=ctx._sync
11040
11245
  ` : ""}${itemCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
11041
11246
  `}let cur=pos
11042
11247
  let count=0
@@ -11075,7 +11280,7 @@ ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
11075
11280
  `}ctx._fc=false
11076
11281
  const v=${child}(input,itemStart,ctx)
11077
11282
  if(v===FAIL){
11078
- ${REC ? `if(ctx._fc===true){
11283
+ ${REC2 ? `if(ctx._fc===true){
11079
11284
  ${rb}
11080
11285
  return FAIL
11081
11286
  }
@@ -11206,7 +11411,7 @@ return nd
11206
11411
  const keepChildren = !structural || cfg.hostReadsChildren !== false || collapse || unwrap;
11207
11412
  const omitsRaw = !hostCst && build !== void 0 && proj < 0 && (flags & 2) !== 0;
11208
11413
  const ty = q(type);
11209
- const stArg = readsState ? "st" : "(ctx.state!==undefined?Object.assign({},ctx.state):undefined)";
11414
+ const stArg = "ctx.state";
11210
11415
  const hostCall = `_pfHost(${ty},hostKids,fieldMap,span,rawKids,tlog,${stArg},${tags})`;
11211
11416
  let value;
11212
11417
  if (proj >= 0) {
@@ -11267,7 +11472,7 @@ ctx.captureTrivia=sCap
11267
11472
  if(v===FAIL)return FAIL
11268
11473
  const end=EC.e
11269
11474
  const span=${tracked ? "spanLines(ctx,pos,end)" : "{start:pos,end}"}
11270
- const st=${readsState ? "(ctx.state!==undefined?Object.assign({},ctx.state):undefined)" : "undefined"}
11475
+ const st=${readsState ? "ctx.state" : "undefined"}
11271
11476
  let nd
11272
11477
  ${unwrap ? "if(kids.length===1)nd=unwrapChild(kids[0])\nelse " : ""}${collapse ? "if(kids.length===1)nd=kids[0]\nelse " : ""}${collapsible ? `if(_pfHost!==undefined&&_pfHost._parsemanCstCollapse!==undefined&&kids.length===1&&rawKids.length===1&&_pfHost._parsemanCstCollapse(${ty},kids[0],kids,rawKids))nd=kids[0]
11273
11478
  else ` : ""}{${value}}
@@ -11280,21 +11485,51 @@ return nd
11280
11485
  throw new Unemittable(`OP_${OP_NAMES[op] ?? String(op)}`);
11281
11486
  }
11282
11487
  }
11488
+ const served = (ip) => select === void 0 || select.has(resolveAlias(ip));
11283
11489
  const ruleEntries = [];
11284
11490
  for (const [rname, entryIp] of Object.entries(prog.rules)) {
11491
+ if (!served(entryIp)) continue;
11285
11492
  const target = link(entryIp);
11286
11493
  const rn = `_r_${rname.replace(/[^A-Za-z0-9_$]/g, "_")}`;
11287
11494
  bodies.push(`const ${rn}=${target}`);
11288
11495
  ruleEntries.push(`${q(rname)}:${rn}`);
11289
11496
  }
11290
11497
  const extra = [];
11291
- for (const ip of extraIps) extra.push(`${ip}:${link(ip)}`);
11498
+ const exported = /* @__PURE__ */ new Set();
11499
+ for (const ip of extraIps) {
11500
+ if (!served(ip)) continue;
11501
+ exported.add(ip);
11502
+ extra.push(`${ip}:${link(ip)}`);
11503
+ }
11504
+ if (select !== void 0) {
11505
+ const kids = [];
11506
+ for (const ip of reachable) {
11507
+ if (select.has(resolveAlias(ip))) continue;
11508
+ kids.length = 0;
11509
+ childSlots(code, ip, kids);
11510
+ for (const kid of kids) {
11511
+ const target = resolveAlias(kid);
11512
+ if (!select.has(target) || exported.has(target)) continue;
11513
+ exported.add(target);
11514
+ extra.push(`${target}:${link(target)}`);
11515
+ }
11516
+ }
11517
+ }
11518
+ const hybrid = select === void 0 ? "" : `,
11519
+ ext:[${externals.join(",")}],
11520
+ bind:function(xs,ps){_xScan=xs
11521
+ ${externals.map((ip, i) => `_xp${ip}=ps[${i}]`).join("\n")}},
11522
+ scan:function(s){const o=_pfScan;_pfScan=s;return o}`;
11523
+ const externalDefs = select === void 0 ? "" : `let _xScan
11524
+ ${externals.map((ip) => `let _xp${ip}
11525
+ function _px${ip}(input,pos,ctx){const o=_xScan(_pfScan);const v=_xp${ip}(input,pos,ctx);_xScan(o);return v}`).join("\n")}
11526
+ `;
11292
11527
  const source = `${RUNTIME_PRELUDE}${needsNoRawPrelude ? NO_RAW_RUNTIME_PRELUDE : ""}
11293
11528
  ${prelude.join("\n")}
11294
11529
  ${skipDefs.join("\n")}
11295
11530
  ${choiceDefs.join("\n")}
11296
11531
  ${bodies.join("\n")}
11297
- function _begin(ctx){
11532
+ ${externalDefs}function _begin(ctx){
11298
11533
  const host=ctx.build
11299
11534
  if(_pfDepth>0)_pfFrames.push([_pfScan,_pfHost,EC.e])
11300
11535
  _pfDepth++
@@ -11321,7 +11556,7 @@ pieces:{${ruleEntries.join(",")}},
11321
11556
  byIp:{${extra.join(",")}},
11322
11557
  end:function(){return EC.e},
11323
11558
  begin:_begin,
11324
- finish:_finish
11559
+ finish:_finish${hybrid}
11325
11560
  }`;
11326
11561
  return {
11327
11562
  source,
@@ -11329,10 +11564,193 @@ finish:_finish
11329
11564
  masks,
11330
11565
  classes,
11331
11566
  armExpected,
11567
+ siteBytes,
11332
11568
  plan: { classes: classPlan, armExpected: armExpectedPlan, masks: maskPlan }
11333
11569
  };
11334
11570
  }
11335
11571
 
11572
+ // src/combinators/lines.ts
11573
+ function lineIndexAt(view, offset) {
11574
+ const lines2 = view.lines;
11575
+ let lo = 0, hi = lines2.length - 1;
11576
+ while (lo < hi) {
11577
+ const mid = lo + hi + 1 >> 1;
11578
+ if (lines2[mid].offset <= offset) lo = mid;
11579
+ else hi = mid - 1;
11580
+ }
11581
+ return lo;
11582
+ }
11583
+ var lineAt = (view, offset) => view.lines[lineIndexAt(view, offset)];
11584
+ function sourceOffset(view, offset) {
11585
+ const line = lineAt(view, offset);
11586
+ return line === void 0 ? offset : line.source + (offset - line.offset);
11587
+ }
11588
+ function columnAt(input, pos, ctx) {
11589
+ const view = ctx.lineView;
11590
+ let start, column, split;
11591
+ const line = view === void 0 ? void 0 : lineAt(view, pos);
11592
+ if (line !== void 0 && pos >= line.offset) {
11593
+ start = line.offset;
11594
+ column = line.column;
11595
+ split = line.split;
11596
+ } else {
11597
+ start = input.lastIndexOf("\n", pos - 1) + 1;
11598
+ column = 0;
11599
+ split = 0;
11600
+ }
11601
+ for (let i = start; i < pos; i++) {
11602
+ if (input.charCodeAt(i) === 9) {
11603
+ const width = 4 - (column - (i === start ? split : 0)) % 4 - (i === start ? split : 0);
11604
+ column += width;
11605
+ } else column++;
11606
+ split = 0;
11607
+ }
11608
+ return { column, split: pos === start ? split : 0 };
11609
+ }
11610
+ function columns(n) {
11611
+ if (!Number.isInteger(n) || n < 0) throw new TypeError(`columns(n) needs a non-negative integer, got ${n}`);
11612
+ const expected = [`columns(${n})`];
11613
+ const meta = {
11614
+ firstSet: n === 0 ? any() : { kind: "ranges", ranges: [{ lo: 9, hi: 9 }, { lo: 32, hi: 32 }] },
11615
+ canMatchNewline: false,
11616
+ isTrivia: false
11617
+ };
11618
+ return {
11619
+ _tag: "columns",
11620
+ _meta: meta,
11621
+ _def: { tag: "columns", n },
11622
+ parse(input, pos, ctx) {
11623
+ const c = ctx;
11624
+ let { column, split } = columnAt(input, pos, c);
11625
+ const goal = column + n;
11626
+ let at = pos;
11627
+ while (column < goal) {
11628
+ const code = input.charCodeAt(at);
11629
+ if (code === 32) {
11630
+ column++;
11631
+ split = 0;
11632
+ at++;
11633
+ continue;
11634
+ }
11635
+ if (code !== 9) return { ok: false, expected, span: { start: at, end: at } };
11636
+ const width = 4 - (column - split) % 4 - split;
11637
+ if (column + width > goal) {
11638
+ c._columnsSplit = { at, split: split + (goal - column) };
11639
+ return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
11640
+ }
11641
+ column += width;
11642
+ split = 0;
11643
+ at++;
11644
+ }
11645
+ return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
11646
+ }
11647
+ };
11648
+ }
11649
+ function lines(prefix, body, options = {}) {
11650
+ const { first, lazy } = options;
11651
+ const opening = first ?? prefix;
11652
+ const meta = {
11653
+ // An opening that matches empty starts a run at any character.
11654
+ firstSet: matchesEmpty(opening) ? any() : opening._meta.firstSet,
11655
+ canMatchNewline: true,
11656
+ isTrivia: false
11657
+ };
11658
+ return {
11659
+ _tag: "lines",
11660
+ _meta: meta,
11661
+ _def: { tag: "lines", prefix, body, ...first === void 0 ? {} : { first }, ...lazy === void 0 ? {} : { lazy } },
11662
+ parse(input, pos, ctx) {
11663
+ const c = ctx;
11664
+ const probe = createDetachedParseContext(false, ctx.state);
11665
+ probe.lineView = c.lineView;
11666
+ const run2 = [];
11667
+ const parts = [];
11668
+ let offset = 0;
11669
+ for (let start = pos; start < input.length || start === pos; ) {
11670
+ let end2 = input.indexOf("\n", start);
11671
+ if (end2 < 0) end2 = input.length;
11672
+ probe._columnsSplit = void 0;
11673
+ const head = (run2.length === 0 ? opening : prefix).parse(input, start, probe);
11674
+ let content;
11675
+ let joined;
11676
+ if (head.ok && head.span.end <= end2) {
11677
+ content = head.span.end;
11678
+ joined = "prefix";
11679
+ } else if (run2.length === 0) {
11680
+ return { ok: false, expected: head.ok ? ["line prefix"] : head.expected, span: { start, end: start } };
11681
+ } else if (lazy !== void 0 && lazy.parse(input, start, probe).ok) {
11682
+ content = start;
11683
+ joined = "lazy";
11684
+ } else break;
11685
+ const mark = probe._columnsSplit;
11686
+ const here = columnAt(input, content, c);
11687
+ const split = joined === "prefix" && mark !== void 0 && mark.at === content ? mark.split : here.split;
11688
+ run2.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
11689
+ const text = input.slice(content, end2);
11690
+ const newline = end2 < input.length;
11691
+ parts.push(newline ? `${text}
11692
+ ` : text);
11693
+ offset += text.length + (newline ? 1 : 0);
11694
+ start = end2 + 1;
11695
+ if (!newline) break;
11696
+ }
11697
+ const view = { lines: run2, parent: c.lineView };
11698
+ const saved = c.lineView, starts = c._lineStarts, lineIndex = c._lineIndex, scanned = c._lineScannedTo, probing = c._probe;
11699
+ const errors = c._errors?.length ?? 0;
11700
+ let cursor = -1;
11701
+ if (probing !== void 0) for (const line2 of run2) {
11702
+ if (probing.offset >= line2.source && probing.offset <= line2.end) {
11703
+ cursor = line2.offset + probing.offset - line2.source;
11704
+ break;
11705
+ }
11706
+ }
11707
+ const inner = cursor < 0 ? void 0 : { offset: cursor, best: null };
11708
+ c.lineView = view;
11709
+ c._lineStarts = void 0;
11710
+ c._lineIndex = void 0;
11711
+ c._lineScannedTo = void 0;
11712
+ c._probe = inner;
11713
+ let result;
11714
+ try {
11715
+ result = body.parse(parts.join(""), 0, ctx);
11716
+ } finally {
11717
+ c.lineView = saved;
11718
+ c._lineStarts = starts;
11719
+ c._lineIndex = lineIndex;
11720
+ c._lineScannedTo = scanned;
11721
+ c._probe = probing;
11722
+ }
11723
+ if (inner?.best) failAt(c, inner.best.expected, sourceOffset(view, inner.best.span.start));
11724
+ if (c._errors !== void 0) for (let i = errors; i < c._errors.length; i++) {
11725
+ const error = c._errors[i];
11726
+ error.span = { start: sourceOffset(view, error.span.start), end: sourceOffset(view, error.span.end) };
11727
+ }
11728
+ if (!result.ok) {
11729
+ const at = sourceOffset(view, result.span.start);
11730
+ return { ...result, span: { start: at, end: at } };
11731
+ }
11732
+ const stop = result.span.end;
11733
+ const index = lineIndexAt(view, stop);
11734
+ const line = run2[index];
11735
+ const end = stop === line.offset && index > 0 ? run2[index - 1].end + 1 : stop - line.offset > line.end - line.source ? line.end + 1 : line.source + (stop - line.offset);
11736
+ recordLineRangeFromContext(c, input, pos, end);
11737
+ return { ok: true, value: result.value, span: { start: pos, end } };
11738
+ }
11739
+ };
11740
+ }
11741
+
11742
+ // src/table/line-scan.ts
11743
+ function rebuildLineScan(s, subtree) {
11744
+ if (s.kind === 2) {
11745
+ return lines(subtree(s.prefix), subtree(s.body), {
11746
+ ...s.first === void 0 ? {} : { first: subtree(s.first) },
11747
+ ...s.lazy === void 0 ? {} : { lazy: subtree(s.lazy) }
11748
+ });
11749
+ }
11750
+ if (s.kind === 3) return columns(s.n);
11751
+ return void 0;
11752
+ }
11753
+
11336
11754
  // src/combinators/scanTo.ts
11337
11755
  function resolveScanSkip(explicitSkip, raw, ctx) {
11338
11756
  if (raw) return [];
@@ -11577,6 +11995,70 @@ function escapeClassChar(ch2) {
11577
11995
  return ch2.replace(/[\\\]^-]/g, "\\$&");
11578
11996
  }
11579
11997
 
11998
+ // src/table/trivia-gap.ts
11999
+ function skipDeferredTrivia(input, cur, ctx) {
12000
+ const triviaP = ctx.trivia;
12001
+ const plain = ctx.trackLines !== true && ctx.captureTrivia !== true;
12002
+ const gap = ctx._gap;
12003
+ if (plain && gap !== void 0 && cur === gap.at && input === gap.input && triviaP === gap.of && ctx._rootTriviaCapture === gap.root && ctx.state === gap.state && ctx._rootTriviaKindIndex === gap.rootKinds && ctx._triviaLog === gap.log) return gap.end;
12004
+ const end = clearRunEnd(input, cur, ctx, triviaP, plain);
12005
+ if (end >= 0) {
12006
+ remember(cur, end, ctx, input, triviaP);
12007
+ return end;
12008
+ }
12009
+ const scan = scanTriviaCompact(input, cur, ctx);
12010
+ if (typeof scan !== "number") return commitTriviaScan(scan);
12011
+ if (plain) remember(cur, scan, ctx, input, triviaP);
12012
+ return scan;
12013
+ }
12014
+ function clearRunEnd(input, cur, ctx, triviaP, plain) {
12015
+ const rootKinds = ctx._rootTriviaKindIndex;
12016
+ const spec = triviaP?._meta.labeledTriviaSpec;
12017
+ if (!plain || triviaP === void 0 || spec == null || rootKinds === void 0 || ctx._triviaLog !== void 0) return -1;
12018
+ if (spec.rootKinds !== rootKinds) prime(spec, triviaP, rootKinds);
12019
+ const fast = spec.rootScan;
12020
+ if (fast === null) return -1;
12021
+ const end = fast(input, cur);
12022
+ return ctx._rootTriviaCapture === false || clearOf(spec.rootLeaders, input, cur, end) ? end : -1;
12023
+ }
12024
+ function remember(cur, end, ctx, input, triviaP) {
12025
+ const gap = ctx._gap;
12026
+ if (gap === void 0) {
12027
+ ctx._gap = { at: cur, end, input, of: triviaP, root: ctx._rootTriviaCapture, rootKinds: ctx._rootTriviaKindIndex, log: ctx._triviaLog, state: ctx.state };
12028
+ return;
12029
+ }
12030
+ gap.at = cur;
12031
+ gap.end = end;
12032
+ gap.input = input;
12033
+ gap.of = triviaP;
12034
+ gap.root = ctx._rootTriviaCapture;
12035
+ gap.rootKinds = ctx._rootTriviaKindIndex;
12036
+ gap.log = ctx._triviaLog;
12037
+ gap.state = ctx.state;
12038
+ }
12039
+ function prime(spec, triviaP, rootKinds) {
12040
+ const ranges = [];
12041
+ let bounded = true;
12042
+ for (const arm of spec.arms) {
12043
+ if (rootKinds[arm.label] === void 0) continue;
12044
+ const fs3 = arm.parser._meta.firstSet;
12045
+ if (fs3.kind === "any") bounded = false;
12046
+ else if (fs3.kind === "ranges") ranges.push(...fs3.ranges);
12047
+ }
12048
+ const classified = triviaP._meta.triviaScanner;
12049
+ spec.rootKinds = rootKinds;
12050
+ spec.rootLeaders = ranges;
12051
+ spec.rootScan = !bounded ? null : classified === void 0 ? fastTriviaScanner(triviaP) : classified;
12052
+ }
12053
+ function clearOf(ranges, input, from, to) {
12054
+ for (let i = from; i < to; i++) {
12055
+ const c = input.charCodeAt(i);
12056
+ if (c >= 55296 && c <= 57343) return false;
12057
+ for (const r of ranges) if (c >= r.lo && c <= r.hi) return false;
12058
+ }
12059
+ return true;
12060
+ }
12061
+
11580
12062
  // src/table/run-support.ts
11581
12063
  function lineCol(ctx, offset) {
11582
12064
  const starts = ctx._lineStarts ?? [0];
@@ -11613,6 +12095,19 @@ function lead(input, pos) {
11613
12095
  // src/cst/host-mode.ts
11614
12096
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
11615
12097
  var FUSED_HOST_ELIDED = /* @__PURE__ */ Symbol.for("parseman.fusedHostElided");
12098
+ function assertHostModeCompatible(mode, build, hostBranchElided = true) {
12099
+ const isCstHost = build?._parsemanCstOutput === true;
12100
+ if (mode === "ast" && isCstHost && hostBranchElided) {
12101
+ throw new Error(
12102
+ "parseman: this parser was compiled for host mode \"ast\" (the default), so its nodes build through their own `build` callbacks and no positioned-CST branch was emitted. It cannot be driven with a positioned-CST host (cstBuildHost / a language-service host). Compile a second artifact from the SAME grammar with `compile(grammar, { hostMode: 'cst' })` \u2014 or, under the macro, a second `rules({ hostMode: 'cst' }, factory)` call site \u2014 and drive that one instead."
12103
+ );
12104
+ }
12105
+ if (mode === "cst" && !isCstHost) {
12106
+ throw new Error(
12107
+ 'parseman: this parser was compiled for host mode "cst", so every node builds through a positioned-CST `ctx.build` host. Pass one (e.g. `{ build: cstBuildHost }`), or use an artifact compiled with the default `hostMode: "ast"` to get the grammar\'s own AST.'
12108
+ );
12109
+ }
12110
+ }
11616
12111
 
11617
12112
  // src/grammar-metadata.ts
11618
12113
  var GRAMMAR_COVERAGE_DEFINITIONS = /* @__PURE__ */ Symbol.for("parseman.grammarCoverageDefinitions");
@@ -11649,7 +12144,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11649
12144
  ...prog.labels === void 0 ? {} : { triviaKindLabels: prog.labels },
11650
12145
  ...prog.classified === 1 ? { rootTriviaClassified: true } : {}
11651
12146
  };
11652
- const lines = prog.lines === 1;
12147
+ const lines2 = prog.lines === 1;
11653
12148
  const mode = prog.hostMode ?? "ast";
11654
12149
  const elided = mode === "ast" && hasDirectBuilder(prog);
11655
12150
  const coverageDefinitions = prog.cov === void 0 ? void 0 : Object.freeze(covDefinitions(prog).map(Object.freeze));
@@ -11677,7 +12172,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11677
12172
  ctx._fe = -1;
11678
12173
  ctx._fx = EMPTY_FX;
11679
12174
  ctx._fc = false;
11680
- if (lines && ctx._lineStarts === void 0) {
12175
+ if (lines2 && ctx._lineStarts === void 0) {
11681
12176
  ctx._lineStarts = [0];
11682
12177
  ctx._lineScannedTo = 0;
11683
12178
  }
@@ -11685,10 +12180,10 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11685
12180
  if (end < 0) {
11686
12181
  const fe = ctx._fe;
11687
12182
  const at = fe === void 0 || fe < 0 ? pos : fe;
11688
- const fspan = lines ? spanLines(ctx, at, at) : { start: at, end: at };
12183
+ const fspan = lines2 ? spanLines(ctx, at, at) : { start: at, end: at };
11689
12184
  return committed2(ctx) ? { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan, committed: true } : { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan };
11690
12185
  }
11691
- return { ok: true, value: d.lastValue(), span: lines ? spanLines(ctx, pos, end) : { start: pos, end } };
12186
+ return { ok: true, value: d.lastValue(), span: lines2 ? spanLines(ctx, pos, end) : { start: pos, end } };
11692
12187
  };
11693
12188
  const ownTrivia = triviaOfRule(prog.rules[name]);
11694
12189
  const meta = ownTrivia === void 0 ? baseMeta : { ...baseMeta, grammarTrivia: ownTrivia };
@@ -11758,7 +12253,7 @@ function assemble(t, prog, cfg) {
11758
12253
  const triviaScan = t.triviaScan;
11759
12254
  const swapLegal = !cfg.trackLines;
11760
12255
  const hostCst = cfg.hostCst;
11761
- const REC = prog.rec === 1 && cfg.tolerant;
12256
+ const REC2 = prog.rec === 1 && cfg.tolerant;
11762
12257
  const sentinels = /* @__PURE__ */ new Map();
11763
12258
  function sentinelFor(cls) {
11764
12259
  if (cls < 0) return void 0;
@@ -11773,6 +12268,9 @@ function assemble(t, prog, cfg) {
11773
12268
  const EC = newEndCell();
11774
12269
  const scalarRecognizers = [];
11775
12270
  const rawScalarSpecs = /* @__PURE__ */ new Set();
12271
+ function lookaheadClass(child) {
12272
+ return code[child] === OP_GATE && !cfg.tolerant && !cfg.probe ? cc[code[child + 1]] : null;
12273
+ }
11776
12274
  function scalarFor(child) {
11777
12275
  const spec = code[child + 1];
11778
12276
  let recognize = scalarRecognizers[spec];
@@ -11782,20 +12280,23 @@ function assemble(t, prog, cfg) {
11782
12280
  }
11783
12281
  return recognize;
11784
12282
  }
11785
- const labelExtraIps = [];
11786
- for (const s of prog.scans ?? []) {
11787
- for (const r of s.skip) labelExtraIps.push(r[0]);
11788
- if (s.sentinel !== void 0) labelExtraIps.push(s.sentinel[0]);
11789
- if (s.recoverAt !== void 0) labelExtraIps.push(s.recoverAt[0]);
11790
- if (s.stopAt !== void 0) labelExtraIps.push(s.stopAt[0]);
11791
- }
11792
- for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0]);
12283
+ const labelExtraIps = assemblyExtraIps(prog);
11793
12284
  const closureLabels = computeSiteLabels(
11794
12285
  code,
11795
12286
  [...Object.values(prog.rules), ...labelExtraIps],
11796
12287
  hostCst
11797
12288
  );
11798
12289
  let SCAN = null;
12290
+ let HYBRID;
12291
+ function hybridPiece(piece) {
12292
+ const setScan = HYBRID.scan;
12293
+ return (input, pos, ctx) => {
12294
+ const saved = setScan(SCAN);
12295
+ const value = piece(input, pos, ctx);
12296
+ setScan(saved);
12297
+ return value;
12298
+ };
12299
+ }
11799
12300
  const memo = /* @__PURE__ */ new Map();
11800
12301
  const reached = /* @__PURE__ */ new Set();
11801
12302
  function pushLeaf(ctx, value, s, e) {
@@ -12087,9 +12588,7 @@ function assemble(t, prog, cfg) {
12087
12588
  return s(input, cur);
12088
12589
  }
12089
12590
  if (s !== null) return skipTriviaScanned(s, input, cur, ctx);
12090
- if (needsDeferredTriviaCommit(ctx)) {
12091
- return commitTriviaScan(scanTriviaCompact(input, cur, ctx));
12092
- }
12591
+ if (needsDeferredTriviaCommit(ctx)) return skipDeferredTrivia(input, cur, ctx);
12093
12592
  return advanceTrivia(input, cur, ctx);
12094
12593
  }
12095
12594
  let TERMV;
@@ -12259,7 +12758,7 @@ function assemble(t, prog, cfg) {
12259
12758
  if (next !== void 0) {
12260
12759
  return next(input, pos, ctx, bits, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best);
12261
12760
  }
12262
- if (best === pos) acc = through(total, prev, acc);
12761
+ if (best === pos && prev !== 0) acc = through(total, prev, acc);
12263
12762
  ctx._fe = pos;
12264
12763
  ctx._fx = acc ?? choiceFx;
12265
12764
  return FAIL;
@@ -12342,7 +12841,7 @@ function assemble(t, prog, cfg) {
12342
12841
  if (next !== void 0) {
12343
12842
  return next(input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best);
12344
12843
  }
12345
- if (best === pos) acc = through(total, prev, acc);
12844
+ if (best === pos && prev !== 0) acc = through(total, prev, acc);
12346
12845
  ctx._fe = pos;
12347
12846
  ctx._fx = acc ?? choiceFx;
12348
12847
  return FAIL;
@@ -12421,6 +12920,13 @@ function assemble(t, prog, cfg) {
12421
12920
  if (done !== void 0) return done;
12422
12921
  const flight = inFlight.get(ip);
12423
12922
  if (flight !== void 0) return flight.fwd;
12923
+ const selected = HYBRID?.byIp[ip];
12924
+ if (selected !== void 0) {
12925
+ const piece2 = hybridPiece(selected);
12926
+ memo.set(ip, piece2);
12927
+ reached.add(ip);
12928
+ return piece2;
12929
+ }
12424
12930
  let target;
12425
12931
  const fwd = (input, pos, ctx) => target(input, pos, ctx);
12426
12932
  const holder = { fwd, set: (p) => {
@@ -12428,7 +12934,7 @@ function assemble(t, prog, cfg) {
12428
12934
  } };
12429
12935
  inFlight.set(ip, holder);
12430
12936
  const op = code[ip];
12431
- const piece = (op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[ip + 3] === 0) && code[ip + 1] >= 0 && closureLabels.at(ip).tri === code[ip + 1] ? link(code[ip + 2]) : lower(ip);
12937
+ const piece = (op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[ip + 3] === 0) && code[ip + 1] >= 0 && closureLabels.at(ip).tri === code[ip + 1] ? link(code[ip + 2]) : countedPiece(lower(ip), ip);
12432
12938
  inFlight.delete(ip);
12433
12939
  holder.set(piece);
12434
12940
  memo.set(ip, piece);
@@ -12769,7 +13275,7 @@ function assemble(t, prog, cfg) {
12769
13275
  const child = link(code[ip + 1]);
12770
13276
  const xf = fx[code[ip + 2]];
12771
13277
  const expectSpan = (ctx, pos) => prog.lines === 1 ? spanLines(ctx, pos, pos) : { start: pos, end: pos };
12772
- if (REC) {
13278
+ if (REC2) {
12773
13279
  return (input, pos, ctx) => {
12774
13280
  const v = child(input, pos, ctx);
12775
13281
  if (v !== FAIL) return v;
@@ -12965,7 +13471,12 @@ function assemble(t, prog, cfg) {
12965
13471
  }
12966
13472
  const child = link(code[ip + 1]);
12967
13473
  const xf = fx[code[ip + 2]];
13474
+ const gate2 = lookaheadClass(code[ip + 1]);
12968
13475
  return (input, pos, ctx) => {
13476
+ if (gate2 !== null && !classHas2(gate2, lead(input, pos))) {
13477
+ EC.e = pos;
13478
+ return null;
13479
+ }
12969
13480
  const need = markCst(ctx);
12970
13481
  const mRaw = MRAW;
12971
13482
  const mTl = MTL;
@@ -12988,7 +13499,13 @@ function assemble(t, prog, cfg) {
12988
13499
  case OP_PEEK: {
12989
13500
  const child = link(code[ip + 1]);
12990
13501
  const xf = fx[code[ip + 2]];
13502
+ const gate2 = lookaheadClass(code[ip + 1]);
12991
13503
  return (input, pos, ctx) => {
13504
+ if (gate2 !== null && !classHas2(gate2, lead(input, pos))) {
13505
+ ctx._fe = pos;
13506
+ ctx._fx = xf;
13507
+ return FAIL;
13508
+ }
12992
13509
  const need = markCst(ctx);
12993
13510
  const mRaw = MRAW;
12994
13511
  const mTl = MTL;
@@ -13010,7 +13527,12 @@ function assemble(t, prog, cfg) {
13010
13527
  }
13011
13528
  case OP_OPT: {
13012
13529
  const child = link(code[ip + 1]);
13530
+ const gate2 = lookaheadClass(code[ip + 1]);
13013
13531
  return (input, pos, ctx) => {
13532
+ if (gate2 !== null && !classHas2(gate2, lead(input, pos))) {
13533
+ EC.e = pos;
13534
+ return null;
13535
+ }
13014
13536
  const need = markCst(ctx);
13015
13537
  const mRaw = MRAW;
13016
13538
  const mTl = MTL;
@@ -13108,8 +13630,8 @@ function assemble(t, prog, cfg) {
13108
13630
  };
13109
13631
  }
13110
13632
  case OP_LEX_PROGRAM: {
13111
- const run = t.lexPrograms[code[ip + 1]];
13112
- const scanId = run.scan;
13633
+ const run2 = t.lexPrograms[code[ip + 1]];
13634
+ const scanId = run2.scan;
13113
13635
  if (scanId !== void 0) {
13114
13636
  return (input, pos, ctx) => {
13115
13637
  const sTrivia = ctx.trivia, sKinds = ctx.triviaKindLabels;
@@ -13130,7 +13652,7 @@ function assemble(t, prog, cfg) {
13130
13652
  ctx._rootTriviaLog = void 0;
13131
13653
  let end;
13132
13654
  try {
13133
- end = run(input, pos, ctx, scans[scanId]);
13655
+ end = run2(input, pos, ctx, scans[scanId]);
13134
13656
  } finally {
13135
13657
  SCAN = sScan;
13136
13658
  ctx.trivia = sTrivia;
@@ -13151,7 +13673,7 @@ function assemble(t, prog, cfg) {
13151
13673
  };
13152
13674
  }
13153
13675
  return (input, pos, ctx) => {
13154
- const end = run(input, pos, ctx);
13676
+ const end = run2(input, pos, ctx);
13155
13677
  if (end < 0) return FAIL;
13156
13678
  const value = input.slice(pos, end);
13157
13679
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: { start: pos, end } });
@@ -13253,7 +13775,7 @@ function assemble(t, prog, cfg) {
13253
13775
  return next === void 0 ? cur : next(input, cur, ctx, values, inherited);
13254
13776
  };
13255
13777
  };
13256
- if (REC) {
13778
+ if (REC2) {
13257
13779
  const first2 = link(code[base]);
13258
13780
  const firstSync = sentinelFor(code[base + n]);
13259
13781
  const runnerAt = (i) => {
@@ -13722,7 +14244,7 @@ function assemble(t, prog, cfg) {
13722
14244
  if (n === 2 || n === 3) {
13723
14245
  const a0 = link(code[base]), a1 = link(code[base + 1]);
13724
14246
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
13725
- const predecide = !REC && !cfg.probe && !cfg.coverage && !cfg.trackLines;
14247
+ const predecide = !REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines;
13726
14248
  const p0ip = predecide ? leadingScalarTerminal(code, code[base], 2, true, true) : -1;
13727
14249
  const p1ip = predecide ? leadingScalarTerminal(code, code[base + 1], 2, true, true) : -1;
13728
14250
  const p2ip = predecide && n === 3 ? leadingScalarTerminal(code, code[base + 2], 2, true, true) : -1;
@@ -13817,6 +14339,11 @@ function assemble(t, prog, cfg) {
13817
14339
  }
13818
14340
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13819
14341
  }
14342
+ if (prev === 0) {
14343
+ ctx._fe = pos;
14344
+ ctx._fx = choiceFx;
14345
+ return FAIL;
14346
+ }
13820
14347
  if (best === pos && prev < 1) acc = accSet(e0, acc);
13821
14348
  if (best === pos && prev < 2) acc = accSet(e1, acc);
13822
14349
  if (best === pos && n === 3 && prev < 3) acc = accSet(e2, acc);
@@ -13947,7 +14474,7 @@ function assemble(t, prog, cfg) {
13947
14474
  const masked = bindMasked(0, EMPTY_EXPECTED_PREFIX);
13948
14475
  const pBit = [];
13949
14476
  const pRec = [];
13950
- if (!REC && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
14477
+ if (!REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
13951
14478
  for (let i = 0; i < n; i++) {
13952
14479
  for (const terminal of largeChoiceLiteralPretest(code, k, code[base + i]) ?? []) {
13953
14480
  pBit.push(1 << i);
@@ -14125,7 +14652,7 @@ function assemble(t, prog, cfg) {
14125
14652
  return (input, pos, ctx) => {
14126
14653
  if (!pred(ctx.state)) {
14127
14654
  ctx._fc = false;
14128
- ctx._fe = pos;
14655
+ ctx._fe = -1;
14129
14656
  ctx._fx = xf;
14130
14657
  return FAIL;
14131
14658
  }
@@ -14183,10 +14710,10 @@ function assemble(t, prog, cfg) {
14183
14710
  const itemFx = reportItem ? fx[code[ip + 6]] : EMPTY_FX2;
14184
14711
  const collect = op === OP_REP;
14185
14712
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
14186
- const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
14713
+ const itemClass = !REC2 && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
14187
14714
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
14188
14715
  const skipBeforeFirst = sepIp < 0 && min === 0;
14189
- if (REC) {
14716
+ if (REC2) {
14190
14717
  const itemFx2 = fx[code[ip + 6]];
14191
14718
  const sepSent = sentinelFor(code[ip + 7]);
14192
14719
  return (input, pos, ctx) => {
@@ -14531,7 +15058,7 @@ function assemble(t, prog, cfg) {
14531
15058
  if (v === FAIL) return FAIL;
14532
15059
  const end = EC.e;
14533
15060
  const span = tracked ? spanLines(ctx, pos, end) : { start: pos, end };
14534
- const st = readsState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
15061
+ const st = readsState ? ctx.state : void 0;
14535
15062
  let nd;
14536
15063
  if (unwrap && kids.length === 1) {
14537
15064
  nd = unwrapChild(kids[0]);
@@ -14544,10 +15071,10 @@ function assemble(t, prog, cfg) {
14544
15071
  ) {
14545
15072
  nd = kids[0];
14546
15073
  } else if (proj >= 0) {
14547
- nd = hostCst && host !== void 0 ? host(type, kids, fieldMap, span, rawKids, tlog, readsState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0, tags) : projectChild(kids, proj, type);
15074
+ nd = hostCst && host !== void 0 ? host(type, kids, fieldMap, span, rawKids, tlog, ctx.state, tags) : projectChild(kids, proj, type);
14548
15075
  } else if (build !== void 0) {
14549
15076
  if (hostCst && host !== void 0) {
14550
- nd = host(type, kids, fieldMap, span, rawKids, tlog, readsState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0, tags);
15077
+ nd = host(type, kids, fieldMap, span, rawKids, tlog, ctx.state, tags);
14551
15078
  } else {
14552
15079
  nd = build(kids, fieldMap, span, rawKids, captureWide ? tlog : EMPTY_TL2, st);
14553
15080
  }
@@ -14598,14 +15125,7 @@ function assemble(t, prog, cfg) {
14598
15125
  let emitRefusal;
14599
15126
  let emitReached;
14600
15127
  {
14601
- const extraIps = [];
14602
- for (const s of prog.scans ?? []) {
14603
- for (const r of s.skip) extraIps.push(r[0]);
14604
- if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
14605
- if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
14606
- if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
14607
- }
14608
- for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
15128
+ const extraIps = assemblyExtraIps(prog);
14609
15129
  const roots = [...Object.values(prog.rules), ...extraIps];
14610
15130
  for (const ip of reachableSites(code, roots)) {
14611
15131
  if (code[ip] === OP_DISPATCH) {
@@ -14686,7 +15206,7 @@ function assemble(t, prog, cfg) {
14686
15206
  lead,
14687
15207
  asciiFoldKey,
14688
15208
  ROUTED_FX,
14689
- REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
15209
+ REC2 ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
14690
15210
  matchesAt,
14691
15211
  recoverScan,
14692
15212
  orSentinel,
@@ -14698,9 +15218,11 @@ function assemble(t, prog, cfg) {
14698
15218
  adjacencyHolds,
14699
15219
  t.lexPrograms,
14700
15220
  LEAD,
14701
- triviaFieldCapture
15221
+ triviaFieldCapture,
15222
+ skipDeferredTrivia
14702
15223
  );
14703
15224
  emitReached = new Set(pre.reached);
15225
+ if (emitted.ext !== void 0) HYBRID = emitted;
14704
15226
  }
14705
15227
  try {
14706
15228
  if (emitted !== void 0) throw new ServedByBuild();
@@ -14770,7 +15292,7 @@ function assemble(t, prog, cfg) {
14770
15292
  // — a strict one emits no reader — and through the SAME `sentinelFor` memo
14771
15293
  // the closure pieces use, so both engines resync to the same combinator
14772
15294
  // object rather than to two separately constructed ones.
14773
- REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
15295
+ REC2 ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
14774
15296
  matchesAt,
14775
15297
  recoverScan,
14776
15298
  orSentinel,
@@ -14782,7 +15304,8 @@ function assemble(t, prog, cfg) {
14782
15304
  adjacencyHolds,
14783
15305
  t.lexPrograms,
14784
15306
  LEAD,
14785
- triviaFieldCapture
15307
+ triviaFieldCapture,
15308
+ skipDeferredTrivia
14786
15309
  );
14787
15310
  emitReached = em.reached;
14788
15311
  } catch (e) {
@@ -14793,7 +15316,7 @@ function assemble(t, prog, cfg) {
14793
15316
  }
14794
15317
  function pieceAt(ip) {
14795
15318
  const em = emitted;
14796
- if (em !== void 0) {
15319
+ if (em !== void 0 && HYBRID === void 0) {
14797
15320
  const p = em.byIp[ip];
14798
15321
  if (p === void 0) throw new Error(`table emitter: no emitted body for site ${ip}`);
14799
15322
  return p;
@@ -14801,6 +15324,11 @@ function assemble(t, prog, cfg) {
14801
15324
  return link(ip);
14802
15325
  }
14803
15326
  for (const s of prog.scans ?? []) {
15327
+ const rebuilt = rebuildLineScan(s, subtreeComb);
15328
+ if (rebuilt !== void 0) {
15329
+ scansArr.push(rebuilt);
15330
+ continue;
15331
+ }
14804
15332
  const skip = s.skip.map((r) => subtreeComb(r));
14805
15333
  const raw = (s.flags & 1) !== 0;
14806
15334
  if (s.kind === 1) {
@@ -14818,6 +15346,37 @@ function assemble(t, prog, cfg) {
14818
15346
  }
14819
15347
  const scans = scansArr;
14820
15348
  const scanSkip = (prog.scanSkip ?? []).map((set) => set.map((r) => subtreeComb(r)));
15349
+ if (HYBRID !== void 0) {
15350
+ const em = HYBRID;
15351
+ em.bind(
15352
+ (s) => {
15353
+ const saved = SCAN;
15354
+ SCAN = s;
15355
+ return saved;
15356
+ },
15357
+ em.ext.map((ip) => link(ip))
15358
+ );
15359
+ const pieces2 = {};
15360
+ for (const [name, entryIp] of Object.entries(prog.rules)) {
15361
+ pieces2[name] = em.pieces[name] ?? link(entryIp);
15362
+ }
15363
+ for (const ip of emitReached) reached.add(ip);
15364
+ return {
15365
+ pieces: pieces2,
15366
+ end: () => EC.e,
15367
+ begin: (ctx) => {
15368
+ begin(ctx);
15369
+ em.begin(ctx);
15370
+ },
15371
+ finish: () => {
15372
+ em.finish();
15373
+ finish();
15374
+ },
15375
+ scanSkip,
15376
+ reached,
15377
+ emitRefusal: void 0
15378
+ };
15379
+ }
14821
15380
  if (emitted !== void 0) {
14822
15381
  const em = emitted;
14823
15382
  return {
@@ -14853,6 +15412,26 @@ function assemble(t, prog, cfg) {
14853
15412
  }
14854
15413
  return { pieces, end: () => EC.e, begin, finish, scanSkip, reached, emitRefusal };
14855
15414
  }
15415
+ var SITE_COUNTS;
15416
+ function countedPiece(piece, ip) {
15417
+ const counts = SITE_COUNTS;
15418
+ if (counts === void 0) return piece;
15419
+ return (input, pos, ctx) => {
15420
+ counts[ip]++;
15421
+ return piece(input, pos, ctx);
15422
+ };
15423
+ }
15424
+ function countSiteVisits(prog, parse2, fns = prog.fns) {
15425
+ const counts = new Int32Array(prog.code.length);
15426
+ const profiled = ownTableProgram({ ...prog, fns, asm: [] });
15427
+ SITE_COUNTS = counts;
15428
+ try {
15429
+ parse2(tableRules(profiled));
15430
+ } finally {
15431
+ SITE_COUNTS = void 0;
15432
+ }
15433
+ return counts;
15434
+ }
14856
15435
  var AssemblyCache = class {
14857
15436
  t;
14858
15437
  prog;
@@ -15042,6 +15621,11 @@ function emitScanSpec(s) {
15042
15621
  if (s.sent !== void 0) parts.push(`sent:${s.sent === null ? "null" : jsString(s.sent)}`);
15043
15622
  if (s.open !== void 0) parts.push(`open:${jsString(s.open)}`);
15044
15623
  if (s.close !== void 0) parts.push(`close:${jsString(s.close)}`);
15624
+ for (const key of ["prefix", "body", "first", "lazy"]) {
15625
+ const r = s[key];
15626
+ if (r !== void 0) parts.push(`${key}:${emitRef(r)}`);
15627
+ }
15628
+ if (s.n !== void 0) parts.push(`n:${s.n}`);
15045
15629
  return `{${parts.join(",")}}`;
15046
15630
  }
15047
15631
  function emitDispatchSpec(d) {
@@ -15061,14 +15645,7 @@ function defaultAssemblyCfgs(prog) {
15061
15645
  function emitAssemblies(prog, cfgs) {
15062
15646
  if (cfgs.length === 0) return ["a:[],"];
15063
15647
  const t = resolveTable(prog);
15064
- const extraIps = [];
15065
- for (const s of prog.scans ?? []) {
15066
- for (const r of s.skip) extraIps.push(r[0]);
15067
- if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
15068
- if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
15069
- if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
15070
- }
15071
- for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
15648
+ const extraIps = assemblyExtraIps(prog);
15072
15649
  const out = [];
15073
15650
  const seen = /* @__PURE__ */ new Set();
15074
15651
  for (const cfg of cfgs) {
@@ -15077,7 +15654,7 @@ function emitAssemblies(prog, cfgs) {
15077
15654
  seen.add(key);
15078
15655
  let em;
15079
15656
  try {
15080
- em = emitAssemblySource(t, prog, cfg, extraIps, true);
15657
+ em = emitAssemblySource(t, prog, cfg, extraIps, true, cfg.select);
15081
15658
  } catch (e) {
15082
15659
  if (e instanceof Unemittable) continue;
15083
15660
  throw e;
@@ -15219,6 +15796,8 @@ function children(def, winners) {
15219
15796
  ...def.recoverAt === void 0 ? [] : [def.recoverAt],
15220
15797
  ...def.stopAt === void 0 ? [] : [def.stopAt]
15221
15798
  ];
15799
+ case "lines":
15800
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
15222
15801
  case "lazy": {
15223
15802
  let resolved;
15224
15803
  try {
@@ -15362,6 +15941,10 @@ function payloadKey(p, d) {
15362
15941
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
15363
15942
  case "scanTo":
15364
15943
  return `scanTo\0${d.raw}\0${d.orEOF}\0${d.recoverAt !== void 0}\0${d.stopAt !== void 0}`;
15944
+ case "lines":
15945
+ return `lines\0${d.first !== void 0}\0${d.lazy !== void 0}`;
15946
+ case "columns":
15947
+ return `columns\0${d.n}`;
15365
15948
  case "guard":
15366
15949
  return `guard\0${fnKey(d.predSrc, d.predicate)}`;
15367
15950
  case "adjacency":
@@ -15482,6 +16065,8 @@ function segment(d, index) {
15482
16065
  if (d.recoverAt !== void 0 && index === d.skip.length + 1) return "scanTo.recoverAt";
15483
16066
  return "scanTo.stopAt";
15484
16067
  }
16068
+ case "lines":
16069
+ return ["lines.prefix", "lines.body", d.first === void 0 ? "lines.lazy" : "lines.first", "lines.lazy"][index] ?? "lines";
15485
16070
  default:
15486
16071
  return d.tag;
15487
16072
  }
@@ -15864,36 +16449,36 @@ function findRegexFragments(visited) {
15864
16449
  if (branches.length >= 2) runs.add(branches.join("|"));
15865
16450
  for (let i = 0; i < branches.length; i++) {
15866
16451
  for (let n = 2; n <= Math.min(MAX_RUN, branches.length - i); n++) {
15867
- const run = branches.slice(i, i + n).join("|");
15868
- if (run.length < 4) continue;
15869
- runs.add(run);
16452
+ const run2 = branches.slice(i, i + n).join("|");
16453
+ if (run2.length < 4) continue;
16454
+ runs.add(run2);
15870
16455
  }
15871
16456
  }
15872
16457
  }
15873
- for (const run of runs) {
15874
- let e = byRun.get(run);
16458
+ for (const run2 of runs) {
16459
+ let e = byRun.get(run2);
15875
16460
  if (e === void 0) {
15876
- e = { occ: [], nodes: /* @__PURE__ */ new Set(), branches: run.split("|").length };
15877
- byRun.set(run, e);
16461
+ e = { occ: [], nodes: /* @__PURE__ */ new Set(), branches: run2.split("|").length };
16462
+ byRun.set(run2, e);
15878
16463
  }
15879
16464
  if (e.nodes.has(v.p)) continue;
15880
16465
  e.nodes.add(v.p);
15881
16466
  e.occ.push({ site: v.site, source: d.source });
15882
16467
  }
15883
16468
  }
15884
- const candidates = [...byRun.entries()].filter(([, e]) => e.occ.length >= 2).map(([run, e]) => ({ run, e, savings: (e.occ.length - 1) * run.length })).sort((a, b) => b.e.occ.length - a.e.occ.length || b.savings - a.savings || b.run.length - a.run.length);
16469
+ const candidates = [...byRun.entries()].filter(([, e]) => e.occ.length >= 2).map(([run2, e]) => ({ run: run2, e, savings: (e.occ.length - 1) * run2.length })).sort((a, b) => b.e.occ.length - a.e.occ.length || b.savings - a.savings || b.run.length - a.run.length);
15885
16470
  const kept = [];
15886
16471
  for (const c of candidates) {
15887
16472
  if (kept.some((k) => k.e.occ.length >= c.e.occ.length && `|${k.run}|`.includes(`|${c.run}|`))) continue;
15888
16473
  kept.push(c);
15889
16474
  }
15890
- return kept.map(({ run, e }) => ({
16475
+ return kept.map(({ run: run2, e }) => ({
15891
16476
  kind: "regex-fragment",
15892
- id: `regex-fragment:${run}`,
15893
- fragment: run,
16477
+ id: `regex-fragment:${run2}`,
16478
+ fragment: run2,
15894
16479
  branches: e.branches,
15895
16480
  count: e.occ.length,
15896
- savings: (e.occ.length - 1) * run.length,
16481
+ savings: (e.occ.length - 1) * run2.length,
15897
16482
  sources: e.occ.map((o) => `/${o.source}/`),
15898
16483
  sites: e.occ.map((o) => o.site),
15899
16484
  suggestion: `this ${e.branches}-branch alternation is re-spelled in ${e.occ.length} DISTINCT regex() terminals. Hoist it to one shared source string and interpolate it (\`new RegExp(\\\`\u2026\\\${OP}\u2026\\\`)\`) or lift the shared part into its own terminal \u2014 an edit to the operator set currently has to land in ${e.occ.length} places, and the analysis cannot tell you when one of them drifts.`
@@ -16524,60 +17109,60 @@ function findKeywordRegexes(visited) {
16524
17109
  var sitesLine = (sites) => sites.slice(0, 6).map(siteToString).join("\n ") + (sites.length > 6 ? `
16525
17110
  \u2026 and ${sites.length - 6} more` : "");
16526
17111
  function formatDuplicationFindings(report) {
16527
- const lines = [];
17112
+ const lines2 = [];
16528
17113
  for (const f of report.structureLoss) {
16529
- lines.push(`parseman BUG [structure-loss] @ ${siteToString(f.site)}: arm[${f.earlier}] flattens \`${f.nodeType}\` where arm[${f.later}] structures it, on ${firstSetToString(f.on)}`);
16530
- lines.push(` flat: ${f.earlierShape}`);
16531
- lines.push(` rich: ${f.laterShape}`);
16532
- lines.push(` lost: ${f.lostNodeTypes.map((t) => `\`${t}\``).join(", ")}`);
16533
- lines.push(` fix: ${f.suggestion}`);
17114
+ lines2.push(`parseman BUG [structure-loss] @ ${siteToString(f.site)}: arm[${f.earlier}] flattens \`${f.nodeType}\` where arm[${f.later}] structures it, on ${firstSetToString(f.on)}`);
17115
+ lines2.push(` flat: ${f.earlierShape}`);
17116
+ lines2.push(` rich: ${f.laterShape}`);
17117
+ lines2.push(` lost: ${f.lostNodeTypes.map((t) => `\`${t}\``).join(", ")}`);
17118
+ lines2.push(` fix: ${f.suggestion}`);
16534
17119
  }
16535
17120
  for (const f of report.rewrites) {
16536
- lines.push(`parseman ${f.bug ? "BUG" : "rewrite"} [${f.rewrite}${f.sepByVerdict === void 0 ? "" : `/${f.sepByVerdict}`}] @ ${siteToString(f.site)}${f.astNeutral ? "" : " (candidate \u2014 verify AST identity)"}`);
16537
- lines.push(` from: ${f.from}`);
16538
- lines.push(` to: ${f.to}`);
16539
- lines.push(` fix: ${f.suggestion}`);
16540
- if (f.perf !== "") lines.push(` perf: ${f.perf}`);
17121
+ lines2.push(`parseman ${f.bug ? "BUG" : "rewrite"} [${f.rewrite}${f.sepByVerdict === void 0 ? "" : `/${f.sepByVerdict}`}] @ ${siteToString(f.site)}${f.astNeutral ? "" : " (candidate \u2014 verify AST identity)"}`);
17122
+ lines2.push(` from: ${f.from}`);
17123
+ lines2.push(` to: ${f.to}`);
17124
+ lines2.push(` fix: ${f.suggestion}`);
17125
+ if (f.perf !== "") lines2.push(` perf: ${f.perf}`);
16541
17126
  }
16542
17127
  for (const f of report.divergentNodes) {
16543
- lines.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t) => `\`${t}\``).join(", ")}`);
16544
- for (const p of f.productions) lines.push(` ${siteToString(p.site)}
17128
+ lines2.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t) => `\`${t}\``).join(", ")}`);
17129
+ for (const p of f.productions) lines2.push(` ${siteToString(p.site)}
16545
17130
  ${p.shape}
16546
17131
  unique to it: ${p.distinctTerms.length === 0 ? "(none)" : p.distinctTerms.map((t) => `\`${t}\``).join(", ")}`);
16547
- lines.push(` fix: ${f.suggestion}`);
17132
+ lines2.push(` fix: ${f.suggestion}`);
16548
17133
  }
16549
17134
  for (const f of report.keywordRegexes) {
16550
17135
  const src = f.source.length > 90 ? `${f.source.slice(0, 88)}\u2026` : f.source;
16551
- lines.push(`parseman ${f.bug ? "BUG " : ""}keyword-regex @ ${siteToString(f.site)}: /${src}/${f.flags}${f.vocabulary ? ` [${f.words.length} literal words${f.longestFirst ? "" : ", NOT longest-first"}]` : ""}${f.hazards.length > 0 ? ` [${f.hazards.length} prefix hazard(s)${f.bug ? ", UNRESCUED" : ""}]` : ""}${f.caseFoldRisk ? " [CASE-FOLD RISK]" : ""}`);
16552
- lines.push(` fix: ${f.suggestion}`);
17136
+ lines2.push(`parseman ${f.bug ? "BUG " : ""}keyword-regex @ ${siteToString(f.site)}: /${src}/${f.flags}${f.vocabulary ? ` [${f.words.length} literal words${f.longestFirst ? "" : ", NOT longest-first"}]` : ""}${f.hazards.length > 0 ? ` [${f.hazards.length} prefix hazard(s)${f.bug ? ", UNRESCUED" : ""}]` : ""}${f.caseFoldRisk ? " [CASE-FOLD RISK]" : ""}`);
17137
+ lines2.push(` fix: ${f.suggestion}`);
16553
17138
  }
16554
17139
  for (const f of report.duplicates) {
16555
- lines.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16556
- lines.push(` at: ${sitesLine(f.sites)}`);
16557
- lines.push(` fix: ${f.suggestion}`);
17140
+ lines2.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
17141
+ lines2.push(` at: ${sitesLine(f.sites)}`);
17142
+ lines2.push(` fix: ${f.suggestion}`);
16558
17143
  }
16559
17144
  for (const f of report.nearDuplicates) {
16560
- lines.push(`parseman near-duplication: ${f.count} clones of a ${f.sharedSize}-node shape differing at ONE slot (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16561
- lines.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
16562
- lines.push(` at: ${sitesLine(f.sites)}`);
16563
- lines.push(` fix: ${f.suggestion}`);
17145
+ lines2.push(`parseman near-duplication: ${f.count} clones of a ${f.sharedSize}-node shape differing at ONE slot (saves ${f.savings} nodes) \u2014 ${f.shape}`);
17146
+ lines2.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
17147
+ lines2.push(` at: ${sitesLine(f.sites)}`);
17148
+ lines2.push(` fix: ${f.suggestion}`);
16564
17149
  }
16565
17150
  for (const f of report.regexFragments) {
16566
- lines.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
16567
- lines.push(` at: ${sitesLine(f.sites)}`);
16568
- lines.push(` fix: ${f.suggestion}`);
17151
+ lines2.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
17152
+ lines2.push(` at: ${sitesLine(f.sites)}`);
17153
+ lines2.push(` fix: ${f.suggestion}`);
16569
17154
  }
16570
17155
  for (const f of report.regexClasses) {
16571
- lines.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
16572
- for (const v of f.variants) lines.push(` [${v.source}] \xD7${v.count} ${v.delta}
17156
+ lines2.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
17157
+ for (const v of f.variants) lines2.push(` [${v.source}] \xD7${v.count} ${v.delta}
16573
17158
  ${sitesLine(v.sites)}`);
16574
- lines.push(` fix: ${f.suggestion}`);
17159
+ lines2.push(` fix: ${f.suggestion}`);
16575
17160
  }
16576
17161
  for (const f of report.overlaps) {
16577
- lines.push(`parseman overlap @ ${siteToString(f.site)}: arm[${f.a}] \u2229 arm[${f.b}] on ${firstSetToString(f.on)}${f.sharedPrefix === null ? "" : ` \u2014 shared prefix: ${f.sharedPrefix}`}${f.handledByStrategy ? " [sharedPrefix strategy already handles this]" : ""}`);
16578
- lines.push(` fix: ${f.suggestion}`);
17162
+ lines2.push(`parseman overlap @ ${siteToString(f.site)}: arm[${f.a}] \u2229 arm[${f.b}] on ${firstSetToString(f.on)}${f.sharedPrefix === null ? "" : ` \u2014 shared prefix: ${f.sharedPrefix}`}${f.handledByStrategy ? " [sharedPrefix strategy already handles this]" : ""}`);
17163
+ lines2.push(` fix: ${f.suggestion}`);
16579
17164
  }
16580
- return lines;
17165
+ return lines2;
16581
17166
  }
16582
17167
  function duplicationFindingCount(report) {
16583
17168
  return report.duplicates.length + report.nearDuplicates.length + report.regexFragments.length + report.regexClasses.length + report.overlaps.length + report.rewrites.length + report.divergentNodes.length + report.structureLoss.length + report.keywordRegexes.length;
@@ -16611,11 +17196,11 @@ function reportDuplication(opt, analyze) {
16611
17196
  );
16612
17197
  return void 0;
16613
17198
  }
16614
- const lines = formatDuplicationFindings(report);
16615
- if (lines.length > 0) {
17199
+ const lines2 = formatDuplicationFindings(report);
17200
+ if (lines2.length > 0) {
16616
17201
  if (level === "error") throw new Error(`parseman: ${duplicationFindingCount(report)} duplication/overlap finding(s)
16617
- ${lines.join("\n")}`);
16618
- for (const l of lines) console.warn(l);
17202
+ ${lines2.join("\n")}`);
17203
+ for (const l of lines2) console.warn(l);
16619
17204
  }
16620
17205
  return report;
16621
17206
  }
@@ -16626,8 +17211,46 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
16626
17211
  return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16627
17212
  }
16628
17213
 
17214
+ // src/table/select-hot.ts
17215
+ var LOOP = 8;
17216
+ var ROUNDS = 60;
17217
+ var CAP = 1e12;
17218
+ function selectHotSites(code, entries, siteBytes, budget, profile) {
17219
+ const selected = /* @__PURE__ */ new Set();
17220
+ if (budget <= 0) return selected;
17221
+ const weight = profile === void 0 ? staticWeights(code, entries) : void 0;
17222
+ const w = (ip) => (weight !== void 0 ? weight.get(ip) : profile[ip]) ?? 0;
17223
+ const ranked = [...siteBytes.keys()].sort((a, b) => w(b) / siteBytes.get(b) - w(a) / siteBytes.get(a) || a - b);
17224
+ let spent = 0;
17225
+ for (const ip of ranked) {
17226
+ if (w(ip) === 0) break;
17227
+ const bytes = siteBytes.get(ip);
17228
+ if (spent + bytes > budget) continue;
17229
+ spent += bytes;
17230
+ selected.add(ip);
17231
+ }
17232
+ return selected;
17233
+ }
17234
+ function staticWeights(code, entries) {
17235
+ let weight = /* @__PURE__ */ new Map();
17236
+ const kids = [];
17237
+ for (let round = 0; round < ROUNDS; round++) {
17238
+ const next = /* @__PURE__ */ new Map();
17239
+ for (const ip of entries) next.set(ip, 1);
17240
+ for (const [ip, w] of weight) {
17241
+ kids.length = 0;
17242
+ childSlots(code, ip, kids);
17243
+ const op = code[ip];
17244
+ const scale = op === OP_REP || op === OP_REPV ? LOOP : op === OP_CHOICE || op === OP_DISPATCH ? 1 / Math.max(1, kids.length) : 1;
17245
+ for (const kid of kids) next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
17246
+ }
17247
+ weight = next;
17248
+ }
17249
+ return weight;
17250
+ }
17251
+
16629
17252
  // src/table/compile-rule-map.ts
16630
- var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
17253
+ var SUPERCOMPILE_GROWTH = 1.05;
16631
17254
  function resolveHostMode(ruleMap, explicit) {
16632
17255
  return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
16633
17256
  }
@@ -16724,14 +17347,40 @@ function compileRuleMap(ruleMap, opts = {}) {
16724
17347
  entry: null,
16725
17348
  runtimeRef: opts.runtimeRef ?? "tableRules",
16726
17349
  fnSources: sources,
16727
- ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
17350
+ ...options.select !== void 0 && options.select.size > 0 ? { assemblies: [{ ...defaultAssemblyCfgs(artifact)[0], select: options.select }] } : {},
16728
17351
  ...metadataSource === void 0 ? {} : { metadataSource }
16729
17352
  });
16730
17353
  const replacement = replacementWithMetadata();
17354
+ const selectAssembly = (budget, profile) => {
17355
+ const none = { sites: /* @__PURE__ */ new Set(), bytes: 0 };
17356
+ if (hostMode !== "ast" || prog.lines === 1) return none;
17357
+ const table = resolveTable(artifact);
17358
+ const cfg = defaultAssemblyCfgs(artifact)[0];
17359
+ const roots = assemblyExtraIps(artifact);
17360
+ let siteBytes, fixed;
17361
+ try {
17362
+ siteBytes = emitAssemblySource(table, artifact, cfg, roots, true).siteBytes;
17363
+ fixed = emitAssemblySource(table, artifact, cfg, roots, true, /* @__PURE__ */ new Set()).source.length;
17364
+ } catch (e) {
17365
+ if (e instanceof Unemittable) return { ...none, refusal: e.construct };
17366
+ throw e;
17367
+ }
17368
+ const ceiling = budget / SUPERCOMPILE_GROWTH;
17369
+ let target = ceiling;
17370
+ for (let attempt2 = 0; attempt2 < 16; attempt2++) {
17371
+ const sites = selectHotSites(artifact.code, Object.values(artifact.rules), siteBytes, target - fixed, profile);
17372
+ if (sites.size === 0) return none;
17373
+ const bytes = emitAssemblySource(table, artifact, cfg, roots, true, sites).source.length;
17374
+ if (bytes <= ceiling) return { sites, bytes: Math.ceil(bytes * SUPERCOMPILE_GROWTH) };
17375
+ target *= Math.min(ceiling / bytes, 0.99);
17376
+ }
17377
+ return none;
17378
+ };
16731
17379
  return {
16732
17380
  keys: ruleMap.map(([key]) => key),
16733
17381
  replacement,
16734
17382
  replacementWithMetadata,
17383
+ selectAssembly,
16735
17384
  hostMode,
16736
17385
  // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
16737
17386
  // dropped", which is what the driver's `'ast' artifact + CST host` check keys
@@ -16886,7 +17535,7 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
16886
17535
  const emitOpts = { fnSources: sources };
16887
17536
  const source = printable ? emitTableModule(artifact, { name: "grammar", ...emitOpts }) : "";
16888
17537
  const inlineExpression = printable ? emitTableExpression(artifact, { entry: ENTRY, runtimeRef: opts.runtimeRef ?? "tableRules", ...emitOpts }) : null;
16889
- const runOnce = (input, pos, ctx) => entry(input, pos, ctx);
17538
+ const runOnce2 = (input, pos, ctx) => entry(input, pos, ctx);
16890
17539
  return {
16891
17540
  source,
16892
17541
  inlineExpression,
@@ -16900,10 +17549,10 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
16900
17549
  // parser runs where a combinator does. A caller probing many positions
16901
17550
  // passes one context and pays for none per call.
16902
17551
  parse(input, pos = 0, ctx) {
16903
- return runOnce(input, pos, ctx ?? createParseContext());
17552
+ return runOnce2(input, pos, ctx ?? createParseContext());
16904
17553
  },
16905
17554
  parseWithContext(input, parseCtx, pos = 0) {
16906
- return runOnce(input, pos, parseCtx);
17555
+ return runOnce2(input, pos, parseCtx);
16907
17556
  },
16908
17557
  /**
16909
17558
  * NO LONGER REFUSES. It used to throw unless the caller passed
@@ -16919,7 +17568,7 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
16919
17568
  const errors = [];
16920
17569
  ctx._errors = errors;
16921
17570
  ctx._tolerant = true;
16922
- const r = runOnce(input, pos, ctx);
17571
+ const r = runOnce2(input, pos, ctx);
16923
17572
  return { ...r, errors };
16924
17573
  }
16925
17574
  };
@@ -16962,6 +17611,9 @@ function childrenOf3(def) {
16962
17611
  ];
16963
17612
  case "routed":
16964
17613
  return def.fallback ? [def.fallback] : [];
17614
+ case "lines":
17615
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
17616
+ case "columns":
16965
17617
  case "lazy":
16966
17618
  case "literal":
16967
17619
  case "regex":
@@ -17291,6 +17943,15 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17291
17943
  return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
17292
17944
  case "scanTo":
17293
17945
  return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}]${def.raw ? ", raw: true" : ""}, orEOF: ${def.orEOF}${def.recoverAt === void 0 ? "" : `, recoverAt: ${kid(def.recoverAt)}`}${def.stopAt === void 0 ? "" : `, stopAt: ${kid(def.stopAt)}`} })`;
17946
+ case "lines": {
17947
+ const options = [
17948
+ ...def.first === void 0 ? [] : [`first: ${kid(def.first)}`],
17949
+ ...def.lazy === void 0 ? [] : [`lazy: ${kid(def.lazy)}`]
17950
+ ];
17951
+ return `lines(${kid(def.prefix)}, ${kid(def.body)}${options.length === 0 ? "" : `, { ${options.join(", ")} }`})`;
17952
+ }
17953
+ case "columns":
17954
+ return `columns(${def.n})`;
17294
17955
  case "transform": {
17295
17956
  if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
17296
17957
  return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
@@ -17355,7 +18016,7 @@ function fieldOptionsSrc(def) {
17355
18016
  }
17356
18017
 
17357
18018
  // src/version.ts
17358
- var PARSEMAN_VERSION = "0.55.4";
18019
+ var PARSEMAN_VERSION = "0.57.0";
17359
18020
 
17360
18021
  // src/compiler/compile-linkable-table.ts
17361
18022
  function isLocal(rule) {
@@ -17448,6 +18109,337 @@ function cstBuildHost(typeOrOptions, children3, _fields, span, rawChildren, triv
17448
18109
  cstBuildHost._parsemanCstOutput = true;
17449
18110
  var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
17450
18111
 
18112
+ // src/cst/trivia-entries.ts
18113
+ var EMPTY_INDICES = Object.freeze([]);
18114
+ var EMPTY_GAPS = Object.freeze([]);
18115
+ var SELECTED_ROOT_STRIDE = 5;
18116
+ function buildRootTriviaIndex(log, labels) {
18117
+ const length = Math.floor(log.length / SELECTED_ROOT_STRIDE);
18118
+ const markerOffset = (i) => i * SELECTED_ROOT_STRIDE;
18119
+ const ownedRangeStart = (i) => log[markerOffset(i)];
18120
+ const ownedRangeEnd = (i) => log[markerOffset(i) + 1];
18121
+ const markerStart = (i) => log[markerOffset(i) + 2];
18122
+ const markerEnd = (i) => log[markerOffset(i) + 3];
18123
+ const markerKind = (i) => log[markerOffset(i) + 4];
18124
+ const entries = {
18125
+ length,
18126
+ labels,
18127
+ stride: SELECTED_ROOT_STRIDE,
18128
+ start: markerStart,
18129
+ end: markerEnd,
18130
+ insertIndex: () => void 0,
18131
+ kindIndex: markerKind,
18132
+ kind(i) {
18133
+ return labels[markerKind(i)];
18134
+ },
18135
+ text(i, input) {
18136
+ return input.slice(markerStart(i), markerEnd(i));
18137
+ }
18138
+ };
18139
+ const lowerBound = (offset, by) => {
18140
+ let lo = 0;
18141
+ let hi = length;
18142
+ while (lo < hi) {
18143
+ const mid = lo + hi >>> 1;
18144
+ if (by(mid) < offset) lo = mid + 1;
18145
+ else hi = mid;
18146
+ }
18147
+ return lo;
18148
+ };
18149
+ const gapRowsAt = (offset, direction) => {
18150
+ const edge = direction === "before" ? ownedRangeEnd : ownedRangeStart;
18151
+ let first = lowerBound(offset, edge);
18152
+ if (first === length || edge(first) !== offset) return void 0;
18153
+ const start = ownedRangeStart(first);
18154
+ const end = ownedRangeEnd(first);
18155
+ while (first > 0 && ownedRangeStart(first - 1) === start && ownedRangeEnd(first - 1) === end) first--;
18156
+ let last = first + 1;
18157
+ while (last < length && ownedRangeStart(last) === start && ownedRangeEnd(last) === end) last++;
18158
+ return [first, last];
18159
+ };
18160
+ const indices = (first, last) => {
18161
+ const out = [];
18162
+ for (let i = first; i < last; i++) out.push(i);
18163
+ return out;
18164
+ };
18165
+ const makeGap = (first, last) => {
18166
+ const start = ownedRangeStart(first);
18167
+ const end = ownedRangeEnd(first);
18168
+ let entryIndices;
18169
+ return {
18170
+ start,
18171
+ end,
18172
+ get entryIndices() {
18173
+ return entryIndices ??= indices(first, last);
18174
+ },
18175
+ hasKind(kind) {
18176
+ for (let i = first; i < last; i++) {
18177
+ if (labels[markerKind(i)] === kind) return true;
18178
+ }
18179
+ return false;
18180
+ },
18181
+ text(input) {
18182
+ return input.slice(start, end);
18183
+ }
18184
+ };
18185
+ };
18186
+ let maps;
18187
+ const getMaps = () => {
18188
+ if (maps !== void 0) return maps;
18189
+ const before = /* @__PURE__ */ new Map();
18190
+ const after = /* @__PURE__ */ new Map();
18191
+ const beforeGaps = /* @__PURE__ */ new Map();
18192
+ const afterGaps = /* @__PURE__ */ new Map();
18193
+ const gaps = [];
18194
+ for (let first = 0; first < length; ) {
18195
+ let last = first + 1;
18196
+ while (last < length && ownedRangeStart(last) === ownedRangeStart(first) && ownedRangeEnd(last) === ownedRangeEnd(first)) last++;
18197
+ const gap = makeGap(first, last);
18198
+ gaps.push(gap);
18199
+ afterGaps.set(gap.start, gap);
18200
+ beforeGaps.set(gap.end, gap);
18201
+ after.set(gap.start, [...gap.entryIndices]);
18202
+ before.set(gap.end, [...gap.entryIndices]);
18203
+ first = last;
18204
+ }
18205
+ maps = { before, after, beforeGaps, afterGaps, gaps };
18206
+ return maps;
18207
+ };
18208
+ return {
18209
+ entries,
18210
+ labels,
18211
+ get before() {
18212
+ return getMaps().before;
18213
+ },
18214
+ get after() {
18215
+ return getMaps().after;
18216
+ },
18217
+ entryIndicesBefore(offset) {
18218
+ const rows = gapRowsAt(offset, "before");
18219
+ return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES;
18220
+ },
18221
+ entryIndicesAfter(offset) {
18222
+ const rows = gapRowsAt(offset, "after");
18223
+ return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES;
18224
+ },
18225
+ gapBefore(offset) {
18226
+ const rows = gapRowsAt(offset, "before");
18227
+ return rows ? makeGap(rows[0], rows[1]) : void 0;
18228
+ },
18229
+ gapAfter(offset) {
18230
+ const rows = gapRowsAt(offset, "after");
18231
+ return rows ? makeGap(rows[0], rows[1]) : void 0;
18232
+ },
18233
+ gaps() {
18234
+ return getMaps().gaps;
18235
+ },
18236
+ gapsWithKind(kind) {
18237
+ const kinds = typeof kind === "string" ? [kind] : kind;
18238
+ const matches2 = [];
18239
+ for (let first = 0; first < length; ) {
18240
+ let last = first + 1;
18241
+ while (last < length && ownedRangeStart(last) === ownedRangeStart(first) && ownedRangeEnd(last) === ownedRangeEnd(first)) last++;
18242
+ let matched = false;
18243
+ for (let i = first; i < last && !matched; i++) {
18244
+ const label2 = labels[markerKind(i)];
18245
+ for (const kind2 of kinds) {
18246
+ if (label2 === kind2) {
18247
+ matched = true;
18248
+ break;
18249
+ }
18250
+ }
18251
+ }
18252
+ if (matched) matches2.push(makeGap(first, last));
18253
+ first = last;
18254
+ }
18255
+ return matches2.length === 0 ? EMPTY_GAPS : matches2;
18256
+ }
18257
+ };
18258
+ }
18259
+
18260
+ // src/functional/run.ts
18261
+ var SELECT_EXAMPLE = "run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })";
18262
+ var REMOVED_RUN_RESULT_FIELDS = [
18263
+ [
18264
+ "triviaMap",
18265
+ `It was a dense root-trivia index built on every parse; root trivia is now an OPT-IN sparse capture, because most grammars paid for an index they never read. Migration: name the trivia labels your grammar defines \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia\`, whose \`.index\` carries the labels and gap lookups \`triviaMap\` exposed.`
18266
+ ],
18267
+ [
18268
+ "triviaLog",
18269
+ `It was the flat \`start, end[, kindIndex]\` log every other trivia view was derived from, and it is no longer allocated at all \u2014 not empty, absent. Migration: request the labels you need \u2014 ${SELECT_EXAMPLE} \u2014 and read \`RunResult.rootTrivia.rows\`. Do NOT reuse a stride-2/3 reader: rows are a different width and a different unit (one row per SELECTED marker, not one per trivia chunk), so an old loop reads confidently wrong offsets rather than failing.`
18270
+ ],
18271
+ [
18272
+ "triviaKindLabels",
18273
+ `It was already optional, so nothing ever flagged its disappearance \u2014 and \`triviaKindMask(undefined, \u2026)\` means "capture everything", so reading it now silently WIDENS capture instead of erroring. Migration: request labels \u2014 ${SELECT_EXAMPLE} \u2014 then the set you asked for is \`RunResult.rootTrivia.select\` and the resolved table is \`RunResult.rootTrivia.index.labels\`.`
18274
+ ]
18275
+ ];
18276
+ var RunResultRecord = class {
18277
+ constructor(ok, value, span, expected, errors, unconsumedFrom) {
18278
+ this.ok = ok;
18279
+ this.value = value;
18280
+ this.span = span;
18281
+ this.expected = expected;
18282
+ this.errors = errors;
18283
+ this.unconsumedFrom = unconsumedFrom;
18284
+ }
18285
+ };
18286
+ var RunResultWithRootTrivia = class {
18287
+ constructor(ok, value, span, expected, errors, rootTrivia, unconsumedFrom) {
18288
+ this.ok = ok;
18289
+ this.value = value;
18290
+ this.span = span;
18291
+ this.expected = expected;
18292
+ this.errors = errors;
18293
+ this.rootTrivia = rootTrivia;
18294
+ this.unconsumedFrom = unconsumedFrom;
18295
+ }
18296
+ };
18297
+ for (const proto of [RunResultRecord.prototype, RunResultWithRootTrivia.prototype]) {
18298
+ for (const [name, detail] of REMOVED_RUN_RESULT_FIELDS) {
18299
+ Object.defineProperty(proto, name, {
18300
+ configurable: true,
18301
+ enumerable: false,
18302
+ get() {
18303
+ throw new TypeError(
18304
+ `RunResult.${name} was REMOVED in parseman 0.44.0 and has no default replacement. ${detail}`
18305
+ );
18306
+ }
18307
+ });
18308
+ }
18309
+ }
18310
+ var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
18311
+ function ambientTriviaFromRunnable(r) {
18312
+ if (!r) return void 0;
18313
+ if (typeof r === "function") return r._meta?.grammarTrivia;
18314
+ if (r._meta.isTrivia) return void 0;
18315
+ return r._meta.grammarTrivia ?? (r._def.tag === "grammar" ? r._def.triviaParser : void 0);
18316
+ }
18317
+ function triviaKindLabelsFromRunnable(r) {
18318
+ if (!r) return void 0;
18319
+ if (typeof r === "function") return r._meta?.triviaKindLabels;
18320
+ const grammarTrivia = r._meta.grammarTrivia;
18321
+ return grammarTrivia?._meta.triviaKindLabels ?? r._meta.triviaKindLabels ?? (r._def.tag === "grammar" ? r._def.triviaParser?._meta.triviaKindLabels : void 0);
18322
+ }
18323
+ function rootTriviaClassifiedFromRunnable(r) {
18324
+ if (!r) return false;
18325
+ if (typeof r === "function") return r._meta?.rootTriviaClassified === true;
18326
+ const grammarTrivia = r._meta.grammarTrivia;
18327
+ return grammarTrivia?._meta.rootTriviaClassified === true || r._meta.rootTriviaClassified === true;
18328
+ }
18329
+ function makeSelectedRootLabelIndex(labels) {
18330
+ const index = /* @__PURE__ */ Object.create(null);
18331
+ for (let i = 0; i < labels.length; i++) {
18332
+ if (index[labels[i]] === void 0) index[labels[i]] = i;
18333
+ }
18334
+ return index;
18335
+ }
18336
+ function growSpanEnd(span, input, end) {
18337
+ if (span.endLine === void 0 || span.endColumn === void 0) {
18338
+ return { start: span.start, end };
18339
+ }
18340
+ let line = span.endLine;
18341
+ let column = span.endColumn;
18342
+ for (let i = span.end; i < end; i++) {
18343
+ if (input.charCodeAt(i) === 10) {
18344
+ line++;
18345
+ column = 1;
18346
+ } else {
18347
+ column++;
18348
+ }
18349
+ }
18350
+ return { ...span, end, endLine: line, endColumn: column };
18351
+ }
18352
+ function runOnce(entry, input, options) {
18353
+ if (typeof entry !== "function" && typeof entry?.parse !== "function") {
18354
+ throw new TypeError(
18355
+ `run(): start production is ${entry === null ? "null" : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`
18356
+ );
18357
+ }
18358
+ const rootTriviaSelection = options.rootTrivia?.select;
18359
+ const captureRootTrivia = rootTriviaSelection !== void 0 && rootTriviaSelection.length > 0;
18360
+ const selectedRootLog = captureRootTrivia ? [] : void 0;
18361
+ const selectedRootLabelIndex = captureRootTrivia ? makeSelectedRootLabelIndex(rootTriviaSelection) : void 0;
18362
+ const errors = [];
18363
+ const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
18364
+ const grammarScanSkip = typeof entry !== "function" ? entry._meta.grammarScanSkip : void 0;
18365
+ const triviaKindLabels = triviaKindLabelsFromRunnable(entry) ?? triviaKindLabelsFromRunnable(options.trivia);
18366
+ if (captureRootTrivia && triviaKindLabels === void 0) {
18367
+ throw new TypeError("run(): rootTrivia.select requires labeled grammar trivia.");
18368
+ }
18369
+ if (captureRootTrivia) {
18370
+ for (const label2 of rootTriviaSelection) {
18371
+ if (!triviaKindLabels.includes(label2)) {
18372
+ throw new TypeError(`run(): rootTrivia.select contains unknown trivia label ${JSON.stringify(label2)}.`);
18373
+ }
18374
+ }
18375
+ }
18376
+ if (captureRootTrivia && !rootTriviaClassifiedFromRunnable(entry) && !rootTriviaClassifiedFromRunnable(options.trivia)) {
18377
+ throw new TypeError(
18378
+ "run(): rootTrivia.select requires classifiedTrivia() at the root."
18379
+ );
18380
+ }
18381
+ const stamped = entry;
18382
+ assertHostModeCompatible(
18383
+ typeof entry === "function" ? stamped[FUSED_HOST_MODE] ?? "ast" : entry._meta.grammarHostMode ?? "ast",
18384
+ options.build,
18385
+ typeof entry === "function" && stamped[FUSED_HOST_ELIDED] === true
18386
+ );
18387
+ const ctx = createParseContext();
18388
+ if (captureRootTrivia) {
18389
+ ctx._rootTriviaLog = selectedRootLog;
18390
+ ctx._rootTriviaKindIndex = selectedRootLabelIndex;
18391
+ ctx._rootTriviaStrictScopes = true;
18392
+ }
18393
+ ctx._errors = errors;
18394
+ ctx.build = options.build;
18395
+ ctx.state = options.state;
18396
+ if (grammarTrivia !== void 0) ctx.trivia = grammarTrivia;
18397
+ if (triviaKindLabels) ctx.triviaKindLabels = triviaKindLabels;
18398
+ if (grammarScanSkip !== void 0) ctx.scanSkip = grammarScanSkip;
18399
+ if (options.triviaCaptureMask !== void 0) ctx._triviaCaptureMask = options.triviaCaptureMask;
18400
+ if (options.tolerant) {
18401
+ ctx._tolerant = true;
18402
+ ctx._rec = REC;
18403
+ }
18404
+ if (options.instrumentation !== void 0) {
18405
+ ctx._grammarCoverage = options.instrumentation._grammarCoverage;
18406
+ ctx._grammarTrace = options.instrumentation._grammarTrace;
18407
+ }
18408
+ const r = invoke(entry, input, 0, ctx);
18409
+ const value = r.ok ? r.value : void 0;
18410
+ let span = r.span ?? { start: 0, end: 0 };
18411
+ const expected = r.ok ? [] : r.expected ?? [];
18412
+ let unconsumedFrom = null;
18413
+ if (r.ok) {
18414
+ let pos = span.end;
18415
+ const tail = options.trivia ?? ambientTriviaFromRunnable(entry);
18416
+ if (tail !== void 0 && pos < input.length) {
18417
+ const t = invoke(tail, input, pos, createParseContext());
18418
+ if (t.ok && t.span.end > pos) {
18419
+ span = growSpanEnd(span, input, t.span.end);
18420
+ pos = t.span.end;
18421
+ }
18422
+ }
18423
+ unconsumedFrom = pos < input.length ? pos : null;
18424
+ }
18425
+ return selectedRootLog !== void 0 && selectedRootLog.length > 0 ? new RunResultWithRootTrivia(
18426
+ r.ok,
18427
+ value,
18428
+ span,
18429
+ expected,
18430
+ errors,
18431
+ {
18432
+ rows: selectedRootLog,
18433
+ select: rootTriviaSelection,
18434
+ index: buildRootTriviaIndex(selectedRootLog, rootTriviaSelection)
18435
+ },
18436
+ unconsumedFrom
18437
+ ) : new RunResultRecord(r.ok, value, span, expected, errors, unconsumedFrom);
18438
+ }
18439
+ function run(entry, input, options = {}) {
18440
+ return runOnce(entry, input, options);
18441
+ }
18442
+
17451
18443
  // src/combinators/gate.ts
17452
18444
  function gate(predicate) {
17453
18445
  const meta = {
@@ -17882,6 +18874,10 @@ var SUPPORTED = {
17882
18874
  field: (...a) => field(a[0], a[1], a[2]),
17883
18875
  noTrivia: (...a) => noTrivia(a[0]),
17884
18876
  token: (...a) => token(a[0]),
18877
+ // `lines(prefix, body, { first, lazy })`: its options are combinators, which the
18878
+ // generic argument evaluation already resolves; a missing option stays absent.
18879
+ lines: (...a) => lines(a[0], a[1], a[2]),
18880
+ columns: (...a) => columns(a[0]),
17885
18881
  // `routed(fallback)` — the fallback must be forwarded. A zero-arg entry here read
17886
18882
  // as "routed takes no arguments" and SILENTLY built a bare `routed()`, so a
17887
18883
  // production written to work both inside and outside a dispatch branch lost its
@@ -19519,19 +20515,14 @@ function fallback(emitted, reason, before) {
19519
20515
  }
19520
20516
  };
19521
20517
  }
19522
- function supercompileRuleMapReplacement(prog, replacement) {
20518
+ function supercompileRuleMapReplacement(prog, replacement, select) {
19523
20519
  const cfg = defaultAssemblyCfgs(prog)[0];
19524
20520
  if (cfg === void 0) return replacement;
19525
- const extraIps = [];
19526
- for (const scan of prog.scans ?? []) {
19527
- for (const ref2 of scan.skip) extraIps.push(ref2[0]);
19528
- if (scan.sentinel !== void 0) extraIps.push(scan.sentinel[0]);
19529
- }
19530
- for (const set of prog.scanSkip ?? []) for (const ref2 of set) extraIps.push(ref2[0]);
20521
+ const extraIps = assemblyExtraIps(prog);
19531
20522
  let canonical2;
19532
20523
  let candidate;
19533
20524
  try {
19534
- canonical2 = emitAssemblySource(resolveTable(prog), prog, cfg, extraIps, true);
20525
+ canonical2 = emitAssemblySource(resolveTable(prog), prog, cfg, extraIps, true, select);
19535
20526
  candidate = supercompileEmittedAssembly(prog, cfg, extraIps, canonical2);
19536
20527
  } catch {
19537
20528
  return replacement;
@@ -19834,6 +20825,7 @@ ${nested}
19834
20825
  }
19835
20826
 
19836
20827
  // src/plugin/ir-eval.ts
20828
+ var INERT_BUILDER = /* @__PURE__ */ Symbol("parseman.inertBuilder");
19837
20829
  function evalRulesIR(ir) {
19838
20830
  return evalIR(ir, rules);
19839
20831
  }
@@ -19873,9 +20865,11 @@ function evalIR(ir, rulesImpl) {
19873
20865
  if (staticError !== void 0 && staticError.length > 0) {
19874
20866
  throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
19875
20867
  }
19876
- const n = node(type, child, (() => {
20868
+ const inert = (() => {
19877
20869
  throw new Error("IR node build requires static re-lowering");
19878
- }), opts);
20870
+ });
20871
+ inert[INERT_BUILDER] = type;
20872
+ const n = node(type, child, inert, opts);
19879
20873
  n._def.buildSrc = src;
19880
20874
  if (staticError !== void 0) n._def.buildStaticError = staticError;
19881
20875
  if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
@@ -19964,6 +20958,8 @@ function evalIR(ir, rulesImpl) {
19964
20958
  "_nd",
19965
20959
  "_gch",
19966
20960
  "_wc",
20961
+ "lines",
20962
+ "columns",
19967
20963
  `return (${ir})`
19968
20964
  );
19969
20965
  return fn(
@@ -20007,14 +21003,28 @@ function evalIR(ir, rulesImpl) {
20007
21003
  _lf,
20008
21004
  _nd,
20009
21005
  _gch,
20010
- _wc
21006
+ _wc,
21007
+ lines,
21008
+ columns
20011
21009
  );
20012
21010
  }
20013
21011
 
20014
21012
  // src/plugin/index.ts
20015
21013
  import { createHash } from "node:crypto";
20016
21014
  var TABLE_RUNTIME_SPECIFIER = "parseman/table";
21015
+ var corpusCache = /* @__PURE__ */ new Map();
21016
+ function corpusTexts(profile) {
21017
+ return profile.files.map((file) => {
21018
+ const mtimeMs = fs2.statSync(file).mtimeMs;
21019
+ const hit = corpusCache.get(file);
21020
+ if (hit !== void 0 && hit.mtimeMs === mtimeMs) return hit.text;
21021
+ const text = fs2.readFileSync(file, "utf8");
21022
+ corpusCache.set(file, { mtimeMs, text });
21023
+ return text;
21024
+ });
21025
+ }
20017
21026
  var PARSEMAN_MODULE = "parseman";
21027
+ var DEFAULT_ASSEMBLY_BUDGET = 0.25;
20018
21028
  function reasonSuffix(runtimeOnly) {
20019
21029
  return runtimeOnly === void 0 || runtimeOnly.length === 0 ? "" : ` \u2014 ${runtimeOnly.join("; ")}`;
20020
21030
  }
@@ -20132,8 +21142,9 @@ var plugin_default = createUnplugin((opts = {}) => ({
20132
21142
  transform(code, id) {
20133
21143
  if (!code.includes("parseman")) return null;
20134
21144
  if (!code.includes("macro")) return null;
21145
+ for (const file of opts.assemblyProfile?.files ?? []) this?.addWatchFile?.(file);
20135
21146
  const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
20136
- const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true, opts.recovery === true, opts.grammarCoverage === true);
21147
+ const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true, opts.recovery === true, opts.grammarCoverage === true, opts.assemblyProfile, opts.assemblyBudget);
20137
21148
  if (result?.warnings.length) {
20138
21149
  for (const w of result.warnings) {
20139
21150
  if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
@@ -20289,17 +21300,17 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
20289
21300
  _sourceLowering.delete(file);
20290
21301
  }
20291
21302
  }
20292
- function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false, recovery = false, grammarCoverage = false) {
21303
+ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false, recovery = false, grammarCoverage = false, assemblyProfile, assemblyBudget = DEFAULT_ASSEMBLY_BUDGET) {
20293
21304
  const depth = degradationCaptureDepth();
20294
21305
  try {
20295
- return transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage);
21306
+ return transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage, assemblyProfile, assemblyBudget);
20296
21307
  } finally {
20297
21308
  setReducerResolver(null);
20298
21309
  setBuilderImportResolver(null);
20299
21310
  for (const d of unwindDegradationCapture(depth)) console.warn(formatDegradation(d));
20300
21311
  }
20301
21312
  }
20302
- function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage) {
21313
+ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage, assemblyProfile, assemblyBudgetShare) {
20303
21314
  let result;
20304
21315
  try {
20305
21316
  result = parseSync5(id, code);
@@ -21125,9 +22136,10 @@ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/v
21125
22136
  carried,
21126
22137
  reflection: compiled.reflection
21127
22138
  });
22139
+ const select = selectAssembly(init.start, "compose()", compiled, !declaresLeaf());
21128
22140
  return {
21129
- replacement: compiled.replacementWithMetadata(reflectionMetadata),
21130
- exportedReplacement: compiled.replacementWithMetadata(exportedMetadata),
22141
+ replacement: assembledReplacement(compiled, reflectionMetadata, select),
22142
+ exportedReplacement: assembledReplacement(compiled, exportedMetadata, select),
21131
22143
  carried,
21132
22144
  ...composing ? { trivia: composing } : {},
21133
22145
  ...importedFactories.length ? { importedFactories } : {},
@@ -21143,6 +22155,42 @@ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/v
21143
22155
  warn(init.start, "compose(): a carried piece has no re-lowerable IR; leaving the runtime compose() in place");
21144
22156
  return null;
21145
22157
  };
22158
+ let assemblyBudget = Math.floor(code.length * assemblyBudgetShare);
22159
+ let leafDeclared;
22160
+ const declaresLeaf = () => leafDeclared ??= body.some((stmt) => {
22161
+ const vd = unwrapVd(stmt);
22162
+ return vd !== null && vd.declarations.some((d) => d.init != null && isComposeLeafCall(d.init));
22163
+ });
22164
+ const selectAssembly = (pos, label2, compiled, terminal) => {
22165
+ if (!terminal || grammarCoverage) return /* @__PURE__ */ new Set();
22166
+ const { sites, bytes, refusal } = compiled.selectAssembly(assemblyBudget, profileOf(compiled));
22167
+ if (refusal !== void 0) {
22168
+ warnings.push(`${id}:${lineOf(pos)} \u2014 ${label2}: its precompiled assembly cannot emit ${refusal}; the artifact runs on the closure engine`);
22169
+ }
22170
+ assemblyBudget -= bytes;
22171
+ return sites;
22172
+ };
22173
+ const profileOf = (compiled) => {
22174
+ if (assemblyProfile === void 0 || compiled.hostMode !== "ast" || compiled.prog.lines === 1 || !(assemblyProfile.entry in compiled.prog.rules)) return void 0;
22175
+ let parsed = 0;
22176
+ const fns = compiled.prog.fns.map((fn) => {
22177
+ const type = fn?.[INERT_BUILDER];
22178
+ return type === void 0 ? fn : (children3, _fields, span) => ({ type, span, children: children3 });
22179
+ });
22180
+ const counts = countSiteVisits(compiled.prog, (rules2) => {
22181
+ const entry = rules2[assemblyProfile.entry];
22182
+ for (const text of corpusTexts(assemblyProfile)) {
22183
+ try {
22184
+ if (run(entry, text).ok) parsed++;
22185
+ } catch {
22186
+ }
22187
+ }
22188
+ }, fns);
22189
+ if (parsed > 0) return counts;
22190
+ warnings.push(`${id} \u2014 assemblyProfile: no corpus file parsed from rule "${assemblyProfile.entry}"; using the static ranking`);
22191
+ return void 0;
22192
+ };
22193
+ const assembledReplacement = (compiled, metadata, select) => select.size === 0 ? compiled.replacementWithMetadata(metadata) : supercompileRuleMapReplacement(compiled.prog, compiled.replacementWithMetadata(metadata, { select }), select);
21146
22194
  const compileComposeLeafCall = (init) => {
21147
22195
  const args = init.arguments;
21148
22196
  const arr = args[0];
@@ -21221,12 +22269,10 @@ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/v
21221
22269
  if (compiled !== null) {
21222
22270
  usedTableRuntime = true;
21223
22271
  return {
21224
- replacement: supercompileRuleMapReplacement(
21225
- compiled.prog,
21226
- compiled.replacementWithMetadata(
21227
- staticTableMetadataSource({ reflection: compiled.reflection, leaf: true }),
21228
- { precompileDefault: true }
21229
- )
22272
+ replacement: assembledReplacement(
22273
+ compiled,
22274
+ staticTableMetadataSource({ reflection: compiled.reflection, leaf: true }),
22275
+ selectAssembly(init.start, "composeLeaf()", compiled, true)
21230
22276
  ),
21231
22277
  ...importedFactories.length ? { importedFactories } : {}
21232
22278
  };
@@ -21478,14 +22524,14 @@ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/v
21478
22524
  }
21479
22525
  if (!allOk) continue;
21480
22526
  const holderVar = `__rules${ruleMapHolderCounter++}`;
21481
- const lines = [
22527
+ const lines2 = [
21482
22528
  `const ${holderVar} = ${compiledRules.replacement}`,
21483
22529
  ...bindings.map(({ ruleKey, localName }) => `${exportPrefix}${kind} ${localName} = ${holderVar}[${JSON.stringify(ruleKey)}]`)
21484
22530
  ];
21485
22531
  replacements.push({
21486
22532
  start: stmtStart,
21487
22533
  end: stmtEnd,
21488
- replacement: lines.join("\n")
22534
+ replacement: lines2.join("\n")
21489
22535
  });
21490
22536
  markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
21491
22537
  }
@@ -21645,12 +22691,12 @@ ${free.map(where).join("\n")}${cause}`
21645
22691
  setBuilderImportResolver(null);
21646
22692
  const degradations = endDegradationCapture();
21647
22693
  if (degradations.length > 0) {
21648
- const lines = formatDegradations(degradations);
22694
+ const lines2 = formatDegradations(degradations);
21649
22695
  if (resolveDegradationLevel() === "error") {
21650
22696
  throw new Error(`parseman: ${degradations.length} degraded compilation path(s) in ${id}
21651
- ${lines.join("\n")}`);
22697
+ ${lines2.join("\n")}`);
21652
22698
  }
21653
- for (const l of lines) warnings.push(`${id}: ${l}`);
22699
+ for (const l of lines2) warnings.push(`${id}: ${l}`);
21654
22700
  }
21655
22701
  return {
21656
22702
  code: ms.toString(),