parseman 0.57.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/analysis/diagnostics.js +1799 -962
  2. package/dist/analysis/diagnostics.js.map +3 -3
  3. package/dist/cli/index.js +1928 -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 +1016 -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 +1209 -236
  17. package/dist/plugin/index.js.map +3 -3
  18. package/dist/table/assemble.d.ts.map +1 -1
  19. package/dist/table/compile-rule-map.d.ts.map +1 -1
  20. package/dist/table/emit-assembly.d.ts +1 -1
  21. package/dist/table/emit-assembly.d.ts.map +1 -1
  22. package/dist/table/emit.d.ts.map +1 -1
  23. package/dist/table/encode.d.ts.map +1 -1
  24. package/dist/table/exec.d.ts.map +1 -1
  25. package/dist/table/factor-choice.d.ts +42 -0
  26. package/dist/table/factor-choice.d.ts.map +1 -0
  27. package/dist/table/fail-clean.d.ts +3 -0
  28. package/dist/table/fail-clean.d.ts.map +1 -0
  29. package/dist/table/hold-gap.d.ts +8 -0
  30. package/dist/table/hold-gap.d.ts.map +1 -0
  31. package/dist/table/index.js +1369 -259
  32. package/dist/table/index.js.map +3 -3
  33. package/dist/table/lex-program.d.ts +7 -3
  34. package/dist/table/lex-program.d.ts.map +1 -1
  35. package/dist/table/ops.d.ts +8 -0
  36. package/dist/table/ops.d.ts.map +1 -1
  37. package/dist/table/run-support.d.ts +19 -0
  38. package/dist/table/run-support.d.ts.map +1 -1
  39. package/dist/table/scalar-terminal.d.ts +2 -0
  40. package/dist/table/scalar-terminal.d.ts.map +1 -1
  41. package/dist/table/site-labels.d.ts +66 -0
  42. package/dist/table/site-labels.d.ts.map +1 -1
  43. package/dist/version.d.ts +1 -1
  44. package/package.json +1 -1
  45. package/src/combinators/dispatch.ts +12 -3
  46. package/src/compiler/token-alphabet.ts +57 -17
  47. package/src/cst/capture-buffer.ts +25 -0
  48. package/src/plugin/index.ts +230 -35
  49. package/src/table/assemble.ts +252 -62
  50. package/src/table/child-slots.ts +2 -2
  51. package/src/table/compile-rule-map.ts +14 -5
  52. package/src/table/emit-assembly.ts +112 -23
  53. package/src/table/emit.ts +3 -1
  54. package/src/table/encode.ts +80 -14
  55. package/src/table/exec.ts +27 -8
  56. package/src/table/factor-choice.ts +173 -0
  57. package/src/table/fail-clean.ts +130 -0
  58. package/src/table/hold-gap.ts +68 -0
  59. package/src/table/lex-program.ts +197 -43
  60. package/src/table/ops.ts +9 -1
  61. package/src/table/run-support.ts +35 -0
  62. package/src/table/scalar-terminal.ts +8 -0
  63. package/src/table/site-labels.ts +135 -4
  64. package/src/version.ts +1 -1
@@ -478,6 +478,31 @@ function pushCstChild(ctx, built, rawEntry2) {
478
478
  else if (ctx._cstLeaves) ctx._cstLeaves.push(built);
479
479
  if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry2);
480
480
  }
481
+ function popCstChild(ctx) {
482
+ const b = ctx._cstBuf;
483
+ if (b) {
484
+ const ch2 = b.ch;
485
+ if (b.rawOnly !== true && ch2) {
486
+ ch2.pop();
487
+ if (ch2.length === 1) {
488
+ b.single = ch2[0];
489
+ b.ch = void 0;
490
+ }
491
+ }
492
+ if (b.noRaw === true) b.rawLen = (b.rawLen ?? 1) - 1;
493
+ else if (b.raw) {
494
+ b.raw.pop();
495
+ if (b.raw.length === 1) {
496
+ b.rawSingle = b.raw[0];
497
+ b.raw = void 0;
498
+ }
499
+ } else b.rawSingle = void 0;
500
+ return;
501
+ }
502
+ if (ctx._cstChildren) ctx._cstChildren.pop();
503
+ else if (ctx._cstLeaves) ctx._cstLeaves.pop();
504
+ if (ctx._cstRawChildren) ctx._cstRawChildren.pop();
505
+ }
481
506
  function cstRawLen(ctx) {
482
507
  const b = ctx._cstBuf;
483
508
  if (b) {
@@ -2346,6 +2371,9 @@ function parser(opts, root) {
2346
2371
  };
2347
2372
  return grammar;
2348
2373
  }
2374
+ function noTrivia(root) {
2375
+ return parser({ trivia: null }, root);
2376
+ }
2349
2377
  var _analyzed = /* @__PURE__ */ new WeakSet();
2350
2378
  function parse(combinator, input, opts = {}) {
2351
2379
  if (!_analyzed.has(combinator)) {
@@ -3840,17 +3868,59 @@ function buildLexBodyRecognizer(spec, constants) {
3840
3868
  };
3841
3869
  }
3842
3870
 
3871
+ // src/table/run-support.ts
3872
+ function lineCol(ctx, offset) {
3873
+ const starts = ctx._lineStarts ?? [0];
3874
+ let lo = 0, hi = starts.length - 1;
3875
+ while (lo < hi) {
3876
+ const mid = lo + hi + 1 >> 1;
3877
+ if (starts[mid] <= offset) lo = mid;
3878
+ else hi = mid - 1;
3879
+ }
3880
+ return [lo + 1, offset - starts[lo] + 1];
3881
+ }
3882
+ function spanLines(ctx, start, end) {
3883
+ const s = lineCol(ctx, start), e = lineCol(ctx, end);
3884
+ return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] };
3885
+ }
3886
+ function rawEntry(v, input, s, e) {
3887
+ if (typeof v === "object" && v !== null) {
3888
+ const tg = v._tag;
3889
+ if (tg === "node" || tg === "leaf" || tg === "parseError") return v;
3890
+ }
3891
+ return {
3892
+ _tag: "leaf",
3893
+ value: typeof v === "string" ? v : typeof v === "object" && v !== null ? input.slice(s, e) : "",
3894
+ span: { start: s, end: e }
3895
+ };
3896
+ }
3897
+ function lead(input, pos) {
3898
+ if (pos >= input.length) return -1;
3899
+ const c = input.charCodeAt(pos);
3900
+ if (c < 55296 || c > 56319) return c;
3901
+ return input.codePointAt(pos) ?? c;
3902
+ }
3903
+ var EMPTY_KIDS = Object.freeze([]);
3904
+ var EMPTY_TRIVIA = Object.freeze([]);
3905
+ function collapseEmpty(ctx, build, input, pos, end, readsRaw) {
3906
+ const collected = ctx._cstBuf !== void 0 || ctx._cstChildren !== void 0;
3907
+ if (collected) popCstChild(ctx);
3908
+ const raw = readsRaw ? [rawEntry(void 0, input, pos, end)] : EMPTY_KIDS;
3909
+ const nd = build(EMPTY_KIDS, void 0, { start: pos, end }, raw, EMPTY_TRIVIA, void 0);
3910
+ if (collected) pushCstChild(ctx, nd, ctx._cstBuf?.noRaw === true ? void 0 : rawEntry(nd, input, pos, end));
3911
+ return nd;
3912
+ }
3913
+
3843
3914
  // src/table/lex-program.ts
3844
3915
  var LEX_NODE_TERMINAL = 0;
3845
3916
  var LEX_NODE_SEQUENCE2 = 1;
3846
3917
  var LEX_NODE_SEQUENCE3 = 2;
3847
3918
  var LEX_NODE_SEQUENCE4 = 3;
3848
3919
  var LEX_NODE_SEQUENCE5 = 4;
3849
- var LEX_NODE_CHOICE2 = 5;
3850
- var LEX_NODE_CHOICE4 = 6;
3851
- var LEX_NODE_CHOICE8 = 7;
3920
+ var LEX_NODE_CHOICE = 5;
3852
3921
  var LEX_NODE_NOT = 8;
3853
3922
  var LEX_NODE_OPTIONAL = 9;
