parseman 0.51.2 → 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 (98) hide show
  1. package/dist/analysis/commitment.d.ts.map +1 -1
  2. package/dist/analysis/diagnostics.js +411 -28
  3. package/dist/analysis/diagnostics.js.map +3 -3
  4. package/dist/cli/index.js +6162 -6372
  5. package/dist/cli/index.js.map +3 -3
  6. package/dist/combinators/attempt.d.ts +11 -1
  7. package/dist/combinators/attempt.d.ts.map +1 -1
  8. package/dist/combinators/map.d.ts.map +1 -1
  9. package/dist/combinators/node.d.ts +6 -2
  10. package/dist/combinators/node.d.ts.map +1 -1
  11. package/dist/combinators/parser.d.ts +34 -0
  12. package/dist/combinators/parser.d.ts.map +1 -1
  13. package/dist/combinators/ref.d.ts +0 -13
  14. package/dist/combinators/ref.d.ts.map +1 -1
  15. package/dist/compiler/borrowed-rules.d.ts +30 -0
  16. package/dist/compiler/borrowed-rules.d.ts.map +1 -0
  17. package/dist/compiler/compile-linkable-table.d.ts +1 -1
  18. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  19. package/dist/compiler/ir-serialize.d.ts +10 -4
  20. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  21. package/dist/compiler/linker.d.ts +48 -91
  22. package/dist/compiler/linker.d.ts.map +1 -1
  23. package/dist/compiler/token-capability.js +4 -0
  24. package/dist/compiler/token-capability.js.map +1 -1
  25. package/dist/coverage.d.ts +1 -1
  26. package/dist/coverage.d.ts.map +1 -1
  27. package/dist/cst/reflection.d.ts.map +1 -1
  28. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +470 -725
  32. package/dist/index.js.map +3 -3
  33. package/dist/language-service/index.js.map +1 -1
  34. package/dist/plugin/evaluator.d.ts +38 -2
  35. package/dist/plugin/evaluator.d.ts.map +1 -1
  36. package/dist/plugin/index.d.ts.map +1 -1
  37. package/dist/plugin/index.js +1127 -544
  38. package/dist/plugin/index.js.map +3 -3
  39. package/dist/plugin/ir-eval.d.ts +21 -0
  40. package/dist/plugin/ir-eval.d.ts.map +1 -0
  41. package/dist/plugin/reducer-resolver.d.ts +6 -0
  42. package/dist/plugin/reducer-resolver.d.ts.map +1 -1
  43. package/dist/spec/index.js +555 -6086
  44. package/dist/spec/index.js.map +3 -3
  45. package/dist/spec/railroad-lib.d.ts +7 -2
  46. package/dist/spec/railroad-lib.d.ts.map +1 -1
  47. package/dist/spec/railroad.d.ts.map +1 -1
  48. package/dist/table/assemble.d.ts.map +1 -1
  49. package/dist/table/compile-rule-map.d.ts +11 -4
  50. package/dist/table/compile-rule-map.d.ts.map +1 -1
  51. package/dist/table/compile.d.ts +16 -0
  52. package/dist/table/compile.d.ts.map +1 -1
  53. package/dist/table/emit-assembly.d.ts.map +1 -1
  54. package/dist/table/encode-baseline.d.ts.map +1 -1
  55. package/dist/table/exec.d.ts.map +1 -1
  56. package/dist/table/index.js +272 -102
  57. package/dist/table/index.js.map +2 -2
  58. package/dist/table/ops.d.ts +7 -5
  59. package/dist/table/ops.d.ts.map +1 -1
  60. package/dist/table/site-labels.d.ts.map +1 -1
  61. package/dist/types.d.ts +8 -1
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/version.d.ts +1 -1
  64. package/package.json +1 -1
  65. package/src/analysis/commitment.ts +3 -1
  66. package/src/analysis/rebuild.ts +1 -1
  67. package/src/combinators/attempt.ts +26 -3
  68. package/src/combinators/first-set.ts +5 -1
  69. package/src/combinators/map.ts +5 -1
  70. package/src/combinators/node.ts +12 -3
  71. package/src/combinators/parser.ts +133 -67
  72. package/src/combinators/ref.ts +10 -1
  73. package/src/compiler/borrowed-rules.ts +88 -0
  74. package/src/compiler/compile-linkable-table.ts +1 -1
  75. package/src/compiler/grammar-coverage-ids.ts +35 -6
  76. package/src/compiler/ir-serialize.ts +13 -119
  77. package/src/compiler/linker.ts +190 -666
  78. package/src/coverage.ts +7 -5
  79. package/src/cst/reflection.ts +6 -1
  80. package/src/cst/trivia-kinds.ts +6 -0
  81. package/src/index.ts +1 -0
  82. package/src/plugin/evaluator.ts +114 -14
  83. package/src/plugin/index.ts +588 -110
  84. package/src/plugin/ir-eval.ts +165 -0
  85. package/src/plugin/reducer-resolver.ts +20 -3
  86. package/src/spec/railroad-lib.ts +18 -8
  87. package/src/spec/railroad.ts +3 -4
  88. package/src/table/assemble.ts +22 -8
  89. package/src/table/compile-rule-map.ts +18 -8
  90. package/src/table/compile.ts +61 -0
  91. package/src/table/emit-assembly.ts +8 -3
  92. package/src/table/encode-baseline.ts +5 -1
  93. package/src/table/encode.ts +13 -13
  94. package/src/table/exec.ts +6 -2
  95. package/src/table/ops.ts +7 -5
  96. package/src/table/site-labels.ts +3 -1
  97. package/src/types.ts +8 -1
  98. package/src/version.ts +1 -1
