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.js CHANGED
@@ -1399,8 +1399,29 @@ function expect(combinator, label2) {
1399
1399
  var BACKSLASH = 92;
1400
1400
  var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1401
1401
  var META = new Set("()[]{}*+?|^$.".split(""));
1402
+ var SPACE_RANGES = [
1403
+ [9, 13],
1404
+ [32, 32],
1405
+ [160, 160],
1406
+ [5760, 5760],
1407
+ [8192, 8202],
1408
+ [8232, 8232],
1409
+ [8233, 8233],
1410
+ [8239, 8239],
1411
+ [8287, 8287],
1412
+ [12288, 12288],
1413
+ [65279, 65279]
1414
+ ];
1402
1415
  function shorthandRanges(ch) {
1403
- return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
1416
+ if (ch === "d") return [[48, 57]];
1417
+ if (ch === "s") return SPACE_RANGES;
1418
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1419
+ }
1420
+ function readUnicodeEscape(body, i) {
1421
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1422
+ const hex = body.slice(i + 2, i + 6);
1423
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1424
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1404
1425
  }
1405
1426
  function parseClassRanges(body) {
1406
1427
  const ranges = [];
@@ -1409,11 +1430,16 @@ function parseClassRanges(body) {
1409
1430
  const ch = body[i];
1410
1431
  if (ch === void 0) return null;
1411
1432
  if (ch === "\\") {
1433
+ const uni = readUnicodeEscape(body, i);
1434
+ if (uni) {
1435
+ i = uni.next;
1436
+ return { cp: uni.cp };
1437
+ }
1412
1438
  const e = body[i + 1];
1413
1439
  if (e === void 0) return null;
1414
1440
  i += 2;
1415
1441
  if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1416
- if (e === "d" || e === "w") return { set: shorthandRanges(e) };
1442
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1417
1443
  if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1418
1444
  return { cp: e.codePointAt(0) };
1419
1445
  }
@@ -1441,10 +1467,16 @@ function parseClassRanges(body) {
1441
1467
  function classToRanges(cls) {
1442
1468
  if (cls === "\\d") return shorthandRanges("d");
1443
1469
  if (cls === "\\w") return shorthandRanges("w");
1470
+ if (cls === "\\s") return shorthandRanges("s");
1444
1471
  const body = cls.slice(1, -1);
1445
1472
  if (body.startsWith("^")) return null;
1446
1473
  return parseClassRanges(body);
1447
1474
  }
1475
+ var foldEq = (cVar, cp) => {
1476
+ if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
1477
+ if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
1478
+ return `${cVar} === ${cp}`;
1479
+ };
1448
1480
  function literalCodePoints(frag) {
1449
1481
  const out = [];
1450
1482
  let i = 0;
@@ -1491,22 +1523,446 @@ function parseStringShape(source) {
1491
1523
  if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
1492
1524
  return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
1493
1525
  }
1526
+ function parseSeqParts(source) {
1527
+ const parts = [];
1528
+ let lit = [];
1529
+ const flush = () => {
1530
+ if (lit.length) {
1531
+ parts.push({ part: "lit", cps: lit, optional: false });
1532
+ lit = [];
1533
+ }
1534
+ };
1535
+ const runFrom = (ranges, negated, q) => {
1536
+ if (q === "+") {
1537
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
1538
+ return 1;
1539
+ }
1540
+ if (q === "*") {
1541
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
1542
+ return 1;
1543
+ }
1544
+ if (q === "?") {
1545
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
1546
+ return 1;
1547
+ }
1548
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
1549
+ return 0;
1550
+ };
1551
+ let i = 0;
1552
+ while (i < source.length) {
1553
+ const ch = source[i];
1554
+ if (ch === "[") {
1555
+ let k = i + 1;
1556
+ const neg = source[k] === "^";
1557
+ if (neg) k++;
1558
+ let body = "";
1559
+ while (k < source.length && source[k] !== "]") {
1560
+ if (source[k] === "\\") {
1561
+ body += source[k] + (source[k + 1] ?? "");
1562
+ k += 2;
1563
+ } else {
1564
+ body += source[k];
1565
+ k++;
1566
+ }
1567
+ }
1568
+ if (source[k] !== "]") return null;
1569
+ const ranges = parseClassRanges(body);
1570
+ if (!ranges) return null;
1571
+ flush();
1572
+ i = k + 1;
1573
+ i += runFrom(ranges, neg, source[i]);
1574
+ continue;
1575
+ }
1576
+ if (ch === "\\") {
1577
+ const e = source[i + 1];
1578
+ if (e === "d" || e === "w" || e === "s") {
1579
+ flush();
1580
+ i += 2;
1581
+ i += runFrom(shorthandRanges(e), false, source[i]);
1582
+ continue;
1583
+ }
1584
+ const uni = readUnicodeEscape(source, i);
1585
+ let cp2;
1586
+ if (uni) {
1587
+ cp2 = uni.cp;
1588
+ i = uni.next;
1589
+ } else if (e === void 0) return null;
1590
+ else if (e in CLASS_ESCAPES) {
1591
+ cp2 = CLASS_ESCAPES[e];
1592
+ i += 2;
1593
+ } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1594
+ else {
1595
+ cp2 = e.codePointAt(0);
1596
+ i += 2;
1597
+ }
1598
+ if (source[i] === "?") {
1599
+ flush();
1600
+ parts.push({ part: "lit", cps: [cp2], optional: true });
1601
+ i++;
1602
+ } else lit.push(cp2);
1603
+ continue;
1604
+ }
1605
+ if (ch === "(") {
1606
+ if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
1607
+ let k = i + 3;
1608
+ let depth = 1;
1609
+ while (k < source.length && depth > 0) {
1610
+ const c2 = source[k];
1611
+ if (c2 === "\\") {
1612
+ k += 2;
1613
+ continue;
1614
+ }
1615
+ if (c2 === "[") {
1616
+ let j = k + 1;
1617
+ if (source[j] === "^") j++;
1618
+ while (j < source.length && source[j] !== "]") {
1619
+ if (source[j] === "\\") j += 2;
1620
+ else j++;
1621
+ }
1622
+ if (source[j] !== "]") return null;
1623
+ k = j + 1;
1624
+ continue;
1625
+ }
1626
+ if (c2 === "(") depth++;
1627
+ else if (c2 === ")") depth--;
1628
+ k++;
1629
+ }
1630
+ if (depth !== 0) return null;
1631
+ const body = source.slice(i + 3, k - 1);
1632
+ const inner = parseScanShape(body);
1633
+ if (!inner || !groupInnerSafe(inner)) return null;
1634
+ const q = source[k];
1635
+ if (q === "{") return null;
1636
+ flush();
1637
+ if (q === "+") {
1638
+ if (shapeCanBeEmpty(inner)) return null;
1639
+ parts.push({ part: "group", inner, min: 1, unbounded: true });
1640
+ i = k + 1;
1641
+ } else if (q === "*") {
1642
+ if (shapeCanBeEmpty(inner)) return null;
1643
+ parts.push({ part: "group", inner, min: 0, unbounded: true });
1644
+ i = k + 1;
1645
+ } else if (q === "?") {
1646
+ parts.push({ part: "group", inner, min: 0, unbounded: false });
1647
+ i = k + 1;
1648
+ } else {
1649
+ parts.push({ part: "group", inner, min: 1, unbounded: false });
1650
+ i = k;
1651
+ }
1652
+ continue;
1653
+ }
1654
+ if (META.has(ch)) return null;
1655
+ const cp = ch.codePointAt(0);
1656
+ i += ch.length;
1657
+ if (source[i] === "?") {
1658
+ flush();
1659
+ parts.push({ part: "lit", cps: [cp], optional: true });
1660
+ i++;
1661
+ } else lit.push(cp);
1662
+ }
1663
+ flush();
1664
+ return parts.length ? parts : null;
1665
+ }
1666
+ function seqIsUnambiguous(parts) {
1667
+ let hasMandatory = false;
1668
+ for (let k = 0; k < parts.length; k++) {
1669
+ const p = parts[k];
1670
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1671
+ if (mandatory) hasMandatory = true;
1672
+ const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
1673
+ const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
1674
+ if (skippableSingle || isUnboundedRepeat) {
1675
+ const rest = seqFirstAccept(parts.slice(k + 1));
1676
+ if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
1677
+ }
1678
+ }
1679
+ return hasMandatory;
1680
+ }
1681
+ function rangesOverlap(a, b) {
1682
+ return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
1683
+ }
1684
+ function rangesSubset(sub, sup) {
1685
+ return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
1686
+ }
1687
+ function parseClassOperand(body) {
1688
+ if (body === "\\d" || body === "\\w" || body === "\\s") {
1689
+ return { ranges: shorthandRanges(body[1]), negated: false };
1690
+ }
1691
+ if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1692
+ let inner = body.slice(1, -1);
1693
+ const negated = inner.startsWith("^");
1694
+ if (negated) inner = inner.slice(1);
1695
+ const ranges = parseClassRanges(inner);
1696
+ return ranges ? { ranges, negated } : null;
1697
+ }
1698
+ const cps = literalCodePoints(body);
1699
+ if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
1700
+ return null;
1701
+ }
1702
+ function classSubset(t, u) {
1703
+ if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
1704
+ if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
1705
+ if (t.negated && !u.negated) return false;
1706
+ return rangesSubset(u.ranges, t.ranges);
1707
+ }
1708
+ function classDisjoint(t, u) {
1709
+ if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
1710
+ if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
1711
+ if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
1712
+ return false;
1713
+ }
1714
+ function firstSetsDisjoint(a, b) {
1715
+ if (a === "any" || b === "any") return false;
1716
+ return classDisjoint(a, b);
1717
+ }
1718
+ function trailingBacktrackClass(shape) {
1719
+ if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
1720
+ if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
1721
+ if (shape.kind === "litFold") return null;
1722
+ if (shape.kind === "seq") {
1723
+ const last = shape.parts[shape.parts.length - 1];
1724
+ if (last.part === "lit") return null;
1725
+ if (last.part === "run") {
1726
+ if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
1727
+ return null;
1728
+ }
1729
+ const innerWiggle = trailingBacktrackClass(last.inner);
1730
+ if (innerWiggle === "unsupported") return "unsupported";
1731
+ const requiredOnce = !last.unbounded && last.min === 1;
1732
+ if (requiredOnce) return innerWiggle;
1733
+ const dropExposed = shapeFirstAccept(last.inner);
1734
+ if (dropExposed === "any") return "unsupported";
1735
+ if (innerWiggle === null) return dropExposed;
1736
+ const unioned = unionCharSets([innerWiggle, dropExposed]);
1737
+ return unioned === "any" ? "unsupported" : unioned;
1738
+ }
1739
+ if (shape.kind === "alt") {
1740
+ if (!shape.disjoint) return "unsupported";
1741
+ const classes = shape.arms.map(trailingBacktrackClass);
1742
+ if (classes.some((c) => c === "unsupported")) return "unsupported";
1743
+ const concrete = classes.filter((c) => c !== null);
1744
+ if (concrete.length === 0) return null;
1745
+ const unioned = unionCharSets(concrete);
1746
+ return unioned === "any" ? "unsupported" : unioned;
1747
+ }
1748
+ return "unsupported";
1749
+ }
1750
+ function lookaheadUnambiguous(inner, operand, negative) {
1751
+ const tail = trailingBacktrackClass(inner);
1752
+ if (tail === "unsupported") return false;
1753
+ if (tail === null) return true;
1754
+ return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
1755
+ }
1756
+ function stripTrailingLookahead(source) {
1757
+ if (!source.endsWith(")")) return null;
1758
+ for (const negative of [true, false]) {
1759
+ const lead = negative ? "(?!" : "(?=";
1760
+ const idx = source.lastIndexOf(lead);
1761
+ if (idx === -1) continue;
1762
+ const body = source.slice(idx + lead.length, -1);
1763
+ const operand = body.length ? parseClassOperand(body) : null;
1764
+ if (!operand) continue;
1765
+ return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
1766
+ }
1767
+ return null;
1768
+ }
1769
+ function unwrapOuterGroup(source) {
1770
+ while (source.startsWith("(?:")) {
1771
+ let depth = 1;
1772
+ let i = 3;
1773
+ let ok = true;
1774
+ while (i < source.length && depth > 0) {
1775
+ const ch = source[i];
1776
+ if (ch === "\\") {
1777
+ i += 2;
1778
+ continue;
1779
+ }
1780
+ if (ch === "[") {
1781
+ let j = i + 1;
1782
+ if (source[j] === "^") j++;
1783
+ while (j < source.length && source[j] !== "]") {
1784
+ if (source[j] === "\\") j += 2;
1785
+ else j++;
1786
+ }
1787
+ if (source[j] !== "]") {
1788
+ ok = false;
1789
+ break;
1790
+ }
1791
+ i = j + 1;
1792
+ continue;
1793
+ }
1794
+ if (ch === "(") depth++;
1795
+ else if (ch === ")") depth--;
1796
+ i++;
1797
+ }
1798
+ if (!ok || depth !== 0 || i !== source.length) break;
1799
+ source = source.slice(3, -1);
1800
+ }
1801
+ return source;
1802
+ }
1803
+ function splitTopLevelAlternation(source) {
1804
+ const body = unwrapOuterGroup(source);
1805
+ const parts = [];
1806
+ let depth = 0;
1807
+ let last = 0;
1808
+ let i = 0;
1809
+ while (i < body.length) {
1810
+ const ch = body[i];
1811
+ if (ch === "\\") {
1812
+ i += 2;
1813
+ continue;
1814
+ }
1815
+ if (ch === "[") {
1816
+ let j = i + 1;
1817
+ if (body[j] === "^") j++;
1818
+ while (j < body.length && body[j] !== "]") {
1819
+ if (body[j] === "\\") j += 2;
1820
+ else j++;
1821
+ }
1822
+ if (body[j] !== "]") return null;
1823
+ i = j + 1;
1824
+ continue;
1825
+ }
1826
+ if (ch === "(") {
1827
+ depth++;
1828
+ i++;
1829
+ continue;
1830
+ }
1831
+ if (ch === ")") {
1832
+ depth--;
1833
+ if (depth < 0) return null;
1834
+ i++;
1835
+ continue;
1836
+ }
1837
+ if (ch === "|" && depth === 0) {
1838
+ parts.push(body.slice(last, i));
1839
+ last = i + 1;
1840
+ i++;
1841
+ continue;
1842
+ }
1843
+ i++;
1844
+ }
1845
+ if (depth !== 0) return null;
1846
+ parts.push(body.slice(last));
1847
+ if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
1848
+ return parts;
1849
+ }
1850
+ function shapeCanBeEmpty(shape) {
1851
+ if (shape.kind === "chars") return !shape.minOne;
1852
+ if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
1853
+ if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
1854
+ return false;
1855
+ }
1856
+ function unionCharSets(sets) {
1857
+ if (sets.some((s) => s === "any")) return "any";
1858
+ const concrete = sets;
1859
+ if (concrete.length === 1) return concrete[0];
1860
+ if (concrete.some((s) => s.negated)) return "any";
1861
+ return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
1862
+ }
1863
+ function partFirstAccept(part) {
1864
+ if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
1865
+ if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
1866
+ return shapeFirstAccept(part.inner);
1867
+ }
1868
+ function seqFirstAccept(parts) {
1869
+ const sets = [];
1870
+ for (const p of parts) {
1871
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1872
+ sets.push(partFirstAccept(p));
1873
+ if (mandatory) break;
1874
+ }
1875
+ return unionCharSets(sets);
1876
+ }
1877
+ function groupInnerSafe(shape) {
1878
+ if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
1879
+ if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
1880
+ if (shape.kind === "alt") return shape.disjoint;
1881
+ return false;
1882
+ }
1883
+ function shapeFirstAccept(shape) {
1884
+ if (shapeCanBeEmpty(shape)) return "any";
1885
+ switch (shape.kind) {
1886
+ case "chars":
1887
+ return { ranges: shape.ranges, negated: false };
1888
+ case "ident":
1889
+ return { ranges: shape.head, negated: false };
1890
+ case "until":
1891
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1892
+ case "delimited":
1893
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1894
+ case "string":
1895
+ return { ranges: [[shape.quote, shape.quote]], negated: false };
1896
+ case "litFold": {
1897
+ const cp = shape.open[0];
1898
+ if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
1899
+ if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
1900
+ return { ranges: [[cp, cp]], negated: false };
1901
+ }
1902
+ case "seq":
1903
+ return seqFirstAccept(shape.parts);
1904
+ case "lookahead":
1905
+ return shapeFirstAccept(shape.inner);
1906
+ // zero-width — doesn't change first-char acceptance
1907
+ case "alt": {
1908
+ if (!shape.disjoint) return "any";
1909
+ const sets = shape.firsts.filter((f) => f !== null);
1910
+ return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
1911
+ }
1912
+ }
1913
+ }
1914
+ function allPairsDisjoint(sets) {
1915
+ for (let i = 0; i < sets.length; i++) {
1916
+ const a = sets[i];
1917
+ if (a === "any") return false;
1918
+ for (let j = i + 1; j < sets.length; j++) {
1919
+ const b = sets[j];
1920
+ if (b === "any" || !classDisjoint(a, b)) return false;
1921
+ }
1922
+ }
1923
+ return true;
1924
+ }
1925
+ function parseAlternation(source) {
1926
+ const parts = splitTopLevelAlternation(source);
1927
+ if (!parts) return null;
1928
+ const arms = [];
1929
+ for (const p of parts) {
1930
+ const arm = parseScanShape(p);
1931
+ if (!arm) return null;
1932
+ arms.push(arm);
1933
+ }
1934
+ const firstSets = arms.map(shapeFirstAccept);
1935
+ const disjoint = allPairsDisjoint(firstSets);
1936
+ return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
1937
+ }
1494
1938
  function parseScanShape(source) {
1939
+ const alt = parseAlternation(source);
1940
+ if (alt) return alt;
1941
+ const la = stripTrailingLookahead(source);
1942
+ if (la) {
1943
+ const inner = parseScanShapeCore(la.base);
1944
+ if (!inner) return null;
1945
+ const operand = { ranges: la.ranges, negated: la.classNegated };
1946
+ if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
1947
+ return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
1948
+ }
1949
+ return parseScanShapeCore(source);
1950
+ }
1951
+ function parseScanShapeCore(source) {
1495
1952
  let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1496
- if (m) {
1497
- if (m[1].startsWith("^")) return null;
1953
+ if (m && !m[1].startsWith("^")) {
1498
1954
  const ranges = parseClassRanges(m[1]);
1499
1955
  return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
1500
1956
  }
1501
- m = /^\\([dw])([+*])$/.exec(source);
1957
+ m = /^\\([dws])([+*])$/.exec(source);
1502
1958
  if (m) {
1503
1959
  return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
1504
1960
  }
1505
- m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
1961
+ m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
1506
1962
  if (m) {
1507
1963
  const head = classToRanges(m[1]);
1508
1964
  const tail = classToRanges(m[2]);
1509
- return head && tail ? { kind: "ident", head, tail } : null;
1965
+ if (head && tail) return { kind: "ident", head, tail };
1510
1966
  }
1511
1967
  const str = parseStringShape(source);
1512
1968
  if (str) return str;
@@ -1515,7 +1971,6 @@ function parseScanShape(source) {
1515
1971
  const open = literalCodePoints(m[1]);
1516
1972
  const stop = parseClassRanges(m[2]);
1517
1973
  if (open && stop) return { kind: "until", open, stop };
1518
- return null;
1519
1974
  }
1520
1975
  if (!source.includes("\\\\")) {
1521
1976
  m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
@@ -1525,10 +1980,16 @@ function parseScanShape(source) {
1525
1980
  if (open && close) return { kind: "delimited", open, close };
1526
1981
  }
1527
1982
  }
1983
+ const parts = parseSeqParts(source);
1984
+ if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
1528
1985
  return null;
1529
1986
  }
1530
1987
  function scanShapeFromRegex(source, flags) {
1531
- if (/[imsu]/.test(flags)) return null;
1988
+ if (/[msu]/.test(flags)) return null;
1989
+ if (/i/.test(flags)) {
1990
+ const open = literalCodePoints(source);
1991
+ return open ? { kind: "litFold", open } : null;
1992
+ }
1532
1993
  return parseScanShape(source);
1533
1994
  }
1534
1995
  var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
@@ -1543,6 +2004,53 @@ function stringHardStop(shape) {
1543
2004
  }
1544
2005
  var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
1545
2006
  function emitShapeMatch(shape, start, mint, ind2, firstChar) {
2007
+ if (shape.kind === "lookahead") {
2008
+ const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
2009
+ const okV = mint("_ok");
2010
+ const endV2 = mint("_end");
2011
+ const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
2012
+ const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
2013
+ const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
2014
+ const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
2015
+ return {
2016
+ setup: [
2017
+ ...inner.setup,
2018
+ `${ind2}let ${okV} = ${inner.ok}`,
2019
+ `${ind2}let ${endV2} = ${inner.end}`,
2020
+ `${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
2021
+ ],
2022
+ ok: okV,
2023
+ end: endV2
2024
+ };
2025
+ }
2026
+ if (shape.kind === "alt") {
2027
+ const okV = mint("_ok");
2028
+ const endV2 = mint("_end");
2029
+ const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
2030
+ if (shape.disjoint) {
2031
+ const c0 = codeAt(start, firstChar);
2032
+ shape.arms.forEach((arm, k) => {
2033
+ const fs = shape.firsts[k];
2034
+ const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
2035
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2036
+ lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
2037
+ lines.push(...m.setup);
2038
+ lines.push(`${ind2} ${okV} = ${m.ok}`);
2039
+ lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
2040
+ lines.push(`${ind2}}`);
2041
+ });
2042
+ } else {
2043
+ const lbl = mint("_alt");
2044
+ lines.push(`${ind2}${lbl}: {`);
2045
+ for (const arm of shape.arms) {
2046
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2047
+ lines.push(...m.setup);
2048
+ lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
2049
+ }
2050
+ lines.push(`${ind2}}`);
2051
+ }
2052
+ return { setup: lines, ok: okV, end: endV2 };
2053
+ }
1546
2054
  if (shape.kind === "chars") {
1547
2055
  const cur = mint("_e");
1548
2056
  return {
@@ -1569,6 +2077,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1569
2077
  end: cur
1570
2078
  };
1571
2079
  }
2080
+ if (shape.kind === "seq") {
2081
+ const endV2 = mint("_e");
2082
+ const okV = mint("_ok");
2083
+ const runCond = (p, at) => {
2084
+ const c = classCond(`input.charCodeAt(${at})`, p.ranges);
2085
+ return p.negated ? `!(${c})` : `(${c})`;
2086
+ };
2087
+ const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
2088
+ let first = true;
2089
+ for (const p of shape.parts) {
2090
+ if (p.part === "lit") {
2091
+ const L = p.cps.length;
2092
+ const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
2093
+ if (p.optional) {
2094
+ lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
2095
+ } else {
2096
+ lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
2097
+ lines.push(`${ind2} ${endV2} += ${L}`);
2098
+ }
2099
+ } else if (p.part === "run" && !p.unbounded) {
2100
+ if (p.min === 1) {
2101
+ lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
2102
+ lines.push(`${ind2} ${endV2}++`);
2103
+ } else {
2104
+ lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2105
+ }
2106
+ } else if (p.part === "run") {
2107
+ const s = mint("_s");
2108
+ lines.push(`${ind2} const ${s} = ${endV2}`);
2109
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2110
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
2111
+ } else {
2112
+ const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
2113
+ if (!p.unbounded) {
2114
+ lines.push(...inner.setup);
2115
+ if (p.min === 1) {
2116
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2117
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2118
+ } else {
2119
+ lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
2120
+ }
2121
+ } else {
2122
+ const progV = mint("_prog");
2123
+ lines.push(`${ind2} let ${progV} = false`);
2124
+ lines.push(`${ind2} while (true) {`);
2125
+ lines.push(...inner.setup);
2126
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2127
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2128
+ lines.push(`${ind2} ${progV} = true`);
2129
+ lines.push(`${ind2} }`);
2130
+ if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
2131
+ }
2132
+ }
2133
+ first = false;
2134
+ }
2135
+ lines.push(`${ind2} ${okV} = true`);
2136
+ lines.push(`${ind2}} while (false)`);
2137
+ lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
2138
+ return { setup: lines, ok: okV, end: endV2 };
2139
+ }
2140
+ if (shape.kind === "litFold") {
2141
+ const openLen2 = shape.open.length;
2142
+ const endV2 = mint("_end");
2143
+ const checks = shape.open.map((cp, k) => {
2144
+ const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
2145
+ return foldEq(cVar, cp);
2146
+ });
2147
+ return {
2148
+ setup: [
2149
+ `${ind2}let ${endV2} = ${start}`,
2150
+ `${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
2151
+ ],
2152
+ ok: `${endV2} > ${start}`,
2153
+ end: endV2
2154
+ };
2155
+ }
1572
2156
  if (shape.kind === "until") {
1573
2157
  const j2 = mint("_j");
1574
2158
  const openLen2 = shape.open.length;
@@ -1964,7 +2548,7 @@ function hoistExpected(ctx, constArrSource) {
1964
2548
  let name = ctx.expectedMap.get(constArrSource);
1965
2549
  if (name === void 0) {
1966
2550
  name = `_fx${ctx.expectedDecls.length}`;
1967
- ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
2551
+ ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
1968
2552
  ctx.expectedMap.set(constArrSource, name);
1969
2553
  }
1970
2554
  return name;
@@ -2286,18 +2870,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
2286
2870
  return true;
2287
2871
  }
2288
2872
  }
2289
- function emitFallible(inner, ctx, pos) {
2873
+ function emitFallible(inner, ctx, pos, swallow = false) {
2290
2874
  const lbl = v(ctx, "_lbl");
2291
2875
  const okV = `${lbl}ok`;
2292
2876
  const valV = `${lbl}v`;
2293
2877
  const endV = `${lbl}e`;
2294
2878
  const savedLabel = ctx.failLabel;
2295
2879
  const savedIndent = ctx.indent;
2880
+ const savedRecord = ctx.recordFail;
2296
2881
  ctx.failLabel = lbl;
2297
2882
  ctx.indent = savedIndent + 1;
2883
+ if (swallow) ctx.recordFail = false;
2298
2884
  const r = emit(inner, ctx, pos);
2299
2885
  ctx.failLabel = savedLabel;
2300
2886
  ctx.indent = savedIndent;
2887
+ ctx.recordFail = savedRecord;
2301
2888
  const ind0 = ind(ctx);
2302
2889
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
2303
2890
  const mL = needsRollback ? v(ctx, "_fcl") : null;
@@ -2320,6 +2907,7 @@ function emitFallible(inner, ctx, pos) {
2320
2907
  ];
2321
2908
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
2322
2909
  }
2910
+ var CHARCODE_CHAIN_MAX = 16;
2323
2911
  function emitLit(def, ctx, pos) {
2324
2912
  const { value, caseInsensitive } = def;
2325
2913
  const len = value.length;
@@ -2342,7 +2930,7 @@ function emitLit(def, ctx, pos) {
2342
2930
  ...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
2343
2931
  `${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
2344
2932
  );
2345
- } else if (len <= 4) {
2933
+ } else if (len <= CHARCODE_CHAIN_MAX) {
2346
2934
  const checks = Array.from(
2347
2935
  { length: len },
2348
2936
  (_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
@@ -2364,7 +2952,55 @@ function emitLit(def, ctx, pos) {
2364
2952
  function escapeKeywordRe(s) {
2365
2953
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2366
2954
  }
2955
+ function emitKeywordsFast(def, ctx, pos) {
2956
+ if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
2957
+ if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
2958
+ if (def.caseInsensitive && def.boundary) return null;
2959
+ let boundary = null;
2960
+ if (def.boundary) {
2961
+ let body = def.boundary;
2962
+ const negated = body.startsWith("^");
2963
+ if (negated) body = body.slice(1);
2964
+ const ranges = parseClassRanges(body);
2965
+ if (!ranges) return null;
2966
+ boundary = { ranges, negated };
2967
+ }
2968
+ const mint = (prefix = "_v") => v(ctx, prefix);
2969
+ const lbl = v(ctx, "_kwLbl");
2970
+ const valV = v(ctx, "_kwv");
2971
+ const endV = v(ctx, "_kwe");
2972
+ const bodyInd = ind(ctx) + " ";
2973
+ const tries = [];
2974
+ for (const w of def.words) {
2975
+ const cps = Array.from(w, (ch) => ch.codePointAt(0));
2976
+ let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
2977
+ if (boundary) {
2978
+ shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
2979
+ }
2980
+ const m = emitShapeMatch(shape, pos, mint, bodyInd);
2981
+ tries.push(
2982
+ ...m.setup,
2983
+ // Slice from input rather than reusing the literal word: `caseInsensitive`
2984
+ // must return the text as it actually appeared (e.g. "ABC" for keyword
2985
+ // "abc"), matching what the original `RegExp.exec()[0]` returned.
2986
+ `${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
2987
+ );
2988
+ }
2989
+ const stmts = [
2990
+ `${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
2991
+ `${ind(ctx)}${lbl}: {`,
2992
+ ...tries,
2993
+ `${ind(ctx)}}`,
2994
+ // Every word has length >= 1 (checked above), so a real match always
2995
+ // advances past `pos` — `endV === pos` only happens when no candidate matched.
2996
+ ...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
2997
+ ];
2998
+ stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
2999
+ return { stmts, valueVar: valV, endVar: endV };
3000
+ }
2367
3001
  function emitKeywords(def, ctx, pos) {
3002
+ const fast = emitKeywordsFast(def, ctx, pos);
3003
+ if (fast) return fast;
2368
3004
  const alt = def.words.map(escapeKeywordRe).join("|");
2369
3005
  const boundary = def.boundary ? `(?![${def.boundary}])` : "";
2370
3006
  const flags = def.caseInsensitive ? "iuy" : "uy";
@@ -2495,6 +3131,31 @@ function failsAtStart(p) {
2495
3131
  return false;
2496
3132
  }
2497
3133
  }
3134
+ function armStaticExpected(ctx, p) {
3135
+ return hoistExpected(ctx, staticExpectedArr([p]));
3136
+ }
3137
+ function canMatchEmptyAtStart(p) {
3138
+ const d = p._def;
3139
+ switch (d.tag) {
3140
+ case "regex":
3141
+ return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
3142
+ case "optional":
3143
+ return true;
3144
+ case "many":
3145
+ return true;
3146
+ case "transform":
3147
+ case "label":
3148
+ return canMatchEmptyAtStart(d.parser);
3149
+ case "literal":
3150
+ return d.value.length === 0;
3151
+ default:
3152
+ return false;
3153
+ }
3154
+ }
3155
+ function needsFirstSetGuard(p) {
3156
+ const fs = p._meta.firstSet;
3157
+ return fs.kind !== "any" && !canMatchEmptyAtStart(p);
3158
+ }
2498
3159
  function emitDisjointArm(p, ctx, pos, valV, endV) {
2499
3160
  if (failsAtStart(p)) {
2500
3161
  const r = emit(p, ctx, pos);
@@ -2636,16 +3297,21 @@ function emitFirstMatch(def, ctx, pos) {
2636
3297
  const resValV = v(ctx, "_crv");
2637
3298
  const resEndV = v(ctx, "_cre");
2638
3299
  const resOkV = v(ctx, "_crok");
3300
+ const codeV = v(ctx, "_chcode");
2639
3301
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
2640
3302
  const ind0 = ind(ctx);
2641
3303
  const stmts = [
2642
3304
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
2643
- `${ind0}let ${slots.join(", ")}`
3305
+ `${ind0}let ${slots.join(", ")}`,
3306
+ `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
2644
3307
  ];
2645
3308
  for (let i = 0; i < def.parsers.length; i++) {
2646
3309
  const p = def.parsers[i];
2647
3310
  const gate = def.gates[i];
2648
3311
  const autoNot = def.autoNot[i];
3312
+ const atStart = failsAtStart(p);
3313
+ const staticFx = armStaticExpected(ctx, p);
3314
+ const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
2649
3315
  let gateCond = null;
2650
3316
  if (gate) {
2651
3317
  const gateIdx = ctx.mapFns.length;
@@ -2660,39 +3326,41 @@ function emitFirstMatch(def, ctx, pos) {
2660
3326
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
2661
3327
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
2662
3328
  const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
3329
+ const failSlot = atStart ? staticFx : "_ctx._fx";
2663
3330
  stmts.push(`${ind0}if (${skipCond}) {`);
2664
- ctx.indent++;
2665
- const ind1 = ind(ctx);
3331
+ if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
3332
+ ctx.indent += fsGuard ? 2 : 1;
2666
3333
  if (markLeaves) {
2667
3334
  stmts.push(
2668
- `${ind1}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
2669
- `${ind1}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
2670
- `${ind1}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
2671
- `${ind1}const ${markLog} = _ctx._triviaLog?.length ?? 0`
3335
+ `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
3336
+ `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
3337
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
3338
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
2672
3339
  );
2673
3340
  }
2674
- const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
3341
+ const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
2675
3342
  stmts.push(...armStmts);
2676
- ctx.indent--;
2677
3343
  if (autoNot && autoNot.length > 0) {
2678
3344
  const anCode = v(ctx, "_anc");
2679
3345
  const rejectCond = autoNot.map(
2680
3346
  (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
2681
3347
  ).join(" || ");
2682
- stmts.push(`${ind0} if (${okVar}) {`);
2683
- stmts.push(`${ind0} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
2684
- stmts.push(`${ind0} if (!(${rejectCond})) {`);
2685
- stmts.push(`${ind0} ${resValV} = ${valVar}`);
2686
- stmts.push(`${ind0} ${resEndV} = ${endVar}`);
2687
- stmts.push(`${ind0} ${resOkV} = true`);
2688
- stmts.push(`${ind0} }`);
2689
- stmts.push(`${ind0} }`);
2690
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx }`);
2691
- if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
3348
+ stmts.push(`${ind(ctx)}if (${okVar}) {`);
3349
+ stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
3350
+ stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
3351
+ stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
3352
+ stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
3353
+ stmts.push(`${ind(ctx)} ${resOkV} = true`);
3354
+ stmts.push(`${ind(ctx)} }`);
3355
+ stmts.push(`${ind(ctx)}}`);
3356
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
3357
+ if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
2692
3358
  } else {
2693
- stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2694
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
3359
+ stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
3360
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
2695
3361
  }
3362
+ ctx.indent -= fsGuard ? 2 : 1;
3363
+ if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
2696
3364
  stmts.push(`${ind0}}`);
2697
3365
  }
2698
3366
  const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
@@ -2725,7 +3393,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2725
3393
  function emitLiteralCondition(litVal, pos) {
2726
3394
  const len = litVal.length;
2727
3395
  if (len === 0) return "true";
2728
- if (len > 4) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
3396
+ if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
2729
3397
  const checks = [`${pos} + ${len} <= input.length`];
2730
3398
  for (let i = 0; i < len; i++) {
2731
3399
  const code = litVal.codePointAt(i);
@@ -2774,7 +3442,7 @@ function emitMany(def, ctx, pos) {
2774
3442
  itemPos = npV;
2775
3443
  }
2776
3444
  }
2777
- const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
3445
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
2778
3446
  stmts.push(
2779
3447
  ...iterStmts,
2780
3448
  `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
@@ -2788,7 +3456,7 @@ function emitMany(def, ctx, pos) {
2788
3456
  function emitOptional(def, ctx, pos) {
2789
3457
  const valV = v(ctx, "_opt");
2790
3458
  const endV = v(ctx, "_opte");
2791
- const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
3459
+ const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
2792
3460
  const ind0 = ind(ctx);
2793
3461
  const stmts = [
2794
3462
  ...lblStmts,
@@ -2800,7 +3468,7 @@ function emitOptional(def, ctx, pos) {
2800
3468
  function emitSepBy(_p, def, ctx, pos) {
2801
3469
  const arrV = v(ctx, "_arr");
2802
3470
  const curV = v(ctx, "_cur");
2803
- const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
3471
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
2804
3472
  const stmts = [
2805
3473
  `${ind(ctx)}const ${arrV} = []`,
2806
3474
  `${ind(ctx)}let ${curV} = ${pos}`,
@@ -2832,11 +3500,11 @@ function emitSepBy(_p, def, ctx, pos) {
2832
3500
  );
2833
3501
  sepAtPos = spV;
2834
3502
  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}; `;
2835
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3503
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2836
3504
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
2837
3505
  const npV = v(ctx, "_np");
2838
3506
  stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
2839
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3507
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2840
3508
  stmts.push(
2841
3509
  ...nextStmts,
2842
3510
  // item failed → unwind the separator too, back to the end of the last item
@@ -2849,11 +3517,11 @@ function emitSepBy(_p, def, ctx, pos) {
2849
3517
  const spV = v(ctx, "_sp");
2850
3518
  stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
2851
3519
  sepAtPos = spV;
2852
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3520
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2853
3521
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2854
3522
  const npV = v(ctx, "_np");
2855
3523
  stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
2856
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3524
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2857
3525
  stmts.push(
2858
3526
  ...nextStmts,
2859
3527
  `${ind(ctx)}if (!${nextOk}) break`,
@@ -2870,10 +3538,10 @@ function emitSepBy(_p, def, ctx, pos) {
2870
3538
  `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
2871
3539
  );
2872
3540
  }
2873
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3541
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2874
3542
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2875
3543
  const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
2876
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
3544
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
2877
3545
  stmts.push(
2878
3546
  ...nextStmts,
2879
3547
  `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
@@ -2931,7 +3599,7 @@ function emitScanTo(def, ctx, pos) {
2931
3599
  return { stmts, valueVar: valV, endVar: curV };
2932
3600
  }
2933
3601
  function emitNot(def, ctx, pos) {
2934
- const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
3602
+ const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
2935
3603
  const label2 = JSON.stringify(`not(${def.parser._tag})`);
2936
3604
  return {
2937
3605
  stmts: [
@@ -3022,11 +3690,14 @@ function emitLazy(p, def, ctx, pos) {
3022
3690
  }
3023
3691
  const savedIndent = ctx.indent;
3024
3692
  const savedFailLabel = ctx.failLabel;
3693
+ const savedRecord = ctx.recordFail;
3025
3694
  ctx.indent = 1;
3026
3695
  ctx.failLabel = "_pfail";
3696
+ ctx.recordFail = true;
3027
3697
  const r = emit(resolved, ctx, "_pos");
3028
3698
  ctx.indent = savedIndent;
3029
3699
  ctx.failLabel = savedFailLabel;
3700
+ ctx.recordFail = savedRecord;
3030
3701
  pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
3031
3702
  }
3032
3703
  const fnName = ctx.namedParsers.get(p);
@@ -3206,11 +3877,14 @@ function emit(p, ctx, pos) {
3206
3877
  ctx.namedParsers.set(innerParser, fnName);
3207
3878
  const savedIndent = ctx.indent;
3208
3879
  const savedFailLabel = ctx.failLabel;
3880
+ const savedRecord = ctx.recordFail;
3209
3881
  ctx.indent = 1;
3210
3882
  ctx.failLabel = "_pfail";
3883
+ ctx.recordFail = true;
3211
3884
  const innerR = emit(innerParser, ctx, "_pos");
3212
3885
  ctx.indent = savedIndent;
3213
3886
  ctx.failLabel = savedFailLabel;
3887
+ ctx.recordFail = savedRecord;
3214
3888
  pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
3215
3889
  }
3216
3890
  const fn = ctx.namedParsers.get(innerParser);
@@ -3358,6 +4032,7 @@ function compile(combinator, mapFnSources) {
3358
4032
  regexMap: /* @__PURE__ */ new Map(),
3359
4033
  expectedDecls: [],
3360
4034
  expectedMap: /* @__PURE__ */ new Map(),
4035
+ recordFail: true,
3361
4036
  mapFns: [],
3362
4037
  mapFnSrcs: [],
3363
4038
  buildFns: [],
@@ -3727,7 +4402,7 @@ function graftAndShift(root, path, newNode, delta) {
3727
4402
  children: newChildren
3728
4403
  };
3729
4404
  }
3730
- var FunctionalDocImpl = class _FunctionalDocImpl {
4405
+ var ParseDocImpl = class _ParseDocImpl {
3731
4406
  _registry;
3732
4407
  _rootRule;
3733
4408
  _opts;
@@ -3744,7 +4419,7 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
3744
4419
  }
3745
4420
  edit(from, to, replacement) {
3746
4421
  const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
3747
- const reparse = () => makeFunctionalDoc(this._registry, this._rootRule, newInput, this._opts);
4422
+ const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
3748
4423
  if (!this.tree) return reparse();
3749
4424
  const delta = replacement.length - (to - from);
3750
4425
  const found = findContaining(this.tree, from);
@@ -3766,25 +4441,25 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
3766
4441
  if (r.span.end === node2.span.end + delta) {
3767
4442
  if (delta === 0) {
3768
4443
  const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
3769
- return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
4444
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
3770
4445
  }
3771
4446
  if (this._opts.rebuild) return reparse();
3772
4447
  const newTree = graftAndShift(this.tree, path, r.value, delta);
3773
- return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
4448
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
3774
4449
  }
3775
4450
  }
3776
4451
  return reparse();
3777
4452
  }
3778
4453
  };
3779
- function makeFunctionalDoc(registry, rootRule, input, opts = {}) {
4454
+ function parseDoc(registry, rootRule, input, opts = {}) {
3780
4455
  const ruleFn = registry[rootRule];
3781
4456
  if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
3782
4457
  const ctx = { trackLines: false, state: opts.state };
3783
4458
  const r = ruleFn(input, 0, ctx);
3784
4459
  if (r.ok) {
3785
- return new FunctionalDocImpl(registry, rootRule, opts, r.value, [], input);
4460
+ return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
3786
4461
  }
3787
- return new FunctionalDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
4462
+ return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
3788
4463
  }
3789
4464
 
3790
4465
  // src/cst/trivia-index.ts
@@ -3936,7 +4611,6 @@ export {
3936
4611
  keywords,
3937
4612
  label,
3938
4613
  literal,
3939
- makeFunctionalDoc,
3940
4614
  makeWord,
3941
4615
  many,
3942
4616
  manyRecover,
@@ -3947,6 +4621,7 @@ export {
3947
4621
  oneOrMore,
3948
4622
  optional,
3949
4623
  parse,
4624
+ parseDoc,
3950
4625
  parser,
3951
4626
  recover,
3952
4627
  ref,