parseman 0.56.0 → 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 (75) hide show
  1. package/dist/analysis/diagnostics.js +379 -72
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +379 -72
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/node.d.ts +1 -1
  6. package/dist/compiler/build-arity.d.ts +1 -1
  7. package/dist/compiler/build-arity.d.ts.map +1 -1
  8. package/dist/cst/trivia-kinds.d.ts +9 -1
  9. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  10. package/dist/index.js +446 -75
  11. package/dist/index.js.map +3 -3
  12. package/dist/language-service/index.js +1 -0
  13. package/dist/language-service/index.js.map +1 -1
  14. package/dist/parse-context.d.ts.map +1 -1
  15. package/dist/plugin/entry-supercompiler.d.ts +1 -1
  16. package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
  17. package/dist/plugin/index.d.ts +27 -1
  18. package/dist/plugin/index.d.ts.map +1 -1
  19. package/dist/plugin/index.js +930 -157
  20. package/dist/plugin/index.js.map +3 -3
  21. package/dist/plugin/ir-eval.d.ts +2 -0
  22. package/dist/plugin/ir-eval.d.ts.map +1 -1
  23. package/dist/regex/classes.d.ts.map +1 -1
  24. package/dist/run/index.js +1 -0
  25. package/dist/run/index.js.map +1 -1
  26. package/dist/table/assemble.d.ts +9 -0
  27. package/dist/table/assemble.d.ts.map +1 -1
  28. package/dist/table/child-slots.d.ts +4 -6
  29. package/dist/table/child-slots.d.ts.map +1 -1
  30. package/dist/table/compile-rule-map.d.ts +14 -2
  31. package/dist/table/compile-rule-map.d.ts.map +1 -1
  32. package/dist/table/emit-assembly.d.ts +17 -2
  33. package/dist/table/emit-assembly.d.ts.map +1 -1
  34. package/dist/table/emit.d.ts +5 -1
  35. package/dist/table/emit.d.ts.map +1 -1
  36. package/dist/table/encode.d.ts.map +1 -1
  37. package/dist/table/exec-baseline.d.ts.map +1 -1
  38. package/dist/table/exec.d.ts.map +1 -1
  39. package/dist/table/index.js +442 -70
  40. package/dist/table/index.js.map +3 -3
  41. package/dist/table/ops.d.ts +9 -6
  42. package/dist/table/ops.d.ts.map +1 -1
  43. package/dist/table/program.d.ts +13 -0
  44. package/dist/table/program.d.ts.map +1 -1
  45. package/dist/table/select-hot.d.ts +6 -0
  46. package/dist/table/select-hot.d.ts.map +1 -0
  47. package/dist/table/trivia-gap.d.ts +31 -0
  48. package/dist/table/trivia-gap.d.ts.map +1 -0
  49. package/dist/types.d.ts +17 -0
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/version.d.ts +1 -1
  52. package/package.json +4 -1
  53. package/src/combinators/node.ts +6 -13
  54. package/src/combinators/trivia-skip.ts +44 -1
  55. package/src/compiler/build-arity.ts +3 -3
  56. package/src/cst/trivia-kinds.ts +12 -1
  57. package/src/parse-context.ts +1 -0
  58. package/src/plugin/entry-supercompiler.ts +8 -9
  59. package/src/plugin/index.ts +138 -13
  60. package/src/plugin/ir-eval.ts +7 -1
  61. package/src/regex/classes.ts +3 -0
  62. package/src/table/assemble.ts +122 -24
  63. package/src/table/child-slots.ts +4 -6
  64. package/src/table/compile-rule-map.ts +55 -15
  65. package/src/table/emit-assembly.ts +139 -26
  66. package/src/table/emit.ts +8 -7
  67. package/src/table/encode.ts +69 -21
  68. package/src/table/exec-baseline.ts +1 -3
  69. package/src/table/exec.ts +7 -6
  70. package/src/table/ops.ts +9 -6
  71. package/src/table/program.ts +24 -0
  72. package/src/table/select-hot.ts +70 -0
  73. package/src/table/trivia-gap.ts +109 -0
  74. package/src/types.ts +18 -0
  75. package/src/version.ts +1 -1
@@ -366,12 +366,12 @@ function scalarRootOf(combinator) {
366
366
  }
367
367
 
368
368
  // src/combinators/ref.ts
