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
@@ -35,8 +35,9 @@ __export(plugin_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(plugin_exports);
37
37
  var fs = __toESM(require("node:fs"), 1);
38
+ var path = __toESM(require("node:path"), 1);
38
39
  var import_unplugin = require("unplugin");
39
- var import_oxc_parser = require("oxc-parser");
40
+ var import_oxc_parser2 = require("oxc-parser");
40
41
  var import_oxc_resolver = require("oxc-resolver");
41
42
  var import_magic_string = __toESM(require("magic-string"), 1);
42
43
 
@@ -101,6 +102,7 @@ function matchesEmpty(p, seen = /* @__PURE__ */ new Set()) {
101
102
  case "label":
102
103
  case "trivia":
103
104
  case "token":
105
+ case "leaf":
104
106
  case "expect":
105
107
  case "withCtx":
106
108
  case "node":
@@ -164,6 +166,7 @@ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
164
166
  case "label":
165
167
  case "trivia":
166
168
  case "token":
169
+ case "leaf":
167
170
  case "node":
168
171
  case "grammar":
169
172
  case "expect":
@@ -269,10 +272,10 @@ function beginCstNodeCapture(ctx) {
269
272
  }
270
273
  function finishCstBuf(buf) {
271
274
  if (!buf) return { children: EMPTY, rawChildren: EMPTY, triviaLog: EMPTY_TL };
272
- const children = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
275
+ const children2 = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY);
273
276
  const rawChildren = buf.raw ?? (buf.rawSingle !== void 0 ? [buf.rawSingle] : EMPTY);
274
277
  const triviaLog = buf.tl ?? EMPTY_TL;
275
- return { children, rawChildren, triviaLog };
278
+ return { children: children2, rawChildren, triviaLog };
276
279
  }
277
280
  function endCstNodeCapture(ctx, saved) {
278
281
  const materialized = finishCstBuf(ctx._cstBuf);
@@ -284,8 +287,8 @@ function endCstNodeCapture(ctx, saved) {
284
287
  ctx.captureTrivia = saved.cap;
285
288
  return materialized;
286
289
  }
287
- function pushCstLeaf(ctx, leaf) {
288
- pushCstChild(ctx, leaf, leaf);
290
+ function pushCstLeaf(ctx, leaf2) {
291
+ pushCstChild(ctx, leaf2, leaf2);
289
292
  }
290
293
  function pushCstChild(ctx, built, rawEntry) {
291
294
  const b = ctx._cstBuf;
@@ -1015,10 +1018,13 @@ function deriveExpected(c) {
1015
1018
  return def.parsers.flatMap(deriveExpected);
1016
1019
  case "sequence":
1017
1020
  return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
1021
+ case "attempt":
1022
+ return deriveExpected(def.parser);
1018
1023
  case "node":
1019
1024
  case "grammar":
1020
1025
  case "trivia":
1021
1026
  case "token":
1027
+ case "leaf":
1022
1028
  case "optional":
1023
1029
  case "many":
1024
1030
  case "oneOrMore":
@@ -1509,6 +1515,12 @@ function scanTrivia(input, cur, ctx) {
1509
1515
  const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
1510
1516
  return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
1511
1517
  }
1518
+ function consumeTrivia(input, cur, ctx) {
1519
+ if (!needsDeferredTriviaCommit(ctx)) return advanceTrivia(input, cur, ctx);
1520
+ const scan = scanTrivia(input, cur, ctx);
1521
+ scan.commit();
1522
+ return scan.end;
1523
+ }
1512
1524
  function fastTriviaScanner(trivia2) {
1513
1525
  const cached = fastTriviaCache.get(trivia2);
1514
1526
  if (cached !== void 0) return cached;
@@ -1787,6 +1799,24 @@ function sequence(...parsers) {
1787
1799
  };
1788
1800
  }
1789
1801
 
1802
+ // src/combinators/attempt.ts
1803
+ function attempt(parser2) {
1804
+ return {
1805
+ _tag: "attempt",
1806
+ _meta: parser2._meta,
1807
+ _def: { tag: "attempt", parser: parser2 },
1808
+ parse(input, pos, ctx) {
1809
+ const mark = saveCstMark(ctx);
1810
+ const log = ctx._triviaLog?.length;
1811
+ const result = parser2.parse(input, pos, ctx);
1812
+ if (result.ok) return result;
1813
+ rollbackCstCapture(ctx, mark);
1814
+ if (log !== void 0 && ctx._triviaLog) ctx._triviaLog.length = log;
1815
+ return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
1816
+ }
1817
+ };
1818
+ }
1819
+
1790
1820
  // src/combinators/repeat.ts
1791
1821
  function repItem(combinator, input, cur, ctx) {
1792
1822
  const mark = saveTriviaMark(ctx);
@@ -1962,19 +1992,19 @@ function sepBy(combinator, separator) {
1962
1992
  sepPos = advanceTrivia(input, cur, ctx);
1963
1993
  }
1964
1994
  }
1965
- const sep = separator.parse(input, sepPos, ctx);
1966
- if (!sep.ok) {
1995
+ const sep2 = separator.parse(input, sepPos, ctx);
1996
+ if (!sep2.ok) {
1967
1997
  rollbackTrivia(ctx, loopMark);
1968
1998
  break;
1969
1999
  }
1970
- let nextPos = sep.span.end;
2000
+ let nextPos = sep2.span.end;
1971
2001
  if (ctx.trivia) {
1972
2002
  if (needsDeferredTriviaCommit(ctx)) {
1973
- const scan = scanTrivia(input, sep.span.end, ctx);
2003
+ const scan = scanTrivia(input, sep2.span.end, ctx);
1974
2004
  scan.commit();
1975
2005
  nextPos = scan.end;
1976
2006
  } else {
1977
- nextPos = advanceTrivia(input, sep.span.end, ctx);
2007
+ nextPos = advanceTrivia(input, sep2.span.end, ctx);
1978
2008
  }
1979
2009
  }
1980
2010
  const next = combinator.parse(input, nextPos, ctx);
@@ -2059,10 +2089,14 @@ function markUnusedValues(root) {
2059
2089
  case "label":
2060
2090
  case "trivia":
2061
2091
  case "token":
2092
+ case "attempt":
2062
2093
  case "withCtx":
2063
2094
  case "expect":
2064
2095
  visit(def.parser, consumed);
2065
2096
  return;
2097
+ case "leaf":
2098
+ visit(def.parser, true);
2099
+ return;
2066
2100
  case "lazy":
2067
2101
  return;
2068
2102
  default:
@@ -2185,6 +2219,20 @@ function confirmedBuildArity(src) {
2185
2219
  if (/\barguments\b/.test(s)) return null;
2186
2220
  return parts.length;
2187
2221
  }
2222
+ function buildReadsChildren(def) {
2223
+ if (!def.build) return true;
2224
+ const src = def.buildSrc ?? def.build.toString();
2225
+ const arity = confirmedBuildArity(src);
2226
+ if (arity === null) return true;
2227
+ return arity >= 1;
2228
+ }
2229
+ function buildReadsRaw(def) {
2230
+ if (!def.build) return true;
2231
+ const src = def.buildSrc ?? def.build.toString();
2232
+ const arity = confirmedBuildArity(src);
2233
+ if (arity === null) return true;
2234
+ return arity >= 4;
2235
+ }
2188
2236
  function buildReadsTrivia(def) {
2189
2237
  if (!def.build) return true;
2190
2238
  const src = def.buildSrc ?? def.build.toString();
@@ -2240,9 +2288,11 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
2240
2288
  case "many":
2241
2289
  case "oneOrMore":
2242
2290
  case "optional":
2291
+ case "attempt":
2243
2292
  case "transform":
2244
2293
  case "trivia":
2245
2294
  case "token":
2295
+ case "leaf":
2246
2296
  case "label":
2247
2297
  case "expect":
2248
2298
  case "withCtx":
@@ -2275,9 +2325,11 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
2275
2325
  case "keywords":
2276
2326
  case "guard":
2277
2327
  case "token":
2328
+ case "leaf":
2278
2329
  return false;
2279
2330
  // Transparent single-child wrappers: recurse.
2280
2331
  case "optional":
2332
+ case "attempt":
2281
2333
  case "transform":
2282
2334
  case "trivia":
2283
2335
  case "label":
@@ -2301,6 +2353,54 @@ function parserHasTriviaSite(p, seen = /* @__PURE__ */ new Set()) {
2301
2353
  return true;
2302
2354
  }
2303
2355
  }
2356
+ function parserEnablesTriviaCapture(p, seen = /* @__PURE__ */ new Set()) {
2357
+ if (seen.has(p)) return false;
2358
+ seen.add(p);
2359
+ const d = p._def;
2360
+ switch (d.tag) {
2361
+ case "grammar":
2362
+ return d.captureTrivia === true || parserEnablesTriviaCapture(d.parser, seen);
2363
+ case "node":
2364
+ return false;
2365
+ case "sequence":
2366
+ case "choice":
2367
+ return d.parsers.some((x) => parserEnablesTriviaCapture(x, seen));
2368
+ case "sepBy":
2369
+ return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.separator, seen);
2370
+ case "skip":
2371
+ return parserEnablesTriviaCapture(d.main, seen) || parserEnablesTriviaCapture(d.skipped, seen);
2372
+ case "scanTo":
2373
+ return parserEnablesTriviaCapture(d.sentinel, seen) || d.skip.some((x) => parserEnablesTriviaCapture(x, seen));
2374
+ case "recover":
2375
+ return parserEnablesTriviaCapture(d.parser, seen) || parserEnablesTriviaCapture(d.sentinel, seen);
2376
+ case "many":
2377
+ case "oneOrMore":
2378
+ case "optional":
2379
+ case "attempt":
2380
+ case "transform":
2381
+ case "trivia":
2382
+ case "token":
2383
+ case "label":
2384
+ case "field":
2385
+ case "expect":
2386
+ case "withCtx":
2387
+ case "not":
2388
+ return parserEnablesTriviaCapture(d.parser, seen);
2389
+ // leaf() clears the inner CST/trivia collector and publishes only its own
2390
+ // terminal result. An inner parser() capture cannot reach this node.
2391
+ case "leaf":
2392
+ return false;
2393
+ case "lazy": {
2394
+ try {
2395
+ return parserEnablesTriviaCapture(d.thunk(), seen);
2396
+ } catch {
2397
+ return false;
2398
+ }
2399
+ }
2400
+ default:
2401
+ return false;
2402
+ }
2403
+ }
2304
2404
  function buildFieldMap(captures) {
2305
2405
  if (!captures?.length) return void 0;
2306
2406
  const out = {};
@@ -2338,11 +2438,13 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2338
2438
  };
2339
2439
  const unwrap = opts?.unwrap === true;
2340
2440
  const collapse = opts?.collapse === true;
2441
+ const captureTrivia = opts?.captureTrivia === true;
2442
+ const trailingTrivia = opts?.trailingTrivia === true;
2341
2443
  if (unwrap && collapse) {
2342
2444
  throw new Error("node() options cannot set both unwrap and collapse");
2343
2445
  }
2344
- const def = unwrap || collapse ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {} } : baseDef;
2345
- const capturesTrivia = build ? buildReadsTrivia(def) : true;
2446
+ const def = unwrap || collapse || captureTrivia || trailingTrivia ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {} } : baseDef;
2447
+ const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : true);
2346
2448
  const clonesState = build ? buildReadsState(def) : true;
2347
2449
  const capturesFields = parserHasOwnFields(combinator) && (build ? buildReadsFields(def) : true);
2348
2450
  return {
@@ -2358,16 +2460,21 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
2358
2460
  ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
2359
2461
  }
2360
2462
  if (!capturesTrivia) ctx.captureTrivia = false;
2361
- const r = combinator.parse(input, pos, ctx);
2463
+ let r = combinator.parse(input, pos, ctx);
2464
+ if (r.ok && trailingTrivia && ctx.trivia) {
2465
+ const end = consumeTrivia(input, r.span.end, ctx);
2466
+ r = { ...r, span: { start: r.span.start, end } };
2467
+ }
2362
2468
  const fields = capturesFields ? buildFieldMap(ctx._fields) : void 0;
2363
2469
  ctx._fields = savedFields;
2364
2470
  ctx._triviaCaptureMask = savedMask;
2365
- const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
2471
+ const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
2366
2472
  if (!r.ok) return r;
2367
2473
  const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
2368
2474
  const nodeType = def.type ?? missingInferredType();
2369
- 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 };
2370
- const rawEntry = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
2475
+ const cstOutput = ctx.build?._parsemanCstOutput === true;
2476
+ 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 };
2477
+ 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 };
2371
2478
  if (saved.buf !== void 0 || saved.ch !== void 0) {
2372
2479
  pushCstChild(ctx, built, rawEntry);
2373
2480
  }
@@ -2484,6 +2591,7 @@ function parser(opts, root) {
2484
2591
  parser: root,
2485
2592
  triviaParser: clearTrivia ? void 0 : opts.trivia ?? void 0,
2486
2593
  clearTrivia,
2594
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
2487
2595
  trackLines: opts.trackLines ?? false
2488
2596
  },
2489
2597
  parse(input, pos, _ctx) {
@@ -2571,6 +2679,141 @@ function token(root) {
2571
2679
  }
2572
2680
  };
2573
2681
  }
