parseman 0.55.4 → 0.56.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 (88) hide show
  1. package/dist/analysis/choice-cost.d.ts.map +1 -1
  2. package/dist/analysis/commitment.d.ts.map +1 -1
  3. package/dist/analysis/diagnostics.js +3353 -3095
  4. package/dist/analysis/diagnostics.js.map +3 -3
  5. package/dist/analysis/duplication.d.ts.map +1 -1
  6. package/dist/analysis/gating.d.ts.map +1 -1
  7. package/dist/cli/index.js +1813 -1555
  8. package/dist/cli/index.js.map +3 -3
  9. package/dist/combinators/attempt.d.ts.map +1 -1
  10. package/dist/combinators/choice.d.ts.map +1 -1
  11. package/dist/combinators/dispatch.d.ts.map +1 -1
  12. package/dist/combinators/expect.d.ts.map +1 -1
  13. package/dist/combinators/first-set.d.ts.map +1 -1
  14. package/dist/combinators/lines.d.ts +79 -0
  15. package/dist/combinators/lines.d.ts.map +1 -0
  16. package/dist/combinators/trivia-skip.d.ts +1 -0
  17. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  18. package/dist/compiler/fields.d.ts.map +1 -1
  19. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  20. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  21. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  22. package/dist/compiler/token-capability.js +25 -0
  23. package/dist/compiler/token-capability.js.map +1 -1
  24. package/dist/coverage.d.ts.map +1 -1
  25. package/dist/cst/reflection.d.ts.map +1 -1
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1905 -1625
  29. package/dist/index.js.map +3 -3
  30. package/dist/language-service/index.js +2 -0
  31. package/dist/language-service/index.js.map +1 -1
  32. package/dist/parse-context.d.ts.map +1 -1
  33. package/dist/plugin/evaluator.d.ts.map +1 -1
  34. package/dist/plugin/index.js +1318 -1045
  35. package/dist/plugin/index.js.map +3 -3
  36. package/dist/plugin/ir-eval.d.ts +0 -4
  37. package/dist/plugin/ir-eval.d.ts.map +1 -1
  38. package/dist/run/index.js +2 -0
  39. package/dist/run/index.js.map +1 -1
  40. package/dist/spec/index.js +15 -0
  41. package/dist/spec/index.js.map +1 -1
  42. package/dist/spec/model.d.ts.map +1 -1
  43. package/dist/table/assemble.d.ts +0 -65
  44. package/dist/table/assemble.d.ts.map +1 -1
  45. package/dist/table/emit.d.ts.map +1 -1
  46. package/dist/table/encode.d.ts.map +1 -1
  47. package/dist/table/exec.d.ts.map +1 -1
  48. package/dist/table/index.js +3442 -3192
  49. package/dist/table/index.js.map +3 -3
  50. package/dist/table/line-scan.d.ts +9 -0
  51. package/dist/table/line-scan.d.ts.map +1 -0
  52. package/dist/table/program.d.ts +11 -2
  53. package/dist/table/program.d.ts.map +1 -1
  54. package/dist/types.d.ts +21 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/package.json +1 -1
  58. package/src/analysis/choice-cost.ts +1 -0
  59. package/src/analysis/commitment.ts +13 -0
  60. package/src/analysis/duplication.ts +3 -0
  61. package/src/analysis/gating.ts +5 -0
  62. package/src/analysis/rebuild.ts +2 -2
  63. package/src/combinators/attempt.ts +2 -0
  64. package/src/combinators/choice.ts +3 -0
  65. package/src/combinators/dispatch.ts +3 -0
  66. package/src/combinators/expect.ts +5 -0
  67. package/src/combinators/first-set.ts +9 -1
  68. package/src/combinators/lines.ts +244 -0
  69. package/src/combinators/trivia-skip.ts +4 -2
  70. package/src/compiler/fields.ts +3 -0
  71. package/src/compiler/grammar-coverage-ids.ts +1 -0
  72. package/src/compiler/ir-serialize.ts +10 -0
  73. package/src/compiler/token-alphabet.ts +3 -1
  74. package/src/coverage.ts +6 -0
  75. package/src/cst/reflection.ts +3 -0
  76. package/src/index.ts +2 -0
  77. package/src/parse-context.ts +2 -0
  78. package/src/plugin/evaluator.ts +4 -0
  79. package/src/plugin/ir-eval.ts +3 -0
  80. package/src/spec/model.ts +15 -0
  81. package/src/table/assemble.ts +6 -12
  82. package/src/table/emit.ts +7 -6
  83. package/src/table/encode.ts +18 -6
  84. package/src/table/exec.ts +3 -0
  85. package/src/table/line-scan.ts +19 -0
  86. package/src/table/program.ts +17 -2
  87. package/src/types.ts +14 -0
  88. package/src/version.ts +1 -1
@@ -163,6 +163,11 @@ function matchesEmptyBody(p, seen, resolve3) {
163
163
  return true;
164
164
  }
165
165
  }
166
+ // A run consumes at least its first line's prefix, and its body after that.
167
+ case "lines":
168
+ return me(d.first ?? d.prefix) && me(d.body);
169
+ case "columns":
170
+ return d.n === 0;
166
171
  default:
167
172
  return true;
168
173
  }
@@ -277,6 +282,11 @@ function firstSetBody(p, seen, resolve3, activeTrivia) {
277
282
  resolve3,
278
283
  d.clearTrivia ? null : d.triviaParser ?? activeTrivia
279
284
  );
285
+ // A run starts with its first line's prefix; a nullable prefix starts with anything.
286
+ case "lines": {
287
+ const opening = d.first ?? d.prefix;
288
+ return empties(opening) ? any() : fs3(opening);
289
+ }
280
290
  default:
281
291
  return p._meta.firstSet;
282
292
  }
@@ -786,6 +796,8 @@ function createParseContext() {
786
796
  _triviaCaptureMask: void 0,
787
797
  trackLines: false,
788
798
  state: void 0,
799
+ lineView: void 0,
800
+ _columnsSplit: void 0,
789
801
  build: void 0,
790
802
  _errors: void 0,
791
803
  _tolerant: void 0,
@@ -1726,6 +1738,9 @@ function childrenOf(def) {
1726
1738
  ];
1727
1739
  case "routed":
1728
1740
  return def.fallback ? [def.fallback] : [];
1741
+ case "lines":
1742
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
1743
+ case "columns":
1729
1744
  case "literal":
1730
1745
  case "regex":
1731
1746
  case "keywords":
@@ -2577,11 +2592,12 @@ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
2577
2592
  return result;
2578
2593
  }
2579
2594
  function saveLookaheadMark(ctx) {
2580
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
2595
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null, split: ctx._columnsSplit };
2581
2596
  }
2582
2597
  function rollbackLookahead(ctx, mark) {
2583
2598
  rollbackTrivia(ctx, mark);
2584
2599
  if (ctx._probe) ctx._probe.best = mark.probeBest;
2600
+ ctx._columnsSplit = mark.split;
2585
2601
  }
2586
2602
  function scanWithLabels(input, cur, ctx) {
2587
2603
  const triviaP = ctx.trivia;
@@ -3427,6 +3443,12 @@ function derive(c, seen) {
3427
3443
  return deriveExpected2(def.parser);
3428
3444
  case "expect":
3429
3445
  return def.expected;
3446
+ case "lines": {
3447
+ const opening = def.first ?? def.prefix;
3448
+ return matchesEmpty(opening) ? [...deriveExpected2(opening), ...deriveExpected2(def.body)] : deriveExpected2(opening);
3449
+ }
3450
+ case "columns":
3451
+ return [`columns(${def.n})`];
3430
3452
  case "node":
3431
3453
  case "grammar":
3432
3454
  case "trivia":
@@ -3611,9 +3633,11 @@ function choice(...args) {
3611
3633
  for (let i = 0; i < parsers.length; i++) {
3612
3634
  if (gates[i] && !gates[i](ctx.state)) continue;
3613
3635
  const mark = saveTriviaMark(ctx);
3636
+ const split = ctx._columnsSplit;
3614
3637
  const result = parsers[i].parse(input, pos, ctx);
3615
3638
  if (!result.ok) {
3616
3639
  rollbackTrivia(ctx, mark);
3640
+ ctx._columnsSplit = split;
3617
3641
  const at = result.span.start;
3618
3642
  if (at > expectedAt) {
3619
3643
  expectedAt = at;
@@ -3834,6 +3858,9 @@ function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
3834
3858
  return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen)) || def.recoverAt !== void 0 && parserUsesRouted(def.recoverAt, seen) || def.stopAt !== void 0 && parserUsesRouted(def.stopAt, seen);
3835
3859
  case "recover":
3836
3860
  return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
3861
+ // The body parses its own input, so only the checks on this one can reuse a routed head.
3862
+ case "lines":
3863
+ return [def.prefix, def.first, def.lazy].some((entry) => entry !== void 0 && parserUsesRouted(entry, seen));
3837
3864
  case "lazy":
3838
3865
  try {
3839
3866
  return parserUsesRouted(def.thunk(), seen);
@@ -4084,9 +4111,11 @@ function attempt(parser2, options) {
4084
4111
  const mark = saveCstMark(ctx);
4085
4112
  const log = ctx._triviaLog?.length;
4086
4113
  const rootLog = ctx._rootTriviaLog?.length;
4114
+ const split = ctx._columnsSplit;
4087
4115
  const result = parser2.parse(input, pos, ctx);
4088
4116
  if (result.ok) return result;
4089
4117
  rollbackCstCapture(ctx, mark);
4118
+ ctx._columnsSplit = split;
4090
4119
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
4091
4120
  if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
4092
4121
  if (result.committed && !contain) return result;
@@ -4601,12 +4630,12 @@ function beginCompileDegradationDrain() {
4601
4630
  return true;
4602
4631
  });
4603
4632
  if (fresh.length === 0) return;
4604
- const lines = formatDegradations(fresh);
4633
+ const lines2 = formatDegradations(fresh);
4605
4634
  if (resolveDegradationLevel() === "error") {
4606
4635
  throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
4607
- ${lines.join("\n")}`);
4636
+ ${lines2.join("\n")}`);
4608
4637
  }
4609
- for (const l of lines) console.warn(l);
4638
+ for (const l of lines2) console.warn(l);
4610
4639
  };
4611
4640
  }
4612
4641
 
@@ -4853,6 +4882,8 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
4853
4882
  return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen)) || d.recoverAt !== void 0 && parserHasOwnFields(d.recoverAt, seen) || d.stopAt !== void 0 && parserHasOwnFields(d.stopAt, seen);
4854
4883
  case "recover":
4855
4884
  return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
4885
+ case "lines":
4886
+ return parserHasOwnFields(d.body, seen);
4856
4887
  case "routed":
4857
4888
  return d.fallback ? parserHasOwnFields(d.fallback, seen) : false;
4858
4889
  case "many":
@@ -5266,801 +5297,230 @@ function sourceLeaf(root, fn) {
5266
5297
  return makeLeaf(root, fn, true);
5267
5298
  }
5268
5299
 
5269
- // src/analysis/gating.ts
5270
- function childrenOf2(d) {
5271
- switch (d.tag) {
5272
- case "sequence":
5273
- case "choice":
5274
- return d.parsers;
5275
- case "dispatch":
5276
- return [
5277
- d.selector,
5278
- ...d.cases.map((c) => c.parser),
5279
- ...d.matchers ? d.matchers.map((c) => c.parser) : [],
5280
- ...d.otherwise === void 0 ? [] : [d.otherwise]
5281
- ];
5282
- case "sepBy":
5283
- return [d.parser, d.separator];
5284
- case "recover":
5285
- return [d.parser, d.sentinel];
5286
- case "scanTo":
5287
- return [
5288
- d.sentinel,
5289
- ...d.skip,
5290
- ...d.recoverAt === void 0 ? [] : [d.recoverAt],
5291
- ...d.stopAt === void 0 ? [] : [d.stopAt]
5292
- ];
5293
- case "grammar":
5294
- return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
5295
- case "routed":
5296
- return d.fallback ? [d.fallback] : [];
5297
- // A `lazy` is a REFERENCE, not a subtree. Descending through it would make
5298
- // every rule's walk cover the whole reachable grammar — site paths become
5299
- // nonsense, and a structural hash includes half the grammar. Treated as a
5300
- // leaf, keyed by the rule name it refers to, which is also the right
5301
- // semantics: two productions referencing `g.Ident` really do fill that slot
5302
- // the same way.
5303
- case "lazy":
5304
- case "literal":
5305
- case "regex":
5306
- case "keywords":
5307
- case "guard":
5308
- case "adjacency":
5309
- case "unknown":
5310
- return [];
5311
- default: {
5312
- const rec = d;
5313
- return rec.parser ? [rec.parser] : [];
5314
- }
5300
+ // src/table/lex-body.ts
5301
+ function buildLexBodyRecognizer(spec, constants) {
5302
+ const raw = constants[spec[0]];
5303
+ if (!(raw instanceof RegExp)) {
5304
+ throw new TypeError("table lexical body does not reference a RegExp constant");
5315
5305
  }
5316
- }
5317
- var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
5318
- function firstSetToString(fs3) {
5319
- if (fs3.kind === "any") return "ANY";
5320
- if (fs3.kind === "empty") return "(empty)";
5321
- return fs3.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
5306
+ if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
5307
+ throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
5308
+ }
5309
+ const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
5310
+ const suffix = spec[1];
5311
+ return (input, pos) => {
5312
+ re.lastIndex = pos;
5313
+ if (!re.test(input)) return -1;
5314
+ const end = re.lastIndex;
5315
+ if (suffix < 0) return end * 2;
5316
+ return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
5317
+ };
5322
5318
  }
5323
5319
 
5324
- // src/analysis/commitment.ts
5325
- function mayFail(p, seen = /* @__PURE__ */ new Set()) {
5326
- if (seen.has(p)) return true;
5327
- seen.add(p);
5328
- const d = p._def;
5329
- switch (d.tag) {
5330
- case "literal":
5331
- return d.value.length > 0;
5332
- case "trivia":
5333
- return false;
5334
- // `optional`/`many`/a min-0 `sepBy` swallow an ORDINARY failure — zero
5335
- // repetitions is a match. They do NOT swallow a COMMITTED one: a dispatch
5336
- // that has selected an arm and then failed inside it breaks out past every
5337
- // enclosing repeat, straight to the fallible boundary. So these are total
5338
- // exactly when their body cannot commit. (Measured: treating them as
5339
- // unconditionally total left an extra zero-width leaf in
5340
- // `BareInterpolatedSelector` on benchmark.less — a rollback the legacy
5341
- // build fired and this one had deleted.)
5342
- case "optional":
5343
- case "many":
5344
- return mayCommitFailure(d.parser);
5345
- case "sepBy":
5346
- return d.min >= 1 ? mayFail(d.parser, seen) : mayCommitFailure(d.parser) || mayCommitFailure(d.separator);
5347
- case "oneOrMore":
5348
- return mayFail(d.parser, seen);
5349
- case "sequence":
5350
- return d.parsers.some((x) => mayFail(x, seen));
5351
- // Delegating wrappers: as fallible as what they wrap.
5352
- case "node":
5353
- case "transform":
5354
- case "label":
5355
- case "field":
5356
- case "expect":
5357
- case "withCtx":
5358
- case "grammar":
5359
- case "token":
5360
- case "leaf":
5361
- return mayFail(d.parser, seen);
5362
- case "lazy": {
5363
- try {
5364
- return mayFail(d.thunk(), seen);
5365
- } catch {
5366
- return true;
5367
- }
5368
- }
5369
- // An adjacency assertion is a TEST — failing is its whole job.
5370
- case "adjacency":
5371
- return true;
5372
- default:
5373
- return true;
5320
+ // src/table/lex-program.ts
5321
+ var LEX_NODE_TERMINAL = 0;
5322
+ var LEX_NODE_SEQUENCE2 = 1;
5323
+ var LEX_NODE_SEQUENCE3 = 2;
5324
+ var LEX_NODE_SEQUENCE4 = 3;
5325
+ var LEX_NODE_SEQUENCE5 = 4;
5326
+ var LEX_NODE_CHOICE2 = 5;
5327
+ var LEX_NODE_CHOICE4 = 6;
5328
+ var LEX_NODE_CHOICE8 = 7;
5329
+ var LEX_NODE_NOT = 8;
5330
+ var LEX_NODE_OPTIONAL = 9;
5331
+ function lexProgramDigest(words) {
5332
+ let hash = 2166136261;
5333
+ for (const word2 of words) {
5334
+ hash ^= word2 | 0;
5335
+ hash = Math.imul(hash, 16777619);
5374
5336
  }
5337
+ return hash | 0;
5375
5338
  }
5376
- function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set(), triviaActive = true) {
5377
- if (seen.has(p)) return false;
5378
- seen.add(p);
5379
- const d = p._def;
5380
- switch (d.tag) {
5381
- // Atomic / non-capturing-then-failing: a failure happens before any push.
5382
- case "literal":
5383
- case "regex":
5384
- case "keywords":
5385
- case "guard":
5386
- // Adjacency probes with `_cap = 0` / no `commit()`: it cannot push anything.
5387
- case "adjacency":
5388
- case "not":
5389
- // peek(): emitted under a non-capturing probe ctx and zero-width on both
5390
- // outcomes, so it can never leave a partial capture behind.
5391
- case "peek":
5392
- case "trivia":
5393
- case "token":
5394
- case "leaf":
5395
- case "scanTo":
5396
- case "unknown":
5397
- return false;
5398
- // node() captures into its own private buffers and rolls them back on
5399
- // failure (emitNode restores _ctx.* and never pushes on the !ok path).
5400
- case "node":
5401
- return false;
5402
- // choice/firstMatch already roll back each failed arm; on overall failure
5403
- // nothing committed remains.
5404
- case "choice":
5405
- return false;
5406
- case "dispatch":
5407
- return capturesLeaf(d.selector, new Set(seen)) || d.cases.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) || (d.matchers ? d.matchers.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) : false) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, new Set(seen), triviaActive) || capturesLeaf(d.otherwise, new Set(seen)) : false);
5408
- case "attempt":
5409
- return false;
5410
- // optional never fails; many/oneOrMore only "fail" with zero captured items.
5411
- case "optional":
5412
- case "many":
5413
- case "oneOrMore":
5414
- return false;
5415
- // sepBy emits its own per-iteration rollback in emitSepBy.
5416
- case "sepBy":
5417
- return false;
5418
- // A sequence is the real case: an earlier capturing term followed by a term
5419
- // that can fail leaves the earlier captures buffered.
5420
- //
5421
- // BOTH halves of that sentence are load-bearing, and the second half is what
5422
- // this walk exists to exploit. "Some earlier term captures" alone was the old
5423
- // test, and it is true of very nearly every sequence in a real grammar — a
5424
- // sequence almost always opens with a literal or a node. The rollback is only
5425
- // reachable if something AFTER the capture can still fail; once the remainder
5426
- // is total (`optional`/`many`/trivia/a min-0 `sepBy`), the sequence has no
5427
- // failure path left to roll back from, and the save+restore is dead code.
5428
- //
5429
- // `captured` tracks whether anything is in the buffers yet as the scan moves
5430
- // left to right. Two things put it there:
5431
- // - a term that captures (`capturesLeaf`/`hasNodeDef`), and
5432
- // - the inter-term TRIVIA scan, which under `capturing` pushes into
5433
- // `_cstRawChildren`/`_cstTriviaLog` BEFORE the term at each boundary
5434
- // i >= 1. `emitSeqValues` only rewinds that push on the term's SUCCESS
5435
- // path (the "matched empty, so leave the whitespace out of the span"
5436
- // branch); a term that FAILS breaks straight out with the trivia still
5437
- // buffered, so from boundary 1 onward the buffers are dirty regardless of
5438
- // what the earlier terms did.
5439
- // Each term is also asked about itself: a nested sequence can leave its own
5440
- // partial capture behind, and its terms are emitted inline into this same
5441
- // block with no boundary of their own to roll them back.
5442
- case "sequence": {
5443
- const parts = d.parsers;
5444
- let captured = false;
5445
- for (let i = 0; i < parts.length; i++) {
5446
- const part = parts[i];
5447
- if (i > 0 && triviaActive) captured = true;
5448
- if (captured && mayFail(part)) return true;
5449
- if (mayLeavePartialCapture(part, seen, triviaActive)) return true;
5450
- if (capturesLeaf(part) || hasNodeDef(part)) captured = true;
5451
- }
5452
- return false;
5453
- }
5454
- // Delegating wrappers: defer to the wrapped parser.
5455
- case "transform":
5456
- case "label":
5457
- case "field":
5458
- case "expect":
5459
- case "withCtx":
5460
- case "grammar":
5461
- return mayLeavePartialCapture(d.parser, seen, triviaActive);
5462
- case "recover":
5463
- return mayLeavePartialCapture(d.parser, seen, triviaActive);
5464
- case "lazy": {
5465
- try {
5466
- return mayLeavePartialCapture(d.thunk(), seen, triviaActive);
5467
- } catch {
5468
- return true;
5469
- }
5470
- }
5471
- // Unknown shapes: be safe and keep the rollback.
5472
- default:
5473
- return true;
5339
+ function classHas(cls, code) {
5340
+ if (code < 0) return false;
5341
+ if (code < 128) return cls.ascii[code] === 1;
5342
+ for (let i = 0; i < cls.hi.length; i += 2) {
5343
+ if (code >= cls.hi[i] && code <= cls.hi[i + 1]) return true;
5474
5344
  }
5345
+ return false;
5475
5346
  }
