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
package/dist/index.js CHANGED
@@ -4005,6 +4005,7 @@ function withRuleTrivia(trivia2, ctx, run3) {
4005
4005
  ctx.triviaKindLabels = savedLabels;
4006
4006
  }
4007
4007
  }
4008
+ var UNDEFINED_REF = new Error("ref<T>() used before .define() was called");
4008
4009
  function ref() {
4009
4010
  let resolved = null;
4010
4011
  let resolvedScalar = null;
@@ -4020,7 +4021,7 @@ function ref() {
4020
4021
  _def: {
4021
4022
  tag: "lazy",
4022
4023
  thunk: () => {
4023
- if (!resolved) throw new Error("ref<T>() used before .define() was called");
4024
+ if (!resolved) throw UNDEFINED_REF;
4024
4025
  return resolved;
4025
4026
  }
4026
4027
  },
@@ -4410,6 +4411,7 @@ function collectFromCombinator(parser2, out, seen, followLazy = true) {
4410
4411
  function collectGrammarReflection(ruleMap, opts) {
4411
4412
  const byType = /* @__PURE__ */ new Map();
4412
4413
  const followLazy = opts?.followLazy !== false;
4414
+ const seen = /* @__PURE__ */ new Set();
4413
4415
  for (const [, rule] of ruleMap) {
4414
4416
  if (!followLazy && rule._def.tag === "lazy") {
4415
4417
  try {
@@ -4419,7 +4421,7 @@ function collectGrammarReflection(ruleMap, opts) {
4419
4421
  }
4420
4422
  continue;
4421
4423
  }
4422
- collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
4424
+ collectFromCombinator(rule, byType, seen, followLazy);
4423
4425
  }
4424
4426
  return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
4425
4427
  }
@@ -4450,9 +4452,26 @@ function isNamedRuleRefForAnotherRule(r, key) {
4450
4452
  const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
4451
4453
  return name !== void 0 && name !== key;
4452
4454
  }
4455
+ var claimedRules = /* @__PURE__ */ new WeakSet();
4456
+ function ownRule(p, copies) {
4457
+ if (!claimedRules.has(p) || p._meta.isTrivia) return p;
4458
+ let own = copies.get(p);
4459
+ if (own === void 0) {
4460
+ const { grammarTrivia: _t, grammarScanSkip: _s, grammarHostMode: _h, grammarTrackLines: _l, ...meta } = p._meta;
4461
+ copies.set(p, own = { ...p, _meta: meta });
4462
+ }
4463
+ return own;
4464
+ }
4453
4465
  function rules(a, b) {
4454
4466
  const factory = typeof a === "function" ? a : b;
4455
4467
  const options = typeof a === "function" ? b : a;
4468
+ const recipe = { factory, options };
4469
+ const map = linkRules([recipe]);
4470
+ Object.defineProperty(map, RULES_RECIPE, { value: [recipe], enumerable: false });
4471
+ return map;
4472
+ }
4473
+ var RULES_RECIPE = /* @__PURE__ */ Symbol.for("parseman.rulesRecipe");
4474
+ function linkRules(recipes, link) {
4456
4475
  const cache = {};
4457
4476
  const proxy = new Proxy(cache, {
4458
4477
  get(target, key) {
@@ -4466,10 +4485,18 @@ function rules(a, b) {
4466
4485
  return record[key];
4467
4486
  }
4468
4487
  });
4469
- const definitions = factory(proxy);
4470
- for (const key of Object.keys(definitions)) {
4488
+ const winners = /* @__PURE__ */ new Map();
4489
+ for (const recipe of recipes) {
4490
+ const definitions = recipe.factory(proxy);
4491
+ for (const key of Object.keys(definitions)) {
4492
+ winners.set(key, { parser: definitions[key], options: recipe.options });
4493
+ }
4494
+ }
4495
+ const keys = [...winners.keys()];
4496
+ const copies = /* @__PURE__ */ new Map();
4497
+ for (const [key, { parser: defined }] of winners) {
4471
4498
  const placeholder = cache[key];
4472
- const parser2 = definitions[key];
4499
+ const parser2 = ownRule(defined, copies);
4473
4500
  if (placeholder === parser2) {
4474
4501
  throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
4475
4502
  }
@@ -4478,7 +4505,8 @@ function rules(a, b) {
4478
4505
  placeholder.define(parser2);
4479
4506
  placeholder._meta.firstSet = parser2._meta.firstSet;
4480
4507
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
4481
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
4508
+ } else if (isNamedRuleRefForAnotherRule(parser2, key) || link !== void 0) {
4509
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
4482
4510
  const alias = ref();
4483
4511
  tagRule(alias, key);
4484
4512
  alias.define(parser2);
@@ -4490,48 +4518,34 @@ function rules(a, b) {
4490
4518
  cache[key] = parser2;
4491
4519
  }
4492
4520
  }
4493
- if (options?.trivia != null) {
4494
- for (const key of Object.keys(definitions)) {
4495
- const rule = cache[key];
4496
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
4497
- }
4498
- }
4499
- if (options?.scanSkip != null) {
4500
- for (const key of Object.keys(definitions)) {
4501
- const rule = cache[key];
4502
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
4503
- }
4504
- }
4505
- if (options?.hostMode === "cst") {
4506
- for (const key of Object.keys(definitions)) {
4507
- const rule = cache[key];
4508
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
4509
- }
4510
- }
4511
- if (options?.trackLines === true) {
4512
- for (const key of Object.keys(definitions)) {
4513
- const rule = cache[key];
4514
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
4515
- }
4516
- for (const key of Object.keys(definitions)) {
4517
- const rule = cache[key];
4518
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
4519
- const wrapped = parser({ trackLines: true }, rule);
4520
- tagRule(wrapped, key);
4521
- wrapped._meta.grammarTrackLines = true;
4522
- cache[key] = wrapped;
4521
+ for (const key of keys) {
4522
+ const options = winners.get(key).options;
4523
+ let rule = cache[key];
4524
+ if (!rule._meta.isTrivia) {
4525
+ const meta = rule._meta;
4526
+ const trivia2 = link !== void 0 ? link.trivia : options?.trivia;
4527
+ if (trivia2 != null) meta.grammarTrivia = trivia2;
4528
+ if (options?.scanSkip != null) meta.grammarScanSkip = options.scanSkip;
4529
+ if ((link?.hostMode ?? options?.hostMode) === "cst") meta.grammarHostMode = "cst";
4530
+ if (options?.trackLines === true) {
4531
+ meta.grammarTrackLines = true;
4532
+ if (rule._def.tag !== "grammar") {
4533
+ rule = parser({ trackLines: true }, rule);
4534
+ tagRule(rule, key);
4535
+ rule._meta.grammarTrackLines = true;
4536
+ cache[key] = rule;
4537
+ }
4523
4538
  }
4524
4539
  }
4525
- }
4526
- for (const key of Object.keys(definitions)) {
4527
- markUnusedValues(cache[key]);
4540
+ markUnusedValues(rule);
4541
+ claimedRules.add(winners.get(key).parser).add(rule);
4528
4542
  }
4529
4543
  Object.defineProperty(cache, RULE_ORDER, {
4530
- value: Object.keys(definitions),
4544
+ value: keys,
4531
4545
  enumerable: false,
4532
4546
  configurable: true
4533
4547
  });
4534
- attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
4548
+ attachGrammarReflection(cache, collectGrammarReflection(keys.map((key) => [key, cache[key]])));
4535
4549
  return cache;
4536
4550
  }
4537
4551
 
@@ -5676,15 +5690,15 @@ function formatGatingWarnings(report) {
5676
5690
  return lines;
5677
5691
  }
5678
5692
  function ruleDependencies(ruleMap) {
5679
- const nameOf = /* @__PURE__ */ new Map();
5680
- for (const [name, comb] of ruleMap) nameOf.set(comb, name);
5693
+ const nameOf2 = /* @__PURE__ */ new Map();
5694
+ for (const [name, comb] of ruleMap) nameOf2.set(comb, name);
5681
5695
  const deps = /* @__PURE__ */ new Map();
5682
5696
  for (const [name, comb] of ruleMap) {
5683
5697
  const found = /* @__PURE__ */ new Set();
5684
5698
  const seen = /* @__PURE__ */ new Set();
5685
5699
  const walk = (p, isRoot) => {
5686
5700
  const def = p._def;
5687
- const boundary = def.tag === "lazy" ? nameOf.get(p) ?? p._ruleName : nameOf.get(p);
5701
+ const boundary = def.tag === "lazy" ? nameOf2.get(p) ?? p._ruleName : nameOf2.get(p);
5688
5702
  if (!isRoot && boundary !== void 0) {
5689
5703
  found.add(boundary);
5690
5704
  return;
@@ -15437,7 +15451,8 @@ function children(def, winners) {
15437
15451
  return [];
15438
15452
  }
15439
15453
  const name = resolved._ruleName;
15440
- return name && winners?.[name] ? [winners[name]] : [resolved];
15454
+ const winner = name ? winners?.[name] : void 0;
15455
+ return winner !== void 0 && winner._def !== def ? [winner] : [resolved];
15441
15456
  }
15442
15457
  default:
15443
15458
  return [];
@@ -15462,16 +15477,39 @@ function buildGrammarPlan(entry, winners) {
15462
15477
  }
15463
15478
  }
15464
15479
  }
15480
+ const selfWinnerBody = (parser2) => {
15481
+ if (parser2._def.tag !== "lazy" || winners === void 0) return void 0;
15482
+ const name = winnerNames.get(parser2);
15483
+ if (name === void 0 || winners[name] !== parser2) return void 0;
15484
+ try {
15485
+ return parser2._def.thunk();
15486
+ } catch {
15487
+ return void 0;
15488
+ }
15489
+ };
15490
+ const slotsPerBody = /* @__PURE__ */ new Map();
15491
+ if (winners) {
15492
+ for (const winner of Object.values(winners)) {
15493
+ const body = selfWinnerBody(winner);
15494
+ if (body !== void 0) slotsPerBody.set(body, (slotsPerBody.get(body) ?? 0) + 1);
15495
+ }
15496
+ }
15497
+ const sharedBody = (parser2) => (slotsPerBody.get(parser2) ?? 0) > 1;
15498
+ const ownRule2 = (parser2, rule) => {
15499
+ const id = `rule:${rule}`;
15500
+ definitions.set(id, { id, kind: "rule" });
15501
+ rules2.set(parser2, id);
15502
+ };
15465
15503
  const seen = /* @__PURE__ */ new Set();
15466
15504
  const visit = (parser2, path) => {
15467
15505
  if (seen.has(parser2)) return;
15468
15506
  seen.add(parser2);
15469
15507
  const rule = winnerNames.get(parser2) ?? parser2._ruleName;
15470
- if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
15471
- const id = `rule:${rule}`;
15472
- definitions.set(id, { id, kind: "rule" });
15473
- rules2.set(parser2, id);
15508
+ const body = selfWinnerBody(parser2);
15509
+ if (rule && body === void 0 && !sharedBody(parser2) && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
15510
+ ownRule2(parser2, rule);
15474
15511
  }
15512
+ if (rule && body !== void 0 && sharedBody(body)) ownRule2(parser2, rule);
15475
15513
  if (parser2._def.tag === "choice") {
15476
15514
  const ids = parser2._def.parsers.map((_, index) => `choice:${path}/arm:${index}`);
15477
15515
  choices.set(parser2, ids);
@@ -15492,7 +15530,13 @@ function buildGrammarPlan(entry, winners) {
15492
15530
  };
15493
15531
  const roots = Array.isArray(entry) ? entry : [entry];
15494
15532
  for (const root of roots) {
15495
- visit(root, winnerNames.get(root) ?? root._ruleName ?? "entry");
15533
+ const name = winnerNames.get(root) ?? root._ruleName ?? "entry";
15534
+ const body = selfWinnerBody(root);
15535
+ if (body !== void 0 && sharedBody(body) && !seen.has(root)) {
15536
+ seen.add(root);
15537
+ ownRule2(root, name);
15538
+ }
15539
+ visit(body ?? root, name);
15496
15540
  }
15497
15541
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
15498
15542
  }
@@ -16798,6 +16842,183 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
16798
16842
  return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16799
16843
  }
16800
16844
 
16845
+ // src/table/compile-rule-map.ts
16846
+ var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
16847
+ function resolveHostMode(ruleMap, explicit) {
16848
+ return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
16849
+ }
16850
+ function resolveTrackLines(ruleMap, explicit) {
16851
+ return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
16852
+ }
16853
+ function withAmbient(ruleMap, opts) {
16854
+ if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
16855
+ const wrappers = /* @__PURE__ */ new Map();
16856
+ return ruleMap.map(([name, rule]) => {
16857
+ if (rule._meta.isTrivia) return [name, rule];
16858
+ const addTrivia = opts.trivia !== void 0 && rule._meta.grammarTrivia === void 0;
16859
+ const addScanSkip = opts.scanSkip !== void 0 && rule._meta.grammarScanSkip === void 0;
16860
+ if (!addTrivia && !addScanSkip) return [name, rule];
16861
+ let wrapped = wrappers.get(rule);
16862
+ if (wrapped === void 0) {
16863
+ wrapped = {
16864
+ ...rule,
16865
+ _meta: {
16866
+ ...rule._meta,
16867
+ ...addTrivia ? { grammarTrivia: opts.trivia } : {},
16868
+ ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
16869
+ }
16870
+ };
16871
+ wrappers.set(rule, wrapped);
16872
+ }
16873
+ return [name, wrapped];
16874
+ });
16875
+ }
16876
+ function compileRuleMapRunnable(ruleMap, opts = {}) {
16877
+ const encoded = encodeForRun(ruleMap, opts);
16878
+ if (encoded === null) return null;
16879
+ const { prog, hostMode, plan } = encoded;
16880
+ const artifact = closureArtifact(prog);
16881
+ const reflection = collectGrammarReflection(ruleMap);
16882
+ return {
16883
+ keys: ruleMap.map(([key]) => key),
16884
+ hostMode,
16885
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
16886
+ reflection,
16887
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16888
+ // Reflection rides on the map's metadata prototype from birth. Only `compile()`
16889
+ // asks to specialise; every other caller runs the closure artifact.
16890
+ rules: tableRules(opts.specialise === true ? prog : artifact, { [GRAMMAR_REFLECTION]: reflection }),
16891
+ prog: artifact
16892
+ };
16893
+ }
16894
+ function encodeForRun(ruleMap, opts) {
16895
+ runDuplicationDiagnosticRules(ruleMap, opts.duplication);
16896
+ const encodeMap = withAmbient(ruleMap, opts);
16897
+ const plan = opts.coverage === true ? buildGrammarPlan(
16898
+ encodeMap.map(([, rule]) => rule),
16899
+ Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== "lazy"))
16900
+ ) : void 0;
16901
+ const hostMode = resolveHostMode(ruleMap, opts.hostMode);
16902
+ const settings = {
16903
+ hostMode,
16904
+ ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
16905
+ ...opts.recovery === true ? { recovery: true } : {},
16906
+ ...plan === void 0 ? {} : { coverage: plan }
16907
+ };
16908
+ try {
16909
+ const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
16910
+ return { prog, fnSrcs, hostMode, plan };
16911
+ } catch (e) {
16912
+ opts.refusals?.push(e instanceof Error ? e.message : String(e));
16913
+ return null;
16914
+ }
16915
+ }
16916
+ function compileRuleMap(ruleMap, opts = {}) {
16917
+ const encoded = encodeForRun(ruleMap, opts);
16918
+ if (encoded === null) return null;
16919
+ const { prog, fnSrcs, hostMode, plan } = encoded;
16920
+ const artifact = closureArtifact(prog);
16921
+ if (artifact.runtimeOnly !== void 0 && artifact.runtimeOnly.length > 0) {
16922
+ opts.refusals?.push(...artifact.runtimeOnly);
16923
+ return null;
16924
+ }
16925
+ const supplied = opts.fnSources;
16926
+ const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null);
16927
+ if (sources.some((s) => s === null)) {
16928
+ const missing = sources.flatMap((s, i) => s === null ? [i] : []);
16929
+ opts.refusals?.push(
16930
+ `author reducer source missing for prog.fns[${missing.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`
16931
+ );
16932
+ return null;
16933
+ }
16934
+ if (supplied !== void 0 && supplied.length > fnSrcs.length) {
16935
+ throw new Error(
16936
+ `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.`
16937
+ );
16938
+ }
16939
+ const replacementWithMetadata = (metadataSource, options = {}) => emitTableExpression(artifact, {
16940
+ entry: null,
16941
+ runtimeRef: opts.runtimeRef ?? "tableRules",
16942
+ fnSources: sources,
16943
+ ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
16944
+ ...metadataSource === void 0 ? {} : { metadataSource }
16945
+ });
16946
+ const replacement = replacementWithMetadata();
16947
+ return {
16948
+ keys: ruleMap.map(([key]) => key),
16949
+ replacement,
16950
+ replacementWithMetadata,
16951
+ hostMode,
16952
+ // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
16953
+ // dropped", which is what the driver's `'ast' artifact + CST host` check keys
16954
+ // off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
16955
+ // direct builder, so there was no branch to drop and it stays usable with either
16956
+ // host — the long-standing `node(parser)` contract. Same predicate codegen uses
16957
+ // for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
16958
+ // lowerings stamp the same artifact rather than disagreeing about it.
16959
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
16960
+ reflection: collectGrammarReflection(ruleMap),
16961
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16962
+ rules: tableRules(artifact),
16963
+ prog: artifact
16964
+ };
16965
+ }
16966
+
16967
+ // src/compiler/borrowed-rules.ts
16968
+ var nameOf = (c) => c._ruleName;
16969
+ function deref(c) {
16970
+ const seen = /* @__PURE__ */ new Set();
16971
+ let current = c;
16972
+ while (current._def.tag === "lazy" && !seen.has(current)) {
16973
+ seen.add(current);
16974
+ try {
16975
+ current = current._def.thunk();
16976
+ } catch {
16977
+ return void 0;
16978
+ }
16979
+ }
16980
+ return current;
16981
+ }
16982
+ function borrowedRuleReference(map) {
16983
+ const seen = /* @__PURE__ */ new Set();
16984
+ for (const [rule, self] of Object.entries(map)) {
16985
+ let found;
16986
+ const visit = (c) => {
16987
+ if (found !== void 0) return;
16988
+ if (c._def.tag === "lazy") {
16989
+ if (!winnerWrapsReference(self, c)) {
16990
+ const name = nameOf(c);
16991
+ const winner = name !== void 0 && Object.hasOwn(map, name) ? map[name] : void 0;
16992
+ if (winner !== void 0) {
16993
+ if (winnerWrapsReference(winner, c) || deref(c) === deref(winner)) return;
16994
+ found = name;
16995
+ return;
16996
+ }
16997
+ }
16998
+ if (seen.has(c)) return;
16999
+ seen.add(c);
17000
+ let body;
17001
+ try {
17002
+ body = c._def.thunk();
17003
+ } catch {
17004
+ return;
17005
+ }
17006
+ visit(body);
17007
+ return;
17008
+ }
17009
+ if (seen.has(c)) return;
17010
+ seen.add(c);
17011
+ for (const child of childrenOf2(c._def)) visit(child);
17012
+ };
17013
+ visit(self);
17014
+ if (found !== void 0) return { rule, reference: found };
17015
+ }
17016
+ return void 0;
17017
+ }
17018
+ function borrowedRuleMessage(api, hit) {
17019
+ 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.`;
17020
+ }
17021
+
16801
17022
  // src/table/compile.ts
16802
17023
  var ENTRY = "Entry";
16803
17024
  function ownSource(fn) {
@@ -16807,7 +17028,47 @@ function ownSource(fn) {
16807
17028
  const name = fn.name;
16808
17029
  return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
16809
17030
  }
16810
- function compile(combinator, mapFnSources, opts = {}) {
17031
+ function compile(target, second, third) {
17032
+ if (!isCombinator(target)) return compileMap(target, second);
17033
+ return compileRoot(target, second, third);
17034
+ }
17035
+ function isCombinator(x) {
17036
+ return typeof x === "object" && x !== null && typeof x.parse === "function" && "_def" in x;
17037
+ }
17038
+ function compileMap(grammar, opts = {}) {
17039
+ const entries = [];
17040
+ for (const [key, value] of Object.entries(grammar)) {
17041
+ if (!isCombinator(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
17042
+ if (value._def.tag === "lazy") {
17043
+ try {
17044
+ value._def.thunk();
17045
+ } catch {
17046
+ continue;
17047
+ }
17048
+ }
17049
+ entries.push([key, value]);
17050
+ }
17051
+ const borrowed = borrowedRuleReference(Object.fromEntries(entries));
17052
+ if (borrowed !== void 0) throw new Error(borrowedRuleMessage("compile", borrowed));
17053
+ const drain = beginCompileDegradationDrain();
17054
+ let done = false;
17055
+ try {
17056
+ const refusals = [];
17057
+ const compiled = compileRuleMapRunnable(entries, {
17058
+ ...opts.hostMode ? { hostMode: opts.hostMode } : {},
17059
+ refusals,
17060
+ specialise: true
17061
+ });
17062
+ if (compiled === null) {
17063
+ throw new Error(`compile: this grammar could not be encoded to a table${refusals.length ? ` \u2014 ${refusals.join("; ")}` : ""}`);
17064
+ }
17065
+ done = true;
17066
+ return compiled.rules;
17067
+ } finally {
17068
+ drain(done);
17069
+ }
17070
+ }
17071
+ function compileRoot(combinator, mapFnSources, opts = {}) {
16811
17072
  runDuplicationDiagnostic(combinator, opts.duplication);
16812
17073
  const drain = beginCompileDegradationDrain();
16813
17074
  let compiled = false;
@@ -16877,29 +17138,6 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
16877
17138
  };
16878
17139
  }
16879
17140
 
16880
- // src/combinators/withCtx.ts
16881
- function withCtx(extra, combinator) {
16882
- const meta = {
16883
- firstSet: combinator._meta.firstSet,
16884
- canMatchNewline: combinator._meta.canMatchNewline,
16885
- isTrivia: false
16886
- };
16887
- return {
16888
- _tag: "withCtx",
16889
- _meta: meta,
16890
- _def: { tag: "withCtx", extra, parser: combinator },
16891
- parse(input, pos, ctx) {
16892
- const saved = ctx.state;
16893
- ctx.state = extra;
16894
- try {
16895
- return combinator.parse(input, pos, ctx);
16896
- } finally {
16897
- ctx.state = saved;
16898
- }
16899
- }
16900
- };
16901
- }
16902
-
16903
17141
  // src/compiler/ir-serialize.ts
16904
17142
  function childrenOf3(def) {
16905
17143
  switch (def.tag) {
@@ -16996,176 +17234,6 @@ function serializeRuleMap(ruleMap, scanSkip) {
16996
17234
  throw e;
16997
17235
  }
16998
17236
  }
16999
- function evalRuleMapIR(ir) {
17000
- const _tf = (child, src, recognitionOnly = false) => {
17001
- let fn2;
17002
- try {
17003
- fn2 = (0, eval)(`(${src})`);
17004
- } catch {
17005
- fn2 = () => {
17006
- throw new Error("IR transform fn not materialized");
17007
- };
17008
- }
17009
- const t = transform(child, fn2);
17010
- t._def.fnSrc = src;
17011
- if (recognitionOnly) t._def.recognitionOnly = true;
17012
- return t;
17013
- };
17014
- const _lf = (child, src, source = false) => {
17015
- let fn2;
17016
- try {
17017
- fn2 = (0, eval)(`(${src})`);
17018
- } catch {
17019
- fn2 = () => {
17020
- throw new Error("IR leaf fn not materialized");
17021
- };
17022
- }
17023
- const l = source ? sourceLeaf(child, fn2) : leaf(child, fn2);
17024
- l._def.fnSrc = src;
17025
- return l;
17026
- };
17027
- const _nd = (type, child, src, opts, staticError, sigSrc, buildImports, rawUnused) => {
17028
- if (staticError !== void 0 && staticError.length > 0) {
17029
- throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
17030
- }
17031
- const n = node(type, child, (() => {
17032
- throw new Error("IR node build requires static re-lowering");
17033
- }), opts);
17034
- n._def.buildSrc = src;
17035
- if (staticError !== void 0) n._def.buildStaticError = staticError;
17036
- if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
17037
- if (rawUnused === true) n._def.buildRawUnused = true;
17038
- if (buildImports !== void 0) n._def.buildImports = buildImports;
17039
- return n;
17040
- };
17041
- const _gch = (items) => {
17042
- const gateSrcs = [];
17043
- const arms = items.map((it) => {
17044
- if (Array.isArray(it)) {
17045
- const [src, comb] = it;
17046
- gateSrcs.push(src);
17047
- let gate2;
17048
- try {
17049
- gate2 = (0, eval)(`(${src})`);
17050
- } catch {
17051
- gate2 = () => {
17052
- throw new Error("IR gate fn not materialized");
17053
- };
17054
- }
17055
- return { gate: gate2, combinator: comb };
17056
- }
17057
- gateSrcs.push(null);
17058
- return it;
17059
- });
17060
- const c = choice(...arms);
17061
- const gates = c._def.gates;
17062
- if (gates && gates.length !== gateSrcs.length) {
17063
- throw new Error(`_gch: gateSrcs length ${gateSrcs.length} != choice gates length ${gates.length}`);
17064
- }
17065
- ;
17066
- c._def.gateSrcs = gateSrcs;
17067
- return c;
17068
- };
17069
- const _wc = (src, inner) => {
17070
- let extra;
17071
- try {
17072
- extra = (0, eval)(`(${src})`);
17073
- } catch {
17074
- extra = void 0;
17075
- }
17076
- const w = withCtx(extra, inner);
17077
- w._def.extraSrc = src;
17078
- return w;
17079
- };
17080
- const fn = new Function(
17081
- "rules",
17082
- "ref",
17083
- "regex",
17084
- "literal",
17085
- "keywords",
17086
- "sequence",
17087
- "choice",
17088
- "dispatch",
17089
- "when",
17090
- "startsWith",
17091
- "endsWith",
17092
- "matches",
17093
- "otherwise",
17094
- "routed",
17095
- "attempt",
17096
- "many",
17097
- "oneOrMore",
17098
- "optional",
17099
- "sepBy",
17100
- "keepSeparator",
17101
- "not",
17102
- "peek",
17103
- "node",
17104
- "parser",
17105
- "scanTo",
17106
- "balanced",
17107
- "token",
17108
- "leaf",
17109
- "transform",
17110
- "trivia",
17111
- "classifiedTrivia",
17112
- "label",
17113
- "field",
17114
- "expect",
17115
- "adjacent",
17116
- "notAdjacent",
17117
- "_tf",
17118
- "_lf",
17119
- "_nd",
17120
- "_gch",
17121
- "_wc",
17122
- `return (${ir})`
17123
- );
17124
- const map = fn(
17125
- rules,
17126
- ref,
17127
- regex,
17128
- literal,
17129
- keywords,
17130
- sequence,
17131
- choice,
17132
- dispatch,
17133
- when,
17134
- startsWith,
17135
- endsWith,
17136
- matches,
17137
- otherwise,
17138
- routed,
17139
- attempt,
17140
- many,
17141
- oneOrMore,
17142
- optional,
17143
- sepBy,
17144
- keepSeparator,
17145
- not,
17146
- peek,
17147
- node,
17148
- parser,
17149
- scanTo,
17150
- balanced,
17151
- token,
17152
- leaf,
17153
- transform,
17154
- trivia,
17155
- classifiedTrivia,
17156
- label,
17157
- field,
17158
- expect,
17159
- adjacent,
17160
- notAdjacent,
17161
- _tf,
17162
- _lf,
17163
- _nd,
17164
- _gch,
17165
- _wc
17166
- );
17167
- return Object.entries(map);
17168
- }
17169
17237
  var Serializer = class {
17170
17238
  counts = /* @__PURE__ */ new Map();
17171
17239
  selfRef = /* @__PURE__ */ new Set();
@@ -17492,127 +17560,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17492
17560
  }
17493
17561
  };
17494
17562
 
17495
- // src/table/compile-rule-map.ts
17496
- var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
17497
- function resolveHostMode(ruleMap, explicit) {
17498
- return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
17499
- }
17500
- function resolveTrackLines(ruleMap, explicit) {
17501
- return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
17502
- }
17503
- function withAmbient(ruleMap, opts) {
17504
- if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
17505
- const wrappers = /* @__PURE__ */ new Map();
17506
- return ruleMap.map(([name, rule]) => {
17507
- if (rule._meta.isTrivia) return [name, rule];
17508
- const addTrivia = opts.trivia !== void 0 && rule._meta.grammarTrivia === void 0;
17509
- const addScanSkip = opts.scanSkip !== void 0 && rule._meta.grammarScanSkip === void 0;
17510
- if (!addTrivia && !addScanSkip) return [name, rule];
17511
- let wrapped = wrappers.get(rule);
17512
- if (wrapped === void 0) {
17513
- wrapped = {
17514
- ...rule,
17515
- _meta: {
17516
- ...rule._meta,
17517
- ...addTrivia ? { grammarTrivia: opts.trivia } : {},
17518
- ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
17519
- }
17520
- };
17521
- wrappers.set(rule, wrapped);
17522
- }
17523
- return [name, wrapped];
17524
- });
17525
- }
17526
- function compileRuleMapRunnable(ruleMap, opts = {}) {
17527
- const encoded = encodeForRun(ruleMap, opts);
17528
- if (encoded === null) return null;
17529
- const { prog, hostMode, plan } = encoded;
17530
- const artifact = closureArtifact(prog);
17531
- return {
17532
- keys: ruleMap.map(([key]) => key),
17533
- hostMode,
17534
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17535
- reflection: collectGrammarReflection(ruleMap),
17536
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17537
- rules: tableRules(prog),
17538
- prog: artifact
17539
- };
17540
- }
17541
- function encodeForRun(ruleMap, opts) {
17542
- runDuplicationDiagnosticRules(ruleMap, opts.duplication);
17543
- const encodeMap = withAmbient(ruleMap, opts);
17544
- const plan = opts.coverage === true ? buildGrammarPlan(
17545
- encodeMap.map(([, rule]) => rule),
17546
- Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== "lazy"))
17547
- ) : void 0;
17548
- const hostMode = resolveHostMode(ruleMap, opts.hostMode);
17549
- const settings = {
17550
- hostMode,
17551
- ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
17552
- ...opts.recovery === true ? { recovery: true } : {},
17553
- ...plan === void 0 ? {} : { coverage: plan }
17554
- };
17555
- try {
17556
- const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
17557
- return { prog, fnSrcs, hostMode, plan };
17558
- } catch (e) {
17559
- opts.refusals?.push(e instanceof Error ? e.message : String(e));
17560
- return null;
17561
- }
17562
- }
17563
- function compileRuleMap(ruleMap, opts = {}) {
17564
- const encoded = encodeForRun(ruleMap, opts);
17565
- if (encoded === null) return null;
17566
- const { prog, fnSrcs, hostMode, plan } = encoded;
17567
- const artifact = closureArtifact(prog);
17568
- if (artifact.runtimeOnly !== void 0 && artifact.runtimeOnly.length > 0) {
17569
- opts.refusals?.push(...artifact.runtimeOnly);
17570
- return null;
17571
- }
17572
- const supplied = opts.fnSources;
17573
- const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null);
17574
- if (sources.some((s) => s === null)) {
17575
- const missing = sources.flatMap((s, i) => s === null ? [i] : []);
17576
- opts.refusals?.push(
17577
- `author reducer source missing for prog.fns[${missing.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`
17578
- );
17579
- return null;
17580
- }
17581
- if (supplied !== void 0 && supplied.length > fnSrcs.length) {
17582
- throw new Error(
17583
- `compileRuleMap: got ${supplied.length} fnSources for a pool of ${fnSrcs.length}. The list is positional, in prog.fns order, so a longer one means it belongs to a different encode \u2014 and the entries would be silently misassigned.`
17584
- );
17585
- }
17586
- const replacementWithMetadata = (metadataSource, options = {}) => emitTableExpression(artifact, {
17587
- entry: null,
17588
- runtimeRef: opts.runtimeRef ?? "tableRules",
17589
- fnSources: sources,
17590
- ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
17591
- ...metadataSource === void 0 ? {} : { metadataSource }
17592
- });
17593
- const replacement = replacementWithMetadata();
17594
- return {
17595
- keys: ruleMap.map(([key]) => key),
17596
- replacement,
17597
- replacementWithMetadata,
17598
- hostMode,
17599
- // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
17600
- // dropped", which is what the driver's `'ast' artifact + CST host` check keys
17601
- // off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
17602
- // direct builder, so there was no branch to drop and it stays usable with either
17603
- // host — the long-standing `node(parser)` contract. Same predicate codegen uses
17604
- // for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
17605
- // lowerings stamp the same artifact rather than disagreeing about it.
17606
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17607
- reflection: collectGrammarReflection(ruleMap),
17608
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17609
- rules: tableRules(prog),
17610
- prog: artifact
17611
- };
17612
- }
17613
-
17614
17563
  // src/version.ts
17615
- var PARSEMAN_VERSION = "0.52.0";
17564
+ var PARSEMAN_VERSION = "0.53.0";
17616
17565
 
17617
17566
  // src/compiler/compile-linkable-table.ts
17618
17567
  function isLocal(rule) {
@@ -17680,16 +17629,6 @@ function compileLinkableTable(ruleMapArg, ns, opts = {}) {
17680
17629
  }
17681
17630
 
17682
17631
  // src/compiler/linker.ts
17683
- var _nsCounter = 0;
17684
- function linkable(rulesMap, ns, trivia2, hostMode) {
17685
- const piece = compileLinkableTable(
17686
- Object.entries(rulesMap),
17687
- ns ?? `_lk${_nsCounter++}_`,
17688
- { ...trivia2 ? { trivia: trivia2 } : {}, ...hostMode ? { hostMode } : {} }
17689
- );
17690
- if (!piece) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
17691
- return piece;
17692
- }
17693
17632
  function normalizeCstCollapse(collapse) {
17694
17633
  if (collapse === true) return () => true;
17695
17634
  if (Array.isArray(collapse)) {
@@ -17713,107 +17652,6 @@ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triv
17713
17652
  return host;
17714
17653
  }
17715
17654
  cstBuildHost._parsemanCstOutput = true;
17716
- function fuseCarried(carried, trivia2, hostMode) {
17717
- const maps = [];
17718
- for (const p of carried) {
17719
- if (!isIRPiece(p)) {
17720
- if (typeof p.v !== "string") {
17721
- throw new Error(
17722
- `parseman: artifact "${p.ns}" is UNSTAMPED (compiled before the version-lock invariant). Recompile the grammar with parseman ${PARSEMAN_VERSION}; parseman does not fuse unversioned or cross-version artifacts.`
17723
- );
17724
- }
17725
- if (p.v !== PARSEMAN_VERSION) {
17726
- throw new Error(
17727
- `parseman: artifact "${p.ns}" was compiled with parseman ${p.v}, but is being fused with parseman ${PARSEMAN_VERSION}. Compiled grammar artifacts are version-locked \u2014 recompile the grammar with parseman ${PARSEMAN_VERSION}; parseman does not fuse across versions.`
17728
- );
17729
- }
17730
- }
17731
- const rules2 = ruleMapOfCarried(p);
17732
- if (rules2 === void 0) {
17733
- throw new Error(`compose: carried piece "${p.ns}" has no re-lowerable IR and cannot be fused`);
17734
- }
17735
- maps.push(rules2);
17736
- }
17737
- const merged = mergeCarriedRuleMaps(maps);
17738
- materializeDirectBuilders(merged);
17739
- if (trivia2) {
17740
- for (const [, rule] of merged) {
17741
- if (rule._meta.isTrivia) continue;
17742
- rule._meta.grammarTrivia = trivia2;
17743
- }
17744
- }
17745
- const refusals = [];
17746
- const compiled = compileRuleMapRunnable(merged, {
17747
- ...trivia2 ? { trivia: trivia2 } : {},
17748
- ...hostMode ? { hostMode } : {},
17749
- refusals
17750
- });
17751
- if (compiled === null) {
17752
- throw new Error(`compose: the merged grammar could not be encoded to a table${refusals.length ? ` \u2014 ${refusals.join("; ")}` : ""}`);
17753
- }
17754
- const map = tableRules(compiled.prog);
17755
- for (const k of Object.keys(map)) {
17756
- Object.defineProperty(map[k], FUSED_HOST_MODE, { value: compiled.hostMode, enumerable: false });
17757
- Object.defineProperty(map[k], FUSED_HOST_ELIDED, { value: compiled.hostBranchElided, enumerable: false });
17758
- }
17759
- Object.defineProperty(map, FUSED_HOST_MODE, { value: compiled.hostMode, enumerable: false });
17760
- Object.defineProperty(map, FUSED_HOST_ELIDED, { value: compiled.hostBranchElided, enumerable: false });
17761
- Object.defineProperty(map, GRAMMAR_REFLECTION, { value: compiled.reflection, enumerable: false });
17762
- return map;
17763
- }
17764
- function materializeDirectBuilders(ruleMap) {
17765
- const seen = /* @__PURE__ */ new Set();
17766
- const visit = (p) => {
17767
- if (seen.has(p)) return;
17768
- seen.add(p);
17769
- const d = p._def;
17770
- if (d.tag === "node" && typeof d.buildSrc === "string") {
17771
- if (d.buildImports !== void 0 && d.buildImports.length > 0) {
17772
- throw new Error(
17773
- `IR direct node builder for ${d.type ?? "<anonymous>"} references module import(s) ${d.buildImports.map((bi) => bi.local).join(", ")} that a runtime compose() cannot supply; compose at build time via the parseman macro plugin, which re-emits the imports.`
17774
- );
17775
- }
17776
- try {
17777
- const fn = (0, eval)(`(${d.buildSrc})`);
17778
- if (typeof fn === "function") d.build = fn;
17779
- } catch {
17780
- }
17781
- }
17782
- if (d.tag === "lazy") {
17783
- let resolved;
17784
- try {
17785
- resolved = d.thunk();
17786
- } catch {
17787
- return;
17788
- }
17789
- if (resolved) visit(resolved);
17790
- return;
17791
- }
17792
- for (const child of childrenOf2(p._def)) visit(child);
17793
- };
17794
- for (const [, rule] of ruleMap) visit(rule);
17795
- }
17796
- function ruleMapOfCarried(p) {
17797
- if (isIRPiece(p)) return evalRuleMapIR(p.ir);
17798
- if (p.ir !== null) return evalRuleMapIR(p.ir);
17799
- return p.ruleMap.length > 0 ? p.ruleMap.map(([k, v]) => [k, v]) : void 0;
17800
- }
17801
- function mergeCarriedRuleMaps(maps) {
17802
- const winners = /* @__PURE__ */ new Map();
17803
- for (const map of maps) {
17804
- for (const [name, rule] of map) {
17805
- if (rule._def.tag === "lazy") {
17806
- try {
17807
- rule._def.thunk();
17808
- } catch {
17809
- continue;
17810
- }
17811
- }
17812
- winners.set(name, rule);
17813
- }
17814
- }
17815
- return [...winners];
17816
- }
17817
17655
  function fusedHostModeOf(registry) {
17818
17656
  const m = registry[FUSED_HOST_MODE];
17819
17657
  return m === "cst" ? "cst" : "ast";
@@ -17828,13 +17666,17 @@ function composedPiecesOf(grammar) {
17828
17666
  }
17829
17667
  var LEAF_COMPOSED = /* @__PURE__ */ Symbol.for("parseman.leafComposed");
17830
17668
  var COMPOSED_TRIVIA = /* @__PURE__ */ Symbol.for("parseman.composedTrivia");
17831
- var COMPOSED_COVERAGE_RULES = /* @__PURE__ */ Symbol.for("parseman.composedCoverageRules");
17669
+ var LINKED = /* @__PURE__ */ Symbol.for("parseman.linkedComposition");
17832
17670
  function isIRPiece(p) {
17833
17671
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("keys" in p);
17834
17672
  }
17835
17673
  function isLinkableTable(p) {
17836
17674
  return !!p && typeof p === "object" && typeof p.ns === "string" && Array.isArray(p.keys);
17837
17675
  }
17676
+ function ruleMapOfCarried(p) {
17677
+ if (isIRPiece(p)) return void 0;
17678
+ return p.ruleMap.length > 0 ? p.ruleMap.map(([k, v]) => [k, v]) : void 0;
17679
+ }
17838
17680
  function carriedRuleMapsDetailed(carried) {
17839
17681
  const maps = [];
17840
17682
  const opaque = [];
@@ -17865,250 +17707,97 @@ function recoverComposedRules(grammar) {
17865
17707
  }
17866
17708
  return { rules: rules2, opaque };
17867
17709
  }
17868
- function coverageRulesOf(carried) {
17869
- const winners = {};
17870
- for (const piece of carried) {
17871
- if (!isIRPiece(piece)) return void 0;
17872
- const map = evalRuleMapIR(piece.ir);
17873
- for (const [name, rule] of map) {
17874
- if (rule._def.tag === "lazy") {
17875
- try {
17876
- rule._def.thunk();
17877
- } catch {
17878
- continue;
17879
- }
17880
- }
17881
- winners[name] = rule;
17882
- }
17883
- }
17884
- return winners;
17885
- }
17886
17710
  function composedCoverageRules(grammar) {
17887
- return grammar[COMPOSED_COVERAGE_RULES];
17888
- }
17889
- function nextComposeNs(used) {
17890
- let ns;
17891
- do {
17892
- ns = `_lk${_nsCounter++}_`;
17893
- } while (used.has(ns));
17894
- used.add(ns);
17895
- return ns;
17896
- }
17897
- function itemCarried(item, used, trivia2, hostMode) {
17898
- const carried = item[COMPOSED_PIECES];
17899
- if (Array.isArray(carried)) {
17900
- const items = carried;
17901
- for (const p of items) used.add(p.ns);
17902
- return items;
17903
- }
17904
- if (isLinkableTable(item)) {
17905
- used.add(item.ns);
17906
- return [item];
17907
- }
17908
- const map = item;
17909
- const ns = nextComposeNs(used);
17910
- const entries = Object.entries(map).filter(([, val]) => {
17911
- const d = val._def;
17912
- if (d.tag !== "lazy") return true;
17913
- try {
17914
- d.thunk();
17915
- return true;
17916
- } catch {
17917
- return false;
17918
- }
17919
- });
17920
- const scanSkip = entries.map(([, val]) => val._meta.grammarScanSkip).find(Boolean);
17921
- const ir = serializeRuleMap(entries, scanSkip);
17922
- return ir ? [{ ns, ir }] : [linkable(map, ns, trivia2, hostMode)];
17711
+ const link = grammar[LINKED];
17712
+ return link?.();
17713
+ }
17714
+ var LINKED_WINNERS = /* @__PURE__ */ new WeakMap();
17715
+ function linkedWinnersOf(rule) {
17716
+ return LINKED_WINNERS.get(rule);
17717
+ }
17718
+ function recipesOf(item) {
17719
+ const recipes = item[RULES_RECIPE];
17720
+ if (Array.isArray(recipes)) return recipes;
17721
+ const what = isLinkableTable(item) ? `the table artifact "${item.ns}"` : composedPiecesOf(item) !== void 0 ? "a build-compiled compose() result" : "a value that is not a rules() grammar";
17722
+ throw new Error(
17723
+ `compose: ${what} has no live rules to link. At runtime compose() links interpreter grammars \u2014 rules() maps and runtime compose() results \u2014 and never evaluates carried IR or source text. Compose compiled grammars at build time (the parseman macro does), or compose the interpreter builds of both.`
17724
+ );
17923
17725
  }
17924
17726
  function composingTriviaOf(items) {
17925
17727
  for (let i = items.length - 1; i >= 0; i--) {
17926
17728
  const item = items[i];
17927
- if (!item || isLinkableTable(item)) continue;
17928
- const stamped = item[COMPOSED_TRIVIA];
17929
- if (stamped) return stamped;
17930
- if (item[COMPOSED_PIECES]) continue;
17931
- for (const v of Object.values(item)) {
17932
- const t = v?._meta?.grammarTrivia;
17933
- if (t) return t;
17729
+ if (item[LINKED] !== void 0) {
17730
+ const stamped = item[COMPOSED_TRIVIA];
17731
+ if (stamped) return stamped;
17732
+ continue;
17934
17733
  }
17734
+ const recipes = item[RULES_RECIPE];
17735
+ const trivia2 = recipes?.[recipes.length - 1]?.options?.trivia;
17736
+ if (trivia2 != null) return trivia2;
17935
17737
  }
17936
17738
  return void 0;
17937
17739
  }
17938
- function compose(items, opts) {
17939
- if (items.some((item) => item[LEAF_COMPOSED] === true)) {
17940
- throw new Error("compose: a composeLeaf() result is terminal and cannot be composed again");
17941
- }
17942
- const used = /* @__PURE__ */ new Set();
17943
- const trivia2 = composingTriviaOf(items);
17944
- const carried = items.flatMap((item) => itemCarried(item, used, trivia2, opts?.hostMode));
17945
- const map = fuseCarried(carried, trivia2, opts?.hostMode);
17946
- Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
17947
- if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
17948
- const coverageRules = coverageRulesOf(carried);
17949
- if (coverageRules) Object.defineProperty(map, COMPOSED_COVERAGE_RULES, { value: coverageRules, enumerable: false });
17950
- return map;
17951
- }
17952
- function composeLeaf(items) {
17953
- const pieces = items.flatMap(interpretedPieces);
17954
- let fused;
17955
- const map = {};
17956
- for (const name of ruleNamesOf(pieces)) {
17957
- Object.defineProperty(map, name, {
17958
- enumerable: true,
17959
- configurable: true,
17960
- get: () => (fused ??= fusePieces(pieces))[name]
17961
- });
17962
- }
17963
- Object.defineProperty(map, LEAF_COMPOSED, { value: true, enumerable: false });
17964
- Object.defineProperty(map, INTERPRETED_PIECES, {
17965
- value: pieces.filter((p) => p.plain).map((p) => p.entries),
17966
- enumerable: false
17967
- });
17968
- return map;
17969
- }
17970
- function isRefSlot(c) {
17971
- return c._def.tag === "lazy" && typeof c.define === "function";
17972
- }
17973
- var FUSE_ORIGINAL = /* @__PURE__ */ Symbol.for("parseman.interpretedFuseOriginal");
17974
- var FUSE_TARGET = /* @__PURE__ */ Symbol.for("parseman.interpretedFuseTarget");
17975
- var INTERPRETED_PIECES = /* @__PURE__ */ Symbol.for("parseman.interpretedPieces");
17976
- function definitionOf(entry) {
17977
- if (isRefSlot(entry)) {
17978
- const original = entry[FUSE_ORIGINAL];
17979
- if (original !== void 0) return original ?? void 0;
17980
- try {
17981
- return entry._def.thunk();
17982
- } catch {
17983
- return void 0;
17984
- }
17985
- }
17986
- if (entry._def.tag === "lazy") {
17987
- try {
17988
- entry._def.thunk();
17989
- } catch {
17990
- return void 0;
17991
- }
17740
+ function declaredNamesOf(items) {
17741
+ const names = /* @__PURE__ */ new Set();
17742
+ for (const item of items) {
17743
+ const linked = item[LINKED] !== void 0;
17744
+ const order = linked ? Object.keys(item) : isLinkableTable(item) ? item.keys : item[RULE_ORDER];
17745
+ for (const name of order ?? Object.keys(item)) names.add(name);
17992
17746
  }
17993
- return entry;
17747
+ return [...names];
17994
17748
  }
17995
- function repointRef(slot, target, name) {
17996
- const tagged = slot;
17997
- const bound = tagged[FUSE_TARGET];
17998
- const original = FUSE_ORIGINAL in tagged ? tagged[FUSE_ORIGINAL] : (() => {
17749
+ function linkComposition(recipes, link) {
17750
+ const map = linkRules(recipes, link);
17751
+ const missing = [];
17752
+ for (const [name, rule] of Object.entries(map)) {
17753
+ if (rule._def.tag !== "lazy") continue;
17999
17754
  try {
18000
- return slot._def.thunk();
17755
+ rule._def.thunk();
18001
17756
  } catch {
18002
- return null;
18003
- }
18004
- })();
18005
- if ((bound ?? original) === target) return;
18006
- if (bound !== void 0) {
18007
- throw new Error(
18008
- `fuseInterpreted: rule "${name}" is already bound by a DIFFERENT interpreted fusion of the same grammar piece. An interpreted fuse binds the shared placeholder objects in place, so two fusions cannot share a piece \u2014 build a fresh instance of the piece (call its rules() factory again, or import the module under a distinct specifier) for the second fusion.`
18009
- );
18010
- }
18011
- if (!(FUSE_ORIGINAL in tagged)) Object.defineProperty(slot, FUSE_ORIGINAL, { value: original, enumerable: false });
18012
- Object.defineProperty(slot, FUSE_TARGET, { value: target, enumerable: false });
18013
- slot._def.thunk = () => target;
18014
- slot.parse = (input, pos, ctx) => target.parse(input, pos, ctx);
18015
- const meta = slot._meta;
18016
- meta.firstSet = target._meta.firstSet;
18017
- meta.canMatchNewline = target._meta.canMatchNewline;
18018
- meta.isTrivia = target._meta.isTrivia;
18019
- if (target._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = target._meta.triviaKindLabels;
18020
- else meta.triviaKindLabels = void 0;
18021
- if (target._meta.disjoint !== void 0) meta.disjoint = target._meta.disjoint;
18022
- else meta.disjoint = void 0;
18023
- }
18024
- function interpretedPieces(item) {
18025
- const fused = item[INTERPRETED_PIECES];
18026
- if (Array.isArray(fused)) return fused.map((entries) => ({ entries, plain: false }));
18027
- const carried = composedPiecesOf(item);
18028
- if (carried !== void 0) {
18029
- const { maps, opaque } = carriedRuleMapsDetailed(carried);
18030
- if (opaque.length > 0) {
18031
- throw new Error(
18032
- `fuseInterpreted: cannot interpret a composed grammar containing precompiled artifact(s) ${opaque.map((o) => `"${o.ns}" (${o.ruleNames.length} rules)`).join(", ")} \u2014 they carry compiled functions, not a combinator graph. Pass the source grammars (the same items you passed to compose()) instead.`
18033
- );
17757
+ missing.push(name);
18034
17758
  }
18035
- return maps.map((entries) => ({ entries, plain: false }));
18036
17759
  }
18037
- if (isLinkableTable(item)) {
18038
- const rules2 = ruleMapOfCarried(item);
18039
- if (rules2 === void 0) {
18040
- throw new Error("fuseInterpreted: a precompiled linkable artifact with no carried IR has no combinator graph to interpret; pass the source grammar (a rules() map) instead");
17760
+ if (missing.length > 0) {
17761
+ const holes = new Set(missing);
17762
+ for (const [name, deps] of ruleDependencies(Object.entries(map).filter(([n]) => !holes.has(n)))) {
17763
+ for (const dep of deps) if (holes.has(dep)) throw new Error(`compose: rule "${name}" references missing rule "${dep}"`);
18041
17764
  }
18042
- return [{ entries: rules2, plain: false }];
17765
+ throw new Error(`compose: rule(s) ${missing.map((n) => `"${n}"`).join(", ")} are referenced but defined by no composed grammar`);
18043
17766
  }
18044
- return [{ entries: Object.entries(item), plain: true }];
17767
+ const borrowed = borrowedRuleReference(map);
17768
+ if (borrowed !== void 0) throw new Error(borrowedRuleMessage("compose", borrowed));
17769
+ for (const rule of Object.values(map)) LINKED_WINNERS.set(rule, map);
17770
+ Object.defineProperty(map, RULES_RECIPE, { value: recipes, enumerable: false });
17771
+ Object.defineProperty(map, LINKED, { value: () => map, enumerable: false });
17772
+ if (link.trivia) Object.defineProperty(map, COMPOSED_TRIVIA, { value: link.trivia, enumerable: false });
17773
+ return map;
18045
17774
  }
18046
- function ruleNamesOf(pieces) {
18047
- const names = /* @__PURE__ */ new Set();
18048
- for (const piece of pieces) {
18049
- for (const [name, value] of piece.entries) if (definitionOf(value) !== void 0) names.add(name);
17775
+ function compose(items, opts) {
17776
+ if (items.some((item) => item[LEAF_COMPOSED] === true)) {
17777
+ throw new Error("compose: a composeLeaf() result is terminal and cannot be composed again");
18050
17778
  }
18051
- return [...names];
17779
+ return linkedMap(items, opts, false);
18052
17780
  }
18053
- function fusePieces(pieces, opts) {
18054
- let trivia2;
18055
- for (let i = pieces.length - 1; i >= 0 && trivia2 === void 0; i--) {
18056
- if (!pieces[i].plain) continue;
18057
- for (const [, rule] of pieces[i].entries) {
18058
- const t = rule._meta.grammarTrivia;
18059
- if (t) {
18060
- trivia2 = t;
18061
- break;
18062
- }
18063
- }
18064
- }
18065
- const winner = /* @__PURE__ */ new Map();
18066
- const entry = /* @__PURE__ */ new Map();
18067
- for (const piece of pieces) {
18068
- for (const [name, value] of piece.entries) {
18069
- const def = definitionOf(value);
18070
- if (def === void 0) continue;
18071
- winner.set(name, def);
18072
- entry.set(name, value);
18073
- }
18074
- }
18075
- const missing = /* @__PURE__ */ new Set();
18076
- for (const piece of pieces) {
18077
- for (const [name, value] of piece.entries) {
18078
- if (!isRefSlot(value)) continue;
18079
- const target = winner.get(name);
18080
- if (target === void 0) {
18081
- missing.add(name);
18082
- continue;
18083
- }
18084
- repointRef(value, target, name);
18085
- }
17781
+ function linkedMap(items, opts, leaf2) {
17782
+ const recipes = items.flatMap(recipesOf);
17783
+ const trivia2 = composingTriviaOf(items);
17784
+ const names = declaredNamesOf(items);
17785
+ let linked;
17786
+ const link = () => linked ??= linkComposition(recipes, { trivia: trivia2, ...opts?.hostMode === void 0 ? {} : { hostMode: opts.hostMode } });
17787
+ const map = {};
17788
+ for (const name of names) {
17789
+ Object.defineProperty(map, name, { enumerable: true, configurable: true, get: () => link()[name] });
18086
17790
  }
18087
- if (missing.size > 0) throw new Error(missingRuleMessage(pieces, missing));
18088
- const out = {};
18089
- for (const [name, value] of entry) {
18090
- out[name] = value;
18091
- const meta = value._meta;
18092
- if (trivia2 !== void 0 && !meta.isTrivia) meta.grammarTrivia = trivia2;
18093
- if (opts?.hostMode !== void 0 && !meta.isTrivia) {
18094
- if (opts.hostMode === "cst") meta.grammarHostMode = "cst";
18095
- else meta.grammarHostMode = void 0;
18096
- }
18097
- }
18098
- Object.defineProperty(out, INTERPRETED_PIECES, {
18099
- value: pieces.filter((p) => p.plain).map((p) => p.entries),
18100
- enumerable: false
18101
- });
18102
- return out;
17791
+ Object.defineProperty(map, RULES_RECIPE, { value: recipes, enumerable: false });
17792
+ Object.defineProperty(map, RULE_ORDER, { value: names, enumerable: false });
17793
+ Object.defineProperty(map, LINKED, { value: link, enumerable: false });
17794
+ Object.defineProperty(map, GRAMMAR_REFLECTION, { enumerable: false, configurable: true, get: () => link()[GRAMMAR_REFLECTION] });
17795
+ if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
17796
+ if (leaf2) Object.defineProperty(map, LEAF_COMPOSED, { value: true, enumerable: false });
17797
+ return map;
18103
17798
  }
18104
- function missingRuleMessage(pieces, missing) {
18105
- for (const piece of pieces) {
18106
- const deps = ruleDependencies(piece.entries.filter(([, v]) => definitionOf(v) !== void 0));
18107
- for (const [name, ds] of deps) {
18108
- for (const d of ds) if (missing.has(d)) return `fuseInterpreted: rule "${name}" references missing rule "${d}"`;
18109
- }
18110
- }
18111
- return `fuseInterpreted: missing rule(s) ${[...missing].map((n) => `"${n}"`).join(", ")}`;
17799
+ function composeLeaf(items) {
17800
+ return linkedMap(items, void 0, true);
18112
17801
  }
18113
17802
 
18114
17803
  // src/analysis/grammar.ts
@@ -18141,7 +17830,7 @@ function analyzeGrammarGating(grammar, opts) {
18141
17830
  }
18142
17831
 
18143
17832
  // src/analysis/diagnose.ts
18144
- var isCombinator = (g) => typeof g === "object" && g !== null && "_def" in g;
17833
+ var isCombinator2 = (g) => typeof g === "object" && g !== null && "_def" in g;
18145
17834
  var isRuleEntries = (g) => Array.isArray(g);
18146
17835
  function diagnoseGrammar(grammar, opts) {
18147
17836
  const depth = degradationCaptureDepth();
@@ -18185,7 +17874,7 @@ function formatGrammarDiagnosis(d) {
18185
17874
  return lines;
18186
17875
  }
18187
17876
  function runAnalysis(grammar, opts) {
18188
- if (isCombinator(grammar)) return analyzeGating(grammar, opts);
17877
+ if (isCombinator2(grammar)) return analyzeGating(grammar, opts);
18189
17878
  if (isRuleEntries(grammar)) return analyzeGatingRules(grammar, opts);
18190
17879
  return analyzeGrammarGating(grammar, opts);
18191
17880
  }
@@ -19453,6 +19142,29 @@ function gate(predicate) {
19453
19142
  };
19454
19143
  }
19455
19144
 
19145
+ // src/combinators/withCtx.ts
19146
+ function withCtx(extra, combinator) {
19147
+ const meta = {
19148
+ firstSet: combinator._meta.firstSet,
19149
+ canMatchNewline: combinator._meta.canMatchNewline,
19150
+ isTrivia: false
19151
+ };
19152
+ return {
19153
+ _tag: "withCtx",
19154
+ _meta: meta,
19155
+ _def: { tag: "withCtx", extra, parser: combinator },
19156
+ parse(input, pos, ctx) {
19157
+ const saved = ctx.state;
19158
+ ctx.state = extra;
19159
+ try {
19160
+ return combinator.parse(input, pos, ctx);
19161
+ } finally {
19162
+ ctx.state = saved;
19163
+ }
19164
+ }
19165
+ };
19166
+ }
19167
+
19456
19168
  // src/combinators/completions.ts
19457
19169
  function completionsAt(target, input, offset, options = {}) {
19458
19170
  const probe = { offset, best: null };
@@ -20135,7 +19847,7 @@ function grammarCoverageDefinitions(entry, winners) {
20135
19847
  }
20136
19848
  function composedGrammarCoverageDefinitions(grammar, startRule) {
20137
19849
  const winners = composedCoverageRules(grammar);
20138
- if (!winners) throw new TypeError("semantic coverage needs re-lowerable composed IR; this composition contains an opaque artifact");
19850
+ if (!winners) throw new TypeError("semantic coverage needs a runtime compose() result (a linked interpreter grammar); this is not one");
20139
19851
  const entry = winners[startRule];
20140
19852
  if (!entry) throw new TypeError(`semantic coverage start rule ${JSON.stringify(startRule)} is not a final winner`);
20141
19853
  return grammarCoverageDefinitions(entry, winners);
@@ -20377,7 +20089,7 @@ function coverageEntry(entry, collector, maps, trace) {
20377
20089
  }
20378
20090
  function runWithGrammarCoverage(entry, input, options = {}) {
20379
20091
  if (typeof entry === "function") throw new TypeError("runWithGrammarCoverage currently requires an interpreter combinator entry");
20380
- const plan = buildGrammarPlan(entry);
20092
+ const plan = buildGrammarPlan(entry, linkedWinnersOf(entry));
20381
20093
  const definitions = plan.definitions;
20382
20094
  const collector = options.collector ?? createGrammarCoverageCollector(definitions);
20383
20095
  const { collector: _collector, trace, ...runOptions } = options;
@@ -20451,13 +20163,13 @@ function buildTriviaIndex(root, input = "", opts) {
20451
20163
  }
20452
20164
 
20453
20165
  // src/cst/walk.ts
20454
- function isCombinator2(value) {
20166
+ function isCombinator3(value) {
20455
20167
  return !!value && typeof value === "object" && "_def" in value && "_meta" in value && typeof value.parse === "function";
20456
20168
  }
20457
20169
  function reflectionFor(grammar) {
20458
20170
  const stamped = grammarReflectionOf(grammar);
20459
20171
  if (stamped) return stamped;
20460
- const entries = Object.entries(grammar).filter((entry) => isCombinator2(entry[1]));
20172
+ const entries = Object.entries(grammar).filter((entry) => isCombinator3(entry[1]));
20461
20173
  return collectGrammarReflection(entries);
20462
20174
  }
20463
20175
  function createVisitor(grammar, spec) {