parseman 0.52.0 → 0.53.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 (62) hide show
  1. package/dist/analysis/diagnostics.js +358 -8
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +5929 -6172
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/parser.d.ts +34 -0
  6. package/dist/combinators/parser.d.ts.map +1 -1
  7. package/dist/combinators/ref.d.ts +0 -13
  8. package/dist/combinators/ref.d.ts.map +1 -1
  9. package/dist/compiler/borrowed-rules.d.ts +30 -0
  10. package/dist/compiler/borrowed-rules.d.ts.map +1 -0
  11. package/dist/compiler/compile-linkable-table.d.ts +1 -1
  12. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  13. package/dist/compiler/ir-serialize.d.ts +10 -4
  14. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  15. package/dist/compiler/linker.d.ts +48 -91
  16. package/dist/compiler/linker.d.ts.map +1 -1
  17. package/dist/coverage.d.ts +1 -1
  18. package/dist/coverage.d.ts.map +1 -1
  19. package/dist/cst/reflection.d.ts.map +1 -1
  20. package/dist/index.js +416 -704
  21. package/dist/index.js.map +3 -3
  22. package/dist/language-service/index.js.map +1 -1
  23. package/dist/plugin/evaluator.d.ts +38 -2
  24. package/dist/plugin/evaluator.d.ts.map +1 -1
  25. package/dist/plugin/index.d.ts.map +1 -1
  26. package/dist/plugin/index.js +1073 -523
  27. package/dist/plugin/index.js.map +3 -3
  28. package/dist/plugin/ir-eval.d.ts +21 -0
  29. package/dist/plugin/ir-eval.d.ts.map +1 -0
  30. package/dist/plugin/reducer-resolver.d.ts +6 -0
  31. package/dist/plugin/reducer-resolver.d.ts.map +1 -1
  32. package/dist/spec/index.js +555 -6109
  33. package/dist/spec/index.js.map +3 -3
  34. package/dist/spec/railroad-lib.d.ts +7 -2
  35. package/dist/spec/railroad-lib.d.ts.map +1 -1
  36. package/dist/spec/railroad.d.ts.map +1 -1
  37. package/dist/table/compile-rule-map.d.ts +11 -4
  38. package/dist/table/compile-rule-map.d.ts.map +1 -1
  39. package/dist/table/compile.d.ts +16 -0
  40. package/dist/table/compile.d.ts.map +1 -1
  41. package/dist/table/index.js +235 -87
  42. package/dist/table/index.js.map +2 -2
  43. package/dist/version.d.ts +1 -1
  44. package/package.json +1 -1
  45. package/src/combinators/parser.ts +133 -67
  46. package/src/combinators/ref.ts +10 -1
  47. package/src/compiler/borrowed-rules.ts +88 -0
  48. package/src/compiler/compile-linkable-table.ts +1 -1
  49. package/src/compiler/grammar-coverage-ids.ts +35 -6
  50. package/src/compiler/ir-serialize.ts +12 -118
  51. package/src/compiler/linker.ts +190 -666
  52. package/src/coverage.ts +6 -4
  53. package/src/cst/reflection.ts +6 -1
  54. package/src/plugin/evaluator.ts +113 -13
  55. package/src/plugin/index.ts +588 -110
  56. package/src/plugin/ir-eval.ts +165 -0
  57. package/src/plugin/reducer-resolver.ts +20 -3
  58. package/src/spec/railroad-lib.ts +18 -8
  59. package/src/spec/railroad.ts +3 -4
  60. package/src/table/compile-rule-map.ts +18 -8
  61. package/src/table/compile.ts +61 -0
  62. 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
  },
