parseman 0.57.0 → 0.58.1

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 (69) hide show
  1. package/dist/analysis/diagnostics.js +1800 -962
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +1929 -1091
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/combinators/dispatch.d.ts.map +1 -1
  6. package/dist/compiler/token-alphabet.d.ts +11 -1
  7. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  8. package/dist/compiler/token-capability.js +69 -16
  9. package/dist/compiler/token-capability.js.map +2 -2
  10. package/dist/cst/capture-buffer.d.ts +6 -0
  11. package/dist/cst/capture-buffer.d.ts.map +1 -1
  12. package/dist/index.js +1017 -177
  13. package/dist/index.js.map +3 -3
  14. package/dist/plugin/index.d.ts +25 -1
  15. package/dist/plugin/index.d.ts.map +1 -1
  16. package/dist/plugin/index.js +1210 -236
  17. package/dist/plugin/index.js.map +3 -3
  18. package/dist/run/index.d.ts +2 -0
  19. package/dist/run/index.d.ts.map +1 -1
  20. package/dist/run/index.js +9 -0
  21. package/dist/run/index.js.map +1 -1
  22. package/dist/table/assemble.d.ts.map +1 -1
  23. package/dist/table/compile-rule-map.d.ts.map +1 -1
  24. package/dist/table/emit-assembly.d.ts +1 -1
  25. package/dist/table/emit-assembly.d.ts.map +1 -1
  26. package/dist/table/emit.d.ts.map +1 -1
  27. package/dist/table/encode.d.ts.map +1 -1
  28. package/dist/table/exec.d.ts.map +1 -1
  29. package/dist/table/factor-choice.d.ts +42 -0
  30. package/dist/table/factor-choice.d.ts.map +1 -0
  31. package/dist/table/fail-clean.d.ts +3 -0
  32. package/dist/table/fail-clean.d.ts.map +1 -0
  33. package/dist/table/hold-gap.d.ts +8 -0
  34. package/dist/table/hold-gap.d.ts.map +1 -0
  35. package/dist/table/index.js +1370 -259
  36. package/dist/table/index.js.map +3 -3
  37. package/dist/table/lex-program.d.ts +7 -3
  38. package/dist/table/lex-program.d.ts.map +1 -1
  39. package/dist/table/ops.d.ts +8 -0
  40. package/dist/table/ops.d.ts.map +1 -1
  41. package/dist/table/run-support.d.ts +19 -0
  42. package/dist/table/run-support.d.ts.map +1 -1
  43. package/dist/table/scalar-terminal.d.ts +2 -0
  44. package/dist/table/scalar-terminal.d.ts.map +1 -1
  45. package/dist/table/site-labels.d.ts +66 -0
  46. package/dist/table/site-labels.d.ts.map +1 -1
  47. package/dist/version.d.ts +1 -1
  48. package/package.json +1 -1
  49. package/src/combinators/dispatch.ts +12 -3
  50. package/src/compiler/token-alphabet.ts +57 -17
  51. package/src/cst/capture-buffer.ts +25 -0
  52. package/src/plugin/index.ts +230 -35
  53. package/src/run/index.ts +5 -0
  54. package/src/table/assemble.ts +252 -62
  55. package/src/table/child-slots.ts +2 -2
  56. package/src/table/compile-rule-map.ts +14 -5
  57. package/src/table/emit-assembly.ts +112 -23
  58. package/src/table/emit.ts +3 -1
  59. package/src/table/encode.ts +83 -14
  60. package/src/table/exec.ts +27 -8
  61. package/src/table/factor-choice.ts +173 -0
  62. package/src/table/fail-clean.ts +130 -0
  63. package/src/table/hold-gap.ts +68 -0
  64. package/src/table/lex-program.ts +197 -43
  65. package/src/table/ops.ts +9 -1
  66. package/src/table/run-support.ts +35 -0
  67. package/src/table/scalar-terminal.ts +8 -0
  68. package/src/table/site-labels.ts +135 -4
  69. package/src/version.ts +1 -1
package/dist/cli/index.js CHANGED
@@ -819,6 +819,31 @@ function pushCstChild(ctx, built, rawEntry2) {
819
819
  else if (ctx._cstLeaves) ctx._cstLeaves.push(built);
820
820
  if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry2);
821
821
  }
