parseman 0.55.4 → 0.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/analysis/choice-cost.d.ts.map +1 -1
  2. package/dist/analysis/commitment.d.ts.map +1 -1
  3. package/dist/analysis/diagnostics.js +3353 -3095
  4. package/dist/analysis/diagnostics.js.map +3 -3
  5. package/dist/analysis/duplication.d.ts.map +1 -1
  6. package/dist/analysis/gating.d.ts.map +1 -1
  7. package/dist/cli/index.js +1813 -1555
  8. package/dist/cli/index.js.map +3 -3
  9. package/dist/combinators/attempt.d.ts.map +1 -1
  10. package/dist/combinators/choice.d.ts.map +1 -1
  11. package/dist/combinators/dispatch.d.ts.map +1 -1
  12. package/dist/combinators/expect.d.ts.map +1 -1
  13. package/dist/combinators/first-set.d.ts.map +1 -1
  14. package/dist/combinators/lines.d.ts +79 -0
  15. package/dist/combinators/lines.d.ts.map +1 -0
  16. package/dist/combinators/trivia-skip.d.ts +1 -0
  17. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  18. package/dist/compiler/fields.d.ts.map +1 -1
  19. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  20. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  21. package/dist/compiler/token-alphabet.d.ts.map +1 -1
  22. package/dist/compiler/token-capability.js +25 -0
  23. package/dist/compiler/token-capability.js.map +1 -1
  24. package/dist/coverage.d.ts.map +1 -1
  25. package/dist/cst/reflection.d.ts.map +1 -1
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1905 -1625
  29. package/dist/index.js.map +3 -3
  30. package/dist/language-service/index.js +2 -0
  31. package/dist/language-service/index.js.map +1 -1
  32. package/dist/parse-context.d.ts.map +1 -1
  33. package/dist/plugin/evaluator.d.ts.map +1 -1
  34. package/dist/plugin/index.js +1318 -1045
  35. package/dist/plugin/index.js.map +3 -3
  36. package/dist/plugin/ir-eval.d.ts +0 -4
  37. package/dist/plugin/ir-eval.d.ts.map +1 -1
  38. package/dist/run/index.js +2 -0
  39. package/dist/run/index.js.map +1 -1
  40. package/dist/spec/index.js +15 -0
  41. package/dist/spec/index.js.map +1 -1
  42. package/dist/spec/model.d.ts.map +1 -1
  43. package/dist/table/assemble.d.ts +0 -65
  44. package/dist/table/assemble.d.ts.map +1 -1
  45. package/dist/table/emit.d.ts.map +1 -1
  46. package/dist/table/encode.d.ts.map +1 -1
  47. package/dist/table/exec.d.ts.map +1 -1
  48. package/dist/table/index.js +3442 -3192
  49. package/dist/table/index.js.map +3 -3
  50. package/dist/table/line-scan.d.ts +9 -0
  51. package/dist/table/line-scan.d.ts.map +1 -0
  52. package/dist/table/program.d.ts +11 -2
  53. package/dist/table/program.d.ts.map +1 -1
  54. package/dist/types.d.ts +21 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/package.json +1 -1
  58. package/src/analysis/choice-cost.ts +1 -0
  59. package/src/analysis/commitment.ts +13 -0
  60. package/src/analysis/duplication.ts +3 -0
  61. package/src/analysis/gating.ts +5 -0
  62. package/src/analysis/rebuild.ts +2 -2
  63. package/src/combinators/attempt.ts +2 -0
  64. package/src/combinators/choice.ts +3 -0
  65. package/src/combinators/dispatch.ts +3 -0
  66. package/src/combinators/expect.ts +5 -0
  67. package/src/combinators/first-set.ts +9 -1
  68. package/src/combinators/lines.ts +244 -0
  69. package/src/combinators/trivia-skip.ts +4 -2
  70. package/src/compiler/fields.ts +3 -0
  71. package/src/compiler/grammar-coverage-ids.ts +1 -0
  72. package/src/compiler/ir-serialize.ts +10 -0
  73. package/src/compiler/token-alphabet.ts +3 -1
  74. package/src/coverage.ts +6 -0
  75. package/src/cst/reflection.ts +3 -0
  76. package/src/index.ts +2 -0
  77. package/src/parse-context.ts +2 -0
  78. package/src/plugin/evaluator.ts +4 -0
  79. package/src/plugin/ir-eval.ts +3 -0
  80. package/src/spec/model.ts +15 -0
  81. package/src/table/assemble.ts +6 -12
  82. package/src/table/emit.ts +7 -6
  83. package/src/table/encode.ts +18 -6
  84. package/src/table/exec.ts +3 -0
  85. package/src/table/line-scan.ts +19 -0
  86. package/src/table/program.ts +17 -2
  87. package/src/types.ts +14 -0
  88. package/src/version.ts +1 -1
package/dist/cli/index.js CHANGED
@@ -163,6 +163,11 @@ function matchesEmptyBody(p, seen, resolve2) {
163
163
  return true;
164
164
  }
165
165
  }
166
+ // A run consumes at least its first line's prefix, and its body after that.
167
+ case "lines":
168
+ return me(d.first ?? d.prefix) && me(d.body);
169
+ case "columns":
170
+ return d.n === 0;
166
171
  default:
167
172
  return true;
168
173
  }
@@ -277,6 +282,11 @@ function firstSetBody(p, seen, resolve2, activeTrivia) {
277
282
  resolve2,
278
283
  d.clearTrivia ? null : d.triviaParser ?? activeTrivia
279
284
  );
285
+ // A run starts with its first line's prefix; a nullable prefix starts with anything.
286
+ case "lines": {
287
+ const opening = d.first ?? d.prefix;
288
+ return empties(opening) ? any() : fs(opening);
289
+ }
280
290
  default:
281
291
  return p._meta.firstSet;
282
292
  }
@@ -326,6 +336,10 @@ function childrenOf(d) {
326
336
  return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
327
337
  case "routed":
328
338
  return d.fallback ? [d.fallback] : [];
339
+ case "lines":
340
+ return [d.prefix, d.body, ...d.first === void 0 ? [] : [d.first], ...d.lazy === void 0 ? [] : [d.lazy]];
341
+ case "columns":
342
+ return [];
329
343
  // A `lazy` is a REFERENCE, not a subtree. Descending through it would make
330
344
  // every rule's walk cover the whole reachable grammar — site paths become
331
345
  // nonsense, and a structural hash includes half the grammar. Treated as a
@@ -417,6 +431,9 @@ function classifyBroadArm(arm, resolve2) {
417
431
  return walk(d.parser);
418
432
  case "scanTo":
419
433
  return { cause: "broad-recognizer", detail: "scanTo (any first char)" };
434
+ // A run opens with its first line's prefix.
435
+ case "lines":
436
+ return walk(d.first ?? d.prefix);
420
437
  // `guard` (the `gate()` state predicate) has a genuinely-`any` first-set of
421
438
  // its own — it is the poison. `withCtx`/`recover` FORWARD their inner
422
439
  // first-set (see firstSetOf), so a broad result comes from the inner parser:
@@ -1052,6 +1069,8 @@ function createParseContext() {
1052
1069
  _triviaCaptureMask: void 0,
1053
1070
  trackLines: false,
1054
1071
  state: void 0,
1072
+ lineView: void 0,
1073
+ _columnsSplit: void 0,
1055
1074
  build: void 0,
1056
1075
  _errors: void 0,
1057
1076
  _tolerant: void 0,
@@ -1773,11 +1792,12 @@ function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1773
1792
  return result;
1774
1793
  }
1775
1794
  function saveLookaheadMark(ctx) {
1776
- return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null };
1795
+ return { ...saveTriviaMark(ctx), probeBest: ctx._probe ? ctx._probe.best : null, split: ctx._columnsSplit };
1777
1796
  }
1778
1797
  function rollbackLookahead(ctx, mark) {
1779
1798
  rollbackTrivia(ctx, mark);
1780
1799
  if (ctx._probe) ctx._probe.best = mark.probeBest;
1800
+ ctx._columnsSplit = mark.split;
1781
1801
  }
1782
1802
  function scanWithLabels(input, cur, ctx) {
1783
1803
  const triviaP = ctx.trivia;
@@ -2295,6 +2315,9 @@ function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
2295
2315
  return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen)) || def.recoverAt !== void 0 && parserUsesRouted(def.recoverAt, seen) || def.stopAt !== void 0 && parserUsesRouted(def.stopAt, seen);
2296
2316
  case "recover":
2297
2317
  return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
2318
+ // The body parses its own input, so only the checks on this one can reuse a routed head.
2319
+ case "lines":
2320
+ return [def.prefix, def.first, def.lazy].some((entry) => entry !== void 0 && parserUsesRouted(entry, seen));
2298
2321
  case "lazy":
2299
2322
  try {
2300
2323
  return parserUsesRouted(def.thunk(), seen);
@@ -2485,6 +2508,12 @@ function derive(c, seen) {
2485
2508
  return deriveExpected2(def.parser);
2486
2509
  case "expect":
2487
2510
  return def.expected;
2511
+ case "lines": {
2512
+ const opening = def.first ?? def.prefix;
2513
+ return matchesEmpty(opening) ? [...deriveExpected2(opening), ...deriveExpected2(def.body)] : deriveExpected2(opening);
2514
+ }
2515
+ case "columns":
2516
+ return [`columns(${def.n})`];
2488
2517
  case "node":
2489
2518
  case "grammar":
2490
2519
  case "trivia":
@@ -3087,6 +3116,9 @@ function childrenOf2(def) {
3087
3116
  ];
3088
3117
  case "routed":
3089
3118
  return def.fallback ? [def.fallback] : [];
3119
+ case "lines":
3120
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
3121
+ case "columns":
3090
3122
  case "literal":
3091
3123
  case "regex":
3092
3124
  case "keywords":
@@ -3479,7 +3511,11 @@ function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set(), triviaActiv
3479
3511
  case "leaf":
3480
3512
  case "scanTo":
3481
3513
  case "unknown":
3514
+ case "columns":
3482
3515
  return false;
3516
+ // A run's prefixes are probed detached; only its body can capture.
3517
+ case "lines":
3518
+ return mayLeavePartialCapture(d.body, seen, triviaActive);
3483
3519
  // node() captures into its own private buffers and rolls them back on
3484
3520
  // failure (emitNode restores _ctx.* and never pushes on the !ok path).
3485
3521
  case "node":
@@ -3599,6 +3635,10 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
3599
3635
  return capturesLeaf(d.parser, seen);
3600
3636
  case "scanTo":
3601
3637
  return true;
3638
+ case "lines":
3639
+ return capturesLeaf(d.body, seen);
3640
+ case "columns":
3641
+ return true;
3602
3642
  case "lazy": {
3603
3643
  try {
3604
3644
  return capturesLeaf(d.thunk(), seen);
@@ -3646,6 +3686,8 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
3646
3686
  return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
3647
3687
  case "scanTo":
3648
3688
  return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen)) || d.recoverAt !== void 0 && hasNodeDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasNodeDef(d.stopAt, seen);
3689
+ case "lines":
3690
+ return hasNodeDef(d.body, seen);
3649
3691
  case "recover":
3650
3692
  return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
3651
3693
  case "expect":
@@ -3695,6 +3737,8 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
3695
3737
  return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
3696
3738
  case "scanTo":
3697
3739
  return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen)) || d.recoverAt !== void 0 && hasDirectBuildDef(d.recoverAt, seen) || d.stopAt !== void 0 && hasDirectBuildDef(d.stopAt, seen);
3740
+ case "lines":
3741
+ return hasDirectBuildDef(d.body, seen);
3698
3742
  case "recover":
3699
3743
  return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
3700
3744
  case "routed":
@@ -3740,6 +3784,9 @@ function commitFailureSuccessors(p, resolve2) {
3740
3784
  ...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
3741
3785
  ...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
3742
3786
  ];