@@ -1810,6 +1811,7 @@ function collectFromCombinator(parser2, out, seen, followLazy = true) {
1810
1811
  function collectGrammarReflection(ruleMap, opts) {
1811
1812
  const byType = /* @__PURE__ */ new Map();
1812
1813
  const followLazy = opts?.followLazy !== false;
1814
+ const seen = /* @__PURE__ */ new Set();
1813
1815
  for (const [, rule] of ruleMap) {
1814
1816
  if (!followLazy && rule._def.tag === "lazy") {
1815
1817
  try {
@@ -1819,7 +1821,7 @@ function collectGrammarReflection(ruleMap, opts) {
1819
1821
  }
1820
1822
  continue;
1821
1823
  }
1822
- collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
1824
+ collectFromCombinator(rule, byType, seen, followLazy);
1823
1825
  }
1824
1826
  return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
1825
1827
  }
@@ -1850,9 +1852,26 @@ function isNamedRuleRefForAnotherRule(r, key) {
1850
1852
  const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
1851
1853
  return name !== void 0 && name !== key;
1852
1854
  }
1855
+ var claimedRules = /* @__PURE__ */ new WeakSet();
1856
+ function ownRule(p, copies) {
1857
+ if (!claimedRules.has(p) || p._meta.isTrivia) return p;
1858
+ let own = copies.get(p);
1859
+ if (own === void 0) {
1860
+ const { grammarTrivia: _t, grammarScanSkip: _s, grammarHostMode: _h, grammarTrackLines: _l, ...meta } = p._meta;
1861
+ copies.set(p, own = { ...p, _meta: meta });
1862
+ }
1863
+ return own;
1864
+ }
1853
1865
  function rules(a, b) {
1854
1866
  const factory = typeof a === "function" ? a : b;
1855
1867
  const options = typeof a === "function" ? b : a;
1868
+ const recipe = { factory, options };
1869
+ const map = linkRules([recipe]);
1870
+ Object.defineProperty(map, RULES_RECIPE, { value: [recipe], enumerable: false });
1871
+ return map;
1872
+ }
1873
+ var RULES_RECIPE = /* @__PURE__ */ Symbol.for("parseman.rulesRecipe");
1874
+ function linkRules(recipes, link) {
1856
1875
  const cache = {};
1857
1876
  const proxy = new Proxy(cache, {
1858
1877
  get(target, key) {
@@ -1866,10 +1885,18 @@ function rules(a, b) {
1866
1885
  return record[key];
1867
1886
  }
1868
1887
  });
1869
- const definitions = factory(proxy);
1870
- for (const key of Object.keys(definitions)) {
1888
+ const winners = /* @__PURE__ */ new Map();
1889
+ for (const recipe of recipes) {
1890
+ const definitions = recipe.factory(proxy);
1891
+ for (const key of Object.keys(definitions)) {
1892
+ winners.set(key, { parser: definitions[key], options: recipe.options });
1893
+ }
1894
+ }
1895
+ const keys = [...winners.keys()];
1896
+ const copies = /* @__PURE__ */ new Map();
1897
+ for (const [key, { parser: defined }] of winners) {
1871
1898
  const placeholder = cache[key];
1872
- const parser2 = definitions[key];
1899
+ const parser2 = ownRule(defined, copies);
1873
1900
  if (placeholder === parser2) {
1874
1901
  throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
1875
1902
  }
@@ -1878,7 +1905,8 @@ function rules(a, b) {
1878
1905
  placeholder.define(parser2);
1879
1906
  placeholder._meta.firstSet = parser2._meta.firstSet;
1880
1907
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
1881
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
1908
+ } else if (isNamedRuleRefForAnotherRule(parser2, key) || link !== void 0) {
1909
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
1882
1910
  const alias = ref();
1883
1911
  tagRule(alias, key);
1884
1912
  alias.define(parser2);
@@ -1890,48 +1918,34 @@ function rules(a, b) {
1890
1918
  cache[key] = parser2;
1891
1919
  }
1892
1920
  }
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;
1921
+ for (const key of keys) {
1922
+ const options = winners.get(key).options;
1923
+ let rule = cache[key];
1924
+ if (!rule._meta.isTrivia) {
1925
+ const meta = rule._meta;
1926
+ const trivia2 = link !== void 0 ? link.trivia : options?.trivia;
1927
+ if (trivia2 != null) meta.grammarTrivia = trivia2;
1928
+ if (options?.scanSkip != null) meta.grammarScanSkip = options.scanSkip;
1929
+ if ((link?.hostMode ?? options?.hostMode) === "cst") meta.grammarHostMode = "cst";
1930
+ if (options?.trackLines === true) {
1931
+ meta.grammarTrackLines = true;
1932
+ if (rule._def.tag !== "grammar") {
1933
+ rule = parser({ trackLines: true }, rule);
1934
+ tagRule(rule, key);
1935
+ rule._meta.grammarTrackLines = true;
1936
+ cache[key] = rule;
1937
+ }
1923
1938
  }
1924
1939
  }
1925
- }
1926
- for (const key of Object.keys(definitions)) {
1927
- markUnusedValues(cache[key]);
1940
+ markUnusedValues(rule);
1941
+ claimedRules.add(winners.get(key).parser).add(rule);
1928
1942
  }
1929
1943
  Object.defineProperty(cache, RULE_ORDER, {
1930
- value: Object.keys(definitions),
1944
+ value: keys,
1931
1945
  enumerable: false,
1932
1946
  configurable: true
1933
1947
  });
1934
- attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
1948
+ attachGrammarReflection(cache, collectGrammarReflection(keys.map((key) => [key, cache[key]])));
1935
1949
  return cache;
1936
1950
  }
1937
1951
 
@@ -15020,7 +15034,8 @@ function children(def, winners) {
15020
15034
  return [];
15021
15035
  }
15022
15036
  const name = resolved._ruleName;
15023
- return name && winners?.[name] ? [winners[name]] : [resolved];
15037
+ const winner = name ? winners?.[name] : void 0;
15038
+ return winner !== void 0 && winner._def !== def ? [winner] : [resolved];
15024
15039
  }
15025
15040
  default:
15026
15041
  return [];
@@ -15045,16 +15060,39 @@ function buildGrammarPlan(entry, winners) {
15045
15060
  }
15046
15061
  }
15047
15062
  }
15063
+ const selfWinnerBody = (parser2) => {
15064
+ if (parser2._def.tag !== "lazy" || winners === void 0) return void 0;
15065
+ const name = winnerNames.get(parser2);
15066
+ if (name === void 0 || winners[name] !== parser2) return void 0;
15067
+ try {
15068
+ return parser2._def.thunk();
15069
+ } catch {
15070
+ return void 0;
15071
+ }
15072
+ };
15073
+ const slotsPerBody = /* @__PURE__ */ new Map();
15074
+ if (winners) {
15075
+ for (const winner of Object.values(winners)) {
15076
+ const body = selfWinnerBody(winner);
15077
+ if (body !== void 0) slotsPerBody.set(body, (slotsPerBody.get(body) ?? 0) + 1);
15078
+ }
15079
+ }
15080
+ const sharedBody = (parser2) => (slotsPerBody.get(parser2) ?? 0) > 1;
15081
+ const ownRule2 = (parser2, rule) => {
15082
+ const id = `rule:${rule}`;
15083
+ definitions.set(id, { id, kind: "rule" });
15084
+ rules2.set(parser2, id);
15085
+ };
15048
15086
  const seen = /* @__PURE__ */ new Set();
15049
15087
  const visit = (parser2, path3) => {
15050
15088
  if (seen.has(parser2)) return;
15051
15089
  seen.add(parser2);
15052
15090
  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);
15091
+ const body = selfWinnerBody(parser2);
15092
+ if (rule && body === void 0 && !sharedBody(parser2) && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
15093
+ ownRule2(parser2, rule);
15057
15094
  }
15095
+ if (rule && body !== void 0 && sharedBody(body)) ownRule2(parser2, rule);
15058
15096
  if (parser2._def.tag === "choice") {
15059
15097
  const ids = parser2._def.parsers.map((_, index) => `choice:${path3}/arm:${index}`);
15060
15098
  choices.set(parser2, ids);
@@ -15075,7 +15113,13 @@ function buildGrammarPlan(entry, winners) {
15075
15113
  };
15076
15114
  const roots = Array.isArray(entry) ? entry : [entry];
15077
15115
  for (const root of roots) {
15078
- visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
15116
+ const name = winnerNames.get(root) ?? root._ruleName ?? "entry";
15117
+ const body = selfWinnerBody(root);
15118
+ if (body !== void 0 && sharedBody(body) && !seen.has(root)) {
15119
+ seen.add(root);
15120
+ ownRule2(root, name);
15121
+ }
15122
+ visit(body ?? root, name);
15079
15123
  }
15080
15124
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
15081
15125
  }
@@ -16381,6 +16425,183 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
16381
16425
  return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16382
16426
  }
16383
16427
 
16428
+ // src/table/compile-rule-map.ts
16429
+ var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
16430
+ function resolveHostMode(ruleMap, explicit) {
16431
+ return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
16432
+ }
16433
+ function resolveTrackLines(ruleMap, explicit) {
16434
+ return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
16435
+ }
16436
+ function withAmbient(ruleMap, opts) {
16437
+ if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
16438
+ const wrappers = /* @__PURE__ */ new Map();
16439
+ return ruleMap.map(([name, rule]) => {
16440
+ if (rule._meta.isTrivia) return [name, rule];
16441
+ const addTrivia = opts.trivia !== void 0 && rule._meta.grammarTrivia === void 0;
16442
+ const addScanSkip = opts.scanSkip !== void 0 && rule._meta.grammarScanSkip === void 0;
16443
+ if (!addTrivia && !addScanSkip) return [name, rule];
16444
+ let wrapped = wrappers.get(rule);
16445
+ if (wrapped === void 0) {
16446
+ wrapped = {
16447
+ ...rule,
16448
+ _meta: {
16449
+ ...rule._meta,
16450
+ ...addTrivia ? { grammarTrivia: opts.trivia } : {},
16451
+ ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
16452
+ }
16453
+ };
16454
+ wrappers.set(rule, wrapped);
16455
+ }
16456
+ return [name, wrapped];
16457
+ });
16458
+ }
16459
+ function compileRuleMapRunnable(ruleMap, opts = {}) {
16460
+ const encoded = encodeForRun(ruleMap, opts);
16461
+ if (encoded === null) return null;
16462
+ const { prog, hostMode, plan } = encoded;
16463
+ const artifact = closureArtifact(prog);
16464
+ const reflection = collectGrammarReflection(ruleMap);
16465
+ return {
16466
+ keys: ruleMap.map(([key]) => key),
16467
+ hostMode,
16468
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
16469
+ reflection,
16470
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16471
+ // Reflection rides on the map's metadata prototype from birth. Only `compile()`
16472
+ // asks to specialise; every other caller runs the closure artifact.
16473
+ rules: tableRules(opts.specialise === true ? prog : artifact, { [GRAMMAR_REFLECTION]: reflection }),
16474
+ prog: artifact
16475
+ };
16476
+ }
16477
+ function encodeForRun(ruleMap, opts) {
16478
+ runDuplicationDiagnosticRules(ruleMap, opts.duplication);
16479
+ const encodeMap = withAmbient(ruleMap, opts);
16480
+ const plan = opts.coverage === true ? buildGrammarPlan(
16481
+ encodeMap.map(([, rule]) => rule),
16482
+ Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== "lazy"))
16483
+ ) : void 0;
16484
+ const hostMode = resolveHostMode(ruleMap, opts.hostMode);
16485
+ const settings = {
16486
+ hostMode,
16487
+ ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
16488
+ ...opts.recovery === true ? { recovery: true } : {},
16489
+ ...plan === void 0 ? {} : { coverage: plan }
16490
+ };
16491
+ try {
16492
+ const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
16493
+ return { prog, fnSrcs, hostMode, plan };
16494
+ } catch (e) {
16495
+ opts.refusals?.push(e instanceof Error ? e.message : String(e));
16496
+ return null;
16497
+ }
16498
+ }
16499
+ function compileRuleMap(ruleMap, opts = {}) {
16500
+ const encoded = encodeForRun(ruleMap, opts);
16501
+ if (encoded === null) return null;
16502
+ const { prog, fnSrcs, hostMode, plan } = encoded;
16503
+ const artifact = closureArtifact(prog);
16504
+ if (artifact.runtimeOnly !== void 0 && artifact.runtimeOnly.length > 0) {
16505
+ opts.refusals?.push(...artifact.runtimeOnly);
16506
+ return null;
16507
+ }
16508
+ const supplied = opts.fnSources;
16509
+ const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null);
16510
+ if (sources.some((s) => s === null)) {
16511
+ const missing = sources.flatMap((s, i) => s === null ? [i] : []);
16512
+ opts.refusals?.push(
16513
+ `author reducer source missing for prog.fns[${missing.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`
16514
+ );
16515
+ return null;
16516
+ }
16517
+ if (supplied !== void 0 && supplied.length > fnSrcs.length) {
16518
+ throw new Error(
16519
+ `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.`
16520
+ );
16521
+ }
16522
+ const replacementWithMetadata = (metadataSource, options = {}) => emitTableExpression(artifact, {
16523
+ entry: null,
16524
+ runtimeRef: opts.runtimeRef ?? "tableRules",
16525
+ fnSources: sources,
16526
+ ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
16527
+ ...metadataSource === void 0 ? {} : { metadataSource }
16528
+ });
16529
+ const replacement = replacementWithMetadata();
16530
+ return {
16531
+ keys: ruleMap.map(([key]) => key),
16532
+ replacement,
16533
+ replacementWithMetadata,
16534
+ hostMode,
16535
+ // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
16536
+ // dropped", which is what the driver's `'ast' artifact + CST host` check keys
16537
+ // off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
16538
+ // direct builder, so there was no branch to drop and it stays usable with either
16539
+ // host — the long-standing `node(parser)` contract. Same predicate codegen uses
16540
+ // for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
16541
+ // lowerings stamp the same artifact rather than disagreeing about it.
16542
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
16543
+ reflection: collectGrammarReflection(ruleMap),
16544
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16545
+ rules: tableRules(artifact),
16546
+ prog: artifact
16547
+ };
16548
+ }
16549
+
16550
+ // src/compiler/borrowed-rules.ts
16551
+ var nameOf = (c) => c._ruleName;
16552
+ function deref(c) {
16553
+ const seen = /* @__PURE__ */ new Set();
16554
+ let current = c;
16555
+ while (current._def.tag === "lazy" && !seen.has(current)) {
16556
+ seen.add(current);
16557
+ try {
16558
+ current = current._def.thunk();
16559
+ } catch {
16560
+ return void 0;
16561
+ }
16562
+ }
16563
+ return current;
16564
+ }
16565
+ function borrowedRuleReference(map) {
16566
+ const seen = /* @__PURE__ */ new Set();
16567
+ for (const [rule, self] of Object.entries(map)) {
16568
+ let found;
16569
+ const visit = (c) => {
16570
+ if (found !== void 0) return;
16571
+ if (c._def.tag === "lazy") {
16572
+ if (!winnerWrapsReference(self, c)) {
16573
+ const name = nameOf(c);
16574
+ const winner = name !== void 0 && Object.hasOwn(map, name) ? map[name] : void 0;
16575
+ if (winner !== void 0) {
16576
+ if (winnerWrapsReference(winner, c) || deref(c) === deref(winner)) return;
16577
+ found = name;
16578
+ return;
16579
+ }
16580
+ }
16581
+ if (seen.has(c)) return;
16582
+ seen.add(c);
16583
+ let body;
16584
+ try {
16585
+ body = c._def.thunk();
16586
+ } catch {
16587
+ return;
16588
+ }
16589
+ visit(body);
16590
+ return;
16591
+ }
16592
+ if (seen.has(c)) return;
16593
+ seen.add(c);
16594
+ for (const child of childrenOf2(c._def)) visit(child);
16595
+ };
16596
+ visit(self);
16597
+ if (found !== void 0) return { rule, reference: found };
16598
+ }
16599
+ return void 0;
16600
+ }
16601
+ function borrowedRuleMessage(api, hit) {
16602
+ 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.`;
16603
+ }
16604
+
16384
16605
  // src/table/compile.ts
16385
16606
  var ENTRY = "Entry";
16386
16607
  function ownSource(fn) {
@@ -16390,7 +16611,47 @@ function ownSource(fn) {
16390
16611
  const name = fn.name;
16391
16612
  return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
16392
16613
  }
16393
- function compile(combinator, mapFnSources, opts = {}) {
16614
+ function compile(target, second, third) {
16615
+ if (!isCombinator(target)) return compileMap(target, second);
16616
+ return compileRoot(target, second, third);
16617
+ }
16618
+ function isCombinator(x) {
16619
+ return typeof x === "object" && x !== null && typeof x.parse === "function" && "_def" in x;
16620
+ }
16621
+ function compileMap(grammar, opts = {}) {
16622
+ const entries = [];
16623
+ for (const [key, value] of Object.entries(grammar)) {
16624
+ if (!isCombinator(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
16625
+ if (value._def.tag === "lazy") {
16626
+ try {
16627
+ value._def.thunk();
16628
+ } catch {
16629
+ continue;
16630
+ }
16631
+ }
16632
+ entries.push([key, value]);
16633
+ }
16634
+ const borrowed = borrowedRuleReference(Object.fromEntries(entries));
16635
+ if (borrowed !== void 0) throw new Error(borrowedRuleMessage("compile", borrowed));
16636
+ const drain = beginCompileDegradationDrain();
16637
+ let done = false;
16638
+ try {
16639
+ const refusals = [];
16640
+ const compiled = compileRuleMapRunnable(entries, {
16641
+ ...opts.hostMode ? { hostMode: opts.hostMode } : {},
16642
+ refusals,
16643
+ specialise: true
16644
+ });
16645
+ if (compiled === null) {
16646
+ throw new Error(`compile: this grammar could not be encoded to a table${refusals.length ? ` \u2014 ${refusals.join("; ")}` : ""}`);
16647
+ }
16648
+ done = true;
16649
+ return compiled.rules;
16650
+ } finally {
16651
+ drain(done);
16652
+ }
16653
+ }
16654
+ function compileRoot(combinator, mapFnSources, opts = {}) {
16394
16655
  runDuplicationDiagnostic(combinator, opts.duplication);
16395
16656
  const drain = beginCompileDegradationDrain();
16396
16657
  let compiled = false;
@@ -16460,29 +16721,6 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
16460
16721
  };
16461
16722
  }
16462
16723
 
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
16469
- };
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;
16477
- try {
16478
- return combinator.parse(input, pos, ctx);
16479
- } finally {
16480
- ctx.state = saved;
16481
- }
16482
- }
16483
- };
16484
- }
16485
-
16486
16724
  // src/compiler/ir-serialize.ts
16487
16725
  function childrenOf3(def) {
16488
16726
  switch (def.tag) {
@@ -16579,195 +16817,28 @@ function serializeRuleMap(ruleMap, scanSkip) {
16579
16817
  throw e;
16580
16818
  }
16581
16819
  }
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);
16751
- }
16752
- var Serializer = class {
16753
- counts = /* @__PURE__ */ new Map();
16754
- selfRef = /* @__PURE__ */ new Set();
16755
- // combinators reached by an unnamed lazy pointing back into their own subtree
16756
- cycleInterior = /* @__PURE__ */ new Set();
16757
- // combinators lying ON a self-reference cycle (strictly below its target)
16758
- constName = /* @__PURE__ */ new Map();
16759
- emitted = /* @__PURE__ */ new Set();
16760
- decls = [];
16761
- ruleValues = /* @__PURE__ */ new Set();
16762
- localRuleNames = /* @__PURE__ */ new Map();
16763
- ruleMap;
16764
- scanSkip;
16765
- constructor(ruleMap, scanSkip = []) {
16766
- this.ruleMap = ruleMap;
16767
- this.scanSkip = scanSkip;
16768
- for (const [name, c] of ruleMap) {
16769
- this.localRuleNames.set(c, name);
16770
- this.ruleValues.add(this.body(c));
16820
+ function serializeCombinator(c) {
16821
+ return serializeRuleMap([], [c]);
16822
+ }
16823
+ var Serializer = class {
16824
+ counts = /* @__PURE__ */ new Map();
16825
+ selfRef = /* @__PURE__ */ new Set();
16826
+ // combinators reached by an unnamed lazy pointing back into their own subtree
16827
+ cycleInterior = /* @__PURE__ */ new Set();
16828
+ // combinators lying ON a self-reference cycle (strictly below its target)
16829
+ constName = /* @__PURE__ */ new Map();
16830
+ emitted = /* @__PURE__ */ new Set();
16831
+ decls = [];
16832
+ ruleValues = /* @__PURE__ */ new Set();
16833
+ localRuleNames = /* @__PURE__ */ new Map();
16834
+ ruleMap;
16835
+ scanSkip;
16836
+ constructor(ruleMap, scanSkip = []) {
16837
+ this.ruleMap = ruleMap;
16838
+ this.scanSkip = scanSkip;
16839
+ for (const [name, c] of ruleMap) {
16840
+ this.localRuleNames.set(c, name);
16841
+ this.ruleValues.add(this.body(c));
16771
16842
  }
16772
16843
  }
16773
16844
  /** A grammar-level `scanSkip` unit is an opaque TERMINAL (a string, a balanced
@@ -17075,127 +17146,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17075
17146
  }
17076
17147
  };
17077
17148
 
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
- };
17195
- }
17196
-
17197
17149
  // src/version.ts
17198
- var PARSEMAN_VERSION = "0.52.0";
17150
+ var PARSEMAN_VERSION = "0.53.0";
17199
17151
 
17200
17152
  // src/compiler/compile-linkable-table.ts
17201
17153
  function isLocal(rule) {
@@ -17307,6 +17259,29 @@ function gate(predicate) {
17307
17259
  };
17308
17260
  }
17309
17261
 
17262
+ // src/combinators/withCtx.ts
17263
+ function withCtx(extra, combinator) {
17264
+ const meta = {
17265
+ firstSet: combinator._meta.firstSet,
17266
+ canMatchNewline: combinator._meta.canMatchNewline,
17267
+ isTrivia: false
17268
+ };
17269
+ return {
17270
+ _tag: "withCtx",
17271
+ _meta: meta,
17272
+ _def: { tag: "withCtx", extra, parser: combinator },
17273
+ parse(input, pos, ctx) {
17274
+ const saved = ctx.state;
17275
+ ctx.state = extra;
17276
+ try {
17277
+ return combinator.parse(input, pos, ctx);
17278
+ } finally {
17279
+ ctx.state = saved;
17280
+ }
17281
+ }
17282
+ };
17283
+ }
17284
+
17310
17285
  // src/plugin/direct-builder-static.ts
17311
17286
  import { parseSync } from "oxc-parser";
17312
17287
  var STATIC_BUILDER_GLOBALS = /* @__PURE__ */ new Set([
@@ -17592,6 +17567,41 @@ var _builderImports = null;
17592
17567
  function setBuilderImportResolver(r) {
17593
17568
  _builderImports = r;
17594
17569
  }
17570
+ var ModuleScope = class _ModuleScope extends Map {
17571
+ tried = /* @__PURE__ */ new Map();
17572
+ resolveFree;
17573
+ unresolved;
17574
+ constructor(resolveFree, unresolved) {
17575
+ super();
17576
+ this.resolveFree = resolveFree;
17577
+ this.unresolved = unresolved;
17578
+ }
17579
+ has(name) {
17580
+ return super.has(name) || this.resolve(name);
17581
+ }
17582
+ get(name) {
17583
+ if (super.has(name) || this.resolve(name)) return super.get(name);
17584
+ const why = this.tried.get(name);
17585
+ if (why !== void 0) this.unresolved.add(`\`${name}\` ${why}`);
17586
+ return void 0;
17587
+ }
17588
+ /** A child scope (a factory body) that keeps resolving this module's free names. */
17589
+ fork() {
17590
+ const child = new _ModuleScope(this.resolveFree, this.unresolved);
17591
+ for (const [k, v] of super.entries()) child.set(k, v);
17592
+ return child;
17593
+ }
17594
+ resolve(name) {
17595
+ if (this.tried.has(name)) return false;
17596
+ const r = this.resolveFree(name);
17597
+ if (r !== null && "value" in r) {
17598
+ super.set(name, r.value);
17599
+ return true;
17600
+ }
17601
+ if (r !== null) this.tried.set(name, r.unresolved);
17602
+ return false;
17603
+ }
17604
+ };
17595
17605
  function isWordFactory(v) {
17596
17606
  return !!v && typeof v === "object" && v.tag === "wordFactory";
17597
17607
  }
