parseman 0.27.1 → 0.28.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 (49) hide show
  1. package/README.md +1 -1
  2. package/dist/combinators/attempt.d.ts +8 -0
  3. package/dist/combinators/attempt.d.ts.map +1 -0
  4. package/dist/combinators/expect.d.ts.map +1 -1
  5. package/dist/combinators/first-set.d.ts.map +1 -1
  6. package/dist/combinators/scanTo.d.ts.map +1 -1
  7. package/dist/combinators/token.d.ts +12 -0
  8. package/dist/combinators/token.d.ts.map +1 -1
  9. package/dist/compiler/build-arity.d.ts +8 -4
  10. package/dist/compiler/build-arity.d.ts.map +1 -1
  11. package/dist/compiler/codegen.d.ts +12 -0
  12. package/dist/compiler/codegen.d.ts.map +1 -1
  13. package/dist/compiler/fields.d.ts.map +1 -1
  14. package/dist/compiler/grammar-coverage-ids.d.ts +19 -0
  15. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -0
  16. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  17. package/dist/compiler/linker.d.ts +12 -1
  18. package/dist/compiler/linker.d.ts.map +1 -1
  19. package/dist/compiler/value-usage.d.ts.map +1 -1
  20. package/dist/coverage.d.ts +68 -0
  21. package/dist/coverage.d.ts.map +1 -0
  22. package/dist/functional/doc.d.ts.map +1 -1
  23. package/dist/functional/run.d.ts +13 -0
  24. package/dist/functional/run.d.ts.map +1 -1
  25. package/dist/index.cjs +816 -77
  26. package/dist/index.cjs.map +4 -4
  27. package/dist/index.d.ts +5 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +816 -77
  30. package/dist/index.js.map +4 -4
  31. package/dist/language-service/index.cjs +4 -1
  32. package/dist/language-service/index.cjs.map +2 -2
  33. package/dist/language-service/index.js +4 -1
  34. package/dist/language-service/index.js.map +2 -2
  35. package/dist/plugin/evaluator.d.ts.map +1 -1
  36. package/dist/plugin/index.cjs +786 -113
  37. package/dist/plugin/index.cjs.map +4 -4
  38. package/dist/plugin/index.d.ts +4 -1
  39. package/dist/plugin/index.d.ts.map +1 -1
  40. package/dist/plugin/index.js +786 -113
  41. package/dist/plugin/index.js.map +4 -4
  42. package/dist/spec/index.cjs +3 -0
  43. package/dist/spec/index.cjs.map +2 -2
  44. package/dist/spec/index.js +3 -0
  45. package/dist/spec/index.js.map +2 -2
  46. package/dist/spec/model.d.ts.map +1 -1
  47. package/dist/types.d.ts +12 -0
  48. package/dist/types.d.ts.map +1 -1
  49. package/package.json +2 -1
package/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ GRAMMAR_COVERAGE_DEFINITIONS: () => GRAMMAR_COVERAGE_DEFINITIONS,
23
24
  OffsetIndex: () => OffsetIndex,
24
25
  absoluteSpanAt: () => absoluteSpanAt,
25
26
  absoluteSpanCST: () => absoluteSpanCST,
