parseman 0.12.0 → 0.13.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.
package/dist/index.cjs CHANGED
@@ -44,7 +44,6 @@ __export(index_exports, {
44
44
  keywords: () => keywords,
45
45
  label: () => label,
46
46
  literal: () => literal,
47
- makeFunctionalDoc: () => makeFunctionalDoc,
48
47
  makeWord: () => makeWord,
49
48
  many: () => many,
50
49
  manyRecover: () => manyRecover,
@@ -55,6 +54,7 @@ __export(index_exports, {
55
54
  oneOrMore: () => oneOrMore,
56
55
  optional: () => optional,
57
56
  parse: () => parse,
57
+ parseDoc: () => parseDoc,
58
58
  parser: () => parser,
59
59
  recover: () => recover,
60
60
  ref: () => ref,
@@ -1476,8 +1476,29 @@ function expect(combinator, label2) {
1476
1476
  var BACKSLASH = 92;
1477
1477
  var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1478
1478
  var META = new Set("()[]{}*+?|^$.".split(""));
1479
+ var SPACE_RANGES = [
1480
+ [9, 13],
1481
+ [32, 32],
1482
+ [160, 160],
1483
+ [5760, 5760],
1484
+ [8192, 8202],
1485
+ [8232, 8232],
1486
+ [8233, 8233],
1487
+ [8239, 8239],
1488
+ [8287, 8287],
1489
+ [12288, 12288],
1490
+ [65279, 65279]
1491
+ ];
1479
1492
  function shorthandRanges(ch) {
1480
- return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
1493
+ if (ch === "d") return [[48, 57]];
1494
+ if (ch === "s") return SPACE_RANGES;
1495
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1496
+ }
1497
+ function readUnicodeEscape(body, i) {
1498
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1499
+ const hex = body.slice(i + 2, i + 6);
1500
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1501
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1481
1502
  }
1482
1503
  function parseClassRanges(body) {
1483
1504
  const ranges = [];
@@ -1486,11 +1507,16 @@ function parseClassRanges(body) {
1486
1507
  const ch = body[i];
1487
1508
  if (ch === void 0) return null;
1488
1509
  if (ch === "\\") {
1510
+ const uni = readUnicodeEscape(body, i);
1511
+ if (uni) {
1512
+ i = uni.next;
1513
+ return { cp: uni.cp };
1514
+ }
1489
1515
  const e = body[i + 1];
1490
1516
  if (e === void 0) return null;
1491
1517
  i += 2;
1492
1518
  if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1493
- if (e === "d" || e === "w") return { set: shorthandRanges(e) };
1519
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1494
1520
  if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1495
1521
  return { cp: e.codePointAt(0) };
1496
1522
  }
@@ -1518,10 +1544,16 @@ function parseClassRanges(body) {
1518
1544
  function classToRanges(cls) {
1519
1545
  if (cls === "\\d") return shorthandRanges("d");
1520
1546
  if (cls === "\\w") return shorthandRanges("w");
1547
+ if (cls === "\\s") return shorthandRanges("s");
1521
1548
  const body = cls.slice(1, -1);
1522
1549
  if (body.startsWith("^")) return null;
1523
1550
  return parseClassRanges(body);
1524
1551
  }
1552
+ var foldEq = (cVar, cp) => {
1553
+ if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
1554
+ if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
1555
+ return `${cVar} === ${cp}`;
1556
+ };
1525
1557
  function literalCodePoints(frag) {
1526
1558
  const out = [];
1527
1559
  let i = 0;
@@ -1568,22 +1600,446 @@ function parseStringShape(source) {
1568
1600
  if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
1569
1601
  return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
1570
1602
  }
1603
+ function parseSeqParts(source) {
1604
+ const parts = [];
1605
+ let lit = [];
1606
+ const flush = () => {
1607
+ if (lit.length) {
1608
+ parts.push({ part: "lit", cps: lit, optional: false });
1609
+ lit = [];
1610
+ }
1611
+ };
1612
+ const runFrom = (ranges, negated, q) => {
1613
+ if (q === "+") {
1614
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
1615
+ return 1;
1616
+ }
1617
+ if (q === "*") {
1618
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
1619
+ return 1;
1620
+ }
1621
+ if (q === "?") {
1622
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
1623
+ return 1;
1624
+ }
1625
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
1626
+ return 0;
1627
+ };
1628
+ let i = 0;
1629
+ while (i < source.length) {
1630
+ const ch = source[i];
1631
+ if (ch === "[") {
1632
+ let k = i + 1;
1633
+ const neg = source[k] === "^";
1634
+ if (neg) k++;
1635
+ let body = "";
1636
+ while (k < source.length && source[k] !== "]") {
1637
+ if (source[k] === "\\") {
1638
+ body += source[k] + (source[k + 1] ?? "");
1639
+ k += 2;
1640
+ } else {
1641
+ body += source[k];
1642
+ k++;
1643
+ }
1644
+ }
1645
+ if (source[k] !== "]") return null;
1646
+ const ranges = parseClassRanges(body);
1647
+ if (!ranges) return null;
1648
+ flush();
1649
+ i = k + 1;
1650
+ i += runFrom(ranges, neg, source[i]);
1651
+ continue;
1652
+ }
1653
+ if (ch === "\\") {
1654
+ const e = source[i + 1];
1655
+ if (e === "d" || e === "w" || e === "s") {
1656
+ flush();
1657
+ i += 2;
1658
+ i += runFrom(shorthandRanges(e), false, source[i]);
1659
+ continue;
1660
+ }
1661
+ const uni = readUnicodeEscape(source, i);
1662
+ let cp2;
1663
+ if (uni) {
1664
+ cp2 = uni.cp;
1665
+ i = uni.next;
1666
+ } else if (e === void 0) return null;
1667
+ else if (e in CLASS_ESCAPES) {
1668
+ cp2 = CLASS_ESCAPES[e];
1669
+ i += 2;
1670
+ } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1671
+ else {
1672
+ cp2 = e.codePointAt(0);
1673
+ i += 2;
1674
+ }
1675
+ if (source[i] === "?") {
1676
+ flush();
1677
+ parts.push({ part: "lit", cps: [cp2], optional: true });
1678
+ i++;
1679
+ } else lit.push(cp2);
1680
+ continue;
1681
+ }
1682
+ if (ch === "(") {
1683
+ if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
1684
+ let k = i + 3;
1685
+ let depth = 1;
1686
+ while (k < source.length && depth > 0) {
1687
+ const c2 = source[k];
1688
+ if (c2 === "\\") {
1689
+ k += 2;
1690
+ continue;
1691
+ }
1692
+ if (c2 === "[") {
1693
+ let j = k + 1;
1694
+ if (source[j] === "^") j++;
1695
+ while (j < source.length && source[j] !== "]") {
1696
+ if (source[j] === "\\") j += 2;
1697
+ else j++;
1698
+ }
1699
+ if (source[j] !== "]") return null;
1700
+ k = j + 1;
1701
+ continue;
1702
+ }
1703
+ if (c2 === "(") depth++;
1704
+ else if (c2 === ")") depth--;
1705
+ k++;
1706
+ }
1707
+ if (depth !== 0) return null;
1708
+ const body = source.slice(i + 3, k - 1);
1709
+ const inner = parseScanShape(body);
1710
+ if (!inner || !groupInnerSafe(inner)) return null;
1711
+ const q = source[k];
1712
+ if (q === "{") return null;
1713
+ flush();
1714
+ if (q === "+") {
1715
+ if (shapeCanBeEmpty(inner)) return null;
1716
+ parts.push({ part: "group", inner, min: 1, unbounded: true });
1717
+ i = k + 1;
1718
+ } else if (q === "*") {
1719
+ if (shapeCanBeEmpty(inner)) return null;
1720
+ parts.push({ part: "group", inner, min: 0, unbounded: true });
1721
+ i = k + 1;
1722
+ } else if (q === "?") {
1723
+ parts.push({ part: "group", inner, min: 0, unbounded: false });
1724
+ i = k + 1;
1725
+ } else {
1726
+ parts.push({ part: "group", inner, min: 1, unbounded: false });
1727
+ i = k;
1728
+ }
1729
+ continue;
1730
+ }
1731
+ if (META.has(ch)) return null;
1732
+ const cp = ch.codePointAt(0);
1733
+ i += ch.length;
1734
+ if (source[i] === "?") {
1735
+ flush();
1736
+ parts.push({ part: "lit", cps: [cp], optional: true });
1737
+ i++;
1738
+ } else lit.push(cp);
1739
+ }
1740
+ flush();
1741
+ return parts.length ? parts : null;
1742
+ }
1743
+ function seqIsUnambiguous(parts) {
1744
+ let hasMandatory = false;
1745
+ for (let k = 0; k < parts.length; k++) {
1746
+ const p = parts[k];
1747
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1748
+ if (mandatory) hasMandatory = true;
1749
+ const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
1750
+ const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
1751
+ if (skippableSingle || isUnboundedRepeat) {
1752
+ const rest = seqFirstAccept(parts.slice(k + 1));
1753
+ if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
1754
+ }
1755
+ }
1756
+ return hasMandatory;
1757
+ }
1758
+ function rangesOverlap(a, b) {
1759
+ return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
1760
+ }
1761
+ function rangesSubset(sub, sup) {
1762
+ return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
1763
+ }
1764
+ function parseClassOperand(body) {
1765
+ if (body === "\\d" || body === "\\w" || body === "\\s") {
1766
+ return { ranges: shorthandRanges(body[1]), negated: false };
1767
+ }
1768
+ if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1769
+ let inner = body.slice(1, -1);
1770
+ const negated = inner.startsWith("^");
1771
+ if (negated) inner = inner.slice(1);
1772
+ const ranges = parseClassRanges(inner);
1773
+ return ranges ? { ranges, negated } : null;
1774
+ }
1775
+ const cps = literalCodePoints(body);
1776
+ if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
1777
+ return null;
1778
+ }
1779
+ function classSubset(t, u) {
1780
+ if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
1781
+ if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
1782
+ if (t.negated && !u.negated) return false;
1783
+ return rangesSubset(u.ranges, t.ranges);
1784
+ }
1785
+ function classDisjoint(t, u) {
1786
+ if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
1787
+ if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
1788
+ if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
1789
+ return false;
1790
+ }
1791
+ function firstSetsDisjoint(a, b) {
1792
+ if (a === "any" || b === "any") return false;
1793
+ return classDisjoint(a, b);
1794
+ }
1795
+ function trailingBacktrackClass(shape) {
1796
+ if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
1797
+ if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
1798
+ if (shape.kind === "litFold") return null;
1799
+ if (shape.kind === "seq") {
1800
+ const last = shape.parts[shape.parts.length - 1];
1801
+ if (last.part === "lit") return null;
1802
+ if (last.part === "run") {
1803
+ if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
1804
+ return null;
1805
+ }
1806
+ const innerWiggle = trailingBacktrackClass(last.inner);
1807
+ if (innerWiggle === "unsupported") return "unsupported";
1808
+ const requiredOnce = !last.unbounded && last.min === 1;
1809
+ if (requiredOnce) return innerWiggle;
1810
+ const dropExposed = shapeFirstAccept(last.inner);
1811
+ if (dropExposed === "any") return "unsupported";
1812
+ if (innerWiggle === null) return dropExposed;
1813
+ const unioned = unionCharSets([innerWiggle, dropExposed]);
1814
+ return unioned === "any" ? "unsupported" : unioned;
1815
+ }
1816
+ if (shape.kind === "alt") {
1817
+ if (!shape.disjoint) return "unsupported";
1818
+ const classes = shape.arms.map(trailingBacktrackClass);
1819
+ if (classes.some((c) => c === "unsupported")) return "unsupported";
1820
+ const concrete = classes.filter((c) => c !== null);
1821
+ if (concrete.length === 0) return null;
1822
+ const unioned = unionCharSets(concrete);
1823
+ return unioned === "any" ? "unsupported" : unioned;
1824
+ }
1825
+ return "unsupported";
1826
+ }
1827
+ function lookaheadUnambiguous(inner, operand, negative) {
1828
+ const tail = trailingBacktrackClass(inner);
1829
+ if (tail === "unsupported") return false;
1830
+ if (tail === null) return true;
1831
+ return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
1832
+ }
1833
+ function stripTrailingLookahead(source) {
1834
+ if (!source.endsWith(")")) return null;
1835
+ for (const negative of [true, false]) {
1836
+ const lead = negative ? "(?!" : "(?=";
1837
+ const idx = source.lastIndexOf(lead);
1838
+ if (idx === -1) continue;
1839
+ const body = source.slice(idx + lead.length, -1);
1840
+ const operand = body.length ? parseClassOperand(body) : null;
1841
+ if (!operand) continue;
1842
+ return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
1843
+ }
1844
+ return null;
1845
+ }
1846
+ function unwrapOuterGroup(source) {
1847
+ while (source.startsWith("(?:")) {
1848
+ let depth = 1;
1849
+ let i = 3;
1850
+ let ok = true;
1851
+ while (i < source.length && depth > 0) {
1852
+ const ch = source[i];
1853
+ if (ch === "\\") {
1854
+ i += 2;
1855
+ continue;
1856
+ }
1857
+ if (ch === "[") {
1858
+ let j = i + 1;
1859
+ if (source[j] === "^") j++;
1860
+ while (j < source.length && source[j] !== "]") {
1861
+ if (source[j] === "\\") j += 2;
1862
+ else j++;
1863
+ }
1864
+ if (source[j] !== "]") {
1865
+ ok = false;
1866
+ break;
1867
+ }
1868
+ i = j + 1;
1869
+ continue;
1870
+ }
1871
+ if (ch === "(") depth++;
1872
+ else if (ch === ")") depth--;
1873
+ i++;
1874
+ }
1875
+ if (!ok || depth !== 0 || i !== source.length) break;
1876
+ source = source.slice(3, -1);
1877
+ }
1878
+ return source;
1879
+ }
1880
+ function splitTopLevelAlternation(source) {
1881
+ const body = unwrapOuterGroup(source);
1882
+ const parts = [];
1883
+ let depth = 0;
1884
+ let last = 0;
1885
+ let i = 0;
1886
+ while (i < body.length) {
1887
+ const ch = body[i];
1888
+ if (ch === "\\") {
1889
+ i += 2;
1890
+ continue;
1891
+ }
1892
+ if (ch === "[") {
1893
+ let j = i + 1;
1894
+ if (body[j] === "^") j++;
1895
+ while (j < body.length && body[j] !== "]") {
1896
+ if (body[j] === "\\") j += 2;
1897
+ else j++;
1898
+ }
1899
+ if (body[j] !== "]") return null;
1900
+ i = j + 1;
1901
+ continue;
1902
+ }
1903
+ if (ch === "(") {
1904
+ depth++;
1905
+ i++;
1906
+ continue;
1907
+ }
1908
+ if (ch === ")") {
1909
+ depth--;
1910
+ if (depth < 0) return null;
1911
+ i++;
1912
+ continue;
1913
+ }
1914
+ if (ch === "|" && depth === 0) {
1915
+ parts.push(body.slice(last, i));
1916
+ last = i + 1;
1917
+ i++;
1918
+ continue;
1919
+ }
1920
+ i++;
1921
+ }
1922
+ if (depth !== 0) return null;
1923
+ parts.push(body.slice(last));
1924
+ if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
1925
+ return parts;
1926
+ }
1927
+ function shapeCanBeEmpty(shape) {
1928
+ if (shape.kind === "chars") return !shape.minOne;
1929
+ if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
1930
+ if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
1931
+ return false;
1932
+ }
1933
+ function unionCharSets(sets) {
1934
+ if (sets.some((s) => s === "any")) return "any";
1935
+ const concrete = sets;
1936
+ if (concrete.length === 1) return concrete[0];
1937
+ if (concrete.some((s) => s.negated)) return "any";
1938
+ return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
1939
+ }
1940
+ function partFirstAccept(part) {
1941
+ if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
1942
+ if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
1943
+ return shapeFirstAccept(part.inner);
1944
+ }
1945
+ function seqFirstAccept(parts) {
1946
+ const sets = [];
1947
+ for (const p of parts) {
1948
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1949
+ sets.push(partFirstAccept(p));
1950
+ if (mandatory) break;
1951
+ }
1952
+ return unionCharSets(sets);
1953
+ }
1954
+ function groupInnerSafe(shape) {
1955
+ if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
1956
+ if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
1957
+ if (shape.kind === "alt") return shape.disjoint;
1958
+ return false;
1959
+ }
1960
+ function shapeFirstAccept(shape) {
1961
+ if (shapeCanBeEmpty(shape)) return "any";
1962
+ switch (shape.kind) {
1963
+ case "chars":
1964
+ return { ranges: shape.ranges, negated: false };
1965
+ case "ident":
1966
+ return { ranges: shape.head, negated: false };
1967
+ case "until":
1968
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1969
+ case "delimited":
1970
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1971
+ case "string":
1972
+ return { ranges: [[shape.quote, shape.quote]], negated: false };
1973
+ case "litFold": {
1974
+ const cp = shape.open[0];
1975
+ if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
1976
+ if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
1977
+ return { ranges: [[cp, cp]], negated: false };
1978
+ }
1979
+ case "seq":
1980
+ return seqFirstAccept(shape.parts);
1981
+ case "lookahead":
1982
+ return shapeFirstAccept(shape.inner);
1983
+ // zero-width — doesn't change first-char acceptance
1984
+ case "alt": {
1985
+ if (!shape.disjoint) return "any";
1986
+ const sets = shape.firsts.filter((f) => f !== null);
1987
+ return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
1988
+ }
1989
+ }
1990
+ }
1991
+ function allPairsDisjoint(sets) {
1992
+ for (let i = 0; i < sets.length; i++) {
1993
+ const a = sets[i];
1994
+ if (a === "any") return false;
1995
+ for (let j = i + 1; j < sets.length; j++) {
1996
+ const b = sets[j];
1997
+ if (b === "any" || !classDisjoint(a, b)) return false;
1998
+ }
1999
+ }
2000
+ return true;
2001
+ }
2002
+ function parseAlternation(source) {
2003
+ const parts = splitTopLevelAlternation(source);
2004
+ if (!parts) return null;
2005
+ const arms = [];
2006
+ for (const p of parts) {
2007
+ const arm = parseScanShape(p);
2008
+ if (!arm) return null;
2009
+ arms.push(arm);
2010
+ }
2011
+ const firstSets = arms.map(shapeFirstAccept);
2012
+ const disjoint = allPairsDisjoint(firstSets);
2013
+ return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
2014
+ }
1571
2015
  function parseScanShape(source) {
2016
+ const alt = parseAlternation(source);
2017
+ if (alt) return alt;
2018
+ const la = stripTrailingLookahead(source);
2019
+ if (la) {
2020
+ const inner = parseScanShapeCore(la.base);
2021
+ if (!inner) return null;
2022
+ const operand = { ranges: la.ranges, negated: la.classNegated };
2023
+ if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
2024
+ return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
2025
+ }
2026
+ return parseScanShapeCore(source);
2027
+ }
2028
+ function parseScanShapeCore(source) {
1572
2029
  let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1573
- if (m) {
1574
- if (m[1].startsWith("^")) return null;
2030
+ if (m && !m[1].startsWith("^")) {
1575
2031
  const ranges = parseClassRanges(m[1]);
1576
2032
  return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
1577
2033
  }
1578
- m = /^\\([dw])([+*])$/.exec(source);
2034
+ m = /^\\([dws])([+*])$/.exec(source);
1579
2035
  if (m) {
1580
2036
  return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
1581
2037
  }
1582
- m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
2038
+ m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
1583
2039
  if (m) {
1584
2040
  const head = classToRanges(m[1]);
1585
2041
  const tail = classToRanges(m[2]);
1586
- return head && tail ? { kind: "ident", head, tail } : null;
2042
+ if (head && tail) return { kind: "ident", head, tail };
1587
2043
  }
1588
2044
  const str = parseStringShape(source);
1589
2045
  if (str) return str;
@@ -1592,7 +2048,6 @@ function parseScanShape(source) {
1592
2048
  const open = literalCodePoints(m[1]);
1593
2049
  const stop = parseClassRanges(m[2]);
1594
2050
  if (open && stop) return { kind: "until", open, stop };
1595
- return null;
1596
2051
  }
1597
2052
  if (!source.includes("\\\\")) {
1598
2053
  m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
@@ -1602,10 +2057,16 @@ function parseScanShape(source) {
1602
2057
  if (open && close) return { kind: "delimited", open, close };
1603
2058
  }
1604
2059
  }
2060
+ const parts = parseSeqParts(source);
2061
+ if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
1605
2062
  return null;
1606
2063
  }
1607
2064
  function scanShapeFromRegex(source, flags) {
1608
- if (/[imsu]/.test(flags)) return null;
2065
+ if (/[msu]/.test(flags)) return null;
2066
+ if (/i/.test(flags)) {
2067
+ const open = literalCodePoints(source);
2068
+ return open ? { kind: "litFold", open } : null;
2069
+ }
1609
2070
  return parseScanShape(source);
1610
2071
  }
1611
2072
  var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
@@ -1620,6 +2081,53 @@ function stringHardStop(shape) {
1620
2081
  }
1621
2082
  var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
1622
2083
  function emitShapeMatch(shape, start, mint, ind2, firstChar) {
2084
+ if (shape.kind === "lookahead") {
2085
+ const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
2086
+ const okV = mint("_ok");
2087
+ const endV2 = mint("_end");
2088
+ const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
2089
+ const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
2090
+ const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
2091
+ const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
2092
+ return {
2093
+ setup: [
2094
+ ...inner.setup,
2095
+ `${ind2}let ${okV} = ${inner.ok}`,
2096
+ `${ind2}let ${endV2} = ${inner.end}`,
2097
+ `${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
2098
+ ],
2099
+ ok: okV,
2100
+ end: endV2
2101
+ };
2102
+ }
2103
+ if (shape.kind === "alt") {
2104
+ const okV = mint("_ok");
2105
+ const endV2 = mint("_end");
2106
+ const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
2107
+ if (shape.disjoint) {
2108
+ const c0 = codeAt(start, firstChar);
2109
+ shape.arms.forEach((arm, k) => {
2110
+ const fs = shape.firsts[k];
2111
+ const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
2112
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2113
+ lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
2114
+ lines.push(...m.setup);
2115
+ lines.push(`${ind2} ${okV} = ${m.ok}`);
2116
+ lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
2117
+ lines.push(`${ind2}}`);
2118
+ });
2119
+ } else {
2120
+ const lbl = mint("_alt");
2121
+ lines.push(`${ind2}${lbl}: {`);
2122
+ for (const arm of shape.arms) {
2123
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2124
+ lines.push(...m.setup);
2125
+ lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
2126
+ }
2127
+ lines.push(`${ind2}}`);
2128
+ }
2129
+ return { setup: lines, ok: okV, end: endV2 };
2130
+ }
1623
2131
  if (shape.kind === "chars") {
1624
2132
  const cur = mint("_e");
1625
2133
  return {
@@ -1646,6 +2154,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1646
2154
  end: cur
1647
2155
  };
1648
2156
  }
2157
+ if (shape.kind === "seq") {
2158
+ const endV2 = mint("_e");
2159
+ const okV = mint("_ok");
2160
+ const runCond = (p, at) => {
2161
+ const c = classCond(`input.charCodeAt(${at})`, p.ranges);
2162
+ return p.negated ? `!(${c})` : `(${c})`;
2163
+ };
2164
+ const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
2165
+ let first = true;
2166
+ for (const p of shape.parts) {
2167
+ if (p.part === "lit") {
2168
+ const L = p.cps.length;
2169
+ const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
2170
+ if (p.optional) {
2171
+ lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
2172
+ } else {
2173
+ lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
2174
+ lines.push(`${ind2} ${endV2} += ${L}`);
2175
+ }
2176
+ } else if (p.part === "run" && !p.unbounded) {
2177
+ if (p.min === 1) {
2178
+ lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
2179
+ lines.push(`${ind2} ${endV2}++`);
2180
+ } else {
2181
+ lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2182
+ }
2183
+ } else if (p.part === "run") {
2184
+ const s = mint("_s");
2185
+ lines.push(`${ind2} const ${s} = ${endV2}`);
2186
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2187
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
2188
+ } else {
2189
+ const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
2190
+ if (!p.unbounded) {
2191
+ lines.push(...inner.setup);
2192
+ if (p.min === 1) {
2193
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2194
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2195
+ } else {
2196
+ lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
2197
+ }
2198
+ } else {
2199
+ const progV = mint("_prog");
2200
+ lines.push(`${ind2} let ${progV} = false`);
2201
+ lines.push(`${ind2} while (true) {`);
2202
+ lines.push(...inner.setup);
2203
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2204
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2205
+ lines.push(`${ind2} ${progV} = true`);
2206
+ lines.push(`${ind2} }`);
2207
+ if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
2208
+ }
2209
+ }
2210
+ first = false;
2211
+ }
2212
+ lines.push(`${ind2} ${okV} = true`);
2213
+ lines.push(`${ind2}} while (false)`);
2214
+ lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
2215
+ return { setup: lines, ok: okV, end: endV2 };
2216
+ }
2217
+ if (shape.kind === "litFold") {
2218
+ const openLen2 = shape.open.length;
2219
+ const endV2 = mint("_end");
2220
+ const checks = shape.open.map((cp, k) => {
2221
+ const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
2222
+ return foldEq(cVar, cp);
2223
+ });
2224
+ return {
2225
+ setup: [
2226
+ `${ind2}let ${endV2} = ${start}`,
2227
+ `${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
2228
+ ],
2229
+ ok: `${endV2} > ${start}`,
2230
+ end: endV2
2231
+ };
2232
+ }
1649
2233
  if (shape.kind === "until") {
1650
2234
  const j2 = mint("_j");
1651
2235
  const openLen2 = shape.open.length;
@@ -2041,7 +2625,7 @@ function hoistExpected(ctx, constArrSource) {
2041
2625
  let name = ctx.expectedMap.get(constArrSource);
2042
2626
  if (name === void 0) {
2043
2627
  name = `_fx${ctx.expectedDecls.length}`;
2044
- ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
2628
+ ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
2045
2629
  ctx.expectedMap.set(constArrSource, name);
2046
2630
  }
2047
2631
  return name;
@@ -2363,18 +2947,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
2363
2947
  return true;
2364
2948
  }
2365
2949
  }
2366
- function emitFallible(inner, ctx, pos) {
2950
+ function emitFallible(inner, ctx, pos, swallow = false) {
2367
2951
  const lbl = v(ctx, "_lbl");
2368
2952
  const okV = `${lbl}ok`;
2369
2953
  const valV = `${lbl}v`;
2370
2954
  const endV = `${lbl}e`;
2371
2955
  const savedLabel = ctx.failLabel;
2372
2956
  const savedIndent = ctx.indent;
2957
+ const savedRecord = ctx.recordFail;
2373
2958
  ctx.failLabel = lbl;
2374
2959
  ctx.indent = savedIndent + 1;
2960
+ if (swallow) ctx.recordFail = false;
2375
2961
  const r = emit(inner, ctx, pos);
2376
2962
  ctx.failLabel = savedLabel;
2377
2963
  ctx.indent = savedIndent;
2964
+ ctx.recordFail = savedRecord;
2378
2965
  const ind0 = ind(ctx);
2379
2966
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
2380
2967
  const mL = needsRollback ? v(ctx, "_fcl") : null;
@@ -2397,6 +2984,7 @@ function emitFallible(inner, ctx, pos) {
2397
2984
  ];
2398
2985
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
2399
2986
  }
2987
+ var CHARCODE_CHAIN_MAX = 16;
2400
2988
  function emitLit(def, ctx, pos) {
2401
2989
  const { value, caseInsensitive } = def;
2402
2990
  const len = value.length;
@@ -2419,7 +3007,7 @@ function emitLit(def, ctx, pos) {
2419
3007
  ...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
2420
3008
  `${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
2421
3009
  );
2422
- } else if (len <= 4) {
3010
+ } else if (len <= CHARCODE_CHAIN_MAX) {
2423
3011
  const checks = Array.from(
2424
3012
  { length: len },
2425
3013
  (_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
@@ -2441,7 +3029,55 @@ function emitLit(def, ctx, pos) {
2441
3029
  function escapeKeywordRe(s) {
2442
3030
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2443
3031
  }
3032
+ function emitKeywordsFast(def, ctx, pos) {
3033
+ if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
3034
+ if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
3035
+ if (def.caseInsensitive && def.boundary) return null;
3036
+ let boundary = null;
3037
+ if (def.boundary) {
3038
+ let body = def.boundary;
3039
+ const negated = body.startsWith("^");
3040
+ if (negated) body = body.slice(1);
3041
+ const ranges = parseClassRanges(body);
3042
+ if (!ranges) return null;
3043
+ boundary = { ranges, negated };
3044
+ }
3045
+ const mint = (prefix = "_v") => v(ctx, prefix);
3046
+ const lbl = v(ctx, "_kwLbl");
3047
+ const valV = v(ctx, "_kwv");
3048
+ const endV = v(ctx, "_kwe");
3049
+ const bodyInd = ind(ctx) + " ";
3050
+ const tries = [];
3051
+ for (const w of def.words) {
3052
+ const cps = Array.from(w, (ch) => ch.codePointAt(0));
3053
+ let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
3054
+ if (boundary) {
3055
+ shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
3056
+ }
3057
+ const m = emitShapeMatch(shape, pos, mint, bodyInd);
3058
+ tries.push(
3059
+ ...m.setup,
3060
+ // Slice from input rather than reusing the literal word: `caseInsensitive`
3061
+ // must return the text as it actually appeared (e.g. "ABC" for keyword
3062
+ // "abc"), matching what the original `RegExp.exec()[0]` returned.
3063
+ `${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
3064
+ );
3065
+ }
3066
+ const stmts = [
3067
+ `${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
3068
+ `${ind(ctx)}${lbl}: {`,
3069
+ ...tries,
3070
+ `${ind(ctx)}}`,
3071
+ // Every word has length >= 1 (checked above), so a real match always
3072
+ // advances past `pos` — `endV === pos` only happens when no candidate matched.
3073
+ ...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
3074
+ ];
3075
+ stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
3076
+ return { stmts, valueVar: valV, endVar: endV };
3077
+ }
2444
3078
  function emitKeywords(def, ctx, pos) {
3079
+ const fast = emitKeywordsFast(def, ctx, pos);
3080
+ if (fast) return fast;
2445
3081
  const alt = def.words.map(escapeKeywordRe).join("|");
2446
3082
  const boundary = def.boundary ? `(?![${def.boundary}])` : "";
2447
3083
  const flags = def.caseInsensitive ? "iuy" : "uy";
@@ -2572,6 +3208,31 @@ function failsAtStart(p) {
2572
3208
  return false;
2573
3209
  }
2574
3210
  }
3211
+ function armStaticExpected(ctx, p) {
3212
+ return hoistExpected(ctx, staticExpectedArr([p]));
3213
+ }
3214
+ function canMatchEmptyAtStart(p) {
3215
+ const d = p._def;
3216
+ switch (d.tag) {
3217
+ case "regex":
3218
+ return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
3219
+ case "optional":
3220
+ return true;
3221
+ case "many":
3222
+ return true;
3223
+ case "transform":
3224
+ case "label":
3225
+ return canMatchEmptyAtStart(d.parser);
3226
+ case "literal":
3227
+ return d.value.length === 0;
3228
+ default:
3229
+ return false;
3230
+ }
3231
+ }
3232
+ function needsFirstSetGuard(p) {
3233
+ const fs = p._meta.firstSet;
3234
+ return fs.kind !== "any" && !canMatchEmptyAtStart(p);
3235
+ }
2575
3236
  function emitDisjointArm(p, ctx, pos, valV, endV) {
2576
3237
  if (failsAtStart(p)) {
2577
3238
  const r = emit(p, ctx, pos);
@@ -2713,16 +3374,21 @@ function emitFirstMatch(def, ctx, pos) {
2713
3374
  const resValV = v(ctx, "_crv");
2714
3375
  const resEndV = v(ctx, "_cre");
2715
3376
  const resOkV = v(ctx, "_crok");
3377
+ const codeV = v(ctx, "_chcode");
2716
3378
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
2717
3379
  const ind0 = ind(ctx);
2718
3380
  const stmts = [
2719
3381
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
2720
- `${ind0}let ${slots.join(", ")}`
3382
+ `${ind0}let ${slots.join(", ")}`,
3383
+ `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
2721
3384
  ];
2722
3385
  for (let i = 0; i < def.parsers.length; i++) {
2723
3386
  const p = def.parsers[i];
2724
3387
  const gate = def.gates[i];
2725
3388
  const autoNot = def.autoNot[i];
3389
+ const atStart = failsAtStart(p);
3390
+ const staticFx = armStaticExpected(ctx, p);
3391
+ const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
2726
3392
  let gateCond = null;
2727
3393
  if (gate) {
2728
3394
  const gateIdx = ctx.mapFns.length;
@@ -2737,39 +3403,41 @@ function emitFirstMatch(def, ctx, pos) {
2737
3403
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
2738
3404
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
2739
3405
  const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
3406
+ const failSlot = atStart ? staticFx : "_ctx._fx";
2740
3407
  stmts.push(`${ind0}if (${skipCond}) {`);
2741
- ctx.indent++;
2742
- const ind1 = ind(ctx);
3408
+ if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
3409
+ ctx.indent += fsGuard ? 2 : 1;
2743
3410
  if (markLeaves) {
2744
3411
  stmts.push(
2745
- `${ind1}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
2746
- `${ind1}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
2747
- `${ind1}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
2748
- `${ind1}const ${markLog} = _ctx._triviaLog?.length ?? 0`
3412
+ `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
3413
+ `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
3414
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
3415
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
2749
3416
  );
2750
3417
  }
2751
- const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
3418
+ const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
2752
3419
  stmts.push(...armStmts);
2753
- ctx.indent--;
2754
3420
  if (autoNot && autoNot.length > 0) {
2755
3421
  const anCode = v(ctx, "_anc");
2756
3422
  const rejectCond = autoNot.map(
2757
3423
  (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
2758
3424
  ).join(" || ");
2759
- stmts.push(`${ind0} if (${okVar}) {`);
2760
- stmts.push(`${ind0} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
2761
- stmts.push(`${ind0} if (!(${rejectCond})) {`);
2762
- stmts.push(`${ind0} ${resValV} = ${valVar}`);
2763
- stmts.push(`${ind0} ${resEndV} = ${endVar}`);
2764
- stmts.push(`${ind0} ${resOkV} = true`);
2765
- stmts.push(`${ind0} }`);
2766
- stmts.push(`${ind0} }`);
2767
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx }`);
2768
- if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
3425
+ stmts.push(`${ind(ctx)}if (${okVar}) {`);
3426
+ stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
3427
+ stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
3428
+ stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
3429
+ stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
3430
+ stmts.push(`${ind(ctx)} ${resOkV} = true`);
3431
+ stmts.push(`${ind(ctx)} }`);
3432
+ stmts.push(`${ind(ctx)}}`);
3433
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
3434
+ if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
2769
3435
  } else {
2770
- stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2771
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
3436
+ stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
3437
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
2772
3438
  }
3439
+ ctx.indent -= fsGuard ? 2 : 1;
3440
+ if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
2773
3441
  stmts.push(`${ind0}}`);
2774
3442
  }
2775
3443
  const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
@@ -2802,7 +3470,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2802
3470
  function emitLiteralCondition(litVal, pos) {
2803
3471
  const len = litVal.length;
2804
3472
  if (len === 0) return "true";
2805
- if (len > 4) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
3473
+ if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
2806
3474
  const checks = [`${pos} + ${len} <= input.length`];
2807
3475
  for (let i = 0; i < len; i++) {
2808
3476
  const code = litVal.codePointAt(i);
@@ -2851,7 +3519,7 @@ function emitMany(def, ctx, pos) {
2851
3519
  itemPos = npV;
2852
3520
  }
2853
3521
  }
2854
- const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
3522
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
2855
3523
  stmts.push(
2856
3524
  ...iterStmts,
2857
3525
  `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
@@ -2865,7 +3533,7 @@ function emitMany(def, ctx, pos) {
2865
3533
  function emitOptional(def, ctx, pos) {
2866
3534
  const valV = v(ctx, "_opt");
2867
3535
  const endV = v(ctx, "_opte");
2868
- const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
3536
+ const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
2869
3537
  const ind0 = ind(ctx);
2870
3538
  const stmts = [
2871
3539
  ...lblStmts,
@@ -2877,7 +3545,7 @@ function emitOptional(def, ctx, pos) {
2877
3545
  function emitSepBy(_p, def, ctx, pos) {
2878
3546
  const arrV = v(ctx, "_arr");
2879
3547
  const curV = v(ctx, "_cur");
2880
- const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
3548
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
2881
3549
  const stmts = [
2882
3550
  `${ind(ctx)}const ${arrV} = []`,
2883
3551
  `${ind(ctx)}let ${curV} = ${pos}`,
@@ -2909,11 +3577,11 @@ function emitSepBy(_p, def, ctx, pos) {
2909
3577
  );
2910
3578
  sepAtPos = spV;
2911
3579
  const rollbackToSep = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
2912
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3580
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2913
3581
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
2914
3582
  const npV = v(ctx, "_np");
2915
3583
  stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
2916
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3584
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2917
3585
  stmts.push(
2918
3586
  ...nextStmts,
2919
3587
  // item failed → unwind the separator too, back to the end of the last item
@@ -2926,11 +3594,11 @@ function emitSepBy(_p, def, ctx, pos) {
2926
3594
  const spV = v(ctx, "_sp");
2927
3595
  stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
2928
3596
  sepAtPos = spV;
2929
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3597
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2930
3598
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2931
3599
  const npV = v(ctx, "_np");
2932
3600
  stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
2933
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3601
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2934
3602
  stmts.push(
2935
3603
  ...nextStmts,
2936
3604
  `${ind(ctx)}if (!${nextOk}) break`,
@@ -2947,10 +3615,10 @@ function emitSepBy(_p, def, ctx, pos) {
2947
3615
  `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
2948
3616
  );
2949
3617
  }
2950
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3618
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2951
3619
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2952
3620
  const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
2953
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
3621
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
2954
3622
  stmts.push(
2955
3623
  ...nextStmts,
2956
3624
  `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
@@ -3008,7 +3676,7 @@ function emitScanTo(def, ctx, pos) {
3008
3676
  return { stmts, valueVar: valV, endVar: curV };
3009
3677
  }
3010
3678
  function emitNot(def, ctx, pos) {
3011
- const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
3679
+ const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
3012
3680
  const label2 = JSON.stringify(`not(${def.parser._tag})`);
3013
3681
  return {
3014
3682
  stmts: [
@@ -3099,11 +3767,14 @@ function emitLazy(p, def, ctx, pos) {
3099
3767
  }
3100
3768
  const savedIndent = ctx.indent;
3101
3769
  const savedFailLabel = ctx.failLabel;
3770
+ const savedRecord = ctx.recordFail;
3102
3771
  ctx.indent = 1;
3103
3772
  ctx.failLabel = "_pfail";
3773
+ ctx.recordFail = true;
3104
3774
  const r = emit(resolved, ctx, "_pos");
3105
3775
  ctx.indent = savedIndent;
3106
3776
  ctx.failLabel = savedFailLabel;
3777
+ ctx.recordFail = savedRecord;
3107
3778
  pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
3108
3779
  }
3109
3780
  const fnName = ctx.namedParsers.get(p);
@@ -3283,11 +3954,14 @@ function emit(p, ctx, pos) {
3283
3954
  ctx.namedParsers.set(innerParser, fnName);
3284
3955
  const savedIndent = ctx.indent;
3285
3956
  const savedFailLabel = ctx.failLabel;
3957
+ const savedRecord = ctx.recordFail;
3286
3958
  ctx.indent = 1;
3287
3959
  ctx.failLabel = "_pfail";
3960
+ ctx.recordFail = true;
3288
3961
  const innerR = emit(innerParser, ctx, "_pos");
3289
3962
  ctx.indent = savedIndent;
3290
3963
  ctx.failLabel = savedFailLabel;
3964
+ ctx.recordFail = savedRecord;
3291
3965
  pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
3292
3966
  }
3293
3967
  const fn = ctx.namedParsers.get(innerParser);
@@ -3435,6 +4109,7 @@ function compile(combinator, mapFnSources) {
3435
4109
  regexMap: /* @__PURE__ */ new Map(),
3436
4110
  expectedDecls: [],
3437
4111
  expectedMap: /* @__PURE__ */ new Map(),
4112
+ recordFail: true,
3438
4113
  mapFns: [],
3439
4114
  mapFnSrcs: [],
3440
4115
  buildFns: [],
@@ -3804,7 +4479,7 @@ function graftAndShift(root, path, newNode, delta) {
3804
4479
  children: newChildren
3805
4480
  };
3806
4481
  }
3807
- var FunctionalDocImpl = class _FunctionalDocImpl {
4482
+ var ParseDocImpl = class _ParseDocImpl {
3808
4483
  _registry;
3809
4484
  _rootRule;
3810
4485
  _opts;
@@ -3821,7 +4496,7 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
3821
4496
  }
3822
4497
  edit(from, to, replacement) {
3823
4498
  const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
3824
- const reparse = () => makeFunctionalDoc(this._registry, this._rootRule, newInput, this._opts);
4499
+ const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
3825
4500
  if (!this.tree) return reparse();
3826
4501
  const delta = replacement.length - (to - from);
3827
4502
  const found = findContaining(this.tree, from);
@@ -3843,25 +4518,25 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
3843
4518
  if (r.span.end === node2.span.end + delta) {
3844
4519
  if (delta === 0) {
3845
4520
  const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
3846
- return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
4521
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
3847
4522
  }
3848
4523
  if (this._opts.rebuild) return reparse();
3849
4524
  const newTree = graftAndShift(this.tree, path, r.value, delta);
3850
- return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
4525
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
3851
4526
  }
3852
4527
  }
3853
4528
  return reparse();
3854
4529
  }
3855
4530
  };
3856
- function makeFunctionalDoc(registry, rootRule, input, opts = {}) {
4531
+ function parseDoc(registry, rootRule, input, opts = {}) {
3857
4532
  const ruleFn = registry[rootRule];
3858
4533
  if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
3859
4534
  const ctx = { trackLines: false, state: opts.state };
3860
4535
  const r = ruleFn(input, 0, ctx);
3861
4536
  if (r.ok) {
3862
- return new FunctionalDocImpl(registry, rootRule, opts, r.value, [], input);
4537
+ return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
3863
4538
  }
3864
- return new FunctionalDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
4539
+ return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
3865
4540
  }
3866
4541
 
3867
4542
  // src/cst/trivia-index.ts