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.js
CHANGED
|
@@ -746,12 +746,15 @@ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
|
|
|
746
746
|
continue;
|
|
747
747
|
}
|
|
748
748
|
if (c === 47 && input.charCodeAt(pos + 1) === 42) {
|
|
749
|
-
const start = pos;
|
|
750
749
|
let j = pos + 2;
|
|
751
750
|
while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
751
|
+
if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
|
|
752
|
+
const start = pos;
|
|
753
|
+
pos = j + 2;
|
|
754
|
+
chunks.push({ start, end: pos, kindIndex: commentKind });
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
break;
|
|
755
758
|
}
|
|
756
759
|
break;
|
|
757
760
|
}
|
|
@@ -1340,38 +1343,140 @@ function parse(combinator, input, opts = {}) {
|
|
|
1340
1343
|
return withErrors;
|
|
1341
1344
|
}
|
|
1342
1345
|
|
|
1346
|
+
// src/combinators/expect.ts
|
|
1347
|
+
function staticExpected(c) {
|
|
1348
|
+
const def = c._def;
|
|
1349
|
+
switch (def.tag) {
|
|
1350
|
+
case "literal":
|
|
1351
|
+
return [JSON.stringify(def.value)];
|
|
1352
|
+
case "regex":
|
|
1353
|
+
return [`/${def.source}/`];
|
|
1354
|
+
case "keywords":
|
|
1355
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
1356
|
+
case "label":
|
|
1357
|
+
return [def.label];
|
|
1358
|
+
case "choice":
|
|
1359
|
+
return def.parsers.flatMap(staticExpected);
|
|
1360
|
+
case "sequence":
|
|
1361
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
1362
|
+
case "node":
|
|
1363
|
+
case "grammar":
|
|
1364
|
+
case "trivia":
|
|
1365
|
+
case "optional":
|
|
1366
|
+
case "many":
|
|
1367
|
+
case "oneOrMore":
|
|
1368
|
+
case "transform":
|
|
1369
|
+
case "not":
|
|
1370
|
+
return staticExpected(def.parser);
|
|
1371
|
+
case "lazy":
|
|
1372
|
+
return staticExpected(def.thunk());
|
|
1373
|
+
default:
|
|
1374
|
+
return [];
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
function expect(combinator, label2) {
|
|
1378
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
1379
|
+
const meta = {
|
|
1380
|
+
firstSet: combinator._meta.firstSet,
|
|
1381
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1382
|
+
isTrivia: false
|
|
1383
|
+
};
|
|
1384
|
+
return {
|
|
1385
|
+
_tag: "expect",
|
|
1386
|
+
_meta: meta,
|
|
1387
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
1388
|
+
parse(input, pos, ctx) {
|
|
1389
|
+
const result = combinator.parse(input, pos, ctx);
|
|
1390
|
+
if (result.ok) return result;
|
|
1391
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
1392
|
+
ctx._errors?.push(error);
|
|
1393
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1343
1398
|
// src/compiler/scannable-run.ts
|
|
1399
|
+
var BACKSLASH = 92;
|
|
1344
1400
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1345
1401
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1402
|
+
var SPACE_RANGES = [
|
|
1403
|
+
[9, 13],
|
|
1404
|
+
[32, 32],
|
|
1405
|
+
[160, 160],
|
|
1406
|
+
[5760, 5760],
|
|
1407
|
+
[8192, 8202],
|
|
1408
|
+
[8232, 8232],
|
|
1409
|
+
[8233, 8233],
|
|
1410
|
+
[8239, 8239],
|
|
1411
|
+
[8287, 8287],
|
|
1412
|
+
[12288, 12288],
|
|
1413
|
+
[65279, 65279]
|
|
1414
|
+
];
|
|
1415
|
+
function shorthandRanges(ch) {
|
|
1416
|
+
if (ch === "d") return [[48, 57]];
|
|
1417
|
+
if (ch === "s") return SPACE_RANGES;
|
|
1418
|
+
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1419
|
+
}
|
|
1420
|
+
function readUnicodeEscape(body, i) {
|
|
1421
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
1422
|
+
const hex = body.slice(i + 2, i + 6);
|
|
1423
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
1424
|
+
return { cp: Number.parseInt(hex, 16), next: i + 6 };
|
|
1425
|
+
}
|
|
1346
1426
|
function parseClassRanges(body) {
|
|
1347
1427
|
const ranges = [];
|
|
1348
1428
|
let i = 0;
|
|
1349
|
-
const
|
|
1429
|
+
const readAtom = () => {
|
|
1350
1430
|
const ch = body[i];
|
|
1351
1431
|
if (ch === void 0) return null;
|
|
1352
1432
|
if (ch === "\\") {
|
|
1433
|
+
const uni = readUnicodeEscape(body, i);
|
|
1434
|
+
if (uni) {
|
|
1435
|
+
i = uni.next;
|
|
1436
|
+
return { cp: uni.cp };
|
|
1437
|
+
}
|
|
1353
1438
|
const e = body[i + 1];
|
|
1354
1439
|
if (e === void 0) return null;
|
|
1355
1440
|
i += 2;
|
|
1356
|
-
|
|
1441
|
+
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1442
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
1443
|
+
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1444
|
+
return { cp: e.codePointAt(0) };
|
|
1357
1445
|
}
|
|
1358
1446
|
i += ch.length;
|
|
1359
|
-
return ch.codePointAt(0);
|
|
1447
|
+
return { cp: ch.codePointAt(0) };
|
|
1360
1448
|
};
|
|
1361
1449
|
while (i < body.length) {
|
|
1362
|
-
const lo =
|
|
1450
|
+
const lo = readAtom();
|
|
1363
1451
|
if (lo === null) return null;
|
|
1452
|
+
if ("set" in lo) {
|
|
1453
|
+
ranges.push(...lo.set);
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1364
1456
|
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1365
1457
|
i += 1;
|
|
1366
|
-
const hi =
|
|
1367
|
-
if (hi === null) return null;
|
|
1368
|
-
ranges.push([lo, hi]);
|
|
1458
|
+
const hi = readAtom();
|
|
1459
|
+
if (hi === null || "set" in hi) return null;
|
|
1460
|
+
ranges.push([lo.cp, hi.cp]);
|
|
1369
1461
|
} else {
|
|
1370
|
-
ranges.push([lo, lo]);
|
|
1462
|
+
ranges.push([lo.cp, lo.cp]);
|
|
1371
1463
|
}
|
|
1372
1464
|
}
|
|
1373
1465
|
return ranges.length ? ranges : null;
|
|
1374
1466
|
}
|
|
1467
|
+
function classToRanges(cls) {
|
|
1468
|
+
if (cls === "\\d") return shorthandRanges("d");
|
|
1469
|
+
if (cls === "\\w") return shorthandRanges("w");
|
|
1470
|
+
if (cls === "\\s") return shorthandRanges("s");
|
|
1471
|
+
const body = cls.slice(1, -1);
|
|
1472
|
+
if (body.startsWith("^")) return null;
|
|
1473
|
+
return parseClassRanges(body);
|
|
1474
|
+
}
|
|
1475
|
+
var foldEq = (cVar, cp) => {
|
|
1476
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
|
|
1477
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
|
|
1478
|
+
return `${cVar} === ${cp}`;
|
|
1479
|
+
};
|
|
1375
1480
|
function literalCodePoints(frag) {
|
|
1376
1481
|
const out = [];
|
|
1377
1482
|
let i = 0;
|
|
@@ -1390,19 +1495,482 @@ function literalCodePoints(frag) {
|
|
|
1390
1495
|
}
|
|
1391
1496
|
return out.length ? out : null;
|
|
1392
1497
|
}
|
|
1498
|
+
function parseStringShape(source) {
|
|
1499
|
+
const q = source[0];
|
|
1500
|
+
if (q === void 0 || q === "\\" || META.has(q)) return null;
|
|
1501
|
+
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
1502
|
+
const inner = source.slice(1, -1);
|
|
1503
|
+
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
1504
|
+
const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
1505
|
+
let body;
|
|
1506
|
+
let esc;
|
|
1507
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
|
|
1508
|
+
if (m) {
|
|
1509
|
+
body = m[1];
|
|
1510
|
+
esc = m[2];
|
|
1511
|
+
} else {
|
|
1512
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
|
|
1513
|
+
if (m) {
|
|
1514
|
+
esc = m[1];
|
|
1515
|
+
body = m[2];
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
if (body === void 0 || esc === void 0) return null;
|
|
1519
|
+
const excluded = parseClassRanges(body);
|
|
1520
|
+
if (!excluded) return null;
|
|
1521
|
+
const qcp = q.codePointAt(0);
|
|
1522
|
+
const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
|
|
1523
|
+
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1524
|
+
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1525
|
+
}
|
|
1526
|
+
function parseSeqParts(source) {
|
|
1527
|
+
const parts = [];
|
|
1528
|
+
let lit = [];
|
|
1529
|
+
const flush = () => {
|
|
1530
|
+
if (lit.length) {
|
|
1531
|
+
parts.push({ part: "lit", cps: lit, optional: false });
|
|
1532
|
+
lit = [];
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
const runFrom = (ranges, negated, q) => {
|
|
1536
|
+
if (q === "+") {
|
|
1537
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
|
|
1538
|
+
return 1;
|
|
1539
|
+
}
|
|
1540
|
+
if (q === "*") {
|
|
1541
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
|
|
1542
|
+
return 1;
|
|
1543
|
+
}
|
|
1544
|
+
if (q === "?") {
|
|
1545
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
|
|
1546
|
+
return 1;
|
|
1547
|
+
}
|
|
1548
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
|
|
1549
|
+
return 0;
|
|
1550
|
+
};
|
|
1551
|
+
let i = 0;
|
|
1552
|
+
while (i < source.length) {
|
|
1553
|
+
const ch = source[i];
|
|
1554
|
+
if (ch === "[") {
|
|
1555
|
+
let k = i + 1;
|
|
1556
|
+
const neg = source[k] === "^";
|
|
1557
|
+
if (neg) k++;
|
|
1558
|
+
let body = "";
|
|
1559
|
+
while (k < source.length && source[k] !== "]") {
|
|
1560
|
+
if (source[k] === "\\") {
|
|
1561
|
+
body += source[k] + (source[k + 1] ?? "");
|
|
1562
|
+
k += 2;
|
|
1563
|
+
} else {
|
|
1564
|
+
body += source[k];
|
|
1565
|
+
k++;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
if (source[k] !== "]") return null;
|
|
1569
|
+
const ranges = parseClassRanges(body);
|
|
1570
|
+
if (!ranges) return null;
|
|
1571
|
+
flush();
|
|
1572
|
+
i = k + 1;
|
|
1573
|
+
i += runFrom(ranges, neg, source[i]);
|
|
1574
|
+
continue;
|
|
1575
|
+
}
|
|
1576
|
+
if (ch === "\\") {
|
|
1577
|
+
const e = source[i + 1];
|
|
1578
|
+
if (e === "d" || e === "w" || e === "s") {
|
|
1579
|
+
flush();
|
|
1580
|
+
i += 2;
|
|
1581
|
+
i += runFrom(shorthandRanges(e), false, source[i]);
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1584
|
+
const uni = readUnicodeEscape(source, i);
|
|
1585
|
+
let cp2;
|
|
1586
|
+
if (uni) {
|
|
1587
|
+
cp2 = uni.cp;
|
|
1588
|
+
i = uni.next;
|
|
1589
|
+
} else if (e === void 0) return null;
|
|
1590
|
+
else if (e in CLASS_ESCAPES) {
|
|
1591
|
+
cp2 = CLASS_ESCAPES[e];
|
|
1592
|
+
i += 2;
|
|
1593
|
+
} else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1594
|
+
else {
|
|
1595
|
+
cp2 = e.codePointAt(0);
|
|
1596
|
+
i += 2;
|
|
1597
|
+
}
|
|
1598
|
+
if (source[i] === "?") {
|
|
1599
|
+
flush();
|
|
1600
|
+
parts.push({ part: "lit", cps: [cp2], optional: true });
|
|
1601
|
+
i++;
|
|
1602
|
+
} else lit.push(cp2);
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
if (ch === "(") {
|
|
1606
|
+
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
1607
|
+
let k = i + 3;
|
|
1608
|
+
let depth = 1;
|
|
1609
|
+
while (k < source.length && depth > 0) {
|
|
1610
|
+
const c2 = source[k];
|
|
1611
|
+
if (c2 === "\\") {
|
|
1612
|
+
k += 2;
|
|
1613
|
+
continue;
|
|
1614
|
+
}
|
|
1615
|
+
if (c2 === "[") {
|
|
1616
|
+
let j = k + 1;
|
|
1617
|
+
if (source[j] === "^") j++;
|
|
1618
|
+
while (j < source.length && source[j] !== "]") {
|
|
1619
|
+
if (source[j] === "\\") j += 2;
|
|
1620
|
+
else j++;
|
|
1621
|
+
}
|
|
1622
|
+
if (source[j] !== "]") return null;
|
|
1623
|
+
k = j + 1;
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
if (c2 === "(") depth++;
|
|
1627
|
+
else if (c2 === ")") depth--;
|
|
1628
|
+
k++;
|
|
1629
|
+
}
|
|
1630
|
+
if (depth !== 0) return null;
|
|
1631
|
+
const body = source.slice(i + 3, k - 1);
|
|
1632
|
+
const inner = parseScanShape(body);
|
|
1633
|
+
if (!inner || !groupInnerSafe(inner)) return null;
|
|
1634
|
+
const q = source[k];
|
|
1635
|
+
if (q === "{") return null;
|
|
1636
|
+
flush();
|
|
1637
|
+
if (q === "+") {
|
|
1638
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1639
|
+
parts.push({ part: "group", inner, min: 1, unbounded: true });
|
|
1640
|
+
i = k + 1;
|
|
1641
|
+
} else if (q === "*") {
|
|
1642
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1643
|
+
parts.push({ part: "group", inner, min: 0, unbounded: true });
|
|
1644
|
+
i = k + 1;
|
|
1645
|
+
} else if (q === "?") {
|
|
1646
|
+
parts.push({ part: "group", inner, min: 0, unbounded: false });
|
|
1647
|
+
i = k + 1;
|
|
1648
|
+
} else {
|
|
1649
|
+
parts.push({ part: "group", inner, min: 1, unbounded: false });
|
|
1650
|
+
i = k;
|
|
1651
|
+
}
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
if (META.has(ch)) return null;
|
|
1655
|
+
const cp = ch.codePointAt(0);
|
|
1656
|
+
i += ch.length;
|
|
1657
|
+
if (source[i] === "?") {
|
|
1658
|
+
flush();
|
|
1659
|
+
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
1660
|
+
i++;
|
|
1661
|
+
} else lit.push(cp);
|
|
1662
|
+
}
|
|
1663
|
+
flush();
|
|
1664
|
+
return parts.length ? parts : null;
|
|
1665
|
+
}
|
|
1666
|
+
function seqIsUnambiguous(parts) {
|
|
1667
|
+
let hasMandatory = false;
|
|
1668
|
+
for (let k = 0; k < parts.length; k++) {
|
|
1669
|
+
const p = parts[k];
|
|
1670
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1671
|
+
if (mandatory) hasMandatory = true;
|
|
1672
|
+
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1673
|
+
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1674
|
+
if (skippableSingle || isUnboundedRepeat) {
|
|
1675
|
+
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
1676
|
+
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
return hasMandatory;
|
|
1680
|
+
}
|
|
1681
|
+
function rangesOverlap(a, b) {
|
|
1682
|
+
return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
|
|
1683
|
+
}
|
|
1684
|
+
function rangesSubset(sub, sup) {
|
|
1685
|
+
return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
|
|
1686
|
+
}
|
|
1687
|
+
function parseClassOperand(body) {
|
|
1688
|
+
if (body === "\\d" || body === "\\w" || body === "\\s") {
|
|
1689
|
+
return { ranges: shorthandRanges(body[1]), negated: false };
|
|
1690
|
+
}
|
|
1691
|
+
if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
|
|
1692
|
+
let inner = body.slice(1, -1);
|
|
1693
|
+
const negated = inner.startsWith("^");
|
|
1694
|
+
if (negated) inner = inner.slice(1);
|
|
1695
|
+
const ranges = parseClassRanges(inner);
|
|
1696
|
+
return ranges ? { ranges, negated } : null;
|
|
1697
|
+
}
|
|
1698
|
+
const cps = literalCodePoints(body);
|
|
1699
|
+
if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1702
|
+
function classSubset(t, u) {
|
|
1703
|
+
if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1704
|
+
if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1705
|
+
if (t.negated && !u.negated) return false;
|
|
1706
|
+
return rangesSubset(u.ranges, t.ranges);
|
|
1707
|
+
}
|
|
1708
|
+
function classDisjoint(t, u) {
|
|
1709
|
+
if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1710
|
+
if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1711
|
+
if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
|
|
1712
|
+
return false;
|
|
1713
|
+
}
|
|
1714
|
+
function firstSetsDisjoint(a, b) {
|
|
1715
|
+
if (a === "any" || b === "any") return false;
|
|
1716
|
+
return classDisjoint(a, b);
|
|
1717
|
+
}
|
|
1718
|
+
function trailingBacktrackClass(shape) {
|
|
1719
|
+
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1720
|
+
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
1721
|
+
if (shape.kind === "litFold") return null;
|
|
1722
|
+
if (shape.kind === "seq") {
|
|
1723
|
+
const last = shape.parts[shape.parts.length - 1];
|
|
1724
|
+
if (last.part === "lit") return null;
|
|
1725
|
+
if (last.part === "run") {
|
|
1726
|
+
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1727
|
+
return null;
|
|
1728
|
+
}
|
|
1729
|
+
const innerWiggle = trailingBacktrackClass(last.inner);
|
|
1730
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1731
|
+
const requiredOnce = !last.unbounded && last.min === 1;
|
|
1732
|
+
if (requiredOnce) return innerWiggle;
|
|
1733
|
+
const dropExposed = shapeFirstAccept(last.inner);
|
|
1734
|
+
if (dropExposed === "any") return "unsupported";
|
|
1735
|
+
if (innerWiggle === null) return dropExposed;
|
|
1736
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1737
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1738
|
+
}
|
|
1739
|
+
if (shape.kind === "alt") {
|
|
1740
|
+
if (!shape.disjoint) return "unsupported";
|
|
1741
|
+
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1742
|
+
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1743
|
+
const concrete = classes.filter((c) => c !== null);
|
|
1744
|
+
if (concrete.length === 0) return null;
|
|
1745
|
+
const unioned = unionCharSets(concrete);
|
|
1746
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1747
|
+
}
|
|
1748
|
+
return "unsupported";
|
|
1749
|
+
}
|
|
1750
|
+
function lookaheadUnambiguous(inner, operand, negative) {
|
|
1751
|
+
const tail = trailingBacktrackClass(inner);
|
|
1752
|
+
if (tail === "unsupported") return false;
|
|
1753
|
+
if (tail === null) return true;
|
|
1754
|
+
return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
|
|
1755
|
+
}
|
|
1756
|
+
function stripTrailingLookahead(source) {
|
|
1757
|
+
if (!source.endsWith(")")) return null;
|
|
1758
|
+
for (const negative of [true, false]) {
|
|
1759
|
+
const lead = negative ? "(?!" : "(?=";
|
|
1760
|
+
const idx = source.lastIndexOf(lead);
|
|
1761
|
+
if (idx === -1) continue;
|
|
1762
|
+
const body = source.slice(idx + lead.length, -1);
|
|
1763
|
+
const operand = body.length ? parseClassOperand(body) : null;
|
|
1764
|
+
if (!operand) continue;
|
|
1765
|
+
return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
|
|
1766
|
+
}
|
|
1767
|
+
return null;
|
|
1768
|
+
}
|
|
1769
|
+
function unwrapOuterGroup(source) {
|
|
1770
|
+
while (source.startsWith("(?:")) {
|
|
1771
|
+
let depth = 1;
|
|
1772
|
+
let i = 3;
|
|
1773
|
+
let ok = true;
|
|
1774
|
+
while (i < source.length && depth > 0) {
|
|
1775
|
+
const ch = source[i];
|
|
1776
|
+
if (ch === "\\") {
|
|
1777
|
+
i += 2;
|
|
1778
|
+
continue;
|
|
1779
|
+
}
|
|
1780
|
+
if (ch === "[") {
|
|
1781
|
+
let j = i + 1;
|
|
1782
|
+
if (source[j] === "^") j++;
|
|
1783
|
+
while (j < source.length && source[j] !== "]") {
|
|
1784
|
+
if (source[j] === "\\") j += 2;
|
|
1785
|
+
else j++;
|
|
1786
|
+
}
|
|
1787
|
+
if (source[j] !== "]") {
|
|
1788
|
+
ok = false;
|
|
1789
|
+
break;
|
|
1790
|
+
}
|
|
1791
|
+
i = j + 1;
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
if (ch === "(") depth++;
|
|
1795
|
+
else if (ch === ")") depth--;
|
|
1796
|
+
i++;
|
|
1797
|
+
}
|
|
1798
|
+
if (!ok || depth !== 0 || i !== source.length) break;
|
|
1799
|
+
source = source.slice(3, -1);
|
|
1800
|
+
}
|
|
1801
|
+
return source;
|
|
1802
|
+
}
|
|
1803
|
+
function splitTopLevelAlternation(source) {
|
|
1804
|
+
const body = unwrapOuterGroup(source);
|
|
1805
|
+
const parts = [];
|
|
1806
|
+
let depth = 0;
|
|
1807
|
+
let last = 0;
|
|
1808
|
+
let i = 0;
|
|
1809
|
+
while (i < body.length) {
|
|
1810
|
+
const ch = body[i];
|
|
1811
|
+
if (ch === "\\") {
|
|
1812
|
+
i += 2;
|
|
1813
|
+
continue;
|
|
1814
|
+
}
|
|
1815
|
+
if (ch === "[") {
|
|
1816
|
+
let j = i + 1;
|
|
1817
|
+
if (body[j] === "^") j++;
|
|
1818
|
+
while (j < body.length && body[j] !== "]") {
|
|
1819
|
+
if (body[j] === "\\") j += 2;
|
|
1820
|
+
else j++;
|
|
1821
|
+
}
|
|
1822
|
+
if (body[j] !== "]") return null;
|
|
1823
|
+
i = j + 1;
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
if (ch === "(") {
|
|
1827
|
+
depth++;
|
|
1828
|
+
i++;
|
|
1829
|
+
continue;
|
|
1830
|
+
}
|
|
1831
|
+
if (ch === ")") {
|
|
1832
|
+
depth--;
|
|
1833
|
+
if (depth < 0) return null;
|
|
1834
|
+
i++;
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
if (ch === "|" && depth === 0) {
|
|
1838
|
+
parts.push(body.slice(last, i));
|
|
1839
|
+
last = i + 1;
|
|
1840
|
+
i++;
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
i++;
|
|
1844
|
+
}
|
|
1845
|
+
if (depth !== 0) return null;
|
|
1846
|
+
parts.push(body.slice(last));
|
|
1847
|
+
if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
|
|
1848
|
+
return parts;
|
|
1849
|
+
}
|
|
1850
|
+
function shapeCanBeEmpty(shape) {
|
|
1851
|
+
if (shape.kind === "chars") return !shape.minOne;
|
|
1852
|
+
if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
|
|
1853
|
+
if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
|
|
1854
|
+
return false;
|
|
1855
|
+
}
|
|
1856
|
+
function unionCharSets(sets) {
|
|
1857
|
+
if (sets.some((s) => s === "any")) return "any";
|
|
1858
|
+
const concrete = sets;
|
|
1859
|
+
if (concrete.length === 1) return concrete[0];
|
|
1860
|
+
if (concrete.some((s) => s.negated)) return "any";
|
|
1861
|
+
return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
|
|
1862
|
+
}
|
|
1863
|
+
function partFirstAccept(part) {
|
|
1864
|
+
if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
|
|
1865
|
+
if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
|
|
1866
|
+
return shapeFirstAccept(part.inner);
|
|
1867
|
+
}
|
|
1868
|
+
function seqFirstAccept(parts) {
|
|
1869
|
+
const sets = [];
|
|
1870
|
+
for (const p of parts) {
|
|
1871
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1872
|
+
sets.push(partFirstAccept(p));
|
|
1873
|
+
if (mandatory) break;
|
|
1874
|
+
}
|
|
1875
|
+
return unionCharSets(sets);
|
|
1876
|
+
}
|
|
1877
|
+
function groupInnerSafe(shape) {
|
|
1878
|
+
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1879
|
+
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1880
|
+
if (shape.kind === "alt") return shape.disjoint;
|
|
1881
|
+
return false;
|
|
1882
|
+
}
|
|
1883
|
+
function shapeFirstAccept(shape) {
|
|
1884
|
+
if (shapeCanBeEmpty(shape)) return "any";
|
|
1885
|
+
switch (shape.kind) {
|
|
1886
|
+
case "chars":
|
|
1887
|
+
return { ranges: shape.ranges, negated: false };
|
|
1888
|
+
case "ident":
|
|
1889
|
+
return { ranges: shape.head, negated: false };
|
|
1890
|
+
case "until":
|
|
1891
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1892
|
+
case "delimited":
|
|
1893
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1894
|
+
case "string":
|
|
1895
|
+
return { ranges: [[shape.quote, shape.quote]], negated: false };
|
|
1896
|
+
case "litFold": {
|
|
1897
|
+
const cp = shape.open[0];
|
|
1898
|
+
if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
|
|
1899
|
+
if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
|
|
1900
|
+
return { ranges: [[cp, cp]], negated: false };
|
|
1901
|
+
}
|
|
1902
|
+
case "seq":
|
|
1903
|
+
return seqFirstAccept(shape.parts);
|
|
1904
|
+
case "lookahead":
|
|
1905
|
+
return shapeFirstAccept(shape.inner);
|
|
1906
|
+
// zero-width — doesn't change first-char acceptance
|
|
1907
|
+
case "alt": {
|
|
1908
|
+
if (!shape.disjoint) return "any";
|
|
1909
|
+
const sets = shape.firsts.filter((f) => f !== null);
|
|
1910
|
+
return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
function allPairsDisjoint(sets) {
|
|
1915
|
+
for (let i = 0; i < sets.length; i++) {
|
|
1916
|
+
const a = sets[i];
|
|
1917
|
+
if (a === "any") return false;
|
|
1918
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
1919
|
+
const b = sets[j];
|
|
1920
|
+
if (b === "any" || !classDisjoint(a, b)) return false;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
return true;
|
|
1924
|
+
}
|
|
1925
|
+
function parseAlternation(source) {
|
|
1926
|
+
const parts = splitTopLevelAlternation(source);
|
|
1927
|
+
if (!parts) return null;
|
|
1928
|
+
const arms = [];
|
|
1929
|
+
for (const p of parts) {
|
|
1930
|
+
const arm = parseScanShape(p);
|
|
1931
|
+
if (!arm) return null;
|
|
1932
|
+
arms.push(arm);
|
|
1933
|
+
}
|
|
1934
|
+
const firstSets = arms.map(shapeFirstAccept);
|
|
1935
|
+
const disjoint = allPairsDisjoint(firstSets);
|
|
1936
|
+
return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
|
|
1937
|
+
}
|
|
1393
1938
|
function parseScanShape(source) {
|
|
1939
|
+
const alt = parseAlternation(source);
|
|
1940
|
+
if (alt) return alt;
|
|
1941
|
+
const la = stripTrailingLookahead(source);
|
|
1942
|
+
if (la) {
|
|
1943
|
+
const inner = parseScanShapeCore(la.base);
|
|
1944
|
+
if (!inner) return null;
|
|
1945
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
1946
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
1947
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
1948
|
+
}
|
|
1949
|
+
return parseScanShapeCore(source);
|
|
1950
|
+
}
|
|
1951
|
+
function parseScanShapeCore(source) {
|
|
1394
1952
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1395
|
-
if (m) {
|
|
1396
|
-
if (m[1].startsWith("^")) return null;
|
|
1953
|
+
if (m && !m[1].startsWith("^")) {
|
|
1397
1954
|
const ranges = parseClassRanges(m[1]);
|
|
1398
|
-
return ranges ? { kind: "chars", ranges } : null;
|
|
1955
|
+
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1399
1956
|
}
|
|
1957
|
+
m = /^\\([dws])([+*])$/.exec(source);
|
|
1958
|
+
if (m) {
|
|
1959
|
+
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1960
|
+
}
|
|
1961
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
|
|
1962
|
+
if (m) {
|
|
1963
|
+
const head = classToRanges(m[1]);
|
|
1964
|
+
const tail = classToRanges(m[2]);
|
|
1965
|
+
if (head && tail) return { kind: "ident", head, tail };
|
|
1966
|
+
}
|
|
1967
|
+
const str = parseStringShape(source);
|
|
1968
|
+
if (str) return str;
|
|
1400
1969
|
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1401
1970
|
if (m) {
|
|
1402
1971
|
const open = literalCodePoints(m[1]);
|
|
1403
1972
|
const stop = parseClassRanges(m[2]);
|
|
1404
1973
|
if (open && stop) return { kind: "until", open, stop };
|
|
1405
|
-
return null;
|
|
1406
1974
|
}
|
|
1407
1975
|
if (!source.includes("\\\\")) {
|
|
1408
1976
|
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
@@ -1412,68 +1980,278 @@ function parseScanShape(source) {
|
|
|
1412
1980
|
if (open && close) return { kind: "delimited", open, close };
|
|
1413
1981
|
}
|
|
1414
1982
|
}
|
|
1983
|
+
const parts = parseSeqParts(source);
|
|
1984
|
+
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1415
1985
|
return null;
|
|
1416
1986
|
}
|
|
1987
|
+
function scanShapeFromRegex(source, flags) {
|
|
1988
|
+
if (/[msu]/.test(flags)) return null;
|
|
1989
|
+
if (/i/.test(flags)) {
|
|
1990
|
+
const open = literalCodePoints(source);
|
|
1991
|
+
return open ? { kind: "litFold", open } : null;
|
|
1992
|
+
}
|
|
1993
|
+
return parseScanShape(source);
|
|
1994
|
+
}
|
|
1417
1995
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1418
1996
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
1419
1997
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1420
1998
|
).join(" && ");
|
|
1421
|
-
|
|
1999
|
+
var LINE_TERMINATORS = [10, 13, 8232, 8233];
|
|
2000
|
+
function stringHardStop(shape) {
|
|
2001
|
+
return shape.excluded.filter(
|
|
2002
|
+
([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
|
|
2003
|
+
);
|
|
2004
|
+
}
|
|
2005
|
+
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
2006
|
+
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2007
|
+
if (shape.kind === "lookahead") {
|
|
2008
|
+
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
2009
|
+
const okV = mint("_ok");
|
|
2010
|
+
const endV2 = mint("_end");
|
|
2011
|
+
const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
|
|
2012
|
+
const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
|
|
2013
|
+
const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
|
|
2014
|
+
const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
|
|
2015
|
+
return {
|
|
2016
|
+
setup: [
|
|
2017
|
+
...inner.setup,
|
|
2018
|
+
`${ind2}let ${okV} = ${inner.ok}`,
|
|
2019
|
+
`${ind2}let ${endV2} = ${inner.end}`,
|
|
2020
|
+
`${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
|
|
2021
|
+
],
|
|
2022
|
+
ok: okV,
|
|
2023
|
+
end: endV2
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
if (shape.kind === "alt") {
|
|
2027
|
+
const okV = mint("_ok");
|
|
2028
|
+
const endV2 = mint("_end");
|
|
2029
|
+
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2030
|
+
if (shape.disjoint) {
|
|
2031
|
+
const c0 = codeAt(start, firstChar);
|
|
2032
|
+
shape.arms.forEach((arm, k) => {
|
|
2033
|
+
const fs = shape.firsts[k];
|
|
2034
|
+
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
2035
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2036
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2037
|
+
lines.push(...m.setup);
|
|
2038
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2039
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2040
|
+
lines.push(`${ind2}}`);
|
|
2041
|
+
});
|
|
2042
|
+
} else {
|
|
2043
|
+
const lbl = mint("_alt");
|
|
2044
|
+
lines.push(`${ind2}${lbl}: {`);
|
|
2045
|
+
for (const arm of shape.arms) {
|
|
2046
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2047
|
+
lines.push(...m.setup);
|
|
2048
|
+
lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
2049
|
+
}
|
|
2050
|
+
lines.push(`${ind2}}`);
|
|
2051
|
+
}
|
|
2052
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2053
|
+
}
|
|
1422
2054
|
if (shape.kind === "chars") {
|
|
2055
|
+
const cur = mint("_e");
|
|
1423
2056
|
return {
|
|
1424
2057
|
setup: [
|
|
1425
|
-
|
|
1426
|
-
|
|
2058
|
+
`${ind2}let ${cur} = ${start}`,
|
|
2059
|
+
`${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
|
|
1427
2060
|
],
|
|
1428
|
-
|
|
2061
|
+
// `*` always matches (possibly empty); `+` needs at least one char.
|
|
2062
|
+
ok: shape.minOne ? `${cur} > ${start}` : "true",
|
|
2063
|
+
end: cur
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
if (shape.kind === "ident") {
|
|
2067
|
+
const cur = mint("_e");
|
|
2068
|
+
return {
|
|
2069
|
+
setup: [
|
|
2070
|
+
`${ind2}let ${cur} = ${start}`,
|
|
2071
|
+
`${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
|
|
2072
|
+
`${ind2} ${cur} = ${start} + 1`,
|
|
2073
|
+
`${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
|
|
2074
|
+
`${ind2}}`
|
|
2075
|
+
],
|
|
2076
|
+
ok: `${cur} > ${start}`,
|
|
2077
|
+
end: cur
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
if (shape.kind === "seq") {
|
|
2081
|
+
const endV2 = mint("_e");
|
|
2082
|
+
const okV = mint("_ok");
|
|
2083
|
+
const runCond = (p, at) => {
|
|
2084
|
+
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
2085
|
+
return p.negated ? `!(${c})` : `(${c})`;
|
|
2086
|
+
};
|
|
2087
|
+
const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
|
|
2088
|
+
let first = true;
|
|
2089
|
+
for (const p of shape.parts) {
|
|
2090
|
+
if (p.part === "lit") {
|
|
2091
|
+
const L = p.cps.length;
|
|
2092
|
+
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
2093
|
+
if (p.optional) {
|
|
2094
|
+
lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
2095
|
+
} else {
|
|
2096
|
+
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
2097
|
+
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
2098
|
+
}
|
|
2099
|
+
} else if (p.part === "run" && !p.unbounded) {
|
|
2100
|
+
if (p.min === 1) {
|
|
2101
|
+
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
2102
|
+
lines.push(`${ind2} ${endV2}++`);
|
|
2103
|
+
} else {
|
|
2104
|
+
lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2105
|
+
}
|
|
2106
|
+
} else if (p.part === "run") {
|
|
2107
|
+
const s = mint("_s");
|
|
2108
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
2109
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2110
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
2111
|
+
} else {
|
|
2112
|
+
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
2113
|
+
if (!p.unbounded) {
|
|
2114
|
+
lines.push(...inner.setup);
|
|
2115
|
+
if (p.min === 1) {
|
|
2116
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2117
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2118
|
+
} else {
|
|
2119
|
+
lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
2120
|
+
}
|
|
2121
|
+
} else {
|
|
2122
|
+
const progV = mint("_prog");
|
|
2123
|
+
lines.push(`${ind2} let ${progV} = false`);
|
|
2124
|
+
lines.push(`${ind2} while (true) {`);
|
|
2125
|
+
lines.push(...inner.setup);
|
|
2126
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2127
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2128
|
+
lines.push(`${ind2} ${progV} = true`);
|
|
2129
|
+
lines.push(`${ind2} }`);
|
|
2130
|
+
if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
first = false;
|
|
2134
|
+
}
|
|
2135
|
+
lines.push(`${ind2} ${okV} = true`);
|
|
2136
|
+
lines.push(`${ind2}} while (false)`);
|
|
2137
|
+
lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
|
|
2138
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2139
|
+
}
|
|
2140
|
+
if (shape.kind === "litFold") {
|
|
2141
|
+
const openLen2 = shape.open.length;
|
|
2142
|
+
const endV2 = mint("_end");
|
|
2143
|
+
const checks = shape.open.map((cp, k) => {
|
|
2144
|
+
const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
|
|
2145
|
+
return foldEq(cVar, cp);
|
|
2146
|
+
});
|
|
2147
|
+
return {
|
|
2148
|
+
setup: [
|
|
2149
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2150
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
|
|
2151
|
+
],
|
|
2152
|
+
ok: `${endV2} > ${start}`,
|
|
2153
|
+
end: endV2
|
|
1429
2154
|
};
|
|
1430
2155
|
}
|
|
1431
2156
|
if (shape.kind === "until") {
|
|
2157
|
+
const j2 = mint("_j");
|
|
2158
|
+
const openLen2 = shape.open.length;
|
|
2159
|
+
const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
1432
2160
|
return {
|
|
1433
2161
|
setup: [
|
|
1434
|
-
|
|
1435
|
-
|
|
2162
|
+
`${ind2}let ${j2} = ${start}`,
|
|
2163
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
|
|
2164
|
+
`${ind2} ${j2} = ${start} + ${openLen2}`,
|
|
2165
|
+
`${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
|
|
2166
|
+
`${ind2}}`
|
|
1436
2167
|
],
|
|
1437
|
-
|
|
2168
|
+
// An open-until-terminator always completes (stop char or EOF), so a matched
|
|
2169
|
+
// open literal is a full match; `end === start` iff the open didn't match.
|
|
2170
|
+
ok: `${j2} > ${start}`,
|
|
2171
|
+
end: j2
|
|
1438
2172
|
};
|
|
1439
2173
|
}
|
|
1440
|
-
|
|
2174
|
+
if (shape.kind === "string") {
|
|
2175
|
+
const okV = mint("_ok");
|
|
2176
|
+
const endV2 = mint("_end");
|
|
2177
|
+
const i = mint("_i");
|
|
2178
|
+
const c2 = mint("_c");
|
|
2179
|
+
const hard = stringHardStop(shape);
|
|
2180
|
+
const ltLines = shape.escLineTerm ? [] : (() => {
|
|
2181
|
+
const c3 = mint("_c");
|
|
2182
|
+
return [
|
|
2183
|
+
`${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
|
|
2184
|
+
`${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
|
|
2185
|
+
];
|
|
2186
|
+
})();
|
|
2187
|
+
return {
|
|
2188
|
+
setup: [
|
|
2189
|
+
`${ind2}let ${okV} = false`,
|
|
2190
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2191
|
+
`${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
|
|
2192
|
+
`${ind2} let ${i} = ${start} + 1`,
|
|
2193
|
+
`${ind2} while (${i} < input.length) {`,
|
|
2194
|
+
`${ind2} const ${c2} = input.charCodeAt(${i})`,
|
|
2195
|
+
`${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
|
|
2196
|
+
`${ind2} if (${c2} === ${BACKSLASH}) {`,
|
|
2197
|
+
`${ind2} if (${i} + 1 >= input.length) break`,
|
|
2198
|
+
...ltLines,
|
|
2199
|
+
`${ind2} ${i} += 2`,
|
|
2200
|
+
`${ind2} continue`,
|
|
2201
|
+
`${ind2} }`,
|
|
2202
|
+
...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
|
|
2203
|
+
`${ind2} ${i}++`,
|
|
2204
|
+
`${ind2} }`,
|
|
2205
|
+
`${ind2}}`
|
|
2206
|
+
],
|
|
2207
|
+
ok: okV,
|
|
2208
|
+
end: endV2
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
const j = mint("_j");
|
|
2212
|
+
const endV = mint("_end");
|
|
2213
|
+
const openLen = shape.open.length;
|
|
2214
|
+
const closeLen = shape.close.length;
|
|
2215
|
+
const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
2216
|
+
const closeChk = litCond(j, shape.close);
|
|
1441
2217
|
return {
|
|
1442
2218
|
setup: [
|
|
1443
|
-
|
|
1444
|
-
|
|
2219
|
+
`${ind2}let ${j} = ${start}`,
|
|
2220
|
+
`${ind2}let ${endV} = ${start}`,
|
|
2221
|
+
`${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
|
|
2222
|
+
`${ind2} ${j} = ${start} + ${openLen}`,
|
|
2223
|
+
`${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
|
|
2224
|
+
`${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
|
|
2225
|
+
`${ind2}}`
|
|
1445
2226
|
],
|
|
1446
|
-
|
|
2227
|
+
ok: `${endV} > ${start}`,
|
|
2228
|
+
end: endV
|
|
1447
2229
|
};
|
|
1448
2230
|
}
|
|
1449
|
-
function
|
|
1450
|
-
const
|
|
1451
|
-
return ` if (${
|
|
2231
|
+
function scanBranch(shape, mint) {
|
|
2232
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
2233
|
+
return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
|
|
1452
2234
|
}
|
|
1453
|
-
function
|
|
1454
|
-
|
|
1455
|
-
return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
|
|
1456
|
-
}
|
|
1457
|
-
const { setup, endVar } = advanceExpr(shape);
|
|
1458
|
-
return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
|
|
1459
|
-
}
|
|
1460
|
-
function captureChunk(startVar, kindIndex) {
|
|
2235
|
+
function scanBranchLabeled(shape, kindIndex, mint) {
|
|
2236
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1461
2237
|
return [
|
|
2238
|
+
...m.setup,
|
|
2239
|
+
` if (${m.end} > _e) {`,
|
|
1462
2240
|
` if (_cap) {`,
|
|
1463
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${
|
|
1464
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${
|
|
1465
|
-
` }
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
|
|
1471
|
-
if (endVar !== "_e") lines.push(` _e = ${endVar}`);
|
|
1472
|
-
lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
|
|
1473
|
-
return lines.join("\n");
|
|
2241
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
|
|
2242
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
2243
|
+
` }`,
|
|
2244
|
+
` _e = ${m.end}`,
|
|
2245
|
+
` continue`,
|
|
2246
|
+
` }`
|
|
2247
|
+
].join("\n");
|
|
1474
2248
|
}
|
|
1475
2249
|
|
|
1476
2250
|
// src/compiler/trivia-fast-path.ts
|
|
2251
|
+
function makeMint() {
|
|
2252
|
+
let n = 0;
|
|
2253
|
+
return (prefix = "_v") => `${prefix}${n++}`;
|
|
2254
|
+
}
|
|
1477
2255
|
function unwrapTrivia2(p) {
|
|
1478
2256
|
let cur = p;
|
|
1479
2257
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1490,14 +2268,14 @@ function choiceArms(p) {
|
|
|
1490
2268
|
return null;
|
|
1491
2269
|
}
|
|
1492
2270
|
function armShape(arm) {
|
|
1493
|
-
const
|
|
1494
|
-
return
|
|
2271
|
+
const d = getCoreRegexDef(arm);
|
|
2272
|
+
return d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1495
2273
|
}
|
|
1496
2274
|
function analyzeTriviaFastPath(trivia2) {
|
|
1497
2275
|
const core = unwrapTrivia2(trivia2);
|
|
1498
|
-
const
|
|
1499
|
-
if (
|
|
1500
|
-
const shape =
|
|
2276
|
+
const directDef = getCoreRegexDef(core);
|
|
2277
|
+
if (directDef) {
|
|
2278
|
+
const shape = scanShapeFromRegex(directDef.source, directDef.flags);
|
|
1501
2279
|
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1502
2280
|
}
|
|
1503
2281
|
const inner = oneOrMoreInner(core);
|
|
@@ -1521,10 +2299,11 @@ var CAP_RECORD = [
|
|
|
1521
2299
|
` }`
|
|
1522
2300
|
].join("\n");
|
|
1523
2301
|
function composeFastLoop(shapes) {
|
|
2302
|
+
const mint = makeMint();
|
|
1524
2303
|
return [
|
|
1525
2304
|
` while (_e < input.length) {`,
|
|
1526
2305
|
` const c = input.charCodeAt(_e)`,
|
|
1527
|
-
...shapes.map(scanBranch),
|
|
2306
|
+
...shapes.map((s) => scanBranch(s, mint)),
|
|
1528
2307
|
` break`,
|
|
1529
2308
|
` }`
|
|
1530
2309
|
].join("\n");
|
|
@@ -1544,20 +2323,21 @@ function analyzeLabeledScannableRun(trivia2) {
|
|
|
1544
2323
|
if (!spec) return null;
|
|
1545
2324
|
const out = [];
|
|
1546
2325
|
for (const arm of spec.arms) {
|
|
1547
|
-
const
|
|
1548
|
-
const shape =
|
|
2326
|
+
const d = getCoreRegexDef(arm.parser);
|
|
2327
|
+
const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1549
2328
|
if (!shape) return null;
|
|
1550
2329
|
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1551
2330
|
}
|
|
1552
2331
|
return out;
|
|
1553
2332
|
}
|
|
1554
2333
|
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
2334
|
+
const mint = makeMint();
|
|
1555
2335
|
return [
|
|
1556
2336
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1557
2337
|
` let _e = _pos`,
|
|
1558
2338
|
` while (_e < input.length) {`,
|
|
1559
2339
|
` const c = input.charCodeAt(_e)`,
|
|
1560
|
-
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
2340
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
|
|
1561
2341
|
` break`,
|
|
1562
2342
|
` }`,
|
|
1563
2343
|
` return _e`,
|
|
@@ -1634,6 +2414,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
|
|
|
1634
2414
|
].join("\n");
|
|
1635
2415
|
}
|
|
1636
2416
|
|
|
2417
|
+
// src/compiler/scannable-terminal.ts
|
|
2418
|
+
function emitScannableTerminal(shape, c) {
|
|
2419
|
+
const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
|
|
2420
|
+
const stmts = [...m.setup];
|
|
2421
|
+
if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
|
|
2422
|
+
stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
|
|
2423
|
+
return { stmts, endVar: m.end };
|
|
2424
|
+
}
|
|
2425
|
+
|
|
1637
2426
|
// src/compiler/inline-build.ts
|
|
1638
2427
|
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*$/;
|
|
1639
2428
|
function escapeRegExp(s) {
|
|
@@ -1755,14 +2544,45 @@ function failReturn(expected, posExpr) {
|
|
|
1755
2544
|
function failReturnArr(expectedArr, posExpr) {
|
|
1756
2545
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1757
2546
|
}
|
|
2547
|
+
function hoistExpected(ctx, constArrSource) {
|
|
2548
|
+
let name = ctx.expectedMap.get(constArrSource);
|
|
2549
|
+
if (name === void 0) {
|
|
2550
|
+
name = `_fx${ctx.expectedDecls.length}`;
|
|
2551
|
+
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
2552
|
+
ctx.expectedMap.set(constArrSource, name);
|
|
2553
|
+
}
|
|
2554
|
+
return name;
|
|
2555
|
+
}
|
|
1758
2556
|
function failBody(ctx, expected, posExpr) {
|
|
1759
|
-
if (ctx.failLabel)
|
|
2557
|
+
if (ctx.failLabel) {
|
|
2558
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2559
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
2560
|
+
}
|
|
1760
2561
|
return failReturn(expected, posExpr);
|
|
1761
2562
|
}
|
|
1762
|
-
function failArrBody(ctx, expectedArr, posExpr) {
|
|
1763
|
-
if (ctx.failLabel)
|
|
2563
|
+
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
2564
|
+
if (ctx.failLabel) {
|
|
2565
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2566
|
+
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
2567
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
2568
|
+
}
|
|
2569
|
+
if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1764
2570
|
return failReturnArr(expectedArr, posExpr);
|
|
1765
2571
|
}
|
|
2572
|
+
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
2573
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
2574
|
+
}
|
|
2575
|
+
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
2576
|
+
if (srcCtx !== "_ctx") {
|
|
2577
|
+
if (ctx.failLabel) {
|
|
2578
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2579
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
2580
|
+
}
|
|
2581
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
2582
|
+
}
|
|
2583
|
+
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
2584
|
+
return resultFromRecorded();
|
|
2585
|
+
}
|
|
1766
2586
|
function emitIfFail(ctx, cond, body) {
|
|
1767
2587
|
return [
|
|
1768
2588
|
`${ind(ctx)}if (${cond}) {`,
|
|
@@ -1798,13 +2618,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
|
1798
2618
|
`}`
|
|
1799
2619
|
].join("\n"));
|
|
1800
2620
|
}
|
|
1801
|
-
function emitNamedFnCall(ctx, fnName, pos
|
|
2621
|
+
function emitNamedFnCall(ctx, fnName, pos) {
|
|
1802
2622
|
const vv = v(ctx, "_pfv");
|
|
1803
2623
|
const ev = v(ctx, "_pfe");
|
|
1804
2624
|
return {
|
|
1805
2625
|
stmts: [
|
|
1806
2626
|
`${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
|
|
1807
|
-
|
|
2627
|
+
// ref/lazy returns the inner failure verbatim — propagate the recorded
|
|
2628
|
+
// deepest failure (shares _ctx with the named fn), not a "parser" label.
|
|
2629
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
|
|
1808
2630
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
1809
2631
|
],
|
|
1810
2632
|
valueVar: vv,
|
|
@@ -1914,6 +2736,27 @@ function firstSetCond(codeVar, fs) {
|
|
|
1914
2736
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1915
2737
|
).join(" || ");
|
|
1916
2738
|
}
|
|
2739
|
+
var SWITCH_RANGE_LIMIT = 4;
|
|
2740
|
+
var SWITCH_MAX_CASES = 48;
|
|
2741
|
+
var SWITCH_MIN_CASES = 3;
|
|
2742
|
+
var _forceDisjointIf = false;
|
|
2743
|
+
function planDisjointDispatch(parsers) {
|
|
2744
|
+
const cases = [];
|
|
2745
|
+
let total = 0;
|
|
2746
|
+
for (const p of parsers) {
|
|
2747
|
+
const fs = p._meta.firstSet;
|
|
2748
|
+
if (fs.kind !== "ranges") return { kind: "if" };
|
|
2749
|
+
const pts = [];
|
|
2750
|
+
for (const r of fs.ranges) {
|
|
2751
|
+
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2752
|
+
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2753
|
+
}
|
|
2754
|
+
total += pts.length;
|
|
2755
|
+
if (total > SWITCH_MAX_CASES) return { kind: "if" };
|
|
2756
|
+
cases.push(pts);
|
|
2757
|
+
}
|
|
2758
|
+
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2759
|
+
}
|
|
1917
2760
|
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1918
2761
|
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1919
2762
|
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
@@ -2027,18 +2870,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2027
2870
|
return true;
|
|
2028
2871
|
}
|
|
2029
2872
|
}
|
|
2030
|
-
function emitFallible(inner, ctx, pos) {
|
|
2873
|
+
function emitFallible(inner, ctx, pos, swallow = false) {
|
|
2031
2874
|
const lbl = v(ctx, "_lbl");
|
|
2032
2875
|
const okV = `${lbl}ok`;
|
|
2033
2876
|
const valV = `${lbl}v`;
|
|
2034
2877
|
const endV = `${lbl}e`;
|
|
2035
2878
|
const savedLabel = ctx.failLabel;
|
|
2036
2879
|
const savedIndent = ctx.indent;
|
|
2880
|
+
const savedRecord = ctx.recordFail;
|
|
2037
2881
|
ctx.failLabel = lbl;
|
|
2038
2882
|
ctx.indent = savedIndent + 1;
|
|
2883
|
+
if (swallow) ctx.recordFail = false;
|
|
2039
2884
|
const r = emit(inner, ctx, pos);
|
|
2040
2885
|
ctx.failLabel = savedLabel;
|
|
2041
2886
|
ctx.indent = savedIndent;
|
|
2887
|
+
ctx.recordFail = savedRecord;
|
|
2042
2888
|
const ind0 = ind(ctx);
|
|
2043
2889
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
2044
2890
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
@@ -2061,6 +2907,7 @@ function emitFallible(inner, ctx, pos) {
|
|
|
2061
2907
|
];
|
|
2062
2908
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
2063
2909
|
}
|
|
2910
|
+
var CHARCODE_CHAIN_MAX = 16;
|
|
2064
2911
|
function emitLit(def, ctx, pos) {
|
|
2065
2912
|
const { value, caseInsensitive } = def;
|
|
2066
2913
|
const len = value.length;
|
|
@@ -2083,7 +2930,7 @@ function emitLit(def, ctx, pos) {
|
|
|
2083
2930
|
...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
|
|
2084
2931
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
2085
2932
|
);
|
|
2086
|
-
} else if (len <=
|
|
2933
|
+
} else if (len <= CHARCODE_CHAIN_MAX) {
|
|
2087
2934
|
const checks = Array.from(
|
|
2088
2935
|
{ length: len },
|
|
2089
2936
|
(_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
|
|
@@ -2105,7 +2952,55 @@ function emitLit(def, ctx, pos) {
|
|
|
2105
2952
|
function escapeKeywordRe(s) {
|
|
2106
2953
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2107
2954
|
}
|
|
2955
|
+
function emitKeywordsFast(def, ctx, pos) {
|
|
2956
|
+
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
2957
|
+
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
2958
|
+
if (def.caseInsensitive && def.boundary) return null;
|
|
2959
|
+
let boundary = null;
|
|
2960
|
+
if (def.boundary) {
|
|
2961
|
+
let body = def.boundary;
|
|
2962
|
+
const negated = body.startsWith("^");
|
|
2963
|
+
if (negated) body = body.slice(1);
|
|
2964
|
+
const ranges = parseClassRanges(body);
|
|
2965
|
+
if (!ranges) return null;
|
|
2966
|
+
boundary = { ranges, negated };
|
|
2967
|
+
}
|
|
2968
|
+
const mint = (prefix = "_v") => v(ctx, prefix);
|
|
2969
|
+
const lbl = v(ctx, "_kwLbl");
|
|
2970
|
+
const valV = v(ctx, "_kwv");
|
|
2971
|
+
const endV = v(ctx, "_kwe");
|
|
2972
|
+
const bodyInd = ind(ctx) + " ";
|
|
2973
|
+
const tries = [];
|
|
2974
|
+
for (const w of def.words) {
|
|
2975
|
+
const cps = Array.from(w, (ch) => ch.codePointAt(0));
|
|
2976
|
+
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
2977
|
+
if (boundary) {
|
|
2978
|
+
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
2979
|
+
}
|
|
2980
|
+
const m = emitShapeMatch(shape, pos, mint, bodyInd);
|
|
2981
|
+
tries.push(
|
|
2982
|
+
...m.setup,
|
|
2983
|
+
// Slice from input rather than reusing the literal word: `caseInsensitive`
|
|
2984
|
+
// must return the text as it actually appeared (e.g. "ABC" for keyword
|
|
2985
|
+
// "abc"), matching what the original `RegExp.exec()[0]` returned.
|
|
2986
|
+
`${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
|
|
2987
|
+
);
|
|
2988
|
+
}
|
|
2989
|
+
const stmts = [
|
|
2990
|
+
`${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
|
|
2991
|
+
`${ind(ctx)}${lbl}: {`,
|
|
2992
|
+
...tries,
|
|
2993
|
+
`${ind(ctx)}}`,
|
|
2994
|
+
// Every word has length >= 1 (checked above), so a real match always
|
|
2995
|
+
// advances past `pos` — `endV === pos` only happens when no candidate matched.
|
|
2996
|
+
...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
|
|
2997
|
+
];
|
|
2998
|
+
stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
|
|
2999
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3000
|
+
}
|
|
2108
3001
|
function emitKeywords(def, ctx, pos) {
|
|
3002
|
+
const fast = emitKeywordsFast(def, ctx, pos);
|
|
3003
|
+
if (fast) return fast;
|
|
2109
3004
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
2110
3005
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
2111
3006
|
const flags = def.caseInsensitive ? "iuy" : "uy";
|
|
@@ -2130,6 +3025,22 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2130
3025
|
return { stmts, valueVar: vv, endVar };
|
|
2131
3026
|
}
|
|
2132
3027
|
function emitRegex(def, ctx, pos) {
|
|
3028
|
+
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
3029
|
+
const shape = scanShapeFromRegex(def.source, def.flags);
|
|
3030
|
+
if (shape) {
|
|
3031
|
+
const vv2 = v(ctx);
|
|
3032
|
+
const scanned = emitScannableTerminal(shape, {
|
|
3033
|
+
ind: ind(ctx),
|
|
3034
|
+
pos,
|
|
3035
|
+
valueVar: vv2,
|
|
3036
|
+
failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
|
|
3037
|
+
fresh: (prefix) => v(ctx, prefix)
|
|
3038
|
+
});
|
|
3039
|
+
if (scanned) {
|
|
3040
|
+
const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
|
|
3041
|
+
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
2133
3044
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2134
3045
|
const key = `${def.optimizedSource}/${flags}`;
|
|
2135
3046
|
let rName = ctx.regexMap.get(key);
|
|
@@ -2140,7 +3051,6 @@ function emitRegex(def, ctx, pos) {
|
|
|
2140
3051
|
}
|
|
2141
3052
|
const mv = v(ctx, "_m");
|
|
2142
3053
|
const vv = v(ctx);
|
|
2143
|
-
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2144
3054
|
const stmts = [
|
|
2145
3055
|
`${ind(ctx)}${rName}.lastIndex = ${pos}`,
|
|
2146
3056
|
`${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
|
|
@@ -2199,15 +3109,68 @@ function emitSeq(def, ctx, pos) {
|
|
|
2199
3109
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
2200
3110
|
return { stmts, valueVar: arrV, endVar };
|
|
2201
3111
|
}
|
|
3112
|
+
function staticExpectedArr(parsers) {
|
|
3113
|
+
return JSON.stringify(parsers.flatMap((p) => {
|
|
3114
|
+
const e = staticExpected(p);
|
|
3115
|
+
return e.length > 0 ? e : [p._tag];
|
|
3116
|
+
}));
|
|
3117
|
+
}
|
|
3118
|
+
function failsAtStart(p) {
|
|
3119
|
+
const d = p._def;
|
|
3120
|
+
switch (d.tag) {
|
|
3121
|
+
case "literal":
|
|
3122
|
+
case "regex":
|
|
3123
|
+
case "keywords":
|
|
3124
|
+
case "guard":
|
|
3125
|
+
case "not":
|
|
3126
|
+
return true;
|
|
3127
|
+
case "transform":
|
|
3128
|
+
case "label":
|
|
3129
|
+
return failsAtStart(d.parser);
|
|
3130
|
+
default:
|
|
3131
|
+
return false;
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
function armStaticExpected(ctx, p) {
|
|
3135
|
+
return hoistExpected(ctx, staticExpectedArr([p]));
|
|
3136
|
+
}
|
|
3137
|
+
function canMatchEmptyAtStart(p) {
|
|
3138
|
+
const d = p._def;
|
|
3139
|
+
switch (d.tag) {
|
|
3140
|
+
case "regex":
|
|
3141
|
+
return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
|
|
3142
|
+
case "optional":
|
|
3143
|
+
return true;
|
|
3144
|
+
case "many":
|
|
3145
|
+
return true;
|
|
3146
|
+
case "transform":
|
|
3147
|
+
case "label":
|
|
3148
|
+
return canMatchEmptyAtStart(d.parser);
|
|
3149
|
+
case "literal":
|
|
3150
|
+
return d.value.length === 0;
|
|
3151
|
+
default:
|
|
3152
|
+
return false;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
function needsFirstSetGuard(p) {
|
|
3156
|
+
const fs = p._meta.firstSet;
|
|
3157
|
+
return fs.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
3158
|
+
}
|
|
3159
|
+
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
3160
|
+
if (failsAtStart(p)) {
|
|
3161
|
+
const r = emit(p, ctx, pos);
|
|
3162
|
+
return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
|
|
3163
|
+
}
|
|
3164
|
+
const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3165
|
+
return [
|
|
3166
|
+
...stmts,
|
|
3167
|
+
...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
|
|
3168
|
+
`${ind(ctx)}${valV} = ${valVar}`,
|
|
3169
|
+
`${ind(ctx)}${endV} = ${endVar}`
|
|
3170
|
+
];
|
|
3171
|
+
}
|
|
2202
3172
|
function emitChoice(def, ctx, pos) {
|
|
2203
|
-
const allExpected =
|
|
2204
|
-
def.parsers.map((p) => {
|
|
2205
|
-
const d = p._def;
|
|
2206
|
-
if (d.tag === "literal") return JSON.stringify(d.value);
|
|
2207
|
-
if (d.tag === "regex") return `/${d.source}/`;
|
|
2208
|
-
return p._tag;
|
|
2209
|
-
})
|
|
2210
|
-
);
|
|
3173
|
+
const allExpected = staticExpectedArr(def.parsers);
|
|
2211
3174
|
if (def.disjoint) {
|
|
2212
3175
|
const codeV = v(ctx, "_code");
|
|
2213
3176
|
const valV = v(ctx, "_chv");
|
|
@@ -2216,6 +3179,27 @@ function emitChoice(def, ctx, pos) {
|
|
|
2216
3179
|
`${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
|
|
2217
3180
|
`${ind(ctx)}let ${valV}, ${endV} = ${pos}`
|
|
2218
3181
|
];
|
|
3182
|
+
const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
|
|
3183
|
+
if (plan.kind === "switch") {
|
|
3184
|
+
stmts.push(`${ind(ctx)}switch (${codeV}) {`);
|
|
3185
|
+
ctx.indent++;
|
|
3186
|
+
for (let i = 0; i < def.parsers.length; i++) {
|
|
3187
|
+
const p = def.parsers[i];
|
|
3188
|
+
for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
|
|
3189
|
+
stmts.push(`${ind(ctx)}{`);
|
|
3190
|
+
ctx.indent++;
|
|
3191
|
+
stmts.push(
|
|
3192
|
+
...emitDisjointArm(p, ctx, pos, valV, endV),
|
|
3193
|
+
`${ind(ctx)}break`
|
|
3194
|
+
);
|
|
3195
|
+
ctx.indent--;
|
|
3196
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3197
|
+
}
|
|
3198
|
+
stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
|
|
3199
|
+
ctx.indent--;
|
|
3200
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3201
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3202
|
+
}
|
|
2219
3203
|
let first = true;
|
|
2220
3204
|
for (const p of def.parsers) {
|
|
2221
3205
|
const cond = firstSetCond(codeV, p._meta.firstSet);
|
|
@@ -2223,10 +3207,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2223
3207
|
first = false;
|
|
2224
3208
|
stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
|
|
2225
3209
|
ctx.indent++;
|
|
2226
|
-
|
|
2227
|
-
stmts.push(...r.stmts);
|
|
2228
|
-
stmts.push(`${ind(ctx)}${valV} = ${r.valueVar}`);
|
|
2229
|
-
stmts.push(`${ind(ctx)}${endV} = ${r.endVar}`);
|
|
3210
|
+
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
|
|
2230
3211
|
ctx.indent--;
|
|
2231
3212
|
stmts.push(`${ind(ctx)}}`);
|
|
2232
3213
|
}
|
|
@@ -2235,7 +3216,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2235
3216
|
}
|
|
2236
3217
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
2237
3218
|
}
|
|
2238
|
-
function emitGreedyClassify(def, superIndex,
|
|
3219
|
+
function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
2239
3220
|
const superParser = def.parsers[superIndex];
|
|
2240
3221
|
const regexDef = getCoreRegexDef(superParser);
|
|
2241
3222
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
@@ -2250,10 +3231,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
|
|
|
2250
3231
|
const wordV = v(ctx, "_gw");
|
|
2251
3232
|
const endV = v(ctx, "_ge");
|
|
2252
3233
|
const valV = v(ctx, "_gcv");
|
|
3234
|
+
const regexExpected = JSON.stringify(staticExpected(superParser));
|
|
2253
3235
|
const stmts = [
|
|
2254
3236
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
2255
3237
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
2256
|
-
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx,
|
|
3238
|
+
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
|
|
2257
3239
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
2258
3240
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
|
|
2259
3241
|
`${ind(ctx)}let ${valV}`
|
|
@@ -2311,18 +3293,25 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2311
3293
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
2312
3294
|
return { stmts, valueVar: valV, endVar: endV };
|
|
2313
3295
|
}
|
|
2314
|
-
function emitFirstMatch(def,
|
|
3296
|
+
function emitFirstMatch(def, ctx, pos) {
|
|
2315
3297
|
const resValV = v(ctx, "_crv");
|
|
2316
3298
|
const resEndV = v(ctx, "_cre");
|
|
2317
3299
|
const resOkV = v(ctx, "_crok");
|
|
3300
|
+
const codeV = v(ctx, "_chcode");
|
|
3301
|
+
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2318
3302
|
const ind0 = ind(ctx);
|
|
2319
3303
|
const stmts = [
|
|
2320
|
-
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false
|
|
3304
|
+
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
3305
|
+
`${ind0}let ${slots.join(", ")}`,
|
|
3306
|
+
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
2321
3307
|
];
|
|
2322
3308
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2323
3309
|
const p = def.parsers[i];
|
|
2324
3310
|
const gate = def.gates[i];
|
|
2325
3311
|
const autoNot = def.autoNot[i];
|
|
3312
|
+
const atStart = failsAtStart(p);
|
|
3313
|
+
const staticFx = armStaticExpected(ctx, p);
|
|
3314
|
+
const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
2326
3315
|
let gateCond = null;
|
|
2327
3316
|
if (gate) {
|
|
2328
3317
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -2337,49 +3326,53 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2337
3326
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2338
3327
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2339
3328
|
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
3329
|
+
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
2340
3330
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2341
|
-
ctx
|
|
2342
|
-
|
|
3331
|
+
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
3332
|
+
ctx.indent += fsGuard ? 2 : 1;
|
|
2343
3333
|
if (markLeaves) {
|
|
2344
3334
|
stmts.push(
|
|
2345
|
-
`${
|
|
2346
|
-
`${
|
|
2347
|
-
`${
|
|
2348
|
-
`${
|
|
3335
|
+
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
3336
|
+
`${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
|
|
3337
|
+
`${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
3338
|
+
`${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
|
|
2349
3339
|
);
|
|
2350
3340
|
}
|
|
2351
|
-
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3341
|
+
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
|
|
2352
3342
|
stmts.push(...armStmts);
|
|
2353
|
-
ctx.indent--;
|
|
2354
3343
|
if (autoNot && autoNot.length > 0) {
|
|
2355
3344
|
const anCode = v(ctx, "_anc");
|
|
2356
3345
|
const rejectCond = autoNot.map(
|
|
2357
3346
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
|
|
2358
3347
|
).join(" || ");
|
|
2359
|
-
stmts.push(`${
|
|
2360
|
-
stmts.push(`${
|
|
2361
|
-
stmts.push(`${
|
|
2362
|
-
stmts.push(`${
|
|
2363
|
-
stmts.push(`${
|
|
2364
|
-
stmts.push(`${
|
|
2365
|
-
stmts.push(`${
|
|
2366
|
-
stmts.push(`${
|
|
2367
|
-
|
|
3348
|
+
stmts.push(`${ind(ctx)}if (${okVar}) {`);
|
|
3349
|
+
stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
|
|
3350
|
+
stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
|
|
3351
|
+
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
3352
|
+
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
3353
|
+
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
3354
|
+
stmts.push(`${ind(ctx)} }`);
|
|
3355
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3356
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
3357
|
+
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
2368
3358
|
} else {
|
|
2369
|
-
stmts.push(`${
|
|
2370
|
-
|
|
3359
|
+
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
3360
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
|
|
2371
3361
|
}
|
|
3362
|
+
ctx.indent -= fsGuard ? 2 : 1;
|
|
3363
|
+
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
2372
3364
|
stmts.push(`${ind0}}`);
|
|
2373
3365
|
}
|
|
2374
|
-
|
|
3366
|
+
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
3367
|
+
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
2375
3368
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
2376
3369
|
}
|
|
2377
3370
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
2378
3371
|
if (strategy.tag === "greedyClassify")
|
|
2379
|
-
return emitGreedyClassify(def, strategy.superIndex,
|
|
3372
|
+
return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
|
|
2380
3373
|
if (strategy.tag === "literalsLongestFirst")
|
|
2381
3374
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
2382
|
-
return emitFirstMatch(def,
|
|
3375
|
+
return emitFirstMatch(def, ctx, pos);
|
|
2383
3376
|
}
|
|
2384
3377
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
2385
3378
|
const def = p._def;
|
|
@@ -2400,7 +3393,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2400
3393
|
function emitLiteralCondition(litVal, pos) {
|
|
2401
3394
|
const len = litVal.length;
|
|
2402
3395
|
if (len === 0) return "true";
|
|
2403
|
-
if (len >
|
|
3396
|
+
if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
|
|
2404
3397
|
const checks = [`${pos} + ${len} <= input.length`];
|
|
2405
3398
|
for (let i = 0; i < len; i++) {
|
|
2406
3399
|
const code = litVal.codePointAt(i);
|
|
@@ -2449,7 +3442,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2449
3442
|
itemPos = npV;
|
|
2450
3443
|
}
|
|
2451
3444
|
}
|
|
2452
|
-
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
3445
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
2453
3446
|
stmts.push(
|
|
2454
3447
|
...iterStmts,
|
|
2455
3448
|
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
|
|
@@ -2463,7 +3456,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2463
3456
|
function emitOptional(def, ctx, pos) {
|
|
2464
3457
|
const valV = v(ctx, "_opt");
|
|
2465
3458
|
const endV = v(ctx, "_opte");
|
|
2466
|
-
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
3459
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2467
3460
|
const ind0 = ind(ctx);
|
|
2468
3461
|
const stmts = [
|
|
2469
3462
|
...lblStmts,
|
|
@@ -2475,7 +3468,7 @@ function emitOptional(def, ctx, pos) {
|
|
|
2475
3468
|
function emitSepBy(_p, def, ctx, pos) {
|
|
2476
3469
|
const arrV = v(ctx, "_arr");
|
|
2477
3470
|
const curV = v(ctx, "_cur");
|
|
2478
|
-
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
3471
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
2479
3472
|
const stmts = [
|
|
2480
3473
|
`${ind(ctx)}const ${arrV} = []`,
|
|
2481
3474
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
@@ -2507,11 +3500,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2507
3500
|
);
|
|
2508
3501
|
sepAtPos = spV;
|
|
2509
3502
|
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}; `;
|
|
2510
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3503
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2511
3504
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
2512
3505
|
const npV = v(ctx, "_np");
|
|
2513
3506
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
2514
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3507
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2515
3508
|
stmts.push(
|
|
2516
3509
|
...nextStmts,
|
|
2517
3510
|
// item failed → unwind the separator too, back to the end of the last item
|
|
@@ -2524,11 +3517,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2524
3517
|
const spV = v(ctx, "_sp");
|
|
2525
3518
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
2526
3519
|
sepAtPos = spV;
|
|
2527
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3520
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2528
3521
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2529
3522
|
const npV = v(ctx, "_np");
|
|
2530
3523
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
2531
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3524
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2532
3525
|
stmts.push(
|
|
2533
3526
|
...nextStmts,
|
|
2534
3527
|
`${ind(ctx)}if (!${nextOk}) break`,
|
|
@@ -2545,10 +3538,10 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2545
3538
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
2546
3539
|
);
|
|
2547
3540
|
}
|
|
2548
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3541
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2549
3542
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2550
3543
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
2551
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
|
|
3544
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
2552
3545
|
stmts.push(
|
|
2553
3546
|
...nextStmts,
|
|
2554
3547
|
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
|
|
@@ -2606,11 +3599,12 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2606
3599
|
return { stmts, valueVar: valV, endVar: curV };
|
|
2607
3600
|
}
|
|
2608
3601
|
function emitNot(def, ctx, pos) {
|
|
2609
|
-
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3602
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
3603
|
+
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2610
3604
|
return {
|
|
2611
3605
|
stmts: [
|
|
2612
3606
|
...stmts,
|
|
2613
|
-
...emitIfFail(ctx, okVar, failBody(ctx,
|
|
3607
|
+
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
2614
3608
|
],
|
|
2615
3609
|
valueVar: "null",
|
|
2616
3610
|
endVar: pos
|
|
@@ -2642,7 +3636,7 @@ function emitNode(def, ctx, pos) {
|
|
|
2642
3636
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2643
3637
|
stmts.push(...innerStmts);
|
|
2644
3638
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2645
|
-
stmts.push(...emitIfFail(ctx, `!${okVar}`,
|
|
3639
|
+
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
2646
3640
|
let stV = "undefined";
|
|
2647
3641
|
if (clonesState) {
|
|
2648
3642
|
stV = v(ctx, "_nst");
|
|
@@ -2664,9 +3658,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2664
3658
|
const rv = v(ctx, "_rt");
|
|
2665
3659
|
const vv = v(ctx, "_rtv");
|
|
2666
3660
|
const ev = v(ctx, "_rte");
|
|
3661
|
+
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 }`;
|
|
2667
3662
|
const stmts = [
|
|
2668
3663
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
2669
|
-
...emitIfFail(ctx, `!${rv}.ok`,
|
|
3664
|
+
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
2670
3665
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
2671
3666
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
2672
3667
|
];
|
|
@@ -2695,15 +3690,18 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2695
3690
|
}
|
|
2696
3691
|
const savedIndent = ctx.indent;
|
|
2697
3692
|
const savedFailLabel = ctx.failLabel;
|
|
3693
|
+
const savedRecord = ctx.recordFail;
|
|
2698
3694
|
ctx.indent = 1;
|
|
2699
3695
|
ctx.failLabel = "_pfail";
|
|
3696
|
+
ctx.recordFail = true;
|
|
2700
3697
|
const r = emit(resolved, ctx, "_pos");
|
|
2701
3698
|
ctx.indent = savedIndent;
|
|
2702
3699
|
ctx.failLabel = savedFailLabel;
|
|
3700
|
+
ctx.recordFail = savedRecord;
|
|
2703
3701
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
2704
3702
|
}
|
|
2705
3703
|
const fnName = ctx.namedParsers.get(p);
|
|
2706
|
-
return emitNamedFnCall(ctx, fnName, pos
|
|
3704
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
2707
3705
|
}
|
|
2708
3706
|
function emitRecover(def, ctx, pos) {
|
|
2709
3707
|
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
@@ -2721,7 +3719,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
2721
3719
|
`${ind0} if (${sentOk}) break`,
|
|
2722
3720
|
`${ind0} ${scanV}++`,
|
|
2723
3721
|
`${ind0} }`,
|
|
2724
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected:
|
|
3722
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
|
|
2725
3723
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2726
3724
|
`${ind0} ${valVar} = ${errV}`,
|
|
2727
3725
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -2879,11 +3877,14 @@ function emit(p, ctx, pos) {
|
|
|
2879
3877
|
ctx.namedParsers.set(innerParser, fnName);
|
|
2880
3878
|
const savedIndent = ctx.indent;
|
|
2881
3879
|
const savedFailLabel = ctx.failLabel;
|
|
3880
|
+
const savedRecord = ctx.recordFail;
|
|
2882
3881
|
ctx.indent = 1;
|
|
2883
3882
|
ctx.failLabel = "_pfail";
|
|
3883
|
+
ctx.recordFail = true;
|
|
2884
3884
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
2885
3885
|
ctx.indent = savedIndent;
|
|
2886
3886
|
ctx.failLabel = savedFailLabel;
|
|
3887
|
+
ctx.recordFail = savedRecord;
|
|
2887
3888
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
2888
3889
|
}
|
|
2889
3890
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -2894,7 +3895,9 @@ function emit(p, ctx, pos) {
|
|
|
2894
3895
|
stmts: [
|
|
2895
3896
|
`${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
|
|
2896
3897
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
2897
|
-
|
|
3898
|
+
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3899
|
+
// and propagate the inner failure verbatim (interpreter parity).
|
|
3900
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
|
|
2898
3901
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
2899
3902
|
],
|
|
2900
3903
|
valueVar: vv,
|
|
@@ -2995,7 +3998,7 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
2995
3998
|
const recursive = /* @__PURE__ */ new Set();
|
|
2996
3999
|
const descended = /* @__PURE__ */ new Set();
|
|
2997
4000
|
const active = /* @__PURE__ */ new Set();
|
|
2998
|
-
function
|
|
4001
|
+
function walk2(p) {
|
|
2999
4002
|
const def = p._def;
|
|
3000
4003
|
if (def.tag === "lazy") {
|
|
3001
4004
|
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
@@ -3012,13 +4015,13 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3012
4015
|
return;
|
|
3013
4016
|
}
|
|
3014
4017
|
active.add(p);
|
|
3015
|
-
|
|
4018
|
+
walk2(resolved);
|
|
3016
4019
|
active.delete(p);
|
|
3017
4020
|
return;
|
|
3018
4021
|
}
|
|
3019
|
-
for (const child of childrenOf(def))
|
|
4022
|
+
for (const child of childrenOf(def)) walk2(child);
|
|
3020
4023
|
}
|
|
3021
|
-
for (const root of roots)
|
|
4024
|
+
for (const root of roots) walk2(root);
|
|
3022
4025
|
return { counts, recursive };
|
|
3023
4026
|
}
|
|
3024
4027
|
function compile(combinator, mapFnSources) {
|
|
@@ -3027,6 +4030,9 @@ function compile(combinator, mapFnSources) {
|
|
|
3027
4030
|
indent: 1,
|
|
3028
4031
|
regexDecls: [],
|
|
3029
4032
|
regexMap: /* @__PURE__ */ new Map(),
|
|
4033
|
+
expectedDecls: [],
|
|
4034
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4035
|
+
recordFail: true,
|
|
3030
4036
|
mapFns: [],
|
|
3031
4037
|
mapFnSrcs: [],
|
|
3032
4038
|
buildFns: [],
|
|
@@ -3048,6 +4054,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3048
4054
|
const source = [
|
|
3049
4055
|
...emptyTlDecls,
|
|
3050
4056
|
...ctx.regexDecls,
|
|
4057
|
+
...ctx.expectedDecls,
|
|
3051
4058
|
"",
|
|
3052
4059
|
...namedPrelude,
|
|
3053
4060
|
ctx.namedFnDecls.join("\n\n"),
|
|
@@ -3060,6 +4067,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3060
4067
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3061
4068
|
...emptyTlDecls,
|
|
3062
4069
|
...ctx.regexDecls,
|
|
4070
|
+
...ctx.expectedDecls,
|
|
3063
4071
|
collatorDecl,
|
|
3064
4072
|
...namedPrelude,
|
|
3065
4073
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
@@ -3113,12 +4121,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3113
4121
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
3114
4122
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3115
4123
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3116
|
-
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
4124
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3117
4125
|
if (!needsWrapper) return innerFn;
|
|
3118
4126
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3119
4127
|
const hoistedDecls = [
|
|
3120
4128
|
emptyTlDecl,
|
|
3121
4129
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4130
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3122
4131
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3123
4132
|
mfDecl,
|
|
3124
4133
|
buildDecl,
|
|
@@ -3179,6 +4188,7 @@ function recover(combinator, sentinel) {
|
|
|
3179
4188
|
canMatchNewline: true,
|
|
3180
4189
|
isTrivia: false
|
|
3181
4190
|
};
|
|
4191
|
+
const expected = staticExpected(combinator);
|
|
3182
4192
|
return {
|
|
3183
4193
|
_tag: "recover",
|
|
3184
4194
|
_meta: meta,
|
|
@@ -3194,7 +4204,7 @@ function recover(combinator, sentinel) {
|
|
|
3194
4204
|
const error = {
|
|
3195
4205
|
_tag: "parseError",
|
|
3196
4206
|
span: { start: pos, end: scanPos },
|
|
3197
|
-
expected
|
|
4207
|
+
expected
|
|
3198
4208
|
};
|
|
3199
4209
|
ctx._errors?.push(error);
|
|
3200
4210
|
return { ok: true, value: error, span: { start: pos, end: scanPos } };
|
|
@@ -3205,58 +4215,6 @@ function isParseError(value) {
|
|
|
3205
4215
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
3206
4216
|
}
|
|
3207
4217
|
|
|
3208
|
-
// src/combinators/expect.ts
|
|
3209
|
-
function staticExpected(c) {
|
|
3210
|
-
const def = c._def;
|
|
3211
|
-
switch (def.tag) {
|
|
3212
|
-
case "literal":
|
|
3213
|
-
return [JSON.stringify(def.value)];
|
|
3214
|
-
case "regex":
|
|
3215
|
-
return [`/${def.source}/`];
|
|
3216
|
-
case "keywords":
|
|
3217
|
-
return def.words.map((w) => JSON.stringify(w));
|
|
3218
|
-
case "label":
|
|
3219
|
-
return [def.label];
|
|
3220
|
-
case "choice":
|
|
3221
|
-
return def.parsers.flatMap(staticExpected);
|
|
3222
|
-
case "sequence":
|
|
3223
|
-
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
3224
|
-
case "node":
|
|
3225
|
-
case "grammar":
|
|
3226
|
-
case "trivia":
|
|
3227
|
-
case "optional":
|
|
3228
|
-
case "many":
|
|
3229
|
-
case "oneOrMore":
|
|
3230
|
-
case "transform":
|
|
3231
|
-
case "not":
|
|
3232
|
-
return staticExpected(def.parser);
|
|
3233
|
-
case "lazy":
|
|
3234
|
-
return staticExpected(def.thunk());
|
|
3235
|
-
default:
|
|
3236
|
-
return [];
|
|
3237
|
-
}
|
|
3238
|
-
}
|
|
3239
|
-
function expect(combinator, label2) {
|
|
3240
|
-
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
3241
|
-
const meta = {
|
|
3242
|
-
firstSet: combinator._meta.firstSet,
|
|
3243
|
-
canMatchNewline: combinator._meta.canMatchNewline,
|
|
3244
|
-
isTrivia: false
|
|
3245
|
-
};
|
|
3246
|
-
return {
|
|
3247
|
-
_tag: "expect",
|
|
3248
|
-
_meta: meta,
|
|
3249
|
-
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
3250
|
-
parse(input, pos, ctx) {
|
|
3251
|
-
const result = combinator.parse(input, pos, ctx);
|
|
3252
|
-
if (result.ok) return result;
|
|
3253
|
-
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
3254
|
-
ctx._errors?.push(error);
|
|
3255
|
-
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
3256
|
-
}
|
|
3257
|
-
};
|
|
3258
|
-
}
|
|
3259
|
-
|
|
3260
4218
|
// src/combinators/completions.ts
|
|
3261
4219
|
function completionsAt(combinator, input, offset) {
|
|
3262
4220
|
const probe = { offset, best: null };
|
|
@@ -3366,6 +4324,23 @@ function escapeClassChar(ch) {
|
|
|
3366
4324
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3367
4325
|
}
|
|
3368
4326
|
|
|
4327
|
+
// src/combinators/recover-list.ts
|
|
4328
|
+
function tolerantElement(item, sentinel, until) {
|
|
4329
|
+
const el = transform(
|
|
4330
|
+
sequence(not(until), recover(item, sentinel)),
|
|
4331
|
+
([, value]) => value
|
|
4332
|
+
);
|
|
4333
|
+
if (el._def.tag === "transform") el._def.fnSrc = "([, value]) => value";
|
|
4334
|
+
return el;
|
|
4335
|
+
}
|
|
4336
|
+
function sepByRecover(item, separator, until) {
|
|
4337
|
+
const sentinel = choice(separator, until);
|
|
4338
|
+
return sepBy(tolerantElement(item, sentinel, until), separator);
|
|
4339
|
+
}
|
|
4340
|
+
function manyRecover(item, until) {
|
|
4341
|
+
return many(tolerantElement(item, until, until));
|
|
4342
|
+
}
|
|
4343
|
+
|
|
3369
4344
|
// src/functional/doc.ts
|
|
3370
4345
|
function isNode(x) {
|
|
3371
4346
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
@@ -3427,7 +4402,7 @@ function graftAndShift(root, path, newNode, delta) {
|
|
|
3427
4402
|
children: newChildren
|
|
3428
4403
|
};
|
|
3429
4404
|
}
|
|
3430
|
-
var
|
|
4405
|
+
var ParseDocImpl = class _ParseDocImpl {
|
|
3431
4406
|
_registry;
|
|
3432
4407
|
_rootRule;
|
|
3433
4408
|
_opts;
|
|
@@ -3444,7 +4419,7 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
|
3444
4419
|
}
|
|
3445
4420
|
edit(from, to, replacement) {
|
|
3446
4421
|
const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
|
|
3447
|
-
const reparse = () =>
|
|
4422
|
+
const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
|
|
3448
4423
|
if (!this.tree) return reparse();
|
|
3449
4424
|
const delta = replacement.length - (to - from);
|
|
3450
4425
|
const found = findContaining(this.tree, from);
|
|
@@ -3466,25 +4441,25 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
|
3466
4441
|
if (r.span.end === node2.span.end + delta) {
|
|
3467
4442
|
if (delta === 0) {
|
|
3468
4443
|
const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
|
|
3469
|
-
return new
|
|
4444
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
|
|
3470
4445
|
}
|
|
3471
4446
|
if (this._opts.rebuild) return reparse();
|
|
3472
4447
|
const newTree = graftAndShift(this.tree, path, r.value, delta);
|
|
3473
|
-
return new
|
|
4448
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
3474
4449
|
}
|
|
3475
4450
|
}
|
|
3476
4451
|
return reparse();
|
|
3477
4452
|
}
|
|
3478
4453
|
};
|
|
3479
|
-
function
|
|
4454
|
+
function parseDoc(registry, rootRule, input, opts = {}) {
|
|
3480
4455
|
const ruleFn = registry[rootRule];
|
|
3481
4456
|
if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
|
|
3482
4457
|
const ctx = { trackLines: false, state: opts.state };
|
|
3483
4458
|
const r = ruleFn(input, 0, ctx);
|
|
3484
4459
|
if (r.ok) {
|
|
3485
|
-
return new
|
|
4460
|
+
return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
|
|
3486
4461
|
}
|
|
3487
|
-
return new
|
|
4462
|
+
return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
3488
4463
|
}
|
|
3489
4464
|
|
|
3490
4465
|
// src/cst/trivia-index.ts
|
|
@@ -3553,6 +4528,41 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
3553
4528
|
return { before, after };
|
|
3554
4529
|
}
|
|
3555
4530
|
|
|
4531
|
+
// src/cst/walk.ts
|
|
4532
|
+
function walk(root, visitor, ctx = void 0) {
|
|
4533
|
+
const go = (node2, parent) => {
|
|
4534
|
+
const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
|
|
4535
|
+
const children = node2.children;
|
|
4536
|
+
if (descend !== false && Array.isArray(children)) {
|
|
4537
|
+
for (const child of children) go(child, node2);
|
|
4538
|
+
}
|
|
4539
|
+
visitor.leave?.(node2, parent, ctx);
|
|
4540
|
+
};
|
|
4541
|
+
go(root, null);
|
|
4542
|
+
}
|
|
4543
|
+
function createVisitor(handlers) {
|
|
4544
|
+
const api = {
|
|
4545
|
+
visit(node2) {
|
|
4546
|
+
const handler = node2.type !== void 0 ? handlers[node2.type] : void 0;
|
|
4547
|
+
if (handler) return handler(node2, api);
|
|
4548
|
+
api.visitChildren(node2);
|
|
4549
|
+
return void 0;
|
|
4550
|
+
},
|
|
4551
|
+
visitChildren(node2) {
|
|
4552
|
+
const out = [];
|
|
4553
|
+
const children = node2.children;
|
|
4554
|
+
if (Array.isArray(children)) {
|
|
4555
|
+
for (const child of children) {
|
|
4556
|
+
const r = api.visit(child);
|
|
4557
|
+
if (r !== void 0) out.push(r);
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
return out;
|
|
4561
|
+
}
|
|
4562
|
+
};
|
|
4563
|
+
return (node2) => api.visit(node2);
|
|
4564
|
+
}
|
|
4565
|
+
|
|
3556
4566
|
// src/cst/trivia-entries.ts
|
|
3557
4567
|
function entryOffset(i, stride) {
|
|
3558
4568
|
return i * stride;
|
|
@@ -3594,15 +4604,16 @@ export {
|
|
|
3594
4604
|
choice,
|
|
3595
4605
|
compile,
|
|
3596
4606
|
completionsAt,
|
|
4607
|
+
createVisitor,
|
|
3597
4608
|
expect,
|
|
3598
4609
|
guard,
|
|
3599
4610
|
isParseError,
|
|
3600
4611
|
keywords,
|
|
3601
4612
|
label,
|
|
3602
4613
|
literal,
|
|
3603
|
-
makeFunctionalDoc,
|
|
3604
4614
|
makeWord,
|
|
3605
4615
|
many,
|
|
4616
|
+
manyRecover,
|
|
3606
4617
|
noTrivia,
|
|
3607
4618
|
node,
|
|
3608
4619
|
not,
|
|
@@ -3610,6 +4621,7 @@ export {
|
|
|
3610
4621
|
oneOrMore,
|
|
3611
4622
|
optional,
|
|
3612
4623
|
parse,
|
|
4624
|
+
parseDoc,
|
|
3613
4625
|
parser,
|
|
3614
4626
|
recover,
|
|
3615
4627
|
ref,
|
|
@@ -3617,12 +4629,14 @@ export {
|
|
|
3617
4629
|
rules,
|
|
3618
4630
|
scanTo,
|
|
3619
4631
|
sepBy,
|
|
4632
|
+
sepByRecover,
|
|
3620
4633
|
sequence,
|
|
3621
4634
|
skip,
|
|
3622
4635
|
staticExpected,
|
|
3623
4636
|
transform,
|
|
3624
4637
|
trivia,
|
|
3625
4638
|
triviaEntries,
|
|
4639
|
+
walk,
|
|
3626
4640
|
withCtx,
|
|
3627
4641
|
word
|
|
3628
4642
|
};
|