parseman 0.27.0 → 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 (58) 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/grammar.d.ts.map +1 -1
  7. package/dist/combinators/node.d.ts +8 -0
  8. package/dist/combinators/node.d.ts.map +1 -1
  9. package/dist/combinators/scanTo.d.ts.map +1 -1
  10. package/dist/combinators/token.d.ts +12 -0
  11. package/dist/combinators/token.d.ts.map +1 -1
  12. package/dist/compiler/build-arity.d.ts +8 -4
  13. package/dist/compiler/build-arity.d.ts.map +1 -1
  14. package/dist/compiler/codegen.d.ts +12 -0
  15. package/dist/compiler/codegen.d.ts.map +1 -1
  16. package/dist/compiler/fields.d.ts +7 -0
  17. package/dist/compiler/fields.d.ts.map +1 -1
  18. package/dist/compiler/grammar-coverage-ids.d.ts +19 -0
  19. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -0
  20. package/dist/compiler/inline-callback.d.ts.map +1 -1
  21. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  22. package/dist/compiler/linker.d.ts +12 -1
  23. package/dist/compiler/linker.d.ts.map +1 -1
  24. package/dist/compiler/value-usage.d.ts.map +1 -1
  25. package/dist/coverage.d.ts +68 -0
  26. package/dist/coverage.d.ts.map +1 -0
  27. package/dist/cst/trivia-entries.d.ts +2 -0
  28. package/dist/cst/trivia-entries.d.ts.map +1 -1
  29. package/dist/functional/doc.d.ts.map +1 -1
  30. package/dist/functional/run.d.ts +13 -0
  31. package/dist/functional/run.d.ts.map +1 -1
  32. package/dist/index.cjs +936 -103
  33. package/dist/index.cjs.map +4 -4
  34. package/dist/index.d.ts +5 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +936 -103
  37. package/dist/index.js.map +4 -4
  38. package/dist/language-service/index.cjs +7 -2
  39. package/dist/language-service/index.cjs.map +2 -2
  40. package/dist/language-service/index.js +7 -2
  41. package/dist/language-service/index.js.map +2 -2
  42. package/dist/plugin/direct-builder-static.d.ts +8 -0
  43. package/dist/plugin/direct-builder-static.d.ts.map +1 -0
  44. package/dist/plugin/evaluator.d.ts.map +1 -1
  45. package/dist/plugin/index.cjs +1084 -145
  46. package/dist/plugin/index.cjs.map +4 -4
  47. package/dist/plugin/index.d.ts +4 -1
  48. package/dist/plugin/index.d.ts.map +1 -1
  49. package/dist/plugin/index.js +1084 -145
  50. package/dist/plugin/index.js.map +4 -4
  51. package/dist/spec/index.cjs +3 -0
  52. package/dist/spec/index.cjs.map +2 -2
  53. package/dist/spec/index.js +3 -0
  54. package/dist/spec/index.js.map +2 -2
  55. package/dist/spec/model.d.ts.map +1 -1
  56. package/dist/types.d.ts +16 -0
  57. package/dist/types.d.ts.map +1 -1
  58. package/package.json +2 -1
@@ -1,7 +1,8 @@
1
1
  // src/plugin/index.ts
2
2
  import * as fs from "node:fs";
3
+ import * as path from "node:path";
3
4
  import { createUnplugin } from "unplugin";
4
- import { parseSync } from "oxc-parser";
5
+ import { parseSync as parseSync2 } from "oxc-parser";
5
6
  import { ResolverFactory } from "oxc-resolver";
6
7
  import MagicString from "magic-string";
7
8
 
@@ -66,6 +67,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
66
67
  case "label":
67
68
  case "trivia":
68
69
  case "token":
70
+ case "leaf":
69
71
  case "expect":
70
72
  case "withCtx":
71
73
  case "node":
@@ -129,6 +131,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
129
131
  case "label":
130
132
  case "trivia":
131
133
  case "token":
134
+ case "leaf":
132
135
  case "node":
133
136
  case "grammar":
134
137
  case "expect":
@@ -234,10 +237,10 @@ function beginCstNodeCapture(ctx) {
234
237
  }
235
238
  function finishCstBuf(buf) {
236
239
  if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL };
237
- const children = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
240
+ const children2 = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
238
241
  const rawChildren = buf.raw ?? (buf.rawSingle !== void 0 ? [buf.rawSingle] : EMPTY);
239
242
  const triviaLog = buf.tl ?? EMPTY_TL;
240
- return { children, rawChildren, triviaLog };
243
+ return { children: children2, rawChildren, triviaLog };
241
244
  }
242
245
  function endCstNodeCapture(ctx, saved) {
243
246
  const materialized = finishCstBuf(ctx._cstBuf);
@@ -249,8 +252,8 @@ function endCstNodeCapture(ctx, saved) {
249
252
  ctx.captureTrivia = saved.cap;
250
253
  return materialized;
251
254
  }
252
- function pushCstLeaf(ctx, leaf) {
253
- pushCstChild(ctx, leaf, leaf);
255
+ function pushCstLeaf(ctx, leaf2) {
256
+ pushCstChild(ctx, leaf2, leaf2);
254
257
  }
255
258
  function pushCstChild(ctx, built, rawEntry) {
256
259
  const b = ctx._cstBuf;
@@ -980,10 +983,13 @@ function deriveExpected(c) {
980
983
  return def.parsers.flatMap(deriveExpected);
981
984
  case "sequence":
982
985
  return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
986
+ case "attempt":
987
+ return deriveExpected(def.parser);
983
988
  case "node":
984
989
  case "grammar":
985
990
  case "trivia":
986
991
  case "token":
992
+ case "leaf":
987
993
  case "optional":
988
994
  case "many":
989
995
  case "oneOrMore":
@@ -1474,6 +1480,12 @@ function scanTrivia(input, cur, ctx) {
1474
1480
  const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1475
1481
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1476
1482
  }
1483
+ function consumeTrivia(input, cur, ctx) {
1484
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1485
+ const scan = scanTrivia(input, cur, ctx);
1486
+ scan.commit();
1487
+ return scan.end;
1488
+ }
1477
1489
  function fastTriviaScanner(trivia2) {
1478
1490
  const cached = fastTriviaCache.get(trivia2);
1479
1491
  if (cached !== void 0) return cached;
@@ -1752,6 +1764,24 @@ function sequence(...parsers) {
1752
1764
  };
1753
1765
  }
1754
1766
 
1767
+ // src/combinators/attempt.ts
1768
+ function attempt(parser2) {
1769
+ return {
1770
+ _tag: "attempt",
1771
+ _meta: parser2._meta,
1772
+ _def: { tag: "attempt", parser: parser2 },
1773
+ parse(input, pos, ctx) {
1774
+ const mark = saveCstMark(ctx);
1775
+ const log = ctx._triviaLog?.length;
1776
+ const result = parser2.parse(input, pos, ctx);
1777
+ if (result.ok) return result;
1778
+ rollbackCstCapture(ctx, mark);
1779
+ if (log !== void 0 && ctx._triviaLog) ctx._triviaLog.length = log;
1780
+ return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
1781
+ }
1782
+ };
1783
+ }
1784
+
1755
1785
  // src/combinators/repeat.ts
1756
1786
  function repItem(combinator, input, cur, ctx) {
1757
1787
  const mark = saveTriviaMark(ctx);
@@ -1927,19 +1957,19 @@ function sepBy(combinator, separator) {
1927
1957
  sepPos = advanceTrivia(input, cur, ctx);
1928
1958
  }
1929
1959
  }
1930
- const sep = separator.parse(input, sepPos, ctx);
1931
- if (!sep.ok) {
1960
+ const sep2 = separator.parse(input, sepPos, ctx);
1961
+ if (!sep2.ok) {
1932
1962
  rollbackTrivia(ctx, loopMark);
1933
1963
  break;
1934
1964
  }
1935
- let nextPos = sep.span.end;
1965
+ let nextPos = sep2.span.end;
1936
1966
  if (ctx.trivia) {
1937
1967
  if (needsDeferredTriviaCommit(ctx)) {
1938
- const scan = scanTrivia(input, sep.span.end, ctx);
1968
+ const scan = scanTrivia(input, sep2.span.end, ctx);
1939
1969
  scan.commit();
1940
1970
  nextPos = scan.end;
1941
1971
  } else {
1942
- nextPos = advanceTrivia(input, sep.span.end, ctx);
1972
+ nextPos = advanceTrivia(input, sep2.span.end, ctx);
1943
1973
  }
1944
1974
  }
1945
1975
  const next = combinator.parse(input, nextPos, ctx);
@@ -2024,10 +2054,14 @@ function markUnusedValues(root) {
2024
2054
  case "label":
2025
2055
  case "trivia":
2026
2056
  case "token":
2057
+ case "attempt":
2027
2058
  case "withCtx":
2028
2059
  case "expect":
2029
2060
  visit(def.parser, consumed);
2030
2061
  return;
2062
+ case "leaf":
2063
+ visit(def.parser, true);
2064
+ return;
2031
2065
  case "lazy":
2032
2066
  return;
2033
2067
  default:
@@ -2150,6 +2184,20 @@ function confirmedBuildArity(src) {
2150
2184
  if (/\barguments\b/.test(s)) return null;
2151
2185
  return parts.length;
2152
2186
  }
2187
+ function buildReadsChildren(def) {
2188
+ if (!def.build) return true;
2189
+ const src = def.buildSrc ?? def.build.toString();
2190
+ const arity = confirmedBuildArity(src);
2191
+ if (arity === null) return true;
2192
+ return arity >= 1;
2193
+ }
2194
+ function buildReadsRaw(def) {
2195
+ if (!def.build) return true;
2196
+ const src = def.buildSrc ?? def.build.toString();
2197
+ const arity = confirmedBuildArity(src);
2198
+ if (arity === null) return true;
2199
+ return arity >= 4;
2200
+ }
2153
2201
  function buildReadsTrivia(def) {
2154
2202
  if (!def.build) return true;
2155
2203
  const src = def.buildSrc ?? def.build.toString();
@@ -2205,9 +2253,11 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
2205
2253
  case "many":
2206
2254
  case "oneOrMore":
2207
2255
  case "optional":
2256
+ case "attempt":
2208
2257
  case "transform":
2209
2258
  case "trivia":
2210
2259
  case "token":
2260
+ case "leaf":
2211
2261
  case "label":
2212
2262
  case "expect":
2213
2263
  case "withCtx":
@@ -2240,9 +2290,11 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
2240
2290
  case "keywords":
2241
2291
  case "guard":
2242
2292
  case "token":
2293
+ case "leaf":
2243
2294
  return false;
2244
2295
  // Transparent single-child wrappers: recurse.
2245
2296
  case "optional":
2297
+ case "attempt":
2246
2298
  case "transform":
2247
2299
  case "trivia":
2248
2300
  case "label":
@@ -2266,6 +2318,54 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
2266
2318
  return true;
2267
2319
  }
2268
2320
  }
2321
+ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
2322
+ if (seen.has(p)) return false;
2323
+ seen.add(p);
2324
+ const d = p._def;
2325
+ switch (d.tag) {
2326
+ case "grammar":
2327
+ return d.captureTrivia === true || parserEnablesTriviaCapture(d.parser, seen);
2328
+ case "node":
2329
+ return false;
2330
+ case "sequence":
2331
+ case "choice":
2332
+ return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
2333
+ case "sepBy":
2334
+ return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
2335
+ case "skip":
2336
+ return parserEnablesTriviaCapture(d.main, seen) || parserEnablesTriviaCapture(d.skipped, seen);
2337
+ case "scanTo":
2338
+ return parserEnablesTriviaCapture(d.sentinel, seen) || d.skip.some((x) => parserEnablesTriviaCapture(x, seen));
2339
+ case "recover":
2340
+ return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.sentinel, seen);
2341
+ case "many":
2342
+ case "oneOrMore":
2343
+ case "optional":
2344
+ case "attempt":
2345
+ case "transform":
2346
+ case "trivia":
2347
+ case "token":
2348
+ case "label":
2349
+ case "field":
2350
+ case "expect":
2351
+ case "withCtx":
2352
+ case "not":
2353
+ return parserEnablesTriviaCapture(d.parser, seen);
2354
+ // leaf() clears the inner CST/trivia collector and publishes only its own
2355
+ // terminal result. An inner parser() capture cannot reach this node.
2356
+ case "leaf":
2357
+ return false;
2358
+ case "lazy": {
2359
+ try {
2360
+ return parserEnablesTriviaCapture(d.thunk(), seen);
2361
+ } catch {
2362
+ return false;
2363
+ }
2364
+ }
2365
+ default:
2366
+ return false;
2367
+ }
2368
+ }
2269
2369
  function buildFieldMap(captures) {
2270
2370
  if (!captures?.length) return void 0;
2271
2371
  const out = {};
@@ -2303,11 +2403,13 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2303
2403
  };
2304
2404
  const unwrap = opts?.unwrap === true;
2305
2405
  const collapse = opts?.collapse === true;
2406
+ const captureTrivia = opts?.captureTrivia === true;
2407
+ const trailingTrivia = opts?.trailingTrivia === true;
2306
2408
  if (unwrap && collapse) {
2307
2409
  throw new Error("node() options cannot set both unwrap and collapse");
2308
2410
  }
2309
- const def = unwrap || collapse ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {} } : baseDef;
2310
- const capturesTrivia = build ? buildReadsTrivia(def) : true;
2411
+ const def = unwrap || collapse || captureTrivia || trailingTrivia ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {} } : baseDef;
2412
+ const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : true);
2311
2413
  const clonesState = build ? buildReadsState(def) : true;
