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.js CHANGED
@@ -59,6 +59,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
59
59
  case "label":
60
60
  case "trivia":
61
61
  case "token":
62
+ case "leaf":
62
63
  case "expect":
63
64
  case "withCtx":
64
65
  case "node":
@@ -122,6 +123,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
122
123
  case "label":
123
124
  case "trivia":
124
125
  case "token":
126
+ case "leaf":
125
127
  case "node":
126
128
  case "grammar":
127
129
  case "expect":
@@ -190,10 +192,10 @@ function beginCstNodeCapture(ctx) {
190
192
  }
191
193
  function finishCstBuf(buf) {
192
194
  if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL };
193
- const children = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
195
+ const children2 = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
194
196
  const rawChildren = buf.raw ?? (buf.rawSingle !== void 0 ? [buf.rawSingle] : EMPTY);
195
197
  const triviaLog = buf.tl ?? EMPTY_TL;
196
- return { children, rawChildren, triviaLog };
198
+ return { children: children2, rawChildren, triviaLog };
197
199
  }
198
200
  function endCstNodeCapture(ctx, saved) {
199
201
  const materialized = finishCstBuf(ctx._cstBuf);
@@ -205,8 +207,8 @@ function endCstNodeCapture(ctx, saved) {
205
207
  ctx.captureTrivia = saved.cap;
206
208
  return materialized;
207
209
  }
208
- function pushCstLeaf(ctx, leaf) {
209
- pushCstChild(ctx, leaf, leaf);
210
+ function pushCstLeaf(ctx, leaf2) {
211
+ pushCstChild(ctx, leaf2, leaf2);
210
212
  }
211
213
  function pushCstChild(ctx, built, rawEntry) {
212
214
  const b = ctx._cstBuf;
@@ -940,10 +942,13 @@ function deriveExpected(c) {
940
942
  return def.parsers.flatMap(deriveExpected);
941
943
  case "sequence":
942
944
  return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
945
+ case "attempt":
946
+ return deriveExpected(def.parser);
943
947
  case "node":
944
948
  case "grammar":
945
949
  case "trivia":
946
950
  case "token":
951
+ case "leaf":
947
952
  case "optional":
948
953
  case "many":
949
954
  case "oneOrMore":
@@ -1721,6 +1726,24 @@ function sequence(...parsers) {
1721
1726
  };
1722
1727
  }
1723
1728
 
1729
+ // src/combinators/attempt.ts
1730
+ function attempt(parser2) {
1731
+ return {
1732
+ _tag: "attempt",
1733
+ _meta: parser2._meta,
1734
+ _def: { tag: "attempt", parser: parser2 },
1735
+ parse(input, pos, ctx) {
1736
+ const mark = saveCstMark(ctx);
1737
+ const log = ctx._triviaLog?.length;
1738
+ const result = parser2.parse(input, pos, ctx);
1739
+ if (result.ok) return result;
1740
+ rollbackCstCapture(ctx, mark);
1741
+ if (log !== void 0 && ctx._triviaLog) ctx._triviaLog.length = log;
1742
+ return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
1743
+ }
1744
+ };
1745
+ }
1746
+
1724
1747
  // src/combinators/repeat.ts
1725
1748
  function repItem(combinator, input, cur, ctx) {
1726
1749
  const mark = saveTriviaMark(ctx);
@@ -2030,10 +2053,14 @@ function markUnusedValues(root) {
2030
2053
  case "label":
2031
2054
  case "trivia":
2032
2055
  case "token":
2056
+ case "attempt":
2033
2057
  case "withCtx":
2034
2058
  case "expect":
2035
2059
  visit(def.parser, consumed);
2036
2060
  return;
2061
+ case "leaf":
2062
+ visit(def.parser, true);
2063
+ return;
2037
2064
  case "lazy":
2038
2065
  return;
2039
2066
  default:
@@ -2156,6 +2183,20 @@ function confirmedBuildArity(src) {
2156
2183
  if (/\barguments\b/.test(s)) return null;
2157
2184
  return parts.length;
2158
2185
  }
2186
+ function buildReadsChildren(def) {
2187
+ if (!def.build) return true;
2188
+ const src = def.buildSrc ?? def.build.toString();
2189
+ const arity = confirmedBuildArity(src);
2190
+ if (arity === null) return true;
2191
+ return arity >= 1;
2192
+ }
2193
+ function buildReadsRaw(def) {
2194
+ if (!def.build) return true;
2195
+ const src = def.buildSrc ?? def.build.toString();
2196
+ const arity = confirmedBuildArity(src);
2197
+ if (arity === null) return true;
2198
+ return arity >= 4;
2199
+ }
2159
2200
  function buildReadsTrivia(def) {
2160
2201
  if (!def.build) return true;
2161
2202
  const src = def.buildSrc ?? def.build.toString();
@@ -2211,9 +2252,11 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
2211
2252
  case "many":
2212
2253
  case "oneOrMore":
2213
2254
  case "optional":
2255
+ case "attempt":
2214
2256
  case "transform":
2215
2257
  case "trivia":
2216
2258
  case "token":
2259
+ case "leaf":
2217
2260
  case "label":
2218
2261
  case "expect":
2219
2262
  case "withCtx":
@@ -2246,9 +2289,11 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
2246
2289
  case "keywords":
2247
2290
  case "guard":
2248
2291
  case "token":
2292
+ case "leaf":
2249
2293
  return false;
2250
2294
  // Transparent single-child wrappers: recurse.
2251
2295
  case "optional":
2296
+ case "attempt":
2252
2297
  case "transform":
2253
2298
  case "trivia":
2254
2299
  case "label":
@@ -2295,6 +2340,7 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
2295
2340
  case "many":
2296
2341
  case "oneOrMore":
2297
2342
  case "optional":
2343
+ case "attempt":
2298
2344
  case "transform":
2299
2345
  case "trivia":
2300
2346
  case "token":
@@ -2304,6 +2350,10 @@ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
2304
2350
  case "withCtx":
2305
2351
  case "not":
2306
2352
  return parserEnablesTriviaCapture(d.parser, seen);
2353
+ // leaf() clears the inner CST/trivia collector and publishes only its own
2354
+ // terminal result. An inner parser() capture cannot reach this node.
2355
+ case "leaf":
2356
+ return false;
2307
2357
  case "lazy": {
2308
2358
  try {
2309
2359
  return parserEnablesTriviaCapture(d.thunk(), seen);
@@ -2382,12 +2432,12 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2382
2432
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
2383
2433
  ctx._fields = savedFields;
2384
2434
  ctx._triviaCaptureMask = savedMask;
2385
- const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
2435
+ const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
2386
2436
  if (!r.ok) return r;
2387
2437
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2388
2438
  const nodeType = def.type ?? missingInferredType();
2389
2439
  const cstOutput = ctx.build?._parsemanCstOutput === true;
2390
- 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 };
2440
+ 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 };
2391
2441
  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 };
2392
2442
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2393
2443
  pushCstChild(ctx, built, rawEntry);
@@ -2618,6 +2668,141 @@ function token(root) {
2618
2668
  }
2619
2669
  };
2620
2670
  }
2671
+ function leaf(root, fn) {
2672
+ const meta = {
2673
+ firstSet: root._meta.firstSet,
2674
+ canMatchNewline: root._meta.canMatchNewline,
2675
+ isTrivia: false
2676
+ };
2677
+ return {
2678
+ _tag: "leaf",
2679
+ _meta: meta,
2680
+ _def: { tag: "leaf", parser: root, fn },
2681
+ parse(input, pos, ctx) {
2682
+ const savedBuf = ctx._cstBuf;
2683
+ const savedChildren = ctx._cstChildren;
2684
+ const savedLeaves = ctx._cstLeaves;
2685
+ const savedRaw = ctx._cstRawChildren;
2686
+ const savedTriviaLog = ctx._cstTriviaLog;
2687
+ const savedOuterTriviaLog = ctx._triviaLog;
2688
+ const wasCapturing = cstCaptureActive(ctx);
2689
+ ctx._cstBuf = void 0;
2690
+ ctx._cstChildren = void 0;
2691
+ ctx._cstLeaves = void 0;
2692
+ ctx._cstRawChildren = void 0;
2693
+ ctx._cstTriviaLog = void 0;
2694
+ delete ctx._triviaLog;
2695
+ let result;
2696
+ try {
2697
+ result = root.parse(input, pos, ctx);
2698
+ } finally {
2699
+ ctx._cstBuf = savedBuf;
2700
+ ctx._cstChildren = savedChildren;
2701
+ ctx._cstLeaves = savedLeaves;
2702
+ ctx._cstRawChildren = savedRaw;
2703
+ ctx._cstTriviaLog = savedTriviaLog;
2704
+ if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
2705
+ else ctx._triviaLog = savedOuterTriviaLog;
2706
+ }
2707
+ if (!result.ok) return result;
2708
+ const value = fn(result.value, result.span);
2709
+ if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span: result.span });
2710
+ return { ok: true, value, span: result.span };
2711
+ }
2712
+ };
2713
+ }
2714
+
2715
+ // src/compiler/grammar-coverage-ids.ts
2716
+ function children(def, winners) {
2717
+ switch (def.tag) {
2718
+ case "sequence":
2719
+ case "choice":
2720
+ return def.parsers;
2721
+ case "many":
2722
+ case "oneOrMore":
2723
+ case "optional":
2724
+ case "attempt":
2725
+ case "transform":
2726
+ case "trivia":
2727
+ case "token":
2728
+ case "leaf":
2729
+ case "label":
2730
+ case "field":
2731
+ case "grammar":
2732
+ case "not":
2733
+ case "node":
2734
+ case "guard":
2735
+ case "withCtx":
2736
+ case "recover":
2737
+ case "expect":
2738
+ return "parser" in def ? [def.parser] : [];
2739
+ case "sepBy":
2740
+ return [def.parser, def.separator];
2741
+ case "skip":
2742
+ return [def.main, def.skipped];
2743
+ case "scanTo":
2744
+ return [def.sentinel, ...def.skip];
2745
+ case "lazy": {
2746
+ let resolved;
2747
+ try {
2748
+ resolved = def.thunk();
2749
+ } catch {
2750
+ return [];
2751
+ }
2752
+ const name = resolved._ruleName;
2753
+ return name && winners?.[name] ? [winners[name]] : [resolved];
2754
+ }
2755
+ default:
2756
+ return [];
2757
+ }
2758
+ }
2759
+ function buildGrammarPlan(entry, winners) {
2760
+ const definitions = /* @__PURE__ */ new Map();
2761
+ const choices = /* @__PURE__ */ new WeakMap();
2762
+ const attempts = /* @__PURE__ */ new WeakMap();
2763
+ const labels = /* @__PURE__ */ new WeakMap();
2764
+ const rules2 = /* @__PURE__ */ new WeakMap();
2765
+ const winnerNames = /* @__PURE__ */ new Map();
2766
+ if (winners) {
2767
+ for (const [name, winner] of Object.entries(winners)) {
2768
+ winnerNames.set(winner, name);
2769
+ if (winner._def.tag === "lazy") {
2770
+ try {
2771
+ winnerNames.set(winner._def.thunk(), name);
2772
+ } catch {
2773
+ }
2774
+ }
2775
+ }
2776
+ }
2777
+ const seen = /* @__PURE__ */ new Set();
2778
+ const visit = (parser2, path) => {
2779
+ if (seen.has(parser2)) return;
2780
+ seen.add(parser2);
2781
+ const rule = winnerNames.get(parser2) ?? parser2._ruleName;
2782
+ if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
2783
+ const id = `rule:${rule}`;
2784
+ definitions.set(id, { id, kind: "rule" });
2785
+ rules2.set(parser2, id);
2786
+ }
2787
+ if (parser2._def.tag === "choice") {
2788
+ const ids = parser2._def.parsers.map((_, index) => `choice:${path}/arm:${index}`);
2789
+ choices.set(parser2, ids);
2790
+ ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
2791
+ }
2792
+ if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path}`);
2793
+ if (parser2._def.tag === "label") {
2794
+ const id = `label:${path}`;
2795
+ labels.set(parser2, [id]);
2796
+ definitions.set(id, { id, kind: "label" });
2797
+ }
2798
+ children(parser2._def, winners).forEach((child, index) => visit(child, `${path}/${parser2._def.tag}:${index}`));
2799
+ };
2800
+ const roots = Array.isArray(entry) ? entry : [entry];
2801
+ for (const root of roots) {
2802
+ visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
2803
+ }
2804
+ return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
2805
+ }
2621
2806
 
2622
2807
  // src/compiler/scannable-run.ts
2623
2808
  var BACKSLASH = 92;
@@ -4002,21 +4187,23 @@ function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
4002
4187
  }
4003
4188
  function failBody(ctx, expected, posExpr) {
4004
4189
  const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
4190
+ const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
4005
4191
  if (ctx.failLabel) {
4006
- if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4007
- return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
4192
+ if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4193
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
4008
4194
  }
4009
- return probe + failReturn(expected, posExpr);
4195
+ return probe + trace + failReturn(expected, posExpr);
4010
4196
  }
4011
4197
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
4012
4198
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
4199
+ const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
4013
4200
  if (ctx.failLabel) {
4014
- if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4201
+ if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4015
4202
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
4016
- return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
4203
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
4017
4204
  }
4018
- if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
4019
- return probe + failReturnArr(expectedArr, posExpr);
4205
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
4206
+ return probe + trace + failReturnArr(expectedArr, posExpr);
4020
4207
  }
4021
4208
  function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
4022
4209
  return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
@@ -4051,7 +4238,7 @@ var NAMED_FN_END = "_pfEnd";
4051
4238
  function namedFnPrelude() {
4052
4239
  return [`const ${NAMED_FN_FAIL} = {}`, `let ${NAMED_FN_END}`];
4053
4240
  }
4054
- function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
4241
+ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar, failureRuleId) {
4055
4242
  ctx.namedFnDecls.push([
4056
4243
  `function ${fnName}(input, _pos, _ctx) {`,
4057
4244
  ` _pfail: {`,
@@ -4059,6 +4246,7 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
4059
4246
  ` ${NAMED_FN_END} = ${endVar}`,
4060
4247
  ` return ${valueVar}`,
4061
4248
  ` }`,
4249
+ ...failureRuleId === void 0 ? [] : [` _ctx._grammarTrace?.write({ id: ${JSON.stringify(failureRuleId)}, phase: 'failure', offset: _ctx._fe ?? _pos })`],
4062
4250
  ` return ${NAMED_FN_FAIL}`,
4063
4251
  `}`
4064
4252
  ].join("\n"));
@@ -4220,6 +4408,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
4220
4408
  case "not":
4221
4409
  case "trivia":
4222
4410
  case "token":
4411
+ case "leaf":
4223
4412
  case "scanTo":
4224
4413
  case "unknown":
4225
4414
  return false;
@@ -4231,6 +4420,8 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
4231
4420
  // nothing committed remains.
4232
4421
  case "choice":
4233
4422
  return false;
4423
+ case "attempt":
4424
+ return false;
4234
4425
  // optional never fails; many/oneOrMore only "fail" with zero captured items.
4235
4426
  case "optional":
4236
4427
  case "many":
@@ -4283,6 +4474,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
4283
4474
  case "keywords":
4284
4475
  case "node":
4285
4476
  case "token":
4477
+ case "leaf":
4286
4478
  return true;
4287
4479
  case "not":
4288
4480
  case "guard":
@@ -4297,6 +4489,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
4297
4489
  case "many":
4298
4490
  case "oneOrMore":
4299
4491
  case "optional":
4492
+ case "attempt":
4300
4493
  case "transform":
4301
4494
  case "label":
4302
4495
  case "field":
@@ -4638,35 +4831,55 @@ function needsFirstSetGuard(p) {
4638
4831
  const fs = p._meta.firstSet;
4639
4832
  return fs.kind !== "any" && !canMatchEmptyAtStart(p);
4640
4833
  }
4641
- function emitDisjointArm(p, ctx, pos, valV, endV) {
4642
- if (failsAtStart(p)) {
4834
+ function coverageHit(ctx, id, offset = "_pos", end) {
4835
+ return id === void 0 ? [] : [
4836
+ `${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`,
4837
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'selected', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`,
4838
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`
4839
+ ];
4840
+ }
4841
+ function coverageAttempt(ctx, id, offset) {
4842
+ return id === void 0 ? [] : [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'attempt', offset: ${offset} })`];
4843
+ }
4844
+ function coverageFailureBacktrack(ctx, id, failureOffset, startOffset) {
4845
+ return id === void 0 ? [] : [
4846
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'failure', offset: ${failureOffset} })`,
4847
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'backtrack', offset: ${startOffset} })`
4848
+ ];
4849
+ }
4850
+ function emitDisjointArm(p, ctx, pos, valV, endV, coverageId) {
4851
+ if (failsAtStart(p) && coverageId === void 0) {
4643
4852
  const r = emit(p, ctx, pos);
4644
- return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
4853
+ return [...coverageAttempt(ctx, coverageId, pos), ...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`, ...coverageHit(ctx, coverageId, pos, r.endVar)];
4645
4854
  }
4646
4855
  const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
4647
4856
  return [
4857
+ ...coverageAttempt(ctx, coverageId, pos),
4648
4858
  ...stmts,
4649
- ...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
4859
+ ...emitIfFail(ctx, `!${okVar}`, `${coverageFailureBacktrack(ctx, coverageId, "_ctx._fe", pos).join("; ")}; ${failArrBody(ctx, "_ctx._fx", pos, false)}`),
4650
4860
  `${ind(ctx)}${valV} = ${valVar}`,
4651
- `${ind(ctx)}${endV} = ${endVar}`
4861
+ `${ind(ctx)}${endV} = ${endVar}`,
4862
+ ...coverageHit(ctx, coverageId, pos, endVar)
4652
4863
  ];
4653
4864
  }
4654
- function emitDisjointArmGated(def, i, ctx, pos, valV, endV) {
4865
+ function emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageId) {
4655
4866
  const p = def.parsers[i];
4656
4867
  const gate = def.gates[i];
4657
- if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV);
4868
+ if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV, coverageId);
4658
4869
  const gateIdx = ctx.mapFns.length;
4659
4870
  ctx.mapFns.push(gate);
4660
4871
  ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4661
4872
  const gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4662
4873
  const stmts = [`${ind(ctx)}if (${gateCond}) {`];
4663
4874
  ctx.indent++;
4664
- stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
4875
+ stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV, coverageId));
4665
4876
  ctx.indent--;
4666
4877
  stmts.push(`${ind(ctx)}} else { ${failArrBody(ctx, deriveExpectedArr([p]), pos)} }`);
4667
4878
  return stmts;
4668
4879
  }
4669
- function emitChoice(def, ctx, pos) {
4880
+ function emitChoice(parser2, def, ctx, pos) {
4881
+ const coverageIds = ctx.coverage?.plan.choices.get(parser2);
4882
+ const coverageBase = coverageIds?.[0]?.slice(0, -1);
4670
4883
  const allExpected = deriveExpectedArr(def.parsers);
4671
4884
  if (def.disjoint) {
4672
4885
  const codeV = v(ctx, "_code");
@@ -4685,7 +4898,7 @@ function emitChoice(def, ctx, pos) {
4685
4898
  stmts.push(`${ind(ctx)}{`);
4686
4899
  ctx.indent++;
4687
4900
  stmts.push(
4688
- ...emitDisjointArmGated(def, i, ctx, pos, valV, endV),
4901
+ ...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]),
4689
4902
  `${ind(ctx)}break`
4690
4903
  );
4691
4904
  ctx.indent--;
@@ -4704,16 +4917,16 @@ function emitChoice(def, ctx, pos) {
4704
4917
  first = false;
4705
4918
  stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
4706
4919
  ctx.indent++;
4707
- stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV));
4920
+ stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]));
4708
4921
  ctx.indent--;
4709
4922
  stmts.push(`${ind(ctx)}}`);
4710
4923
  }
4711
4924
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
4712
4925
  return { stmts, valueVar: valV, endVar: endV };
4713
4926
  }
4714
- return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
4927
+ return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos, coverageBase);
4715
4928
  }
4716
- function emitGreedyClassify(def, superIndex, ctx, pos) {
4929
+ function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
4717
4930
  const superParser = def.parsers[superIndex];
4718
4931
  const regexDef = getCoreRegexDef(superParser);
4719
4932
  const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
@@ -4729,10 +4942,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4729
4942
  const endV = v(ctx, "_ge");
4730
4943
  const valV = v(ctx, "_gcv");
4731
4944
  const regexExpected = JSON.stringify(deriveExpected(superParser));
4945
+ const superCoverageId = coverageBase === void 0 ? void 0 : `${coverageBase}${superIndex}`;
4732
4946
  const stmts = [
4947
+ ...coverageAttempt(ctx, superCoverageId, pos),
4733
4948
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
4734
4949
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
4735
- ...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
4950
+ ...emitIfFail(ctx, `${matchV} === null`, `${coverageFailureBacktrack(ctx, superCoverageId, pos, pos).join("; ")}; ${failArrBody(ctx, regexExpected, pos)}`),
4736
4951
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
4737
4952
  `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
4738
4953
  `${ind(ctx)}let ${valV}`
@@ -4748,8 +4963,9 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4748
4963
  stmts.push(`${ind(ctx)}${kw} (${wordV} === ${JSON.stringify(litVal)}) {`);
4749
4964
  ctx.indent++;
4750
4965
  const tR = emitTransformChain(p, JSON.stringify(litVal), endV, pos, ctx);
4966
+ stmts.push(...coverageFailureBacktrack(ctx, superCoverageId, pos, pos));
4751
4967
  stmts.push(...emitLeafCapture(ctx, JSON.stringify(litVal), pos, endV));
4752
- stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`);
4968
+ stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`, ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endV));
4753
4969
  ctx.indent--;
4754
4970
  stmts.push(`${ind(ctx)}}`);
4755
4971
  }
