parseman 0.50.6 → 0.51.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 (59) 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 +214 -67
  4. package/dist/analysis/diagnostics.js.map +2 -2
  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 +220 -68
  8. package/dist/cli/index.js.map +2 -2
  9. package/dist/combinators/dispatch.d.ts.map +1 -1
  10. package/dist/combinators/first-set.d.ts +2 -26
  11. package/dist/combinators/first-set.d.ts.map +1 -1
  12. package/dist/combinators/scanTo.d.ts +23 -1
  13. package/dist/combinators/scanTo.d.ts.map +1 -1
  14. package/dist/compiler/fields.d.ts.map +1 -1
  15. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  16. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  17. package/dist/compiler/token-capability.js +40 -29
  18. package/dist/compiler/token-capability.js.map +1 -1
  19. package/dist/coverage.d.ts.map +1 -1
  20. package/dist/cst/reflection.d.ts.map +1 -1
  21. package/dist/index.js +241 -74
  22. package/dist/index.js.map +2 -2
  23. package/dist/plugin/index.js +228 -71
  24. package/dist/plugin/index.js.map +2 -2
  25. package/dist/spec/index.js +164 -48
  26. package/dist/spec/index.js.map +2 -2
  27. package/dist/table/assemble.d.ts.map +1 -1
  28. package/dist/table/emit.d.ts.map +1 -1
  29. package/dist/table/encode.d.ts.map +1 -1
  30. package/dist/table/exec.d.ts.map +1 -1
  31. package/dist/table/index.js +220 -68
  32. package/dist/table/index.js.map +2 -2
  33. package/dist/table/program.d.ts +2 -0
  34. package/dist/table/program.d.ts.map +1 -1
  35. package/dist/types.d.ts +2 -0
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/version.d.ts +1 -1
  38. package/package.json +1 -1
  39. package/src/analysis/choice-cost.ts +6 -1
  40. package/src/analysis/commitment.ts +10 -2
  41. package/src/analysis/duplication.ts +7 -2
  42. package/src/analysis/gating.ts +6 -1
  43. package/src/analysis/rebuild.ts +1 -1
  44. package/src/combinators/dispatch.ts +4 -1
  45. package/src/combinators/first-set.ts +58 -36
  46. package/src/combinators/scanTo.ts +163 -9
  47. package/src/compiler/fields.ts +8 -2
  48. package/src/compiler/grammar-coverage-ids.ts +6 -1
  49. package/src/compiler/ir-serialize.ts +7 -2
  50. package/src/compiler/token-alphabet.ts +1 -1
  51. package/src/coverage.ts +7 -1
  52. package/src/cst/reflection.ts +6 -1
  53. package/src/table/assemble.ts +11 -1
  54. package/src/table/emit.ts +4 -0
  55. package/src/table/encode.ts +16 -8
  56. package/src/table/exec.ts +7 -1
  57. package/src/table/program.ts +2 -0
  58. package/src/types.ts +1 -1
  59. package/src/version.ts +1 -1
@@ -7,14 +7,14 @@ import { ResolverFactory } from "oxc-resolver";
7
7
  import MagicString from "magic-string";
8
8
 
9
9
  // src/combinators/first-set.ts