2312
2414
  const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
2313
2415
  return {
@@ -2323,16 +2425,21 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2323
2425
  ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
2324
2426
  }
2325
2427
  if (!capturesTrivia) ctx.captureTrivia = false;
2326
- const r = combinator.parse(input, pos, ctx);
2428
+ let r = combinator.parse(input, pos, ctx);
2429
+ if (r.ok && trailingTrivia && ctx.trivia) {
2430
+ const end = consumeTrivia(input, r.span.end, ctx);
2431
+ r = { ...r, span: { start: r.span.start, end } };
2432
+ }
2327
2433
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
2328
2434
  ctx._fields = savedFields;
2329
2435
  ctx._triviaCaptureMask = savedMask;
2330
- const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
2436
+ const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
2331
2437
  if (!r.ok) return r;
2332
2438
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2333
2439
  const nodeType = def.type ?? missingInferredType();
2334
- 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 ? 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 };
2335
- const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
2440
+ const cstOutput = ctx.build?._parsemanCstOutput === true;
2441
+ 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 };
2442
+ 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 };
2336
2443
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2337
2444
  pushCstChild(ctx, built, rawEntry);
2338
2445
  }
@@ -2449,6 +2556,7 @@ function parser(opts, root) {
2449
2556
  parser: root,
2450
2557
  triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2451
2558
  clearTrivia,
2559
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2452
2560
  trackLines: opts.trackLines ?? false
2453
2561
  },
2454
2562
  parse(input, pos, _ctx) {
@@ -2536,6 +2644,141 @@ function token(root) {
2536
2644
  }
2537
2645
  };
2538
2646
  }
2647
+ function leaf(root, fn) {
2648
+ const meta = {
2649
+ firstSet: root._meta.firstSet,
2650
+ canMatchNewline: root._meta.canMatchNewline,
2651
+ isTrivia: false
2652
+ };
2653
+ return {
2654
+ _tag: "leaf",
2655
+ _meta: meta,
2656
+ _def: { tag: "leaf", parser: root, fn },
2657
+ parse(input, pos, ctx) {
2658
+ const savedBuf = ctx._cstBuf;
2659
+ const savedChildren = ctx._cstChildren;
2660
+ const savedLeaves = ctx._cstLeaves;
2661
+ const savedRaw = ctx._cstRawChildren;
2662
+ const savedTriviaLog = ctx._cstTriviaLog;
2663
+ const savedOuterTriviaLog = ctx._triviaLog;
2664
+ const wasCapturing = cstCaptureActive(ctx);
2665
+ ctx._cstBuf = void 0;
2666
+ ctx._cstChildren = void 0;
2667
+ ctx._cstLeaves = void 0;
2668
+ ctx._cstRawChildren = void 0;
2669
+ ctx._cstTriviaLog = void 0;
2670
+ delete ctx._triviaLog;
2671
+ let result;
2672
+ try {
2673
+ result = root.parse(input, pos, ctx);
2674
+ } finally {
2675
+ ctx._cstBuf = savedBuf;
2676
+ ctx._cstChildren = savedChildren;
2677
+ ctx._cstLeaves = savedLeaves;
2678
+ ctx._cstRawChildren = savedRaw;
2679
+ ctx._cstTriviaLog = savedTriviaLog;
2680
+ if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
2681
+ else ctx._triviaLog = savedOuterTriviaLog;
2682
+ }
2683
+ if (!result.ok) return result;
2684
+ const value = fn(result.value, result.span);
2685
+ if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span: result.span });
2686
+ return { ok: true, value, span: result.span };
2687
+ }
2688
+ };
2689
+ }
2690
+
2691
+ // src/compiler/grammar-coverage-ids.ts
2692
+ function children(def, winners) {
2693
+ switch (def.tag) {
2694
+ case "sequence":
2695
+ case "choice":
2696
+ return def.parsers;
2697
+ case "many":
2698
+ case "oneOrMore":
2699
+ case "optional":
2700
+ case "attempt":
2701
+ case "transform":
2702
+ case "trivia":
2703
+ case "token":
2704
+ case "leaf":
2705
+ case "label":
2706
+ case "field":
2707
+ case "grammar":
2708
+ case "not":
2709
+ case "node":
2710
+ case "guard":
2711
+ case "withCtx":
2712
+ case "recover":
2713
+ case "expect":
2714
+ return "parser" in def ? [def.parser] : [];
2715
+ case "sepBy":
2716
+ return [def.parser, def.separator];
2717
+ case "skip":
2718
+ return [def.main, def.skipped];
2719
+ case "scanTo":
2720
+ return [def.sentinel, ...def.skip];
2721
+ case "lazy": {
2722
+ let resolved;
2723
+ try {
2724
+ resolved = def.thunk();
2725
+ } catch {
2726
+ return [];
2727
+ }
2728
+ const name = resolved._ruleName;
2729
+ return name && winners?.[name] ? [winners[name]] : [resolved];
2730
+ }
2731
+ default:
2732
+ return [];
2733
+ }
2734
+ }
2735
+ function buildGrammarPlan(entry, winners) {
2736
+ const definitions = /* @__PURE__ */ new Map();
2737
+ const choices = /* @__PURE__ */ new WeakMap();
2738
+ const attempts = /* @__PURE__ */ new WeakMap();
2739
+ const labels = /* @__PURE__ */ new WeakMap();
2740
+ const rules2 = /* @__PURE__ */ new WeakMap();
2741
+ const winnerNames = /* @__PURE__ */ new Map();
2742
+ if (winners) {
2743
+ for (const [name, winner] of Object.entries(winners)) {
2744
+ winnerNames.set(winner, name);
2745
+ if (winner._def.tag === "lazy") {
2746
+ try {
2747
+ winnerNames.set(winner._def.thunk(), name);
2748
+ } catch {
2749
+ }
2750
+ }
2751
+ }
2752
+ }
2753
+ const seen = /* @__PURE__ */ new Set();
2754
+ const visit = (parser2, path2) => {
2755
+ if (seen.has(parser2)) return;
2756
+ seen.add(parser2);
2757
+ const rule = winnerNames.get(parser2) ?? parser2._ruleName;
2758
+ if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
2759
+ const id = `rule:${rule}`;
2760
+ definitions.set(id, { id, kind: "rule" });
2761
+ rules2.set(parser2, id);
2762
+ }
2763
+ if (parser2._def.tag === "choice") {
2764
+ const ids = parser2._def.parsers.map((_, index) => `choice:${path2}/arm:${index}`);
2765
+ choices.set(parser2, ids);
2766
+ ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
2767
+ }
2768
+ if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path2}`);
2769
+ if (parser2._def.tag === "label") {
2770
+ const id = `label:${path2}`;
2771
+ labels.set(parser2, [id]);
2772
+ definitions.set(id, { id, kind: "label" });
2773
+ }
2774
+ children(parser2._def, winners).forEach((child, index) => visit(child, `${path2}/${parser2._def.tag}:${index}`));
2775
+ };
2776
+ const roots = Array.isArray(entry) ? entry : [entry];
2777
+ for (const root of roots) {
2778
+ visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
2779
+ }
2780
+ return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
2781
+ }
2539
2782
 
2540
2783
  // src/compiler/scannable-run.ts
2541
2784
  var BACKSLASH = 92;
@@ -3585,7 +3828,7 @@ function scanBranchLabeled(shape, kindIndex, mint) {
3585
3828
  ` if (${m.end} > _e) {`,
3586
3829
  ` if (_cap) {`,
3587
3830
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
3588
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3831
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3589
3832
  ` }`,
3590
3833
  ` _e = ${m.end}`,
3591
3834
  ` continue`,
@@ -3641,7 +3884,7 @@ function analyzeTriviaFastPath(trivia2) {
3641
3884
  var CAP_RECORD = [
3642
3885
  ` if (_cap && _e > _pos) {`,
3643
3886
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
3644
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
3887
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
3645
3888
  ` }`
3646
3889
  ].join("\n");
3647
3890
  function composeFastLoop(shapes) {
@@ -3701,7 +3944,7 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
3701
3944
  ` const _ce = _e + _m${i}[0].length`,
3702
3945
  ` if (_cap) {`,
3703
3946
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3704
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3947
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3705
3948
  ` }`,
3706
3949
  ` _e = _ce`,
3707
3950
  ` _matched = true`,
@@ -3739,7 +3982,7 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
3739
3982
  ` const _ce = _r${i}.span.end`,
3740
3983
  ` if (_cap) {`,
3741
3984
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3742
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3985
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3743
3986
  ` }`,
3744
3987
  ` _e = _ce`,
3745
3988
  ` _matched = true`,
@@ -3928,21 +4171,23 @@ function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
3928
4171
  }
3929
4172
  function failBody(ctx, expected, posExpr) {
3930
4173
  const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
4174
+ const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
3931
4175
  if (ctx.failLabel) {
3932
- if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3933
- return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
4176
+ if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4177
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
3934
4178
  }
3935
- return probe + failReturn(expected, posExpr);
4179
+ return probe + trace + failReturn(expected, posExpr);
3936
4180
  }
3937
4181
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
3938
4182
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
4183
+ const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
3939
4184
  if (ctx.failLabel) {
3940
- if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4185
+ if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3941
4186
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
3942
- return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
4187
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
3943
4188
  }
3944
- if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
3945
- return probe + failReturnArr(expectedArr, posExpr);
4189
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
4190
+ return probe + trace + failReturnArr(expectedArr, posExpr);
3946
4191
  }
3947
4192
  function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
3948
4193
  return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
@@ -3977,7 +4222,7 @@ var NAMED_FN_END = "_pfEnd";
3977
4222
  function namedFnPrelude() {
3978
4223
  return [`const ${NAMED_FN_FAIL} = {}`, `let ${NAMED_FN_END}`];
3979
4224
  }
3980
- function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
4225
+ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar, failureRuleId) {
3981
4226
  ctx.namedFnDecls.push([
3982
4227
  `function ${fnName}(input, _pos, _ctx) {`,
3983
4228
  ` _pfail: {`,
@@ -3985,6 +4230,7 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
3985
4230
  ` ${NAMED_FN_END} = ${endVar}`,
3986
4231
  ` return ${valueVar}`,
3987
4232
  ` }`,
4233
+ ...failureRuleId === void 0 ? [] : [` _ctx._grammarTrace?.write({ id: ${JSON.stringify(failureRuleId)}, phase: 'failure', offset: _ctx._fe ?? _pos })`],
3988
4234
  ` return ${NAMED_FN_FAIL}`,
3989
4235
  `}`
3990
4236
  ].join("\n"));
@@ -4090,7 +4336,7 @@ function ensureTriviaFn(ctx) {
4090
4336
  ` }`,
4091
4337
  ` if (_cap && _e > _pos) {`,
4092
4338
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
4093
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
4339
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
4094
4340
  ` }`,
4095
4341
  ` return _e`,
4096
4342
  `}`
@@ -4146,6 +4392,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
4146
4392
  case "not":
4147
4393
  case "trivia":
4148
4394
  case "token":
4395
+ case "leaf":
4149
4396
  case "scanTo":
4150
4397
  case "unknown":
4151
4398
  return false;
@@ -4157,6 +4404,8 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
4157
4404
  // nothing committed remains.
4158
4405
  case "choice":
4159
4406
  return false;
4407
+ case "attempt":
4408
+ return false;
4160
4409
  // optional never fails; many/oneOrMore only "fail" with zero captured items.
4161
4410
  case "optional":
4162
4411
  case "many":
@@ -4209,6 +4458,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
4209
4458
  case "keywords":
4210
4459
  case "node":
4211
4460
  case "token":
4461
+ case "leaf":
4212
4462
  return true;
4213
4463
  case "not":
4214
4464
  case "guard":
@@ -4223,6 +4473,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
4223
4473
  case "many":
4224
4474
  case "oneOrMore":
4225
4475
  case "optional":
4476
+ case "attempt":
4226
4477
  case "transform":
4227
4478
  case "label":
4228
4479
  case "field":
@@ -4564,35 +4815,55 @@ function needsFirstSetGuard(p) {
4564
4815
  const fs2 = p._meta.firstSet;
4565
4816
  return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
4566
4817
  }
4567
- function emitDisjointArm(p, ctx, pos, valV, endV) {
4568
- if (failsAtStart(p)) {
4818
+ function coverageHit(ctx, id, offset = "_pos", end) {
4819
+ return id === void 0 ? [] : [
4820
+ `${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`,
4821
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'selected', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`,
4822
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`
4823
+ ];
4824
+ }
4825
+ function coverageAttempt(ctx, id, offset) {
4826
+ return id === void 0 ? [] : [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'attempt', offset: ${offset} })`];
4827
+ }
4828
+ function coverageFailureBacktrack(ctx, id, failureOffset, startOffset) {
4829
+ return id === void 0 ? [] : [
4830
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'failure', offset: ${failureOffset} })`,
4831
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'backtrack', offset: ${startOffset} })`
4832
+ ];
4833
+ }
4834
+ function emitDisjointArm(p, ctx, pos, valV, endV, coverageId) {
4835
+ if (failsAtStart(p) && coverageId === void 0) {
4569
4836
  const r = emit(p, ctx, pos);
4570
- return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
4837
+ return [...coverageAttempt(ctx, coverageId, pos), ...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`, ...coverageHit(ctx, coverageId, pos, r.endVar)];
4571
4838
  }
4572
4839
  const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
4573
4840
  return [
4841
+ ...coverageAttempt(ctx, coverageId, pos),
4574
4842
  ...stmts,
4575
- ...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
4843
+ ...emitIfFail(ctx, `!${okVar}`, `${coverageFailureBacktrack(ctx, coverageId, "_ctx._fe", pos).join("; ")}; ${failArrBody(ctx, "_ctx._fx", pos, false)}`),
4576
4844
  `${ind(ctx)}${valV} = ${valVar}`,
4577
- `${ind(ctx)}${endV} = ${endVar}`
4845
+ `${ind(ctx)}${endV} = ${endVar}`,
4846
+ ...coverageHit(ctx, coverageId, pos, endVar)
4578
4847
  ];
4579
4848
  }
4580
- function emitDisjointArmGated(def, i, ctx, pos, valV, endV) {
4849
+ function emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageId) {
4581
4850
  const p = def.parsers[i];
4582
4851
  const gate = def.gates[i];
4583
- if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV);
4852
+ if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV, coverageId);
4584
4853
  const gateIdx = ctx.mapFns.length;
4585
4854
  ctx.mapFns.push(gate);
4586
4855
  ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4587
4856
  const gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4588
4857
  const stmts = [`${ind(ctx)}if (${gateCond}) {`];
4589
4858
  ctx.indent++;
4590
- stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
4859
+ stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV, coverageId));
4591
4860
  ctx.indent--;
4592
4861
  stmts.push(`${ind(ctx)}} else { ${failArrBody(ctx, deriveExpectedArr([p]), pos)} }`);
4593
4862
  return stmts;
4594
4863
  }
4595
- function emitChoice(def, ctx, pos) {
4864
+ function emitChoice(parser2, def, ctx, pos) {
4865
+ const coverageIds = ctx.coverage?.plan.choices.get(parser2);
4866
+ const coverageBase = coverageIds?.[0]?.slice(0, -1);
4596
4867
  const allExpected = deriveExpectedArr(def.parsers);
4597
4868
  if (def.disjoint) {
4598
4869
  const codeV = v(ctx, "_code");
@@ -4611,7 +4882,7 @@ function emitChoice(def, ctx, pos) {
4611
4882
  stmts.push(`${ind(ctx)}{`);
4612
4883
  ctx.indent++;
4613
4884
  stmts.push(
4614
- ...emitDisjointArmGated(def, i, ctx, pos, valV, endV),
4885
+ ...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]),
4615
4886
  `${ind(ctx)}break`
4616
4887
  );
4617
4888
  ctx.indent--;
@@ -4630,16 +4901,16 @@ function emitChoice(def, ctx, pos) {
4630
4901
  first = false;
4631
4902
  stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
4632
4903
  ctx.indent++;
4633
- stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV));
4904
+ stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]));
4634
4905
  ctx.indent--;
4635
4906
  stmts.push(`${ind(ctx)}}`);
4636
4907
  }
4637
4908
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
4638
4909
  return { stmts, valueVar: valV, endVar: endV };
4639
4910
  }
4640
- return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
4911
+ return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos, coverageBase);
4641
4912
  }
4642
- function emitGreedyClassify(def, superIndex, ctx, pos) {
4913
+ function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
4643
4914
  const superParser = def.parsers[superIndex];
4644
4915
  const regexDef = getCoreRegexDef(superParser);
4645
4916
  const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
@@ -4655,10 +4926,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4655
4926
  const endV = v(ctx, "_ge");
4656
4927
  const valV = v(ctx, "_gcv");
4657
4928
  const regexExpected = JSON.stringify(deriveExpected(superParser));
4929
+ const superCoverageId = coverageBase === void 0 ? void 0 : `${coverageBase}${superIndex}`;
4658
4930
  const stmts = [
4931
+ ...coverageAttempt(ctx, superCoverageId, pos),
4659
4932
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
4660
4933
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
4661
- ...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
4934
+ ...emitIfFail(ctx, `${matchV} === null`, `${coverageFailureBacktrack(ctx, superCoverageId, pos, pos).join("; ")}; ${failArrBody(ctx, regexExpected, pos)}`),
4662
4935
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
4663
4936
  `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
4664
4937
  `${ind(ctx)}let ${valV}`
@@ -4674,8 +4947,9 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4674
4947
  stmts.push(`${ind(ctx)}${kw} (${wordV} === ${JSON.stringify(litVal)}) {`);
4675
4948
  ctx.indent++;
4676
4949
  const tR = emitTransformChain(p, JSON.stringify(litVal), endV, pos, ctx);
4950
+ stmts.push(...coverageFailureBacktrack(ctx, superCoverageId, pos, pos));
4677
4951
  stmts.push(...emitLeafCapture(ctx, JSON.stringify(litVal), pos, endV));
4678
- stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`);
4952
+ stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`, ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endV));
4679
4953
  ctx.indent--;
4680
4954
  stmts.push(`${ind(ctx)}}`);
4681
4955
  }
