parseman 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/combinators/completions.d.ts +14 -4
  2. package/dist/combinators/completions.d.ts.map +1 -1
  3. package/dist/combinators/expect.d.ts +2 -0
  4. package/dist/combinators/expect.d.ts.map +1 -1
  5. package/dist/combinators/grammar.d.ts +5 -5
  6. package/dist/combinators/not.d.ts.map +1 -1
  7. package/dist/combinators/parser.d.ts +3 -2
  8. package/dist/combinators/parser.d.ts.map +1 -1
  9. package/dist/combinators/repeat.d.ts.map +1 -1
  10. package/dist/combinators/sequence.d.ts.map +1 -1
  11. package/dist/combinators/trivia-skip.d.ts +1 -0
  12. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  13. package/dist/compiler/codegen.d.ts +9 -4
  14. package/dist/compiler/codegen.d.ts.map +1 -1
  15. package/dist/compiler/scannable-run.d.ts +2 -2
  16. package/dist/compiler/scannable-run.d.ts.map +1 -1
  17. package/dist/cst/capture-buffer.d.ts +1 -0
  18. package/dist/cst/capture-buffer.d.ts.map +1 -1
  19. package/dist/cst/types.d.ts +7 -7
  20. package/dist/cst/types.d.ts.map +1 -1
  21. package/dist/functional/doc.d.ts +37 -2
  22. package/dist/functional/doc.d.ts.map +1 -1
  23. package/dist/functional/run.d.ts +12 -1
  24. package/dist/functional/run.d.ts.map +1 -1
  25. package/dist/index.cjs +505 -195
  26. package/dist/index.cjs.map +4 -4
  27. package/dist/index.d.ts +1 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +505 -195
  30. package/dist/index.js.map +4 -4
  31. package/dist/language-service/index.cjs +776 -0
  32. package/dist/language-service/index.cjs.map +7 -0
  33. package/dist/language-service/index.d.ts +94 -0
  34. package/dist/language-service/index.d.ts.map +1 -0
  35. package/dist/language-service/index.js +753 -0
  36. package/dist/language-service/index.js.map +7 -0
  37. package/dist/plugin/evaluator.d.ts.map +1 -1
  38. package/dist/plugin/index.cjs +568 -129
  39. package/dist/plugin/index.cjs.map +4 -4
  40. package/dist/plugin/index.d.ts +11 -1
  41. package/dist/plugin/index.d.ts.map +1 -1
  42. package/dist/plugin/index.js +568 -129
  43. package/dist/plugin/index.js.map +4 -4
  44. package/dist/recovery/scan.d.ts +54 -0
  45. package/dist/recovery/scan.d.ts.map +1 -0
  46. package/dist/spec/index.cjs.map +1 -1
  47. package/dist/spec/index.js.map +1 -1
  48. package/dist/types.d.ts +60 -6
  49. package/dist/types.d.ts.map +1 -1
  50. package/package.json +6 -1
  51. package/dist/combinators/recover-list.d.ts +0 -41
  52. package/dist/combinators/recover-list.d.ts.map +0 -1
  53. package/dist/combinators/recover.d.ts +0 -20
  54. 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);
@@ -1306,10 +1310,10 @@ function needsDeferredTriviaCommit(ctx) {
1306
1310
  }
1307
1311
  function saveTriviaMark(ctx) {
1308
1312
  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 };
1313
+ return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
1310
1314
  }
1311
1315
  function rollbackTrivia(ctx, mark) {
1312
- rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields });
1316
+ rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
1313
1317
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
1314
1318
  }