5476
- function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
5477
- if (seen.has(p)) return false;
5478
- seen.add(p);
5479
- const d = p._def;
5480
- switch (d.tag) {
5481
- case "literal":
5482
- case "regex":
5483
- case "keywords":
5484
- case "node":
5485
- case "token":
5486
- case "routed":
5487
- case "leaf":
5488
- return true;
5489
- case "not":
5490
- case "peek":
5491
- case "guard":
5492
- case "adjacency":
5493
- case "trivia":
5494
- case "unknown":
5495
- return false;
5496
- case "sequence":
5497
- case "choice":
5498
- return d.parsers.some((x) => capturesLeaf(x, seen));
5499
- case "dispatch":
5500
- return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => capturesLeaf(entry.parser, seen)) : false) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
5501
- case "sepBy":
5502
- return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
5503
- case "many":
5504
- case "oneOrMore":
5505
- case "optional":
5506
- case "attempt":
5507
- case "transform":
5508
- case "label":
5509
- case "field":
5510
- case "expect":
5511
- case "withCtx":
5512
- case "grammar":
5513
- case "recover":
5514
- return capturesLeaf(d.parser, seen);
5515
- case "scanTo":
5516
- return true;
5517
- case "lazy": {
5518
- try {
5519
- return capturesLeaf(d.thunk(), seen);
5520
- } catch {
5521
- return true;
5522
- }
5523
- }
5524
- default:
5525
- return true;
5347
+ var committed = (ctx) => ctx._fc === true;
5348
+ function trackLexLines(ctx, input, end) {
5349
+ let at = ctx._lineScannedTo ?? 0;
5350
+ const starts = ctx._lineStarts;
5351
+ if (starts === void 0 || end <= at) return;
5352
+ while (at < end) {
5353
+ if (input.charCodeAt(at) === 10) starts.push(at + 1);
5354
+ at++;
5526
5355
  }
5356
+ ctx._lineScannedTo = end;
5527
5357
  }
5528
- function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5529
- if (seen.has(p)) return false;
5530
- seen.add(p);
5531
- const d = p._def;
5532
- switch (d.tag) {
5533
- case "node":
5534
- return true;
5535
- case "lazy": {
5536
- try {
5537
- return hasNodeDef(d.thunk(), seen);
5538
- } catch {
5539
- return false;
5358
+ function terminalNode(matcher, failure, tracksLines) {
5359
+ const match = (input, pos) => {
5360
+ const result = matcher(input, pos);
5361
+ return result < 0 ? -1 : result / 2;
5362
+ };
5363
+ return {
5364
+ match,
5365
+ run: (input, pos, ctx) => {
5366
+ const end = match(input, pos);
5367
+ if (end < 0) {
5368
+ ctx._fe = pos;
5369
+ ctx._fx = failure;
5370
+ if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
5371
+ return -1;
5540
5372
  }
5373
+ if (tracksLines) trackLexLines(ctx, input, end);
5374
+ return end;
5541
5375
  }
5542
- case "grammar":
5543
- case "trivia":
5544
- case "token":
5545
- case "leaf":
5546
- case "label":
5547
- case "field":
5548
- case "optional":
5549
- case "many":
5550
- case "oneOrMore":
5551
- case "not":
5552
- case "peek":
5553
- case "transform":
5554
- return hasNodeDef(d.parser, seen);
5555
- case "sequence":
5556
- case "choice":
5557
- return d.parsers.some((x) => hasNodeDef(x, seen));
5558
- case "dispatch":
5559
- return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => hasNodeDef(entry.parser, seen)) : false) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
5560
- case "sepBy":
5561
- return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
5562
- case "scanTo":
5563
- return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen)) || d.recoverAt !== void 0 && hasNodeDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasNodeDef(d.stopAt, seen);
5564
- case "recover":
5565
- return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
5566
- case "expect":
5567
- return hasNodeDef(d.parser, seen);
5568
- case "withCtx":
5569
- return hasNodeDef(d.parser, seen);
5570
- default:
5571
- return false;
5572
- }
5376
+ };
5573
5377
  }
5574
- function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
5575
- if (seen.has(p)) return false;
5576
- seen.add(p);
5577
- const d = p._def;
5578
- switch (d.tag) {
5579
- case "node":
5580
- return d.build !== void 0 || hasDirectBuildDef(d.parser, seen);
5581
- case "lazy": {
5582
- try {
5583
- return hasDirectBuildDef(d.thunk(), seen);
5584
- } catch {
5585
- return false;
5586
- }
5378
+ function sequence2(a, b) {
5379
+ return {
5380
+ match: (input, pos) => {
5381
+ const e0 = a.match(input, pos);
5382
+ return e0 < 0 ? -1 : b.match(input, e0);
5383
+ },
5384
+ run: (input, pos, ctx) => {
5385
+ const e0 = a.run(input, pos, ctx);
5386
+ if (e0 < 0) return -1;
5387
+ ctx._fc = false;
5388
+ return b.run(input, e0, ctx);
5587
5389
  }
5588
- case "grammar":
5589
- case "trivia":
5590
- case "token":
5591
- case "leaf":
5592
- case "label":
5593
- case "field":
5594
- case "optional":
5595
- case "many":
5596
- case "oneOrMore":
5597
- case "attempt":
5598
- case "not":
5599
- case "peek":
5600
- case "withCtx":
5601
- case "expect":
5602
- case "transform":
5603
- return hasDirectBuildDef(d.parser, seen);
5604
- case "sequence":
5605
- case "choice":
5606
- return d.parsers.some((x) => hasDirectBuildDef(x, seen));
5607
- case "dispatch":
5608
- return hasDirectBuildDef(d.selector, seen) || d.cases.some((x) => hasDirectBuildDef(x.parser, seen)) || (d.matchers ? d.matchers.some((x) => hasDirectBuildDef(x.parser, seen)) : false) || (d.otherwise ? hasDirectBuildDef(d.otherwise, seen) : false);
5609
- case "sepBy":
5610
- return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
5611
- case "scanTo":
5612
- return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen)) || d.recoverAt !== void 0 && hasDirectBuildDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasDirectBuildDef(d.stopAt, seen);
5613
- case "recover":
5614
- return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
5615
- case "routed":
5616
- return d.fallback ? hasDirectBuildDef(d.fallback, seen) : false;
5617
- default:
5618
- return false;
5619
- }
5390
+ };
5620
5391
  }