822
+ function popCstChild(ctx) {
823
+ const b = ctx._cstBuf;
824
+ if (b) {
825
+ const ch2 = b.ch;
826
+ if (b.rawOnly !== true && ch2) {
827
+ ch2.pop();
828
+ if (ch2.length === 1) {
829
+ b.single = ch2[0];
830
+ b.ch = void 0;
831
+ }
832
+ }
833
+ if (b.noRaw === true) b.rawLen = (b.rawLen ?? 1) - 1;
834
+ else if (b.raw) {
835
+ b.raw.pop();
836
+ if (b.raw.length === 1) {
837
+ b.rawSingle = b.raw[0];
838
+ b.raw = void 0;
839
+ }
840
+ } else b.rawSingle = void 0;
841
+ return;
842
+ }
843
+ if (ctx._cstChildren) ctx._cstChildren.pop();
844
+ else if (ctx._cstLeaves) ctx._cstLeaves.pop();
845
+ if (ctx._cstRawChildren) ctx._cstRawChildren.pop();
846
+ }
822
847
  function cstRawLen(ctx) {
823
848
  const b = ctx._cstBuf;
824
849
  if (b) {
@@ -2395,10 +2420,16 @@ function branchUsesRouted(branch) {
2395
2420
  return branch.usesRouted === true || parserUsesRouted(branch.parser);
2396
2421
  }
2397
2422
  function asciiFoldKey(key) {
2398
- let out = "";
2399
- for (let i = 0; i < key.length; i++) {
2423
+ let i = 0;
2424
+ for (; i < key.length; i++) {
2425
+ const c = key.charCodeAt(i);
2426
+ if (c >= 65 && c <= 90) break;
2427
+ }
2428
+ if (i === key.length) return key;
2429
+ let out = key.slice(0, i);
2430
+ for (; i < key.length; i++) {
2400
2431
  const c = key.charCodeAt(i);
2401
- out += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
2432
+ out += c >= 65 && c <= 90 ? String.fromCharCode(c + 32) : key[i];
2402
2433
  }
2403
2434
  return out;
2404
2435
  }
@@ -2727,6 +2758,42 @@ function keywords(words, opts = {}) {
2727
2758
  };
2728
2759
  }
2729
2760
 
2761
+ // src/combinators/trivia-boundary.ts
2762
+ var MAX_DEPTH = 12;
2763
+ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
2764
+ if (depth === 0) return true;
2765
+ const d = p._def;
2766
+ switch (d.tag) {
2767
+ // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
2768
+ case "sequence":
2769
+ return d.parsers.length > 1;
2770
+ case "many":
2771
+ case "oneOrMore":
2772
+ case "sepBy":
2773
+ return true;
2774
+ // Transparent wrappers — they add no position, so ask what they wrap.
2775
+ case "node":
2776
+ case "transform":
2777
+ case "leaf":
2778
+ case "label":
2779
+ case "field":
2780
+ case "token":
2781
+ case "attempt":
2782
+ case "optional":
2783
+ case "expect":
2784
+ case "withCtx":
2785
+ case "recover":
2786
+ case "trivia":
2787
+ return hasOwnTriviaBoundary(d.parser, depth - 1);
2788
+ // An alternation adds no position either, but ONE arm that has one is enough
2789
+ // to make the scope meaningful for this rule.
2790
+ case "choice":
2791
+ return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
2792
+ default:
2793
+ return false;
2794
+ }
2795
+ }
2796
+
2730
2797
  // src/compiler/token-alphabet.ts
2731
2798
  function optionalSuffixLexBody(ir) {
2732
2799
  if (ir.kind !== "sequence" || ir.parts.length !== 2) return void 0;
@@ -2746,7 +2813,10 @@ function selectOptionalSuffixLexBody(ir) {
2746
2813
  const token2 = { strategy: "token", estimatedOps: 2, body };
2747
2814
  return token2.estimatedOps < character.estimatedOps ? token2 : character;
2748
2815
  }
2749
- function directTokenBodyParser(parser2, resolve2) {
2816
+ function reinstallsRuleTrivia(reference, target) {
2817
+ return (reference._meta.grammarTrivia ?? target._meta.grammarTrivia) !== void 0 && hasOwnTriviaBoundary(target);
2818
+ }
2819
+ function directTokenBodyParser(parser2, resolve2, scope) {
2750
2820
  const outer = parser2._def;
2751
2821
  if (outer.tag !== "token") return void 0;
2752
2822
  let body = outer.parser;
@@ -2756,12 +2826,13 @@ function directTokenBodyParser(parser2, resolve2) {
2756
2826
  const wrapper = body._def;
2757
2827
  if (wrapper.tag === "grammar") {
2758
2828
  if (wrapper.clearTrivia !== true || wrapper.triviaParser !== void 0 || wrapper.captureTrivia === true || wrapper.rootCapture === "opaque" || wrapper.constructionTrackLines !== "inherit" || wrapper.constructionCaptureTriviaKinds !== void 0) return void 0;
2829
+ if (scope !== void 0) scope.cleared = true;
2759
2830
  body = wrapper.parser;
2760
2831
  continue;
2761
2832
  }
2762
2833
  if (wrapper.tag === "lazy") {
2763
2834
  const target = resolvedLazyTarget(body, resolve2);
2764
- if (target === void 0) return void 0;
2835
+ if (target === void 0 || reinstallsRuleTrivia(body, target)) return void 0;
2765
2836
  body = target;
2766
2837
  continue;
2767
2838
  }
@@ -2933,7 +3004,7 @@ function directNot2TokenBody(parser2, resolve2) {
2933
3004
  terminal
2934
3005
  };
2935
3006
  }
2936
- function directFixedLexNode(parser2, resolve2, active, pureOnly = false) {
3007
+ function directFixedLexNode(parser2, resolve2, active, pureOnly = false, cleared = false) {
2937
3008
  let current = parser2;
2938
3009
  const wrappers = /* @__PURE__ */ new Set();
2939
3010
  while (!wrappers.has(current)) {
@@ -2941,7 +3012,7 @@ function directFixedLexNode(parser2, resolve2, active, pureOnly = false) {
2941
3012
  const def = current._def;
2942
3013
  if (def.tag === "lazy") {
2943
3014
  const target = resolvedLazyTarget(current, resolve2);
2944
- if (target === void 0) return void 0;
3015
+ if (target === void 0 || reinstallsRuleTrivia(current, target)) return void 0;
2945
3016
  current = target;
2946
3017
  continue;
2947
3018
  }
@@ -2959,30 +3030,33 @@ function directFixedLexNode(parser2, resolve2, active, pureOnly = false) {
2959
3030
  if (terminal !== void 0) return { kind: "terminal", terminal };
2960
3031
  const def = current._def;
2961
3032
  if (def.tag === "grammar") {
3033
+ if (def.triviaParser === void 0 && def.captureTrivia !== true && def.rootCapture !== "opaque" && def.constructionCaptureTriviaKinds === void 0) {
3034
+ return directFixedLexNode(def.parser, resolve2, active, pureOnly, cleared || def.clearTrivia === true);
3035
+ }
2962
3036
  if (!pureOnly || def.parser._def.tag !== "token" && (def.clearTrivia !== true || def.triviaParser !== void 0 || def.captureTrivia === true || def.rootCapture === "opaque")) return void 0;
2963
- return directFixedLexNode(def.parser, resolve2, active, true);
3037
+ return directFixedLexNode(def.parser, resolve2, active, true, cleared);
2964
3038
  }
2965
3039
  if (def.tag === "sequence") {
2966
3040
  if (def.parsers.length < 2 || def.parsers.length > 5) return void 0;
2967
- const parts = def.parsers.map((part) => directFixedLexNode(part, resolve2, active, pureOnly));
3041
+ const parts = def.parsers.map((part) => directFixedLexNode(part, resolve2, active, pureOnly, cleared));
2968
3042
  if (parts.some((part) => part === void 0)) return void 0;
2969
3043
  return { kind: "sequence", parts };
2970
3044
  }
2971
3045
  if (def.tag === "choice") {
2972
- if (def.parsers.length !== 2 && def.parsers.length !== 4 && def.parsers.length !== 8 || def.strategy.tag !== "firstMatch" || def.gates.some((gate) => gate !== null) || def.autoNot.some((check) => check !== null)) return void 0;
2973
- const arms = def.parsers.map((arm) => directFixedLexNode(arm, resolve2, active, pureOnly));
3046
+ if (def.parsers.length < 2 || def.strategy.tag !== "firstMatch" || def.gates.some((gate) => gate !== null) || def.autoNot.some((check) => check !== null)) return void 0;
3047
+ const arms = def.parsers.map((arm) => directFixedLexNode(arm, resolve2, active, pureOnly, cleared));
2974
3048
  if (arms.some((arm) => arm === void 0)) return void 0;
2975
- const final = classifyFinalChoice(def.parsers, resolve2);
2976
3049
  return {
2977
3050
  kind: "choice",
2978
3051
  arms,
2979
- firstSets: final.firstSets,
3052
+ parsers: def.parsers,
3053
+ cleared,
2980
3054
  armExpected: def.parsers.map(deriveExpected),
2981
3055
  expected: def.parsers.flatMap(deriveExpected)
2982
3056
  };
2983
3057
  }
2984
3058
  if (def.tag === "not") {
2985
- const body = directFixedLexNode(def.parser, resolve2, active, true);
3059
+ const body = directFixedLexNode(def.parser, resolve2, active, true, cleared);
2986
3060
  return body === void 0 ? void 0 : {
2987
3061
  kind: "not",
2988
3062
  body,
@@ -2990,18 +3064,28 @@ function directFixedLexNode(parser2, resolve2, active, pureOnly = false) {
2990
3064
  };
2991
3065
  }
2992
3066
  if (def.tag === "optional") {
2993
- const body = directFixedLexNode(def.parser, resolve2, active, pureOnly);
3067
+ const body = directFixedLexNode(def.parser, resolve2, active, pureOnly, cleared);
2994
3068
  return body === void 0 ? void 0 : { kind: "optional", body };
2995
3069
  }
3070
+ if (def.tag === "many" || def.tag === "oneOrMore") {
3071
+ const body = directFixedLexNode(def.parser, resolve2, active, pureOnly, cleared);
3072
+ return body === void 0 ? void 0 : {
3073
+ kind: "repeat",
3074
+ body,
3075
+ min: def.tag === "many" ? 0 : def.min,
3076
+ max: def.max ?? -1
3077
+ };
3078
+ }
2996
3079
  return void 0;
2997
3080
  } finally {
2998
3081
  active.delete(current);
2999
3082
  }
3000
3083
  }
3001
3084
  function directFixedTokenBody(parser2, resolve2) {
3002
- const body = directTokenBodyParser(parser2, resolve2);
3085
+ const scope = { cleared: false };
3086
+ const body = directTokenBodyParser(parser2, resolve2, scope);
3003
3087
  if (body === void 0) return void 0;
3004
- const root = directFixedLexNode(body, resolve2, /* @__PURE__ */ new Set());
3088
+ const root = directFixedLexNode(body, resolve2, /* @__PURE__ */ new Set(), false, scope.cleared);
3005
3089
  return root === void 0 ? void 0 : { kind: "fixed-tree", root };
3006
3090
  }
3007
3091
  function directBalancedTokenBody(parser2, resolve2) {
@@ -4910,17 +4994,59 @@ function buildLexBodyRecognizer(spec, constants) {
4910
4994
  };
4911
4995
  }
4912
4996
 
4997
+ // src/table/run-support.ts
4998
+ function lineCol(ctx, offset) {
4999
+ const starts = ctx._lineStarts ?? [0];
5000
+ let lo = 0, hi = starts.length - 1;
5001
+ while (lo < hi) {
5002
+ const mid = lo + hi + 1 >> 1;
5003
+ if (starts[mid] <= offset) lo = mid;
5004
+ else hi = mid - 1;
5005
+ }
5006
+ return [lo + 1, offset - starts[lo] + 1];
5007
+ }
5008
+ function spanLines(ctx, start, end) {
5009
+ const s = lineCol(ctx, start), e = lineCol(ctx, end);
5010
+ return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] };
5011
+ }
5012
+ function rawEntry(v, input, s, e) {
5013
+ if (typeof v === "object" && v !== null) {
5014
+ const tg = v._tag;
5015
+ if (tg === "node" || tg === "leaf" || tg === "parseError") return v;
5016
+ }
5017
+ return {
5018
+ _tag: "leaf",
5019
+ value: typeof v === "string" ? v : typeof v === "object" && v !== null ? input.slice(s, e) : "",
5020
+ span: { start: s, end: e }
5021
+ };
5022
+ }
5023
+ function lead(input, pos) {
5024
+ if (pos >= input.length) return -1;
5025
+ const c = input.charCodeAt(pos);
5026
+ if (c < 55296 || c > 56319) return c;
5027
+ return input.codePointAt(pos) ?? c;
5028
+ }
5029
+ var EMPTY_KIDS = Object.freeze([]);
5030
+ var EMPTY_TRIVIA = Object.freeze([]);
5031
+ function collapseEmpty(ctx, build, input, pos, end, readsRaw) {
5032
+ const collected = ctx._cstBuf !== void 0 || ctx._cstChildren !== void 0;
5033
+ if (collected) popCstChild(ctx);
5034
+ const raw = readsRaw ? [rawEntry(void 0, input, pos, end)] : EMPTY_KIDS;
5035
+ const nd = build(EMPTY_KIDS, void 0, { start: pos, end }, raw, EMPTY_TRIVIA, void 0);
5036
+ if (collected) pushCstChild(ctx, nd, ctx._cstBuf?.noRaw === true ? void 0 : rawEntry(nd, input, pos, end));
5037
+ return nd;
5038
+ }
5039
+
4913
5040
  // src/table/lex-program.ts
4914
5041
  var LEX_NODE_TERMINAL = 0;
4915
5042
  var LEX_NODE_SEQUENCE2 = 1;
4916
5043
  var LEX_NODE_SEQUENCE3 = 2;
4917
5044
  var LEX_NODE_SEQUENCE4 = 3;
4918
5045
  var LEX_NODE_SEQUENCE5 = 4;
4919
- var LEX_NODE_CHOICE2 = 5;
4920
- var LEX_NODE_CHOICE4 = 6;
4921
- var LEX_NODE_CHOICE8 = 7;
5046
+ var LEX_NODE_CHOICE = 5;
4922
5047
  var LEX_NODE_NOT = 8;
4923
5048
  var LEX_NODE_OPTIONAL = 9;
5049
+ var LEX_NODE_REPEAT = 10;
4924
5050
  function lexProgramDigest(words) {
4925
5051
  let hash = 2166136261;
4926
5052
  for (const word of words) {
@@ -4949,85 +5075,114 @@ function trackLexLines(ctx, input, end) {
4949
5075
  ctx._lineScannedTo = end;
4950
5076
  }
4951
5077
  function terminalNode(matcher, failure, tracksLines) {
4952
- const match = (input, pos) => {
4953
- const result = matcher(input, pos);
4954
- return result < 0 ? -1 : result / 2;
4955
- };
4956
5078
  return {
4957
- match,
5079
+ match: (input, pos) => {
5080
+ const result = matcher(input, pos);
5081
+ return result < 0 ? -1 : result / 2;
5082
+ },
4958
5083
  run: (input, pos, ctx) => {
4959
- const end = match(input, pos);
4960
- if (end < 0) {
5084
+ const result = matcher(input, pos);
5085
+ if (result < 0) {
4961
5086
  ctx._fe = pos;
4962
5087
  ctx._fx = failure;
4963
5088
  if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
4964
5089
  return -1;
4965
5090
  }
5091
+ const end = result / 2;
4966
5092
  if (tracksLines) trackLexLines(ctx, input, end);
4967
5093
  return end;
4968
5094
  }
4969
5095
  };
4970
5096
  }
4971
5097
  function sequence2(a, b) {
5098
+ const am = a.match, bm = b.match, ar = a.run, br = b.run;
4972
5099
  return {
4973
5100
  match: (input, pos) => {
4974
- const e0 = a.match(input, pos);
4975
- return e0 < 0 ? -1 : b.match(input, e0);
5101
+ const e0 = am(input, pos);
5102
+ return e0 < 0 ? -1 : bm(input, e0);
4976
5103
  },
4977
5104
  run: (input, pos, ctx) => {
4978
- const e0 = a.run(input, pos, ctx);
5105
+ const e0 = ar(input, pos, ctx);
4979
5106
  if (e0 < 0) return -1;
4980
5107
  ctx._fc = false;
4981
- return b.run(input, e0, ctx);
5108
+ return br(input, e0, ctx);
4982
5109
  }
4983
5110
  };
4984
5111
  }
4985
5112
  function sequence3(a, b, c) {
5113
+ const am = a.match, bm = b.match, cm = c.match, ar = a.run, br = b.run, cr = c.run;
4986
5114
  return {
4987
5115
  match: (input, pos) => {
4988
- const e0 = a.match(input, pos);
5116
+ const e0 = am(input, pos);
4989
5117
  if (e0 < 0) return -1;
4990
- const e1 = b.match(input, e0);
4991
- return e1 < 0 ? -1 : c.match(input, e1);
5118
+ const e1 = bm(input, e0);
5119
+ return e1 < 0 ? -1 : cm(input, e1);
4992
5120
  },
4993
5121
  run: (input, pos, ctx) => {
4994
- const e0 = a.run(input, pos, ctx);
5122
+ const e0 = ar(input, pos, ctx);
4995
5123
  if (e0 < 0) return -1;
4996
5124
  ctx._fc = false;
4997
- const e1 = b.run(input, e0, ctx);
5125
+ const e1 = br(input, e0, ctx);
4998
5126
  if (e1 < 0) return -1;
4999
5127
  ctx._fc = false;
5000
- return c.run(input, e1, ctx);
5128
+ return cr(input, e1, ctx);
5001
5129
  }
5002
5130
  };
5003
5131
  }
5004
5132
  function sequence4(a, b, c, d) {
5005
- const abc = sequence3(a, b, c);
5133
+ const am = a.match, bm = b.match, cm = c.match, dm = d.match;
5134
+ const ar = a.run, br = b.run, cr = c.run, dr = d.run;
5006
5135
  return {
5007
5136
  match: (input, pos) => {
5008
- const end = abc.match(input, pos);
5009
- return end < 0 ? -1 : d.match(input, end);
5137
+ const e0 = am(input, pos);
5138
+ if (e0 < 0) return -1;
5139
+ const e1 = bm(input, e0);
5140
+ if (e1 < 0) return -1;
5141
+ const e2 = cm(input, e1);
5142
+ return e2 < 0 ? -1 : dm(input, e2);
5010
5143
  },
5011
5144
  run: (input, pos, ctx) => {
5012
- const end = abc.run(input, pos, ctx);
5013
- if (end < 0) return -1;
5145
+ const e0 = ar(input, pos, ctx);
5146
+ if (e0 < 0) return -1;
5147
+ ctx._fc = false;
5148
+ const e1 = br(input, e0, ctx);
5149
+ if (e1 < 0) return -1;
5014
5150
  ctx._fc = false;
5015
- return d.run(input, end, ctx);
5151
+ const e2 = cr(input, e1, ctx);
5152
+ if (e2 < 0) return -1;
5153
+ ctx._fc = false;
5154
+ return dr(input, e2, ctx);
5016
5155
  }
5017
5156
  };
5018
5157
  }
5019
5158
  function sequence5(a, b, c, d, e) {
5020
- const abcd = sequence4(a, b, c, d);
5159
+ const am = a.match, bm = b.match, cm = c.match, dm = d.match, em = e.match;
5160
+ const ar = a.run, br = b.run, cr = c.run, dr = d.run, er = e.run;
5021
5161
  return {
5022
5162
  match: (input, pos) => {
5023
- const end = abcd.match(input, pos);
5024
- return end < 0 ? -1 : e.match(input, end);
5163
+ const e0 = am(input, pos);
5164
+ if (e0 < 0) return -1;
5165
+ const e1 = bm(input, e0);
5166
+ if (e1 < 0) return -1;
5167
+ const e2 = cm(input, e1);
5168
+ if (e2 < 0) return -1;
5169
+ const e3 = dm(input, e2);
5170
+ return e3 < 0 ? -1 : em(input, e3);
5025
5171
  },
5026
5172
  run: (input, pos, ctx) => {
5027
- const end = abcd.run(input, pos, ctx);
5028
- if (end < 0) return -1;
5173
+ const e0 = ar(input, pos, ctx);
5174
+ if (e0 < 0) return -1;
5175
+ ctx._fc = false;
5176
+ const e1 = br(input, e0, ctx);
5177
+ if (e1 < 0) return -1;
5178
+ ctx._fc = false;
5179
+ const e2 = cr(input, e1, ctx);
5180
+ if (e2 < 0) return -1;
5181
+ ctx._fc = false;
5182
+ const e3 = dr(input, e2, ctx);
5183
+ if (e3 < 0) return -1;
5029
5184
  ctx._fc = false;
5030
- return e.run(input, end, ctx);
5185
+ return er(input, e3, ctx);
5031
5186
  }
5032
5187
  };
5033
5188
  }
@@ -5040,9 +5195,9 @@ function appendExpected(acc, values) {
5040
5195
  function choiceNode(arms, classes, armExpected, choiceExpected) {
5041
5196
  let matchTail = () => -1;
5042
5197
  for (let i = arms.length - 1; i >= 0; i--) {
5043
- const arm = arms[i], next = matchTail;
5198
+ const armMatch = arms[i].match, next = matchTail;
5044
5199
  matchTail = (input, pos) => {
5045
- const end = arm.match(input, pos);
5200
+ const end = armMatch(input, pos);
5046
5201
  return end < 0 ? next(input, pos) : end;
5047
5202
  };
5048
5203
  }
@@ -5052,7 +5207,7 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5052
5207
  return -1;
5053
5208
  };
5054
5209
  for (let i = arms.length - 1; i >= 0; i--) {
5055
- const arm = arms[i], cls = classes[i], staticExpected = armExpected[i], next = tail;
5210
+ const armRun = arms[i].run, cls = classes[i], staticExpected = armExpected[i], next = tail;
5056
5211
  tail = (input, pos, ctx, lead2, acc, best) => {
5057
5212
  if (cls !== void 0 && !classHas(cls, lead2)) {
5058
5213
  return next(
@@ -5065,7 +5220,7 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5065
5220
  );
5066
5221
  }
5067
5222
  ctx._fc = false;
5068
- const end = arm.run(input, pos, ctx);
5223
+ const end = armRun(input, pos, ctx);
5069
5224
  if (end >= 0) return end;
5070
5225
  const at = ctx._fe ?? pos;
5071
5226
  if (at > best) {
@@ -5092,11 +5247,58 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
5092
5247
  )
5093
5248
  };
5094
5249
  }
5250
+ function exclusiveChoiceNode(arms, classes, choiceFx) {
5251
+ const ascii = (classes.length <= 128 ? new Int8Array(128) : new Int32Array(128)).fill(-1);
5252
+ const hi = [];
5253
+ for (let i = 0; i < classes.length; i++) {
5254
+ const cls = classes[i];
5255
+ for (let c = 0; c < 128; c++) if (cls.ascii[c] === 1) ascii[c] = i;
5256
+ for (let r = 0; r < cls.hi.length; r += 2) hi.push(cls.hi[r], cls.hi[r + 1], i);
5257
+ }
5258
+ const select = (code) => {
5259
+ if (code < 0) return -1;
5260
+ if (code < 128) return ascii[code];
5261
+ for (let r = 0; r < hi.length; r += 3) if (code >= hi[r] && code <= hi[r + 1]) return hi[r + 2];
5262
+ return -1;
5263
+ };
5264
+ let matchArm = () => -1;
5265
+ let selectRun = () => -1;
5266
+ for (let i = arms.length - 1; i >= 0; i--) {
5267
+ const armMatch = arms[i].match, armRun = arms[i].run, nextMatch = matchArm, nextRun = selectRun;
5268
+ matchArm = (selected, input, pos) => selected === i ? armMatch(input, pos) : nextMatch(selected, input, pos);
5269
+ selectRun = (selected, input, pos, ctx) => selected === i ? armRun(input, pos, ctx) : nextRun(selected, input, pos, ctx);
5270
+ }
5271
+ const runArm = (input, pos, ctx) => {
5272
+ const arm = select(lead(input, pos));
5273
+ if (arm >= 0) {
5274
+ ctx._fc = false;
5275
+ const end = selectRun(arm, input, pos, ctx);
5276
+ if (end >= 0) return end;
5277
+ if (committed(ctx)) return -1;
5278
+ const failed = ctx._fx;
5279
+ if (failed !== void 0 && failed.length > 0) {
5280
+ ctx._fe = pos;
5281
+ return -1;
5282
+ }
5283
+ }
5284
+ ctx._fe = pos;
5285
+ ctx._fx = choiceFx;
5286
+ return -1;
5287
+ };
5288
+ return {
5289
+ match: (input, pos) => {
5290
+ const arm = select(lead(input, pos));
5291
+ return arm < 0 ? -1 : matchArm(arm, input, pos);
5292
+ },
5293
+ run: runArm
5294
+ };
5295
+ }
5095
5296
  function notNode(body, failure) {
5297
+ const bodyMatch = body.match;
5096
5298
  return {
5097
- match: (input, pos) => body.match(input, pos) < 0 ? pos : -1,
5299
+ match: (input, pos) => bodyMatch(input, pos) < 0 ? pos : -1,
5098
5300
  run: (input, pos, ctx) => {
5099
- if (body.match(input, pos) < 0) return pos;
5301
+ if (bodyMatch(input, pos) < 0) return pos;
5100
5302
  ctx._fe = pos;
5101
5303
  ctx._fx = failure;
5102
5304
  return -1;
@@ -5104,18 +5306,57 @@ function notNode(body, failure) {
5104
5306
  };
5105
5307
  }
5106
5308
  function optionalNode(body) {
5309
+ const bodyMatch = body.match, bodyRun = body.run;
5107
5310
  return {
5108
5311
  match: (input, pos) => {
5109
- const end = body.match(input, pos);
5312
+ const end = bodyMatch(input, pos);
5110
5313
  return end < 0 ? pos : end;
5111
5314
  },
5112
5315
  run: (input, pos, ctx) => {
5113
5316
  ctx._fc = false;
5114
- const end = body.run(input, pos, ctx);
5317
+ const end = bodyRun(input, pos, ctx);
5115
5318
  return end < 0 && !committed(ctx) ? pos : end;
5116
5319
  }
5117
5320
  };
5118
5321
  }
5322
+ function repeatNode(item, min, max) {
5323
+ const itemMatch = item.match, itemRun = item.run;
5324
+ return {
5325
+ match: (input, pos) => {
5326
+ let cur = pos;
5327
+ let count = 0;
5328
+ for (; ; ) {
5329
+ if (max >= 0 && count >= max) break;
5330
+ const optional2 = count >= min && (count > 0 || min === 0);
5331
+ if (optional2 && cur >= input.length) break;
5332
+ const end = itemMatch(input, cur);
5333
+ if (end < 0 || optional2 && end === cur) break;
5334
+ cur = end;
5335
+ count++;
5336
+ }
5337
+ return count < min ? -1 : cur;
5338
+ },
5339
+ run: (input, pos, ctx) => {
5340
+ let cur = pos;
5341
+ let count = 0;
5342
+ for (; ; ) {
5343
+ if (max >= 0 && count >= max) break;
5344
+ const optional2 = count >= min && (count > 0 || min === 0);
5345
+ if (optional2 && cur >= input.length) break;
5346
+ ctx._fc = false;
5347
+ const end = itemRun(input, cur, ctx);
5348
+ if (end < 0) {
5349
+ if (committed(ctx)) return -1;
5350
+ break;
5351
+ }
5352
+ if (optional2 && end === cur) break;
5353
+ cur = end;
5354
+ count++;
5355
+ }
5356
+ return count < min ? -1 : cur;
5357
+ }
5358
+ };
5359
+ }
5119
5360
  function fixedTreeRunner(words, matchers, classes, expected) {
5120
5361
  const stack = [];
5121
5362
  const pop = () => {
@@ -5143,9 +5384,14 @@ function fixedTreeRunner(words, matchers, classes, expected) {
5143
5384
  else stack.push(sequence5(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4]));
5144
5385
  continue;
5145
5386
  }
5146
- if (op >= LEX_NODE_CHOICE2 && op <= LEX_NODE_CHOICE8) {
5147
- const count = op === LEX_NODE_CHOICE2 ? 2 : op === LEX_NODE_CHOICE4 ? 4 : 8;
5387
+ if (op === LEX_NODE_CHOICE) {
5388
+ const count = words[at++];
5389
+ const exclusive = words[at++];
5390
+ if (exclusive !== 0 && exclusive !== 1) throw new TypeError("table lexical tree choice has an invalid dispatch mode");
5148
5391
  const choiceFx = expected[words[at++]];
5392
+ if (!Number.isInteger(count) || count < 2 || count > stack.length) {
5393
+ throw new TypeError("table lexical tree choice has an invalid arm count");
5394
+ }
5149
5395
  if (choiceFx === void 0) throw new TypeError("table lexical tree choice has an invalid expected set");
5150
5396
  const armClasses = [];
5151
5397
  const armFx = [];
@@ -5158,7 +5404,7 @@ function fixedTreeRunner(words, matchers, classes, expected) {
5158
5404
  armFx.push(fx);
5159
5405
  }
5160
5406
  const arms = Array.from({ length: count }, pop).reverse();
5161
- stack.push(choiceNode(arms, armClasses, armFx, choiceFx));
5407
+ stack.push(exclusive === 1 ? exclusiveChoiceNode(arms, armClasses, choiceFx) : choiceNode(arms, armClasses, armFx, choiceFx));
5162
5408
  continue;
5163
5409
  }
5164
5410
  if (op === LEX_NODE_NOT) {
@@ -5171,6 +5417,14 @@ function fixedTreeRunner(words, matchers, classes, expected) {
5171
5417
  stack.push(optionalNode(pop()));
5172
5418
  continue;
5173
5419
  }
5420
+ if (op === LEX_NODE_REPEAT) {
5421
+ const min = words[at++], max = words[at++];
5422
+ if (!Number.isInteger(min) || min < 0 || !Number.isInteger(max) || max >= 0 && max < min) {
5423
+ throw new TypeError("table lexical tree repeat has invalid bounds");
5424
+ }
5425
+ stack.push(repeatNode(pop(), min, max));
5426
+ continue;
5427
+ }
5174
5428
  throw new TypeError("table lexical tree has an unknown fixed body");
5175
5429
  }
5176
5430
  if (stack.length !== 1) throw new TypeError("table lexical tree does not resolve to one root body");
@@ -5539,75 +5793,270 @@ function resolveTable(prog) {
5539
5793
  return built;
5540
5794
  }
5541
5795
 
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}`;
5796
+ // src/combinators/adjacency.ts
5797
+ function adjacencyOf(p) {
5798
+ return p._def.tag === "adjacency" ? p._def : null;
5545
5799
  }
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
- );
5561
- }
5562
- }
5563
- return out;
5800
+ function adjacencyExpected(def) {
5801
+ if (def.polarity === "adjacent") return "adjacent";
5802
+ return def.kinds === void 0 ? "notAdjacent" : `notAdjacent(${def.kinds.join("|")})`;
5564
5803
  }
5565
- var _sinks = [];
5566
- function beginDegradationCapture() {
5567
- _sinks.push(/* @__PURE__ */ new Map());
5804
+ function holdsAdjacency(input, cur, ctx, def) {
5805
+ return adjacencyHolds(input, cur, ctx, def.polarity === "notAdjacent", def.kinds);
5568
5806
  }
5569
- function degradationCaptureDepth() {
5570
- return _sinks.length;
5807
+ function adjacencyHolds(input, cur, ctx, negated, kinds) {
5808
+ const end = probeTriviaEnd(input, cur, ctx);
5809
+ if (!negated) return end === cur;
5810
+ if (end === cur) return false;
5811
+ if (kinds === void 0) return true;
5812
+ const want = resolveAdjacencyKindMask(ctx.trivia, kinds);
5813
+ const spec = analyzeLabeledTrivia(ctx.trivia);
5814
+ return (triviaKindMaskAt(input, cur, spec, ctx.state) & want) !== 0;
5571
5815
  }
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;
5816
+ function adjacencyFail(pos, def) {
5817
+ return { ok: false, expected: [adjacencyExpected(def)], span: { start: pos, end: pos } };
5579
5818
  }
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";
5819
+ 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.";
5820
+ function adjacencyMisuse(polarity) {
5821
+ return new TypeError(`${polarity}(): ${NO_STANDALONE_PARSE}`);
5585
5822
  }
5586
- function degradationKey(d) {
5587
- return JSON.stringify([d.code, d.where, d.subject]);
5823
+ function adjacencyCombinator(def) {
5824
+ const meta = {
5825
+ // Zero-width and never leading (sequence() rejects index 0), so it contributes
5826
+ // no first char. `isZeroWidthAssertion` drops it from a sequence's first-set
5827
+ // rather than letting `any` poison the enclosing choice's dispatch.
5828
+ firstSet: { kind: "any" },
5829
+ canMatchNewline: false,
5830
+ isTrivia: false
5831
+ };
5832
+ return {
5833
+ _tag: "adjacency",
5834
+ _meta: meta,
5835
+ _def: def,
5836
+ parse() {
5837
+ throw adjacencyMisuse(def.polarity);
5838
+ }
5839
+ };
5588
5840
  }
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));
5841
+ function adjacent() {
5842
+ return adjacencyCombinator({ tag: "adjacency", polarity: "adjacent" });
5603
5843
  }
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) => {
5844
+ function notAdjacent(options) {
5845
+ const kinds = options?.kinds;
5846
+ if (kinds !== void 0) {
5847
+ if (!Array.isArray(kinds) || kinds.length === 0) {
5848
+ throw new TypeError("notAdjacent({ kinds }): kinds must be a non-empty array of trivia category names.");
5849
+ }
5850
+ for (const k of kinds) {
5851
+ if (typeof k !== "string" || k.length === 0) {
5852
+ throw new TypeError(`notAdjacent({ kinds }): every kind must be a non-empty string, got ${JSON.stringify(k)}.`);
5853
+ }
5854
+ }
5855
+ }
5856
+ return adjacencyCombinator(
5857
+ kinds === void 0 ? { tag: "adjacency", polarity: "notAdjacent" } : { tag: "adjacency", polarity: "notAdjacent", kinds: [...kinds] }
5858
+ );
5859
+ }
5860
+
5861
+ // src/combinators/sequence.ts
5862
+ function sequence(...parsers) {
5863
+ let adjacency = null;
5864
+ for (let i = 0; i < parsers.length; i++) {
5865
+ const a = adjacencyOf(parsers[i]);
5866
+ if (a === null) continue;
5867
+ if (i === 0) {
5868
+ throw new TypeError(
5869
+ `sequence(): ${a.polarity}() cannot be the FIRST term \u2014 an adjacency assertion tests the gap after the PRECEDING term. Put a concrete term first.`
5870
+ );
5871
+ }
5872
+ ;
5873
+ (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
5874
+ }
5875
+ const hasAdjacency = adjacency !== null;
5876
+ let leading = null;
5877
+ for (let i = 1; i < parsers.length; i++) {
5878
+ if (leadsTrivia(parsers[i])) (leading ??= Array.from({ length: parsers.length }, () => false))[i] = true;
5879
+ }
5880
+ const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
5881
+ const meta = {
5882
+ // Union through the nullable prefix — a leading optional()/many() lets a later
5883
+ // term's first char start the sequence. Just `parsers[0]` under-approximates.
5884
+ firstSet: sequenceFirstSet(parsers),
5885
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
5886
+ isTrivia: false
5887
+ };
5888
+ const def = { tag: "sequence", parsers };
5889
+ const scalarParsers = parsers.map(scalarOf);
5890
+ const parseScalar = (input, pos, ctx) => {
5891
+ let cur = pos;
5892
+ if (strictArity !== 0) {
5893
+ cur = scalarParsers[0](input, cur, ctx);
5894
+ if (cur < 0) return cur;
5895
+ const first = ctx._sv;
5896
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
5897
+ cur = scalarParsers[1](input, cur, ctx);
5898
+ if (cur < 0) return cur;
5899
+ const second = ctx._sv;
5900
+ if (strictArity === 2) {
5901
+ ctx._sv = def.valueUnused ? void 0 : [first, second];
5902
+ return cur;
5903
+ }
5904
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
5905
+ cur = scalarParsers[2](input, cur, ctx);
5906
+ if (cur < 0) return cur;
5907
+ ctx._sv = def.valueUnused ? void 0 : [first, second, ctx._sv];
5908
+ return cur;
5909
+ }
5910
+ const values = def.valueUnused ? void 0 : [];
5911
+ for (let i = 0; i < scalarParsers.length; i++) {
5912
+ let termPos = cur;
5913
+ if (i > 0 && ctx.trivia) termPos = advanceTrivia(input, cur, ctx);
5914
+ const end = scalarParsers[i](input, termPos, ctx);
5915
+ if (end < 0) return end;
5916
+ if (values !== void 0) values.push(ctx._sv);
5917
+ if (end > termPos || termPos === cur) cur = end;
5918
+ }
5919
+ ctx._sv = values;
5920
+ return cur;
5921
+ };
5922
+ let followSentinels;
5923
+ return {
5924
+ _tag: "sequence",
5925
+ _meta: meta,
5926
+ _def: def,
5927
+ _parseScalar: hasAdjacency || leading !== null ? void 0 : parseScalar,
5928
+ parse(input, pos, ctx) {
5929
+ const tolerant = ctx._tolerant === true;
5930
+ if (tolerant) {
5931
+ followSentinels ??= parsers.map((_, i) => firstSetSentinel(
5932
+ parsers.slice(i + 1).reduce(
5933
+ (set, parser2) => union(set, firstSetOf(parser2)),
5934
+ { kind: "empty" }
5935
+ )
5936
+ ));
5937
+ }
5938
+ const inheritedSync = ctx._sync;
5939
+ const deferredTrivia = needsDeferredTriviaCommit(ctx);
5940
+ const values = def.valueUnused ? void 0 : [];
5941
+ let cur = pos;
5942
+ try {
5943
+ for (let i = 0; i < parsers.length; i++) {
5944
+ if (tolerant) ctx._sync = followSentinels[i] ?? inheritedSync;
5945
+ const assertion = adjacency?.[i];
5946
+ if (assertion) {
5947
+ if (!holdsAdjacency(input, cur, ctx, assertion)) return adjacencyFail(cur, assertion);
5948
+ if (values !== void 0) values.push(null);
5949
+ continue;
5950
+ }
5951
+ let termPos = cur;
5952
+ const markTlog = deferredTrivia ? cstTlLen(ctx) : 0;
5953
+ const markLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
5954
+ const markRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
5955
+ if (ctx.trivia && i > 0) {
5956
+ termPos = deferredTrivia ? commitTriviaScan(scanTriviaCompact(input, cur, ctx)) : advanceTrivia(input, cur, ctx);
5957
+ }
5958
+ const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
5959
+ const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
5960
+ const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
5961
+ if (leading?.[i] === true) LEAD.at = cur;
5962
+ const result = parsers[i].parse(input, termPos, ctx);
5963
+ if (!result.ok) return result;
5964
+ if (values !== void 0) values.push(result.value);
5965
+ if (result.span.end > termPos || termPos === cur) cur = result.span.end;
5966
+ else if (deferredTrivia) {
5967
+ rollbackScannedTriviaAt(
5968
+ ctx,
5969
+ markTlog,
5970
+ scanTlog,
5971
+ markLog,
5972
+ scanLog,
5973
+ markRootLog,
5974
+ scanRootLog
5975
+ );
5976
+ }
5977
+ }
5978
+ } finally {
5979
+ if (tolerant) ctx._sync = inheritedSync;
5980
+ }
5981
+ return {
5982
+ ok: true,
5983
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
5984
+ value: values ?? void 0,
5985
+ span: { start: pos, end: cur }
5986
+ };
5987
+ }
5988
+ };
5989
+ }
5990
+
5991
+ // src/compiler/degradation.ts
5992
+ function formatDegradation(d) {
5993
+ return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
5994
+ }
5995
+ var DETAIL_CAP = 8;
5996
+ function formatDegradations(list) {
5997
+ const byCode = /* @__PURE__ */ new Map();
5998
+ for (const d of list) {
5999
+ const bucket = byCode.get(d.code);
6000
+ if (bucket) bucket.push(d);
6001
+ else byCode.set(d.code, [d]);
6002
+ }
6003
+ const out = [];
6004
+ for (const [code, items] of byCode) {
6005
+ for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
6006
+ if (items.length > DETAIL_CAP) {
6007
+ out.push(
6008
+ `[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.`
6009
+ );
6010
+ }
6011
+ }
6012
+ return out;
6013
+ }
6014
+ var _sinks = [];
6015
+ function beginDegradationCapture() {
6016
+ _sinks.push(/* @__PURE__ */ new Map());
6017
+ }
6018
+ function degradationCaptureDepth() {
6019
+ return _sinks.length;
6020
+ }
6021
+ function unwindDegradationCapture(depth) {
6022
+ const found = [];
6023
+ while (_sinks.length > depth) {
6024
+ const sink = _sinks.pop();
6025
+ found.unshift(...sink.values());
6026
+ }
6027
+ return found;
6028
+ }
6029
+ function resolveDegradationLevel(explicit) {
6030
+ if (explicit !== void 0) return explicit;
6031
+ const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
6032
+ if (env === "off" || env === "warn" || env === "error") return env;
6033
+ return "warn";
6034
+ }
6035
+ function degradationKey(d) {
6036
+ return JSON.stringify([d.code, d.where, d.subject]);
6037
+ }
6038
+ function recordDegradation(d) {
6039
+ const level = resolveDegradationLevel();
6040
+ if (level === "off") return;
6041
+ const key = degradationKey(d);
6042
+ const sink = _sinks[_sinks.length - 1];
6043
+ if (sink) {
6044
+ if (!sink.has(key)) sink.set(key, d);
6045
+ return;
6046
+ }
6047
+ if (_immediate.has(key)) return;
6048
+ _immediate.add(key);
6049
+ if (level === "error") throw new Error(`parseman: degraded compilation path
6050
+ ${formatDegradation(d)}`);
6051
+ console.warn(formatDegradation(d));
6052
+ }
6053
+ var _immediate = /* @__PURE__ */ new Set();
6054
+ function beginCompileDegradationDrain() {
6055
+ if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
6056
+ };
6057
+ const depth = degradationCaptureDepth();
6058
+ beginDegradationCapture();
6059
+ return (report) => {
5611
6060
  const found = unwindDegradationCapture(depth);
5612
6061
  if (!report || found.length === 0) return;
5613
6062
  const fresh = found.filter((d) => {
@@ -5717,91 +6166,620 @@ function splitTopLevel(inner) {
5717
6166
  last = i + 1;
5718
6167
  }
5719
6168
  }
5720
- if (depth !== 0 || angle !== 0) return null;
5721
- parts.push(inner.slice(last));
5722
- return parts;
6169
+ if (depth !== 0 || angle !== 0) return null;
6170
+ parts.push(inner.slice(last));
6171
+ return parts;
6172
+ }
6173
+ function isConfirmableParam(part) {
6174
+ const head = PARAM_HEAD_RE.exec(part);
6175
+ if (!head) return false;
6176
+ const rest = part.slice(head[0].length);
6177
+ if (rest === "") return true;
6178
+ if (rest[0] !== ":") return false;
6179
+ return !hasDefaultAssign(rest.slice(1));
6180
+ }
6181
+ function hasDefaultAssign(annotation) {
6182
+ for (let i = 0; i < annotation.length; i++) {
6183
+ if (annotation[i] !== "=") continue;
6184
+ if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
6185
+ i++;
6186
+ continue;
6187
+ }
6188
+ const prev = annotation[i - 1];
6189
+ if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
6190
+ return true;
6191
+ }
6192
+ return false;
6193
+ }
6194
+ function buildAnalysisSrc(def) {
6195
+ return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
6196
+ }
6197
+ var RESOLVER_REASONS = {
6198
+ "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
6199
+ "arguments": "its body references `arguments`",
6200
+ "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
6201
+ "not-a-function": "the binding it names is not a function declaration",
6202
+ "unresolved-import": "its module could not be resolved or parsed",
6203
+ "not-found": "the name did not resolve to any binding",
6204
+ "computed": "it is a computed expression",
6205
+ "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
6206
+ "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
6207
+ };
6208
+ function unconfirmableReason(def, src) {
6209
+ const declared = def.buildArityUnresolved;
6210
+ if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
6211
+ const s = src.trim();
6212
+ const params = s.slice(0, s.indexOf(")") + 1);
6213
+ if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
6214
+ if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
6215
+ if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
6216
+ return "it names a binding this build could not resolve to a function declaration";
6217
+ }
6218
+ return "its parameter list could not be parsed";
6219
+ }
6220
+ function confirmedArityForDef(def) {
6221
+ if (def.buildArity !== void 0) return def.buildArity;
6222
+ const src = buildAnalysisSrc(def);
6223
+ const arity = confirmedBuildArity(src);
6224
+ if (arity !== null) return arity;
6225
+ const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
6226
+ recordDegradation({
6227
+ code: "build-arity-unconfirmed",
6228
+ severity: "warn",
6229
+ where: `node("${def.type ?? "<inferred>"}")`,
6230
+ subject,
6231
+ 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`,
6232
+ 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"
6233
+ });
6234
+ return null;
6235
+ }
6236
+ function buildReadsRaw(def) {
6237
+ if (!def.build) return true;
6238
+ if (def.buildRawUnused === true) return false;
6239
+ const arity = confirmedArityForDef(def);
6240
+ if (arity === null) return true;
6241
+ return arity >= 4;
6242
+ }
6243
+ function buildReadsTrivia(def) {
6244
+ if (!def.build) return true;
6245
+ const arity = confirmedArityForDef(def);
6246
+ if (arity === null) return true;
6247
+ return arity >= 5;
6248
+ }
6249
+ function buildReadsState(def) {
6250
+ if (!def.build) return true;
6251
+ const arity = confirmedArityForDef(def);
6252
+ if (arity === null) return true;
6253
+ return arity >= 6;
6254
+ }
6255
+
6256
+ // src/combinators/token.ts
6257
+ function semanticLeafValue(_ctx, _input, _start, _end, value) {
6258
+ return value;
6259
+ }
6260
+ function sourceLeafValue(ctx, input, start, end, value) {
6261
+ return cstOutputHost(ctx.build) ? input.slice(start, end) : value;
6262
+ }
6263
+ function token(root) {
6264
+ const meta = {
6265
+ firstSet: root._meta.firstSet,
6266
+ canMatchNewline: root._meta.canMatchNewline,
6267
+ isTrivia: false
6268
+ };
6269
+ return {
6270
+ _tag: "token",
6271
+ _meta: meta,
6272
+ _def: { tag: "token", parser: root },
6273
+ parse(input, pos, ctx) {
6274
+ const savedTrivia = ctx.trivia;
6275
+ const savedKinds = ctx.triviaKindLabels;
6276
+ const savedBuf = ctx._cstBuf;
6277
+ const savedChildren = ctx._cstChildren;
6278
+ const savedLeaves = ctx._cstLeaves;
6279
+ const savedRaw = ctx._cstRawChildren;
6280
+ const savedTriviaLog = ctx._cstTriviaLog;
6281
+ const savedOuterTriviaLog = ctx._triviaLog;
6282
+ const savedRootTriviaLog = ctx._rootTriviaLog;
6283
+ const wasCapturing = cstCaptureActive(ctx);
6284
+ ctx.trivia = void 0;
6285
+ ctx.triviaKindLabels = void 0;
6286
+ ctx._cstBuf = void 0;
6287
+ ctx._cstChildren = void 0;
6288
+ ctx._cstLeaves = void 0;
6289
+ ctx._cstRawChildren = void 0;
6290
+ ctx._cstTriviaLog = void 0;
6291
+ ctx._triviaLog = void 0;
6292
+ ctx._rootTriviaLog = void 0;
6293
+ let result;
6294
+ try {
6295
+ result = root.parse(input, pos, ctx);
6296
+ } finally {
6297
+ ctx.trivia = savedTrivia;
6298
+ ctx.triviaKindLabels = savedKinds;
6299
+ ctx._cstBuf = savedBuf;
6300
+ ctx._cstChildren = savedChildren;
6301
+ ctx._cstLeaves = savedLeaves;
6302
+ ctx._cstRawChildren = savedRaw;
6303
+ ctx._cstTriviaLog = savedTriviaLog;
6304
+ ctx._triviaLog = savedOuterTriviaLog;
6305
+ ctx._rootTriviaLog = savedRootTriviaLog;
6306
+ }
6307
+ if (!result.ok) return result;
6308
+ const span = result.span;
6309
+ const value = input.slice(pos, span.end);
6310
+ if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span });
6311
+ return { ok: true, value, span };
6312
+ }
6313
+ };
6314
+ }
6315
+ function makeLeaf(root, fn, sourceCstValue) {
6316
+ const cstValue = sourceCstValue ? sourceLeafValue : semanticLeafValue;
6317
+ const meta = {
6318
+ firstSet: root._meta.firstSet,
6319
+ canMatchNewline: root._meta.canMatchNewline,
6320
+ isTrivia: false
6321
+ };
6322
+ return {
6323
+ _tag: "leaf",
6324
+ _meta: meta,
6325
+ _def: {
6326
+ tag: "leaf",
6327
+ parser: root,
6328
+ fn,
6329
+ ...sourceCstValue ? { cstValue: "source" } : {}
6330
+ },
6331
+ parse(input, pos, ctx) {
6332
+ const savedBuf = ctx._cstBuf;
6333
+ const savedChildren = ctx._cstChildren;
6334
+ const savedLeaves = ctx._cstLeaves;
6335
+ const savedRaw = ctx._cstRawChildren;
6336
+ const savedTriviaLog = ctx._cstTriviaLog;
6337
+ const savedOuterTriviaLog = ctx._triviaLog;
6338
+ const wasCapturing = cstCaptureActive(ctx);
6339
+ ctx._cstBuf = void 0;
6340
+ ctx._cstChildren = void 0;
6341
+ ctx._cstLeaves = void 0;
6342
+ ctx._cstRawChildren = void 0;
6343
+ ctx._cstTriviaLog = void 0;
6344
+ ctx._triviaLog = void 0;
6345
+ let result;
6346
+ try {
6347
+ result = root.parse(input, pos, ctx);
6348
+ } finally {
6349
+ ctx._cstBuf = savedBuf;
6350
+ ctx._cstChildren = savedChildren;
6351
+ ctx._cstLeaves = savedLeaves;
6352
+ ctx._cstRawChildren = savedRaw;
6353
+ ctx._cstTriviaLog = savedTriviaLog;
6354
+ ctx._triviaLog = savedOuterTriviaLog;
6355
+ }
6356
+ if (!result.ok) return result;
6357
+ const value = fn(result.value, result.span);
6358
+ if (wasCapturing) pushCstLeaf(ctx, {
6359
+ _tag: "leaf",
6360
+ value: cstValue(ctx, input, pos, result.span.end, value),
6361
+ span: result.span
6362
+ });
6363
+ return { ok: true, value, span: result.span };
6364
+ }
6365
+ };
6366
+ }
6367
+ function leaf(root, fn) {
6368
+ return makeLeaf(root, fn, false);
6369
+ }
6370
+ function sourceLeaf(root, fn) {
6371
+ return makeLeaf(root, fn, true);
6372
+ }
6373
+
6374
+ // src/compiler/value-usage.ts
6375
+ function markUnusedValues(root) {
6376
+ const seenConsumed = /* @__PURE__ */ new WeakSet();
6377
+ const seenUnconsumed = /* @__PURE__ */ new WeakSet();
6378
+ const mark = (def, consumed) => {
6379
+ if (consumed) def.valueUnused = false;
6380
+ else if (def.valueUnused !== false) def.valueUnused = true;
6381
+ };
6382
+ const visit = (c, consumed) => {
6383
+ const seen = consumed ? seenConsumed : seenUnconsumed;
6384
+ if (seen.has(c)) return;
6385
+ seen.add(c);
6386
+ const def = c._def;
6387
+ switch (def.tag) {
6388
+ case "many":
6389
+ case "oneOrMore":
6390
+ mark(def, consumed);
6391
+ visit(def.parser, consumed);
6392
+ return;
6393
+ case "optional":
6394
+ visit(def.parser, consumed);
6395
+ return;
6396
+ case "sequence":
6397
+ mark(def, consumed);
6398
+ for (const p of def.parsers) visit(p, consumed);
6399
+ return;
6400
+ case "choice":
6401
+ for (const p of def.parsers) visit(p, consumed);
6402
+ return;
6403
+ case "dispatch":
6404
+ visit(def.selector, consumed);
6405
+ for (const entry of def.cases) visit(entry.parser, consumed);
6406
+ if (def.matchers) for (const entry of def.matchers) visit(entry.parser, consumed);
6407
+ if (def.otherwise) visit(def.otherwise, consumed);
6408
+ return;
6409
+ case "node":
6410
+ visit(def.parser, false);
6411
+ return;
6412
+ case "transform":
6413
+ visit(def.parser, true);
6414
+ return;
6415
+ case "field":
6416
+ visit(def.parser, true);
6417
+ return;
6418
+ case "sepBy":
6419
+ visit(def.parser, true);
6420
+ visit(def.separator, false);
6421
+ return;
6422
+ case "not":
6423
+ case "peek":
6424
+ visit(def.parser, false);
6425
+ return;
6426
+ case "grammar":
6427
+ visit(def.parser, consumed);
6428
+ if (def.triviaParser) visit(def.triviaParser, false);
6429
+ return;
6430
+ case "recover":
6431
+ visit(def.parser, consumed);
6432
+ visit(def.sentinel, false);
6433
+ return;
6434
+ case "token":
6435
+ visit(def.parser, false);
6436
+ return;
6437
+ case "label":
6438
+ case "trivia":
6439
+ case "attempt":
6440
+ case "withCtx":
6441
+ case "expect":
6442
+ visit(def.parser, consumed);
6443
+ return;
6444
+ case "leaf":
6445
+ visit(def.parser, true);
6446
+ return;
6447
+ case "lazy":
6448
+ return;
6449
+ default:
6450
+ return;
6451
+ }
6452
+ };
6453
+ const resolveRoot = (c) => {
6454
+ const guard = /* @__PURE__ */ new Set();
6455
+ let cur = c;
6456
+ while (!guard.has(cur)) {
6457
+ const d = cur._def;
6458
+ if (d.tag === "grammar") {
6459
+ guard.add(cur);
6460
+ cur = d.parser;
6461
+ continue;
6462
+ }
6463
+ if (d.tag !== "lazy") return cur;
6464
+ guard.add(cur);
6465
+ try {
6466
+ cur = d.thunk();
6467
+ } catch {
6468
+ return c;
6469
+ }
6470
+ }
6471
+ return cur;
6472
+ };
6473
+ visit(resolveRoot(root), true);
6474
+ }
6475
+
6476
+ // src/cst/root-trivia-scope.ts
6477
+ var ROOT_TRIVIA_SCOPE_REFUSAL = "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'.";
6478
+ function refuseUnclassifiedRootScope(strict) {
6479
+ if (strict === true) throw new TypeError(ROOT_TRIVIA_SCOPE_REFUSAL);
6480
+ }
6481
+
6482
+ // src/combinators/grammar.ts
6483
+ function annotateResultLines(result, index) {
6484
+ const annotated = { ...result, span: annotateSpan(result.span, index) };
6485
+ const errors = annotated.errors;
6486
+ if (errors) {
6487
+ ;
6488
+ annotated.errors = errors.map((error) => ({
6489
+ ...error,
6490
+ span: annotateSpan(error.span, index)
6491
+ }));
6492
+ }
6493
+ return annotated;
6494
+ }
6495
+ function createParseLineContext(input, pos) {
6496
+ const lineIndex = createLineIndex();
6497
+ if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
6498
+ return { lineIndex, lineScannedTo: pos };
6499
+ }
6500
+ var TRACK_LINES_ON = () => true;
6501
+ var TRACK_LINES_OFF = () => false;
6502
+ var TRACK_LINES_INHERIT = (ctx) => ctx?.trackLines === true;
6503
+ function parser(opts, root) {
6504
+ const clearTrivia = opts.trivia === null;
6505
+ const opaqueRootCapture = opts.rootCapture === "opaque";
6506
+ if (opaqueRootCapture && opts.trivia === void 0) {
6507
+ throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
6508
+ }
6509
+ const scopeTrivia = clearTrivia ? void 0 : opts.trivia ?? void 0;
6510
+ const scopeLabels = scopeTrivia?._meta?.triviaKindLabels;
6511
+ const refuseUnclassified = scopeTrivia !== void 0 && !scopeTrivia._meta?.rootTriviaClassified && !opaqueRootCapture;
6512
+ const forceCaptureTrivia = opts.captureTrivia === true;
6513
+ const trackLinesPolicy = opts.trackLines === true ? "on" : opts.trackLines === false ? "off" : "inherit";
6514
+ const trackLinesOf = trackLinesPolicy === "on" ? TRACK_LINES_ON : trackLinesPolicy === "off" ? TRACK_LINES_OFF : TRACK_LINES_INHERIT;
6515
+ const captureKinds = clearTrivia ? void 0 : opts.captureTriviaKinds;
6516
+ const ownCaptureMask = captureKinds === void 0 ? void 0 : scopeLabels !== void 0 ? triviaKindMask(scopeLabels, captureKinds) : void 0;
6517
+ const inheritCaptureMask = captureKinds !== void 0 && scopeLabels === void 0;
6518
+ const rootScalar = scalarOf(root);
6519
+ const scalarEligible = opts.trackLines !== true && opts.recover !== true && !opaqueRootCapture && !forceCaptureTrivia && captureKinds === void 0;
6520
+ const parseScalar = scalarEligible ? (input, pos, ctx) => {
6521
+ const savedTrivia = ctx.trivia;
6522
+ const savedLabels = ctx.triviaKindLabels;
6523
+ if (clearTrivia) {
6524
+ ctx.trivia = void 0;
6525
+ ctx.triviaKindLabels = void 0;
6526
+ } else if (scopeTrivia !== void 0) {
6527
+ ctx.trivia = scopeTrivia;
6528
+ ctx.triviaKindLabels = scopeLabels;
6529
+ }
6530
+ const end = rootScalar(input, pos, ctx);
6531
+ ctx.trivia = savedTrivia;
6532
+ ctx.triviaKindLabels = savedLabels;
6533
+ return end;
6534
+ } : void 0;
6535
+ let publicScalar;
6536
+ const grammar = {
6537
+ _tag: "grammar",
6538
+ _meta: {
6539
+ ...root._meta,
6540
+ // Trivia classification describes this grammar's own active trivia
6541
+ // scope. A directly nested grammar may have classified trivia of its
6542
+ // own, but that must not advertise classification for this wrapper.
6543
+ triviaKindLabels: void 0,
6544
+ rootTriviaClassified: void 0,
6545
+ ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
6546
+ ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
6547
+ },
6548
+ _def: {
6549
+ tag: "grammar",
6550
+ parser: root,
6551
+ triviaParser: scopeTrivia,
6552
+ clearTrivia,
6553
+ ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
6554
+ ...opts.captureTrivia ? { captureTrivia: true } : {},
6555
+ trackLines: opts.trackLines ?? false,
6556
+ constructionTrackLines: trackLinesPolicy,
6557
+ ...captureKinds === void 0 ? {} : { constructionCaptureTriviaKinds: captureKinds }
6558
+ },
6559
+ _parseScalar: parseScalar,
6560
+ parse(input, pos, _ctx) {
6561
+ if (pos === void 0 && _ctx === void 0) {
6562
+ publicScalar ??= scalarRootOf(grammar) ?? null;
6563
+ if (publicScalar) {
6564
+ const ctx2 = createParseContext();
6565
+ const end = publicScalar(input, 0, ctx2);
6566
+ if (end < 0) {
6567
+ const at = ~end;
6568
+ return { ok: false, expected: ctx2._fx ?? [], span: { start: at, end: at } };
6569
+ }
6570
+ return { ok: true, value: ctx2._sv, span: { start: 0, end } };
6571
+ }
6572
+ }
6573
+ if (refuseUnclassified) refuseUnclassifiedRootScope(_ctx?._rootTriviaStrictScopes);
6574
+ const trackLines = trackLinesOf(_ctx);
6575
+ const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
6576
+ const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
6577
+ const ctx = _ctx === void 0 ? createParseContext() : { ..._ctx };
6578
+ ctx.trackLines = trackLines;
6579
+ if (lineIndex) ctx._lineIndex = lineIndex;
6580
+ if (lineContext) ctx._lineScannedTo = lineContext.lineScannedTo;
6581
+ if (clearTrivia) {
6582
+ ctx.trivia = void 0;
6583
+ ctx.triviaKindLabels = void 0;
6584
+ } else if (scopeTrivia !== void 0) {
6585
+ ctx.trivia = scopeTrivia;
6586
+ if (scopeLabels) ctx.triviaKindLabels = scopeLabels;
6587
+ }
6588
+ if (forceCaptureTrivia || _ctx?.captureTrivia) ctx.captureTrivia = true;
6589
+ if (ownCaptureMask !== void 0) ctx._triviaCaptureMask = ownCaptureMask;
6590
+ else if (inheritCaptureMask) ctx._triviaCaptureMask = triviaKindMask(_ctx?.triviaKindLabels, captureKinds);
6591
+ if (opaqueRootCapture) ctx._rootTriviaCapture = false;
6592
+ const result = root.parse(input, pos ?? 0, ctx);
6593
+ if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
6594
+ _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
6595
+ }
6596
+ return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
6597
+ }
6598
+ };
6599
+ return grammar;
6600
+ }
6601
+ function noTrivia(root) {
6602
+ return parser({ trivia: null }, root);
6603
+ }
6604
+ var _analyzed = /* @__PURE__ */ new WeakSet();
6605
+ function parse(combinator, input, opts = {}) {
6606
+ if (!_analyzed.has(combinator)) {
6607
+ _analyzed.add(combinator);
6608
+ markUnusedValues(combinator);
6609
+ }
6610
+ if (!opts.trackLines && !opts.recover && combinator._def.tag === "grammar" && combinator._meta.grammarScanSkip === void 0) {
6611
+ return combinator.parse(input);
6612
+ }
6613
+ const trackLines = opts.trackLines ?? combinator._meta.grammarTrackLines ?? false;
6614
+ const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
6615
+ const lineIndex = lineContext?.lineIndex;
6616
+ const _errors = opts.recover ? [] : void 0;
6617
+ const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
6618
+ const grammarTrivia = combinator._meta.grammarTrivia;
6619
+ const grammarScanSkip = combinator._meta.grammarScanSkip;
6620
+ const ctx = createParseContext();
6621
+ ctx.trackLines = trackLines;
6622
+ if (lineIndex) ctx._lineIndex = lineIndex;
6623
+ if (lineContext) ctx._lineScannedTo = lineContext.lineScannedTo;
6624
+ if (grammarTrivia !== void 0) {
6625
+ ctx.trivia = grammarTrivia;
6626
+ if (grammarTrivia._meta.triviaKindLabels) ctx.triviaKindLabels = grammarTrivia._meta.triviaKindLabels;
6627
+ }
6628
+ if (grammarScanSkip !== void 0) ctx.scanSkip = grammarScanSkip;
6629
+ if (_errors !== void 0) ctx._errors = _errors;
6630
+ if (_probe !== void 0) ctx._probe = _probe;
6631
+ const result = combinator.parse(input, 0, ctx);
6632
+ if (!result.ok) return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
6633
+ const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
6634
+ return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
5723
6635
  }
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));
6636
+
6637
+ // src/combinators/attempt.ts
6638
+ function containOption(options) {
6639
+ if (options === void 0) return false;
6640
+ for (const key of Object.keys(options)) {
6641
+ if (key !== "contain") throw new TypeError(`attempt(): unknown option ${JSON.stringify(key)}`);
6642
+ }
6643
+ if (options.contain !== void 0 && typeof options.contain !== "boolean") {
6644
+ throw new TypeError("attempt(): `contain` must be a boolean");
6645
+ }
6646
+ return options.contain === true;
5731
6647
  }
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;
6648
+ function attempt(parser2, options) {
6649
+ const contain = containOption(options);
6650
+ const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
6651
+ let failExpected;
6652
+ return {
6653
+ _tag: "attempt",
6654
+ _meta: parser2._meta,
6655
+ _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
6656
+ parse(input, pos, ctx) {
6657
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
6658
+ if (failExpected === void 0) {
6659
+ const e = deriveExpected(parser2);
6660
+ failExpected = e.length > 0 ? e : [parser2._tag];
6661
+ }
6662
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
6663
+ }
6664
+ const mark = saveCstMark(ctx);
6665
+ const log = ctx._triviaLog?.length;
6666
+ const rootLog = ctx._rootTriviaLog?.length;
6667
+ const split = ctx._columnsSplit;
6668
+ const result = parser2.parse(input, pos, ctx);
6669
+ if (result.ok) return result;
6670
+ rollbackCstCapture(ctx, mark);
6671
+ ctx._columnsSplit = split;
6672
+ if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
6673
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
6674
+ if (result.committed && !contain) return result;
6675
+ return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
5738
6676
  }
5739
- const prev = annotation[i - 1];
5740
- if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
5741
- return true;
6677
+ };
6678
+ }
6679
+
6680
+ // src/table/factor-choice.ts
6681
+ var DERIVED = /* @__PURE__ */ new Set(["_tag", "_meta", "_def", "_parseScalar", "_stickyRegex", "parse"]);
6682
+ function isCombinator(v) {
6683
+ return typeof v === "object" && v !== null && "_def" in v && "_meta" in v;
6684
+ }
6685
+ function sameComb(a, b, depth) {
6686
+ if (a === b) return true;
6687
+ if (depth > 32 || a._tag !== b._tag) return false;
6688
+ for (const k of Object.keys(a)) if (!DERIVED.has(k)) return false;
6689
+ for (const k of Object.keys(b)) if (!DERIVED.has(k)) return false;
6690
+ return sameFields(a._def, b._def, depth, void 0);
6691
+ }
6692
+ function sameValue(u, v, depth) {
6693
+ if (u === v) return true;
6694
+ if (isCombinator(u) && isCombinator(v)) return sameComb(u, v, depth + 1);
6695
+ if (Array.isArray(u) && Array.isArray(v)) return u.length === v.length && u.every((x, i) => sameValue(x, v[i], depth));
6696
+ if (typeof u === "object" && u !== null && typeof v === "object" && v !== null && Object.getPrototypeOf(u) === Object.prototype && Object.getPrototypeOf(v) === Object.prototype) {
6697
+ return sameFields(u, v, depth, void 0);
5742
6698
  }
5743
6699
  return false;
5744
6700
  }
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";
6701
+ function sameFields(x, y, depth, skip) {
6702
+ const kx = Object.keys(x).filter((k) => k !== skip && x[k] !== void 0);
6703
+ const ky = Object.keys(y).filter((k) => k !== skip && y[k] !== void 0);
6704
+ if (kx.length !== ky.length) return false;
6705
+ for (const k of kx) {
6706
+ if (!(k in y)) return false;
6707
+ const u = x[k];
6708
+ const v = y[k];
6709
+ if (k === "fn" && typeof u === "function" && typeof v === "function" && u !== v) {
6710
+ if (typeof x.fnSrc !== "string" || x.fnSrc === "" || x.fnSrc !== y.fnSrc) return false;
6711
+ continue;
6712
+ }
6713
+ if (!sameValue(u, v, depth)) return false;
5768
6714
  }
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;
6715
+ return true;
5799
6716
  }
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;
6717
+ function rewrap(w, body) {
6718
+ const d = w._def;
6719
+ let out;
6720
+ if (d.tag === "leaf") {
6721
+ out = d.cstValue === "source" ? sourceLeaf(body, d.fn) : leaf(body, d.fn);
6722
+ if (d.fnSrc !== void 0) out._def.fnSrc = d.fnSrc;
6723
+ } else if (d.tag === "grammar") {
6724
+ out = d.clearTrivia === true ? noTrivia(body) : parser({ trivia: d.triviaParser ?? null }, body);
6725
+ } else return void 0;
6726
+ return sameFields(d, out._def, 0, "parser") ? out : void 0;
6727
+ }
6728
+ function unusedOf(seq) {
6729
+ return seq._def.tag === "sequence" && seq._def.valueUnused === true;
6730
+ }
6731
+ function never(c) {
6732
+ const t2 = c._def.tag;
6733
+ return t2 === "optional" || t2 === "many";
6734
+ }
6735
+ function factorSharedEnds(d) {
6736
+ const arms = d.parsers;
6737
+ if (arms.length < 2) return void 0;
6738
+ if (d.strategy.tag !== "firstMatch" && d.strategy.tag !== "sharedPrefix") return void 0;
6739
+ if (d.gates.some((g) => g !== null) || d.autoNot.some((a) => a !== null)) return void 0;
6740
+ const shapes = [];
6741
+ for (const arm of arms) {
6742
+ const wrappers = [];
6743
+ let c = arm;
6744
+ while ((c._def.tag === "leaf" || c._def.tag === "grammar") && wrappers.length < 4) {
6745
+ wrappers.push(c);
6746
+ c = c._def.parser;
6747
+ }
6748
+ if (c._def.tag !== "sequence" || c._def.parsers.length < 2) return void 0;
6749
+ shapes.push({ wrappers, terms: c._def.parsers, seq: c });
6750
+ }
6751
+ const first = shapes[0];
6752
+ const n = first.terms.length;
6753
+ for (const s of shapes) {
6754
+ if (s.wrappers.length !== first.wrappers.length || s.terms.length !== n) return void 0;
6755
+ if (unusedOf(s.seq) !== unusedOf(first.seq)) return void 0;
6756
+ for (let j = 0; j < s.wrappers.length; j++) {
6757
+ const a = first.wrappers[j]._def;
6758
+ const b = s.wrappers[j]._def;
6759
+ if (a.tag !== b.tag || !sameFields(a, b, 0, "parser")) return void 0;
6760
+ }
6761
+ }
6762
+ let at = -1;
6763
+ for (let i = 0; i < n; i++) {
6764
+ if (shapes.every((s) => sameComb(s.terms[i], first.terms[i], 0))) continue;
6765
+ if (at >= 0) return void 0;
6766
+ at = i;
6767
+ }
6768
+ if (at < 0) return void 0;
6769
+ for (let i = at + 1; i < n; i++) if (!never(first.terms[i])) return void 0;
6770
+ if (arms.some((a) => mayCommitFailure(a))) return void 0;
6771
+ const middle = choice(...shapes.map((s) => s.terms[at]));
6772
+ if (middle._def.tag === "choice" && middle._def.autoNot.some((a) => a !== null)) return void 0;
6773
+ const st = middle._def.tag === "choice" ? middle._def.strategy : void 0;
6774
+ if (st === void 0 || st.tag !== "firstMatch" && st.tag !== "sharedPrefix" && !(st.tag === "literalsLongestFirst" && st.sortedIndices.every((x, i) => x === i))) return void 0;
6775
+ let out = sequence(
6776
+ ...[...first.terms.slice(0, at), middle, ...first.terms.slice(at + 1)]
6777
+ );
6778
+ if (unusedOf(first.seq)) {
6779
+ out._def.valueUnused = true;
6780
+ }
6781
+ for (let j = first.wrappers.length - 1; j >= 0 && out !== void 0; j--) out = rewrap(first.wrappers[j], out);
6782
+ return out === void 0 ? void 0 : attempt(out);
5805
6783
  }
5806
6784
 
5807
6785
  // src/compiler/fields.ts
@@ -5918,6 +6896,7 @@ var OP_SCOPE_PLAIN = 41;
5918
6896
  var OP_LEX_BODY = 42;
5919
6897
  var OP_LEX_PROGRAM = 43;
5920
6898
  var OP_SOURCE_LEAF = 44;
6899
+ var OP_RGATE = 45;
5921
6900
  var OP_DISPATCH = 25;
5922
6901
  var OP_ROUTED = 26;
5923
6902
  var OP_NAMES = {
@@ -5937,6 +6916,7 @@ var OP_NAMES = {
5937
6916
  [OP_PEEK]: "PEEK",
5938
6917
  [OP_LEAF]: "LEAF",
5939
6918
  [OP_SOURCE_LEAF]: "SOURCE_LEAF",
6919
+ [OP_RGATE]: "RGATE",
5940
6920
  [OP_EMPTY]: "EMPTY",
5941
6921
  [OP_LIT_TRACK]: "LIT_TRACK",
5942
6922
  [OP_RX_TRACK]: "RX_TRACK",
@@ -5967,71 +6947,6 @@ var OP_NAMES = {
5967
6947
  [OP_LEX_PROGRAM]: "LEX_PROGRAM"
5968
6948
  };
5969
6949
 
5970
- // src/combinators/adjacency.ts
5971
- function adjacencyOf(p) {
5972
- return p._def.tag === "adjacency" ? p._def : null;
5973
- }
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);
5980
- }
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;
5989
- }
5990
- function adjacencyFail(pos, def) {
5991
- return { ok: false, expected: [adjacencyExpected(def)], span: { start: pos, end: pos } };
5992
- }
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}`);
5996
- }
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
6005
- };
6006
- return {
6007
- _tag: "adjacency",
6008
- _meta: meta,
6009
- _def: def,
6010
- parse() {
6011
- throw adjacencyMisuse(def.polarity);
6012
- }
6013
- };
6014
- }
6015
- function adjacent() {
6016
- return adjacencyCombinator({ tag: "adjacency", polarity: "adjacent" });
6017
- }
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
- }
6028
- }
6029
- }
6030
- return adjacencyCombinator(
6031
- kinds === void 0 ? { tag: "adjacency", polarity: "notAdjacent" } : { tag: "adjacency", polarity: "notAdjacent", kinds: [...kinds] }
6032
- );
6033
- }
6034
-
6035
6950
  // src/combinators/node.ts
6036
6951
  function unwrapChild(child) {
6037
6952
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
@@ -6148,42 +7063,6 @@ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
6148
7063
  };
6149
7064
  }
6150
7065
 
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;
6184
- }
6185
- }
6186
-
6187
7066
  // src/compiler/direct-projection.ts
6188
7067
  function directArrayProjection(fn, fnSrc, arity) {
6189
7068
  let source = fnSrc;
@@ -6404,13 +7283,16 @@ var Encoder = class {
6404
7283
  }
6405
7284
  case "choice": {
6406
7285
  for (const arm of node2.arms) if (!emitNode(arm)) return false;
6407
- const op = node2.arms.length === 2 ? LEX_NODE_CHOICE2 : node2.arms.length === 4 ? LEX_NODE_CHOICE4 : node2.arms.length === 8 ? LEX_NODE_CHOICE8 : -1;
6408
- if (op < 0) return false;
6409
- words.push(op, this.expected(node2.expected));
7286
+ const final = classifyFinalChoice(
7287
+ node2.parsers,
7288
+ this.refResolver(),
7289
+ node2.cleared ? void 0 : this.activeTrivia
7290
+ );
7291
+ const classes = final.firstSets.map((first, i) => final.nullable[i] ? -1 : this.charClass(first));
7292
+ const exclusive = final.exclusive && classes.every((cls) => cls >= 0);
7293
+ words.push(LEX_NODE_CHOICE, node2.arms.length, exclusive ? 1 : 0, this.expected(node2.expected));
6410
7294
  for (let i = 0; i < node2.arms.length; i++) {
6411
- const first = node2.firstSets[i];
6412
- words.push(first.kind === "ranges" ? this.charClass(first) : -1);
6413
- words.push(this.expected(node2.armExpected[i]));
7295
+ words.push(classes[i], this.expected(node2.armExpected[i]));
6414
7296
  }
6415
7297
  return true;
6416
7298
  }
@@ -6422,6 +7304,10 @@ var Encoder = class {
6422
7304
  if (!emitNode(node2.body)) return false;
6423
7305
  words.push(LEX_NODE_OPTIONAL);
6424
7306
  return true;
7307
+ case "repeat":
7308
+ if (!emitNode(node2.body)) return false;
7309
+ words.push(LEX_NODE_REPEAT, node2.min, node2.max);
7310
+ return true;
6425
7311
  }
6426
7312
  };
6427
7313
  if (!emitNode(body.root)) return void 0;
@@ -6927,6 +7813,51 @@ var Encoder = class {
6927
7813
  if (!hasOwnTriviaBoundary(target)) return ip;
6928
7814
  return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(amb), ip);
6929
7815
  }
7816
+ /**
7817
+ * The `OP_RGATE` operands of a choice arm that opens with `routed()`, or
7818
+ * `undefined`. Through `node()`/scope wrappers and first sequence terms, the
7819
+ * arm's next term after the routed token is found; the gate is that term's
7820
+ * first characters, with the trivia in force at the boundary before it, so
7821
+ * the arm is entered only where it can get past the token. The term must be
7822
+ * unable to match empty or to commit, so a miss is exactly that term failing
7823
+ * at the token's end with its own set.
7824
+ */
7825
+ routedArmGate(arm) {
7826
+ const walk = (p, trivia2, depth) => {
7827
+ if (depth > 16) return void 0;
7828
+ const d = p._def;
7829
+ switch (d.tag) {
7830
+ case "routed":
7831
+ return "end";
7832
+ case "node":
7833
+ return walk(d.parser, trivia2, depth + 1);
7834
+ case "grammar":
7835
+ return walk(d.parser, d.clearTrivia === true ? void 0 : d.triviaParser ?? trivia2, depth + 1);
7836
+ case "sequence": {
7837
+ const head = d.parsers[0];
7838
+ if (head === void 0) return void 0;
7839
+ const r = walk(head, trivia2, depth + 1);
7840
+ if (r !== "end") return r;
7841
+ const next2 = d.parsers[1];
7842
+ return next2 === void 0 ? "end" : { next: next2, boundary: trivia2 };
7843
+ }
7844
+ default:
7845
+ return void 0;
7846
+ }
7847
+ };
7848
+ const follow = walk(arm, this.activeTrivia, 0);
7849
+ if (follow === void 0 || follow === "end") return void 0;
7850
+ const rr = this.refResolver();
7851
+ const { next, boundary } = follow;
7852
+ if (matchesEmpty(next, /* @__PURE__ */ new Set(), rr) || mayCommitFailure(next, rr, this.commitFailureMemo)) return void 0;
7853
+ const nextDef = next._def;
7854
+ if (nextDef.tag !== "literal" && nextDef.tag !== "regex" && nextDef.tag !== "keywords" && nextDef.tag !== "label") return void 0;
7855
+ const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary);
7856
+ const cls = this.charClass(boundary === void 0 ? first : union(first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr)));
7857
+ if (cls < 0) return void 0;
7858
+ const miss = nextDef.tag === "label" ? deriveExpected(next) : directTerminalFailureExpected(nextDef);
7859
+ return { cls, fx: this.expected(miss) };
7860
+ }
6930
7861
  node(p) {
6931
7862
  const lazyRef = p._def.tag === "lazy";
6932
7863
  const scopeKey = lazyRef ? this.activeTrivia : void 0;
@@ -7051,6 +7982,10 @@ var Encoder = class {
7051
7982
  return head;
7052
7983
  }
7053
7984
  case "choice": {
7985
+ if (this.plan === void 0) {
7986
+ const factored = factorSharedEnds(d);
7987
+ if (factored !== void 0) return this.node(factored).ip;
7988
+ }
7054
7989
  if (d.strategy.tag === "greedyClassify") {
7055
7990
  const superIndex = d.strategy.superIndex;
7056
7991
  const sup = this.node(d.parsers[superIndex]).ip;
@@ -7074,9 +8009,12 @@ var Encoder = class {
7074
8009
  const kids = arms.map((c, k) => {
7075
8010
  const src = order[k];
7076
8011
  const checks = d.autoNot[src];
7077
- const inner = this.node(c).ip;
7078
- const ip = checks === null || checks === void 0 || checks.length === 0 ? inner : this.reject(inner, checks);
7079
8012
  const gate = d.gates[src];
8013
+ const plain2 = (checks === null || checks === void 0 || checks.length === 0) && (gate === null || gate === void 0);
8014
+ const routedGate = plain2 ? this.routedArmGate(c) : void 0;
8015
+ const body = this.node(c).ip;
8016
+ const inner = routedGate === void 0 ? body : this.emit(OP_RGATE, routedGate.cls, body, routedGate.fx);
8017
+ const ip = checks === null || checks === void 0 || checks.length === 0 ? inner : this.reject(inner, checks);
7080
8018
  const gated = gate === null || gate === void 0 ? ip : this.emit(OP_ARMGATE, this.fn(gate, d.gateSrcs?.[src] ?? null), ip, this.expected(deriveExpected(c)));
7081
8019
  return this.covWrap(gated, armCovIds?.[src], 1);
7082
8020
  });
@@ -7400,6 +8338,7 @@ var Encoder = class {
7400
8338
  const slots = (ip) => {
7401
8339
  switch (this.code[ip]) {
7402
8340
  case OP_GATE:
8341
+ case OP_RGATE:
7403
8342
  return [ip + 2];
7404
8343
  case OP_RULE:
7405
8344
  case OP_OPT:
@@ -8627,6 +9566,7 @@ function childSlots(code, ip, out) {
8627
9566
  case OP_LEAF:
8628
9567
  case OP_SOURCE_LEAF:
8629
9568
  case OP_ARMGATE:
9569
+ case OP_RGATE:
8630
9570
  out.push(code[ip + 2]);
8631
9571
  return true;
8632
9572
  // `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
@@ -8710,6 +9650,42 @@ function reachableSites(code, roots) {
8710
9650
  }
8711
9651
  return seen;
8712
9652
  }
9653
+ function passCollapse(code, ip, hostCst) {
9654
+ const flags = code[ip + 3];
9655
+ if (hostCst || code[ip] !== OP_NODE || code[ip + 1] < 0 || code[ip + 4] >= 0) return false;
9656
+ if (flags !== 32 && flags !== 34) return false;
9657
+ let at = code[ip + 2];
9658
+ for (let n = 0; n < 8; n++) {
9659
+ const op = code[at];
9660
+ if (op === OP_GATE || op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[at + 3] === 0) at = code[at + 2];
9661
+ else if (op === OP_RULE) at = code[at + 1];
9662
+ else break;
9663
+ }
9664
+ return code[at] === OP_NODE;
9665
+ }
9666
+ function gatedPassCollapse(code, ip, hostCst) {
9667
+ const flags = code[ip + 3];
9668
+ if (hostCst || code[ip] !== OP_NODE || code[ip + 1] < 0 || code[ip + 4] >= 0) return void 0;
9669
+ if (flags !== 32 && flags !== 34) return void 0;
9670
+ const seq = code[ip + 2];
9671
+ if (code[seq] !== OP_SEQ && code[seq] !== OP_SEQV) return void 0;
9672
+ const n = code[seq + 1];
9673
+ if (n < 2) return void 0;
9674
+ const leads = [];
9675
+ const lits = [];
9676
+ for (let i = 0; i < n - 1; i++) {
9677
+ const t2 = code[seq + 2 + i];
9678
+ if (code[t2] !== OP_OPT && code[t2] !== OP_NOT) return void 0;
9679
+ const g = code[t2 + 1];
9680
+ if (code[g] === OP_GATE) leads.push(g);
9681
+ else if (code[g] === OP_LIT) lits.push(code[g + 1]);
9682
+ else return void 0;
9683
+ }
9684
+ const last = code[seq + 2 + n - 1];
9685
+ let at = last;
9686
+ for (let k = 0; k < 8 && (code[at] === OP_GATE || code[at] === OP_RULE); k++) at = code[at] === OP_RULE ? code[at + 1] : code[at + 2];
9687
+ return code[at] === OP_NODE ? { leads, lits, last } : void 0;
9688
+ }
8713
9689
  function transfer(code, ip, at, hostCst) {
8714
9690
  const op = code[ip];
8715
9691
  if (op === OP_SCOPE || op === OP_SCOPE_CAP || op === OP_SCOPE_PLAIN) {
@@ -8721,14 +9697,18 @@ function transfer(code, ip, at, hostCst) {
8721
9697
  }
8722
9698
  if (op === OP_NODE || op === OP_NODE_TRACK) {
8723
9699
  const flags = code[ip + 3];
9700
+ if (passCollapse(code, ip, hostCst)) return at;
9701
+ const gated = gatedPassCollapse(code, ip, hostCst) !== void 0;
8724
9702
  const directChildren = !hostCst && op === OP_NODE && code[ip + 1] >= 0 && code[ip + 4] < 0 && ((flags & ~128) === 2 || flags === 18 || flags === 34);
8725
9703
  if (directChildren) {
8726
- return { tri: at.tri, buf: false, raw: RAW_UNKNOWN, cap: CAP_OFF };
9704
+ const framed2 = { tri: at.tri, buf: false, raw: RAW_UNKNOWN, cap: CAP_OFF };
9705
+ return gated ? meet(at, framed2) : framed2;
8727
9706
  }
8728
9707
  const cap = (flags & 4) !== 0 || hostCst ? CAP_ON : CAP_OFF;
8729
9708
  const raw = !hostCst && code[ip + 1] >= 0 && code[ip + 4] < 0 && (flags & 2) !== 0 ? RAW_OMIT : RAW_CAPTURE;
8730
9709
  if (at.buf && raw === at.raw && cap === at.cap) return at;
8731
- return { tri: at.tri, buf: true, raw, cap };
9710
+ const framed = { tri: at.tri, buf: true, raw, cap };
9711
+ return gated ? meet(at, framed) : framed;
8732
9712
  }
8733
9713
  if (op === OP_TOKEN) {
8734
9714
  if (at.tri === TRI_NONE && !at.buf) return at;
@@ -8766,6 +9746,40 @@ function computeSiteLabels(code, roots, hostCst) {
8766
9746
  }
8767
9747
  return { at: (ip) => labels.get(ip) ?? TOP };
8768
9748
  }
9749
+ function scopeIsIdentity(code, ip, at) {
9750
+ const op = code[ip];
9751
+ if (op !== OP_SCOPE_PLAIN && !(op === OP_SCOPE && code[ip + 3] === 0)) return false;
9752
+ const ki = code[ip + 1];
9753
+ return at.tri === (ki < 0 ? TRI_NONE : ki);
9754
+ }
9755
+ function scanHandoffNeeded(code, ip, labels) {
9756
+ const seen = /* @__PURE__ */ new Set();
9757
+ const stack = [ip];
9758
+ const kids = [];
9759
+ while (stack.length > 0) {
9760
+ const at = stack.pop();
9761
+ if (seen.has(at)) continue;
9762
+ seen.add(at);
9763
+ const l = labels.at(at);
9764
+ if (l.tri === TRI_UNKNOWN) return true;
9765
+ const op = code[at];
9766
+ if (op === OP_SCAN) return true;
9767
+ if (op === OP_TOKEN || op === OP_SCOPE_CAP) continue;
9768
+ if ((op === OP_SCOPE_PLAIN || op === OP_SCOPE) && !(code[at + 1] >= 0 && l.tri === code[at + 1] && (op === OP_SCOPE_PLAIN || code[at + 3] === 0))) continue;
9769
+ kids.length = 0;
9770
+ if (!childSlots(code, at, kids)) return true;
9771
+ for (const c of kids) stack.push(c);
9772
+ }
9773
+ return false;
9774
+ }
9775
+ function triviaLeadRanges(tri, trivia2) {
9776
+ if (tri === TRI_NONE) return [];
9777
+ if (tri < 0) return void 0;
9778
+ const fs = trivia2[tri]?._meta.firstSet;
9779
+ if (fs === void 0) return void 0;
9780
+ if (fs.kind === "empty") return [];
9781
+ return fs.kind === "ranges" && fs.ranges !== void 0 ? fs.ranges : void 0;
9782
+ }
8769
9783
 
8770
9784
  // src/table/scalar-terminal.ts
8771
9785
  function makeScalarRecognizer(op, spec) {
@@ -8803,6 +9817,12 @@ function scalarTerminalNotChild(code, ip) {
8803
9817
  const op = code[child];
8804
9818
  return op === OP_RX || op === OP_LIT ? child : -1;
8805
9819
  }
9820
+ function scalarTerminalPeekChild(code, ip) {
9821
+ if (code[ip] !== OP_PEEK) return -1;
9822
+ const child = code[ip + 1];
9823
+ const op = code[child];
9824
+ return op === OP_RX || op === OP_LIT ? child : -1;
9825
+ }
8806
9826
  function leadingScalarTerminal(code, ip, minDepth = 2, throughAttempt = true, throughPeek = false) {
8807
9827
  const seen = /* @__PURE__ */ new Set();
8808
9828
  let at = ip;
@@ -8980,6 +10000,59 @@ function leadingTriviaFieldAt(code, k, ip) {
8980
10000
  return code[ip] === OP_FIELD && (fieldSpec(k[code[ip + 1]]).bits & FIELD_LEADING_TRIVIA) !== 0;
8981
10001
  }
8982
10002
 
10003
+ // src/table/hold-gap.ts
10004
+ var WRAPPERS = /* @__PURE__ */ new Set([
10005
+ OP_NODE,
10006
+ OP_XFORM,
10007
+ OP_LEAF,
10008
+ OP_SOURCE_LEAF,
10009
+ OP_TOKEN,
10010
+ OP_SCOPE,
10011
+ OP_SCOPE_CAP,
10012
+ OP_SCOPE_PLAIN,
10013
+ OP_FIELD,
10014
+ OP_LABEL,
10015
+ OP_EXPECT,
10016
+ OP_WITHCTX,
10017
+ OP_RULE,
10018
+ OP_ATTEMPT,
10019
+ OP_COV
10020
+ ]);
10021
+ function rowNullable(code, k, ip, memo) {
10022
+ const hit = memo.get(ip);
10023
+ if (hit !== void 0) return hit;
10024
+ memo.set(ip, true);
10025
+ const op = code[ip];
10026
+ const kids = [];
10027
+ let out = true;
10028
+ if (op === OP_GATE) out = false;
10029
+ else if (op === OP_LIT || op === OP_LIT_CI) out = k[code[ip + 1]].length === 0;
10030
+ else if (op === OP_RX) {
10031
+ const re = k[code[ip + 1]];
10032
+ out = !(re instanceof RegExp) || regexCanMatchEmpty(re.source);
10033
+ } else if (op === OP_SEQ || op === OP_SEQV || op === OP_SEQX) {
10034
+ childSlots(code, ip, kids);
10035
+ out = kids.every((c) => rowNullable(code, k, c, memo));
10036
+ } else if (op === OP_CHOICE) {
10037
+ childSlots(code, ip, kids);
10038
+ out = kids.length === 0 || kids.some((c) => rowNullable(code, k, c, memo));
10039
+ } else if (op === OP_REP || op === OP_REPV) {
10040
+ out = code[ip + 2] === 0 || rowNullable(code, k, code[ip + 1], memo);
10041
+ } else if (WRAPPERS.has(op)) {
10042
+ if (childSlots(code, ip, kids) && kids.length === 1) out = rowNullable(code, k, kids[0], memo);
10043
+ }
10044
+ memo.set(ip, out);
10045
+ return out;
10046
+ }
10047
+ function holdsGap(code, k, base, n, i, memo) {
10048
+ if (i < 1 || i + 1 >= n) return false;
10049
+ const term = code[base + i];
10050
+ if (code[term] !== OP_NOT && code[term] !== OP_PEEK) return false;
10051
+ const next = code[base + i + 1];
10052
+ if (code[next] === OP_ADJ || leadingTriviaFieldAt(code, k, next)) return false;
10053
+ return !rowNullable(code, k, next, memo);
10054
+ }
10055
+
8983
10056
  // src/table/emit-assembly.ts
8984
10057
  var Unemittable = class extends Error {
8985
10058
  construct;
@@ -9060,7 +10133,10 @@ var EMITTED_PARAMS = [
9060
10133
  "triviaFieldCapture",
9061
10134
  // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
9062
10135
  // factories ignore it.
9063
- "skipDeferredTrivia"
10136
+ "skipDeferredTrivia",
10137
+ // Appended for the pass-through collapse (`run-support.ts`); old factories
10138
+ // ignore it.
10139
+ "collapseEmpty"
9064
10140
  ];
9065
10141
  var RUNTIME_PRELUDE = `
9066
10142
  let _pfScan=null
@@ -9257,6 +10333,24 @@ ${fast}
9257
10333
  ${scanned}
9258
10334
  }`;
9259
10335
  }
10336
+ function emitHoldTerm(callee, dst, t2, l, skip) {
10337
+ const fast = `const ${t2}v=${callee}(input,cur,ctx)
10338
+ if(${t2}v===FAIL)return FAIL
10339
+ ${dst}=${t2}v
10340
+ cur=EC.e`;
10341
+ if (l.tri === TRI_NONE) return fast;
10342
+ const held = `const ${t2}s=${skip}(input,cur,ctx)
10343
+ const ${t2}v=${callee}(input,${t2}s,ctx)
10344
+ if(${t2}v===FAIL)return FAIL
10345
+ ${dst}=${t2}v
10346
+ cur=${t2}s`;
10347
+ if (l.tri !== TRI_UNKNOWN) return held;
10348
+ return `if(ctx.trivia===undefined){
10349
+ ${fast}
10350
+ }else{
10351
+ ${held}
10352
+ }`;
10353
+ }
9260
10354
  function leadSkip(hasTrivia, leadTrivia, skip) {
9261
10355
  if (hasTrivia === "false") return "{}";
9262
10356
  const call = `itemStart=${skip}(input,itemStart,ctx)`;
@@ -9298,6 +10392,7 @@ function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false, s
9298
10392
  const reachable = [...reachableSites(code, roots)];
9299
10393
  const labels = computeSiteLabels(code, roots, hostCst);
9300
10394
  const scalarSpecs = /* @__PURE__ */ new Set();
10395
+ const nullable2 = /* @__PURE__ */ new Map();
9301
10396
  const largeChoiceScalarSpecs = /* @__PURE__ */ new Set();
9302
10397
  const largeChoiceLiteralFamilies = /* @__PURE__ */ new Map();
9303
10398
  const largeChoiceScalarSequences = /* @__PURE__ */ new Map();
@@ -9622,9 +10717,7 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
9622
10717
  const op = code[ip];
9623
10718
  if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2];
9624
10719
  if (op === OP_RULE) return code[ip + 1];
9625
- if ((op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[ip + 3] === 0) && code[ip + 1] >= 0 && labels.at(ip).tri === code[ip + 1]) {
9626
- return code[ip + 2];
9627
- }
10720
+ if (scopeIsIdentity(code, ip, labels.at(ip))) return code[ip + 2];
9628
10721
  return void 0;
9629
10722
  }
9630
10723
  function lower(ip, fname) {
@@ -9746,6 +10839,22 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
9746
10839
  return `${head}
9747
10840
  if(${gateMissTest(ip)}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
9748
10841
  return ${child}(input,pos,ctx)
10842
+ }`;
10843
+ }
10844
+ case OP_RGATE: {
10845
+ const child = link(code[ip + 2]);
10846
+ if (cfg.tolerant || cfg.probe) return `${head}
10847
+ return ${child}(input,pos,ctx)
10848
+ }`;
10849
+ const resolved = t2.cc[code[ip + 1]];
10850
+ const ci = classes.push([resolved]) - 1;
10851
+ classPlan.push([code[ip + 1]]);
10852
+ const cls = hoist("cl", `CLS[${ci}][0]`);
10853
+ const xf = fxRef(code[ip + 3]);
10854
+ return `${head}
10855
+ const it=ctx._routed
10856
+ if(it!==undefined&&it.span.start===pos&&!classHas(${cls},lead(input,it.span.end))){ctx._fe=it.span.end;ctx._fx=${xf};return FAIL}
10857
+ return ${child}(input,pos,ctx)
9749
10858
  }`;
9750
10859
  }
9751
10860
  case OP_ARMGATE: {
@@ -10020,23 +11129,33 @@ return FAIL
10020
11129
  const p = tmp();
10021
11130
  return `${head}
10022
11131
  ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${emitMark(p, L.buf, L.raw, sinks)}
10023
- const v=${child}(input,pos,ctx)
11132
+ ${cfg.probe ? "const pb=ctx._probe?.best\n" : ""}const v=${child}(input,pos,ctx)
10024
11133
  ${emitRollback(p, L.buf, L.raw, sinks)}
10025
- if(v===FAIL){EC.e=pos;return null}
11134
+ ${cfg.probe ? "if(ctx._probe!==undefined)ctx._probe.best=pb??null\n" : ""}if(v===FAIL){EC.e=pos;return null}
10026
11135
  ctx._fe=pos
10027
11136
  ctx._fx=${xf}
10028
11137
  return FAIL
10029
11138
  }`;
10030
11139
  }
10031
11140
  case OP_PEEK: {
11141
+ const scalarChild = scalarTerminalPeekChild(code, ip);
11142
+ if (scalarChild >= 0) {
11143
+ const recognize = recognizerRef(code[scalarChild + 1]);
11144
+ const xf2 = fxRef(code[ip + 2]);
11145
+ return `${head}
11146
+ if(${recognize}(input,pos)<0){ctx._fe=pos;ctx._fx=${xf2};return FAIL}
11147
+ EC.e=pos
11148
+ return null
11149
+ }`;
11150
+ }
10032
11151
  const child = link(code[ip + 1]);
10033
11152
  const xf = fxRef(code[ip + 2]);
10034
11153
  const p = tmp();
10035
11154
  return `${head}
10036
11155
  ${lookaheadMiss(code[ip + 1], `ctx._fe=pos;ctx._fx=${xf};return FAIL`)}${emitMark(p, L.buf, L.raw, sinks)}
10037
- const v=${child}(input,pos,ctx)
11156
+ ${cfg.probe ? "const pb=ctx._probe?.best\n" : ""}const v=${child}(input,pos,ctx)
10038
11157
  ${emitRollback(p, L.buf, L.raw, sinks)}
10039
- if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
11158
+ ${cfg.probe ? "if(ctx._probe!==undefined)ctx._probe.best=pb??null\n" : ""}if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
10040
11159
  EC.e=pos
10041
11160
  return null
10042
11161
  }`;
@@ -10107,7 +11226,7 @@ return v
10107
11226
  );
10108
11227
  } else {
10109
11228
  const callee = leadingTriviaFieldAt(code, k, childIp) ? `(LEAD.at=cur,${kids[i]})` : kids[i];
10110
- parts.push(emitTerm(callee, vn, tmp(), L, skipFor(L)));
11229
+ parts.push(holdsGap(code, k, base, n, i, nullable2) ? emitHoldTerm(callee, vn, tmp(), L, skipFor(L)) : emitTerm(callee, vn, tmp(), L, skipFor(L)));
10111
11230
  }
10112
11231
  names.push(vn);
10113
11232
  }
@@ -10590,6 +11709,26 @@ return nd
10590
11709
  const trailingTrivia = (flags & 128) !== 0;
10591
11710
  const build = buildIdx >= 0 ? fnRef(buildIdx) : void 0;
10592
11711
  const leanFlags = flags & ~128;
11712
+ if (build !== void 0 && passCollapse(code, ip, hostCst)) {
11713
+ return `${head}
11714
+ const v=${child}(input,pos,ctx)
11715
+ if(v!==undefined)return v
11716
+ return collapseEmpty(ctx,${build},input,pos,EC.e,${flags === 32})
11717
+ }`;
11718
+ }
11719
+ const gated = build !== void 0 && !cfg.tolerant && !cfg.probe ? gatedPassCollapse(code, ip, hostCst) : void 0;
11720
+ const triviaLead = gated === void 0 ? void 0 : triviaLeadRanges(labels.at(code[ip + 2]).tri, t2.trivia);
11721
+ const gp = gated !== void 0 && triviaLead !== void 0 && triviaLead.length <= 16 ? `const gc=lead(input,pos)
11722
+ if(!(${[
11723
+ ...gated.leads.map((g) => `!(${gateMissTest(g)})`),
11724
+ ...gated.lits.map((i) => `gc===${k[i].codePointAt(0)}`),
11725
+ ...triviaLead.map((r) => r.lo === r.hi ? `gc===${r.lo}` : `(gc>=${r.lo}&&gc<=${r.hi})`)
11726
+ ].join("||") || "false"})){
11727
+ const gv=${link(gated.last)}(input,pos,ctx)
11728
+ if(gv!==undefined)return gv
11729
+ return collapseEmpty(ctx,${build},input,pos,EC.e,${flags === 32})
11730
+ }
11731
+ ` : "";
10593
11732
  if (!hostCst && !tracked && build !== void 0 && proj < 0 && (leanFlags === 2 || (flags === 18 || flags === 34))) {
10594
11733
  const fields = flags === 18;
10595
11734
  const collapseChildren = flags === 34;
@@ -10624,7 +11763,7 @@ sCh.push(nd)
10624
11763
  if(sRaw!==undefined)sRaw.push(rawEntry(nd,input,pos,end))
10625
11764
  }`;
10626
11765
  return `${head}
10627
- const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
11766
+ ${gp}const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
10628
11767
  const sCap=ctx.captureTrivia,sBuf=ctx._cstBuf,sFields=ctx._fields
10629
11768
  const flat=[]
10630
11769
  ctx._cstBuf=undefined
@@ -10699,7 +11838,7 @@ const hostKids=kids
10699
11838
  const rawKids=buf.raw??(buf.rawSingle!==undefined?[buf.rawSingle]:EMPTY_CH)
10700
11839
  const tlog=buf.tl??EMPTY_TLOG`;
10701
11840
  return `${head}
10702
- const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
11841
+ ${!hostCst && !tracked ? gp : ""}const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
10703
11842
  const sCap=ctx.captureTrivia,sBuf=ctx._cstBuf
10704
11843
  ${openCapture}
10705
11844
  ctx.captureTrivia=${captureWide}
@@ -10769,9 +11908,15 @@ ext:[${externals.join(",")}],
10769
11908
  bind:function(xs,ps){_xScan=xs
10770
11909
  ${externals.map((ip, i) => `_xp${ip}=ps[${i}]`).join("\n")}},
10771
11910
  scan:function(s){const o=_pfScan;_pfScan=s;return o}`;
11911
+ const scannerFor = (ip) => {
11912
+ const ki = labels.at(ip).tri;
11913
+ if (ki === TRI_UNKNOWN) return "_pfScan";
11914
+ if (ki === TRI_NONE) return "null";
11915
+ return swapLegal && !triviaLabelled[ki] && t2.triviaScan[ki] != null ? hoist("ts", `TRIVIASCAN[${ki}]`) : "null";
11916
+ };
10772
11917
  const externalDefs = select === void 0 ? "" : `let _xScan
10773
11918
  ${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")}
11919
+ function _px${ip}(input,pos,ctx){const o=_xScan(${scannerFor(ip)});const v=_xp${ip}(input,pos,ctx);_xScan(o);return v}`).join("\n")}
10775
11920
  `;
10776
11921
  const source = `${RUNTIME_PRELUDE}${needsNoRawPrelude ? NO_RAW_RUNTIME_PRELUDE : ""}
10777
11922
  ${prelude.join("\n")}
@@ -11023,198 +12168,68 @@ function literal(value, opts = {}) {
11023
12168
  const canMatchNewline = meta.canMatchNewline;
11024
12169
  const parseScalar = !caseInsensitive && value.length === 1 ? (() => {
11025
12170
  const code = value.charCodeAt(0);
11026
- return (input, pos, ctx) => {
11027
- if (input.charCodeAt(pos) === code) {
11028
- ctx._sv = value;
11029
- return pos + 1;
11030
- }
11031
- ctx._fx = expected;
11032
- return ~pos;
11033
- };
11034
- })() : (input, pos, ctx) => {
11035
- const end = pos + value.length;
11036
- if (end <= input.length) {
11037
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
11038
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
11039
- ctx._sv = matchedValue;
11040
- return end;
11041
- }
11042
- }
11043
- ctx._fx = expected;
11044
- return ~pos;
11045
- };
11046
- if (caseInsensitive) {
11047
- const upper = value.toUpperCase();
11048
- const lower = value.toLowerCase();
11049
- const firstUpper = upper.codePointAt(0);
11050
- const firstLower = lower.codePointAt(0);
11051
- meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
11052
- { lo: firstLower, hi: firstLower },
11053
- { lo: firstUpper, hi: firstUpper }
11054
- ] } : firstSet2;
11055
- }
11056
- const expected = directTerminalFailureExpected({ tag: "literal", value, caseInsensitive });
11057
- const parse2 = !caseInsensitive && value.length === 1 ? (() => {
11058
- const code = value.charCodeAt(0);
11059
- return function parse3(input, pos, ctx) {
11060
- if (input.charCodeAt(pos) === code) {
11061
- const span = { start: pos, end: pos + 1 };
11062
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
11063
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
11064
- return { ok: true, value, span };
11065
- }
11066
- return failAt(ctx, expected, pos);
11067
- };
11068
- })() : function parse3(input, pos, ctx) {
11069
- const end = pos + value.length;
11070
- if (end > input.length) {
11071
- return failAt(ctx, expected, pos);
11072
- }
11073
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
11074
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
11075
- const span = { start: pos, end };
11076
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
11077
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
11078
- return { ok: true, value: matchedValue, span };
11079
- }
11080
- return failAt(ctx, expected, pos);
11081
- };
11082
- return {
11083
- _tag: "literal",
11084
- _meta: meta,
11085
- _def: { tag: "literal", value, caseInsensitive },
11086
- _parseScalar: parseScalar,
11087
- parse: parse2
11088
- };
11089
- }
11090
-
11091
- // src/combinators/sequence.ts
11092
- function sequence(...parsers) {
11093
- let adjacency = null;
11094
- for (let i = 0; i < parsers.length; i++) {
11095
- const a = adjacencyOf(parsers[i]);
11096
- if (a === null) continue;
11097
- if (i === 0) {
11098
- throw new TypeError(
11099
- `sequence(): ${a.polarity}() cannot be the FIRST term \u2014 an adjacency assertion tests the gap after the PRECEDING term. Put a concrete term first.`
11100
- );
11101
- }
11102
- ;
11103
- (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
11104
- }
11105
- const hasAdjacency = adjacency !== null;
11106
- let leading = null;
11107
- for (let i = 1; i < parsers.length; i++) {
11108
- if (leadsTrivia(parsers[i])) (leading ??= Array.from({ length: parsers.length }, () => false))[i] = true;
11109
- }
11110
- const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
11111
- const meta = {
11112
- // Union through the nullable prefix — a leading optional()/many() lets a later
11113
- // term's first char start the sequence. Just `parsers[0]` under-approximates.
11114
- firstSet: sequenceFirstSet(parsers),
11115
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
11116
- isTrivia: false
11117
- };
11118
- const def = { tag: "sequence", parsers };
11119
- const scalarParsers = parsers.map(scalarOf);
11120
- const parseScalar = (input, pos, ctx) => {
11121
- let cur = pos;
11122
- if (strictArity !== 0) {
11123
- cur = scalarParsers[0](input, cur, ctx);
11124
- if (cur < 0) return cur;
11125
- const first = ctx._sv;
11126
- if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
11127
- cur = scalarParsers[1](input, cur, ctx);
11128
- if (cur < 0) return cur;
11129
- const second = ctx._sv;
11130
- if (strictArity === 2) {
11131
- ctx._sv = def.valueUnused ? void 0 : [first, second];
11132
- return cur;
11133
- }
11134
- if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
11135
- cur = scalarParsers[2](input, cur, ctx);
11136
- if (cur < 0) return cur;
11137
- ctx._sv = def.valueUnused ? void 0 : [first, second, ctx._sv];
11138
- return cur;
11139
- }
11140
- const values = def.valueUnused ? void 0 : [];
11141
- for (let i = 0; i < scalarParsers.length; i++) {
11142
- let termPos = cur;
11143
- if (i > 0 && ctx.trivia) termPos = advanceTrivia(input, cur, ctx);
11144
- const end = scalarParsers[i](input, termPos, ctx);
11145
- if (end < 0) return end;
11146
- if (values !== void 0) values.push(ctx._sv);
11147
- if (end > termPos || termPos === cur) cur = end;
11148
- }
11149
- ctx._sv = values;
11150
- return cur;
11151
- };
11152
- let followSentinels;
11153
- return {
11154
- _tag: "sequence",
11155
- _meta: meta,
11156
- _def: def,
11157
- _parseScalar: hasAdjacency || leading !== null ? void 0 : parseScalar,
11158
- parse(input, pos, ctx) {
11159
- const tolerant = ctx._tolerant === true;
11160
- if (tolerant) {
11161
- followSentinels ??= parsers.map((_, i) => firstSetSentinel(
11162
- parsers.slice(i + 1).reduce(
11163
- (set, parser2) => union(set, firstSetOf(parser2)),
11164
- { kind: "empty" }
11165
- )
11166
- ));
11167
- }
11168
- const inheritedSync = ctx._sync;
11169
- const deferredTrivia = needsDeferredTriviaCommit(ctx);
11170
- const values = def.valueUnused ? void 0 : [];
11171
- let cur = pos;
11172
- try {
11173
- for (let i = 0; i < parsers.length; i++) {
11174
- if (tolerant) ctx._sync = followSentinels[i] ?? inheritedSync;
11175
- const assertion = adjacency?.[i];
11176
- if (assertion) {
11177
- if (!holdsAdjacency(input, cur, ctx, assertion)) return adjacencyFail(cur, assertion);
11178
- if (values !== void 0) values.push(null);
11179
- continue;
11180
- }
11181
- let termPos = cur;
11182
- const markTlog = deferredTrivia ? cstTlLen(ctx) : 0;
11183
- const markLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
11184
- const markRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
11185
- if (ctx.trivia && i > 0) {
11186
- termPos = deferredTrivia ? commitTriviaScan(scanTriviaCompact(input, cur, ctx)) : advanceTrivia(input, cur, ctx);
11187
- }
11188
- const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
11189
- const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
11190
- const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
11191
- if (leading?.[i] === true) LEAD.at = cur;
11192
- const result = parsers[i].parse(input, termPos, ctx);
11193
- if (!result.ok) return result;
11194
- if (values !== void 0) values.push(result.value);
11195
- if (result.span.end > termPos || termPos === cur) cur = result.span.end;
11196
- else if (deferredTrivia) {
11197
- rollbackScannedTriviaAt(
11198
- ctx,
11199
- markTlog,
11200
- scanTlog,
11201
- markLog,
11202
- scanLog,
11203
- markRootLog,
11204
- scanRootLog
11205
- );
11206
- }
11207
- }
11208
- } finally {
11209
- if (tolerant) ctx._sync = inheritedSync;
12171
+ return (input, pos, ctx) => {
12172
+ if (input.charCodeAt(pos) === code) {
12173
+ ctx._sv = value;
12174
+ return pos + 1;
11210
12175
  }
11211
- return {
11212
- ok: true,
11213
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
11214
- value: values ?? void 0,
11215
- span: { start: pos, end: cur }
11216
- };
12176
+ ctx._fx = expected;
12177
+ return ~pos;
12178
+ };
12179
+ })() : (input, pos, ctx) => {
12180
+ const end = pos + value.length;
12181
+ if (end <= input.length) {
12182
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
12183
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
12184
+ ctx._sv = matchedValue;
12185
+ return end;
12186
+ }
12187
+ }
12188
+ ctx._fx = expected;
12189
+ return ~pos;
12190
+ };
12191
+ if (caseInsensitive) {
12192
+ const upper = value.toUpperCase();
12193
+ const lower = value.toLowerCase();
12194
+ const firstUpper = upper.codePointAt(0);
12195
+ const firstLower = lower.codePointAt(0);
12196
+ meta.firstSet = firstLower !== void 0 && firstUpper !== void 0 ? firstLower === firstUpper ? { kind: "ranges", ranges: [{ lo: firstLower, hi: firstLower }] } : { kind: "ranges", ranges: [
12197
+ { lo: firstLower, hi: firstLower },
12198
+ { lo: firstUpper, hi: firstUpper }
12199
+ ] } : firstSet2;
12200
+ }
12201
+ const expected = directTerminalFailureExpected({ tag: "literal", value, caseInsensitive });
12202
+ const parse2 = !caseInsensitive && value.length === 1 ? (() => {
12203
+ const code = value.charCodeAt(0);
12204
+ return function parse3(input, pos, ctx) {
12205
+ if (input.charCodeAt(pos) === code) {
12206
+ const span = { start: pos, end: pos + 1 };
12207
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
12208
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
12209
+ return { ok: true, value, span };
12210
+ }
12211
+ return failAt(ctx, expected, pos);
12212
+ };
12213
+ })() : function parse3(input, pos, ctx) {
12214
+ const end = pos + value.length;
12215
+ if (end > input.length) {
12216
+ return failAt(ctx, expected, pos);
12217
+ }
12218
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
12219
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
12220
+ const span = { start: pos, end };
12221
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
12222
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
12223
+ return { ok: true, value: matchedValue, span };
11217
12224
  }
12225
+ return failAt(ctx, expected, pos);
12226
+ };
12227
+ return {
12228
+ _tag: "literal",
12229
+ _meta: meta,
12230
+ _def: { tag: "literal", value, caseInsensitive },
12231
+ _parseScalar: parseScalar,
12232
+ parse: parse2
11218
12233
  };
11219
12234
  }
11220
12235
 
@@ -11285,124 +12300,6 @@ function ref() {
11285
12300
  return slot;
11286
12301
  }
11287
12302
 
11288
- // src/combinators/token.ts
11289
- function semanticLeafValue(_ctx, _input, _start, _end, value) {
11290
- return value;
11291
- }
11292
- function sourceLeafValue(ctx, input, start, end, value) {
11293
- return cstOutputHost(ctx.build) ? input.slice(start, end) : value;
11294
- }
11295
- function token(root) {
11296
- const meta = {
11297
- firstSet: root._meta.firstSet,
11298
- canMatchNewline: root._meta.canMatchNewline,
11299
- isTrivia: false
11300
- };
11301
- return {
11302
- _tag: "token",
11303
- _meta: meta,
11304
- _def: { tag: "token", parser: root },
11305
- parse(input, pos, ctx) {
11306
- const savedTrivia = ctx.trivia;
11307
- const savedKinds = ctx.triviaKindLabels;
11308
- const savedBuf = ctx._cstBuf;
11309
- const savedChildren = ctx._cstChildren;
11310
- const savedLeaves = ctx._cstLeaves;
11311
- const savedRaw = ctx._cstRawChildren;
11312
- const savedTriviaLog = ctx._cstTriviaLog;
11313
- const savedOuterTriviaLog = ctx._triviaLog;
11314
- const savedRootTriviaLog = ctx._rootTriviaLog;
11315
- const wasCapturing = cstCaptureActive(ctx);
11316
- ctx.trivia = void 0;
11317
- ctx.triviaKindLabels = void 0;
11318
- ctx._cstBuf = void 0;
11319
- ctx._cstChildren = void 0;
11320
- ctx._cstLeaves = void 0;
11321
- ctx._cstRawChildren = void 0;
11322
- ctx._cstTriviaLog = void 0;
11323
- ctx._triviaLog = void 0;
11324
- ctx._rootTriviaLog = void 0;
11325
- let result;
11326
- try {
11327
- result = root.parse(input, pos, ctx);
11328
- } finally {
11329
- ctx.trivia = savedTrivia;
11330
- ctx.triviaKindLabels = savedKinds;
11331
- ctx._cstBuf = savedBuf;
11332
- ctx._cstChildren = savedChildren;
11333
- ctx._cstLeaves = savedLeaves;
11334
- ctx._cstRawChildren = savedRaw;
11335
- ctx._cstTriviaLog = savedTriviaLog;
11336
- ctx._triviaLog = savedOuterTriviaLog;
11337
- ctx._rootTriviaLog = savedRootTriviaLog;
11338
- }
11339
- if (!result.ok) return result;
11340
- const span = result.span;
11341
- const value = input.slice(pos, span.end);
11342
- if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span });
11343
- return { ok: true, value, span };
11344
- }
11345
- };
11346
- }
11347
- function makeLeaf(root, fn, sourceCstValue) {
11348
- const cstValue = sourceCstValue ? sourceLeafValue : semanticLeafValue;
11349
- const meta = {
11350
- firstSet: root._meta.firstSet,
11351
- canMatchNewline: root._meta.canMatchNewline,
11352
- isTrivia: false
11353
- };
11354
- return {
11355
- _tag: "leaf",
11356
- _meta: meta,
11357
- _def: {
11358
- tag: "leaf",
11359
- parser: root,
11360
- fn,
11361
- ...sourceCstValue ? { cstValue: "source" } : {}
11362
- },
11363
- parse(input, pos, ctx) {
11364
- const savedBuf = ctx._cstBuf;
11365
- const savedChildren = ctx._cstChildren;
11366
- const savedLeaves = ctx._cstLeaves;
11367
- const savedRaw = ctx._cstRawChildren;
11368
- const savedTriviaLog = ctx._cstTriviaLog;
11369
- const savedOuterTriviaLog = ctx._triviaLog;
11370
- const wasCapturing = cstCaptureActive(ctx);
11371
- ctx._cstBuf = void 0;
11372
- ctx._cstChildren = void 0;
11373
- ctx._cstLeaves = void 0;
11374
- ctx._cstRawChildren = void 0;
11375
- ctx._cstTriviaLog = void 0;
11376
- ctx._triviaLog = void 0;
11377
- let result;
11378
- try {
11379
- result = root.parse(input, pos, ctx);
11380
- } finally {
11381
- ctx._cstBuf = savedBuf;
11382
- ctx._cstChildren = savedChildren;
11383
- ctx._cstLeaves = savedLeaves;
11384
- ctx._cstRawChildren = savedRaw;
11385
- ctx._cstTriviaLog = savedTriviaLog;
11386
- ctx._triviaLog = savedOuterTriviaLog;
11387
- }
11388
- if (!result.ok) return result;
11389
- const value = fn(result.value, result.span);
11390
- if (wasCapturing) pushCstLeaf(ctx, {
11391
- _tag: "leaf",
11392
- value: cstValue(ctx, input, pos, result.span.end, value),
11393
- span: result.span
11394
- });
11395
- return { ok: true, value, span: result.span };
11396
- }
11397
- };
11398
- }
11399
- function leaf(root, fn) {
11400
- return makeLeaf(root, fn, false);
11401
- }
11402
- function sourceLeaf(root, fn) {
11403
- return makeLeaf(root, fn, true);
11404
- }
11405
-
11406
12303
  // src/combinators/scanTo.ts
11407
12304
  function resolveScanSkip(explicitSkip, raw, ctx) {
11408
12305
  if (raw) return [];
@@ -11711,37 +12608,120 @@ function clearOf(ranges, input, from, to) {
11711
12608
  return true;
11712
12609
  }
11713
12610
 
11714
- // src/table/run-support.ts
11715
- function lineCol(ctx, offset) {
11716
- const starts = ctx._lineStarts ?? [0];
11717
- let lo = 0, hi = starts.length - 1;
11718
- while (lo < hi) {
11719
- const mid = lo + hi + 1 >> 1;
11720
- if (starts[mid] <= offset) lo = mid;
11721
- else hi = mid - 1;
12611
+ // src/table/fail-clean.ts
12612
+ function passChild(code, ip) {
12613
+ switch (code[ip]) {
12614
+ case OP_RULE:
12615
+ case OP_LABEL:
12616
+ case OP_COV:
12617
+ case OP_TOKEN:
12618
+ return code[ip + 1];
12619
+ case OP_GATE:
12620
+ case OP_NODE:
12621
+ case OP_NODE_TRACK:
12622
+ case OP_XFORM:
12623
+ case OP_FIELD:
12624
+ case OP_SCOPE:
12625
+ case OP_SCOPE_CAP:
12626
+ case OP_SCOPE_PLAIN:
12627
+ case OP_WITHCTX:
12628
+ case OP_LEAF:
12629
+ case OP_SOURCE_LEAF:
12630
+ return code[ip + 2];
12631
+ default:
12632
+ return -1;
11722
12633
  }
11723
- return [lo + 1, offset - starts[lo] + 1];
11724
- }
11725
- function spanLines(ctx, start, end) {
11726
- const s = lineCol(ctx, start), e = lineCol(ctx, end);
11727
- return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] };
11728
12634
  }
11729
- function rawEntry(v, input, s, e) {
11730
- if (typeof v === "object" && v !== null) {
11731
- const tg = v._tag;
11732
- if (tg === "node" || tg === "leaf" || tg === "parseError") return v;
11733
- }
11734
- return {
11735
- _tag: "leaf",
11736
- value: typeof v === "string" ? v : typeof v === "object" && v !== null ? input.slice(s, e) : "",
11737
- span: { start: s, end: e }
12635
+ function seqCount(code, ip) {
12636
+ const op = code[ip];
12637
+ return op === OP_SEQ || op === OP_SEQV ? ip + 1 : op === OP_SEQX ? ip + 2 : -1;
12638
+ }
12639
+ function failCleanAnalysis(code) {
12640
+ const cleanAt = new Uint8Array(code.length);
12641
+ const neverAt = new Uint8Array(code.length);
12642
+ const neverFails = (ip) => {
12643
+ const hit = neverAt[ip];
12644
+ if (hit !== 0) return hit === 1;
12645
+ neverAt[ip] = 3;
12646
+ let out = false;
12647
+ const op = code[ip];
12648
+ if (op === OP_OPT || op === OP_EMPTY || op === OP_EXPECT) out = true;
12649
+ else if (op === OP_REP || op === OP_REPV) out = code[ip + 2] === 0;
12650
+ else {
12651
+ const at = seqCount(code, ip);
12652
+ if (at >= 0) {
12653
+ out = true;
12654
+ for (let i = 1, n = code[at]; out && i <= n; i++) out = neverFails(code[at + i]);
12655
+ } else if (op !== OP_GATE) {
12656
+ const c = passChild(code, ip);
12657
+ out = c >= 0 && neverFails(c);
12658
+ }
12659
+ }
12660
+ neverAt[ip] = out ? 1 : 2;
12661
+ return out;
11738
12662
  };
11739
- }
11740
- function lead(input, pos) {
11741
- if (pos >= input.length) return -1;
11742
- const c = input.charCodeAt(pos);
11743
- if (c < 55296 || c > 56319) return c;
11744
- return input.codePointAt(pos) ?? c;
12663
+ const clean = (ip) => {
12664
+ const hit = cleanAt[ip];
12665
+ if (hit !== 0) return hit === 1;
12666
+ cleanAt[ip] = 3;
12667
+ let out = false;
12668
+ switch (code[ip]) {
12669
+ // A terminal pushes its leaf only on a match; NOT/PEEK/ATTEMPT restore
12670
+ // their own mark; OPT, EMPTY and EXPECT do not fail uncommitted.
12671
+ case OP_LIT:
12672
+ case OP_RX:
12673
+ case OP_LIT_TRACK:
12674
+ case OP_RX_TRACK:
12675
+ case OP_LIT_CI:
12676
+ case OP_LIT_CI_TRACK:
12677
+ case OP_GUARD:
12678
+ case OP_NOT:
12679
+ case OP_PEEK:
12680
+ case OP_ATTEMPT:
12681
+ case OP_EMPTY:
12682
+ case OP_OPT:
12683
+ case OP_EXPECT:
12684
+ out = true;
12685
+ break;
12686
+ // Min 0 does not fail uncommitted; min 1 fails only with no item kept, and
12687
+ // restores the failed item itself.
12688
+ case OP_REP:
12689
+ case OP_REPV:
12690
+ out = code[ip + 2] <= 1;
12691
+ break;
12692
+ // Only the first term may fail: a later one fails after the terms before it
12693
+ // wrote, and after the trivia scanned in front of it.
12694
+ case OP_SEQ:
12695
+ case OP_SEQV:
12696
+ case OP_SEQX: {
12697
+ const at = seqCount(code, ip);
12698
+ const n = code[at];
12699
+ out = n > 0 && clean(code[at + 1]);
12700
+ for (let i = 2; out && i <= n; i++) out = neverFails(code[at + i]);
12701
+ break;
12702
+ }
12703
+ case OP_CHOICE: {
12704
+ out = true;
12705
+ for (let i = 0, n = code[ip + 2]; out && i < n; i++) out = clean(code[ip + 4 + i]);
12706
+ break;
12707
+ }
12708
+ case OP_ROUTED:
12709
+ out = code[ip + 1] < 0 || clean(code[ip + 1]);
12710
+ break;
12711
+ // An arm's failure and the fallback's are committed; only the selector's is
12712
+ // not, and without a fallback an unknown key fails after the selector wrote.
12713
+ case OP_DISPATCH:
12714
+ out = code[ip + 3] >= 0 && clean(code[ip + 1]);
12715
+ break;
12716
+ default: {
12717
+ const c = passChild(code, ip);
12718
+ out = c >= 0 && clean(c);
12719
+ }
12720
+ }
12721
+ cleanAt[ip] = out ? 1 : 2;
12722
+ return out;
12723
+ };
12724
+ return clean;
11745
12725
  }
11746
12726
 
11747
12727
  // src/grammar-metadata.ts
@@ -11839,12 +12819,6 @@ function hasDirectBuilder(prog) {
11839
12819
  return false;
11840
12820
  }
11841
12821
 
11842
- // src/cst/root-trivia-scope.ts
11843
- var ROOT_TRIVIA_SCOPE_REFUSAL = "parser(): selected root trivia requires classifiedTrivia() for every local trivia scope, or rootCapture: 'opaque'.";
11844
- function refuseUnclassifiedRootScope(strict) {
11845
- if (strict === true) throw new TypeError(ROOT_TRIVIA_SCOPE_REFUSAL);
11846
- }
11847
-
11848
12822
  // src/table/cell.ts
11849
12823
  var FAIL = /* @__PURE__ */ Symbol("pm.fail");
11850
12824
  var newEndCell = () => ({ e: 0 });
@@ -11895,6 +12869,7 @@ function assemble(t2, prog, cfg) {
11895
12869
  const swapLegal = !cfg.trackLines;
11896
12870
  const hostCst = cfg.hostCst;
11897
12871
  const REC = prog.rec === 1 && cfg.tolerant;
12872
+ const PROBE = cfg.probe;
11898
12873
  const sentinels = /* @__PURE__ */ new Map();
11899
12874
  function sentinelFor(cls) {
11900
12875
  if (cls < 0) return void 0;
@@ -11940,6 +12915,8 @@ function assemble(t2, prog, cfg) {
11940
12915
  }
11941
12916
  const memo = /* @__PURE__ */ new Map();
11942
12917
  const reached = /* @__PURE__ */ new Set();
12918
+ const NULLABLE = /* @__PURE__ */ new Map();
12919
+ const CLEAN = REC ? () => false : failCleanAnalysis(code);
11943
12920
  function pushLeaf(ctx, value, s, e) {
11944
12921
  const lf = { _tag: "leaf", value, span: { start: s, end: e } };
11945
12922
  pushCstLeaf(ctx, lf);
@@ -12138,6 +13115,22 @@ function assemble(t2, prog, cfg) {
12138
13115
  return node2;
12139
13116
  };
12140
13117
  }
13118
+ function passCollapseNode(child, build, readsRaw) {
13119
+ return (input, pos, ctx) => {
13120
+ const v = child(input, pos, ctx);
13121
+ if (v !== void 0) return v;
13122
+ return collapseEmpty(ctx, build, input, pos, EC.e, readsRaw);
13123
+ };
13124
+ }
13125
+ function gatedPassCollapseNode(blockers, last, classic, build, readsRaw) {
13126
+ return (input, pos, ctx) => {
13127
+ const c = lead(input, pos);
13128
+ for (let i = 0; i < blockers.length; i++) if (classHas2(blockers[i], c)) return classic(input, pos, ctx);
13129
+ const v = last(input, pos, ctx);
13130
+ if (v !== void 0) return v;
13131
+ return collapseEmpty(ctx, build, input, pos, EC.e, readsRaw);
13132
+ };
13133
+ }
12141
13134
  function plainProjectNode(child, projection, type) {
12142
13135
  return (input, pos, ctx) => {
12143
13136
  const sCh = ctx._cstChildren;
@@ -12508,21 +13501,27 @@ function assemble(t2, prog, cfg) {
12508
13501
  }
12509
13502
  function dispatchBranch(child, usesRouted) {
12510
13503
  if (!usesRouted) {
12511
- return (input, _pos, selEnd, key, _selectorMark, ctx) => {
12512
- const mark = saveTriviaMark(ctx);
13504
+ return (input, _pos, selEnd, key, ctx) => {
13505
+ const need = markCst(ctx);
13506
+ const mRaw = MRAW;
13507
+ const mTl = MTL;
13508
+ const mLv = MLV;
13509
+ const mFl = need ? ctx._fields?.length ?? 0 : 0;
13510
+ const mEr = need ? ctx._errors?.length ?? 0 : 0;
13511
+ const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
13512
+ const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
12513
13513
  const v = child(input, selEnd, ctx);
12514
13514
  if (v === FAIL) {
12515
- rollbackTrivia(ctx, mark);
13515
+ if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12516
13516
  ctx._fc = true;
12517
13517
  return FAIL;
12518
13518
  }
12519
13519
  return [key, v];
12520
13520
  };
12521
13521
  }
12522
- return (input, pos, selEnd, key, selectorMark, ctx) => {
13522
+ return (input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
12523
13523
  const savedRouted = ctx._routed;
12524
- rollbackTrivia(ctx, selectorMark);
12525
- const mark = saveTriviaMark(ctx);
13524
+ if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12526
13525
  ctx._routed = { value: key, span: { start: pos, end: selEnd } };
12527
13526
  let v;
12528
13527
  try {
@@ -12531,7 +13530,7 @@ function assemble(t2, prog, cfg) {
12531
13530
  ctx._routed = savedRouted;
12532
13531
  }
12533
13532
  if (v === FAIL) {
12534
- rollbackTrivia(ctx, mark);
13533
+ if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12535
13534
  ctx._fc = true;
12536
13535
  return FAIL;
12537
13536
  }
@@ -12539,12 +13538,12 @@ function assemble(t2, prog, cfg) {
12539
13538
  };
12540
13539
  }
12541
13540
  function dispatchArmBlock(start, a0, a1, a2, a3, next) {
12542
- return (arm, input, pos, selEnd, key, selectorMark, ctx) => {
12543
- if (arm === start) return a0(input, pos, selEnd, key, selectorMark, ctx);
12544
- if (arm === start + 1) return a1(input, pos, selEnd, key, selectorMark, ctx);
12545
- if (arm === start + 2) return a2(input, pos, selEnd, key, selectorMark, ctx);
12546
- if (arm === start + 3) return a3(input, pos, selEnd, key, selectorMark, ctx);
12547
- return next === void 0 ? FAIL : next(arm, input, pos, selEnd, key, selectorMark, ctx);
13541
+ return (arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
13542
+ if (arm === start) return a0(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13543
+ if (arm === start + 1) return a1(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13544
+ if (arm === start + 2) return a2(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13545
+ if (arm === start + 3) return a3(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13546
+ return next === void 0 ? FAIL : next(arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12548
13547
  };
12549
13548
  }
12550
13549
  function trackLinesInto(ctx, input, end) {
@@ -12563,7 +13562,7 @@ function assemble(t2, prog, cfg) {
12563
13562
  if (flight !== void 0) return flight.fwd;
12564
13563
  const selected = HYBRID?.byIp[ip];
12565
13564
  if (selected !== void 0) {
12566
- const piece2 = hybridPiece(selected);
13565
+ const piece2 = scanHandoffNeeded(code, ip, closureLabels) ? hybridPiece(selected) : selected;
12567
13566
  memo.set(ip, piece2);
12568
13567
  reached.add(ip);
12569
13568
  return piece2;
@@ -12574,14 +13573,55 @@ function assemble(t2, prog, cfg) {
12574
13573
  target = p;
12575
13574
  } };
12576
13575
  inFlight.set(ip, holder);
12577
- const op = code[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);
13576
+ const piece = scopeIsIdentity(code, ip, closureLabels.at(ip)) ? link(code[ip + 2]) : countedPiece(lookaheadLedNode(ip) ?? lower(ip), ip);
12579
13577
  inFlight.delete(ip);
12580
13578
  holder.set(piece);
12581
13579
  memo.set(ip, piece);
12582
13580
  reached.add(ip);
12583
13581
  return piece;
12584
13582
  }
13583
+ let FIRST_DECIDED;
13584
+ let NODE_BODY;
13585
+ const DECIDED = (_input, pos) => {
13586
+ EC.e = pos;
13587
+ return null;
13588
+ };
13589
+ function lookaheadLedNode(ip) {
13590
+ const op = code[ip];
13591
+ if (op !== OP_NODE && op !== OP_NODE_TRACK || hostCst || REC || cfg.probe || cfg.coverage || cfg.trackLines) return void 0;
13592
+ if (passCollapse(code, ip, hostCst) || gatedPassCollapse(code, ip, hostCst) !== void 0) return void 0;
13593
+ const body = code[ip + 2];
13594
+ const bop = code[body];
13595
+ if (bop !== OP_SEQ && bop !== OP_SEQV && bop !== OP_SEQX) return void 0;
13596
+ const base = bop === OP_SEQX ? body + 3 : body + 2;
13597
+ if (code[bop === OP_SEQX ? body + 2 : body + 1] < 2) return void 0;
13598
+ const first = code[base];
13599
+ const negative = code[first] === OP_NOT;
13600
+ const terminal = negative ? scalarTerminalNotChild(code, first) : code[first] === OP_PEEK ? scalarTerminalPeekChild(code, first) : -1;
13601
+ if (terminal < 0) return void 0;
13602
+ const recognize = scalarFor(terminal);
13603
+ const xf = fx[code[first + 2]];
13604
+ FIRST_DECIDED = DECIDED;
13605
+ NODE_BODY = lower(body);
13606
+ const node2 = lower(ip);
13607
+ NODE_BODY = void 0;
13608
+ return negative ? (input, pos, ctx) => {
13609
+ if (recognize(input, pos) >= 0) {
13610
+ ctx._fe = pos;
13611
+ ctx._fx = xf;
13612
+ EC.e = pos;
13613
+ return FAIL;
13614
+ }
13615
+ return node2(input, pos, ctx);
13616
+ } : (input, pos, ctx) => {
13617
+ if (recognize(input, pos) < 0) {
13618
+ ctx._fe = pos;
13619
+ ctx._fx = xf;
13620
+ return FAIL;
13621
+ }
13622
+ return node2(input, pos, ctx);
13623
+ };
13624
+ }
12585
13625
  function lower(ip) {
12586
13626
  const op = code[ip];
12587
13627
  switch (op) {
@@ -13126,8 +14166,10 @@ function assemble(t2, prog, cfg) {
13126
14166
  const mEr = need ? ctx._errors?.length ?? 0 : 0;
13127
14167
  const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
13128
14168
  const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
14169
+ const probeBest = PROBE ? ctx._probe?.best : void 0;
13129
14170
  const v = child(input, pos, ctx);
13130
14171
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14172
+ if (PROBE && ctx._probe !== void 0) ctx._probe.best = probeBest ?? null;
13131
14173
  if (v === FAIL) {
13132
14174
  EC.e = pos;
13133
14175
  return null;
@@ -13138,6 +14180,20 @@ function assemble(t2, prog, cfg) {
13138
14180
  };
13139
14181
  }
13140
14182
  case OP_PEEK: {
14183
+ const scalarChild = scalarTerminalPeekChild(code, ip);
14184
+ if (scalarChild >= 0) {
14185
+ const recognize = scalarFor(scalarChild);
14186
+ const xf2 = fx[code[ip + 2]];
14187
+ return (input, pos, ctx) => {
14188
+ if (recognize(input, pos) < 0) {
14189
+ ctx._fe = pos;
14190
+ ctx._fx = xf2;
14191
+ return FAIL;
14192
+ }
14193
+ EC.e = pos;
14194
+ return null;
14195
+ };
14196
+ }
13141
14197
  const child = link(code[ip + 1]);
13142
14198
  const xf = fx[code[ip + 2]];
13143
14199
  const gate = lookaheadClass(code[ip + 1]);
@@ -13155,8 +14211,10 @@ function assemble(t2, prog, cfg) {
13155
14211
  const mEr = need ? ctx._errors?.length ?? 0 : 0;
13156
14212
  const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
13157
14213
  const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
14214
+ const probeBest = PROBE ? ctx._probe?.best : void 0;
13158
14215
  const v = child(input, pos, ctx);
13159
14216
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14217
+ if (PROBE && ctx._probe !== void 0) ctx._probe.best = probeBest ?? null;
13160
14218
  if (v === FAIL) {
13161
14219
  ctx._fe = pos;
13162
14220
  ctx._fx = xf;
@@ -13169,12 +14227,13 @@ function assemble(t2, prog, cfg) {
13169
14227
  case OP_OPT: {
13170
14228
  const child = link(code[ip + 1]);
13171
14229
  const gate = lookaheadClass(code[ip + 1]);
14230
+ const childClean = CLEAN(code[ip + 1]);
13172
14231
  return (input, pos, ctx) => {
13173
14232
  if (gate !== null && !classHas2(gate, lead(input, pos))) {
13174
14233
  EC.e = pos;
13175
14234
  return null;
13176
14235
  }
13177
- const need = markCst(ctx);
14236
+ const need = !childClean && markCst(ctx);
13178
14237
  const mRaw = MRAW;
13179
14238
  const mTl = MTL;
13180
14239
  const mLv = MLV;
@@ -13372,6 +14431,9 @@ function assemble(t2, prog, cfg) {
13372
14431
  const projection = reducer < 0 ? ~reducer : -1;
13373
14432
  const fn = fused && projection < 0 ? fns[reducer] : void 0;
13374
14433
  const step = closureLabels.at(ip).buf ? nextTermBuffered : nextTerm;
14434
+ const decided = FIRST_DECIDED;
14435
+ FIRST_DECIDED = void 0;
14436
+ const linkFirst = () => decided ?? link(code[base]);
13375
14437
  const runnerBlock = (count, r0, r1, r2, r3, next) => {
13376
14438
  if (count === 1) return (input, cur, ctx, values, inherited) => {
13377
14439
  cur = r0(input, cur, ctx, inherited);
@@ -13417,7 +14479,7 @@ function assemble(t2, prog, cfg) {
13417
14479
  };
13418
14480
  };
13419
14481
  if (REC) {
13420
- const first2 = link(code[base]);
14482
+ const first2 = linkFirst();
13421
14483
  const firstSync = sentinelFor(code[base + n]);
13422
14484
  const runnerAt = (i) => {
13423
14485
  const sync = sentinelFor(code[base + n + i]);
@@ -13453,13 +14515,13 @@ function assemble(t2, prog, cfg) {
13453
14515
  const bindRunners = (start) => {
13454
14516
  const count = Math.min(4, n - start);
13455
14517
  const at = (offset) => runnerAt(start + offset);
13456
- const never = () => -1;
14518
+ const never2 = () => -1;
13457
14519
  return runnerBlock(
13458
14520
  count,
13459
14521
  at(0),
13460
- count > 1 ? at(1) : never,
13461
- count > 2 ? at(2) : never,
13462
- count > 3 ? at(3) : never,
14522
+ count > 1 ? at(1) : never2,
14523
+ count > 2 ? at(2) : never2,
14524
+ count > 3 ? at(3) : never2,
13463
14525
  start + count < n ? bindRunners(start + count) : void 0
13464
14526
  );
13465
14527
  };
@@ -13509,15 +14571,31 @@ function assemble(t2, prog, cfg) {
13509
14571
  let hasAdj = false;
13510
14572
  for (let i = 1; i < n; i++) {
13511
14573
  const kidIp = code[base + i];
13512
- if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp)) {
14574
+ if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp) || holdsGap(code, k, base, n, i, NULLABLE)) {
13513
14575
  hasAdj = true;
13514
14576
  break;
13515
14577
  }
13516
14578
  }
13517
14579
  if (hasAdj) {
13518
- const first2 = link(code[base]);
14580
+ const first2 = linkFirst();
13519
14581
  const runnerAt = (i) => {
13520
14582
  const kidIp = code[base + i];
14583
+ if (holdsGap(code, k, base, n, i, NULLABLE)) {
14584
+ const kid = link(kidIp);
14585
+ return (input, cur, ctx) => {
14586
+ if (ctx.trivia === void 0) {
14587
+ const v2 = kid(input, cur, ctx);
14588
+ if (v2 === FAIL) return -1;
14589
+ TERMV = v2;
14590
+ return EC.e;
14591
+ }
14592
+ const scanEnd = skipTrivia(input, cur, ctx);
14593
+ const v = kid(input, scanEnd, ctx);
14594
+ if (v === FAIL) return -1;
14595
+ TERMV = v;
14596
+ return scanEnd;
14597
+ };
14598
+ }
13521
14599
  if (code[kidIp] !== OP_ADJ) {
13522
14600
  const kid = link(kidIp);
13523
14601
  if (leadingTriviaFieldAt(code, k, kidIp)) {
@@ -13542,13 +14620,13 @@ function assemble(t2, prog, cfg) {
13542
14620
  const bindRunners = (start) => {
13543
14621
  const count = Math.min(4, n - start);
13544
14622
  const at = (offset) => runnerAt(start + offset);
13545
- const never = () => -1;
14623
+ const never2 = () => -1;
13546
14624
  return runnerBlock(
13547
14625
  count,
13548
14626
  at(0),
13549
- count > 1 ? at(1) : never,
13550
- count > 2 ? at(2) : never,
13551
- count > 3 ? at(3) : never,
14627
+ count > 1 ? at(1) : never2,
14628
+ count > 2 ? at(2) : never2,
14629
+ count > 3 ? at(3) : never2,
13552
14630
  start + count < n ? bindRunners(start + count) : void 0
13553
14631
  );
13554
14632
  };
@@ -13585,7 +14663,7 @@ function assemble(t2, prog, cfg) {
13585
14663
  };
13586
14664
  }
13587
14665
  if (n === 1) {
13588
- const k0 = link(code[base]);
14666
+ const k0 = linkFirst();
13589
14667
  if (fused) {
13590
14668
  return (input, pos, ctx) => {
13591
14669
  const v = k0(input, pos, ctx);
@@ -13607,7 +14685,7 @@ function assemble(t2, prog, cfg) {
13607
14685
  };
13608
14686
  }
13609
14687
  if (n === 2) {
13610
- const k0 = link(code[base]), k1 = link(code[base + 1]);
14688
+ const k0 = linkFirst(), k1 = link(code[base + 1]);
13611
14689
  if (fused) {
13612
14690
  return (input, pos, ctx) => {
13613
14691
  const v0 = k0(input, pos, ctx);
@@ -13638,7 +14716,7 @@ function assemble(t2, prog, cfg) {
13638
14716
  };
13639
14717
  }
13640
14718
  if (n === 3) {
13641
- const k0 = link(code[base]), k1 = link(code[base + 1]), k2 = link(code[base + 2]);
14719
+ const k0 = linkFirst(), k1 = link(code[base + 1]), k2 = link(code[base + 2]);
13642
14720
  if (fused) {
13643
14721
  return (input, pos, ctx) => {
13644
14722
  const v0 = k0(input, pos, ctx);
@@ -13732,7 +14810,7 @@ function assemble(t2, prog, cfg) {
13732
14810
  start + count < n ? bind(start + count) : void 0
13733
14811
  );
13734
14812
  };
13735
- const first = link(code[base]);
14813
+ const first = linkFirst();
13736
14814
  const tail = bind(1);
13737
14815
  const runDirectTerms = (input, pos, ctx, values) => {
13738
14816
  const v0 = first(input, pos, ctx);
@@ -13771,6 +14849,8 @@ function assemble(t2, prog, cfg) {
13771
14849
  const n = code[ip + 2];
13772
14850
  const choiceFx = fx[code[ip + 3]];
13773
14851
  const base = ip + 4;
14852
+ for (let i = 0; i < n; i++) link(code[base + i]);
14853
+ const armsClean = CLEAN(ip);
13774
14854
  if (table.exclusive) {
13775
14855
  const ascii = table.ascii;
13776
14856
  const hi = table.hi;
@@ -13910,7 +14990,7 @@ function assemble(t2, prog, cfg) {
13910
14990
  if (n === 3) addGate(g2, 4);
13911
14991
  return (input, pos, ctx) => {
13912
14992
  const c = lead(input, pos);
13913
- const need = markCst(ctx);
14993
+ const need = !armsClean && markCst(ctx);
13914
14994
  const mRaw = MRAW;
13915
14995
  const mTl = MTL;
13916
14996
  const mLv = MLV;
@@ -14127,7 +15207,7 @@ function assemble(t2, prog, cfg) {
14127
15207
  if (pn !== 0) {
14128
15208
  return (input, pos, ctx) => {
14129
15209
  const c = lead(input, pos);
14130
- const need = markCst(ctx);
15210
+ const need = !armsClean && markCst(ctx);
14131
15211
  const mRaw = MRAW;
14132
15212
  const mTl = MTL;
14133
15213
  const mLv = MLV;
@@ -14184,7 +15264,7 @@ function assemble(t2, prog, cfg) {
14184
15264
  }
14185
15265
  return (input, pos, ctx) => {
14186
15266
  const c = lead(input, pos);
14187
- const need = markCst(ctx);
15267
+ const need = !armsClean && markCst(ctx);
14188
15268
  const mRaw = MRAW;
14189
15269
  const mTl = MTL;
14190
15270
  const mLv = MLV;
@@ -14232,7 +15312,7 @@ function assemble(t2, prog, cfg) {
14232
15312
  }
14233
15313
  return (input, pos, ctx) => {
14234
15314
  const c = lead(input, pos);
14235
- const need = markCst(ctx);
15315
+ const need = !armsClean && markCst(ctx);
14236
15316
  const mRaw = MRAW;
14237
15317
  const mTl = MTL;
14238
15318
  const mLv = MLV;
@@ -14286,6 +15366,21 @@ function assemble(t2, prog, cfg) {
14286
15366
  return lit(input, pos, ctx);
14287
15367
  };
14288
15368
  }
15369
+ case OP_RGATE: {
15370
+ const cls = cc[code[ip + 1]];
15371
+ const xf = fx[code[ip + 3]];
15372
+ const child = link(code[ip + 2]);
15373
+ if (cfg.tolerant || cfg.probe) return child;
15374
+ return (input, pos, ctx) => {
15375
+ const item = ctx._routed;
15376
+ if (item !== void 0 && item.span.start === pos && !classHas2(cls, lead(input, item.span.end))) {
15377
+ ctx._fe = item.span.end;
15378
+ ctx._fx = xf;
15379
+ return FAIL;
15380
+ }
15381
+ return child(input, pos, ctx);
15382
+ };
15383
+ }
14289
15384
  case OP_ARMGATE: {
14290
15385
  const pred = fns[code[ip + 1]];
14291
15386
  const child = link(code[ip + 2]);
@@ -14354,6 +15449,7 @@ function assemble(t2, prog, cfg) {
14354
15449
  const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
14355
15450
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
14356
15451
  const skipBeforeFirst = sepIp < 0 && min === 0;
15452
+ const itemClean = sepIp < 0 && CLEAN(code[ip + 1]) && !rowNullable(code, k, code[ip + 1], NULLABLE);
14357
15453
  if (REC) {
14358
15454
  const itemFx2 = fx[code[ip + 6]];
14359
15455
  const sepSent = sentinelFor(code[ip + 7]);
@@ -14450,6 +15546,8 @@ function assemble(t2, prog, cfg) {
14450
15546
  const out = collect ? [] : void 0;
14451
15547
  const hasTrivia = ctx.trivia !== void 0;
14452
15548
  const needMark = rollbackNeeded(ctx);
15549
+ const markLater = needMark && (!itemClean || hasTrivia);
15550
+ const markFirst = needMark && (!itemClean || hasTrivia && skipBeforeFirst);
14453
15551
  const gateItems = itemMayStart !== void 0 && ctx._probe === void 0;
14454
15552
  let cur = pos;
14455
15553
  let count = 0;
@@ -14457,14 +15555,15 @@ function assemble(t2, prog, cfg) {
14457
15555
  if (max >= 0 && count >= max) break;
14458
15556
  if (sep !== void 0 && count > 0 && count >= min && cur >= input.length) break;
14459
15557
  if (count >= min && sep === void 0 && !hasTrivia && gateItems && !itemMayStart(input, cur)) break;
14460
- if (needMark) markCst(ctx);
14461
- const mRaw = needMark ? MRAW : 0;
14462
- const mTl = needMark ? MTL : 0;
14463
- const mLv = needMark ? MLV : 0;
14464
- const mFl = needMark ? ctx._fields?.length ?? 0 : 0;
14465
- const mEr = needMark ? ctx._errors?.length ?? 0 : 0;
14466
- const mLog = needMark ? ctx._triviaLog?.length ?? 0 : 0;
14467
- const mRoot = needMark ? ctx._rootTriviaLog?.length ?? 0 : 0;
15558
+ const markIt = count > 0 ? markLater : markFirst;
15559
+ if (markIt) markCst(ctx);
15560
+ const mRaw = markIt ? MRAW : 0;
15561
+ const mTl = markIt ? MTL : 0;
15562
+ const mLv = markIt ? MLV : 0;
15563
+ const mFl = markIt ? ctx._fields?.length ?? 0 : 0;
15564
+ const mEr = markIt ? ctx._errors?.length ?? 0 : 0;
15565
+ const mLog = markIt ? ctx._triviaLog?.length ?? 0 : 0;
15566
+ const mRoot = markIt ? ctx._rootTriviaLog?.length ?? 0 : 0;
14468
15567
  let itemStart = cur;
14469
15568
  let sepEnd = -1;
14470
15569
  const viaRepItem = sep === void 0 && count >= min && (count > 0 || skipBeforeFirst);
@@ -14476,7 +15575,7 @@ function assemble(t2, prog, cfg) {
14476
15575
  if (sepLeads) LEAD.at = cur;
14477
15576
  const sv = sep(input, sp, ctx);
14478
15577
  if (sv === FAIL) {
14479
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15578
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14480
15579
  if (committed3(ctx)) return FAIL;
14481
15580
  break;
14482
15581
  }
@@ -14487,25 +15586,25 @@ function assemble(t2, prog, cfg) {
14487
15586
  itemStart = skipTrivia(input, itemStart, ctx);
14488
15587
  }
14489
15588
  if (itemStart >= input.length && viaRepItem) {
14490
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15589
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14491
15590
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
14492
15591
  break;
14493
15592
  }
14494
15593
  if (count >= min && gateItems && !itemMayStart(input, itemStart)) {
14495
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15594
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14496
15595
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
14497
15596
  break;
14498
15597
  }
14499
15598
  ctx._fc = false;
14500
15599
  const v = child(input, itemStart, ctx);
14501
15600
  if (v === FAIL) {
14502
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15601
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14503
15602
  if (committed3(ctx)) return FAIL;
14504
15603
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
14505
15604
  break;
14506
15605
  }
14507
15606
  if (EC.e === itemStart && viaRepItem) {
14508
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15607
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14509
15608
  break;
14510
15609
  }
14511
15610
  if (out !== void 0) out.push(v);
@@ -14579,7 +15678,14 @@ function assemble(t2, prog, cfg) {
14579
15678
  const runArm = n === 0 ? void 0 : bindArms(0);
14580
15679
  const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted);
14581
15680
  return (input, pos, ctx) => {
14582
- const selectorMark = saveTriviaMark(ctx);
15681
+ const need = markCst(ctx);
15682
+ const mRaw = MRAW;
15683
+ const mTl = MTL;
15684
+ const mLv = MLV;
15685
+ const mFl = need ? ctx._fields?.length ?? 0 : 0;
15686
+ const mEr = need ? ctx._errors?.length ?? 0 : 0;
15687
+ const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
15688
+ const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
14583
15689
  const selVal = selector(input, pos, ctx);
14584
15690
  if (selVal === FAIL) return FAIL;
14585
15691
  const selEnd = EC.e;
@@ -14591,9 +15697,9 @@ function assemble(t2, prog, cfg) {
14591
15697
  ctx._fx = expected;
14592
15698
  return FAIL;
14593
15699
  }
14594
- return runOther(input, pos, selEnd, key, selectorMark, ctx);
15700
+ return runOther(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14595
15701
  }
14596
- return runArm(arm, input, pos, selEnd, key, selectorMark, ctx);
15702
+ return runArm(arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14597
15703
  };
14598
15704
  }
14599
15705
  /* ── node ────────────────────────────────────────────────────────────── */
@@ -14629,7 +15735,8 @@ function assemble(t2, prog, cfg) {
14629
15735
  return nd;
14630
15736
  };
14631
15737
  }
14632
- const child = link(code[ip + 2]);
15738
+ const child = NODE_BODY ?? link(code[ip + 2]);
15739
+ NODE_BODY = void 0;
14633
15740
  const proj = code[ip + 4];
14634
15741
  const buildIdx = code[ip + 1];
14635
15742
  const type = k[code[ip + 5]];
@@ -14653,6 +15760,30 @@ function assemble(t2, prog, cfg) {
14653
15760
  if (build !== void 0 && proj < 0 && flags === 18) {
14654
15761
  return childrenOnlyFieldsBuildNode(child, build);
14655
15762
  }
15763
+ if (build !== void 0 && passCollapse(code, ip, hostCst)) {
15764
+ return passCollapseNode(child, build, flags === 32);
15765
+ }
15766
+ const gated = build !== void 0 && !cfg.tolerant && !cfg.probe ? gatedPassCollapse(code, ip, hostCst) : void 0;
15767
+ const triviaLead = gated === void 0 ? void 0 : triviaLeadRanges(closureLabels.at(code[ip + 2]).tri, trivia2);
15768
+ if (build !== void 0 && gated !== void 0 && triviaLead !== void 0) {
15769
+ const classic = flags === 32 ? collapseBuildNode(child, build) : childrenOnlyCollapseBuildNode(child, build);
15770
+ return gatedPassCollapseNode(
15771
+ [
15772
+ ...gated.leads.map((g) => cc[code[g + 1]]),
15773
+ resolveClass(encodeClassSpec([
15774
+ ...triviaLead,
15775
+ ...gated.lits.map((i) => {
15776
+ const c = k[i].codePointAt(0);
15777
+ return { lo: c, hi: c };
15778
+ })
15779
+ ]))
15780
+ ],
15781
+ link(gated.last),
15782
+ classic,
15783
+ build,
15784
+ flags === 32
15785
+ );
15786
+ }
14656
15787
  if (build !== void 0 && proj < 0 && flags === 32) {
14657
15788
  return collapseBuildNode(child, build);
14658
15789
  }
@@ -14801,6 +15932,8 @@ function assemble(t2, prog, cfg) {
14801
15932
  }
14802
15933
  const notChild = scalarTerminalNotChild(code, ip);
14803
15934
  if (notChild >= 0) scalarFor(notChild);
15935
+ const peekChild = scalarTerminalPeekChild(code, ip);
15936
+ if (peekChild >= 0) scalarFor(peekChild);
14804
15937
  }
14805
15938
  const pre = cfg.hostCaptureTrivia === void 0 ? prog.asm?.find((a) => a.key === cfgKey(cfg)) : void 0;
14806
15939
  if (pre !== void 0) {
@@ -14860,7 +15993,8 @@ function assemble(t2, prog, cfg) {
14860
15993
  t2.lexPrograms,
14861
15994
  LEAD,
14862
15995
  triviaFieldCapture,
14863
- skipDeferredTrivia
15996
+ skipDeferredTrivia,
15997
+ collapseEmpty
14864
15998
  );
14865
15999
  emitReached = new Set(pre.reached);
14866
16000
  if (emitted.ext !== void 0) HYBRID = emitted;
@@ -14946,7 +16080,8 @@ function assemble(t2, prog, cfg) {
14946
16080
  t2.lexPrograms,
14947
16081
  LEAD,
14948
16082
  triviaFieldCapture,
14949
- skipDeferredTrivia
16083
+ skipDeferredTrivia,
16084
+ collapseEmpty
14950
16085
  );
14951
16086
  emitReached = em.reached;
14952
16087
  } catch (e) {
@@ -15213,7 +16348,7 @@ function emitClassPool(classes) {
15213
16348
  if (classes.length < 2) return array;
15214
16349
  const delimiter = "|/~?^`".split("").find((ch2) => classes.every((spec) => !spec.includes(ch2)));
15215
16350
  if (delimiter === void 0) return array;
15216
- const split = `${jsString(classes.join(delimiter))}.split(${jsString(delimiter)})`;
16351
+ const split = `/* @__PURE__ */ ${jsString(classes.join(delimiter))}.split(${jsString(delimiter)})`;
15217
16352
  return split.length < array.length ? split : array;
15218
16353
  }
15219
16354
  function emitConst(v) {
@@ -16820,339 +17955,85 @@ function reportDuplication(opt, analyze) {
16820
17955
  if (level === "error") throw new Error(`parseman: ${duplicationFindingCount(report)} duplication/overlap finding(s)
16821
17956
  ${lines2.join("\n")}`);
16822
17957
  for (const l of lines2) console.warn(l);
16823
- }
16824
- return report;
16825
- }
16826
- function runDuplicationDiagnostic(combinator, opt) {
16827
- return reportDuplication(opt, (o) => analyzeDuplication(combinator, o));
16828
- }
16829
- function runDuplicationDiagnosticRules(ruleMap, opt) {
16830
- return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
16831
- }
16832
-
16833
- // src/table/compile-rule-map.ts
16834
- function resolveHostMode(ruleMap, explicit) {
16835
- return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
16836
- }
16837
- function resolveTrackLines(ruleMap, explicit) {
16838
- return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
16839
- }
16840
- function withAmbient(ruleMap, opts) {
16841
- if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
16842
- const wrappers = /* @__PURE__ */ new Map();
16843
- return ruleMap.map(([name, rule2]) => {
16844
- if (rule2._meta.isTrivia) return [name, rule2];
16845
- const addTrivia = opts.trivia !== void 0 && rule2._meta.grammarTrivia === void 0;
16846
- const addScanSkip = opts.scanSkip !== void 0 && rule2._meta.grammarScanSkip === void 0;
16847
- if (!addTrivia && !addScanSkip) return [name, rule2];
16848
- let wrapped = wrappers.get(rule2);
16849
- if (wrapped === void 0) {
16850
- wrapped = {
16851
- ...rule2,
16852
- _meta: {
16853
- ...rule2._meta,
16854
- ...addTrivia ? { grammarTrivia: opts.trivia } : {},
16855
- ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
16856
- }
16857
- };
16858
- wrappers.set(rule2, wrapped);
16859
- }
16860
- return [name, wrapped];
16861
- });
16862
- }
16863
- function compileRuleMapRunnable(ruleMap, opts = {}) {
16864
- const encoded = encodeForRun(ruleMap, opts);
16865
- if (encoded === null) return null;
16866
- const { prog, hostMode, plan } = encoded;
16867
- const artifact = closureArtifact(prog);
16868
- const reflection = collectGrammarReflection(ruleMap);
16869
- return {
16870
- keys: ruleMap.map(([key]) => key),
16871
- hostMode,
16872
- hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule2]) => hasDirectBuildDef(rule2)),
16873
- reflection,
16874
- ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
16875
- // Reflection rides on the map's metadata prototype from birth. Only `compile()`
16876
- // asks to specialise; every other caller runs the closure artifact.
16877
- rules: tableRules(opts.specialise === true ? prog : artifact, { [GRAMMAR_REFLECTION]: reflection }),
16878
- prog: artifact
16879
- };
16880
- }
16881
- function encodeForRun(ruleMap, opts) {
16882
- runDuplicationDiagnosticRules(ruleMap, opts.duplication);
16883
- const encodeMap = withAmbient(ruleMap, opts);
16884
- const plan = opts.coverage === true ? buildGrammarPlan(
16885
- encodeMap.map(([, rule2]) => rule2),
16886
- Object.fromEntries(encodeMap.filter(([, rule2]) => rule2._def.tag !== "lazy"))
16887
- ) : void 0;
16888
- const hostMode = resolveHostMode(ruleMap, opts.hostMode);
16889
- const settings = {
16890
- hostMode,
16891
- ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
16892
- ...opts.recovery === true ? { recovery: true } : {},
16893
- ...plan === void 0 ? {} : { coverage: plan }
16894
- };
16895
- try {
16896
- const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
16897
- return { prog, fnSrcs, hostMode, plan };
16898
- } catch (e) {
16899
- opts.refusals?.push(e instanceof Error ? e.message : String(e));
16900
- return null;
16901
- }
16902
- }
16903
-
16904
- // src/compiler/value-usage.ts
16905
- function markUnusedValues(root) {
16906
- const seenConsumed = /* @__PURE__ */ new WeakSet();
16907
- const seenUnconsumed = /* @__PURE__ */ new WeakSet();
16908
- const mark = (def, consumed) => {
16909
- if (consumed) def.valueUnused = false;
16910
- else if (def.valueUnused !== false) def.valueUnused = true;
16911
- };
16912
- const visit = (c, consumed) => {
16913
- const seen = consumed ? seenConsumed : seenUnconsumed;
16914
- if (seen.has(c)) return;
16915
- seen.add(c);
16916
- const def = c._def;
16917
- switch (def.tag) {
16918
- case "many":
16919
- case "oneOrMore":
16920
- mark(def, consumed);
16921
- visit(def.parser, consumed);
16922
- return;
16923
- case "optional":
16924
- visit(def.parser, consumed);
16925
- return;
16926
- case "sequence":
16927
- mark(def, consumed);
16928
- for (const p of def.parsers) visit(p, consumed);
16929
- return;
16930
- case "choice":
16931
- for (const p of def.parsers) visit(p, consumed);
16932
- return;
16933
- case "dispatch":
16934
- visit(def.selector, consumed);
16935
- for (const entry of def.cases) visit(entry.parser, consumed);
16936
- if (def.matchers) for (const entry of def.matchers) visit(entry.parser, consumed);
16937
- if (def.otherwise) visit(def.otherwise, consumed);
16938
- return;
16939
- case "node":
16940
- visit(def.parser, false);
16941
- return;
16942
- case "transform":
16943
- visit(def.parser, true);
16944
- return;
16945
- case "field":
16946
- visit(def.parser, true);
16947
- return;
16948
- case "sepBy":
16949
- visit(def.parser, true);
16950
- visit(def.separator, false);
16951
- return;
16952
- case "not":
16953
- case "peek":
16954
- visit(def.parser, false);
16955
- return;
16956
- case "grammar":
16957
- visit(def.parser, consumed);
16958
- if (def.triviaParser) visit(def.triviaParser, false);
16959
- return;
16960
- case "recover":
16961
- visit(def.parser, consumed);
16962
- visit(def.sentinel, false);
16963
- return;
16964
- case "token":
16965
- visit(def.parser, false);
16966
- return;
16967
- case "label":
16968
- case "trivia":
16969
- case "attempt":
16970
- case "withCtx":
16971
- case "expect":
16972
- visit(def.parser, consumed);
16973
- return;
16974
- case "leaf":
16975
- visit(def.parser, true);
16976
- return;
16977
- case "lazy":
16978
- return;
16979
- default:
16980
- return;
16981
- }
16982
- };
16983
- const resolveRoot = (c) => {
16984
- const guard = /* @__PURE__ */ new Set();
16985
- let cur = c;
16986
- while (!guard.has(cur)) {
16987
- const d = cur._def;
16988
- if (d.tag === "grammar") {
16989
- guard.add(cur);
16990
- cur = d.parser;
16991
- continue;
16992
- }
16993
- if (d.tag !== "lazy") return cur;
16994
- guard.add(cur);
16995
- try {
16996
- cur = d.thunk();
16997
- } catch {
16998
- return c;
16999
- }
17000
- }
17001
- return cur;
17002
- };
17003
- visit(resolveRoot(root), true);
17958
+ }
17959
+ return report;
17960
+ }
17961
+ function runDuplicationDiagnostic(combinator, opt) {
17962
+ return reportDuplication(opt, (o) => analyzeDuplication(combinator, o));
17963
+ }
17964
+ function runDuplicationDiagnosticRules(ruleMap, opt) {
17965
+ return reportDuplication(opt, (o) => analyzeDuplicationRules(ruleMap, o));
17004
17966
  }
