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
@@ -329,6 +329,7 @@ function createParseContext() {
329
329
  _rootTriviaKindIndex: void 0,
330
330
  _rootTriviaStrictScopes: void 0,
331
331
  _rootTriviaCapture: void 0,
332
+ _gap: void 0,
332
333
  _cstTriviaLog: void 0,
333
334
  _fields: void 0,
334
335
  _cstBuf: void 0,
@@ -780,6 +781,7 @@ function literalCodePoints(frag) {
780
781
  if (ch2 === "\\") {
781
782
  const e = frag[i + 1];
782
783
  if (e === void 0) return null;
784
+ if (!(e in CLASS_ESCAPES) && /\w/.test(e)) return null;
783
785
  out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
784
786
  i += 2;
785
787
  continue;
@@ -1326,7 +1328,10 @@ function labeledTriviaSpec(arms, minRepeats) {
1326
1328
  arms,
1327
1329
  minRepeats,
1328
1330
  visitor: commonLabeledTriviaVisitor({ arms, minRepeats }),
1329
- charArms: charArmsFor({ arms, minRepeats })
1331
+ charArms: charArmsFor({ arms, minRepeats }),
1332
+ rootKinds: void 0,
1333
+ rootLeaders: [],
1334
+ rootScan: null
1330
1335
  };
1331
1336
  }
1332
1337
  function matchArmAt(input, pos, arm, state) {
@@ -1987,8 +1992,50 @@ function loopScanner(arms) {
1987
1992
  function regexTriviaScanner(parser) {
1988
1993
  const d = parser._def.tag === "label" ? parser._def.parser._def : parser._def;
1989
1994
  if (d.tag !== "regex" || d.flags) return null;
1990
- const source = d.source;
1991
- return classRunSource(source) ?? altStarSource(source) ?? prefixRunSource(source) ?? delimitedSource(source);
1995
+ return sourceScanner(d.source);
1996
+ }
1997
+ function sourceScanner(source) {
1998
+ return classRunSource(source) ?? altStarSource(source) ?? prefixRunSource(source) ?? delimitedSource(source) ?? sharedPrefixAltSource(source);
1999
+ }
2000
+ function sharedPrefixAltSource(source) {
2001
+ const m = /^((?:\\.|[^\\[\]()*+?|^$.{}])+)\(\?:(.*)\)$/.exec(source);
2002
+ if (!m) return null;
2003
+ const alts = [];
2004
+ const body = m[2];
2005
+ let depth = 0;
2006
+ let start = 0;
2007
+ for (let i = 0; i < body.length; i++) {
2008
+ const c = body[i];
2009
+ if (c === "\\") {
2010
+ i++;
2011
+ continue;
2012
+ }
2013
+ if (c === "[") {
2014
+ while (i < body.length && body[i] !== "]") i += body[i] === "\\" ? 2 : 1;
2015
+ continue;
2016
+ }
2017
+ if (c === "(") depth++;
2018
+ else if (c === ")" && --depth < 0) return null;
2019
+ else if (c === "|" && depth === 0) {
2020
+ alts.push(body.slice(start, i));
2021
+ start = i + 1;
2022
+ }
2023
+ }
2024
+ if (depth !== 0 || alts.length === 0) return null;
2025
+ alts.push(body.slice(start));
2026
+ const scanners = [];
2027
+ for (const alt of alts) {
2028
+ const scanner = sourceScanner(m[1] + alt);
2029
+ if (scanner === null) return null;
2030
+ scanners.push(scanner);
2031
+ }
2032
+ return (input, cur) => {
2033
+ for (const scanner of scanners) {
2034
+ const end = scanner(input, cur);
2035
+ if (end > cur) return end;
2036
+ }
2037
+ return cur;
2038
+ };
1992
2039
  }
1993
2040
  function bmpString(cps) {
1994
2041
  if (!cps) return null;
@@ -3707,6 +3754,12 @@ function scanRefs(s) {
3707
3754
  refs.push(...s.skip);
3708
3755
  return refs;
3709
3756
  }
3757
+ function assemblyExtraIps(prog) {
3758
+ const ips = [];
3759
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) ips.push(r[0]);
3760
+ for (const set of prog.scanSkip ?? []) for (const r of set) ips.push(r[0]);
3761
+ return ips;
3762
+ }
3710
3763
  function expandCompact(p) {
3711
3764
  if ("code" in p) return p;
3712
3765
  return ownTableProgram({
@@ -3837,6 +3890,11 @@ function classHas2(cls, code) {
3837
3890
  for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
3838
3891
  return false;
3839
3892
  }
3893
+ function dispatchIsExclusive(arms, cc) {
3894
+ const resolved = [];
3895
+ for (const ci of arms) if (ci >= 0) resolved[ci] = resolveClass(cc[ci]);
3896
+ return resolveDispatch(arms, resolved).exclusive;
3897
+ }
3840
3898
  function resolveDispatch(arms, cc) {
3841
3899
  const ascii = new Uint8Array(128);
3842
3900
  const hi = [];
@@ -4618,7 +4676,7 @@ function confirmedArityForDef(def) {
4618
4676
  severity: "warn",
4619
4677
  where: `node("${def.type ?? "<inferred>"}")`,
4620
4678
  subject,
4621
- 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`,
4679
+ 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`,
4622
4680
  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"
4623
4681
  });
4624
4682
  return null;
@@ -5012,6 +5070,70 @@ function buildFieldMap(captures) {
5012
5070
  return out;
5013
5071
  }
5014
5072
 
5073
+ // src/table/trivia-gap.ts
5074
+ function skipDeferredTrivia(input, cur, ctx) {
5075
+ const triviaP = ctx.trivia;
5076
+ const plain = ctx.trackLines !== true && ctx.captureTrivia !== true;
5077
+ const gap = ctx._gap;
5078
+ 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;
5079
+ const end = clearRunEnd(input, cur, ctx, triviaP, plain);
5080
+ if (end >= 0) {
5081
+ remember(cur, end, ctx, input, triviaP);
5082
+ return end;
5083
+ }
5084
+ const scan = scanTriviaCompact(input, cur, ctx);
5085
+ if (typeof scan !== "number") return commitTriviaScan(scan);
5086
+ if (plain) remember(cur, scan, ctx, input, triviaP);
5087
+ return scan;
5088
+ }
5089
+ function clearRunEnd(input, cur, ctx, triviaP, plain) {
5090
+ const rootKinds = ctx._rootTriviaKindIndex;
5091
+ const spec = triviaP?._meta.labeledTriviaSpec;
5092
+ if (!plain || triviaP === void 0 || spec == null || rootKinds === void 0 || ctx._triviaLog !== void 0) return -1;
5093
+ if (spec.rootKinds !== rootKinds) prime(spec, triviaP, rootKinds);
5094
+ const fast = spec.rootScan;
5095
+ if (fast === null) return -1;
5096
+ const end = fast(input, cur);
5097
+ return ctx._rootTriviaCapture === false || clearOf(spec.rootLeaders, input, cur, end) ? end : -1;
5098
+ }
5099
+ function remember(cur, end, ctx, input, triviaP) {
5100
+ const gap = ctx._gap;
5101
+ if (gap === void 0) {
5102
+ ctx._gap = { at: cur, end, input, of: triviaP, root: ctx._rootTriviaCapture, rootKinds: ctx._rootTriviaKindIndex, log: ctx._triviaLog, state: ctx.state };
5103
+ return;
5104
+ }
5105
+ gap.at = cur;
5106
+ gap.end = end;
5107
+ gap.input = input;
5108
+ gap.of = triviaP;
5109
+ gap.root = ctx._rootTriviaCapture;
5110
+ gap.rootKinds = ctx._rootTriviaKindIndex;
5111
+ gap.log = ctx._triviaLog;
5112
+ gap.state = ctx.state;
5113
+ }
5114
+ function prime(spec, triviaP, rootKinds) {
5115
+ const ranges = [];
5116
+ let bounded = true;
5117
+ for (const arm of spec.arms) {
5118
+ if (rootKinds[arm.label] === void 0) continue;
5119
+ const fs = arm.parser._meta.firstSet;
5120
+ if (fs.kind === "any") bounded = false;
5121
+ else if (fs.kind === "ranges") ranges.push(...fs.ranges);
5122
+ }
5123
+ const classified = triviaP._meta.triviaScanner;
5124
+ spec.rootKinds = rootKinds;
5125
+ spec.rootLeaders = ranges;
5126
+ spec.rootScan = !bounded ? null : classified === void 0 ? fastTriviaScanner(triviaP) : classified;
5127
+ }
5128
+ function clearOf(ranges, input, from, to) {
5129
+ for (let i = from; i < to; i++) {
5130
+ const c = input.charCodeAt(i);
5131
+ if (c >= 55296 && c <= 57343) return false;
5132
+ for (const r of ranges) if (c >= r.lo && c <= r.hi) return false;
5133
+ }
5134
+ return true;
5135
+ }
5136
+
5015
5137
  // src/combinators/dispatch.ts
5016
5138
  function parserUsesRouted(parser, seen = /* @__PURE__ */ new Set()) {
5017
5139
  if (seen.has(parser)) return false;
@@ -6679,7 +6801,10 @@ var EMITTED_PARAMS = [
6679
6801
  // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
6680
6802
  // old factories ignore them.
6681
6803
  "LEAD",
6682
- "triviaFieldCapture"
6804
+ "triviaFieldCapture",
6805
+ // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
6806
+ // factories ignore it.
6807
+ "skipDeferredTrivia"
6683
6808
  ];
6684
6809
  var RUNTIME_PRELUDE = `
6685
6810
  let _pfScan=null
@@ -6700,7 +6825,7 @@ function _skipTrivia(input,cur,ctx){
6700
6825
  const s=_pfScan
6701
6826
  if(s!==null&&ctx._triviaLog===undefined&&!(ctx.captureTrivia===true&&(ctx._cstBuf!==undefined||ctx._cstTriviaLog!==undefined)))return s(input,cur)
6702
6827
  if(s!==null)return skipTriviaScanned(s,input,cur,ctx)
6703
- if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
6828
+ if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
6704
6829
  return advanceTrivia(input,cur,ctx)
6705
6830
  }
6706
6831
  function _pushLeaf(ctx,value,s,e){pushCstLeaf(ctx,{_tag:'leaf',value,span:{start:s,end:e}})}
@@ -6907,7 +7032,7 @@ function rebuildPools(cc, fx, disp, plan) {
6907
7032
  })
6908
7033
  };
6909
7034
  }
6910
- function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
7035
+ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false, select) {
6911
7036
  const { code, k, fx, disp, dsp, triviaLabelled } = t;
6912
7037
  const swapLegal = !cfg.trackLines;
6913
7038
  const hostCst = cfg.hostCst;
@@ -7006,6 +7131,8 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
7006
7131
  };
7007
7132
  const bodies = [];
7008
7133
  const byIp = /* @__PURE__ */ new Map();
7134
+ const externals = [];
7135
+ const siteBytes = /* @__PURE__ */ new Map();
7009
7136
  const reached = /* @__PURE__ */ new Set();
7010
7137
  const prelude = [];
7011
7138
  const skipDefs = [];
@@ -7141,6 +7268,18 @@ return 1
7141
7268
  tokenDecisionRefs.set(dispatchIp, made);
7142
7269
  return made;
7143
7270
  }
7271
+ function gateMissTest(gip) {
7272
+ const resolved = t.cc[code[gip + 1]];
7273
+ const ci = classes.push([resolved]) - 1;
7274
+ classPlan.push([code[gip + 1]]);
7275
+ const cls = hoist("cl", `CLS[${ci}][0]`);
7276
+ return isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`;
7277
+ }
7278
+ function lookaheadMiss(child, miss) {
7279
+ if (code[child] !== OP_GATE || cfg.tolerant || cfg.probe) return "";
7280
+ return `if(${gateMissTest(child)}){${miss}}
7281
+ `;
7282
+ }
7144
7283
  function skipFor(l) {
7145
7284
  if (l.tri < 0) return "_skipTrivia";
7146
7285
  const ki = l.tri;
@@ -7151,8 +7290,8 @@ return 1
7151
7290
  const nm = `_sk${skipPool.size}`;
7152
7291
  skipPool.set(key, nm);
7153
7292
  if (!hasScan) {
7154
- skipDefs.push(l.buf ? `function ${nm}(input,cur,ctx){return commitTriviaScan(scanTriviaCompact(input,cur,ctx))}` : `function ${nm}(input,cur,ctx){
7155
- if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
7293
+ skipDefs.push(l.buf ? `function ${nm}(input,cur,ctx){return skipDeferredTrivia(input,cur,ctx)}` : `function ${nm}(input,cur,ctx){
7294
+ if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
7156
7295
  return advanceTrivia(input,cur,ctx)}`);
7157
7296
  return nm;
7158
7297
  }
@@ -7197,9 +7336,17 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
7197
7336
  const target = resolveAlias(ip);
7198
7337
  const done = byIp.get(target);
7199
7338
  if (done !== void 0) return done;
7339
+ if (select !== void 0 && !select.has(target)) {
7340
+ const ext = `_px${target}`;
7341
+ byIp.set(target, ext);
7342
+ externals.push(target);
7343
+ return ext;
7344
+ }
7200
7345
  const fname = `_pf${target}`;
7201
7346
  byIp.set(target, fname);
7202
- bodies.push(lower(target, fname));
7347
+ const body = lower(target, fname);
7348
+ siteBytes.set(target, body.length);
7349
+ bodies.push(body);
7203
7350
  return fname;
7204
7351
  }
7205
7352
  function resolveAlias(ip) {
@@ -7339,14 +7486,28 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
7339
7486
  return `${head}EC.e=pos;return ''}`;
7340
7487
  case OP_GATE: {
7341
7488
  const child = link(code[ip + 2]);
7342
- const resolved = t.cc[code[ip + 1]];
7343
- const ci = classes.push([resolved]) - 1;
7344
- classPlan.push([code[ip + 1]]);
7345
- const cls = hoist("cl", `CLS[${ci}][0]`);
7346
7489
  const xf = fxRef(code[ip + 3]);
7347
7490
  return `${head}
7348
- if(${staticBuild && isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
7491
+ if(${gateMissTest(ip)}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
7492
+ return ${child}(input,pos,ctx)
7493
+ }`;
7494
+ }
7495
+ case OP_ARMGATE: {
7496
+ const pred = fnRef(code[ip + 1]);
7497
+ const child = link(code[ip + 2]);
7498
+ const xf = fxRef(code[ip + 3]);
7499
+ return `${head}
7500
+ if(!${pred}(ctx.state)){ctx._fc=false;ctx._fe=-1;ctx._fx=${xf};return FAIL}
7349
7501
  return ${child}(input,pos,ctx)
7502
+ }`;
7503
+ }
7504
+ case OP_WITHCTX: {
7505
+ const ki = code[ip + 1];
7506
+ const child = link(code[ip + 2]);
7507
+ return `${head}
7508
+ const saved=ctx.state
7509
+ ctx.state=${ki < 0 ? `${fnRef(~ki)}(saved)` : kRef(ki)}
7510
+ try{return ${child}(input,pos,ctx)}finally{ctx.state=saved}
7350
7511
  }`;
7351
7512
  }
7352
7513
  case OP_XFORM: {
@@ -7602,7 +7763,7 @@ return FAIL
7602
7763
  const xf = fxRef(code[ip + 2]);
7603
7764
  const p = tmp();
7604
7765
  return `${head}
7605
- ${emitMark(p, L.buf, L.raw, sinks)}
7766
+ ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${emitMark(p, L.buf, L.raw, sinks)}
7606
7767
  const v=${child}(input,pos,ctx)
7607
7768
  ${emitRollback(p, L.buf, L.raw, sinks)}
7608
7769
  if(v===FAIL){EC.e=pos;return null}
@@ -7616,7 +7777,7 @@ return FAIL
7616
7777
  const xf = fxRef(code[ip + 2]);
7617
7778
  const p = tmp();
7618
7779
  return `${head}
7619
- ${emitMark(p, L.buf, L.raw, sinks)}
7780
+ ${lookaheadMiss(code[ip + 1], `ctx._fe=pos;ctx._fx=${xf};return FAIL`)}${emitMark(p, L.buf, L.raw, sinks)}
7620
7781
  const v=${child}(input,pos,ctx)
7621
7782
  ${emitRollback(p, L.buf, L.raw, sinks)}
7622
7783
  if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
@@ -7629,7 +7790,7 @@ return null
7629
7790
  const clean = !REC && failureRollbackClean(prog, ip);
7630
7791
  const p = tmp();
7631
7792
  return `${head}
7632
- ${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
7793
+ ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
7633
7794
  ctx._fc=false
7634
7795
  const v=${child}(input,pos,ctx)
7635
7796
  if(v===FAIL){
@@ -7710,7 +7871,7 @@ return v
7710
7871
  const asc = hoist("as", `DISP[${di}].ascii`);
7711
7872
  const hiArr = table.hi;
7712
7873
  const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join("\n");
7713
- const directAscii = staticBuild && table.hi.length === 0;
7874
+ const directAscii = table.hi.length === 0;
7714
7875
  return `${head}
7715
7876
  const c=${directAscii ? "input.charCodeAt(pos)" : "lead(input,pos)"}
7716
7877
  let arm=-1
@@ -7737,7 +7898,7 @@ return FAIL
7737
7898
  const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]));
7738
7899
  const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null);
7739
7900
  const maskable = n <= 32;
7740
- const directAsciiMask = staticBuild && maskable && gates.every(isAsciiOnlyClass);
7901
+ const directAsciiMask = maskable && gates.every(isAsciiOnlyClass);
7741
7902
  const gateRefs = directAsciiMask ? [] : Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
7742
7903
  const tokenCandidate = tokenChoiceCandidates.get(ip);
7743
7904
  const pretests = Array.from({ length: n }, (_, i) => {
@@ -7779,7 +7940,7 @@ return FAIL
7779
7940
  if (choiceCannotCommit(prog, ip) && !startFailureExact) {
7780
7941
  const deepAt = Array.from({ length: n }, () => tmp());
7781
7942
  const deepFx = Array.from({ length: n }, () => tmp());
7782
- const remember = (i, at, expectedSet) => `${deepAt[i]}=${at};${deepFx[i]}=${expectedSet}`;
7943
+ const remember2 = (i, at, expectedSet) => `${deepAt[i]}=${at};${deepFx[i]}=${expectedSet}`;
7783
7944
  const lazyArms = (ascii) => arms.map((arm, i) => {
7784
7945
  const pretest = pretests[i];
7785
7946
  const decision = pretest?.token === void 0 && pretest?.sequence === void 0 ? "" : tmp();
@@ -7791,13 +7952,13 @@ return FAIL
7791
7952
  if(${decision}===0){
7792
7953
  ctx._fc=false
7793
7954
  {const at=_pfTokEnd
7794
- ${remember(i, "at", pretest.token.expected)}}
7955
+ ${remember2(i, "at", pretest.token.expected)}}
7795
7956
  }`;
7796
7957
  const sequenceMiss = pretest?.sequence === void 0 ? "" : `
7797
7958
  else if(${ascii ? `(bits&${1 << i})!==0&&` : ""}${decision}<0){
7798
7959
  ctx._fc=false
7799
7960
  {const at=_pfSeqEnd
7800
- ${remember(i, "at", pretest.sequence.expected.map((value, at) => `${decision}===${~at}?${value}:`).join("") + pretest.sequence.expected.at(-1))}}
7961
+ ${remember2(i, "at", pretest.sequence.expected.map((value, at) => `${decision}===${~at}?${value}:`).join("") + pretest.sequence.expected.at(-1))}}
7801
7962
  }`;
7802
7963
  return `${decision === "" ? "" : `let ${decision}=${pretest?.sequence === void 0 ? -1 : 0}
7803
7964
  `}if(${gate}){
@@ -7805,7 +7966,7 @@ ctx._fc=false
7805
7966
  {const v=${arm}(input,pos,ctx)
7806
7967
  if(v!==FAIL)return v}
7807
7968
  {const at=ctx._fe??pos
7808
- ${remember(i, "at", "ctx._fx")}}
7969
+ ${remember2(i, "at", "ctx._fx")}}
7809
7970
  ${rollbackFor(i)}
7810
7971
  }${routeMiss}${sequenceMiss}`;
7811
7972
  }).join("\n");
@@ -7918,7 +8079,7 @@ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
7918
8079
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
7919
8080
  ${startFailureExact ? "" : "let prev=0"}
7920
8081
  ${maskArms}
7921
- ${startFailureExact ? "" : `if(best===pos)acc=${catchName}(${n},prev,acc)`}
8082
+ ${startFailureExact ? "" : `if(best===pos&&prev!==0)acc=${catchName}(${n},prev,acc)`}
7922
8083
  ctx._fe=pos;ctx._fx=${startFailureExact ? `best===pos?${choiceFx}:acc??${choiceFx}` : `acc??${choiceFx}`}
7923
8084
  return FAIL
7924
8085
  }
@@ -8017,7 +8178,7 @@ return [key,v]
8017
8178
  const ci = classes.push([resolved]) - 1;
8018
8179
  classPlan.push([itemClassIndex]);
8019
8180
  itemCls = hoist("ri", `CLS[${ci}][0]`);
8020
- itemAscii = staticBuild && isAsciiOnlyClass(resolved);
8181
+ itemAscii = isAsciiOnlyClass(resolved);
8021
8182
  }
8022
8183
  }
8023
8184
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
@@ -8243,7 +8404,7 @@ return nd
8243
8404
  const keepChildren = !structural || cfg.hostReadsChildren !== false || collapse || unwrap;
8244
8405
  const omitsRaw = !hostCst && build !== void 0 && proj < 0 && (flags & 2) !== 0;
8245
8406
  const ty = q(type);
8246
- const stArg = readsState ? "st" : "(ctx.state!==undefined?Object.assign({},ctx.state):undefined)";
8407
+ const stArg = "ctx.state";
8247
8408
  const hostCall = `_pfHost(${ty},hostKids,fieldMap,span,rawKids,tlog,${stArg},${tags})`;
8248
8409
  let value;
8249
8410
  if (proj >= 0) {
@@ -8304,7 +8465,7 @@ ctx.captureTrivia=sCap
8304
8465
  if(v===FAIL)return FAIL
8305
8466
  const end=EC.e
8306
8467
  const span=${tracked ? "spanLines(ctx,pos,end)" : "{start:pos,end}"}
8307
- const st=${readsState ? "(ctx.state!==undefined?Object.assign({},ctx.state):undefined)" : "undefined"}
8468
+ const st=${readsState ? "ctx.state" : "undefined"}
8308
8469
  let nd
8309
8470
  ${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]
8310
8471
  else ` : ""}{${value}}
@@ -8317,21 +8478,51 @@ return nd
8317
8478
  throw new Unemittable(`OP_${OP_NAMES[op] ?? String(op)}`);
8318
8479
  }
8319
8480
  }
8481
+ const served = (ip) => select === void 0 || select.has(resolveAlias(ip));
8320
8482
  const ruleEntries = [];
8321
8483
  for (const [rname, entryIp] of Object.entries(prog.rules)) {
8484
+ if (!served(entryIp)) continue;
8322
8485
  const target = link(entryIp);
8323
8486
  const rn = `_r_${rname.replace(/[^A-Za-z0-9_$]/g, "_")}`;
8324
8487
  bodies.push(`const ${rn}=${target}`);
8325
8488
  ruleEntries.push(`${q(rname)}:${rn}`);
8326
8489
  }
8327
8490
  const extra = [];
8328
- for (const ip of extraIps) extra.push(`${ip}:${link(ip)}`);
8491
+ const exported = /* @__PURE__ */ new Set();
8492
+ for (const ip of extraIps) {
8493
+ if (!served(ip)) continue;
8494
+ exported.add(ip);
8495
+ extra.push(`${ip}:${link(ip)}`);
8496
+ }
8497
+ if (select !== void 0) {
8498
+ const kids = [];
8499
+ for (const ip of reachable) {
8500
+ if (select.has(resolveAlias(ip))) continue;
8501
+ kids.length = 0;
8502
+ childSlots(code, ip, kids);
8503
+ for (const kid of kids) {
8504
+ const target = resolveAlias(kid);
8505
+ if (!select.has(target) || exported.has(target)) continue;
8506
+ exported.add(target);
8507
+ extra.push(`${target}:${link(target)}`);
8508
+ }
8509
+ }
8510
+ }
8511
+ const hybrid = select === void 0 ? "" : `,
8512
+ ext:[${externals.join(",")}],
8513
+ bind:function(xs,ps){_xScan=xs
8514
+ ${externals.map((ip, i) => `_xp${ip}=ps[${i}]`).join("\n")}},
8515
+ scan:function(s){const o=_pfScan;_pfScan=s;return o}`;
8516
+ const externalDefs = select === void 0 ? "" : `let _xScan
8517
+ ${externals.map((ip) => `let _xp${ip}
8518
+ function _px${ip}(input,pos,ctx){const o=_xScan(_pfScan);const v=_xp${ip}(input,pos,ctx);_xScan(o);return v}`).join("\n")}
8519
+ `;
8329
8520
  const source = `${RUNTIME_PRELUDE}${needsNoRawPrelude ? NO_RAW_RUNTIME_PRELUDE : ""}
8330
8521
  ${prelude.join("\n")}
8331
8522
  ${skipDefs.join("\n")}
8332
8523
  ${choiceDefs.join("\n")}
8333
8524
  ${bodies.join("\n")}
8334
- function _begin(ctx){
8525
+ ${externalDefs}function _begin(ctx){
8335
8526
  const host=ctx.build
8336
8527
  if(_pfDepth>0)_pfFrames.push([_pfScan,_pfHost,EC.e])
8337
8528
  _pfDepth++
@@ -8358,7 +8549,7 @@ pieces:{${ruleEntries.join(",")}},
8358
8549
  byIp:{${extra.join(",")}},
8359
8550
  end:function(){return EC.e},
8360
8551
  begin:_begin,
8361
- finish:_finish
8552
+ finish:_finish${hybrid}
8362
8553
  }`;
8363
8554
  return {
8364
8555
  source,
@@ -8366,6 +8557,7 @@ finish:_finish
8366
8557
  masks,
8367
8558
  classes,
8368
8559
  armExpected,
8560
+ siteBytes,
8369
8561
  plan: { classes: classPlan, armExpected: armExpectedPlan, masks: maskPlan }
8370
8562
  };
8371
8563
  }
@@ -8586,6 +8778,9 @@ function assemble(t, prog, cfg) {
8586
8778
  const EC = newEndCell();
8587
8779
  const scalarRecognizers = [];
8588
8780
  const rawScalarSpecs = /* @__PURE__ */ new Set();
8781
+ function lookaheadClass(child) {
8782
+ return code[child] === OP_GATE && !cfg.tolerant && !cfg.probe ? cc[code[child + 1]] : null;
8783
+ }
8589
8784
  function scalarFor(child) {
8590
8785
  const spec = code[child + 1];
8591
8786
  let recognize = scalarRecognizers[spec];
@@ -8595,15 +8790,23 @@ function assemble(t, prog, cfg) {
8595
8790
  }
8596
8791
  return recognize;
8597
8792
  }
8598
- const labelExtraIps = [];
8599
- for (const s of prog.scans ?? []) for (const r of scanRefs(s)) labelExtraIps.push(r[0]);
8600
- for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0]);
8793
+ const labelExtraIps = assemblyExtraIps(prog);
8601
8794
  const closureLabels = computeSiteLabels(
8602
8795
  code,
8603
8796
  [...Object.values(prog.rules), ...labelExtraIps],
8604
8797
  hostCst
8605
8798
  );
8606
8799
  let SCAN = null;
8800
+ let HYBRID;
8801
+ function hybridPiece(piece) {
8802
+ const setScan = HYBRID.scan;
8803
+ return (input, pos, ctx) => {
8804
+ const saved = setScan(SCAN);
8805
+ const value = piece(input, pos, ctx);
8806
+ setScan(saved);
8807
+ return value;
8808
+ };
8809
+ }
8607
8810
  const memo = /* @__PURE__ */ new Map();
8608
8811
  const reached = /* @__PURE__ */ new Set();
8609
8812
  function pushLeaf(ctx, value, s, e) {
@@ -8895,9 +9098,7 @@ function assemble(t, prog, cfg) {
8895
9098
  return s(input, cur);
8896
9099
  }
8897
9100
  if (s !== null) return skipTriviaScanned(s, input, cur, ctx);
8898
- if (needsDeferredTriviaCommit(ctx)) {
8899
- return commitTriviaScan(scanTriviaCompact(input, cur, ctx));
8900
- }
9101
+ if (needsDeferredTriviaCommit(ctx)) return skipDeferredTrivia(input, cur, ctx);
8901
9102
  return advanceTrivia(input, cur, ctx);
8902
9103
  }
8903
9104
  let TERMV;
@@ -9067,7 +9268,7 @@ function assemble(t, prog, cfg) {
9067
9268
  if (next !== void 0) {
9068
9269
  return next(input, pos, ctx, bits, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best);
9069
9270
  }
9070
- if (best === pos) acc = through(total, prev, acc);
9271
+ if (best === pos && prev !== 0) acc = through(total, prev, acc);
9071
9272
  ctx._fe = pos;
9072
9273
  ctx._fx = acc ?? choiceFx;
9073
9274
  return FAIL;
@@ -9150,7 +9351,7 @@ function assemble(t, prog, cfg) {
9150
9351
  if (next !== void 0) {
9151
9352
  return next(input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best);
9152
9353
  }
9153
- if (best === pos) acc = through(total, prev, acc);
9354
+ if (best === pos && prev !== 0) acc = through(total, prev, acc);
9154
9355
  ctx._fe = pos;
9155
9356
  ctx._fx = acc ?? choiceFx;
9156
9357
  return FAIL;
@@ -9229,6 +9430,13 @@ function assemble(t, prog, cfg) {
9229
9430
  if (done !== void 0) return done;
9230
9431
  const flight = inFlight.get(ip);
9231
9432
  if (flight !== void 0) return flight.fwd;
9433
+ const selected = HYBRID?.byIp[ip];
9434
+ if (selected !== void 0) {
9435
+ const piece2 = hybridPiece(selected);
9436
+ memo.set(ip, piece2);
9437
+ reached.add(ip);
9438
+ return piece2;
9439
+ }
9232
9440
  let target;
9233
9441
  const fwd = (input, pos, ctx) => target(input, pos, ctx);
9234
9442
  const holder = { fwd, set: (p) => {
@@ -9236,7 +9444,7 @@ function assemble(t, prog, cfg) {
9236
9444
  } };
9237
9445
  inFlight.set(ip, holder);
9238
9446
  const op = code[ip];
9239
- 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);
9447
+ 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);
9240
9448
  inFlight.delete(ip);
9241
9449
  holder.set(piece);
9242
9450
  memo.set(ip, piece);
@@ -9773,7 +9981,12 @@ function assemble(t, prog, cfg) {
9773
9981
  }
9774
9982
  const child = link(code[ip + 1]);
9775
9983
  const xf = fx[code[ip + 2]];
9984
+ const gate = lookaheadClass(code[ip + 1]);
9776
9985
  return (input, pos, ctx) => {
9986
+ if (gate !== null && !classHas2(gate, lead(input, pos))) {
9987
+ EC.e = pos;
9988
+ return null;
9989
+ }
9777
9990
  const need = markCst(ctx);
9778
9991
  const mRaw = MRAW;
9779
9992
  const mTl = MTL;
@@ -9796,7 +10009,13 @@ function assemble(t, prog, cfg) {
9796
10009
  case OP_PEEK: {
9797
10010
  const child = link(code[ip + 1]);
9798
10011
  const xf = fx[code[ip + 2]];
10012
+ const gate = lookaheadClass(code[ip + 1]);
9799
10013
  return (input, pos, ctx) => {
10014
+ if (gate !== null && !classHas2(gate, lead(input, pos))) {
10015
+ ctx._fe = pos;
10016
+ ctx._fx = xf;
10017
+ return FAIL;
10018
+ }
9800
10019
  const need = markCst(ctx);
9801
10020
  const mRaw = MRAW;
9802
10021
  const mTl = MTL;
@@ -9818,7 +10037,12 @@ function assemble(t, prog, cfg) {
9818
10037
  }
9819
10038
  case OP_OPT: {
9820
10039
  const child = link(code[ip + 1]);
10040
+ const gate = lookaheadClass(code[ip + 1]);
9821
10041
  return (input, pos, ctx) => {
10042
+ if (gate !== null && !classHas2(gate, lead(input, pos))) {
10043
+ EC.e = pos;
10044
+ return null;
10045
+ }
9822
10046
  const need = markCst(ctx);
9823
10047
  const mRaw = MRAW;
9824
10048
  const mTl = MTL;
@@ -10625,6 +10849,11 @@ function assemble(t, prog, cfg) {
10625
10849
  }
10626
10850
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
10627
10851
  }
10852
+ if (prev === 0) {
10853
+ ctx._fe = pos;
10854
+ ctx._fx = choiceFx;
10855
+ return FAIL;
10856
+ }
10628
10857
  if (best === pos && prev < 1) acc = accSet(e0, acc);
10629
10858
  if (best === pos && prev < 2) acc = accSet(e1, acc);
10630
10859
  if (best === pos && n === 3 && prev < 3) acc = accSet(e2, acc);
@@ -10933,7 +11162,7 @@ function assemble(t, prog, cfg) {
10933
11162
  return (input, pos, ctx) => {
10934
11163
  if (!pred(ctx.state)) {
10935
11164
  ctx._fc = false;
10936
- ctx._fe = pos;
11165
+ ctx._fe = -1;
10937
11166
  ctx._fx = xf;
10938
11167
  return FAIL;
10939
11168
  }
@@ -11339,7 +11568,7 @@ function assemble(t, prog, cfg) {
11339
11568
  if (v === FAIL) return FAIL;
11340
11569
  const end = EC.e;
11341
11570
  const span = tracked ? spanLines(ctx, pos, end) : { start: pos, end };
11342
- const st = readsState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
11571
+ const st = readsState ? ctx.state : void 0;
11343
11572
  let nd;
11344
11573
  if (unwrap && kids.length === 1) {
11345
11574
  nd = unwrapChild(kids[0]);
@@ -11352,10 +11581,10 @@ function assemble(t, prog, cfg) {
11352
11581
  ) {
11353
11582
  nd = kids[0];
11354
11583
  } else if (proj >= 0) {
11355
- 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);
11584
+ nd = hostCst && host !== void 0 ? host(type, kids, fieldMap, span, rawKids, tlog, ctx.state, tags) : projectChild(kids, proj, type);
11356
11585
  } else if (build !== void 0) {
11357
11586
  if (hostCst && host !== void 0) {
11358
- nd = host(type, kids, fieldMap, span, rawKids, tlog, readsState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0, tags);
11587
+ nd = host(type, kids, fieldMap, span, rawKids, tlog, ctx.state, tags);
11359
11588
  } else {
11360
11589
  nd = build(kids, fieldMap, span, rawKids, captureWide ? tlog : EMPTY_TL, st);
11361
11590
  }
@@ -11406,9 +11635,7 @@ function assemble(t, prog, cfg) {
11406
11635
  let emitRefusal;
11407
11636
  let emitReached;
11408
11637
  {
11409
- const extraIps = [];
11410
- for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
11411
- for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
11638
+ const extraIps = assemblyExtraIps(prog);
11412
11639
  const roots = [...Object.values(prog.rules), ...extraIps];
11413
11640
  for (const ip of reachableSites(code, roots)) {
11414
11641
  if (code[ip] === OP_DISPATCH) {
@@ -11501,9 +11728,11 @@ function assemble(t, prog, cfg) {
11501
11728
  adjacencyHolds,
11502
11729
  t.lexPrograms,
11503
11730
  LEAD,
11504
- triviaFieldCapture
11731
+ triviaFieldCapture,
11732
+ skipDeferredTrivia
11505
11733
  );
11506
11734
  emitReached = new Set(pre.reached);
11735
+ if (emitted.ext !== void 0) HYBRID = emitted;
11507
11736
  }
11508
11737
  try {
11509
11738
  if (emitted !== void 0) throw new ServedByBuild();
@@ -11585,7 +11814,8 @@ function assemble(t, prog, cfg) {
11585
11814
  adjacencyHolds,
11586
11815
  t.lexPrograms,
11587
11816
  LEAD,
11588
- triviaFieldCapture
11817
+ triviaFieldCapture,
11818
+ skipDeferredTrivia
11589
11819
  );
11590
11820
  emitReached = em.reached;
11591
11821
  } catch (e) {
@@ -11596,7 +11826,7 @@ function assemble(t, prog, cfg) {
11596
11826
  }
11597
11827
  function pieceAt(ip) {
11598
11828
  const em = emitted;
11599
- if (em !== void 0) {
11829
+ if (em !== void 0 && HYBRID === void 0) {
11600
11830
  const p = em.byIp[ip];
11601
11831
  if (p === void 0) throw new Error(`table emitter: no emitted body for site ${ip}`);
11602
11832
  return p;
@@ -11626,6 +11856,37 @@ function assemble(t, prog, cfg) {
11626
11856
  }
11627
11857
  const scans = scansArr;
11628
11858
  const scanSkip = (prog.scanSkip ?? []).map((set) => set.map((r) => subtreeComb(r)));
11859
+ if (HYBRID !== void 0) {
11860
+ const em = HYBRID;
11861
+ em.bind(
11862
+ (s) => {
11863
+ const saved = SCAN;
11864
+ SCAN = s;
11865
+ return saved;
11866
+ },
11867
+ em.ext.map((ip) => link(ip))
11868
+ );
11869
+ const pieces2 = {};
11870
+ for (const [name, entryIp] of Object.entries(prog.rules)) {
11871
+ pieces2[name] = em.pieces[name] ?? link(entryIp);
11872
+ }
11873
+ for (const ip of emitReached) reached.add(ip);
11874
+ return {
11875
+ pieces: pieces2,
11876
+ end: () => EC.e,
11877
+ begin: (ctx) => {
11878
+ begin(ctx);
11879
+ em.begin(ctx);
11880
+ },
11881
+ finish: () => {
11882
+ em.finish();
11883
+ finish();
11884
+ },
11885
+ scanSkip,
11886
+ reached,
11887
+ emitRefusal: void 0
11888
+ };
11889
+ }
11629
11890
  if (emitted !== void 0) {
11630
11891
  const em = emitted;
11631
11892
  return {
@@ -11661,6 +11922,15 @@ function assemble(t, prog, cfg) {
11661
11922
  }
11662
11923
  return { pieces, end: () => EC.e, begin, finish, scanSkip, reached, emitRefusal };
11663
11924
  }
11925
+ var SITE_COUNTS;
11926
+ function countedPiece(piece, ip) {
11927
+ const counts = SITE_COUNTS;
11928
+ if (counts === void 0) return piece;
11929
+ return (input, pos, ctx) => {
11930
+ counts[ip]++;
11931
+ return piece(input, pos, ctx);
11932
+ };
11933
+ }
11664
11934
  var AssemblyCache = class {
11665
11935
  t;
11666
11936
  prog;
@@ -13177,6 +13447,33 @@ var Encoder = class {
13177
13447
  return childrenOf(def);
13178
13448
  }
13179
13449
  }
13450
+ /**
13451
+ * THE FIRST-CHARACTER GATE OF AN `optional`, `not` OR `peek` CHILD.
13452
+ *
13453
+ * A non-nullable child cannot match at a character outside its first set, so
13454
+ * the wrapper's outcome there is already decided: `optional` yields `null`,
13455
+ * `not` succeeds, `peek` fails with its own set. The interpreter's `optional`
13456
+ * makes that test itself (`repeat.ts` `startsFirstSet`); the table entered the
13457
+ * child — a rollback mark, the call, the child's own failure — to learn it.
13458
+ *
13459
+ * The gate is an `OP_GATE` row around the child, so every engine runs it
13460
+ * unchanged, a probe or tolerant parse forwards through it as for a node's
13461
+ * gate, and the wrapper's row keeps its layout. The engines test the class
13462
+ * before taking the wrapper's mark. The first set is resolved through
13463
+ * `winners`, the analysis a choice arm's gate uses, so a composed rule's set
13464
+ * is the merged one. A scalar `not` child stays bare: `scalarTerminalNotChild`
13465
+ * already reduces it to one recognizer call.
13466
+ */
13467
+ lookaheadGate(p, child) {
13468
+ const op = this.code[child];
13469
+ 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;
13470
+ const rr = this.refResolver();
13471
+ if (matchesEmpty(p, /* @__PURE__ */ new Set(), rr)) return child;
13472
+ const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
13473
+ if (cls < 0) return child;
13474
+ const e = deriveExpected(p);
13475
+ return this.emit(OP_GATE, cls, child, this.expected(e.length > 0 ? e : [p._tag]));
13476
+ }
13180
13477
  /** A first set becomes a char class string, or −1 for `any`. */
13181
13478
  charClass(fs) {
13182
13479
  if (fs.kind !== "ranges" || fs.ranges.length === 0) return -1;
@@ -13188,12 +13485,19 @@ var Encoder = class {
13188
13485
  this.ccIndex.set(spec, i);
13189
13486
  return i;
13190
13487
  }
13191
- /** Bind a guard only when its class is already part of the program. Repeat
13192
- * gating must not grow the compact artifact merely to avoid a failed call. */
13193
- existingCharClass(fs) {
13194
- if (fs.kind !== "ranges" || fs.ranges.length === 0) return -1;
13195
- const hit = this.ccIndex.get(encodeClassSpec(fs.ranges));
13196
- return hit !== void 0 && hit < 100 ? hit : -1;
13488
+ /**
13489
+ * The first set a node's or an attempt's fail-fast gate tests. A bare
13490
+ * cross-rule ref resolves through `winners` (a compose OVERRIDE may widen
13491
+ * it). Any other body keeps its construction-time `_meta.firstSet`, except
13492
+ * where that is `any`: under `compose()` a body that reaches a rule of
13493
+ * another piece is built before that rule exists, so its cached set is
13494
+ * `any` and the node got NO gate (jess's `CompoundSelector` was entered at
13495
+ * `{`, `Call` at `}`). The resolved set is the one a choice arm's gate uses,
13496
+ * over the final merged map.
13497
+ */
13498
+ gateFirstSet(p) {
13499
+ const cached = p._meta.firstSet;
13500
+ return p._def.tag === "lazy" || cached.kind === "any" ? firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : cached;
13197
13501
  }
13198
13502
  /**
13199
13503
  * Wrap one choice arm in its `autoNot` checks (`OP_REJECT`).
@@ -13498,6 +13802,12 @@ var Encoder = class {
13498
13802
  if (transactionalFirst[i] !== void 0) classes[i] = -1;
13499
13803
  }
13500
13804
  }
13805
+ if (d.gates.some((g) => g !== null && g !== void 0) && !dispatchIsExclusive(classes, this.cc)) {
13806
+ for (let k = 0; k < classes.length; k++) {
13807
+ const gate = d.gates[order[k]];
13808
+ if (gate !== null && gate !== void 0) classes[k] = -1;
13809
+ }
13810
+ }
13501
13811
  const dispIdx = this.disp.length;
13502
13812
  this.disp.push(classes);
13503
13813
  let rollbackMask = kids.length > 31 ? -1 : 0;
@@ -13527,7 +13837,7 @@ var Encoder = class {
13527
13837
  case "oneOrMore": {
13528
13838
  const child = this.node(d.parser).ip;
13529
13839
  const rr = this.refResolver();
13530
- const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.existingCharClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
13840
+ const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
13531
13841
  const op = d.valueUnused ? OP_REPV : OP_REP;
13532
13842
  const min = d.tag === "many" ? 0 : d.min;
13533
13843
  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);
@@ -13551,7 +13861,7 @@ var Encoder = class {
13551
13861
  return d.min >= 2 ? this.emit(OP_REP, child, d.min, d.max ?? -1, sep, flags, this.expected(deriveExpected(d.parser))) : this.emit(OP_REP, child, d.min, d.max ?? -1, sep, flags);
13552
13862
  }
13553
13863
  case "optional": {
13554
- const head = this.emit(OP_OPT, this.node(d.parser).ip);
13864
+ const head = this.emit(OP_OPT, this.lookaheadGate(d.parser, this.node(d.parser).ip));
13555
13865
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(head);
13556
13866
  return head;
13557
13867
  }
@@ -13599,8 +13909,8 @@ var Encoder = class {
13599
13909
  // every CST node, so this is load-bearing, not reflection trivia.
13600
13910
  d.tags === void 0 ? -1 : this.constant(d.tags)
13601
13911
  );
13602
- if (matchesEmpty(d.parser)) return body;
13603
- const fs = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
13912
+ if (matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return body;
13913
+ const fs = this.gateFirstSet(d.parser);
13604
13914
  if (fs.kind === "any") return body;
13605
13915
  const cls = this.charClass(fs);
13606
13916
  if (cls < 0) return body;
@@ -13692,7 +14002,7 @@ var Encoder = class {
13692
14002
  // other engine reported `['not(literal)']`. It hid because `expected` is a
13693
14003
  // TOP-LEVEL field on `RunResult` and is NOT part of the identity digest.
13694
14004
  case "not":
13695
- return this.emit(OP_NOT, this.node(d.parser).ip, this.expected(assertionFailureExpected(false, d.parser._tag)));
14005
+ return this.emit(OP_NOT, this.lookaheadGate(d.parser, this.node(d.parser).ip), this.expected(assertionFailureExpected(false, d.parser._tag)));
13696
14006
  case "guard":
13697
14007
  return this.emit(OP_GUARD, this.fn(d.predicate, d.predSrc ?? null), this.expected(["gate"]));
13698
14008
  case "adjacency":
@@ -13713,14 +14023,14 @@ var Encoder = class {
13713
14023
  return this.emit(OP_WITHCTX, this.constant(d.extra), this.node(d.parser).ip);
13714
14024
  }
13715
14025
  case "peek":
13716
- return this.emit(OP_PEEK, this.node(d.parser).ip, this.expected(assertionFailureExpected(true, d.parser._tag)));
14026
+ return this.emit(OP_PEEK, this.lookaheadGate(d.parser, this.node(d.parser).ip), this.expected(assertionFailureExpected(true, d.parser._tag)));
13717
14027
  // A TRANSACTION IS A ROW. See `OP_ATTEMPT` for why the transparent
13718
14028
  // lowering was correct only for a choice arm.
13719
14029
  case "attempt": {
13720
14030
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
13721
14031
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
13722
- const fs = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
13723
- if (fs.kind === "any" || matchesEmpty(d.parser)) return inner;
14032
+ const fs = this.gateFirstSet(d.parser);
14033
+ if (fs.kind === "any" || matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return inner;
13724
14034
  const cls = this.charClass(fs);
13725
14035
  if (cls < 0) return inner;
13726
14036
  const e = deriveExpected(d.parser);
@@ -14020,9 +14330,7 @@ function defaultAssemblyCfgs(prog) {
14020
14330
  function emitAssemblies(prog, cfgs) {
14021
14331
  if (cfgs.length === 0) return ["a:[],"];
14022
14332
  const t = resolveTable(prog);
14023
- const extraIps = [];
14024
- for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
14025
- for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
14333
+ const extraIps = assemblyExtraIps(prog);
14026
14334
  const out = [];
14027
14335
  const seen = /* @__PURE__ */ new Set();
14028
14336
  for (const cfg of cfgs) {
@@ -14031,7 +14339,7 @@ function emitAssemblies(prog, cfgs) {
14031
14339
  seen.add(key);
14032
14340
  let em;
14033
14341
  try {
14034
- em = emitAssemblySource(t, prog, cfg, extraIps, true);
14342
+ em = emitAssemblySource(t, prog, cfg, extraIps, true, cfg.select);
14035
14343
  } catch (e) {
14036
14344
  if (e instanceof Unemittable) continue;
14037
14345
  throw e;
@@ -15720,8 +16028,46 @@ function collectGrammarReflection(ruleMap, opts) {
15720
16028
  return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
15721
16029
  }
15722
16030
 
16031
+ // src/table/select-hot.ts
16032
+ var LOOP = 8;
16033
+ var ROUNDS = 60;
16034
+ var CAP = 1e12;
16035
+ function selectHotSites(code, entries, siteBytes, budget, profile) {
16036
+ const selected = /* @__PURE__ */ new Set();
16037
+ if (budget <= 0) return selected;
16038
+ const weight = profile === void 0 ? staticWeights(code, entries) : void 0;
16039
+ const w = (ip) => (weight !== void 0 ? weight.get(ip) : profile[ip]) ?? 0;
16040
+ const ranked = [...siteBytes.keys()].sort((a, b) => w(b) / siteBytes.get(b) - w(a) / siteBytes.get(a) || a - b);
16041
+ let spent = 0;
16042
+ for (const ip of ranked) {
16043
+ if (w(ip) === 0) break;
16044
+ const bytes = siteBytes.get(ip);
16045
+ if (spent + bytes > budget) continue;
16046
+ spent += bytes;
16047
+ selected.add(ip);
16048
+ }
16049
+ return selected;
16050
+ }
16051
+ function staticWeights(code, entries) {
16052
+ let weight = /* @__PURE__ */ new Map();
16053
+ const kids = [];
16054
+ for (let round = 0; round < ROUNDS; round++) {
16055
+ const next = /* @__PURE__ */ new Map();
16056
+ for (const ip of entries) next.set(ip, 1);
16057
+ for (const [ip, w] of weight) {
16058
+ kids.length = 0;
16059
+ childSlots(code, ip, kids);
16060
+ const op = code[ip];
16061
+ const scale = op === OP_REP || op === OP_REPV ? LOOP : op === OP_CHOICE || op === OP_DISPATCH ? 1 / Math.max(1, kids.length) : 1;
16062
+ for (const kid of kids) next.set(kid, Math.min(CAP, (next.get(kid) ?? 0) + w * scale));
16063
+ }
16064
+ weight = next;
16065
+ }
16066
+ return weight;
16067
+ }
16068
+
15723
16069
  // src/table/compile-rule-map.ts
15724
- var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
16070
+ var SUPERCOMPILE_GROWTH = 1.05;
15725
16071
  function resolveHostMode(ruleMap, explicit) {
15726
16072
  return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
15727
16073
  }
@@ -15818,14 +16164,40 @@ function compileRuleMap(ruleMap, opts = {}) {
15818
16164
  entry: null,
15819
16165
  runtimeRef: opts.runtimeRef ?? "tableRules",
15820
16166
  fnSources: sources,
15821
- ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
16167
+ ...options.select !== void 0 && options.select.size > 0 ? { assemblies: [{ ...defaultAssemblyCfgs(artifact)[0], select: options.select }] } : {},
15822
16168
  ...metadataSource === void 0 ? {} : { metadataSource }
15823
16169
  });
15824
16170
  const replacement = replacementWithMetadata();
16171
+ const selectAssembly = (budget, profile) => {
16172
+ const none = { sites: /* @__PURE__ */ new Set(), bytes: 0 };
16173
+ if (hostMode !== "ast" || prog.lines === 1) return none;
16174
+ const table = resolveTable(artifact);
16175
+ const cfg = defaultAssemblyCfgs(artifact)[0];
16176
+ const roots = assemblyExtraIps(artifact);
16177
+ let siteBytes, fixed;
16178
+ try {
16179
+ siteBytes = emitAssemblySource(table, artifact, cfg, roots, true).siteBytes;
16180
+ fixed = emitAssemblySource(table, artifact, cfg, roots, true, /* @__PURE__ */ new Set()).source.length;
16181
+ } catch (e) {
16182
+ if (e instanceof Unemittable) return { ...none, refusal: e.construct };
16183
+ throw e;
16184
+ }
16185
+ const ceiling = budget / SUPERCOMPILE_GROWTH;
16186
+ let target = ceiling;
16187
+ for (let attempt = 0; attempt < 16; attempt++) {
16188
+ const sites = selectHotSites(artifact.code, Object.values(artifact.rules), siteBytes, target - fixed, profile);
16189
+ if (sites.size === 0) return none;
16190
+ const bytes = emitAssemblySource(table, artifact, cfg, roots, true, sites).source.length;
16191
+ if (bytes <= ceiling) return { sites, bytes: Math.ceil(bytes * SUPERCOMPILE_GROWTH) };
16192
+ target *= Math.min(ceiling / bytes, 0.99);
16193
+ }
16194
+ return none;
16195
+ };
15825
16196
  return {
15826
16197
  keys: ruleMap.map(([key]) => key),
15827
16198
  replacement,
15828
16199
  replacementWithMetadata,
16200
+ selectAssembly,
15829
16201
  hostMode,
15830
16202
  // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
15831
16203
  // dropped", which is what the driver's `'ast' artifact + CST host` check keys