parseman 0.61.0 → 0.61.1

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.
@@ -424,13 +424,13 @@ function caseFoldVariants(cp) {
424
424
  // src/combinators/probe.ts
425
425
  function failAt(ctx, expected, pos) {
426
426
  const r = { ok: false, expected, span: { start: pos, end: pos } };
427
- const probe = ctx._probe;
428
- if (probe !== void 0 && pos <= probe.offset) {
429
- const best = probe.best;
427
+ const probe2 = ctx._probe;
428
+ if (probe2 !== void 0 && pos <= probe2.offset) {
429
+ const best = probe2.best;
430
430
  if (best === null || pos > best.span.start) {
431
- probe.best = r;
431
+ probe2.best = r;
432
432
  } else if (pos === best.span.start) {
433
- probe.best = { ...best, expected: [...best.expected, ...expected] };
433
+ probe2.best = { ...best, expected: [...best.expected, ...expected] };
434
434
  }
435
435
  }
436
436
  return r;
@@ -2610,6 +2610,7 @@ function matchesAt(sentinel, input, pos, ctx) {
2610
2610
  function captureError(ctx, error) {
2611
2611
  if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
2612
2612
  }
2613
+ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
2613
2614
 
2614
2615
  // src/combinators/expect.ts
2615
2616
  function deriveExpected(c) {
@@ -4063,7 +4064,7 @@ function regex(pattern, flags = "") {
4063
4064
  }
4064
4065
 
4065
4066
  // src/version.ts
4066
- var PARSEMAN_VERSION = "0.61.0";
4067
+ var PARSEMAN_VERSION = "0.61.1";
4067
4068
 
4068
4069
  // src/table/lex-body.ts
4069
4070
  function buildLexBodyRecognizer(spec, constants) {
@@ -4533,7 +4534,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
4533
4534
  if (!Number.isInteger(spec[2]) || spec[2] < 0) {
4534
4535
  throw new TypeError("table balanced lexical program has an invalid scan reference");
4535
4536
  }
4536
- const run = (_input, _pos, ctx, scan) => {
4537
+ const run2 = (_input, _pos, ctx, scan) => {
4537
4538
  if (scan === void 0) throw new TypeError("table balanced lexical program lost its scan body");
4538
4539
  const result = scan.parse(_input, _pos, ctx);
4539
4540
  if (!result.ok) {
@@ -4543,7 +4544,7 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
4543
4544
  }
4544
4545
  return result.span.end;
4545
4546
  };
4546
- return Object.assign(run, { scan: spec[2] });
4547
+ return Object.assign(run2, { scan: spec[2] });
4547
4548
  }
4548
4549
  if (spec[0] === 2) return fixedTreeRunner(spec.slice(2), matchers, classes, expected);
4549
4550
  if (spec[0] === 1) {
@@ -6590,6 +6591,206 @@ function asciiFoldKey(key) {
6590
6591
  }
6591
6592
  return out;
6592
6593
  }
6594
+ function matchesDispatchMatcher(value, matcher) {
6595
+ const candidate = matcher.caseInsensitive ? asciiFoldKey(value) : value;
6596
+ const expected = matcher.caseInsensitive ? asciiFoldKey(matcher.value) : matcher.value;
6597
+ switch (matcher.kind) {
6598
+ case "startsWith":
6599
+ return candidate.startsWith(expected);
6600
+ case "endsWith":
6601
+ return candidate.endsWith(expected);
6602
+ case "matches": {
6603
+ const flags = matcher.caseInsensitive && !matcher.flags?.includes("i") ? `${matcher.flags ?? ""}i` : matcher.flags ?? "";
6604
+ return new RegExp(matcher.value, flags).test(value);
6605
+ }
6606
+ }
6607
+ }
6608
+ function isStringMatcher(value) {
6609
+ if (typeof value !== "object" || value === null) return false;
6610
+ const kind = value.kind;
6611
+ return kind === "startsWith" || kind === "endsWith" || kind === "matches";
6612
+ }
6613
+ function fixedMatcher(kind, value) {
6614
+ if (typeof value !== "string") throw new TypeError(`parseman: ${kind}() value must be a string`);
6615
+ if (value.length === 0) throw new RangeError(`parseman: ${kind}() requires a non-empty value`);
6616
+ return { kind, value };
6617
+ }
6618
+ function startsWith(prefix) {
6619
+ return fixedMatcher("startsWith", prefix);
6620
+ }
6621
+ function endsWith(suffix) {
6622
+ return fixedMatcher("endsWith", suffix);
6623
+ }
6624
+ function matches(pattern) {
6625
+ if (!(pattern instanceof RegExp)) throw new TypeError("parseman: matches() requires a RegExp");
6626
+ if (pattern.flags.includes("g") || pattern.flags.includes("y")) {
6627
+ throw new TypeError("parseman: matches() does not accept global or sticky regex flags");
6628
+ }
6629
+ return { kind: "matches", value: pattern.source, flags: pattern.flags };
6630
+ }
6631
+ function dispatchMatcher(entry) {
6632
+ switch (entry.kind) {
6633
+ case "startsWith":
6634
+ return startsWith(entry.value);
6635
+ case "endsWith":
6636
+ return endsWith(entry.value);
6637
+ case "matches":
6638
+ return matches(new RegExp(entry.value, entry.flags));
6639
+ }
6640
+ }
6641
+ function when(key, parser2, opts = {}) {
6642
+ const usesRouted = parserUsesRouted(parser2);
6643
+ if (isStringMatcher(key)) {
6644
+ return { kind: "whenMatcher", matcher: key, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
6645
+ }
6646
+ const keys = Array.isArray(key) ? [...key] : [key];
6647
+ if (keys.length === 0) throw new RangeError("parseman: when() requires at least one key");
6648
+ for (const item of keys) {
6649
+ if (typeof item !== "string") throw new TypeError("parseman: when() keys must be strings");
6650
+ }
6651
+ return { kind: "when", keys, parser: parser2, caseInsensitive: opts.caseInsensitive ?? false, usesRouted };
6652
+ }
6653
+ function otherwise(parser2, opts = {}) {
6654
+ const usesRouted = parserUsesRouted(parser2);
6655
+ return opts.commit === false ? { kind: "otherwise", parser: parser2, usesRouted, commit: false } : { kind: "otherwise", parser: parser2, usesRouted };
6656
+ }
6657
+ function routed(fallback) {
6658
+ return {
6659
+ _tag: "routed",
6660
+ _meta: {
6661
+ // The routed token is whatever the selector matched, so the first set stays
6662
+ // `any` even with a fallback — `any` already subsumes the fallback's.
6663
+ firstSet: { kind: "any" },
6664
+ canMatchNewline: fallback?._meta.canMatchNewline ?? false,
6665
+ isTrivia: false
6666
+ },
6667
+ _def: fallback === void 0 ? { tag: "routed" } : { tag: "routed", fallback },
6668
+ parse(_input, pos, ctx) {
6669
+ const item = ctx._routed;
6670
+ if (item === void 0 || pos !== item.span.start) {
6671
+ if (fallback !== void 0) return fallback.parse(_input, pos, ctx);
6672
+ return { ok: false, expected: ["routed()"], span: { start: pos, end: pos } };
6673
+ }
6674
+ if (cstCaptureActive(ctx)) {
6675
+ pushCstLeaf(ctx, new PlainLeaf6(item.value, item.span));
6676
+ }
6677
+ return { ok: true, value: item.value, span: item.span };
6678
+ }
6679
+ };
6680
+ }
6681
+ function dispatch(selector, ...arms) {
6682
+ if (parserUsesRouted(selector)) {
6683
+ throw new Error("parseman: routed() can only appear inside a dispatch() branch");
6684
+ }
6685
+ let fallback;
6686
+ let fallbackUsesRouted = false;
6687
+ let fallbackCommits = true;
6688
+ const matchers = [];
6689
+ const cases = [];
6690
+ const seen = [];
6691
+ for (let i = 0; i < arms.length; i++) {
6692
+ const arm = arms[i];
6693
+ if (arm.kind === "otherwise") {
6694
+ if (fallback !== void 0) throw new Error("parseman: dispatch() accepts at most one otherwise() arm");
6695
+ if (i !== arms.length - 1) throw new Error("parseman: otherwise() must be the last dispatch() arm");
6696
+ fallback = arm.parser;
6697
+ fallbackUsesRouted = arm.usesRouted === true;
6698
+ fallbackCommits = arm.commit !== false;
6699
+ continue;
6700
+ }
6701
+ if (arm.kind === "whenMatcher") {
6702
+ const matcher = arm.matcher;
6703
+ matchers.push({
6704
+ kind: matcher.kind,
6705
+ value: matcher.value,
6706
+ ...matcher.kind === "matches" ? { flags: matcher.flags } : {},
6707
+ parser: arm.parser,
6708
+ caseInsensitive: arm.caseInsensitive,
6709
+ ...arm.usesRouted === true ? { usesRouted: true } : {}
6710
+ });
6711
+ continue;
6712
+ }
6713
+ for (const key of arm.keys) {
6714
+ const folded = asciiFoldKey(key);
6715
+ for (const prior of seen) {
6716
+ const overlaps = arm.caseInsensitive || prior.caseInsensitive ? folded === prior.folded : key === prior.raw;
6717
+ if (overlaps) throw new Error(`parseman: duplicate dispatch key ${JSON.stringify(key)}`);
6718
+ }
6719
+ seen.push({ raw: key, folded, caseInsensitive: arm.caseInsensitive });
6720
+ }
6721
+ cases.push({
6722
+ keys: arm.keys,
6723
+ parser: arm.parser,
6724
+ caseInsensitive: arm.caseInsensitive,
6725
+ ...arm.usesRouted === true ? { usesRouted: true } : {}
6726
+ });
6727
+ }
6728
+ const meta = {
6729
+ firstSet: selector._meta.firstSet,
6730
+ canMatchNewline: selector._meta.canMatchNewline || cases.some((entry) => entry.parser._meta.canMatchNewline) || matchers.some((entry) => entry.parser._meta.canMatchNewline) || (fallback?._meta.canMatchNewline ?? false),
6731
+ isTrivia: false
6732
+ };
6733
+ const byKey = /* @__PURE__ */ new Map();
6734
+ const byFoldedKey = /* @__PURE__ */ new Map();
6735
+ for (const entry of cases) {
6736
+ for (const key of entry.keys) {
6737
+ if (entry.caseInsensitive) byFoldedKey.set(asciiFoldKey(key), entry);
6738
+ else byKey.set(key, entry);
6739
+ }
6740
+ }
6741
+ return {
6742
+ _tag: "dispatch",
6743
+ _meta: meta,
6744
+ _def: {
6745
+ tag: "dispatch",
6746
+ selector,
6747
+ cases,
6748
+ ...matchers.length === 0 ? {} : { matchers },
6749
+ ...fallback === void 0 ? {} : {
6750
+ otherwise: fallback,
6751
+ ...fallbackUsesRouted ? { otherwiseUsesRouted: true } : {},
6752
+ ...fallbackCommits ? {} : { otherwiseCommit: false }
6753
+ }
6754
+ },
6755
+ parse(input, pos, ctx) {
6756
+ if (parserUsesRouted(selector)) {
6757
+ throw new Error("parseman: routed() can only appear inside a dispatch() branch");
6758
+ }
6759
+ const selectorMark = saveTriviaMark(ctx);
6760
+ const selected = selector.parse(input, pos, ctx);
6761
+ if (!selected.ok) return selected;
6762
+ const chosen = byKey.get(selected.value) ?? (byFoldedKey.size === 0 ? void 0 : byFoldedKey.get(asciiFoldKey(selected.value))) ?? matchers.find((matcher) => matchesDispatchMatcher(selected.value, matcher));
6763
+ const branch = chosen ?? (fallback === void 0 ? void 0 : { keys: [], parser: fallback, caseInsensitive: false, usesRouted: fallbackUsesRouted });
6764
+ if (branch === void 0) {
6765
+ const expected = cases.flatMap((entry) => entry.keys.map((key) => JSON.stringify(key)));
6766
+ return { ok: false, expected, span: { start: selected.span.end, end: selected.span.end } };
6767
+ }
6768
+ const savedRouted = ctx._routed;
6769
+ const usesRouted = branchUsesRouted(branch);
6770
+ let mark = saveTriviaMark(ctx);
6771
+ if (usesRouted) {
6772
+ rollbackTrivia(ctx, selectorMark);
6773
+ mark = saveTriviaMark(ctx);
6774
+ ctx._routed = { value: selected.value, span: selected.span };
6775
+ }
6776
+ let result;
6777
+ try {
6778
+ result = branch.parser.parse(input, usesRouted ? pos : selected.span.end, ctx);
6779
+ } finally {
6780
+ if (usesRouted) ctx._routed = savedRouted;
6781
+ }
6782
+ if (!result.ok) {
6783
+ rollbackTrivia(ctx, mark);
6784
+ return chosen === void 0 && !fallbackCommits ? result : { ...result, committed: true };
6785
+ }
6786
+ return {
6787
+ ok: true,
6788
+ value: [selected.value, result.value],
6789
+ span: { start: pos, end: result.span.end }
6790
+ };
6791
+ }
6792
+ };
6793
+ }
6593
6794
 
6594
6795
  // src/table/ops.ts
6595
6796
  var OP_LIT = 1;
@@ -8644,7 +8845,7 @@ var Encoder = class {
8644
8845
  const dispCovIds = this.plan?.dispatches.get(p);
8645
8846
  let dispCov = 0;
8646
8847
  const arms = [];
8647
- const routed = [];
8848
+ const routed2 = [];
8648
8849
  const key = [], keyArm = [];
8649
8850
  const fold = [], foldArm = [];
8650
8851
  const match = [];
@@ -8652,7 +8853,7 @@ var Encoder = class {
8652
8853
  for (const c of d.cases) {
8653
8854
  const arm = arms.length;
8654
8855
  arms.push(this.covWrap(this.node(c.parser).ip, dispCovIds?.[dispCov++], 1));
8655
- routed.push(branchUsesRouted(c) ? 1 : 0);
8856
+ routed2.push(branchUsesRouted(c) ? 1 : 0);
8656
8857
  for (const kk of c.keys) {
8657
8858
  expected.push(JSON.stringify(kk));
8658
8859
  if (c.caseInsensitive) {
@@ -8668,7 +8869,7 @@ var Encoder = class {
8668
8869
  for (const m of d.matchers ?? []) {
8669
8870
  const arm = arms.length;
8670
8871
  arms.push(this.covWrap(this.node(m.parser).ip, dispCovIds?.[dispCov++], 1));
8671
- routed.push(branchUsesRouted(m) ? 1 : 0);
8872
+ routed2.push(branchUsesRouted(m) ? 1 : 0);
8672
8873
  if (!m.caseInsensitive) {
8673
8874
  match.push([KIND[m.kind], m.value, m.flags ?? "", arm]);
8674
8875
  continue;
@@ -8682,7 +8883,7 @@ var Encoder = class {
8682
8883
  }
8683
8884
  const other = d.otherwise === void 0 ? -1 : this.covWrap(this.node(d.otherwise).ip, dispCovIds?.[dispCov++], 1);
8684
8885
  const dspIdx = this.dsp.length;
8685
- this.dsp.push({ key, keyArm, fold, foldArm, match, routed, expected });
8886
+ this.dsp.push({ key, keyArm, fold, foldArm, match, routed: routed2, expected });
8686
8887
  const head = this.emitHead(OP_DISPATCH, 5 + arms.length);
8687
8888
  this.code[head + 1] = sel;
8688
8889
  this.code[head + 2] = dspIdx;
@@ -9998,6 +10199,12 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
9998
10199
 
9999
10200
  // src/table/child-slots.ts
10000
10201
  function childSlots(code, ip, out) {
10202
+ const from = out.length;
10203
+ if (!childSlotOffsets(code, ip, out)) return false;
10204
+ for (let i = from; i < out.length; i++) out[i] = code[out[i]];
10205
+ return true;
10206
+ }
10207
+ function childSlotOffsets(code, ip, out) {
10001
10208
  switch (code[ip]) {
10002
10209
  // ZERO-WIDTH / TERMINAL ROWS — no successor. `OP_GUARD` and `OP_ADJ` are both
10003
10210
  // childless tests. `OP_LIVE` holds a hand-written combinator in `prog.fns`; its
@@ -10030,7 +10237,7 @@ function childSlots(code, ip, out) {
10030
10237
  case OP_COV:
10031
10238
  case OP_REJECT:
10032
10239
  case OP_GAPGATE:
10033
- out.push(code[ip + 1]);
10240
+ out.push(ip + 1);
10034
10241
  return true;
10035
10242
  // Single child at `ip + 2`.
10036
10243
  case OP_GATE:
@@ -10047,55 +10254,55 @@ function childSlots(code, ip, out) {
10047
10254
  case OP_ARMGATE:
10048
10255
  case OP_RGATE:
10049
10256
  case OP_RGATE2:
10050
- out.push(code[ip + 2]);
10257
+ out.push(ip + 2);
10051
10258
  return true;
10052
10259
  // `STRICT strict tolerant` — both encodings are reachable; an assembly keeps one.
10053
10260
  case OP_STRICT:
10054
- out.push(code[ip + 1], code[ip + 2]);
10261
+ out.push(ip + 1, ip + 2);
10055
10262
  return true;
10056
10263
  // `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
10057
10264
  // data the enclosing `dispatch()` already consumed, so the only edge is the
10058
10265
  // fallback.
10059
10266
  case OP_ROUTED:
10060
- if (code[ip + 1] >= 0) out.push(code[ip + 1]);
10267
+ if (code[ip + 1] >= 0) out.push(ip + 1);
10061
10268
  return true;
10062
10269
  // `DISPATCH sel d other otherRouted n a1 … an` — the SELECTOR is a child on
10063
10270
  // exactly the same footing as the arms: it runs inside whatever node and scope
10064
10271
  // this site sits in.
10065
10272
  case OP_DISPATCH: {
10066
- out.push(code[ip + 1]);
10067
- if (code[ip + 3] >= 0) out.push(code[ip + 3]);
10273
+ out.push(ip + 1);
10274
+ if (code[ip + 3] >= 0) out.push(ip + 3);
10068
10275
  const n = code[ip + 5];
10069
- for (let i = 0; i < n; i++) out.push(code[ip + 6 + i]);
10276
+ for (let i = 0; i < n; i++) out.push(ip + 6 + i);
10070
10277
  return true;
10071
10278
  }
10072
10279
  case OP_SEQ:
10073
10280
  case OP_SEQV: {
10074
10281
  const n = code[ip + 1];
10075
- for (let i = 0; i < n; i++) out.push(code[ip + 2 + i]);
10282
+ for (let i = 0; i < n; i++) out.push(ip + 2 + i);
10076
10283
  return true;
10077
10284
  }
10078
10285
  case OP_SEQX: {
10079
10286
  const n = code[ip + 2];
10080
- for (let i = 0; i < n; i++) out.push(code[ip + 3 + i]);
10287
+ for (let i = 0; i < n; i++) out.push(ip + 3 + i);
10081
10288
  return true;
10082
10289
  }
10083
10290
  case OP_CHOICE: {
10084
10291
  const n = code[ip + 2];
10085
- for (let i = 0; i < n; i++) out.push(code[ip + 4 + i]);
10292
+ for (let i = 0; i < n; i++) out.push(ip + 4 + i);
10086
10293
  return true;
10087
10294
  }
10088
10295
  // Arms are stride-2 from `ip + 4` — the gate word sits beside each arm offset.
10089
10296
  case OP_GREEDY: {
10090
- out.push(code[ip + 1]);
10297
+ out.push(ip + 1);
10091
10298
  const n = code[ip + 2];
10092
- for (let i = 0; i < n; i++) out.push(code[ip + 4 + 2 * i]);
10299
+ for (let i = 0; i < n; i++) out.push(ip + 4 + 2 * i);
10093
10300
  return true;
10094
10301
  }
10095
10302
  case OP_REP:
10096
10303
  case OP_REPV:
10097
- out.push(code[ip + 1]);
10098
- if (code[ip + 4] >= 0) out.push(code[ip + 4]);
10304
+ out.push(ip + 1);
10305
+ if (code[ip + 4] >= 0) out.push(ip + 4);
10099
10306
  return true;
10100
10307
  default:
10101
10308
  return false;
@@ -10910,7 +11117,7 @@ function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false, s
10910
11117
  const swapLegal = !cfg.trackLines;
10911
11118
  const hostCst = cfg.hostCst;
10912
11119
  const LOGS = (prog.triviaSpecs?.length ?? 0) > 0 || cfg.inheritedTrivia === true;
10913
- const REC = prog.rec === 1 && cfg.tolerant;
11120
+ const REC2 = prog.rec === 1 && cfg.tolerant;
10914
11121
  if (cfg.coverage) throw new Unemittable("a coverage assembly");
10915
11122
  const roots = [...Object.values(prog.rules), ...extraIps];
10916
11123
  const reachable = [...reachableSites(code, roots)];
@@ -10983,7 +11190,7 @@ function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false, s
10983
11190
  const op = code[ip];
10984
11191
  sinkBits.set(
10985
11192
  ip,
10986
- op === OP_SCAN || !scanSinkNeutral && op === OP_LEX_PROGRAM && t2.lexPrograms[code[ip + 1]].scan !== void 0 ? 3 : (op === OP_FIELD ? 1 : 0) | (op === OP_EXPECT || REC && (op === OP_REP || op === OP_REPV) ? 2 : 0)
11193
+ op === OP_SCAN || !scanSinkNeutral && op === OP_LEX_PROGRAM && t2.lexPrograms[code[ip + 1]].scan !== void 0 ? 3 : (op === OP_FIELD ? 1 : 0) | (op === OP_EXPECT || REC2 && (op === OP_REP || op === OP_REPV) ? 2 : 0)
10987
11194
  );
10988
11195
  }
10989
11196
  const sinkKids = [];
@@ -11508,7 +11715,7 @@ if(v!==FAIL)return v
11508
11715
  const err={_tag:'parseError',span:${prog.lines === 1 ? "spanLines(ctx,pos,pos)" : "new _SP(pos,pos)"},expected:${xf}}
11509
11716
  const es=ctx._errors
11510
11717
  if(es!==undefined)es.push(err)
11511
- ${REC ? "if(ctx._tolerant===true)captureError(ctx,err)\n" : ""}ctx._fc=false
11718
+ ${REC2 ? "if(ctx._tolerant===true)captureError(ctx,err)\n" : ""}ctx._fc=false
11512
11719
  EC.e=pos
11513
11720
  return err
11514
11721
  }`;
@@ -11638,7 +11845,7 @@ return v
11638
11845
  }
11639
11846
  case OP_LEX_PROGRAM: {
11640
11847
  const programId = code[ip + 1];
11641
- const run = hoist("lexProgram", `LEXPROG[${programId}]`);
11848
+ const run2 = hoist("lexProgram", `LEXPROG[${programId}]`);
11642
11849
  const scanId = t2.lexPrograms[programId].scan;
11643
11850
  if (scanId !== void 0) return `${head}
11644
11851
  const sTri=ctx.trivia,sKinds=ctx.triviaKindLabels,sScan=_pfScan
@@ -11656,7 +11863,7 @@ ctx._cstTriviaLog=undefined
11656
11863
  ctx._triviaLog=undefined
11657
11864
  ctx._rootTriviaLog=undefined
11658
11865
  let e
11659
- try{e=${run}(input,pos,ctx,SCANS[${scanId}])}finally{
11866
+ try{e=${run2}(input,pos,ctx,SCANS[${scanId}])}finally{
11660
11867
  _pfScan=sScan
11661
11868
  ctx.trivia=sTri
11662
11869
  ctx.triviaKindLabels=sKinds
@@ -11675,7 +11882,7 @@ EC.e=e
11675
11882
  return v
11676
11883
  }`;
11677
11884
  return `${head}
11678
- const e=${run}(input,pos,ctx)
11885
+ const e=${run2}(input,pos,ctx)
11679
11886
  if(e<0)return FAIL
11680
11887
  const v=input.slice(pos,e)
11681
11888
  if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,new _LF(v,new _SP(pos,e)))
@@ -11686,7 +11893,7 @@ return v
11686
11893
  /* ── transaction ─────────────────────────────────────────────────────── */
11687
11894
  case OP_ATTEMPT: {
11688
11895
  const child = link(code[ip + 1]);
11689
- const clean = !REC && failureRollbackClean(prog, ip);
11896
+ const clean = !REC2 && failureRollbackClean(prog, ip);
11690
11897
  const logs = clean && LOGS && failureLogRollback(prog, ip);
11691
11898
  const p = tmp();
11692
11899
  return `${head}
@@ -11752,7 +11959,7 @@ return null
11752
11959
  }
11753
11960
  case OP_OPT: {
11754
11961
  const child = link(code[ip + 1]);
11755
- const clean = !REC && failureRollbackClean(prog, ip);
11962
+ const clean = !REC2 && failureRollbackClean(prog, ip);
11756
11963
  const logs = clean && LOGS && failureLogRollback(prog, ip);
11757
11964
  const p = tmp();
11758
11965
  return `${head}
@@ -11783,15 +11990,15 @@ return v
11783
11990
  const childIp = code[base + i];
11784
11991
  kids.push(code[childIp] === OP_ADJ ? "" : link(childIp));
11785
11992
  }
11786
- const sy = REC ? `${tmp()}sy` : "";
11993
+ const sy = REC2 ? `${tmp()}sy` : "";
11787
11994
  const pub = (i) => {
11788
11995
  const s = sentRef(code[base + n + i]);
11789
11996
  return s === "undefined" ? `ctx._sync=${sy}` : `ctx._sync=${s}??${sy}`;
11790
11997
  };
11791
11998
  const parts = [head];
11792
- if (REC) parts.push(`const ${sy}=ctx._sync`, "try{", pub(0));
11999
+ if (REC2) parts.push(`const ${sy}=ctx._sync`, "try{", pub(0));
11793
12000
  parts.push(`const v0=${kids[0]}(input,pos,ctx)`, "if(v0===FAIL)return FAIL");
11794
- const close = () => REC ? `${parts.join("\n")}
12001
+ const close = () => REC2 ? `${parts.join("\n")}
11795
12002
  }finally{ctx._sync=${sy}}
11796
12003
  }` : `${parts.join("\n")}
11797
12004
  }`;
@@ -11804,7 +12011,7 @@ return v
11804
12011
  for (let i = 1; i < n; i++) {
11805
12012
  const vn = `v${i}`;
11806
12013
  parts.push(`let ${vn}`);
11807
- if (REC) parts.push(pub(i));
12014
+ if (REC2) parts.push(pub(i));
11808
12015
  const childIp = code[base + i];
11809
12016
  if (code[childIp] === OP_ADJ) {
11810
12017
  const negated = code[childIp + 1] === 1;
@@ -11909,7 +12116,7 @@ return FAIL
11909
12116
  if (maskable) maskPlan.push(~di);
11910
12117
  const p = tmp();
11911
12118
  const encodedRollbackMask = choiceRollbackMask(prog, ip) ?? -1;
11912
- const rollbackMask = REC ? -1 : encodedRollbackMask;
12119
+ const rollbackMask = REC2 ? -1 : encodedRollbackMask;
11913
12120
  const hasRollback = rollbackMask !== 0;
11914
12121
  const logMask = rollbackMask === -1 || !LOGS ? 0 : choiceLogRollbackMask(prog, ip);
11915
12122
  const choiceMark = hasRollback ? emitMark(p, L.buf, L.raw, sinks) : "";
@@ -12162,7 +12369,7 @@ return [key,v]
12162
12369
  const collect = op === OP_REP;
12163
12370
  let itemCls;
12164
12371
  let itemAscii = false;
12165
- if (!REC) {
12372
+ if (!REC2) {
12166
12373
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
12167
12374
  if (itemClassIndex >= 0) {
12168
12375
  const resolved = t2.cc[itemClassIndex];
@@ -12175,14 +12382,14 @@ return [key,v]
12175
12382
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
12176
12383
  let sepCls;
12177
12384
  let sepAscii = false;
12178
- if (!REC && sepIp >= 0 && (code[ip + 5] & 8) !== 0 && !leadingTriviaFieldAt(code, k, sepIp) && !rowNullable(code, k, sepIp, nullable2)) {
12385
+ if (!REC2 && sepIp >= 0 && (code[ip + 5] & 8) !== 0 && !leadingTriviaFieldAt(code, k, sepIp) && !rowNullable(code, k, sepIp, nullable2)) {
12179
12386
  const resolved = t2.cc[code[ip + 8]];
12180
12387
  const ci = classes.push([resolved]) - 1;
12181
12388
  classPlan.push([code[ip + 8]]);
12182
12389
  sepCls = hoist("rs", `CLS[${ci}][0]`);
12183
12390
  sepAscii = isAsciiOnlyClass(resolved);
12184
12391
  }
12185
- const gapIp = !gapNeutral && sepIp < 0 && min === 0 && !REC && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
12392
+ const gapIp = !gapNeutral && sepIp < 0 && min === 0 && !REC2 && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
12186
12393
  let gapCode = (_rb) => "";
12187
12394
  if (gapIp >= 0) {
12188
12395
  const row = code[gapIp + 2];
@@ -12267,11 +12474,11 @@ ${p}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
12267
12474
  ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
12268
12475
  }`;
12269
12476
  const my = `${p}my`;
12270
- const recFx = !REC ? "" : (flags & 16) !== 0 ? "[]" : fxRef(code[ip + 6]);
12271
- const sepSent = REC ? sentRef(code[ip + 7]) : "undefined";
12477
+ const recFx = !REC2 ? "" : (flags & 16) !== 0 ? "[]" : fxRef(code[ip + 6]);
12478
+ const sepSent = REC2 ? sentRef(code[ip + 7]) : "undefined";
12272
12479
  const recSent = sep === void 0 ? my : sepSent === "undefined" ? `orSentinel(${my},undefined)` : `orSentinel(${sepSent}??${my},${sepSent}===undefined?undefined:${my})`;
12273
12480
  const recGate = sep !== void 0 || min === 0 ? "" : `count>=${min}&&`;
12274
- const recBranch = !REC ? "" : `if(ctx._tolerant===true&&${my}!==undefined&&${recGate}!matchesAt(${my},input,itemStart,ctx)){
12481
+ const recBranch = !REC2 ? "" : `if(ctx._tolerant===true&&${my}!==undefined&&${recGate}!matchesAt(${my},input,itemStart,ctx)){
12275
12482
  ${sep === void 0 ? `${rb}
12276
12483
  ` : ""}const rr=recoverScan(input,itemStart,ctx,${recSent},${recFx})
12277
12484
  ${collect ? "out.push(rr.error)\n" : ""}captureError(ctx,rr.error)
@@ -12282,7 +12489,7 @@ continue
12282
12489
  `;
12283
12490
  return `${head}
12284
12491
  const out=${collect ? "[]" : "undefined"}
12285
- ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC ? `const ${my}=ctx._sync
12492
+ ${knownTrivia === void 0 ? "const hasTrivia=ctx.trivia!==undefined\n" : ""}${L.buf ? "" : "const needMark=_rollbackNeeded(ctx)\n"}${REC2 ? `const ${my}=ctx._sync
12286
12493
  ` : ""}${itemCls === void 0 && sepCls === void 0 ? "" : `const ${p}gate=ctx._probe===undefined
12287
12494
  `}let cur=pos
12288
12495
  let count=0
@@ -12325,7 +12532,7 @@ ${trailingAllowed ? "if(sepEnd>=0)cur=sepEnd\n" : ""}break
12325
12532
  `}${gapCode(rb)}ctx._fc=false
12326
12533
  const v=${child}(input,itemStart,ctx)
12327
12534
  ${gapIp >= 0 ? "if(gapped)GAP.at=-1\n" : ""}if(v===FAIL){
12328
- ${REC ? `if(ctx._fc===true){
12535
+ ${REC2 ? `if(ctx._fc===true){
12329
12536
  ${rb}
12330
12537
  return FAIL
12331
12538
  }
@@ -12551,13 +12758,13 @@ return nd
12551
12758
  }
12552
12759
  }
12553
12760
  const served = (ip) => select === void 0 || select.has(resolveAlias(ip));
12554
- const ruleEntries = [];
12761
+ const ruleEntries2 = [];
12555
12762
  for (const [rname, entryIp] of Object.entries(prog.rules)) {
12556
12763
  if (!served(entryIp)) continue;
12557
12764
  const target = link(entryIp);
12558
12765
  const rn = `_r_${rname.replace(/[^A-Za-z0-9_$]/g, "_")}`;
12559
12766
  bodies.push(`const ${rn}=${target}`);
12560
- ruleEntries.push(`${q(rname)}:${rn}`);
12767
+ ruleEntries2.push(`${q(rname)}:${rn}`);
12561
12768
  }
12562
12769
  const extra = [];
12563
12770
  const exported = /* @__PURE__ */ new Set();
@@ -12623,7 +12830,7 @@ _pfHost=prior[1]
12623
12830
  EC.e=prior[2]
12624
12831
  }
12625
12832
  return{
12626
- pieces:{${ruleEntries.join(",")}},
12833
+ pieces:{${ruleEntries2.join(",")}},
12627
12834
  byIp:{${extra.join(",")}},
12628
12835
  end:function(){return EC.e},
12629
12836
  begin:_begin,
@@ -12732,31 +12939,31 @@ function lines(prefix, body, options = {}) {
12732
12939
  _def: { tag: "lines", prefix, body, ...first === void 0 ? {} : { first }, ...lazy === void 0 ? {} : { lazy } },
12733
12940
  parse(input, pos, ctx) {
12734
12941
  const c = ctx;
12735
- const probe = createDetachedParseContext(false, ctx.state);
12736
- probe.lineView = c.lineView;
12737
- const run = [];
12942
+ const probe2 = createDetachedParseContext(false, ctx.state);
12943
+ probe2.lineView = c.lineView;
12944
+ const run2 = [];
12738
12945
  const parts = [];
12739
12946
  let offset = 0;
12740
12947
  for (let start = pos; start < input.length || start === pos; ) {
12741
12948
  let end2 = input.indexOf("\n", start);
12742
12949
  if (end2 < 0) end2 = input.length;
12743
- probe._columnsSplit = void 0;
12744
- const head = (run.length === 0 ? opening : prefix).parse(input, start, probe);
12950
+ probe2._columnsSplit = void 0;
12951
+ const head = (run2.length === 0 ? opening : prefix).parse(input, start, probe2);
12745
12952
  let content;
12746
12953
  let joined;
12747
12954
  if (head.ok && head.span.end <= end2) {
12748
12955
  content = head.span.end;
12749
12956
  joined = "prefix";
12750
- } else if (run.length === 0) {
12957
+ } else if (run2.length === 0) {
12751
12958
  return { ok: false, expected: head.ok ? ["line prefix"] : head.expected, span: { start, end: start } };
12752
- } else if (lazy !== void 0 && lazy.parse(input, start, probe).ok) {
12959
+ } else if (lazy !== void 0 && lazy.parse(input, start, probe2).ok) {
12753
12960
  content = start;
12754
12961
  joined = "lazy";
12755
12962
  } else break;
12756
- const mark = probe._columnsSplit;
12963
+ const mark = probe2._columnsSplit;
12757
12964
  const here = columnAt(input, content, c);
12758
12965
  const split = joined === "prefix" && mark !== void 0 && mark.at === content ? mark.split : here.split;
12759
- run.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
12966
+ run2.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
12760
12967
  const text = input.slice(content, end2);
12761
12968
  const newline = end2 < input.length;
12762
12969
  parts.push(newline ? `${text}
@@ -12765,11 +12972,11 @@ function lines(prefix, body, options = {}) {
12765
12972
  start = end2 + 1;
12766
12973
  if (!newline) break;
12767
12974
  }
12768
- const view = { lines: run, parent: c.lineView };
12975
+ const view = { lines: run2, parent: c.lineView };
12769
12976
  const saved = c.lineView, starts = c._lineStarts, lineIndex = c._lineIndex, scanned = c._lineScannedTo, probing = c._probe;
12770
12977
  const errors = c._errors?.length ?? 0;
12771
12978
  let cursor = -1;
12772
- if (probing !== void 0) for (const line2 of run) {
12979
+ if (probing !== void 0) for (const line2 of run2) {
12773
12980
  if (probing.offset >= line2.source && probing.offset <= line2.end) {
12774
12981
  cursor = line2.offset + probing.offset - line2.source;
12775
12982
  break;
@@ -12802,8 +13009,8 @@ function lines(prefix, body, options = {}) {
12802
13009
  }
12803
13010
  const stop = result.span.end;
12804
13011
  const index = lineIndexAt(view, stop);
12805
- const line = run[index];
12806
- 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);
13012
+ const line = run2[index];
13013
+ 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);
12807
13014
  recordLineRangeFromContext(c, input, pos, end);
12808
13015
  return { ok: true, value: result.value, span: { start: pos, end } };
12809
13016
  }
@@ -12912,12 +13119,12 @@ function literal(value, opts = {}) {
12912
13119
  }
12913
13120
 
12914
13121
  // src/combinators/ref.ts
12915
- function withRuleTrivia(trivia2, ctx, run) {
13122
+ function withRuleTrivia(trivia2, ctx, run2) {
12916
13123
  const savedLabels = ctx.triviaKindLabels;
12917
13124
  ctx.trivia = trivia2;
12918
13125
  ctx.triviaKindLabels = trivia2._meta.triviaKindLabels;
12919
13126
  try {
12920
- return run();
13127
+ return run2();
12921
13128
  } finally {
12922
13129
  ctx.trivia = void 0;
12923
13130
  ctx.triviaKindLabels = savedLabels;
@@ -13419,6 +13626,19 @@ function failCleanAnalysis(code) {
13419
13626
  // src/cst/host-mode.ts
13420
13627
  var FUSED_HOST_MODE = /* @__PURE__ */ Symbol.for("parseman.fusedHostMode");
13421
13628
  var FUSED_HOST_ELIDED = /* @__PURE__ */ Symbol.for("parseman.fusedHostElided");
13629
+ function assertHostModeCompatible(mode, build, hostBranchElided = true) {
13630
+ const isCstHost = build?._parsemanCstOutput === true;
13631
+ if (mode === "ast" && isCstHost && hostBranchElided) {
13632
+ throw new Error(
13633
+ "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."
13634
+ );
13635
+ }
13636
+ if (mode === "cst" && !isCstHost) {
13637
+ throw new Error(
13638
+ '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.'
13639
+ );
13640
+ }
13641
+ }
13422
13642
 
13423
13643
  // src/grammar-metadata.ts
13424
13644
  var GRAMMAR_COVERAGE_DEFINITIONS = /* @__PURE__ */ Symbol.for("parseman.grammarCoverageDefinitions");
@@ -13553,8 +13773,8 @@ function linkMatcher(m) {
13553
13773
  }
13554
13774
  var NO_COVERAGE = (_id) => {
13555
13775
  };
13556
- function cfgKeyOf(hostCst, trackLines, tolerant, coverage, probe, hostReadsChildren = true, inheritedTrivia = false) {
13557
- return (hostCst ? 1 : 0) | (trackLines ? 2 : 0) | (tolerant ? 4 : 0) | (coverage ? 8 : 0) | (probe ? 16 : 0) | (hostReadsChildren ? 0 : 32) | (inheritedTrivia ? 64 : 0);
13776
+ function cfgKeyOf(hostCst, trackLines, tolerant, coverage, probe2, hostReadsChildren = true, inheritedTrivia = false) {
13777
+ return (hostCst ? 1 : 0) | (trackLines ? 2 : 0) | (tolerant ? 4 : 0) | (coverage ? 8 : 0) | (probe2 ? 16 : 0) | (hostReadsChildren ? 0 : 32) | (inheritedTrivia ? 64 : 0);
13558
13778
  }
13559
13779
  function cfgKey(c) {
13560
13780
  return cfgKeyOf(c.hostCst, c.trackLines, c.tolerant, c.coverage, c.probe, c.hostReadsChildren, c.inheritedTrivia);
@@ -13571,7 +13791,7 @@ function assemble(t2, prog, cfg) {
13571
13791
  const triviaScan = t2.triviaScan;
13572
13792
  const swapLegal = !cfg.trackLines;
13573
13793
  const hostCst = cfg.hostCst;
13574
- const REC = prog.rec === 1 && cfg.tolerant;
13794
+ const REC2 = prog.rec === 1 && cfg.tolerant;
13575
13795
  const PROBE = cfg.probe;
13576
13796
  const sentinels = /* @__PURE__ */ new Map();
13577
13797
  function sentinelFor(cls) {
@@ -13621,7 +13841,7 @@ function assemble(t2, prog, cfg) {
13621
13841
  const memo = /* @__PURE__ */ new Map();
13622
13842
  const reached = /* @__PURE__ */ new Set();
13623
13843
  const NULLABLE = /* @__PURE__ */ new Map();
13624
- const CLEAN = REC ? () => false : failCleanAnalysis(code);
13844
+ const CLEAN = REC2 ? () => false : failCleanAnalysis(code);
13625
13845
  function pushLeaf(ctx, value, s, e) {
13626
13846
  const lf = new PlainLeaf9(value, new PlainSpan10(s, e));
13627
13847
  pushCstLeaf(ctx, lf);
@@ -14332,7 +14552,7 @@ function assemble(t2, prog, cfg) {
14332
14552
  };
14333
14553
  function lookaheadLedNode(ip) {
14334
14554
  const op = code[ip];
14335
- if (op !== OP_NODE && op !== OP_NODE_TRACK || hostCst || REC || cfg.probe || cfg.coverage || cfg.trackLines) return void 0;
14555
+ if (op !== OP_NODE && op !== OP_NODE_TRACK || hostCst || REC2 || cfg.probe || cfg.coverage || cfg.trackLines) return void 0;
14336
14556
  if (passCollapse(code, ip, hostCst) || gatedPassCollapse(code, ip, hostCst) !== void 0) return void 0;
14337
14557
  const body = code[ip + 2];
14338
14558
  const bop = code[body];
@@ -14708,7 +14928,7 @@ function assemble(t2, prog, cfg) {
14708
14928
  const child = link(code[ip + 1]);
14709
14929
  const xf = fx[code[ip + 2]];
14710
14930
  const expectSpan = (ctx, pos) => prog.lines === 1 ? spanLines(ctx, pos, pos) : new PlainSpan10(pos, pos);
14711
- if (REC) {
14931
+ if (REC2) {
14712
14932
  return (input, pos, ctx) => {
14713
14933
  const v = child(input, pos, ctx);
14714
14934
  if (v !== FAIL) return v;
@@ -15105,8 +15325,8 @@ function assemble(t2, prog, cfg) {
15105
15325
  };
15106
15326
  }
15107
15327
  case OP_LEX_PROGRAM: {
15108
- const run = t2.lexPrograms[code[ip + 1]];
15109
- const scanId = run.scan;
15328
+ const run2 = t2.lexPrograms[code[ip + 1]];
15329
+ const scanId = run2.scan;
15110
15330
  if (scanId !== void 0) {
15111
15331
  return (input, pos, ctx) => {
15112
15332
  const sTrivia = ctx.trivia, sKinds = ctx.triviaKindLabels;
@@ -15127,7 +15347,7 @@ function assemble(t2, prog, cfg) {
15127
15347
  ctx._rootTriviaLog = void 0;
15128
15348
  let end;
15129
15349
  try {
15130
- end = run(input, pos, ctx, scans[scanId]);
15350
+ end = run2(input, pos, ctx, scans[scanId]);
15131
15351
  } finally {
15132
15352
  SCAN = sScan;
15133
15353
  ctx.trivia = sTrivia;
@@ -15148,7 +15368,7 @@ function assemble(t2, prog, cfg) {
15148
15368
  };
15149
15369
  }
15150
15370
  return (input, pos, ctx) => {
15151
- const end = run(input, pos, ctx);
15371
+ const end = run2(input, pos, ctx);
15152
15372
  if (end < 0) return FAIL;
15153
15373
  const value = input.slice(pos, end);
15154
15374
  if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf9(value, new PlainSpan10(pos, end)));
@@ -15249,7 +15469,7 @@ function assemble(t2, prog, cfg) {
15249
15469
  return next === void 0 ? cur : next(input, cur, ctx, values, inherited);
15250
15470
  };
15251
15471
  };
15252
- if (REC) {
15472
+ if (REC2) {
15253
15473
  const first2 = linkFirst();
15254
15474
  const firstSync = sentinelFor(code[base + n]);
15255
15475
  const runnerAt = (i) => {
@@ -15738,7 +15958,7 @@ function assemble(t2, prog, cfg) {
15738
15958
  if (n === 2 || n === 3) {
15739
15959
  const a0 = link(code[base]), a1 = link(code[base + 1]);
15740
15960
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
15741
- const predecide = !REC && !cfg.probe && !cfg.coverage && !cfg.trackLines;
15961
+ const predecide = !REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines;
15742
15962
  const p0ip = predecide ? leadingScalarTerminal(code, code[base], 2, true, true) : -1;
15743
15963
  const p1ip = predecide ? leadingScalarTerminal(code, code[base + 1], 2, true, true) : -1;
15744
15964
  const p2ip = predecide && n === 3 ? leadingScalarTerminal(code, code[base + 2], 2, true, true) : -1;
@@ -15968,7 +16188,7 @@ function assemble(t2, prog, cfg) {
15968
16188
  const masked = bindMasked(0, EMPTY_EXPECTED_PREFIX);
15969
16189
  const pBit = [];
15970
16190
  const pRec = [];
15971
- if (!REC && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
16191
+ if (!REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
15972
16192
  for (let i = 0; i < n; i++) {
15973
16193
  for (const terminal of largeChoiceLiteralPretest(code, k, code[base + i]) ?? []) {
15974
16194
  pBit.push(1 << i);
@@ -16256,18 +16476,18 @@ function assemble(t2, prog, cfg) {
16256
16476
  const itemFx = reportItem ? fx[code[ip + 6]] : EMPTY_FX2;
16257
16477
  const collect = op === OP_REP;
16258
16478
  const itemClassIndex = sepIp < 0 ? code[ip + 7] : -1;
16259
- const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
16479
+ const itemClass = !REC2 && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
16260
16480
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
16261
16481
  const sepClassIndex = sepIp >= 0 && !sepLeads && (flags & 8) !== 0 ? code[ip + 8] : -1;
16262
- const sepClass = !REC && sepClassIndex >= 0 && !rowNullable(code, k, sepIp, NULLABLE) ? cc[sepClassIndex] : void 0;
16263
- const gapIp = sepIp < 0 && min === 0 && !REC && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
16482
+ const sepClass = !REC2 && sepClassIndex >= 0 && !rowNullable(code, k, sepIp, NULLABLE) ? cc[sepClassIndex] : void 0;
16483
+ const gapIp = sepIp < 0 && min === 0 && !REC2 && code[code[ip + 1]] === OP_GAPGATE ? code[ip + 1] : -1;
16264
16484
  const gapRow = gapIp >= 0 ? code[gapIp + 2] : -1;
16265
16485
  const gapRe = gapIp >= 0 ? k[code[gapRow + 1]] : void 0;
16266
16486
  const gapFollow = gapIp >= 0 ? cc[code[gapIp + 3]] : void 0;
16267
16487
  const gapOther = gapIp >= 0 && code[gapIp + 4] >= 0 ? cc[code[gapIp + 4]] : void 0;
16268
16488
  const skipBeforeFirst = sepIp < 0 && min === 0;
16269
16489
  const itemClean = sepIp < 0 && CLEAN(code[ip + 1]) && !rowNullable(code, k, code[ip + 1], NULLABLE);
16270
- if (REC) {
16490
+ if (REC2) {
16271
16491
  const itemFx2 = fx[code[ip + 6]];
16272
16492
  const recFx = (flags & 16) !== 0 ? EMPTY_FX2 : itemFx2;
16273
16493
  const sepSent = sentinelFor(code[ip + 7]);
@@ -16477,10 +16697,10 @@ function assemble(t2, prog, cfg) {
16477
16697
  const hasFold = spec.byFold.size > 0;
16478
16698
  const match = spec.match;
16479
16699
  const matchN = match.length;
16480
- const routed = spec.routed;
16700
+ const routed2 = spec.routed;
16481
16701
  const expected = spec.expected;
16482
16702
  const validArm = (arm) => Number.isInteger(arm) && arm >= 0 && arm < n;
16483
- if (routed.length !== n) throw new TypeError("table assembler: malformed dispatch routed arity");
16703
+ if (routed2.length !== n) throw new TypeError("table assembler: malformed dispatch routed arity");
16484
16704
  for (const arm of byKey.values()) if (!validArm(arm)) throw new TypeError("table assembler: malformed dispatch exact arm");
16485
16705
  for (const arm of byFold.values()) if (!validArm(arm)) throw new TypeError("table assembler: malformed dispatch folded arm");
16486
16706
  for (const m of match) if (!validArm(m[3])) throw new TypeError("table assembler: malformed dispatch matcher arm");
@@ -16505,7 +16725,7 @@ function assemble(t2, prog, cfg) {
16505
16725
  const classify = hasFold ? matchBlock === void 0 ? (key) => byKey.get(key) ?? byFold.get(asciiFoldKey(key)) : (key) => byKey.get(key) ?? byFold.get(asciiFoldKey(key)) ?? matchBlock(key) : matchBlock === void 0 ? (key) => byKey.get(key) : (key) => byKey.get(key) ?? matchBlock(key);
16506
16726
  const neverBranch = () => FAIL;
16507
16727
  const bindArms = (start) => {
16508
- const branch = (i) => i < n ? dispatchBranch(link(code[armBase + i]), routed[i] === 1) : neverBranch;
16728
+ const branch = (i) => i < n ? dispatchBranch(link(code[armBase + i]), routed2[i] === 1) : neverBranch;
16509
16729
  const next = start + 4 < n ? bindArms(start + 4) : void 0;
16510
16730
  return dispatchArmBlock(
16511
16731
  start,
@@ -16820,7 +17040,7 @@ function assemble(t2, prog, cfg) {
16820
17040
  lead,
16821
17041
  asciiFoldKey,
16822
17042
  ROUTED_FX,
16823
- REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
17043
+ REC2 ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
16824
17044
  matchesAt,
16825
17045
  recoverScan,
16826
17046
  orSentinel,
@@ -16909,7 +17129,7 @@ function assemble(t2, prog, cfg) {
16909
17129
  // — a strict one emits no reader — and through the SAME `sentinelFor` memo
16910
17130
  // the closure pieces use, so both engines resync to the same combinator
16911
17131
  // object rather than to two separately constructed ones.
16912
- REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
17132
+ REC2 ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
16913
17133
  matchesAt,
16914
17134
  recoverScan,
16915
17135
  orSentinel,
@@ -17124,9 +17344,9 @@ var AssemblyCache = class {
17124
17344
  const trackLines = ctx.trackLines === true;
17125
17345
  const tolerant = ctx._tolerant === true;
17126
17346
  const coverage = ctx._grammarCoverage !== void 0;
17127
- const probe = ctx._probe !== void 0;
17347
+ const probe2 = ctx._probe !== void 0;
17128
17348
  const inheritedTrivia = this.noOwnTrivia && ctx.trivia !== void 0;
17129
- const key = cfgKeyOf(hostCst, trackLines, tolerant, coverage, probe, hostReadsChildren, inheritedTrivia);
17349
+ const key = cfgKeyOf(hostCst, trackLines, tolerant, coverage, probe2, hostReadsChildren, inheritedTrivia);
17130
17350
  let cache = this.byCfg;
17131
17351
  if (hostCaptureTrivia !== void 0) {
17132
17352
  if (this.hostPredicate === hostCaptureTrivia) {
@@ -17146,7 +17366,7 @@ var AssemblyCache = class {
17146
17366
  trackLines,
17147
17367
  tolerant,
17148
17368
  coverage,
17149
- probe,
17369
+ probe: probe2,
17150
17370
  inheritedTrivia
17151
17371
  });
17152
17372
  cache[key] = made;
@@ -18058,36 +18278,36 @@ function findRegexFragments(visited) {
18058
18278
  if (branches.length >= 2) runs.add(branches.join("|"));
18059
18279
  for (let i = 0; i < branches.length; i++) {
18060
18280
  for (let n = 2; n <= Math.min(MAX_RUN, branches.length - i); n++) {
18061
- const run = branches.slice(i, i + n).join("|");
18062
- if (run.length < 4) continue;
18063
- runs.add(run);
18281
+ const run2 = branches.slice(i, i + n).join("|");
18282
+ if (run2.length < 4) continue;
18283
+ runs.add(run2);
18064
18284
  }
18065
18285
  }
18066
18286
  }
18067
- for (const run of runs) {
18068
- let e = byRun.get(run);
18287
+ for (const run2 of runs) {
18288
+ let e = byRun.get(run2);
18069
18289
  if (e === void 0) {
18070
- e = { occ: [], nodes: /* @__PURE__ */ new Set(), branches: run.split("|").length };
18071
- byRun.set(run, e);
18290
+ e = { occ: [], nodes: /* @__PURE__ */ new Set(), branches: run2.split("|").length };
18291
+ byRun.set(run2, e);
18072
18292
  }
18073
18293
  if (e.nodes.has(v.p)) continue;
18074
18294
  e.nodes.add(v.p);
18075
18295
  e.occ.push({ site: v.site, source: d.source });
18076
18296
  }
18077
18297
  }
18078
- 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);
18298
+ 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);
18079
18299
  const kept = [];
18080
18300
  for (const c of candidates) {
18081
18301
  if (kept.some((k) => k.e.occ.length >= c.e.occ.length && `|${k.run}|`.includes(`|${c.run}|`))) continue;
18082
18302
  kept.push(c);
18083
18303
  }
18084
- return kept.map(({ run, e }) => ({
18304
+ return kept.map(({ run: run2, e }) => ({
18085
18305
  kind: "regex-fragment",
18086
- id: `regex-fragment:${run}`,
18087
- fragment: run,
18306
+ id: `regex-fragment:${run2}`,
18307
+ fragment: run2,
18088
18308
  branches: e.branches,
18089
18309
  count: e.occ.length,
18090
- savings: (e.occ.length - 1) * run.length,
18310
+ savings: (e.occ.length - 1) * run2.length,
18091
18311
  sources: e.occ.map((o) => `/${o.source}/`),
18092
18312
  sites: e.occ.map((o) => o.site),
18093
18313
  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.`
@@ -18941,9 +19161,9 @@ function withAmbient(ruleMap, opts) {
18941
19161
  const addTrivia = opts.trivia !== void 0 && rule2._meta.grammarTrivia === void 0;
18942
19162
  const addScanSkip = opts.scanSkip !== void 0 && rule2._meta.grammarScanSkip === void 0;
18943
19163
  if (!addTrivia && !addScanSkip) return [name, rule2];
18944
- let wrapped = wrappers.get(rule2);
18945
- if (wrapped === void 0) {
18946
- wrapped = {
19164
+ let wrapped2 = wrappers.get(rule2);
19165
+ if (wrapped2 === void 0) {
19166
+ wrapped2 = {
18947
19167
  ...rule2,
18948
19168
  _meta: {
18949
19169
  ...rule2._meta,
@@ -18951,9 +19171,9 @@ function withAmbient(ruleMap, opts) {
18951
19171
  ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
18952
19172
  }
18953
19173
  };
18954
- wrappers.set(rule2, wrapped);
19174
+ wrappers.set(rule2, wrapped2);
18955
19175
  }
18956
- return [name, wrapped];
19176
+ return [name, wrapped2];
18957
19177
  });
18958
19178
  }
18959
19179
  function compileRuleMapRunnable(ruleMap, opts = {}) {
@@ -19135,7 +19355,7 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
19135
19355
  const emitOpts = { fnSources: sources };
19136
19356
  const source = printable ? emitTableModule(artifact, { name: "grammar", ...emitOpts }) : "";
19137
19357
  const inlineExpression = printable ? emitTableExpression(artifact, { entry: ENTRY, runtimeRef: opts.runtimeRef ?? "tableRules", ...emitOpts }) : null;
19138
- const runOnce = (input, pos, ctx) => entry(input, pos, ctx);
19358
+ const runOnce2 = (input, pos, ctx) => entry(input, pos, ctx);
19139
19359
  return {
19140
19360
  source,
19141
19361
  inlineExpression,
@@ -19149,10 +19369,10 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
19149
19369
  // parser runs where a combinator does. A caller probing many positions
19150
19370
  // passes one context and pays for none per call.
19151
19371
  parse(input, pos = 0, ctx) {
19152
- return runOnce(input, pos, ctx ?? createParseContext());
19372
+ return runOnce2(input, pos, ctx ?? createParseContext());
19153
19373
  },
19154
19374
  parseWithContext(input, parseCtx, pos = 0) {
19155
- return runOnce(input, pos, parseCtx);
19375
+ return runOnce2(input, pos, parseCtx);
19156
19376
  },
19157
19377
  /**
19158
19378
  * NO LONGER REFUSES. It used to throw unless the caller passed
@@ -19168,7 +19388,7 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
19168
19388
  const errors = [];
19169
19389
  ctx._errors = errors;
19170
19390
  ctx._tolerant = true;
19171
- const r = runOnce(input, pos, ctx);
19391
+ const r = runOnce2(input, pos, ctx);
19172
19392
  return { ...r, errors };
19173
19393
  }
19174
19394
  };
@@ -19413,6 +19633,38 @@ function not(combinator) {
19413
19633
  };
19414
19634
  }
19415
19635
 
19636
+ // src/combinators/withCtx.ts
19637
+ function withCtx(extra, combinator) {
19638
+ const meta = {
19639
+ firstSet: combinator._meta.firstSet,
19640
+ canMatchNewline: combinator._meta.canMatchNewline,
19641
+ isTrivia: false
19642
+ };
19643
+ const parse2 = typeof extra === "function" ? (input, pos, ctx) => {
19644
+ const saved = ctx.state;
19645
+ ctx.state = extra(saved);
19646
+ try {
19647
+ return combinator.parse(input, pos, ctx);
19648
+ } finally {
19649
+ ctx.state = saved;
19650
+ }
19651
+ } : (input, pos, ctx) => {
19652
+ const saved = ctx.state;
19653
+ ctx.state = extra;
19654
+ try {
19655
+ return combinator.parse(input, pos, ctx);
19656
+ } finally {
19657
+ ctx.state = saved;
19658
+ }
19659
+ };
19660
+ return {
19661
+ _tag: "withCtx",
19662
+ _meta: meta,
19663
+ _def: { tag: "withCtx", extra, parser: combinator },
19664
+ parse: parse2
19665
+ };
19666
+ }
19667
+
19416
19668
  // src/analysis/rebuild.ts
19417
19669
  var FROZEN_TAGS = /* @__PURE__ */ new Set([
19418
19670
  "dispatch",
@@ -19426,6 +19678,7 @@ var FROZEN_TAGS = /* @__PURE__ */ new Set([
19426
19678
  "lines",
19427
19679
  "columns"
19428
19680
  ]);
19681
+ var THAWED_TAGS = /* @__PURE__ */ new Set(["dispatch", "withCtx", "routed", "guard"]);
19429
19682
  var CARRIED_META = [
19430
19683
  "grammarTrivia",
19431
19684
  "grammarScanSkip",
@@ -19464,7 +19717,9 @@ function subtreeContains(p, targets) {
19464
19717
  walk(p);
19465
19718
  return hit;
19466
19719
  }
19467
- function rebuildCombinator(root, replacements) {
19720
+ function rebuildCombinator(root, replacements, options = {}) {
19721
+ const thaw = options.thaw === true;
19722
+ const after = options.after ?? ((_p, out) => out);
19468
19723
  const memo = /* @__PURE__ */ new Map();
19469
19724
  const frozenKeys = /* @__PURE__ */ new Set();
19470
19725
  const frozen = [];
@@ -19484,15 +19739,16 @@ function rebuildCombinator(root, replacements) {
19484
19739
  return p;
19485
19740
  }
19486
19741
  const here = p._ruleName ?? rule2;
19487
- if (FROZEN_TAGS.has(d.tag)) {
19742
+ if (FROZEN_TAGS.has(d.tag) && !(thaw && THAWED_TAGS.has(d.tag))) {
19488
19743
  const key = JSON.stringify([here, d.tag]);
19489
19744
  if (!frozenKeys.has(key)) {
19490
19745
  frozenKeys.add(key);
19491
19746
  frozen.push({ tag: d.tag, rule: here });
19492
19747
  }
19493
19748
  for (const t2 of subtreeContains(p, targets)) if (!unapplied.includes(t2)) unapplied.push(t2);
19494
- memo.set(p, p);
19495
- return p;
19749
+ const kept = after(p, p);
19750
+ memo.set(p, kept);
19751
+ return kept;
19496
19752
  }
19497
19753
  if (d.tag === "lazy") {
19498
19754
  let target;
@@ -19529,6 +19785,14 @@ function rebuildCombinator(root, replacements) {
19529
19785
  break;
19530
19786
  }
19531
19787
  case "choice": {
19788
+ if (d.gates.some((g) => g !== null) && thaw) {
19789
+ const unsafeChoice = choice;
19790
+ out = unsafeChoice(...d.parsers.map((arm, i) => {
19791
+ const gate = d.gates[i];
19792
+ return gate ? { combinator: one(arm), gate } : one(arm);
19793
+ }));
19794
+ break;
19795
+ }
19532
19796
  if (d.gates.some((g) => g !== null)) {
19533
19797
  const key = JSON.stringify([here, "choice(gated)"]);
19534
19798
  if (!frozenKeys.has(key)) {
@@ -19536,8 +19800,9 @@ function rebuildCombinator(root, replacements) {
19536
19800
  frozen.push({ tag: "choice(gated)", rule: here });
19537
19801
  }
19538
19802
  for (const t2 of subtreeContains(p, targets)) if (!unapplied.includes(t2)) unapplied.push(t2);
19539
- memo.set(p, p);
19540
- return p;
19803
+ const kept = after(p, p);
19804
+ memo.set(p, kept);
19805
+ return kept;
19541
19806
  }
19542
19807
  const kids = d.parsers.map(one);
19543
19808
  out = choice(...kids);
@@ -19615,13 +19880,33 @@ function rebuildCombinator(root, replacements) {
19615
19880
  frozen.push({ tag: "node(untyped+build+opts)", rule: here });
19616
19881
  }
19617
19882
  for (const t2 of subtreeContains(p, targets)) if (!unapplied.includes(t2)) unapplied.push(t2);
19618
- memo.set(p, p);
19619
- return p;
19883
+ const kept = after(p, p);
19884
+ memo.set(p, kept);
19885
+ return kept;
19620
19886
  }
19621
19887
  out = d.type === void 0 ? d.build === void 0 ? node(one(d.parser), opts) : node(one(d.parser), d.build) : node(d.type, one(d.parser), d.build, opts);
19622
19888
  if (d.buildRawUnused === true && out._def.tag === "node") out._def.buildRawUnused = true;
19623
19889
  break;
19624
19890
  }
19891
+ case "dispatch": {
19892
+ const arms = d.cases.map((e) => when(e.keys, one(e.parser), e.caseInsensitive ? { caseInsensitive: true } : void 0));
19893
+ for (const e of d.matchers ?? []) {
19894
+ arms.push(when(dispatchMatcher(e), one(e.parser), e.caseInsensitive ? { caseInsensitive: true } : void 0));
19895
+ }
19896
+ if (d.otherwise) arms.push(otherwise(one(d.otherwise), d.otherwiseCommit === false ? { commit: false } : void 0));
19897
+ const unsafeDispatch = dispatch;
19898
+ out = unsafeDispatch(one(d.selector), ...arms);
19899
+ break;
19900
+ }
19901
+ case "withCtx":
19902
+ out = withCtx(d.extra, one(d.parser));
19903
+ break;
19904
+ case "routed":
19905
+ out = d.fallback === void 0 ? routed() : routed(one(d.fallback));
19906
+ break;
19907
+ case "guard":
19908
+ out = p;
19909
+ break;
19625
19910
  case "grammar": {
19626
19911
  const inner = one(d.parser);
19627
19912
  out = parser({
@@ -19639,13 +19924,15 @@ function rebuildCombinator(root, replacements) {
19639
19924
  frozen.push({ tag: d.tag, rule: here });
19640
19925
  }
19641
19926
  for (const t2 of subtreeContains(p, targets)) if (!unapplied.includes(t2)) unapplied.push(t2);
19642
- memo.set(p, p);
19643
- return p;
19927
+ const kept = after(p, p);
19928
+ memo.set(p, kept);
19929
+ return kept;
19644
19930
  }
19645
19931
  }
19646
19932
  carryStamps(p, out);
19647
- memo.set(p, out);
19648
- return out;
19933
+ const done = after(p, out);
19934
+ memo.set(p, done);
19935
+ return done;
19649
19936
  };
19650
19937
  const rebuilt = go(root, root._ruleName ?? "<entry>");
19651
19938
  frozen.sort((a, b) => a.rule < b.rule ? -1 : a.rule > b.rule ? 1 : a.tag < b.tag ? -1 : a.tag > b.tag ? 1 : 0);
@@ -20882,9 +21169,715 @@ function measureChoiceCost(c, samples, arms) {
20882
21169
  function armFirstSets(choiceArms2) {
20883
21170
  return choiceArms2.map((a) => ({ firstSet: firstSetOf(a) }));
20884
21171
  }
21172
+
21173
+ // src/cst/trivia-entries.ts
21174
+ var EMPTY_INDICES = Object.freeze([]);
21175
+ var EMPTY_GAPS = Object.freeze([]);
21176
+ var SELECTED_ROOT_STRIDE = 5;
21177
+ function buildRootTriviaIndex(log, labels) {
21178
+ const length = Math.floor(log.length / SELECTED_ROOT_STRIDE);
21179
+ const markerOffset = (i) => i * SELECTED_ROOT_STRIDE;
21180
+ const ownedRangeStart = (i) => log[markerOffset(i)];
21181
+ const ownedRangeEnd = (i) => log[markerOffset(i) + 1];
21182
+ const markerStart = (i) => log[markerOffset(i) + 2];
21183
+ const markerEnd = (i) => log[markerOffset(i) + 3];
21184
+ const markerKind = (i) => log[markerOffset(i) + 4];
21185
+ const entries = {
21186
+ length,
21187
+ labels,
21188
+ stride: SELECTED_ROOT_STRIDE,
21189
+ start: markerStart,
21190
+ end: markerEnd,
21191
+ insertIndex: () => void 0,
21192
+ kindIndex: markerKind,
21193
+ kind(i) {
21194
+ return labels[markerKind(i)];
21195
+ },
21196
+ text(i, input) {
21197
+ return input.slice(markerStart(i), markerEnd(i));
21198
+ }
21199
+ };
21200
+ const lowerBound = (offset, by) => {
21201
+ let lo = 0;
21202
+ let hi = length;
21203
+ while (lo < hi) {
21204
+ const mid = lo + hi >>> 1;
21205
+ if (by(mid) < offset) lo = mid + 1;
21206
+ else hi = mid;
21207
+ }
21208
+ return lo;
21209
+ };
21210
+ const gapRowsAt = (offset, direction) => {
21211
+ const edge = direction === "before" ? ownedRangeEnd : ownedRangeStart;
21212
+ let first = lowerBound(offset, edge);
21213
+ if (first === length || edge(first) !== offset) return void 0;
21214
+ const start = ownedRangeStart(first);
21215
+ const end = ownedRangeEnd(first);
21216
+ while (first > 0 && ownedRangeStart(first - 1) === start && ownedRangeEnd(first - 1) === end) first--;
21217
+ let last = first + 1;
21218
+ while (last < length && ownedRangeStart(last) === start && ownedRangeEnd(last) === end) last++;
21219
+ return [first, last];
21220
+ };
21221
+ const indices = (first, last) => {
21222
+ const out = [];
21223
+ for (let i = first; i < last; i++) out.push(i);
21224
+ return out;
21225
+ };
21226
+ const makeGap = (first, last) => {
21227
+ const start = ownedRangeStart(first);
21228
+ const end = ownedRangeEnd(first);
21229
+ let entryIndices;
21230
+ return {
21231
+ start,
21232
+ end,
21233
+ get entryIndices() {
21234
+ return entryIndices ??= indices(first, last);
21235
+ },
21236
+ hasKind(kind) {
21237
+ for (let i = first; i < last; i++) {
21238
+ if (labels[markerKind(i)] === kind) return true;
21239
+ }
21240
+ return false;
21241
+ },
21242
+ text(input) {
21243
+ return input.slice(start, end);
21244
+ }
21245
+ };
21246
+ };
21247
+ let maps;
21248
+ const getMaps = () => {
21249
+ if (maps !== void 0) return maps;
21250
+ const before = /* @__PURE__ */ new Map();
21251
+ const after = /* @__PURE__ */ new Map();
21252
+ const beforeGaps = /* @__PURE__ */ new Map();
21253
+ const afterGaps = /* @__PURE__ */ new Map();
21254
+ const gaps = [];
21255
+ for (let first = 0; first < length; ) {
21256
+ let last = first + 1;
21257
+ while (last < length && ownedRangeStart(last) === ownedRangeStart(first) && ownedRangeEnd(last) === ownedRangeEnd(first)) last++;
21258
+ const gap = makeGap(first, last);
21259
+ gaps.push(gap);
21260
+ afterGaps.set(gap.start, gap);
21261
+ beforeGaps.set(gap.end, gap);
21262
+ after.set(gap.start, [...gap.entryIndices]);
21263
+ before.set(gap.end, [...gap.entryIndices]);
21264
+ first = last;
21265
+ }
21266
+ maps = { before, after, beforeGaps, afterGaps, gaps };
21267
+ return maps;
21268
+ };
21269
+ return {
21270
+ entries,
21271
+ labels,
21272
+ get before() {
21273
+ return getMaps().before;
21274
+ },
21275
+ get after() {
21276
+ return getMaps().after;
21277
+ },
21278
+ entryIndicesBefore(offset) {
21279
+ const rows = gapRowsAt(offset, "before");
21280
+ return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES;
21281
+ },
21282
+ entryIndicesAfter(offset) {
21283
+ const rows = gapRowsAt(offset, "after");
21284
+ return rows ? indices(rows[0], rows[1]) : EMPTY_INDICES;
21285
+ },
21286
+ gapBefore(offset) {
21287
+ const rows = gapRowsAt(offset, "before");
21288
+ return rows ? makeGap(rows[0], rows[1]) : void 0;
21289
+ },
21290
+ gapAfter(offset) {
21291
+ const rows = gapRowsAt(offset, "after");
21292
+ return rows ? makeGap(rows[0], rows[1]) : void 0;
21293
+ },
21294
+ gaps() {
21295
+ return getMaps().gaps;
21296
+ },
21297
+ gapsWithKind(kind) {
21298
+ const kinds = typeof kind === "string" ? [kind] : kind;
21299
+ const matches2 = [];
21300
+ for (let first = 0; first < length; ) {
21301
+ let last = first + 1;
21302
+ while (last < length && ownedRangeStart(last) === ownedRangeStart(first) && ownedRangeEnd(last) === ownedRangeEnd(first)) last++;
21303
+ let matched = false;
21304
+ for (let i = first; i < last && !matched; i++) {
21305
+ const label2 = labels[markerKind(i)];
21306
+ for (const kind2 of kinds) {
21307
+ if (label2 === kind2) {
21308
+ matched = true;
21309
+ break;
21310
+ }
21311
+ }
21312
+ }
21313
+ if (matched) matches2.push(makeGap(first, last));
21314
+ first = last;
21315
+ }
21316
+ return matches2.length === 0 ? EMPTY_GAPS : matches2;
21317
+ }
21318
+ };
21319
+ }
21320
+
21321
+ // src/functional/run.ts
21322
+ var SELECT_EXAMPLE = "run(entry, input, { rootTrivia: { select: ['blockComment', 'lineComment'] } })";
21323
+ var REMOVED_RUN_RESULT_FIELDS = [
21324
+ [
21325
+ "triviaMap",
21326
+ `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.`
21327
+ ],
21328
+ [
21329
+ "triviaLog",
21330
+ `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.`
21331
+ ],
21332
+ [
21333
+ "triviaKindLabels",
21334
+ `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\`.`
21335
+ ]
21336
+ ];
21337
+ var RunResultRecord = class {
21338
+ constructor(ok, value, span, expected, errors, unconsumedFrom) {
21339
+ this.ok = ok;
21340
+ this.value = value;
21341
+ this.span = span;
21342
+ this.expected = expected;
21343
+ this.errors = errors;
21344
+ this.unconsumedFrom = unconsumedFrom;
21345
+ }
21346
+ };
21347
+ var RunResultWithRootTrivia = class {
21348
+ constructor(ok, value, span, expected, errors, rootTrivia, unconsumedFrom) {
21349
+ this.ok = ok;
21350
+ this.value = value;
21351
+ this.span = span;
21352
+ this.expected = expected;
21353
+ this.errors = errors;
21354
+ this.rootTrivia = rootTrivia;
21355
+ this.unconsumedFrom = unconsumedFrom;
21356
+ }
21357
+ };
21358
+ for (const proto of [RunResultRecord.prototype, RunResultWithRootTrivia.prototype]) {
21359
+ for (const [name, detail] of REMOVED_RUN_RESULT_FIELDS) {
21360
+ Object.defineProperty(proto, name, {
21361
+ configurable: true,
21362
+ enumerable: false,
21363
+ get() {
21364
+ throw new TypeError(
21365
+ `RunResult.${name} was REMOVED in parseman 0.44.0 and has no default replacement. ${detail}`
21366
+ );
21367
+ }
21368
+ });
21369
+ }
21370
+ }
21371
+ var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
21372
+ function ambientTriviaFromRunnable(r) {
21373
+ if (!r) return void 0;
21374
+ if (typeof r === "function") return r._meta?.grammarTrivia;
21375
+ if (r._meta.isTrivia) return void 0;
21376
+ return r._meta.grammarTrivia ?? (r._def.tag === "grammar" ? r._def.triviaParser : void 0);
21377
+ }
21378
+ function triviaKindLabelsFromRunnable(r) {
21379
+ if (!r) return void 0;
21380
+ if (typeof r === "function") return r._meta?.triviaKindLabels;
21381
+ const grammarTrivia = r._meta.grammarTrivia;
21382
+ return grammarTrivia?._meta.triviaKindLabels ?? r._meta.triviaKindLabels ?? (r._def.tag === "grammar" ? r._def.triviaParser?._meta.triviaKindLabels : void 0);
21383
+ }
21384
+ function rootTriviaClassifiedFromRunnable(r) {
21385
+ if (!r) return false;
21386
+ if (typeof r === "function") return r._meta?.rootTriviaClassified === true;
21387
+ const grammarTrivia = r._meta.grammarTrivia;
21388
+ return grammarTrivia?._meta.rootTriviaClassified === true || r._meta.rootTriviaClassified === true;
21389
+ }
21390
+ function makeSelectedRootLabelIndex(labels) {
21391
+ const index = /* @__PURE__ */ Object.create(null);
21392
+ for (let i = 0; i < labels.length; i++) {
21393
+ if (index[labels[i]] === void 0) index[labels[i]] = i;
21394
+ }
21395
+ return index;
21396
+ }
21397
+ function growSpanEnd(span, input, end) {
21398
+ if (span.endLine === void 0 || span.endColumn === void 0) {
21399
+ return { start: span.start, end };
21400
+ }
21401
+ let line = span.endLine;
21402
+ let column = span.endColumn;
21403
+ for (let i = span.end; i < end; i++) {
21404
+ if (input.charCodeAt(i) === 10) {
21405
+ line++;
21406
+ column = 1;
21407
+ } else {
21408
+ column++;
21409
+ }
21410
+ }
21411
+ return { ...span, end, endLine: line, endColumn: column };
21412
+ }
21413
+ function runOnce(entry, input, options) {
21414
+ if (typeof entry !== "function" && typeof entry?.parse !== "function") {
21415
+ throw new TypeError(
21416
+ `run(): start production is ${entry === null ? "null" : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`
21417
+ );
21418
+ }
21419
+ const rootTriviaSelection = options.rootTrivia?.select;
21420
+ const captureRootTrivia = rootTriviaSelection !== void 0 && rootTriviaSelection.length > 0;
21421
+ const selectedRootLog = captureRootTrivia ? [] : void 0;
21422
+ const selectedRootLabelIndex = captureRootTrivia ? makeSelectedRootLabelIndex(rootTriviaSelection) : void 0;
21423
+ const errors = [];
21424
+ const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
21425
+ const grammarScanSkip = typeof entry !== "function" ? entry._meta.grammarScanSkip : void 0;
21426
+ const triviaKindLabels = triviaKindLabelsFromRunnable(entry) ?? triviaKindLabelsFromRunnable(options.trivia);
21427
+ if (captureRootTrivia && triviaKindLabels === void 0) {
21428
+ throw new TypeError("run(): rootTrivia.select requires labeled grammar trivia.");
21429
+ }
21430
+ if (captureRootTrivia) {
21431
+ for (const label2 of rootTriviaSelection) {
21432
+ if (!triviaKindLabels.includes(label2)) {
21433
+ throw new TypeError(`run(): rootTrivia.select contains unknown trivia label ${JSON.stringify(label2)}.`);
21434
+ }
21435
+ }
21436
+ }
21437
+ if (captureRootTrivia && !rootTriviaClassifiedFromRunnable(entry) && !rootTriviaClassifiedFromRunnable(options.trivia)) {
21438
+ throw new TypeError(
21439
+ "run(): rootTrivia.select requires classifiedTrivia() at the root."
21440
+ );
21441
+ }
21442
+ const stamped = entry;
21443
+ assertHostModeCompatible(
21444
+ typeof entry === "function" ? stamped[FUSED_HOST_MODE] ?? "ast" : entry._meta.grammarHostMode ?? "ast",
21445
+ options.build,
21446
+ typeof entry === "function" && stamped[FUSED_HOST_ELIDED] === true
21447
+ );
21448
+ const ctx = createParseContext();
21449
+ if (captureRootTrivia) {
21450
+ ctx._rootTriviaLog = selectedRootLog;
21451
+ ctx._rootTriviaKindIndex = selectedRootLabelIndex;
21452
+ ctx._rootTriviaStrictScopes = true;
21453
+ }
21454
+ ctx._errors = errors;
21455
+ ctx.build = options.build;
21456
+ ctx.state = options.state;
21457
+ if (grammarTrivia !== void 0) ctx.trivia = grammarTrivia;
21458
+ if (triviaKindLabels) ctx.triviaKindLabels = triviaKindLabels;
21459
+ if (grammarScanSkip !== void 0) ctx.scanSkip = grammarScanSkip;
21460
+ if (options.triviaCaptureMask !== void 0) ctx._triviaCaptureMask = options.triviaCaptureMask;
21461
+ if (options.tolerant) {
21462
+ ctx._tolerant = true;
21463
+ ctx._rec = REC;
21464
+ }
21465
+ if (options.instrumentation !== void 0) {
21466
+ ctx._grammarCoverage = options.instrumentation._grammarCoverage;
21467
+ ctx._grammarTrace = options.instrumentation._grammarTrace;
21468
+ }
21469
+ const r = invoke(entry, input, 0, ctx);
21470
+ const value = r.ok ? r.value : void 0;
21471
+ let span = r.span ?? { start: 0, end: 0 };
21472
+ const expected = r.ok ? [] : [...new Set(r.expected ?? [])];
21473
+ let unconsumedFrom = null;
21474
+ if (r.ok) {
21475
+ let pos = span.end;
21476
+ const tail = options.trivia ?? ambientTriviaFromRunnable(entry);
21477
+ if (tail !== void 0 && pos < input.length) {
21478
+ const t2 = invoke(tail, input, pos, createParseContext());
21479
+ if (t2.ok && t2.span.end > pos) {
21480
+ span = growSpanEnd(span, input, t2.span.end);
21481
+ pos = t2.span.end;
21482
+ }
21483
+ }
21484
+ unconsumedFrom = pos < input.length ? pos : null;
21485
+ }
21486
+ return selectedRootLog !== void 0 && selectedRootLog.length > 0 ? new RunResultWithRootTrivia(
21487
+ r.ok,
21488
+ value,
21489
+ span,
21490
+ expected,
21491
+ errors,
21492
+ {
21493
+ rows: selectedRootLog,
21494
+ select: rootTriviaSelection,
21495
+ index: buildRootTriviaIndex(selectedRootLog, rootTriviaSelection)
21496
+ },
21497
+ unconsumedFrom
21498
+ ) : new RunResultRecord(r.ok, value, span, expected, errors, unconsumedFrom);
21499
+ }
21500
+ function run(entry, input, options = {}) {
21501
+ return runOnce(entry, input, options);
21502
+ }
21503
+
21504
+ // src/table/backtrack-probe.ts
21505
+ var LOOKAROUND = /(?<!\\)\(\?<?[=!]/;
21506
+ function hasLookaround(re) {
21507
+ return LOOKAROUND.test(re.source);
21508
+ }
21509
+ function lookaroundExtent(re, input, start, end) {
21510
+ const probe2 = new RegExp(re.source, re.flags.includes("y") ? re.flags : `${re.flags}y`);
21511
+ const same = (n) => {
21512
+ probe2.lastIndex = start;
21513
+ return probe2.test(input.slice(0, end + n)) && probe2.lastIndex === end;
21514
+ };
21515
+ let lo = 0;
21516
+ let hi = input.length - end;
21517
+ while (lo < hi) {
21518
+ const mid = lo + hi >>> 1;
21519
+ if (same(mid)) hi = mid;
21520
+ else lo = mid + 1;
21521
+ }
21522
+ return lo;
21523
+ }
21524
+ var BacktrackRecorder = class {
21525
+ input = "";
21526
+ counts = zeroCounts();
21527
+ look = 0;
21528
+ opaque = 0;
21529
+ last = 0;
21530
+ furthest = 0;
21531
+ begin(input) {
21532
+ this.input = input;
21533
+ this.counts = zeroCounts();
21534
+ this.look = 0;
21535
+ this.opaque = 0;
21536
+ this.last = 0;
21537
+ this.furthest = 0;
21538
+ }
21539
+ /** The counts for the parse `begin` opened. Throws if a region was left open. */
21540
+ end() {
21541
+ if (this.look !== 0 || this.opaque !== 0) {
21542
+ throw new Error(`backtrack probe: unbalanced region (lookahead ${this.look}, opaque ${this.opaque})`);
21543
+ }
21544
+ return this.counts;
21545
+ }
21546
+ consume(start, end) {
21547
+ if (this.opaque > 0 || end <= start) return;
21548
+ const c = this.counts;
21549
+ if (this.look > 0) {
21550
+ c.lookaheadBytes += end - start;
21551
+ return;
21552
+ }
21553
+ if (start < this.last) c.rewinds++;
21554
+ if (start < this.furthest) c.bytesReread += Math.min(end, this.furthest) - start;
21555
+ this.last = end;
21556
+ if (end > this.furthest) this.furthest = end;
21557
+ }
21558
+ enterLookahead() {
21559
+ if (this.opaque === 0) this.counts.lookaheads++;
21560
+ this.look++;
21561
+ }
21562
+ exitLookahead() {
21563
+ this.look--;
21564
+ }
21565
+ regexAttempt() {
21566
+ if (this.opaque === 0) this.counts.regexLookarounds++;
21567
+ }
21568
+ regexMatch(re, start, end) {
21569
+ if (this.opaque === 0) this.counts.regexLookaroundBytes += lookaroundExtent(re, this.input, start, end);
21570
+ this.consume(start, end);
21571
+ }
21572
+ /** A read whose inner steps are not the grammar's (the interpreter's scans). */
21573
+ enterOpaque() {
21574
+ this.opaque++;
21575
+ }
21576
+ exitOpaque() {
21577
+ this.opaque--;
21578
+ }
21579
+ };
21580
+ function zeroCounts() {
21581
+ return { rewinds: 0, bytesReread: 0, lookaheads: 0, lookaheadBytes: 0, regexLookarounds: 0, regexLookaroundBytes: 0 };
21582
+ }
21583
+ function rowStarts(code, roots) {
21584
+ const seen = /* @__PURE__ */ new Set();
21585
+ const stack = [...roots];
21586
+ const kids = [];
21587
+ while (stack.length > 0) {
21588
+ const ip = stack.pop();
21589
+ if (seen.has(ip)) continue;
21590
+ seen.add(ip);
21591
+ kids.length = 0;
21592
+ childSlots(code, ip, kids);
21593
+ for (const c of kids) stack.push(c);
21594
+ }
21595
+ return [...seen, code.length].sort((a, b) => a - b);
21596
+ }
21597
+ function probeProgram(source, rec) {
21598
+ const prog = expandCompact(source);
21599
+ const code = [...prog.code];
21600
+ const fns = [...prog.fns];
21601
+ const fx = [...prog.fx, []];
21602
+ const noFx = fx.length - 1;
21603
+ const row = (...words) => {
21604
+ const ip = code.length;
21605
+ code.push(...words);
21606
+ return ip;
21607
+ };
21608
+ const fn = (f) => fns.push(f) - 1;
21609
+ const slots = [];
21610
+ const extraIps = assemblyExtraIps(prog);
21611
+ const starts = rowStarts(prog.code, [...Object.values(prog.rules), ...extraIps]);
21612
+ const copies = /* @__PURE__ */ new Map();
21613
+ function copyOf(ip) {
21614
+ const hit = copies.get(ip);
21615
+ if (hit !== void 0) return hit;
21616
+ slots.length = 0;
21617
+ childSlotOffsets(prog.code, ip, slots);
21618
+ if (slots.length === 0) {
21619
+ copies.set(ip, ip);
21620
+ return ip;
21621
+ }
21622
+ const next = starts[starts.findIndex((s) => s > ip)];
21623
+ const at = code.length;
21624
+ copies.set(ip, at);
21625
+ for (let i = ip; i < next; i++) code.push(prog.code[i]);
21626
+ const own = slots.map((s) => s - ip);
21627
+ for (const o of own) code[at + o] = copyOf(prog.code[ip + o]);
21628
+ return at;
21629
+ }
21630
+ const ref2 = (r) => [copyOf(r[0]), r[1]];
21631
+ const scans = prog.scans?.map((s) => ({
21632
+ ...s,
21633
+ skip: s.skip.map(ref2),
21634
+ ...s.sentinel === void 0 ? {} : { sentinel: ref2(s.sentinel) },
21635
+ ...s.recoverAt === void 0 ? {} : { recoverAt: ref2(s.recoverAt) },
21636
+ ...s.stopAt === void 0 ? {} : { stopAt: ref2(s.stopAt) },
21637
+ ...s.prefix === void 0 ? {} : { prefix: ref2(s.prefix) },
21638
+ ...s.body === void 0 ? {} : { body: ref2(s.body) },
21639
+ ...s.first === void 0 ? {} : { first: ref2(s.first) },
21640
+ ...s.lazy === void 0 ? {} : { lazy: ref2(s.lazy) }
21641
+ }));
21642
+ const scanSkip = prog.scanSkip?.map((set) => set.map(ref2));
21643
+ const report = fn((v, span) => {
21644
+ rec.consume(span.start, span.end);
21645
+ return v;
21646
+ });
21647
+ const lookIn = fn(() => {
21648
+ rec.enterLookahead();
21649
+ return true;
21650
+ });
21651
+ const lookOut = fn((v) => {
21652
+ rec.exitLookahead();
21653
+ return v;
21654
+ });
21655
+ const regexTry = fn(() => {
21656
+ rec.regexAttempt();
21657
+ return true;
21658
+ });
21659
+ const lookahead = (child, fxi) => row(
21660
+ OP_ARMGATE,
21661
+ lookIn,
21662
+ row(OP_XFORM, lookOut, row(OP_NOT, row(OP_XFORM, lookOut, replacement(child) ?? child), fxi)),
21663
+ noFx
21664
+ );
21665
+ const replaced = /* @__PURE__ */ new Map();
21666
+ function replacement(c) {
21667
+ const hit = replaced.get(c);
21668
+ if (hit !== void 0) return hit;
21669
+ let r;
21670
+ switch (code[c]) {
21671
+ case OP_LIT:
21672
+ case OP_LIT_TRACK:
21673
+ case OP_LIT_CI:
21674
+ case OP_LIT_CI_TRACK:
21675
+ case OP_LEX_BODY:
21676
+ case OP_LEX_PROGRAM:
21677
+ case OP_SCAN:
21678
+ case OP_LIVE:
21679
+ r = row(OP_XFORM, report, c);
21680
+ break;
21681
+ case OP_RX:
21682
+ case OP_RX_TRACK: {
21683
+ const re = prog.k[code[c + 1]];
21684
+ r = hasLookaround(re) ? row(
21685
+ OP_ARMGATE,
21686
+ regexTry,
21687
+ row(OP_XFORM, fn((v, span) => {
21688
+ rec.regexMatch(re, span.start, span.end);
21689
+ return v;
21690
+ }), c),
21691
+ noFx
21692
+ ) : row(OP_XFORM, report, c);
21693
+ break;
21694
+ }
21695
+ case OP_NOT:
21696
+ r = lookahead(code[c + 1], code[c + 2]);
21697
+ break;
21698
+ case OP_PEEK:
21699
+ r = row(OP_NOT, lookahead(code[c + 1], code[c + 2]), code[c + 2]);
21700
+ break;
21701
+ }
21702
+ if (r !== void 0) replaced.set(c, r);
21703
+ return r;
21704
+ }
21705
+ const rules = {};
21706
+ const stack = [];
21707
+ for (const [name, ip] of Object.entries(prog.rules)) {
21708
+ rules[name] = replacement(ip) ?? ip;
21709
+ stack.push(ip);
21710
+ }
21711
+ const seen = /* @__PURE__ */ new Set();
21712
+ while (stack.length > 0) {
21713
+ const ip = stack.pop();
21714
+ if (seen.has(ip)) continue;
21715
+ seen.add(ip);
21716
+ slots.length = 0;
21717
+ if (!childSlotOffsets(code, ip, slots)) throw new Error(`backtrack probe: unknown opcode ${String(code[ip])} at ${ip}`);
21718
+ for (let i = 0; i < slots.length; i++) {
21719
+ const s = slots[i];
21720
+ const c = code[s];
21721
+ stack.push(c);
21722
+ if (code[ip] === OP_GREEDY && i > 0) continue;
21723
+ const r = replacement(c);
21724
+ if (r !== void 0) code[s] = r;
21725
+ }
21726
+ }
21727
+ const out = Object.fromEntries(Object.entries(prog));
21728
+ return {
21729
+ ...out,
21730
+ code,
21731
+ fns,
21732
+ fx,
21733
+ rules,
21734
+ asm: [],
21735
+ ...scans === void 0 ? {} : { scans },
21736
+ ...scanSkip === void 0 ? {} : { scanSkip }
21737
+ };
21738
+ }
21739
+
21740
+ // src/analysis/backtrack.ts
21741
+ function isCombinator3(x) {
21742
+ return typeof x === "object" && x !== null && "_def" in x && typeof x.parse === "function";
21743
+ }
21744
+ function isProgram(x) {
21745
+ return Array.isArray(x.code) || Array.isArray(x.c);
21746
+ }
21747
+ function ruleEntries(grammar) {
21748
+ const out = [];
21749
+ for (const [name, value] of Object.entries(grammar)) {
21750
+ if (!isCombinator3(value)) continue;
21751
+ if (value._def.tag === "lazy") {
21752
+ try {
21753
+ value._def.thunk();
21754
+ } catch {
21755
+ continue;
21756
+ }
21757
+ }
21758
+ out.push([name, value]);
21759
+ }
21760
+ return out;
21761
+ }
21762
+ function outcome(r) {
21763
+ return digestValue({ ok: r.ok, value: r.value, span: r.span, unconsumedFrom: r.unconsumedFrom });
21764
+ }
21765
+ function tableOf(grammar) {
21766
+ if (isProgram(grammar)) return expandCompact(grammar);
21767
+ const refusals = [];
21768
+ const compiled = compileRuleMapRunnable(ruleEntries(grammar), { refusals });
21769
+ if (compiled === null) throw new Error(`backtrackProbe: this grammar could not be encoded to a table: ${refusals.join("; ")}`);
21770
+ return compiled.prog;
21771
+ }
21772
+ function backtrackProbe(grammar, engine = "closure") {
21773
+ const rec = new BacktrackRecorder();
21774
+ if (engine === "interpreter") {
21775
+ if (isProgram(grammar)) throw new TypeError("backtrackProbe: the interpreter runs a rule map of combinators, not a table program");
21776
+ const rules2 = Object.fromEntries(ruleEntries(grammar));
21777
+ const built = /* @__PURE__ */ new Map();
21778
+ const instrument = instrumentFor(rec);
21779
+ return probe(rec, "interpreter", (entry) => ruleOf(rules2, entry), (entry) => {
21780
+ let hit = built.get(entry);
21781
+ if (hit === void 0) {
21782
+ hit = rebuildCombinator(ruleOf(rules2, entry), /* @__PURE__ */ new Map(), { thaw: true, after: instrument }).root;
21783
+ built.set(entry, hit);
21784
+ }
21785
+ return hit;
21786
+ });
21787
+ }
21788
+ if (engine !== "closure") throw new TypeError(`backtrackProbe: no engine ${JSON.stringify(engine)}; it runs 'closure' or 'interpreter'`);
21789
+ const prog = tableOf(grammar);
21790
+ const base = tableRules({ ...prog, asm: [] });
21791
+ const rules = tableRules(probeProgram(prog, rec));
21792
+ return probe(rec, "closure", (entry) => ruleOf(base, entry), (entry) => ruleOf(rules, entry));
21793
+ }
21794
+ function probe(rec, ran, plain2, probed) {
21795
+ return {
21796
+ engine: { ran },
21797
+ measure(entry, input, options = {}) {
21798
+ const result = run(plain2(entry), input, options);
21799
+ rec.begin(input);
21800
+ const probedResult = run(probed(entry), input, options);
21801
+ const counts = rec.end();
21802
+ if (outcome(probedResult) !== outcome(result)) {
21803
+ throw new Error(`backtrackProbe: the probed ${ran} parse of rule ${JSON.stringify(entry)} differs from the plain one; its counts would describe another parse`);
21804
+ }
21805
+ return { result, counts };
21806
+ }
21807
+ };
21808
+ }
21809
+ function ruleOf(rules, entry) {
21810
+ if (!Object.hasOwn(rules, entry)) throw new Error(`backtrackProbe: the grammar has no rule ${JSON.stringify(entry)}`);
21811
+ return rules[entry];
21812
+ }
21813
+ function wrapped(out, parse2) {
21814
+ const w = { _tag: out._tag, _meta: out._meta, _def: out._def, parse: parse2 };
21815
+ const name = out._ruleName;
21816
+ if (name !== void 0) w._ruleName = name;
21817
+ return w;
21818
+ }
21819
+ function instrumentFor(rec) {
21820
+ return (_original, out) => {
21821
+ const d = out._def;
21822
+ switch (d.tag) {
21823
+ case "literal":
21824
+ case "keywords":
21825
+ return wrapped(out, (input, pos, ctx) => {
21826
+ const r = out.parse(input, pos, ctx);
21827
+ if (r.ok) rec.consume(r.span.start, r.span.end);
21828
+ return r;
21829
+ });
21830
+ case "regex": {
21831
+ const re = new RegExp(d.source, d.flags);
21832
+ if (!hasLookaround(re)) {
21833
+ return wrapped(out, (input, pos, ctx) => {
21834
+ const r = out.parse(input, pos, ctx);
21835
+ if (r.ok) rec.consume(r.span.start, r.span.end);
21836
+ return r;
21837
+ });
21838
+ }
21839
+ return wrapped(out, (input, pos, ctx) => {
21840
+ rec.regexAttempt();
21841
+ const r = out.parse(input, pos, ctx);
21842
+ if (r.ok) rec.regexMatch(re, r.span.start, r.span.end);
21843
+ return r;
21844
+ });
21845
+ }
21846
+ case "scanTo":
21847
+ case "lines":
21848
+ case "columns":
21849
+ case "recover":
21850
+ case "unknown":
21851
+ return wrapped(out, (input, pos, ctx) => {
21852
+ rec.enterOpaque();
21853
+ let r;
21854
+ try {
21855
+ r = out.parse(input, pos, ctx);
21856
+ } finally {
21857
+ rec.exitOpaque();
21858
+ }
21859
+ if (r.ok) rec.consume(r.span.start, r.span.end);
21860
+ return r;
21861
+ });
21862
+ case "not":
21863
+ case "peek":
21864
+ return wrapped(out, (input, pos, ctx) => {
21865
+ rec.enterLookahead();
21866
+ try {
21867
+ return out.parse(input, pos, ctx);
21868
+ } finally {
21869
+ rec.exitLookahead();
21870
+ }
21871
+ });
21872
+ default:
21873
+ return out;
21874
+ }
21875
+ };
21876
+ }
20885
21877
  export {
20886
21878
  applyFixEdits,
20887
21879
  armFirstSets,
21880
+ backtrackProbe,
20888
21881
  diagnosisLines,
20889
21882
  fixReportLines,
20890
21883
  groupDigits,