2682
+ function leaf(root, fn) {
2683
+ const meta = {
2684
+ firstSet: root._meta.firstSet,
2685
+ canMatchNewline: root._meta.canMatchNewline,
2686
+ isTrivia: false
2687
+ };
2688
+ return {
2689
+ _tag: "leaf",
2690
+ _meta: meta,
2691
+ _def: { tag: "leaf", parser: root, fn },
2692
+ parse(input, pos, ctx) {
2693
+ const savedBuf = ctx._cstBuf;
2694
+ const savedChildren = ctx._cstChildren;
2695
+ const savedLeaves = ctx._cstLeaves;
2696
+ const savedRaw = ctx._cstRawChildren;
2697
+ const savedTriviaLog = ctx._cstTriviaLog;
2698
+ const savedOuterTriviaLog = ctx._triviaLog;
2699
+ const wasCapturing = cstCaptureActive(ctx);
2700
+ ctx._cstBuf = void 0;
2701
+ ctx._cstChildren = void 0;
2702
+ ctx._cstLeaves = void 0;
2703
+ ctx._cstRawChildren = void 0;
2704
+ ctx._cstTriviaLog = void 0;
2705
+ delete ctx._triviaLog;
2706
+ let result;
2707
+ try {
2708
+ result = root.parse(input, pos, ctx);
2709
+ } finally {
2710
+ ctx._cstBuf = savedBuf;
2711
+ ctx._cstChildren = savedChildren;
2712
+ ctx._cstLeaves = savedLeaves;
2713
+ ctx._cstRawChildren = savedRaw;
2714
+ ctx._cstTriviaLog = savedTriviaLog;
2715
+ if (savedOuterTriviaLog === void 0) delete ctx._triviaLog;
2716
+ else ctx._triviaLog = savedOuterTriviaLog;
2717
+ }
2718
+ if (!result.ok) return result;
2719
+ const value = fn(result.value, result.span);
2720
+ if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span: result.span });
2721
+ return { ok: true, value, span: result.span };
2722
+ }
2723
+ };
2724
+ }
2725
+
2726
+ // src/compiler/grammar-coverage-ids.ts
2727
+ function children(def, winners) {
2728
+ switch (def.tag) {
2729
+ case "sequence":
2730
+ case "choice":
2731
+ return def.parsers;
2732
+ case "many":
2733
+ case "oneOrMore":
2734
+ case "optional":
2735
+ case "attempt":
2736
+ case "transform":
2737
+ case "trivia":
2738
+ case "token":
2739
+ case "leaf":
2740
+ case "label":
2741
+ case "field":
2742
+ case "grammar":
2743
+ case "not":
2744
+ case "node":
2745
+ case "guard":
2746
+ case "withCtx":
2747
+ case "recover":
2748
+ case "expect":
2749
+ return "parser" in def ? [def.parser] : [];
2750
+ case "sepBy":
2751
+ return [def.parser, def.separator];
2752
+ case "skip":
2753
+ return [def.main, def.skipped];
2754
+ case "scanTo":
2755
+ return [def.sentinel, ...def.skip];
2756
+ case "lazy": {
2757
+ let resolved;
2758
+ try {
2759
+ resolved = def.thunk();
2760
+ } catch {
2761
+ return [];
2762
+ }
2763
+ const name = resolved._ruleName;
2764
+ return name && winners?.[name] ? [winners[name]] : [resolved];
2765
+ }
2766
+ default:
2767
+ return [];
2768
+ }
2769
+ }
2770
+ function buildGrammarPlan(entry, winners) {
2771
+ const definitions = /* @__PURE__ */ new Map();
2772
+ const choices = /* @__PURE__ */ new WeakMap();
2773
+ const attempts = /* @__PURE__ */ new WeakMap();
2774
+ const labels = /* @__PURE__ */ new WeakMap();
2775
+ const rules2 = /* @__PURE__ */ new WeakMap();
2776
+ const winnerNames = /* @__PURE__ */ new Map();
2777
+ if (winners) {
2778
+ for (const [name, winner] of Object.entries(winners)) {
2779
+ winnerNames.set(winner, name);
2780
+ if (winner._def.tag === "lazy") {
2781
+ try {
2782
+ winnerNames.set(winner._def.thunk(), name);
2783
+ } catch {
2784
+ }
2785
+ }
2786
+ }
2787
+ }
2788
+ const seen = /* @__PURE__ */ new Set();
2789
+ const visit = (parser2, path2) => {
2790
+ if (seen.has(parser2)) return;
2791
+ seen.add(parser2);
2792
+ const rule = winnerNames.get(parser2) ?? parser2._ruleName;
2793
+ if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
2794
+ const id = `rule:${rule}`;
2795
+ definitions.set(id, { id, kind: "rule" });
2796
+ rules2.set(parser2, id);
2797
+ }
2798
+ if (parser2._def.tag === "choice") {
2799
+ const ids = parser2._def.parsers.map((_, index) => `choice:${path2}/arm:${index}`);
2800
+ choices.set(parser2, ids);
2801
+ ids.forEach((id) => definitions.set(id, { id, kind: "choice-arm" }));
2802
+ }
2803
+ if (parser2._def.tag === "attempt") attempts.set(parser2, `attempt:${path2}`);
2804
+ if (parser2._def.tag === "label") {
2805
+ const id = `label:${path2}`;
2806
+ labels.set(parser2, [id]);
2807
+ definitions.set(id, { id, kind: "label" });
2808
+ }
2809
+ children(parser2._def, winners).forEach((child, index) => visit(child, `${path2}/${parser2._def.tag}:${index}`));
2810
+ };
2811
+ const roots = Array.isArray(entry) ? entry : [entry];
2812
+ for (const root of roots) {
2813
+ visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
2814
+ }
2815
+ return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, attempts, labels, rules: rules2 };
2816
+ }
2574
2817
 
2575
2818
  // src/compiler/scannable-run.ts
2576
2819
  var BACKSLASH = 92;
@@ -3620,7 +3863,7 @@ function scanBranchLabeled(shape, kindIndex, mint) {
3620
3863
  ` if (${m.end} > _e) {`,
3621
3864
  ` if (_cap) {`,
3622
3865
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
3623
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << kindIndex}))) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
3866
+ ` 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})`,
3624
3867
  ` }`,
3625
3868
  ` _e = ${m.end}`,
3626
3869
  ` continue`,
@@ -3676,7 +3919,7 @@ function analyzeTriviaFastPath(trivia2) {
3676
3919
  var CAP_RECORD = [
3677
3920
  ` if (_cap && _e > _pos) {`,
3678
3921
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
3679
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
3922
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
3680
3923
  ` }`
3681
3924
  ].join("\n");
3682
3925
  function composeFastLoop(shapes) {
@@ -3736,7 +3979,7 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
3736
3979
  ` const _ce = _e + _m${i}[0].length`,
3737
3980
  ` if (_cap) {`,
3738
3981
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3739
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
3982
+ ` 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})`,
3740
3983
  ` }`,
3741
3984
  ` _e = _ce`,
3742
3985
  ` _matched = true`,
@@ -3774,7 +4017,7 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
3774
4017
  ` const _ce = _r${i}.span.end`,
3775
4018
  ` if (_cap) {`,
3776
4019
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, _ce, ${k})`,
3777
- ` if (_ctx._cstTriviaLog !== undefined && (_ctx._triviaCaptureMask === undefined || (_ctx._triviaCaptureMask & ${1 << k}))) _ctx._cstTriviaLog.push(_e, _ce, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${k})`,
4020
+ ` 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})`,
3778
4021
  ` }`,
3779
4022
  ` _e = _ce`,
3780
4023
  ` _matched = true`,
@@ -3963,21 +4206,23 @@ function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
3963
4206
  }
3964
4207
  function failBody(ctx, expected, posExpr) {
3965
4208
  const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
4209
+ const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
3966
4210
  if (ctx.failLabel) {
3967
- if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3968
- return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
4211
+ if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4212
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; ${trace}break ${ctx.failLabel} }`;
3969
4213
  }
3970
- return probe + failReturn(expected, posExpr);
4214
+ return probe + trace + failReturn(expected, posExpr);
3971
4215
  }
3972
4216
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
3973
4217
  const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
4218
+ const trace = ctx.activeCoverageRuleId === void 0 ? "" : `_ctx._grammarTrace?.write({ id: ${JSON.stringify(ctx.activeCoverageRuleId)}, phase: 'failure', offset: ${posExpr} }); `;
3974
4219
  if (ctx.failLabel) {
3975
- if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
4220
+ if (!ctx.recordFail) return probe ? `{ ${probe}${trace}break ${ctx.failLabel} }` : trace ? `{ ${trace}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3976
4221
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
3977
- return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
4222
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; ${trace}break ${ctx.failLabel} }`;
3978
4223
  }
3979
- if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
3980
- return probe + failReturnArr(expectedArr, posExpr);
4224
+ if (!constant) return `${probe}${trace}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
4225
+ return probe + trace + failReturnArr(expectedArr, posExpr);
3981
4226
  }
3982
4227
  function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
3983
4228
  return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
@@ -4012,7 +4257,7 @@ var NAMED_FN_END = "_pfEnd";
4012
4257
  function namedFnPrelude() {
4013
4258
  return [`const ${NAMED_FN_FAIL} = {}`, `let ${NAMED_FN_END}`];
4014
4259
  }
4015
- function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
4260
+ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar, failureRuleId) {
4016
4261
  ctx.namedFnDecls.push([
4017
4262
  `function ${fnName}(input, _pos, _ctx) {`,
4018
4263
  ` _pfail: {`,
@@ -4020,6 +4265,7 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
4020
4265
  ` ${NAMED_FN_END} = ${endVar}`,
4021
4266
  ` return ${valueVar}`,
4022
4267
  ` }`,
4268
+ ...failureRuleId === void 0 ? [] : [` _ctx._grammarTrace?.write({ id: ${JSON.stringify(failureRuleId)}, phase: 'failure', offset: _ctx._fe ?? _pos })`],
4023
4269
  ` return ${NAMED_FN_FAIL}`,
4024
4270
  `}`
4025
4271
  ].join("\n"));
@@ -4125,7 +4371,7 @@ function ensureTriviaFn(ctx) {
4125
4371
  ` }`,
4126
4372
  ` if (_cap && _e > _pos) {`,
4127
4373
  ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
4128
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
4374
+ ` if (_ctx._cstTriviaLog !== undefined && _ctx.captureTrivia) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
4129
4375
  ` }`,
4130
4376
  ` return _e`,
4131
4377
  `}`
@@ -4181,6 +4427,7 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
4181
4427
  case "not":
4182
4428
  case "trivia":
4183
4429
  case "token":
4430
+ case "leaf":
4184
4431
  case "scanTo":
4185
4432
  case "unknown":
4186
4433
  return false;
@@ -4192,6 +4439,8 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
4192
4439
  // nothing committed remains.
4193
4440
  case "choice":
4194
4441
  return false;
4442
+ case "attempt":
4443
+ return false;
4195
4444
  // optional never fails; many/oneOrMore only "fail" with zero captured items.
4196
4445
  case "optional":
4197
4446
  case "many":
@@ -4244,6 +4493,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
4244
4493
  case "keywords":
4245
4494
  case "node":
4246
4495
  case "token":
4496
+ case "leaf":
4247
4497
  return true;
4248
4498
  case "not":
4249
4499
  case "guard":
@@ -4258,6 +4508,7 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
4258
4508
  case "many":
4259
4509
  case "oneOrMore":
4260
4510
  case "optional":
4511
+ case "attempt":
4261
4512
  case "transform":
4262
4513
  case "label":
4263
4514
  case "field":
@@ -4599,35 +4850,55 @@ function needsFirstSetGuard(p) {
4599
4850
  const fs2 = p._meta.firstSet;
4600
4851
  return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
4601
4852
  }
4602
- function emitDisjointArm(p, ctx, pos, valV, endV) {
4603
- if (failsAtStart(p)) {
4853
+ function coverageHit(ctx, id, offset = "_pos", end) {
4854
+ return id === void 0 ? [] : [
4855
+ `${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`,
4856
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'selected', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`,
4857
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${offset}${end === void 0 ? "" : `, end: ${end}`} })`
4858
+ ];
4859
+ }
4860
+ function coverageAttempt(ctx, id, offset) {
4861
+ return id === void 0 ? [] : [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'attempt', offset: ${offset} })`];
4862
+ }
4863
+ function coverageFailureBacktrack(ctx, id, failureOffset, startOffset) {
4864
+ return id === void 0 ? [] : [
4865
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'failure', offset: ${failureOffset} })`,
4866
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'backtrack', offset: ${startOffset} })`
4867
+ ];
4868
+ }
4869
+ function emitDisjointArm(p, ctx, pos, valV, endV, coverageId) {
4870
+ if (failsAtStart(p) && coverageId === void 0) {
4604
4871
  const r = emit(p, ctx, pos);
4605
- return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
4872
+ return [...coverageAttempt(ctx, coverageId, pos), ...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`, ...coverageHit(ctx, coverageId, pos, r.endVar)];
4606
4873
  }
4607
4874
  const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
4608
4875
  return [
4876
+ ...coverageAttempt(ctx, coverageId, pos),
4609
4877
  ...stmts,
4610
- ...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
4878
+ ...emitIfFail(ctx, `!${okVar}`, `${coverageFailureBacktrack(ctx, coverageId, "_ctx._fe", pos).join("; ")}; ${failArrBody(ctx, "_ctx._fx", pos, false)}`),
4611
4879
  `${ind(ctx)}${valV} = ${valVar}`,
4612
- `${ind(ctx)}${endV} = ${endVar}`
4880
+ `${ind(ctx)}${endV} = ${endVar}`,
4881
+ ...coverageHit(ctx, coverageId, pos, endVar)
4613
4882
  ];
4614
4883
  }
4615
- function emitDisjointArmGated(def, i, ctx, pos, valV, endV) {
4884
+ function emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageId) {
4616
4885
  const p = def.parsers[i];
4617
4886
  const gate = def.gates[i];
4618
- if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV);
4887
+ if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV, coverageId);
4619
4888
  const gateIdx = ctx.mapFns.length;
4620
4889
  ctx.mapFns.push(gate);
4621
4890
  ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4622
4891
  const gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4623
4892
  const stmts = [`${ind(ctx)}if (${gateCond}) {`];
4624
4893
  ctx.indent++;
4625
- stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
4894
+ stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV, coverageId));
4626
4895
  ctx.indent--;
4627
4896
  stmts.push(`${ind(ctx)}} else { ${failArrBody(ctx, deriveExpectedArr([p]), pos)} }`);
4628
4897
  return stmts;
4629
4898
  }