3923
+ var LEX_NODE_REPEAT = 10;
3854
3924
  function lexProgramDigest(words) {
3855
3925
  let hash = 2166136261;
3856
3926
  for (const word of words) {
@@ -3879,85 +3949,114 @@ function trackLexLines(ctx, input, end) {
3879
3949
  ctx._lineScannedTo = end;
3880
3950
  }
3881
3951
  function terminalNode(matcher, failure, tracksLines) {
3882
- const match = (input, pos) => {
3883
- const result = matcher(input, pos);
3884
- return result < 0 ? -1 : result / 2;
3885
- };
3886
3952
  return {
3887
- match,
3953
+ match: (input, pos) => {
3954
+ const result = matcher(input, pos);
3955
+ return result < 0 ? -1 : result / 2;
3956
+ },
3888
3957
  run: (input, pos, ctx) => {
3889
- const end = match(input, pos);
3890
- if (end < 0) {
3958
+ const result = matcher(input, pos);
3959
+ if (result < 0) {
3891
3960
  ctx._fe = pos;
3892
3961
  ctx._fx = failure;
3893
3962
  if (ctx._probe !== void 0) failAt(ctx, ctx._fx, pos);
3894
3963
  return -1;
3895
3964
  }
3965
+ const end = result / 2;
3896
3966
  if (tracksLines) trackLexLines(ctx, input, end);
3897
3967
  return end;
3898
3968
  }
3899
3969
  };
3900
3970
  }
3901
3971
  function sequence2(a, b) {
3972
+ const am = a.match, bm = b.match, ar = a.run, br = b.run;
3902
3973
  return {
3903
3974
  match: (input, pos) => {
3904
- const e0 = a.match(input, pos);
3905
- return e0 < 0 ? -1 : b.match(input, e0);
3975
+ const e0 = am(input, pos);
3976
+ return e0 < 0 ? -1 : bm(input, e0);
3906
3977
  },
3907
3978
  run: (input, pos, ctx) => {
3908
- const e0 = a.run(input, pos, ctx);
3979
+ const e0 = ar(input, pos, ctx);
3909
3980
  if (e0 < 0) return -1;
3910
3981
  ctx._fc = false;
3911
- return b.run(input, e0, ctx);
3982
+ return br(input, e0, ctx);
3912
3983
  }
3913
3984
  };
3914
3985
  }
3915
3986
  function sequence3(a, b, c) {
3987
+ const am = a.match, bm = b.match, cm = c.match, ar = a.run, br = b.run, cr = c.run;
3916
3988
  return {
3917
3989
  match: (input, pos) => {
3918
- const e0 = a.match(input, pos);
3990
+ const e0 = am(input, pos);
3919
3991
  if (e0 < 0) return -1;
3920
- const e1 = b.match(input, e0);
3921
- return e1 < 0 ? -1 : c.match(input, e1);
3992
+ const e1 = bm(input, e0);
3993
+ return e1 < 0 ? -1 : cm(input, e1);
3922
3994
  },
3923
3995
  run: (input, pos, ctx) => {
3924
- const e0 = a.run(input, pos, ctx);
3996
+ const e0 = ar(input, pos, ctx);
3925
3997
  if (e0 < 0) return -1;
3926
3998
  ctx._fc = false;
3927
- const e1 = b.run(input, e0, ctx);
3999
+ const e1 = br(input, e0, ctx);
3928
4000
  if (e1 < 0) return -1;
3929
4001
  ctx._fc = false;
3930
- return c.run(input, e1, ctx);
4002
+ return cr(input, e1, ctx);
3931
4003
  }
3932
4004
  };
3933
4005
  }
3934
4006
  function sequence4(a, b, c, d) {
3935
- const abc = sequence3(a, b, c);
4007
+ const am = a.match, bm = b.match, cm = c.match, dm = d.match;
4008
+ const ar = a.run, br = b.run, cr = c.run, dr = d.run;
3936
4009
  return {
3937
4010
  match: (input, pos) => {
3938
- const end = abc.match(input, pos);
3939
- return end < 0 ? -1 : d.match(input, end);
4011
+ const e0 = am(input, pos);
4012
+ if (e0 < 0) return -1;
4013
+ const e1 = bm(input, e0);
4014
+ if (e1 < 0) return -1;
4015
+ const e2 = cm(input, e1);
4016
+ return e2 < 0 ? -1 : dm(input, e2);
3940
4017
  },
3941
4018
  run: (input, pos, ctx) => {
3942
- const end = abc.run(input, pos, ctx);
3943
- if (end < 0) return -1;
4019
+ const e0 = ar(input, pos, ctx);
4020
+ if (e0 < 0) return -1;
4021
+ ctx._fc = false;
4022
+ const e1 = br(input, e0, ctx);
4023
+ if (e1 < 0) return -1;
4024
+ ctx._fc = false;
4025
+ const e2 = cr(input, e1, ctx);
4026
+ if (e2 < 0) return -1;
3944
4027
  ctx._fc = false;
3945
- return d.run(input, end, ctx);
4028
+ return dr(input, e2, ctx);
3946
4029
  }
3947
4030
  };
3948
4031
  }
3949
4032
  function sequence5(a, b, c, d, e) {
3950
- const abcd = sequence4(a, b, c, d);
4033
+ const am = a.match, bm = b.match, cm = c.match, dm = d.match, em = e.match;
4034
+ const ar = a.run, br = b.run, cr = c.run, dr = d.run, er = e.run;
3951
4035
  return {
3952
4036
  match: (input, pos) => {
3953
- const end = abcd.match(input, pos);
3954
- return end < 0 ? -1 : e.match(input, end);
4037
+ const e0 = am(input, pos);
4038
+ if (e0 < 0) return -1;
4039
+ const e1 = bm(input, e0);
4040
+ if (e1 < 0) return -1;
4041
+ const e2 = cm(input, e1);
4042
+ if (e2 < 0) return -1;
4043
+ const e3 = dm(input, e2);
4044
+ return e3 < 0 ? -1 : em(input, e3);
3955
4045
  },
3956
4046
  run: (input, pos, ctx) => {
3957
- const end = abcd.run(input, pos, ctx);
3958
- if (end < 0) return -1;
4047
+ const e0 = ar(input, pos, ctx);
4048
+ if (e0 < 0) return -1;
4049
+ ctx._fc = false;
4050
+ const e1 = br(input, e0, ctx);
4051
+ if (e1 < 0) return -1;
4052
+ ctx._fc = false;
4053
+ const e2 = cr(input, e1, ctx);
4054
+ if (e2 < 0) return -1;
3959
4055
  ctx._fc = false;
3960
- return e.run(input, end, ctx);
4056
+ const e3 = dr(input, e2, ctx);
4057
+ if (e3 < 0) return -1;
4058
+ ctx._fc = false;
4059
+ return er(input, e3, ctx);
3961
4060
  }
3962
4061
  };
3963
4062
  }
@@ -3970,9 +4069,9 @@ function appendExpected(acc, values) {
3970
4069
  function choiceNode(arms, classes, armExpected, choiceExpected) {
3971
4070
  let matchTail = () => -1;
3972
4071
  for (let i = arms.length - 1; i >= 0; i--) {
3973
- const arm = arms[i], next = matchTail;
4072
+ const armMatch = arms[i].match, next = matchTail;
3974
4073
  matchTail = (input, pos) => {
3975
- const end = arm.match(input, pos);
4074
+ const end = armMatch(input, pos);
3976
4075
  return end < 0 ? next(input, pos) : end;
3977
4076
  };
3978
4077
  }
@@ -3982,7 +4081,7 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
3982
4081
  return -1;
3983
4082
  };
3984
4083
  for (let i = arms.length - 1; i >= 0; i--) {
3985
- const arm = arms[i], cls = classes[i], staticExpected = armExpected[i], next = tail;
4084
+ const armRun = arms[i].run, cls = classes[i], staticExpected = armExpected[i], next = tail;
3986
4085
  tail = (input, pos, ctx, lead2, acc, best) => {
3987
4086
  if (cls !== void 0 && !classHas(cls, lead2)) {
3988
4087
  return next(
@@ -3995,7 +4094,7 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
3995
4094
  );
3996
4095
  }
3997
4096
  ctx._fc = false;
3998
- const end = arm.run(input, pos, ctx);
4097
+ const end = armRun(input, pos, ctx);
3999
4098
  if (end >= 0) return end;
4000
4099
  const at = ctx._fe ?? pos;
4001
4100
  if (at > best) {
@@ -4022,11 +4121,58 @@ function choiceNode(arms, classes, armExpected, choiceExpected) {
4022
4121
  )
4023
4122
  };
4024
4123
  }
4124
+ function exclusiveChoiceNode(arms, classes, choiceFx) {
4125
+ const ascii = (classes.length <= 128 ? new Int8Array(128) : new Int32Array(128)).fill(-1);
4126
+ const hi = [];
4127
+ for (let i = 0; i < classes.length; i++) {
4128
+ const cls = classes[i];
4129
+ for (let c = 0; c < 128; c++) if (cls.ascii[c] === 1) ascii[c] = i;
4130
+ for (let r = 0; r < cls.hi.length; r += 2) hi.push(cls.hi[r], cls.hi[r + 1], i);
4131
+ }
4132
+ const select = (code) => {
4133
+ if (code < 0) return -1;
4134
+ if (code < 128) return ascii[code];
4135
+ for (let r = 0; r < hi.length; r += 3) if (code >= hi[r] && code <= hi[r + 1]) return hi[r + 2];
4136
+ return -1;
4137
+ };
4138
+ let matchArm = () => -1;
4139
+ let selectRun = () => -1;
4140
+ for (let i = arms.length - 1; i >= 0; i--) {
4141
+ const armMatch = arms[i].match, armRun = arms[i].run, nextMatch = matchArm, nextRun = selectRun;
4142
+ matchArm = (selected, input, pos) => selected === i ? armMatch(input, pos) : nextMatch(selected, input, pos);
4143
+ selectRun = (selected, input, pos, ctx) => selected === i ? armRun(input, pos, ctx) : nextRun(selected, input, pos, ctx);
4144
+ }
4145
+ const runArm = (input, pos, ctx) => {
4146
+ const arm = select(lead(input, pos));
4147
+ if (arm >= 0) {
4148
+ ctx._fc = false;
4149
+ const end = selectRun(arm, input, pos, ctx);
4150
+ if (end >= 0) return end;
4151
+ if (committed(ctx)) return -1;
4152
+ const failed = ctx._fx;
4153
+ if (failed !== void 0 && failed.length > 0) {
4154
+ ctx._fe = pos;
4155
+ return -1;
4156
+ }
4157
+ }
4158
+ ctx._fe = pos;
4159
+ ctx._fx = choiceFx;
4160
+ return -1;
4161
+ };
4162
+ return {
4163
+ match: (input, pos) => {
4164
+ const arm = select(lead(input, pos));
4165
+ return arm < 0 ? -1 : matchArm(arm, input, pos);
4166
+ },
4167
+ run: runArm
4168
+ };
4169
+ }
4025
4170
  function notNode(body, failure) {
4171
+ const bodyMatch = body.match;
4026
4172
  return {
4027
- match: (input, pos) => body.match(input, pos) < 0 ? pos : -1,
4173
+ match: (input, pos) => bodyMatch(input, pos) < 0 ? pos : -1,
4028
4174
  run: (input, pos, ctx) => {
4029
- if (body.match(input, pos) < 0) return pos;
4175
+ if (bodyMatch(input, pos) < 0) return pos;
4030
4176
  ctx._fe = pos;
4031
4177
  ctx._fx = failure;
4032
4178
  return -1;
@@ -4034,18 +4180,57 @@ function notNode(body, failure) {
4034
4180
  };
4035
4181
  }
4036
4182
  function optionalNode(body) {
4183
+ const bodyMatch = body.match, bodyRun = body.run;
4037
4184
  return {
4038
4185
  match: (input, pos) => {
4039
- const end = body.match(input, pos);
4186
+ const end = bodyMatch(input, pos);
4040
4187
  return end < 0 ? pos : end;
4041
4188
  },
4042
4189
  run: (input, pos, ctx) => {
4043
4190
  ctx._fc = false;
4044
- const end = body.run(input, pos, ctx);
4191
+ const end = bodyRun(input, pos, ctx);
4045
4192
  return end < 0 && !committed(ctx) ? pos : end;
4046
4193
  }
4047
4194
  };
4048
4195
  }
4196
+ function repeatNode(item, min, max) {
4197
+ const itemMatch = item.match, itemRun = item.run;
4198
+ return {
4199
+ match: (input, pos) => {
4200
+ let cur = pos;
4201
+ let count = 0;
4202
+ for (; ; ) {
4203
+ if (max >= 0 && count >= max) break;
4204
+ const optional2 = count >= min && (count > 0 || min === 0);
4205
+ if (optional2 && cur >= input.length) break;
4206
+ const end = itemMatch(input, cur);
4207
+ if (end < 0 || optional2 && end === cur) break;
4208
+ cur = end;
4209
+ count++;
4210
+ }
4211
+ return count < min ? -1 : cur;
4212
+ },
4213
+ run: (input, pos, ctx) => {
4214
+ let cur = pos;
4215
+ let count = 0;
4216
+ for (; ; ) {
4217
+ if (max >= 0 && count >= max) break;
4218
+ const optional2 = count >= min && (count > 0 || min === 0);
4219
+ if (optional2 && cur >= input.length) break;
4220
+ ctx._fc = false;
4221
+ const end = itemRun(input, cur, ctx);
4222
+ if (end < 0) {
4223
+ if (committed(ctx)) return -1;
4224
+ break;
4225
+ }
4226
+ if (optional2 && end === cur) break;
4227
+ cur = end;
4228
+ count++;
4229
+ }
4230
+ return count < min ? -1 : cur;
4231
+ }
4232
+ };
4233
+ }
4049
4234
  function fixedTreeRunner(words, matchers, classes, expected) {
4050
4235
  const stack = [];
4051
4236
  const pop = () => {
@@ -4073,9 +4258,14 @@ function fixedTreeRunner(words, matchers, classes, expected) {
4073
4258
  else stack.push(sequence5(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4]));
4074
4259
  continue;
4075
4260
  }
4076
- if (op >= LEX_NODE_CHOICE2 && op <= LEX_NODE_CHOICE8) {
4077
- const count = op === LEX_NODE_CHOICE2 ? 2 : op === LEX_NODE_CHOICE4 ? 4 : 8;
4261
+ if (op === LEX_NODE_CHOICE) {
4262
+ const count = words[at++];
4263
+ const exclusive = words[at++];
4264
+ if (exclusive !== 0 && exclusive !== 1) throw new TypeError("table lexical tree choice has an invalid dispatch mode");
4078
4265
  const choiceFx = expected[words[at++]];
4266
+ if (!Number.isInteger(count) || count < 2 || count > stack.length) {
4267
+ throw new TypeError("table lexical tree choice has an invalid arm count");
4268
+ }
4079
4269
  if (choiceFx === void 0) throw new TypeError("table lexical tree choice has an invalid expected set");
4080
4270
  const armClasses = [];
4081
4271
  const armFx = [];
@@ -4088,7 +4278,7 @@ function fixedTreeRunner(words, matchers, classes, expected) {
4088
4278
  armFx.push(fx);
4089
4279
  }
4090
4280
  const arms = Array.from({ length: count }, pop).reverse();
4091
- stack.push(choiceNode(arms, armClasses, armFx, choiceFx));
4281
+ stack.push(exclusive === 1 ? exclusiveChoiceNode(arms, armClasses, choiceFx) : choiceNode(arms, armClasses, armFx, choiceFx));
4092
4282
  continue;
4093
4283
  }
4094
4284
  if (op === LEX_NODE_NOT) {
@@ -4101,6 +4291,14 @@ function fixedTreeRunner(words, matchers, classes, expected) {
4101
4291
  stack.push(optionalNode(pop()));
4102
4292
  continue;
4103
4293
  }
4294
+ if (op === LEX_NODE_REPEAT) {
4295
+ const min = words[at++], max = words[at++];
4296
+ if (!Number.isInteger(min) || min < 0 || !Number.isInteger(max) || max >= 0 && max < min) {
4297
+ throw new TypeError("table lexical tree repeat has invalid bounds");
4298
+ }
4299
+ stack.push(repeatNode(pop(), min, max));
4300
+ continue;
4301
+ }
4104
4302
  throw new TypeError("table lexical tree has an unknown fixed body");
4105
4303
  }
4106
4304
  if (stack.length !== 1) throw new TypeError("table lexical tree does not resolve to one root body");
@@ -4469,143 +4667,764 @@ function resolveTable(prog) {
4469
4667
  return built;
4470
4668
  }
4471
4669
 
4472
- // src/analysis/gating.ts
4473
- var choiceArms = (c) => c.arms;
4474
- function childrenOf(d) {
4475
- switch (d.tag) {
4476
- case "sequence":
4477
- case "choice":
4478
- return d.parsers;
4479
- case "dispatch":
4480
- return [
4481
- d.selector,
4482
- ...d.cases.map((c) => c.parser),
4483
- ...d.matchers ? d.matchers.map((c) => c.parser) : [],
4484
- ...d.otherwise === void 0 ? [] : [d.otherwise]
4485
- ];
4486
- case "sepBy":
4487
- return [d.parser, d.separator];
4488
- case "recover":
4489
- return [d.parser, d.sentinel];
4490
- case "scanTo":
4491
- return [
4492
- d.sentinel,
4493
- ...d.skip,
4494
- ...d.recoverAt === void 0 ? [] : [d.recoverAt],
4495
- ...d.stopAt === void 0 ? [] : [d.stopAt]
4496
- ];
4497
- case "grammar":
4498
- return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
4499
- case "routed":
4500
- return d.fallback ? [d.fallback] : [];
4501
- case "lines":
4502
- return [d.prefix, d.body, ...d.first === void 0 ? [] : [d.first], ...d.lazy === void 0 ? [] : [d.lazy]];
4503
- case "columns":
4504
- return [];
4505
- // A `lazy` is a REFERENCE, not a subtree. Descending through it would make
4506
- // every rule's walk cover the whole reachable grammar — site paths become
4507
- // nonsense, and a structural hash includes half the grammar. Treated as a
4508
- // leaf, keyed by the rule name it refers to, which is also the right
4509
- // semantics: two productions referencing `g.Ident` really do fill that slot
4510
- // the same way.
4511
- case "lazy":
4512
- case "literal":
4513
- case "regex":
4514
- case "keywords":
4515
- case "guard":
4516
- case "adjacency":
4517
- case "unknown":
4518
- return [];
4519
- default: {
4520
- const rec = d;
4521
- return rec.parser ? [rec.parser] : [];
4522
- }
4523
- }
4670
+ // src/combinators/adjacency.ts
4671
+ function adjacencyOf(p) {
4672
+ return p._def.tag === "adjacency" ? p._def : null;
4524
4673
  }
4525
- var isAny = (fs) => fs.kind === "any";
4526
- function intersection(a, b) {
4527
- if (a.kind === "any") return b;
4528
- if (b.kind === "any") return a;
4529
- if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
4530
- const ranges = [];
4531
- for (const ra of a.ranges) for (const rb of b.ranges) {
4532
- const lo = Math.max(ra.lo, rb.lo);
4533
- const hi = Math.min(ra.hi, rb.hi);
4534
- if (lo <= hi) ranges.push({ lo, hi });
4535
- }
4536
- return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
4674
+ function adjacencyExpected(def) {
4675
+ if (def.polarity === "adjacent") return "adjacent";
4676
+ return def.kinds === void 0 ? "notAdjacent" : `notAdjacent(${def.kinds.join("|")})`;
4537
4677
  }
4538
- function combine(sets) {
4539
- let acc = { kind: "empty" };
4540
- for (const s of sets) {
4541
- if (s.kind === "any") return { kind: "any" };
4542
- if (s.kind === "empty") continue;
4543
- acc = acc.kind === "ranges" ? { kind: "ranges", ranges: [...acc.ranges, ...s.ranges] } : s;
4544
- }
4545
- return acc;
4678
+ function holdsAdjacency(input, cur, ctx, def) {
4679
+ return adjacencyHolds(input, cur, ctx, def.polarity === "notAdjacent", def.kinds);
4546
4680
  }
4547
- var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
4548
- function firstSetToString(fs) {
4549
- if (fs.kind === "any") return "ANY";
4550
- if (fs.kind === "empty") return "(empty)";
4551
- return fs.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
4681
+ function adjacencyHolds(input, cur, ctx, negated, kinds) {
4682
+ const end = probeTriviaEnd(input, cur, ctx);
4683
+ if (!negated) return end === cur;
4684
+ if (end === cur) return false;
4685
+ if (kinds === void 0) return true;
4686
+ const want = resolveAdjacencyKindMask(ctx.trivia, kinds);
4687
+ const spec = analyzeLabeledTrivia(ctx.trivia);
4688
+ return (triviaKindMaskAt(input, cur, spec, ctx.state) & want) !== 0;
4552
4689
  }
4553
- var ruleNameOf = (p) => p._ruleName;
4554
- var KEYWORD_REGEX_RE = /^\^?[@#.-]?[A-Za-z][\w-]*(\(\?![^)]*\))?\$?$/;
4555
- var SUGGESTIONS = {
4556
- "broad-recognizer": "This arm can begin with any character, so no single-character test can rule it out. The parser has to enter it \u2014 set up, try, and undo \u2014 at every position it reaches, instead of skipping it for free.\nTo fix: make the arm begin with a fixed character, word or keyword. If it is meant to be a catch-all that matches anything, leave it and add the choice to the accept list at the end.",
4557
- "leading-not": "This arm begins with a not(...) check, which matches nothing and so says nothing about which character the arm starts with. The parser then cannot rule the arm out and enters it everywhere.\nTo fix: put the term that actually consumes text first and keep not(...) after it as a trailing boundary. If you need to REQUIRE something ahead without consuming it, use peek(X) \u2014 it checks the same thing but still tells parseman that the arm starts with X.",
4558
- "nullable-prefix": "This arm begins with something optional \u2014 an optional(...) or a repeat that allows zero items \u2014 so the arm can also start with whatever comes AFTER it, and that is broad enough that no character rules the arm out.\nTo fix: give the optional part its own arm so each arm has a definite beginning, or require at least one item. A plain sepBy(item, sep) also matches nothing at all; pass { min: 1 } when the list must not be empty.",
4559
- "cross-artifact-ref": "This arm hands off to another rule, and that rule has the same problem \u2014 it can begin with any character \u2014 so the cost is inherited rather than caused here.\nTo fix: run this check on the rule it refers to and fix it there. One rule given a definite beginning fixes every choice that uses it.",
4560
- "opaque-wrapper": "This arm begins with a wrapper that only inspects parser state (a gate/withCtx/recover) and consumes no text, so it says nothing about which character the arm starts with.\nTo fix: put a term that actually matches text in front of the wrapper.",
4561
- "ref-cycle": "This arm refers back to itself through a cycle of rules, and parseman could not work out a definite first character for any of them.\nTo fix: make sure the base case of the recursion begins with a fixed character or keyword, so the cycle has somewhere definite to start."
4562
- };
4563
- function refineSuggestion(cause, arm) {
4564
- if (cause !== "broad-recognizer") return SUGGESTIONS[cause];
4565
- const lead2 = peelToLeading(arm);
4566
- const d = lead2._def;
4567
- if (d.tag === "regex" && KEYWORD_REGEX_RE.test(d.source)) {
4568
- return `This arm matches the fixed word \`${d.source}\` with a regular expression, and parseman cannot always tell from a regular expression which character it starts with.
4569
- To fix: write it as word('\u2026') or keywords([\u2026]) instead. They match exactly the same text and compile to the same character scan, but they also tell parseman the first character, which is what lets the parser skip this arm when it cannot match.`;
4570
- }
4571
- if (d.tag === "scanTo") {
4572
- return "This arm ends in a scanTo(...) catch-all, which reads forward until it finds something and so can begin at any character. The parser can never rule it out. That is usually exactly what you want from a fallback.\nTo fix: usually nothing. Add this choice to the accept list at the end so the check keeps flagging the choices that are real problems.";
4573
- }
4574
- return SUGGESTIONS[cause];
4690
+ function adjacencyFail(pos, def) {
4691
+ return { ok: false, expected: [adjacencyExpected(def)], span: { start: pos, end: pos } };
4575
4692
  }
4576
- function classifyBroadArm(arm, resolve) {
4577
- const seen = /* @__PURE__ */ new Set();
4578
- const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
4579
- const walk = (p) => {
4580
- if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
4581
- seen.add(p);
4582
- const d = p._def;
4583
- switch (d.tag) {
4584
- case "literal":
4585
- case "regex":
4586
- case "keywords":
4587
- return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
4588
- case "not":
4589
- return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
4590
- // peek() CARRIES its body's first-set, so it is only broad when the body is
4591
- // (or the body is nullable) — walk in for the real cause.
4592
- case "peek":
4593
- return walk(d.parser);
4594
- case "scanTo":
4595
- return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
4596
- // A run opens with its first line's prefix.
4597
- case "lines":
4598
- return walk(d.first ?? d.prefix);
4599
- // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
4600
- // its own — it is the poison. `withCtx`/`recover` FORWARD their inner
4601
- // first-set (see firstSetOf), so a broad result comes from the inner parser:
4602
- // walk into it for a precise cause.
4603
- case "guard":
4604
- return { cause: "opaque-wrapper", detail: `opaque wrapper (${d.tag})` };
4605
- // Zero-width and dropped from a sequence's first-set (isZeroWidthAssertion),
4606
- // so it is never the reason an arm is broad — but it can be reached as the
4607
- // sole term of a degenerate arm. Report it precisely rather than as a wrapper.
4608
- case "adjacency":
4693
+ 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.";
4694
+ function adjacencyMisuse(polarity) {
4695
+ return new TypeError(`${polarity}(): ${NO_STANDALONE_PARSE}`);
4696
+ }
4697
+ function adjacencyCombinator(def) {
4698
+ const meta = {
4699
+ // Zero-width and never leading (sequence() rejects index 0), so it contributes
4700
+ // no first char. `isZeroWidthAssertion` drops it from a sequence's first-set
4701
+ // rather than letting `any` poison the enclosing choice's dispatch.
4702
+ firstSet: { kind: "any" },
4703
+ canMatchNewline: false,
4704
+ isTrivia: false
4705
+ };
4706
+ return {
4707
+ _tag: "adjacency",
4708
+ _meta: meta,
4709
+ _def: def,
4710
+ parse() {
4711
+ throw adjacencyMisuse(def.polarity);
4712
+ }
4713
+ };
4714
+ }
4715
+ function adjacent() {
4716
+ return adjacencyCombinator({ tag: "adjacency", polarity: "adjacent" });
4717
+ }
4718
+ function notAdjacent(options) {
4719
+ const kinds = options?.kinds;
4720
+ if (kinds !== void 0) {
4721
+ if (!Array.isArray(kinds) || kinds.length === 0) {
4722
+ throw new TypeError("notAdjacent({ kinds }): kinds must be a non-empty array of trivia category names.");
4723
+ }
4724
+ for (const k of kinds) {
4725
+ if (typeof k !== "string" || k.length === 0) {
4726
+ throw new TypeError(`notAdjacent({ kinds }): every kind must be a non-empty string, got ${JSON.stringify(k)}.`);
4727
+ }
4728
+ }
4729
+ }
4730
+ return adjacencyCombinator(
4731
+ kinds === void 0 ? { tag: "adjacency", polarity: "notAdjacent" } : { tag: "adjacency", polarity: "notAdjacent", kinds: [...kinds] }
4732
+ );
4733
+ }
4734
+
4735
+ // src/combinators/sequence.ts
4736
+ function sequence(...parsers) {
4737
+ let adjacency = null;
4738
+ for (let i = 0; i < parsers.length; i++) {
4739
+ const a = adjacencyOf(parsers[i]);
4740
+ if (a === null) continue;
4741
+ if (i === 0) {
4742
+ throw new TypeError(
4743
+ `sequence(): ${a.polarity}() cannot be the FIRST term \u2014 an adjacency assertion tests the gap after the PRECEDING term. Put a concrete term first.`
4744
+ );
4745
+ }
4746
+ ;
4747
+ (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
4748
+ }
4749
+ const hasAdjacency = adjacency !== null;
4750
+ let leading = null;
4751
+ for (let i = 1; i < parsers.length; i++) {
4752
+ if (leadsTrivia(parsers[i])) (leading ??= Array.from({ length: parsers.length }, () => false))[i] = true;
4753
+ }
4754
+ const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
4755
+ const meta = {
4756
+ // Union through the nullable prefix — a leading optional()/many() lets a later
4757
+ // term's first char start the sequence. Just `parsers[0]` under-approximates.
4758
+ firstSet: sequenceFirstSet(parsers),
4759
+ canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
4760
+ isTrivia: false
4761
+ };
4762
+ const def = { tag: "sequence", parsers };
4763
+ const scalarParsers = parsers.map(scalarOf);
4764
+ const parseScalar = (input, pos, ctx) => {
4765
+ let cur = pos;
4766
+ if (strictArity !== 0) {
4767
+ cur = scalarParsers[0](input, cur, ctx);
4768
+ if (cur < 0) return cur;
4769
+ const first = ctx._sv;
4770
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
4771
+ cur = scalarParsers[1](input, cur, ctx);
4772
+ if (cur < 0) return cur;
4773
+ const second = ctx._sv;
4774
+ if (strictArity === 2) {
4775
+ ctx._sv = def.valueUnused ? void 0 : [first, second];
4776
+ return cur;
4777
+ }
4778
+ if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
4779
+ cur = scalarParsers[2](input, cur, ctx);
4780
+ if (cur < 0) return cur;
4781
+ ctx._sv = def.valueUnused ? void 0 : [first, second, ctx._sv];
4782
+ return cur;
4783
+ }
4784
+ const values = def.valueUnused ? void 0 : [];
4785
+ for (let i = 0; i < scalarParsers.length; i++) {
4786
+ let termPos = cur;
4787
+ if (i > 0 && ctx.trivia) termPos = advanceTrivia(input, cur, ctx);
4788
+ const end = scalarParsers[i](input, termPos, ctx);
4789
+ if (end < 0) return end;
4790
+ if (values !== void 0) values.push(ctx._sv);
4791
+ if (end > termPos || termPos === cur) cur = end;
4792
+ }
4793
+ ctx._sv = values;
4794
+ return cur;
4795
+ };
4796
+ let followSentinels;
4797
+ return {
4798
+ _tag: "sequence",
4799
+ _meta: meta,
4800
+ _def: def,
4801
+ _parseScalar: hasAdjacency || leading !== null ? void 0 : parseScalar,
4802
+ parse(input, pos, ctx) {
4803
+ const tolerant = ctx._tolerant === true;
4804
+ if (tolerant) {
4805
+ followSentinels ??= parsers.map((_, i) => firstSetSentinel(
4806
+ parsers.slice(i + 1).reduce(
4807
+ (set, parser2) => union(set, firstSetOf(parser2)),
4808
+ { kind: "empty" }
4809
+ )
4810
+ ));
4811
+ }
4812
+ const inheritedSync = ctx._sync;
4813
+ const deferredTrivia = needsDeferredTriviaCommit(ctx);
4814
+ const values = def.valueUnused ? void 0 : [];
4815
+ let cur = pos;
4816
+ try {
4817
+ for (let i = 0; i < parsers.length; i++) {
4818
+ if (tolerant) ctx._sync = followSentinels[i] ?? inheritedSync;
4819
+ const assertion = adjacency?.[i];
4820
+ if (assertion) {
4821
+ if (!holdsAdjacency(input, cur, ctx, assertion)) return adjacencyFail(cur, assertion);
4822
+ if (values !== void 0) values.push(null);
4823
+ continue;
4824
+ }
4825
+ let termPos = cur;
4826
+ const markTlog = deferredTrivia ? cstTlLen(ctx) : 0;
4827
+ const markLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
4828
+ const markRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
4829
+ if (ctx.trivia && i > 0) {
4830
+ termPos = deferredTrivia ? commitTriviaScan(scanTriviaCompact(input, cur, ctx)) : advanceTrivia(input, cur, ctx);
4831
+ }
4832
+ const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
4833
+ const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
4834
+ const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
4835
+ if (leading?.[i] === true) LEAD.at = cur;
4836
+ const result = parsers[i].parse(input, termPos, ctx);
4837
+ if (!result.ok) return result;
4838
+ if (values !== void 0) values.push(result.value);
4839
+ if (result.span.end > termPos || termPos === cur) cur = result.span.end;
4840
+ else if (deferredTrivia) {
4841
+ rollbackScannedTriviaAt(
4842
+ ctx,
4843
+ markTlog,
4844
+ scanTlog,
4845
+ markLog,
4846
+ scanLog,
4847
+ markRootLog,
4848
+ scanRootLog
4849
+ );
4850
+ }
4851
+ }
4852
+ } finally {
4853
+ if (tolerant) ctx._sync = inheritedSync;
4854
+ }
4855
+ return {
4856
+ ok: true,
4857
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
4858
+ value: values ?? void 0,
4859
+ span: { start: pos, end: cur }
4860
+ };
4861
+ }
4862
+ };
4863
+ }
4864
+
4865
+ // src/compiler/degradation.ts
4866
+ function formatDegradation(d) {
4867
+ return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
4868
+ }
4869
+ var DETAIL_CAP = 8;
4870
+ function formatDegradations(list) {
4871
+ const byCode = /* @__PURE__ */ new Map();
4872
+ for (const d of list) {
4873
+ const bucket = byCode.get(d.code);
4874
+ if (bucket) bucket.push(d);
4875
+ else byCode.set(d.code, [d]);
4876
+ }
4877
+ const out = [];
4878
+ for (const [code, items] of byCode) {
4879
+ for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
4880
+ if (items.length > DETAIL_CAP) {
4881
+ out.push(
4882
+ `[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.`
4883
+ );
4884
+ }
4885
+ }
4886
+ return out;
4887
+ }
4888
+ var _sinks = [];
4889
+ function beginDegradationCapture() {
4890
+ _sinks.push(/* @__PURE__ */ new Map());
4891
+ }
4892
+ function degradationCaptureDepth() {
4893
+ return _sinks.length;
4894
+ }
4895
+ function unwindDegradationCapture(depth) {
4896
+ const found = [];
4897
+ while (_sinks.length > depth) {
4898
+ const sink = _sinks.pop();
4899
+ found.unshift(...sink.values());
4900
+ }
4901
+ return found;
4902
+ }
4903
+ function resolveDegradationLevel(explicit) {
4904
+ if (explicit !== void 0) return explicit;
4905
+ const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
4906
+ if (env === "off" || env === "warn" || env === "error") return env;
4907
+ return "warn";
4908
+ }
4909
+ function degradationKey(d) {
4910
+ return JSON.stringify([d.code, d.where, d.subject]);
4911
+ }
4912
+ function recordDegradation(d) {
4913
+ const level = resolveDegradationLevel();
4914
+ if (level === "off") return;
4915
+ const key = degradationKey(d);
4916
+ const sink = _sinks[_sinks.length - 1];
4917
+ if (sink) {
4918
+ if (!sink.has(key)) sink.set(key, d);
4919
+ return;
4920
+ }
4921
+ if (_immediate.has(key)) return;
4922
+ _immediate.add(key);
4923
+ if (level === "error") throw new Error(`parseman: degraded compilation path
4924
+ ${formatDegradation(d)}`);
4925
+ console.warn(formatDegradation(d));
4926
+ }
4927
+ var _immediate = /* @__PURE__ */ new Set();
4928
+ function beginCompileDegradationDrain() {
4929
+ if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
4930
+ };
4931
+ const depth = degradationCaptureDepth();
4932
+ beginDegradationCapture();
4933
+ return (report) => {
4934
+ const found = unwindDegradationCapture(depth);
4935
+ if (!report || found.length === 0) return;
4936
+ const fresh = found.filter((d) => {
4937
+ const k = degradationKey(d);
4938
+ if (_immediate.has(k)) return false;
4939
+ _immediate.add(k);
4940
+ return true;
4941
+ });
4942
+ if (fresh.length === 0) return;
4943
+ const lines2 = formatDegradations(fresh);
4944
+ if (resolveDegradationLevel() === "error") {
4945
+ throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
4946
+ ${lines2.join("\n")}`);
4947
+ }
4948
+ for (const l of lines2) console.warn(l);
4949
+ };
4950
+ }
4951
+
4952
+ // src/compiler/build-arity.ts
4953
+ function cstOutputHost(build) {
4954
+ return build?._parsemanCstOutput === true;
4955
+ }
4956
+ var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
4957
+ var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
4958
+ var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
4959
+ var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
4960
+ function confirmedBuildArity(src) {
4961
+ const s = src.trim();
4962
+ if (NATIVE_CODE_RE.test(s)) return null;
4963
+ if (/\barguments\b/.test(s)) return null;
4964
+ if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
4965
+ const inner = paramListText(s);
4966
+ if (inner === null) return null;
4967
+ if (inner.trim() === "") return 0;
4968
+ const parts = splitTopLevel(inner);
4969
+ if (parts === null) return null;
4970
+ for (const part of parts) {
4971
+ if (!isConfirmableParam(part.trim())) return null;
4972
+ }
4973
+ return parts.length;
4974
+ }
4975
+ function paramListText(s) {
4976
+ const open = PARAM_LIST_OPEN_RE.exec(s);
4977
+ if (!open) return null;
4978
+ const start = open[0].length;
4979
+ const end = matchingParen(s, start);
4980
+ if (end === null) return null;
4981
+ if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
4982
+ return s.slice(start, end);
4983
+ }
4984
+ function matchingParen(s, start) {
4985
+ let depth = 0;
4986
+ for (let i = start; i < s.length; i++) {
4987
+ const c = s[i];
4988
+ if (c === '"' || c === "'" || c === "`") {
4989
+ const close = skipString(s, i);
4990
+ if (close === null) return null;
4991
+ i = close;
4992
+ continue;
4993
+ }
4994
+ if (c === "(" || c === "[" || c === "{") depth++;
4995
+ else if (c === "]" || c === "}") {
4996
+ if (--depth < 0) return null;
4997
+ } else if (c === ")") {
4998
+ if (depth === 0) return i;
4999
+ depth--;
5000
+ }
5001
+ }
5002
+ return null;
5003
+ }
5004
+ function skipString(s, open) {
5005
+ const quote = s[open];
5006
+ for (let i = open + 1; i < s.length; i++) {
5007
+ if (s[i] === "\\") {
5008
+ i++;
5009
+ continue;
5010
+ }
5011
+ if (s[i] === quote) return i;
5012
+ }
5013
+ return null;
5014
+ }
5015
+ function splitTopLevel(inner) {
5016
+ const parts = [];
5017
+ let depth = 0;
5018
+ let angle = 0;
5019
+ let last = 0;
5020
+ for (let i = 0; i < inner.length; i++) {
5021
+ const c = inner[i];
5022
+ if (c === "=" && inner[i + 1] === ">") {
5023
+ i++;
5024
+ continue;
5025
+ }
5026
+ if (c === '"' || c === "'" || c === "`") {
5027
+ const close = skipString(inner, i);
5028
+ if (close === null) return null;
5029
+ i = close;
5030
+ continue;
5031
+ }
5032
+ if (c === "(" || c === "[" || c === "{") depth++;
5033
+ else if (c === ")" || c === "]" || c === "}") {
5034
+ if (--depth < 0) return null;
5035
+ } else if (c === "<") angle++;
5036
+ else if (c === ">") {
5037
+ if (--angle < 0) return null;
5038
+ } else if (c === "," && depth === 0 && angle === 0) {
5039
+ parts.push(inner.slice(last, i));
5040
+ last = i + 1;
5041
+ }
5042
+ }
5043
+ if (depth !== 0 || angle !== 0) return null;
5044
+ parts.push(inner.slice(last));
5045
+ return parts;
5046
+ }
5047
+ function isConfirmableParam(part) {
5048
+ const head = PARAM_HEAD_RE.exec(part);
5049
+ if (!head) return false;
5050
+ const rest = part.slice(head[0].length);
5051
+ if (rest === "") return true;
5052
+ if (rest[0] !== ":") return false;
5053
+ return !hasDefaultAssign(rest.slice(1));
5054
+ }
5055
+ function hasDefaultAssign(annotation) {
5056
+ for (let i = 0; i < annotation.length; i++) {
5057
+ if (annotation[i] !== "=") continue;
5058
+ if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
5059
+ i++;
5060
+ continue;
5061
+ }
5062
+ const prev = annotation[i - 1];
5063
+ if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
5064
+ return true;
5065
+ }
5066
+ return false;
5067
+ }
5068
+ function buildAnalysisSrc(def) {
5069
+ return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
5070
+ }
5071
+ var RESOLVER_REASONS = {
5072
+ "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
5073
+ "arguments": "its body references `arguments`",
5074
+ "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
5075
+ "not-a-function": "the binding it names is not a function declaration",
5076
+ "unresolved-import": "its module could not be resolved or parsed",
5077
+ "not-found": "the name did not resolve to any binding",
5078
+ "computed": "it is a computed expression",
5079
+ "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
5080
+ "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
5081
+ };
5082
+ function unconfirmableReason(def, src) {
5083
+ const declared = def.buildArityUnresolved;
5084
+ if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
5085
+ const s = src.trim();
5086
+ const params = s.slice(0, s.indexOf(")") + 1);
5087
+ if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
5088
+ if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
5089
+ if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
5090
+ return "it names a binding this build could not resolve to a function declaration";
5091
+ }
5092
+ return "its parameter list could not be parsed";
5093
+ }
5094
+ function confirmedArityForDef(def) {
5095
+ if (def.buildArity !== void 0) return def.buildArity;
5096
+ const src = buildAnalysisSrc(def);
5097
+ const arity = confirmedBuildArity(src);
5098
+ if (arity !== null) return arity;
5099
+ const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
5100
+ recordDegradation({
5101
+ code: "build-arity-unconfirmed",
5102
+ severity: "warn",
5103
+ where: `node("${def.type ?? "<inferred>"}")`,
5104
+ subject,
5105
+ 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`,
5106
+ 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"
5107
+ });
5108
+ return null;
5109
+ }
5110
+ function buildReadsRaw(def) {
5111
+ if (!def.build) return true;
5112
+ if (def.buildRawUnused === true) return false;
5113
+ const arity = confirmedArityForDef(def);
5114
+ if (arity === null) return true;
5115
+ return arity >= 4;
5116
+ }
5117
+ function buildReadsTrivia(def) {
5118
+ if (!def.build) return true;
5119
+ const arity = confirmedArityForDef(def);
5120
+ if (arity === null) return true;
5121
+ return arity >= 5;
5122
+ }
5123
+ function buildReadsState(def) {
5124
+ if (!def.build) return true;
5125
+ const arity = confirmedArityForDef(def);
5126
+ if (arity === null) return true;
5127
+ return arity >= 6;
5128
+ }
5129
+
5130
+ // src/combinators/token.ts
5131
+ function semanticLeafValue(_ctx, _input, _start, _end, value) {
5132
+ return value;
5133
+ }
5134
+ function sourceLeafValue(ctx, input, start, end, value) {
5135
+ return cstOutputHost(ctx.build) ? input.slice(start, end) : value;
5136
+ }
5137
+ function token(root) {
5138
+ const meta = {
5139
+ firstSet: root._meta.firstSet,
5140
+ canMatchNewline: root._meta.canMatchNewline,
5141
+ isTrivia: false
5142
+ };
5143
+ return {
5144
+ _tag: "token",
5145
+ _meta: meta,
5146
+ _def: { tag: "token", parser: root },
5147
+ parse(input, pos, ctx) {
5148
+ const savedTrivia = ctx.trivia;
5149
+ const savedKinds = ctx.triviaKindLabels;
5150
+ const savedBuf = ctx._cstBuf;
5151
+ const savedChildren = ctx._cstChildren;
5152
+ const savedLeaves = ctx._cstLeaves;
5153
+ const savedRaw = ctx._cstRawChildren;
5154
+ const savedTriviaLog = ctx._cstTriviaLog;
5155
+ const savedOuterTriviaLog = ctx._triviaLog;
5156
+ const savedRootTriviaLog = ctx._rootTriviaLog;
5157
+ const wasCapturing = cstCaptureActive(ctx);
5158
+ ctx.trivia = void 0;
5159
+ ctx.triviaKindLabels = void 0;
5160
+ ctx._cstBuf = void 0;
5161
+ ctx._cstChildren = void 0;
5162
+ ctx._cstLeaves = void 0;
5163
+ ctx._cstRawChildren = void 0;
5164
+ ctx._cstTriviaLog = void 0;
5165
+ ctx._triviaLog = void 0;
5166
+ ctx._rootTriviaLog = void 0;
5167
+ let result;
5168
+ try {
5169
+ result = root.parse(input, pos, ctx);
5170
+ } finally {
5171
+ ctx.trivia = savedTrivia;
5172
+ ctx.triviaKindLabels = savedKinds;
5173
+ ctx._cstBuf = savedBuf;
5174
+ ctx._cstChildren = savedChildren;
5175
+ ctx._cstLeaves = savedLeaves;
5176
+ ctx._cstRawChildren = savedRaw;
5177
+ ctx._cstTriviaLog = savedTriviaLog;
5178
+ ctx._triviaLog = savedOuterTriviaLog;
5179
+ ctx._rootTriviaLog = savedRootTriviaLog;
5180
+ }
5181
+ if (!result.ok) return result;
5182
+ const span = result.span;
5183
+ const value = input.slice(pos, span.end);
5184
+ if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span });
5185
+ return { ok: true, value, span };
5186
+ }
5187
+ };
5188
+ }
5189
+ function makeLeaf(root, fn, sourceCstValue) {
5190
+ const cstValue = sourceCstValue ? sourceLeafValue : semanticLeafValue;
5191
+ const meta = {
5192
+ firstSet: root._meta.firstSet,
5193
+ canMatchNewline: root._meta.canMatchNewline,
5194
+ isTrivia: false
5195
+ };
5196
+ return {
5197
+ _tag: "leaf",
5198
+ _meta: meta,
5199
+ _def: {
5200
+ tag: "leaf",
5201
+ parser: root,
5202
+ fn,
5203
+ ...sourceCstValue ? { cstValue: "source" } : {}
5204
+ },
5205
+ parse(input, pos, ctx) {
5206
+ const savedBuf = ctx._cstBuf;
5207
+ const savedChildren = ctx._cstChildren;
5208
+ const savedLeaves = ctx._cstLeaves;
5209
+ const savedRaw = ctx._cstRawChildren;
5210
+ const savedTriviaLog = ctx._cstTriviaLog;
5211
+ const savedOuterTriviaLog = ctx._triviaLog;
5212
+ const wasCapturing = cstCaptureActive(ctx);
5213
+ ctx._cstBuf = void 0;
5214
+ ctx._cstChildren = void 0;
5215
+ ctx._cstLeaves = void 0;
5216
+ ctx._cstRawChildren = void 0;
5217
+ ctx._cstTriviaLog = void 0;
5218
+ ctx._triviaLog = void 0;
5219
+ let result;
5220
+ try {
5221
+ result = root.parse(input, pos, ctx);
5222
+ } finally {
5223
+ ctx._cstBuf = savedBuf;
5224
+ ctx._cstChildren = savedChildren;
5225
+ ctx._cstLeaves = savedLeaves;
5226
+ ctx._cstRawChildren = savedRaw;
5227
+ ctx._cstTriviaLog = savedTriviaLog;
5228
+ ctx._triviaLog = savedOuterTriviaLog;
5229
+ }
5230
+ if (!result.ok) return result;
5231
+ const value = fn(result.value, result.span);
5232
+ if (wasCapturing) pushCstLeaf(ctx, {
5233
+ _tag: "leaf",
5234
+ value: cstValue(ctx, input, pos, result.span.end, value),
5235
+ span: result.span
5236
+ });
5237
+ return { ok: true, value, span: result.span };
5238
+ }
5239
+ };
5240
+ }
5241
+ function leaf(root, fn) {
5242
+ return makeLeaf(root, fn, false);
5243
+ }
5244
+ function sourceLeaf(root, fn) {
5245
+ return makeLeaf(root, fn, true);
5246
+ }
5247
+
5248
+ // src/combinators/attempt.ts
5249
+ function containOption(options) {
5250
+ if (options === void 0) return false;
5251
+ for (const key of Object.keys(options)) {
5252
+ if (key !== "contain") throw new TypeError(`attempt(): unknown option ${JSON.stringify(key)}`);
5253
+ }
5254
+ if (options.contain !== void 0 && typeof options.contain !== "boolean") {
5255
+ throw new TypeError("attempt(): `contain` must be a boolean");
5256
+ }
5257
+ return options.contain === true;
5258
+ }
5259
+ function attempt(parser2, options) {
5260
+ const contain = containOption(options);
5261
+ const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
5262
+ let failExpected;
5263
+ return {
5264
+ _tag: "attempt",
5265
+ _meta: parser2._meta,
5266
+ _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
5267
+ parse(input, pos, ctx) {
5268
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
5269
+ if (failExpected === void 0) {
5270
+ const e = deriveExpected(parser2);
5271
+ failExpected = e.length > 0 ? e : [parser2._tag];
5272
+ }
5273
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
5274
+ }
5275
+ const mark = saveCstMark(ctx);
5276
+ const log = ctx._triviaLog?.length;
5277
+ const rootLog = ctx._rootTriviaLog?.length;
5278
+ const split = ctx._columnsSplit;
5279
+ const result = parser2.parse(input, pos, ctx);
5280
+ if (result.ok) return result;
5281
+ rollbackCstCapture(ctx, mark);
5282
+ ctx._columnsSplit = split;
5283
+ if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
5284
+ if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
5285
+ if (result.committed && !contain) return result;
5286
+ return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
5287
+ }
5288
+ };
5289
+ }
5290
+
5291
+ // src/analysis/gating.ts
5292
+ var choiceArms = (c) => c.arms;
5293
+ function childrenOf(d) {
5294
+ switch (d.tag) {
5295
+ case "sequence":
5296
+ case "choice":
5297
+ return d.parsers;
5298
+ case "dispatch":
5299
+ return [
5300
+ d.selector,
5301
+ ...d.cases.map((c) => c.parser),
5302
+ ...d.matchers ? d.matchers.map((c) => c.parser) : [],
5303
+ ...d.otherwise === void 0 ? [] : [d.otherwise]
5304
+ ];
5305
+ case "sepBy":
5306
+ return [d.parser, d.separator];
5307
+ case "recover":
5308
+ return [d.parser, d.sentinel];
5309
+ case "scanTo":
5310
+ return [
5311
+ d.sentinel,
5312
+ ...d.skip,
5313
+ ...d.recoverAt === void 0 ? [] : [d.recoverAt],
5314
+ ...d.stopAt === void 0 ? [] : [d.stopAt]
5315
+ ];
5316
+ case "grammar":
5317
+ return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
5318
+ case "routed":
5319
+ return d.fallback ? [d.fallback] : [];
5320
+ case "lines":
5321
+ return [d.prefix, d.body, ...d.first === void 0 ? [] : [d.first], ...d.lazy === void 0 ? [] : [d.lazy]];
5322
+ case "columns":
5323
+ return [];
5324
+ // A `lazy` is a REFERENCE, not a subtree. Descending through it would make
5325
+ // every rule's walk cover the whole reachable grammar — site paths become
5326
+ // nonsense, and a structural hash includes half the grammar. Treated as a
5327
+ // leaf, keyed by the rule name it refers to, which is also the right
5328
+ // semantics: two productions referencing `g.Ident` really do fill that slot
5329
+ // the same way.
5330
+ case "lazy":
5331
+ case "literal":
5332
+ case "regex":
5333
+ case "keywords":
5334
+ case "guard":
5335
+ case "adjacency":
5336
+ case "unknown":
5337
+ return [];
5338
+ default: {
5339
+ const rec = d;
5340
+ return rec.parser ? [rec.parser] : [];
5341
+ }
5342
+ }
5343
+ }
5344
+ var isAny = (fs) => fs.kind === "any";
5345
+ function intersection(a, b) {
5346
+ if (a.kind === "any") return b;
5347
+ if (b.kind === "any") return a;
5348
+ if (a.kind === "empty" || b.kind === "empty") return { kind: "empty" };
5349
+ const ranges = [];
5350
+ for (const ra of a.ranges) for (const rb of b.ranges) {
5351
+ const lo = Math.max(ra.lo, rb.lo);
5352
+ const hi = Math.min(ra.hi, rb.hi);
5353
+ if (lo <= hi) ranges.push({ lo, hi });
5354
+ }
5355
+ return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
5356
+ }
5357
+ function combine(sets) {
5358
+ let acc = { kind: "empty" };
5359
+ for (const s of sets) {
5360
+ if (s.kind === "any") return { kind: "any" };
5361
+ if (s.kind === "empty") continue;
5362
+ acc = acc.kind === "ranges" ? { kind: "ranges", ranges: [...acc.ranges, ...s.ranges] } : s;
5363
+ }
5364
+ return acc;
5365
+ }
5366
+ var ch = (c) => c >= 32 && c < 127 ? `'${String.fromCharCode(c)}'` : `\\u${c}`;
5367
+ function firstSetToString(fs) {
5368
+ if (fs.kind === "any") return "ANY";
5369
+ if (fs.kind === "empty") return "(empty)";
5370
+ return fs.ranges.map((r) => r.lo === r.hi ? ch(r.lo) : `${ch(r.lo)}-${ch(r.hi)}`).join(",");
5371
+ }
5372
+ var ruleNameOf = (p) => p._ruleName;
5373
+ var KEYWORD_REGEX_RE = /^\^?[@#.-]?[A-Za-z][\w-]*(\(\?![^)]*\))?\$?$/;
5374
+ var SUGGESTIONS = {
5375
+ "broad-recognizer": "This arm can begin with any character, so no single-character test can rule it out. The parser has to enter it \u2014 set up, try, and undo \u2014 at every position it reaches, instead of skipping it for free.\nTo fix: make the arm begin with a fixed character, word or keyword. If it is meant to be a catch-all that matches anything, leave it and add the choice to the accept list at the end.",
5376
+ "leading-not": "This arm begins with a not(...) check, which matches nothing and so says nothing about which character the arm starts with. The parser then cannot rule the arm out and enters it everywhere.\nTo fix: put the term that actually consumes text first and keep not(...) after it as a trailing boundary. If you need to REQUIRE something ahead without consuming it, use peek(X) \u2014 it checks the same thing but still tells parseman that the arm starts with X.",
5377
+ "nullable-prefix": "This arm begins with something optional \u2014 an optional(...) or a repeat that allows zero items \u2014 so the arm can also start with whatever comes AFTER it, and that is broad enough that no character rules the arm out.\nTo fix: give the optional part its own arm so each arm has a definite beginning, or require at least one item. A plain sepBy(item, sep) also matches nothing at all; pass { min: 1 } when the list must not be empty.",
5378
+ "cross-artifact-ref": "This arm hands off to another rule, and that rule has the same problem \u2014 it can begin with any character \u2014 so the cost is inherited rather than caused here.\nTo fix: run this check on the rule it refers to and fix it there. One rule given a definite beginning fixes every choice that uses it.",
5379
+ "opaque-wrapper": "This arm begins with a wrapper that only inspects parser state (a gate/withCtx/recover) and consumes no text, so it says nothing about which character the arm starts with.\nTo fix: put a term that actually matches text in front of the wrapper.",
5380
+ "ref-cycle": "This arm refers back to itself through a cycle of rules, and parseman could not work out a definite first character for any of them.\nTo fix: make sure the base case of the recursion begins with a fixed character or keyword, so the cycle has somewhere definite to start."
5381
+ };
5382
+ function refineSuggestion(cause, arm) {
5383
+ if (cause !== "broad-recognizer") return SUGGESTIONS[cause];
5384
+ const lead2 = peelToLeading(arm);
5385
+ const d = lead2._def;
5386
+ if (d.tag === "regex" && KEYWORD_REGEX_RE.test(d.source)) {
5387
+ return `This arm matches the fixed word \`${d.source}\` with a regular expression, and parseman cannot always tell from a regular expression which character it starts with.
5388
+ To fix: write it as word('\u2026') or keywords([\u2026]) instead. They match exactly the same text and compile to the same character scan, but they also tell parseman the first character, which is what lets the parser skip this arm when it cannot match.`;
5389
+ }
5390
+ if (d.tag === "scanTo") {
5391
+ return "This arm ends in a scanTo(...) catch-all, which reads forward until it finds something and so can begin at any character. The parser can never rule it out. That is usually exactly what you want from a fallback.\nTo fix: usually nothing. Add this choice to the accept list at the end so the check keeps flagging the choices that are real problems.";
5392
+ }
5393
+ return SUGGESTIONS[cause];
5394
+ }
5395
+ function classifyBroadArm(arm, resolve) {
5396
+ const seen = /* @__PURE__ */ new Set();
5397
+ const relabel = (inner, cause, detail) => ({ cause, detail, ...inner?.unresolvedExternal ? { unresolvedExternal: true } : {} });
5398
+ const walk = (p) => {
5399
+ if (seen.has(p)) return { cause: "ref-cycle", detail: "ref cycle" };
5400
+ seen.add(p);
5401
+ const d = p._def;
5402
+ switch (d.tag) {
5403
+ case "literal":
5404
+ case "regex":
5405
+ case "keywords":
5406
+ return isAny(p._meta.firstSet) ? { cause: "broad-recognizer", detail: `broad recognizer (${d.tag})` } : null;
5407
+ case "not":
5408
+ return { cause: "leading-not", detail: "leading not(...) (first-set ANY)" };
5409
+ // peek() CARRIES its body's first-set, so it is only broad when the body is
5410
+ // (or the body is nullable) — walk in for the real cause.
5411
+ case "peek":
5412
+ return walk(d.parser);
5413
+ case "scanTo":
5414
+ return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
5415
+ // A run opens with its first line's prefix.
5416
+ case "lines":
5417
+ return walk(d.first ?? d.prefix);
5418
+ // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
5419
+ // its own — it is the poison. `withCtx`/`recover` FORWARD their inner
5420
+ // first-set (see firstSetOf), so a broad result comes from the inner parser:
5421
+ // walk into it for a precise cause.
5422
+ case "guard":
5423
+ return { cause: "opaque-wrapper", detail: `opaque wrapper (${d.tag})` };
5424
+ // Zero-width and dropped from a sequence's first-set (isZeroWidthAssertion),
5425
+ // so it is never the reason an arm is broad — but it can be reached as the
5426
+ // sole term of a degenerate arm. Report it precisely rather than as a wrapper.
5427
+ case "adjacency":
4609
5428
  return { cause: "broad-recognizer", detail: `${d.polarity}() (zero-width assertion)` };
4610
5429
  // attempt/withCtx/recover FORWARD their inner first-set (transparent for
4611
5430
  // dispatch), so a broad result comes from the inner parser — walk into it.
@@ -5245,284 +6064,124 @@ function reachesMarked(root, successors, memo) {
5245
6064
  const k = visit(child);
5246
6065
  if (low[k] < low[at]) low[at] = low[k];
5247
6066
  if (found[k]) found[at] = true;
5248
- }
5249
- }
5250
- if (low[at] === at) {
5251
- const answer = found[at];
5252
- let member;
5253
- do {
5254
- member = stack.pop();
5255
- memo.set(member, answer);
5256
- } while (member !== node2);
5257
- }
5258
- return at;
5259
- };
5260
- return found[visit(root)];
5261
- }
5262
-
5263
- // src/compiler/degradation.ts
5264
- function formatDegradation(d) {
5265
- return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
5266
- }
5267
- var DETAIL_CAP = 8;
5268
- function formatDegradations(list) {
5269
- const byCode = /* @__PURE__ */ new Map();
5270
- for (const d of list) {
5271
- const bucket = byCode.get(d.code);
5272
- if (bucket) bucket.push(d);
5273
- else byCode.set(d.code, [d]);
5274
- }
5275
- const out = [];
5276
- for (const [code, items] of byCode) {
5277
- for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
5278
- if (items.length > DETAIL_CAP) {
5279
- out.push(
5280
- `[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.`
5281
- );
5282
- }
5283
- }
5284
- return out;
5285
- }
5286
- var _sinks = [];
5287
- function beginDegradationCapture() {
5288
- _sinks.push(/* @__PURE__ */ new Map());
5289
- }
5290
- function degradationCaptureDepth() {
5291
- return _sinks.length;
5292
- }
5293
- function unwindDegradationCapture(depth) {
5294
- const found = [];
5295
- while (_sinks.length > depth) {
5296
- const sink = _sinks.pop();
5297
- found.unshift(...sink.values());
5298
- }
5299
- return found;
5300
- }
5301
- function resolveDegradationLevel(explicit) {
5302
- if (explicit !== void 0) return explicit;
5303
- const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
5304
- if (env === "off" || env === "warn" || env === "error") return env;
5305
- return "warn";
5306
- }
5307
- function degradationKey(d) {
5308
- return JSON.stringify([d.code, d.where, d.subject]);
5309
- }
5310
- function recordDegradation(d) {
5311
- const level = resolveDegradationLevel();
5312
- if (level === "off") return;
5313
- const key = degradationKey(d);
5314
- const sink = _sinks[_sinks.length - 1];
5315
- if (sink) {
5316
- if (!sink.has(key)) sink.set(key, d);
5317
- return;
5318
- }
5319
- if (_immediate.has(key)) return;
5320
- _immediate.add(key);
5321
- if (level === "error") throw new Error(`parseman: degraded compilation path
5322
- ${formatDegradation(d)}`);
5323
- console.warn(formatDegradation(d));
5324
- }
5325
- var _immediate = /* @__PURE__ */ new Set();
5326
- function beginCompileDegradationDrain() {
5327
- if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
5328
- };
5329
- const depth = degradationCaptureDepth();
5330
- beginDegradationCapture();
5331
- return (report) => {
5332
- const found = unwindDegradationCapture(depth);
5333
- if (!report || found.length === 0) return;
5334
- const fresh = found.filter((d) => {
5335
- const k = degradationKey(d);
5336
- if (_immediate.has(k)) return false;
5337
- _immediate.add(k);
5338
- return true;
5339
- });
5340
- if (fresh.length === 0) return;
5341
- const lines2 = formatDegradations(fresh);
5342
- if (resolveDegradationLevel() === "error") {
5343
- throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
5344
- ${lines2.join("\n")}`);
5345
- }
5346
- for (const l of lines2) console.warn(l);
5347
- };
5348
- }
5349
-
5350
- // src/compiler/build-arity.ts
5351
- function cstOutputHost(build) {
5352
- return build?._parsemanCstOutput === true;
5353
- }
5354
- var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
5355
- var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
5356
- var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
5357
- var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
5358
- function confirmedBuildArity(src) {
5359
- const s = src.trim();
5360
- if (NATIVE_CODE_RE.test(s)) return null;
5361
- if (/\barguments\b/.test(s)) return null;
5362
- if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
5363
- const inner = paramListText(s);
5364
- if (inner === null) return null;
5365
- if (inner.trim() === "") return 0;
5366
- const parts = splitTopLevel(inner);
5367
- if (parts === null) return null;
5368
- for (const part of parts) {
5369
- if (!isConfirmableParam(part.trim())) return null;
5370
- }
5371
- return parts.length;
5372
- }
5373
- function paramListText(s) {
5374
- const open = PARAM_LIST_OPEN_RE.exec(s);
5375
- if (!open) return null;
5376
- const start = open[0].length;
5377
- const end = matchingParen(s, start);
5378
- if (end === null) return null;
5379
- if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
5380
- return s.slice(start, end);
5381
- }
5382
- function matchingParen(s, start) {
5383
- let depth = 0;
5384
- for (let i = start; i < s.length; i++) {
5385
- const c = s[i];
5386
- if (c === '"' || c === "'" || c === "`") {
5387
- const close = skipString(s, i);
5388
- if (close === null) return null;
5389
- i = close;
5390
- continue;
5391
- }
5392
- if (c === "(" || c === "[" || c === "{") depth++;
5393
- else if (c === "]" || c === "}") {
5394
- if (--depth < 0) return null;
5395
- } else if (c === ")") {
5396
- if (depth === 0) return i;
5397
- depth--;
5398
- }
5399
- }
5400
- return null;
5401
- }
5402
- function skipString(s, open) {
5403
- const quote = s[open];
5404
- for (let i = open + 1; i < s.length; i++) {
5405
- if (s[i] === "\\") {
5406
- i++;
5407
- continue;
5408
- }
5409
- if (s[i] === quote) return i;
5410
- }
5411
- return null;
5412
- }
5413
- function splitTopLevel(inner) {
5414
- const parts = [];
5415
- let depth = 0;
5416
- let angle = 0;
5417
- let last = 0;
5418
- for (let i = 0; i < inner.length; i++) {
5419
- const c = inner[i];
5420
- if (c === "=" && inner[i + 1] === ">") {
5421
- i++;
5422
- continue;
5423
- }
5424
- if (c === '"' || c === "'" || c === "`") {
5425
- const close = skipString(inner, i);
5426
- if (close === null) return null;
5427
- i = close;
5428
- continue;
5429
- }
5430
- if (c === "(" || c === "[" || c === "{") depth++;
5431
- else if (c === ")" || c === "]" || c === "}") {
5432
- if (--depth < 0) return null;
5433
- } else if (c === "<") angle++;
5434
- else if (c === ">") {
5435
- if (--angle < 0) return null;
5436
- } else if (c === "," && depth === 0 && angle === 0) {
5437
- parts.push(inner.slice(last, i));
5438
- last = i + 1;
6067
+ }
5439
6068
  }
5440
- }
5441
- if (depth !== 0 || angle !== 0) return null;
5442
- parts.push(inner.slice(last));
5443
- return parts;
6069
+ if (low[at] === at) {
6070
+ const answer = found[at];
6071
+ let member;
6072
+ do {
6073
+ member = stack.pop();
6074
+ memo.set(member, answer);
6075
+ } while (member !== node2);
6076
+ }
6077
+ return at;
6078
+ };
6079
+ return found[visit(root)];
5444
6080
  }
5445
- function isConfirmableParam(part) {
5446
- const head = PARAM_HEAD_RE.exec(part);
5447
- if (!head) return false;
5448
- const rest = part.slice(head[0].length);
5449
- if (rest === "") return true;
5450
- if (rest[0] !== ":") return false;
5451
- return !hasDefaultAssign(rest.slice(1));
6081
+
6082
+ // src/table/factor-choice.ts
6083
+ var DERIVED = /* @__PURE__ */ new Set(["_tag", "_meta", "_def", "_parseScalar", "_stickyRegex", "parse"]);
6084
+ function isCombinator(v) {
6085
+ return typeof v === "object" && v !== null && "_def" in v && "_meta" in v;
6086
+ }
6087
+ function sameComb(a, b, depth) {
6088
+ if (a === b) return true;
6089
+ if (depth > 32 || a._tag !== b._tag) return false;
6090
+ for (const k of Object.keys(a)) if (!DERIVED.has(k)) return false;
6091
+ for (const k of Object.keys(b)) if (!DERIVED.has(k)) return false;
6092
+ return sameFields(a._def, b._def, depth, void 0);
6093
+ }
6094
+ function sameValue(u, v, depth) {
6095
+ if (u === v) return true;
6096
+ if (isCombinator(u) && isCombinator(v)) return sameComb(u, v, depth + 1);
6097
+ if (Array.isArray(u) && Array.isArray(v)) return u.length === v.length && u.every((x, i) => sameValue(x, v[i], depth));
6098
+ if (typeof u === "object" && u !== null && typeof v === "object" && v !== null && Object.getPrototypeOf(u) === Object.prototype && Object.getPrototypeOf(v) === Object.prototype) {
6099
+ return sameFields(u, v, depth, void 0);
6100
+ }
6101
+ return false;
5452
6102
  }
5453
- function hasDefaultAssign(annotation) {
5454
- for (let i = 0; i < annotation.length; i++) {
5455
- if (annotation[i] !== "=") continue;
5456
- if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
5457
- i++;
6103
+ function sameFields(x, y, depth, skip) {
6104
+ const kx = Object.keys(x).filter((k) => k !== skip && x[k] !== void 0);
6105
+ const ky = Object.keys(y).filter((k) => k !== skip && y[k] !== void 0);
6106
+ if (kx.length !== ky.length) return false;
6107
+ for (const k of kx) {
6108
+ if (!(k in y)) return false;
6109
+ const u = x[k];
6110
+ const v = y[k];
6111
+ if (k === "fn" && typeof u === "function" && typeof v === "function" && u !== v) {
6112
+ if (typeof x.fnSrc !== "string" || x.fnSrc === "" || x.fnSrc !== y.fnSrc) return false;
5458
6113
  continue;
5459
6114
  }
5460
- const prev = annotation[i - 1];
5461
- if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
5462
- return true;
6115
+ if (!sameValue(u, v, depth)) return false;
5463
6116
  }
5464
- return false;
5465
- }
5466
- function buildAnalysisSrc(def) {
5467
- return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
6117
+ return true;
5468
6118
  }
5469
- var RESOLVER_REASONS = {
5470
- "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
5471
- "arguments": "its body references `arguments`",
5472
- "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
5473
- "not-a-function": "the binding it names is not a function declaration",
5474
- "unresolved-import": "its module could not be resolved or parsed",
5475
- "not-found": "the name did not resolve to any binding",
5476
- "computed": "it is a computed expression",
5477
- "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
5478
- "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
5479
- };
5480
- function unconfirmableReason(def, src) {
5481
- const declared = def.buildArityUnresolved;
5482
- if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
5483
- const s = src.trim();
5484
- const params = s.slice(0, s.indexOf(")") + 1);
5485
- if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
5486
- if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
5487
- if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
5488
- return "it names a binding this build could not resolve to a function declaration";
6119
+ function rewrap(w, body) {
6120
+ const d = w._def;
6121
+ let out;
6122
+ if (d.tag === "leaf") {
6123
+ out = d.cstValue === "source" ? sourceLeaf(body, d.fn) : leaf(body, d.fn);
6124
+ if (d.fnSrc !== void 0) out._def.fnSrc = d.fnSrc;
6125
+ } else if (d.tag === "grammar") {
6126
+ out = d.clearTrivia === true ? noTrivia(body) : parser({ trivia: d.triviaParser ?? null }, body);
6127
+ } else return void 0;
6128
+ return sameFields(d, out._def, 0, "parser") ? out : void 0;
6129
+ }
6130
+ function unusedOf(seq) {
6131
+ return seq._def.tag === "sequence" && seq._def.valueUnused === true;
6132
+ }
6133
+ function never(c) {
6134
+ const t2 = c._def.tag;
6135
+ return t2 === "optional" || t2 === "many";
6136
+ }
6137
+ function factorSharedEnds(d) {
6138
+ const arms = d.parsers;
6139
+ if (arms.length < 2) return void 0;
6140
+ if (d.strategy.tag !== "firstMatch" && d.strategy.tag !== "sharedPrefix") return void 0;
6141
+ if (d.gates.some((g) => g !== null) || d.autoNot.some((a) => a !== null)) return void 0;
6142
+ const shapes = [];
6143
+ for (const arm of arms) {
6144
+ const wrappers = [];
6145
+ let c = arm;
6146
+ while ((c._def.tag === "leaf" || c._def.tag === "grammar") && wrappers.length < 4) {
6147
+ wrappers.push(c);
6148
+ c = c._def.parser;
6149
+ }
6150
+ if (c._def.tag !== "sequence" || c._def.parsers.length < 2) return void 0;
6151
+ shapes.push({ wrappers, terms: c._def.parsers, seq: c });
6152
+ }
6153
+ const first = shapes[0];
6154
+ const n = first.terms.length;
6155
+ for (const s of shapes) {
6156
+ if (s.wrappers.length !== first.wrappers.length || s.terms.length !== n) return void 0;
6157
+ if (unusedOf(s.seq) !== unusedOf(first.seq)) return void 0;
6158
+ for (let j = 0; j < s.wrappers.length; j++) {
6159
+ const a = first.wrappers[j]._def;
6160
+ const b = s.wrappers[j]._def;
6161
+ if (a.tag !== b.tag || !sameFields(a, b, 0, "parser")) return void 0;
6162
+ }
6163
+ }
6164
+ let at = -1;
6165
+ for (let i = 0; i < n; i++) {
6166
+ if (shapes.every((s) => sameComb(s.terms[i], first.terms[i], 0))) continue;
6167
+ if (at >= 0) return void 0;
6168
+ at = i;
6169
+ }
6170
+ if (at < 0) return void 0;
6171
+ for (let i = at + 1; i < n; i++) if (!never(first.terms[i])) return void 0;
6172
+ if (arms.some((a) => mayCommitFailure(a))) return void 0;
6173
+ const middle = choice(...shapes.map((s) => s.terms[at]));
6174
+ if (middle._def.tag === "choice" && middle._def.autoNot.some((a) => a !== null)) return void 0;
6175
+ const st = middle._def.tag === "choice" ? middle._def.strategy : void 0;
6176
+ if (st === void 0 || st.tag !== "firstMatch" && st.tag !== "sharedPrefix" && !(st.tag === "literalsLongestFirst" && st.sortedIndices.every((x, i) => x === i))) return void 0;
6177
+ let out = sequence(
6178
+ ...[...first.terms.slice(0, at), middle, ...first.terms.slice(at + 1)]
6179
+ );
6180
+ if (unusedOf(first.seq)) {
6181
+ out._def.valueUnused = true;
5489
6182
  }
5490
- return "its parameter list could not be parsed";
5491
- }
5492
- function confirmedArityForDef(def) {
5493
- if (def.buildArity !== void 0) return def.buildArity;
5494
- const src = buildAnalysisSrc(def);
5495
- const arity = confirmedBuildArity(src);
5496
- if (arity !== null) return arity;
5497
- const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
5498
- recordDegradation({
5499
- code: "build-arity-unconfirmed",
5500
- severity: "warn",
5501
- where: `node("${def.type ?? "<inferred>"}")`,
5502
- subject,
5503
- 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`,
5504
- 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"
5505
- });
5506
- return null;
5507
- }
5508
- function buildReadsRaw(def) {
5509
- if (!def.build) return true;
5510
- if (def.buildRawUnused === true) return false;
5511
- const arity = confirmedArityForDef(def);
5512
- if (arity === null) return true;
5513
- return arity >= 4;
5514
- }
5515
- function buildReadsTrivia(def) {
5516
- if (!def.build) return true;
5517
- const arity = confirmedArityForDef(def);
5518
- if (arity === null) return true;
5519
- return arity >= 5;
5520
- }
5521
- function buildReadsState(def) {
5522
- if (!def.build) return true;
5523
- const arity = confirmedArityForDef(def);
5524
- if (arity === null) return true;
5525
- return arity >= 6;
6183
+ for (let j = first.wrappers.length - 1; j >= 0 && out !== void 0; j--) out = rewrap(first.wrappers[j], out);
6184
+ return out === void 0 ? void 0 : attempt(out);
5526
6185
  }
5527
6186
 
5528
6187
  // src/compiler/fields.ts
@@ -5650,10 +6309,16 @@ function branchUsesRouted(branch) {
5650
6309
  return branch.usesRouted === true || parserUsesRouted(branch.parser);
5651
6310
  }
5652
6311
  function asciiFoldKey(key) {
5653
- let out = "";
5654
- for (let i = 0; i < key.length; i++) {
6312
+ let i = 0;
6313
+ for (; i < key.length; i++) {
5655
6314
  const c = key.charCodeAt(i);
5656
- out += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
6315
+ if (c >= 65 && c <= 90) break;
6316
+ }
6317
+ if (i === key.length) return key;
6318
+ let out = key.slice(0, i);
6319
+ for (; i < key.length; i++) {
6320
+ const c = key.charCodeAt(i);
6321
+ out += c >= 65 && c <= 90 ? String.fromCharCode(c + 32) : key[i];
5657
6322
  }
5658
6323
  return out;
5659
6324
  }
@@ -5701,6 +6366,7 @@ var OP_SCOPE_PLAIN = 41;
5701
6366
  var OP_LEX_BODY = 42;
5702
6367
  var OP_LEX_PROGRAM = 43;
5703
6368
  var OP_SOURCE_LEAF = 44;
6369
+ var OP_RGATE = 45;
5704
6370
  var OP_DISPATCH = 25;
5705
6371
  var OP_ROUTED = 26;
5706
6372
  var OP_NAMES = {
@@ -5720,6 +6386,7 @@ var OP_NAMES = {
5720
6386
  [OP_PEEK]: "PEEK",
5721
6387
  [OP_LEAF]: "LEAF",
5722
6388
  [OP_SOURCE_LEAF]: "SOURCE_LEAF",
6389
+ [OP_RGATE]: "RGATE",
5723
6390
  [OP_EMPTY]: "EMPTY",
5724
6391
  [OP_LIT_TRACK]: "LIT_TRACK",
5725
6392
  [OP_RX_TRACK]: "RX_TRACK",
@@ -5750,71 +6417,6 @@ var OP_NAMES = {
5750
6417
  [OP_LEX_PROGRAM]: "LEX_PROGRAM"
5751
6418
  };
5752
6419
 
5753
- // src/combinators/adjacency.ts
5754
- function adjacencyOf(p) {
5755
- return p._def.tag === "adjacency" ? p._def : null;
5756
- }
5757
- function adjacencyExpected(def) {
5758
- if (def.polarity === "adjacent") return "adjacent";
5759
- return def.kinds === void 0 ? "notAdjacent" : `notAdjacent(${def.kinds.join("|")})`;
5760
- }
5761
- function holdsAdjacency(input, cur, ctx, def) {
5762
- return adjacencyHolds(input, cur, ctx, def.polarity === "notAdjacent", def.kinds);
5763
- }
5764
- function adjacencyHolds(input, cur, ctx, negated, kinds) {
5765
- const end = probeTriviaEnd(input, cur, ctx);
5766
- if (!negated) return end === cur;
5767
- if (end === cur) return false;
5768
- if (kinds === void 0) return true;
5769
- const want = resolveAdjacencyKindMask(ctx.trivia, kinds);
5770
- const spec = analyzeLabeledTrivia(ctx.trivia);
5771
- return (triviaKindMaskAt(input, cur, spec, ctx.state) & want) !== 0;
5772
- }
5773
- function adjacencyFail(pos, def) {
5774
- return { ok: false, expected: [adjacencyExpected(def)], span: { start: pos, end: pos } };
5775
- }
5776
- 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.";
5777
- function adjacencyMisuse(polarity) {
5778
- return new TypeError(`${polarity}(): ${NO_STANDALONE_PARSE}`);
5779
- }
5780
- function adjacencyCombinator(def) {
5781
- const meta = {
5782
- // Zero-width and never leading (sequence() rejects index 0), so it contributes
5783
- // no first char. `isZeroWidthAssertion` drops it from a sequence's first-set
5784
- // rather than letting `any` poison the enclosing choice's dispatch.
5785
- firstSet: { kind: "any" },
5786
- canMatchNewline: false,
5787
- isTrivia: false
5788
- };
5789
- return {
5790
- _tag: "adjacency",
5791
- _meta: meta,
5792
- _def: def,
5793
- parse() {
5794
- throw adjacencyMisuse(def.polarity);
5795
- }
5796
- };
5797
- }
5798
- function adjacent() {
5799
- return adjacencyCombinator({ tag: "adjacency", polarity: "adjacent" });
5800
- }
5801
- function notAdjacent(options) {
5802
- const kinds = options?.kinds;
5803
- if (kinds !== void 0) {
5804
- if (!Array.isArray(kinds) || kinds.length === 0) {
5805
- throw new TypeError("notAdjacent({ kinds }): kinds must be a non-empty array of trivia category names.");
5806
- }
5807
- for (const k of kinds) {
5808
- if (typeof k !== "string" || k.length === 0) {
5809
- throw new TypeError(`notAdjacent({ kinds }): every kind must be a non-empty string, got ${JSON.stringify(k)}.`);
5810
- }
5811
- }
5812
- }
5813
- return adjacencyCombinator(
5814
- kinds === void 0 ? { tag: "adjacency", polarity: "notAdjacent" } : { tag: "adjacency", polarity: "notAdjacent", kinds: [...kinds] }
5815
- );
5816
- }
5817
-
5818
6420
  // src/combinators/node.ts
5819
6421
  function unwrapChild(child) {
5820
6422
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
@@ -6017,7 +6619,10 @@ function selectOptionalSuffixLexBody(ir) {
6017
6619
  const token2 = { strategy: "token", estimatedOps: 2, body };
6018
6620
  return token2.estimatedOps < character.estimatedOps ? token2 : character;
6019
6621
  }
6020
- function directTokenBodyParser(parser2, resolve) {
6622
+ function reinstallsRuleTrivia(reference, target) {
6623
+ return (reference._meta.grammarTrivia ?? target._meta.grammarTrivia) !== void 0 && hasOwnTriviaBoundary(target);
6624
+ }
6625
+ function directTokenBodyParser(parser2, resolve, scope) {
6021
6626
  const outer = parser2._def;
6022
6627
  if (outer.tag !== "token") return void 0;
6023
6628
  let body = outer.parser;
@@ -6027,12 +6632,13 @@ function directTokenBodyParser(parser2, resolve) {
6027
6632
  const wrapper = body._def;
6028
6633
  if (wrapper.tag === "grammar") {
6029
6634
  if (wrapper.clearTrivia !== true || wrapper.triviaParser !== void 0 || wrapper.captureTrivia === true || wrapper.rootCapture === "opaque" || wrapper.constructionTrackLines !== "inherit" || wrapper.constructionCaptureTriviaKinds !== void 0) return void 0;
6635
+ if (scope !== void 0) scope.cleared = true;
6030
6636
  body = wrapper.parser;
6031
6637
  continue;
6032
6638
  }
6033
6639
  if (wrapper.tag === "lazy") {
6034
6640
  const target = resolvedLazyTarget(body, resolve);
6035
- if (target === void 0) return void 0;
6641
+ if (target === void 0 || reinstallsRuleTrivia(body, target)) return void 0;
6036
6642
  body = target;
6037
6643
  continue;
6038
6644
  }
@@ -6204,7 +6810,7 @@ function directNot2TokenBody(parser2, resolve) {
6204
6810
  terminal
6205
6811
  };
6206
6812
  }
6207
- function directFixedLexNode(parser2, resolve, active, pureOnly = false) {
6813
+ function directFixedLexNode(parser2, resolve, active, pureOnly = false, cleared = false) {
6208
6814
  let current = parser2;
6209
6815
  const wrappers = /* @__PURE__ */ new Set();
6210
6816
  while (!wrappers.has(current)) {
@@ -6212,7 +6818,7 @@ function directFixedLexNode(parser2, resolve, active, pureOnly = false) {
6212
6818
  const def = current._def;
6213
6819
  if (def.tag === "lazy") {
6214
6820
  const target = resolvedLazyTarget(current, resolve);
6215
- if (target === void 0) return void 0;
6821
+ if (target === void 0 || reinstallsRuleTrivia(current, target)) return void 0;
6216
6822
  current = target;
6217
6823
  continue;
6218
6824
  }
@@ -6230,30 +6836,33 @@ function directFixedLexNode(parser2, resolve, active, pureOnly = false) {
6230
6836
  if (terminal !== void 0) return { kind: "terminal", terminal };
6231
6837
  const def = current._def;
6232
6838
  if (def.tag === "grammar") {
6839
+ if (def.triviaParser === void 0 && def.captureTrivia !== true && def.rootCapture !== "opaque" && def.constructionCaptureTriviaKinds === void 0) {
6840
+ return directFixedLexNode(def.parser, resolve, active, pureOnly, cleared || def.clearTrivia === true);
6841
+ }
6233
6842
  if (!pureOnly || def.parser._def.tag !== "token" && (def.clearTrivia !== true || def.triviaParser !== void 0 || def.captureTrivia === true || def.rootCapture === "opaque")) return void 0;
6234
- return directFixedLexNode(def.parser, resolve, active, true);
6843
+ return directFixedLexNode(def.parser, resolve, active, true, cleared);
6235
6844
  }
6236
6845
  if (def.tag === "sequence") {
6237
6846
  if (def.parsers.length < 2 || def.parsers.length > 5) return void 0;
6238
- const parts = def.parsers.map((part) => directFixedLexNode(part, resolve, active, pureOnly));
6847
+ const parts = def.parsers.map((part) => directFixedLexNode(part, resolve, active, pureOnly, cleared));
6239
6848
  if (parts.some((part) => part === void 0)) return void 0;
6240
6849
  return { kind: "sequence", parts };
6241
6850
  }
6242
6851
  if (def.tag === "choice") {
6243
- 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;
6244
- const arms = def.parsers.map((arm) => directFixedLexNode(arm, resolve, active, pureOnly));
6852
+ if (def.parsers.length < 2 || def.strategy.tag !== "firstMatch" || def.gates.some((gate) => gate !== null) || def.autoNot.some((check) => check !== null)) return void 0;
6853
+ const arms = def.parsers.map((arm) => directFixedLexNode(arm, resolve, active, pureOnly, cleared));
6245
6854
  if (arms.some((arm) => arm === void 0)) return void 0;
6246
- const final = classifyFinalChoice(def.parsers, resolve);
6247
6855
  return {
6248
6856
  kind: "choice",
6249
6857
  arms,
6250
- firstSets: final.firstSets,
6858
+ parsers: def.parsers,
6859
+ cleared,
6251
6860
  armExpected: def.parsers.map(deriveExpected),
6252
6861
  expected: def.parsers.flatMap(deriveExpected)
6253
6862
  };
6254
6863
  }
6255
6864
  if (def.tag === "not") {
6256
- const body = directFixedLexNode(def.parser, resolve, active, true);
6865
+ const body = directFixedLexNode(def.parser, resolve, active, true, cleared);
6257
6866
  return body === void 0 ? void 0 : {
6258
6867
  kind: "not",
6259
6868
  body,
@@ -6261,18 +6870,28 @@ function directFixedLexNode(parser2, resolve, active, pureOnly = false) {
6261
6870
  };
6262
6871
  }
6263
6872
  if (def.tag === "optional") {
6264
- const body = directFixedLexNode(def.parser, resolve, active, pureOnly);
6873
+ const body = directFixedLexNode(def.parser, resolve, active, pureOnly, cleared);
6265
6874
  return body === void 0 ? void 0 : { kind: "optional", body };
6266
6875
  }
6876
+ if (def.tag === "many" || def.tag === "oneOrMore") {
6877
+ const body = directFixedLexNode(def.parser, resolve, active, pureOnly, cleared);
6878
+ return body === void 0 ? void 0 : {
6879
+ kind: "repeat",
6880
+ body,
6881
+ min: def.tag === "many" ? 0 : def.min,
6882
+ max: def.max ?? -1
6883
+ };
6884
+ }
6267
6885
  return void 0;
6268
6886
  } finally {
6269
6887
  active.delete(current);
6270
6888
  }
6271
6889
  }
6272
6890
  function directFixedTokenBody(parser2, resolve) {
6273
- const body = directTokenBodyParser(parser2, resolve);
6891
+ const scope = { cleared: false };
6892
+ const body = directTokenBodyParser(parser2, resolve, scope);
6274
6893
  if (body === void 0) return void 0;
6275
- const root = directFixedLexNode(body, resolve, /* @__PURE__ */ new Set());
6894
+ const root = directFixedLexNode(body, resolve, /* @__PURE__ */ new Set(), false, scope.cleared);
6276
6895
  return root === void 0 ? void 0 : { kind: "fixed-tree", root };
6277
6896
  }
6278
6897
  function directBalancedTokenBody(parser2, resolve) {
@@ -6519,13 +7138,16 @@ var Encoder = class {
6519
7138
  }
6520
7139
  case "choice": {
6521
7140
  for (const arm of node2.arms) if (!emitNode(arm)) return false;
6522
- const op = node2.arms.length === 2 ? LEX_NODE_CHOICE2 : node2.arms.length === 4 ? LEX_NODE_CHOICE4 : node2.arms.length === 8 ? LEX_NODE_CHOICE8 : -1;
6523
- if (op < 0) return false;
6524
- words.push(op, this.expected(node2.expected));
7141
+ const final = classifyFinalChoice(
7142
+ node2.parsers,
7143
+ this.refResolver(),
7144
+ node2.cleared ? void 0 : this.activeTrivia
7145
+ );
7146
+ const classes = final.firstSets.map((first, i) => final.nullable[i] ? -1 : this.charClass(first));
7147
+ const exclusive = final.exclusive && classes.every((cls) => cls >= 0);
7148
+ words.push(LEX_NODE_CHOICE, node2.arms.length, exclusive ? 1 : 0, this.expected(node2.expected));
6525
7149
  for (let i = 0; i < node2.arms.length; i++) {
6526
- const first = node2.firstSets[i];
6527
- words.push(first.kind === "ranges" ? this.charClass(first) : -1);
6528
- words.push(this.expected(node2.armExpected[i]));
7150
+ words.push(classes[i], this.expected(node2.armExpected[i]));
6529
7151
  }
6530
7152
  return true;
6531
7153
  }
@@ -6537,6 +7159,10 @@ var Encoder = class {
6537
7159
  if (!emitNode(node2.body)) return false;
6538
7160
  words.push(LEX_NODE_OPTIONAL);
6539
7161
  return true;
7162
+ case "repeat":
7163
+ if (!emitNode(node2.body)) return false;
7164
+ words.push(LEX_NODE_REPEAT, node2.min, node2.max);
7165
+ return true;
6540
7166
  }
6541
7167
  };
6542
7168
  if (!emitNode(body.root)) return void 0;
@@ -7042,6 +7668,50 @@ var Encoder = class {
7042
7668
  if (!hasOwnTriviaBoundary(target)) return ip;
7043
7669
  return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(amb), ip);
7044
7670
  }
7671
+ /**
7672
+ * The `OP_RGATE` operands of a choice arm that opens with `routed()`, or
7673
+ * `undefined`. Through `node()`/scope wrappers and first sequence terms, the
7674
+ * arm's next term after the routed token is found; the gate is that term's
7675
+ * first characters, with the trivia in force at the boundary before it, so
7676
+ * the arm is entered only where it can get past the token. The term must be
7677
+ * unable to match empty or to commit, so a miss is exactly that term failing
7678
+ * at the token's end with its own set.
7679
+ */
7680
+ routedArmGate(arm) {
7681
+ const walk = (p, trivia2, depth) => {
7682
+ if (depth > 16) return void 0;
7683
+ const d = p._def;
7684
+ switch (d.tag) {
7685
+ case "routed":
7686
+ return "end";
7687
+ case "node":
7688
+ return walk(d.parser, trivia2, depth + 1);
7689
+ case "grammar":
7690
+ return walk(d.parser, d.clearTrivia === true ? void 0 : d.triviaParser ?? trivia2, depth + 1);
7691
+ case "sequence": {
7692
+ const head = d.parsers[0];
7693
+ if (head === void 0) return void 0;
7694
+ const r = walk(head, trivia2, depth + 1);
7695
+ if (r !== "end") return r;
7696
+ const next2 = d.parsers[1];
7697
+ return next2 === void 0 ? "end" : { next: next2, boundary: trivia2 };
7698
+ }
7699
+ default:
7700
+ return void 0;
7701
+ }
7702
+ };
7703
+ const follow = walk(arm, this.activeTrivia, 0);
7704
+ if (follow === void 0 || follow === "end") return void 0;
7705
+ const rr = this.refResolver();
7706
+ const { next, boundary } = follow;
7707
+ if (matchesEmpty(next, /* @__PURE__ */ new Set(), rr) || mayCommitFailure(next, rr, this.commitFailureMemo)) return void 0;
7708
+ const nextTag = next._def.tag;
7709
+ if (nextTag !== "literal" && nextTag !== "regex" && nextTag !== "keywords" && nextTag !== "label") return void 0;
7710
+ const first = firstSetOf(next, /* @__PURE__ */ new Set(), rr, boundary);
7711
+ const cls = this.charClass(boundary === void 0 ? first : union(first, firstSetOf(boundary, /* @__PURE__ */ new Set(), rr)));
7712
+ if (cls < 0) return void 0;
7713
+ return { cls, fx: this.expected(deriveExpected(next)) };
7714
+ }
7045
7715
  node(p) {
7046
7716
  const lazyRef = p._def.tag === "lazy";
7047
7717
  const scopeKey = lazyRef ? this.activeTrivia : void 0;
@@ -7166,6 +7836,10 @@ var Encoder = class {
7166
7836
  return head;
7167
7837
  }
7168
7838
  case "choice": {
7839
+ if (this.plan === void 0) {
7840
+ const factored = factorSharedEnds(d);
7841
+ if (factored !== void 0) return this.node(factored).ip;
7842
+ }
7169
7843
  if (d.strategy.tag === "greedyClassify") {
7170
7844
  const superIndex = d.strategy.superIndex;
7171
7845
  const sup = this.node(d.parsers[superIndex]).ip;
@@ -7189,9 +7863,12 @@ var Encoder = class {
7189
7863
  const kids = arms.map((c, k) => {
7190
7864
  const src = order[k];
7191
7865
  const checks = d.autoNot[src];
7192
- const inner = this.node(c).ip;
7193
- const ip = checks === null || checks === void 0 || checks.length === 0 ? inner : this.reject(inner, checks);
7194
7866
  const gate = d.gates[src];
7867
+ const plain2 = (checks === null || checks === void 0 || checks.length === 0) && (gate === null || gate === void 0);
7868
+ const routedGate = plain2 ? this.routedArmGate(c) : void 0;
7869
+ const body = this.node(c).ip;
7870
+ const inner = routedGate === void 0 ? body : this.emit(OP_RGATE, routedGate.cls, body, routedGate.fx);
7871
+ const ip = checks === null || checks === void 0 || checks.length === 0 ? inner : this.reject(inner, checks);
7195
7872
  const gated = gate === null || gate === void 0 ? ip : this.emit(OP_ARMGATE, this.fn(gate, d.gateSrcs?.[src] ?? null), ip, this.expected(deriveExpected(c)));
7196
7873
  return this.covWrap(gated, armCovIds?.[src], 1);
7197
7874
  });
@@ -7515,6 +8192,7 @@ var Encoder = class {
7515
8192
  const slots = (ip) => {
7516
8193
  switch (this.code[ip]) {
7517
8194
  case OP_GATE:
8195
+ case OP_RGATE:
7518
8196
  return [ip + 2];
7519
8197
  case OP_RULE:
7520
8198
  case OP_OPT:
@@ -8742,6 +9420,7 @@ function childSlots(code, ip, out) {
8742
9420
  case OP_LEAF:
8743
9421
  case OP_SOURCE_LEAF:
8744
9422
  case OP_ARMGATE:
9423
+ case OP_RGATE:
8745
9424
  out.push(code[ip + 2]);
8746
9425
  return true;
8747
9426
  // `ROUTED fallback` — the operand is an offset or −1. The routed token itself is
@@ -8825,6 +9504,42 @@ function reachableSites(code, roots) {
8825
9504
  }
8826
9505
  return seen;
8827
9506
  }
9507
+ function passCollapse(code, ip, hostCst) {
9508
+ const flags = code[ip + 3];
9509
+ if (hostCst || code[ip] !== OP_NODE || code[ip + 1] < 0 || code[ip + 4] >= 0) return false;
9510
+ if (flags !== 32 && flags !== 34) return false;
9511
+ let at = code[ip + 2];
9512
+ for (let n = 0; n < 8; n++) {
9513
+ const op = code[at];
9514
+ if (op === OP_GATE || op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[at + 3] === 0) at = code[at + 2];
9515
+ else if (op === OP_RULE) at = code[at + 1];
9516
+ else break;
9517
+ }
9518
+ return code[at] === OP_NODE;
9519
+ }
9520
+ function gatedPassCollapse(code, ip, hostCst) {
9521
+ const flags = code[ip + 3];
9522
+ if (hostCst || code[ip] !== OP_NODE || code[ip + 1] < 0 || code[ip + 4] >= 0) return void 0;
9523
+ if (flags !== 32 && flags !== 34) return void 0;
9524
+ const seq = code[ip + 2];
9525
+ if (code[seq] !== OP_SEQ && code[seq] !== OP_SEQV) return void 0;
9526
+ const n = code[seq + 1];
9527
+ if (n < 2) return void 0;
9528
+ const leads = [];
9529
+ const lits = [];
9530
+ for (let i = 0; i < n - 1; i++) {
9531
+ const t2 = code[seq + 2 + i];
9532
+ if (code[t2] !== OP_OPT && code[t2] !== OP_NOT) return void 0;
9533
+ const g = code[t2 + 1];
9534
+ if (code[g] === OP_GATE) leads.push(g);
9535
+ else if (code[g] === OP_LIT) lits.push(code[g + 1]);
9536
+ else return void 0;
9537
+ }
9538
+ const last = code[seq + 2 + n - 1];
9539
+ let at = last;
9540
+ 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];
9541
+ return code[at] === OP_NODE ? { leads, lits, last } : void 0;
9542
+ }
8828
9543
  function transfer(code, ip, at, hostCst) {
8829
9544
  const op = code[ip];
8830
9545
  if (op === OP_SCOPE || op === OP_SCOPE_CAP || op === OP_SCOPE_PLAIN) {
@@ -8836,14 +9551,18 @@ function transfer(code, ip, at, hostCst) {
8836
9551
  }
8837
9552
  if (op === OP_NODE || op === OP_NODE_TRACK) {
8838
9553
  const flags = code[ip + 3];
9554
+ if (passCollapse(code, ip, hostCst)) return at;
9555
+ const gated = gatedPassCollapse(code, ip, hostCst) !== void 0;
8839
9556
  const directChildren = !hostCst && op === OP_NODE && code[ip + 1] >= 0 && code[ip + 4] < 0 && ((flags & ~128) === 2 || flags === 18 || flags === 34);
8840
9557
  if (directChildren) {
8841
- return { tri: at.tri, buf: false, raw: RAW_UNKNOWN, cap: CAP_OFF };
9558
+ const framed2 = { tri: at.tri, buf: false, raw: RAW_UNKNOWN, cap: CAP_OFF };
9559
+ return gated ? meet(at, framed2) : framed2;
8842
9560
  }
8843
9561
  const cap = (flags & 4) !== 0 || hostCst ? CAP_ON : CAP_OFF;
8844
9562
  const raw = !hostCst && code[ip + 1] >= 0 && code[ip + 4] < 0 && (flags & 2) !== 0 ? RAW_OMIT : RAW_CAPTURE;
8845
9563
  if (at.buf && raw === at.raw && cap === at.cap) return at;
8846
- return { tri: at.tri, buf: true, raw, cap };
9564
+ const framed = { tri: at.tri, buf: true, raw, cap };
9565
+ return gated ? meet(at, framed) : framed;
8847
9566
  }
8848
9567
  if (op === OP_TOKEN) {
8849
9568
  if (at.tri === TRI_NONE && !at.buf) return at;
@@ -8881,6 +9600,40 @@ function computeSiteLabels(code, roots, hostCst) {
8881
9600
  }
8882
9601
  return { at: (ip) => labels.get(ip) ?? TOP };
8883
9602
  }
9603
+ function scopeIsIdentity(code, ip, at) {
9604
+ const op = code[ip];
9605
+ if (op !== OP_SCOPE_PLAIN && !(op === OP_SCOPE && code[ip + 3] === 0)) return false;
9606
+ const ki = code[ip + 1];
9607
+ return at.tri === (ki < 0 ? TRI_NONE : ki);
9608
+ }
9609
+ function scanHandoffNeeded(code, ip, labels) {
9610
+ const seen = /* @__PURE__ */ new Set();
9611
+ const stack = [ip];
9612
+ const kids = [];
9613
+ while (stack.length > 0) {
9614
+ const at = stack.pop();
9615
+ if (seen.has(at)) continue;
9616
+ seen.add(at);
9617
+ const l = labels.at(at);
9618
+ if (l.tri === TRI_UNKNOWN) return true;
9619
+ const op = code[at];
9620
+ if (op === OP_SCAN) return true;
9621
+ if (op === OP_TOKEN || op === OP_SCOPE_CAP) continue;
9622
+ 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;
9623
+ kids.length = 0;
9624
+ if (!childSlots(code, at, kids)) return true;
9625
+ for (const c of kids) stack.push(c);
9626
+ }
9627
+ return false;
9628
+ }
9629
+ function triviaLeadRanges(tri, trivia2) {
9630
+ if (tri === TRI_NONE) return [];
9631
+ if (tri < 0) return void 0;
9632
+ const fs = trivia2[tri]?._meta.firstSet;
9633
+ if (fs === void 0) return void 0;
9634
+ if (fs.kind === "empty") return [];
9635
+ return fs.kind === "ranges" && fs.ranges !== void 0 ? fs.ranges : void 0;
9636
+ }
8884
9637
 
8885
9638
  // src/table/scalar-terminal.ts
8886
9639
  function makeScalarRecognizer(op, spec) {
@@ -8918,6 +9671,12 @@ function scalarTerminalNotChild(code, ip) {
8918
9671
  const op = code[child];
8919
9672
  return op === OP_RX || op === OP_LIT ? child : -1;
8920
9673
  }
9674
+ function scalarTerminalPeekChild(code, ip) {
9675
+ if (code[ip] !== OP_PEEK) return -1;
9676
+ const child = code[ip + 1];
9677
+ const op = code[child];
9678
+ return op === OP_RX || op === OP_LIT ? child : -1;
9679
+ }
8921
9680
  function leadingScalarTerminal(code, ip, minDepth = 2, throughAttempt = true, throughPeek = false) {
8922
9681
  const seen = /* @__PURE__ */ new Set();
8923
9682
  let at = ip;
@@ -9095,6 +9854,59 @@ function leadingTriviaFieldAt(code, k, ip) {
9095
9854
  return code[ip] === OP_FIELD && (fieldSpec(k[code[ip + 1]]).bits & FIELD_LEADING_TRIVIA) !== 0;
9096
9855
  }
9097
9856
 
9857
+ // src/table/hold-gap.ts
9858
+ var WRAPPERS = /* @__PURE__ */ new Set([
9859
+ OP_NODE,
9860
+ OP_XFORM,
9861
+ OP_LEAF,
9862
+ OP_SOURCE_LEAF,
9863
+ OP_TOKEN,
9864
+ OP_SCOPE,
9865
+ OP_SCOPE_CAP,
9866
+ OP_SCOPE_PLAIN,
9867
+ OP_FIELD,
9868
+ OP_LABEL,
9869
+ OP_EXPECT,
9870
+ OP_WITHCTX,
9871
+ OP_RULE,
9872
+ OP_ATTEMPT,
9873
+ OP_COV
9874
+ ]);
9875
+ function rowNullable(code, k, ip, memo) {
9876
+ const hit = memo.get(ip);
9877
+ if (hit !== void 0) return hit;
9878
+ memo.set(ip, true);
9879
+ const op = code[ip];
9880
+ const kids = [];
9881
+ let out = true;
9882
+ if (op === OP_GATE) out = false;
9883
+ else if (op === OP_LIT || op === OP_LIT_CI) out = k[code[ip + 1]].length === 0;
9884
+ else if (op === OP_RX) {
9885
+ const re = k[code[ip + 1]];
9886
+ out = !(re instanceof RegExp) || regexCanMatchEmpty(re.source);
9887
+ } else if (op === OP_SEQ || op === OP_SEQV || op === OP_SEQX) {
9888
+ childSlots(code, ip, kids);
9889
+ out = kids.every((c) => rowNullable(code, k, c, memo));
9890
+ } else if (op === OP_CHOICE) {
9891
+ childSlots(code, ip, kids);
9892
+ out = kids.length === 0 || kids.some((c) => rowNullable(code, k, c, memo));
9893
+ } else if (op === OP_REP || op === OP_REPV) {
9894
+ out = code[ip + 2] === 0 || rowNullable(code, k, code[ip + 1], memo);
9895
+ } else if (WRAPPERS.has(op)) {
9896
+ if (childSlots(code, ip, kids) && kids.length === 1) out = rowNullable(code, k, kids[0], memo);
9897
+ }
9898
+ memo.set(ip, out);
9899
+ return out;
9900
+ }
9901
+ function holdsGap(code, k, base, n, i, memo) {
9902
+ if (i < 1 || i + 1 >= n) return false;
9903
+ const term = code[base + i];
9904
+ if (code[term] !== OP_NOT && code[term] !== OP_PEEK) return false;
9905
+ const next = code[base + i + 1];
9906
+ if (code[next] === OP_ADJ || leadingTriviaFieldAt(code, k, next)) return false;
9907
+ return !rowNullable(code, k, next, memo);
9908
+ }
9909
+
9098
9910
  // src/table/emit-assembly.ts
9099
9911
  var Unemittable = class extends Error {
9100
9912
  construct;
@@ -9175,7 +9987,10 @@ var EMITTED_PARAMS = [
9175
9987
  "triviaFieldCapture",
9176
9988
  // Appended for the deferred-commit trivia skip (`trivia-gap.ts`); old
9177
9989
  // factories ignore it.
9178
- "skipDeferredTrivia"
9990
+ "skipDeferredTrivia",
9991
+ // Appended for the pass-through collapse (`run-support.ts`); old factories
9992
+ // ignore it.
9993
+ "collapseEmpty"
9179
9994
  ];
9180
9995
  var RUNTIME_PRELUDE = `
9181
9996
  let _pfScan=null
@@ -9372,6 +10187,24 @@ ${fast}
9372
10187
  ${scanned}
9373
10188
  }`;
9374
10189
  }
10190
+ function emitHoldTerm(callee, dst, t2, l, skip) {
10191
+ const fast = `const ${t2}v=${callee}(input,cur,ctx)
10192
+ if(${t2}v===FAIL)return FAIL
10193
+ ${dst}=${t2}v
10194
+ cur=EC.e`;
10195
+ if (l.tri === TRI_NONE) return fast;
10196
+ const held = `const ${t2}s=${skip}(input,cur,ctx)
10197
+ const ${t2}v=${callee}(input,${t2}s,ctx)
10198
+ if(${t2}v===FAIL)return FAIL
10199
+ ${dst}=${t2}v
10200
+ cur=${t2}s`;
10201
+ if (l.tri !== TRI_UNKNOWN) return held;
10202
+ return `if(ctx.trivia===undefined){
10203
+ ${fast}
10204
+ }else{
10205
+ ${held}
10206
+ }`;
10207
+ }
9375
10208
  function leadSkip(hasTrivia, leadTrivia, skip) {
9376
10209
  if (hasTrivia === "false") return "{}";
9377
10210
  const call = `itemStart=${skip}(input,itemStart,ctx)`;
@@ -9413,6 +10246,7 @@ function emitAssemblySource(t2, prog, cfg, extraIps = [], staticBuild = false, s
9413
10246
  const reachable = [...reachableSites(code, roots)];
9414
10247
  const labels = computeSiteLabels(code, roots, hostCst);
9415
10248
  const scalarSpecs = /* @__PURE__ */ new Set();
10249
+ const nullable2 = /* @__PURE__ */ new Map();
9416
10250
  const largeChoiceScalarSpecs = /* @__PURE__ */ new Set();
9417
10251
  const largeChoiceLiteralFamilies = /* @__PURE__ */ new Map();
9418
10252
  const largeChoiceScalarSequences = /* @__PURE__ */ new Map();
@@ -9737,9 +10571,7 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
9737
10571
  const op = code[ip];
9738
10572
  if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2];
9739
10573
  if (op === OP_RULE) return code[ip + 1];
9740
- if ((op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[ip + 3] === 0) && code[ip + 1] >= 0 && labels.at(ip).tri === code[ip + 1]) {
9741
- return code[ip + 2];
9742
- }
10574
+ if (scopeIsIdentity(code, ip, labels.at(ip))) return code[ip + 2];
9743
10575
  return void 0;
9744
10576
  }
9745
10577
  function lower(ip, fname) {
@@ -9861,6 +10693,22 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
9861
10693
  return `${head}
9862
10694
  if(${gateMissTest(ip)}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
9863
10695
  return ${child}(input,pos,ctx)
10696
+ }`;
10697
+ }
10698
+ case OP_RGATE: {
10699
+ const child = link(code[ip + 2]);
10700
+ if (cfg.tolerant || cfg.probe) return `${head}
10701
+ return ${child}(input,pos,ctx)
10702
+ }`;
10703
+ const resolved = t2.cc[code[ip + 1]];
10704
+ const ci = classes.push([resolved]) - 1;
10705
+ classPlan.push([code[ip + 1]]);
10706
+ const cls = hoist("cl", `CLS[${ci}][0]`);
10707
+ const xf = fxRef(code[ip + 3]);
10708
+ return `${head}
10709
+ const it=ctx._routed
10710
+ if(it!==undefined&&it.span.start===pos&&!classHas(${cls},lead(input,it.span.end))){ctx._fe=it.span.end;ctx._fx=${xf};return FAIL}
10711
+ return ${child}(input,pos,ctx)
9864
10712
  }`;
9865
10713
  }
9866
10714
  case OP_ARMGATE: {
@@ -10135,23 +10983,33 @@ return FAIL
10135
10983
  const p = tmp();
10136
10984
  return `${head}
10137
10985
  ${lookaheadMiss(code[ip + 1], "EC.e=pos;return null")}${emitMark(p, L.buf, L.raw, sinks)}
10138
- const v=${child}(input,pos,ctx)
10986
+ ${cfg.probe ? "const pb=ctx._probe?.best\n" : ""}const v=${child}(input,pos,ctx)
10139
10987
  ${emitRollback(p, L.buf, L.raw, sinks)}
10140
- if(v===FAIL){EC.e=pos;return null}
10988
+ ${cfg.probe ? "if(ctx._probe!==undefined)ctx._probe.best=pb??null\n" : ""}if(v===FAIL){EC.e=pos;return null}
10141
10989
  ctx._fe=pos
10142
10990
  ctx._fx=${xf}
10143
10991
  return FAIL
10144
10992
  }`;
10145
10993
  }
10146
10994
  case OP_PEEK: {
10995
+ const scalarChild = scalarTerminalPeekChild(code, ip);
10996
+ if (scalarChild >= 0) {
10997
+ const recognize = recognizerRef(code[scalarChild + 1]);
10998
+ const xf2 = fxRef(code[ip + 2]);
10999
+ return `${head}
11000
+ if(${recognize}(input,pos)<0){ctx._fe=pos;ctx._fx=${xf2};return FAIL}
11001
+ EC.e=pos
11002
+ return null
11003
+ }`;
11004
+ }
10147
11005
  const child = link(code[ip + 1]);
10148
11006
  const xf = fxRef(code[ip + 2]);
10149
11007
  const p = tmp();
10150
11008
  return `${head}
10151
11009
  ${lookaheadMiss(code[ip + 1], `ctx._fe=pos;ctx._fx=${xf};return FAIL`)}${emitMark(p, L.buf, L.raw, sinks)}
10152
- const v=${child}(input,pos,ctx)
11010
+ ${cfg.probe ? "const pb=ctx._probe?.best\n" : ""}const v=${child}(input,pos,ctx)
10153
11011
  ${emitRollback(p, L.buf, L.raw, sinks)}
10154
- if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
11012
+ ${cfg.probe ? "if(ctx._probe!==undefined)ctx._probe.best=pb??null\n" : ""}if(v===FAIL){ctx._fe=pos;ctx._fx=${xf};return FAIL}
10155
11013
  EC.e=pos
10156
11014
  return null
10157
11015
  }`;
@@ -10222,7 +11080,7 @@ return v
10222
11080
  );
10223
11081
  } else {
10224
11082
  const callee = leadingTriviaFieldAt(code, k, childIp) ? `(LEAD.at=cur,${kids[i]})` : kids[i];
10225
- parts.push(emitTerm(callee, vn, tmp(), L, skipFor(L)));
11083
+ parts.push(holdsGap(code, k, base, n, i, nullable2) ? emitHoldTerm(callee, vn, tmp(), L, skipFor(L)) : emitTerm(callee, vn, tmp(), L, skipFor(L)));
10226
11084
  }
10227
11085
  names.push(vn);
10228
11086
  }
@@ -10705,6 +11563,26 @@ return nd
10705
11563
  const trailingTrivia = (flags & 128) !== 0;
10706
11564
  const build = buildIdx >= 0 ? fnRef(buildIdx) : void 0;
10707
11565
  const leanFlags = flags & ~128;
11566
+ if (build !== void 0 && passCollapse(code, ip, hostCst)) {
11567
+ return `${head}
11568
+ const v=${child}(input,pos,ctx)
11569
+ if(v!==undefined)return v
11570
+ return collapseEmpty(ctx,${build},input,pos,EC.e,${flags === 32})
11571
+ }`;
11572
+ }
11573
+ const gated = build !== void 0 && !cfg.tolerant && !cfg.probe ? gatedPassCollapse(code, ip, hostCst) : void 0;
11574
+ const triviaLead = gated === void 0 ? void 0 : triviaLeadRanges(labels.at(code[ip + 2]).tri, t2.trivia);
11575
+ const gp = gated !== void 0 && triviaLead !== void 0 && triviaLead.length <= 16 ? `const gc=lead(input,pos)
11576
+ if(!(${[
11577
+ ...gated.leads.map((g) => `!(${gateMissTest(g)})`),
11578
+ ...gated.lits.map((i) => `gc===${k[i].codePointAt(0)}`),
11579
+ ...triviaLead.map((r) => r.lo === r.hi ? `gc===${r.lo}` : `(gc>=${r.lo}&&gc<=${r.hi})`)
11580
+ ].join("||") || "false"})){
11581
+ const gv=${link(gated.last)}(input,pos,ctx)
11582
+ if(gv!==undefined)return gv
11583
+ return collapseEmpty(ctx,${build},input,pos,EC.e,${flags === 32})
11584
+ }
11585
+ ` : "";
10708
11586
  if (!hostCst && !tracked && build !== void 0 && proj < 0 && (leanFlags === 2 || (flags === 18 || flags === 34))) {
10709
11587
  const fields = flags === 18;
10710
11588
  const collapseChildren = flags === 34;
@@ -10739,7 +11617,7 @@ sCh.push(nd)
10739
11617
  if(sRaw!==undefined)sRaw.push(rawEntry(nd,input,pos,end))
10740
11618
  }`;
10741
11619
  return `${head}
10742
- const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
11620
+ ${gp}const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
10743
11621
  const sCap=ctx.captureTrivia,sBuf=ctx._cstBuf,sFields=ctx._fields
10744
11622
  const flat=[]
10745
11623
  ctx._cstBuf=undefined
@@ -10814,7 +11692,7 @@ const hostKids=kids
10814
11692
  const rawKids=buf.raw??(buf.rawSingle!==undefined?[buf.rawSingle]:EMPTY_CH)
10815
11693
  const tlog=buf.tl??EMPTY_TLOG`;
10816
11694
  return `${head}
10817
- const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
11695
+ ${!hostCst && !tracked ? gp : ""}const sCh=ctx._cstChildren,sLv=ctx._cstLeaves,sRaw=ctx._cstRawChildren,sTl=ctx._cstTriviaLog
10818
11696
  const sCap=ctx.captureTrivia,sBuf=ctx._cstBuf
10819
11697
  ${openCapture}
10820
11698
  ctx.captureTrivia=${captureWide}
@@ -10884,9 +11762,15 @@ ext:[${externals.join(",")}],
10884
11762
  bind:function(xs,ps){_xScan=xs
10885
11763
  ${externals.map((ip, i) => `_xp${ip}=ps[${i}]`).join("\n")}},
10886
11764
  scan:function(s){const o=_pfScan;_pfScan=s;return o}`;
11765
+ const scannerFor = (ip) => {
11766
+ const ki = labels.at(ip).tri;
11767
+ if (ki === TRI_UNKNOWN) return "_pfScan";
11768
+ if (ki === TRI_NONE) return "null";
11769
+ return swapLegal && !triviaLabelled[ki] && t2.triviaScan[ki] != null ? hoist("ts", `TRIVIASCAN[${ki}]`) : "null";
11770
+ };
10887
11771
  const externalDefs = select === void 0 ? "" : `let _xScan
10888
11772
  ${externals.map((ip) => `let _xp${ip}
10889
- function _px${ip}(input,pos,ctx){const o=_xScan(_pfScan);const v=_xp${ip}(input,pos,ctx);_xScan(o);return v}`).join("\n")}
11773
+ function _px${ip}(input,pos,ctx){const o=_xScan(${scannerFor(ip)});const v=_xp${ip}(input,pos,ctx);_xScan(o);return v}`).join("\n")}
10890
11774
  `;
10891
11775
  const source = `${RUNTIME_PRELUDE}${needsNoRawPrelude ? NO_RAW_RUNTIME_PRELUDE : ""}
10892
11776
  ${prelude.join("\n")}
@@ -11203,136 +12087,6 @@ function literal(value, opts = {}) {
11203
12087
  };
11204
12088
  }
11205
12089
 
11206
- // src/combinators/sequence.ts
11207
- function sequence(...parsers) {
11208
- let adjacency = null;
11209
- for (let i = 0; i < parsers.length; i++) {
11210
- const a = adjacencyOf(parsers[i]);
11211
- if (a === null) continue;
11212
- if (i === 0) {
11213
- throw new TypeError(
11214
- `sequence(): ${a.polarity}() cannot be the FIRST term \u2014 an adjacency assertion tests the gap after the PRECEDING term. Put a concrete term first.`
11215
- );
11216
- }
11217
- ;
11218
- (adjacency ??= Array.from({ length: parsers.length }, () => null))[i] = a;
11219
- }
11220
- const hasAdjacency = adjacency !== null;
11221
- let leading = null;
11222
- for (let i = 1; i < parsers.length; i++) {
11223
- if (leadsTrivia(parsers[i])) (leading ??= Array.from({ length: parsers.length }, () => false))[i] = true;
11224
- }
11225
- const strictArity = !hasAdjacency && (parsers.length === 2 && !matchesEmpty(parsers[1]) || parsers.length === 3 && parsers.every((p) => !matchesEmpty(p))) ? parsers.length : 0;
11226
- const meta = {
11227
- // Union through the nullable prefix — a leading optional()/many() lets a later
11228
- // term's first char start the sequence. Just `parsers[0]` under-approximates.
11229
- firstSet: sequenceFirstSet(parsers),
11230
- canMatchNewline: parsers.some((p) => p._meta.canMatchNewline),
11231
- isTrivia: false
11232
- };
11233
- const def = { tag: "sequence", parsers };
11234
- const scalarParsers = parsers.map(scalarOf);
11235
- const parseScalar = (input, pos, ctx) => {
11236
- let cur = pos;
11237
- if (strictArity !== 0) {
11238
- cur = scalarParsers[0](input, cur, ctx);
11239
- if (cur < 0) return cur;
11240
- const first = ctx._sv;
11241
- if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
11242
- cur = scalarParsers[1](input, cur, ctx);
11243
- if (cur < 0) return cur;
11244
- const second = ctx._sv;
11245
- if (strictArity === 2) {
11246
- ctx._sv = def.valueUnused ? void 0 : [first, second];
11247
- return cur;
11248
- }
11249
- if (ctx.trivia) cur = advanceTrivia(input, cur, ctx);
11250
- cur = scalarParsers[2](input, cur, ctx);
11251
- if (cur < 0) return cur;
11252
- ctx._sv = def.valueUnused ? void 0 : [first, second, ctx._sv];
11253
- return cur;
11254
- }
11255
- const values = def.valueUnused ? void 0 : [];
11256
- for (let i = 0; i < scalarParsers.length; i++) {
11257
- let termPos = cur;
11258
- if (i > 0 && ctx.trivia) termPos = advanceTrivia(input, cur, ctx);
11259
- const end = scalarParsers[i](input, termPos, ctx);
11260
- if (end < 0) return end;
11261
- if (values !== void 0) values.push(ctx._sv);
11262
- if (end > termPos || termPos === cur) cur = end;
11263
- }
11264
- ctx._sv = values;
11265
- return cur;
11266
- };
11267
- let followSentinels;
11268
- return {
11269
- _tag: "sequence",
11270
- _meta: meta,
11271
- _def: def,
11272
- _parseScalar: hasAdjacency || leading !== null ? void 0 : parseScalar,
11273
- parse(input, pos, ctx) {
11274
- const tolerant = ctx._tolerant === true;
11275
- if (tolerant) {
11276
- followSentinels ??= parsers.map((_, i) => firstSetSentinel(
11277
- parsers.slice(i + 1).reduce(
11278
- (set, parser2) => union(set, firstSetOf(parser2)),
11279
- { kind: "empty" }
11280
- )
11281
- ));
11282
- }
11283
- const inheritedSync = ctx._sync;
11284
- const deferredTrivia = needsDeferredTriviaCommit(ctx);
11285
- const values = def.valueUnused ? void 0 : [];
11286
- let cur = pos;
11287
- try {
11288
- for (let i = 0; i < parsers.length; i++) {
11289
- if (tolerant) ctx._sync = followSentinels[i] ?? inheritedSync;
11290
- const assertion = adjacency?.[i];
11291
- if (assertion) {
11292
- if (!holdsAdjacency(input, cur, ctx, assertion)) return adjacencyFail(cur, assertion);
11293
- if (values !== void 0) values.push(null);
11294
- continue;
11295
- }
11296
- let termPos = cur;
11297
- const markTlog = deferredTrivia ? cstTlLen(ctx) : 0;
11298
- const markLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
11299
- const markRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
11300
- if (ctx.trivia && i > 0) {
11301
- termPos = deferredTrivia ? commitTriviaScan(scanTriviaCompact(input, cur, ctx)) : advanceTrivia(input, cur, ctx);
11302
- }
11303
- const scanTlog = deferredTrivia ? cstTlLen(ctx) : 0;
11304
- const scanLog = deferredTrivia ? ctx._triviaLog?.length ?? 0 : 0;
11305
- const scanRootLog = deferredTrivia ? ctx._rootTriviaLog?.length ?? 0 : 0;
11306
- if (leading?.[i] === true) LEAD.at = cur;
11307
- const result = parsers[i].parse(input, termPos, ctx);
11308
- if (!result.ok) return result;
11309
- if (values !== void 0) values.push(result.value);
11310
- if (result.span.end > termPos || termPos === cur) cur = result.span.end;
11311
- else if (deferredTrivia) {
11312
- rollbackScannedTriviaAt(
11313
- ctx,
11314
- markTlog,
11315
- scanTlog,
11316
- markLog,
11317
- scanLog,
11318
- markRootLog,
11319
- scanRootLog
11320
- );
11321
- }
11322
- }
11323
- } finally {
11324
- if (tolerant) ctx._sync = inheritedSync;
11325
- }
11326
- return {
11327
- ok: true,
11328
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
11329
- value: values ?? void 0,
11330
- span: { start: pos, end: cur }
11331
- };
11332
- }
11333
- };
11334
- }
11335
-
11336
12090
  // src/combinators/ref.ts
11337
12091
  function withRuleTrivia(trivia2, ctx, run) {
11338
12092
  const savedLabels = ctx.triviaKindLabels;
@@ -11358,164 +12112,46 @@ function ref() {
11358
12112
  const slot = {
11359
12113
  _tag: "lazy",
11360
12114
  _meta: meta,
11361
- _def: {
11362
- tag: "lazy",
11363
- thunk: () => {
11364
- if (!resolved) throw UNDEFINED_REF;
11365
- return resolved;
11366
- }
11367
- },
11368
- _parseScalar(input, pos, ctx) {
11369
- const scalar = resolvedScalar;
11370
- if (!scalar) throw new Error("ref<T>() used before .define() was called");
11371
- const gt = meta.grammarTrivia;
11372
- if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
11373
- return withRuleTrivia(gt, ctx, () => scalar(input, pos, ctx));
11374
- }
11375
- return scalar(input, pos, ctx);
11376
- },
11377
- parse(input, pos, ctx) {
11378
- const parser2 = resolved;
11379
- if (!parser2) throw new Error("ref<T>() used before .define() was called");
11380
- const gt = meta.grammarTrivia;
11381
- if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
11382
- return withRuleTrivia(gt, ctx, () => parser2.parse(input, pos, ctx));
11383
- }
11384
- return parser2.parse(input, pos, ctx);
11385
- },
11386
- define(p) {
11387
- if (resolved) throw new Error("ref<T>() already defined");
11388
- resolved = p;
11389
- resolvedScalar = scalarOf(p);
11390
- ownBoundary = hasOwnTriviaBoundary(p);
11391
- meta.firstSet = p._meta.firstSet;
11392
- meta.canMatchNewline = p._meta.canMatchNewline;
11393
- meta.isTrivia = p._meta.isTrivia;
11394
- if (p._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = p._meta.triviaKindLabels;
11395
- else delete meta.triviaKindLabels;
11396
- if (p._meta.disjoint !== void 0) meta.disjoint = p._meta.disjoint;
11397
- else delete meta.disjoint;
11398
- }
11399
- };
11400
- return slot;
11401
- }
11402
-
11403
- // src/combinators/token.ts
11404
- function semanticLeafValue(_ctx, _input, _start, _end, value) {
11405
- return value;
11406
- }
11407
- function sourceLeafValue(ctx, input, start, end, value) {
11408
- return cstOutputHost(ctx.build) ? input.slice(start, end) : value;
11409
- }
11410
- function token(root) {
11411
- const meta = {
11412
- firstSet: root._meta.firstSet,
11413
- canMatchNewline: root._meta.canMatchNewline,
11414
- isTrivia: false
11415
- };
11416
- return {
11417
- _tag: "token",
11418
- _meta: meta,
11419
- _def: { tag: "token", parser: root },
11420
- parse(input, pos, ctx) {
11421
- const savedTrivia = ctx.trivia;
11422
- const savedKinds = ctx.triviaKindLabels;
11423
- const savedBuf = ctx._cstBuf;
11424
- const savedChildren = ctx._cstChildren;
11425
- const savedLeaves = ctx._cstLeaves;
11426
- const savedRaw = ctx._cstRawChildren;
11427
- const savedTriviaLog = ctx._cstTriviaLog;
11428
- const savedOuterTriviaLog = ctx._triviaLog;
11429
- const savedRootTriviaLog = ctx._rootTriviaLog;
11430
- const wasCapturing = cstCaptureActive(ctx);
11431
- ctx.trivia = void 0;
11432
- ctx.triviaKindLabels = void 0;
11433
- ctx._cstBuf = void 0;
11434
- ctx._cstChildren = void 0;
11435
- ctx._cstLeaves = void 0;
11436
- ctx._cstRawChildren = void 0;
11437
- ctx._cstTriviaLog = void 0;
11438
- ctx._triviaLog = void 0;
11439
- ctx._rootTriviaLog = void 0;
11440
- let result;
11441
- try {
11442
- result = root.parse(input, pos, ctx);
11443
- } finally {
11444
- ctx.trivia = savedTrivia;
11445
- ctx.triviaKindLabels = savedKinds;
11446
- ctx._cstBuf = savedBuf;
11447
- ctx._cstChildren = savedChildren;
11448
- ctx._cstLeaves = savedLeaves;
11449
- ctx._cstRawChildren = savedRaw;
11450
- ctx._cstTriviaLog = savedTriviaLog;
11451
- ctx._triviaLog = savedOuterTriviaLog;
11452
- ctx._rootTriviaLog = savedRootTriviaLog;
11453
- }
11454
- if (!result.ok) return result;
11455
- const span = result.span;
11456
- const value = input.slice(pos, span.end);
11457
- if (wasCapturing) pushCstLeaf(ctx, { _tag: "leaf", value, span });
11458
- return { ok: true, value, span };
11459
- }
11460
- };
11461
- }
11462
- function makeLeaf(root, fn, sourceCstValue) {
11463
- const cstValue = sourceCstValue ? sourceLeafValue : semanticLeafValue;
11464
- const meta = {
11465
- firstSet: root._meta.firstSet,
11466
- canMatchNewline: root._meta.canMatchNewline,
11467
- isTrivia: false
11468
- };
11469
- return {
11470
- _tag: "leaf",
11471
- _meta: meta,
11472
- _def: {
11473
- tag: "leaf",
11474
- parser: root,
11475
- fn,
11476
- ...sourceCstValue ? { cstValue: "source" } : {}
12115
+ _def: {
12116
+ tag: "lazy",
12117
+ thunk: () => {
12118
+ if (!resolved) throw UNDEFINED_REF;
12119
+ return resolved;
12120
+ }
12121
+ },
12122
+ _parseScalar(input, pos, ctx) {
12123
+ const scalar = resolvedScalar;
12124
+ if (!scalar) throw new Error("ref<T>() used before .define() was called");
12125
+ const gt = meta.grammarTrivia;
12126
+ if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
12127
+ return withRuleTrivia(gt, ctx, () => scalar(input, pos, ctx));
12128
+ }
12129
+ return scalar(input, pos, ctx);
11477
12130
  },
11478
12131
  parse(input, pos, ctx) {
11479
- const savedBuf = ctx._cstBuf;
11480
- const savedChildren = ctx._cstChildren;
11481
- const savedLeaves = ctx._cstLeaves;
11482
- const savedRaw = ctx._cstRawChildren;
11483
- const savedTriviaLog = ctx._cstTriviaLog;
11484
- const savedOuterTriviaLog = ctx._triviaLog;
11485
- const wasCapturing = cstCaptureActive(ctx);
11486
- ctx._cstBuf = void 0;
11487
- ctx._cstChildren = void 0;
11488
- ctx._cstLeaves = void 0;
11489
- ctx._cstRawChildren = void 0;
11490
- ctx._cstTriviaLog = void 0;
11491
- ctx._triviaLog = void 0;
11492
- let result;
11493
- try {
11494
- result = root.parse(input, pos, ctx);
11495
- } finally {
11496
- ctx._cstBuf = savedBuf;
11497
- ctx._cstChildren = savedChildren;
11498
- ctx._cstLeaves = savedLeaves;
11499
- ctx._cstRawChildren = savedRaw;
11500
- ctx._cstTriviaLog = savedTriviaLog;
11501
- ctx._triviaLog = savedOuterTriviaLog;
12132
+ const parser2 = resolved;
12133
+ if (!parser2) throw new Error("ref<T>() used before .define() was called");
12134
+ const gt = meta.grammarTrivia;
12135
+ if (gt !== void 0 && ownBoundary && ctx.trivia === void 0) {
12136
+ return withRuleTrivia(gt, ctx, () => parser2.parse(input, pos, ctx));
11502
12137
  }
11503
- if (!result.ok) return result;
11504
- const value = fn(result.value, result.span);
11505
- if (wasCapturing) pushCstLeaf(ctx, {
11506
- _tag: "leaf",
11507
- value: cstValue(ctx, input, pos, result.span.end, value),
11508
- span: result.span
11509
- });
11510
- return { ok: true, value, span: result.span };
12138
+ return parser2.parse(input, pos, ctx);
12139
+ },
12140
+ define(p) {
12141
+ if (resolved) throw new Error("ref<T>() already defined");
12142
+ resolved = p;
12143
+ resolvedScalar = scalarOf(p);
12144
+ ownBoundary = hasOwnTriviaBoundary(p);
12145
+ meta.firstSet = p._meta.firstSet;
12146
+ meta.canMatchNewline = p._meta.canMatchNewline;
12147
+ meta.isTrivia = p._meta.isTrivia;
12148
+ if (p._meta.triviaKindLabels !== void 0) meta.triviaKindLabels = p._meta.triviaKindLabels;
12149
+ else delete meta.triviaKindLabels;
12150
+ if (p._meta.disjoint !== void 0) meta.disjoint = p._meta.disjoint;
12151
+ else delete meta.disjoint;
11511
12152
  }
11512
12153
  };
11513
- }
11514
- function leaf(root, fn) {
11515
- return makeLeaf(root, fn, false);
11516
- }
11517
- function sourceLeaf(root, fn) {
11518
- return makeLeaf(root, fn, true);
12154
+ return slot;
11519
12155
  }
11520
12156
 
11521
12157
  // src/combinators/scanTo.ts
@@ -11826,37 +12462,120 @@ function clearOf(ranges, input, from, to) {
11826
12462
  return true;
11827
12463
  }
11828
12464
 
11829
- // src/table/run-support.ts
11830
- function lineCol(ctx, offset) {
11831
- const starts = ctx._lineStarts ?? [0];
11832
- let lo = 0, hi = starts.length - 1;
11833
- while (lo < hi) {
11834
- const mid = lo + hi + 1 >> 1;
11835
- if (starts[mid] <= offset) lo = mid;
11836
- else hi = mid - 1;
12465
+ // src/table/fail-clean.ts
12466
+ function passChild(code, ip) {
12467
+ switch (code[ip]) {
12468
+ case OP_RULE:
12469
+ case OP_LABEL:
12470
+ case OP_COV:
12471
+ case OP_TOKEN:
12472
+ return code[ip + 1];
12473
+ case OP_GATE:
12474
+ case OP_NODE:
12475
+ case OP_NODE_TRACK:
12476
+ case OP_XFORM:
12477
+ case OP_FIELD:
12478
+ case OP_SCOPE:
12479
+ case OP_SCOPE_CAP:
12480
+ case OP_SCOPE_PLAIN:
12481
+ case OP_WITHCTX:
12482
+ case OP_LEAF:
12483
+ case OP_SOURCE_LEAF:
12484
+ return code[ip + 2];
12485
+ default:
12486
+ return -1;
11837
12487
  }
11838
- return [lo + 1, offset - starts[lo] + 1];
11839
- }
11840
- function spanLines(ctx, start, end) {
11841
- const s = lineCol(ctx, start), e = lineCol(ctx, end);
11842
- return { start, end, startLine: s[0], startColumn: s[1], endLine: e[0], endColumn: e[1] };
11843
12488
  }
11844
- function rawEntry(v, input, s, e) {
11845
- if (typeof v === "object" && v !== null) {
11846
- const tg = v._tag;
11847
- if (tg === "node" || tg === "leaf" || tg === "parseError") return v;
11848
- }
11849
- return {
11850
- _tag: "leaf",
11851
- value: typeof v === "string" ? v : typeof v === "object" && v !== null ? input.slice(s, e) : "",
11852
- span: { start: s, end: e }
12489
+ function seqCount(code, ip) {
12490
+ const op = code[ip];
12491
+ return op === OP_SEQ || op === OP_SEQV ? ip + 1 : op === OP_SEQX ? ip + 2 : -1;
12492
+ }
12493
+ function failCleanAnalysis(code) {
12494
+ const cleanAt = new Uint8Array(code.length);
12495
+ const neverAt = new Uint8Array(code.length);
12496
+ const neverFails = (ip) => {
12497
+ const hit = neverAt[ip];
12498
+ if (hit !== 0) return hit === 1;
12499
+ neverAt[ip] = 3;
12500
+ let out = false;
12501
+ const op = code[ip];
12502
+ if (op === OP_OPT || op === OP_EMPTY || op === OP_EXPECT) out = true;
12503
+ else if (op === OP_REP || op === OP_REPV) out = code[ip + 2] === 0;
12504
+ else {
12505
+ const at = seqCount(code, ip);
12506
+ if (at >= 0) {
12507
+ out = true;
12508
+ for (let i = 1, n = code[at]; out && i <= n; i++) out = neverFails(code[at + i]);
12509
+ } else if (op !== OP_GATE) {
12510
+ const c = passChild(code, ip);
12511
+ out = c >= 0 && neverFails(c);
12512
+ }
12513
+ }
12514
+ neverAt[ip] = out ? 1 : 2;
12515
+ return out;
11853
12516
  };
11854
- }
11855
- function lead(input, pos) {
11856
- if (pos >= input.length) return -1;
11857
- const c = input.charCodeAt(pos);
11858
- if (c < 55296 || c > 56319) return c;
11859
- return input.codePointAt(pos) ?? c;
12517
+ const clean = (ip) => {
12518
+ const hit = cleanAt[ip];
12519
+ if (hit !== 0) return hit === 1;
12520
+ cleanAt[ip] = 3;
12521
+ let out = false;
12522
+ switch (code[ip]) {
12523
+ // A terminal pushes its leaf only on a match; NOT/PEEK/ATTEMPT restore
12524
+ // their own mark; OPT, EMPTY and EXPECT do not fail uncommitted.
12525
+ case OP_LIT:
12526
+ case OP_RX:
12527
+ case OP_LIT_TRACK:
12528
+ case OP_RX_TRACK:
12529
+ case OP_LIT_CI:
12530
+ case OP_LIT_CI_TRACK:
12531
+ case OP_GUARD:
12532
+ case OP_NOT:
12533
+ case OP_PEEK:
12534
+ case OP_ATTEMPT:
12535
+ case OP_EMPTY:
12536
+ case OP_OPT:
12537
+ case OP_EXPECT:
12538
+ out = true;
12539
+ break;
12540
+ // Min 0 does not fail uncommitted; min 1 fails only with no item kept, and
12541
+ // restores the failed item itself.
12542
+ case OP_REP:
12543
+ case OP_REPV:
12544
+ out = code[ip + 2] <= 1;
12545
+ break;
12546
+ // Only the first term may fail: a later one fails after the terms before it
12547
+ // wrote, and after the trivia scanned in front of it.
12548
+ case OP_SEQ:
12549
+ case OP_SEQV:
12550
+ case OP_SEQX: {
12551
+ const at = seqCount(code, ip);
12552
+ const n = code[at];
12553
+ out = n > 0 && clean(code[at + 1]);
12554
+ for (let i = 2; out && i <= n; i++) out = neverFails(code[at + i]);
12555
+ break;
12556
+ }
12557
+ case OP_CHOICE: {
12558
+ out = true;
12559
+ for (let i = 0, n = code[ip + 2]; out && i < n; i++) out = clean(code[ip + 4 + i]);
12560
+ break;
12561
+ }
12562
+ case OP_ROUTED:
12563
+ out = code[ip + 1] < 0 || clean(code[ip + 1]);
12564
+ break;
12565
+ // An arm's failure and the fallback's are committed; only the selector's is
12566
+ // not, and without a fallback an unknown key fails after the selector wrote.
12567
+ case OP_DISPATCH:
12568
+ out = code[ip + 3] >= 0 && clean(code[ip + 1]);
12569
+ break;
12570
+ default: {
12571
+ const c = passChild(code, ip);
12572
+ out = c >= 0 && clean(c);
12573
+ }
12574
+ }
12575
+ cleanAt[ip] = out ? 1 : 2;
12576
+ return out;
12577
+ };
12578
+ return clean;
11860
12579
  }
11861
12580
 
11862
12581
  // src/cst/host-mode.ts
@@ -12008,6 +12727,7 @@ function assemble(t2, prog, cfg) {
12008
12727
  const swapLegal = !cfg.trackLines;
12009
12728
  const hostCst = cfg.hostCst;
12010
12729
  const REC = prog.rec === 1 && cfg.tolerant;
12730
+ const PROBE = cfg.probe;
12011
12731
  const sentinels = /* @__PURE__ */ new Map();
12012
12732
  function sentinelFor(cls) {
12013
12733
  if (cls < 0) return void 0;
@@ -12053,6 +12773,8 @@ function assemble(t2, prog, cfg) {
12053
12773
  }
12054
12774
  const memo = /* @__PURE__ */ new Map();
12055
12775
  const reached = /* @__PURE__ */ new Set();
12776
+ const NULLABLE = /* @__PURE__ */ new Map();
12777
+ const CLEAN = REC ? () => false : failCleanAnalysis(code);
12056
12778
  function pushLeaf(ctx, value, s, e) {
12057
12779
  const lf = { _tag: "leaf", value, span: { start: s, end: e } };
12058
12780
  pushCstLeaf(ctx, lf);
@@ -12251,6 +12973,22 @@ function assemble(t2, prog, cfg) {
12251
12973
  return node2;
12252
12974
  };
12253
12975
  }
12976
+ function passCollapseNode(child, build, readsRaw) {
12977
+ return (input, pos, ctx) => {
12978
+ const v = child(input, pos, ctx);
12979
+ if (v !== void 0) return v;
12980
+ return collapseEmpty(ctx, build, input, pos, EC.e, readsRaw);
12981
+ };
12982
+ }
12983
+ function gatedPassCollapseNode(blockers, last, classic, build, readsRaw) {
12984
+ return (input, pos, ctx) => {
12985
+ const c = lead(input, pos);
12986
+ for (let i = 0; i < blockers.length; i++) if (classHas2(blockers[i], c)) return classic(input, pos, ctx);
12987
+ const v = last(input, pos, ctx);
12988
+ if (v !== void 0) return v;
12989
+ return collapseEmpty(ctx, build, input, pos, EC.e, readsRaw);
12990
+ };
12991
+ }
12254
12992
  function plainProjectNode(child, projection, type) {
12255
12993
  return (input, pos, ctx) => {
12256
12994
  const sCh = ctx._cstChildren;
@@ -12621,21 +13359,27 @@ function assemble(t2, prog, cfg) {
12621
13359
  }
12622
13360
  function dispatchBranch(child, usesRouted) {
12623
13361
  if (!usesRouted) {
12624
- return (input, _pos, selEnd, key, _selectorMark, ctx) => {
12625
- const mark = saveTriviaMark(ctx);
13362
+ return (input, _pos, selEnd, key, ctx) => {
13363
+ const need = markCst(ctx);
13364
+ const mRaw = MRAW;
13365
+ const mTl = MTL;
13366
+ const mLv = MLV;
13367
+ const mFl = need ? ctx._fields?.length ?? 0 : 0;
13368
+ const mEr = need ? ctx._errors?.length ?? 0 : 0;
13369
+ const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
13370
+ const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
12626
13371
  const v = child(input, selEnd, ctx);
12627
13372
  if (v === FAIL) {
12628
- rollbackTrivia(ctx, mark);
13373
+ if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12629
13374
  ctx._fc = true;
12630
13375
  return FAIL;
12631
13376
  }
12632
13377
  return [key, v];
12633
13378
  };
12634
13379
  }
12635
- return (input, pos, selEnd, key, selectorMark, ctx) => {
13380
+ return (input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
12636
13381
  const savedRouted = ctx._routed;
12637
- rollbackTrivia(ctx, selectorMark);
12638
- const mark = saveTriviaMark(ctx);
13382
+ if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12639
13383
  ctx._routed = { value: key, span: { start: pos, end: selEnd } };
12640
13384
  let v;
12641
13385
  try {
@@ -12644,7 +13388,7 @@ function assemble(t2, prog, cfg) {
12644
13388
  ctx._routed = savedRouted;
12645
13389
  }
12646
13390
  if (v === FAIL) {
12647
- rollbackTrivia(ctx, mark);
13391
+ if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12648
13392
  ctx._fc = true;
12649
13393
  return FAIL;
12650
13394
  }
@@ -12652,12 +13396,12 @@ function assemble(t2, prog, cfg) {
12652
13396
  };
12653
13397
  }
12654
13398
  function dispatchArmBlock(start, a0, a1, a2, a3, next) {
12655
- return (arm, input, pos, selEnd, key, selectorMark, ctx) => {
12656
- if (arm === start) return a0(input, pos, selEnd, key, selectorMark, ctx);
12657
- if (arm === start + 1) return a1(input, pos, selEnd, key, selectorMark, ctx);
12658
- if (arm === start + 2) return a2(input, pos, selEnd, key, selectorMark, ctx);
12659
- if (arm === start + 3) return a3(input, pos, selEnd, key, selectorMark, ctx);
12660
- return next === void 0 ? FAIL : next(arm, input, pos, selEnd, key, selectorMark, ctx);
13399
+ return (arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot) => {
13400
+ if (arm === start) return a0(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13401
+ if (arm === start + 1) return a1(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13402
+ if (arm === start + 2) return a2(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13403
+ if (arm === start + 3) return a3(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
13404
+ return next === void 0 ? FAIL : next(arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
12661
13405
  };
12662
13406
  }
12663
13407
  function trackLinesInto(ctx, input, end) {
@@ -12676,7 +13420,7 @@ function assemble(t2, prog, cfg) {
12676
13420
  if (flight !== void 0) return flight.fwd;
12677
13421
  const selected = HYBRID?.byIp[ip];
12678
13422
  if (selected !== void 0) {
12679
- const piece2 = hybridPiece(selected);
13423
+ const piece2 = scanHandoffNeeded(code, ip, closureLabels) ? hybridPiece(selected) : selected;
12680
13424
  memo.set(ip, piece2);
12681
13425
  reached.add(ip);
12682
13426
  return piece2;
@@ -12687,14 +13431,55 @@ function assemble(t2, prog, cfg) {
12687
13431
  target = p;
12688
13432
  } };
12689
13433
  inFlight.set(ip, holder);
12690
- const op = code[ip];
12691
- 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);
13434
+ const piece = scopeIsIdentity(code, ip, closureLabels.at(ip)) ? link(code[ip + 2]) : countedPiece(lookaheadLedNode(ip) ?? lower(ip), ip);
12692
13435
  inFlight.delete(ip);
12693
13436
  holder.set(piece);
12694
13437
  memo.set(ip, piece);
12695
13438
  reached.add(ip);
12696
13439
  return piece;
12697
13440
  }
13441
+ let FIRST_DECIDED;
13442
+ let NODE_BODY;
13443
+ const DECIDED = (_input, pos) => {
13444
+ EC.e = pos;
13445
+ return null;
13446
+ };
13447
+ function lookaheadLedNode(ip) {
13448
+ const op = code[ip];
13449
+ if (op !== OP_NODE && op !== OP_NODE_TRACK || hostCst || REC || cfg.probe || cfg.coverage || cfg.trackLines) return void 0;
13450
+ if (passCollapse(code, ip, hostCst) || gatedPassCollapse(code, ip, hostCst) !== void 0) return void 0;
13451
+ const body = code[ip + 2];
13452
+ const bop = code[body];
13453
+ if (bop !== OP_SEQ && bop !== OP_SEQV && bop !== OP_SEQX) return void 0;
13454
+ const base = bop === OP_SEQX ? body + 3 : body + 2;
13455
+ if (code[bop === OP_SEQX ? body + 2 : body + 1] < 2) return void 0;
13456
+ const first = code[base];
13457
+ const negative = code[first] === OP_NOT;
13458
+ const terminal = negative ? scalarTerminalNotChild(code, first) : code[first] === OP_PEEK ? scalarTerminalPeekChild(code, first) : -1;
13459
+ if (terminal < 0) return void 0;
13460
+ const recognize = scalarFor(terminal);
13461
+ const xf = fx[code[first + 2]];
13462
+ FIRST_DECIDED = DECIDED;
13463
+ NODE_BODY = lower(body);
13464
+ const node2 = lower(ip);
13465
+ NODE_BODY = void 0;
13466
+ return negative ? (input, pos, ctx) => {
13467
+ if (recognize(input, pos) >= 0) {
13468
+ ctx._fe = pos;
13469
+ ctx._fx = xf;
13470
+ EC.e = pos;
13471
+ return FAIL;
13472
+ }
13473
+ return node2(input, pos, ctx);
13474
+ } : (input, pos, ctx) => {
13475
+ if (recognize(input, pos) < 0) {
13476
+ ctx._fe = pos;
13477
+ ctx._fx = xf;
13478
+ return FAIL;
13479
+ }
13480
+ return node2(input, pos, ctx);
13481
+ };
13482
+ }
12698
13483
  function lower(ip) {
12699
13484
  const op = code[ip];
12700
13485
  switch (op) {
@@ -13239,8 +14024,10 @@ function assemble(t2, prog, cfg) {
13239
14024
  const mEr = need ? ctx._errors?.length ?? 0 : 0;
13240
14025
  const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
13241
14026
  const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
14027
+ const probeBest = PROBE ? ctx._probe?.best : void 0;
13242
14028
  const v = child(input, pos, ctx);
13243
14029
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14030
+ if (PROBE && ctx._probe !== void 0) ctx._probe.best = probeBest ?? null;
13244
14031
  if (v === FAIL) {
13245
14032
  EC.e = pos;
13246
14033
  return null;
@@ -13251,6 +14038,20 @@ function assemble(t2, prog, cfg) {
13251
14038
  };
13252
14039
  }
13253
14040
  case OP_PEEK: {
14041
+ const scalarChild = scalarTerminalPeekChild(code, ip);
14042
+ if (scalarChild >= 0) {
14043
+ const recognize = scalarFor(scalarChild);
14044
+ const xf2 = fx[code[ip + 2]];
14045
+ return (input, pos, ctx) => {
14046
+ if (recognize(input, pos) < 0) {
14047
+ ctx._fe = pos;
14048
+ ctx._fx = xf2;
14049
+ return FAIL;
14050
+ }
14051
+ EC.e = pos;
14052
+ return null;
14053
+ };
14054
+ }
13254
14055
  const child = link(code[ip + 1]);
13255
14056
  const xf = fx[code[ip + 2]];
13256
14057
  const gate = lookaheadClass(code[ip + 1]);
@@ -13268,8 +14069,10 @@ function assemble(t2, prog, cfg) {
13268
14069
  const mEr = need ? ctx._errors?.length ?? 0 : 0;
13269
14070
  const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
13270
14071
  const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
14072
+ const probeBest = PROBE ? ctx._probe?.best : void 0;
13271
14073
  const v = child(input, pos, ctx);
13272
14074
  if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14075
+ if (PROBE && ctx._probe !== void 0) ctx._probe.best = probeBest ?? null;
13273
14076
  if (v === FAIL) {
13274
14077
  ctx._fe = pos;
13275
14078
  ctx._fx = xf;
@@ -13282,12 +14085,13 @@ function assemble(t2, prog, cfg) {
13282
14085
  case OP_OPT: {
13283
14086
  const child = link(code[ip + 1]);
13284
14087
  const gate = lookaheadClass(code[ip + 1]);
14088
+ const childClean = CLEAN(code[ip + 1]);
13285
14089
  return (input, pos, ctx) => {
13286
14090
  if (gate !== null && !classHas2(gate, lead(input, pos))) {
13287
14091
  EC.e = pos;
13288
14092
  return null;
13289
14093
  }
13290
- const need = markCst(ctx);
14094
+ const need = !childClean && markCst(ctx);
13291
14095
  const mRaw = MRAW;
13292
14096
  const mTl = MTL;
13293
14097
  const mLv = MLV;
@@ -13485,6 +14289,9 @@ function assemble(t2, prog, cfg) {
13485
14289
  const projection = reducer < 0 ? ~reducer : -1;
13486
14290
  const fn = fused && projection < 0 ? fns[reducer] : void 0;
13487
14291
  const step = closureLabels.at(ip).buf ? nextTermBuffered : nextTerm;
14292
+ const decided = FIRST_DECIDED;
14293
+ FIRST_DECIDED = void 0;
14294
+ const linkFirst = () => decided ?? link(code[base]);
13488
14295
  const runnerBlock = (count, r0, r1, r2, r3, next) => {
13489
14296
  if (count === 1) return (input, cur, ctx, values, inherited) => {
13490
14297
  cur = r0(input, cur, ctx, inherited);
@@ -13530,7 +14337,7 @@ function assemble(t2, prog, cfg) {
13530
14337
  };
13531
14338
  };
13532
14339
  if (REC) {
13533
- const first2 = link(code[base]);
14340
+ const first2 = linkFirst();
13534
14341
  const firstSync = sentinelFor(code[base + n]);
13535
14342
  const runnerAt = (i) => {
13536
14343
  const sync = sentinelFor(code[base + n + i]);
@@ -13566,13 +14373,13 @@ function assemble(t2, prog, cfg) {
13566
14373
  const bindRunners = (start) => {
13567
14374
  const count = Math.min(4, n - start);
13568
14375
  const at = (offset) => runnerAt(start + offset);
13569
- const never = () => -1;
14376
+ const never2 = () => -1;
13570
14377
  return runnerBlock(
13571
14378
  count,
13572
14379
  at(0),
13573
- count > 1 ? at(1) : never,
13574
- count > 2 ? at(2) : never,
13575
- count > 3 ? at(3) : never,
14380
+ count > 1 ? at(1) : never2,
14381
+ count > 2 ? at(2) : never2,
14382
+ count > 3 ? at(3) : never2,
13576
14383
  start + count < n ? bindRunners(start + count) : void 0
13577
14384
  );
13578
14385
  };
@@ -13622,15 +14429,31 @@ function assemble(t2, prog, cfg) {
13622
14429
  let hasAdj = false;
13623
14430
  for (let i = 1; i < n; i++) {
13624
14431
  const kidIp = code[base + i];
13625
- if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp)) {
14432
+ if (code[kidIp] === OP_ADJ || leadingTriviaFieldAt(code, k, kidIp) || holdsGap(code, k, base, n, i, NULLABLE)) {
13626
14433
  hasAdj = true;
13627
14434
  break;
13628
14435
  }
13629
14436
  }
13630
14437
  if (hasAdj) {
13631
- const first2 = link(code[base]);
14438
+ const first2 = linkFirst();
13632
14439
  const runnerAt = (i) => {
13633
14440
  const kidIp = code[base + i];
14441
+ if (holdsGap(code, k, base, n, i, NULLABLE)) {
14442
+ const kid = link(kidIp);
14443
+ return (input, cur, ctx) => {
14444
+ if (ctx.trivia === void 0) {
14445
+ const v2 = kid(input, cur, ctx);
14446
+ if (v2 === FAIL) return -1;
14447
+ TERMV = v2;
14448
+ return EC.e;
14449
+ }
14450
+ const scanEnd = skipTrivia(input, cur, ctx);
14451
+ const v = kid(input, scanEnd, ctx);
14452
+ if (v === FAIL) return -1;
14453
+ TERMV = v;
14454
+ return scanEnd;
14455
+ };
14456
+ }
13634
14457
  if (code[kidIp] !== OP_ADJ) {
13635
14458
  const kid = link(kidIp);
13636
14459
  if (leadingTriviaFieldAt(code, k, kidIp)) {
@@ -13655,13 +14478,13 @@ function assemble(t2, prog, cfg) {
13655
14478
  const bindRunners = (start) => {
13656
14479
  const count = Math.min(4, n - start);
13657
14480
  const at = (offset) => runnerAt(start + offset);
13658
- const never = () => -1;
14481
+ const never2 = () => -1;
13659
14482
  return runnerBlock(
13660
14483
  count,
13661
14484
  at(0),
13662
- count > 1 ? at(1) : never,
13663
- count > 2 ? at(2) : never,
13664
- count > 3 ? at(3) : never,
14485
+ count > 1 ? at(1) : never2,
14486
+ count > 2 ? at(2) : never2,
14487
+ count > 3 ? at(3) : never2,
13665
14488
  start + count < n ? bindRunners(start + count) : void 0
13666
14489
  );
13667
14490
  };
@@ -13698,7 +14521,7 @@ function assemble(t2, prog, cfg) {
13698
14521
  };
13699
14522
  }
13700
14523
  if (n === 1) {
13701
- const k0 = link(code[base]);
14524
+ const k0 = linkFirst();
13702
14525
  if (fused) {
13703
14526
  return (input, pos, ctx) => {
13704
14527
  const v = k0(input, pos, ctx);
@@ -13720,7 +14543,7 @@ function assemble(t2, prog, cfg) {
13720
14543
  };
13721
14544
  }
13722
14545
  if (n === 2) {
13723
- const k0 = link(code[base]), k1 = link(code[base + 1]);
14546
+ const k0 = linkFirst(), k1 = link(code[base + 1]);
13724
14547
  if (fused) {
13725
14548
  return (input, pos, ctx) => {
13726
14549
  const v0 = k0(input, pos, ctx);
@@ -13751,7 +14574,7 @@ function assemble(t2, prog, cfg) {
13751
14574
  };
13752
14575
  }
13753
14576
  if (n === 3) {
13754
- const k0 = link(code[base]), k1 = link(code[base + 1]), k2 = link(code[base + 2]);
14577
+ const k0 = linkFirst(), k1 = link(code[base + 1]), k2 = link(code[base + 2]);
13755
14578
  if (fused) {
13756
14579
  return (input, pos, ctx) => {
13757
14580
  const v0 = k0(input, pos, ctx);
@@ -13845,7 +14668,7 @@ function assemble(t2, prog, cfg) {
13845
14668
  start + count < n ? bind(start + count) : void 0
13846
14669
  );
13847
14670
  };
13848
- const first = link(code[base]);
14671
+ const first = linkFirst();
13849
14672
  const tail = bind(1);
13850
14673
  const runDirectTerms = (input, pos, ctx, values) => {
13851
14674
  const v0 = first(input, pos, ctx);
@@ -13884,6 +14707,8 @@ function assemble(t2, prog, cfg) {
13884
14707
  const n = code[ip + 2];
13885
14708
  const choiceFx = fx[code[ip + 3]];
13886
14709
  const base = ip + 4;
14710
+ for (let i = 0; i < n; i++) link(code[base + i]);
14711
+ const armsClean = CLEAN(ip);
13887
14712
  if (table.exclusive) {
13888
14713
  const ascii = table.ascii;
13889
14714
  const hi = table.hi;
@@ -14023,7 +14848,7 @@ function assemble(t2, prog, cfg) {
14023
14848
  if (n === 3) addGate(g2, 4);
14024
14849
  return (input, pos, ctx) => {
14025
14850
  const c = lead(input, pos);
14026
- const need = markCst(ctx);
14851
+ const need = !armsClean && markCst(ctx);
14027
14852
  const mRaw = MRAW;
14028
14853
  const mTl = MTL;
14029
14854
  const mLv = MLV;
@@ -14240,7 +15065,7 @@ function assemble(t2, prog, cfg) {
14240
15065
  if (pn !== 0) {
14241
15066
  return (input, pos, ctx) => {
14242
15067
  const c = lead(input, pos);
14243
- const need = markCst(ctx);
15068
+ const need = !armsClean && markCst(ctx);
14244
15069
  const mRaw = MRAW;
14245
15070
  const mTl = MTL;
14246
15071
  const mLv = MLV;
@@ -14297,7 +15122,7 @@ function assemble(t2, prog, cfg) {
14297
15122
  }
14298
15123
  return (input, pos, ctx) => {
14299
15124
  const c = lead(input, pos);
14300
- const need = markCst(ctx);
15125
+ const need = !armsClean && markCst(ctx);
14301
15126
  const mRaw = MRAW;
14302
15127
  const mTl = MTL;
14303
15128
  const mLv = MLV;
@@ -14345,7 +15170,7 @@ function assemble(t2, prog, cfg) {
14345
15170
  }
14346
15171
  return (input, pos, ctx) => {
14347
15172
  const c = lead(input, pos);
14348
- const need = markCst(ctx);
15173
+ const need = !armsClean && markCst(ctx);
14349
15174
  const mRaw = MRAW;
14350
15175
  const mTl = MTL;
14351
15176
  const mLv = MLV;
@@ -14399,6 +15224,21 @@ function assemble(t2, prog, cfg) {
14399
15224
  return lit(input, pos, ctx);
14400
15225
  };
14401
15226
  }
15227
+ case OP_RGATE: {
15228
+ const cls = cc[code[ip + 1]];
15229
+ const xf = fx[code[ip + 3]];
15230
+ const child = link(code[ip + 2]);
15231
+ if (cfg.tolerant || cfg.probe) return child;
15232
+ return (input, pos, ctx) => {
15233
+ const item = ctx._routed;
15234
+ if (item !== void 0 && item.span.start === pos && !classHas2(cls, lead(input, item.span.end))) {
15235
+ ctx._fe = item.span.end;
15236
+ ctx._fx = xf;
15237
+ return FAIL;
15238
+ }
15239
+ return child(input, pos, ctx);
15240
+ };
15241
+ }
14402
15242
  case OP_ARMGATE: {
14403
15243
  const pred = fns[code[ip + 1]];
14404
15244
  const child = link(code[ip + 2]);
@@ -14467,6 +15307,7 @@ function assemble(t2, prog, cfg) {
14467
15307
  const itemClass = !REC && itemClassIndex >= 0 ? cc[itemClassIndex] : void 0;
14468
15308
  const itemMayStart = itemClass === void 0 ? void 0 : (input, pos) => classHas2(itemClass, lead(input, pos));
14469
15309
  const skipBeforeFirst = sepIp < 0 && min === 0;
15310
+ const itemClean = sepIp < 0 && CLEAN(code[ip + 1]) && !rowNullable(code, k, code[ip + 1], NULLABLE);
14470
15311
  if (REC) {
14471
15312
  const itemFx2 = fx[code[ip + 6]];
14472
15313
  const sepSent = sentinelFor(code[ip + 7]);
@@ -14563,6 +15404,8 @@ function assemble(t2, prog, cfg) {
14563
15404
  const out = collect ? [] : void 0;
14564
15405
  const hasTrivia = ctx.trivia !== void 0;
14565
15406
  const needMark = rollbackNeeded(ctx);
15407
+ const markLater = needMark && (!itemClean || hasTrivia);
15408
+ const markFirst = needMark && (!itemClean || hasTrivia && skipBeforeFirst);
14566
15409
  const gateItems = itemMayStart !== void 0 && ctx._probe === void 0;
14567
15410
  let cur = pos;
14568
15411
  let count = 0;
@@ -14570,14 +15413,15 @@ function assemble(t2, prog, cfg) {
14570
15413
  if (max >= 0 && count >= max) break;
14571
15414
  if (sep !== void 0 && count > 0 && count >= min && cur >= input.length) break;
14572
15415
  if (count >= min && sep === void 0 && !hasTrivia && gateItems && !itemMayStart(input, cur)) break;
14573
- if (needMark) markCst(ctx);
14574
- const mRaw = needMark ? MRAW : 0;
14575
- const mTl = needMark ? MTL : 0;
14576
- const mLv = needMark ? MLV : 0;
14577
- const mFl = needMark ? ctx._fields?.length ?? 0 : 0;
14578
- const mEr = needMark ? ctx._errors?.length ?? 0 : 0;
14579
- const mLog = needMark ? ctx._triviaLog?.length ?? 0 : 0;
14580
- const mRoot = needMark ? ctx._rootTriviaLog?.length ?? 0 : 0;
15416
+ const markIt = count > 0 ? markLater : markFirst;
15417
+ if (markIt) markCst(ctx);
15418
+ const mRaw = markIt ? MRAW : 0;
15419
+ const mTl = markIt ? MTL : 0;
15420
+ const mLv = markIt ? MLV : 0;
15421
+ const mFl = markIt ? ctx._fields?.length ?? 0 : 0;
15422
+ const mEr = markIt ? ctx._errors?.length ?? 0 : 0;
15423
+ const mLog = markIt ? ctx._triviaLog?.length ?? 0 : 0;
15424
+ const mRoot = markIt ? ctx._rootTriviaLog?.length ?? 0 : 0;
14581
15425
  let itemStart = cur;
14582
15426
  let sepEnd = -1;
14583
15427
  const viaRepItem = sep === void 0 && count >= min && (count > 0 || skipBeforeFirst);
@@ -14589,7 +15433,7 @@ function assemble(t2, prog, cfg) {
14589
15433
  if (sepLeads) LEAD.at = cur;
14590
15434
  const sv = sep(input, sp, ctx);
14591
15435
  if (sv === FAIL) {
14592
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15436
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14593
15437
  if (committed3(ctx)) return FAIL;
14594
15438
  break;
14595
15439
  }
@@ -14600,25 +15444,25 @@ function assemble(t2, prog, cfg) {
14600
15444
  itemStart = skipTrivia(input, itemStart, ctx);
14601
15445
  }
14602
15446
  if (itemStart >= input.length && viaRepItem) {
14603
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15447
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14604
15448
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
14605
15449
  break;
14606
15450
  }
14607
15451
  if (count >= min && gateItems && !itemMayStart(input, itemStart)) {
14608
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15452
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14609
15453
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
14610
15454
  break;
14611
15455
  }
14612
15456
  ctx._fc = false;
14613
15457
  const v = child(input, itemStart, ctx);
14614
15458
  if (v === FAIL) {
14615
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15459
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14616
15460
  if (committed3(ctx)) return FAIL;
14617
15461
  if (trailingAllowed && sepEnd >= 0) cur = sepEnd;
14618
15462
  break;
14619
15463
  }
14620
15464
  if (EC.e === itemStart && viaRepItem) {
14621
- if (needMark) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
15465
+ if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14622
15466
  break;
14623
15467
  }
14624
15468
  if (out !== void 0) out.push(v);
@@ -14692,7 +15536,14 @@ function assemble(t2, prog, cfg) {
14692
15536
  const runArm = n === 0 ? void 0 : bindArms(0);
14693
15537
  const runOther = other === void 0 ? void 0 : dispatchBranch(other, otherRouted);
14694
15538
  return (input, pos, ctx) => {
14695
- const selectorMark = saveTriviaMark(ctx);
15539
+ const need = markCst(ctx);
15540
+ const mRaw = MRAW;
15541
+ const mTl = MTL;
15542
+ const mLv = MLV;
15543
+ const mFl = need ? ctx._fields?.length ?? 0 : 0;
15544
+ const mEr = need ? ctx._errors?.length ?? 0 : 0;
15545
+ const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
15546
+ const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
14696
15547
  const selVal = selector(input, pos, ctx);
14697
15548
  if (selVal === FAIL) return FAIL;
14698
15549
  const selEnd = EC.e;
@@ -14704,9 +15555,9 @@ function assemble(t2, prog, cfg) {
14704
15555
  ctx._fx = expected;
14705
15556
  return FAIL;
14706
15557
  }
14707
- return runOther(input, pos, selEnd, key, selectorMark, ctx);
15558
+ return runOther(input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14708
15559
  }
14709
- return runArm(arm, input, pos, selEnd, key, selectorMark, ctx);
15560
+ return runArm(arm, input, pos, selEnd, key, ctx, need, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
14710
15561
  };
14711
15562
  }
14712
15563
  /* ── node ────────────────────────────────────────────────────────────── */
@@ -14742,7 +15593,8 @@ function assemble(t2, prog, cfg) {
14742
15593
  return nd;
14743
15594
  };
14744
15595
  }
14745
- const child = link(code[ip + 2]);
15596
+ const child = NODE_BODY ?? link(code[ip + 2]);
15597
+ NODE_BODY = void 0;
14746
15598
  const proj = code[ip + 4];
14747
15599
  const buildIdx = code[ip + 1];
14748
15600
  const type = k[code[ip + 5]];
@@ -14766,6 +15618,30 @@ function assemble(t2, prog, cfg) {
14766
15618
  if (build !== void 0 && proj < 0 && flags === 18) {
14767
15619
  return childrenOnlyFieldsBuildNode(child, build);
14768
15620
  }
15621
+ if (build !== void 0 && passCollapse(code, ip, hostCst)) {
15622
+ return passCollapseNode(child, build, flags === 32);
15623
+ }
15624
+ const gated = build !== void 0 && !cfg.tolerant && !cfg.probe ? gatedPassCollapse(code, ip, hostCst) : void 0;
15625
+ const triviaLead = gated === void 0 ? void 0 : triviaLeadRanges(closureLabels.at(code[ip + 2]).tri, trivia2);
15626
+ if (build !== void 0 && gated !== void 0 && triviaLead !== void 0) {
15627
+ const classic = flags === 32 ? collapseBuildNode(child, build) : childrenOnlyCollapseBuildNode(child, build);
15628
+ return gatedPassCollapseNode(
15629
+ [
15630
+ ...gated.leads.map((g) => cc[code[g + 1]]),
15631
+ resolveClass(encodeClassSpec([
15632
+ ...triviaLead,
15633
+ ...gated.lits.map((i) => {
15634
+ const c = k[i].codePointAt(0);
15635
+ return { lo: c, hi: c };
15636
+ })
15637
+ ]))
15638
+ ],
15639
+ link(gated.last),
15640
+ classic,
15641
+ build,
15642
+ flags === 32
15643
+ );
15644
+ }
14769
15645
  if (build !== void 0 && proj < 0 && flags === 32) {
14770
15646
  return collapseBuildNode(child, build);
14771
15647
  }
@@ -14914,6 +15790,8 @@ function assemble(t2, prog, cfg) {
14914
15790
  }
14915
15791
  const notChild = scalarTerminalNotChild(code, ip);
14916
15792
  if (notChild >= 0) scalarFor(notChild);
15793
+ const peekChild = scalarTerminalPeekChild(code, ip);
15794
+ if (peekChild >= 0) scalarFor(peekChild);
14917
15795
  }
14918
15796
  const pre = cfg.hostCaptureTrivia === void 0 ? prog.asm?.find((a) => a.key === cfgKey(cfg)) : void 0;
14919
15797
  if (pre !== void 0) {
@@ -14973,7 +15851,8 @@ function assemble(t2, prog, cfg) {
14973
15851
  t2.lexPrograms,
14974
15852
  LEAD,
14975
15853
  triviaFieldCapture,
14976
- skipDeferredTrivia
15854
+ skipDeferredTrivia,
15855
+ collapseEmpty
14977
15856
  );
14978
15857
  emitReached = new Set(pre.reached);
14979
15858
  if (emitted.ext !== void 0) HYBRID = emitted;
@@ -15059,7 +15938,8 @@ function assemble(t2, prog, cfg) {
15059
15938
  t2.lexPrograms,
15060
15939
  LEAD,
15061
15940
  triviaFieldCapture,
15062
- skipDeferredTrivia
15941
+ skipDeferredTrivia,
15942
+ collapseEmpty
15063
15943
  );
15064
15944
  emitReached = em.reached;
15065
15945
  } catch (e) {
@@ -15326,7 +16206,7 @@ function emitClassPool(classes) {
15326
16206
  if (classes.length < 2) return array;
15327
16207
  const delimiter = "|/~?^`".split("").find((ch2) => classes.every((spec) => !spec.includes(ch2)));
15328
16208
  if (delimiter === void 0) return array;
15329
- const split = `${jsString(classes.join(delimiter))}.split(${jsString(delimiter)})`;
16209
+ const split = `/* @__PURE__ */ ${jsString(classes.join(delimiter))}.split(${jsString(delimiter)})`;
15330
16210
  return split.length < array.length ? split : array;
15331
16211
  }
15332
16212
  function emitConst(v) {
@@ -17185,16 +18065,16 @@ function ownSource(fn) {
17185
18065
  return typeof name === "string" && name.length > 0 && globalThis[name] === fn ? name : null;
17186
18066
  }
17187
18067
  function compile(target, second, third) {
17188
- if (!isCombinator(target)) return compileMap(target, second);
18068
+ if (!isCombinator2(target)) return compileMap(target, second);
17189
18069
  return compileRoot(target, second, third);
17190
18070
  }
17191
- function isCombinator(x) {
18071
+ function isCombinator2(x) {
17192
18072
  return typeof x === "object" && x !== null && typeof x.parse === "function" && "_def" in x;
17193
18073
  }
17194
18074
  function compileMap(grammar, opts = {}) {
17195
18075
  const entries = [];
17196
18076
  for (const [key, value] of Object.entries(grammar)) {
17197
- if (!isCombinator(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
18077
+ if (!isCombinator2(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`);
17198
18078
  if (value._def.tag === "lazy") {
17199
18079
  try {
17200
18080
  value._def.thunk();
@@ -17508,49 +18388,6 @@ function digestValue(value, prefix = "", options) {
17508
18388
  return sha.digest("hex");
17509
18389
  }
17510
18390
 
17511
- // src/combinators/attempt.ts
17512
- function containOption(options) {
17513
- if (options === void 0) return false;
17514
- for (const key of Object.keys(options)) {
17515
- if (key !== "contain") throw new TypeError(`attempt(): unknown option ${JSON.stringify(key)}`);
17516
- }
17517
- if (options.contain !== void 0 && typeof options.contain !== "boolean") {
17518
- throw new TypeError("attempt(): `contain` must be a boolean");
17519
- }
17520
- return options.contain === true;
17521
- }
17522
- function attempt(parser2, options) {
17523
- const contain = containOption(options);
17524
- const guardable = parser2._meta.firstSet.kind !== "any" && !matchesEmpty(parser2);
17525
- let failExpected;
17526
- return {
17527
- _tag: "attempt",
17528
- _meta: parser2._meta,
17529
- _def: contain ? { tag: "attempt", parser: parser2, contain: true } : { tag: "attempt", parser: parser2 },
17530
- parse(input, pos, ctx) {
17531
- if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(parser2, input, pos)) {
17532
- if (failExpected === void 0) {
17533
- const e = deriveExpected(parser2);
17534
- failExpected = e.length > 0 ? e : [parser2._tag];
17535
- }
17536
- return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
17537
- }
17538
- const mark = saveCstMark(ctx);
17539
- const log = ctx._triviaLog?.length;
17540
- const rootLog = ctx._rootTriviaLog?.length;
17541
- const split = ctx._columnsSplit;
17542
- const result = parser2.parse(input, pos, ctx);
17543
- if (result.ok) return result;
17544
- rollbackCstCapture(ctx, mark);
17545
- ctx._columnsSplit = split;
17546
- if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
17547
- if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
17548
- if (result.committed && !contain) return result;
17549
- return { ok: false, expected: result.expected, span: { start: pos, end: pos } };
17550
- }
17551
- };
17552
- }
17553
-
17554
18391
  // src/combinators/not.ts
17555
18392
  function not(combinator) {
17556
18393
  const meta = {