@@ -4683,12 +4957,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4683
4957
  const regexKw = first ? "if" : "else";
4684
4958
  stmts.push(`${ind(ctx)}${regexKw} {`);
4685
4959
  ctx.indent++;
4686
- stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}`);
4960
+ 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));
4687
4961
  ctx.indent--;
4688
4962
  stmts.push(`${ind(ctx)}}`);
4689
4963
  return { stmts, valueVar: valV, endVar: endV };
4690
4964
  }
4691
- function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4965
+ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, coverageBase) {
4692
4966
  const valV = v(ctx, "_llv");
4693
4967
  const endV = v(ctx, "_lle");
4694
4968
  const stmts = [`${ind(ctx)}let ${valV}, ${endV} = ${pos}`];
@@ -4705,10 +4979,12 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4705
4979
  ctx.indent++;
4706
4980
  const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
4707
4981
  stmts.push(
4982
+ ...coverageAttempt(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos),
4708
4983
  ...emitLeafCapture(ctx, JSON.stringify(litVal), pos, litEnd),
4709
4984
  ...tR.stmts,
4710
4985
  `${ind(ctx)}${valV} = ${tR.valueVar}`,
4711
- `${ind(ctx)}${endV} = ${litEnd}`
4986
+ `${ind(ctx)}${endV} = ${litEnd}`,
4987
+ ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos, litEnd)
4712
4988
  );
4713
4989
  ctx.indent--;
4714
4990
  stmts.push(`${ind(ctx)}}`);
@@ -4716,16 +4992,18 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4716
4992
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
4717
4993
  return { stmts, valueVar: valV, endVar: endV };
4718
4994
  }
4719
- function emitFirstMatch(def, ctx, pos) {
4995
+ function emitFirstMatch(def, ctx, pos, coverageBase) {
4720
4996
  const resValV = v(ctx, "_crv");
4721
4997
  const resEndV = v(ctx, "_cre");
4722
4998
  const resOkV = v(ctx, "_crok");
4723
4999
  const codeV = v(ctx, "_chcode");
4724
5000
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
5001
+ const autoRejectedV = coverageBase === void 0 ? void 0 : v(ctx, "_carej");
4725
5002
  const ind0 = ind(ctx);
4726
5003
  const stmts = [
4727
5004
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
4728
5005
  `${ind0}let ${slots.join(", ")}`,
5006
+ ...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
4729
5007
  `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
4730
5008
  ];
4731
5009
  for (let i = 0; i < def.parsers.length; i++) {
@@ -4761,6 +5039,7 @@ function emitFirstMatch(def, ctx, pos) {
4761
5039
  stmts.push(`${ind0}if (${skipCond}) {`);
4762
5040
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
4763
5041
  ctx.indent += fsGuard ? 2 : 1;
5042
+ if (coverageBase !== void 0) stmts.push(`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'attempt', offset: ${pos} })`);
4764
5043
  if (markLeaves) {
4765
5044
  stmts.push(
4766
5045
  `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
@@ -4783,13 +5062,21 @@ function emitFirstMatch(def, ctx, pos) {
4783
5062
  stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
4784
5063
  stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
4785
5064
  stmts.push(`${ind(ctx)} ${resOkV} = true`);
5065
+ if (autoRejectedV !== void 0) {
5066
+ 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} }) }`);
5067
+ }
5068
+ stmts.push(...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endVar));
4786
5069
  stmts.push(`${ind(ctx)} }`);
5070
+ if (autoRejectedV !== void 0) {
5071
+ stmts.push(`${ind(ctx)} else { ${autoRejectedV}.push(${JSON.stringify(`${coverageBase}${i}`)}) }`);
5072
+ }
4787
5073
  stmts.push(`${ind(ctx)}}`);
4788
- stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
5074
+ 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} });`} }`);
4789
5075
  if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
4790
5076
  } else {
4791
- stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
4792
- stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
5077
+ 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} }) }`;
5078
+ 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} })`} }`);
5079
+ 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} })`} }`);
4793
5080
  }
4794
5081
  ctx.indent -= fsGuard ? 2 : 1;
4795
5082
  if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
@@ -4799,12 +5086,12 @@ function emitFirstMatch(def, ctx, pos) {
4799
5086
  stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
4800
5087
  return { stmts, valueVar: resValV, endVar: resEndV };
4801
5088
  }
4802
- function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
5089
+ function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
4803
5090
  if (strategy.tag === "greedyClassify")
4804
- return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
5091
+ return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
4805
5092
  if (strategy.tag === "literalsLongestFirst")
4806
- return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
4807
- return emitFirstMatch(def, ctx, pos);
5093
+ return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
5094
+ return emitFirstMatch(def, ctx, pos, coverageBase);
4808
5095
  }
4809
5096
  function pushMapFn(ctx, fn, src) {
4810
5097
  if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
@@ -4922,6 +5209,27 @@ function emitOptional(def, ctx, pos) {
4922
5209
  ];
4923
5210
  return { stmts, valueVar: valV, endVar: endV };
4924
5211
  }
5212
+ function emitAttempt(p, def, ctx, pos) {
5213
+ const inner = emitFallible(def.parser, ctx, pos);
5214
+ const leaves = v(ctx, "_atl");
5215
+ const raw = v(ctx, "_atr");
5216
+ const trivia2 = v(ctx, "_att");
5217
+ const log = v(ctx, "_atg");
5218
+ const fields = v(ctx, "_atf");
5219
+ const errors = v(ctx, "_ate");
5220
+ 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}`;
5221
+ const traceId = ctx.coverage?.plan.attempts.get(p);
5222
+ const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
5223
+ return {
5224
+ stmts: [
5225
+ `${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`,
5226
+ ...inner.stmts,
5227
+ ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
5228
+ ],
5229
+ valueVar: inner.valVar,
5230
+ endVar: inner.endVar
5231
+ };
5232
+ }
4925
5233
  function emitSepBy(_p, def, ctx, pos) {
4926
5234
  const arrV = v(ctx, "_arr");
4927
5235
  const curV = v(ctx, "_cur");
@@ -5167,11 +5475,11 @@ function tokenNullableRegex(def) {
5167
5475
  }
5168
5476
  if (inner.tag === "sepBy") {
5169
5477
  const item = tokenSequenceSource(inner.parser);
5170
- const sep = tokenSequenceSource(inner.separator);
5171
- if (!item || !sep) return null;
5172
- const combined = combineTokenRegexSources([item, sep]);
5478
+ const sep2 = tokenSequenceSource(inner.separator);
5479
+ if (!item || !sep2) return null;
5480
+ const combined = combineTokenRegexSources([item, sep2]);
5173
5481
  if (!combined) return null;
5174
- return { tag: "regex", source: `(?:${item.source}(?:${sep.source}${item.source})*)?`, flags: combined.flags };
5482
+ return { tag: "regex", source: `(?:${item.source}(?:${sep2.source}${item.source})*)?`, flags: combined.flags };
5175
5483
  }
5176
5484
  return null;
5177
5485
  }
@@ -5212,6 +5520,36 @@ function emitToken(def, ctx, pos) {
5212
5520
  endVar: inner.endVar
5213
5521
  };
5214
5522
  }