@@ -4757,12 +4973,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4757
4973
  const regexKw = first ? "if" : "else";
4758
4974
  stmts.push(`${ind(ctx)}${regexKw} {`);
4759
4975
  ctx.indent++;
4760
- stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}`);
4976
+ 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));
4761
4977
  ctx.indent--;
4762
4978
  stmts.push(`${ind(ctx)}}`);
4763
4979
  return { stmts, valueVar: valV, endVar: endV };
4764
4980
  }
4765
- function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4981
+ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, coverageBase) {
4766
4982
  const valV = v(ctx, "_llv");
4767
4983
  const endV = v(ctx, "_lle");
4768
4984
  const stmts = [`${ind(ctx)}let ${valV}, ${endV} = ${pos}`];
@@ -4779,10 +4995,12 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4779
4995
  ctx.indent++;
4780
4996
  const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
4781
4997
  stmts.push(
4998
+ ...coverageAttempt(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos),
4782
4999
  ...emitLeafCapture(ctx, JSON.stringify(litVal), pos, litEnd),
4783
5000
  ...tR.stmts,
4784
5001
  `${ind(ctx)}${valV} = ${tR.valueVar}`,
4785
- `${ind(ctx)}${endV} = ${litEnd}`
5002
+ `${ind(ctx)}${endV} = ${litEnd}`,
5003
+ ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos, litEnd)
4786
5004
  );
4787
5005
  ctx.indent--;
4788
5006
  stmts.push(`${ind(ctx)}}`);
@@ -4790,16 +5008,18 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4790
5008
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
4791
5009
  return { stmts, valueVar: valV, endVar: endV };
4792
5010
  }
4793
- function emitFirstMatch(def, ctx, pos) {
5011
+ function emitFirstMatch(def, ctx, pos, coverageBase) {
4794
5012
  const resValV = v(ctx, "_crv");
4795
5013
  const resEndV = v(ctx, "_cre");
4796
5014
  const resOkV = v(ctx, "_crok");
4797
5015
  const codeV = v(ctx, "_chcode");
4798
5016
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
5017
+ const autoRejectedV = coverageBase === void 0 ? void 0 : v(ctx, "_carej");
4799
5018
  const ind0 = ind(ctx);
4800
5019
  const stmts = [
4801
5020
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
4802
5021
  `${ind0}let ${slots.join(", ")}`,
5022
+ ...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
4803
5023
  `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
