parseman 0.52.0 → 0.54.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 (108) hide show
  1. package/dist/analysis/diagnostics.js +636 -126
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +6191 -6274
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/field-trivia.d.ts +40 -0
  6. package/dist/combinators/field-trivia.d.ts.map +1 -0
  7. package/dist/combinators/map.d.ts +19 -2
  8. package/dist/combinators/map.d.ts.map +1 -1
  9. package/dist/combinators/parser.d.ts +34 -0
  10. package/dist/combinators/parser.d.ts.map +1 -1
  11. package/dist/combinators/ref.d.ts +0 -13
  12. package/dist/combinators/ref.d.ts.map +1 -1
  13. package/dist/combinators/repeat.d.ts.map +1 -1
  14. package/dist/combinators/sequence.d.ts.map +1 -1
  15. package/dist/compiler/borrowed-rules.d.ts +30 -0
  16. package/dist/compiler/borrowed-rules.d.ts.map +1 -0
  17. package/dist/compiler/compile-linkable-table.d.ts +1 -1
  18. package/dist/compiler/fields.d.ts +2 -0
  19. package/dist/compiler/fields.d.ts.map +1 -1
  20. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  21. package/dist/compiler/ir-serialize.d.ts +10 -4
  22. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  23. package/dist/compiler/linker.d.ts +48 -91
  24. package/dist/compiler/linker.d.ts.map +1 -1
  25. package/dist/coverage.d.ts +1 -1
  26. package/dist/coverage.d.ts.map +1 -1
  27. package/dist/cst/reflection.d.ts.map +1 -1
  28. package/dist/cst/trivia-charscan.d.ts +11 -3
  29. package/dist/cst/trivia-charscan.d.ts.map +1 -1
  30. package/dist/cst/trivia-kinds.d.ts +11 -0
  31. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +764 -888
  35. package/dist/index.js.map +3 -3
  36. package/dist/language-service/index.js.map +1 -1
  37. package/dist/plugin/evaluator.d.ts +38 -2
  38. package/dist/plugin/evaluator.d.ts.map +1 -1
  39. package/dist/plugin/index.d.ts.map +1 -1
  40. package/dist/plugin/index.js +1443 -729
  41. package/dist/plugin/index.js.map +3 -3
  42. package/dist/plugin/ir-eval.d.ts +21 -0
  43. package/dist/plugin/ir-eval.d.ts.map +1 -0
  44. package/dist/plugin/reducer-resolver.d.ts +6 -0
  45. package/dist/plugin/reducer-resolver.d.ts.map +1 -1
  46. package/dist/spec/index.js +555 -6109
  47. package/dist/spec/index.js.map +3 -3
  48. package/dist/spec/railroad-lib.d.ts +7 -2
  49. package/dist/spec/railroad-lib.d.ts.map +1 -1
  50. package/dist/spec/railroad.d.ts.map +1 -1
  51. package/dist/table/assemble.d.ts +9 -0
  52. package/dist/table/assemble.d.ts.map +1 -1
  53. package/dist/table/compile-rule-map.d.ts +11 -4
  54. package/dist/table/compile-rule-map.d.ts.map +1 -1
  55. package/dist/table/compile.d.ts +16 -0
  56. package/dist/table/compile.d.ts.map +1 -1
  57. package/dist/table/emit-assembly.d.ts +1 -1
  58. package/dist/table/emit-assembly.d.ts.map +1 -1
  59. package/dist/table/emit.d.ts +6 -2
  60. package/dist/table/emit.d.ts.map +1 -1
  61. package/dist/table/encode.d.ts.map +1 -1
  62. package/dist/table/exec.d.ts.map +1 -1
  63. package/dist/table/field-spec.d.ts +13 -0
  64. package/dist/table/field-spec.d.ts.map +1 -0
  65. package/dist/table/index.js +480 -199
  66. package/dist/table/index.js.map +2 -2
  67. package/dist/table/scalar-terminal.d.ts +11 -0
  68. package/dist/table/scalar-terminal.d.ts.map +1 -1
  69. package/dist/types.d.ts +14 -0
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/version.d.ts +1 -1
  72. package/package.json +1 -1
  73. package/src/analysis/duplication.ts +1 -1
  74. package/src/analysis/rebuild.ts +1 -1
  75. package/src/combinators/field-trivia.ts +44 -0
  76. package/src/combinators/map.ts +48 -6
  77. package/src/combinators/parser.ts +133 -67
  78. package/src/combinators/ref.ts +10 -1
  79. package/src/combinators/repeat.ts +6 -1
  80. package/src/combinators/sequence.ts +9 -1
  81. package/src/compiler/borrowed-rules.ts +88 -0
  82. package/src/compiler/compile-linkable-table.ts +1 -1
  83. package/src/compiler/fields.ts +5 -2
  84. package/src/compiler/grammar-coverage-ids.ts +35 -6
  85. package/src/compiler/ir-serialize.ts +19 -119
  86. package/src/compiler/linker.ts +190 -666
  87. package/src/coverage.ts +7 -5
  88. package/src/cst/reflection.ts +6 -1
  89. package/src/cst/trivia-charscan.ts +17 -56
  90. package/src/cst/trivia-kinds.ts +24 -8
  91. package/src/index.ts +1 -0
  92. package/src/plugin/evaluator.ts +114 -14
  93. package/src/plugin/index.ts +588 -110
  94. package/src/plugin/ir-eval.ts +165 -0
  95. package/src/plugin/reducer-resolver.ts +20 -3
  96. package/src/spec/railroad-lib.ts +18 -8
  97. package/src/spec/railroad.ts +3 -4
  98. package/src/table/assemble.ts +126 -22
  99. package/src/table/compile-rule-map.ts +18 -8
  100. package/src/table/compile.ts +61 -0
  101. package/src/table/emit-assembly.ts +38 -18
  102. package/src/table/emit.ts +15 -4
  103. package/src/table/encode.ts +18 -2
  104. package/src/table/exec.ts +15 -1
  105. package/src/table/field-spec.ts +17 -0
  106. package/src/table/scalar-terminal.ts +25 -0
  107. package/src/types.ts +12 -2
  108. package/src/version.ts +1 -1
package/dist/index.js CHANGED
@@ -1659,65 +1659,17 @@ function plainRegexSource(p) {
1659
1659
  const d = p._def;
1660
1660
  return d.tag === "regex" && d.flags === "" ? d.source : null;
1661
1661
  }