5523
+ function emitLeaf(def, ctx, pos) {
5524
+ const inner = emitFallible(def.parser, ctx, pos);
5525
+ const i = ind(ctx);
5526
+ const sc = v(ctx, "_leafCh");
5527
+ const sl = v(ctx, "_leafLv");
5528
+ const sr = v(ctx, "_leafRaw");
5529
+ const stl = v(ctx, "_leafTl");
5530
+ const sol = v(ctx, "_leafLog");
5531
+ const sb = v(ctx, "_leafBuf");
5532
+ const rawV = v(ctx, "_leafRawValue");
5533
+ const valV = v(ctx, "_leaf");
5534
+ const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
5535
+ return {
5536
+ stmts: [
5537
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${stl} = _ctx._cstTriviaLog, ${sol} = _ctx._triviaLog, ${sb} = _ctx._cstBuf`,
5538
+ `${i}_ctx._cstChildren = undefined; _ctx._cstLeaves = undefined; _ctx._cstRawChildren = undefined; _ctx._cstTriviaLog = undefined; _ctx._triviaLog = undefined; _ctx._cstBuf = undefined`,
5539
+ `${i}try {`,
5540
+ ...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
5541
+ `${i}} finally {`,
5542
+ `${i} _ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx._cstTriviaLog = ${stl}; _ctx._triviaLog = ${sol}; _ctx._cstBuf = ${sb}`,
5543
+ `${i}}`,
5544
+ ...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
5545
+ `${i}const ${rawV} = ${inner.valVar}`,
5546
+ `${i}const ${valV} = ${mfRef(ctx)}[${fnIdx}](${rawV}, { start: ${pos}, end: ${inner.endVar} })`,
5547
+ ...emitLeafCapture(ctx, valV, pos, inner.endVar)
5548
+ ],
5549
+ valueVar: valV,
5550
+ endVar: inner.endVar
5551
+ };
5552
+ }
5215
5553
  function emitNode(def, ctx, pos) {
5216
5554
  if (def.type === void 0) {
5217
5555
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
@@ -5225,17 +5563,20 @@ function emitNode(def, ctx, pos) {
5225
5563
  ctx.buildSrcs.push(def.buildSrc ?? null);
5226
5564
  }
5227
5565
  const i = ind(ctx);
5228
- const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
5566
+ const capturesTrivia = mkType !== null || def.captureTrivia === true || def.trailingTrivia === true || !structural && buildReadsTrivia(def);
5229
5567
  const clonesState = !structural && buildReadsState(def);
5568
+ const capturesChildren = !structural && (mkType !== null || def.unwrap || def.collapse || buildReadsChildren(def));
5569
+ const capturesRaw = !structural && (mkType !== null || buildReadsRaw(def));
5230
5570
  const hasFields = parserHasOwnFields(def.parser);
5231
5571
  const capturesFields = hasFields && !structural && buildReadsFields(def);
5572
+ const innerEnablesTriviaCapture = parserEnablesTriviaCapture(def.parser);
5232
5573
  const chV = v(ctx, "_ch");
5233
5574
  const rawV = v(ctx, "_raw");
5234
- const capTLv = structural ? v(ctx, "_ctl") : null;
5575
+ const capTLv = structural || capturesTrivia ? v(ctx, "_ctl") : null;
5235
5576
  const capSTv = structural ? v(ctx, "_cst") : null;
5236
5577
  const capFv = structural && hasFields ? v(ctx, "_cf") : null;
5237
5578
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
5238
- if (!capturesTrivia) ctx.needsEmptyTl = true;
5579
+ if (!capturesTrivia || structural) ctx.needsEmptyTl = true;
5239
5580
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
5240
5581
  const smk = structural ? v(ctx, "_smk") : null;
5241
5582
  if (structural) ctx.needsHostReads = true;
@@ -5244,16 +5585,21 @@ function emitNode(def, ctx, pos) {
5244
5585
  const profHoist = `${i}const ${pmV} = _ctx._pmProfile, ${recV} = ${pmV}?.phase === 'recognizer', ${capV} = ${pmV}?.phase === 'capture'`;
5245
5586
  const profileRecognizer = recV;
5246
5587
  const profileCapture = capV;
5247
- const allocStmt = structural ? `${i}const ${capTLv} = !(${profileRecognizer}) && (${profileCapture} || ${hostTriviaGate}), ${capSTv} = !(${profileRecognizer} || ${profileCapture}) && (_ctx._pmCapST ??= (_ctx.build === undefined || _hostReads(_ctx.build, 6)))${capFv ? `, ${capFv} = !(${profileRecognizer}) && (${profileCapture} || (_ctx.build !== undefined && _hostReads(_ctx.build, 2)))` : ""}
5248
- ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capturesTrivia ? `${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : []` : `${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : []`;
5249
- const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
5588
+ const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
5589
+ const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
5590
+ const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
5591
+ 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)))` : ""}
5592
+ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5593
+ ` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5594
+ ` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
5595
+ const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
5250
5596
  const fieldsOn = structural ? capFv ?? "false" : `${profileRecognizer} ? false : ${capturesFields ? "true" : "false"}`;
5251
5597
  const sf = hasFields ? v(ctx, "_sf") : null;
5252
5598
  const fArr = hasFields ? v(ctx, "_fa") : null;
5253
5599
  const fObj = hasFields ? v(ctx, "_fields") : "undefined";
5254
- const needsTriviaFrame = parserHasTriviaSite(def.parser);
5600
+ const needsTriviaFrame = def.trailingTrivia === true || parserHasTriviaSite(def.parser);
5255
5601
  const saveTrivia = needsTriviaFrame ? `, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${smk ? `, ${smk} = _ctx._triviaCaptureMask` : ""}` : "";
5256
- const installTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}${smk ? `; _ctx._triviaCaptureMask = ${capTLv} && _ctx.build._parsemanTriviaKinds !== undefined ? _ctx.build._parsemanTriviaKinds(${JSON.stringify(def.type)}) : ${smk}` : ""}` : "";
5602
+ const installTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${capTLv ?? "false"}; _ctx._cstTriviaLog = ${innerTl}${smk ? `; _ctx._triviaCaptureMask = ${capTLv} && _ctx.build !== undefined && _ctx.build._parsemanTriviaKinds !== undefined ? _ctx.build._parsemanTriviaKinds(${JSON.stringify(def.type)}) : ${smk}` : ""}` : "";
5257
5603
  const restoreTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}` : "";
5258
5604
  const stmts = [
5259
5605
  profHoist,
@@ -5261,8 +5607,13 @@ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRec
5261
5607
  `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren${saveTrivia}${sf ? `, ${sf} = _ctx._fields` : ""}`,
5262
5608
  `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}${installTrivia}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
5263
5609
  ];
5264
- const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
5610
+ const { stmts: innerStmts, okVar, endVar: innerEndVar } = emitFallible(def.parser, ctx, pos);
5611
+ const endVar = def.trailingTrivia === true && ctx.activeTrivia ? v(ctx, "_trailend") : innerEndVar;
5265
5612
  stmts.push(...innerStmts);
5613
+ if (def.trailingTrivia === true && ctx.activeTrivia) {
5614
+ const triviaFn = ensureTriviaFn(ctx);
5615
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
5616
+ }
5266
5617
  if (sf && fArr) {
5267
5618
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
5268
5619
  }
@@ -5302,8 +5653,9 @@ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRec
5302
5653
  }
5303
5654
  const ndV = v(ctx, "_nd");
5304
5655
  const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
5305
- const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? (_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})) : (${buildExpr})` : buildExpr;
5306
- const hostCollapseExpr = ctx.ns || structural ? `_ctx.build !== undefined && _ctx.build._parsemanCstCollapse !== undefined && ${chV}.length === 1 && ${rawV}.length === 1 && _ctx.build._parsemanCstCollapse(${JSON.stringify(def.type)}, ${chV}[0], ${chV}, ${rawV}) ? ${chV}[0] : (${ndExpr})` : ndExpr;
5656
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
5657
+ const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : `_ctx.build?._parsemanCstOutput === true ? (${hostBuildExpr}) : (${buildExpr})`;
5658
+ const hostCollapseExpr = structural ? `_ctx.build !== undefined && _ctx.build._parsemanCstCollapse !== undefined && ${chV}.length === 1 && ${rawV}.length === 1 && _ctx.build._parsemanCstCollapse(${JSON.stringify(def.type)}, ${chV}[0], ${chV}, ${rawV}) ? ${chV}[0] : (${ndExpr})` : ndExpr;
5307
5659
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
5308
5660
  const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
5309
5661
  const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
@@ -5311,7 +5663,7 @@ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRec
5311
5663
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
5312
5664
  `${i}if (!(${profileRecognizer})) {`,
5313
5665
  `${i} if (${sc}) ${sc}.push(${ndV})`,
5314
- `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`,
5666
+ `${i} if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : (typeof ${ndV} === 'object' && ${ndV} !== null ? input.slice(${pos}, ${endVar}) : ''), span: { start: ${pos}, end: ${endVar} } })`,
5315
5667
  `${i}}`
5316
5668
  );
5317
5669
  return { stmts, valueVar: ndV, endVar };
@@ -5356,14 +5708,23 @@ function emitLazy(p, def, ctx, pos) {
5356
5708
  const savedIndent = ctx.indent;
5357
5709
  const savedFailLabel = ctx.failLabel;
5358
5710
  const savedRecord = ctx.recordFail;
5711
+ const failureRuleId = p === ctx.coverage?.entry ? void 0 : ctx.coverage?.plan.rules.get(p) ?? ctx.coverage?.plan.rules.get(resolved);
5712
+ const savedActiveRule = ctx.activeCoverageRuleId;
5713
+ const savedSuppressFailure = ctx.suppressCoverageFailure;
5359
5714
  ctx.indent = 1;
5360
5715
  ctx.failLabel = "_pfail";
5361
5716
  ctx.recordFail = true;
5717
+ if (failureRuleId !== void 0) {
5718
+ ctx.activeCoverageRuleId = void 0;
5719
+ ctx.suppressCoverageFailure = true;
5720
+ }
5362
5721
  const r = emit(resolved, ctx, "_pos");
5363
5722
  ctx.indent = savedIndent;
5364
5723
  ctx.failLabel = savedFailLabel;
5365
5724
  ctx.recordFail = savedRecord;
5366
- pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
5725
+ ctx.activeCoverageRuleId = savedActiveRule;
5726
+ ctx.suppressCoverageFailure = savedSuppressFailure;
5727
+ pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar, failureRuleId);
5367
5728
  }
5368
5729
  const fnName = ctx.namedParsers.get(p);
5369
5730
  return emitNamedFnCall(ctx, fnName, pos);
@@ -5414,6 +5775,30 @@ function emitExpect(def, ctx, pos) {
5414
5775
  return { stmts, valueVar: valVar, endVar };
5415
5776
  }
5416
5777
  function emit(p, ctx, pos) {
5778
+ const dispatch = (emissionPos = pos) => {
5779
+ const savedRule = ctx.activeCoverageRuleId;
5780
+ const rule = ctx.coverage?.plan.rules.get(p);
5781
+ if (rule && p !== ctx.coverage?.entry && !ctx.suppressCoverageFailure) ctx.activeCoverageRuleId = rule;
5782
+ const result = emitDispatch(p, ctx, emissionPos);
5783
+ ctx.activeCoverageRuleId = savedRule;
5784
+ return result;
5785
+ };
5786
+ const instrument = (r) => {
5787
+ const ruleId = ctx.coverage?.plan.rules.get(p);
5788
+ const labelIds = ctx.coverage?.plan.labels.get(p);
5789
+ if (!ruleId && !labelIds) return r;
5790
+ return {
5791
+ ...r,
5792
+ stmts: [
5793
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`] : [],
5794
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: ${pos} })`] : [],
5795
+ ...r.stmts,
5796
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`] : [],
5797
+ ...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`]) ?? [],
5798
+ ...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`]) ?? []
5799
+ ]
5800
+ };
5801
+ };
5417
5802
  const usage = ctx.lazyUsage;
5418
5803
  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
5419
5804
  // overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
@@ -5430,14 +5815,14 @@ function emit(p, ctx, pos) {
5430
5815
  ctx.indent = 1;
5431
5816
  ctx.failLabel = "_pfail";
5432
5817
  ctx.recordFail = true;
5433
- const r = emitDispatch(p, ctx, "_pos");
5818
+ const r = dispatch("_pos");
5434
5819
  ctx.indent = savedIndent;
5435
5820
  ctx.failLabel = savedFailLabel;
5436
5821
  ctx.recordFail = savedRecord;
5437
5822
  pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
5438
- return emitNamedFnCall(ctx, fnName, pos);
5823
+ return instrument(emitNamedFnCall(ctx, fnName, pos));
5439
5824
  }
5440
- return emitDispatch(p, ctx, pos);
5825
+ return instrument(dispatch());
5441
5826
  }
5442
5827
  function emitDispatch(p, ctx, pos) {
5443
5828
  const def = p._def;
@@ -5451,7 +5836,9 @@ function emitDispatch(p, ctx, pos) {
5451
5836
  case "sequence":
5452
5837
  return emitSeq(def, ctx, pos);
5453
5838
  case "choice":
5454
- return emitChoice(def, ctx, pos);
5839
+ return emitChoice(p, def, ctx, pos);
5840
+ case "attempt":
5841
+ return emitAttempt(p, def, ctx, pos);
5455
5842
  case "many":
5456
5843
  case "oneOrMore":
5457
5844
  return emitMany(def, ctx, pos);
@@ -5520,6 +5907,8 @@ function emitDispatch(p, ctx, pos) {
5520
5907
  return emit(def.parser, ctx, pos);
5521
5908
  case "token":
5522
5909
  return emitToken(def, ctx, pos);
5910
+ case "leaf":
5911
+ return emitLeaf(def, ctx, pos);
5523
5912
  case "label": {
5524
5913
  const inner = emitFallible(def.parser, ctx, pos);
5525
5914
  return {
@@ -5554,10 +5943,19 @@ function emitDispatch(p, ctx, pos) {
5554
5943
  ctx.triviaKindLabels = def.triviaParser._meta.triviaKindLabels;
5555
5944
  }
5556
5945
  }
5946
+ const savedCapture = def.captureTrivia ? v(ctx, "_gcap") : null;
5557
5947
  const r = emit(def.parser, ctx, pos);
5558
5948
  ctx.activeTrivia = savedTrivia;
5559
5949
  ctx.triviaKindLabels = savedKindLabels;
5560
- return r;
5950
+ if (!savedCapture) return r;
5951
+ return {
5952
+ ...r,
5953
+ stmts: [
5954
+ `${ind(ctx)}const ${savedCapture} = _ctx.captureTrivia; _ctx.captureTrivia = true`,
5955
+ ...r.stmts,
5956
+ `${ind(ctx)}_ctx.captureTrivia = ${savedCapture}`
5957
+ ]
5958
+ };
5561
5959
  }
5562
5960
  case "not":
5563
5961
  return emitNot(def, ctx, pos);
@@ -5642,6 +6040,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5642
6040
  case "grammar":
5643
6041
  case "trivia":
5644
6042
  case "token":
6043
+ case "leaf":
5645
6044
  case "label":
5646
6045
  case "field":
5647
6046
  case "optional":
@@ -5669,16 +6068,32 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5669
6068
  return false;
5670
6069
  }
5671
6070
  }
6071
+ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
6072
+ if (seen.has(p)) return false;
6073
+ seen.add(p);
6074
+ const d = p._def;
6075
+ if (d.tag === "node" && d.build !== void 0) return true;
6076
+ if (d.tag === "lazy") {
6077
+ try {
6078
+ return hasDirectBuildDef(d.thunk(), seen);
6079
+ } catch {
6080
+ return false;
6081
+ }
6082
+ }
6083
+ return childrenOf(d).some((child) => hasDirectBuildDef(child, seen));
6084
+ }
5672
6085
  function childrenOf(def) {
5673
6086
  switch (def.tag) {
5674
6087
  case "sequence":
5675
6088
  case "choice":
5676
6089
  return def.parsers;
5677
6090
  case "many":
6091
+ case "attempt":
5678
6092
  case "optional":
5679
6093
  case "transform":
5680
6094
  case "trivia":
5681
6095
  case "token":
6096
+ case "leaf":
5682
6097
  case "label":
5683
6098
  case "field":
5684
6099
  case "grammar":
@@ -5853,10 +6268,25 @@ function compile(combinator, mapFnSources, opts) {
5853
6268
  namedFnDecls: [],
5854
6269
  capturing: hasNodeDef(combinator),
5855
6270
  recovery: opts?.recovery ?? false,
6271
+ ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
5856
6272
  lazyUsage: analyzeLazyUsage(combinator),
5857
6273
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5858
6274
  };
5859
6275
  const r = emit(combinator, ctx, "_pos");
6276
+ const coverageRootRuleId = ctx.coverage?.plan.rules.get(combinator);
6277
+ const resultBody = coverageRootRuleId === void 0 ? [
6278
+ ` let pos = _pos`,
6279
+ ...r.stmts,
6280
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6281
+ ] : [
6282
+ ` const _coverageResult = (() => {`,
6283
+ ` let pos = _pos`,
6284
+ ...r.stmts,
6285
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6286
+ ` })()`,
6287
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6288
+ ` return _coverageResult`
6289
+ ];
5860
6290
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
5861
6291
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
5862
6292
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
@@ -5869,9 +6299,7 @@ function compile(combinator, mapFnSources, opts) {
5869
6299
  ...namedPrelude,
5870
6300
  ctx.namedFnDecls.join("\n\n"),
5871
6301
  `function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
5872
- ` let pos = _pos`,
5873
- ...r.stmts,
5874
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6302
+ ...resultBody,
5875
6303
  `}`
5876
6304
  ].join("\n");