4630
- function emitChoice(def, ctx, pos) {
4899
+ function emitChoice(parser2, def, ctx, pos) {
4900
+ const coverageIds = ctx.coverage?.plan.choices.get(parser2);
4901
+ const coverageBase = coverageIds?.[0]?.slice(0, -1);
4631
4902
  const allExpected = deriveExpectedArr(def.parsers);
4632
4903
  if (def.disjoint) {
4633
4904
  const codeV = v(ctx, "_code");
@@ -4646,7 +4917,7 @@ function emitChoice(def, ctx, pos) {
4646
4917
  stmts.push(`${ind(ctx)}{`);
4647
4918
  ctx.indent++;
4648
4919
  stmts.push(
4649
- ...emitDisjointArmGated(def, i, ctx, pos, valV, endV),
4920
+ ...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]),
4650
4921
  `${ind(ctx)}break`
4651
4922
  );
4652
4923
  ctx.indent--;
@@ -4665,16 +4936,16 @@ function emitChoice(def, ctx, pos) {
4665
4936
  first = false;
4666
4937
  stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
4667
4938
  ctx.indent++;
4668
- stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV));
4939
+ stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV, coverageIds?.[i]));
4669
4940
  ctx.indent--;
4670
4941
  stmts.push(`${ind(ctx)}}`);
4671
4942
  }
4672
4943
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
4673
4944
  return { stmts, valueVar: valV, endVar: endV };
4674
4945
  }
4675
- return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
4946
+ return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos, coverageBase);
4676
4947
  }
4677
- function emitGreedyClassify(def, superIndex, ctx, pos) {
4948
+ function emitGreedyClassify(def, superIndex, ctx, pos, coverageBase) {
4678
4949
  const superParser = def.parsers[superIndex];
4679
4950
  const regexDef = getCoreRegexDef(superParser);
4680
4951
  const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
@@ -4690,10 +4961,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4690
4961
  const endV = v(ctx, "_ge");
4691
4962
  const valV = v(ctx, "_gcv");
4692
4963
  const regexExpected = JSON.stringify(deriveExpected(superParser));
4964
+ const superCoverageId = coverageBase === void 0 ? void 0 : `${coverageBase}${superIndex}`;
4693
4965
  const stmts = [
4966
+ ...coverageAttempt(ctx, superCoverageId, pos),
4694
4967
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
4695
4968
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
4696
- ...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
4969
+ ...emitIfFail(ctx, `${matchV} === null`, `${coverageFailureBacktrack(ctx, superCoverageId, pos, pos).join("; ")}; ${failArrBody(ctx, regexExpected, pos)}`),
4697
4970
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
4698
4971
  `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
4699
4972
  `${ind(ctx)}let ${valV}`
@@ -4709,8 +4982,9 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4709
4982
  stmts.push(`${ind(ctx)}${kw} (${wordV} === ${JSON.stringify(litVal)}) {`);
4710
4983
  ctx.indent++;
4711
4984
  const tR = emitTransformChain(p, JSON.stringify(litVal), endV, pos, ctx);
4985
+ stmts.push(...coverageFailureBacktrack(ctx, superCoverageId, pos, pos));
4712
4986
  stmts.push(...emitLeafCapture(ctx, JSON.stringify(litVal), pos, endV));
4713
- stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`);
4987
+ stmts.push(...tR.stmts, `${ind(ctx)}${valV} = ${tR.valueVar}`, ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endV));
4714
4988
  ctx.indent--;
4715
4989
  stmts.push(`${ind(ctx)}}`);
4716
4990
  }
@@ -4718,12 +4992,12 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
4718
4992
  const regexKw = first ? "if" : "else";
4719
4993
  stmts.push(`${ind(ctx)}${regexKw} {`);
4720
4994
  ctx.indent++;
4721
- stmts.push(...emitLeafCapture(ctx, wordV, pos, endV), ...rR.stmts, `${ind(ctx)}${valV} = ${rR.valueVar}`);
4995
+ 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));
4722
4996
  ctx.indent--;
4723
4997
  stmts.push(`${ind(ctx)}}`);
4724
4998
  return { stmts, valueVar: valV, endVar: endV };
4725
4999
  }
4726
- function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
5000
+ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos, coverageBase) {
4727
5001
  const valV = v(ctx, "_llv");
4728
5002
  const endV = v(ctx, "_lle");
4729
5003
  const stmts = [`${ind(ctx)}let ${valV}, ${endV} = ${pos}`];
@@ -4740,10 +5014,12 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4740
5014
  ctx.indent++;
4741
5015
  const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
4742
5016
  stmts.push(
5017
+ ...coverageAttempt(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos),
4743
5018
  ...emitLeafCapture(ctx, JSON.stringify(litVal), pos, litEnd),
4744
5019
  ...tR.stmts,
4745
5020
  `${ind(ctx)}${valV} = ${tR.valueVar}`,
4746
- `${ind(ctx)}${endV} = ${litEnd}`
5021
+ `${ind(ctx)}${endV} = ${litEnd}`,
5022
+ ...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${idx}`, pos, litEnd)
4747
5023
  );
4748
5024
  ctx.indent--;
4749
5025
  stmts.push(`${ind(ctx)}}`);
@@ -4751,16 +5027,18 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
4751
5027
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
4752
5028
  return { stmts, valueVar: valV, endVar: endV };
4753
5029
  }
4754
- function emitFirstMatch(def, ctx, pos) {
5030
+ function emitFirstMatch(def, ctx, pos, coverageBase) {
4755
5031
  const resValV = v(ctx, "_crv");
4756
5032
  const resEndV = v(ctx, "_cre");
4757
5033
  const resOkV = v(ctx, "_crok");
4758
5034
  const codeV = v(ctx, "_chcode");
4759
5035
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
5036
+ const autoRejectedV = coverageBase === void 0 ? void 0 : v(ctx, "_carej");
4760
5037
  const ind0 = ind(ctx);
4761
5038
  const stmts = [
4762
5039
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
4763
5040
  `${ind0}let ${slots.join(", ")}`,
5041
+ ...autoRejectedV === void 0 ? [] : [`${ind0}const ${autoRejectedV} = []`],
4764
5042
  `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
4765
5043
  ];
4766
5044
  for (let i = 0; i < def.parsers.length; i++) {
@@ -4796,6 +5074,7 @@ function emitFirstMatch(def, ctx, pos) {
4796
5074
  stmts.push(`${ind0}if (${skipCond}) {`);
4797
5075
  if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
4798
5076
  ctx.indent += fsGuard ? 2 : 1;
5077
+ if (coverageBase !== void 0) stmts.push(`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(`${coverageBase}${i}`)}, phase: 'attempt', offset: ${pos} })`);
4799
5078
  if (markLeaves) {
4800
5079
  stmts.push(
4801
5080
  `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
@@ -4818,13 +5097,21 @@ function emitFirstMatch(def, ctx, pos) {
4818
5097
  stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
4819
5098
  stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
4820
5099
  stmts.push(`${ind(ctx)} ${resOkV} = true`);
5100
+ if (autoRejectedV !== void 0) {
5101
+ 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} }) }`);
5102
+ }
5103
+ stmts.push(...coverageHit(ctx, coverageBase === void 0 ? void 0 : `${coverageBase}${i}`, pos, endVar));
4821
5104
  stmts.push(`${ind(ctx)} }`);
5105
+ if (autoRejectedV !== void 0) {
5106
+ stmts.push(`${ind(ctx)} else { ${autoRejectedV}.push(${JSON.stringify(`${coverageBase}${i}`)}) }`);
5107
+ }
4822
5108
  stmts.push(`${ind(ctx)}}`);
4823
- stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
5109
+ 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} });`} }`);
4824
5110
  if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
4825
5111
  } else {
4826
- stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
4827
- stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
5112
+ 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} }) }`;
5113
+ 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} })`} }`);
5114
+ 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} })`} }`);
4828
5115
  }
4829
5116
  ctx.indent -= fsGuard ? 2 : 1;
4830
5117
  if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
@@ -4834,12 +5121,12 @@ function emitFirstMatch(def, ctx, pos) {
4834
5121
  stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
4835
5122
  return { stmts, valueVar: resValV, endVar: resEndV };
4836
5123
  }
4837
- function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
5124
+ function emitNonDisjoint(def, strategy, allExpected, ctx, pos, coverageBase) {
4838
5125
  if (strategy.tag === "greedyClassify")
4839
- return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
5126
+ return emitGreedyClassify(def, strategy.superIndex, ctx, pos, coverageBase);
4840
5127
  if (strategy.tag === "literalsLongestFirst")
4841
- return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
4842
- return emitFirstMatch(def, ctx, pos);
5128
+ return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos, coverageBase);
5129
+ return emitFirstMatch(def, ctx, pos, coverageBase);
4843
5130
  }
4844
5131
  function pushMapFn(ctx, fn, src) {
4845
5132
  if (src !== null && ctx.mapFns.length === ctx.mapFnSrcs.length) {
@@ -4957,6 +5244,27 @@ function emitOptional(def, ctx, pos) {
4957
5244
  ];
4958
5245
  return { stmts, valueVar: valV, endVar: endV };
4959
5246
  }
5247
+ function emitAttempt(p, def, ctx, pos) {
5248
+ const inner = emitFallible(def.parser, ctx, pos);
5249
+ const leaves = v(ctx, "_atl");
5250
+ const raw = v(ctx, "_atr");
5251
+ const trivia2 = v(ctx, "_att");
5252
+ const log = v(ctx, "_atg");
5253
+ const fields = v(ctx, "_atf");
5254
+ const errors = v(ctx, "_ate");
5255
+ 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}`;
5256
+ const traceId = ctx.coverage?.plan.attempts.get(p);
5257
+ const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
5258
+ return {
5259
+ stmts: [
5260
+ `${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`,
5261
+ ...inner.stmts,
5262
+ ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
5263
+ ],
5264
+ valueVar: inner.valVar,
5265
+ endVar: inner.endVar
5266
+ };
5267
+ }
4960
5268
  function emitSepBy(_p, def, ctx, pos) {
4961
5269
  const arrV = v(ctx, "_arr");
4962
5270
  const curV = v(ctx, "_cur");
@@ -5202,11 +5510,11 @@ function tokenNullableRegex(def) {
5202
5510
  }
5203
5511
  if (inner.tag === "sepBy") {
5204
5512
  const item = tokenSequenceSource(inner.parser);
5205
- const sep = tokenSequenceSource(inner.separator);
5206
- if (!item || !sep) return null;
5207
- const combined = combineTokenRegexSources([item, sep]);
5513
+ const sep2 = tokenSequenceSource(inner.separator);
5514
+ if (!item || !sep2) return null;
5515
+ const combined = combineTokenRegexSources([item, sep2]);
5208
5516
  if (!combined) return null;
5209
- return { tag: "regex", source: `(?:${item.source}(?:${sep.source}${item.source})*)?`, flags: combined.flags };
5517
+ return { tag: "regex", source: `(?:${item.source}(?:${sep2.source}${item.source})*)?`, flags: combined.flags };
5210
5518
  }
5211
5519
  return null;
5212
5520
  }
@@ -5247,6 +5555,36 @@ function emitToken(def, ctx, pos) {
5247
5555
  endVar: inner.endVar
5248
5556
  };
5249
5557
  }