1662
- var visitSpec0;
1663
- var visitSpec1;
1664
- var visitSpec2;
1665
- var visitSpec3;
1666
- var visitValue0;
1667
- var visitValue1;
1668
- var visitValue2;
1669
- var visitValue3;
1670
1662
  function commonLabeledTriviaVisitor(spec) {
1671
- if (spec === visitSpec0) return visitValue0;
1672
- if (spec === visitSpec1) return visitValue1;
1673
- if (spec === visitSpec2) return visitValue2;
1674
- if (spec === visitSpec3) return visitValue3;
1675
- const sources = spec.minRepeats <= 1 ? spec.arms.map((arm) => plainRegexSource(arm.parser)) : [];
1676
- const visitor = commonCssTriviaVisitor(sources);
1677
- visitSpec3 = visitSpec2;
1678
- visitValue3 = visitValue2;
1679
- visitSpec2 = visitSpec1;
1680
- visitValue2 = visitValue1;
1681
- visitSpec1 = visitSpec0;
1682
- visitValue1 = visitValue0;
1683
- visitSpec0 = spec;
1684
- visitValue0 = visitor;
1685
- return visitor;
1686
- }
1687
- var armSpec0;
1688
- var armSpec1;
1689
- var armSpec2;
1690
- var armSpec3;
1691
- var armValue0;
1692
- var armValue1;
1693
- var armValue2;
1694
- var armValue3;
1663
+ return commonCssTriviaVisitor(spec.minRepeats <= 1 ? spec.arms.map((arm) => plainRegexSource(arm.parser)) : []);
1664
+ }
1695
1665
  function charArmsFor(spec) {
1696
- if (spec === armSpec0) return armValue0;
1697
- if (spec === armSpec1) return armValue1;
1698
- if (spec === armSpec2) return armValue2;
1699
- if (spec === armSpec3) return armValue3;
1700
- let arms = [];
1701
- if (spec.minRepeats > 1) {
1702
- arms = null;
1703
- } else {
1704
- for (const arm of spec.arms) {
1705
- const c = classifyParser(arm.parser);
1706
- if (!c) {
1707
- arms = null;
1708
- break;
1709
- }
1710
- arms.push(c);
1711
- }
1666
+ if (spec.minRepeats > 1) return null;
1667
+ const arms = [];
1668
+ for (const arm of spec.arms) {
1669
+ const c = classifyParser(arm.parser);
1670
+ if (!c) return null;
1671
+ arms.push(c);
1712
1672
  }
1713
- armSpec3 = armSpec2;
1714
- armValue3 = armValue2;
1715
- armSpec2 = armSpec1;
1716
- armValue2 = armValue1;
1717
- armSpec1 = armSpec0;
1718
- armValue1 = armValue0;
1719
- armSpec0 = spec;
1720
- armValue0 = arms;
1721
1673
  return arms;
1722
1674
  }
1723
1675
  function charTriviaEnd(input, cur, arms) {
@@ -1833,12 +1785,16 @@ function analyzeLabeledTrivia(trivia2) {
1833
1785
  }
1834
1786
  arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1835
1787
  }
1836
- const spec = {
1788
+ return cacheLabeledTrivia(trivia2, labeledTriviaSpec(arms, minRepeats));
1789
+ }
1790
+ function labeledTriviaSpec(arms, minRepeats) {
1791
+ return {
1837
1792
  labels: arms.map((a) => a.label),
1838
1793
  arms,
1839
- minRepeats
1794
+ minRepeats,
1795
+ visitor: commonLabeledTriviaVisitor({ arms, minRepeats }),
1796
+ charArms: charArmsFor({ arms, minRepeats })
1840
1797
  };
1841
- return cacheLabeledTrivia(trivia2, spec);
1842
1798
  }
1843
1799
  function matchArmAt(input, pos, arm, state) {
1844
1800
  if (!startsFirstSet(arm, input, pos)) return null;
@@ -1868,7 +1824,7 @@ function scanLabeledTriviaChunks(input, cur, spec, state) {
1868
1824
  return { end: pos, chunks };
1869
1825
  }
1870
1826
  function scanLabeledTriviaEnd(input, cur, spec, state) {
1871
- const chars = charArmsFor(spec);
1827
+ const chars = spec.charArms;
1872
1828
  if (chars !== null) return charTriviaEnd(input, cur, chars);
1873
1829
  let pos = cur;
1874
1830
  let count = 0;
@@ -1886,9 +1842,9 @@ function scanLabeledTriviaEnd(input, cur, spec, state) {
1886
1842
  }
1887
1843
  function visitLabeledTrivia(input, cur, spec, state, visit) {
1888
1844
  if (spec.minRepeats > 1) return void 0;
1889
- const common = commonLabeledTriviaVisitor(spec);
1845
+ const common = spec.visitor;
1890
1846
  if (common !== null) return common(input, cur, visit);
1891
- const chars = charArmsFor(spec);
1847
+ const chars = spec.charArms;
1892
1848
  if (chars !== null) return charTriviaVisit(input, cur, chars, visit);
1893
1849
  let pos = cur;
1894
1850
  let count = 0;
@@ -1955,7 +1911,7 @@ function resolveAdjacencyKindMask(trivia2, kinds) {
1955
1911
  return mask;
1956
1912
  }
1957
1913
  function triviaKindMaskAt(input, pos, spec, state) {
1958
- const chars = charArmsFor(spec);
1914
+ const chars = spec.charArms;
1959
1915
  if (chars !== null) return charTriviaKindMask(input, pos, chars);
1960
1916
  let cur = pos;
1961
1917
  let mask = 0;
@@ -2739,6 +2695,23 @@ function scalarRootOf(combinator) {
2739
2695
  return scalar !== void 0 && def.tag === "grammar" && scalarTree(def.parser, /* @__PURE__ */ new Set()) ? scalar : void 0;
2740
2696
  }
2741
2697
 
2698
+ // src/combinators/field-trivia.ts
2699
+ var LEAD = { at: -1 };
2700
+ var FIELD_LEADING_TRIVIA = 1;
2701
+ var FIELD_TRAILING_TRIVIA = 2;
2702
+ function triviaFieldCapture(name, value, input, start, end, leadFrom, trailEnd) {
2703
+ return {
2704
+ name,
2705
+ value,
2706
+ span: { start, end },
2707
+ leading: leadFrom >= 0 && leadFrom < start ? input.slice(leadFrom, start) : "",
2708
+ trailing: trailEnd > end ? input.slice(end, trailEnd) : ""
2709
+ };
2710
+ }
2711
+ function leadsTrivia(p) {
2712
+ return p._def.tag === "field" && p._def.leadingTrivia === true;
2713
+ }
2714
+
2742
2715
  // src/combinators/sequence.ts
2743
2716
  function sequence(...parsers) {
2744
2717
  let adjacency = null;
@@ -2754,6 +2727,10 @@ function sequence(...parsers) {
2754
2727
  (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
2755
2728
  }
2756
2729
  const hasAdjacency = adjacency !== null;
2730
+ let leading = null;
2731
+ for (let i = 1; i < parsers.length; i++) {
2732
+ if (leadsTrivia(parsers[i])) (leading ??= Array.from({ length: parsers.length }, () => false))[i] = true;
2733
+ }
2757
2734
  const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
2758
2735
  const meta = {
2759
2736
  // Union through the nullable prefix — a leading optional()/many() lets a later
@@ -2801,7 +2778,7 @@ function sequence(...parsers) {
2801
2778
  _tag: "sequence",
2802
2779
  _meta: meta,
2803
2780
  _def: def,
2804
- _parseScalar: hasAdjacency ? void 0 : parseScalar,
2781
+ _parseScalar: hasAdjacency || leading !== null ? void 0 : parseScalar,
2805
2782
  parse(input, pos, ctx) {
2806
2783
  const tolerant = ctx._tolerant === true;
2807
2784
  if (tolerant) {
@@ -2835,6 +2812,7 @@ function sequence(...parsers) {
2835
2812
  const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
2836
2813
  const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
2837
2814
  const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
2815
+ if (leading?.[i] === true) LEAD.at = cur;
2838
2816
  const result = parsers[i].parse(input, termPos, ctx);
2839
2817
  if (!result.ok) return result;
2840
2818
  if (values !== void 0) values.push(result.value);
@@ -3816,6 +3794,7 @@ function unwrapSeparator(separator) {
3816
3794
  }
3817
3795
  function sepBy(combinator, separatorArg, opts = {}) {
3818
3796
  const { sep: separator, keep: keepSeparators } = unwrapSeparator(separatorArg);
3797
+ const separatorLeads = leadsTrivia(separator);
3819
3798
  const { min, max } = resolveBounds("sepBy()", opts);
3820
3799
  const trailing = opts.trailing ?? "forbid";
3821
3800
  const meta = {
@@ -3880,7 +3859,7 @@ function sepBy(combinator, separatorArg, opts = {}) {
3880
3859
  // this option surface already; parity is held by test/parity, not by hope.
3881
3860
  ...keepSeparators ? { keepSeparators: true } : {}
3882
3861
  },
3883
- _parseScalar: keepSeparators ? void 0 : parseScalar,
3862
+ _parseScalar: keepSeparators || separatorLeads ? void 0 : parseScalar,
3884
3863
  parse(input, pos, ctx) {
3885
3864
  const first = combinator.parse(input, pos, ctx);
3886
3865
  const values = [];
@@ -3912,6 +3891,7 @@ function sepBy(combinator, separatorArg, opts = {}) {
3912
3891
  sepPos = advanceTrivia(input, cur, ctx);
3913
3892
  }
3914
3893
  }
3894
+ if (separatorLeads) LEAD.at = cur;
3915
3895
  const sep = separator.parse(input, sepPos, ctx);
3916
3896
  if (!sep.ok) {
3917
3897
  rollbackTrivia(ctx, loopMark);
@@ -4005,6 +3985,7 @@ function withRuleTrivia(trivia2, ctx, run3) {
4005
3985
  ctx.triviaKindLabels = savedLabels;
4006
3986
  }
4007
3987
  }
3988
+ var UNDEFINED_REF = new Error("ref<T>() used before .define() was called");
4008
3989
  function ref() {
4009
3990
  let resolved = null;
4010
3991
  let resolvedScalar = null;
@@ -4020,7 +4001,7 @@ function ref() {
4020
4001
  _def: {
4021
4002
  tag: "lazy",
4022
4003
  thunk: () => {
4023
- if (!resolved) throw new Error("ref<T>() used before .define() was called");
4004
+ if (!resolved) throw UNDEFINED_REF;
4024
4005
  return resolved;
4025
4006
  }
4026
4007
  },
@@ -4410,6 +4391,7 @@ function collectFromCombinator(parser2, out, seen, followLazy = true) {
4410
4391
  function collectGrammarReflection(ruleMap, opts) {
4411
4392
  const byType = /* @__PURE__ */ new Map();
4412
4393
  const followLazy = opts?.followLazy !== false;
4394
+ const seen = /* @__PURE__ */ new Set();
4413
4395
  for (const [, rule] of ruleMap) {
4414
4396
  if (!followLazy && rule._def.tag === "lazy") {
4415
4397
  try {
@@ -4419,7 +4401,7 @@ function collectGrammarReflection(ruleMap, opts) {
4419
4401
  }
4420
4402
  continue;
4421
4403
  }
4422
- collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
4404
+ collectFromCombinator(rule, byType, seen, followLazy);
4423
4405
  }
4424
4406
  return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
4425
4407
  }
@@ -4450,9 +4432,26 @@ function isNamedRuleRefForAnotherRule(r, key) {
4450
4432
  const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
4451
4433
  return name !== void 0 && name !== key;
4452
4434
  }
4435
+ var claimedRules = /* @__PURE__ */ new WeakSet();
4436
+ function ownRule(p, copies) {
4437
+ if (!claimedRules.has(p) || p._meta.isTrivia) return p;
4438
+ let own = copies.get(p);
4439
+ if (own === void 0) {
4440
+ const { grammarTrivia: _t, grammarScanSkip: _s, grammarHostMode: _h, grammarTrackLines: _l, ...meta } = p._meta;
4441
+ copies.set(p, own = { ...p, _meta: meta });
4442
+ }
4443
+ return own;
4444
+ }
4453
4445
  function rules(a, b) {
4454
4446
  const factory = typeof a === "function" ? a : b;
4455
4447
  const options = typeof a === "function" ? b : a;
4448
+ const recipe = { factory, options };
4449
+ const map = linkRules([recipe]);
4450
+ Object.defineProperty(map, RULES_RECIPE, { value: [recipe], enumerable: false });
4451
+ return map;
4452
+ }
4453
+ var RULES_RECIPE = /* @__PURE__ */ Symbol.for("parseman.rulesRecipe");
4454
+ function linkRules(recipes, link) {
4456
4455
  const cache = {};
4457
4456
  const proxy = new Proxy(cache, {
4458
4457
  get(target, key) {
@@ -4466,10 +4465,18 @@ function rules(a, b) {
4466
4465
  return record[key];
4467
4466
  }
4468
4467
  });
4469
- const definitions = factory(proxy);
4470
- for (const key of Object.keys(definitions)) {
4468
+ const winners = /* @__PURE__ */ new Map();
4469
+ for (const recipe of recipes) {
4470
+ const definitions = recipe.factory(proxy);
4471
+ for (const key of Object.keys(definitions)) {
4472
+ winners.set(key, { parser: definitions[key], options: recipe.options });
4473
+ }
4474
+ }
4475
+ const keys = [...winners.keys()];
4476
+ const copies = /* @__PURE__ */ new Map();
4477
+ for (const [key, { parser: defined }] of winners) {
4471
4478
  const placeholder = cache[key];
4472
- const parser2 = definitions[key];
4479
+ const parser2 = ownRule(defined, copies);
4473
4480
  if (placeholder === parser2) {
4474
4481
  throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
4475
4482
  }
@@ -4478,7 +4485,8 @@ function rules(a, b) {
4478
4485
  placeholder.define(parser2);
4479
4486
  placeholder._meta.firstSet = parser2._meta.firstSet;
4480
4487
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
4481
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
4488
+ } else if (isNamedRuleRefForAnotherRule(parser2, key) || link !== void 0) {
4489
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
4482
4490
  const alias = ref();
4483
4491
  tagRule(alias, key);
4484
4492
  alias.define(parser2);
@@ -4490,48 +4498,34 @@ function rules(a, b) {
4490
4498
  cache[key] = parser2;
4491
4499
  }
4492
4500
  }
4493
- if (options?.trivia != null) {
4494
- for (const key of Object.keys(definitions)) {
4495
- const rule = cache[key];
4496
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
4497
- }
4498
- }
4499
- if (options?.scanSkip != null) {
4500
- for (const key of Object.keys(definitions)) {
4501
- const rule = cache[key];
4502
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
4503
- }
4504
- }
4505
- if (options?.hostMode === "cst") {
4506
- for (const key of Object.keys(definitions)) {
4507
- const rule = cache[key];
4508
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
4509
- }
4510
- }
4511
- if (options?.trackLines === true) {
4512
- for (const key of Object.keys(definitions)) {
4513
- const rule = cache[key];
4514
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
4515
- }
4516
- for (const key of Object.keys(definitions)) {
4517
- const rule = cache[key];
4518
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
4519
- const wrapped = parser({ trackLines: true }, rule);
4520
- tagRule(wrapped, key);
4521
- wrapped._meta.grammarTrackLines = true;
4522
- cache[key] = wrapped;
4501
+ for (const key of keys) {
4502
+ const options = winners.get(key).options;
4503
+ let rule = cache[key];
4504
+ if (!rule._meta.isTrivia) {
4505
+ const meta = rule._meta;
4506
+ const trivia2 = link !== void 0 ? link.trivia : options?.trivia;
4507
+ if (trivia2 != null) meta.grammarTrivia = trivia2;
4508
+ if (options?.scanSkip != null) meta.grammarScanSkip = options.scanSkip;
4509
+ if ((link?.hostMode ?? options?.hostMode) === "cst") meta.grammarHostMode = "cst";
4510
+ if (options?.trackLines === true) {
4511
+ meta.grammarTrackLines = true;
4512
+ if (rule._def.tag !== "grammar") {
4513
+ rule = parser({ trackLines: true }, rule);
4514
+ tagRule(rule, key);
4515
+ rule._meta.grammarTrackLines = true;
4516
+ cache[key] = rule;
4517
+ }
4523
4518
  }
4524
4519
  }
4525
- }
4526
- for (const key of Object.keys(definitions)) {
4527
- markUnusedValues(cache[key]);
4520
+ markUnusedValues(rule);
4521
+ claimedRules.add(winners.get(key).parser).add(rule);
4528
4522
  }
4529
4523
  Object.defineProperty(cache, RULE_ORDER, {
4530
- value: Object.keys(definitions),
4524
+ value: keys,
4531
4525
  enumerable: false,
4532
4526
  configurable: true
4533
4527
  });
4534
- attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
4528
+ attachGrammarReflection(cache, collectGrammarReflection(keys.map((key) => [key, cache[key]])));
4535
4529
  return cache;
4536
4530
  }
4537
4531
 
@@ -4914,7 +4908,7 @@ function buildFieldMap(captures) {
4914
4908
  if (!captures?.length) return void 0;
4915
4909
  const out = {};
4916
4910
  for (const cap of captures) {
4917
- const entry = { value: cap.value, span: cap.span };
4911
+ const entry = cap.leading === void 0 ? { value: cap.value, span: cap.span } : { value: cap.value, span: cap.span, leading: cap.leading, trailing: cap.trailing };
4918
4912
  const current = out[cap.name];
4919
4913
  if (current === void 0) out[cap.name] = entry;
4920
4914
  else if (Array.isArray(current)) current.push(entry);
@@ -5146,16 +5140,41 @@ function label(name, combinator) {
5146
5140
  }
5147
5141
  };
5148
5142
  }
5149
- function field(name, combinator) {
5143
+ function field(name, combinator, opts) {
5144
+ const leadingTrivia = opts?.leadingTrivia === true;
5145
+ const trailingTrivia = opts?.trailingTrivia === true;
5146
+ if (!leadingTrivia && !trailingTrivia) {
5147
+ return {
5148
+ _tag: combinator._tag,
5149
+ _meta: combinator._meta,
5150
+ _def: { tag: "field", name, parser: combinator },
5151
+ parse(input, pos, ctx) {
5152
+ const result = combinator.parse(input, pos, ctx);
5153
+ if (!result.ok) return result;
5154
+ ctx._fields?.push({ name, value: result.value, span: result.span });
5155
+ return result;
5156
+ }
5157
+ };
5158
+ }
5150
5159
  return {
5151
5160
  _tag: combinator._tag,
5152
5161
  _meta: combinator._meta,
5153
- _def: { tag: "field", name, parser: combinator },
5162
+ _def: {
5163
+ tag: "field",
5164
+ name,
5165
+ parser: combinator,
5166
+ ...leadingTrivia ? { leadingTrivia: true } : {},
5167
+ ...trailingTrivia ? { trailingTrivia: true } : {}
5168
+ },
5154
5169
  parse(input, pos, ctx) {
5170
+ const leadFrom = LEAD.at;
5171
+ LEAD.at = -1;
5155
5172
  const result = combinator.parse(input, pos, ctx);
5156
5173
  if (!result.ok) return result;
5157
- ctx._fields?.push({ name, value: result.value, span: result.span });
5158
- return result;
5174
+ const end = result.span.end;
5175
+ const trailEnd = trailingTrivia && ctx.trivia ? consumeTrivia(input, end, ctx) : end;
5176
+ ctx._fields?.push(triviaFieldCapture(name, result.value, input, result.span.start, end, leadingTrivia ? leadFrom : -1, trailEnd));
5177
+ return trailEnd === end ? result : { ok: true, value: result.value, span: { start: result.span.start, end: trailEnd } };
5159
5178
  }
5160
5179
  };
5161
5180
  }
@@ -5676,15 +5695,15 @@ function formatGatingWarnings(report) {
5676
5695
  return lines;
5677
5696
  }
5678
5697
  function ruleDependencies(ruleMap) {
5679
- const nameOf = /* @__PURE__ */ new Map();
5680
- for (const [name, comb] of ruleMap) nameOf.set(comb, name);
5698
+ const nameOf2 = /* @__PURE__ */ new Map();
5699
+ for (const [name, comb] of ruleMap) nameOf2.set(comb, name);
5681
5700
  const deps = /* @__PURE__ */ new Map();
5682
5701
  for (const [name, comb] of ruleMap) {
5683
5702
  const found = /* @__PURE__ */ new Set();
5684
5703
  const seen = /* @__PURE__ */ new Set();
5685
5704
  const walk = (p, isRoot) => {
5686
5705
  const def = p._def;
5687
- const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
5706
+ const boundary = def.tag === "lazy" ? nameOf2.get(p) ?? p._ruleName : nameOf2.get(p);
5688
5707
  if (!isRoot && boundary !== void 0) {
5689
5708
  found.add(boundary);
5690
5709
  return;
@@ -7593,6 +7612,16 @@ var Encoder = class {
7593
7612
  this.kIndex.set(key, i);
7594
7613
  return i;
7595
7614
  }
7615
+ fieldSpecIndex = /* @__PURE__ */ new Map();
7616
+ fieldSpecSlot(name, bits) {
7617
+ const key = JSON.stringify([name, bits]);
7618
+ const hit = this.fieldSpecIndex.get(key);
7619
+ if (hit !== void 0) return hit;
7620
+ const i = this.k.length;
7621
+ this.k.push([name, bits]);
7622
+ this.fieldSpecIndex.set(key, i);
7623
+ return i;
7624
+ }
7596
7625
  /**
7597
7626
  * A `notAdjacent({ kinds })` filter, pooled by its CONTENTS.
7598
7627
  *
@@ -8213,8 +8242,10 @@ var Encoder = class {
8213
8242
  for (let i = 0; i < arms.length; i++) this.code[head + 6 + i] = arms[i];
8214
8243
  return head;
8215
8244
  }
8216
- case "field":
8217
- return this.emit(OP_FIELD, this.constant(d.name), this.node(d.parser).ip);
8245
+ case "field": {
8246
+ const bits = (d.leadingTrivia === true ? FIELD_LEADING_TRIVIA : 0) | (d.trailingTrivia === true ? FIELD_TRAILING_TRIVIA : 0);
8247
+ return this.emit(OP_FIELD, bits === 0 ? this.constant(d.name) : this.fieldSpecSlot(d.name, bits), this.node(d.parser).ip);
8248
+ }
8218
8249
  case "lazy": {
8219
8250
  const refName = p._ruleName;
8220
8251
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
@@ -9890,6 +9921,26 @@ function leadingLiteralFamily(code, constants, ip, minDepth = 2, limit = 4) {
9890
9921
  };
9891
9922
  return walk(ip, 0);
9892
9923
  }
9924
+ function largeChoiceLiteralPretest(code, constants, armIp) {
9925
+ const child = leadingScalarTerminal(code, armIp, 2, true, true);
9926
+ if (child >= 0) {
9927
+ const spec = constants[code[child + 1]];
9928
+ if (code[child] === OP_LIT && typeof spec === "string" && spec.length >= 2) return [child];
9929
+ }
9930
+ const family = leadingLiteralFamily(code, constants, armIp);
9931
+ return family !== void 0 && family.length >= 2 && family.every((terminal) => {
9932
+ const value = constants[code[terminal + 1]];
9933
+ return typeof value === "string" && value.length >= 2;
9934
+ }) ? family : void 0;
9935
+ }
9936
+
9937
+ // src/table/field-spec.ts
9938
+ function fieldSpec(slot) {
9939
+ return Array.isArray(slot) ? { name: slot[0], bits: slot[1] } : { name: slot, bits: 0 };
9940
+ }
9941
+ function leadingTriviaFieldAt(code, k, ip) {
9942
+ return code[ip] === OP_FIELD && (fieldSpec(k[code[ip + 1]]).bits & FIELD_LEADING_TRIVIA) !== 0;
9943
+ }
9893
9944
 
9894
9945
  // src/table/emit-assembly.ts
9895
9946
  var Unemittable = class extends Error {
@@ -9964,7 +10015,11 @@ var EMITTED_PARAMS = [
9964
10015
  // Appended for old precompiled-factory ABI: old factories ignore it.
9965
10016
  "adjacencyHolds",
9966
10017
  // Appended for selected composite lexical programs; old factories ignore it.
9967
- "LEXPROG"
10018
+ "LEXPROG",
10019
+ // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
10020
+ // old factories ignore them.
10021
+ "LEAD",
10022
+ "triviaFieldCapture"
9968
10023
  ];
9969
10024
  var RUNTIME_PRELUDE = `
9970
10025
  let _pfScan=null
@@ -10224,20 +10279,12 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
10224
10279
  if (child >= 0) scalarSpecs.add(code[child + 1]);
10225
10280
  continue;
10226
10281
  }
10227
- if (child >= 0) {
10228
- const spec = k[code[child + 1]];
10229
- if (code[child] === OP_LIT && typeof spec === "string" && spec.length >= 2) {
10230
- largeChoiceScalarSpecs.add(code[child + 1]);
10231
- continue;
10232
- }
10233
- }
10234
- const family = leadingLiteralFamily(code, k, armIp);
10235
- if (family !== void 0 && family.length >= 2 && family.every((terminal) => {
10236
- const value = k[code[terminal + 1]];
10237
- return typeof value === "string" && value.length >= 2;
10238
- })) {
10239
- largeChoiceLiteralFamilies.set(armIp, family);
10282
+ const literals = largeChoiceLiteralPretest(code, k, armIp);
10283
+ if (literals !== void 0 && literals.length === 1 && literals[0] === child) {
10284
+ largeChoiceScalarSpecs.add(code[child + 1]);
10285
+ continue;
10240
10286
  }
10287
+ if (literals !== void 0) largeChoiceLiteralFamilies.set(armIp, literals);
10241
10288
  const sequence6 = leadingScalarSequence(code, armIp);
10242
10289
  if (sequence6 !== void 0 && (sequence6.trivia < 0 || swapLegal && t.triviaScan[sequence6.trivia] != null)) {
10243
10290
  largeChoiceScalarSequences.set(armIp, sequence6);
@@ -10661,14 +10708,31 @@ return v
10661
10708
  }`;
10662
10709
  }
10663
10710
  case OP_FIELD: {
10664
- const name = q(k[code[ip + 1]]);
10711
+ const spec = fieldSpec(k[code[ip + 1]]);
10712
+ const name = q(spec.name);
10665
10713
  const child = link(code[ip + 2]);
10666
- return `${head}
10714
+ if (spec.bits === 0) {
10715
+ return `${head}
10667
10716
  const v=${child}(input,pos,ctx)
10668
10717
  if(v===FAIL)return FAIL
10669
10718
  const f=ctx._fields
10670
10719
  if(f!==undefined)f.push({name:${name},value:v,span:{start:pos,end:EC.e}})
10671
10720
  return v
10721
+ }`;
10722
+ }
10723
+ const leading = (spec.bits & FIELD_LEADING_TRIVIA) !== 0;
10724
+ const trailing = (spec.bits & FIELD_TRAILING_TRIVIA) !== 0;
10725
+ return `${head}
10726
+ const lf=${leading ? "LEAD.at" : "-1"}
10727
+ LEAD.at=-1
10728
+ const v=${child}(input,pos,ctx)
10729
+ if(v===FAIL)return FAIL
10730
+ const e=EC.e
10731
+ const te=${trailing ? "ctx.trivia!==undefined?consumeTrivia(input,e,ctx):e" : "e"}
10732
+ const f=ctx._fields
10733
+ if(f!==undefined)f.push(triviaFieldCapture(${name},v,input,pos,e,lf,te))
10734
+ EC.e=te
10735
+ return v
10672
10736
  }`;
10673
10737
  }
10674
10738
  case OP_EXPECT: {
@@ -10964,7 +11028,10 @@ return v
10964
11028
  `if(!adjacencyHolds(input,cur,ctx,${negated ? "true" : "false"},${kinds})){ctx._fe=cur;ctx._fx=${expected};return FAIL}`,
10965
11029
  `${vn}=null`
10966
11030
  );
10967
- } else parts.push(emitTerm(kids[i], vn, tmp(), L, skipFor(L)));
11031
+ } else {
11032
+ const callee = leadingTriviaFieldAt(code, k, childIp) ? `(LEAD.at=cur,${kids[i]})` : kids[i];
11033
+ parts.push(emitTerm(callee, vn, tmp(), L, skipFor(L)));
11034
+ }
10968
11035
  names.push(vn);
10969
11036
  }
10970
11037
  parts.push("EC.e=cur");
@@ -11364,7 +11431,7 @@ let sp=cur
11364
11431
  ${hasTrivia === "false" ? "" : hasTrivia === "true" ? `sp=${skip}(input,sp,ctx)
11365
11432
  ` : `if(hasTrivia)sp=${skip}(input,sp,ctx)
11366
11433
  `}ctx._fc=false
11367
- const sv=${sep}(input,sp,ctx)
11434
+ ${sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp) ? "LEAD.at=cur\n" : ""}const sv=${sep}(input,sp,ctx)
11368
11435
  if(sv===FAIL){
11369
11436
  ${rb}
11370
11437
  if(ctx._fc===true)return FAIL
@@ -12474,6 +12541,12 @@ function assemble(t, prog, cfg) {
12474
12541
  rollbackScannedTriviaAt(ctx, mTl, scanTl, mLog, scanLog, mRoot, scanRoot);
12475
12542
  return cur;
12476
12543
  }
12544
+ function leadingFieldTerm(step, child, input, cur, ctx) {
12545
+ return step((i, p, c) => {
12546
+ LEAD.at = cur;
12547
+ return child(i, p, c);
12548
+ }, input, cur, ctx);
12549
+ }
12477
12550
  function committed3(c) {
12478
12551
  return c._fc === true;
12479
12552
  }
@@ -13111,12 +13184,27 @@ function assemble(t, prog, cfg) {
13111
13184
  };
13112
13185
  }
13113
13186
  case OP_FIELD: {
13114
- const name = k[code[ip + 1]];
13187
+ const { name, bits } = fieldSpec(k[code[ip + 1]]);
13115
13188
  const child = link(code[ip + 2]);
13189
+ if (bits === 0) {
13190
+ return (input, pos, ctx) => {
13191
+ const v = child(input, pos, ctx);
13192
+ if (v === FAIL) return FAIL;
13193
+ ctx._fields?.push({ name, value: v, span: { start: pos, end: EC.e } });
13194
+ return v;
13195
+ };
13196
+ }
13197
+ const leading = (bits & FIELD_LEADING_TRIVIA) !== 0;
13198
+ const trailing = (bits & FIELD_TRAILING_TRIVIA) !== 0;
13116
13199
  return (input, pos, ctx) => {
13200
+ const leadFrom = leading ? LEAD.at : -1;
13201
+ LEAD.at = -1;
13117
13202
  const v = child(input, pos, ctx);
13118
13203
  if (v === FAIL) return FAIL;
13119
- ctx._fields?.push({ name, value: v, span: { start: pos, end: EC.e } });
13204
+ const end = EC.e;
13205
+ const trailEnd = trailing && ctx.trivia !== void 0 ? consumeTrivia(input, end, ctx) : end;
13206
+ ctx._fields?.push(triviaFieldCapture(name, v, input, pos, end, leadFrom, trailEnd));
13207
+ EC.e = trailEnd;
13120
13208
  return v;
13121
13209
  };
13122
13210
  }
@@ -13550,6 +13638,12 @@ function assemble(t, prog, cfg) {
13550
13638
  const kidIp = code[base + i];
13551
13639
  if (code[kidIp] !== OP_ADJ) {
13552
13640
  const kid = link(kidIp);
13641
+ if (leadingTriviaFieldAt(code, k, kidIp)) {
13642
+ return (input, cur, ctx, inherited) => {
13643
+ ctx._sync = sync ?? inherited;
13644
+ return leadingFieldTerm(step, kid, input, cur, ctx);
13645
+ };
13646
+ }
13553
13647
  return (input, cur, ctx, inherited) => {
13554
13648
  ctx._sync = sync ?? inherited;
13555
13649
  return step(kid, input, cur, ctx);
@@ -13627,9 +13721,12 @@ function assemble(t, prog, cfg) {
13627
13721
  };
13628
13722
  }
13629
13723
  let hasAdj = false;
13630
- for (let i = 1; i < n; i++) if (code[code[base + i]] === OP_ADJ) {
13631
- hasAdj = true;
13632
- break;
13724
+ for (let i = 1; i < n; i++) {
13725
+ const kidIp = code[base + i];
13726
+ if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp)) {
13727
+ hasAdj = true;
13728
+ break;
13729
+ }
13633
13730
  }
13634
13731
  if (hasAdj) {
13635
13732
  const first2 = link(code[base]);
@@ -13637,6 +13734,9 @@ function assemble(t, prog, cfg) {
13637
13734
  const kidIp = code[base + i];
13638
13735
  if (code[kidIp] !== OP_ADJ) {
13639
13736
  const kid = link(kidIp);
13737
+ if (leadingTriviaFieldAt(code, k, kidIp)) {
13738
+ return (input, cur, ctx) => leadingFieldTerm(step, kid, input, cur, ctx);
13739
+ }
13640
13740
  return (input, cur, ctx) => step(kid, input, cur, ctx);
13641
13741
  }
13642
13742
  const negated = code[kidIp + 1] === 1;
@@ -13999,7 +14099,7 @@ function assemble(t, prog, cfg) {
13999
14099
  if (n === 2 || n === 3) {
14000
14100
  const a0 = link(code[base]), a1 = link(code[base + 1]);
14001
14101
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
14002
- const predecide = !hostCst && !REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines;
14102
+ const predecide = !REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines;
14003
14103
  const p0ip = predecide ? leadingScalarTerminal(code, code[base], 2, true, true) : -1;
14004
14104
  const p1ip = predecide ? leadingScalarTerminal(code, code[base + 1], 2, true, true) : -1;
14005
14105
  const p2ip = predecide && n === 3 ? leadingScalarTerminal(code, code[base + 2], 2, true, true) : -1;
@@ -14222,6 +14322,75 @@ function assemble(t, prog, cfg) {
14222
14322
  );
14223
14323
  };
14224
14324
  const masked = bindMasked(0, EMPTY_EXPECTED_PREFIX);
14325
+ const pBit = [];
14326
+ const pRec = [];
14327
+ if (!REC2 && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
14328
+ for (let i = 0; i < n; i++) {
14329
+ for (const terminal of largeChoiceLiteralPretest(code, k, code[base + i]) ?? []) {
14330
+ pBit.push(1 << i);
14331
+ pRec.push(scalarFor(terminal));
14332
+ }
14333
+ }
14334
+ }
14335
+ const pn = pBit.length;
14336
+ if (pn !== 0) {
14337
+ return (input, pos, ctx) => {
14338
+ const c = lead(input, pos);
14339
+ const need = markCst(ctx);
14340
+ const mRaw = MRAW;
14341
+ const mTl = MTL;
14342
+ const mLv = MLV;
14343
+ const mFl = need ? ctx._fields?.length ?? 0 : 0;
14344
+ const mEr = need ? ctx._errors?.length ?? 0 : 0;
14345
+ const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
14346
+ const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
14347
+ if (c < 128) {
14348
+ const bits = mask[c < 0 ? 128 : c];
14349
+ let tested = 0;
14350
+ let hit = 0;
14351
+ for (let i = 0; i < pn; i++) {
14352
+ const b = pBit[i];
14353
+ if ((bits & b) === 0 || (hit & b) !== 0) continue;
14354
+ tested |= b;
14355
+ if (pRec[i](input, pos) >= 0) hit |= b;
14356
+ }
14357
+ return masked(
14358
+ input,
14359
+ pos,
14360
+ ctx,
14361
+ bits & (~tested | hit),
14362
+ need,
14363
+ mRaw,
14364
+ mTl,
14365
+ mLv,
14366
+ mFl,
14367
+ mEr,
14368
+ mLog,
14369
+ mRoot,
14370
+ void 0,
14371
+ 0,
14372
+ pos
14373
+ );
14374
+ }
14375
+ return general(
14376
+ input,
14377
+ pos,
14378
+ ctx,
14379
+ c,
14380
+ need,
14381
+ mRaw,
14382
+ mTl,
14383
+ mLv,
14384
+ mFl,
14385
+ mEr,
14386
+ mLog,
14387
+ mRoot,
14388
+ void 0,
14389
+ 0,
14390
+ pos
14391
+ );
14392
+ };
14393
+ }
14225
14394
  return (input, pos, ctx) => {
14226
14395
  const c = lead(input, pos);
14227
14396
  const need = markCst(ctx);
@@ -14383,6 +14552,7 @@ function assemble(t, prog, cfg) {
14383
14552
  const max = code[ip + 3];
14384
14553
  const sepIp = code[ip + 4];
14385
14554
  const sep = sepIp >= 0 ? link(sepIp) : void 0;
14555
+ const sepLeads = sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp);
14386
14556
  const flags = code[ip + 5];
14387
14557
  const keepSeparators = (flags & 2) !== 0;
14388
14558
  const trailingAllowed = (flags & 1) !== 0;
@@ -14422,6 +14592,7 @@ function assemble(t, prog, cfg) {
14422
14592
  let sp = cur;
14423
14593
  if (hasTrivia) sp = skipTrivia(input, sp, ctx);
14424
14594
  ctx._fc = false;
14595
+ if (sepLeads) LEAD.at = cur;
14425
14596
  const sv = sep(input, sp, ctx);
14426
14597
  if (sv === FAIL) {
14427
14598
  if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
@@ -14511,6 +14682,7 @@ function assemble(t, prog, cfg) {
14511
14682
  let sp = cur;
14512
14683
  if (hasTrivia) sp = skipTrivia(input, sp, ctx);
14513
14684
  ctx._fc = false;
14685
+ if (sepLeads) LEAD.at = cur;
14514
14686
  const sv = sep(input, sp, ctx);
14515
14687
  if (sv === FAIL) {
14516
14688
  if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
@@ -14833,20 +15005,9 @@ function assemble(t, prog, cfg) {
14833
15005
  if (child >= 0) scalarFor(child);
14834
15006
  continue;
14835
15007
  }
14836
- if (child >= 0) {
14837
- const spec = k[code[child + 1]];
14838
- if (code[child] === OP_LIT && typeof spec === "string" && spec.length >= 2) {
14839
- scalarFor(child);
14840
- continue;
14841
- }
14842
- }
14843
- const family = leadingLiteralFamily(code, k, armIp);
14844
- if (family !== void 0 && family.length >= 2 && family.every((terminal) => {
14845
- const value = k[code[terminal + 1]];
14846
- return typeof value === "string" && value.length >= 2;
14847
- })) {
14848
- for (const terminal of family) scalarFor(terminal);
14849
- }
15008
+ const literals = largeChoiceLiteralPretest(code, k, armIp);
15009
+ if (literals !== void 0) for (const terminal of literals) scalarFor(terminal);
15010
+ if (literals !== void 0 && literals.length === 1 && literals[0] === child) continue;
14850
15011
  const sequence6 = leadingScalarSequence(code, armIp);
14851
15012
  if (sequence6 !== void 0 && (sequence6.trivia < 0 || triviaScan[sequence6.trivia] != null)) {
14852
15013
  for (const terminal of sequence6.terminals) scalarFor(terminal);
@@ -14912,7 +15073,9 @@ function assemble(t, prog, cfg) {
14912
15073
  scanTriviaCompact,
14913
15074
  t.lex,
14914
15075
  adjacencyHolds,
14915
- t.lexPrograms
15076
+ t.lexPrograms,
15077
+ LEAD,
15078
+ triviaFieldCapture
14916
15079
  );
14917
15080
  emitReached = new Set(pre.reached);
14918
15081
  }
@@ -14994,7 +15157,9 @@ function assemble(t, prog, cfg) {
14994
15157
  scanTriviaCompact,
14995
15158
  t.lex,
14996
15159
  adjacencyHolds,
14997
- t.lexPrograms
15160
+ t.lexPrograms,
15161
+ LEAD,
15162
+ triviaFieldCapture
14998
15163
  );
14999
15164
  emitReached = em.reached;
15000
15165
  } catch (e) {
@@ -15170,7 +15335,9 @@ var AssemblyCache = class {
15170
15335
  return made;
15171
15336
  }
15172
15337
  };
15173
- function tableRules(source, artifactMetadata = {}) {
15338
+ function tableRules(source, metadataOrEntry = {}) {
15339
+ const entry = typeof metadataOrEntry === "string" ? metadataOrEntry : void 0;
15340
+ const artifactMetadata = typeof metadataOrEntry === "string" ? {} : metadataOrEntry;
15174
15341
  const prog = expandCompact(source);
15175
15342
  const resolved = resolveTable(prog);
15176
15343
  const cache = new AssemblyCache(prog, resolved);
@@ -15178,7 +15345,7 @@ function tableRules(source, artifactMetadata = {}) {
15178
15345
  const skipOf = prog.scanSkipOf;
15179
15346
  let last;
15180
15347
  let selected;
15181
- return stampRuleMap(prog, {
15348
+ const map = stampRuleMap(prog, {
15182
15349
  runRule: (ri, input, pos, ctx) => {
15183
15350
  const a = selected ?? cache.forCtx(ctx);
15184
15351
  selected = void 0;
@@ -15199,6 +15366,9 @@ function tableRules(source, artifactMetadata = {}) {
15199
15366
  return a.scanSkip[skipOf?.[ri] ?? -1];
15200
15367
  }
15201
15368
  }, artifactMetadata, resolved);
15369
+ if (entry === void 0) return map;
15370
+ if (!Object.hasOwn(map, entry)) throw new Error(`tableRules: the program has no rule ${JSON.stringify(entry)}`);
15371
+ return map[entry];
15202
15372
  }
15203
15373
 
15204
15374
  // src/table/emit.ts
@@ -15364,11 +15534,15 @@ function emitTableExpression(prog, opts = {}) {
15364
15534
  const entry = opts.entry === void 0 ? "grammar" : opts.entry;
15365
15535
  const ref2 = opts.runtimeRef ?? "tableRules";
15366
15536
  const fns = opts.fnSources ?? prog.fns.map(() => "() => {}");
15367
- const close = `}${opts.metadataSource === void 0 ? "" : `,${opts.metadataSource}`})`;
15537
+ const meta = opts.metadataSource;
15538
+ if (entry !== null && meta !== void 0) {
15539
+ throw new Error("emitTableExpression: metadataSource is for the rule-map form (entry: null); a single entry carries none");
15540
+ }
15541
+ const tail = entry !== null ? `,${jsString(entry)}` : meta === void 0 ? "" : `,${meta}`;
15368
15542
  return [
15369
15543
  `/* @__PURE__ */ ${ref2}({`,
15370
15544
  ...programFields(prog, fns, opts),
15371
- entry === null ? close : `${close}[${jsString(entry)}]`
15545
+ `}${tail})`
15372
15546
  ].join("\n");
15373
15547
  }
15374
15548
 
@@ -15437,7 +15611,8 @@ function children(def, winners) {
15437
15611
  return [];
15438
15612
  }
15439
15613
  const name = resolved._ruleName;
15440
- return name && winners?.[name] ? [winners[name]] : [resolved];
15614
+ const winner = name ? winners?.[name] : void 0;
15615
+ return winner !== void 0 && winner._def !== def ? [winner] : [resolved];
15441
15616
  }
15442
15617
  default:
15443
15618
  return [];
@@ -15462,16 +15637,39 @@ function buildGrammarPlan(entry, winners) {
15462
15637
  }
15463
15638
  }
15464
15639
  }
15640
+ const selfWinnerBody = (parser2) => {
15641
+ if (parser2._def.tag !== "lazy" || winners === void 0) return void 0;
15642
+ const name = winnerNames.get(parser2);
15643
+ if (name === void 0 || winners[name] !== parser2) return void 0;
15644
+ try {
15645
+ return parser2._def.thunk();
15646
+ } catch {
15647
+ return void 0;
15648
+ }
15649
+ };
15650
+ const slotsPerBody = /* @__PURE__ */ new Map();
15651
+ if (winners) {
15652
+ for (const winner of Object.values(winners)) {
15653
+ const body = selfWinnerBody(winner);
15654
+ if (body !== void 0) slotsPerBody.set(body, (slotsPerBody.get(body) ?? 0) + 1);
15655
+ }
15656
+ }
15657
+ const sharedBody = (parser2) => (slotsPerBody.get(parser2) ?? 0) > 1;
15658
+ const ownRule2 = (parser2, rule) => {
15659
+ const id = `rule:${rule}`;
15660
+ definitions.set(id, { id, kind: "rule" });
15661
+ rules2.set(parser2, id);
15662
+ };
15465
15663
  const seen = /* @__PURE__ */ new Set();
15466
15664
  const visit = (parser2, path) => {
15467
15665
  if (seen.has(parser2)) return;
15468
15666
  seen.add(parser2);
15469
15667
  const rule = winnerNames.get(parser2) ?? parser2._ruleName;
15470
- if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
15471
- const id = `rule:${rule}`;
15472
- definitions.set(id, { id, kind: "rule" });
15473
- rules2.set(parser2, id);
15668
+ const body = selfWinnerBody(parser2);
15669
+ if (rule && body === void 0 && !sharedBody(parser2) && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
15670
+ ownRule2(parser2, rule);
15474
15671
  }
15672
+ if (rule && body !== void 0 && sharedBody(body)) ownRule2(parser2, rule);
15475
15673
  if (parser2._def.tag === "choice") {
15476
15674
  const ids = parser2._def.parsers.map((_, index) => `choice:${path}/arm:${index}`);
15477
15675
  choices.set(parser2, ids);
@@ -15492,7 +15690,13 @@ function buildGrammarPlan(entry, winners) {
15492
15690
  };
15493
15691
  const roots = Array.isArray(entry) ? entry : [entry];
15494
15692
  for (const root of roots) {
15495
- visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
15693
+ const name = winnerNames.get(root) ?? root._ruleName ?? "entry";
15694
+ const body = selfWinnerBody(root);
15695
+ if (body !== void 0 && sharedBody(body) && !seen.has(root)) {
15696
+ seen.add(root);
15697
+ ownRule2(root, name);
15698
+ }
15699
+ visit(body ?? root, name);
15496
15700
  }
15497
15701
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
15498
15702
  }
@@ -15516,7 +15720,7 @@ function payloadKey(p, d) {
15516
15720
  case "label":
15517
15721
  return `label\0${d.label}`;
15518
15722
  case "field":
15519
- return `field\0${d.name}`;
15723
+ return `field\0${d.name}\0${d.leadingTrivia === true}\0${d.trailingTrivia === true}`;
15520
15724
  case "node":
15521
15725
  return `node\0${d.type ?? ""}\0${d.unwrap === true}\0${d.collapse === true}\0${fnKey(d.buildSrc, d.build)}`;
15522
15726
  case "transform":
@@ -16798,104 +17002,298 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
16798
17002
  return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16799
17003
  }
16800
17004
 
16801
- // src/table/compile.ts
16802
- var ENTRY = "Entry";
16803
- function ownSource(fn) {
16804
- if (typeof fn !== "function") return null;
16805
- const text = Function.prototype.toString.call(fn);
16806
- if (!text.includes("[native code]")) return text;
16807
- const name = fn.name;
16808
- return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
17005
+ // src/table/compile-rule-map.ts
17006
+ var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
17007
+ function resolveHostMode(ruleMap, explicit) {
17008
+ return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
16809
17009
  }
16810
- function compile(combinator, mapFnSources, opts = {}) {
16811
- runDuplicationDiagnostic(combinator, opts.duplication);
16812
- const drain = beginCompileDegradationDrain();
16813
- let compiled = false;
16814
- try {
16815
- const out = compileImpl(combinator, mapFnSources, opts);
16816
- compiled = true;
16817
- return out;
16818
- } finally {
16819
- drain(compiled);
16820
- }
17010
+ function resolveTrackLines(ruleMap, explicit) {
17011
+ return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
16821
17012
  }
16822
- function compileImpl(combinator, mapFnSources, opts = {}) {
16823
- const plan = opts.coverage === true ? buildGrammarPlan(combinator) : void 0;
16824
- const settings = {
16825
- ...opts.hostMode === void 0 ? {} : { hostMode: opts.hostMode },
16826
- ...opts.trackLines === void 0 ? {} : { trackLines: opts.trackLines },
16827
- ...plan === void 0 ? {} : { coverage: plan }
16828
- };
16829
- const { prog, fnSrcs } = encodeTableProgram({ [ENTRY]: combinator }, settings);
17013
+ function withAmbient(ruleMap, opts) {
17014
+ if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
17015
+ const wrappers = /* @__PURE__ */ new Map();
17016
+ return ruleMap.map(([name, rule]) => {
17017
+ if (rule._meta.isTrivia) return [name, rule];
17018
+ const addTrivia = opts.trivia !== void 0 && rule._meta.grammarTrivia === void 0;
17019
+ const addScanSkip = opts.scanSkip !== void 0 && rule._meta.grammarScanSkip === void 0;
17020
+ if (!addTrivia && !addScanSkip) return [name, rule];
17021
+ let wrapped = wrappers.get(rule);
17022
+ if (wrapped === void 0) {
17023
+ wrapped = {
17024
+ ...rule,
17025
+ _meta: {
17026
+ ...rule._meta,
17027
+ ...addTrivia ? { grammarTrivia: opts.trivia } : {},
17028
+ ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
17029
+ }
17030
+ };
17031
+ wrappers.set(rule, wrapped);
17032
+ }
17033
+ return [name, wrapped];
17034
+ });
17035
+ }
17036
+ function compileRuleMapRunnable(ruleMap, opts = {}) {
17037
+ const encoded = encodeForRun(ruleMap, opts);
17038
+ if (encoded === null) return null;
17039
+ const { prog, hostMode, plan } = encoded;
16830
17040
  const artifact = closureArtifact(prog);
16831
- const entry = tableRules(prog)[ENTRY];
16832
- const offered = opts.fnSources ?? mapFnSources;
16833
- const supplied = offered !== void 0 && offered.length === fnSrcs.length ? offered : void 0;
16834
- const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? ownSource(prog.fns[i]));
16835
- const unsourced = sources.reduce((acc, s, i) => s === null ? [...acc, i] : acc, []);
16836
- const runtimeOnly = [
16837
- ...prog.runtimeOnly ?? [],
16838
- ...unsourced.length === 0 ? [] : [`author reducer source missing for prog.fns[${unsourced.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`]
16839
- ];
16840
- const printable = runtimeOnly.length === 0;
16841
- const emitOpts = { fnSources: sources };
16842
- const source = printable ? emitTableModule(artifact, { name: "grammar", ...emitOpts }) : "";
16843
- const inlineExpression = printable ? emitTableExpression(artifact, { entry: ENTRY, runtimeRef: opts.runtimeRef ?? "tableRules", ...emitOpts }) : null;
16844
- const runOnce2 = (input, pos, ctx) => entry(input, pos, ctx);
17041
+ const reflection = collectGrammarReflection(ruleMap);
16845
17042
  return {
16846
- source,
16847
- inlineExpression,
16848
- ...printable ? {} : { runtimeOnly },
16849
- // The DENOMINATOR, handed back rather than scraped out of the emitted text.
16850
- // `plugin/index.ts` prefers a compiler-supplied `coverageDefinitions` and only
16851
- // falls back to regex-scanning generated hooks; a table has no hooks to scan,
16852
- // so this is the channel.
17043
+ keys: ruleMap.map(([key]) => key),
17044
+ hostMode,
17045
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17046
+ reflection,
16853
17047
  ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16854
- parse(input, pos = 0) {
16855
- return runOnce2(input, pos, createParseContext());
16856
- },
16857
- parseWithContext(input, parseCtx, pos = 0) {
16858
- return runOnce2(input, pos, parseCtx);
16859
- },
16860
- /**
16861
- * NO LONGER REFUSES. It used to throw unless the caller passed
16862
- * `{ recovery: true }`, because setting `_tolerant` on a table with no
16863
- * recovery rows would report a clean parse with an empty error list whatever
16864
- * the input — refusing was the right answer to that, and OPTIONAL RECOVERY
16865
- * was the wrong question. `compile()` requires no such flag, so the refusal
16866
- * was a `CompiledParser` contract break; recovery is now always lowered and
16867
- * there is nothing left to refuse.
16868
- */
16869
- parseWithErrors(input, pos = 0) {
16870
- const ctx = createParseContext();
16871
- const errors = [];
16872
- ctx._errors = errors;
16873
- ctx._tolerant = true;
16874
- const r = runOnce2(input, pos, ctx);
16875
- return { ...r, errors };
16876
- }
17048
+ // Reflection rides on the map's metadata prototype from birth. Only `compile()`
17049
+ // asks to specialise; every other caller runs the closure artifact.
17050
+ rules: tableRules(opts.specialise === true ? prog : artifact, { [GRAMMAR_REFLECTION]: reflection }),
17051
+ prog: artifact
16877
17052
  };
16878
17053
  }
16879
-
16880
- // src/combinators/withCtx.ts
16881
- function withCtx(extra, combinator) {
16882
- const meta = {
16883
- firstSet: combinator._meta.firstSet,
16884
- canMatchNewline: combinator._meta.canMatchNewline,
16885
- isTrivia: false
16886
- };
16887
- return {
16888
- _tag: "withCtx",
16889
- _meta: meta,
16890
- _def: { tag: "withCtx", extra, parser: combinator },
16891
- parse(input, pos, ctx) {
16892
- const saved = ctx.state;
16893
- ctx.state = extra;
16894
- try {
16895
- return combinator.parse(input, pos, ctx);
16896
- } finally {
16897
- ctx.state = saved;
17054
+ function encodeForRun(ruleMap, opts) {
17055
+ runDuplicationDiagnosticRules(ruleMap, opts.duplication);
17056
+ const encodeMap = withAmbient(ruleMap, opts);
17057
+ const plan = opts.coverage === true ? buildGrammarPlan(
17058
+ encodeMap.map(([, rule]) => rule),
17059
+ Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== "lazy"))
17060
+ ) : void 0;
17061
+ const hostMode = resolveHostMode(ruleMap, opts.hostMode);
17062
+ const settings = {
17063
+ hostMode,
17064
+ ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
17065
+ ...opts.recovery === true ? { recovery: true } : {},
17066
+ ...plan === void 0 ? {} : { coverage: plan }
17067
+ };
17068
+ try {
17069
+ const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
17070
+ return { prog, fnSrcs, hostMode, plan };
17071
+ } catch (e) {
17072
+ opts.refusals?.push(e instanceof Error ? e.message : String(e));
17073
+ return null;
17074
+ }
17075
+ }
17076
+ function compileRuleMap(ruleMap, opts = {}) {
17077
+ const encoded = encodeForRun(ruleMap, opts);
17078
+ if (encoded === null) return null;
17079
+ const { prog, fnSrcs, hostMode, plan } = encoded;
17080
+ const artifact = closureArtifact(prog);
17081
+ if (artifact.runtimeOnly !== void 0 && artifact.runtimeOnly.length > 0) {
17082
+ opts.refusals?.push(...artifact.runtimeOnly);
17083
+ return null;
17084
+ }
17085
+ const supplied = opts.fnSources;
17086
+ const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null);
17087
+ if (sources.some((s) => s === null)) {
17088
+ const missing = sources.flatMap((s, i) => s === null ? [i] : []);
17089
+ opts.refusals?.push(
17090
+ `author reducer source missing for prog.fns[${missing.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`
17091
+ );
17092
+ return null;
17093
+ }
17094
+ if (supplied !== void 0 && supplied.length > fnSrcs.length) {
17095
+ throw new Error(
17096
+ `compileRuleMap: got ${supplied.length} fnSources for a pool of ${fnSrcs.length}. The list is positional, in prog.fns order, so a longer one means it belongs to a different encode \u2014 and the entries would be silently misassigned.`
17097
+ );
17098
+ }
17099
+ const replacementWithMetadata = (metadataSource, options = {}) => emitTableExpression(artifact, {
17100
+ entry: null,
17101
+ runtimeRef: opts.runtimeRef ?? "tableRules",
17102
+ fnSources: sources,
17103
+ ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
17104
+ ...metadataSource === void 0 ? {} : { metadataSource }
17105
+ });
17106
+ const replacement = replacementWithMetadata();
17107
+ return {
17108
+ keys: ruleMap.map(([key]) => key),
17109
+ replacement,
17110
+ replacementWithMetadata,
17111
+ hostMode,
17112
+ // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
17113
+ // dropped", which is what the driver's `'ast' artifact + CST host` check keys
17114
+ // off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
17115
+ // direct builder, so there was no branch to drop and it stays usable with either
17116
+ // host — the long-standing `node(parser)` contract. Same predicate codegen uses
17117
+ // for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
17118
+ // lowerings stamp the same artifact rather than disagreeing about it.
17119
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17120
+ reflection: collectGrammarReflection(ruleMap),
17121
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17122
+ rules: tableRules(artifact),
17123
+ prog: artifact
17124
+ };
17125
+ }
17126
+
17127
+ // src/compiler/borrowed-rules.ts
17128
+ var nameOf = (c) => c._ruleName;
17129
+ function deref(c) {
17130
+ const seen = /* @__PURE__ */ new Set();
17131
+ let current = c;
17132
+ while (current._def.tag === "lazy" && !seen.has(current)) {
17133
+ seen.add(current);
17134
+ try {
17135
+ current = current._def.thunk();
17136
+ } catch {
17137
+ return void 0;
17138
+ }
17139
+ }
17140
+ return current;
17141
+ }
17142
+ function borrowedRuleReference(map) {
17143
+ const seen = /* @__PURE__ */ new Set();
17144
+ for (const [rule, self] of Object.entries(map)) {
17145
+ let found;
17146
+ const visit = (c) => {
17147
+ if (found !== void 0) return;
17148
+ if (c._def.tag === "lazy") {
17149
+ if (!winnerWrapsReference(self, c)) {
17150
+ const name = nameOf(c);
17151
+ const winner = name !== void 0 && Object.hasOwn(map, name) ? map[name] : void 0;
17152
+ if (winner !== void 0) {
17153
+ if (winnerWrapsReference(winner, c) || deref(c) === deref(winner)) return;
17154
+ found = name;
17155
+ return;
17156
+ }
17157
+ }
17158
+ if (seen.has(c)) return;
17159
+ seen.add(c);
17160
+ let body;
17161
+ try {
17162
+ body = c._def.thunk();
17163
+ } catch {
17164
+ return;
17165
+ }
17166
+ visit(body);
17167
+ return;
16898
17168
  }
17169
+ if (seen.has(c)) return;
17170
+ seen.add(c);
17171
+ for (const child of childrenOf2(c._def)) visit(child);
17172
+ };
17173
+ visit(self);
17174
+ if (found !== void 0) return { rule, reference: found };
17175
+ }
17176
+ return void 0;
17177
+ }
17178
+ function borrowedRuleMessage(api, hit) {
17179
+ return `${api}: rule "${hit.rule}" is a rule object borrowed from another grammar, and its graph references "${hit.reference}", which this grammar also defines. The interpreter would keep the other grammar's "${hit.reference}" while a compiled table binds this grammar's, so the two would accept different input. Compose the grammar that defines "${hit.rule}" instead of borrowing its rule object, so its references resolve to this grammar's rules.`;
17180
+ }
17181
+
17182
+ // src/table/compile.ts
17183
+ var ENTRY = "Entry";
17184
+ function ownSource(fn) {
17185
+ if (typeof fn !== "function") return null;
17186
+ const text = Function.prototype.toString.call(fn);
17187
+ if (!text.includes("[native code]")) return text;
17188
+ const name = fn.name;
17189
+ return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
17190
+ }
17191
+ function compile(target, second, third) {
17192
+ if (!isCombinator(target)) return compileMap(target, second);
17193
+ return compileRoot(target, second, third);
17194
+ }
17195
+ function isCombinator(x) {
17196
+ return typeof x === "object" && x !== null && typeof x.parse === "function" && "_def" in x;
17197
+ }
17198
+ function compileMap(grammar, opts = {}) {
17199
+ const entries = [];
17200
+ for (const [key, value] of Object.entries(grammar)) {
17201
+ if (!isCombinator(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
17202
+ if (value._def.tag === "lazy") {
17203
+ try {
17204
+ value._def.thunk();
17205
+ } catch {
17206
+ continue;
17207
+ }
17208
+ }
17209
+ entries.push([key, value]);
17210
+ }
17211
+ const borrowed = borrowedRuleReference(Object.fromEntries(entries));
17212
+ if (borrowed !== void 0) throw new Error(borrowedRuleMessage("compile", borrowed));
17213
+ const drain = beginCompileDegradationDrain();
17214
+ let done = false;
17215
+ try {
17216
+ const refusals = [];
17217
+ const compiled = compileRuleMapRunnable(entries, {
17218
+ ...opts.hostMode ? { hostMode: opts.hostMode } : {},
17219
+ refusals,
17220
+ specialise: true
17221
+ });
17222
+ if (compiled === null) {
17223
+ throw new Error(`compile: this grammar could not be encoded to a table${refusals.length ? ` \u2014 ${refusals.join("; ")}` : ""}`);
17224
+ }
17225
+ done = true;
17226
+ return compiled.rules;
17227
+ } finally {
17228
+ drain(done);
17229
+ }
17230
+ }
17231
+ function compileRoot(combinator, mapFnSources, opts = {}) {
17232
+ runDuplicationDiagnostic(combinator, opts.duplication);
17233
+ const drain = beginCompileDegradationDrain();
17234
+ let compiled = false;
17235
+ try {
17236
+ const out = compileImpl(combinator, mapFnSources, opts);
17237
+ compiled = true;
17238
+ return out;
17239
+ } finally {
17240
+ drain(compiled);
17241
+ }
17242
+ }
17243
+ function compileImpl(combinator, mapFnSources, opts = {}) {
17244
+ const plan = opts.coverage === true ? buildGrammarPlan(combinator) : void 0;
17245
+ const settings = {
17246
+ ...opts.hostMode === void 0 ? {} : { hostMode: opts.hostMode },
17247
+ ...opts.trackLines === void 0 ? {} : { trackLines: opts.trackLines },
17248
+ ...plan === void 0 ? {} : { coverage: plan }
17249
+ };
17250
+ const { prog, fnSrcs } = encodeTableProgram({ [ENTRY]: combinator }, settings);
17251
+ const artifact = closureArtifact(prog);
17252
+ const entry = tableRules(prog)[ENTRY];
17253
+ const offered = opts.fnSources ?? mapFnSources;
17254
+ const supplied = offered !== void 0 && offered.length === fnSrcs.length ? offered : void 0;
17255
+ const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? ownSource(prog.fns[i]));
17256
+ const unsourced = sources.reduce((acc, s, i) => s === null ? [...acc, i] : acc, []);
17257
+ const runtimeOnly = [
17258
+ ...prog.runtimeOnly ?? [],
17259
+ ...unsourced.length === 0 ? [] : [`author reducer source missing for prog.fns[${unsourced.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`]
17260
+ ];
17261
+ const printable = runtimeOnly.length === 0;
17262
+ const emitOpts = { fnSources: sources };
17263
+ const source = printable ? emitTableModule(artifact, { name: "grammar", ...emitOpts }) : "";
17264
+ const inlineExpression = printable ? emitTableExpression(artifact, { entry: ENTRY, runtimeRef: opts.runtimeRef ?? "tableRules", ...emitOpts }) : null;
17265
+ const runOnce2 = (input, pos, ctx) => entry(input, pos, ctx);
17266
+ return {
17267
+ source,
17268
+ inlineExpression,
17269
+ ...printable ? {} : { runtimeOnly },
17270
+ // The DENOMINATOR, handed back rather than scraped out of the emitted text.
17271
+ // `plugin/index.ts` prefers a compiler-supplied `coverageDefinitions` and only
17272
+ // falls back to regex-scanning generated hooks; a table has no hooks to scan,
17273
+ // so this is the channel.
17274
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17275
+ parse(input, pos = 0) {
17276
+ return runOnce2(input, pos, createParseContext());
17277
+ },
17278
+ parseWithContext(input, parseCtx, pos = 0) {
17279
+ return runOnce2(input, pos, parseCtx);
17280
+ },
17281
+ /**
17282
+ * NO LONGER REFUSES. It used to throw unless the caller passed
17283
+ * `{ recovery: true }`, because setting `_tolerant` on a table with no
17284
+ * recovery rows would report a clean parse with an empty error list whatever
17285
+ * the input — refusing was the right answer to that, and OPTIONAL RECOVERY
17286
+ * was the wrong question. `compile()` requires no such flag, so the refusal
17287
+ * was a `CompiledParser` contract break; recovery is now always lowered and
17288
+ * there is nothing left to refuse.
17289
+ */
17290
+ parseWithErrors(input, pos = 0) {
17291
+ const ctx = createParseContext();
17292
+ const errors = [];
17293
+ ctx._errors = errors;
17294
+ ctx._tolerant = true;
17295
+ const r = runOnce2(input, pos, ctx);
17296
+ return { ...r, errors };
16899
17297
  }
16900
17298
  };
16901
17299
  }
@@ -16996,176 +17394,6 @@ function serializeRuleMap(ruleMap, scanSkip) {
16996
17394
  throw e;
16997
17395
  }
16998
17396
  }
16999
- function evalRuleMapIR(ir) {
17000
- const _tf = (child, src, recognitionOnly = false) => {
17001
- let fn2;
17002
- try {
17003
- fn2 = (0, eval)(`(${src})`);
17004
- } catch {
17005
- fn2 = () => {
17006
- throw new Error("IR transform fn not materialized");
17007
- };
17008
- }
17009
- const t = transform(child, fn2);
17010
- t._def.fnSrc = src;
17011
- if (recognitionOnly) t._def.recognitionOnly = true;
17012
- return t;
17013
- };
17014
- const _lf = (child, src, source = false) => {
17015
- let fn2;
17016
- try {
17017
- fn2 = (0, eval)(`(${src})`);
17018
- } catch {
17019
- fn2 = () => {
17020
- throw new Error("IR leaf fn not materialized");
17021
- };
17022
- }
17023
- const l = source ? sourceLeaf(child, fn2) : leaf(child, fn2);
17024
- l._def.fnSrc = src;
17025
- return l;
17026
- };
17027
- const _nd = (type, child, src, opts, staticError, sigSrc, buildImports, rawUnused) => {
17028
- if (staticError !== void 0 && staticError.length > 0) {
17029
- throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
17030
- }
17031
- const n = node(type, child, (() => {
17032
- throw new Error("IR node build requires static re-lowering");
17033
- }), opts);
17034
- n._def.buildSrc = src;
17035
- if (staticError !== void 0) n._def.buildStaticError = staticError;
17036
- if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
17037
- if (rawUnused === true) n._def.buildRawUnused = true;
17038
- if (buildImports !== void 0) n._def.buildImports = buildImports;
17039
- return n;
17040
- };
17041
- const _gch = (items) => {
17042
- const gateSrcs = [];
17043
- const arms = items.map((it) => {
17044
- if (Array.isArray(it)) {
17045
- const [src, comb] = it;
17046
- gateSrcs.push(src);
17047
- let gate2;
17048
- try {
17049
- gate2 = (0, eval)(`(${src})`);
17050
- } catch {
17051
- gate2 = () => {
17052
- throw new Error("IR gate fn not materialized");
17053
- };
17054
- }
17055
- return { gate: gate2, combinator: comb };
17056
- }
17057
- gateSrcs.push(null);
17058
- return it;
17059
- });
17060
- const c = choice(...arms);
17061
- const gates = c._def.gates;
17062
- if (gates && gates.length !== gateSrcs.length) {
17063
- throw new Error(`_gch: gateSrcs length ${gateSrcs.length} != choice gates length ${gates.length}`);
17064
- }
17065
- ;
17066
- c._def.gateSrcs = gateSrcs;
17067
- return c;
17068
- };
17069
- const _wc = (src, inner) => {
17070
- let extra;
17071
- try {
17072
- extra = (0, eval)(`(${src})`);
17073
- } catch {
17074
- extra = void 0;
17075
- }
17076
- const w = withCtx(extra, inner);
17077
- w._def.extraSrc = src;
17078
- return w;
17079
- };
17080
- const fn = new Function(
17081
- "rules",
17082
- "ref",
17083
- "regex",
17084
- "literal",
17085
- "keywords",
17086
- "sequence",
17087
- "choice",
17088
- "dispatch",
17089
- "when",
17090
- "startsWith",
17091
- "endsWith",
17092
- "matches",
17093
- "otherwise",
17094
- "routed",
17095
- "attempt",
17096
- "many",
17097
- "oneOrMore",
17098
- "optional",
17099
- "sepBy",
17100
- "keepSeparator",
17101
- "not",
17102
- "peek",
17103
- "node",
17104
- "parser",
17105
- "scanTo",
17106
- "balanced",
17107
- "token",
17108
- "leaf",
17109
- "transform",
17110
- "trivia",
17111
- "classifiedTrivia",
17112
- "label",
17113
- "field",
17114
- "expect",
17115
- "adjacent",
17116
- "notAdjacent",
17117
- "_tf",
17118
- "_lf",
17119
- "_nd",
17120
- "_gch",
17121
- "_wc",
17122
- `return (${ir})`
17123
- );
17124
- const map = fn(
17125
- rules,
17126
- ref,
17127
- regex,
17128
- literal,
17129
- keywords,
17130
- sequence,
17131
- choice,
17132
- dispatch,
17133
- when,
17134
- startsWith,
17135
- endsWith,
17136
- matches,
17137
- otherwise,
17138
- routed,
17139
- attempt,
17140
- many,
17141
- oneOrMore,
17142
- optional,
17143
- sepBy,
17144
- keepSeparator,
17145
- not,
17146
- peek,
17147
- node,
17148
- parser,
17149
- scanTo,
17150
- balanced,
17151
- token,
17152
- leaf,
17153
- transform,
17154
- trivia,
17155
- classifiedTrivia,
17156
- label,
17157
- field,
17158
- expect,
17159
- adjacent,
17160
- notAdjacent,
17161
- _tf,
17162
- _lf,
17163
- _nd,
17164
- _gch,
17165
- _wc
17166
- );
17167
- return Object.entries(map);
17168
- }
17169
17397
  var Serializer = class {
17170
17398
  counts = /* @__PURE__ */ new Map();
17171
17399
  selfRef = /* @__PURE__ */ new Set();
@@ -17428,7 +17656,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17428
17656
  case "label":
17429
17657
  return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
17430
17658
  case "field":
17431
- return `field(${JSON.stringify(def.name)}, ${kid(def.parser)})`;
17659
+ return `field(${JSON.stringify(def.name)}, ${kid(def.parser)}${fieldOptionsSrc(def)})`;
17432
17660
  case "expect":
17433
17661
  return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
17434
17662
  case "scanTo":
@@ -17491,128 +17719,13 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17491
17719
  }
17492
17720
  }
17493
17721
  };
17494
-
17495
- // src/table/compile-rule-map.ts
17496
- var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
17497
- function resolveHostMode(ruleMap, explicit) {
17498
- return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
17499
- }
17500
- function resolveTrackLines(ruleMap, explicit) {
17501
- return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
17502
- }
17503
- function withAmbient(ruleMap, opts) {
17504
- if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
17505
- const wrappers = /* @__PURE__ */ new Map();
17506
- return ruleMap.map(([name, rule]) => {
17507
- if (rule._meta.isTrivia) return [name, rule];
17508
- const addTrivia = opts.trivia !== void 0 && rule._meta.grammarTrivia === void 0;
17509
- const addScanSkip = opts.scanSkip !== void 0 && rule._meta.grammarScanSkip === void 0;
17510
- if (!addTrivia && !addScanSkip) return [name, rule];
17511
- let wrapped = wrappers.get(rule);
17512
- if (wrapped === void 0) {
17513
- wrapped = {
17514
- ...rule,
17515
- _meta: {
17516
- ...rule._meta,
17517
- ...addTrivia ? { grammarTrivia: opts.trivia } : {},
17518
- ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
17519
- }
17520
- };
17521
- wrappers.set(rule, wrapped);
17522
- }
17523
- return [name, wrapped];
17524
- });
17525
- }
17526
- function compileRuleMapRunnable(ruleMap, opts = {}) {
17527
- const encoded = encodeForRun(ruleMap, opts);
17528
- if (encoded === null) return null;
17529
- const { prog, hostMode, plan } = encoded;
17530
- const artifact = closureArtifact(prog);
17531
- return {
17532
- keys: ruleMap.map(([key]) => key),
17533
- hostMode,
17534
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17535
- reflection: collectGrammarReflection(ruleMap),
17536
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17537
- rules: tableRules(prog),
17538
- prog: artifact
17539
- };
17540
- }
17541
- function encodeForRun(ruleMap, opts) {
17542
- runDuplicationDiagnosticRules(ruleMap, opts.duplication);
17543
- const encodeMap = withAmbient(ruleMap, opts);
17544
- const plan = opts.coverage === true ? buildGrammarPlan(
17545
- encodeMap.map(([, rule]) => rule),
17546
- Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== "lazy"))
17547
- ) : void 0;
17548
- const hostMode = resolveHostMode(ruleMap, opts.hostMode);
17549
- const settings = {
17550
- hostMode,
17551
- ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
17552
- ...opts.recovery === true ? { recovery: true } : {},
17553
- ...plan === void 0 ? {} : { coverage: plan }
17554
- };
17555
- try {
17556
- const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
17557
- return { prog, fnSrcs, hostMode, plan };
17558
- } catch (e) {
17559
- opts.refusals?.push(e instanceof Error ? e.message : String(e));
17560
- return null;
17561
- }
17562
- }
17563
- function compileRuleMap(ruleMap, opts = {}) {
17564
- const encoded = encodeForRun(ruleMap, opts);
17565
- if (encoded === null) return null;
17566
- const { prog, fnSrcs, hostMode, plan } = encoded;
17567
- const artifact = closureArtifact(prog);
17568
- if (artifact.runtimeOnly !== void 0 && artifact.runtimeOnly.length > 0) {
17569
- opts.refusals?.push(...artifact.runtimeOnly);
17570
- return null;
17571
- }
17572
- const supplied = opts.fnSources;
17573
- const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null);
17574
- if (sources.some((s) => s === null)) {
17575
- const missing = sources.flatMap((s, i) => s === null ? [i] : []);
17576
- opts.refusals?.push(
17577
- `author reducer source missing for prog.fns[${missing.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`
17578
- );
17579
- return null;
17580
- }
17581
- if (supplied !== void 0 && supplied.length > fnSrcs.length) {
17582
- throw new Error(
17583
- `compileRuleMap: got ${supplied.length} fnSources for a pool of ${fnSrcs.length}. The list is positional, in prog.fns order, so a longer one means it belongs to a different encode \u2014 and the entries would be silently misassigned.`
17584
- );
17585
- }
17586
- const replacementWithMetadata = (metadataSource, options = {}) => emitTableExpression(artifact, {
17587
- entry: null,
17588
- runtimeRef: opts.runtimeRef ?? "tableRules",
17589
- fnSources: sources,
17590
- ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
17591
- ...metadataSource === void 0 ? {} : { metadataSource }
17592
- });
17593
- const replacement = replacementWithMetadata();
17594
- return {
17595
- keys: ruleMap.map(([key]) => key),
17596
- replacement,
17597
- replacementWithMetadata,
17598
- hostMode,
17599
- // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
17600
- // dropped", which is what the driver's `'ast' artifact + CST host` check keys
17601
- // off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
17602
- // direct builder, so there was no branch to drop and it stays usable with either
17603
- // host — the long-standing `node(parser)` contract. Same predicate codegen uses
17604
- // for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
17605
- // lowerings stamp the same artifact rather than disagreeing about it.
17606
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17607
- reflection: collectGrammarReflection(ruleMap),
17608
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17609
- rules: tableRules(prog),
17610
- prog: artifact
17611
- };
17722
+ function fieldOptionsSrc(def) {
17723
+ if (def.leadingTrivia !== true && def.trailingTrivia !== true) return "";
17724
+ return `, { ${[def.leadingTrivia === true ? "leadingTrivia: true" : "", def.trailingTrivia === true ? "trailingTrivia: true" : ""].filter(Boolean).join(", ")} }`;
17612
17725
  }
17613
17726
 
17614
17727
  // src/version.ts
17615
- var PARSEMAN_VERSION = "0.52.0";
17728
+ var PARSEMAN_VERSION = "0.54.0";
17616
17729
 
17617
17730
  // src/compiler/compile-linkable-table.ts
17618
17731
  function isLocal(rule) {
@@ -17680,16 +17793,6 @@ function compileLinkableTable(ruleMapArg, ns, opts = {}) {
17680
17793
  }
17681
17794
 
17682
17795
  // src/compiler/linker.ts
17683
- var _nsCounter = 0;
17684
- function linkable(rulesMap, ns, trivia2, hostMode) {
17685
- const piece = compileLinkableTable(
17686
- Object.entries(rulesMap),
17687
- ns ?? `_lk${_nsCounter++}_`,
17688
- { ...trivia2 ? { trivia: trivia2 } : {}, ...hostMode ? { hostMode } : {} }
17689
- );
17690
- if (!piece) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
17691
- return piece;
17692
- }
17693
17796
  function normalizeCstCollapse(collapse) {
17694
17797
  if (collapse === true) return () => true;
17695
17798
  if (Array.isArray(collapse)) {
@@ -17713,107 +17816,6 @@ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triv
17713
17816
  return host;
17714
17817
  }
17715
17818
  cstBuildHost._parsemanCstOutput = true;
17716
- function fuseCarried(carried, trivia2, hostMode) {
17717
- const maps = [];
17718
- for (const p of carried) {
17719
- if (!isIRPiece(p)) {
17720
- if (typeof p.v !== "string") {
17721
- throw new Error(
17722
- `parseman: artifact "${p.ns}" is UNSTAMPED (compiled before the version-lock invariant). Recompile the grammar with parseman ${PARSEMAN_VERSION}; parseman does not fuse unversioned or cross-version artifacts.`
17723
- );
17724
- }
17725
- if (p.v !== PARSEMAN_VERSION) {
17726
- throw new Error(
17727
- `parseman: artifact "${p.ns}" was compiled with parseman ${p.v}, but is being fused with parseman ${PARSEMAN_VERSION}. Compiled grammar artifacts are version-locked \u2014 recompile the grammar with parseman ${PARSEMAN_VERSION}; parseman does not fuse across versions.`
17728
- );
17729
- }
17730
- }
17731
- const rules2 = ruleMapOfCarried(p);
17732
- if (rules2 === void 0) {
17733
- throw new Error(`compose: carried piece "${p.ns}" has no re-lowerable IR and cannot be fused`);
17734
- }
17735
- maps.push(rules2);
17736
- }
17737
- const merged = mergeCarriedRuleMaps(maps);
17738
- materializeDirectBuilders(merged);
17739
- if (trivia2) {
17740
- for (const [, rule] of merged) {
17741
- if (rule._meta.isTrivia) continue;
17742
- rule._meta.grammarTrivia = trivia2;
17743
- }
17744
- }
17745
- const refusals = [];
17746
- const compiled = compileRuleMapRunnable(merged, {
17747
- ...trivia2 ? { trivia: trivia2 } : {},
17748
- ...hostMode ? { hostMode } : {},
17749
- refusals
17750
- });
17751
- if (compiled === null) {
17752
- throw new Error(`compose: the merged grammar could not be encoded to a table${refusals.length ? ` \u2014 ${refusals.join("; ")}` : ""}`);
17753
- }
17754
- const map = tableRules(compiled.prog);
17755
- for (const k of Object.keys(map)) {
17756
- Object.defineProperty(map[k], FUSED_HOST_MODE, { value: compiled.hostMode, enumerable: false });
17757
- Object.defineProperty(map[k], FUSED_HOST_ELIDED, { value: compiled.hostBranchElided, enumerable: false });
17758
- }
17759
- Object.defineProperty(map, FUSED_HOST_MODE, { value: compiled.hostMode, enumerable: false });
17760
- Object.defineProperty(map, FUSED_HOST_ELIDED, { value: compiled.hostBranchElided, enumerable: false });
17761
- Object.defineProperty(map, GRAMMAR_REFLECTION, { value: compiled.reflection, enumerable: false });
17762
- return map;
17763
- }
17764
- function materializeDirectBuilders(ruleMap) {
17765
- const seen = /* @__PURE__ */ new Set();
17766
- const visit = (p) => {
17767
- if (seen.has(p)) return;
17768
- seen.add(p);
17769
- const d = p._def;
17770
- if (d.tag === "node" && typeof d.buildSrc === "string") {
17771
- if (d.buildImports !== void 0 && d.buildImports.length > 0) {
17772
- throw new Error(
17773
- `IR direct node builder for ${d.type ?? "<anonymous>"} references module import(s) ${d.buildImports.map((bi) => bi.local).join(", ")} that a runtime compose() cannot supply; compose at build time via the parseman macro plugin, which re-emits the imports.`
17774
- );
17775
- }
17776
- try {
17777
- const fn = (0, eval)(`(${d.buildSrc})`);
17778
- if (typeof fn === "function") d.build = fn;
17779
- } catch {
17780
- }
17781
- }
17782
- if (d.tag === "lazy") {
17783
- let resolved;
17784
- try {
17785
- resolved = d.thunk();
17786
- } catch {
17787
- return;
17788
- }
17789
- if (resolved) visit(resolved);
17790
- return;
17791
- }
17792
- for (const child of childrenOf2(p._def)) visit(child);
17793
- };
17794
- for (const [, rule] of ruleMap) visit(rule);
17795
- }
17796
- function ruleMapOfCarried(p) {
17797
- if (isIRPiece(p)) return evalRuleMapIR(p.ir);
17798
- if (p.ir !== null) return evalRuleMapIR(p.ir);
17799
- return p.ruleMap.length > 0 ? p.ruleMap.map(([k, v]) => [k, v]) : void 0;
17800
- }
17801
- function mergeCarriedRuleMaps(maps) {
17802
- const winners = /* @__PURE__ */ new Map();
17803
- for (const map of maps) {
17804
- for (const [name, rule] of map) {
17805
- if (rule._def.tag === "lazy") {
17806
- try {
17807
- rule._def.thunk();
17808
- } catch {
17809
- continue;
17810
- }
17811
- }
17812
- winners.set(name, rule);
17813
- }
17814
- }
17815
- return [...winners];
17816
- }
17817
17819
  function fusedHostModeOf(registry) {
17818
17820
  const m = registry[FUSED_HOST_MODE];
17819
17821
  return m === "cst" ? "cst" : "ast";
@@ -17828,13 +17830,17 @@ function composedPiecesOf(grammar) {
17828
17830
  }
17829
17831
  var LEAF_COMPOSED = /* @__PURE__ */ Symbol.for("parseman.leafComposed");
17830
17832
  var COMPOSED_TRIVIA = /* @__PURE__ */ Symbol.for("parseman.composedTrivia");
17831
- var COMPOSED_COVERAGE_RULES = /* @__PURE__ */ Symbol.for("parseman.composedCoverageRules");
17833
+ var LINKED = /* @__PURE__ */ Symbol.for("parseman.linkedComposition");
17832
17834
  function isIRPiece(p) {
17833
17835
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("keys" in p);
17834
17836
  }
17835
17837
  function isLinkableTable(p) {
17836
17838
  return !!p && typeof p === "object" && typeof p.ns === "string" && Array.isArray(p.keys);
17837
17839
  }
17840
+ function ruleMapOfCarried(p) {
17841
+ if (isIRPiece(p)) return void 0;
17842
+ return p.ruleMap.length > 0 ? p.ruleMap.map(([k, v]) => [k, v]) : void 0;
17843
+ }
17838
17844
  function carriedRuleMapsDetailed(carried) {
17839
17845
  const maps = [];
17840
17846
  const opaque = [];
@@ -17865,250 +17871,97 @@ function recoverComposedRules(grammar) {
17865
17871
  }
17866
17872
  return { rules: rules2, opaque };
17867
17873
  }
17868
- function coverageRulesOf(carried) {
17869
- const winners = {};
17870
- for (const piece of carried) {
17871
- if (!isIRPiece(piece)) return void 0;
17872
- const map = evalRuleMapIR(piece.ir);
17873
- for (const [name, rule] of map) {
17874
- if (rule._def.tag === "lazy") {
17875
- try {
17876
- rule._def.thunk();
17877
- } catch {
17878
- continue;
17879
- }
17880
- }
17881
- winners[name] = rule;
17882
- }
17883
- }
17884
- return winners;
17885
- }
17886
17874
  function composedCoverageRules(grammar) {
17887
- return grammar[COMPOSED_COVERAGE_RULES];
17888
- }
17889
- function nextComposeNs(used) {
17890
- let ns;
17891
- do {
17892
- ns = `_lk${_nsCounter++}_`;
17893
- } while (used.has(ns));
17894
- used.add(ns);
17895
- return ns;
17896
- }
17897
- function itemCarried(item, used, trivia2, hostMode) {
17898
- const carried = item[COMPOSED_PIECES];
17899
- if (Array.isArray(carried)) {
17900
- const items = carried;
17901
- for (const p of items) used.add(p.ns);
17902
- return items;
17903
- }
17904
- if (isLinkableTable(item)) {
17905
- used.add(item.ns);
17906
- return [item];
17907
- }
17908
- const map = item;
17909
- const ns = nextComposeNs(used);
17910
- const entries = Object.entries(map).filter(([, val]) => {
17911
- const d = val._def;
17912
- if (d.tag !== "lazy") return true;
17913
- try {
17914
- d.thunk();
17915
- return true;
17916
- } catch {
17917
- return false;
17918
- }
17919
- });
17920
- const scanSkip = entries.map(([, val]) => val._meta.grammarScanSkip).find(Boolean);
17921
- const ir = serializeRuleMap(entries, scanSkip);
17922
- return ir ? [{ ns, ir }] : [linkable(map, ns, trivia2, hostMode)];
17875
+ const link = grammar[LINKED];
17876
+ return link?.();
17877
+ }
17878
+ var LINKED_WINNERS = /* @__PURE__ */ new WeakMap();
17879
+ function linkedWinnersOf(rule) {
17880
+ return LINKED_WINNERS.get(rule);
17881
+ }
17882
+ function recipesOf(item) {
17883
+ const recipes = item[RULES_RECIPE];
17884
+ if (Array.isArray(recipes)) return recipes;
17885
+ const what = isLinkableTable(item) ? `the table artifact "${item.ns}"` : composedPiecesOf(item) !== void 0 ? "a build-compiled compose() result" : "a value that is not a rules() grammar";
17886
+ throw new Error(
17887
+ `compose: ${what} has no live rules to link. At runtime compose() links interpreter grammars \u2014 rules() maps and runtime compose() results \u2014 and never evaluates carried IR or source text. Compose compiled grammars at build time (the parseman macro does), or compose the interpreter builds of both.`
17888
+ );
17923
17889
  }
17924
17890
  function composingTriviaOf(items) {
17925
17891
  for (let i = items.length - 1; i >= 0; i--) {
17926
17892
  const item = items[i];
17927
- if (!item || isLinkableTable(item)) continue;
17928
- const stamped = item[COMPOSED_TRIVIA];
17929
- if (stamped) return stamped;
17930
- if (item[COMPOSED_PIECES]) continue;
17931
- for (const v of Object.values(item)) {
17932
- const t = v?._meta?.grammarTrivia;
17933
- if (t) return t;
17893
+ if (item[LINKED] !== void 0) {
17894
+ const stamped = item[COMPOSED_TRIVIA];
17895
+ if (stamped) return stamped;
17896
+ continue;
17934
17897
  }
17898
+ const recipes = item[RULES_RECIPE];
17899
+ const trivia2 = recipes?.[recipes.length - 1]?.options?.trivia;
17900
+ if (trivia2 != null) return trivia2;
17935
17901
  }
17936
17902
  return void 0;
17937
17903
  }
17938
- function compose(items, opts) {
17939
- if (items.some((item) => item[LEAF_COMPOSED] === true)) {
17940
- throw new Error("compose: a composeLeaf() result is terminal and cannot be composed again");
17941
- }
17942
- const used = /* @__PURE__ */ new Set();
17943
- const trivia2 = composingTriviaOf(items);
17944
- const carried = items.flatMap((item) => itemCarried(item, used, trivia2, opts?.hostMode));
17945
- const map = fuseCarried(carried, trivia2, opts?.hostMode);
17946
- Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
17947
- if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
17948
- const coverageRules = coverageRulesOf(carried);
17949
- if (coverageRules) Object.defineProperty(map, COMPOSED_COVERAGE_RULES, { value: coverageRules, enumerable: false });
17950
- return map;
17951
- }
17952
- function composeLeaf(items) {
17953
- const pieces = items.flatMap(interpretedPieces);
17954
- let fused;
17955
- const map = {};
17956
- for (const name of ruleNamesOf(pieces)) {
17957
- Object.defineProperty(map, name, {
17958
- enumerable: true,
17959
- configurable: true,
17960
- get: () => (fused ??= fusePieces(pieces))[name]
17961
- });
17962
- }
17963
- Object.defineProperty(map, LEAF_COMPOSED, { value: true, enumerable: false });
17964
- Object.defineProperty(map, INTERPRETED_PIECES, {
17965
- value: pieces.filter((p) => p.plain).map((p) => p.entries),
17966
- enumerable: false
17967
- });
17968
- return map;
17969
- }
17970
- function isRefSlot(c) {
17971
- return c._def.tag === "lazy" && typeof c.define === "function";
17972
- }
17973
- var FUSE_ORIGINAL = /* @__PURE__ */ Symbol.for("parseman.interpretedFuseOriginal");
17974
- var FUSE_TARGET = /* @__PURE__ */ Symbol.for("parseman.interpretedFuseTarget");
17975
- var INTERPRETED_PIECES = /* @__PURE__ */ Symbol.for("parseman.interpretedPieces");
17976
- function definitionOf(entry) {
17977
- if (isRefSlot(entry)) {
17978
- const original = entry[FUSE_ORIGINAL];
17979
- if (original !== void 0) return original ?? void 0;
17980
- try {
17981
- return entry._def.thunk();
17982
- } catch {
17983
- return void 0;
17984
- }
17985
- }
17986
- if (entry._def.tag === "lazy") {
17987
- try {
17988
- entry._def.thunk();
17989
- } catch {
17990
- return void 0;
17991
- }
17904
+ function declaredNamesOf(items) {
17905
+ const names = /* @__PURE__ */ new Set();
17906
+ for (const item of items) {
17907
+ const linked = item[LINKED] !== void 0;
17908
+ const order = linked ? Object.keys(item) : isLinkableTable(item) ? item.keys : item[RULE_ORDER];
17909
+ for (const name of order ?? Object.keys(item)) names.add(name);
17992
17910
  }
17993
- return entry;
17911
+ return [...names];
17994
17912
  }
17995
- function repointRef(slot, target, name) {
17996
- const tagged = slot;
17997
- const bound = tagged[FUSE_TARGET];
17998
- const original = FUSE_ORIGINAL in tagged ? tagged[FUSE_ORIGINAL] : (() => {
17913
+ function linkComposition(recipes, link) {
17914
+ const map = linkRules(recipes, link);
17915
+ const missing = [];
17916
+ for (const [name, rule] of Object.entries(map)) {
17917
+ if (rule._def.tag !== "lazy") continue;
17999
17918
  try {
18000
- return slot._def.thunk();
17919
+ rule._def.thunk();
18001
17920
  } catch {
18002
- return null;
18003
- }
18004
- })();
18005
- if ((bound ?? original) === target) return;
18006
- if (bound !== void 0) {
18007
- throw new Error(
18008
- `fuseInterpreted: rule "${name}" is already bound by a DIFFERENT interpreted fusion of the same grammar piece. An interpreted fuse binds the shared placeholder objects in place, so two fusions cannot share a piece \u2014 build a fresh instance of the piece (call its rules() factory again, or import the module under a distinct specifier) for the second fusion.`
18009
- );
18010
- }
18011
- if (!(FUSE_ORIGINAL in tagged)) Object.defineProperty(slot, FUSE_ORIGINAL, { value: original, enumerable: false });
18012
- Object.defineProperty(slot, FUSE_TARGET, { value: target, enumerable: false });
18013
- slot._def.thunk = () => target;
18014
- slot.parse = (input, pos, ctx) => target.parse(input, pos, ctx);
18015
- const meta = slot._meta;
18016
- meta.firstSet = target._meta.firstSet;
18017
- meta.canMatchNewline = target._meta.canMatchNewline;
18018
- meta.isTrivia = target._meta.isTrivia;
18019
- if (target._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = target._meta.triviaKindLabels;
18020
- else meta.triviaKindLabels = void 0;
18021
- if (target._meta.disjoint !== void 0) meta.disjoint = target._meta.disjoint;
18022
- else meta.disjoint = void 0;
18023
- }
18024
- function interpretedPieces(item) {
18025
- const fused = item[INTERPRETED_PIECES];
18026
- if (Array.isArray(fused)) return fused.map((entries) => ({ entries, plain: false }));
18027
- const carried = composedPiecesOf(item);
18028
- if (carried !== void 0) {
18029
- const { maps, opaque } = carriedRuleMapsDetailed(carried);
18030
- if (opaque.length > 0) {
18031
- throw new Error(
18032
- `fuseInterpreted: cannot interpret a composed grammar containing precompiled artifact(s) ${opaque.map((o) => `"${o.ns}" (${o.ruleNames.length} rules)`).join(", ")} \u2014 they carry compiled functions, not a combinator graph. Pass the source grammars (the same items you passed to compose()) instead.`
18033
- );
17921
+ missing.push(name);
18034
17922
  }
18035
- return maps.map((entries) => ({ entries, plain: false }));
18036
17923
  }
18037
- if (isLinkableTable(item)) {
18038
- const rules2 = ruleMapOfCarried(item);
18039
- if (rules2 === void 0) {
18040
- throw new Error("fuseInterpreted: a precompiled linkable artifact with no carried IR has no combinator graph to interpret; pass the source grammar (a rules() map) instead");
17924
+ if (missing.length > 0) {
17925
+ const holes = new Set(missing);
17926
+ for (const [name, deps] of ruleDependencies(Object.entries(map).filter(([n]) => !holes.has(n)))) {
17927
+ for (const dep of deps) if (holes.has(dep)) throw new Error(`compose: rule "${name}" references missing rule "${dep}"`);
18041
17928
  }
18042
- return [{ entries: rules2, plain: false }];
17929
+ throw new Error(`compose: rule(s) ${missing.map((n) => `"${n}"`).join(", ")} are referenced but defined by no composed grammar`);
18043
17930
  }
18044
- return [{ entries: Object.entries(item), plain: true }];
17931
+ const borrowed = borrowedRuleReference(map);
17932
+ if (borrowed !== void 0) throw new Error(borrowedRuleMessage("compose", borrowed));
17933
+ for (const rule of Object.values(map)) LINKED_WINNERS.set(rule, map);
17934
+ Object.defineProperty(map, RULES_RECIPE, { value: recipes, enumerable: false });
17935
+ Object.defineProperty(map, LINKED, { value: () => map, enumerable: false });
17936
+ if (link.trivia) Object.defineProperty(map, COMPOSED_TRIVIA, { value: link.trivia, enumerable: false });
17937
+ return map;
18045
17938
  }
18046
- function ruleNamesOf(pieces) {
18047
- const names = /* @__PURE__ */ new Set();
18048
- for (const piece of pieces) {
18049
- for (const [name, value] of piece.entries) if (definitionOf(value) !== void 0) names.add(name);
17939
+ function compose(items, opts) {
17940
+ if (items.some((item) => item[LEAF_COMPOSED] === true)) {
17941
+ throw new Error("compose: a composeLeaf() result is terminal and cannot be composed again");
18050
17942
  }
18051
- return [...names];
17943
+ return linkedMap(items, opts, false);
18052
17944
  }
18053
- function fusePieces(pieces, opts) {
18054
- let trivia2;
18055
- for (let i = pieces.length - 1; i >= 0 && trivia2 === void 0; i--) {
18056
- if (!pieces[i].plain) continue;
18057
- for (const [, rule] of pieces[i].entries) {
18058
- const t = rule._meta.grammarTrivia;
18059
- if (t) {
18060
- trivia2 = t;
18061
- break;
18062
- }
18063
- }
18064
- }
18065
- const winner = /* @__PURE__ */ new Map();
18066
- const entry = /* @__PURE__ */ new Map();
18067
- for (const piece of pieces) {
18068
- for (const [name, value] of piece.entries) {
18069
- const def = definitionOf(value);
18070
- if (def === void 0) continue;
18071
- winner.set(name, def);
18072
- entry.set(name, value);
18073
- }
18074
- }
18075
- const missing = /* @__PURE__ */ new Set();
18076
- for (const piece of pieces) {
18077
- for (const [name, value] of piece.entries) {
18078
- if (!isRefSlot(value)) continue;
18079
- const target = winner.get(name);
18080
- if (target === void 0) {
18081
- missing.add(name);
18082
- continue;
18083
- }
18084
- repointRef(value, target, name);
18085
- }
17945
+ function linkedMap(items, opts, leaf2) {
17946
+ const recipes = items.flatMap(recipesOf);
17947
+ const trivia2 = composingTriviaOf(items);
17948
+ const names = declaredNamesOf(items);
17949
+ let linked;
17950
+ const link = () => linked ??= linkComposition(recipes, { trivia: trivia2, ...opts?.hostMode === void 0 ? {} : { hostMode: opts.hostMode } });
17951
+ const map = {};
17952
+ for (const name of names) {
17953
+ Object.defineProperty(map, name, { enumerable: true, configurable: true, get: () => link()[name] });
18086
17954
  }
18087
- if (missing.size > 0) throw new Error(missingRuleMessage(pieces, missing));
18088
- const out = {};
18089
- for (const [name, value] of entry) {
18090
- out[name] = value;
18091
- const meta = value._meta;
18092
- if (trivia2 !== void 0 && !meta.isTrivia) meta.grammarTrivia = trivia2;
18093
- if (opts?.hostMode !== void 0 && !meta.isTrivia) {
18094
- if (opts.hostMode === "cst") meta.grammarHostMode = "cst";
18095
- else meta.grammarHostMode = void 0;
18096
- }
18097
- }
18098
- Object.defineProperty(out, INTERPRETED_PIECES, {
18099
- value: pieces.filter((p) => p.plain).map((p) => p.entries),
18100
- enumerable: false
18101
- });
18102
- return out;
17955
+ Object.defineProperty(map, RULES_RECIPE, { value: recipes, enumerable: false });
17956
+ Object.defineProperty(map, RULE_ORDER, { value: names, enumerable: false });
17957
+ Object.defineProperty(map, LINKED, { value: link, enumerable: false });
17958
+ Object.defineProperty(map, GRAMMAR_REFLECTION, { enumerable: false, configurable: true, get: () => link()[GRAMMAR_REFLECTION] });
17959
+ if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
17960
+ if (leaf2) Object.defineProperty(map, LEAF_COMPOSED, { value: true, enumerable: false });
17961
+ return map;
18103
17962
  }
18104
- function missingRuleMessage(pieces, missing) {
18105
- for (const piece of pieces) {
18106
- const deps = ruleDependencies(piece.entries.filter(([, v]) => definitionOf(v) !== void 0));
18107
- for (const [name, ds] of deps) {
18108
- for (const d of ds) if (missing.has(d)) return `fuseInterpreted: rule "${name}" references missing rule "${d}"`;
18109
- }
18110
- }
18111
- return `fuseInterpreted: missing rule(s) ${[...missing].map((n) => `"${n}"`).join(", ")}`;
17963
+ function composeLeaf(items) {
17964
+ return linkedMap(items, void 0, true);
18112
17965
  }
18113
17966
 
18114
17967
  // src/analysis/grammar.ts
@@ -18141,7 +17994,7 @@ function analyzeGrammarGating(grammar, opts) {
18141
17994
  }
18142
17995
 
18143
17996
  // src/analysis/diagnose.ts
18144
- var isCombinator = (g) => typeof g === "object" && g !== null && "_def" in g;
17997
+ var isCombinator2 = (g) => typeof g === "object" && g !== null && "_def" in g;
18145
17998
  var isRuleEntries = (g) => Array.isArray(g);
18146
17999
  function diagnoseGrammar(grammar, opts) {
18147
18000
  const depth = degradationCaptureDepth();
@@ -18185,7 +18038,7 @@ function formatGrammarDiagnosis(d) {
18185
18038
  return lines;
18186
18039
  }
18187
18040
  function runAnalysis(grammar, opts) {
18188
- if (isCombinator(grammar)) return analyzeGating(grammar, opts);
18041
+ if (isCombinator2(grammar)) return analyzeGating(grammar, opts);
18189
18042
  if (isRuleEntries(grammar)) return analyzeGatingRules(grammar, opts);
18190
18043
  return analyzeGrammarGating(grammar, opts);
18191
18044
  }
@@ -19453,6 +19306,29 @@ function gate(predicate) {
19453
19306
  };
19454
19307
  }
19455
19308
 
19309
+ // src/combinators/withCtx.ts
19310
+ function withCtx(extra, combinator) {
19311
+ const meta = {
19312
+ firstSet: combinator._meta.firstSet,
19313
+ canMatchNewline: combinator._meta.canMatchNewline,
19314
+ isTrivia: false
19315
+ };
19316
+ return {
19317
+ _tag: "withCtx",
19318
+ _meta: meta,
19319
+ _def: { tag: "withCtx", extra, parser: combinator },
19320
+ parse(input, pos, ctx) {
19321
+ const saved = ctx.state;
19322
+ ctx.state = extra;
19323
+ try {
19324
+ return combinator.parse(input, pos, ctx);
19325
+ } finally {
19326
+ ctx.state = saved;
19327
+ }
19328
+ }
19329
+ };
19330
+ }
19331
+
19456
19332
  // src/combinators/completions.ts
19457
19333
  function completionsAt(target, input, offset, options = {}) {
19458
19334
  const probe = { offset, best: null };
@@ -20135,7 +20011,7 @@ function grammarCoverageDefinitions(entry, winners) {
20135
20011
  }
20136
20012
  function composedGrammarCoverageDefinitions(grammar, startRule) {
20137
20013
  const winners = composedCoverageRules(grammar);
20138
- if (!winners) throw new TypeError("semantic coverage needs re-lowerable composed IR; this composition contains an opaque artifact");
20014
+ if (!winners) throw new TypeError("semantic coverage needs a runtime compose() result (a linked interpreter grammar); this is not one");
20139
20015
  const entry = winners[startRule];
20140
20016
  if (!entry) throw new TypeError(`semantic coverage start rule ${JSON.stringify(startRule)} is not a final winner`);
20141
20017
  return grammarCoverageDefinitions(entry, winners);
@@ -20327,7 +20203,7 @@ function coverageEntry(entry, collector, maps, trace) {
20327
20203
  case "leaf":
20328
20204
  return def.cstValue === "source" ? sourceLeaf(build(def.parser), def.fn) : leaf(build(def.parser), def.fn);
20329
20205
  case "field":
20330
- return field(def.name, build(def.parser));
20206
+ return field(def.name, build(def.parser), { leadingTrivia: def.leadingTrivia === true, trailingTrivia: def.trailingTrivia === true });
20331
20207
  case "grammar":
20332
20208
  return parser({
20333
20209
  ...def.triviaParser === void 0 ? def.clearTrivia ? { trivia: null } : {} : { trivia: build(def.triviaParser) },
@@ -20377,7 +20253,7 @@ function coverageEntry(entry, collector, maps, trace) {
20377
20253
  }
20378
20254
  function runWithGrammarCoverage(entry, input, options = {}) {
20379
20255
  if (typeof entry === "function") throw new TypeError("runWithGrammarCoverage currently requires an interpreter combinator entry");
20380
- const plan = buildGrammarPlan(entry);
20256
+ const plan = buildGrammarPlan(entry, linkedWinnersOf(entry));
20381
20257
  const definitions = plan.definitions;
20382
20258
  const collector = options.collector ?? createGrammarCoverageCollector(definitions);
20383
20259
  const { collector: _collector, trace, ...runOptions } = options;
@@ -20451,13 +20327,13 @@ function buildTriviaIndex(root, input = "", opts) {
20451
20327
  }
20452
20328
 
20453
20329
  // src/cst/walk.ts
20454
- function isCombinator2(value) {
20330
+ function isCombinator3(value) {
20455
20331
  return !!value && typeof value === "object" && "_def" in value && "_meta" in value && typeof value.parse === "function";
20456
20332
  }
20457
20333
  function reflectionFor(grammar) {
20458
20334
  const stamped = grammarReflectionOf(grammar);
20459
20335
  if (stamped) return stamped;
20460
- const entries = Object.entries(grammar).filter((entry) => isCombinator2(entry[1]));
20336
+ const entries = Object.entries(grammar).filter((entry) => isCombinator3(entry[1]));
20461
20337
  return collectGrammarReflection(entries);
20462
20338
  }
20463
20339
  function createVisitor(grammar, spec) {