@@ -27,6 +28,7 @@ __export(index_exports, {
27
28
  absolutizeCST: () => absolutizeCST,
28
29
  annotateSpan: () => annotateSpan,
29
30
  applyEdit: () => applyEdit,
31
+ attempt: () => attempt,
30
32
  balanced: () => balanced,
31
33
  blankLinesIn: () => blankLinesIn,
32
34
  buildLineIndex: () => buildLineIndex,
@@ -36,20 +38,28 @@ __export(index_exports, {
36
38
  collectLeafSlots: () => collectLeafSlots,
37
39
  commentsIn: () => commentsIn,
38
40
  compile: () => compile,
41
+ compiledGrammarCoverageDefinitions: () => compiledGrammarCoverageDefinitions,
39
42
  completionsAt: () => completionsAt,
40
43
  compose: () => compose,
44
+ composeLeaf: () => composeLeaf,
45
+ composedGrammarCoverageDefinitions: () => composedGrammarCoverageDefinitions,
46
+ createGrammarCoverageCollector: () => createGrammarCoverageCollector,
47
+ createGrammarInstrumentationContext: () => createGrammarInstrumentationContext,
48
+ createGrammarTraceSink: () => createGrammarTraceSink,
41
49
  createVisitor: () => createVisitor,
42
50
  cstBuildHost: () => cstBuildHost,
43
51
  expect: () => expect,
44
52
  field: () => field,
45
53
  gapIsSignificant: () => gapIsSignificant,
46
54
  gapText: () => gapText,
55
+ grammarCoverageDefinitions: () => grammarCoverageDefinitions,
47
56
  guard: () => guard,
48
57
  indentMixed: () => indentMixed,
49
58
  indentWidth: () => indentWidth,
50
59
  isParseError: () => isParseError,
51
60
  keywords: () => keywords,
52
61
  label: () => label,
62
+ leaf: () => leaf,
53
63
  lineBreaksIn: () => lineBreaksIn,
54
64
  lineStartWithin: () => lineStartWithin,
55
65
  literal: () => literal,
@@ -70,6 +80,7 @@ __export(index_exports, {
70
80
  relativizeCST: () => relativizeCST,
71
81
  rules: () => rules,
72
82
  run: () => run,
83
+ runWithGrammarCoverage: () => runWithGrammarCoverage,
73
84
  scanTo: () => scanTo,
74
85
  sepBy: () => sepBy,
75
86
  sequence: () => sequence,
@@ -147,6 +158,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
147
158
  case "label":
148
159
  case "trivia":
149
160
  case "token":
161
+ case "leaf":
150
162
  case "expect":
151
163
  case "withCtx":
152
164
  case "node":
@@ -210,6 +222,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
210
222
  case "label":
211
223
  case "trivia":
212
224
  case "token":
225
+ case "leaf":
213
226
  case "node":
214
227
  case "grammar":
215
228
  case "expect":
@@ -278,10 +291,10 @@ function beginCstNodeCapture(ctx) {
278
291
  }
279
292
  function finishCstBuf(buf) {
280
293
  if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL };
281
- const children = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
294
+ const children2 = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
282
295
  const rawChildren = buf.raw ?? (buf.rawSingle !== void 0 ? [buf.rawSingle] : EMPTY);
283
296
  const triviaLog = buf.tl ?? EMPTY_TL;
284
- return { children, rawChildren, triviaLog };
297
+ return { children: children2, rawChildren, triviaLog };
285
298
  }
286
299
  function endCstNodeCapture(ctx, saved) {
287
300
  const materialized = finishCstBuf(ctx._cstBuf);
@@ -293,8 +306,8 @@ function endCstNodeCapture(ctx, saved) {
293
306
  ctx.captureTrivia = saved.cap;
294
307
  return materialized;
295
308
  }
296
- function pushCstLeaf(ctx, leaf) {
297
- pushCstChild(ctx, leaf, leaf);
309
+ function pushCstLeaf(ctx, leaf2) {
310
+ pushCstChild(ctx, leaf2, leaf2);
298
311
  }
299
312
  function pushCstChild(ctx, built, rawEntry) {
300
313
  const b = ctx._cstBuf;
@@ -1028,10 +1041,13 @@ function deriveExpected(c) {
1028
1041
  return def.parsers.flatMap(deriveExpected);
1029
1042
  case "sequence":
1030
1043
  return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
1044
+ case "attempt":
1045
+ return deriveExpected(def.parser);
1031
1046
  case "node":
1032
1047
  case "grammar":
1033
1048
  case "trivia":
1034
1049
  case "token":
1050
+ case "leaf":
1035
1051
  case "optional":
1036
1052
  case "many":
1037
1053
  case "oneOrMore":
@@ -1809,6 +1825,24 @@ function sequence(...parsers) {
1809
1825
  };
1810
1826
  }
1811
1827
 
1828
+ // src/combinators/attempt.ts
1829
+ function attempt(parser2) {
1830
+ return {
1831
+ _tag: "attempt",
1832
+ _meta: parser2._meta,
1833
+ _def: { tag: "attempt", parser: parser2 },
1834
+ parse(input, pos, ctx) {
1835
+ const mark = saveCstMark(ctx);
1836
+ const log = ctx._triviaLog?.length;
1837
+ const result = parser2.parse(input, pos, ctx);
1838
+ if (result.ok) return result;
1839
+ rollbackCstCapture(ctx, mark);
1840
+ if (log !== void 0 && ctx._triviaLog) ctx._triviaLog.length = log;
1841
+ return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
1842
+ }
1843
+ };
1844
+ }
1845
+
1812
1846
  // src/combinators/repeat.ts
1813
1847
  function repItem(combinator, input, cur, ctx) {
1814
1848
  const mark = saveTriviaMark(ctx);
@@ -2118,10 +2152,14 @@ function markUnusedValues(root) {
2118
2152
  case "label":
2119
2153
  case "trivia":
2120
2154
  case "token":
2155
+ case "attempt":
2121
2156
  case "withCtx":
2122
2157
  case "expect":
2123
2158
  visit(def.parser, consumed);
2124
2159
  return;
2160
+ case "leaf":
2161
+ visit(def.parser, true);
2162
+ return;
2125
2163
  case "lazy":
2126
2164
  return;
2127
2165
  default:
@@ -2244,6 +2282,20 @@ function confirmedBuildArity(src) {
2244
2282
  if (/\barguments\b/.test(s)) return null;
2245
2283
  return parts.length;
2246
2284
  }
2285
+ function buildReadsChildren(def) {
2286
+ if (!def.build) return true;
2287
+ const src = def.buildSrc ?? def.build.toString();
2288
+ const arity = confirmedBuildArity(src);
2289
+ if (arity === null) return true;
2290
+ return arity >= 1;
2291
+ }
2292
+ function buildReadsRaw(def) {
2293
+ if (!def.build) return true;
2294
+ const src = def.buildSrc ?? def.build.toString();
2295
+ const arity = confirmedBuildArity(src);
2296
+ if (arity === null) return true;
2297
+ return arity >= 4;
2298
+ }
2247
2299
  function buildReadsTrivia(def) {
2248
2300
  if (!def.build) return true;
2249
2301
  const src = def.buildSrc ?? def.build.toString();
@@ -2299,9 +2351,11 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
2299
2351
  case "many":
2300
2352
  case "oneOrMore":
2301
2353
  case "optional":
2354
+ case "attempt":
2302
2355
  case "transform":
2303
2356
  case "trivia":
2304
2357
  case "token":
2358
+ case "leaf":
2305
2359
  case "label":
2306
2360
  case "expect":
2307
2361
  case "withCtx":
@@ -2334,9 +2388,11 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
2334
2388
  case "keywords":
2335
2389
  case "guard":
2336
2390
  case "token":
2391
+ case "leaf":
2337
2392
  return false;
2338
2393
  // Transparent single-child wrappers: recurse.
2339
2394
  case "optional":
2395
+ case "attempt":
2340
2396
  case "transform":
2341
2397
  case "trivia":
2342
2398
  case "label":
@@ -2383,6 +2439,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
2383
2439
  case "many":
2384
2440
  case "oneOrMore":
2385
2441
  case "optional":
2442
+ case "attempt":
2386
2443
  case "transform":
2387
2444
  case "trivia":
2388
2445
  case "token":
@@ -2392,6 +2449,10 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
2392
2449
  case "withCtx":
2393
2450
  case "not":
2394
2451
  return parserEnablesTriviaCapture(d.parser, seen);
2452
+ // leaf() clears the inner CST/trivia collector and publishes only its own
2453
+ // terminal result. An inner parser() capture cannot reach this node.
2454
+ case "leaf":
2455
+ return false;
2395
2456
  case "lazy": {
2396
2457
  try {
2397
2458
  return parserEnablesTriviaCapture(d.thunk(), seen);
@@ -2470,12 +2531,12 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2470
2531
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
2471
2532
  ctx._fields = savedFields;
2472
2533
  ctx._triviaCaptureMask = savedMask;
2473
- const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
2534
+ const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
2474
2535
  if (!r.ok) return r;
2475
2536
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2476
2537
  const nodeType = def.type ?? missingInferredType();
2477
2538
  const cstOutput = ctx.build?._parsemanCstOutput === true;
2478
- const built = unwrap && children.length === 1 ? unwrapChild(children[0]) : collapse && children.length === 1 ? children[0] : !build && ctx.build?._parsemanCstCollapse && children.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children[0], children, rawChildren) ? children[0] : build ? cstOutput && ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, st) : build(children, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
2539
+ const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : !build && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, st) : build(children2, fields, r.span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, r.span, rawChildren, triviaLog, st) : { _tag: "node", type: nodeType, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
2479
2540
  const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span: r.span };
2480
2541
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2481
2542
  pushCstChild(ctx, built, rawEntry);
@@ -2706,6 +2767,141 @@ function token(root) {
2706
2767
  }
2707
2768
  };
2708
2769
  }
2770
+ function leaf(root, fn) {
2771
+ const meta = {
2772
+ firstSet: root._meta.firstSet,
2773
+ canMatchNewline: root._meta.canMatchNewline,
2774
+ isTrivia: false
2775
+ };
2776
+ return {
2777
+ _tag: "leaf",
2778
+ _meta: meta,
2779
+ _def: { tag: "leaf", parser: root, fn },
2780
+ parse(input, pos, ctx) {
2781
+ const savedBuf = ctx._cstBuf;
2782
+ const savedChildren = ctx._cstChildren;
2783
+ const savedLeaves = ctx._cstLeaves;
2784
+ const savedRaw = ctx._cstRawChildren;
2785
+ const savedTriviaLog = ctx._cstTriviaLog;
2786
+ const savedOuterTriviaLog = ctx._triviaLog;
2787
+ const wasCapturing = cstCaptureActive(ctx);
2788
+ ctx._cstBuf = void 0;
2789
+ ctx._cstChildren = void 0;
2790
+ ctx._cstLeaves = void 0;
2791
+ ctx._cstRawChildren = void 0;
2792
+ ctx._cstTriviaLog = void 0;
2793
+ delete ctx._triviaLog;
2794
+ let result;
2795
+ try {
2796
+ result = root.parse(input, pos, ctx);
2797
+ } finally {
2798
+ ctx._cstBuf = savedBuf;
2799
+ ctx._cstChildren = savedChildren;
2800
+ ctx._cstLeaves = savedLeaves;
2801
+ ctx._cstRawChildren = savedRaw;
2802
+ ctx._cstTriviaLog = savedTriviaLog;
2803
+ if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
2804
+ else ctx._triviaLog = savedOuterTriviaLog;
2805
+ }
2806
+ if (!result.ok) return result;
2807
+ const value = fn(result.value, result.span);
2808
+ if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span: result.span });
2809
+ return { ok: true, value, span: result.span };
2810
+ }
2811
+ };
2812
+ }
2813
+
2814
+ // src/compiler/grammar-coverage-ids.ts
2815
+ function children(def, winners) {
2816
+ switch (def.tag) {
2817
+ case "sequence":
2818
+ case "choice":
2819
+ return def.parsers;
2820
+ case "many":
2821
+ case "oneOrMore":
2822
+ case "optional":
2823
+ case "attempt":
2824
+ case "transform":
2825
+ case "trivia":
2826
+ case "token":
2827
+ case "leaf":
2828
+ case "label":
2829
+ case "field":
2830
+ case "grammar":
2831
+ case "not":
2832
+ case "node":
2833
+ case "guard":
2834
+ case "withCtx":
2835
+ case "recover":
2836
+ case "expect":
2837
+ return "parser" in def ? [def.parser] : [];
2838
+ case "sepBy":
2839
+ return [def.parser, def.separator];
2840
+ case "skip":
2841
+ return [def.main, def.skipped];
2842
+ case "scanTo":
2843
+ return [def.sentinel, ...def.skip];
2844
+ case "lazy": {
2845
+ let resolved;
2846
+ try {
2847
+ resolved = def.thunk();
2848
+ } catch {
2849
+ return [];
2850
+ }
2851
+ const name = resolved._ruleName;
2852
+ return name && winners?.[name] ? [winners[name]] : [resolved];
2853
+ }
2854
+ default:
2855
+ return [];
2856
+ }
2857
+ }
2858
+ function buildGrammarPlan(entry, winners) {
2859
+ const definitions = /* @__PURE__ */ new Map();
2860
+ const choices = /* @__PURE__ */ new WeakMap();
2861
+ const attempts = /* @__PURE__ */ new WeakMap();
2862
+ const labels = /* @__PURE__ */ new WeakMap();
2863
+ const rules2 = /* @__PURE__ */ new WeakMap();
2864
+ const winnerNames = /* @__PURE__ */ new Map();
2865
+ if (winners) {
2866
+ for (const [name, winner] of Object.entries(winners)) {
2867
+ winnerNames.set(winner, name);
2868
+ if (winner._def.tag === "lazy") {
2869
+ try {
2870
+ winnerNames.set(winner._def.thunk(), name);
2871
+ } catch {
2872
+ }
2873
+ }
2874
+ }
2875
+ }
2876
+ const seen = /* @__PURE__ */ new Set();
2877
+ const visit = (parser2, path) => {
2878
+ if (seen.has(parser2)) return;
2879
+ seen.add(parser2);
2880
+ const rule = winnerNames.get(parser2) ?? parser2._ruleName;
2881
+ if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
2882
+ const id = `rule:${rule}`;
2883
+ definitions.set(id, { id, kind: "rule" });
2884
+ rules2.set(parser2, id);
2885
+ }
2886
+ if (parser2._def.tag === "choice") {
2887
+ const ids = parser2._def.parsers.map((_, index) => `choice:${path}/arm:${index}`);
2888
+ choices.set(parser2, ids);
2889
+ ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
2890
+ }
2891
+ if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path}`);
2892
+ if (parser2._def.tag === "label") {
2893
+ const id = `label:${path}`;
2894
+ labels.set(parser2, [id]);
2895
+ definitions.set(id, { id, kind: "label" });
2896
+ }
2897
+ children(parser2._def, winners).forEach((child, index) => visit(child, `${path}/${parser2._def.tag}:${index}`));
2898
+ };
2899
+ const roots = Array.isArray(entry) ? entry : [entry];
2900
+ for (const root of roots) {
2901
+ visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
2902
+ }
2903
+ return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
2904
+ }
2709
2905
 
2710
2906
  // src/compiler/scannable-run.ts
2711
2907
  var BACKSLASH = 92;
@@ -4090,21 +4286,23 @@ function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
4090
4286
  }
4091
4287
  function failBody(ctx, expected, posExpr) {
4092
4288
  const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
4289
+ const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
4093
4290
  if (ctx.failLabel) {
4094
- if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4095
- return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
4291
+ if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4292
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
4096
4293
  }
4097
- return probe + failReturn(expected, posExpr);
4294
+ return probe + trace + failReturn(expected, posExpr);
4098
4295
  }
4099
4296
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
4100
4297
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
4298
+ const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
4101
4299
  if (ctx.failLabel) {
4102
- if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4300
+ if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4103
4301
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
4104
- return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
4302
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
4105
4303
  }
4106
- if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
4107
- return probe + failReturnArr(expectedArr, posExpr);
4304
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
4305
+ return probe + trace + failReturnArr(expectedArr, posExpr);
4108
4306
  }
4109
4307
  function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
4110
4308
  return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
@@ -4139,7 +4337,7 @@ var NAMED_FN_END = "_pfEnd";
4139
4337
  function namedFnPrelude() {
4140
4338
  return [`const ${NAMED_FN_FAIL} = {}`, `let ${NAMED_FN_END}`];
4141
4339
  }
4142
- function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
4340
+ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar, failureRuleId) {
4143
4341
  ctx.namedFnDecls.push([
4144
4342
  `function ${fnName}(input, _pos, _ctx) {`,
4145
4343
  ` _pfail: {`,
@@ -4147,6 +4345,7 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
4147
4345
  ` ${NAMED_FN_END} = ${endVar}`,
4148
4346
  ` return ${valueVar}`,
4149
4347
  ` }`,
4348
+ ...failureRuleId === void 0 ? [] : [` _ctx._grammarTrace?.write({ id: ${JSON.stringify(failureRuleId)}, phase: 'failure', offset: _ctx._fe ?? _pos })`],
4150
4349
  ` return ${NAMED_FN_FAIL}`,
4151
4350
  `}`
4152
4351
  ].join("\n"));
@@ -4308,6 +4507,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
4308
4507
  case "not":
4309
4508
  case "trivia":
4310
4509
  case "token":
4510
+ case "leaf":
4311
4511
  case "scanTo":
4312
4512
  case "unknown":
4313
4513
  return false;
@@ -4319,6 +4519,8 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
4319
4519
  // nothing committed remains.
4320
4520
  case "choice":
4321
4521
  return false;
4522
+ case "attempt":
4523
+ return false;
4322
4524
  // optional never fails; many/oneOrMore only "fail" with zero captured items.
4323
4525
  case "optional":
4324
4526
  case "many":
@@ -4371,6 +4573,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
4371
4573
  case "keywords":
4372
4574
  case "node":
4373
4575
  case "token":
4576
+ case "leaf":
4374
4577
  return true;
4375
4578
  case "not":
4376
4579
  case "guard":
@@ -4385,6 +4588,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
4385
4588
  case "many":
4386
4589
  case "oneOrMore":
4387
4590
  case "optional":
4591
+ case "attempt":
4388
4592
  case "transform":
4389
4593
  case "label":
4390
4594
  case "field":
@@ -4726,35 +4930,55 @@ function needsFirstSetGuard(p) {
4726
4930
  const fs = p._meta.firstSet;
4727
4931
  return fs.kind !== "any" && !canMatchEmptyAtStart(p);
4728
4932
  }
4729
- function emitDisjointArm(p, ctx, pos, valV, endV) {
4730
- if (failsAtStart(p)) {
4933
+ function coverageHit(ctx, id, offset = "_pos", end) {
4934
+ return id === void 0 ? [] : [
4935
+ `${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`,
4936
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'selected', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`,
4937
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`
4938
+ ];
4939
+ }
4940
+ function coverageAttempt(ctx, id, offset) {
4941
+ return id === void 0 ? [] : [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'attempt', offset: ${offset} })`];
4942
+ }
4943
+ function coverageFailureBacktrack(ctx, id, failureOffset, startOffset) {
4944
+ return id === void 0 ? [] : [
4945
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'failure', offset: ${failureOffset} })`,
4946
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'backtrack', offset: ${startOffset} })`
4947
+ ];
4948
+ }
4949
+ function emitDisjointArm(p, ctx, pos, valV, endV, coverageId) {
4950
+ if (failsAtStart(p) && coverageId === void 0) {
4731
4951
  const r = emit(p, ctx, pos);
4732
- return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
4952
+ return [...coverageAttempt(ctx, coverageId, pos), ...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`, ...coverageHit(ctx, coverageId, pos, r.endVar)];
4733
4953
  }
4734
4954
  const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
4735
4955
  return [
4956
+ ...coverageAttempt(ctx, coverageId, pos),
4736
4957
  ...stmts,
4737
- ...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
4958
+ ...emitIfFail(ctx, `!${okVar}`, `${coverageFailureBacktrack(ctx, coverageId, "_ctx._fe", pos).join("; ")}; ${failArrBody(ctx, "_ctx._fx", pos, false)}`),
4738
4959
  `${ind(ctx)}${valV} = ${valVar}`,
4739
- `${ind(ctx)}${endV} = ${endVar}`
4960
+ `${ind(ctx)}${endV} = ${endVar}`,
4961
+ ...coverageHit(ctx, coverageId, pos, endVar)
4740
4962
  ];
4741
4963
  }
4742
- function emitDisjointArmGated(def, i, ctx, pos, valV, endV) {
4964
+ function emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageId) {
4743
4965
  const p = def.parsers[i];
4744
4966
  const gate = def.gates[i];
4745
- if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV);
4967
+ if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV, coverageId);
4746
4968
  const gateIdx = ctx.mapFns.length;
4747
4969
  ctx.mapFns.push(gate);
4748
4970
  ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4749
4971
  const gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4750
4972
  const stmts = [`${ind(ctx)}if (${gateCond}) {`];
4751
4973
  ctx.indent++;
4752
- stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
4974
+ stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV, coverageId));
4753
4975
  ctx.indent--;
4754
4976
  stmts.push(`${ind(ctx)}} else { ${failArrBody(ctx, deriveExpectedArr([p]), pos)} }`);
4755
4977
  return stmts;
4756
4978
  }
4757
- function emitChoice(def, ctx, pos) {
4979
+ function emitChoice(parser2, def, ctx, pos) {
4980
+ const coverageIds = ctx.coverage?.plan.choices.get(parser2);
4981
+ const coverageBase = coverageIds?.[0]?.slice(0, -1);
4758
4982
  const allExpected = deriveExpectedArr(def.parsers);
4759
4983
  if (def.disjoint) {
4760
4984
  const codeV = v(ctx, "_code");
@@ -4773,7 +4997,7 @@ function emitChoice(def, ctx, pos) {
4773
4997
  stmts.push(`${ind(ctx)}{`);
4774
4998
  ctx.indent++;
4775
4999
  stmts.push(
4776
- ...emitDisjointArmGated(def, i, ctx, pos, valV, endV),
5000
+ ...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]),
4777
5001
  `${ind(ctx)}break`
4778
5002
  );
4779
5003
  ctx.indent--;
@@ -4792,16 +5016,16 @@ function emitChoice(def, ctx, pos) {
4792
5016
  first = false;
4793
5017
  stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
4794
5018
  ctx.indent++;
4795
- stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV));
5019
+ stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]));
4796
5020
  ctx.indent--;
4797
5021
  stmts.push(`${ind(ctx)}}`);
4798
5022
  }
4799
5023
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
4800
5024
  return { stmts, valueVar: valV, endVar: endV };
4801
5025
  }
4802
- return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
5026
+ return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos, coverageBase);
4803
5027
  }
4804
- function emitGreedyClassify(def, superIndex, ctx, pos) {
5028
+ function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
4805
5029
  const superParser = def.parsers[superIndex];
4806
5030
  const regexDef = getCoreRegexDef(superParser);
4807
5031
  const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
@@ -4817,10 +5041,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4817
5041
  const endV = v(ctx, "_ge");
4818
5042
  const valV = v(ctx, "_gcv");
4819
5043
  const regexExpected = JSON.stringify(deriveExpected(superParser));
5044
+ const superCoverageId = coverageBase === void 0 ? void 0 : `${coverageBase}${superIndex}`;
4820
5045
  const stmts = [
5046
+ ...coverageAttempt(ctx, superCoverageId, pos),
4821
5047
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
4822
5048
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
4823
- ...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
5049
+ ...emitIfFail(ctx, `${matchV} === null`, `${coverageFailureBacktrack(ctx, superCoverageId, pos, pos).join("; ")}; ${failArrBody(ctx, regexExpected, pos)}`),
4824
5050
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
4825
5051
  `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
4826
5052
  `${ind(ctx)}let ${valV}`
@@ -4836,8 +5062,9 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4836
5062
  stmts.push(`${ind(ctx)}${kw} (${wordV} === ${JSON.stringify(litVal)}) {`);
4837
5063
  ctx.indent++;
4838
5064
  const tR = emitTransformChain(p, JSON.stringify(litVal), endV, pos, ctx);
5065
+ stmts.push(...coverageFailureBacktrack(ctx, superCoverageId, pos, pos));
4839
5066
  stmts.push(...emitLeafCapture(ctx, JSON.stringify(litVal), pos, endV));
4840
- stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`);
5067
+ stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`, ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endV));
4841
5068
  ctx.indent--;
4842
5069
  stmts.push(`${ind(ctx)}}`);
4843
5070
  }
@@ -4845,12 +5072,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4845
5072
  const regexKw = first ? "if" : "else";
4846
5073
  stmts.push(`${ind(ctx)}${regexKw} {`);
4847
5074
  ctx.indent++;
4848
- stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}`);
5075
+ stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}`, ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${superIndex}`, pos, endV));
4849
5076
  ctx.indent--;
4850
5077
  stmts.push(`${ind(ctx)}}`);
4851
5078
  return { stmts, valueVar: valV, endVar: endV };
4852
5079
  }
4853
- function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
5080
+ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, coverageBase) {
4854
5081
  const valV = v(ctx, "_llv");
4855
5082
  const endV = v(ctx, "_lle");
4856
5083
  const stmts = [`${ind(ctx)}let ${valV}, ${endV} = ${pos}`];
@@ -4867,10 +5094,12 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4867
5094
  ctx.indent++;
4868
5095
  const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
4869
5096
  stmts.push(
5097
+ ...coverageAttempt(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos),
4870
5098
  ...emitLeafCapture(ctx, JSON.stringify(litVal), pos, litEnd),
4871
5099
  ...tR.stmts,
4872
5100
  `${ind(ctx)}${valV} = ${tR.valueVar}`,
4873
- `${ind(ctx)}${endV} = ${litEnd}`
5101
+ `${ind(ctx)}${endV} = ${litEnd}`,
5102
+ ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos, litEnd)
4874
5103
  );
4875
5104
  ctx.indent--;
4876
5105
  stmts.push(`${ind(ctx)}}`);
@@ -4878,16 +5107,18 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4878
5107
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
4879
5108
  return { stmts, valueVar: valV, endVar: endV };
4880
5109
  }
4881
- function emitFirstMatch(def, ctx, pos) {
5110
+ function emitFirstMatch(def, ctx, pos, coverageBase) {
4882
5111
  const resValV = v(ctx, "_crv");
4883
5112
  const resEndV = v(ctx, "_cre");
4884
5113
  const resOkV = v(ctx, "_crok");
4885
5114
  const codeV = v(ctx, "_chcode");
4886
5115
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
5116
+ const autoRejectedV = coverageBase === void 0 ? void 0 : v(ctx, "_carej");
4887
5117
  const ind0 = ind(ctx);
4888
5118
  const stmts = [
4889
5119
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
4890
5120
  `${ind0}let ${slots.join(", ")}`,
5121
+ ...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
4891
5122
  `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
4892
5123
  ];