5621
- function mayCommitFailure(p, resolve3, memo = /* @__PURE__ */ new Map()) {
5622
- return reachesMarked(p, (node2) => commitFailureSuccessors(node2, resolve3), memo);
5392
+ function sequence3(a, b, c) {
5393
+ return {
5394
+ match: (input, pos) => {
5395
+ const e0 = a.match(input, pos);
5396
+ if (e0 < 0) return -1;
5397
+ const e1 = b.match(input, e0);
5398
+ return e1 < 0 ? -1 : c.match(input, e1);
5399
+ },
5400
+ run: (input, pos, ctx) => {
5401
+ const e0 = a.run(input, pos, ctx);
5402
+ if (e0 < 0) return -1;
5403
+ ctx._fc = false;
5404
+ const e1 = b.run(input, e0, ctx);
5405
+ if (e1 < 0) return -1;
5406
+ ctx._fc = false;
5407
+ return c.run(input, e1, ctx);
5408
+ }
5409
+ };
5623
5410
  }
5624
- var NO_EDGES = [];
5625
- function commitFailureSuccessors(p, resolve3) {
5626
- const d = p._def;
5627
- switch (d.tag) {
5628
- case "dispatch":
5629
- case "unknown":
5630
- return true;
5631
- case "choice":
5632
- case "sequence":
5633
- return d.parsers;
5634
- // A contained transaction reports every failure uncommitted.
5635
- case "attempt":
5636
- return d.contain === true ? NO_EDGES : [d.parser];
5637
- case "many":
5638
- case "oneOrMore":
5639
- case "optional":
5640
- case "transform":
5641
- case "label":
5642
- case "field":
5643
- case "grammar":
5644
- case "node":
5645
- case "token":
5646
- case "leaf":
5647
- case "withCtx":
5648
- return [d.parser];
5649
- case "sepBy":
5650
- return [d.parser, d.separator];
5651
- case "scanTo":
5652
- return [
5653
- d.sentinel,
5654
- ...d.skip,
5655
- ...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
5656
- ...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
5657
- ];
5658
- case "lazy": {
5659
- try {
5660
- return [d.thunk()];
5661
- } catch {
5662
- const name = p._ruleName;
5663
- const target = name === void 0 ? void 0 : resolve3?.(name);
5664
- return target === void 0 ? true : [target];
5665
- }
5411
+ function sequence4(a, b, c, d) {
5412
+ const abc = sequence3(a, b, c);
5413
+ return {
5414
+ match: (input, pos) => {
5415
+ const end = abc.match(input, pos);
5416
+ return end < 0 ? -1 : d.match(input, end);
5417
+ },
5418
+ run: (input, pos, ctx) => {
5419
+ const end = abc.run(input, pos, ctx);
5420
+ if (end < 0) return -1;
5421
+ ctx._fc = false;
5422
+ return d.run(input, end, ctx);
5666
5423
  }
5667
- default:
5668
- return NO_EDGES;
5669
- }
5424
+ };
5670
5425
  }
5671
- function reachesMarked(root, successors, memo) {
5672
- const known = memo.get(root);
5673
- if (known !== void 0) return known;
5674
- const order = /* @__PURE__ */ new Map();
5675
- const low = [];
5676
- const found = [];
5677
- const stack = [];
5678
- const visit = (node2) => {
5679
- const at = low.length;
5680
- order.set(node2, at);
5681
- low.push(at);
5682
- found.push(false);
5683
- stack.push(node2);
5684
- const next = successors(node2);
5685
- if (next === true) found[at] = true;
5686
- else {
5687
- for (const child of next) {
5688
- if (found[at]) break;
5689
- const hit = memo.get(child);
5690
- if (hit !== void 0) {
5691
- if (hit) found[at] = true;
5692
- continue;
5693
- }
5694
- const seen = order.get(child);
5695
- if (seen !== void 0) {
5696
- if (seen < low[at]) low[at] = seen;
5697
- continue;
5698
- }
5699
- const k = visit(child);
5700
- if (low[k] < low[at]) low[at] = low[k];
5701
- if (found[k]) found[at] = true;
5702
- }
5703
- }
5704
- if (low[at] === at) {
5705
- const answer = found[at];
5706
- let member;
5707
- do {
5708
- member = stack.pop();
5709
- memo.set(member, answer);
5710
- } while (member !== node2);
5426
+ function sequence5(a, b, c, d, e) {
5427
+ const abcd = sequence4(a, b, c, d);
5428
+ return {
5429
+ match: (input, pos) => {
5430
+ const end = abcd.match(input, pos);
5431
+ return end < 0 ? -1 : e.match(input, end);
5432
+ },
5433
+ run: (input, pos, ctx) => {
5434
+ const end = abcd.run(input, pos, ctx);
5435
+ if (end < 0) return -1;
5436
+ ctx._fc = false;
5437
+ return e.run(input, end, ctx);
5711
5438
  }
5712
- return at;
5713
5439
  };
5714
- return found[visit(root)];
5715
5440
  }
5716
- function hasSemanticReduction(roots, externalRefs) {
5717
- const seen = /* @__PURE__ */ new Set();
5718
- const visit = (parser2) => {
5719
- if (seen.has(parser2)) return false;
5720
- seen.add(parser2);
5721
- const def = parser2._def;
5722
- if (def.tag === "transform" && !def.recognitionOnly) return true;
5723
- if (def.tag === "choice" && def.gates.some(Boolean)) return true;
5724
- if (def.tag === "guard" || def.tag === "withCtx") return true;
5725
- if (def.tag === "node" && def.build !== void 0) return true;
5726
- if (def.tag === "lazy") {
5727
- if (externalRefs?.has(parser2)) return false;
5728
- try {
5729
- return visit(def.thunk());
5730
- } catch {
5731
- return true;
5732
- }
5733
- }
5734
- return childrenOf2(def).some(visit);
5735
- };
5736
- return roots.some(visit);
5441
+ function appendExpected(acc, values) {
5442
+ if (values === void 0 || values.length === 0) return acc;
5443
+ if (acc === void 0) return values.slice();
5444
+ acc.push(...values);
5445
+ return acc;
5737
5446
  }
5738
- function classifyRuleMap(ruleMap) {
5739
- const externalRefs = /* @__PURE__ */ new Set();
5740
- const scanned = /* @__PURE__ */ new Set();
5741
- const scanExternal = (p) => {
5742
- if (scanned.has(p)) return;
5743
- scanned.add(p);
5744
- const def = p._def;
5745
- if (def.tag === "lazy") {
5746
- let resolved;
5747
- try {
5748
- resolved = def.thunk();
5749
- } catch {
5750
- resolved = void 0;
5447
+ function choiceNode(arms, classes, armExpected, choiceExpected) {
5448
+ let matchTail = () => -1;
5449
+ for (let i = arms.length - 1; i >= 0; i--) {
5450
+ const arm = arms[i], next = matchTail;
5451
+ matchTail = (input, pos) => {
5452
+ const end = arm.match(input, pos);
5453
+ return end < 0 ? next(input, pos) : end;
5454
+ };
5455
+ }
5456
+ let tail = (_input, pos, ctx, _lead, acc) => {
5457
+ ctx._fe = pos;
5458
+ ctx._fx = acc ?? choiceExpected;
5459
+ return -1;
5460
+ };
5461
+ for (let i = arms.length - 1; i >= 0; i--) {
5462
+ const arm = arms[i], cls = classes[i], staticExpected = armExpected[i], next = tail;
5463
+ tail = (input, pos, ctx, lead2, acc, best) => {
5464
+ if (cls !== void 0 && !classHas(cls, lead2)) {
5465
+ return next(
5466
+ input,
5467
+ pos,
5468
+ ctx,
5469
+ lead2,
5470
+ best === pos ? appendExpected(acc, staticExpected) : acc,
5471
+ best
5472
+ );
5751
5473
  }
5752
- if (resolved === void 0) {
5753
- if (p._ruleName) externalRefs.add(p);
5754
- return;
5474
+ ctx._fc = false;
5475
+ const end = arm.run(input, pos, ctx);
5476
+ if (end >= 0) return end;
5477
+ const at = ctx._fe ?? pos;
5478
+ if (at > best) {
5479
+ best = at;
5480
+ acc = void 0;
5755
5481
  }
5756
- scanExternal(resolved);
5757
- return;
5482
+ const nextAcc = at === best ? appendExpected(acc, ctx._fx) : acc;
5483
+ if (committed(ctx)) {
5484
+ if (nextAcc !== void 0) ctx._fx = nextAcc;
5485
+ return -1;
5486
+ }
5487
+ return next(input, pos, ctx, lead2, nextAcc, best);
5488
+ };
5489
+ }
5490
+ return {
5491
+ match: matchTail,
5492
+ run: (input, pos, ctx) => tail(
5493
+ input,
5494
+ pos,
5495
+ ctx,
5496
+ pos < input.length ? input.codePointAt(pos) : -1,
5497
+ void 0,
5498
+ pos
5499
+ )
5500
+ };
5501
+ }
5502
+ function notNode(body, failure) {
5503
+ return {
5504
+ match: (input, pos) => body.match(input, pos) < 0 ? pos : -1,
5505
+ run: (input, pos, ctx) => {
5506
+ if (body.match(input, pos) < 0) return pos;
5507
+ ctx._fe = pos;
5508
+ ctx._fx = failure;
5509
+ return -1;
5758
5510
  }
5759
- for (const child of childrenOf2(def)) scanExternal(child);
5760
5511
  };
5761
- for (const [, rule] of ruleMap) scanExternal(rule);
5512
+ }
5513
+ function optionalNode(body) {
5762
5514
  return {
5763
- hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
5764
- isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs)
5765
- };
5766
- }
5767
-
5768
- // src/table/ops.ts
5769
- var OP_LIT = 1;
5770
- var OP_RX = 2;
5771
- var OP_SEQ = 3;
5772
- var OP_SEQV = 4;
5773
- var OP_CHOICE = 5;
5774
- var OP_REP = 6;
5775
- var OP_REPV = 7;
5776
- var OP_OPT = 8;
5777
- var OP_XFORM = 9;
5778
- var OP_NODE = 10;
5779
- var OP_RULE = 11;
5780
- var OP_GATE = 12;
5781
- var OP_NOT = 13;
5782
- var OP_PEEK = 14;
5783
- var OP_LEAF = 15;
5784
- var OP_EMPTY = 16;
5785
- var OP_LIT_TRACK = 17;
5786
- var OP_RX_TRACK = 18;
5787
- var OP_NODE_TRACK = 19;
5788
- var OP_SCOPE = 20;
5789
- var OP_EXPECT = 21;
5790
- var OP_SEQX = 22;
5791
- var OP_SCAN = 23;
5792
- var OP_FIELD = 24;
5793
- var OP_LIT_CI = 27;
5794
- var OP_LIT_CI_TRACK = 28;
5795
- var OP_TOKEN = 29;
5796
- var OP_SCOPE_CAP = 30;
5797
- var OP_WITHCTX = 31;
5798
- var OP_GUARD = 32;
5799
- var OP_ADJ = 33;
5800
- var OP_GREEDY = 34;
5801
- var OP_REJECT = 35;
5802
- var OP_ARMGATE = 36;
5803
- var OP_LIVE = 37;
5804
- var OP_ATTEMPT = 38;
5805
- var OP_LABEL = 39;
5806
- var OP_COV = 40;
5807
- var OP_SCOPE_PLAIN = 41;
5808
- var OP_LEX_BODY = 42;
5809
- var OP_LEX_PROGRAM = 43;
5810
- var OP_SOURCE_LEAF = 44;
5811
- var OP_DISPATCH = 25;
5812
- var OP_ROUTED = 26;
5813
- var OP_NAMES = {
5814
- [OP_LIT]: "LIT",
5815
- [OP_RX]: "RX",
5816
- [OP_SEQ]: "SEQ",
5817
- [OP_SEQV]: "SEQV",
5818
- [OP_CHOICE]: "CHOICE",
5819
- [OP_REP]: "REP",
5820
- [OP_REPV]: "REPV",
5821
- [OP_OPT]: "OPT",
5822
- [OP_XFORM]: "XFORM",
5823
- [OP_NODE]: "NODE",
5824
- [OP_RULE]: "RULE",
5825
- [OP_GATE]: "GATE",
5826
- [OP_NOT]: "NOT",
5827
- [OP_PEEK]: "PEEK",
5828
- [OP_LEAF]: "LEAF",
5829
- [OP_SOURCE_LEAF]: "SOURCE_LEAF",
5830
- [OP_EMPTY]: "EMPTY",
5831
- [OP_LIT_TRACK]: "LIT_TRACK",
5832
- [OP_RX_TRACK]: "RX_TRACK",
5833
- [OP_NODE_TRACK]: "NODE_TRACK",
5834
- [OP_SCOPE]: "SCOPE",
5835
- [OP_EXPECT]: "EXPECT",
5836
- [OP_SEQX]: "SEQX",
5837
- [OP_SCAN]: "SCAN",
5838
- [OP_FIELD]: "FIELD",
5839
- [OP_LIT_CI]: "LIT_CI",
5840
- [OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
5841
- [OP_DISPATCH]: "DISPATCH",
5842
- [OP_ROUTED]: "ROUTED",
5843
- [OP_TOKEN]: "TOKEN",
5844
- [OP_SCOPE_CAP]: "SCOPE_CAP",
5845
- [OP_WITHCTX]: "WITHCTX",
5846
- [OP_GUARD]: "GUARD",
5847
- [OP_ADJ]: "ADJ",
5848
- [OP_GREEDY]: "GREEDY",
5849
- [OP_REJECT]: "REJECT",
5850
- [OP_ARMGATE]: "ARMGATE",
5851
- [OP_LIVE]: "LIVE",
5852
- [OP_ATTEMPT]: "ATTEMPT",
5853
- [OP_LABEL]: "LABEL",
5854
- [OP_COV]: "COV",
5855
- [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
5856
- [OP_LEX_BODY]: "LEX_BODY",
5857
- [OP_LEX_PROGRAM]: "LEX_PROGRAM"
5858
- };
5859
-
5860
- // src/table/lex-program.ts
5861
- var LEX_NODE_TERMINAL = 0;
5862
- var LEX_NODE_SEQUENCE2 = 1;
5863
- var LEX_NODE_SEQUENCE3 = 2;
5864
- var LEX_NODE_SEQUENCE4 = 3;
5865
- var LEX_NODE_SEQUENCE5 = 4;
5866
- var LEX_NODE_CHOICE2 = 5;
5867
- var LEX_NODE_CHOICE4 = 6;
5868
- var LEX_NODE_CHOICE8 = 7;
5869
- var LEX_NODE_NOT = 8;
5870
- var LEX_NODE_OPTIONAL = 9;
5871
- function lexProgramDigest(words) {
5872
- let hash = 2166136261;
5873
- for (const word2 of words) {
5874
- hash ^= word2 | 0;
5875
- hash = Math.imul(hash, 16777619);
5876
- }
5877
- return hash | 0;
5878
- }
5879
- function classHas(cls, code) {
5880
- if (code < 0) return false;
5881
- if (code < 128) return cls.ascii[code] === 1;
5882
- for (let i = 0; i < cls.hi.length; i += 2) {
5883
- if (code >= cls.hi[i] && code <= cls.hi[i + 1]) return true;
5884
- }
5885
- return false;
5886
- }
5887
- var committed = (ctx) => ctx._fc === true;
5888
- function trackLexLines(ctx, input, end) {
5889
- let at = ctx._lineScannedTo ?? 0;
5890
- const starts = ctx._lineStarts;
5891
- if (starts === void 0 || end <= at) return;
5892
- while (at < end) {
5893
- if (input.charCodeAt(at) === 10) starts.push(at + 1);
5894
- at++;
5895
- }
5896
- ctx._lineScannedTo = end;
5897
- }
5898
- function terminalNode(matcher, failure, tracksLines) {
5899
- const match = (input, pos) => {
5900
- const result = matcher(input, pos);
5901
- return result < 0 ? -1 : result / 2;
5902
- };
5903
- return {
5904
- match,
5905
- run: (input, pos, ctx) => {
5906
- const end = match(input, pos);
5907
- if (end < 0) {
5908
- ctx._fe = pos;
5909
- ctx._fx = failure;
5910
- if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
5911
- return -1;
5912
- }
5913
- if (tracksLines) trackLexLines(ctx, input, end);
5914
- return end;
5915
- }
5916
- };
5917
- }
5918
- function sequence2(a, b) {
5919
- return {
5920
- match: (input, pos) => {
5921
- const e0 = a.match(input, pos);
5922
- return e0 < 0 ? -1 : b.match(input, e0);
5923
- },
5924
- run: (input, pos, ctx) => {
5925
- const e0 = a.run(input, pos, ctx);
5926
- if (e0 < 0) return -1;
5927
- ctx._fc = false;
5928
- return b.run(input, e0, ctx);
5929
- }
5930
- };
5931
- }
5932
- function sequence3(a, b, c) {
5933
- return {
5934
- match: (input, pos) => {
5935
- const e0 = a.match(input, pos);
5936
- if (e0 < 0) return -1;
5937
- const e1 = b.match(input, e0);
5938
- return e1 < 0 ? -1 : c.match(input, e1);
5939
- },
5940
- run: (input, pos, ctx) => {
5941
- const e0 = a.run(input, pos, ctx);
5942
- if (e0 < 0) return -1;
5943
- ctx._fc = false;
5944
- const e1 = b.run(input, e0, ctx);
5945
- if (e1 < 0) return -1;
5946
- ctx._fc = false;
5947
- return c.run(input, e1, ctx);
5948
- }
5949
- };
5950
- }
5951
- function sequence4(a, b, c, d) {
5952
- const abc = sequence3(a, b, c);
5953
- return {
5954
- match: (input, pos) => {
5955
- const end = abc.match(input, pos);
5956
- return end < 0 ? -1 : d.match(input, end);
5957
- },
5958
- run: (input, pos, ctx) => {
5959
- const end = abc.run(input, pos, ctx);
5960
- if (end < 0) return -1;
5961
- ctx._fc = false;
5962
- return d.run(input, end, ctx);
5963
- }
5964
- };
5965
- }
5966
- function sequence5(a, b, c, d, e) {
5967
- const abcd = sequence4(a, b, c, d);
5968
- return {
5969
- match: (input, pos) => {
5970
- const end = abcd.match(input, pos);
5971
- return end < 0 ? -1 : e.match(input, end);
5972
- },
5973
- run: (input, pos, ctx) => {
5974
- const end = abcd.run(input, pos, ctx);
5975
- if (end < 0) return -1;
5976
- ctx._fc = false;
5977
- return e.run(input, end, ctx);
5978
- }
5979
- };
5980
- }
5981
- function appendExpected(acc, values) {
5982
- if (values === void 0 || values.length === 0) return acc;
5983
- if (acc === void 0) return values.slice();
5984
- acc.push(...values);
5985
- return acc;
5986
- }
5987
- function choiceNode(arms, classes, armExpected, choiceExpected) {
5988
- let matchTail = () => -1;
5989
- for (let i = arms.length - 1; i >= 0; i--) {
5990
- const arm = arms[i], next = matchTail;
5991
- matchTail = (input, pos) => {
5992
- const end = arm.match(input, pos);
5993
- return end < 0 ? next(input, pos) : end;
5994
- };
5995
- }
5996
- let tail = (_input, pos, ctx, _lead, acc) => {
5997
- ctx._fe = pos;
5998
- ctx._fx = acc ?? choiceExpected;
5999
- return -1;
6000
- };
6001
- for (let i = arms.length - 1; i >= 0; i--) {
6002
- const arm = arms[i], cls = classes[i], staticExpected = armExpected[i], next = tail;
6003
- tail = (input, pos, ctx, lead2, acc, best) => {
6004
- if (cls !== void 0 && !classHas(cls, lead2)) {
6005
- return next(
6006
- input,
6007
- pos,
6008
- ctx,
6009
- lead2,
6010
- best === pos ? appendExpected(acc, staticExpected) : acc,
6011
- best
6012
- );
6013
- }
6014
- ctx._fc = false;
6015
- const end = arm.run(input, pos, ctx);
6016
- if (end >= 0) return end;
6017
- const at = ctx._fe ?? pos;
6018
- if (at > best) {
6019
- best = at;
6020
- acc = void 0;
6021
- }
6022
- const nextAcc = at === best ? appendExpected(acc, ctx._fx) : acc;
6023
- if (committed(ctx)) {
6024
- if (nextAcc !== void 0) ctx._fx = nextAcc;
6025
- return -1;
6026
- }
6027
- return next(input, pos, ctx, lead2, nextAcc, best);
6028
- };
6029
- }
6030
- return {
6031
- match: matchTail,
6032
- run: (input, pos, ctx) => tail(
6033
- input,
6034
- pos,
6035
- ctx,
6036
- pos < input.length ? input.codePointAt(pos) : -1,
6037
- void 0,
6038
- pos
6039
- )
6040
- };
6041
- }
6042
- function notNode(body, failure) {
6043
- return {
6044
- match: (input, pos) => body.match(input, pos) < 0 ? pos : -1,
6045
- run: (input, pos, ctx) => {
6046
- if (body.match(input, pos) < 0) return pos;
6047
- ctx._fe = pos;
6048
- ctx._fx = failure;
6049
- return -1;
6050
- }
6051
- };
6052
- }
6053
- function optionalNode(body) {
6054
- return {
6055
- match: (input, pos) => {
6056
- const end = body.match(input, pos);
6057
- return end < 0 ? pos : end;
6058
- },
6059
- run: (input, pos, ctx) => {
6060
- ctx._fc = false;
6061
- const end = body.run(input, pos, ctx);
6062
- return end < 0 && !committed(ctx) ? pos : end;
6063
- }
5515
+ match: (input, pos) => {
5516
+ const end = body.match(input, pos);
5517
+ return end < 0 ? pos : end;
5518
+ },
5519
+ run: (input, pos, ctx) => {
5520
+ ctx._fc = false;
5521
+ const end = body.run(input, pos, ctx);
5522
+ return end < 0 && !committed(ctx) ? pos : end;
5523
+ }
6064
5524
  };
6065
5525
  }
6066
5526
  function fixedTreeRunner(words, matchers, classes, expected) {
@@ -6235,26 +5695,6 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
6235
5695
  };
6236
5696
  }
6237
5697
 
6238
- // src/table/lex-body.ts
6239
- function buildLexBodyRecognizer(spec, constants) {
6240
- const raw = constants[spec[0]];
6241
- if (!(raw instanceof RegExp)) {
6242
- throw new TypeError("table lexical body does not reference a RegExp constant");
6243
- }
6244
- if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
6245
- throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
6246
- }
6247
- const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
6248
- const suffix = spec[1];
6249
- return (input, pos) => {
6250
- re.lastIndex = pos;
6251
- if (!re.test(input)) return -1;
6252
- const end = re.lastIndex;
6253
- if (suffix < 0) return end * 2;
6254
- return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
6255
- };
6256
- }
6257
-
6258
5698
  // src/table/program.ts
6259
5699
  function closureArtifact(prog) {
6260
5700
  return { ...prog, asm: [] };
@@ -6286,6 +5726,12 @@ function covKindName(code) {
6286
5726
  function covDefinitions(prog) {
6287
5727
  return (prog.cov ?? []).map(([id, kind]) => ({ id, kind: covKindName(kind) }));
6288
5728
  }
5729
+ function scanRefs(s) {
5730
+ const refs = [];
5731
+ for (const r of [s.sentinel, s.recoverAt, s.stopAt, s.prefix, s.body, s.first, s.lazy]) if (r !== void 0) refs.push(r);
5732
+ refs.push(...s.skip);
5733
+ return refs;
5734
+ }
6289
5735
  function expandCompact(p) {
6290
5736
  if ("code" in p) return p;
6291
5737
  return ownTableProgram({
@@ -6351,144 +5797,754 @@ function validateClassRange(lo, hi) {
6351
5797
  throw new TypeError(`table char-class endpoint is outside Unicode: ${cp}`);
6352
5798
  }
6353
5799
  }
6354
- if (lo > hi) {
6355
- throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
6356
- }
6357
- }
6358
- function encodeClassSpec(ranges) {
6359
- let wide = false;
6360
- for (const range2 of ranges) {
6361
- validateClassRange(range2.lo, range2.hi);
6362
- if (range2.hi > 65535) wide = true;
6363
- }
6364
- if (!wide) {
6365
- let spec2 = "";
6366
- for (const range2 of ranges) spec2 += String.fromCharCode(range2.lo, range2.hi);
6367
- return spec2;
6368
- }
6369
- let spec = String.fromCharCode(WIDE_CLASS_PREFIX_LO, WIDE_CLASS_PREFIX_HI);
6370
- for (const range2 of ranges) {
6371
- spec += String.fromCharCode(
6372
- range2.lo >>> 16,
6373
- range2.lo & 65535,
6374
- range2.hi >>> 16,
6375
- range2.hi & 65535
6376
- );
6377
- }
6378
- return spec;
5800
+ if (lo > hi) {
5801
+ throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
5802
+ }
5803
+ }
5804
+ function encodeClassSpec(ranges) {
5805
+ let wide = false;
5806
+ for (const range2 of ranges) {
5807
+ validateClassRange(range2.lo, range2.hi);
5808
+ if (range2.hi > 65535) wide = true;
5809
+ }
5810
+ if (!wide) {
5811
+ let spec2 = "";
5812
+ for (const range2 of ranges) spec2 += String.fromCharCode(range2.lo, range2.hi);
5813
+ return spec2;
5814
+ }
5815
+ let spec = String.fromCharCode(WIDE_CLASS_PREFIX_LO, WIDE_CLASS_PREFIX_HI);
5816
+ for (const range2 of ranges) {
5817
+ spec += String.fromCharCode(
5818
+ range2.lo >>> 16,
5819
+ range2.lo & 65535,
5820
+ range2.hi >>> 16,
5821
+ range2.hi & 65535
5822
+ );
5823
+ }
5824
+ return spec;
5825
+ }
5826
+ function decodeClassSpec(spec) {
5827
+ if (spec.charCodeAt(0) === WIDE_CLASS_PREFIX_LO && spec.charCodeAt(1) === WIDE_CLASS_PREFIX_HI) {
5828
+ if ((spec.length - 2) % 4 !== 0) throw new TypeError("table char-class spec has a truncated wide range");
5829
+ if (spec.length === 2) throw new TypeError("table char-class spec has a wide prefix but no ranges");
5830
+ const ranges2 = [];
5831
+ for (let i = 2; i < spec.length; i += 4) {
5832
+ const lo = spec.charCodeAt(i) * 65536 + spec.charCodeAt(i + 1);
5833
+ const hi = spec.charCodeAt(i + 2) * 65536 + spec.charCodeAt(i + 3);
5834
+ validateClassRange(lo, hi);
5835
+ ranges2.push({ lo, hi });
5836
+ }
5837
+ return ranges2;
5838
+ }
5839
+ if (spec.length % 2 !== 0) throw new TypeError("table char-class spec has an unmatched range endpoint");
5840
+ const ranges = [];
5841
+ for (let i = 0; i < spec.length; i += 2) {
5842
+ const lo = spec.charCodeAt(i);
5843
+ const hi = spec.charCodeAt(i + 1);
5844
+ validateClassRange(lo, hi);
5845
+ ranges.push({ lo, hi });
5846
+ }
5847
+ return ranges;
5848
+ }
5849
+ function resolveClass(spec) {
5850
+ const ascii = new Uint8Array(128);
5851
+ const hi = [];
5852
+ for (const { lo, hi: up } of decodeClassSpec(spec)) {
5853
+ if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1;
5854
+ if (up >= 128) hi.push(Math.max(lo, 128), up);
5855
+ }
5856
+ return { ascii, hi };
5857
+ }
5858
+ function classHas2(cls, code) {
5859
+ if (code < 0) return false;
5860
+ if (code < 128) return cls.ascii[code] === 1;
5861
+ const hi = cls.hi;
5862
+ for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
5863
+ return false;
5864
+ }
5865
+ function resolveDispatch(arms, cc) {
5866
+ const ascii = new Uint8Array(128);
5867
+ const hi = [];
5868
+ const open = [];
5869
+ const armCls = [];
5870
+ let exclusive = true;
5871
+ let hiOwners = 0;
5872
+ for (let a = 0; a < arms.length; a++) {
5873
+ const ci = arms[a];
5874
+ if (ci < 0) {
5875
+ open.push(a);
5876
+ armCls.push(null);
5877
+ exclusive = false;
5878
+ continue;
5879
+ }
5880
+ const cls = cc[ci];
5881
+ armCls.push(cls);
5882
+ for (let c = 0; c < 128; c++) {
5883
+ if (cls.ascii[c] !== 1) continue;
5884
+ if (ascii[c] === 0) ascii[c] = a + 1;
5885
+ else exclusive = false;
5886
+ }
5887
+ if (cls.hi.length > 0 && ++hiOwners > 1) exclusive = false;
5888
+ for (let i = 0; i < cls.hi.length; i += 2) hi.push(cls.hi[i], cls.hi[i + 1], a);
5889
+ }
5890
+ return { ascii, hi, open, armCls, exclusive };
5891
+ }
5892
+ function buildTrivia(spec) {
5893
+ if (spec.live !== void 0) return spec.live;
5894
+ if (spec.plain !== void 0) return trivia(regex(new RegExp(spec.plain[0], spec.plain[1])));
5895
+ if (spec.alts !== void 0) {
5896
+ const [head, ...rest] = spec.alts.map(([source, flags]) => regex(new RegExp(source, flags)));
5897
+ if (head === void 0) throw new TypeError("trivia spec has an EMPTY alternation, which matches nothing");
5898
+ const body = rest.length === 0 ? head : choice(head, ...rest);
5899
+ return trivia(many(body, { min: spec.min ?? 1 }));
5900
+ }
5901
+ const arms = {};
5902
+ for (const [name, source, flags] of spec.arms) arms[name] = regex(new RegExp(source, flags));
5903
+ return classifiedTrivia(arms);
5904
+ }
5905
+ function resolveTable(prog) {
5906
+ const owner = prog[TABLE_RUNTIME];
5907
+ const hit = owner?.resolved;
5908
+ if (hit !== void 0) return hit;
5909
+ const cc = prog.cc.map(resolveClass);
5910
+ const triviaBuilt = (prog.triviaSpecs ?? []).map(buildTrivia);
5911
+ const lex = (prog.lex ?? []).map((spec) => buildLexBodyRecognizer(spec, prog.k));
5912
+ const built = {
5913
+ prog,
5914
+ code: Int32Array.from(prog.code),
5915
+ k: prog.k,
5916
+ fns: prog.fns,
5917
+ cc,
5918
+ fx: prog.fx,
5919
+ disp: prog.disp.map((d) => resolveDispatch(d, cc)),
5920
+ trivia: triviaBuilt,
5921
+ triviaScan: triviaBuilt.map(fastTriviaScanner),
5922
+ triviaLabelled: triviaBuilt.map((t) => t._meta.triviaKindLabels !== void 0),
5923
+ dsp: prog.dsp.map((d) => ({
5924
+ byKey: new Map(d.key.map((x, i) => [x, d.keyArm[i]])),
5925
+ byFold: new Map(d.fold.map((x, i) => [x, d.foldArm[i]])),
5926
+ match: d.match,
5927
+ routed: d.routed,
5928
+ expected: d.expected
5929
+ })),
5930
+ lex,
5931
+ lexPrograms: (prog.lexPrograms ?? []).map((spec) => buildLexProgramRunner(spec, lex, cc, prog.fx)),
5932
+ rules: prog.rules
5933
+ };
5934
+ if (owner !== void 0) owner.resolved = built;
5935
+ return built;
5936
+ }
5937
+
5938
+ // src/analysis/gating.ts
5939
+ function childrenOf2(d) {
5940
+ switch (d.tag) {
5941
+ case "sequence":
5942
+ case "choice":
5943
+ return d.parsers;
5944
+ case "dispatch":
5945
+ return [
5946
+ d.selector,
5947
+ ...d.cases.map((c) => c.parser),
5948
+ ...d.matchers ? d.matchers.map((c) => c.parser) : [],
5949
+ ...d.otherwise === void 0 ? [] : [d.otherwise]
5950
+ ];
5951
+ case "sepBy":
5952
+ return [d.parser, d.separator];
5953
+ case "recover":
5954
+ return [d.parser, d.sentinel];
5955
+ case "scanTo":
5956
+ return [
5957
+ d.sentinel,
5958
+ ...d.skip,
5959
+ ...d.recoverAt === void 0 ? [] : [d.recoverAt],
5960
+ ...d.stopAt === void 0 ? [] : [d.stopAt]
5961
+ ];
5962
+ case "grammar":
5963
+ return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
5964
+ case "routed":
5965
+ return d.fallback ? [d.fallback] : [];
5966
+ case "lines":
5967
+ return [d.prefix, d.body, ...d.first === void 0 ? [] : [d.first], ...d.lazy === void 0 ? [] : [d.lazy]];
5968
+ case "columns":
5969
+ return [];
5970
+ // A `lazy` is a REFERENCE, not a subtree. Descending through it would make
5971
+ // every rule's walk cover the whole reachable grammar — site paths become
5972
+ // nonsense, and a structural hash includes half the grammar. Treated as a
5973
+ // leaf, keyed by the rule name it refers to, which is also the right
5974
+ // semantics: two productions referencing `g.Ident` really do fill that slot
5975
+ // the same way.
5976
+ case "lazy":
5977
+ case "literal":
5978
+ case "regex":
5979
+ case "keywords":
5980
+ case "guard":
5981
+ case "adjacency":
5982
+ case "unknown":
5983
+ return [];
5984
+ default: {
5985
+ const rec = d;
5986
+ return rec.parser ? [rec.parser] : [];
5987
+ }
5988
+ }
5989
+ }
5990
+ var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
5991
+ function firstSetToString(fs3) {
5992
+ if (fs3.kind === "any") return "ANY";
5993
+ if (fs3.kind === "empty") return "(empty)";
5994
+ return fs3.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
5995
+ }
5996
+
5997
+ // src/analysis/commitment.ts
5998
+ function mayFail(p, seen = /* @__PURE__ */ new Set()) {
5999
+ if (seen.has(p)) return true;
6000
+ seen.add(p);
6001
+ const d = p._def;
6002
+ switch (d.tag) {
6003
+ case "literal":
6004
+ return d.value.length > 0;
6005
+ case "trivia":
6006
+ return false;
6007
+ // `optional`/`many`/a min-0 `sepBy` swallow an ORDINARY failure — zero
6008
+ // repetitions is a match. They do NOT swallow a COMMITTED one: a dispatch
6009
+ // that has selected an arm and then failed inside it breaks out past every
6010
+ // enclosing repeat, straight to the fallible boundary. So these are total
6011
+ // exactly when their body cannot commit. (Measured: treating them as
6012
+ // unconditionally total left an extra zero-width leaf in
6013
+ // `BareInterpolatedSelector` on benchmark.less — a rollback the legacy
6014
+ // build fired and this one had deleted.)
6015
+ case "optional":
6016
+ case "many":
6017
+ return mayCommitFailure(d.parser);
6018
+ case "sepBy":
6019
+ return d.min >= 1 ? mayFail(d.parser, seen) : mayCommitFailure(d.parser) || mayCommitFailure(d.separator);
6020
+ case "oneOrMore":
6021
+ return mayFail(d.parser, seen);
6022
+ case "sequence":
6023
+ return d.parsers.some((x) => mayFail(x, seen));
6024
+ // Delegating wrappers: as fallible as what they wrap.
6025
+ case "node":
6026
+ case "transform":
6027
+ case "label":
6028
+ case "field":
6029
+ case "expect":
6030
+ case "withCtx":
6031
+ case "grammar":
6032
+ case "token":
6033
+ case "leaf":
6034
+ return mayFail(d.parser, seen);
6035
+ case "lazy": {
6036
+ try {
6037
+ return mayFail(d.thunk(), seen);
6038
+ } catch {
6039
+ return true;
6040
+ }
6041
+ }
6042
+ // An adjacency assertion is a TEST — failing is its whole job.
6043
+ case "adjacency":
6044
+ return true;
6045
+ default:
6046
+ return true;
6047
+ }
6048
+ }
6049
+ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set(), triviaActive = true) {
6050
+ if (seen.has(p)) return false;
6051
+ seen.add(p);
6052
+ const d = p._def;
6053
+ switch (d.tag) {
6054
+ // Atomic / non-capturing-then-failing: a failure happens before any push.
6055
+ case "literal":
6056
+ case "regex":
6057
+ case "keywords":
6058
+ case "guard":
6059
+ // Adjacency probes with `_cap = 0` / no `commit()`: it cannot push anything.
6060
+ case "adjacency":
6061
+ case "not":
6062
+ // peek(): emitted under a non-capturing probe ctx and zero-width on both
6063
+ // outcomes, so it can never leave a partial capture behind.
6064
+ case "peek":
6065
+ case "trivia":
6066
+ case "token":
6067
+ case "leaf":
6068
+ case "scanTo":
6069
+ case "unknown":
6070
+ case "columns":
6071
+ return false;
6072
+ // A run's prefixes are probed detached; only its body can capture.
6073
+ case "lines":
6074
+ return mayLeavePartialCapture(d.body, seen, triviaActive);
6075
+ // node() captures into its own private buffers and rolls them back on
6076
+ // failure (emitNode restores _ctx.* and never pushes on the !ok path).
6077
+ case "node":
6078
+ return false;
6079
+ // choice/firstMatch already roll back each failed arm; on overall failure
6080
+ // nothing committed remains.
6081
+ case "choice":
6082
+ return false;
6083
+ case "dispatch":
6084
+ return capturesLeaf(d.selector, new Set(seen)) || d.cases.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) || (d.matchers ? d.matchers.some((x) => mayLeavePartialCapture(x.parser, new Set(seen), triviaActive) || capturesLeaf(x.parser, new Set(seen))) : false) || (d.otherwise ? mayLeavePartialCapture(d.otherwise, new Set(seen), triviaActive) || capturesLeaf(d.otherwise, new Set(seen)) : false);
6085
+ case "attempt":
6086
+ return false;
6087
+ // optional never fails; many/oneOrMore only "fail" with zero captured items.
6088
+ case "optional":
6089
+ case "many":
6090
+ case "oneOrMore":
6091
+ return false;
6092
+ // sepBy emits its own per-iteration rollback in emitSepBy.
6093
+ case "sepBy":
6094
+ return false;
6095
+ // A sequence is the real case: an earlier capturing term followed by a term
6096
+ // that can fail leaves the earlier captures buffered.
6097
+ //
6098
+ // BOTH halves of that sentence are load-bearing, and the second half is what
6099
+ // this walk exists to exploit. "Some earlier term captures" alone was the old
6100
+ // test, and it is true of very nearly every sequence in a real grammar — a
6101
+ // sequence almost always opens with a literal or a node. The rollback is only
6102
+ // reachable if something AFTER the capture can still fail; once the remainder
6103
+ // is total (`optional`/`many`/trivia/a min-0 `sepBy`), the sequence has no
6104
+ // failure path left to roll back from, and the save+restore is dead code.
6105
+ //
6106
+ // `captured` tracks whether anything is in the buffers yet as the scan moves
6107
+ // left to right. Two things put it there:
6108
+ // - a term that captures (`capturesLeaf`/`hasNodeDef`), and
6109
+ // - the inter-term TRIVIA scan, which under `capturing` pushes into
6110
+ // `_cstRawChildren`/`_cstTriviaLog` BEFORE the term at each boundary
6111
+ // i >= 1. `emitSeqValues` only rewinds that push on the term's SUCCESS
6112
+ // path (the "matched empty, so leave the whitespace out of the span"
6113
+ // branch); a term that FAILS breaks straight out with the trivia still
6114
+ // buffered, so from boundary 1 onward the buffers are dirty regardless of
6115
+ // what the earlier terms did.
6116
+ // Each term is also asked about itself: a nested sequence can leave its own
6117
+ // partial capture behind, and its terms are emitted inline into this same
6118
+ // block with no boundary of their own to roll them back.
6119
+ case "sequence": {
6120
+ const parts = d.parsers;
6121
+ let captured = false;
6122
+ for (let i = 0; i < parts.length; i++) {
6123
+ const part = parts[i];
6124
+ if (i > 0 && triviaActive) captured = true;
6125
+ if (captured && mayFail(part)) return true;
6126
+ if (mayLeavePartialCapture(part, seen, triviaActive)) return true;
6127
+ if (capturesLeaf(part) || hasNodeDef(part)) captured = true;
6128
+ }
6129
+ return false;
6130
+ }
6131
+ // Delegating wrappers: defer to the wrapped parser.
6132
+ case "transform":
6133
+ case "label":
6134
+ case "field":
6135
+ case "expect":
6136
+ case "withCtx":
6137
+ case "grammar":
6138
+ return mayLeavePartialCapture(d.parser, seen, triviaActive);
6139
+ case "recover":
6140
+ return mayLeavePartialCapture(d.parser, seen, triviaActive);
6141
+ case "lazy": {
6142
+ try {
6143
+ return mayLeavePartialCapture(d.thunk(), seen, triviaActive);
6144
+ } catch {
6145
+ return true;
6146
+ }
6147
+ }
6148
+ // Unknown shapes: be safe and keep the rollback.
6149
+ default:
6150
+ return true;
6151
+ }
6152
+ }
6153
+ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
6154
+ if (seen.has(p)) return false;
6155
+ seen.add(p);
6156
+ const d = p._def;
6157
+ switch (d.tag) {
6158
+ case "literal":
6159
+ case "regex":
6160
+ case "keywords":
6161
+ case "node":
6162
+ case "token":
6163
+ case "routed":
6164
+ case "leaf":
6165
+ return true;
6166
+ case "not":
6167
+ case "peek":
6168
+ case "guard":
6169
+ case "adjacency":
6170
+ case "trivia":
6171
+ case "unknown":
6172
+ return false;
6173
+ case "sequence":
6174
+ case "choice":
6175
+ return d.parsers.some((x) => capturesLeaf(x, seen));
6176
+ case "dispatch":
6177
+ return capturesLeaf(d.selector, seen) || d.cases.some((x) => capturesLeaf(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => capturesLeaf(entry.parser, seen)) : false) || (d.otherwise ? capturesLeaf(d.otherwise, seen) : false);
6178
+ case "sepBy":
6179
+ return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
6180
+ case "many":
6181
+ case "oneOrMore":
6182
+ case "optional":
6183
+ case "attempt":
6184
+ case "transform":
6185
+ case "label":
6186
+ case "field":
6187
+ case "expect":
6188
+ case "withCtx":
6189
+ case "grammar":
6190
+ case "recover":
6191
+ return capturesLeaf(d.parser, seen);
6192
+ case "scanTo":
6193
+ return true;
6194
+ case "lines":
6195
+ return capturesLeaf(d.body, seen);
6196
+ case "columns":
6197
+ return true;
6198
+ case "lazy": {
6199
+ try {
6200
+ return capturesLeaf(d.thunk(), seen);
6201
+ } catch {
6202
+ return true;
6203
+ }
6204
+ }
6205
+ default:
6206
+ return true;
6207
+ }
6379
6208
  }
6380
- function decodeClassSpec(spec) {
6381
- if (spec.charCodeAt(0) === WIDE_CLASS_PREFIX_LO && spec.charCodeAt(1) === WIDE_CLASS_PREFIX_HI) {
6382
- if ((spec.length - 2) % 4 !== 0) throw new TypeError("table char-class spec has a truncated wide range");
6383
- if (spec.length === 2) throw new TypeError("table char-class spec has a wide prefix but no ranges");
6384
- const ranges2 = [];
6385
- for (let i = 2; i < spec.length; i += 4) {
6386
- const lo = spec.charCodeAt(i) * 65536 + spec.charCodeAt(i + 1);
6387
- const hi = spec.charCodeAt(i + 2) * 65536 + spec.charCodeAt(i + 3);
6388
- validateClassRange(lo, hi);
6389
- ranges2.push({ lo, hi });
6209
+ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
6210
+ if (seen.has(p)) return false;
6211
+ seen.add(p);
6212
+ const d = p._def;
6213
+ switch (d.tag) {
6214
+ case "node":
6215
+ return true;
6216
+ case "lazy": {
6217
+ try {
6218
+ return hasNodeDef(d.thunk(), seen);
6219
+ } catch {
6220
+ return false;
6221
+ }
6390
6222
  }
6391
- return ranges2;
6392
- }
6393
- if (spec.length % 2 !== 0) throw new TypeError("table char-class spec has an unmatched range endpoint");
6394
- const ranges = [];
6395
- for (let i = 0; i < spec.length; i += 2) {
6396
- const lo = spec.charCodeAt(i);
6397
- const hi = spec.charCodeAt(i + 1);
6398
- validateClassRange(lo, hi);
6399
- ranges.push({ lo, hi });
6223
+ case "grammar":
6224
+ case "trivia":
6225
+ case "token":
6226
+ case "leaf":
6227
+ case "label":
6228
+ case "field":
6229
+ case "optional":
6230
+ case "many":
6231
+ case "oneOrMore":
6232
+ case "not":
6233
+ case "peek":
6234
+ case "transform":
6235
+ return hasNodeDef(d.parser, seen);
6236
+ case "sequence":
6237
+ case "choice":
6238
+ return d.parsers.some((x) => hasNodeDef(x, seen));
6239
+ case "dispatch":
6240
+ return hasNodeDef(d.selector, seen) || d.cases.some((x) => hasNodeDef(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => hasNodeDef(entry.parser, seen)) : false) || (d.otherwise ? hasNodeDef(d.otherwise, seen) : false);
6241
+ case "sepBy":
6242
+ return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
6243
+ case "scanTo":
6244
+ return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen)) || d.recoverAt !== void 0 && hasNodeDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasNodeDef(d.stopAt, seen);
6245
+ case "lines":
6246
+ return hasNodeDef(d.body, seen);
6247
+ case "recover":
6248
+ return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
6249
+ case "expect":
6250
+ return hasNodeDef(d.parser, seen);
6251
+ case "withCtx":
6252
+ return hasNodeDef(d.parser, seen);
6253
+ default:
6254
+ return false;
6400
6255
  }
6401
- return ranges;
6402
6256
  }
6403
- function resolveClass(spec) {
6404
- const ascii = new Uint8Array(128);
6405
- const hi = [];
6406
- for (const { lo, hi: up } of decodeClassSpec(spec)) {
6407
- if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1;
6408
- if (up >= 128) hi.push(Math.max(lo, 128), up);
6257
+ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
6258
+ if (seen.has(p)) return false;
6259
+ seen.add(p);
6260
+ const d = p._def;
6261
+ switch (d.tag) {
6262
+ case "node":
6263
+ return d.build !== void 0 || hasDirectBuildDef(d.parser, seen);
6264
+ case "lazy": {
6265
+ try {
6266
+ return hasDirectBuildDef(d.thunk(), seen);
6267
+ } catch {
6268
+ return false;
6269
+ }
6270
+ }
6271
+ case "grammar":
6272
+ case "trivia":
6273
+ case "token":
6274
+ case "leaf":
6275
+ case "label":
6276
+ case "field":
6277
+ case "optional":
6278
+ case "many":
6279
+ case "oneOrMore":
6280
+ case "attempt":
6281
+ case "not":
6282
+ case "peek":
6283
+ case "withCtx":
6284
+ case "expect":
6285
+ case "transform":
6286
+ return hasDirectBuildDef(d.parser, seen);
6287
+ case "sequence":
6288
+ case "choice":
6289
+ return d.parsers.some((x) => hasDirectBuildDef(x, seen));
6290
+ case "dispatch":
6291
+ return hasDirectBuildDef(d.selector, seen) || d.cases.some((x) => hasDirectBuildDef(x.parser, seen)) || (d.matchers ? d.matchers.some((x) => hasDirectBuildDef(x.parser, seen)) : false) || (d.otherwise ? hasDirectBuildDef(d.otherwise, seen) : false);
6292
+ case "sepBy":
6293
+ return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
6294
+ case "scanTo":
6295
+ return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen)) || d.recoverAt !== void 0 && hasDirectBuildDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasDirectBuildDef(d.stopAt, seen);
6296
+ case "lines":
6297
+ return hasDirectBuildDef(d.body, seen);
6298
+ case "recover":
6299
+ return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
6300
+ case "routed":
6301
+ return d.fallback ? hasDirectBuildDef(d.fallback, seen) : false;
6302
+ default:
6303
+ return false;
6409
6304
  }
6410
- return { ascii, hi };
6411
6305
  }
6412
- function classHas2(cls, code) {
6413
- if (code < 0) return false;
6414
- if (code < 128) return cls.ascii[code] === 1;
6415
- const hi = cls.hi;
6416
- for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
6417
- return false;
6306
+ function mayCommitFailure(p, resolve3, memo = /* @__PURE__ */ new Map()) {
6307
+ return reachesMarked(p, (node2) => commitFailureSuccessors(node2, resolve3), memo);
6418
6308
  }
6419
- function resolveDispatch(arms, cc) {
6420
- const ascii = new Uint8Array(128);
6421
- const hi = [];
6422
- const open = [];
6423
- const armCls = [];
6424
- let exclusive = true;
6425
- let hiOwners = 0;
6426
- for (let a = 0; a < arms.length; a++) {
6427
- const ci = arms[a];
6428
- if (ci < 0) {
6429
- open.push(a);
6430
- armCls.push(null);
6431
- exclusive = false;
6432
- continue;
6309
+ var NO_EDGES = [];
6310
+ function commitFailureSuccessors(p, resolve3) {
6311
+ const d = p._def;
6312
+ switch (d.tag) {
6313
+ case "dispatch":
6314
+ case "unknown":
6315
+ return true;
6316
+ case "choice":
6317
+ case "sequence":
6318
+ return d.parsers;
6319
+ // A contained transaction reports every failure uncommitted.
6320
+ case "attempt":
6321
+ return d.contain === true ? NO_EDGES : [d.parser];
6322
+ case "many":
6323
+ case "oneOrMore":
6324
+ case "optional":
6325
+ case "transform":
6326
+ case "label":
6327
+ case "field":
6328
+ case "grammar":
6329
+ case "node":
6330
+ case "token":
6331
+ case "leaf":
6332
+ case "withCtx":
6333
+ return [d.parser];
6334
+ case "sepBy":
6335
+ return [d.parser, d.separator];
6336
+ case "scanTo":
6337
+ return [
6338
+ d.sentinel,
6339
+ ...d.skip,
6340
+ ...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
6341
+ ...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
6342
+ ];
6343
+ // A run's prefixes are probed detached; a committed failure reaches it only from its body.
6344
+ case "lines":
6345
+ return [d.body];
6346
+ case "lazy": {
6347
+ try {
6348
+ return [d.thunk()];
6349
+ } catch {
6350
+ const name = p._ruleName;
6351
+ const target = name === void 0 ? void 0 : resolve3?.(name);
6352
+ return target === void 0 ? true : [target];
6353
+ }
6433
6354
  }
6434
- const cls = cc[ci];
6435
- armCls.push(cls);
6436
- for (let c = 0; c < 128; c++) {
6437
- if (cls.ascii[c] !== 1) continue;
6438
- if (ascii[c] === 0) ascii[c] = a + 1;
6439
- else exclusive = false;
6355
+ default:
6356
+ return NO_EDGES;
6357
+ }
6358
+ }
6359
+ function reachesMarked(root, successors, memo) {
6360
+ const known = memo.get(root);
6361
+ if (known !== void 0) return known;
6362
+ const order = /* @__PURE__ */ new Map();
6363
+ const low = [];
6364
+ const found = [];
6365
+ const stack = [];
6366
+ const visit = (node2) => {
6367
+ const at = low.length;
6368
+ order.set(node2, at);
6369
+ low.push(at);
6370
+ found.push(false);
6371
+ stack.push(node2);
6372
+ const next = successors(node2);
6373
+ if (next === true) found[at] = true;
6374
+ else {
6375
+ for (const child of next) {
6376
+ if (found[at]) break;
6377
+ const hit = memo.get(child);
6378
+ if (hit !== void 0) {
6379
+ if (hit) found[at] = true;
6380
+ continue;
6381
+ }
6382
+ const seen = order.get(child);
6383
+ if (seen !== void 0) {
6384
+ if (seen < low[at]) low[at] = seen;
6385
+ continue;
6386
+ }
6387
+ const k = visit(child);
6388
+ if (low[k] < low[at]) low[at] = low[k];
6389
+ if (found[k]) found[at] = true;
6390
+ }
6440
6391
  }
6441
- if (cls.hi.length > 0 && ++hiOwners > 1) exclusive = false;
6442
- for (let i = 0; i < cls.hi.length; i += 2) hi.push(cls.hi[i], cls.hi[i + 1], a);
6443
- }
6444
- return { ascii, hi, open, armCls, exclusive };
6392
+ if (low[at] === at) {
6393
+ const answer = found[at];
6394
+ let member;
6395
+ do {
6396
+ member = stack.pop();
6397
+ memo.set(member, answer);
6398
+ } while (member !== node2);
6399
+ }
6400
+ return at;
6401
+ };
6402
+ return found[visit(root)];
6445
6403
  }
6446
- function buildTrivia(spec) {
6447
- if (spec.live !== void 0) return spec.live;
6448
- if (spec.plain !== void 0) return trivia(regex(new RegExp(spec.plain[0], spec.plain[1])));
6449
- if (spec.alts !== void 0) {
6450
- const [head, ...rest] = spec.alts.map(([source, flags]) => regex(new RegExp(source, flags)));
6451
- if (head === void 0) throw new TypeError("trivia spec has an EMPTY alternation, which matches nothing");
6452
- const body = rest.length === 0 ? head : choice(head, ...rest);
6453
- return trivia(many(body, { min: spec.min ?? 1 }));
6454
- }
6455
- const arms = {};
6456
- for (const [name, source, flags] of spec.arms) arms[name] = regex(new RegExp(source, flags));
6457
- return classifiedTrivia(arms);
6404
+ function hasSemanticReduction(roots, externalRefs) {
6405
+ const seen = /* @__PURE__ */ new Set();
6406
+ const visit = (parser2) => {
6407
+ if (seen.has(parser2)) return false;
6408
+ seen.add(parser2);
6409
+ const def = parser2._def;
6410
+ if (def.tag === "transform" && !def.recognitionOnly) return true;
6411
+ if (def.tag === "choice" && def.gates.some(Boolean)) return true;
6412
+ if (def.tag === "guard" || def.tag === "withCtx") return true;
6413
+ if (def.tag === "node" && def.build !== void 0) return true;
6414
+ if (def.tag === "lazy") {
6415
+ if (externalRefs?.has(parser2)) return false;
6416
+ try {
6417
+ return visit(def.thunk());
6418
+ } catch {
6419
+ return true;
6420
+ }
6421
+ }
6422
+ return childrenOf2(def).some(visit);
6423
+ };
6424
+ return roots.some(visit);
6458
6425
  }
6459
- function resolveTable(prog) {
6460
- const owner = prog[TABLE_RUNTIME];
6461
- const hit = owner?.resolved;
6462
- if (hit !== void 0) return hit;
6463
- const cc = prog.cc.map(resolveClass);
6464
- const triviaBuilt = (prog.triviaSpecs ?? []).map(buildTrivia);
6465
- const lex = (prog.lex ?? []).map((spec) => buildLexBodyRecognizer(spec, prog.k));
6466
- const built = {
6467
- prog,
6468
- code: Int32Array.from(prog.code),
6469
- k: prog.k,
6470
- fns: prog.fns,
6471
- cc,
6472
- fx: prog.fx,
6473
- disp: prog.disp.map((d) => resolveDispatch(d, cc)),
6474
- trivia: triviaBuilt,
6475
- triviaScan: triviaBuilt.map(fastTriviaScanner),
6476
- triviaLabelled: triviaBuilt.map((t) => t._meta.triviaKindLabels !== void 0),
6477
- dsp: prog.dsp.map((d) => ({
6478
- byKey: new Map(d.key.map((x, i) => [x, d.keyArm[i]])),
6479
- byFold: new Map(d.fold.map((x, i) => [x, d.foldArm[i]])),
6480
- match: d.match,
6481
- routed: d.routed,
6482
- expected: d.expected
6483
- })),
6484
- lex,
6485
- lexPrograms: (prog.lexPrograms ?? []).map((spec) => buildLexProgramRunner(spec, lex, cc, prog.fx)),
6486
- rules: prog.rules
6426
+ function classifyRuleMap(ruleMap) {
6427
+ const externalRefs = /* @__PURE__ */ new Set();
6428
+ const scanned = /* @__PURE__ */ new Set();
6429
+ const scanExternal = (p) => {
6430
+ if (scanned.has(p)) return;
6431
+ scanned.add(p);
6432
+ const def = p._def;
6433
+ if (def.tag === "lazy") {
6434
+ let resolved;
6435
+ try {
6436
+ resolved = def.thunk();
6437
+ } catch {
6438
+ resolved = void 0;
6439
+ }
6440
+ if (resolved === void 0) {
6441
+ if (p._ruleName) externalRefs.add(p);
6442
+ return;
6443
+ }
6444
+ scanExternal(resolved);
6445
+ return;
6446
+ }
6447
+ for (const child of childrenOf2(def)) scanExternal(child);
6448
+ };
6449
+ for (const [, rule] of ruleMap) scanExternal(rule);
6450
+ return {
6451
+ hasDirectBuilders: ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
6452
+ isRecognitionOnly: !hasSemanticReduction(ruleMap.map(([, rule]) => rule), externalRefs)
6487
6453
  };
6488
- if (owner !== void 0) owner.resolved = built;
6489
- return built;
6490
6454
  }
6491
6455
 
6456
+ // src/table/ops.ts
6457
+ var OP_LIT = 1;
6458
+ var OP_RX = 2;
6459
+ var OP_SEQ = 3;
6460
+ var OP_SEQV = 4;
6461
+ var OP_CHOICE = 5;
6462
+ var OP_REP = 6;
6463
+ var OP_REPV = 7;
6464
+ var OP_OPT = 8;
6465
+ var OP_XFORM = 9;
6466
+ var OP_NODE = 10;
6467
+ var OP_RULE = 11;
6468
+ var OP_GATE = 12;
6469
+ var OP_NOT = 13;
6470
+ var OP_PEEK = 14;
6471
+ var OP_LEAF = 15;
6472
+ var OP_EMPTY = 16;
6473
+ var OP_LIT_TRACK = 17;
6474
+ var OP_RX_TRACK = 18;
6475
+ var OP_NODE_TRACK = 19;
6476
+ var OP_SCOPE = 20;
6477
+ var OP_EXPECT = 21;
6478
+ var OP_SEQX = 22;
6479
+ var OP_SCAN = 23;
6480
+ var OP_FIELD = 24;
6481
+ var OP_LIT_CI = 27;
6482
+ var OP_LIT_CI_TRACK = 28;
6483
+ var OP_TOKEN = 29;
6484
+ var OP_SCOPE_CAP = 30;
6485
+ var OP_WITHCTX = 31;
6486
+ var OP_GUARD = 32;
6487
+ var OP_ADJ = 33;
6488
+ var OP_GREEDY = 34;
6489
+ var OP_REJECT = 35;
6490
+ var OP_ARMGATE = 36;
6491
+ var OP_LIVE = 37;
6492
+ var OP_ATTEMPT = 38;
6493
+ var OP_LABEL = 39;
6494
+ var OP_COV = 40;
6495
+ var OP_SCOPE_PLAIN = 41;
6496
+ var OP_LEX_BODY = 42;
6497
+ var OP_LEX_PROGRAM = 43;
6498
+ var OP_SOURCE_LEAF = 44;
6499
+ var OP_DISPATCH = 25;
6500
+ var OP_ROUTED = 26;
6501
+ var OP_NAMES = {
6502
+ [OP_LIT]: "LIT",
6503
+ [OP_RX]: "RX",
6504
+ [OP_SEQ]: "SEQ",
6505
+ [OP_SEQV]: "SEQV",
6506
+ [OP_CHOICE]: "CHOICE",
6507
+ [OP_REP]: "REP",
6508
+ [OP_REPV]: "REPV",
6509
+ [OP_OPT]: "OPT",
6510
+ [OP_XFORM]: "XFORM",
6511
+ [OP_NODE]: "NODE",
6512
+ [OP_RULE]: "RULE",
6513
+ [OP_GATE]: "GATE",
6514
+ [OP_NOT]: "NOT",
6515
+ [OP_PEEK]: "PEEK",
6516
+ [OP_LEAF]: "LEAF",
6517
+ [OP_SOURCE_LEAF]: "SOURCE_LEAF",
6518
+ [OP_EMPTY]: "EMPTY",
6519
+ [OP_LIT_TRACK]: "LIT_TRACK",
6520
+ [OP_RX_TRACK]: "RX_TRACK",
6521
+ [OP_NODE_TRACK]: "NODE_TRACK",
6522
+ [OP_SCOPE]: "SCOPE",
6523
+ [OP_EXPECT]: "EXPECT",
6524
+ [OP_SEQX]: "SEQX",
6525
+ [OP_SCAN]: "SCAN",
6526
+ [OP_FIELD]: "FIELD",
6527
+ [OP_LIT_CI]: "LIT_CI",
6528
+ [OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
6529
+ [OP_DISPATCH]: "DISPATCH",
6530
+ [OP_ROUTED]: "ROUTED",
6531
+ [OP_TOKEN]: "TOKEN",
6532
+ [OP_SCOPE_CAP]: "SCOPE_CAP",
6533
+ [OP_WITHCTX]: "WITHCTX",
6534
+ [OP_GUARD]: "GUARD",
6535
+ [OP_ADJ]: "ADJ",
6536
+ [OP_GREEDY]: "GREEDY",
6537
+ [OP_REJECT]: "REJECT",
6538
+ [OP_ARMGATE]: "ARMGATE",
6539
+ [OP_LIVE]: "LIVE",
6540
+ [OP_ATTEMPT]: "ATTEMPT",
6541
+ [OP_LABEL]: "LABEL",
6542
+ [OP_COV]: "COV",
6543
+ [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
6544
+ [OP_LEX_BODY]: "LEX_BODY",
6545
+ [OP_LEX_PROGRAM]: "LEX_PROGRAM"
6546
+ };
6547
+
6492
6548
  // src/compiler/direct-projection.ts
6493
6549
  function directArrayProjection(fn, fnSrc, arity) {
6494
6550
  let source = fnSrc;
@@ -7369,6 +7425,7 @@ var Encoder = class {
7369
7425
  case "expect":
7370
7426
  case "recover":
7371
7427
  case "scanTo":
7428
+ case "lines":
7372
7429
  case "unknown":
7373
7430
  return true;
7374
7431
  case "lazy": {
@@ -7589,6 +7646,18 @@ var Encoder = class {
7589
7646
  open: bal.open,
7590
7647
  close: bal.close
7591
7648
  }));
7649
+ if (d.tag === "lines") {
7650
+ return this.emit(OP_SCAN, this.scanSlot({
7651
+ kind: 2,
7652
+ flags: 0,
7653
+ skip: [],
7654
+ prefix: this.subtree(d.prefix),
7655
+ body: this.subtree(d.body),
7656
+ ...d.first === void 0 ? {} : { first: this.subtree(d.first) },
7657
+ ...d.lazy === void 0 ? {} : { lazy: this.subtree(d.lazy) }
7658
+ }));
7659
+ }
7660
+ if (d.tag === "columns") return this.emit(OP_SCAN, this.scanSlot({ kind: 3, flags: 0, skip: [], n: d.n }));
7592
7661
  if (d.tag === "scanTo") {
7593
7662
  const sentDef = d.sentinel._def;
7594
7663
  return this.emit(OP_SCAN, this.scanSlot({
@@ -8035,12 +8104,7 @@ var Encoder = class {
8035
8104
  };
8036
8105
  const seen = /* @__PURE__ */ new Set();
8037
8106
  const refs = [];
8038
- for (const s of this.scans) {
8039
- if (s.sentinel !== void 0) refs.push(s.sentinel);
8040
- if (s.recoverAt !== void 0) refs.push(s.recoverAt);
8041
- if (s.stopAt !== void 0) refs.push(s.stopAt);
8042
- refs.push(...s.skip);
8043
- }
8107
+ for (const s of this.scans) refs.push(...scanRefs(s));
8044
8108
  for (const set of this.scanSkipSets) refs.push(...set);
8045
8109
  const stack = [...Object.values(this.rules), ...refs.map((r) => r[0])];
8046
8110
  while (stack.length > 0) {
@@ -8060,7 +8124,11 @@ var Encoder = class {
8060
8124
  skip: s.skip.map(res),
8061
8125
  ...s.sentinel === void 0 ? {} : { sentinel: res(s.sentinel) },
8062
8126
  ...s.recoverAt === void 0 ? {} : { recoverAt: res(s.recoverAt) },
8063
- ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) }
8127
+ ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) },
8128
+ ...s.prefix === void 0 ? {} : { prefix: res(s.prefix) },
8129
+ ...s.body === void 0 ? {} : { body: res(s.body) },
8130
+ ...s.first === void 0 ? {} : { first: res(s.first) },
8131
+ ...s.lazy === void 0 ? {} : { lazy: res(s.lazy) }
8064
8132
  }));
8065
8133
  this.scanSkipSets = this.scanSkipSets.map((set) => set.map(res));
8066
8134
  }
@@ -8902,7 +8970,7 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8902
8970
  if (shape.kind === "alt") {
8903
8971
  const okV = mint("_ok");
8904
8972
  const endV2 = mint("_end");
8905
- const lines = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
8973
+ const lines2 = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
8906
8974
  if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
8907
8975
  const groups = /* @__PURE__ */ new Map();
8908
8976
  for (const a of shape.arms) {
@@ -8911,60 +8979,60 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8911
8979
  const lower = f >= 65 && f <= 90 ? f + 32 : f;
8912
8980
  (groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
8913
8981
  }
8914
- lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8982
+ lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8915
8983
  for (const [lower, arms] of groups) {
8916
8984
  const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
8917
- lines.push(`${ind} ${labels} {`);
8985
+ lines2.push(`${ind} ${labels} {`);
8918
8986
  for (const open of arms) {
8919
8987
  const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
8920
8988
  const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
8921
- lines.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
8989
+ lines2.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
8922
8990
  }
8923
- lines.push(`${ind} break`);
8924
- lines.push(`${ind} }`);
8991
+ lines2.push(`${ind} break`);
8992
+ lines2.push(`${ind} }`);
8925
8993
  }
8926
- lines.push(`${ind}}`);
8927
- return { setup: lines, ok: okV, end: endV2 };
8994
+ lines2.push(`${ind}}`);
8995
+ return { setup: lines2, ok: okV, end: endV2 };
8928
8996
  }
8929
8997
  if (shape.disjoint) {
8930
8998
  const plan = planAltSwitch(shape.firsts);
8931
8999
  if (plan.kind === "switch") {
8932
- lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
9000
+ lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8933
9001
  shape.arms.forEach((arm, k) => {
8934
9002
  const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
8935
9003
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8936
- lines.push(`${ind} ${labels} {`);
8937
- lines.push(...m.setup);
8938
- lines.push(`${ind} ${okV} = ${m.ok}`);
8939
- lines.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8940
- lines.push(`${ind} break`);
8941
- lines.push(`${ind} }`);
9004
+ lines2.push(`${ind} ${labels} {`);
9005
+ lines2.push(...m.setup);
9006
+ lines2.push(`${ind} ${okV} = ${m.ok}`);
9007
+ lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
9008
+ lines2.push(`${ind} break`);
9009
+ lines2.push(`${ind} }`);
8942
9010
  });
8943
- lines.push(`${ind}}`);
9011
+ lines2.push(`${ind}}`);
8944
9012
  } else {
8945
9013
  const c0 = codeAt(start, firstChar);
8946
9014
  shape.arms.forEach((arm, k) => {
8947
9015
  const fs3 = shape.firsts[k];
8948
9016
  const cond = fs3.negated ? `!(${classCond(c0, fs3.ranges)})` : `(${classCond(c0, fs3.ranges)})`;
8949
9017
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8950
- lines.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
8951
- lines.push(...m.setup);
8952
- lines.push(`${ind} ${okV} = ${m.ok}`);
8953
- lines.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8954
- lines.push(`${ind}}`);
9018
+ lines2.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
9019
+ lines2.push(...m.setup);
9020
+ lines2.push(`${ind} ${okV} = ${m.ok}`);
9021
+ lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
9022
+ lines2.push(`${ind}}`);
8955
9023
  });
8956
9024
  }
8957
9025
  } else {
8958
9026
  const lbl = mint("_alt");
8959
- lines.push(`${ind}${lbl}: {`);
9027
+ lines2.push(`${ind}${lbl}: {`);
8960
9028
  for (const arm of shape.arms) {
8961
9029
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8962
- lines.push(...m.setup);
8963
- lines.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
9030
+ lines2.push(...m.setup);
9031
+ lines2.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
8964
9032
  }
8965
- lines.push(`${ind}}`);
9033
+ lines2.push(`${ind}}`);
8966
9034
  }
8967
- return { setup: lines, ok: okV, end: endV2 };
9035
+ return { setup: lines2, ok: okV, end: endV2 };
8968
9036
  }
8969
9037
  if (shape.kind === "chars") {
8970
9038
  const cur = mint("_e");
@@ -8999,64 +9067,64 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8999
9067
  const c = classCond(`input.charCodeAt(${at})`, p.ranges);
9000
9068
  return p.negated ? `!(${c})` : `(${c})`;
9001
9069
  };
9002
- const lines = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
9070
+ const lines2 = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
9003
9071
  let first = true;
9004
9072
  for (const p of shape.parts) {
9005
9073
  if (p.part === "lit") {
9006
9074
  const L = p.cps.length;
9007
9075
  const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
9008
9076
  if (p.optional) {
9009
- lines.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
9077
+ lines2.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
9010
9078
  } else {
9011
- lines.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
9012
- lines.push(`${ind} ${endV2} += ${L}`);
9079
+ lines2.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
9080
+ lines2.push(`${ind} ${endV2} += ${L}`);
9013
9081
  }
9014
9082
  } else if (p.part === "run" && p.max === Infinity) {
9015
9083
  const s = mint("_s");
9016
- lines.push(`${ind} const ${s} = ${endV2}`);
9017
- lines.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9018
- if (p.min === 1) lines.push(`${ind} if (${endV2} === ${s}) break`);
9019
- else if (p.min > 1) lines.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
9084
+ lines2.push(`${ind} const ${s} = ${endV2}`);
9085
+ lines2.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9086
+ if (p.min === 1) lines2.push(`${ind} if (${endV2} === ${s}) break`);
9087
+ else if (p.min > 1) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
9020
9088
  } else if (p.part === "run" && p.max === 1) {
9021
9089
  if (p.min === 1) {
9022
- lines.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
9023
- lines.push(`${ind} ${endV2}++`);
9090
+ lines2.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
9091
+ lines2.push(`${ind} ${endV2}++`);
9024
9092
  } else {
9025
- lines.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9093
+ lines2.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9026
9094
  }
9027
9095
  } else if (p.part === "run") {
9028
9096
  const s = mint("_s");
9029
- lines.push(`${ind} const ${s} = ${endV2}`);
9030
- lines.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9031
- if (p.min > 0) lines.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
9097
+ lines2.push(`${ind} const ${s} = ${endV2}`);
9098
+ lines2.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
9099
+ if (p.min > 0) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
9032
9100
  } else {
9033
9101
  const inner = emitShapeMatch(p.inner, endV2, mint, `${ind} `, first ? firstChar : void 0);
9034
9102
  if (!p.unbounded) {
9035
- lines.push(...inner.setup);
9103
+ lines2.push(...inner.setup);
9036
9104
  if (p.min === 1) {
9037
- lines.push(`${ind} if (!(${inner.ok})) break`);
9038
- lines.push(`${ind} ${endV2} = ${inner.end}`);
9105
+ lines2.push(`${ind} if (!(${inner.ok})) break`);
9106
+ lines2.push(`${ind} ${endV2} = ${inner.end}`);
9039
9107
  } else {
9040
- lines.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
9108
+ lines2.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
9041
9109
  }
9042
9110
  } else {
9043
9111
  const progV = mint("_prog");
9044
- lines.push(`${ind} let ${progV} = false`);
9045
- lines.push(`${ind} while (true) {`);
9046
- lines.push(...inner.setup);
9047
- lines.push(`${ind} if (!(${inner.ok})) break`);
9048
- lines.push(`${ind} ${endV2} = ${inner.end}`);
9049
- lines.push(`${ind} ${progV} = true`);
9050
- lines.push(`${ind} }`);
9051
- if (p.min === 1) lines.push(`${ind} if (!${progV}) break`);
9112
+ lines2.push(`${ind} let ${progV} = false`);
9113
+ lines2.push(`${ind} while (true) {`);
9114
+ lines2.push(...inner.setup);
9115
+ lines2.push(`${ind} if (!(${inner.ok})) break`);
9116
+ lines2.push(`${ind} ${endV2} = ${inner.end}`);
9117
+ lines2.push(`${ind} ${progV} = true`);
9118
+ lines2.push(`${ind} }`);
9119
+ if (p.min === 1) lines2.push(`${ind} if (!${progV}) break`);
9052
9120
  }
9053
9121
  }
9054
9122
  first = false;
9055
9123
  }
9056
- lines.push(`${ind} ${okV} = true`);
9057
- lines.push(`${ind}} while (false)`);
9058
- lines.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
9059
- return { setup: lines, ok: okV, end: endV2 };
9124
+ lines2.push(`${ind} ${okV} = true`);
9125
+ lines2.push(`${ind}} while (false)`);
9126
+ lines2.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
9127
+ return { setup: lines2, ok: okV, end: endV2 };
9060
9128
  }
9061
9129
  if (shape.kind === "litFold") {
9062
9130
  const openLen2 = shape.open.length;
@@ -11333,6 +11401,188 @@ finish:_finish
11333
11401
  };
11334
11402
  }
11335
11403
 
11404
+ // src/combinators/lines.ts
11405
+ function lineIndexAt(view, offset) {
11406
+ const lines2 = view.lines;
11407
+ let lo = 0, hi = lines2.length - 1;
11408
+ while (lo < hi) {
11409
+ const mid = lo + hi + 1 >> 1;
11410
+ if (lines2[mid].offset <= offset) lo = mid;
11411
+ else hi = mid - 1;
11412
+ }
11413
+ return lo;
11414
+ }
11415
+ var lineAt = (view, offset) => view.lines[lineIndexAt(view, offset)];
11416
+ function sourceOffset(view, offset) {
11417
+ const line = lineAt(view, offset);
11418
+ return line === void 0 ? offset : line.source + (offset - line.offset);
11419
+ }
11420
+ function columnAt(input, pos, ctx) {
11421
+ const view = ctx.lineView;
11422
+ let start, column, split;
11423
+ const line = view === void 0 ? void 0 : lineAt(view, pos);
11424
+ if (line !== void 0 && pos >= line.offset) {
11425
+ start = line.offset;
11426
+ column = line.column;
11427
+ split = line.split;
11428
+ } else {
11429
+ start = input.lastIndexOf("\n", pos - 1) + 1;
11430
+ column = 0;
11431
+ split = 0;
11432
+ }
11433
+ for (let i = start; i < pos; i++) {
11434
+ if (input.charCodeAt(i) === 9) {
11435
+ const width = 4 - (column - (i === start ? split : 0)) % 4 - (i === start ? split : 0);
11436
+ column += width;
11437
+ } else column++;
11438
+ split = 0;
11439
+ }
11440
+ return { column, split: pos === start ? split : 0 };
11441
+ }
11442
+ function columns(n) {
11443
+ if (!Number.isInteger(n) || n < 0) throw new TypeError(`columns(n) needs a non-negative integer, got ${n}`);
11444
+ const expected = [`columns(${n})`];
11445
+ const meta = {
11446
+ firstSet: n === 0 ? any() : { kind: "ranges", ranges: [{ lo: 9, hi: 9 }, { lo: 32, hi: 32 }] },
11447
+ canMatchNewline: false,
11448
+ isTrivia: false
11449
+ };
11450
+ return {
11451
+ _tag: "columns",
11452
+ _meta: meta,
11453
+ _def: { tag: "columns", n },
11454
+ parse(input, pos, ctx) {
11455
+ const c = ctx;
11456
+ let { column, split } = columnAt(input, pos, c);
11457
+ const goal = column + n;
11458
+ let at = pos;
11459
+ while (column < goal) {
11460
+ const code = input.charCodeAt(at);
11461
+ if (code === 32) {
11462
+ column++;
11463
+ split = 0;
11464
+ at++;
11465
+ continue;
11466
+ }
11467
+ if (code !== 9) return { ok: false, expected, span: { start: at, end: at } };
11468
+ const width = 4 - (column - split) % 4 - split;
11469
+ if (column + width > goal) {
11470
+ c._columnsSplit = { at, split: split + (goal - column) };
11471
+ return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
11472
+ }
11473
+ column += width;
11474
+ split = 0;
11475
+ at++;
11476
+ }
11477
+ return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
11478
+ }
11479
+ };
11480
+ }
11481
+ function lines(prefix, body, options = {}) {
11482
+ const { first, lazy } = options;
11483
+ const opening = first ?? prefix;
11484
+ const meta = {
11485
+ // An opening that matches empty starts a run at any character.
11486
+ firstSet: matchesEmpty(opening) ? any() : opening._meta.firstSet,
11487
+ canMatchNewline: true,
11488
+ isTrivia: false
11489
+ };
11490
+ return {
11491
+ _tag: "lines",
11492
+ _meta: meta,
11493
+ _def: { tag: "lines", prefix, body, ...first === void 0 ? {} : { first }, ...lazy === void 0 ? {} : { lazy } },
11494
+ parse(input, pos, ctx) {
11495
+ const c = ctx;
11496
+ const probe = createDetachedParseContext(false, ctx.state);
11497
+ probe.lineView = c.lineView;
11498
+ const run = [];
11499
+ const parts = [];
11500
+ let offset = 0;
11501
+ for (let start = pos; start < input.length || start === pos; ) {
11502
+ let end2 = input.indexOf("\n", start);
11503
+ if (end2 < 0) end2 = input.length;
11504
+ probe._columnsSplit = void 0;
11505
+ const head = (run.length === 0 ? opening : prefix).parse(input, start, probe);
11506
+ let content;
11507
+ let joined;
11508
+ if (head.ok && head.span.end <= end2) {
11509
+ content = head.span.end;
11510
+ joined = "prefix";
11511
+ } else if (run.length === 0) {
11512
+ return { ok: false, expected: head.ok ? ["line prefix"] : head.expected, span: { start, end: start } };
11513
+ } else if (lazy !== void 0 && lazy.parse(input, start, probe).ok) {
11514
+ content = start;
11515
+ joined = "lazy";
11516
+ } else break;
11517
+ const mark = probe._columnsSplit;
11518
+ const here = columnAt(input, content, c);
11519
+ const split = joined === "prefix" && mark !== void 0 && mark.at === content ? mark.split : here.split;
11520
+ run.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
11521
+ const text = input.slice(content, end2);
11522
+ const newline = end2 < input.length;
11523
+ parts.push(newline ? `${text}
11524
+ ` : text);
11525
+ offset += text.length + (newline ? 1 : 0);
11526
+ start = end2 + 1;
11527
+ if (!newline) break;
11528
+ }
11529
+ const view = { lines: run, parent: c.lineView };
11530
+ const saved = c.lineView, starts = c._lineStarts, lineIndex = c._lineIndex, scanned = c._lineScannedTo, probing = c._probe;
11531
+ const errors = c._errors?.length ?? 0;
11532
+ let cursor = -1;
11533
+ if (probing !== void 0) for (const line2 of run) {
11534
+ if (probing.offset >= line2.source && probing.offset <= line2.end) {
11535
+ cursor = line2.offset + probing.offset - line2.source;
11536
+ break;
11537
+ }
11538
+ }
11539
+ const inner = cursor < 0 ? void 0 : { offset: cursor, best: null };
11540
+ c.lineView = view;
11541
+ c._lineStarts = void 0;
11542
+ c._lineIndex = void 0;
11543
+ c._lineScannedTo = void 0;
11544
+ c._probe = inner;
11545
+ let result;
11546
+ try {
11547
+ result = body.parse(parts.join(""), 0, ctx);
11548
+ } finally {
11549
+ c.lineView = saved;
11550
+ c._lineStarts = starts;
11551
+ c._lineIndex = lineIndex;
11552
+ c._lineScannedTo = scanned;
11553
+ c._probe = probing;
11554
+ }
11555
+ if (inner?.best) failAt(c, inner.best.expected, sourceOffset(view, inner.best.span.start));
11556
+ if (c._errors !== void 0) for (let i = errors; i < c._errors.length; i++) {
11557
+ const error = c._errors[i];
11558
+ error.span = { start: sourceOffset(view, error.span.start), end: sourceOffset(view, error.span.end) };
11559
+ }
11560
+ if (!result.ok) {
11561
+ const at = sourceOffset(view, result.span.start);
11562
+ return { ...result, span: { start: at, end: at } };
11563
+ }
11564
+ const stop = result.span.end;
11565
+ const index = lineIndexAt(view, stop);
11566
+ const line = run[index];
11567
+ const end = stop === line.offset && index > 0 ? run[index - 1].end + 1 : stop - line.offset > line.end - line.source ? line.end + 1 : line.source + (stop - line.offset);
11568
+ recordLineRangeFromContext(c, input, pos, end);
11569
+ return { ok: true, value: result.value, span: { start: pos, end } };
11570
+ }
11571
+ };
11572
+ }
11573
+
11574
+ // src/table/line-scan.ts
11575
+ function rebuildLineScan(s, subtree) {
11576
+ if (s.kind === 2) {
11577
+ return lines(subtree(s.prefix), subtree(s.body), {
11578
+ ...s.first === void 0 ? {} : { first: subtree(s.first) },
11579
+ ...s.lazy === void 0 ? {} : { lazy: subtree(s.lazy) }
11580
+ });
11581
+ }
11582
+ if (s.kind === 3) return columns(s.n);
11583
+ return void 0;
11584
+ }
11585
+
11336
11586
  // src/combinators/scanTo.ts
11337
11587
  function resolveScanSkip(explicitSkip, raw, ctx) {
11338
11588
  if (raw) return [];
@@ -11649,7 +11899,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11649
11899
  ...prog.labels === void 0 ? {} : { triviaKindLabels: prog.labels },
11650
11900
  ...prog.classified === 1 ? { rootTriviaClassified: true } : {}
11651
11901
  };
11652
- const lines = prog.lines === 1;
11902
+ const lines2 = prog.lines === 1;
11653
11903
  const mode = prog.hostMode ?? "ast";
11654
11904
  const elided = mode === "ast" && hasDirectBuilder(prog);
11655
11905
  const coverageDefinitions = prog.cov === void 0 ? void 0 : Object.freeze(covDefinitions(prog).map(Object.freeze));
@@ -11677,7 +11927,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11677
11927
  ctx._fe = -1;
11678
11928
  ctx._fx = EMPTY_FX;
11679
11929
  ctx._fc = false;
11680
- if (lines && ctx._lineStarts === void 0) {
11930
+ if (lines2 && ctx._lineStarts === void 0) {
11681
11931
  ctx._lineStarts = [0];
11682
11932
  ctx._lineScannedTo = 0;
11683
11933
  }
@@ -11685,10 +11935,10 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11685
11935
  if (end < 0) {
11686
11936
  const fe = ctx._fe;
11687
11937
  const at = fe === void 0 || fe < 0 ? pos : fe;
11688
- const fspan = lines ? spanLines(ctx, at, at) : { start: at, end: at };
11938
+ const fspan = lines2 ? spanLines(ctx, at, at) : { start: at, end: at };
11689
11939
  return committed2(ctx) ? { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan, committed: true } : { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan };
11690
11940
  }
11691
- return { ok: true, value: d.lastValue(), span: lines ? spanLines(ctx, pos, end) : { start: pos, end } };
11941
+ return { ok: true, value: d.lastValue(), span: lines2 ? spanLines(ctx, pos, end) : { start: pos, end } };
11692
11942
  };
11693
11943
  const ownTrivia = triviaOfRule(prog.rules[name]);
11694
11944
  const meta = ownTrivia === void 0 ? baseMeta : { ...baseMeta, grammarTrivia: ownTrivia };
@@ -11783,12 +12033,7 @@ function assemble(t, prog, cfg) {
11783
12033
  return recognize;
11784
12034
  }
11785
12035
  const labelExtraIps = [];
11786
- for (const s of prog.scans ?? []) {
11787
- for (const r of s.skip) labelExtraIps.push(r[0]);
11788
- if (s.sentinel !== void 0) labelExtraIps.push(s.sentinel[0]);
11789
- if (s.recoverAt !== void 0) labelExtraIps.push(s.recoverAt[0]);
11790
- if (s.stopAt !== void 0) labelExtraIps.push(s.stopAt[0]);
11791
- }
12036
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) labelExtraIps.push(r[0]);
11792
12037
  for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0]);
11793
12038
  const closureLabels = computeSiteLabels(
11794
12039
  code,
@@ -14599,12 +14844,7 @@ function assemble(t, prog, cfg) {
14599
14844
  let emitReached;
14600
14845
  {
14601
14846
  const extraIps = [];
14602
- for (const s of prog.scans ?? []) {
14603
- for (const r of s.skip) extraIps.push(r[0]);
14604
- if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
14605
- if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
14606
- if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
14607
- }
14847
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
14608
14848
  for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
14609
14849
  const roots = [...Object.values(prog.rules), ...extraIps];
14610
14850
  for (const ip of reachableSites(code, roots)) {
@@ -14801,6 +15041,11 @@ function assemble(t, prog, cfg) {
14801
15041
  return link(ip);
14802
15042
  }
14803
15043
  for (const s of prog.scans ?? []) {
15044
+ const rebuilt = rebuildLineScan(s, subtreeComb);
15045
+ if (rebuilt !== void 0) {
15046
+ scansArr.push(rebuilt);
15047
+ continue;
15048
+ }
14804
15049
  const skip = s.skip.map((r) => subtreeComb(r));
14805
15050
  const raw = (s.flags & 1) !== 0;
14806
15051
  if (s.kind === 1) {
@@ -15042,6 +15287,11 @@ function emitScanSpec(s) {
15042
15287
  if (s.sent !== void 0) parts.push(`sent:${s.sent === null ? "null" : jsString(s.sent)}`);
15043
15288
  if (s.open !== void 0) parts.push(`open:${jsString(s.open)}`);
15044
15289
  if (s.close !== void 0) parts.push(`close:${jsString(s.close)}`);
15290
+ for (const key of ["prefix", "body", "first", "lazy"]) {
15291
+ const r = s[key];
15292
+ if (r !== void 0) parts.push(`${key}:${emitRef(r)}`);
15293
+ }
15294
+ if (s.n !== void 0) parts.push(`n:${s.n}`);
15045
15295
  return `{${parts.join(",")}}`;
15046
15296
  }
15047
15297
  function emitDispatchSpec(d) {
@@ -15062,12 +15312,7 @@ function emitAssemblies(prog, cfgs) {
15062
15312
  if (cfgs.length === 0) return ["a:[],"];
15063
15313
  const t = resolveTable(prog);
15064
15314
  const extraIps = [];
15065
- for (const s of prog.scans ?? []) {
15066
- for (const r of s.skip) extraIps.push(r[0]);
15067
- if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
15068
- if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
15069
- if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
15070
- }
15315
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
15071
15316
  for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
15072
15317
  const out = [];
15073
15318
  const seen = /* @__PURE__ */ new Set();
@@ -15219,6 +15464,8 @@ function children(def, winners) {
15219
15464
  ...def.recoverAt === void 0 ? [] : [def.recoverAt],
15220
15465
  ...def.stopAt === void 0 ? [] : [def.stopAt]
15221
15466
  ];
15467
+ case "lines":
15468
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
15222
15469
  case "lazy": {
15223
15470
  let resolved;
15224
15471
  try {
@@ -15362,6 +15609,10 @@ function payloadKey(p, d) {
15362
15609
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
15363
15610
  case "scanTo":
15364
15611
  return `scanTo\0${d.raw}\0${d.orEOF}\0${d.recoverAt !== void 0}\0${d.stopAt !== void 0}`;
15612
+ case "lines":
15613
+ return `lines\0${d.first !== void 0}\0${d.lazy !== void 0}`;
15614
+ case "columns":
15615
+ return `columns\0${d.n}`;
15365
15616
  case "guard":
15366
15617
  return `guard\0${fnKey(d.predSrc, d.predicate)}`;
15367
15618
  case "adjacency":
@@ -15482,6 +15733,8 @@ function segment(d, index) {
15482
15733
  if (d.recoverAt !== void 0 && index === d.skip.length + 1) return "scanTo.recoverAt";
15483
15734
  return "scanTo.stopAt";
15484
15735
  }
15736
+ case "lines":
15737
+ return ["lines.prefix", "lines.body", d.first === void 0 ? "lines.lazy" : "lines.first", "lines.lazy"][index] ?? "lines";
15485
15738
  default:
15486
15739
  return d.tag;
15487
15740
  }
@@ -16524,60 +16777,60 @@ function findKeywordRegexes(visited) {
16524
16777
  var sitesLine = (sites) => sites.slice(0, 6).map(siteToString).join("\n ") + (sites.length > 6 ? `
16525
16778
  \u2026 and ${sites.length - 6} more` : "");
16526
16779
  function formatDuplicationFindings(report) {
16527
- const lines = [];
16780
+ const lines2 = [];
16528
16781
  for (const f of report.structureLoss) {
16529
- lines.push(`parseman BUG [structure-loss] @ ${siteToString(f.site)}: arm[${f.earlier}] flattens \`${f.nodeType}\` where arm[${f.later}] structures it, on ${firstSetToString(f.on)}`);
16530
- lines.push(` flat: ${f.earlierShape}`);
16531
- lines.push(` rich: ${f.laterShape}`);
16532
- lines.push(` lost: ${f.lostNodeTypes.map((t) => `\`${t}\``).join(", ")}`);
16533
- lines.push(` fix: ${f.suggestion}`);
16782
+ lines2.push(`parseman BUG [structure-loss] @ ${siteToString(f.site)}: arm[${f.earlier}] flattens \`${f.nodeType}\` where arm[${f.later}] structures it, on ${firstSetToString(f.on)}`);
16783
+ lines2.push(` flat: ${f.earlierShape}`);
16784
+ lines2.push(` rich: ${f.laterShape}`);
16785
+ lines2.push(` lost: ${f.lostNodeTypes.map((t) => `\`${t}\``).join(", ")}`);
16786
+ lines2.push(` fix: ${f.suggestion}`);
16534
16787
  }
16535
16788
  for (const f of report.rewrites) {
16536
- lines.push(`parseman ${f.bug ? "BUG" : "rewrite"} [${f.rewrite}${f.sepByVerdict === void 0 ? "" : `/${f.sepByVerdict}`}] @ ${siteToString(f.site)}${f.astNeutral ? "" : " (candidate \u2014 verify AST identity)"}`);
16537
- lines.push(` from: ${f.from}`);
16538
- lines.push(` to: ${f.to}`);
16539
- lines.push(` fix: ${f.suggestion}`);
16540
- if (f.perf !== "") lines.push(` perf: ${f.perf}`);
16789
+ lines2.push(`parseman ${f.bug ? "BUG" : "rewrite"} [${f.rewrite}${f.sepByVerdict === void 0 ? "" : `/${f.sepByVerdict}`}] @ ${siteToString(f.site)}${f.astNeutral ? "" : " (candidate \u2014 verify AST identity)"}`);
16790
+ lines2.push(` from: ${f.from}`);
16791
+ lines2.push(` to: ${f.to}`);
16792
+ lines2.push(` fix: ${f.suggestion}`);
16793
+ if (f.perf !== "") lines2.push(` perf: ${f.perf}`);
16541
16794
  }
16542
16795
  for (const f of report.divergentNodes) {
16543
- lines.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t) => `\`${t}\``).join(", ")}`);
16544
- for (const p of f.productions) lines.push(` ${siteToString(p.site)}
16796
+ lines2.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t) => `\`${t}\``).join(", ")}`);
16797
+ for (const p of f.productions) lines2.push(` ${siteToString(p.site)}
16545
16798
  ${p.shape}
16546
16799
  unique to it: ${p.distinctTerms.length === 0 ? "(none)" : p.distinctTerms.map((t) => `\`${t}\``).join(", ")}`);
16547
- lines.push(` fix: ${f.suggestion}`);
16800
+ lines2.push(` fix: ${f.suggestion}`);
16548
16801
  }
16549
16802
  for (const f of report.keywordRegexes) {
16550
16803
  const src = f.source.length > 90 ? `${f.source.slice(0, 88)}\u2026` : f.source;
16551
- lines.push(`parseman ${f.bug ? "BUG " : ""}keyword-regex @ ${siteToString(f.site)}: /${src}/${f.flags}${f.vocabulary ? ` [${f.words.length} literal words${f.longestFirst ? "" : ", NOT longest-first"}]` : ""}${f.hazards.length > 0 ? ` [${f.hazards.length} prefix hazard(s)${f.bug ? ", UNRESCUED" : ""}]` : ""}${f.caseFoldRisk ? " [CASE-FOLD RISK]" : ""}`);
16552
- lines.push(` fix: ${f.suggestion}`);
16804
+ lines2.push(`parseman ${f.bug ? "BUG " : ""}keyword-regex @ ${siteToString(f.site)}: /${src}/${f.flags}${f.vocabulary ? ` [${f.words.length} literal words${f.longestFirst ? "" : ", NOT longest-first"}]` : ""}${f.hazards.length > 0 ? ` [${f.hazards.length} prefix hazard(s)${f.bug ? ", UNRESCUED" : ""}]` : ""}${f.caseFoldRisk ? " [CASE-FOLD RISK]" : ""}`);
16805
+ lines2.push(` fix: ${f.suggestion}`);
16553
16806
  }
16554
16807
  for (const f of report.duplicates) {
16555
- lines.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16556
- lines.push(` at: ${sitesLine(f.sites)}`);
16557
- lines.push(` fix: ${f.suggestion}`);
16808
+ lines2.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16809
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16810
+ lines2.push(` fix: ${f.suggestion}`);
16558
16811
  }
16559
16812
  for (const f of report.nearDuplicates) {
16560
- lines.push(`parseman near-duplication: ${f.count} clones of a ${f.sharedSize}-node shape differing at ONE slot (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16561
- lines.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
16562
- lines.push(` at: ${sitesLine(f.sites)}`);
16563
- lines.push(` fix: ${f.suggestion}`);
16813
+ lines2.push(`parseman near-duplication: ${f.count} clones of a ${f.sharedSize}-node shape differing at ONE slot (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16814
+ lines2.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
16815
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16816
+ lines2.push(` fix: ${f.suggestion}`);
16564
16817
  }
16565
16818
  for (const f of report.regexFragments) {
16566
- lines.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
16567
- lines.push(` at: ${sitesLine(f.sites)}`);
16568
- lines.push(` fix: ${f.suggestion}`);
16819
+ lines2.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
16820
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16821
+ lines2.push(` fix: ${f.suggestion}`);
16569
16822
  }
16570
16823
  for (const f of report.regexClasses) {
16571
- lines.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
16572
- for (const v of f.variants) lines.push(` [${v.source}] \xD7${v.count} ${v.delta}
16824
+ lines2.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
16825
+ for (const v of f.variants) lines2.push(` [${v.source}] \xD7${v.count} ${v.delta}
16573
16826
  ${sitesLine(v.sites)}`);
16574
- lines.push(` fix: ${f.suggestion}`);
16827
+ lines2.push(` fix: ${f.suggestion}`);
16575
16828
  }
16576
16829
  for (const f of report.overlaps) {
16577
- lines.push(`parseman overlap @ ${siteToString(f.site)}: arm[${f.a}] \u2229 arm[${f.b}] on ${firstSetToString(f.on)}${f.sharedPrefix === null ? "" : ` \u2014 shared prefix: ${f.sharedPrefix}`}${f.handledByStrategy ? " [sharedPrefix strategy already handles this]" : ""}`);
16578
- lines.push(` fix: ${f.suggestion}`);
16830
+ lines2.push(`parseman overlap @ ${siteToString(f.site)}: arm[${f.a}] \u2229 arm[${f.b}] on ${firstSetToString(f.on)}${f.sharedPrefix === null ? "" : ` \u2014 shared prefix: ${f.sharedPrefix}`}${f.handledByStrategy ? " [sharedPrefix strategy already handles this]" : ""}`);
16831
+ lines2.push(` fix: ${f.suggestion}`);
16579
16832
  }
16580
- return lines;
16833
+ return lines2;
16581
16834
  }
16582
16835
  function duplicationFindingCount(report) {
16583
16836
  return report.duplicates.length + report.nearDuplicates.length + report.regexFragments.length + report.regexClasses.length + report.overlaps.length + report.rewrites.length + report.divergentNodes.length + report.structureLoss.length + report.keywordRegexes.length;
@@ -16611,11 +16864,11 @@ function reportDuplication(opt, analyze) {
16611
16864
  );
16612
16865
  return void 0;
16613
16866
  }
16614
- const lines = formatDuplicationFindings(report);
16615
- if (lines.length > 0) {
16867
+ const lines2 = formatDuplicationFindings(report);
16868
+ if (lines2.length > 0) {
16616
16869
  if (level === "error") throw new Error(`parseman: ${duplicationFindingCount(report)} duplication/overlap finding(s)
16617
- ${lines.join("\n")}`);
16618
- for (const l of lines) console.warn(l);
16870
+ ${lines2.join("\n")}`);
16871
+ for (const l of lines2) console.warn(l);
16619
16872
  }
16620
16873
  return report;
16621
16874
  }
@@ -16962,6 +17215,9 @@ function childrenOf3(def) {
16962
17215
  ];
16963
17216
  case "routed":
16964
17217
  return def.fallback ? [def.fallback] : [];
17218
+ case "lines":
17219
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
17220
+ case "columns":
16965
17221
  case "lazy":
16966
17222
  case "literal":
16967
17223
  case "regex":
@@ -17291,6 +17547,15 @@ ${outerDecls.map((d) => " " + d).join("\n")}
17291
17547
  return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
17292
17548
  case "scanTo":
17293
17549
  return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}]${def.raw ? ", raw: true" : ""}, orEOF: ${def.orEOF}${def.recoverAt === void 0 ? "" : `, recoverAt: ${kid(def.recoverAt)}`}${def.stopAt === void 0 ? "" : `, stopAt: ${kid(def.stopAt)}`} })`;
17550
+ case "lines": {
17551
+ const options = [
17552
+ ...def.first === void 0 ? [] : [`first: ${kid(def.first)}`],
17553
+ ...def.lazy === void 0 ? [] : [`lazy: ${kid(def.lazy)}`]
17554
+ ];
17555
+ return `lines(${kid(def.prefix)}, ${kid(def.body)}${options.length === 0 ? "" : `, { ${options.join(", ")} }`})`;
17556
+ }
17557
+ case "columns":
17558
+ return `columns(${def.n})`;
17294
17559
  case "transform": {
17295
17560
  if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
17296
17561
  return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
@@ -17355,7 +17620,7 @@ function fieldOptionsSrc(def) {
17355
17620
  }
17356
17621
 
17357
17622
  // src/version.ts
17358
- var PARSEMAN_VERSION = "0.55.4";
17623
+ var PARSEMAN_VERSION = "0.56.0";
17359
17624
 
17360
17625
  // src/compiler/compile-linkable-table.ts
17361
17626
  function isLocal(rule) {
@@ -17882,6 +18147,10 @@ var SUPPORTED = {
17882
18147
  field: (...a) => field(a[0], a[1], a[2]),
17883
18148
  noTrivia: (...a) => noTrivia(a[0]),
17884
18149
  token: (...a) => token(a[0]),
18150
+ // `lines(prefix, body, { first, lazy })`: its options are combinators, which the
18151
+ // generic argument evaluation already resolves; a missing option stays absent.
18152
+ lines: (...a) => lines(a[0], a[1], a[2]),
18153
+ columns: (...a) => columns(a[0]),
17885
18154
  // `routed(fallback)` — the fallback must be forwarded. A zero-arg entry here read
17886
18155
  // as "routed takes no arguments" and SILENTLY built a bare `routed()`, so a
17887
18156
  // production written to work both inside and outside a dispatch branch lost its
@@ -19964,6 +20233,8 @@ function evalIR(ir, rulesImpl) {
19964
20233
  "_nd",
19965
20234
  "_gch",
19966
20235
  "_wc",
20236
+ "lines",
20237
+ "columns",
19967
20238
  `return (${ir})`
19968
20239
  );
19969
20240
  return fn(
@@ -20007,7 +20278,9 @@ function evalIR(ir, rulesImpl) {
20007
20278
  _lf,
20008
20279
  _nd,
20009
20280
  _gch,
20010
- _wc
20281
+ _wc,
20282
+ lines,
20283
+ columns
20011
20284
  );
20012
20285
  }
20013
20286
 
@@ -21478,14 +21751,14 @@ ${names.map((n) => ` - ${n}`).join("\n")}` : " (it is not a static combinator/v
21478
21751
  }
21479
21752
  if (!allOk) continue;
21480
21753
  const holderVar = `__rules${ruleMapHolderCounter++}`;
21481
- const lines = [
21754
+ const lines2 = [
21482
21755
  `const ${holderVar} = ${compiledRules.replacement}`,
21483
21756
  ...bindings.map(({ ruleKey, localName }) => `${exportPrefix}${kind} ${localName} = ${holderVar}[${JSON.stringify(ruleKey)}]`)
21484
21757
  ];
21485
21758
  replacements.push({
21486
21759
  start: stmtStart,
21487
21760
  end: stmtEnd,
21488
- replacement: lines.join("\n")
21761
+ replacement: lines2.join("\n")
21489
21762
  });
21490
21763
  markUsedImportedFactories(compiledRules.importedFactory ? [compiledRules.importedFactory] : void 0);
21491
21764
  }
@@ -21645,12 +21918,12 @@ ${free.map(where).join("\n")}${cause}`
21645
21918
  setBuilderImportResolver(null);
21646
21919
  const degradations = endDegradationCapture();
21647
21920
  if (degradations.length > 0) {
21648
- const lines = formatDegradations(degradations);
21921
+ const lines2 = formatDegradations(degradations);
21649
21922
  if (resolveDegradationLevel() === "error") {
21650
21923
  throw new Error(`parseman: ${degradations.length} degraded compilation path(s) in ${id}
21651
- ${lines.join("\n")}`);
21924
+ ${lines2.join("\n")}`);
21652
21925
  }
21653
- for (const l of lines) warnings.push(`${id}: ${l}`);
21926
+ for (const l of lines2) warnings.push(`${id}: ${l}`);
21654
21927
  }
21655
21928
  return {
21656
21929
  code: ms.toString(),