@@ -17715,7 +17725,7 @@ function dispatchArmValue(node2, scope, code, mfs) {
17715
17725
  if (!keyArg || !parserArg || extraArg !== void 0 || keyArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
17716
17726
  const key = anyValue(keyArg, scope, code, mfs);
17717
17727
  const parserValue = anyValue(parserArg, scope, code, mfs);
17718
- if (!isCombinator(parserValue)) return null;
17728
+ if (!isCombinator2(parserValue)) return null;
17719
17729
  const opts = { caseInsensitive: factory.caseInsensitive };
17720
17730
  if (typeof key === "string") return when(key, parserValue, opts);
17721
17731
  if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue, opts);
@@ -17726,7 +17736,7 @@ function dispatchArmValue(node2, scope, code, mfs) {
17726
17736
  const [parserArg] = node2.arguments;
17727
17737
  if (!parserArg || parserArg.type === "SpreadElement") return null;
17728
17738
  const parserValue = anyValue(parserArg, scope, code, mfs);
17729
- if (!isCombinator(parserValue)) return null;
17739
+ if (!isCombinator2(parserValue)) return null;
17730
17740
  return otherwise(parserValue);
17731
17741
  }
17732
17742
  if (callee.name === "when") {
@@ -17736,24 +17746,24 @@ function dispatchArmValue(node2, scope, code, mfs) {
17736
17746
  const parserValue = anyValue(parserArg, scope, code, mfs);
17737
17747
  const opts = dispatchWhenOptions(optsArg === void 0 ? void 0 : anyValue(optsArg, scope, code, mfs));
17738
17748
  if (opts === null) return null;
17739
- if (!isCombinator(parserValue)) return null;
17749
+ if (!isCombinator2(parserValue)) return null;
17740
17750
  if (typeof key === "string") return when(key, parserValue, opts);
17741
17751
  if (Array.isArray(key) && key.every((item) => typeof item === "string")) return when(key, parserValue, opts);
17742
17752
  if (isDispatchMatcher(key)) return when(key, parserValue, opts);
17743
17753
  }
17744
17754
  return null;
17745
17755
  }
17746
- function isCombinator(v) {
17756
+ function isCombinator2(v) {
17747
17757
  return !!v && typeof v === "object" && "_def" in v;
17748
17758
  }
17749
17759
  function isDispatchArm(v) {
17750
17760
  if (!v || typeof v !== "object") return false;
17751
17761
  const rec = v;
17752
- if (rec.kind === "otherwise") return isCombinator(rec.parser);
17762
+ if (rec.kind === "otherwise") return isCombinator2(rec.parser);
17753
17763
  if (rec.kind === "whenMatcher") {
17754
- return isDispatchMatcher(rec.matcher) && typeof rec.caseInsensitive === "boolean" && isCombinator(rec.parser);
17764
+ return isDispatchMatcher(rec.matcher) && typeof rec.caseInsensitive === "boolean" && isCombinator2(rec.parser);
17755
17765
  }
17756
- return rec.kind === "when" && Array.isArray(rec.keys) && rec.keys.every((key) => typeof key === "string") && typeof rec.caseInsensitive === "boolean" && isCombinator(rec.parser);
17766
+ return rec.kind === "when" && Array.isArray(rec.keys) && rec.keys.every((key) => typeof key === "string") && typeof rec.caseInsensitive === "boolean" && isCombinator2(rec.parser);
17757
17767
  }
17758
17768
  function isDispatchMatcher(v) {
17759
17769
  if (!v || typeof v !== "object") return false;
@@ -17768,7 +17778,7 @@ function scopeGet(scope, name, mfs) {
17768
17778
  if (mfs && entry.mfSrcs.length > 0) mfs.push(...entry.mfSrcs);
17769
17779
  return entry.combi;
17770
17780
  }
17771
- if (isCombinator(entry)) return entry;
17781
+ if (isCombinator2(entry)) return entry;
17772
17782
  return null;
17773
17783
  }
17774
17784
  function unwrapStaticExpr(expr) {
@@ -17820,7 +17830,7 @@ function scopedStaticValue(expr, scope) {
17820
17830
  return { found: true, value };
17821
17831
  }
17822
17832
  function staticNodeOptionsFromValue(value) {
17823
- if (value === null || typeof value !== "object" || Array.isArray(value) || isCombinator(value)) return STATIC_NODE_OPTIONS_NOT_OPTIONS;
17833
+ if (value === null || typeof value !== "object" || Array.isArray(value) || isCombinator2(value)) return STATIC_NODE_OPTIONS_NOT_OPTIONS;
17824
17834
  const opts = {};
17825
17835
  const rec = value;
17826
17836
  for (const name of Object.keys(rec)) {
@@ -17872,6 +17882,13 @@ function staticNodeOptions(expr, scope) {
17872
17882
  }
17873
17883
  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
17884
  }
17885
+ var UNKNOWN_OPTIONS = /* @__PURE__ */ Symbol("parseman.unknownOptions");
17886
+ function scanOptions(optsArg, scope, code) {
17887
+ if (optsArg === void 0) return void 0;
17888
+ if (optsArg.type === "SpreadElement") return UNKNOWN_OPTIONS;
17889
+ const opts = anyValue(optsArg, scope, code, []);
17890
+ return isHole(optsArg, opts, scope) ? UNKNOWN_OPTIONS : opts;
17891
+ }
17875
17892
  function exprToCombi(node2, scope, code, mfs) {
17876
17893
  if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
17877
17894
  if (node2.type !== "CallExpression") return null;
@@ -17907,7 +17924,7 @@ function exprToCombi(node2, scope, code, mfs) {
17907
17924
  const [parserArg, fnArg] = node2.arguments;
17908
17925
  if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
17909
17926
  const inner = anyValue(parserArg, scope, code, mfs);
17910
- if (!isCombinator(inner)) return null;
17927
+ if (!isCombinator2(inner)) return null;
17911
17928
  const fnSrc = stripTsFromSource(fnArg, code);
17912
17929
  mfs.push(fnSrc);
17913
17930
  try {
@@ -17922,7 +17939,7 @@ function exprToCombi(node2, scope, code, mfs) {
17922
17939
  const [parserArg, fnArg] = node2.arguments;
17923
17940
  if (!parserArg || !fnArg || parserArg.type === "SpreadElement" || fnArg.type === "SpreadElement") return null;
17924
17941
  const inner = anyValue(parserArg, scope, code, mfs);
17925
- if (!isCombinator(inner)) return null;
17942
+ if (!isCombinator2(inner)) return null;
17926
17943
  const fnSrc = stripTsFromSource(fnArg, code);
17927
17944
  mfs.push(fnSrc);
17928
17945
  try {
@@ -17943,7 +17960,7 @@ function exprToCombi(node2, scope, code, mfs) {
17943
17960
  const optsArg = explicitType !== void 0 ? fourthArg : thirdArg;
17944
17961
  if (!parserArg || parserArg.type === "SpreadElement") return null;
17945
17962
  const inner = anyValue(parserArg, scope, code, mfs);
17946
- if (!isCombinator(inner)) return null;
17963
+ if (!isCombinator2(inner)) return null;
17947
17964
  const be = buildArg;
17948
17965
  const buildExpr = be === void 0 || be.type === "SpreadElement" ? void 0 : unwrapStaticExpr(be);
17949
17966
  const buildArgOptions = be !== void 0 && be.type !== "SpreadElement" ? staticNodeOptions(buildArg, scope) : STATIC_NODE_OPTIONS_NOT_OPTIONS;
@@ -18017,7 +18034,7 @@ function exprToCombi(node2, scope, code, mfs) {
18017
18034
  const opts = anyValue(optsArg, scope, code, []);
18018
18035
  if (!opts || typeof opts !== "object") return null;
18019
18036
  const root = anyValue(rootArg, scope, code, mfs);
18020
- if (!isCombinator(root)) return null;
18037
+ if (!isCombinator2(root)) return null;
18021
18038
  try {
18022
18039
  return parser(opts, root);
18023
18040
  } catch {
@@ -18029,10 +18046,10 @@ function exprToCombi(node2, scope, code, mfs) {
18029
18046
  if (!itemArg || !sepArg || itemArg.type === "SpreadElement" || sepArg.type === "SpreadElement") return null;
18030
18047
  const itemMfs = [];
18031
18048
  const itemCombi = anyValue(itemArg, scope, code, itemMfs);
18032
- if (!isCombinator(itemCombi)) return null;
18049
+ if (!isCombinator2(itemCombi)) return null;
18033
18050
  const sepMfs = [];
18034
18051
  const sepCombi = anyValue(sepArg, scope, code, sepMfs);
18035
- if (!isCombinator(sepCombi)) return null;
18052
+ if (!isCombinator2(sepCombi)) return null;
18036
18053
  let opts;
18037
18054
  if (optsArg) {
18038
18055
  if (optsArg.type === "SpreadElement") return null;
@@ -18052,7 +18069,7 @@ function exprToCombi(node2, scope, code, mfs) {
18052
18069
  if (!itemArg || itemArg.type === "SpreadElement") return null;
18053
18070
  const itemMfs = [];
18054
18071
  const itemCombi = anyValue(itemArg, scope, code, itemMfs);
18055
- if (!isCombinator(itemCombi)) return null;
18072
+ if (!isCombinator2(itemCombi)) return null;
18056
18073
  let opts;
18057
18074
  if (optsArg) {
18058
18075
  if (optsArg.type === "SpreadElement") return null;
@@ -18074,7 +18091,7 @@ function exprToCombi(node2, scope, code, mfs) {
18074
18091
  const [innerArg] = node2.arguments;
18075
18092
  if (!innerArg || innerArg.type === "SpreadElement") return null;
18076
18093
  const inner = anyValue(innerArg, scope, code, mfs);
18077
- if (!isCombinator(inner)) return null;
18094
+ if (!isCombinator2(inner)) return null;
18078
18095
  try {
18079
18096
  return not(inner);
18080
18097
  } catch {
@@ -18085,7 +18102,7 @@ function exprToCombi(node2, scope, code, mfs) {
18085
18102
  const [innerArg] = node2.arguments;
18086
18103
  if (!innerArg || innerArg.type === "SpreadElement") return null;
18087
18104
  const inner = anyValue(innerArg, scope, code, mfs);
18088
- if (!isCombinator(inner)) return null;
18105
+ if (!isCombinator2(inner)) return null;
18089
18106
  try {
18090
18107
  return peek(inner);
18091
18108
  } catch {
@@ -18098,7 +18115,8 @@ function exprToCombi(node2, scope, code, mfs) {
18098
18115
  const open = anyValue(openArg, scope, code, []);
18099
18116
  const close = anyValue(closeArg, scope, code, []);
18100
18117
  if (typeof open !== "string" || typeof close !== "string") return null;
18101
- const opts = optsArg && optsArg.type !== "SpreadElement" ? anyValue(optsArg, scope, code, []) : void 0;
18118
+ const opts = scanOptions(optsArg, scope, code);
18119
+ if (opts === UNKNOWN_OPTIONS) return null;
18102
18120
  try {
18103
18121
  return balanced(open, close, opts);
18104
18122
  } catch {
@@ -18109,8 +18127,9 @@ function exprToCombi(node2, scope, code, mfs) {
18109
18127
  const [sentinelArg, optsArg] = node2.arguments;
18110
18128
  if (!sentinelArg || sentinelArg.type === "SpreadElement") return null;
18111
18129
  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;
18130
+ if (!isCombinator2(sentinel)) return null;
18131
+ const opts = scanOptions(optsArg, scope, code);
18132
+ if (opts === UNKNOWN_OPTIONS) return null;
18114
18133
  try {
18115
18134
  return scanTo(sentinel, opts);
18116
18135
  } catch {
@@ -18139,7 +18158,7 @@ function exprToCombi(node2, scope, code, mfs) {
18139
18158
  const extraSrc = stripTsFromSource(extraArg, code);
18140
18159
  mfs.push(extraSrc);
18141
18160
  const inner = anyValue(innerArg, scope, code, mfs);
18142
- if (!isCombinator(inner)) return null;
18161
+ if (!isCombinator2(inner)) return null;
18143
18162
  try {
18144
18163
  const evaluated = anyValue(extraArg, scope, code, []);
18145
18164
  const usable = typeof evaluated === "object" && evaluated !== null && !Array.isArray(evaluated) && Object.getPrototypeOf(evaluated) === Object.prototype;
@@ -18155,7 +18174,7 @@ function exprToCombi(node2, scope, code, mfs) {
18155
18174
  const [selectorArg, ...armArgs] = node2.arguments;
18156
18175
  if (!selectorArg || selectorArg.type === "SpreadElement") return null;
18157
18176
  const selector = anyValue(selectorArg, scope, code, mfs);
18158
- if (!isCombinator(selector)) return null;
18177
+ if (!isCombinator2(selector)) return null;
18159
18178
  const arms = [];
18160
18179
  for (const arg of armArgs) {
18161
18180
  if (arg.type === "SpreadElement") return null;
@@ -18182,12 +18201,12 @@ function exprToCombi(node2, scope, code, mfs) {
18182
18201
  const gateSrc = stripTsFromSource(parts.gate, code);
18183
18202
  mfs.push(gateSrc);
18184
18203
  const combi = anyValue(parts.combinator, scope, code, mfs);
18185
- if (!isCombinator(combi)) return null;
18204
+ if (!isCombinator2(combi)) return null;
18186
18205
  arms.push({ gate: () => true, combinator: combi });
18187
18206
  gateSrcs.push(gateSrc);
18188
18207
  } else {
18189
18208
  const combi = anyValue(argNode, scope, code, mfs);
18190
- if (!isCombinator(combi)) return null;
18209
+ if (!isCombinator2(combi)) return null;
18191
18210
  arms.push(combi);
18192
18211
  gateSrcs.push(null);
18193
18212
  }
@@ -18217,6 +18236,12 @@ function exprToCombi(node2, scope, code, mfs) {
18217
18236
  return null;
18218
18237
  }
18219
18238
  }
18239
+ function isHole(node2, value, scope) {
18240
+ if (value !== null) return false;
18241
+ const n = unwrapStaticExpr(node2);
18242
+ if (n.type === "Identifier") return !scope.has(n.name);
18243
+ return !(n.type === "Literal" && n.value === null) && n.type !== "ArrowFunctionExpression" && n.type !== "FunctionExpression";
18244
+ }
18220
18245
  function anyValue(node2, scope, code, mfs) {
18221
18246
  if (node2.type === "TSAsExpression" || node2.type === "TSSatisfiesExpression" || node2.type === "TSNonNullExpression" || node2.type === "TSTypeAssertion" || node2.type === "TSInstantiationExpression" || node2.type === "ParenthesizedExpression") {
18222
18247
  const inner = node2.expression;
@@ -18237,7 +18262,9 @@ function anyValue(node2, scope, code, mfs) {
18237
18262
  continue;
18238
18263
  }
18239
18264
  if (el.type === "SpreadElement") return null;
18240
- out.push(anyValue(el, scope, code, mfs));
18265
+ const v = anyValue(el, scope, code, mfs);
18266
+ if (isHole(el, v, scope)) return null;
18267
+ out.push(v);
18241
18268
  }
18242
18269
  return out;
18243
18270
  }
@@ -18246,7 +18273,10 @@ function anyValue(node2, scope, code, mfs) {
18246
18273
  for (const prop of node2.properties) {
18247
18274
  const key = propName(prop);
18248
18275
  if (key === null) return null;
18249
- obj[key] = anyValue(prop.value, scope, code, mfs);
18276
+ const valueNode = prop.value;
18277
+ const v = anyValue(valueNode, scope, code, mfs);
18278
+ if (isHole(valueNode, v, scope)) return null;
18279
+ obj[key] = v;
18250
18280
  }
18251
18281
  return obj;
18252
18282
  }
@@ -18322,10 +18352,9 @@ function evaluateExpr(node2, scope, code, mapFnSources) {
18322
18352
  return exprToCombi(node2, scope, code, mapFnSources);
18323
18353
  }
18324
18354
  function evaluateCombinatorArray(node2, scope, code) {
18325
- if (node2.type !== "ArrayExpression") return null;
18326
18355
  const val = anyValue(node2, scope, code, []);
18327
18356
  if (!Array.isArray(val) || val.length === 0) return null;
18328
- if (!val.every(isCombinator)) return null;
18357
+ if (!val.every(isCombinator2)) return null;
18329
18358
  return val;
18330
18359
  }
18331
18360
  function collectRuleKeys(retObj) {
@@ -18403,7 +18432,7 @@ function evalBodyStatements(statements, scope, code, out) {
18403
18432
  return false;
18404
18433
  }
18405
18434
  const thisDeclMfSrcs = bodyMfs.slice(before);
18406
- if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
18435
+ if (isCombinator2(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
18407
18436
  else if (isDispatchArm(val)) scope.set(name, { value: val, mfSrcs: thisDeclMfSrcs });
18408
18437
  else scope.set(name, val);
18409
18438
  }
@@ -18448,7 +18477,7 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, out, opti
18448
18477
  let built;
18449
18478
  try {
18450
18479
  built = rules(options ?? {}, (g) => {
18451
- const localScope = new Map(scope);
18480
+ const localScope = scope instanceof ModuleScope ? scope.fork() : new Map(scope);
18452
18481
  localScope.set(proxyName, g);
18453
18482
  if (!evalBodyStatements(statements, localScope, code, out)) throw ABORT;
18454
18483
  const entries = flattenRuleEntries(retObj, localScope, code);
@@ -18458,7 +18487,7 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, out, opti
18458
18487
  const definitions = {};
18459
18488
  for (const [key, e] of finalByKey) {
18460
18489
  const val = anyValue(e.value, e.scope, e.code, mapFnSources);
18461
- if (!isCombinator(val)) throw ABORT;
18490
+ if (!isCombinator2(val)) throw ABORT;
18462
18491
  definitions[key] = val;
18463
18492
  }
18464
18493
  return definitions;
@@ -18473,7 +18502,7 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, out, opti
18473
18502
  return map;
18474
18503
  }
18475
18504
  function isDefinableRef2(v) {
18476
- return isCombinator(v) && typeof v.define === "function" && v._def.tag === "lazy";
18505
+ return isCombinator2(v) && typeof v.define === "function" && v._def.tag === "lazy";
18477
18506
  }
18478
18507
  function evaluateRefDeclaration(init, name, scope) {
18479
18508
  if (init.type !== "CallExpression") return null;
@@ -18493,12 +18522,12 @@ function applyDefineStatement(callExpr, scope, code) {
18493
18522
  if (mem.property.type !== "Identifier" || mem.property.name !== "define") return false;
18494
18523
  if (mem.object.type !== "Identifier" || !mem.object.name) return false;
18495
18524
  const target = scope.get(mem.object.name);
18496
- const refCombi = isScopeEntry(target) ? target.combi : isCombinator(target) ? target : null;
18525
+ const refCombi = isScopeEntry(target) ? target.combi : isCombinator2(target) ? target : null;
18497
18526
  if (!refCombi || !isDefinableRef2(refCombi)) return false;
18498
18527
  const args = callExpr.arguments;
18499
18528
  if (args.length !== 1 || args[0].type === "SpreadElement") return false;
18500
18529
  const inner = anyValue(args[0], scope, code, []);
18501
- if (!isCombinator(inner)) return false;
18530
+ if (!isCombinator2(inner)) return false;
18502
18531
  try {
18503
18532
  refCombi.define(inner);
18504
18533
  } catch {
@@ -18508,7 +18537,7 @@ function applyDefineStatement(callExpr, scope, code) {
18508
18537
  }
18509
18538
  function referencesAny(node2, names, scope) {
18510
18539
  if (node2.type === "Identifier") {
18511
- return names.has(node2.name) || scope.has(node2.name);
18540
+ return names.has(node2.name) || Map.prototype.has.call(scope, node2.name);
18512
18541
  }
18513
18542
  for (const key of Object.keys(node2)) {
18514
18543
  const child = node2[key];
@@ -18569,7 +18598,9 @@ function patternNames(pat, out) {
18569
18598
  }
18570
18599
  }
18571
18600
  function declare(scope, name, b) {
18572
- if (!scope.bindings.has(name)) scope.bindings.set(name, b);
18601
+ const prev = scope.bindings.get(name);
18602
+ if (prev) prev.reassigned = true;
18603
+ else scope.bindings.set(name, b);
18573
18604
  }
18574
18605
  function functionScope(scope) {
18575
18606
  let s = scope;
@@ -18669,7 +18700,8 @@ function markReassignments(body, root) {
18669
18700
  }
18670
18701
  const node2 = n;
18671
18702
  if (typeof node2.type !== "string") return;
18672
- if (node2.type === "AssignmentExpression" || node2.type === "UpdateExpression") {
18703
+ const loopWrite = (node2.type === "ForOfStatement" || node2.type === "ForInStatement") && node2.left?.type !== "VariableDeclaration";
18704
+ if (node2.type === "AssignmentExpression" || node2.type === "UpdateExpression" || loopWrite) {
18673
18705
  const target = node2.type === "UpdateExpression" ? node2.argument : node2.left;
18674
18706
  const names = [];
18675
18707
  if (target) patternNames(target, names);
@@ -18685,6 +18717,11 @@ function markReassignments(body, root) {
18685
18717
  };
18686
18718
  for (const stmt of body) walk(stmt);
18687
18719
  }
18720
+ function topLevelIsStable(body, moduleEnd) {
18721
+ const root = buildScopeTree(body, moduleEnd);
18722
+ markReassignments(body, root);
18723
+ return (name) => root.bindings.get(name)?.reassigned !== true;
18724
+ }
18688
18725
  function functionArity(fn) {
18689
18726
  const params = fn.params ?? [];
18690
18727
  let count = 0;
@@ -19137,7 +19174,7 @@ function ast(value) {
19137
19174
  function nodes(value) {
19138
19175
  return Array.isArray(value) ? value.filter((v) => ast(v) !== void 0) : [];
19139
19176
  }
19140
- function nameOf(node2) {
19177
+ function nameOf2(node2) {
19141
19178
  return node2?.type === "Identifier" && typeof node2.name === "string" ? node2.name : void 0;
19142
19179
  }
19143
19180
  function range(node2) {
@@ -19170,7 +19207,7 @@ function isDirectCallUse(call, parent, statement) {
19170
19207
  return declarations.length === 1 && declarations[0] === parent && parent.init === call;
19171
19208
  }
19172
19209
  if (parent.type === "AssignmentExpression") {
19173
- return statement.type === "ExpressionStatement" && statement.expression === parent && nameOf(ast(parent.left)) !== void 0 && parent.right === call && parent.operator === "=";
19210
+ return statement.type === "ExpressionStatement" && statement.expression === parent && nameOf2(ast(parent.left)) !== void 0 && parent.right === call && parent.operator === "=";
19174
19211
  }
19175
19212
  if (parent.type === "ReturnStatement") return statement === parent && parent.argument === call;
19176
19213
  return false;
@@ -19188,7 +19225,7 @@ function hasInlineScopeHazard(fn) {
19188
19225
  if (node2.type === "Identifier" && node2.name === "arguments") hazard = true;
19189
19226
  else if (node2.type === "ThisExpression") hazard = true;
19190
19227
  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;
19228
+ else if (node2.type === "MetaProperty" && nameOf2(ast(node2.meta)) === "new" && nameOf2(ast(node2.property)) === "target") hazard = true;
19192
19229
  else for (const child of children2(node2)) walk(child);
19193
19230
  };
19194
19231
  walk(fn.body);
@@ -19196,7 +19233,7 @@ function hasInlineScopeHazard(fn) {
19196
19233
  }
19197
19234
  function hasInlineSignature(fn) {
19198
19235
  const params = nodes(fn.node.params);
19199
- return params.length === 3 && nameOf(params[0]) === "input" && nameOf(params[1]) === "pos" && nameOf(params[2]) === "ctx";
19236
+ return params.length === 3 && nameOf2(params[0]) === "input" && nameOf2(params[1]) === "pos" && nameOf2(params[2]) === "ctx";
19200
19237
  }
19201
19238
  function render2(source, start, end, edits) {
19202
19239
  const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
@@ -19289,7 +19326,7 @@ function supercompileRuleMapReplacement(prog, replacement) {
19289
19326
  const call = ast(declarator?.init);
19290
19327
  const table = nodes(call?.arguments)[0];
19291
19328
  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);
19329
+ const property = (object, key) => nodes(object.properties).find((prop) => prop.type === "Property" && nameOf2(ast(prop.key)) === key);
19293
19330
  const assembliesProp = property(table, "a");
19294
19331
  const assemblies = nodes(ast(assembliesProp?.value)?.elements);
19295
19332
  if (assembliesProp === void 0 || assemblies.length !== 1 || assemblies[0]?.type !== "ObjectExpression") return replacement;
@@ -19322,7 +19359,7 @@ function supercompileEmittedAssembly(prog, _cfg, extraIps, emitted) {
19322
19359
  const fns = /* @__PURE__ */ new Map();
19323
19360
  for (const statement of nodes(wrapperBody.body)) {
19324
19361
  if (statement.type !== "FunctionDeclaration") continue;
19325
- const name = nameOf(ast(statement.id));
19362
+ const name = nameOf2(ast(statement.id));
19326
19363
  const hit = name === void 0 ? void 0 : PIECE.exec(name);
19327
19364
  const body = ast(statement.body);
19328
19365
  if (hit !== void 0 && hit !== null && body?.type === "BlockStatement") {
@@ -19335,7 +19372,7 @@ function supercompileEmittedAssembly(prog, _cfg, extraIps, emitted) {
19335
19372
  const allCalls = [];
19336
19373
  const walk = (node2, current, parent, statement, statementParent) => {
19337
19374
  if (isFunction(node2) && node2 !== current?.node) {
19338
- const own = node2.type === "FunctionDeclaration" ? nameOf(ast(node2.id)) : void 0;
19375
+ const own = node2.type === "FunctionDeclaration" ? nameOf2(ast(node2.id)) : void 0;
19339
19376
  const hit = own === void 0 ? void 0 : PIECE.exec(own);
19340
19377
  if (hit !== void 0 && hit !== null) {
19341
19378
  const fn = fns.get(Number(hit[1]));
@@ -19359,12 +19396,12 @@ function supercompileEmittedAssembly(prog, _cfg, extraIps, emitted) {
19359
19396
  ownerParent = parent;
19360
19397
  }
19361
19398
  if (node2.type === "CallExpression") {
19362
- const calleeName = nameOf(ast(node2.callee));
19399
+ const calleeName = nameOf2(ast(node2.callee));
19363
19400
  const hit = calleeName === void 0 ? void 0 : PIECE.exec(calleeName);
19364
19401
  if (hit !== void 0 && hit !== null) {
19365
19402
  const callee = Number(hit[1]);
19366
19403
  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);
19404
+ 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
19405
  if (current === void 0 || owner2 === void 0) externalRefs.add(callee);
19369
19406
  else {
19370
19407
  const call = { caller: current.ip, callee, node: node2, statement: owner2, args, transformable };
@@ -19539,7 +19576,7 @@ ${nested}
19539
19576
  const referenced = /* @__PURE__ */ new Set();
19540
19577
  const checkRefs = (node2, parent) => {
19541
19578
  if (node2.type === "FunctionDeclaration") {
19542
- const name = nameOf(ast(node2.id));
19579
+ const name = nameOf2(ast(node2.id));
19543
19580
  if (name !== void 0 && PIECE.test(name)) declared.add(name);
19544
19581
  }
19545
19582
  if (node2.type === "Identifier" && typeof node2.name === "string" && PIECE.test(node2.name)) {
@@ -19548,32 +19585,210 @@ ${nested}
19548
19585
  }
19549
19586
  for (const child of children2(node2)) checkRefs(child, node2);
19550
19587
  };
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
19588
+ checkRefs(checked.program);
19589
+ if (declared.size !== functionsAfter || [...referenced].some((name) => !declared.has(name))) {
19590
+ return fallback(emitted, "deleted piece body remains reachable after structured relinking", before);
19591
+ }
19592
+ const largestRegion = [...regionSizes].sort((a, b) => b[1] - a[1])[0];
19593
+ const protocolRegions = {
19594
+ node: [...regionProtocol.values()].filter((bits) => (bits & 1) !== 0).length,
19595
+ repv: [...regionProtocol.values()].filter((bits) => (bits & 2) !== 0).length,
19596
+ nodeAndRepv: [...regionProtocol.values()].filter((bits) => bits === 3).length
19597
+ };
19598
+ return {
19599
+ ...emitted,
19600
+ source: candidate,
19601
+ supercompile: {
19602
+ ...before,
19603
+ activated: removed > 0,
19604
+ ...removed > 0 ? {} : { reason: "no uniquely-owned region bodies" },
19605
+ functionsAfter,
19606
+ pieceCallsAfter: pieceCallsBefore - removed,
19607
+ sourceAfter: candidate.length,
19608
+ regions: roots.length,
19609
+ largestRegionSource: Math.max(0, ...regionSizes.values()),
19610
+ ...largestRegion === void 0 ? {} : { largestRegionIp: largestRegion[0] },
19611
+ protocolRegions
19612
+ }
19613
+ };
19614
+ }
19615
+
19616
+ // src/plugin/ir-eval.ts
19617
+ function evalRulesIR(ir) {
19618
+ return evalIR(ir, rules);
19619
+ }
19620
+ function evalCombinatorIR(ir) {
19621
+ const units = evalIR(ir, (opts) => opts.scanSkip);
19622
+ return units?.length === 1 ? units[0] : null;
19623
+ }
19624
+ function evalIR(ir, rulesImpl) {
19625
+ const _tf = (child, src, recognitionOnly = false) => {
19626
+ let fn2;
19627
+ try {
19628
+ fn2 = (0, eval)(`(${src})`);
19629
+ } catch {
19630
+ fn2 = () => {
19631
+ throw new Error("IR transform fn not materialized");
19632
+ };
19633
+ }
19634
+ const t = transform(child, fn2);
19635
+ t._def.fnSrc = src;
19636
+ if (recognitionOnly) t._def.recognitionOnly = true;
19637
+ return t;
19638
+ };
19639
+ const _lf = (child, src, source = false) => {
19640
+ let fn2;
19641
+ try {
19642
+ fn2 = (0, eval)(`(${src})`);
19643
+ } catch {
19644
+ fn2 = () => {
19645
+ throw new Error("IR leaf fn not materialized");
19646
+ };
19647
+ }
19648
+ const l = source ? sourceLeaf(child, fn2) : leaf(child, fn2);
19649
+ l._def.fnSrc = src;
19650
+ return l;
19651
+ };
19652
+ const _nd = (type, child, src, opts, staticError, sigSrc, buildImports, rawUnused) => {
19653
+ if (staticError !== void 0 && staticError.length > 0) {
19654
+ throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
19655
+ }
19656
+ const n = node(type, child, (() => {
19657
+ throw new Error("IR node build requires static re-lowering");
19658
+ }), opts);
19659
+ n._def.buildSrc = src;
19660
+ if (staticError !== void 0) n._def.buildStaticError = staticError;
19661
+ if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
19662
+ if (rawUnused === true) n._def.buildRawUnused = true;
19663
+ if (buildImports !== void 0) n._def.buildImports = buildImports;
19664
+ return n;
19665
+ };
19666
+ const _gch = (items) => {
19667
+ const gateSrcs = [];
19668
+ const arms = items.map((it) => {
19669
+ if (Array.isArray(it)) {
19670
+ const [src, comb] = it;
19671
+ gateSrcs.push(src);
19672
+ let gate2;
19673
+ try {
19674
+ gate2 = (0, eval)(`(${src})`);
19675
+ } catch {
19676
+ gate2 = () => {
19677
+ throw new Error("IR gate fn not materialized");
19678
+ };
19679
+ }
19680
+ return { gate: gate2, combinator: comb };
19681
+ }
19682
+ gateSrcs.push(null);
19683
+ return it;
19684
+ });
19685
+ const c = choice(...arms);
19686
+ const gates = c._def.gates;
19687
+ if (gates && gates.length !== gateSrcs.length) {
19688
+ throw new Error(`_gch: gateSrcs length ${gateSrcs.length} != choice gates length ${gates.length}`);
19689
+ }
19690
+ ;
19691
+ c._def.gateSrcs = gateSrcs;
19692
+ return c;
19560
19693
  };
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
19694
+ const _wc = (src, inner) => {
19695
+ let extra;
19696
+ try {
19697
+ extra = (0, eval)(`(${src})`);
19698
+ } catch {
19699
+ extra = void 0;
19575
19700
  }
19701
+ const w = withCtx(extra, inner);
19702
+ w._def.extraSrc = src;
19703
+ return w;
19576
19704
  };
19705
+ const fn = new Function(
19706
+ "rules",
19707
+ "ref",
19708
+ "regex",
19709
+ "literal",
19710
+ "keywords",
19711
+ "sequence",
19712
+ "choice",
19713
+ "dispatch",
19714
+ "when",
19715
+ "startsWith",
19716
+ "endsWith",
19717
+ "matches",
19718
+ "otherwise",
19719
+ "routed",
19720
+ "attempt",
19721
+ "many",
19722
+ "oneOrMore",
19723
+ "optional",
19724
+ "sepBy",
19725
+ "keepSeparator",
19726
+ "not",
19727
+ "peek",
19728
+ "node",
19729
+ "parser",
19730
+ "scanTo",
19731
+ "balanced",
19732
+ "token",
19733
+ "leaf",
19734
+ "transform",
19735
+ "trivia",
19736
+ "classifiedTrivia",
19737
+ "label",
19738
+ "field",
19739
+ "expect",
19740
+ "adjacent",
19741
+ "notAdjacent",
19742
+ "_tf",
19743
+ "_lf",
19744
+ "_nd",
19745
+ "_gch",
19746
+ "_wc",
19747
+ `return (${ir})`
19748
+ );
19749
+ return fn(
19750
+ rulesImpl,
19751
+ ref,
19752
+ regex,
19753
+ literal,
19754
+ keywords,
19755
+ sequence,
19756
+ choice,
19757
+ dispatch,
19758
+ when,
19759
+ startsWith,
19760
+ endsWith,
19761
+ matches,
19762
+ otherwise,
19763
+ routed,
19764
+ attempt,
19765
+ many,
19766
+ oneOrMore,
19767
+ optional,
19768
+ sepBy,
19769
+ keepSeparator,
19770
+ not,
19771
+ peek,
19772
+ node,
19773
+ parser,
19774
+ scanTo,
19775
+ balanced,
19776
+ token,
19777
+ leaf,
19778
+ transform,
19779
+ trivia,
19780
+ classifiedTrivia,
19781
+ label,
19782
+ field,
19783
+ expect,
19784
+ adjacent,
19785
+ notAdjacent,
19786
+ _tf,
19787
+ _lf,
19788
+ _nd,
19789
+ _gch,
19790
+ _wc
19791
+ );
19577
19792
  }
19578
19793
 
19579
19794
  // src/plugin/index.ts
@@ -19738,13 +19953,30 @@ function topLevelInit(body, name) {
19738
19953
  }
19739
19954
  return null;
19740
19955
  }
19741
- function isComposedPiecesSymbol(n) {
19956
+ function isSymbolFor(n, key) {
19742
19957
  if (!n || n.type !== "CallExpression") return false;
19743
19958
  const callee = n.callee;
19744
19959
  const obj = callee?.object?.name;
19745
19960
  const prop = callee?.property?.name;
19746
19961
  const arg0 = n.arguments?.[0];
19747
- return obj === "Symbol" && prop === "for" && arg0?.value === "parseman.composedPieces";
19962
+ return obj === "Symbol" && prop === "for" && arg0?.value === key;
19963
+ }
19964
+ var isComposedPiecesSymbol = (n) => isSymbolFor(n, "parseman.composedPieces");
19965
+ var COMBINATOR_IR_KEY = "parseman.combinatorIR";
19966
+ function carriedCombinatorIR(init) {
19967
+ let n = init;
19968
+ while (n.type === "ParenthesizedExpression") n = n.expression;
19969
+ if (n.type !== "CallExpression") return null;
19970
+ const callee = n.callee;
19971
+ const args = n.arguments;
19972
+ if (callee.object?.name !== "Object" || callee.property?.name !== "defineProperty" || !isSymbolFor(args[1], COMBINATOR_IR_KEY)) return null;
19973
+ for (const p of args[2]?.properties ?? []) {
19974
+ const v = p.value;
19975
+ if (propName(p) !== "value") continue;
19976
+ if (v?.type === "Literal" && typeof v.value === "string") return v.value;
19977
+ if (v?.type === "TemplateLiteral" && v.expressions?.length === 0) return v.quasis?.[0]?.value.cooked ?? null;
19978
+ }
19979
+ return null;
19748
19980
  }
19749
19981
  function findCarriedPiecesLiteral(root) {
19750
19982
  let found = null;
@@ -19760,7 +19992,7 @@ function findCarriedPiecesLiteral(root) {
19760
19992
  const obj = callee?.object?.name;
19761
19993
  const prop = callee?.property?.name;
19762
19994
  const args = node2.arguments;
19763
- if (callee?.type === "Identifier" && callee.name === "tableRules" && args?.[1]?.type === "ObjectExpression") {
19995
+ if (callee?.type === "Identifier" && args?.[1]?.type === "ObjectExpression") {
19764
19996
  for (const p of args[1].properties ?? []) {
19765
19997
  if (p.computed && isComposedPiecesSymbol(p.key) && p.value) {
19766
19998
  found = { start: p.value.start, end: p.value.end };
@@ -19860,13 +20092,14 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19860
20092
  const allNames = /* @__PURE__ */ new Set();
19861
20093
  const importBindings = /* @__PURE__ */ new Map();
19862
20094
  const ordinaryImports = [];
20095
+ const isMacroImport = (s) => s.attributes.some((a) => {
20096
+ const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
20097
+ return key === "type" && a.value.value === "macro";
20098
+ });
19863
20099
  for (const stmt of body) {
19864
20100
  if (stmt.type !== "ImportDeclaration") continue;
19865
20101
  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
- });
20102
+ const isMacro = moduleAliases.has(s.source.value) && isMacroImport(s);
19870
20103
  if (!isMacro) {
19871
20104
  const specifiers = [];
19872
20105
  for (const spec of s.specifiers) {
@@ -19909,7 +20142,209 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19909
20142
  }
19910
20143
  const reducerResolver = createReducerResolver(id, body, code);
19911
20144
  setReducerResolver(reducerResolver, code);
19912
- setBuilderImportResolver((name) => importBindings.get(name) ?? null);
20145
+ let builderImports = (name) => importBindings.get(name) ?? null;
20146
+ setBuilderImportResolver((name) => builderImports(name));
20147
+ const ownRoot = nearestPackageRoot(id);
20148
+ let ownDir = path2.dirname(id);
20149
+ try {
20150
+ ownDir = fs2.realpathSync(ownDir);
20151
+ } catch {
20152
+ }
20153
+ const rebaseSpecifier = (from, spec) => {
20154
+ if (!spec.startsWith(".")) return spec;
20155
+ const target = path2.resolve(path2.dirname(from), spec);
20156
+ if (ownRoot === null || nearestPackageRoot(target) !== ownRoot) return null;
20157
+ const rel = path2.relative(ownDir, target).split(path2.sep).join("/");
20158
+ return rel.startsWith("../") ? rel : `./${rel}`;
20159
+ };
20160
+ const unresolvedImports = /* @__PURE__ */ new Set();
20161
+ const valueModules = /* @__PURE__ */ new Map();
20162
+ const OPAQUE_IMPORT = "is a default or namespace import, which the macro does not resolve \u2014 import the binding by name";
20163
+ const opaqueImportsOf = (moduleBody) => {
20164
+ const out = /* @__PURE__ */ new Set();
20165
+ for (const st of moduleBody) {
20166
+ if (st.type !== "ImportDeclaration") continue;
20167
+ for (const sp of st.specifiers) {
20168
+ if (sp.type !== "ImportSpecifier") out.add(sp.local.name);
20169
+ }
20170
+ }
20171
+ return out;
20172
+ };
20173
+ const valueModule = (file) => {
20174
+ if (valueModules.has(file)) return valueModules.get(file);
20175
+ const mod = parseModuleCached(file);
20176
+ let out = null;
20177
+ if (mod) {
20178
+ const consts = /* @__PURE__ */ new Map();
20179
+ let stable;
20180
+ for (const st of mod.body) {
20181
+ const vd = unwrapVd(st);
20182
+ if (!vd) continue;
20183
+ const isConst = vd.kind === "const";
20184
+ for (const d of vd.declarations) {
20185
+ const idn = d.id;
20186
+ if (idn.type !== "Identifier" || !idn.name || !d.init) continue;
20187
+ if (isConst || (stable ??= topLevelIsStable(mod.body, mod.src.length))(idn.name)) consts.set(idn.name, d.init);
20188
+ }
20189
+ }
20190
+ out = {
20191
+ file,
20192
+ src: mod.src,
20193
+ body: mod.body,
20194
+ consts,
20195
+ imports: extractImportBindings(mod.body),
20196
+ opaqueImports: opaqueImportsOf(mod.body)
20197
+ };
20198
+ }
20199
+ valueModules.set(file, out);
20200
+ return out;
20201
+ };
20202
+ const resolveValueFile = (from, specifier) => {
20203
+ const source = resolvePrivateSourceModule(from, specifier);
20204
+ if (source) return source;
20205
+ try {
20206
+ return getCompiledResolver().resolveFileSync(from, specifier).path ?? null;
20207
+ } catch {
20208
+ return null;
20209
+ }
20210
+ };
20211
+ const evaluatingConsts = /* @__PURE__ */ new Set();
20212
+ const rebaseCarriedBuilderImports = (combi, from) => {
20213
+ const seen = /* @__PURE__ */ new Set();
20214
+ const walk = (v) => {
20215
+ if (v === null || typeof v !== "object" || seen.has(v)) return null;
20216
+ seen.add(v);
20217
+ const def = v._def;
20218
+ if (def?.buildImports) {
20219
+ const out = [];
20220
+ for (const bi of def.buildImports) {
20221
+ const source = rebaseSpecifier(from, bi.source);
20222
+ if (source === null) return bi;
20223
+ out.push({ ...bi, source });
20224
+ }
20225
+ def.buildImports = out;
20226
+ }
20227
+ for (const child of Object.values(v)) {
20228
+ for (const c of Array.isArray(child) ? child : [child]) {
20229
+ const stuck = walk(c);
20230
+ if (stuck) return stuck;
20231
+ }
20232
+ }
20233
+ return null;
20234
+ };
20235
+ return walk(combi);
20236
+ };
20237
+ const moduleConstValue = (m, name) => {
20238
+ const key = `${m.file}\0${name}`;
20239
+ if (evaluatingConsts.has(key)) return { unresolved: `is part of a declaration cycle in ${m.file}` };
20240
+ evaluatingConsts.add(key);
20241
+ const outerBuilderImports = builderImports;
20242
+ try {
20243
+ const init = m.consts.get(name);
20244
+ const ir = carriedCombinatorIR(init);
20245
+ if (ir !== null) {
20246
+ let combi2 = null;
20247
+ let why = "";
20248
+ try {
20249
+ combi2 = evalCombinatorIR(ir);
20250
+ } catch (e) {
20251
+ why = `: ${e.message}`;
20252
+ }
20253
+ if (!combi2) return { unresolved: `carries combinator IR that does not evaluate (${m.file})${why}` };
20254
+ const stuck = rebaseCarriedBuilderImports(combi2, m.file);
20255
+ 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: [] } };
20256
+ }
20257
+ const scope2 = moduleValueScope(m);
20258
+ reducerResolver.register(m.file);
20259
+ builderImports = (name2) => {
20260
+ const b = m.imports.get(name2);
20261
+ const source = b ? rebaseSpecifier(m.file, b.source) : null;
20262
+ return b && source !== null ? { source, imported: b.imported } : null;
20263
+ };
20264
+ const mfs = [];
20265
+ const combi = evaluateExpr(init, scope2, m.src, mfs);
20266
+ if (combi) return { value: { combi, mfSrcs: mfs } };
20267
+ const factory = evaluateWordFactory(init, scope2, m.src) ?? evaluateWhenFactory(init, scope2, m.src);
20268
+ if (factory) return { value: factory };
20269
+ const value = evaluateStaticValue(init, scope2, m.src);
20270
+ if (value !== null && value !== void 0 || isStaticNullishExpression(init)) return { value };
20271
+ return {
20272
+ 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})`
20273
+ };
20274
+ } finally {
20275
+ evaluatingConsts.delete(key);
20276
+ builderImports = outerBuilderImports;
20277
+ }
20278
+ };
20279
+ const moduleValueScope = (m) => m.scope ??= new ModuleScope((name) => {
20280
+ if (m.consts.has(name)) return moduleConstValue(m, name);
20281
+ const binding = m.imports.get(name);
20282
+ if (binding) return importedValue(m.file, binding);
20283
+ return m.opaqueImports.has(name) ? { unresolved: OPAQUE_IMPORT } : null;
20284
+ }, unresolvedImports);
20285
+ const exportedValue = (m, name, depth) => {
20286
+ for (const st of m.body) {
20287
+ if (st.type !== "ExportNamedDeclaration" || !st.source) continue;
20288
+ for (const sp of st.specifiers ?? []) {
20289
+ if (sp.exported.name !== name) continue;
20290
+ const source = st.source.value;
20291
+ const file = depth < 16 ? resolveValueFile(m.file, source) : null;
20292
+ const next = file ? valueModule(file) : null;
20293
+ if (!next) return { unresolved: `is re-exported from '${source}', which does not resolve from ${m.file}` };
20294
+ return exportedValue(next, sp.local.name, depth + 1);
20295
+ }
20296
+ }
20297
+ const local = exportLocalName(m.body, name);
20298
+ if (local === null) {
20299
+ for (const st of m.body) {
20300
+ if (st.type !== "ExportAllDeclaration" || st.exported) continue;
20301
+ const file = depth < 16 ? resolveValueFile(m.file, st.source.value) : null;
20302
+ const next = file ? valueModule(file) : null;
20303
+ if (!next) continue;
20304
+ const r = exportedValue(next, name, depth + 1);
20305
+ if (r !== null && "unresolved" in r && r.unresolved.startsWith("is not exported by ")) continue;
20306
+ return r;
20307
+ }
20308
+ return { unresolved: `is not exported by ${m.file}` };
20309
+ }
20310
+ const scope2 = moduleValueScope(m);
20311
+ if (!scope2.has(local)) {
20312
+ scope2.get(local);
20313
+ return { unresolved: `could not be resolved in ${m.file}` };
20314
+ }
20315
+ return { value: scope2.get(local) };
20316
+ };
20317
+ const importedValue = (from, binding) => {
20318
+ const file = resolveValueFile(from, binding.source);
20319
+ const m = file ? valueModule(file) : null;
20320
+ if (!m) return { unresolved: `is imported from '${binding.source}', which does not resolve from ${from}` };
20321
+ return exportedValue(m, binding.imported, 0);
20322
+ };
20323
+ const deferredLocals = /* @__PURE__ */ new Map();
20324
+ const deferredResults = /* @__PURE__ */ new Map();
20325
+ const mainOpaqueImports = opaqueImportsOf(body);
20326
+ const resolveMainFree = (name) => {
20327
+ const done = deferredResults.get(name);
20328
+ if (done !== void 0) return done;
20329
+ const deferred = deferredLocals.get(name);
20330
+ if (deferred !== void 0) {
20331
+ deferredLocals.delete(name);
20332
+ const outer = [...unresolvedImports];
20333
+ unresolvedImports.clear();
20334
+ const value = evaluateStaticValue(deferred, scope, code);
20335
+ const why = [...unresolvedImports];
20336
+ unresolvedImports.clear();
20337
+ for (const n of outer) unresolvedImports.add(n);
20338
+ 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;
20339
+ deferredResults.set(name, result2);
20340
+ return result2;
20341
+ }
20342
+ const binding = importBindings.get(name);
20343
+ if (!binding) return mainOpaqueImports.has(name) ? { unresolved: OPAQUE_IMPORT } : null;
20344
+ const resolved = importedValue(id, binding);
20345
+ if (resolved !== null && "value" in resolved) usedImportedFactories.add(name);
20346
+ return resolved;
20347
+ };
19913
20348
  const topLevelFunction = (moduleBody, name) => {
19914
20349
  for (const st of moduleBody) {
19915
20350
  const decl = st.type === "ExportNamedDeclaration" ? st.declaration : st;
@@ -19918,19 +20353,17 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19918
20353
  }
19919
20354
  return null;
19920
20355
  };
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();
20356
+ const sourceScopeUntil = (file, moduleBody, source, until) => {
20357
+ const imports = extractImportBindings(moduleBody);
20358
+ const opaque = opaqueImportsOf(moduleBody);
20359
+ const out = new ModuleScope((name) => {
20360
+ const binding = imports.get(name);
20361
+ if (binding) return importedValue(file, binding);
20362
+ return opaque.has(name) ? { unresolved: OPAQUE_IMPORT } : null;
20363
+ }, unresolvedImports);
19931
20364
  for (const stmt of moduleBody) {
19932
20365
  if (stmt.type === "ImportDeclaration") {
19933
- if (!isParsemanMacroImport(stmt)) return null;
20366
+ if (moduleAliases.has(stmt.source.value) && !isMacroImport(stmt)) return null;
19934
20367
  continue;
19935
20368
  }
19936
20369
  if (stmt.type === "FunctionDeclaration") continue;
@@ -19983,6 +20416,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19983
20416
  }
19984
20417
  return out;
19985
20418
  };
20419
+ const factoryFailures = /* @__PURE__ */ new Map();
19986
20420
  const usedImportedFactories = /* @__PURE__ */ new Set();
19987
20421
  const markUsedImportedFactories = (names) => {
19988
20422
  for (const name of names ?? []) usedImportedFactories.add(name);
@@ -19995,8 +20429,13 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
19995
20429
  const localFor = exportLocalName(mod.body, binding.imported);
19996
20430
  const init = localFor ? topLevelInit(mod.body, localFor) ?? topLevelFunction(mod.body, localFor) : null;
19997
20431
  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;
20432
+ unresolvedImports.clear();
20433
+ const factoryScope = sourceScopeUntil(file, mod.body, mod.src, init.start);
20434
+ if (!factoryScope) {
20435
+ if (unresolvedImports.size > 0) factoryFailures.set(localName, [...unresolvedImports]);
20436
+ unresolvedImports.clear();
20437
+ continue;
20438
+ }
20000
20439
  factoryDecls.set(localName, {
20001
20440
  fn: init,
20002
20441
  declaredAt: -1,
@@ -20006,7 +20445,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20006
20445
  });
20007
20446
  reducerResolver.register(file);
20008
20447
  }
20009
- const scope = /* @__PURE__ */ new Map();
20448
+ const scope = new ModuleScope(resolveMainFree, unresolvedImports);
20010
20449
  const replacements = [];
20011
20450
  const warnings = [];
20012
20451
  beginDegradationCapture();
@@ -20026,6 +20465,12 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20026
20465
  m = /* @__PURE__ */ new Map();
20027
20466
  pendingBuilderImports.set(bi.source, m);
20028
20467
  }
20468
+ const prev = m.get(bi.local);
20469
+ if (prev !== void 0 && prev !== bi.imported) {
20470
+ throw new Error(
20471
+ `${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.`
20472
+ );
20473
+ }
20029
20474
  m.set(bi.local, bi.imported);
20030
20475
  }
20031
20476
  if (def?.tag === "lazy" && v._ruleName === void 0 && typeof def.thunk === "function") {
@@ -20067,6 +20512,41 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20067
20512
  anyUnresolved = true;
20068
20513
  warnings.push(`${id}:${lineOf(pos)} \u2014 ${msg} (running via the interpreter; add the plugin or simplify the declaration to compile it)`);
20069
20514
  };
20515
+ const unevaluable = (pos, what) => {
20516
+ const names = [...unresolvedImports];
20517
+ unresolvedImports.clear();
20518
+ throw new Error(
20519
+ `${id}:${lineOf(pos)} \u2014 ${what} can't be evaluated at build time` + (names.length > 0 ? `; unresolved binding(s):
20520
+ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/value expression)") + `
20521
+ parseman will not emit this module rather than drop it silently.`
20522
+ );
20523
+ };
20524
+ const requiredOption = (label2, name, value, evaluate) => {
20525
+ if (value === void 0 || isStaticNullishExpression(value)) return void 0;
20526
+ unresolvedImports.clear();
20527
+ const out = evaluate(value);
20528
+ if (out === null) {
20529
+ const id2 = value.type === "Identifier" ? value.name : null;
20530
+ if (id2 !== null && scope.has(id2) && scope.get(id2) == null) return void 0;
20531
+ return unevaluable(value.start, `${label2}: rules({ ${name} })`);
20532
+ }
20533
+ unresolvedImports.clear();
20534
+ return out;
20535
+ };
20536
+ const RULES_OPTIONS = /* @__PURE__ */ new Set(["trivia", "scanSkip", "hostMode", "trackLines"]);
20537
+ const assertPlainOptions = (optExpr, label2) => {
20538
+ for (const p of optExpr?.type === "ObjectExpression" ? optExpr.properties : []) {
20539
+ if (propName(p) !== null) continue;
20540
+ const key = p.type === "Property" ? evaluateStaticValue(p.key, scope, code) : null;
20541
+ if (typeof key !== "string" || RULES_OPTIONS.has(key)) {
20542
+ unevaluable(p.start, `${label2}: an options ${p.type === "Property" ? "computed key" : "spread"}`);
20543
+ }
20544
+ }
20545
+ };
20546
+ const triviaOption = (optExpr, label2) => {
20547
+ assertPlainOptions(optExpr, label2);
20548
+ return requiredOption(label2, "trivia", optionProp(optExpr, "trivia"), (v) => evaluateExpr(v, scope, code, []));
20549
+ };
20070
20550
  const evaluateRulesFactory = (init, label2) => {
20071
20551
  const args = init.arguments;
20072
20552
  const arg0 = args[0];
@@ -20082,7 +20562,15 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20082
20562
  const factoryScope = namedFactory?.scope ?? scope;
20083
20563
  const factoryCode = namedFactory?.code ?? code;
20084
20564
  const importedFactory = namedFactory?.imported === true && factoryArg === namedFactory.fn ? factoryArgRaw.name : void 0;
20565
+ const failedFactory = factoryArgRaw.type === "Identifier" ? factoryFailures.get(factoryArgRaw.name) : void 0;
20566
+ if (failedFactory) {
20567
+ for (const n of failedFactory) unresolvedImports.add(n);
20568
+ unevaluable(init.start, `${label2}: the imported rules() factory \`${factoryArgRaw.name}\``);
20569
+ }
20085
20570
  const optionsArg = optionsFirst ? arg0 : arg1;
20571
+ if (optionsArg !== void 0 && optionsArg.type !== "ObjectExpression" && !isStaticNullishExpression(optionsArg)) {
20572
+ unevaluable(optionsArg.start, `${label2}: rules() options`);
20573
+ }
20086
20574
  const optionValue = (name) => optionProp(optionsArg, name);
20087
20575
  const hostModeValue = optionValue("hostMode");
20088
20576
  const gHostMode = hostModeValue?.type === "Literal" ? hostModeValue.value : void 0;
@@ -20096,11 +20584,16 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20096
20584
  warn(init.start, `${label2}: rules({ trackLines }) must be a boolean literal`);
20097
20585
  return null;
20098
20586
  }
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;
20587
+ const gTrivia = triviaOption(optionsArg, label2);
20588
+ const scanSkipArray = requiredOption(label2, "scanSkip", optionValue("scanSkip"), (v) => {
20589
+ const units = evaluateCombinatorArray(v, scope, code);
20590
+ if (units) return units;
20591
+ const value = evaluateStaticValue(v, scope, code);
20592
+ return Array.isArray(value) && value.length === 0 ? [] : null;
20593
+ });
20594
+ const gScanSkip = scanSkipArray?.length ? scanSkipArray : void 0;
20103
20595
  const why = {};
20596
+ unresolvedImports.clear();
20104
20597
  const ruleMap = evaluateParserFactory(factoryArg, factoryScope, factoryCode, [], why, {
20105
20598
  ...gTrivia ? { trivia: gTrivia } : {},
20106
20599
  ...gScanSkip ? { scanSkip: gScanSkip } : {},
@@ -20108,6 +20601,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20108
20601
  ...gTrackLines === true ? { trackLines: true } : {}
20109
20602
  });
20110
20603
  if (!ruleMap) {
20604
+ if (unresolvedImports.size > 0) unevaluable(init.start, `${label2}: rules(...) factory`);
20111
20605
  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
20606
  return null;
20113
20607
  }
@@ -20124,6 +20618,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20124
20618
  if (!evaluated) return null;
20125
20619
  const refusals = [];
20126
20620
  const compiled = compileRuleMap([...evaluated.ruleMap], { ...evaluated.trivia ? { trivia: evaluated.trivia } : {}, ...evaluated.scanSkip ? { scanSkip: evaluated.scanSkip } : {}, ...evaluated.trackLines ? { trackLines: true } : {}, recovery, coverage: grammarCoverage, refusals });
20621
+ if (compiled !== null) collectBuilderImports([...evaluated.ruleMap]);
20127
20622
  if (compiled !== null) usedTableRuntime = true;
20128
20623
  return {
20129
20624
  replacement: compiled?.replacement ?? null,
@@ -20185,49 +20680,63 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20185
20680
  };
20186
20681
  const localComposedCarried = /* @__PURE__ */ new Map();
20187
20682
  const importedPiecesCache = /* @__PURE__ */ new Map();
20188
- const resolveModulePieces = (module, exportName, seen) => {
20683
+ const resolveModulePieces = (module, exportName, seen, unresolved, local = false) => {
20189
20684
  const { file } = module;
20190
20685
  const mod = module.source ? lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recovery) : parseModuleCached(file);
20191
20686
  if (!mod) return null;
20192
- const localFor = exportLocalName(mod.body, exportName);
20687
+ const localFor = local ? exportName : exportLocalName(mod.body, exportName);
20193
20688
  const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
20194
20689
  const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
20195
20690
  if (!literalRange) return null;
20196
20691
  const literal2 = mod.src.slice(literalRange.start, literalRange.end);
20692
+ const fail = (why) => {
20693
+ unresolved.why ??= `${exportName} in ${file}: ${why}`;
20694
+ return null;
20695
+ };
20197
20696
  const imports = extractImportBindings(mod.body);
20198
20697
  const stubNames = [];
20199
20698
  const stubVals = [];
20200
- const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
20699
+ const spreadRe = /\.\.\.\s*\(?\s*([A-Za-z_$][\w$]*)\s*\[\s*(?:\/\*[^]*?\*\/\s*)*Symbol\s*\.\s*for\s*\(\s*['"]parseman\.composedPieces['"]/g;
20201
20700
  const done = /* @__PURE__ */ new Set();
20202
20701
  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);
20702
+ const local2 = m[1];
20703
+ if (done.has(local2)) continue;
20704
+ done.add(local2);
20705
+ const b = imports.get(local2);
20207
20706
  let subPieces = [];
20208
20707
  if (b) {
20209
20708
  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)) ?? [];
20709
+ if (subModule === null) return fail(`its carried pieces spread \`${local2}\` from '${b.source}', a module that could not be found`);
20710
+ if (!seen.has(subModule.file)) {
20711
+ subPieces = resolveModulePieces(subModule, b.imported, new Set(seen).add(subModule.file), unresolved);
20712
+ if (subPieces === null) return fail(`its carried pieces spread \`${local2}\` from '${b.source}', which could not be resolved`);
20212
20713
  }
20714
+ } else if (topLevelInit(mod.body, local2) !== null && !seen.has(`${file}#${local2}`)) {
20715
+ subPieces = resolveModulePieces(module, local2, new Set(seen).add(`${file}#${local2}`), unresolved, true);
20716
+ if (subPieces === null) return fail(`its carried pieces spread the inlined binding \`${local2}\`, which carries none`);
20717
+ } else {
20718
+ return fail(`its carried pieces spread \`${local2}\`, which is neither an import nor a top-level binding of the module`);
20213
20719
  }
20214
- stubNames.push(local);
20720
+ stubNames.push(local2);
20215
20721
  stubVals.push({ [COMPOSED_PIECES]: subPieces });
20216
20722
  }
20217
20723
  try {
20218
20724
  const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
20219
20725
  return list;
20220
- } catch {
20221
- return null;
20726
+ } catch (e) {
20727
+ return fail(`its carried pieces literal did not evaluate (${e.message})`);
20222
20728
  }
20223
20729
  };
20730
+ const unresolvedCompiled = /* @__PURE__ */ new Map();
20224
20731
  const importedPieces = (localName) => {
20225
20732
  if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
20226
20733
  let result2 = null;
20227
20734
  const binding = importBindings.get(localName);
20228
20735
  if (binding) {
20229
20736
  const module = resolveGrammarModule(id, binding.source);
20230
- result2 = module ? resolveModulePieces(module, binding.imported, /* @__PURE__ */ new Set([module.file])) : null;
20737
+ const unresolved = {};
20738
+ result2 = module ? resolveModulePieces(module, binding.imported, /* @__PURE__ */ new Set([module.file]), unresolved) : null;
20739
+ if (result2 === null && unresolved.why !== void 0) unresolvedCompiled.set(localName, unresolved.why);
20231
20740
  }
20232
20741
  importedPiecesCache.set(localName, result2);
20233
20742
  return result2;
@@ -20238,7 +20747,8 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20238
20747
  const add = (item) => {
20239
20748
  if (!isIR(item)) return false;
20240
20749
  if (item.trackLines === true) trackLines = true;
20241
- pieces.push({ ns: item.ns, rules: evalRuleMapIR(item.ir) });
20750
+ const map = evalRulesIR(item.ir);
20751
+ pieces.push({ ns: item.ns, rules: Object.entries(map), recipes: map[RULES_RECIPE] });
20242
20752
  return true;
20243
20753
  };
20244
20754
  for (const item of items) {
@@ -20265,10 +20775,20 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20265
20775
  }
20266
20776
  return [...winners];
20267
20777
  };
20268
- const mergedCarriedRules = (items) => {
20778
+ const mergedCarriedRules = (items, link) => {
20269
20779
  const carriedMaps = carriedRuleMaps(items);
20270
20780
  if (carriedMaps === null) return null;
20271
- return { rules: mergeRuleMaps(carriedMaps.pieces.map((p) => p.rules)), trackLines: carriedMaps.trackLines };
20781
+ const linked = linkRules(carriedMaps.pieces.flatMap((p) => p.recipes), link);
20782
+ const rules2 = Object.entries(linked).filter(([, rule]) => {
20783
+ if (rule._def.tag !== "lazy") return true;
20784
+ try {
20785
+ rule._def.thunk();
20786
+ return true;
20787
+ } catch {
20788
+ return false;
20789
+ }
20790
+ });
20791
+ return { rules: rules2, trackLines: carriedMaps.trackLines };
20272
20792
  };
20273
20793
  const applyComposingTrivia = (ruleMap, composing) => {
20274
20794
  for (const [, rule] of ruleMap) {
@@ -20308,9 +20828,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20308
20828
  const a0 = rulesArgs[0];
20309
20829
  const a1 = rulesArgs[1];
20310
20830
  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;
20831
+ return triviaOption(optExpr, "compose() element");
20314
20832
  };
20315
20833
  const composingTrivia = (elements) => {
20316
20834
  for (let i = elements.length - 1; i >= 0; i--) {
@@ -20337,6 +20855,8 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20337
20855
  }
20338
20856
  const elements = arr.elements;
20339
20857
  const cOptions = init.arguments[1];
20858
+ if (cOptions !== void 0 && cOptions.type !== "ObjectExpression") unevaluable(cOptions.start, "compose() options");
20859
+ assertPlainOptions(cOptions, "compose()");
20340
20860
  const cHostModeValue = optionProp(cOptions, "hostMode");
20341
20861
  const cHostMode = cHostModeValue?.type === "Literal" ? cHostModeValue.value : void 0;
20342
20862
  if (cHostModeValue !== void 0 && cHostMode !== "ast" && cHostMode !== "cst") {
@@ -20349,15 +20869,25 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20349
20869
  for (let i = 0; i < elements.length; i++) {
20350
20870
  const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
20351
20871
  if (!r) {
20872
+ const el = elements[i];
20873
+ const name = el.type === "Identifier" ? el.name : void 0;
20874
+ const why = name === void 0 ? void 0 : unresolvedCompiled.get(name);
20875
+ if (why !== void 0) {
20876
+ throw new Error(
20877
+ `${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.`
20878
+ );
20879
+ }
20352
20880
  warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
20353
20881
  return null;
20354
20882
  }
20355
20883
  carried.push(...r.carried);
20356
20884
  importedFactories.push(...r.importedFactories ?? []);
20357
20885
  }
20358
- const merged = mergedCarriedRules(carried);
20886
+ const merged = mergedCarriedRules(carried, {
20887
+ trivia: composing,
20888
+ ...cHostMode ? { hostMode: cHostMode } : {}
20889
+ });
20359
20890
  if (merged !== null) {
20360
- if (composing) applyComposingTrivia(merged.rules, composing);
20361
20891
  collectBuilderImports(merged.rules);
20362
20892
  const refusals = [];
20363
20893
  const compiled = compileRuleMap(merged.rules, {
@@ -20552,11 +21082,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20552
21082
  if (d.id.type === "Identifier") {
20553
21083
  const varName = d.id.name;
20554
21084
  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
- }
21085
+ deferredLocals.set(varName, init);
20560
21086
  continue;
20561
21087
  }
20562
21088
  if (refNames.has(varName)) {
@@ -20643,6 +21169,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20643
21169
  continue;
20644
21170
  }
20645
21171
  const mapFnSources = [];
21172
+ unresolvedImports.clear();
20646
21173
  const parser2 = evaluateExpr(init, scope, code, mapFnSources);
20647
21174
  if (parser2 === null) {
20648
21175
  const wordFactory = evaluateWordFactory(init, scope, code);
@@ -20675,6 +21202,7 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20675
21202
  if (exportPrefix) exportedFactories.push({ name: varName, pos: init.start });
20676
21203
  continue;
20677
21204
  }
21205
+ if (unresolvedImports.size > 0) unevaluable(init.start, `"${varName}"`);
20678
21206
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
20679
21207
  continue;
20680
21208
  }
@@ -20687,7 +21215,8 @@ function transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recover
20687
21215
  replacements.push({
20688
21216
  start: init.start,
20689
21217
  end: init.end,
20690
- replacement: compiled.inlineExpression
21218
+ replacement: compiled.inlineExpression,
21219
+ carry: { name: varName, combi: parser2, exported: exportPrefix !== "" }
20691
21220
  });
20692
21221
  scope.set(varName, { combi: parser2, mfSrcs: mapFnSources });
20693
21222
  } else if (d.id.type === "ObjectPattern") {
@@ -20754,37 +21283,58 @@ ${which}
20754
21283
  Drop the \`export\`. The factory is macro-only; \`rules(${exportedFactories[0].name})\` is lowered in place and needs no runtime value.`
20755
21284
  );
20756
21285
  }
21286
+ const withCombinatorIR = (r) => {
21287
+ if (!r.carry || !r.carry.exported && !referencedNames().live.has(r.carry.name)) return r;
21288
+ let ir = null;
21289
+ try {
21290
+ ir = serializeCombinator(r.carry.combi);
21291
+ } catch {
21292
+ }
21293
+ return ir === null ? r : {
21294
+ ...r,
21295
+ replacement: `/* @__PURE__ */ Object.defineProperty(${r.replacement}, Symbol.for('${COMBINATOR_IR_KEY}'), { value: ${JSON.stringify(ir)}, enumerable: false })`
21296
+ };
21297
+ };
20757
21298
  const ms = new MagicString(code);
20758
21299
  const replacementRanges = runtimeComposeFallback ? [] : replacements.map(({ start, end }) => ({ start, end }));
20759
21300
  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;
21301
+ let referenced = null;
21302
+ const referencedNames = () => {
21303
+ if (referenced !== null) return referenced;
21304
+ const all = /* @__PURE__ */ new Set();
21305
+ const live = /* @__PURE__ */ new Set();
21306
+ const deadRanges = [...[...factoryDecls.values()].filter((f) => !f.imported).map((f) => f.fn), ...anyUnresolved ? [] : defineRemovals];
21307
+ const walk = (node2, inFactory, parent, parentKey) => {
21308
+ if (!node2 || typeof node2 !== "object") return;
20763
21309
  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;
21310
+ if (rec.type === "ImportDeclaration") return;
21311
+ if (typeof rec.start === "number" && typeof rec.end === "number") {
21312
+ if (isInsideReplacement(rec.start, rec.end)) return;
21313
+ inFactory ||= deadRanges.some((f) => rec.start >= f.start && rec.end <= f.end);
21314
+ }
21315
+ if (rec.type === "Identifier" || rec.type === "BindingIdentifier") {
21316
+ const isKey = parentKey === "key" && (parent?.type === "ObjectProperty" || parent?.type === "Property" || parent?.type === "PropertyDefinition") && parent.computed !== true;
21317
+ const isMember = parentKey === "property" && (parent?.type === "StaticMemberExpression" || parent?.type === "MemberExpression" && parent.computed !== true);
21318
+ const isDeclared = parentKey === "id" && parent?.type === "VariableDeclarator";
21319
+ if (!isKey && !isMember && !isDeclared) {
21320
+ all.add(rec.name);
21321
+ if (!inFactory) live.add(rec.name);
21322
+ }
21323
+ return;
20771
21324
  }
20772
21325
  for (const key of Object.keys(rec)) {
20773
21326
  if (key === "type" || key === "start" || key === "end") continue;
20774
21327
  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
- }
21328
+ if (Array.isArray(value)) for (const child of value) walk(child, inFactory, rec, key);
21329
+ else walk(value, inFactory, rec, key);
20780
21330
  }
20781
- return false;
20782
21331
  };
20783
- return body.some((stmt) => walk(stmt));
21332
+ for (const stmt of body) walk(stmt, false);
21333
+ return referenced = { all, live };
20784
21334
  };
20785
21335
  for (const imp of ordinaryImports) {
20786
21336
  if (!runtimeComposeFallback && imp.specifiers.length > 0 && imp.specifiers.every(
20787
- (spec) => spec.type === "ImportSpecifier" && usedImportedFactories.has(spec.local) && !importedBindingStillReferenced(spec.local)
21337
+ (spec) => spec.type === "ImportSpecifier" && usedImportedFactories.has(spec.local) && !referencedNames().all.has(spec.local)
20788
21338
  )) {
20789
21339
  ms.remove(imp.start, imp.end);
20790
21340
  }
@@ -20801,7 +21351,7 @@ ${which}
20801
21351
  ms.overwrite(imp.start, imp.end, stripped);
20802
21352
  }
20803
21353
  }
20804
- const applied = runtimeComposeFallback ? [] : replacements;
21354
+ const applied = runtimeComposeFallback ? [] : replacements.map((r) => withCombinatorIR(r));
20805
21355
  for (const { start, end, replacement } of applied.slice().sort((a, b) => b.start - a.start)) {
20806
21356
  ms.overwrite(start, end, replacement);
20807
21357
  }