369
- function withRuleTrivia(trivia2, ctx, run) {
369
+ function withRuleTrivia(trivia2, ctx, run2) {
370
370
  const savedLabels = ctx.triviaKindLabels;
371
371
  ctx.trivia = trivia2;
372
372
  ctx.triviaKindLabels = trivia2._meta.triviaKindLabels;
373
373
  try {
374
- return run();
374
+ return run2();
375
375
  } finally {
376
376
  ctx.trivia = void 0;
377
377
  ctx.triviaKindLabels = savedLabels;
@@ -816,6 +816,7 @@ function createParseContext() {
816
816
  _rootTriviaKindIndex: void 0,
817
817
  _rootTriviaStrictScopes: void 0,
818
818
  _rootTriviaCapture: void 0,
819
+ _gap: void 0,
819
820
  _cstTriviaLog: void 0,
820
821
  _fields: void 0,
821
822
  _cstBuf: void 0,
@@ -869,6 +870,7 @@ function literalCodePoints(frag) {
869
870
  if (ch2 === "\\") {
870
871
  const e = frag[i + 1];
871
872
  if (e === void 0) return null;
873
+ if (!(e in CLASS_ESCAPES) && /\w/.test(e)) return null;
872
874
  out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
873
875
  i += 2;
874
876
  continue;
@@ -1415,7 +1417,10 @@ function labeledTriviaSpec(arms, minRepeats) {
1415
1417
  arms,
1416
1418
  minRepeats,
1417
1419
  visitor: commonLabeledTriviaVisitor({ arms, minRepeats }),
1418
- charArms: charArmsFor({ arms, minRepeats })
1420
+ charArms: charArmsFor({ arms, minRepeats }),
1421
+ rootKinds: void 0,
1422
+ rootLeaders: [],
1423
+ rootScan: null
1419
1424
  };
1420
1425
  }
1421
1426
  function matchArmAt(input, pos, arm, state) {
@@ -2831,8 +2836,50 @@ function loopScanner(arms) {
2831
2836
  function regexTriviaScanner(parser2) {
2832
2837
  const d = parser2._def.tag === "label" ? parser2._def.parser._def : parser2._def;
2833
2838
  if (d.tag !== "regex" || d.flags) return null;
2834
- const source = d.source;
2835
- 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
+ };
2836
2883
  }
2837
2884
  function bmpString(cps) {
2838
2885
  if (!cps) return null;
@@ -3185,6 +3232,7 @@ function matchesAt(sentinel, input, pos, ctx) {
3185
3232
  function captureError(ctx, error) {
3186
3233
  if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
3187
3234
  }
3235
+ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
3188
3236
 
3189
3237
  // src/combinators/adjacency.ts
3190
3238
  function adjacencyOf(p) {
@@ -4821,7 +4869,7 @@ function confirmedArityForDef(def) {
4821
4869
  severity: "warn",
4822
4870
  where: `node("${def.type ?? "<inferred>"}")`,
4823
4871
  subject,
4824
- 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`,
4825
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"
4826
4874
  });
4827
4875
  return null;
@@ -4978,7 +5026,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
4978
5026
  }
4979
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;
4980
5028
  const capturesTrivia = captureTrivia || (build ? buildReadsTrivia(def) : project === void 0);
4981
- const clonesState = build ? buildReadsState(def) : project === void 0;
5029
+ const readsState = build ? buildReadsState(def) : project === void 0;
4982
5030
  const hasOwnFields = parserHasOwnFields(combinator);
4983
5031
  const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
4984
5032
  const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
@@ -5019,12 +5067,11 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
5019
5067
  ctx._triviaCaptureMask = savedMask;
5020
5068
  const { children: children3, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
5021
5069
  if (!r.ok) return r;
5022
- const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
5070
+ const st = readsState ? ctx.state : void 0;
5023
5071
  const nodeType = def.type ?? missingInferredType();
5024
5072
  const cstOutput = cstOutputHost(ctx.build);
5025
5073
  const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
5026
- const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
5027
- 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 };
5028
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 };
5029
5076
  if (saved.buf !== void 0 || saved.ch !== void 0) {
5030
5077
  pushCstChild(ctx, built, rawEntry2);
@@ -5594,7 +5641,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
5594
5641
  if (!Number.isInteger(spec[2]) || spec[2] < 0) {
5595
5642
  throw new TypeError("table balanced lexical program has an invalid scan reference");
5596
5643
  }
5597
- const run = (_input, _pos, ctx, scan) => {
5644
+ const run2 = (_input, _pos, ctx, scan) => {
5598
5645
  if (scan === void 0) throw new TypeError("table balanced lexical program lost its scan body");
5599
5646
  const result = scan.parse(_input, _pos, ctx);
5600
5647
  if (!result.ok) {
@@ -5604,7 +5651,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
5604
5651
  }
5605
5652
  return result.span.end;
5606
5653
  };
5607
- return Object.assign(run, { scan: spec[2] });
5654
+ return Object.assign(run2, { scan: spec[2] });
5608
5655
  }
5609
5656
  if (spec[0] === 2) return fixedTreeRunner(spec.slice(2), matchers, classes, expected);
5610
5657
  if (spec[0] === 1) {
@@ -5732,6 +5779,12 @@ function scanRefs(s) {
5732
5779
  refs.push(...s.skip);
5733
5780
  return refs;
5734
5781
  }
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
+ }
5735
5788
  function expandCompact(p) {
5736
5789
  if ("code" in p) return p;
5737
5790
  return ownTableProgram({
@@ -5862,6 +5915,11 @@ function classHas2(cls, code) {
5862
5915
  for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
5863
5916
  return false;
5864
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
+ }
5865
5923
  function resolveDispatch(arms, cc) {
5866
5924
  const ascii = new Uint8Array(128);
5867
5925
  const hi = [];
@@ -7442,6 +7500,33 @@ var Encoder = class {
7442
7500
  return childrenOf2(def);
7443
7501
  }
7444
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
+ }
7445
7530
  /** A first set becomes a char class string, or −1 for `any`. */
7446
7531
  charClass(fs3) {
7447
7532
  if (fs3.kind !== "ranges" || fs3.ranges.length === 0) return -1;
@@ -7453,12 +7538,19 @@ var Encoder = class {
7453
7538
  this.ccIndex.set(spec, i);
7454
7539
  return i;
7455
7540
  }
7456
- /** Bind a guard only when its class is already part of the program. Repeat
7457
- * gating must not grow the compact artifact merely to avoid a failed call. */
7458
- existingCharClass(fs3) {
7459
- if (fs3.kind !== "ranges" || fs3.ranges.length === 0) return -1;
7460
- const hit = this.ccIndex.get(encodeClassSpec(fs3.ranges));
7461
- 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;
7462
7554
  }
7463
7555
  /**
7464
7556
  * Wrap one choice arm in its `autoNot` checks (`OP_REJECT`).
@@ -7763,6 +7855,12 @@ var Encoder = class {
7763
7855
  if (transactionalFirst[i] !== void 0) classes[i] = -1;
7764
7856
  }
7765
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
+ }
7766
7864
  const dispIdx = this.disp.length;
7767
7865
  this.disp.push(classes);
7768
7866
  let rollbackMask = kids.length > 31 ? -1 : 0;
@@ -7792,7 +7890,7 @@ var Encoder = class {
7792
7890
  case "oneOrMore": {
7793
7891
  const child = this.node(d.parser).ip;
7794
7892
  const rr = this.refResolver();
7795
- 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));
7796
7894
  const op = d.valueUnused ? OP_REPV : OP_REP;
7797
7895
  const min = d.tag === "many" ? 0 : d.min;
7798
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);
@@ -7816,7 +7914,7 @@ var Encoder = class {
7816
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);
7817
7915
  }
7818
7916
  case "optional": {
7819
- 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));
7820
7918
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(head);
7821
7919
  return head;
7822
7920
  }
@@ -7864,8 +7962,8 @@ var Encoder = class {
7864
7962
  // every CST node, so this is load-bearing, not reflection trivia.
7865
7963
  d.tags === void 0 ? -1 : this.constant(d.tags)
7866
7964
  );
7867
- if (matchesEmpty(d.parser)) return body;
7868
- 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);
7869
7967
  if (fs3.kind === "any") return body;
7870
7968
  const cls = this.charClass(fs3);
7871
7969
  if (cls < 0) return body;
@@ -7957,7 +8055,7 @@ var Encoder = class {
7957
8055
  // other engine reported `['not(literal)']`. It hid because `expected` is a
7958
8056
  // TOP-LEVEL field on `RunResult` and is NOT part of the identity digest.
7959
8057
  case "not":
7960
- 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)));
7961
8059
  case "guard":
7962
8060
  return this.emit(OP_GUARD, this.fn(d.predicate, d.predSrc ?? null), this.expected(["gate"]));
7963
8061
  case "adjacency":
@@ -7978,14 +8076,14 @@ var Encoder = class {
7978
8076
  return this.emit(OP_WITHCTX, this.constant(d.extra), this.node(d.parser).ip);
7979
8077
  }
7980
8078
  case "peek":
7981
- 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)));
7982
8080
  // A TRANSACTION IS A ROW. See `OP_ATTEMPT` for why the transparent
7983
8081
  // lowering was correct only for a choice arm.
7984
8082
  case "attempt": {
7985
8083
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
7986
8084
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
7987
- const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
7988
- 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;
7989
8087
  const cls = this.charClass(fs3);
7990
8088
  if (cls < 0) return inner;
7991
8089
  const e = deriveExpected(d.parser);
@@ -9710,7 +9808,10 @@ var EMITTED_PARAMS = [
9710
9808
  // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
9711
9809
  // old factories ignore them.
9712
9810
  "LEAD",
9713
- "triviaFieldCapture"
9811
+ "triviaFieldCapture",
9812
+ // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
9813
+ // factories ignore it.
9814
+ "skipDeferredTrivia"
9714
9815
  ];
9715
9816
  var RUNTIME_PRELUDE = `
9716
9817
  let _pfScan=null
@@ -9731,7 +9832,7 @@ function _skipTrivia(input,cur,ctx){
9731
9832
  const s=_pfScan
9732
9833
  if(s!==null&&ctx._triviaLog===undefined&&!(ctx.captureTrivia===true&&(ctx._cstBuf!==undefined||ctx._cstTriviaLog!==undefined)))return s(input,cur)
9733
9834
  if(s!==null)return skipTriviaScanned(s,input,cur,ctx)
9734
- if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
9835
+ if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
9735
9836
  return advanceTrivia(input,cur,ctx)
9736
9837
  }
9737
9838
  function _pushLeaf(ctx,value,s,e){pushCstLeaf(ctx,{_tag:'leaf',value,span:{start:s,end:e}})}
@@ -9938,11 +10039,11 @@ function rebuildPools(cc, fx, disp, plan) {
9938
10039
  })
9939
10040
  };
9940
10041
  }
9941
- function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
10042
+ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select) {
9942
10043
  const { code, k, fx, disp, dsp, triviaLabelled } = t;
9943
10044
  const swapLegal = !cfg.trackLines;
9944
10045
  const hostCst = cfg.hostCst;
9945
- const REC = prog.rec === 1 && cfg.tolerant;
10046
+ const REC2 = prog.rec === 1 && cfg.tolerant;
9946
10047
  if (cfg.coverage) throw new Unemittable("a coverage assembly");
9947
10048
  const roots = [...Object.values(prog.rules), ...extraIps];
9948
10049
  const reachable = [...reachableSites(code, roots)];
@@ -10013,7 +10114,7 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
10013
10114
  const op = code[ip];
10014
10115
  sinkBits.set(
10015
10116
  ip,
10016
- 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)
10017
10118
  );
10018
10119
  }
10019
10120
  const sinkKids = [];
@@ -10037,6 +10138,8 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
10037
10138
  };
10038
10139
  const bodies = [];
10039
10140
  const byIp = /* @__PURE__ */ new Map();
10141
+ const externals = [];
10142
+ const siteBytes = /* @__PURE__ */ new Map();
10040
10143
  const reached = /* @__PURE__ */ new Set();
10041
10144
  const prelude = [];
10042
10145
  const skipDefs = [];
@@ -10172,6 +10275,18 @@ return 1
10172
10275
  tokenDecisionRefs.set(dispatchIp, made);
10173
10276
  return made;
10174
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
+ }
10175
10290
  function skipFor(l) {
10176
10291
  if (l.tri < 0) return "_skipTrivia";
10177
10292
  const ki = l.tri;
@@ -10182,8 +10297,8 @@ return 1
10182
10297
  const nm = `_sk${skipPool.size}`;
10183
10298
  skipPool.set(key, nm);
10184
10299
  if (!hasScan) {
10185
- skipDefs.push(l.buf ? `function ${nm}(input,cur,ctx){return commitTriviaScan(scanTriviaCompact(input,cur,ctx))}` : `function ${nm}(input,cur,ctx){
10186
- 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)
10187
10302
  return advanceTrivia(input,cur,ctx)}`);
10188
10303
  return nm;
10189
10304
  }
@@ -10228,9 +10343,17 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
10228
10343
  const target = resolveAlias(ip);
10229
10344
  const done = byIp.get(target);
10230
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
+ }
10231
10352
  const fname = `_pf${target}`;
10232
10353
  byIp.set(target, fname);
10233
- bodies.push(lower(target, fname));
10354
+ const body = lower(target, fname);
10355
+ siteBytes.set(target, body.length);
10356
+ bodies.push(body);
10234
10357
  return fname;
10235
10358
  }
10236
10359
  function resolveAlias(ip) {
@@ -10370,14 +10493,28 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
10370
10493
  return `${head}EC.e=pos;return ''}`;
10371
10494
  case OP_GATE: {
10372
10495
  const child = link(code[ip + 2]);
10373
- const resolved = t.cc[code[ip + 1]];
10374
- const ci = classes.push([resolved]) - 1;
10375
- classPlan.push([code[ip + 1]]);
10376
- const cls = hoist("cl", `CLS[${ci}][0]`);
10377
10496
  const xf = fxRef(code[ip + 3]);
10378
10497
  return `${head}
10379
- 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}
10380
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}
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}
10381
10518
  }`;
10382
10519
  }
10383
10520
  case OP_XFORM: {
@@ -10435,7 +10572,7 @@ if(v!==FAIL)return v
10435
10572
  const err={_tag:'parseError',span:${prog.lines === 1 ? "spanLines(ctx,pos,pos)" : "{start:pos,end:pos}"},expected:${xf}}
10436
10573
  const es=ctx._errors
10437
10574
  if(es!==undefined)es.push(err)
10438
- ${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
10439
10576
  EC.e=pos
10440
10577
  return err
10441
10578
  }`;
@@ -10556,7 +10693,7 @@ return v
10556
10693
  }
10557
10694
  case OP_LEX_PROGRAM: {
10558
10695
  const programId = code[ip + 1];
10559
- const run = hoist("lexProgram", `LEXPROG[${programId}]`);
10696
+ const run2 = hoist("lexProgram", `LEXPROG[${programId}]`);
10560
10697
  const scanId = t.lexPrograms[programId].scan;
10561
10698
  if (scanId !== void 0) return `${head}
10562
10699
  const sTri=ctx.trivia,sKinds=ctx.triviaKindLabels,sScan=_pfScan
@@ -10574,7 +10711,7 @@ ctx._cstTriviaLog=undefined
10574
10711
  ctx._triviaLog=undefined
10575
10712
  ctx._rootTriviaLog=undefined
10576
10713
  let e
10577
- try{e=${run}(input,pos,ctx,SCANS[${scanId}])}finally{
10714
+ try{e=${run2}(input,pos,ctx,SCANS[${scanId}])}finally{
10578
10715
  _pfScan=sScan
10579
10716
  ctx.trivia=sTri
10580
10717
  ctx.triviaKindLabels=sKinds
@@ -10593,7 +10730,7 @@ EC.e=e
10593
10730
  return v
10594
10731
  }`;
10595
10732
  return `${head}
10596
- const e=${run}(input,pos,ctx)
10733
+ const e=${run2}(input,pos,ctx)
10597
10734
  if(e<0)return FAIL
10598
10735
  const v=input.slice(pos,e)
10599
10736
  if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,{_tag:'leaf',value:v,span:{start:pos,end:e}})
@@ -10604,7 +10741,7 @@ return v
10604
10741
  /* ── transaction ─────────────────────────────────────────────────────── */
10605
10742
  case OP_ATTEMPT: {
10606
10743
  const child = link(code[ip + 1]);
10607
- const clean = !REC && failureRollbackClean(prog, ip);
10744
+ const clean = !REC2 && failureRollbackClean(prog, ip);
10608
10745
  const p = tmp();
10609
10746
  return `${head}
10610
10747
  ${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
@@ -10633,7 +10770,7 @@ return FAIL
10633
10770
  const xf = fxRef(code[ip + 2]);
10634
10771
  const p = tmp();
10635
10772
  return `${head}
10636
- ${emitMark(p, L.buf, L.raw, sinks)}
10773
+ ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${emitMark(p, L.buf, L.raw, sinks)}
10637
10774
  const v=${child}(input,pos,ctx)
10638
10775
  ${emitRollback(p, L.buf, L.raw, sinks)}
10639
10776
  if(v===FAIL){EC.e=pos;return null}
@@ -10647,7 +10784,7 @@ return FAIL
10647
10784
  const xf = fxRef(code[ip + 2]);
10648
10785
  const p = tmp();
10649
10786
  return `${head}
10650
- ${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)}
10651
10788
  const v=${child}(input,pos,ctx)
10652
10789
  ${emitRollback(p, L.buf, L.raw, sinks)}
10653
10790
  if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
@@ -10657,10 +10794,10 @@ return null
10657
10794
  }
10658
10795
  case OP_OPT: {
10659
10796
  const child = link(code[ip + 1]);
10660
- const clean = !REC && failureRollbackClean(prog, ip);
10797
+ const clean = !REC2 && failureRollbackClean(prog, ip);
10661
10798
  const p = tmp();
10662
10799
  return `${head}
10663
- ${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)}
10664
10801
  ctx._fc=false
10665
10802
  const v=${child}(input,pos,ctx)
10666
10803
  if(v===FAIL){
@@ -10687,15 +10824,15 @@ return v
10687
10824
  const childIp = code[base + i];
10688
10825
  kids.push(code[childIp] === OP_ADJ ? "" : link(childIp));
10689
10826
  }
10690
- const sy = REC ? `${tmp()}sy` : "";
10827
+ const sy = REC2 ? `${tmp()}sy` : "";
10691
10828
  const pub = (i) => {
10692
10829
  const s = sentRef(code[base + n + i]);
10693
10830
  return s === "undefined" ? `ctx._sync=${sy}` : `ctx._sync=${s}??${sy}`;
10694
10831
  };
10695
10832
  const parts = [head];
10696
- if (REC) parts.push(`const ${sy}=ctx._sync`, "try{", pub(0));
10833
+ if (REC2) parts.push(`const ${sy}=ctx._sync`, "try{", pub(0));
10697
10834
  parts.push(`const v0=${kids[0]}(input,pos,ctx)`, "if(v0===FAIL)return FAIL");
10698
- const close = () => REC ? `${parts.join("\n")}
10835
+ const close = () => REC2 ? `${parts.join("\n")}
10699
10836
  }finally{ctx._sync=${sy}}
10700
10837
  }` : `${parts.join("\n")}
10701
10838
  }`;
@@ -10708,7 +10845,7 @@ return v
10708
10845
  for (let i = 1; i < n; i++) {
10709
10846
  const vn = `v${i}`;
10710
10847
  parts.push(`let ${vn}`);
10711
- if (REC) parts.push(pub(i));
10848
+ if (REC2) parts.push(pub(i));
10712
10849
  const childIp = code[base + i];
10713
10850
  if (code[childIp] === OP_ADJ) {
10714
10851
  const negated = code[childIp + 1] === 1;
@@ -10741,7 +10878,7 @@ return v
10741
10878
  const asc = hoist("as", `DISP[${di}].ascii`);
10742
10879
  const hiArr = table.hi;
10743
10880
  const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join("\n");
10744
- const directAscii = staticBuild && table.hi.length === 0;
10881
+ const directAscii = table.hi.length === 0;
10745
10882
  return `${head}
10746
10883
  const c=${directAscii ? "input.charCodeAt(pos)" : "lead(input,pos)"}
10747
10884
  let arm=-1
@@ -10768,7 +10905,7 @@ return FAIL
10768
10905
  const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]));
