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.cjs CHANGED
@@ -55,7 +55,6 @@ __export(index_exports, {
55
55
  literal: () => literal,
56
56
  makeWord: () => makeWord,
57
57
  many: () => many,
58
- manyRecover: () => manyRecover,
59
58
  noTrivia: () => noTrivia,
60
59
  node: () => node,
61
60
  not: () => not,
@@ -65,7 +64,6 @@ __export(index_exports, {
65
64
  parse: () => parse,
66
65
  parseDoc: () => parseDoc,
67
66
  parser: () => parser,
68
- recover: () => recover,
69
67
  ref: () => ref,
70
68
  regex: () => regex,
71
69
  relativize: () => relativize,
@@ -74,7 +72,6 @@ __export(index_exports, {
74
72
  run: () => run,
75
73
  scanTo: () => scanTo,
76
74
  sepBy: () => sepBy,
77
- sepByRecover: () => sepByRecover,
78
75
  sequence: () => sequence,
79
76
  shiftAbsolute: () => shiftAbsolute,
80
77
  skip: () => skip,
@@ -344,7 +341,13 @@ function saveCstMark(ctx) {
344
341
  raw: cstRawLen(ctx),
345
342
  tlog: cstTlLen(ctx),
346
343
  leaves: cstLeavesLen(ctx),
347
- fields: ctx._fields?.length ?? 0
344
+ fields: ctx._fields?.length ?? 0,
345
+ // Speculative rollback must truncate the flat recovery-error sink in lockstep
346
+ // with the CST leaves: a branch that recovered (pushing a ParseError to
347
+ // ctx._errors AND embedding it as a CST child via captureError) then failed
348
+ // must remove BOTH, or the flat error survives as a GHOST that a tree walk
349
+ // can't see (flat run().errors would disagree with the embedded set).
350
+ errors: ctx._errors?.length ?? 0
348
351
  };
349
352
  }
350
353
  function rollbackBufList(b, keyMulti, keySingle, len) {
@@ -360,6 +363,7 @@ function rollbackBufList(b, keyMulti, keySingle, len) {
360
363
  if (len === 0) b[keySingle] = void 0;
361
364
  }
362
365
  function rollbackCstCapture(ctx, mark) {
366
+ if (ctx._errors && mark.errors !== void 0) ctx._errors.length = mark.errors;
363
367
  const b = ctx._cstBuf;
364
368
  if (b) {
365
369
  rollbackBufList(b, "raw", "rawSingle", mark.raw);
@@ -920,12 +924,163 @@ function keywords(words, opts = {}) {
920
924
  };
921
925
  }
922
926
 
927
+ // src/recovery/scan.ts
928
+ function probeAt(sentinel, input, pos, ctx) {
929
+ const s = {
930
+ _probe: ctx._probe,
931
+ _errors: ctx._errors,
932
+ _cstLeaves: ctx._cstLeaves,
933
+ _cstChildren: ctx._cstChildren,
934
+ _cstRawChildren: ctx._cstRawChildren,
935
+ _cstTriviaLog: ctx._cstTriviaLog,
936
+ _triviaLog: ctx._triviaLog,
937
+ _fields: ctx._fields,
938
+ _cstBuf: ctx._cstBuf,
939
+ _tolerant: ctx._tolerant,
940
+ _sync: ctx._sync
941
+ };
942
+ ctx._probe = void 0;
943
+ ctx._errors = void 0;
944
+ ctx._cstLeaves = void 0;
945
+ ctx._cstChildren = void 0;
946
+ ctx._cstRawChildren = void 0;
947
+ ctx._cstTriviaLog = void 0;
948
+ ctx._triviaLog = void 0;
949
+ ctx._fields = void 0;
950
+ ctx._cstBuf = void 0;
951
+ ctx._tolerant = void 0;
952
+ ctx._sync = void 0;
953
+ try {
954
+ return sentinel.parse(input, pos, ctx).ok;
955
+ } finally {
956
+ ctx._probe = s._probe;
957
+ ctx._errors = s._errors;
958
+ ctx._cstLeaves = s._cstLeaves;
959
+ ctx._cstChildren = s._cstChildren;
960
+ ctx._cstRawChildren = s._cstRawChildren;
961
+ ctx._cstTriviaLog = s._cstTriviaLog;
962
+ ctx._triviaLog = s._triviaLog;
963
+ ctx._fields = s._fields;
964
+ ctx._cstBuf = s._cstBuf;
965
+ ctx._tolerant = s._tolerant;
966
+ ctx._sync = s._sync;
967
+ }
968
+ }
969
+ function orSentinel(a, b) {
970
+ if (b === void 0) return a;
971
+ return {
972
+ _tag: "orSentinel",
973
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
974
+ _def: { tag: "unknown" },
975
+ parse(input, pos, ctx) {
976
+ const ra = a.parse(input, pos, ctx);
977
+ if (ra.ok) return ra;
978
+ return b.parse(input, pos, ctx);
979
+ }
980
+ };
981
+ }
982
+ function firstSetSentinel(fs) {
983
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
984
+ const ranges = fs.ranges;
985
+ return {
986
+ _tag: "firstSetSentinel",
987
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
988
+ _def: { tag: "unknown" },
989
+ parse(input, pos) {
990
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
991
+ if (code !== void 0) {
992
+ for (const r of ranges) {
993
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
994
+ }
995
+ }
996
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
997
+ }
998
+ };
999
+ }
1000
+ function recoverScan(input, from, ctx, sync, expected) {
1001
+ let scanPos = from;
1002
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1003
+ const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
1004
+ ctx._errors?.push(error);
1005
+ return { error, end: scanPos };
1006
+ }
1007
+ function matchesAt(sentinel, input, pos, ctx) {
1008
+ return probeAt(sentinel, input, pos, ctx);
1009
+ }
1010
+ function captureError(ctx, error) {
1011
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1012
+ }
1013
+ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
1014
+
1015
+ // src/combinators/expect.ts
1016
+ function deriveExpected(c) {
1017
+ const def = c._def;
1018
+ switch (def.tag) {
1019
+ case "literal":
1020
+ return [JSON.stringify(def.value)];
1021
+ case "regex":
1022
+ return [`/${def.source}/`];
1023
+ case "keywords":
1024
+ return def.words.map((w) => JSON.stringify(w));
1025
+ case "label":
1026
+ return [def.label];
1027
+ case "choice":
1028
+ return def.parsers.flatMap(deriveExpected);
1029
+ case "sequence":
1030
+ return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
1031
+ case "node":
1032
+ case "grammar":
1033
+ case "trivia":
1034
+ case "token":
1035
+ case "optional":
1036
+ case "many":
1037
+ case "oneOrMore":
1038
+ case "transform":
1039
+ case "not":
1040
+ return deriveExpected(def.parser);
1041
+ case "lazy": {
1042
+ const name = c._ruleName;
1043
+ try {
1044
+ return deriveExpected(def.thunk());
1045
+ } catch {
1046
+ return name ? [name] : [];
1047
+ }
1048
+ }
1049
+ default:
1050
+ return [];
1051
+ }
1052
+ }
1053
+ function expect(combinator, label2) {
1054
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1055
+ const meta = {
1056
+ firstSet: combinator._meta.firstSet,
1057
+ canMatchNewline: combinator._meta.canMatchNewline,
1058
+ isTrivia: false
1059
+ };
1060
+ return {
1061
+ _tag: "expect",
1062
+ _meta: meta,
1063
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1064
+ parse(input, pos, ctx) {
1065
+ const result = combinator.parse(input, pos, ctx);
1066
+ if (result.ok) return result;
1067
+ const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1068
+ ctx._errors?.push(error);
1069
+ if (ctx._tolerant) captureError(ctx, error);
1070
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1071
+ }
1072
+ };
1073
+ }
1074
+ function isParseError(value) {
1075
+ return typeof value === "object" && value !== null && value._tag === "parseError";
1076
+ }
1077
+
923
1078
  // src/combinators/choice.ts
924
1079
  function choice(...args) {
925
1080
  const parsers = args.map((a) => "gate" in a ? a.combinator : a);
926
1081
  const gates = args.map((a) => "gate" in a ? a.gate : null);
927
1082
  const hasGates = gates.some((g) => g !== null);
928
- const disjoint = !hasGates && areDisjoint(parsers.map((p) => p._meta.firstSet));
1083
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
929
1084
  let combined = { kind: "empty" };
930
1085
  for (const p of parsers) combined = union(combined, p._meta.firstSet);
931
1086
  const meta = {
@@ -964,17 +1119,21 @@ function choice(...args) {
964
1119
  const expected = [];
965
1120
  if (disjoint && pos < input.length) {
966
1121
  const code = input.codePointAt(pos);
967
- let parser2 = code < 128 ? asciiDispatch?.[code] ?? null : null;
968
- if (!parser2) {
969
- for (const p of parsers) {
970
- if (inFirstSet(code, p._meta.firstSet)) {
971
- parser2 = p;
1122
+ let idx = code < 128 ? asciiDispatch[code] : -1;
1123
+ if (idx < 0) {
1124
+ for (let i = 0; i < parsers.length; i++) {
1125
+ if (inFirstSet(code, parsers[i]._meta.firstSet)) {
1126
+ idx = i;
972
1127
  break;
973
1128
  }
974
1129
  }
975
1130
  }
976
- if (parser2) {
977
- const result = parser2.parse(input, pos, ctx);
1131
+ if (idx >= 0) {
1132
+ const gate = gates[idx];
1133
+ if (gate && !gate(ctx.state)) {
1134
+ return { ok: false, expected: deriveExpected(parsers[idx]), span: { start: pos, end: pos } };
1135
+ }
1136
+ const result = parsers[idx].parse(input, pos, ctx);
978
1137
  if (result.ok) return result;
979
1138
  expected.push(...result.expected);
980
1139
  return { ok: false, expected, span: { start: pos, end: pos } };
@@ -1155,13 +1314,13 @@ function areDisjoint(sets) {
1155
1314
  return true;
1156
1315
  }
1157
1316
  function buildAsciiDispatch(parsers) {
1158
- const table = Array(128).fill(null);
1159
- for (const parser2 of parsers) {
1160
- const fs = parser2._meta.firstSet;
1317
+ const table = Array(128).fill(-1);
1318
+ for (let i = 0; i < parsers.length; i++) {
1319
+ const fs = parsers[i]._meta.firstSet;
1161
1320
  if (fs.kind !== "ranges") continue;
1162
1321
  for (const { lo, hi } of fs.ranges) {
1163
1322
  for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) {
1164
- table[code] = parser2;
1323
+ table[code] = i;
1165
1324
  }
1166
1325
  }
1167
1326
  }
@@ -1306,10 +1465,10 @@ function needsDeferredTriviaCommit(ctx) {
1306
1465
  }
1307
1466
  function saveTriviaMark(ctx) {
1308
1467
  const m = saveCstMark(ctx);
1309
- return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1468
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1310
1469
  }
1311
1470
  function rollbackTrivia(ctx, mark) {
1312
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields });
1471
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1313
1472
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
1314
1473
  }
1315
1474
  function scanWithLabels(input, cur, ctx) {
@@ -1553,11 +1712,59 @@ function sequence(...parsers) {
1553
1712
  isTrivia: false
1554
1713
  };
1555
1714
  const def = { tag: "sequence", parsers };
1715
+ let followSentinels;
1716
+ function parseTolerant(input, pos, ctx) {
1717
+ followSentinels ??= parsers.map((_, i) => {
1718
+ const fs = parsers.slice(i + 1).reduce(
1719
+ (acc, p) => union(acc, firstSetOf(p)),
1720
+ { kind: "empty" }
1721
+ );
1722
+ return firstSetSentinel(fs);
1723
+ });
1724
+ const values = def.valueUnused ? void 0 : [];
1725
+ let cur = pos;
1726
+ const inheritedSync = ctx._sync;
1727
+ try {
1728
+ for (let i = 0; i < parsers.length; i++) {
1729
+ ctx._sync = followSentinels[i] ?? inheritedSync;
1730
+ if (ctx.trivia && i > 0) {
1731
+ const mark = saveTriviaMark(ctx);
1732
+ let scanEnd;
1733
+ if (needsDeferredTriviaCommit(ctx)) {
1734
+ const scan = scanTrivia(input, cur, ctx);
1735
+ scan.commit();
1736
+ scanEnd = scan.end;
1737
+ } else {
1738
+ scanEnd = advanceTrivia(input, cur, ctx);
1739
+ }
1740
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1741
+ if (!result2.ok) return result2;
1742
+ if (result2.span.end > scanEnd) cur = result2.span.end;
1743
+ else rollbackTrivia(ctx, mark);
1744
+ if (values !== void 0) values.push(result2.value);
1745
+ continue;
1746
+ }
1747
+ const result = parsers[i].parse(input, cur, ctx);
1748
+ if (!result.ok) return result;
1749
+ if (values !== void 0) values.push(result.value);
1750
+ cur = result.span.end;
1751
+ }
1752
+ } finally {
1753
+ ctx._sync = inheritedSync;
1754
+ }
1755
+ return {
1756
+ ok: true,
1757
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1758
+ value: values ?? void 0,
1759
+ span: { start: pos, end: cur }
1760
+ };
1761
+ }
1556
1762
  return {
1557
1763
  _tag: "sequence",
1558
1764
  _meta: meta,
1559
1765
  _def: def,
1560
1766
  parse(input, pos, ctx) {
1767
+ if (ctx._tolerant) return parseTolerant(input, pos, ctx);
1561
1768
  const values = def.valueUnused ? void 0 : [];
1562
1769
  let cur = pos;
1563
1770
  for (let i = 0; i < parsers.length; i++) {
@@ -1616,7 +1823,7 @@ function repItem(combinator, input, cur, ctx) {
1616
1823
  const result = combinator.parse(input, pos, ctx);
1617
1824
  if (!result.ok) {
1618
1825
  rollbackTrivia(ctx, mark);
1619
- return { fail: result };
1826
+ return { fail: result, failPos: pos };
1620
1827
  }
1621
1828
  if (result.span.end === pos) {
1622
1829
  rollbackTrivia(ctx, mark);
@@ -1631,6 +1838,7 @@ function many(combinator) {
1631
1838
  isTrivia: false
1632
1839
  };
1633
1840
  const def = { tag: "many", parser: combinator, min: 0 };
1841
+ let expected;
1634
1842
  return {
1635
1843
  _tag: "many",
1636
1844
  _meta: meta,
@@ -1640,7 +1848,18 @@ function many(combinator) {
1640
1848
  let cur = pos;
1641
1849
  while (cur < input.length) {
1642
1850
  const item = repItem(combinator, input, cur, ctx);
1643
- if (item === "stop" || "fail" in item) break;
1851
+ if (item === "stop") break;
1852
+ if ("fail" in item) {
1853
+ const sync = ctx._tolerant ? ctx._sync : void 0;
1854
+ if (sync === void 0) break;
1855
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
1856
+ expected ??= deriveExpected(combinator);
1857
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
1858
+ if (values !== void 0) values.push(error);
1859
+ captureError(ctx, error);
1860
+ cur = end;
1861
+ continue;
1862
+ }
1644
1863
  if (values !== void 0) values.push(item.value);
1645
1864
  cur = item.end;
1646
1865
  }
@@ -1655,6 +1874,7 @@ function oneOrMore(combinator) {
1655
1874
  isTrivia: false
1656
1875
  };
1657
1876
  const def = { tag: "oneOrMore", parser: combinator, min: 1 };
1877
+ let expected;
1658
1878
  return {
1659
1879
  _tag: "oneOrMore",
1660
1880
  _meta: meta,
@@ -1666,7 +1886,18 @@ function oneOrMore(combinator) {
1666
1886
  let cur = first.span.end;
1667
1887
  while (cur < input.length) {
1668
1888
  const item = repItem(combinator, input, cur, ctx);
1669
- if (item === "stop" || "fail" in item) break;
1889
+ if (item === "stop") break;
1890
+ if ("fail" in item) {
1891
+ const sync = ctx._tolerant ? ctx._sync : void 0;
1892
+ if (sync === void 0) break;
1893
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
1894
+ expected ??= deriveExpected(combinator);
1895
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
1896
+ if (values !== void 0) values.push(error);
1897
+ captureError(ctx, error);
1898
+ cur = end;
1899
+ continue;
1900
+ }
1670
1901
  if (values !== void 0) values.push(item.value);
1671
1902
  cur = item.end;
1672
1903
  }
@@ -1712,15 +1943,29 @@ function sepBy(combinator, separator) {
1712
1943
  canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
1713
1944
  isTrivia: false
1714
1945
  };
1946
+ let expected;
1715
1947
  return {
1716
1948
  _tag: "sepBy",
1717
1949
  _meta: meta,
1718
1950
  _def: { tag: "sepBy", parser: combinator, separator },
1719
1951
  parse(input, pos, ctx) {
1720
1952
  const first = combinator.parse(input, pos, ctx);
1721
- if (!first.ok) return { ok: true, value: [], span: { start: pos, end: pos } };
1722
- const values = [first.value];
1723
- let cur = first.span.end;
1953
+ const values = [];
1954
+ let cur;
1955
+ if (first.ok) {
1956
+ values.push(first.value);
1957
+ cur = first.span.end;
1958
+ } else {
1959
+ const term = ctx._tolerant ? ctx._sync : void 0;
1960
+ if (term === void 0 || matchesAt(term, input, pos, ctx)) {
1961
+ return { ok: true, value: [], span: { start: pos, end: pos } };
1962
+ }
1963
+ expected ??= deriveExpected(combinator);
1964
+ const rec = recoverScan(input, pos, ctx, orSentinel(separator, term), expected);
1965
+ values.push(rec.error);
1966
+ captureError(ctx, rec.error);
1967
+ cur = rec.end;
1968
+ }
1724
1969
  while (cur < input.length) {
1725
1970
  const loopMark = saveTriviaMark(ctx);
1726
1971
  let sepPos = cur;
@@ -1750,6 +1995,15 @@ function sepBy(combinator, separator) {
1750
1995
  }
1751
1996
  const next = combinator.parse(input, nextPos, ctx);
1752
1997
  if (!next.ok) {
1998
+ const term = ctx._tolerant ? ctx._sync : void 0;
1999
+ if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
2000
+ expected ??= deriveExpected(combinator);
2001
+ const rec = recoverScan(input, nextPos, ctx, orSentinel(separator, term), expected);
2002
+ values.push(rec.error);
2003
+ captureError(ctx, rec.error);
2004
+ cur = rec.end;
2005
+ continue;
2006
+ }
1753
2007
  rollbackTrivia(ctx, loopMark);
1754
2008
  break;
1755
2009
  }
@@ -1923,7 +2177,7 @@ function rules(a, b) {
1923
2177
  cache[key] = parser2;
1924
2178
  }
1925
2179
  }
1926
- if (options?.trivia !== void 0) {
2180
+ if (options?.trivia != null) {
1927
2181
  for (const key of Object.keys(definitions)) {
1928
2182
  const rule = cache[key];
1929
2183
  if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
@@ -1953,7 +2207,9 @@ function not(combinator) {
1953
2207
  _meta: meta,
1954
2208
  _def: { tag: "not", parser: combinator },
1955
2209
  parse(input, pos, ctx) {
2210
+ const mark = saveCstMark(ctx);
1956
2211
  const result = combinator.parse(input, pos, ctx);
2212
+ rollbackCstCapture(ctx, mark);
1957
2213
  if (result.ok) {
1958
2214
  return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
1959
2215
  }
@@ -2067,7 +2323,7 @@ function unwrapChild(child) {
2067
2323
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
2068
2324
  }
2069
2325
  function isCstChild(value) {
2070
- return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "error");
2326
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "parseError");
2071
2327
  }
2072
2328
  function missingInferredType() {
2073
2329
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
@@ -2345,65 +2601,6 @@ function token(root) {
2345
2601
  };
2346
2602
  }
2347
2603
 
2348
- // src/combinators/expect.ts
2349
- function deriveExpected(c) {
2350
- const def = c._def;
2351
- switch (def.tag) {
2352
- case "literal":
2353
- return [JSON.stringify(def.value)];
2354
- case "regex":
2355
- return [`/${def.source}/`];
2356
- case "keywords":
2357
- return def.words.map((w) => JSON.stringify(w));
2358
- case "label":
2359
- return [def.label];
2360
- case "choice":
2361
- return def.parsers.flatMap(deriveExpected);
2362
- case "sequence":
2363
- return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
2364
- case "node":
2365
- case "grammar":
2366
- case "trivia":
2367
- case "token":
2368
- case "optional":
2369
- case "many":
2370
- case "oneOrMore":
2371
- case "transform":
2372
- case "not":
2373
- return deriveExpected(def.parser);
2374
- case "lazy": {
2375
- const name = c._ruleName;
2376
- try {
2377
- return deriveExpected(def.thunk());
2378
- } catch {
2379
- return name ? [name] : [];
2380
- }
2381
- }
2382
- default:
2383
- return [];
2384
- }
2385
- }
2386
- function expect(combinator, label2) {
2387
- const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
2388
- const meta = {
2389
- firstSet: combinator._meta.firstSet,
2390
- canMatchNewline: combinator._meta.canMatchNewline,
2391
- isTrivia: false
2392
- };
2393
- return {
2394
- _tag: "expect",
2395
- _meta: meta,
2396
- _def: { tag: "expect", parser: combinator, label: label2, expected },
2397
- parse(input, pos, ctx) {
2398
- const result = combinator.parse(input, pos, ctx);
2399
- if (result.ok) return result;
2400
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
2401
- ctx._errors?.push(error);
2402
- return { ok: true, value: error, span: { start: pos, end: pos } };
2403
- }
2404
- };
2405
- }
2406
-
2407
2604
  // src/compiler/scannable-run.ts
2408
2605
  var BACKSLASH = 92;
2409
2606
  var META = new Set("()[]{}*+?|^$.".split(""));
@@ -2475,20 +2672,39 @@ function parseSeqParts(source) {
2475
2672
  lit = [];
2476
2673
  }
2477
2674
  };
2478
- const runFrom = (ranges, negated, q) => {
2675
+ const braceQuant = (at) => {
2676
+ const close = source.indexOf("}", at);
2677
+ if (close === -1) return null;
2678
+ const body = source.slice(at + 1, close);
2679
+ const m = /^(\d+)(,(\d*)?)?$/.exec(body);
2680
+ if (!m) return null;
2681
+ const min = Number(m[1]);
2682
+ const max = m[2] === void 0 ? min : m[3] ? Number(m[3]) : Infinity;
2683
+ if (max < 1 || min > max) return null;
2684
+ return { min, max, len: close - at + 1 };
2685
+ };
2686
+ const runFrom = (ranges, negated, at) => {
2687
+ const q = source[at];
2479
2688
  if (q === "+") {
2480
- parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
2689
+ parts.push({ part: "run", ranges, negated, min: 1, max: Infinity });
2481
2690
  return 1;
2482
2691
  }
2483
2692
  if (q === "*") {
2484
- parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
2693
+ parts.push({ part: "run", ranges, negated, min: 0, max: Infinity });
2485
2694
  return 1;
2486
2695
  }
2487
2696
  if (q === "?") {
2488
- parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
2697
+ parts.push({ part: "run", ranges, negated, min: 0, max: 1 });
2489
2698
  return 1;
2490
2699
  }
2491
- parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
2700
+ if (q === "{") {
2701
+ const b = braceQuant(at);
2702
+ if (b) {
2703
+ parts.push({ part: "run", ranges, negated, min: b.min, max: b.max });
2704
+ return b.len;
2705
+ }
2706
+ }
2707
+ parts.push({ part: "run", ranges, negated, min: 1, max: 1 });
2492
2708
  return 0;
2493
2709
  };
2494
2710
  let i = 0;
@@ -2513,7 +2729,7 @@ function parseSeqParts(source) {
2513
2729
  if (!ranges) return null;
2514
2730
  flush();
2515
2731
  i = k + 1;
2516
- i += runFrom(ranges, neg, source[i]);
2732
+ i += runFrom(ranges, neg, i);
2517
2733
  continue;
2518
2734
  }
2519
2735
  if (ch === "\\") {
@@ -2521,7 +2737,7 @@ function parseSeqParts(source) {
2521
2737
  if (e === "d" || e === "w" || e === "s") {
2522
2738
  flush();
2523
2739
  i += 2;
2524
- i += runFrom(shorthandRanges(e), false, source[i]);
2740
+ i += runFrom(shorthandRanges(e), false, i);
2525
2741
  continue;
2526
2742
  }
2527
2743
  const uni = readUnicodeEscape(source, i);
@@ -2610,7 +2826,7 @@ function seqIsUnambiguous(parts) {
2610
2826
  let hasMandatory = false;
2611
2827
  for (let k = 0; k < parts.length; k++) {
2612
2828
  const p = parts[k];
2613
- const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
2829
+ const mandatory = p.part === "lit" ? !p.optional : p.min >= 1;
2614
2830
  if (mandatory) hasMandatory = true;
2615
2831
  if (p.part === "group") {
2616
2832
  const exposure = groupPartExposure(p);
@@ -2621,9 +2837,8 @@ function seqIsUnambiguous(parts) {
2621
2837
  if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
2622
2838
  }
2623
2839
  }
2624
- const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
2625
- const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
2626
- if (skippableSingle || isUnboundedRepeat) {
2840
+ const needsDisjoint = p.part === "lit" ? p.optional : p.part === "run" ? p.max > p.min : p.unbounded || p.min === 0;
2841
+ if (needsDisjoint) {
2627
2842
  const rest = seqFirstAccept(parts.slice(k + 1));
2628
2843
  if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
2629
2844
  }
@@ -2680,8 +2895,8 @@ function fixedClassSeq(shape) {
2680
2895
  if (p.part === "lit") {
2681
2896
  if (p.optional) return null;
2682
2897
  for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
2683
- } else if (p.part === "run" && !p.unbounded && p.min === 1) {
2684
- out.push({ ranges: p.ranges, negated: p.negated });
2898
+ } else if (p.part === "run" && p.max === p.min && Number.isFinite(p.max)) {
2899
+ for (let c = 0; c < p.max; c++) out.push({ ranges: p.ranges, negated: p.negated });
2685
2900
  } else {
2686
2901
  return null;
2687
2902
  }
@@ -2733,7 +2948,7 @@ function trailingBacktrackClass(shape) {
2733
2948
  const last = shape.parts[shape.parts.length - 1];
2734
2949
  if (last.part === "lit") return null;
2735
2950
  if (last.part === "run") {
2736
- if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
2951
+ if (last.max > last.min) return { ranges: last.ranges, negated: last.negated };
2737
2952
  return null;
2738
2953
  }
2739
2954
  return groupPartExposure(last);
@@ -2870,7 +3085,7 @@ function partFirstAccept(part) {
2870
3085
  function seqFirstAccept(parts) {
2871
3086
  const sets = [];
2872
3087
  for (const p of parts) {
2873
- const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
3088
+ const mandatory = p.part === "lit" ? !p.optional : p.min >= 1;
2874
3089
  sets.push(partFirstAccept(p));
2875
3090
  if (mandatory) break;
2876
3091
  }
@@ -3285,7 +3500,13 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
3285
3500
  lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
3286
3501
  lines.push(`${ind2} ${endV2} += ${L}`);
3287
3502
  }
3288
- } else if (p.part === "run" && !p.unbounded) {
3503
+ } else if (p.part === "run" && p.max === Infinity) {
3504
+ const s = mint("_s");
3505
+ lines.push(`${ind2} const ${s} = ${endV2}`);
3506
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
3507
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
3508
+ else if (p.min > 1) lines.push(`${ind2} if (${endV2} - ${s} < ${p.min}) break`);
3509
+ } else if (p.part === "run" && p.max === 1) {
3289
3510
  if (p.min === 1) {
3290
3511
  lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
3291
3512
  lines.push(`${ind2} ${endV2}++`);
@@ -3295,8 +3516,8 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
3295
3516
  } else if (p.part === "run") {
3296
3517
  const s = mint("_s");
3297
3518
  lines.push(`${ind2} const ${s} = ${endV2}`);
3298
- lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
3299
- if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
3519
+ lines.push(`${ind2} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
3520
+ if (p.min > 0) lines.push(`${ind2} if (${endV2} - ${s} < ${p.min}) break`);
3300
3521
  } else {
3301
3522
  const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
3302
3523
  if (!p.unbounded) {
@@ -3755,21 +3976,29 @@ function hoistExpected(ctx, constArrSource) {
3755
3976
  }
3756
3977
  return name;
3757
3978
  }
3979
+ function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
3980
+ if (!ctx.recovery) return "";
3981
+ const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
3982
+ const pb = v(ctx, "_pb");
3983
+ 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} } } } `;
3984
+ }
3758
3985
  function failBody(ctx, expected, posExpr) {
3986
+ const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
3759
3987
  if (ctx.failLabel) {
3760
- if (!ctx.recordFail) return `break ${ctx.failLabel}`;
3761
- return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
3988
+ if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3989
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
3762
3990
  }
3763
- return failReturn(expected, posExpr);
3991
+ return probe + failReturn(expected, posExpr);
3764
3992
  }
3765
3993
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
3994
+ const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
3766
3995
  if (ctx.failLabel) {
3767
- if (!ctx.recordFail) return `break ${ctx.failLabel}`;
3996
+ if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3768
3997
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
3769
- return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
3998
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
3770
3999
  }
3771
- if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
3772
- return failReturnArr(expectedArr, posExpr);
4000
+ if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
4001
+ return probe + failReturnArr(expectedArr, posExpr);
3773
4002
  }
3774
4003
  function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
3775
4004
  return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
@@ -4269,6 +4498,10 @@ function emitRegex(def, ctx, pos) {
4269
4498
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
4270
4499
  return { stmts, valueVar: vv, endVar };
4271
4500
  }
4501
+ function syncSentinelExpr(fs) {
4502
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
4503
+ return `_ctx._rec.sentinel({ kind: 'ranges', ranges: ${JSON.stringify(fs.ranges)} })`;
4504
+ }
4272
4505
  function emitSeqValues(def, ctx, pos) {
4273
4506
  const startV = v(ctx, "_start");
4274
4507
  const curV = v(ctx, "_cur");
@@ -4277,7 +4510,17 @@ function emitSeqValues(def, ctx, pos) {
4277
4510
  `${ind(ctx)}let ${curV} = ${pos}`
4278
4511
  ];
4279
4512
  const valueVars = [];
4513
+ const syncInV = ctx.recovery ? v(ctx, "_syncIn") : "";
4514
+ if (ctx.recovery) stmts.push(`${ind(ctx)}const ${syncInV} = _ctx._sync`);
4515
+ const publishSync = (i) => {
4516
+ if (!ctx.recovery) return "";
4517
+ const fs = def.parsers.slice(i + 1).reduce((acc, p) => union(acc, firstSetOf(p)), { kind: "empty" });
4518
+ const ref2 = syncSentinelExpr(fs);
4519
+ return `${ind(ctx)}if (_ctx._tolerant) _ctx._sync = ${ref2 ?? syncInV}`;
4520
+ };
4280
4521
  for (let i = 0; i < def.parsers.length; i++) {
4522
+ const syncPub = publishSync(i);
4523
+ if (syncPub) stmts.push(syncPub);
4281
4524
  if (i > 0 && ctx.activeTrivia) {
4282
4525
  if (ctx.capturing) {
4283
4526
  const capFn = ensureTriviaCaptureFn(ctx);
@@ -4302,7 +4545,17 @@ function emitSeqValues(def, ctx, pos) {
4302
4545
  continue;
4303
4546
  } else {
4304
4547
  const trivFn = ensureTriviaFn(ctx);
4305
- stmts.push(`${ind(ctx)}${curV} = ${trivFn}(input, ${curV}, _ctx)`);
4548
+ const scanEndV = v(ctx, "_sne");
4549
+ stmts.push(`${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx)`);
4550
+ const r2 = emit(def.parsers[i], ctx, scanEndV);
4551
+ stmts.push(...r2.stmts);
4552
+ const endAfterV = v(ctx, "_sea");
4553
+ stmts.push(
4554
+ `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
4555
+ `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}`
4556
+ );
4557
+ valueVars.push(r2.valueVar);
4558
+ continue;
4306
4559
  }
4307
4560
  }
4308
4561
  const r = emit(def.parsers[i], ctx, curV);
@@ -4380,6 +4633,21 @@ function emitDisjointArm(p, ctx, pos, valV, endV) {
4380
4633
  `${ind(ctx)}${endV} = ${endVar}`
4381
4634
  ];
4382
4635
  }
4636
+ function emitDisjointArmGated(def, i, ctx, pos, valV, endV) {
4637
+ const p = def.parsers[i];
4638
+ const gate = def.gates[i];
4639
+ if (!gate) return emitDisjointArm(p, ctx, pos, valV, endV);
4640
+ const gateIdx = ctx.mapFns.length;
4641
+ ctx.mapFns.push(gate);
4642
+ ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4643
+ const gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4644
+ const stmts = [`${ind(ctx)}if (${gateCond}) {`];
4645
+ ctx.indent++;
4646
+ stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
4647
+ ctx.indent--;
4648
+ stmts.push(`${ind(ctx)}} else { ${failArrBody(ctx, deriveExpectedArr([p]), pos)} }`);
4649
+ return stmts;
4650
+ }
4383
4651
  function emitChoice(def, ctx, pos) {
4384
4652
  const allExpected = deriveExpectedArr(def.parsers);
4385
4653
  if (def.disjoint) {
@@ -4395,12 +4663,11 @@ function emitChoice(def, ctx, pos) {
4395
4663
  stmts.push(`${ind(ctx)}switch (${codeV}) {`);
4396
4664
  ctx.indent++;
4397
4665
  for (let i = 0; i < def.parsers.length; i++) {
4398
- const p = def.parsers[i];
4399
4666
  for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
4400
4667
  stmts.push(`${ind(ctx)}{`);
4401
4668
  ctx.indent++;
4402
4669
  stmts.push(
4403
- ...emitDisjointArm(p, ctx, pos, valV, endV),
4670
+ ...emitDisjointArmGated(def, i, ctx, pos, valV, endV),
4404
4671
  `${ind(ctx)}break`
4405
4672
  );
4406
4673
  ctx.indent--;
@@ -4412,13 +4679,14 @@ function emitChoice(def, ctx, pos) {
4412
4679
  return { stmts, valueVar: valV, endVar: endV };
4413
4680
  }
4414
4681
  let first = true;
4415
- for (const p of def.parsers) {
4682
+ for (let i = 0; i < def.parsers.length; i++) {
4683
+ const p = def.parsers[i];
4416
4684
  const cond = firstSetCond(codeV, p._meta.firstSet);
4417
4685
  const kw = first ? "if" : "else if";
4418
4686
  first = false;
4419
4687
  stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
4420
4688
  ctx.indent++;
4421
- stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
4689
+ stmts.push(...emitDisjointArmGated(def, i, ctx, pos, valV, endV));
4422
4690
  ctx.indent--;
4423
4691
  stmts.push(`${ind(ctx)}}`);
4424
4692
  }
@@ -4532,7 +4800,7 @@ function emitFirstMatch(def, ctx, pos) {
4532
4800
  if (gate) {
4533
4801
  const gateIdx = ctx.mapFns.length;
4534
4802
  ctx.mapFns.push(gate);
4535
- ctx.mapFnSrcs.push(null);
4803
+ ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4536
4804
  gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4537
4805
  }
4538
4806
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
@@ -4637,6 +4905,8 @@ function emitMany(def, ctx, pos) {
4637
4905
  ...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
4638
4906
  `${ind(ctx)}let ${curV} = ${pos}`
4639
4907
  ];
4908
+ const mySyncV = ctx.recovery ? v(ctx, "_mysy") : "";
4909
+ if (ctx.recovery) stmts.push(`${ind(ctx)}const ${mySyncV} = _ctx._sync`);
4640
4910
  if (def.min === 1) {
4641
4911
  const firstR = emit(def.parser, ctx, curV);
4642
4912
  stmts.push(...firstR.stmts);
@@ -4670,10 +4940,26 @@ function emitMany(def, ctx, pos) {
4670
4940
  }
4671
4941
  }
4672
4942
  const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
4673
- stmts.push(
4674
- ...iterStmts,
4675
- `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`
4676
- );
4943
+ stmts.push(...iterStmts);
4944
+ if (ctx.recovery) {
4945
+ const rrV = v(ctx, "_rr");
4946
+ const exp = hoistExpected(ctx, deriveExpectedArr([def.parser]));
4947
+ stmts.push(
4948
+ `${ind(ctx)}if (!${iterOk}) {`,
4949
+ `${ind(ctx)} ${rollback}if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPos}, _ctx)) {`,
4950
+ `${ind(ctx)} const ${rrV} = _ctx._rec.scan(input, ${itemPos}, _ctx, ${mySyncV}, ${exp})`,
4951
+ ...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : [],
4952
+ `${ind(ctx)} _ctx._rec.capture(_ctx, ${rrV}.error)`,
4953
+ `${ind(ctx)} ${curV} = ${rrV}.end`,
4954
+ `${ind(ctx)} continue`,
4955
+ `${ind(ctx)} }`,
4956
+ `${ind(ctx)} break`,
4957
+ `${ind(ctx)}}`,
4958
+ `${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`
4959
+ );
4960
+ } else {
4961
+ stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
4962
+ }
4677
4963
  if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
4678
4964
  stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
4679
4965
  ctx.indent--;
@@ -4695,20 +4981,56 @@ function emitOptional(def, ctx, pos) {
4695
4981
  function emitSepBy(_p, def, ctx, pos) {
4696
4982
  const arrV = v(ctx, "_arr");
4697
4983
  const curV = v(ctx, "_cur");
4984
+ const rec = !!ctx.recovery;
4985
+ const mySyncV = rec ? v(ctx, "_mysy") : "";
4986
+ const exp = rec ? hoistExpected(ctx, deriveExpectedArr([def.parser])) : "";
4987
+ const sepSent = rec ? syncSentinelExpr(firstSetOf(def.separator)) : null;
4988
+ const scanSync = sepSent ? `_ctx._rec.or(${sepSent}, ${mySyncV})` : mySyncV;
4698
4989
  const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
4699
4990
  const stmts = [
4700
4991
  `${ind(ctx)}const ${arrV} = []`,
4701
4992
  `${ind(ctx)}let ${curV} = ${pos}`,
4702
- ...firstStmts,
4703
- `${ind(ctx)}if (${firstOk}) {`
4993
+ ...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
4994
+ ...firstStmts
4704
4995
  ];
4705
- ctx.indent++;
4706
- stmts.push(
4707
- `${ind(ctx)}${arrV}.push(${firstVal})`,
4708
- `${ind(ctx)}${curV} = ${firstEnd}`,
4709
- `${ind(ctx)}while (${curV} < input.length) {`
4710
- );
4711
- ctx.indent++;
4996
+ const failItem = (nextOkVar, itemPosVar, rb, originalFail) => {
4997
+ if (!rec) return [originalFail];
4998
+ const rr = v(ctx, "_rr");
4999
+ return [
5000
+ `${ind(ctx)}if (!${nextOkVar}) {`,
5001
+ `${ind(ctx)} if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPosVar}, _ctx)) {`,
5002
+ `${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
5003
+ `${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
5004
+ `${ind(ctx)} }`,
5005
+ `${ind(ctx)} ${rb}break`,
5006
+ `${ind(ctx)}}`
5007
+ ];
5008
+ };
5009
+ if (rec) {
5010
+ const rr0 = v(ctx, "_rr0");
5011
+ const didV = v(ctx, "_did");
5012
+ stmts.push(
5013
+ `${ind(ctx)}let ${didV} = false`,
5014
+ `${ind(ctx)}if (${firstOk}) { ${arrV}.push(${firstVal}); ${curV} = ${firstEnd}; ${didV} = true }`,
5015
+ `${ind(ctx)}else if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${pos}, _ctx)) {`,
5016
+ `${ind(ctx)} const ${rr0} = _ctx._rec.scan(input, ${pos}, _ctx, ${scanSync}, ${exp})`,
5017
+ `${ind(ctx)} ${arrV}.push(${rr0}.error); _ctx._rec.capture(_ctx, ${rr0}.error); ${curV} = ${rr0}.end; ${didV} = true`,
5018
+ `${ind(ctx)}}`,
5019
+ `${ind(ctx)}if (${didV}) {`
5020
+ );
5021
+ ctx.indent++;
5022
+ stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
5023
+ ctx.indent++;
5024
+ } else {
5025
+ stmts.push(`${ind(ctx)}if (${firstOk}) {`);
5026
+ ctx.indent++;
5027
+ stmts.push(
5028
+ `${ind(ctx)}${arrV}.push(${firstVal})`,
5029
+ `${ind(ctx)}${curV} = ${firstEnd}`,
5030
+ `${ind(ctx)}while (${curV} < input.length) {`
5031
+ );
5032
+ ctx.indent++;
5033
+ }
4712
5034
  let sepAtPos = curV;
4713
5035
  if (ctx.activeTrivia) {
4714
5036
  if (ctx.capturing) {
@@ -4734,8 +5056,7 @@ function emitSepBy(_p, def, ctx, pos) {
4734
5056
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
4735
5057
  stmts.push(
4736
5058
  ...nextStmts,
4737
- // item failed → unwind the separator too, back to the end of the last item
4738
- `${ind(ctx)}if (!${nextOk}) { ${rollbackToSep}break }`,
5059
+ ...failItem(nextOk, npV, rollbackToSep, `${ind(ctx)}if (!${nextOk}) { ${rollbackToSep}break }`),
4739
5060
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4740
5061
  `${ind(ctx)}${curV} = ${nextEnd}`
4741
5062
  );
@@ -4751,7 +5072,7 @@ function emitSepBy(_p, def, ctx, pos) {
4751
5072
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
4752
5073
  stmts.push(
4753
5074
  ...nextStmts,
4754
- `${ind(ctx)}if (!${nextOk}) break`,
5075
+ ...failItem(nextOk, npV, "", `${ind(ctx)}if (!${nextOk}) break`),
4755
5076
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4756
5077
  `${ind(ctx)}${curV} = ${nextEnd}`
4757
5078
  );
@@ -4771,7 +5092,7 @@ function emitSepBy(_p, def, ctx, pos) {
4771
5092
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
4772
5093
  stmts.push(
4773
5094
  ...nextStmts,
4774
- `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
5095
+ ...failItem(nextOk, sepEnd, nextRb, `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`),
4775
5096
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4776
5097
  `${ind(ctx)}${curV} = ${nextEnd}`
4777
5098
  );
@@ -5028,7 +5349,7 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
5028
5349
  stmts.push(
5029
5350
  `${i}const ${ndV} = ${finalExpr}`,
5030
5351
  `${i}if (${sc}) ${sc}.push(${ndV})`,
5031
- `${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} } })`
5352
+ `${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} } })`
5032
5353
  );
5033
5354
  return { stmts, valueVar: ndV, endVar };
5034
5355
  }
@@ -5118,6 +5439,10 @@ function emitExpect(def, ctx, pos) {
5118
5439
  `${ind0}if (!${okVar}) {`,
5119
5440
  `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
5120
5441
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
5442
+ // Embed the missing-token error as a `parseError` CST child, mirroring the
5443
+ // interpreter (expect.ts) and the list-recovery emit sites — guarded on
5444
+ // `_ctx._tolerant`, under which the driver always installs `_ctx._rec`.
5445
+ `${ind0} if (_ctx._tolerant) _ctx._rec.capture(_ctx, ${errV})`,
5121
5446
  `${ind0} ${valVar} = ${errV}`,
5122
5447
  `${ind0} ${endVar} = ${pos}`,
5123
5448
  `${ind0} ${okVar} = true`,
@@ -5284,7 +5609,7 @@ function emitDispatch(p, ctx, pos) {
5284
5609
  case "guard": {
5285
5610
  const fnIdx = ctx.mapFns.length;
5286
5611
  ctx.mapFns.push(def.predicate);
5287
- ctx.mapFnSrcs.push(null);
5612
+ ctx.mapFnSrcs.push(def.predSrc ?? null);
5288
5613
  const vv = v(ctx);
5289
5614
  return {
5290
5615
  stmts: [
@@ -5299,7 +5624,7 @@ function emitDispatch(p, ctx, pos) {
5299
5624
  const evIdx = ctx.mapFns.length;
5300
5625
  const extra = def.extra;
5301
5626
  ctx.mapFns.push((() => extra));
5302
- ctx.mapFnSrcs.push(null);
5627
+ ctx.mapFnSrcs.push(def.extraSrc !== void 0 ? `() => (${def.extraSrc})` : null);
5303
5628
  const innerParser = def.parser;
5304
5629
  if (!ctx.namedParsers.has(innerParser)) {
5305
5630
  const fnName = `_wcf${ctx.namedParsers.size}`;
@@ -5543,7 +5868,7 @@ function ruleDependencies(ruleMap) {
5543
5868
  }
5544
5869
  return deps;
5545
5870
  }
5546
- function compile(combinator, mapFnSources) {
5871
+ function compile(combinator, mapFnSources, opts) {
5547
5872
  markUnusedValues(combinator);
5548
5873
  const grammarTrivia = combinator._meta.grammarTrivia;
5549
5874
  const ctx = {
@@ -5564,6 +5889,7 @@ function compile(combinator, mapFnSources) {
5564
5889
  triviaFnNames: /* @__PURE__ */ new Map(),
5565
5890
  namedFnDecls: [],
5566
5891
  capturing: hasNodeDef(combinator),
5892
+ recovery: opts?.recovery ?? false,
5567
5893
  lazyUsage: analyzeLazyUsage(combinator),
5568
5894
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5569
5895
  };
@@ -5616,7 +5942,7 @@ function compile(combinator, mapFnSources) {
5616
5942
  parseWithContext(input, parseCtx, pos = 0) {
5617
5943
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
5618
5944
  },
5619
- // Note: collects recover()/expect() errors via _errors. Unlike interpreter
5945
+ // Note: collects expect() errors via _errors. Unlike interpreter
5620
5946
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
5621
5947
  // inlines failures for throughput and deliberately skips _probe bookkeeping.
5622
5948
  // Callers wanting a furthest-position diagnostic detect unconsumed input
@@ -5673,6 +5999,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
5673
5999
  triviaFnNames: /* @__PURE__ */ new Map(),
5674
6000
  namedFnDecls: [],
5675
6001
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
6002
+ recovery: opts?.recovery ?? false,
5676
6003
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5677
6004
  ns,
5678
6005
  deferFirstSetRefs: true,
@@ -6418,46 +6745,13 @@ function withCtx(extra, combinator) {
6418
6745
  };
6419
6746
  }
6420
6747
 
6421
- // src/combinators/recover.ts
6422
- function recover(combinator, sentinel) {
6423
- const meta = {
6424
- firstSet: { kind: "any" },
6425
- canMatchNewline: true,
6426
- isTrivia: false
6427
- };
6428
- const expected = deriveExpected(combinator);
6429
- return {
6430
- _tag: "recover",
6431
- _meta: meta,
6432
- _def: { tag: "recover", parser: combinator, sentinel },
6433
- parse(input, pos, ctx) {
6434
- const result = combinator.parse(input, pos, ctx);
6435
- if (result.ok) return result;
6436
- let scanPos = pos;
6437
- while (scanPos < input.length) {
6438
- if (sentinel.parse(input, scanPos, ctx).ok) break;
6439
- scanPos++;
6440
- }
6441
- const error = {
6442
- _tag: "parseError",
6443
- span: { start: pos, end: scanPos },
6444
- expected
6445
- };
6446
- ctx._errors?.push(error);
6447
- return { ok: true, value: error, span: { start: pos, end: scanPos } };
6448
- }
6449
- };
6450
- }
6451
- function isParseError(value) {
6452
- return typeof value === "object" && value !== null && value._tag === "parseError";
6453
- }
6454
-
6455
6748
  // src/combinators/completions.ts
6456
- function completionsAt(combinator, input, offset) {
6749
+ function completionsAt(target, input, offset, options = {}) {
6457
6750
  const probe = { offset, best: null };
6458
- const ctx = { trackLines: false, _probe: probe };
6459
- const result = combinator.parse(input.slice(0, offset), 0, ctx);
6460
- if (result.ok) return [];
6751
+ const ctx = { trackLines: false, _probe: probe, ...options.tolerant ? { _tolerant: true, _rec: REC } : {} };
6752
+ const sliced = input.slice(0, offset);
6753
+ const result = "parseWithContext" in target ? target.parseWithContext(sliced, ctx, 0) : target.parse(sliced, 0, ctx);
6754
+ if (result.ok) return options.tolerant ? probe.best?.expected ?? [] : [];
6461
6755
  const best = deeperFail(probe.best, result);
6462
6756
  return best?.expected ?? [];
6463
6757
  }
@@ -6467,23 +6761,6 @@ function deeperFail(a, b) {
6467
6761
  return a.span.start >= b.span.start ? a : b;
6468
6762
  }
6469
6763
 
6470
- // src/combinators/recover-list.ts
6471
- function tolerantElement(item, sentinel, until) {
6472
- const el = transform(
6473
- sequence(not(until), recover(item, sentinel)),
6474
- ([, value]) => value
6475
- );
6476
- if (el._def.tag === "transform") el._def.fnSrc = "([, value]) => value";
6477
- return el;
6478
- }
6479
- function sepByRecover(item, separator, until) {
6480
- const sentinel = choice(separator, until);
6481
- return sepBy(tolerantElement(item, sentinel, until), separator);
6482
- }
6483
- function manyRecover(item, until) {
6484
- return many(tolerantElement(item, until, until));
6485
- }
6486
-
6487
6764
  // src/cst/relative-spans.ts
6488
6765
  function relativize(node2, parentBase = 0) {
6489
6766
  return {
@@ -6578,6 +6855,9 @@ function editNode(node2, at, delta, parentBase, parentShift, stats) {
6578
6855
  }
6579
6856
 
6580
6857
  // src/functional/doc.ts
6858
+ function mkCtx(state, build, tolerant) {
6859
+ return tolerant ? { trackLines: false, state, build, _tolerant: true, _rec: REC, _errors: [] } : { trackLines: false, state, build };
6860
+ }
6581
6861
  function asRuleFn(entry) {
6582
6862
  return typeof entry === "function" ? entry : (input, pos, ctx) => entry.parse(input, pos, ctx);
6583
6863
  }
@@ -6684,8 +6964,8 @@ function graftRelative(root, path, newNode, delta) {
6684
6964
  children: newChildren
6685
6965
  };
6686
6966
  }
6687
- function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
6688
- const ctx = { trackLines: false, state: null, build };
6967
+ function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build, tolerant) {
6968
+ const ctx = mkCtx(null, build, tolerant);
6689
6969
  const out = [];
6690
6970
  let pos = reStart;
6691
6971
  let guard2 = 0;
@@ -6709,7 +6989,7 @@ function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
6709
6989
  }
6710
6990
  return pos === reEnd ? out : null;
6711
6991
  }
6712
- function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build) {
6992
+ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build, tolerant) {
6713
6993
  const kids = C.children;
6714
6994
  if (kids.length === 0) return null;
6715
6995
  let h = 0;
@@ -6750,7 +7030,7 @@ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registr
6750
7030
  const reStart = h > 0 ? cStart + kids[h - 1].span.end : cStart;
6751
7031
  const reEnd = cStart + kids[t].span.start + delta;
6752
7032
  if (reEnd < reStart) return null;
6753
- const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build);
7033
+ const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
6754
7034
  if (!middle) return null;
6755
7035
  if (middle.length > 0) {
6756
7036
  const last2 = middle[middle.length - 1];
@@ -6760,7 +7040,7 @@ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registr
6760
7040
  for (const sentinel of [" ", "\uFFFF"]) {
6761
7041
  if (newInput[reEnd] === sentinel) continue;
6762
7042
  const probed = newInput.slice(0, reEnd) + sentinel.repeat(newInput.length - reEnd);
6763
- const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build);
7043
+ const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
6764
7044
  if (!probe || probe.length !== middle.length) return null;
6765
7045
  for (let i = 0; i < middle.length; i++) if (!structurallyEqual(probe[i], middle[i])) return null;
6766
7046
  }
@@ -6788,6 +7068,11 @@ function structurallyEqual(a, b) {
6788
7068
  if (at === "leaf" || at === "trivia") {
6789
7069
  return a.value === b.value;
6790
7070
  }
7071
+ if (at === "parseError") {
7072
+ const ae = a.expected ?? [];
7073
+ const be = b.expected ?? [];
7074
+ return ae.length === be.length && ae.every((x, i) => x === be[i]);
7075
+ }
6791
7076
  if (a.type !== b.type) return false;
6792
7077
  const ac = a.children;
6793
7078
  const bc = b.children;
@@ -6797,13 +7082,13 @@ function structurallyEqual(a, b) {
6797
7082
  }
6798
7083
  return true;
6799
7084
  }
6800
- function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced) {
7085
+ function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced, tolerant) {
6801
7086
  if (!produced.ok) return false;
6802
7087
  if (boundary >= newInput.length) return true;
6803
7088
  for (const sentinel of [" ", "\uFFFF"]) {
6804
7089
  if (newInput[boundary] === sentinel) continue;
6805
7090
  const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
6806
- const ctx = { trackLines: false, state, build };
7091
+ const ctx = mkCtx(state, build, tolerant);
6807
7092
  let r;
6808
7093
  try {
6809
7094
  r = ruleFn(probed, start, ctx);
@@ -6835,8 +7120,9 @@ var ParseDocImpl = class _ParseDocImpl {
6835
7120
  _abs;
6836
7121
  _rel;
6837
7122
  errors;
7123
+ unconsumedFrom;
6838
7124
  input;
6839
- constructor(registry, rootRule, opts, trees, errors, input) {
7125
+ constructor(registry, rootRule, opts, trees, errors, unconsumedFrom, input) {
6840
7126
  this._registry = registry;
6841
7127
  this._fns = {};
6842
7128
  this._reps = /* @__PURE__ */ new Set();
@@ -6849,6 +7135,7 @@ var ParseDocImpl = class _ParseDocImpl {
6849
7135
  this._abs = trees.abs;
6850
7136
  this._rel = trees.rel;
6851
7137
  this.errors = errors;
7138
+ this.unconsumedFrom = unconsumedFrom;
6852
7139
  this.input = input;
6853
7140
  }
6854
7141
  /**
@@ -6871,6 +7158,19 @@ var ParseDocImpl = class _ParseDocImpl {
6871
7158
  if (!rel) throw new Error("spanAt on a failed parse (tree is null)");
6872
7159
  return absoluteSpanCST(rel, path);
6873
7160
  }
7161
+ /**
7162
+ * Wrap a reused (grafted/spliced) RELATIVE tree, recomputing `errors` and
7163
+ * `unconsumedFrom` from it so both stay consistent with the tree across the
7164
+ * edit (the flat errors are recovered errors embedded in the reused subtrees;
7165
+ * unconsumedFrom re-derives trailing junk over the new input).
7166
+ */
7167
+ wrapReuse(newTree, newInput) {
7168
+ const trivia2 = triviaOf(this._registry[this._rootRule], this._opts);
7169
+ const end = newTree.span.end;
7170
+ const errors = collectEmbeddedErrors(newTree, 0, []);
7171
+ const unconsumedFrom = unconsumedAfter(end, newInput, trivia2);
7172
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, errors, unconsumedFrom, newInput);
7173
+ }
6874
7174
  edit(from, to, replacement) {
6875
7175
  const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
6876
7176
  const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
@@ -6892,22 +7192,22 @@ var ParseDocImpl = class _ParseDocImpl {
6892
7192
  const ruleFn = this._fns[node2.type];
6893
7193
  if (!ruleFn) continue;
6894
7194
  if (!(absStart <= from && to <= absEnd)) continue;
6895
- const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
7195
+ const ctx = mkCtx(node2.state, this._opts.build, !!this._opts.tolerant);
6896
7196
  const r = ruleFn(newInput, absStart, ctx);
6897
7197
  if (!r.ok) continue;
6898
7198
  if (r.span.end !== absEnd + delta) continue;
6899
- if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r)) {
7199
+ if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r, !!this._opts.tolerant)) {
6900
7200
  continue;
6901
7201
  }
6902
7202
  const parentBase = absStart - node2.span.start;
6903
7203
  const newRel = relativizeCST(r.value, parentBase);
6904
7204
  if (delta === 0) {
6905
7205
  const newTree2 = replaceAtPath(rebuild, root, path, newRel);
6906
- return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree2 }, [], newInput);
7206
+ return this.wrapReuse(newTree2, newInput);
6907
7207
  }
6908
7208
  if (this._opts.rebuild) return reparse();
6909
7209
  const newTree = graftRelative(root, path, newRel, delta);
6910
- return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, [], newInput);
7210
+ return this.wrapReuse(newTree, newInput);
6911
7211
  }
6912
7212
  if (this._opts.structuralReuse && delta !== 0 && !this._opts.rebuild) {
6913
7213
  const spliceCandidates = [...candidates, { node: root, path: [] }];
@@ -6915,22 +7215,50 @@ var ParseDocImpl = class _ParseDocImpl {
6915
7215
  if (!this._reps.has(node2.type)) continue;
6916
7216
  const { start: cStart, end: cEnd } = absoluteSpanCST(root, path);
6917
7217
  if (!(cStart <= from && to <= cEnd)) continue;
6918
- const spliced = tryListSplice(root, path, node2, cStart, newInput, from, to, delta, this._fns, this._opts.build);
6919
- if (spliced) return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: spliced }, [], newInput);
7218
+ const spliced = tryListSplice(root, path, node2, cStart, newInput, from, to, delta, this._fns, this._opts.build, !!this._opts.tolerant);
7219
+ if (spliced) return this.wrapReuse(spliced, newInput);
6920
7220
  }
6921
7221
  }
6922
7222
  return reparse();
6923
7223
  }
6924
7224
  };
7225
+ function triviaOf(entry, opts) {
7226
+ if (opts.trivia) return opts.trivia;
7227
+ if (entry === void 0 || typeof entry === "function") return void 0;
7228
+ const meta = entry._meta;
7229
+ return meta ? meta.grammarTrivia : void 0;
7230
+ }
7231
+ function unconsumedAfter(end, input, trivia2) {
7232
+ let pos = end;
7233
+ if (trivia2 && pos < input.length) {
7234
+ const t = trivia2.parse(input, pos, { trackLines: false });
7235
+ if (t.ok && t.span.end > pos) pos = t.span.end;
7236
+ }
7237
+ return pos < input.length ? pos : null;
7238
+ }
7239
+ function collectEmbeddedErrors(node2, base, out) {
7240
+ const c = node2;
7241
+ if (!c || typeof c !== "object" || !c.span) return out;
7242
+ const start = base + c.span.start;
7243
+ if (c._tag === "parseError") {
7244
+ out.push({ _tag: "parseError", span: { start, end: base + c.span.end }, expected: c.expected ?? [] });
7245
+ return out;
7246
+ }
7247
+ if (Array.isArray(c.children)) for (const k of c.children) collectEmbeddedErrors(k, start, out);
7248
+ return out;
7249
+ }
6925
7250
  function parseDoc(registry, rootRule, input, opts = {}) {
6926
7251
  const entry = registry[rootRule];
6927
7252
  if (!entry) throw new Error(`No rule '${rootRule}' in registry`);
6928
- const ctx = { trackLines: false, state: opts.state, build: opts.build };
7253
+ const ctx = mkCtx(opts.state, opts.build, !!opts.tolerant);
6929
7254
  const r = asRuleFn(entry)(input, 0, ctx);
6930
7255
  if (r.ok) {
6931
- return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, [], input);
7256
+ const errors = ctx._errors ? [...ctx._errors] : [];
7257
+ const unconsumedFrom = unconsumedAfter(r.span.end, input, triviaOf(entry, opts));
7258
+ return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, errors, unconsumedFrom, input);
6932
7259
  }
6933
- return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [{ ok: false, expected: r.expected, span: r.span }], input);
7260
+ const fail = { _tag: "parseError", span: r.span, expected: r.expected };
7261
+ return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input);
6934
7262
  }
6935
7263
 
6936
7264
  // src/functional/run.ts
@@ -6951,7 +7279,8 @@ function run(entry, input, options = {}) {
6951
7279
  build: options.build,
6952
7280
  state: options.state,
6953
7281
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
6954
- ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {}
7282
+ ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
7283
+ ...options.tolerant ? { _tolerant: true, _rec: REC } : {}
6955
7284
  };
6956
7285
  const r = invoke(entry, input, 0, ctx);
6957
7286
  let unconsumedFrom = null;