4893
5124
  for (let i = 0; i < def.parsers.length; i++) {
@@ -4923,6 +5154,7 @@ function emitFirstMatch(def, ctx, pos) {
4923
5154
  stmts.push(`${ind0}if (${skipCond}) {`);
4924
5155
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
4925
5156
  ctx.indent += fsGuard ? 2 : 1;
5157
+ if (coverageBase !== void 0) stmts.push(`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'attempt', offset: ${pos} })`);
4926
5158
  if (markLeaves) {
4927
5159
  stmts.push(
4928
5160
  `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
@@ -4945,13 +5177,21 @@ function emitFirstMatch(def, ctx, pos) {
4945
5177
  stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
4946
5178
  stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
4947
5179
  stmts.push(`${ind(ctx)} ${resOkV} = true`);
5180
+ if (autoRejectedV !== void 0) {
5181
+ stmts.push(`${ind(ctx)} for (const _rejectedId of ${autoRejectedV}) { _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'failure', offset: ${pos} }); _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'backtrack', offset: ${pos} }) }`);
5182
+ }
5183
+ stmts.push(...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endVar));
4948
5184
  stmts.push(`${ind(ctx)} }`);
5185
+ if (autoRejectedV !== void 0) {
5186
+ stmts.push(`${ind(ctx)} else { ${autoRejectedV}.push(${JSON.stringify(`${coverageBase}${i}`)}) }`);
5187
+ }
4949
5188
  stmts.push(`${ind(ctx)}}`);
4950
- stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
5189
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}; ${coverageBase === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'failure', offset: ${atStart ? pos : "_ctx._fe"} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'backtrack', offset: ${pos} });`} }`);
4951
5190
  if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
4952
5191
  } else {
4953
- stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
4954
- stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
5192
+ const rejectedFlush = autoRejectedV === void 0 ? "" : `; for (const _rejectedId of ${autoRejectedV}) { _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'failure', offset: ${pos} }); _ctx._grammarTrace?.write({ id: _rejectedId, phase: 'backtrack', offset: ${pos} }) }`;
5193
+ stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true${coverageBase === void 0 ? "" : `${rejectedFlush}; _ctx._grammarCoverage?.(${JSON.stringify(`${coverageBase}${i}`)}); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'selected', offset: ${pos}, end: ${endVar} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'success', offset: ${pos}, end: ${endVar} })`} }`);
5194
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""}${coverageBase === void 0 ? "" : `; _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'failure', offset: ${atStart ? pos : "_ctx._fe"} }); _ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'backtrack', offset: ${pos} })`} }`);
4955
5195
  }
4956
5196
  ctx.indent -= fsGuard ? 2 : 1;
4957
5197
  if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
@@ -4961,12 +5201,12 @@ function emitFirstMatch(def, ctx, pos) {
4961
5201
  stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
4962
5202
  return { stmts, valueVar: resValV, endVar: resEndV };
4963
5203
  }
4964
- function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
5204
+ function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
4965
5205
  if (strategy.tag === "greedyClassify")
4966
- return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
5206
+ return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
4967
5207
  if (strategy.tag === "literalsLongestFirst")
4968
- return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
4969
- return emitFirstMatch(def, ctx, pos);
5208
+ return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
5209
+ return emitFirstMatch(def, ctx, pos, coverageBase);
4970
5210
  }
4971
5211
  function pushMapFn(ctx, fn, src) {
4972
5212
  if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
@@ -5084,6 +5324,27 @@ function emitOptional(def, ctx, pos) {
5084
5324
  ];
5085
5325
  return { stmts, valueVar: valV, endVar: endV };
5086
5326
  }
5327
+ function emitAttempt(p, def, ctx, pos) {
5328
+ const inner = emitFallible(def.parser, ctx, pos);
5329
+ const leaves = v(ctx, "_atl");
5330
+ const raw = v(ctx, "_atr");
5331
+ const trivia2 = v(ctx, "_att");
5332
+ const log = v(ctx, "_atg");
5333
+ const fields = v(ctx, "_atf");
5334
+ const errors = v(ctx, "_ate");
5335
+ const rollback = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${trivia2}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${log}; if (_ctx._fields) _ctx._fields.length = ${fields}; if (_ctx._errors) _ctx._errors.length = ${errors}`;
5336
+ const traceId = ctx.coverage?.plan.attempts.get(p);
5337
+ const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
5338
+ return {
5339
+ stmts: [
5340
+ `${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${trivia2} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`,
5341
+ ...inner.stmts,
5342
+ ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
5343
+ ],
5344
+ valueVar: inner.valVar,
5345
+ endVar: inner.endVar
5346
+ };
5347
+ }
5087
5348
  function emitSepBy(_p, def, ctx, pos) {
5088
5349
  const arrV = v(ctx, "_arr");
5089
5350
  const curV = v(ctx, "_cur");
@@ -5374,6 +5635,36 @@ function emitToken(def, ctx, pos) {
5374
5635
  endVar: inner.endVar
5375
5636
  };
5376
5637
  }