4804
5024
  ];
4805
5025
  for (let i = 0; i < def.parsers.length; i++) {
@@ -4835,6 +5055,7 @@ function emitFirstMatch(def, ctx, pos) {
4835
5055
  stmts.push(`${ind0}if (${skipCond}) {`);
4836
5056
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
4837
5057
  ctx.indent += fsGuard ? 2 : 1;
5058
+ if (coverageBase !== void 0) stmts.push(`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'attempt', offset: ${pos} })`);
4838
5059
  if (markLeaves) {
4839
5060
  stmts.push(
4840
5061
  `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
@@ -4857,13 +5078,21 @@ function emitFirstMatch(def, ctx, pos) {
4857
5078
  stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
4858
5079
  stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
4859
5080
  stmts.push(`${ind(ctx)} ${resOkV} = true`);
5081
+ if (autoRejectedV !== void 0) {
5082
+ 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} }) }`);
5083
+ }
5084
+ stmts.push(...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endVar));
4860
5085
  stmts.push(`${ind(ctx)} }`);
5086
+ if (autoRejectedV !== void 0) {
5087
+ stmts.push(`${ind(ctx)} else { ${autoRejectedV}.push(${JSON.stringify(`${coverageBase}${i}`)}) }`);
5088
+ }
4861
5089
  stmts.push(`${ind(ctx)}}`);
4862
- stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
5090
+ 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} });`} }`);
4863
5091
  if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