5558
+ function emitLeaf(def, ctx, pos) {
5559
+ const inner = emitFallible(def.parser, ctx, pos);
5560
+ const i = ind(ctx);
5561
+ const sc = v(ctx, "_leafCh");
5562
+ const sl = v(ctx, "_leafLv");
5563
+ const sr = v(ctx, "_leafRaw");
5564
+ const stl = v(ctx, "_leafTl");
5565
+ const sol = v(ctx, "_leafLog");
5566
+ const sb = v(ctx, "_leafBuf");
5567
+ const rawV = v(ctx, "_leafRawValue");
5568
+ const valV = v(ctx, "_leaf");
5569
+ const fnIdx = pushMapFn(ctx, def.fn, def.fnSrc ?? null);
5570
+ return {
5571
+ stmts: [
5572
+ `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${stl} = _ctx._cstTriviaLog, ${sol} = _ctx._triviaLog, ${sb} = _ctx._cstBuf`,
5573
+ `${i}_ctx._cstChildren = undefined; _ctx._cstLeaves = undefined; _ctx._cstRawChildren = undefined; _ctx._cstTriviaLog = undefined; _ctx._triviaLog = undefined; _ctx._cstBuf = undefined`,
5574
+ `${i}try {`,
5575
+ ...reindentStmts(inner.stmts, ctx.indent + 1).map((stmt) => stmt.replace(/^(\s*)(?:const|let)\s+/, "$1var ")),
5576
+ `${i}} finally {`,
5577
+ `${i} _ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx._cstTriviaLog = ${stl}; _ctx._triviaLog = ${sol}; _ctx._cstBuf = ${sb}`,
5578
+ `${i}}`,
5579
+ ...emitIfFail(ctx, `!${inner.okVar}`, propagateFailBody(ctx)),
5580
+ `${i}const ${rawV} = ${inner.valVar}`,
5581
+ `${i}const ${valV} = ${mfRef(ctx)}[${fnIdx}](${rawV}, { start: ${pos}, end: ${inner.endVar} })`,
5582
+ ...emitLeafCapture(ctx, valV, pos, inner.endVar)
5583
+ ],
5584
+ valueVar: valV,
5585
+ endVar: inner.endVar
5586
+ };
5587
+ }
5250
5588
  function emitNode(def, ctx, pos) {
5251
5589
  if (def.type === void 0) {
5252
5590
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
@@ -5260,17 +5598,20 @@ function emitNode(def, ctx, pos) {
5260
5598
  ctx.buildSrcs.push(def.buildSrc ?? null);
5261
5599
  }
5262
5600
  const i = ind(ctx);
5263
- const capturesTrivia = mkType !== null || !structural && buildReadsTrivia(def);
5601
+ const capturesTrivia = mkType !== null || def.captureTrivia === true || def.trailingTrivia === true || !structural && buildReadsTrivia(def);
5264
5602
  const clonesState = !structural && buildReadsState(def);
5603
+ const capturesChildren = !structural && (mkType !== null || def.unwrap || def.collapse || buildReadsChildren(def));
5604
+ const capturesRaw = !structural && (mkType !== null || buildReadsRaw(def));
5265
5605
  const hasFields = parserHasOwnFields(def.parser);
5266
5606
  const capturesFields = hasFields && !structural && buildReadsFields(def);
5607
+ const innerEnablesTriviaCapture = parserEnablesTriviaCapture(def.parser);
5267
5608
  const chV = v(ctx, "_ch");
5268
5609
  const rawV = v(ctx, "_raw");
5269
- const capTLv = structural ? v(ctx, "_ctl") : null;
5610
+ const capTLv = structural || capturesTrivia ? v(ctx, "_ctl") : null;
5270
5611
  const capSTv = structural ? v(ctx, "_cst") : null;
5271
5612
  const capFv = structural && hasFields ? v(ctx, "_cf") : null;
5272
5613
  const tlV = capturesTrivia || structural ? v(ctx, "_tl") : "_EMPTY_TL";
5273
- if (!capturesTrivia) ctx.needsEmptyTl = true;
5614
+ if (!capturesTrivia || structural) ctx.needsEmptyTl = true;
5274
5615
  const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
5275
5616
  const smk = structural ? v(ctx, "_smk") : null;
5276
5617
  if (structural) ctx.needsHostReads = true;
@@ -5279,16 +5620,21 @@ function emitNode(def, ctx, pos) {
5279
5620
  const profHoist = `${i}const ${pmV} = _ctx._pmProfile, ${recV} = ${pmV}?.phase === 'recognizer', ${capV} = ${pmV}?.phase === 'capture'`;
5280
5621
  const profileRecognizer = recV;
5281
5622
  const profileCapture = capV;
5282
- 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)))` : ""}
5283
- ${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 : []`;
5284
- const innerTl = structural ? `${capTLv} ? ${tlV} : undefined` : capturesTrivia ? tlV : "undefined";
5623
+ const directCstV = !structural && (!capturesChildren || !capturesRaw) ? v(ctx, "_dcst") : null;
5624
+ const directCstGate = directCstV === null ? "false" : `${profileCapture} || _ctx.build?._parsemanCstOutput === true`;
5625
+ const structuralCapturesTrivia = structural && (def.captureTrivia === true || def.trailingTrivia === true);
5626
+ 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)))` : ""}
5627
+ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRecognizer} ? undefined : [], ${tlV} = ${profileRecognizer} ? undefined : ${innerEnablesTriviaCapture ? "[]" : `${capTLv} ? [] : _EMPTY_TL`}` : capturesTrivia ? `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5628
+ ` : ""}${i}const ${capTLv} = !(${profileRecognizer}), ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}, ${tlV} = ${profileRecognizer} ? undefined : []` : `${directCstV ? `${i}const ${directCstV} = ${directCstGate}
5629
+ ` : ""}${i}const ${chV} = ${profileRecognizer} ? undefined : ${capturesChildren ? "[]" : `${directCstV} ? [] : undefined`}, ${rawV} = ${profileRecognizer} ? undefined : ${capturesRaw ? "[]" : `${directCstV} ? [] : undefined`}`;
5630
+ const innerTl = structural || capturesTrivia ? structural && !innerEnablesTriviaCapture ? `${capTLv} ? ${tlV} : undefined` : tlV : "undefined";
5285
5631
  const fieldsOn = structural ? capFv ?? "false" : `${profileRecognizer} ? false : ${capturesFields ? "true" : "false"}`;
5286
5632
  const sf = hasFields ? v(ctx, "_sf") : null;
5287
5633
  const fArr = hasFields ? v(ctx, "_fa") : null;
5288
5634
  const fObj = hasFields ? v(ctx, "_fields") : "undefined";
5289
- const needsTriviaFrame = parserHasTriviaSite(def.parser);
5635
+ const needsTriviaFrame = def.trailingTrivia === true || parserHasTriviaSite(def.parser);
5290
5636
  const saveTrivia = needsTriviaFrame ? `, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${smk ? `, ${smk} = _ctx._triviaCaptureMask` : ""}` : "";
5291
- 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}` : ""}` : "";
5637
+ 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}` : ""}` : "";
5292
5638
  const restoreTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}` : "";
5293
5639
  const stmts = [
5294
5640
  profHoist,
@@ -5296,8 +5642,13 @@ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRec
5296
5642
  `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren${saveTrivia}${sf ? `, ${sf} = _ctx._fields` : ""}`,
5297
5643
  `${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}${installTrivia}${sf ? `; _ctx._fields = ${fieldsOn} ? [] : undefined` : ""}`
5298
5644
  ];
5299
- const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
5645
+ const { stmts: innerStmts, okVar, endVar: innerEndVar } = emitFallible(def.parser, ctx, pos);
5646
+ const endVar = def.trailingTrivia === true && ctx.activeTrivia ? v(ctx, "_trailend") : innerEndVar;
5300
5647
  stmts.push(...innerStmts);
5648
+ if (def.trailingTrivia === true && ctx.activeTrivia) {
5649
+ const triviaFn = ensureTriviaFn(ctx);
5650
+ stmts.push(`${i}const ${endVar} = ${okVar} ? ${triviaFn}(input, ${innerEndVar}, _ctx, 1) : ${innerEndVar}`);
5651
+ }
5301
5652
  if (sf && fArr) {
5302
5653
  stmts.push(`${i}const ${fArr} = _ctx._fields`);
5303
5654
  }
@@ -5337,8 +5688,9 @@ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRec
5337
5688
  }
5338
5689
  const ndV = v(ctx, "_nd");
5339
5690
  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})`;
5340
- 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;
5341
- 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;
5691
+ const hostBuildExpr = `_ctx._pmProfile?.phase === 'host' && _ctx._pmProfile.hostCalls++, _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${fObj}, { start: ${pos}, end: ${endVar} }, ${rawV}, ${tlV}, ${stV})`;
5692
+ const ndExpr = structural ? `_ctx.build !== undefined ? (${hostBuildExpr}) : (${buildExpr})` : `_ctx.build?._parsemanCstOutput === true ? (${hostBuildExpr}) : (${buildExpr})`;
5693
+ 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;
5342
5694
  const unwrapExpr = `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})`;
5343
5695
  const collapseExpr = `${chV}.length === 1 ? ${chV}[0] : (${ndExpr})`;
5344
5696
  const finalExpr = def.unwrap ? unwrapExpr : def.collapse ? collapseExpr : hostCollapseExpr;
@@ -5346,7 +5698,7 @@ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRec
5346
5698
  `${i}const ${ndV} = (${profileRecognizer} || ${profileCapture}) ? undefined : (${finalExpr})`,
5347
5699
  `${i}if (!(${profileRecognizer})) {`,
5348
5700
  `${i} if (${sc}) ${sc}.push(${ndV})`,
5349
- `${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} } })`,
5701
+ `${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} } })`,
5350
5702
  `${i}}`
5351
5703
  );
5352
5704
  return { stmts, valueVar: ndV, endVar };
@@ -5391,14 +5743,23 @@ function emitLazy(p, def, ctx, pos) {
5391
5743
  const savedIndent = ctx.indent;
5392
5744
  const savedFailLabel = ctx.failLabel;
5393
5745
  const savedRecord = ctx.recordFail;
5746
+ const failureRuleId = p === ctx.coverage?.entry ? void 0 : ctx.coverage?.plan.rules.get(p) ?? ctx.coverage?.plan.rules.get(resolved);
5747
+ const savedActiveRule = ctx.activeCoverageRuleId;
5748
+ const savedSuppressFailure = ctx.suppressCoverageFailure;
5394
5749
  ctx.indent = 1;
5395
5750
  ctx.failLabel = "_pfail";
5396
5751
  ctx.recordFail = true;
5752
+ if (failureRuleId !== void 0) {
5753
+ ctx.activeCoverageRuleId = void 0;
5754
+ ctx.suppressCoverageFailure = true;
5755
+ }
5397
5756
  const r = emit(resolved, ctx, "_pos");
5398
5757
  ctx.indent = savedIndent;
5399
5758
  ctx.failLabel = savedFailLabel;
5400
5759
  ctx.recordFail = savedRecord;
5401
- pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
5760
+ ctx.activeCoverageRuleId = savedActiveRule;
5761
+ ctx.suppressCoverageFailure = savedSuppressFailure;
5762
+ pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar, failureRuleId);
5402
5763
  }
5403
5764
  const fnName = ctx.namedParsers.get(p);
5404
5765
  return emitNamedFnCall(ctx, fnName, pos);
@@ -5449,6 +5810,30 @@ function emitExpect(def, ctx, pos) {
5449
5810
  return { stmts, valueVar: valVar, endVar };
5450
5811
  }
5451
5812
  function emit(p, ctx, pos) {
5813
+ const dispatch = (emissionPos = pos) => {
5814
+ const savedRule = ctx.activeCoverageRuleId;
5815
+ const rule = ctx.coverage?.plan.rules.get(p);
5816
+ if (rule && p !== ctx.coverage?.entry && !ctx.suppressCoverageFailure) ctx.activeCoverageRuleId = rule;
5817
+ const result = emitDispatch(p, ctx, emissionPos);
5818
+ ctx.activeCoverageRuleId = savedRule;
5819
+ return result;
5820
+ };
5821
+ const instrument = (r) => {
5822
+ const ruleId = ctx.coverage?.plan.rules.get(p);
5823
+ const labelIds = ctx.coverage?.plan.labels.get(p);
5824
+ if (!ruleId && !labelIds) return r;
5825
+ return {
5826
+ ...r,
5827
+ stmts: [
5828
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`] : [],
5829
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: ${pos} })`] : [],
5830
+ ...r.stmts,
5831
+ ...ruleId ? [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`] : [],
5832
+ ...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(id)})`]) ?? [],
5833
+ ...labelIds?.flatMap((id) => [`${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(id)}, phase: 'success', offset: ${pos}, end: ${r.endVar} })`]) ?? []
5834
+ ]
5835
+ };
5836
+ };
5452
5837
  const usage = ctx.lazyUsage;
5453
5838
  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
5454
5839
  // overhead. A tiny shared wrapper (e.g. `optional(literal(';'))`) inlines
@@ -5465,14 +5850,14 @@ function emit(p, ctx, pos) {
5465
5850
  ctx.indent = 1;
5466
5851
  ctx.failLabel = "_pfail";
5467
5852
  ctx.recordFail = true;
5468
- const r = emitDispatch(p, ctx, "_pos");
5853
+ const r = dispatch("_pos");
5469
5854
  ctx.indent = savedIndent;
5470
5855
  ctx.failLabel = savedFailLabel;
5471
5856
  ctx.recordFail = savedRecord;
5472
5857
  pushNamedFnDecl(ctx, fnName, r.stmts, r.valueVar, r.endVar);
5473
- return emitNamedFnCall(ctx, fnName, pos);
5858
+ return instrument(emitNamedFnCall(ctx, fnName, pos));
5474
5859
  }
5475
- return emitDispatch(p, ctx, pos);
5860
+ return instrument(dispatch());
5476
5861
  }
5477
5862
  function emitDispatch(p, ctx, pos) {
5478
5863
  const def = p._def;
@@ -5486,7 +5871,9 @@ function emitDispatch(p, ctx, pos) {
5486
5871
  case "sequence":
5487
5872
  return emitSeq(def, ctx, pos);
5488
5873
  case "choice":
5489
- return emitChoice(def, ctx, pos);
5874
+ return emitChoice(p, def, ctx, pos);
5875
+ case "attempt":
5876
+ return emitAttempt(p, def, ctx, pos);
5490
5877
  case "many":
5491
5878
  case "oneOrMore":
5492
5879
  return emitMany(def, ctx, pos);
@@ -5555,6 +5942,8 @@ function emitDispatch(p, ctx, pos) {
5555
5942
  return emit(def.parser, ctx, pos);
5556
5943
  case "token":
5557
5944
  return emitToken(def, ctx, pos);
5945
+ case "leaf":
5946
+ return emitLeaf(def, ctx, pos);
5558
5947
  case "label": {
5559
5948
  const inner = emitFallible(def.parser, ctx, pos);
5560
5949
  return {
@@ -5589,10 +5978,19 @@ function emitDispatch(p, ctx, pos) {
5589
5978
  ctx.triviaKindLabels = def.triviaParser._meta.triviaKindLabels;
5590
5979
  }
5591
5980
  }
5981
+ const savedCapture = def.captureTrivia ? v(ctx, "_gcap") : null;
5592
5982
  const r = emit(def.parser, ctx, pos);
5593
5983
  ctx.activeTrivia = savedTrivia;
5594
5984
  ctx.triviaKindLabels = savedKindLabels;
5595
- return r;
5985
+ if (!savedCapture) return r;
5986
+ return {
5987
+ ...r,
5988
+ stmts: [
5989
+ `${ind(ctx)}const ${savedCapture} = _ctx.captureTrivia; _ctx.captureTrivia = true`,
5990
+ ...r.stmts,
5991
+ `${ind(ctx)}_ctx.captureTrivia = ${savedCapture}`
5992
+ ]
5993
+ };
5596
5994
  }
5597
5995
  case "not":
5598
5996
  return emitNot(def, ctx, pos);
@@ -5677,6 +6075,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5677
6075
  case "grammar":
5678
6076
  case "trivia":
5679
6077
  case "token":
6078
+ case "leaf":
5680
6079
  case "label":
5681
6080
  case "field":
5682
6081
  case "optional":
@@ -5704,16 +6103,32 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5704
6103
  return false;
5705
6104
  }
5706
6105
  }
6106
+ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
6107
+ if (seen.has(p)) return false;
6108
+ seen.add(p);
6109
+ const d = p._def;
6110
+ if (d.tag === "node" && d.build !== void 0) return true;
6111
+ if (d.tag === "lazy") {
6112
+ try {
6113
+ return hasDirectBuildDef(d.thunk(), seen);
6114
+ } catch {
6115
+ return false;
6116
+ }
6117
+ }
6118
+ return childrenOf(d).some((child) => hasDirectBuildDef(child, seen));
6119
+ }
5707
6120
  function childrenOf(def) {
5708
6121
  switch (def.tag) {
5709
6122
  case "sequence":
5710
6123
  case "choice":
5711
6124
  return def.parsers;
5712
6125
  case "many":
6126
+ case "attempt":
5713
6127
  case "optional":
5714
6128
  case "transform":
5715
6129
  case "trivia":
5716
6130
  case "token":
6131
+ case "leaf":
5717
6132
  case "label":
5718
6133
  case "field":
5719
6134
  case "grammar":
@@ -5888,10 +6303,25 @@ function compile(combinator, mapFnSources, opts) {
5888
6303
  namedFnDecls: [],
5889
6304
  capturing: hasNodeDef(combinator),
5890
6305
  recovery: opts?.recovery ?? false,
6306
+ ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(combinator), entry: combinator } } : {},
5891
6307
  lazyUsage: analyzeLazyUsage(combinator),
5892
6308
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5893
6309
  };
5894
6310
  const r = emit(combinator, ctx, "_pos");
6311
+ const coverageRootRuleId = ctx.coverage?.plan.rules.get(combinator);
6312
+ const resultBody = coverageRootRuleId === void 0 ? [
6313
+ ` let pos = _pos`,
6314
+ ...r.stmts,
6315
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6316
+ ] : [
6317
+ ` const _coverageResult = (() => {`,
6318
+ ` let pos = _pos`,
6319
+ ...r.stmts,
6320
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6321
+ ` })()`,
6322
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6323
+ ` return _coverageResult`
6324
+ ];
5895
6325
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
5896
6326
  const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
5897
6327
  const hostReadsDecls = ctx.needsHostReads ? [HOST_READS_DECL] : [];
@@ -5904,9 +6334,7 @@ function compile(combinator, mapFnSources, opts) {
5904
6334
  ...namedPrelude,
5905
6335
  ctx.namedFnDecls.join("\n\n"),
5906
6336
  `function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
