parseman 0.55.4 → 0.57.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 (125) 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 +3745 -3180
  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 +2182 -1617
  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/node.d.ts +1 -1
  17. package/dist/combinators/trivia-skip.d.ts +1 -0
  18. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  19. package/dist/compiler/build-arity.d.ts +1 -1
  20. package/dist/compiler/build-arity.d.ts.map +1 -1
  21. package/dist/compiler/fields.d.ts.map +1 -1
  22. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  23. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  24. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  25. package/dist/compiler/token-capability.js +25 -0
  26. package/dist/compiler/token-capability.js.map +1 -1
  27. package/dist/coverage.d.ts.map +1 -1
  28. package/dist/cst/reflection.d.ts.map +1 -1
  29. package/dist/cst/trivia-kinds.d.ts +9 -1
  30. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2348 -1697
  34. package/dist/index.js.map +3 -3
  35. package/dist/language-service/index.js +3 -0
  36. package/dist/language-service/index.js.map +1 -1
  37. package/dist/parse-context.d.ts.map +1 -1
  38. package/dist/plugin/entry-supercompiler.d.ts +1 -1
  39. package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
  40. package/dist/plugin/evaluator.d.ts.map +1 -1
  41. package/dist/plugin/index.d.ts +27 -1
  42. package/dist/plugin/index.d.ts.map +1 -1
  43. package/dist/plugin/index.js +2360 -1314
  44. package/dist/plugin/index.js.map +3 -3
  45. package/dist/plugin/ir-eval.d.ts +2 -4
  46. package/dist/plugin/ir-eval.d.ts.map +1 -1
  47. package/dist/regex/classes.d.ts.map +1 -1
  48. package/dist/run/index.js +3 -0
  49. package/dist/run/index.js.map +1 -1
  50. package/dist/spec/index.js +15 -0
  51. package/dist/spec/index.js.map +1 -1
  52. package/dist/spec/model.d.ts.map +1 -1
  53. package/dist/table/assemble.d.ts +9 -65
  54. package/dist/table/assemble.d.ts.map +1 -1
  55. package/dist/table/child-slots.d.ts +4 -6
  56. package/dist/table/child-slots.d.ts.map +1 -1
  57. package/dist/table/compile-rule-map.d.ts +14 -2
  58. package/dist/table/compile-rule-map.d.ts.map +1 -1
  59. package/dist/table/emit-assembly.d.ts +17 -2
  60. package/dist/table/emit-assembly.d.ts.map +1 -1
  61. package/dist/table/emit.d.ts +5 -1
  62. package/dist/table/emit.d.ts.map +1 -1
  63. package/dist/table/encode.d.ts.map +1 -1
  64. package/dist/table/exec-baseline.d.ts.map +1 -1
  65. package/dist/table/exec.d.ts.map +1 -1
  66. package/dist/table/index.js +3884 -3262
  67. package/dist/table/index.js.map +3 -3
  68. package/dist/table/line-scan.d.ts +9 -0
  69. package/dist/table/line-scan.d.ts.map +1 -0
  70. package/dist/table/ops.d.ts +9 -6
  71. package/dist/table/ops.d.ts.map +1 -1
  72. package/dist/table/program.d.ts +24 -2
  73. package/dist/table/program.d.ts.map +1 -1
  74. package/dist/table/select-hot.d.ts +6 -0
  75. package/dist/table/select-hot.d.ts.map +1 -0
  76. package/dist/table/trivia-gap.d.ts +31 -0
  77. package/dist/table/trivia-gap.d.ts.map +1 -0
  78. package/dist/types.d.ts +38 -0
  79. package/dist/types.d.ts.map +1 -1
  80. package/dist/version.d.ts +1 -1
  81. package/package.json +4 -1
  82. package/src/analysis/choice-cost.ts +1 -0
  83. package/src/analysis/commitment.ts +13 -0
  84. package/src/analysis/duplication.ts +3 -0
  85. package/src/analysis/gating.ts +5 -0
  86. package/src/analysis/rebuild.ts +2 -2
  87. package/src/combinators/attempt.ts +2 -0
  88. package/src/combinators/choice.ts +3 -0
  89. package/src/combinators/dispatch.ts +3 -0
  90. package/src/combinators/expect.ts +5 -0
  91. package/src/combinators/first-set.ts +9 -1
  92. package/src/combinators/lines.ts +244 -0
  93. package/src/combinators/node.ts +6 -13
  94. package/src/combinators/trivia-skip.ts +48 -3
  95. package/src/compiler/build-arity.ts +3 -3
  96. package/src/compiler/fields.ts +3 -0
  97. package/src/compiler/grammar-coverage-ids.ts +1 -0
  98. package/src/compiler/ir-serialize.ts +10 -0
  99. package/src/compiler/token-alphabet.ts +3 -1
  100. package/src/coverage.ts +6 -0
  101. package/src/cst/reflection.ts +3 -0
  102. package/src/cst/trivia-kinds.ts +12 -1
  103. package/src/index.ts +2 -0
  104. package/src/parse-context.ts +3 -0
  105. package/src/plugin/entry-supercompiler.ts +8 -9
  106. package/src/plugin/evaluator.ts +4 -0
  107. package/src/plugin/index.ts +138 -13
  108. package/src/plugin/ir-eval.ts +10 -1
  109. package/src/regex/classes.ts +3 -0
  110. package/src/spec/model.ts +15 -0
  111. package/src/table/assemble.ts +125 -33
  112. package/src/table/child-slots.ts +4 -6
  113. package/src/table/compile-rule-map.ts +55 -15
  114. package/src/table/emit-assembly.ts +139 -26
  115. package/src/table/emit.ts +13 -11
  116. package/src/table/encode.ts +87 -27
  117. package/src/table/exec-baseline.ts +1 -3
  118. package/src/table/exec.ts +10 -6
  119. package/src/table/line-scan.ts +19 -0
  120. package/src/table/ops.ts +9 -6
  121. package/src/table/program.ts +41 -2
  122. package/src/table/select-hot.ts +70 -0
  123. package/src/table/trivia-gap.ts +109 -0
  124. package/src/types.ts +32 -0
  125. package/src/version.ts +1 -1
package/dist/cli/index.js CHANGED
@@ -163,6 +163,11 @@ function matchesEmptyBody(p, seen, resolve2) {
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, resolve2, activeTrivia) {
277
282
  resolve2,
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() : fs(opening);
289
+ }
280
290
  default:
281
291
  return p._meta.firstSet;
282
292
  }