10
- function classifyFinalChoice(arms, resolve3) {
10
+ function classifyFinalChoice(arms, resolve3, activeTrivia) {
11
11
  const firstSets = [];
12
12
  const nullable2 = [];
13
13
  let exclusive = true;
14
14
  for (const arm of arms) {
15
15
  const empty2 = matchesEmpty(arm, /* @__PURE__ */ new Set(), resolve3);
16
16
  nullable2.push(empty2);
17
- firstSets.push(empty2 ? { kind: "any" } : firstSetOf(arm, /* @__PURE__ */ new Set(), resolve3));
17
+ firstSets.push(empty2 ? { kind: "any" } : firstSetOf(arm, /* @__PURE__ */ new Set(), resolve3, activeTrivia));
18
18
  if (empty2) exclusive = false;
19
19
  }
20
20
  if (exclusive) {
@@ -75,7 +75,7 @@ function intersects(a, b) {
75
75
  return false;
76
76
  }
77
77
  function fromChar(code) {
78
- return { kind: "ranges", ranges: [{ lo: code, hi: code }] };
78
+ return fromRange(code, code);
79
79
  }
80
80
  function startsFirstSet(combinator, input, pos) {
81
81
  const fs3 = combinator._meta.firstSet;
@@ -168,11 +168,10 @@ function matchesEmptyBody(p, seen, resolve3) {
168
168
  }
169
169
  }
170
170
  function isZeroWidthAssertion(p) {
171
- const tag = p._def.tag;
172
- return tag === "not" || tag === "adjacency";
171
+ return p._tag === "not" || p._tag === "adjacency";
173
172
  }
174
173
  function isPositiveLookahead(p) {
175
- return p._def.tag === "peek";
174
+ return p._tag === "peek";
176
175
  }
177
176
  function narrowBy(acc, constraint) {
178
177
  if (constraint.kind === "any") return acc;
@@ -188,34 +187,42 @@ function narrowBy(acc, constraint) {
188
187
  return ranges.length === 0 ? { kind: "empty" } : { kind: "ranges", ranges };
189
188
  }
190
189
  function applyAssertion(fs3, assertion) {
191
- if (assertion === null) return fs3;
192
- if (fs3.kind === "empty") return assertion;
193
- return narrowBy(fs3, assertion) ?? fs3;
190
+ return assertion === null ? fs3 : fs3.kind === "empty" ? assertion : narrowBy(fs3, assertion);
194
191
  }
195
- function sequenceFirstSet(parsers) {
196
- let fs3 = empty();
197
- let assertion = null;
198
- for (const p of parsers) {
192
+ function sequenceSet(parsers, fs3, empties, trivia2) {
193
+ const afterBoundary = (i) => {
194
+ const rest = at(i);
195
+ return trivia2 ? union(rest, fs3(trivia2)) : rest;
196
+ };
197
+ const at = (i) => {
198
+ const p = parsers[i];
199
199
  if (isPositiveLookahead(p)) {
200
- assertion = narrowBy(assertion, p._meta.firstSet);
201
- continue;
200
+ const constraint = fs3(p);
201
+ return i + 1 === parsers.length ? constraint : applyAssertion(afterBoundary(i + 1), constraint);
202
202
  }
203
- if (!isZeroWidthAssertion(p)) fs3 = union(fs3, p._meta.firstSet);
204
- if (!matchesEmpty(p)) return applyAssertion(fs3, assertion);
205
- }
206
- return applyAssertion(fs3, assertion);
203
+ if (isZeroWidthAssertion(p)) {
204
+ return i + 1 === parsers.length ? empty() : afterBoundary(i + 1);
205
+ }
206
+ const first = fs3(p);
207
+ if (!empties(p) || i + 1 === parsers.length) return first;
208
+ return union(first, afterBoundary(i + 1));
209
+ };
210
+ return at(0);
211
+ }
212
+ function sequenceFirstSet(parsers) {
213
+ return sequenceSet(parsers, (p) => p._meta.firstSet, matchesEmpty);
207
214
  }
208
- function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve3) {
215
+ function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve3, activeTrivia) {
209
216
  if (seen.has(p)) return any();
210
217
  seen.add(p);
211
218
  try {
212
- return firstSetBody(p, seen, resolve3);
219
+ return firstSetBody(p, seen, resolve3, activeTrivia);
213
220
  } finally {
214
221
  seen.delete(p);
215
222
  }
216
223
  }
217
- function firstSetBody(p, seen, resolve3) {
218
- const fs3 = (c) => firstSetOf(c, seen, resolve3);
224
+ function firstSetBody(p, seen, resolve3, activeTrivia) {
225
+ const fs3 = (c) => firstSetOf(c, seen, resolve3, activeTrivia);
219
226
  const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve3);
220
227
  const d = p._def;
221
228
  switch (d.tag) {
@@ -241,17 +248,7 @@ function firstSetBody(p, seen, resolve3) {
241
248
  return out;
242
249
  }
243
250
  case "sequence": {
244
- let out = empty();
245
- let assertion = null;
246
- for (const term of d.parsers) {
247
- if (isPositiveLookahead(term)) {
248
- assertion = narrowBy(assertion, fs3(term));
249
- continue;
250
- }
251
- if (!isZeroWidthAssertion(term)) out = union(out, fs3(term));
252
- if (!empties(term)) return applyAssertion(out, assertion);
253
- }
254
- return applyAssertion(out, assertion);
251
+ return sequenceSet(d.parsers, fs3, empties, activeTrivia);
255
252
  }
256
253
  case "peek": {
257
254
  const inner = d.parser;
@@ -266,12 +263,16 @@ function firstSetBody(p, seen, resolve3) {
266
263
  case "token":
267
264
  case "leaf":
268
265
  case "node":
269
- case "grammar":
270
- case "expect":
271
- return fs3(d.parser);
272
266
  case "sepBy":
267
+ case "expect":
273
268
  return fs3(d.parser);
274
- // both min 0 and min 1 start with the item
269
+ case "grammar":
270
+ return firstSetOf(
271
+ d.parser,
272
+ seen,
273
+ resolve3,
274
+ d.clearTrivia ? null : d.triviaParser ?? activeTrivia
275
+ );
275
276
  default:
276
277
  return p._meta.firstSet;
277
278
  }
@@ -1752,7 +1753,12 @@ function childrenOf(def) {
1752
1753
  case "sepBy":
1753
1754
  return [def.parser, def.separator];
1754
1755
  case "scanTo":
1755
- return [def.sentinel, ...def.skip];
1756
+ return [
1757
+ def.sentinel,
1758
+ ...def.skip,
1759
+ ...def.recoverAt === void 0 ? [] : [def.recoverAt],
1760
+ ...def.stopAt === void 0 ? [] : [def.stopAt]
1761
+ ];
1756
1762
  case "routed":
1757
1763
  return def.fallback ? [def.fallback] : [];
1758
1764
  case "literal":
@@ -3831,7 +3837,7 @@ function parserUsesRouted(parser2, seen = /* @__PURE__ */ new Set()) {
3831
3837
  case "grammar":
3832
3838
  return parserUsesRouted(def.parser, seen) || (def.triviaParser ? parserUsesRouted(def.triviaParser, seen) : false);
3833
3839
  case "scanTo":
3834
- return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen));
3840
+ return parserUsesRouted(def.sentinel, seen) || def.skip.some((entry) => parserUsesRouted(entry, seen)) || def.recoverAt !== void 0 && parserUsesRouted(def.recoverAt, seen) || def.stopAt !== void 0 && parserUsesRouted(def.stopAt, seen);
3835
3841
  case "recover":
3836
3842
  return parserUsesRouted(def.parser, seen) || parserUsesRouted(def.sentinel, seen);
3837
3843
  case "lazy":
@@ -4837,7 +4843,7 @@ function parserHasOwnFields(p, seen = /* @__PURE__ */ new Set()) {
4837
4843
  case "grammar":
4838
4844
  return parserHasOwnFields(d.parser, seen) || (d.triviaParser ? parserHasOwnFields(d.triviaParser, seen) : false);
4839
4845
  case "scanTo":
4840
- return parserHasOwnFields(d.sentinel, seen) || d.skip.some((x) => parserHasOwnFields(x, seen));
4846
+ 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);
4841
4847
  case "recover":
4842
4848
  return parserHasOwnFields(d.parser, seen) || parserHasOwnFields(d.sentinel, seen);
4843
4849
  case "routed":
@@ -5220,7 +5226,12 @@ function childrenOf2(d) {
5220
5226
  case "recover":
5221
5227
  return [d.parser, d.sentinel];
5222
5228
  case "scanTo":
5223
- return [d.sentinel, ...d.skip];
5229
+ return [
5230
+ d.sentinel,
5231
+ ...d.skip,
5232
+ ...d.recoverAt === void 0 ? [] : [d.recoverAt],
5233
+ ...d.stopAt === void 0 ? [] : [d.stopAt]
5234
+ ];
5224
5235
  case "grammar":
5225
5236
  return d.triviaParser ? [d.parser, d.triviaParser] : [d.parser];
5226
5237
  case "routed":
@@ -5491,7 +5502,7 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
5491
5502
  case "sepBy":
5492
5503
  return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
5493
5504
  case "scanTo":
5494
- return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen));
5505
+ 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);
5495
5506
  case "recover":
5496
5507
  return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
5497
5508
  case "expect":
@@ -5540,7 +5551,7 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
5540
5551
  case "sepBy":
5541
5552
  return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.separator, seen);
5542
5553
  case "scanTo":
5543
- return hasDirectBuildDef(d.sentinel, seen) || d.skip.some((x) => hasDirectBuildDef(x, seen));
5554
+ 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);
5544
5555
  case "recover":
5545
5556
  return hasDirectBuildDef(d.parser, seen) || hasDirectBuildDef(d.sentinel, seen);
5546
5557
  case "routed":
@@ -5577,7 +5588,7 @@ function mayCommitFailure(p, seen = /* @__PURE__ */ new Set(), resolve3) {
5577
5588
  case "withCtx":
5578
5589
  return mayCommitFailure(d.parser, seen, resolve3);
5579
5590
  case "scanTo":
5580
- return mayCommitFailure(d.sentinel, seen, resolve3) || d.skip.some((x) => mayCommitFailure(x, seen, resolve3));
5591
+ return mayCommitFailure(d.sentinel, seen, resolve3) || d.skip.some((x) => mayCommitFailure(x, seen, resolve3)) || d.recoverAt !== void 0 && mayCommitFailure(d.recoverAt, seen, resolve3) || d.stopAt !== void 0 && mayCommitFailure(d.stopAt, seen, resolve3);
5581
5592
  case "lazy": {
5582
5593
  try {
5583
5594
  return mayCommitFailure(d.thunk(), seen, resolve3);
@@ -7118,7 +7129,9 @@ var Encoder = class {
7118
7129
  followClass(parsers, i) {
7119
7130
  let fs3 = { kind: "empty" };
7120
7131
  const rr = this.refResolver();
7121
- for (let j = i + 1; j < parsers.length; j++) fs3 = union(fs3, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr));
7132
+ for (let j = i + 1; j < parsers.length; j++) {
7133
+ fs3 = union(fs3, firstSetOf(parsers[j], /* @__PURE__ */ new Set(), rr, this.activeTrivia));
7134
+ }
7122
7135
  return this.charClass(fs3);
7123
7136
  }
7124
7137
  constant(v) {
@@ -7335,7 +7348,7 @@ var Encoder = class {
7335
7348
  */
7336
7349
  subtree(c) {
7337
7350
  const ip = this.node(c).ip;
7338
- const fs3 = firstSetOf(c, /* @__PURE__ */ new Set(), this.refResolver());
7351
+ const fs3 = firstSetOf(c, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia);
7339
7352
  return [ip, fs3.kind === "empty" ? -2 : this.charClass(fs3)];
7340
7353
  }
7341
7354
  /** Pool one scan spec. One per combinator: `node()` memoizes by identity. */
@@ -7481,6 +7494,8 @@ var Encoder = class {
7481
7494
  flags: (d.raw ? 1 : 0) | (d.orEOF ? 2 : 0),
7482
7495
  skip: d.skip.map((c) => this.subtree(c)),
7483
7496
  sentinel: this.subtree(d.sentinel),
7497
+ ...d.recoverAt === void 0 ? {} : { recoverAt: this.subtree(d.recoverAt) },
7498
+ ...d.stopAt === void 0 ? {} : { stopAt: this.subtree(d.stopAt) },
7484
7499
  sent: sentDef.tag === "literal" ? sentDef.value : null
7485
7500
  }));
7486
7501
  }
@@ -7554,12 +7569,12 @@ var Encoder = class {
7554
7569
  return this.covWrap(gated, armCovIds?.[src], 1);
7555
7570
  });
7556
7571
  const rr = this.refResolver();
7557
- const finalChoice = classifyFinalChoice(arms, rr);
7572
+ const finalChoice = classifyFinalChoice(arms, rr, this.activeTrivia);
7558
7573
  const transactionalFirst = arms.map((arm, i) => {
7559
7574
  if (!finalChoice.nullable[i]) return void 0;
7560
7575
  const def = arm._def;
7561
7576
  if (def.tag !== "attempt" || matchesEmpty(def.parser, /* @__PURE__ */ new Set(), rr)) return void 0;
7562
- return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr);
7577
+ return firstSetOf(def.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia);
7563
7578
  });
7564
7579
  const gateFirst = finalChoice.firstSets.map((first, i) => transactionalFirst[i] ?? first);
7565
7580
  const classes = gateFirst.map((first, i) => finalChoice.nullable[i] && transactionalFirst[i] === void 0 ? -1 : this.charClass(first));
@@ -7606,7 +7621,7 @@ var Encoder = class {
7606
7621
  case "oneOrMore": {
7607
7622
  const child = this.node(d.parser).ip;
7608
7623
  const rr = this.refResolver();
7609
- const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.existingCharClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr));
7624
+ const itemClass = matchesEmpty(d.parser, /* @__PURE__ */ new Set(), rr) ? -1 : this.existingCharClass(firstSetOf(d.parser, /* @__PURE__ */ new Set(), rr, this.activeTrivia));
7610
7625
  const op = d.valueUnused ? OP_REPV : OP_REP;
7611
7626
  const min = d.tag === "many" ? 0 : d.min;
7612
7627
  return this.rec ? this.emit(op, child, min, d.max ?? -1, -1, 0, this.expected(deriveExpected(d.parser)), itemClass) : this.emit(op, child, min, d.max ?? -1, -1, 0);
@@ -7624,7 +7639,7 @@ var Encoder = class {
7624
7639
  sep2,
7625
7640
  flags,
7626
7641
  this.expected(deriveExpected(d.parser)),
7627
- this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver()))
7642
+ this.charClass(firstSetOf(d.separator, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia))
7628
7643
  );
7629
7644
  }
