parseman 0.25.0 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/combinators/choice.d.ts.map +1 -1
  2. package/dist/combinators/completions.d.ts +14 -4
  3. package/dist/combinators/completions.d.ts.map +1 -1
  4. package/dist/combinators/expect.d.ts +2 -0
  5. package/dist/combinators/expect.d.ts.map +1 -1
  6. package/dist/combinators/grammar.d.ts +5 -5
  7. package/dist/combinators/not.d.ts.map +1 -1
  8. package/dist/combinators/parser.d.ts +3 -2
  9. package/dist/combinators/parser.d.ts.map +1 -1
  10. package/dist/combinators/repeat.d.ts.map +1 -1
  11. package/dist/combinators/sequence.d.ts.map +1 -1
  12. package/dist/combinators/trivia-skip.d.ts +1 -0
  13. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  14. package/dist/compiler/codegen.d.ts +9 -4
  15. package/dist/compiler/codegen.d.ts.map +1 -1
  16. package/dist/compiler/scannable-run.d.ts +2 -2
  17. package/dist/compiler/scannable-run.d.ts.map +1 -1
  18. package/dist/cst/capture-buffer.d.ts +1 -0
  19. package/dist/cst/capture-buffer.d.ts.map +1 -1
  20. package/dist/cst/types.d.ts +7 -7
  21. package/dist/cst/types.d.ts.map +1 -1
  22. package/dist/functional/doc.d.ts +37 -2
  23. package/dist/functional/doc.d.ts.map +1 -1
  24. package/dist/functional/run.d.ts +12 -1
  25. package/dist/functional/run.d.ts.map +1 -1
  26. package/dist/index.cjs +540 -211
  27. package/dist/index.cjs.map +4 -4
  28. package/dist/index.d.ts +1 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +540 -211
  31. package/dist/index.js.map +4 -4
  32. package/dist/language-service/index.cjs +776 -0
  33. package/dist/language-service/index.cjs.map +7 -0
  34. package/dist/language-service/index.d.ts +94 -0
  35. package/dist/language-service/index.d.ts.map +1 -0
  36. package/dist/language-service/index.js +753 -0
  37. package/dist/language-service/index.js.map +7 -0
  38. package/dist/plugin/evaluator.d.ts.map +1 -1
  39. package/dist/plugin/index.cjs +603 -145
  40. package/dist/plugin/index.cjs.map +4 -4
  41. package/dist/plugin/index.d.ts +11 -1
  42. package/dist/plugin/index.d.ts.map +1 -1
  43. package/dist/plugin/index.js +603 -145
  44. package/dist/plugin/index.js.map +4 -4
  45. package/dist/recovery/scan.d.ts +54 -0
  46. package/dist/recovery/scan.d.ts.map +1 -0
  47. package/dist/spec/index.cjs.map +1 -1
  48. package/dist/spec/index.js.map +1 -1
  49. package/dist/types.d.ts +60 -6
  50. package/dist/types.d.ts.map +1 -1
  51. package/package.json +6 -1
  52. package/dist/combinators/recover-list.d.ts +0 -41
  53. package/dist/combinators/recover-list.d.ts.map +0 -1
  54. package/dist/combinators/recover.d.ts +0 -20
  55. package/dist/combinators/recover.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -253,7 +253,13 @@ function saveCstMark(ctx) {
253
253
  raw: cstRawLen(ctx),
254
254
  tlog: cstTlLen(ctx),
255
255
  leaves: cstLeavesLen(ctx),
256
- fields: ctx._fields?.length ?? 0
256
+ fields: ctx._fields?.length ?? 0,
257
+ // Speculative rollback must truncate the flat recovery-error sink in lockstep
258
+ // with the CST leaves: a branch that recovered (pushing a ParseError to
259
+ // ctx._errors AND embedding it as a CST child via captureError) then failed
260
+ // must remove BOTH, or the flat error survives as a GHOST that a tree walk
261
+ // can't see (flat run().errors would disagree with the embedded set).
262
+ errors: ctx._errors?.length ?? 0
257
263
  };
258
264
  }