5907
- ` let pos = _pos`,
5908
- ...r.stmts,
5909
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6337
+ ...resultBody,
5910
6338
  `}`
5911
6339
  ].join("\n");
5912
6340
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
@@ -5916,9 +6344,15 @@ function compile(combinator, mapFnSources, opts) {
5916
6344
  ...ctx.expectedDecls,
5917
6345
  ...namedPrelude,
5918
6346
  ...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
5919
- `let pos = _pos`,
5920
- ...r.stmts,
5921
- `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6347
+ ...coverageRootRuleId === void 0 ? [`let pos = _pos`, ...r.stmts, `return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`] : [
6348
+ `const _coverageResult = (() => {`,
6349
+ ` let pos = _pos`,
6350
+ ...r.stmts,
6351
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6352
+ `})()`,
6353
+ `if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6354
+ `return _coverageResult`
6355
+ ]
5922
6356
  ].join("\n"));
5923
6357
  const defaultCtx = {
5924
6358
  trackLines: false,
@@ -5930,10 +6364,11 @@ function compile(combinator, mapFnSources, opts) {
5930
6364
  const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
5931
6365
  const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
5932
6366
  const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
5933
- const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources) : null;
6367
+ const inlineExpression = canInline ? buildInlineExpression(ctx, r, effectiveSources, buildSources, coverageRootRuleId) : null;
5934
6368
  return {
5935
6369
  source,
5936
6370
  inlineExpression,
6371
+ ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions },
5937
6372
  parse(input, pos = 0) {
5938
6373
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
5939
6374
  },
@@ -5967,6 +6402,7 @@ function publicRuleWrapperSource(rule, fnSource) {
5967
6402
  }
5968
6403
  function compileRuleMap(ruleMap, opts) {
5969
6404
  for (const [, rule] of ruleMap) markUnusedValues(rule);
6405
+ const coverageWinners = opts?.coverage ? Object.fromEntries(ruleMap.filter(([, rule]) => rule._def.tag !== "lazy")) : void 0;
5970
6406
  const grammarTrivia = opts?.trivia;
5971
6407
  const ctx = {
5972
6408
  vars: 0,
@@ -5987,6 +6423,7 @@ function compileRuleMap(ruleMap, opts) {
5987
6423
  namedFnDecls: [],
5988
6424
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
5989
6425
  recovery: opts?.recovery ?? false,
6426
+ ...opts?.coverage ? { coverage: { plan: buildGrammarPlan(ruleMap.map(([, rule]) => rule), coverageWinners) } } : {},
5990
6427
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5991
6428
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5992
6429
  };
@@ -6029,15 +6466,34 @@ function compileRuleMap(ruleMap, opts) {
6029
6466
  return i > 0 ? ["", ...lines] : lines;
6030
6467
  })
6031
6468
  ].filter(Boolean);
6032
- const entryFnText = (r) => [
6033
- `function(input, _pos, _ctx) {`,
6034
- ` let pos = _pos`,
6035
- ...r.stmts,
6036
- ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6037
- `}`
6038
- ].join("\n");
6469
+ const entryRuleId = (rule) => {
6470
+ const direct = ctx.coverage?.plan.rules.get(rule);
6471
+ if (direct !== void 0 || rule._def.tag !== "lazy") return direct;
6472
+ try {
6473
+ return ctx.coverage?.plan.rules.get(rule._def.thunk());
6474
+ } catch {
6475
+ return void 0;
6476
+ }
6477
+ };
6478
+ const entryFnText = (r, rule) => {
6479
+ const ruleId = entryRuleId(rule);
6480
+ const body = ruleId === void 0 ? [
6481
+ ` let pos = _pos`,
6482
+ ...r.stmts,
6483
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6484
+ ] : [
6485
+ ` const _coverageResult = (() => {`,
6486
+ ` let pos = _pos`,
6487
+ ...r.stmts,
6488
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6489
+ ` })()`,
6490
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6491
+ ` return _coverageResult`
6492
+ ];
6493
+ return [`function(input, _pos, _ctx) {`, ...body, `}`].join("\n");
6494
+ };
6039
6495
  const objBody = perEntry.map(({ key, rule, r }) => {
6040
- const src = publicRuleWrapperSource(rule, entryFnText(r));
6496
+ const src = publicRuleWrapperSource(rule, entryFnText(r, rule));
6041
6497
  return ` ${JSON.stringify(key)}: ${src.split("\n").join("\n ")}`;
6042
6498
  }).join(",\n");
6043
6499
  const replacement = [
@@ -6048,7 +6504,11 @@ function compileRuleMap(ruleMap, opts) {
6048
6504
  ` }`,
6049
6505
  `})()`
6050
6506
  ].join("\n");
6051
- return { keys: perEntry.map((e) => e.key), replacement };
6507
+ return {
6508
+ keys: perEntry.map((e) => e.key),
6509
+ replacement,
6510
+ ...ctx.coverage === void 0 ? {} : { coverageDefinitions: ctx.coverage.plan.definitions }
6511
+ };
6052
6512
  }
6053
6513
  function compileLinkable(ruleMapArg, ns, opts) {
6054
6514
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
@@ -6081,8 +6541,13 @@ function compileLinkable(ruleMapArg, ns, opts) {
6081
6541
  triviaCaptureNames: /* @__PURE__ */ new Map(),
6082
6542
  triviaFnNames: /* @__PURE__ */ new Map(),
6083
6543
  namedFnDecls: [],
6084
- capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
6544
+ // A recognition-only fragment normally has no node collector of its own.
6545
+ // Leaf composition can re-lower it beneath a local semantic node, though;
6546
+ // in that case its terminals must feed the caller's collector rather than
6547
+ // merely returning their scalar parse values.
6548
+ capturing: opts?.captureTerminals === true || ruleMap.some(([, rule]) => hasNodeDef(rule)),
6085
6549
  recovery: opts?.recovery ?? false,
6550
+ ...opts?.coverage ? { coverage: { plan: opts.coverage } } : {},
6086
6551
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
6087
6552
  ns,
6088
6553
  deferFirstSetRefs: true,
@@ -6156,7 +6621,14 @@ function compileLinkable(ruleMapArg, ns, opts) {
6156
6621
  ctx.indent = savedIndent;
6157
6622
  ctx.failLabel = savedFail;
6158
6623
  ctx.recordFail = savedRec;
6159
- pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
6624
+ const ruleId = ctx.coverage?.plan.rules.get(rule);
6625
+ const stmts = ruleId === void 0 ? body.stmts : [
6626
+ `${ind(ctx)}_ctx._grammarCoverage?.(${JSON.stringify(ruleId)})`,
6627
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'enter', offset: _pos })`,
6628
+ ...body.stmts,
6629
+ `${ind(ctx)}_ctx._grammarTrace?.write({ id: ${JSON.stringify(ruleId)}, phase: 'success', offset: _pos, end: ${body.endVar} })`
6630
+ ];
6631
+ pushNamedFnDecl(ctx, fn, stmts, body.valueVar, body.endVar);
6160
6632
  }
6161
6633
  perEntry.push({ key, rule, r: emitNamedFnCall(ctx, fn, "_pos") });
6162
6634
  }
@@ -6213,15 +6685,46 @@ function compileLinkable(ruleMapArg, ns, opts) {
6213
6685
  deps: ruleDependencies(ruleMap),
6214
6686
  needsEmptyTl: !!ctx.needsEmptyTl,
6215
6687
  needsHostReads: !!ctx.needsHostReads,
6688
+ hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
6689
+ isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule)),
6216
6690
  mfFns: mfSrcs ? [] : ctx.mapFns,
6217
6691
  buildFns: buildSrcs ? [] : ctx.buildFns
6218
6692
  };
6219
6693
  }
6220
- function buildInlineExpression(ctx, r, mapFnSources, buildSources) {
6221
- const bodyLines = [
6694
+ function hasSemanticReduction(roots) {
6695
+ const seen = /* @__PURE__ */ new Set();
6696
+ const visit = (parser2) => {
6697
+ if (seen.has(parser2)) return false;
6698
+ seen.add(parser2);
6699
+ const def = parser2._def;
6700
+ if (def.tag === "transform" && !def.recognitionOnly) return true;
6701
+ if (def.tag === "choice" && def.gates.some(Boolean)) return true;
6702
+ if (def.tag === "guard" || def.tag === "withCtx") return true;
6703
+ if (def.tag === "node" && def.build !== void 0) return true;
6704
+ if (def.tag === "lazy") {
6705
+ try {
6706
+ return visit(def.thunk());
6707
+ } catch {
6708
+ return true;
6709
+ }
6710
+ }
6711
+ return childrenOf(def).some(visit);
6712
+ };
6713
+ return roots.some(visit);
6714
+ }
6715
+ function buildInlineExpression(ctx, r, mapFnSources, buildSources, coverageRootRuleId) {
6716
+ const bodyLines = coverageRootRuleId === void 0 ? [
6222
6717
  ` let pos = _pos`,
6223
6718
  ...r.stmts,
6224
6719
  ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
6720
+ ] : [
6721
+ ` const _coverageResult = (() => {`,
6722
+ ` let pos = _pos`,
6723
+ ...r.stmts,
6724
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
6725
+ ` })()`,
6726
+ ` if (!_coverageResult.ok) _ctx._grammarTrace?.write({ id: ${JSON.stringify(coverageRootRuleId)}, phase: 'failure', offset: _coverageResult.span.start })`,
6727
+ ` return _coverageResult`
6225
6728
  ];
6226
6729
  const innerFn = [
6227
6730
  `function(input, _pos, _ctx) {`,
@@ -6276,8 +6779,8 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
6276
6779
  };
6277
6780
  const emit2 = (end) => {
6278
6781
  if (end > pos && cstCaptureActive(ctx)) {
6279
- const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
6280
- pushCstLeaf(ctx, leaf);
6782
+ const leaf2 = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
6783
+ pushCstLeaf(ctx, leaf2);
6281
6784
  }
6282
6785
  };
6283
6786
  while (cur < input.length) {
@@ -6331,6 +6834,7 @@ function balanced(open, close, options = {}) {
6331
6834
  );
6332
6835
  if (combi._def.tag === "transform") {
6333
6836
  combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
6837
+ combi._def.recognitionOnly = true;
6334
6838
  }
6335
6839
  self.define(combi);
6336
6840
  return combi;
@@ -6376,9 +6880,11 @@ function childrenOf2(def) {
6376
6880
  case "many":
6377
6881
  case "oneOrMore":
6378
6882
  case "optional":
6883
+ case "attempt":
6379
6884
  case "transform":
6380
6885
  case "trivia":
6381
6886
  case "token":
6887
+ case "leaf":
6382
6888
  case "label":
6383
6889
  case "field":
6384
6890
  case "not":
@@ -6427,7 +6933,7 @@ function serializeRuleMap(ruleMap) {
6427
6933
  }
6428
6934
  }
6429
6935
  function evalRuleMapIR(ir) {
6430
- const _tf = (child, src) => {
6936
+ const _tf = (child, src, recognitionOnly = false) => {
6431
6937
  let fn2;
6432
6938
  try {
6433
6939
  fn2 = (0, eval)(`(${src})`);
@@ -6438,11 +6944,31 @@ function evalRuleMapIR(ir) {
6438
6944
  }
6439
6945
  const t = transform(child, fn2);
6440
6946
  t._def.fnSrc = src;
6947
+ if (recognitionOnly) t._def.recognitionOnly = true;
6441
6948
  return t;
6442
6949
  };
6443
- const _nd = (type, child, src, opts) => {
6444
- const n = node(type, child, void 0, opts);
6950
+ const _lf = (child, src) => {
6951
+ let fn2;
6952
+ try {
6953
+ fn2 = (0, eval)(`(${src})`);
6954
+ } catch {
6955
+ fn2 = () => {
6956
+ throw new Error("IR leaf fn not materialized");
6957
+ };
6958
+ }
6959
+ const l = leaf(child, fn2);
6960
+ l._def.fnSrc = src;
6961
+ return l;
6962
+ };
6963
+ const _nd = (type, child, src, opts, staticError) => {
6964
+ if (staticError !== void 0 && staticError.length > 0) {
6965
+ throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
6966
+ }
6967
+ const n = node(type, child, (() => {
6968
+ throw new Error("IR node build requires static re-lowering");
6969
+ }), opts);
6445
6970
  n._def.buildSrc = src;
6971
+ if (staticError !== void 0) n._def.buildStaticError = staticError;
6446
6972
  return n;
6447
6973
  };
6448
6974
  const _gch = (items) => {
@@ -6492,6 +7018,7 @@ function evalRuleMapIR(ir) {
6492
7018
  "keywords",
6493
7019
  "sequence",
6494
7020
  "choice",
7021
+ "attempt",
6495
7022
  "many",
6496
7023
  "oneOrMore",
6497
7024
  "optional",
@@ -6501,6 +7028,7 @@ function evalRuleMapIR(ir) {
6501
7028
  "parser",
6502
7029
  "scanTo",
6503
7030
  "token",
7031
+ "leaf",
6504
7032
  "transform",
6505
7033
  "skip",
6506
7034
  "trivia",
@@ -6508,6 +7036,7 @@ function evalRuleMapIR(ir) {
6508
7036
  "field",
6509
7037
  "expect",
6510
7038
  "_tf",
7039
+ "_lf",
6511
7040
  "_nd",
6512
7041
  "_gch",
6513
7042
  "_wc",
@@ -6521,6 +7050,7 @@ function evalRuleMapIR(ir) {
6521
7050
  keywords,
6522
7051
  sequence,
6523
7052
  choice,
7053
+ attempt,
6524
7054
  many,
6525
7055
  oneOrMore,
6526
7056
  optional,
@@ -6530,6 +7060,7 @@ function evalRuleMapIR(ir) {
6530
7060
  parser,
6531
7061
  scanTo,
6532
7062
  token,
7063
+ leaf,
6533
7064
  transform,
6534
7065
  skip,
6535
7066
  trivia,
@@ -6537,6 +7068,7 @@ function evalRuleMapIR(ir) {
6537
7068
  field,
6538
7069
  expect,
6539
7070
  _tf,
7071
+ _lf,
6540
7072
  _nd,
6541
7073
  _gch,
6542
7074
  _wc
@@ -6668,6 +7200,8 @@ ${body}
6668
7200
  return `oneOrMore(${kid(def.parser)})`;
6669
7201
  case "optional":
6670
7202
  return `optional(${kid(def.parser)})`;
7203
+ case "attempt":
7204
+ return `attempt(${kid(def.parser)})`;
6671
7205
  case "sepBy":
6672
7206
  return `sepBy(${kid(def.parser)}, ${kid(def.separator)})`;
6673
7207
  case "not":
@@ -6676,6 +7210,10 @@ ${body}
6676
7210
  return `trivia(${kid(def.parser)})`;
6677
7211
  case "token":
6678
7212
  return `token(${kid(def.parser)})`;
7213
+ case "leaf": {
7214
+ if (def.fnSrc === void 0) throw new Unserializable("leaf without fnSrc");
7215
+ return `_lf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
7216
+ }
6679
7217
  case "label":
6680
7218
  return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
6681
7219
  case "field":
@@ -6688,21 +7226,30 @@ ${body}
6688
7226
  return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}], orEOF: ${def.orEOF} })`;
6689
7227
  case "transform": {
6690
7228
  if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
6691
- return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)})`;
7229
+ return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
6692
7230
  }