7630
7645
  return d.min >= 2 ? this.emit(OP_REP, child, d.min, d.max ?? -1, sep2, flags, this.expected(deriveExpected(d.parser))) : this.emit(OP_REP, child, d.min, d.max ?? -1, sep2, flags);
@@ -7679,7 +7694,7 @@ var Encoder = class {
7679
7694
  d.tags === void 0 ? -1 : this.constant(d.tags)
7680
7695
  );
7681
7696
  if (matchesEmpty(d.parser)) return body;
7682
- const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver()) : d.parser._meta.firstSet;
7697
+ const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
7683
7698
  if (fs3.kind === "any") return body;
7684
7699
  const cls = this.charClass(fs3);
7685
7700
  if (cls < 0) return body;
@@ -7793,7 +7808,7 @@ var Encoder = class {
7793
7808
  case "attempt": {
7794
7809
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip);
7795
7810
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
7796
- const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver()) : d.parser._meta.firstSet;
7811
+ const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver(), this.activeTrivia) : d.parser._meta.firstSet;
7797
7812
  if (fs3.kind === "any" || matchesEmpty(d.parser)) return inner;
7798
7813
  const cls = this.charClass(fs3);
7799
7814
  if (cls < 0) return inner;
@@ -7914,6 +7929,8 @@ var Encoder = class {
7914
7929
  const refs = [];
7915
7930
  for (const s of this.scans) {
7916
7931
  if (s.sentinel !== void 0) refs.push(s.sentinel);
7932
+ if (s.recoverAt !== void 0) refs.push(s.recoverAt);
7933
+ if (s.stopAt !== void 0) refs.push(s.stopAt);
7917
7934
  refs.push(...s.skip);
7918
7935
  }
7919
7936
  for (const set of this.scanSkipSets) refs.push(...set);
@@ -7933,7 +7950,9 @@ var Encoder = class {
7933
7950
  this.scans = this.scans.map((s) => ({
7934
7951
  ...s,
7935
7952
  skip: s.skip.map(res),
7936
- ...s.sentinel === void 0 ? {} : { sentinel: res(s.sentinel) }
7953
+ ...s.sentinel === void 0 ? {} : { sentinel: res(s.sentinel) },
7954
+ ...s.recoverAt === void 0 ? {} : { recoverAt: res(s.recoverAt) },
7955
+ ...s.stopAt === void 0 ? {} : { stopAt: res(s.stopAt) }
7937
7956
  }));
7938
7957
  this.scanSkipSets = this.scanSkipSets.map((set) => set.map(res));
7939
7958
  }
@@ -11177,7 +11196,7 @@ function resolveScanSkip(explicitSkip, raw, ctx) {
11177
11196
  out.push(...explicitSkip);
11178
11197
  return out;
11179
11198
  }
11180
- function scanTo(sentinel, { skip, raw = false, orEOF = false } = {}) {
11199
+ function scanTo(sentinel, { skip, raw = false, orEOF = false, recoverAt, stopAt } = {}) {
11181
11200
  const meta = {
11182
11201
  firstSet: any(),
11183
11202
  canMatchNewline: true,
@@ -11187,7 +11206,15 @@ function scanTo(sentinel, { skip, raw = false, orEOF = false } = {}) {
11187
11206
  return {
11188
11207
  _tag: "scanTo",
11189
11208
  _meta: meta,
11190
- _def: { tag: "scanTo", sentinel, skip: explicitSkip, raw, orEOF },
11209
+ _def: {
11210
+ tag: "scanTo",
11211
+ sentinel,
11212
+ skip: explicitSkip,
11213
+ raw,
11214
+ orEOF,
11215
+ ...recoverAt === void 0 ? {} : { recoverAt },
11216
+ ...stopAt === void 0 ? {} : { stopAt }
11217
+ },
11191
11218
  parse(input, pos, ctx) {
11192
11219
  let cur = pos;
11193
11220
  const skip2 = resolveScanSkip(explicitSkip, raw, ctx);
@@ -11199,28 +11226,130 @@ function scanTo(sentinel, { skip, raw = false, orEOF = false } = {}) {
11199
11226
  pushCstLeaf(ctx, leaf2);
11200
11227
  }
11201
11228
  };
11202
- while (cur < input.length) {
11203
- const s = sentinel.parse(input, cur, probeCtx);
11204
- if (s.ok) {
11229
+ if (recoverAt === void 0 && stopAt === void 0) {
11230
+ while (cur < input.length) {
11231
+ const s = sentinel.parse(input, cur, probeCtx);
11232
+ if (s.ok) {
11233
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
11234
+ emit(cur);
11235
+ return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
11236
+ }
11237
+ let advanced = false;
11238
+ for (const skipper of skip2) {
11239
+ const r = skipper.parse(input, cur, probeCtx);
11240
+ if (r.ok && r.span.end > cur) {
11241
+ cur = r.span.end;
11242
+ advanced = true;
11243
+ break;
11244
+ }
11245
+ }
11246
+ if (!advanced) cur++;
11247
+ }
11248
+ if (orEOF) {
11205
11249
  if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
11206
11250
  emit(cur);
11207
11251
  return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
11208
11252
  }
11253
+ const sentDef2 = sentinel._def;
11254
+ const expected2 = sentDef2.tag === "literal" ? [JSON.stringify(sentDef2.value)] : ["sentinel"];
11255
+ return { ok: false, expected: expected2, span: { start: pos, end: cur } };
11256
+ }
11257
+ let recovery = -1;
11258
+ let firstSentinel = -1;
11259
+ let sentinelCount = 0;
11260
+ let recoveryErrorCount = -1;
11261
+ let sentinelErrorCount = -1;
11262
+ const succeed = (end) => {
11263
+ if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end);
11264
+ emit(end);
11265
+ return { ok: true, value: input.slice(pos, end), span: { start: pos, end } };
11266
+ };
11267
+ const recoveredEnd = () => {
11268
+ const acceptSentinel = firstSentinel >= 0 && (sentinelCount & 1) === 1;
11269
+ const errors = ctx._errors;
11270
+ if (errors !== void 0) {
11271
+ const keep = acceptSentinel ? sentinelErrorCount : recoveryErrorCount;
11272
+ if (keep >= 0 && errors.length > keep) errors.length = keep;
11273
+ }
11274
+ return acceptSentinel ? firstSentinel : recovery;
11275
+ };
11276
+ while (cur < input.length) {
11277
+ const sentinelProbeErrorCount = ctx._errors?.length ?? -1;
11278
+ const s = sentinel.parse(input, cur, probeCtx);
11279
+ const sentinelProbeErrors = ctx._errors;
11280
+ if (sentinelProbeErrors !== void 0 && sentinelProbeErrorCount >= 0 && sentinelProbeErrors.length > sentinelProbeErrorCount) {
11281
+ sentinelProbeErrors.length = sentinelProbeErrorCount;
11282
+ }
11283
+ if (s.ok) {
11284
+ if (recovery < 0) return succeed(cur);
11285
+ if (s.span.end <= cur) {
11286
+ throw new TypeError("scanTo recovery requires a sentinel that consumes input");
11287
+ }
11288
+ if (firstSentinel < 0) {
11289
+ firstSentinel = cur;
11290
+ sentinelErrorCount = sentinelProbeErrorCount;
11291
+ }
11292
+ sentinelCount++;
11293
+ cur = s.span.end;
11294
+ continue;
11295
+ }
11296
+ if (stopAt !== void 0) {
11297
+ const errorCount = ctx._errors?.length ?? -1;
11298
+ const stop = stopAt.parse(input, cur, probeCtx);
11299
+ const errors = ctx._errors;
11300
+ if (errors !== void 0 && errorCount >= 0 && errors.length > errorCount) {
11301
+ errors.length = errorCount;
11302
+ }
11303
+ if (stop.ok) {
11304
+ if (recovery < 0) {
11305
+ recovery = cur;
11306
+ recoveryErrorCount = errorCount;
11307
+ }
11308
+ return succeed(recoveredEnd());
11309
+ }
11310
+ }
11209
11311
  let advanced = false;
11210
11312
  for (const skipper of skip2) {
11313
+ const errorCount = ctx._errors?.length ?? -1;
11211
11314
  const r = skipper.parse(input, cur, probeCtx);
11212
11315
  if (r.ok && r.span.end > cur) {
11213
11316
  cur = r.span.end;
11214
11317
  advanced = true;
11215
11318
  break;
11216
11319
  }
11320
+ const errors = ctx._errors;
11321
+ if (errors !== void 0 && errorCount >= 0 && errors.length > errorCount) {
11322
+ errors.length = errorCount;
11323
+ }
11324
+ }
11325
+ if (advanced) continue;
11326
+ if (recoverAt !== void 0) {
11327
+ const errorCount = ctx._errors?.length ?? -1;
11328
+ const recover = recoverAt.parse(input, cur, probeCtx);
11329
+ const errors = ctx._errors;
11330
+ if (errors !== void 0 && errorCount >= 0 && errors.length > errorCount) {
11331
+ errors.length = errorCount;
11332
+ }
11333
+ if (recover.ok) {
11334
+ if (recover.span.end <= cur) {
11335
+ throw new TypeError("scanTo recoverAt must consume input");
11336
+ }
11337
+ if (recovery < 0) {
11338
+ recovery = cur;
11339
+ recoveryErrorCount = errorCount;
11340
+ }
11341
+ if (firstSentinel >= 0 && (sentinelCount & 1) === 1) {
11342
+ return succeed(recoveredEnd());
11343
+ }
11344
+ cur = recover.span.end;
11345
+ continue;
11346
+ }
11217
11347
  }
11218
- if (!advanced) cur++;
11348
+ cur++;
11219
11349
  }
11350
+ if (recovery >= 0) return succeed(recoveredEnd());
11220
11351
  if (orEOF) {
11221
- if (ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, cur);
11222
- emit(cur);
11223
- return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
11352
+ return succeed(cur);
11224
11353
  }
11225
11354
  const sentDef = sentinel._def;
11226
11355
  const expected = sentDef.tag === "literal" ? [JSON.stringify(sentDef.value)] : ["sentinel"];
@@ -11508,6 +11637,8 @@ function assemble(t, prog, cfg) {
11508
11637
  for (const s of prog.scans ?? []) {
11509
11638
  for (const r of s.skip) labelExtraIps.push(r[0]);
11510
11639
  if (s.sentinel !== void 0) labelExtraIps.push(s.sentinel[0]);
11640
+ if (s.recoverAt !== void 0) labelExtraIps.push(s.recoverAt[0]);
11641
+ if (s.stopAt !== void 0) labelExtraIps.push(s.stopAt[0]);
11511
11642
  }
11512
11643
  for (const set of prog.scanSkip ?? []) for (const r of set) labelExtraIps.push(r[0]);
11513
11644
  const closureLabels = computeSiteLabels(
@@ -14193,6 +14324,8 @@ function assemble(t, prog, cfg) {
14193
14324
  for (const s of prog.scans ?? []) {
14194
14325
  for (const r of s.skip) extraIps.push(r[0]);
14195
14326
  if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
14327
+ if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
14328
+ if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
14196
14329
  }
14197
14330
  for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
14198
14331
  const roots = [...Object.values(prog.rules), ...extraIps];
@@ -14404,7 +14537,13 @@ function assemble(t, prog, cfg) {
14404
14537
  continue;
14405
14538
  }
14406
14539
  const sentDef = typeof s.sent === "string" ? { tag: "literal", value: s.sent, caseInsensitive: false } : void 0;
14407
- scansArr.push(scanTo(subtreeComb(s.sentinel, sentDef), { skip, raw, orEOF: (s.flags & 2) !== 0 }));
14540
+ scansArr.push(scanTo(subtreeComb(s.sentinel, sentDef), {
14541
+ skip,
14542
+ raw,
14543
+ orEOF: (s.flags & 2) !== 0,
14544
+ ...s.recoverAt === void 0 ? {} : { recoverAt: subtreeComb(s.recoverAt) },
14545
+ ...s.stopAt === void 0 ? {} : { stopAt: subtreeComb(s.stopAt) }
14546
+ }));
14408
14547
  }
14409
14548
  const scans = scansArr;
14410
14549
  const scanSkip = (prog.scanSkip ?? []).map((set) => set.map((r) => subtreeComb(r)));
@@ -14621,6 +14760,8 @@ function emitRef(r) {
14621
14760
  function emitScanSpec(s) {
14622
14761
  const parts = [`kind:${s.kind}`, `flags:${s.flags}`, `skip:[${s.skip.map(emitRef).join(",")}]`];
14623
14762
  if (s.sentinel !== void 0) parts.push(`sentinel:${emitRef(s.sentinel)}`);
14763
+ if (s.recoverAt !== void 0) parts.push(`recoverAt:${emitRef(s.recoverAt)}`);
14764
+ if (s.stopAt !== void 0) parts.push(`stopAt:${emitRef(s.stopAt)}`);
14624
14765
  if (s.sent !== void 0) parts.push(`sent:${s.sent === null ? "null" : jsString(s.sent)}`);
14625
14766
  if (s.open !== void 0) parts.push(`open:${jsString(s.open)}`);
14626
14767
  if (s.close !== void 0) parts.push(`close:${jsString(s.close)}`);
@@ -14647,6 +14788,8 @@ function emitAssemblies(prog, cfgs) {
14647
14788
  for (const s of prog.scans ?? []) {
14648
14789
  for (const r of s.skip) extraIps.push(r[0]);
14649
14790
  if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
14791
+ if (s.recoverAt !== void 0) extraIps.push(s.recoverAt[0]);
14792
+ if (s.stopAt !== void 0) extraIps.push(s.stopAt[0]);
14650
14793
  }
14651
14794
  for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
14652
14795
  const out = [];
@@ -14789,7 +14932,12 @@ function children(def, winners) {
14789
14932
  case "sepBy":
14790
14933
  return [def.parser, def.separator];
14791
14934
  case "scanTo":
14792
- return [def.sentinel, ...def.skip];
14935
+ return [
14936
+ def.sentinel,
14937
+ ...def.skip,
14938
+ ...def.recoverAt === void 0 ? [] : [def.recoverAt],
14939
+ ...def.stopAt === void 0 ? [] : [def.stopAt]
14940
+ ];
14793
14941
  case "lazy": {
14794
14942
  let resolved;
14795
14943
  try {
@@ -14902,7 +15050,7 @@ function payloadKey(p, d) {
14902
15050
  case "expect":
14903
15051
  return `expect\0${d.label ?? ""}\0${d.expected.join("")}`;
14904
15052
  case "scanTo":
14905
- return `scanTo\0${d.raw}\0${d.orEOF}`;
15053
+ return `scanTo\0${d.raw}\0${d.orEOF}\0${d.recoverAt !== void 0}\0${d.stopAt !== void 0}`;
14906
15054
  case "guard":
14907
15055
  return `guard\0${fnKey(d.predSrc, d.predicate)}`;
14908
15056
  case "adjacency":
@@ -15017,8 +15165,12 @@ function segment(d, index) {
15017
15165
  return index === 0 ? "sepBy.item" : "sepBy.sep";
15018
15166
  case "recover":
15019
15167
  return index === 0 ? "recover.body" : "recover.sentinel";
15020
- case "scanTo":
15021
- return index === 0 ? "scanTo.sentinel" : `scanTo.skip[${index - 1}]`;
15168
+ case "scanTo": {
15169
+ if (index === 0) return "scanTo.sentinel";
15170
+ if (index <= d.skip.length) return `scanTo.skip[${index - 1}]`;
15171
+ if (d.recoverAt !== void 0 && index === d.skip.length + 1) return "scanTo.recoverAt";
15172
+ return "scanTo.stopAt";
15173
+ }
15022
15174
  default:
15023
15175
  return d.tag;
15024
15176
  }
@@ -16294,7 +16446,12 @@ function childrenOf3(def) {
16294
16446
  case "sepBy":
16295
16447
  return [def.parser, def.separator];
16296
16448
  case "scanTo":
16297
- return [def.sentinel, ...def.skip];
16449
+ return [
16450
+ def.sentinel,
16451
+ ...def.skip,
16452
+ ...def.recoverAt === void 0 ? [] : [def.recoverAt],
16453
+ ...def.stopAt === void 0 ? [] : [def.stopAt]
16454
+ ];
16298
16455
  case "routed":
16299
16456
  return def.fallback ? [def.fallback] : [];
16300
16457
  case "lazy":
@@ -16788,7 +16945,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
16788
16945
  case "expect":
16789
16946
  return `expect(${kid(def.parser)}${def.label !== void 0 ? `, ${JSON.stringify(def.label)}` : ""})`;
16790
16947
  case "scanTo":
16791
- return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}]${def.raw ? ", raw: true" : ""}, orEOF: ${def.orEOF} })`;
16948
+ return `scanTo(${kid(def.sentinel)}, { skip: [${def.skip.map(kid).join(", ")}]${def.raw ? ", raw: true" : ""}, orEOF: ${def.orEOF}${def.recoverAt === void 0 ? "" : `, recoverAt: ${kid(def.recoverAt)}`}${def.stopAt === void 0 ? "" : `, stopAt: ${kid(def.stopAt)}`} })`;
16792
16949
  case "transform": {
16793
16950
  if (def.fnSrc === void 0) throw new Unserializable("transform without fnSrc");
16794
16951
  return `_tf(${kid(def.parser)}, ${JSON.stringify(def.fnSrc)}${def.recognitionOnly ? ", true" : ""})`;
@@ -16968,7 +17125,7 @@ function compileRuleMap(ruleMap, opts = {}) {
16968
17125
  }
16969
17126
 
16970
17127
  // src/version.ts
16971
- var PARSEMAN_VERSION = "0.50.6";
17128
+ var PARSEMAN_VERSION = "0.51.0";
16972
17129
 
16973
17130
  // src/compiler/compile-linkable-table.ts
16974
17131
  function isLocal(rule) {