@@ -326,6 +336,10 @@ function childrenOf(d) {
326
336
  return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
327
337
  case "routed":
328
338
  return d.fallback ? [d.fallback] : [];
339
+ case "lines":
340
+ return [d.prefix, d.body, ...d.first === void 0 ? [] : [d.first], ...d.lazy === void 0 ? [] : [d.lazy]];
341
+ case "columns":
342
+ return [];
329
343
  // A `lazy` is a REFERENCE, not a subtree. Descending through it would make
330
344
  // every rule's walk cover the whole reachable grammar — site paths become
331
345
  // nonsense, and a structural hash includes half the grammar. Treated as a
@@ -417,6 +431,9 @@ function classifyBroadArm(arm, resolve2) {
417
431
  return walk(d.parser);
418
432
  case "scanTo":
419
433
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
434
+ // A run opens with its first line's prefix.
435
+ case "lines":
436
+ return walk(d.first ?? d.prefix);
420
437
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
421
438
  // its own — it is the poison. `withCtx`/`recover` FORWARD their inner
422
439
  // first-set (see firstSetOf), so a broad result comes from the inner parser:
@@ -960,6 +977,7 @@ function literalCodePoints(frag) {
960
977
  if (ch2 === "\\") {
961
978
  const e = frag[i + 1];
962
979
  if (e === void 0) return null;
980
+ if (!(e in CLASS_ESCAPES) && /\w/.test(e)) return null;
963
981
  out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
964
982
  i += 2;
965
983
  continue;
@@ -1052,6 +1070,8 @@ function createParseContext() {
1052
1070
  _triviaCaptureMask: void 0,
1053
1071
  trackLines: false,
1054
1072
  state: void 0,
1073
+ lineView: void 0,
1074
+ _columnsSplit: void 0,
1055
1075
  build: void 0,
1056
1076
  _errors: void 0,
1057
1077
  _tolerant: void 0,
@@ -1070,6 +1090,7 @@ function createParseContext() {
1070
1090
  _rootTriviaKindIndex: void 0,
1071
1091
  _rootTriviaStrictScopes: void 0,
1072
1092
  _rootTriviaCapture: void 0,
1093
+ _gap: void 0,
1073
1094
  _cstTriviaLog: void 0,
1074
1095
  _fields: void 0,
1075
1096
  _cstBuf: void 0,
@@ -1552,7 +1573,10 @@ function labeledTriviaSpec(arms, minRepeats) {
1552
1573
  arms,
1553
1574
  minRepeats,
1554
1575
  visitor: commonLabeledTriviaVisitor({ arms, minRepeats }),
1555
- charArms: charArmsFor({ arms, minRepeats })
1576
+ charArms: charArmsFor({ arms, minRepeats }),
1577
+ rootKinds: void 0,
1578
+ rootLeaders: [],
1579
+ rootScan: null
1556
1580
  };
1557
1581
  }
1558
1582
  function matchArmAt(input, pos, arm, state) {
@@ -1773,11 +1797,12 @@ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1773
1797
  return result;
1774
1798
  }
1775
1799
  function saveLookaheadMark(ctx) {
1776
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1800
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null, split: ctx._columnsSplit };
1777
1801
  }
1778
1802
  function rollbackLookahead(ctx, mark) {
1779
1803
  rollbackTrivia(ctx, mark);
1780
1804
  if (ctx._probe) ctx._probe.best = mark.probeBest;
1805
+ ctx._columnsSplit = mark.split;
1781
1806
  }
1782
1807
  function scanWithLabels(input, cur, ctx) {
1783
1808
  const triviaP = ctx.trivia;
@@ -2011,8 +2036,50 @@ function loopScanner(arms) {
2011
2036
  function regexTriviaScanner(parser2) {
2012
2037
  const d = parser2._def.tag === "label" ? parser2._def.parser._def : parser2._def;
2013
2038
  if (d.tag !== "regex" || d.flags) return null;
2014
- const source = d.source;
2015
- return classRunSource(source) ?? altStarSource(source) ?? prefixRunSource(source) ?? delimitedSource(source);
2039
+ return sourceScanner(d.source);
2040
+ }
2041
+ function sourceScanner(source) {
2042
+ return classRunSource(source) ?? altStarSource(source) ?? prefixRunSource(source) ?? delimitedSource(source) ?? sharedPrefixAltSource(source);
2043
+ }
2044
+ function sharedPrefixAltSource(source) {
2045
+ const m = /^((?:\\.|[^\\[\]()*+?|^$.{}])+)\(\?:(.*)\)$/.exec(source);
2046
+ if (!m) return null;
2047
+ const alts = [];
2048
+ const body = m[2];
2049
+ let depth = 0;
2050
+ let start = 0;
2051
+ for (let i = 0; i < body.length; i++) {
2052
+ const c = body[i];
2053
+ if (c === "\\") {
2054
+ i++;
2055
+ continue;
2056
+ }
2057
+ if (c === "[") {
2058
+ while (i < body.length && body[i] !== "]") i += body[i] === "\\" ? 2 : 1;
2059
+ continue;
2060
+ }
2061
+ if (c === "(") depth++;
2062
+ else if (c === ")" && --depth < 0) return null;
2063
+ else if (c === "|" && depth === 0) {
2064
+ alts.push(body.slice(start, i));
2065
+ start = i + 1;
2066
+ }
2067
+ }
2068
+ if (depth !== 0 || alts.length === 0) return null;
2069
+ alts.push(body.slice(start));
2070
+ const scanners = [];
2071
+ for (const alt of alts) {
2072
+ const scanner = sourceScanner(m[1] + alt);
2073
+ if (scanner === null) return null;
2074
+ scanners.push(scanner);
2075
+ }
2076
+ return (input, cur) => {
2077
+ for (const scanner of scanners) {
2078
+ const end = scanner(input, cur);
2079
+ if (end > cur) return end;
2080
+ }
2081
+ return cur;
2082
+ };
2016
2083
  }
2017
2084
  function bmpString(cps) {
2018
2085
  if (!cps) return null;
@@ -2295,6 +2362,9 @@ function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
2295
2362
  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);
2296
2363
  case "recover":
2297
2364
  return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
2365
+ // The body parses its own input, so only the checks on this one can reuse a routed head.
2366
+ case "lines":
2367
+ return [def.prefix, def.first, def.lazy].some((entry) => entry !== void 0 && parserUsesRouted(entry, seen));
2298
2368
  case "lazy":
2299
2369
  try {
2300
2370
  return parserUsesRouted(def.thunk(), seen);
@@ -2485,6 +2555,12 @@ function derive(c, seen) {
2485
2555
  return deriveExpected2(def.parser);
2486
2556
  case "expect":
2487
2557
  return def.expected;
2558
+ case "lines": {
2559
+ const opening = def.first ?? def.prefix;
2560
+ return matchesEmpty(opening) ? [...deriveExpected2(opening), ...deriveExpected2(def.body)] : deriveExpected2(opening);
2561
+ }
2562
+ case "columns":
2563
+ return [`columns(${def.n})`];
2488
2564
  case "node":
2489
2565
  case "grammar":
2490
2566
  case "trivia":
@@ -3087,6 +3163,9 @@ function childrenOf2(def) {
3087
3163
  ];
3088
3164
  case "routed":
3089
3165
  return def.fallback ? [def.fallback] : [];
3166
+ case "lines":
3167
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
3168
+ case "columns":
3090
3169
  case "literal":
3091
3170
  case "regex":
3092
3171
  case "keywords":
@@ -3479,7 +3558,11 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set(), triviaActiv
3479
3558
  case "leaf":
3480
3559
  case "scanTo":
3481
3560
  case "unknown":
3561
+ case "columns":
3482
3562
  return false;
3563
+ // A run's prefixes are probed detached; only its body can capture.
3564
+ case "lines":
3565
+ return mayLeavePartialCapture(d.body, seen, triviaActive);
3483
3566
  // node() captures into its own private buffers and rolls them back on
3484
3567
  // failure (emitNode restores _ctx.* and never pushes on the !ok path).
3485
3568
  case "node":
@@ -3599,6 +3682,10 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
3599
3682
  return capturesLeaf(d.parser, seen);
3600
3683
  case "scanTo":
3601
3684
  return true;
3685
+ case "lines":
3686
+ return capturesLeaf(d.body, seen);
3687
+ case "columns":
3688
+ return true;
3602
3689
  case "lazy": {
3603
3690
  try {
3604
3691
  return capturesLeaf(d.thunk(), seen);
@@ -3646,6 +3733,8 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
3646
3733
  return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
3647
3734
  case "scanTo":
3648
3735
  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);
3736
+ case "lines":
3737
+ return hasNodeDef(d.body, seen);
3649
3738
  case "recover":
3650
3739
  return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
3651
3740
  case "expect":
@@ -3695,6 +3784,8 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
3695
3784
  return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
3696
3785
  case "scanTo":
3697
3786
  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);
3787
+ case "lines":
3788
+ return hasDirectBuildDef(d.body, seen);
3698
3789
  case "recover":
3699
3790
  return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
3700
3791
  case "routed":
@@ -3740,6 +3831,9 @@ function commitFailureSuccessors(p, resolve2) {
3740
3831
  ...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
3741
3832
  ...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
3742
3833
  ];
3834
+ // A run's prefixes are probed detached; a committed failure reaches it only from its body.
3835
+ case "lines":
3836
+ return [d.body];
3743
3837
  case "lazy": {
3744
3838
  try {
3745
3839
  return [d.thunk()];
@@ -3945,9 +4039,11 @@ function choice(...args) {
3945
4039
  for (let i = 0; i < parsers.length; i++) {
3946
4040
  if (gates[i] && !gates[i](ctx.state)) continue;
3947
4041
  const mark = saveTriviaMark(ctx);
4042
+ const split = ctx._columnsSplit;
3948
4043
  const result = parsers[i].parse(input, pos, ctx);
3949
4044
  if (!result.ok) {
3950
4045
  rollbackTrivia(ctx, mark);
4046
+ ctx._columnsSplit = split;
3951
4047
  const at = result.span.start;
3952
4048
  if (at > expectedAt) {
3953
4049
  expectedAt = at;
@@ -4147,665 +4243,671 @@ function buildAsciiDispatch(parsers) {
4147
4243
  return table;
4148
4244
  }
4149
4245
 
4150
- // src/compiler/degradation.ts
4151
- function formatDegradation(d) {
4152
- return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
4246
+ // src/combinators/field-trivia.ts
4247
+ var LEAD = { at: -1 };
4248
+ var FIELD_LEADING_TRIVIA = 1;
4249
+ var FIELD_TRAILING_TRIVIA = 2;
4250
+ function triviaFieldCapture(name, value, input, start, end, leadFrom, trailEnd) {
4251
+ return {
4252
+ name,
4253
+ value,
4254
+ span: { start, end },
4255
+ leading: leadFrom >= 0 && leadFrom < start ? input.slice(leadFrom, start) : "",
4256
+ trailing: trailEnd > end ? input.slice(end, trailEnd) : ""
4257
+ };
4153
4258
  }
4154
- var DETAIL_CAP = 8;
4155
- function formatDegradations(list) {
4156
- const byCode = /* @__PURE__ */ new Map();
4157
- for (const d of list) {
4158
- const bucket = byCode.get(d.code);
4159
- if (bucket) bucket.push(d);
4160
- else byCode.set(d.code, [d]);
4161
- }
4162
- const out = [];
4163
- for (const [code, items] of byCode) {
4164
- for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
4165
- if (items.length > DETAIL_CAP) {
4166
- out.push(
4167
- `[parseman] degraded [${code}] +${items.length - DETAIL_CAP} more site(s) not listed (${items.length} total). Set PARSEMAN_DEGRADATION=error to fail the build on these.`
4168
- );
4259
+ function leadsTrivia(p) {
4260
+ return p._def.tag === "field" && p._def.leadingTrivia === true;
4261
+ }
4262
+
4263
+ // src/combinators/repeat.ts
4264
+ function repItem(combinator, input, cur, ctx, guardable, mandatory, rollbackNeeded) {
4265
+ const mark = rollbackNeeded ? saveTriviaMark(ctx) : void 0;
4266
+ let pos = cur;
4267
+ if (ctx.trivia) {
4268
+ if (needsDeferredTriviaCommit(ctx)) {
4269
+ pos = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
4270
+ } else {
4271
+ pos = advanceTrivia(input, cur, ctx);
4169
4272
  }
4170
4273
  }
4171
- return out;
4172
- }
4173
- var _sinks = [];
4174
- function beginDegradationCapture() {
4175
- _sinks.push(/* @__PURE__ */ new Map());
4176
- }
4177
- function degradationCaptureDepth() {
4178
- return _sinks.length;
4179
- }
4180
- function unwindDegradationCapture(depth) {
4181
- const found = [];
4182
- while (_sinks.length > depth) {
4183
- const sink = _sinks.pop();
4184
- found.unshift(...sink.values());
4274
+ if (pos >= input.length && !mandatory) {
4275
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4276
+ return "stop";
4185
4277
  }
4186
- return found;
4187
- }
4188
- function resolveDegradationLevel(explicit) {
4189
- if (explicit !== void 0) return explicit;
4190
- const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
4191
- if (env === "off" || env === "warn" || env === "error") return env;
4192
- return "warn";
4278
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
4279
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4280
+ return "stop";
4281
+ }
4282
+ const result = combinator.parse(input, pos, ctx);
4283
+ if (!result.ok) {
4284
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4285
+ if (result.committed) return { fail: result, failPos: pos };
4286
+ return { fail: result, failPos: pos };
4287
+ }
4288
+ if (result.span.end === pos && !mandatory) {
4289
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4290
+ return "stop";
4291
+ }
4292
+ return result;
4193
4293
  }
4194
- function degradationKey(d) {
4195
- return JSON.stringify([d.code, d.where, d.subject]);
4294
+ function resolveBounds(what, opts) {
4295
+ const min = opts.min ?? 0;
4296
+ const max = opts.max ?? Infinity;
4297
+ const bad = (msg) => {
4298
+ throw new RangeError(`parseman: ${what} ${msg}`);
4299
+ };
4300
+ if (!Number.isInteger(min) || min < 0) bad(`min must be a non-negative integer (got ${String(opts.min)})`);
4301
+ if (max !== Infinity && (!Number.isInteger(max) || max < 1)) bad(`max must be a positive integer (got ${String(opts.max)})`);
4302
+ if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
4303
+ return { min, max };
4196
4304
  }
4197
- function recordDegradation(d) {
4198
- const level = resolveDegradationLevel();
4199
- if (level === "off") return;
4200
- const key = degradationKey(d);
4201
- const sink = _sinks[_sinks.length - 1];
4202
- if (sink) {
4203
- if (!sink.has(key)) sink.set(key, d);
4204
- return;
4305
+ function repeatScalarTail(combinator, child, input, cur, ctx, guardable, values, remaining) {
4306
+ while (cur < input.length && remaining > 0) {
4307
+ let itemPos = cur;
4308
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
4309
+ if (itemPos >= input.length) break;
4310
+ if (guardable && !startsFirstSet(combinator, input, itemPos)) break;
4311
+ const end = child(input, itemPos, ctx);
4312
+ if (end <= itemPos) break;
4313
+ if (values !== void 0) values.push(ctx._sv);
4314
+ cur = end;
4315
+ remaining--;
4205
4316
  }
4206
- if (_immediate.has(key)) return;
4207
- _immediate.add(key);
4208
- if (level === "error") throw new Error(`parseman: degraded compilation path
4209
- ${formatDegradation(d)}`);
4210
- console.warn(formatDegradation(d));
4317
+ ctx._sv = values;
4318
+ return cur;
4211
4319
  }
4212
- var _immediate = /* @__PURE__ */ new Set();
4213
- function beginCompileDegradationDrain() {
4214
- if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
4320
+ function many(combinator, opts = {}) {
4321
+ const { min, max } = resolveBounds("many()", opts);
4322
+ if (min >= 1) return atLeast(combinator, min, max);
4323
+ const meta = {
4324
+ firstSet: combinator._meta.firstSet,
4325
+ canMatchNewline: combinator._meta.canMatchNewline,
4326
+ isTrivia: false
4215
4327
  };
4216
- const depth = degradationCaptureDepth();
4217
- beginDegradationCapture();
4218
- return (report) => {
4219
- const found = unwindDegradationCapture(depth);
4220
- if (!report || found.length === 0) return;
4221
- const fresh = found.filter((d) => {
4222
- const k = degradationKey(d);
4223
- if (_immediate.has(k)) return false;
4224
- _immediate.add(k);
4225
- return true;
4226
- });
4227
- if (fresh.length === 0) return;
4228
- const lines = formatDegradations(fresh);
4229
- if (resolveDegradationLevel() === "error") {
4230
- throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
4231
- ${lines.join("\n")}`);
4328
+ const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
4329
+ let expected;
4330
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4331
+ const scalarChild = scalarOf(combinator);
4332
+ const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
4333
+ return {
4334
+ _tag: "many",
4335
+ _meta: meta,
4336
+ _def: def,
4337
+ _parseScalar: parseScalar,
4338
+ parse(input, pos, ctx) {
4339
+ const values = def.valueUnused ? void 0 : [];
4340
+ const rollbackNeeded = needsTriviaRollback(ctx);
4341
+ let cur = pos;
4342
+ let count = 0;
4343
+ while (cur < input.length) {
4344
+ if (count >= max) break;
4345
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
4346
+ if (item === "stop") break;
4347
+ if ("fail" in item) {
4348
+ if (!item.fail.ok && item.fail.committed) return item.fail;
4349
+ const sync = ctx._tolerant ? ctx._sync : void 0;
4350
+ if (sync === void 0) break;
4351
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
4352
+ expected ??= deriveExpected(combinator);
4353
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
4354
+ if (values !== void 0) values.push(error);
4355
+ captureError(ctx, error);
4356
+ count++;
4357
+ cur = end;
4358
+ continue;
4359
+ }
4360
+ if (values !== void 0) values.push(item.value);
4361
+ count++;
4362
+ cur = item.span.end;
4363
+ }
4364
+ return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
4232
4365
  }
4233
- for (const l of lines) console.warn(l);
4234
4366
  };
4235
4367
  }
4236
-
4237
- // src/compiler/build-arity.ts
4238
- function cstOutputHost(build) {
4239
- return build?._parsemanCstOutput === true;
4240
- }
4241
- var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
4242
- var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
4243
- var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
4244
- var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
4245
- function confirmedBuildArity(src) {
4246
- const s = src.trim();
4247
- if (NATIVE_CODE_RE.test(s)) return null;
4248
- if (/\barguments\b/.test(s)) return null;
4249
- if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
4250
- const inner = paramListText(s);
4251
- if (inner === null) return null;
4252
- if (inner.trim() === "") return 0;
4253
- const parts = splitTopLevel(inner);
4254
- if (parts === null) return null;
4255
- for (const part of parts) {
4256
- if (!isConfirmableParam(part.trim())) return null;
4257
- }
4258
- return parts.length;
4259
- }
4260
- function paramListText(s) {
4261
- const open = PARAM_LIST_OPEN_RE.exec(s);
4262
- if (!open) return null;
4263
- const start = open[0].length;
4264
- const end = matchingParen(s, start);
4265
- if (end === null) return null;
4266
- if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
4267
- return s.slice(start, end);
4368
+ function oneOrMore(combinator, opts = {}) {
4369
+ return many(combinator, { ...opts, min: opts.min ?? 1 });
4268
4370
  }
4269
- function matchingParen(s, start) {
4270
- let depth = 0;
4271
- for (let i = start; i < s.length; i++) {
4272
- const c = s[i];
4273
- if (c === '"' || c === "'" || c === "`") {
4274
- const close = skipString(s, i);
4275
- if (close === null) return null;
4276
- i = close;
4277
- continue;
4278
- }
4279
- if (c === "(" || c === "[" || c === "{") depth++;
4280
- else if (c === "]" || c === "}") {
4281
- if (--depth < 0) return null;
4282
- } else if (c === ")") {
4283
- if (depth === 0) return i;
4284
- depth--;
4371
+ function atLeast(combinator, min, max) {
4372
+ const meta = {
4373
+ firstSet: combinator._meta.firstSet,
4374
+ canMatchNewline: combinator._meta.canMatchNewline,
4375
+ isTrivia: false
4376
+ };
4377
+ const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
4378
+ let expected;
4379
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4380
+ const scalarChild = scalarOf(combinator);
4381
+ const parseScalar = (input, pos, ctx) => {
4382
+ let cur = scalarChild(input, pos, ctx);
4383
+ if (cur < 0) return cur;
4384
+ const values = def.valueUnused ? void 0 : [ctx._sv];
4385
+ for (let count = 1; count < min; count++) {
4386
+ let itemPos = cur;
4387
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
4388
+ const end = scalarChild(input, itemPos, ctx);
4389
+ if (end < 0) return ~cur;
4390
+ cur = end;
4391
+ if (values !== void 0) values.push(ctx._sv);
4285
4392
  }
4286
- }
4287
- return null;
4288
- }
4289
- function skipString(s, open) {
4290
- const quote = s[open];
4291
- for (let i = open + 1; i < s.length; i++) {
4292
- if (s[i] === "\\") {
4293
- i++;
4294
- continue;
4393
+ return repeatScalarTail(combinator, scalarChild, input, cur, ctx, guardable, values, max - min);
4394
+ };
4395
+ return {
4396
+ _tag: "oneOrMore",
4397
+ _meta: meta,
4398
+ _def: def,
4399
+ _parseScalar: parseScalar,
4400
+ parse(input, pos, ctx) {
4401
+ const first = combinator.parse(input, pos, ctx);
4402
+ if (!first.ok) return first;
4403
+ const values = def.valueUnused ? void 0 : [first.value];
4404
+ const rollbackNeeded = needsTriviaRollback(ctx);
4405
+ let cur = first.span.end;
4406
+ let count = 1;
4407
+ while (count < min) {
4408
+ const item = repItem(combinator, input, cur, ctx, guardable, true, rollbackNeeded);
4409
+ if (item === "stop" || "fail" in item) {
4410
+ if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
4411
+ expected ??= deriveExpected(combinator);
4412
+ return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
4413
+ }
4414
+ if (values !== void 0) values.push(item.value);
4415
+ cur = item.span.end;
4416
+ count++;
4417
+ }
4418
+ while (cur < input.length) {
4419
+ if (count >= max) break;
4420
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
4421
+ if (item === "stop") break;
4422
+ if ("fail" in item) {
4423
+ if (!item.fail.ok && item.fail.committed) return item.fail;
4424
+ const sync = ctx._tolerant ? ctx._sync : void 0;
4425
+ if (sync === void 0) break;
4426
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
4427
+ expected ??= deriveExpected(combinator);
4428
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
4429
+ if (values !== void 0) values.push(error);
4430
+ captureError(ctx, error);
4431
+ count++;
4432
+ cur = end;
4433
+ continue;
4434
+ }
4435
+ if (values !== void 0) values.push(item.value);
4436
+ count++;
4437
+ cur = item.span.end;
4438
+ }
4439
+ return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
4295
4440
  }
4296
- if (s[i] === quote) return i;
4297
- }
4298
- return null;
4441
+ };
4299
4442
  }
4300
- function splitTopLevel(inner) {
4301
- const parts = [];
4302
- let depth = 0;
4303
- let angle = 0;
4304
- let last = 0;
4305
- for (let i = 0; i < inner.length; i++) {
4306
- const c = inner[i];
4307
- if (c === "=" && inner[i + 1] === ">") {
4308
- i++;
4309
- continue;
4310
- }
4311
- if (c === '"' || c === "'" || c === "`") {
4312
- const close = skipString(inner, i);
4313
- if (close === null) return null;
4314
- i = close;
4315
- continue;
4443
+ function optional(combinator) {
4444
+ const meta = {
4445
+ firstSet: combinator._meta.firstSet,
4446
+ canMatchNewline: combinator._meta.canMatchNewline,
4447
+ isTrivia: false
4448
+ };
4449
+ const firstSetSkippable = !matchesEmpty(combinator);
4450
+ const scalarChild = scalarOf(combinator);
4451
+ const parseScalar = (input, pos, ctx) => {
4452
+ if (firstSetSkippable && !startsFirstSet(combinator, input, pos)) {
4453
+ ctx._sv = null;
4454
+ return pos;
4316
4455
  }
4317
- if (c === "(" || c === "[" || c === "{") depth++;
4318
- else if (c === ")" || c === "]" || c === "}") {
4319
- if (--depth < 0) return null;
4320
- } else if (c === "<") angle++;
4321
- else if (c === ">") {
4322
- if (--angle < 0) return null;
4323
- } else if (c === "," && depth === 0 && angle === 0) {
4324
- parts.push(inner.slice(last, i));
4325
- last = i + 1;
4456
+ const end = scalarChild(input, pos, ctx);
4457
+ if (end >= 0) return end;
4458
+ ctx._sv = null;
4459
+ return pos;
4460
+ };
4461
+ return {
4462
+ _tag: "optional",
4463
+ _meta: meta,
4464
+ _def: { tag: "optional", parser: combinator },
4465
+ _parseScalar: parseScalar,
4466
+ parse(input, pos, ctx) {
4467
+ if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
4468
+ return { ok: true, value: null, span: { start: pos, end: pos } };
4469
+ }
4470
+ const mark = saveTriviaMark(ctx);
4471
+ const result = combinator.parse(input, pos, ctx);
4472
+ if (result.ok) return result;
4473
+ rollbackTrivia(ctx, mark);
4474
+ if (result.committed) return result;
4475
+ return { ok: true, value: null, span: { start: pos, end: pos } };
4326
4476
  }
4327
- }
4328
- if (depth !== 0 || angle !== 0) return null;
4329
- parts.push(inner.slice(last));
4330
- return parts;
4477
+ };
4331
4478
  }
4332
- function isConfirmableParam(part) {
4333
- const head = PARAM_HEAD_RE.exec(part);
4334
- if (!head) return false;
4335
- const rest = part.slice(head[0].length);
4336
- if (rest === "") return true;
4337
- if (rest[0] !== ":") return false;
4338
- return !hasDefaultAssign(rest.slice(1));
4479
+ function unwrapSeparator(separator) {
4480
+ return "_keepSeparator" in separator ? { sep: separator._keepSeparator, keep: true } : { sep: separator, keep: false };
4339
4481
  }
4340
- function hasDefaultAssign(annotation) {
4341
- for (let i = 0; i < annotation.length; i++) {
4342
- if (annotation[i] !== "=") continue;
4343
- if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
4344
- i++;
4345
- continue;
4482
+ function sepBy(combinator, separatorArg, opts = {}) {
4483
+ const { sep: separator, keep: keepSeparators } = unwrapSeparator(separatorArg);
4484
+ const separatorLeads = leadsTrivia(separator);
4485
+ const { min, max } = resolveBounds("sepBy()", opts);
4486
+ const trailing = opts.trailing ?? "forbid";
4487
+ const meta = {
4488
+ firstSet: combinator._meta.firstSet,
4489
+ canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
4490
+ isTrivia: false
4491
+ };
4492
+ let expected;
4493
+ const failAt2 = (at) => {
4494
+ expected ??= deriveExpected(combinator);
4495
+ return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
4496
+ };
4497
+ const scalarExpected = deriveExpected(combinator);
4498
+ const scalarChild = scalarOf(combinator);
4499
+ const scalarSeparator = scalarOf(separator);
4500
+ const parseScalar = (input, pos, ctx) => {
4501
+ let cur = scalarChild(input, pos, ctx);
4502
+ if (cur < 0) {
4503
+ if (min >= 1) {
4504
+ ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
4505
+ return ~pos;
4506
+ }
4507
+ ctx._sv = [];
4508
+ return pos;
4346
4509
  }
4347
- const prev = annotation[i - 1];
4348
- if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
4349
- return true;
4350
- }
4351
- return false;
4352
- }
4353
- function buildAnalysisSrc(def) {
4354
- return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
4355
- }
4356
- var RESOLVER_REASONS = {
4357
- "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
4358
- "arguments": "its body references `arguments`",
4359
- "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
4360
- "not-a-function": "the binding it names is not a function declaration",
4361
- "unresolved-import": "its module could not be resolved or parsed",
4362
- "not-found": "the name did not resolve to any binding",
4363
- "computed": "it is a computed expression",
4364
- "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
4365
- "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
4366
- };
4367
- function unconfirmableReason(def, src) {
4368
- const declared = def.buildArityUnresolved;
4369
- if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
4370
- const s = src.trim();
4371
- const params = s.slice(0, s.indexOf(")") + 1);
4372
- if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
4373
- if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
4374
- if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
4375
- return "it names a binding this build could not resolve to a function declaration";
4376
- }
4377
- return "its parameter list could not be parsed";
4378
- }
4379
- function confirmedArityForDef(def) {
4380
- if (def.buildArity !== void 0) return def.buildArity;
4381
- const src = buildAnalysisSrc(def);
4382
- const arity = confirmedBuildArity(src);
4383
- if (arity !== null) return arity;
4384
- const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
4385
- recordDegradation({
4386
- code: "build-arity-unconfirmed",
4387
- severity: "warn",
4388
- where: `node("${def.type ?? "<inferred>"}")`,
4389
- subject,
4390
- fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and clones \`_ctx.state\` on every match`,
4391
- otherwise: "only the tiers the reducer actually declares would be captured (arity >= 1 children, >= 2 fields, >= 4 raw, >= 5 trivia, >= 6 state). Declare it with `node(..., { buildArity: n })` if it cannot be derived"
4392
- });
4393
- return null;
4394
- }
4395
- function buildReadsRaw(def) {
4396
- if (!def.build) return true;
4397
- if (def.buildRawUnused === true) return false;
4398
- const arity = confirmedArityForDef(def);
4399
- if (arity === null) return true;
4400
- return arity >= 4;
4401
- }
4402
- function buildReadsTrivia(def) {
4403
- if (!def.build) return true;
4404
- const arity = confirmedArityForDef(def);
4405
- if (arity === null) return true;
4406
- return arity >= 5;
4407
- }
4408
- function buildReadsState(def) {
4409
- if (!def.build) return true;
4410
- const arity = confirmedArityForDef(def);
4411
- if (arity === null) return true;
4412
- return arity >= 6;
4413
- }
4414
-
4415
- // src/compiler/fields.ts
4416
- function buildReadsFields(def) {
4417
- if (!def.build) return true;
4418
- const arity = confirmedArityForDef(def);
4419
- if (arity === null) return true;
4420
- return arity >= 2;
4421
- }
4422
- function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
4423
- if (seen.has(p)) return false;
4424
- seen.add(p);
4425
- const d = p._def;
4426
- switch (d.tag) {
4427
- case "field":
4428
- return true;
4429
- case "node":
4430
- return false;
4431
- case "lazy": {
4432
- try {
4433
- return parserHasOwnFields(d.thunk(), seen);
4434
- } catch {
4435
- return false;
4510
+ const values = [ctx._sv];
4511
+ while (values.length < max && (cur < input.length || values.length < min)) {
4512
+ const beforeSeparator = cur;
4513
+ let separatorPos = cur;
4514
+ if (ctx.trivia) separatorPos = advanceTrivia(input, cur, ctx);
4515
+ const separatorEnd = scalarSeparator(input, separatorPos, ctx);
4516
+ if (separatorEnd < 0) break;
4517
+ let itemPos = separatorEnd;
4518
+ if (ctx.trivia) itemPos = advanceTrivia(input, itemPos, ctx);
4519
+ const itemEnd = scalarChild(input, itemPos, ctx);
4520
+ if (itemEnd < 0) {
4521
+ cur = trailing === "allow" ? separatorEnd : beforeSeparator;
4522
+ break;
4436
4523
  }
4524
+ values.push(ctx._sv);
4525
+ cur = itemEnd;
4437
4526
  }
4438
- case "sequence":
4439
- case "choice":
4440
- return d.parsers.some((x) => parserHasOwnFields(x, seen));
4441
- case "dispatch":
4442
- return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => parserHasOwnFields(entry.parser, seen)) : false) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
4443
- case "sepBy":
4444
- return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
4445
- case "grammar":
4446
- return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
4447
- case "scanTo":
4448
- 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);
4449
- case "recover":
4450
- return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
4451
- case "routed":
4452
- return d.fallback ? parserHasOwnFields(d.fallback, seen) : false;
4453
- case "many":
4454
- case "oneOrMore":
4455
- case "optional":
4456
- case "attempt":
4457
- case "transform":
4458
- case "trivia":
4459
- case "token":
4460
- case "leaf":
4461
- case "label":
4462
- case "expect":
4463
- case "withCtx":
4464
- case "not":
4465
- case "peek":
4466
- return parserHasOwnFields(d.parser, seen);
4467
- default:
4468
- return false;
4469
- }
4470
- }
4471
- function buildFieldMap(captures) {
4472
- if (!captures?.length) return void 0;
4473
- const out = {};
4474
- for (const cap of captures) {
4475
- const entry = cap.leading === void 0 ? { value: cap.value, span: cap.span } : { value: cap.value, span: cap.span, leading: cap.leading, trailing: cap.trailing };
4476
- const current = out[cap.name];
4477
- if (current === void 0) out[cap.name] = entry;
4478
- else if (Array.isArray(current)) current.push(entry);
4479
- else out[cap.name] = [current, entry];
4480
- }
4481
- return out;
4482
- }
4483
-
4484
- // src/combinators/field-trivia.ts
4485
- var LEAD = { at: -1 };
4486
- var FIELD_LEADING_TRIVIA = 1;
4487
- var FIELD_TRAILING_TRIVIA = 2;
4488
- function triviaFieldCapture(name, value, input, start, end, leadFrom, trailEnd) {
4527
+ if (values.length < min) {
4528
+ ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
4529
+ return ~cur;
4530
+ }
4531
+ ctx._sv = values;
4532
+ return cur;
4533
+ };
4489
4534
  return {
4490
- name,
4491
- value,
4492
- span: { start, end },
4493
- leading: leadFrom >= 0 && leadFrom < start ? input.slice(leadFrom, start) : "",
4494
- trailing: trailEnd > end ? input.slice(end, trailEnd) : ""
4535
+ _tag: "sepBy",
4536
+ _meta: meta,
4537
+ _def: {
4538
+ tag: "sepBy",
4539
+ parser: combinator,
4540
+ separator,
4541
+ min,
4542
+ ...max === Infinity ? {} : { max },
4543
+ ...trailing === "forbid" ? {} : { trailing },
4544
+ // Carried into the IR so the COMPILED engine demotes exactly where the
4545
+ // interpreter does. The two engines have drifted three separate ways across
4546
+ // this option surface already; parity is held by test/parity, not by hope.
4547
+ ...keepSeparators ? { keepSeparators: true } : {}
4548
+ },
4549
+ _parseScalar: keepSeparators || separatorLeads ? void 0 : parseScalar,
4550
+ parse(input, pos, ctx) {
4551
+ const first = combinator.parse(input, pos, ctx);
4552
+ const values = [];
4553
+ let cur;
4554
+ if (first.ok) {
4555
+ values.push(first.value);
4556
+ cur = first.span.end;
4557
+ } else {
4558
+ if (first.committed) return first;
4559
+ const term = ctx._tolerant ? ctx._sync : void 0;
4560
+ if (term === void 0 || matchesAt(term, input, pos, ctx)) {
4561
+ if (min >= 1) return failAt2(pos);
4562
+ return { ok: true, value: [], span: { start: pos, end: pos } };
4563
+ }
4564
+ expected ??= deriveExpected(combinator);
4565
+ const rec = recoverScan(input, pos, ctx, orSentinel(separator, term), expected);
4566
+ values.push(rec.error);
4567
+ captureError(ctx, rec.error);
4568
+ cur = rec.end;
4569
+ }
4570
+ while (cur < input.length || values.length < min) {
4571
+ if (values.length >= max) break;
4572
+ const loopMark = saveTriviaMark(ctx);
4573
+ let sepPos = cur;
4574
+ if (ctx.trivia) {
4575
+ if (needsDeferredTriviaCommit(ctx)) {
4576
+ sepPos = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
4577
+ } else {
4578
+ sepPos = advanceTrivia(input, cur, ctx);
4579
+ }
4580
+ }
4581
+ if (separatorLeads) LEAD.at = cur;
4582
+ const sep = separator.parse(input, sepPos, ctx);
4583
+ if (!sep.ok) {
4584
+ rollbackTrivia(ctx, loopMark);
4585
+ if (sep.committed) return sep;
4586
+ break;
4587
+ }
4588
+ if (!keepSeparators) demoteCapturedToRaw(ctx, loopMark.leaves);
4589
+ const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
4590
+ let nextPos = sep.span.end;
4591
+ if (ctx.trivia) {
4592
+ if (needsDeferredTriviaCommit(ctx)) {
4593
+ nextPos = commitTriviaScan(scanTriviaCompact(input, sep.span.end, ctx));
4594
+ } else {
4595
+ nextPos = advanceTrivia(input, sep.span.end, ctx);
4596
+ }
4597
+ }
4598
+ const next = combinator.parse(input, nextPos, ctx);
4599
+ if (!next.ok) {
4600
+ if (next.committed) return next;
4601
+ const term = ctx._tolerant ? ctx._sync : void 0;
4602
+ if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
4603
+ expected ??= deriveExpected(combinator);
4604
+ const rec = recoverScan(input, nextPos, ctx, orSentinel(separator, term), expected);
4605
+ values.push(rec.error);
4606
+ captureError(ctx, rec.error);
4607
+ cur = rec.end;
4608
+ continue;
4609
+ }
4610
+ if (sepMark !== void 0) {
4611
+ rollbackTrivia(ctx, sepMark);
4612
+ cur = sep.span.end;
4613
+ break;
4614
+ }
4615
+ rollbackTrivia(ctx, loopMark);
4616
+ break;
4617
+ }
4618
+ values.push(next.value);
4619
+ cur = next.span.end;
4620
+ }
4621
+ if (values.length < min) return failAt2(cur);
4622
+ return { ok: true, value: values, span: { start: pos, end: cur } };
4623
+ }
4495
4624
  };
4496
4625
  }
4497
- function leadsTrivia(p) {
4498
- return p._def.tag === "field" && p._def.leadingTrivia === true;
4499
- }
4500
-
4501
- // src/table/ops.ts
4502
- var OP_LIT = 1;
4503
- var OP_RX = 2;
4504
- var OP_SEQ = 3;
4505
- var OP_SEQV = 4;
4506
- var OP_CHOICE = 5;
4507
- var OP_REP = 6;
4508
- var OP_REPV = 7;
4509
- var OP_OPT = 8;
4510
- var OP_XFORM = 9;
4511
- var OP_NODE = 10;
4512
- var OP_RULE = 11;
4513
- var OP_GATE = 12;
4514
- var OP_NOT = 13;
4515
- var OP_PEEK = 14;
4516
- var OP_LEAF = 15;
4517
- var OP_EMPTY = 16;
4518
- var OP_LIT_TRACK = 17;
4519
- var OP_RX_TRACK = 18;
4520
- var OP_NODE_TRACK = 19;
4521
- var OP_SCOPE = 20;
4522
- var OP_EXPECT = 21;
4523
- var OP_SEQX = 22;
4524
- var OP_SCAN = 23;
4525
- var OP_FIELD = 24;
4526
- var OP_LIT_CI = 27;
4527
- var OP_LIT_CI_TRACK = 28;
4528
- var OP_TOKEN = 29;
4529
- var OP_SCOPE_CAP = 30;
4530
- var OP_WITHCTX = 31;
4531
- var OP_GUARD = 32;
4532
- var OP_ADJ = 33;
4533
- var OP_GREEDY = 34;
4534
- var OP_REJECT = 35;
4535
- var OP_ARMGATE = 36;
4536
- var OP_LIVE = 37;
4537
- var OP_ATTEMPT = 38;
4538
- var OP_LABEL = 39;
4539
- var OP_COV = 40;
4540
- var OP_SCOPE_PLAIN = 41;
4541
- var OP_LEX_BODY = 42;
4542
- var OP_LEX_PROGRAM = 43;
4543
- var OP_SOURCE_LEAF = 44;
4544
- var OP_DISPATCH = 25;
4545
- var OP_ROUTED = 26;
4546
- var OP_NAMES = {
4547
- [OP_LIT]: "LIT",
4548
- [OP_RX]: "RX",
4549
- [OP_SEQ]: "SEQ",
4550
- [OP_SEQV]: "SEQV",
4551
- [OP_CHOICE]: "CHOICE",
4552
- [OP_REP]: "REP",
4553
- [OP_REPV]: "REPV",
4554
- [OP_OPT]: "OPT",
4555
- [OP_XFORM]: "XFORM",
4556
- [OP_NODE]: "NODE",
4557
- [OP_RULE]: "RULE",
4558
- [OP_GATE]: "GATE",
4559
- [OP_NOT]: "NOT",
4560
- [OP_PEEK]: "PEEK",
4561
- [OP_LEAF]: "LEAF",
4562
- [OP_SOURCE_LEAF]: "SOURCE_LEAF",
4563
- [OP_EMPTY]: "EMPTY",
4564
- [OP_LIT_TRACK]: "LIT_TRACK",
4565
- [OP_RX_TRACK]: "RX_TRACK",
4566
- [OP_NODE_TRACK]: "NODE_TRACK",
4567
- [OP_SCOPE]: "SCOPE",
4568
- [OP_EXPECT]: "EXPECT",
4569
- [OP_SEQX]: "SEQX",
4570
- [OP_SCAN]: "SCAN",
4571
- [OP_FIELD]: "FIELD",
4572
- [OP_LIT_CI]: "LIT_CI",
4573
- [OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
4574
- [OP_DISPATCH]: "DISPATCH",
4575
- [OP_ROUTED]: "ROUTED",
4576
- [OP_TOKEN]: "TOKEN",
4577
- [OP_SCOPE_CAP]: "SCOPE_CAP",
4578
- [OP_WITHCTX]: "WITHCTX",
4579
- [OP_GUARD]: "GUARD",
4580
- [OP_ADJ]: "ADJ",
4581
- [OP_GREEDY]: "GREEDY",
4582
- [OP_REJECT]: "REJECT",
4583
- [OP_ARMGATE]: "ARMGATE",
4584
- [OP_LIVE]: "LIVE",
4585
- [OP_ATTEMPT]: "ATTEMPT",
4586
- [OP_LABEL]: "LABEL",
4587
- [OP_COV]: "COV",
4588
- [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
4589
- [OP_LEX_BODY]: "LEX_BODY",
4590
- [OP_LEX_PROGRAM]: "LEX_PROGRAM"
4591
- };
4592
4626
 
4593
- // src/combinators/adjacency.ts
4594
- function adjacencyOf(p) {
4595
- return p._def.tag === "adjacency" ? p._def : null;
4596
- }
4597
- function adjacencyExpected(def) {
4598
- if (def.polarity === "adjacent") return "adjacent";
4599
- return def.kinds === void 0 ? "notAdjacent" : `notAdjacent(${def.kinds.join("|")})`;
4600
- }
4601
- function holdsAdjacency(input, cur, ctx, def) {
4602
- return adjacencyHolds(input, cur, ctx, def.polarity === "notAdjacent", def.kinds);
4603
- }
4604
- function adjacencyHolds(input, cur, ctx, negated, kinds) {
4605
- const end = probeTriviaEnd(input, cur, ctx);
4606
- if (!negated) return end === cur;
4607
- if (end === cur) return false;
4608
- if (kinds === void 0) return true;
4609
- const want = resolveAdjacencyKindMask(ctx.trivia, kinds);
4610
- const spec = analyzeLabeledTrivia(ctx.trivia);
4611
- return (triviaKindMaskAt(input, cur, spec, ctx.state) & want) !== 0;
4627
+ // src/combinators/map.ts
4628
+ var JSON_STRING_BODY = String.raw`(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*`;
4629
+ function transform(combinator, fn) {
4630
+ const observesSpan = fn.length > 1;
4631
+ const sequenceDef = combinator._def.tag === "sequence" ? combinator._def : void 0;
4632
+ const terms = sequenceDef?.parsers;
4633
+ const inner = terms?.[1];
4634
+ const fused = terms?.length === 3 && terms[0]._def.tag === "literal" && terms[0]._def.value === '"' && !terms[0]._def.caseInsensitive && inner._def.tag === "regex" && inner._def.source === JSON_STRING_BODY && inner._def.flags === "" && terms[2]._def.tag === "literal" && terms[2]._def.value === '"' && !terms[2]._def.caseInsensitive && inner._stickyRegex !== void 0;
4635
+ const child = scalarOf(combinator);
4636
+ const parseScalar = fused ? (() => {
4637
+ const body = inner._stickyRegex;
4638
+ const tuple = ['"', "", '"'];
4639
+ const expected = [JSON.stringify('"')];
4640
+ return (input, pos, ctx) => {
4641
+ if (ctx.trivia !== void 0) {
4642
+ const end2 = child(input, pos, ctx);
4643
+ if (end2 < 0) return end2;
4644
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end: end2 }) : fn(ctx._sv);
4645
+ return end2;
4646
+ }
4647
+ if (input.charCodeAt(pos) !== 34) {
4648
+ ctx._fx = expected;
4649
+ return ~pos;
4650
+ }
4651
+ body.lastIndex = pos + 1;
4652
+ const match = body.exec(input);
4653
+ const close = pos + 1 + match[0].length;
4654
+ if (input.charCodeAt(close) !== 34) {
4655
+ ctx._fx = expected;
4656
+ return ~close;
4657
+ }
4658
+ tuple[1] = match[0];
4659
+ const end = close + 1;
4660
+ ctx._sv = observesSpan ? fn(tuple, { start: pos, end }) : fn(tuple);
4661
+ return end;
4662
+ };
4663
+ })() : /* @__PURE__ */ (() => {
4664
+ return (input, pos, ctx) => {
4665
+ const end = child(input, pos, ctx);
4666
+ if (end < 0) return end;
4667
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end }) : fn(ctx._sv);
4668
+ return end;
4669
+ };
4670
+ })();
4671
+ return {
4672
+ _tag: "transform",
4673
+ _meta: combinator._meta,
4674
+ _def: { tag: "transform", parser: combinator, fn },
4675
+ _parseScalar: parseScalar,
4676
+ parse(input, pos, ctx) {
4677
+ const result = combinator.parse(input, pos, ctx);
4678
+ if (!result.ok) return result;
4679
+ return { ok: true, value: fn(result.value, result.span), span: result.span };
4680
+ }
4681
+ };
4612
4682
  }
4613
- function adjacencyFail(pos, def) {
4614
- return { ok: false, expected: [adjacencyExpected(def)], span: { start: pos, end: pos } };
4683
+ function trivia(combinator) {
4684
+ const labeled = analyzeLabeledTrivia(combinator);
4685
+ const kindLabels = labeled?.labels;
4686
+ return {
4687
+ _tag: combinator._tag,
4688
+ _meta: {
4689
+ ...combinator._meta,
4690
+ isTrivia: true,
4691
+ triviaScanner: fastTriviaScanner(combinator),
4692
+ // Every labeled scan of this trivia reads its spec; the module's four-slot
4693
+ // memo alone thrashes on a grammar with more than four trivia tables.
4694
+ labeledTriviaSpec: labeled,
4695
+ ...kindLabels ? { triviaKindLabels: kindLabels } : {}
4696
+ },
4697
+ _def: { tag: "trivia", parser: combinator },
4698
+ _parseScalar: scalarOf(combinator),
4699
+ parse: combinator.parse.bind(combinator)
4700
+ };
4615
4701
  }
4616
- var NO_STANDALONE_PARSE = "adjacency assertions are boundary tests: use adjacent()/notAdjacent() as a NON-FIRST term of a sequence(), where the preceding term defines the gap being asserted.";
4617
- function adjacencyMisuse(polarity) {
4618
- return new TypeError(`${polarity}(): ${NO_STANDALONE_PARSE}`);
4702
+ function classifiedTrivia(arms) {
4703
+ const entries = Object.entries(arms);
4704
+ if (entries.length === 0) {
4705
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
4706
+ }
4707
+ for (let i = 0; i < entries.length; i++) {
4708
+ const [name, arm] = entries[i];
4709
+ const first = arm._meta.firstSet;
4710
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
4711
+ throw new TypeError(
4712
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
4713
+ );
4714
+ }
4715
+ }
4716
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
4717
+ const result = trivia(oneOrMore(choice(...labeledArms)));
4718
+ result._meta.rootTriviaClassified = true;
4719
+ return result;
4619
4720
  }
4620
- function adjacencyCombinator(def) {
4621
- const meta = {
4622
- // Zero-width and never leading (sequence() rejects index 0), so it contributes
4623
- // no first char. `isZeroWidthAssertion` drops it from a sequence's first-set
4624
- // rather than letting `any` poison the enclosing choice's dispatch.
4625
- firstSet: { kind: "any" },
4626
- canMatchNewline: false,
4627
- isTrivia: false
4628
- };
4721
+ function label(name, combinator) {
4629
4722
  return {
4630
- _tag: "adjacency",
4631
- _meta: meta,
4632
- _def: def,
4633
- parse() {
4634
- throw adjacencyMisuse(def.polarity);
4723
+ _tag: combinator._tag,
4724
+ _meta: combinator._meta,
4725
+ _def: { tag: "label", label: name, parser: combinator },
4726
+ parse(input, pos, ctx) {
4727
+ const result = combinator.parse(input, pos, ctx);
4728
+ if (!result.ok) return { ok: false, expected: [name], span: result.span };
4729
+ return result;
4635
4730
  }
4636
4731
  };
4637
4732
  }
4638
- function adjacent() {
4639
- return adjacencyCombinator({ tag: "adjacency", polarity: "adjacent" });
4640
- }
4641
- function notAdjacent(options) {
4642
- const kinds = options?.kinds;
4643
- if (kinds !== void 0) {
4644
- if (!Array.isArray(kinds) || kinds.length === 0) {
4645
- throw new TypeError("notAdjacent({ kinds }): kinds must be a non-empty array of trivia category names.");
4646
- }
4647
- for (const k of kinds) {
4648
- if (typeof k !== "string" || k.length === 0) {
4649
- throw new TypeError(`notAdjacent({ kinds }): every kind must be a non-empty string, got ${JSON.stringify(k)}.`);
4733
+ function field(name, combinator, opts) {
4734
+ const leadingTrivia = opts?.leadingTrivia === true;
4735
+ const trailingTrivia = opts?.trailingTrivia === true;
4736
+ if (!leadingTrivia && !trailingTrivia) {
4737
+ return {
4738
+ _tag: combinator._tag,
4739
+ _meta: combinator._meta,
4740
+ _def: { tag: "field", name, parser: combinator },
4741
+ parse(input, pos, ctx) {
4742
+ const result = combinator.parse(input, pos, ctx);
4743
+ if (!result.ok) return result;
4744
+ ctx._fields?.push({ name, value: result.value, span: result.span });
4745
+ return result;
4650
4746
  }
4651
- }
4747
+ };
4652
4748
  }
4653
- return adjacencyCombinator(
4654
- kinds === void 0 ? { tag: "adjacency", polarity: "notAdjacent" } : { tag: "adjacency", polarity: "notAdjacent", kinds: [...kinds] }
4655
- );
4749
+ return {
4750
+ _tag: combinator._tag,
4751
+ _meta: combinator._meta,
4752
+ _def: {
4753
+ tag: "field",
4754
+ name,
4755
+ parser: combinator,
4756
+ ...leadingTrivia ? { leadingTrivia: true } : {},
4757
+ ...trailingTrivia ? { trailingTrivia: true } : {}
4758
+ },
4759
+ parse(input, pos, ctx) {
4760
+ const leadFrom = LEAD.at;
4761
+ LEAD.at = -1;
4762
+ const result = combinator.parse(input, pos, ctx);
4763
+ if (!result.ok) return result;
4764
+ const end = result.span.end;
4765
+ const trailEnd = trailingTrivia && ctx.trivia ? consumeTrivia(input, end, ctx) : end;
4766
+ ctx._fields?.push(triviaFieldCapture(name, result.value, input, result.span.start, end, leadingTrivia ? leadFrom : -1, trailEnd));
4767
+ return trailEnd === end ? result : { ok: true, value: result.value, span: { start: result.span.start, end: trailEnd } };
4768
+ }
4769
+ };
4656
4770
  }
4657
4771
 
4658
- // src/combinators/node.ts
4659
- function unwrapChild(child) {
4660
- return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
4661
- }
4662
- function isCstChild(value) {
4663
- return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "parseError");
4664
- }
4665
- function missingInferredType() {
4666
- throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
4667
- }
4668
- function normalizeBuildArity(arity) {
4669
- if (arity === void 0) return void 0;
4670
- if (!Number.isInteger(arity) || arity < 0 || arity > 6) {
4671
- throw new Error("node() buildArity must be an integer 0..6 (children, fields, span, rawChildren, triviaLog, state)");
4772
+ // src/combinators/regex.ts
4773
+ var SCAN_BAIL_AT = 64;
4774
+ function inRanges3(cp, ranges) {
4775
+ for (let i = 0; i < ranges.length; i++) {
4776
+ const [lo, hi] = ranges[i];
4777
+ if (cp >= lo && cp <= hi) return true;
4672
4778
  }
4673
- return arity;
4779
+ return false;
4674
4780
  }
4675
- function normalizeProject(project) {
4676
- if (project === void 0) return void 0;
4677
- if (!Number.isInteger(project) || project < 0) {
4678
- throw new Error("node() project child must be a non-negative integer");
4679
- }
4680
- return project;
4781
+ function readClassRanges(body) {
4782
+ if (body.startsWith("^")) return null;
4783
+ return parseClassRanges(body);
4681
4784
  }
4682
- function projectChild(children2, project, type) {
4683
- if (!(project in children2)) {
4684
- throw new Error(`node(${JSON.stringify(type)}) project child ${project} was not captured`);
4785
+ function shortScanner(source, flags) {
4786
+ if (/[imsuvy]/.test(flags)) return null;
4787
+ let ranges = null;
4788
+ let quant = "";
4789
+ if (source[0] === "[") {
4790
+ let end = 1;
4791
+ while (end < source.length && source[end] !== "]") {
4792
+ if (source[end] === "\\") end += 2;
4793
+ else end++;
4794
+ }
4795
+ if (source[end] !== "]") return null;
4796
+ ranges = readClassRanges(source.slice(1, end));
4797
+ quant = source.slice(end + 1);
4798
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
4799
+ ranges = shorthandRanges(source[1]);
4800
+ quant = source.slice(2);
4685
4801
  }
4686
- return unwrapChild(children2[project]);
4802
+ if (!ranges || quant !== "+" && quant !== "*") return null;
4803
+ const minOne = quant === "+";
4804
+ return (input, pos) => {
4805
+ let end = pos;
4806
+ while (end < input.length && inRanges3(input.charCodeAt(end), ranges)) {
4807
+ end++;
4808
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
4809
+ }
4810
+ return minOne && end === pos ? null : end;
4811
+ };
4687
4812
  }
4688
- function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
4689
- const hasExplicitType = typeof typeOrCombinator === "string";
4690
- const type = hasExplicitType ? typeOrCombinator : void 0;
4691
- const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
4692
- const buildArg = hasExplicitType ? buildOrOpts : combinatorOrBuild;
4693
- const build = typeof buildArg === "function" ? buildArg : void 0;
4694
- const opts = typeof buildArg === "function" ? maybeOpts : buildArg ?? maybeOpts;
4695
- const project = normalizeProject(opts?.project);
4696
- if (project !== void 0 && build !== void 0) {
4697
- throw new Error("node() options cannot combine project with a build callback");
4698
- }
4699
- const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
4700
- const meta = {
4701
- firstSet: combinator._meta.firstSet,
4702
- canMatchNewline: combinator._meta.canMatchNewline,
4703
- isTrivia: false
4813
+ function asciiCaseFold(fs) {
4814
+ if (fs.kind !== "ranges") return fs;
4815
+ const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
4816
+ const add = (lo, hi) => {
4817
+ if (lo <= hi) ranges.push({ lo, hi });
4704
4818
  };
4705
- const unwrap = opts?.unwrap === true;
4706
- const collapse = opts?.collapse === true;
4707
- const captureTrivia = opts?.captureTrivia === true;
4708
- const trailingTrivia = opts?.trailingTrivia === true;
4709
- const tags = opts?.tags;
4710
- const buildArity = normalizeBuildArity(opts?.buildArity);
4711
- if (unwrap && collapse) {
4712
- throw new Error("node() options cannot set both unwrap and collapse");
4819
+ for (const r of fs.ranges) {
4820
+ const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
4821
+ add(uLo + 32, uHi + 32);
4822
+ const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
4823
+ add(lLo - 32, lHi - 32);
4713
4824
  }
4714
- if (project !== void 0 && (unwrap || collapse)) {
4715
- throw new Error("node() options cannot combine project with unwrap or collapse");
4825
+ ranges.sort((a, b) => a.lo - b.lo);
4826
+ const merged = [];
4827
+ for (const r of ranges) {
4828
+ const last = merged[merged.length - 1];
4829
+ if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
4830
+ else merged.push({ lo: r.lo, hi: r.hi });
4716
4831
  }
4717
- const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || buildArity !== void 0 || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...buildArity !== void 0 ? { buildArity } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
4718
- const capturesTrivia = captureTrivia || (build ? buildReadsTrivia(def) : project === void 0);
4719
- const clonesState = build ? buildReadsState(def) : project === void 0;
4720
- const hasOwnFields = parserHasOwnFields(combinator);
4721
- const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
4722
- const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4723
- let failExpected;
4832
+ return { kind: "ranges", ranges: merged };
4833
+ }
4834
+ function regex(pattern, flags = "") {
4835
+ const source = typeof pattern === "string" ? pattern : pattern.source;
4836
+ const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
4837
+ const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
4838
+ const scan = shortScanner(source, resolvedFlags);
4839
+ const raw = firstSetFromRegex(source);
4840
+ const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
4841
+ const canMatchNewline = raw.canMatchNewline;
4842
+ const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
4843
+ const def = { tag: "regex", source, flags: resolvedFlags };
4844
+ const expected = directTerminalFailureExpected(def);
4845
+ const parseScalar = (input, pos, ctx) => {
4846
+ const scanEnd = scan?.(input, pos);
4847
+ if (scanEnd !== void 0) {
4848
+ if (scanEnd === null) {
4849
+ ctx._fx = expected;
4850
+ return ~pos;
4851
+ }
4852
+ ctx._sv = input.slice(pos, scanEnd);
4853
+ return scanEnd;
4854
+ }
4855
+ anchored.lastIndex = pos;
4856
+ const match = anchored.exec(input);
4857
+ if (match === null) {
4858
+ ctx._fx = expected;
4859
+ return ~pos;
4860
+ }
4861
+ ctx._sv = match[0];
4862
+ return pos + match[0].length;
4863
+ };
4724
4864
  return {
4725
- _tag: "node",
4865
+ _tag: "regex",
4726
4866
  _meta: meta,
4727
4867
  _def: def,
4868
+ _stickyRegex: anchored,
4869
+ _parseScalar: parseScalar,
4728
4870
  parse(input, pos, ctx) {
4729
- if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
4730
- if (failExpected === void 0) {
4731
- const e = deriveExpected(combinator);
4732
- failExpected = e.length > 0 ? e : [combinator._tag];
4733
- }
4734
- return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
4735
- }
4736
- const hasDirectValue = build !== void 0 || project !== void 0;
4737
- const hostCst = hasDirectValue && cstOutputHost(ctx.build);
4738
- const structural = !hasDirectValue;
4739
- const hostCapturesThisType = structural && def.type !== void 0 && ctx.build?._parsemanCaptureTrivia !== void 0 ? ctx.build._parsemanCaptureTrivia(def.type) : void 0;
4740
- const effTrivia = structural ? captureTrivia || trailingTrivia || hostCapturesThisType !== false : capturesTrivia || hostCst;
4741
- const effFields = capturesFields || hostCst && hasOwnFields;
4742
- const saved = beginCstNodeCapture(ctx);
4743
- const savedFields = ctx._fields;
4744
- ctx._fields = effFields ? [] : void 0;
4745
- const savedMask = ctx._triviaCaptureMask;
4746
- if (build === void 0 && project === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
4747
- ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
4748
- }
4749
- if (!effTrivia) ctx.captureTrivia = false;
4750
- let r = combinator.parse(input, pos, ctx);
4751
- if (r.ok && trailingTrivia && ctx.trivia) {
4752
- const end = consumeTrivia(input, r.span.end, ctx);
4753
- r = { ...r, span: { start: r.span.start, end } };
4754
- }
4755
- const fields = effFields ? buildFieldMap(ctx._fields) : void 0;
4756
- ctx._fields = savedFields;
4757
- ctx._triviaCaptureMask = savedMask;
4758
- const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
4759
- if (!r.ok) return r;
4760
- const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
4761
- const nodeType = def.type ?? missingInferredType();
4762
- const cstOutput = cstOutputHost(ctx.build);
4763
- const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
4764
- const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
4765
- const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : (cstOutput || !build && project === void 0) && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
4766
- const rawEntry2 = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
4767
- if (saved.buf !== void 0 || saved.ch !== void 0) {
4768
- pushCstChild(ctx, built, rawEntry2);
4769
- }
4770
- return { ok: true, value: built, span };
4771
- }
4772
- };
4773
- }
4774
-
4775
- // src/combinators/trivia-boundary.ts
4776
- var MAX_DEPTH = 12;
4777
- function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
4778
- if (depth === 0) return true;
4779
- const d = p._def;
4780
- switch (d.tag) {
4781
- // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
4782
- case "sequence":
4783
- return d.parsers.length > 1;
4784
- case "many":
4785
- case "oneOrMore":
4786
- case "sepBy":
4787
- return true;
4788
- // Transparent wrappers — they add no position, so ask what they wrap.
4789
- case "node":
4790
- case "transform":
4791
- case "leaf":
4792
- case "label":
4793
- case "field":
4794
- case "token":
4795
- case "attempt":
4796
- case "optional":
4797
- case "expect":
4798
- case "withCtx":
4799
- case "recover":
4800
- case "trivia":
4801
- return hasOwnTriviaBoundary(d.parser, depth - 1);
4802
- // An alternation adds no position either, but ONE arm that has one is enough
4803
- // to make the scope meaningful for this rule.
4804
- case "choice":
4805
- return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
4806
- default:
4807
- return false;
4871
+ const scanEnd = scan?.(input, pos);
4872
+ if (scanEnd !== void 0) {
4873
+ if (scanEnd === null) return failAt(ctx, directTerminalFailureExpected(def), pos);
4874
+ const value = input.slice(pos, scanEnd);
4875
+ const span2 = { start: pos, end: scanEnd };
4876
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
4877
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
4878
+ return { ok: true, value, span: span2 };
4879
+ }
4880
+ anchored.lastIndex = pos;
4881
+ const m = anchored.exec(input);
4882
+ if (m === null) {
4883
+ return failAt(ctx, directTerminalFailureExpected(def), pos);
4884
+ }
4885
+ const span = { start: pos, end: pos + m[0].length };
4886
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
4887
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
4888
+ return { ok: true, value: m[0], span };
4889
+ }
4890
+ };
4891
+ }
4892
+
4893
+ // src/table/lex-body.ts
4894
+ function buildLexBodyRecognizer(spec, constants) {
4895
+ const raw = constants[spec[0]];
4896
+ if (!(raw instanceof RegExp)) {
4897
+ throw new TypeError("table lexical body does not reference a RegExp constant");
4808
4898
  }
4899
+ if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
4900
+ throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
4901
+ }
4902
+ const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
4903
+ const suffix = spec[1];
4904
+ return (input, pos) => {
4905
+ re.lastIndex = pos;
4906
+ if (!re.test(input)) return -1;
4907
+ const end = re.lastIndex;
4908
+ if (suffix < 0) return end * 2;
4909
+ return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
4910
+ };
4809
4911
  }
4810
4912
 
4811
4913
  // src/table/lex-program.ts
@@ -5186,888 +5288,900 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
5186
5288
  };
5187
5289
  }
5188
5290
 
5189
- // src/combinators/repeat.ts
5190
- function repItem(combinator, input, cur, ctx, guardable, mandatory, rollbackNeeded) {
5191
- const mark = rollbackNeeded ? saveTriviaMark(ctx) : void 0;
5192
- let pos = cur;
5193
- if (ctx.trivia) {
5194
- if (needsDeferredTriviaCommit(ctx)) {
5195
- pos = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
5196
- } else {
5197
- pos = advanceTrivia(input, cur, ctx);
5198
- }
5199
- }
5200
- if (pos >= input.length && !mandatory) {
5201
- if (mark !== void 0) rollbackTrivia(ctx, mark);
5202
- return "stop";
5203
- }
5204
- if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
5205
- if (mark !== void 0) rollbackTrivia(ctx, mark);
5206
- return "stop";
5207
- }
5208
- const result = combinator.parse(input, pos, ctx);
5209
- if (!result.ok) {
5210
- if (mark !== void 0) rollbackTrivia(ctx, mark);
5211
- if (result.committed) return { fail: result, failPos: pos };
5212
- return { fail: result, failPos: pos };
5213
- }
5214
- if (result.span.end === pos && !mandatory) {
5215
- if (mark !== void 0) rollbackTrivia(ctx, mark);
5216
- return "stop";
5217
- }
5218
- return result;
5291
+ // src/table/program.ts
5292
+ function closureArtifact(prog) {
5293
+ return { ...prog, asm: [] };
5219
5294
  }
5220
- function resolveBounds(what, opts) {
5221
- const min = opts.min ?? 0;
5222
- const max = opts.max ?? Infinity;
5223
- const bad = (msg) => {
5224
- throw new RangeError(`parseman: ${what} ${msg}`);
5225
- };
5226
- if (!Number.isInteger(min) || min < 0) bad(`min must be a non-negative integer (got ${String(opts.min)})`);
5227
- if (max !== Infinity && (!Number.isInteger(max) || max < 1)) bad(`max must be a positive integer (got ${String(opts.max)})`);
5228
- if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
5229
- return { min, max };
5295
+ function covKindCode(kind) {
5296
+ switch (kind) {
5297
+ case "rule":
5298
+ return 0;
5299
+ case "choice-arm":
5300
+ return 1;
5301
+ case "dispatch-arm":
5302
+ return 2;
5303
+ case "label":
5304
+ return 3;
5305
+ }
5230
5306
  }
5231
- function repeatScalarTail(combinator, child, input, cur, ctx, guardable, values, remaining) {
5232
- while (cur < input.length && remaining > 0) {
5233
- let itemPos = cur;
5234
- if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
5235
- if (itemPos >= input.length) break;
5236
- if (guardable && !startsFirstSet(combinator, input, itemPos)) break;
5237
- const end = child(input, itemPos, ctx);
5238
- if (end <= itemPos) break;
5239
- if (values !== void 0) values.push(ctx._sv);
5240
- cur = end;
5241
- remaining--;
5307
+ function covKindName(code) {
5308
+ switch (code) {
5309
+ case 0:
5310
+ return "rule";
5311
+ case 1:
5312
+ return "choice-arm";
5313
+ case 2:
5314
+ return "dispatch-arm";
5315
+ case 3:
5316
+ return "label";
5242
5317
  }
5243
- ctx._sv = values;
5244
- return cur;
5245
5318
  }
5246
- function many(combinator, opts = {}) {
5247
- const { min, max } = resolveBounds("many()", opts);
5248
- if (min >= 1) return atLeast(combinator, min, max);
5249
- const meta = {
5250
- firstSet: combinator._meta.firstSet,
5251
- canMatchNewline: combinator._meta.canMatchNewline,
5252
- isTrivia: false
5253
- };
5254
- const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
5255
- let expected;
5256
- const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
5257
- const scalarChild = scalarOf(combinator);
5258
- const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
5259
- return {
5260
- _tag: "many",
5261
- _meta: meta,
5262
- _def: def,
5263
- _parseScalar: parseScalar,
5264
- parse(input, pos, ctx) {
5265
- const values = def.valueUnused ? void 0 : [];
5266
- const rollbackNeeded = needsTriviaRollback(ctx);
5267
- let cur = pos;
5268
- let count = 0;
5269
- while (cur < input.length) {
5270
- if (count >= max) break;
5271
- const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
5272
- if (item === "stop") break;
5273
- if ("fail" in item) {
5274
- if (!item.fail.ok && item.fail.committed) return item.fail;
5275
- const sync = ctx._tolerant ? ctx._sync : void 0;
5276
- if (sync === void 0) break;
5277
- if (matchesAt(sync, input, item.failPos, ctx)) break;
5278
- expected ??= deriveExpected(combinator);
5279
- const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
5280
- if (values !== void 0) values.push(error);
5281
- captureError(ctx, error);
5282
- count++;
5283
- cur = end;
5284
- continue;
5285
- }
5286
- if (values !== void 0) values.push(item.value);
5287
- count++;
5288
- cur = item.span.end;
5289
- }
5290
- return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
5291
- }
5292
- };
5319
+ function covDefinitions(prog) {
5320
+ return (prog.cov ?? []).map(([id, kind]) => ({ id, kind: covKindName(kind) }));
5293
5321
  }
5294
- function oneOrMore(combinator, opts = {}) {
5295
- return many(combinator, { ...opts, min: opts.min ?? 1 });
5322
+ function scanRefs(s) {
5323
+ const refs = [];
5324
+ 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);
5325
+ refs.push(...s.skip);
5326
+ return refs;
5296
5327
  }
5297
- function atLeast(combinator, min, max) {
5298
- const meta = {
5299
- firstSet: combinator._meta.firstSet,
5300
- canMatchNewline: combinator._meta.canMatchNewline,
5301
- isTrivia: false
5302
- };
5303
- const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
5304
- let expected;
5305
- const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
5306
- const scalarChild = scalarOf(combinator);
5307
- const parseScalar = (input, pos, ctx) => {
5308
- let cur = scalarChild(input, pos, ctx);
5309
- if (cur < 0) return cur;
5310
- const values = def.valueUnused ? void 0 : [ctx._sv];
5311
- for (let count = 1; count < min; count++) {
5312
- let itemPos = cur;
5313
- if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
5314
- const end = scalarChild(input, itemPos, ctx);
5315
- if (end < 0) return ~cur;
5316
- cur = end;
5317
- if (values !== void 0) values.push(ctx._sv);
5318
- }
5319
- return repeatScalarTail(combinator, scalarChild, input, cur, ctx, guardable, values, max - min);
5320
- };
5321
- return {
5322
- _tag: "oneOrMore",
5323
- _meta: meta,
5324
- _def: def,
5325
- _parseScalar: parseScalar,
5326
- parse(input, pos, ctx) {
5327
- const first = combinator.parse(input, pos, ctx);
5328
- if (!first.ok) return first;
5329
- const values = def.valueUnused ? void 0 : [first.value];
5330
- const rollbackNeeded = needsTriviaRollback(ctx);
5331
- let cur = first.span.end;
5332
- let count = 1;
5333
- while (count < min) {
5334
- const item = repItem(combinator, input, cur, ctx, guardable, true, rollbackNeeded);
5335
- if (item === "stop" || "fail" in item) {
5336
- if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
5337
- expected ??= deriveExpected(combinator);
5338
- return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
5339
- }
5340
- if (values !== void 0) values.push(item.value);
5341
- cur = item.span.end;
5342
- count++;
5343
- }
5344
- while (cur < input.length) {
5345
- if (count >= max) break;
5346
- const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
5347
- if (item === "stop") break;
5348
- if ("fail" in item) {
5349
- if (!item.fail.ok && item.fail.committed) return item.fail;
5350
- const sync = ctx._tolerant ? ctx._sync : void 0;
5351
- if (sync === void 0) break;
5352
- if (matchesAt(sync, input, item.failPos, ctx)) break;
5353
- expected ??= deriveExpected(combinator);
5354
- const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
5355
- if (values !== void 0) values.push(error);
5356
- captureError(ctx, error);
5357
- count++;
5358
- cur = end;
5359
- continue;
5360
- }
5361
- if (values !== void 0) values.push(item.value);
5362
- count++;
5363
- cur = item.span.end;
5364
- }
5365
- return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
5366
- }
5367
- };
5328
+ function assemblyExtraIps(prog) {
5329
+ const ips = [];
5330
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) ips.push(r[0]);
5331
+ for (const set of prog.scanSkip ?? []) for (const r of set) ips.push(r[0]);
5332
+ return ips;
5333
+ }
5334
+ function expandCompact(p) {
5335
+ if ("code" in p) return p;
5336
+ return ownTableProgram({
5337
+ code: p.c,
5338
+ k: p.k,
5339
+ cc: p.x,
5340
+ fx: p.e,
5341
+ disp: p.d,
5342
+ rules: p.r,
5343
+ fns: p.f,
5344
+ lines: p.l ?? 0,
5345
+ dsp: p.p ?? [],
5346
+ ...p.lx === void 0 ? {} : { lex: p.lx },
5347
+ ...p.lp === void 0 ? {} : { lexPrograms: p.lp },
5348
+ ...p.lb === void 0 ? {} : { labels: p.lb },
5349
+ ...p.rc === void 0 ? {} : { classified: p.rc },
5350
+ ...p.h === void 0 ? {} : { hostMode: p.h },
5351
+ ...p.tv === void 0 ? {} : { triviaSpecs: p.tv },
5352
+ ...p.sc === void 0 ? {} : { scans: p.sc },
5353
+ ...p.ss === void 0 ? {} : { scanSkip: p.ss },
5354
+ ...p.so === void 0 ? {} : { scanSkipOf: p.so },
5355
+ ...p.rv === void 0 ? {} : { rec: p.rv },
5356
+ ...p.cv === void 0 ? {} : { cov: p.cv },
5357
+ ...p.a === void 0 ? {} : { asm: p.a }
5358
+ });
5368
5359
  }
5369
- function optional(combinator) {
5370
- const meta = {
5371
- firstSet: combinator._meta.firstSet,
5372
- canMatchNewline: combinator._meta.canMatchNewline,
5373
- isTrivia: false
5374
- };
5375
- const firstSetSkippable = !matchesEmpty(combinator);
5376
- const scalarChild = scalarOf(combinator);
5377
- const parseScalar = (input, pos, ctx) => {
5378
- if (firstSetSkippable && !startsFirstSet(combinator, input, pos)) {
5379
- ctx._sv = null;
5380
- return pos;
5381
- }
5382
- const end = scalarChild(input, pos, ctx);
5383
- if (end >= 0) return end;
5384
- ctx._sv = null;
5385
- return pos;
5360
+ function validateDispatchSpec(spec, arity, fallbackRouted) {
5361
+ const invalid = (arms) => {
5362
+ for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
5363
+ return false;
5386
5364
  };
5365
+ if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
5366
+ throw new TypeError("table: malformed dispatch");
5367
+ }
5368
+ }
5369
+ var TABLE_RUNTIME = /* @__PURE__ */ Symbol("parseman.tableRuntime");
5370
+ function ownTableProgram(prog, resolved, caches = {}) {
5371
+ const runtime = prog[TABLE_RUNTIME];
5387
5372
  return {
5388
- _tag: "optional",
5389
- _meta: meta,
5390
- _def: { tag: "optional", parser: combinator },
5391
- _parseScalar: parseScalar,
5392
- parse(input, pos, ctx) {
5393
- if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
5394
- return { ok: true, value: null, span: { start: pos, end: pos } };
5395
- }
5396
- const mark = saveTriviaMark(ctx);
5397
- const result = combinator.parse(input, pos, ctx);
5398
- if (result.ok) return result;
5399
- rollbackTrivia(ctx, mark);
5400
- if (result.committed) return result;
5401
- return { ok: true, value: null, span: { start: pos, end: pos } };
5373
+ ...prog,
5374
+ [TABLE_RUNTIME]: {
5375
+ resolved,
5376
+ choiceRollbackMasks: caches.choiceRollbackMasks ?? runtime?.choiceRollbackMasks,
5377
+ nonCommittingChoiceSites: caches.nonCommittingChoiceSites ?? runtime?.nonCommittingChoiceSites,
5378
+ failureRollbackCleanSites: caches.failureRollbackCleanSites ?? runtime?.failureRollbackCleanSites
5402
5379
  }
5403
5380
  };
5404
5381
  }
5405
- function unwrapSeparator(separator) {
5406
- return "_keepSeparator" in separator ? { sep: separator._keepSeparator, keep: true } : { sep: separator, keep: false };
5382
+ function choiceRollbackMask(prog, ip) {
5383
+ return prog[TABLE_RUNTIME]?.choiceRollbackMasks?.get(ip);
5407
5384
  }
5408
- function sepBy(combinator, separatorArg, opts = {}) {
5409
- const { sep: separator, keep: keepSeparators } = unwrapSeparator(separatorArg);
5410
- const separatorLeads = leadsTrivia(separator);
5411
- const { min, max } = resolveBounds("sepBy()", opts);
5412
- const trailing = opts.trailing ?? "forbid";
5413
- const meta = {
5414
- firstSet: combinator._meta.firstSet,
5415
- canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
5416
- isTrivia: false
5417
- };
5418
- let expected;
5419
- const failAt2 = (at) => {
5420
- expected ??= deriveExpected(combinator);
5421
- return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
5422
- };
5423
- const scalarExpected = deriveExpected(combinator);
5424
- const scalarChild = scalarOf(combinator);
5425
- const scalarSeparator = scalarOf(separator);
5426
- const parseScalar = (input, pos, ctx) => {
5427
- let cur = scalarChild(input, pos, ctx);
5428
- if (cur < 0) {
5429
- if (min >= 1) {
5430
- ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
5431
- return ~pos;
5432
- }
5433
- ctx._sv = [];
5434
- return pos;
5435
- }
5436
- const values = [ctx._sv];
5437
- while (values.length < max && (cur < input.length || values.length < min)) {
5438
- const beforeSeparator = cur;
5439
- let separatorPos = cur;
5440
- if (ctx.trivia) separatorPos = advanceTrivia(input, cur, ctx);
5441
- const separatorEnd = scalarSeparator(input, separatorPos, ctx);
5442
- if (separatorEnd < 0) break;
5443
- let itemPos = separatorEnd;
5444
- if (ctx.trivia) itemPos = advanceTrivia(input, itemPos, ctx);
5445
- const itemEnd = scalarChild(input, itemPos, ctx);
5446
- if (itemEnd < 0) {
5447
- cur = trailing === "allow" ? separatorEnd : beforeSeparator;
5448
- break;
5449
- }
5450
- values.push(ctx._sv);
5451
- cur = itemEnd;
5385
+ function choiceCannotCommit(prog, ip) {
5386
+ return prog[TABLE_RUNTIME]?.nonCommittingChoiceSites?.has(ip) === true;
5387
+ }
5388
+ function failureRollbackClean(prog, ip) {
5389
+ return prog[TABLE_RUNTIME]?.failureRollbackCleanSites?.has(ip) === true;
5390
+ }
5391
+ var WIDE_CLASS_PREFIX_LO = 65535;
5392
+ var WIDE_CLASS_PREFIX_HI = 0;
5393
+ function validateClassRange(lo, hi) {
5394
+ for (const cp of [lo, hi]) {
5395
+ if (!Number.isInteger(cp) || cp < 0 || cp > 1114111) {
5396
+ throw new TypeError(`table char-class endpoint is outside Unicode: ${cp}`);
5452
5397
  }
5453
- if (values.length < min) {
5454
- ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
5455
- return ~cur;
5398
+ }
5399
+ if (lo > hi) {
5400
+ throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
5401
+ }
5402
+ }
5403
+ function encodeClassSpec(ranges) {
5404
+ let wide = false;
5405
+ for (const range of ranges) {
5406
+ validateClassRange(range.lo, range.hi);
5407
+ if (range.hi > 65535) wide = true;
5408
+ }
5409
+ if (!wide) {
5410
+ let spec2 = "";
5411
+ for (const range of ranges) spec2 += String.fromCharCode(range.lo, range.hi);
5412
+ return spec2;
5413
+ }
5414
+ let spec = String.fromCharCode(WIDE_CLASS_PREFIX_LO, WIDE_CLASS_PREFIX_HI);
5415
+ for (const range of ranges) {
5416
+ spec += String.fromCharCode(
5417
+ range.lo >>> 16,
5418
+ range.lo & 65535,
5419
+ range.hi >>> 16,
5420
+ range.hi & 65535
5421
+ );
5422
+ }
5423
+ return spec;
5424
+ }
5425
+ function decodeClassSpec(spec) {
5426
+ if (spec.charCodeAt(0) === WIDE_CLASS_PREFIX_LO && spec.charCodeAt(1) === WIDE_CLASS_PREFIX_HI) {
5427
+ if ((spec.length - 2) % 4 !== 0) throw new TypeError("table char-class spec has a truncated wide range");
5428
+ if (spec.length === 2) throw new TypeError("table char-class spec has a wide prefix but no ranges");
5429
+ const ranges2 = [];
5430
+ for (let i = 2; i < spec.length; i += 4) {
5431
+ const lo = spec.charCodeAt(i) * 65536 + spec.charCodeAt(i + 1);
5432
+ const hi = spec.charCodeAt(i + 2) * 65536 + spec.charCodeAt(i + 3);
5433
+ validateClassRange(lo, hi);
5434
+ ranges2.push({ lo, hi });
5456
5435
  }
5457
- ctx._sv = values;
5458
- return cur;
5459
- };
5460
- return {
5461
- _tag: "sepBy",
5462
- _meta: meta,
5463
- _def: {
5464
- tag: "sepBy",
5465
- parser: combinator,
5466
- separator,
5467
- min,
5468
- ...max === Infinity ? {} : { max },
5469
- ...trailing === "forbid" ? {} : { trailing },
5470
- // Carried into the IR so the COMPILED engine demotes exactly where the
5471
- // interpreter does. The two engines have drifted three separate ways across
5472
- // this option surface already; parity is held by test/parity, not by hope.
5473
- ...keepSeparators ? { keepSeparators: true } : {}
5474
- },
5475
- _parseScalar: keepSeparators || separatorLeads ? void 0 : parseScalar,
5476
- parse(input, pos, ctx) {
5477
- const first = combinator.parse(input, pos, ctx);
5478
- const values = [];
5479
- let cur;
5480
- if (first.ok) {
5481
- values.push(first.value);
5482
- cur = first.span.end;
5483
- } else {
5484
- if (first.committed) return first;
5485
- const term = ctx._tolerant ? ctx._sync : void 0;
5486
- if (term === void 0 || matchesAt(term, input, pos, ctx)) {
5487
- if (min >= 1) return failAt2(pos);
5488
- return { ok: true, value: [], span: { start: pos, end: pos } };
5489
- }
5490
- expected ??= deriveExpected(combinator);
5491
- const rec = recoverScan(input, pos, ctx, orSentinel(separator, term), expected);
5492
- values.push(rec.error);
5493
- captureError(ctx, rec.error);
5494
- cur = rec.end;
5495
- }
5496
- while (cur < input.length || values.length < min) {
5497
- if (values.length >= max) break;
5498
- const loopMark = saveTriviaMark(ctx);
5499
- let sepPos = cur;
5500
- if (ctx.trivia) {
5501
- if (needsDeferredTriviaCommit(ctx)) {
5502
- sepPos = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
5503
- } else {
5504
- sepPos = advanceTrivia(input, cur, ctx);
5505
- }
5506
- }
5507
- if (separatorLeads) LEAD.at = cur;
5508
- const sep = separator.parse(input, sepPos, ctx);
5509
- if (!sep.ok) {
5510
- rollbackTrivia(ctx, loopMark);
5511
- if (sep.committed) return sep;
5512
- break;
5513
- }
5514
- if (!keepSeparators) demoteCapturedToRaw(ctx, loopMark.leaves);
5515
- const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
5516
- let nextPos = sep.span.end;
5517
- if (ctx.trivia) {
5518
- if (needsDeferredTriviaCommit(ctx)) {
5519
- nextPos = commitTriviaScan(scanTriviaCompact(input, sep.span.end, ctx));
5520
- } else {
5521
- nextPos = advanceTrivia(input, sep.span.end, ctx);
5522
- }
5523
- }
5524
- const next = combinator.parse(input, nextPos, ctx);
5525
- if (!next.ok) {
5526
- if (next.committed) return next;
5527
- const term = ctx._tolerant ? ctx._sync : void 0;
5528
- if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
5529
- expected ??= deriveExpected(combinator);
5530
- const rec = recoverScan(input, nextPos, ctx, orSentinel(separator, term), expected);
5531
- values.push(rec.error);
5532
- captureError(ctx, rec.error);
5533
- cur = rec.end;
5534
- continue;
5535
- }
5536
- if (sepMark !== void 0) {
5537
- rollbackTrivia(ctx, sepMark);
5538
- cur = sep.span.end;
5539
- break;
5540
- }
5541
- rollbackTrivia(ctx, loopMark);
5542
- break;
5543
- }
5544
- values.push(next.value);
5545
- cur = next.span.end;
5546
- }
5547
- if (values.length < min) return failAt2(cur);
5548
- return { ok: true, value: values, span: { start: pos, end: cur } };
5436
+ return ranges2;
5437
+ }
5438
+ if (spec.length % 2 !== 0) throw new TypeError("table char-class spec has an unmatched range endpoint");
5439
+ const ranges = [];
5440
+ for (let i = 0; i < spec.length; i += 2) {
5441
+ const lo = spec.charCodeAt(i);
5442
+ const hi = spec.charCodeAt(i + 1);
5443
+ validateClassRange(lo, hi);
5444
+ ranges.push({ lo, hi });
5445
+ }
5446
+ return ranges;
5447
+ }
5448
+ function resolveClass(spec) {
5449
+ const ascii = new Uint8Array(128);
5450
+ const hi = [];
5451
+ for (const { lo, hi: up } of decodeClassSpec(spec)) {
5452
+ if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1;
5453
+ if (up >= 128) hi.push(Math.max(lo, 128), up);
5454
+ }
5455
+ return { ascii, hi };
5456
+ }
5457
+ function classHas2(cls, code) {
5458
+ if (code < 0) return false;
5459
+ if (code < 128) return cls.ascii[code] === 1;
5460
+ const hi = cls.hi;
5461
+ for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
5462
+ return false;
5463
+ }
5464
+ function dispatchIsExclusive(arms, cc) {
5465
+ const resolved = [];
5466
+ for (const ci of arms) if (ci >= 0) resolved[ci] = resolveClass(cc[ci]);
5467
+ return resolveDispatch(arms, resolved).exclusive;
5468
+ }
5469
+ function resolveDispatch(arms, cc) {
5470
+ const ascii = new Uint8Array(128);
5471
+ const hi = [];
5472
+ const open = [];
5473
+ const armCls = [];
5474
+ let exclusive = true;
5475
+ let hiOwners = 0;
5476
+ for (let a = 0; a < arms.length; a++) {
5477
+ const ci = arms[a];
5478
+ if (ci < 0) {
5479
+ open.push(a);
5480
+ armCls.push(null);
5481
+ exclusive = false;
5482
+ continue;
5549
5483
  }
5484
+ const cls = cc[ci];
5485
+ armCls.push(cls);
5486
+ for (let c = 0; c < 128; c++) {
5487
+ if (cls.ascii[c] !== 1) continue;
5488
+ if (ascii[c] === 0) ascii[c] = a + 1;
5489
+ else exclusive = false;
5490
+ }
5491
+ if (cls.hi.length > 0 && ++hiOwners > 1) exclusive = false;
5492
+ for (let i = 0; i < cls.hi.length; i += 2) hi.push(cls.hi[i], cls.hi[i + 1], a);
5493
+ }
5494
+ return { ascii, hi, open, armCls, exclusive };
5495
+ }
5496
+ function buildTrivia(spec) {
5497
+ if (spec.live !== void 0) return spec.live;
5498
+ if (spec.plain !== void 0) return trivia(regex(new RegExp(spec.plain[0], spec.plain[1])));
5499
+ if (spec.alts !== void 0) {
5500
+ const [head, ...rest] = spec.alts.map(([source, flags]) => regex(new RegExp(source, flags)));
5501
+ if (head === void 0) throw new TypeError("trivia spec has an EMPTY alternation, which matches nothing");
5502
+ const body = rest.length === 0 ? head : choice(head, ...rest);
5503
+ return trivia(many(body, { min: spec.min ?? 1 }));
5504
+ }
5505
+ const arms = {};
5506
+ for (const [name, source, flags] of spec.arms) arms[name] = regex(new RegExp(source, flags));
5507
+ return classifiedTrivia(arms);
5508
+ }
5509
+ function resolveTable(prog) {
5510
+ const owner = prog[TABLE_RUNTIME];
5511
+ const hit = owner?.resolved;
5512
+ if (hit !== void 0) return hit;
5513
+ const cc = prog.cc.map(resolveClass);
5514
+ const triviaBuilt = (prog.triviaSpecs ?? []).map(buildTrivia);
5515
+ const lex = (prog.lex ?? []).map((spec) => buildLexBodyRecognizer(spec, prog.k));
5516
+ const built = {
5517
+ prog,
5518
+ code: Int32Array.from(prog.code),
5519
+ k: prog.k,
5520
+ fns: prog.fns,
5521
+ cc,
5522
+ fx: prog.fx,
5523
+ disp: prog.disp.map((d) => resolveDispatch(d, cc)),
5524
+ trivia: triviaBuilt,
5525
+ triviaScan: triviaBuilt.map(fastTriviaScanner),
5526
+ triviaLabelled: triviaBuilt.map((t2) => t2._meta.triviaKindLabels !== void 0),
5527
+ dsp: prog.dsp.map((d) => ({
5528
+ byKey: new Map(d.key.map((x, i) => [x, d.keyArm[i]])),
5529
+ byFold: new Map(d.fold.map((x, i) => [x, d.foldArm[i]])),
5530
+ match: d.match,
5531
+ routed: d.routed,
5532
+ expected: d.expected
5533
+ })),
5534
+ lex,
5535
+ lexPrograms: (prog.lexPrograms ?? []).map((spec) => buildLexProgramRunner(spec, lex, cc, prog.fx)),
5536
+ rules: prog.rules
5550
5537
  };
5538
+ if (owner !== void 0) owner.resolved = built;
5539
+ return built;
5551
5540
  }
5552
5541
 
5553
- // src/combinators/map.ts
5554
- var JSON_STRING_BODY = String.raw`(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*`;
5555
- function transform(combinator, fn) {
5556
- const observesSpan = fn.length > 1;
5557
- const sequenceDef = combinator._def.tag === "sequence" ? combinator._def : void 0;
5558
- const terms = sequenceDef?.parsers;
5559
- const inner = terms?.[1];
5560
- const fused = terms?.length === 3 && terms[0]._def.tag === "literal" && terms[0]._def.value === '"' && !terms[0]._def.caseInsensitive && inner._def.tag === "regex" && inner._def.source === JSON_STRING_BODY && inner._def.flags === "" && terms[2]._def.tag === "literal" && terms[2]._def.value === '"' && !terms[2]._def.caseInsensitive && inner._stickyRegex !== void 0;
5561
- const child = scalarOf(combinator);
5562
- const parseScalar = fused ? (() => {
5563
- const body = inner._stickyRegex;
5564
- const tuple = ['"', "", '"'];
5565
- const expected = [JSON.stringify('"')];
5566
- return (input, pos, ctx) => {
5567
- if (ctx.trivia !== void 0) {
5568
- const end2 = child(input, pos, ctx);
5569
- if (end2 < 0) return end2;
5570
- ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end: end2 }) : fn(ctx._sv);
5571
- return end2;
5572
- }
5573
- if (input.charCodeAt(pos) !== 34) {
5574
- ctx._fx = expected;
5575
- return ~pos;
5576
- }
5577
- body.lastIndex = pos + 1;
5578
- const match = body.exec(input);
5579
- const close = pos + 1 + match[0].length;
5580
- if (input.charCodeAt(close) !== 34) {
5581
- ctx._fx = expected;
5582
- return ~close;
5583
- }
5584
- tuple[1] = match[0];
5585
- const end = close + 1;
5586
- ctx._sv = observesSpan ? fn(tuple, { start: pos, end }) : fn(tuple);
5587
- return end;
5588
- };
5589
- })() : /* @__PURE__ */ (() => {
5590
- return (input, pos, ctx) => {
5591
- const end = child(input, pos, ctx);
5592
- if (end < 0) return end;
5593
- ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end }) : fn(ctx._sv);
5594
- return end;
5595
- };
5596
- })();
5597
- return {
5598
- _tag: "transform",
5599
- _meta: combinator._meta,
5600
- _def: { tag: "transform", parser: combinator, fn },
5601
- _parseScalar: parseScalar,
5602
- parse(input, pos, ctx) {
5603
- const result = combinator.parse(input, pos, ctx);
5604
- if (!result.ok) return result;
5605
- return { ok: true, value: fn(result.value, result.span), span: result.span };
5542
+ // src/compiler/degradation.ts
5543
+ function formatDegradation(d) {
5544
+ return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
5545
+ }
5546
+ var DETAIL_CAP = 8;
5547
+ function formatDegradations(list) {
5548
+ const byCode = /* @__PURE__ */ new Map();
5549
+ for (const d of list) {
5550
+ const bucket = byCode.get(d.code);
5551
+ if (bucket) bucket.push(d);
5552
+ else byCode.set(d.code, [d]);
5553
+ }
5554
+ const out = [];
5555
+ for (const [code, items] of byCode) {
5556
+ for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
5557
+ if (items.length > DETAIL_CAP) {
5558
+ out.push(
5559
+ `[parseman] degraded [${code}] +${items.length - DETAIL_CAP} more site(s) not listed (${items.length} total). Set PARSEMAN_DEGRADATION=error to fail the build on these.`
5560
+ );
5606
5561
  }
5607
- };
5562
+ }
5563
+ return out;
5608
5564
  }
5609
- function trivia(combinator) {
5610
- const labeled = analyzeLabeledTrivia(combinator);
5611
- const kindLabels = labeled?.labels;
5612
- return {
5613
- _tag: combinator._tag,
5614
- _meta: {
5615
- ...combinator._meta,
5616
- isTrivia: true,
5617
- triviaScanner: fastTriviaScanner(combinator),
5618
- // Every labeled scan of this trivia reads its spec; the module's four-slot
5619
- // memo alone thrashes on a grammar with more than four trivia tables.
5620
- labeledTriviaSpec: labeled,
5621
- ...kindLabels ? { triviaKindLabels: kindLabels } : {}
5622
- },
5623
- _def: { tag: "trivia", parser: combinator },
5624
- _parseScalar: scalarOf(combinator),
5625
- parse: combinator.parse.bind(combinator)
5565
+ var _sinks = [];
5566
+ function beginDegradationCapture() {
5567
+ _sinks.push(/* @__PURE__ */ new Map());
5568
+ }
5569
+ function degradationCaptureDepth() {
5570
+ return _sinks.length;
5571
+ }
5572
+ function unwindDegradationCapture(depth) {
5573
+ const found = [];
5574
+ while (_sinks.length > depth) {
5575
+ const sink = _sinks.pop();
5576
+ found.unshift(...sink.values());
5577
+ }
5578
+ return found;
5579
+ }
5580
+ function resolveDegradationLevel(explicit) {
5581
+ if (explicit !== void 0) return explicit;
5582
+ const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
5583
+ if (env === "off" || env === "warn" || env === "error") return env;
5584
+ return "warn";
5585
+ }
5586
+ function degradationKey(d) {
5587
+ return JSON.stringify([d.code, d.where, d.subject]);
5588
+ }
5589
+ function recordDegradation(d) {
5590
+ const level = resolveDegradationLevel();
5591
+ if (level === "off") return;
5592
+ const key = degradationKey(d);
5593
+ const sink = _sinks[_sinks.length - 1];
5594
+ if (sink) {
5595
+ if (!sink.has(key)) sink.set(key, d);
5596
+ return;
5597
+ }
5598
+ if (_immediate.has(key)) return;
5599
+ _immediate.add(key);
5600
+ if (level === "error") throw new Error(`parseman: degraded compilation path
5601
+ ${formatDegradation(d)}`);
5602
+ console.warn(formatDegradation(d));
5603
+ }
5604
+ var _immediate = /* @__PURE__ */ new Set();
5605
+ function beginCompileDegradationDrain() {
5606
+ if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
5607
+ };
5608
+ const depth = degradationCaptureDepth();
5609
+ beginDegradationCapture();
5610
+ return (report) => {
5611
+ const found = unwindDegradationCapture(depth);
5612
+ if (!report || found.length === 0) return;
5613
+ const fresh = found.filter((d) => {
5614
+ const k = degradationKey(d);
5615
+ if (_immediate.has(k)) return false;
5616
+ _immediate.add(k);
5617
+ return true;
5618
+ });
5619
+ if (fresh.length === 0) return;
5620
+ const lines2 = formatDegradations(fresh);
5621
+ if (resolveDegradationLevel() === "error") {
5622
+ throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
5623
+ ${lines2.join("\n")}`);
5624
+ }
5625
+ for (const l of lines2) console.warn(l);
5626
5626
  };
5627
5627
  }
5628
- function classifiedTrivia(arms) {
5629
- const entries = Object.entries(arms);
5630
- if (entries.length === 0) {
5631
- throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
5628
+
5629
+ // src/compiler/build-arity.ts
5630
+ function cstOutputHost(build) {
5631
+ return build?._parsemanCstOutput === true;
5632
+ }
5633
+ var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
5634
+ var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
5635
+ var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
5636
+ var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
5637
+ function confirmedBuildArity(src) {
5638
+ const s = src.trim();
5639
+ if (NATIVE_CODE_RE.test(s)) return null;
5640
+ if (/\barguments\b/.test(s)) return null;
5641
+ if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
5642
+ const inner = paramListText(s);
5643
+ if (inner === null) return null;
5644
+ if (inner.trim() === "") return 0;
5645
+ const parts = splitTopLevel(inner);
5646
+ if (parts === null) return null;
5647
+ for (const part of parts) {
5648
+ if (!isConfirmableParam(part.trim())) return null;
5632
5649
  }
5633
- for (let i = 0; i < entries.length; i++) {
5634
- const [name, arm] = entries[i];
5635
- const first = arm._meta.firstSet;
5636
- if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
5637
- throw new TypeError(
5638
- `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
5639
- );
5650
+ return parts.length;
5651
+ }
5652
+ function paramListText(s) {
5653
+ const open = PARAM_LIST_OPEN_RE.exec(s);
5654
+ if (!open) return null;
5655
+ const start = open[0].length;
5656
+ const end = matchingParen(s, start);
5657
+ if (end === null) return null;
5658
+ if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
5659
+ return s.slice(start, end);
5660
+ }
5661
+ function matchingParen(s, start) {
5662
+ let depth = 0;
5663
+ for (let i = start; i < s.length; i++) {
5664
+ const c = s[i];
5665
+ if (c === '"' || c === "'" || c === "`") {
5666
+ const close = skipString(s, i);
5667
+ if (close === null) return null;
5668
+ i = close;
5669
+ continue;
5670
+ }
5671
+ if (c === "(" || c === "[" || c === "{") depth++;
5672
+ else if (c === "]" || c === "}") {
5673
+ if (--depth < 0) return null;
5674
+ } else if (c === ")") {
5675
+ if (depth === 0) return i;
5676
+ depth--;
5640
5677
  }
5641
5678
  }
5642
- const labeledArms = entries.map(([name, arm]) => label(name, arm));
5643
- const result = trivia(oneOrMore(choice(...labeledArms)));
5644
- result._meta.rootTriviaClassified = true;
5645
- return result;
5679
+ return null;
5646
5680
  }
5647
- function label(name, combinator) {
5648
- return {
5649
- _tag: combinator._tag,
5650
- _meta: combinator._meta,
5651
- _def: { tag: "label", label: name, parser: combinator },
5652
- parse(input, pos, ctx) {
5653
- const result = combinator.parse(input, pos, ctx);
5654
- if (!result.ok) return { ok: false, expected: [name], span: result.span };
5655
- return result;
5681
+ function skipString(s, open) {
5682
+ const quote = s[open];
5683
+ for (let i = open + 1; i < s.length; i++) {
5684
+ if (s[i] === "\\") {
5685
+ i++;
5686
+ continue;
5656
5687
  }
5657
- };
5658
- }
5659
- function field(name, combinator, opts) {
5660
- const leadingTrivia = opts?.leadingTrivia === true;
5661
- const trailingTrivia = opts?.trailingTrivia === true;
5662
- if (!leadingTrivia && !trailingTrivia) {
5663
- return {
5664
- _tag: combinator._tag,
5665
- _meta: combinator._meta,
5666
- _def: { tag: "field", name, parser: combinator },
5667
- parse(input, pos, ctx) {
5668
- const result = combinator.parse(input, pos, ctx);
5669
- if (!result.ok) return result;
5670
- ctx._fields?.push({ name, value: result.value, span: result.span });
5671
- return result;
5672
- }
5673
- };
5688
+ if (s[i] === quote) return i;
5674
5689
  }
5675
- return {
5676
- _tag: combinator._tag,
5677
- _meta: combinator._meta,
5678
- _def: {
5679
- tag: "field",
5680
- name,
5681
- parser: combinator,
5682
- ...leadingTrivia ? { leadingTrivia: true } : {},
5683
- ...trailingTrivia ? { trailingTrivia: true } : {}
5684
- },
5685
- parse(input, pos, ctx) {
5686
- const leadFrom = LEAD.at;
5687
- LEAD.at = -1;
5688
- const result = combinator.parse(input, pos, ctx);
5689
- if (!result.ok) return result;
5690
- const end = result.span.end;
5691
- const trailEnd = trailingTrivia && ctx.trivia ? consumeTrivia(input, end, ctx) : end;
5692
- ctx._fields?.push(triviaFieldCapture(name, result.value, input, result.span.start, end, leadingTrivia ? leadFrom : -1, trailEnd));
5693
- return trailEnd === end ? result : { ok: true, value: result.value, span: { start: result.span.start, end: trailEnd } };
5694
- }
5695
- };
5690
+ return null;
5696
5691
  }
5697
-
5698
- // src/combinators/regex.ts
5699
- var SCAN_BAIL_AT = 64;
5700
- function inRanges3(cp, ranges) {
5701
- for (let i = 0; i < ranges.length; i++) {
5702
- const [lo, hi] = ranges[i];
5703
- if (cp >= lo && cp <= hi) return true;
5692
+ function splitTopLevel(inner) {
5693
+ const parts = [];
5694
+ let depth = 0;
5695
+ let angle = 0;
5696
+ let last = 0;
5697
+ for (let i = 0; i < inner.length; i++) {
5698
+ const c = inner[i];
5699
+ if (c === "=" && inner[i + 1] === ">") {
5700
+ i++;
5701
+ continue;
5702
+ }
5703
+ if (c === '"' || c === "'" || c === "`") {
5704
+ const close = skipString(inner, i);
5705
+ if (close === null) return null;
5706
+ i = close;
5707
+ continue;
5708
+ }
5709
+ if (c === "(" || c === "[" || c === "{") depth++;
5710
+ else if (c === ")" || c === "]" || c === "}") {
5711
+ if (--depth < 0) return null;
5712
+ } else if (c === "<") angle++;
5713
+ else if (c === ">") {
5714
+ if (--angle < 0) return null;
5715
+ } else if (c === "," && depth === 0 && angle === 0) {
5716
+ parts.push(inner.slice(last, i));
5717
+ last = i + 1;
5718
+ }
5704
5719
  }
5705
- return false;
5720
+ if (depth !== 0 || angle !== 0) return null;
5721
+ parts.push(inner.slice(last));
5722
+ return parts;
5706
5723
  }
5707
- function readClassRanges(body) {
5708
- if (body.startsWith("^")) return null;
5709
- return parseClassRanges(body);
5724
+ function isConfirmableParam(part) {
5725
+ const head = PARAM_HEAD_RE.exec(part);
5726
+ if (!head) return false;
5727
+ const rest = part.slice(head[0].length);
5728
+ if (rest === "") return true;
5729
+ if (rest[0] !== ":") return false;
5730
+ return !hasDefaultAssign(rest.slice(1));
5710
5731
  }
5711
- function shortScanner(source, flags) {
5712
- if (/[imsuvy]/.test(flags)) return null;
5713
- let ranges = null;
5714
- let quant = "";
5715
- if (source[0] === "[") {
5716
- let end = 1;
5717
- while (end < source.length && source[end] !== "]") {
5718
- if (source[end] === "\\") end += 2;
5719
- else end++;
5732
+ function hasDefaultAssign(annotation) {
5733
+ for (let i = 0; i < annotation.length; i++) {
5734
+ if (annotation[i] !== "=") continue;
5735
+ if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
5736
+ i++;
5737
+ continue;
5720
5738
  }
5721
- if (source[end] !== "]") return null;
5722
- ranges = readClassRanges(source.slice(1, end));
5723
- quant = source.slice(end + 1);
5724
- } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
5725
- ranges = shorthandRanges(source[1]);
5726
- quant = source.slice(2);
5739
+ const prev = annotation[i - 1];
5740
+ if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
5741
+ return true;
5727
5742
  }
5728
- if (!ranges || quant !== "+" && quant !== "*") return null;
5729
- const minOne = quant === "+";
5730
- return (input, pos) => {
5731
- let end = pos;
5732
- while (end < input.length && inRanges3(input.charCodeAt(end), ranges)) {
5733
- end++;
5734
- if (end - pos >= SCAN_BAIL_AT) return void 0;
5735
- }
5736
- return minOne && end === pos ? null : end;
5737
- };
5743
+ return false;
5738
5744
  }
5739
- function asciiCaseFold(fs) {
5740
- if (fs.kind !== "ranges") return fs;
5741
- const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
5742
- const add = (lo, hi) => {
5743
- if (lo <= hi) ranges.push({ lo, hi });
5744
- };
5745
- for (const r of fs.ranges) {
5746
- const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
5747
- add(uLo + 32, uHi + 32);
5748
- const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
5749
- add(lLo - 32, lHi - 32);
5750
- }
5751
- ranges.sort((a, b) => a.lo - b.lo);
5752
- const merged = [];
5753
- for (const r of ranges) {
5754
- const last = merged[merged.length - 1];
5755
- if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
5756
- else merged.push({ lo: r.lo, hi: r.hi });
5745
+ function buildAnalysisSrc(def) {
5746
+ return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
5747
+ }
5748
+ var RESOLVER_REASONS = {
5749
+ "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
5750
+ "arguments": "its body references `arguments`",
5751
+ "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
5752
+ "not-a-function": "the binding it names is not a function declaration",
5753
+ "unresolved-import": "its module could not be resolved or parsed",
5754
+ "not-found": "the name did not resolve to any binding",
5755
+ "computed": "it is a computed expression",
5756
+ "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
5757
+ "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
5758
+ };
5759
+ function unconfirmableReason(def, src) {
5760
+ const declared = def.buildArityUnresolved;
5761
+ if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
5762
+ const s = src.trim();
5763
+ const params = s.slice(0, s.indexOf(")") + 1);
5764
+ if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
5765
+ if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
5766
+ if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
5767
+ return "it names a binding this build could not resolve to a function declaration";
5757
5768
  }
5758
- return { kind: "ranges", ranges: merged };
5769
+ return "its parameter list could not be parsed";
5770
+ }
5771
+ function confirmedArityForDef(def) {
5772
+ if (def.buildArity !== void 0) return def.buildArity;
5773
+ const src = buildAnalysisSrc(def);
5774
+ const arity = confirmedBuildArity(src);
5775
+ if (arity !== null) return arity;
5776
+ const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
5777
+ recordDegradation({
5778
+ code: "build-arity-unconfirmed",
5779
+ severity: "warn",
5780
+ where: `node("${def.type ?? "<inferred>"}")`,
5781
+ subject,
5782
+ fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and hands it \`_ctx.state\` on every match`,
5783
+ otherwise: "only the tiers the reducer actually declares would be captured (arity >= 1 children, >= 2 fields, >= 4 raw, >= 5 trivia, >= 6 state). Declare it with `node(..., { buildArity: n })` if it cannot be derived"
5784
+ });
5785
+ return null;
5786
+ }
5787
+ function buildReadsRaw(def) {
5788
+ if (!def.build) return true;
5789
+ if (def.buildRawUnused === true) return false;
5790
+ const arity = confirmedArityForDef(def);
5791
+ if (arity === null) return true;
5792
+ return arity >= 4;
5793
+ }
5794
+ function buildReadsTrivia(def) {
5795
+ if (!def.build) return true;
5796
+ const arity = confirmedArityForDef(def);
5797
+ if (arity === null) return true;
5798
+ return arity >= 5;
5799
+ }
5800
+ function buildReadsState(def) {
5801
+ if (!def.build) return true;
5802
+ const arity = confirmedArityForDef(def);
5803
+ if (arity === null) return true;
5804
+ return arity >= 6;
5759
5805
  }
5760
- function regex(pattern, flags = "") {
5761
- const source = typeof pattern === "string" ? pattern : pattern.source;
5762
- const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
5763
- const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
5764
- const scan = shortScanner(source, resolvedFlags);
5765
- const raw = firstSetFromRegex(source);
5766
- const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
5767
- const canMatchNewline = raw.canMatchNewline;
5768
- const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
5769
- const def = { tag: "regex", source, flags: resolvedFlags };
5770
- const expected = directTerminalFailureExpected(def);
5771
- const parseScalar = (input, pos, ctx) => {
5772
- const scanEnd = scan?.(input, pos);
5773
- if (scanEnd !== void 0) {
5774
- if (scanEnd === null) {
5775
- ctx._fx = expected;
5776
- return ~pos;
5777
- }
5778
- ctx._sv = input.slice(pos, scanEnd);
5779
- return scanEnd;
5780
- }
5781
- anchored.lastIndex = pos;
5782
- const match = anchored.exec(input);
5783
- if (match === null) {
5784
- ctx._fx = expected;
5785
- return ~pos;
5786
- }
5787
- ctx._sv = match[0];
5788
- return pos + match[0].length;
5789
- };
5790
- return {
5791
- _tag: "regex",
5792
- _meta: meta,
5793
- _def: def,
5794
- _stickyRegex: anchored,
5795
- _parseScalar: parseScalar,
5796
- parse(input, pos, ctx) {
5797
- const scanEnd = scan?.(input, pos);
5798
- if (scanEnd !== void 0) {
5799
- if (scanEnd === null) return failAt(ctx, directTerminalFailureExpected(def), pos);
5800
- const value = input.slice(pos, scanEnd);
5801
- const span2 = { start: pos, end: scanEnd };
5802
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
5803
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
5804
- return { ok: true, value, span: span2 };
5805
- }
5806
- anchored.lastIndex = pos;
5807
- const m = anchored.exec(input);
5808
- if (m === null) {
5809
- return failAt(ctx, directTerminalFailureExpected(def), pos);
5806
+
5807
+ // src/compiler/fields.ts
5808
+ function buildReadsFields(def) {
5809
+ if (!def.build) return true;
5810
+ const arity = confirmedArityForDef(def);
5811
+ if (arity === null) return true;
5812
+ return arity >= 2;
5813
+ }
5814
+ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
5815
+ if (seen.has(p)) return false;
5816
+ seen.add(p);
5817
+ const d = p._def;
5818
+ switch (d.tag) {
5819
+ case "field":
5820
+ return true;
5821
+ case "node":
5822
+ return false;
5823
+ case "lazy": {
5824
+ try {
5825
+ return parserHasOwnFields(d.thunk(), seen);
5826
+ } catch {
5827
+ return false;
5810
5828
  }
5811
- const span = { start: pos, end: pos + m[0].length };
5812
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
5813
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
5814
- return { ok: true, value: m[0], span };
5815
5829
  }
5816
- };
5817
- }
5818
-
5819
- // src/table/lex-body.ts
5820
- function buildLexBodyRecognizer(spec, constants) {
5821
- const raw = constants[spec[0]];
5822
- if (!(raw instanceof RegExp)) {
5823
- throw new TypeError("table lexical body does not reference a RegExp constant");
5830
+ case "sequence":
5831
+ case "choice":
5832
+ return d.parsers.some((x) => parserHasOwnFields(x, seen));
5833
+ case "dispatch":
5834
+ return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => parserHasOwnFields(entry.parser, seen)) : false) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
5835
+ case "sepBy":
5836
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
5837
+ case "grammar":
5838
+ return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
5839
+ case "scanTo":
5840
+ 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);
5841
+ case "recover":
5842
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
5843
+ case "lines":
5844
+ return parserHasOwnFields(d.body, seen);
5845
+ case "routed":
5846
+ return d.fallback ? parserHasOwnFields(d.fallback, seen) : false;
5847
+ case "many":
5848
+ case "oneOrMore":
5849
+ case "optional":
5850
+ case "attempt":
5851
+ case "transform":
5852
+ case "trivia":
5853
+ case "token":
5854
+ case "leaf":
5855
+ case "label":
5856
+ case "expect":
5857
+ case "withCtx":
5858
+ case "not":
5859
+ case "peek":
5860
+ return parserHasOwnFields(d.parser, seen);
5861
+ default:
5862
+ return false;
5824
5863
  }
5825
- if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
5826
- throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
5864
+ }
5865
+ function buildFieldMap(captures) {
5866
+ if (!captures?.length) return void 0;
5867
+ const out = {};
5868
+ for (const cap of captures) {
5869
+ const entry = cap.leading === void 0 ? { value: cap.value, span: cap.span } : { value: cap.value, span: cap.span, leading: cap.leading, trailing: cap.trailing };
5870
+ const current = out[cap.name];
5871
+ if (current === void 0) out[cap.name] = entry;
5872
+ else if (Array.isArray(current)) current.push(entry);
5873
+ else out[cap.name] = [current, entry];
5827
5874
  }
5828
- const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
5829
- const suffix = spec[1];
5830
- return (input, pos) => {
5831
- re.lastIndex = pos;
5832
- if (!re.test(input)) return -1;
5833
- const end = re.lastIndex;
5834
- if (suffix < 0) return end * 2;
5835
- return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
5836
- };
5875
+ return out;
5837
5876
  }
5838
5877
 
5839
- // src/table/program.ts
5840
- function closureArtifact(prog) {
5841
- return { ...prog, asm: [] };
5878
+ // src/table/ops.ts
5879
+ var OP_LIT = 1;
5880
+ var OP_RX = 2;
5881
+ var OP_SEQ = 3;
5882
+ var OP_SEQV = 4;
5883
+ var OP_CHOICE = 5;
5884
+ var OP_REP = 6;
5885
+ var OP_REPV = 7;
5886
+ var OP_OPT = 8;
5887
+ var OP_XFORM = 9;
5888
+ var OP_NODE = 10;
5889
+ var OP_RULE = 11;
5890
+ var OP_GATE = 12;
5891
+ var OP_NOT = 13;
5892
+ var OP_PEEK = 14;
5893
+ var OP_LEAF = 15;
5894
+ var OP_EMPTY = 16;
5895
+ var OP_LIT_TRACK = 17;
5896
+ var OP_RX_TRACK = 18;
5897
+ var OP_NODE_TRACK = 19;
5898
+ var OP_SCOPE = 20;
5899
+ var OP_EXPECT = 21;
5900
+ var OP_SEQX = 22;
5901
+ var OP_SCAN = 23;
5902
+ var OP_FIELD = 24;
5903
+ var OP_LIT_CI = 27;
5904
+ var OP_LIT_CI_TRACK = 28;
5905
+ var OP_TOKEN = 29;
5906
+ var OP_SCOPE_CAP = 30;
5907
+ var OP_WITHCTX = 31;
5908
+ var OP_GUARD = 32;
5909
+ var OP_ADJ = 33;
5910
+ var OP_GREEDY = 34;
5911
+ var OP_REJECT = 35;
5912
+ var OP_ARMGATE = 36;
5913
+ var OP_LIVE = 37;
5914
+ var OP_ATTEMPT = 38;
5915
+ var OP_LABEL = 39;
5916
+ var OP_COV = 40;
5917
+ var OP_SCOPE_PLAIN = 41;
5918
+ var OP_LEX_BODY = 42;
5919
+ var OP_LEX_PROGRAM = 43;
5920
+ var OP_SOURCE_LEAF = 44;
5921
+ var OP_DISPATCH = 25;
5922
+ var OP_ROUTED = 26;
5923
+ var OP_NAMES = {
5924
+ [OP_LIT]: "LIT",
5925
+ [OP_RX]: "RX",
5926
+ [OP_SEQ]: "SEQ",
5927
+ [OP_SEQV]: "SEQV",
5928
+ [OP_CHOICE]: "CHOICE",
5929
+ [OP_REP]: "REP",
5930
+ [OP_REPV]: "REPV",
5931
+ [OP_OPT]: "OPT",
5932
+ [OP_XFORM]: "XFORM",
5933
+ [OP_NODE]: "NODE",
5934
+ [OP_RULE]: "RULE",
5935
+ [OP_GATE]: "GATE",
5936
+ [OP_NOT]: "NOT",
5937
+ [OP_PEEK]: "PEEK",
5938
+ [OP_LEAF]: "LEAF",
5939
+ [OP_SOURCE_LEAF]: "SOURCE_LEAF",
5940
+ [OP_EMPTY]: "EMPTY",
5941
+ [OP_LIT_TRACK]: "LIT_TRACK",
5942
+ [OP_RX_TRACK]: "RX_TRACK",
5943
+ [OP_NODE_TRACK]: "NODE_TRACK",
5944
+ [OP_SCOPE]: "SCOPE",
5945
+ [OP_EXPECT]: "EXPECT",
5946
+ [OP_SEQX]: "SEQX",
5947
+ [OP_SCAN]: "SCAN",
5948
+ [OP_FIELD]: "FIELD",
5949
+ [OP_LIT_CI]: "LIT_CI",
5950
+ [OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
5951
+ [OP_DISPATCH]: "DISPATCH",
5952
+ [OP_ROUTED]: "ROUTED",
5953
+ [OP_TOKEN]: "TOKEN",
5954
+ [OP_SCOPE_CAP]: "SCOPE_CAP",
5955
+ [OP_WITHCTX]: "WITHCTX",
5956
+ [OP_GUARD]: "GUARD",
5957
+ [OP_ADJ]: "ADJ",
5958
+ [OP_GREEDY]: "GREEDY",
5959
+ [OP_REJECT]: "REJECT",
5960
+ [OP_ARMGATE]: "ARMGATE",
5961
+ [OP_LIVE]: "LIVE",
5962
+ [OP_ATTEMPT]: "ATTEMPT",
5963
+ [OP_LABEL]: "LABEL",
5964
+ [OP_COV]: "COV",
5965
+ [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
5966
+ [OP_LEX_BODY]: "LEX_BODY",
5967
+ [OP_LEX_PROGRAM]: "LEX_PROGRAM"
5968
+ };
5969
+
5970
+ // src/combinators/adjacency.ts
5971
+ function adjacencyOf(p) {
5972
+ return p._def.tag === "adjacency" ? p._def : null;
5842
5973
  }
5843
- function covKindCode(kind) {
5844
- switch (kind) {
5845
- case "rule":
5846
- return 0;
5847
- case "choice-arm":
5848
- return 1;
5849
- case "dispatch-arm":
5850
- return 2;
5851
- case "label":
5852
- return 3;
5853
- }
5974
+ function adjacencyExpected(def) {
5975
+ if (def.polarity === "adjacent") return "adjacent";
5976
+ return def.kinds === void 0 ? "notAdjacent" : `notAdjacent(${def.kinds.join("|")})`;
5977
+ }
5978
+ function holdsAdjacency(input, cur, ctx, def) {
5979
+ return adjacencyHolds(input, cur, ctx, def.polarity === "notAdjacent", def.kinds);
5854
5980
  }
5855
- function covKindName(code) {
5856
- switch (code) {
5857
- case 0:
5858
- return "rule";
5859
- case 1:
5860
- return "choice-arm";
5861
- case 2:
5862
- return "dispatch-arm";
5863
- case 3:
5864
- return "label";
5865
- }
5981
+ function adjacencyHolds(input, cur, ctx, negated, kinds) {
5982
+ const end = probeTriviaEnd(input, cur, ctx);
5983
+ if (!negated) return end === cur;
5984
+ if (end === cur) return false;
5985
+ if (kinds === void 0) return true;
5986
+ const want = resolveAdjacencyKindMask(ctx.trivia, kinds);
5987
+ const spec = analyzeLabeledTrivia(ctx.trivia);
5988
+ return (triviaKindMaskAt(input, cur, spec, ctx.state) & want) !== 0;
5866
5989
  }
5867
- function covDefinitions(prog) {
5868
- return (prog.cov ?? []).map(([id, kind]) => ({ id, kind: covKindName(kind) }));
5990
+ function adjacencyFail(pos, def) {
5991
+ return { ok: false, expected: [adjacencyExpected(def)], span: { start: pos, end: pos } };
5869
5992
  }
5870
- function expandCompact(p) {
5871
- if ("code" in p) return p;
5872
- return ownTableProgram({
5873
- code: p.c,
5874
- k: p.k,
5875
- cc: p.x,
5876
- fx: p.e,
5877
- disp: p.d,
5878
- rules: p.r,
5879
- fns: p.f,
5880
- lines: p.l ?? 0,
5881
- dsp: p.p ?? [],
5882
- ...p.lx === void 0 ? {} : { lex: p.lx },
5883
- ...p.lp === void 0 ? {} : { lexPrograms: p.lp },
5884
- ...p.lb === void 0 ? {} : { labels: p.lb },
5885
- ...p.rc === void 0 ? {} : { classified: p.rc },
5886
- ...p.h === void 0 ? {} : { hostMode: p.h },
5887
- ...p.tv === void 0 ? {} : { triviaSpecs: p.tv },
5888
- ...p.sc === void 0 ? {} : { scans: p.sc },
5889
- ...p.ss === void 0 ? {} : { scanSkip: p.ss },
5890
- ...p.so === void 0 ? {} : { scanSkipOf: p.so },
5891
- ...p.rv === void 0 ? {} : { rec: p.rv },
5892
- ...p.cv === void 0 ? {} : { cov: p.cv },
5893
- ...p.a === void 0 ? {} : { asm: p.a }
5894
- });
5993
+ var NO_STANDALONE_PARSE = "adjacency assertions are boundary tests: use adjacent()/notAdjacent() as a NON-FIRST term of a sequence(), where the preceding term defines the gap being asserted.";
5994
+ function adjacencyMisuse(polarity) {
5995
+ return new TypeError(`${polarity}(): ${NO_STANDALONE_PARSE}`);
5895
5996
  }
5896
- function validateDispatchSpec(spec, arity, fallbackRouted) {
5897
- const invalid = (arms) => {
5898
- for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
5899
- return false;
5997
+ function adjacencyCombinator(def) {
5998
+ const meta = {
5999
+ // Zero-width and never leading (sequence() rejects index 0), so it contributes
6000
+ // no first char. `isZeroWidthAssertion` drops it from a sequence's first-set
6001
+ // rather than letting `any` poison the enclosing choice's dispatch.
6002
+ firstSet: { kind: "any" },
6003
+ canMatchNewline: false,
6004
+ isTrivia: false
5900
6005
  };
5901
- if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
5902
- throw new TypeError("table: malformed dispatch");
5903
- }
5904
- }
5905
- var TABLE_RUNTIME = /* @__PURE__ */ Symbol("parseman.tableRuntime");
5906
- function ownTableProgram(prog, resolved, caches = {}) {
5907
- const runtime = prog[TABLE_RUNTIME];
5908
6006
  return {
5909
- ...prog,
5910
- [TABLE_RUNTIME]: {
5911
- resolved,
5912
- choiceRollbackMasks: caches.choiceRollbackMasks ?? runtime?.choiceRollbackMasks,
5913
- nonCommittingChoiceSites: caches.nonCommittingChoiceSites ?? runtime?.nonCommittingChoiceSites,
5914
- failureRollbackCleanSites: caches.failureRollbackCleanSites ?? runtime?.failureRollbackCleanSites
6007
+ _tag: "adjacency",
6008
+ _meta: meta,
6009
+ _def: def,
6010
+ parse() {
6011
+ throw adjacencyMisuse(def.polarity);
5915
6012
  }
5916
6013
  };
5917
6014
  }
5918
- function choiceRollbackMask(prog, ip) {
5919
- return prog[TABLE_RUNTIME]?.choiceRollbackMasks?.get(ip);
5920
- }
5921
- function choiceCannotCommit(prog, ip) {
5922
- return prog[TABLE_RUNTIME]?.nonCommittingChoiceSites?.has(ip) === true;
5923
- }
5924
- function failureRollbackClean(prog, ip) {
5925
- return prog[TABLE_RUNTIME]?.failureRollbackCleanSites?.has(ip) === true;
6015
+ function adjacent() {
6016
+ return adjacencyCombinator({ tag: "adjacency", polarity: "adjacent" });
5926
6017
  }
5927
- var WIDE_CLASS_PREFIX_LO = 65535;
5928
- var WIDE_CLASS_PREFIX_HI = 0;
5929
- function validateClassRange(lo, hi) {
5930
- for (const cp of [lo, hi]) {
5931
- if (!Number.isInteger(cp) || cp < 0 || cp > 1114111) {
5932
- throw new TypeError(`table char-class endpoint is outside Unicode: ${cp}`);
6018
+ function notAdjacent(options) {
6019
+ const kinds = options?.kinds;
6020
+ if (kinds !== void 0) {
6021
+ if (!Array.isArray(kinds) || kinds.length === 0) {
6022
+ throw new TypeError("notAdjacent({ kinds }): kinds must be a non-empty array of trivia category names.");
6023
+ }
6024
+ for (const k of kinds) {
6025
+ if (typeof k !== "string" || k.length === 0) {
6026
+ throw new TypeError(`notAdjacent({ kinds }): every kind must be a non-empty string, got ${JSON.stringify(k)}.`);
6027
+ }
5933
6028
  }
5934
6029
  }
5935
- if (lo > hi) {
5936
- throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
5937
- }
6030
+ return adjacencyCombinator(
6031
+ kinds === void 0 ? { tag: "adjacency", polarity: "notAdjacent" } : { tag: "adjacency", polarity: "notAdjacent", kinds: [...kinds] }
6032
+ );
5938
6033
  }
5939
- function encodeClassSpec(ranges) {
5940
- let wide = false;
5941
- for (const range of ranges) {
5942
- validateClassRange(range.lo, range.hi);
5943
- if (range.hi > 65535) wide = true;
6034
+
6035
+ // src/combinators/node.ts
6036
+ function unwrapChild(child) {
6037
+ return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
6038
+ }
6039
+ function isCstChild(value) {
6040
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "parseError");
6041
+ }
6042
+ function missingInferredType() {
6043
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
6044
+ }
6045
+ function normalizeBuildArity(arity) {
6046
+ if (arity === void 0) return void 0;
6047
+ if (!Number.isInteger(arity) || arity < 0 || arity > 6) {
6048
+ throw new Error("node() buildArity must be an integer 0..6 (children, fields, span, rawChildren, triviaLog, state)");
5944
6049
  }
5945
- if (!wide) {
5946
- let spec2 = "";
5947
- for (const range of ranges) spec2 += String.fromCharCode(range.lo, range.hi);
5948
- return spec2;
6050
+ return arity;
6051
+ }
6052
+ function normalizeProject(project) {
6053
+ if (project === void 0) return void 0;
6054
+ if (!Number.isInteger(project) || project < 0) {
6055
+ throw new Error("node() project child must be a non-negative integer");
5949
6056
  }
5950
- let spec = String.fromCharCode(WIDE_CLASS_PREFIX_LO, WIDE_CLASS_PREFIX_HI);
5951
- for (const range of ranges) {
5952
- spec += String.fromCharCode(
5953
- range.lo >>> 16,
5954
- range.lo & 65535,
5955
- range.hi >>> 16,
5956
- range.hi & 65535
5957
- );
6057
+ return project;
6058
+ }
6059
+ function projectChild(children2, project, type) {
6060
+ if (!(project in children2)) {
6061
+ throw new Error(`node(${JSON.stringify(type)}) project child ${project} was not captured`);
5958
6062
  }
5959
- return spec;
6063
+ return unwrapChild(children2[project]);
5960
6064
  }
5961
- function decodeClassSpec(spec) {
5962
- if (spec.charCodeAt(0) === WIDE_CLASS_PREFIX_LO && spec.charCodeAt(1) === WIDE_CLASS_PREFIX_HI) {
5963
- if ((spec.length - 2) % 4 !== 0) throw new TypeError("table char-class spec has a truncated wide range");
5964
- if (spec.length === 2) throw new TypeError("table char-class spec has a wide prefix but no ranges");
5965
- const ranges2 = [];
5966
- for (let i = 2; i < spec.length; i += 4) {
5967
- const lo = spec.charCodeAt(i) * 65536 + spec.charCodeAt(i + 1);
5968
- const hi = spec.charCodeAt(i + 2) * 65536 + spec.charCodeAt(i + 3);
5969
- validateClassRange(lo, hi);
5970
- ranges2.push({ lo, hi });
5971
- }
5972
- return ranges2;
6065
+ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
6066
+ const hasExplicitType = typeof typeOrCombinator === "string";
6067
+ const type = hasExplicitType ? typeOrCombinator : void 0;
6068
+ const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
6069
+ const buildArg = hasExplicitType ? buildOrOpts : combinatorOrBuild;
6070
+ const build = typeof buildArg === "function" ? buildArg : void 0;
6071
+ const opts = typeof buildArg === "function" ? maybeOpts : buildArg ?? maybeOpts;
6072
+ const project = normalizeProject(opts?.project);
6073
+ if (project !== void 0 && build !== void 0) {
6074
+ throw new Error("node() options cannot combine project with a build callback");
5973
6075
  }
5974
- if (spec.length % 2 !== 0) throw new TypeError("table char-class spec has an unmatched range endpoint");
5975
- const ranges = [];
5976
- for (let i = 0; i < spec.length; i += 2) {
5977
- const lo = spec.charCodeAt(i);
5978
- const hi = spec.charCodeAt(i + 1);
5979
- validateClassRange(lo, hi);
5980
- ranges.push({ lo, hi });
6076
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
6077
+ const meta = {
6078
+ firstSet: combinator._meta.firstSet,
6079
+ canMatchNewline: combinator._meta.canMatchNewline,
6080
+ isTrivia: false
6081
+ };
6082
+ const unwrap = opts?.unwrap === true;
6083
+ const collapse = opts?.collapse === true;
6084
+ const captureTrivia = opts?.captureTrivia === true;
6085
+ const trailingTrivia = opts?.trailingTrivia === true;
6086
+ const tags = opts?.tags;
6087
+ const buildArity = normalizeBuildArity(opts?.buildArity);
6088
+ if (unwrap && collapse) {
6089
+ throw new Error("node() options cannot set both unwrap and collapse");
5981
6090
  }
5982
- return ranges;
5983
- }
5984
- function resolveClass(spec) {
5985
- const ascii = new Uint8Array(128);
5986
- const hi = [];
5987
- for (const { lo, hi: up } of decodeClassSpec(spec)) {
5988
- if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1;
5989
- if (up >= 128) hi.push(Math.max(lo, 128), up);
6091
+ if (project !== void 0 && (unwrap || collapse)) {
6092
+ throw new Error("node() options cannot combine project with unwrap or collapse");
5990
6093
  }
5991
- return { ascii, hi };
5992
- }
5993
- function classHas2(cls, code) {
5994
- if (code < 0) return false;
5995
- if (code < 128) return cls.ascii[code] === 1;
5996
- const hi = cls.hi;
5997
- for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
5998
- return false;
5999
- }
6000
- function resolveDispatch(arms, cc) {
6001
- const ascii = new Uint8Array(128);
6002
- const hi = [];
6003
- const open = [];
6004
- const armCls = [];
6005
- let exclusive = true;
6006
- let hiOwners = 0;
6007
- for (let a = 0; a < arms.length; a++) {
6008
- const ci = arms[a];
6009
- if (ci < 0) {
6010
- open.push(a);
6011
- armCls.push(null);
6012
- exclusive = false;
6013
- continue;
6014
- }
6015
- const cls = cc[ci];
6016
- armCls.push(cls);
6017
- for (let c = 0; c < 128; c++) {
6018
- if (cls.ascii[c] !== 1) continue;
6019
- if (ascii[c] === 0) ascii[c] = a + 1;
6020
- else exclusive = false;
6094
+ const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || buildArity !== void 0 || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...buildArity !== void 0 ? { buildArity } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
6095
+ const capturesTrivia = captureTrivia || (build ? buildReadsTrivia(def) : project === void 0);
6096
+ const readsState = build ? buildReadsState(def) : project === void 0;
6097
+ const hasOwnFields = parserHasOwnFields(combinator);
6098
+ const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
6099
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
6100
+ let failExpected;
6101
+ return {
6102
+ _tag: "node",
6103
+ _meta: meta,
6104
+ _def: def,
6105
+ parse(input, pos, ctx) {
6106
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
6107
+ if (failExpected === void 0) {
6108
+ const e = deriveExpected(combinator);
6109
+ failExpected = e.length > 0 ? e : [combinator._tag];
6110
+ }
6111
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
6112
+ }
6113
+ const hasDirectValue = build !== void 0 || project !== void 0;
6114
+ const hostCst = hasDirectValue && cstOutputHost(ctx.build);
6115
+ const structural = !hasDirectValue;
6116
+ const hostCapturesThisType = structural && def.type !== void 0 && ctx.build?._parsemanCaptureTrivia !== void 0 ? ctx.build._parsemanCaptureTrivia(def.type) : void 0;
6117
+ const effTrivia = structural ? captureTrivia || trailingTrivia || hostCapturesThisType !== false : capturesTrivia || hostCst;
6118
+ const effFields = capturesFields || hostCst && hasOwnFields;
6119
+ const saved = beginCstNodeCapture(ctx);
6120
+ const savedFields = ctx._fields;
6121
+ ctx._fields = effFields ? [] : void 0;
6122
+ const savedMask = ctx._triviaCaptureMask;
6123
+ if (build === void 0 && project === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
6124
+ ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
6125
+ }
6126
+ if (!effTrivia) ctx.captureTrivia = false;
6127
+ let r = combinator.parse(input, pos, ctx);
6128
+ if (r.ok && trailingTrivia && ctx.trivia) {
6129
+ const end = consumeTrivia(input, r.span.end, ctx);
6130
+ r = { ...r, span: { start: r.span.start, end } };
6131
+ }
6132
+ const fields = effFields ? buildFieldMap(ctx._fields) : void 0;
6133
+ ctx._fields = savedFields;
6134
+ ctx._triviaCaptureMask = savedMask;
6135
+ const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
6136
+ if (!r.ok) return r;
6137
+ const st = readsState ? ctx.state : void 0;
6138
+ const nodeType = def.type ?? missingInferredType();
6139
+ const cstOutput = cstOutputHost(ctx.build);
6140
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
6141
+ const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : (cstOutput || !build && project === void 0) && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, ctx.state, tags) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, ctx.state, tags) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
6142
+ const rawEntry2 = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
6143
+ if (saved.buf !== void 0 || saved.ch !== void 0) {
6144
+ pushCstChild(ctx, built, rawEntry2);
6145
+ }
6146
+ return { ok: true, value: built, span };
6021
6147
  }
6022
- if (cls.hi.length > 0 && ++hiOwners > 1) exclusive = false;
6023
- for (let i = 0; i < cls.hi.length; i += 2) hi.push(cls.hi[i], cls.hi[i + 1], a);
6024
- }
6025
- return { ascii, hi, open, armCls, exclusive };
6148
+ };
6026
6149
  }
6027
- function buildTrivia(spec) {
6028
- if (spec.live !== void 0) return spec.live;
6029
- if (spec.plain !== void 0) return trivia(regex(new RegExp(spec.plain[0], spec.plain[1])));
6030
- if (spec.alts !== void 0) {
6031
- const [head, ...rest] = spec.alts.map(([source, flags]) => regex(new RegExp(source, flags)));
6032
- if (head === void 0) throw new TypeError("trivia spec has an EMPTY alternation, which matches nothing");
6033
- const body = rest.length === 0 ? head : choice(head, ...rest);
6034
- return trivia(many(body, { min: spec.min ?? 1 }));
6150
+
6151
+ // src/combinators/trivia-boundary.ts
6152
+ var MAX_DEPTH = 12;
6153
+ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
6154
+ if (depth === 0) return true;
6155
+ const d = p._def;
6156
+ switch (d.tag) {
6157
+ // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
6158
+ case "sequence":
6159
+ return d.parsers.length > 1;
6160
+ case "many":
6161
+ case "oneOrMore":
6162
+ case "sepBy":
6163
+ return true;
6164
+ // Transparent wrappers — they add no position, so ask what they wrap.
6165
+ case "node":
6166
+ case "transform":
6167
+ case "leaf":
6168
+ case "label":
6169
+ case "field":
6170
+ case "token":
6171
+ case "attempt":
6172
+ case "optional":
6173
+ case "expect":
6174
+ case "withCtx":
6175
+ case "recover":
6176
+ case "trivia":
6177
+ return hasOwnTriviaBoundary(d.parser, depth - 1);
6178
+ // An alternation adds no position either, but ONE arm that has one is enough
6179
+ // to make the scope meaningful for this rule.
6180
+ case "choice":
6181
+ return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
6182
+ default:
6183
+ return false;
6035
6184
  }
6036
- const arms = {};
6037
- for (const [name, source, flags] of spec.arms) arms[name] = regex(new RegExp(source, flags));
6038
- return classifiedTrivia(arms);
6039
- }
6040
- function resolveTable(prog) {
6041
- const owner = prog[TABLE_RUNTIME];
6042
- const hit = owner?.resolved;
6043
- if (hit !== void 0) return hit;
6044
- const cc = prog.cc.map(resolveClass);
6045
- const triviaBuilt = (prog.triviaSpecs ?? []).map(buildTrivia);
6046
- const lex = (prog.lex ?? []).map((spec) => buildLexBodyRecognizer(spec, prog.k));
6047
- const built = {
6048
- prog,
6049
- code: Int32Array.from(prog.code),
6050
- k: prog.k,
6051
- fns: prog.fns,
6052
- cc,
6053
- fx: prog.fx,
6054
- disp: prog.disp.map((d) => resolveDispatch(d, cc)),
6055
- trivia: triviaBuilt,
6056
- triviaScan: triviaBuilt.map(fastTriviaScanner),
6057
- triviaLabelled: triviaBuilt.map((t2) => t2._meta.triviaKindLabels !== void 0),
6058
- dsp: prog.dsp.map((d) => ({
6059
- byKey: new Map(d.key.map((x, i) => [x, d.keyArm[i]])),
6060
- byFold: new Map(d.fold.map((x, i) => [x, d.foldArm[i]])),
6061
- match: d.match,
6062
- routed: d.routed,
6063
- expected: d.expected
6064
- })),
6065
- lex,
6066
- lexPrograms: (prog.lexPrograms ?? []).map((spec) => buildLexProgramRunner(spec, lex, cc, prog.fx)),
6067
- rules: prog.rules
6068
- };
6069
- if (owner !== void 0) owner.resolved = built;
6070
- return built;
6071
6185
  }
6072
6186
 
6073
6187
  // src/compiler/direct-projection.ts
@@ -6618,6 +6732,7 @@ var Encoder = class {
6618
6732
  case "expect":
6619
6733
  case "recover":
6620
6734
  case "scanTo":
6735
+ case "lines":
6621
6736
  case "unknown":
6622
6737
  return true;
6623
6738
  case "lazy": {
@@ -6634,6 +6749,33 @@ var Encoder = class {
6634
6749
  return childrenOf(def);
6635
6750
  }
6636
6751
  }
6752
+ /**
6753
+ * THE FIRST-CHARACTER GATE OF AN `optional`, `not` OR `peek` CHILD.
6754
+ *
6755
+ * A non-nullable child cannot match at a character outside its first set, so
6756
+ * the wrapper's outcome there is already decided: `optional` yields `null`,
6757
+ * `not` succeeds, `peek` fails with its own set. The interpreter's `optional`
6758
+ * makes that test itself (`repeat.ts` `startsFirstSet`); the table entered the
6759
+ * child — a rollback mark, the call, the child's own failure — to learn it.
6760
+ *
6761
+ * The gate is an `OP_GATE` row around the child, so every engine runs it
6762
+ * unchanged, a probe or tolerant parse forwards through it as for a node's
6763
+ * gate, and the wrapper's row keeps its layout. The engines test the class
6764
+ * before taking the wrapper's mark. The first set is resolved through
6765
+ * `winners`, the analysis a choice arm's gate uses, so a composed rule's set
6766
+ * is the merged one. A scalar `not` child stays bare: `scalarTerminalNotChild`
6767
+ * already reduces it to one recognizer call.
6768
+ */
6769
+ lookaheadGate(p, child) {
6770
+ const op = this.code[child];
6771
+ if (op === OP_GATE || op === OP_LIT || op === OP_LIT_CI || op === OP_LIT_TRACK || op === OP_LIT_CI_TRACK || op === OP_RX || op === OP_RX_TRACK) return child;
6772
+ const rr = this.refResolver();
6773
+ if (matchesEmpty(p, /* @__PURE__ */ new Set(), rr)) return child;
6774
+ const cls = this.charClass(firstSetOf(p, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
6775
+ if (cls < 0) return child;
6776
+ const e = deriveExpected(p);
6777
+ return this.emit(OP_GATE, cls, child, this.expected(e.length > 0 ? e : [p._tag]));
6778
+ }
6637
6779
  /** A first set becomes a char class string, or −1 for `any`. */
6638
6780
  charClass(fs) {
6639
6781
  if (fs.kind !== "ranges" || fs.ranges.length === 0) return -1;
@@ -6645,12 +6787,19 @@ var Encoder = class {
6645
6787
  this.ccIndex.set(spec, i);
6646
6788
  return i;
6647
6789
  }
6648
- /** Bind a guard only when its class is already part of the program. Repeat
6649
- * gating must not grow the compact artifact merely to avoid a failed call. */
6650
- existingCharClass(fs) {
6651
- if (fs.kind !== "ranges" || fs.ranges.length === 0) return -1;
6652
- const hit = this.ccIndex.get(encodeClassSpec(fs.ranges));
6653
- return hit !== void 0 && hit < 100 ? hit : -1;
6790
+ /**
6791
+ * The first set a node's or an attempt's fail-fast gate tests. A bare
6792
+ * cross-rule ref resolves through `winners` (a compose OVERRIDE may widen
6793
+ * it). Any other body keeps its construction-time `_meta.firstSet`, except
6794
+ * where that is `any`: under `compose()` a body that reaches a rule of
6795
+ * another piece is built before that rule exists, so its cached set is
6796
+ * `any` and the node got NO gate (jess's `CompoundSelector` was entered at
6797
+ * `{`, `Call` at `}`). The resolved set is the one a choice arm's gate uses,
6798
+ * over the final merged map.
6799
+ */
6800
+ gateFirstSet(p) {
6801
+ const cached = p._meta.firstSet;
6802
+ return p._def.tag === "lazy" || cached.kind === "any" ? firstSetOf(p, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : cached;
6654
6803
  }
6655
6804
  /**
6656
6805
  * Wrap one choice arm in its `autoNot` checks (`OP_REJECT`).
@@ -6838,6 +6987,18 @@ var Encoder = class {
6838
6987
  open: bal.open,
6839
6988
  close: bal.close
6840
6989
  }));
6990
+ if (d.tag === "lines") {
6991
+ return this.emit(OP_SCAN, this.scanSlot({
6992
+ kind: 2,
6993
+ flags: 0,
6994
+ skip: [],
6995
+ prefix: this.subtree(d.prefix),
6996
+ body: this.subtree(d.body),
6997
+ ...d.first === void 0 ? {} : { first: this.subtree(d.first) },
6998
+ ...d.lazy === void 0 ? {} : { lazy: this.subtree(d.lazy) }
6999
+ }));
7000
+ }
7001
+ if (d.tag === "columns") return this.emit(OP_SCAN, this.scanSlot({ kind: 3, flags: 0, skip: [], n: d.n }));
6841
7002
  if (d.tag === "scanTo") {
6842
7003
  const sentDef = d.sentinel._def;
6843
7004
  return this.emit(OP_SCAN, this.scanSlot({
@@ -6943,6 +7104,12 @@ var Encoder = class {
6943
7104
  if (transactionalFirst[i] !== void 0) classes[i] = -1;
6944
7105
  }
6945
7106
  }
7107
+ if (d.gates.some((g) => g !== null && g !== void 0) && !dispatchIsExclusive(classes, this.cc)) {
7108
+ for (let k = 0; k < classes.length; k++) {
7109
+ const gate = d.gates[order[k]];
7110
+ if (gate !== null && gate !== void 0) classes[k] = -1;
7111
+ }
7112
+ }
6946
7113
  const dispIdx = this.disp.length;
6947
7114
  this.disp.push(classes);
6948
7115
  let rollbackMask = kids.length > 31 ? -1 : 0;
@@ -6972,7 +7139,7 @@ var Encoder = class {
6972
7139
  case "oneOrMore": {
6973
7140
  const child = this.node(d.parser).ip;
6974
7141
  const rr = this.refResolver();
6975
- const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.existingCharClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
7142
+ const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.charClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
6976
7143
  const op = d.valueUnused ? OP_REPV : OP_REP;
6977
7144
  const min = d.tag === "many" ? 0 : d.min;
6978
7145
  return this.rec ? this.emit(op, child, min, d.max ?? -1, -1, 0, this.expected(deriveExpected(d.parser)), itemClass) : this.emit(op, child, min, d.max ?? -1, -1, 0);
@@ -6996,7 +7163,7 @@ var Encoder = class {
6996
7163
  return d.min >= 2 ? this.emit(OP_REP, child, d.min, d.max ?? -1, sep, flags, this.expected(deriveExpected(d.parser))) : this.emit(OP_REP, child, d.min, d.max ?? -1, sep, flags);
6997
7164
  }
6998
7165
  case "optional": {
6999
- const head = this.emit(OP_OPT, this.node(d.parser).ip);
7166
+ const head = this.emit(OP_OPT, this.lookaheadGate(d.parser, this.node(d.parser).ip));
7000
7167
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(head);
7001
7168
  return head;
7002
7169
  }
@@ -7044,8 +7211,8 @@ var Encoder = class {
7044
7211
  // every CST node, so this is load-bearing, not reflection trivia.
7045
7212
  d.tags === void 0 ? -1 : this.constant(d.tags)
7046
7213
  );
7047
- if (matchesEmpty(d.parser)) return body;
7048
- const fs = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
7214
+ if (matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return body;
7215
+ const fs = this.gateFirstSet(d.parser);
7049
7216
  if (fs.kind === "any") return body;
7050
7217
  const cls = this.charClass(fs);
7051
7218
  if (cls < 0) return body;
@@ -7137,7 +7304,7 @@ var Encoder = class {
7137
7304
  // other engine reported `['not(literal)']`. It hid because `expected` is a
7138
7305
  // TOP-LEVEL field on `RunResult` and is NOT part of the identity digest.
7139
7306
  case "not":
7140
- return this.emit(OP_NOT, this.node(d.parser).ip, this.expected(assertionFailureExpected(false, d.parser._tag)));
7307
+ return this.emit(OP_NOT, this.lookaheadGate(d.parser, this.node(d.parser).ip), this.expected(assertionFailureExpected(false, d.parser._tag)));
7141
7308
  case "guard":
7142
7309
  return this.emit(OP_GUARD, this.fn(d.predicate, d.predSrc ?? null), this.expected(["gate"]));
7143
7310
  case "adjacency":
@@ -7158,14 +7325,14 @@ var Encoder = class {
7158
7325
  return this.emit(OP_WITHCTX, this.constant(d.extra), this.node(d.parser).ip);
7159
7326
  }
7160
7327
  case "peek":
7161
- return this.emit(OP_PEEK, this.node(d.parser).ip, this.expected(assertionFailureExpected(true, d.parser._tag)));
7328
+ return this.emit(OP_PEEK, this.lookaheadGate(d.parser, this.node(d.parser).ip), this.expected(assertionFailureExpected(true, d.parser._tag)));
7162
7329
  // A TRANSACTION IS A ROW. See `OP_ATTEMPT` for why the transparent
7163
7330
  // lowering was correct only for a choice arm.
7164
7331
  case "attempt": {
7165
7332
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0);
7166
7333
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
7167
- const fs = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
7168
- if (fs.kind === "any" || matchesEmpty(d.parser)) return inner;
7334
+ const fs = this.gateFirstSet(d.parser);
7335
+ if (fs.kind === "any" || matchesEmpty(d.parser, /* @__PURE__ */ new Set(), this.refResolver())) return inner;
7169
7336
  const cls = this.charClass(fs);
7170
7337
  if (cls < 0) return inner;
7171
7338
  const e = deriveExpected(d.parser);
@@ -7284,12 +7451,7 @@ var Encoder = class {
7284
7451
  };
7285
7452
  const seen = /* @__PURE__ */ new Set();
7286
7453
  const refs = [];
7287
- for (const s of this.scans) {
7288
- if (s.sentinel !== void 0) refs.push(s.sentinel);
7289
- if (s.recoverAt !== void 0) refs.push(s.recoverAt);
7290
- if (s.stopAt !== void 0) refs.push(s.stopAt);
7291
- refs.push(...s.skip);
7292
- }
7454
+ for (const s of this.scans) refs.push(...scanRefs(s));
7293
7455
  for (const set of this.scanSkipSets) refs.push(...set);
7294
7456
  const stack = [...Object.values(this.rules), ...refs.map((r) => r[0])];
7295
7457
  while (stack.length > 0) {
@@ -7309,7 +7471,11 @@ var Encoder = class {
7309
7471
  skip: s.skip.map(res),
7310
7472
  ...s.sentinel === void 0 ? {} : { sentinel: res(s.sentinel) },
7311
7473
  ...s.recoverAt === void 0 ? {} : { recoverAt: res(s.recoverAt) },
7312
- ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) }
7474
+ ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) },
7475
+ ...s.prefix === void 0 ? {} : { prefix: res(s.prefix) },
7476
+ ...s.body === void 0 ? {} : { body: res(s.body) },
7477
+ ...s.first === void 0 ? {} : { first: res(s.first) },
7478
+ ...s.lazy === void 0 ? {} : { lazy: res(s.lazy) }
7313
7479
  }));
7314
7480
  this.scanSkipSets = this.scanSkipSets.map((set) => set.map(res));
7315
7481
  }
@@ -8151,7 +8317,7 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8151
8317
  if (shape.kind === "alt") {
8152
8318
  const okV = mint("_ok");
8153
8319
  const endV2 = mint("_end");
8154
- const lines = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
8320
+ const lines2 = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
8155
8321
  if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
8156
8322
  const groups = /* @__PURE__ */ new Map();
8157
8323
  for (const a of shape.arms) {
@@ -8160,60 +8326,60 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8160
8326
  const lower = f >= 65 && f <= 90 ? f + 32 : f;
8161
8327
  (groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
8162
8328
  }
8163
- lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8329
+ lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8164
8330
  for (const [lower, arms] of groups) {
8165
8331
  const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
8166
- lines.push(`${ind} ${labels} {`);
8332
+ lines2.push(`${ind} ${labels} {`);
8167
8333
  for (const open of arms) {
8168
8334
  const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
8169
8335
  const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
8170
- lines.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
8336
+ lines2.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
8171
8337
  }
8172
- lines.push(`${ind} break`);
8173
- lines.push(`${ind} }`);
8338
+ lines2.push(`${ind} break`);
8339
+ lines2.push(`${ind} }`);
8174
8340
  }
8175
- lines.push(`${ind}}`);
8176
- return { setup: lines, ok: okV, end: endV2 };
8341
+ lines2.push(`${ind}}`);
8342
+ return { setup: lines2, ok: okV, end: endV2 };
8177
8343
  }
8178
8344
  if (shape.disjoint) {
8179
8345
  const plan = planAltSwitch(shape.firsts);
8180
8346
  if (plan.kind === "switch") {
8181
- lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8347
+ lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8182
8348
  shape.arms.forEach((arm, k) => {
8183
8349
  const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
8184
8350
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8185
- lines.push(`${ind} ${labels} {`);
8186
- lines.push(...m.setup);
8187
- lines.push(`${ind} ${okV} = ${m.ok}`);
8188
- lines.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8189
- lines.push(`${ind} break`);
8190
- lines.push(`${ind} }`);
8351
+ lines2.push(`${ind} ${labels} {`);
8352
+ lines2.push(...m.setup);
8353
+ lines2.push(`${ind} ${okV} = ${m.ok}`);
8354
+ lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8355
+ lines2.push(`${ind} break`);
8356
+ lines2.push(`${ind} }`);
8191
8357
  });
8192
- lines.push(`${ind}}`);
8358
+ lines2.push(`${ind}}`);
8193
8359
  } else {
8194
8360
  const c0 = codeAt(start, firstChar);
8195
8361
  shape.arms.forEach((arm, k) => {
8196
8362
  const fs = shape.firsts[k];
8197
8363
  const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
8198
8364
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8199
- lines.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
8200
- lines.push(...m.setup);
8201
- lines.push(`${ind} ${okV} = ${m.ok}`);
8202
- lines.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8203
- lines.push(`${ind}}`);
8365
+ lines2.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
8366
+ lines2.push(...m.setup);
8367
+ lines2.push(`${ind} ${okV} = ${m.ok}`);
8368
+ lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8369
+ lines2.push(`${ind}}`);
8204
8370
  });
8205
8371
  }
8206
8372
  } else {
8207
8373
  const lbl = mint("_alt");
8208
- lines.push(`${ind}${lbl}: {`);
8374
+ lines2.push(`${ind}${lbl}: {`);
8209
8375
  for (const arm of shape.arms) {
8210
8376
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8211
- lines.push(...m.setup);
8212
- lines.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
8377
+ lines2.push(...m.setup);
8378
+ lines2.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
8213
8379
  }
8214
- lines.push(`${ind}}`);
8380
+ lines2.push(`${ind}}`);
8215
8381
  }
8216
- return { setup: lines, ok: okV, end: endV2 };
8382
+ return { setup: lines2, ok: okV, end: endV2 };
8217
8383
  }
8218
8384
  if (shape.kind === "chars") {
8219
8385
  const cur = mint("_e");
@@ -8248,64 +8414,64 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8248
8414
  const c = classCond(`input.charCodeAt(${at})`, p.ranges);
8249
8415
  return p.negated ? `!(${c})` : `(${c})`;
8250
8416
  };
8251
- const lines = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
8417
+ const lines2 = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
8252
8418
  let first = true;
8253
8419
  for (const p of shape.parts) {
8254
8420
  if (p.part === "lit") {
8255
8421
  const L = p.cps.length;
8256
8422
  const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
8257
8423
  if (p.optional) {
8258
- lines.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
8424
+ lines2.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
8259
8425
  } else {
8260
- lines.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
8261
- lines.push(`${ind} ${endV2} += ${L}`);
8426
+ lines2.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
8427
+ lines2.push(`${ind} ${endV2} += ${L}`);
8262
8428
  }
8263
8429
  } else if (p.part === "run" && p.max === Infinity) {
8264
8430
  const s = mint("_s");
8265
- lines.push(`${ind} const ${s} = ${endV2}`);
8266
- lines.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8267
- if (p.min === 1) lines.push(`${ind} if (${endV2} === ${s}) break`);
8268
- else if (p.min > 1) lines.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
8431
+ lines2.push(`${ind} const ${s} = ${endV2}`);
8432
+ lines2.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8433
+ if (p.min === 1) lines2.push(`${ind} if (${endV2} === ${s}) break`);
8434
+ else if (p.min > 1) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
8269
8435
  } else if (p.part === "run" && p.max === 1) {
8270
8436
  if (p.min === 1) {
8271
- lines.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
8272
- lines.push(`${ind} ${endV2}++`);
8437
+ lines2.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
8438
+ lines2.push(`${ind} ${endV2}++`);
8273
8439
  } else {
8274
- lines.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8440
+ lines2.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8275
8441
  }
8276
8442
  } else if (p.part === "run") {
8277
8443
  const s = mint("_s");
8278
- lines.push(`${ind} const ${s} = ${endV2}`);
8279
- lines.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8280
- if (p.min > 0) lines.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
8444
+ lines2.push(`${ind} const ${s} = ${endV2}`);
8445
+ lines2.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8446
+ if (p.min > 0) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
8281
8447
  } else {
8282
8448
  const inner = emitShapeMatch(p.inner, endV2, mint, `${ind} `, first ? firstChar : void 0);
8283
8449
  if (!p.unbounded) {
8284
- lines.push(...inner.setup);
8450
+ lines2.push(...inner.setup);
8285
8451
  if (p.min === 1) {
8286
- lines.push(`${ind} if (!(${inner.ok})) break`);
8287
- lines.push(`${ind} ${endV2} = ${inner.end}`);
8452
+ lines2.push(`${ind} if (!(${inner.ok})) break`);
8453
+ lines2.push(`${ind} ${endV2} = ${inner.end}`);
8288
8454
  } else {
8289
- lines.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
8455
+ lines2.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
8290
8456
  }
8291
8457
  } else {
8292
8458
  const progV = mint("_prog");
8293
- lines.push(`${ind} let ${progV} = false`);
8294
- lines.push(`${ind} while (true) {`);
8295
- lines.push(...inner.setup);
8296
- lines.push(`${ind} if (!(${inner.ok})) break`);
8297
- lines.push(`${ind} ${endV2} = ${inner.end}`);
8298
- lines.push(`${ind} ${progV} = true`);
8299
- lines.push(`${ind} }`);
8300
- if (p.min === 1) lines.push(`${ind} if (!${progV}) break`);
8459
+ lines2.push(`${ind} let ${progV} = false`);
8460
+ lines2.push(`${ind} while (true) {`);
8461
+ lines2.push(...inner.setup);
8462
+ lines2.push(`${ind} if (!(${inner.ok})) break`);
8463
+ lines2.push(`${ind} ${endV2} = ${inner.end}`);
8464
+ lines2.push(`${ind} ${progV} = true`);
8465
+ lines2.push(`${ind} }`);
8466
+ if (p.min === 1) lines2.push(`${ind} if (!${progV}) break`);
8301
8467
  }
8302
8468
  }
8303
8469
  first = false;
8304
8470
  }
8305
- lines.push(`${ind} ${okV} = true`);
8306
- lines.push(`${ind}} while (false)`);
8307
- lines.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
8308
- return { setup: lines, ok: okV, end: endV2 };
8471
+ lines2.push(`${ind} ${okV} = true`);
8472
+ lines2.push(`${ind}} while (false)`);
8473
+ lines2.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
8474
+ return { setup: lines2, ok: okV, end: endV2 };
8309
8475
  }
8310
8476
  if (shape.kind === "litFold") {
8311
8477
  const openLen2 = shape.open.length;
@@ -8891,7 +9057,10 @@ var EMITTED_PARAMS = [
8891
9057
  // Appended for trivia-attributing fields (`field(…, { leadingTrivia })`);
8892
9058
  // old factories ignore them.
8893
9059
  "LEAD",
8894
- "triviaFieldCapture"
9060
+ "triviaFieldCapture",
9061
+ // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
9062
+ // factories ignore it.
9063
+ "skipDeferredTrivia"
8895
9064
  ];
8896
9065
  var RUNTIME_PRELUDE = `
8897
9066
  let _pfScan=null
@@ -8912,7 +9081,7 @@ function _skipTrivia(input,cur,ctx){
8912
9081
  const s=_pfScan
8913
9082
  if(s!==null&&ctx._triviaLog===undefined&&!(ctx.captureTrivia===true&&(ctx._cstBuf!==undefined||ctx._cstTriviaLog!==undefined)))return s(input,cur)
8914
9083
  if(s!==null)return skipTriviaScanned(s,input,cur,ctx)
8915
- if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
9084
+ if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
8916
9085
  return advanceTrivia(input,cur,ctx)
8917
9086
  }
8918
9087
  function _pushLeaf(ctx,value,s,e){pushCstLeaf(ctx,{_tag:'leaf',value,span:{start:s,end:e}})}
@@ -9119,7 +9288,7 @@ function rebuildPools(cc, fx, disp, plan) {
9119
9288
  })
9120
9289
  };
9121
9290
  }
9122
- function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false) {
9291
+ function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false, select) {
9123
9292
  const { code, k, fx, disp, dsp, triviaLabelled } = t2;
9124
9293
  const swapLegal = !cfg.trackLines;
9125
9294
  const hostCst = cfg.hostCst;
@@ -9218,6 +9387,8 @@ function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false) {
9218
9387
  };
9219
9388
  const bodies = [];
9220
9389
  const byIp = /* @__PURE__ */ new Map();
9390
+ const externals = [];
9391
+ const siteBytes = /* @__PURE__ */ new Map();
9221
9392
  const reached = /* @__PURE__ */ new Set();
9222
9393
  const prelude = [];
9223
9394
  const skipDefs = [];
@@ -9353,6 +9524,18 @@ return 1
9353
9524
  tokenDecisionRefs.set(dispatchIp, made);
9354
9525
  return made;
9355
9526
  }
9527
+ function gateMissTest(gip) {
9528
+ const resolved = t2.cc[code[gip + 1]];
9529
+ const ci = classes.push([resolved]) - 1;
9530
+ classPlan.push([code[gip + 1]]);
9531
+ const cls = hoist("cl", `CLS[${ci}][0]`);
9532
+ return isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`;
9533
+ }
9534
+ function lookaheadMiss(child, miss) {
9535
+ if (code[child] !== OP_GATE || cfg.tolerant || cfg.probe) return "";
9536
+ return `if(${gateMissTest(child)}){${miss}}
9537
+ `;
9538
+ }
9356
9539
  function skipFor(l) {
9357
9540
  if (l.tri < 0) return "_skipTrivia";
9358
9541
  const ki = l.tri;
@@ -9363,8 +9546,8 @@ return 1
9363
9546
  const nm = `_sk${skipPool.size}`;
9364
9547
  skipPool.set(key, nm);
9365
9548
  if (!hasScan) {
9366
- skipDefs.push(l.buf ? `function ${nm}(input,cur,ctx){return commitTriviaScan(scanTriviaCompact(input,cur,ctx))}` : `function ${nm}(input,cur,ctx){
9367
- if(needsDeferredTriviaCommit(ctx))return commitTriviaScan(scanTriviaCompact(input,cur,ctx))
9549
+ skipDefs.push(l.buf ? `function ${nm}(input,cur,ctx){return skipDeferredTrivia(input,cur,ctx)}` : `function ${nm}(input,cur,ctx){
9550
+ if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
9368
9551
  return advanceTrivia(input,cur,ctx)}`);
9369
9552
  return nm;
9370
9553
  }
@@ -9409,9 +9592,17 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
9409
9592
  const target = resolveAlias(ip);
9410
9593
  const done = byIp.get(target);
9411
9594
  if (done !== void 0) return done;
9595
+ if (select !== void 0 && !select.has(target)) {
9596
+ const ext = `_px${target}`;
9597
+ byIp.set(target, ext);
9598
+ externals.push(target);
9599
+ return ext;
9600
+ }
9412
9601
  const fname = `_pf${target}`;
9413
9602
  byIp.set(target, fname);
9414
- bodies.push(lower(target, fname));
9603
+ const body = lower(target, fname);
9604
+ siteBytes.set(target, body.length);
9605
+ bodies.push(body);
9415
9606
  return fname;
9416
9607
  }
9417
9608
  function resolveAlias(ip) {
@@ -9551,14 +9742,28 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
9551
9742
  return `${head}EC.e=pos;return ''}`;
9552
9743
  case OP_GATE: {
9553
9744
  const child = link(code[ip + 2]);
9554
- const resolved = t2.cc[code[ip + 1]];
9555
- const ci = classes.push([resolved]) - 1;
9556
- classPlan.push([code[ip + 1]]);
9557
- const cls = hoist("cl", `CLS[${ci}][0]`);
9558
9745
  const xf = fxRef(code[ip + 3]);
9559
9746
  return `${head}
9560
- if(${staticBuild && isAsciiOnlyClass(resolved) ? `${cls}.ascii[input.charCodeAt(pos)]!==1` : `!classHas(${cls},lead(input,pos))`}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
9747
+ if(${gateMissTest(ip)}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
9561
9748
  return ${child}(input,pos,ctx)
9749
+ }`;
9750
+ }
9751
+ case OP_ARMGATE: {
9752
+ const pred = fnRef(code[ip + 1]);
9753
+ const child = link(code[ip + 2]);
9754
+ const xf = fxRef(code[ip + 3]);
9755
+ return `${head}
9756
+ if(!${pred}(ctx.state)){ctx._fc=false;ctx._fe=-1;ctx._fx=${xf};return FAIL}
9757
+ return ${child}(input,pos,ctx)
9758
+ }`;
9759
+ }
9760
+ case OP_WITHCTX: {
9761
+ const ki = code[ip + 1];
9762
+ const child = link(code[ip + 2]);
9763
+ return `${head}
9764
+ const saved=ctx.state
9765
+ ctx.state=${ki < 0 ? `${fnRef(~ki)}(saved)` : kRef(ki)}
9766
+ try{return ${child}(input,pos,ctx)}finally{ctx.state=saved}
9562
9767
  }`;
9563
9768
  }
9564
9769
  case OP_XFORM: {
@@ -9814,7 +10019,7 @@ return FAIL
9814
10019
  const xf = fxRef(code[ip + 2]);
9815
10020
  const p = tmp();
9816
10021
  return `${head}
9817
- ${emitMark(p, L.buf, L.raw, sinks)}
10022
+ ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${emitMark(p, L.buf, L.raw, sinks)}
9818
10023
  const v=${child}(input,pos,ctx)
9819
10024
  ${emitRollback(p, L.buf, L.raw, sinks)}
9820
10025
  if(v===FAIL){EC.e=pos;return null}
@@ -9828,7 +10033,7 @@ return FAIL
9828
10033
  const xf = fxRef(code[ip + 2]);
9829
10034
  const p = tmp();
9830
10035
  return `${head}
9831
- ${emitMark(p, L.buf, L.raw, sinks)}
10036
+ ${lookaheadMiss(code[ip + 1], `ctx._fe=pos;ctx._fx=${xf};return FAIL`)}${emitMark(p, L.buf, L.raw, sinks)}
9832
10037
  const v=${child}(input,pos,ctx)
9833
10038
  ${emitRollback(p, L.buf, L.raw, sinks)}
9834
10039
  if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
@@ -9841,7 +10046,7 @@ return null
9841
10046
  const clean = !REC && failureRollbackClean(prog, ip);
9842
10047
  const p = tmp();
9843
10048
  return `${head}
9844
- ${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
10049
+ ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${clean ? "" : emitMark(p, L.buf, L.raw, sinks)}
9845
10050
  ctx._fc=false
9846
10051
  const v=${child}(input,pos,ctx)
9847
10052
  if(v===FAIL){
@@ -9922,7 +10127,7 @@ return v
9922
10127
  const asc = hoist("as", `DISP[${di}].ascii`);
9923
10128
  const hiArr = table.hi;
9924
10129
  const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join("\n");
9925
- const directAscii = staticBuild && table.hi.length === 0;
10130
+ const directAscii = table.hi.length === 0;
9926
10131
  return `${head}
9927
10132
  const c=${directAscii ? "input.charCodeAt(pos)" : "lead(input,pos)"}
9928
10133
  let arm=-1
@@ -9949,7 +10154,7 @@ return FAIL
9949
10154
  const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]));
9950
10155
  const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null);
9951
10156
  const maskable = n <= 32;
9952
- const directAsciiMask = staticBuild && maskable && gates.every(isAsciiOnlyClass);
10157
+ const directAsciiMask = maskable && gates.every(isAsciiOnlyClass);
9953
10158
  const gateRefs = directAsciiMask ? [] : Array.from({ length: n }, (_, i) => hoist("g", `DISP[${di}].armCls[${i}]`));
9954
10159
  const tokenCandidate = tokenChoiceCandidates.get(ip);
9955
10160
  const pretests = Array.from({ length: n }, (_, i) => {
@@ -9991,7 +10196,7 @@ return FAIL
9991
10196
  if (choiceCannotCommit(prog, ip) && !startFailureExact) {
9992
10197
  const deepAt = Array.from({ length: n }, () => tmp());
9993
10198
  const deepFx = Array.from({ length: n }, () => tmp());
9994
- const remember = (i, at, expectedSet) => `${deepAt[i]}=${at};${deepFx[i]}=${expectedSet}`;
10199
+ const remember2 = (i, at, expectedSet) => `${deepAt[i]}=${at};${deepFx[i]}=${expectedSet}`;
9995
10200
  const lazyArms = (ascii) => arms.map((arm, i) => {
9996
10201
  const pretest = pretests[i];
9997
10202
  const decision = pretest?.token === void 0 && pretest?.sequence === void 0 ? "" : tmp();
@@ -10003,13 +10208,13 @@ return FAIL
10003
10208
  if(${decision}===0){
10004
10209
  ctx._fc=false
10005
10210
  {const at=_pfTokEnd
10006
- ${remember(i, "at", pretest.token.expected)}}
10211
+ ${remember2(i, "at", pretest.token.expected)}}
10007
10212
  }`;
10008
10213
  const sequenceMiss = pretest?.sequence === void 0 ? "" : `
10009
10214
  else if(${ascii ? `(bits&${1 << i})!==0&&` : ""}${decision}<0){
10010
10215
  ctx._fc=false
10011
10216
  {const at=_pfSeqEnd
10012
- ${remember(i, "at", pretest.sequence.expected.map((value, at) => `${decision}===${~at}?${value}:`).join("") + pretest.sequence.expected.at(-1))}}
10217
+ ${remember2(i, "at", pretest.sequence.expected.map((value, at) => `${decision}===${~at}?${value}:`).join("") + pretest.sequence.expected.at(-1))}}
10013
10218
  }`;
10014
10219
  return `${decision === "" ? "" : `let ${decision}=${pretest?.sequence === void 0 ? -1 : 0}
10015
10220
  `}if(${gate}){
@@ -10017,7 +10222,7 @@ ctx._fc=false
10017
10222
  {const v=${arm}(input,pos,ctx)
10018
10223
  if(v!==FAIL)return v}
10019
10224
  {const at=ctx._fe??pos
10020
- ${remember(i, "at", "ctx._fx")}}
10225
+ ${remember2(i, "at", "ctx._fx")}}
10021
10226
  ${rollbackFor(i)}
10022
10227
  }${routeMiss}${sequenceMiss}`;
10023
10228
  }).join("\n");
@@ -10130,7 +10335,7 @@ const bits=${maskName}[${directAsciiMask ? "c" : "c<0?128:c"}]
10130
10335
  if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
10131
10336
  ${startFailureExact ? "" : "let prev=0"}
10132
10337
  ${maskArms}
10133
- ${startFailureExact ? "" : `if(best===pos)acc=${catchName}(${n},prev,acc)`}
10338
+ ${startFailureExact ? "" : `if(best===pos&&prev!==0)acc=${catchName}(${n},prev,acc)`}
10134
10339
  ctx._fe=pos;ctx._fx=${startFailureExact ? `best===pos?${choiceFx}:acc??${choiceFx}` : `acc??${choiceFx}`}
10135
10340
  return FAIL
10136
10341
  }
@@ -10229,7 +10434,7 @@ return [key,v]
10229
10434
  const ci = classes.push([resolved]) - 1;
10230
10435
  classPlan.push([itemClassIndex]);
10231
10436
  itemCls = hoist("ri", `CLS[${ci}][0]`);
10232
- itemAscii = staticBuild && isAsciiOnlyClass(resolved);
10437
+ itemAscii = isAsciiOnlyClass(resolved);
10233
10438
  }
10234
10439
  }
10235
10440
  const itemMiss = (at) => itemAscii ? `${itemCls}.ascii[input.charCodeAt(${at})]!==1` : `!classHas(${itemCls},lead(input,${at}))`;
@@ -10455,7 +10660,7 @@ return nd
10455
10660
  const keepChildren = !structural || cfg.hostReadsChildren !== false || collapse || unwrap;
10456
10661
  const omitsRaw = !hostCst && build !== void 0 && proj < 0 && (flags & 2) !== 0;
10457
10662
  const ty = q(type);
10458
- const stArg = readsState ? "st" : "(ctx.state!==undefined?Object.assign({},ctx.state):undefined)";
10663
+ const stArg = "ctx.state";
10459
10664
  const hostCall = `_pfHost(${ty},hostKids,fieldMap,span,rawKids,tlog,${stArg},${tags})`;
10460
10665
  let value;
10461
10666
  if (proj >= 0) {
@@ -10516,7 +10721,7 @@ ctx.captureTrivia=sCap
10516
10721
  if(v===FAIL)return FAIL
10517
10722
  const end=EC.e
10518
10723
  const span=${tracked ? "spanLines(ctx,pos,end)" : "{start:pos,end}"}
10519
- const st=${readsState ? "(ctx.state!==undefined?Object.assign({},ctx.state):undefined)" : "undefined"}
10724
+ const st=${readsState ? "ctx.state" : "undefined"}
10520
10725
  let nd
10521
10726
  ${unwrap ? "if(kids.length===1)nd=unwrapChild(kids[0])\nelse " : ""}${collapse ? "if(kids.length===1)nd=kids[0]\nelse " : ""}${collapsible ? `if(_pfHost!==undefined&&_pfHost._parsemanCstCollapse!==undefined&&kids.length===1&&rawKids.length===1&&_pfHost._parsemanCstCollapse(${ty},kids[0],kids,rawKids))nd=kids[0]
10522
10727
  else ` : ""}{${value}}
@@ -10529,21 +10734,51 @@ return nd
10529
10734
  throw new Unemittable(`OP_${OP_NAMES[op] ?? String(op)}`);
10530
10735
  }
10531
10736
  }
10737
+ const served = (ip) => select === void 0 || select.has(resolveAlias(ip));
10532
10738
  const ruleEntries = [];
10533
10739
  for (const [rname, entryIp] of Object.entries(prog.rules)) {
10740
+ if (!served(entryIp)) continue;
10534
10741
  const target = link(entryIp);
10535
10742
  const rn = `_r_${rname.replace(/[^A-Za-z0-9_$]/g, "_")}`;
10536
10743
  bodies.push(`const ${rn}=${target}`);
10537
10744
  ruleEntries.push(`${q(rname)}:${rn}`);
10538
10745
  }
10539
10746
  const extra = [];
10540
- for (const ip of extraIps) extra.push(`${ip}:${link(ip)}`);
10747
+ const exported = /* @__PURE__ */ new Set();
10748
+ for (const ip of extraIps) {
10749
+ if (!served(ip)) continue;
10750
+ exported.add(ip);
10751
+ extra.push(`${ip}:${link(ip)}`);
10752
+ }
10753
+ if (select !== void 0) {
10754
+ const kids = [];
10755
+ for (const ip of reachable) {
10756
+ if (select.has(resolveAlias(ip))) continue;
10757
+ kids.length = 0;
10758
+ childSlots(code, ip, kids);
10759
+ for (const kid of kids) {
10760
+ const target = resolveAlias(kid);
10761
+ if (!select.has(target) || exported.has(target)) continue;
10762
+ exported.add(target);
10763
+ extra.push(`${target}:${link(target)}`);
10764
+ }
10765
+ }
10766
+ }
10767
+ const hybrid = select === void 0 ? "" : `,
10768
+ ext:[${externals.join(",")}],
10769
+ bind:function(xs,ps){_xScan=xs
10770
+ ${externals.map((ip, i) => `_xp${ip}=ps[${i}]`).join("\n")}},
10771
+ scan:function(s){const o=_pfScan;_pfScan=s;return o}`;
10772
+ const externalDefs = select === void 0 ? "" : `let _xScan
10773
+ ${externals.map((ip) => `let _xp${ip}
10774
+ function _px${ip}(input,pos,ctx){const o=_xScan(_pfScan);const v=_xp${ip}(input,pos,ctx);_xScan(o);return v}`).join("\n")}
10775
+ `;
10541
10776
  const source = `${RUNTIME_PRELUDE}${needsNoRawPrelude ? NO_RAW_RUNTIME_PRELUDE : ""}
10542
10777
  ${prelude.join("\n")}
10543
10778
  ${skipDefs.join("\n")}
10544
10779
  ${choiceDefs.join("\n")}
10545
10780
  ${bodies.join("\n")}
10546
- function _begin(ctx){
10781
+ ${externalDefs}function _begin(ctx){
10547
10782
  const host=ctx.build
10548
10783
  if(_pfDepth>0)_pfFrames.push([_pfScan,_pfHost,EC.e])
10549
10784
  _pfDepth++
@@ -10570,7 +10805,7 @@ pieces:{${ruleEntries.join(",")}},
10570
10805
  byIp:{${extra.join(",")}},
10571
10806
  end:function(){return EC.e},
10572
10807
  begin:_begin,
10573
- finish:_finish
10808
+ finish:_finish${hybrid}
10574
10809
  }`;
10575
10810
  return {
10576
10811
  source,
@@ -10578,10 +10813,193 @@ finish:_finish
10578
10813
  masks,
10579
10814
  classes,
10580
10815
  armExpected,
10816
+ siteBytes,
10581
10817
  plan: { classes: classPlan, armExpected: armExpectedPlan, masks: maskPlan }
10582
10818
  };
10583
10819
  }
10584
10820
 
10821
+ // src/combinators/lines.ts
10822
+ function lineIndexAt(view, offset) {
10823
+ const lines2 = view.lines;
10824
+ let lo = 0, hi = lines2.length - 1;
10825
+ while (lo < hi) {
10826
+ const mid = lo + hi + 1 >> 1;
10827
+ if (lines2[mid].offset <= offset) lo = mid;
10828
+ else hi = mid - 1;
10829
+ }
10830
+ return lo;
10831
+ }
10832
+ var lineAt = (view, offset) => view.lines[lineIndexAt(view, offset)];
10833
+ function sourceOffset(view, offset) {
10834
+ const line = lineAt(view, offset);
10835
+ return line === void 0 ? offset : line.source + (offset - line.offset);
10836
+ }
10837
+ function columnAt(input, pos, ctx) {
10838
+ const view = ctx.lineView;
10839
+ let start, column, split;
10840
+ const line = view === void 0 ? void 0 : lineAt(view, pos);
10841
+ if (line !== void 0 && pos >= line.offset) {
10842
+ start = line.offset;
10843
+ column = line.column;
10844
+ split = line.split;
10845
+ } else {
10846
+ start = input.lastIndexOf("\n", pos - 1) + 1;
10847
+ column = 0;
10848
+ split = 0;
10849
+ }
10850
+ for (let i = start; i < pos; i++) {
10851
+ if (input.charCodeAt(i) === 9) {
10852
+ const width = 4 - (column - (i === start ? split : 0)) % 4 - (i === start ? split : 0);
10853
+ column += width;
10854
+ } else column++;
10855
+ split = 0;
10856
+ }
10857
+ return { column, split: pos === start ? split : 0 };
10858
+ }
10859
+ function columns(n) {
10860
+ if (!Number.isInteger(n) || n < 0) throw new TypeError(`columns(n) needs a non-negative integer, got ${n}`);
10861
+ const expected = [`columns(${n})`];
10862
+ const meta = {
10863
+ firstSet: n === 0 ? any() : { kind: "ranges", ranges: [{ lo: 9, hi: 9 }, { lo: 32, hi: 32 }] },
10864
+ canMatchNewline: false,
10865
+ isTrivia: false
10866
+ };
10867
+ return {
10868
+ _tag: "columns",
10869
+ _meta: meta,
10870
+ _def: { tag: "columns", n },
10871
+ parse(input, pos, ctx) {
10872
+ const c = ctx;
10873
+ let { column, split } = columnAt(input, pos, c);
10874
+ const goal = column + n;
10875
+ let at = pos;
10876
+ while (column < goal) {
10877
+ const code = input.charCodeAt(at);
10878
+ if (code === 32) {
10879
+ column++;
10880
+ split = 0;
10881
+ at++;
10882
+ continue;
10883
+ }
10884
+ if (code !== 9) return { ok: false, expected, span: { start: at, end: at } };
10885
+ const width = 4 - (column - split) % 4 - split;
10886
+ if (column + width > goal) {
10887
+ c._columnsSplit = { at, split: split + (goal - column) };
10888
+ return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
10889
+ }
10890
+ column += width;
10891
+ split = 0;
10892
+ at++;
10893
+ }
10894
+ return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
10895
+ }
10896
+ };
10897
+ }
10898
+ function lines(prefix, body, options = {}) {
10899
+ const { first, lazy } = options;
10900
+ const opening = first ?? prefix;
10901
+ const meta = {
10902
+ // An opening that matches empty starts a run at any character.
10903
+ firstSet: matchesEmpty(opening) ? any() : opening._meta.firstSet,
10904
+ canMatchNewline: true,
10905
+ isTrivia: false
10906
+ };
10907
+ return {
10908
+ _tag: "lines",
10909
+ _meta: meta,
10910
+ _def: { tag: "lines", prefix, body, ...first === void 0 ? {} : { first }, ...lazy === void 0 ? {} : { lazy } },
10911
+ parse(input, pos, ctx) {
10912
+ const c = ctx;
10913
+ const probe = createDetachedParseContext(false, ctx.state);
10914
+ probe.lineView = c.lineView;
10915
+ const run = [];
10916
+ const parts = [];
10917
+ let offset = 0;
10918
+ for (let start = pos; start < input.length || start === pos; ) {
10919
+ let end2 = input.indexOf("\n", start);
10920
+ if (end2 < 0) end2 = input.length;
10921
+ probe._columnsSplit = void 0;
10922
+ const head = (run.length === 0 ? opening : prefix).parse(input, start, probe);
10923
+ let content;
10924
+ let joined;
10925
+ if (head.ok && head.span.end <= end2) {
10926
+ content = head.span.end;
10927
+ joined = "prefix";
10928
+ } else if (run.length === 0) {
10929
+ return { ok: false, expected: head.ok ? ["line prefix"] : head.expected, span: { start, end: start } };
10930
+ } else if (lazy !== void 0 && lazy.parse(input, start, probe).ok) {
10931
+ content = start;
10932
+ joined = "lazy";
10933
+ } else break;
10934
+ const mark = probe._columnsSplit;
10935
+ const here = columnAt(input, content, c);
10936
+ const split = joined === "prefix" && mark !== void 0 && mark.at === content ? mark.split : here.split;
10937
+ run.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
10938
+ const text = input.slice(content, end2);
10939
+ const newline = end2 < input.length;
10940
+ parts.push(newline ? `${text}
10941
+ ` : text);
10942
+ offset += text.length + (newline ? 1 : 0);
10943
+ start = end2 + 1;
10944
+ if (!newline) break;
10945
+ }
10946
+ const view = { lines: run, parent: c.lineView };
10947
+ const saved = c.lineView, starts = c._lineStarts, lineIndex = c._lineIndex, scanned = c._lineScannedTo, probing = c._probe;
10948
+ const errors = c._errors?.length ?? 0;
10949
+ let cursor = -1;
10950
+ if (probing !== void 0) for (const line2 of run) {
10951
+ if (probing.offset >= line2.source && probing.offset <= line2.end) {
10952
+ cursor = line2.offset + probing.offset - line2.source;
10953
+ break;
10954
+ }
10955
+ }
10956
+ const inner = cursor < 0 ? void 0 : { offset: cursor, best: null };
10957
+ c.lineView = view;
10958
+ c._lineStarts = void 0;
10959
+ c._lineIndex = void 0;
10960
+ c._lineScannedTo = void 0;
10961
+ c._probe = inner;
10962
+ let result;
10963
+ try {
10964
+ result = body.parse(parts.join(""), 0, ctx);
10965
+ } finally {
10966
+ c.lineView = saved;
10967
+ c._lineStarts = starts;
10968
+ c._lineIndex = lineIndex;
10969
+ c._lineScannedTo = scanned;
10970
+ c._probe = probing;
10971
+ }
10972
+ if (inner?.best) failAt(c, inner.best.expected, sourceOffset(view, inner.best.span.start));
10973
+ if (c._errors !== void 0) for (let i = errors; i < c._errors.length; i++) {
10974
+ const error = c._errors[i];
10975
+ error.span = { start: sourceOffset(view, error.span.start), end: sourceOffset(view, error.span.end) };
10976
+ }
10977
+ if (!result.ok) {
10978
+ const at = sourceOffset(view, result.span.start);
10979
+ return { ...result, span: { start: at, end: at } };
10980
+ }
10981
+ const stop = result.span.end;
10982
+ const index = lineIndexAt(view, stop);
10983
+ const line = run[index];
10984
+ 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);
10985
+ recordLineRangeFromContext(c, input, pos, end);
10986
+ return { ok: true, value: result.value, span: { start: pos, end } };
10987
+ }
10988
+ };
10989
+ }
10990
+
10991
+ // src/table/line-scan.ts
10992
+ function rebuildLineScan(s, subtree) {
10993
+ if (s.kind === 2) {
10994
+ return lines(subtree(s.prefix), subtree(s.body), {
10995
+ ...s.first === void 0 ? {} : { first: subtree(s.first) },
10996
+ ...s.lazy === void 0 ? {} : { lazy: subtree(s.lazy) }
10997
+ });
10998
+ }
10999
+ if (s.kind === 3) return columns(s.n);
11000
+ return void 0;
11001
+ }
11002
+
10585
11003
  // src/combinators/literal.ts
10586
11004
  function asciiFoldEq(a, b) {
10587
11005
  if (a.length !== b.length) return false;
@@ -11229,6 +11647,70 @@ function escapeClassChar(ch2) {
11229
11647
  return ch2.replace(/[\\\]^-]/g, "\\$&");
11230
11648
  }
11231
11649
 
11650
+ // src/table/trivia-gap.ts
11651
+ function skipDeferredTrivia(input, cur, ctx) {
11652
+ const triviaP = ctx.trivia;
11653
+ const plain2 = ctx.trackLines !== true && ctx.captureTrivia !== true;
11654
+ const gap = ctx._gap;
11655
+ if (plain2 && gap !== void 0 && cur === gap.at && input === gap.input && triviaP === gap.of && ctx._rootTriviaCapture === gap.root && ctx.state === gap.state && ctx._rootTriviaKindIndex === gap.rootKinds && ctx._triviaLog === gap.log) return gap.end;
11656
+ const end = clearRunEnd(input, cur, ctx, triviaP, plain2);
11657
+ if (end >= 0) {
11658
+ remember(cur, end, ctx, input, triviaP);
11659
+ return end;
11660
+ }
11661
+ const scan = scanTriviaCompact(input, cur, ctx);
11662
+ if (typeof scan !== "number") return commitTriviaScan(scan);
11663
+ if (plain2) remember(cur, scan, ctx, input, triviaP);
11664
+ return scan;
11665
+ }
11666
+ function clearRunEnd(input, cur, ctx, triviaP, plain2) {
11667
+ const rootKinds = ctx._rootTriviaKindIndex;
11668
+ const spec = triviaP?._meta.labeledTriviaSpec;
11669
+ if (!plain2 || triviaP === void 0 || spec == null || rootKinds === void 0 || ctx._triviaLog !== void 0) return -1;
11670
+ if (spec.rootKinds !== rootKinds) prime(spec, triviaP, rootKinds);
11671
+ const fast = spec.rootScan;
11672
+ if (fast === null) return -1;
11673
+ const end = fast(input, cur);
11674
+ return ctx._rootTriviaCapture === false || clearOf(spec.rootLeaders, input, cur, end) ? end : -1;
11675
+ }
11676
+ function remember(cur, end, ctx, input, triviaP) {
11677
+ const gap = ctx._gap;
11678
+ if (gap === void 0) {
11679
+ ctx._gap = { at: cur, end, input, of: triviaP, root: ctx._rootTriviaCapture, rootKinds: ctx._rootTriviaKindIndex, log: ctx._triviaLog, state: ctx.state };
11680
+ return;
11681
+ }
11682
+ gap.at = cur;
11683
+ gap.end = end;
11684
+ gap.input = input;
11685
+ gap.of = triviaP;
11686
+ gap.root = ctx._rootTriviaCapture;
11687
+ gap.rootKinds = ctx._rootTriviaKindIndex;
11688
+ gap.log = ctx._triviaLog;
11689
+ gap.state = ctx.state;
11690
+ }
11691
+ function prime(spec, triviaP, rootKinds) {
11692
+ const ranges = [];
11693
+ let bounded = true;
11694
+ for (const arm of spec.arms) {
11695
+ if (rootKinds[arm.label] === void 0) continue;
11696
+ const fs = arm.parser._meta.firstSet;
11697
+ if (fs.kind === "any") bounded = false;
11698
+ else if (fs.kind === "ranges") ranges.push(...fs.ranges);
11699
+ }
11700
+ const classified = triviaP._meta.triviaScanner;
11701
+ spec.rootKinds = rootKinds;
11702
+ spec.rootLeaders = ranges;
11703
+ spec.rootScan = !bounded ? null : classified === void 0 ? fastTriviaScanner(triviaP) : classified;
11704
+ }
11705
+ function clearOf(ranges, input, from, to) {
11706
+ for (let i = from; i < to; i++) {
11707
+ const c = input.charCodeAt(i);
11708
+ if (c >= 55296 && c <= 57343) return false;
11709
+ for (const r of ranges) if (c >= r.lo && c <= r.hi) return false;
11710
+ }
11711
+ return true;
11712
+ }
11713
+
11232
11714
  // src/table/run-support.ts
11233
11715
  function lineCol(ctx, offset) {
11234
11716
  const starts = ctx._lineStarts ?? [0];
@@ -11297,7 +11779,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11297
11779
  ...prog.labels === void 0 ? {} : { triviaKindLabels: prog.labels },
11298
11780
  ...prog.classified === 1 ? { rootTriviaClassified: true } : {}
11299
11781
  };
11300
- const lines = prog.lines === 1;
11782
+ const lines2 = prog.lines === 1;
11301
11783
  const mode = prog.hostMode ?? "ast";
11302
11784
  const elided = mode === "ast" && hasDirectBuilder(prog);
11303
11785
  const coverageDefinitions = prog.cov === void 0 ? void 0 : Object.freeze(covDefinitions(prog).map(Object.freeze));
@@ -11325,7 +11807,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11325
11807
  ctx._fe = -1;
11326
11808
  ctx._fx = EMPTY_FX;
11327
11809
  ctx._fc = false;
11328
- if (lines && ctx._lineStarts === void 0) {
11810
+ if (lines2 && ctx._lineStarts === void 0) {
11329
11811
  ctx._lineStarts = [0];
11330
11812
  ctx._lineScannedTo = 0;
11331
11813
  }
@@ -11333,10 +11815,10 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11333
11815
  if (end < 0) {
11334
11816
  const fe = ctx._fe;
11335
11817
  const at = fe === void 0 || fe < 0 ? pos : fe;
11336
- const fspan = lines ? spanLines(ctx, at, at) : { start: at, end: at };
11818
+ const fspan = lines2 ? spanLines(ctx, at, at) : { start: at, end: at };
11337
11819
  return committed2(ctx) ? { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan, committed: true } : { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan };
11338
11820
  }
11339
- return { ok: true, value: d.lastValue(), span: lines ? spanLines(ctx, pos, end) : { start: pos, end } };
11821
+ return { ok: true, value: d.lastValue(), span: lines2 ? spanLines(ctx, pos, end) : { start: pos, end } };
11340
11822
  };
11341
11823
  const ownTrivia = triviaOfRule(prog.rules[name]);
11342
11824
  const meta = ownTrivia === void 0 ? baseMeta : { ...baseMeta, grammarTrivia: ownTrivia };
@@ -11427,6 +11909,9 @@ function assemble(t2, prog, cfg) {
11427
11909
  const EC = newEndCell();
11428
11910
  const scalarRecognizers = [];
11429
11911
  const rawScalarSpecs = /* @__PURE__ */ new Set();
11912
+ function lookaheadClass(child) {
11913
+ return code[child] === OP_GATE && !cfg.tolerant && !cfg.probe ? cc[code[child + 1]] : null;
11914
+ }
11430
11915
  function scalarFor(child) {
11431
11916
  const spec = code[child + 1];
11432
11917
  let recognize = scalarRecognizers[spec];
@@ -11436,20 +11921,23 @@ function assemble(t2, prog, cfg) {
11436
11921
  }
11437
11922
  return recognize;
11438
11923
  }
11439
- const labelExtraIps = [];
11440
- for (const s of prog.scans ?? []) {
11441
- for (const r of s.skip) labelExtraIps.push(r[0]);
11442
- if (s.sentinel !== void 0) labelExtraIps.push(s.sentinel[0]);
11443
- if (s.recoverAt !== void 0) labelExtraIps.push(s.recoverAt[0]);
11444
- if (s.stopAt !== void 0) labelExtraIps.push(s.stopAt[0]);
11445
- }
11446
- for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0]);
11924
+ const labelExtraIps = assemblyExtraIps(prog);
11447
11925
  const closureLabels = computeSiteLabels(
11448
11926
  code,
11449
11927
  [...Object.values(prog.rules), ...labelExtraIps],
11450
11928
  hostCst
11451
11929
  );
11452
11930
  let SCAN = null;
11931
+ let HYBRID;
11932
+ function hybridPiece(piece) {
11933
+ const setScan = HYBRID.scan;
11934
+ return (input, pos, ctx) => {
11935
+ const saved = setScan(SCAN);
11936
+ const value = piece(input, pos, ctx);
11937
+ setScan(saved);
11938
+ return value;
11939
+ };
11940
+ }
11453
11941
  const memo = /* @__PURE__ */ new Map();
11454
11942
  const reached = /* @__PURE__ */ new Set();
11455
11943
  function pushLeaf(ctx, value, s, e) {
@@ -11741,9 +12229,7 @@ function assemble(t2, prog, cfg) {
11741
12229
  return s(input, cur);
11742
12230
  }
11743
12231
  if (s !== null) return skipTriviaScanned(s, input, cur, ctx);
11744
- if (needsDeferredTriviaCommit(ctx)) {
11745
- return commitTriviaScan(scanTriviaCompact(input, cur, ctx));
11746
- }
12232
+ if (needsDeferredTriviaCommit(ctx)) return skipDeferredTrivia(input, cur, ctx);
11747
12233
  return advanceTrivia(input, cur, ctx);
11748
12234
  }
11749
12235
  let TERMV;
@@ -11913,7 +12399,7 @@ function assemble(t2, prog, cfg) {
11913
12399
  if (next !== void 0) {
11914
12400
  return next(input, pos, ctx, bits, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best);
11915
12401
  }
11916
- if (best === pos) acc = through(total, prev, acc);
12402
+ if (best === pos && prev !== 0) acc = through(total, prev, acc);
11917
12403
  ctx._fe = pos;
11918
12404
  ctx._fx = acc ?? choiceFx;
11919
12405
  return FAIL;
@@ -11996,7 +12482,7 @@ function assemble(t2, prog, cfg) {
11996
12482
  if (next !== void 0) {
11997
12483
  return next(input, pos, ctx, c, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot, acc, prev, best);
11998
12484
  }
11999
- if (best === pos) acc = through(total, prev, acc);
12485
+ if (best === pos && prev !== 0) acc = through(total, prev, acc);
12000
12486
  ctx._fe = pos;
12001
12487
  ctx._fx = acc ?? choiceFx;
12002
12488
  return FAIL;
@@ -12075,6 +12561,13 @@ function assemble(t2, prog, cfg) {
12075
12561
  if (done !== void 0) return done;
12076
12562
  const flight = inFlight.get(ip);
12077
12563
  if (flight !== void 0) return flight.fwd;
12564
+ const selected = HYBRID?.byIp[ip];
12565
+ if (selected !== void 0) {
12566
+ const piece2 = hybridPiece(selected);
12567
+ memo.set(ip, piece2);
12568
+ reached.add(ip);
12569
+ return piece2;
12570
+ }
12078
12571
  let target;
12079
12572
  const fwd = (input, pos, ctx) => target(input, pos, ctx);
12080
12573
  const holder = { fwd, set: (p) => {
@@ -12082,7 +12575,7 @@ function assemble(t2, prog, cfg) {
12082
12575
  } };
12083
12576
  inFlight.set(ip, holder);
12084
12577
  const op = code[ip];
12085
- const piece = (op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[ip + 3] === 0) && code[ip + 1] >= 0 && closureLabels.at(ip).tri === code[ip + 1] ? link(code[ip + 2]) : lower(ip);
12578
+ const piece = (op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[ip + 3] === 0) && code[ip + 1] >= 0 && closureLabels.at(ip).tri === code[ip + 1] ? link(code[ip + 2]) : countedPiece(lower(ip), ip);
12086
12579
  inFlight.delete(ip);
12087
12580
  holder.set(piece);
12088
12581
  memo.set(ip, piece);
@@ -12619,7 +13112,12 @@ function assemble(t2, prog, cfg) {
12619
13112
  }
12620
13113
  const child = link(code[ip + 1]);
12621
13114
  const xf = fx[code[ip + 2]];
13115
+ const gate = lookaheadClass(code[ip + 1]);
12622
13116
  return (input, pos, ctx) => {
13117
+ if (gate !== null && !classHas2(gate, lead(input, pos))) {
13118
+ EC.e = pos;
13119
+ return null;
13120
+ }
12623
13121
  const need = markCst(ctx);
12624
13122
  const mRaw = MRAW;
12625
13123
  const mTl = MTL;
@@ -12642,7 +13140,13 @@ function assemble(t2, prog, cfg) {
12642
13140
  case OP_PEEK: {
12643
13141
  const child = link(code[ip + 1]);
12644
13142
  const xf = fx[code[ip + 2]];
13143
+ const gate = lookaheadClass(code[ip + 1]);
12645
13144
  return (input, pos, ctx) => {
13145
+ if (gate !== null && !classHas2(gate, lead(input, pos))) {
13146
+ ctx._fe = pos;
13147
+ ctx._fx = xf;
13148
+ return FAIL;
13149
+ }
12646
13150
  const need = markCst(ctx);
12647
13151
  const mRaw = MRAW;
12648
13152
  const mTl = MTL;
@@ -12664,7 +13168,12 @@ function assemble(t2, prog, cfg) {
12664
13168
  }
12665
13169
  case OP_OPT: {
12666
13170
  const child = link(code[ip + 1]);
13171
+ const gate = lookaheadClass(code[ip + 1]);
12667
13172
  return (input, pos, ctx) => {
13173
+ if (gate !== null && !classHas2(gate, lead(input, pos))) {
13174
+ EC.e = pos;
13175
+ return null;
13176
+ }
12668
13177
  const need = markCst(ctx);
12669
13178
  const mRaw = MRAW;
12670
13179
  const mTl = MTL;
@@ -13471,6 +13980,11 @@ function assemble(t2, prog, cfg) {
13471
13980
  }
13472
13981
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13473
13982
  }
13983
+ if (prev === 0) {
13984
+ ctx._fe = pos;
13985
+ ctx._fx = choiceFx;
13986
+ return FAIL;
13987
+ }
13474
13988
  if (best === pos && prev < 1) acc = accSet(e0, acc);
13475
13989
  if (best === pos && prev < 2) acc = accSet(e1, acc);
13476
13990
  if (best === pos && n === 3 && prev < 3) acc = accSet(e2, acc);
@@ -13779,7 +14293,7 @@ function assemble(t2, prog, cfg) {
13779
14293
  return (input, pos, ctx) => {
13780
14294
  if (!pred(ctx.state)) {
13781
14295
  ctx._fc = false;
13782
- ctx._fe = pos;
14296
+ ctx._fe = -1;
13783
14297
  ctx._fx = xf;
13784
14298
  return FAIL;
13785
14299
  }
@@ -14185,7 +14699,7 @@ function assemble(t2, prog, cfg) {
14185
14699
  if (v === FAIL) return FAIL;
14186
14700
  const end = EC.e;
14187
14701
  const span = tracked ? spanLines(ctx, pos, end) : { start: pos, end };
14188
- const st = readsState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
14702
+ const st = readsState ? ctx.state : void 0;
14189
14703
  let nd;
14190
14704
  if (unwrap && kids.length === 1) {
14191
14705
  nd = unwrapChild(kids[0]);
@@ -14198,10 +14712,10 @@ function assemble(t2, prog, cfg) {
14198
14712
  ) {
14199
14713
  nd = kids[0];
14200
14714
  } else if (proj >= 0) {
14201
- nd = hostCst && host !== void 0 ? host(type, kids, fieldMap, span, rawKids, tlog, readsState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0, tags) : projectChild(kids, proj, type);
14715
+ nd = hostCst && host !== void 0 ? host(type, kids, fieldMap, span, rawKids, tlog, ctx.state, tags) : projectChild(kids, proj, type);
14202
14716
  } else if (build !== void 0) {
14203
14717
  if (hostCst && host !== void 0) {
14204
- nd = host(type, kids, fieldMap, span, rawKids, tlog, readsState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0, tags);
14718
+ nd = host(type, kids, fieldMap, span, rawKids, tlog, ctx.state, tags);
14205
14719
  } else {
14206
14720
  nd = build(kids, fieldMap, span, rawKids, captureWide ? tlog : EMPTY_TL2, st);
14207
14721
  }
@@ -14252,14 +14766,7 @@ function assemble(t2, prog, cfg) {
14252
14766
  let emitRefusal;
14253
14767
  let emitReached;
14254
14768
  {
14255
- const extraIps = [];
14256
- for (const s of prog.scans ?? []) {
14257
- for (const r of s.skip) extraIps.push(r[0]);
14258
- if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
14259
- if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
14260
- if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
14261
- }
14262
- for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
14769
+ const extraIps = assemblyExtraIps(prog);
14263
14770
  const roots = [...Object.values(prog.rules), ...extraIps];
14264
14771
  for (const ip of reachableSites(code, roots)) {
14265
14772
  if (code[ip] === OP_DISPATCH) {
@@ -14352,9 +14859,11 @@ function assemble(t2, prog, cfg) {
14352
14859
  adjacencyHolds,
14353
14860
  t2.lexPrograms,
14354
14861
  LEAD,
14355
- triviaFieldCapture
14862
+ triviaFieldCapture,
14863
+ skipDeferredTrivia
14356
14864
  );
14357
14865
  emitReached = new Set(pre.reached);
14866
+ if (emitted.ext !== void 0) HYBRID = emitted;
14358
14867
  }
14359
14868
  try {
14360
14869
  if (emitted !== void 0) throw new ServedByBuild();
@@ -14436,7 +14945,8 @@ function assemble(t2, prog, cfg) {
14436
14945
  adjacencyHolds,
14437
14946
  t2.lexPrograms,
14438
14947
  LEAD,
14439
- triviaFieldCapture
14948
+ triviaFieldCapture,
14949
+ skipDeferredTrivia
14440
14950
  );
14441
14951
  emitReached = em.reached;
14442
14952
  } catch (e) {
@@ -14447,7 +14957,7 @@ function assemble(t2, prog, cfg) {
14447
14957
  }
14448
14958
  function pieceAt(ip) {
14449
14959
  const em = emitted;
14450
- if (em !== void 0) {
14960
+ if (em !== void 0 && HYBRID === void 0) {
14451
14961
  const p = em.byIp[ip];
14452
14962
  if (p === void 0) throw new Error(`table emitter: no emitted body for site ${ip}`);
14453
14963
  return p;
@@ -14455,6 +14965,11 @@ function assemble(t2, prog, cfg) {
14455
14965
  return link(ip);
14456
14966
  }
14457
14967
  for (const s of prog.scans ?? []) {
14968
+ const rebuilt = rebuildLineScan(s, subtreeComb);
14969
+ if (rebuilt !== void 0) {
14970
+ scansArr.push(rebuilt);
14971
+ continue;
14972
+ }
14458
14973
  const skip = s.skip.map((r) => subtreeComb(r));
14459
14974
  const raw = (s.flags & 1) !== 0;
14460
14975
  if (s.kind === 1) {
@@ -14472,6 +14987,37 @@ function assemble(t2, prog, cfg) {
14472
14987
  }
14473
14988
  const scans = scansArr;
14474
14989
  const scanSkip = (prog.scanSkip ?? []).map((set) => set.map((r) => subtreeComb(r)));
14990
+ if (HYBRID !== void 0) {
14991
+ const em = HYBRID;
14992
+ em.bind(
14993
+ (s) => {
14994
+ const saved = SCAN;
14995
+ SCAN = s;
14996
+ return saved;
14997
+ },
14998
+ em.ext.map((ip) => link(ip))
14999
+ );
15000
+ const pieces2 = {};
15001
+ for (const [name, entryIp] of Object.entries(prog.rules)) {
15002
+ pieces2[name] = em.pieces[name] ?? link(entryIp);
15003
+ }
15004
+ for (const ip of emitReached) reached.add(ip);
15005
+ return {
15006
+ pieces: pieces2,
15007
+ end: () => EC.e,
15008
+ begin: (ctx) => {
15009
+ begin(ctx);
15010
+ em.begin(ctx);
15011
+ },
15012
+ finish: () => {
15013
+ em.finish();
15014
+ finish();
15015
+ },
15016
+ scanSkip,
15017
+ reached,
15018
+ emitRefusal: void 0
15019
+ };
15020
+ }
14475
15021
  if (emitted !== void 0) {
14476
15022
  const em = emitted;
14477
15023
  return {
@@ -14507,6 +15053,15 @@ function assemble(t2, prog, cfg) {
14507
15053
  }
14508
15054
  return { pieces, end: () => EC.e, begin, finish, scanSkip, reached, emitRefusal };
14509
15055
  }
15056
+ var SITE_COUNTS;
15057
+ function countedPiece(piece, ip) {
15058
+ const counts = SITE_COUNTS;
15059
+ if (counts === void 0) return piece;
15060
+ return (input, pos, ctx) => {
15061
+ counts[ip]++;
15062
+ return piece(input, pos, ctx);
15063
+ };
15064
+ }
14510
15065
  var AssemblyCache = class {
14511
15066
  t;
14512
15067
  prog;
@@ -14696,6 +15251,11 @@ function emitScanSpec(s) {
14696
15251
  if (s.sent !== void 0) parts.push(`sent:${s.sent === null ? "null" : jsString(s.sent)}`);
14697
15252
  if (s.open !== void 0) parts.push(`open:${jsString(s.open)}`);
14698
15253
  if (s.close !== void 0) parts.push(`close:${jsString(s.close)}`);
15254
+ for (const key of ["prefix", "body", "first", "lazy"]) {
15255
+ const r = s[key];
15256
+ if (r !== void 0) parts.push(`${key}:${emitRef(r)}`);
15257
+ }
15258
+ if (s.n !== void 0) parts.push(`n:${s.n}`);
14699
15259
  return `{${parts.join(",")}}`;
14700
15260
  }
14701
15261
  function emitDispatchSpec(d) {
@@ -14704,14 +15264,7 @@ function emitDispatchSpec(d) {
14704
15264
  function emitAssemblies(prog, cfgs) {
14705
15265
  if (cfgs.length === 0) return ["a:[],"];
14706
15266
  const t2 = resolveTable(prog);
14707
- const extraIps = [];
14708
- for (const s of prog.scans ?? []) {
14709
- for (const r of s.skip) extraIps.push(r[0]);
14710
- if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
14711
- if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
14712
- if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
14713
- }
14714
- for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
15267
+ const extraIps = assemblyExtraIps(prog);
14715
15268
  const out = [];
14716
15269
  const seen = /* @__PURE__ */ new Set();
14717
15270
  for (const cfg of cfgs) {
@@ -14720,7 +15273,7 @@ function emitAssemblies(prog, cfgs) {
14720
15273
  seen.add(key);
14721
15274
  let em;
14722
15275
  try {
14723
- em = emitAssemblySource(t2, prog, cfg, extraIps, true);
15276
+ em = emitAssemblySource(t2, prog, cfg, extraIps, true, cfg.select);
14724
15277
  } catch (e) {
14725
15278
  if (e instanceof Unemittable) continue;
14726
15279
  throw e;
@@ -14862,6 +15415,8 @@ function children(def, winners) {
14862
15415
  ...def.recoverAt === void 0 ? [] : [def.recoverAt],
14863
15416
  ...def.stopAt === void 0 ? [] : [def.stopAt]
14864
15417
  ];
15418
+ case "lines":
15419
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
14865
15420
  case "lazy": {
14866
15421
  let resolved;
14867
15422
  try {
@@ -15005,6 +15560,10 @@ function payloadKey(p, d) {
15005
15560
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
15006
15561
  case "scanTo":
15007
15562
  return `scanTo\0${d.raw}\0${d.orEOF}\0${d.recoverAt !== void 0}\0${d.stopAt !== void 0}`;
15563
+ case "lines":
15564
+ return `lines\0${d.first !== void 0}\0${d.lazy !== void 0}`;
15565
+ case "columns":
15566
+ return `columns\0${d.n}`;
15008
15567
  case "guard":
15009
15568
  return `guard\0${fnKey(d.predSrc, d.predicate)}`;
15010
15569
  case "adjacency":
@@ -15125,6 +15684,8 @@ function segment(d, index) {
15125
15684
  if (d.recoverAt !== void 0 && index === d.skip.length + 1) return "scanTo.recoverAt";
15126
15685
  return "scanTo.stopAt";
15127
15686
  }
15687
+ case "lines":
15688
+ return ["lines.prefix", "lines.body", d.first === void 0 ? "lines.lazy" : "lines.first", "lines.lazy"][index] ?? "lines";
15128
15689
  default:
15129
15690
  return d.tag;
15130
15691
  }
@@ -16167,60 +16728,60 @@ function findKeywordRegexes(visited) {
16167
16728
  var sitesLine = (sites) => sites.slice(0, 6).map(siteToString).join("\n ") + (sites.length > 6 ? `
16168
16729
  \u2026 and ${sites.length - 6} more` : "");
16169
16730
  function formatDuplicationFindings(report) {
16170
- const lines = [];
16731
+ const lines2 = [];
16171
16732
  for (const f of report.structureLoss) {
16172
- 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)}`);
16173
- lines.push(` flat: ${f.earlierShape}`);
16174
- lines.push(` rich: ${f.laterShape}`);
16175
- lines.push(` lost: ${f.lostNodeTypes.map((t2) => `\`${t2}\``).join(", ")}`);
16176
- lines.push(` fix: ${f.suggestion}`);
16733
+ 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)}`);
16734
+ lines2.push(` flat: ${f.earlierShape}`);
16735
+ lines2.push(` rich: ${f.laterShape}`);
16736
+ lines2.push(` lost: ${f.lostNodeTypes.map((t2) => `\`${t2}\``).join(", ")}`);
16737
+ lines2.push(` fix: ${f.suggestion}`);
16177
16738
  }
16178
16739
  for (const f of report.rewrites) {
16179
- 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)"}`);
16180
- lines.push(` from: ${f.from}`);
16181
- lines.push(` to: ${f.to}`);
16182
- lines.push(` fix: ${f.suggestion}`);
16183
- if (f.perf !== "") lines.push(` perf: ${f.perf}`);
16740
+ 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)"}`);
16741
+ lines2.push(` from: ${f.from}`);
16742
+ lines2.push(` to: ${f.to}`);
16743
+ lines2.push(` fix: ${f.suggestion}`);
16744
+ if (f.perf !== "") lines2.push(` perf: ${f.perf}`);
16184
16745
  }
16185
16746
  for (const f of report.divergentNodes) {
16186
- lines.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t2) => `\`${t2}\``).join(", ")}`);
16187
- for (const p of f.productions) lines.push(` ${siteToString(p.site)}
16747
+ lines2.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t2) => `\`${t2}\``).join(", ")}`);
16748
+ for (const p of f.productions) lines2.push(` ${siteToString(p.site)}
16188
16749
  ${p.shape}
16189
16750
  unique to it: ${p.distinctTerms.length === 0 ? "(none)" : p.distinctTerms.map((t2) => `\`${t2}\``).join(", ")}`);
16190
- lines.push(` fix: ${f.suggestion}`);
16751
+ lines2.push(` fix: ${f.suggestion}`);
16191
16752
  }
16192
16753
  for (const f of report.keywordRegexes) {
16193
16754
  const src = f.source.length > 90 ? `${f.source.slice(0, 88)}\u2026` : f.source;
16194
- 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]" : ""}`);
16195
- lines.push(` fix: ${f.suggestion}`);
16755
+ 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]" : ""}`);
16756
+ lines2.push(` fix: ${f.suggestion}`);
16196
16757
  }
16197
16758
  for (const f of report.duplicates) {
16198
- lines.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16199
- lines.push(` at: ${sitesLine(f.sites)}`);
16200
- lines.push(` fix: ${f.suggestion}`);
16759
+ lines2.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16760
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16761
+ lines2.push(` fix: ${f.suggestion}`);
16201
16762
  }
16202
16763
  for (const f of report.nearDuplicates) {
16203
- 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}`);
16204
- lines.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
16205
- lines.push(` at: ${sitesLine(f.sites)}`);
16206
- lines.push(` fix: ${f.suggestion}`);
16764
+ 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}`);
16765
+ lines2.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
16766
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16767
+ lines2.push(` fix: ${f.suggestion}`);
16207
16768
  }
16208
16769
  for (const f of report.regexFragments) {
16209
- lines.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
16210
- lines.push(` at: ${sitesLine(f.sites)}`);
16211
- lines.push(` fix: ${f.suggestion}`);
16770
+ lines2.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
16771
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16772
+ lines2.push(` fix: ${f.suggestion}`);
16212
16773
  }
16213
16774
  for (const f of report.regexClasses) {
16214
- lines.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
16215
- for (const v of f.variants) lines.push(` [${v.source}] \xD7${v.count} ${v.delta}
16775
+ lines2.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
16776
+ for (const v of f.variants) lines2.push(` [${v.source}] \xD7${v.count} ${v.delta}
16216
16777
  ${sitesLine(v.sites)}`);
16217
- lines.push(` fix: ${f.suggestion}`);
16778
+ lines2.push(` fix: ${f.suggestion}`);
16218
16779
  }
16219
16780
  for (const f of report.overlaps) {
16220
- 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]" : ""}`);
16221
- lines.push(` fix: ${f.suggestion}`);
16781
+ 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]" : ""}`);
16782
+ lines2.push(` fix: ${f.suggestion}`);
16222
16783
  }
16223
- return lines;
16784
+ return lines2;
16224
16785
  }
16225
16786
  function duplicationFindingCount(report) {
16226
16787
  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;
@@ -16254,11 +16815,11 @@ function reportDuplication(opt, analyze) {
16254
16815
  );
16255
16816
  return void 0;
16256
16817
  }
16257
- const lines = formatDuplicationFindings(report);
16258
- if (lines.length > 0) {
16818
+ const lines2 = formatDuplicationFindings(report);
16819
+ if (lines2.length > 0) {
16259
16820
  if (level === "error") throw new Error(`parseman: ${duplicationFindingCount(report)} duplication/overlap finding(s)
16260
- ${lines.join("\n")}`);
16261
- for (const l of lines) console.warn(l);
16821
+ ${lines2.join("\n")}`);
16822
+ for (const l of lines2) console.warn(l);
16262
16823
  }
16263
16824
  return report;
16264
16825
  }
@@ -16874,7 +17435,7 @@ function cellsOf(line) {
16874
17435
  }
16875
17436
  return cells;
16876
17437
  }
16877
- var plain = (lines) => lines.map((l) => cellsOf(l).map((c) => c.text).join("")).join("\n");
17438
+ var plain = (lines2) => lines2.map((l) => cellsOf(l).map((c) => c.text).join("")).join("\n");
16878
17439
  var CODE_CACHE = /* @__PURE__ */ new Map();
16879
17440
  function codesFor(style) {
16880
17441
  if (style === void 0) return { pre: "", post: "" };
@@ -16935,10 +17496,10 @@ function linked(text, path) {
16935
17496
  const trail = text.slice(body.length);
16936
17497
  return `${tpl.pre}${path}${tpl.mid}${body}${tpl.post}${trail}`;
16937
17498
  }
16938
- function render2(lines, target = {}) {
16939
- if (target.color !== true) return plain(lines);
17499
+ function render2(lines2, target = {}) {
17500
+ if (target.color !== true) return plain(lines2);
16940
17501
  const wantLinks = target.links !== false;
16941
- return lines.map((line) => {
17502
+ return lines2.map((line) => {
16942
17503
  if (line.length === 1 && line[0].raw === true) return line[0].text;
16943
17504
  return cellsOf(line).map((c) => {
16944
17505
  const { pre, post } = codesFor(c.style);
@@ -17824,9 +18385,11 @@ function attempt(parser2, options) {
17824
18385
  const mark = saveCstMark(ctx);
17825
18386
  const log = ctx._triviaLog?.length;
17826
18387
  const rootLog = ctx._rootTriviaLog?.length;
18388
+ const split = ctx._columnsSplit;
17827
18389
  const result = parser2.parse(input, pos, ctx);
17828
18390
  if (result.ok) return result;
17829
18391
  rollbackCstCapture(ctx, mark);
18392
+ ctx._columnsSplit = split;
17830
18393
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
17831
18394
  if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
17832
18395
  if (result.committed && !contain) return result;
@@ -17872,7 +18435,9 @@ var FROZEN_TAGS = /* @__PURE__ */ new Set([
17872
18435
  "withCtx",
17873
18436
  "routed",
17874
18437
  "trivia",
17875
- "unknown"
18438
+ "unknown",
18439
+ "lines",
18440
+ "columns"
17876
18441
  ]);
17877
18442
  var CARRIED_META = [
17878
18443
  "grammarTrivia",
@@ -17901,7 +18466,7 @@ function subtreeContains(p, targets) {
17901
18466
  const kids = [];
17902
18467
  if (Array.isArray(rec.parsers)) kids.push(...rec.parsers);
17903
18468
  if (Array.isArray(rec.skip)) kids.push(...rec.skip);
17904
- for (const k of ["parser", "main", "skipped", "separator", "sentinel", "recoverAt", "stopAt", "selector", "otherwise", "fallback", "triviaParser"])
18469
+ for (const k of ["parser", "main", "skipped", "separator", "sentinel", "recoverAt", "stopAt", "selector", "otherwise", "fallback", "triviaParser", "prefix", "body", "first", "lazy"])
17905
18470
  if (rec[k]) kids.push(rec[k]);
17906
18471
  if (d.tag === "dispatch") {
17907
18472
  for (const c2 of d.cases) kids.push(c2.parser);
@@ -18708,9 +19273,9 @@ function locatedLines(l, opts) {
18708
19273
  t(l.id, TONE.ident)
18709
19274
  ]);
18710
19275
  out.push([t(" here ", TONE.faint), t(l.site, TONE.quiet)]);
18711
- const lines = wrap(`No change can be offered here: ${l.reason}`, width - 12, "");
18712
- out.push([t(" why ", TONE.quiet), t(lines[0] ?? "", TONE.warn)]);
18713
- for (const x of lines.slice(1)) out.push([t(" "), t(x, TONE.warn)]);
19276
+ const lines2 = wrap(`No change can be offered here: ${l.reason}`, width - 12, "");
19277
+ out.push([t(" why ", TONE.quiet), t(lines2[0] ?? "", TONE.warn)]);
19278
+ for (const x of lines2.slice(1)) out.push([t(" "), t(x, TONE.warn)]);
18714
19279
  return out;
18715
19280
  }
18716
19281