6693
7231
  case "node": {
6694
7232
  if (def.build !== void 0 && def.buildSrc === void 0) throw new Unserializable("node build without buildSrc");
6695
- const opts = def.unwrap || def.collapse ? `, { ${def.unwrap ? "unwrap: true" : "collapse: true"} }` : "";
7233
+ const optEntries = [
7234
+ ...def.unwrap ? ["unwrap: true"] : [],
7235
+ ...def.collapse ? ["collapse: true"] : [],
7236
+ ...def.captureTrivia ? ["captureTrivia: true"] : [],
7237
+ ...def.trailingTrivia ? ["trailingTrivia: true"] : []
7238
+ ];
7239
+ const opts = optEntries.length > 0 ? `, { ${optEntries.join(", ")} }` : "";
6696
7240
  if (def.type === void 0) {
6697
7241
  if (def.buildSrc !== void 0) throw new Unserializable("inferred node build without inferred type");
6698
7242
  return opts ? `node(${kid(def.parser)}, undefined${opts})` : `node(${kid(def.parser)})`;
6699
7243
  }
6700
- if (def.buildSrc !== void 0) return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts})`;
7244
+ if (def.buildSrc !== void 0) {
7245
+ const staticError = def.buildStaticError === void 0 ? "" : `${opts ? "" : ", undefined"}, ${JSON.stringify(def.buildStaticError)}`;
7246
+ return `_nd(${JSON.stringify(def.type)}, ${kid(def.parser)}, ${JSON.stringify(def.buildSrc)}${opts}${staticError})`;
7247
+ }
6701
7248
  return opts ? `node(${JSON.stringify(def.type)}, ${kid(def.parser)}, undefined${opts})` : `node(${JSON.stringify(def.type)}, ${kid(def.parser)})`;
6702
7249
  }
6703
7250
  case "grammar": {
6704
7251
  const trivia2 = def.clearTrivia ? "null" : def.triviaParser ? kid(def.triviaParser) : "undefined";
6705
- return `parser({ trivia: ${trivia2}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
7252
+ return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
6706
7253
  }
6707
7254
  case "withCtx": {
6708
7255
  if (def.extraSrc === void 0) throw new Unserializable("withCtx without extraSrc");
@@ -6717,6 +7264,28 @@ ${body}
6717
7264
  };
6718
7265
 
6719
7266
  // src/compiler/linker.ts
7267
+ function normalizeCstCollapse(collapse) {
7268
+ if (collapse === true) return () => true;
7269
+ if (Array.isArray(collapse)) {
7270
+ const types = new Set(collapse);
7271
+ return (type) => types.has(type);
7272
+ }
7273
+ return typeof collapse === "function" ? collapse : void 0;
7274
+ }
7275
+ function buildCstNode(type, children2, _fields, span, _rawChildren, _triviaLog, state) {
7276
+ return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children2] };
7277
+ }
7278
+ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triviaLog, state) {
7279
+ if (typeof typeOrOptions === "string") {
7280
+ return buildCstNode(typeOrOptions, children2 ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
7281
+ }
7282
+ const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
7283
+ const host = (type, children3, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children3, fields, span2, rawChildren2, triviaLog2, state2);
7284
+ host._parsemanCstOutput = true;
7285
+ if (collapse) host._parsemanCstCollapse = collapse;
7286
+ return host;
7287
+ }
7288
+ cstBuildHost._parsemanCstOutput = true;
6720
7289
  function pickPieces(pieces, names) {
6721
7290
  const winner = /* @__PURE__ */ new Map();
6722
7291
  for (const pc of pieces) for (const k of pc.keys) winner.set(k, pc);
@@ -6794,9 +7363,12 @@ ${body}
6794
7363
  function isIRPiece(p) {
6795
7364
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
6796
7365
  }
6797
- function materializePiece(p, trivia2) {
7366
+ function materializePiece(p, trivia2, captureTerminals = false) {
6798
7367
  if (!isIRPiece(p)) return p;
6799
- const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, trivia2 ? { trivia: trivia2 } : void 0);
7368
+ const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, {
7369
+ ...trivia2 ? { trivia: trivia2 } : {},
7370
+ ...captureTerminals ? { captureTerminals: true } : {}
7371
+ });
6800
7372
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
6801
7373
  return pieces;
6802
7374
  }
@@ -6826,6 +7398,152 @@ var { isFinite } = Number;
6826
7398
  // src/index.ts
6827
7399
  registerRegexAnalyzer(firstSetFromRegex);
6828
7400
 
7401
+ // src/plugin/direct-builder-static.ts
7402
+ var import_oxc_parser = require("oxc-parser");
7403
+ var STATIC_BUILDER_GLOBALS = /* @__PURE__ */ new Set([
7404
+ "Array",
7405
+ "Boolean",
7406
+ "Date",
7407
+ "JSON",
7408
+ "Math",
7409
+ "NaN",
7410
+ "Number",
7411
+ "Object",
7412
+ "String",
7413
+ "Infinity",
7414
+ "parseFloat",
7415
+ "parseInt",
7416
+ "undefined"
7417
+ ]);
7418
+ function astArray(value) {
7419
+ return Array.isArray(value) ? value : [];
7420
+ }
7421
+ function astChild(value) {
7422
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
7423
+ }
7424
+ function unwrapCallbackExpression(node2) {
7425
+ let current = node2;
7426
+ while (current?.type === "ParenthesizedExpression") current = astChild(current.expression) ?? void 0;
7427
+ return current;
7428
+ }
7429
+ function directBuilderUnsupportedBindings(src) {
7430
+ let init;
7431
+ try {
7432
+ const parsed = (0, import_oxc_parser.parseSync)("parseman-direct-builder.ts", `const _direct = (${src})`);
7433
+ if (parsed.errors.length > 0) return ["invalid callback source"];
7434
+ const program = parsed.program;
7435
+ const statement = program.body?.[0];
7436
+ const declaration = statement?.declarations?.[0];
7437
+ init = unwrapCallbackExpression(declaration?.init);
7438
+ } catch {
7439
+ return ["invalid callback source"];
7440
+ }
7441
+ if (init?.type !== "ArrowFunctionExpression" || !Array.isArray(init.params)) return ["unsupported callback shape"];
7442
+ const allowed = new Set(STATIC_BUILDER_GLOBALS);
7443
+ for (const param of init.params) {
7444
+ if (param.type !== "Identifier" || typeof param.name !== "string") return ["unsupported parameter pattern"];
7445
+ allowed.add(param.name);
7446
+ }
7447
+ const unsupported = /* @__PURE__ */ new Set();
7448
+ const reportUnsupportedNode = (ast) => {
7449
+ unsupported.add(`unsupported ${ast.type}`);
7450
+ };
7451
+ const read = (ast, allowedNames) => {
7452
+ if (typeof ast.name === "string" && allowedNames.has(ast.name)) return;
7453
+ unsupported.add(typeof ast.name === "string" ? ast.name : "identifier");
7454
+ };
7455
+ const expression = (node2, allowedNames) => {
7456
+ const ast = astChild(node2);
7457
+ if (!ast) return;
7458
+ switch (ast.type) {
7459
+ case "Literal":
7460
+ return;
7461
+ case "Identifier":
7462
+ read(ast, allowedNames);
7463
+ return;
7464
+ case "ParenthesizedExpression":
7465
+ case "ChainExpression":
7466
+ case "TSAsExpression":
7467
+ case "TSTypeAssertion":
7468
+ case "TSNonNullExpression":
7469
+ expression(ast.expression, allowedNames);
7470
+ return;
7471
+ case "ArrayExpression":
7472
+ for (const element of astArray(ast.elements)) expression(element, allowedNames);
7473
+ return;
7474
+ case "ObjectExpression":
7475
+ for (const property of astArray(ast.properties)) {
7476
+ if (property.type === "SpreadElement") {
7477
+ expression(property.argument, allowedNames);
7478
+ continue;
7479
+ }
7480
+ if (property.type !== "Property" || property.method === true || property.kind !== "init") {
7481
+ reportUnsupportedNode(property);
7482
+ continue;
7483
+ }
7484
+ if (property.computed === true) expression(property.key, allowedNames);
7485
+ expression(property.value, allowedNames);
7486
+ }
7487
+ return;
7488
+ case "MemberExpression":
7489
+ expression(ast.object, allowedNames);
7490
+ if (ast.computed === true) expression(ast.property, allowedNames);
7491
+ return;
7492
+ case "CallExpression":
7493
+ case "NewExpression":
7494
+ expression(ast.callee, allowedNames);
7495
+ for (const argument of astArray(ast.arguments)) expression(argument, allowedNames);
7496
+ return;
7497
+ case "UnaryExpression":
7498
+ case "UpdateExpression":
7499
+ expression(ast.argument, allowedNames);
7500
+ return;
7501
+ case "BinaryExpression":
7502
+ case "LogicalExpression":
7503
+ case "AssignmentExpression":
7504
+ expression(ast.left, allowedNames);
7505
+ expression(ast.right, allowedNames);
7506
+ return;
7507
+ case "ConditionalExpression":
7508
+ expression(ast.test, allowedNames);
7509
+ expression(ast.consequent, allowedNames);
7510
+ expression(ast.alternate, allowedNames);
7511
+ return;
7512
+ case "SequenceExpression":
7513
+ for (const value of astArray(ast.expressions)) expression(value, allowedNames);
7514
+ return;
7515
+ case "TemplateLiteral":
7516
+ for (const value of astArray(ast.expressions)) expression(value, allowedNames);
7517
+ return;
7518
+ case "SpreadElement":
7519
+ expression(ast.argument, allowedNames);
7520
+ return;
7521
+ case "ArrowFunctionExpression": {
7522
+ const nested = new Set(allowedNames);
7523
+ for (const param of astArray(ast.params)) {
7524
+ if (param.type !== "Identifier" || typeof param.name !== "string") {
7525
+ reportUnsupportedNode(param);
7526
+ continue;
7527
+ }
7528
+ nested.add(param.name);
7529
+ }
7530
+ if (astChild(ast.body)?.type === "BlockStatement") {
7531
+ reportUnsupportedNode(astChild(ast.body));
7532
+ return;
7533
+ }
7534
+ expression(ast.body, nested);
7535
+ return;
7536
+ }
7537
+ default:
7538
+ reportUnsupportedNode(ast);
7539
+ return;
7540
+ }
7541
+ };
7542
+ if (astChild(init.body)?.type === "BlockStatement") unsupported.add("unsupported BlockStatement");
7543
+ else expression(init.body, allowed);
7544
+ return [...unsupported];
7545
+ }
7546
+
6829
7547
  // src/plugin/evaluator.ts
6830
7548
  function stripTsFromSource(node2, code) {
6831
7549
  const cuts = [];
@@ -6879,15 +7597,18 @@ var SUPPORTED = {
6879
7597
  word: (...a) => word(a[0], a[1]),
6880
7598
  sequence: (...a) => sequence(...a),
6881
7599
  choice: (...a) => choice(...a),
7600
+ attempt: (...a) => attempt(a[0]),
6882
7601
  many: (...a) => many(a[0]),
6883
7602
  oneOrMore: (...a) => oneOrMore(a[0]),
6884
7603
  optional: (...a) => optional(a[0]),
7604
+ skip: (...a) => skip(a[0], a[1]),
6885
7605
  sepBy: (...a) => sepBy(a[0], a[1]),
6886
7606
  trivia: (...a) => trivia(a[0]),
6887
7607
  label: (...a) => label(a[0], a[1]),
6888
7608
  field: (...a) => field(a[0], a[1]),
6889
7609
  noTrivia: (...a) => noTrivia(a[0]),
6890
7610
  token: (...a) => token(a[0]),
7611
+ leaf: (...a) => leaf(a[0], a[1]),
6891
7612
  expect: (...a) => expect(a[0], a[1])
6892
7613
  };
6893
7614
  function isScopeEntry(v2) {
@@ -6943,12 +7664,12 @@ function staticNodeOptions(expr) {
6943
7664
  if (p.computed) continue;
6944
7665
  const key = p.key;
6945
7666
  const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
6946
- if (name === "unwrap" || name === "collapse") {
7667
+ if (name === "unwrap" || name === "collapse" || name === "captureTrivia" || name === "trailingTrivia") {
6947
7668
  const val = p.value;
6948
7669
  if ((val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true) opts[name] = true;
6949
7670
  }
6950
7671
  }
6951
- return opts.unwrap || opts.collapse ? opts : void 0;
7672
+ return opts.unwrap || opts.collapse || opts.captureTrivia || opts.trailingTrivia ? opts : void 0;
6952
7673
  }
6953
7674
  function exprToCombi(node2, scope, code, mfs) {
6954
7675
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
@@ -6987,7 +7708,7 @@ function exprToCombi(node2, scope, code, mfs) {
6987
7708
  if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
6988
7709
  const inner = anyValue(parserArg, scope, code, mfs);
6989
7710
  if (!isCombinator(inner)) return null;
6990
- const fnSrc = code.slice(fnArg.start, fnArg.end);
7711
+ const fnSrc = stripTsFromSource(fnArg, code);
6991
7712
  mfs.push(fnSrc);
6992
7713
  try {
6993
7714
  const combi = transform(inner, (v2) => v2);
@@ -6997,6 +7718,21 @@ function exprToCombi(node2, scope, code, mfs) {
6997
7718
  return null;
6998
7719
  }
6999
7720
  }
7721
+ if (callee.name === "leaf" && code !== void 0 && mfs !== void 0) {
7722
+ const [parserArg, fnArg] = node2.arguments;
7723
+ if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
7724
+ const inner = anyValue(parserArg, scope, code, mfs);
7725
+ if (!isCombinator(inner)) return null;
7726
+ const fnSrc = stripTsFromSource(fnArg, code);
7727
+ mfs.push(fnSrc);
7728
+ try {
7729
+ const combi = leaf(inner, (v2) => v2);
7730
+ if (combi._def.tag === "leaf") combi._def.fnSrc = fnSrc;
7731
+ return combi;
7732
+ } catch {
7733
+ return null;
7734
+ }
7735
+ }
7000
7736
  if (callee.name === "node" && code !== void 0) {
7001
7737
  const [firstArg, secondArg, thirdArg, fourthArg] = node2.arguments;
7002
7738
  if (!firstArg || firstArg.type === "SpreadElement") return null;
@@ -7010,11 +7746,15 @@ function exprToCombi(node2, scope, code, mfs) {
7010
7746
  if (!isCombinator(inner)) return null;
7011
7747
  const be = buildArg;
7012
7748
  const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !(be.type === "Identifier" && be.name === "undefined");
7013
- const buildSrc = hasBuild ? code.slice(be.start, be.end) : void 0;
7749
+ const buildSrc = hasBuild ? stripTsFromSource(be, code) : void 0;
7014
7750
  const opts = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticNodeOptions(optsArg) : void 0;
7015
7751
  try {
7016
7752
  const combi = explicitType !== void 0 ? node(explicitType, inner, hasBuild ? () => null : void 0, opts) : node(inner, hasBuild ? () => null : void 0, opts);
7017
- if (combi._def.tag === "node" && buildSrc !== void 0) combi._def.buildSrc = buildSrc;
7753
+ if (combi._def.tag === "node" && buildSrc !== void 0) {
7754
+ combi._def.buildSrc = buildSrc;
7755
+ const staticError = directBuilderUnsupportedBindings(buildSrc);
7756
+ if (staticError.length > 0) combi._def.buildStaticError = staticError;
7757
+ }
7018
7758
  return combi;
7019
7759
  } catch {
7020
7760
  return null;
@@ -7107,7 +7847,7 @@ function exprToCombi(node2, scope, code, mfs) {
7107
7847
  if (code === void 0 || mfs === void 0) return null;
7108
7848
  const [predArg] = node2.arguments;
7109
7849
  if (!predArg || predArg.type === "SpreadElement") return null;
7110
- const predSrc = code.slice(predArg.start, predArg.end);
7850
+ const predSrc = stripTsFromSource(predArg, code);
7111
7851
  mfs.push(predSrc);
7112
7852
  try {
7113
7853
  const combi = guard(() => true);
@@ -7122,7 +7862,7 @@ function exprToCombi(node2, scope, code, mfs) {
7122
7862
  if (code === void 0 || mfs === void 0) return null;
7123
7863
  const [extraArg, innerArg] = node2.arguments;
7124
7864
  if (!extraArg || !innerArg || extraArg.type === "SpreadElement" || innerArg.type === "SpreadElement") return null;
7125
- const extraSrc = code.slice(extraArg.start, extraArg.end);
7865
+ const extraSrc = stripTsFromSource(extraArg, code);
7126
7866
  mfs.push(extraSrc);
7127
7867
  const inner = anyValue(innerArg, scope, code, mfs);
7128
7868
  if (!isCombinator(inner)) return null;
@@ -7445,6 +8185,63 @@ function getCompiledResolver() {
7445
8185
  mainFields: ["module", "main"]
7446
8186
  });
7447
8187
  }
8188
+ var PRIVATE_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
8189
+ function nearestPackageRoot(file) {
8190
+ let dir = path.dirname(file);
8191
+ for (; ; ) {
8192
+ try {
8193
+ if (fs.statSync(path.join(dir, "package.json")).isFile()) return fs.realpathSync(dir);
8194
+ } catch {
8195
+ }
8196
+ const parent = path.dirname(dir);
8197
+ if (parent === dir) return null;
8198
+ dir = parent;
8199
+ }
8200
+ }
8201
+ function isWithinPackageRoot(file, root) {
8202
+ const relative2 = path.relative(root, file);
8203
+ return relative2 !== ".." && !relative2.startsWith(`..${path.sep}`) && !path.isAbsolute(relative2);
8204
+ }
8205
+ function resolvePrivateSourceModule(from, specifier) {
8206
+ if (!specifier.startsWith(".") && !path.isAbsolute(specifier)) return null;
8207
+ const packageRoot = nearestPackageRoot(from);
8208
+ if (!packageRoot) return null;
8209
+ const base = path.resolve(path.dirname(from), specifier);
8210
+ const ext = path.extname(base);
8211
+ const candidates = [
8212
+ base,
8213
+ ...ext ? [".ts", ".tsx", ".mts", ".cts"].map((next) => `${base.slice(0, -ext.length)}${next}`) : [],
8214
+ ...!ext ? [".ts", ".tsx", ".mts", ".cts"].map((next) => `${base}${next}`) : [],
8215
+ ...!ext ? ["index.ts", "index.tsx", "index.mts", "index.cts"].map((next) => path.join(base, next)) : []
8216
+ ];
8217
+ for (const candidate of candidates) {
8218
+ try {
8219
+ if (!fs.statSync(candidate).isFile()) continue;
8220
+ const real = fs.realpathSync(candidate);
8221
+ if (isWithinPackageRoot(real, packageRoot)) return real;
8222
+ } catch {
8223
+ }
8224
+ }
8225
+ return null;
8226
+ }
8227
+ function resolveGrammarModule(from, specifier) {
8228
+ if (PRIVATE_SOURCE_EXTENSIONS.has(path.extname(specifier))) {
8229
+ const file2 = resolvePrivateSourceModule(from, specifier);
8230
+ return file2 ? { file: file2, source: true } : null;
8231
+ }
8232
+ try {
8233
+ const file2 = getCompiledResolver().resolveFileSync(from, specifier).path;
8234
+ if (file2) return { file: file2, source: false };
8235
+ } catch {
8236
+ }
8237
+ const file = resolvePrivateSourceModule(from, specifier);
8238
+ return file ? { file, source: true } : null;
8239
+ }
8240
+ var _sourceLowering = /* @__PURE__ */ new Set();
8241
+ var _sourceLoweringCache = /* @__PURE__ */ new Map();
8242
+ function sourceLoweringCacheKey(file, moduleAliases, warnUnloweredRegex, recovery) {
8243
+ return `${file}\0${[...moduleAliases].sort().join("\0")}\0${warnUnloweredRegex ? "warn" : ""}\0${recovery ? "recovery" : ""}`;
8244
+ }
7448
8245
  var _moduleParseCache = /* @__PURE__ */ new Map();
7449
8246
  function parseModuleCached(filePath) {
7450
8247
  let mtimeMs;
@@ -7458,7 +8255,7 @@ function parseModuleCached(filePath) {
7458
8255
  let parsed = null;
7459
8256
  try {
7460
8257
  const src = fs.readFileSync(filePath, "utf8");
7461
- const r = (0, import_oxc_parser.parseSync)(filePath, src);
8258
+ const r = (0, import_oxc_parser2.parseSync)(filePath, src);
7462
8259
  if (r.errors.length === 0) parsed = { body: r.program.body, src };
7463
8260
  } catch {
7464
8261
  }
@@ -7478,7 +8275,7 @@ var plugin_default = (0, import_unplugin.createUnplugin)((opts = {}) => ({
7478
8275
  if (!code.includes("parseman")) return null;
7479
8276
  if (!code.includes("macro")) return null;
7480
8277
  const moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE, ...opts.moduleAliases ?? []]);
7481
- const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true, opts.recovery === true);
8278
+ const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true, opts.recovery === true, opts.grammarCoverage === true);
7482
8279
  if (result?.warnings.length) {
7483
8280
  for (const w of result.warnings) {
7484
8281
  if (typeof this?.warn === "function") this.warn(`[parseman] ${w}`);
@@ -7582,10 +8379,37 @@ function extractImportBindings(body) {
7582
8379
  }
7583
8380
  return out;
7584
8381
  }
7585
- function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false, recovery = false) {
8382
+ function lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recovery) {
8383
+ if (_sourceLowering.has(file)) return null;
8384
+ let mtimeMs;
8385
+ try {
8386
+ mtimeMs = fs.statSync(file).mtimeMs;
8387
+ } catch {
8388
+ return null;
8389
+ }
8390
+ const cacheKey = sourceLoweringCacheKey(file, moduleAliases, warnUnloweredRegex, recovery);
8391
+ const hit = _sourceLoweringCache.get(cacheKey);
8392
+ if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
8393
+ _sourceLowering.add(file);
8394
+ let parsedModule = null;
8395
+ try {
8396
+ const source = fs.readFileSync(file, "utf8");
8397
+ const transformed = transformMacro(source, file, moduleAliases, warnUnloweredRegex, recovery);
8398
+ if (!transformed || transformed.warnings.length > 0) return null;
8399
+ const parsed = (0, import_oxc_parser2.parseSync)(file, transformed.code);
8400
+ parsedModule = parsed.errors.length === 0 ? { body: parsed.program.body, src: transformed.code } : null;
8401
+ return parsedModule;
8402
+ } catch {
8403
+ return null;
8404
+ } finally {
8405
+ _sourceLoweringCache.set(cacheKey, { mtimeMs, parsed: parsedModule });
8406
+ _sourceLowering.delete(file);
8407
+ }
8408
+ }
8409
+ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE]), warnUnloweredRegex = false, recovery = false, grammarCoverage = false) {
7586
8410
  let result;
7587
8411
  try {
7588
- result = (0, import_oxc_parser.parseSync)(id, code);
8412
+ result = (0, import_oxc_parser2.parseSync)(id, code);
7589
8413
  } catch {
7590
8414
  return null;
7591
8415
  }
@@ -7623,6 +8447,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7623
8447
  const warnings = [];
7624
8448
  beginLoweringCapture();
7625
8449
  let anyUnresolved = false;
8450
+ let runtimeComposeFallback = false;
7626
8451
  let ruleMapHolderCounter = 0;
7627
8452
  const lineOf = (pos) => {
7628
8453
  let line = 1;
@@ -7633,7 +8458,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7633
8458
  anyUnresolved = true;
7634
8459
  warnings.push(`${id}:${lineOf(pos)} \u2014 ${msg} (running via the interpreter; add the plugin or simplify the declaration to compile it)`);
7635
8460
  };
7636
- const compileRulesFactory = (init, label2) => {
8461
+ const evaluateRulesFactory = (init, label2) => {
7637
8462
  const args = init.arguments;
7638
8463
  const arg0 = args[0];
7639
8464
  const arg1 = args[1];
@@ -7653,15 +8478,21 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7653
8478
  (p) => p.key?.name === "trivia"
7654
8479
  )?.value : void 0;
7655
8480
  const gTrivia = triviaValue ? evaluateExpr(triviaValue, scope, code, []) : void 0;
7656
- const compiled = compileRuleMap([...ruleMap], { ...gTrivia ? { trivia: gTrivia } : {}, recovery });
8481
+ return { ruleMap, ...gTrivia ? { trivia: gTrivia } : {} };
8482
+ };
8483
+ const compileRulesFactory = (init, label2) => {
8484
+ const evaluated = evaluateRulesFactory(init, label2);
8485
+ if (!evaluated) return null;
8486
+ const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, recovery, coverage: grammarCoverage });
7657
8487
  if (!compiled) {
7658
8488
  warn(init.start, `${label2}: rule map couldn't be inlined`);
7659
8489
  return null;
7660
8490
  }
7661
- return { replacement: compiled.replacement, ruleMap, ...gTrivia ? { trivia: gTrivia } : {} };
8491
+ return { replacement: compiled.replacement, ruleMap: evaluated.ruleMap, ...compiled.coverageDefinitions ? { coverageDefinitions: compiled.coverageDefinitions } : {}, ...evaluated.trivia ? { trivia: evaluated.trivia } : {} };
7662
8492
  };
7663
8493
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
7664
8494
  const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
8495
+ const isComposeLeafCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "composeLeaf";
7665
8496
  const isPickCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "pick";
7666
8497
  const localRuleMaps = /* @__PURE__ */ new Map();
7667
8498
  const localGrammarTrivia = /* @__PURE__ */ new Map();
@@ -7671,18 +8502,29 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7671
8502
  const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
7672
8503
  ([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
7673
8504
  ).join(", ")}])`;
7674
- 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: [] }`;
8505
+ 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: [] }`;
7675
8506
  };
7676
8507
  const isSpread = (it) => "__spreadLocal" in it;
7677
8508
  const isIR = (it) => "ir" in it && !("ruleFns" in it);
7678
8509
  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);
7679
8510
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
7680
8511
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
8512
+ const withLeafMarker = (grammarExpr) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.leafComposed'), { value: true, enumerable: false })`;
8513
+ 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 })`;
8514
+ const emittedCoverageDefinitions = (source) => {
8515
+ const ids = /* @__PURE__ */ new Set();
8516
+ for (const match of source.matchAll(/id:\s*"([^"]+)"/g)) ids.add(match[1]);
8517
+ return [...ids].sort().map((id2) => ({
8518
+ id: id2,
8519
+ kind: id2.startsWith("rule:") ? "rule" : id2.startsWith("label:") ? "label" : "choice-arm"
8520
+ }));
8521
+ };
7681
8522
  const localComposedCarried = /* @__PURE__ */ new Map();
7682
8523
  const localComposedTrivia = /* @__PURE__ */ new Map();
7683
8524
  const importedPiecesCache = /* @__PURE__ */ new Map();
7684
- const resolveModulePieces = (file, exportName, seen) => {
7685
- const mod = parseModuleCached(file);
8525
+ const resolveModulePieces = (module2, exportName, seen) => {
8526
+ const { file } = module2;
8527
+ const mod = module2.source ? lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recovery) : parseModuleCached(file);
7686
8528
  if (!mod) return null;
7687
8529
  const localFor = exportLocalName(mod.body, exportName);
7688
8530
  const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
@@ -7701,12 +8543,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7701
8543
  const b = imports.get(local);
7702
8544
  let subPieces = [];
7703
8545
  if (b) {
7704
- let subFile = null;
7705
- try {
7706
- subFile = getCompiledResolver().resolveFileSync(file, b.source).path ?? null;
7707
- } catch {
8546
+ const subModule = resolveGrammarModule(file, b.source);
8547
+ if (subModule && !seen.has(subModule.file)) {
8548
+ subPieces = resolveModulePieces(subModule, b.imported, new Set(seen).add(subModule.file)) ?? [];
7708
8549
  }
7709
- if (subFile && !seen.has(subFile)) subPieces = resolveModulePieces(subFile, b.imported, new Set(seen).add(subFile)) ?? [];
7710
8550
  }
7711
8551
  stubNames.push(local);
7712
8552
  stubVals.push({ [/* @__PURE__ */ Symbol.for("parseman.composedPieces")]: subPieces });
@@ -7723,29 +8563,56 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7723
8563
  let result2 = null;
7724
8564
  const binding = importBindings.get(localName);
7725
8565
  if (binding) {
7726
- let file = null;
7727
- try {
7728
- file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
7729
- } catch {
7730
- }
7731
- result2 = file ? resolveModulePieces(file, binding.imported, /* @__PURE__ */ new Set([file])) : null;
8566
+ const module2 = resolveGrammarModule(id, binding.source);
8567
+ result2 = module2 ? resolveModulePieces(module2, binding.imported, /* @__PURE__ */ new Set([module2.file])) : null;
7732
8568
  }
7733
8569
  importedPiecesCache.set(localName, result2);
7734
8570
  return result2;
7735
8571
  };
7736
- const materializeCarried = (items, composing) => {
8572
+ const materializeCarried = (items, composing, captureTerminals = false) => {
7737
8573
  const out = [];
7738
8574
  for (const it of items) {
7739
8575
  if (isSpread(it)) {
7740
- for (const p of importedPieces(it.__spreadLocal) ?? []) out.push(materializePiece(p, composing));
8576
+ for (const p of importedPieces(it.__spreadLocal) ?? []) out.push(materializePiece(p, composing, captureTerminals));
7741
8577
  } else if (isIR(it)) {
7742
- out.push(materializePiece(it, composing));
8578
+ out.push(materializePiece(it, composing, captureTerminals));
7743
8579
  } else {
7744
8580
  out.push(it);
7745
8581
  }
7746
8582
  }
7747
8583
  return out;
7748
8584
  };
8585
+ const materializeLeafCoverage = (items, localRules, localNs, composing, captureTerminals = false) => {
8586
+ const sources = [];
8587
+ const add = (item) => {
8588
+ if (!isIR(item)) return false;
8589
+ sources.push({ ns: item.ns, rules: evalRuleMapIR(item.ir) });
8590
+ return true;
8591
+ };
8592
+ for (const item of items) {
8593
+ if (isSpread(item)) {
8594
+ const imported = importedPieces(item.__spreadLocal);
8595
+ if (!imported || !imported.every(add)) return null;
8596
+ } else if (!add(item)) return null;
8597
+ }
8598
+ sources.push({ ns: localNs, rules: [...localRules] });
8599
+ const winners = {};
8600
+ for (const source of sources) for (const [name, rule] of source.rules) winners[name] = rule;
8601
+ const plan = buildGrammarPlan(Object.values(winners), winners);
8602
+ const pieces = [];
8603
+ for (let index = 0; index < sources.length; index++) {
8604
+ const source = sources[index];
8605
+ const piece = compileLinkable(source.rules, source.ns, {
8606
+ ...composing ? { trivia: composing } : {},
8607
+ recovery,
8608
+ ...index < sources.length - 1 && captureTerminals ? { captureTerminals: true } : {},
8609
+ coverage: plan
8610
+ });
8611
+ if (!piece) return null;
8612
+ pieces.push(piece);
8613
+ }
8614
+ return pieces;
8615
+ };
7749
8616
  const localCarried = (rm, label2, composing) => {
7750
8617
  const entries = [...rm];
7751
8618
  const ns = nsFor(label2);
@@ -7861,6 +8728,64 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7861
8728
  return null;
7862
8729
  }
7863
8730
  };
8731
+ const compileComposeLeafCall = (init) => {
8732
+ const args = init.arguments;
8733
+ const arr = args[0];
8734
+ if (!arr || arr.type !== "ArrayExpression") {
8735
+ warn(init.start, "composeLeaf(): expected a static array of grammars");
8736
+ return null;
8737
+ }
8738
+ const elements = arr.elements;
8739
+ if (elements.length < 2) {
8740
+ warn(init.start, "composeLeaf(): needs imported recognition grammar(s) and one local rules() map");
8741
+ return null;
8742
+ }
8743
+ const localArg = elements[elements.length - 1];
8744
+ let localRules = null;
8745
+ if (isRulesCall(localArg)) {
8746
+ const evaluated = evaluateRulesFactory(localArg, `composeLeaf${init.start}`);
8747
+ localRules = evaluated?.ruleMap ?? null;
8748
+ } else if (localArg.type === "Identifier") {
8749
+ localRules = localRuleMaps.get(localArg.name) ?? null;
8750
+ }
8751
+ if (!localRules) {
8752
+ warn(init.start, "composeLeaf(): final argument must be a local rules() map");
8753
+ return null;
8754
+ }
8755
+ const composing = composingTrivia(elements);
8756
+ const carried = [];
8757
+ for (let i = 0; i < elements.length - 1; i++) {
8758
+ const r = argPieces(elements[i], `composeLeaf${init.start}_${i}`, composing);
8759
+ if (!r) {
8760
+ warn(init.start, `composeLeaf(): argument ${i} isn't a build-resolvable recognition grammar`);
8761
+ return null;
8762
+ }
8763
+ carried.push(...r.carried);
8764
+ }
8765
+ try {
8766
+ const localNs = nsFor(`composeLeaf${init.start}`);
8767
+ const plainLocalPiece = compileLinkable([...localRules], localNs, { ...composing ? { trivia: composing } : {}, recovery });
8768
+ if (!plainLocalPiece) {
8769
+ warn(init.start, "composeLeaf(): local rules could not be statically compiled");
8770
+ return null;
8771
+ }
8772
+ const recognitionPieces = grammarCoverage ? materializeLeafCoverage(carried, localRules, localNs, composing, plainLocalPiece.hasDirectBuilders === true) : materializeCarried(carried, composing, plainLocalPiece.hasDirectBuilders === true);
8773
+ if (!recognitionPieces) {
8774
+ warn(init.start, "composeLeaf(): coverage needs re-lowerable recognition IR");
8775
+ return null;
8776
+ }
8777
+ const importedRecognitionPieces = grammarCoverage ? recognitionPieces.slice(0, -1) : recognitionPieces;
8778
+ if (importedRecognitionPieces.some((piece) => piece.hasDirectBuilders !== false || piece.isRecognitionOnly !== true)) {
8779
+ warn(init.start, "composeLeaf(): every pre-final grammar must explicitly prove recognition-only");
8780
+ return null;
8781
+ }
8782
+ const replacement = withLeafMarker(emitFusedSource(grammarCoverage ? recognitionPieces : [...recognitionPieces, plainLocalPiece]));
8783
+ return { replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) };
8784
+ } catch (e) {
8785
+ warn(init.start, `composeLeaf(): ${e.message}`);
8786
+ return null;
8787
+ }
8788
+ };
7864
8789
  const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
