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.
- package/LICENSE +21 -0
- package/README.md +6 -6
- 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 +139 -9
- 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/types.d.ts +1 -1
- package/dist/cst/walk.d.ts +72 -0
- package/dist/cst/walk.d.ts.map +1 -0
- package/dist/functional/doc.d.ts +6 -6
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/index.cjs +1201 -187
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1201 -187
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts +36 -2
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +1403 -232
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1403 -232
- 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 +9 -3
- package/dist/combinators/lazy.d.ts +0 -14
- package/dist/combinators/lazy.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -37,15 +37,16 @@ __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,
|
|
43
44
|
keywords: () => keywords,
|
|
44
45
|
label: () => label,
|
|
45
46
|
literal: () => literal,
|
|
46
|
-
makeFunctionalDoc: () => makeFunctionalDoc,
|
|
47
47
|
makeWord: () => makeWord,
|
|
48
48
|
many: () => many,
|
|
49
|
+
manyRecover: () => manyRecover,
|
|
49
50
|
noTrivia: () => noTrivia,
|
|
50
51
|
node: () => node,
|
|
51
52
|
not: () => not,
|
|
@@ -53,6 +54,7 @@ __export(index_exports, {
|
|
|
53
54
|
oneOrMore: () => oneOrMore,
|
|
54
55
|
optional: () => optional,
|
|
55
56
|
parse: () => parse,
|
|
57
|
+
parseDoc: () => parseDoc,
|
|
56
58
|
parser: () => parser,
|
|
57
59
|
recover: () => recover,
|
|
58
60
|
ref: () => ref,
|
|
@@ -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,140 @@ 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
|
+
var SPACE_RANGES = [
|
|
1480
|
+
[9, 13],
|
|
1481
|
+
[32, 32],
|
|
1482
|
+
[160, 160],
|
|
1483
|
+
[5760, 5760],
|
|
1484
|
+
[8192, 8202],
|
|
1485
|
+
[8232, 8232],
|
|
1486
|
+
[8233, 8233],
|
|
1487
|
+
[8239, 8239],
|
|
1488
|
+
[8287, 8287],
|
|
1489
|
+
[12288, 12288],
|
|
1490
|
+
[65279, 65279]
|
|
1491
|
+
];
|
|
1492
|
+
function shorthandRanges(ch) {
|
|
1493
|
+
if (ch === "d") return [[48, 57]];
|
|
1494
|
+
if (ch === "s") return SPACE_RANGES;
|
|
1495
|
+
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1496
|
+
}
|
|
1497
|
+
function readUnicodeEscape(body, i) {
|
|
1498
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
1499
|
+
const hex = body.slice(i + 2, i + 6);
|
|
1500
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
1501
|
+
return { cp: Number.parseInt(hex, 16), next: i + 6 };
|
|
1502
|
+
}
|
|
1419
1503
|
function parseClassRanges(body) {
|
|
1420
1504
|
const ranges = [];
|
|
1421
1505
|
let i = 0;
|
|
1422
|
-
const
|
|
1506
|
+
const readAtom = () => {
|
|
1423
1507
|
const ch = body[i];
|
|
1424
1508
|
if (ch === void 0) return null;
|
|
1425
1509
|
if (ch === "\\") {
|
|
1510
|
+
const uni = readUnicodeEscape(body, i);
|
|
1511
|
+
if (uni) {
|
|
1512
|
+
i = uni.next;
|
|
1513
|
+
return { cp: uni.cp };
|
|
1514
|
+
}
|
|
1426
1515
|
const e = body[i + 1];
|
|
1427
1516
|
if (e === void 0) return null;
|
|
1428
1517
|
i += 2;
|
|
1429
|
-
|
|
1518
|
+
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1519
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
1520
|
+
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1521
|
+
return { cp: e.codePointAt(0) };
|
|
1430
1522
|
}
|
|
1431
1523
|
i += ch.length;
|
|
1432
|
-
return ch.codePointAt(0);
|
|
1524
|
+
return { cp: ch.codePointAt(0) };
|
|
1433
1525
|
};
|
|
1434
1526
|
while (i < body.length) {
|
|
1435
|
-
const lo =
|
|
1527
|
+
const lo = readAtom();
|
|
1436
1528
|
if (lo === null) return null;
|
|
1529
|
+
if ("set" in lo) {
|
|
1530
|
+
ranges.push(...lo.set);
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1437
1533
|
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1438
1534
|
i += 1;
|
|
1439
|
-
const hi =
|
|
1440
|
-
if (hi === null) return null;
|
|
1441
|
-
ranges.push([lo, hi]);
|
|
1535
|
+
const hi = readAtom();
|
|
1536
|
+
if (hi === null || "set" in hi) return null;
|
|
1537
|
+
ranges.push([lo.cp, hi.cp]);
|
|
1442
1538
|
} else {
|
|
1443
|
-
ranges.push([lo, lo]);
|
|
1539
|
+
ranges.push([lo.cp, lo.cp]);
|
|
1444
1540
|
}
|
|
1445
1541
|
}
|
|
1446
1542
|
return ranges.length ? ranges : null;
|
|
1447
1543
|
}
|
|
1544
|
+
function classToRanges(cls) {
|
|
1545
|
+
if (cls === "\\d") return shorthandRanges("d");
|
|
1546
|
+
if (cls === "\\w") return shorthandRanges("w");
|
|
1547
|
+
if (cls === "\\s") return shorthandRanges("s");
|
|
1548
|
+
const body = cls.slice(1, -1);
|
|
1549
|
+
if (body.startsWith("^")) return null;
|
|
1550
|
+
return parseClassRanges(body);
|
|
1551
|
+
}
|
|
1552
|
+
var foldEq = (cVar, cp) => {
|
|
1553
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
|
|
1554
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
|
|
1555
|
+
return `${cVar} === ${cp}`;
|
|
1556
|
+
};
|
|
1448
1557
|
function literalCodePoints(frag) {
|
|
1449
1558
|
const out = [];
|
|
1450
1559
|
let i = 0;
|
|
@@ -1463,19 +1572,482 @@ function literalCodePoints(frag) {
|
|
|
1463
1572
|
}
|
|
1464
1573
|
return out.length ? out : null;
|
|
1465
1574
|
}
|
|
1575
|
+
function parseStringShape(source) {
|
|
1576
|
+
const q = source[0];
|
|
1577
|
+
if (q === void 0 || q === "\\" || META.has(q)) return null;
|
|
1578
|
+
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
1579
|
+
const inner = source.slice(1, -1);
|
|
1580
|
+
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
1581
|
+
const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
1582
|
+
let body;
|
|
1583
|
+
let esc;
|
|
1584
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
|
|
1585
|
+
if (m) {
|
|
1586
|
+
body = m[1];
|
|
1587
|
+
esc = m[2];
|
|
1588
|
+
} else {
|
|
1589
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
|
|
1590
|
+
if (m) {
|
|
1591
|
+
esc = m[1];
|
|
1592
|
+
body = m[2];
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
if (body === void 0 || esc === void 0) return null;
|
|
1596
|
+
const excluded = parseClassRanges(body);
|
|
1597
|
+
if (!excluded) return null;
|
|
1598
|
+
const qcp = q.codePointAt(0);
|
|
1599
|
+
const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
|
|
1600
|
+
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1601
|
+
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1602
|
+
}
|
|
1603
|
+
function parseSeqParts(source) {
|
|
1604
|
+
const parts = [];
|
|
1605
|
+
let lit = [];
|
|
1606
|
+
const flush = () => {
|
|
1607
|
+
if (lit.length) {
|
|
1608
|
+
parts.push({ part: "lit", cps: lit, optional: false });
|
|
1609
|
+
lit = [];
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
const runFrom = (ranges, negated, q) => {
|
|
1613
|
+
if (q === "+") {
|
|
1614
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
|
|
1615
|
+
return 1;
|
|
1616
|
+
}
|
|
1617
|
+
if (q === "*") {
|
|
1618
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
|
|
1619
|
+
return 1;
|
|
1620
|
+
}
|
|
1621
|
+
if (q === "?") {
|
|
1622
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
|
|
1623
|
+
return 1;
|
|
1624
|
+
}
|
|
1625
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
|
|
1626
|
+
return 0;
|
|
1627
|
+
};
|
|
1628
|
+
let i = 0;
|
|
1629
|
+
while (i < source.length) {
|
|
1630
|
+
const ch = source[i];
|
|
1631
|
+
if (ch === "[") {
|
|
1632
|
+
let k = i + 1;
|
|
1633
|
+
const neg = source[k] === "^";
|
|
1634
|
+
if (neg) k++;
|
|
1635
|
+
let body = "";
|
|
1636
|
+
while (k < source.length && source[k] !== "]") {
|
|
1637
|
+
if (source[k] === "\\") {
|
|
1638
|
+
body += source[k] + (source[k + 1] ?? "");
|
|
1639
|
+
k += 2;
|
|
1640
|
+
} else {
|
|
1641
|
+
body += source[k];
|
|
1642
|
+
k++;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
if (source[k] !== "]") return null;
|
|
1646
|
+
const ranges = parseClassRanges(body);
|
|
1647
|
+
if (!ranges) return null;
|
|
1648
|
+
flush();
|
|
1649
|
+
i = k + 1;
|
|
1650
|
+
i += runFrom(ranges, neg, source[i]);
|
|
1651
|
+
continue;
|
|
1652
|
+
}
|
|
1653
|
+
if (ch === "\\") {
|
|
1654
|
+
const e = source[i + 1];
|
|
1655
|
+
if (e === "d" || e === "w" || e === "s") {
|
|
1656
|
+
flush();
|
|
1657
|
+
i += 2;
|
|
1658
|
+
i += runFrom(shorthandRanges(e), false, source[i]);
|
|
1659
|
+
continue;
|
|
1660
|
+
}
|
|
1661
|
+
const uni = readUnicodeEscape(source, i);
|
|
1662
|
+
let cp2;
|
|
1663
|
+
if (uni) {
|
|
1664
|
+
cp2 = uni.cp;
|
|
1665
|
+
i = uni.next;
|
|
1666
|
+
} else if (e === void 0) return null;
|
|
1667
|
+
else if (e in CLASS_ESCAPES) {
|
|
1668
|
+
cp2 = CLASS_ESCAPES[e];
|
|
1669
|
+
i += 2;
|
|
1670
|
+
} else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1671
|
+
else {
|
|
1672
|
+
cp2 = e.codePointAt(0);
|
|
1673
|
+
i += 2;
|
|
1674
|
+
}
|
|
1675
|
+
if (source[i] === "?") {
|
|
1676
|
+
flush();
|
|
1677
|
+
parts.push({ part: "lit", cps: [cp2], optional: true });
|
|
1678
|
+
i++;
|
|
1679
|
+
} else lit.push(cp2);
|
|
1680
|
+
continue;
|
|
1681
|
+
}
|
|
1682
|
+
if (ch === "(") {
|
|
1683
|
+
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
1684
|
+
let k = i + 3;
|
|
1685
|
+
let depth = 1;
|
|
1686
|
+
while (k < source.length && depth > 0) {
|
|
1687
|
+
const c2 = source[k];
|
|
1688
|
+
if (c2 === "\\") {
|
|
1689
|
+
k += 2;
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
if (c2 === "[") {
|
|
1693
|
+
let j = k + 1;
|
|
1694
|
+
if (source[j] === "^") j++;
|
|
1695
|
+
while (j < source.length && source[j] !== "]") {
|
|
1696
|
+
if (source[j] === "\\") j += 2;
|
|
1697
|
+
else j++;
|
|
1698
|
+
}
|
|
1699
|
+
if (source[j] !== "]") return null;
|
|
1700
|
+
k = j + 1;
|
|
1701
|
+
continue;
|
|
1702
|
+
}
|
|
1703
|
+
if (c2 === "(") depth++;
|
|
1704
|
+
else if (c2 === ")") depth--;
|
|
1705
|
+
k++;
|
|
1706
|
+
}
|
|
1707
|
+
if (depth !== 0) return null;
|
|
1708
|
+
const body = source.slice(i + 3, k - 1);
|
|
1709
|
+
const inner = parseScanShape(body);
|
|
1710
|
+
if (!inner || !groupInnerSafe(inner)) return null;
|
|
1711
|
+
const q = source[k];
|
|
1712
|
+
if (q === "{") return null;
|
|
1713
|
+
flush();
|
|
1714
|
+
if (q === "+") {
|
|
1715
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1716
|
+
parts.push({ part: "group", inner, min: 1, unbounded: true });
|
|
1717
|
+
i = k + 1;
|
|
1718
|
+
} else if (q === "*") {
|
|
1719
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1720
|
+
parts.push({ part: "group", inner, min: 0, unbounded: true });
|
|
1721
|
+
i = k + 1;
|
|
1722
|
+
} else if (q === "?") {
|
|
1723
|
+
parts.push({ part: "group", inner, min: 0, unbounded: false });
|
|
1724
|
+
i = k + 1;
|
|
1725
|
+
} else {
|
|
1726
|
+
parts.push({ part: "group", inner, min: 1, unbounded: false });
|
|
1727
|
+
i = k;
|
|
1728
|
+
}
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
if (META.has(ch)) return null;
|
|
1732
|
+
const cp = ch.codePointAt(0);
|
|
1733
|
+
i += ch.length;
|
|
1734
|
+
if (source[i] === "?") {
|
|
1735
|
+
flush();
|
|
1736
|
+
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
1737
|
+
i++;
|
|
1738
|
+
} else lit.push(cp);
|
|
1739
|
+
}
|
|
1740
|
+
flush();
|
|
1741
|
+
return parts.length ? parts : null;
|
|
1742
|
+
}
|
|
1743
|
+
function seqIsUnambiguous(parts) {
|
|
1744
|
+
let hasMandatory = false;
|
|
1745
|
+
for (let k = 0; k < parts.length; k++) {
|
|
1746
|
+
const p = parts[k];
|
|
1747
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1748
|
+
if (mandatory) hasMandatory = true;
|
|
1749
|
+
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1750
|
+
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1751
|
+
if (skippableSingle || isUnboundedRepeat) {
|
|
1752
|
+
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
1753
|
+
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return hasMandatory;
|
|
1757
|
+
}
|
|
1758
|
+
function rangesOverlap(a, b) {
|
|
1759
|
+
return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
|
|
1760
|
+
}
|
|
1761
|
+
function rangesSubset(sub, sup) {
|
|
1762
|
+
return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
|
|
1763
|
+
}
|
|
1764
|
+
function parseClassOperand(body) {
|
|
1765
|
+
if (body === "\\d" || body === "\\w" || body === "\\s") {
|
|
1766
|
+
return { ranges: shorthandRanges(body[1]), negated: false };
|
|
1767
|
+
}
|
|
1768
|
+
if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
|
|
1769
|
+
let inner = body.slice(1, -1);
|
|
1770
|
+
const negated = inner.startsWith("^");
|
|
1771
|
+
if (negated) inner = inner.slice(1);
|
|
1772
|
+
const ranges = parseClassRanges(inner);
|
|
1773
|
+
return ranges ? { ranges, negated } : null;
|
|
1774
|
+
}
|
|
1775
|
+
const cps = literalCodePoints(body);
|
|
1776
|
+
if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
|
|
1777
|
+
return null;
|
|
1778
|
+
}
|
|
1779
|
+
function classSubset(t, u) {
|
|
1780
|
+
if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1781
|
+
if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1782
|
+
if (t.negated && !u.negated) return false;
|
|
1783
|
+
return rangesSubset(u.ranges, t.ranges);
|
|
1784
|
+
}
|
|
1785
|
+
function classDisjoint(t, u) {
|
|
1786
|
+
if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1787
|
+
if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1788
|
+
if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
|
|
1789
|
+
return false;
|
|
1790
|
+
}
|
|
1791
|
+
function firstSetsDisjoint(a, b) {
|
|
1792
|
+
if (a === "any" || b === "any") return false;
|
|
1793
|
+
return classDisjoint(a, b);
|
|
1794
|
+
}
|
|
1795
|
+
function trailingBacktrackClass(shape) {
|
|
1796
|
+
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1797
|
+
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
1798
|
+
if (shape.kind === "litFold") return null;
|
|
1799
|
+
if (shape.kind === "seq") {
|
|
1800
|
+
const last = shape.parts[shape.parts.length - 1];
|
|
1801
|
+
if (last.part === "lit") return null;
|
|
1802
|
+
if (last.part === "run") {
|
|
1803
|
+
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1804
|
+
return null;
|
|
1805
|
+
}
|
|
1806
|
+
const innerWiggle = trailingBacktrackClass(last.inner);
|
|
1807
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1808
|
+
const requiredOnce = !last.unbounded && last.min === 1;
|
|
1809
|
+
if (requiredOnce) return innerWiggle;
|
|
1810
|
+
const dropExposed = shapeFirstAccept(last.inner);
|
|
1811
|
+
if (dropExposed === "any") return "unsupported";
|
|
1812
|
+
if (innerWiggle === null) return dropExposed;
|
|
1813
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1814
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1815
|
+
}
|
|
1816
|
+
if (shape.kind === "alt") {
|
|
1817
|
+
if (!shape.disjoint) return "unsupported";
|
|
1818
|
+
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1819
|
+
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1820
|
+
const concrete = classes.filter((c) => c !== null);
|
|
1821
|
+
if (concrete.length === 0) return null;
|
|
1822
|
+
const unioned = unionCharSets(concrete);
|
|
1823
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1824
|
+
}
|
|
1825
|
+
return "unsupported";
|
|
1826
|
+
}
|
|
1827
|
+
function lookaheadUnambiguous(inner, operand, negative) {
|
|
1828
|
+
const tail = trailingBacktrackClass(inner);
|
|
1829
|
+
if (tail === "unsupported") return false;
|
|
1830
|
+
if (tail === null) return true;
|
|
1831
|
+
return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
|
|
1832
|
+
}
|
|
1833
|
+
function stripTrailingLookahead(source) {
|
|
1834
|
+
if (!source.endsWith(")")) return null;
|
|
1835
|
+
for (const negative of [true, false]) {
|
|
1836
|
+
const lead = negative ? "(?!" : "(?=";
|
|
1837
|
+
const idx = source.lastIndexOf(lead);
|
|
1838
|
+
if (idx === -1) continue;
|
|
1839
|
+
const body = source.slice(idx + lead.length, -1);
|
|
1840
|
+
const operand = body.length ? parseClassOperand(body) : null;
|
|
1841
|
+
if (!operand) continue;
|
|
1842
|
+
return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
|
|
1843
|
+
}
|
|
1844
|
+
return null;
|
|
1845
|
+
}
|
|
1846
|
+
function unwrapOuterGroup(source) {
|
|
1847
|
+
while (source.startsWith("(?:")) {
|
|
1848
|
+
let depth = 1;
|
|
1849
|
+
let i = 3;
|
|
1850
|
+
let ok = true;
|
|
1851
|
+
while (i < source.length && depth > 0) {
|
|
1852
|
+
const ch = source[i];
|
|
1853
|
+
if (ch === "\\") {
|
|
1854
|
+
i += 2;
|
|
1855
|
+
continue;
|
|
1856
|
+
}
|
|
1857
|
+
if (ch === "[") {
|
|
1858
|
+
let j = i + 1;
|
|
1859
|
+
if (source[j] === "^") j++;
|
|
1860
|
+
while (j < source.length && source[j] !== "]") {
|
|
1861
|
+
if (source[j] === "\\") j += 2;
|
|
1862
|
+
else j++;
|
|
1863
|
+
}
|
|
1864
|
+
if (source[j] !== "]") {
|
|
1865
|
+
ok = false;
|
|
1866
|
+
break;
|
|
1867
|
+
}
|
|
1868
|
+
i = j + 1;
|
|
1869
|
+
continue;
|
|
1870
|
+
}
|
|
1871
|
+
if (ch === "(") depth++;
|
|
1872
|
+
else if (ch === ")") depth--;
|
|
1873
|
+
i++;
|
|
1874
|
+
}
|
|
1875
|
+
if (!ok || depth !== 0 || i !== source.length) break;
|
|
1876
|
+
source = source.slice(3, -1);
|
|
1877
|
+
}
|
|
1878
|
+
return source;
|
|
1879
|
+
}
|
|
1880
|
+
function splitTopLevelAlternation(source) {
|
|
1881
|
+
const body = unwrapOuterGroup(source);
|
|
1882
|
+
const parts = [];
|
|
1883
|
+
let depth = 0;
|
|
1884
|
+
let last = 0;
|
|
1885
|
+
let i = 0;
|
|
1886
|
+
while (i < body.length) {
|
|
1887
|
+
const ch = body[i];
|
|
1888
|
+
if (ch === "\\") {
|
|
1889
|
+
i += 2;
|
|
1890
|
+
continue;
|
|
1891
|
+
}
|
|
1892
|
+
if (ch === "[") {
|
|
1893
|
+
let j = i + 1;
|
|
1894
|
+
if (body[j] === "^") j++;
|
|
1895
|
+
while (j < body.length && body[j] !== "]") {
|
|
1896
|
+
if (body[j] === "\\") j += 2;
|
|
1897
|
+
else j++;
|
|
1898
|
+
}
|
|
1899
|
+
if (body[j] !== "]") return null;
|
|
1900
|
+
i = j + 1;
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
if (ch === "(") {
|
|
1904
|
+
depth++;
|
|
1905
|
+
i++;
|
|
1906
|
+
continue;
|
|
1907
|
+
}
|
|
1908
|
+
if (ch === ")") {
|
|
1909
|
+
depth--;
|
|
1910
|
+
if (depth < 0) return null;
|
|
1911
|
+
i++;
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
if (ch === "|" && depth === 0) {
|
|
1915
|
+
parts.push(body.slice(last, i));
|
|
1916
|
+
last = i + 1;
|
|
1917
|
+
i++;
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
i++;
|
|
1921
|
+
}
|
|
1922
|
+
if (depth !== 0) return null;
|
|
1923
|
+
parts.push(body.slice(last));
|
|
1924
|
+
if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
|
|
1925
|
+
return parts;
|
|
1926
|
+
}
|
|
1927
|
+
function shapeCanBeEmpty(shape) {
|
|
1928
|
+
if (shape.kind === "chars") return !shape.minOne;
|
|
1929
|
+
if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
|
|
1930
|
+
if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
|
|
1931
|
+
return false;
|
|
1932
|
+
}
|
|
1933
|
+
function unionCharSets(sets) {
|
|
1934
|
+
if (sets.some((s) => s === "any")) return "any";
|
|
1935
|
+
const concrete = sets;
|
|
1936
|
+
if (concrete.length === 1) return concrete[0];
|
|
1937
|
+
if (concrete.some((s) => s.negated)) return "any";
|
|
1938
|
+
return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
|
|
1939
|
+
}
|
|
1940
|
+
function partFirstAccept(part) {
|
|
1941
|
+
if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
|
|
1942
|
+
if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
|
|
1943
|
+
return shapeFirstAccept(part.inner);
|
|
1944
|
+
}
|
|
1945
|
+
function seqFirstAccept(parts) {
|
|
1946
|
+
const sets = [];
|
|
1947
|
+
for (const p of parts) {
|
|
1948
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1949
|
+
sets.push(partFirstAccept(p));
|
|
1950
|
+
if (mandatory) break;
|
|
1951
|
+
}
|
|
1952
|
+
return unionCharSets(sets);
|
|
1953
|
+
}
|
|
1954
|
+
function groupInnerSafe(shape) {
|
|
1955
|
+
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1956
|
+
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1957
|
+
if (shape.kind === "alt") return shape.disjoint;
|
|
1958
|
+
return false;
|
|
1959
|
+
}
|
|
1960
|
+
function shapeFirstAccept(shape) {
|
|
1961
|
+
if (shapeCanBeEmpty(shape)) return "any";
|
|
1962
|
+
switch (shape.kind) {
|
|
1963
|
+
case "chars":
|
|
1964
|
+
return { ranges: shape.ranges, negated: false };
|
|
1965
|
+
case "ident":
|
|
1966
|
+
return { ranges: shape.head, negated: false };
|
|
1967
|
+
case "until":
|
|
1968
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1969
|
+
case "delimited":
|
|
1970
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1971
|
+
case "string":
|
|
1972
|
+
return { ranges: [[shape.quote, shape.quote]], negated: false };
|
|
1973
|
+
case "litFold": {
|
|
1974
|
+
const cp = shape.open[0];
|
|
1975
|
+
if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
|
|
1976
|
+
if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
|
|
1977
|
+
return { ranges: [[cp, cp]], negated: false };
|
|
1978
|
+
}
|
|
1979
|
+
case "seq":
|
|
1980
|
+
return seqFirstAccept(shape.parts);
|
|
1981
|
+
case "lookahead":
|
|
1982
|
+
return shapeFirstAccept(shape.inner);
|
|
1983
|
+
// zero-width — doesn't change first-char acceptance
|
|
1984
|
+
case "alt": {
|
|
1985
|
+
if (!shape.disjoint) return "any";
|
|
1986
|
+
const sets = shape.firsts.filter((f) => f !== null);
|
|
1987
|
+
return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
function allPairsDisjoint(sets) {
|
|
1992
|
+
for (let i = 0; i < sets.length; i++) {
|
|
1993
|
+
const a = sets[i];
|
|
1994
|
+
if (a === "any") return false;
|
|
1995
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
1996
|
+
const b = sets[j];
|
|
1997
|
+
if (b === "any" || !classDisjoint(a, b)) return false;
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
return true;
|
|
2001
|
+
}
|
|
2002
|
+
function parseAlternation(source) {
|
|
2003
|
+
const parts = splitTopLevelAlternation(source);
|
|
2004
|
+
if (!parts) return null;
|
|
2005
|
+
const arms = [];
|
|
2006
|
+
for (const p of parts) {
|
|
2007
|
+
const arm = parseScanShape(p);
|
|
2008
|
+
if (!arm) return null;
|
|
2009
|
+
arms.push(arm);
|
|
2010
|
+
}
|
|
2011
|
+
const firstSets = arms.map(shapeFirstAccept);
|
|
2012
|
+
const disjoint = allPairsDisjoint(firstSets);
|
|
2013
|
+
return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
|
|
2014
|
+
}
|
|
1466
2015
|
function parseScanShape(source) {
|
|
2016
|
+
const alt = parseAlternation(source);
|
|
2017
|
+
if (alt) return alt;
|
|
2018
|
+
const la = stripTrailingLookahead(source);
|
|
2019
|
+
if (la) {
|
|
2020
|
+
const inner = parseScanShapeCore(la.base);
|
|
2021
|
+
if (!inner) return null;
|
|
2022
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2023
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2024
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2025
|
+
}
|
|
2026
|
+
return parseScanShapeCore(source);
|
|
2027
|
+
}
|
|
2028
|
+
function parseScanShapeCore(source) {
|
|
1467
2029
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1468
|
-
if (m) {
|
|
1469
|
-
if (m[1].startsWith("^")) return null;
|
|
2030
|
+
if (m && !m[1].startsWith("^")) {
|
|
1470
2031
|
const ranges = parseClassRanges(m[1]);
|
|
1471
|
-
return ranges ? { kind: "chars", ranges } : null;
|
|
2032
|
+
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1472
2033
|
}
|
|
2034
|
+
m = /^\\([dws])([+*])$/.exec(source);
|
|
2035
|
+
if (m) {
|
|
2036
|
+
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
2037
|
+
}
|
|
2038
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
|
|
2039
|
+
if (m) {
|
|
2040
|
+
const head = classToRanges(m[1]);
|
|
2041
|
+
const tail = classToRanges(m[2]);
|
|
2042
|
+
if (head && tail) return { kind: "ident", head, tail };
|
|
2043
|
+
}
|
|
2044
|
+
const str = parseStringShape(source);
|
|
2045
|
+
if (str) return str;
|
|
1473
2046
|
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1474
2047
|
if (m) {
|
|
1475
2048
|
const open = literalCodePoints(m[1]);
|
|
1476
2049
|
const stop = parseClassRanges(m[2]);
|
|
1477
2050
|
if (open && stop) return { kind: "until", open, stop };
|
|
1478
|
-
return null;
|
|
1479
2051
|
}
|
|
1480
2052
|
if (!source.includes("\\\\")) {
|
|
1481
2053
|
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
@@ -1485,68 +2057,278 @@ function parseScanShape(source) {
|
|
|
1485
2057
|
if (open && close) return { kind: "delimited", open, close };
|
|
1486
2058
|
}
|
|
1487
2059
|
}
|
|
2060
|
+
const parts = parseSeqParts(source);
|
|
2061
|
+
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1488
2062
|
return null;
|
|
1489
2063
|
}
|
|
2064
|
+
function scanShapeFromRegex(source, flags) {
|
|
2065
|
+
if (/[msu]/.test(flags)) return null;
|
|
2066
|
+
if (/i/.test(flags)) {
|
|
2067
|
+
const open = literalCodePoints(source);
|
|
2068
|
+
return open ? { kind: "litFold", open } : null;
|
|
2069
|
+
}
|
|
2070
|
+
return parseScanShape(source);
|
|
2071
|
+
}
|
|
1490
2072
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1491
2073
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
1492
2074
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1493
2075
|
).join(" && ");
|
|
1494
|
-
|
|
2076
|
+
var LINE_TERMINATORS = [10, 13, 8232, 8233];
|
|
2077
|
+
function stringHardStop(shape) {
|
|
2078
|
+
return shape.excluded.filter(
|
|
2079
|
+
([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
2082
|
+
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2083
|
+
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2084
|
+
if (shape.kind === "lookahead") {
|
|
2085
|
+
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
2086
|
+
const okV = mint("_ok");
|
|
2087
|
+
const endV2 = mint("_end");
|
|
2088
|
+
const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
|
|
2089
|
+
const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
|
|
2090
|
+
const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
|
|
2091
|
+
const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
|
|
2092
|
+
return {
|
|
2093
|
+
setup: [
|
|
2094
|
+
...inner.setup,
|
|
2095
|
+
`${ind2}let ${okV} = ${inner.ok}`,
|
|
2096
|
+
`${ind2}let ${endV2} = ${inner.end}`,
|
|
2097
|
+
`${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
|
|
2098
|
+
],
|
|
2099
|
+
ok: okV,
|
|
2100
|
+
end: endV2
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
if (shape.kind === "alt") {
|
|
2104
|
+
const okV = mint("_ok");
|
|
2105
|
+
const endV2 = mint("_end");
|
|
2106
|
+
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2107
|
+
if (shape.disjoint) {
|
|
2108
|
+
const c0 = codeAt(start, firstChar);
|
|
2109
|
+
shape.arms.forEach((arm, k) => {
|
|
2110
|
+
const fs = shape.firsts[k];
|
|
2111
|
+
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
2112
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2113
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2114
|
+
lines.push(...m.setup);
|
|
2115
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2116
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2117
|
+
lines.push(`${ind2}}`);
|
|
2118
|
+
});
|
|
2119
|
+
} else {
|
|
2120
|
+
const lbl = mint("_alt");
|
|
2121
|
+
lines.push(`${ind2}${lbl}: {`);
|
|
2122
|
+
for (const arm of shape.arms) {
|
|
2123
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2124
|
+
lines.push(...m.setup);
|
|
2125
|
+
lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
2126
|
+
}
|
|
2127
|
+
lines.push(`${ind2}}`);
|
|
2128
|
+
}
|
|
2129
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2130
|
+
}
|
|
1495
2131
|
if (shape.kind === "chars") {
|
|
2132
|
+
const cur = mint("_e");
|
|
1496
2133
|
return {
|
|
1497
2134
|
setup: [
|
|
1498
|
-
|
|
1499
|
-
|
|
2135
|
+
`${ind2}let ${cur} = ${start}`,
|
|
2136
|
+
`${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
|
|
1500
2137
|
],
|
|
1501
|
-
|
|
2138
|
+
// `*` always matches (possibly empty); `+` needs at least one char.
|
|
2139
|
+
ok: shape.minOne ? `${cur} > ${start}` : "true",
|
|
2140
|
+
end: cur
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
if (shape.kind === "ident") {
|
|
2144
|
+
const cur = mint("_e");
|
|
2145
|
+
return {
|
|
2146
|
+
setup: [
|
|
2147
|
+
`${ind2}let ${cur} = ${start}`,
|
|
2148
|
+
`${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
|
|
2149
|
+
`${ind2} ${cur} = ${start} + 1`,
|
|
2150
|
+
`${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
|
|
2151
|
+
`${ind2}}`
|
|
2152
|
+
],
|
|
2153
|
+
ok: `${cur} > ${start}`,
|
|
2154
|
+
end: cur
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
if (shape.kind === "seq") {
|
|
2158
|
+
const endV2 = mint("_e");
|
|
2159
|
+
const okV = mint("_ok");
|
|
2160
|
+
const runCond = (p, at) => {
|
|
2161
|
+
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
2162
|
+
return p.negated ? `!(${c})` : `(${c})`;
|
|
2163
|
+
};
|
|
2164
|
+
const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
|
|
2165
|
+
let first = true;
|
|
2166
|
+
for (const p of shape.parts) {
|
|
2167
|
+
if (p.part === "lit") {
|
|
2168
|
+
const L = p.cps.length;
|
|
2169
|
+
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
2170
|
+
if (p.optional) {
|
|
2171
|
+
lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
2172
|
+
} else {
|
|
2173
|
+
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
2174
|
+
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
2175
|
+
}
|
|
2176
|
+
} else if (p.part === "run" && !p.unbounded) {
|
|
2177
|
+
if (p.min === 1) {
|
|
2178
|
+
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
2179
|
+
lines.push(`${ind2} ${endV2}++`);
|
|
2180
|
+
} else {
|
|
2181
|
+
lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2182
|
+
}
|
|
2183
|
+
} else if (p.part === "run") {
|
|
2184
|
+
const s = mint("_s");
|
|
2185
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
2186
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2187
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
2188
|
+
} else {
|
|
2189
|
+
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
2190
|
+
if (!p.unbounded) {
|
|
2191
|
+
lines.push(...inner.setup);
|
|
2192
|
+
if (p.min === 1) {
|
|
2193
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2194
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2195
|
+
} else {
|
|
2196
|
+
lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
2197
|
+
}
|
|
2198
|
+
} else {
|
|
2199
|
+
const progV = mint("_prog");
|
|
2200
|
+
lines.push(`${ind2} let ${progV} = false`);
|
|
2201
|
+
lines.push(`${ind2} while (true) {`);
|
|
2202
|
+
lines.push(...inner.setup);
|
|
2203
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2204
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2205
|
+
lines.push(`${ind2} ${progV} = true`);
|
|
2206
|
+
lines.push(`${ind2} }`);
|
|
2207
|
+
if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
first = false;
|
|
2211
|
+
}
|
|
2212
|
+
lines.push(`${ind2} ${okV} = true`);
|
|
2213
|
+
lines.push(`${ind2}} while (false)`);
|
|
2214
|
+
lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
|
|
2215
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2216
|
+
}
|
|
2217
|
+
if (shape.kind === "litFold") {
|
|
2218
|
+
const openLen2 = shape.open.length;
|
|
2219
|
+
const endV2 = mint("_end");
|
|
2220
|
+
const checks = shape.open.map((cp, k) => {
|
|
2221
|
+
const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
|
|
2222
|
+
return foldEq(cVar, cp);
|
|
2223
|
+
});
|
|
2224
|
+
return {
|
|
2225
|
+
setup: [
|
|
2226
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2227
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
|
|
2228
|
+
],
|
|
2229
|
+
ok: `${endV2} > ${start}`,
|
|
2230
|
+
end: endV2
|
|
1502
2231
|
};
|
|
1503
2232
|
}
|
|
1504
2233
|
if (shape.kind === "until") {
|
|
2234
|
+
const j2 = mint("_j");
|
|
2235
|
+
const openLen2 = shape.open.length;
|
|
2236
|
+
const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
1505
2237
|
return {
|
|
1506
2238
|
setup: [
|
|
1507
|
-
|
|
1508
|
-
|
|
2239
|
+
`${ind2}let ${j2} = ${start}`,
|
|
2240
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
|
|
2241
|
+
`${ind2} ${j2} = ${start} + ${openLen2}`,
|
|
2242
|
+
`${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
|
|
2243
|
+
`${ind2}}`
|
|
1509
2244
|
],
|
|
1510
|
-
|
|
2245
|
+
// An open-until-terminator always completes (stop char or EOF), so a matched
|
|
2246
|
+
// open literal is a full match; `end === start` iff the open didn't match.
|
|
2247
|
+
ok: `${j2} > ${start}`,
|
|
2248
|
+
end: j2
|
|
1511
2249
|
};
|
|
1512
2250
|
}
|
|
1513
|
-
|
|
2251
|
+
if (shape.kind === "string") {
|
|
2252
|
+
const okV = mint("_ok");
|
|
2253
|
+
const endV2 = mint("_end");
|
|
2254
|
+
const i = mint("_i");
|
|
2255
|
+
const c2 = mint("_c");
|
|
2256
|
+
const hard = stringHardStop(shape);
|
|
2257
|
+
const ltLines = shape.escLineTerm ? [] : (() => {
|
|
2258
|
+
const c3 = mint("_c");
|
|
2259
|
+
return [
|
|
2260
|
+
`${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
|
|
2261
|
+
`${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
|
|
2262
|
+
];
|
|
2263
|
+
})();
|
|
2264
|
+
return {
|
|
2265
|
+
setup: [
|
|
2266
|
+
`${ind2}let ${okV} = false`,
|
|
2267
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2268
|
+
`${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
|
|
2269
|
+
`${ind2} let ${i} = ${start} + 1`,
|
|
2270
|
+
`${ind2} while (${i} < input.length) {`,
|
|
2271
|
+
`${ind2} const ${c2} = input.charCodeAt(${i})`,
|
|
2272
|
+
`${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
|
|
2273
|
+
`${ind2} if (${c2} === ${BACKSLASH}) {`,
|
|
2274
|
+
`${ind2} if (${i} + 1 >= input.length) break`,
|
|
2275
|
+
...ltLines,
|
|
2276
|
+
`${ind2} ${i} += 2`,
|
|
2277
|
+
`${ind2} continue`,
|
|
2278
|
+
`${ind2} }`,
|
|
2279
|
+
...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
|
|
2280
|
+
`${ind2} ${i}++`,
|
|
2281
|
+
`${ind2} }`,
|
|
2282
|
+
`${ind2}}`
|
|
2283
|
+
],
|
|
2284
|
+
ok: okV,
|
|
2285
|
+
end: endV2
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
const j = mint("_j");
|
|
2289
|
+
const endV = mint("_end");
|
|
2290
|
+
const openLen = shape.open.length;
|
|
2291
|
+
const closeLen = shape.close.length;
|
|
2292
|
+
const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
2293
|
+
const closeChk = litCond(j, shape.close);
|
|
1514
2294
|
return {
|
|
1515
2295
|
setup: [
|
|
1516
|
-
|
|
1517
|
-
|
|
2296
|
+
`${ind2}let ${j} = ${start}`,
|
|
2297
|
+
`${ind2}let ${endV} = ${start}`,
|
|
2298
|
+
`${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
|
|
2299
|
+
`${ind2} ${j} = ${start} + ${openLen}`,
|
|
2300
|
+
`${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
|
|
2301
|
+
`${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
|
|
2302
|
+
`${ind2}}`
|
|
1518
2303
|
],
|
|
1519
|
-
|
|
2304
|
+
ok: `${endV} > ${start}`,
|
|
2305
|
+
end: endV
|
|
1520
2306
|
};
|
|
1521
2307
|
}
|
|
1522
|
-
function
|
|
1523
|
-
const
|
|
1524
|
-
return ` if (${
|
|
2308
|
+
function scanBranch(shape, mint) {
|
|
2309
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
2310
|
+
return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
|
|
1525
2311
|
}
|
|
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) {
|
|
2312
|
+
function scanBranchLabeled(shape, kindIndex, mint) {
|
|
2313
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1534
2314
|
return [
|
|
2315
|
+
...m.setup,
|
|
2316
|
+
` if (${m.end} > _e) {`,
|
|
1535
2317
|
` 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");
|
|
2318
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
|
|
2319
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
2320
|
+
` }`,
|
|
2321
|
+
` _e = ${m.end}`,
|
|
2322
|
+
` continue`,
|
|
2323
|
+
` }`
|
|
2324
|
+
].join("\n");
|
|
1547
2325
|
}
|
|
1548
2326
|
|
|
1549
2327
|
// src/compiler/trivia-fast-path.ts
|
|
2328
|
+
function makeMint() {
|
|
2329
|
+
let n = 0;
|
|
2330
|
+
return (prefix = "_v") => `${prefix}${n++}`;
|
|
2331
|
+
}
|
|
1550
2332
|
function unwrapTrivia2(p) {
|
|
1551
2333
|
let cur = p;
|
|
1552
2334
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1563,14 +2345,14 @@ function choiceArms(p) {
|
|
|
1563
2345
|
return null;
|
|
1564
2346
|
}
|
|
1565
2347
|
function armShape(arm) {
|
|
1566
|
-
const
|
|
1567
|
-
return
|
|
2348
|
+
const d = getCoreRegexDef(arm);
|
|
2349
|
+
return d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1568
2350
|
}
|
|
1569
2351
|
function analyzeTriviaFastPath(trivia2) {
|
|
1570
2352
|
const core = unwrapTrivia2(trivia2);
|
|
1571
|
-
const
|
|
1572
|
-
if (
|
|
1573
|
-
const shape =
|
|
2353
|
+
const directDef = getCoreRegexDef(core);
|
|
2354
|
+
if (directDef) {
|
|
2355
|
+
const shape = scanShapeFromRegex(directDef.source, directDef.flags);
|
|
1574
2356
|
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1575
2357
|
}
|
|
1576
2358
|
const inner = oneOrMoreInner(core);
|
|
@@ -1594,10 +2376,11 @@ var CAP_RECORD = [
|
|
|
1594
2376
|
` }`
|
|
1595
2377
|
].join("\n");
|
|
1596
2378
|
function composeFastLoop(shapes) {
|
|
2379
|
+
const mint = makeMint();
|
|
1597
2380
|
return [
|
|
1598
2381
|
` while (_e < input.length) {`,
|
|
1599
2382
|
` const c = input.charCodeAt(_e)`,
|
|
1600
|
-
...shapes.map(scanBranch),
|
|
2383
|
+
...shapes.map((s) => scanBranch(s, mint)),
|
|
1601
2384
|
` break`,
|
|
1602
2385
|
` }`
|
|
1603
2386
|
].join("\n");
|
|
@@ -1617,20 +2400,21 @@ function analyzeLabeledScannableRun(trivia2) {
|
|
|
1617
2400
|
if (!spec) return null;
|
|
1618
2401
|
const out = [];
|
|
1619
2402
|
for (const arm of spec.arms) {
|
|
1620
|
-
const
|
|
1621
|
-
const shape =
|
|
2403
|
+
const d = getCoreRegexDef(arm.parser);
|
|
2404
|
+
const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1622
2405
|
if (!shape) return null;
|
|
1623
2406
|
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1624
2407
|
}
|
|
1625
2408
|
return out;
|
|
1626
2409
|
}
|
|
1627
2410
|
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
2411
|
+
const mint = makeMint();
|
|
1628
2412
|
return [
|
|
1629
2413
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1630
2414
|
` let _e = _pos`,
|
|
1631
2415
|
` while (_e < input.length) {`,
|
|
1632
2416
|
` const c = input.charCodeAt(_e)`,
|
|
1633
|
-
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
2417
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
|
|
1634
2418
|
` break`,
|
|
1635
2419
|
` }`,
|
|
1636
2420
|
` return _e`,
|
|
@@ -1707,6 +2491,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
|
|
|
1707
2491
|
].join("\n");
|
|
1708
2492
|
}
|
|
1709
2493
|
|
|
2494
|
+
// src/compiler/scannable-terminal.ts
|
|
2495
|
+
function emitScannableTerminal(shape, c) {
|
|
2496
|
+
const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
|
|
2497
|
+
const stmts = [...m.setup];
|
|
2498
|
+
if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
|
|
2499
|
+
stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
|
|
2500
|
+
return { stmts, endVar: m.end };
|
|
2501
|
+
}
|
|
2502
|
+
|
|
1710
2503
|
// src/compiler/inline-build.ts
|
|
1711
2504
|
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
2505
|
function escapeRegExp(s) {
|
|
@@ -1828,14 +2621,45 @@ function failReturn(expected, posExpr) {
|
|
|
1828
2621
|
function failReturnArr(expectedArr, posExpr) {
|
|
1829
2622
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1830
2623
|
}
|
|
2624
|
+
function hoistExpected(ctx, constArrSource) {
|
|
2625
|
+
let name = ctx.expectedMap.get(constArrSource);
|
|
2626
|
+
if (name === void 0) {
|
|
2627
|
+
name = `_fx${ctx.expectedDecls.length}`;
|
|
2628
|
+
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
2629
|
+
ctx.expectedMap.set(constArrSource, name);
|
|
2630
|
+
}
|
|
2631
|
+
return name;
|
|
2632
|
+
}
|
|
1831
2633
|
function failBody(ctx, expected, posExpr) {
|
|
1832
|
-
if (ctx.failLabel)
|
|
2634
|
+
if (ctx.failLabel) {
|
|
2635
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2636
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
2637
|
+
}
|
|
1833
2638
|
return failReturn(expected, posExpr);
|
|
1834
2639
|
}
|
|
1835
|
-
function failArrBody(ctx, expectedArr, posExpr) {
|
|
1836
|
-
if (ctx.failLabel)
|
|
2640
|
+
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
2641
|
+
if (ctx.failLabel) {
|
|
2642
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2643
|
+
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
2644
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
2645
|
+
}
|
|
2646
|
+
if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1837
2647
|
return failReturnArr(expectedArr, posExpr);
|
|
1838
2648
|
}
|
|
2649
|
+
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
2650
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
2651
|
+
}
|
|
2652
|
+
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
2653
|
+
if (srcCtx !== "_ctx") {
|
|
2654
|
+
if (ctx.failLabel) {
|
|
2655
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2656
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
2657
|
+
}
|
|
2658
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
2659
|
+
}
|
|
2660
|
+
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
2661
|
+
return resultFromRecorded();
|
|
2662
|
+
}
|
|
1839
2663
|
function emitIfFail(ctx, cond, body) {
|
|
1840
2664
|
return [
|
|
1841
2665
|
`${ind(ctx)}if (${cond}) {`,
|
|
@@ -1871,13 +2695,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
|
1871
2695
|
`}`
|
|
1872
2696
|
].join("\n"));
|
|
1873
2697
|
}
|
|
1874
|
-
function emitNamedFnCall(ctx, fnName, pos
|
|
2698
|
+
function emitNamedFnCall(ctx, fnName, pos) {
|
|
1875
2699
|
const vv = v(ctx, "_pfv");
|
|
1876
2700
|
const ev = v(ctx, "_pfe");
|
|
1877
2701
|
return {
|
|
1878
2702
|
stmts: [
|
|
1879
2703
|
`${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
|
|
1880
|
-
|
|
2704
|
+
// ref/lazy returns the inner failure verbatim — propagate the recorded
|
|
2705
|
+
// deepest failure (shares _ctx with the named fn), not a "parser" label.
|
|
2706
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
|
|
1881
2707
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
1882
2708
|
],
|
|
1883
2709
|
valueVar: vv,
|
|
@@ -1987,6 +2813,27 @@ function firstSetCond(codeVar, fs) {
|
|
|
1987
2813
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1988
2814
|
).join(" || ");
|
|
1989
2815
|
}
|
|
2816
|
+
var SWITCH_RANGE_LIMIT = 4;
|
|
2817
|
+
var SWITCH_MAX_CASES = 48;
|
|
2818
|
+
var SWITCH_MIN_CASES = 3;
|
|
2819
|
+
var _forceDisjointIf = false;
|
|
2820
|
+
function planDisjointDispatch(parsers) {
|
|
2821
|
+
const cases = [];
|
|
2822
|
+
let total = 0;
|
|
2823
|
+
for (const p of parsers) {
|
|
2824
|
+
const fs = p._meta.firstSet;
|
|
2825
|
+
if (fs.kind !== "ranges") return { kind: "if" };
|
|
2826
|
+
const pts = [];
|
|
2827
|
+
for (const r of fs.ranges) {
|
|
2828
|
+
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2829
|
+
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2830
|
+
}
|
|
2831
|
+
total += pts.length;
|
|
2832
|
+
if (total > SWITCH_MAX_CASES) return { kind: "if" };
|
|
2833
|
+
cases.push(pts);
|
|
2834
|
+
}
|
|
2835
|
+
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2836
|
+
}
|
|
1990
2837
|
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1991
2838
|
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1992
2839
|
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
@@ -2100,18 +2947,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2100
2947
|
return true;
|
|
2101
2948
|
}
|
|
2102
2949
|
}
|
|
2103
|
-
function emitFallible(inner, ctx, pos) {
|
|
2950
|
+
function emitFallible(inner, ctx, pos, swallow = false) {
|
|
2104
2951
|
const lbl = v(ctx, "_lbl");
|
|
2105
2952
|
const okV = `${lbl}ok`;
|
|
2106
2953
|
const valV = `${lbl}v`;
|
|
2107
2954
|
const endV = `${lbl}e`;
|
|
2108
2955
|
const savedLabel = ctx.failLabel;
|
|
2109
2956
|
const savedIndent = ctx.indent;
|
|
2957
|
+
const savedRecord = ctx.recordFail;
|
|
2110
2958
|
ctx.failLabel = lbl;
|
|
2111
2959
|
ctx.indent = savedIndent + 1;
|
|
2960
|
+
if (swallow) ctx.recordFail = false;
|
|
2112
2961
|
const r = emit(inner, ctx, pos);
|
|
2113
2962
|
ctx.failLabel = savedLabel;
|
|
2114
2963
|
ctx.indent = savedIndent;
|
|
2964
|
+
ctx.recordFail = savedRecord;
|
|
2115
2965
|
const ind0 = ind(ctx);
|
|
2116
2966
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
2117
2967
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
@@ -2134,6 +2984,7 @@ function emitFallible(inner, ctx, pos) {
|
|
|
2134
2984
|
];
|
|
2135
2985
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
2136
2986
|
}
|
|
2987
|
+
var CHARCODE_CHAIN_MAX = 16;
|
|
2137
2988
|
function emitLit(def, ctx, pos) {
|
|
2138
2989
|
const { value, caseInsensitive } = def;
|
|
2139
2990
|
const len = value.length;
|
|
@@ -2156,7 +3007,7 @@ function emitLit(def, ctx, pos) {
|
|
|
2156
3007
|
...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
|
|
2157
3008
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
2158
3009
|
);
|
|
2159
|
-
} else if (len <=
|
|
3010
|
+
} else if (len <= CHARCODE_CHAIN_MAX) {
|
|
2160
3011
|
const checks = Array.from(
|
|
2161
3012
|
{ length: len },
|
|
2162
3013
|
(_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
|
|
@@ -2178,7 +3029,55 @@ function emitLit(def, ctx, pos) {
|
|
|
2178
3029
|
function escapeKeywordRe(s) {
|
|
2179
3030
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2180
3031
|
}
|
|
3032
|
+
function emitKeywordsFast(def, ctx, pos) {
|
|
3033
|
+
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
3034
|
+
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
3035
|
+
if (def.caseInsensitive && def.boundary) return null;
|
|
3036
|
+
let boundary = null;
|
|
3037
|
+
if (def.boundary) {
|
|
3038
|
+
let body = def.boundary;
|
|
3039
|
+
const negated = body.startsWith("^");
|
|
3040
|
+
if (negated) body = body.slice(1);
|
|
3041
|
+
const ranges = parseClassRanges(body);
|
|
3042
|
+
if (!ranges) return null;
|
|
3043
|
+
boundary = { ranges, negated };
|
|
3044
|
+
}
|
|
3045
|
+
const mint = (prefix = "_v") => v(ctx, prefix);
|
|
3046
|
+
const lbl = v(ctx, "_kwLbl");
|
|
3047
|
+
const valV = v(ctx, "_kwv");
|
|
3048
|
+
const endV = v(ctx, "_kwe");
|
|
3049
|
+
const bodyInd = ind(ctx) + " ";
|
|
3050
|
+
const tries = [];
|
|
3051
|
+
for (const w of def.words) {
|
|
3052
|
+
const cps = Array.from(w, (ch) => ch.codePointAt(0));
|
|
3053
|
+
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
3054
|
+
if (boundary) {
|
|
3055
|
+
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
3056
|
+
}
|
|
3057
|
+
const m = emitShapeMatch(shape, pos, mint, bodyInd);
|
|
3058
|
+
tries.push(
|
|
3059
|
+
...m.setup,
|
|
3060
|
+
// Slice from input rather than reusing the literal word: `caseInsensitive`
|
|
3061
|
+
// must return the text as it actually appeared (e.g. "ABC" for keyword
|
|
3062
|
+
// "abc"), matching what the original `RegExp.exec()[0]` returned.
|
|
3063
|
+
`${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
|
|
3064
|
+
);
|
|
3065
|
+
}
|
|
3066
|
+
const stmts = [
|
|
3067
|
+
`${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
|
|
3068
|
+
`${ind(ctx)}${lbl}: {`,
|
|
3069
|
+
...tries,
|
|
3070
|
+
`${ind(ctx)}}`,
|
|
3071
|
+
// Every word has length >= 1 (checked above), so a real match always
|
|
3072
|
+
// advances past `pos` — `endV === pos` only happens when no candidate matched.
|
|
3073
|
+
...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
|
|
3074
|
+
];
|
|
3075
|
+
stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
|
|
3076
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3077
|
+
}
|
|
2181
3078
|
function emitKeywords(def, ctx, pos) {
|
|
3079
|
+
const fast = emitKeywordsFast(def, ctx, pos);
|
|
3080
|
+
if (fast) return fast;
|
|
2182
3081
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
2183
3082
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
2184
3083
|
const flags = def.caseInsensitive ? "iuy" : "uy";
|
|
@@ -2203,6 +3102,22 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2203
3102
|
return { stmts, valueVar: vv, endVar };
|
|
2204
3103
|
}
|
|
2205
3104
|
function emitRegex(def, ctx, pos) {
|
|
3105
|
+
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
3106
|
+
const shape = scanShapeFromRegex(def.source, def.flags);
|
|
3107
|
+
if (shape) {
|
|
3108
|
+
const vv2 = v(ctx);
|
|
3109
|
+
const scanned = emitScannableTerminal(shape, {
|
|
3110
|
+
ind: ind(ctx),
|
|
3111
|
+
pos,
|
|
3112
|
+
valueVar: vv2,
|
|
3113
|
+
failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
|
|
3114
|
+
fresh: (prefix) => v(ctx, prefix)
|
|
3115
|
+
});
|
|
3116
|
+
if (scanned) {
|
|
3117
|
+
const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
|
|
3118
|
+
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
2206
3121
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2207
3122
|
const key = `${def.optimizedSource}/${flags}`;
|
|
2208
3123
|
let rName = ctx.regexMap.get(key);
|
|
@@ -2213,7 +3128,6 @@ function emitRegex(def, ctx, pos) {
|
|
|
2213
3128
|
}
|
|
2214
3129
|
const mv = v(ctx, "_m");
|
|
2215
3130
|
const vv = v(ctx);
|
|
2216
|
-
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2217
3131
|
const stmts = [
|
|
2218
3132
|
`${ind(ctx)}${rName}.lastIndex = ${pos}`,
|
|
2219
3133
|
`${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
|
|
@@ -2272,15 +3186,68 @@ function emitSeq(def, ctx, pos) {
|
|
|
2272
3186
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
2273
3187
|
return { stmts, valueVar: arrV, endVar };
|
|
2274
3188
|
}
|
|
3189
|
+
function staticExpectedArr(parsers) {
|
|
3190
|
+
return JSON.stringify(parsers.flatMap((p) => {
|
|
3191
|
+
const e = staticExpected(p);
|
|
3192
|
+
return e.length > 0 ? e : [p._tag];
|
|
3193
|
+
}));
|
|
3194
|
+
}
|
|
3195
|
+
function failsAtStart(p) {
|
|
3196
|
+
const d = p._def;
|
|
3197
|
+
switch (d.tag) {
|
|
3198
|
+
case "literal":
|
|
3199
|
+
case "regex":
|
|
3200
|
+
case "keywords":
|
|
3201
|
+
case "guard":
|
|
3202
|
+
case "not":
|
|
3203
|
+
return true;
|
|
3204
|
+
case "transform":
|
|
3205
|
+
case "label":
|
|
3206
|
+
return failsAtStart(d.parser);
|
|
3207
|
+
default:
|
|
3208
|
+
return false;
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
function armStaticExpected(ctx, p) {
|
|
3212
|
+
return hoistExpected(ctx, staticExpectedArr([p]));
|
|
3213
|
+
}
|
|
3214
|
+
function canMatchEmptyAtStart(p) {
|
|
3215
|
+
const d = p._def;
|
|
3216
|
+
switch (d.tag) {
|
|
3217
|
+
case "regex":
|
|
3218
|
+
return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
|
|
3219
|
+
case "optional":
|
|
3220
|
+
return true;
|
|
3221
|
+
case "many":
|
|
3222
|
+
return true;
|
|
3223
|
+
case "transform":
|
|
3224
|
+
case "label":
|
|
3225
|
+
return canMatchEmptyAtStart(d.parser);
|
|
3226
|
+
case "literal":
|
|
3227
|
+
return d.value.length === 0;
|
|
3228
|
+
default:
|
|
3229
|
+
return false;
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
function needsFirstSetGuard(p) {
|
|
3233
|
+
const fs = p._meta.firstSet;
|
|
3234
|
+
return fs.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
3235
|
+
}
|
|
3236
|
+
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
3237
|
+
if (failsAtStart(p)) {
|
|
3238
|
+
const r = emit(p, ctx, pos);
|
|
3239
|
+
return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
|
|
3240
|
+
}
|
|
3241
|
+
const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3242
|
+
return [
|
|
3243
|
+
...stmts,
|
|
3244
|
+
...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
|
|
3245
|
+
`${ind(ctx)}${valV} = ${valVar}`,
|
|
3246
|
+
`${ind(ctx)}${endV} = ${endVar}`
|
|
3247
|
+
];
|
|
3248
|
+
}
|
|
2275
3249
|
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
|
-
);
|
|
3250
|
+
const allExpected = staticExpectedArr(def.parsers);
|
|
2284
3251
|
if (def.disjoint) {
|
|
2285
3252
|
const codeV = v(ctx, "_code");
|
|
2286
3253
|
const valV = v(ctx, "_chv");
|
|
@@ -2289,6 +3256,27 @@ function emitChoice(def, ctx, pos) {
|
|
|
2289
3256
|
`${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
|
|
2290
3257
|
`${ind(ctx)}let ${valV}, ${endV} = ${pos}`
|
|
2291
3258
|
];
|
|
3259
|
+
const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
|
|
3260
|
+
if (plan.kind === "switch") {
|
|
3261
|
+
stmts.push(`${ind(ctx)}switch (${codeV}) {`);
|
|
3262
|
+
ctx.indent++;
|
|
3263
|
+
for (let i = 0; i < def.parsers.length; i++) {
|
|
3264
|
+
const p = def.parsers[i];
|
|
3265
|
+
for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
|
|
3266
|
+
stmts.push(`${ind(ctx)}{`);
|
|
3267
|
+
ctx.indent++;
|
|
3268
|
+
stmts.push(
|
|
3269
|
+
...emitDisjointArm(p, ctx, pos, valV, endV),
|
|
3270
|
+
`${ind(ctx)}break`
|
|
3271
|
+
);
|
|
3272
|
+
ctx.indent--;
|
|
3273
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3274
|
+
}
|
|
3275
|
+
stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
|
|
3276
|
+
ctx.indent--;
|
|
3277
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3278
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3279
|
+
}
|
|
2292
3280
|
let first = true;
|
|
2293
3281
|
for (const p of def.parsers) {
|
|
2294
3282
|
const cond = firstSetCond(codeV, p._meta.firstSet);
|
|
@@ -2296,10 +3284,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2296
3284
|
first = false;
|
|
2297
3285
|
stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
|
|
2298
3286
|
ctx.indent++;
|
|
2299
|
-
|
|
2300
|
-
stmts.push(...r.stmts);
|
|
2301
|
-
stmts.push(`${ind(ctx)}${valV} = ${r.valueVar}`);
|
|
2302
|
-
stmts.push(`${ind(ctx)}${endV} = ${r.endVar}`);
|
|
3287
|
+
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
|
|
2303
3288
|
ctx.indent--;
|
|
2304
3289
|
stmts.push(`${ind(ctx)}}`);
|
|
2305
3290
|
}
|
|
@@ -2308,7 +3293,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2308
3293
|
}
|
|
2309
3294
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
2310
3295
|
}
|
|
2311
|
-
function emitGreedyClassify(def, superIndex,
|
|
3296
|
+
function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
2312
3297
|
const superParser = def.parsers[superIndex];
|
|
2313
3298
|
const regexDef = getCoreRegexDef(superParser);
|
|
2314
3299
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
@@ -2323,10 +3308,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
|
|
|
2323
3308
|
const wordV = v(ctx, "_gw");
|
|
2324
3309
|
const endV = v(ctx, "_ge");
|
|
2325
3310
|
const valV = v(ctx, "_gcv");
|
|
3311
|
+
const regexExpected = JSON.stringify(staticExpected(superParser));
|
|
2326
3312
|
const stmts = [
|
|
2327
3313
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
2328
3314
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
2329
|
-
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx,
|
|
3315
|
+
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
|
|
2330
3316
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
2331
3317
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
|
|
2332
3318
|
`${ind(ctx)}let ${valV}`
|
|
@@ -2384,18 +3370,25 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2384
3370
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
2385
3371
|
return { stmts, valueVar: valV, endVar: endV };
|
|
2386
3372
|
}
|
|
2387
|
-
function emitFirstMatch(def,
|
|
3373
|
+
function emitFirstMatch(def, ctx, pos) {
|
|
2388
3374
|
const resValV = v(ctx, "_crv");
|
|
2389
3375
|
const resEndV = v(ctx, "_cre");
|
|
2390
3376
|
const resOkV = v(ctx, "_crok");
|
|
3377
|
+
const codeV = v(ctx, "_chcode");
|
|
3378
|
+
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2391
3379
|
const ind0 = ind(ctx);
|
|
2392
3380
|
const stmts = [
|
|
2393
|
-
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false
|
|
3381
|
+
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
3382
|
+
`${ind0}let ${slots.join(", ")}`,
|
|
3383
|
+
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
2394
3384
|
];
|
|
2395
3385
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2396
3386
|
const p = def.parsers[i];
|
|
2397
3387
|
const gate = def.gates[i];
|
|
2398
3388
|
const autoNot = def.autoNot[i];
|
|
3389
|
+
const atStart = failsAtStart(p);
|
|
3390
|
+
const staticFx = armStaticExpected(ctx, p);
|
|
3391
|
+
const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
2399
3392
|
let gateCond = null;
|
|
2400
3393
|
if (gate) {
|
|
2401
3394
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -2410,49 +3403,53 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2410
3403
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2411
3404
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2412
3405
|
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
3406
|
+
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
2413
3407
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2414
|
-
ctx
|
|
2415
|
-
|
|
3408
|
+
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
3409
|
+
ctx.indent += fsGuard ? 2 : 1;
|
|
2416
3410
|
if (markLeaves) {
|
|
2417
3411
|
stmts.push(
|
|
2418
|
-
`${
|
|
2419
|
-
`${
|
|
2420
|
-
`${
|
|
2421
|
-
`${
|
|
3412
|
+
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
3413
|
+
`${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
|
|
3414
|
+
`${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
3415
|
+
`${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
|
|
2422
3416
|
);
|
|
2423
3417
|
}
|
|
2424
|
-
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3418
|
+
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
|
|
2425
3419
|
stmts.push(...armStmts);
|
|
2426
|
-
ctx.indent--;
|
|
2427
3420
|
if (autoNot && autoNot.length > 0) {
|
|
2428
3421
|
const anCode = v(ctx, "_anc");
|
|
2429
3422
|
const rejectCond = autoNot.map(
|
|
2430
3423
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
|
|
2431
3424
|
).join(" || ");
|
|
2432
|
-
stmts.push(`${
|
|
2433
|
-
stmts.push(`${
|
|
2434
|
-
stmts.push(`${
|
|
2435
|
-
stmts.push(`${
|
|
2436
|
-
stmts.push(`${
|
|
2437
|
-
stmts.push(`${
|
|
2438
|
-
stmts.push(`${
|
|
2439
|
-
stmts.push(`${
|
|
2440
|
-
|
|
3425
|
+
stmts.push(`${ind(ctx)}if (${okVar}) {`);
|
|
3426
|
+
stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
|
|
3427
|
+
stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
|
|
3428
|
+
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
3429
|
+
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
3430
|
+
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
3431
|
+
stmts.push(`${ind(ctx)} }`);
|
|
3432
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3433
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
3434
|
+
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
2441
3435
|
} else {
|
|
2442
|
-
stmts.push(`${
|
|
2443
|
-
|
|
3436
|
+
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
3437
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
|
|
2444
3438
|
}
|
|
3439
|
+
ctx.indent -= fsGuard ? 2 : 1;
|
|
3440
|
+
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
2445
3441
|
stmts.push(`${ind0}}`);
|
|
2446
3442
|
}
|
|
2447
|
-
|
|
3443
|
+
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
3444
|
+
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
2448
3445
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
2449
3446
|
}
|
|
2450
3447
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
2451
3448
|
if (strategy.tag === "greedyClassify")
|
|
2452
|
-
return emitGreedyClassify(def, strategy.superIndex,
|
|
3449
|
+
return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
|
|
2453
3450
|
if (strategy.tag === "literalsLongestFirst")
|
|
2454
3451
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
2455
|
-
return emitFirstMatch(def,
|
|
3452
|
+
return emitFirstMatch(def, ctx, pos);
|
|
2456
3453
|
}
|
|
2457
3454
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
2458
3455
|
const def = p._def;
|
|
@@ -2473,7 +3470,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2473
3470
|
function emitLiteralCondition(litVal, pos) {
|
|
2474
3471
|
const len = litVal.length;
|
|
2475
3472
|
if (len === 0) return "true";
|
|
2476
|
-
if (len >
|
|
3473
|
+
if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
|
|
2477
3474
|
const checks = [`${pos} + ${len} <= input.length`];
|
|
2478
3475
|
for (let i = 0; i < len; i++) {
|
|
2479
3476
|
const code = litVal.codePointAt(i);
|
|
@@ -2522,7 +3519,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2522
3519
|
itemPos = npV;
|
|
2523
3520
|
}
|
|
2524
3521
|
}
|
|
2525
|
-
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
3522
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
2526
3523
|
stmts.push(
|
|
2527
3524
|
...iterStmts,
|
|
2528
3525
|
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
|
|
@@ -2536,7 +3533,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2536
3533
|
function emitOptional(def, ctx, pos) {
|
|
2537
3534
|
const valV = v(ctx, "_opt");
|
|
2538
3535
|
const endV = v(ctx, "_opte");
|
|
2539
|
-
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
3536
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2540
3537
|
const ind0 = ind(ctx);
|
|
2541
3538
|
const stmts = [
|
|
2542
3539
|
...lblStmts,
|
|
@@ -2548,7 +3545,7 @@ function emitOptional(def, ctx, pos) {
|
|
|
2548
3545
|
function emitSepBy(_p, def, ctx, pos) {
|
|
2549
3546
|
const arrV = v(ctx, "_arr");
|
|
2550
3547
|
const curV = v(ctx, "_cur");
|
|
2551
|
-
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
3548
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
2552
3549
|
const stmts = [
|
|
2553
3550
|
`${ind(ctx)}const ${arrV} = []`,
|
|
2554
3551
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
@@ -2580,11 +3577,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2580
3577
|
);
|
|
2581
3578
|
sepAtPos = spV;
|
|
2582
3579
|
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}; `;
|
|
2583
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3580
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2584
3581
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
2585
3582
|
const npV = v(ctx, "_np");
|
|
2586
3583
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
2587
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3584
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2588
3585
|
stmts.push(
|
|
2589
3586
|
...nextStmts,
|
|
2590
3587
|
// item failed → unwind the separator too, back to the end of the last item
|
|
@@ -2597,11 +3594,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2597
3594
|
const spV = v(ctx, "_sp");
|
|
2598
3595
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
2599
3596
|
sepAtPos = spV;
|
|
2600
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3597
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2601
3598
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2602
3599
|
const npV = v(ctx, "_np");
|
|
2603
3600
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
2604
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3601
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2605
3602
|
stmts.push(
|
|
2606
3603
|
...nextStmts,
|
|
2607
3604
|
`${ind(ctx)}if (!${nextOk}) break`,
|
|
@@ -2618,10 +3615,10 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2618
3615
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
2619
3616
|
);
|
|
2620
3617
|
}
|
|
2621
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3618
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2622
3619
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2623
3620
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
2624
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
|
|
3621
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
2625
3622
|
stmts.push(
|
|
2626
3623
|
...nextStmts,
|
|
2627
3624
|
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
|
|
@@ -2679,11 +3676,12 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2679
3676
|
return { stmts, valueVar: valV, endVar: curV };
|
|
2680
3677
|
}
|
|
2681
3678
|
function emitNot(def, ctx, pos) {
|
|
2682
|
-
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3679
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
3680
|
+
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2683
3681
|
return {
|
|
2684
3682
|
stmts: [
|
|
2685
3683
|
...stmts,
|
|
2686
|
-
...emitIfFail(ctx, okVar, failBody(ctx,
|
|
3684
|
+
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
2687
3685
|
],
|
|
2688
3686
|
valueVar: "null",
|
|
2689
3687
|
endVar: pos
|
|
@@ -2715,7 +3713,7 @@ function emitNode(def, ctx, pos) {
|
|
|
2715
3713
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2716
3714
|
stmts.push(...innerStmts);
|
|
2717
3715
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2718
|
-
stmts.push(...emitIfFail(ctx, `!${okVar}`,
|
|
3716
|
+
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
2719
3717
|
let stV = "undefined";
|
|
2720
3718
|
if (clonesState) {
|
|
2721
3719
|
stV = v(ctx, "_nst");
|
|
@@ -2737,9 +3735,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2737
3735
|
const rv = v(ctx, "_rt");
|
|
2738
3736
|
const vv = v(ctx, "_rtv");
|
|
2739
3737
|
const ev = v(ctx, "_rte");
|
|
3738
|
+
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
3739
|
const stmts = [
|
|
2741
3740
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
2742
|
-
...emitIfFail(ctx, `!${rv}.ok`,
|
|
3741
|
+
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
2743
3742
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
2744
3743
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
2745
3744
|
];
|
|
@@ -2768,15 +3767,18 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2768
3767
|
}
|
|
2769
3768
|
const savedIndent = ctx.indent;
|
|
2770
3769
|
const savedFailLabel = ctx.failLabel;
|
|
3770
|
+
const savedRecord = ctx.recordFail;
|
|
2771
3771
|
ctx.indent = 1;
|
|
2772
3772
|
ctx.failLabel = "_pfail";
|
|
3773
|
+
ctx.recordFail = true;
|
|
2773
3774
|
const r = emit(resolved, ctx, "_pos");
|
|
2774
3775
|
ctx.indent = savedIndent;
|
|
2775
3776
|
ctx.failLabel = savedFailLabel;
|
|
3777
|
+
ctx.recordFail = savedRecord;
|
|
2776
3778
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
2777
3779
|
}
|
|
2778
3780
|
const fnName = ctx.namedParsers.get(p);
|
|
2779
|
-
return emitNamedFnCall(ctx, fnName, pos
|
|
3781
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
2780
3782
|
}
|
|
2781
3783
|
function emitRecover(def, ctx, pos) {
|
|
2782
3784
|
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
@@ -2794,7 +3796,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
2794
3796
|
`${ind0} if (${sentOk}) break`,
|
|
2795
3797
|
`${ind0} ${scanV}++`,
|
|
2796
3798
|
`${ind0} }`,
|
|
2797
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected:
|
|
3799
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
|
|
2798
3800
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2799
3801
|
`${ind0} ${valVar} = ${errV}`,
|
|
2800
3802
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -2952,11 +3954,14 @@ function emit(p, ctx, pos) {
|
|
|
2952
3954
|
ctx.namedParsers.set(innerParser, fnName);
|
|
2953
3955
|
const savedIndent = ctx.indent;
|
|
2954
3956
|
const savedFailLabel = ctx.failLabel;
|
|
3957
|
+
const savedRecord = ctx.recordFail;
|
|
2955
3958
|
ctx.indent = 1;
|
|
2956
3959
|
ctx.failLabel = "_pfail";
|
|
3960
|
+
ctx.recordFail = true;
|
|
2957
3961
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
2958
3962
|
ctx.indent = savedIndent;
|
|
2959
3963
|
ctx.failLabel = savedFailLabel;
|
|
3964
|
+
ctx.recordFail = savedRecord;
|
|
2960
3965
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
2961
3966
|
}
|
|
2962
3967
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -2967,7 +3972,9 @@ function emit(p, ctx, pos) {
|
|
|
2967
3972
|
stmts: [
|
|
2968
3973
|
`${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
|
|
2969
3974
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
2970
|
-
|
|
3975
|
+
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3976
|
+
// and propagate the inner failure verbatim (interpreter parity).
|
|
3977
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
|
|
2971
3978
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
2972
3979
|
],
|
|
2973
3980
|
valueVar: vv,
|
|
@@ -3068,7 +4075,7 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3068
4075
|
const recursive = /* @__PURE__ */ new Set();
|
|
3069
4076
|
const descended = /* @__PURE__ */ new Set();
|
|
3070
4077
|
const active = /* @__PURE__ */ new Set();
|
|
3071
|
-
function
|
|
4078
|
+
function walk2(p) {
|
|
3072
4079
|
const def = p._def;
|
|
3073
4080
|
if (def.tag === "lazy") {
|
|
3074
4081
|
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
@@ -3085,13 +4092,13 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3085
4092
|
return;
|
|
3086
4093
|
}
|
|
3087
4094
|
active.add(p);
|
|
3088
|
-
|
|
4095
|
+
walk2(resolved);
|
|
3089
4096
|
active.delete(p);
|
|
3090
4097
|
return;
|
|
3091
4098
|
}
|
|
3092
|
-
for (const child of childrenOf(def))
|
|
4099
|
+
for (const child of childrenOf(def)) walk2(child);
|
|
3093
4100
|
}
|
|
3094
|
-
for (const root of roots)
|
|
4101
|
+
for (const root of roots) walk2(root);
|
|
3095
4102
|
return { counts, recursive };
|
|
3096
4103
|
}
|
|
3097
4104
|
function compile(combinator, mapFnSources) {
|
|
@@ -3100,6 +4107,9 @@ function compile(combinator, mapFnSources) {
|
|
|
3100
4107
|
indent: 1,
|
|
3101
4108
|
regexDecls: [],
|
|
3102
4109
|
regexMap: /* @__PURE__ */ new Map(),
|
|
4110
|
+
expectedDecls: [],
|
|
4111
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4112
|
+
recordFail: true,
|
|
3103
4113
|
mapFns: [],
|
|
3104
4114
|
mapFnSrcs: [],
|
|
3105
4115
|
buildFns: [],
|
|
@@ -3121,6 +4131,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3121
4131
|
const source = [
|
|
3122
4132
|
...emptyTlDecls,
|
|
3123
4133
|
...ctx.regexDecls,
|
|
4134
|
+
...ctx.expectedDecls,
|
|
3124
4135
|
"",
|
|
3125
4136
|
...namedPrelude,
|
|
3126
4137
|
ctx.namedFnDecls.join("\n\n"),
|
|
@@ -3133,6 +4144,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3133
4144
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3134
4145
|
...emptyTlDecls,
|
|
3135
4146
|
...ctx.regexDecls,
|
|
4147
|
+
...ctx.expectedDecls,
|
|
3136
4148
|
collatorDecl,
|
|
3137
4149
|
...namedPrelude,
|
|
3138
4150
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
@@ -3186,12 +4198,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3186
4198
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
3187
4199
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3188
4200
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3189
|
-
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
4201
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3190
4202
|
if (!needsWrapper) return innerFn;
|
|
3191
4203
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3192
4204
|
const hoistedDecls = [
|
|
3193
4205
|
emptyTlDecl,
|
|
3194
4206
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4207
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3195
4208
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3196
4209
|
mfDecl,
|
|
3197
4210
|
buildDecl,
|
|
@@ -3252,6 +4265,7 @@ function recover(combinator, sentinel) {
|
|
|
3252
4265
|
canMatchNewline: true,
|
|
3253
4266
|
isTrivia: false
|
|
3254
4267
|
};
|
|
4268
|
+
const expected = staticExpected(combinator);
|
|
3255
4269
|
return {
|
|
3256
4270
|
_tag: "recover",
|
|
3257
4271
|
_meta: meta,
|
|
@@ -3267,7 +4281,7 @@ function recover(combinator, sentinel) {
|
|
|
3267
4281
|
const error = {
|
|
3268
4282
|
_tag: "parseError",
|
|
3269
4283
|
span: { start: pos, end: scanPos },
|
|
3270
|
-
expected
|
|
4284
|
+
expected
|
|
3271
4285
|
};
|
|
3272
4286
|
ctx._errors?.push(error);
|
|
3273
4287
|
return { ok: true, value: error, span: { start: pos, end: scanPos } };
|
|
@@ -3278,58 +4292,6 @@ function isParseError(value) {
|
|
|
3278
4292
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
3279
4293
|
}
|
|
3280
4294
|
|
|
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
4295
|
// src/combinators/completions.ts
|
|
3334
4296
|
function completionsAt(combinator, input, offset) {
|
|
3335
4297
|
const probe = { offset, best: null };
|
|
@@ -3439,6 +4401,23 @@ function escapeClassChar(ch) {
|
|
|
3439
4401
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3440
4402
|
}
|
|
3441
4403
|
|
|
4404
|
+
// src/combinators/recover-list.ts
|
|
4405
|
+
function tolerantElement(item, sentinel, until) {
|
|
4406
|
+
const el = transform(
|
|
4407
|
+
sequence(not(until), recover(item, sentinel)),
|
|
4408
|
+
([, value]) => value
|
|
4409
|
+
);
|
|
4410
|
+
if (el._def.tag === "transform") el._def.fnSrc = "([, value]) => value";
|
|
4411
|
+
return el;
|
|
4412
|
+
}
|
|
4413
|
+
function sepByRecover(item, separator, until) {
|
|
4414
|
+
const sentinel = choice(separator, until);
|
|
4415
|
+
return sepBy(tolerantElement(item, sentinel, until), separator);
|
|
4416
|
+
}
|
|
4417
|
+
function manyRecover(item, until) {
|
|
4418
|
+
return many(tolerantElement(item, until, until));
|
|
4419
|
+
}
|
|
4420
|
+
|
|
3442
4421
|
// src/functional/doc.ts
|
|
3443
4422
|
function isNode(x) {
|
|
3444
4423
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
@@ -3500,7 +4479,7 @@ function graftAndShift(root, path, newNode, delta) {
|
|
|
3500
4479
|
children: newChildren
|
|
3501
4480
|
};
|
|
3502
4481
|
}
|
|
3503
|
-
var
|
|
4482
|
+
var ParseDocImpl = class _ParseDocImpl {
|
|
3504
4483
|
_registry;
|
|
3505
4484
|
_rootRule;
|
|
3506
4485
|
_opts;
|
|
@@ -3517,7 +4496,7 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
|
3517
4496
|
}
|
|
3518
4497
|
edit(from, to, replacement) {
|
|
3519
4498
|
const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
|
|
3520
|
-
const reparse = () =>
|
|
4499
|
+
const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
|
|
3521
4500
|
if (!this.tree) return reparse();
|
|
3522
4501
|
const delta = replacement.length - (to - from);
|
|
3523
4502
|
const found = findContaining(this.tree, from);
|
|
@@ -3539,25 +4518,25 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
|
3539
4518
|
if (r.span.end === node2.span.end + delta) {
|
|
3540
4519
|
if (delta === 0) {
|
|
3541
4520
|
const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
|
|
3542
|
-
return new
|
|
4521
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
|
|
3543
4522
|
}
|
|
3544
4523
|
if (this._opts.rebuild) return reparse();
|
|
3545
4524
|
const newTree = graftAndShift(this.tree, path, r.value, delta);
|
|
3546
|
-
return new
|
|
4525
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
3547
4526
|
}
|
|
3548
4527
|
}
|
|
3549
4528
|
return reparse();
|
|
3550
4529
|
}
|
|
3551
4530
|
};
|
|
3552
|
-
function
|
|
4531
|
+
function parseDoc(registry, rootRule, input, opts = {}) {
|
|
3553
4532
|
const ruleFn = registry[rootRule];
|
|
3554
4533
|
if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
|
|
3555
4534
|
const ctx = { trackLines: false, state: opts.state };
|
|
3556
4535
|
const r = ruleFn(input, 0, ctx);
|
|
3557
4536
|
if (r.ok) {
|
|
3558
|
-
return new
|
|
4537
|
+
return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
|
|
3559
4538
|
}
|
|
3560
|
-
return new
|
|
4539
|
+
return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
3561
4540
|
}
|
|
3562
4541
|
|
|
3563
4542
|
// src/cst/trivia-index.ts
|
|
@@ -3626,6 +4605,41 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
3626
4605
|
return { before, after };
|
|
3627
4606
|
}
|
|
3628
4607
|
|
|
4608
|
+
// src/cst/walk.ts
|
|
4609
|
+
function walk(root, visitor, ctx = void 0) {
|
|
4610
|
+
const go = (node2, parent) => {
|
|
4611
|
+
const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
|
|
4612
|
+
const children = node2.children;
|
|
4613
|
+
if (descend !== false && Array.isArray(children)) {
|
|
4614
|
+
for (const child of children) go(child, node2);
|
|
4615
|
+
}
|
|
4616
|
+
visitor.leave?.(node2, parent, ctx);
|
|
4617
|
+
};
|
|
4618
|
+
go(root, null);
|
|
4619
|
+
}
|
|
4620
|
+
function createVisitor(handlers) {
|
|
4621
|
+
const api = {
|
|
4622
|
+
visit(node2) {
|
|
4623
|
+
const handler = node2.type !== void 0 ? handlers[node2.type] : void 0;
|
|
4624
|
+
if (handler) return handler(node2, api);
|
|
4625
|
+
api.visitChildren(node2);
|
|
4626
|
+
return void 0;
|
|
4627
|
+
},
|
|
4628
|
+
visitChildren(node2) {
|
|
4629
|
+
const out = [];
|
|
4630
|
+
const children = node2.children;
|
|
4631
|
+
if (Array.isArray(children)) {
|
|
4632
|
+
for (const child of children) {
|
|
4633
|
+
const r = api.visit(child);
|
|
4634
|
+
if (r !== void 0) out.push(r);
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4637
|
+
return out;
|
|
4638
|
+
}
|
|
4639
|
+
};
|
|
4640
|
+
return (node2) => api.visit(node2);
|
|
4641
|
+
}
|
|
4642
|
+
|
|
3629
4643
|
// src/cst/trivia-entries.ts
|
|
3630
4644
|
function entryOffset(i, stride) {
|
|
3631
4645
|
return i * stride;
|