4864
5092
  } else {
4865
- stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
4866
- stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
5093
+ 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} }) }`;
5094
+ 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} })`} }`);
5095
+ 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} })`} }`);
4867
5096
  }
4868
5097
  ctx.indent -= fsGuard ? 2 : 1;
4869
5098
  if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
@@ -4873,12 +5102,12 @@ function emitFirstMatch(def, ctx, pos) {
4873
5102
  stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
4874
5103
  return { stmts, valueVar: resValV, endVar: resEndV };
4875
5104
  }
4876
- function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
5105
+ function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
4877
5106
  if (strategy.tag === "greedyClassify")
4878
- return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
5107
+ return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
4879
5108
  if (strategy.tag === "literalsLongestFirst")
4880
- return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
4881
- return emitFirstMatch(def, ctx, pos);
5109
+ return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
5110
+ return emitFirstMatch(def, ctx, pos, coverageBase);
4882
5111
  }
4883
5112
  function pushMapFn(ctx, fn, src) {
4884
5113
  if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
@@ -4996,6 +5225,27 @@ function emitOptional(def, ctx, pos) {
4996
5225
  ];
4997
5226
  return { stmts, valueVar: valV, endVar: endV };
4998
5227
  }
5228
+ function emitAttempt(p, def, ctx, pos) {
5229
+ const inner = emitFallible(def.parser, ctx, pos);
5230
+ const leaves = v(ctx, "_atl");
5231
+ const raw = v(ctx, "_atr");
5232
+ const trivia2 = v(ctx, "_att");
5233
+ const log = v(ctx, "_atg");
5234
+ const fields = v(ctx, "_atf");
5235
+ const errors = v(ctx, "_ate");
5236
+ 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}`;
5237
+ const traceId = ctx.coverage?.plan.attempts.get(p);
5238
+ const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
5239
+ return {
5240
+ stmts: [
5241
+ `${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`,
5242
+ ...inner.stmts,
5243
+ ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
5244
+ ],
5245
+ valueVar: inner.valVar,
5246
+ endVar: inner.endVar
5247
+ };
5248
+ }
4999
5249
  function emitSepBy(_p, def, ctx, pos) {
5000
5250
  const arrV = v(ctx, "_arr");
5001
5251
  const curV = v(ctx, "_cur");
@@ -5286,6 +5536,36 @@ function emitToken(def, ctx, pos) {
5286
5536
  endVar: inner.endVar
5287
5537
  };
5288
5538
  }
5539
+ function emitLeaf(def, ctx, pos) {
5540
+ const inner = emitFallible(def.parser, ctx, pos);
5541
+ const i = ind(ctx);
5542
+ const sc = v(ctx, "_leafCh");
5543
+ const sl = v(ctx, "_leafLv");
5544
+ const sr = v(ctx, "_leafRaw");
5545
+ const stl = v(ctx, "_leafTl");
5546
+ const sol = v(ctx, "_leafLog");
5547
+ const sb = v(ctx, "_leafBuf");
5548
+ const rawV = v(ctx, "_leafRawValue");
5549
+ const valV = v(ctx, "_leaf");
5550
+ const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
5551
+ return {
5552
+ stmts: [
5553
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${stl} = _ctx._cstTriviaLog, ${sol} = _ctx._triviaLog, ${sb} = _ctx._cstBuf`,
5554
+ `${i}_ctx._cstChildren = undefined; _ctx._cstLeaves = undefined; _ctx._cstRawChildren = undefined; _ctx._cstTriviaLog = undefined; _ctx._triviaLog = undefined; _ctx._cstBuf = undefined`,
5555
+ `${i}try {`,
5556
+ ...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
5557
+ `${i}} finally {`,
5558
+ `${i} _ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx._cstTriviaLog = ${stl}; _ctx._triviaLog = ${sol}; _ctx._cstBuf = ${sb}`,
5559
+ `${i}}`,
5560
+ ...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
5561
+ `${i}const ${rawV} = ${inner.valVar}`,
5562
+ `${i}const ${valV} = ${mfRef(ctx)}[${fnIdx}](${rawV}, { start: ${pos}, end: ${inner.endVar} })`,
5563
+ ...emitLeafCapture(ctx, valV, pos, inner.endVar)
5564
+ ],
5565
+ valueVar: valV,
5566
+ endVar: inner.endVar
5567
+ };
5568
+ }
5289
5569
  function emitNode(def, ctx, pos) {
5290
5570
  if (def.type === void 0) {
5291
5571
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
@@ -5301,6 +5581,8 @@ function emitNode(def, ctx, pos) {
5301
5581
  const i = ind(ctx);
5302
5582
  const capturesTrivia = mkType !== null || def.captureTrivia === true || def.trailingTrivia === true || !structural && buildReadsTrivia(def);
5303
5583
  const clonesState = !structural && buildReadsState(def);
5584
+ const capturesChildren = !structural && (mkType !== null || def.unwrap || def.collapse || buildReadsChildren(def));
5585
+ const capturesRaw = !structural && (mkType !== null || buildReadsRaw(def));
5304
5586
  const hasFields = parserHasOwnFields(def.parser);
5305
5587
  const capturesFields = hasFields && !structural && buildReadsFields(def);
5306
5588
  const innerEnablesTriviaCapture = parserEnablesTriviaCapture(def.parser);
@@ -5319,9 +5601,13 @@ function emitNode(def, ctx, pos) {
5319
5601
  const profHoist = `${i}const ${pmV} = _ctx._pmProfile, ${recV} = ${pmV}?.phase === 'recognizer', ${capV} = ${pmV}?.phase === 'capture'`;
5320
5602
  const profileRecognizer = recV;
5321
5603
  const profileCapture = capV;
5604
+ const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
5605
+ const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
5322
5606
  const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
5323
5607
  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)))` : ""}
5324
- ${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 : []`;
5608
+ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5609
+ ` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5610
+ ` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
5325
5611
  const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
5326
5612
  const fieldsOn = structural ? capFv ?? "false" : `${profileRecognizer} ? false : ${capturesFields ? "true" : "false"}`;
5327
5613
  const sf = hasFields ? v(ctx, "_sf") : null;
@@ -5438,14 +5724,23 @@ function emitLazy(p, def, ctx, pos) {
5438
5724
  const savedIndent = ctx.indent;
5439
5725
  const savedFailLabel = ctx.failLabel;
5440
5726
  const savedRecord = ctx.recordFail;
5727
+ const failureRuleId = p === ctx.coverage?.entry ? void 0 : ctx.coverage?.plan.rules.get(p) ?? ctx.coverage?.plan.rules.get(resolved);
5728
+ const savedActiveRule = ctx.activeCoverageRuleId;
5729
+ const savedSuppressFailure = ctx.suppressCoverageFailure;
5441
5730
  ctx.indent = 1;
5442
5731
  ctx.failLabel = "_pfail";
5443
5732
  ctx.recordFail = true;
5733
+ if (failureRuleId !== void 0) {
5734
+ ctx.activeCoverageRuleId = void 0;
5735
+ ctx.suppressCoverageFailure = true;
5736
+ }
5444
5737
  const r = emit(resolved, ctx, "_pos");
5445
5738
  ctx.indent = savedIndent;
5446
5739
  ctx.failLabel = savedFailLabel;
5447
5740
  ctx.recordFail = savedRecord;
5448
- pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
5741
+ ctx.activeCoverageRuleId = savedActiveRule;
5742
+ ctx.suppressCoverageFailure = savedSuppressFailure;
5743
+ pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar, failureRuleId);
5449
5744
  }
5450
5745
  const fnName = ctx.namedParsers.get(p);
5451
5746
  return emitNamedFnCall(ctx, fnName, pos);
@@ -5496,6 +5791,30 @@ function emitExpect(def, ctx, pos) {
5496
5791
  return { stmts, valueVar: valVar, endVar };
5497
5792
  }
5498
5793
  function emit(p, ctx, pos) {
5794
+ const dispatch = (emissionPos = pos) => {
5795
+ const savedRule = ctx.activeCoverageRuleId;
5796
+ const rule = ctx.coverage?.plan.rules.get(p);
5797
+ if (rule && p !== ctx.coverage?.entry && !ctx.suppressCoverageFailure) ctx.activeCoverageRuleId = rule;
5798
+ const result = emitDispatch(p, ctx, emissionPos);
5799
+ ctx.activeCoverageRuleId = savedRule;
5800
+ return result;
5801
+ };
5802
+ const instrument = (r) => {
5803
+ const ruleId = ctx.coverage?.plan.rules.get(p);
5804
+ const labelIds = ctx.coverage?.plan.labels.get(p);
5805
+ if (!ruleId && !labelIds) return r;
5806
+ return {
5807
+ ...r,
5808
+ stmts: [
5809
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`] : [],
5810
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: ${pos} })`] : [],
5811
+ ...r.stmts,
5812
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`] : [],
5813
+ ...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`]) ?? [],
5814
+ ...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`]) ?? []
5815
+ ]
5816
+ };
5817
+ };
5499
5818
  const usage = ctx.lazyUsage;
5500
5819
  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
5501
5820
  // overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
@@ -5512,14 +5831,14 @@ function emit(p, ctx, pos) {
5512
5831
  ctx.indent = 1;
5513
5832
  ctx.failLabel = "_pfail";
5514
5833
  ctx.recordFail = true;
5515
- const r = emitDispatch(p, ctx, "_pos");
5834
+ const r = dispatch("_pos");
5516
5835
  ctx.indent = savedIndent;
5517
5836
  ctx.failLabel = savedFailLabel;
5518
5837
  ctx.recordFail = savedRecord;
5519
5838
  pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
5520
- return emitNamedFnCall(ctx, fnName, pos);
5839
+ return instrument(emitNamedFnCall(ctx, fnName, pos));
5521
5840
  }