10769
10906
  const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null);
10770
10907
  const maskable = n <= 32;
10771
- const directAsciiMask = staticBuild && maskable && gates.every(isAsciiOnlyClass);
10908
+ const directAsciiMask = maskable && gates.every(isAsciiOnlyClass);
10772
10909
  const gateRefs = directAsciiMask ? [] : Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
10773
10910
  const tokenCandidate = tokenChoiceCandidates.get(ip);
10774
10911
  const pretests = Array.from({ length: n }, (_, i) => {
@@ -10793,7 +10930,7 @@ return FAIL
10793
10930
  if (maskable) maskPlan.push(~di);
10794
10931
  const p = tmp();
10795
10932
  const encodedRollbackMask = choiceRollbackMask(prog, ip) ?? -1;
10796
- const rollbackMask = REC ? -1 : encodedRollbackMask;
10933
+ const rollbackMask = REC2 ? -1 : encodedRollbackMask;
10797
10934
  const hasRollback = rollbackMask !== 0;
10798
10935
  const startFailureExact = arms.every((_arm, i) => {
10799
10936
  const terminal = leadingScalarTerminal(code, code[base + i], 0, false);
@@ -10810,7 +10947,7 @@ return FAIL
10810
10947
  if (choiceCannotCommit(prog, ip) && !startFailureExact) {
10811
10948
  const deepAt = Array.from({ length: n }, () => tmp());
10812
10949
  const deepFx = Array.from({ length: n }, () => tmp());
10813
- const remember = (i, at, expectedSet) => `${deepAt[i]}=${at};${deepFx[i]}=${expectedSet}`;
10950
+ const remember2 = (i, at, expectedSet) => `${deepAt[i]}=${at};${deepFx[i]}=${expectedSet}`;
10814
10951
  const lazyArms = (ascii) => arms.map((arm, i) => {
10815
10952
  const pretest = pretests[i];
10816
10953
  const decision = pretest?.token === void 0 && pretest?.sequence === void 0 ? "" : tmp();
@@ -10822,13 +10959,13 @@ return FAIL
10822
10959
  if(${decision}===0){
10823
10960
  ctx._fc=false
10824
10961
  {const at=_pfTokEnd
10825
- ${remember(i, "at", pretest.token.expected)}}
10962
+ ${remember2(i, "at", pretest.token.expected)}}
10826
10963
  }`;
10827
10964
  const sequenceMiss = pretest?.sequence === void 0 ? "" : `
10828
10965
  else if(${ascii ? `(bits&${1 << i})!==0&&` : ""}${decision}<0){
10829
10966
  ctx._fc=false
10830
10967
  {const at=_pfSeqEnd
10831
- ${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))}}
10832
10969
  }`;
10833
10970
  return `${decision === "" ? "" : `let ${decision}=${pretest?.sequence === void 0 ? -1 : 0}
10834
10971
  `}if(${gate2}){
@@ -10836,7 +10973,7 @@ ctx._fc=false
10836
10973
  {const v=${arm}(input,pos,ctx)
10837
10974
  if(v!==FAIL)return v}
10838
10975
  {const at=ctx._fe??pos
10839
- ${remember(i, "at", "ctx._fx")}}
10976
+ ${remember2(i, "at", "ctx._fx")}}
10840
10977
  ${rollbackFor(i)}
10841
10978
  }${routeMiss}${sequenceMiss}`;
10842
10979
  }).join("\n");
@@ -10949,7 +11086,7 @@ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
10949
11086
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
10950
11087
  ${startFailureExact ? "" : "let prev=0"}
10951
11088
  ${maskArms}
10952
- ${startFailureExact ? "" : `if(best===pos)acc=${catchName}(${n},prev,acc)`}
11089
+ ${startFailureExact ? "" : `if(best===pos&&prev!==0)acc=${catchName}(${n},prev,acc)`}
10953
11090
  ctx._fe=pos;ctx._fx=${startFailureExact ? `best===pos?${choiceFx}:acc??${choiceFx}` : `acc??${choiceFx}`}
10954
11091
  return FAIL
10955
11092
  }
@@ -11041,14 +11178,14 @@ return [key,v]
11041
11178
  const collect = op === OP_REP;
11042
11179
  let itemCls;
11043
11180
  let itemAscii = false;
11044
- if (!REC) {
11181
+ if (!REC2) {
11045
11182
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
11046
11183
  if (itemClassIndex >= 0) {
11047
11184
  const resolved = t.cc[itemClassIndex];
11048
11185
  const ci = classes.push([resolved]) - 1;
11049
11186
  classPlan.push([itemClassIndex]);
11050
11187
  itemCls = hoist("ri", `CLS[${ci}][0]`);
11051
- itemAscii = staticBuild && isAsciiOnlyClass(resolved);
11188
+ itemAscii = isAsciiOnlyClass(resolved);
11052
11189
  }
11053
11190
  }
11054
11191
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
@@ -11089,11 +11226,11 @@ ${p}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
11089
11226
  ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
11090
11227
  }`;
11091
11228
  const my = `${p}my`;
11092
- const recFx = REC ? fxRef(code[ip + 6]) : "";
11093
- 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";
11094
11231
  const recSent = sep2 === void 0 ? my : sepSent === "undefined" ? `orSentinel(${my},undefined)` : `orSentinel(${sepSent}??${my},${sepSent}===undefined?undefined:${my})`;
11095
11232
  const recGate = sep2 !== void 0 || min === 0 ? "" : `count>=${min}&&`;
11096
- 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)){
11097
11234
  ${sep2 === void 0 ? `${rb}
11098
11235
  ` : ""}const rr=recoverScan(input,itemStart,ctx,${recSent},${recFx})
11099
11236
  ${collect ? "out.push(rr.error)\n" : ""}captureError(ctx,rr.error)
@@ -11104,7 +11241,7 @@ continue
11104
11241
  `;
11105
11242
  return `${head}
11106
11243
  const out=${collect ? "[]" : "undefined"}
11107
- ${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
11108
11245
  ` : ""}${itemCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
11109
11246
  `}let cur=pos
11110
11247
  let count=0
@@ -11143,7 +11280,7 @@ ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
11143
11280
  `}ctx._fc=false
11144
11281
  const v=${child}(input,itemStart,ctx)
11145
11282
  if(v===FAIL){
11146
- ${REC ? `if(ctx._fc===true){
11283
+ ${REC2 ? `if(ctx._fc===true){
11147
11284
  ${rb}
11148
11285
  return FAIL
11149
11286
  }
@@ -11274,7 +11411,7 @@ return nd
11274
11411
  const keepChildren = !structural || cfg.hostReadsChildren !== false || collapse || unwrap;
11275
11412
  const omitsRaw = !hostCst && build !== void 0 && proj < 0 && (flags & 2) !== 0;
11276
11413
  const ty = q(type);
11277
- const stArg = readsState ? "st" : "(ctx.state!==undefined?Object.assign({},ctx.state):undefined)";
11414
+ const stArg = "ctx.state";
11278
11415
  const hostCall = `_pfHost(${ty},hostKids,fieldMap,span,rawKids,tlog,${stArg},${tags})`;
11279
11416
  let value;
11280
11417
  if (proj >= 0) {
@@ -11335,7 +11472,7 @@ ctx.captureTrivia=sCap
11335
11472
  if(v===FAIL)return FAIL
11336
11473
  const end=EC.e
11337
11474
  const span=${tracked ? "spanLines(ctx,pos,end)" : "{start:pos,end}"}
11338
- const st=${readsState ? "(ctx.state!==undefined?Object.assign({},ctx.state):undefined)" : "undefined"}
11475
+ const st=${readsState ? "ctx.state" : "undefined"}
11339
11476
  let nd
11340
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]
11341
11478
  else ` : ""}{${value}}
@@ -11348,21 +11485,51 @@ return nd
11348
11485
  throw new Unemittable(`OP_${OP_NAMES[op] ?? String(op)}`);
11349
11486
  }
11350
11487
  }
11488
+ const served = (ip) => select === void 0 || select.has(resolveAlias(ip));
11351
11489
  const ruleEntries = [];
11352
11490
  for (const [rname, entryIp] of Object.entries(prog.rules)) {
11491
+ if (!served(entryIp)) continue;
11353
11492
  const target = link(entryIp);
11354
11493
  const rn = `_r_${rname.replace(/[^A-Za-z0-9_$]/g, "_")}`;
11355
11494
  bodies.push(`const ${rn}=${target}`);
11356
11495
  ruleEntries.push(`${q(rname)}:${rn}`);
11357
11496
  }
11358
11497
  const extra = [];
11359
- 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
+ `;
11360
11527
  const source = `${RUNTIME_PRELUDE}${needsNoRawPrelude ? NO_RAW_RUNTIME_PRELUDE : ""}
11361
11528
  ${prelude.join("\n")}
11362
11529
  ${skipDefs.join("\n")}
11363
11530
  ${choiceDefs.join("\n")}
11364
11531
  ${bodies.join("\n")}
11365
- function _begin(ctx){
11532
+ ${externalDefs}function _begin(ctx){
11366
11533
  const host=ctx.build
11367
11534
  if(_pfDepth>0)_pfFrames.push([_pfScan,_pfHost,EC.e])
11368
11535
  _pfDepth++
@@ -11389,7 +11556,7 @@ pieces:{${ruleEntries.join(",")}},
11389
11556
  byIp:{${extra.join(",")}},
11390
11557
  end:function(){return EC.e},
11391
11558
  begin:_begin,
11392
- finish:_finish
11559
+ finish:_finish${hybrid}
11393
11560
  }`;
11394
11561
  return {
11395
11562
  source,
@@ -11397,6 +11564,7 @@ finish:_finish
11397
11564
  masks,
11398
11565
  classes,
11399
11566
  armExpected,
11567
+ siteBytes,
11400
11568
  plan: { classes: classPlan, armExpected: armExpectedPlan, masks: maskPlan }
11401
11569
  };
11402
11570
  }
@@ -11495,20 +11663,20 @@ function lines(prefix, body, options = {}) {
11495
11663
  const c = ctx;
11496
11664
  const probe = createDetachedParseContext(false, ctx.state);
11497
11665
  probe.lineView = c.lineView;
11498
- const run = [];
11666
+ const run2 = [];
11499
11667
  const parts = [];
11500
11668
  let offset = 0;
11501
11669
  for (let start = pos; start < input.length || start === pos; ) {
11502
11670
  let end2 = input.indexOf("\n", start);
11503
11671
  if (end2 < 0) end2 = input.length;
11504
11672
  probe._columnsSplit = void 0;
11505
- const head = (run.length === 0 ? opening : prefix).parse(input, start, probe);
11673
+ const head = (run2.length === 0 ? opening : prefix).parse(input, start, probe);
11506
11674
  let content;
11507
11675
  let joined;
11508
11676
  if (head.ok && head.span.end <= end2) {
11509
11677
  content = head.span.end;
11510
11678
  joined = "prefix";
11511
- } else if (run.length === 0) {
11679
+ } else if (run2.length === 0) {
11512
11680
  return { ok: false, expected: head.ok ? ["line prefix"] : head.expected, span: { start, end: start } };
11513
11681
  } else if (lazy !== void 0 && lazy.parse(input, start, probe).ok) {
11514
11682
  content = start;
@@ -11517,7 +11685,7 @@ function lines(prefix, body, options = {}) {
11517
11685
  const mark = probe._columnsSplit;
11518
11686
  const here = columnAt(input, content, c);
11519
11687
  const split = joined === "prefix" && mark !== void 0 && mark.at === content ? mark.split : here.split;
11520
- run.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
11688
+ run2.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
11521
11689
  const text = input.slice(content, end2);
11522
11690
  const newline = end2 < input.length;
11523
11691
  parts.push(newline ? `${text}
@@ -11526,11 +11694,11 @@ function lines(prefix, body, options = {}) {
11526
11694
  start = end2 + 1;
11527
11695
  if (!newline) break;
11528
11696
  }
11529
- const view = { lines: run, parent: c.lineView };
11697
+ const view = { lines: run2, parent: c.lineView };
11530
11698
  const saved = c.lineView, starts = c._lineStarts, lineIndex = c._lineIndex, scanned = c._lineScannedTo, probing = c._probe;
11531
11699
  const errors = c._errors?.length ?? 0;
11532
11700
  let cursor = -1;
11533
- if (probing !== void 0) for (const line2 of run) {
11701
+ if (probing !== void 0) for (const line2 of run2) {
11534
11702
  if (probing.offset >= line2.source && probing.offset <= line2.end) {
11535
11703
  cursor = line2.offset + probing.offset - line2.source;
11536
11704
  break;
@@ -11563,8 +11731,8 @@ function lines(prefix, body, options = {}) {
11563
11731
  }
11564
11732
  const stop = result.span.end;
11565
11733
  const index = lineIndexAt(view, stop);
11566
- const line = run[index];
11567
- const end = stop === line.offset && index > 0 ? run[index - 1].end + 1 : stop - line.offset > line.end - line.source ? line.end + 1 : line.source + (stop - line.offset);
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);
11568
11736
  recordLineRangeFromContext(c, input, pos, end);
11569
11737
  return { ok: true, value: result.value, span: { start: pos, end } };
11570
11738
  }
@@ -11827,6 +11995,70 @@ function escapeClassChar(ch2) {
11827
11995
  return ch2.replace(/[\\\]^-]/g, "\\$&");
11828
11996
  }
11829
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
+
11830
12062
  // src/table/run-support.ts
11831
12063
  function lineCol(ctx, offset) {
11832
12064
  const starts = ctx._lineStarts ?? [0];
@@ -11863,6 +12095,19 @@ function lead(input, pos) {
11863
12095
  // src/cst/host-mode.ts
11864
12096
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
11865
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
+ }
11866
12111
 
11867
12112
  // src/grammar-metadata.ts
11868
12113
  var GRAMMAR_COVERAGE_DEFINITIONS = /* @__PURE__ */ Symbol.for("parseman.grammarCoverageDefinitions");
@@ -12008,7 +12253,7 @@ function assemble(t, prog, cfg) {
12008
12253
  const triviaScan = t.triviaScan;
12009
12254
  const swapLegal = !cfg.trackLines;
12010
12255
  const hostCst = cfg.hostCst;
12011
- const REC = prog.rec === 1 && cfg.tolerant;
12256
+ const REC2 = prog.rec === 1 && cfg.tolerant;
12012
12257
  const sentinels = /* @__PURE__ */ new Map();
12013
12258
  function sentinelFor(cls) {
12014
12259
  if (cls < 0) return void 0;
@@ -12023,6 +12268,9 @@ function assemble(t, prog, cfg) {
12023
12268
  const EC = newEndCell();
12024
12269
  const scalarRecognizers = [];
12025
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
+ }
12026
12274
  function scalarFor(child) {
12027
12275
  const spec = code[child + 1];
12028
12276
  let recognize = scalarRecognizers[spec];
@@ -12032,15 +12280,23 @@ function assemble(t, prog, cfg) {
12032
12280
  }
12033
12281
  return recognize;
12034
12282
  }
12035
- const labelExtraIps = [];
12036
- for (const s of prog.scans ?? []) for (const r of scanRefs(s)) labelExtraIps.push(r[0]);
12037
- for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0]);
12283
+ const labelExtraIps = assemblyExtraIps(prog);
12038
12284
  const closureLabels = computeSiteLabels(
12039
12285
  code,
12040
12286
  [...Object.values(prog.rules), ...labelExtraIps],
12041
12287
  hostCst
12042
12288
  );
12043
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
+ }
12044
12300
  const memo = /* @__PURE__ */ new Map();
12045
12301
  const reached = /* @__PURE__ */ new Set();
12046
12302
  function pushLeaf(ctx, value, s, e) {
@@ -12332,9 +12588,7 @@ function assemble(t, prog, cfg) {
12332
12588
  return s(input, cur);
12333
12589
  }
12334
12590
  if (s !== null) return skipTriviaScanned(s, input, cur, ctx);
12335
- if (needsDeferredTriviaCommit(ctx)) {
12336
- return commitTriviaScan(scanTriviaCompact(input, cur, ctx));
12337
- }
12591
+ if (needsDeferredTriviaCommit(ctx)) return skipDeferredTrivia(input, cur, ctx);
12338
12592
  return advanceTrivia(input, cur, ctx);
12339
12593
  }
12340
12594
  let TERMV;
@@ -12504,7 +12758,7 @@ function assemble(t, prog, cfg) {
12504
12758
  if (next !== void 0) {
12505
12759
  return next(input, pos, ctx, bits, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best);
12506
12760
  }
12507
- if (best === pos) acc = through(total, prev, acc);
12761
+ if (best === pos && prev !== 0) acc = through(total, prev, acc);
12508
12762
  ctx._fe = pos;
12509
12763
  ctx._fx = acc ?? choiceFx;
12510
12764
  return FAIL;
@@ -12587,7 +12841,7 @@ function assemble(t, prog, cfg) {
12587
12841
  if (next !== void 0) {
12588
12842
  return next(input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best);
12589
12843
  }
12590
- if (best === pos) acc = through(total, prev, acc);
12844
+ if (best === pos && prev !== 0) acc = through(total, prev, acc);
12591
12845
  ctx._fe = pos;
12592
12846
  ctx._fx = acc ?? choiceFx;
12593
12847
  return FAIL;
@@ -12666,6 +12920,13 @@ function assemble(t, prog, cfg) {
12666
12920
  if (done !== void 0) return done;
12667
12921
  const flight = inFlight.get(ip);
12668
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
+ }
12669
12930
  let target;
12670
12931
  const fwd = (input, pos, ctx) => target(input, pos, ctx);
12671
12932
  const holder = { fwd, set: (p) => {
@@ -12673,7 +12934,7 @@ function assemble(t, prog, cfg) {
12673
12934
  } };
12674
12935
  inFlight.set(ip, holder);
12675
12936
  const op = code[ip];
12676
- 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);
12677
12938
  inFlight.delete(ip);
12678
12939
  holder.set(piece);
12679
12940
  memo.set(ip, piece);
@@ -13014,7 +13275,7 @@ function assemble(t, prog, cfg) {
13014
13275
  const child = link(code[ip + 1]);
13015
13276
  const xf = fx[code[ip + 2]];
13016
13277
  const expectSpan = (ctx, pos) => prog.lines === 1 ? spanLines(ctx, pos, pos) : { start: pos, end: pos };
13017
- if (REC) {
13278
+ if (REC2) {
13018
13279
  return (input, pos, ctx) => {
13019
13280
  const v = child(input, pos, ctx);
13020
13281
  if (v !== FAIL) return v;
@@ -13210,7 +13471,12 @@ function assemble(t, prog, cfg) {
13210
13471
  }
13211
13472
  const child = link(code[ip + 1]);
13212
13473
  const xf = fx[code[ip + 2]];
13474
+ const gate2 = lookaheadClass(code[ip + 1]);
13213
13475
  return (input, pos, ctx) => {
13476
+ if (gate2 !== null && !classHas2(gate2, lead(input, pos))) {
13477
+ EC.e = pos;
13478
+ return null;
13479
+ }
13214
13480
  const need = markCst(ctx);
13215
13481
  const mRaw = MRAW;
13216
13482
  const mTl = MTL;
@@ -13233,7 +13499,13 @@ function assemble(t, prog, cfg) {
13233
13499
  case OP_PEEK: {
13234
13500
  const child = link(code[ip + 1]);
13235
13501
  const xf = fx[code[ip + 2]];
13502
+ const gate2 = lookaheadClass(code[ip + 1]);
13236
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
+ }
13237
13509
  const need = markCst(ctx);
13238
13510
  const mRaw = MRAW;
13239
13511
  const mTl = MTL;
@@ -13255,7 +13527,12 @@ function assemble(t, prog, cfg) {
13255
13527
  }
13256
13528
  case OP_OPT: {
13257
13529
  const child = link(code[ip + 1]);
13530
+ const gate2 = lookaheadClass(code[ip + 1]);
13258
13531
  return (input, pos, ctx) => {
13532
+ if (gate2 !== null && !classHas2(gate2, lead(input, pos))) {
13533
+ EC.e = pos;
13534
+ return null;
13535
+ }
13259
13536
  const need = markCst(ctx);
13260
13537
  const mRaw = MRAW;
13261
13538
  const mTl = MTL;
@@ -13353,8 +13630,8 @@ function assemble(t, prog, cfg) {
13353
13630
  };
13354
13631
  }
13355
13632
  case OP_LEX_PROGRAM: {
13356
- const run = t.lexPrograms[code[ip + 1]];
13357
- const scanId = run.scan;
13633
+ const run2 = t.lexPrograms[code[ip + 1]];
13634
+ const scanId = run2.scan;
13358
13635
  if (scanId !== void 0) {
13359
13636
  return (input, pos, ctx) => {
13360
13637
  const sTrivia = ctx.trivia, sKinds = ctx.triviaKindLabels;
@@ -13375,7 +13652,7 @@ function assemble(t, prog, cfg) {
13375
13652
  ctx._rootTriviaLog = void 0;
13376
13653
  let end;
13377
13654
  try {
13378
- end = run(input, pos, ctx, scans[scanId]);
13655
+ end = run2(input, pos, ctx, scans[scanId]);
13379
13656
  } finally {
13380
13657
  SCAN = sScan;
13381
13658
  ctx.trivia = sTrivia;
@@ -13396,7 +13673,7 @@ function assemble(t, prog, cfg) {
13396
13673
  };
13397
13674
  }
13398
13675
  return (input, pos, ctx) => {
13399
- const end = run(input, pos, ctx);
13676
+ const end = run2(input, pos, ctx);
13400
13677
  if (end < 0) return FAIL;
13401
13678
  const value = input.slice(pos, end);
13402
13679
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: { start: pos, end } });
@@ -13498,7 +13775,7 @@ function assemble(t, prog, cfg) {
13498
13775
  return next === void 0 ? cur : next(input, cur, ctx, values, inherited);
13499
13776
  };
13500
13777
  };
13501
- if (REC) {
13778
+ if (REC2) {
13502
13779
  const first2 = link(code[base]);
13503
13780
  const firstSync = sentinelFor(code[base + n]);
13504
13781
  const runnerAt = (i) => {
@@ -13967,7 +14244,7 @@ function assemble(t, prog, cfg) {
13967
14244
  if (n === 2 || n === 3) {
13968
14245
  const a0 = link(code[base]), a1 = link(code[base + 1]);
13969
14246
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
13970
- const predecide = !REC && !cfg.probe && !cfg.coverage && !cfg.trackLines;
14247
+ const predecide = !REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines;
13971
14248
  const p0ip = predecide ? leadingScalarTerminal(code, code[base], 2, true, true) : -1;
13972
14249
  const p1ip = predecide ? leadingScalarTerminal(code, code[base + 1], 2, true, true) : -1;
13973
14250
  const p2ip = predecide && n === 3 ? leadingScalarTerminal(code, code[base + 2], 2, true, true) : -1;
@@ -14062,6 +14339,11 @@ function assemble(t, prog, cfg) {
14062
14339
  }
14063
14340
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14064
14341
  }
14342
+ if (prev === 0) {
14343
+ ctx._fe = pos;
14344
+ ctx._fx = choiceFx;
14345
+ return FAIL;
14346
+ }
14065
14347
  if (best === pos && prev < 1) acc = accSet(e0, acc);
14066
14348
  if (best === pos && prev < 2) acc = accSet(e1, acc);
14067
14349
  if (best === pos && n === 3 && prev < 3) acc = accSet(e2, acc);
@@ -14192,7 +14474,7 @@ function assemble(t, prog, cfg) {
14192
14474
  const masked = bindMasked(0, EMPTY_EXPECTED_PREFIX);
14193
14475
  const pBit = [];
14194
14476
  const pRec = [];
14195
- if (!REC && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
14477
+ if (!REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
14196
14478
  for (let i = 0; i < n; i++) {
14197
14479
  for (const terminal of largeChoiceLiteralPretest(code, k, code[base + i]) ?? []) {
14198
14480
  pBit.push(1 << i);
@@ -14370,7 +14652,7 @@ function assemble(t, prog, cfg) {
14370
14652
  return (input, pos, ctx) => {
14371
14653
  if (!pred(ctx.state)) {
14372
14654
  ctx._fc = false;
14373
- ctx._fe = pos;
14655
+ ctx._fe = -1;
14374
14656
  ctx._fx = xf;
14375
14657
  return FAIL;
14376
14658
  }
@@ -14428,10 +14710,10 @@ function assemble(t, prog, cfg) {
14428
14710
  const itemFx = reportItem ? fx[code[ip + 6]] : EMPTY_FX2;
14429
14711
  const collect = op === OP_REP;
14430
14712
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
14431
- const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
14713
+ const itemClass = !REC2 && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
14432
14714
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
14433
14715
  const skipBeforeFirst = sepIp < 0 && min === 0;
14434
- if (REC) {
14716
+ if (REC2) {
14435
14717
  const itemFx2 = fx[code[ip + 6]];
14436
14718
  const sepSent = sentinelFor(code[ip + 7]);
14437
14719
  return (input, pos, ctx) => {
@@ -14776,7 +15058,7 @@ function assemble(t, prog, cfg) {
14776
15058
  if (v === FAIL) return FAIL;
14777
15059
  const end = EC.e;
14778
15060
  const span = tracked ? spanLines(ctx, pos, end) : { start: pos, end };
14779
- const st = readsState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
15061
+ const st = readsState ? ctx.state : void 0;
14780
15062
  let nd;
14781
15063
  if (unwrap && kids.length === 1) {
14782
15064
  nd = unwrapChild(kids[0]);
@@ -14789,10 +15071,10 @@ function assemble(t, prog, cfg) {
14789
15071
  ) {
14790
15072
  nd = kids[0];
14791
15073
  } else if (proj >= 0) {
14792
- 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);
14793
15075
  } else if (build !== void 0) {
14794
15076
  if (hostCst && host !== void 0) {
14795
- 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);
14796
15078
  } else {
14797
15079
  nd = build(kids, fieldMap, span, rawKids, captureWide ? tlog : EMPTY_TL2, st);
14798
15080
  }
@@ -14843,9 +15125,7 @@ function assemble(t, prog, cfg) {
14843
15125
  let emitRefusal;
14844
15126
  let emitReached;
14845
15127
  {
14846
- const extraIps = [];
14847
- for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
14848
- for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
15128
+ const extraIps = assemblyExtraIps(prog);
14849
15129
  const roots = [...Object.values(prog.rules), ...extraIps];
14850
15130
  for (const ip of reachableSites(code, roots)) {
14851
15131
  if (code[ip] === OP_DISPATCH) {
@@ -14926,7 +15206,7 @@ function assemble(t, prog, cfg) {
14926
15206
  lead,
14927
15207
  asciiFoldKey,
14928
15208
  ROUTED_FX,
14929
- REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
15209
+ REC2 ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
14930
15210
  matchesAt,
14931
15211
  recoverScan,
14932
15212
  orSentinel,
@@ -14938,9 +15218,11 @@ function assemble(t, prog, cfg) {
14938
15218
  adjacencyHolds,
14939
15219
  t.lexPrograms,
14940
15220
  LEAD,
14941
- triviaFieldCapture
15221
+ triviaFieldCapture,
15222
+ skipDeferredTrivia
14942
15223
  );
14943
15224
  emitReached = new Set(pre.reached);
15225
+ if (emitted.ext !== void 0) HYBRID = emitted;
14944
15226
  }
14945
15227
  try {
14946
15228
  if (emitted !== void 0) throw new ServedByBuild();
@@ -15010,7 +15292,7 @@ function assemble(t, prog, cfg) {
15010
15292
  // — a strict one emits no reader — and through the SAME `sentinelFor` memo
15011
15293
  // the closure pieces use, so both engines resync to the same combinator
15012
15294
  // object rather than to two separately constructed ones.
15013
- REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
15295
+ REC2 ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
15014
15296
  matchesAt,
15015
15297
  recoverScan,
15016
15298
  orSentinel,
@@ -15022,7 +15304,8 @@ function assemble(t, prog, cfg) {
15022
15304
  adjacencyHolds,
15023
15305
  t.lexPrograms,
15024
15306
  LEAD,
15025
- triviaFieldCapture
15307
+ triviaFieldCapture,
15308
+ skipDeferredTrivia
15026
15309
  );
15027
15310
  emitReached = em.reached;
15028
15311
  } catch (e) {
@@ -15033,7 +15316,7 @@ function assemble(t, prog, cfg) {
15033
15316
  }
15034
15317
  function pieceAt(ip) {
15035
15318
  const em = emitted;
15036
- if (em !== void 0) {
15319
+ if (em !== void 0 && HYBRID === void 0) {
15037
15320
  const p = em.byIp[ip];
15038
15321
  if (p === void 0) throw new Error(`table emitter: no emitted body for site ${ip}`);
15039
15322
  return p;
@@ -15063,6 +15346,37 @@ function assemble(t, prog, cfg) {
15063
15346
  }
15064
15347
  const scans = scansArr;
15065
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
+ }
15066
15380
  if (emitted !== void 0) {
15067
15381
  const em = emitted;
15068
15382
  return {
@@ -15098,6 +15412,26 @@ function assemble(t, prog, cfg) {
15098
15412
  }
15099
15413
  return { pieces, end: () => EC.e, begin, finish, scanSkip, reached, emitRefusal };
15100
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
+ }
15101
15435
  var AssemblyCache = class {
15102
15436
  t;
15103
15437
  prog;
@@ -15311,9 +15645,7 @@ function defaultAssemblyCfgs(prog) {
15311
15645
  function emitAssemblies(prog, cfgs) {
15312
15646
  if (cfgs.length === 0) return ["a:[],"];
15313
15647
  const t = resolveTable(prog);
15314
- const extraIps = [];
15315
- for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
15316
- for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
15648
+ const extraIps = assemblyExtraIps(prog);
15317
15649
  const out = [];
15318
15650
  const seen = /* @__PURE__ */ new Set();
15319
15651
  for (const cfg of cfgs) {
@@ -15322,7 +15654,7 @@ function emitAssemblies(prog, cfgs) {
15322
15654
  seen.add(key);
15323
15655
  let em;
15324
15656
  try {
15325
- em = emitAssemblySource(t, prog, cfg, extraIps, true);
15657
+ em = emitAssemblySource(t, prog, cfg, extraIps, true, cfg.select);
15326
15658
  } catch (e) {
15327
15659
  if (e instanceof Unemittable) continue;
15328
15660
  throw e;
@@ -16117,36 +16449,36 @@ function findRegexFragments(visited) {
16117
16449
  if (branches.length >= 2) runs.add(branches.join("|"));
16118
16450
  for (let i = 0; i < branches.length; i++) {
16119
16451
  for (let n = 2; n <= Math.min(MAX_RUN, branches.length - i); n++) {
16120
- const run = branches.slice(i, i + n).join("|");
16121
- if (run.length < 4) continue;
16122
- runs.add(run);
16452
+ const run2 = branches.slice(i, i + n).join("|");
16453
+ if (run2.length < 4) continue;
16454
+ runs.add(run2);
16123
16455
  }
16124
16456
  }
16125
16457
  }
16126
- for (const run of runs) {
16127
- let e = byRun.get(run);
16458
+ for (const run2 of runs) {
16459
+ let e = byRun.get(run2);
16128
16460
  if (e === void 0) {
16129
- e = { occ: [], nodes: /* @__PURE__ */ new Set(), branches: run.split("|").length };
16130
- byRun.set(run, e);
16461
+ e = { occ: [], nodes: /* @__PURE__ */ new Set(), branches: run2.split("|").length };
16462
+ byRun.set(run2, e);
16131
16463
  }
16132
16464
  if (e.nodes.has(v.p)) continue;
16133
16465
  e.nodes.add(v.p);
16134
16466
  e.occ.push({ site: v.site, source: d.source });
16135
16467
  }
16136
16468
  }
16137
- 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);
16138
16470
  const kept = [];
16139
16471
  for (const c of candidates) {
16140
16472
  if (kept.some((k) => k.e.occ.length >= c.e.occ.length && `|${k.run}|`.includes(`|${c.run}|`))) continue;
16141
16473
  kept.push(c);
16142
16474
  }
16143
- return kept.map(({ run, e }) => ({
16475
+ return kept.map(({ run: run2, e }) => ({
16144
16476
  kind: "regex-fragment",
16145
- id: `regex-fragment:${run}`,
16146
- fragment: run,
16477
+ id: `regex-fragment:${run2}`,
16478
+ fragment: run2,
16147
16479
  branches: e.branches,
16148
16480
  count: e.occ.length,
16149
- savings: (e.occ.length - 1) * run.length,
16481
+ savings: (e.occ.length - 1) * run2.length,
16150
16482
  sources: e.occ.map((o) => `/${o.source}/`),
16151
16483
  sites: e.occ.map((o) => o.site),
16152
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.`
@@ -16879,8 +17211,46 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
16879
17211
  return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16880
17212
  }
16881
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
+
16882
17252
  // src/table/compile-rule-map.ts
16883
- var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
17253
+ var SUPERCOMPILE_GROWTH = 1.05;
16884
17254
  function resolveHostMode(ruleMap, explicit) {
16885
17255
  return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
16886
17256
  }
@@ -16977,14 +17347,40 @@ function compileRuleMap(ruleMap, opts = {}) {
16977
17347
  entry: null,
16978
17348
  runtimeRef: opts.runtimeRef ?? "tableRules",
16979
17349
  fnSources: sources,
16980
- ...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 }] } : {},
16981
17351
  ...metadataSource === void 0 ? {} : { metadataSource }
16982
17352
  });
16983
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
+ };
16984
17379
  return {
16985
17380
  keys: ruleMap.map(([key]) => key),
16986
17381
  replacement,
16987
17382
  replacementWithMetadata,
17383
+ selectAssembly,
16988
17384
  hostMode,
16989
17385
  // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
16990
17386
  // dropped", which is what the driver's `'ast' artifact + CST host` check keys
@@ -17139,7 +17535,7 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
17139
17535
  const emitOpts = { fnSources: sources };
17140
17536
  const source = printable ? emitTableModule(artifact, { name: "grammar", ...emitOpts }) : "";
17141
17537
  const inlineExpression = printable ? emitTableExpression(artifact, { entry: ENTRY, runtimeRef: opts.runtimeRef ?? "tableRules", ...emitOpts }) : null;
17142
- const runOnce = (input, pos, ctx) => entry(input, pos, ctx);
17538
+ const runOnce2 = (input, pos, ctx) => entry(input, pos, ctx);
17143
17539
  return {
17144
17540
  source,
17145
17541
  inlineExpression,
@@ -17153,10 +17549,10 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
17153
17549
  // parser runs where a combinator does. A caller probing many positions
17154
17550
  // passes one context and pays for none per call.
17155
17551
  parse(input, pos = 0, ctx) {
17156
- return runOnce(input, pos, ctx ?? createParseContext());
17552
+ return runOnce2(input, pos, ctx ?? createParseContext());
17157
17553
  },
17158
17554
  parseWithContext(input, parseCtx, pos = 0) {
17159
- return runOnce(input, pos, parseCtx);
17555
+ return runOnce2(input, pos, parseCtx);
17160
17556
  },
17161
17557
  /**
17162
17558
  * NO LONGER REFUSES. It used to throw unless the caller passed
@@ -17172,7 +17568,7 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
17172
17568
  const errors = [];
17173
17569
  ctx._errors = errors;
17174
17570
  ctx._tolerant = true;
17175
- const r = runOnce(input, pos, ctx);
17571
+ const r = runOnce2(input, pos, ctx);
17176
17572
  return { ...r, errors };
17177
17573
  }
17178
17574
  };
@@ -17620,7 +18016,7 @@ function fieldOptionsSrc(def) {
17620
18016
  }
17621
18017
 
17622
18018
  // src/version.ts
17623
- var PARSEMAN_VERSION = "0.56.0";
18019
+ var PARSEMAN_VERSION = "0.57.0";
17624
18020
 
17625
18021
  // src/compiler/compile-linkable-table.ts
17626
18022
  function isLocal(rule) {
@@ -17713,6 +18109,337 @@ function cstBuildHost(typeOrOptions, children3, _fields, span, rawChildren, triv
17713
18109
  cstBuildHost._parsemanCstOutput = true;
17714
18110
  var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
17715
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
+
17716
18443
  // src/combinators/gate.ts
17717
18444
  function gate(predicate) {
17718
18445
  const meta = {
@@ -19788,19 +20515,14 @@ function fallback(emitted, reason, before) {
19788
20515
  }
19789
20516
  };
19790
20517
  }
19791
- function supercompileRuleMapReplacement(prog, replacement) {
20518
+ function supercompileRuleMapReplacement(prog, replacement, select) {
19792
20519
  const cfg = defaultAssemblyCfgs(prog)[0];
19793
20520
  if (cfg === void 0) return replacement;
19794
- const extraIps = [];
19795
- for (const scan of prog.scans ?? []) {
19796
- for (const ref2 of scan.skip) extraIps.push(ref2[0]);
19797
- if (scan.sentinel !== void 0) extraIps.push(scan.sentinel[0]);
19798
- }
19799
- for (const set of prog.scanSkip ?? []) for (const ref2 of set) extraIps.push(ref2[0]);
20521
+ const extraIps = assemblyExtraIps(prog);
19800
20522
  let canonical2;
19801
20523
  let candidate;
19802
20524
  try {
19803
- canonical2 = emitAssemblySource(resolveTable(prog), prog, cfg, extraIps, true);
20525
+ canonical2 = emitAssemblySource(resolveTable(prog), prog, cfg, extraIps, true, select);
19804
20526
  candidate = supercompileEmittedAssembly(prog, cfg, extraIps, canonical2);
19805
20527
  } catch {
19806
20528
  return replacement;
@@ -20103,6 +20825,7 @@ ${nested}
20103
20825
  }
20104
20826
 
20105
20827
  // src/plugin/ir-eval.ts
20828
+ var INERT_BUILDER = /* @__PURE__ */ Symbol("parseman.inertBuilder");
20106
20829
  function evalRulesIR(ir) {
20107
20830
  return evalIR(ir, rules);
20108
20831
  }
@@ -20142,9 +20865,11 @@ function evalIR(ir, rulesImpl) {
20142
20865
  if (staticError !== void 0 && staticError.length > 0) {
20143
20866
  throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
20144
20867
  }
20145
- const n = node(type, child, (() => {
20868
+ const inert = (() => {
20146
20869
  throw new Error("IR node build requires static re-lowering");
20147
- }), opts);
20870
+ });
20871
+ inert[INERT_BUILDER] = type;
20872
+ const n = node(type, child, inert, opts);
20148
20873
  n._def.buildSrc = src;
20149
20874
  if (staticError !== void 0) n._def.buildStaticError = staticError;
20150
20875
  if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
@@ -20287,7 +21012,19 @@ function evalIR(ir, rulesImpl) {
20287
21012
  // src/plugin/index.ts
20288
21013
  import { createHash } from "node:crypto";
20289
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
+ }
20290
21026
  var PARSEMAN_MODULE = "parseman";
21027
+ var DEFAULT_ASSEMBLY_BUDGET = 0.25;
20291
21028
  function reasonSuffix(runtimeOnly) {
20292
21029
  return runtimeOnly === void 0 || runtimeOnly.length === 0 ? "" : ` \u2014 ${runtimeOnly.join("; ")}`;
20293
21030
  }
@@ -20405,8 +21142,9 @@ var plugin_default = createUnplugin((opts = {}) => ({
20405
21142
  transform(code, id) {
20406
21143
  if (!code.includes("parseman")) return null;
20407
21144
  if (!code.includes("macro")) return null;
21145
+ for (const file of opts.assemblyProfile?.files ?? []) this?.addWatchFile?.(file);
20408
21146
  const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
20409
- 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);
20410
21148
  if (result?.warnings.length) {
20411
21149
  for (const w of result.warnings) {
20412
21150
  if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
@@ -20562,17 +21300,17 @@ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recov
20562
21300
  _sourceLowering.delete(file);
20563
21301
  }
20564
21302
  }
20565
- 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) {
20566
21304
  const depth = degradationCaptureDepth();
20567
21305
  try {
20568
- return transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage);
21306
+ return transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage, assemblyProfile, assemblyBudget);
20569
21307
  } finally {
20570
21308
  setReducerResolver(null);
20571
21309
  setBuilderImportResolver(null);
20572
21310
  for (const d of unwindDegradationCapture(depth)) console.warn(formatDegradation(d));
20573
21311
  }
20574
21312
  }
20575
- function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage) {
21313
+ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage, assemblyProfile, assemblyBudgetShare) {
20576
21314
  let result;
20577
21315
  try {
20578
21316
  result = parseSync5(id, code);
@@ -21398,9 +22136,10 @@ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/v
21398
22136
  carried,
21399
22137
  reflection: compiled.reflection
21400
22138
  });
22139
+ const select = selectAssembly(init.start, "compose()", compiled, !declaresLeaf());
21401
22140
  return {
21402
- replacement: compiled.replacementWithMetadata(reflectionMetadata),
21403
- exportedReplacement: compiled.replacementWithMetadata(exportedMetadata),
22141
+ replacement: assembledReplacement(compiled, reflectionMetadata, select),
22142
+ exportedReplacement: assembledReplacement(compiled, exportedMetadata, select),
21404
22143
  carried,
21405
22144
  ...composing ? { trivia: composing } : {},
21406
22145
  ...importedFactories.length ? { importedFactories } : {},
@@ -21416,6 +22155,42 @@ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/v
21416
22155
  warn(init.start, "compose(): a carried piece has no re-lowerable IR; leaving the runtime compose() in place");
21417
22156
  return null;
21418
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);
21419
22194
  const compileComposeLeafCall = (init) => {
21420
22195
  const args = init.arguments;
21421
22196
  const arr = args[0];
@@ -21494,12 +22269,10 @@ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/v
21494
22269
  if (compiled !== null) {
21495
22270
  usedTableRuntime = true;
21496
22271
  return {
21497
- replacement: supercompileRuleMapReplacement(
21498
- compiled.prog,
21499
- compiled.replacementWithMetadata(
21500
- staticTableMetadataSource({ reflection: compiled.reflection, leaf: true }),
21501
- { precompileDefault: true }
21502
- )
22272
+ replacement: assembledReplacement(
22273
+ compiled,
22274
+ staticTableMetadataSource({ reflection: compiled.reflection, leaf: true }),
22275
+ selectAssembly(init.start, "composeLeaf()", compiled, true)
21503
22276
  ),
21504
22277
  ...importedFactories.length ? { importedFactories } : {}
21505
22278
  };