5877
6305
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
@@ -5881,9 +6309,15 @@ function compile(combinator, mapFnSources, opts) {
5881
6309
  ...ctx.expectedDecls,
5882
6310
  ...namedPrelude,
5883
6311
  ...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
5884
- `let pos = _pos`,
5885
- ...r.stmts,
5886
- `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6312
+ ...coverageRootRuleId === void 0 ? [`let pos = _pos`, ...r.stmts, `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`] : [
6313
+ `const _coverageResult = (() => {`,
6314
+ ` let pos = _pos`,
6315
+ ...r.stmts,
6316
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6317
+ `})()`,
6318
+ `if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6319
+ `return _coverageResult`
6320
+ ]
5887
6321
  ].join("\n"));
5888
6322
  const defaultCtx = {
5889
6323
  trackLines: false,
@@ -5895,10 +6329,11 @@ function compile(combinator, mapFnSources, opts) {
5895
6329
  const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
5896
6330
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
5897
6331
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
5898
- const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
6332
+ const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
5899
6333
  return {
5900
6334
  source,
5901
6335
  inlineExpression,
6336
+ ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
5902
6337
  parse(input, pos = 0) {
5903
6338
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
5904
6339
  },
@@ -5932,6 +6367,7 @@ function publicRuleWrapperSource(rule, fnSource) {
5932
6367
  }
5933
6368
  function compileRuleMap(ruleMap, opts) {
5934
6369
  for (const [, rule] of ruleMap) markUnusedValues(rule);
6370
+ const coverageWinners = opts?.coverage ? Object.fromEntries(ruleMap.filter(([, rule]) => rule._def.tag !== "lazy")) : void 0;
5935
6371
  const grammarTrivia = opts?.trivia;
5936
6372
  const ctx = {
5937
6373
  vars: 0,
@@ -5952,6 +6388,7 @@ function compileRuleMap(ruleMap, opts) {
5952
6388
  namedFnDecls: [],
5953
6389
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
5954
6390
  recovery: opts?.recovery ?? false,
6391
+ ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(ruleMap.map(([, rule]) => rule), coverageWinners) } } : {},
5955
6392
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5956
6393
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5957
6394
  };
@@ -5994,15 +6431,34 @@ function compileRuleMap(ruleMap, opts) {
5994
6431
  return i > 0 ? ["", ...lines] : lines;
5995
6432
  })
5996
6433
  ].filter(Boolean);
5997
- const entryFnText = (r) => [
5998
- `function(input, _pos, _ctx) {`,
5999
- ` let pos = _pos`,
6000
- ...r.stmts,
6001
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6002
- `}`
6003
- ].join("\n");
6434
+ const entryRuleId = (rule) => {
6435
+ const direct = ctx.coverage?.plan.rules.get(rule);
6436
+ if (direct !== void 0 || rule._def.tag !== "lazy") return direct;
6437
+ try {
6438
+ return ctx.coverage?.plan.rules.get(rule._def.thunk());
6439
+ } catch {
6440
+ return void 0;
6441
+ }
6442
+ };
6443
+ const entryFnText = (r, rule) => {
6444
+ const ruleId = entryRuleId(rule);
6445
+ const body = ruleId === void 0 ? [
6446
+ ` let pos = _pos`,
6447
+ ...r.stmts,
6448
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6449
+ ] : [
6450
+ ` const _coverageResult = (() => {`,
6451
+ ` let pos = _pos`,
6452
+ ...r.stmts,
6453
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6454
+ ` })()`,
6455
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6456
+ ` return _coverageResult`
6457
+ ];
6458
+ return [`function(input, _pos, _ctx) {`, ...body, `}`].join("\n");
6459
+ };
6004
6460
  const objBody = perEntry.map(({ key, rule, r }) => {
6005
- const src = publicRuleWrapperSource(rule, entryFnText(r));
6461
+ const src = publicRuleWrapperSource(rule, entryFnText(r, rule));
6006
6462
  return ` ${JSON.stringify(key)}: ${src.split("\n").join("\n ")}`;
6007
6463
  }).join(",\n");
6008
6464
  const replacement = [
@@ -6013,7 +6469,11 @@ function compileRuleMap(ruleMap, opts) {
6013
6469
  ` }`,
6014
6470
  `})()`
6015
6471
  ].join("\n");
6016
- return { keys: perEntry.map((e) => e.key), replacement };
6472
+ return {
6473
+ keys: perEntry.map((e) => e.key),
6474
+ replacement,
6475
+ ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
6476
+ };
6017
6477
  }
6018
6478
  function compileLinkable(ruleMapArg, ns, opts) {
6019
6479
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
@@ -6046,8 +6506,13 @@ function compileLinkable(ruleMapArg, ns, opts) {
6046
6506
  triviaCaptureNames: /* @__PURE__ */ new Map(),
6047
6507
  triviaFnNames: /* @__PURE__ */ new Map(),
6048
6508
  namedFnDecls: [],
6049
- capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
6509
+ // A recognition-only fragment normally has no node collector of its own.
6510
+ // Leaf composition can re-lower it beneath a local semantic node, though;
6511
+ // in that case its terminals must feed the caller's collector rather than
6512
+ // merely returning their scalar parse values.
6513
+ capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
6050
6514
  recovery: opts?.recovery ?? false,
6515
+ ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
6051
6516
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
6052
6517
  ns,
6053
6518
  deferFirstSetRefs: true,
@@ -6121,7 +6586,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
6121
6586
  ctx.indent = savedIndent;
6122
6587
  ctx.failLabel = savedFail;
6123
6588
  ctx.recordFail = savedRec;
6124
- pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
6589
+ const ruleId = ctx.coverage?.plan.rules.get(rule);
6590
+ const stmts = ruleId === void 0 ? body.stmts : [
6591
+ `${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`,
6592
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: _pos })`,
6593
+ ...body.stmts,
6594
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: _pos, end: ${body.endVar} })`
6595
+ ];
6596
+ pushNamedFnDecl(ctx, fn, stmts, body.valueVar, body.endVar);
6125
6597
  }
6126
6598
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
6127
6599
  }
@@ -6178,15 +6650,46 @@ function compileLinkable(ruleMapArg, ns, opts) {
6178
6650
  deps: ruleDependencies(ruleMap),
6179
6651
  needsEmptyTl: !!ctx.needsEmptyTl,
6180
6652
  needsHostReads: !!ctx.needsHostReads,
6653
+ hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
6654
+ isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
6181
6655
  mfFns: mfSrcs ? [] : ctx.mapFns,
6182
6656
  buildFns: buildSrcs ? [] : ctx.buildFns
6183
6657
  };
6184
6658
  }