5522
- return emitDispatch(p, ctx, pos);
5841
+ return instrument(dispatch());
5523
5842
  }
5524
5843
  function emitDispatch(p, ctx, pos) {
5525
5844
  const def = p._def;
@@ -5533,7 +5852,9 @@ function emitDispatch(p, ctx, pos) {
5533
5852
  case "sequence":
5534
5853
  return emitSeq(def, ctx, pos);
5535
5854
  case "choice":
5536
- return emitChoice(def, ctx, pos);
5855
+ return emitChoice(p, def, ctx, pos);
5856
+ case "attempt":
5857
+ return emitAttempt(p, def, ctx, pos);
5537
5858
  case "many":
5538
5859
  case "oneOrMore":
5539
5860
  return emitMany(def, ctx, pos);
@@ -5602,6 +5923,8 @@ function emitDispatch(p, ctx, pos) {
5602
5923
  return emit(def.parser, ctx, pos);
5603
5924
  case "token":
5604
5925
  return emitToken(def, ctx, pos);
5926
+ case "leaf":
5927
+ return emitLeaf(def, ctx, pos);
5605
5928
  case "label": {
5606
5929
  const inner = emitFallible(def.parser, ctx, pos);
5607
5930
  return {
@@ -5733,6 +6056,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5733
6056
  case "grammar":
5734
6057
  case "trivia":
5735
6058
  case "token":
6059
+ case "leaf":
5736
6060
  case "label":
5737
6061
  case "field":
5738
6062
  case "optional":
@@ -5760,16 +6084,32 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5760
6084
  return false;
5761
6085
  }
5762
6086
  }
6087
+ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
6088
+ if (seen.has(p)) return false;
6089
+ seen.add(p);
6090
+ const d = p._def;
6091
+ if (d.tag === "node" && d.build !== void 0) return true;
6092
+ if (d.tag === "lazy") {
6093
+ try {
6094
+ return hasDirectBuildDef(d.thunk(), seen);
6095
+ } catch {
6096
+ return false;
6097
+ }
6098
+ }
6099
+ return childrenOf(d).some((child) => hasDirectBuildDef(child, seen));
6100
+ }
5763
6101
  function childrenOf(def) {
5764
6102
  switch (def.tag) {
5765
6103
  case "sequence":
5766
6104
  case "choice":
5767
6105
  return def.parsers;
5768
6106
  case "many":
6107
+ case "attempt":
5769
6108
  case "optional":
5770
6109
  case "transform":
5771
6110
  case "trivia":
5772
6111
  case "token":
6112
+ case "leaf":
5773
6113
  case "label":
5774
6114
  case "field":
5775
6115
  case "grammar":
@@ -5944,10 +6284,25 @@ function compile(combinator, mapFnSources, opts) {
5944
6284
  namedFnDecls: [],
5945
6285
  capturing: hasNodeDef(combinator),
5946
6286
  recovery: opts?.recovery ?? false,
6287
+ ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
5947
6288
  lazyUsage: analyzeLazyUsage(combinator),
5948
6289
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5949
6290
  };
5950
6291
  const r = emit(combinator, ctx, "_pos");
6292
+ const coverageRootRuleId = ctx.coverage?.plan.rules.get(combinator);
6293
+ const resultBody = coverageRootRuleId === void 0 ? [
6294
+ ` let pos = _pos`,
6295
+ ...r.stmts,
6296
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6297
+ ] : [
6298
+ ` const _coverageResult = (() => {`,
6299
+ ` let pos = _pos`,
6300
+ ...r.stmts,
6301
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6302
+ ` })()`,
6303
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6304
+ ` return _coverageResult`
6305
+ ];
5951
6306
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
5952
6307
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
5953
6308
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
@@ -5960,9 +6315,7 @@ function compile(combinator, mapFnSources, opts) {
5960
6315
  ...namedPrelude,
5961
6316
  ctx.namedFnDecls.join("\n\n"),
5962
6317
  `function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
5963
- ` let pos = _pos`,
5964
- ...r.stmts,
5965
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6318
+ ...resultBody,
5966
6319
  `}`
5967
6320
  ].join("\n");
5968
6321
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
@@ -5972,9 +6325,15 @@ function compile(combinator, mapFnSources, opts) {
5972
6325
  ...ctx.expectedDecls,
5973
6326
  ...namedPrelude,
5974
6327
  ...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
5975
- `let pos = _pos`,
5976
- ...r.stmts,
5977
- `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6328
+ ...coverageRootRuleId === void 0 ? [`let pos = _pos`, ...r.stmts, `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`] : [
6329
+ `const _coverageResult = (() => {`,
6330
+ ` let pos = _pos`,
6331
+ ...r.stmts,
6332
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6333
+ `})()`,
6334
+ `if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6335
+ `return _coverageResult`
6336
+ ]
5978
6337
  ].join("\n"));
5979
6338
  const defaultCtx = {
5980
6339
  trackLines: false,
@@ -5986,10 +6345,11 @@ function compile(combinator, mapFnSources, opts) {
5986
6345
  const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
5987
6346
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
5988
6347
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
5989
- const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
6348
+ const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
5990
6349
  return {
5991
6350
  source,
5992
6351
  inlineExpression,
6352
+ ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
5993
6353
  parse(input, pos = 0) {
5994
6354
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
5995
6355
  },
@@ -6052,8 +6412,13 @@ function compileLinkable(ruleMapArg, ns, opts) {
6052
6412
  triviaCaptureNames: /* @__PURE__ */ new Map(),
6053
6413
  triviaFnNames: /* @__PURE__ */ new Map(),
6054
6414
  namedFnDecls: [],
6055
- capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
6415
+ // A recognition-only fragment normally has no node collector of its own.
6416
+ // Leaf composition can re-lower it beneath a local semantic node, though;
6417
+ // in that case its terminals must feed the caller's collector rather than
6418
+ // merely returning their scalar parse values.
6419
+ capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
6056
6420
  recovery: opts?.recovery ?? false,
6421
+ ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
6057
6422
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
6058
6423
  ns,
6059
6424
  deferFirstSetRefs: true,
@@ -6127,7 +6492,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
6127
6492
  ctx.indent = savedIndent;
6128
6493
  ctx.failLabel = savedFail;
6129
6494
  ctx.recordFail = savedRec;
6130
- pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
6495
+ const ruleId = ctx.coverage?.plan.rules.get(rule);
6496
+ const stmts = ruleId === void 0 ? body.stmts : [
6497
+ `${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`,
6498
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: _pos })`,
6499
+ ...body.stmts,
6500
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: _pos, end: ${body.endVar} })`
6501
+ ];
6502
+ pushNamedFnDecl(ctx, fn, stmts, body.valueVar, body.endVar);
6131
6503
  }
6132
6504
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
6133
6505
  }
@@ -6184,15 +6556,46 @@ function compileLinkable(ruleMapArg, ns, opts) {
6184
6556
  deps: ruleDependencies(ruleMap),
6185
6557
  needsEmptyTl: !!ctx.needsEmptyTl,
6186
6558
  needsHostReads: !!ctx.needsHostReads,
6559
+ hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
6560
+ isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
6187
6561
  mfFns: mfSrcs ? [] : ctx.mapFns,
6188
6562
  buildFns: buildSrcs ? [] : ctx.buildFns
6189
6563
  };
6190
6564
  }
6191
- function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
6192
- const bodyLines = [
6565
+ function hasSemanticReduction(roots) {
6566
+ const seen = /* @__PURE__ */ new Set();
6567
+ const visit = (parser2) => {
6568
+ if (seen.has(parser2)) return false;
6569
+ seen.add(parser2);
6570
+ const def = parser2._def;
6571
+ if (def.tag === "transform" && !def.recognitionOnly) return true;
6572
+ if (def.tag === "choice" && def.gates.some(Boolean)) return true;
6573
+ if (def.tag === "guard" || def.tag === "withCtx") return true;
6574
+ if (def.tag === "node" && def.build !== void 0) return true;
6575
+ if (def.tag === "lazy") {
6576
+ try {
6577
+ return visit(def.thunk());
6578
+ } catch {
6579
+ return true;
6580
+ }
6581
+ }
6582
+ return childrenOf(def).some(visit);
6583
+ };
6584
+ return roots.some(visit);
6585
+ }
6586
+ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootRuleId) {
6587
+ const bodyLines = coverageRootRuleId === void 0 ? [
6193
6588
  ` let pos = _pos`,
6194
6589
  ...r.stmts,
6195
6590
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6591
+ ] : [
6592
+ ` const _coverageResult = (() => {`,
6593
+ ` let pos = _pos`,
6594
+ ...r.stmts,
6595
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6596
+ ` })()`,
6597
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6598
+ ` return _coverageResult`
6196
6599
  ];
6197
6600
  const innerFn = [
6198
6601
  `function(input, _pos, _ctx) {`,
@@ -6247,8 +6650,8 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
6247
6650
  };
6248
6651
  const emit2 = (end) => {
6249
6652
  if (end > pos && cstCaptureActive(ctx)) {
6250
- const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
6251
- pushCstLeaf(ctx, leaf);
6653
+ const leaf2 = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
6654
+ pushCstLeaf(ctx, leaf2);
6252
6655
  }
6253
6656
  };
6254
6657
  while (cur < input.length) {
@@ -6302,6 +6705,7 @@ function balanced(open, close, options = {}) {
6302
6705
  );
6303
6706
  if (combi._def.tag === "transform") {
6304
6707
  combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
6708
+ combi._def.recognitionOnly = true;
6305
6709
  }
6306
6710
  self.define(combi);
6307
6711
  return combi;
@@ -6347,9 +6751,11 @@ function childrenOf2(def) {
6347
6751
  case "many":
6348
6752
  case "oneOrMore":
6349
6753
  case "optional":
6754
+ case "attempt":
6350
6755
  case "transform":
6351
6756
  case "trivia":
6352
6757
  case "token":
6758
+ case "leaf":
6353
6759
  case "label":
6354
6760
  case "field":
6355
6761
  case "not":
@@ -6398,7 +6804,7 @@ function serializeRuleMap(ruleMap) {
6398
6804
  }
6399
6805
  }
6400
6806
  function evalRuleMapIR(ir) {
6401
- const _tf = (child, src) => {
6807
+ const _tf = (child, src, recognitionOnly = false) => {
6402
6808
  let fn2;
6403
6809
  try {
6404
6810
  fn2 = (0, eval)(`(${src})`);
@@ -6409,8 +6815,22 @@ function evalRuleMapIR(ir) {
6409
6815
  }
6410
6816
  const t = transform(child, fn2);
6411
6817
  t._def.fnSrc = src;
6818
+ if (recognitionOnly) t._def.recognitionOnly = true;
6412
6819
  return t;
6413
6820
  };
6821
+ const _lf = (child, src) => {
6822
+ let fn2;
6823
+ try {
6824
+ fn2 = (0, eval)(`(${src})`);
6825
+ } catch {
6826
+ fn2 = () => {
6827
+ throw new Error("IR leaf fn not materialized");
6828
+ };
6829
+ }
6830
+ const l = leaf(child, fn2);
6831
+ l._def.fnSrc = src;
6832
+ return l;
6833
+ };
6414
6834
  const _nd = (type, child, src, opts, staticError) => {
6415
6835
  if (staticError !== void 0 && staticError.length > 0) {
6416
6836
  throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
@@ -6469,6 +6889,7 @@ function evalRuleMapIR(ir) {
6469
6889
  "keywords",
6470
6890
  "sequence",
6471
6891
  "choice",
6892
+ "attempt",
6472
6893
  "many",
6473
6894
  "oneOrMore",
6474
6895
  "optional",
@@ -6478,6 +6899,7 @@ function evalRuleMapIR(ir) {
6478
6899
  "parser",
6479
6900
  "scanTo",
6480
6901
  "token",
6902
+ "leaf",
6481
6903
  "transform",
6482
6904
  "skip",
6483
6905
  "trivia",
@@ -6485,6 +6907,7 @@ function evalRuleMapIR(ir) {
6485
6907
  "field",
6486
6908
  "expect",
6487
6909
  "_tf",
6910
+ "_lf",
6488
6911
  "_nd",
6489
6912
  "_gch",
6490
6913
  "_wc",
@@ -6498,6 +6921,7 @@ function evalRuleMapIR(ir) {
6498
6921
  keywords,
6499
6922
  sequence,
6500
6923
  choice,
6924
+ attempt,
6501
6925
  many,
6502
6926
  oneOrMore,
6503
6927
  optional,
@@ -6507,6 +6931,7 @@ function evalRuleMapIR(ir) {
6507
6931
  parser,
6508
6932
  scanTo,
6509
6933
  token,
6934
+ leaf,
6510
6935
  transform,
6511
6936
  skip,
6512
6937
  trivia,
@@ -6514,6 +6939,7 @@ function evalRuleMapIR(ir) {
6514
6939
  field,
6515
6940
  expect,
6516
6941
  _tf,
6942
+ _lf,
6517
6943
  _nd,
6518
6944
  _gch,
6519
6945
  _wc
@@ -6645,6 +7071,8 @@ ${body}
6645
7071
  return `oneOrMore(${kid(def.parser)})`;
6646
7072
  case "optional":
6647
7073
  return `optional(${kid(def.parser)})`;
7074
+ case "attempt":
7075
+ return `attempt(${kid(def.parser)})`;
6648
7076
  case "sepBy":
6649
7077
  return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
6650
7078
  case "not":
@@ -6653,6 +7081,10 @@ ${body}
6653
7081
  return `trivia(${kid(def.parser)})`;
6654
7082
  case "token":
6655
7083
  return `token(${kid(def.parser)})`;
7084
+ case "leaf": {
7085
+ if (def.fnSrc === void 0) throw new Unserializable("leaf without fnSrc");
7086
+ return `_lf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
7087
+ }
6656
7088
  case "label":
6657
7089
  return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
6658
7090
  case "field":
@@ -6665,7 +7097,7 @@ ${body}
6665
7097
  return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
6666
7098
  case "transform": {
6667
7099
  if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
6668
- return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
7100
+ return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
6669
7101
  }
6670
7102
  case "node": {
6671
7103
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
@@ -6717,15 +7149,15 @@ function normalizeCstCollapse(collapse) {
6717
7149
  }
6718
7150
  return typeof collapse === "function" ? collapse : void 0;
6719
7151
  }
6720
- function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
6721
- return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
7152
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
7153
+ return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
6722
7154
  }
6723
- function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
7155
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
6724
7156
  if (typeof typeOrOptions === "string") {
6725
- return buildCstNode(typeOrOptions, children ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
7157
+ return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
6726
7158
  }
6727
7159
  const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
6728
- const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children2, fields, span2, rawChildren2, triviaLog2, state2);
7160
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
6729
7161
  host._parsemanCstOutput = true;
6730
7162
  if (collapse) host._parsemanCstCollapse = collapse;
6731
7163
  return host;
@@ -6784,13 +7216,39 @@ function fuseRules(pieces) {
6784
7216
  return new Function("_env", body)(env);
6785
7217
  }
6786
7218
  var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
7219
+ var LEAF_COMPOSED = /* @__PURE__ */ Symbol.for("parseman.leafComposed");
6787
7220
  var COMPOSED_TRIVIA = /* @__PURE__ */ Symbol.for("parseman.composedTrivia");
7221
+ var COMPOSED_COVERAGE_RULES = /* @__PURE__ */ Symbol.for("parseman.composedCoverageRules");
6788
7222
  function isIRPiece(p) {
6789
7223
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
6790
7224
  }
6791
- function materializePiece(p, trivia2) {
7225
+ function coverageRulesOf(carried) {
7226
+ const winners = {};
7227
+ for (const piece of carried) {
7228
+ if (!isIRPiece(piece)) return void 0;
7229
+ const map = evalRuleMapIR(piece.ir);
7230
+ for (const [name, rule] of map) {
7231
+ if (rule._def.tag === "lazy") {
7232
+ try {
7233
+ rule._def.thunk();
7234
+ } catch {
7235
+ continue;
7236
+ }
7237
+ }
7238
+ winners[name] = rule;
7239
+ }
7240
+ }
7241
+ return winners;
7242
+ }
7243
+ function composedCoverageRules(grammar) {
7244
+ return grammar[COMPOSED_COVERAGE_RULES];
7245
+ }
7246
+ function materializePiece(p, trivia2, captureTerminals = false) {
6792
7247
  if (!isIRPiece(p)) return p;
6793
- const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, trivia2 ? { trivia: trivia2 } : void 0);
7248
+ const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
7249
+ ...trivia2 ? { trivia: trivia2 } : {},
7250
+ ...captureTerminals ? { captureTerminals: true } : {}
7251
+ });
6794
7252
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
6795
7253
  return pieces;
6796
7254
  }
@@ -6841,6 +7299,9 @@ function composingTriviaOf(items) {
6841
7299
  return void 0;
6842
7300
  }
6843
7301
  function compose(items) {
7302
+ if (items.some((item) => item[LEAF_COMPOSED] === true)) {
7303
+ throw new Error("compose: a composeLeaf() result is terminal and cannot be composed again");
7304
+ }
6844
7305
  const used = /* @__PURE__ */ new Set();
6845
7306
  const trivia2 = composingTriviaOf(items);
6846
7307
  const carried = items.flatMap((item) => itemCarried(item, used, trivia2));
@@ -6848,8 +7309,13 @@ function compose(items) {
6848
7309
  const map = fuseRules(pieces);
6849
7310
  Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
6850
7311
  if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
7312
+ const coverageRules = coverageRulesOf(carried);
7313
+ if (coverageRules) Object.defineProperty(map, COMPOSED_COVERAGE_RULES, { value: coverageRules, enumerable: false });
6851
7314
  return map;
6852
7315
  }
7316
+ function composeLeaf(_items) {
7317
+ throw new Error("composeLeaf(): requires Parseman macro lowering; runtime composition is forbidden");
7318
+ }
6853
7319
 
6854
7320
  // src/combinators/guard.ts
6855
7321
  function guard(predicate) {
@@ -7013,7 +7479,9 @@ function producesRepetition(def, depth = 0) {
7013
7479
  // Transparent wrappers — look through to the inner parser.
7014
7480
  case "node":
7015
7481
  case "transform":
7482
+ case "attempt":
7016
7483
  case "token":
7484
+ case "leaf":
7017
7485
  case "label":
7018
7486
  case "field":
7019
7487
  case "expect":
@@ -7065,8 +7533,8 @@ function replaceAtPath(rebuild, root, path, newNode) {
7065
7533
  newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath(rebuild, root.children[idx], rest, newNode);
7066
7534
  return rebuild(root, newChildren);
7067
7535
  }
7068
- function defaultRebuild(node2, children) {
7069
- return { ...node2, children };
7536
+ function defaultRebuild(node2, children2) {
7537
+ return { ...node2, children: children2 };
7070
7538
  }
7071
7539
  function shiftRelStart(child, delta) {
7072
7540
  const c = child;
@@ -7406,7 +7874,8 @@ function runOnce(entry, input, options, phase, profileState) {
7406
7874
  state: options.state,
7407
7875
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
7408
7876
  ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
7409
- ...options.tolerant ? { _tolerant: true, _rec: REC } : {}
7877
+ ...options.tolerant ? { _tolerant: true, _rec: REC } : {},
7878
+ ...options.instrumentation === void 0 ? {} : options.instrumentation
7410
7879
  };
7411
7880
  const r = invoke(entry, input, 0, ctx);
7412
7881
  let unconsumedFrom = null;
@@ -7461,6 +7930,265 @@ function run(entry, input, options = {}) {
7461
7930
  };
7462
7931
  }
7463
7932
 
7933
+ // src/coverage.ts
7934
+ var GRAMMAR_COVERAGE_DEFINITIONS = /* @__PURE__ */ Symbol.for("parseman.grammarCoverageDefinitions");
7935
+ function compiledGrammarCoverageDefinitions(grammar) {
7936
+ const definitions = grammar[GRAMMAR_COVERAGE_DEFINITIONS];
7937
+ 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"))) {
7938
+ throw new TypeError("grammar has no coverage definitions; enable grammarCoverage for this macro build");
7939
+ }
7940
+ return definitions;
7941
+ }
7942
+ function createGrammarInstrumentationContext(options = {}) {
7943
+ return {
7944
+ trackLines: options.trackLines ?? false,
7945
+ ...options.state === void 0 ? {} : { state: options.state },
7946
+ ...options.collector === void 0 ? {} : { _grammarCoverage: (id) => options.collector.hit(id) },
7947
+ ...options.trace === void 0 ? {} : { _grammarTrace: options.trace }
7948
+ };
7949
+ }
7950
+ function createGrammarTraceSink(options) {
7951
+ if (!Number.isInteger(options.capacity) || options.capacity < 0) throw new TypeError("trace capacity must be a finite non-negative integer");
7952
+ const events = [];
7953
+ let detached = false;
7954
+ let truncated = false;
7955
+ let dropped = 0;
7956
+ return {
7957
+ write(event) {
7958
+ if (detached || events.length >= options.capacity) {
7959
+ detached = true;
7960
+ truncated = true;
7961
+ dropped++;
7962
+ return;
7963
+ }
7964
+ events.push(Object.freeze({ ...event }));
7965
+ try {
7966
+ if (options.write?.(event) === false) {
7967
+ detached = true;
7968
+ truncated = true;
7969
+ }
7970
+ } catch {
7971
+ detached = true;
7972
+ truncated = true;
7973
+ }
7974
+ },
7975
+ snapshot() {
7976
+ return Object.freeze({ events: Object.freeze([...events]), truncated, dropped });
7977
+ }
7978
+ };
7979
+ }
7980
+ function createGrammarCoverageCollector(definitions) {
7981
+ const ordered = [...definitions].sort((a, b) => a.id.localeCompare(b.id));
7982
+ const known = new Set(ordered.map((definition) => definition.id));
7983
+ const hits = /* @__PURE__ */ new Set();
7984
+ return {
7985
+ hit(id) {
7986
+ if (known.has(id)) hits.add(id);
7987
+ },
7988
+ snapshot() {
7989
+ const hitList = [...hits].sort();
7990
+ const unhit = ordered.map((definition) => definition.id).filter((id) => !hits.has(id));
7991
+ return { definitions: ordered, hits: hitList, unhit, ratio: ordered.length === 0 ? 1 : hitList.length / ordered.length };
7992
+ },
7993
+ reset() {
7994
+ hits.clear();
7995
+ }
7996
+ };
7997
+ }
7998
+ function grammarCoverageDefinitions(entry, winners) {
7999
+ return buildGrammarPlan(entry, winners).definitions;
8000
+ }
8001
+ function composedGrammarCoverageDefinitions(grammar, startRule) {
8002
+ const winners = composedCoverageRules(grammar);
8003
+ if (!winners) throw new TypeError("semantic coverage needs re-lowerable composed IR; this composition contains an opaque artifact");
8004
+ const entry = winners[startRule];
8005
+ if (!entry) throw new TypeError(`semantic coverage start rule ${JSON.stringify(startRule)} is not a final winner`);
8006
+ return grammarCoverageDefinitions(entry, winners);
8007
+ }
8008
+ function coverageEntry(entry, collector, maps, trace) {
8009
+ const cache = /* @__PURE__ */ new WeakMap();
8010
+ const build = (parser2) => {
8011
+ const existing = cache.get(parser2);
8012
+ if (existing) return existing;
8013
+ const rule = maps.rules.get(parser2);
8014
+ const wrap = (parse2) => {
8015
+ const wrapped = {
8016
+ _tag: parser2._tag,
8017
+ _meta: parser2._meta,
8018
+ _def: parser2._def,
8019
+ parse(input, pos, ctx) {
8020
+ if (rule) {
8021
+ collector.hit(rule);
8022
+ trace?.write({ id: rule, phase: "enter", offset: pos });
8023
+ }
8024
+ const result = parse2(input, pos, ctx);
8025
+ if (rule) trace?.write(result.ok ? { id: rule, phase: "success", offset: pos, end: result.span.end } : { id: rule, phase: "failure", offset: result.span.end });
8026
+ return result;
8027
+ }
8028
+ };
8029
+ cache.set(parser2, wrapped);
8030
+ return wrapped;
8031
+ };
8032
+ const def = parser2._def;
8033
+ if (def.tag === "lazy") {
8034
+ return wrap((input, pos, ctx) => build(def.thunk()).parse(input, pos, ctx));
8035
+ }
8036
+ if (def.tag === "label") {
8037
+ const base2 = label(def.label, build(def.parser));
8038
+ return wrap((input, pos, ctx) => {
8039
+ const result = base2.parse(input, pos, ctx);
8040
+ if (result.ok) for (const id of maps.labels.get(parser2) ?? []) {
8041
+ collector.hit(id);
8042
+ trace?.write({ id, phase: "success", offset: pos, end: result.span.end });
8043
+ }
8044
+ return result;
8045
+ });
8046
+ }
8047
+ if (def.tag === "choice") {
8048
+ const ids = maps.choices.get(parser2) ?? [];
8049
+ const invocations = [];
8050
+ const arms = def.parsers.map((arm, index) => {
8051
+ const child = build(arm);
8052
+ return {
8053
+ _tag: child._tag,
8054
+ _meta: child._meta,
8055
+ _def: child._def,
8056
+ parse(input, pos, ctx) {
8057
+ const id = ids[index];
8058
+ if (id) trace?.write({ id, phase: "attempt", offset: pos });
8059
+ const result = child.parse(input, pos, ctx);
8060
+ if (result.ok) {
8061
+ if (def.strategy.tag !== "greedyClassify") collector.hit(ids[index]);
8062
+ const invocation = invocations[invocations.length - 1];
8063
+ if (invocation) {
8064
+ invocation.lastSuccessfulArm = index;
8065
+ invocation.successfulArms.push(index);
8066
+ }
8067
+ } else if (id) {
8068
+ trace?.write({ id, phase: "failure", offset: result.span.end });
8069
+ trace?.write({ id, phase: "backtrack", offset: pos });
8070
+ }
8071
+ return result;
8072
+ }
8073
+ };
8074
+ });
8075
+ const args = arms.map((arm, index) => def.gates[index] ? { combinator: arm, gate: def.gates[index] } : arm);
8076
+ const unsafeChoice = choice;
8077
+ const baseChoice = unsafeChoice(...args);
8078
+ return wrap((input, pos, ctx) => {
8079
+ const invocation = { lastSuccessfulArm: void 0, successfulArms: [] };
8080
+ invocations.push(invocation);
8081
+ const result = baseChoice.parse(input, pos, ctx);
8082
+ const strategy = def.strategy;
8083
+ if (result.ok && strategy.tag === "greedyClassify") {
8084
+ const text = input.slice(pos, result.span.end);
8085
+ const classified = def.parsers.findIndex((arm, index) => index !== strategy.superIndex && getCoreLiteralValue(arm) === text);
8086
+ if (classified !== -1) invocation.lastSuccessfulArm = classified;
8087
+ else invocation.lastSuccessfulArm = strategy.superIndex;
8088
+ }
8089
+ if (result.ok && invocation.lastSuccessfulArm !== void 0) {
8090
+ const id = ids[invocation.lastSuccessfulArm];
8091
+ for (const rejected of invocation.successfulArms) {
8092
+ if (rejected !== invocation.lastSuccessfulArm) {
8093
+ const rejectedId = ids[rejected];
8094
+ if (rejectedId) {
8095
+ trace?.write({ id: rejectedId, phase: "failure", offset: pos });
8096
+ trace?.write({ id: rejectedId, phase: "backtrack", offset: pos });
8097
+ }
8098
+ }
8099
+ }
8100
+ if (id) {
8101
+ collector.hit(id);
8102
+ trace?.write({ id, phase: "selected", offset: pos, end: result.span.end });
8103
+ trace?.write({ id, phase: "success", offset: pos, end: result.span.end });
8104
+ }
8105
+ }
8106
+ invocations.pop();
8107
+ return result;
8108
+ });
8109
+ }
8110
+ const base = (() => {
8111
+ switch (def.tag) {
8112
+ case "sequence": {
8113
+ const unsafeSequence = sequence;
8114
+ return unsafeSequence(...def.parsers.map(build));
8115
+ }
8116
+ case "many":
8117
+ return many(build(def.parser));
8118
+ case "oneOrMore":
8119
+ return oneOrMore(build(def.parser));
8120
+ case "optional":
8121
+ return optional(build(def.parser));
8122
+ case "attempt": {
8123
+ const base2 = attempt(build(def.parser));
8124
+ const id = maps.attempts.get(parser2);
8125
+ return id === void 0 ? base2 : {
8126
+ ...base2,
8127
+ parse(input, pos, ctx) {
8128
+ const result = base2.parse(input, pos, ctx);
8129
+ if (!result.ok) trace?.write({ id, phase: "rollback", offset: pos });
8130
+ return result;
8131
+ }
8132
+ };
8133
+ }
8134
+ case "sepBy":
8135
+ return sepBy(build(def.parser), build(def.separator));
8136
+ case "transform":
8137
+ return transform(build(def.parser), def.fn);
8138
+ case "skip":
8139
+ return skip(build(def.main), build(def.skipped));
8140
+ case "trivia":
8141
+ return trivia(build(def.parser));
8142
+ case "token":
8143
+ return token(build(def.parser));
8144
+ case "leaf":
8145
+ return leaf(build(def.parser), def.fn);
8146
+ case "field":
8147
+ return field(def.name, build(def.parser));
8148
+ case "grammar":
8149
+ return parser({
8150
+ ...def.triviaParser === void 0 ? def.clearTrivia ? { trivia: null } : {} : { trivia: build(def.triviaParser) },
8151
+ ...def.captureTrivia ? { captureTrivia: true } : {},
8152
+ ...def.trackLines ? { trackLines: true } : {}
8153
+ }, build(def.parser));
8154
+ case "not":
8155
+ return not(build(def.parser));
8156
+ case "node": {
8157
+ const opts = { ...def.unwrap ? { unwrap: true } : {}, ...def.collapse ? { collapse: true } : {}, ...def.captureTrivia ? { captureTrivia: true } : {}, ...def.trailingTrivia ? { trailingTrivia: true } : {} };
8158
+ return def.type === void 0 ? node(build(def.parser), def.build, opts) : node(def.type, build(def.parser), def.build, opts);
8159
+ }
8160
+ case "guard":
8161
+ return guard(def.predicate);
8162
+ case "withCtx":
8163
+ return withCtx(def.extra, build(def.parser));
8164
+ case "expect":
8165
+ return expect(build(def.parser), def.label);
8166
+ case "scanTo":
8167
+ return scanTo(build(def.sentinel), { skip: def.skip.map(build), orEOF: def.orEOF });
8168
+ case "literal":
8169
+ case "regex":
8170
+ case "keywords":
8171
+ return parser2;
8172
+ case "recover":
8173
+ case "unknown":
8174
+ throw new TypeError(`runWithGrammarCoverage does not yet support ${def.tag}`);
8175
+ default:
8176
+ return parser2;
8177
+ }
8178
+ })();
8179
+ return wrap((input, pos, ctx) => base.parse(input, pos, ctx));
8180
+ };
8181
+ return build(entry);
8182
+ }
8183
+ function runWithGrammarCoverage(entry, input, options = {}) {
8184
+ if (typeof entry === "function") throw new TypeError("runWithGrammarCoverage currently requires an interpreter combinator entry");
8185
+ const plan = buildGrammarPlan(entry);
8186
+ const definitions = plan.definitions;
8187
+ const collector = options.collector ?? createGrammarCoverageCollector(definitions);
8188
+ const { collector: _collector, trace, ...runOptions } = options;
8189
+ return { result: run(coverageEntry(entry, collector, plan, trace), input, runOptions), coverage: collector.snapshot() };
8190
+ }
8191
+
7464
8192
  // src/cst/trivia-index.ts
7465
8193
  function merge(map, key, run2) {
7466
8194
  if (run2.length === 0) return;
@@ -7531,9 +8259,9 @@ function buildTriviaIndex(root, input = "", opts) {
7531
8259
  function walk(root, visitor, ctx = void 0) {
7532
8260
  const go = (node2, parent) => {
7533
8261
  const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
7534
- const children = node2.children;
7535
- if (descend !== false && Array.isArray(children)) {
7536
- for (const child of children) go(child, node2);
8262
+ const children2 = node2.children;
8263
+ if (descend !== false && Array.isArray(children2)) {
8264
+ for (const child of children2) go(child, node2);
7537
8265
  }
7538
8266
  visitor.leave?.(node2, parent, ctx);
7539
8267
  };
@@ -7549,9 +8277,9 @@ function createVisitor(handlers) {
7549
8277
  },
7550
8278
  visitChildren(node2) {
7551
8279
  const out = [];
7552
- const children = node2.children;
7553
- if (Array.isArray(children)) {
7554
- for (const child of children) {
8280
+ const children2 = node2.children;
8281
+ if (Array.isArray(children2)) {
8282
+ for (const child of children2) {
7555
8283
  const r = api.visit(child);
7556
8284
  if (r !== void 0) out.push(r);
7557
8285
  }
@@ -7792,6 +8520,7 @@ function buildOffsetIndex(root, input = "") {
7792
8520
  // src/index.ts
7793
8521
  registerRegexAnalyzer(firstSetFromRegex);
7794
8522
  export {
8523
+ GRAMMAR_COVERAGE_DEFINITIONS,
7795
8524
  OffsetIndex,
7796
8525
  absoluteSpanAt,
7797
8526
  absoluteSpanCST,
@@ -7799,6 +8528,7 @@ export {
7799
8528
  absolutizeCST,
7800
8529
  annotateSpan,
7801
8530
  applyEdit,
8531
+ attempt,
7802
8532
  balanced,
7803
8533
  blankLinesIn,
7804
8534
  buildLineIndex,
@@ -7808,20 +8538,28 @@ export {
7808
8538
  collectLeafSlots,
7809
8539
  commentsIn,
7810
8540
  compile,
8541
+ compiledGrammarCoverageDefinitions,
7811
8542
  completionsAt,
7812
8543
  compose,
8544
+ composeLeaf,
8545
+ composedGrammarCoverageDefinitions,
8546
+ createGrammarCoverageCollector,
8547
+ createGrammarInstrumentationContext,
8548
+ createGrammarTraceSink,
7813
8549
  createVisitor,
7814
8550
  cstBuildHost,
7815
8551
  expect,
7816
8552
  field,
7817
8553
  gapIsSignificant,
7818
8554
  gapText,
8555
+ grammarCoverageDefinitions,
7819
8556
  guard,
7820
8557
  indentMixed,
7821
8558
  indentWidth,
7822
8559
  isParseError,
7823
8560
  keywords,
7824
8561
  label,
8562
+ leaf,
7825
8563
  lineBreaksIn,
7826
8564
  lineStartWithin,
7827
8565
  literal,
@@ -7842,6 +8580,7 @@ export {
7842
8580
  relativizeCST,
7843
8581
  rules,
7844
8582
  run,
8583
+ runWithGrammarCoverage,
7845
8584
  scanTo,
7846
8585
  sepBy,
7847
8586
  sequence,