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
@@ -367,6 +367,7 @@ function withRuleTrivia(trivia2, ctx, run) {
367
367
  ctx.triviaKindLabels = savedLabels;
368
368
  }
369
369
  }
370
+ var UNDEFINED_REF = new Error("ref<T>() used before .define() was called");
370
371
  function ref() {
371
372
  let resolved = null;
372
373
  let resolvedScalar = null;
@@ -382,7 +383,7 @@ function ref() {
382
383
  _def: {
383
384
  tag: "lazy",
384
385
  thunk: () => {
385
- if (!resolved) throw new Error("ref<T>() used before .define() was called");
386
+ if (!resolved) throw UNDEFINED_REF;
386
387
  return resolved;
387
388
  }
388
389
  },
@@ -1268,65 +1269,17 @@ function plainRegexSource(p) {
1268
1269
  const d = p._def;
1269
1270
  return d.tag === "regex" && d.flags === "" ? d.source : null;
1270
1271
  }
1271
- var visitSpec0;
1272
- var visitSpec1;
1273
- var visitSpec2;
1274
- var visitSpec3;
1275
- var visitValue0;
1276
- var visitValue1;
1277
- var visitValue2;
1278
- var visitValue3;
1279
1272
  function commonLabeledTriviaVisitor(spec) {
1280
- if (spec === visitSpec0) return visitValue0;
1281
- if (spec === visitSpec1) return visitValue1;
1282
- if (spec === visitSpec2) return visitValue2;
1283
- if (spec === visitSpec3) return visitValue3;
1284
- const sources = spec.minRepeats <= 1 ? spec.arms.map((arm) => plainRegexSource(arm.parser)) : [];
1285
- const visitor = commonCssTriviaVisitor(sources);
1286
- visitSpec3 = visitSpec2;
1287
- visitValue3 = visitValue2;
1288
- visitSpec2 = visitSpec1;
1289
- visitValue2 = visitValue1;
1290
- visitSpec1 = visitSpec0;
1291
- visitValue1 = visitValue0;
1292
- visitSpec0 = spec;
1293
- visitValue0 = visitor;
1294
- return visitor;
1295
- }
1296
- var armSpec0;
1297
- var armSpec1;
1298
- var armSpec2;
1299
- var armSpec3;
1300
- var armValue0;
1301
- var armValue1;
1302
- var armValue2;
1303
- var armValue3;
1273
+ return commonCssTriviaVisitor(spec.minRepeats <= 1 ? spec.arms.map((arm) => plainRegexSource(arm.parser)) : []);
1274
+ }
1304
1275
  function charArmsFor(spec) {
1305
- if (spec === armSpec0) return armValue0;
1306
- if (spec === armSpec1) return armValue1;
1307
- if (spec === armSpec2) return armValue2;
1308
- if (spec === armSpec3) return armValue3;
1309
- let arms = [];
1310
- if (spec.minRepeats > 1) {
1311
- arms = null;
1312
- } else {
1313
- for (const arm of spec.arms) {
1314
- const c = classifyParser(arm.parser);
1315
- if (!c) {
1316
- arms = null;
1317
- break;
1318
- }
1319
- arms.push(c);
1320
- }
1276
+ if (spec.minRepeats > 1) return null;
1277
+ const arms = [];
1278
+ for (const arm of spec.arms) {
1279
+ const c = classifyParser(arm.parser);
1280
+ if (!c) return null;
1281
+ arms.push(c);
1321
1282
  }
1322
- armSpec3 = armSpec2;
1323
- armValue3 = armValue2;
1324
- armSpec2 = armSpec1;
1325
- armValue2 = armValue1;
1326
- armSpec1 = armSpec0;
1327
- armValue1 = armValue0;
1328
- armSpec0 = spec;
1329
- armValue0 = arms;
1330
1283
  return arms;
1331
1284
  }
1332
1285
  function charTriviaEnd(input, cur, arms) {
@@ -1442,12 +1395,16 @@ function analyzeLabeledTrivia(trivia2) {
1442
1395
  }
1443
1396
  arms.push({ label: peeled.label, kindIndex: 0, parser: peeled.parser });
1444
1397
  }
1445
- const spec = {
1398
+ return cacheLabeledTrivia(trivia2, labeledTriviaSpec(arms, minRepeats));
1399
+ }
1400
+ function labeledTriviaSpec(arms, minRepeats) {
1401
+ return {
1446
1402
  labels: arms.map((a) => a.label),
1447
1403
  arms,
1448
- minRepeats
1404
+ minRepeats,
1405
+ visitor: commonLabeledTriviaVisitor({ arms, minRepeats }),
1406
+ charArms: charArmsFor({ arms, minRepeats })
1449
1407
  };
1450
- return cacheLabeledTrivia(trivia2, spec);
1451
1408
  }
1452
1409
  function matchArmAt(input, pos, arm, state) {
1453
1410
  if (!startsFirstSet(arm, input, pos)) return null;
@@ -1477,7 +1434,7 @@ function scanLabeledTriviaChunks(input, cur, spec, state) {
1477
1434
  return { end: pos, chunks };
1478
1435
  }
1479
1436
  function scanLabeledTriviaEnd(input, cur, spec, state) {
1480
- const chars = charArmsFor(spec);
1437
+ const chars = spec.charArms;
1481
1438
  if (chars !== null) return charTriviaEnd(input, cur, chars);
1482
1439
  let pos = cur;
1483
1440
  let count = 0;
@@ -1495,9 +1452,9 @@ function scanLabeledTriviaEnd(input, cur, spec, state) {
1495
1452
  }
1496
1453
  function visitLabeledTrivia(input, cur, spec, state, visit) {
1497
1454
  if (spec.minRepeats > 1) return void 0;
1498
- const common = commonLabeledTriviaVisitor(spec);
1455
+ const common = spec.visitor;
1499
1456
  if (common !== null) return common(input, cur, visit);
1500
- const chars = charArmsFor(spec);
1457
+ const chars = spec.charArms;
1501
1458
  if (chars !== null) return charTriviaVisit(input, cur, chars, visit);
1502
1459
  let pos = cur;
1503
1460
  let count = 0;
@@ -1564,7 +1521,7 @@ function resolveAdjacencyKindMask(trivia2, kinds) {
1564
1521
  return mask;
1565
1522
  }
1566
1523
  function triviaKindMaskAt(input, pos, spec, state) {
1567
- const chars = charArmsFor(spec);
1524
+ const chars = spec.charArms;
1568
1525
  if (chars !== null) return charTriviaKindMask(input, pos, chars);
1569
1526
  let cur = pos;
1570
1527
  let mask = 0;
@@ -1810,6 +1767,7 @@ function collectFromCombinator(parser2, out, seen, followLazy = true) {
1810
1767
  function collectGrammarReflection(ruleMap, opts) {
1811
1768
  const byType = /* @__PURE__ */ new Map();
1812
1769
  const followLazy = opts?.followLazy !== false;
1770
+ const seen = /* @__PURE__ */ new Set();
1813
1771
  for (const [, rule] of ruleMap) {
1814
1772
  if (!followLazy && rule._def.tag === "lazy") {
1815
1773
  try {
@@ -1819,7 +1777,7 @@ function collectGrammarReflection(ruleMap, opts) {
1819
1777
  }
1820
1778
  continue;
1821
1779
  }
1822
- collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
1780
+ collectFromCombinator(rule, byType, seen, followLazy);
1823
1781
  }
1824
1782
  return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
1825
1783
  }
@@ -1850,9 +1808,26 @@ function isNamedRuleRefForAnotherRule(r, key) {
1850
1808
  const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
1851
1809
  return name !== void 0 && name !== key;
1852
1810
  }
1811
+ var claimedRules = /* @__PURE__ */ new WeakSet();
1812
+ function ownRule(p, copies) {
1813
+ if (!claimedRules.has(p) || p._meta.isTrivia) return p;
1814
+ let own = copies.get(p);
1815
+ if (own === void 0) {
1816
+ const { grammarTrivia: _t, grammarScanSkip: _s, grammarHostMode: _h, grammarTrackLines: _l, ...meta } = p._meta;
1817
+ copies.set(p, own = { ...p, _meta: meta });
1818
+ }
1819
+ return own;
1820
+ }
1853
1821
  function rules(a, b) {
1854
1822
  const factory = typeof a === "function" ? a : b;
1855
1823
  const options = typeof a === "function" ? b : a;
1824
+ const recipe = { factory, options };
1825
+ const map = linkRules([recipe]);
1826
+ Object.defineProperty(map, RULES_RECIPE, { value: [recipe], enumerable: false });
1827
+ return map;
1828
+ }
1829
+ var RULES_RECIPE = /* @__PURE__ */ Symbol.for("parseman.rulesRecipe");
1830
+ function linkRules(recipes, link) {
1856
1831
  const cache = {};
1857
1832
  const proxy = new Proxy(cache, {
1858
1833
  get(target, key) {
@@ -1866,10 +1841,18 @@ function rules(a, b) {
1866
1841
  return record[key];
1867
1842
  }
1868
1843
  });
1869
- const definitions = factory(proxy);
1870
- for (const key of Object.keys(definitions)) {
1844
+ const winners = /* @__PURE__ */ new Map();
1845
+ for (const recipe of recipes) {
1846
+ const definitions = recipe.factory(proxy);
1847
+ for (const key of Object.keys(definitions)) {
1848
+ winners.set(key, { parser: definitions[key], options: recipe.options });
1849
+ }
1850
+ }
1851
+ const keys = [...winners.keys()];
1852
+ const copies = /* @__PURE__ */ new Map();
1853
+ for (const [key, { parser: defined }] of winners) {
1871
1854
  const placeholder = cache[key];
1872
- const parser2 = definitions[key];
1855
+ const parser2 = ownRule(defined, copies);
1873
1856
  if (placeholder === parser2) {
1874
1857
  throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
1875
1858
  }
@@ -1878,7 +1861,8 @@ function rules(a, b) {
1878
1861
  placeholder.define(parser2);
1879
1862
  placeholder._meta.firstSet = parser2._meta.firstSet;
1880
1863
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1881
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
1864
+ } else if (isNamedRuleRefForAnotherRule(parser2, key) || link !== void 0) {
1865
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
1882
1866
  const alias = ref();
1883
1867
  tagRule(alias, key);
1884
1868
  alias.define(parser2);
@@ -1890,48 +1874,34 @@ function rules(a, b) {
1890
1874
  cache[key] = parser2;
1891
1875
  }
1892
1876
  }
1893
- if (options?.trivia != null) {
1894
- for (const key of Object.keys(definitions)) {
1895
- const rule = cache[key];
1896
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1897
- }
1898
- }
1899
- if (options?.scanSkip != null) {
1900
- for (const key of Object.keys(definitions)) {
1901
- const rule = cache[key];
1902
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
1903
- }
1904
- }
1905
- if (options?.hostMode === "cst") {
1906
- for (const key of Object.keys(definitions)) {
1907
- const rule = cache[key];
1908
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
1909
- }
1910
- }
1911
- if (options?.trackLines === true) {
1912
- for (const key of Object.keys(definitions)) {
1913
- const rule = cache[key];
1914
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
1915
- }
1916
- for (const key of Object.keys(definitions)) {
1917
- const rule = cache[key];
1918
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
1919
- const wrapped = parser({ trackLines: true }, rule);
1920
- tagRule(wrapped, key);
1921
- wrapped._meta.grammarTrackLines = true;
1922
- cache[key] = wrapped;
1877
+ for (const key of keys) {
1878
+ const options = winners.get(key).options;
1879
+ let rule = cache[key];
1880
+ if (!rule._meta.isTrivia) {
1881
+ const meta = rule._meta;
1882
+ const trivia2 = link !== void 0 ? link.trivia : options?.trivia;
1883
+ if (trivia2 != null) meta.grammarTrivia = trivia2;
1884
+ if (options?.scanSkip != null) meta.grammarScanSkip = options.scanSkip;
1885
+ if ((link?.hostMode ?? options?.hostMode) === "cst") meta.grammarHostMode = "cst";
1886
+ if (options?.trackLines === true) {
1887
+ meta.grammarTrackLines = true;
1888
+ if (rule._def.tag !== "grammar") {
1889
+ rule = parser({ trackLines: true }, rule);
1890
+ tagRule(rule, key);
1891
+ rule._meta.grammarTrackLines = true;
1892
+ cache[key] = rule;
1893
+ }
1923
1894
  }
1924
1895
  }
1925
- }
1926
- for (const key of Object.keys(definitions)) {
1927
- markUnusedValues(cache[key]);
1896
+ markUnusedValues(rule);
1897
+ claimedRules.add(winners.get(key).parser).add(rule);
1928
1898
  }
1929
1899
  Object.defineProperty(cache, RULE_ORDER, {
1930
- value: Object.keys(definitions),
1900
+ value: keys,
1931
1901
  enumerable: false,
1932
1902
  configurable: true
1933
1903
  });
1934
- attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1904
+ attachGrammarReflection(cache, collectGrammarReflection(keys.map((key) => [key, cache[key]])));
1935
1905
  return cache;
1936
1906
  }
1937
1907
 
@@ -3265,6 +3235,23 @@ function notAdjacent(options) {
3265
3235
  );
3266
3236
  }
3267
3237
 
3238
+ // src/combinators/field-trivia.ts
3239
+ var LEAD = { at: -1 };
3240
+ var FIELD_LEADING_TRIVIA = 1;
3241
+ var FIELD_TRAILING_TRIVIA = 2;
3242
+ function triviaFieldCapture(name, value, input, start, end, leadFrom, trailEnd) {
3243
+ return {
3244
+ name,
3245
+ value,
3246
+ span: { start, end },
3247
+ leading: leadFrom >= 0 && leadFrom < start ? input.slice(leadFrom, start) : "",
3248
+ trailing: trailEnd > end ? input.slice(end, trailEnd) : ""
3249
+ };
3250
+ }
3251
+ function leadsTrivia(p) {
3252
+ return p._def.tag === "field" && p._def.leadingTrivia === true;
3253
+ }
3254
+
3268
3255
  // src/combinators/sequence.ts
3269
3256
  function sequence(...parsers) {
3270
3257
  let adjacency = null;
@@ -3280,6 +3267,10 @@ function sequence(...parsers) {
3280
3267
  (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
3281
3268
  }
3282
3269
  const hasAdjacency = adjacency !== null;
3270
+ let leading = null;
3271
+ for (let i = 1; i < parsers.length; i++) {
3272
+ if (leadsTrivia(parsers[i])) (leading ??= Array.from({ length: parsers.length }, () => false))[i] = true;
3273
+ }
3283
3274
  const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
3284
3275
  const meta = {
3285
3276
  // Union through the nullable prefix — a leading optional()/many() lets a later
@@ -3327,7 +3318,7 @@ function sequence(...parsers) {
3327
3318
  _tag: "sequence",
3328
3319
  _meta: meta,
3329
3320
  _def: def,
3330
- _parseScalar: hasAdjacency ? void 0 : parseScalar,
3321
+ _parseScalar: hasAdjacency || leading !== null ? void 0 : parseScalar,
3331
3322
  parse(input, pos, ctx) {
3332
3323
  const tolerant = ctx._tolerant === true;
3333
3324
  if (tolerant) {
@@ -3361,6 +3352,7 @@ function sequence(...parsers) {
3361
3352
  const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
3362
3353
  const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
3363
3354
  const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
3355
+ if (leading?.[i] === true) LEAD.at = cur;
3364
3356
  const result = parsers[i].parse(input, termPos, ctx);
3365
3357
  if (!result.ok) return result;
3366
3358
  if (values !== void 0) values.push(result.value);
@@ -4336,6 +4328,7 @@ function unwrapSeparator(separator) {
4336
4328
  }
4337
4329
  function sepBy(combinator, separatorArg, opts = {}) {
4338
4330
  const { sep: separator, keep: keepSeparators } = unwrapSeparator(separatorArg);
4331
+ const separatorLeads = leadsTrivia(separator);
4339
4332
  const { min, max } = resolveBounds("sepBy()", opts);
4340
4333
  const trailing = opts.trailing ?? "forbid";
4341
4334
  const meta = {
@@ -4400,7 +4393,7 @@ function sepBy(combinator, separatorArg, opts = {}) {
4400
4393
  // this option surface already; parity is held by test/parity, not by hope.
4401
4394
  ...keepSeparators ? { keepSeparators: true } : {}
4402
4395
  },
4403
- _parseScalar: keepSeparators ? void 0 : parseScalar,
4396
+ _parseScalar: keepSeparators || separatorLeads ? void 0 : parseScalar,
4404
4397
  parse(input, pos, ctx) {
4405
4398
  const first = combinator.parse(input, pos, ctx);
4406
4399
  const values = [];
@@ -4432,6 +4425,7 @@ function sepBy(combinator, separatorArg, opts = {}) {
4432
4425
  sepPos = advanceTrivia(input, cur, ctx);
4433
4426
  }
4434
4427
  }
4428
+ if (separatorLeads) LEAD.at = cur;
4435
4429
  const sep2 = separator.parse(input, sepPos, ctx);
4436
4430
  if (!sep2.ok) {
4437
4431
  rollbackTrivia(ctx, loopMark);
@@ -4889,7 +4883,7 @@ function buildFieldMap(captures) {
4889
4883
  if (!captures?.length) return void 0;
4890
4884
  const out = {};
4891
4885
  for (const cap of captures) {
4892
- const entry = { value: cap.value, span: cap.span };
4886
+ const entry = cap.leading === void 0 ? { value: cap.value, span: cap.span } : { value: cap.value, span: cap.span, leading: cap.leading, trailing: cap.trailing };
4893
4887
  const current = out[cap.name];
4894
4888
  if (current === void 0) out[cap.name] = entry;
4895
4889
  else if (Array.isArray(current)) current.push(entry);
@@ -5121,16 +5115,41 @@ function label(name, combinator) {
5121
5115
  }
5122
5116
  };
5123
5117
  }
5124
- function field(name, combinator) {
5118
+ function field(name, combinator, opts) {
5119
+ const leadingTrivia = opts?.leadingTrivia === true;
5120
+ const trailingTrivia = opts?.trailingTrivia === true;
5121
+ if (!leadingTrivia && !trailingTrivia) {
5122
+ return {
5123
+ _tag: combinator._tag,
5124
+ _meta: combinator._meta,
5125
+ _def: { tag: "field", name, parser: combinator },
5126
+ parse(input, pos, ctx) {
5127
+ const result = combinator.parse(input, pos, ctx);
5128
+ if (!result.ok) return result;
5129
+ ctx._fields?.push({ name, value: result.value, span: result.span });
5130
+ return result;
5131
+ }
5132
+ };
5133
+ }
5125
5134
  return {
5126
5135
  _tag: combinator._tag,
5127
5136
  _meta: combinator._meta,
5128
- _def: { tag: "field", name, parser: combinator },
5137
+ _def: {
5138
+ tag: "field",
5139
+ name,
5140
+ parser: combinator,
5141
+ ...leadingTrivia ? { leadingTrivia: true } : {},
5142
+ ...trailingTrivia ? { trailingTrivia: true } : {}
5143
+ },
5129
5144
  parse(input, pos, ctx) {
5145
+ const leadFrom = LEAD.at;
5146
+ LEAD.at = -1;
5130
5147
  const result = combinator.parse(input, pos, ctx);
5131
5148
  if (!result.ok) return result;
5132
- ctx._fields?.push({ name, value: result.value, span: result.span });
5133
- return result;
5149
+ const end = result.span.end;
5150
+ const trailEnd = trailingTrivia && ctx.trivia ? consumeTrivia(input, end, ctx) : end;
5151
+ ctx._fields?.push(triviaFieldCapture(name, result.value, input, result.span.start, end, leadingTrivia ? leadFrom : -1, trailEnd));
5152
+ return trailEnd === end ? result : { ok: true, value: result.value, span: { start: result.span.start, end: trailEnd } };
5134
5153
  }
5135
5154
  };
5136
5155
  }
@@ -7192,6 +7211,16 @@ var Encoder = class {
7192
7211
  this.kIndex.set(key, i);
7193
7212
  return i;
7194
7213
  }
7214
+ fieldSpecIndex = /* @__PURE__ */ new Map();
7215
+ fieldSpecSlot(name, bits) {
7216
+ const key = JSON.stringify([name, bits]);
7217
+ const hit = this.fieldSpecIndex.get(key);
7218
+ if (hit !== void 0) return hit;
7219
+ const i = this.k.length;
7220
+ this.k.push([name, bits]);
7221
+ this.fieldSpecIndex.set(key, i);
7222
+ return i;
7223
+ }
7195
7224
  /**
7196
7225
  * A `notAdjacent({ kinds })` filter, pooled by its CONTENTS.
7197
7226
  *
@@ -7812,8 +7841,10 @@ var Encoder = class {
7812
7841
  for (let i = 0; i < arms.length; i++) this.code[head + 6 + i] = arms[i];
7813
7842
  return head;
7814
7843
  }
7815
- case "field":
7816
- return this.emit(OP_FIELD, this.constant(d.name), this.node(d.parser).ip);
7844
+ case "field": {
7845
+ const bits = (d.leadingTrivia === true ? FIELD_LEADING_TRIVIA : 0) | (d.trailingTrivia === true ? FIELD_TRAILING_TRIVIA : 0);
7846
+ return this.emit(OP_FIELD, bits === 0 ? this.constant(d.name) : this.fieldSpecSlot(d.name, bits), this.node(d.parser).ip);
7847
+ }
7817
7848
  case "lazy": {
7818
7849
  const refName = p._ruleName;
7819
7850
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
@@ -9486,6 +9517,26 @@ function leadingLiteralFamily(code, constants, ip, minDepth = 2, limit = 4) {
9486
9517
  };
9487
9518
  return walk(ip, 0);
9488
9519
  }
9520
+ function largeChoiceLiteralPretest(code, constants, armIp) {
9521
+ const child = leadingScalarTerminal(code, armIp, 2, true, true);
9522
+ if (child >= 0) {
9523
+ const spec = constants[code[child + 1]];
9524
+ if (code[child] === OP_LIT && typeof spec === "string" && spec.length >= 2) return [child];
9525
+ }
9526
+ const family = leadingLiteralFamily(code, constants, armIp);
9527
+ return family !== void 0 && family.length >= 2 && family.every((terminal) => {
9528
+ const value = constants[code[terminal + 1]];
9529
+ return typeof value === "string" && value.length >= 2;
9530
+ }) ? family : void 0;
9531
+ }
9532
+
9533
+ // src/table/field-spec.ts
9534
+ function fieldSpec(slot) {
9535
+ return Array.isArray(slot) ? { name: slot[0], bits: slot[1] } : { name: slot, bits: 0 };
9536
+ }
9537
+ function leadingTriviaFieldAt(code, k, ip) {
9538
+ return code[ip] === OP_FIELD && (fieldSpec(k[code[ip + 1]]).bits & FIELD_LEADING_TRIVIA) !== 0;
9539
+ }
9489
9540
 
9490
9541
  // src/table/emit-assembly.ts
9491
9542
  var Unemittable = class extends Error {
@@ -9560,7 +9611,11 @@ var EMITTED_PARAMS = [
9560
9611
  // Appended for old precompiled-factory ABI: old factories ignore it.
9561
9612
  "adjacencyHolds",
9562
9613
  // Appended for selected composite lexical programs; old factories ignore it.
9563
- "LEXPROG"
9614
+ "LEXPROG",
9615
+ // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
9616
+ // old factories ignore them.
9617
+ "LEAD",
9618
+ "triviaFieldCapture"
9564
9619
  ];
9565
9620
  var RUNTIME_PRELUDE = `
9566
9621
  let _pfScan=null
@@ -9820,20 +9875,12 @@ function emitAssemblySource(t, prog, cfg, extraIps = [], staticBuild = false) {
9820
9875
  if (child >= 0) scalarSpecs.add(code[child + 1]);
9821
9876
  continue;
9822
9877
  }
9823
- if (child >= 0) {
9824
- const spec = k[code[child + 1]];
9825
- if (code[child] === OP_LIT && typeof spec === "string" && spec.length >= 2) {
9826
- largeChoiceScalarSpecs.add(code[child + 1]);
9827
- continue;
9828
- }
9829
- }
9830
- const family = leadingLiteralFamily(code, k, armIp);
9831
- if (family !== void 0 && family.length >= 2 && family.every((terminal) => {
9832
- const value = k[code[terminal + 1]];
9833
- return typeof value === "string" && value.length >= 2;
9834
- })) {
9835
- largeChoiceLiteralFamilies.set(armIp, family);
9878
+ const literals = largeChoiceLiteralPretest(code, k, armIp);
9879
+ if (literals !== void 0 && literals.length === 1 && literals[0] === child) {
9880
+ largeChoiceScalarSpecs.add(code[child + 1]);
9881
+ continue;
9836
9882
  }
9883
+ if (literals !== void 0) largeChoiceLiteralFamilies.set(armIp, literals);
9837
9884
  const sequence6 = leadingScalarSequence(code, armIp);
9838
9885
  if (sequence6 !== void 0 && (sequence6.trivia < 0 || swapLegal && t.triviaScan[sequence6.trivia] != null)) {
9839
9886
  largeChoiceScalarSequences.set(armIp, sequence6);
@@ -10257,14 +10304,31 @@ return v
10257
10304
  }`;
10258
10305
  }
10259
10306
  case OP_FIELD: {
10260
- const name = q(k[code[ip + 1]]);
10307
+ const spec = fieldSpec(k[code[ip + 1]]);
10308
+ const name = q(spec.name);
10261
10309
  const child = link(code[ip + 2]);
10262
- return `${head}
10310
+ if (spec.bits === 0) {
10311
+ return `${head}
10263
10312
  const v=${child}(input,pos,ctx)
10264
10313
  if(v===FAIL)return FAIL
10265
10314
  const f=ctx._fields
10266
10315
  if(f!==undefined)f.push({name:${name},value:v,span:{start:pos,end:EC.e}})
10267
10316
  return v
10317
+ }`;
10318
+ }
10319
+ const leading = (spec.bits & FIELD_LEADING_TRIVIA) !== 0;
10320
+ const trailing = (spec.bits & FIELD_TRAILING_TRIVIA) !== 0;
10321
+ return `${head}
10322
+ const lf=${leading ? "LEAD.at" : "-1"}
10323
+ LEAD.at=-1
10324
+ const v=${child}(input,pos,ctx)
10325
+ if(v===FAIL)return FAIL
10326
+ const e=EC.e
10327
+ const te=${trailing ? "ctx.trivia!==undefined?consumeTrivia(input,e,ctx):e" : "e"}
10328
+ const f=ctx._fields
10329
+ if(f!==undefined)f.push(triviaFieldCapture(${name},v,input,pos,e,lf,te))
10330
+ EC.e=te
10331
+ return v
10268
10332
  }`;
10269
10333
  }
10270
10334
  case OP_EXPECT: {
@@ -10560,7 +10624,10 @@ return v
10560
10624
  `if(!adjacencyHolds(input,cur,ctx,${negated ? "true" : "false"},${kinds})){ctx._fe=cur;ctx._fx=${expected};return FAIL}`,
10561
10625
  `${vn}=null`
10562
10626
  );
10563
- } else parts.push(emitTerm(kids[i], vn, tmp(), L, skipFor(L)));
10627
+ } else {
10628
+ const callee = leadingTriviaFieldAt(code, k, childIp) ? `(LEAD.at=cur,${kids[i]})` : kids[i];
10629
+ parts.push(emitTerm(callee, vn, tmp(), L, skipFor(L)));
10630
+ }
10564
10631
  names.push(vn);
10565
10632
  }
10566
10633
  parts.push("EC.e=cur");
@@ -10960,7 +11027,7 @@ let sp=cur
10960
11027
  ${hasTrivia === "false" ? "" : hasTrivia === "true" ? `sp=${skip}(input,sp,ctx)
10961
11028
  ` : `if(hasTrivia)sp=${skip}(input,sp,ctx)
10962
11029
  `}ctx._fc=false
10963
- const sv=${sep2}(input,sp,ctx)
11030
+ ${sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp) ? "LEAD.at=cur\n" : ""}const sv=${sep2}(input,sp,ctx)
10964
11031
  if(sv===FAIL){
10965
11032
  ${rb}
10966
11033
  if(ctx._fc===true)return FAIL
@@ -12057,6 +12124,12 @@ function assemble(t, prog, cfg) {
12057
12124
  rollbackScannedTriviaAt(ctx, mTl, scanTl, mLog, scanLog, mRoot, scanRoot);
12058
12125
  return cur;
12059
12126
  }
12127
+ function leadingFieldTerm(step, child, input, cur, ctx) {
12128
+ return step((i, p, c) => {
12129
+ LEAD.at = cur;
12130
+ return child(i, p, c);
12131
+ }, input, cur, ctx);
12132
+ }
12060
12133
  function committed3(c) {
12061
12134
  return c._fc === true;
12062
12135
  }
@@ -12694,12 +12767,27 @@ function assemble(t, prog, cfg) {
12694
12767
  };
12695
12768
  }
12696
12769
  case OP_FIELD: {
12697
- const name = k[code[ip + 1]];
12770
+ const { name, bits } = fieldSpec(k[code[ip + 1]]);
12698
12771
  const child = link(code[ip + 2]);
12772
+ if (bits === 0) {
12773
+ return (input, pos, ctx) => {
12774
+ const v = child(input, pos, ctx);
12775
+ if (v === FAIL) return FAIL;
12776
+ ctx._fields?.push({ name, value: v, span: { start: pos, end: EC.e } });
12777
+ return v;
12778
+ };
12779
+ }
12780
+ const leading = (bits & FIELD_LEADING_TRIVIA) !== 0;
12781
+ const trailing = (bits & FIELD_TRAILING_TRIVIA) !== 0;
12699
12782
  return (input, pos, ctx) => {
12783
+ const leadFrom = leading ? LEAD.at : -1;
12784
+ LEAD.at = -1;
12700
12785
  const v = child(input, pos, ctx);
12701
12786
  if (v === FAIL) return FAIL;
12702
- ctx._fields?.push({ name, value: v, span: { start: pos, end: EC.e } });
12787
+ const end = EC.e;
12788
+ const trailEnd = trailing && ctx.trivia !== void 0 ? consumeTrivia(input, end, ctx) : end;
12789
+ ctx._fields?.push(triviaFieldCapture(name, v, input, pos, end, leadFrom, trailEnd));
12790
+ EC.e = trailEnd;
12703
12791
  return v;
12704
12792
  };
12705
12793
  }
@@ -13133,6 +13221,12 @@ function assemble(t, prog, cfg) {
13133
13221
  const kidIp = code[base + i];
13134
13222
  if (code[kidIp] !== OP_ADJ) {
13135
13223
  const kid = link(kidIp);
13224
+ if (leadingTriviaFieldAt(code, k, kidIp)) {
13225
+ return (input, cur, ctx, inherited) => {
13226
+ ctx._sync = sync ?? inherited;
13227
+ return leadingFieldTerm(step, kid, input, cur, ctx);
13228
+ };
13229
+ }
13136
13230
  return (input, cur, ctx, inherited) => {
13137
13231
  ctx._sync = sync ?? inherited;
13138
13232
  return step(kid, input, cur, ctx);
@@ -13210,9 +13304,12 @@ function assemble(t, prog, cfg) {
13210
13304
  };
13211
13305
  }
13212
13306
  let hasAdj = false;
13213
- for (let i = 1; i < n; i++) if (code[code[base + i]] === OP_ADJ) {
13214
- hasAdj = true;
13215
- break;
13307
+ for (let i = 1; i < n; i++) {
13308
+ const kidIp = code[base + i];
13309
+ if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp)) {
13310
+ hasAdj = true;
13311
+ break;
13312
+ }
13216
13313
  }
13217
13314
  if (hasAdj) {
13218
13315
  const first2 = link(code[base]);
@@ -13220,6 +13317,9 @@ function assemble(t, prog, cfg) {
13220
13317
  const kidIp = code[base + i];
13221
13318
  if (code[kidIp] !== OP_ADJ) {
13222
13319
  const kid = link(kidIp);
13320
+ if (leadingTriviaFieldAt(code, k, kidIp)) {
13321
+ return (input, cur, ctx) => leadingFieldTerm(step, kid, input, cur, ctx);
13322
+ }
13223
13323
  return (input, cur, ctx) => step(kid, input, cur, ctx);
13224
13324
  }
13225
13325
  const negated = code[kidIp + 1] === 1;
@@ -13582,7 +13682,7 @@ function assemble(t, prog, cfg) {
13582
13682
  if (n === 2 || n === 3) {
13583
13683
  const a0 = link(code[base]), a1 = link(code[base + 1]);
13584
13684
  const a2 = n === 3 ? link(code[base + 2]) : void 0;
13585
- const predecide = !hostCst && !REC && !cfg.probe && !cfg.coverage && !cfg.trackLines;
13685
+ const predecide = !REC && !cfg.probe && !cfg.coverage && !cfg.trackLines;
13586
13686
  const p0ip = predecide ? leadingScalarTerminal(code, code[base], 2, true, true) : -1;
13587
13687
  const p1ip = predecide ? leadingScalarTerminal(code, code[base + 1], 2, true, true) : -1;
13588
13688
  const p2ip = predecide && n === 3 ? leadingScalarTerminal(code, code[base + 2], 2, true, true) : -1;
@@ -13805,6 +13905,75 @@ function assemble(t, prog, cfg) {
13805
13905
  );
13806
13906
  };
13807
13907
  const masked = bindMasked(0, EMPTY_EXPECTED_PREFIX);
13908
+ const pBit = [];
13909
+ const pRec = [];
13910
+ if (!REC && !cfg.probe && !cfg.coverage && !cfg.trackLines) {
13911
+ for (let i = 0; i < n; i++) {
13912
+ for (const terminal of largeChoiceLiteralPretest(code, k, code[base + i]) ?? []) {
13913
+ pBit.push(1 << i);
13914
+ pRec.push(scalarFor(terminal));
13915
+ }
13916
+ }
13917
+ }
13918
+ const pn = pBit.length;
13919
+ if (pn !== 0) {
13920
+ return (input, pos, ctx) => {
13921
+ const c = lead(input, pos);
13922
+ const need = markCst(ctx);
13923
+ const mRaw = MRAW;
13924
+ const mTl = MTL;
13925
+ const mLv = MLV;
13926
+ const mFl = need ? ctx._fields?.length ?? 0 : 0;
13927
+ const mEr = need ? ctx._errors?.length ?? 0 : 0;
13928
+ const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
13929
+ const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
13930
+ if (c < 128) {
13931
+ const bits = mask[c < 0 ? 128 : c];
13932
+ let tested = 0;
13933
+ let hit = 0;
13934
+ for (let i = 0; i < pn; i++) {
13935
+ const b = pBit[i];
13936
+ if ((bits & b) === 0 || (hit & b) !== 0) continue;
13937
+ tested |= b;
13938
+ if (pRec[i](input, pos) >= 0) hit |= b;
13939
+ }
13940
+ return masked(
13941
+ input,
13942
+ pos,
13943
+ ctx,
13944
+ bits & (~tested | hit),
13945
+ need,
13946
+ mRaw,
13947
+ mTl,
13948
+ mLv,
13949
+ mFl,
13950
+ mEr,
13951
+ mLog,
13952
+ mRoot,
13953
+ void 0,
13954
+ 0,
13955
+ pos
13956
+ );
13957
+ }
13958
+ return general(
13959
+ input,
13960
+ pos,
13961
+ ctx,
13962
+ c,
13963
+ need,
13964
+ mRaw,
13965
+ mTl,
13966
+ mLv,
13967
+ mFl,
13968
+ mEr,
13969
+ mLog,
13970
+ mRoot,
13971
+ void 0,
13972
+ 0,
13973
+ pos
13974
+ );
13975
+ };
13976
+ }
13808
13977
  return (input, pos, ctx) => {
13809
13978
  const c = lead(input, pos);
13810
13979
  const need = markCst(ctx);
@@ -13966,6 +14135,7 @@ function assemble(t, prog, cfg) {
13966
14135
  const max = code[ip + 3];
13967
14136
  const sepIp = code[ip + 4];
13968
14137
  const sep2 = sepIp >= 0 ? link(sepIp) : void 0;
14138
+ const sepLeads = sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp);
13969
14139
  const flags = code[ip + 5];
13970
14140
  const keepSeparators = (flags & 2) !== 0;
13971
14141
  const trailingAllowed = (flags & 1) !== 0;
@@ -14005,6 +14175,7 @@ function assemble(t, prog, cfg) {
14005
14175
  let sp = cur;
14006
14176
  if (hasTrivia) sp = skipTrivia(input, sp, ctx);
14007
14177
  ctx._fc = false;
14178
+ if (sepLeads) LEAD.at = cur;
14008
14179
  const sv = sep2(input, sp, ctx);
14009
14180
  if (sv === FAIL) {
14010
14181
  if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
@@ -14094,6 +14265,7 @@ function assemble(t, prog, cfg) {
14094
14265
  let sp = cur;
14095
14266
  if (hasTrivia) sp = skipTrivia(input, sp, ctx);
14096
14267
  ctx._fc = false;
14268
+ if (sepLeads) LEAD.at = cur;
14097
14269
  const sv = sep2(input, sp, ctx);
14098
14270
  if (sv === FAIL) {
14099
14271
  if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
@@ -14416,20 +14588,9 @@ function assemble(t, prog, cfg) {
14416
14588
  if (child >= 0) scalarFor(child);
14417
14589
  continue;
14418
14590
  }
14419
- if (child >= 0) {
14420
- const spec = k[code[child + 1]];
14421
- if (code[child] === OP_LIT && typeof spec === "string" && spec.length >= 2) {
14422
- scalarFor(child);
14423
- continue;
14424
- }
14425
- }
14426
- const family = leadingLiteralFamily(code, k, armIp);
14427
- if (family !== void 0 && family.length >= 2 && family.every((terminal) => {
14428
- const value = k[code[terminal + 1]];
14429
- return typeof value === "string" && value.length >= 2;
14430
- })) {
14431
- for (const terminal of family) scalarFor(terminal);
14432
- }
14591
+ const literals = largeChoiceLiteralPretest(code, k, armIp);
14592
+ if (literals !== void 0) for (const terminal of literals) scalarFor(terminal);
14593
+ if (literals !== void 0 && literals.length === 1 && literals[0] === child) continue;
14433
14594
  const sequence6 = leadingScalarSequence(code, armIp);
14434
14595
  if (sequence6 !== void 0 && (sequence6.trivia < 0 || triviaScan[sequence6.trivia] != null)) {
14435
14596
  for (const terminal of sequence6.terminals) scalarFor(terminal);
@@ -14495,7 +14656,9 @@ function assemble(t, prog, cfg) {
14495
14656
  scanTriviaCompact,
14496
14657
  t.lex,
14497
14658
  adjacencyHolds,
14498
- t.lexPrograms
14659
+ t.lexPrograms,
14660
+ LEAD,
14661
+ triviaFieldCapture
14499
14662
  );
14500
14663
  emitReached = new Set(pre.reached);
14501
14664
  }
@@ -14577,7 +14740,9 @@ function assemble(t, prog, cfg) {
14577
14740
  scanTriviaCompact,
14578
14741
  t.lex,
14579
14742
  adjacencyHolds,
14580
- t.lexPrograms
14743
+ t.lexPrograms,
14744
+ LEAD,
14745
+ triviaFieldCapture
14581
14746
  );
14582
14747
  emitReached = em.reached;
14583
14748
  } catch (e) {
@@ -14753,7 +14918,9 @@ var AssemblyCache = class {
14753
14918
  return made;
14754
14919
  }
14755
14920
  };
14756
- function tableRules(source, artifactMetadata = {}) {
14921
+ function tableRules(source, metadataOrEntry = {}) {
14922
+ const entry = typeof metadataOrEntry === "string" ? metadataOrEntry : void 0;
14923
+ const artifactMetadata = typeof metadataOrEntry === "string" ? {} : metadataOrEntry;
14757
14924
  const prog = expandCompact(source);
14758
14925
  const resolved = resolveTable(prog);
14759
14926
  const cache = new AssemblyCache(prog, resolved);
@@ -14761,7 +14928,7 @@ function tableRules(source, artifactMetadata = {}) {
14761
14928
  const skipOf = prog.scanSkipOf;
14762
14929
  let last;
14763
14930
  let selected;
14764
- return stampRuleMap(prog, {
14931
+ const map = stampRuleMap(prog, {
14765
14932
  runRule: (ri, input, pos, ctx) => {
14766
14933
  const a = selected ?? cache.forCtx(ctx);
14767
14934
  selected = void 0;
@@ -14782,6 +14949,9 @@ function tableRules(source, artifactMetadata = {}) {
14782
14949
  return a.scanSkip[skipOf?.[ri] ?? -1];
14783
14950
  }
14784
14951
  }, artifactMetadata, resolved);
14952
+ if (entry === void 0) return map;
14953
+ if (!Object.hasOwn(map, entry)) throw new Error(`tableRules: the program has no rule ${JSON.stringify(entry)}`);
14954
+ return map[entry];
14785
14955
  }
14786
14956
 
14787
14957
  // src/table/emit.ts
@@ -14947,11 +15117,15 @@ function emitTableExpression(prog, opts = {}) {
14947
15117
  const entry = opts.entry === void 0 ? "grammar" : opts.entry;
14948
15118
  const ref2 = opts.runtimeRef ?? "tableRules";
14949
15119
  const fns = opts.fnSources ?? prog.fns.map(() => "() => {}");
14950
- const close = `}${opts.metadataSource === void 0 ? "" : `,${opts.metadataSource}`})`;
15120
+ const meta = opts.metadataSource;
15121
+ if (entry !== null && meta !== void 0) {
15122
+ throw new Error("emitTableExpression: metadataSource is for the rule-map form (entry: null); a single entry carries none");
15123
+ }
15124
+ const tail = entry !== null ? `,${jsString(entry)}` : meta === void 0 ? "" : `,${meta}`;
14951
15125
  return [
14952
15126
  `/* @__PURE__ */ ${ref2}({`,
14953
15127
  ...programFields(prog, fns, opts),
14954
- entry === null ? close : `${close}[${jsString(entry)}]`
15128
+ `}${tail})`
14955
15129
  ].join("\n");
14956
15130
  }
14957
15131
 
@@ -15020,7 +15194,8 @@ function children(def, winners) {
15020
15194
  return [];
15021
15195
  }
15022
15196
  const name = resolved._ruleName;
15023
- return name && winners?.[name] ? [winners[name]] : [resolved];
15197
+ const winner = name ? winners?.[name] : void 0;
15198
+ return winner !== void 0 && winner._def !== def ? [winner] : [resolved];
15024
15199
  }
15025
15200
  default:
15026
15201
  return [];
@@ -15045,16 +15220,39 @@ function buildGrammarPlan(entry, winners) {
15045
15220
  }
15046
15221
  }
15047
15222
  }
15223
+ const selfWinnerBody = (parser2) => {
15224
+ if (parser2._def.tag !== "lazy" || winners === void 0) return void 0;
15225
+ const name = winnerNames.get(parser2);
15226
+ if (name === void 0 || winners[name] !== parser2) return void 0;
15227
+ try {
15228
+ return parser2._def.thunk();
15229
+ } catch {
15230
+ return void 0;
15231
+ }
15232
+ };
15233
+ const slotsPerBody = /* @__PURE__ */ new Map();
15234
+ if (winners) {
15235
+ for (const winner of Object.values(winners)) {
15236
+ const body = selfWinnerBody(winner);
15237
+ if (body !== void 0) slotsPerBody.set(body, (slotsPerBody.get(body) ?? 0) + 1);
15238
+ }
15239
+ }
15240
+ const sharedBody = (parser2) => (slotsPerBody.get(parser2) ?? 0) > 1;
15241
+ const ownRule2 = (parser2, rule) => {
15242
+ const id = `rule:${rule}`;
15243
+ definitions.set(id, { id, kind: "rule" });
15244
+ rules2.set(parser2, id);
15245
+ };
15048
15246
  const seen = /* @__PURE__ */ new Set();
15049
15247
  const visit = (parser2, path3) => {
15050
15248
  if (seen.has(parser2)) return;
15051
15249
  seen.add(parser2);
15052
15250
  const rule = winnerNames.get(parser2) ?? parser2._ruleName;
15053
- if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
15054
- const id = `rule:${rule}`;
15055
- definitions.set(id, { id, kind: "rule" });
15056
- rules2.set(parser2, id);
15251
+ const body = selfWinnerBody(parser2);
15252
+ if (rule && body === void 0 && !sharedBody(parser2) && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
15253
+ ownRule2(parser2, rule);
15057
15254
  }
15255
+ if (rule && body !== void 0 && sharedBody(body)) ownRule2(parser2, rule);
15058
15256
  if (parser2._def.tag === "choice") {
15059
15257
  const ids = parser2._def.parsers.map((_, index) => `choice:${path3}/arm:${index}`);
15060
15258
  choices.set(parser2, ids);
@@ -15075,7 +15273,13 @@ function buildGrammarPlan(entry, winners) {
15075
15273
  };
15076
15274
  const roots = Array.isArray(entry) ? entry : [entry];
15077
15275
  for (const root of roots) {
15078
- visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
15276
+ const name = winnerNames.get(root) ?? root._ruleName ?? "entry";
15277
+ const body = selfWinnerBody(root);
15278
+ if (body !== void 0 && sharedBody(body) && !seen.has(root)) {
15279
+ seen.add(root);
15280
+ ownRule2(root, name);
15281
+ }
15282
+ visit(body ?? root, name);
15079
15283
  }
15080
15284
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
15081
15285
  }
@@ -15099,7 +15303,7 @@ function payloadKey(p, d) {
15099
15303
  case "label":
15100
15304
  return `label\0${d.label}`;
15101
15305
  case "field":
15102
- return `field\0${d.name}`;
15306
+ return `field\0${d.name}\0${d.leadingTrivia === true}\0${d.trailingTrivia === true}`;
15103
15307
  case "node":
15104
15308
  return `node\0${d.type ?? ""}\0${d.unwrap === true}\0${d.collapse === true}\0${fnKey(d.buildSrc, d.build)}`;
15105
15309
  case "transform":
@@ -16381,105 +16585,299 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
16381
16585
  return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16382
16586
  }
16383
16587
 
16384
- // src/table/compile.ts
16385
- var ENTRY = "Entry";
16386
- function ownSource(fn) {
16387
- if (typeof fn !== "function") return null;
16388
- const text = Function.prototype.toString.call(fn);
16389
- if (!text.includes("[native code]")) return text;
16390
- const name = fn.name;
16391
- return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
16588
+ // src/table/compile-rule-map.ts
16589
+ var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
16590
+ function resolveHostMode(ruleMap, explicit) {
16591
+ return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
16392
16592
  }
16393
- function compile(combinator, mapFnSources, opts = {}) {
16394
- runDuplicationDiagnostic(combinator, opts.duplication);
16395
- const drain = beginCompileDegradationDrain();
16396
- let compiled = false;
16397
- try {
16398
- const out = compileImpl(combinator, mapFnSources, opts);
16399
- compiled = true;
16400
- return out;
16401
- } finally {
16402
- drain(compiled);
16403
- }
16593
+ function resolveTrackLines(ruleMap, explicit) {
16594
+ return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
16404
16595
  }
16405
- function compileImpl(combinator, mapFnSources, opts = {}) {
16406
- const plan = opts.coverage === true ? buildGrammarPlan(combinator) : void 0;
16407
- const settings = {
16408
- ...opts.hostMode === void 0 ? {} : { hostMode: opts.hostMode },
16409
- ...opts.trackLines === void 0 ? {} : { trackLines: opts.trackLines },
16410
- ...plan === void 0 ? {} : { coverage: plan }
16411
- };
16412
- const { prog, fnSrcs } = encodeTableProgram({ [ENTRY]: combinator }, settings);
16596
+ function withAmbient(ruleMap, opts) {
16597
+ if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
16598
+ const wrappers = /* @__PURE__ */ new Map();
16599
+ return ruleMap.map(([name, rule]) => {
16600
+ if (rule._meta.isTrivia) return [name, rule];
16601
+ const addTrivia = opts.trivia !== void 0 && rule._meta.grammarTrivia === void 0;
16602
+ const addScanSkip = opts.scanSkip !== void 0 && rule._meta.grammarScanSkip === void 0;
16603
+ if (!addTrivia && !addScanSkip) return [name, rule];
16604
+ let wrapped = wrappers.get(rule);
16605
+ if (wrapped === void 0) {
16606
+ wrapped = {
16607
+ ...rule,
16608
+ _meta: {
16609
+ ...rule._meta,
16610
+ ...addTrivia ? { grammarTrivia: opts.trivia } : {},
16611
+ ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
16612
+ }
16613
+ };
16614
+ wrappers.set(rule, wrapped);
16615
+ }
16616
+ return [name, wrapped];
16617
+ });
16618
+ }
16619
+ function compileRuleMapRunnable(ruleMap, opts = {}) {
16620
+ const encoded = encodeForRun(ruleMap, opts);
16621
+ if (encoded === null) return null;
16622
+ const { prog, hostMode, plan } = encoded;
16413
16623
  const artifact = closureArtifact(prog);
16414
- const entry = tableRules(prog)[ENTRY];
16415
- const offered = opts.fnSources ?? mapFnSources;
16416
- const supplied = offered !== void 0 && offered.length === fnSrcs.length ? offered : void 0;
16417
- const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? ownSource(prog.fns[i]));
16418
- const unsourced = sources.reduce((acc, s, i) => s === null ? [...acc, i] : acc, []);
16419
- const runtimeOnly = [
16420
- ...prog.runtimeOnly ?? [],
16421
- ...unsourced.length === 0 ? [] : [`author reducer source missing for prog.fns[${unsourced.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`]
16422
- ];
16423
- const printable = runtimeOnly.length === 0;
16424
- const emitOpts = { fnSources: sources };
16425
- const source = printable ? emitTableModule(artifact, { name: "grammar", ...emitOpts }) : "";
16426
- const inlineExpression = printable ? emitTableExpression(artifact, { entry: ENTRY, runtimeRef: opts.runtimeRef ?? "tableRules", ...emitOpts }) : null;
16427
- const runOnce = (input, pos, ctx) => entry(input, pos, ctx);
16624
+ const reflection = collectGrammarReflection(ruleMap);
16428
16625
  return {
16429
- source,
16430
- inlineExpression,
16431
- ...printable ? {} : { runtimeOnly },
16432
- // The DENOMINATOR, handed back rather than scraped out of the emitted text.
16433
- // `plugin/index.ts` prefers a compiler-supplied `coverageDefinitions` and only
16434
- // falls back to regex-scanning generated hooks; a table has no hooks to scan,
16435
- // so this is the channel.
16626
+ keys: ruleMap.map(([key]) => key),
16627
+ hostMode,
16628
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
16629
+ reflection,
16436
16630
  ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16437
- parse(input, pos = 0) {
16438
- return runOnce(input, pos, createParseContext());
16439
- },
16440
- parseWithContext(input, parseCtx, pos = 0) {
16441
- return runOnce(input, pos, parseCtx);
16442
- },
16443
- /**
16444
- * NO LONGER REFUSES. It used to throw unless the caller passed
16445
- * `{ recovery: true }`, because setting `_tolerant` on a table with no
16446
- * recovery rows would report a clean parse with an empty error list whatever
16447
- * the input — refusing was the right answer to that, and OPTIONAL RECOVERY
16448
- * was the wrong question. `compile()` requires no such flag, so the refusal
16449
- * was a `CompiledParser` contract break; recovery is now always lowered and
16450
- * there is nothing left to refuse.
16451
- */
16452
- parseWithErrors(input, pos = 0) {
16453
- const ctx = createParseContext();
16454
- const errors = [];
16455
- ctx._errors = errors;
16456
- ctx._tolerant = true;
16457
- const r = runOnce(input, pos, ctx);
16458
- return { ...r, errors };
16459
- }
16631
+ // Reflection rides on the map's metadata prototype from birth. Only `compile()`
16632
+ // asks to specialise; every other caller runs the closure artifact.
16633
+ rules: tableRules(opts.specialise === true ? prog : artifact, { [GRAMMAR_REFLECTION]: reflection }),
16634
+ prog: artifact
16460
16635
  };
16461
16636
  }
16462
-
16463
- // src/combinators/withCtx.ts
16464
- function withCtx(extra, combinator) {
16465
- const meta = {
16466
- firstSet: combinator._meta.firstSet,
16467
- canMatchNewline: combinator._meta.canMatchNewline,
16468
- isTrivia: false
16637
+ function encodeForRun(ruleMap, opts) {
16638
+ runDuplicationDiagnosticRules(ruleMap, opts.duplication);
16639
+ const encodeMap = withAmbient(ruleMap, opts);
16640
+ const plan = opts.coverage === true ? buildGrammarPlan(
16641
+ encodeMap.map(([, rule]) => rule),
16642
+ Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== "lazy"))
16643
+ ) : void 0;
16644
+ const hostMode = resolveHostMode(ruleMap, opts.hostMode);
16645
+ const settings = {
16646
+ hostMode,
16647
+ ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
16648
+ ...opts.recovery === true ? { recovery: true } : {},
16649
+ ...plan === void 0 ? {} : { coverage: plan }
16469
16650
  };
16470
- return {
16471
- _tag: "withCtx",
16472
- _meta: meta,
16473
- _def: { tag: "withCtx", extra, parser: combinator },
16474
- parse(input, pos, ctx) {
16475
- const saved = ctx.state;
16476
- ctx.state = extra;
16651
+ try {
16652
+ const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
16653
+ return { prog, fnSrcs, hostMode, plan };
16654
+ } catch (e) {
16655
+ opts.refusals?.push(e instanceof Error ? e.message : String(e));
16656
+ return null;
16657
+ }
16658
+ }
16659
+ function compileRuleMap(ruleMap, opts = {}) {
16660
+ const encoded = encodeForRun(ruleMap, opts);
16661
+ if (encoded === null) return null;
16662
+ const { prog, fnSrcs, hostMode, plan } = encoded;
16663
+ const artifact = closureArtifact(prog);
16664
+ if (artifact.runtimeOnly !== void 0 && artifact.runtimeOnly.length > 0) {
16665
+ opts.refusals?.push(...artifact.runtimeOnly);
16666
+ return null;
16667
+ }
16668
+ const supplied = opts.fnSources;
16669
+ const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null);
16670
+ if (sources.some((s) => s === null)) {
16671
+ const missing = sources.flatMap((s, i) => s === null ? [i] : []);
16672
+ opts.refusals?.push(
16673
+ `author reducer source missing for prog.fns[${missing.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`
16674
+ );
16675
+ return null;
16676
+ }
16677
+ if (supplied !== void 0 && supplied.length > fnSrcs.length) {
16678
+ throw new Error(
16679
+ `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.`
16680
+ );
16681
+ }
16682
+ const replacementWithMetadata = (metadataSource, options = {}) => emitTableExpression(artifact, {
16683
+ entry: null,
16684
+ runtimeRef: opts.runtimeRef ?? "tableRules",
16685
+ fnSources: sources,
16686
+ ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
16687
+ ...metadataSource === void 0 ? {} : { metadataSource }
16688
+ });
16689
+ const replacement = replacementWithMetadata();
16690
+ return {
16691
+ keys: ruleMap.map(([key]) => key),
16692
+ replacement,
16693
+ replacementWithMetadata,
16694
+ hostMode,
16695
+ // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
16696
+ // dropped", which is what the driver's `'ast' artifact + CST host` check keys
16697
+ // off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
16698
+ // direct builder, so there was no branch to drop and it stays usable with either
16699
+ // host — the long-standing `node(parser)` contract. Same predicate codegen uses
16700
+ // for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
16701
+ // lowerings stamp the same artifact rather than disagreeing about it.
16702
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
16703
+ reflection: collectGrammarReflection(ruleMap),
16704
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16705
+ rules: tableRules(artifact),
16706
+ prog: artifact
16707
+ };
16708
+ }
16709
+
16710
+ // src/compiler/borrowed-rules.ts
16711
+ var nameOf = (c) => c._ruleName;
16712
+ function deref(c) {
16713
+ const seen = /* @__PURE__ */ new Set();
16714
+ let current = c;
16715
+ while (current._def.tag === "lazy" && !seen.has(current)) {
16716
+ seen.add(current);
16717
+ try {
16718
+ current = current._def.thunk();
16719
+ } catch {
16720
+ return void 0;
16721
+ }
16722
+ }
16723
+ return current;
16724
+ }
16725
+ function borrowedRuleReference(map) {
16726
+ const seen = /* @__PURE__ */ new Set();
16727
+ for (const [rule, self] of Object.entries(map)) {
16728
+ let found;
16729
+ const visit = (c) => {
16730
+ if (found !== void 0) return;
16731
+ if (c._def.tag === "lazy") {
16732
+ if (!winnerWrapsReference(self, c)) {
16733
+ const name = nameOf(c);
16734
+ const winner = name !== void 0 && Object.hasOwn(map, name) ? map[name] : void 0;
16735
+ if (winner !== void 0) {
16736
+ if (winnerWrapsReference(winner, c) || deref(c) === deref(winner)) return;
16737
+ found = name;
16738
+ return;
16739
+ }
16740
+ }
16741
+ if (seen.has(c)) return;
16742
+ seen.add(c);
16743
+ let body;
16744
+ try {
16745
+ body = c._def.thunk();
16746
+ } catch {
16747
+ return;
16748
+ }
16749
+ visit(body);
16750
+ return;
16751
+ }
16752
+ if (seen.has(c)) return;
16753
+ seen.add(c);
16754
+ for (const child of childrenOf2(c._def)) visit(child);
16755
+ };
16756
+ visit(self);
16757
+ if (found !== void 0) return { rule, reference: found };
16758
+ }
16759
+ return void 0;
16760
+ }
16761
+ function borrowedRuleMessage(api, hit) {
16762
+ 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.`;
16763
+ }
16764
+
16765
+ // src/table/compile.ts
16766
+ var ENTRY = "Entry";
16767
+ function ownSource(fn) {
16768
+ if (typeof fn !== "function") return null;
16769
+ const text = Function.prototype.toString.call(fn);
16770
+ if (!text.includes("[native code]")) return text;
16771
+ const name = fn.name;
16772
+ return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
16773
+ }
16774
+ function compile(target, second, third) {
16775
+ if (!isCombinator(target)) return compileMap(target, second);
16776
+ return compileRoot(target, second, third);
16777
+ }
16778
+ function isCombinator(x) {
16779
+ return typeof x === "object" && x !== null && typeof x.parse === "function" && "_def" in x;
16780
+ }
16781
+ function compileMap(grammar, opts = {}) {
16782
+ const entries = [];
16783
+ for (const [key, value] of Object.entries(grammar)) {
16784
+ if (!isCombinator(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
16785
+ if (value._def.tag === "lazy") {
16477
16786
  try {
16478
- return combinator.parse(input, pos, ctx);
16479
- } finally {
16480
- ctx.state = saved;
16787
+ value._def.thunk();
16788
+ } catch {
16789
+ continue;
16481
16790
  }
16482
16791
  }
16792
+ entries.push([key, value]);
16793
+ }
16794
+ const borrowed = borrowedRuleReference(Object.fromEntries(entries));
16795
+ if (borrowed !== void 0) throw new Error(borrowedRuleMessage("compile", borrowed));
16796
+ const drain = beginCompileDegradationDrain();
16797
+ let done = false;
16798
+ try {
16799
+ const refusals = [];
16800
+ const compiled = compileRuleMapRunnable(entries, {
16801
+ ...opts.hostMode ? { hostMode: opts.hostMode } : {},
16802
+ refusals,
16803
+ specialise: true
16804
+ });
16805
+ if (compiled === null) {
16806
+ throw new Error(`compile: this grammar could not be encoded to a table${refusals.length ? ` \u2014 ${refusals.join("; ")}` : ""}`);
16807
+ }
16808
+ done = true;
16809
+ return compiled.rules;
16810
+ } finally {
16811
+ drain(done);
16812
+ }
16813
+ }
16814
+ function compileRoot(combinator, mapFnSources, opts = {}) {
16815
+ runDuplicationDiagnostic(combinator, opts.duplication);
16816
+ const drain = beginCompileDegradationDrain();
16817
+ let compiled = false;
16818
+ try {
16819
+ const out = compileImpl(combinator, mapFnSources, opts);
16820
+ compiled = true;
16821
+ return out;
16822
+ } finally {
16823
+ drain(compiled);
16824
+ }
16825
+ }
16826
+ function compileImpl(combinator, mapFnSources, opts = {}) {
16827
+ const plan = opts.coverage === true ? buildGrammarPlan(combinator) : void 0;
16828
+ const settings = {
16829
+ ...opts.hostMode === void 0 ? {} : { hostMode: opts.hostMode },
16830
+ ...opts.trackLines === void 0 ? {} : { trackLines: opts.trackLines },
16831
+ ...plan === void 0 ? {} : { coverage: plan }
16832
+ };
16833
+ const { prog, fnSrcs } = encodeTableProgram({ [ENTRY]: combinator }, settings);
16834
+ const artifact = closureArtifact(prog);
16835
+ const entry = tableRules(prog)[ENTRY];
16836
+ const offered = opts.fnSources ?? mapFnSources;
16837
+ const supplied = offered !== void 0 && offered.length === fnSrcs.length ? offered : void 0;
16838
+ const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? ownSource(prog.fns[i]));
16839
+ const unsourced = sources.reduce((acc, s, i) => s === null ? [...acc, i] : acc, []);
16840
+ const runtimeOnly = [
16841
+ ...prog.runtimeOnly ?? [],
16842
+ ...unsourced.length === 0 ? [] : [`author reducer source missing for prog.fns[${unsourced.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`]
16843
+ ];
16844
+ const printable = runtimeOnly.length === 0;
16845
+ const emitOpts = { fnSources: sources };
16846
+ const source = printable ? emitTableModule(artifact, { name: "grammar", ...emitOpts }) : "";
16847
+ const inlineExpression = printable ? emitTableExpression(artifact, { entry: ENTRY, runtimeRef: opts.runtimeRef ?? "tableRules", ...emitOpts }) : null;
16848
+ const runOnce = (input, pos, ctx) => entry(input, pos, ctx);
16849
+ return {
16850
+ source,
16851
+ inlineExpression,
16852
+ ...printable ? {} : { runtimeOnly },
16853
+ // The DENOMINATOR, handed back rather than scraped out of the emitted text.
16854
+ // `plugin/index.ts` prefers a compiler-supplied `coverageDefinitions` and only
16855
+ // falls back to regex-scanning generated hooks; a table has no hooks to scan,
16856
+ // so this is the channel.
16857
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16858
+ parse(input, pos = 0) {
16859
+ return runOnce(input, pos, createParseContext());
16860
+ },
16861
+ parseWithContext(input, parseCtx, pos = 0) {
16862
+ return runOnce(input, pos, parseCtx);
16863
+ },
16864
+ /**
16865
+ * NO LONGER REFUSES. It used to throw unless the caller passed
16866
+ * `{ recovery: true }`, because setting `_tolerant` on a table with no
16867
+ * recovery rows would report a clean parse with an empty error list whatever
16868
+ * the input — refusing was the right answer to that, and OPTIONAL RECOVERY
16869
+ * was the wrong question. `compile()` requires no such flag, so the refusal
16870
+ * was a `CompiledParser` contract break; recovery is now always lowered and
16871
+ * there is nothing left to refuse.
16872
+ */
16873
+ parseWithErrors(input, pos = 0) {
16874
+ const ctx = createParseContext();
16875
+ const errors = [];
16876
+ ctx._errors = errors;
16877
+ ctx._tolerant = true;
16878
+ const r = runOnce(input, pos, ctx);
16879
+ return { ...r, errors };
16880
+ }
16483
16881
  };
16484
16882
  }
16485
16883
 
@@ -16535,219 +16933,52 @@ var ruleNameOf3 = (c) => c._ruleName;
16535
16933
  var balancedOf = (c) => c._balancedAmbient;
16536
16934
  function lazyTarget(c) {
16537
16935
  if (c._def.tag !== "lazy") return null;
16538
- try {
16539
- return c._def.thunk();
16540
- } catch {
16541
- return null;
16542
- }
16543
- }
16544
- var Unserializable = class extends Error {
16545
- };
16546
- function repeatOpts(min, max, trailing) {
16547
- const parts = [];
16548
- if (min !== 0) parts.push(`min: ${min}`);
16549
- if (max !== void 0) parts.push(`max: ${max}`);
16550
- if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
16551
- return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
16552
- }
16553
- function projectOpt(project) {
16554
- if (project === void 0) return void 0;
16555
- return `project: ${project}`;
16556
- }
16557
- function tagsOpt(tags) {
16558
- if (tags === void 0 || tags.length === 0) return void 0;
16559
- return `tags: ${JSON.stringify(tags)}`;
16560
- }
16561
- function matcherExpr(entry) {
16562
- switch (entry.kind) {
16563
- case "startsWith":
16564
- return `startsWith(${JSON.stringify(entry.value)})`;
16565
- case "endsWith":
16566
- return `endsWith(${JSON.stringify(entry.value)})`;
16567
- case "matches":
16568
- return `matches(${new RegExp(entry.value, entry.flags ?? "").toString()})`;
16569
- }
16570
- }
16571
- function serializeRuleMap(ruleMap, scanSkip) {
16572
- try {
16573
- return new Serializer(ruleMap, scanSkip ?? []).run();
16574
- } catch (e) {
16575
- if (e instanceof Unserializable) {
16576
- if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
16577
- return null;
16578
- }
16579
- throw e;
16580
- }
16581
- }
16582
- function evalRuleMapIR(ir) {
16583
- const _tf = (child, src, recognitionOnly = false) => {
16584
- let fn2;
16585
- try {
16586
- fn2 = (0, eval)(`(${src})`);
16587
- } catch {
16588
- fn2 = () => {
16589
- throw new Error("IR transform fn not materialized");
16590
- };
16591
- }
16592
- const t = transform(child, fn2);
16593
- t._def.fnSrc = src;
16594
- if (recognitionOnly) t._def.recognitionOnly = true;
16595
- return t;
16596
- };
16597
- const _lf = (child, src, source = false) => {
16598
- let fn2;
16599
- try {
16600
- fn2 = (0, eval)(`(${src})`);
16601
- } catch {
16602
- fn2 = () => {
16603
- throw new Error("IR leaf fn not materialized");
16604
- };
16605
- }
16606
- const l = source ? sourceLeaf(child, fn2) : leaf(child, fn2);
16607
- l._def.fnSrc = src;
16608
- return l;
16609
- };
16610
- const _nd = (type, child, src, opts, staticError, sigSrc, buildImports, rawUnused) => {
16611
- if (staticError !== void 0 && staticError.length > 0) {
16612
- throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
16613
- }
16614
- const n = node(type, child, (() => {
16615
- throw new Error("IR node build requires static re-lowering");
16616
- }), opts);
16617
- n._def.buildSrc = src;
16618
- if (staticError !== void 0) n._def.buildStaticError = staticError;
16619
- if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
16620
- if (rawUnused === true) n._def.buildRawUnused = true;
16621
- if (buildImports !== void 0) n._def.buildImports = buildImports;
16622
- return n;
16623
- };
16624
- const _gch = (items) => {
16625
- const gateSrcs = [];
16626
- const arms = items.map((it) => {
16627
- if (Array.isArray(it)) {
16628
- const [src, comb] = it;
16629
- gateSrcs.push(src);
16630
- let gate2;
16631
- try {
16632
- gate2 = (0, eval)(`(${src})`);
16633
- } catch {
16634
- gate2 = () => {
16635
- throw new Error("IR gate fn not materialized");
16636
- };
16637
- }
16638
- return { gate: gate2, combinator: comb };
16639
- }
16640
- gateSrcs.push(null);
16641
- return it;
16642
- });
16643
- const c = choice(...arms);
16644
- const gates = c._def.gates;
16645
- if (gates && gates.length !== gateSrcs.length) {
16646
- throw new Error(`_gch: gateSrcs length ${gateSrcs.length} != choice gates length ${gates.length}`);
16647
- }
16648
- ;
16649
- c._def.gateSrcs = gateSrcs;
16650
- return c;
16651
- };
16652
- const _wc = (src, inner) => {
16653
- let extra;
16654
- try {
16655
- extra = (0, eval)(`(${src})`);
16656
- } catch {
16657
- extra = void 0;
16658
- }
16659
- const w = withCtx(extra, inner);
16660
- w._def.extraSrc = src;
16661
- return w;
16662
- };
16663
- const fn = new Function(
16664
- "rules",
16665
- "ref",
16666
- "regex",
16667
- "literal",
16668
- "keywords",
16669
- "sequence",
16670
- "choice",
16671
- "dispatch",
16672
- "when",
16673
- "startsWith",
16674
- "endsWith",
16675
- "matches",
16676
- "otherwise",
16677
- "routed",
16678
- "attempt",
16679
- "many",
16680
- "oneOrMore",
16681
- "optional",
16682
- "sepBy",
16683
- "keepSeparator",
16684
- "not",
16685
- "peek",
16686
- "node",
16687
- "parser",
16688
- "scanTo",
16689
- "balanced",
16690
- "token",
16691
- "leaf",
16692
- "transform",
16693
- "trivia",
16694
- "classifiedTrivia",
16695
- "label",
16696
- "field",
16697
- "expect",
16698
- "adjacent",
16699
- "notAdjacent",
16700
- "_tf",
16701
- "_lf",
16702
- "_nd",
16703
- "_gch",
16704
- "_wc",
16705
- `return (${ir})`
16706
- );
16707
- const map = fn(
16708
- rules,
16709
- ref,
16710
- regex,
16711
- literal,
16712
- keywords,
16713
- sequence,
16714
- choice,
16715
- dispatch,
16716
- when,
16717
- startsWith,
16718
- endsWith,
16719
- matches,
16720
- otherwise,
16721
- routed,
16722
- attempt,
16723
- many,
16724
- oneOrMore,
16725
- optional,
16726
- sepBy,
16727
- keepSeparator,
16728
- not,
16729
- peek,
16730
- node,
16731
- parser,
16732
- scanTo,
16733
- balanced,
16734
- token,
16735
- leaf,
16736
- transform,
16737
- trivia,
16738
- classifiedTrivia,
16739
- label,
16740
- field,
16741
- expect,
16742
- adjacent,
16743
- notAdjacent,
16744
- _tf,
16745
- _lf,
16746
- _nd,
16747
- _gch,
16748
- _wc
16749
- );
16750
- return Object.entries(map);
16936
+ try {
16937
+ return c._def.thunk();
16938
+ } catch {
16939
+ return null;
16940
+ }
16941
+ }
16942
+ var Unserializable = class extends Error {
16943
+ };
16944
+ function repeatOpts(min, max, trailing) {
16945
+ const parts = [];
16946
+ if (min !== 0) parts.push(`min: ${min}`);
16947
+ if (max !== void 0) parts.push(`max: ${max}`);
16948
+ if (trailing !== void 0) parts.push(`trailing: ${JSON.stringify(trailing)}`);
16949
+ return parts.length === 0 ? "" : `, { ${parts.join(", ")} }`;
16950
+ }
16951
+ function projectOpt(project) {
16952
+ if (project === void 0) return void 0;
16953
+ return `project: ${project}`;
16954
+ }
16955
+ function tagsOpt(tags) {
16956
+ if (tags === void 0 || tags.length === 0) return void 0;
16957
+ return `tags: ${JSON.stringify(tags)}`;
16958
+ }
16959
+ function matcherExpr(entry) {
16960
+ switch (entry.kind) {
16961
+ case "startsWith":
16962
+ return `startsWith(${JSON.stringify(entry.value)})`;
16963
+ case "endsWith":
16964
+ return `endsWith(${JSON.stringify(entry.value)})`;
16965
+ case "matches":
16966
+ return `matches(${new RegExp(entry.value, entry.flags ?? "").toString()})`;
16967
+ }
16968
+ }
16969
+ function serializeRuleMap(ruleMap, scanSkip) {
16970
+ try {
16971
+ return new Serializer(ruleMap, scanSkip ?? []).run();
16972
+ } catch (e) {
16973
+ if (e instanceof Unserializable) {
16974
+ if (process.env.PARSEMAN_IR_DEBUG) console.error(`[ir] fallback: ${e.message}`);
16975
+ return null;
16976
+ }
16977
+ throw e;
16978
+ }
16979
+ }
16980
+ function serializeCombinator(c) {
16981
+ return serializeRuleMap([], [c]);
16751
16982
  }
16752
16983
  var Serializer = class {
16753
16984
  counts = /* @__PURE__ */ new Map();
@@ -17011,7 +17242,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17011
17242
  case "label":
17012
17243
  return `label(${JSON.stringify(def.label)}, ${kid(def.parser)})`;
17013
17244
  case "field":
17014
- return `field(${JSON.stringify(def.name)}, ${kid(def.parser)})`;
17245
+ return `field(${JSON.stringify(def.name)}, ${kid(def.parser)}${fieldOptionsSrc(def)})`;
17015
17246
  case "expect":
17016
17247
  return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
17017
17248
  case "scanTo":
@@ -17074,128 +17305,13 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17074
17305
  }
17075
17306
  }
17076
17307
  };
17077
-
17078
- // src/table/compile-rule-map.ts
17079
- var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
17080
- function resolveHostMode(ruleMap, explicit) {
17081
- return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
17082
- }
17083
- function resolveTrackLines(ruleMap, explicit) {
17084
- return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
17085
- }
17086
- function withAmbient(ruleMap, opts) {
17087
- if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
17088
- const wrappers = /* @__PURE__ */ new Map();
17089
- return ruleMap.map(([name, rule]) => {
17090
- if (rule._meta.isTrivia) return [name, rule];
17091
- const addTrivia = opts.trivia !== void 0 && rule._meta.grammarTrivia === void 0;
17092
- const addScanSkip = opts.scanSkip !== void 0 && rule._meta.grammarScanSkip === void 0;
17093
- if (!addTrivia && !addScanSkip) return [name, rule];
17094
- let wrapped = wrappers.get(rule);
17095
- if (wrapped === void 0) {
17096
- wrapped = {
17097
- ...rule,
17098
- _meta: {
17099
- ...rule._meta,
17100
- ...addTrivia ? { grammarTrivia: opts.trivia } : {},
17101
- ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
17102
- }
17103
- };
17104
- wrappers.set(rule, wrapped);
17105
- }
17106
- return [name, wrapped];
17107
- });
17108
- }
17109
- function compileRuleMapRunnable(ruleMap, opts = {}) {
17110
- const encoded = encodeForRun(ruleMap, opts);
17111
- if (encoded === null) return null;
17112
- const { prog, hostMode, plan } = encoded;
17113
- const artifact = closureArtifact(prog);
17114
- return {
17115
- keys: ruleMap.map(([key]) => key),
17116
- hostMode,
17117
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17118
- reflection: collectGrammarReflection(ruleMap),
17119
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17120
- rules: tableRules(prog),
17121
- prog: artifact
17122
- };
17123
- }
17124
- function encodeForRun(ruleMap, opts) {
17125
- runDuplicationDiagnosticRules(ruleMap, opts.duplication);
17126
- const encodeMap = withAmbient(ruleMap, opts);
17127
- const plan = opts.coverage === true ? buildGrammarPlan(
17128
- encodeMap.map(([, rule]) => rule),
17129
- Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== "lazy"))
17130
- ) : void 0;
17131
- const hostMode = resolveHostMode(ruleMap, opts.hostMode);
17132
- const settings = {
17133
- hostMode,
17134
- ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
17135
- ...opts.recovery === true ? { recovery: true } : {},
17136
- ...plan === void 0 ? {} : { coverage: plan }
17137
- };
17138
- try {
17139
- const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
17140
- return { prog, fnSrcs, hostMode, plan };
17141
- } catch (e) {
17142
- opts.refusals?.push(e instanceof Error ? e.message : String(e));
17143
- return null;
17144
- }
17145
- }
17146
- function compileRuleMap(ruleMap, opts = {}) {
17147
- const encoded = encodeForRun(ruleMap, opts);
17148
- if (encoded === null) return null;
17149
- const { prog, fnSrcs, hostMode, plan } = encoded;
17150
- const artifact = closureArtifact(prog);
17151
- if (artifact.runtimeOnly !== void 0 && artifact.runtimeOnly.length > 0) {
17152
- opts.refusals?.push(...artifact.runtimeOnly);
17153
- return null;
17154
- }
17155
- const supplied = opts.fnSources;
17156
- const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null);
17157
- if (sources.some((s) => s === null)) {
17158
- const missing = sources.flatMap((s, i) => s === null ? [i] : []);
17159
- opts.refusals?.push(
17160
- `author reducer source missing for prog.fns[${missing.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`
17161
- );
17162
- return null;
17163
- }
17164
- if (supplied !== void 0 && supplied.length > fnSrcs.length) {
17165
- throw new Error(
17166
- `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.`
17167
- );
17168
- }
17169
- const replacementWithMetadata = (metadataSource, options = {}) => emitTableExpression(artifact, {
17170
- entry: null,
17171
- runtimeRef: opts.runtimeRef ?? "tableRules",
17172
- fnSources: sources,
17173
- ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
17174
- ...metadataSource === void 0 ? {} : { metadataSource }
17175
- });
17176
- const replacement = replacementWithMetadata();
17177
- return {
17178
- keys: ruleMap.map(([key]) => key),
17179
- replacement,
17180
- replacementWithMetadata,
17181
- hostMode,
17182
- // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
17183
- // dropped", which is what the driver's `'ast' artifact + CST host` check keys
17184
- // off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
17185
- // direct builder, so there was no branch to drop and it stays usable with either
17186
- // host — the long-standing `node(parser)` contract. Same predicate codegen uses
17187
- // for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
17188
- // lowerings stamp the same artifact rather than disagreeing about it.
17189
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17190
- reflection: collectGrammarReflection(ruleMap),
17191
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17192
- rules: tableRules(prog),
17193
- prog: artifact
17194
- };
17308
+ function fieldOptionsSrc(def) {
17309
+ if (def.leadingTrivia !== true && def.trailingTrivia !== true) return "";
17310
+ return `, { ${[def.leadingTrivia === true ? "leadingTrivia: true" : "", def.trailingTrivia === true ? "trailingTrivia: true" : ""].filter(Boolean).join(", ")} }`;
17195
17311
  }
17196
17312
 
17197
17313
  // src/version.ts
17198
- var PARSEMAN_VERSION = "0.52.0";
17314
+ var PARSEMAN_VERSION = "0.54.0";
17199
17315
 
17200
17316
  // src/compiler/compile-linkable-table.ts
17201
17317
  function isLocal(rule) {
@@ -17307,6 +17423,29 @@ function gate(predicate) {
17307
17423
  };
17308
17424
  }
17309
17425
 
17426
+ // src/combinators/withCtx.ts
17427
+ function withCtx(extra, combinator) {
17428
+ const meta = {
17429
+ firstSet: combinator._meta.firstSet,
17430
+ canMatchNewline: combinator._meta.canMatchNewline,
17431
+ isTrivia: false
17432
+ };
17433
+ return {
17434
+ _tag: "withCtx",
17435
+ _meta: meta,
17436
+ _def: { tag: "withCtx", extra, parser: combinator },
17437
+ parse(input, pos, ctx) {
17438
+ const saved = ctx.state;
17439
+ ctx.state = extra;
17440
+ try {
17441
+ return combinator.parse(input, pos, ctx);
17442
+ } finally {
17443
+ ctx.state = saved;
17444
+ }
17445
+ }
17446
+ };
17447
+ }
17448
+
17310
17449
  // src/plugin/direct-builder-static.ts
17311
17450
  import { parseSync } from "oxc-parser";
17312
17451
  var STATIC_BUILDER_GLOBALS = /* @__PURE__ */ new Set([
@@ -17592,6 +17731,41 @@ var _builderImports = null;
17592
17731
  function setBuilderImportResolver(r) {
17593
17732
  _builderImports = r;
17594
17733
  }
17734
+ var ModuleScope = class _ModuleScope extends Map {
17735
+ tried = /* @__PURE__ */ new Map();
17736
+ resolveFree;
17737
+ unresolved;
17738
+ constructor(resolveFree, unresolved) {
17739
+ super();
17740
+ this.resolveFree = resolveFree;
17741
+ this.unresolved = unresolved;
17742
+ }
17743
+ has(name) {
17744
+ return super.has(name) || this.resolve(name);
17745
+ }
17746
+ get(name) {
17747
+ if (super.has(name) || this.resolve(name)) return super.get(name);
17748
+ const why = this.tried.get(name);
17749
+ if (why !== void 0) this.unresolved.add(`\`${name}\` ${why}`);
17750
+ return void 0;
17751
+ }
17752
+ /** A child scope (a factory body) that keeps resolving this module's free names. */
17753
+ fork() {
17754
+ const child = new _ModuleScope(this.resolveFree, this.unresolved);
17755
+ for (const [k, v] of super.entries()) child.set(k, v);
17756
+ return child;
17757
+ }
17758
+ resolve(name) {
17759
+ if (this.tried.has(name)) return false;
17760
+ const r = this.resolveFree(name);
17761
+ if (r !== null && "value" in r) {
17762
+ super.set(name, r.value);
17763
+ return true;
17764
+ }
17765
+ if (r !== null) this.tried.set(name, r.unresolved);
17766
+ return false;
17767
+ }
17768
+ };
17595
17769
  function isWordFactory(v) {
17596
17770
  return !!v && typeof v === "object" && v.tag === "wordFactory";
17597
17771
  }
@@ -17651,7 +17825,7 @@ var SUPPORTED = {
17651
17825
  a[0]
17652
17826
  ),
17653
17827
  label: (...a) => label(a[0], a[1]),
17654
- field: (...a) => field(a[0], a[1]),
17828
+ field: (...a) => field(a[0], a[1], a[2]),
17655
17829
  noTrivia: (...a) => noTrivia(a[0]),
17656
17830
  token: (...a) => token(a[0]),
17657
17831
  // `routed(fallback)` — the fallback must be forwarded. A zero-arg entry here read
@@ -17715,7 +17889,7 @@ function dispatchArmValue(node2, scope, code, mfs) {
17715
17889
  if (!keyArg || !parserArg || extraArg !== void 0 || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
17716
17890
  const key = anyValue(keyArg, scope, code, mfs);
17717
17891
  const parserValue = anyValue(parserArg, scope, code, mfs);
17718
- if (!isCombinator(parserValue)) return null;
17892
+ if (!isCombinator2(parserValue)) return null;
17719
17893
  const opts = { caseInsensitive: factory.caseInsensitive };
17720
17894
  if (typeof key === "string") return when(key, parserValue, opts);
17721
17895
  if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue, opts);
@@ -17726,7 +17900,7 @@ function dispatchArmValue(node2, scope, code, mfs) {
17726
17900
  const [parserArg] = node2.arguments;
17727
17901
  if (!parserArg || parserArg.type === "SpreadElement") return null;
17728
17902
  const parserValue = anyValue(parserArg, scope, code, mfs);
17729
- if (!isCombinator(parserValue)) return null;
17903
+ if (!isCombinator2(parserValue)) return null;
17730
17904
  return otherwise(parserValue);
17731
17905
  }
17732
17906
  if (callee.name === "when") {
@@ -17736,24 +17910,24 @@ function dispatchArmValue(node2, scope, code, mfs) {
17736
17910
  const parserValue = anyValue(parserArg, scope, code, mfs);
17737
17911
  const opts = dispatchWhenOptions(optsArg === void 0 ? void 0 : anyValue(optsArg, scope, code, mfs));
17738
17912
  if (opts === null) return null;
17739
- if (!isCombinator(parserValue)) return null;
17913
+ if (!isCombinator2(parserValue)) return null;
17740
17914
  if (typeof key === "string") return when(key, parserValue, opts);
17741
17915
  if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue, opts);
17742
17916
  if (isDispatchMatcher(key)) return when(key, parserValue, opts);
17743
17917
  }
17744
17918
  return null;
17745
17919
  }
17746
- function isCombinator(v) {
17920
+ function isCombinator2(v) {
17747
17921
  return !!v && typeof v === "object" && "_def" in v;
17748
17922
  }
17749
17923
  function isDispatchArm(v) {
17750
17924
  if (!v || typeof v !== "object") return false;
17751
17925
  const rec = v;
17752
- if (rec.kind === "otherwise") return isCombinator(rec.parser);
17926
+ if (rec.kind === "otherwise") return isCombinator2(rec.parser);
17753
17927
  if (rec.kind === "whenMatcher") {
17754
- return isDispatchMatcher(rec.matcher) && typeof rec.caseInsensitive === "boolean" && isCombinator(rec.parser);
17928
+ return isDispatchMatcher(rec.matcher) && typeof rec.caseInsensitive === "boolean" && isCombinator2(rec.parser);
17755
17929
  }
17756
- return rec.kind === "when" && Array.isArray(rec.keys) && rec.keys.every((key) => typeof key === "string") && typeof rec.caseInsensitive === "boolean" && isCombinator(rec.parser);
17930
+ return rec.kind === "when" && Array.isArray(rec.keys) && rec.keys.every((key) => typeof key === "string") && typeof rec.caseInsensitive === "boolean" && isCombinator2(rec.parser);
17757
17931
  }
17758
17932
  function isDispatchMatcher(v) {
17759
17933
  if (!v || typeof v !== "object") return false;
@@ -17768,7 +17942,7 @@ function scopeGet(scope, name, mfs) {
17768
17942
  if (mfs && entry.mfSrcs.length > 0) mfs.push(...entry.mfSrcs);
17769
17943
  return entry.combi;
17770
17944
  }
17771
- if (isCombinator(entry)) return entry;
17945
+ if (isCombinator2(entry)) return entry;
17772
17946
  return null;
17773
17947
  }
17774
17948
  function unwrapStaticExpr(expr) {
@@ -17820,7 +17994,7 @@ function scopedStaticValue(expr, scope) {
17820
17994
  return { found: true, value };
17821
17995
  }
17822
17996
  function staticNodeOptionsFromValue(value) {
17823
- if (value === null || typeof value !== "object" || Array.isArray(value) || isCombinator(value)) return STATIC_NODE_OPTIONS_NOT_OPTIONS;
17997
+ if (value === null || typeof value !== "object" || Array.isArray(value) || isCombinator2(value)) return STATIC_NODE_OPTIONS_NOT_OPTIONS;
17824
17998
  const opts = {};
17825
17999
  const rec = value;
17826
18000
  for (const name of Object.keys(rec)) {
@@ -17872,6 +18046,13 @@ function staticNodeOptions(expr, scope) {
17872
18046
  }
17873
18047
  return opts.unwrap || opts.collapse || opts.project !== void 0 || opts.captureTrivia || opts.trailingTrivia || opts.tags !== void 0 || opts.buildArity !== void 0 ? opts : void 0;
17874
18048
  }
18049
+ var UNKNOWN_OPTIONS = /* @__PURE__ */ Symbol("parseman.unknownOptions");
18050
+ function scanOptions(optsArg, scope, code) {
18051
+ if (optsArg === void 0) return void 0;
18052
+ if (optsArg.type === "SpreadElement") return UNKNOWN_OPTIONS;
18053
+ const opts = anyValue(optsArg, scope, code, []);
18054
+ return isHole(optsArg, opts, scope) ? UNKNOWN_OPTIONS : opts;
18055
+ }
17875
18056
  function exprToCombi(node2, scope, code, mfs) {
17876
18057
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
17877
18058
  if (node2.type !== "CallExpression") return null;
@@ -17907,7 +18088,7 @@ function exprToCombi(node2, scope, code, mfs) {
17907
18088
  const [parserArg, fnArg] = node2.arguments;
17908
18089
  if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
17909
18090
  const inner = anyValue(parserArg, scope, code, mfs);
17910
- if (!isCombinator(inner)) return null;
18091
+ if (!isCombinator2(inner)) return null;
17911
18092
  const fnSrc = stripTsFromSource(fnArg, code);
17912
18093
  mfs.push(fnSrc);
17913
18094
  try {
@@ -17922,7 +18103,7 @@ function exprToCombi(node2, scope, code, mfs) {
17922
18103
  const [parserArg, fnArg] = node2.arguments;
17923
18104
  if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
17924
18105
  const inner = anyValue(parserArg, scope, code, mfs);
17925
- if (!isCombinator(inner)) return null;
18106
+ if (!isCombinator2(inner)) return null;
17926
18107
  const fnSrc = stripTsFromSource(fnArg, code);
17927
18108
  mfs.push(fnSrc);
17928
18109
  try {
@@ -17943,7 +18124,7 @@ function exprToCombi(node2, scope, code, mfs) {
17943
18124
  const optsArg = explicitType !== void 0 ? fourthArg : thirdArg;
17944
18125
  if (!parserArg || parserArg.type === "SpreadElement") return null;
17945
18126
  const inner = anyValue(parserArg, scope, code, mfs);
17946
- if (!isCombinator(inner)) return null;
18127
+ if (!isCombinator2(inner)) return null;
17947
18128
  const be = buildArg;
17948
18129
  const buildExpr = be === void 0 || be.type === "SpreadElement" ? void 0 : unwrapStaticExpr(be);
17949
18130
  const buildArgOptions = be !== void 0 && be.type !== "SpreadElement" ? staticNodeOptions(buildArg, scope) : STATIC_NODE_OPTIONS_NOT_OPTIONS;
@@ -18017,7 +18198,7 @@ function exprToCombi(node2, scope, code, mfs) {
18017
18198
  const opts = anyValue(optsArg, scope, code, []);
18018
18199
  if (!opts || typeof opts !== "object") return null;
18019
18200
  const root = anyValue(rootArg, scope, code, mfs);
18020
- if (!isCombinator(root)) return null;
18201
+ if (!isCombinator2(root)) return null;
18021
18202
  try {
18022
18203
  return parser(opts, root);
18023
18204
  } catch {
@@ -18029,10 +18210,10 @@ function exprToCombi(node2, scope, code, mfs) {
18029
18210
  if (!itemArg || !sepArg || itemArg.type === "SpreadElement" || sepArg.type === "SpreadElement") return null;
18030
18211
  const itemMfs = [];
18031
18212
  const itemCombi = anyValue(itemArg, scope, code, itemMfs);
18032
- if (!isCombinator(itemCombi)) return null;
18213
+ if (!isCombinator2(itemCombi)) return null;
18033
18214
  const sepMfs = [];
18034
18215
  const sepCombi = anyValue(sepArg, scope, code, sepMfs);
18035
- if (!isCombinator(sepCombi)) return null;
18216
+ if (!isCombinator2(sepCombi)) return null;
18036
18217
  let opts;
18037
18218
  if (optsArg) {
18038
18219
  if (optsArg.type === "SpreadElement") return null;
@@ -18052,7 +18233,7 @@ function exprToCombi(node2, scope, code, mfs) {
18052
18233
  if (!itemArg || itemArg.type === "SpreadElement") return null;
18053
18234
  const itemMfs = [];
18054
18235
  const itemCombi = anyValue(itemArg, scope, code, itemMfs);
18055
- if (!isCombinator(itemCombi)) return null;
18236
+ if (!isCombinator2(itemCombi)) return null;
18056
18237
  let opts;
18057
18238
  if (optsArg) {
18058
18239
  if (optsArg.type === "SpreadElement") return null;
@@ -18074,7 +18255,7 @@ function exprToCombi(node2, scope, code, mfs) {
18074
18255
  const [innerArg] = node2.arguments;
18075
18256
  if (!innerArg || innerArg.type === "SpreadElement") return null;
18076
18257
  const inner = anyValue(innerArg, scope, code, mfs);
18077
- if (!isCombinator(inner)) return null;
18258
+ if (!isCombinator2(inner)) return null;
18078
18259
  try {
18079
18260
  return not(inner);
18080
18261
  } catch {
@@ -18085,7 +18266,7 @@ function exprToCombi(node2, scope, code, mfs) {
18085
18266
  const [innerArg] = node2.arguments;
18086
18267
  if (!innerArg || innerArg.type === "SpreadElement") return null;
18087
18268
  const inner = anyValue(innerArg, scope, code, mfs);
18088
- if (!isCombinator(inner)) return null;
18269
+ if (!isCombinator2(inner)) return null;
18089
18270
  try {
18090
18271
  return peek(inner);
18091
18272
  } catch {
@@ -18098,7 +18279,8 @@ function exprToCombi(node2, scope, code, mfs) {
18098
18279
  const open = anyValue(openArg, scope, code, []);
18099
18280
  const close = anyValue(closeArg, scope, code, []);
18100
18281
  if (typeof open !== "string" || typeof close !== "string") return null;
18101
- const opts = optsArg && optsArg.type !== "SpreadElement" ? anyValue(optsArg, scope, code, []) : void 0;
18282
+ const opts = scanOptions(optsArg, scope, code);
18283
+ if (opts === UNKNOWN_OPTIONS) return null;
18102
18284
  try {
18103
18285
  return balanced(open, close, opts);
18104
18286
  } catch {
@@ -18109,8 +18291,9 @@ function exprToCombi(node2, scope, code, mfs) {
18109
18291
  const [sentinelArg, optsArg] = node2.arguments;
18110
18292
  if (!sentinelArg || sentinelArg.type === "SpreadElement") return null;
18111
18293
  const sentinel = anyValue(sentinelArg, scope, code, []);
18112
- if (!isCombinator(sentinel)) return null;
18113
- const opts = optsArg && optsArg.type !== "SpreadElement" ? anyValue(optsArg, scope, code, []) : void 0;
18294
+ if (!isCombinator2(sentinel)) return null;
18295
+ const opts = scanOptions(optsArg, scope, code);
18296
+ if (opts === UNKNOWN_OPTIONS) return null;
18114
18297
  try {
18115
18298
  return scanTo(sentinel, opts);
18116
18299
  } catch {
@@ -18139,7 +18322,7 @@ function exprToCombi(node2, scope, code, mfs) {
18139
18322
  const extraSrc = stripTsFromSource(extraArg, code);
18140
18323
  mfs.push(extraSrc);
18141
18324
  const inner = anyValue(innerArg, scope, code, mfs);
18142
- if (!isCombinator(inner)) return null;
18325
+ if (!isCombinator2(inner)) return null;
18143
18326
  try {
18144
18327
  const evaluated = anyValue(extraArg, scope, code, []);
18145
18328
  const usable = typeof evaluated === "object" && evaluated !== null && !Array.isArray(evaluated) && Object.getPrototypeOf(evaluated) === Object.prototype;
@@ -18155,7 +18338,7 @@ function exprToCombi(node2, scope, code, mfs) {
18155
18338
  const [selectorArg, ...armArgs] = node2.arguments;
18156
18339
  if (!selectorArg || selectorArg.type === "SpreadElement") return null;
18157
18340
  const selector = anyValue(selectorArg, scope, code, mfs);
18158
- if (!isCombinator(selector)) return null;
18341
+ if (!isCombinator2(selector)) return null;
18159
18342
  const arms = [];
18160
18343
  for (const arg of armArgs) {
18161
18344
  if (arg.type === "SpreadElement") return null;
@@ -18182,12 +18365,12 @@ function exprToCombi(node2, scope, code, mfs) {
18182
18365
  const gateSrc = stripTsFromSource(parts.gate, code);
18183
18366
  mfs.push(gateSrc);
18184
18367
  const combi = anyValue(parts.combinator, scope, code, mfs);
18185
- if (!isCombinator(combi)) return null;
18368
+ if (!isCombinator2(combi)) return null;
18186
18369
  arms.push({ gate: () => true, combinator: combi });
18187
18370
  gateSrcs.push(gateSrc);
18188
18371
  } else {
18189
18372
  const combi = anyValue(argNode, scope, code, mfs);
18190
- if (!isCombinator(combi)) return null;
18373
+ if (!isCombinator2(combi)) return null;
18191
18374
  arms.push(combi);
18192
18375
  gateSrcs.push(null);
18193
18376
  }
@@ -18217,6 +18400,12 @@ function exprToCombi(node2, scope, code, mfs) {
18217
18400
  return null;
18218
18401
  }
18219
18402
  }
18403
+ function isHole(node2, value, scope) {
18404
+ if (value !== null) return false;
18405
+ const n = unwrapStaticExpr(node2);
18406
+ if (n.type === "Identifier") return !scope.has(n.name);
18407
+ return !(n.type === "Literal" && n.value === null) && n.type !== "ArrowFunctionExpression" && n.type !== "FunctionExpression";
18408
+ }
18220
18409
  function anyValue(node2, scope, code, mfs) {
18221
18410
  if (node2.type === "TSAsExpression" || node2.type === "TSSatisfiesExpression" || node2.type === "TSNonNullExpression" || node2.type === "TSTypeAssertion" || node2.type === "TSInstantiationExpression" || node2.type === "ParenthesizedExpression") {
18222
18411
  const inner = node2.expression;
@@ -18237,7 +18426,9 @@ function anyValue(node2, scope, code, mfs) {
18237
18426
  continue;
18238
18427
  }
18239
18428
  if (el.type === "SpreadElement") return null;
18240
- out.push(anyValue(el, scope, code, mfs));
18429
+ const v = anyValue(el, scope, code, mfs);
18430
+ if (isHole(el, v, scope)) return null;
18431
+ out.push(v);
18241
18432
  }
18242
18433
  return out;
18243
18434
  }
@@ -18246,7 +18437,10 @@ function anyValue(node2, scope, code, mfs) {
18246
18437
  for (const prop of node2.properties) {
18247
18438
  const key = propName(prop);
18248
18439
  if (key === null) return null;
18249
- obj[key] = anyValue(prop.value, scope, code, mfs);
18440
+ const valueNode = prop.value;
18441
+ const v = anyValue(valueNode, scope, code, mfs);
18442
+ if (isHole(valueNode, v, scope)) return null;
18443
+ obj[key] = v;
18250
18444
  }
18251
18445
  return obj;
18252
18446
  }
@@ -18322,10 +18516,9 @@ function evaluateExpr(node2, scope, code, mapFnSources) {
18322
18516
  return exprToCombi(node2, scope, code, mapFnSources);
18323
18517
  }
18324
18518
  function evaluateCombinatorArray(node2, scope, code) {
18325
- if (node2.type !== "ArrayExpression") return null;
18326
18519
  const val = anyValue(node2, scope, code, []);
18327
18520
  if (!Array.isArray(val) || val.length === 0) return null;
18328
- if (!val.every(isCombinator)) return null;
18521
+ if (!val.every(isCombinator2)) return null;
18329
18522
  return val;
18330
18523
  }
18331
18524
  function collectRuleKeys(retObj) {
@@ -18403,7 +18596,7 @@ function evalBodyStatements(statements, scope, code, out) {
18403
18596
  return false;
18404
18597
  }
18405
18598
  const thisDeclMfSrcs = bodyMfs.slice(before);
18406
- if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
18599
+ if (isCombinator2(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
18407
18600
  else if (isDispatchArm(val)) scope.set(name, { value: val, mfSrcs: thisDeclMfSrcs });
18408
18601
  else scope.set(name, val);
18409
18602
  }
@@ -18448,7 +18641,7 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, out, opti
18448
18641
  let built;
18449
18642
  try {
18450
18643
  built = rules(options ?? {}, (g) => {
18451
- const localScope = new Map(scope);
18644
+ const localScope = scope instanceof ModuleScope ? scope.fork() : new Map(scope);
18452
18645
  localScope.set(proxyName, g);
18453
18646
  if (!evalBodyStatements(statements, localScope, code, out)) throw ABORT;
18454
18647
  const entries = flattenRuleEntries(retObj, localScope, code);
@@ -18458,7 +18651,7 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, out, opti
18458
18651
  const definitions = {};
18459
18652
  for (const [key, e] of finalByKey) {
18460
18653
  const val = anyValue(e.value, e.scope, e.code, mapFnSources);
18461
- if (!isCombinator(val)) throw ABORT;
18654
+ if (!isCombinator2(val)) throw ABORT;
18462
18655
  definitions[key] = val;
18463
18656
  }
18464
18657
  return definitions;
@@ -18473,7 +18666,7 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, out, opti
18473
18666
  return map;
18474
18667
  }
18475
18668
  function isDefinableRef2(v) {
18476
- return isCombinator(v) && typeof v.define === "function" && v._def.tag === "lazy";
18669
+ return isCombinator2(v) && typeof v.define === "function" && v._def.tag === "lazy";
18477
18670
  }
18478
18671
  function evaluateRefDeclaration(init, name, scope) {
18479
18672
  if (init.type !== "CallExpression") return null;
@@ -18493,12 +18686,12 @@ function applyDefineStatement(callExpr, scope, code) {
18493
18686
  if (mem.property.type !== "Identifier" || mem.property.name !== "define") return false;
18494
18687
  if (mem.object.type !== "Identifier" || !mem.object.name) return false;
18495
18688
  const target = scope.get(mem.object.name);
18496
- const refCombi = isScopeEntry(target) ? target.combi : isCombinator(target) ? target : null;
18689
+ const refCombi = isScopeEntry(target) ? target.combi : isCombinator2(target) ? target : null;
18497
18690
  if (!refCombi || !isDefinableRef2(refCombi)) return false;
18498
18691
  const args = callExpr.arguments;
18499
18692
  if (args.length !== 1 || args[0].type === "SpreadElement") return false;
18500
18693
  const inner = anyValue(args[0], scope, code, []);
18501
- if (!isCombinator(inner)) return false;
18694
+ if (!isCombinator2(inner)) return false;
18502
18695
  try {
18503
18696
  refCombi.define(inner);
18504
18697
  } catch {
@@ -18508,7 +18701,7 @@ function applyDefineStatement(callExpr, scope, code) {
18508
18701
  }
18509
18702
  function referencesAny(node2, names, scope) {
18510
18703
  if (node2.type === "Identifier") {
18511
- return names.has(node2.name) || scope.has(node2.name);
18704
+ return names.has(node2.name) || Map.prototype.has.call(scope, node2.name);
18512
18705
  }
18513
18706
  for (const key of Object.keys(node2)) {
18514
18707
  const child = node2[key];
@@ -18569,7 +18762,9 @@ function patternNames(pat, out) {
18569
18762
  }
18570
18763
  }
18571
18764
  function declare(scope, name, b) {
18572
- if (!scope.bindings.has(name)) scope.bindings.set(name, b);
18765
+ const prev = scope.bindings.get(name);
18766
+ if (prev) prev.reassigned = true;
18767
+ else scope.bindings.set(name, b);
18573
18768
  }
18574
18769
  function functionScope(scope) {
18575
18770
  let s = scope;
@@ -18669,7 +18864,8 @@ function markReassignments(body, root) {
18669
18864
  }
18670
18865
  const node2 = n;
18671
18866
  if (typeof node2.type !== "string") return;
18672
- if (node2.type === "AssignmentExpression" || node2.type === "UpdateExpression") {
18867
+ const loopWrite = (node2.type === "ForOfStatement" || node2.type === "ForInStatement") && node2.left?.type !== "VariableDeclaration";
18868
+ if (node2.type === "AssignmentExpression" || node2.type === "UpdateExpression" || loopWrite) {
18673
18869
  const target = node2.type === "UpdateExpression" ? node2.argument : node2.left;
18674
18870
  const names = [];
18675
18871
  if (target) patternNames(target, names);
@@ -18685,6 +18881,11 @@ function markReassignments(body, root) {
18685
18881
  };
18686
18882
  for (const stmt of body) walk(stmt);
18687
18883
  }
18884
+ function topLevelIsStable(body, moduleEnd) {
18885
+ const root = buildScopeTree(body, moduleEnd);
18886
+ markReassignments(body, root);
18887
+ return (name) => root.bindings.get(name)?.reassigned !== true;
18888
+ }
18688
18889
  function functionArity(fn) {
18689
18890
  const params = fn.params ?? [];
18690
18891
  let count = 0;
@@ -19137,7 +19338,7 @@ function ast(value) {
19137
19338
  function nodes(value) {
19138
19339
  return Array.isArray(value) ? value.filter((v) => ast(v) !== void 0) : [];
19139
19340
  }
19140
- function nameOf(node2) {
19341
+ function nameOf2(node2) {
19141
19342
  return node2?.type === "Identifier" && typeof node2.name === "string" ? node2.name : void 0;
19142
19343
  }
19143
19344
  function range(node2) {
@@ -19170,7 +19371,7 @@ function isDirectCallUse(call, parent, statement) {
19170
19371
  return declarations.length === 1 && declarations[0] === parent && parent.init === call;
19171
19372
  }
19172
19373
  if (parent.type === "AssignmentExpression") {
19173
- return statement.type === "ExpressionStatement" && statement.expression === parent && nameOf(ast(parent.left)) !== void 0 && parent.right === call && parent.operator === "=";
19374
+ return statement.type === "ExpressionStatement" && statement.expression === parent && nameOf2(ast(parent.left)) !== void 0 && parent.right === call && parent.operator === "=";
19174
19375
  }
19175
19376
  if (parent.type === "ReturnStatement") return statement === parent && parent.argument === call;
19176
19377
  return false;
@@ -19188,7 +19389,7 @@ function hasInlineScopeHazard(fn) {
19188
19389
  if (node2.type === "Identifier" && node2.name === "arguments") hazard = true;
19189
19390
  else if (node2.type === "ThisExpression") hazard = true;
19190
19391
  else if (node2.type === "VariableDeclaration" && node2.kind === "var") hazard = true;
19191
- else if (node2.type === "MetaProperty" && nameOf(ast(node2.meta)) === "new" && nameOf(ast(node2.property)) === "target") hazard = true;
19392
+ else if (node2.type === "MetaProperty" && nameOf2(ast(node2.meta)) === "new" && nameOf2(ast(node2.property)) === "target") hazard = true;
19192
19393
  else for (const child of children2(node2)) walk(child);
19193
19394
  };
19194
19395
  walk(fn.body);
@@ -19196,7 +19397,7 @@ function hasInlineScopeHazard(fn) {
19196
19397
  }
19197
19398
  function hasInlineSignature(fn) {
19198
19399
  const params = nodes(fn.node.params);
19199
- return params.length === 3 && nameOf(params[0]) === "input" && nameOf(params[1]) === "pos" && nameOf(params[2]) === "ctx";
19400
+ return params.length === 3 && nameOf2(params[0]) === "input" && nameOf2(params[1]) === "pos" && nameOf2(params[2]) === "ctx";
19200
19401
  }
19201
19402
  function render2(source, start, end, edits) {
19202
19403
  const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
@@ -19289,7 +19490,7 @@ function supercompileRuleMapReplacement(prog, replacement) {
19289
19490
  const call = ast(declarator?.init);
19290
19491
  const table = nodes(call?.arguments)[0];
19291
19492
  if (declaration?.type !== "VariableDeclaration" || declarator?.type !== "VariableDeclarator" || call?.type !== "CallExpression" || table?.type !== "ObjectExpression") return replacement;
19292
- const property = (object, key) => nodes(object.properties).find((prop) => prop.type === "Property" && nameOf(ast(prop.key)) === key);
19493
+ const property = (object, key) => nodes(object.properties).find((prop) => prop.type === "Property" && nameOf2(ast(prop.key)) === key);
19293
19494
  const assembliesProp = property(table, "a");
19294
19495
  const assemblies = nodes(ast(assembliesProp?.value)?.elements);
19295
19496
  if (assembliesProp === void 0 || assemblies.length !== 1 || assemblies[0]?.type !== "ObjectExpression") return replacement;
@@ -19322,7 +19523,7 @@ function supercompileEmittedAssembly(prog, _cfg, extraIps, emitted) {
19322
19523
  const fns = /* @__PURE__ */ new Map();
19323
19524
  for (const statement of nodes(wrapperBody.body)) {
19324
19525
  if (statement.type !== "FunctionDeclaration") continue;
19325
- const name = nameOf(ast(statement.id));
19526
+ const name = nameOf2(ast(statement.id));
19326
19527
  const hit = name === void 0 ? void 0 : PIECE.exec(name);
19327
19528
  const body = ast(statement.body);
19328
19529
  if (hit !== void 0 && hit !== null && body?.type === "BlockStatement") {
@@ -19335,7 +19536,7 @@ function supercompileEmittedAssembly(prog, _cfg, extraIps, emitted) {
19335
19536
  const allCalls = [];
19336
19537
  const walk = (node2, current, parent, statement, statementParent) => {
19337
19538
  if (isFunction(node2) && node2 !== current?.node) {
19338
- const own = node2.type === "FunctionDeclaration" ? nameOf(ast(node2.id)) : void 0;
19539
+ const own = node2.type === "FunctionDeclaration" ? nameOf2(ast(node2.id)) : void 0;
19339
19540
  const hit = own === void 0 ? void 0 : PIECE.exec(own);
19340
19541
  if (hit !== void 0 && hit !== null) {
19341
19542
  const fn = fns.get(Number(hit[1]));
@@ -19359,12 +19560,12 @@ function supercompileEmittedAssembly(prog, _cfg, extraIps, emitted) {
19359
19560
  ownerParent = parent;
19360
19561
  }
19361
19562
  if (node2.type === "CallExpression") {
19362
- const calleeName = nameOf(ast(node2.callee));
19563
+ const calleeName = nameOf2(ast(node2.callee));
19363
19564
  const hit = calleeName === void 0 ? void 0 : PIECE.exec(calleeName);
19364
19565
  if (hit !== void 0 && hit !== null) {
19365
19566
  const callee = Number(hit[1]);
19366
19567
  const args = nodes(node2.arguments);
19367
- const transformable = current !== void 0 && owner2 !== void 0 && args.length === 3 && nameOf(args[0]) === "input" && nameOf(args[2]) === "ctx" && isDirectCallUse(node2, parent, owner2) && isListStatement(owner2, ownerParent);
19568
+ const transformable = current !== void 0 && owner2 !== void 0 && args.length === 3 && nameOf2(args[0]) === "input" && nameOf2(args[2]) === "ctx" && isDirectCallUse(node2, parent, owner2) && isListStatement(owner2, ownerParent);
19368
19569
  if (current === void 0 || owner2 === void 0) externalRefs.add(callee);
19369
19570
  else {
19370
19571
  const call = { caller: current.ip, callee, node: node2, statement: owner2, args, transformable };
@@ -19539,7 +19740,7 @@ ${nested}
19539
19740
  const referenced = /* @__PURE__ */ new Set();
19540
19741
  const checkRefs = (node2, parent) => {
19541
19742
  if (node2.type === "FunctionDeclaration") {
19542
- const name = nameOf(ast(node2.id));
19743
+ const name = nameOf2(ast(node2.id));
19543
19744
  if (name !== void 0 && PIECE.test(name)) declared.add(name);
19544
19745
  }
19545
19746
  if (node2.type === "Identifier" && typeof node2.name === "string" && PIECE.test(node2.name)) {
@@ -19548,32 +19749,210 @@ ${nested}
19548
19749
  }
19549
19750
  for (const child of children2(node2)) checkRefs(child, node2);
19550
19751
  };
19551
- checkRefs(checked.program);
19552
- if (declared.size !== functionsAfter || [...referenced].some((name) => !declared.has(name))) {
19553
- return fallback(emitted, "deleted piece body remains reachable after structured relinking", before);
19554
- }
19555
- const largestRegion = [...regionSizes].sort((a, b) => b[1] - a[1])[0];
19556
- const protocolRegions = {
19557
- node: [...regionProtocol.values()].filter((bits) => (bits & 1) !== 0).length,
19558
- repv: [...regionProtocol.values()].filter((bits) => (bits & 2) !== 0).length,
19559
- nodeAndRepv: [...regionProtocol.values()].filter((bits) => bits === 3).length
19752
+ checkRefs(checked.program);
19753
+ if (declared.size !== functionsAfter || [...referenced].some((name) => !declared.has(name))) {
19754
+ return fallback(emitted, "deleted piece body remains reachable after structured relinking", before);
19755
+ }
19756
+ const largestRegion = [...regionSizes].sort((a, b) => b[1] - a[1])[0];
19757
+ const protocolRegions = {
19758
+ node: [...regionProtocol.values()].filter((bits) => (bits & 1) !== 0).length,
19759
+ repv: [...regionProtocol.values()].filter((bits) => (bits & 2) !== 0).length,
19760
+ nodeAndRepv: [...regionProtocol.values()].filter((bits) => bits === 3).length
19761
+ };
19762
+ return {
19763
+ ...emitted,
19764
+ source: candidate,
19765
+ supercompile: {
19766
+ ...before,
19767
+ activated: removed > 0,
19768
+ ...removed > 0 ? {} : { reason: "no uniquely-owned region bodies" },
19769
+ functionsAfter,
19770
+ pieceCallsAfter: pieceCallsBefore - removed,
19771
+ sourceAfter: candidate.length,
19772
+ regions: roots.length,
19773
+ largestRegionSource: Math.max(0, ...regionSizes.values()),
19774
+ ...largestRegion === void 0 ? {} : { largestRegionIp: largestRegion[0] },
19775
+ protocolRegions
19776
+ }
19777
+ };
19778
+ }
19779
+
19780
+ // src/plugin/ir-eval.ts
19781
+ function evalRulesIR(ir) {
19782
+ return evalIR(ir, rules);
19783
+ }
19784
+ function evalCombinatorIR(ir) {
19785
+ const units = evalIR(ir, (opts) => opts.scanSkip);
19786
+ return units?.length === 1 ? units[0] : null;
19787
+ }
19788
+ function evalIR(ir, rulesImpl) {
19789
+ const _tf = (child, src, recognitionOnly = false) => {
19790
+ let fn2;
19791
+ try {
19792
+ fn2 = (0, eval)(`(${src})`);
19793
+ } catch {
19794
+ fn2 = () => {
19795
+ throw new Error("IR transform fn not materialized");
19796
+ };
19797
+ }
19798
+ const t = transform(child, fn2);
19799
+ t._def.fnSrc = src;
19800
+ if (recognitionOnly) t._def.recognitionOnly = true;
19801
+ return t;
19802
+ };
19803
+ const _lf = (child, src, source = false) => {
19804
+ let fn2;
19805
+ try {
19806
+ fn2 = (0, eval)(`(${src})`);
19807
+ } catch {
19808
+ fn2 = () => {
19809
+ throw new Error("IR leaf fn not materialized");
19810
+ };
19811
+ }
19812
+ const l = source ? sourceLeaf(child, fn2) : leaf(child, fn2);
19813
+ l._def.fnSrc = src;
19814
+ return l;
19815
+ };
19816
+ const _nd = (type, child, src, opts, staticError, sigSrc, buildImports, rawUnused) => {
19817
+ if (staticError !== void 0 && staticError.length > 0) {
19818
+ throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
19819
+ }
19820
+ const n = node(type, child, (() => {
19821
+ throw new Error("IR node build requires static re-lowering");
19822
+ }), opts);
19823
+ n._def.buildSrc = src;
19824
+ if (staticError !== void 0) n._def.buildStaticError = staticError;
19825
+ if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
19826
+ if (rawUnused === true) n._def.buildRawUnused = true;
19827
+ if (buildImports !== void 0) n._def.buildImports = buildImports;
19828
+ return n;
19829
+ };
19830
+ const _gch = (items) => {
19831
+ const gateSrcs = [];
19832
+ const arms = items.map((it) => {
19833
+ if (Array.isArray(it)) {
19834
+ const [src, comb] = it;
19835
+ gateSrcs.push(src);
19836
+ let gate2;
19837
+ try {
19838
+ gate2 = (0, eval)(`(${src})`);
19839
+ } catch {
19840
+ gate2 = () => {
19841
+ throw new Error("IR gate fn not materialized");
19842
+ };
19843
+ }
19844
+ return { gate: gate2, combinator: comb };
19845
+ }
19846
+ gateSrcs.push(null);
19847
+ return it;
19848
+ });
19849
+ const c = choice(...arms);
19850
+ const gates = c._def.gates;
19851
+ if (gates && gates.length !== gateSrcs.length) {
19852
+ throw new Error(`_gch: gateSrcs length ${gateSrcs.length} != choice gates length ${gates.length}`);
19853
+ }
19854
+ ;
19855
+ c._def.gateSrcs = gateSrcs;
19856
+ return c;
19560
19857
  };
19561
- return {
19562
- ...emitted,
19563
- source: candidate,
19564
- supercompile: {
19565
- ...before,
19566
- activated: removed > 0,
19567
- ...removed > 0 ? {} : { reason: "no uniquely-owned region bodies" },
19568
- functionsAfter,
19569
- pieceCallsAfter: pieceCallsBefore - removed,
19570
- sourceAfter: candidate.length,
19571
- regions: roots.length,
19572
- largestRegionSource: Math.max(0, ...regionSizes.values()),
19573
- ...largestRegion === void 0 ? {} : { largestRegionIp: largestRegion[0] },
19574
- protocolRegions
19858
+ const _wc = (src, inner) => {
19859
+ let extra;
19860
+ try {
19861
+ extra = (0, eval)(`(${src})`);
19862
+ } catch {
19863
+ extra = void 0;
19575
19864
  }
19865
+ const w = withCtx(extra, inner);
19866
+ w._def.extraSrc = src;
19867
+ return w;
19576
19868
  };
19869
+ const fn = new Function(
19870
+ "rules",
19871
+ "ref",
19872
+ "regex",
19873
+ "literal",
19874
+ "keywords",
19875
+ "sequence",
19876
+ "choice",
19877
+ "dispatch",
19878
+ "when",
19879
+ "startsWith",
19880
+ "endsWith",
19881
+ "matches",
19882
+ "otherwise",
19883
+ "routed",
19884
+ "attempt",
19885
+ "many",
19886
+ "oneOrMore",
19887
+ "optional",
19888
+ "sepBy",
19889
+ "keepSeparator",
19890
+ "not",
19891
+ "peek",
19892
+ "node",
19893
+ "parser",
19894
+ "scanTo",
19895
+ "balanced",
19896
+ "token",
19897
+ "leaf",
19898
+ "transform",
19899
+ "trivia",
19900
+ "classifiedTrivia",
19901
+ "label",
19902
+ "field",
19903
+ "expect",
19904
+ "adjacent",
19905
+ "notAdjacent",
19906
+ "_tf",
19907
+ "_lf",
19908
+ "_nd",
19909
+ "_gch",
19910
+ "_wc",
19911
+ `return (${ir})`
19912
+ );
19913
+ return fn(
19914
+ rulesImpl,
19915
+ ref,
19916
+ regex,
19917
+ literal,
19918
+ keywords,
19919
+ sequence,
19920
+ choice,
19921
+ dispatch,
19922
+ when,
19923
+ startsWith,
19924
+ endsWith,
19925
+ matches,
19926
+ otherwise,
19927
+ routed,
19928
+ attempt,
19929
+ many,
19930
+ oneOrMore,
19931
+ optional,
19932
+ sepBy,
19933
+ keepSeparator,
19934
+ not,
19935
+ peek,
19936
+ node,
19937
+ parser,
19938
+ scanTo,
19939
+ balanced,
19940
+ token,
19941
+ leaf,
19942
+ transform,
19943
+ trivia,
19944
+ classifiedTrivia,
19945
+ label,
19946
+ field,
19947
+ expect,
19948
+ adjacent,
19949
+ notAdjacent,
19950
+ _tf,
19951
+ _lf,
19952
+ _nd,
19953
+ _gch,
19954
+ _wc
19955
+ );
19577
19956
  }
19578
19957
 
19579
19958
  // src/plugin/index.ts
@@ -19738,13 +20117,30 @@ function topLevelInit(body, name) {
19738
20117
  }
19739
20118
  return null;
19740
20119
  }
19741
- function isComposedPiecesSymbol(n) {
20120
+ function isSymbolFor(n, key) {
19742
20121
  if (!n || n.type !== "CallExpression") return false;
19743
20122
  const callee = n.callee;
19744
20123
  const obj = callee?.object?.name;
19745
20124
  const prop = callee?.property?.name;
19746
20125
  const arg0 = n.arguments?.[0];
19747
- return obj === "Symbol" && prop === "for" && arg0?.value === "parseman.composedPieces";
20126
+ return obj === "Symbol" && prop === "for" && arg0?.value === key;
20127
+ }
20128
+ var isComposedPiecesSymbol = (n) => isSymbolFor(n, "parseman.composedPieces");
20129
+ var COMBINATOR_IR_KEY = "parseman.combinatorIR";
20130
+ function carriedCombinatorIR(init) {
20131
+ let n = init;
20132
+ while (n.type === "ParenthesizedExpression") n = n.expression;
20133
+ if (n.type !== "CallExpression") return null;
20134
+ const callee = n.callee;
20135
+ const args = n.arguments;
20136
+ if (callee.object?.name !== "Object" || callee.property?.name !== "defineProperty" || !isSymbolFor(args[1], COMBINATOR_IR_KEY)) return null;
20137
+ for (const p of args[2]?.properties ?? []) {
20138
+ const v = p.value;
20139
+ if (propName(p) !== "value") continue;
20140
+ if (v?.type === "Literal" && typeof v.value === "string") return v.value;
20141
+ if (v?.type === "TemplateLiteral" && v.expressions?.length === 0) return v.quasis?.[0]?.value.cooked ?? null;
20142
+ }
20143
+ return null;
19748
20144
  }
19749
20145
  function findCarriedPiecesLiteral(root) {
19750
20146
  let found = null;
@@ -19760,7 +20156,7 @@ function findCarriedPiecesLiteral(root) {
19760
20156
  const obj = callee?.object?.name;
19761
20157
  const prop = callee?.property?.name;
19762
20158
  const args = node2.arguments;
19763
- if (callee?.type === "Identifier" && callee.name === "tableRules" && args?.[1]?.type === "ObjectExpression") {
20159
+ if (callee?.type === "Identifier" && args?.[1]?.type === "ObjectExpression") {
19764
20160
  for (const p of args[1].properties ?? []) {
19765
20161
  if (p.computed && isComposedPiecesSymbol(p.key) && p.value) {
19766
20162
  found = { start: p.value.start, end: p.value.end };
@@ -19860,13 +20256,14 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19860
20256
  const allNames = /* @__PURE__ */ new Set();
19861
20257
  const importBindings = /* @__PURE__ */ new Map();
19862
20258
  const ordinaryImports = [];
20259
+ const isMacroImport = (s) => s.attributes.some((a) => {
20260
+ const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
20261
+ return key === "type" && a.value.value === "macro";
20262
+ });
19863
20263
  for (const stmt of body) {
19864
20264
  if (stmt.type !== "ImportDeclaration") continue;
19865
20265
  const s = stmt;
19866
- const isMacro = moduleAliases.has(s.source.value) && s.attributes.some((a) => {
19867
- const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
19868
- return key === "type" && a.value.value === "macro";
19869
- });
20266
+ const isMacro = moduleAliases.has(s.source.value) && isMacroImport(s);
19870
20267
  if (!isMacro) {
19871
20268
  const specifiers = [];
19872
20269
  for (const spec of s.specifiers) {
@@ -19909,7 +20306,209 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19909
20306
  }
19910
20307
  const reducerResolver = createReducerResolver(id, body, code);
19911
20308
  setReducerResolver(reducerResolver, code);
19912
- setBuilderImportResolver((name) => importBindings.get(name) ?? null);
20309
+ let builderImports = (name) => importBindings.get(name) ?? null;
20310
+ setBuilderImportResolver((name) => builderImports(name));
20311
+ const ownRoot = nearestPackageRoot(id);
20312
+ let ownDir = path2.dirname(id);
20313
+ try {
20314
+ ownDir = fs2.realpathSync(ownDir);
20315
+ } catch {
20316
+ }
20317
+ const rebaseSpecifier = (from, spec) => {
20318
+ if (!spec.startsWith(".")) return spec;
20319
+ const target = path2.resolve(path2.dirname(from), spec);
20320
+ if (ownRoot === null || nearestPackageRoot(target) !== ownRoot) return null;
20321
+ const rel = path2.relative(ownDir, target).split(path2.sep).join("/");
20322
+ return rel.startsWith("../") ? rel : `./${rel}`;
20323
+ };
20324
+ const unresolvedImports = /* @__PURE__ */ new Set();
20325
+ const valueModules = /* @__PURE__ */ new Map();
20326
+ const OPAQUE_IMPORT = "is a default or namespace import, which the macro does not resolve \u2014 import the binding by name";
20327
+ const opaqueImportsOf = (moduleBody) => {
20328
+ const out = /* @__PURE__ */ new Set();
20329
+ for (const st of moduleBody) {
20330
+ if (st.type !== "ImportDeclaration") continue;
20331
+ for (const sp of st.specifiers) {
20332
+ if (sp.type !== "ImportSpecifier") out.add(sp.local.name);
20333
+ }
20334
+ }
20335
+ return out;
20336
+ };
20337
+ const valueModule = (file) => {
20338
+ if (valueModules.has(file)) return valueModules.get(file);
20339
+ const mod = parseModuleCached(file);
20340
+ let out = null;
20341
+ if (mod) {
20342
+ const consts = /* @__PURE__ */ new Map();
20343
+ let stable;
20344
+ for (const st of mod.body) {
20345
+ const vd = unwrapVd(st);
20346
+ if (!vd) continue;
20347
+ const isConst = vd.kind === "const";
20348
+ for (const d of vd.declarations) {
20349
+ const idn = d.id;
20350
+ if (idn.type !== "Identifier" || !idn.name || !d.init) continue;
20351
+ if (isConst || (stable ??= topLevelIsStable(mod.body, mod.src.length))(idn.name)) consts.set(idn.name, d.init);
20352
+ }
20353
+ }
20354
+ out = {
20355
+ file,
20356
+ src: mod.src,
20357
+ body: mod.body,
20358
+ consts,
20359
+ imports: extractImportBindings(mod.body),
20360
+ opaqueImports: opaqueImportsOf(mod.body)
20361
+ };
20362
+ }
20363
+ valueModules.set(file, out);
20364
+ return out;
20365
+ };
20366
+ const resolveValueFile = (from, specifier) => {
20367
+ const source = resolvePrivateSourceModule(from, specifier);
20368
+ if (source) return source;
20369
+ try {
20370
+ return getCompiledResolver().resolveFileSync(from, specifier).path ?? null;
20371
+ } catch {
20372
+ return null;
20373
+ }
20374
+ };
20375
+ const evaluatingConsts = /* @__PURE__ */ new Set();
20376
+ const rebaseCarriedBuilderImports = (combi, from) => {
20377
+ const seen = /* @__PURE__ */ new Set();
20378
+ const walk = (v) => {
20379
+ if (v === null || typeof v !== "object" || seen.has(v)) return null;
20380
+ seen.add(v);
20381
+ const def = v._def;
20382
+ if (def?.buildImports) {
20383
+ const out = [];
20384
+ for (const bi of def.buildImports) {
20385
+ const source = rebaseSpecifier(from, bi.source);
20386
+ if (source === null) return bi;
20387
+ out.push({ ...bi, source });
20388
+ }
20389
+ def.buildImports = out;
20390
+ }
20391
+ for (const child of Object.values(v)) {
20392
+ for (const c of Array.isArray(child) ? child : [child]) {
20393
+ const stuck = walk(c);
20394
+ if (stuck) return stuck;
20395
+ }
20396
+ }
20397
+ return null;
20398
+ };
20399
+ return walk(combi);
20400
+ };
20401
+ const moduleConstValue = (m, name) => {
20402
+ const key = `${m.file}\0${name}`;
20403
+ if (evaluatingConsts.has(key)) return { unresolved: `is part of a declaration cycle in ${m.file}` };
20404
+ evaluatingConsts.add(key);
20405
+ const outerBuilderImports = builderImports;
20406
+ try {
20407
+ const init = m.consts.get(name);
20408
+ const ir = carriedCombinatorIR(init);
20409
+ if (ir !== null) {
20410
+ let combi2 = null;
20411
+ let why = "";
20412
+ try {
20413
+ combi2 = evalCombinatorIR(ir);
20414
+ } catch (e) {
20415
+ why = `: ${e.message}`;
20416
+ }
20417
+ if (!combi2) return { unresolved: `carries combinator IR that does not evaluate (${m.file})${why}` };
20418
+ const stuck = rebaseCarriedBuilderImports(combi2, m.file);
20419
+ return stuck ? { unresolved: `has a node() builder reading \`${stuck.local}\` from '${stuck.source}' inside ${m.file}, which this module cannot import \u2014 import it there by package name` } : { value: { combi: combi2, mfSrcs: [] } };
20420
+ }
20421
+ const scope2 = moduleValueScope(m);
20422
+ reducerResolver.register(m.file);
20423
+ builderImports = (name2) => {
20424
+ const b = m.imports.get(name2);
20425
+ const source = b ? rebaseSpecifier(m.file, b.source) : null;
20426
+ return b && source !== null ? { source, imported: b.imported } : null;
20427
+ };
20428
+ const mfs = [];
20429
+ const combi = evaluateExpr(init, scope2, m.src, mfs);
20430
+ if (combi) return { value: { combi, mfSrcs: mfs } };
20431
+ const factory = evaluateWordFactory(init, scope2, m.src) ?? evaluateWhenFactory(init, scope2, m.src);
20432
+ if (factory) return { value: factory };
20433
+ const value = evaluateStaticValue(init, scope2, m.src);
20434
+ if (value !== null && value !== void 0 || isStaticNullishExpression(init)) return { value };
20435
+ return {
20436
+ unresolved: /\btableRules\b/.test(m.src.slice(init.start, init.end)) ? `is a compiled parser in ${m.file} that carries no combinator IR \u2014 rebuild that module with parseman v${PARSEMAN_VERSION}` : `is not a statically-known combinator or value (${m.file})`
20437
+ };
20438
+ } finally {
20439
+ evaluatingConsts.delete(key);
20440
+ builderImports = outerBuilderImports;
20441
+ }
20442
+ };
20443
+ const moduleValueScope = (m) => m.scope ??= new ModuleScope((name) => {
20444
+ if (m.consts.has(name)) return moduleConstValue(m, name);
20445
+ const binding = m.imports.get(name);
20446
+ if (binding) return importedValue(m.file, binding);
20447
+ return m.opaqueImports.has(name) ? { unresolved: OPAQUE_IMPORT } : null;
20448
+ }, unresolvedImports);
20449
+ const exportedValue = (m, name, depth) => {
20450
+ for (const st of m.body) {
20451
+ if (st.type !== "ExportNamedDeclaration" || !st.source) continue;
20452
+ for (const sp of st.specifiers ?? []) {
20453
+ if (sp.exported.name !== name) continue;
20454
+ const source = st.source.value;
20455
+ const file = depth < 16 ? resolveValueFile(m.file, source) : null;
20456
+ const next = file ? valueModule(file) : null;
20457
+ if (!next) return { unresolved: `is re-exported from '${source}', which does not resolve from ${m.file}` };
20458
+ return exportedValue(next, sp.local.name, depth + 1);
20459
+ }
20460
+ }
20461
+ const local = exportLocalName(m.body, name);
20462
+ if (local === null) {
20463
+ for (const st of m.body) {
20464
+ if (st.type !== "ExportAllDeclaration" || st.exported) continue;
20465
+ const file = depth < 16 ? resolveValueFile(m.file, st.source.value) : null;
20466
+ const next = file ? valueModule(file) : null;
20467
+ if (!next) continue;
20468
+ const r = exportedValue(next, name, depth + 1);
20469
+ if (r !== null && "unresolved" in r && r.unresolved.startsWith("is not exported by ")) continue;
20470
+ return r;
20471
+ }
20472
+ return { unresolved: `is not exported by ${m.file}` };
20473
+ }
20474
+ const scope2 = moduleValueScope(m);
20475
+ if (!scope2.has(local)) {
20476
+ scope2.get(local);
20477
+ return { unresolved: `could not be resolved in ${m.file}` };
20478
+ }
20479
+ return { value: scope2.get(local) };
20480
+ };
20481
+ const importedValue = (from, binding) => {
20482
+ const file = resolveValueFile(from, binding.source);
20483
+ const m = file ? valueModule(file) : null;
20484
+ if (!m) return { unresolved: `is imported from '${binding.source}', which does not resolve from ${from}` };
20485
+ return exportedValue(m, binding.imported, 0);
20486
+ };
20487
+ const deferredLocals = /* @__PURE__ */ new Map();
20488
+ const deferredResults = /* @__PURE__ */ new Map();
20489
+ const mainOpaqueImports = opaqueImportsOf(body);
20490
+ const resolveMainFree = (name) => {
20491
+ const done = deferredResults.get(name);
20492
+ if (done !== void 0) return done;
20493
+ const deferred = deferredLocals.get(name);
20494
+ if (deferred !== void 0) {
20495
+ deferredLocals.delete(name);
20496
+ const outer = [...unresolvedImports];
20497
+ unresolvedImports.clear();
20498
+ const value = evaluateStaticValue(deferred, scope, code);
20499
+ const why = [...unresolvedImports];
20500
+ unresolvedImports.clear();
20501
+ for (const n of outer) unresolvedImports.add(n);
20502
+ const result2 = value !== null && value !== void 0 || isStaticNullishExpression(deferred) ? { value } : why.length > 0 ? { unresolved: `could not be evaluated at build time, because: ${why.join("; ")}` } : null;
20503
+ deferredResults.set(name, result2);
20504
+ return result2;
20505
+ }
20506
+ const binding = importBindings.get(name);
20507
+ if (!binding) return mainOpaqueImports.has(name) ? { unresolved: OPAQUE_IMPORT } : null;
20508
+ const resolved = importedValue(id, binding);
20509
+ if (resolved !== null && "value" in resolved) usedImportedFactories.add(name);
20510
+ return resolved;
20511
+ };
19913
20512
  const topLevelFunction = (moduleBody, name) => {
19914
20513
  for (const st of moduleBody) {
19915
20514
  const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
@@ -19918,19 +20517,17 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19918
20517
  }
19919
20518
  return null;
19920
20519
  };
19921
- const isParsemanMacroImport = (stmt) => {
19922
- if (stmt.type !== "ImportDeclaration") return false;
19923
- const s = stmt;
19924
- return moduleAliases.has(s.source.value) && s.attributes.some((a) => {
19925
- const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
19926
- return key === "type" && a.value.value === "macro";
19927
- });
19928
- };
19929
- const sourceScopeUntil = (moduleBody, source, until) => {
19930
- const out = /* @__PURE__ */ new Map();
20520
+ const sourceScopeUntil = (file, moduleBody, source, until) => {
20521
+ const imports = extractImportBindings(moduleBody);
20522
+ const opaque = opaqueImportsOf(moduleBody);
20523
+ const out = new ModuleScope((name) => {
20524
+ const binding = imports.get(name);
20525
+ if (binding) return importedValue(file, binding);
20526
+ return opaque.has(name) ? { unresolved: OPAQUE_IMPORT } : null;
20527
+ }, unresolvedImports);
19931
20528
  for (const stmt of moduleBody) {
19932
20529
  if (stmt.type === "ImportDeclaration") {
19933
- if (!isParsemanMacroImport(stmt)) return null;
20530
+ if (moduleAliases.has(stmt.source.value) && !isMacroImport(stmt)) return null;
19934
20531
  continue;
19935
20532
  }
19936
20533
  if (stmt.type === "FunctionDeclaration") continue;
@@ -19983,6 +20580,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19983
20580
  }
19984
20581
  return out;
19985
20582
  };
20583
+ const factoryFailures = /* @__PURE__ */ new Map();
19986
20584
  const usedImportedFactories = /* @__PURE__ */ new Set();
19987
20585
  const markUsedImportedFactories = (names) => {
19988
20586
  for (const name of names ?? []) usedImportedFactories.add(name);
@@ -19995,8 +20593,13 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19995
20593
  const localFor = exportLocalName(mod.body, binding.imported);
19996
20594
  const init = localFor ? topLevelInit(mod.body, localFor) ?? topLevelFunction(mod.body, localFor) : null;
19997
20595
  if (!init || init.type !== "ArrowFunctionExpression" && init.type !== "FunctionExpression" && init.type !== "FunctionDeclaration") continue;
19998
- const factoryScope = sourceScopeUntil(mod.body, mod.src, init.start);
19999
- if (!factoryScope) continue;
20596
+ unresolvedImports.clear();
20597
+ const factoryScope = sourceScopeUntil(file, mod.body, mod.src, init.start);
20598
+ if (!factoryScope) {
20599
+ if (unresolvedImports.size > 0) factoryFailures.set(localName, [...unresolvedImports]);
20600
+ unresolvedImports.clear();
20601
+ continue;
20602
+ }
20000
20603
  factoryDecls.set(localName, {
20001
20604
  fn: init,
20002
20605
  declaredAt: -1,
@@ -20006,7 +20609,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20006
20609
  });
20007
20610
  reducerResolver.register(file);
20008
20611
  }
20009
- const scope = /* @__PURE__ */ new Map();
20612
+ const scope = new ModuleScope(resolveMainFree, unresolvedImports);
20010
20613
  const replacements = [];
20011
20614
  const warnings = [];
20012
20615
  beginDegradationCapture();
@@ -20026,6 +20629,12 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20026
20629
  m = /* @__PURE__ */ new Map();
20027
20630
  pendingBuilderImports.set(bi.source, m);
20028
20631
  }
20632
+ const prev = m.get(bi.local);
20633
+ if (prev !== void 0 && prev !== bi.imported) {
20634
+ throw new Error(
20635
+ `${id} \u2014 parseman will not emit this module: inlined builders read \`${bi.local}\` as both \`${prev}\` and \`${bi.imported}\` from ${JSON.stringify(bi.source)}. The builder source names \`${bi.local}\` verbatim, so one import cannot bind both; import them under different names.`
20636
+ );
20637
+ }
20029
20638
  m.set(bi.local, bi.imported);
20030
20639
  }
20031
20640
  if (def?.tag === "lazy" && v._ruleName === void 0 && typeof def.thunk === "function") {
@@ -20067,6 +20676,41 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20067
20676
  anyUnresolved = true;
20068
20677
  warnings.push(`${id}:${lineOf(pos)} \u2014 ${msg} (running via the interpreter; add the plugin or simplify the declaration to compile it)`);
20069
20678
  };
20679
+ const unevaluable = (pos, what) => {
20680
+ const names = [...unresolvedImports];
20681
+ unresolvedImports.clear();
20682
+ throw new Error(
20683
+ `${id}:${lineOf(pos)} \u2014 ${what} can't be evaluated at build time` + (names.length > 0 ? `; unresolved binding(s):
20684
+ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/value expression)") + `
20685
+ parseman will not emit this module rather than drop it silently.`
20686
+ );
20687
+ };
20688
+ const requiredOption = (label2, name, value, evaluate) => {
20689
+ if (value === void 0 || isStaticNullishExpression(value)) return void 0;
20690
+ unresolvedImports.clear();
20691
+ const out = evaluate(value);
20692
+ if (out === null) {
20693
+ const id2 = value.type === "Identifier" ? value.name : null;
20694
+ if (id2 !== null && scope.has(id2) && scope.get(id2) == null) return void 0;
20695
+ return unevaluable(value.start, `${label2}: rules({ ${name} })`);
20696
+ }
20697
+ unresolvedImports.clear();
20698
+ return out;
20699
+ };
20700
+ const RULES_OPTIONS = /* @__PURE__ */ new Set(["trivia", "scanSkip", "hostMode", "trackLines"]);
20701
+ const assertPlainOptions = (optExpr, label2) => {
20702
+ for (const p of optExpr?.type === "ObjectExpression" ? optExpr.properties : []) {
20703
+ if (propName(p) !== null) continue;
20704
+ const key = p.type === "Property" ? evaluateStaticValue(p.key, scope, code) : null;
20705
+ if (typeof key !== "string" || RULES_OPTIONS.has(key)) {
20706
+ unevaluable(p.start, `${label2}: an options ${p.type === "Property" ? "computed key" : "spread"}`);
20707
+ }
20708
+ }
20709
+ };
20710
+ const triviaOption = (optExpr, label2) => {
20711
+ assertPlainOptions(optExpr, label2);
20712
+ return requiredOption(label2, "trivia", optionProp(optExpr, "trivia"), (v) => evaluateExpr(v, scope, code, []));
20713
+ };
20070
20714
  const evaluateRulesFactory = (init, label2) => {
20071
20715
  const args = init.arguments;
20072
20716
  const arg0 = args[0];
@@ -20082,7 +20726,15 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20082
20726
  const factoryScope = namedFactory?.scope ?? scope;
20083
20727
  const factoryCode = namedFactory?.code ?? code;
20084
20728
  const importedFactory = namedFactory?.imported === true && factoryArg === namedFactory.fn ? factoryArgRaw.name : void 0;
20729
+ const failedFactory = factoryArgRaw.type === "Identifier" ? factoryFailures.get(factoryArgRaw.name) : void 0;
20730
+ if (failedFactory) {
20731
+ for (const n of failedFactory) unresolvedImports.add(n);
20732
+ unevaluable(init.start, `${label2}: the imported rules() factory \`${factoryArgRaw.name}\``);
20733
+ }
20085
20734
  const optionsArg = optionsFirst ? arg0 : arg1;
20735
+ if (optionsArg !== void 0 && optionsArg.type !== "ObjectExpression" && !isStaticNullishExpression(optionsArg)) {
20736
+ unevaluable(optionsArg.start, `${label2}: rules() options`);
20737
+ }
20086
20738
  const optionValue = (name) => optionProp(optionsArg, name);
20087
20739
  const hostModeValue = optionValue("hostMode");
20088
20740
  const gHostMode = hostModeValue?.type === "Literal" ? hostModeValue.value : void 0;
@@ -20096,11 +20748,16 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20096
20748
  warn(init.start, `${label2}: rules({ trackLines }) must be a boolean literal`);
20097
20749
  return null;
20098
20750
  }
20099
- const triviaValue = optionValue("trivia");
20100
- const gTrivia = triviaValue ? evaluateExpr(triviaValue, scope, code, []) : void 0;
20101
- const scanSkipValue = optionValue("scanSkip");
20102
- const gScanSkip = scanSkipValue ? evaluateCombinatorArray(scanSkipValue, scope, code) ?? void 0 : void 0;
20751
+ const gTrivia = triviaOption(optionsArg, label2);
20752
+ const scanSkipArray = requiredOption(label2, "scanSkip", optionValue("scanSkip"), (v) => {
20753
+ const units = evaluateCombinatorArray(v, scope, code);
20754
+ if (units) return units;
20755
+ const value = evaluateStaticValue(v, scope, code);
20756
+ return Array.isArray(value) && value.length === 0 ? [] : null;
20757
+ });
20758
+ const gScanSkip = scanSkipArray?.length ? scanSkipArray : void 0;
20103
20759
  const why = {};
20760
+ unresolvedImports.clear();
20104
20761
  const ruleMap = evaluateParserFactory(factoryArg, factoryScope, factoryCode, [], why, {
20105
20762
  ...gTrivia ? { trivia: gTrivia } : {},
20106
20763
  ...gScanSkip ? { scanSkip: gScanSkip } : {},
@@ -20108,6 +20765,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20108
20765
  ...gTrackLines === true ? { trackLines: true } : {}
20109
20766
  });
20110
20767
  if (!ruleMap) {
20768
+ if (unresolvedImports.size > 0) unevaluable(init.start, `${label2}: rules(...) factory`);
20111
20769
  warn(init.start, why.reason === void 0 ? `${label2}: rules(...) factory isn't statically evaluable` : `${label2}: rules(...) factory isn't statically evaluable \u2014 ${why.reason}`);
20112
20770
  return null;
20113
20771
  }
@@ -20124,6 +20782,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20124
20782
  if (!evaluated) return null;
20125
20783
  const refusals = [];
20126
20784
  const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, ...evaluated.trackLines ? { trackLines: true } : {}, recovery, coverage: grammarCoverage, refusals });
20785
+ if (compiled !== null) collectBuilderImports([...evaluated.ruleMap]);
20127
20786
  if (compiled !== null) usedTableRuntime = true;
20128
20787
  return {
20129
20788
  replacement: compiled?.replacement ?? null,
@@ -20185,49 +20844,63 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20185
20844
  };
20186
20845
  const localComposedCarried = /* @__PURE__ */ new Map();
20187
20846
  const importedPiecesCache = /* @__PURE__ */ new Map();
20188
- const resolveModulePieces = (module, exportName, seen) => {
20847
+ const resolveModulePieces = (module, exportName, seen, unresolved, local = false) => {
20189
20848
  const { file } = module;
20190
20849
  const mod = module.source ? lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recovery) : parseModuleCached(file);
20191
20850
  if (!mod) return null;
20192
- const localFor = exportLocalName(mod.body, exportName);
20851
+ const localFor = local ? exportName : exportLocalName(mod.body, exportName);
20193
20852
  const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
20194
20853
  const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
20195
20854
  if (!literalRange) return null;
20196
20855
  const literal2 = mod.src.slice(literalRange.start, literalRange.end);
20856
+ const fail = (why) => {
20857
+ unresolved.why ??= `${exportName} in ${file}: ${why}`;
20858
+ return null;
20859
+ };
20197
20860
  const imports = extractImportBindings(mod.body);
20198
20861
  const stubNames = [];
20199
20862
  const stubVals = [];
20200
- const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
20863
+ const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*(?:\/\*[^]*?\*\/\s*)*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
20201
20864
  const done = /* @__PURE__ */ new Set();
20202
20865
  for (let m; m = spreadRe.exec(literal2); ) {
20203
- const local = m[1];
20204
- if (done.has(local)) continue;
20205
- done.add(local);
20206
- const b = imports.get(local);
20866
+ const local2 = m[1];
20867
+ if (done.has(local2)) continue;
20868
+ done.add(local2);
20869
+ const b = imports.get(local2);
20207
20870
  let subPieces = [];
20208
20871
  if (b) {
20209
20872
  const subModule = resolveGrammarModule(file, b.source);
20210
- if (subModule && !seen.has(subModule.file)) {
20211
- subPieces = resolveModulePieces(subModule, b.imported, new Set(seen).add(subModule.file)) ?? [];
20873
+ if (subModule === null) return fail(`its carried pieces spread \`${local2}\` from '${b.source}', a module that could not be found`);
20874
+ if (!seen.has(subModule.file)) {
20875
+ subPieces = resolveModulePieces(subModule, b.imported, new Set(seen).add(subModule.file), unresolved);
20876
+ if (subPieces === null) return fail(`its carried pieces spread \`${local2}\` from '${b.source}', which could not be resolved`);
20212
20877
  }
20878
+ } else if (topLevelInit(mod.body, local2) !== null && !seen.has(`${file}#${local2}`)) {
20879
+ subPieces = resolveModulePieces(module, local2, new Set(seen).add(`${file}#${local2}`), unresolved, true);
20880
+ if (subPieces === null) return fail(`its carried pieces spread the inlined binding \`${local2}\`, which carries none`);
20881
+ } else {
20882
+ return fail(`its carried pieces spread \`${local2}\`, which is neither an import nor a top-level binding of the module`);
20213
20883
  }
20214
- stubNames.push(local);
20884
+ stubNames.push(local2);
20215
20885
  stubVals.push({ [COMPOSED_PIECES]: subPieces });
20216
20886
  }
20217
20887
  try {
20218
20888
  const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
20219
20889
  return list;
20220
- } catch {
20221
- return null;
20890
+ } catch (e) {
20891
+ return fail(`its carried pieces literal did not evaluate (${e.message})`);
20222
20892
  }
20223
20893
  };
20894
+ const unresolvedCompiled = /* @__PURE__ */ new Map();
20224
20895
  const importedPieces = (localName) => {
20225
20896
  if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
20226
20897
  let result2 = null;
20227
20898
  const binding = importBindings.get(localName);
20228
20899
  if (binding) {
20229
20900
  const module = resolveGrammarModule(id, binding.source);
20230
- result2 = module ? resolveModulePieces(module, binding.imported, /* @__PURE__ */ new Set([module.file])) : null;
20901
+ const unresolved = {};
20902
+ result2 = module ? resolveModulePieces(module, binding.imported, /* @__PURE__ */ new Set([module.file]), unresolved) : null;
20903
+ if (result2 === null && unresolved.why !== void 0) unresolvedCompiled.set(localName, unresolved.why);
20231
20904
  }
20232
20905
  importedPiecesCache.set(localName, result2);
20233
20906
  return result2;
@@ -20238,7 +20911,8 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20238
20911
  const add = (item) => {
20239
20912
  if (!isIR(item)) return false;
20240
20913
  if (item.trackLines === true) trackLines = true;
20241
- pieces.push({ ns: item.ns, rules: evalRuleMapIR(item.ir) });
20914
+ const map = evalRulesIR(item.ir);
20915
+ pieces.push({ ns: item.ns, rules: Object.entries(map), recipes: map[RULES_RECIPE] });
20242
20916
  return true;
20243
20917
  };
20244
20918
  for (const item of items) {
@@ -20265,10 +20939,20 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20265
20939
  }
20266
20940
  return [...winners];
20267
20941
  };
20268
- const mergedCarriedRules = (items) => {
20942
+ const mergedCarriedRules = (items, link) => {
20269
20943
  const carriedMaps = carriedRuleMaps(items);
20270
20944
  if (carriedMaps === null) return null;
20271
- return { rules: mergeRuleMaps(carriedMaps.pieces.map((p) => p.rules)), trackLines: carriedMaps.trackLines };
20945
+ const linked = linkRules(carriedMaps.pieces.flatMap((p) => p.recipes), link);
20946
+ const rules2 = Object.entries(linked).filter(([, rule]) => {
20947
+ if (rule._def.tag !== "lazy") return true;
20948
+ try {
20949
+ rule._def.thunk();
20950
+ return true;
20951
+ } catch {
20952
+ return false;
20953
+ }
20954
+ });
20955
+ return { rules: rules2, trackLines: carriedMaps.trackLines };
20272
20956
  };
20273
20957
  const applyComposingTrivia = (ruleMap, composing) => {
20274
20958
  for (const [, rule] of ruleMap) {
@@ -20308,9 +20992,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20308
20992
  const a0 = rulesArgs[0];
20309
20993
  const a1 = rulesArgs[1];
20310
20994
  const optExpr = a0?.type === "ObjectExpression" ? a0 : a1?.type === "ObjectExpression" ? a1 : void 0;
20311
- const triviaValue = optionProp(optExpr, "trivia");
20312
- if (!triviaValue) return void 0;
20313
- return evaluateExpr(triviaValue, scope, code, []) ?? void 0;
20995
+ return triviaOption(optExpr, "compose() element");
20314
20996
  };
20315
20997
  const composingTrivia = (elements) => {
20316
20998
  for (let i = elements.length - 1; i >= 0; i--) {
@@ -20337,6 +21019,8 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20337
21019
  }
20338
21020
  const elements = arr.elements;
20339
21021
  const cOptions = init.arguments[1];
21022
+ if (cOptions !== void 0 && cOptions.type !== "ObjectExpression") unevaluable(cOptions.start, "compose() options");
21023
+ assertPlainOptions(cOptions, "compose()");
20340
21024
  const cHostModeValue = optionProp(cOptions, "hostMode");
20341
21025
  const cHostMode = cHostModeValue?.type === "Literal" ? cHostModeValue.value : void 0;
20342
21026
  if (cHostModeValue !== void 0 && cHostMode !== "ast" && cHostMode !== "cst") {
@@ -20349,15 +21033,25 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20349
21033
  for (let i = 0; i < elements.length; i++) {
20350
21034
  const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
20351
21035
  if (!r) {
21036
+ const el = elements[i];
21037
+ const name = el.type === "Identifier" ? el.name : void 0;
21038
+ const why = name === void 0 ? void 0 : unresolvedCompiled.get(name);
21039
+ if (why !== void 0) {
21040
+ throw new Error(
21041
+ `${id}:${lineOf(init.start)} \u2014 compose(): argument ${i} (\`${name}\` from '${importBindings.get(name)?.source}') is a build-compiled grammar whose carried pieces could not be resolved: ${why}. A runtime compose() cannot link a build-compiled grammar, so this would throw at import. The usual cause is a bundler rewriting the upstream module (inlining or renaming an ancestor grammar it composes, or reprinting its carried-pieces literal); keep upstream grammar packages external to the bundle.`
21042
+ );
21043
+ }
20352
21044
  warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
20353
21045
  return null;
20354
21046
  }
20355
21047
  carried.push(...r.carried);
20356
21048
  importedFactories.push(...r.importedFactories ?? []);
20357
21049
  }
20358
- const merged = mergedCarriedRules(carried);
21050
+ const merged = mergedCarriedRules(carried, {
21051
+ trivia: composing,
21052
+ ...cHostMode ? { hostMode: cHostMode } : {}
21053
+ });
20359
21054
  if (merged !== null) {
20360
- if (composing) applyComposingTrivia(merged.rules, composing);
20361
21055
  collectBuilderImports(merged.rules);
20362
21056
  const refusals = [];
20363
21057
  const compiled = compileRuleMap(merged.rules, {
@@ -20552,11 +21246,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20552
21246
  if (d.id.type === "Identifier") {
20553
21247
  const varName = d.id.name;
20554
21248
  if (!referencesAny(init, allNames, scope)) {
20555
- const staticValue = evaluateStaticValue(init, scope, code);
20556
- if (staticValue !== null && staticValue !== void 0 || isStaticNullishExpression(init)) {
20557
- ;
20558
- scope.set(varName, staticValue);
20559
- }
21249
+ deferredLocals.set(varName, init);
20560
21250
  continue;
20561
21251
  }
20562
21252
  if (refNames.has(varName)) {
@@ -20643,6 +21333,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20643
21333
  continue;
20644
21334
  }
20645
21335
  const mapFnSources = [];
21336
+ unresolvedImports.clear();
20646
21337
  const parser2 = evaluateExpr(init, scope, code, mapFnSources);
20647
21338
  if (parser2 === null) {
20648
21339
  const wordFactory = evaluateWordFactory(init, scope, code);
@@ -20675,6 +21366,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20675
21366
  if (exportPrefix) exportedFactories.push({ name: varName, pos: init.start });
20676
21367
  continue;
20677
21368
  }
21369
+ if (unresolvedImports.size > 0) unevaluable(init.start, `"${varName}"`);
20678
21370
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
20679
21371
  continue;
20680
21372
  }
@@ -20687,7 +21379,8 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20687
21379
  replacements.push({
20688
21380
  start: init.start,
20689
21381
  end: init.end,
20690
- replacement: compiled.inlineExpression
21382
+ replacement: compiled.inlineExpression,
21383
+ carry: { name: varName, combi: parser2, exported: exportPrefix !== "" }
20691
21384
  });
20692
21385
  scope.set(varName, { combi: parser2, mfSrcs: mapFnSources });
20693
21386
  } else if (d.id.type === "ObjectPattern") {
@@ -20754,37 +21447,58 @@ ${which}
20754
21447
  Drop the \`export\`. The factory is macro-only; \`rules(${exportedFactories[0].name})\` is lowered in place and needs no runtime value.`
20755
21448
  );
20756
21449
  }
21450
+ const withCombinatorIR = (r) => {
21451
+ if (!r.carry || !r.carry.exported && !referencedNames().live.has(r.carry.name)) return r;
21452
+ let ir = null;
21453
+ try {
21454
+ ir = serializeCombinator(r.carry.combi);
21455
+ } catch {
21456
+ }
21457
+ return ir === null ? r : {
21458
+ ...r,
21459
+ replacement: `/* @__PURE__ */ Object.defineProperty(${r.replacement}, Symbol.for('${COMBINATOR_IR_KEY}'), { value: ${JSON.stringify(ir)}, enumerable: false })`
21460
+ };
21461
+ };
20757
21462
  const ms = new MagicString(code);
20758
21463
  const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
20759
21464
  const isInsideReplacement = (start, end) => replacementRanges.some((r) => start >= r.start && end <= r.end);
20760
- const importedBindingStillReferenced = (local) => {
20761
- const walk = (node2, parent, parentKey) => {
20762
- if (!node2 || typeof node2 !== "object") return false;
21465
+ let referenced = null;
21466
+ const referencedNames = () => {
21467
+ if (referenced !== null) return referenced;
21468
+ const all = /* @__PURE__ */ new Set();
21469
+ const live = /* @__PURE__ */ new Set();
21470
+ const deadRanges = [...[...factoryDecls.values()].filter((f) => !f.imported).map((f) => f.fn), ...anyUnresolved ? [] : defineRemovals];
21471
+ const walk = (node2, inFactory, parent, parentKey) => {
21472
+ if (!node2 || typeof node2 !== "object") return;
20763
21473
  const rec = node2;
20764
- if (rec.type === "ImportDeclaration") return false;
20765
- if (typeof rec.start === "number" && typeof rec.end === "number" && isInsideReplacement(rec.start, rec.end)) return false;
20766
- if ((rec.type === "Identifier" || rec.type === "BindingIdentifier") && rec.name === local) {
20767
- if (parentKey === "key" && (parent?.type === "ObjectProperty" || parent?.type === "Property" || parent?.type === "PropertyDefinition") && parent.computed !== true) return false;
20768
- if (parentKey === "property" && parent?.type === "StaticMemberExpression") return false;
20769
- if (parentKey === "property" && parent?.type === "MemberExpression" && parent.computed !== true) return false;
20770
- return true;
21474
+ if (rec.type === "ImportDeclaration") return;
21475
+ if (typeof rec.start === "number" && typeof rec.end === "number") {
21476
+ if (isInsideReplacement(rec.start, rec.end)) return;
21477
+ inFactory ||= deadRanges.some((f) => rec.start >= f.start && rec.end <= f.end);
21478
+ }
21479
+ if (rec.type === "Identifier" || rec.type === "BindingIdentifier") {
21480
+ const isKey = parentKey === "key" && (parent?.type === "ObjectProperty" || parent?.type === "Property" || parent?.type === "PropertyDefinition") && parent.computed !== true;
21481
+ const isMember = parentKey === "property" && (parent?.type === "StaticMemberExpression" || parent?.type === "MemberExpression" && parent.computed !== true);
21482
+ const isDeclared = parentKey === "id" && parent?.type === "VariableDeclarator";
21483
+ if (!isKey && !isMember && !isDeclared) {
21484
+ all.add(rec.name);
21485
+ if (!inFactory) live.add(rec.name);
21486
+ }
21487
+ return;
20771
21488
  }
20772
21489
  for (const key of Object.keys(rec)) {
20773
21490
  if (key === "type" || key === "start" || key === "end") continue;
20774
21491
  const value = rec[key];
20775
- if (Array.isArray(value)) {
20776
- if (value.some((child) => walk(child, rec, key))) return true;
20777
- } else if (walk(value, rec, key)) {
20778
- return true;
20779
- }
21492
+ if (Array.isArray(value)) for (const child of value) walk(child, inFactory, rec, key);
21493
+ else walk(value, inFactory, rec, key);
20780
21494
  }
20781
- return false;
20782
21495
  };
20783
- return body.some((stmt) => walk(stmt));
21496
+ for (const stmt of body) walk(stmt, false);
21497
+ return referenced = { all, live };
20784
21498
  };
20785
21499
  for (const imp of ordinaryImports) {
20786
21500
  if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(
20787
- (spec) => spec.type === "ImportSpecifier" && usedImportedFactories.has(spec.local) && !importedBindingStillReferenced(spec.local)
21501
+ (spec) => spec.type === "ImportSpecifier" && usedImportedFactories.has(spec.local) && !referencedNames().all.has(spec.local)
20788
21502
  )) {
20789
21503
  ms.remove(imp.start, imp.end);
20790
21504
  }
@@ -20801,7 +21515,7 @@ ${which}
20801
21515
  ms.overwrite(imp.start, imp.end, stripped);
20802
21516
  }
20803
21517
  }
20804
- const applied = runtimeComposeFallback ? [] : replacements;
21518
+ const applied = runtimeComposeFallback ? [] : replacements.map((r) => withCombinatorIR(r));
20805
21519
  for (const { start, end, replacement } of applied.slice().sort((a, b) => b.start - a.start)) {
20806
21520
  ms.overwrite(start, end, replacement);
20807
21521
  }