1315
1319
  function scanWithLabels(input, cur, ctx) {
@@ -1543,6 +1547,94 @@ function scanBlockComment(input, cur) {
1543
1547
  return close === -1 ? cur : close + 2;
1544
1548
  }
1545
1549
 
1550
+ // src/recovery/scan.ts
1551
+ function probeAt(sentinel, input, pos, ctx) {
1552
+ const s = {
1553
+ _probe: ctx._probe,
1554
+ _errors: ctx._errors,
1555
+ _cstLeaves: ctx._cstLeaves,
1556
+ _cstChildren: ctx._cstChildren,
1557
+ _cstRawChildren: ctx._cstRawChildren,
1558
+ _cstTriviaLog: ctx._cstTriviaLog,
1559
+ _triviaLog: ctx._triviaLog,
1560
+ _fields: ctx._fields,
1561
+ _cstBuf: ctx._cstBuf,
1562
+ _tolerant: ctx._tolerant,
1563
+ _sync: ctx._sync
1564
+ };
1565
+ ctx._probe = void 0;
1566
+ ctx._errors = void 0;
1567
+ ctx._cstLeaves = void 0;
1568
+ ctx._cstChildren = void 0;
1569
+ ctx._cstRawChildren = void 0;
1570
+ ctx._cstTriviaLog = void 0;
1571
+ ctx._triviaLog = void 0;
1572
+ ctx._fields = void 0;
1573
+ ctx._cstBuf = void 0;
1574
+ ctx._tolerant = void 0;
1575
+ ctx._sync = void 0;
1576
+ try {
1577
+ return sentinel.parse(input, pos, ctx).ok;
1578
+ } finally {
1579
+ ctx._probe = s._probe;
1580
+ ctx._errors = s._errors;
1581
+ ctx._cstLeaves = s._cstLeaves;
1582
+ ctx._cstChildren = s._cstChildren;
1583
+ ctx._cstRawChildren = s._cstRawChildren;
1584
+ ctx._cstTriviaLog = s._cstTriviaLog;
1585
+ ctx._triviaLog = s._triviaLog;
1586
+ ctx._fields = s._fields;
1587
+ ctx._cstBuf = s._cstBuf;
1588
+ ctx._tolerant = s._tolerant;
1589
+ ctx._sync = s._sync;
1590
+ }
1591
+ }
1592
+ function orSentinel(a, b) {
1593
+ if (b === void 0) return a;
1594
+ return {
1595
+ _tag: "orSentinel",
1596
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
1597
+ _def: { tag: "unknown" },
1598
+ parse(input, pos, ctx) {
1599
+ const ra = a.parse(input, pos, ctx);
1600
+ if (ra.ok) return ra;
1601
+ return b.parse(input, pos, ctx);
1602
+ }
1603
+ };
1604
+ }
1605
+ function firstSetSentinel(fs) {
1606
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
1607
+ const ranges = fs.ranges;
1608
+ return {
1609
+ _tag: "firstSetSentinel",
1610
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
1611
+ _def: { tag: "unknown" },
1612
+ parse(input, pos) {
1613
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
1614
+ if (code !== void 0) {
1615
+ for (const r of ranges) {
1616
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
1617
+ }
1618
+ }
1619
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
1620
+ }
1621
+ };
1622
+ }
1623
+ function recoverScan(input, from, ctx, sync, expected) {
1624
+ let scanPos = from;
1625
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
1626
+ const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
1627
+ ctx._errors?.push(error);
1628
+ return { error, end: scanPos };
1629
+ }
1630
+ function matchesAt(sentinel, input, pos, ctx) {
1631
+ return probeAt(sentinel, input, pos, ctx);
1632
+ }
1633
+ function captureError(ctx, error) {
1634
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
1635
+ }
1636
+ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
1637
+
1546
1638
  // src/combinators/sequence.ts
1547
1639
  function sequence(...parsers) {
1548
1640
  const meta = {
@@ -1553,11 +1645,59 @@ function sequence(...parsers) {
1553
1645
  isTrivia: false
1554
1646
  };
1555
1647
  const def = { tag: "sequence", parsers };
1648
+ let followSentinels;
1649
+ function parseTolerant(input, pos, ctx) {
1650
+ followSentinels ??= parsers.map((_, i) => {
1651
+ const fs = parsers.slice(i + 1).reduce(
1652
+ (acc, p) => union(acc, firstSetOf(p)),
1653
+ { kind: "empty" }
1654
+ );
1655
+ return firstSetSentinel(fs);
1656
+ });
1657
+ const values = def.valueUnused ? void 0 : [];
1658
+ let cur = pos;
1659
+ const inheritedSync = ctx._sync;
1660
+ try {
1661
+ for (let i = 0; i < parsers.length; i++) {
1662
+ ctx._sync = followSentinels[i] ?? inheritedSync;
1663
+ if (ctx.trivia && i > 0) {
1664
+ const mark = saveTriviaMark(ctx);
1665
+ let scanEnd;
1666
+ if (needsDeferredTriviaCommit(ctx)) {
1667
+ const scan = scanTrivia(input, cur, ctx);
1668
+ scan.commit();
1669
+ scanEnd = scan.end;
1670
+ } else {
1671
+ scanEnd = advanceTrivia(input, cur, ctx);
1672
+ }
1673
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1674
+ if (!result2.ok) return result2;
1675
+ if (result2.span.end > scanEnd) cur = result2.span.end;
1676
+ else rollbackTrivia(ctx, mark);
1677
+ if (values !== void 0) values.push(result2.value);
1678
+ continue;
1679
+ }
1680
+ const result = parsers[i].parse(input, cur, ctx);
1681
+ if (!result.ok) return result;
1682
+ if (values !== void 0) values.push(result.value);
1683
+ cur = result.span.end;
1684
+ }
1685
+ } finally {
1686
+ ctx._sync = inheritedSync;
1687
+ }
1688
+ return {
1689
+ ok: true,
1690
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1691
+ value: values ?? void 0,
1692
+ span: { start: pos, end: cur }
1693
+ };
1694
+ }
1556
1695
  return {
1557
1696
  _tag: "sequence",
1558
1697
  _meta: meta,
1559
1698
  _def: def,
1560
1699
  parse(input, pos, ctx) {
1700
+ if (ctx._tolerant) return parseTolerant(input, pos, ctx);
1561
1701
  const values = def.valueUnused ? void 0 : [];
1562
1702
  let cur = pos;
1563
1703
  for (let i = 0; i < parsers.length; i++) {
@@ -1596,6 +1736,69 @@ function sequence(...parsers) {
1596
1736
  };
1597
1737
  }
1598
1738
 
1739
+ // src/combinators/expect.ts
1740
+ function deriveExpected(c) {
1741
+ const def = c._def;
1742
+ switch (def.tag) {
1743
+ case "literal":
1744
+ return [JSON.stringify(def.value)];
1745
+ case "regex":
1746
+ return [`/${def.source}/`];
1747
+ case "keywords":
1748
+ return def.words.map((w) => JSON.stringify(w));
1749
+ case "label":
1750
+ return [def.label];
1751
+ case "choice":
1752
+ return def.parsers.flatMap(deriveExpected);
1753
+ case "sequence":
1754
+ return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
1755
+ case "node":
1756
+ case "grammar":
1757
+ case "trivia":
1758
+ case "token":
1759
+ case "optional":
1760
+ case "many":
1761
+ case "oneOrMore":
1762
+ case "transform":
1763
+ case "not":
1764
+ return deriveExpected(def.parser);
1765
+ case "lazy": {
1766
+ const name = c._ruleName;
1767
+ try {
1768
+ return deriveExpected(def.thunk());
1769
+ } catch {
1770
+ return name ? [name] : [];
1771
+ }
1772
+ }
1773
+ default:
1774
+ return [];
1775
+ }
1776
+ }
1777
+ function expect(combinator, label2) {
1778
+ const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
1779
+ const meta = {
1780
+ firstSet: combinator._meta.firstSet,
1781
+ canMatchNewline: combinator._meta.canMatchNewline,
1782
+ isTrivia: false
1783
+ };
1784
+ return {
1785
+ _tag: "expect",
1786
+ _meta: meta,
1787
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1788
+ parse(input, pos, ctx) {
1789
+ const result = combinator.parse(input, pos, ctx);
1790
+ if (result.ok) return result;
1791
+ const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1792
+ ctx._errors?.push(error);
1793
+ if (ctx._tolerant) captureError(ctx, error);
1794
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1795
+ }
1796
+ };
1797
+ }
1798
+ function isParseError(value) {
1799
+ return typeof value === "object" && value !== null && value._tag === "parseError";
1800
+ }
1801
+
1599
1802
  // src/combinators/repeat.ts
1600
1803
  function repItem(combinator, input, cur, ctx) {
1601
1804
  const mark = saveTriviaMark(ctx);
@@ -1616,7 +1819,7 @@ function repItem(combinator, input, cur, ctx) {
1616
1819
  const result = combinator.parse(input, pos, ctx);
1617
1820
  if (!result.ok) {
1618
1821
  rollbackTrivia(ctx, mark);
1619
- return { fail: result };
1822
+ return { fail: result, failPos: pos };
1620
1823
  }
1621
1824
  if (result.span.end === pos) {
1622
1825
  rollbackTrivia(ctx, mark);
@@ -1631,6 +1834,7 @@ function many(combinator) {
1631
1834
  isTrivia: false
1632
1835
  };
1633
1836
  const def = { tag: "many", parser: combinator, min: 0 };
1837
+ let expected;
1634
1838
  return {
1635
1839
  _tag: "many",
1636
1840
  _meta: meta,
@@ -1640,7 +1844,18 @@ function many(combinator) {
1640
1844
  let cur = pos;
1641
1845
  while (cur < input.length) {
1642
1846
  const item = repItem(combinator, input, cur, ctx);
1643
- if (item === "stop" || "fail" in item) break;
1847
+ if (item === "stop") break;
1848
+ if ("fail" in item) {
1849
+ const sync = ctx._tolerant ? ctx._sync : void 0;
1850
+ if (sync === void 0) break;
1851
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
1852
+ expected ??= deriveExpected(combinator);
1853
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
1854
+ if (values !== void 0) values.push(error);
1855
+ captureError(ctx, error);
1856
+ cur = end;
1857
+ continue;
1858
+ }
1644
1859
  if (values !== void 0) values.push(item.value);
1645
1860
  cur = item.end;
1646
1861
  }
@@ -1655,6 +1870,7 @@ function oneOrMore(combinator) {
1655
1870
  isTrivia: false
1656
1871
  };
1657
1872
  const def = { tag: "oneOrMore", parser: combinator, min: 1 };
1873
+ let expected;
1658
1874
  return {
1659
1875
  _tag: "oneOrMore",
1660
1876
  _meta: meta,
@@ -1666,7 +1882,18 @@ function oneOrMore(combinator) {
1666
1882
  let cur = first.span.end;
1667
1883
  while (cur < input.length) {
1668
1884
  const item = repItem(combinator, input, cur, ctx);
1669
- if (item === "stop" || "fail" in item) break;
1885
+ if (item === "stop") break;
1886
+ if ("fail" in item) {
1887
+ const sync = ctx._tolerant ? ctx._sync : void 0;
1888
+ if (sync === void 0) break;
1889
+ if (matchesAt(sync, input, item.failPos, ctx)) break;
1890
+ expected ??= deriveExpected(combinator);
1891
+ const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
1892
+ if (values !== void 0) values.push(error);
1893
+ captureError(ctx, error);
1894
+ cur = end;
1895
+ continue;
1896
+ }
1670
1897
  if (values !== void 0) values.push(item.value);
1671
1898
  cur = item.end;
1672
1899
  }
@@ -1712,15 +1939,29 @@ function sepBy(combinator, separator) {
1712
1939
  canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
1713
1940
  isTrivia: false
1714
1941
  };
1942
+ let expected;
1715
1943
  return {
1716
1944
  _tag: "sepBy",
1717
1945
  _meta: meta,
1718
1946
  _def: { tag: "sepBy", parser: combinator, separator },
1719
1947
  parse(input, pos, ctx) {
1720
1948
  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;
1949
+ const values = [];
1950
+ let cur;
1951
+ if (first.ok) {
1952
+ values.push(first.value);
1953
+ cur = first.span.end;
1954
+ } else {
1955
+ const term = ctx._tolerant ? ctx._sync : void 0;
1956
+ if (term === void 0 || matchesAt(term, input, pos, ctx)) {
1957
+ return { ok: true, value: [], span: { start: pos, end: pos } };
1958
+ }
1959
+ expected ??= deriveExpected(combinator);
1960
+ const rec = recoverScan(input, pos, ctx, orSentinel(separator, term), expected);
1961
+ values.push(rec.error);
1962
+ captureError(ctx, rec.error);
1963
+ cur = rec.end;
1964
+ }
1724
1965
  while (cur < input.length) {
1725
1966
  const loopMark = saveTriviaMark(ctx);
1726
1967
  let sepPos = cur;
@@ -1750,6 +1991,15 @@ function sepBy(combinator, separator) {
1750
1991
  }
1751
1992
  const next = combinator.parse(input, nextPos, ctx);
1752
1993
  if (!next.ok) {
1994
+ const term = ctx._tolerant ? ctx._sync : void 0;
1995
+ if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
1996
+ expected ??= deriveExpected(combinator);
1997
+ const rec = recoverScan(input, nextPos, ctx, orSentinel(separator, term), expected);
1998
+ values.push(rec.error);
1999
+ captureError(ctx, rec.error);
2000
+ cur = rec.end;
2001
+ continue;
2002
+ }
1753
2003
  rollbackTrivia(ctx, loopMark);
1754
2004
  break;
1755
2005
  }
@@ -1923,7 +2173,7 @@ function rules(a, b) {
1923
2173
  cache[key] = parser2;
1924
2174
  }
1925
2175
  }
1926
- if (options?.trivia !== void 0) {
2176
+ if (options?.trivia != null) {
1927
2177
  for (const key of Object.keys(definitions)) {
1928
2178
  const rule = cache[key];
1929
2179
  if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
@@ -1953,7 +2203,9 @@ function not(combinator) {
1953
2203
  _meta: meta,
1954
2204
  _def: { tag: "not", parser: combinator },
1955
2205
  parse(input, pos, ctx) {
2206
+ const mark = saveCstMark(ctx);
1956
2207
  const result = combinator.parse(input, pos, ctx);
2208
+ rollbackCstCapture(ctx, mark);
1957
2209
  if (result.ok) {
1958
2210
  return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
1959
2211
  }
@@ -2067,7 +2319,7 @@ function unwrapChild(child) {
2067
2319
  return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
2068
2320
  }
2069
2321
  function isCstChild(value) {
2070
- return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "error");
2322
+ return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "parseError");
2071
2323
  }
2072
2324
  function missingInferredType() {
2073
2325
  throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
@@ -2345,65 +2597,6 @@ function token(root) {
2345
2597
  };
2346
2598
  }
2347
2599
 
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
2600
  // src/compiler/scannable-run.ts
2408
2601
  var BACKSLASH = 92;
2409
2602
  var META = new Set("()[]{}*+?|^$.".split(""));
@@ -2475,20 +2668,39 @@ function parseSeqParts(source) {
2475
2668
  lit = [];
2476
2669
  }
2477
2670
  };
2478
- const runFrom = (ranges, negated, q) => {
2671
+ const braceQuant = (at) => {
2672
+ const close = source.indexOf("}", at);
2673
+ if (close === -1) return null;
2674
+ const body = source.slice(at + 1, close);
2675
+ const m = /^(\d+)(,(\d*)?)?$/.exec(body);
2676
+ if (!m) return null;
2677
+ const min = Number(m[1]);
2678
+ const max = m[2] === void 0 ? min : m[3] ? Number(m[3]) : Infinity;
2679
+ if (max < 1 || min > max) return null;
2680
+ return { min, max, len: close - at + 1 };
2681
+ };
2682
+ const runFrom = (ranges, negated, at) => {
2683
+ const q = source[at];
2479
2684
  if (q === "+") {
2480
- parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
2685
+ parts.push({ part: "run", ranges, negated, min: 1, max: Infinity });
2481
2686
  return 1;
2482
2687
  }
2483
2688
  if (q === "*") {
2484
- parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
2689
+ parts.push({ part: "run", ranges, negated, min: 0, max: Infinity });
2485
2690
  return 1;
2486
2691
  }
2487
2692
  if (q === "?") {
2488
- parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
2693
+ parts.push({ part: "run", ranges, negated, min: 0, max: 1 });
2489
2694
  return 1;
2490
2695
  }
2491
- parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
2696
+ if (q === "{") {
2697
+ const b = braceQuant(at);
2698
+ if (b) {
2699
+ parts.push({ part: "run", ranges, negated, min: b.min, max: b.max });
2700
+ return b.len;
2701
+ }
2702
+ }
2703
+ parts.push({ part: "run", ranges, negated, min: 1, max: 1 });
2492
2704
  return 0;
2493
2705
  };
2494
2706
  let i = 0;
@@ -2513,7 +2725,7 @@ function parseSeqParts(source) {
2513
2725
  if (!ranges) return null;
2514
2726
  flush();
2515
2727
  i = k + 1;
2516
- i += runFrom(ranges, neg, source[i]);
2728
+ i += runFrom(ranges, neg, i);
2517
2729
  continue;
2518
2730
  }
2519
2731
  if (ch === "\\") {
@@ -2521,7 +2733,7 @@ function parseSeqParts(source) {
2521
2733
  if (e === "d" || e === "w" || e === "s") {
2522
2734
  flush();
2523
2735
  i += 2;
2524
- i += runFrom(shorthandRanges(e), false, source[i]);
2736
+ i += runFrom(shorthandRanges(e), false, i);
2525
2737
  continue;
2526
2738
  }
2527
2739
  const uni = readUnicodeEscape(source, i);
@@ -2610,7 +2822,7 @@ function seqIsUnambiguous(parts) {
2610
2822
  let hasMandatory = false;
2611
2823
  for (let k = 0; k < parts.length; k++) {
2612
2824
  const p = parts[k];
2613
- const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
2825
+ const mandatory = p.part === "lit" ? !p.optional : p.min >= 1;
2614
2826
  if (mandatory) hasMandatory = true;
2615
2827
  if (p.part === "group") {
2616
2828
  const exposure = groupPartExposure(p);
@@ -2621,9 +2833,8 @@ function seqIsUnambiguous(parts) {
2621
2833
  if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
2622
2834
  }
2623
2835
  }
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) {
2836
+ const needsDisjoint = p.part === "lit" ? p.optional : p.part === "run" ? p.max > p.min : p.unbounded || p.min === 0;
2837
+ if (needsDisjoint) {
2627
2838
  const rest = seqFirstAccept(parts.slice(k + 1));
2628
2839
  if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
2629
2840
  }
@@ -2680,8 +2891,8 @@ function fixedClassSeq(shape) {
2680
2891
  if (p.part === "lit") {
2681
2892
  if (p.optional) return null;
2682
2893
  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 });
2894
+ } else if (p.part === "run" && p.max === p.min && Number.isFinite(p.max)) {
2895
+ for (let c = 0; c < p.max; c++) out.push({ ranges: p.ranges, negated: p.negated });
2685
2896
  } else {
2686
2897
  return null;
2687
2898
  }
@@ -2733,7 +2944,7 @@ function trailingBacktrackClass(shape) {
2733
2944
  const last = shape.parts[shape.parts.length - 1];
2734
2945
  if (last.part === "lit") return null;
2735
2946
  if (last.part === "run") {
2736
- if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
2947
+ if (last.max > last.min) return { ranges: last.ranges, negated: last.negated };
2737
2948
  return null;
2738
2949
  }
2739
2950
  return groupPartExposure(last);
@@ -2870,7 +3081,7 @@ function partFirstAccept(part) {
2870
3081
  function seqFirstAccept(parts) {
2871
3082
  const sets = [];
2872
3083
  for (const p of parts) {
2873
- const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
3084
+ const mandatory = p.part === "lit" ? !p.optional : p.min >= 1;
2874
3085
  sets.push(partFirstAccept(p));
2875
3086
  if (mandatory) break;
2876
3087
  }
@@ -3285,7 +3496,13 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
3285
3496
  lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
3286
3497
  lines.push(`${ind2} ${endV2} += ${L}`);
3287
3498
  }
3288
- } else if (p.part === "run" && !p.unbounded) {
3499
+ } else if (p.part === "run" && p.max === Infinity) {
3500
+ const s = mint("_s");
3501
+ lines.push(`${ind2} const ${s} = ${endV2}`);
3502
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
3503
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
3504
+ else if (p.min > 1) lines.push(`${ind2} if (${endV2} - ${s} < ${p.min}) break`);
3505
+ } else if (p.part === "run" && p.max === 1) {
3289
3506
  if (p.min === 1) {
3290
3507
  lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
3291
3508
  lines.push(`${ind2} ${endV2}++`);
@@ -3295,8 +3512,8 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
3295
3512
  } else if (p.part === "run") {
3296
3513
  const s = mint("_s");
3297
3514
  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`);
3515
+ lines.push(`${ind2} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
3516
+ if (p.min > 0) lines.push(`${ind2} if (${endV2} - ${s} < ${p.min}) break`);
3300
3517
  } else {
3301
3518
  const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
3302
3519
  if (!p.unbounded) {
@@ -3755,21 +3972,29 @@ function hoistExpected(ctx, constArrSource) {
3755
3972
  }
3756
3973
  return name;
3757
3974
  }
3975
+ function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
3976
+ if (!ctx.recovery) return "";
3977
+ const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
3978
+ const pb = v(ctx, "_pb");
3979
+ 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} } } } `;
3980
+ }
3758
3981
  function failBody(ctx, expected, posExpr) {
3982
+ const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
3759
3983
  if (ctx.failLabel) {
3760
- if (!ctx.recordFail) return `break ${ctx.failLabel}`;
3761
- return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
3984
+ if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3985
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
3762
3986
  }
3763
- return failReturn(expected, posExpr);
3987
+ return probe + failReturn(expected, posExpr);
3764
3988
  }
3765
3989
  function failArrBody(ctx, expectedArr, posExpr, constant = true) {
3990
+ const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
3766
3991
  if (ctx.failLabel) {
3767
- if (!ctx.recordFail) return `break ${ctx.failLabel}`;
3992
+ if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
3768
3993
  const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
3769
- return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
3994
+ return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
3770
3995
  }
3771
- if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
3772
- return failReturnArr(expectedArr, posExpr);
3996
+ if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
3997
+ return probe + failReturnArr(expectedArr, posExpr);
3773
3998
  }
3774
3999
  function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
3775
4000
  return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
@@ -4269,6 +4494,10 @@ function emitRegex(def, ctx, pos) {
4269
4494
  stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
4270
4495
  return { stmts, valueVar: vv, endVar };
4271
4496
  }
4497
+ function syncSentinelExpr(fs) {
4498
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
4499
+ return `_ctx._rec.sentinel({ kind: 'ranges', ranges: ${JSON.stringify(fs.ranges)} })`;
4500
+ }
4272
4501
  function emitSeqValues(def, ctx, pos) {
4273
4502
  const startV = v(ctx, "_start");
4274
4503
  const curV = v(ctx, "_cur");
@@ -4277,7 +4506,17 @@ function emitSeqValues(def, ctx, pos) {
4277
4506
  `${ind(ctx)}let ${curV} = ${pos}`
4278
4507
  ];
4279
4508
  const valueVars = [];
4509
+ const syncInV = ctx.recovery ? v(ctx, "_syncIn") : "";
4510
+ if (ctx.recovery) stmts.push(`${ind(ctx)}const ${syncInV} = _ctx._sync`);
4511
+ const publishSync = (i) => {
4512
+ if (!ctx.recovery) return "";
4513
+ const fs = def.parsers.slice(i + 1).reduce((acc, p) => union(acc, firstSetOf(p)), { kind: "empty" });
4514
+ const ref2 = syncSentinelExpr(fs);
4515
+ return `${ind(ctx)}if (_ctx._tolerant) _ctx._sync = ${ref2 ?? syncInV}`;
4516
+ };
4280
4517
  for (let i = 0; i < def.parsers.length; i++) {
4518
+ const syncPub = publishSync(i);
4519
+ if (syncPub) stmts.push(syncPub);
4281
4520
  if (i > 0 && ctx.activeTrivia) {
4282
4521
  if (ctx.capturing) {
4283
4522
  const capFn = ensureTriviaCaptureFn(ctx);
@@ -4302,7 +4541,17 @@ function emitSeqValues(def, ctx, pos) {
4302
4541
  continue;
4303
4542
  } else {
4304
4543
  const trivFn = ensureTriviaFn(ctx);
4305
- stmts.push(`${ind(ctx)}${curV} = ${trivFn}(input, ${curV}, _ctx)`);
4544
+ const scanEndV = v(ctx, "_sne");
4545
+ stmts.push(`${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx)`);
4546
+ const r2 = emit(def.parsers[i], ctx, scanEndV);
4547
+ stmts.push(...r2.stmts);
4548
+ const endAfterV = v(ctx, "_sea");
4549
+ stmts.push(
4550
+ `${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
4551
+ `${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}`
4552
+ );
4553
+ valueVars.push(r2.valueVar);
4554
+ continue;
4306
4555
  }
4307
4556
  }
4308
4557
  const r = emit(def.parsers[i], ctx, curV);
@@ -4532,7 +4781,7 @@ function emitFirstMatch(def, ctx, pos) {
4532
4781
  if (gate) {
4533
4782
  const gateIdx = ctx.mapFns.length;
4534
4783
  ctx.mapFns.push(gate);
4535
- ctx.mapFnSrcs.push(null);
4784
+ ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
4536
4785
  gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
4537
4786
  }
4538
4787
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
@@ -4637,6 +4886,8 @@ function emitMany(def, ctx, pos) {
4637
4886
  ...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
4638
4887
  `${ind(ctx)}let ${curV} = ${pos}`
4639
4888
  ];
4889
+ const mySyncV = ctx.recovery ? v(ctx, "_mysy") : "";
4890
+ if (ctx.recovery) stmts.push(`${ind(ctx)}const ${mySyncV} = _ctx._sync`);
4640
4891
  if (def.min === 1) {
4641
4892
  const firstR = emit(def.parser, ctx, curV);
4642
4893
  stmts.push(...firstR.stmts);
@@ -4670,10 +4921,26 @@ function emitMany(def, ctx, pos) {
4670
4921
  }
4671
4922
  }
4672
4923
  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
- );
4924
+ stmts.push(...iterStmts);
4925
+ if (ctx.recovery) {
4926
+ const rrV = v(ctx, "_rr");
4927
+ const exp = hoistExpected(ctx, deriveExpectedArr([def.parser]));
4928
+ stmts.push(
4929
+ `${ind(ctx)}if (!${iterOk}) {`,
4930
+ `${ind(ctx)} ${rollback}if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPos}, _ctx)) {`,
4931
+ `${ind(ctx)} const ${rrV} = _ctx._rec.scan(input, ${itemPos}, _ctx, ${mySyncV}, ${exp})`,
4932
+ ...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : [],
4933
+ `${ind(ctx)} _ctx._rec.capture(_ctx, ${rrV}.error)`,
4934
+ `${ind(ctx)} ${curV} = ${rrV}.end`,
4935
+ `${ind(ctx)} continue`,
4936
+ `${ind(ctx)} }`,
4937
+ `${ind(ctx)} break`,
4938
+ `${ind(ctx)}}`,
4939
+ `${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`
4940
+ );
4941
+ } else {
4942
+ stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
4943
+ }
4677
4944
  if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
4678
4945
  stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
4679
4946
  ctx.indent--;
@@ -4695,20 +4962,56 @@ function emitOptional(def, ctx, pos) {
4695
4962
  function emitSepBy(_p, def, ctx, pos) {
4696
4963
  const arrV = v(ctx, "_arr");
4697
4964
  const curV = v(ctx, "_cur");
4965
+ const rec = !!ctx.recovery;
4966
+ const mySyncV = rec ? v(ctx, "_mysy") : "";
4967
+ const exp = rec ? hoistExpected(ctx, deriveExpectedArr([def.parser])) : "";
4968
+ const sepSent = rec ? syncSentinelExpr(firstSetOf(def.separator)) : null;
4969
+ const scanSync = sepSent ? `_ctx._rec.or(${sepSent}, ${mySyncV})` : mySyncV;
4698
4970
  const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
4699
4971
  const stmts = [
4700
4972
  `${ind(ctx)}const ${arrV} = []`,
4701
4973
  `${ind(ctx)}let ${curV} = ${pos}`,
4702
- ...firstStmts,
4703
- `${ind(ctx)}if (${firstOk}) {`
4974
+ ...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
4975
+ ...firstStmts
4704
4976
  ];
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++;
4977
+ const failItem = (nextOkVar, itemPosVar, rb, originalFail) => {
4978
+ if (!rec) return [originalFail];
4979
+ const rr = v(ctx, "_rr");
4980
+ return [
4981
+ `${ind(ctx)}if (!${nextOkVar}) {`,
4982
+ `${ind(ctx)} if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPosVar}, _ctx)) {`,
4983
+ `${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
4984
+ `${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
4985
+ `${ind(ctx)} }`,
4986
+ `${ind(ctx)} ${rb}break`,
4987
+ `${ind(ctx)}}`
4988
+ ];
4989
+ };
4990
+ if (rec) {
4991
+ const rr0 = v(ctx, "_rr0");
4992
+ const didV = v(ctx, "_did");
4993
+ stmts.push(
4994
+ `${ind(ctx)}let ${didV} = false`,
4995
+ `${ind(ctx)}if (${firstOk}) { ${arrV}.push(${firstVal}); ${curV} = ${firstEnd}; ${didV} = true }`,
4996
+ `${ind(ctx)}else if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${pos}, _ctx)) {`,
4997
+ `${ind(ctx)} const ${rr0} = _ctx._rec.scan(input, ${pos}, _ctx, ${scanSync}, ${exp})`,
4998
+ `${ind(ctx)} ${arrV}.push(${rr0}.error); _ctx._rec.capture(_ctx, ${rr0}.error); ${curV} = ${rr0}.end; ${didV} = true`,
4999
+ `${ind(ctx)}}`,
5000
+ `${ind(ctx)}if (${didV}) {`
5001
+ );
5002
+ ctx.indent++;
5003
+ stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
5004
+ ctx.indent++;
5005
+ } else {
5006
+ stmts.push(`${ind(ctx)}if (${firstOk}) {`);
5007
+ ctx.indent++;
5008
+ stmts.push(
5009
+ `${ind(ctx)}${arrV}.push(${firstVal})`,
5010
+ `${ind(ctx)}${curV} = ${firstEnd}`,
5011
+ `${ind(ctx)}while (${curV} < input.length) {`
5012
+ );
5013
+ ctx.indent++;
5014
+ }
4712
5015
  let sepAtPos = curV;
4713
5016
  if (ctx.activeTrivia) {
4714
5017
  if (ctx.capturing) {
@@ -4734,8 +5037,7 @@ function emitSepBy(_p, def, ctx, pos) {
4734
5037
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
4735
5038
  stmts.push(
4736
5039
  ...nextStmts,
4737
- // item failed → unwind the separator too, back to the end of the last item
4738
- `${ind(ctx)}if (!${nextOk}) { ${rollbackToSep}break }`,
5040
+ ...failItem(nextOk, npV, rollbackToSep, `${ind(ctx)}if (!${nextOk}) { ${rollbackToSep}break }`),
4739
5041
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4740
5042
  `${ind(ctx)}${curV} = ${nextEnd}`
4741
5043
  );
@@ -4751,7 +5053,7 @@ function emitSepBy(_p, def, ctx, pos) {
4751
5053
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
4752
5054
  stmts.push(
4753
5055
  ...nextStmts,
4754
- `${ind(ctx)}if (!${nextOk}) break`,
5056
+ ...failItem(nextOk, npV, "", `${ind(ctx)}if (!${nextOk}) break`),
4755
5057
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4756
5058
  `${ind(ctx)}${curV} = ${nextEnd}`
4757
5059
  );
@@ -4771,7 +5073,7 @@ function emitSepBy(_p, def, ctx, pos) {
4771
5073
  const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
4772
5074
  stmts.push(
4773
5075
  ...nextStmts,
4774
- `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
5076
+ ...failItem(nextOk, sepEnd, nextRb, `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`),
4775
5077
  `${ind(ctx)}${arrV}.push(${nextVal})`,
4776
5078
  `${ind(ctx)}${curV} = ${nextEnd}`
4777
5079
  );
@@ -5028,7 +5330,7 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
5028
5330
  stmts.push(
5029
5331
  `${i}const ${ndV} = ${finalExpr}`,
5030
5332
  `${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} } })`
5333
+ `${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
5334
  );
5033
5335
  return { stmts, valueVar: ndV, endVar };
5034
5336
  }
@@ -5118,6 +5420,10 @@ function emitExpect(def, ctx, pos) {
5118
5420
  `${ind0}if (!${okVar}) {`,
5119
5421
  `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
5120
5422
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
5423
+ // Embed the missing-token error as a `parseError` CST child, mirroring the
5424
+ // interpreter (expect.ts) and the list-recovery emit sites — guarded on
5425
+ // `_ctx._tolerant`, under which the driver always installs `_ctx._rec`.
5426
+ `${ind0} if (_ctx._tolerant) _ctx._rec.capture(_ctx, ${errV})`,
5121
5427
  `${ind0} ${valVar} = ${errV}`,
5122
5428
  `${ind0} ${endVar} = ${pos}`,
5123
5429
  `${ind0} ${okVar} = true`,
@@ -5284,7 +5590,7 @@ function emitDispatch(p, ctx, pos) {
5284
5590
  case "guard": {
5285
5591
  const fnIdx = ctx.mapFns.length;
5286
5592
  ctx.mapFns.push(def.predicate);
5287
- ctx.mapFnSrcs.push(null);
5593
+ ctx.mapFnSrcs.push(def.predSrc ?? null);
5288
5594
  const vv = v(ctx);
5289
5595
  return {
5290
5596
  stmts: [
@@ -5299,7 +5605,7 @@ function emitDispatch(p, ctx, pos) {
5299
5605
  const evIdx = ctx.mapFns.length;
5300
5606
  const extra = def.extra;
5301
5607
  ctx.mapFns.push((() => extra));
5302
- ctx.mapFnSrcs.push(null);
5608
+ ctx.mapFnSrcs.push(def.extraSrc !== void 0 ? `() => (${def.extraSrc})` : null);
5303
5609
  const innerParser = def.parser;
5304
5610
  if (!ctx.namedParsers.has(innerParser)) {
5305
5611
  const fnName = `_wcf${ctx.namedParsers.size}`;
@@ -5543,7 +5849,7 @@ function ruleDependencies(ruleMap) {
5543
5849
  }
5544
5850
  return deps;
5545
5851
  }
5546
- function compile(combinator, mapFnSources) {
5852
+ function compile(combinator, mapFnSources, opts) {
5547
5853
  markUnusedValues(combinator);
5548
5854
  const grammarTrivia = combinator._meta.grammarTrivia;
5549
5855
  const ctx = {
@@ -5564,6 +5870,7 @@ function compile(combinator, mapFnSources) {
5564
5870
  triviaFnNames: /* @__PURE__ */ new Map(),
5565
5871
  namedFnDecls: [],
5566
5872
  capturing: hasNodeDef(combinator),
5873
+ recovery: opts?.recovery ?? false,
5567
5874
  lazyUsage: analyzeLazyUsage(combinator),
5568
5875
  ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5569
5876
  };
@@ -5616,7 +5923,7 @@ function compile(combinator, mapFnSources) {
5616
5923
  parseWithContext(input, parseCtx, pos = 0) {
5617
5924
  return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
5618
5925
  },
5619
- // Note: collects recover()/expect() errors via _errors. Unlike interpreter
5926
+ // Note: collects expect() errors via _errors. Unlike interpreter
5620
5927
  // parse({recover:true}) it does NOT populate furthestFail — the compiled path
5621
5928
  // inlines failures for throughput and deliberately skips _probe bookkeeping.
5622
5929
  // Callers wanting a furthest-position diagnostic detect unconsumed input
@@ -5673,6 +5980,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
5673
5980
  triviaFnNames: /* @__PURE__ */ new Map(),
5674
5981
  namedFnDecls: [],
5675
5982
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
5983
+ recovery: opts?.recovery ?? false,
5676
5984
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5677
5985
  ns,
5678
5986
  deferFirstSetRefs: true,
@@ -6418,46 +6726,13 @@ function withCtx(extra, combinator) {
6418
6726
  };
6419
6727
  }
6420
6728
 
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
6729
  // src/combinators/completions.ts
6456
- function completionsAt(combinator, input, offset) {
6730
+ function completionsAt(target, input, offset, options = {}) {
6457
6731
  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 [];
6732
+ const ctx = { trackLines: false, _probe: probe, ...options.tolerant ? { _tolerant: true, _rec: REC } : {} };
6733
+ const sliced = input.slice(0, offset);
6734
+ const result = "parseWithContext" in target ? target.parseWithContext(sliced, ctx, 0) : target.parse(sliced, 0, ctx);
6735
+ if (result.ok) return options.tolerant ? probe.best?.expected ?? [] : [];
6461
6736
  const best = deeperFail(probe.best, result);
6462
6737
  return best?.expected ?? [];
6463
6738
  }
@@ -6467,23 +6742,6 @@ function deeperFail(a, b) {
6467
6742
  return a.span.start >= b.span.start ? a : b;
6468
6743
  }
6469
6744
 
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
6745
  // src/cst/relative-spans.ts
6488
6746
  function relativize(node2, parentBase = 0) {
6489
6747
  return {
@@ -6578,6 +6836,9 @@ function editNode(node2, at, delta, parentBase, parentShift, stats) {
6578
6836
  }
6579
6837
 
6580
6838
  // src/functional/doc.ts
6839
+ function mkCtx(state, build, tolerant) {
6840
+ return tolerant ? { trackLines: false, state, build, _tolerant: true, _rec: REC, _errors: [] } : { trackLines: false, state, build };
6841
+ }
6581
6842
  function asRuleFn(entry) {
6582
6843
  return typeof entry === "function" ? entry : (input, pos, ctx) => entry.parse(input, pos, ctx);
6583
6844
  }
@@ -6684,8 +6945,8 @@ function graftRelative(root, path, newNode, delta) {
6684
6945
  children: newChildren
6685
6946
  };
6686
6947
  }
6687
- function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
6688
- const ctx = { trackLines: false, state: null, build };
6948
+ function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build, tolerant) {
6949
+ const ctx = mkCtx(null, build, tolerant);
6689
6950
  const out = [];
6690
6951
  let pos = reStart;
6691
6952
  let guard2 = 0;
@@ -6709,7 +6970,7 @@ function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
6709
6970
  }
6710
6971
  return pos === reEnd ? out : null;
6711
6972
  }
6712
- function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build) {
6973
+ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build, tolerant) {
6713
6974
  const kids = C.children;
6714
6975
  if (kids.length === 0) return null;
6715
6976
  let h = 0;
@@ -6750,7 +7011,7 @@ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registr
6750
7011
  const reStart = h > 0 ? cStart + kids[h - 1].span.end : cStart;
6751
7012
  const reEnd = cStart + kids[t].span.start + delta;
6752
7013
  if (reEnd < reStart) return null;
6753
- const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build);
7014
+ const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
6754
7015
  if (!middle) return null;
6755
7016
  if (middle.length > 0) {
6756
7017
  const last2 = middle[middle.length - 1];
@@ -6760,7 +7021,7 @@ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registr
6760
7021
  for (const sentinel of [" ", "\uFFFF"]) {
6761
7022
  if (newInput[reEnd] === sentinel) continue;
6762
7023
  const probed = newInput.slice(0, reEnd) + sentinel.repeat(newInput.length - reEnd);
6763
- const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build);
7024
+ const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
6764
7025
  if (!probe || probe.length !== middle.length) return null;
6765
7026
  for (let i = 0; i < middle.length; i++) if (!structurallyEqual(probe[i], middle[i])) return null;
6766
7027
  }
@@ -6788,6 +7049,11 @@ function structurallyEqual(a, b) {
6788
7049
  if (at === "leaf" || at === "trivia") {
6789
7050
  return a.value === b.value;
6790
7051
  }
7052
+ if (at === "parseError") {
7053
+ const ae = a.expected ?? [];
7054
+ const be = b.expected ?? [];
7055
+ return ae.length === be.length && ae.every((x, i) => x === be[i]);
7056
+ }
6791
7057
  if (a.type !== b.type) return false;
6792
7058
  const ac = a.children;
6793
7059
  const bc = b.children;
@@ -6797,13 +7063,13 @@ function structurallyEqual(a, b) {
6797
7063
  }
6798
7064
  return true;
6799
7065
  }
6800
- function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced) {
7066
+ function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced, tolerant) {
6801
7067
  if (!produced.ok) return false;
6802
7068
  if (boundary >= newInput.length) return true;
6803
7069
  for (const sentinel of [" ", "\uFFFF"]) {
6804
7070
  if (newInput[boundary] === sentinel) continue;
6805
7071
  const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
6806
- const ctx = { trackLines: false, state, build };
7072
+ const ctx = mkCtx(state, build, tolerant);
6807
7073
  let r;
6808
7074
  try {
6809
7075
  r = ruleFn(probed, start, ctx);
@@ -6835,8 +7101,9 @@ var ParseDocImpl = class _ParseDocImpl {
6835
7101
  _abs;
6836
7102
  _rel;
6837
7103
  errors;
7104
+ unconsumedFrom;
6838
7105
  input;
6839
- constructor(registry, rootRule, opts, trees, errors, input) {
7106
+ constructor(registry, rootRule, opts, trees, errors, unconsumedFrom, input) {
6840
7107
  this._registry = registry;
6841
7108
  this._fns = {};
6842
7109
  this._reps = /* @__PURE__ */ new Set();
@@ -6849,6 +7116,7 @@ var ParseDocImpl = class _ParseDocImpl {
6849
7116
  this._abs = trees.abs;
6850
7117
  this._rel = trees.rel;
6851
7118
  this.errors = errors;
7119
+ this.unconsumedFrom = unconsumedFrom;
6852
7120
  this.input = input;
6853
7121
  }
6854
7122
  /**
@@ -6871,6 +7139,19 @@ var ParseDocImpl = class _ParseDocImpl {
6871
7139
  if (!rel) throw new Error("spanAt on a failed parse (tree is null)");
6872
7140
  return absoluteSpanCST(rel, path);
6873
7141
  }
7142
+ /**
7143
+ * Wrap a reused (grafted/spliced) RELATIVE tree, recomputing `errors` and
7144
+ * `unconsumedFrom` from it so both stay consistent with the tree across the
7145
+ * edit (the flat errors are recovered errors embedded in the reused subtrees;
7146
+ * unconsumedFrom re-derives trailing junk over the new input).
7147
+ */
7148
+ wrapReuse(newTree, newInput) {
7149
+ const trivia2 = triviaOf(this._registry[this._rootRule], this._opts);
7150
+ const end = newTree.span.end;
7151
+ const errors = collectEmbeddedErrors(newTree, 0, []);
7152
+ const unconsumedFrom = unconsumedAfter(end, newInput, trivia2);
7153
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, errors, unconsumedFrom, newInput);
7154
+ }
6874
7155
  edit(from, to, replacement) {
6875
7156
  const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
6876
7157
  const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
@@ -6892,22 +7173,22 @@ var ParseDocImpl = class _ParseDocImpl {
6892
7173
  const ruleFn = this._fns[node2.type];
6893
7174
  if (!ruleFn) continue;
6894
7175
  if (!(absStart <= from && to <= absEnd)) continue;
6895
- const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
7176
+ const ctx = mkCtx(node2.state, this._opts.build, !!this._opts.tolerant);
6896
7177
  const r = ruleFn(newInput, absStart, ctx);
6897
7178
  if (!r.ok) continue;
6898
7179
  if (r.span.end !== absEnd + delta) continue;
6899
- if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r)) {
7180
+ if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r, !!this._opts.tolerant)) {
6900
7181
  continue;
6901
7182
  }
6902
7183
  const parentBase = absStart - node2.span.start;
6903
7184
  const newRel = relativizeCST(r.value, parentBase);
6904
7185
  if (delta === 0) {
6905
7186
  const newTree2 = replaceAtPath(rebuild, root, path, newRel);
6906
- return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree2 }, [], newInput);
7187
+ return this.wrapReuse(newTree2, newInput);
6907
7188
  }
6908
7189
  if (this._opts.rebuild) return reparse();
6909
7190
  const newTree = graftRelative(root, path, newRel, delta);
6910
- return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, [], newInput);
7191
+ return this.wrapReuse(newTree, newInput);
6911
7192
  }
6912
7193
  if (this._opts.structuralReuse && delta !== 0 && !this._opts.rebuild) {
6913
7194
  const spliceCandidates = [...candidates, { node: root, path: [] }];
@@ -6915,22 +7196,50 @@ var ParseDocImpl = class _ParseDocImpl {
6915
7196
  if (!this._reps.has(node2.type)) continue;
6916
7197
  const { start: cStart, end: cEnd } = absoluteSpanCST(root, path);
6917
7198
  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);
7199
+ const spliced = tryListSplice(root, path, node2, cStart, newInput, from, to, delta, this._fns, this._opts.build, !!this._opts.tolerant);
7200
+ if (spliced) return this.wrapReuse(spliced, newInput);
6920
7201
  }
6921
7202
  }
6922
7203
  return reparse();
6923
7204
  }
6924
7205
  };
7206
+ function triviaOf(entry, opts) {
7207
+ if (opts.trivia) return opts.trivia;
7208
+ if (entry === void 0 || typeof entry === "function") return void 0;
7209
+ const meta = entry._meta;
7210
+ return meta ? meta.grammarTrivia : void 0;
7211
+ }
7212
+ function unconsumedAfter(end, input, trivia2) {
7213
+ let pos = end;
7214
+ if (trivia2 && pos < input.length) {
7215
+ const t = trivia2.parse(input, pos, { trackLines: false });
7216
+ if (t.ok && t.span.end > pos) pos = t.span.end;
7217
+ }
7218
+ return pos < input.length ? pos : null;
7219
+ }
7220
+ function collectEmbeddedErrors(node2, base, out) {
7221
+ const c = node2;
7222
+ if (!c || typeof c !== "object" || !c.span) return out;
7223
+ const start = base + c.span.start;
7224
+ if (c._tag === "parseError") {
7225
+ out.push({ _tag: "parseError", span: { start, end: base + c.span.end }, expected: c.expected ?? [] });
7226
+ return out;
7227
+ }
7228
+ if (Array.isArray(c.children)) for (const k of c.children) collectEmbeddedErrors(k, start, out);
7229
+ return out;
7230
+ }
6925
7231
  function parseDoc(registry, rootRule, input, opts = {}) {
6926
7232
  const entry = registry[rootRule];
6927
7233
  if (!entry) throw new Error(`No rule '${rootRule}' in registry`);
6928
- const ctx = { trackLines: false, state: opts.state, build: opts.build };
7234
+ const ctx = mkCtx(opts.state, opts.build, !!opts.tolerant);
6929
7235
  const r = asRuleFn(entry)(input, 0, ctx);
6930
7236
  if (r.ok) {
6931
- return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, [], input);
7237
+ const errors = ctx._errors ? [...ctx._errors] : [];
7238
+ const unconsumedFrom = unconsumedAfter(r.span.end, input, triviaOf(entry, opts));
7239
+ return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, errors, unconsumedFrom, input);
6932
7240
  }
6933
- return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [{ ok: false, expected: r.expected, span: r.span }], input);
7241
+ const fail = { _tag: "parseError", span: r.span, expected: r.expected };
7242
+ return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input);
6934
7243
  }
6935
7244
 
6936
7245
  // src/functional/run.ts
@@ -6951,7 +7260,8 @@ function run(entry, input, options = {}) {
6951
7260
  build: options.build,
6952
7261
  state: options.state,
6953
7262
  ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
6954
- ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {}
7263
+ ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
7264
+ ...options.tolerant ? { _tolerant: true, _rec: REC } : {}
6955
7265
  };
6956
7266
  const r = invoke(entry, input, 0, ctx);
6957
7267
  let unconsumedFrom = null;