parseman 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/combinators/expect.d.ts.map +1 -1
  2. package/dist/combinators/node.d.ts +1 -1
  3. package/dist/combinators/node.d.ts.map +1 -1
  4. package/dist/combinators/parser.d.ts.map +1 -1
  5. package/dist/combinators/regex-analyze.d.ts +6 -0
  6. package/dist/combinators/regex-analyze.d.ts.map +1 -0
  7. package/dist/combinators/regex.d.ts +16 -1
  8. package/dist/combinators/regex.d.ts.map +1 -1
  9. package/dist/compiler/build-arity.d.ts.map +1 -1
  10. package/dist/compiler/codegen.d.ts +45 -0
  11. package/dist/compiler/codegen.d.ts.map +1 -1
  12. package/dist/compiler/inline-build.d.ts.map +1 -1
  13. package/dist/compiler/linker.d.ts +44 -0
  14. package/dist/compiler/linker.d.ts.map +1 -0
  15. package/dist/compiler/scannable-run.d.ts +80 -2
  16. package/dist/compiler/scannable-run.d.ts.map +1 -1
  17. package/dist/cst/offset-model.d.ts +119 -0
  18. package/dist/cst/offset-model.d.ts.map +1 -0
  19. package/dist/cst/relative-spans.d.ts +56 -0
  20. package/dist/cst/relative-spans.d.ts.map +1 -0
  21. package/dist/cst/types.d.ts +1 -1
  22. package/dist/functional/doc.d.ts +26 -6
  23. package/dist/functional/doc.d.ts.map +1 -1
  24. package/dist/functional/run.d.ts +63 -0
  25. package/dist/functional/run.d.ts.map +1 -0
  26. package/dist/index.cjs +1613 -148
  27. package/dist/index.cjs.map +4 -4
  28. package/dist/index.d.ts +11 -3
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +1613 -148
  31. package/dist/index.js.map +4 -4
  32. package/dist/plugin/evaluator.d.ts.map +1 -1
  33. package/dist/plugin/index.cjs +1520 -187
  34. package/dist/plugin/index.cjs.map +4 -4
  35. package/dist/plugin/index.d.ts.map +1 -1
  36. package/dist/plugin/index.js +1520 -187
  37. package/dist/plugin/index.js.map +4 -4
  38. package/dist/types.d.ts +12 -3
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +7 -4
  41. package/dist/combinators/lazy.d.ts +0 -14
  42. package/dist/combinators/lazy.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -251,6 +251,36 @@ function literal(value, opts = {}) {
251
251
  }
252
252
 
253
253
  // src/combinators/regex.ts
254
+ var firstSetAnalyzer = null;
255
+ function registerRegexAnalyzer(analyzer) {
256
+ firstSetAnalyzer = analyzer;
257
+ }
258
+ var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
259
+ function regex(pattern, flags = "") {
260
+ const source = typeof pattern === "string" ? pattern : pattern.source;
261
+ const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
262
+ const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
263
+ const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
264
+ const meta = { firstSet, canMatchNewline, isTrivia: false };
265
+ return {
266
+ _tag: "regex",
267
+ _meta: meta,
268
+ _def: { tag: "regex", source, flags: resolvedFlags },
269
+ parse(input, pos, ctx) {
270
+ anchored.lastIndex = pos;
271
+ const m = anchored.exec(input);
272
+ if (m === null) {
273
+ return failAt(ctx, [`/${source}/`], pos);
274
+ }
275
+ const span = { start: pos, end: pos + m[0].length };
276
+ const leaf = { _tag: "leaf", value: m[0], span };
277
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
278
+ return { ok: true, value: m[0], span };
279
+ }
280
+ };
281
+ }
282
+
283
+ // src/combinators/regex-analyze.ts
254
284
  import regexpTree from "regexp-tree";
