parseman 0.11.0 → 0.12.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.
@@ -813,12 +813,15 @@ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
813
813
  continue;
814
814
  }
815
815
  if (c === 47 && input.charCodeAt(pos + 1) === 42) {
816
- const start = pos;
817
816
  let j = pos + 2;
818
817
  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;
818
+ if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
819
+ const start = pos;
820
+ pos = j + 2;
821
+ chunks.push({ start, end: pos, kindIndex: commentKind });
822
+ continue;
823
+ }
824
+ break;
822
825
  }
823
826
  break;
824
827
  }
@@ -1315,38 +1318,108 @@ function noTrivia(root) {
1315
1318
  return parser({ trivia: null }, root);
1316
1319
  }
1317
1320
 
1321
+ // src/combinators/expect.ts
1322
+ function staticExpected(c) {
1323
+ const def = c._def;
1324
+ switch (def.tag) {
1325
+ case "literal":
1326
+ return [JSON.stringify(def.value)];
1327
+ case "regex":
1328
+ return [`/${def.source}/`];
1329
+ case "keywords":
1330
+ return def.words.map((w) => JSON.stringify(w));
1331
+ case "label":
1332
+ return [def.label];
1333
+ case "choice":
1334
+ return def.parsers.flatMap(staticExpected);
1335
+ case "sequence":
1336
+ return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
1337
+ case "node":
1338
+ case "grammar":
1339
+ case "trivia":
1340
+ case "optional":
1341
+ case "many":
1342
+ case "oneOrMore":
1343
+ case "transform":
1344
+ case "not":
1345
+ return staticExpected(def.parser);
1346
+ case "lazy":
1347
+ return staticExpected(def.thunk());
1348
+ default:
1349
+ return [];
1350
+ }
1351
+ }
1352
+ function expect(combinator, label2) {
1353
+ const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
1354
+ const meta = {
1355
+ firstSet: combinator._meta.firstSet,
1356
+ canMatchNewline: combinator._meta.canMatchNewline,
1357
+ isTrivia: false
1358
+ };
1359
+ return {
1360
+ _tag: "expect",
1361
+ _meta: meta,
1362
+ _def: { tag: "expect", parser: combinator, label: label2, expected },
1363
+ parse(input, pos, ctx) {
1364
+ const result = combinator.parse(input, pos, ctx);
1365
+ if (result.ok) return result;
1366
+ const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
1367
+ ctx._errors?.push(error);
1368
+ return { ok: true, value: error, span: { start: pos, end: pos } };
1369
+ }
1370
+ };
1371
+ }
1372
+
1318
1373
  // src/compiler/scannable-run.ts
1374
+ var BACKSLASH = 92;
1319
1375
  var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
1320
1376
  var META = new Set("()[]{}*+?|^$.".split(""));
1377
+ function shorthandRanges(ch) {
1378
+ return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
1379
+ }
1321
1380
  function parseClassRanges(body) {
1322
1381
  const ranges = [];
1323
1382
  let i = 0;
1324
- const readCp = () => {
1383
+ const readAtom = () => {
1325
1384
  const ch = body[i];
1326
1385
  if (ch === void 0) return null;
1327
1386
  if (ch === "\\") {
1328
1387
  const e = body[i + 1];
1329
1388
  if (e === void 0) return null;
1330
1389
  i += 2;
1331
- return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
1390
+ if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
1391
+ if (e === "d" || e === "w") return { set: shorthandRanges(e) };
1392
+ if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
1393
+ return { cp: e.codePointAt(0) };
1332
1394
  }
1333
1395
  i += ch.length;
1334
- return ch.codePointAt(0);
1396
+ return { cp: ch.codePointAt(0) };
1335
1397
  };
1336
1398
  while (i < body.length) {
1337
- const lo = readCp();
1399
+ const lo = readAtom();
1338
1400
  if (lo === null) return null;
1401
+ if ("set" in lo) {
1402
+ ranges.push(...lo.set);
1403
+ continue;
1404
+ }
1339
1405
  if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
1340
1406
  i += 1;
1341
- const hi = readCp();
1342
- if (hi === null) return null;
1343
- ranges.push([lo, hi]);
1407
+ const hi = readAtom();
1408
+ if (hi === null || "set" in hi) return null;
1409
+ ranges.push([lo.cp, hi.cp]);
1344
1410
  } else {
1345
- ranges.push([lo, lo]);
1411
+ ranges.push([lo.cp, lo.cp]);
1346
1412
  }
1347
1413
  }
1348
1414
  return ranges.length ? ranges : null;
1349
1415
  }