3787
+ // A run's prefixes are probed detached; a committed failure reaches it only from its body.
3788
+ case "lines":
3789
+ return [d.body];
3743
3790
  case "lazy": {
3744
3791
  try {
3745
3792
  return [d.thunk()];
@@ -3945,9 +3992,11 @@ function choice(...args) {
3945
3992
  for (let i = 0; i < parsers.length; i++) {
3946
3993
  if (gates[i] && !gates[i](ctx.state)) continue;
3947
3994
  const mark = saveTriviaMark(ctx);
3995
+ const split = ctx._columnsSplit;
3948
3996
  const result = parsers[i].parse(input, pos, ctx);
3949
3997
  if (!result.ok) {
3950
3998
  rollbackTrivia(ctx, mark);
3999
+ ctx._columnsSplit = split;
3951
4000
  const at = result.span.start;
3952
4001
  if (at > expectedAt) {
3953
4002
  expectedAt = at;
@@ -4147,665 +4196,671 @@ function buildAsciiDispatch(parsers) {
4147
4196
  return table;
4148
4197
  }
4149
4198
 
4150
- // src/compiler/degradation.ts
4151
- function formatDegradation(d) {
4152
- return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
4199
+ // src/combinators/field-trivia.ts
4200
+ var LEAD = { at: -1 };
4201
+ var FIELD_LEADING_TRIVIA = 1;
4202
+ var FIELD_TRAILING_TRIVIA = 2;
4203
+ function triviaFieldCapture(name, value, input, start, end, leadFrom, trailEnd) {
4204
+ return {
4205
+ name,
4206
+ value,
4207
+ span: { start, end },
4208
+ leading: leadFrom >= 0 && leadFrom < start ? input.slice(leadFrom, start) : "",
4209
+ trailing: trailEnd > end ? input.slice(end, trailEnd) : ""
4210
+ };
4153
4211
  }
4154
- var DETAIL_CAP = 8;
4155
- function formatDegradations(list) {
4156
- const byCode = /* @__PURE__ */ new Map();
4157
- for (const d of list) {
4158
- const bucket = byCode.get(d.code);
4159
- if (bucket) bucket.push(d);
4160
- else byCode.set(d.code, [d]);
4161
- }
4162
- const out = [];
4163
- for (const [code, items] of byCode) {
4164
- for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
4165
- if (items.length > DETAIL_CAP) {
4166
- out.push(
4167
- `[parseman] degraded [${code}] +${items.length - DETAIL_CAP} more site(s) not listed (${items.length} total). Set PARSEMAN_DEGRADATION=error to fail the build on these.`
4168
- );
4212
+ function leadsTrivia(p) {
4213
+ return p._def.tag === "field" && p._def.leadingTrivia === true;
4214
+ }
4215
+
4216
+ // src/combinators/repeat.ts
4217
+ function repItem(combinator, input, cur, ctx, guardable, mandatory, rollbackNeeded) {
4218
+ const mark = rollbackNeeded ? saveTriviaMark(ctx) : void 0;
4219
+ let pos = cur;
4220
+ if (ctx.trivia) {
4221
+ if (needsDeferredTriviaCommit(ctx)) {
4222
+ pos = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
4223
+ } else {
4224
+ pos = advanceTrivia(input, cur, ctx);
4169
4225
  }
4170
4226
  }
4171
- return out;
4172
- }
4173
- var _sinks = [];
4174
- function beginDegradationCapture() {
4175
- _sinks.push(/* @__PURE__ */ new Map());
4176
- }
4177
- function degradationCaptureDepth() {
4178
- return _sinks.length;
4179
- }
4180
- function unwindDegradationCapture(depth) {
4181
- const found = [];
4182
- while (_sinks.length > depth) {
4183
- const sink = _sinks.pop();
4184
- found.unshift(...sink.values());
4227
+ if (pos >= input.length && !mandatory) {
4228
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4229
+ return "stop";
4185
4230
  }
4186
- return found;
4187
- }
4188
- function resolveDegradationLevel(explicit) {
4189
- if (explicit !== void 0) return explicit;
4190
- const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
4191
- if (env === "off" || env === "warn" || env === "error") return env;
4192
- return "warn";
4231
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
4232
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4233
+ return "stop";
4234
+ }
4235
+ const result = combinator.parse(input, pos, ctx);
4236
+ if (!result.ok) {
4237
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4238
+ if (result.committed) return { fail: result, failPos: pos };
4239
+ return { fail: result, failPos: pos };
4240
+ }
4241
+ if (result.span.end === pos && !mandatory) {
4242
+ if (mark !== void 0) rollbackTrivia(ctx, mark);
4243
+ return "stop";
4244
+ }
4245
+ return result;
4193
4246
  }
4194
- function degradationKey(d) {
4195
- return JSON.stringify([d.code, d.where, d.subject]);
4247
+ function resolveBounds(what, opts) {
4248
+ const min = opts.min ?? 0;
4249
+ const max = opts.max ?? Infinity;
4250
+ const bad = (msg) => {
4251
+ throw new RangeError(`parseman: ${what} ${msg}`);
4252
+ };
4253
+ if (!Number.isInteger(min) || min < 0) bad(`min must be a non-negative integer (got ${String(opts.min)})`);
4254
+ if (max !== Infinity && (!Number.isInteger(max) || max < 1)) bad(`max must be a positive integer (got ${String(opts.max)})`);
4255
+ if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
4256
+ return { min, max };
4196
4257
  }
4197
- function recordDegradation(d) {
4198
- const level = resolveDegradationLevel();
4199
- if (level === "off") return;
4200
- const key = degradationKey(d);
4201
- const sink = _sinks[_sinks.length - 1];
4202
- if (sink) {
4203
- if (!sink.has(key)) sink.set(key, d);
4204
- return;
4258
+ function repeatScalarTail(combinator, child, input, cur, ctx, guardable, values, remaining) {
4259
+ while (cur < input.length && remaining > 0) {
4260
+ let itemPos = cur;
4261
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
4262
+ if (itemPos >= input.length) break;
4263
+ if (guardable && !startsFirstSet(combinator, input, itemPos)) break;
4264
+ const end = child(input, itemPos, ctx);
4265
+ if (end <= itemPos) break;
4266
+ if (values !== void 0) values.push(ctx._sv);
4267
+ cur = end;
4268
+ remaining--;
4205
4269
  }
4206
- if (_immediate.has(key)) return;
4207
- _immediate.add(key);
4208
- if (level === "error") throw new Error(`parseman: degraded compilation path
4209
- ${formatDegradation(d)}`);
4210
- console.warn(formatDegradation(d));
4270
+ ctx._sv = values;
4271
+ return cur;
4211
4272
  }
4212
- var _immediate = /* @__PURE__ */ new Set();
4213
- function beginCompileDegradationDrain() {
4214
- if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
4273
+ function many(combinator, opts = {}) {
4274
+ const { min, max } = resolveBounds("many()", opts);
4275
+ if (min >= 1) return atLeast(combinator, min, max);
4276
+ const meta = {
4277
+ firstSet: combinator._meta.firstSet,
4278
+ canMatchNewline: combinator._meta.canMatchNewline,
4279
+ isTrivia: false
4215
4280
  };
4216
- const depth = degradationCaptureDepth();
4217
- beginDegradationCapture();
4218
- return (report) => {
4219
- const found = unwindDegradationCapture(depth);
4220
- if (!report || found.length === 0) return;
4221
- const fresh = found.filter((d) => {
4222
- const k = degradationKey(d);
4223
- if (_immediate.has(k)) return false;
4224
- _immediate.add(k);
4225
- return true;
4226
- });
4227
- if (fresh.length === 0) return;
4228
- const lines = formatDegradations(fresh);
4229
- if (resolveDegradationLevel() === "error") {
4230
- throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
4231
- ${lines.join("\n")}`);
4281
+ const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
4282
+ let expected;
4283
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4284
+ const scalarChild = scalarOf(combinator);
4285
+ const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
4286
+ return {
4287
+ _tag: "many",
4288
+ _meta: meta,
4289
+ _def: def,
4290
+ _parseScalar: parseScalar,
4291
+ parse(input, pos, ctx) {
4292
+ const values = def.valueUnused ? void 0 : [];
4293
+ const rollbackNeeded = needsTriviaRollback(ctx);
4294
+ let cur = pos;
4295
+ let count = 0;
4296
+ while (cur < input.length) {
4297
+ if (count >= max) break;
4298
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
4299
+ if (item === "stop") break;
4300
+ if ("fail" in item) {
4301
+ if (!item.fail.ok && item.fail.committed) return item.fail;
4302
+ const sync = ctx._tolerant ? ctx._sync : void 0;
4303
+ if (sync === void 0) break;
4304
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
4305
+ expected ??= deriveExpected(combinator);
4306
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
4307
+ if (values !== void 0) values.push(error);
4308
+ captureError(ctx, error);
4309
+ count++;
4310
+ cur = end;
4311
+ continue;
4312
+ }
4313
+ if (values !== void 0) values.push(item.value);
4314
+ count++;
4315
+ cur = item.span.end;
4316
+ }
4317
+ return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
4232
4318
  }
4233
- for (const l of lines) console.warn(l);
4234
4319
  };
4235
4320
  }
4236
-
4237
- // src/compiler/build-arity.ts
4238
- function cstOutputHost(build) {
4239
- return build?._parsemanCstOutput === true;
4240
- }
4241
- var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
4242
- var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
4243
- var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
4244
- var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
4245
- function confirmedBuildArity(src) {
4246
- const s = src.trim();
4247
- if (NATIVE_CODE_RE.test(s)) return null;
4248
- if (/\barguments\b/.test(s)) return null;
4249
- if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
4250
- const inner = paramListText(s);
4251
- if (inner === null) return null;
4252
- if (inner.trim() === "") return 0;
4253
- const parts = splitTopLevel(inner);
4254
- if (parts === null) return null;
4255
- for (const part of parts) {
4256
- if (!isConfirmableParam(part.trim())) return null;
4257
- }
4258
- return parts.length;
4259
- }
4260
- function paramListText(s) {
4261
- const open = PARAM_LIST_OPEN_RE.exec(s);
4262
- if (!open) return null;
4263
- const start = open[0].length;
4264
- const end = matchingParen(s, start);
4265
- if (end === null) return null;
4266
- if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
4267
- return s.slice(start, end);
4321
+ function oneOrMore(combinator, opts = {}) {
4322
+ return many(combinator, { ...opts, min: opts.min ?? 1 });
4268
4323
  }
4269
- function matchingParen(s, start) {
4270
- let depth = 0;
4271
- for (let i = start; i < s.length; i++) {
4272
- const c = s[i];
4273
- if (c === '"' || c === "'" || c === "`") {
4274
- const close = skipString(s, i);
4275
- if (close === null) return null;
4276
- i = close;
4277
- continue;
4278
- }
4279
- if (c === "(" || c === "[" || c === "{") depth++;
4280
- else if (c === "]" || c === "}") {
4281
- if (--depth < 0) return null;
4282
- } else if (c === ")") {
4283
- if (depth === 0) return i;
4284
- depth--;
4324
+ function atLeast(combinator, min, max) {
4325
+ const meta = {
4326
+ firstSet: combinator._meta.firstSet,
4327
+ canMatchNewline: combinator._meta.canMatchNewline,
4328
+ isTrivia: false
4329
+ };
4330
+ const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
4331
+ let expected;
4332
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4333
+ const scalarChild = scalarOf(combinator);
4334
+ const parseScalar = (input, pos, ctx) => {
4335
+ let cur = scalarChild(input, pos, ctx);
4336
+ if (cur < 0) return cur;
4337
+ const values = def.valueUnused ? void 0 : [ctx._sv];
4338
+ for (let count = 1; count < min; count++) {
4339
+ let itemPos = cur;
4340
+ if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
4341
+ const end = scalarChild(input, itemPos, ctx);
4342
+ if (end < 0) return ~cur;
4343
+ cur = end;
4344
+ if (values !== void 0) values.push(ctx._sv);
4285
4345
  }
4286
- }
4287
- return null;
4288
- }
4289
- function skipString(s, open) {
4290
- const quote = s[open];
4291
- for (let i = open + 1; i < s.length; i++) {
4292
- if (s[i] === "\\") {
4293
- i++;
4294
- continue;
4346
+ return repeatScalarTail(combinator, scalarChild, input, cur, ctx, guardable, values, max - min);
4347
+ };
4348
+ return {
4349
+ _tag: "oneOrMore",
4350
+ _meta: meta,
4351
+ _def: def,
4352
+ _parseScalar: parseScalar,
4353
+ parse(input, pos, ctx) {
4354
+ const first = combinator.parse(input, pos, ctx);
4355
+ if (!first.ok) return first;
4356
+ const values = def.valueUnused ? void 0 : [first.value];
4357
+ const rollbackNeeded = needsTriviaRollback(ctx);
4358
+ let cur = first.span.end;
4359
+ let count = 1;
4360
+ while (count < min) {
4361
+ const item = repItem(combinator, input, cur, ctx, guardable, true, rollbackNeeded);
4362
+ if (item === "stop" || "fail" in item) {
4363
+ if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
4364
+ expected ??= deriveExpected(combinator);
4365
+ return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
4366
+ }
4367
+ if (values !== void 0) values.push(item.value);
4368
+ cur = item.span.end;
4369
+ count++;
4370
+ }
4371
+ while (cur < input.length) {
4372
+ if (count >= max) break;
4373
+ const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
4374
+ if (item === "stop") break;
4375
+ if ("fail" in item) {
4376
+ if (!item.fail.ok && item.fail.committed) return item.fail;
4377
+ const sync = ctx._tolerant ? ctx._sync : void 0;
4378
+ if (sync === void 0) break;
4379
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
4380
+ expected ??= deriveExpected(combinator);
4381
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
4382
+ if (values !== void 0) values.push(error);
4383
+ captureError(ctx, error);
4384
+ count++;
4385
+ cur = end;
4386
+ continue;
4387
+ }
4388
+ if (values !== void 0) values.push(item.value);
4389
+ count++;
4390
+ cur = item.span.end;
4391
+ }
4392
+ return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
4295
4393
  }
4296
- if (s[i] === quote) return i;
4297
- }
4298
- return null;
4394
+ };
4299
4395
  }
4300
- function splitTopLevel(inner) {
4301
- const parts = [];
4302
- let depth = 0;
4303
- let angle = 0;
4304
- let last = 0;
4305
- for (let i = 0; i < inner.length; i++) {
4306
- const c = inner[i];
4307
- if (c === "=" && inner[i + 1] === ">") {
4308
- i++;
4309
- continue;
4310
- }
4311
- if (c === '"' || c === "'" || c === "`") {
4312
- const close = skipString(inner, i);
4313
- if (close === null) return null;
4314
- i = close;
4315
- continue;
4316
- }
4317
- if (c === "(" || c === "[" || c === "{") depth++;
4318
- else if (c === ")" || c === "]" || c === "}") {
4319
- if (--depth < 0) return null;
4320
- } else if (c === "<") angle++;
4321
- else if (c === ">") {
4322
- if (--angle < 0) return null;
4323
- } else if (c === "," && depth === 0 && angle === 0) {
4324
- parts.push(inner.slice(last, i));
4325
- last = i + 1;
4396
+ function optional(combinator) {
4397
+ const meta = {
4398
+ firstSet: combinator._meta.firstSet,
4399
+ canMatchNewline: combinator._meta.canMatchNewline,
4400
+ isTrivia: false
4401
+ };
4402
+ const firstSetSkippable = !matchesEmpty(combinator);
4403
+ const scalarChild = scalarOf(combinator);
4404
+ const parseScalar = (input, pos, ctx) => {
4405
+ if (firstSetSkippable && !startsFirstSet(combinator, input, pos)) {
4406
+ ctx._sv = null;
4407
+ return pos;
4326
4408
  }
4327
- }
4328
- if (depth !== 0 || angle !== 0) return null;
4329
- parts.push(inner.slice(last));
4330
- return parts;
4331
- }
4332
- function isConfirmableParam(part) {
4333
- const head = PARAM_HEAD_RE.exec(part);
4334
- if (!head) return false;
4335
- const rest = part.slice(head[0].length);
4336
- if (rest === "") return true;
4337
- if (rest[0] !== ":") return false;
4338
- return !hasDefaultAssign(rest.slice(1));
4339
- }
4340
- function hasDefaultAssign(annotation) {
4341
- for (let i = 0; i < annotation.length; i++) {
4342
- if (annotation[i] !== "=") continue;
4343
- if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
4344
- i++;
4345
- continue;
4409
+ const end = scalarChild(input, pos, ctx);
4410
+ if (end >= 0) return end;
4411
+ ctx._sv = null;
4412
+ return pos;
4413
+ };
4414
+ return {
4415
+ _tag: "optional",
4416
+ _meta: meta,
4417
+ _def: { tag: "optional", parser: combinator },
4418
+ _parseScalar: parseScalar,
4419
+ parse(input, pos, ctx) {
4420
+ if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
4421
+ return { ok: true, value: null, span: { start: pos, end: pos } };
4422
+ }
4423
+ const mark = saveTriviaMark(ctx);
4424
+ const result = combinator.parse(input, pos, ctx);
4425
+ if (result.ok) return result;
4426
+ rollbackTrivia(ctx, mark);
4427
+ if (result.committed) return result;
4428
+ return { ok: true, value: null, span: { start: pos, end: pos } };
4346
4429
  }
4347
- const prev = annotation[i - 1];
4348
- if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
4349
- return true;
4350
- }
4351
- return false;
4352
- }
4353
- function buildAnalysisSrc(def) {
4354
- return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
4355
- }
4356
- var RESOLVER_REASONS = {
4357
- "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
4358
- "arguments": "its body references `arguments`",
4359
- "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
4360
- "not-a-function": "the binding it names is not a function declaration",
4361
- "unresolved-import": "its module could not be resolved or parsed",
4362
- "not-found": "the name did not resolve to any binding",
4363
- "computed": "it is a computed expression",
4364
- "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
4365
- "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
4366
- };
4367
- function unconfirmableReason(def, src) {
4368
- const declared = def.buildArityUnresolved;
4369
- if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
4370
- const s = src.trim();
4371
- const params = s.slice(0, s.indexOf(")") + 1);
4372
- if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
4373
- if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
4374
- if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
4375
- return "it names a binding this build could not resolve to a function declaration";
4376
- }
4377
- return "its parameter list could not be parsed";
4378
- }
4379
- function confirmedArityForDef(def) {
4380
- if (def.buildArity !== void 0) return def.buildArity;
4381
- const src = buildAnalysisSrc(def);
4382
- const arity = confirmedBuildArity(src);
4383
- if (arity !== null) return arity;
4384
- const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
4385
- recordDegradation({
4386
- code: "build-arity-unconfirmed",
4387
- severity: "warn",
4388
- where: `node("${def.type ?? "<inferred>"}")`,
4389
- subject,
4390
- fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and clones \`_ctx.state\` on every match`,
4391
- otherwise: "only the tiers the reducer actually declares would be captured (arity >= 1 children, >= 2 fields, >= 4 raw, >= 5 trivia, >= 6 state). Declare it with `node(..., { buildArity: n })` if it cannot be derived"
4392
- });
4393
- return null;
4394
- }
4395
- function buildReadsRaw(def) {
4396
- if (!def.build) return true;
4397
- if (def.buildRawUnused === true) return false;
4398
- const arity = confirmedArityForDef(def);
4399
- if (arity === null) return true;
4400
- return arity >= 4;
4401
- }
4402
- function buildReadsTrivia(def) {
4403
- if (!def.build) return true;
4404
- const arity = confirmedArityForDef(def);
4405
- if (arity === null) return true;
4406
- return arity >= 5;
4407
- }
4408
- function buildReadsState(def) {
4409
- if (!def.build) return true;
4410
- const arity = confirmedArityForDef(def);
4411
- if (arity === null) return true;
4412
- return arity >= 6;
4430
+ };
4413
4431
  }
4414
-
4415
- // src/compiler/fields.ts
4416
- function buildReadsFields(def) {
4417
- if (!def.build) return true;
4418
- const arity = confirmedArityForDef(def);
4419
- if (arity === null) return true;
4420
- return arity >= 2;
4432
+ function unwrapSeparator(separator) {
4433
+ return "_keepSeparator" in separator ? { sep: separator._keepSeparator, keep: true } : { sep: separator, keep: false };
4421
4434
  }
4422
- function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
4423
- if (seen.has(p)) return false;
4424
- seen.add(p);
4425
- const d = p._def;
4426
- switch (d.tag) {
4427
- case "field":
4428
- return true;
4429
- case "node":
4430
- return false;
4431
- case "lazy": {
4432
- try {
4433
- return parserHasOwnFields(d.thunk(), seen);
4434
- } catch {
4435
- return false;
4435
+ function sepBy(combinator, separatorArg, opts = {}) {
4436
+ const { sep: separator, keep: keepSeparators } = unwrapSeparator(separatorArg);
4437
+ const separatorLeads = leadsTrivia(separator);
4438
+ const { min, max } = resolveBounds("sepBy()", opts);
4439
+ const trailing = opts.trailing ?? "forbid";
4440
+ const meta = {
4441
+ firstSet: combinator._meta.firstSet,
4442
+ canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
4443
+ isTrivia: false
4444
+ };
4445
+ let expected;
4446
+ const failAt2 = (at) => {
4447
+ expected ??= deriveExpected(combinator);
4448
+ return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
4449
+ };
4450
+ const scalarExpected = deriveExpected(combinator);
4451
+ const scalarChild = scalarOf(combinator);
4452
+ const scalarSeparator = scalarOf(separator);
4453
+ const parseScalar = (input, pos, ctx) => {
4454
+ let cur = scalarChild(input, pos, ctx);
4455
+ if (cur < 0) {
4456
+ if (min >= 1) {
4457
+ ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
4458
+ return ~pos;
4436
4459
  }
4460
+ ctx._sv = [];
4461
+ return pos;
4437
4462
  }
4438
- case "sequence":
4439
- case "choice":
4440
- return d.parsers.some((x) => parserHasOwnFields(x, seen));
4441
- case "dispatch":
4442
- return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => parserHasOwnFields(entry.parser, seen)) : false) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
4443
- case "sepBy":
4444
- return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
4445
- case "grammar":
4446
- return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
4447
- case "scanTo":
4448
- return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen)) || d.recoverAt !== void 0 && parserHasOwnFields(d.recoverAt, seen) || d.stopAt !== void 0 && parserHasOwnFields(d.stopAt, seen);
4449
- case "recover":
4450
- return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
4451
- case "routed":
4452
- return d.fallback ? parserHasOwnFields(d.fallback, seen) : false;
4453
- case "many":
4454
- case "oneOrMore":
4455
- case "optional":
4456
- case "attempt":
4457
- case "transform":
4458
- case "trivia":
4459
- case "token":
4460
- case "leaf":
4461
- case "label":
4462
- case "expect":
4463
- case "withCtx":
4464
- case "not":
4465
- case "peek":
4466
- return parserHasOwnFields(d.parser, seen);
4467
- default:
4468
- return false;
4469
- }
4470
- }
4471
- function buildFieldMap(captures) {
4472
- if (!captures?.length) return void 0;
4473
- const out = {};
4474
- for (const cap of captures) {
4475
- const entry = cap.leading === void 0 ? { value: cap.value, span: cap.span } : { value: cap.value, span: cap.span, leading: cap.leading, trailing: cap.trailing };
4476
- const current = out[cap.name];
4477
- if (current === void 0) out[cap.name] = entry;
4478
- else if (Array.isArray(current)) current.push(entry);
4479
- else out[cap.name] = [current, entry];
4480
- }
4481
- return out;
4463
+ const values = [ctx._sv];
4464
+ while (values.length < max && (cur < input.length || values.length < min)) {
4465
+ const beforeSeparator = cur;
4466
+ let separatorPos = cur;
4467
+ if (ctx.trivia) separatorPos = advanceTrivia(input, cur, ctx);
4468
+ const separatorEnd = scalarSeparator(input, separatorPos, ctx);
4469
+ if (separatorEnd < 0) break;
4470
+ let itemPos = separatorEnd;
4471
+ if (ctx.trivia) itemPos = advanceTrivia(input, itemPos, ctx);
4472
+ const itemEnd = scalarChild(input, itemPos, ctx);
4473
+ if (itemEnd < 0) {
4474
+ cur = trailing === "allow" ? separatorEnd : beforeSeparator;
4475
+ break;
4476
+ }
4477
+ values.push(ctx._sv);
4478
+ cur = itemEnd;
4479
+ }
4480
+ if (values.length < min) {
4481
+ ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
4482
+ return ~cur;
4483
+ }
4484
+ ctx._sv = values;
4485
+ return cur;
4486
+ };
4487
+ return {
4488
+ _tag: "sepBy",
4489
+ _meta: meta,
4490
+ _def: {
4491
+ tag: "sepBy",
4492
+ parser: combinator,
4493
+ separator,
4494
+ min,
4495
+ ...max === Infinity ? {} : { max },
4496
+ ...trailing === "forbid" ? {} : { trailing },
4497
+ // Carried into the IR so the COMPILED engine demotes exactly where the
4498
+ // interpreter does. The two engines have drifted three separate ways across
4499
+ // this option surface already; parity is held by test/parity, not by hope.
4500
+ ...keepSeparators ? { keepSeparators: true } : {}
4501
+ },
4502
+ _parseScalar: keepSeparators || separatorLeads ? void 0 : parseScalar,
4503
+ parse(input, pos, ctx) {
4504
+ const first = combinator.parse(input, pos, ctx);
4505
+ const values = [];
4506
+ let cur;
4507
+ if (first.ok) {
4508
+ values.push(first.value);
4509
+ cur = first.span.end;
4510
+ } else {
4511
+ if (first.committed) return first;
4512
+ const term = ctx._tolerant ? ctx._sync : void 0;
4513
+ if (term === void 0 || matchesAt(term, input, pos, ctx)) {
4514
+ if (min >= 1) return failAt2(pos);
4515
+ return { ok: true, value: [], span: { start: pos, end: pos } };
4516
+ }
4517
+ expected ??= deriveExpected(combinator);
4518
+ const rec = recoverScan(input, pos, ctx, orSentinel(separator, term), expected);
4519
+ values.push(rec.error);
4520
+ captureError(ctx, rec.error);
4521
+ cur = rec.end;
4522
+ }
4523
+ while (cur < input.length || values.length < min) {
4524
+ if (values.length >= max) break;
4525
+ const loopMark = saveTriviaMark(ctx);
4526
+ let sepPos = cur;
4527
+ if (ctx.trivia) {
4528
+ if (needsDeferredTriviaCommit(ctx)) {
4529
+ sepPos = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
4530
+ } else {
4531
+ sepPos = advanceTrivia(input, cur, ctx);
4532
+ }
4533
+ }
4534
+ if (separatorLeads) LEAD.at = cur;
4535
+ const sep = separator.parse(input, sepPos, ctx);
4536
+ if (!sep.ok) {
4537
+ rollbackTrivia(ctx, loopMark);
4538
+ if (sep.committed) return sep;
4539
+ break;
4540
+ }
4541
+ if (!keepSeparators) demoteCapturedToRaw(ctx, loopMark.leaves);
4542
+ const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
4543
+ let nextPos = sep.span.end;
4544
+ if (ctx.trivia) {
4545
+ if (needsDeferredTriviaCommit(ctx)) {
4546
+ nextPos = commitTriviaScan(scanTriviaCompact(input, sep.span.end, ctx));
4547
+ } else {
4548
+ nextPos = advanceTrivia(input, sep.span.end, ctx);
4549
+ }
4550
+ }
4551
+ const next = combinator.parse(input, nextPos, ctx);
4552
+ if (!next.ok) {
4553
+ if (next.committed) return next;
4554
+ const term = ctx._tolerant ? ctx._sync : void 0;
4555
+ if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
4556
+ expected ??= deriveExpected(combinator);
4557
+ const rec = recoverScan(input, nextPos, ctx, orSentinel(separator, term), expected);
4558
+ values.push(rec.error);
4559
+ captureError(ctx, rec.error);
4560
+ cur = rec.end;
4561
+ continue;
4562
+ }
4563
+ if (sepMark !== void 0) {
4564
+ rollbackTrivia(ctx, sepMark);
4565
+ cur = sep.span.end;
4566
+ break;
4567
+ }
4568
+ rollbackTrivia(ctx, loopMark);
4569
+ break;
4570
+ }
4571
+ values.push(next.value);
4572
+ cur = next.span.end;
4573
+ }
4574
+ if (values.length < min) return failAt2(cur);
4575
+ return { ok: true, value: values, span: { start: pos, end: cur } };
4576
+ }
4577
+ };
4482
4578
  }
4483
4579
 
4484
- // src/combinators/field-trivia.ts
4485
- var LEAD = { at: -1 };
4486
- var FIELD_LEADING_TRIVIA = 1;
4487
- var FIELD_TRAILING_TRIVIA = 2;
4488
- function triviaFieldCapture(name, value, input, start, end, leadFrom, trailEnd) {
4580
+ // src/combinators/map.ts
4581
+ var JSON_STRING_BODY = String.raw`(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*`;
4582
+ function transform(combinator, fn) {
4583
+ const observesSpan = fn.length > 1;
4584
+ const sequenceDef = combinator._def.tag === "sequence" ? combinator._def : void 0;
4585
+ const terms = sequenceDef?.parsers;
4586
+ const inner = terms?.[1];
4587
+ const fused = terms?.length === 3 && terms[0]._def.tag === "literal" && terms[0]._def.value === '"' && !terms[0]._def.caseInsensitive && inner._def.tag === "regex" && inner._def.source === JSON_STRING_BODY && inner._def.flags === "" && terms[2]._def.tag === "literal" && terms[2]._def.value === '"' && !terms[2]._def.caseInsensitive && inner._stickyRegex !== void 0;
4588
+ const child = scalarOf(combinator);
4589
+ const parseScalar = fused ? (() => {
4590
+ const body = inner._stickyRegex;
4591
+ const tuple = ['"', "", '"'];
4592
+ const expected = [JSON.stringify('"')];
4593
+ return (input, pos, ctx) => {
4594
+ if (ctx.trivia !== void 0) {
4595
+ const end2 = child(input, pos, ctx);
4596
+ if (end2 < 0) return end2;
4597
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end: end2 }) : fn(ctx._sv);
4598
+ return end2;
4599
+ }
4600
+ if (input.charCodeAt(pos) !== 34) {
4601
+ ctx._fx = expected;
4602
+ return ~pos;
4603
+ }
4604
+ body.lastIndex = pos + 1;
4605
+ const match = body.exec(input);
4606
+ const close = pos + 1 + match[0].length;
4607
+ if (input.charCodeAt(close) !== 34) {
4608
+ ctx._fx = expected;
4609
+ return ~close;
4610
+ }
4611
+ tuple[1] = match[0];
4612
+ const end = close + 1;
4613
+ ctx._sv = observesSpan ? fn(tuple, { start: pos, end }) : fn(tuple);
4614
+ return end;
4615
+ };
4616
+ })() : /* @__PURE__ */ (() => {
4617
+ return (input, pos, ctx) => {
4618
+ const end = child(input, pos, ctx);
4619
+ if (end < 0) return end;
4620
+ ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end }) : fn(ctx._sv);
4621
+ return end;
4622
+ };
4623
+ })();
4489
4624
  return {
4490
- name,
4491
- value,
4492
- span: { start, end },
4493
- leading: leadFrom >= 0 && leadFrom < start ? input.slice(leadFrom, start) : "",
4494
- trailing: trailEnd > end ? input.slice(end, trailEnd) : ""
4625
+ _tag: "transform",
4626
+ _meta: combinator._meta,
4627
+ _def: { tag: "transform", parser: combinator, fn },
4628
+ _parseScalar: parseScalar,
4629
+ parse(input, pos, ctx) {
4630
+ const result = combinator.parse(input, pos, ctx);
4631
+ if (!result.ok) return result;
4632
+ return { ok: true, value: fn(result.value, result.span), span: result.span };
4633
+ }
4495
4634
  };
4496
4635
  }
4497
- function leadsTrivia(p) {
4498
- return p._def.tag === "field" && p._def.leadingTrivia === true;
4636
+ function trivia(combinator) {
4637
+ const labeled = analyzeLabeledTrivia(combinator);
4638
+ const kindLabels = labeled?.labels;
4639
+ return {
4640
+ _tag: combinator._tag,
4641
+ _meta: {
4642
+ ...combinator._meta,
4643
+ isTrivia: true,
4644
+ triviaScanner: fastTriviaScanner(combinator),
4645
+ // Every labeled scan of this trivia reads its spec; the module's four-slot
4646
+ // memo alone thrashes on a grammar with more than four trivia tables.
4647
+ labeledTriviaSpec: labeled,
4648
+ ...kindLabels ? { triviaKindLabels: kindLabels } : {}
4649
+ },
4650
+ _def: { tag: "trivia", parser: combinator },
4651
+ _parseScalar: scalarOf(combinator),
4652
+ parse: combinator.parse.bind(combinator)
4653
+ };
4499
4654
  }
4500
-
4501
- // src/table/ops.ts
4502
- var OP_LIT = 1;
4503
- var OP_RX = 2;
4504
- var OP_SEQ = 3;
4505
- var OP_SEQV = 4;
4506
- var OP_CHOICE = 5;
4507
- var OP_REP = 6;
4508
- var OP_REPV = 7;
4509
- var OP_OPT = 8;
4510
- var OP_XFORM = 9;
4511
- var OP_NODE = 10;
4512
- var OP_RULE = 11;
4513
- var OP_GATE = 12;
4514
- var OP_NOT = 13;
4515
- var OP_PEEK = 14;
4516
- var OP_LEAF = 15;
4517
- var OP_EMPTY = 16;
4518
- var OP_LIT_TRACK = 17;
4519
- var OP_RX_TRACK = 18;
4520
- var OP_NODE_TRACK = 19;
4521
- var OP_SCOPE = 20;
4522
- var OP_EXPECT = 21;
4523
- var OP_SEQX = 22;
4524
- var OP_SCAN = 23;
4525
- var OP_FIELD = 24;
4526
- var OP_LIT_CI = 27;
4527
- var OP_LIT_CI_TRACK = 28;
4528
- var OP_TOKEN = 29;
4529
- var OP_SCOPE_CAP = 30;
4530
- var OP_WITHCTX = 31;
4531
- var OP_GUARD = 32;
4532
- var OP_ADJ = 33;
4533
- var OP_GREEDY = 34;
4534
- var OP_REJECT = 35;
4535
- var OP_ARMGATE = 36;
4536
- var OP_LIVE = 37;
4537
- var OP_ATTEMPT = 38;
4538
- var OP_LABEL = 39;
4539
- var OP_COV = 40;
4540
- var OP_SCOPE_PLAIN = 41;
4541
- var OP_LEX_BODY = 42;
4542
- var OP_LEX_PROGRAM = 43;
4543
- var OP_SOURCE_LEAF = 44;
4544
- var OP_DISPATCH = 25;
4545
- var OP_ROUTED = 26;
4546
- var OP_NAMES = {
4547
- [OP_LIT]: "LIT",
4548
- [OP_RX]: "RX",
4549
- [OP_SEQ]: "SEQ",
4550
- [OP_SEQV]: "SEQV",
4551
- [OP_CHOICE]: "CHOICE",
4552
- [OP_REP]: "REP",
4553
- [OP_REPV]: "REPV",
4554
- [OP_OPT]: "OPT",
4555
- [OP_XFORM]: "XFORM",
4556
- [OP_NODE]: "NODE",
4557
- [OP_RULE]: "RULE",
4558
- [OP_GATE]: "GATE",
4559
- [OP_NOT]: "NOT",
4560
- [OP_PEEK]: "PEEK",
4561
- [OP_LEAF]: "LEAF",
4562
- [OP_SOURCE_LEAF]: "SOURCE_LEAF",
4563
- [OP_EMPTY]: "EMPTY",
4564
- [OP_LIT_TRACK]: "LIT_TRACK",
4565
- [OP_RX_TRACK]: "RX_TRACK",
4566
- [OP_NODE_TRACK]: "NODE_TRACK",
4567
- [OP_SCOPE]: "SCOPE",
4568
- [OP_EXPECT]: "EXPECT",
4569
- [OP_SEQX]: "SEQX",
4570
- [OP_SCAN]: "SCAN",
4571
- [OP_FIELD]: "FIELD",
4572
- [OP_LIT_CI]: "LIT_CI",
4573
- [OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
4574
- [OP_DISPATCH]: "DISPATCH",
4575
- [OP_ROUTED]: "ROUTED",
4576
- [OP_TOKEN]: "TOKEN",
4577
- [OP_SCOPE_CAP]: "SCOPE_CAP",
4578
- [OP_WITHCTX]: "WITHCTX",
4579
- [OP_GUARD]: "GUARD",
4580
- [OP_ADJ]: "ADJ",
4581
- [OP_GREEDY]: "GREEDY",
4582
- [OP_REJECT]: "REJECT",
4583
- [OP_ARMGATE]: "ARMGATE",
4584
- [OP_LIVE]: "LIVE",
4585
- [OP_ATTEMPT]: "ATTEMPT",
4586
- [OP_LABEL]: "LABEL",
4587
- [OP_COV]: "COV",
4588
- [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
4589
- [OP_LEX_BODY]: "LEX_BODY",
4590
- [OP_LEX_PROGRAM]: "LEX_PROGRAM"
4591
- };
4592
-
4593
- // src/combinators/adjacency.ts
4594
- function adjacencyOf(p) {
4595
- return p._def.tag === "adjacency" ? p._def : null;
4596
- }
4597
- function adjacencyExpected(def) {
4598
- if (def.polarity === "adjacent") return "adjacent";
4599
- return def.kinds === void 0 ? "notAdjacent" : `notAdjacent(${def.kinds.join("|")})`;
4600
- }
4601
- function holdsAdjacency(input, cur, ctx, def) {
4602
- return adjacencyHolds(input, cur, ctx, def.polarity === "notAdjacent", def.kinds);
4603
- }
4604
- function adjacencyHolds(input, cur, ctx, negated, kinds) {
4605
- const end = probeTriviaEnd(input, cur, ctx);
4606
- if (!negated) return end === cur;
4607
- if (end === cur) return false;
4608
- if (kinds === void 0) return true;
4609
- const want = resolveAdjacencyKindMask(ctx.trivia, kinds);
4610
- const spec = analyzeLabeledTrivia(ctx.trivia);
4611
- return (triviaKindMaskAt(input, cur, spec, ctx.state) & want) !== 0;
4612
- }
4613
- function adjacencyFail(pos, def) {
4614
- return { ok: false, expected: [adjacencyExpected(def)], span: { start: pos, end: pos } };
4615
- }
4616
- var NO_STANDALONE_PARSE = "adjacency assertions are boundary tests: use adjacent()/notAdjacent() as a NON-FIRST term of a sequence(), where the preceding term defines the gap being asserted.";
4617
- function adjacencyMisuse(polarity) {
4618
- return new TypeError(`${polarity}(): ${NO_STANDALONE_PARSE}`);
4655
+ function classifiedTrivia(arms) {
4656
+ const entries = Object.entries(arms);
4657
+ if (entries.length === 0) {
4658
+ throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
4659
+ }
4660
+ for (let i = 0; i < entries.length; i++) {
4661
+ const [name, arm] = entries[i];
4662
+ const first = arm._meta.firstSet;
4663
+ if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
4664
+ throw new TypeError(
4665
+ `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
4666
+ );
4667
+ }
4668
+ }
4669
+ const labeledArms = entries.map(([name, arm]) => label(name, arm));
4670
+ const result = trivia(oneOrMore(choice(...labeledArms)));
4671
+ result._meta.rootTriviaClassified = true;
4672
+ return result;
4619
4673
  }
4620
- function adjacencyCombinator(def) {
4621
- const meta = {
4622
- // Zero-width and never leading (sequence() rejects index 0), so it contributes
4623
- // no first char. `isZeroWidthAssertion` drops it from a sequence's first-set
4624
- // rather than letting `any` poison the enclosing choice's dispatch.
4625
- firstSet: { kind: "any" },
4626
- canMatchNewline: false,
4627
- isTrivia: false
4628
- };
4674
+ function label(name, combinator) {
4629
4675
  return {
4630
- _tag: "adjacency",
4631
- _meta: meta,
4632
- _def: def,
4633
- parse() {
4634
- throw adjacencyMisuse(def.polarity);
4676
+ _tag: combinator._tag,
4677
+ _meta: combinator._meta,
4678
+ _def: { tag: "label", label: name, parser: combinator },
4679
+ parse(input, pos, ctx) {
4680
+ const result = combinator.parse(input, pos, ctx);
4681
+ if (!result.ok) return { ok: false, expected: [name], span: result.span };
4682
+ return result;
4635
4683
  }
4636
4684
  };
4637
4685
  }
4638
- function adjacent() {
4639
- return adjacencyCombinator({ tag: "adjacency", polarity: "adjacent" });
4640
- }
4641
- function notAdjacent(options) {
4642
- const kinds = options?.kinds;
4643
- if (kinds !== void 0) {
4644
- if (!Array.isArray(kinds) || kinds.length === 0) {
4645
- throw new TypeError("notAdjacent({ kinds }): kinds must be a non-empty array of trivia category names.");
4646
- }
4647
- for (const k of kinds) {
4648
- if (typeof k !== "string" || k.length === 0) {
4649
- throw new TypeError(`notAdjacent({ kinds }): every kind must be a non-empty string, got ${JSON.stringify(k)}.`);
4686
+ function field(name, combinator, opts) {
4687
+ const leadingTrivia = opts?.leadingTrivia === true;
4688
+ const trailingTrivia = opts?.trailingTrivia === true;
4689
+ if (!leadingTrivia && !trailingTrivia) {
4690
+ return {
4691
+ _tag: combinator._tag,
4692
+ _meta: combinator._meta,
4693
+ _def: { tag: "field", name, parser: combinator },
4694
+ parse(input, pos, ctx) {
4695
+ const result = combinator.parse(input, pos, ctx);
4696
+ if (!result.ok) return result;
4697
+ ctx._fields?.push({ name, value: result.value, span: result.span });
4698
+ return result;
4650
4699
  }
4651
- }
4700
+ };
4652
4701
  }
4653
- return adjacencyCombinator(
4654
- kinds === void 0 ? { tag: "adjacency", polarity: "notAdjacent" } : { tag: "adjacency", polarity: "notAdjacent", kinds: [...kinds] }
4655
- );
4702
+ return {
4703
+ _tag: combinator._tag,
4704
+ _meta: combinator._meta,
4705
+ _def: {
4706
+ tag: "field",
4707
+ name,
4708
+ parser: combinator,
4709
+ ...leadingTrivia ? { leadingTrivia: true } : {},
4710
+ ...trailingTrivia ? { trailingTrivia: true } : {}
4711
+ },
4712
+ parse(input, pos, ctx) {
4713
+ const leadFrom = LEAD.at;
4714
+ LEAD.at = -1;
4715
+ const result = combinator.parse(input, pos, ctx);
4716
+ if (!result.ok) return result;
4717
+ const end = result.span.end;
4718
+ const trailEnd = trailingTrivia && ctx.trivia ? consumeTrivia(input, end, ctx) : end;
4719
+ ctx._fields?.push(triviaFieldCapture(name, result.value, input, result.span.start, end, leadingTrivia ? leadFrom : -1, trailEnd));
4720
+ return trailEnd === end ? result : { ok: true, value: result.value, span: { start: result.span.start, end: trailEnd } };
4721
+ }
4722
+ };
4656
4723
  }
4657
4724
 
4658
- // src/combinators/node.ts
4659
- function unwrapChild(child) {
4660
- return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
4661
- }
4662
- function isCstChild(value) {
4663
- return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "parseError");
4664
- }
4665
- function missingInferredType() {
4666
- throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
4667
- }
4668
- function normalizeBuildArity(arity) {
4669
- if (arity === void 0) return void 0;
4670
- if (!Number.isInteger(arity) || arity < 0 || arity > 6) {
4671
- throw new Error("node() buildArity must be an integer 0..6 (children, fields, span, rawChildren, triviaLog, state)");
4725
+ // src/combinators/regex.ts
4726
+ var SCAN_BAIL_AT = 64;
4727
+ function inRanges3(cp, ranges) {
4728
+ for (let i = 0; i < ranges.length; i++) {
4729
+ const [lo, hi] = ranges[i];
4730
+ if (cp >= lo && cp <= hi) return true;
4672
4731
  }
4673
- return arity;
4732
+ return false;
4674
4733
  }
4675
- function normalizeProject(project) {
4676
- if (project === void 0) return void 0;
4677
- if (!Number.isInteger(project) || project < 0) {
4678
- throw new Error("node() project child must be a non-negative integer");
4679
- }
4680
- return project;
4734
+ function readClassRanges(body) {
4735
+ if (body.startsWith("^")) return null;
4736
+ return parseClassRanges(body);
4681
4737
  }
4682
- function projectChild(children2, project, type) {
4683
- if (!(project in children2)) {
4684
- throw new Error(`node(${JSON.stringify(type)}) project child ${project} was not captured`);
4738
+ function shortScanner(source, flags) {
4739
+ if (/[imsuvy]/.test(flags)) return null;
4740
+ let ranges = null;
4741
+ let quant = "";
4742
+ if (source[0] === "[") {
4743
+ let end = 1;
4744
+ while (end < source.length && source[end] !== "]") {
4745
+ if (source[end] === "\\") end += 2;
4746
+ else end++;
4747
+ }
4748
+ if (source[end] !== "]") return null;
4749
+ ranges = readClassRanges(source.slice(1, end));
4750
+ quant = source.slice(end + 1);
4751
+ } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
4752
+ ranges = shorthandRanges(source[1]);
4753
+ quant = source.slice(2);
4685
4754
  }
4686
- return unwrapChild(children2[project]);
4755
+ if (!ranges || quant !== "+" && quant !== "*") return null;
4756
+ const minOne = quant === "+";
4757
+ return (input, pos) => {
4758
+ let end = pos;
4759
+ while (end < input.length && inRanges3(input.charCodeAt(end), ranges)) {
4760
+ end++;
4761
+ if (end - pos >= SCAN_BAIL_AT) return void 0;
4762
+ }
4763
+ return minOne && end === pos ? null : end;
4764
+ };
4687
4765
  }
4688
- function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
4689
- const hasExplicitType = typeof typeOrCombinator === "string";
4690
- const type = hasExplicitType ? typeOrCombinator : void 0;
4691
- const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
4692
- const buildArg = hasExplicitType ? buildOrOpts : combinatorOrBuild;
4693
- const build = typeof buildArg === "function" ? buildArg : void 0;
4694
- const opts = typeof buildArg === "function" ? maybeOpts : buildArg ?? maybeOpts;
4695
- const project = normalizeProject(opts?.project);
4696
- if (project !== void 0 && build !== void 0) {
4697
- throw new Error("node() options cannot combine project with a build callback");
4698
- }
4699
- const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
4700
- const meta = {
4701
- firstSet: combinator._meta.firstSet,
4702
- canMatchNewline: combinator._meta.canMatchNewline,
4703
- isTrivia: false
4766
+ function asciiCaseFold(fs) {
4767
+ if (fs.kind !== "ranges") return fs;
4768
+ const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
4769
+ const add = (lo, hi) => {
4770
+ if (lo <= hi) ranges.push({ lo, hi });
4704
4771
  };
4705
- const unwrap = opts?.unwrap === true;
4706
- const collapse = opts?.collapse === true;
4707
- const captureTrivia = opts?.captureTrivia === true;
4708
- const trailingTrivia = opts?.trailingTrivia === true;
4709
- const tags = opts?.tags;
4710
- const buildArity = normalizeBuildArity(opts?.buildArity);
4711
- if (unwrap && collapse) {
4712
- throw new Error("node() options cannot set both unwrap and collapse");
4772
+ for (const r of fs.ranges) {
4773
+ const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
4774
+ add(uLo + 32, uHi + 32);
4775
+ const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
4776
+ add(lLo - 32, lHi - 32);
4713
4777
  }
4714
- if (project !== void 0 && (unwrap || collapse)) {
4715
- throw new Error("node() options cannot combine project with unwrap or collapse");
4778
+ ranges.sort((a, b) => a.lo - b.lo);
4779
+ const merged = [];
4780
+ for (const r of ranges) {
4781
+ const last = merged[merged.length - 1];
4782
+ if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
4783
+ else merged.push({ lo: r.lo, hi: r.hi });
4716
4784
  }
4717
- const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || buildArity !== void 0 || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...buildArity !== void 0 ? { buildArity } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
4718
- const capturesTrivia = captureTrivia || (build ? buildReadsTrivia(def) : project === void 0);
4719
- const clonesState = build ? buildReadsState(def) : project === void 0;
4720
- const hasOwnFields = parserHasOwnFields(combinator);
4721
- const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
4722
- const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
4723
- let failExpected;
4785
+ return { kind: "ranges", ranges: merged };
4786
+ }
4787
+ function regex(pattern, flags = "") {
4788
+ const source = typeof pattern === "string" ? pattern : pattern.source;
4789
+ const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
4790
+ const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
4791
+ const scan = shortScanner(source, resolvedFlags);
4792
+ const raw = firstSetFromRegex(source);
4793
+ const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
4794
+ const canMatchNewline = raw.canMatchNewline;
4795
+ const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
4796
+ const def = { tag: "regex", source, flags: resolvedFlags };
4797
+ const expected = directTerminalFailureExpected(def);
4798
+ const parseScalar = (input, pos, ctx) => {
4799
+ const scanEnd = scan?.(input, pos);
4800
+ if (scanEnd !== void 0) {
4801
+ if (scanEnd === null) {
4802
+ ctx._fx = expected;
4803
+ return ~pos;
4804
+ }
4805
+ ctx._sv = input.slice(pos, scanEnd);
4806
+ return scanEnd;
4807
+ }
4808
+ anchored.lastIndex = pos;
4809
+ const match = anchored.exec(input);
4810
+ if (match === null) {
4811
+ ctx._fx = expected;
4812
+ return ~pos;
4813
+ }
4814
+ ctx._sv = match[0];
4815
+ return pos + match[0].length;
4816
+ };
4724
4817
  return {
4725
- _tag: "node",
4818
+ _tag: "regex",
4726
4819
  _meta: meta,
4727
4820
  _def: def,
4821
+ _stickyRegex: anchored,
4822
+ _parseScalar: parseScalar,
4728
4823
  parse(input, pos, ctx) {
4729
- if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
4730
- if (failExpected === void 0) {
4731
- const e = deriveExpected(combinator);
4732
- failExpected = e.length > 0 ? e : [combinator._tag];
4733
- }
4734
- return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
4735
- }
4736
- const hasDirectValue = build !== void 0 || project !== void 0;
4737
- const hostCst = hasDirectValue && cstOutputHost(ctx.build);
4738
- const structural = !hasDirectValue;
4739
- const hostCapturesThisType = structural && def.type !== void 0 && ctx.build?._parsemanCaptureTrivia !== void 0 ? ctx.build._parsemanCaptureTrivia(def.type) : void 0;
4740
- const effTrivia = structural ? captureTrivia || trailingTrivia || hostCapturesThisType !== false : capturesTrivia || hostCst;
4741
- const effFields = capturesFields || hostCst && hasOwnFields;
4742
- const saved = beginCstNodeCapture(ctx);
4743
- const savedFields = ctx._fields;
4744
- ctx._fields = effFields ? [] : void 0;
4745
- const savedMask = ctx._triviaCaptureMask;
4746
- if (build === void 0 && project === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
4747
- ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
4748
- }
4749
- if (!effTrivia) ctx.captureTrivia = false;
4750
- let r = combinator.parse(input, pos, ctx);
4751
- if (r.ok && trailingTrivia && ctx.trivia) {
4752
- const end = consumeTrivia(input, r.span.end, ctx);
4753
- r = { ...r, span: { start: r.span.start, end } };
4824
+ const scanEnd = scan?.(input, pos);
4825
+ if (scanEnd !== void 0) {
4826
+ if (scanEnd === null) return failAt(ctx, directTerminalFailureExpected(def), pos);
4827
+ const value = input.slice(pos, scanEnd);
4828
+ const span2 = { start: pos, end: scanEnd };
4829
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
4830
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
4831
+ return { ok: true, value, span: span2 };
4754
4832
  }
4755
- const fields = effFields ? buildFieldMap(ctx._fields) : void 0;
4756
- ctx._fields = savedFields;
4757
- ctx._triviaCaptureMask = savedMask;
4758
- const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
4759
- if (!r.ok) return r;
4760
- const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
4761
- const nodeType = def.type ?? missingInferredType();
4762
- const cstOutput = cstOutputHost(ctx.build);
4763
- const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
4764
- const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
4765
- const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : (cstOutput || !build && project === void 0) && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
4766
- const rawEntry2 = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
4767
- if (saved.buf !== void 0 || saved.ch !== void 0) {
4768
- pushCstChild(ctx, built, rawEntry2);
4833
+ anchored.lastIndex = pos;
4834
+ const m = anchored.exec(input);
4835
+ if (m === null) {
4836
+ return failAt(ctx, directTerminalFailureExpected(def), pos);
4769
4837
  }
4770
- return { ok: true, value: built, span };
4838
+ const span = { start: pos, end: pos + m[0].length };
4839
+ if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
4840
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
4841
+ return { ok: true, value: m[0], span };
4771
4842
  }
4772
4843
  };
4773
4844
  }
4774
4845
 
4775
- // src/combinators/trivia-boundary.ts
4776
- var MAX_DEPTH = 12;
4777
- function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
4778
- if (depth === 0) return true;
4779
- const d = p._def;
4780
- switch (d.tag) {
4781
- // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
4782
- case "sequence":
4783
- return d.parsers.length > 1;
4784
- case "many":
4785
- case "oneOrMore":
4786
- case "sepBy":
4787
- return true;
4788
- // Transparent wrappers — they add no position, so ask what they wrap.
4789
- case "node":
4790
- case "transform":
4791
- case "leaf":
4792
- case "label":
4793
- case "field":
4794
- case "token":
4795
- case "attempt":
4796
- case "optional":
4797
- case "expect":
4798
- case "withCtx":
4799
- case "recover":
4800
- case "trivia":
4801
- return hasOwnTriviaBoundary(d.parser, depth - 1);
4802
- // An alternation adds no position either, but ONE arm that has one is enough
4803
- // to make the scope meaningful for this rule.
4804
- case "choice":
4805
- return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
4806
- default:
4807
- return false;
4846
+ // src/table/lex-body.ts
4847
+ function buildLexBodyRecognizer(spec, constants) {
4848
+ const raw = constants[spec[0]];
4849
+ if (!(raw instanceof RegExp)) {
4850
+ throw new TypeError("table lexical body does not reference a RegExp constant");
4851
+ }
4852
+ if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
4853
+ throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
4808
4854
  }
4855
+ const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
4856
+ const suffix = spec[1];
4857
+ return (input, pos) => {
4858
+ re.lastIndex = pos;
4859
+ if (!re.test(input)) return -1;
4860
+ const end = re.lastIndex;
4861
+ if (suffix < 0) return end * 2;
4862
+ return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
4863
+ };
4809
4864
  }
4810
4865
 
4811
4866
  // src/table/lex-program.ts
@@ -5186,888 +5241,890 @@ function buildLexProgramRunner(spec, matchers, classes, expected) {
5186
5241
  };
5187
5242
  }
5188
5243
 
5189
- // src/combinators/repeat.ts
5190
- function repItem(combinator, input, cur, ctx, guardable, mandatory, rollbackNeeded) {
5191
- const mark = rollbackNeeded ? saveTriviaMark(ctx) : void 0;
5192
- let pos = cur;
5193
- if (ctx.trivia) {
5194
- if (needsDeferredTriviaCommit(ctx)) {
5195
- pos = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
5196
- } else {
5197
- pos = advanceTrivia(input, cur, ctx);
5198
- }
5199
- }
5200
- if (pos >= input.length && !mandatory) {
5201
- if (mark !== void 0) rollbackTrivia(ctx, mark);
5202
- return "stop";
5203
- }
5204
- if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
5205
- if (mark !== void 0) rollbackTrivia(ctx, mark);
5206
- return "stop";
5207
- }
5208
- const result = combinator.parse(input, pos, ctx);
5209
- if (!result.ok) {
5210
- if (mark !== void 0) rollbackTrivia(ctx, mark);
5211
- if (result.committed) return { fail: result, failPos: pos };
5212
- return { fail: result, failPos: pos };
5244
+ // src/table/program.ts
5245
+ function closureArtifact(prog) {
5246
+ return { ...prog, asm: [] };
5247
+ }
5248
+ function covKindCode(kind) {
5249
+ switch (kind) {
5250
+ case "rule":
5251
+ return 0;
5252
+ case "choice-arm":
5253
+ return 1;
5254
+ case "dispatch-arm":
5255
+ return 2;
5256
+ case "label":
5257
+ return 3;
5213
5258
  }
5214
- if (result.span.end === pos && !mandatory) {
5215
- if (mark !== void 0) rollbackTrivia(ctx, mark);
5216
- return "stop";
5259
+ }
5260
+ function covKindName(code) {
5261
+ switch (code) {
5262
+ case 0:
5263
+ return "rule";
5264
+ case 1:
5265
+ return "choice-arm";
5266
+ case 2:
5267
+ return "dispatch-arm";
5268
+ case 3:
5269
+ return "label";
5217
5270
  }
5218
- return result;
5219
5271
  }
5220
- function resolveBounds(what, opts) {
5221
- const min = opts.min ?? 0;
5222
- const max = opts.max ?? Infinity;
5223
- const bad = (msg) => {
5224
- throw new RangeError(`parseman: ${what} ${msg}`);
5225
- };
5226
- if (!Number.isInteger(min) || min < 0) bad(`min must be a non-negative integer (got ${String(opts.min)})`);
5227
- if (max !== Infinity && (!Number.isInteger(max) || max < 1)) bad(`max must be a positive integer (got ${String(opts.max)})`);
5228
- if (max < min) bad(`max (${max}) is less than min (${min}) \u2014 the combinator could never succeed`);
5229
- return { min, max };
5272
+ function covDefinitions(prog) {
5273
+ return (prog.cov ?? []).map(([id, kind]) => ({ id, kind: covKindName(kind) }));
5230
5274
  }
5231
- function repeatScalarTail(combinator, child, input, cur, ctx, guardable, values, remaining) {
5232
- while (cur < input.length && remaining > 0) {
5233
- let itemPos = cur;
5234
- if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
5235
- if (itemPos >= input.length) break;
5236
- if (guardable && !startsFirstSet(combinator, input, itemPos)) break;
5237
- const end = child(input, itemPos, ctx);
5238
- if (end <= itemPos) break;
5239
- if (values !== void 0) values.push(ctx._sv);
5240
- cur = end;
5241
- remaining--;
5242
- }
5243
- ctx._sv = values;
5244
- return cur;
5275
+ function scanRefs(s) {
5276
+ const refs = [];
5277
+ for (const r of [s.sentinel, s.recoverAt, s.stopAt, s.prefix, s.body, s.first, s.lazy]) if (r !== void 0) refs.push(r);
5278
+ refs.push(...s.skip);
5279
+ return refs;
5245
5280
  }
5246
- function many(combinator, opts = {}) {
5247
- const { min, max } = resolveBounds("many()", opts);
5248
- if (min >= 1) return atLeast(combinator, min, max);
5249
- const meta = {
5250
- firstSet: combinator._meta.firstSet,
5251
- canMatchNewline: combinator._meta.canMatchNewline,
5252
- isTrivia: false
5281
+ function expandCompact(p) {
5282
+ if ("code" in p) return p;
5283
+ return ownTableProgram({
5284
+ code: p.c,
5285
+ k: p.k,
5286
+ cc: p.x,
5287
+ fx: p.e,
5288
+ disp: p.d,
5289
+ rules: p.r,
5290
+ fns: p.f,
5291
+ lines: p.l ?? 0,
5292
+ dsp: p.p ?? [],
5293
+ ...p.lx === void 0 ? {} : { lex: p.lx },
5294
+ ...p.lp === void 0 ? {} : { lexPrograms: p.lp },
5295
+ ...p.lb === void 0 ? {} : { labels: p.lb },
5296
+ ...p.rc === void 0 ? {} : { classified: p.rc },
5297
+ ...p.h === void 0 ? {} : { hostMode: p.h },
5298
+ ...p.tv === void 0 ? {} : { triviaSpecs: p.tv },
5299
+ ...p.sc === void 0 ? {} : { scans: p.sc },
5300
+ ...p.ss === void 0 ? {} : { scanSkip: p.ss },
5301
+ ...p.so === void 0 ? {} : { scanSkipOf: p.so },
5302
+ ...p.rv === void 0 ? {} : { rec: p.rv },
5303
+ ...p.cv === void 0 ? {} : { cov: p.cv },
5304
+ ...p.a === void 0 ? {} : { asm: p.a }
5305
+ });
5306
+ }
5307
+ function validateDispatchSpec(spec, arity, fallbackRouted) {
5308
+ const invalid = (arms) => {
5309
+ for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
5310
+ return false;
5253
5311
  };
5254
- const def = { tag: "many", parser: combinator, min: 0, ...max === Infinity ? {} : { max } };
5255
- let expected;
5256
- const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
5257
- const scalarChild = scalarOf(combinator);
5258
- const parseScalar = (input, pos, ctx) => repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? void 0 : [], max);
5312
+ if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
5313
+ throw new TypeError("table: malformed dispatch");
5314
+ }
5315
+ }
5316
+ var TABLE_RUNTIME = /* @__PURE__ */ Symbol("parseman.tableRuntime");
5317
+ function ownTableProgram(prog, resolved, caches = {}) {
5318
+ const runtime = prog[TABLE_RUNTIME];
5259
5319
  return {
5260
- _tag: "many",
5261
- _meta: meta,
5262
- _def: def,
5263
- _parseScalar: parseScalar,
5264
- parse(input, pos, ctx) {
5265
- const values = def.valueUnused ? void 0 : [];
5266
- const rollbackNeeded = needsTriviaRollback(ctx);
5267
- let cur = pos;
5268
- let count = 0;
5269
- while (cur < input.length) {
5270
- if (count >= max) break;
5271
- const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
5272
- if (item === "stop") break;
5273
- if ("fail" in item) {
5274
- if (!item.fail.ok && item.fail.committed) return item.fail;
5275
- const sync = ctx._tolerant ? ctx._sync : void 0;
5276
- if (sync === void 0) break;
5277
- if (matchesAt(sync, input, item.failPos, ctx)) break;
5278
- expected ??= deriveExpected(combinator);
5279
- const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
5280
- if (values !== void 0) values.push(error);
5281
- captureError(ctx, error);
5282
- count++;
5283
- cur = end;
5284
- continue;
5285
- }
5286
- if (values !== void 0) values.push(item.value);
5287
- count++;
5288
- cur = item.span.end;
5289
- }
5290
- return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
5320
+ ...prog,
5321
+ [TABLE_RUNTIME]: {
5322
+ resolved,
5323
+ choiceRollbackMasks: caches.choiceRollbackMasks ?? runtime?.choiceRollbackMasks,
5324
+ nonCommittingChoiceSites: caches.nonCommittingChoiceSites ?? runtime?.nonCommittingChoiceSites,
5325
+ failureRollbackCleanSites: caches.failureRollbackCleanSites ?? runtime?.failureRollbackCleanSites
5291
5326
  }
5292
5327
  };
5293
5328
  }
5294
- function oneOrMore(combinator, opts = {}) {
5295
- return many(combinator, { ...opts, min: opts.min ?? 1 });
5329
+ function choiceRollbackMask(prog, ip) {
5330
+ return prog[TABLE_RUNTIME]?.choiceRollbackMasks?.get(ip);
5296
5331
  }
5297
- function atLeast(combinator, min, max) {
5298
- const meta = {
5299
- firstSet: combinator._meta.firstSet,
5300
- canMatchNewline: combinator._meta.canMatchNewline,
5301
- isTrivia: false
5302
- };
5303
- const def = { tag: "oneOrMore", parser: combinator, min, ...max === Infinity ? {} : { max } };
5304
- let expected;
5305
- const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
5306
- const scalarChild = scalarOf(combinator);
5307
- const parseScalar = (input, pos, ctx) => {
5308
- let cur = scalarChild(input, pos, ctx);
5309
- if (cur < 0) return cur;
5310
- const values = def.valueUnused ? void 0 : [ctx._sv];
5311
- for (let count = 1; count < min; count++) {
5312
- let itemPos = cur;
5313
- if (ctx.trivia) itemPos = advanceTrivia(input, cur, ctx);
5314
- const end = scalarChild(input, itemPos, ctx);
5315
- if (end < 0) return ~cur;
5316
- cur = end;
5317
- if (values !== void 0) values.push(ctx._sv);
5318
- }
5319
- return repeatScalarTail(combinator, scalarChild, input, cur, ctx, guardable, values, max - min);
5320
- };
5321
- return {
5322
- _tag: "oneOrMore",
5323
- _meta: meta,
5324
- _def: def,
5325
- _parseScalar: parseScalar,
5326
- parse(input, pos, ctx) {
5327
- const first = combinator.parse(input, pos, ctx);
5328
- if (!first.ok) return first;
5329
- const values = def.valueUnused ? void 0 : [first.value];
5330
- const rollbackNeeded = needsTriviaRollback(ctx);
5331
- let cur = first.span.end;
5332
- let count = 1;
5333
- while (count < min) {
5334
- const item = repItem(combinator, input, cur, ctx, guardable, true, rollbackNeeded);
5335
- if (item === "stop" || "fail" in item) {
5336
- if (item !== "stop" && !item.fail.ok && item.fail.committed) return item.fail;
5337
- expected ??= deriveExpected(combinator);
5338
- return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: cur, end: cur } };
5339
- }
5340
- if (values !== void 0) values.push(item.value);
5341
- cur = item.span.end;
5342
- count++;
5343
- }
5344
- while (cur < input.length) {
5345
- if (count >= max) break;
5346
- const item = repItem(combinator, input, cur, ctx, guardable, false, rollbackNeeded);
5347
- if (item === "stop") break;
5348
- if ("fail" in item) {
5349
- if (!item.fail.ok && item.fail.committed) return item.fail;
5350
- const sync = ctx._tolerant ? ctx._sync : void 0;
5351
- if (sync === void 0) break;
5352
- if (matchesAt(sync, input, item.failPos, ctx)) break;
5353
- expected ??= deriveExpected(combinator);
5354
- const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
5355
- if (values !== void 0) values.push(error);
5356
- captureError(ctx, error);
5357
- count++;
5358
- cur = end;
5359
- continue;
5360
- }
5361
- if (values !== void 0) values.push(item.value);
5362
- count++;
5363
- cur = item.span.end;
5364
- }
5365
- return { ok: true, value: values ?? void 0, span: { start: pos, end: cur } };
5366
- }
5367
- };
5332
+ function choiceCannotCommit(prog, ip) {
5333
+ return prog[TABLE_RUNTIME]?.nonCommittingChoiceSites?.has(ip) === true;
5368
5334
  }
5369
- function optional(combinator) {
5370
- const meta = {
5371
- firstSet: combinator._meta.firstSet,
5372
- canMatchNewline: combinator._meta.canMatchNewline,
5373
- isTrivia: false
5374
- };
5375
- const firstSetSkippable = !matchesEmpty(combinator);
5376
- const scalarChild = scalarOf(combinator);
5377
- const parseScalar = (input, pos, ctx) => {
5378
- if (firstSetSkippable && !startsFirstSet(combinator, input, pos)) {
5379
- ctx._sv = null;
5380
- return pos;
5381
- }
5382
- const end = scalarChild(input, pos, ctx);
5383
- if (end >= 0) return end;
5384
- ctx._sv = null;
5385
- return pos;
5386
- };
5387
- return {
5388
- _tag: "optional",
5389
- _meta: meta,
5390
- _def: { tag: "optional", parser: combinator },
5391
- _parseScalar: parseScalar,
5392
- parse(input, pos, ctx) {
5393
- if (firstSetSkippable && ctx._probe === void 0 && !startsFirstSet(combinator, input, pos)) {
5394
- return { ok: true, value: null, span: { start: pos, end: pos } };
5395
- }
5396
- const mark = saveTriviaMark(ctx);
5397
- const result = combinator.parse(input, pos, ctx);
5398
- if (result.ok) return result;
5399
- rollbackTrivia(ctx, mark);
5400
- if (result.committed) return result;
5401
- return { ok: true, value: null, span: { start: pos, end: pos } };
5335
+ function failureRollbackClean(prog, ip) {
5336
+ return prog[TABLE_RUNTIME]?.failureRollbackCleanSites?.has(ip) === true;
5337
+ }
5338
+ var WIDE_CLASS_PREFIX_LO = 65535;
5339
+ var WIDE_CLASS_PREFIX_HI = 0;
5340
+ function validateClassRange(lo, hi) {
5341
+ for (const cp of [lo, hi]) {
5342
+ if (!Number.isInteger(cp) || cp < 0 || cp > 1114111) {
5343
+ throw new TypeError(`table char-class endpoint is outside Unicode: ${cp}`);
5402
5344
  }
5403
- };
5345
+ }
5346
+ if (lo > hi) {
5347
+ throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
5348
+ }
5404
5349
  }
5405
- function unwrapSeparator(separator) {
5406
- return "_keepSeparator" in separator ? { sep: separator._keepSeparator, keep: true } : { sep: separator, keep: false };
5350
+ function encodeClassSpec(ranges) {
5351
+ let wide = false;
5352
+ for (const range of ranges) {
5353
+ validateClassRange(range.lo, range.hi);
5354
+ if (range.hi > 65535) wide = true;
5355
+ }
5356
+ if (!wide) {
5357
+ let spec2 = "";
5358
+ for (const range of ranges) spec2 += String.fromCharCode(range.lo, range.hi);
5359
+ return spec2;
5360
+ }
5361
+ let spec = String.fromCharCode(WIDE_CLASS_PREFIX_LO, WIDE_CLASS_PREFIX_HI);
5362
+ for (const range of ranges) {
5363
+ spec += String.fromCharCode(
5364
+ range.lo >>> 16,
5365
+ range.lo & 65535,
5366
+ range.hi >>> 16,
5367
+ range.hi & 65535
5368
+ );
5369
+ }
5370
+ return spec;
5407
5371
  }
5408
- function sepBy(combinator, separatorArg, opts = {}) {
5409
- const { sep: separator, keep: keepSeparators } = unwrapSeparator(separatorArg);
5410
- const separatorLeads = leadsTrivia(separator);
5411
- const { min, max } = resolveBounds("sepBy()", opts);
5412
- const trailing = opts.trailing ?? "forbid";
5413
- const meta = {
5414
- firstSet: combinator._meta.firstSet,
5415
- canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
5416
- isTrivia: false
5417
- };
5418
- let expected;
5419
- const failAt2 = (at) => {
5420
- expected ??= deriveExpected(combinator);
5421
- return { ok: false, expected: expected.length > 0 ? expected : [combinator._tag], span: { start: at, end: at } };
5422
- };
5423
- const scalarExpected = deriveExpected(combinator);
5424
- const scalarChild = scalarOf(combinator);
5425
- const scalarSeparator = scalarOf(separator);
5426
- const parseScalar = (input, pos, ctx) => {
5427
- let cur = scalarChild(input, pos, ctx);
5428
- if (cur < 0) {
5429
- if (min >= 1) {
5430
- ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
5431
- return ~pos;
5432
- }
5433
- ctx._sv = [];
5434
- return pos;
5435
- }
5436
- const values = [ctx._sv];
5437
- while (values.length < max && (cur < input.length || values.length < min)) {
5438
- const beforeSeparator = cur;
5439
- let separatorPos = cur;
5440
- if (ctx.trivia) separatorPos = advanceTrivia(input, cur, ctx);
5441
- const separatorEnd = scalarSeparator(input, separatorPos, ctx);
5442
- if (separatorEnd < 0) break;
5443
- let itemPos = separatorEnd;
5444
- if (ctx.trivia) itemPos = advanceTrivia(input, itemPos, ctx);
5445
- const itemEnd = scalarChild(input, itemPos, ctx);
5446
- if (itemEnd < 0) {
5447
- cur = trailing === "allow" ? separatorEnd : beforeSeparator;
5448
- break;
5449
- }
5450
- values.push(ctx._sv);
5451
- cur = itemEnd;
5372
+ function decodeClassSpec(spec) {
5373
+ if (spec.charCodeAt(0) === WIDE_CLASS_PREFIX_LO && spec.charCodeAt(1) === WIDE_CLASS_PREFIX_HI) {
5374
+ if ((spec.length - 2) % 4 !== 0) throw new TypeError("table char-class spec has a truncated wide range");
5375
+ if (spec.length === 2) throw new TypeError("table char-class spec has a wide prefix but no ranges");
5376
+ const ranges2 = [];
5377
+ for (let i = 2; i < spec.length; i += 4) {
5378
+ const lo = spec.charCodeAt(i) * 65536 + spec.charCodeAt(i + 1);
5379
+ const hi = spec.charCodeAt(i + 2) * 65536 + spec.charCodeAt(i + 3);
5380
+ validateClassRange(lo, hi);
5381
+ ranges2.push({ lo, hi });
5452
5382
  }
5453
- if (values.length < min) {
5454
- ctx._fx = scalarExpected.length > 0 ? scalarExpected : [combinator._tag];
5455
- return ~cur;
5383
+ return ranges2;
5384
+ }
5385
+ if (spec.length % 2 !== 0) throw new TypeError("table char-class spec has an unmatched range endpoint");
5386
+ const ranges = [];
5387
+ for (let i = 0; i < spec.length; i += 2) {
5388
+ const lo = spec.charCodeAt(i);
5389
+ const hi = spec.charCodeAt(i + 1);
5390
+ validateClassRange(lo, hi);
5391
+ ranges.push({ lo, hi });
5392
+ }
5393
+ return ranges;
5394
+ }
5395
+ function resolveClass(spec) {
5396
+ const ascii = new Uint8Array(128);
5397
+ const hi = [];
5398
+ for (const { lo, hi: up } of decodeClassSpec(spec)) {
5399
+ if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1;
5400
+ if (up >= 128) hi.push(Math.max(lo, 128), up);
5401
+ }
5402
+ return { ascii, hi };
5403
+ }
5404
+ function classHas2(cls, code) {
5405
+ if (code < 0) return false;
5406
+ if (code < 128) return cls.ascii[code] === 1;
5407
+ const hi = cls.hi;
5408
+ for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
5409
+ return false;
5410
+ }
5411
+ function resolveDispatch(arms, cc) {
5412
+ const ascii = new Uint8Array(128);
5413
+ const hi = [];
5414
+ const open = [];
5415
+ const armCls = [];
5416
+ let exclusive = true;
5417
+ let hiOwners = 0;
5418
+ for (let a = 0; a < arms.length; a++) {
5419
+ const ci = arms[a];
5420
+ if (ci < 0) {
5421
+ open.push(a);
5422
+ armCls.push(null);
5423
+ exclusive = false;
5424
+ continue;
5456
5425
  }
5457
- ctx._sv = values;
5458
- return cur;
5459
- };
5460
- return {
5461
- _tag: "sepBy",
5462
- _meta: meta,
5463
- _def: {
5464
- tag: "sepBy",
5465
- parser: combinator,
5466
- separator,
5467
- min,
5468
- ...max === Infinity ? {} : { max },
5469
- ...trailing === "forbid" ? {} : { trailing },
5470
- // Carried into the IR so the COMPILED engine demotes exactly where the
5471
- // interpreter does. The two engines have drifted three separate ways across
5472
- // this option surface already; parity is held by test/parity, not by hope.
5473
- ...keepSeparators ? { keepSeparators: true } : {}
5474
- },
5475
- _parseScalar: keepSeparators || separatorLeads ? void 0 : parseScalar,
5476
- parse(input, pos, ctx) {
5477
- const first = combinator.parse(input, pos, ctx);
5478
- const values = [];
5479
- let cur;
5480
- if (first.ok) {
5481
- values.push(first.value);
5482
- cur = first.span.end;
5483
- } else {
5484
- if (first.committed) return first;
5485
- const term = ctx._tolerant ? ctx._sync : void 0;
5486
- if (term === void 0 || matchesAt(term, input, pos, ctx)) {
5487
- if (min >= 1) return failAt2(pos);
5488
- return { ok: true, value: [], span: { start: pos, end: pos } };
5489
- }
5490
- expected ??= deriveExpected(combinator);
5491
- const rec = recoverScan(input, pos, ctx, orSentinel(separator, term), expected);
5492
- values.push(rec.error);
5493
- captureError(ctx, rec.error);
5494
- cur = rec.end;
5495
- }
5496
- while (cur < input.length || values.length < min) {
5497
- if (values.length >= max) break;
5498
- const loopMark = saveTriviaMark(ctx);
5499
- let sepPos = cur;
5500
- if (ctx.trivia) {
5501
- if (needsDeferredTriviaCommit(ctx)) {
5502
- sepPos = commitTriviaScan(scanTriviaCompact(input, cur, ctx));
5503
- } else {
5504
- sepPos = advanceTrivia(input, cur, ctx);
5505
- }
5506
- }
5507
- if (separatorLeads) LEAD.at = cur;
5508
- const sep = separator.parse(input, sepPos, ctx);
5509
- if (!sep.ok) {
5510
- rollbackTrivia(ctx, loopMark);
5511
- if (sep.committed) return sep;
5512
- break;
5513
- }
5514
- if (!keepSeparators) demoteCapturedToRaw(ctx, loopMark.leaves);
5515
- const sepMark = trailing === "forbid" ? void 0 : saveTriviaMark(ctx);
5516
- let nextPos = sep.span.end;
5517
- if (ctx.trivia) {
5518
- if (needsDeferredTriviaCommit(ctx)) {
5519
- nextPos = commitTriviaScan(scanTriviaCompact(input, sep.span.end, ctx));
5520
- } else {
5521
- nextPos = advanceTrivia(input, sep.span.end, ctx);
5522
- }
5523
- }
5524
- const next = combinator.parse(input, nextPos, ctx);
5525
- if (!next.ok) {
5526
- if (next.committed) return next;
5527
- const term = ctx._tolerant ? ctx._sync : void 0;
5528
- if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
5529
- expected ??= deriveExpected(combinator);
5530
- const rec = recoverScan(input, nextPos, ctx, orSentinel(separator, term), expected);
5531
- values.push(rec.error);
5532
- captureError(ctx, rec.error);
5533
- cur = rec.end;
5534
- continue;
5535
- }
5536
- if (sepMark !== void 0) {
5537
- rollbackTrivia(ctx, sepMark);
5538
- cur = sep.span.end;
5539
- break;
5540
- }
5541
- rollbackTrivia(ctx, loopMark);
5542
- break;
5543
- }
5544
- values.push(next.value);
5545
- cur = next.span.end;
5546
- }
5547
- if (values.length < min) return failAt2(cur);
5548
- return { ok: true, value: values, span: { start: pos, end: cur } };
5426
+ const cls = cc[ci];
5427
+ armCls.push(cls);
5428
+ for (let c = 0; c < 128; c++) {
5429
+ if (cls.ascii[c] !== 1) continue;
5430
+ if (ascii[c] === 0) ascii[c] = a + 1;
5431
+ else exclusive = false;
5549
5432
  }
5433
+ if (cls.hi.length > 0 && ++hiOwners > 1) exclusive = false;
5434
+ for (let i = 0; i < cls.hi.length; i += 2) hi.push(cls.hi[i], cls.hi[i + 1], a);
5435
+ }
5436
+ return { ascii, hi, open, armCls, exclusive };
5437
+ }
5438
+ function buildTrivia(spec) {
5439
+ if (spec.live !== void 0) return spec.live;
5440
+ if (spec.plain !== void 0) return trivia(regex(new RegExp(spec.plain[0], spec.plain[1])));
5441
+ if (spec.alts !== void 0) {
5442
+ const [head, ...rest] = spec.alts.map(([source, flags]) => regex(new RegExp(source, flags)));
5443
+ if (head === void 0) throw new TypeError("trivia spec has an EMPTY alternation, which matches nothing");
5444
+ const body = rest.length === 0 ? head : choice(head, ...rest);
5445
+ return trivia(many(body, { min: spec.min ?? 1 }));
5446
+ }
5447
+ const arms = {};
5448
+ for (const [name, source, flags] of spec.arms) arms[name] = regex(new RegExp(source, flags));
5449
+ return classifiedTrivia(arms);
5450
+ }
5451
+ function resolveTable(prog) {
5452
+ const owner = prog[TABLE_RUNTIME];
5453
+ const hit = owner?.resolved;
5454
+ if (hit !== void 0) return hit;
5455
+ const cc = prog.cc.map(resolveClass);
5456
+ const triviaBuilt = (prog.triviaSpecs ?? []).map(buildTrivia);
5457
+ const lex = (prog.lex ?? []).map((spec) => buildLexBodyRecognizer(spec, prog.k));
5458
+ const built = {
5459
+ prog,
5460
+ code: Int32Array.from(prog.code),
5461
+ k: prog.k,
5462
+ fns: prog.fns,
5463
+ cc,
5464
+ fx: prog.fx,
5465
+ disp: prog.disp.map((d) => resolveDispatch(d, cc)),
5466
+ trivia: triviaBuilt,
5467
+ triviaScan: triviaBuilt.map(fastTriviaScanner),
5468
+ triviaLabelled: triviaBuilt.map((t2) => t2._meta.triviaKindLabels !== void 0),
5469
+ dsp: prog.dsp.map((d) => ({
5470
+ byKey: new Map(d.key.map((x, i) => [x, d.keyArm[i]])),
5471
+ byFold: new Map(d.fold.map((x, i) => [x, d.foldArm[i]])),
5472
+ match: d.match,
5473
+ routed: d.routed,
5474
+ expected: d.expected
5475
+ })),
5476
+ lex,
5477
+ lexPrograms: (prog.lexPrograms ?? []).map((spec) => buildLexProgramRunner(spec, lex, cc, prog.fx)),
5478
+ rules: prog.rules
5550
5479
  };
5480
+ if (owner !== void 0) owner.resolved = built;
5481
+ return built;
5551
5482
  }
5552
5483
 
5553
- // src/combinators/map.ts
5554
- var JSON_STRING_BODY = String.raw`(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*`;
5555
- function transform(combinator, fn) {
5556
- const observesSpan = fn.length > 1;
5557
- const sequenceDef = combinator._def.tag === "sequence" ? combinator._def : void 0;
5558
- const terms = sequenceDef?.parsers;
5559
- const inner = terms?.[1];
5560
- const fused = terms?.length === 3 && terms[0]._def.tag === "literal" && terms[0]._def.value === '"' && !terms[0]._def.caseInsensitive && inner._def.tag === "regex" && inner._def.source === JSON_STRING_BODY && inner._def.flags === "" && terms[2]._def.tag === "literal" && terms[2]._def.value === '"' && !terms[2]._def.caseInsensitive && inner._stickyRegex !== void 0;
5561
- const child = scalarOf(combinator);
5562
- const parseScalar = fused ? (() => {
5563
- const body = inner._stickyRegex;
5564
- const tuple = ['"', "", '"'];
5565
- const expected = [JSON.stringify('"')];
5566
- return (input, pos, ctx) => {
5567
- if (ctx.trivia !== void 0) {
5568
- const end2 = child(input, pos, ctx);
5569
- if (end2 < 0) return end2;
5570
- ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end: end2 }) : fn(ctx._sv);
5571
- return end2;
5572
- }
5573
- if (input.charCodeAt(pos) !== 34) {
5574
- ctx._fx = expected;
5575
- return ~pos;
5576
- }
5577
- body.lastIndex = pos + 1;
5578
- const match = body.exec(input);
5579
- const close = pos + 1 + match[0].length;
5580
- if (input.charCodeAt(close) !== 34) {
5581
- ctx._fx = expected;
5582
- return ~close;
5583
- }
5584
- tuple[1] = match[0];
5585
- const end = close + 1;
5586
- ctx._sv = observesSpan ? fn(tuple, { start: pos, end }) : fn(tuple);
5587
- return end;
5588
- };
5589
- })() : /* @__PURE__ */ (() => {
5590
- return (input, pos, ctx) => {
5591
- const end = child(input, pos, ctx);
5592
- if (end < 0) return end;
5593
- ctx._sv = observesSpan ? fn(ctx._sv, { start: pos, end }) : fn(ctx._sv);
5594
- return end;
5595
- };
5596
- })();
5597
- return {
5598
- _tag: "transform",
5599
- _meta: combinator._meta,
5600
- _def: { tag: "transform", parser: combinator, fn },
5601
- _parseScalar: parseScalar,
5602
- parse(input, pos, ctx) {
5603
- const result = combinator.parse(input, pos, ctx);
5604
- if (!result.ok) return result;
5605
- return { ok: true, value: fn(result.value, result.span), span: result.span };
5484
+ // src/compiler/degradation.ts
5485
+ function formatDegradation(d) {
5486
+ return `[parseman] degraded [${d.code}] ${d.where}: ${d.subject} \u2014 ${d.fellBackTo}; otherwise ${d.otherwise}`;
5487
+ }
5488
+ var DETAIL_CAP = 8;
5489
+ function formatDegradations(list) {
5490
+ const byCode = /* @__PURE__ */ new Map();
5491
+ for (const d of list) {
5492
+ const bucket = byCode.get(d.code);
5493
+ if (bucket) bucket.push(d);
5494
+ else byCode.set(d.code, [d]);
5495
+ }
5496
+ const out = [];
5497
+ for (const [code, items] of byCode) {
5498
+ for (const d of items.slice(0, DETAIL_CAP)) out.push(formatDegradation(d));
5499
+ if (items.length > DETAIL_CAP) {
5500
+ out.push(
5501
+ `[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.`
5502
+ );
5503
+ }
5504
+ }
5505
+ return out;
5506
+ }
5507
+ var _sinks = [];
5508
+ function beginDegradationCapture() {
5509
+ _sinks.push(/* @__PURE__ */ new Map());
5510
+ }
5511
+ function degradationCaptureDepth() {
5512
+ return _sinks.length;
5513
+ }
5514
+ function unwindDegradationCapture(depth) {
5515
+ const found = [];
5516
+ while (_sinks.length > depth) {
5517
+ const sink = _sinks.pop();
5518
+ found.unshift(...sink.values());
5519
+ }
5520
+ return found;
5521
+ }
5522
+ function resolveDegradationLevel(explicit) {
5523
+ if (explicit !== void 0) return explicit;
5524
+ const env = typeof process !== "undefined" ? process.env?.PARSEMAN_DEGRADATION : void 0;
5525
+ if (env === "off" || env === "warn" || env === "error") return env;
5526
+ return "warn";
5527
+ }
5528
+ function degradationKey(d) {
5529
+ return JSON.stringify([d.code, d.where, d.subject]);
5530
+ }
5531
+ function recordDegradation(d) {
5532
+ const level = resolveDegradationLevel();
5533
+ if (level === "off") return;
5534
+ const key = degradationKey(d);
5535
+ const sink = _sinks[_sinks.length - 1];
5536
+ if (sink) {
5537
+ if (!sink.has(key)) sink.set(key, d);
5538
+ return;
5539
+ }
5540
+ if (_immediate.has(key)) return;
5541
+ _immediate.add(key);
5542
+ if (level === "error") throw new Error(`parseman: degraded compilation path
5543
+ ${formatDegradation(d)}`);
5544
+ console.warn(formatDegradation(d));
5545
+ }
5546
+ var _immediate = /* @__PURE__ */ new Set();
5547
+ function beginCompileDegradationDrain() {
5548
+ if (resolveDegradationLevel() === "off" || _sinks.length > 0) return () => {
5549
+ };
5550
+ const depth = degradationCaptureDepth();
5551
+ beginDegradationCapture();
5552
+ return (report) => {
5553
+ const found = unwindDegradationCapture(depth);
5554
+ if (!report || found.length === 0) return;
5555
+ const fresh = found.filter((d) => {
5556
+ const k = degradationKey(d);
5557
+ if (_immediate.has(k)) return false;
5558
+ _immediate.add(k);
5559
+ return true;
5560
+ });
5561
+ if (fresh.length === 0) return;
5562
+ const lines2 = formatDegradations(fresh);
5563
+ if (resolveDegradationLevel() === "error") {
5564
+ throw new Error(`parseman: ${fresh.length} degraded compilation path(s)
5565
+ ${lines2.join("\n")}`);
5606
5566
  }
5567
+ for (const l of lines2) console.warn(l);
5607
5568
  };
5608
5569
  }
5609
- function trivia(combinator) {
5610
- const labeled = analyzeLabeledTrivia(combinator);
5611
- const kindLabels = labeled?.labels;
5612
- return {
5613
- _tag: combinator._tag,
5614
- _meta: {
5615
- ...combinator._meta,
5616
- isTrivia: true,
5617
- triviaScanner: fastTriviaScanner(combinator),
5618
- // Every labeled scan of this trivia reads its spec; the module's four-slot
5619
- // memo alone thrashes on a grammar with more than four trivia tables.
5620
- labeledTriviaSpec: labeled,
5621
- ...kindLabels ? { triviaKindLabels: kindLabels } : {}
5622
- },
5623
- _def: { tag: "trivia", parser: combinator },
5624
- _parseScalar: scalarOf(combinator),
5625
- parse: combinator.parse.bind(combinator)
5626
- };
5570
+
5571
+ // src/compiler/build-arity.ts
5572
+ function cstOutputHost(build) {
5573
+ return build?._parsemanCstOutput === true;
5627
5574
  }
5628
- function classifiedTrivia(arms) {
5629
- const entries = Object.entries(arms);
5630
- if (entries.length === 0) {
5631
- throw new TypeError("classifiedTrivia() requires at least one named trivia arm.");
5575
+ var SINGLE_IDENT_ARROW_RE = /^[A-Za-z_$][\w$]*\s*=>/;
5576
+ var PARAM_LIST_OPEN_RE = /^(?:function\b[^(]*)?\(/;
5577
+ var NATIVE_CODE_RE = /\{\s*\[native code\]\s*\}/;
5578
+ var PARAM_HEAD_RE = /^[A-Za-z_$][\w$]*\s*\??\s*/;
5579
+ function confirmedBuildArity(src) {
5580
+ const s = src.trim();
5581
+ if (NATIVE_CODE_RE.test(s)) return null;
5582
+ if (/\barguments\b/.test(s)) return null;
5583
+ if (SINGLE_IDENT_ARROW_RE.test(s)) return 1;
5584
+ const inner = paramListText(s);
5585
+ if (inner === null) return null;
5586
+ if (inner.trim() === "") return 0;
5587
+ const parts = splitTopLevel(inner);
5588
+ if (parts === null) return null;
5589
+ for (const part of parts) {
5590
+ if (!isConfirmableParam(part.trim())) return null;
5632
5591
  }
5633
- for (let i = 0; i < entries.length; i++) {
5634
- const [name, arm] = entries[i];
5635
- const first = arm._meta.firstSet;
5636
- if (matchesEmpty(arm) || first.kind !== "ranges" || first.ranges.length === 0) {
5637
- throw new TypeError(
5638
- `classifiedTrivia(): ${JSON.stringify(name)} must be non-nullable with a concrete finite first set.`
5639
- );
5592
+ return parts.length;
5593
+ }
5594
+ function paramListText(s) {
5595
+ const open = PARAM_LIST_OPEN_RE.exec(s);
5596
+ if (!open) return null;
5597
+ const start = open[0].length;
5598
+ const end = matchingParen(s, start);
5599
+ if (end === null) return null;
5600
+ if (!s.startsWith("function") && !/^\s*=>/.test(s.slice(end + 1))) return null;
5601
+ return s.slice(start, end);
5602
+ }
5603
+ function matchingParen(s, start) {
5604
+ let depth = 0;
5605
+ for (let i = start; i < s.length; i++) {
5606
+ const c = s[i];
5607
+ if (c === '"' || c === "'" || c === "`") {
5608
+ const close = skipString(s, i);
5609
+ if (close === null) return null;
5610
+ i = close;
5611
+ continue;
5612
+ }
5613
+ if (c === "(" || c === "[" || c === "{") depth++;
5614
+ else if (c === "]" || c === "}") {
5615
+ if (--depth < 0) return null;
5616
+ } else if (c === ")") {
5617
+ if (depth === 0) return i;
5618
+ depth--;
5640
5619
  }
5641
5620
  }
5642
- const labeledArms = entries.map(([name, arm]) => label(name, arm));
5643
- const result = trivia(oneOrMore(choice(...labeledArms)));
5644
- result._meta.rootTriviaClassified = true;
5645
- return result;
5621
+ return null;
5646
5622
  }
5647
- function label(name, combinator) {
5648
- return {
5649
- _tag: combinator._tag,
5650
- _meta: combinator._meta,
5651
- _def: { tag: "label", label: name, parser: combinator },
5652
- parse(input, pos, ctx) {
5653
- const result = combinator.parse(input, pos, ctx);
5654
- if (!result.ok) return { ok: false, expected: [name], span: result.span };
5655
- return result;
5623
+ function skipString(s, open) {
5624
+ const quote = s[open];
5625
+ for (let i = open + 1; i < s.length; i++) {
5626
+ if (s[i] === "\\") {
5627
+ i++;
5628
+ continue;
5656
5629
  }
5657
- };
5630
+ if (s[i] === quote) return i;
5631
+ }
5632
+ return null;
5658
5633
  }
5659
- function field(name, combinator, opts) {
5660
- const leadingTrivia = opts?.leadingTrivia === true;
5661
- const trailingTrivia = opts?.trailingTrivia === true;
5662
- if (!leadingTrivia && !trailingTrivia) {
5663
- return {
5664
- _tag: combinator._tag,
5665
- _meta: combinator._meta,
5666
- _def: { tag: "field", name, parser: combinator },
5667
- parse(input, pos, ctx) {
5668
- const result = combinator.parse(input, pos, ctx);
5669
- if (!result.ok) return result;
5670
- ctx._fields?.push({ name, value: result.value, span: result.span });
5671
- return result;
5672
- }
5673
- };
5634
+ function splitTopLevel(inner) {
5635
+ const parts = [];
5636
+ let depth = 0;
5637
+ let angle = 0;
5638
+ let last = 0;
5639
+ for (let i = 0; i < inner.length; i++) {
5640
+ const c = inner[i];
5641
+ if (c === "=" && inner[i + 1] === ">") {
5642
+ i++;
5643
+ continue;
5644
+ }
5645
+ if (c === '"' || c === "'" || c === "`") {
5646
+ const close = skipString(inner, i);
5647
+ if (close === null) return null;
5648
+ i = close;
5649
+ continue;
5650
+ }
5651
+ if (c === "(" || c === "[" || c === "{") depth++;
5652
+ else if (c === ")" || c === "]" || c === "}") {
5653
+ if (--depth < 0) return null;
5654
+ } else if (c === "<") angle++;
5655
+ else if (c === ">") {
5656
+ if (--angle < 0) return null;
5657
+ } else if (c === "," && depth === 0 && angle === 0) {
5658
+ parts.push(inner.slice(last, i));
5659
+ last = i + 1;
5660
+ }
5674
5661
  }
5675
- return {
5676
- _tag: combinator._tag,
5677
- _meta: combinator._meta,
5678
- _def: {
5679
- tag: "field",
5680
- name,
5681
- parser: combinator,
5682
- ...leadingTrivia ? { leadingTrivia: true } : {},
5683
- ...trailingTrivia ? { trailingTrivia: true } : {}
5684
- },
5685
- parse(input, pos, ctx) {
5686
- const leadFrom = LEAD.at;
5687
- LEAD.at = -1;
5688
- const result = combinator.parse(input, pos, ctx);
5689
- if (!result.ok) return result;
5690
- const end = result.span.end;
5691
- const trailEnd = trailingTrivia && ctx.trivia ? consumeTrivia(input, end, ctx) : end;
5692
- ctx._fields?.push(triviaFieldCapture(name, result.value, input, result.span.start, end, leadingTrivia ? leadFrom : -1, trailEnd));
5693
- return trailEnd === end ? result : { ok: true, value: result.value, span: { start: result.span.start, end: trailEnd } };
5662
+ if (depth !== 0 || angle !== 0) return null;
5663
+ parts.push(inner.slice(last));
5664
+ return parts;
5665
+ }
5666
+ function isConfirmableParam(part) {
5667
+ const head = PARAM_HEAD_RE.exec(part);
5668
+ if (!head) return false;
5669
+ const rest = part.slice(head[0].length);
5670
+ if (rest === "") return true;
5671
+ if (rest[0] !== ":") return false;
5672
+ return !hasDefaultAssign(rest.slice(1));
5673
+ }
5674
+ function hasDefaultAssign(annotation) {
5675
+ for (let i = 0; i < annotation.length; i++) {
5676
+ if (annotation[i] !== "=") continue;
5677
+ if (annotation[i + 1] === ">" || annotation[i + 1] === "=") {
5678
+ i++;
5679
+ continue;
5694
5680
  }
5695
- };
5681
+ const prev = annotation[i - 1];
5682
+ if (prev === "<" || prev === ">" || prev === "!" || prev === "=") continue;
5683
+ return true;
5684
+ }
5685
+ return false;
5686
+ }
5687
+ function buildAnalysisSrc(def) {
5688
+ return def.buildSigSrc ?? def.buildSrc ?? def.build.toString();
5689
+ }
5690
+ var RESOLVER_REASONS = {
5691
+ "rest-parameter": "its parameter list uses a rest parameter, so the declared arity is unbounded",
5692
+ "arguments": "its body references `arguments`",
5693
+ "reassigned": "the binding it names is reassigned, so the function read here is not decidable",
5694
+ "not-a-function": "the binding it names is not a function declaration",
5695
+ "unresolved-import": "its module could not be resolved or parsed",
5696
+ "not-found": "the name did not resolve to any binding",
5697
+ "computed": "it is a computed expression",
5698
+ "foreign-source": "it is named in a module the resolver was never told about, so its offsets index no scope tree the resolver holds",
5699
+ "ambiguous-source": "two distinct registered modules hold byte-identical text, so that text no longer names the module its offsets index"
5700
+ };
5701
+ function unconfirmableReason(def, src) {
5702
+ const declared = def.buildArityUnresolved;
5703
+ if (declared !== void 0 && RESOLVER_REASONS[declared]) return RESOLVER_REASONS[declared];
5704
+ const s = src.trim();
5705
+ const params = s.slice(0, s.indexOf(")") + 1);
5706
+ if (params.includes("...")) return RESOLVER_REASONS["rest-parameter"];
5707
+ if (/\barguments\b/.test(s)) return RESOLVER_REASONS["arguments"];
5708
+ if (/^[A-Za-z_$][\w$]*(\s*\.\s*[A-Za-z_$][\w$]*)?$/.test(s)) {
5709
+ return "it names a binding this build could not resolve to a function declaration";
5710
+ }
5711
+ return "its parameter list could not be parsed";
5712
+ }
5713
+ function confirmedArityForDef(def) {
5714
+ if (def.buildArity !== void 0) return def.buildArity;
5715
+ const src = buildAnalysisSrc(def);
5716
+ const arity = confirmedBuildArity(src);
5717
+ if (arity !== null) return arity;
5718
+ const subject = `build reducer \`${(def.buildSrc ?? "<runtime fn>").trim().split("\n")[0].slice(0, 60)}\``;
5719
+ recordDegradation({
5720
+ code: "build-arity-unconfirmed",
5721
+ severity: "warn",
5722
+ where: `node("${def.type ?? "<inferred>"}")`,
5723
+ subject,
5724
+ fellBackTo: `could not confirm its declared arity \u2014 ${unconfirmableReason(def, src)} \u2014 so this node captures children, fields and raw children, logs trivia, and clones \`_ctx.state\` on every match`,
5725
+ 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"
5726
+ });
5727
+ return null;
5728
+ }
5729
+ function buildReadsRaw(def) {
5730
+ if (!def.build) return true;
5731
+ if (def.buildRawUnused === true) return false;
5732
+ const arity = confirmedArityForDef(def);
5733
+ if (arity === null) return true;
5734
+ return arity >= 4;
5696
5735
  }
5697
-
5698
- // src/combinators/regex.ts
5699
- var SCAN_BAIL_AT = 64;
5700
- function inRanges3(cp, ranges) {
5701
- for (let i = 0; i < ranges.length; i++) {
5702
- const [lo, hi] = ranges[i];
5703
- if (cp >= lo && cp <= hi) return true;
5704
- }
5705
- return false;
5736
+ function buildReadsTrivia(def) {
5737
+ if (!def.build) return true;
5738
+ const arity = confirmedArityForDef(def);
5739
+ if (arity === null) return true;
5740
+ return arity >= 5;
5706
5741
  }
5707
- function readClassRanges(body) {
5708
- if (body.startsWith("^")) return null;
5709
- return parseClassRanges(body);
5742
+ function buildReadsState(def) {
5743
+ if (!def.build) return true;
5744
+ const arity = confirmedArityForDef(def);
5745
+ if (arity === null) return true;
5746
+ return arity >= 6;
5710
5747
  }
5711
- function shortScanner(source, flags) {
5712
- if (/[imsuvy]/.test(flags)) return null;
5713
- let ranges = null;
5714
- let quant = "";
5715
- if (source[0] === "[") {
5716
- let end = 1;
5717
- while (end < source.length && source[end] !== "]") {
5718
- if (source[end] === "\\") end += 2;
5719
- else end++;
5748
+
5749
+ // src/compiler/fields.ts
5750
+ function buildReadsFields(def) {
5751
+ if (!def.build) return true;
5752
+ const arity = confirmedArityForDef(def);
5753
+ if (arity === null) return true;
5754
+ return arity >= 2;
5755
+ }
5756
+ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
5757
+ if (seen.has(p)) return false;
5758
+ seen.add(p);
5759
+ const d = p._def;
5760
+ switch (d.tag) {
5761
+ case "field":
5762
+ return true;
5763
+ case "node":
5764
+ return false;
5765
+ case "lazy": {
5766
+ try {
5767
+ return parserHasOwnFields(d.thunk(), seen);
5768
+ } catch {
5769
+ return false;
5770
+ }
5720
5771
  }
5721
- if (source[end] !== "]") return null;
5722
- ranges = readClassRanges(source.slice(1, end));
5723
- quant = source.slice(end + 1);
5724
- } else if (source[0] === "\\" && (source[1] === "d" || source[1] === "w" || source[1] === "s")) {
5725
- ranges = shorthandRanges(source[1]);
5726
- quant = source.slice(2);
5772
+ case "sequence":
5773
+ case "choice":
5774
+ return d.parsers.some((x) => parserHasOwnFields(x, seen));
5775
+ case "dispatch":
5776
+ return parserHasOwnFields(d.selector, seen) || d.cases.some((x) => parserHasOwnFields(x.parser, seen)) || (d.matchers ? d.matchers.some((entry) => parserHasOwnFields(entry.parser, seen)) : false) || (d.otherwise ? parserHasOwnFields(d.otherwise, seen) : false);
5777
+ case "sepBy":
5778
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.separator, seen);
5779
+ case "grammar":
5780
+ return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
5781
+ case "scanTo":
5782
+ return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen)) || d.recoverAt !== void 0 && parserHasOwnFields(d.recoverAt, seen) || d.stopAt !== void 0 && parserHasOwnFields(d.stopAt, seen);
5783
+ case "recover":
5784
+ return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
5785
+ case "lines":
5786
+ return parserHasOwnFields(d.body, seen);
5787
+ case "routed":
5788
+ return d.fallback ? parserHasOwnFields(d.fallback, seen) : false;
5789
+ case "many":
5790
+ case "oneOrMore":
5791
+ case "optional":
5792
+ case "attempt":
5793
+ case "transform":
5794
+ case "trivia":
5795
+ case "token":
5796
+ case "leaf":
5797
+ case "label":
5798
+ case "expect":
5799
+ case "withCtx":
5800
+ case "not":
5801
+ case "peek":
5802
+ return parserHasOwnFields(d.parser, seen);
5803
+ default:
5804
+ return false;
5727
5805
  }
5728
- if (!ranges || quant !== "+" && quant !== "*") return null;
5729
- const minOne = quant === "+";
5730
- return (input, pos) => {
5731
- let end = pos;
5732
- while (end < input.length && inRanges3(input.charCodeAt(end), ranges)) {
5733
- end++;
5734
- if (end - pos >= SCAN_BAIL_AT) return void 0;
5735
- }
5736
- return minOne && end === pos ? null : end;
5737
- };
5738
5806
  }
5739
- function asciiCaseFold(fs) {
5740
- if (fs.kind !== "ranges") return fs;
5741
- const ranges = fs.ranges.map((r) => ({ lo: r.lo, hi: r.hi }));
5742
- const add = (lo, hi) => {
5743
- if (lo <= hi) ranges.push({ lo, hi });
5744
- };
5745
- for (const r of fs.ranges) {
5746
- const uLo = Math.max(r.lo, 65), uHi = Math.min(r.hi, 90);
5747
- add(uLo + 32, uHi + 32);
5748
- const lLo = Math.max(r.lo, 97), lHi = Math.min(r.hi, 122);
5749
- add(lLo - 32, lHi - 32);
5750
- }
5751
- ranges.sort((a, b) => a.lo - b.lo);
5752
- const merged = [];
5753
- for (const r of ranges) {
5754
- const last = merged[merged.length - 1];
5755
- if (last && r.lo <= last.hi + 1) last.hi = Math.max(last.hi, r.hi);
5756
- else merged.push({ lo: r.lo, hi: r.hi });
5807
+ function buildFieldMap(captures) {
5808
+ if (!captures?.length) return void 0;
5809
+ const out = {};
5810
+ for (const cap of captures) {
5811
+ const entry = cap.leading === void 0 ? { value: cap.value, span: cap.span } : { value: cap.value, span: cap.span, leading: cap.leading, trailing: cap.trailing };
5812
+ const current = out[cap.name];
5813
+ if (current === void 0) out[cap.name] = entry;
5814
+ else if (Array.isArray(current)) current.push(entry);
5815
+ else out[cap.name] = [current, entry];
5757
5816
  }
5758
- return { kind: "ranges", ranges: merged };
5759
- }
5760
- function regex(pattern, flags = "") {
5761
- const source = typeof pattern === "string" ? pattern : pattern.source;
5762
- const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
5763
- const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
5764
- const scan = shortScanner(source, resolvedFlags);
5765
- const raw = firstSetFromRegex(source);
5766
- const firstSet2 = !resolvedFlags.includes("i") ? raw.firstSet : resolvedFlags.includes("u") || resolvedFlags.includes("v") ? any() : asciiCaseFold(raw.firstSet);
5767
- const canMatchNewline = raw.canMatchNewline;
5768
- const meta = { firstSet: firstSet2, canMatchNewline, isTrivia: false };
5769
- const def = { tag: "regex", source, flags: resolvedFlags };
5770
- const expected = directTerminalFailureExpected(def);
5771
- const parseScalar = (input, pos, ctx) => {
5772
- const scanEnd = scan?.(input, pos);
5773
- if (scanEnd !== void 0) {
5774
- if (scanEnd === null) {
5775
- ctx._fx = expected;
5776
- return ~pos;
5777
- }
5778
- ctx._sv = input.slice(pos, scanEnd);
5779
- return scanEnd;
5780
- }
5781
- anchored.lastIndex = pos;
5782
- const match = anchored.exec(input);
5783
- if (match === null) {
5784
- ctx._fx = expected;
5785
- return ~pos;
5786
- }
5787
- ctx._sv = match[0];
5788
- return pos + match[0].length;
5789
- };
5790
- return {
5791
- _tag: "regex",
5792
- _meta: meta,
5793
- _def: def,
5794
- _stickyRegex: anchored,
5795
- _parseScalar: parseScalar,
5796
- parse(input, pos, ctx) {
5797
- const scanEnd = scan?.(input, pos);
5798
- if (scanEnd !== void 0) {
5799
- if (scanEnd === null) return failAt(ctx, directTerminalFailureExpected(def), pos);
5800
- const value = input.slice(pos, scanEnd);
5801
- const span2 = { start: pos, end: scanEnd };
5802
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd);
5803
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
5804
- return { ok: true, value, span: span2 };
5805
- }
5806
- anchored.lastIndex = pos;
5807
- const m = anchored.exec(input);
5808
- if (m === null) {
5809
- return failAt(ctx, directTerminalFailureExpected(def), pos);
5810
- }
5811
- const span = { start: pos, end: pos + m[0].length };
5812
- if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end);
5813
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
5814
- return { ok: true, value: m[0], span };
5815
- }
5816
- };
5817
+ return out;
5817
5818
  }
5818
5819
 
5819
- // src/table/lex-body.ts
5820
- function buildLexBodyRecognizer(spec, constants) {
5821
- const raw = constants[spec[0]];
5822
- if (!(raw instanceof RegExp)) {
5823
- throw new TypeError("table lexical body does not reference a RegExp constant");
5824
- }
5825
- if (!Number.isInteger(spec[1]) || spec[1] < -1 || spec[1] > 65535) {
5826
- throw new TypeError("table lexical body suffix is not absent or one UTF-16 code unit");
5827
- }
5828
- const re = raw.sticky ? new RegExp(raw.source, raw.flags) : new RegExp(raw.source, `${raw.flags.replace(/[gy]/g, "")}y`);
5829
- const suffix = spec[1];
5830
- return (input, pos) => {
5831
- re.lastIndex = pos;
5832
- if (!re.test(input)) return -1;
5833
- const end = re.lastIndex;
5834
- if (suffix < 0) return end * 2;
5835
- return input.charCodeAt(end) === suffix ? (end + 1) * 2 + 1 : end * 2;
5836
- };
5820
+ // src/table/ops.ts
5821
+ var OP_LIT = 1;
5822
+ var OP_RX = 2;
5823
+ var OP_SEQ = 3;
5824
+ var OP_SEQV = 4;
5825
+ var OP_CHOICE = 5;
5826
+ var OP_REP = 6;
5827
+ var OP_REPV = 7;
5828
+ var OP_OPT = 8;
5829
+ var OP_XFORM = 9;
5830
+ var OP_NODE = 10;
5831
+ var OP_RULE = 11;
5832
+ var OP_GATE = 12;
5833
+ var OP_NOT = 13;
5834
+ var OP_PEEK = 14;
5835
+ var OP_LEAF = 15;
5836
+ var OP_EMPTY = 16;
5837
+ var OP_LIT_TRACK = 17;
5838
+ var OP_RX_TRACK = 18;
5839
+ var OP_NODE_TRACK = 19;
5840
+ var OP_SCOPE = 20;
5841
+ var OP_EXPECT = 21;
5842
+ var OP_SEQX = 22;
5843
+ var OP_SCAN = 23;
5844
+ var OP_FIELD = 24;
5845
+ var OP_LIT_CI = 27;
5846
+ var OP_LIT_CI_TRACK = 28;
5847
+ var OP_TOKEN = 29;
5848
+ var OP_SCOPE_CAP = 30;
5849
+ var OP_WITHCTX = 31;
5850
+ var OP_GUARD = 32;
5851
+ var OP_ADJ = 33;
5852
+ var OP_GREEDY = 34;
5853
+ var OP_REJECT = 35;
5854
+ var OP_ARMGATE = 36;
5855
+ var OP_LIVE = 37;
5856
+ var OP_ATTEMPT = 38;
5857
+ var OP_LABEL = 39;
5858
+ var OP_COV = 40;
5859
+ var OP_SCOPE_PLAIN = 41;
5860
+ var OP_LEX_BODY = 42;
5861
+ var OP_LEX_PROGRAM = 43;
5862
+ var OP_SOURCE_LEAF = 44;
5863
+ var OP_DISPATCH = 25;
5864
+ var OP_ROUTED = 26;
5865
+ var OP_NAMES = {
5866
+ [OP_LIT]: "LIT",
5867
+ [OP_RX]: "RX",
5868
+ [OP_SEQ]: "SEQ",
5869
+ [OP_SEQV]: "SEQV",
5870
+ [OP_CHOICE]: "CHOICE",
5871
+ [OP_REP]: "REP",
5872
+ [OP_REPV]: "REPV",
5873
+ [OP_OPT]: "OPT",
5874
+ [OP_XFORM]: "XFORM",
5875
+ [OP_NODE]: "NODE",
5876
+ [OP_RULE]: "RULE",
5877
+ [OP_GATE]: "GATE",
5878
+ [OP_NOT]: "NOT",
5879
+ [OP_PEEK]: "PEEK",
5880
+ [OP_LEAF]: "LEAF",
5881
+ [OP_SOURCE_LEAF]: "SOURCE_LEAF",
5882
+ [OP_EMPTY]: "EMPTY",
5883
+ [OP_LIT_TRACK]: "LIT_TRACK",
5884
+ [OP_RX_TRACK]: "RX_TRACK",
5885
+ [OP_NODE_TRACK]: "NODE_TRACK",
5886
+ [OP_SCOPE]: "SCOPE",
5887
+ [OP_EXPECT]: "EXPECT",
5888
+ [OP_SEQX]: "SEQX",
5889
+ [OP_SCAN]: "SCAN",
5890
+ [OP_FIELD]: "FIELD",
5891
+ [OP_LIT_CI]: "LIT_CI",
5892
+ [OP_LIT_CI_TRACK]: "LIT_CI_TRACK",
5893
+ [OP_DISPATCH]: "DISPATCH",
5894
+ [OP_ROUTED]: "ROUTED",
5895
+ [OP_TOKEN]: "TOKEN",
5896
+ [OP_SCOPE_CAP]: "SCOPE_CAP",
5897
+ [OP_WITHCTX]: "WITHCTX",
5898
+ [OP_GUARD]: "GUARD",
5899
+ [OP_ADJ]: "ADJ",
5900
+ [OP_GREEDY]: "GREEDY",
5901
+ [OP_REJECT]: "REJECT",
5902
+ [OP_ARMGATE]: "ARMGATE",
5903
+ [OP_LIVE]: "LIVE",
5904
+ [OP_ATTEMPT]: "ATTEMPT",
5905
+ [OP_LABEL]: "LABEL",
5906
+ [OP_COV]: "COV",
5907
+ [OP_SCOPE_PLAIN]: "SCOPE_PLAIN",
5908
+ [OP_LEX_BODY]: "LEX_BODY",
5909
+ [OP_LEX_PROGRAM]: "LEX_PROGRAM"
5910
+ };
5911
+
5912
+ // src/combinators/adjacency.ts
5913
+ function adjacencyOf(p) {
5914
+ return p._def.tag === "adjacency" ? p._def : null;
5837
5915
  }
5838
-
5839
- // src/table/program.ts
5840
- function closureArtifact(prog) {
5841
- return { ...prog, asm: [] };
5916
+ function adjacencyExpected(def) {
5917
+ if (def.polarity === "adjacent") return "adjacent";
5918
+ return def.kinds === void 0 ? "notAdjacent" : `notAdjacent(${def.kinds.join("|")})`;
5842
5919
  }
5843
- function covKindCode(kind) {
5844
- switch (kind) {
5845
- case "rule":
5846
- return 0;
5847
- case "choice-arm":
5848
- return 1;
5849
- case "dispatch-arm":
5850
- return 2;
5851
- case "label":
5852
- return 3;
5853
- }
5920
+ function holdsAdjacency(input, cur, ctx, def) {
5921
+ return adjacencyHolds(input, cur, ctx, def.polarity === "notAdjacent", def.kinds);
5854
5922
  }
5855
- function covKindName(code) {
5856
- switch (code) {
5857
- case 0:
5858
- return "rule";
5859
- case 1:
5860
- return "choice-arm";
5861
- case 2:
5862
- return "dispatch-arm";
5863
- case 3:
5864
- return "label";
5865
- }
5923
+ function adjacencyHolds(input, cur, ctx, negated, kinds) {
5924
+ const end = probeTriviaEnd(input, cur, ctx);
5925
+ if (!negated) return end === cur;
5926
+ if (end === cur) return false;
5927
+ if (kinds === void 0) return true;
5928
+ const want = resolveAdjacencyKindMask(ctx.trivia, kinds);
5929
+ const spec = analyzeLabeledTrivia(ctx.trivia);
5930
+ return (triviaKindMaskAt(input, cur, spec, ctx.state) & want) !== 0;
5866
5931
  }
5867
- function covDefinitions(prog) {
5868
- return (prog.cov ?? []).map(([id, kind]) => ({ id, kind: covKindName(kind) }));
5932
+ function adjacencyFail(pos, def) {
5933
+ return { ok: false, expected: [adjacencyExpected(def)], span: { start: pos, end: pos } };
5869
5934
  }
5870
- function expandCompact(p) {
5871
- if ("code" in p) return p;
5872
- return ownTableProgram({
5873
- code: p.c,
5874
- k: p.k,
5875
- cc: p.x,
5876
- fx: p.e,
5877
- disp: p.d,
5878
- rules: p.r,
5879
- fns: p.f,
5880
- lines: p.l ?? 0,
5881
- dsp: p.p ?? [],
5882
- ...p.lx === void 0 ? {} : { lex: p.lx },
5883
- ...p.lp === void 0 ? {} : { lexPrograms: p.lp },
5884
- ...p.lb === void 0 ? {} : { labels: p.lb },
5885
- ...p.rc === void 0 ? {} : { classified: p.rc },
5886
- ...p.h === void 0 ? {} : { hostMode: p.h },
5887
- ...p.tv === void 0 ? {} : { triviaSpecs: p.tv },
5888
- ...p.sc === void 0 ? {} : { scans: p.sc },
5889
- ...p.ss === void 0 ? {} : { scanSkip: p.ss },
5890
- ...p.so === void 0 ? {} : { scanSkipOf: p.so },
5891
- ...p.rv === void 0 ? {} : { rec: p.rv },
5892
- ...p.cv === void 0 ? {} : { cov: p.cv },
5893
- ...p.a === void 0 ? {} : { asm: p.a }
5894
- });
5935
+ 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.";
5936
+ function adjacencyMisuse(polarity) {
5937
+ return new TypeError(`${polarity}(): ${NO_STANDALONE_PARSE}`);
5895
5938
  }
5896
- function validateDispatchSpec(spec, arity, fallbackRouted) {
5897
- const invalid = (arms) => {
5898
- for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
5899
- return false;
5939
+ function adjacencyCombinator(def) {
5940
+ const meta = {
5941
+ // Zero-width and never leading (sequence() rejects index 0), so it contributes
5942
+ // no first char. `isZeroWidthAssertion` drops it from a sequence's first-set
5943
+ // rather than letting `any` poison the enclosing choice's dispatch.
5944
+ firstSet: { kind: "any" },
5945
+ canMatchNewline: false,
5946
+ isTrivia: false
5900
5947
  };
5901
- if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
5902
- throw new TypeError("table: malformed dispatch");
5903
- }
5904
- }
5905
- var TABLE_RUNTIME = /* @__PURE__ */ Symbol("parseman.tableRuntime");
5906
- function ownTableProgram(prog, resolved, caches = {}) {
5907
- const runtime = prog[TABLE_RUNTIME];
5908
5948
  return {
5909
- ...prog,
5910
- [TABLE_RUNTIME]: {
5911
- resolved,
5912
- choiceRollbackMasks: caches.choiceRollbackMasks ?? runtime?.choiceRollbackMasks,
5913
- nonCommittingChoiceSites: caches.nonCommittingChoiceSites ?? runtime?.nonCommittingChoiceSites,
5914
- failureRollbackCleanSites: caches.failureRollbackCleanSites ?? runtime?.failureRollbackCleanSites
5949
+ _tag: "adjacency",
5950
+ _meta: meta,
5951
+ _def: def,
5952
+ parse() {
5953
+ throw adjacencyMisuse(def.polarity);
5915
5954
  }
5916
5955
  };
5917
5956
  }
5918
- function choiceRollbackMask(prog, ip) {
5919
- return prog[TABLE_RUNTIME]?.choiceRollbackMasks?.get(ip);
5920
- }
5921
- function choiceCannotCommit(prog, ip) {
5922
- return prog[TABLE_RUNTIME]?.nonCommittingChoiceSites?.has(ip) === true;
5923
- }
5924
- function failureRollbackClean(prog, ip) {
5925
- return prog[TABLE_RUNTIME]?.failureRollbackCleanSites?.has(ip) === true;
5926
- }
5927
- var WIDE_CLASS_PREFIX_LO = 65535;
5928
- var WIDE_CLASS_PREFIX_HI = 0;
5929
- function validateClassRange(lo, hi) {
5930
- for (const cp of [lo, hi]) {
5931
- if (!Number.isInteger(cp) || cp < 0 || cp > 1114111) {
5932
- throw new TypeError(`table char-class endpoint is outside Unicode: ${cp}`);
5933
- }
5934
- }
5935
- if (lo > hi) {
5936
- throw new TypeError(`table char-class range is descending: ${lo} > ${hi}`);
5937
- }
5938
- }
5939
- function encodeClassSpec(ranges) {
5940
- let wide = false;
5941
- for (const range of ranges) {
5942
- validateClassRange(range.lo, range.hi);
5943
- if (range.hi > 65535) wide = true;
5944
- }
5945
- if (!wide) {
5946
- let spec2 = "";
5947
- for (const range of ranges) spec2 += String.fromCharCode(range.lo, range.hi);
5948
- return spec2;
5949
- }
5950
- let spec = String.fromCharCode(WIDE_CLASS_PREFIX_LO, WIDE_CLASS_PREFIX_HI);
5951
- for (const range of ranges) {
5952
- spec += String.fromCharCode(
5953
- range.lo >>> 16,
5954
- range.lo & 65535,
5955
- range.hi >>> 16,
5956
- range.hi & 65535
5957
- );
5958
- }
5959
- return spec;
5960
- }
5961
- function decodeClassSpec(spec) {
5962
- if (spec.charCodeAt(0) === WIDE_CLASS_PREFIX_LO && spec.charCodeAt(1) === WIDE_CLASS_PREFIX_HI) {
5963
- if ((spec.length - 2) % 4 !== 0) throw new TypeError("table char-class spec has a truncated wide range");
5964
- if (spec.length === 2) throw new TypeError("table char-class spec has a wide prefix but no ranges");
5965
- const ranges2 = [];
5966
- for (let i = 2; i < spec.length; i += 4) {
5967
- const lo = spec.charCodeAt(i) * 65536 + spec.charCodeAt(i + 1);
5968
- const hi = spec.charCodeAt(i + 2) * 65536 + spec.charCodeAt(i + 3);
5969
- validateClassRange(lo, hi);
5970
- ranges2.push({ lo, hi });
5971
- }
5972
- return ranges2;
5973
- }
5974
- if (spec.length % 2 !== 0) throw new TypeError("table char-class spec has an unmatched range endpoint");
5975
- const ranges = [];
5976
- for (let i = 0; i < spec.length; i += 2) {
5977
- const lo = spec.charCodeAt(i);
5978
- const hi = spec.charCodeAt(i + 1);
5979
- validateClassRange(lo, hi);
5980
- ranges.push({ lo, hi });
5981
- }
5982
- return ranges;
5983
- }
5984
- function resolveClass(spec) {
5985
- const ascii = new Uint8Array(128);
5986
- const hi = [];
5987
- for (const { lo, hi: up } of decodeClassSpec(spec)) {
5988
- if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1;
5989
- if (up >= 128) hi.push(Math.max(lo, 128), up);
5990
- }
5991
- return { ascii, hi };
5992
- }
5993
- function classHas2(cls, code) {
5994
- if (code < 0) return false;
5995
- if (code < 128) return cls.ascii[code] === 1;
5996
- const hi = cls.hi;
5997
- for (let i = 0; i < hi.length; i += 2) if (code >= hi[i] && code <= hi[i + 1]) return true;
5998
- return false;
5957
+ function adjacent() {
5958
+ return adjacencyCombinator({ tag: "adjacency", polarity: "adjacent" });
5999
5959
  }
6000
- function resolveDispatch(arms, cc) {
6001
- const ascii = new Uint8Array(128);
6002
- const hi = [];
6003
- const open = [];
6004
- const armCls = [];
6005
- let exclusive = true;
6006
- let hiOwners = 0;
6007
- for (let a = 0; a < arms.length; a++) {
6008
- const ci = arms[a];
6009
- if (ci < 0) {
6010
- open.push(a);
6011
- armCls.push(null);
6012
- exclusive = false;
6013
- continue;
5960
+ function notAdjacent(options) {
5961
+ const kinds = options?.kinds;
5962
+ if (kinds !== void 0) {
5963
+ if (!Array.isArray(kinds) || kinds.length === 0) {
5964
+ throw new TypeError("notAdjacent({ kinds }): kinds must be a non-empty array of trivia category names.");
6014
5965
  }
6015
- const cls = cc[ci];
6016
- armCls.push(cls);
6017
- for (let c = 0; c < 128; c++) {
6018
- if (cls.ascii[c] !== 1) continue;
6019
- if (ascii[c] === 0) ascii[c] = a + 1;
6020
- else exclusive = false;
5966
+ for (const k of kinds) {
5967
+ if (typeof k !== "string" || k.length === 0) {
5968
+ throw new TypeError(`notAdjacent({ kinds }): every kind must be a non-empty string, got ${JSON.stringify(k)}.`);
5969
+ }
6021
5970
  }
6022
- if (cls.hi.length > 0 && ++hiOwners > 1) exclusive = false;
6023
- for (let i = 0; i < cls.hi.length; i += 2) hi.push(cls.hi[i], cls.hi[i + 1], a);
6024
5971
  }
6025
- return { ascii, hi, open, armCls, exclusive };
5972
+ return adjacencyCombinator(
5973
+ kinds === void 0 ? { tag: "adjacency", polarity: "notAdjacent" } : { tag: "adjacency", polarity: "notAdjacent", kinds: [...kinds] }
5974
+ );
6026
5975
  }
6027
- function buildTrivia(spec) {
6028
- if (spec.live !== void 0) return spec.live;
6029
- if (spec.plain !== void 0) return trivia(regex(new RegExp(spec.plain[0], spec.plain[1])));
6030
- if (spec.alts !== void 0) {
6031
- const [head, ...rest] = spec.alts.map(([source, flags]) => regex(new RegExp(source, flags)));
6032
- if (head === void 0) throw new TypeError("trivia spec has an EMPTY alternation, which matches nothing");
6033
- const body = rest.length === 0 ? head : choice(head, ...rest);
6034
- return trivia(many(body, { min: spec.min ?? 1 }));
5976
+
5977
+ // src/combinators/node.ts
5978
+ function unwrapChild(child) {
5979
+ return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
5980
+ }
5981
+ function isCstChild(value) {
5982
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "parseError");
5983
+ }
5984
+ function missingInferredType() {
5985
+ throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
5986
+ }
5987
+ function normalizeBuildArity(arity) {
5988
+ if (arity === void 0) return void 0;
5989
+ if (!Number.isInteger(arity) || arity < 0 || arity > 6) {
5990
+ throw new Error("node() buildArity must be an integer 0..6 (children, fields, span, rawChildren, triviaLog, state)");
6035
5991
  }
6036
- const arms = {};
6037
- for (const [name, source, flags] of spec.arms) arms[name] = regex(new RegExp(source, flags));
6038
- return classifiedTrivia(arms);
5992
+ return arity;
6039
5993
  }
6040
- function resolveTable(prog) {
6041
- const owner = prog[TABLE_RUNTIME];
6042
- const hit = owner?.resolved;
6043
- if (hit !== void 0) return hit;
6044
- const cc = prog.cc.map(resolveClass);
6045
- const triviaBuilt = (prog.triviaSpecs ?? []).map(buildTrivia);
6046
- const lex = (prog.lex ?? []).map((spec) => buildLexBodyRecognizer(spec, prog.k));
6047
- const built = {
6048
- prog,
6049
- code: Int32Array.from(prog.code),
6050
- k: prog.k,
6051
- fns: prog.fns,
6052
- cc,
6053
- fx: prog.fx,
6054
- disp: prog.disp.map((d) => resolveDispatch(d, cc)),
6055
- trivia: triviaBuilt,
6056
- triviaScan: triviaBuilt.map(fastTriviaScanner),
6057
- triviaLabelled: triviaBuilt.map((t2) => t2._meta.triviaKindLabels !== void 0),
6058
- dsp: prog.dsp.map((d) => ({
6059
- byKey: new Map(d.key.map((x, i) => [x, d.keyArm[i]])),
6060
- byFold: new Map(d.fold.map((x, i) => [x, d.foldArm[i]])),
6061
- match: d.match,
6062
- routed: d.routed,
6063
- expected: d.expected
6064
- })),
6065
- lex,
6066
- lexPrograms: (prog.lexPrograms ?? []).map((spec) => buildLexProgramRunner(spec, lex, cc, prog.fx)),
6067
- rules: prog.rules
5994
+ function normalizeProject(project) {
5995
+ if (project === void 0) return void 0;
5996
+ if (!Number.isInteger(project) || project < 0) {
5997
+ throw new Error("node() project child must be a non-negative integer");
5998
+ }
5999
+ return project;
6000
+ }
6001
+ function projectChild(children2, project, type) {
6002
+ if (!(project in children2)) {
6003
+ throw new Error(`node(${JSON.stringify(type)}) project child ${project} was not captured`);
6004
+ }
6005
+ return unwrapChild(children2[project]);
6006
+ }
6007
+ function node(typeOrCombinator, combinatorOrBuild, buildOrOpts, maybeOpts) {
6008
+ const hasExplicitType = typeof typeOrCombinator === "string";
6009
+ const type = hasExplicitType ? typeOrCombinator : void 0;
6010
+ const combinator = hasExplicitType ? combinatorOrBuild : typeOrCombinator;
6011
+ const buildArg = hasExplicitType ? buildOrOpts : combinatorOrBuild;
6012
+ const build = typeof buildArg === "function" ? buildArg : void 0;
6013
+ const opts = typeof buildArg === "function" ? maybeOpts : buildArg ?? maybeOpts;
6014
+ const project = normalizeProject(opts?.project);
6015
+ if (project !== void 0 && build !== void 0) {
6016
+ throw new Error("node() options cannot combine project with a build callback");
6017
+ }
6018
+ const baseDef = { tag: "node", parser: combinator, ...type === void 0 ? {} : { type }, ...build === void 0 ? {} : { build } };
6019
+ const meta = {
6020
+ firstSet: combinator._meta.firstSet,
6021
+ canMatchNewline: combinator._meta.canMatchNewline,
6022
+ isTrivia: false
6068
6023
  };
6069
- if (owner !== void 0) owner.resolved = built;
6070
- return built;
6024
+ const unwrap = opts?.unwrap === true;
6025
+ const collapse = opts?.collapse === true;
6026
+ const captureTrivia = opts?.captureTrivia === true;
6027
+ const trailingTrivia = opts?.trailingTrivia === true;
6028
+ const tags = opts?.tags;
6029
+ const buildArity = normalizeBuildArity(opts?.buildArity);
6030
+ if (unwrap && collapse) {
6031
+ throw new Error("node() options cannot set both unwrap and collapse");
6032
+ }
6033
+ if (project !== void 0 && (unwrap || collapse)) {
6034
+ throw new Error("node() options cannot combine project with unwrap or collapse");
6035
+ }
6036
+ const def = unwrap || collapse || project !== void 0 || captureTrivia || trailingTrivia || buildArity !== void 0 || tags !== void 0 && tags.length > 0 ? { ...baseDef, ...unwrap ? { unwrap: true } : {}, ...collapse ? { collapse: true } : {}, ...project !== void 0 ? { project } : {}, ...captureTrivia ? { captureTrivia: true } : {}, ...trailingTrivia ? { trailingTrivia: true } : {}, ...buildArity !== void 0 ? { buildArity } : {}, ...tags !== void 0 && tags.length > 0 ? { tags } : {} } : baseDef;
6037
+ const capturesTrivia = captureTrivia || (build ? buildReadsTrivia(def) : project === void 0);
6038
+ const clonesState = build ? buildReadsState(def) : project === void 0;
6039
+ const hasOwnFields = parserHasOwnFields(combinator);
6040
+ const capturesFields = hasOwnFields && (build ? buildReadsFields(def) : project === void 0);
6041
+ const guardable = combinator._meta.firstSet.kind !== "any" && !matchesEmpty(combinator);
6042
+ let failExpected;
6043
+ return {
6044
+ _tag: "node",
6045
+ _meta: meta,
6046
+ _def: def,
6047
+ parse(input, pos, ctx) {
6048
+ if (guardable && ctx._probe === void 0 && !ctx._tolerant && !startsFirstSet(combinator, input, pos)) {
6049
+ if (failExpected === void 0) {
6050
+ const e = deriveExpected(combinator);
6051
+ failExpected = e.length > 0 ? e : [combinator._tag];
6052
+ }
6053
+ return { ok: false, expected: failExpected, span: { start: pos, end: pos } };
6054
+ }
6055
+ const hasDirectValue = build !== void 0 || project !== void 0;
6056
+ const hostCst = hasDirectValue && cstOutputHost(ctx.build);
6057
+ const structural = !hasDirectValue;
6058
+ const hostCapturesThisType = structural && def.type !== void 0 && ctx.build?._parsemanCaptureTrivia !== void 0 ? ctx.build._parsemanCaptureTrivia(def.type) : void 0;
6059
+ const effTrivia = structural ? captureTrivia || trailingTrivia || hostCapturesThisType !== false : capturesTrivia || hostCst;
6060
+ const effFields = capturesFields || hostCst && hasOwnFields;
6061
+ const saved = beginCstNodeCapture(ctx);
6062
+ const savedFields = ctx._fields;
6063
+ ctx._fields = effFields ? [] : void 0;
6064
+ const savedMask = ctx._triviaCaptureMask;
6065
+ if (build === void 0 && project === void 0 && ctx.build?._parsemanTriviaKinds !== void 0 && def.type !== void 0) {
6066
+ ctx._triviaCaptureMask = ctx.build._parsemanTriviaKinds(def.type);
6067
+ }
6068
+ if (!effTrivia) ctx.captureTrivia = false;
6069
+ let r = combinator.parse(input, pos, ctx);
6070
+ if (r.ok && trailingTrivia && ctx.trivia) {
6071
+ const end = consumeTrivia(input, r.span.end, ctx);
6072
+ r = { ...r, span: { start: r.span.start, end } };
6073
+ }
6074
+ const fields = effFields ? buildFieldMap(ctx._fields) : void 0;
6075
+ ctx._fields = savedFields;
6076
+ ctx._triviaCaptureMask = savedMask;
6077
+ const { children: children2, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
6078
+ if (!r.ok) return r;
6079
+ const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
6080
+ const nodeType = def.type ?? missingInferredType();
6081
+ const cstOutput = cstOutputHost(ctx.build);
6082
+ const span = ctx.trackLines ? annotateSpanFromLineContext(r.span, ctx) : r.span;
6083
+ const hostState = clonesState ? st : ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
6084
+ const built = unwrap && children2.length === 1 ? unwrapChild(children2[0]) : collapse && children2.length === 1 ? children2[0] : (cstOutput || !build && project === void 0) && ctx.build?._parsemanCstCollapse && children2.length === 1 && rawChildren.length === 1 && ctx.build._parsemanCstCollapse(nodeType, children2[0], children2, rawChildren) ? children2[0] : project !== void 0 ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : projectChild(children2, project, nodeType) : build ? cstOutput && ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, hostState, tags) : build(children2, fields, span, rawChildren, triviaLog, st) : ctx.build ? ctx.build(nodeType, children2, fields, span, rawChildren, triviaLog, st, tags) : { _tag: "node", type: nodeType, span: ctx.trackLines ? { ...span } : { start: r.span.start, end: r.span.end }, state: st ?? null, children: children2 };
6085
+ const rawEntry2 = isCstChild(built) ? built : { _tag: "leaf", value: typeof built === "string" ? built : typeof built === "object" && built !== null ? input.slice(r.span.start, r.span.end) : "", span };
6086
+ if (saved.buf !== void 0 || saved.ch !== void 0) {
6087
+ pushCstChild(ctx, built, rawEntry2);
6088
+ }
6089
+ return { ok: true, value: built, span };
6090
+ }
6091
+ };
6092
+ }
6093
+
6094
+ // src/combinators/trivia-boundary.ts
6095
+ var MAX_DEPTH = 12;
6096
+ function hasOwnTriviaBoundary(p, depth = MAX_DEPTH) {
6097
+ if (depth === 0) return true;
6098
+ const d = p._def;
6099
+ switch (d.tag) {
6100
+ // Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
6101
+ case "sequence":
6102
+ return d.parsers.length > 1;
6103
+ case "many":
6104
+ case "oneOrMore":
6105
+ case "sepBy":
6106
+ return true;
6107
+ // Transparent wrappers — they add no position, so ask what they wrap.
6108
+ case "node":
6109
+ case "transform":
6110
+ case "leaf":
6111
+ case "label":
6112
+ case "field":
6113
+ case "token":
6114
+ case "attempt":
6115
+ case "optional":
6116
+ case "expect":
6117
+ case "withCtx":
6118
+ case "recover":
6119
+ case "trivia":
6120
+ return hasOwnTriviaBoundary(d.parser, depth - 1);
6121
+ // An alternation adds no position either, but ONE arm that has one is enough
6122
+ // to make the scope meaningful for this rule.
6123
+ case "choice":
6124
+ return d.parsers.some((a) => hasOwnTriviaBoundary(a, depth - 1));
6125
+ default:
6126
+ return false;
6127
+ }
6071
6128
  }
6072
6129
 
6073
6130
  // src/compiler/direct-projection.ts
@@ -6618,6 +6675,7 @@ var Encoder = class {
6618
6675
  case "expect":
6619
6676
  case "recover":
6620
6677
  case "scanTo":
6678
+ case "lines":
6621
6679
  case "unknown":
6622
6680
  return true;
6623
6681
  case "lazy": {
@@ -6838,6 +6896,18 @@ var Encoder = class {
6838
6896
  open: bal.open,
6839
6897
  close: bal.close
6840
6898
  }));
6899
+ if (d.tag === "lines") {
6900
+ return this.emit(OP_SCAN, this.scanSlot({
6901
+ kind: 2,
6902
+ flags: 0,
6903
+ skip: [],
6904
+ prefix: this.subtree(d.prefix),
6905
+ body: this.subtree(d.body),
6906
+ ...d.first === void 0 ? {} : { first: this.subtree(d.first) },
6907
+ ...d.lazy === void 0 ? {} : { lazy: this.subtree(d.lazy) }
6908
+ }));
6909
+ }
6910
+ if (d.tag === "columns") return this.emit(OP_SCAN, this.scanSlot({ kind: 3, flags: 0, skip: [], n: d.n }));
6841
6911
  if (d.tag === "scanTo") {
6842
6912
  const sentDef = d.sentinel._def;
6843
6913
  return this.emit(OP_SCAN, this.scanSlot({
@@ -7284,12 +7354,7 @@ var Encoder = class {
7284
7354
  };
7285
7355
  const seen = /* @__PURE__ */ new Set();
7286
7356
  const refs = [];
7287
- for (const s of this.scans) {
7288
- if (s.sentinel !== void 0) refs.push(s.sentinel);
7289
- if (s.recoverAt !== void 0) refs.push(s.recoverAt);
7290
- if (s.stopAt !== void 0) refs.push(s.stopAt);
7291
- refs.push(...s.skip);
7292
- }
7357
+ for (const s of this.scans) refs.push(...scanRefs(s));
7293
7358
  for (const set of this.scanSkipSets) refs.push(...set);
7294
7359
  const stack = [...Object.values(this.rules), ...refs.map((r) => r[0])];
7295
7360
  while (stack.length > 0) {
@@ -7309,7 +7374,11 @@ var Encoder = class {
7309
7374
  skip: s.skip.map(res),
7310
7375
  ...s.sentinel === void 0 ? {} : { sentinel: res(s.sentinel) },
7311
7376
  ...s.recoverAt === void 0 ? {} : { recoverAt: res(s.recoverAt) },
7312
- ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) }
7377
+ ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) },
7378
+ ...s.prefix === void 0 ? {} : { prefix: res(s.prefix) },
7379
+ ...s.body === void 0 ? {} : { body: res(s.body) },
7380
+ ...s.first === void 0 ? {} : { first: res(s.first) },
7381
+ ...s.lazy === void 0 ? {} : { lazy: res(s.lazy) }
7313
7382
  }));
7314
7383
  this.scanSkipSets = this.scanSkipSets.map((set) => set.map(res));
7315
7384
  }
@@ -8151,7 +8220,7 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8151
8220
  if (shape.kind === "alt") {
8152
8221
  const okV = mint("_ok");
8153
8222
  const endV2 = mint("_end");
8154
- const lines = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
8223
+ const lines2 = [`${ind}let ${okV} = false`, `${ind}let ${endV2} = ${start}`];
8155
8224
  if (shape.arms.length >= 2 && shape.arms.every((a) => a.kind === "litFold" && a.open.length >= 1)) {
8156
8225
  const groups = /* @__PURE__ */ new Map();
8157
8226
  for (const a of shape.arms) {
@@ -8160,60 +8229,60 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8160
8229
  const lower = f >= 65 && f <= 90 ? f + 32 : f;
8161
8230
  (groups.get(lower) ?? groups.set(lower, []).get(lower)).push(open);
8162
8231
  }
8163
- lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8232
+ lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8164
8233
  for (const [lower, arms] of groups) {
8165
8234
  const labels = lower >= 97 && lower <= 122 ? `case ${lower}: case ${lower - 32}:` : `case ${lower}:`;
8166
- lines.push(`${ind} ${labels} {`);
8235
+ lines2.push(`${ind} ${labels} {`);
8167
8236
  for (const open of arms) {
8168
8237
  const tail = open.slice(1).map((cp, i) => foldEq(`input.charCodeAt(${start} + ${i + 1})`, cp));
8169
8238
  const cond = tail.length ? ` && ${tail.join(" && ")}` : "";
8170
- lines.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
8239
+ lines2.push(`${ind} if (${start} + ${open.length} <= input.length${cond}) { ${okV} = true; ${endV2} = ${start} + ${open.length}; break }`);
8171
8240
  }
8172
- lines.push(`${ind} break`);
8173
- lines.push(`${ind} }`);
8241
+ lines2.push(`${ind} break`);
8242
+ lines2.push(`${ind} }`);
8174
8243
  }
8175
- lines.push(`${ind}}`);
8176
- return { setup: lines, ok: okV, end: endV2 };
8244
+ lines2.push(`${ind}}`);
8245
+ return { setup: lines2, ok: okV, end: endV2 };
8177
8246
  }
8178
8247
  if (shape.disjoint) {
8179
8248
  const plan = planAltSwitch(shape.firsts);
8180
8249
  if (plan.kind === "switch") {
8181
- lines.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8250
+ lines2.push(`${ind}if (${start} < input.length) switch (${codeAt(start, firstChar)}) {`);
8182
8251
  shape.arms.forEach((arm, k) => {
8183
8252
  const labels = plan.cases[k].map((cp) => `case ${cp}:`).join(" ");
8184
8253
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8185
- lines.push(`${ind} ${labels} {`);
8186
- lines.push(...m.setup);
8187
- lines.push(`${ind} ${okV} = ${m.ok}`);
8188
- lines.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8189
- lines.push(`${ind} break`);
8190
- lines.push(`${ind} }`);
8254
+ lines2.push(`${ind} ${labels} {`);
8255
+ lines2.push(...m.setup);
8256
+ lines2.push(`${ind} ${okV} = ${m.ok}`);
8257
+ lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8258
+ lines2.push(`${ind} break`);
8259
+ lines2.push(`${ind} }`);
8191
8260
  });
8192
- lines.push(`${ind}}`);
8261
+ lines2.push(`${ind}}`);
8193
8262
  } else {
8194
8263
  const c0 = codeAt(start, firstChar);
8195
8264
  shape.arms.forEach((arm, k) => {
8196
8265
  const fs = shape.firsts[k];
8197
8266
  const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
8198
8267
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8199
- lines.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
8200
- lines.push(...m.setup);
8201
- lines.push(`${ind} ${okV} = ${m.ok}`);
8202
- lines.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8203
- lines.push(`${ind}}`);
8268
+ lines2.push(`${ind}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
8269
+ lines2.push(...m.setup);
8270
+ lines2.push(`${ind} ${okV} = ${m.ok}`);
8271
+ lines2.push(`${ind} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
8272
+ lines2.push(`${ind}}`);
8204
8273
  });
8205
8274
  }
8206
8275
  } else {
8207
8276
  const lbl = mint("_alt");
8208
- lines.push(`${ind}${lbl}: {`);
8277
+ lines2.push(`${ind}${lbl}: {`);
8209
8278
  for (const arm of shape.arms) {
8210
8279
  const m = emitShapeMatch(arm, start, mint, `${ind} `, firstChar);
8211
- lines.push(...m.setup);
8212
- lines.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
8280
+ lines2.push(...m.setup);
8281
+ lines2.push(`${ind} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
8213
8282
  }
8214
- lines.push(`${ind}}`);
8283
+ lines2.push(`${ind}}`);
8215
8284
  }
8216
- return { setup: lines, ok: okV, end: endV2 };
8285
+ return { setup: lines2, ok: okV, end: endV2 };
8217
8286
  }
8218
8287
  if (shape.kind === "chars") {
8219
8288
  const cur = mint("_e");
@@ -8248,64 +8317,64 @@ function emitShapeMatch(shape, start, mint, ind, firstChar) {
8248
8317
  const c = classCond(`input.charCodeAt(${at})`, p.ranges);
8249
8318
  return p.negated ? `!(${c})` : `(${c})`;
8250
8319
  };
8251
- const lines = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
8320
+ const lines2 = [`${ind}let ${endV2} = ${start}`, `${ind}let ${okV} = false`, `${ind}do {`];
8252
8321
  let first = true;
8253
8322
  for (const p of shape.parts) {
8254
8323
  if (p.part === "lit") {
8255
8324
  const L = p.cps.length;
8256
8325
  const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
8257
8326
  if (p.optional) {
8258
- lines.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
8327
+ lines2.push(`${ind} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
8259
8328
  } else {
8260
- lines.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
8261
- lines.push(`${ind} ${endV2} += ${L}`);
8329
+ lines2.push(`${ind} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
8330
+ lines2.push(`${ind} ${endV2} += ${L}`);
8262
8331
  }
8263
8332
  } else if (p.part === "run" && p.max === Infinity) {
8264
8333
  const s = mint("_s");
8265
- lines.push(`${ind} const ${s} = ${endV2}`);
8266
- lines.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8267
- if (p.min === 1) lines.push(`${ind} if (${endV2} === ${s}) break`);
8268
- else if (p.min > 1) lines.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
8334
+ lines2.push(`${ind} const ${s} = ${endV2}`);
8335
+ lines2.push(`${ind} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8336
+ if (p.min === 1) lines2.push(`${ind} if (${endV2} === ${s}) break`);
8337
+ else if (p.min > 1) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
8269
8338
  } else if (p.part === "run" && p.max === 1) {
8270
8339
  if (p.min === 1) {
8271
- lines.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
8272
- lines.push(`${ind} ${endV2}++`);
8340
+ lines2.push(`${ind} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
8341
+ lines2.push(`${ind} ${endV2}++`);
8273
8342
  } else {
8274
- lines.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8343
+ lines2.push(`${ind} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8275
8344
  }
8276
8345
  } else if (p.part === "run") {
8277
8346
  const s = mint("_s");
8278
- lines.push(`${ind} const ${s} = ${endV2}`);
8279
- lines.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8280
- if (p.min > 0) lines.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
8347
+ lines2.push(`${ind} const ${s} = ${endV2}`);
8348
+ lines2.push(`${ind} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
8349
+ if (p.min > 0) lines2.push(`${ind} if (${endV2} - ${s} < ${p.min}) break`);
8281
8350
  } else {
8282
8351
  const inner = emitShapeMatch(p.inner, endV2, mint, `${ind} `, first ? firstChar : void 0);
8283
8352
  if (!p.unbounded) {
8284
- lines.push(...inner.setup);
8353
+ lines2.push(...inner.setup);
8285
8354
  if (p.min === 1) {
8286
- lines.push(`${ind} if (!(${inner.ok})) break`);
8287
- lines.push(`${ind} ${endV2} = ${inner.end}`);
8355
+ lines2.push(`${ind} if (!(${inner.ok})) break`);
8356
+ lines2.push(`${ind} ${endV2} = ${inner.end}`);
8288
8357
  } else {
8289
- lines.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
8358
+ lines2.push(`${ind} if (${inner.ok}) ${endV2} = ${inner.end}`);
8290
8359
  }
8291
8360
  } else {
8292
8361
  const progV = mint("_prog");
8293
- lines.push(`${ind} let ${progV} = false`);
8294
- lines.push(`${ind} while (true) {`);
8295
- lines.push(...inner.setup);
8296
- lines.push(`${ind} if (!(${inner.ok})) break`);
8297
- lines.push(`${ind} ${endV2} = ${inner.end}`);
8298
- lines.push(`${ind} ${progV} = true`);
8299
- lines.push(`${ind} }`);
8300
- if (p.min === 1) lines.push(`${ind} if (!${progV}) break`);
8362
+ lines2.push(`${ind} let ${progV} = false`);
8363
+ lines2.push(`${ind} while (true) {`);
8364
+ lines2.push(...inner.setup);
8365
+ lines2.push(`${ind} if (!(${inner.ok})) break`);
8366
+ lines2.push(`${ind} ${endV2} = ${inner.end}`);
8367
+ lines2.push(`${ind} ${progV} = true`);
8368
+ lines2.push(`${ind} }`);
8369
+ if (p.min === 1) lines2.push(`${ind} if (!${progV}) break`);
8301
8370
  }
8302
8371
  }
8303
8372
  first = false;
8304
8373
  }
8305
- lines.push(`${ind} ${okV} = true`);
8306
- lines.push(`${ind}} while (false)`);
8307
- lines.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
8308
- return { setup: lines, ok: okV, end: endV2 };
8374
+ lines2.push(`${ind} ${okV} = true`);
8375
+ lines2.push(`${ind}} while (false)`);
8376
+ lines2.push(`${ind}if (!${okV}) ${endV2} = ${start}`);
8377
+ return { setup: lines2, ok: okV, end: endV2 };
8309
8378
  }
8310
8379
  if (shape.kind === "litFold") {
8311
8380
  const openLen2 = shape.open.length;
@@ -10582,6 +10651,188 @@ finish:_finish
10582
10651
  };
10583
10652
  }
10584
10653
 
10654
+ // src/combinators/lines.ts
10655
+ function lineIndexAt(view, offset) {
10656
+ const lines2 = view.lines;
10657
+ let lo = 0, hi = lines2.length - 1;
10658
+ while (lo < hi) {
10659
+ const mid = lo + hi + 1 >> 1;
10660
+ if (lines2[mid].offset <= offset) lo = mid;
10661
+ else hi = mid - 1;
10662
+ }
10663
+ return lo;
10664
+ }
10665
+ var lineAt = (view, offset) => view.lines[lineIndexAt(view, offset)];
10666
+ function sourceOffset(view, offset) {
10667
+ const line = lineAt(view, offset);
10668
+ return line === void 0 ? offset : line.source + (offset - line.offset);
10669
+ }
10670
+ function columnAt(input, pos, ctx) {
10671
+ const view = ctx.lineView;
10672
+ let start, column, split;
10673
+ const line = view === void 0 ? void 0 : lineAt(view, pos);
10674
+ if (line !== void 0 && pos >= line.offset) {
10675
+ start = line.offset;
10676
+ column = line.column;
10677
+ split = line.split;
10678
+ } else {
10679
+ start = input.lastIndexOf("\n", pos - 1) + 1;
10680
+ column = 0;
10681
+ split = 0;
10682
+ }
10683
+ for (let i = start; i < pos; i++) {
10684
+ if (input.charCodeAt(i) === 9) {
10685
+ const width = 4 - (column - (i === start ? split : 0)) % 4 - (i === start ? split : 0);
10686
+ column += width;
10687
+ } else column++;
10688
+ split = 0;
10689
+ }
10690
+ return { column, split: pos === start ? split : 0 };
10691
+ }
10692
+ function columns(n) {
10693
+ if (!Number.isInteger(n) || n < 0) throw new TypeError(`columns(n) needs a non-negative integer, got ${n}`);
10694
+ const expected = [`columns(${n})`];
10695
+ const meta = {
10696
+ firstSet: n === 0 ? any() : { kind: "ranges", ranges: [{ lo: 9, hi: 9 }, { lo: 32, hi: 32 }] },
10697
+ canMatchNewline: false,
10698
+ isTrivia: false
10699
+ };
10700
+ return {
10701
+ _tag: "columns",
10702
+ _meta: meta,
10703
+ _def: { tag: "columns", n },
10704
+ parse(input, pos, ctx) {
10705
+ const c = ctx;
10706
+ let { column, split } = columnAt(input, pos, c);
10707
+ const goal = column + n;
10708
+ let at = pos;
10709
+ while (column < goal) {
10710
+ const code = input.charCodeAt(at);
10711
+ if (code === 32) {
10712
+ column++;
10713
+ split = 0;
10714
+ at++;
10715
+ continue;
10716
+ }
10717
+ if (code !== 9) return { ok: false, expected, span: { start: at, end: at } };
10718
+ const width = 4 - (column - split) % 4 - split;
10719
+ if (column + width > goal) {
10720
+ c._columnsSplit = { at, split: split + (goal - column) };
10721
+ return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
10722
+ }
10723
+ column += width;
10724
+ split = 0;
10725
+ at++;
10726
+ }
10727
+ return { ok: true, value: input.slice(pos, at), span: { start: pos, end: at } };
10728
+ }
10729
+ };
10730
+ }
10731
+ function lines(prefix, body, options = {}) {
10732
+ const { first, lazy } = options;
10733
+ const opening = first ?? prefix;
10734
+ const meta = {
10735
+ // An opening that matches empty starts a run at any character.
10736
+ firstSet: matchesEmpty(opening) ? any() : opening._meta.firstSet,
10737
+ canMatchNewline: true,
10738
+ isTrivia: false
10739
+ };
10740
+ return {
10741
+ _tag: "lines",
10742
+ _meta: meta,
10743
+ _def: { tag: "lines", prefix, body, ...first === void 0 ? {} : { first }, ...lazy === void 0 ? {} : { lazy } },
10744
+ parse(input, pos, ctx) {
10745
+ const c = ctx;
10746
+ const probe = createDetachedParseContext(false, ctx.state);
10747
+ probe.lineView = c.lineView;
10748
+ const run = [];
10749
+ const parts = [];
10750
+ let offset = 0;
10751
+ for (let start = pos; start < input.length || start === pos; ) {
10752
+ let end2 = input.indexOf("\n", start);
10753
+ if (end2 < 0) end2 = input.length;
10754
+ probe._columnsSplit = void 0;
10755
+ const head = (run.length === 0 ? opening : prefix).parse(input, start, probe);
10756
+ let content;
10757
+ let joined;
10758
+ if (head.ok && head.span.end <= end2) {
10759
+ content = head.span.end;
10760
+ joined = "prefix";
10761
+ } else if (run.length === 0) {
10762
+ return { ok: false, expected: head.ok ? ["line prefix"] : head.expected, span: { start, end: start } };
10763
+ } else if (lazy !== void 0 && lazy.parse(input, start, probe).ok) {
10764
+ content = start;
10765
+ joined = "lazy";
10766
+ } else break;
10767
+ const mark = probe._columnsSplit;
10768
+ const here = columnAt(input, content, c);
10769
+ const split = joined === "prefix" && mark !== void 0 && mark.at === content ? mark.split : here.split;
10770
+ run.push({ offset, source: content, end: end2, column: here.column + (split - here.split), split, lazy: joined === "lazy" });
10771
+ const text = input.slice(content, end2);
10772
+ const newline = end2 < input.length;
10773
+ parts.push(newline ? `${text}
10774
+ ` : text);
10775
+ offset += text.length + (newline ? 1 : 0);
10776
+ start = end2 + 1;
10777
+ if (!newline) break;
10778
+ }
10779
+ const view = { lines: run, parent: c.lineView };
10780
+ const saved = c.lineView, starts = c._lineStarts, lineIndex = c._lineIndex, scanned = c._lineScannedTo, probing = c._probe;
10781
+ const errors = c._errors?.length ?? 0;
10782
+ let cursor = -1;
10783
+ if (probing !== void 0) for (const line2 of run) {
10784
+ if (probing.offset >= line2.source && probing.offset <= line2.end) {
10785
+ cursor = line2.offset + probing.offset - line2.source;
10786
+ break;
10787
+ }
10788
+ }
10789
+ const inner = cursor < 0 ? void 0 : { offset: cursor, best: null };
10790
+ c.lineView = view;
10791
+ c._lineStarts = void 0;
10792
+ c._lineIndex = void 0;
10793
+ c._lineScannedTo = void 0;
10794
+ c._probe = inner;
10795
+ let result;
10796
+ try {
10797
+ result = body.parse(parts.join(""), 0, ctx);
10798
+ } finally {
10799
+ c.lineView = saved;
10800
+ c._lineStarts = starts;
10801
+ c._lineIndex = lineIndex;
10802
+ c._lineScannedTo = scanned;
10803
+ c._probe = probing;
10804
+ }
10805
+ if (inner?.best) failAt(c, inner.best.expected, sourceOffset(view, inner.best.span.start));
10806
+ if (c._errors !== void 0) for (let i = errors; i < c._errors.length; i++) {
10807
+ const error = c._errors[i];
10808
+ error.span = { start: sourceOffset(view, error.span.start), end: sourceOffset(view, error.span.end) };
10809
+ }
10810
+ if (!result.ok) {
10811
+ const at = sourceOffset(view, result.span.start);
10812
+ return { ...result, span: { start: at, end: at } };
10813
+ }
10814
+ const stop = result.span.end;
10815
+ const index = lineIndexAt(view, stop);
10816
+ const line = run[index];
10817
+ const end = stop === line.offset && index > 0 ? run[index - 1].end + 1 : stop - line.offset > line.end - line.source ? line.end + 1 : line.source + (stop - line.offset);
10818
+ recordLineRangeFromContext(c, input, pos, end);
10819
+ return { ok: true, value: result.value, span: { start: pos, end } };
10820
+ }
10821
+ };
10822
+ }
10823
+
10824
+ // src/table/line-scan.ts
10825
+ function rebuildLineScan(s, subtree) {
10826
+ if (s.kind === 2) {
10827
+ return lines(subtree(s.prefix), subtree(s.body), {
10828
+ ...s.first === void 0 ? {} : { first: subtree(s.first) },
10829
+ ...s.lazy === void 0 ? {} : { lazy: subtree(s.lazy) }
10830
+ });
10831
+ }
10832
+ if (s.kind === 3) return columns(s.n);
10833
+ return void 0;
10834
+ }
10835
+
10585
10836
  // src/combinators/literal.ts
10586
10837
  function asciiFoldEq(a, b) {
10587
10838
  if (a.length !== b.length) return false;
@@ -11297,7 +11548,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11297
11548
  ...prog.labels === void 0 ? {} : { triviaKindLabels: prog.labels },
11298
11549
  ...prog.classified === 1 ? { rootTriviaClassified: true } : {}
11299
11550
  };
11300
- const lines = prog.lines === 1;
11551
+ const lines2 = prog.lines === 1;
11301
11552
  const mode = prog.hostMode ?? "ast";
11302
11553
  const elided = mode === "ast" && hasDirectBuilder(prog);
11303
11554
  const coverageDefinitions = prog.cov === void 0 ? void 0 : Object.freeze(covDefinitions(prog).map(Object.freeze));
@@ -11325,7 +11576,7 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11325
11576
  ctx._fe = -1;
11326
11577
  ctx._fx = EMPTY_FX;
11327
11578
  ctx._fc = false;
11328
- if (lines && ctx._lineStarts === void 0) {
11579
+ if (lines2 && ctx._lineStarts === void 0) {
11329
11580
  ctx._lineStarts = [0];
11330
11581
  ctx._lineScannedTo = 0;
11331
11582
  }
@@ -11333,10 +11584,10 @@ function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(pr
11333
11584
  if (end < 0) {
11334
11585
  const fe = ctx._fe;
11335
11586
  const at = fe === void 0 || fe < 0 ? pos : fe;
11336
- const fspan = lines ? spanLines(ctx, at, at) : { start: at, end: at };
11587
+ const fspan = lines2 ? spanLines(ctx, at, at) : { start: at, end: at };
11337
11588
  return committed2(ctx) ? { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan, committed: true } : { ok: false, expected: ctx._fx ?? EMPTY_FX, span: fspan };
11338
11589
  }
11339
- return { ok: true, value: d.lastValue(), span: lines ? spanLines(ctx, pos, end) : { start: pos, end } };
11590
+ return { ok: true, value: d.lastValue(), span: lines2 ? spanLines(ctx, pos, end) : { start: pos, end } };
11340
11591
  };
11341
11592
  const ownTrivia = triviaOfRule(prog.rules[name]);
11342
11593
  const meta = ownTrivia === void 0 ? baseMeta : { ...baseMeta, grammarTrivia: ownTrivia };
@@ -11437,12 +11688,7 @@ function assemble(t2, prog, cfg) {
11437
11688
  return recognize;
11438
11689
  }
11439
11690
  const labelExtraIps = [];
11440
- for (const s of prog.scans ?? []) {
11441
- for (const r of s.skip) labelExtraIps.push(r[0]);
11442
- if (s.sentinel !== void 0) labelExtraIps.push(s.sentinel[0]);
11443
- if (s.recoverAt !== void 0) labelExtraIps.push(s.recoverAt[0]);
11444
- if (s.stopAt !== void 0) labelExtraIps.push(s.stopAt[0]);
11445
- }
11691
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) labelExtraIps.push(r[0]);
11446
11692
  for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0]);
11447
11693
  const closureLabels = computeSiteLabels(
11448
11694
  code,
@@ -14253,12 +14499,7 @@ function assemble(t2, prog, cfg) {
14253
14499
  let emitReached;
14254
14500
  {
14255
14501
  const extraIps = [];
14256
- for (const s of prog.scans ?? []) {
14257
- for (const r of s.skip) extraIps.push(r[0]);
14258
- if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
14259
- if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
14260
- if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
14261
- }
14502
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
14262
14503
  for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
14263
14504
  const roots = [...Object.values(prog.rules), ...extraIps];
14264
14505
  for (const ip of reachableSites(code, roots)) {
@@ -14455,6 +14696,11 @@ function assemble(t2, prog, cfg) {
14455
14696
  return link(ip);
14456
14697
  }
14457
14698
  for (const s of prog.scans ?? []) {
14699
+ const rebuilt = rebuildLineScan(s, subtreeComb);
14700
+ if (rebuilt !== void 0) {
14701
+ scansArr.push(rebuilt);
14702
+ continue;
14703
+ }
14458
14704
  const skip = s.skip.map((r) => subtreeComb(r));
14459
14705
  const raw = (s.flags & 1) !== 0;
14460
14706
  if (s.kind === 1) {
@@ -14696,6 +14942,11 @@ function emitScanSpec(s) {
14696
14942
  if (s.sent !== void 0) parts.push(`sent:${s.sent === null ? "null" : jsString(s.sent)}`);
14697
14943
  if (s.open !== void 0) parts.push(`open:${jsString(s.open)}`);
14698
14944
  if (s.close !== void 0) parts.push(`close:${jsString(s.close)}`);
14945
+ for (const key of ["prefix", "body", "first", "lazy"]) {
14946
+ const r = s[key];
14947
+ if (r !== void 0) parts.push(`${key}:${emitRef(r)}`);
14948
+ }
14949
+ if (s.n !== void 0) parts.push(`n:${s.n}`);
14699
14950
  return `{${parts.join(",")}}`;
14700
14951
  }
14701
14952
  function emitDispatchSpec(d) {
@@ -14705,12 +14956,7 @@ function emitAssemblies(prog, cfgs) {
14705
14956
  if (cfgs.length === 0) return ["a:[],"];
14706
14957
  const t2 = resolveTable(prog);
14707
14958
  const extraIps = [];
14708
- for (const s of prog.scans ?? []) {
14709
- for (const r of s.skip) extraIps.push(r[0]);
14710
- if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
14711
- if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
14712
- if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
14713
- }
14959
+ for (const s of prog.scans ?? []) for (const r of scanRefs(s)) extraIps.push(r[0]);
14714
14960
  for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
14715
14961
  const out = [];
14716
14962
  const seen = /* @__PURE__ */ new Set();
@@ -14862,6 +15108,8 @@ function children(def, winners) {
14862
15108
  ...def.recoverAt === void 0 ? [] : [def.recoverAt],
14863
15109
  ...def.stopAt === void 0 ? [] : [def.stopAt]
14864
15110
  ];
15111
+ case "lines":
15112
+ return [def.prefix, def.body, ...def.first === void 0 ? [] : [def.first], ...def.lazy === void 0 ? [] : [def.lazy]];
14865
15113
  case "lazy": {
14866
15114
  let resolved;
14867
15115
  try {
@@ -15005,6 +15253,10 @@ function payloadKey(p, d) {
15005
15253
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
15006
15254
  case "scanTo":
15007
15255
  return `scanTo\0${d.raw}\0${d.orEOF}\0${d.recoverAt !== void 0}\0${d.stopAt !== void 0}`;
15256
+ case "lines":
15257
+ return `lines\0${d.first !== void 0}\0${d.lazy !== void 0}`;
15258
+ case "columns":
15259
+ return `columns\0${d.n}`;
15008
15260
  case "guard":
15009
15261
  return `guard\0${fnKey(d.predSrc, d.predicate)}`;
15010
15262
  case "adjacency":
@@ -15125,6 +15377,8 @@ function segment(d, index) {
15125
15377
  if (d.recoverAt !== void 0 && index === d.skip.length + 1) return "scanTo.recoverAt";
15126
15378
  return "scanTo.stopAt";
15127
15379
  }
15380
+ case "lines":
15381
+ return ["lines.prefix", "lines.body", d.first === void 0 ? "lines.lazy" : "lines.first", "lines.lazy"][index] ?? "lines";
15128
15382
  default:
15129
15383
  return d.tag;
15130
15384
  }
@@ -16167,60 +16421,60 @@ function findKeywordRegexes(visited) {
16167
16421
  var sitesLine = (sites) => sites.slice(0, 6).map(siteToString).join("\n ") + (sites.length > 6 ? `
16168
16422
  \u2026 and ${sites.length - 6} more` : "");
16169
16423
  function formatDuplicationFindings(report) {
16170
- const lines = [];
16424
+ const lines2 = [];
16171
16425
  for (const f of report.structureLoss) {
16172
- lines.push(`parseman BUG [structure-loss] @ ${siteToString(f.site)}: arm[${f.earlier}] flattens \`${f.nodeType}\` where arm[${f.later}] structures it, on ${firstSetToString(f.on)}`);
16173
- lines.push(` flat: ${f.earlierShape}`);
16174
- lines.push(` rich: ${f.laterShape}`);
16175
- lines.push(` lost: ${f.lostNodeTypes.map((t2) => `\`${t2}\``).join(", ")}`);
16176
- lines.push(` fix: ${f.suggestion}`);
16426
+ lines2.push(`parseman BUG [structure-loss] @ ${siteToString(f.site)}: arm[${f.earlier}] flattens \`${f.nodeType}\` where arm[${f.later}] structures it, on ${firstSetToString(f.on)}`);
16427
+ lines2.push(` flat: ${f.earlierShape}`);
16428
+ lines2.push(` rich: ${f.laterShape}`);
16429
+ lines2.push(` lost: ${f.lostNodeTypes.map((t2) => `\`${t2}\``).join(", ")}`);
16430
+ lines2.push(` fix: ${f.suggestion}`);
16177
16431
  }
16178
16432
  for (const f of report.rewrites) {
16179
- lines.push(`parseman ${f.bug ? "BUG" : "rewrite"} [${f.rewrite}${f.sepByVerdict === void 0 ? "" : `/${f.sepByVerdict}`}] @ ${siteToString(f.site)}${f.astNeutral ? "" : " (candidate \u2014 verify AST identity)"}`);
16180
- lines.push(` from: ${f.from}`);
16181
- lines.push(` to: ${f.to}`);
16182
- lines.push(` fix: ${f.suggestion}`);
16183
- if (f.perf !== "") lines.push(` perf: ${f.perf}`);
16433
+ lines2.push(`parseman ${f.bug ? "BUG" : "rewrite"} [${f.rewrite}${f.sepByVerdict === void 0 ? "" : `/${f.sepByVerdict}`}] @ ${siteToString(f.site)}${f.astNeutral ? "" : " (candidate \u2014 verify AST identity)"}`);
16434
+ lines2.push(` from: ${f.from}`);
16435
+ lines2.push(` to: ${f.to}`);
16436
+ lines2.push(` fix: ${f.suggestion}`);
16437
+ if (f.perf !== "") lines2.push(` perf: ${f.perf}`);
16184
16438
  }
16185
16439
  for (const f of report.divergentNodes) {
16186
- lines.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t2) => `\`${t2}\``).join(", ")}`);
16187
- for (const p of f.productions) lines.push(` ${siteToString(p.site)}
16440
+ lines2.push(`parseman divergent-node: \`${f.nodeType}\` is built by ${f.count} different productions sharing ${f.sharedTerms.length} term(s): ${f.sharedTerms.map((t2) => `\`${t2}\``).join(", ")}`);
16441
+ for (const p of f.productions) lines2.push(` ${siteToString(p.site)}
16188
16442
  ${p.shape}
16189
16443
  unique to it: ${p.distinctTerms.length === 0 ? "(none)" : p.distinctTerms.map((t2) => `\`${t2}\``).join(", ")}`);
16190
- lines.push(` fix: ${f.suggestion}`);
16444
+ lines2.push(` fix: ${f.suggestion}`);
16191
16445
  }
16192
16446
  for (const f of report.keywordRegexes) {
16193
16447
  const src = f.source.length > 90 ? `${f.source.slice(0, 88)}\u2026` : f.source;
16194
- lines.push(`parseman ${f.bug ? "BUG " : ""}keyword-regex @ ${siteToString(f.site)}: /${src}/${f.flags}${f.vocabulary ? ` [${f.words.length} literal words${f.longestFirst ? "" : ", NOT longest-first"}]` : ""}${f.hazards.length > 0 ? ` [${f.hazards.length} prefix hazard(s)${f.bug ? ", UNRESCUED" : ""}]` : ""}${f.caseFoldRisk ? " [CASE-FOLD RISK]" : ""}`);
16195
- lines.push(` fix: ${f.suggestion}`);
16448
+ lines2.push(`parseman ${f.bug ? "BUG " : ""}keyword-regex @ ${siteToString(f.site)}: /${src}/${f.flags}${f.vocabulary ? ` [${f.words.length} literal words${f.longestFirst ? "" : ", NOT longest-first"}]` : ""}${f.hazards.length > 0 ? ` [${f.hazards.length} prefix hazard(s)${f.bug ? ", UNRESCUED" : ""}]` : ""}${f.caseFoldRisk ? " [CASE-FOLD RISK]" : ""}`);
16449
+ lines2.push(` fix: ${f.suggestion}`);
16196
16450
  }
16197
16451
  for (const f of report.duplicates) {
16198
- lines.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16199
- lines.push(` at: ${sitesLine(f.sites)}`);
16200
- lines.push(` fix: ${f.suggestion}`);
16452
+ lines2.push(`parseman duplication: ${f.count}\xD7 identical ${f.size}-node shape (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16453
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16454
+ lines2.push(` fix: ${f.suggestion}`);
16201
16455
  }
16202
16456
  for (const f of report.nearDuplicates) {
16203
- lines.push(`parseman near-duplication: ${f.count} clones of a ${f.sharedSize}-node shape differing at ONE slot (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16204
- lines.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
16205
- lines.push(` at: ${sitesLine(f.sites)}`);
16206
- lines.push(` fix: ${f.suggestion}`);
16457
+ lines2.push(`parseman near-duplication: ${f.count} clones of a ${f.sharedSize}-node shape differing at ONE slot (saves ${f.savings} nodes) \u2014 ${f.shape}`);
16458
+ lines2.push(` slot: ${f.slotPath} \u2208 { ${f.variants.join(" | ")} }`);
16459
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16460
+ lines2.push(` fix: ${f.suggestion}`);
16207
16461
  }
16208
16462
  for (const f of report.regexFragments) {
16209
- lines.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
16210
- lines.push(` at: ${sitesLine(f.sites)}`);
16211
- lines.push(` fix: ${f.suggestion}`);
16463
+ lines2.push(`parseman regex-fragment: \`${f.fragment}\` (${f.branches} branches) re-spelled in ${f.count} regex() terminals`);
16464
+ lines2.push(` at: ${sitesLine(f.sites)}`);
16465
+ lines2.push(` fix: ${f.suggestion}`);
16212
16466
  }
16213
16467
  for (const f of report.regexClasses) {
16214
- lines.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
16215
- for (const v of f.variants) lines.push(` [${v.source}] \xD7${v.count} ${v.delta}
16468
+ lines2.push(`parseman regex-class${f.drifted ? " DRIFT" : ""}: [${f.canonical}] across ${f.count} regex() terminals${f.bmpCeiling ? " [BMP ceiling]" : ""}`);
16469
+ for (const v of f.variants) lines2.push(` [${v.source}] \xD7${v.count} ${v.delta}
16216
16470
  ${sitesLine(v.sites)}`);
16217
- lines.push(` fix: ${f.suggestion}`);
16471
+ lines2.push(` fix: ${f.suggestion}`);
16218
16472
  }
16219
16473
  for (const f of report.overlaps) {
16220
- lines.push(`parseman overlap @ ${siteToString(f.site)}: arm[${f.a}] \u2229 arm[${f.b}] on ${firstSetToString(f.on)}${f.sharedPrefix === null ? "" : ` \u2014 shared prefix: ${f.sharedPrefix}`}${f.handledByStrategy ? " [sharedPrefix strategy already handles this]" : ""}`);
16221
- lines.push(` fix: ${f.suggestion}`);
16474
+ lines2.push(`parseman overlap @ ${siteToString(f.site)}: arm[${f.a}] \u2229 arm[${f.b}] on ${firstSetToString(f.on)}${f.sharedPrefix === null ? "" : ` \u2014 shared prefix: ${f.sharedPrefix}`}${f.handledByStrategy ? " [sharedPrefix strategy already handles this]" : ""}`);
16475
+ lines2.push(` fix: ${f.suggestion}`);
16222
16476
  }
16223
- return lines;
16477
+ return lines2;
16224
16478
  }
16225
16479
  function duplicationFindingCount(report) {
16226
16480
  return report.duplicates.length + report.nearDuplicates.length + report.regexFragments.length + report.regexClasses.length + report.overlaps.length + report.rewrites.length + report.divergentNodes.length + report.structureLoss.length + report.keywordRegexes.length;
@@ -16254,11 +16508,11 @@ function reportDuplication(opt, analyze) {
16254
16508
  );
16255
16509
  return void 0;
16256
16510
  }
16257
- const lines = formatDuplicationFindings(report);
16258
- if (lines.length > 0) {
16511
+ const lines2 = formatDuplicationFindings(report);
16512
+ if (lines2.length > 0) {
16259
16513
  if (level === "error") throw new Error(`parseman: ${duplicationFindingCount(report)} duplication/overlap finding(s)
16260
- ${lines.join("\n")}`);
16261
- for (const l of lines) console.warn(l);
16514
+ ${lines2.join("\n")}`);
16515
+ for (const l of lines2) console.warn(l);
16262
16516
  }
16263
16517
  return report;
16264
16518
  }
@@ -16874,7 +17128,7 @@ function cellsOf(line) {
16874
17128
  }
16875
17129
  return cells;
16876
17130
  }
16877
- var plain = (lines) => lines.map((l) => cellsOf(l).map((c) => c.text).join("")).join("\n");
17131
+ var plain = (lines2) => lines2.map((l) => cellsOf(l).map((c) => c.text).join("")).join("\n");
16878
17132
  var CODE_CACHE = /* @__PURE__ */ new Map();
16879
17133
  function codesFor(style) {
16880
17134
  if (style === void 0) return { pre: "", post: "" };
@@ -16935,10 +17189,10 @@ function linked(text, path) {
16935
17189
  const trail = text.slice(body.length);
16936
17190
  return `${tpl.pre}${path}${tpl.mid}${body}${tpl.post}${trail}`;
16937
17191
  }
16938
- function render2(lines, target = {}) {
16939
- if (target.color !== true) return plain(lines);
17192
+ function render2(lines2, target = {}) {
17193
+ if (target.color !== true) return plain(lines2);
16940
17194
  const wantLinks = target.links !== false;
16941
- return lines.map((line) => {
17195
+ return lines2.map((line) => {
16942
17196
  if (line.length === 1 && line[0].raw === true) return line[0].text;
16943
17197
  return cellsOf(line).map((c) => {
16944
17198
  const { pre, post } = codesFor(c.style);
@@ -17824,9 +18078,11 @@ function attempt(parser2, options) {
17824
18078
  const mark = saveCstMark(ctx);
17825
18079
  const log = ctx._triviaLog?.length;
17826
18080
  const rootLog = ctx._rootTriviaLog?.length;
18081
+ const split = ctx._columnsSplit;
17827
18082
  const result = parser2.parse(input, pos, ctx);
17828
18083
  if (result.ok) return result;
17829
18084
  rollbackCstCapture(ctx, mark);
18085
+ ctx._columnsSplit = split;
17830
18086
  if (log !== void 0 && ctx._triviaLog && ctx._triviaLog.length !== log) ctx._triviaLog.length = log;
17831
18087
  if (rootLog !== void 0 && ctx._rootTriviaLog && ctx._rootTriviaLog.length !== rootLog) ctx._rootTriviaLog.length = rootLog;
17832
18088
  if (result.committed && !contain) return result;
@@ -17872,7 +18128,9 @@ var FROZEN_TAGS = /* @__PURE__ */ new Set([
17872
18128
  "withCtx",
17873
18129
  "routed",
17874
18130
  "trivia",
17875
- "unknown"
18131
+ "unknown",
18132
+ "lines",
18133
+ "columns"
17876
18134
  ]);
17877
18135
  var CARRIED_META = [
17878
18136
  "grammarTrivia",
@@ -17901,7 +18159,7 @@ function subtreeContains(p, targets) {
17901
18159
  const kids = [];
17902
18160
  if (Array.isArray(rec.parsers)) kids.push(...rec.parsers);
17903
18161
  if (Array.isArray(rec.skip)) kids.push(...rec.skip);
17904
- for (const k of ["parser", "main", "skipped", "separator", "sentinel", "recoverAt", "stopAt", "selector", "otherwise", "fallback", "triviaParser"])
18162
+ for (const k of ["parser", "main", "skipped", "separator", "sentinel", "recoverAt", "stopAt", "selector", "otherwise", "fallback", "triviaParser", "prefix", "body", "first", "lazy"])
17905
18163
  if (rec[k]) kids.push(rec[k]);
17906
18164
  if (d.tag === "dispatch") {
17907
18165
  for (const c2 of d.cases) kids.push(c2.parser);
@@ -18708,9 +18966,9 @@ function locatedLines(l, opts) {
18708
18966
  t(l.id, TONE.ident)
18709
18967
  ]);
18710
18968
  out.push([t(" here ", TONE.faint), t(l.site, TONE.quiet)]);
18711
- const lines = wrap(`No change can be offered here: ${l.reason}`, width - 12, "");
18712
- out.push([t(" why ", TONE.quiet), t(lines[0] ?? "", TONE.warn)]);
18713
- for (const x of lines.slice(1)) out.push([t(" "), t(x, TONE.warn)]);
18969
+ const lines2 = wrap(`No change can be offered here: ${l.reason}`, width - 12, "");
18970
+ out.push([t(" why ", TONE.quiet), t(lines2[0] ?? "", TONE.warn)]);
18971
+ for (const x of lines2.slice(1)) out.push([t(" "), t(x, TONE.warn)]);
18714
18972
  return out;
18715
18973
  }
18716
18974