259
265
  function rollbackBufList(b, keyMulti, keySingle, len) {
@@ -269,6 +275,7 @@ function rollbackBufList(b, keyMulti, keySingle, len) {
269
275
  if (len === 0) b[keySingle] = void 0;
270
276
  }
271
277
  function rollbackCstCapture(ctx, mark) {
278
+ if (ctx._errors && mark.errors !== void 0) ctx._errors.length = mark.errors;
272
279
  const b = ctx._cstBuf;
273
280
  if (b) {
274
281
  rollbackBufList(b, "raw", "rawSingle", mark.raw);
@@ -829,12 +836,163 @@ function keywords(words, opts = {}) {
829
836
  };
830
837
  }
831
838
 
839
+ // src/recovery/scan.ts
840
+ function probeAt(sentinel, input, pos, ctx) {
841
+ const s = {
842
+ _probe: ctx._probe,
843
+ _errors: ctx._errors,
844
+ _cstLeaves: ctx._cstLeaves,
845
+ _cstChildren: ctx._cstChildren,
846
+ _cstRawChildren: ctx._cstRawChildren,
847
+ _cstTriviaLog: ctx._cstTriviaLog,
848
+ _triviaLog: ctx._triviaLog,
849
+ _fields: ctx._fields,
850
+ _cstBuf: ctx._cstBuf,
851
+ _tolerant: ctx._tolerant,
852
+ _sync: ctx._sync
853
+ };
854
+ ctx._probe = void 0;
855
+ ctx._errors = void 0;
856
+ ctx._cstLeaves = void 0;
857
+ ctx._cstChildren = void 0;
858
+ ctx._cstRawChildren = void 0;
859
+ ctx._cstTriviaLog = void 0;
860
+ ctx._triviaLog = void 0;
861
+ ctx._fields = void 0;
862
+ ctx._cstBuf = void 0;
863
+ ctx._tolerant = void 0;
864
+ ctx._sync = void 0;
865
+ try {
866
+ return sentinel.parse(input, pos, ctx).ok;
867
+ } finally {
868
+ ctx._probe = s._probe;
869
+ ctx._errors = s._errors;
870
+ ctx._cstLeaves = s._cstLeaves;
871
+ ctx._cstChildren = s._cstChildren;
872
+ ctx._cstRawChildren = s._cstRawChildren;
873
+ ctx._cstTriviaLog = s._cstTriviaLog;
874
+ ctx._triviaLog = s._triviaLog;
875
+ ctx._fields = s._fields;
876
+ ctx._cstBuf = s._cstBuf;
877
+ ctx._tolerant = s._tolerant;
878
+ ctx._sync = s._sync;
879
+ }
880
+ }
881
+ function orSentinel(a, b) {
882
+ if (b === void 0) return a;
883
+ return {
884
+ _tag: "orSentinel",
885
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
886
+ _def: { tag: "unknown" },
887
+ parse(input, pos, ctx) {
888
+ const ra = a.parse(input, pos, ctx);
889
+ if (ra.ok) return ra;
890
+ return b.parse(input, pos, ctx);
891
+ }
892
+ };
893
+ }
894
+ function firstSetSentinel(fs) {
895
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
896
+ const ranges = fs.ranges;
897
+ return {
898
+ _tag: "firstSetSentinel",
899
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
900
+ _def: { tag: "unknown" },
901
+ parse(input, pos) {
902
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
903
+ if (code !== void 0) {
904
+ for (const r of ranges) {
905
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
906
+ }
907
+ }
908
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
909
+ }
910
+ };
911
+ }
912
+ function recoverScan(input, from, ctx, sync, expected) {
913
+ let scanPos = from;
914
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
915
+ const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
916
+ ctx._errors?.push(error);
917
+ return { error, end: scanPos };
918
+ }
919
+ function matchesAt(sentinel, input, pos, ctx) {
920
+ return probeAt(sentinel, input, pos, ctx);
921
+ }
922
+ function captureError(ctx, error) {
923
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
924
+ }
925
+ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
926
+
927
+ // src/combinators/expect.ts
928
+ function deriveExpected(c) {
929
+ const def = c._def;
930
+ switch (def.tag) {
931
+ case "literal":
932
+ return [JSON.stringify(def.value)];
933
+ case "regex":
934
+ return [`/${def.source}/`];
935
+ case "keywords":
936
+ return def.words.map((w) => JSON.stringify(w));
937
+ case "label":
938
+ return [def.label];
939
+ case "choice":
940
+ return def.parsers.flatMap(deriveExpected);
941
+ case "sequence":
942
+ return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
943
+ case "node":
944
+ case "grammar":
945
+ case "trivia":
946
+ case "token":
947
+ case "optional":
948
+ case "many":
949
+ case "oneOrMore":
950
+ case "transform":
951
+ case "not":
952
+ return deriveExpected(def.parser);
953
+ case "lazy": {
954
+ const name = c._ruleName;
955
+ try {
956
+ return deriveExpected(def.thunk());
957
+ } catch {
958
+ return name ? [name] : [];
959
+ }
960
+ }
961
+ default:
962
+ return [];
963
+ }
964
+ }
965
+ function expect(combinator, label2) {
966
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
967
+ const meta = {
968
+ firstSet: combinator._meta.firstSet,
969
+ canMatchNewline: combinator._meta.canMatchNewline,
970
+ isTrivia: false
971
+ };
972
+ return {
973
+ _tag: "expect",
974
+ _meta: meta,
975
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
976
+ parse(input, pos, ctx) {
977
+ const result = combinator.parse(input, pos, ctx);
978
+ if (result.ok) return result;
979
+ const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
980
+ ctx._errors?.push(error);
981
+ if (ctx._tolerant) captureError(ctx, error);
982
+ return { ok: true, value: error, span: { start: pos, end: pos } };
983
+ }
984
+ };
985
+ }
986
+ function isParseError(value) {
987
+ return typeof value === "object" && value !== null && value._tag === "parseError";
988
+ }
989
+
832
990
  // src/combinators/choice.ts
833
991
  function choice(...args) {
834
992
  const parsers = args.map((a) => "gate" in a ? a.combinator : a);
835
993
  const gates = args.map((a) => "gate" in a ? a.gate : null);
836
994
  const hasGates = gates.some((g) => g !== null);
837
- const disjoint = !hasGates && areDisjoint(parsers.map((p) => p._meta.firstSet));
995
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
838
996
  let combined = { kind: "empty" };
839
997
  for (const p of parsers) combined = union(combined, p._meta.firstSet);
840
998
  const meta = {
@@ -873,17 +1031,21 @@ function choice(...args) {
873
1031
  const expected = [];
874
1032
  if (disjoint && pos < input.length) {
875
1033
  const code = input.codePointAt(pos);
876
- let parser2 = code < 128 ? asciiDispatch?.[code] ?? null : null;
877
- if (!parser2) {
878
- for (const p of parsers) {
879
- if (inFirstSet(code, p._meta.firstSet)) {
880
- parser2 = p;
1034
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1035
+ if (idx < 0) {
1036
+ for (let i = 0; i < parsers.length; i++) {
1037
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1038
+ idx = i;
881
1039
  break;
882
1040
  }
883
1041
  }
884
1042
  }
885
- if (parser2) {
886
- const result = parser2.parse(input, pos, ctx);
1043
+ if (idx >= 0) {
1044
+ const gate = gates[idx];
1045
+ if (gate && !gate(ctx.state)) {
1046
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1047
+ }
1048
+ const result = parsers[idx].parse(input, pos, ctx);
887
1049
  if (result.ok) return result;
888
1050
  expected.push(...result.expected);
889
1051
  return { ok: false, expected, span: { start: pos, end: pos } };
@@ -1064,13 +1226,13 @@ function areDisjoint(sets) {
1064
1226
  return true;
1065
1227
  }
1066
1228
  function buildAsciiDispatch(parsers) {
1067
- const table = Array(128).fill(null);
1068
- for (const parser2 of parsers) {
1069
- const fs = parser2._meta.firstSet;
1229
+ const table = Array(128).fill(-1);
1230
+ for (let i = 0; i < parsers.length; i++) {
1231
+ const fs = parsers[i]._meta.firstSet;
1070
1232
  if (fs.kind !== "ranges") continue;
1071
1233
  for (const { lo, hi } of fs.ranges) {
1072
1234
  for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1073
- table[code] = parser2;
1235
+ table[code] = i;
1074
1236
  }
1075
1237
  }
1076
1238
  }
@@ -1215,10 +1377,10 @@ function needsDeferredTriviaCommit(ctx) {
1215
1377
  }
1216
1378
  function saveTriviaMark(ctx) {
1217
1379
  const m = saveCstMark(ctx);
1218
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1380
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1219
1381
  }
1220
1382
  function rollbackTrivia(ctx, mark) {
1221
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields });
1383
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1222
1384
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
1223
1385
  }
1224
1386
  function scanWithLabels(input, cur, ctx) {
@@ -1462,11 +1624,59 @@ function sequence(...parsers) {
1462
1624
  isTrivia: false
1463
1625
  };
1464
1626
  const def = { tag: "sequence", parsers };
1627
+ let followSentinels;
1628
+ function parseTolerant(input, pos, ctx) {
1629
+ followSentinels ??= parsers.map((_, i) => {
1630
+ const fs = parsers.slice(i + 1).reduce(
1631
+ (acc, p) => union(acc, firstSetOf(p)),
1632
+ { kind: "empty" }
1633
+ );
1634
+ return firstSetSentinel(fs);
1635
+ });
1636
+ const values = def.valueUnused ? void 0 : [];
1637
+ let cur = pos;
1638
+ const inheritedSync = ctx._sync;
1639
+ try {
1640
+ for (let i = 0; i < parsers.length; i++) {
1641
+ ctx._sync = followSentinels[i] ?? inheritedSync;
1642
+ if (ctx.trivia && i > 0) {
1643
+ const mark = saveTriviaMark(ctx);
1644
+ let scanEnd;
1645
+ if (needsDeferredTriviaCommit(ctx)) {
1646
+ const scan = scanTrivia(input, cur, ctx);
1647
+ scan.commit();
1648
+ scanEnd = scan.end;
1649
+ } else {
1650
+ scanEnd = advanceTrivia(input, cur, ctx);
1651
+ }
1652
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1653
+ if (!result2.ok) return result2;
1654
+ if (result2.span.end > scanEnd) cur = result2.span.end;
1655
+ else rollbackTrivia(ctx, mark);
1656
+ if (values !== void 0) values.push(result2.value);
1657
+ continue;
1658
+ }
1659
+ const result = parsers[i].parse(input, cur, ctx);
1660
+ if (!result.ok) return result;
1661
+ if (values !== void 0) values.push(result.value);
1662
+ cur = result.span.end;
1663
+ }
1664
+ } finally {
1665
+ ctx._sync = inheritedSync;
1666
+ }
1667
+ return {
1668
+ ok: true,
1669
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1670
+ value: values ?? void 0,
1671
+ span: { start: pos, end: cur }
1672
+ };
1673
+ }
1465
1674
  return {
1466
1675
  _tag: "sequence",
1467
1676
  _meta: meta,
1468
1677
  _def: def,
1469
1678
  parse(input, pos, ctx) {
1679
+ if (ctx._tolerant) return parseTolerant(input, pos, ctx);
1470
1680
  const values = def.valueUnused ? void 0 : [];
1471
1681
  let cur = pos;
1472
1682
  for (let i = 0; i < parsers.length; i++) {
@@ -1525,7 +1735,7 @@ function repItem(combinator, input, cur, ctx) {
1525
1735
  const result = combinator.parse(input, pos, ctx);
1526
1736
  if (!result.ok) {
1527
1737
  rollbackTrivia(ctx, mark);
1528
- return { fail: result };
1738
+ return { fail: result, failPos: pos };
1529
1739
  }
1530
1740
  if (result.span.end === pos) {
1531
1741
  rollbackTrivia(ctx, mark);
@@ -1540,6 +1750,7 @@ function many(combinator) {
1540
1750
  isTrivia: false
1541
1751
  };
1542
1752
  const def = { tag: "many", parser: combinator, min: 0 };
1753
+ let expected;
1543
1754
  return {
1544
1755
  _tag: "many",
1545
1756
  _meta: meta,
@@ -1549,7 +1760,18 @@ function many(combinator) {
1549
1760
  let cur = pos;
1550
1761
  while (cur < input.length) {
1551
1762
  const item = repItem(combinator, input, cur, ctx);
1552
- if (item === "stop" || "fail" in item) break;
1763
+ if (item === "stop") break;
1764
+ if ("fail" in item) {
1765
+ const sync = ctx._tolerant ? ctx._sync : void 0;
1766
+ if (sync === void 0) break;
1767
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
1768
+ expected ??= deriveExpected(combinator);
1769
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
1770
+ if (values !== void 0) values.push(error);
1771
+ captureError(ctx, error);
1772
+ cur = end;
1773
+ continue;
1774
+ }
1553
1775
  if (values !== void 0) values.push(item.value);
1554
1776
  cur = item.end;
1555
1777
  }
@@ -1564,6 +1786,7 @@ function oneOrMore(combinator) {
1564
1786
  isTrivia: false
1565
1787
  };
1566
1788
  const def = { tag: "oneOrMore", parser: combinator, min: 1 };
1789
+ let expected;
1567
1790
  return {
1568
1791
  _tag: "oneOrMore",
1569
1792
  _meta: meta,
@@ -1575,7 +1798,18 @@ function oneOrMore(combinator) {
1575
1798
  let cur = first.span.end;
1576
1799
  while (cur < input.length) {
1577
1800
  const item = repItem(combinator, input, cur, ctx);
1578
- if (item === "stop" || "fail" in item) break;
1801
+ if (item === "stop") break;
1802
+ if ("fail" in item) {
1803
+ const sync = ctx._tolerant ? ctx._sync : void 0;
1804
+ if (sync === void 0) break;
1805
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
1806
+ expected ??= deriveExpected(combinator);
1807
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
1808
+ if (values !== void 0) values.push(error);
1809
+ captureError(ctx, error);
1810
+ cur = end;
1811
+ continue;
1812
+ }
1579
1813
  if (values !== void 0) values.push(item.value);
1580
1814
  cur = item.end;
1581
1815
  }
@@ -1621,15 +1855,29 @@ function sepBy(combinator, separator) {
1621
1855
  canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
1622
1856
  isTrivia: false
1623
1857
  };
1858
+ let expected;
1624
1859
  return {
1625
1860
  _tag: "sepBy",
1626
1861
  _meta: meta,
1627
1862
  _def: { tag: "sepBy", parser: combinator, separator },
1628
1863
  parse(input, pos, ctx) {
1629
1864
  const first = combinator.parse(input, pos, ctx);
1630
- if (!first.ok) return { ok: true, value: [], span: { start: pos, end: pos } };
1631
- const values = [first.value];
1632
- let cur = first.span.end;
1865
+ const values = [];
1866
+ let cur;
1867
+ if (first.ok) {
1868
+ values.push(first.value);
1869
+ cur = first.span.end;
1870
+ } else {
1871
+ const term = ctx._tolerant ? ctx._sync : void 0;
1872
+ if (term === void 0 || matchesAt(term, input, pos, ctx)) {
1873
+ return { ok: true, value: [], span: { start: pos, end: pos } };
1874
+ }
1875
+ expected ??= deriveExpected(combinator);
1876
+ const rec = recoverScan(input, pos, ctx, orSentinel(separator, term), expected);
1877
+ values.push(rec.error);
1878
+ captureError(ctx, rec.error);
1879
+ cur = rec.end;
1880
+ }
1633
1881
  while (cur < input.length) {
1634
1882
  const loopMark = saveTriviaMark(ctx);
1635
1883
  let sepPos = cur;
@@ -1659,6 +1907,15 @@ function sepBy(combinator, separator) {
1659
1907
  }
1660
1908
  const next = combinator.parse(input, nextPos, ctx);
1661
1909
  if (!next.ok) {
1910
+ const term = ctx._tolerant ? ctx._sync : void 0;
1911
+ if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
1912
+ expected ??= deriveExpected(combinator);
1913
+ const rec = recoverScan(input, nextPos, ctx, orSentinel(separator, term), expected);
1914
+ values.push(rec.error);
1915
+ captureError(ctx, rec.error);
1916
+ cur = rec.end;
1917
+ continue;
1918
+ }
1662
1919
  rollbackTrivia(ctx, loopMark);
1663
1920
  break;
1664
1921
  }
@@ -1832,7 +2089,7 @@ function rules(a, b) {
1832
2089
  cache[key] = parser2;
1833
2090
  }
1834
2091
  }
1835
- if (options?.trivia !== void 0) {
2092
+ if (options?.trivia != null) {
1836
2093
  for (const key of Object.keys(definitions)) {
1837
2094
  const rule = cache[key];
1838
2095
  if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
@@ -1862,7 +2119,9 @@ function not(combinator) {
1862
2119
  _meta: meta,
1863
2120
  _def: { tag: "not", parser: combinator },
1864
2121
  parse(input, pos, ctx) {
2122
+ const mark = saveCstMark(ctx);
1865
2123
  const result = combinator.parse(input, pos, ctx);
2124
+ rollbackCstCapture(ctx, mark);
1866
2125
  if (result.ok) {
1867
2126
  return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
1868
2127
  }
@@ -1976,7 +2235,7 @@ function unwrapChild(child) {
1976
2235
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
1977
2236
  }
1978
2237
  function isCstChild(value) {
1979
- return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "error");
2238
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "parseError");
1980
2239
  }
1981
2240
  function missingInferredType() {
1982
2241
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
@@ -2254,65 +2513,6 @@ function token(root) {
2254
2513
  };
2255
2514
  }
2256
2515
 
2257
- // src/combinators/expect.ts
2258
- function deriveExpected(c) {
2259
- const def = c._def;
2260
- switch (def.tag) {
2261
- case "literal":
2262
- return [JSON.stringify(def.value)];
2263
- case "regex":
2264
- return [`/${def.source}/`];
2265
- case "keywords":
2266
- return def.words.map((w) => JSON.stringify(w));
2267
- case "label":
2268
- return [def.label];
2269
- case "choice":
2270
- return def.parsers.flatMap(deriveExpected);
2271
- case "sequence":
2272
- return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
2273
- case "node":
2274
- case "grammar":
2275
- case "trivia":
2276
- case "token":
2277
- case "optional":
2278
- case "many":
2279
- case "oneOrMore":
2280
- case "transform":
2281
- case "not":
2282
- return deriveExpected(def.parser);
2283
- case "lazy": {
2284
- const name = c._ruleName;
2285
- try {
2286
- return deriveExpected(def.thunk());
2287
- } catch {
2288
- return name ? [name] : [];
2289
- }
2290
- }
2291
- default:
2292
- return [];
2293
- }
2294
- }
2295
- function expect(combinator, label2) {
2296
- const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
2297
- const meta = {
2298
- firstSet: combinator._meta.firstSet,
2299
- canMatchNewline: combinator._meta.canMatchNewline,
2300
- isTrivia: false
2301
- };
2302
- return {
2303
- _tag: "expect",
2304
- _meta: meta,
2305
- _def: { tag: "expect", parser: combinator, label: label2, expected },
2306
- parse(input, pos, ctx) {
2307
- const result = combinator.parse(input, pos, ctx);
2308
- if (result.ok) return result;
2309
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
2310
- ctx._errors?.push(error);
2311
- return { ok: true, value: error, span: { start: pos, end: pos } };
2312
- }
2313
- };
2314
- }
2315
-
2316
2516
  // src/compiler/scannable-run.ts
2317
2517
  var BACKSLASH = 92;
2318
2518
  var META = new Set("()[]{}*+?|^$.".split(""));
@@ -2384,20 +2584,39 @@ function parseSeqParts(source) {
2384
2584
  lit = [];
2385
2585
  }
2386
2586
  };
2387
- const runFrom = (ranges, negated, q) => {
2587
+ const braceQuant = (at) => {
2588
+ const close = source.indexOf("}", at);
2589
+ if (close === -1) return null;
2590
+ const body = source.slice(at + 1, close);
2591
+ const m = /^(\d+)(,(\d*)?)?$/.exec(body);
2592
+ if (!m) return null;
2593
+ const min = Number(m[1]);
2594
+ const max = m[2] === void 0 ? min : m[3] ? Number(m[3]) : Infinity;
2595
+ if (max < 1 || min > max) return null;
2596
+ return { min, max, len: close - at + 1 };
2597
+ };
2598
+ const runFrom = (ranges, negated, at) => {
2599
+ const q = source[at];
2388
2600
  if (q === "+") {
2389
- parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
2601
+ parts.push({ part: "run", ranges, negated, min: 1, max: Infinity });
2390
2602
  return 1;
2391
2603
  }
2392
2604
  if (q === "*") {
2393
- parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
2605
+ parts.push({ part: "run", ranges, negated, min: 0, max: Infinity });
2394
2606
  return 1;
2395
2607
  }
2396
2608
  if (q === "?") {
2397
- parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
2609
+ parts.push({ part: "run", ranges, negated, min: 0, max: 1 });
2398
2610
  return 1;
2399
2611
  }
2400
- parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
2612
+ if (q === "{") {
2613
+ const b = braceQuant(at);
2614
+ if (b) {
2615
+ parts.push({ part: "run", ranges, negated, min: b.min, max: b.max });
2616
+ return b.len;
2617
+ }
2618
+ }
2619
+ parts.push({ part: "run", ranges, negated, min: 1, max: 1 });
2401
2620
  return 0;
2402
2621
  };
2403
2622
  let i = 0;
@@ -2422,7 +2641,7 @@ function parseSeqParts(source) {
2422
2641
  if (!ranges) return null;
2423
2642
  flush();
2424
2643
  i = k + 1;
2425
- i += runFrom(ranges, neg, source[i]);
2644
+ i += runFrom(ranges, neg, i);
2426
2645
  continue;
2427
2646
  }
2428
2647
  if (ch === "\\") {
@@ -2430,7 +2649,7 @@ function parseSeqParts(source) {
2430
2649
  if (e === "d" || e === "w" || e === "s") {
2431
2650
  flush();
2432
2651
  i += 2;
2433
- i += runFrom(shorthandRanges(e), false, source[i]);
2652
+ i += runFrom(shorthandRanges(e), false, i);
2434
2653
  continue;
2435
2654
  }
2436
2655
  const uni = readUnicodeEscape(source, i);
@@ -2519,7 +2738,7 @@ function seqIsUnambiguous(parts) {
2519
2738
  let hasMandatory = false;
2520
2739
  for (let k = 0; k < parts.length; k++) {
2521
2740
  const p = parts[k];
2522
- const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
2741
+ const mandatory = p.part === "lit" ? !p.optional : p.min >= 1;
2523
2742
  if (mandatory) hasMandatory = true;
2524
2743
  if (p.part === "group") {
2525
2744
  const exposure = groupPartExposure(p);
@@ -2530,9 +2749,8 @@ function seqIsUnambiguous(parts) {
2530
2749
  if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
2531
2750
  }
2532
2751
  }
2533
- const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
2534
- const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
2535
- if (skippableSingle || isUnboundedRepeat) {
2752
+ const needsDisjoint = p.part === "lit" ? p.optional : p.part === "run" ? p.max > p.min : p.unbounded || p.min === 0;
2753
+ if (needsDisjoint) {
2536
2754
  const rest = seqFirstAccept(parts.slice(k + 1));
2537
2755
  if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
2538
2756
  }
@@ -2589,8 +2807,8 @@ function fixedClassSeq(shape) {
2589
2807
  if (p.part === "lit") {
2590
2808
  if (p.optional) return null;
2591
2809
  for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
2592
- } else if (p.part === "run" && !p.unbounded && p.min === 1) {
2593
- out.push({ ranges: p.ranges, negated: p.negated });
2810
+ } else if (p.part === "run" && p.max === p.min && Number.isFinite(p.max)) {
2811
+ for (let c = 0; c < p.max; c++) out.push({ ranges: p.ranges, negated: p.negated });
2594
2812
  } else {
2595
2813
  return null;
2596
2814
  }
@@ -2642,7 +2860,7 @@ function trailingBacktrackClass(shape) {
2642
2860
  const last = shape.parts[shape.parts.length - 1];
2643
2861
  if (last.part === "lit") return null;
2644
2862
  if (last.part === "run") {
2645
- if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
2863
+ if (last.max > last.min) return { ranges: last.ranges, negated: last.negated };
2646
2864
  return null;
2647
2865
  }
2648
2866
  return groupPartExposure(last);
@@ -2779,7 +2997,7 @@ function partFirstAccept(part) {
2779
2997
  function seqFirstAccept(parts) {
2780
2998
  const sets = [];
2781
2999
  for (const p of parts) {
2782
- const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
3000
+ const mandatory = p.part === "lit" ? !p.optional : p.min >= 1;
2783
3001
  sets.push(partFirstAccept(p));
2784
3002
  if (mandatory) break;
2785
3003
  }
@@ -3194,7 +3412,13 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
3194
3412
  lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
3195
3413
  lines.push(`${ind2} ${endV2} += ${L}`);
3196
3414
  }
3197
- } else if (p.part === "run" && !p.unbounded) {
3415
+ } else if (p.part === "run" && p.max === Infinity) {
3416
+ const s = mint("_s");
3417
+ lines.push(`${ind2} const ${s} = ${endV2}`);
3418
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
3419
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
3420
+ else if (p.min > 1) lines.push(`${ind2} if (${endV2} - ${s} < ${p.min}) break`);
3421
+ } else if (p.part === "run" && p.max === 1) {
3198
3422
  if (p.min === 1) {
3199
3423
  lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
3200
3424
  lines.push(`${ind2} ${endV2}++`);
@@ -3204,8 +3428,8 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
3204
3428
  } else if (p.part === "run") {
3205
3429
  const s = mint("_s");
3206
3430
  lines.push(`${ind2} const ${s} = ${endV2}`);
3207
- lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
3208
- if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
3431
+ lines.push(`${ind2} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
3432
+ if (p.min > 0) lines.push(`${ind2} if (${endV2} - ${s} < ${p.min}) break`);
3209
3433
  } else {
3210
3434
  const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
3211
3435
  if (!p.unbounded) {
@@ -3664,21 +3888,29 @@ function hoistExpected(ctx, constArrSource) {
3664
3888
  }
3665
3889
  return name;
3666
3890
  }
3891
+ function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
3892
+ if (!ctx.recovery) return "";
3893
+ const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
3894
+ const pb = v(ctx, "_pb");
3895
+ return `if (_ctx._probe !== undefined && ${posExpr} <= _ctx._probe.offset) { const ${pb} = _ctx._probe.best; if (${pb} === null || ${posExpr} > ${pb}.span.start) _ctx._probe.best = { ok: false, expected: [...${fx}], span: { start: ${posExpr}, end: ${posExpr} } }; else if (${posExpr} === ${pb}.span.start) _ctx._probe.best = { ok: false, expected: [...${pb}.expected, ...${fx}], span: { start: ${posExpr}, end: ${posExpr} } } } `;
3896
+ }
3667
3897
  function failBody(ctx, expected, posExpr) {
3898
+ const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
3668
3899
  if (ctx.failLabel) {
3669
- if (!ctx.recordFail) return `break ${ctx.failLabel}`;
3670
- return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
3900
+ if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3901
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
3671
3902
  }
3672
- return failReturn(expected, posExpr);
3903
+ return probe + failReturn(expected, posExpr);
3673
3904
  }
3674
3905
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
3906
+ const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
3675
3907
  if (ctx.failLabel) {
3676
- if (!ctx.recordFail) return `break ${ctx.failLabel}`;
3908
+ if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3677
3909
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
3678
- return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
3910
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
3679
3911
  }
3680
- if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
3681
- return failReturnArr(expectedArr, posExpr);
3912
+ if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
3913
+ return probe + failReturnArr(expectedArr, posExpr);
3682
3914
  }
3683
3915
  function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
3684
3916
  return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
@@ -4178,6 +4410,10 @@ function emitRegex(def, ctx, pos) {
4178
4410
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
4179
4411
  return { stmts, valueVar: vv, endVar };
4180
4412
  }
4413
+ function syncSentinelExpr(fs) {
4414
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
4415
+ return `_ctx._rec.sentinel({ kind: 'ranges', ranges: ${JSON.stringify(fs.ranges)} })`;
4416
+ }
4181
4417
  function emitSeqValues(def, ctx, pos) {
4182
4418
  const startV = v(ctx, "_start");
4183
4419
  const curV = v(ctx, "_cur");
@@ -4186,7 +4422,17 @@ function emitSeqValues(def, ctx, pos) {
4186
4422
  `${ind(ctx)}let ${curV} = ${pos}`
4187
4423
  ];
4188
4424
  const valueVars = [];
4425
+ const syncInV = ctx.recovery ? v(ctx, "_syncIn") : "";
4426
+ if (ctx.recovery) stmts.push(`${ind(ctx)}const ${syncInV} = _ctx._sync`);
4427
+ const publishSync = (i) => {
4428
+ if (!ctx.recovery) return "";
4429
+ const fs = def.parsers.slice(i + 1).reduce((acc, p) => union(acc, firstSetOf(p)), { kind: "empty" });
4430
+ const ref2 = syncSentinelExpr(fs);
4431
+ return `${ind(ctx)}if (_ctx._tolerant) _ctx._sync = ${ref2 ?? syncInV}`;
4432
+ };
4189
4433
  for (let i = 0; i < def.parsers.length; i++) {
4434
+ const syncPub = publishSync(i);
4435
+ if (syncPub) stmts.push(syncPub);
4190
4436
  if (i > 0 && ctx.activeTrivia) {
4191
4437
  if (ctx.capturing) {
4192
4438
  const capFn = ensureTriviaCaptureFn(ctx);
@@ -4211,7 +4457,17 @@ function emitSeqValues(def, ctx, pos) {
4211
4457
  continue;
4212
4458
  } else {
4213
4459
  const trivFn = ensureTriviaFn(ctx);
4214
- stmts.push(`${ind(ctx)}${curV} = ${trivFn}(input, ${curV}, _ctx)`);
4460
+ const scanEndV = v(ctx, "_sne");
4461
+ stmts.push(`${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx)`);
4462
+ const r2 = emit(def.parsers[i], ctx, scanEndV);
4463
+ stmts.push(...r2.stmts);
4464
+ const endAfterV = v(ctx, "_sea");
4465
+ stmts.push(
4466
+ `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
4467
+ `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}`
4468
+ );
4469
+ valueVars.push(r2.valueVar);
4470
+ continue;
4215
4471
  }
4216
4472
  }
4217
4473
  const r = emit(def.parsers[i], ctx, curV);
@@ -4289,6 +4545,21 @@ function emitDisjointArm(p, ctx, pos, valV, endV) {
4289
4545
  `${ind(ctx)}${endV} = ${endVar}`
4290
4546
  ];
4291
4547
  }
4548
+ function emitDisjointArmGated(def, i, ctx, pos, valV, endV) {
4549
+ const p = def.parsers[i];
4550
+ const gate = def.gates[i];
4551
+ if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV);
4552
+ const gateIdx = ctx.mapFns.length;
4553
+ ctx.mapFns.push(gate);
4554
+ ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4555
+ const gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4556
+ const stmts = [`${ind(ctx)}if (${gateCond}) {`];
4557
+ ctx.indent++;
4558
+ stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
4559
+ ctx.indent--;
4560
+ stmts.push(`${ind(ctx)}} else { ${failArrBody(ctx, deriveExpectedArr([p]), pos)} }`);
4561
+ return stmts;
4562
+ }
4292
4563
  function emitChoice(def, ctx, pos) {
4293
4564
  const allExpected = deriveExpectedArr(def.parsers);
4294
4565
  if (def.disjoint) {
@@ -4304,12 +4575,11 @@ function emitChoice(def, ctx, pos) {
4304
4575
  stmts.push(`${ind(ctx)}switch (${codeV}) {`);
4305
4576
  ctx.indent++;
4306
4577
  for (let i = 0; i < def.parsers.length; i++) {
4307
- const p = def.parsers[i];
4308
4578
  for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
4309
4579
  stmts.push(`${ind(ctx)}{`);
4310
4580
  ctx.indent++;
4311
4581
  stmts.push(
4312
- ...emitDisjointArm(p, ctx, pos, valV, endV),
4582
+ ...emitDisjointArmGated(def, i, ctx, pos, valV, endV),
4313
4583
  `${ind(ctx)}break`
4314
4584
  );
4315
4585
  ctx.indent--;
@@ -4321,13 +4591,14 @@ function emitChoice(def, ctx, pos) {
4321
4591
  return { stmts, valueVar: valV, endVar: endV };
4322
4592
  }
4323
4593
  let first = true;
4324
- for (const p of def.parsers) {
4594
+ for (let i = 0; i < def.parsers.length; i++) {
4595
+ const p = def.parsers[i];
4325
4596
  const cond = firstSetCond(codeV, p._meta.firstSet);
4326
4597
  const kw = first ? "if" : "else if";
4327
4598
  first = false;
4328
4599
  stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
4329
4600
  ctx.indent++;
4330
- stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
4601
+ stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV));
4331
4602
  ctx.indent--;
4332
4603
  stmts.push(`${ind(ctx)}}`);
4333
4604
  }
@@ -4441,7 +4712,7 @@ function emitFirstMatch(def, ctx, pos) {
4441
4712
  if (gate) {
4442
4713
  const gateIdx = ctx.mapFns.length;
4443
4714
  ctx.mapFns.push(gate);
4444
- ctx.mapFnSrcs.push(null);
4715
+ ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4445
4716
  gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4446
4717
  }
4447
4718
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
@@ -4546,6 +4817,8 @@ function emitMany(def, ctx, pos) {
4546
4817
  ...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
4547
4818
  `${ind(ctx)}let ${curV} = ${pos}`
4548
4819
  ];
4820
+ const mySyncV = ctx.recovery ? v(ctx, "_mysy") : "";
4821
+ if (ctx.recovery) stmts.push(`${ind(ctx)}const ${mySyncV} = _ctx._sync`);
4549
4822
  if (def.min === 1) {
4550
4823
  const firstR = emit(def.parser, ctx, curV);
4551
4824
  stmts.push(...firstR.stmts);
@@ -4579,10 +4852,26 @@ function emitMany(def, ctx, pos) {
4579
4852
  }
4580
4853
  }
4581
4854
  const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
4582
- stmts.push(
4583
- ...iterStmts,
4584
- `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
4585
- );
4855
+ stmts.push(...iterStmts);
4856
+ if (ctx.recovery) {
4857
+ const rrV = v(ctx, "_rr");
4858
+ const exp = hoistExpected(ctx, deriveExpectedArr([def.parser]));
4859
+ stmts.push(
4860
+ `${ind(ctx)}if (!${iterOk}) {`,
4861
+ `${ind(ctx)} ${rollback}if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPos}, _ctx)) {`,
4862
+ `${ind(ctx)} const ${rrV} = _ctx._rec.scan(input, ${itemPos}, _ctx, ${mySyncV}, ${exp})`,
4863
+ ...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : [],
4864
+ `${ind(ctx)} _ctx._rec.capture(_ctx, ${rrV}.error)`,
4865
+ `${ind(ctx)} ${curV} = ${rrV}.end`,
4866
+ `${ind(ctx)} continue`,
4867
+ `${ind(ctx)} }`,
4868
+ `${ind(ctx)} break`,
4869
+ `${ind(ctx)}}`,
4870
+ `${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`
4871
+ );
4872
+ } else {
4873
+ stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
4874
+ }
4586
4875
  if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
4587
4876
  stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
4588
4877
  ctx.indent--;
@@ -4604,20 +4893,56 @@ function emitOptional(def, ctx, pos) {
4604
4893
  function emitSepBy(_p, def, ctx, pos) {
4605
4894
  const arrV = v(ctx, "_arr");
4606
4895
  const curV = v(ctx, "_cur");
4896
+ const rec = !!ctx.recovery;
4897
+ const mySyncV = rec ? v(ctx, "_mysy") : "";
4898
+ const exp = rec ? hoistExpected(ctx, deriveExpectedArr([def.parser])) : "";
4899
+ const sepSent = rec ? syncSentinelExpr(firstSetOf(def.separator)) : null;
4900
+ const scanSync = sepSent ? `_ctx._rec.or(${sepSent}, ${mySyncV})` : mySyncV;
4607
4901
  const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
4608
4902
  const stmts = [
4609
4903
  `${ind(ctx)}const ${arrV} = []`,
4610
4904
  `${ind(ctx)}let ${curV} = ${pos}`,
4611
- ...firstStmts,
4612
- `${ind(ctx)}if (${firstOk}) {`
4905
+ ...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
4906
+ ...firstStmts
4613
4907
  ];
4614
- ctx.indent++;
4615
- stmts.push(
4616
- `${ind(ctx)}${arrV}.push(${firstVal})`,
4617
- `${ind(ctx)}${curV} = ${firstEnd}`,
4618
- `${ind(ctx)}while (${curV} < input.length) {`
4619
- );
4620
- ctx.indent++;
4908
+ const failItem = (nextOkVar, itemPosVar, rb, originalFail) => {
4909
+ if (!rec) return [originalFail];
4910
+ const rr = v(ctx, "_rr");
4911
+ return [
4912
+ `${ind(ctx)}if (!${nextOkVar}) {`,
4913
+ `${ind(ctx)} if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPosVar}, _ctx)) {`,
4914
+ `${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
4915
+ `${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
4916
+ `${ind(ctx)} }`,
4917
+ `${ind(ctx)} ${rb}break`,
4918
+ `${ind(ctx)}}`
4919
+ ];
4920
+ };
4921
+ if (rec) {
4922
+ const rr0 = v(ctx, "_rr0");
4923
+ const didV = v(ctx, "_did");
4924
+ stmts.push(
4925
+ `${ind(ctx)}let ${didV} = false`,
4926
+ `${ind(ctx)}if (${firstOk}) { ${arrV}.push(${firstVal}); ${curV} = ${firstEnd}; ${didV} = true }`,
4927
+ `${ind(ctx)}else if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${pos}, _ctx)) {`,
4928
+ `${ind(ctx)} const ${rr0} = _ctx._rec.scan(input, ${pos}, _ctx, ${scanSync}, ${exp})`,
4929
+ `${ind(ctx)} ${arrV}.push(${rr0}.error); _ctx._rec.capture(_ctx, ${rr0}.error); ${curV} = ${rr0}.end; ${didV} = true`,
4930
+ `${ind(ctx)}}`,
4931
+ `${ind(ctx)}if (${didV}) {`
4932
+ );
4933
+ ctx.indent++;
4934
+ stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
4935
+ ctx.indent++;
4936
+ } else {
4937
+ stmts.push(`${ind(ctx)}if (${firstOk}) {`);
4938
+ ctx.indent++;
4939
+ stmts.push(
4940
+ `${ind(ctx)}${arrV}.push(${firstVal})`,
4941
+ `${ind(ctx)}${curV} = ${firstEnd}`,
4942
+ `${ind(ctx)}while (${curV} < input.length) {`
4943
+ );
4944
+ ctx.indent++;
4945
+ }
4621
4946
  let sepAtPos = curV;
4622
4947
  if (ctx.activeTrivia) {
4623
4948
  if (ctx.capturing) {
@@ -4643,8 +4968,7 @@ function emitSepBy(_p, def, ctx, pos) {
4643
4968
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
4644
4969
  stmts.push(
4645
4970
  ...nextStmts,
4646
- // item failed → unwind the separator too, back to the end of the last item
4647
- `${ind(ctx)}if (!${nextOk}) { ${rollbackToSep}break }`,
4971
+ ...failItem(nextOk, npV, rollbackToSep, `${ind(ctx)}if (!${nextOk}) { ${rollbackToSep}break }`),
4648
4972
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4649
4973
  `${ind(ctx)}${curV} = ${nextEnd}`
4650
4974
  );
@@ -4660,7 +4984,7 @@ function emitSepBy(_p, def, ctx, pos) {
4660
4984
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
4661
4985
  stmts.push(
4662
4986
  ...nextStmts,
4663
- `${ind(ctx)}if (!${nextOk}) break`,
4987
+ ...failItem(nextOk, npV, "", `${ind(ctx)}if (!${nextOk}) break`),
4664
4988
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4665
4989
  `${ind(ctx)}${curV} = ${nextEnd}`
4666
4990
  );
@@ -4680,7 +5004,7 @@ function emitSepBy(_p, def, ctx, pos) {
4680
5004
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
4681
5005
  stmts.push(
4682
5006
  ...nextStmts,
4683
- `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
5007
+ ...failItem(nextOk, sepEnd, nextRb, `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`),
4684
5008
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4685
5009
  `${ind(ctx)}${curV} = ${nextEnd}`
4686
5010
  );
@@ -4937,7 +5261,7 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
4937
5261
  stmts.push(
4938
5262
  `${i}const ${ndV} = ${finalExpr}`,
4939
5263
  `${i}if (${sc}) ${sc}.push(${ndV})`,
4940
- `${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'error')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
5264
+ `${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
4941
5265
  );
4942
5266
  return { stmts, valueVar: ndV, endVar };
4943
5267
  }
@@ -5027,6 +5351,10 @@ function emitExpect(def, ctx, pos) {
5027
5351
  `${ind0}if (!${okVar}) {`,
5028
5352
  `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
5029
5353
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
5354
+ // Embed the missing-token error as a `parseError` CST child, mirroring the
5355
+ // interpreter (expect.ts) and the list-recovery emit sites — guarded on
5356
+ // `_ctx._tolerant`, under which the driver always installs `_ctx._rec`.
5357
+ `${ind0} if (_ctx._tolerant) _ctx._rec.capture(_ctx, ${errV})`,
5030
5358
  `${ind0} ${valVar} = ${errV}`,
5031
5359
  `${ind0} ${endVar} = ${pos}`,
5032
5360
  `${ind0} ${okVar} = true`,
@@ -5193,7 +5521,7 @@ function emitDispatch(p, ctx, pos) {
5193
5521
  case "guard": {
5194
5522
  const fnIdx = ctx.mapFns.length;
5195
5523
  ctx.mapFns.push(def.predicate);
5196
- ctx.mapFnSrcs.push(null);
5524
+ ctx.mapFnSrcs.push(def.predSrc ?? null);
5197
5525
  const vv = v(ctx);
5198
5526
  return {
5199
5527
  stmts: [
@@ -5208,7 +5536,7 @@ function emitDispatch(p, ctx, pos) {
5208
5536
  const evIdx = ctx.mapFns.length;
5209
5537
  const extra = def.extra;
5210
5538
  ctx.mapFns.push((() => extra));
5211
- ctx.mapFnSrcs.push(null);
5539
+ ctx.mapFnSrcs.push(def.extraSrc !== void 0 ? `() => (${def.extraSrc})` : null);
5212
5540
  const innerParser = def.parser;
5213
5541
  if (!ctx.namedParsers.has(innerParser)) {
5214
5542
  const fnName = `_wcf${ctx.namedParsers.size}`;
@@ -5452,7 +5780,7 @@ function ruleDependencies(ruleMap) {
5452
5780
  }
5453
5781
  return deps;
5454
5782
  }
5455
- function compile(combinator, mapFnSources) {
5783
+ function compile(combinator, mapFnSources, opts) {
5456
5784
  markUnusedValues(combinator);
5457
5785
  const grammarTrivia = combinator._meta.grammarTrivia;
5458
5786
  const ctx = {
@@ -5473,6 +5801,7 @@ function compile(combinator, mapFnSources) {
5473
5801
  triviaFnNames: /* @__PURE__ */ new Map(),
5474
5802
  namedFnDecls: [],
5475
5803
  capturing: hasNodeDef(combinator),
5804
+ recovery: opts?.recovery ?? false,
5476
5805
  lazyUsage: analyzeLazyUsage(combinator),
5477
5806
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5478
5807
  };
@@ -5525,7 +5854,7 @@ function compile(combinator, mapFnSources) {
5525
5854
  parseWithContext(input, parseCtx, pos = 0) {
5526
5855
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
5527
5856
  },
5528
- // Note: collects recover()/expect() errors via _errors. Unlike interpreter
5857
+ // Note: collects expect() errors via _errors. Unlike interpreter
5529
5858
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
5530
5859
  // inlines failures for throughput and deliberately skips _probe bookkeeping.
5531
5860
  // Callers wanting a furthest-position diagnostic detect unconsumed input
@@ -5582,6 +5911,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
5582
5911
  triviaFnNames: /* @__PURE__ */ new Map(),
5583
5912
  namedFnDecls: [],
5584
5913
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
5914
+ recovery: opts?.recovery ?? false,
5585
5915
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5586
5916
  ns,
5587
5917
  deferFirstSetRefs: true,
@@ -6327,46 +6657,13 @@ function withCtx(extra, combinator) {
6327
6657
  };
6328
6658
  }
6329
6659
 
6330
- // src/combinators/recover.ts
6331
- function recover(combinator, sentinel) {
6332
- const meta = {
6333
- firstSet: { kind: "any" },
6334
- canMatchNewline: true,
6335
- isTrivia: false
6336
- };
6337
- const expected = deriveExpected(combinator);
6338
- return {
6339
- _tag: "recover",
6340
- _meta: meta,
6341
- _def: { tag: "recover", parser: combinator, sentinel },
6342
- parse(input, pos, ctx) {
6343
- const result = combinator.parse(input, pos, ctx);
6344
- if (result.ok) return result;
6345
- let scanPos = pos;
6346
- while (scanPos < input.length) {
6347
- if (sentinel.parse(input, scanPos, ctx).ok) break;
6348
- scanPos++;
6349
- }
6350
- const error = {
6351
- _tag: "parseError",
6352
- span: { start: pos, end: scanPos },
6353
- expected
6354
- };
6355
- ctx._errors?.push(error);
6356
- return { ok: true, value: error, span: { start: pos, end: scanPos } };
6357
- }
6358
- };
6359
- }
6360
- function isParseError(value) {
6361
- return typeof value === "object" && value !== null && value._tag === "parseError";
6362
- }
6363
-
6364
6660
  // src/combinators/completions.ts
6365
- function completionsAt(combinator, input, offset) {
6661
+ function completionsAt(target, input, offset, options = {}) {
6366
6662
  const probe = { offset, best: null };
6367
- const ctx = { trackLines: false, _probe: probe };
6368
- const result = combinator.parse(input.slice(0, offset), 0, ctx);
6369
- if (result.ok) return [];
6663
+ const ctx = { trackLines: false, _probe: probe, ...options.tolerant ? { _tolerant: true, _rec: REC } : {} };
6664
+ const sliced = input.slice(0, offset);
6665
+ const result = "parseWithContext" in target ? target.parseWithContext(sliced, ctx, 0) : target.parse(sliced, 0, ctx);
6666
+ if (result.ok) return options.tolerant ? probe.best?.expected ?? [] : [];
6370
6667
  const best = deeperFail(probe.best, result);
6371
6668
  return best?.expected ?? [];
6372
6669
  }
@@ -6376,23 +6673,6 @@ function deeperFail(a, b) {
6376
6673
  return a.span.start >= b.span.start ? a : b;
6377
6674
  }
6378
6675
 
6379
- // src/combinators/recover-list.ts
6380
- function tolerantElement(item, sentinel, until) {
6381
- const el = transform(
6382
- sequence(not(until), recover(item, sentinel)),
6383
- ([, value]) => value
6384
- );
6385
- if (el._def.tag === "transform") el._def.fnSrc = "([, value]) => value";
6386
- return el;
6387
- }
6388
- function sepByRecover(item, separator, until) {
6389
- const sentinel = choice(separator, until);
6390
- return sepBy(tolerantElement(item, sentinel, until), separator);
6391
- }
6392
- function manyRecover(item, until) {
6393
- return many(tolerantElement(item, until, until));
6394
- }
6395
-
6396
6676
  // src/cst/relative-spans.ts
6397
6677
  function relativize(node2, parentBase = 0) {
6398
6678
  return {
@@ -6487,6 +6767,9 @@ function editNode(node2, at, delta, parentBase, parentShift, stats) {
6487
6767
  }
6488
6768
 
6489
6769
  // src/functional/doc.ts
6770
+ function mkCtx(state, build, tolerant) {
6771
+ return tolerant ? { trackLines: false, state, build, _tolerant: true, _rec: REC, _errors: [] } : { trackLines: false, state, build };
6772
+ }
6490
6773
  function asRuleFn(entry) {
6491
6774
  return typeof entry === "function" ? entry : (input, pos, ctx) => entry.parse(input, pos, ctx);
6492
6775
  }
@@ -6593,8 +6876,8 @@ function graftRelative(root, path, newNode, delta) {
6593
6876
  children: newChildren
6594
6877
  };
6595
6878
  }
6596
- function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
6597
- const ctx = { trackLines: false, state: null, build };
6879
+ function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build, tolerant) {
6880
+ const ctx = mkCtx(null, build, tolerant);
6598
6881
  const out = [];
6599
6882
  let pos = reStart;
6600
6883
  let guard2 = 0;
@@ -6618,7 +6901,7 @@ function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
6618
6901
  }
6619
6902
  return pos === reEnd ? out : null;
6620
6903
  }
6621
- function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build) {
6904
+ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build, tolerant) {
6622
6905
  const kids = C.children;
6623
6906
  if (kids.length === 0) return null;
6624
6907
  let h = 0;
@@ -6659,7 +6942,7 @@ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registr
6659
6942
  const reStart = h > 0 ? cStart + kids[h - 1].span.end : cStart;
6660
6943
  const reEnd = cStart + kids[t].span.start + delta;
6661
6944
  if (reEnd < reStart) return null;
6662
- const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build);
6945
+ const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
6663
6946
  if (!middle) return null;
6664
6947
  if (middle.length > 0) {
6665
6948
  const last2 = middle[middle.length - 1];
@@ -6669,7 +6952,7 @@ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registr
6669
6952
  for (const sentinel of [" ", "\uFFFF"]) {
6670
6953
  if (newInput[reEnd] === sentinel) continue;
6671
6954
  const probed = newInput.slice(0, reEnd) + sentinel.repeat(newInput.length - reEnd);
6672
- const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build);
6955
+ const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
6673
6956
  if (!probe || probe.length !== middle.length) return null;
6674
6957
  for (let i = 0; i < middle.length; i++) if (!structurallyEqual(probe[i], middle[i])) return null;
6675
6958
  }
@@ -6697,6 +6980,11 @@ function structurallyEqual(a, b) {
6697
6980
  if (at === "leaf" || at === "trivia") {
6698
6981
  return a.value === b.value;
6699
6982
  }
6983
+ if (at === "parseError") {
6984
+ const ae = a.expected ?? [];
6985
+ const be = b.expected ?? [];
6986
+ return ae.length === be.length && ae.every((x, i) => x === be[i]);
6987
+ }
6700
6988
  if (a.type !== b.type) return false;
6701
6989
  const ac = a.children;
6702
6990
  const bc = b.children;
@@ -6706,13 +6994,13 @@ function structurallyEqual(a, b) {
6706
6994
  }
6707
6995
  return true;
6708
6996
  }
6709
- function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced) {
6997
+ function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced, tolerant) {
6710
6998
  if (!produced.ok) return false;
6711
6999
  if (boundary >= newInput.length) return true;
6712
7000
  for (const sentinel of [" ", "\uFFFF"]) {
6713
7001
  if (newInput[boundary] === sentinel) continue;
6714
7002
  const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
6715
- const ctx = { trackLines: false, state, build };
7003
+ const ctx = mkCtx(state, build, tolerant);
6716
7004
  let r;
6717
7005
  try {
6718
7006
  r = ruleFn(probed, start, ctx);
@@ -6744,8 +7032,9 @@ var ParseDocImpl = class _ParseDocImpl {
6744
7032
  _abs;
6745
7033
  _rel;
6746
7034
  errors;
7035
+ unconsumedFrom;
6747
7036
  input;
6748
- constructor(registry, rootRule, opts, trees, errors, input) {
7037
+ constructor(registry, rootRule, opts, trees, errors, unconsumedFrom, input) {
6749
7038
  this._registry = registry;
6750
7039
  this._fns = {};
6751
7040
  this._reps = /* @__PURE__ */ new Set();
@@ -6758,6 +7047,7 @@ var ParseDocImpl = class _ParseDocImpl {
6758
7047
  this._abs = trees.abs;
6759
7048
  this._rel = trees.rel;
6760
7049
  this.errors = errors;
7050
+ this.unconsumedFrom = unconsumedFrom;
6761
7051
  this.input = input;
6762
7052
  }
6763
7053
  /**
@@ -6780,6 +7070,19 @@ var ParseDocImpl = class _ParseDocImpl {
6780
7070
  if (!rel) throw new Error("spanAt on a failed parse (tree is null)");
6781
7071
  return absoluteSpanCST(rel, path);
6782
7072
  }
7073
+ /**
7074
+ * Wrap a reused (grafted/spliced) RELATIVE tree, recomputing `errors` and
7075
+ * `unconsumedFrom` from it so both stay consistent with the tree across the
7076
+ * edit (the flat errors are recovered errors embedded in the reused subtrees;
7077
+ * unconsumedFrom re-derives trailing junk over the new input).
7078
+ */
7079
+ wrapReuse(newTree, newInput) {
7080
+ const trivia2 = triviaOf(this._registry[this._rootRule], this._opts);
7081
+ const end = newTree.span.end;
7082
+ const errors = collectEmbeddedErrors(newTree, 0, []);
7083
+ const unconsumedFrom = unconsumedAfter(end, newInput, trivia2);
7084
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, errors, unconsumedFrom, newInput);
7085
+ }
6783
7086
  edit(from, to, replacement) {
6784
7087
  const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
6785
7088
  const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
@@ -6801,22 +7104,22 @@ var ParseDocImpl = class _ParseDocImpl {
6801
7104
  const ruleFn = this._fns[node2.type];
6802
7105
  if (!ruleFn) continue;
6803
7106
  if (!(absStart <= from && to <= absEnd)) continue;
6804
- const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
7107
+ const ctx = mkCtx(node2.state, this._opts.build, !!this._opts.tolerant);
6805
7108
  const r = ruleFn(newInput, absStart, ctx);
6806
7109
  if (!r.ok) continue;
6807
7110
  if (r.span.end !== absEnd + delta) continue;
6808
- if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r)) {
7111
+ if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r, !!this._opts.tolerant)) {
6809
7112
  continue;
6810
7113
  }
6811
7114
  const parentBase = absStart - node2.span.start;
6812
7115
  const newRel = relativizeCST(r.value, parentBase);
6813
7116
  if (delta === 0) {
6814
7117
  const newTree2 = replaceAtPath(rebuild, root, path, newRel);
6815
- return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree2 }, [], newInput);
7118
+ return this.wrapReuse(newTree2, newInput);
6816
7119
  }
6817
7120
  if (this._opts.rebuild) return reparse();
6818
7121
  const newTree = graftRelative(root, path, newRel, delta);
6819
- return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, [], newInput);
7122
+ return this.wrapReuse(newTree, newInput);
6820
7123
  }
6821
7124
  if (this._opts.structuralReuse && delta !== 0 && !this._opts.rebuild) {
6822
7125
  const spliceCandidates = [...candidates, { node: root, path: [] }];
@@ -6824,22 +7127,50 @@ var ParseDocImpl = class _ParseDocImpl {
6824
7127
  if (!this._reps.has(node2.type)) continue;
6825
7128
  const { start: cStart, end: cEnd } = absoluteSpanCST(root, path);
6826
7129
  if (!(cStart <= from && to <= cEnd)) continue;
6827
- const spliced = tryListSplice(root, path, node2, cStart, newInput, from, to, delta, this._fns, this._opts.build);
6828
- if (spliced) return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: spliced }, [], newInput);
7130
+ const spliced = tryListSplice(root, path, node2, cStart, newInput, from, to, delta, this._fns, this._opts.build, !!this._opts.tolerant);
7131
+ if (spliced) return this.wrapReuse(spliced, newInput);
6829
7132
  }
6830
7133
  }
6831
7134
  return reparse();
6832
7135
  }
6833
7136
  };
7137
+ function triviaOf(entry, opts) {
7138
+ if (opts.trivia) return opts.trivia;
7139
+ if (entry === void 0 || typeof entry === "function") return void 0;
7140
+ const meta = entry._meta;
7141
+ return meta ? meta.grammarTrivia : void 0;
7142
+ }
7143
+ function unconsumedAfter(end, input, trivia2) {
7144
+ let pos = end;
7145
+ if (trivia2 && pos < input.length) {
7146
+ const t = trivia2.parse(input, pos, { trackLines: false });
7147
+ if (t.ok && t.span.end > pos) pos = t.span.end;
7148
+ }
7149
+ return pos < input.length ? pos : null;
7150
+ }
7151
+ function collectEmbeddedErrors(node2, base, out) {
7152
+ const c = node2;
7153
+ if (!c || typeof c !== "object" || !c.span) return out;
7154
+ const start = base + c.span.start;
7155
+ if (c._tag === "parseError") {
7156
+ out.push({ _tag: "parseError", span: { start, end: base + c.span.end }, expected: c.expected ?? [] });
7157
+ return out;
7158
+ }
7159
+ if (Array.isArray(c.children)) for (const k of c.children) collectEmbeddedErrors(k, start, out);
7160
+ return out;
7161
+ }
6834
7162
  function parseDoc(registry, rootRule, input, opts = {}) {
6835
7163
  const entry = registry[rootRule];
6836
7164
  if (!entry) throw new Error(`No rule '${rootRule}' in registry`);
6837
- const ctx = { trackLines: false, state: opts.state, build: opts.build };
7165
+ const ctx = mkCtx(opts.state, opts.build, !!opts.tolerant);
6838
7166
  const r = asRuleFn(entry)(input, 0, ctx);
6839
7167
  if (r.ok) {
6840
- return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, [], input);
7168
+ const errors = ctx._errors ? [...ctx._errors] : [];
7169
+ const unconsumedFrom = unconsumedAfter(r.span.end, input, triviaOf(entry, opts));
7170
+ return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, errors, unconsumedFrom, input);
6841
7171
  }
6842
- return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [{ ok: false, expected: r.expected, span: r.span }], input);
7172
+ const fail = { _tag: "parseError", span: r.span, expected: r.expected };
7173
+ return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input);
6843
7174
  }
6844
7175
 
6845
7176
  // src/functional/run.ts
@@ -6860,7 +7191,8 @@ function run(entry, input, options = {}) {
6860
7191
  build: options.build,
6861
7192
  state: options.state,
6862
7193
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
6863
- ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {}
7194
+ ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
7195
+ ...options.tolerant ? { _tolerant: true, _rec: REC } : {}
6864
7196
  };
6865
7197
  const r = invoke(entry, input, 0, ctx);
6866
7198
  let unconsumedFrom = null;
@@ -7246,7 +7578,6 @@ export {
7246
7578
  literal,
7247
7579
  makeWord,
7248
7580
  many,
7249
- manyRecover,
7250
7581
  noTrivia,
7251
7582
  node,
7252
7583
  not,
@@ -7256,7 +7587,6 @@ export {
7256
7587
  parse,
7257
7588
  parseDoc,
7258
7589
  parser,
7259
- recover,
7260
7590
  ref,
7261
7591
  regex,
7262
7592
  relativize,
@@ -7265,7 +7595,6 @@ export {
7265
7595
  run,
7266
7596
  scanTo,
7267
7597
  sepBy,
7268
- sepByRecover,
7269
7598
  sequence,
7270
7599
  shiftAbsolute,
7271
7600
  skip,