package/dist/index.js CHANGED
@@ -257,6 +257,10 @@ function firstSetBody(p, seen, resolve, activeTrivia) {
257
257
  case "node":
258
258
  case "sepBy":
259
259
  case "expect":
260
+ // A transaction accepts exactly what its inner accepts. Its own `_meta` is
261
+ // the inner's construction-time set, which reads `any` when the inner opens
262
+ // with a rule reference — resolve through it like any other wrapper.
263
+ case "attempt":
260
264
  return fs(d.parser);
261
265
  case "grammar":
262
266
  return firstSetOf(
@@ -1795,6 +1799,10 @@ function peelLabel(p) {
1795
1799
  return null;
1796
1800
  }
1797
1801
  function analyzeLabeledTrivia(trivia2) {
1802
+ if (trivia2._def.tag === "trivia") {
1803
+ const own = trivia2._meta.labeledTriviaSpec;
1804
+ if (own !== void 0) return own;
1805
+ }
1798
1806
  if (trivia2 === labeledTrivia0) return labeledSpec0;
1799
1807
  if (trivia2 === labeledTrivia1) return labeledSpec1;
1800
1808
  if (trivia2 === labeledTrivia2) return labeledSpec2;
@@ -3541,13 +3549,24 @@ function dispatch(selector, ...arms) {
3541
3549
  }
3542
3550
 
3543
3551
  // src/combinators/attempt.ts
3544
- function attempt(parser2) {
3552
+ function containOption(options) {
3553
+ if (options === void 0) return false;
3554
+ for (const key of Object.keys(options)) {
3555
+ if (key !== "contain") throw new TypeError(`attempt(): unknown option ${JSON.stringify(key)}`);
3556
+ }
3557
+ if (options.contain !== void 0 && typeof options.contain !== "boolean") {
3558
+ throw new TypeError("attempt(): `contain` must be a boolean");
3559
+ }
3560
+ return options.contain === true;
3561
+ }
3562
+ function attempt(parser2, options) {
3563
+ const contain = containOption(options);
3545
3564
  const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
3546
3565
  let failExpected;
3547
3566
  return {
3548
3567
  _tag: "attempt",
3549
3568
  _meta: parser2._meta,
3550
- _def: { tag: "attempt", parser: parser2 },
3569
+ _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
3551
3570
  parse(input, pos, ctx) {
3552
3571
  if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
3553
3572
  if (failExpected === void 0) {
@@ -3564,7 +3583,7 @@ function attempt(parser2) {
3564
3583
  rollbackCstCapture(ctx, mark);
3565
3584
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
3566
3585
  if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
3567
- if (result.committed) return result;
3586
+ if (result.committed && !contain) return result;
3568
3587
  return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
3569
3588
  }
3570
3589
  };
@@ -3986,6 +4005,7 @@ function withRuleTrivia(trivia2, ctx, run3) {
3986
4005
  ctx.triviaKindLabels = savedLabels;
3987
4006
  }
3988
4007
  }
4008
+ var UNDEFINED_REF = new Error("ref<T>() used before .define() was called");
3989
4009
  function ref() {
3990
4010
  let resolved = null;
3991
4011
  let resolvedScalar = null;
@@ -4001,7 +4021,7 @@ function ref() {
4001
4021
  _def: {
4002
4022
  tag: "lazy",
4003
4023
  thunk: () => {
4004
- if (!resolved) throw new Error("ref<T>() used before .define() was called");
4024
+ if (!resolved) throw UNDEFINED_REF;
4005
4025
  return resolved;
4006
4026
  }
4007
4027
  },
@@ -4391,6 +4411,7 @@ function collectFromCombinator(parser2, out, seen, followLazy = true) {
4391
4411
  function collectGrammarReflection(ruleMap, opts) {
4392
4412
  const byType = /* @__PURE__ */ new Map();
4393
4413
  const followLazy = opts?.followLazy !== false;
4414
+ const seen = /* @__PURE__ */ new Set();
4394
4415
  for (const [, rule] of ruleMap) {
4395
4416
  if (!followLazy && rule._def.tag === "lazy") {
4396
4417
  try {
@@ -4400,7 +4421,7 @@ function collectGrammarReflection(ruleMap, opts) {
4400
4421
  }
4401
4422
  continue;
4402
4423
  }
4403
- collectFromCombinator(rule, byType, /* @__PURE__ */ new Set(), followLazy);
4424
+ collectFromCombinator(rule, byType, seen, followLazy);
4404
4425
  }
4405
4426
  return { nodes: [...byType].map(([type, tags]) => ({ type, tags })) };
4406
4427
  }
@@ -4431,9 +4452,26 @@ function isNamedRuleRefForAnotherRule(r, key) {
4431
4452
  const name = r._def.tag === "lazy" ? ruleNameOf(r) : void 0;
4432
4453
  return name !== void 0 && name !== key;
4433
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
+ }
4434
4465
  function rules(a, b) {
4435
4466
  const factory = typeof a === "function" ? a : b;
4436
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) {
4437
4475
  const cache = {};
4438
4476
  const proxy = new Proxy(cache, {
4439
4477
  get(target, key) {
@@ -4447,10 +4485,18 @@ function rules(a, b) {
4447
4485
  return record[key];
4448
4486
  }
4449
4487
  });
4450
- const definitions = factory(proxy);
4451
- 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) {
4452
4498
  const placeholder = cache[key];
4453
- const parser2 = definitions[key];
4499
+ const parser2 = ownRule(defined, copies);
4454
4500
  if (placeholder === parser2) {
4455
4501
  throw new Error(`rules(): rule "${key}" cannot be a direct alias to itself`);
4456
4502
  }
@@ -4459,7 +4505,8 @@ function rules(a, b) {
4459
4505
  placeholder.define(parser2);
4460
4506
  placeholder._meta.firstSet = parser2._meta.firstSet;
4461
4507
  placeholder._meta.canMatchNewline = parser2._meta.canMatchNewline;
4462
- } else if (isNamedRuleRefForAnotherRule(parser2, key)) {
4508
+ } else if (isNamedRuleRefForAnotherRule(parser2, key) || link !== void 0) {
4509
+ if (!isNamedRuleRefForAnotherRule(parser2, key)) tagRule(parser2, key);
4463
4510
  const alias = ref();
4464
4511
  tagRule(alias, key);
4465
4512
  alias.define(parser2);
@@ -4471,48 +4518,34 @@ function rules(a, b) {
4471
4518
  cache[key] = parser2;
4472
4519
  }
4473
4520
  }
4474
- if (options?.trivia != null) {
4475
- for (const key of Object.keys(definitions)) {
4476
- const rule = cache[key];
4477
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
4478
- }
4479
- }
4480
- if (options?.scanSkip != null) {
4481
- for (const key of Object.keys(definitions)) {
4482
- const rule = cache[key];
4483
- if (rule && !rule._meta.isTrivia) rule._meta.grammarScanSkip = options.scanSkip;
4484
- }
4485
- }
4486
- if (options?.hostMode === "cst") {
4487
- for (const key of Object.keys(definitions)) {
4488
- const rule = cache[key];
4489
- if (rule && !rule._meta.isTrivia) rule._meta.grammarHostMode = "cst";
4490
- }
4491
- }
4492
- if (options?.trackLines === true) {
4493
- for (const key of Object.keys(definitions)) {
4494
- const rule = cache[key];
4495
- if (rule && !rule._meta.isTrivia) rule._meta.grammarTrackLines = true;
4496
- }
4497
- for (const key of Object.keys(definitions)) {
4498
- const rule = cache[key];
4499
- if (rule && !rule._meta.isTrivia && rule._def.tag !== "grammar") {
4500
- const wrapped = parser({ trackLines: true }, rule);
4501
- tagRule(wrapped, key);
4502
- wrapped._meta.grammarTrackLines = true;
4503
- 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
+ }
4504
4538
  }
4505
4539
  }
4506
- }
4507
- for (const key of Object.keys(definitions)) {
4508
- markUnusedValues(cache[key]);
4540
+ markUnusedValues(rule);
4541
+ claimedRules.add(winners.get(key).parser).add(rule);
4509
4542
  }
4510
4543
  Object.defineProperty(cache, RULE_ORDER, {
4511
- value: Object.keys(definitions),
4544
+ value: keys,
4512
4545
  enumerable: false,
4513
4546
  configurable: true
4514
4547
  });
4515
- attachGrammarReflection(cache, collectGrammarReflection(Object.keys(definitions).map((key) => [key, cache[key]])));
4548
+ attachGrammarReflection(cache, collectGrammarReflection(keys.map((key) => [key, cache[key]])));
4516
4549
  return cache;
4517
4550
  }
4518
4551
 
@@ -4964,7 +4997,7 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
4964
4997
  throw new Error("node() options cannot combine project with unwrap or collapse");
4965
4998
  }
4966
4999
  const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || buildArity !== void 0 || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...buildArity !== void 0 ? { buildArity } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
4967
- const capturesTrivia = captureTrivia || trailingTrivia || (build ? buildReadsTrivia(def) : project === void 0);
5000
+ const capturesTrivia = captureTrivia || (build ? buildReadsTrivia(def) : project === void 0);
4968
5001
  const clonesState = build ? buildReadsState(def) : project === void 0;
4969
5002
  const hasOwnFields = parserHasOwnFields(combinator);
4970
5003
  const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
@@ -5078,13 +5111,17 @@ function transform(combinator, fn) {
5078
5111
  };
5079
5112
  }
5080
5113
  function trivia(combinator) {
5081
- const kindLabels = analyzeLabeledTrivia(combinator)?.labels;
5114
+ const labeled = analyzeLabeledTrivia(combinator);
5115
+ const kindLabels = labeled?.labels;
5082
5116
  return {
5083
5117
  _tag: combinator._tag,
5084
5118
  _meta: {
5085
5119
  ...combinator._meta,
5086
5120
  isTrivia: true,
5087
5121
  triviaScanner: fastTriviaScanner(combinator),
5122
+ // Every labeled scan of this trivia reads its spec; the module's four-slot
5123
+ // memo alone thrashes on a grammar with more than four trivia tables.
5124
+ labeledTriviaSpec: labeled,
5088
5125
  ...kindLabels ? { triviaKindLabels: kindLabels } : {}
5089
5126
  },
5090
5127
  _def: { tag: "trivia", parser: combinator },
@@ -5653,15 +5690,15 @@ function formatGatingWarnings(report) {
5653
5690
  return lines;
5654
5691
  }
5655
5692
  function ruleDependencies(ruleMap) {
5656
- const nameOf = /* @__PURE__ */ new Map();
5657
- 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);
5658
5695
  const deps = /* @__PURE__ */ new Map();
5659
5696
  for (const [name, comb] of ruleMap) {
5660
5697
  const found = /* @__PURE__ */ new Set();
5661
5698
  const seen = /* @__PURE__ */ new Set();
5662
5699
  const walk = (p, isRoot) => {
5663
5700
  const def = p._def;
5664
- 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);
5665
5702
  if (!isRoot && boundary !== void 0) {
5666
5703
  found.add(boundary);
5667
5704
  return;
@@ -5993,10 +6030,12 @@ function mayCommitFailure(p, seen = /* @__PURE__ */ new Set(), resolve) {
5993
6030
  case "choice":
5994
6031
  case "sequence":
5995
6032
  return d.parsers.some((x) => mayCommitFailure(x, seen, resolve));
6033
+ // A contained transaction reports every failure uncommitted.
6034
+ case "attempt":
6035
+ return d.contain !== true && mayCommitFailure(d.parser, seen, resolve);
5996
6036
  case "many":
5997
6037
  case "oneOrMore":
5998
6038
  case "optional":
5999
- case "attempt":
6000
6039
  case "transform":
6001
6040
  case "label":
6002
6041
  case "field":
@@ -8105,7 +8144,7 @@ var Encoder = class {
8105
8144
  const derivedState = d.build !== void 0 ? buildReadsState(d) : noBuildCaptures;
8106
8145
  const derivedFields = d.build !== void 0 ? buildReadsFields(d) : noBuildCaptures;
8107
8146
  const wantsFields = parserHasOwnFields(d.parser) && (cstOut || derivedFields);
8108
- const flags = (cstOut || d.captureTrivia === true || d.trailingTrivia === true || derivedTrivia ? 4 : 0) | (!cstOut && omitsRaw ? 2 : 0) | (cstOut || derivedState ? 8 : 0) | (wantsFields ? 16 : 0) | (d.collapse === true ? 32 : 0) | (d.unwrap === true ? 64 : 0) | (d.trailingTrivia === true ? 128 : 0) | (d.captureTrivia === true ? 1 : 0);
8147
+ const flags = (cstOut || d.captureTrivia === true || derivedTrivia ? 4 : 0) | (!cstOut && omitsRaw ? 2 : 0) | (cstOut || derivedState ? 8 : 0) | (wantsFields ? 16 : 0) | (d.collapse === true ? 32 : 0) | (d.unwrap === true ? 64 : 0) | (d.trailingTrivia === true ? 128 : 0) | (d.captureTrivia === true ? 1 : 0);
8109
8148
  const body = this.emit(
8110
8149
  this.track ? OP_NODE_TRACK : OP_NODE,
8111
8150
  d.build === void 0 ? -1 : this.fn(d.build, d.buildSrc ?? null),
@@ -8231,7 +8270,7 @@ var Encoder = class {
8231
8270
  // A TRANSACTION IS A ROW. See `OP_ATTEMPT` for why the transparent
8232
8271
  // lowering was correct only for a choice arm.
8233
8272
  case "attempt": {
8234
- const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip);
8273
+ const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
8235
8274
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
8236
8275
  const fs = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
8237
8276
  if (fs.kind === "any" || matchesEmpty(d.parser)) return inner;
@@ -9627,7 +9666,7 @@ function transfer(code, ip, at, hostCst) {
9627
9666
  }
9628
9667
  if (op === OP_NODE || op === OP_NODE_TRACK) {
9629
9668
  const flags = code[ip + 3];
9630
- const directChildren = !hostCst && op === OP_NODE && code[ip + 1] >= 0 && code[ip + 4] < 0 && (flags === 2 || flags === 18 || flags === 34);
9669
+ const directChildren = !hostCst && op === OP_NODE && code[ip + 1] >= 0 && code[ip + 4] < 0 && ((flags & ~128) === 2 || flags === 18 || flags === 34);
9631
9670
  if (directChildren) {
9632
9671
  return { tri: at.tri, buf: false, raw: RAW_UNKNOWN, cap: CAP_OFF };
9633
9672
  }
@@ -10831,7 +10870,7 @@ ${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
10831
10870
  const v=${child}(input,pos,ctx)
10832
10871
  if(v!==FAIL)return v
10833
10872
  ${clean ? "" : emitRollback(p, L.buf, L.raw, sinks)}
10834
- if(ctx._fc===true)return FAIL
10873
+ ${code[ip + 2] === 1 ? "ctx._fc=false" : "if(ctx._fc===true)return FAIL"}
10835
10874
  ctx._fe=pos
10836
10875
  return FAIL
10837
10876
  }`;
@@ -11420,7 +11459,8 @@ return nd
11420
11459
  const unwrap = (flags & 64) !== 0;
11421
11460
  const trailingTrivia = (flags & 128) !== 0;
11422
11461
  const build = buildIdx >= 0 ? fnRef(buildIdx) : void 0;
11423
- if (!hostCst && !tracked && build !== void 0 && proj < 0 && (flags === 2 || flags === 18 || flags === 34)) {
11462
+ const leanFlags = flags & ~128;
11463
+ if (!hostCst && !tracked && build !== void 0 && proj < 0 && (leanFlags === 2 || (flags === 18 || flags === 34))) {
11424
11464
  const fields = flags === 18;
11425
11465
  const collapseChildren = flags === 34;
11426
11466
  const publish = L.buf && L.raw === RAW_OMIT ? "_pushNodeNoRawBuf(ctx,nd)" : L.buf && L.raw === RAW_CAPTURE ? `if(sBuf!==undefined){
@@ -11465,7 +11505,8 @@ ctx._cstTriviaLog=undefined
11465
11505
  ctx.captureTrivia=false
11466
11506
  ctx._fields=${fields ? "[]" : "undefined"}
11467
11507
  const v=${child}(input,pos,ctx)
11468
- const captured=_capturedFlatChildren(flat)
11508
+ ${trailingTrivia && L.tri !== TRI_NONE ? `if(v!==FAIL${L.tri === TRI_UNKNOWN ? "&&ctx.trivia!==undefined" : ""})EC.e=consumeTrivia(input,EC.e,ctx)
11509
+ ` : ""}const captured=_capturedFlatChildren(flat)
11469
11510
  ${fields ? "const fieldMap=buildFieldMap(ctx._fields)\n" : ""}ctx._fields=sFields
11470
11511
  ctx._cstBuf=sBuf
11471
11512
  ctx._cstChildren=sCh
@@ -12098,7 +12139,7 @@ function assemble(t, prog, cfg) {
12098
12139
  const lf = { _tag: "leaf", value, span: { start: s, end: e } };
12099
12140
  pushCstLeaf(ctx, lf);
12100
12141
  }
12101
- function plainBuildNode(child, build) {
12142
+ function plainBuildNode(child, build, trailing) {
12102
12143
  return (input, pos, ctx) => {
12103
12144
  const sCh = ctx._cstChildren;
12104
12145
  const sLv = ctx._cstLeaves;
@@ -12116,6 +12157,7 @@ function assemble(t, prog, cfg) {
12116
12157
  ctx.captureTrivia = false;
12117
12158
  ctx._fields = void 0;
12118
12159
  const value = child(input, pos, ctx);
12160
+ if (trailing && value !== FAIL && ctx.trivia !== void 0) EC.e = consumeTrivia(input, EC.e, ctx);
12119
12161
  const kids = buf.ch ?? (buf.single !== void 0 ? [buf.single] : EMPTY_CH);
12120
12162
  const rawKids = buf.raw ?? (buf.rawSingle !== void 0 ? [buf.rawSingle] : EMPTY_CH);
12121
12163
  ctx._fields = savedFields;
@@ -12143,7 +12185,7 @@ function assemble(t, prog, cfg) {
12143
12185
  while (first < children2.length && children2[first] === void 0) first++;
12144
12186
  return first === children2.length ? EMPTY_CH : children2.slice(first);
12145
12187
  }
12146
- function childrenOnlyBuildNode(child, build) {
12188
+ function childrenOnlyBuildNode(child, build, trailing) {
12147
12189
  return (input, pos, ctx) => {
12148
12190
  const sCh = ctx._cstChildren;
12149
12191
  const sLv = ctx._cstLeaves;
@@ -12161,6 +12203,7 @@ function assemble(t, prog, cfg) {
12161
12203
  ctx.captureTrivia = false;
12162
12204
  ctx._fields = void 0;
12163
12205
  const value = child(input, pos, ctx);
12206
+ if (trailing && value !== FAIL && ctx.trivia !== void 0) EC.e = consumeTrivia(input, EC.e, ctx);
12164
12207
  const captured = capturedFlatChildren(kids);
12165
12208
  ctx._fields = savedFields;
12166
12209
  ctx._cstBuf = sBuf;
@@ -13185,6 +13228,7 @@ function assemble(t, prog, cfg) {
13185
13228
  /* ── transaction ─────────────────────────────────────────────────────── */
13186
13229
  case OP_ATTEMPT: {
13187
13230
  const child = link(code[ip + 1]);
13231
+ const contain = code[ip + 2] === 1;
13188
13232
  return (input, pos, ctx) => {
13189
13233
  const need = markCst(ctx);
13190
13234
  const mRaw = MRAW;
@@ -13197,7 +13241,10 @@ function assemble(t, prog, cfg) {
13197
13241
  const v = child(input, pos, ctx);
13198
13242
  if (v !== FAIL) return v;
13199
13243
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13200
- if (committed3(ctx)) return FAIL;
13244
+ if (committed3(ctx)) {
13245
+ if (!contain) return FAIL;
13246
+ ctx._fc = false;
13247
+ }
13201
13248
  ctx._fe = pos;
13202
13249
  return FAIL;
13203
13250
  };
@@ -14648,11 +14695,11 @@ function assemble(t, prog, cfg) {
14648
14695
  const trailingTrivia = (flags & 128) !== 0;
14649
14696
  const build = buildIdx >= 0 ? fns[buildIdx] : void 0;
14650
14697
  if (!hostCst && !tracked) {
14651
- if (build !== void 0 && proj < 0 && flags === 0) {
14652
- return plainBuildNode(child, build);
14698
+ if (build !== void 0 && proj < 0 && (flags & ~128) === 0) {
14699
+ return plainBuildNode(child, build, trailingTrivia);
14653
14700
  }
14654
- if (build !== void 0 && proj < 0 && flags === 2) {
14655
- return childrenOnlyBuildNode(child, build);
14701
+ if (build !== void 0 && proj < 0 && (flags & ~128) === 2) {
14702
+ return childrenOnlyBuildNode(child, build, trailingTrivia);
14656
14703
  }
14657
14704
  if (build !== void 0 && proj < 0 && flags === 18) {
14658
14705
  return childrenOnlyFieldsBuildNode(child, build);
@@ -15404,7 +15451,8 @@ function children(def, winners) {
15404
15451
  return [];
15405
15452
  }
15406
15453
  const name = resolved._ruleName;
15407
- 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];
15408
15456
  }
15409
15457
  default:
15410
15458
  return [];
@@ -15429,16 +15477,39 @@ function buildGrammarPlan(entry, winners) {
15429
15477
  }
15430
15478
  }
15431
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
+ };
15432
15503
  const seen = /* @__PURE__ */ new Set();
15433
15504
  const visit = (parser2, path) => {
15434
15505
  if (seen.has(parser2)) return;
15435
15506
  seen.add(parser2);
15436
15507
  const rule = winnerNames.get(parser2) ?? parser2._ruleName;
15437
- if (rule && (winnerNames.has(parser2) || parser2._def.tag !== "lazy")) {
15438
- const id = `rule:${rule}`;
15439
- definitions.set(id, { id, kind: "rule" });
15440
- 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);
15441
15511
  }
15512
+ if (rule && body !== void 0 && sharedBody(body)) ownRule2(parser2, rule);
15442
15513
  if (parser2._def.tag === "choice") {
15443
15514
  const ids = parser2._def.parsers.map((_, index) => `choice:${path}/arm:${index}`);
15444
15515
  choices.set(parser2, ids);
@@ -15459,7 +15530,13 @@ function buildGrammarPlan(entry, winners) {
15459
15530
  };
15460
15531
  const roots = Array.isArray(entry) ? entry : [entry];
15461
15532
  for (const root of roots) {
15462
- 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);
15463
15540
  }
15464
15541
  return { definitions: [...definitions.values()].sort((a, b) => a.id.localeCompare(b.id)), choices, dispatches, attempts, labels, rules: rules2 };
15465
15542
  }
@@ -16765,6 +16842,183 @@ function runDuplicationDiagnosticRules(ruleMap, opt) {
16765
16842
  return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16766
16843
  }
16767
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
+
16768
17022
  // src/table/compile.ts
16769
17023
  var ENTRY = "Entry";
16770
17024
  function ownSource(fn) {
@@ -16774,7 +17028,47 @@ function ownSource(fn) {
16774
17028
  const name = fn.name;
16775
17029
  return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
16776
17030
  }
16777
- 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 = {}) {
16778
17072
  runDuplicationDiagnostic(combinator, opts.duplication);
16779
17073
  const drain = beginCompileDegradationDrain();
16780
17074
  let compiled = false;
@@ -16844,29 +17138,6 @@ function compileImpl(combinator, mapFnSources, opts = {}) {
16844
17138
  };
16845
17139
  }
16846
17140
 
16847
- // src/combinators/withCtx.ts
16848
- function withCtx(extra, combinator) {
16849
- const meta = {
16850
- firstSet: combinator._meta.firstSet,
16851
- canMatchNewline: combinator._meta.canMatchNewline,
16852
- isTrivia: false
16853
- };
16854
- return {
16855
- _tag: "withCtx",
16856
- _meta: meta,
16857
- _def: { tag: "withCtx", extra, parser: combinator },
16858
- parse(input, pos, ctx) {
16859
- const saved = ctx.state;
16860
- ctx.state = extra;
16861
- try {
16862
- return combinator.parse(input, pos, ctx);
16863
- } finally {
16864
- ctx.state = saved;
16865
- }
16866
- }
16867
- };
16868
- }
16869
-
16870
17141
  // src/compiler/ir-serialize.ts
16871
17142
  function childrenOf3(def) {
16872
17143
  switch (def.tag) {
@@ -16963,176 +17234,6 @@ function serializeRuleMap(ruleMap, scanSkip) {
16963
17234
  throw e;
16964
17235
  }
16965
17236
  }
16966
- function evalRuleMapIR(ir) {
16967
- const _tf = (child, src, recognitionOnly = false) => {
16968
- let fn2;
16969
- try {
16970
- fn2 = (0, eval)(`(${src})`);
16971
- } catch {
16972
- fn2 = () => {
16973
- throw new Error("IR transform fn not materialized");
16974
- };
16975
- }
16976
- const t = transform(child, fn2);
16977
- t._def.fnSrc = src;
16978
- if (recognitionOnly) t._def.recognitionOnly = true;
16979
- return t;
16980
- };
16981
- const _lf = (child, src, source = false) => {
16982
- let fn2;
16983
- try {
16984
- fn2 = (0, eval)(`(${src})`);
16985
- } catch {
16986
- fn2 = () => {
16987
- throw new Error("IR leaf fn not materialized");
16988
- };
16989
- }
16990
- const l = source ? sourceLeaf(child, fn2) : leaf(child, fn2);
16991
- l._def.fnSrc = src;
16992
- return l;
16993
- };
16994
- const _nd = (type, child, src, opts, staticError, sigSrc, buildImports, rawUnused) => {
16995
- if (staticError !== void 0 && staticError.length > 0) {
16996
- throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(", ")}`);
16997
- }
16998
- const n = node(type, child, (() => {
16999
- throw new Error("IR node build requires static re-lowering");
17000
- }), opts);
17001
- n._def.buildSrc = src;
17002
- if (staticError !== void 0) n._def.buildStaticError = staticError;
17003
- if (sigSrc !== void 0) n._def.buildSigSrc = sigSrc;
17004
- if (rawUnused === true) n._def.buildRawUnused = true;
17005
- if (buildImports !== void 0) n._def.buildImports = buildImports;
17006
- return n;
17007
- };
17008
- const _gch = (items) => {
17009
- const gateSrcs = [];
17010
- const arms = items.map((it) => {
17011
- if (Array.isArray(it)) {
17012
- const [src, comb] = it;
17013
- gateSrcs.push(src);
17014
- let gate2;
17015
- try {
17016
- gate2 = (0, eval)(`(${src})`);
17017
- } catch {
17018
- gate2 = () => {
17019
- throw new Error("IR gate fn not materialized");
17020
- };
17021
- }
17022
- return { gate: gate2, combinator: comb };
17023
- }
17024
- gateSrcs.push(null);
17025
- return it;
17026
- });
17027
- const c = choice(...arms);
17028
- const gates = c._def.gates;
17029
- if (gates && gates.length !== gateSrcs.length) {
17030
- throw new Error(`_gch: gateSrcs length ${gateSrcs.length} != choice gates length ${gates.length}`);
17031
- }
17032
- ;
17033
- c._def.gateSrcs = gateSrcs;
17034
- return c;
17035
- };
17036
- const _wc = (src, inner) => {
17037
- let extra;
17038
- try {
17039
- extra = (0, eval)(`(${src})`);
17040
- } catch {
17041
- extra = void 0;
17042
- }
17043
- const w = withCtx(extra, inner);
17044
- w._def.extraSrc = src;
17045
- return w;
17046
- };
17047
- const fn = new Function(
17048
- "rules",
17049
- "ref",
17050
- "regex",
17051
- "literal",
17052
- "keywords",
17053
- "sequence",
17054
- "choice",
17055
- "dispatch",
17056
- "when",
17057
- "startsWith",
17058
- "endsWith",
17059
- "matches",
17060
- "otherwise",
17061
- "routed",
17062
- "attempt",
17063
- "many",
17064
- "oneOrMore",
17065
- "optional",
17066
- "sepBy",
17067
- "keepSeparator",
17068
- "not",
17069
- "peek",
17070
- "node",
17071
- "parser",
17072
- "scanTo",
17073
- "balanced",
17074
- "token",
17075
- "leaf",
17076
- "transform",
17077
- "trivia",
17078
- "classifiedTrivia",
17079
- "label",
17080
- "field",
17081
- "expect",
17082
- "adjacent",
17083
- "notAdjacent",
17084
- "_tf",
17085
- "_lf",
17086
- "_nd",
17087
- "_gch",
17088
- "_wc",
17089
- `return (${ir})`
17090
- );
17091
- const map = fn(
17092
- rules,
17093
- ref,
17094
- regex,
17095
- literal,
17096
- keywords,
17097
- sequence,
17098
- choice,
17099
- dispatch,
17100
- when,
17101
- startsWith,
17102
- endsWith,
17103
- matches,
17104
- otherwise,
17105
- routed,
17106
- attempt,
17107
- many,
17108
- oneOrMore,
17109
- optional,
17110
- sepBy,
17111
- keepSeparator,
17112
- not,
17113
- peek,
17114
- node,
17115
- parser,
17116
- scanTo,
17117
- balanced,
17118
- token,
17119
- leaf,
17120
- transform,
17121
- trivia,
17122
- classifiedTrivia,
17123
- label,
17124
- field,
17125
- expect,
17126
- adjacent,
17127
- notAdjacent,
17128
- _tf,
17129
- _lf,
17130
- _nd,
17131
- _gch,
17132
- _wc
17133
- );
17134
- return Object.entries(map);
17135
- }
17136
17237
  var Serializer = class {
17137
17238
  counts = /* @__PURE__ */ new Map();
17138
17239
  selfRef = /* @__PURE__ */ new Set();
@@ -17371,7 +17472,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17371
17472
  case "optional":
17372
17473
  return `optional(${kid(def.parser)})`;
17373
17474
  case "attempt":
17374
- return `attempt(${kid(def.parser)})`;
17475
+ return `attempt(${kid(def.parser)}${def.contain === true ? ", { contain: true }" : ""})`;
17375
17476
  // `keepSeparators` is an opt-in expressed at the SEPARATOR, not in the options
17376
17477
  // bag, so it must round-trip as `keepSeparator(sep)` — serializing it as an
17377
17478
  // option would reconstruct a grammar whose call site no longer states its own
@@ -17459,127 +17560,8 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17459
17560
  }
17460
17561
  };
17461
17562
 
17462
- // src/table/compile-rule-map.ts
17463
- var DEFAULT_PRECOMPILE_MIN_WORDS = 1024;
17464
- function resolveHostMode(ruleMap, explicit) {
17465
- return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
17466
- }
17467
- function resolveTrackLines(ruleMap, explicit) {
17468
- return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
17469
- }
17470
- function withAmbient(ruleMap, opts) {
17471
- if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
17472
- const wrappers = /* @__PURE__ */ new Map();
17473
- return ruleMap.map(([name, rule]) => {
17474
- if (rule._meta.isTrivia) return [name, rule];
17475
- const addTrivia = opts.trivia !== void 0 && rule._meta.grammarTrivia === void 0;
17476
- const addScanSkip = opts.scanSkip !== void 0 && rule._meta.grammarScanSkip === void 0;
17477
- if (!addTrivia && !addScanSkip) return [name, rule];
17478
- let wrapped = wrappers.get(rule);
17479
- if (wrapped === void 0) {
17480
- wrapped = {
17481
- ...rule,
17482
- _meta: {
17483
- ...rule._meta,
17484
- ...addTrivia ? { grammarTrivia: opts.trivia } : {},
17485
- ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
17486
- }
17487
- };
17488
- wrappers.set(rule, wrapped);
17489
- }
17490
- return [name, wrapped];
17491
- });
17492
- }
17493
- function compileRuleMapRunnable(ruleMap, opts = {}) {
17494
- const encoded = encodeForRun(ruleMap, opts);
17495
- if (encoded === null) return null;
17496
- const { prog, hostMode, plan } = encoded;
17497
- const artifact = closureArtifact(prog);
17498
- return {
17499
- keys: ruleMap.map(([key]) => key),
17500
- hostMode,
17501
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17502
- reflection: collectGrammarReflection(ruleMap),
17503
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17504
- rules: tableRules(prog),
17505
- prog: artifact
17506
- };
17507
- }
17508
- function encodeForRun(ruleMap, opts) {
17509
- runDuplicationDiagnosticRules(ruleMap, opts.duplication);
17510
- const encodeMap = withAmbient(ruleMap, opts);
17511
- const plan = opts.coverage === true ? buildGrammarPlan(
17512
- encodeMap.map(([, rule]) => rule),
17513
- Object.fromEntries(encodeMap.filter(([, rule]) => rule._def.tag !== "lazy"))
17514
- ) : void 0;
17515
- const hostMode = resolveHostMode(ruleMap, opts.hostMode);
17516
- const settings = {
17517
- hostMode,
17518
- ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
17519
- ...opts.recovery === true ? { recovery: true } : {},
17520
- ...plan === void 0 ? {} : { coverage: plan }
17521
- };
17522
- try {
17523
- const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
17524
- return { prog, fnSrcs, hostMode, plan };
17525
- } catch (e) {
17526
- opts.refusals?.push(e instanceof Error ? e.message : String(e));
17527
- return null;
17528
- }
17529
- }
17530
- function compileRuleMap(ruleMap, opts = {}) {
17531
- const encoded = encodeForRun(ruleMap, opts);
17532
- if (encoded === null) return null;
17533
- const { prog, fnSrcs, hostMode, plan } = encoded;
17534
- const artifact = closureArtifact(prog);
17535
- if (artifact.runtimeOnly !== void 0 && artifact.runtimeOnly.length > 0) {
17536
- opts.refusals?.push(...artifact.runtimeOnly);
17537
- return null;
17538
- }
17539
- const supplied = opts.fnSources;
17540
- const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? null);
17541
- if (sources.some((s) => s === null)) {
17542
- const missing = sources.flatMap((s, i) => s === null ? [i] : []);
17543
- opts.refusals?.push(
17544
- `author reducer source missing for prog.fns[${missing.join(", ")}] \u2014 printing would emit \`() => {}\` and the parse would return no value`
17545
- );
17546
- return null;
17547
- }
17548
- if (supplied !== void 0 && supplied.length > fnSrcs.length) {
17549
- throw new Error(
17550
- `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.`
17551
- );
17552
- }
17553
- const replacementWithMetadata = (metadataSource, options = {}) => emitTableExpression(artifact, {
17554
- entry: null,
17555
- runtimeRef: opts.runtimeRef ?? "tableRules",
17556
- fnSources: sources,
17557
- ...options.precompileDefault === true && hostMode === "ast" && prog.lines !== 1 && prog.code.length >= DEFAULT_PRECOMPILE_MIN_WORDS ? { assemblies: defaultAssemblyCfgs(artifact).slice(0, 1) } : {},
17558
- ...metadataSource === void 0 ? {} : { metadataSource }
17559
- });
17560
- const replacement = replacementWithMetadata();
17561
- return {
17562
- keys: ruleMap.map(([key]) => key),
17563
- replacement,
17564
- replacementWithMetadata,
17565
- hostMode,
17566
- // WHAT THE FLAG MEANS is "a DIRECT BUILDER's positioned-CST branch was
17567
- // dropped", which is what the driver's `'ast' artifact + CST host` check keys
17568
- // off. `mode === 'ast'` alone over-reports it: an all-STRUCTURAL grammar has no
17569
- // direct builder, so there was no branch to drop and it stays usable with either
17570
- // host — the long-standing `node(parser)` contract. Same predicate codegen uses
17571
- // for its own `hasDirectBuilders` (both read `analysis/commitment.ts`), so the two
17572
- // lowerings stamp the same artifact rather than disagreeing about it.
17573
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
17574
- reflection: collectGrammarReflection(ruleMap),
17575
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
17576
- rules: tableRules(prog),
17577
- prog: artifact
17578
- };
17579
- }
17580
-
17581
17563
  // src/version.ts
17582
- var PARSEMAN_VERSION = "0.51.2";
17564
+ var PARSEMAN_VERSION = "0.53.0";
17583
17565
 
17584
17566
  // src/compiler/compile-linkable-table.ts
17585
17567
  function isLocal(rule) {
@@ -17647,16 +17629,6 @@ function compileLinkableTable(ruleMapArg, ns, opts = {}) {
17647
17629
  }
17648
17630
 
17649
17631
  // src/compiler/linker.ts
17650
- var _nsCounter = 0;
17651
- function linkable(rulesMap, ns, trivia2, hostMode) {
17652
- const piece = compileLinkableTable(
17653
- Object.entries(rulesMap),
17654
- ns ?? `_lk${_nsCounter++}_`,
17655
- { ...trivia2 ? { trivia: trivia2 } : {}, ...hostMode ? { hostMode } : {} }
17656
- );
17657
- if (!piece) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
17658
- return piece;
17659
- }
17660
17632
  function normalizeCstCollapse(collapse) {
17661
17633
  if (collapse === true) return () => true;
17662
17634
  if (Array.isArray(collapse)) {
@@ -17680,107 +17652,6 @@ function cstBuildHost(typeOrOptions, children2, _fields, span, rawChildren, triv
17680
17652
  return host;
17681
17653
  }
17682
17654
  cstBuildHost._parsemanCstOutput = true;
17683
- function fuseCarried(carried, trivia2, hostMode) {
17684
- const maps = [];
17685
- for (const p of carried) {
17686
- if (!isIRPiece(p)) {
17687
- if (typeof p.v !== "string") {
17688
- throw new Error(
17689
- `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.`
17690
- );
17691
- }
17692
- if (p.v !== PARSEMAN_VERSION) {
17693
- throw new Error(
17694
- `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.`
17695
- );
17696
- }
17697
- }
17698
- const rules2 = ruleMapOfCarried(p);
17699
- if (rules2 === void 0) {
17700
- throw new Error(`compose: carried piece "${p.ns}" has no re-lowerable IR and cannot be fused`);
17701
- }
17702
- maps.push(rules2);
17703
- }
17704
- const merged = mergeCarriedRuleMaps(maps);
17705
- materializeDirectBuilders(merged);
17706
- if (trivia2) {
17707
- for (const [, rule] of merged) {
17708
- if (rule._meta.isTrivia) continue;
17709
- rule._meta.grammarTrivia = trivia2;
17710
- }
17711
- }
17712
- const refusals = [];
17713
- const compiled = compileRuleMapRunnable(merged, {
17714
- ...trivia2 ? { trivia: trivia2 } : {},
17715
- ...hostMode ? { hostMode } : {},
17716
- refusals
17717
- });
17718
- if (compiled === null) {
17719
- throw new Error(`compose: the merged grammar could not be encoded to a table${refusals.length ? ` \u2014 ${refusals.join("; ")}` : ""}`);
17720
- }
17721
- const map = tableRules(compiled.prog);
17722
- for (const k of Object.keys(map)) {
17723
- Object.defineProperty(map[k], FUSED_HOST_MODE, { value: compiled.hostMode, enumerable: false });
17724
- Object.defineProperty(map[k], FUSED_HOST_ELIDED, { value: compiled.hostBranchElided, enumerable: false });
17725
- }
17726
- Object.defineProperty(map, FUSED_HOST_MODE, { value: compiled.hostMode, enumerable: false });
17727
- Object.defineProperty(map, FUSED_HOST_ELIDED, { value: compiled.hostBranchElided, enumerable: false });
17728
- Object.defineProperty(map, GRAMMAR_REFLECTION, { value: compiled.reflection, enumerable: false });
17729
- return map;
17730
- }
17731
- function materializeDirectBuilders(ruleMap) {
17732
- const seen = /* @__PURE__ */ new Set();
17733
- const visit = (p) => {
17734
- if (seen.has(p)) return;
17735
- seen.add(p);
17736
- const d = p._def;
17737
- if (d.tag === "node" && typeof d.buildSrc === "string") {
17738
- if (d.buildImports !== void 0 && d.buildImports.length > 0) {
17739
- throw new Error(
17740
- `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.`
17741
- );
17742
- }
17743
- try {
17744
- const fn = (0, eval)(`(${d.buildSrc})`);
17745
- if (typeof fn === "function") d.build = fn;
17746
- } catch {
17747
- }
17748
- }
17749
- if (d.tag === "lazy") {
17750
- let resolved;
17751
- try {
17752
- resolved = d.thunk();
17753
- } catch {
17754
- return;
17755
- }
17756
- if (resolved) visit(resolved);
17757
- return;
17758
- }
17759
- for (const child of childrenOf2(p._def)) visit(child);
17760
- };
17761
- for (const [, rule] of ruleMap) visit(rule);
17762
- }
17763
- function ruleMapOfCarried(p) {
17764
- if (isIRPiece(p)) return evalRuleMapIR(p.ir);
17765
- if (p.ir !== null) return evalRuleMapIR(p.ir);
17766
- return p.ruleMap.length > 0 ? p.ruleMap.map(([k, v]) => [k, v]) : void 0;
17767
- }
17768
- function mergeCarriedRuleMaps(maps) {
17769
- const winners = /* @__PURE__ */ new Map();
17770
- for (const map of maps) {
17771
- for (const [name, rule] of map) {
17772
- if (rule._def.tag === "lazy") {
17773
- try {
17774
- rule._def.thunk();
17775
- } catch {
17776
- continue;
17777
- }
17778
- }
17779
- winners.set(name, rule);
17780
- }
17781
- }
17782
- return [...winners];
17783
- }
17784
17655
  function fusedHostModeOf(registry) {
17785
17656
  const m = registry[FUSED_HOST_MODE];
17786
17657
  return m === "cst" ? "cst" : "ast";
@@ -17795,13 +17666,17 @@ function composedPiecesOf(grammar) {
17795
17666
  }
17796
17667
  var LEAF_COMPOSED = /* @__PURE__ */ Symbol.for("parseman.leafComposed");
17797
17668
  var COMPOSED_TRIVIA = /* @__PURE__ */ Symbol.for("parseman.composedTrivia");
17798
- var COMPOSED_COVERAGE_RULES = /* @__PURE__ */ Symbol.for("parseman.composedCoverageRules");
17669
+ var LINKED = /* @__PURE__ */ Symbol.for("parseman.linkedComposition");
17799
17670
  function isIRPiece(p) {
17800
17671
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("keys" in p);
17801
17672
  }
17802
17673
  function isLinkableTable(p) {
17803
17674
  return !!p && typeof p === "object" && typeof p.ns === "string" && Array.isArray(p.keys);
17804
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
+ }
17805
17680
  function carriedRuleMapsDetailed(carried) {
17806
17681
  const maps = [];
17807
17682
  const opaque = [];
@@ -17832,250 +17707,97 @@ function recoverComposedRules(grammar) {
17832
17707
  }
17833
17708
  return { rules: rules2, opaque };
17834
17709
  }
17835
- function coverageRulesOf(carried) {
17836
- const winners = {};
17837
- for (const piece of carried) {
17838
- if (!isIRPiece(piece)) return void 0;
17839
- const map = evalRuleMapIR(piece.ir);
17840
- for (const [name, rule] of map) {
17841
- if (rule._def.tag === "lazy") {
17842
- try {
17843
- rule._def.thunk();
17844
- } catch {
17845
- continue;
17846
- }
17847
- }
17848
- winners[name] = rule;
17849
- }
17850
- }
17851
- return winners;
17852
- }
17853
17710
  function composedCoverageRules(grammar) {
17854
- return grammar[COMPOSED_COVERAGE_RULES];
17855
- }
17856
- function nextComposeNs(used) {
17857
- let ns;
17858
- do {
17859
- ns = `_lk${_nsCounter++}_`;
17860
- } while (used.has(ns));
17861
- used.add(ns);
17862
- return ns;
17863
- }
17864
- function itemCarried(item, used, trivia2, hostMode) {
17865
- const carried = item[COMPOSED_PIECES];
17866
- if (Array.isArray(carried)) {
17867
- const items = carried;
17868
- for (const p of items) used.add(p.ns);
17869
- return items;
17870
- }
17871
- if (isLinkableTable(item)) {
17872
- used.add(item.ns);
17873
- return [item];
17874
- }
17875
- const map = item;
17876
- const ns = nextComposeNs(used);
17877
- const entries = Object.entries(map).filter(([, val]) => {
17878
- const d = val._def;
17879
- if (d.tag !== "lazy") return true;
17880
- try {
17881
- d.thunk();
17882
- return true;
17883
- } catch {
17884
- return false;
17885
- }
17886
- });
17887
- const scanSkip = entries.map(([, val]) => val._meta.grammarScanSkip).find(Boolean);
17888
- const ir = serializeRuleMap(entries, scanSkip);
17889
- 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
+ );
17890
17725
  }
17891
17726
  function composingTriviaOf(items) {
17892
17727
  for (let i = items.length - 1; i >= 0; i--) {
17893
17728
  const item = items[i];
17894
- if (!item || isLinkableTable(item)) continue;
17895
- const stamped = item[COMPOSED_TRIVIA];
17896
- if (stamped) return stamped;
17897
- if (item[COMPOSED_PIECES]) continue;
17898
- for (const v of Object.values(item)) {
17899
- const t = v?._meta?.grammarTrivia;
17900
- if (t) return t;
17729
+ if (item[LINKED] !== void 0) {
17730
+ const stamped = item[COMPOSED_TRIVIA];
17731
+ if (stamped) return stamped;
17732
+ continue;
17901
17733
  }
17734
+ const recipes = item[RULES_RECIPE];
17735
+ const trivia2 = recipes?.[recipes.length - 1]?.options?.trivia;
17736
+ if (trivia2 != null) return trivia2;
17902
17737
  }
17903
17738
  return void 0;
17904
17739
  }
17905
- function compose(items, opts) {
17906
- if (items.some((item) => item[LEAF_COMPOSED] === true)) {
17907
- throw new Error("compose: a composeLeaf() result is terminal and cannot be composed again");
17908
- }
17909
- const used = /* @__PURE__ */ new Set();
17910
- const trivia2 = composingTriviaOf(items);
17911
- const carried = items.flatMap((item) => itemCarried(item, used, trivia2, opts?.hostMode));
17912
- const map = fuseCarried(carried, trivia2, opts?.hostMode);
17913
- Object.defineProperty(map, COMPOSED_PIECES, { value: carried, enumerable: false });
17914
- if (trivia2) Object.defineProperty(map, COMPOSED_TRIVIA, { value: trivia2, enumerable: false });
17915
- const coverageRules = coverageRulesOf(carried);
17916
- if (coverageRules) Object.defineProperty(map, COMPOSED_COVERAGE_RULES, { value: coverageRules, enumerable: false });
17917
- return map;
17918
- }
17919
- function composeLeaf(items) {
17920
- const pieces = items.flatMap(interpretedPieces);
17921
- let fused;
17922
- const map = {};
17923
- for (const name of ruleNamesOf(pieces)) {
17924
- Object.defineProperty(map, name, {
17925
- enumerable: true,
17926
- configurable: true,
17927
- get: () => (fused ??= fusePieces(pieces))[name]
17928
- });
17929
- }
17930
- Object.defineProperty(map, LEAF_COMPOSED, { value: true, enumerable: false });
17931
- Object.defineProperty(map, INTERPRETED_PIECES, {
17932
- value: pieces.filter((p) => p.plain).map((p) => p.entries),
17933
- enumerable: false
17934
- });
17935
- return map;
17936
- }
17937
- function isRefSlot(c) {
17938
- return c._def.tag === "lazy" && typeof c.define === "function";
17939
- }
17940
- var FUSE_ORIGINAL = /* @__PURE__ */ Symbol.for("parseman.interpretedFuseOriginal");
17941
- var FUSE_TARGET = /* @__PURE__ */ Symbol.for("parseman.interpretedFuseTarget");
17942
- var INTERPRETED_PIECES = /* @__PURE__ */ Symbol.for("parseman.interpretedPieces");
17943
- function definitionOf(entry) {
17944
- if (isRefSlot(entry)) {
17945
- const original = entry[FUSE_ORIGINAL];
17946
- if (original !== void 0) return original ?? void 0;
17947
- try {
17948
- return entry._def.thunk();
17949
- } catch {
17950
- return void 0;
17951
- }
17952
- }
17953
- if (entry._def.tag === "lazy") {
17954
- try {
17955
- entry._def.thunk();
17956
- } catch {
17957
- return void 0;
17958
- }
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);
17959
17746
  }
17960
- return entry;
17747
+ return [...names];
17961
17748
  }
17962
- function repointRef(slot, target, name) {
17963
- const tagged = slot;
17964
- const bound = tagged[FUSE_TARGET];
17965
- 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;
17966
17754
  try {
17967
- return slot._def.thunk();
17755
+ rule._def.thunk();
17968
17756
  } catch {
17969
- return null;
17970
- }
17971
- })();
17972
- if ((bound ?? original) === target) return;
17973
- if (bound !== void 0) {
17974
- throw new Error(
17975
- `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.`
17976
- );
17977
- }
17978
- if (!(FUSE_ORIGINAL in tagged)) Object.defineProperty(slot, FUSE_ORIGINAL, { value: original, enumerable: false });
17979
- Object.defineProperty(slot, FUSE_TARGET, { value: target, enumerable: false });
17980
- slot._def.thunk = () => target;
17981
- slot.parse = (input, pos, ctx) => target.parse(input, pos, ctx);
17982
- const meta = slot._meta;
17983
- meta.firstSet = target._meta.firstSet;
17984
- meta.canMatchNewline = target._meta.canMatchNewline;
17985
- meta.isTrivia = target._meta.isTrivia;
17986
- if (target._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = target._meta.triviaKindLabels;
17987
- else meta.triviaKindLabels = void 0;
17988
- if (target._meta.disjoint !== void 0) meta.disjoint = target._meta.disjoint;
17989
- else meta.disjoint = void 0;
17990
- }
17991
- function interpretedPieces(item) {
17992
- const fused = item[INTERPRETED_PIECES];
17993
- if (Array.isArray(fused)) return fused.map((entries) => ({ entries, plain: false }));
17994
- const carried = composedPiecesOf(item);
17995
- if (carried !== void 0) {
17996
- const { maps, opaque } = carriedRuleMapsDetailed(carried);
17997
- if (opaque.length > 0) {
17998
- throw new Error(
17999
- `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.`
18000
- );
17757
+ missing.push(name);
18001
17758
  }
18002
- return maps.map((entries) => ({ entries, plain: false }));
18003
17759
  }
18004
- if (isLinkableTable(item)) {
18005
- const rules2 = ruleMapOfCarried(item);
18006
- if (rules2 === void 0) {
18007
- 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}"`);
18008
17764
  }
18009
- 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`);
18010
17766
  }
18011
- 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;
18012
17774
  }
18013
- function ruleNamesOf(pieces) {
18014
- const names = /* @__PURE__ */ new Set();
18015
- for (const piece of pieces) {
18016
- 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");
18017
17778
  }
18018
- return [...names];
17779
+ return linkedMap(items, opts, false);
18019
17780
  }
18020
- function fusePieces(pieces, opts) {
18021
- let trivia2;
18022
- for (let i = pieces.length - 1; i >= 0 && trivia2 === void 0; i--) {
18023
- if (!pieces[i].plain) continue;
18024
- for (const [, rule] of pieces[i].entries) {
18025
- const t = rule._meta.grammarTrivia;
18026
- if (t) {
18027
- trivia2 = t;
18028
- break;
18029
- }
18030
- }
18031
- }
18032
- const winner = /* @__PURE__ */ new Map();
18033
- const entry = /* @__PURE__ */ new Map();
18034
- for (const piece of pieces) {
18035
- for (const [name, value] of piece.entries) {
18036
- const def = definitionOf(value);
18037
- if (def === void 0) continue;
18038
- winner.set(name, def);
18039
- entry.set(name, value);
18040
- }
18041
- }
18042
- const missing = /* @__PURE__ */ new Set();
18043
- for (const piece of pieces) {
18044
- for (const [name, value] of piece.entries) {
18045
- if (!isRefSlot(value)) continue;
18046
- const target = winner.get(name);
18047
- if (target === void 0) {
18048
- missing.add(name);
18049
- continue;
18050
- }
18051
- repointRef(value, target, name);
18052
- }
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] });
18053
17790
  }
18054
- if (missing.size > 0) throw new Error(missingRuleMessage(pieces, missing));
18055
- const out = {};
18056
- for (const [name, value] of entry) {
18057
- out[name] = value;
18058
- const meta = value._meta;
18059
- if (trivia2 !== void 0 && !meta.isTrivia) meta.grammarTrivia = trivia2;
18060
- if (opts?.hostMode !== void 0 && !meta.isTrivia) {
18061
- if (opts.hostMode === "cst") meta.grammarHostMode = "cst";
18062
- else meta.grammarHostMode = void 0;
18063
- }
18064
- }
18065
- Object.defineProperty(out, INTERPRETED_PIECES, {
18066
- value: pieces.filter((p) => p.plain).map((p) => p.entries),
18067
- enumerable: false
18068
- });
18069
- 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;
18070
17798
  }
18071
- function missingRuleMessage(pieces, missing) {
18072
- for (const piece of pieces) {
18073
- const deps = ruleDependencies(piece.entries.filter(([, v]) => definitionOf(v) !== void 0));
18074
- for (const [name, ds] of deps) {
18075
- for (const d of ds) if (missing.has(d)) return `fuseInterpreted: rule "${name}" references missing rule "${d}"`;
18076
- }
18077
- }
18078
- return `fuseInterpreted: missing rule(s) ${[...missing].map((n) => `"${n}"`).join(", ")}`;
17799
+ function composeLeaf(items) {
17800
+ return linkedMap(items, void 0, true);
18079
17801
  }
18080
17802
 
18081
17803
  // src/analysis/grammar.ts
@@ -18108,7 +17830,7 @@ function analyzeGrammarGating(grammar, opts) {
18108
17830
  }
18109
17831
 
18110
17832
  // src/analysis/diagnose.ts
18111
- var isCombinator = (g) => typeof g === "object" && g !== null && "_def" in g;
17833
+ var isCombinator2 = (g) => typeof g === "object" && g !== null && "_def" in g;
18112
17834
  var isRuleEntries = (g) => Array.isArray(g);
18113
17835
  function diagnoseGrammar(grammar, opts) {
18114
17836
  const depth = degradationCaptureDepth();
@@ -18152,7 +17874,7 @@ function formatGrammarDiagnosis(d) {
18152
17874
  return lines;
18153
17875
  }
18154
17876
  function runAnalysis(grammar, opts) {
18155
- if (isCombinator(grammar)) return analyzeGating(grammar, opts);
17877
+ if (isCombinator2(grammar)) return analyzeGating(grammar, opts);
18156
17878
  if (isRuleEntries(grammar)) return analyzeGatingRules(grammar, opts);
18157
17879
  return analyzeGrammarGating(grammar, opts);
18158
17880
  }
@@ -19420,6 +19142,29 @@ function gate(predicate) {
19420
19142
  };
19421
19143
  }
19422
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
+
19423
19168
  // src/combinators/completions.ts
19424
19169
  function completionsAt(target, input, offset, options = {}) {
19425
19170
  const probe = { offset, best: null };
@@ -20102,7 +19847,7 @@ function grammarCoverageDefinitions(entry, winners) {
20102
19847
  }
20103
19848
  function composedGrammarCoverageDefinitions(grammar, startRule) {
20104
19849
  const winners = composedCoverageRules(grammar);
20105
- 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");
20106
19851
  const entry = winners[startRule];
20107
19852
  if (!entry) throw new TypeError(`semantic coverage start rule ${JSON.stringify(startRule)} is not a final winner`);
20108
19853
  return grammarCoverageDefinitions(entry, winners);
@@ -20272,7 +20017,7 @@ function coverageEntry(entry, collector, maps, trace) {
20272
20017
  case "optional":
20273
20018
  return optional(build(def.parser));
20274
20019
  case "attempt": {
20275
- const base2 = attempt(build(def.parser));
20020
+ const base2 = attempt(build(def.parser), def.contain === true ? { contain: true } : void 0);
20276
20021
  const id = maps.attempts.get(parser2);
20277
20022
  return id === void 0 ? base2 : {
20278
20023
  ...base2,
@@ -20344,7 +20089,7 @@ function coverageEntry(entry, collector, maps, trace) {
20344
20089
  }
20345
20090
  function runWithGrammarCoverage(entry, input, options = {}) {
20346
20091
  if (typeof entry === "function") throw new TypeError("runWithGrammarCoverage currently requires an interpreter combinator entry");
20347
- const plan = buildGrammarPlan(entry);
20092
+ const plan = buildGrammarPlan(entry, linkedWinnersOf(entry));
20348
20093
  const definitions = plan.definitions;
20349
20094
  const collector = options.collector ?? createGrammarCoverageCollector(definitions);
20350
20095
  const { collector: _collector, trace, ...runOptions } = options;
@@ -20418,13 +20163,13 @@ function buildTriviaIndex(root, input = "", opts) {
20418
20163
  }
20419
20164
 
20420
20165
  // src/cst/walk.ts
20421
- function isCombinator2(value) {
20166
+ function isCombinator3(value) {
20422
20167
  return !!value && typeof value === "object" && "_def" in value && "_meta" in value && typeof value.parse === "function";
20423
20168
  }
20424
20169
  function reflectionFor(grammar) {
20425
20170
  const stamped = grammarReflectionOf(grammar);
20426
20171
  if (stamped) return stamped;
20427
- const entries = Object.entries(grammar).filter((entry) => isCombinator2(entry[1]));
20172
+ const entries = Object.entries(grammar).filter((entry) => isCombinator3(entry[1]));
20428
20173
  return collectGrammarReflection(entries);
20429
20174
  }
20430
20175
  function createVisitor(grammar, spec) {