255
285
  function firstSetFromRegex(pattern) {
256
286
  try {
@@ -355,40 +385,6 @@ function extractFirstSet(node2) {
355
385
  return { firstSet: any(), canMatchNewline: true };
356
386
  }
357
387
  }
358
- function optimizeRegex(source, flags) {
359
- try {
360
- const result = regexpTree.optimize(`/${source}/${flags}`);
361
- const str = result.toString();
362
- const lastSlash = str.lastIndexOf("/");
363
- return str.slice(1, lastSlash);
364
- } catch {
365
- return source;
366
- }
367
- }
368
- function regex(pattern, flags = "") {
369
- const source = typeof pattern === "string" ? pattern : pattern.source;
370
- const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
371
- const optimizedSource = optimizeRegex(source, resolvedFlags);
372
- const anchored = new RegExp(optimizedSource, "y" + resolvedFlags.replace(/[gy]/g, ""));
373
- const { firstSet, canMatchNewline } = firstSetFromRegex(source);
374
- const meta = { firstSet, canMatchNewline, isTrivia: false };
375
- return {
376
- _tag: "regex",
377
- _meta: meta,
378
- _def: { tag: "regex", source, flags: resolvedFlags, optimizedSource },
379
- parse(input, pos, ctx) {
380
- anchored.lastIndex = pos;
381
- const m = anchored.exec(input);
382
- if (m === null) {
383
- return failAt(ctx, [`/${source}/`], pos);
384
- }
385
- const span = { start: pos, end: pos + m[0].length };
386
- const leaf = { _tag: "leaf", value: m[0], span };
387
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
388
- return { ok: true, value: m[0], span };
389
- }
390
- };
391
- }
392
388
 
393
389
  // src/combinators/keywords.ts
394
390
  function word(str, boundary = "_0-9A-Za-z") {
@@ -1086,7 +1082,9 @@ function rules(factory) {
1086
1082
  if (typeof key !== "string") return void 0;
1087
1083
  const record = target;
1088
1084
  if (!(key in record)) {
1089
- record[key] = ref();
1085
+ const r = ref();
1086
+ r._ruleName = key;
1087
+ record[key] = r;
1090
1088
  }
1091
1089
  return record[key];
1092
1090
  }
@@ -1151,12 +1149,14 @@ function confirmedBuildArity(src) {
1151
1149
  return parts.length;
1152
1150
  }
1153
1151
  function buildReadsTrivia(def) {
1152
+ if (!def.build) return true;
1154
1153
  const src = def.buildSrc ?? def.build.toString();
1155
1154
  const arity = confirmedBuildArity(src);
1156
1155
  if (arity === null) return true;
1157
1156
  return arity >= 4;
1158
1157
  }
1159
1158
  function buildReadsState(def) {
1159
+ if (!def.build) return true;
1160
1160
  const src = def.buildSrc ?? def.build.toString();
1161
1161
  const arity = confirmedBuildArity(src);
1162
1162
  if (arity === null) return true;
@@ -1175,8 +1175,8 @@ function node(type, combinator, build, opts) {
1175
1175
  };
1176
1176
  const collapse = opts?.collapse === true;
1177
1177
  const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
1178
- const capturesTrivia = buildReadsTrivia(def);
1179
- const clonesState = buildReadsState(def);
1178
+ const capturesTrivia = build ? buildReadsTrivia(def) : true;
1179
+ const clonesState = build ? buildReadsState(def) : true;
1180
1180
  return {
1181
1181
  _tag: "node",
1182
1182
  _meta: meta,
@@ -1187,13 +1187,8 @@ function node(type, combinator, build, opts) {
1187
1187
  const r = combinator.parse(input, pos, ctx);
1188
1188
  const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
1189
1189
  if (!r.ok) return r;
1190
- const built = collapse && children.length === 1 ? collapseChild(children[0]) : build(
1191
- children,
1192
- rawChildren,
1193
- r.span,
1194
- triviaLog,
1195
- clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
1196
- );
1190
+ const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1191
+ const built = collapse && children.length === 1 ? collapseChild(children[0]) : build ? build(children, rawChildren, r.span, triviaLog, st) : ctx.build ? ctx.build(type, children, rawChildren, r.span, triviaLog, st) : { _tag: "node", type, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
1197
1192
  const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
1198
1193
  const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
1199
1194
  if (saved.buf !== void 0 || saved.ch !== void 0) {
@@ -1368,8 +1363,14 @@ function staticExpected(c) {
1368
1363
  case "transform":
1369
1364
  case "not":
1370
1365
  return staticExpected(def.parser);
1371
- case "lazy":
1372
- return staticExpected(def.thunk());
1366
+ case "lazy": {
1367
+ const name = c._ruleName;
1368
+ try {
1369
+ return staticExpected(def.thunk());
1370
+ } catch {
1371
+ return name ? [name] : [];
1372
+ }
1373
+ }
1373
1374
  default:
1374
1375
  return [];
1375
1376
  }
@@ -1399,8 +1400,29 @@ function expect(combinator, label2) {
1399
1400
  var BACKSLASH = 92;
1400
1401
  var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1401
1402
  var META = new Set("()[]{}*+?|^$.".split(""));
1403
+ var SPACE_RANGES = [
1404
+ [9, 13],
1405
+ [32, 32],
1406
+ [160, 160],
1407
+ [5760, 5760],
1408
+ [8192, 8202],
1409
+ [8232, 8232],
1410
+ [8233, 8233],
1411
+ [8239, 8239],
1412
+ [8287, 8287],
1413
+ [12288, 12288],
1414
+ [65279, 65279]
1415
+ ];
1402
1416
  function shorthandRanges(ch) {
1403
- return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
1417
+ if (ch === "d") return [[48, 57]];
1418
+ if (ch === "s") return SPACE_RANGES;
1419
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1420
+ }
1421
+ function readUnicodeEscape(body, i) {
1422
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1423
+ const hex = body.slice(i + 2, i + 6);
1424
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1425
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1404
1426
  }
1405
1427
  function parseClassRanges(body) {
1406
1428
  const ranges = [];
@@ -1409,11 +1431,16 @@ function parseClassRanges(body) {
1409
1431
  const ch = body[i];
1410
1432
  if (ch === void 0) return null;
1411
1433
  if (ch === "\\") {
1434
+ const uni = readUnicodeEscape(body, i);
1435
+ if (uni) {
1436
+ i = uni.next;
1437
+ return { cp: uni.cp };
1438
+ }
1412
1439
  const e = body[i + 1];
1413
1440
  if (e === void 0) return null;
1414
1441
  i += 2;
1415
1442
  if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1416
- if (e === "d" || e === "w") return { set: shorthandRanges(e) };
1443
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1417
1444
  if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1418
1445
  return { cp: e.codePointAt(0) };
1419
1446
  }
@@ -1441,10 +1468,16 @@ function parseClassRanges(body) {
1441
1468
  function classToRanges(cls) {
1442
1469
  if (cls === "\\d") return shorthandRanges("d");
1443
1470
  if (cls === "\\w") return shorthandRanges("w");
1471
+ if (cls === "\\s") return shorthandRanges("s");
1444
1472
  const body = cls.slice(1, -1);
1445
1473
  if (body.startsWith("^")) return null;
1446
1474
  return parseClassRanges(body);
1447
1475
  }
1476
+ var foldEq = (cVar, cp) => {
1477
+ if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
1478
+ if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
1479
+ return `${cVar} === ${cp}`;
1480
+ };
1448
1481
  function literalCodePoints(frag) {
1449
1482
  const out = [];
1450
1483
  let i = 0;
@@ -1491,22 +1524,612 @@ function parseStringShape(source) {
1491
1524
  if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
1492
1525
  return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
1493
1526
  }
1527
+ function parseSeqParts(source) {
1528
+ const parts = [];
1529
+ let lit = [];
1530
+ const flush = () => {
1531
+ if (lit.length) {
1532
+ parts.push({ part: "lit", cps: lit, optional: false });
1533
+ lit = [];
1534
+ }
1535
+ };
1536
+ const runFrom = (ranges, negated, q) => {
1537
+ if (q === "+") {
1538
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
1539
+ return 1;
1540
+ }
1541
+ if (q === "*") {
1542
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
1543
+ return 1;
1544
+ }
1545
+ if (q === "?") {
1546
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
1547
+ return 1;
1548
+ }
1549
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
1550
+ return 0;
1551
+ };
1552
+ let i = 0;
1553
+ while (i < source.length) {
1554
+ const ch = source[i];
1555
+ if (ch === "[") {
1556
+ let k = i + 1;
1557
+ const neg = source[k] === "^";
1558
+ if (neg) k++;
1559
+ let body = "";
1560
+ while (k < source.length && source[k] !== "]") {
1561
+ if (source[k] === "\\") {
1562
+ body += source[k] + (source[k + 1] ?? "");
1563
+ k += 2;
1564
+ } else {
1565
+ body += source[k];
1566
+ k++;
1567
+ }
1568
+ }
1569
+ if (source[k] !== "]") return null;
1570
+ const ranges = parseClassRanges(body);
1571
+ if (!ranges) return null;
1572
+ flush();
1573
+ i = k + 1;
1574
+ i += runFrom(ranges, neg, source[i]);
1575
+ continue;
1576
+ }
1577
+ if (ch === "\\") {
1578
+ const e = source[i + 1];
1579
+ if (e === "d" || e === "w" || e === "s") {
1580
+ flush();
1581
+ i += 2;
1582
+ i += runFrom(shorthandRanges(e), false, source[i]);
1583
+ continue;
1584
+ }
1585
+ const uni = readUnicodeEscape(source, i);
1586
+ let cp2;
1587
+ if (uni) {
1588
+ cp2 = uni.cp;
1589
+ i = uni.next;
1590
+ } else if (e === void 0) return null;
1591
+ else if (e in CLASS_ESCAPES) {
1592
+ cp2 = CLASS_ESCAPES[e];
1593
+ i += 2;
1594
+ } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1595
+ else {
1596
+ cp2 = e.codePointAt(0);
1597
+ i += 2;
1598
+ }
1599
+ if (source[i] === "?") {
1600
+ flush();
1601
+ parts.push({ part: "lit", cps: [cp2], optional: true });
1602
+ i++;
1603
+ } else lit.push(cp2);
1604
+ continue;
1605
+ }
1606
+ if (ch === "(") {
1607
+ if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
1608
+ let k = i + 3;
1609
+ let depth = 1;
1610
+ while (k < source.length && depth > 0) {
1611
+ const c2 = source[k];
1612
+ if (c2 === "\\") {
1613
+ k += 2;
1614
+ continue;
1615
+ }
1616
+ if (c2 === "[") {
1617
+ let j = k + 1;
1618
+ if (source[j] === "^") j++;
1619
+ while (j < source.length && source[j] !== "]") {
1620
+ if (source[j] === "\\") j += 2;
1621
+ else j++;
1622
+ }
1623
+ if (source[j] !== "]") return null;
1624
+ k = j + 1;
1625
+ continue;
1626
+ }
1627
+ if (c2 === "(") depth++;
1628
+ else if (c2 === ")") depth--;
1629
+ k++;
1630
+ }
1631
+ if (depth !== 0) return null;
1632
+ const body = source.slice(i + 3, k - 1);
1633
+ const inner = parseScanShape(body);
1634
+ if (!inner || !groupInnerSafe(inner)) return null;
1635
+ const q = source[k];
1636
+ if (q === "{") return null;
1637
+ flush();
1638
+ if (q === "+") {
1639
+ if (shapeCanBeEmpty(inner)) return null;
1640
+ parts.push({ part: "group", inner, min: 1, unbounded: true });
1641
+ i = k + 1;
1642
+ } else if (q === "*") {
1643
+ if (shapeCanBeEmpty(inner)) return null;
1644
+ parts.push({ part: "group", inner, min: 0, unbounded: true });
1645
+ i = k + 1;
1646
+ } else if (q === "?") {
1647
+ parts.push({ part: "group", inner, min: 0, unbounded: false });
1648
+ i = k + 1;
1649
+ } else {
1650
+ parts.push({ part: "group", inner, min: 1, unbounded: false });
1651
+ i = k;
1652
+ }
1653
+ continue;
1654
+ }
1655
+ if (META.has(ch)) return null;
1656
+ const cp = ch.codePointAt(0);
1657
+ i += ch.length;
1658
+ if (source[i] === "?") {
1659
+ flush();
1660
+ parts.push({ part: "lit", cps: [cp], optional: true });
1661
+ i++;
1662
+ } else lit.push(cp);
1663
+ }
1664
+ flush();
1665
+ return parts.length ? parts : null;
1666
+ }
1667
+ function seqIsUnambiguous(parts) {
1668
+ let hasMandatory = false;
1669
+ for (let k = 0; k < parts.length; k++) {
1670
+ const p = parts[k];
1671
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1672
+ if (mandatory) hasMandatory = true;
1673
+ if (p.part === "group") {
1674
+ const exposure = groupPartExposure(p);
1675
+ if (exposure === "unsupported") {
1676
+ const trailingOnce = k === parts.length - 1 && p.min === 1 && !p.unbounded;
1677
+ if (!trailingOnce) return false;
1678
+ } else if (exposure !== null) {
1679
+ if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
1680
+ }
1681
+ }
1682
+ const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
1683
+ const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
1684
+ if (skippableSingle || isUnboundedRepeat) {
1685
+ const rest = seqFirstAccept(parts.slice(k + 1));
1686
+ if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
1687
+ }
1688
+ }
1689
+ return hasMandatory;
1690
+ }
1691
+ function rangesOverlap(a, b) {
1692
+ return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
1693
+ }
1694
+ function rangesSubset(sub, sup) {
1695
+ return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
1696
+ }
1697
+ function parseClassOperand(body) {
1698
+ if (body === "\\d" || body === "\\w" || body === "\\s") {
1699
+ return { ranges: shorthandRanges(body[1]), negated: false };
1700
+ }
1701
+ if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1702
+ let inner = body.slice(1, -1);
1703
+ const negated = inner.startsWith("^");
1704
+ if (negated) inner = inner.slice(1);
1705
+ const ranges = parseClassRanges(inner);
1706
+ return ranges ? { ranges, negated } : null;
1707
+ }
1708
+ const cps = literalCodePoints(body);
1709
+ if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
1710
+ return null;
1711
+ }
1712
+ function classSubset(t, u) {
1713
+ if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
1714
+ if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
1715
+ if (t.negated && !u.negated) return false;
1716
+ return rangesSubset(u.ranges, t.ranges);
1717
+ }
1718
+ function classDisjoint(t, u) {
1719
+ if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
1720
+ if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
1721
+ if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
1722
+ return false;
1723
+ }
1724
+ function firstSetsDisjoint(a, b) {
1725
+ if (a === "any" || b === "any") return false;
1726
+ return classDisjoint(a, b);
1727
+ }
1728
+ function fixedClassSeq(shape) {
1729
+ if (shape.kind !== "seq") return null;
1730
+ const out = [];
1731
+ for (const p of shape.parts) {
1732
+ if (p.part === "lit") {
1733
+ if (p.optional) return null;
1734
+ for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
1735
+ } else if (p.part === "run" && !p.unbounded && p.min === 1) {
1736
+ out.push({ ranges: p.ranges, negated: p.negated });
1737
+ } else {
1738
+ return null;
1739
+ }
1740
+ }
1741
+ return out.length ? out : null;
1742
+ }
1743
+ function altFixedMutuallyExclusive(shape) {
1744
+ if (shape.kind !== "alt" || shape.disjoint) return false;
1745
+ const lists = [];
1746
+ for (const arm of shape.arms) {
1747
+ const f = fixedClassSeq(arm);
1748
+ if (!f) return false;
1749
+ lists.push(f);
1750
+ }
1751
+ for (let i = 0; i < lists.length; i++) {
1752
+ for (let j = 0; j < lists.length; j++) {
1753
+ if (i === j) continue;
1754
+ const a = lists[i];
1755
+ const b = lists[j];
1756
+ if (a.length > b.length) continue;
1757
+ let disjointAt = false;
1758
+ for (let p = 0; p < a.length; p++) {
1759
+ if (classDisjoint(a[p], b[p])) {
1760
+ disjointAt = true;
1761
+ break;
1762
+ }
1763
+ }
1764
+ if (!disjointAt) return false;
1765
+ }
1766
+ }
1767
+ return true;
1768
+ }
1769
+ function groupPartExposure(part) {
1770
+ const innerWiggle = trailingBacktrackClass(part.inner);
1771
+ if (innerWiggle === "unsupported") return "unsupported";
1772
+ const requiredOnce = !part.unbounded && part.min === 1;
1773
+ if (requiredOnce) return innerWiggle;
1774
+ const dropExposed = shapeFirstAccept(part.inner);
1775
+ if (dropExposed === "any") return "unsupported";
1776
+ if (innerWiggle === null) return dropExposed;
1777
+ const unioned = unionCharSets([innerWiggle, dropExposed]);
1778
+ return unioned === "any" ? "unsupported" : unioned;
1779
+ }
1780
+ function trailingBacktrackClass(shape) {
1781
+ if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
1782
+ if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
1783
+ if (shape.kind === "litFold") return null;
1784
+ if (shape.kind === "seq") {
1785
+ const last = shape.parts[shape.parts.length - 1];
1786
+ if (last.part === "lit") return null;
1787
+ if (last.part === "run") {
1788
+ if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
1789
+ return null;
1790
+ }
1791
+ return groupPartExposure(last);
1792
+ }
1793
+ if (shape.kind === "alt") {
1794
+ if (!shape.disjoint) return altFixedMutuallyExclusive(shape) ? null : "unsupported";
1795
+ const classes = shape.arms.map(trailingBacktrackClass);
1796
+ if (classes.some((c) => c === "unsupported")) return "unsupported";
1797
+ const concrete = classes.filter((c) => c !== null);
1798
+ if (concrete.length === 0) return null;
1799
+ const unioned = unionCharSets(concrete);
1800
+ return unioned === "any" ? "unsupported" : unioned;
1801
+ }
1802
+ return "unsupported";
1803
+ }
1804
+ function lookaheadUnambiguous(inner, operand, negative) {
1805
+ const tail = trailingBacktrackClass(inner);
1806
+ if (tail === "unsupported") return false;
1807
+ if (tail === null) return true;
1808
+ return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
1809
+ }
1810
+ function stripTrailingLookahead(source) {
1811
+ if (!source.endsWith(")")) return null;
1812
+ for (const negative of [true, false]) {
1813
+ const lead = negative ? "(?!" : "(?=";
1814
+ const idx = source.lastIndexOf(lead);
1815
+ if (idx === -1) continue;
1816
+ const body = source.slice(idx + lead.length, -1);
1817
+ const operand = body.length ? parseClassOperand(body) : null;
1818
+ if (!operand) continue;
1819
+ return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
1820
+ }
1821
+ return null;
1822
+ }
1823
+ function unwrapOuterGroup(source) {
1824
+ while (source.startsWith("(?:")) {
1825
+ let depth = 1;
1826
+ let i = 3;
1827
+ let ok = true;
1828
+ while (i < source.length && depth > 0) {
1829
+ const ch = source[i];
1830
+ if (ch === "\\") {
1831
+ i += 2;
1832
+ continue;
1833
+ }
1834
+ if (ch === "[") {
1835
+ let j = i + 1;
1836
+ if (source[j] === "^") j++;
1837
+ while (j < source.length && source[j] !== "]") {
1838
+ if (source[j] === "\\") j += 2;
1839
+ else j++;
1840
+ }
1841
+ if (source[j] !== "]") {
1842
+ ok = false;
1843
+ break;
1844
+ }
1845
+ i = j + 1;
1846
+ continue;
1847
+ }
1848
+ if (ch === "(") depth++;
1849
+ else if (ch === ")") depth--;
1850
+ i++;
1851
+ }
1852
+ if (!ok || depth !== 0 || i !== source.length) break;
1853
+ source = source.slice(3, -1);
1854
+ }
1855
+ return source;
1856
+ }
1857
+ function splitTopLevelAlternation(source) {
1858
+ const body = unwrapOuterGroup(source);
1859
+ const parts = [];
1860
+ let depth = 0;
1861
+ let last = 0;
1862
+ let i = 0;
1863
+ while (i < body.length) {
1864
+ const ch = body[i];
1865
+ if (ch === "\\") {
1866
+ i += 2;
1867
+ continue;
1868
+ }
1869
+ if (ch === "[") {
1870
+ let j = i + 1;
1871
+ if (body[j] === "^") j++;
1872
+ while (j < body.length && body[j] !== "]") {
1873
+ if (body[j] === "\\") j += 2;
1874
+ else j++;
1875
+ }
1876
+ if (body[j] !== "]") return null;
1877
+ i = j + 1;
1878
+ continue;
1879
+ }
1880
+ if (ch === "(") {
1881
+ depth++;
1882
+ i++;
1883
+ continue;
1884
+ }
1885
+ if (ch === ")") {
1886
+ depth--;
1887
+ if (depth < 0) return null;
1888
+ i++;
1889
+ continue;
1890
+ }
1891
+ if (ch === "|" && depth === 0) {
1892
+ parts.push(body.slice(last, i));
1893
+ last = i + 1;
1894
+ i++;
1895
+ continue;
1896
+ }
1897
+ i++;
1898
+ }
1899
+ if (depth !== 0) return null;
1900
+ parts.push(body.slice(last));
1901
+ if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
1902
+ return parts;
1903
+ }
1904
+ function shapeCanBeEmpty(shape) {
1905
+ if (shape.kind === "chars") return !shape.minOne;
1906
+ if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
1907
+ if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
1908
+ return false;
1909
+ }
1910
+ function unionCharSets(sets) {
1911
+ if (sets.some((s) => s === "any")) return "any";
1912
+ const concrete = sets;
1913
+ if (concrete.length === 1) return concrete[0];
1914
+ if (concrete.some((s) => s.negated)) return "any";
1915
+ return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
1916
+ }
1917
+ function partFirstAccept(part) {
1918
+ if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
1919
+ if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
1920
+ return shapeFirstAccept(part.inner);
1921
+ }
1922
+ function seqFirstAccept(parts) {
1923
+ const sets = [];
1924
+ for (const p of parts) {
1925
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1926
+ sets.push(partFirstAccept(p));
1927
+ if (mandatory) break;
1928
+ }
1929
+ return unionCharSets(sets);
1930
+ }
1931
+ function groupInnerSafe(shape) {
1932
+ if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
1933
+ if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
1934
+ if (shape.kind === "alt") return true;
1935
+ return false;
1936
+ }
1937
+ function shapeFirstAccept(shape) {
1938
+ if (shapeCanBeEmpty(shape)) return "any";
1939
+ switch (shape.kind) {
1940
+ case "chars":
1941
+ return { ranges: shape.ranges, negated: false };
1942
+ case "ident":
1943
+ return { ranges: shape.head, negated: false };
1944
+ case "until":
1945
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1946
+ case "delimited":
1947
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1948
+ case "string":
1949
+ return { ranges: [[shape.quote, shape.quote]], negated: false };
1950
+ case "litFold": {
1951
+ const cp = shape.open[0];
1952
+ if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
1953
+ if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
1954
+ return { ranges: [[cp, cp]], negated: false };
1955
+ }
1956
+ case "seq":
1957
+ return seqFirstAccept(shape.parts);
1958
+ case "lookahead":
1959
+ return shapeFirstAccept(shape.inner);
1960
+ // zero-width — doesn't change first-char acceptance
1961
+ case "alt": {
1962
+ const sets = shape.firsts.map((f) => f === null ? "any" : f);
1963
+ return unionCharSets(sets);
1964
+ }
1965
+ }
1966
+ }
1967
+ function allPairsDisjoint(sets) {
1968
+ for (let i = 0; i < sets.length; i++) {
1969
+ const a = sets[i];
1970
+ if (a === "any") return false;
1971
+ for (let j = i + 1; j < sets.length; j++) {
1972
+ const b = sets[j];
1973
+ if (b === "any" || !classDisjoint(a, b)) return false;
1974
+ }
1975
+ }
1976
+ return true;
1977
+ }
1978
+ function parseAlternation(source) {
1979
+ const parts = splitTopLevelAlternation(source);
1980
+ if (!parts) return null;
1981
+ const arms = [];
1982
+ for (const p of parts) {
1983
+ const arm = parseScanShape(p);
1984
+ if (!arm) return null;
1985
+ arms.push(arm);
1986
+ }
1987
+ const firstSets = arms.map(shapeFirstAccept);
1988
+ const disjoint = allPairsDisjoint(firstSets);
1989
+ return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
1990
+ }
1494
1991
  function parseScanShape(source) {
1992
+ const alt = parseAlternation(source);
1993
+ if (alt) return alt;
1994
+ const la = stripTrailingLookahead(source);
1995
+ if (la) {
1996
+ const inner = parseScanShapeCore(la.base);
1997
+ if (!inner) return null;
1998
+ const operand = { ranges: la.ranges, negated: la.classNegated };
1999
+ if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
2000
+ return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
2001
+ }
2002
+ return parseScanShapeCore(source);
2003
+ }
2004
+ function splitDelimArms(body) {
2005
+ const arms = [];
2006
+ let depth = 0;
2007
+ let last = 0;
2008
+ let i = 0;
2009
+ while (i < body.length) {
2010
+ const ch = body[i];
2011
+ if (ch === "\\") {
2012
+ i += 2;
2013
+ continue;
2014
+ }
2015
+ if (ch === "[") {
2016
+ let j = i + 1;
2017
+ if (body[j] === "^") j++;
2018
+ while (j < body.length && body[j] !== "]") {
2019
+ if (body[j] === "\\") j += 2;
2020
+ else j++;
2021
+ }
2022
+ if (body[j] !== "]") return null;
2023
+ i = j + 1;
2024
+ continue;
2025
+ }
2026
+ if (ch === "(") {
2027
+ depth++;
2028
+ i++;
2029
+ continue;
2030
+ }
2031
+ if (ch === ")") {
2032
+ depth--;
2033
+ if (depth < 0) return null;
2034
+ i++;
2035
+ continue;
2036
+ }
2037
+ if (ch === "|" && depth === 0) {
2038
+ arms.push(body.slice(last, i));
2039
+ last = i + 1;
2040
+ }
2041
+ i++;
2042
+ }
2043
+ if (depth !== 0) return null;
2044
+ arms.push(body.slice(last));
2045
+ return arms;
2046
+ }
2047
+ function delimitedBodySound(body, close) {
2048
+ const l0 = close[0];
2049
+ if (close.length > 2) return false;
2050
+ const arms = splitDelimArms(body);
2051
+ if (!arms) return false;
2052
+ let bulkSeen = false;
2053
+ let guardSeen = false;
2054
+ for (const arm of arms) {
2055
+ const bulk = /^\[\^((?:\\.|[^\]])+)\]$/.exec(arm);
2056
+ if (bulk) {
2057
+ if (bulkSeen) return false;
2058
+ const ranges = parseClassRanges(bulk[1]);
2059
+ if (!ranges || ranges.length !== 1 || ranges[0][0] !== l0 || ranges[0][1] !== l0) return false;
2060
+ bulkSeen = true;
2061
+ continue;
2062
+ }
2063
+ if (close.length === 2 && arm.endsWith(")")) {
2064
+ const idx = arm.indexOf("(?!");
2065
+ if (idx === -1) return false;
2066
+ const prefix = literalCodePoints(arm.slice(0, idx));
2067
+ const op = parseClassOperand(arm.slice(idx + 3, -1));
2068
+ const prefixIsL0 = prefix?.length === 1 && prefix[0] === l0;
2069
+ const forbidsL1 = op && !op.negated && op.ranges.length === 1 && op.ranges[0][0] === close[1] && op.ranges[0][1] === close[1];
2070
+ if (!prefixIsL0 || !forbidsL1 || guardSeen) return false;
2071
+ guardSeen = true;
2072
+ continue;
2073
+ }
2074
+ return false;
2075
+ }
2076
+ return bulkSeen && guardSeen === (close.length === 2);
2077
+ }
2078
+ function parseDelimited(source) {
2079
+ if (source.includes("\\\\")) return null;
2080
+ let i = 0;
2081
+ while (i < source.length && source[i] !== "(") {
2082
+ i += source[i] === "\\" ? 2 : 1;
2083
+ }
2084
+ if (source[i] !== "(" || source[i + 1] !== "?" || source[i + 2] !== ":") return null;
2085
+ const openFrag = source.slice(0, i);
2086
+ let k = i + 3;
2087
+ let depth = 1;
2088
+ while (k < source.length && depth > 0) {
2089
+ const ch = source[k];
2090
+ if (ch === "\\") {
2091
+ k += 2;
2092
+ continue;
2093
+ }
2094
+ if (ch === "[") {
2095
+ let j = k + 1;
2096
+ if (source[j] === "^") j++;
2097
+ while (j < source.length && source[j] !== "]") {
2098
+ if (source[j] === "\\") j += 2;
2099
+ else j++;
2100
+ }
2101
+ if (source[j] !== "]") return null;
2102
+ k = j + 1;
2103
+ continue;
2104
+ }
2105
+ if (ch === "(") depth++;
2106
+ else if (ch === ")") depth--;
2107
+ k++;
2108
+ }
2109
+ if (depth !== 0) return null;
2110
+ const body = source.slice(i + 3, k - 1);
2111
+ if (source[k] !== "*") return null;
2112
+ const closeFrag = source.slice(k + 1);
2113
+ const open = literalCodePoints(openFrag);
2114
+ const close = literalCodePoints(closeFrag);
2115
+ if (!open || !close || !delimitedBodySound(body, close)) return null;
2116
+ return { kind: "delimited", open, close };
2117
+ }
2118
+ function parseScanShapeCore(source) {
1495
2119
  let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1496
- if (m) {
1497
- if (m[1].startsWith("^")) return null;
2120
+ if (m && !m[1].startsWith("^")) {
1498
2121
  const ranges = parseClassRanges(m[1]);
1499
2122
  return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
1500
2123
  }
1501
- m = /^\\([dw])([+*])$/.exec(source);
2124
+ m = /^\\([dws])([+*])$/.exec(source);
1502
2125
  if (m) {
1503
2126
  return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
1504
2127
  }
1505
- m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
2128
+ m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
1506
2129
  if (m) {
1507
2130
  const head = classToRanges(m[1]);
1508
2131
  const tail = classToRanges(m[2]);
1509
- return head && tail ? { kind: "ident", head, tail } : null;
2132
+ if (head && tail) return { kind: "ident", head, tail };
1510
2133
  }
1511
2134
  const str = parseStringShape(source);
1512
2135
  if (str) return str;
@@ -1515,20 +2138,19 @@ function parseScanShape(source) {
1515
2138
  const open = literalCodePoints(m[1]);
1516
2139
  const stop = parseClassRanges(m[2]);
1517
2140
  if (open && stop) return { kind: "until", open, stop };
1518
- return null;
1519
- }
1520
- if (!source.includes("\\\\")) {
1521
- m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
1522
- if (m && m[1] && m[2]) {
1523
- const open = literalCodePoints(m[1]);
1524
- const close = literalCodePoints(m[2]);
1525
- if (open && close) return { kind: "delimited", open, close };
1526
- }
1527
2141
  }
2142
+ const delim = parseDelimited(source);
2143
+ if (delim) return delim;
2144
+ const parts = parseSeqParts(source);
2145
+ if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
1528
2146
  return null;
1529
2147
  }
1530
2148
  function scanShapeFromRegex(source, flags) {
1531
- if (/[imsu]/.test(flags)) return null;
2149
+ if (/[msu]/.test(flags)) return null;
2150
+ if (/i/.test(flags)) {
2151
+ const open = literalCodePoints(source);
2152
+ return open ? { kind: "litFold", open } : null;
2153
+ }
1532
2154
  return parseScanShape(source);
1533
2155
  }
1534
2156
  var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
@@ -1543,6 +2165,53 @@ function stringHardStop(shape) {
1543
2165
  }
1544
2166
  var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
1545
2167
  function emitShapeMatch(shape, start, mint, ind2, firstChar) {
2168
+ if (shape.kind === "lookahead") {
2169
+ const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
2170
+ const okV = mint("_ok");
2171
+ const endV2 = mint("_end");
2172
+ const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
2173
+ const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
2174
+ const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
2175
+ const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
2176
+ return {
2177
+ setup: [
2178
+ ...inner.setup,
2179
+ `${ind2}let ${okV} = ${inner.ok}`,
2180
+ `${ind2}let ${endV2} = ${inner.end}`,
2181
+ `${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
2182
+ ],
2183
+ ok: okV,
2184
+ end: endV2
2185
+ };
2186
+ }
2187
+ if (shape.kind === "alt") {
2188
+ const okV = mint("_ok");
2189
+ const endV2 = mint("_end");
2190
+ const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
2191
+ if (shape.disjoint) {
2192
+ const c0 = codeAt(start, firstChar);
2193
+ shape.arms.forEach((arm, k) => {
2194
+ const fs = shape.firsts[k];
2195
+ const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
2196
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2197
+ lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
2198
+ lines.push(...m.setup);
2199
+ lines.push(`${ind2} ${okV} = ${m.ok}`);
2200
+ lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
2201
+ lines.push(`${ind2}}`);
2202
+ });
2203
+ } else {
2204
+ const lbl = mint("_alt");
2205
+ lines.push(`${ind2}${lbl}: {`);
2206
+ for (const arm of shape.arms) {
2207
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2208
+ lines.push(...m.setup);
2209
+ lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
2210
+ }
2211
+ lines.push(`${ind2}}`);
2212
+ }
2213
+ return { setup: lines, ok: okV, end: endV2 };
2214
+ }
1546
2215
  if (shape.kind === "chars") {
1547
2216
  const cur = mint("_e");
1548
2217
  return {
@@ -1569,6 +2238,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1569
2238
  end: cur
1570
2239
  };
1571
2240
  }
2241
+ if (shape.kind === "seq") {
2242
+ const endV2 = mint("_e");
2243
+ const okV = mint("_ok");
2244
+ const runCond = (p, at) => {
2245
+ const c = classCond(`input.charCodeAt(${at})`, p.ranges);
2246
+ return p.negated ? `!(${c})` : `(${c})`;
2247
+ };
2248
+ const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
2249
+ let first = true;
2250
+ for (const p of shape.parts) {
2251
+ if (p.part === "lit") {
2252
+ const L = p.cps.length;
2253
+ const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
2254
+ if (p.optional) {
2255
+ lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
2256
+ } else {
2257
+ lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
2258
+ lines.push(`${ind2} ${endV2} += ${L}`);
2259
+ }
2260
+ } else if (p.part === "run" && !p.unbounded) {
2261
+ if (p.min === 1) {
2262
+ lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
2263
+ lines.push(`${ind2} ${endV2}++`);
2264
+ } else {
2265
+ lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2266
+ }
2267
+ } else if (p.part === "run") {
2268
+ const s = mint("_s");
2269
+ lines.push(`${ind2} const ${s} = ${endV2}`);
2270
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2271
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
2272
+ } else {
2273
+ const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
2274
+ if (!p.unbounded) {
2275
+ lines.push(...inner.setup);
2276
+ if (p.min === 1) {
2277
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2278
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2279
+ } else {
2280
+ lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
2281
+ }
2282
+ } else {
2283
+ const progV = mint("_prog");
2284
+ lines.push(`${ind2} let ${progV} = false`);
2285
+ lines.push(`${ind2} while (true) {`);
2286
+ lines.push(...inner.setup);
2287
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2288
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2289
+ lines.push(`${ind2} ${progV} = true`);
2290
+ lines.push(`${ind2} }`);
2291
+ if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
2292
+ }
2293
+ }
2294
+ first = false;
2295
+ }
2296
+ lines.push(`${ind2} ${okV} = true`);
2297
+ lines.push(`${ind2}} while (false)`);
2298
+ lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
2299
+ return { setup: lines, ok: okV, end: endV2 };
2300
+ }
2301
+ if (shape.kind === "litFold") {
2302
+ const openLen2 = shape.open.length;
2303
+ const endV2 = mint("_end");
2304
+ const checks = shape.open.map((cp, k) => {
2305
+ const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
2306
+ return foldEq(cVar, cp);
2307
+ });
2308
+ return {
2309
+ setup: [
2310
+ `${ind2}let ${endV2} = ${start}`,
2311
+ `${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
2312
+ ],
2313
+ ok: `${endV2} > ${start}`,
2314
+ end: endV2
2315
+ };
2316
+ }
1572
2317
  if (shape.kind === "until") {
1573
2318
  const j2 = mint("_j");
1574
2319
  const openLen2 = shape.open.length;
@@ -1850,6 +2595,7 @@ function looseMkBuildRe(type) {
1850
2595
  );
1851
2596
  }
1852
2597
  function analyzeMkInlineBuild(def) {
2598
+ if (!def.build) return null;
1853
2599
  const src = (def.buildSrc ?? def.build.toString()).trim();
1854
2600
  const strict = src.match(MK_BUILD_RE);
1855
2601
  if (strict) {
@@ -1947,6 +2693,15 @@ function v(ctx, prefix = "_v") {
1947
2693
  function ind(ctx) {
1948
2694
  return " ".repeat(ctx.indent);
1949
2695
  }
2696
+ function nsp(ctx) {
2697
+ return ctx.ns ?? "";
2698
+ }
2699
+ function mfRef(ctx) {
2700
+ return `${nsp(ctx)}_mf`;
2701
+ }
2702
+ function buildRef(ctx) {
2703
+ return `${nsp(ctx)}_build`;
2704
+ }
1950
2705
  function reindentStmts(stmts, targetLevels) {
1951
2706
  const nonEmpty = stmts.filter((s) => s.trim().length > 0);
1952
2707
  if (nonEmpty.length === 0) return stmts;
@@ -1963,8 +2718,8 @@ function failReturnArr(expectedArr, posExpr) {
1963
2718
  function hoistExpected(ctx, constArrSource) {
1964
2719
  let name = ctx.expectedMap.get(constArrSource);
1965
2720
  if (name === void 0) {
1966
- name = `_fx${ctx.expectedDecls.length}`;
1967
- ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
2721
+ name = `${nsp(ctx)}_fx${ctx.expectedDecls.length}`;
2722
+ ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
1968
2723
  ctx.expectedMap.set(constArrSource, name);
1969
2724
  }
1970
2725
  return name;
@@ -2062,13 +2817,13 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
2062
2817
  `${i}}`
2063
2818
  ];
2064
2819
  }
2065
- function ensureRegexDecl(ctx, optimizedSource, flags) {
2820
+ function ensureRegexDecl(ctx, source, flags) {
2066
2821
  const f = "y" + flags.replace(/[gy]/g, "");
2067
- const key = `${optimizedSource}/${f}`;
2822
+ const key = `${source}/${f}`;
2068
2823
  let rName = ctx.regexMap.get(key);
2069
2824
  if (rName === void 0) {
2070
- rName = `_re${ctx.regexDecls.length}`;
2071
- ctx.regexDecls.push(`const ${rName} = /${optimizedSource}/${f}`);
2825
+ rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
2826
+ ctx.regexDecls.push(`const ${rName} = /${source}/${f}`);
2072
2827
  ctx.regexMap.set(key, rName);
2073
2828
  }
2074
2829
  return rName;
@@ -2077,7 +2832,7 @@ function ensureTriviaFn(ctx) {
2077
2832
  const trivia2 = ctx.activeTrivia;
2078
2833
  const existing = ctx.triviaFnNames.get(trivia2);
2079
2834
  if (existing) return existing;
2080
- const fnName = `_tf${ctx.triviaFnNames.size}`;
2835
+ const fnName = `${nsp(ctx)}_tf${ctx.triviaFnNames.size}`;
2081
2836
  ctx.triviaFnNames.set(trivia2, fnName);
2082
2837
  ctx.triviaCaptureNames.set(trivia2, fnName);
2083
2838
  const labeledSpec = analyzeLabeledTrivia(trivia2);
@@ -2099,7 +2854,7 @@ function ensureTriviaFn(ctx) {
2099
2854
  for (const arm of regexSpec.arms) {
2100
2855
  const def = arm.parser._def;
2101
2856
  if (def.tag !== "regex") break;
2102
- reNames.push(ensureRegexDecl(ctx, def.optimizedSource, def.flags));
2857
+ reNames.push(ensureRegexDecl(ctx, def.source, def.flags));
2103
2858
  }
2104
2859
  if (reNames.length === regexSpec.arms.length) {
2105
2860
  ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
@@ -2286,18 +3041,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
2286
3041
  return true;
2287
3042
  }
2288
3043
  }
2289
- function emitFallible(inner, ctx, pos) {
3044
+ function emitFallible(inner, ctx, pos, swallow = false) {
2290
3045
  const lbl = v(ctx, "_lbl");
2291
3046
  const okV = `${lbl}ok`;
2292
3047
  const valV = `${lbl}v`;
2293
3048
  const endV = `${lbl}e`;
2294
3049
  const savedLabel = ctx.failLabel;
2295
3050
  const savedIndent = ctx.indent;
3051
+ const savedRecord = ctx.recordFail;
2296
3052
  ctx.failLabel = lbl;
2297
3053
  ctx.indent = savedIndent + 1;
3054
+ if (swallow) ctx.recordFail = false;
2298
3055
  const r = emit(inner, ctx, pos);
2299
3056
  ctx.failLabel = savedLabel;
2300
3057
  ctx.indent = savedIndent;
3058
+ ctx.recordFail = savedRecord;
2301
3059
  const ind0 = ind(ctx);
2302
3060
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
2303
3061
  const mL = needsRollback ? v(ctx, "_fcl") : null;
@@ -2320,6 +3078,7 @@ function emitFallible(inner, ctx, pos) {
2320
3078
  ];
2321
3079
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
2322
3080
  }
3081
+ var CHARCODE_CHAIN_MAX = 16;
2323
3082
  function emitLit(def, ctx, pos) {
2324
3083
  const { value, caseInsensitive } = def;
2325
3084
  const len = value.length;
@@ -2342,7 +3101,7 @@ function emitLit(def, ctx, pos) {
2342
3101
  ...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
2343
3102
  `${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
2344
3103
  );
2345
- } else if (len <= 4) {
3104
+ } else if (len <= CHARCODE_CHAIN_MAX) {
2346
3105
  const checks = Array.from(
2347
3106
  { length: len },
2348
3107
  (_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
@@ -2364,7 +3123,55 @@ function emitLit(def, ctx, pos) {
2364
3123
  function escapeKeywordRe(s) {
2365
3124
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2366
3125
  }
3126
+ function emitKeywordsFast(def, ctx, pos) {
3127
+ if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
3128
+ if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
3129
+ if (def.caseInsensitive && def.boundary) return null;
3130
+ let boundary = null;
3131
+ if (def.boundary) {
3132
+ let body = def.boundary;
3133
+ const negated = body.startsWith("^");
3134
+ if (negated) body = body.slice(1);
3135
+ const ranges = parseClassRanges(body);
3136
+ if (!ranges) return null;
3137
+ boundary = { ranges, negated };
3138
+ }
3139
+ const mint = (prefix = "_v") => v(ctx, prefix);
3140
+ const lbl = v(ctx, "_kwLbl");
3141
+ const valV = v(ctx, "_kwv");
3142
+ const endV = v(ctx, "_kwe");
3143
+ const bodyInd = ind(ctx) + " ";
3144
+ const tries = [];
3145
+ for (const w of def.words) {
3146
+ const cps = Array.from(w, (ch) => ch.codePointAt(0));
3147
+ let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
3148
+ if (boundary) {
3149
+ shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
3150
+ }
3151
+ const m = emitShapeMatch(shape, pos, mint, bodyInd);
3152
+ tries.push(
3153
+ ...m.setup,
3154
+ // Slice from input rather than reusing the literal word: `caseInsensitive`
3155
+ // must return the text as it actually appeared (e.g. "ABC" for keyword
3156
+ // "abc"), matching what the original `RegExp.exec()[0]` returned.
3157
+ `${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
3158
+ );
3159
+ }
3160
+ const stmts = [
3161
+ `${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
3162
+ `${ind(ctx)}${lbl}: {`,
3163
+ ...tries,
3164
+ `${ind(ctx)}}`,
3165
+ // Every word has length >= 1 (checked above), so a real match always
3166
+ // advances past `pos` — `endV === pos` only happens when no candidate matched.
3167
+ ...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
3168
+ ];
3169
+ stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
3170
+ return { stmts, valueVar: valV, endVar: endV };
3171
+ }
2367
3172
  function emitKeywords(def, ctx, pos) {
3173
+ const fast = emitKeywordsFast(def, ctx, pos);
3174
+ if (fast) return fast;
2368
3175
  const alt = def.words.map(escapeKeywordRe).join("|");
2369
3176
  const boundary = def.boundary ? `(?![${def.boundary}])` : "";
2370
3177
  const flags = def.caseInsensitive ? "iuy" : "uy";
@@ -2372,7 +3179,7 @@ function emitKeywords(def, ctx, pos) {
2372
3179
  const key = `${source}/${flags}`;
2373
3180
  let rName = ctx.regexMap.get(key);
2374
3181
  if (rName === void 0) {
2375
- rName = `_re${ctx.regexDecls.length}`;
3182
+ rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
2376
3183
  ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
2377
3184
  ctx.regexMap.set(key, rName);
2378
3185
  }
@@ -2406,11 +3213,11 @@ function emitRegex(def, ctx, pos) {
2406
3213
  }
2407
3214
  }
2408
3215
  const flags = "y" + def.flags.replace(/[gy]/g, "");
2409
- const key = `${def.optimizedSource}/${flags}`;
3216
+ const key = `${def.source}/${flags}`;
2410
3217
  let rName = ctx.regexMap.get(key);
2411
3218
  if (rName === void 0) {
2412
- rName = `_re${ctx.regexDecls.length}`;
2413
- ctx.regexDecls.push(`const ${rName} = /${def.optimizedSource}/${flags}`);
3219
+ rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
3220
+ ctx.regexDecls.push(`const ${rName} = /${def.source}/${flags}`);
2414
3221
  ctx.regexMap.set(key, rName);
2415
3222
  }
2416
3223
  const mv = v(ctx, "_m");
@@ -2495,6 +3302,31 @@ function failsAtStart(p) {
2495
3302
  return false;
2496
3303
  }
2497
3304
  }
3305
+ function armStaticExpected(ctx, p) {
3306
+ return hoistExpected(ctx, staticExpectedArr([p]));
3307
+ }
3308
+ function canMatchEmptyAtStart(p) {
3309
+ const d = p._def;
3310
+ switch (d.tag) {
3311
+ case "regex":
3312
+ return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
3313
+ case "optional":
3314
+ return true;
3315
+ case "many":
3316
+ return true;
3317
+ case "transform":
3318
+ case "label":
3319
+ return canMatchEmptyAtStart(d.parser);
3320
+ case "literal":
3321
+ return d.value.length === 0;
3322
+ default:
3323
+ return false;
3324
+ }
3325
+ }
3326
+ function needsFirstSetGuard(p) {
3327
+ const fs = p._meta.firstSet;
3328
+ return fs.kind !== "any" && !canMatchEmptyAtStart(p);
3329
+ }
2498
3330
  function emitDisjointArm(p, ctx, pos, valV, endV) {
2499
3331
  if (failsAtStart(p)) {
2500
3332
  const r = emit(p, ctx, pos);
@@ -2562,7 +3394,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
2562
3394
  const reKey = `${regexDef.source}/${cleanFlags}`;
2563
3395
  let reVar = ctx.regexMap.get(reKey);
2564
3396
  if (reVar === void 0) {
2565
- reVar = `_re${ctx.regexDecls.length}`;
3397
+ reVar = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
2566
3398
  ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
2567
3399
  ctx.regexMap.set(reKey, reVar);
2568
3400
  }
@@ -2636,21 +3468,26 @@ function emitFirstMatch(def, ctx, pos) {
2636
3468
  const resValV = v(ctx, "_crv");
2637
3469
  const resEndV = v(ctx, "_cre");
2638
3470
  const resOkV = v(ctx, "_crok");
3471
+ const codeV = v(ctx, "_chcode");
2639
3472
  const slots = def.parsers.map(() => v(ctx, "_cfx"));
2640
3473
  const ind0 = ind(ctx);
2641
3474
  const stmts = [
2642
3475
  `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
2643
- `${ind0}let ${slots.join(", ")}`
3476
+ `${ind0}let ${slots.join(", ")}`,
3477
+ `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
2644
3478
  ];
2645
3479
  for (let i = 0; i < def.parsers.length; i++) {
2646
3480
  const p = def.parsers[i];
2647
3481
  const gate = def.gates[i];
2648
3482
  const autoNot = def.autoNot[i];
3483
+ const atStart = failsAtStart(p);
3484
+ const staticFx = armStaticExpected(ctx, p);
3485
+ const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
2649
3486
  let gateCond = null;
2650
3487
  if (gate) {
2651
3488
  const gateIdx = ctx.mapFns.length;
2652
3489
  ctx.mapFns.push(gate);
2653
- gateCond = `_mf[${gateIdx}](_ctx.state)`;
3490
+ gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
2654
3491
  }
2655
3492
  const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
2656
3493
  const armHasAutoNot = !!(autoNot && autoNot.length > 0);
@@ -2660,39 +3497,41 @@ function emitFirstMatch(def, ctx, pos) {
2660
3497
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
2661
3498
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
2662
3499
  const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
3500
+ const failSlot = atStart ? staticFx : "_ctx._fx";
2663
3501
  stmts.push(`${ind0}if (${skipCond}) {`);
2664
- ctx.indent++;
2665
- const ind1 = ind(ctx);
3502
+ if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
3503
+ ctx.indent += fsGuard ? 2 : 1;
2666
3504
  if (markLeaves) {
2667
3505
  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`
3506
+ `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
3507
+ `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
3508
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
3509
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
2672
3510
  );
2673
3511
  }
2674
- const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
3512
+ const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
2675
3513
  stmts.push(...armStmts);
2676
- ctx.indent--;
2677
3514
  if (autoNot && autoNot.length > 0) {
2678
3515
  const anCode = v(ctx, "_anc");
2679
3516
  const rejectCond = autoNot.map(
2680
3517
  (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
2681
3518
  ).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} }`);
3519
+ stmts.push(`${ind(ctx)}if (${okVar}) {`);
3520
+ stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
3521
+ stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
3522
+ stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
3523
+ stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
3524
+ stmts.push(`${ind(ctx)} ${resOkV} = true`);
3525
+ stmts.push(`${ind(ctx)} }`);
3526
+ stmts.push(`${ind(ctx)}}`);
3527
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
3528
+ if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
2692
3529
  } else {
2693
- stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2694
- stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
3530
+ stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
3531
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
2695
3532
  }
3533
+ ctx.indent -= fsGuard ? 2 : 1;
3534
+ if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
2696
3535
  stmts.push(`${ind0}}`);
2697
3536
  }
2698
3537
  const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
@@ -2715,7 +3554,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2715
3554
  ctx.mapFnSrcs.push(def.fnSrc ?? null);
2716
3555
  const vv = v(ctx);
2717
3556
  return {
2718
- stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = _mf[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
3557
+ stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
2719
3558
  valueVar: vv,
2720
3559
  endVar: endV
2721
3560
  };
@@ -2725,7 +3564,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2725
3564
  function emitLiteralCondition(litVal, pos) {
2726
3565
  const len = litVal.length;
2727
3566
  if (len === 0) return "true";
2728
- if (len > 4) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
3567
+ if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
2729
3568
  const checks = [`${pos} + ${len} <= input.length`];
2730
3569
  for (let i = 0; i < len; i++) {
2731
3570
  const code = litVal.codePointAt(i);
@@ -2774,7 +3613,7 @@ function emitMany(def, ctx, pos) {
2774
3613
  itemPos = npV;
2775
3614
  }
2776
3615
  }
2777
- const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
3616
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
2778
3617
  stmts.push(
2779
3618
  ...iterStmts,
2780
3619
  `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
@@ -2788,7 +3627,7 @@ function emitMany(def, ctx, pos) {
2788
3627
  function emitOptional(def, ctx, pos) {
2789
3628
  const valV = v(ctx, "_opt");
2790
3629
  const endV = v(ctx, "_opte");
2791
- const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
3630
+ const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
2792
3631
  const ind0 = ind(ctx);
2793
3632
  const stmts = [
2794
3633
  ...lblStmts,
@@ -2800,7 +3639,7 @@ function emitOptional(def, ctx, pos) {
2800
3639
  function emitSepBy(_p, def, ctx, pos) {
2801
3640
  const arrV = v(ctx, "_arr");
2802
3641
  const curV = v(ctx, "_cur");
2803
- const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
3642
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
2804
3643
  const stmts = [
2805
3644
  `${ind(ctx)}const ${arrV} = []`,
2806
3645
  `${ind(ctx)}let ${curV} = ${pos}`,
@@ -2832,11 +3671,11 @@ function emitSepBy(_p, def, ctx, pos) {
2832
3671
  );
2833
3672
  sepAtPos = spV;
2834
3673
  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);
3674
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2836
3675
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
2837
3676
  const npV = v(ctx, "_np");
2838
3677
  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);
3678
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2840
3679
  stmts.push(
2841
3680
  ...nextStmts,
2842
3681
  // item failed → unwind the separator too, back to the end of the last item
@@ -2849,11 +3688,11 @@ function emitSepBy(_p, def, ctx, pos) {
2849
3688
  const spV = v(ctx, "_sp");
2850
3689
  stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
2851
3690
  sepAtPos = spV;
2852
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3691
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2853
3692
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2854
3693
  const npV = v(ctx, "_np");
2855
3694
  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);
3695
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2857
3696
  stmts.push(
2858
3697
  ...nextStmts,
2859
3698
  `${ind(ctx)}if (!${nextOk}) break`,
@@ -2870,10 +3709,10 @@ function emitSepBy(_p, def, ctx, pos) {
2870
3709
  `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
2871
3710
  );
2872
3711
  }
2873
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3712
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2874
3713
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2875
3714
  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);
3715
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
2877
3716
  stmts.push(
2878
3717
  ...nextStmts,
2879
3718
  `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
@@ -2931,7 +3770,7 @@ function emitScanTo(def, ctx, pos) {
2931
3770
  return { stmts, valueVar: valV, endVar: curV };
2932
3771
  }
2933
3772
  function emitNot(def, ctx, pos) {
2934
- const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
3773
+ const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
2935
3774
  const label2 = JSON.stringify(`not(${def.parser._tag})`);
2936
3775
  return {
2937
3776
  stmts: [
@@ -2943,16 +3782,17 @@ function emitNot(def, ctx, pos) {
2943
3782
  };
2944
3783
  }
2945
3784
  function emitNode(def, ctx, pos) {
2946
- const mkType = analyzeMkInlineBuild(def);
3785
+ const structural = def.build === void 0;
3786
+ const mkType = structural ? null : analyzeMkInlineBuild(def);
2947
3787
  let buildIdx = null;
2948
- if (!mkType) {
3788
+ if (!mkType && !structural) {
2949
3789
  buildIdx = ctx.buildFns.length;
2950
3790
  ctx.buildFns.push(def.build);
2951
3791
  ctx.buildSrcs.push(def.buildSrc ?? null);
2952
3792
  }
2953
3793
  const i = ind(ctx);
2954
- const capturesTrivia = mkType !== null || buildReadsTrivia(def);
2955
- const clonesState = buildReadsState(def);
3794
+ const capturesTrivia = mkType !== null || structural || buildReadsTrivia(def);
3795
+ const clonesState = structural || buildReadsState(def);
2956
3796
  const chV = v(ctx, "_ch");
2957
3797
  const rawV = v(ctx, "_raw");
2958
3798
  const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
@@ -2975,7 +3815,8 @@ function emitNode(def, ctx, pos) {
2975
3815
  stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
2976
3816
  }
2977
3817
  const ndV = v(ctx, "_nd");
2978
- const ndExpr = mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `_build[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
3818
+ const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
3819
+ const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
2979
3820
  const finalExpr = def.collapse ? `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})` : ndExpr;
2980
3821
  stmts.push(
2981
3822
  `${i}const ${ndV} = ${finalExpr}`,
@@ -3000,8 +3841,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
3000
3841
  return { stmts, valueVar: vv, endVar: ev };
3001
3842
  }
3002
3843
  function emitLazy(p, def, ctx, pos) {
3844
+ const canonical = ctx.ruleNames?.get(p);
3003
3845
  const usage = ctx.lazyUsage;
3004
- if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
3846
+ if (!canonical && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
3005
3847
  let resolved;
3006
3848
  try {
3007
3849
  resolved = def.thunk();
@@ -3011,7 +3853,7 @@ function emitLazy(p, def, ctx, pos) {
3011
3853
  return emit(resolved, ctx, pos);
3012
3854
  }
3013
3855
  if (!ctx.namedParsers.has(p)) {
3014
- const fnName2 = `_pf${ctx.namedParsers.size}`;
3856
+ const fnName2 = canonical ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
3015
3857
  ctx.namedParsers.set(p, fnName2);
3016
3858
  let resolved;
3017
3859
  try {
@@ -3022,11 +3864,14 @@ function emitLazy(p, def, ctx, pos) {
3022
3864
  }
3023
3865
  const savedIndent = ctx.indent;
3024
3866
  const savedFailLabel = ctx.failLabel;
3867
+ const savedRecord = ctx.recordFail;
3025
3868
  ctx.indent = 1;
3026
3869
  ctx.failLabel = "_pfail";
3870
+ ctx.recordFail = true;
3027
3871
  const r = emit(resolved, ctx, "_pos");
3028
3872
  ctx.indent = savedIndent;
3029
3873
  ctx.failLabel = savedFailLabel;
3874
+ ctx.recordFail = savedRecord;
3030
3875
  pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
3031
3876
  }
3032
3877
  const fnName = ctx.namedParsers.get(p);
@@ -3126,7 +3971,7 @@ function emit(p, ctx, pos) {
3126
3971
  return {
3127
3972
  stmts: [
3128
3973
  ...inner.stmts,
3129
- `${ind(ctx)}const ${mv} = _mf[${fnIdx}](${inner.valueVar}, { start: ${pos}, end: ${inner.endVar} })`
3974
+ `${ind(ctx)}const ${mv} = ${mfRef(ctx)}[${fnIdx}](${inner.valueVar}, { start: ${pos}, end: ${inner.endVar} })`
3130
3975
  ],
3131
3976
  valueVar: mv,
3132
3977
  endVar: inner.endVar
@@ -3189,7 +4034,7 @@ function emit(p, ctx, pos) {
3189
4034
  const vv = v(ctx);
3190
4035
  return {
3191
4036
  stmts: [
3192
- ...emitIfFail(ctx, `!_mf[${fnIdx}](_ctx.state)`, failBody(ctx, '"gate"', pos)),
4037
+ ...emitIfFail(ctx, `!${mfRef(ctx)}[${fnIdx}](_ctx.state)`, failBody(ctx, '"gate"', pos)),
3193
4038
  `${ind(ctx)}const ${vv} = null`
3194
4039
  ],
3195
4040
  valueVar: vv,
@@ -3206,11 +4051,14 @@ function emit(p, ctx, pos) {
3206
4051
  ctx.namedParsers.set(innerParser, fnName);
3207
4052
  const savedIndent = ctx.indent;
3208
4053
  const savedFailLabel = ctx.failLabel;
4054
+ const savedRecord = ctx.recordFail;
3209
4055
  ctx.indent = 1;
3210
4056
  ctx.failLabel = "_pfail";
4057
+ ctx.recordFail = true;
3211
4058
  const innerR = emit(innerParser, ctx, "_pos");
3212
4059
  ctx.indent = savedIndent;
3213
4060
  ctx.failLabel = savedFailLabel;
4061
+ ctx.recordFail = savedRecord;
3214
4062
  pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
3215
4063
  }
3216
4064
  const fn = ctx.namedParsers.get(innerParser);
@@ -3219,7 +4067,7 @@ function emit(p, ctx, pos) {
3219
4067
  const ev = v(ctx, "_wce");
3220
4068
  return {
3221
4069
  stmts: [
3222
- `${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
4070
+ `${ind(ctx)}const ${rv} = { ..._ctx, state: ${mfRef(ctx)}[${evIdx}]() }`,
3223
4071
  `${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
3224
4072
  // withCtx runs on a spread child ctx — copy its recorded failure back
3225
4073
  // and propagate the inner failure verbatim (interpreter parity).
@@ -3350,6 +4198,39 @@ function analyzeLazyUsageMulti(roots) {
3350
4198
  for (const root of roots) walk2(root);
3351
4199
  return { counts, recursive };
3352
4200
  }
4201
+ function ruleDependencies(ruleMap) {
4202
+ const nameOf = /* @__PURE__ */ new Map();
4203
+ for (const [name, comb] of ruleMap) nameOf.set(comb, name);
4204
+ const deps = /* @__PURE__ */ new Map();
4205
+ for (const [name, comb] of ruleMap) {
4206
+ const found = /* @__PURE__ */ new Set();
4207
+ const seen = /* @__PURE__ */ new Set();
4208
+ const walk2 = (p, isRoot) => {
4209
+ const def = p._def;
4210
+ const boundary = def.tag === "lazy" ? p._ruleName ?? nameOf.get(p) : nameOf.get(p);
4211
+ if (!isRoot && boundary !== void 0) {
4212
+ found.add(boundary);
4213
+ return;
4214
+ }
4215
+ if (seen.has(p)) return;
4216
+ seen.add(p);
4217
+ if (def.tag === "lazy") {
4218
+ let resolved;
4219
+ try {
4220
+ resolved = def.thunk();
4221
+ } catch {
4222
+ return;
4223
+ }
4224
+ walk2(resolved, false);
4225
+ return;
4226
+ }
4227
+ for (const child of childrenOf(def)) walk2(child, false);
4228
+ };
4229
+ walk2(comb, true);
4230
+ deps.set(name, [...found]);
4231
+ }
4232
+ return deps;
4233
+ }
3353
4234
  function compile(combinator, mapFnSources) {
3354
4235
  const ctx = {
3355
4236
  vars: 0,
@@ -3358,6 +4239,7 @@ function compile(combinator, mapFnSources) {
3358
4239
  regexMap: /* @__PURE__ */ new Map(),
3359
4240
  expectedDecls: [],
3360
4241
  expectedMap: /* @__PURE__ */ new Map(),
4242
+ recordFail: true,
3361
4243
  mapFns: [],
3362
4244
  mapFnSrcs: [],
3363
4245
  buildFns: [],
@@ -3432,6 +4314,150 @@ function compile(combinator, mapFnSources) {
3432
4314
  }
3433
4315
  };
3434
4316
  }
4317
+ function compileLinkable(ruleMapArg, ns) {
4318
+ if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
4319
+ const ruleMap = ruleMapArg.filter(([, val]) => {
4320
+ const d = val._def;
4321
+ if (d.tag !== "lazy") return true;
4322
+ try {
4323
+ d.thunk();
4324
+ return true;
4325
+ } catch {
4326
+ return false;
4327
+ }
4328
+ });
4329
+ const ctx = {
4330
+ vars: 0,
4331
+ indent: 1,
4332
+ regexDecls: [],
4333
+ regexMap: /* @__PURE__ */ new Map(),
4334
+ expectedDecls: [],
4335
+ expectedMap: /* @__PURE__ */ new Map(),
4336
+ recordFail: true,
4337
+ mapFns: [],
4338
+ mapFnSrcs: [],
4339
+ buildFns: [],
4340
+ buildSrcs: [],
4341
+ runtimeParsers: [],
4342
+ needsCollator: false,
4343
+ namedParsers: /* @__PURE__ */ new Map(),
4344
+ triviaCaptureNames: /* @__PURE__ */ new Map(),
4345
+ triviaFnNames: /* @__PURE__ */ new Map(),
4346
+ namedFnDecls: [],
4347
+ capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
4348
+ lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
4349
+ ns
4350
+ };
4351
+ const seen = /* @__PURE__ */ new Map();
4352
+ const ruleNames = /* @__PURE__ */ new Map();
4353
+ const fnNameToKey = /* @__PURE__ */ new Map();
4354
+ for (const [key, rule] of ruleMap) {
4355
+ const base = `_r_${key.replace(/[^A-Za-z0-9_$]/g, "_")}`;
4356
+ const n = seen.get(base) ?? 0;
4357
+ seen.set(base, n + 1);
4358
+ const fn = n === 0 ? base : `${base}$${n}`;
4359
+ ruleNames.set(rule, fn);
4360
+ fnNameToKey.set(fn, key);
4361
+ }
4362
+ ctx.ruleNames = ruleNames;
4363
+ {
4364
+ const scanned = /* @__PURE__ */ new Set();
4365
+ const scanExternal = (p) => {
4366
+ if (scanned.has(p)) return;
4367
+ scanned.add(p);
4368
+ const def = p._def;
4369
+ if (def.tag === "lazy") {
4370
+ let resolved;
4371
+ try {
4372
+ resolved = def.thunk();
4373
+ } catch {
4374
+ resolved = void 0;
4375
+ }
4376
+ if (resolved === void 0) {
4377
+ const name = p._ruleName;
4378
+ if (name && !ctx.namedParsers.has(p)) {
4379
+ const fn = `_r_${name.replace(/[^A-Za-z0-9_$]/g, "_")}`;
4380
+ ruleNames.set(p, fn);
4381
+ ctx.namedParsers.set(p, fn);
4382
+ }
4383
+ return;
4384
+ }
4385
+ scanExternal(resolved);
4386
+ return;
4387
+ }
4388
+ for (const child of childrenOf(def)) scanExternal(child);
4389
+ };
4390
+ for (const [, rule] of ruleMap) scanExternal(rule);
4391
+ }
4392
+ const perEntry = [];
4393
+ for (const [key, rule] of ruleMap) {
4394
+ const fn = ruleNames.get(rule);
4395
+ if (!ctx.namedParsers.has(rule)) {
4396
+ ctx.namedParsers.set(rule, fn);
4397
+ let resolved = rule;
4398
+ const def = rule._def;
4399
+ if (def.tag === "lazy") {
4400
+ try {
4401
+ resolved = def.thunk();
4402
+ } catch {
4403
+ return null;
4404
+ }
4405
+ }
4406
+ const savedIndent = ctx.indent, savedFail = ctx.failLabel, savedRec = ctx.recordFail;
4407
+ ctx.indent = 1;
4408
+ ctx.failLabel = "_pfail";
4409
+ ctx.recordFail = true;
4410
+ const body = emit(resolved, ctx, "_pos");
4411
+ ctx.indent = savedIndent;
4412
+ ctx.failLabel = savedFail;
4413
+ ctx.recordFail = savedRec;
4414
+ pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
4415
+ }
4416
+ perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
4417
+ }
4418
+ if (ctx.runtimeParsers.length !== 0) return null;
4419
+ const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
4420
+ const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
4421
+ const ruleFns = /* @__PURE__ */ new Map();
4422
+ const privateFns = [];
4423
+ for (const decl of ctx.namedFnDecls) {
4424
+ const fnName = decl.match(/^function (\S+?)\(/)?.[1];
4425
+ const key = fnName ? fnNameToKey.get(fnName) : void 0;
4426
+ if (key !== void 0) ruleFns.set(key, decl);
4427
+ else privateFns.push(decl);
4428
+ }
4429
+ const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
4430
+ const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
4431
+ const prelude = [
4432
+ ...ctx.regexDecls,
4433
+ ...ctx.expectedDecls,
4434
+ mfDecl,
4435
+ buildDecl,
4436
+ ...privateFns
4437
+ ].filter(Boolean);
4438
+ const wrappers = /* @__PURE__ */ new Map();
4439
+ for (const { key, r } of perEntry) {
4440
+ wrappers.set(key, [
4441
+ `function(input, _pos, _ctx) {`,
4442
+ ` let pos = _pos`,
4443
+ ...r.stmts,
4444
+ ` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
4445
+ `}`
4446
+ ].join("\n"));
4447
+ }
4448
+ return {
4449
+ ns,
4450
+ keys: perEntry.map((e) => e.key),
4451
+ prelude,
4452
+ ruleFns,
4453
+ wrappers,
4454
+ deps: ruleDependencies(ruleMap),
4455
+ needsEmptyTl: !!ctx.needsEmptyTl,
4456
+ needsCollator: ctx.needsCollator,
4457
+ mfFns: mfSrcs ? [] : ctx.mapFns,
4458
+ buildFns: buildSrcs ? [] : ctx.buildFns
4459
+ };
4460
+ }
3435
4461
  function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
3436
4462
  const bodyLines = [
3437
4463
  ` let pos = _pos`,
@@ -3470,6 +4496,87 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
3470
4496
  ].join("\n");
3471
4497
  }
3472
4498
 
4499
+ // src/compiler/linker.ts
4500
+ var _nsCounter = 0;
4501
+ function linkable(rulesMap, ns) {
4502
+ const pieces = compileLinkable([...Object.entries(rulesMap)], ns ?? `_lk${_nsCounter++}_`);
4503
+ if (!pieces) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
4504
+ return pieces;
4505
+ }
4506
+ function cstBuildHost(type, children, _rawChildren, span, _triviaLog, state) {
4507
+ return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
4508
+ }
4509
+ function pick(grammar, names) {
4510
+ const p = grammar.ruleFns instanceof Map ? grammar : linkable(grammar);
4511
+ const keep = /* @__PURE__ */ new Set();
4512
+ const has = new Set(p.keys);
4513
+ for (const n of names) {
4514
+ if (!has.has(n)) throw new Error(`pick: rule "${n}" is not in this grammar (available: ${p.keys.join(", ")})`);
4515
+ }
4516
+ const visit = (n) => {
4517
+ if (keep.has(n) || !has.has(n)) return;
4518
+ keep.add(n);
4519
+ for (const d of p.deps.get(n) ?? []) visit(d);
4520
+ };
4521
+ for (const n of names) visit(n);
4522
+ const filt = (m) => new Map([...m].filter(([k]) => keep.has(k)));
4523
+ return {
4524
+ ...p,
4525
+ keys: [...keep],
4526
+ ruleFns: filt(p.ruleFns),
4527
+ wrappers: filt(p.wrappers),
4528
+ deps: filt(p.deps)
4529
+ };
4530
+ }
4531
+ function fusedBody(pieces) {
4532
+ const winner = /* @__PURE__ */ new Map();
4533
+ for (const p of pieces) for (const k of p.keys) winner.set(k, p);
4534
+ for (const [k, p] of winner) {
4535
+ for (const d of p.deps.get(k) ?? []) {
4536
+ if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
4537
+ }
4538
+ }
4539
+ const contributing = new Set(winner.values());
4540
+ const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
4541
+ const needsCollator = [...contributing].some((p) => p.needsCollator);
4542
+ const lines = [
4543
+ // Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
4544
+ "const _pfFail = {}",
4545
+ "let _pfEnd",
4546
+ ...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
4547
+ ...needsCollator ? ["const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })"] : [],
4548
+ // Each contributing artifact's namespaced private prelude (regexes, _pf, …).
4549
+ ...[...contributing].flatMap((p) => p.prelude),
4550
+ // The winning `_r_<Name>` function for each rule (one per name → no redeclare).
4551
+ ...[...winner].map(([k, p]) => p.ruleFns.get(k))
4552
+ ];
4553
+ const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
4554
+ const body = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
4555
+ const env = {};
4556
+ for (const p of contributing) {
4557
+ if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
4558
+ if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
4559
+ }
4560
+ return { body, env };
4561
+ }
4562
+ function fuseRules(pieces) {
4563
+ const { body, env } = fusedBody(pieces);
4564
+ return new Function("_env", body)(env);
4565
+ }
4566
+ var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
4567
+ function itemPieces(item) {
4568
+ const carried = item[COMPOSED_PIECES];
4569
+ if (Array.isArray(carried)) return carried;
4570
+ if (item.ruleFns instanceof Map) return [item];
4571
+ return [linkable(item)];
4572
+ }
4573
+ function compose(items) {
4574
+ const pieces = items.flatMap(itemPieces);
4575
+ const map = fuseRules(pieces);
4576
+ Object.defineProperty(map, COMPOSED_PIECES, { value: pieces, enumerable: false });
4577
+ return map;
4578
+ }
4579
+
3473
4580
  // src/combinators/guard.ts
3474
4581
  function guard(predicate) {
3475
4582
  const meta = {
@@ -3667,6 +4774,7 @@ function manyRecover(item, until) {
3667
4774
  }
3668
4775
 
3669
4776
  // src/functional/doc.ts
4777
+ var REENTRY_MAX_SPAN_FRACTION = 0.5;
3670
4778
  function isNode(x) {
3671
4779
  return typeof x === "object" && x !== null && x._tag === "node";
3672
4780
  }
@@ -3727,7 +4835,48 @@ function graftAndShift(root, path, newNode, delta) {
3727
4835
  children: newChildren
3728
4836
  };
3729
4837
  }
3730
- var FunctionalDocImpl = class _FunctionalDocImpl {
4838
+ function structurallyEqual(a, b) {
4839
+ if (a === b) return true;
4840
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
4841
+ const at = a._tag;
4842
+ const bt = b._tag;
4843
+ if (at !== bt) return false;
4844
+ const as = a.span;
4845
+ const bs = b.span;
4846
+ if (as || bs) {
4847
+ if (!as || !bs || as.start !== bs.start || as.end !== bs.end) return false;
4848
+ }
4849
+ if (at === "leaf" || at === "trivia") {
4850
+ return a.value === b.value;
4851
+ }
4852
+ if (a.type !== b.type) return false;
4853
+ const ac = a.children;
4854
+ const bc = b.children;
4855
+ if (ac || bc) {
4856
+ if (!ac || !bc || ac.length !== bc.length) return false;
4857
+ for (let i = 0; i < ac.length; i++) if (!structurallyEqual(ac[i], bc[i])) return false;
4858
+ }
4859
+ return true;
4860
+ }
4861
+ function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced) {
4862
+ if (!produced.ok) return false;
4863
+ if (boundary >= newInput.length) return true;
4864
+ for (const sentinel of [" ", "\uFFFF"]) {
4865
+ if (newInput[boundary] === sentinel) continue;
4866
+ const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
4867
+ const ctx = { trackLines: false, state, build };
4868
+ let r;
4869
+ try {
4870
+ r = ruleFn(probed, start, ctx);
4871
+ } catch {
4872
+ return false;
4873
+ }
4874
+ if (!r.ok || r.span.end !== produced.span.end) return false;
4875
+ if (!structurallyEqual(r.value, produced.value)) return false;
4876
+ }
4877
+ return true;
4878
+ }
4879
+ var ParseDocImpl = class _ParseDocImpl {
3731
4880
  _registry;
3732
4881
  _rootRule;
3733
4882
  _opts;
@@ -3744,7 +4893,7 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
3744
4893
  }
3745
4894
  edit(from, to, replacement) {
3746
4895
  const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
3747
- const reparse = () => makeFunctionalDoc(this._registry, this._rootRule, newInput, this._opts);
4896
+ const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
3748
4897
  if (!this.tree) return reparse();
3749
4898
  const delta = replacement.length - (to - from);
3750
4899
  const found = findContaining(this.tree, from);
@@ -3758,41 +4907,78 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
3758
4907
  }
3759
4908
  const rebuild = this._opts.rebuild ?? defaultRebuild;
3760
4909
  for (const { node: node2, path } of candidates) {
4910
+ if (node2.span.end - node2.span.start > this.input.length * REENTRY_MAX_SPAN_FRACTION) break;
3761
4911
  const ruleFn = this._registry[node2.type];
3762
4912
  if (!ruleFn) continue;
3763
- const ctx = { trackLines: false, state: node2.state };
4913
+ if (!(node2.span.start <= from && to <= node2.span.end)) continue;
4914
+ const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
3764
4915
  const r = ruleFn(newInput, node2.span.start, ctx);
3765
4916
  if (!r.ok) continue;
3766
- if (r.span.end === node2.span.end + delta) {
3767
- if (delta === 0) {
3768
- const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
3769
- return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
3770
- }
3771
- if (this._opts.rebuild) return reparse();
3772
- const newTree = graftAndShift(this.tree, path, r.value, delta);
3773
- return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
4917
+ if (r.span.end !== node2.span.end + delta) continue;
4918
+ if (!boundaryIsSafe(ruleFn, newInput, node2.span.start, node2.span.end + delta, node2.state, this._opts.build, r)) {
4919
+ continue;
4920
+ }
4921
+ if (delta === 0) {
4922
+ const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
4923
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
3774
4924
  }
4925
+ if (this._opts.rebuild) return reparse();
4926
+ const newTree = graftAndShift(this.tree, path, r.value, delta);
4927
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
3775
4928
  }
3776
4929
  return reparse();
3777
4930
  }
3778
4931
  };
3779
- function makeFunctionalDoc(registry, rootRule, input, opts = {}) {
4932
+ function parseDoc(registry, rootRule, input, opts = {}) {
3780
4933
  const ruleFn = registry[rootRule];
3781
4934
  if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
3782
- const ctx = { trackLines: false, state: opts.state };
4935
+ const ctx = { trackLines: false, state: opts.state, build: opts.build };
3783
4936
  const r = ruleFn(input, 0, ctx);
3784
4937
  if (r.ok) {
3785
- return new FunctionalDocImpl(registry, rootRule, opts, r.value, [], input);
4938
+ return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
4939
+ }
4940
+ return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
4941
+ }
4942
+
4943
+ // src/functional/run.ts
4944
+ var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
4945
+ function run(entry, input, options = {}) {
4946
+ const triviaLog = [];
4947
+ const errors = [];
4948
+ const ctx = {
4949
+ trackLines: false,
4950
+ _triviaLog: triviaLog,
4951
+ _errors: errors,
4952
+ build: options.build,
4953
+ state: options.state
4954
+ };
4955
+ const r = invoke(entry, input, 0, ctx);
4956
+ let unconsumedFrom = null;
4957
+ if (r.ok) {
4958
+ let pos = r.span?.end ?? 0;
4959
+ if (options.trivia && pos < input.length) {
4960
+ const t = invoke(options.trivia, input, pos, { trackLines: false });
4961
+ if (t.ok && t.span.end > pos) pos = t.span.end;
4962
+ }
4963
+ unconsumedFrom = pos < input.length ? pos : null;
3786
4964
  }
3787
- return new FunctionalDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
4965
+ return {
4966
+ ok: r.ok,
4967
+ value: r.ok ? r.value : void 0,
4968
+ span: r.span ?? { start: 0, end: 0 },
4969
+ expected: r.ok ? [] : r.expected ?? [],
4970
+ errors,
4971
+ triviaLog,
4972
+ unconsumedFrom
4973
+ };
3788
4974
  }
3789
4975
 
3790
4976
  // src/cst/trivia-index.ts
3791
- function merge(map, key, run) {
3792
- if (run.length === 0) return;
4977
+ function merge(map, key, run2) {
4978
+ if (run2.length === 0) return;
3793
4979
  const existing = map.get(key);
3794
- if (existing) existing.push(...run);
3795
- else map.set(key, [...run]);
4980
+ if (existing) existing.push(...run2);
4981
+ else map.set(key, [...run2]);
3796
4982
  }
3797
4983
  function buildTriviaIndex(root, input = "", opts) {
3798
4984
  const before = /* @__PURE__ */ new Map();
@@ -3804,11 +4990,11 @@ function buildTriviaIndex(root, input = "", opts) {
3804
4990
  for (let i = 0; i < log.length; i += 3) {
3805
4991
  const tStart = log[i], tEnd = log[i + 1], insertIdx = log[i + 2];
3806
4992
  if (tEnd <= tStart) continue;
3807
- const run = [{ value: input.slice(tStart, tEnd), span: { start: tStart, end: tEnd } }];
4993
+ const run2 = [{ value: input.slice(tStart, tEnd), span: { start: tStart, end: tEnd } }];
3808
4994
  const next = raw[insertIdx];
3809
- if (next) merge(before, next.span.start, run);
4995
+ if (next) merge(before, next.span.start, run2);
3810
4996
  const prev = insertIdx > 0 ? raw[insertIdx - 1] : void 0;
3811
- if (prev) merge(after, prev.span.end, run);
4997
+ if (prev) merge(after, prev.span.end, run2);
3812
4998
  }
3813
4999
  }
3814
5000
  const ch = node2.children;
@@ -3825,17 +5011,17 @@ function buildTriviaIndex(root, input = "", opts) {
3825
5011
  const rootSpan = root?.span;
3826
5012
  const re = new RegExp(opts.trivia.source, opts.trivia.flags.replace(/[gy]/g, "") + "y");
3827
5013
  const scanFrom = (from) => {
3828
- const run = [];
5014
+ const run2 = [];
3829
5015
  let pos = from;
3830
5016
  re.lastIndex = pos;
3831
5017
  let m = re.exec(input);
3832
5018
  while (m && m.index === pos && m[0].length > 0) {
3833
- run.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
5019
+ run2.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
3834
5020
  pos += m[0].length;
3835
5021
  re.lastIndex = pos;
3836
5022
  m = re.exec(input);
3837
5023
  }
3838
- return run;
5024
+ return run2;
3839
5025
  };
3840
5026
  if (rootSpan && rootSpan.start > 0) {
3841
5027
  const lead = scanFrom(0);
@@ -3921,22 +5107,296 @@ function triviaEntries(log, labels, opts) {
3921
5107
  }
3922
5108
  };
3923
5109
  }
5110
+
5111
+ // src/cst/offset-model.ts
5112
+ var { isFinite } = Number;
5113
+ function floorIndex(arr, len, x) {
5114
+ let lo = 0;
5115
+ let hi = len - 1;
5116
+ let ans = -1;
5117
+ while (lo <= hi) {
5118
+ const mid = lo + hi >>> 1;
5119
+ if (arr[mid] <= x) {
5120
+ ans = mid;
5121
+ lo = mid + 1;
5122
+ } else {
5123
+ hi = mid - 1;
5124
+ }
5125
+ }
5126
+ return ans;
5127
+ }
5128
+ var OffsetIndex = class {
5129
+ /** Slot starts, sorted ascending. Parallel to `_ends`. */
5130
+ _starts;
5131
+ _ends;
5132
+ count;
5133
+ docStart;
5134
+ docEnd;
5135
+ constructor(slots, docStart = 0, docEnd) {
5136
+ const sorted = [...slots].sort((a, b) => a.start - b.start);
5137
+ const starts = [];
5138
+ const ends = [];
5139
+ let prevEnd = docStart;
5140
+ for (const s of sorted) {
5141
+ if (!isFinite(s.start) || !isFinite(s.end) || s.end < s.start) {
5142
+ throw new RangeError(`OffsetIndex: invalid slot [${s.start}, ${s.end}]`);
5143
+ }
5144
+ if (s.start < prevEnd) {
5145
+ throw new RangeError(`OffsetIndex: overlapping/out-of-order slot [${s.start}, ${s.end}] after end ${prevEnd}`);
5146
+ }
5147
+ starts.push(s.start);
5148
+ ends.push(s.end);
5149
+ prevEnd = s.end;
5150
+ }
5151
+ this._starts = starts;
5152
+ this._ends = ends;
5153
+ this.count = starts.length;
5154
+ this.docStart = docStart;
5155
+ this.docEnd = docEnd ?? (this.count ? Math.max(prevEnd, ends[ends.length - 1]) : docStart);
5156
+ }
5157
+ /** The slot at index `i` (0-based, source order), or undefined. */
5158
+ slot(i) {
5159
+ if (i < 0 || i >= this.count) return void 0;
5160
+ return { start: this._starts[i], end: this._ends[i] };
5161
+ }
5162
+ /**
5163
+ * Index of the gap that CONTAINS `offset`, in `[0, count]`. Gap `i` spans
5164
+ * `[endOf(i-1), startOf(i))` where `endOf(-1)=docStart` and `startOf(count)=docEnd`.
5165
+ * An offset that lands inside a slot returns that slot's TRAILING gap index
5166
+ * (i.e. the gap after it) — callers querying at slot boundaries get the
5167
+ * intuitive answer via `gapBefore`/`gapAfter`.
5168
+ */
5169
+ gapIndexAt(offset) {
5170
+ const i = floorIndex(this._starts, this.count, offset);
5171
+ if (i < 0) return 0;
5172
+ if (offset < this._ends[i]) return i + 1;
5173
+ return i + 1;
5174
+ }
5175
+ /** The gap at index `i` in `[0, count]`. */
5176
+ gap(i) {
5177
+ if (i < 0 || i > this.count) return void 0;
5178
+ const start = i === 0 ? this.docStart : this._ends[i - 1];
5179
+ const end = i === this.count ? this.docEnd : this._starts[i];
5180
+ return { start, end };
5181
+ }
5182
+ /**
5183
+ * The trivia gap immediately BEFORE `offset` (`[prevSlotEnd, offset)`), where
5184
+ * `prevSlotEnd` is the end of the last slot ending at or before `offset` (or
5185
+ * `docStart`). For a real slot start this is the gap preceding that slot; for an
5186
+ * adjacent slot the gap is empty and agrees with `gapAfter` on the same seam.
5187
+ */
5188
+ gapBefore(offset) {
5189
+ const e = floorIndex(this._ends, this.count, offset);
5190
+ const start = e >= 0 ? this._ends[e] : this.docStart;
5191
+ return { start, end: offset };
5192
+ }
5193
+ /**
5194
+ * The trivia gap immediately AFTER `offset` (`[offset, nextSlotStart)`), where
5195
+ * `nextSlotStart` is the least slot start at or after `offset` (or `docEnd`).
5196
+ */
5197
+ gapAfter(offset) {
5198
+ return { start: offset, end: this._ceilStart(offset) };
5199
+ }
5200
+ /** Least slot start `>= offset`, else `docEnd`. */
5201
+ _ceilStart(offset) {
5202
+ let lo = 0;
5203
+ let hi = this.count - 1;
5204
+ let ans = this.docEnd;
5205
+ while (lo <= hi) {
5206
+ const mid = lo + hi >>> 1;
5207
+ if (this._starts[mid] >= offset) {
5208
+ ans = this._starts[mid];
5209
+ hi = mid - 1;
5210
+ } else {
5211
+ lo = mid + 1;
5212
+ }
5213
+ }
5214
+ return ans;
5215
+ }
5216
+ /** Is this gap empty (adjacent slots, no trivia)? */
5217
+ isEmpty(gap) {
5218
+ return gap.end <= gap.start;
5219
+ }
5220
+ };
5221
+ var NL = 10;
5222
+ var CR = 13;
5223
+ var SP = 32;
5224
+ var TAB = 9;
5225
+ function gapText(source, gap) {
5226
+ return gap.end > gap.start ? source.slice(gap.start, gap.end) : "";
5227
+ }
5228
+ function lineBreaksIn(source, gap) {
5229
+ let n = 0;
5230
+ for (let i = gap.start; i < gap.end; i++) {
5231
+ const c = source.charCodeAt(i);
5232
+ if (c === NL) n++;
5233
+ else if (c === CR && source.charCodeAt(i + 1) !== NL) n++;
5234
+ }
5235
+ return n;
5236
+ }
5237
+ function blankLinesIn(source, gap) {
5238
+ return Math.max(0, lineBreaksIn(source, gap) - 1);
5239
+ }
5240
+ function lineStartWithin(source, gap) {
5241
+ for (let i = gap.end - 1; i >= gap.start; i--) {
5242
+ const c = source.charCodeAt(i);
5243
+ if (c === NL || c === CR) return i + 1;
5244
+ }
5245
+ return gap.start;
5246
+ }
5247
+ function indentWidth(source, gap) {
5248
+ const ls = lineStartWithin(source, gap);
5249
+ if (ls === gap.start) return 0;
5250
+ return gap.end - ls;
5251
+ }
5252
+ function indentMixed(source, gap) {
5253
+ const ls = lineStartWithin(source, gap);
5254
+ let sawSpace = false;
5255
+ let sawTab = false;
5256
+ for (let i = ls; i < gap.end; i++) {
5257
+ const c = source.charCodeAt(i);
5258
+ if (c === SP) sawSpace = true;
5259
+ else if (c === TAB) sawTab = true;
5260
+ }
5261
+ return sawSpace && sawTab;
5262
+ }
5263
+ function commentsIn(source, gap, comment) {
5264
+ const re = new RegExp(comment.source, comment.flags.replace(/[gy]/g, "") + "g");
5265
+ re.lastIndex = gap.start;
5266
+ const out = [];
5267
+ let m;
5268
+ while ((m = re.exec(source)) && m.index < gap.end) {
5269
+ if (m[0].length === 0) {
5270
+ re.lastIndex++;
5271
+ continue;
5272
+ }
5273
+ if (m.index + m[0].length > gap.end) break;
5274
+ out.push({ start: m.index, end: m.index + m[0].length });
5275
+ }
5276
+ return out;
5277
+ }
5278
+ function gapIsSignificant(source, gap, comment) {
5279
+ if (gap.end <= gap.start) return false;
5280
+ if (lineBreaksIn(source, gap) > 0) return true;
5281
+ if (comment && commentsIn(source, gap, comment).length > 0) return true;
5282
+ return false;
5283
+ }
5284
+ function collectLeafSlots(root) {
5285
+ const slots = [];
5286
+ const visit = (n) => {
5287
+ const kids = n.children;
5288
+ if (kids && kids.length) {
5289
+ for (const c of kids) visit(c);
5290
+ } else if (n.span) {
5291
+ slots.push({ start: n.span.start, end: n.span.end });
5292
+ }
5293
+ };
5294
+ if (root) visit(root);
5295
+ return slots;
5296
+ }
5297
+ function buildOffsetIndex(root, input = "") {
5298
+ return new OffsetIndex(collectLeafSlots(root), 0, input.length);
5299
+ }
5300
+
5301
+ // src/cst/relative-spans.ts
5302
+ function relativize(node2, parentBase = 0) {
5303
+ return {
5304
+ start: node2.start - parentBase,
5305
+ end: node2.end - parentBase,
5306
+ children: node2.children.map((c) => relativize(c, node2.start))
5307
+ };
5308
+ }
5309
+ function absolutize(node2, parentBase = 0) {
5310
+ const start = parentBase + node2.start;
5311
+ return {
5312
+ start,
5313
+ end: parentBase + node2.end,
5314
+ children: node2.children.map((c) => absolutize(c, start))
5315
+ };
5316
+ }
5317
+ function absoluteSpanAt(root, path) {
5318
+ let base = 0;
5319
+ let node2 = root;
5320
+ for (const idx of path) {
5321
+ base += node2.start;
5322
+ const child = node2.children[idx];
5323
+ if (!child) throw new RangeError(`absoluteSpanAt: no child ${idx} on path ${path.join(".")}`);
5324
+ node2 = child;
5325
+ }
5326
+ return { start: base + node2.start, end: base + node2.end };
5327
+ }
5328
+ function shiftAbsolute(node2, at, delta) {
5329
+ const s = node2.start >= at ? node2.start + delta : node2.start;
5330
+ const e = node2.end >= at ? node2.end + delta : node2.end;
5331
+ return {
5332
+ start: s,
5333
+ end: e,
5334
+ children: node2.children.map((c) => shiftAbsolute(c, at, delta))
5335
+ };
5336
+ }
5337
+ function applyEdit(root, at, delta, stats) {
5338
+ return editNode(root, at, delta, 0, 0, stats);
5339
+ }
5340
+ function editNode(node2, at, delta, parentBase, parentShift, stats) {
5341
+ const absStart = parentBase + node2.start;
5342
+ const absEnd = parentBase + node2.end;
5343
+ if (absEnd < at) return node2;
5344
+ const startShifts = absStart >= at;
5345
+ if (startShifts && parentShift === delta) return node2;
5346
+ if (stats) stats.allocated++;
5347
+ const newAbsStart = startShifts ? absStart + delta : absStart;
5348
+ const newAbsEnd = absEnd + delta;
5349
+ const newParentBase = parentBase + parentShift;
5350
+ const newStart = newAbsStart - newParentBase;
5351
+ const newEnd = newAbsEnd - newParentBase;
5352
+ const ownShift = newAbsStart - absStart;
5353
+ let changed = false;
5354
+ const newChildren = [];
5355
+ for (const c of node2.children) {
5356
+ const nc = editNode(c, at, delta, absStart, ownShift, stats);
5357
+ if (nc !== c) changed = true;
5358
+ newChildren.push(nc);
5359
+ }
5360
+ return {
5361
+ start: newStart,
5362
+ end: newEnd,
5363
+ children: changed ? newChildren : node2.children
5364
+ };
5365
+ }
5366
+
5367
+ // src/index.ts
5368
+ registerRegexAnalyzer(firstSetFromRegex);
3924
5369
  export {
5370
+ OffsetIndex,
5371
+ absoluteSpanAt,
5372
+ absolutize,
3925
5373
  annotateSpan,
5374
+ applyEdit,
3926
5375
  balanced,
5376
+ blankLinesIn,
3927
5377
  buildLineIndex,
5378
+ buildOffsetIndex,
3928
5379
  buildTriviaIndex,
3929
5380
  choice,
5381
+ collectLeafSlots,
5382
+ commentsIn,
3930
5383
  compile,
3931
5384
  completionsAt,
5385
+ compose,
3932
5386
  createVisitor,
5387
+ cstBuildHost,
3933
5388
  expect,
5389
+ gapIsSignificant,
5390
+ gapText,
3934
5391
  guard,
5392
+ indentMixed,
5393
+ indentWidth,
3935
5394
  isParseError,
3936
5395
  keywords,
3937
5396
  label,
5397
+ lineBreaksIn,
5398
+ lineStartWithin,
3938
5399
  literal,
3939
- makeFunctionalDoc,
3940
5400
  makeWord,
3941
5401
  many,
3942
5402
  manyRecover,
@@ -3947,15 +5407,20 @@ export {
3947
5407
  oneOrMore,
3948
5408
  optional,
3949
5409
  parse,
5410
+ parseDoc,
3950
5411
  parser,
5412
+ pick,
3951
5413
  recover,
3952
5414
  ref,
3953
5415
  regex,
5416
+ relativize,
3954
5417
  rules,
5418
+ run,
3955
5419
  scanTo,
3956
5420
  sepBy,
3957
5421
  sepByRecover,
3958
5422
  sequence,
5423
+ shiftAbsolute,
3959
5424
  skip,
3960
5425
  staticExpected,
3961
5426
  transform,