17005
17967
 
17006
- // src/combinators/grammar.ts
17007
- function annotateResultLines(result, index) {
17008
- const annotated = { ...result, span: annotateSpan(result.span, index) };
17009
- const errors = annotated.errors;
17010
- if (errors) {
17011
- ;
17012
- annotated.errors = errors.map((error) => ({
17013
- ...error,
17014
- span: annotateSpan(error.span, index)
17015
- }));
17016
- }
17017
- return annotated;
17968
+ // src/table/compile-rule-map.ts
17969
+ function resolveHostMode(ruleMap, explicit) {
17970
+ return explicit ?? ruleMap.map(([, r]) => r._meta.grammarHostMode).find(Boolean) ?? "ast";
17018
17971
  }
17019
- function createParseLineContext(input, pos) {
17020
- const lineIndex = createLineIndex();
17021
- if (pos > 0) recordLineRange(lineIndex, input, 0, pos);
17022
- return { lineIndex, lineScannedTo: pos };
17972
+ function resolveTrackLines(ruleMap, explicit) {
17973
+ return explicit === true || ruleMap.some(([, r]) => r._meta.grammarTrackLines === true);
17023
17974
  }
17024
- var TRACK_LINES_ON = () => true;
17025
- var TRACK_LINES_OFF = () => false;
17026
- var TRACK_LINES_INHERIT = (ctx) => ctx?.trackLines === true;
17027
- function parser(opts, root) {
17028
- const clearTrivia = opts.trivia === null;
17029
- const opaqueRootCapture = opts.rootCapture === "opaque";
17030
- if (opaqueRootCapture && opts.trivia === void 0) {
17031
- throw new TypeError("parser({ rootCapture: 'opaque' }) requires an explicit trivia scope.");
17032
- }
17033
- const scopeTrivia = clearTrivia ? void 0 : opts.trivia ?? void 0;
17034
- const scopeLabels = scopeTrivia?._meta?.triviaKindLabels;
17035
- const refuseUnclassified = scopeTrivia !== void 0 && !scopeTrivia._meta?.rootTriviaClassified && !opaqueRootCapture;
17036
- const forceCaptureTrivia = opts.captureTrivia === true;
17037
- const trackLinesPolicy = opts.trackLines === true ? "on" : opts.trackLines === false ? "off" : "inherit";
17038
- const trackLinesOf = trackLinesPolicy === "on" ? TRACK_LINES_ON : trackLinesPolicy === "off" ? TRACK_LINES_OFF : TRACK_LINES_INHERIT;
17039
- const captureKinds = clearTrivia ? void 0 : opts.captureTriviaKinds;
17040
- const ownCaptureMask = captureKinds === void 0 ? void 0 : scopeLabels !== void 0 ? triviaKindMask(scopeLabels, captureKinds) : void 0;
17041
- const inheritCaptureMask = captureKinds !== void 0 && scopeLabels === void 0;
17042
- const rootScalar = scalarOf(root);
17043
- const scalarEligible = opts.trackLines !== true && opts.recover !== true && !opaqueRootCapture && !forceCaptureTrivia && captureKinds === void 0;
17044
- const parseScalar = scalarEligible ? (input, pos, ctx) => {
17045
- const savedTrivia = ctx.trivia;
17046
- const savedLabels = ctx.triviaKindLabels;
17047
- if (clearTrivia) {
17048
- ctx.trivia = void 0;
17049
- ctx.triviaKindLabels = void 0;
17050
- } else if (scopeTrivia !== void 0) {
17051
- ctx.trivia = scopeTrivia;
17052
- ctx.triviaKindLabels = scopeLabels;
17053
- }
17054
- const end = rootScalar(input, pos, ctx);
17055
- ctx.trivia = savedTrivia;
17056
- ctx.triviaKindLabels = savedLabels;
17057
- return end;
17058
- } : void 0;
17059
- let publicScalar;
17060
- const grammar = {
17061
- _tag: "grammar",
17062
- _meta: {
17063
- ...root._meta,
17064
- // Trivia classification describes this grammar's own active trivia
17065
- // scope. A directly nested grammar may have classified trivia of its
17066
- // own, but that must not advertise classification for this wrapper.
17067
- triviaKindLabels: void 0,
17068
- rootTriviaClassified: void 0,
17069
- ...opts.trivia?._meta?.triviaKindLabels ? { triviaKindLabels: opts.trivia._meta.triviaKindLabels } : {},
17070
- ...opts.trivia?._meta?.rootTriviaClassified ? { rootTriviaClassified: true } : {}
17071
- },
17072
- _def: {
17073
- tag: "grammar",
17074
- parser: root,
17075
- triviaParser: scopeTrivia,
17076
- clearTrivia,
17077
- ...opaqueRootCapture ? { rootCapture: "opaque" } : {},
17078
- ...opts.captureTrivia ? { captureTrivia: true } : {},
17079
- trackLines: opts.trackLines ?? false,
17080
- constructionTrackLines: trackLinesPolicy,
17081
- ...captureKinds === void 0 ? {} : { constructionCaptureTriviaKinds: captureKinds }
17082
- },
17083
- _parseScalar: parseScalar,
17084
- parse(input, pos, _ctx) {
17085
- if (pos === void 0 && _ctx === void 0) {
17086
- publicScalar ??= scalarRootOf(grammar) ?? null;
17087
- if (publicScalar) {
17088
- const ctx2 = createParseContext();
17089
- const end = publicScalar(input, 0, ctx2);
17090
- if (end < 0) {
17091
- const at = ~end;
17092
- return { ok: false, expected: ctx2._fx ?? [], span: { start: at, end: at } };
17093
- }
17094
- return { ok: true, value: ctx2._sv, span: { start: 0, end } };
17975
+ function withAmbient(ruleMap, opts) {
17976
+ if (opts.trivia === void 0 && opts.scanSkip === void 0) return ruleMap;
17977
+ const wrappers = /* @__PURE__ */ new Map();
17978
+ return ruleMap.map(([name, rule2]) => {
17979
+ if (rule2._meta.isTrivia) return [name, rule2];
17980
+ const addTrivia = opts.trivia !== void 0 && rule2._meta.grammarTrivia === void 0;
17981
+ const addScanSkip = opts.scanSkip !== void 0 && rule2._meta.grammarScanSkip === void 0;
17982
+ if (!addTrivia && !addScanSkip) return [name, rule2];
17983
+ let wrapped = wrappers.get(rule2);
17984
+ if (wrapped === void 0) {
17985
+ wrapped = {
17986
+ ...rule2,
17987
+ _meta: {
17988
+ ...rule2._meta,
17989
+ ...addTrivia ? { grammarTrivia: opts.trivia } : {},
17990
+ ...addScanSkip ? { grammarScanSkip: opts.scanSkip } : {}
17095
17991
  }
17096
- }
17097
- if (refuseUnclassified) refuseUnclassifiedRootScope(_ctx?._rootTriviaStrictScopes);
17098
- const trackLines = trackLinesOf(_ctx);
17099
- const lineContext = trackLines && _ctx?._lineIndex === void 0 && _ctx?._lineStarts === void 0 ? createParseLineContext(input, pos ?? 0) : void 0;
17100
- const lineIndex = trackLines ? _ctx?._lineIndex ?? (_ctx?._lineStarts ? { lineStarts: _ctx._lineStarts } : lineContext.lineIndex) : void 0;
17101
- const ctx = _ctx === void 0 ? createParseContext() : { ..._ctx };
17102
- ctx.trackLines = trackLines;
17103
- if (lineIndex) ctx._lineIndex = lineIndex;
17104
- if (lineContext) ctx._lineScannedTo = lineContext.lineScannedTo;
17105
- if (clearTrivia) {
17106
- ctx.trivia = void 0;
17107
- ctx.triviaKindLabels = void 0;
17108
- } else if (scopeTrivia !== void 0) {
17109
- ctx.trivia = scopeTrivia;
17110
- if (scopeLabels) ctx.triviaKindLabels = scopeLabels;
17111
- }
17112
- if (forceCaptureTrivia || _ctx?.captureTrivia) ctx.captureTrivia = true;
17113
- if (ownCaptureMask !== void 0) ctx._triviaCaptureMask = ownCaptureMask;
17114
- else if (inheritCaptureMask) ctx._triviaCaptureMask = triviaKindMask(_ctx?.triviaKindLabels, captureKinds);
17115
- if (opaqueRootCapture) ctx._rootTriviaCapture = false;
17116
- const result = root.parse(input, pos ?? 0, ctx);
17117
- if (trackLines && _ctx && ctx._lineScannedTo !== void 0) {
17118
- _ctx._lineScannedTo = Math.max(_ctx._lineScannedTo ?? 0, ctx._lineScannedTo);
17119
- }
17120
- return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
17992
+ };
17993
+ wrappers.set(rule2, wrapped);
17121
17994
  }
17995
+ return [name, wrapped];
17996
+ });
17997
+ }
17998
+ function compileRuleMapRunnable(ruleMap, opts = {}) {
17999
+ const encoded = encodeForRun(ruleMap, opts);
18000
+ if (encoded === null) return null;
18001
+ const { prog, hostMode, plan } = encoded;
18002
+ const artifact = closureArtifact(prog);
18003
+ const reflection = collectGrammarReflection(ruleMap);
18004
+ return {
18005
+ keys: ruleMap.map(([key]) => key),
18006
+ hostMode,
18007
+ hostBranchElided: hostMode === "ast" && ruleMap.some(([, rule2]) => hasDirectBuildDef(rule2)),
18008
+ reflection,
18009
+ ...plan === void 0 ? {} : { coverageDefinitions: plan.definitions },
18010
+ // Reflection rides on the map's metadata prototype from birth. Only `compile()`
18011
+ // asks to specialise; every other caller runs the closure artifact.
18012
+ rules: tableRules(opts.specialise === true ? prog : artifact, { [GRAMMAR_REFLECTION]: reflection }),
18013
+ prog: artifact
17122
18014
  };
17123
- return grammar;
17124
18015
  }
17125
- var _analyzed = /* @__PURE__ */ new WeakSet();
17126
- function parse(combinator, input, opts = {}) {
17127
- if (!_analyzed.has(combinator)) {
17128
- _analyzed.add(combinator);
17129
- markUnusedValues(combinator);
17130
- }
17131
- if (!opts.trackLines && !opts.recover && combinator._def.tag === "grammar" && combinator._meta.grammarScanSkip === void 0) {
17132
- return combinator.parse(input);
17133
- }
17134
- const trackLines = opts.trackLines ?? combinator._meta.grammarTrackLines ?? false;
17135
- const lineContext = trackLines ? createParseLineContext(input, 0) : void 0;
17136
- const lineIndex = lineContext?.lineIndex;
17137
- const _errors = opts.recover ? [] : void 0;
17138
- const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
17139
- const grammarTrivia = combinator._meta.grammarTrivia;
17140
- const grammarScanSkip = combinator._meta.grammarScanSkip;
17141
- const ctx = createParseContext();
17142
- ctx.trackLines = trackLines;
17143
- if (lineIndex) ctx._lineIndex = lineIndex;
17144
- if (lineContext) ctx._lineScannedTo = lineContext.lineScannedTo;
17145
- if (grammarTrivia !== void 0) {
17146
- ctx.trivia = grammarTrivia;
17147
- if (grammarTrivia._meta.triviaKindLabels) ctx.triviaKindLabels = grammarTrivia._meta.triviaKindLabels;
18016
+ function encodeForRun(ruleMap, opts) {
18017
+ runDuplicationDiagnosticRules(ruleMap, opts.duplication);
18018
+ const encodeMap = withAmbient(ruleMap, opts);
18019
+ const plan = opts.coverage === true ? buildGrammarPlan(
18020
+ encodeMap.map(([, rule2]) => rule2),
18021
+ Object.fromEntries(encodeMap.filter(([, rule2]) => rule2._def.tag !== "lazy"))
18022
+ ) : void 0;
18023
+ const hostMode = resolveHostMode(ruleMap, opts.hostMode);
18024
+ const settings = {
18025
+ hostMode,
18026
+ ...resolveTrackLines(ruleMap, opts.trackLines) ? { trackLines: true } : {},
18027
+ ...opts.recovery === true ? { recovery: true } : {},
18028
+ ...plan === void 0 ? {} : { coverage: plan }
18029
+ };
18030
+ try {
18031
+ const { prog, fnSrcs } = encodeTableProgram(Object.fromEntries(encodeMap), settings);
18032
+ return { prog, fnSrcs, hostMode, plan };
18033
+ } catch (e) {
18034
+ opts.refusals?.push(e instanceof Error ? e.message : String(e));
18035
+ return null;
17148
18036
  }
17149
- if (grammarScanSkip !== void 0) ctx.scanSkip = grammarScanSkip;
17150
- if (_errors !== void 0) ctx._errors = _errors;
17151
- if (_probe !== void 0) ctx._probe = _probe;
17152
- const result = combinator.parse(input, 0, ctx);
17153
- if (!result.ok) return lineIndex ? annotateResultLines(result, normalizeLineIndex(lineIndex)) : result;
17154
- const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
17155
- return lineIndex ? annotateResultLines(withErrors, normalizeLineIndex(lineIndex)) : withErrors;
17156
18037
  }
17157
18038
 
17158
18039
  // src/compiler/linker.ts
@@ -17252,7 +18133,7 @@ function analyzeGrammarGating(grammar, opts) {
17252
18133
  }
17253
18134
 
17254
18135
  // src/analysis/diagnose.ts
17255
- var isCombinator = (g) => typeof g === "object" && g !== null && "_def" in g;
18136
+ var isCombinator2 = (g) => typeof g === "object" && g !== null && "_def" in g;
17256
18137
  var isRuleEntries = (g) => Array.isArray(g);
17257
18138
  function diagnoseGrammar(grammar, opts) {
17258
18139
  const depth = degradationCaptureDepth();
@@ -17273,7 +18154,7 @@ function examinedNothing(d) {
17273
18154
  return d.summary.totalChoices === 0 && d.summary.unanalysable > 0;
17274
18155
  }
17275
18156
  function runAnalysis(grammar, opts) {
17276
- if (isCombinator(grammar)) return analyzeGating(grammar, opts);
18157
+ if (isCombinator2(grammar)) return analyzeGating(grammar, opts);
17277
18158
  if (isRuleEntries(grammar)) return analyzeGatingRules(grammar, opts);
17278
18159
  return analyzeGrammarGating(grammar, opts);
17279
18160
  }
@@ -18032,16 +18913,16 @@ function ownSource(fn) {
18032
18913
  return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
18033
18914
  }
18034
18915
  function compile(target, second, third) {
18035
- if (!isCombinator2(target)) return compileMap(target, second);
18916
+ if (!isCombinator3(target)) return compileMap(target, second);
18036
18917
  return compileRoot(target, second, third);
18037
18918
  }
18038
- function isCombinator2(x) {
18919
+ function isCombinator3(x) {
18039
18920
  return typeof x === "object" && x !== null && typeof x.parse === "function" && "_def" in x;
18040
18921
  }
18041
18922
  function compileMap(grammar, opts = {}) {
18042
18923
  const entries = [];
18043
18924
  for (const [key, value] of Object.entries(grammar)) {
18044
- if (!isCombinator2(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
18925
+ if (!isCombinator3(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
18045
18926
  if (value._def.tag === "lazy") {
18046
18927
  try {
18047
18928
  value._def.thunk();
@@ -18355,49 +19236,6 @@ function digestValue(value, prefix = "", options) {
18355
19236
  return sha.digest("hex");
18356
19237
  }
18357
19238
 
18358
- // src/combinators/attempt.ts
18359
- function containOption(options) {
18360
- if (options === void 0) return false;
18361
- for (const key of Object.keys(options)) {
18362
- if (key !== "contain") throw new TypeError(`attempt(): unknown option ${JSON.stringify(key)}`);
18363
- }
18364
- if (options.contain !== void 0 && typeof options.contain !== "boolean") {
18365
- throw new TypeError("attempt(): `contain` must be a boolean");
18366
- }
18367
- return options.contain === true;
18368
- }
18369
- function attempt(parser2, options) {
18370
- const contain = containOption(options);
18371
- const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
18372
- let failExpected;
18373
- return {
18374
- _tag: "attempt",
18375
- _meta: parser2._meta,
18376
- _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
18377
- parse(input, pos, ctx) {
18378
- if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
18379
- if (failExpected === void 0) {
18380
- const e = deriveExpected(parser2);
18381
- failExpected = e.length > 0 ? e : [parser2._tag];
18382
- }
18383
- return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
18384
- }
18385
- const mark = saveCstMark(ctx);
18386
- const log = ctx._triviaLog?.length;
18387
- const rootLog = ctx._rootTriviaLog?.length;
18388
- const split = ctx._columnsSplit;
18389
- const result = parser2.parse(input, pos, ctx);
18390
- if (result.ok) return result;
18391
- rollbackCstCapture(ctx, mark);
18392
- ctx._columnsSplit = split;
18393
- if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
18394
- if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
18395
- if (result.committed && !contain) return result;
18396
- return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
18397
- }
18398
- };
18399
- }
18400
-
18401
19239
  // src/combinators/not.ts
18402
19240
  function not(combinator) {
18403
19241
  const meta = {