1416
+ function classToRanges(cls) {
1417
+ if (cls === "\\d") return shorthandRanges("d");
1418
+ if (cls === "\\w") return shorthandRanges("w");
1419
+ const body = cls.slice(1, -1);
1420
+ if (body.startsWith("^")) return null;
1421
+ return parseClassRanges(body);
1422
+ }
1350
1423
  function literalCodePoints(frag) {
1351
1424
  const out = [];
1352
1425
  let i = 0;
@@ -1365,13 +1438,53 @@ function literalCodePoints(frag) {
1365
1438
  }
1366
1439
  return out.length ? out : null;
1367
1440
  }
1441
+ function parseStringShape(source) {
1442
+ const q = source[0];
1443
+ if (q === void 0 || q === "\\" || META.has(q)) return null;
1444
+ if (source.length < 3 || source[source.length - 1] !== q) return null;
1445
+ const inner = source.slice(1, -1);
1446
+ const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
1447
+ const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
1448
+ let body;
1449
+ let esc;
1450
+ let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
1451
+ if (m) {
1452
+ body = m[1];
1453
+ esc = m[2];
1454
+ } else {
1455
+ m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
1456
+ if (m) {
1457
+ esc = m[1];
1458
+ body = m[2];
1459
+ }
1460
+ }
1461
+ if (body === void 0 || esc === void 0) return null;
1462
+ const excluded = parseClassRanges(body);
1463
+ if (!excluded) return null;
1464
+ const qcp = q.codePointAt(0);
1465
+ const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
1466
+ if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
1467
+ return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
1468
+ }
1368
1469
  function parseScanShape(source) {
1369
1470
  let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
1370
1471
  if (m) {
1371
1472
  if (m[1].startsWith("^")) return null;
1372
1473
  const ranges = parseClassRanges(m[1]);
1373
- return ranges ? { kind: "chars", ranges } : null;
1474
+ return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
1475
+ }
1476
+ m = /^\\([dw])([+*])$/.exec(source);
1477
+ if (m) {
1478
+ return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
1374
1479
  }
1480
+ m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
1481
+ if (m) {
1482
+ const head = classToRanges(m[1]);
1483
+ const tail = classToRanges(m[2]);
1484
+ return head && tail ? { kind: "ident", head, tail } : null;
1485
+ }
1486
+ const str = parseStringShape(source);
1487
+ if (str) return str;
1375
1488
  m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
1376
1489
  if (m) {
1377
1490
  const open = literalCodePoints(m[1]);
@@ -1389,66 +1502,147 @@ function parseScanShape(source) {
1389
1502
  }
1390
1503
  return null;
1391
1504
  }
1505
+ function scanShapeFromRegex(source, flags) {
1506
+ if (/[imsu]/.test(flags)) return null;
1507
+ return parseScanShape(source);
1508
+ }
1392
1509
  var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
1393
1510
  var litCond = (base, cps, firstVar) => cps.map(
1394
1511
  (cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
1395
1512
  ).join(" && ");
1396
- function advanceExpr(shape) {
1513
+ var LINE_TERMINATORS = [10, 13, 8232, 8233];
1514
+ function stringHardStop(shape) {
1515
+ return shape.excluded.filter(
1516
+ ([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
1517
+ );
1518
+ }
1519
+ var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
1520
+ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
1397
1521
  if (shape.kind === "chars") {
1522
+ const cur = mint("_e");
1398
1523
  return {
1399
1524
  setup: [
1400
- ` _e++`,
1401
- ` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
1525
+ `${ind2}let ${cur} = ${start}`,
1526
+ `${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
1402
1527
  ],
1403
- endVar: "_e"
1528
+ // `*` always matches (possibly empty); `+` needs at least one char.
1529
+ ok: shape.minOne ? `${cur} > ${start}` : "true",
1530
+ end: cur
1531
+ };
1532
+ }
1533
+ if (shape.kind === "ident") {
1534
+ const cur = mint("_e");
1535
+ return {
1536
+ setup: [
1537
+ `${ind2}let ${cur} = ${start}`,
1538
+ `${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
1539
+ `${ind2} ${cur} = ${start} + 1`,
1540
+ `${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
1541
+ `${ind2}}`
1542
+ ],
1543
+ ok: `${cur} > ${start}`,
1544
+ end: cur
1404
1545
  };
1405
1546
  }
1406
1547
  if (shape.kind === "until") {
1548
+ const j2 = mint("_j");
1549
+ const openLen2 = shape.open.length;
1550
+ const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
1551
+ return {
1552
+ setup: [
1553
+ `${ind2}let ${j2} = ${start}`,
1554
+ `${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
1555
+ `${ind2} ${j2} = ${start} + ${openLen2}`,
1556
+ `${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
1557
+ `${ind2}}`
1558
+ ],
1559
+ // An open-until-terminator always completes (stop char or EOF), so a matched
1560
+ // open literal is a full match; `end === start` iff the open didn't match.
1561
+ ok: `${j2} > ${start}`,
1562
+ end: j2
1563
+ };
1564
+ }
1565
+ if (shape.kind === "string") {
1566
+ const okV = mint("_ok");
1567
+ const endV2 = mint("_end");
1568
+ const i = mint("_i");
1569
+ const c2 = mint("_c");
1570
+ const hard = stringHardStop(shape);
1571
+ const ltLines = shape.escLineTerm ? [] : (() => {
1572
+ const c3 = mint("_c");
1573
+ return [
1574
+ `${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
1575
+ `${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
1576
+ ];
1577
+ })();
1407
1578
  return {
1408
1579
  setup: [
1409
- ` let j = _e + ${shape.open.length}`,
1410
- ` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
1580
+ `${ind2}let ${okV} = false`,
1581
+ `${ind2}let ${endV2} = ${start}`,
1582
+ `${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
1583
+ `${ind2} let ${i} = ${start} + 1`,
1584
+ `${ind2} while (${i} < input.length) {`,
1585
+ `${ind2} const ${c2} = input.charCodeAt(${i})`,
1586
+ `${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
1587
+ `${ind2} if (${c2} === ${BACKSLASH}) {`,
1588
+ `${ind2} if (${i} + 1 >= input.length) break`,
1589
+ ...ltLines,
1590
+ `${ind2} ${i} += 2`,
1591
+ `${ind2} continue`,
1592
+ `${ind2} }`,
1593
+ ...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
1594
+ `${ind2} ${i}++`,
1595
+ `${ind2} }`,
1596
+ `${ind2}}`
1411
1597
  ],
1412
- endVar: "j"
1598
+ ok: okV,
1599
+ end: endV2
1413
1600
  };
1414
1601
  }
1415
- const n = shape.close.length;
1602
+ const j = mint("_j");
1603
+ const endV = mint("_end");
1604
+ const openLen = shape.open.length;
1605
+ const closeLen = shape.close.length;
1606
+ const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
1607
+ const closeChk = litCond(j, shape.close);
1416
1608
  return {
1417
1609
  setup: [
1418
- ` let j = _e + ${shape.open.length}`,
1419
- ` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
1610
+ `${ind2}let ${j} = ${start}`,
1611
+ `${ind2}let ${endV} = ${start}`,
1612
+ `${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
1613
+ `${ind2} ${j} = ${start} + ${openLen}`,
1614
+ `${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
1615
+ `${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
1616
+ `${ind2}}`
1420
1617
  ],
1421
- endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
1618
+ ok: `${endV} > ${start}`,
1619
+ end: endV
1422
1620
  };
1423
1621
  }
1424
- function guardLine(shape) {
1425
- const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
1426
- return ` if (${cond}) {`;
1427
- }
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");
1622
+ function scanBranch(shape, mint) {
1623
+ const m = emitShapeMatch(shape, "_e", mint, " ", "c");
1624
+ return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
1434
1625
  }
1435
- function captureChunk(startVar, kindIndex) {
1626
+ function scanBranchLabeled(shape, kindIndex, mint) {
1627
+ const m = emitShapeMatch(shape, "_e", mint, " ", "c");
1436
1628
  return [
1629
+ ...m.setup,
1630
+ ` if (${m.end} > _e) {`,
1437
1631
  ` 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");
1632
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
1633
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
1634
+ ` }`,
1635
+ ` _e = ${m.end}`,
1636
+ ` continue`,
1637
+ ` }`
1638
+ ].join("\n");
1449
1639
  }
1450
1640
 
1451
1641
  // src/compiler/trivia-fast-path.ts
1642
+ function makeMint() {
1643
+ let n = 0;
1644
+ return (prefix = "_v") => `${prefix}${n++}`;
1645
+ }
1452
1646
  function unwrapTrivia2(p) {
1453
1647
  let cur = p;
1454
1648
  while (cur._def.tag === "trivia") cur = cur._def.parser;
@@ -1465,14 +1659,14 @@ function choiceArms(p) {
1465
1659
  return null;
1466
1660
  }
1467
1661
  function armShape(arm) {
1468
- const src = getCoreRegexDef(arm)?.source;
1469
- return src ? parseScanShape(src) : null;
1662
+ const d = getCoreRegexDef(arm);
1663
+ return d ? scanShapeFromRegex(d.source, d.flags) : null;
1470
1664
  }
1471
1665
  function analyzeTriviaFastPath(trivia2) {
1472
1666
  const core = unwrapTrivia2(trivia2);
1473
- const directSrc = getCoreRegexDef(core)?.source;
1474
- if (directSrc) {
1475
- const shape = parseScanShape(directSrc);
1667
+ const directDef = getCoreRegexDef(core);
1668
+ if (directDef) {
1669
+ const shape = scanShapeFromRegex(directDef.source, directDef.flags);
1476
1670
  return shape && shape.kind === "chars" ? [shape] : null;
1477
1671
  }
1478
1672
  const inner = oneOrMoreInner(core);
@@ -1496,10 +1690,11 @@ var CAP_RECORD = [
1496
1690
  ` }`
1497
1691
  ].join("\n");
1498
1692
  function composeFastLoop(shapes) {
1693
+ const mint = makeMint();
1499
1694
  return [
1500
1695
  ` while (_e < input.length) {`,
1501
1696
  ` const c = input.charCodeAt(_e)`,
1502
- ...shapes.map(scanBranch),
1697
+ ...shapes.map((s) => scanBranch(s, mint)),
1503
1698
  ` break`,
1504
1699
  ` }`
1505
1700
  ].join("\n");
@@ -1519,20 +1714,21 @@ function analyzeLabeledScannableRun(trivia2) {
1519
1714
  if (!spec) return null;
1520
1715
  const out = [];
1521
1716
  for (const arm of spec.arms) {
1522
- const src = getCoreRegexDef(arm.parser)?.source;
1523
- const shape = src ? parseScanShape(src) : null;
1717
+ const d = getCoreRegexDef(arm.parser);
1718
+ const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
1524
1719
  if (!shape) return null;
1525
1720
  out.push({ shape, kindIndex: arm.kindIndex });
1526
1721
  }
1527
1722
  return out;
1528
1723
  }
1529
1724
  function buildLabeledScannableTriviaFnDecl(fnName, arms) {
1725
+ const mint = makeMint();
1530
1726
  return [
1531
1727
  `function ${fnName}(input, _pos, _ctx, _cap) {`,
1532
1728
  ` let _e = _pos`,
1533
1729
  ` while (_e < input.length) {`,
1534
1730
  ` const c = input.charCodeAt(_e)`,
1535
- ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
1731
+ ...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
1536
1732
  ` break`,
1537
1733
  ` }`,
1538
1734
  ` return _e`,
@@ -1609,6 +1805,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
1609
1805
  ].join("\n");
1610
1806
  }
1611
1807
 
1808
+ // src/compiler/scannable-terminal.ts
1809
+ function emitScannableTerminal(shape, c) {
1810
+ const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
1811
+ const stmts = [...m.setup];
1812
+ if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
1813
+ stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
1814
+ return { stmts, endVar: m.end };
1815
+ }
1816
+
1612
1817
  // src/compiler/inline-build.ts
1613
1818
  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
1819
  function escapeRegExp(s) {
@@ -1730,14 +1935,45 @@ function failReturn(expected, posExpr) {
1730
1935
  function failReturnArr(expectedArr, posExpr) {
1731
1936
  return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
1732
1937
  }
1938
+ function hoistExpected(ctx, constArrSource) {
1939
+ let name = ctx.expectedMap.get(constArrSource);
1940
+ if (name === void 0) {
1941
+ name = `_fx${ctx.expectedDecls.length}`;
1942
+ ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
1943
+ ctx.expectedMap.set(constArrSource, name);
1944
+ }
1945
+ return name;
1946
+ }
1733
1947
  function failBody(ctx, expected, posExpr) {
1734
- if (ctx.failLabel) return `break ${ctx.failLabel}`;
1948
+ if (ctx.failLabel) {
1949
+ if (!ctx.recordFail) return `break ${ctx.failLabel}`;
1950
+ return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
1951
+ }
1735
1952
  return failReturn(expected, posExpr);
1736
1953
  }
1737
- function failArrBody(ctx, expectedArr, posExpr) {
1738
- if (ctx.failLabel) return `break ${ctx.failLabel}`;
1954
+ function failArrBody(ctx, expectedArr, posExpr, constant = true) {
1955
+ if (ctx.failLabel) {
1956
+ if (!ctx.recordFail) return `break ${ctx.failLabel}`;
1957
+ const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
1958
+ return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
1959
+ }
1960
+ if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
1739
1961
  return failReturnArr(expectedArr, posExpr);
1740
1962
  }
1963
+ function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
1964
+ return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
1965
+ }
1966
+ function propagateFailBody(ctx, srcCtx = "_ctx") {
1967
+ if (srcCtx !== "_ctx") {
1968
+ if (ctx.failLabel) {
1969
+ if (!ctx.recordFail) return `break ${ctx.failLabel}`;
1970
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
1971
+ }
1972
+ return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
1973
+ }
1974
+ if (ctx.failLabel) return `break ${ctx.failLabel}`;
1975
+ return resultFromRecorded();
1976
+ }
1741
1977
  function emitIfFail(ctx, cond, body) {
1742
1978
  return [
1743
1979
  `${ind(ctx)}if (${cond}) {`,
@@ -1773,13 +2009,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
1773
2009
  `}`
1774
2010
  ].join("\n"));
1775
2011
  }
1776
- function emitNamedFnCall(ctx, fnName, pos, failExpected) {
2012
+ function emitNamedFnCall(ctx, fnName, pos) {
1777
2013
  const vv = v(ctx, "_pfv");
1778
2014
  const ev = v(ctx, "_pfe");
1779
2015
  return {
1780
2016
  stmts: [
1781
2017
  `${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
1782
- ...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, failBody(ctx, failExpected, pos)),
2018
+ // ref/lazy returns the inner failure verbatim — propagate the recorded
2019
+ // deepest failure (shares _ctx with the named fn), not a "parser" label.
2020
+ ...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
1783
2021
  `${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
1784
2022
  ],
1785
2023
  valueVar: vv,
@@ -1889,6 +2127,27 @@ function firstSetCond(codeVar, fs) {
1889
2127
  (r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
1890
2128
  ).join(" || ");
1891
2129
  }
2130
+ var SWITCH_RANGE_LIMIT = 4;
2131
+ var SWITCH_MAX_CASES = 48;
2132
+ var SWITCH_MIN_CASES = 3;
2133
+ var _forceDisjointIf = false;
2134
+ function planDisjointDispatch(parsers) {
2135
+ const cases = [];
2136
+ let total = 0;
2137
+ for (const p of parsers) {
2138
+ const fs = p._meta.firstSet;
2139
+ if (fs.kind !== "ranges") return { kind: "if" };
2140
+ const pts = [];
2141
+ for (const r of fs.ranges) {
2142
+ if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
2143
+ for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
2144
+ }
2145
+ total += pts.length;
2146
+ if (total > SWITCH_MAX_CASES) return { kind: "if" };
2147
+ cases.push(pts);
2148
+ }
2149
+ return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
2150
+ }
1892
2151
  function captureRestoreBody(mL, mR, mTl, mLg) {
1893
2152
  const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
1894
2153
  return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
@@ -2105,6 +2364,22 @@ function emitKeywords(def, ctx, pos) {
2105
2364
  return { stmts, valueVar: vv, endVar };
2106
2365
  }
2107
2366
  function emitRegex(def, ctx, pos) {
2367
+ const expectedStr = JSON.stringify(`/${def.source}/`);
2368
+ const shape = scanShapeFromRegex(def.source, def.flags);
2369
+ if (shape) {
2370
+ const vv2 = v(ctx);
2371
+ const scanned = emitScannableTerminal(shape, {
2372
+ ind: ind(ctx),
2373
+ pos,
2374
+ valueVar: vv2,
2375
+ failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
2376
+ fresh: (prefix) => v(ctx, prefix)
2377
+ });
2378
+ if (scanned) {
2379
+ const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
2380
+ return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
2381
+ }
2382
+ }
2108
2383
  const flags = "y" + def.flags.replace(/[gy]/g, "");
2109
2384
  const key = `${def.optimizedSource}/${flags}`;
2110
2385
  let rName = ctx.regexMap.get(key);
@@ -2115,7 +2390,6 @@ function emitRegex(def, ctx, pos) {
2115
2390
  }
2116
2391
  const mv = v(ctx, "_m");
2117
2392
  const vv = v(ctx);
2118
- const expectedStr = JSON.stringify(`/${def.source}/`);
2119
2393
  const stmts = [
2120
2394
  `${ind(ctx)}${rName}.lastIndex = ${pos}`,
2121
2395
  `${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
@@ -2174,15 +2448,43 @@ function emitSeq(def, ctx, pos) {
2174
2448
  stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
2175
2449
  return { stmts, valueVar: arrV, endVar };
2176
2450
  }
2451
+ function staticExpectedArr(parsers) {
2452
+ return JSON.stringify(parsers.flatMap((p) => {
2453
+ const e = staticExpected(p);
2454
+ return e.length > 0 ? e : [p._tag];
2455
+ }));
2456
+ }
2457
+ function failsAtStart(p) {
2458
+ const d = p._def;
2459
+ switch (d.tag) {
2460
+ case "literal":
2461
+ case "regex":
2462
+ case "keywords":
2463
+ case "guard":
2464
+ case "not":
2465
+ return true;
2466
+ case "transform":
2467
+ case "label":
2468
+ return failsAtStart(d.parser);
2469
+ default:
2470
+ return false;
2471
+ }
2472
+ }
2473
+ function emitDisjointArm(p, ctx, pos, valV, endV) {
2474
+ if (failsAtStart(p)) {
2475
+ const r = emit(p, ctx, pos);
2476
+ return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
2477
+ }
2478
+ const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
2479
+ return [
2480
+ ...stmts,
2481
+ ...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
2482
+ `${ind(ctx)}${valV} = ${valVar}`,
2483
+ `${ind(ctx)}${endV} = ${endVar}`
2484
+ ];
2485
+ }
2177
2486
  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
- );
2487
+ const allExpected = staticExpectedArr(def.parsers);
2186
2488
  if (def.disjoint) {
2187
2489
  const codeV = v(ctx, "_code");
2188
2490
  const valV = v(ctx, "_chv");
@@ -2191,6 +2493,27 @@ function emitChoice(def, ctx, pos) {
2191
2493
  `${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
2192
2494
  `${ind(ctx)}let ${valV}, ${endV} = ${pos}`
2193
2495
  ];
2496
+ const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
2497
+ if (plan.kind === "switch") {
2498
+ stmts.push(`${ind(ctx)}switch (${codeV}) {`);
2499
+ ctx.indent++;
2500
+ for (let i = 0; i < def.parsers.length; i++) {
2501
+ const p = def.parsers[i];
2502
+ for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
2503
+ stmts.push(`${ind(ctx)}{`);
2504
+ ctx.indent++;
2505
+ stmts.push(
2506
+ ...emitDisjointArm(p, ctx, pos, valV, endV),
2507
+ `${ind(ctx)}break`
2508
+ );
2509
+ ctx.indent--;
2510
+ stmts.push(`${ind(ctx)}}`);
2511
+ }
2512
+ stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
2513
+ ctx.indent--;
2514
+ stmts.push(`${ind(ctx)}}`);
2515
+ return { stmts, valueVar: valV, endVar: endV };
2516
+ }
2194
2517
  let first = true;
2195
2518
  for (const p of def.parsers) {
2196
2519
  const cond = firstSetCond(codeV, p._meta.firstSet);
@@ -2198,10 +2521,7 @@ function emitChoice(def, ctx, pos) {
2198
2521
  first = false;
2199
2522
  stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
2200
2523
  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}`);
2524
+ stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
2205
2525
  ctx.indent--;
2206
2526
  stmts.push(`${ind(ctx)}}`);
2207
2527
  }
@@ -2210,7 +2530,7 @@ function emitChoice(def, ctx, pos) {
2210
2530
  }
2211
2531
  return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
2212
2532
  }
2213
- function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
2533
+ function emitGreedyClassify(def, superIndex, ctx, pos) {
2214
2534
  const superParser = def.parsers[superIndex];
2215
2535
  const regexDef = getCoreRegexDef(superParser);
2216
2536
  const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
@@ -2225,10 +2545,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
2225
2545
  const wordV = v(ctx, "_gw");
2226
2546
  const endV = v(ctx, "_ge");
2227
2547
  const valV = v(ctx, "_gcv");
2548
+ const regexExpected = JSON.stringify(staticExpected(superParser));
2228
2549
  const stmts = [
2229
2550
  `${ind(ctx)}${reVar}.lastIndex = ${pos}`,
2230
2551
  `${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
2231
- ...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, allExpected, pos)),
2552
+ ...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
2232
2553
  `${ind(ctx)}const ${wordV} = ${matchV}[0]`,
2233
2554
  `${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
2234
2555
  `${ind(ctx)}let ${valV}`
@@ -2286,13 +2607,15 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
2286
2607
  stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
2287
2608
  return { stmts, valueVar: valV, endVar: endV };
2288
2609
  }
2289
- function emitFirstMatch(def, allExpected, ctx, pos) {
2610
+ function emitFirstMatch(def, ctx, pos) {
2290
2611
  const resValV = v(ctx, "_crv");
2291
2612
  const resEndV = v(ctx, "_cre");
2292
2613
  const resOkV = v(ctx, "_crok");
2614
+ const slots = def.parsers.map(() => v(ctx, "_cfx"));
2293
2615
  const ind0 = ind(ctx);
2294
2616
  const stmts = [
2295
- `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`
2617
+ `${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
2618
+ `${ind0}let ${slots.join(", ")}`
2296
2619
  ];
2297
2620
  for (let i = 0; i < def.parsers.length; i++) {
2298
2621
  const p = def.parsers[i];
@@ -2339,22 +2662,24 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
2339
2662
  stmts.push(`${ind0} ${resOkV} = true`);
2340
2663
  stmts.push(`${ind0} }`);
2341
2664
  stmts.push(`${ind0} }`);
2665
+ stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx }`);
2342
2666
  if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
2343
2667
  } else {
2344
2668
  stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
2345
- if (rollback) stmts.push(`${ind0} else { ${rollback} }`);
2669
+ stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
2346
2670
  }
2347
2671
  stmts.push(`${ind0}}`);
2348
2672
  }
2349
- stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, allExpected, pos)));
2673
+ const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
2674
+ stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
2350
2675
  return { stmts, valueVar: resValV, endVar: resEndV };
2351
2676
  }
2352
2677
  function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
2353
2678
  if (strategy.tag === "greedyClassify")
2354
- return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
2679
+ return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
2355
2680
  if (strategy.tag === "literalsLongestFirst")
2356
2681
  return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
2357
- return emitFirstMatch(def, allExpected, ctx, pos);
2682
+ return emitFirstMatch(def, ctx, pos);
2358
2683
  }
2359
2684
  function emitTransformChain(p, baseValue, endV, startPos, ctx) {
2360
2685
  const def = p._def;
@@ -2582,10 +2907,11 @@ function emitScanTo(def, ctx, pos) {
2582
2907
  }
2583
2908
  function emitNot(def, ctx, pos) {
2584
2909
  const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
2910
+ const label2 = JSON.stringify(`not(${def.parser._tag})`);
2585
2911
  return {
2586
2912
  stmts: [
2587
2913
  ...stmts,
2588
- ...emitIfFail(ctx, okVar, failBody(ctx, '"not"', pos))
2914
+ ...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
2589
2915
  ],
2590
2916
  valueVar: "null",
2591
2917
  endVar: pos
@@ -2617,7 +2943,7 @@ function emitNode(def, ctx, pos) {
2617
2943
  const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
2618
2944
  stmts.push(...innerStmts);
2619
2945
  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)));
2946
+ stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
2621
2947
  let stV = "undefined";
2622
2948
  if (clonesState) {
2623
2949
  stV = v(ctx, "_nst");
@@ -2639,9 +2965,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
2639
2965
  const rv = v(ctx, "_rt");
2640
2966
  const vv = v(ctx, "_rtv");
2641
2967
  const ev = v(ctx, "_rte");
2968
+ 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
2969
  const stmts = [
2643
2970
  `${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
2644
- ...emitIfFail(ctx, `!${rv}.ok`, failBody(ctx, '"runtime"', pos)),
2971
+ ...emitIfFail(ctx, `!${rv}.ok`, failStmt),
2645
2972
  `${ind(ctx)}const ${vv} = ${rv}.value`,
2646
2973
  `${ind(ctx)}const ${ev} = ${rv}.span.end`
2647
2974
  ];
@@ -2678,7 +3005,7 @@ function emitLazy(p, def, ctx, pos) {
2678
3005
  pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
2679
3006
  }
2680
3007
  const fnName = ctx.namedParsers.get(p);
2681
- return emitNamedFnCall(ctx, fnName, pos, '"parser"');
3008
+ return emitNamedFnCall(ctx, fnName, pos);
2682
3009
  }
2683
3010
  function emitRecover(def, ctx, pos) {
2684
3011
  const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
@@ -2696,7 +3023,7 @@ function emitRecover(def, ctx, pos) {
2696
3023
  `${ind0} if (${sentOk}) break`,
2697
3024
  `${ind0} ${scanV}++`,
2698
3025
  `${ind0} }`,
2699
- `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: [] }`,
3026
+ `${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
2700
3027
  `${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
2701
3028
  `${ind0} ${valVar} = ${errV}`,
2702
3029
  `${ind0} ${endVar} = ${scanV}`,
@@ -2869,7 +3196,9 @@ function emit(p, ctx, pos) {
2869
3196
  stmts: [
2870
3197
  `${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
2871
3198
  `${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
2872
- ...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, failBody(ctx, '"withCtx"', pos)),
3199
+ // withCtx runs on a spread child ctx — copy its recorded failure back
3200
+ // and propagate the inner failure verbatim (interpreter parity).
3201
+ ...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
2873
3202
  `${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
2874
3203
  ],
2875
3204
  valueVar: vv,
@@ -2970,7 +3299,7 @@ function analyzeLazyUsageMulti(roots) {
2970
3299
  const recursive = /* @__PURE__ */ new Set();
2971
3300
  const descended = /* @__PURE__ */ new Set();
2972
3301
  const active = /* @__PURE__ */ new Set();
2973
- function walk(p) {
3302
+ function walk2(p) {
2974
3303
  const def = p._def;
2975
3304
  if (def.tag === "lazy") {
2976
3305
  counts.set(p, (counts.get(p) ?? 0) + 1);
@@ -2987,13 +3316,13 @@ function analyzeLazyUsageMulti(roots) {
2987
3316
  return;
2988
3317
  }
2989
3318
  active.add(p);
2990
- walk(resolved);
3319
+ walk2(resolved);
2991
3320
  active.delete(p);
2992
3321
  return;
2993
3322
  }
2994
- for (const child of childrenOf(def)) walk(child);
3323
+ for (const child of childrenOf(def)) walk2(child);
2995
3324
  }
2996
- for (const root of roots) walk(root);
3325
+ for (const root of roots) walk2(root);
2997
3326
  return { counts, recursive };
2998
3327
  }
2999
3328
  function compile(combinator, mapFnSources) {
@@ -3002,6 +3331,8 @@ function compile(combinator, mapFnSources) {
3002
3331
  indent: 1,
3003
3332
  regexDecls: [],
3004
3333
  regexMap: /* @__PURE__ */ new Map(),
3334
+ expectedDecls: [],
3335
+ expectedMap: /* @__PURE__ */ new Map(),
3005
3336
  mapFns: [],
3006
3337
  mapFnSrcs: [],
3007
3338
  buildFns: [],
@@ -3023,6 +3354,7 @@ function compile(combinator, mapFnSources) {
3023
3354
  const source = [
3024
3355
  ...emptyTlDecls,
3025
3356
  ...ctx.regexDecls,
3357
+ ...ctx.expectedDecls,
3026
3358
  "",
3027
3359
  ...namedPrelude,
3028
3360
  ctx.namedFnDecls.join("\n\n"),
@@ -3035,6 +3367,7 @@ function compile(combinator, mapFnSources) {
3035
3367
  const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
3036
3368
  ...emptyTlDecls,
3037
3369
  ...ctx.regexDecls,
3370
+ ...ctx.expectedDecls,
3038
3371
  collatorDecl,
3039
3372
  ...namedPrelude,
3040
3373
  ...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
@@ -3080,6 +3413,8 @@ function compileRuleMap(ruleMap) {
3080
3413
  indent: 1,
3081
3414
  regexDecls: [],
3082
3415
  regexMap: /* @__PURE__ */ new Map(),
3416
+ expectedDecls: [],
3417
+ expectedMap: /* @__PURE__ */ new Map(),
3083
3418
  mapFns: [],
3084
3419
  mapFnSrcs: [],
3085
3420
  buildFns: [],
@@ -3108,6 +3443,7 @@ function compileRuleMap(ruleMap) {
3108
3443
  const hoistedDecls = [
3109
3444
  ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
3110
3445
  ...ctx.regexDecls.map((d) => ` ${d}`),
3446
+ ...ctx.expectedDecls.map((d) => ` ${d}`),
3111
3447
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
3112
3448
  mfDecl,
3113
3449
  buildDecl,
@@ -3149,12 +3485,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
3149
3485
  const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
3150
3486
  const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
3151
3487
  const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
3152
- const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
3488
+ const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
3153
3489
  if (!needsWrapper) return innerFn;
3154
3490
  const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
3155
3491
  const hoistedDecls = [
3156
3492
  emptyTlDecl,
3157
3493
  ...ctx.regexDecls.map((d) => ` ${d}`),
3494
+ ...ctx.expectedDecls.map((d) => ` ${d}`),
3158
3495
  collatorDecl ? ` ${collatorDecl.trim()}` : "",
3159
3496
  mfDecl,
3160
3497
  buildDecl,
@@ -3172,58 +3509,6 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
3172
3509
  ].join("\n");
3173
3510
  }
3174
3511
 
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
3512
  // src/combinators/scanTo.ts
3228
3513
  function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
3229
3514
  const meta = {
@@ -3605,6 +3890,13 @@ function evaluateWordFactory(node2, scope, code) {
3605
3890
  function evaluateExpr(node2, scope, code, mapFnSources) {
3606
3891
  return exprToCombi(node2, scope, code, mapFnSources);
3607
3892
  }
3893
+ function evaluateCombinatorArray(node2, scope, code) {
3894
+ if (node2.type !== "ArrayExpression") return null;
3895
+ const val = anyValue(node2, scope, code, []);
3896
+ if (!Array.isArray(val) || val.length === 0) return null;
3897
+ if (!val.every(isCombinator)) return null;
3898
+ return val;
3899
+ }
3608
3900
  function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
3609
3901
  if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
3610
3902
  const factory = factoryNode;
@@ -3908,6 +4200,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
3908
4200
  scope.set(varName, wordFactory);
3909
4201
  continue;
3910
4202
  }
4203
+ const combiArray = evaluateCombinatorArray(init, scope, code);
4204
+ if (combiArray) {
4205
+ ;
4206
+ scope.set(varName, combiArray);
4207
+ continue;
4208
+ }
3911
4209
  warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
3912
4210
  continue;
3913
4211
  }