7865
8790
  const refNames = /* @__PURE__ */ new Set();
7866
8791
  for (const stmt of body) {
@@ -7927,7 +8852,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7927
8852
  const refEntry = scope.get(varName);
7928
8853
  const refCombi = refEntry?.combi ?? null;
7929
8854
  if (refCombi) {
7930
- const compiled2 = compile(refCombi, void 0, { recovery });
8855
+ const compiled2 = compile(refCombi, void 0, { recovery, coverage: grammarCoverage });
7931
8856
  if (compiled2.inlineExpression === null) {
7932
8857
  warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
7933
8858
  continue;
@@ -7941,7 +8866,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7941
8866
  if (!compiledRules) continue;
7942
8867
  localRuleMaps.set(varName, compiledRules.ruleMap);
7943
8868
  if (compiledRules.trivia) localGrammarTrivia.set(varName, compiledRules.trivia);
7944
- let replacement = compiledRules.replacement;
8869
+ let replacement = withCoverageDefinitions(
8870
+ compiledRules.replacement,
8871
+ compiledRules.coverageDefinitions?.length ? compiledRules.coverageDefinitions : emittedCoverageDefinitions(compiledRules.replacement)
8872
+ );
7945
8873
  if (exportPrefix) {
7946
8874
  const ns = nsFor(varName);
7947
8875
  const pieces = compileLinkable([...compiledRules.ruleMap], ns, { recovery });
@@ -7955,11 +8883,22 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7955
8883
  }
7956
8884
  if (isComposeCall(init)) {
7957
8885
  const fused = compileComposeCall(init);
7958
- if (!fused) continue;
8886
+ if (!fused) {
8887
+ runtimeComposeFallback = true;
8888
+ continue;
8889
+ }
7959
8890
  localComposedCarried.set(varName, fused.carried);
7960
8891
  if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
7961
8892
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
7962
- replacements.push({ start: init.start, end: init.end, replacement });
8893
+ replacements.push({ start: init.start, end: init.end, replacement: withCoverageDefinitions(replacement, emittedCoverageDefinitions(replacement)) });
8894
+ continue;
8895
+ }
8896
+ if (isComposeLeafCall(init)) {
8897
+ const fused = compileComposeLeafCall(init);
8898
+ if (!fused) {
8899
+ throw new Error(`${id}:${lineOf(init.start)} \u2014 composeLeaf() must macro-fuse; runtime composition is forbidden`);
8900
+ }
8901
+ replacements.push({ start: init.start, end: init.end, replacement: fused.replacement });
7963
8902
  continue;
7964
8903
  }
7965
8904
  const mapFnSources = [];
@@ -7980,7 +8919,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7980
8919
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
7981
8920
  continue;
7982
8921
  }
7983
- const compiled = compile(parser2, void 0, { recovery });
8922
+ const compiled = compile(parser2, void 0, { recovery, coverage: grammarCoverage });
7984
8923
  if (compiled.inlineExpression === null) {
7985
8924
  warn(init.start, `"${varName}" couldn't be inlined (likely closes over a runtime value)`);
7986
8925
  continue;
@@ -8055,7 +8994,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
8055
8994
  ms.overwrite(imp.start, imp.end, stripped);
8056
8995
  }
8057
8996
  }
8058
- for (const { start, end, replacement } of [...replacements].sort((a, b) => b.start - a.start)) {
8997
+ for (const { start, end, replacement } of (runtimeComposeFallback ? [] : replacements).sort((a, b) => b.start - a.start)) {
8059
8998
  ms.overwrite(start, end, replacement);
8060
8999
  }
8061
9000
  const unlowered = endLoweringCapture();