5638
+ function emitLeaf(def, ctx, pos) {
5639
+ const inner = emitFallible(def.parser, ctx, pos);
5640
+ const i = ind(ctx);
5641
+ const sc = v(ctx, "_leafCh");
5642
+ const sl = v(ctx, "_leafLv");
5643
+ const sr = v(ctx, "_leafRaw");
5644
+ const stl = v(ctx, "_leafTl");
5645
+ const sol = v(ctx, "_leafLog");
5646
+ const sb = v(ctx, "_leafBuf");
5647
+ const rawV = v(ctx, "_leafRawValue");
5648
+ const valV = v(ctx, "_leaf");
5649
+ const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
5650
+ return {
5651
+ stmts: [
5652
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${stl} = _ctx._cstTriviaLog, ${sol} = _ctx._triviaLog, ${sb} = _ctx._cstBuf`,
5653
+ `${i}_ctx._cstChildren = undefined; _ctx._cstLeaves = undefined; _ctx._cstRawChildren = undefined; _ctx._cstTriviaLog = undefined; _ctx._triviaLog = undefined; _ctx._cstBuf = undefined`,
5654
+ `${i}try {`,
5655
+ ...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
5656
+ `${i}} finally {`,
5657
+ `${i} _ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx._cstTriviaLog = ${stl}; _ctx._triviaLog = ${sol}; _ctx._cstBuf = ${sb}`,
5658
+ `${i}}`,
5659
+ ...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
5660
+ `${i}const ${rawV} = ${inner.valVar}`,
5661
+ `${i}const ${valV} = ${mfRef(ctx)}[${fnIdx}](${rawV}, { start: ${pos}, end: ${inner.endVar} })`,
5662
+ ...emitLeafCapture(ctx, valV, pos, inner.endVar)
5663
+ ],
5664
+ valueVar: valV,
5665
+ endVar: inner.endVar
5666
+ };
5667
+ }
5377
5668
  function emitNode(def, ctx, pos) {
5378
5669
  if (def.type === void 0) {
5379
5670
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
@@ -5389,6 +5680,8 @@ function emitNode(def, ctx, pos) {
5389
5680
  const i = ind(ctx);
5390
5681
  const capturesTrivia = mkType !== null || def.captureTrivia === true || def.trailingTrivia === true || !structural && buildReadsTrivia(def);
5391
5682
  const clonesState = !structural && buildReadsState(def);
5683
+ const capturesChildren = !structural && (mkType !== null || def.unwrap || def.collapse || buildReadsChildren(def));
5684
+ const capturesRaw = !structural && (mkType !== null || buildReadsRaw(def));
5392
5685
  const hasFields = parserHasOwnFields(def.parser);
5393
5686
  const capturesFields = hasFields && !structural && buildReadsFields(def);
5394
5687
  const innerEnablesTriviaCapture = parserEnablesTriviaCapture(def.parser);
@@ -5407,9 +5700,13 @@ function emitNode(def, ctx, pos) {
5407
5700
  const profHoist = `${i}const ${pmV} = _ctx._pmProfile, ${recV} = ${pmV}?.phase === 'recognizer', ${capV} = ${pmV}?.phase === 'capture'`;
5408
5701
  const profileRecognizer = recV;
5409
5702
  const profileCapture = capV;
5703
+ const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
5704
+ const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
5410
5705
  const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
5411
5706
  const allocStmt = structural ? `${i}const ${capTLv} = !(${profileRecognizer}) && (${profileCapture} || ${structuralCapturesTrivia ? "true" : hostTriviaGate}), ${capSTv} = !(${profileRecognizer} || ${profileCapture}) && (_ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6)))${capFv ? `, ${capFv} = !(${profileRecognizer}) && (${profileCapture} || (_ctx.build !== undefined && _hostReads(_ctx.build, 2)))` : ""}
5412
- ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : []` : `${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : []`;
5707
+ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5708
+ ` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5709
+ ` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
5413
5710
  const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
5414
5711
  const fieldsOn = structural ? capFv ?? "false" : `${profileRecognizer} ? false : ${capturesFields ? "true" : "false"}`;
5415
5712
  const sf = hasFields ? v(ctx, "_sf") : null;
@@ -5526,14 +5823,23 @@ function emitLazy(p, def, ctx, pos) {
5526
5823
  const savedIndent = ctx.indent;
5527
5824
  const savedFailLabel = ctx.failLabel;
5528
5825
  const savedRecord = ctx.recordFail;
5826
+ const failureRuleId = p === ctx.coverage?.entry ? void 0 : ctx.coverage?.plan.rules.get(p) ?? ctx.coverage?.plan.rules.get(resolved);
5827
+ const savedActiveRule = ctx.activeCoverageRuleId;
5828
+ const savedSuppressFailure = ctx.suppressCoverageFailure;
5529
5829
  ctx.indent = 1;
5530
5830
  ctx.failLabel = "_pfail";
5531
5831
  ctx.recordFail = true;
5832
+ if (failureRuleId !== void 0) {
5833
+ ctx.activeCoverageRuleId = void 0;
5834
+ ctx.suppressCoverageFailure = true;
5835
+ }
5532
5836
  const r = emit(resolved, ctx, "_pos");
5533
5837
  ctx.indent = savedIndent;
5534
5838
  ctx.failLabel = savedFailLabel;
5535
5839
  ctx.recordFail = savedRecord;
5536
- pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
5840
+ ctx.activeCoverageRuleId = savedActiveRule;
5841
+ ctx.suppressCoverageFailure = savedSuppressFailure;
5842
+ pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar, failureRuleId);
5537
5843
  }
5538
5844
  const fnName = ctx.namedParsers.get(p);
5539
5845
  return emitNamedFnCall(ctx, fnName, pos);
@@ -5584,6 +5890,30 @@ function emitExpect(def, ctx, pos) {
5584
5890
  return { stmts, valueVar: valVar, endVar };
5585
5891
  }
5586
5892
  function emit(p, ctx, pos) {
5893
+ const dispatch = (emissionPos = pos) => {
5894
+ const savedRule = ctx.activeCoverageRuleId;
5895
+ const rule = ctx.coverage?.plan.rules.get(p);
5896
+ if (rule && p !== ctx.coverage?.entry && !ctx.suppressCoverageFailure) ctx.activeCoverageRuleId = rule;
5897
+ const result = emitDispatch(p, ctx, emissionPos);
5898
+ ctx.activeCoverageRuleId = savedRule;
5899
+ return result;
5900
+ };
5901
+ const instrument = (r) => {
5902
+ const ruleId = ctx.coverage?.plan.rules.get(p);
5903
+ const labelIds = ctx.coverage?.plan.labels.get(p);
5904
+ if (!ruleId && !labelIds) return r;
5905
+ return {
5906
+ ...r,
5907
+ stmts: [
5908
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`] : [],
5909
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: ${pos} })`] : [],
5910
+ ...r.stmts,
5911
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`] : [],
5912
+ ...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`]) ?? [],
5913
+ ...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`]) ?? []
5914
+ ]
5915
+ };
5916
+ };
5587
5917
  const usage = ctx.lazyUsage;