6185
- function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
6186
- const bodyLines = [
6659
+ function hasSemanticReduction(roots) {
6660
+ const seen = /* @__PURE__ */ new Set();
6661
+ const visit = (parser2) => {
6662
+ if (seen.has(parser2)) return false;
6663
+ seen.add(parser2);
6664
+ const def = parser2._def;
6665
+ if (def.tag === "transform" && !def.recognitionOnly) return true;
6666
+ if (def.tag === "choice" && def.gates.some(Boolean)) return true;
6667
+ if (def.tag === "guard" || def.tag === "withCtx") return true;
6668
+ if (def.tag === "node" && def.build !== void 0) return true;
6669
+ if (def.tag === "lazy") {
6670
+ try {
6671
+ return visit(def.thunk());
6672
+ } catch {
6673
+ return true;
6674
+ }
6675
+ }
6676
+ return childrenOf(def).some(visit);
6677
+ };
6678
+ return roots.some(visit);
6679
+ }
6680
+ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootRuleId) {
6681
+ const bodyLines = coverageRootRuleId === void 0 ? [
6187
6682
  ` let pos = _pos`,
6188
6683
  ...r.stmts,
6189
6684
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6685
+ ] : [
6686
+ ` const _coverageResult = (() => {`,
6687
+ ` let pos = _pos`,
6688
+ ...r.stmts,
6689
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6690
+ ` })()`,
6691
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6692
+ ` return _coverageResult`
6190
6693
  ];
6191
6694
  const innerFn = [
6192
6695
  `function(input, _pos, _ctx) {`,
@@ -6241,8 +6744,8 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
6241
6744
  };
6242
6745
  const emit2 = (end) => {
6243
6746
  if (end > pos && cstCaptureActive(ctx)) {
6244
- const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
6245
- pushCstLeaf(ctx, leaf);
6747
+ const leaf2 = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
6748
+ pushCstLeaf(ctx, leaf2);
6246
6749
  }
6247
6750
  };
6248
6751
  while (cur < input.length) {
@@ -6296,6 +6799,7 @@ function balanced(open, close, options = {}) {
6296
6799
  );
6297
6800
  if (combi._def.tag === "transform") {
6298
6801
  combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
6802
+ combi._def.recognitionOnly = true;
6299
6803
  }
6300
6804
  self.define(combi);
6301
6805
  return combi;
@@ -6341,9 +6845,11 @@ function childrenOf2(def) {
6341
6845
  case "many":
6342
6846
  case "oneOrMore":
6343
6847
  case "optional":
6848
+ case "attempt":
6344
6849
  case "transform":
6345
6850
  case "trivia":
6346
6851
  case "token":
6852
+ case "leaf":
6347
6853
  case "label":
6348
6854
  case "field":
6349
6855
  case "not":
@@ -6392,7 +6898,7 @@ function serializeRuleMap(ruleMap) {
6392
6898
  }
6393
6899
  }
6394
6900
  function evalRuleMapIR(ir) {
6395
- const _tf = (child, src) => {
6901
+ const _tf = (child, src, recognitionOnly = false) => {
6396
6902
  let fn2;
6397
6903
  try {
6398
6904
  fn2 = (0, eval)(`(${src})`);
@@ -6403,11 +6909,31 @@ function evalRuleMapIR(ir) {
6403
6909
  }
6404
6910
  const t = transform(child, fn2);
6405
6911
  t._def.fnSrc = src;
6912
+ if (recognitionOnly) t._def.recognitionOnly = true;
6406
6913
  return t;
6407
6914
  };
6408
- const _nd = (type, child, src, opts) => {
6409
- const n = node(type, child, void 0, opts);
6915
+ const _lf = (child, src) => {
6916
+ let fn2;
6917
+ try {
6918
+ fn2 = (0, eval)(`(${src})`);
6919
+ } catch {
6920
+ fn2 = () => {
6921
+ throw new Error("IR leaf fn not materialized");
6922
+ };
6923
+ }
6924
+ const l = leaf(child, fn2);
6925
+ l._def.fnSrc = src;
6926
+ return l;
6927
+ };
6928
+ const _nd = (type, child, src, opts, staticError) => {
6929
+ if (staticError !== void 0 && staticError.length > 0) {
6930
+ throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
6931
+ }
6932
+ const n = node(type, child, (() => {
6933
+ throw new Error("IR node build requires static re-lowering");
6934
+ }), opts);
6410
6935
  n._def.buildSrc = src;
6936
+ if (staticError !== void 0) n._def.buildStaticError = staticError;
6411
6937
  return n;
6412
6938
  };
6413
6939
  const _gch = (items) => {
@@ -6457,6 +6983,7 @@ function evalRuleMapIR(ir) {
6457
6983
  "keywords",
6458
6984
  "sequence",
6459
6985
  "choice",
6986
+ "attempt",
6460
6987
  "many",
6461
6988
  "oneOrMore",
6462
6989
  "optional",
@@ -6466,6 +6993,7 @@ function evalRuleMapIR(ir) {
6466
6993
  "parser",
6467
6994
  "scanTo",
6468
6995
  "token",
6996
+ "leaf",
6469
6997
  "transform",
6470
6998
  "skip",
6471
6999
  "trivia",
@@ -6473,6 +7001,7 @@ function evalRuleMapIR(ir) {
6473
7001
  "field",
6474
7002
  "expect",
6475
7003
  "_tf",
7004
+ "_lf",
6476
7005
  "_nd",
6477
7006
  "_gch",
6478
7007
  "_wc",
@@ -6486,6 +7015,7 @@ function evalRuleMapIR(ir) {
6486
7015
  keywords,
6487
7016
  sequence,
6488
7017
  choice,
7018
+ attempt,
6489
7019
  many,
6490
7020
  oneOrMore,
6491
7021
  optional,
@@ -6495,6 +7025,7 @@ function evalRuleMapIR(ir) {
6495
7025
  parser,
6496
7026
  scanTo,
6497
7027
  token,
7028
+ leaf,
6498
7029
  transform,
6499
7030
  skip,
6500
7031
  trivia,
@@ -6502,6 +7033,7 @@ function evalRuleMapIR(ir) {
6502
7033
  field,
6503
7034
  expect,
6504
7035
  _tf,
7036
+ _lf,
6505
7037
  _nd,
6506
7038
  _gch,
6507
7039
  _wc
@@ -6633,6 +7165,8 @@ ${body}
6633
7165
  return `oneOrMore(${kid(def.parser)})`;
6634
7166
  case "optional":
6635
7167
  return `optional(${kid(def.parser)})`;
7168
+ case "attempt":
7169
+ return `attempt(${kid(def.parser)})`;
6636
7170
  case "sepBy":
6637
7171
  return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
6638
7172
  case "not":
@@ -6641,6 +7175,10 @@ ${body}
6641
7175
  return `trivia(${kid(def.parser)})`;
6642
7176
  case "token":
6643
7177
  return `token(${kid(def.parser)})`;
7178
+ case "leaf": {
7179
+ if (def.fnSrc === void 0) throw new Unserializable("leaf without fnSrc");
7180
+ return `_lf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
7181
+ }
6644
7182
  case "label":
6645
7183
  return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
6646
7184
  case "field":
@@ -6653,21 +7191,30 @@ ${body}
6653
7191
  return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
6654
7192
  case "transform": {
6655
7193
  if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
6656
- return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
7194
+ return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
6657
7195
  }
6658
7196
  case "node": {
6659
7197
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
6660
- const opts = def.unwrap || def.collapse ? `, { ${def.unwrap ? "unwrap: true" : "collapse: true"} }` : "";
7198
+ const optEntries = [
7199
+ ...def.unwrap ? ["unwrap: true"] : [],
7200
+ ...def.collapse ? ["collapse: true"] : [],
7201
+ ...def.captureTrivia ? ["captureTrivia: true"] : [],
7202
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : []
7203
+ ];
7204
+ const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
6661
7205
  if (def.type === void 0) {
6662
7206
  if (def.buildSrc !== void 0) throw new Unserializable("inferred node build without inferred type");
6663
7207
  return opts ? `node(${kid(def.parser)}, undefined${opts})` : `node(${kid(def.parser)})`;
6664
7208
  }
6665
- if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
7209
+ if (def.buildSrc !== void 0) {
7210
+ const staticError = def.buildStaticError === void 0 ? "" : `${opts ? "" : ", undefined"}, ${JSON.stringify(def.buildStaticError)}`;
7211
+ return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts}${staticError})`;
7212
+ }
6666
7213
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
6667
7214
  }
6668
7215
  case "grammar": {
6669
7216
  const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
6670
- return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
7217
+ return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
6671
7218
  }
6672
7219
  case "withCtx": {
6673
7220
  if (def.extraSrc === void 0) throw new Unserializable("withCtx without extraSrc");
@@ -6682,6 +7229,28 @@ ${body}
6682
7229
  };
6683
7230
 
6684
7231
  // src/compiler/linker.ts
7232
+ function normalizeCstCollapse(collapse) {
7233
+ if (collapse === true) return () => true;
7234
+ if (Array.isArray(collapse)) {
7235
+ const types = new Set(collapse);
7236
+ return (type) => types.has(type);
7237
+ }
7238
+ return typeof collapse === "function" ? collapse : void 0;
7239
+ }
7240
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
7241
+ return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
7242
+ }
7243
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
7244
+ if (typeof typeOrOptions === "string") {
7245
+ return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
7246
+ }
7247
+ const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
7248
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
7249
+ host._parsemanCstOutput = true;
7250
+ if (collapse) host._parsemanCstCollapse = collapse;
7251
+ return host;
7252
+ }
7253
+ cstBuildHost._parsemanCstOutput = true;
6685
7254
  function pickPieces(pieces, names) {
6686
7255
  const winner = /* @__PURE__ */ new Map();
6687
7256
  for (const pc of pieces) for (const k of pc.keys) winner.set(k, pc);
@@ -6759,9 +7328,12 @@ ${body}
6759
7328
  function isIRPiece(p) {
6760
7329
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
6761
7330
  }
6762
- function materializePiece(p, trivia2) {
7331
+ function materializePiece(p, trivia2, captureTerminals = false) {
6763
7332
  if (!isIRPiece(p)) return p;
6764
- const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, trivia2 ? { trivia: trivia2 } : void 0);
7333
+ const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
7334
+ ...trivia2 ? { trivia: trivia2 } : {},
7335
+ ...captureTerminals ? { captureTerminals: true } : {}
7336
+ });
6765
7337
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
6766
7338
  return pieces;
6767
7339
  }
@@ -6791,6 +7363,152 @@ var { isFinite } = Number;
6791
7363
  // src/index.ts
6792
7364
  registerRegexAnalyzer(firstSetFromRegex);
6793
7365
 
7366
+ // src/plugin/direct-builder-static.ts
7367
+ import { parseSync } from "oxc-parser";
7368
+ var STATIC_BUILDER_GLOBALS = /* @__PURE__ */ new Set([
7369
+ "Array",
7370
+ "Boolean",
7371
+ "Date",
7372
+ "JSON",
7373
+ "Math",
7374
+ "NaN",
7375
+ "Number",
7376
+ "Object",
7377
+ "String",
7378
+ "Infinity",
7379
+ "parseFloat",
7380
+ "parseInt",
7381
+ "undefined"
7382
+ ]);
7383
+ function astArray(value) {
7384
+ return Array.isArray(value) ? value : [];
7385
+ }
7386
+ function astChild(value) {
7387
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
7388
+ }
7389
+ function unwrapCallbackExpression(node2) {
7390
+ let current = node2;
7391
+ while (current?.type === "ParenthesizedExpression") current = astChild(current.expression) ?? void 0;
7392
+ return current;
7393
+ }
7394
+ function directBuilderUnsupportedBindings(src) {
7395
+ let init;
7396
+ try {
7397
+ const parsed = parseSync("parseman-direct-builder.ts", `const _direct = (${src})`);
7398
+ if (parsed.errors.length > 0) return ["invalid callback source"];
7399
+ const program = parsed.program;
7400
+ const statement = program.body?.[0];
7401
+ const declaration = statement?.declarations?.[0];
7402
+ init = unwrapCallbackExpression(declaration?.init);
7403
+ } catch {
7404
+ return ["invalid callback source"];
7405
+ }
7406
+ if (init?.type !== "ArrowFunctionExpression" || !Array.isArray(init.params)) return ["unsupported callback shape"];
7407
+ const allowed = new Set(STATIC_BUILDER_GLOBALS);
7408
+ for (const param of init.params) {
7409
+ if (param.type !== "Identifier" || typeof param.name !== "string") return ["unsupported parameter pattern"];
7410
+ allowed.add(param.name);
7411
+ }
7412
+ const unsupported = /* @__PURE__ */ new Set();
7413
+ const reportUnsupportedNode = (ast) => {
7414
+ unsupported.add(`unsupported ${ast.type}`);
7415
+ };
7416
+ const read = (ast, allowedNames) => {
7417
+ if (typeof ast.name === "string" && allowedNames.has(ast.name)) return;
7418
+ unsupported.add(typeof ast.name === "string" ? ast.name : "identifier");
7419
+ };
7420
+ const expression = (node2, allowedNames) => {
7421
+ const ast = astChild(node2);
7422
+ if (!ast) return;
7423
+ switch (ast.type) {
7424
+ case "Literal":
7425
+ return;
7426
+ case "Identifier":
7427
+ read(ast, allowedNames);
7428
+ return;
7429
+ case "ParenthesizedExpression":
7430
+ case "ChainExpression":
7431
+ case "TSAsExpression":
7432
+ case "TSTypeAssertion":
7433
+ case "TSNonNullExpression":
7434
+ expression(ast.expression, allowedNames);
7435
+ return;
7436
+ case "ArrayExpression":
7437
+ for (const element of astArray(ast.elements)) expression(element, allowedNames);
7438
+ return;
7439
+ case "ObjectExpression":
7440
+ for (const property of astArray(ast.properties)) {
7441
+ if (property.type === "SpreadElement") {
7442
+ expression(property.argument, allowedNames);
7443
+ continue;
7444
+ }
7445
+ if (property.type !== "Property" || property.method === true || property.kind !== "init") {
7446
+ reportUnsupportedNode(property);
7447
+ continue;
7448
+ }
7449
+ if (property.computed === true) expression(property.key, allowedNames);
7450
+ expression(property.value, allowedNames);
7451
+ }
7452
+ return;
7453
+ case "MemberExpression":
7454
+ expression(ast.object, allowedNames);
7455
+ if (ast.computed === true) expression(ast.property, allowedNames);
7456
+ return;
7457
+ case "CallExpression":
7458
+ case "NewExpression":
7459
+ expression(ast.callee, allowedNames);
7460
+ for (const argument of astArray(ast.arguments)) expression(argument, allowedNames);
7461
+ return;
7462
+ case "UnaryExpression":
7463
+ case "UpdateExpression":
7464
+ expression(ast.argument, allowedNames);
7465
+ return;
7466
+ case "BinaryExpression":
7467
+ case "LogicalExpression":
7468
+ case "AssignmentExpression":
7469
+ expression(ast.left, allowedNames);
7470
+ expression(ast.right, allowedNames);
7471
+ return;
7472
+ case "ConditionalExpression":
7473
+ expression(ast.test, allowedNames);
7474
+ expression(ast.consequent, allowedNames);
7475
+ expression(ast.alternate, allowedNames);
7476
+ return;
7477
+ case "SequenceExpression":
7478
+ for (const value of astArray(ast.expressions)) expression(value, allowedNames);
7479
+ return;
7480
+ case "TemplateLiteral":
7481
+ for (const value of astArray(ast.expressions)) expression(value, allowedNames);
7482
+ return;
7483
+ case "SpreadElement":
7484
+ expression(ast.argument, allowedNames);
7485
+ return;
7486
+ case "ArrowFunctionExpression": {
7487
+ const nested = new Set(allowedNames);
7488
+ for (const param of astArray(ast.params)) {
7489
+ if (param.type !== "Identifier" || typeof param.name !== "string") {
7490
+ reportUnsupportedNode(param);
7491
+ continue;
7492
+ }
7493
+ nested.add(param.name);
7494
+ }
7495
+ if (astChild(ast.body)?.type === "BlockStatement") {
7496
+ reportUnsupportedNode(astChild(ast.body));
7497
+ return;
7498
+ }
7499
+ expression(ast.body, nested);
7500
+ return;
7501
+ }
7502
+ default:
7503
+ reportUnsupportedNode(ast);
7504
+ return;
7505
+ }
7506
+ };
7507
+ if (astChild(init.body)?.type === "BlockStatement") unsupported.add("unsupported BlockStatement");
7508
+ else expression(init.body, allowed);
7509
+ return [...unsupported];
7510
+ }
7511
+
6794
7512
  // src/plugin/evaluator.ts
6795
7513
  function stripTsFromSource(node2, code) {
6796
7514
  const cuts = [];
@@ -6844,15 +7562,18 @@ var SUPPORTED = {
6844
7562
  word: (...a) => word(a[0], a[1]),
6845
7563
  sequence: (...a) => sequence(...a),
6846
7564
  choice: (...a) => choice(...a),
7565
+ attempt: (...a) => attempt(a[0]),
6847
7566
  many: (...a) => many(a[0]),
6848
7567
  oneOrMore: (...a) => oneOrMore(a[0]),
6849
7568
  optional: (...a) => optional(a[0]),
7569
+ skip: (...a) => skip(a[0], a[1]),
6850
7570
  sepBy: (...a) => sepBy(a[0], a[1]),
6851
7571
  trivia: (...a) => trivia(a[0]),
6852
7572
  label: (...a) => label(a[0], a[1]),
6853
7573
  field: (...a) => field(a[0], a[1]),
6854
7574
  noTrivia: (...a) => noTrivia(a[0]),
6855
7575
  token: (...a) => token(a[0]),
7576
+ leaf: (...a) => leaf(a[0], a[1]),
6856
7577
  expect: (...a) => expect(a[0], a[1])
6857
7578
  };
6858
7579
  function isScopeEntry(v2) {
@@ -6908,12 +7629,12 @@ function staticNodeOptions(expr) {
6908
7629
  if (p.computed) continue;
6909
7630
  const key = p.key;
6910
7631
  const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
6911
- if (name === "unwrap" || name === "collapse") {
7632
+ if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
6912
7633
  const val = p.value;
6913
7634
  if ((val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true) opts[name] = true;
6914
7635
  }
6915
7636
  }
6916
- return opts.unwrap || opts.collapse ? opts : void 0;
7637
+ return opts.unwrap || opts.collapse || opts.captureTrivia || opts.trailingTrivia ? opts : void 0;
6917
7638
  }
6918
7639
  function exprToCombi(node2, scope, code, mfs) {
6919
7640
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
@@ -6952,7 +7673,7 @@ function exprToCombi(node2, scope, code, mfs) {
6952
7673
  if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
6953
7674
  const inner = anyValue(parserArg, scope, code, mfs);
6954
7675
  if (!isCombinator(inner)) return null;
6955
- const fnSrc = code.slice(fnArg.start, fnArg.end);
7676
+ const fnSrc = stripTsFromSource(fnArg, code);
6956
7677
  mfs.push(fnSrc);
6957
7678
  try {
6958
7679
  const combi = transform(inner, (v2) => v2);
@@ -6962,6 +7683,21 @@ function exprToCombi(node2, scope, code, mfs) {
6962
7683
  return null;
6963
7684
  }
6964
7685
  }
7686
+ if (callee.name === "leaf" && code !== void 0 && mfs !== void 0) {
7687
+ const [parserArg, fnArg] = node2.arguments;
7688
+ if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
7689
+ const inner = anyValue(parserArg, scope, code, mfs);
7690
+ if (!isCombinator(inner)) return null;
7691
+ const fnSrc = stripTsFromSource(fnArg, code);
7692
+ mfs.push(fnSrc);
7693
+ try {
7694
+ const combi = leaf(inner, (v2) => v2);
7695
+ if (combi._def.tag === "leaf") combi._def.fnSrc = fnSrc;
7696
+ return combi;
7697
+ } catch {
7698
+ return null;
7699
+ }
7700
+ }
6965
7701
  if (callee.name === "node" && code !== void 0) {
6966
7702
  const [firstArg, secondArg, thirdArg, fourthArg] = node2.arguments;
6967
7703
  if (!firstArg || firstArg.type === "SpreadElement") return null;
@@ -6975,11 +7711,15 @@ function exprToCombi(node2, scope, code, mfs) {
6975
7711
  if (!isCombinator(inner)) return null;
6976
7712
  const be = buildArg;
6977
7713
  const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !(be.type === "Identifier" && be.name === "undefined");
6978
- const buildSrc = hasBuild ? code.slice(be.start, be.end) : void 0;
7714
+ const buildSrc = hasBuild ? stripTsFromSource(be, code) : void 0;
6979
7715
  const opts = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticNodeOptions(optsArg) : void 0;
6980
7716
  try {
6981
7717
  const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
6982
- if (combi._def.tag === "node" && buildSrc !== void 0) combi._def.buildSrc = buildSrc;
7718
+ if (combi._def.tag === "node" && buildSrc !== void 0) {
7719
+ combi._def.buildSrc = buildSrc;
7720
+ const staticError = directBuilderUnsupportedBindings(buildSrc);
7721
+ if (staticError.length > 0) combi._def.buildStaticError = staticError;
7722
+ }
6983
7723
  return combi;
6984
7724
  } catch {
6985
7725
  return null;
@@ -7072,7 +7812,7 @@ function exprToCombi(node2, scope, code, mfs) {
7072
7812
  if (code === void 0 || mfs === void 0) return null;
7073
7813
  const [predArg] = node2.arguments;
7074
7814
  if (!predArg || predArg.type === "SpreadElement") return null;
7075
- const predSrc = code.slice(predArg.start, predArg.end);
7815
+ const predSrc = stripTsFromSource(predArg, code);
7076
7816
  mfs.push(predSrc);
7077
7817
  try {
7078
7818
  const combi = guard(() => true);
@@ -7087,7 +7827,7 @@ function exprToCombi(node2, scope, code, mfs) {
7087
7827
  if (code === void 0 || mfs === void 0) return null;
7088
7828
  const [extraArg, innerArg] = node2.arguments;
7089
7829
  if (!extraArg || !innerArg || extraArg.type === "SpreadElement" || innerArg.type === "SpreadElement") return null;
7090
- const extraSrc = code.slice(extraArg.start, extraArg.end);
7830
+ const extraSrc = stripTsFromSource(extraArg, code);
7091
7831
  mfs.push(extraSrc);
7092
7832
  const inner = anyValue(innerArg, scope, code, mfs);
7093
7833
  if (!isCombinator(inner)) return null;
@@ -7410,6 +8150,63 @@ function getCompiledResolver() {
7410
8150
  mainFields: ["module", "main"]
7411
8151
  });
7412
8152
  }
8153
+ var PRIVATE_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
8154
+ function nearestPackageRoot(file) {
8155
+ let dir = path.dirname(file);
8156
+ for (; ; ) {
8157
+ try {
8158
+ if (fs.statSync(path.join(dir, "package.json")).isFile()) return fs.realpathSync(dir);
8159
+ } catch {
8160
+ }
8161
+ const parent = path.dirname(dir);
8162
+ if (parent === dir) return null;
8163
+ dir = parent;
8164
+ }
8165
+ }
8166
+ function isWithinPackageRoot(file, root) {
8167
+ const relative2 = path.relative(root, file);
8168
+ return relative2 !== ".." && !relative2.startsWith(`..${path.sep}`) && !path.isAbsolute(relative2);
8169
+ }
8170
+ function resolvePrivateSourceModule(from, specifier) {
8171
+ if (!specifier.startsWith(".") && !path.isAbsolute(specifier)) return null;
8172
+ const packageRoot = nearestPackageRoot(from);
8173
+ if (!packageRoot) return null;
8174
+ const base = path.resolve(path.dirname(from), specifier);
8175
+ const ext = path.extname(base);
8176
+ const candidates = [
8177
+ base,
8178
+ ...ext ? [".ts", ".tsx", ".mts", ".cts"].map((next) => `${base.slice(0, -ext.length)}${next}`) : [],
8179
+ ...!ext ? [".ts", ".tsx", ".mts", ".cts"].map((next) => `${base}${next}`) : [],
8180
+ ...!ext ? ["index.ts", "index.tsx", "index.mts", "index.cts"].map((next) => path.join(base, next)) : []
8181
+ ];
8182
+ for (const candidate of candidates) {
8183
+ try {
8184
+ if (!fs.statSync(candidate).isFile()) continue;
8185
+ const real = fs.realpathSync(candidate);
8186
+ if (isWithinPackageRoot(real, packageRoot)) return real;
8187
+ } catch {
8188
+ }
8189
+ }
8190
+ return null;
8191
+ }
8192
+ function resolveGrammarModule(from, specifier) {
8193
+ if (PRIVATE_SOURCE_EXTENSIONS.has(path.extname(specifier))) {
8194
+ const file2 = resolvePrivateSourceModule(from, specifier);
8195
+ return file2 ? { file: file2, source: true } : null;
8196
+ }
8197
+ try {
8198
+ const file2 = getCompiledResolver().resolveFileSync(from, specifier).path;
8199
+ if (file2) return { file: file2, source: false };
8200
+ } catch {
8201
+ }
8202
+ const file = resolvePrivateSourceModule(from, specifier);
8203
+ return file ? { file, source: true } : null;
8204
+ }
8205
+ var _sourceLowering = /* @__PURE__ */ new Set();
8206
+ var _sourceLoweringCache = /* @__PURE__ */ new Map();
8207
+ function sourceLoweringCacheKey(file, moduleAliases, warnUnloweredRegex, recovery) {
8208
+ return `${file}\0${[...moduleAliases].sort().join("\0")}\0${warnUnloweredRegex ? "warn" : ""}\0${recovery ? "recovery" : ""}`;
8209
+ }
7413
8210
  var _moduleParseCache = /* @__PURE__ */ new Map();
7414
8211
  function parseModuleCached(filePath) {
7415
8212
  let mtimeMs;
@@ -7423,7 +8220,7 @@ function parseModuleCached(filePath) {
7423
8220
  let parsed = null;
7424
8221
  try {
7425
8222
  const src = fs.readFileSync(filePath, "utf8");
7426
- const r = parseSync(filePath, src);
8223
+ const r = parseSync2(filePath, src);
7427
8224
  if (r.errors.length === 0) parsed = { body: r.program.body, src };
7428
8225
  } catch {
7429
8226
  }
@@ -7443,7 +8240,7 @@ var plugin_default = createUnplugin((opts = {}) => ({
7443
8240
  if (!code.includes("parseman")) return null;
7444
8241
  if (!code.includes("macro")) return null;
7445
8242
  const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
7446
- const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true, opts.recovery === true);
8243
+ const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true, opts.recovery === true, opts.grammarCoverage === true);
7447
8244
  if (result?.warnings.length) {
7448
8245
  for (const w of result.warnings) {
7449
8246
  if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
@@ -7547,10 +8344,37 @@ function extractImportBindings(body) {
7547
8344
  }
7548
8345
  return out;
7549
8346
  }
7550
- function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false, recovery = false) {
8347
+ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recovery) {
8348
+ if (_sourceLowering.has(file)) return null;
8349
+ let mtimeMs;
8350
+ try {
8351
+ mtimeMs = fs.statSync(file).mtimeMs;
8352
+ } catch {
8353
+ return null;
8354
+ }
8355
+ const cacheKey = sourceLoweringCacheKey(file, moduleAliases, warnUnloweredRegex, recovery);
8356
+ const hit = _sourceLoweringCache.get(cacheKey);
8357
+ if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
8358
+ _sourceLowering.add(file);
8359
+ let parsedModule = null;
8360
+ try {
8361
+ const source = fs.readFileSync(file, "utf8");
8362
+ const transformed = transformMacro(source, file, moduleAliases, warnUnloweredRegex, recovery);
8363
+ if (!transformed || transformed.warnings.length > 0) return null;
8364
+ const parsed = parseSync2(file, transformed.code);
8365
+ parsedModule = parsed.errors.length === 0 ? { body: parsed.program.body, src: transformed.code } : null;
8366
+ return parsedModule;
8367
+ } catch {
8368
+ return null;
8369
+ } finally {
8370
+ _sourceLoweringCache.set(cacheKey, { mtimeMs, parsed: parsedModule });
8371
+ _sourceLowering.delete(file);
8372
+ }
8373
+ }
8374
+ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false, recovery = false, grammarCoverage = false) {
7551
8375
  let result;
7552
8376
  try {
7553
- result = parseSync(id, code);
8377
+ result = parseSync2(id, code);
7554
8378
  } catch {
7555
8379
  return null;
7556
8380
  }
@@ -7588,6 +8412,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7588
8412
  const warnings = [];
7589
8413
  beginLoweringCapture();
7590
8414
  let anyUnresolved = false;
8415
+ let runtimeComposeFallback = false;
7591
8416
  let ruleMapHolderCounter = 0;
7592
8417
  const lineOf = (pos) => {
7593
8418
  let line = 1;
@@ -7598,7 +8423,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7598
8423
  anyUnresolved = true;
7599
8424
  warnings.push(`${id}:${lineOf(pos)} \u2014 ${msg} (running via the interpreter; add the plugin or simplify the declaration to compile it)`);
7600
8425
  };
7601
- const compileRulesFactory = (init, label2) => {
8426
+ const evaluateRulesFactory = (init, label2) => {
7602
8427
  const args = init.arguments;
7603
8428
  const arg0 = args[0];
7604
8429
  const arg1 = args[1];
@@ -7618,15 +8443,21 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7618
8443
  (p) => p.key?.name === "trivia"
7619
8444
  )?.value : void 0;
7620
8445
  const gTrivia = triviaValue ? evaluateExpr(triviaValue, scope, code, []) : void 0;
7621
- const compiled = compileRuleMap([...ruleMap], { ...gTrivia ? { trivia: gTrivia } : {}, recovery });
8446
+ return { ruleMap, ...gTrivia ? { trivia: gTrivia } : {} };
8447
+ };
8448
+ const compileRulesFactory = (init, label2) => {
8449
+ const evaluated = evaluateRulesFactory(init, label2);
8450
+ if (!evaluated) return null;
8451
+ const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, recovery, coverage: grammarCoverage });
7622
8452
  if (!compiled) {
7623
8453
  warn(init.start, `${label2}: rule map couldn't be inlined`);
7624
8454
  return null;
7625
8455
  }
7626
- return { replacement: compiled.replacement, ruleMap, ...gTrivia ? { trivia: gTrivia } : {} };
8456
+ return { replacement: compiled.replacement, ruleMap: evaluated.ruleMap, ...compiled.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {}, ...evaluated.trivia ? { trivia: evaluated.trivia } : {} };
7627
8457
  };
7628
8458
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
7629
8459
  const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
8460
+ const isComposeLeafCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "composeLeaf";
7630
8461
  const isPickCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "pick";
7631
8462
  const localRuleMaps = /* @__PURE__ */ new Map();
7632
8463
  const localGrammarTrivia = /* @__PURE__ */ new Map();
@@ -7636,18 +8467,29 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7636
8467
  const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
7637
8468
  ([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
7638
8469
  ).join(", ")}])`;
7639
- return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, mfFns: [], buildFns: [] }`;
8470
+ return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
7640
8471
  };
7641
8472
  const isSpread = (it) => "__spreadLocal" in it;
7642
8473
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
7643
8474
  const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)} }` : serializePieces(it);
7644
8475
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
7645
8476
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
8477
+ const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
8478
+ const withCoverageDefinitions = (grammarExpr, definitions) => !grammarCoverage ? grammarExpr : `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.grammarCoverageDefinitions'), { value: Object.freeze(${JSON.stringify(definitions)}.map(Object.freeze)), enumerable: false })`;
8479
+ const emittedCoverageDefinitions = (source) => {
8480
+ const ids = /* @__PURE__ */ new Set();
8481
+ for (const match of source.matchAll(/id:\s*"([^"]+)"/g)) ids.add(match[1]);
8482
+ return [...ids].sort().map((id2) => ({
8483
+ id: id2,
8484
+ kind: id2.startsWith("rule:") ? "rule" : id2.startsWith("label:") ? "label" : "choice-arm"
8485
+ }));
8486
+ };
7646
8487
  const localComposedCarried = /* @__PURE__ */ new Map();
7647
8488
  const localComposedTrivia = /* @__PURE__ */ new Map();
7648
8489
  const importedPiecesCache = /* @__PURE__ */ new Map();
7649
- const resolveModulePieces = (file, exportName, seen) => {
7650
- const mod = parseModuleCached(file);
8490
+ const resolveModulePieces = (module, exportName, seen) => {
8491
+ const { file } = module;
8492
+ const mod = module.source ? lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recovery) : parseModuleCached(file);
7651
8493
  if (!mod) return null;
7652
8494
  const localFor = exportLocalName(mod.body, exportName);
7653
8495
  const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
@@ -7666,12 +8508,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7666
8508
  const b = imports.get(local);
7667
8509
  let subPieces = [];
7668
8510
  if (b) {
7669
- let subFile = null;
7670
- try {
7671
- subFile = getCompiledResolver().resolveFileSync(file, b.source).path ?? null;
7672
- } catch {
8511
+ const subModule = resolveGrammarModule(file, b.source);
8512
+ if (subModule && !seen.has(subModule.file)) {
8513
+ subPieces = resolveModulePieces(subModule, b.imported, new Set(seen).add(subModule.file)) ?? [];
7673
8514
  }
7674
- if (subFile && !seen.has(subFile)) subPieces = resolveModulePieces(subFile, b.imported, new Set(seen).add(subFile)) ?? [];
7675
8515
  }
7676
8516
  stubNames.push(local);
7677
8517
  stubVals.push({ [/* @__PURE__ */ Symbol.for("parseman.composedPieces")]: subPieces });
@@ -7688,29 +8528,56 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7688
8528
  let result2 = null;
7689
8529
  const binding = importBindings.get(localName);
7690
8530
  if (binding) {
7691
- let file = null;
7692
- try {
7693
- file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
7694
- } catch {
7695
- }
7696
- result2 = file ? resolveModulePieces(file, binding.imported, /* @__PURE__ */ new Set([file])) : null;
8531
+ const module = resolveGrammarModule(id, binding.source);
8532
+ result2 = module ? resolveModulePieces(module, binding.imported, /* @__PURE__ */ new Set([module.file])) : null;
7697
8533
  }
7698
8534
  importedPiecesCache.set(localName, result2);
7699
8535
  return result2;
7700
8536
  };
7701
- const materializeCarried = (items, composing) => {
8537
+ const materializeCarried = (items, composing, captureTerminals = false) => {
7702
8538
  const out = [];
7703
8539
  for (const it of items) {
7704
8540
  if (isSpread(it)) {
7705
- for (const p of importedPieces(it.__spreadLocal) ?? []) out.push(materializePiece(p, composing));
8541
+ for (const p of importedPieces(it.__spreadLocal) ?? []) out.push(materializePiece(p, composing, captureTerminals));
7706
8542
  } else if (isIR(it)) {
7707
- out.push(materializePiece(it, composing));
8543
+ out.push(materializePiece(it, composing, captureTerminals));
7708
8544
  } else {
7709
8545
  out.push(it);
7710
8546
  }
7711
8547
  }
7712
8548
  return out;
7713
8549
  };
8550
+ const materializeLeafCoverage = (items, localRules, localNs, composing, captureTerminals = false) => {
8551
+ const sources = [];
8552
+ const add = (item) => {
8553
+ if (!isIR(item)) return false;
8554
+ sources.push({ ns: item.ns, rules: evalRuleMapIR(item.ir) });
8555
+ return true;
8556
+ };
8557
+ for (const item of items) {
8558
+ if (isSpread(item)) {
8559
+ const imported = importedPieces(item.__spreadLocal);
8560
+ if (!imported || !imported.every(add)) return null;
8561
+ } else if (!add(item)) return null;
8562
+ }
8563
+ sources.push({ ns: localNs, rules: [...localRules] });
8564
+ const winners = {};
8565
+ for (const source of sources) for (const [name, rule] of source.rules) winners[name] = rule;
8566
+ const plan = buildGrammarPlan(Object.values(winners), winners);
8567
+ const pieces = [];
8568
+ for (let index = 0; index < sources.length; index++) {
8569
+ const source = sources[index];
8570
+ const piece = compileLinkable(source.rules, source.ns, {
8571
+ ...composing ? { trivia: composing } : {},
8572
+ recovery,
8573
+ ...index < sources.length - 1 && captureTerminals ? { captureTerminals: true } : {},
8574
+ coverage: plan
8575
+ });
8576
+ if (!piece) return null;
8577
+ pieces.push(piece);
8578
+ }
8579
+ return pieces;
8580
+ };
7714
8581
  const localCarried = (rm, label2, composing) => {
7715
8582
  const entries = [...rm];
7716
8583
  const ns = nsFor(label2);
@@ -7826,6 +8693,64 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7826
8693
  return null;
7827
8694
  }
7828
8695
  };
8696
+ const compileComposeLeafCall = (init) => {
8697
+ const args = init.arguments;
8698
+ const arr = args[0];
8699
+ if (!arr || arr.type !== "ArrayExpression") {
8700
+ warn(init.start, "composeLeaf(): expected a static array of grammars");
8701
+ return null;
8702
+ }
8703
+ const elements = arr.elements;
8704
+ if (elements.length < 2) {
8705
+ warn(init.start, "composeLeaf(): needs imported recognition grammar(s) and one local rules() map");
8706
+ return null;
8707
+ }
8708
+ const localArg = elements[elements.length - 1];
8709
+ let localRules = null;
8710
+ if (isRulesCall(localArg)) {
8711
+ const evaluated = evaluateRulesFactory(localArg, `composeLeaf${init.start}`);
8712
+ localRules = evaluated?.ruleMap ?? null;
8713
+ } else if (localArg.type === "Identifier") {
8714
+ localRules = localRuleMaps.get(localArg.name) ?? null;
8715
+ }
8716
+ if (!localRules) {
8717
+ warn(init.start, "composeLeaf(): final argument must be a local rules() map");
8718
+ return null;
8719
+ }
8720
+ const composing = composingTrivia(elements);
8721
+ const carried = [];
8722
+ for (let i = 0; i < elements.length - 1; i++) {
8723
+ const r = argPieces(elements[i], `composeLeaf${init.start}_${i}`, composing);
8724
+ if (!r) {
8725
+ warn(init.start, `composeLeaf(): argument ${i} isn't a build-resolvable recognition grammar`);
8726
+ return null;
8727
+ }
8728
+ carried.push(...r.carried);
8729
+ }
8730
+ try {
8731
+ const localNs = nsFor(`composeLeaf${init.start}`);
8732
+ const plainLocalPiece = compileLinkable([...localRules], localNs, { ...composing ? { trivia: composing } : {}, recovery });
8733
+ if (!plainLocalPiece) {
8734
+ warn(init.start, "composeLeaf(): local rules could not be statically compiled");
8735
+ return null;
8736
+ }
8737
+ const recognitionPieces = grammarCoverage ? materializeLeafCoverage(carried, localRules, localNs, composing, plainLocalPiece.hasDirectBuilders === true) : materializeCarried(carried, composing, plainLocalPiece.hasDirectBuilders === true);
8738
+ if (!recognitionPieces) {
8739
+ warn(init.start, "composeLeaf(): coverage needs re-lowerable recognition IR");
8740
+ return null;
8741
+ }
8742
+ const importedRecognitionPieces = grammarCoverage ? recognitionPieces.slice(0, -1) : recognitionPieces;
8743
+ if (importedRecognitionPieces.some((piece) => piece.hasDirectBuilders !== false || piece.isRecognitionOnly !== true)) {
8744
+ warn(init.start, "composeLeaf(): every pre-final grammar must explicitly prove recognition-only");
8745
+ return null;
8746
+ }
8747
+ const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
8748
+ return { replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) };
8749
+ } catch (e) {
8750
+ warn(init.start, `composeLeaf(): ${e.message}`);
8751
+ return null;
8752
+ }
8753
+ };
7829
8754
  const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
7830
8755
  const refNames = /* @__PURE__ */ new Set();
7831
8756
  for (const stmt of body) {
@@ -7892,7 +8817,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7892
8817
  const refEntry = scope.get(varName);
7893
8818
  const refCombi = refEntry?.combi ?? null;
7894
8819
  if (refCombi) {
7895
- const compiled2 = compile(refCombi, void 0, { recovery });
8820
+ const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage });
7896
8821
  if (compiled2.inlineExpression === null) {
7897
8822
  warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
7898
8823
  continue;
@@ -7906,7 +8831,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7906
8831
  if (!compiledRules) continue;
7907
8832
  localRuleMaps.set(varName, compiledRules.ruleMap);
7908
8833
  if (compiledRules.trivia) localGrammarTrivia.set(varName, compiledRules.trivia);
7909
- let replacement = compiledRules.replacement;
8834
+ let replacement = withCoverageDefinitions(
8835
+ compiledRules.replacement,
8836
+ compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(compiledRules.replacement)
8837
+ );
7910
8838
  if (exportPrefix) {
7911
8839
  const ns = nsFor(varName);
7912
8840
  const pieces = compileLinkable([...compiledRules.ruleMap], ns, { recovery });
@@ -7920,11 +8848,22 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7920
8848
  }
7921
8849
  if (isComposeCall(init)) {
7922
8850
  const fused = compileComposeCall(init);
7923
- if (!fused) continue;
8851
+ if (!fused) {
8852
+ runtimeComposeFallback = true;
8853
+ continue;
8854
+ }
7924
8855
  localComposedCarried.set(varName, fused.carried);
7925
8856
  if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
7926
8857
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
7927
- replacements.push({ start: init.start, end: init.end, replacement });
8858
+ replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) });
8859
+ continue;
8860
+ }
8861
+ if (isComposeLeafCall(init)) {
8862
+ const fused = compileComposeLeafCall(init);
8863
+ if (!fused) {
8864
+ throw new Error(`${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden`);
8865
+ }
8866
+ replacements.push({ start: init.start, end: init.end, replacement: fused.replacement });
7928
8867
  continue;
7929
8868
  }
7930
8869
  const mapFnSources = [];
@@ -7945,7 +8884,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7945
8884
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
7946
8885
  continue;
7947
8886
  }
7948
- const compiled = compile(parser2, void 0, { recovery });
8887
+ const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage });
7949
8888
  if (compiled.inlineExpression === null) {
7950
8889
  warn(init.start, `"${varName}" couldn't be inlined (likely closes over a runtime value)`);
7951
8890
  continue;
@@ -8020,7 +8959,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
8020
8959
  ms.overwrite(imp.start, imp.end, stripped);
8021
8960
  }
8022
8961
  }
8023
- for (const { start, end, replacement } of [...replacements].sort((a, b) => b.start - a.start)) {
8962
+ for (const { start, end, replacement } of (runtimeComposeFallback ? [] : replacements).sort((a, b) => b.start - a.start)) {
8024
8963
  ms.overwrite(start, end, replacement);
8025
8964
  }
8026
8965
  const unlowered = endLoweringCapture();