parseman 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,8 +34,10 @@ __export(plugin_exports, {
34
34
  transformMacro: () => transformMacro
35
35
  });
36
36
  module.exports = __toCommonJS(plugin_exports);
37
+ var fs = __toESM(require("node:fs"), 1);
37
38
  var import_unplugin = require("unplugin");
38
39
  var import_oxc_parser = require("oxc-parser");
40
+ var import_oxc_resolver = require("oxc-resolver");
39
41
  var import_magic_string = __toESM(require("magic-string"), 1);
40
42
 
41
43
  // src/combinators/first-set.ts
@@ -363,15 +365,15 @@ function extractFirstSet(node2) {
363
365
  case "Alternative": {
364
366
  const exprs = node2.expressions;
365
367
  if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
366
- let fs = empty();
368
+ let fs2 = empty();
367
369
  let canNL = false;
368
370
  for (const expr of exprs) {
369
371
  const r = extractFirstSet(expr);
370
- fs = union(fs, r.firstSet);
372
+ fs2 = union(fs2, r.firstSet);
371
373
  canNL = canNL || r.canMatchNewline;
372
374
  if (!canBeEmpty(expr)) break;
373
375
  }
374
- return { firstSet: fs, canMatchNewline: canNL };
376
+ return { firstSet: fs2, canMatchNewline: canNL };
375
377
  }
376
378
  case "Char": {
377
379
  const kind = node2.kind;
@@ -398,22 +400,22 @@ function extractFirstSet(node2) {
398
400
  }
399
401
  case "CharacterClass": {
400
402
  const expressions = node2.expressions;
401
- let fs = empty();
403
+ let fs2 = empty();
402
404
  let canNL = false;
403
405
  for (const expr of expressions) {
404
406
  if (expr.type === "ClassRange") {
405
407
  const from = expr.from.codePoint;
406
408
  const to = expr.to.codePoint;
407
- fs = union(fs, fromRange(from, to));
409
+ fs2 = union(fs2, fromRange(from, to));
408
410
  if (from <= 10 && 10 <= to) canNL = true;
409
411
  } else if (expr.type === "Char") {
410
412
  const r = extractFirstSet(expr);
411
- fs = union(fs, r.firstSet);
413
+ fs2 = union(fs2, r.firstSet);
412
414
  canNL = canNL || r.canMatchNewline;
413
415
  }
414
416
  }
415
417
  if (node2.negative) return { firstSet: any(), canMatchNewline: true };
416
- return { firstSet: fs, canMatchNewline: canNL };
418
+ return { firstSet: fs2, canMatchNewline: canNL };
417
419
  }
418
420
  case "Repetition": {
419
421
  return extractFirstSet(node2.expression);
@@ -722,10 +724,10 @@ function codesToFirstSet(codes) {
722
724
  ranges.push({ lo, hi });
723
725
  return { kind: "ranges", ranges };
724
726
  }
725
- function inFirstSet(code, fs) {
726
- if (fs.kind === "any") return true;
727
- if (fs.kind === "empty") return false;
728
- for (const r of fs.ranges) if (code >= r.lo && code <= r.hi) return true;
727
+ function inFirstSet(code, fs2) {
728
+ if (fs2.kind === "any") return true;
729
+ if (fs2.kind === "empty") return false;
730
+ for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
729
731
  return false;
730
732
  }
731
733
  function areDisjoint(sets) {
@@ -813,12 +815,15 @@ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
813
815
  continue;
814
816
  }
815
817
  if (c === 47 && input.charCodeAt(pos + 1) === 42) {
816
- const start = pos;
817
818
  let j = pos + 2;
818
819
  while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
819
- pos = j + 2 <= input.length ? j + 2 : input.length;
820
- chunks.push({ start, end: pos, kindIndex: commentKind });
821
- continue;
820
+ if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
821
+ const start = pos;
822
+ pos = j + 2;
823
+ chunks.push({ start, end: pos, kindIndex: commentKind });
824
+ continue;
825
+ }
826
+ break;
822
827
  }
823
828
  break;
824
829
  }
@@ -1315,38 +1320,140 @@ function noTrivia(root) {
1315
1320
  return parser({ trivia: null }, root);
1316
1321
  }
1317
1322
 
1323
+ // src/combinators/expect.ts
1324
+ function staticExpected(c) {
1325
+ const def = c._def;
1326
+ switch (def.tag) {
1327
+ case "literal":
1328
+ return [JSON.stringify(def.value)];
1329
+ case "regex":
1330
+ return [`/${def.source}/`];
1331
+ case "keywords":
1332
+ return def.words.map((w) => JSON.stringify(w));
1333
+ case "label":
1334
+ return [def.label];
1335
+ case "choice":
1336
+ return def.parsers.flatMap(staticExpected);
1337
+ case "sequence":
1338
+ return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
1339
+ case "node":
1340
+ case "grammar":
1341
+ case "trivia":
1342
+ case "optional":
1343
+ case "many":
1344
+ case "oneOrMore":
1345
+ case "transform":
1346
+ case "not":
1347
+ return staticExpected(def.parser);
1348
+ case "lazy":
1349
+ return staticExpected(def.thunk());
1350
+ default:
1351
+ return [];
1352
+ }
1353
+ }
1354
+ function expect(combinator, label2) {
1355
+ const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
1356
+ const meta = {
1357
+ firstSet: combinator._meta.firstSet,
1358
+ canMatchNewline: combinator._meta.canMatchNewline,
1359
+ isTrivia: false
1360
+ };
1361
+ return {
1362
+ _tag: "expect",
1363
+ _meta: meta,
1364
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1365
+ parse(input, pos, ctx) {
1366
+ const result = combinator.parse(input, pos, ctx);
1367
+ if (result.ok) return result;
1368
+ const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1369
+ ctx._errors?.push(error);
1370
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1371
+ }
1372
+ };
1373
+ }
1374
+
1318
1375
  // src/compiler/scannable-run.ts
1376
+ var BACKSLASH = 92;
1319
1377
  var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1320
1378
  var META = new Set("()[]{}*+?|^$.".split(""));
1379
+ var SPACE_RANGES = [
1380
+ [9, 13],
1381
+ [32, 32],
1382
+ [160, 160],
1383
+ [5760, 5760],
1384
+ [8192, 8202],
1385
+ [8232, 8232],
1386
+ [8233, 8233],
1387
+ [8239, 8239],
1388
+ [8287, 8287],
1389
+ [12288, 12288],
1390
+ [65279, 65279]
1391
+ ];
1392
+ function shorthandRanges(ch) {
1393
+ if (ch === "d") return [[48, 57]];
1394
+ if (ch === "s") return SPACE_RANGES;
1395
+ return [[48, 57], [65, 90], [97, 122], [95, 95]];
1396
+ }
1397
+ function readUnicodeEscape(body, i) {
1398
+ if (body[i] !== "\\" || body[i + 1] !== "u") return null;
1399
+ const hex = body.slice(i + 2, i + 6);
1400
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
1401
+ return { cp: Number.parseInt(hex, 16), next: i + 6 };
1402
+ }
1321
1403
  function parseClassRanges(body) {
1322
1404
  const ranges = [];
1323
1405
  let i = 0;
1324
- const readCp = () => {
1406
+ const readAtom = () => {
1325
1407
  const ch = body[i];
1326
1408
  if (ch === void 0) return null;
1327
1409
  if (ch === "\\") {
1410
+ const uni = readUnicodeEscape(body, i);
1411
+ if (uni) {
1412
+ i = uni.next;
1413
+ return { cp: uni.cp };
1414
+ }
1328
1415
  const e = body[i + 1];
1329
1416
  if (e === void 0) return null;
1330
1417
  i += 2;
1331
- return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
1418
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1419
+ if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
1420
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1421
+ return { cp: e.codePointAt(0) };
1332
1422
  }
1333
1423
  i += ch.length;
1334
- return ch.codePointAt(0);
1424
+ return { cp: ch.codePointAt(0) };
1335
1425
  };
1336
1426
  while (i < body.length) {
1337
- const lo = readCp();
1427
+ const lo = readAtom();
1338
1428
  if (lo === null) return null;
1429
+ if ("set" in lo) {
1430
+ ranges.push(...lo.set);
1431
+ continue;
1432
+ }
1339
1433
  if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1340
1434
  i += 1;
1341
- const hi = readCp();
1342
- if (hi === null) return null;
1343
- ranges.push([lo, hi]);
1435
+ const hi = readAtom();
1436
+ if (hi === null || "set" in hi) return null;
1437
+ ranges.push([lo.cp, hi.cp]);
1344
1438
  } else {
1345
- ranges.push([lo, lo]);
1439
+ ranges.push([lo.cp, lo.cp]);
1346
1440
  }
1347
1441
  }
1348
1442
  return ranges.length ? ranges : null;
1349
1443
  }
1444
+ function classToRanges(cls) {
1445
+ if (cls === "\\d") return shorthandRanges("d");
1446
+ if (cls === "\\w") return shorthandRanges("w");
1447
+ if (cls === "\\s") return shorthandRanges("s");
1448
+ const body = cls.slice(1, -1);
1449
+ if (body.startsWith("^")) return null;
1450
+ return parseClassRanges(body);
1451
+ }
1452
+ var foldEq = (cVar, cp) => {
1453
+ if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
1454
+ if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
1455
+ return `${cVar} === ${cp}`;
1456
+ };
1350
1457
  function literalCodePoints(frag) {
1351
1458
  const out = [];
1352
1459
  let i = 0;
@@ -1365,19 +1472,482 @@ function literalCodePoints(frag) {
1365
1472
  }
1366
1473
  return out.length ? out : null;
1367
1474
  }
1475
+ function parseStringShape(source) {
1476
+ const q = source[0];
1477
+ if (q === void 0 || q === "\\" || META.has(q)) return null;
1478
+ if (source.length < 3 || source[source.length - 1] !== q) return null;
1479
+ const inner = source.slice(1, -1);
1480
+ const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
1481
+ const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
1482
+ let body;
1483
+ let esc;
1484
+ let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
1485
+ if (m) {
1486
+ body = m[1];
1487
+ esc = m[2];
1488
+ } else {
1489
+ m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
1490
+ if (m) {
1491
+ esc = m[1];
1492
+ body = m[2];
1493
+ }
1494
+ }
1495
+ if (body === void 0 || esc === void 0) return null;
1496
+ const excluded = parseClassRanges(body);
1497
+ if (!excluded) return null;
1498
+ const qcp = q.codePointAt(0);
1499
+ const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
1500
+ if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
1501
+ return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
1502
+ }
1503
+ function parseSeqParts(source) {
1504
+ const parts = [];
1505
+ let lit = [];
1506
+ const flush = () => {
1507
+ if (lit.length) {
1508
+ parts.push({ part: "lit", cps: lit, optional: false });
1509
+ lit = [];
1510
+ }
1511
+ };
1512
+ const runFrom = (ranges, negated, q) => {
1513
+ if (q === "+") {
1514
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
1515
+ return 1;
1516
+ }
1517
+ if (q === "*") {
1518
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
1519
+ return 1;
1520
+ }
1521
+ if (q === "?") {
1522
+ parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
1523
+ return 1;
1524
+ }
1525
+ parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
1526
+ return 0;
1527
+ };
1528
+ let i = 0;
1529
+ while (i < source.length) {
1530
+ const ch = source[i];
1531
+ if (ch === "[") {
1532
+ let k = i + 1;
1533
+ const neg = source[k] === "^";
1534
+ if (neg) k++;
1535
+ let body = "";
1536
+ while (k < source.length && source[k] !== "]") {
1537
+ if (source[k] === "\\") {
1538
+ body += source[k] + (source[k + 1] ?? "");
1539
+ k += 2;
1540
+ } else {
1541
+ body += source[k];
1542
+ k++;
1543
+ }
1544
+ }
1545
+ if (source[k] !== "]") return null;
1546
+ const ranges = parseClassRanges(body);
1547
+ if (!ranges) return null;
1548
+ flush();
1549
+ i = k + 1;
1550
+ i += runFrom(ranges, neg, source[i]);
1551
+ continue;
1552
+ }
1553
+ if (ch === "\\") {
1554
+ const e = source[i + 1];
1555
+ if (e === "d" || e === "w" || e === "s") {
1556
+ flush();
1557
+ i += 2;
1558
+ i += runFrom(shorthandRanges(e), false, source[i]);
1559
+ continue;
1560
+ }
1561
+ const uni = readUnicodeEscape(source, i);
1562
+ let cp2;
1563
+ if (uni) {
1564
+ cp2 = uni.cp;
1565
+ i = uni.next;
1566
+ } else if (e === void 0) return null;
1567
+ else if (e in CLASS_ESCAPES) {
1568
+ cp2 = CLASS_ESCAPES[e];
1569
+ i += 2;
1570
+ } else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1571
+ else {
1572
+ cp2 = e.codePointAt(0);
1573
+ i += 2;
1574
+ }
1575
+ if (source[i] === "?") {
1576
+ flush();
1577
+ parts.push({ part: "lit", cps: [cp2], optional: true });
1578
+ i++;
1579
+ } else lit.push(cp2);
1580
+ continue;
1581
+ }
1582
+ if (ch === "(") {
1583
+ if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
1584
+ let k = i + 3;
1585
+ let depth = 1;
1586
+ while (k < source.length && depth > 0) {
1587
+ const c2 = source[k];
1588
+ if (c2 === "\\") {
1589
+ k += 2;
1590
+ continue;
1591
+ }
1592
+ if (c2 === "[") {
1593
+ let j = k + 1;
1594
+ if (source[j] === "^") j++;
1595
+ while (j < source.length && source[j] !== "]") {
1596
+ if (source[j] === "\\") j += 2;
1597
+ else j++;
1598
+ }
1599
+ if (source[j] !== "]") return null;
1600
+ k = j + 1;
1601
+ continue;
1602
+ }
1603
+ if (c2 === "(") depth++;
1604
+ else if (c2 === ")") depth--;
1605
+ k++;
1606
+ }
1607
+ if (depth !== 0) return null;
1608
+ const body = source.slice(i + 3, k - 1);
1609
+ const inner = parseScanShape(body);
1610
+ if (!inner || !groupInnerSafe(inner)) return null;
1611
+ const q = source[k];
1612
+ if (q === "{") return null;
1613
+ flush();
1614
+ if (q === "+") {
1615
+ if (shapeCanBeEmpty(inner)) return null;
1616
+ parts.push({ part: "group", inner, min: 1, unbounded: true });
1617
+ i = k + 1;
1618
+ } else if (q === "*") {
1619
+ if (shapeCanBeEmpty(inner)) return null;
1620
+ parts.push({ part: "group", inner, min: 0, unbounded: true });
1621
+ i = k + 1;
1622
+ } else if (q === "?") {
1623
+ parts.push({ part: "group", inner, min: 0, unbounded: false });
1624
+ i = k + 1;
1625
+ } else {
1626
+ parts.push({ part: "group", inner, min: 1, unbounded: false });
1627
+ i = k;
1628
+ }
1629
+ continue;
1630
+ }
1631
+ if (META.has(ch)) return null;
1632
+ const cp = ch.codePointAt(0);
1633
+ i += ch.length;
1634
+ if (source[i] === "?") {
1635
+ flush();
1636
+ parts.push({ part: "lit", cps: [cp], optional: true });
1637
+ i++;
1638
+ } else lit.push(cp);
1639
+ }
1640
+ flush();
1641
+ return parts.length ? parts : null;
1642
+ }
1643
+ function seqIsUnambiguous(parts) {
1644
+ let hasMandatory = false;
1645
+ for (let k = 0; k < parts.length; k++) {
1646
+ const p = parts[k];
1647
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1648
+ if (mandatory) hasMandatory = true;
1649
+ const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
1650
+ const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
1651
+ if (skippableSingle || isUnboundedRepeat) {
1652
+ const rest = seqFirstAccept(parts.slice(k + 1));
1653
+ if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
1654
+ }
1655
+ }
1656
+ return hasMandatory;
1657
+ }
1658
+ function rangesOverlap(a, b) {
1659
+ return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
1660
+ }
1661
+ function rangesSubset(sub, sup) {
1662
+ return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
1663
+ }
1664
+ function parseClassOperand(body) {
1665
+ if (body === "\\d" || body === "\\w" || body === "\\s") {
1666
+ return { ranges: shorthandRanges(body[1]), negated: false };
1667
+ }
1668
+ if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
1669
+ let inner = body.slice(1, -1);
1670
+ const negated = inner.startsWith("^");
1671
+ if (negated) inner = inner.slice(1);
1672
+ const ranges = parseClassRanges(inner);
1673
+ return ranges ? { ranges, negated } : null;
1674
+ }
1675
+ const cps = literalCodePoints(body);
1676
+ if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
1677
+ return null;
1678
+ }
1679
+ function classSubset(t, u) {
1680
+ if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
1681
+ if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
1682
+ if (t.negated && !u.negated) return false;
1683
+ return rangesSubset(u.ranges, t.ranges);
1684
+ }
1685
+ function classDisjoint(t, u) {
1686
+ if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
1687
+ if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
1688
+ if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
1689
+ return false;
1690
+ }
1691
+ function firstSetsDisjoint(a, b) {
1692
+ if (a === "any" || b === "any") return false;
1693
+ return classDisjoint(a, b);
1694
+ }
1695
+ function trailingBacktrackClass(shape) {
1696
+ if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
1697
+ if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
1698
+ if (shape.kind === "litFold") return null;
1699
+ if (shape.kind === "seq") {
1700
+ const last = shape.parts[shape.parts.length - 1];
1701
+ if (last.part === "lit") return null;
1702
+ if (last.part === "run") {
1703
+ if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
1704
+ return null;
1705
+ }
1706
+ const innerWiggle = trailingBacktrackClass(last.inner);
1707
+ if (innerWiggle === "unsupported") return "unsupported";
1708
+ const requiredOnce = !last.unbounded && last.min === 1;
1709
+ if (requiredOnce) return innerWiggle;
1710
+ const dropExposed = shapeFirstAccept(last.inner);
1711
+ if (dropExposed === "any") return "unsupported";
1712
+ if (innerWiggle === null) return dropExposed;
1713
+ const unioned = unionCharSets([innerWiggle, dropExposed]);
1714
+ return unioned === "any" ? "unsupported" : unioned;
1715
+ }
1716
+ if (shape.kind === "alt") {
1717
+ if (!shape.disjoint) return "unsupported";
1718
+ const classes = shape.arms.map(trailingBacktrackClass);
1719
+ if (classes.some((c) => c === "unsupported")) return "unsupported";
1720
+ const concrete = classes.filter((c) => c !== null);
1721
+ if (concrete.length === 0) return null;
1722
+ const unioned = unionCharSets(concrete);
1723
+ return unioned === "any" ? "unsupported" : unioned;
1724
+ }
1725
+ return "unsupported";
1726
+ }
1727
+ function lookaheadUnambiguous(inner, operand, negative) {
1728
+ const tail = trailingBacktrackClass(inner);
1729
+ if (tail === "unsupported") return false;
1730
+ if (tail === null) return true;
1731
+ return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
1732
+ }
1733
+ function stripTrailingLookahead(source) {
1734
+ if (!source.endsWith(")")) return null;
1735
+ for (const negative of [true, false]) {
1736
+ const lead = negative ? "(?!" : "(?=";
1737
+ const idx = source.lastIndexOf(lead);
1738
+ if (idx === -1) continue;
1739
+ const body = source.slice(idx + lead.length, -1);
1740
+ const operand = body.length ? parseClassOperand(body) : null;
1741
+ if (!operand) continue;
1742
+ return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
1743
+ }
1744
+ return null;
1745
+ }
1746
+ function unwrapOuterGroup(source) {
1747
+ while (source.startsWith("(?:")) {
1748
+ let depth = 1;
1749
+ let i = 3;
1750
+ let ok = true;
1751
+ while (i < source.length && depth > 0) {
1752
+ const ch = source[i];
1753
+ if (ch === "\\") {
1754
+ i += 2;
1755
+ continue;
1756
+ }
1757
+ if (ch === "[") {
1758
+ let j = i + 1;
1759
+ if (source[j] === "^") j++;
1760
+ while (j < source.length && source[j] !== "]") {
1761
+ if (source[j] === "\\") j += 2;
1762
+ else j++;
1763
+ }
1764
+ if (source[j] !== "]") {
1765
+ ok = false;
1766
+ break;
1767
+ }
1768
+ i = j + 1;
1769
+ continue;
1770
+ }
1771
+ if (ch === "(") depth++;
1772
+ else if (ch === ")") depth--;
1773
+ i++;
1774
+ }
1775
+ if (!ok || depth !== 0 || i !== source.length) break;
1776
+ source = source.slice(3, -1);
1777
+ }
1778
+ return source;
1779
+ }
1780
+ function splitTopLevelAlternation(source) {
1781
+ const body = unwrapOuterGroup(source);
1782
+ const parts = [];
1783
+ let depth = 0;
1784
+ let last = 0;
1785
+ let i = 0;
1786
+ while (i < body.length) {
1787
+ const ch = body[i];
1788
+ if (ch === "\\") {
1789
+ i += 2;
1790
+ continue;
1791
+ }
1792
+ if (ch === "[") {
1793
+ let j = i + 1;
1794
+ if (body[j] === "^") j++;
1795
+ while (j < body.length && body[j] !== "]") {
1796
+ if (body[j] === "\\") j += 2;
1797
+ else j++;
1798
+ }
1799
+ if (body[j] !== "]") return null;
1800
+ i = j + 1;
1801
+ continue;
1802
+ }
1803
+ if (ch === "(") {
1804
+ depth++;
1805
+ i++;
1806
+ continue;
1807
+ }
1808
+ if (ch === ")") {
1809
+ depth--;
1810
+ if (depth < 0) return null;
1811
+ i++;
1812
+ continue;
1813
+ }
1814
+ if (ch === "|" && depth === 0) {
1815
+ parts.push(body.slice(last, i));
1816
+ last = i + 1;
1817
+ i++;
1818
+ continue;
1819
+ }
1820
+ i++;
1821
+ }
1822
+ if (depth !== 0) return null;
1823
+ parts.push(body.slice(last));
1824
+ if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
1825
+ return parts;
1826
+ }
1827
+ function shapeCanBeEmpty(shape) {
1828
+ if (shape.kind === "chars") return !shape.minOne;
1829
+ if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
1830
+ if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
1831
+ return false;
1832
+ }
1833
+ function unionCharSets(sets) {
1834
+ if (sets.some((s) => s === "any")) return "any";
1835
+ const concrete = sets;
1836
+ if (concrete.length === 1) return concrete[0];
1837
+ if (concrete.some((s) => s.negated)) return "any";
1838
+ return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
1839
+ }
1840
+ function partFirstAccept(part) {
1841
+ if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
1842
+ if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
1843
+ return shapeFirstAccept(part.inner);
1844
+ }
1845
+ function seqFirstAccept(parts) {
1846
+ const sets = [];
1847
+ for (const p of parts) {
1848
+ const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
1849
+ sets.push(partFirstAccept(p));
1850
+ if (mandatory) break;
1851
+ }
1852
+ return unionCharSets(sets);
1853
+ }
1854
+ function groupInnerSafe(shape) {
1855
+ if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
1856
+ if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
1857
+ if (shape.kind === "alt") return shape.disjoint;
1858
+ return false;
1859
+ }
1860
+ function shapeFirstAccept(shape) {
1861
+ if (shapeCanBeEmpty(shape)) return "any";
1862
+ switch (shape.kind) {
1863
+ case "chars":
1864
+ return { ranges: shape.ranges, negated: false };
1865
+ case "ident":
1866
+ return { ranges: shape.head, negated: false };
1867
+ case "until":
1868
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1869
+ case "delimited":
1870
+ return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
1871
+ case "string":
1872
+ return { ranges: [[shape.quote, shape.quote]], negated: false };
1873
+ case "litFold": {
1874
+ const cp = shape.open[0];
1875
+ if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
1876
+ if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
1877
+ return { ranges: [[cp, cp]], negated: false };
1878
+ }
1879
+ case "seq":
1880
+ return seqFirstAccept(shape.parts);
1881
+ case "lookahead":
1882
+ return shapeFirstAccept(shape.inner);
1883
+ // zero-width — doesn't change first-char acceptance
1884
+ case "alt": {
1885
+ if (!shape.disjoint) return "any";
1886
+ const sets = shape.firsts.filter((f) => f !== null);
1887
+ return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
1888
+ }
1889
+ }
1890
+ }
1891
+ function allPairsDisjoint(sets) {
1892
+ for (let i = 0; i < sets.length; i++) {
1893
+ const a = sets[i];
1894
+ if (a === "any") return false;
1895
+ for (let j = i + 1; j < sets.length; j++) {
1896
+ const b = sets[j];
1897
+ if (b === "any" || !classDisjoint(a, b)) return false;
1898
+ }
1899
+ }
1900
+ return true;
1901
+ }
1902
+ function parseAlternation(source) {
1903
+ const parts = splitTopLevelAlternation(source);
1904
+ if (!parts) return null;
1905
+ const arms = [];
1906
+ for (const p of parts) {
1907
+ const arm = parseScanShape(p);
1908
+ if (!arm) return null;
1909
+ arms.push(arm);
1910
+ }
1911
+ const firstSets = arms.map(shapeFirstAccept);
1912
+ const disjoint = allPairsDisjoint(firstSets);
1913
+ return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
1914
+ }
1368
1915
  function parseScanShape(source) {
1916
+ const alt = parseAlternation(source);
1917
+ if (alt) return alt;
1918
+ const la = stripTrailingLookahead(source);
1919
+ if (la) {
1920
+ const inner = parseScanShapeCore(la.base);
1921
+ if (!inner) return null;
1922
+ const operand = { ranges: la.ranges, negated: la.classNegated };
1923
+ if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
1924
+ return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
1925
+ }
1926
+ return parseScanShapeCore(source);
1927
+ }
1928
+ function parseScanShapeCore(source) {
1369
1929
  let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1370
- if (m) {
1371
- if (m[1].startsWith("^")) return null;
1930
+ if (m && !m[1].startsWith("^")) {
1372
1931
  const ranges = parseClassRanges(m[1]);
1373
- return ranges ? { kind: "chars", ranges } : null;
1932
+ return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
1933
+ }
1934
+ m = /^\\([dws])([+*])$/.exec(source);
1935
+ if (m) {
1936
+ return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
1374
1937
  }
1938
+ m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
1939
+ if (m) {
1940
+ const head = classToRanges(m[1]);
1941
+ const tail = classToRanges(m[2]);
1942
+ if (head && tail) return { kind: "ident", head, tail };
1943
+ }
1944
+ const str = parseStringShape(source);
1945
+ if (str) return str;
1375
1946
  m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
1376
1947
  if (m) {
1377
1948
  const open = literalCodePoints(m[1]);
1378
1949
  const stop = parseClassRanges(m[2]);
1379
1950
  if (open && stop) return { kind: "until", open, stop };
1380
- return null;
1381
1951
  }
1382
1952
  if (!source.includes("\\\\")) {
1383
1953
  m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
@@ -1387,68 +1957,278 @@ function parseScanShape(source) {
1387
1957
  if (open && close) return { kind: "delimited", open, close };
1388
1958
  }
1389
1959
  }
1960
+ const parts = parseSeqParts(source);
1961
+ if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
1390
1962
  return null;
1391
1963
  }
1964
+ function scanShapeFromRegex(source, flags) {
1965
+ if (/[msu]/.test(flags)) return null;
1966
+ if (/i/.test(flags)) {
1967
+ const open = literalCodePoints(source);
1968
+ return open ? { kind: "litFold", open } : null;
1969
+ }
1970
+ return parseScanShape(source);
1971
+ }
1392
1972
  var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
1393
1973
  var litCond = (base, cps, firstVar) => cps.map(
1394
1974
  (cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
1395
1975
  ).join(" && ");
1396
- function advanceExpr(shape) {
1976
+ var LINE_TERMINATORS = [10, 13, 8232, 8233];
1977
+ function stringHardStop(shape) {
1978
+ return shape.excluded.filter(
1979
+ ([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
1980
+ );
1981
+ }
1982
+ var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
1983
+ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1984
+ if (shape.kind === "lookahead") {
1985
+ const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
1986
+ const okV = mint("_ok");
1987
+ const endV2 = mint("_end");
1988
+ const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
1989
+ const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
1990
+ const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
1991
+ const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
1992
+ return {
1993
+ setup: [
1994
+ ...inner.setup,
1995
+ `${ind2}let ${okV} = ${inner.ok}`,
1996
+ `${ind2}let ${endV2} = ${inner.end}`,
1997
+ `${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
1998
+ ],
1999
+ ok: okV,
2000
+ end: endV2
2001
+ };
2002
+ }
2003
+ if (shape.kind === "alt") {
2004
+ const okV = mint("_ok");
2005
+ const endV2 = mint("_end");
2006
+ const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
2007
+ if (shape.disjoint) {
2008
+ const c0 = codeAt(start, firstChar);
2009
+ shape.arms.forEach((arm, k) => {
2010
+ const fs2 = shape.firsts[k];
2011
+ const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
2012
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2013
+ lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
2014
+ lines.push(...m.setup);
2015
+ lines.push(`${ind2} ${okV} = ${m.ok}`);
2016
+ lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
2017
+ lines.push(`${ind2}}`);
2018
+ });
2019
+ } else {
2020
+ const lbl = mint("_alt");
2021
+ lines.push(`${ind2}${lbl}: {`);
2022
+ for (const arm of shape.arms) {
2023
+ const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
2024
+ lines.push(...m.setup);
2025
+ lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
2026
+ }
2027
+ lines.push(`${ind2}}`);
2028
+ }
2029
+ return { setup: lines, ok: okV, end: endV2 };
2030
+ }
1397
2031
  if (shape.kind === "chars") {
2032
+ const cur = mint("_e");
1398
2033
  return {
1399
2034
  setup: [
1400
- ` _e++`,
1401
- ` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
2035
+ `${ind2}let ${cur} = ${start}`,
2036
+ `${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
1402
2037
  ],
1403
- endVar: "_e"
2038
+ // `*` always matches (possibly empty); `+` needs at least one char.
2039
+ ok: shape.minOne ? `${cur} > ${start}` : "true",
2040
+ end: cur
2041
+ };
2042
+ }
2043
+ if (shape.kind === "ident") {
2044
+ const cur = mint("_e");
2045
+ return {
2046
+ setup: [
2047
+ `${ind2}let ${cur} = ${start}`,
2048
+ `${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
2049
+ `${ind2} ${cur} = ${start} + 1`,
2050
+ `${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
2051
+ `${ind2}}`
2052
+ ],
2053
+ ok: `${cur} > ${start}`,
2054
+ end: cur
2055
+ };
2056
+ }
2057
+ if (shape.kind === "seq") {
2058
+ const endV2 = mint("_e");
2059
+ const okV = mint("_ok");
2060
+ const runCond = (p, at) => {
2061
+ const c = classCond(`input.charCodeAt(${at})`, p.ranges);
2062
+ return p.negated ? `!(${c})` : `(${c})`;
2063
+ };
2064
+ const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
2065
+ let first = true;
2066
+ for (const p of shape.parts) {
2067
+ if (p.part === "lit") {
2068
+ const L = p.cps.length;
2069
+ const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
2070
+ if (p.optional) {
2071
+ lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
2072
+ } else {
2073
+ lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
2074
+ lines.push(`${ind2} ${endV2} += ${L}`);
2075
+ }
2076
+ } else if (p.part === "run" && !p.unbounded) {
2077
+ if (p.min === 1) {
2078
+ lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
2079
+ lines.push(`${ind2} ${endV2}++`);
2080
+ } else {
2081
+ lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2082
+ }
2083
+ } else if (p.part === "run") {
2084
+ const s = mint("_s");
2085
+ lines.push(`${ind2} const ${s} = ${endV2}`);
2086
+ lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
2087
+ if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
2088
+ } else {
2089
+ const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
2090
+ if (!p.unbounded) {
2091
+ lines.push(...inner.setup);
2092
+ if (p.min === 1) {
2093
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2094
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2095
+ } else {
2096
+ lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
2097
+ }
2098
+ } else {
2099
+ const progV = mint("_prog");
2100
+ lines.push(`${ind2} let ${progV} = false`);
2101
+ lines.push(`${ind2} while (true) {`);
2102
+ lines.push(...inner.setup);
2103
+ lines.push(`${ind2} if (!(${inner.ok})) break`);
2104
+ lines.push(`${ind2} ${endV2} = ${inner.end}`);
2105
+ lines.push(`${ind2} ${progV} = true`);
2106
+ lines.push(`${ind2} }`);
2107
+ if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
2108
+ }
2109
+ }
2110
+ first = false;
2111
+ }
2112
+ lines.push(`${ind2} ${okV} = true`);
2113
+ lines.push(`${ind2}} while (false)`);
2114
+ lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
2115
+ return { setup: lines, ok: okV, end: endV2 };
2116
+ }
2117
+ if (shape.kind === "litFold") {
2118
+ const openLen2 = shape.open.length;
2119
+ const endV2 = mint("_end");
2120
+ const checks = shape.open.map((cp, k) => {
2121
+ const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
2122
+ return foldEq(cVar, cp);
2123
+ });
2124
+ return {
2125
+ setup: [
2126
+ `${ind2}let ${endV2} = ${start}`,
2127
+ `${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
2128
+ ],
2129
+ ok: `${endV2} > ${start}`,
2130
+ end: endV2
1404
2131
  };
1405
2132
  }
1406
2133
  if (shape.kind === "until") {
2134
+ const j2 = mint("_j");
2135
+ const openLen2 = shape.open.length;
2136
+ const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
2137
+ return {
2138
+ setup: [
2139
+ `${ind2}let ${j2} = ${start}`,
2140
+ `${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
2141
+ `${ind2} ${j2} = ${start} + ${openLen2}`,
2142
+ `${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
2143
+ `${ind2}}`
2144
+ ],
2145
+ // An open-until-terminator always completes (stop char or EOF), so a matched
2146
+ // open literal is a full match; `end === start` iff the open didn't match.
2147
+ ok: `${j2} > ${start}`,
2148
+ end: j2
2149
+ };
2150
+ }
2151
+ if (shape.kind === "string") {
2152
+ const okV = mint("_ok");
2153
+ const endV2 = mint("_end");
2154
+ const i = mint("_i");
2155
+ const c2 = mint("_c");
2156
+ const hard = stringHardStop(shape);
2157
+ const ltLines = shape.escLineTerm ? [] : (() => {
2158
+ const c3 = mint("_c");
2159
+ return [
2160
+ `${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
2161
+ `${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
2162
+ ];
2163
+ })();
1407
2164
  return {
1408
2165
  setup: [
1409
- ` let j = _e + ${shape.open.length}`,
1410
- ` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
2166
+ `${ind2}let ${okV} = false`,
2167
+ `${ind2}let ${endV2} = ${start}`,
2168
+ `${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
2169
+ `${ind2} let ${i} = ${start} + 1`,
2170
+ `${ind2} while (${i} < input.length) {`,
2171
+ `${ind2} const ${c2} = input.charCodeAt(${i})`,
2172
+ `${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
2173
+ `${ind2} if (${c2} === ${BACKSLASH}) {`,
2174
+ `${ind2} if (${i} + 1 >= input.length) break`,
2175
+ ...ltLines,
2176
+ `${ind2} ${i} += 2`,
2177
+ `${ind2} continue`,
2178
+ `${ind2} }`,
2179
+ ...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
2180
+ `${ind2} ${i}++`,
2181
+ `${ind2} }`,
2182
+ `${ind2}}`
1411
2183
  ],
1412
- endVar: "j"
2184
+ ok: okV,
2185
+ end: endV2
1413
2186
  };
1414
2187
  }
1415
- const n = shape.close.length;
2188
+ const j = mint("_j");
2189
+ const endV = mint("_end");
2190
+ const openLen = shape.open.length;
2191
+ const closeLen = shape.close.length;
2192
+ const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
2193
+ const closeChk = litCond(j, shape.close);
1416
2194
  return {
1417
2195
  setup: [
1418
- ` let j = _e + ${shape.open.length}`,
1419
- ` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
2196
+ `${ind2}let ${j} = ${start}`,
2197
+ `${ind2}let ${endV} = ${start}`,
2198
+ `${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
2199
+ `${ind2} ${j} = ${start} + ${openLen}`,
2200
+ `${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
2201
+ `${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
2202
+ `${ind2}}`
1420
2203
  ],
1421
- endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
2204
+ ok: `${endV} > ${start}`,
2205
+ end: endV
1422
2206
  };
1423
2207
  }
1424
- function guardLine(shape) {
1425
- const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
1426
- return ` if (${cond}) {`;
2208
+ function scanBranch(shape, mint) {
2209
+ const m = emitShapeMatch(shape, "_e", mint, " ", "c");
2210
+ return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
1427
2211
  }
1428
- function scanBranch(shape) {
1429
- if (shape.kind === "chars") {
1430
- return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
1431
- }
1432
- const { setup, endVar } = advanceExpr(shape);
1433
- return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
1434
- }
1435
- function captureChunk(startVar, kindIndex) {
2212
+ function scanBranchLabeled(shape, kindIndex, mint) {
2213
+ const m = emitShapeMatch(shape, "_e", mint, " ", "c");
1436
2214
  return [
2215
+ ...m.setup,
2216
+ ` if (${m.end} > _e) {`,
1437
2217
  ` if (_cap) {`,
1438
- ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${startVar}, _e, ${kindIndex})`,
1439
- ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${startVar}, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
1440
- ` }`
1441
- ];
1442
- }
1443
- function scanBranchLabeled(shape, kindIndex) {
1444
- const { setup, endVar } = advanceExpr(shape);
1445
- const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
1446
- if (endVar !== "_e") lines.push(` _e = ${endVar}`);
1447
- lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
1448
- return lines.join("\n");
2218
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
2219
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
2220
+ ` }`,
2221
+ ` _e = ${m.end}`,
2222
+ ` continue`,
2223
+ ` }`
2224
+ ].join("\n");
1449
2225
  }
1450
2226
 
1451
2227
  // src/compiler/trivia-fast-path.ts
2228
+ function makeMint() {
2229
+ let n = 0;
2230
+ return (prefix = "_v") => `${prefix}${n++}`;
2231
+ }
1452
2232
  function unwrapTrivia2(p) {
1453
2233
  let cur = p;
1454
2234
  while (cur._def.tag === "trivia") cur = cur._def.parser;
@@ -1465,14 +2245,14 @@ function choiceArms(p) {
1465
2245
  return null;
1466
2246
  }
1467
2247
  function armShape(arm) {
1468
- const src = getCoreRegexDef(arm)?.source;
1469
- return src ? parseScanShape(src) : null;
2248
+ const d = getCoreRegexDef(arm);
2249
+ return d ? scanShapeFromRegex(d.source, d.flags) : null;
1470
2250
  }
1471
2251
  function analyzeTriviaFastPath(trivia2) {
1472
2252
  const core = unwrapTrivia2(trivia2);
1473
- const directSrc = getCoreRegexDef(core)?.source;
1474
- if (directSrc) {
1475
- const shape = parseScanShape(directSrc);
2253
+ const directDef = getCoreRegexDef(core);
2254
+ if (directDef) {
2255
+ const shape = scanShapeFromRegex(directDef.source, directDef.flags);
1476
2256
  return shape && shape.kind === "chars" ? [shape] : null;
1477
2257
  }
1478
2258
  const inner = oneOrMoreInner(core);
@@ -1496,10 +2276,11 @@ var CAP_RECORD = [
1496
2276
  ` }`
1497
2277
  ].join("\n");
1498
2278
  function composeFastLoop(shapes) {
2279
+ const mint = makeMint();
1499
2280
  return [
1500
2281
  ` while (_e < input.length) {`,
1501
2282
  ` const c = input.charCodeAt(_e)`,
1502
- ...shapes.map(scanBranch),
2283
+ ...shapes.map((s) => scanBranch(s, mint)),
1503
2284
  ` break`,
1504
2285
  ` }`
1505
2286
  ].join("\n");
@@ -1519,20 +2300,21 @@ function analyzeLabeledScannableRun(trivia2) {
1519
2300
  if (!spec) return null;
1520
2301
  const out = [];
1521
2302
  for (const arm of spec.arms) {
1522
- const src = getCoreRegexDef(arm.parser)?.source;
1523
- const shape = src ? parseScanShape(src) : null;
2303
+ const d = getCoreRegexDef(arm.parser);
2304
+ const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
1524
2305
  if (!shape) return null;
1525
2306
  out.push({ shape, kindIndex: arm.kindIndex });
1526
2307
  }
1527
2308
  return out;
1528
2309
  }
1529
2310
  function buildLabeledScannableTriviaFnDecl(fnName, arms) {
2311
+ const mint = makeMint();
1530
2312
  return [
1531
2313
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
1532
2314
  ` let _e = _pos`,
1533
2315
  ` while (_e < input.length) {`,
1534
2316
  ` const c = input.charCodeAt(_e)`,
1535
- ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
2317
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
1536
2318
  ` break`,
1537
2319
  ` }`,
1538
2320
  ` return _e`,
@@ -1609,6 +2391,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
1609
2391
  ].join("\n");
1610
2392
  }
1611
2393
 
2394
+ // src/compiler/scannable-terminal.ts
2395
+ function emitScannableTerminal(shape, c) {
2396
+ const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
2397
+ const stmts = [...m.setup];
2398
+ if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
2399
+ stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
2400
+ return { stmts, endVar: m.end };
2401
+ }
2402
+
1612
2403
  // src/compiler/inline-build.ts
1613
2404
  var MK_BUILD_RE = /^\(\s*(?:\w+\s*,\s*){3}\w+\s*\)\s*=>\s*mk\s*\(\s*(['"])([^'"]+)\1\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*$/;
1614
2405
  function escapeRegExp(s) {
@@ -1730,14 +2521,45 @@ function failReturn(expected, posExpr) {
1730
2521
  function failReturnArr(expectedArr, posExpr) {
1731
2522
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
1732
2523
  }
2524
+ function hoistExpected(ctx, constArrSource) {
2525
+ let name = ctx.expectedMap.get(constArrSource);
2526
+ if (name === void 0) {
2527
+ name = `_fx${ctx.expectedDecls.length}`;
2528
+ ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
2529
+ ctx.expectedMap.set(constArrSource, name);
2530
+ }
2531
+ return name;
2532
+ }
1733
2533
  function failBody(ctx, expected, posExpr) {
1734
- if (ctx.failLabel) return `break ${ctx.failLabel}`;
2534
+ if (ctx.failLabel) {
2535
+ if (!ctx.recordFail) return `break ${ctx.failLabel}`;
2536
+ return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
2537
+ }
1735
2538
  return failReturn(expected, posExpr);
1736
2539
  }
1737
- function failArrBody(ctx, expectedArr, posExpr) {
1738
- if (ctx.failLabel) return `break ${ctx.failLabel}`;
2540
+ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
2541
+ if (ctx.failLabel) {
2542
+ if (!ctx.recordFail) return `break ${ctx.failLabel}`;
2543
+ const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
2544
+ return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
2545
+ }
2546
+ if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
1739
2547
  return failReturnArr(expectedArr, posExpr);
1740
2548
  }
2549
+ function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
2550
+ return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
2551
+ }
2552
+ function propagateFailBody(ctx, srcCtx = "_ctx") {
2553
+ if (srcCtx !== "_ctx") {
2554
+ if (ctx.failLabel) {
2555
+ if (!ctx.recordFail) return `break ${ctx.failLabel}`;
2556
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
2557
+ }
2558
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
2559
+ }
2560
+ if (ctx.failLabel) return `break ${ctx.failLabel}`;
2561
+ return resultFromRecorded();
2562
+ }
1741
2563
  function emitIfFail(ctx, cond, body) {
1742
2564
  return [
1743
2565
  `${ind(ctx)}if (${cond}) {`,
@@ -1773,13 +2595,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
1773
2595
  `}`
1774
2596
  ].join("\n"));
1775
2597
  }
1776
- function emitNamedFnCall(ctx, fnName, pos, failExpected) {
2598
+ function emitNamedFnCall(ctx, fnName, pos) {
1777
2599
  const vv = v(ctx, "_pfv");
1778
2600
  const ev = v(ctx, "_pfe");
1779
2601
  return {
1780
2602
  stmts: [
1781
2603
  `${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
1782
- ...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, failBody(ctx, failExpected, pos)),
2604
+ // ref/lazy returns the inner failure verbatim — propagate the recorded
2605
+ // deepest failure (shares _ctx with the named fn), not a "parser" label.
2606
+ ...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
1783
2607
  `${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
1784
2608
  ],
1785
2609
  valueVar: vv,
@@ -1882,13 +2706,34 @@ function ensureTriviaFn(ctx) {
1882
2706
  function ensureTriviaCaptureFn(ctx) {
1883
2707
  return ensureTriviaFn(ctx);
1884
2708
  }
1885
- function firstSetCond(codeVar, fs) {
1886
- if (fs.kind === "any") return "true";
1887
- if (fs.kind === "empty") return "false";
1888
- return fs.ranges.map(
2709
+ function firstSetCond(codeVar, fs2) {
2710
+ if (fs2.kind === "any") return "true";
2711
+ if (fs2.kind === "empty") return "false";
2712
+ return fs2.ranges.map(
1889
2713
  (r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
1890
2714
  ).join(" || ");
1891
2715
  }
2716
+ var SWITCH_RANGE_LIMIT = 4;
2717
+ var SWITCH_MAX_CASES = 48;
2718
+ var SWITCH_MIN_CASES = 3;
2719
+ var _forceDisjointIf = false;
2720
+ function planDisjointDispatch(parsers) {
2721
+ const cases = [];
2722
+ let total = 0;
2723
+ for (const p of parsers) {
2724
+ const fs2 = p._meta.firstSet;
2725
+ if (fs2.kind !== "ranges") return { kind: "if" };
2726
+ const pts = [];
2727
+ for (const r of fs2.ranges) {
2728
+ if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
2729
+ for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
2730
+ }
2731
+ total += pts.length;
2732
+ if (total > SWITCH_MAX_CASES) return { kind: "if" };
2733
+ cases.push(pts);
2734
+ }
2735
+ return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
2736
+ }
1892
2737
  function captureRestoreBody(mL, mR, mTl, mLg) {
1893
2738
  const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
1894
2739
  return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
@@ -2002,18 +2847,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
2002
2847
  return true;
2003
2848
  }
2004
2849
  }
2005
- function emitFallible(inner, ctx, pos) {
2850
+ function emitFallible(inner, ctx, pos, swallow = false) {
2006
2851
  const lbl = v(ctx, "_lbl");
2007
2852
  const okV = `${lbl}ok`;
2008
2853
  const valV = `${lbl}v`;
2009
2854
  const endV = `${lbl}e`;
2010
2855
  const savedLabel = ctx.failLabel;
2011
2856
  const savedIndent = ctx.indent;
2857
+ const savedRecord = ctx.recordFail;
2012
2858
  ctx.failLabel = lbl;
2013
2859
  ctx.indent = savedIndent + 1;
2860
+ if (swallow) ctx.recordFail = false;
2014
2861
  const r = emit(inner, ctx, pos);
2015
2862
  ctx.failLabel = savedLabel;
2016
2863
  ctx.indent = savedIndent;
2864
+ ctx.recordFail = savedRecord;
2017
2865
  const ind0 = ind(ctx);
2018
2866
  const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
2019
2867
  const mL = needsRollback ? v(ctx, "_fcl") : null;
@@ -2036,6 +2884,7 @@ function emitFallible(inner, ctx, pos) {
2036
2884
  ];
2037
2885
  return { stmts, okVar: okV, valVar: valV, endVar: endV };
2038
2886
  }
2887
+ var CHARCODE_CHAIN_MAX = 16;
2039
2888
  function emitLit(def, ctx, pos) {
2040
2889
  const { value, caseInsensitive } = def;
2041
2890
  const len = value.length;
@@ -2058,7 +2907,7 @@ function emitLit(def, ctx, pos) {
2058
2907
  ...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
2059
2908
  `${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
2060
2909
  );
2061
- } else if (len <= 4) {
2910
+ } else if (len <= CHARCODE_CHAIN_MAX) {
2062
2911
  const checks = Array.from(
2063
2912
  { length: len },
2064
2913
  (_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
@@ -2080,7 +2929,55 @@ function emitLit(def, ctx, pos) {
2080
2929
  function escapeKeywordRe(s) {
2081
2930
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2082
2931
  }
2932
+ function emitKeywordsFast(def, ctx, pos) {
2933
+ if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
2934
+ if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
2935
+ if (def.caseInsensitive && def.boundary) return null;
2936
+ let boundary = null;
2937
+ if (def.boundary) {
2938
+ let body = def.boundary;
2939
+ const negated = body.startsWith("^");
2940
+ if (negated) body = body.slice(1);
2941
+ const ranges = parseClassRanges(body);
2942
+ if (!ranges) return null;
2943
+ boundary = { ranges, negated };
2944
+ }
2945
+ const mint = (prefix = "_v") => v(ctx, prefix);
2946
+ const lbl = v(ctx, "_kwLbl");
2947
+ const valV = v(ctx, "_kwv");
2948
+ const endV = v(ctx, "_kwe");
2949
+ const bodyInd = ind(ctx) + " ";
2950
+ const tries = [];
2951
+ for (const w of def.words) {
2952
+ const cps = Array.from(w, (ch) => ch.codePointAt(0));
2953
+ let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
2954
+ if (boundary) {
2955
+ shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
2956
+ }
2957
+ const m = emitShapeMatch(shape, pos, mint, bodyInd);
2958
+ tries.push(
2959
+ ...m.setup,
2960
+ // Slice from input rather than reusing the literal word: `caseInsensitive`
2961
+ // must return the text as it actually appeared (e.g. "ABC" for keyword
2962
+ // "abc"), matching what the original `RegExp.exec()[0]` returned.
2963
+ `${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
2964
+ );
2965
+ }
2966
+ const stmts = [
2967
+ `${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
2968
+ `${ind(ctx)}${lbl}: {`,
2969
+ ...tries,
2970
+ `${ind(ctx)}}`,
2971
+ // Every word has length >= 1 (checked above), so a real match always
2972
+ // advances past `pos` — `endV === pos` only happens when no candidate matched.
2973
+ ...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
2974
+ ];
2975
+ stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
2976
+ return { stmts, valueVar: valV, endVar: endV };
2977
+ }
2083
2978
  function emitKeywords(def, ctx, pos) {
2979
+ const fast = emitKeywordsFast(def, ctx, pos);
2980
+ if (fast) return fast;
2084
2981
  const alt = def.words.map(escapeKeywordRe).join("|");
2085
2982
  const boundary = def.boundary ? `(?![${def.boundary}])` : "";
2086
2983
  const flags = def.caseInsensitive ? "iuy" : "uy";
@@ -2105,6 +3002,22 @@ function emitKeywords(def, ctx, pos) {
2105
3002
  return { stmts, valueVar: vv, endVar };
2106
3003
  }
2107
3004
  function emitRegex(def, ctx, pos) {
3005
+ const expectedStr = JSON.stringify(`/${def.source}/`);
3006
+ const shape = scanShapeFromRegex(def.source, def.flags);
3007
+ if (shape) {
3008
+ const vv2 = v(ctx);
3009
+ const scanned = emitScannableTerminal(shape, {
3010
+ ind: ind(ctx),
3011
+ pos,
3012
+ valueVar: vv2,
3013
+ failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
3014
+ fresh: (prefix) => v(ctx, prefix)
3015
+ });
3016
+ if (scanned) {
3017
+ const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
3018
+ return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
3019
+ }
3020
+ }
2108
3021
  const flags = "y" + def.flags.replace(/[gy]/g, "");
2109
3022
  const key = `${def.optimizedSource}/${flags}`;
2110
3023
  let rName = ctx.regexMap.get(key);
@@ -2115,7 +3028,6 @@ function emitRegex(def, ctx, pos) {
2115
3028
  }
2116
3029
  const mv = v(ctx, "_m");
2117
3030
  const vv = v(ctx);
2118
- const expectedStr = JSON.stringify(`/${def.source}/`);
2119
3031
  const stmts = [
2120
3032
  `${ind(ctx)}${rName}.lastIndex = ${pos}`,
2121
3033
  `${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
@@ -2174,15 +3086,68 @@ function emitSeq(def, ctx, pos) {
2174
3086
  stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
2175
3087
  return { stmts, valueVar: arrV, endVar };
2176
3088
  }
3089
+ function staticExpectedArr(parsers) {
3090
+ return JSON.stringify(parsers.flatMap((p) => {
3091
+ const e = staticExpected(p);
3092
+ return e.length > 0 ? e : [p._tag];
3093
+ }));
3094
+ }
3095
+ function failsAtStart(p) {
3096
+ const d = p._def;
3097
+ switch (d.tag) {
3098
+ case "literal":
3099
+ case "regex":
3100
+ case "keywords":
3101
+ case "guard":
3102
+ case "not":
3103
+ return true;
3104
+ case "transform":
3105
+ case "label":
3106
+ return failsAtStart(d.parser);
3107
+ default:
3108
+ return false;
3109
+ }
3110
+ }
3111
+ function armStaticExpected(ctx, p) {
3112
+ return hoistExpected(ctx, staticExpectedArr([p]));
3113
+ }
3114
+ function canMatchEmptyAtStart(p) {
3115
+ const d = p._def;
3116
+ switch (d.tag) {
3117
+ case "regex":
3118
+ return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
3119
+ case "optional":
3120
+ return true;
3121
+ case "many":
3122
+ return true;
3123
+ case "transform":
3124
+ case "label":
3125
+ return canMatchEmptyAtStart(d.parser);
3126
+ case "literal":
3127
+ return d.value.length === 0;
3128
+ default:
3129
+ return false;
3130
+ }
3131
+ }
3132
+ function needsFirstSetGuard(p) {
3133
+ const fs2 = p._meta.firstSet;
3134
+ return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
3135
+ }
3136
+ function emitDisjointArm(p, ctx, pos, valV, endV) {
3137
+ if (failsAtStart(p)) {
3138
+ const r = emit(p, ctx, pos);
3139
+ return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
3140
+ }
3141
+ const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
3142
+ return [
3143
+ ...stmts,
3144
+ ...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
3145
+ `${ind(ctx)}${valV} = ${valVar}`,
3146
+ `${ind(ctx)}${endV} = ${endVar}`
3147
+ ];
3148
+ }
2177
3149
  function emitChoice(def, ctx, pos) {
2178
- const allExpected = JSON.stringify(
2179
- def.parsers.map((p) => {
2180
- const d = p._def;
2181
- if (d.tag === "literal") return JSON.stringify(d.value);
2182
- if (d.tag === "regex") return `/${d.source}/`;
2183
- return p._tag;
2184
- })
2185
- );
3150
+ const allExpected = staticExpectedArr(def.parsers);
2186
3151
  if (def.disjoint) {
2187
3152
  const codeV = v(ctx, "_code");
2188
3153
  const valV = v(ctx, "_chv");
@@ -2191,6 +3156,27 @@ function emitChoice(def, ctx, pos) {
2191
3156
  `${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
2192
3157
  `${ind(ctx)}let ${valV}, ${endV} = ${pos}`
2193
3158
  ];
3159
+ const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
3160
+ if (plan.kind === "switch") {
3161
+ stmts.push(`${ind(ctx)}switch (${codeV}) {`);
3162
+ ctx.indent++;
3163
+ for (let i = 0; i < def.parsers.length; i++) {
3164
+ const p = def.parsers[i];
3165
+ for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
3166
+ stmts.push(`${ind(ctx)}{`);
3167
+ ctx.indent++;
3168
+ stmts.push(
3169
+ ...emitDisjointArm(p, ctx, pos, valV, endV),
3170
+ `${ind(ctx)}break`
3171
+ );
3172
+ ctx.indent--;
3173
+ stmts.push(`${ind(ctx)}}`);
3174
+ }
3175
+ stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
3176
+ ctx.indent--;
3177
+ stmts.push(`${ind(ctx)}}`);
3178
+ return { stmts, valueVar: valV, endVar: endV };
3179
+ }
2194
3180
  let first = true;
2195
3181
  for (const p of def.parsers) {
2196
3182
  const cond = firstSetCond(codeV, p._meta.firstSet);
@@ -2198,10 +3184,7 @@ function emitChoice(def, ctx, pos) {
2198
3184
  first = false;
2199
3185
  stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
2200
3186
  ctx.indent++;
2201
- const r = emit(p, ctx, pos);
2202
- stmts.push(...r.stmts);
2203
- stmts.push(`${ind(ctx)}${valV} = ${r.valueVar}`);
2204
- stmts.push(`${ind(ctx)}${endV} = ${r.endVar}`);
3187
+ stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
2205
3188
  ctx.indent--;
2206
3189
  stmts.push(`${ind(ctx)}}`);
2207
3190
  }
@@ -2210,7 +3193,7 @@ function emitChoice(def, ctx, pos) {
2210
3193
  }
2211
3194
  return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
2212
3195
  }
2213
- function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
3196
+ function emitGreedyClassify(def, superIndex, ctx, pos) {
2214
3197
  const superParser = def.parsers[superIndex];
2215
3198
  const regexDef = getCoreRegexDef(superParser);
2216
3199
  const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
@@ -2225,10 +3208,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
2225
3208
  const wordV = v(ctx, "_gw");
2226
3209
  const endV = v(ctx, "_ge");
2227
3210
  const valV = v(ctx, "_gcv");
3211
+ const regexExpected = JSON.stringify(staticExpected(superParser));
2228
3212
  const stmts = [
2229
3213
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
2230
3214
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
2231
- ...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, allExpected, pos)),
3215
+ ...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
2232
3216
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
2233
3217
  `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
2234
3218
  `${ind(ctx)}let ${valV}`
@@ -2286,18 +3270,25 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
2286
3270
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
2287
3271
  return { stmts, valueVar: valV, endVar: endV };
2288
3272
  }
2289
- function emitFirstMatch(def, allExpected, ctx, pos) {
3273
+ function emitFirstMatch(def, ctx, pos) {
2290
3274
  const resValV = v(ctx, "_crv");
2291
3275
  const resEndV = v(ctx, "_cre");
2292
3276
  const resOkV = v(ctx, "_crok");
3277
+ const codeV = v(ctx, "_chcode");
3278
+ const slots = def.parsers.map(() => v(ctx, "_cfx"));
2293
3279
  const ind0 = ind(ctx);
2294
3280
  const stmts = [
2295
- `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`
3281
+ `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
3282
+ `${ind0}let ${slots.join(", ")}`,
3283
+ `${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
2296
3284
  ];
2297
3285
  for (let i = 0; i < def.parsers.length; i++) {
2298
3286
  const p = def.parsers[i];
2299
3287
  const gate = def.gates[i];
2300
3288
  const autoNot = def.autoNot[i];
3289
+ const atStart = failsAtStart(p);
3290
+ const staticFx = armStaticExpected(ctx, p);
3291
+ const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
2301
3292
  let gateCond = null;
2302
3293
  if (gate) {
2303
3294
  const gateIdx = ctx.mapFns.length;
@@ -2312,49 +3303,53 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
2312
3303
  const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
2313
3304
  const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
2314
3305
  const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
3306
+ const failSlot = atStart ? staticFx : "_ctx._fx";
2315
3307
  stmts.push(`${ind0}if (${skipCond}) {`);
2316
- ctx.indent++;
2317
- const ind1 = ind(ctx);
3308
+ if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
3309
+ ctx.indent += fsGuard ? 2 : 1;
2318
3310
  if (markLeaves) {
2319
3311
  stmts.push(
2320
- `${ind1}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
2321
- `${ind1}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
2322
- `${ind1}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
2323
- `${ind1}const ${markLog} = _ctx._triviaLog?.length ?? 0`
3312
+ `${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
3313
+ `${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
3314
+ `${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
3315
+ `${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
2324
3316
  );
2325
3317
  }
2326
- const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
3318
+ const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
2327
3319
  stmts.push(...armStmts);
2328
- ctx.indent--;
2329
3320
  if (autoNot && autoNot.length > 0) {
2330
3321
  const anCode = v(ctx, "_anc");
2331
3322
  const rejectCond = autoNot.map(
2332
3323
  (check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
2333
3324
  ).join(" || ");
2334
- stmts.push(`${ind0} if (${okVar}) {`);
2335
- stmts.push(`${ind0} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
2336
- stmts.push(`${ind0} if (!(${rejectCond})) {`);
2337
- stmts.push(`${ind0} ${resValV} = ${valVar}`);
2338
- stmts.push(`${ind0} ${resEndV} = ${endVar}`);
2339
- stmts.push(`${ind0} ${resOkV} = true`);
2340
- stmts.push(`${ind0} }`);
2341
- stmts.push(`${ind0} }`);
2342
- if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
3325
+ stmts.push(`${ind(ctx)}if (${okVar}) {`);
3326
+ stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
3327
+ stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
3328
+ stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
3329
+ stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
3330
+ stmts.push(`${ind(ctx)} ${resOkV} = true`);
3331
+ stmts.push(`${ind(ctx)} }`);
3332
+ stmts.push(`${ind(ctx)}}`);
3333
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
3334
+ if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
2343
3335
  } else {
2344
- stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2345
- if (rollback) stmts.push(`${ind0} else { ${rollback} }`);
3336
+ stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
3337
+ stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
2346
3338
  }
3339
+ ctx.indent -= fsGuard ? 2 : 1;
3340
+ if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
2347
3341
  stmts.push(`${ind0}}`);
2348
3342
  }
2349
- stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, allExpected, pos)));
3343
+ const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
3344
+ stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
2350
3345
  return { stmts, valueVar: resValV, endVar: resEndV };
2351
3346
  }
2352
3347
  function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
2353
3348
  if (strategy.tag === "greedyClassify")
2354
- return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
3349
+ return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
2355
3350
  if (strategy.tag === "literalsLongestFirst")
2356
3351
  return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
2357
- return emitFirstMatch(def, allExpected, ctx, pos);
3352
+ return emitFirstMatch(def, ctx, pos);
2358
3353
  }
2359
3354
  function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2360
3355
  const def = p._def;
@@ -2375,7 +3370,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2375
3370
  function emitLiteralCondition(litVal, pos) {
2376
3371
  const len = litVal.length;
2377
3372
  if (len === 0) return "true";
2378
- if (len > 4) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
3373
+ if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
2379
3374
  const checks = [`${pos} + ${len} <= input.length`];
2380
3375
  for (let i = 0; i < len; i++) {
2381
3376
  const code = litVal.codePointAt(i);
@@ -2424,7 +3419,7 @@ function emitMany(def, ctx, pos) {
2424
3419
  itemPos = npV;
2425
3420
  }
2426
3421
  }
2427
- const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
3422
+ const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
2428
3423
  stmts.push(
2429
3424
  ...iterStmts,
2430
3425
  `${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
@@ -2438,7 +3433,7 @@ function emitMany(def, ctx, pos) {
2438
3433
  function emitOptional(def, ctx, pos) {
2439
3434
  const valV = v(ctx, "_opt");
2440
3435
  const endV = v(ctx, "_opte");
2441
- const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
3436
+ const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
2442
3437
  const ind0 = ind(ctx);
2443
3438
  const stmts = [
2444
3439
  ...lblStmts,
@@ -2450,7 +3445,7 @@ function emitOptional(def, ctx, pos) {
2450
3445
  function emitSepBy(_p, def, ctx, pos) {
2451
3446
  const arrV = v(ctx, "_arr");
2452
3447
  const curV = v(ctx, "_cur");
2453
- const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
3448
+ const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
2454
3449
  const stmts = [
2455
3450
  `${ind(ctx)}const ${arrV} = []`,
2456
3451
  `${ind(ctx)}let ${curV} = ${pos}`,
@@ -2482,11 +3477,11 @@ function emitSepBy(_p, def, ctx, pos) {
2482
3477
  );
2483
3478
  sepAtPos = spV;
2484
3479
  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}; `;
2485
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3480
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2486
3481
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
2487
3482
  const npV = v(ctx, "_np");
2488
3483
  stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
2489
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3484
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2490
3485
  stmts.push(
2491
3486
  ...nextStmts,
2492
3487
  // item failed → unwind the separator too, back to the end of the last item
@@ -2499,11 +3494,11 @@ function emitSepBy(_p, def, ctx, pos) {
2499
3494
  const spV = v(ctx, "_sp");
2500
3495
  stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
2501
3496
  sepAtPos = spV;
2502
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3497
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2503
3498
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2504
3499
  const npV = v(ctx, "_np");
2505
3500
  stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
2506
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
3501
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
2507
3502
  stmts.push(
2508
3503
  ...nextStmts,
2509
3504
  `${ind(ctx)}if (!${nextOk}) break`,
@@ -2520,10 +3515,10 @@ function emitSepBy(_p, def, ctx, pos) {
2520
3515
  `${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
2521
3516
  );
2522
3517
  }
2523
- const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
3518
+ const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
2524
3519
  stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
2525
3520
  const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
2526
- const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
3521
+ const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
2527
3522
  stmts.push(
2528
3523
  ...nextStmts,
2529
3524
  `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
@@ -2581,11 +3576,12 @@ function emitScanTo(def, ctx, pos) {
2581
3576
  return { stmts, valueVar: valV, endVar: curV };
2582
3577
  }
2583
3578
  function emitNot(def, ctx, pos) {
2584
- const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
3579
+ const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
3580
+ const label2 = JSON.stringify(`not(${def.parser._tag})`);
2585
3581
  return {
2586
3582
  stmts: [
2587
3583
  ...stmts,
2588
- ...emitIfFail(ctx, okVar, failBody(ctx, '"not"', pos))
3584
+ ...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
2589
3585
  ],
2590
3586
  valueVar: "null",
2591
3587
  endVar: pos
@@ -2617,7 +3613,7 @@ function emitNode(def, ctx, pos) {
2617
3613
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
2618
3614
  stmts.push(...innerStmts);
2619
3615
  stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
2620
- stmts.push(...emitIfFail(ctx, `!${okVar}`, failBody(ctx, '"node"', pos)));
3616
+ stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
2621
3617
  let stV = "undefined";
2622
3618
  if (clonesState) {
2623
3619
  stV = v(ctx, "_nst");
@@ -2639,9 +3635,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
2639
3635
  const rv = v(ctx, "_rt");
2640
3636
  const vv = v(ctx, "_rtv");
2641
3637
  const ev = v(ctx, "_rte");
3638
+ const failStmt = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${rv}.span.start; _ctx._fx = ${rv}.expected; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : `return { ok: false, expected: ${rv}.expected, span: ${rv}.span }`;
2642
3639
  const stmts = [
2643
3640
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
2644
- ...emitIfFail(ctx, `!${rv}.ok`, failBody(ctx, '"runtime"', pos)),
3641
+ ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
2645
3642
  `${ind(ctx)}const ${vv} = ${rv}.value`,
2646
3643
  `${ind(ctx)}const ${ev} = ${rv}.span.end`
2647
3644
  ];
@@ -2670,15 +3667,18 @@ function emitLazy(p, def, ctx, pos) {
2670
3667
  }
2671
3668
  const savedIndent = ctx.indent;
2672
3669
  const savedFailLabel = ctx.failLabel;
3670
+ const savedRecord = ctx.recordFail;
2673
3671
  ctx.indent = 1;
2674
3672
  ctx.failLabel = "_pfail";
3673
+ ctx.recordFail = true;
2675
3674
  const r = emit(resolved, ctx, "_pos");
2676
3675
  ctx.indent = savedIndent;
2677
3676
  ctx.failLabel = savedFailLabel;
3677
+ ctx.recordFail = savedRecord;
2678
3678
  pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
2679
3679
  }
2680
3680
  const fnName = ctx.namedParsers.get(p);
2681
- return emitNamedFnCall(ctx, fnName, pos, '"parser"');
3681
+ return emitNamedFnCall(ctx, fnName, pos);
2682
3682
  }
2683
3683
  function emitRecover(def, ctx, pos) {
2684
3684
  const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
@@ -2696,7 +3696,7 @@ function emitRecover(def, ctx, pos) {
2696
3696
  `${ind0} if (${sentOk}) break`,
2697
3697
  `${ind0} ${scanV}++`,
2698
3698
  `${ind0} }`,
2699
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: [] }`,
3699
+ `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
2700
3700
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
2701
3701
  `${ind0} ${valVar} = ${errV}`,
2702
3702
  `${ind0} ${endVar} = ${scanV}`,
@@ -2854,11 +3854,14 @@ function emit(p, ctx, pos) {
2854
3854
  ctx.namedParsers.set(innerParser, fnName);
2855
3855
  const savedIndent = ctx.indent;
2856
3856
  const savedFailLabel = ctx.failLabel;
3857
+ const savedRecord = ctx.recordFail;
2857
3858
  ctx.indent = 1;
2858
3859
  ctx.failLabel = "_pfail";
3860
+ ctx.recordFail = true;
2859
3861
  const innerR = emit(innerParser, ctx, "_pos");
2860
3862
  ctx.indent = savedIndent;
2861
3863
  ctx.failLabel = savedFailLabel;
3864
+ ctx.recordFail = savedRecord;
2862
3865
  pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
2863
3866
  }
2864
3867
  const fn = ctx.namedParsers.get(innerParser);
@@ -2869,7 +3872,9 @@ function emit(p, ctx, pos) {
2869
3872
  stmts: [
2870
3873
  `${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
2871
3874
  `${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
2872
- ...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, failBody(ctx, '"withCtx"', pos)),
3875
+ // withCtx runs on a spread child ctx — copy its recorded failure back
3876
+ // and propagate the inner failure verbatim (interpreter parity).
3877
+ ...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
2873
3878
  `${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
2874
3879
  ],
2875
3880
  valueVar: vv,
@@ -2970,7 +3975,7 @@ function analyzeLazyUsageMulti(roots) {
2970
3975
  const recursive = /* @__PURE__ */ new Set();
2971
3976
  const descended = /* @__PURE__ */ new Set();
2972
3977
  const active = /* @__PURE__ */ new Set();
2973
- function walk(p) {
3978
+ function walk2(p) {
2974
3979
  const def = p._def;
2975
3980
  if (def.tag === "lazy") {
2976
3981
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -2987,13 +3992,13 @@ function analyzeLazyUsageMulti(roots) {
2987
3992
  return;
2988
3993
  }
2989
3994
  active.add(p);
2990
- walk(resolved);
3995
+ walk2(resolved);
2991
3996
  active.delete(p);
2992
3997
  return;
2993
3998
  }
2994
- for (const child of childrenOf(def)) walk(child);
3999
+ for (const child of childrenOf(def)) walk2(child);
2995
4000
  }
2996
- for (const root of roots) walk(root);
4001
+ for (const root of roots) walk2(root);
2997
4002
  return { counts, recursive };
2998
4003
  }
2999
4004
  function compile(combinator, mapFnSources) {
@@ -3002,6 +4007,9 @@ function compile(combinator, mapFnSources) {
3002
4007
  indent: 1,
3003
4008
  regexDecls: [],
3004
4009
  regexMap: /* @__PURE__ */ new Map(),
4010
+ expectedDecls: [],
4011
+ expectedMap: /* @__PURE__ */ new Map(),
4012
+ recordFail: true,
3005
4013
  mapFns: [],
3006
4014
  mapFnSrcs: [],
3007
4015
  buildFns: [],
@@ -3023,6 +4031,7 @@ function compile(combinator, mapFnSources) {
3023
4031
  const source = [
3024
4032
  ...emptyTlDecls,
3025
4033
  ...ctx.regexDecls,
4034
+ ...ctx.expectedDecls,
3026
4035
  "",
3027
4036
  ...namedPrelude,
3028
4037
  ctx.namedFnDecls.join("\n\n"),
@@ -3035,6 +4044,7 @@ function compile(combinator, mapFnSources) {
3035
4044
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
3036
4045
  ...emptyTlDecls,
3037
4046
  ...ctx.regexDecls,
4047
+ ...ctx.expectedDecls,
3038
4048
  collatorDecl,
3039
4049
  ...namedPrelude,
3040
4050
  ...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
@@ -3080,6 +4090,9 @@ function compileRuleMap(ruleMap) {
3080
4090
  indent: 1,
3081
4091
  regexDecls: [],
3082
4092
  regexMap: /* @__PURE__ */ new Map(),
4093
+ expectedDecls: [],
4094
+ expectedMap: /* @__PURE__ */ new Map(),
4095
+ recordFail: true,
3083
4096
  mapFns: [],
3084
4097
  mapFnSrcs: [],
3085
4098
  buildFns: [],
@@ -3108,6 +4121,7 @@ function compileRuleMap(ruleMap) {
3108
4121
  const hoistedDecls = [
3109
4122
  ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
3110
4123
  ...ctx.regexDecls.map((d) => ` ${d}`),
4124
+ ...ctx.expectedDecls.map((d) => ` ${d}`),
3111
4125
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
3112
4126
  mfDecl,
3113
4127
  buildDecl,
@@ -3149,12 +4163,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
3149
4163
  const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
3150
4164
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
3151
4165
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
3152
- const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
4166
+ const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
3153
4167
  if (!needsWrapper) return innerFn;
3154
4168
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
3155
4169
  const hoistedDecls = [
3156
4170
  emptyTlDecl,
3157
4171
  ...ctx.regexDecls.map((d) => ` ${d}`),
4172
+ ...ctx.expectedDecls.map((d) => ` ${d}`),
3158
4173
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
3159
4174
  mfDecl,
3160
4175
  buildDecl,
@@ -3172,58 +4187,6 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
3172
4187
  ].join("\n");
3173
4188
  }
3174
4189
 
3175
- // src/combinators/expect.ts
3176
- function staticExpected(c) {
3177
- const def = c._def;
3178
- switch (def.tag) {
3179
- case "literal":
3180
- return [JSON.stringify(def.value)];
3181
- case "regex":
3182
- return [`/${def.source}/`];
3183
- case "keywords":
3184
- return def.words.map((w) => JSON.stringify(w));
3185
- case "label":
3186
- return [def.label];
3187
- case "choice":
3188
- return def.parsers.flatMap(staticExpected);
3189
- case "sequence":
3190
- return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
3191
- case "node":
3192
- case "grammar":
3193
- case "trivia":
3194
- case "optional":
3195
- case "many":
3196
- case "oneOrMore":
3197
- case "transform":
3198
- case "not":
3199
- return staticExpected(def.parser);
3200
- case "lazy":
3201
- return staticExpected(def.thunk());
3202
- default:
3203
- return [];
3204
- }
3205
- }
3206
- function expect(combinator, label2) {
3207
- const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
3208
- const meta = {
3209
- firstSet: combinator._meta.firstSet,
3210
- canMatchNewline: combinator._meta.canMatchNewline,
3211
- isTrivia: false
3212
- };
3213
- return {
3214
- _tag: "expect",
3215
- _meta: meta,
3216
- _def: { tag: "expect", parser: combinator, label: label2, expected },
3217
- parse(input, pos, ctx) {
3218
- const result = combinator.parse(input, pos, ctx);
3219
- if (result.ok) return result;
3220
- const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
3221
- ctx._errors?.push(error);
3222
- return { ok: true, value: error, span: { start: pos, end: pos } };
3223
- }
3224
- };
3225
- }
3226
-
3227
4190
  // src/combinators/scanTo.ts
3228
4191
  function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
3229
4192
  const meta = {
@@ -3304,11 +4267,11 @@ function balanced(open, close, options = {}) {
3304
4267
  return combi;
3305
4268
  }
3306
4269
  function firstSetClassChars(c) {
3307
- const fs = c._meta.firstSet;
3308
- if (fs.kind === "empty") return [];
3309
- if (fs.kind !== "ranges") return null;
4270
+ const fs2 = c._meta.firstSet;
4271
+ if (fs2.kind === "empty") return [];
4272
+ if (fs2.kind !== "ranges") return null;
3310
4273
  const out = [];
3311
- for (const { lo, hi } of fs.ranges) {
4274
+ for (const { lo, hi } of fs2.ranges) {
3312
4275
  if (hi - lo > 8) return null;
3313
4276
  for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
3314
4277
  }
@@ -3605,7 +4568,124 @@ function evaluateWordFactory(node2, scope, code) {
3605
4568
  function evaluateExpr(node2, scope, code, mapFnSources) {
3606
4569
  return exprToCombi(node2, scope, code, mapFnSources);
3607
4570
  }
3608
- function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
4571
+ function evaluateCombinatorArray(node2, scope, code) {
4572
+ if (node2.type !== "ArrayExpression") return null;
4573
+ const val = anyValue(node2, scope, code, []);
4574
+ if (!Array.isArray(val) || val.length === 0) return null;
4575
+ if (!val.every(isCombinator)) return null;
4576
+ return val;
4577
+ }
4578
+ function asFragmentFactory(expr) {
4579
+ if (expr.type !== "ArrowFunctionExpression" && expr.type !== "FunctionExpression" && expr.type !== "FunctionDeclaration") return null;
4580
+ const fn = expr;
4581
+ return factoryBodyAndReturn(fn) ? fn : null;
4582
+ }
4583
+ var unwrapParen = (e) => e && e.type === "ParenthesizedExpression" ? e.expression : e;
4584
+ function factoryBodyAndReturn(fn) {
4585
+ const body = fn.body;
4586
+ if (!body) return null;
4587
+ const b = unwrapParen(body);
4588
+ if (b.type === "ObjectExpression") return { statements: [], retObj: b };
4589
+ if (b.type === "BlockStatement") {
4590
+ const statements = [];
4591
+ for (const stmt of b.body) {
4592
+ if (stmt.type === "ReturnStatement") {
4593
+ const a = unwrapParen(stmt.argument);
4594
+ if (!a || a.type !== "ObjectExpression") return null;
4595
+ return { statements, retObj: a };
4596
+ }
4597
+ if (stmt.type === "VariableDeclaration") statements.push(stmt);
4598
+ else return null;
4599
+ }
4600
+ }
4601
+ return null;
4602
+ }
4603
+ function spreadFactoryCall(spread, resolve) {
4604
+ const call = spread.argument;
4605
+ if (!call || call.type !== "CallExpression") return null;
4606
+ if (!call.callee || call.callee.type !== "Identifier" || !call.callee.name) return null;
4607
+ const resolved = resolve(call.callee.name);
4608
+ return resolved ? { fn: resolved.fn, args: call.arguments ?? [], code: resolved.code } : null;
4609
+ }
4610
+ function factoryCallScope(fn, args, outer) {
4611
+ const s = new Map(outer);
4612
+ const params = fn.params ?? [];
4613
+ for (let i = 0; i < params.length; i++) {
4614
+ const pn = params[i];
4615
+ if (!pn || pn.type !== "Identifier" || !pn.name) continue;
4616
+ const arg = args[i];
4617
+ if (arg && arg.type === "Identifier" && arg.name && outer.has(arg.name)) {
4618
+ s.set(pn.name, outer.get(arg.name));
4619
+ }
4620
+ }
4621
+ return s;
4622
+ }
4623
+ function propKey(prop) {
4624
+ if (prop.type !== "Property" || prop.computed || !prop.key) return null;
4625
+ if (prop.key.type === "Identifier") return prop.key.name ?? null;
4626
+ if (prop.key.type === "Literal") return String(prop.key.value);
4627
+ return null;
4628
+ }
4629
+ function collectRuleKeys(retObj, resolve, seen) {
4630
+ const out = [];
4631
+ for (const prop of retObj.properties) {
4632
+ if (prop.type === "SpreadElement") {
4633
+ const sf = spreadFactoryCall(prop, resolve);
4634
+ if (!sf || seen.has(sf.fn)) return null;
4635
+ const br = factoryBodyAndReturn(sf.fn);
4636
+ if (!br) return null;
4637
+ const nested = collectRuleKeys(br.retObj, resolve, new Set(seen).add(sf.fn));
4638
+ if (!nested) return null;
4639
+ out.push(...nested);
4640
+ continue;
4641
+ }
4642
+ const key = propKey(prop);
4643
+ if (!key) return null;
4644
+ out.push(key);
4645
+ }
4646
+ return out;
4647
+ }
4648
+ function flattenRuleEntries(retObj, scope, code, resolve, seen) {
4649
+ const out = [];
4650
+ for (const prop of retObj.properties) {
4651
+ if (prop.type === "SpreadElement") {
4652
+ const sf = spreadFactoryCall(prop, resolve);
4653
+ if (!sf || seen.has(sf.fn)) return null;
4654
+ const br = factoryBodyAndReturn(sf.fn);
4655
+ if (!br) return null;
4656
+ const fragScope = factoryCallScope(sf.fn, sf.args, scope);
4657
+ if (!evalBodyStatements(br.statements, fragScope, sf.code)) return null;
4658
+ const nested = flattenRuleEntries(br.retObj, fragScope, sf.code, resolve, new Set(seen).add(sf.fn));
4659
+ if (!nested) return null;
4660
+ out.push(...nested);
4661
+ continue;
4662
+ }
4663
+ const key = propKey(prop);
4664
+ if (!key) return null;
4665
+ out.push({ key, value: prop.value, scope, code });
4666
+ }
4667
+ return out;
4668
+ }
4669
+ function evalBodyStatements(statements, scope, code) {
4670
+ const bodyMfs = [];
4671
+ for (const stmt of statements) {
4672
+ for (const d of stmt.declarations) {
4673
+ const decl = d;
4674
+ if (!decl.init) return false;
4675
+ const id = decl.id;
4676
+ if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return false;
4677
+ const name = id.name;
4678
+ const before = bodyMfs.length;
4679
+ const val = anyValue(decl.init, scope, code, bodyMfs);
4680
+ if (val === null) return false;
4681
+ const thisDeclMfSrcs = bodyMfs.slice(before);
4682
+ if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
4683
+ else scope.set(name, val);
4684
+ }
4685
+ }
4686
+ return true;
4687
+ }
4688
+ function evaluateParserFactory(factoryNode, scope, code, mapFnSources, resolveFactory = () => null) {
3609
4689
  if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
3610
4690
  const factory = factoryNode;
3611
4691
  const params = factory.params;
@@ -3637,40 +4717,21 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
3637
4717
  if (!returnExpr) return null;
3638
4718
  const retObj = returnExpr.type === "ParenthesizedExpression" ? returnExpr.expression : returnExpr;
3639
4719
  if (retObj.type !== "ObjectExpression") return null;
4720
+ const keys = collectRuleKeys(retObj, resolveFactory, /* @__PURE__ */ new Set());
4721
+ if (!keys) return null;
3640
4722
  const ruleRefs = /* @__PURE__ */ new Map();
3641
- for (const prop of retObj.properties) {
3642
- if (prop.type !== "Property") return null;
3643
- if (prop.computed) return null;
3644
- const p = prop;
3645
- const key = p.key.type === "Identifier" ? p.key.name : p.key.type === "Literal" ? String(p.key.value) : null;
3646
- if (!key) return null;
3647
- ruleRefs.set(key, ref());
4723
+ for (const key of keys) {
4724
+ if (!ruleRefs.has(key)) ruleRefs.set(key, ref());
3648
4725
  }
3649
4726
  const localScope = new Map(scope);
3650
4727
  localScope.set(proxyName, Object.fromEntries(ruleRefs));
3651
- const bodyMfs = [];
3652
- for (const stmt of statements) {
3653
- for (const d of stmt.declarations) {
3654
- const decl = d;
3655
- if (!decl.init) return null;
3656
- const id = decl.id;
3657
- if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return null;
3658
- const name = id.name;
3659
- const before = bodyMfs.length;
3660
- const val = anyValue(decl.init, localScope, code, bodyMfs);
3661
- if (val === null) return null;
3662
- const thisDeclMfSrcs = bodyMfs.slice(before);
3663
- if (isCombinator(val)) {
3664
- localScope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
3665
- } else {
3666
- localScope.set(name, val);
3667
- }
3668
- }
3669
- }
3670
- for (const prop of retObj.properties) {
3671
- const p = prop;
3672
- const key = p.key.type === "Identifier" ? p.key.name : String(p.key.value);
3673
- const val = anyValue(p.value, localScope, code, mapFnSources);
4728
+ if (!evalBodyStatements(statements, localScope, code)) return null;
4729
+ const entries = flattenRuleEntries(retObj, localScope, code, resolveFactory, /* @__PURE__ */ new Set());
4730
+ if (!entries) return null;
4731
+ const finalByKey = /* @__PURE__ */ new Map();
4732
+ for (const e of entries) finalByKey.set(e.key, e);
4733
+ for (const [key, e] of finalByKey) {
4734
+ const val = anyValue(e.value, e.scope, e.code, mapFnSources);
3674
4735
  if (!isCombinator(val)) return null;
3675
4736
  ruleRefs.get(key).define(val);
3676
4737
  }
@@ -3728,6 +4789,40 @@ function referencesAny(node2, names, scope) {
3728
4789
 
3729
4790
  // src/plugin/index.ts
3730
4791
  var PARSEMAN_MODULE = "parseman";
4792
+ var _fragmentResolver = null;
4793
+ function getFragmentResolver() {
4794
+ return _fragmentResolver ??= new import_oxc_resolver.ResolverFactory({
4795
+ extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"],
4796
+ // TS/NodeNext style: a `./x.js` import may point at `./x.ts` source.
4797
+ extensionAlias: {
4798
+ ".js": [".ts", ".tsx", ".js", ".jsx"],
4799
+ ".mjs": [".mts", ".mjs"],
4800
+ ".cjs": [".cts", ".cjs"]
4801
+ },
4802
+ conditionNames: ["source", "development", "import", "require", "default"],
4803
+ mainFields: ["source", "module", "main"]
4804
+ });
4805
+ }
4806
+ var _moduleParseCache = /* @__PURE__ */ new Map();
4807
+ function parseModuleCached(filePath) {
4808
+ let mtimeMs;
4809
+ try {
4810
+ mtimeMs = fs.statSync(filePath).mtimeMs;
4811
+ } catch {
4812
+ return null;
4813
+ }
4814
+ const hit = _moduleParseCache.get(filePath);
4815
+ if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
4816
+ let parsed = null;
4817
+ try {
4818
+ const src = fs.readFileSync(filePath, "utf8");
4819
+ const r = (0, import_oxc_parser.parseSync)(filePath, src);
4820
+ if (r.errors.length === 0) parsed = { body: r.program.body, src };
4821
+ } catch {
4822
+ }
4823
+ _moduleParseCache.set(filePath, { mtimeMs, parsed });
4824
+ return parsed;
4825
+ }
3731
4826
  var plugin_default = (0, import_unplugin.createUnplugin)((opts = {}) => ({
3732
4827
  name: "parseman",
3733
4828
  // Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
@@ -3763,15 +4858,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3763
4858
  const body = result.program.body;
3764
4859
  const macroImports = [];
3765
4860
  const allNames = /* @__PURE__ */ new Set();
4861
+ const importBindings = /* @__PURE__ */ new Map();
3766
4862
  for (const stmt of body) {
3767
4863
  if (stmt.type !== "ImportDeclaration") continue;
3768
4864
  const s = stmt;
3769
- if (!moduleAliases.has(s.source.value)) continue;
3770
- const isMacro = s.attributes.some((a) => {
4865
+ const isMacro = moduleAliases.has(s.source.value) && s.attributes.some((a) => {
3771
4866
  const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
3772
4867
  return key === "type" && a.value.value === "macro";
3773
4868
  });
3774
- if (!isMacro) continue;
4869
+ if (!isMacro) {
4870
+ for (const spec of s.specifiers) {
4871
+ if (spec.type === "ImportSpecifier") {
4872
+ const imported = spec.imported.name ?? spec.local.name;
4873
+ importBindings.set(spec.local.name, { source: s.source.value, imported });
4874
+ }
4875
+ }
4876
+ continue;
4877
+ }
3775
4878
  const names = /* @__PURE__ */ new Set();
3776
4879
  for (const spec of s.specifiers) {
3777
4880
  if (spec.type === "ImportSpecifier") names.add(spec.local.name);
@@ -3781,6 +4884,63 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3781
4884
  }
3782
4885
  if (macroImports.length === 0) return null;
3783
4886
  const scope = /* @__PURE__ */ new Map();
4887
+ const factories = /* @__PURE__ */ new Map();
4888
+ const resolver = getFragmentResolver();
4889
+ const importedFactoryCache = /* @__PURE__ */ new Map();
4890
+ const OUTPUT_DIR = /([\\/])(dist|lib|build|out|output|compiled|cjs|esm|es)([\\/])/;
4891
+ const resolveSourcePath = (spec) => {
4892
+ let resolved = null;
4893
+ try {
4894
+ resolved = resolver.resolveFileSync(id, spec).path ?? null;
4895
+ } catch {
4896
+ }
4897
+ if (!resolved) return null;
4898
+ if (OUTPUT_DIR.test(resolved)) {
4899
+ const srcCandidate = resolved.replace(OUTPUT_DIR, "$1src$3").replace(/\.(c|m)?js$/, ".ts");
4900
+ try {
4901
+ if (fs.statSync(srcCandidate).isFile()) return srcCandidate;
4902
+ } catch {
4903
+ }
4904
+ }
4905
+ return resolved;
4906
+ };
4907
+ const resolveImportedFactory = (name) => {
4908
+ if (importedFactoryCache.has(name)) return importedFactoryCache.get(name);
4909
+ let result2 = null;
4910
+ const binding = importBindings.get(name);
4911
+ if (binding) {
4912
+ const filePath = resolveSourcePath(binding.source);
4913
+ const mod = filePath ? parseModuleCached(filePath) : null;
4914
+ if (mod) {
4915
+ for (const st of mod.body) {
4916
+ if (st.type !== "ExportNamedDeclaration" || !st.declaration) continue;
4917
+ const decl = st.declaration;
4918
+ if (decl.type === "VariableDeclaration") {
4919
+ for (const d of decl.declarations ?? []) {
4920
+ if (d.id?.type === "Identifier" && d.id.name === binding.imported && d.init) {
4921
+ const fac = asFragmentFactory(d.init);
4922
+ if (fac) {
4923
+ result2 = { fn: fac, code: mod.src };
4924
+ break;
4925
+ }
4926
+ }
4927
+ }
4928
+ } else if (decl.type === "FunctionDeclaration" && decl.id?.type === "Identifier" && decl.id.name === binding.imported) {
4929
+ const fac = asFragmentFactory(decl);
4930
+ if (fac) result2 = { fn: fac, code: mod.src };
4931
+ }
4932
+ if (result2) break;
4933
+ }
4934
+ }
4935
+ }
4936
+ importedFactoryCache.set(name, result2);
4937
+ return result2;
4938
+ };
4939
+ const resolveFactory = (n) => {
4940
+ const local = factories.get(n);
4941
+ if (local) return { fn: local, code };
4942
+ return resolveImportedFactory(n);
4943
+ };
3784
4944
  const replacements = [];
3785
4945
  const warnings = [];
3786
4946
  let anyUnresolved = false;
@@ -3801,7 +4961,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3801
4961
  warn(init.start, `${label2}: rules() needs a factory argument`);
3802
4962
  return null;
3803
4963
  }
3804
- const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
4964
+ const ruleMap = evaluateParserFactory(factoryArg, scope, code, [], resolveFactory);
3805
4965
  if (!ruleMap) {
3806
4966
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
3807
4967
  return null;
@@ -3899,6 +5059,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3899
5059
  });
3900
5060
  continue;
3901
5061
  }
5062
+ const frag = asFragmentFactory(init);
5063
+ if (frag) {
5064
+ factories.set(varName, frag);
5065
+ continue;
5066
+ }
3902
5067
  const mapFnSources = [];
3903
5068
  const parser2 = evaluateExpr(init, scope, code, mapFnSources);
3904
5069
  if (parser2 === null) {
@@ -3908,6 +5073,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3908
5073
  scope.set(varName, wordFactory);
3909
5074
  continue;
3910
5075
  }
5076
+ const combiArray = evaluateCombinatorArray(init, scope, code);
5077
+ if (combiArray) {
5078
+ ;
5079
+ scope.set(varName, combiArray);
5080
+ continue;
5081
+ }
3911
5082
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
3912
5083
  continue;
3913
5084
  }