5588
5918
  if (usage && !ctx.noHoist && !ctx.capAsTrivia && !ctx.ruleNames?.has(p) && isHoistableTag(p._def.tag) && (usage.counts.get(p) ?? 0) > 1 && // Only hoist subtrees big enough that de-duplicating them beats the call
5589
5919
  // overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
@@ -5600,14 +5930,14 @@ function emit(p, ctx, pos) {
5600
5930
  ctx.indent = 1;
5601
5931
  ctx.failLabel = "_pfail";
5602
5932
  ctx.recordFail = true;
5603
- const r = emitDispatch(p, ctx, "_pos");
5933
+ const r = dispatch("_pos");
5604
5934
  ctx.indent = savedIndent;
5605
5935
  ctx.failLabel = savedFailLabel;
5606
5936
  ctx.recordFail = savedRecord;
5607
5937
  pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
5608
- return emitNamedFnCall(ctx, fnName, pos);
5938
+ return instrument(emitNamedFnCall(ctx, fnName, pos));
5609
5939
  }
5610
- return emitDispatch(p, ctx, pos);
5940
+ return instrument(dispatch());
5611
5941
  }
5612
5942
  function emitDispatch(p, ctx, pos) {
5613
5943
  const def = p._def;
@@ -5621,7 +5951,9 @@ function emitDispatch(p, ctx, pos) {
5621
5951
  case "sequence":
5622
5952
  return emitSeq(def, ctx, pos);
5623
5953
  case "choice":
5624
- return emitChoice(def, ctx, pos);
5954
+ return emitChoice(p, def, ctx, pos);
5955
+ case "attempt":
5956
+ return emitAttempt(p, def, ctx, pos);
5625
5957
  case "many":
5626
5958
  case "oneOrMore":
5627
5959
  return emitMany(def, ctx, pos);
@@ -5690,6 +6022,8 @@ function emitDispatch(p, ctx, pos) {
5690
6022
  return emit(def.parser, ctx, pos);
5691
6023
  case "token":
5692
6024
  return emitToken(def, ctx, pos);
6025
+ case "leaf":
6026
+ return emitLeaf(def, ctx, pos);
5693
6027
  case "label": {
5694
6028
  const inner = emitFallible(def.parser, ctx, pos);
5695
6029
  return {
@@ -5821,6 +6155,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5821
6155
  case "grammar":
5822
6156
  case "trivia":
5823
6157
  case "token":
6158
+ case "leaf":
5824
6159
  case "label":
5825
6160
  case "field":
5826
6161
  case "optional":
@@ -5848,16 +6183,32 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5848
6183
  return false;
5849
6184
  }
5850
6185
  }
6186
+ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
6187
+ if (seen.has(p)) return false;
6188
+ seen.add(p);
6189
+ const d = p._def;
6190
+ if (d.tag === "node" && d.build !== void 0) return true;
6191
+ if (d.tag === "lazy") {
6192
+ try {
6193
+ return hasDirectBuildDef(d.thunk(), seen);
6194
+ } catch {
6195
+ return false;
6196
+ }
6197
+ }
6198
+ return childrenOf(d).some((child) => hasDirectBuildDef(child, seen));
6199
+ }
5851
6200
  function childrenOf(def) {
5852
6201
  switch (def.tag) {
5853
6202
  case "sequence":
5854
6203
  case "choice":
5855
6204
  return def.parsers;
5856
6205
  case "many":
6206
+ case "attempt":
5857
6207
  case "optional":
5858
6208
  case "transform":
5859
6209
  case "trivia":
5860
6210
  case "token":
6211
+ case "leaf":
5861
6212
  case "label":
5862
6213
  case "field":
5863
6214
  case "grammar":
@@ -6032,10 +6383,25 @@ function compile(combinator, mapFnSources, opts) {
6032
6383
  namedFnDecls: [],
6033
6384
  capturing: hasNodeDef(combinator),
6034
6385
  recovery: opts?.recovery ?? false,
6386
+ ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
6035
6387
  lazyUsage: analyzeLazyUsage(combinator),
6036
6388
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
6037
6389
  };
6038
6390
  const r = emit(combinator, ctx, "_pos");
6391
+ const coverageRootRuleId = ctx.coverage?.plan.rules.get(combinator);
6392
+ const resultBody = coverageRootRuleId === void 0 ? [
6393
+ ` let pos = _pos`,
6394
+ ...r.stmts,
6395
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6396
+ ] : [
6397
+ ` const _coverageResult = (() => {`,
6398
+ ` let pos = _pos`,
6399
+ ...r.stmts,
6400
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6401
+ ` })()`,
6402
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6403
+ ` return _coverageResult`
6404
+ ];
6039
6405
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
6040
6406
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
6041
6407
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
@@ -6048,9 +6414,7 @@ function compile(combinator, mapFnSources, opts) {
6048
6414
  ...namedPrelude,
6049
6415
  ctx.namedFnDecls.join("\n\n"),
6050
6416
  `function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
6051
- ` let pos = _pos`,
6052
- ...r.stmts,
6053
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6417
+ ...resultBody,
6054
6418
  `}`
6055
6419
  ].join("\n");
6056
6420
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
@@ -6060,9 +6424,15 @@ function compile(combinator, mapFnSources, opts) {
6060
6424
  ...ctx.expectedDecls,
6061
6425
  ...namedPrelude,
6062
6426
  ...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
6063
- `let pos = _pos`,
6064
- ...r.stmts,
6065
- `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6427
+ ...coverageRootRuleId === void 0 ? [`let pos = _pos`, ...r.stmts, `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`] : [
6428
+ `const _coverageResult = (() => {`,
6429
+ ` let pos = _pos`,
6430
+ ...r.stmts,
6431
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6432
+ `})()`,
6433
+ `if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6434
+ `return _coverageResult`
6435
+ ]
6066
6436
  ].join("\n"));
6067
6437
  const defaultCtx = {
6068
6438
  trackLines: false,
@@ -6074,10 +6444,11 @@ function compile(combinator, mapFnSources, opts) {
6074
6444
  const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
6075
6445
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
6076
6446
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
6077
- const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
6447
+ const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
6078
6448
  return {
6079
6449
  source,
6080
6450
  inlineExpression,
6451
+ ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
6081
6452
  parse(input, pos = 0) {
6082
6453
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
6083
6454
  },
@@ -6140,8 +6511,13 @@ function compileLinkable(ruleMapArg, ns, opts) {
6140
6511
  triviaCaptureNames: /* @__PURE__ */ new Map(),
6141
6512
  triviaFnNames: /* @__PURE__ */ new Map(),
6142
6513
  namedFnDecls: [],
6143
- capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
6514
+ // A recognition-only fragment normally has no node collector of its own.
6515
+ // Leaf composition can re-lower it beneath a local semantic node, though;
6516
+ // in that case its terminals must feed the caller's collector rather than
6517
+ // merely returning their scalar parse values.
6518
+ capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
6144
6519
  recovery: opts?.recovery ?? false,
6520
+ ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
6145
6521
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
6146
6522
  ns,
6147
6523
  deferFirstSetRefs: true,
@@ -6215,7 +6591,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
6215
6591
  ctx.indent = savedIndent;
6216
6592
  ctx.failLabel = savedFail;
6217
6593
  ctx.recordFail = savedRec;
6218
- pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
6594
+ const ruleId = ctx.coverage?.plan.rules.get(rule);
6595
+ const stmts = ruleId === void 0 ? body.stmts : [
6596
+ `${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`,
6597
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: _pos })`,
6598
+ ...body.stmts,
6599
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: _pos, end: ${body.endVar} })`
6600
+ ];
6601
+ pushNamedFnDecl(ctx, fn, stmts, body.valueVar, body.endVar);
6219
6602
  }
6220
6603
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
6221
6604
  }
@@ -6272,15 +6655,46 @@ function compileLinkable(ruleMapArg, ns, opts) {
6272
6655
  deps: ruleDependencies(ruleMap),
6273
6656
  needsEmptyTl: !!ctx.needsEmptyTl,
6274
6657
  needsHostReads: !!ctx.needsHostReads,
6658
+ hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
6659
+ isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
6275
6660
  mfFns: mfSrcs ? [] : ctx.mapFns,
6276
6661
  buildFns: buildSrcs ? [] : ctx.buildFns
6277
6662
  };
6278
6663
  }
6279
- function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
6280
- const bodyLines = [
6664
+ function hasSemanticReduction(roots) {
6665
+ const seen = /* @__PURE__ */ new Set();
6666
+ const visit = (parser2) => {
6667
+ if (seen.has(parser2)) return false;
6668
+ seen.add(parser2);
6669
+ const def = parser2._def;
6670
+ if (def.tag === "transform" && !def.recognitionOnly) return true;
6671
+ if (def.tag === "choice" && def.gates.some(Boolean)) return true;
6672
+ if (def.tag === "guard" || def.tag === "withCtx") return true;
6673
+ if (def.tag === "node" && def.build !== void 0) return true;
6674
+ if (def.tag === "lazy") {
6675
+ try {
6676
+ return visit(def.thunk());
6677
+ } catch {
6678
+ return true;
6679
+ }
6680
+ }
6681
+ return childrenOf(def).some(visit);
6682
+ };
6683
+ return roots.some(visit);
6684
+ }
6685
+ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootRuleId) {
6686
+ const bodyLines = coverageRootRuleId === void 0 ? [
6281
6687
  ` let pos = _pos`,
6282
6688
  ...r.stmts,
6283
6689
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6690
+ ] : [
6691
+ ` const _coverageResult = (() => {`,
6692
+ ` let pos = _pos`,
6693
+ ...r.stmts,
6694
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6695
+ ` })()`,
6696
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6697
+ ` return _coverageResult`
6284
6698
  ];
6285
6699
  const innerFn = [
6286
6700
  `function(input, _pos, _ctx) {`,
@@ -6335,8 +6749,8 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
6335
6749
  };
6336
6750
  const emit2 = (end) => {
6337
6751
  if (end > pos && cstCaptureActive(ctx)) {
6338
- const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
6339
- pushCstLeaf(ctx, leaf);
6752
+ const leaf2 = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
6753
+ pushCstLeaf(ctx, leaf2);
6340
6754
  }
6341
6755
  };
6342
6756
  while (cur < input.length) {
@@ -6390,6 +6804,7 @@ function balanced(open, close, options = {}) {
6390
6804
  );
6391
6805
  if (combi._def.tag === "transform") {
6392
6806
  combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
6807
+ combi._def.recognitionOnly = true;
6393
6808
  }
6394
6809
  self.define(combi);
6395
6810
  return combi;
@@ -6435,9 +6850,11 @@ function childrenOf2(def) {
6435
6850
  case "many":
6436
6851
  case "oneOrMore":
6437
6852
  case "optional":
6853
+ case "attempt":
6438
6854
  case "transform":
6439
6855
  case "trivia":
6440
6856
  case "token":
6857
+ case "leaf":
6441
6858
  case "label":
6442
6859
  case "field":
6443
6860
  case "not":
@@ -6486,7 +6903,7 @@ function serializeRuleMap(ruleMap) {
6486
6903
  }
6487
6904
  }
6488
6905
  function evalRuleMapIR(ir) {
6489
- const _tf = (child, src) => {
6906
+ const _tf = (child, src, recognitionOnly = false) => {
6490
6907
  let fn2;
6491
6908
  try {
6492
6909
  fn2 = (0, eval)(`(${src})`);
@@ -6497,8 +6914,22 @@ function evalRuleMapIR(ir) {
6497
6914
  }
6498
6915
  const t = transform(child, fn2);
6499
6916
  t._def.fnSrc = src;
6917
+ if (recognitionOnly) t._def.recognitionOnly = true;
6500
6918
  return t;
6501
6919
  };
6920
+ const _lf = (child, src) => {
6921
+ let fn2;
6922
+ try {
6923
+ fn2 = (0, eval)(`(${src})`);
6924
+ } catch {
6925
+ fn2 = () => {
6926
+ throw new Error("IR leaf fn not materialized");
6927
+ };
6928
+ }
6929
+ const l = leaf(child, fn2);
6930
+ l._def.fnSrc = src;
6931
+ return l;
6932
+ };
6502
6933
  const _nd = (type, child, src, opts, staticError) => {
6503
6934
  if (staticError !== void 0 && staticError.length > 0) {
6504
6935
  throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
@@ -6557,6 +6988,7 @@ function evalRuleMapIR(ir) {
6557
6988
  "keywords",
6558
6989
  "sequence",
6559
6990
  "choice",
6991
+ "attempt",
6560
6992
  "many",
6561
6993
  "oneOrMore",
6562
6994
  "optional",
@@ -6566,6 +6998,7 @@ function evalRuleMapIR(ir) {
6566
6998
  "parser",
6567
6999
  "scanTo",
6568
7000
  "token",
7001
+ "leaf",
6569
7002
  "transform",
6570
7003
  "skip",
6571
7004
  "trivia",
@@ -6573,6 +7006,7 @@ function evalRuleMapIR(ir) {
6573
7006
  "field",
6574
7007
  "expect",
6575
7008
  "_tf",
7009
+ "_lf",
6576
7010
  "_nd",
6577
7011
  "_gch",
6578
7012
  "_wc",
@@ -6586,6 +7020,7 @@ function evalRuleMapIR(ir) {
6586
7020
  keywords,
6587
7021
  sequence,
6588
7022
  choice,
7023
+ attempt,
6589
7024
  many,
6590
7025
  oneOrMore,
6591
7026
  optional,
@@ -6595,6 +7030,7 @@ function evalRuleMapIR(ir) {
6595
7030
  parser,
6596
7031
  scanTo,
6597
7032
  token,
7033
+ leaf,
6598
7034
  transform,
6599
7035
  skip,
6600
7036
  trivia,
@@ -6602,6 +7038,7 @@ function evalRuleMapIR(ir) {
6602
7038
  field,
6603
7039
  expect,
6604
7040
  _tf,
7041
+ _lf,
6605
7042
  _nd,
6606
7043
  _gch,
6607
7044
  _wc
@@ -6733,6 +7170,8 @@ ${body}
6733
7170
  return `oneOrMore(${kid(def.parser)})`;
6734
7171
  case "optional":
6735
7172
  return `optional(${kid(def.parser)})`;
7173
+ case "attempt":
7174
+ return `attempt(${kid(def.parser)})`;
6736
7175
  case "sepBy":
6737
7176
  return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
6738
7177
  case "not":
@@ -6741,6 +7180,10 @@ ${body}
6741
7180
  return `trivia(${kid(def.parser)})`;
6742
7181
  case "token":
6743
7182
  return `token(${kid(def.parser)})`;
7183
+ case "leaf": {
7184
+ if (def.fnSrc === void 0) throw new Unserializable("leaf without fnSrc");
7185
+ return `_lf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
7186
+ }
6744
7187
  case "label":
6745
7188
  return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
6746
7189
  case "field":
@@ -6753,7 +7196,7 @@ ${body}
6753
7196
  return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
6754
7197
  case "transform": {
6755
7198
  if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
6756
- return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
7199
+ return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
6757
7200
  }
6758
7201
  case "node": {
6759
7202
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
@@ -6805,15 +7248,15 @@ function normalizeCstCollapse(collapse) {
6805
7248
  }
6806
7249
  return typeof collapse === "function" ? collapse : void 0;
6807
7250
  }
6808
- function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
6809
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
7251
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
7252
+ return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
6810
7253
  }
6811
- function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
7254
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
6812
7255
  if (typeof typeOrOptions === "string") {
6813
- return buildCstNode(typeOrOptions, children ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
7256
+ return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
6814
7257
  }
6815
7258
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
6816
- const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children2, fields, span2, rawChildren2, triviaLog2, state2);
7259
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
6817
7260
  host._parsemanCstOutput = true;
6818
7261
  if (collapse) host._parsemanCstCollapse = collapse;
6819
7262
  return host;
@@ -6872,13 +7315,39 @@ function fuseRules(pieces) {
6872
7315
  return new Function("_env", body)(env);
6873
7316
  }
6874
7317
  var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
7318
+ var LEAF_COMPOSED = /* @__PURE__ */ Symbol.for("parseman.leafComposed");
6875
7319
  var COMPOSED_TRIVIA = /* @__PURE__ */ Symbol.for("parseman.composedTrivia");
7320
+ var COMPOSED_COVERAGE_RULES = /* @__PURE__ */ Symbol.for("parseman.composedCoverageRules");
6876
7321
  function isIRPiece(p) {
6877
7322
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
6878
7323
  }
6879
- function materializePiece(p, trivia2) {
7324
+ function coverageRulesOf(carried) {
7325
+ const winners = {};
7326
+ for (const piece of carried) {
7327
+ if (!isIRPiece(piece)) return void 0;
7328
+ const map = evalRuleMapIR(piece.ir);
7329
+ for (const [name, rule] of map) {
7330
+ if (rule._def.tag === "lazy") {
7331
+ try {
7332
+ rule._def.thunk();
7333
+ } catch {
7334
+ continue;
7335
+ }
7336
+ }
7337
+ winners[name] = rule;
7338
+ }
7339
+ }
7340
+ return winners;
7341
+ }
7342
+ function composedCoverageRules(grammar) {
7343
+ return grammar[COMPOSED_COVERAGE_RULES];
7344
+ }
7345
+ function materializePiece(p, trivia2, captureTerminals = false) {
6880
7346
  if (!isIRPiece(p)) return p;
6881
- const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, trivia2 ? { trivia: trivia2 } : void 0);
7347
+ const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
7348
+ ...trivia2 ? { trivia: trivia2 } : {},
7349
+ ...captureTerminals ? { captureTerminals: true } : {}
7350
+ });
6882
7351
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
6883
7352
  return pieces;
6884
7353
  }
@@ -6929,6 +7398,9 @@ function composingTriviaOf(items) {
6929
7398
  return void 0;
6930
7399
  }
6931
7400
  function compose(items) {
7401
+ if (items.some((item) => item[LEAF_COMPOSED] === true)) {
7402
+ throw new Error("compose: a composeLeaf() result is terminal and cannot be composed again");
7403
+ }
6932
7404
  const used = /* @__PURE__ */ new Set();
6933
7405
  const trivia2 = composingTriviaOf(items);
6934
7406
  const carried = items.flatMap((item) => itemCarried(item, used, trivia2));
@@ -6936,8 +7408,13 @@ function compose(items) {
6936
7408
  const map = fuseRules(pieces);
6937
7409
  Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
6938
7410
  if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
7411
+ const coverageRules = coverageRulesOf(carried);
7412
+ if (coverageRules) Object.defineProperty(map, COMPOSED_COVERAGE_RULES, { value: coverageRules, enumerable: false });
6939
7413
  return map;
6940
7414
  }
7415
+ function composeLeaf(_items) {
7416
+ throw new Error("composeLeaf(): requires Parseman macro lowering; runtime composition is forbidden");
7417
+ }
6941
7418
 
6942
7419
  // src/combinators/guard.ts
6943
7420
  function guard(predicate) {
@@ -7101,7 +7578,9 @@ function producesRepetition(def, depth = 0) {
7101
7578
  // Transparent wrappers — look through to the inner parser.
7102
7579
  case "node":
7103
7580
  case "transform":
7581
+ case "attempt":
7104
7582
  case "token":
7583
+ case "leaf":
7105
7584
  case "label":
7106
7585
  case "field":
7107
7586
  case "expect":
@@ -7153,8 +7632,8 @@ function replaceAtPath(rebuild, root, path, newNode) {
7153
7632
  newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath(rebuild, root.children[idx], rest, newNode);
7154
7633
  return rebuild(root, newChildren);
7155
7634
  }
7156
- function defaultRebuild(node2, children) {
7157
- return { ...node2, children };
7635
+ function defaultRebuild(node2, children2) {
7636
+ return { ...node2, children: children2 };
7158
7637
  }
7159
7638
  function shiftRelStart(child, delta) {
7160
7639
  const c = child;
@@ -7494,7 +7973,8 @@ function runOnce(entry, input, options, phase, profileState) {
7494
7973
  state: options.state,
7495
7974
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
7496
7975
  ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
7497
- ...options.tolerant ? { _tolerant: true, _rec: REC } : {}
7976
+ ...options.tolerant ? { _tolerant: true, _rec: REC } : {},
7977
+ ...options.instrumentation === void 0 ? {} : options.instrumentation
7498
7978
  };
7499
7979
  const r = invoke(entry, input, 0, ctx);
7500
7980
  let unconsumedFrom = null;
@@ -7549,6 +8029,265 @@ function run(entry, input, options = {}) {
7549
8029
  };
7550
8030
  }
7551
8031
 
8032
+ // src/coverage.ts
8033
+ var GRAMMAR_COVERAGE_DEFINITIONS = /* @__PURE__ */ Symbol.for("parseman.grammarCoverageDefinitions");
8034
+ function compiledGrammarCoverageDefinitions(grammar) {
8035
+ const definitions = grammar[GRAMMAR_COVERAGE_DEFINITIONS];
8036
+ if (!Array.isArray(definitions) || !definitions.every((definition) => typeof definition === "object" && definition !== null && typeof definition.id === "string" && (definition.kind === "rule" || definition.kind === "choice-arm" || definition.kind === "label"))) {
8037
+ throw new TypeError("grammar has no coverage definitions; enable grammarCoverage for this macro build");
8038
+ }
8039
+ return definitions;
8040
+ }
8041
+ function createGrammarInstrumentationContext(options = {}) {
8042
+ return {
8043
+ trackLines: options.trackLines ?? false,
8044
+ ...options.state === void 0 ? {} : { state: options.state },
8045
+ ...options.collector === void 0 ? {} : { _grammarCoverage: (id) => options.collector.hit(id) },
8046
+ ...options.trace === void 0 ? {} : { _grammarTrace: options.trace }
8047
+ };
8048
+ }
8049
+ function createGrammarTraceSink(options) {
8050
+ if (!Number.isInteger(options.capacity) || options.capacity < 0) throw new TypeError("trace capacity must be a finite non-negative integer");
8051
+ const events = [];
8052
+ let detached = false;
8053
+ let truncated = false;
8054
+ let dropped = 0;
8055
+ return {
8056
+ write(event) {
8057
+ if (detached || events.length >= options.capacity) {
8058
+ detached = true;
8059
+ truncated = true;
8060
+ dropped++;
8061
+ return;
8062
+ }
8063
+ events.push(Object.freeze({ ...event }));
8064
+ try {
8065
+ if (options.write?.(event) === false) {
8066
+ detached = true;
8067
+ truncated = true;
8068
+ }
8069
+ } catch {
8070
+ detached = true;
8071
+ truncated = true;
8072
+ }
8073
+ },
8074
+ snapshot() {
8075
+ return Object.freeze({ events: Object.freeze([...events]), truncated, dropped });
8076
+ }
8077
+ };
8078
+ }
8079
+ function createGrammarCoverageCollector(definitions) {
8080
+ const ordered = [...definitions].sort((a, b) => a.id.localeCompare(b.id));
8081
+ const known = new Set(ordered.map((definition) => definition.id));
8082
+ const hits = /* @__PURE__ */ new Set();
8083
+ return {
8084
+ hit(id) {
8085
+ if (known.has(id)) hits.add(id);
8086
+ },
8087
+ snapshot() {
8088
+ const hitList = [...hits].sort();
8089
+ const unhit = ordered.map((definition) => definition.id).filter((id) => !hits.has(id));
8090
+ return { definitions: ordered, hits: hitList, unhit, ratio: ordered.length === 0 ? 1 : hitList.length / ordered.length };
8091
+ },
8092
+ reset() {
8093
+ hits.clear();
8094
+ }
8095
+ };
8096
+ }
8097
+ function grammarCoverageDefinitions(entry, winners) {
8098
+ return buildGrammarPlan(entry, winners).definitions;
8099
+ }
8100
+ function composedGrammarCoverageDefinitions(grammar, startRule) {
8101
+ const winners = composedCoverageRules(grammar);
8102
+ if (!winners) throw new TypeError("semantic coverage needs re-lowerable composed IR; this composition contains an opaque artifact");
8103
+ const entry = winners[startRule];
8104
+ if (!entry) throw new TypeError(`semantic coverage start rule ${JSON.stringify(startRule)} is not a final winner`);
8105
+ return grammarCoverageDefinitions(entry, winners);
8106
+ }
8107
+ function coverageEntry(entry, collector, maps, trace) {
8108
+ const cache = /* @__PURE__ */ new WeakMap();
8109
+ const build = (parser2) => {
8110
+ const existing = cache.get(parser2);
8111
+ if (existing) return existing;
8112
+ const rule = maps.rules.get(parser2);
8113
+ const wrap = (parse2) => {
8114
+ const wrapped = {
8115
+ _tag: parser2._tag,
8116
+ _meta: parser2._meta,
8117
+ _def: parser2._def,
8118
+ parse(input, pos, ctx) {
8119
+ if (rule) {
8120
+ collector.hit(rule);
8121
+ trace?.write({ id: rule, phase: "enter", offset: pos });
8122
+ }
8123
+ const result = parse2(input, pos, ctx);
8124
+ if (rule) trace?.write(result.ok ? { id: rule, phase: "success", offset: pos, end: result.span.end } : { id: rule, phase: "failure", offset: result.span.end });
8125
+ return result;
8126
+ }
8127
+ };
8128
+ cache.set(parser2, wrapped);
8129
+ return wrapped;
8130
+ };
8131
+ const def = parser2._def;
8132
+ if (def.tag === "lazy") {
8133
+ return wrap((input, pos, ctx) => build(def.thunk()).parse(input, pos, ctx));
8134
+ }
8135
+ if (def.tag === "label") {
8136
+ const base2 = label(def.label, build(def.parser));
8137
+ return wrap((input, pos, ctx) => {
8138
+ const result = base2.parse(input, pos, ctx);
8139
+ if (result.ok) for (const id of maps.labels.get(parser2) ?? []) {
8140
+ collector.hit(id);
8141
+ trace?.write({ id, phase: "success", offset: pos, end: result.span.end });
8142
+ }
8143
+ return result;
8144
+ });
8145
+ }
8146
+ if (def.tag === "choice") {
8147
+ const ids = maps.choices.get(parser2) ?? [];
8148
+ const invocations = [];
8149
+ const arms = def.parsers.map((arm, index) => {
8150
+ const child = build(arm);
8151
+ return {
8152
+ _tag: child._tag,
8153
+ _meta: child._meta,
8154
+ _def: child._def,
8155
+ parse(input, pos, ctx) {
8156
+ const id = ids[index];
8157
+ if (id) trace?.write({ id, phase: "attempt", offset: pos });
8158
+ const result = child.parse(input, pos, ctx);
8159
+ if (result.ok) {
8160
+ if (def.strategy.tag !== "greedyClassify") collector.hit(ids[index]);
8161
+ const invocation = invocations[invocations.length - 1];
8162
+ if (invocation) {
8163
+ invocation.lastSuccessfulArm = index;
8164
+ invocation.successfulArms.push(index);
8165
+ }
8166
+ } else if (id) {
8167
+ trace?.write({ id, phase: "failure", offset: result.span.end });
8168
+ trace?.write({ id, phase: "backtrack", offset: pos });
8169
+ }
8170
+ return result;
8171
+ }
8172
+ };
8173
+ });
8174
+ const args = arms.map((arm, index) => def.gates[index] ? { combinator: arm, gate: def.gates[index] } : arm);
8175
+ const unsafeChoice = choice;
8176
+ const baseChoice = unsafeChoice(...args);
8177
+ return wrap((input, pos, ctx) => {
8178
+ const invocation = { lastSuccessfulArm: void 0, successfulArms: [] };
8179
+ invocations.push(invocation);
8180
+ const result = baseChoice.parse(input, pos, ctx);
8181
+ const strategy = def.strategy;
8182
+ if (result.ok && strategy.tag === "greedyClassify") {
8183
+ const text = input.slice(pos, result.span.end);
8184
+ const classified = def.parsers.findIndex((arm, index) => index !== strategy.superIndex && getCoreLiteralValue(arm) === text);
8185
+ if (classified !== -1) invocation.lastSuccessfulArm = classified;
8186
+ else invocation.lastSuccessfulArm = strategy.superIndex;
8187
+ }
8188
+ if (result.ok && invocation.lastSuccessfulArm !== void 0) {
8189
+ const id = ids[invocation.lastSuccessfulArm];
8190
+ for (const rejected of invocation.successfulArms) {
8191
+ if (rejected !== invocation.lastSuccessfulArm) {
8192
+ const rejectedId = ids[rejected];
8193
+ if (rejectedId) {
8194
+ trace?.write({ id: rejectedId, phase: "failure", offset: pos });
8195
+ trace?.write({ id: rejectedId, phase: "backtrack", offset: pos });
8196
+ }
8197
+ }
8198
+ }
8199
+ if (id) {
8200
+ collector.hit(id);
8201
+ trace?.write({ id, phase: "selected", offset: pos, end: result.span.end });
8202
+ trace?.write({ id, phase: "success", offset: pos, end: result.span.end });
8203
+ }
8204
+ }
8205
+ invocations.pop();
8206
+ return result;
8207
+ });
8208
+ }
8209
+ const base = (() => {
8210
+ switch (def.tag) {
8211
+ case "sequence": {
8212
+ const unsafeSequence = sequence;
8213
+ return unsafeSequence(...def.parsers.map(build));
8214
+ }
8215
+ case "many":
8216
+ return many(build(def.parser));
8217
+ case "oneOrMore":
8218
+ return oneOrMore(build(def.parser));
8219
+ case "optional":
8220
+ return optional(build(def.parser));
8221
+ case "attempt": {
8222
+ const base2 = attempt(build(def.parser));
8223
+ const id = maps.attempts.get(parser2);
8224
+ return id === void 0 ? base2 : {
8225
+ ...base2,
8226
+ parse(input, pos, ctx) {
8227
+ const result = base2.parse(input, pos, ctx);
8228
+ if (!result.ok) trace?.write({ id, phase: "rollback", offset: pos });
8229
+ return result;
8230
+ }
8231
+ };
8232
+ }
8233
+ case "sepBy":
8234
+ return sepBy(build(def.parser), build(def.separator));
8235
+ case "transform":
8236
+ return transform(build(def.parser), def.fn);
8237
+ case "skip":
8238
+ return skip(build(def.main), build(def.skipped));
8239
+ case "trivia":
8240
+ return trivia(build(def.parser));
8241
+ case "token":
8242
+ return token(build(def.parser));
8243
+ case "leaf":
8244
+ return leaf(build(def.parser), def.fn);
8245
+ case "field":
8246
+ return field(def.name, build(def.parser));
8247
+ case "grammar":
8248
+ return parser({
8249
+ ...def.triviaParser === void 0 ? def.clearTrivia ? { trivia: null } : {} : { trivia: build(def.triviaParser) },
8250
+ ...def.captureTrivia ? { captureTrivia: true } : {},
8251
+ ...def.trackLines ? { trackLines: true } : {}
8252
+ }, build(def.parser));
8253
+ case "not":
8254
+ return not(build(def.parser));
8255
+ case "node": {
8256
+ const opts = { ...def.unwrap ? { unwrap: true } : {}, ...def.collapse ? { collapse: true } : {}, ...def.captureTrivia ? { captureTrivia: true } : {}, ...def.trailingTrivia ? { trailingTrivia: true } : {} };
8257
+ return def.type === void 0 ? node(build(def.parser), def.build, opts) : node(def.type, build(def.parser), def.build, opts);
8258
+ }
8259
+ case "guard":
8260
+ return guard(def.predicate);
8261
+ case "withCtx":
8262
+ return withCtx(def.extra, build(def.parser));
8263
+ case "expect":
8264
+ return expect(build(def.parser), def.label);
8265
+ case "scanTo":
8266
+ return scanTo(build(def.sentinel), { skip: def.skip.map(build), orEOF: def.orEOF });
8267
+ case "literal":
8268
+ case "regex":
8269
+ case "keywords":
8270
+ return parser2;
8271
+ case "recover":
8272
+ case "unknown":
8273
+ throw new TypeError(`runWithGrammarCoverage does not yet support ${def.tag}`);
8274
+ default:
8275
+ return parser2;
8276
+ }
8277
+ })();
8278
+ return wrap((input, pos, ctx) => base.parse(input, pos, ctx));
8279
+ };
8280
+ return build(entry);
8281
+ }
8282
+ function runWithGrammarCoverage(entry, input, options = {}) {
8283
+ if (typeof entry === "function") throw new TypeError("runWithGrammarCoverage currently requires an interpreter combinator entry");
8284
+ const plan = buildGrammarPlan(entry);
8285
+ const definitions = plan.definitions;
8286
+ const collector = options.collector ?? createGrammarCoverageCollector(definitions);
8287
+ const { collector: _collector, trace, ...runOptions } = options;
8288
+ return { result: run(coverageEntry(entry, collector, plan, trace), input, runOptions), coverage: collector.snapshot() };
8289
+ }
8290
+
7552
8291
  // src/cst/trivia-index.ts
7553
8292
  function merge(map, key, run2) {
7554
8293
  if (run2.length === 0) return;
@@ -7619,9 +8358,9 @@ function buildTriviaIndex(root, input = "", opts) {
7619
8358
  function walk(root, visitor, ctx = void 0) {
7620
8359
  const go = (node2, parent) => {
7621
8360
  const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
7622
- const children = node2.children;
7623
- if (descend !== false && Array.isArray(children)) {
7624
- for (const child of children) go(child, node2);
8361
+ const children2 = node2.children;
8362
+ if (descend !== false && Array.isArray(children2)) {
8363
+ for (const child of children2) go(child, node2);
7625
8364
  }
7626
8365
  visitor.leave?.(node2, parent, ctx);
7627
8366
  };
@@ -7637,9 +8376,9 @@ function createVisitor(handlers) {
7637
8376
  },
7638
8377
  visitChildren(node2) {
7639
8378
  const out = [];
7640
- const children = node2.children;
7641
- if (Array.isArray(children)) {
7642
- for (const child of children) {
8379
+ const children2 = node2.children;
8380
+ if (Array.isArray(children2)) {
8381
+ for (const child of children2) {
7643
8382
  const r = api.visit(child);
7644
8383
  if (r !== void 0) out.push(r);
7645
8384
  }