parseman 0.10.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.
- package/LICENSE +21 -0
- package/README.md +9 -9
- package/dist/combinators/recover-list.d.ts +41 -0
- package/dist/combinators/recover-list.d.ts.map +1 -0
- package/dist/combinators/recover.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +1 -0
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/scannable-run.d.ts +60 -8
- package/dist/compiler/scannable-run.d.ts.map +1 -1
- package/dist/compiler/scannable-terminal.d.ts +23 -0
- package/dist/compiler/scannable-terminal.d.ts.map +1 -0
- package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/cst/walk.d.ts +72 -0
- package/dist/cst/walk.d.ts.map +1 -0
- package/dist/index.cjs +484 -145
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +484 -145
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts +8 -0
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +442 -144
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +442 -144
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -1
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
choice: () => choice,
|
|
38
38
|
compile: () => compile,
|
|
39
39
|
completionsAt: () => completionsAt,
|
|
40
|
+
createVisitor: () => createVisitor,
|
|
40
41
|
expect: () => expect,
|
|
41
42
|
guard: () => guard,
|
|
42
43
|
isParseError: () => isParseError,
|
|
@@ -46,6 +47,7 @@ __export(index_exports, {
|
|
|
46
47
|
makeFunctionalDoc: () => makeFunctionalDoc,
|
|
47
48
|
makeWord: () => makeWord,
|
|
48
49
|
many: () => many,
|
|
50
|
+
manyRecover: () => manyRecover,
|
|
49
51
|
noTrivia: () => noTrivia,
|
|
50
52
|
node: () => node,
|
|
51
53
|
not: () => not,
|
|
@@ -60,12 +62,14 @@ __export(index_exports, {
|
|
|
60
62
|
rules: () => rules,
|
|
61
63
|
scanTo: () => scanTo,
|
|
62
64
|
sepBy: () => sepBy,
|
|
65
|
+
sepByRecover: () => sepByRecover,
|
|
63
66
|
sequence: () => sequence,
|
|
64
67
|
skip: () => skip,
|
|
65
68
|
staticExpected: () => staticExpected,
|
|
66
69
|
transform: () => transform,
|
|
67
70
|
trivia: () => trivia,
|
|
68
71
|
triviaEntries: () => triviaEntries,
|
|
72
|
+
walk: () => walk,
|
|
69
73
|
withCtx: () => withCtx,
|
|
70
74
|
word: () => word
|
|
71
75
|
});
|
|
@@ -819,12 +823,15 @@ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
|
|
|
819
823
|
continue;
|
|
820
824
|
}
|
|
821
825
|
if (c === 47 && input.charCodeAt(pos + 1) === 42) {
|
|
822
|
-
const start = pos;
|
|
823
826
|
let j = pos + 2;
|
|
824
827
|
while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
+
if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
|
|
829
|
+
const start = pos;
|
|
830
|
+
pos = j + 2;
|
|
831
|
+
chunks.push({ start, end: pos, kindIndex: commentKind });
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
break;
|
|
828
835
|
}
|
|
829
836
|
break;
|
|
830
837
|
}
|
|
@@ -1413,38 +1420,108 @@ function parse(combinator, input, opts = {}) {
|
|
|
1413
1420
|
return withErrors;
|
|
1414
1421
|
}
|
|
1415
1422
|
|
|
1423
|
+
// src/combinators/expect.ts
|
|
1424
|
+
function staticExpected(c) {
|
|
1425
|
+
const def = c._def;
|
|
1426
|
+
switch (def.tag) {
|
|
1427
|
+
case "literal":
|
|
1428
|
+
return [JSON.stringify(def.value)];
|
|
1429
|
+
case "regex":
|
|
1430
|
+
return [`/${def.source}/`];
|
|
1431
|
+
case "keywords":
|
|
1432
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
1433
|
+
case "label":
|
|
1434
|
+
return [def.label];
|
|
1435
|
+
case "choice":
|
|
1436
|
+
return def.parsers.flatMap(staticExpected);
|
|
1437
|
+
case "sequence":
|
|
1438
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
1439
|
+
case "node":
|
|
1440
|
+
case "grammar":
|
|
1441
|
+
case "trivia":
|
|
1442
|
+
case "optional":
|
|
1443
|
+
case "many":
|
|
1444
|
+
case "oneOrMore":
|
|
1445
|
+
case "transform":
|
|
1446
|
+
case "not":
|
|
1447
|
+
return staticExpected(def.parser);
|
|
1448
|
+
case "lazy":
|
|
1449
|
+
return staticExpected(def.thunk());
|
|
1450
|
+
default:
|
|
1451
|
+
return [];
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
function expect(combinator, label2) {
|
|
1455
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
1456
|
+
const meta = {
|
|
1457
|
+
firstSet: combinator._meta.firstSet,
|
|
1458
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1459
|
+
isTrivia: false
|
|
1460
|
+
};
|
|
1461
|
+
return {
|
|
1462
|
+
_tag: "expect",
|
|
1463
|
+
_meta: meta,
|
|
1464
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
1465
|
+
parse(input, pos, ctx) {
|
|
1466
|
+
const result = combinator.parse(input, pos, ctx);
|
|
1467
|
+
if (result.ok) return result;
|
|
1468
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
1469
|
+
ctx._errors?.push(error);
|
|
1470
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1416
1475
|
// src/compiler/scannable-run.ts
|
|
1476
|
+
var BACKSLASH = 92;
|
|
1417
1477
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1418
1478
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1479
|
+
function shorthandRanges(ch) {
|
|
1480
|
+
return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1481
|
+
}
|
|
1419
1482
|
function parseClassRanges(body) {
|
|
1420
1483
|
const ranges = [];
|
|
1421
1484
|
let i = 0;
|
|
1422
|
-
const
|
|
1485
|
+
const readAtom = () => {
|
|
1423
1486
|
const ch = body[i];
|
|
1424
1487
|
if (ch === void 0) return null;
|
|
1425
1488
|
if (ch === "\\") {
|
|
1426
1489
|
const e = body[i + 1];
|
|
1427
1490
|
if (e === void 0) return null;
|
|
1428
1491
|
i += 2;
|
|
1429
|
-
|
|
1492
|
+
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1493
|
+
if (e === "d" || e === "w") return { set: shorthandRanges(e) };
|
|
1494
|
+
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1495
|
+
return { cp: e.codePointAt(0) };
|
|
1430
1496
|
}
|
|
1431
1497
|
i += ch.length;
|
|
1432
|
-
return ch.codePointAt(0);
|
|
1498
|
+
return { cp: ch.codePointAt(0) };
|
|
1433
1499
|
};
|
|
1434
1500
|
while (i < body.length) {
|
|
1435
|
-
const lo =
|
|
1501
|
+
const lo = readAtom();
|
|
1436
1502
|
if (lo === null) return null;
|
|
1503
|
+
if ("set" in lo) {
|
|
1504
|
+
ranges.push(...lo.set);
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1437
1507
|
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1438
1508
|
i += 1;
|
|
1439
|
-
const hi =
|
|
1440
|
-
if (hi === null) return null;
|
|
1441
|
-
ranges.push([lo, hi]);
|
|
1509
|
+
const hi = readAtom();
|
|
1510
|
+
if (hi === null || "set" in hi) return null;
|
|
1511
|
+
ranges.push([lo.cp, hi.cp]);
|
|
1442
1512
|
} else {
|
|
1443
|
-
ranges.push([lo, lo]);
|
|
1513
|
+
ranges.push([lo.cp, lo.cp]);
|
|
1444
1514
|
}
|
|
1445
1515
|
}
|
|
1446
1516
|
return ranges.length ? ranges : null;
|
|
1447
1517
|
}
|
|
1518
|
+
function classToRanges(cls) {
|
|
1519
|
+
if (cls === "\\d") return shorthandRanges("d");
|
|
1520
|
+
if (cls === "\\w") return shorthandRanges("w");
|
|
1521
|
+
const body = cls.slice(1, -1);
|
|
1522
|
+
if (body.startsWith("^")) return null;
|
|
1523
|
+
return parseClassRanges(body);
|
|
1524
|
+
}
|
|
1448
1525
|
function literalCodePoints(frag) {
|
|
1449
1526
|
const out = [];
|
|
1450
1527
|
let i = 0;
|
|
@@ -1463,13 +1540,53 @@ function literalCodePoints(frag) {
|
|
|
1463
1540
|
}
|
|
1464
1541
|
return out.length ? out : null;
|
|
1465
1542
|
}
|
|
1543
|
+
function parseStringShape(source) {
|
|
1544
|
+
const q = source[0];
|
|
1545
|
+
if (q === void 0 || q === "\\" || META.has(q)) return null;
|
|
1546
|
+
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
1547
|
+
const inner = source.slice(1, -1);
|
|
1548
|
+
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
1549
|
+
const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
1550
|
+
let body;
|
|
1551
|
+
let esc;
|
|
1552
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
|
|
1553
|
+
if (m) {
|
|
1554
|
+
body = m[1];
|
|
1555
|
+
esc = m[2];
|
|
1556
|
+
} else {
|
|
1557
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
|
|
1558
|
+
if (m) {
|
|
1559
|
+
esc = m[1];
|
|
1560
|
+
body = m[2];
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
if (body === void 0 || esc === void 0) return null;
|
|
1564
|
+
const excluded = parseClassRanges(body);
|
|
1565
|
+
if (!excluded) return null;
|
|
1566
|
+
const qcp = q.codePointAt(0);
|
|
1567
|
+
const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
|
|
1568
|
+
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1569
|
+
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1570
|
+
}
|
|
1466
1571
|
function parseScanShape(source) {
|
|
1467
1572
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1468
1573
|
if (m) {
|
|
1469
1574
|
if (m[1].startsWith("^")) return null;
|
|
1470
1575
|
const ranges = parseClassRanges(m[1]);
|
|
1471
|
-
return ranges ? { kind: "chars", ranges } : null;
|
|
1576
|
+
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1577
|
+
}
|
|
1578
|
+
m = /^\\([dw])([+*])$/.exec(source);
|
|
1579
|
+
if (m) {
|
|
1580
|
+
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1581
|
+
}
|
|
1582
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
|
|
1583
|
+
if (m) {
|
|
1584
|
+
const head = classToRanges(m[1]);
|
|
1585
|
+
const tail = classToRanges(m[2]);
|
|
1586
|
+
return head && tail ? { kind: "ident", head, tail } : null;
|
|
1472
1587
|
}
|
|
1588
|
+
const str = parseStringShape(source);
|
|
1589
|
+
if (str) return str;
|
|
1473
1590
|
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1474
1591
|
if (m) {
|
|
1475
1592
|
const open = literalCodePoints(m[1]);
|
|
@@ -1487,66 +1604,147 @@ function parseScanShape(source) {
|
|
|
1487
1604
|
}
|
|
1488
1605
|
return null;
|
|
1489
1606
|
}
|
|
1607
|
+
function scanShapeFromRegex(source, flags) {
|
|
1608
|
+
if (/[imsu]/.test(flags)) return null;
|
|
1609
|
+
return parseScanShape(source);
|
|
1610
|
+
}
|
|
1490
1611
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1491
1612
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
1492
1613
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1493
1614
|
).join(" && ");
|
|
1494
|
-
|
|
1615
|
+
var LINE_TERMINATORS = [10, 13, 8232, 8233];
|
|
1616
|
+
function stringHardStop(shape) {
|
|
1617
|
+
return shape.excluded.filter(
|
|
1618
|
+
([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1621
|
+
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1622
|
+
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
1495
1623
|
if (shape.kind === "chars") {
|
|
1624
|
+
const cur = mint("_e");
|
|
1625
|
+
return {
|
|
1626
|
+
setup: [
|
|
1627
|
+
`${ind2}let ${cur} = ${start}`,
|
|
1628
|
+
`${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
|
|
1629
|
+
],
|
|
1630
|
+
// `*` always matches (possibly empty); `+` needs at least one char.
|
|
1631
|
+
ok: shape.minOne ? `${cur} > ${start}` : "true",
|
|
1632
|
+
end: cur
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
if (shape.kind === "ident") {
|
|
1636
|
+
const cur = mint("_e");
|
|
1496
1637
|
return {
|
|
1497
1638
|
setup: [
|
|
1498
|
-
|
|
1499
|
-
|
|
1639
|
+
`${ind2}let ${cur} = ${start}`,
|
|
1640
|
+
`${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
|
|
1641
|
+
`${ind2} ${cur} = ${start} + 1`,
|
|
1642
|
+
`${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
|
|
1643
|
+
`${ind2}}`
|
|
1500
1644
|
],
|
|
1501
|
-
|
|
1645
|
+
ok: `${cur} > ${start}`,
|
|
1646
|
+
end: cur
|
|
1502
1647
|
};
|
|
1503
1648
|
}
|
|
1504
1649
|
if (shape.kind === "until") {
|
|
1650
|
+
const j2 = mint("_j");
|
|
1651
|
+
const openLen2 = shape.open.length;
|
|
1652
|
+
const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
1653
|
+
return {
|
|
1654
|
+
setup: [
|
|
1655
|
+
`${ind2}let ${j2} = ${start}`,
|
|
1656
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
|
|
1657
|
+
`${ind2} ${j2} = ${start} + ${openLen2}`,
|
|
1658
|
+
`${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
|
|
1659
|
+
`${ind2}}`
|
|
1660
|
+
],
|
|
1661
|
+
// An open-until-terminator always completes (stop char or EOF), so a matched
|
|
1662
|
+
// open literal is a full match; `end === start` iff the open didn't match.
|
|
1663
|
+
ok: `${j2} > ${start}`,
|
|
1664
|
+
end: j2
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
if (shape.kind === "string") {
|
|
1668
|
+
const okV = mint("_ok");
|
|
1669
|
+
const endV2 = mint("_end");
|
|
1670
|
+
const i = mint("_i");
|
|
1671
|
+
const c2 = mint("_c");
|
|
1672
|
+
const hard = stringHardStop(shape);
|
|
1673
|
+
const ltLines = shape.escLineTerm ? [] : (() => {
|
|
1674
|
+
const c3 = mint("_c");
|
|
1675
|
+
return [
|
|
1676
|
+
`${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
|
|
1677
|
+
`${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
|
|
1678
|
+
];
|
|
1679
|
+
})();
|
|
1505
1680
|
return {
|
|
1506
1681
|
setup: [
|
|
1507
|
-
|
|
1508
|
-
|
|
1682
|
+
`${ind2}let ${okV} = false`,
|
|
1683
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
1684
|
+
`${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
|
|
1685
|
+
`${ind2} let ${i} = ${start} + 1`,
|
|
1686
|
+
`${ind2} while (${i} < input.length) {`,
|
|
1687
|
+
`${ind2} const ${c2} = input.charCodeAt(${i})`,
|
|
1688
|
+
`${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
|
|
1689
|
+
`${ind2} if (${c2} === ${BACKSLASH}) {`,
|
|
1690
|
+
`${ind2} if (${i} + 1 >= input.length) break`,
|
|
1691
|
+
...ltLines,
|
|
1692
|
+
`${ind2} ${i} += 2`,
|
|
1693
|
+
`${ind2} continue`,
|
|
1694
|
+
`${ind2} }`,
|
|
1695
|
+
...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
|
|
1696
|
+
`${ind2} ${i}++`,
|
|
1697
|
+
`${ind2} }`,
|
|
1698
|
+
`${ind2}}`
|
|
1509
1699
|
],
|
|
1510
|
-
|
|
1700
|
+
ok: okV,
|
|
1701
|
+
end: endV2
|
|
1511
1702
|
};
|
|
1512
1703
|
}
|
|
1513
|
-
const
|
|
1704
|
+
const j = mint("_j");
|
|
1705
|
+
const endV = mint("_end");
|
|
1706
|
+
const openLen = shape.open.length;
|
|
1707
|
+
const closeLen = shape.close.length;
|
|
1708
|
+
const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
1709
|
+
const closeChk = litCond(j, shape.close);
|
|
1514
1710
|
return {
|
|
1515
1711
|
setup: [
|
|
1516
|
-
|
|
1517
|
-
|
|
1712
|
+
`${ind2}let ${j} = ${start}`,
|
|
1713
|
+
`${ind2}let ${endV} = ${start}`,
|
|
1714
|
+
`${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
|
|
1715
|
+
`${ind2} ${j} = ${start} + ${openLen}`,
|
|
1716
|
+
`${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
|
|
1717
|
+
`${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
|
|
1718
|
+
`${ind2}}`
|
|
1518
1719
|
],
|
|
1519
|
-
|
|
1720
|
+
ok: `${endV} > ${start}`,
|
|
1721
|
+
end: endV
|
|
1520
1722
|
};
|
|
1521
1723
|
}
|
|
1522
|
-
function
|
|
1523
|
-
const
|
|
1524
|
-
return ` if (${
|
|
1724
|
+
function scanBranch(shape, mint) {
|
|
1725
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1726
|
+
return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
|
|
1525
1727
|
}
|
|
1526
|
-
function
|
|
1527
|
-
|
|
1528
|
-
return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
|
|
1529
|
-
}
|
|
1530
|
-
const { setup, endVar } = advanceExpr(shape);
|
|
1531
|
-
return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
|
|
1532
|
-
}
|
|
1533
|
-
function captureChunk(startVar, kindIndex) {
|
|
1728
|
+
function scanBranchLabeled(shape, kindIndex, mint) {
|
|
1729
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1534
1730
|
return [
|
|
1731
|
+
...m.setup,
|
|
1732
|
+
` if (${m.end} > _e) {`,
|
|
1535
1733
|
` if (_cap) {`,
|
|
1536
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${
|
|
1537
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${
|
|
1538
|
-
` }
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
|
|
1544
|
-
if (endVar !== "_e") lines.push(` _e = ${endVar}`);
|
|
1545
|
-
lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
|
|
1546
|
-
return lines.join("\n");
|
|
1734
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
|
|
1735
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
1736
|
+
` }`,
|
|
1737
|
+
` _e = ${m.end}`,
|
|
1738
|
+
` continue`,
|
|
1739
|
+
` }`
|
|
1740
|
+
].join("\n");
|
|
1547
1741
|
}
|
|
1548
1742
|
|
|
1549
1743
|
// src/compiler/trivia-fast-path.ts
|
|
1744
|
+
function makeMint() {
|
|
1745
|
+
let n = 0;
|
|
1746
|
+
return (prefix = "_v") => `${prefix}${n++}`;
|
|
1747
|
+
}
|
|
1550
1748
|
function unwrapTrivia2(p) {
|
|
1551
1749
|
let cur = p;
|
|
1552
1750
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1563,14 +1761,14 @@ function choiceArms(p) {
|
|
|
1563
1761
|
return null;
|
|
1564
1762
|
}
|
|
1565
1763
|
function armShape(arm) {
|
|
1566
|
-
const
|
|
1567
|
-
return
|
|
1764
|
+
const d = getCoreRegexDef(arm);
|
|
1765
|
+
return d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1568
1766
|
}
|
|
1569
1767
|
function analyzeTriviaFastPath(trivia2) {
|
|
1570
1768
|
const core = unwrapTrivia2(trivia2);
|
|
1571
|
-
const
|
|
1572
|
-
if (
|
|
1573
|
-
const shape =
|
|
1769
|
+
const directDef = getCoreRegexDef(core);
|
|
1770
|
+
if (directDef) {
|
|
1771
|
+
const shape = scanShapeFromRegex(directDef.source, directDef.flags);
|
|
1574
1772
|
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1575
1773
|
}
|
|
1576
1774
|
const inner = oneOrMoreInner(core);
|
|
@@ -1594,10 +1792,11 @@ var CAP_RECORD = [
|
|
|
1594
1792
|
` }`
|
|
1595
1793
|
].join("\n");
|
|
1596
1794
|
function composeFastLoop(shapes) {
|
|
1795
|
+
const mint = makeMint();
|
|
1597
1796
|
return [
|
|
1598
1797
|
` while (_e < input.length) {`,
|
|
1599
1798
|
` const c = input.charCodeAt(_e)`,
|
|
1600
|
-
...shapes.map(scanBranch),
|
|
1799
|
+
...shapes.map((s) => scanBranch(s, mint)),
|
|
1601
1800
|
` break`,
|
|
1602
1801
|
` }`
|
|
1603
1802
|
].join("\n");
|
|
@@ -1617,20 +1816,21 @@ function analyzeLabeledScannableRun(trivia2) {
|
|
|
1617
1816
|
if (!spec) return null;
|
|
1618
1817
|
const out = [];
|
|
1619
1818
|
for (const arm of spec.arms) {
|
|
1620
|
-
const
|
|
1621
|
-
const shape =
|
|
1819
|
+
const d = getCoreRegexDef(arm.parser);
|
|
1820
|
+
const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1622
1821
|
if (!shape) return null;
|
|
1623
1822
|
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1624
1823
|
}
|
|
1625
1824
|
return out;
|
|
1626
1825
|
}
|
|
1627
1826
|
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
1827
|
+
const mint = makeMint();
|
|
1628
1828
|
return [
|
|
1629
1829
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1630
1830
|
` let _e = _pos`,
|
|
1631
1831
|
` while (_e < input.length) {`,
|
|
1632
1832
|
` const c = input.charCodeAt(_e)`,
|
|
1633
|
-
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
1833
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
|
|
1634
1834
|
` break`,
|
|
1635
1835
|
` }`,
|
|
1636
1836
|
` return _e`,
|
|
@@ -1707,6 +1907,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
|
|
|
1707
1907
|
].join("\n");
|
|
1708
1908
|
}
|
|
1709
1909
|
|
|
1910
|
+
// src/compiler/scannable-terminal.ts
|
|
1911
|
+
function emitScannableTerminal(shape, c) {
|
|
1912
|
+
const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
|
|
1913
|
+
const stmts = [...m.setup];
|
|
1914
|
+
if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
|
|
1915
|
+
stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
|
|
1916
|
+
return { stmts, endVar: m.end };
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1710
1919
|
// src/compiler/inline-build.ts
|
|
1711
1920
|
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*$/;
|
|
1712
1921
|
function escapeRegExp(s) {
|
|
@@ -1828,14 +2037,45 @@ function failReturn(expected, posExpr) {
|
|
|
1828
2037
|
function failReturnArr(expectedArr, posExpr) {
|
|
1829
2038
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1830
2039
|
}
|
|
2040
|
+
function hoistExpected(ctx, constArrSource) {
|
|
2041
|
+
let name = ctx.expectedMap.get(constArrSource);
|
|
2042
|
+
if (name === void 0) {
|
|
2043
|
+
name = `_fx${ctx.expectedDecls.length}`;
|
|
2044
|
+
ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
|
|
2045
|
+
ctx.expectedMap.set(constArrSource, name);
|
|
2046
|
+
}
|
|
2047
|
+
return name;
|
|
2048
|
+
}
|
|
1831
2049
|
function failBody(ctx, expected, posExpr) {
|
|
1832
|
-
if (ctx.failLabel)
|
|
2050
|
+
if (ctx.failLabel) {
|
|
2051
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2052
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
2053
|
+
}
|
|
1833
2054
|
return failReturn(expected, posExpr);
|
|
1834
2055
|
}
|
|
1835
|
-
function failArrBody(ctx, expectedArr, posExpr) {
|
|
1836
|
-
if (ctx.failLabel)
|
|
2056
|
+
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
2057
|
+
if (ctx.failLabel) {
|
|
2058
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2059
|
+
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
2060
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
2061
|
+
}
|
|
2062
|
+
if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1837
2063
|
return failReturnArr(expectedArr, posExpr);
|
|
1838
2064
|
}
|
|
2065
|
+
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
2066
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
2067
|
+
}
|
|
2068
|
+
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
2069
|
+
if (srcCtx !== "_ctx") {
|
|
2070
|
+
if (ctx.failLabel) {
|
|
2071
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2072
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
2073
|
+
}
|
|
2074
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
2075
|
+
}
|
|
2076
|
+
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
2077
|
+
return resultFromRecorded();
|
|
2078
|
+
}
|
|
1839
2079
|
function emitIfFail(ctx, cond, body) {
|
|
1840
2080
|
return [
|
|
1841
2081
|
`${ind(ctx)}if (${cond}) {`,
|
|
@@ -1871,13 +2111,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
|
1871
2111
|
`}`
|
|
1872
2112
|
].join("\n"));
|
|
1873
2113
|
}
|
|
1874
|
-
function emitNamedFnCall(ctx, fnName, pos
|
|
2114
|
+
function emitNamedFnCall(ctx, fnName, pos) {
|
|
1875
2115
|
const vv = v(ctx, "_pfv");
|
|
1876
2116
|
const ev = v(ctx, "_pfe");
|
|
1877
2117
|
return {
|
|
1878
2118
|
stmts: [
|
|
1879
2119
|
`${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
|
|
1880
|
-
|
|
2120
|
+
// ref/lazy returns the inner failure verbatim — propagate the recorded
|
|
2121
|
+
// deepest failure (shares _ctx with the named fn), not a "parser" label.
|
|
2122
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
|
|
1881
2123
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
1882
2124
|
],
|
|
1883
2125
|
valueVar: vv,
|
|
@@ -1987,6 +2229,27 @@ function firstSetCond(codeVar, fs) {
|
|
|
1987
2229
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1988
2230
|
).join(" || ");
|
|
1989
2231
|
}
|
|
2232
|
+
var SWITCH_RANGE_LIMIT = 4;
|
|
2233
|
+
var SWITCH_MAX_CASES = 48;
|
|
2234
|
+
var SWITCH_MIN_CASES = 3;
|
|
2235
|
+
var _forceDisjointIf = false;
|
|
2236
|
+
function planDisjointDispatch(parsers) {
|
|
2237
|
+
const cases = [];
|
|
2238
|
+
let total = 0;
|
|
2239
|
+
for (const p of parsers) {
|
|
2240
|
+
const fs = p._meta.firstSet;
|
|
2241
|
+
if (fs.kind !== "ranges") return { kind: "if" };
|
|
2242
|
+
const pts = [];
|
|
2243
|
+
for (const r of fs.ranges) {
|
|
2244
|
+
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2245
|
+
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2246
|
+
}
|
|
2247
|
+
total += pts.length;
|
|
2248
|
+
if (total > SWITCH_MAX_CASES) return { kind: "if" };
|
|
2249
|
+
cases.push(pts);
|
|
2250
|
+
}
|
|
2251
|
+
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2252
|
+
}
|
|
1990
2253
|
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1991
2254
|
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1992
2255
|
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
@@ -2203,6 +2466,22 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2203
2466
|
return { stmts, valueVar: vv, endVar };
|
|
2204
2467
|
}
|
|
2205
2468
|
function emitRegex(def, ctx, pos) {
|
|
2469
|
+
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2470
|
+
const shape = scanShapeFromRegex(def.source, def.flags);
|
|
2471
|
+
if (shape) {
|
|
2472
|
+
const vv2 = v(ctx);
|
|
2473
|
+
const scanned = emitScannableTerminal(shape, {
|
|
2474
|
+
ind: ind(ctx),
|
|
2475
|
+
pos,
|
|
2476
|
+
valueVar: vv2,
|
|
2477
|
+
failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
|
|
2478
|
+
fresh: (prefix) => v(ctx, prefix)
|
|
2479
|
+
});
|
|
2480
|
+
if (scanned) {
|
|
2481
|
+
const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
|
|
2482
|
+
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2206
2485
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2207
2486
|
const key = `${def.optimizedSource}/${flags}`;
|
|
2208
2487
|
let rName = ctx.regexMap.get(key);
|
|
@@ -2213,7 +2492,6 @@ function emitRegex(def, ctx, pos) {
|
|
|
2213
2492
|
}
|
|
2214
2493
|
const mv = v(ctx, "_m");
|
|
2215
2494
|
const vv = v(ctx);
|
|
2216
|
-
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2217
2495
|
const stmts = [
|
|
2218
2496
|
`${ind(ctx)}${rName}.lastIndex = ${pos}`,
|
|
2219
2497
|
`${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
|
|
@@ -2272,15 +2550,43 @@ function emitSeq(def, ctx, pos) {
|
|
|
2272
2550
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
2273
2551
|
return { stmts, valueVar: arrV, endVar };
|
|
2274
2552
|
}
|
|
2553
|
+
function staticExpectedArr(parsers) {
|
|
2554
|
+
return JSON.stringify(parsers.flatMap((p) => {
|
|
2555
|
+
const e = staticExpected(p);
|
|
2556
|
+
return e.length > 0 ? e : [p._tag];
|
|
2557
|
+
}));
|
|
2558
|
+
}
|
|
2559
|
+
function failsAtStart(p) {
|
|
2560
|
+
const d = p._def;
|
|
2561
|
+
switch (d.tag) {
|
|
2562
|
+
case "literal":
|
|
2563
|
+
case "regex":
|
|
2564
|
+
case "keywords":
|
|
2565
|
+
case "guard":
|
|
2566
|
+
case "not":
|
|
2567
|
+
return true;
|
|
2568
|
+
case "transform":
|
|
2569
|
+
case "label":
|
|
2570
|
+
return failsAtStart(d.parser);
|
|
2571
|
+
default:
|
|
2572
|
+
return false;
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
2576
|
+
if (failsAtStart(p)) {
|
|
2577
|
+
const r = emit(p, ctx, pos);
|
|
2578
|
+
return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
|
|
2579
|
+
}
|
|
2580
|
+
const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
2581
|
+
return [
|
|
2582
|
+
...stmts,
|
|
2583
|
+
...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
|
|
2584
|
+
`${ind(ctx)}${valV} = ${valVar}`,
|
|
2585
|
+
`${ind(ctx)}${endV} = ${endVar}`
|
|
2586
|
+
];
|
|
2587
|
+
}
|
|
2275
2588
|
function emitChoice(def, ctx, pos) {
|
|
2276
|
-
const allExpected =
|
|
2277
|
-
def.parsers.map((p) => {
|
|
2278
|
-
const d = p._def;
|
|
2279
|
-
if (d.tag === "literal") return JSON.stringify(d.value);
|
|
2280
|
-
if (d.tag === "regex") return `/${d.source}/`;
|
|
2281
|
-
return p._tag;
|
|
2282
|
-
})
|
|
2283
|
-
);
|
|
2589
|
+
const allExpected = staticExpectedArr(def.parsers);
|
|
2284
2590
|
if (def.disjoint) {
|
|
2285
2591
|
const codeV = v(ctx, "_code");
|
|
2286
2592
|
const valV = v(ctx, "_chv");
|
|
@@ -2289,6 +2595,27 @@ function emitChoice(def, ctx, pos) {
|
|
|
2289
2595
|
`${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
|
|
2290
2596
|
`${ind(ctx)}let ${valV}, ${endV} = ${pos}`
|
|
2291
2597
|
];
|
|
2598
|
+
const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
|
|
2599
|
+
if (plan.kind === "switch") {
|
|
2600
|
+
stmts.push(`${ind(ctx)}switch (${codeV}) {`);
|
|
2601
|
+
ctx.indent++;
|
|
2602
|
+
for (let i = 0; i < def.parsers.length; i++) {
|
|
2603
|
+
const p = def.parsers[i];
|
|
2604
|
+
for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
|
|
2605
|
+
stmts.push(`${ind(ctx)}{`);
|
|
2606
|
+
ctx.indent++;
|
|
2607
|
+
stmts.push(
|
|
2608
|
+
...emitDisjointArm(p, ctx, pos, valV, endV),
|
|
2609
|
+
`${ind(ctx)}break`
|
|
2610
|
+
);
|
|
2611
|
+
ctx.indent--;
|
|
2612
|
+
stmts.push(`${ind(ctx)}}`);
|
|
2613
|
+
}
|
|
2614
|
+
stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
|
|
2615
|
+
ctx.indent--;
|
|
2616
|
+
stmts.push(`${ind(ctx)}}`);
|
|
2617
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
2618
|
+
}
|
|
2292
2619
|
let first = true;
|
|
2293
2620
|
for (const p of def.parsers) {
|
|
2294
2621
|
const cond = firstSetCond(codeV, p._meta.firstSet);
|
|
@@ -2296,10 +2623,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2296
2623
|
first = false;
|
|
2297
2624
|
stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
|
|
2298
2625
|
ctx.indent++;
|
|
2299
|
-
|
|
2300
|
-
stmts.push(...r.stmts);
|
|
2301
|
-
stmts.push(`${ind(ctx)}${valV} = ${r.valueVar}`);
|
|
2302
|
-
stmts.push(`${ind(ctx)}${endV} = ${r.endVar}`);
|
|
2626
|
+
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
|
|
2303
2627
|
ctx.indent--;
|
|
2304
2628
|
stmts.push(`${ind(ctx)}}`);
|
|
2305
2629
|
}
|
|
@@ -2308,7 +2632,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2308
2632
|
}
|
|
2309
2633
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
2310
2634
|
}
|
|
2311
|
-
function emitGreedyClassify(def, superIndex,
|
|
2635
|
+
function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
2312
2636
|
const superParser = def.parsers[superIndex];
|
|
2313
2637
|
const regexDef = getCoreRegexDef(superParser);
|
|
2314
2638
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
@@ -2323,10 +2647,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
|
|
|
2323
2647
|
const wordV = v(ctx, "_gw");
|
|
2324
2648
|
const endV = v(ctx, "_ge");
|
|
2325
2649
|
const valV = v(ctx, "_gcv");
|
|
2650
|
+
const regexExpected = JSON.stringify(staticExpected(superParser));
|
|
2326
2651
|
const stmts = [
|
|
2327
2652
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
2328
2653
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
2329
|
-
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx,
|
|
2654
|
+
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
|
|
2330
2655
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
2331
2656
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
|
|
2332
2657
|
`${ind(ctx)}let ${valV}`
|
|
@@ -2384,13 +2709,15 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2384
2709
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
2385
2710
|
return { stmts, valueVar: valV, endVar: endV };
|
|
2386
2711
|
}
|
|
2387
|
-
function emitFirstMatch(def,
|
|
2712
|
+
function emitFirstMatch(def, ctx, pos) {
|
|
2388
2713
|
const resValV = v(ctx, "_crv");
|
|
2389
2714
|
const resEndV = v(ctx, "_cre");
|
|
2390
2715
|
const resOkV = v(ctx, "_crok");
|
|
2716
|
+
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2391
2717
|
const ind0 = ind(ctx);
|
|
2392
2718
|
const stmts = [
|
|
2393
|
-
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false
|
|
2719
|
+
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
2720
|
+
`${ind0}let ${slots.join(", ")}`
|
|
2394
2721
|
];
|
|
2395
2722
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2396
2723
|
const p = def.parsers[i];
|
|
@@ -2437,22 +2764,24 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2437
2764
|
stmts.push(`${ind0} ${resOkV} = true`);
|
|
2438
2765
|
stmts.push(`${ind0} }`);
|
|
2439
2766
|
stmts.push(`${ind0} }`);
|
|
2767
|
+
stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx }`);
|
|
2440
2768
|
if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
|
|
2441
2769
|
} else {
|
|
2442
2770
|
stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
2443
|
-
|
|
2771
|
+
stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
|
|
2444
2772
|
}
|
|
2445
2773
|
stmts.push(`${ind0}}`);
|
|
2446
2774
|
}
|
|
2447
|
-
|
|
2775
|
+
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
2776
|
+
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
2448
2777
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
2449
2778
|
}
|
|
2450
2779
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
2451
2780
|
if (strategy.tag === "greedyClassify")
|
|
2452
|
-
return emitGreedyClassify(def, strategy.superIndex,
|
|
2781
|
+
return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
|
|
2453
2782
|
if (strategy.tag === "literalsLongestFirst")
|
|
2454
2783
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
2455
|
-
return emitFirstMatch(def,
|
|
2784
|
+
return emitFirstMatch(def, ctx, pos);
|
|
2456
2785
|
}
|
|
2457
2786
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
2458
2787
|
const def = p._def;
|
|
@@ -2680,10 +3009,11 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2680
3009
|
}
|
|
2681
3010
|
function emitNot(def, ctx, pos) {
|
|
2682
3011
|
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3012
|
+
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2683
3013
|
return {
|
|
2684
3014
|
stmts: [
|
|
2685
3015
|
...stmts,
|
|
2686
|
-
...emitIfFail(ctx, okVar, failBody(ctx,
|
|
3016
|
+
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
2687
3017
|
],
|
|
2688
3018
|
valueVar: "null",
|
|
2689
3019
|
endVar: pos
|
|
@@ -2715,7 +3045,7 @@ function emitNode(def, ctx, pos) {
|
|
|
2715
3045
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2716
3046
|
stmts.push(...innerStmts);
|
|
2717
3047
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2718
|
-
stmts.push(...emitIfFail(ctx, `!${okVar}`,
|
|
3048
|
+
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
2719
3049
|
let stV = "undefined";
|
|
2720
3050
|
if (clonesState) {
|
|
2721
3051
|
stV = v(ctx, "_nst");
|
|
@@ -2737,9 +3067,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2737
3067
|
const rv = v(ctx, "_rt");
|
|
2738
3068
|
const vv = v(ctx, "_rtv");
|
|
2739
3069
|
const ev = v(ctx, "_rte");
|
|
3070
|
+
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 }`;
|
|
2740
3071
|
const stmts = [
|
|
2741
3072
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
2742
|
-
...emitIfFail(ctx, `!${rv}.ok`,
|
|
3073
|
+
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
2743
3074
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
2744
3075
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
2745
3076
|
];
|
|
@@ -2776,7 +3107,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2776
3107
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
2777
3108
|
}
|
|
2778
3109
|
const fnName = ctx.namedParsers.get(p);
|
|
2779
|
-
return emitNamedFnCall(ctx, fnName, pos
|
|
3110
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
2780
3111
|
}
|
|
2781
3112
|
function emitRecover(def, ctx, pos) {
|
|
2782
3113
|
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
@@ -2794,7 +3125,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
2794
3125
|
`${ind0} if (${sentOk}) break`,
|
|
2795
3126
|
`${ind0} ${scanV}++`,
|
|
2796
3127
|
`${ind0} }`,
|
|
2797
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected:
|
|
3128
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
|
|
2798
3129
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2799
3130
|
`${ind0} ${valVar} = ${errV}`,
|
|
2800
3131
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -2967,7 +3298,9 @@ function emit(p, ctx, pos) {
|
|
|
2967
3298
|
stmts: [
|
|
2968
3299
|
`${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
|
|
2969
3300
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
2970
|
-
|
|
3301
|
+
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3302
|
+
// and propagate the inner failure verbatim (interpreter parity).
|
|
3303
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
|
|
2971
3304
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
2972
3305
|
],
|
|
2973
3306
|
valueVar: vv,
|
|
@@ -3068,7 +3401,7 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3068
3401
|
const recursive = /* @__PURE__ */ new Set();
|
|
3069
3402
|
const descended = /* @__PURE__ */ new Set();
|
|
3070
3403
|
const active = /* @__PURE__ */ new Set();
|
|
3071
|
-
function
|
|
3404
|
+
function walk2(p) {
|
|
3072
3405
|
const def = p._def;
|
|
3073
3406
|
if (def.tag === "lazy") {
|
|
3074
3407
|
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
@@ -3085,13 +3418,13 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3085
3418
|
return;
|
|
3086
3419
|
}
|
|
3087
3420
|
active.add(p);
|
|
3088
|
-
|
|
3421
|
+
walk2(resolved);
|
|
3089
3422
|
active.delete(p);
|
|
3090
3423
|
return;
|
|
3091
3424
|
}
|
|
3092
|
-
for (const child of childrenOf(def))
|
|
3425
|
+
for (const child of childrenOf(def)) walk2(child);
|
|
3093
3426
|
}
|
|
3094
|
-
for (const root of roots)
|
|
3427
|
+
for (const root of roots) walk2(root);
|
|
3095
3428
|
return { counts, recursive };
|
|
3096
3429
|
}
|
|
3097
3430
|
function compile(combinator, mapFnSources) {
|
|
@@ -3100,6 +3433,8 @@ function compile(combinator, mapFnSources) {
|
|
|
3100
3433
|
indent: 1,
|
|
3101
3434
|
regexDecls: [],
|
|
3102
3435
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3436
|
+
expectedDecls: [],
|
|
3437
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
3103
3438
|
mapFns: [],
|
|
3104
3439
|
mapFnSrcs: [],
|
|
3105
3440
|
buildFns: [],
|
|
@@ -3121,6 +3456,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3121
3456
|
const source = [
|
|
3122
3457
|
...emptyTlDecls,
|
|
3123
3458
|
...ctx.regexDecls,
|
|
3459
|
+
...ctx.expectedDecls,
|
|
3124
3460
|
"",
|
|
3125
3461
|
...namedPrelude,
|
|
3126
3462
|
ctx.namedFnDecls.join("\n\n"),
|
|
@@ -3133,6 +3469,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3133
3469
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3134
3470
|
...emptyTlDecls,
|
|
3135
3471
|
...ctx.regexDecls,
|
|
3472
|
+
...ctx.expectedDecls,
|
|
3136
3473
|
collatorDecl,
|
|
3137
3474
|
...namedPrelude,
|
|
3138
3475
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
@@ -3186,12 +3523,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3186
3523
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
3187
3524
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3188
3525
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3189
|
-
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3526
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3190
3527
|
if (!needsWrapper) return innerFn;
|
|
3191
3528
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3192
3529
|
const hoistedDecls = [
|
|
3193
3530
|
emptyTlDecl,
|
|
3194
3531
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
3532
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3195
3533
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3196
3534
|
mfDecl,
|
|
3197
3535
|
buildDecl,
|
|
@@ -3252,6 +3590,7 @@ function recover(combinator, sentinel) {
|
|
|
3252
3590
|
canMatchNewline: true,
|
|
3253
3591
|
isTrivia: false
|
|
3254
3592
|
};
|
|
3593
|
+
const expected = staticExpected(combinator);
|
|
3255
3594
|
return {
|
|
3256
3595
|
_tag: "recover",
|
|
3257
3596
|
_meta: meta,
|
|
@@ -3267,7 +3606,7 @@ function recover(combinator, sentinel) {
|
|
|
3267
3606
|
const error = {
|
|
3268
3607
|
_tag: "parseError",
|
|
3269
3608
|
span: { start: pos, end: scanPos },
|
|
3270
|
-
expected
|
|
3609
|
+
expected
|
|
3271
3610
|
};
|
|
3272
3611
|
ctx._errors?.push(error);
|
|
3273
3612
|
return { ok: true, value: error, span: { start: pos, end: scanPos } };
|
|
@@ -3278,58 +3617,6 @@ function isParseError(value) {
|
|
|
3278
3617
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
3279
3618
|
}
|
|
3280
3619
|
|
|
3281
|
-
// src/combinators/expect.ts
|
|
3282
|
-
function staticExpected(c) {
|
|
3283
|
-
const def = c._def;
|
|
3284
|
-
switch (def.tag) {
|
|
3285
|
-
case "literal":
|
|
3286
|
-
return [JSON.stringify(def.value)];
|
|
3287
|
-
case "regex":
|
|
3288
|
-
return [`/${def.source}/`];
|
|
3289
|
-
case "keywords":
|
|
3290
|
-
return def.words.map((w) => JSON.stringify(w));
|
|
3291
|
-
case "label":
|
|
3292
|
-
return [def.label];
|
|
3293
|
-
case "choice":
|
|
3294
|
-
return def.parsers.flatMap(staticExpected);
|
|
3295
|
-
case "sequence":
|
|
3296
|
-
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
3297
|
-
case "node":
|
|
3298
|
-
case "grammar":
|
|
3299
|
-
case "trivia":
|
|
3300
|
-
case "optional":
|
|
3301
|
-
case "many":
|
|
3302
|
-
case "oneOrMore":
|
|
3303
|
-
case "transform":
|
|
3304
|
-
case "not":
|
|
3305
|
-
return staticExpected(def.parser);
|
|
3306
|
-
case "lazy":
|
|
3307
|
-
return staticExpected(def.thunk());
|
|
3308
|
-
default:
|
|
3309
|
-
return [];
|
|
3310
|
-
}
|
|
3311
|
-
}
|
|
3312
|
-
function expect(combinator, label2) {
|
|
3313
|
-
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
3314
|
-
const meta = {
|
|
3315
|
-
firstSet: combinator._meta.firstSet,
|
|
3316
|
-
canMatchNewline: combinator._meta.canMatchNewline,
|
|
3317
|
-
isTrivia: false
|
|
3318
|
-
};
|
|
3319
|
-
return {
|
|
3320
|
-
_tag: "expect",
|
|
3321
|
-
_meta: meta,
|
|
3322
|
-
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
3323
|
-
parse(input, pos, ctx) {
|
|
3324
|
-
const result = combinator.parse(input, pos, ctx);
|
|
3325
|
-
if (result.ok) return result;
|
|
3326
|
-
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
3327
|
-
ctx._errors?.push(error);
|
|
3328
|
-
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
3329
|
-
}
|
|
3330
|
-
};
|
|
3331
|
-
}
|
|
3332
|
-
|
|
3333
3620
|
// src/combinators/completions.ts
|
|
3334
3621
|
function completionsAt(combinator, input, offset) {
|
|
3335
3622
|
const probe = { offset, best: null };
|
|
@@ -3439,6 +3726,23 @@ function escapeClassChar(ch) {
|
|
|
3439
3726
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3440
3727
|
}
|
|
3441
3728
|
|
|
3729
|
+
// src/combinators/recover-list.ts
|
|
3730
|
+
function tolerantElement(item, sentinel, until) {
|
|
3731
|
+
const el = transform(
|
|
3732
|
+
sequence(not(until), recover(item, sentinel)),
|
|
3733
|
+
([, value]) => value
|
|
3734
|
+
);
|
|
3735
|
+
if (el._def.tag === "transform") el._def.fnSrc = "([, value]) => value";
|
|
3736
|
+
return el;
|
|
3737
|
+
}
|
|
3738
|
+
function sepByRecover(item, separator, until) {
|
|
3739
|
+
const sentinel = choice(separator, until);
|
|
3740
|
+
return sepBy(tolerantElement(item, sentinel, until), separator);
|
|
3741
|
+
}
|
|
3742
|
+
function manyRecover(item, until) {
|
|
3743
|
+
return many(tolerantElement(item, until, until));
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3442
3746
|
// src/functional/doc.ts
|
|
3443
3747
|
function isNode(x) {
|
|
3444
3748
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
@@ -3626,6 +3930,41 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
3626
3930
|
return { before, after };
|
|
3627
3931
|
}
|
|
3628
3932
|
|
|
3933
|
+
// src/cst/walk.ts
|
|
3934
|
+
function walk(root, visitor, ctx = void 0) {
|
|
3935
|
+
const go = (node2, parent) => {
|
|
3936
|
+
const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
|
|
3937
|
+
const children = node2.children;
|
|
3938
|
+
if (descend !== false && Array.isArray(children)) {
|
|
3939
|
+
for (const child of children) go(child, node2);
|
|
3940
|
+
}
|
|
3941
|
+
visitor.leave?.(node2, parent, ctx);
|
|
3942
|
+
};
|
|
3943
|
+
go(root, null);
|
|
3944
|
+
}
|
|
3945
|
+
function createVisitor(handlers) {
|
|
3946
|
+
const api = {
|
|
3947
|
+
visit(node2) {
|
|
3948
|
+
const handler = node2.type !== void 0 ? handlers[node2.type] : void 0;
|
|
3949
|
+
if (handler) return handler(node2, api);
|
|
3950
|
+
api.visitChildren(node2);
|
|
3951
|
+
return void 0;
|
|
3952
|
+
},
|
|
3953
|
+
visitChildren(node2) {
|
|
3954
|
+
const out = [];
|
|
3955
|
+
const children = node2.children;
|
|
3956
|
+
if (Array.isArray(children)) {
|
|
3957
|
+
for (const child of children) {
|
|
3958
|
+
const r = api.visit(child);
|
|
3959
|
+
if (r !== void 0) out.push(r);
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
return out;
|
|
3963
|
+
}
|
|
3964
|
+
};
|
|
3965
|
+
return (node2) => api.visit(node2);
|
|
3966
|
+
}
|
|
3967
|
+
|
|
3629
3968
|
// src/cst/trivia-entries.ts
|
|
3630
3969
|
function entryOffset(i, stride) {
|
|
3631
3970
|
return i * stride;
|