parseman 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +9 -9
- package/dist/combinators/recover-list.d.ts +41 -0
- package/dist/combinators/recover-list.d.ts.map +1 -0
- package/dist/combinators/recover.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +1 -0
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/scannable-run.d.ts +60 -8
- package/dist/compiler/scannable-run.d.ts.map +1 -1
- package/dist/compiler/scannable-terminal.d.ts +23 -0
- package/dist/compiler/scannable-terminal.d.ts.map +1 -0
- package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/cst/walk.d.ts +72 -0
- package/dist/cst/walk.d.ts.map +1 -0
- package/dist/index.cjs +484 -145
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +484 -145
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts +8 -0
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +442 -144
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +442 -144
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -1
package/dist/index.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,108 @@ 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
|
+
function shorthandRanges(ch) {
|
|
1403
|
+
return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1404
|
+
}
|
|
1346
1405
|
function parseClassRanges(body) {
|
|
1347
1406
|
const ranges = [];
|
|
1348
1407
|
let i = 0;
|
|
1349
|
-
const
|
|
1408
|
+
const readAtom = () => {
|
|
1350
1409
|
const ch = body[i];
|
|
1351
1410
|
if (ch === void 0) return null;
|
|
1352
1411
|
if (ch === "\\") {
|
|
1353
1412
|
const e = body[i + 1];
|
|
1354
1413
|
if (e === void 0) return null;
|
|
1355
1414
|
i += 2;
|
|
1356
|
-
|
|
1415
|
+
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1416
|
+
if (e === "d" || e === "w") return { set: shorthandRanges(e) };
|
|
1417
|
+
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1418
|
+
return { cp: e.codePointAt(0) };
|
|
1357
1419
|
}
|
|
1358
1420
|
i += ch.length;
|
|
1359
|
-
return ch.codePointAt(0);
|
|
1421
|
+
return { cp: ch.codePointAt(0) };
|
|
1360
1422
|
};
|
|
1361
1423
|
while (i < body.length) {
|
|
1362
|
-
const lo =
|
|
1424
|
+
const lo = readAtom();
|
|
1363
1425
|
if (lo === null) return null;
|
|
1426
|
+
if ("set" in lo) {
|
|
1427
|
+
ranges.push(...lo.set);
|
|
1428
|
+
continue;
|
|
1429
|
+
}
|
|
1364
1430
|
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1365
1431
|
i += 1;
|
|
1366
|
-
const hi =
|
|
1367
|
-
if (hi === null) return null;
|
|
1368
|
-
ranges.push([lo, hi]);
|
|
1432
|
+
const hi = readAtom();
|
|
1433
|
+
if (hi === null || "set" in hi) return null;
|
|
1434
|
+
ranges.push([lo.cp, hi.cp]);
|
|
1369
1435
|
} else {
|
|
1370
|
-
ranges.push([lo, lo]);
|
|
1436
|
+
ranges.push([lo.cp, lo.cp]);
|
|
1371
1437
|
}
|
|
1372
1438
|
}
|
|
1373
1439
|
return ranges.length ? ranges : null;
|
|
1374
1440
|
}
|
|
1441
|
+
function classToRanges(cls) {
|
|
1442
|
+
if (cls === "\\d") return shorthandRanges("d");
|
|
1443
|
+
if (cls === "\\w") return shorthandRanges("w");
|
|
1444
|
+
const body = cls.slice(1, -1);
|
|
1445
|
+
if (body.startsWith("^")) return null;
|
|
1446
|
+
return parseClassRanges(body);
|
|
1447
|
+
}
|
|
1375
1448
|
function literalCodePoints(frag) {
|
|
1376
1449
|
const out = [];
|
|
1377
1450
|
let i = 0;
|
|
@@ -1390,13 +1463,53 @@ function literalCodePoints(frag) {
|
|
|
1390
1463
|
}
|
|
1391
1464
|
return out.length ? out : null;
|
|
1392
1465
|
}
|
|
1466
|
+
function parseStringShape(source) {
|
|
1467
|
+
const q = source[0];
|
|
1468
|
+
if (q === void 0 || q === "\\" || META.has(q)) return null;
|
|
1469
|
+
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
1470
|
+
const inner = source.slice(1, -1);
|
|
1471
|
+
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
1472
|
+
const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
1473
|
+
let body;
|
|
1474
|
+
let esc;
|
|
1475
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
|
|
1476
|
+
if (m) {
|
|
1477
|
+
body = m[1];
|
|
1478
|
+
esc = m[2];
|
|
1479
|
+
} else {
|
|
1480
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
|
|
1481
|
+
if (m) {
|
|
1482
|
+
esc = m[1];
|
|
1483
|
+
body = m[2];
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
if (body === void 0 || esc === void 0) return null;
|
|
1487
|
+
const excluded = parseClassRanges(body);
|
|
1488
|
+
if (!excluded) return null;
|
|
1489
|
+
const qcp = q.codePointAt(0);
|
|
1490
|
+
const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
|
|
1491
|
+
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1492
|
+
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1493
|
+
}
|
|
1393
1494
|
function parseScanShape(source) {
|
|
1394
1495
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1395
1496
|
if (m) {
|
|
1396
1497
|
if (m[1].startsWith("^")) return null;
|
|
1397
1498
|
const ranges = parseClassRanges(m[1]);
|
|
1398
|
-
return ranges ? { kind: "chars", ranges } : null;
|
|
1499
|
+
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1500
|
+
}
|
|
1501
|
+
m = /^\\([dw])([+*])$/.exec(source);
|
|
1502
|
+
if (m) {
|
|
1503
|
+
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1504
|
+
}
|
|
1505
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
|
|
1506
|
+
if (m) {
|
|
1507
|
+
const head = classToRanges(m[1]);
|
|
1508
|
+
const tail = classToRanges(m[2]);
|
|
1509
|
+
return head && tail ? { kind: "ident", head, tail } : null;
|
|
1399
1510
|
}
|
|
1511
|
+
const str = parseStringShape(source);
|
|
1512
|
+
if (str) return str;
|
|
1400
1513
|
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1401
1514
|
if (m) {
|
|
1402
1515
|
const open = literalCodePoints(m[1]);
|
|
@@ -1414,66 +1527,147 @@ function parseScanShape(source) {
|
|
|
1414
1527
|
}
|
|
1415
1528
|
return null;
|
|
1416
1529
|
}
|
|
1530
|
+
function scanShapeFromRegex(source, flags) {
|
|
1531
|
+
if (/[imsu]/.test(flags)) return null;
|
|
1532
|
+
return parseScanShape(source);
|
|
1533
|
+
}
|
|
1417
1534
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1418
1535
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
1419
1536
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1420
1537
|
).join(" && ");
|
|
1421
|
-
|
|
1538
|
+
var LINE_TERMINATORS = [10, 13, 8232, 8233];
|
|
1539
|
+
function stringHardStop(shape) {
|
|
1540
|
+
return shape.excluded.filter(
|
|
1541
|
+
([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1545
|
+
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
1422
1546
|
if (shape.kind === "chars") {
|
|
1547
|
+
const cur = mint("_e");
|
|
1548
|
+
return {
|
|
1549
|
+
setup: [
|
|
1550
|
+
`${ind2}let ${cur} = ${start}`,
|
|
1551
|
+
`${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
|
|
1552
|
+
],
|
|
1553
|
+
// `*` always matches (possibly empty); `+` needs at least one char.
|
|
1554
|
+
ok: shape.minOne ? `${cur} > ${start}` : "true",
|
|
1555
|
+
end: cur
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
if (shape.kind === "ident") {
|
|
1559
|
+
const cur = mint("_e");
|
|
1423
1560
|
return {
|
|
1424
1561
|
setup: [
|
|
1425
|
-
|
|
1426
|
-
|
|
1562
|
+
`${ind2}let ${cur} = ${start}`,
|
|
1563
|
+
`${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
|
|
1564
|
+
`${ind2} ${cur} = ${start} + 1`,
|
|
1565
|
+
`${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
|
|
1566
|
+
`${ind2}}`
|
|
1427
1567
|
],
|
|
1428
|
-
|
|
1568
|
+
ok: `${cur} > ${start}`,
|
|
1569
|
+
end: cur
|
|
1429
1570
|
};
|
|
1430
1571
|
}
|
|
1431
1572
|
if (shape.kind === "until") {
|
|
1573
|
+
const j2 = mint("_j");
|
|
1574
|
+
const openLen2 = shape.open.length;
|
|
1575
|
+
const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
1576
|
+
return {
|
|
1577
|
+
setup: [
|
|
1578
|
+
`${ind2}let ${j2} = ${start}`,
|
|
1579
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
|
|
1580
|
+
`${ind2} ${j2} = ${start} + ${openLen2}`,
|
|
1581
|
+
`${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
|
|
1582
|
+
`${ind2}}`
|
|
1583
|
+
],
|
|
1584
|
+
// An open-until-terminator always completes (stop char or EOF), so a matched
|
|
1585
|
+
// open literal is a full match; `end === start` iff the open didn't match.
|
|
1586
|
+
ok: `${j2} > ${start}`,
|
|
1587
|
+
end: j2
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
if (shape.kind === "string") {
|
|
1591
|
+
const okV = mint("_ok");
|
|
1592
|
+
const endV2 = mint("_end");
|
|
1593
|
+
const i = mint("_i");
|
|
1594
|
+
const c2 = mint("_c");
|
|
1595
|
+
const hard = stringHardStop(shape);
|
|
1596
|
+
const ltLines = shape.escLineTerm ? [] : (() => {
|
|
1597
|
+
const c3 = mint("_c");
|
|
1598
|
+
return [
|
|
1599
|
+
`${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
|
|
1600
|
+
`${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
|
|
1601
|
+
];
|
|
1602
|
+
})();
|
|
1432
1603
|
return {
|
|
1433
1604
|
setup: [
|
|
1434
|
-
|
|
1435
|
-
|
|
1605
|
+
`${ind2}let ${okV} = false`,
|
|
1606
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
1607
|
+
`${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
|
|
1608
|
+
`${ind2} let ${i} = ${start} + 1`,
|
|
1609
|
+
`${ind2} while (${i} < input.length) {`,
|
|
1610
|
+
`${ind2} const ${c2} = input.charCodeAt(${i})`,
|
|
1611
|
+
`${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
|
|
1612
|
+
`${ind2} if (${c2} === ${BACKSLASH}) {`,
|
|
1613
|
+
`${ind2} if (${i} + 1 >= input.length) break`,
|
|
1614
|
+
...ltLines,
|
|
1615
|
+
`${ind2} ${i} += 2`,
|
|
1616
|
+
`${ind2} continue`,
|
|
1617
|
+
`${ind2} }`,
|
|
1618
|
+
...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
|
|
1619
|
+
`${ind2} ${i}++`,
|
|
1620
|
+
`${ind2} }`,
|
|
1621
|
+
`${ind2}}`
|
|
1436
1622
|
],
|
|
1437
|
-
|
|
1623
|
+
ok: okV,
|
|
1624
|
+
end: endV2
|
|
1438
1625
|
};
|
|
1439
1626
|
}
|
|
1440
|
-
const
|
|
1627
|
+
const j = mint("_j");
|
|
1628
|
+
const endV = mint("_end");
|
|
1629
|
+
const openLen = shape.open.length;
|
|
1630
|
+
const closeLen = shape.close.length;
|
|
1631
|
+
const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
1632
|
+
const closeChk = litCond(j, shape.close);
|
|
1441
1633
|
return {
|
|
1442
1634
|
setup: [
|
|
1443
|
-
|
|
1444
|
-
|
|
1635
|
+
`${ind2}let ${j} = ${start}`,
|
|
1636
|
+
`${ind2}let ${endV} = ${start}`,
|
|
1637
|
+
`${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
|
|
1638
|
+
`${ind2} ${j} = ${start} + ${openLen}`,
|
|
1639
|
+
`${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
|
|
1640
|
+
`${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
|
|
1641
|
+
`${ind2}}`
|
|
1445
1642
|
],
|
|
1446
|
-
|
|
1643
|
+
ok: `${endV} > ${start}`,
|
|
1644
|
+
end: endV
|
|
1447
1645
|
};
|
|
1448
1646
|
}
|
|
1449
|
-
function
|
|
1450
|
-
const
|
|
1451
|
-
return ` if (${
|
|
1647
|
+
function scanBranch(shape, mint) {
|
|
1648
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1649
|
+
return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
|
|
1452
1650
|
}
|
|
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) {
|
|
1651
|
+
function scanBranchLabeled(shape, kindIndex, mint) {
|
|
1652
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1461
1653
|
return [
|
|
1654
|
+
...m.setup,
|
|
1655
|
+
` if (${m.end} > _e) {`,
|
|
1462
1656
|
` 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");
|
|
1657
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
|
|
1658
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
1659
|
+
` }`,
|
|
1660
|
+
` _e = ${m.end}`,
|
|
1661
|
+
` continue`,
|
|
1662
|
+
` }`
|
|
1663
|
+
].join("\n");
|
|
1474
1664
|
}
|
|
1475
1665
|
|
|
1476
1666
|
// src/compiler/trivia-fast-path.ts
|
|
1667
|
+
function makeMint() {
|
|
1668
|
+
let n = 0;
|
|
1669
|
+
return (prefix = "_v") => `${prefix}${n++}`;
|
|
1670
|
+
}
|
|
1477
1671
|
function unwrapTrivia2(p) {
|
|
1478
1672
|
let cur = p;
|
|
1479
1673
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1490,14 +1684,14 @@ function choiceArms(p) {
|
|
|
1490
1684
|
return null;
|
|
1491
1685
|
}
|
|
1492
1686
|
function armShape(arm) {
|
|
1493
|
-
const
|
|
1494
|
-
return
|
|
1687
|
+
const d = getCoreRegexDef(arm);
|
|
1688
|
+
return d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1495
1689
|
}
|
|
1496
1690
|
function analyzeTriviaFastPath(trivia2) {
|
|
1497
1691
|
const core = unwrapTrivia2(trivia2);
|
|
1498
|
-
const
|
|
1499
|
-
if (
|
|
1500
|
-
const shape =
|
|
1692
|
+
const directDef = getCoreRegexDef(core);
|
|
1693
|
+
if (directDef) {
|
|
1694
|
+
const shape = scanShapeFromRegex(directDef.source, directDef.flags);
|
|
1501
1695
|
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1502
1696
|
}
|
|
1503
1697
|
const inner = oneOrMoreInner(core);
|
|
@@ -1521,10 +1715,11 @@ var CAP_RECORD = [
|
|
|
1521
1715
|
` }`
|
|
1522
1716
|
].join("\n");
|
|
1523
1717
|
function composeFastLoop(shapes) {
|
|
1718
|
+
const mint = makeMint();
|
|
1524
1719
|
return [
|
|
1525
1720
|
` while (_e < input.length) {`,
|
|
1526
1721
|
` const c = input.charCodeAt(_e)`,
|
|
1527
|
-
...shapes.map(scanBranch),
|
|
1722
|
+
...shapes.map((s) => scanBranch(s, mint)),
|
|
1528
1723
|
` break`,
|
|
1529
1724
|
` }`
|
|
1530
1725
|
].join("\n");
|
|
@@ -1544,20 +1739,21 @@ function analyzeLabeledScannableRun(trivia2) {
|
|
|
1544
1739
|
if (!spec) return null;
|
|
1545
1740
|
const out = [];
|
|
1546
1741
|
for (const arm of spec.arms) {
|
|
1547
|
-
const
|
|
1548
|
-
const shape =
|
|
1742
|
+
const d = getCoreRegexDef(arm.parser);
|
|
1743
|
+
const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1549
1744
|
if (!shape) return null;
|
|
1550
1745
|
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1551
1746
|
}
|
|
1552
1747
|
return out;
|
|
1553
1748
|
}
|
|
1554
1749
|
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
1750
|
+
const mint = makeMint();
|
|
1555
1751
|
return [
|
|
1556
1752
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1557
1753
|
` let _e = _pos`,
|
|
1558
1754
|
` while (_e < input.length) {`,
|
|
1559
1755
|
` const c = input.charCodeAt(_e)`,
|
|
1560
|
-
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
1756
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
|
|
1561
1757
|
` break`,
|
|
1562
1758
|
` }`,
|
|
1563
1759
|
` return _e`,
|
|
@@ -1634,6 +1830,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
|
|
|
1634
1830
|
].join("\n");
|
|
1635
1831
|
}
|
|
1636
1832
|
|
|
1833
|
+
// src/compiler/scannable-terminal.ts
|
|
1834
|
+
function emitScannableTerminal(shape, c) {
|
|
1835
|
+
const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
|
|
1836
|
+
const stmts = [...m.setup];
|
|
1837
|
+
if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
|
|
1838
|
+
stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
|
|
1839
|
+
return { stmts, endVar: m.end };
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1637
1842
|
// src/compiler/inline-build.ts
|
|
1638
1843
|
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
1844
|
function escapeRegExp(s) {
|
|
@@ -1755,14 +1960,45 @@ function failReturn(expected, posExpr) {
|
|
|
1755
1960
|
function failReturnArr(expectedArr, posExpr) {
|
|
1756
1961
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1757
1962
|
}
|
|
1963
|
+
function hoistExpected(ctx, constArrSource) {
|
|
1964
|
+
let name = ctx.expectedMap.get(constArrSource);
|
|
1965
|
+
if (name === void 0) {
|
|
1966
|
+
name = `_fx${ctx.expectedDecls.length}`;
|
|
1967
|
+
ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
|
|
1968
|
+
ctx.expectedMap.set(constArrSource, name);
|
|
1969
|
+
}
|
|
1970
|
+
return name;
|
|
1971
|
+
}
|
|
1758
1972
|
function failBody(ctx, expected, posExpr) {
|
|
1759
|
-
if (ctx.failLabel)
|
|
1973
|
+
if (ctx.failLabel) {
|
|
1974
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
1975
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
1976
|
+
}
|
|
1760
1977
|
return failReturn(expected, posExpr);
|
|
1761
1978
|
}
|
|
1762
|
-
function failArrBody(ctx, expectedArr, posExpr) {
|
|
1763
|
-
if (ctx.failLabel)
|
|
1979
|
+
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
1980
|
+
if (ctx.failLabel) {
|
|
1981
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
1982
|
+
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
1983
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
1984
|
+
}
|
|
1985
|
+
if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1764
1986
|
return failReturnArr(expectedArr, posExpr);
|
|
1765
1987
|
}
|
|
1988
|
+
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
1989
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
1990
|
+
}
|
|
1991
|
+
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
1992
|
+
if (srcCtx !== "_ctx") {
|
|
1993
|
+
if (ctx.failLabel) {
|
|
1994
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
1995
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
1996
|
+
}
|
|
1997
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
1998
|
+
}
|
|
1999
|
+
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
2000
|
+
return resultFromRecorded();
|
|
2001
|
+
}
|
|
1766
2002
|
function emitIfFail(ctx, cond, body) {
|
|
1767
2003
|
return [
|
|
1768
2004
|
`${ind(ctx)}if (${cond}) {`,
|
|
@@ -1798,13 +2034,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
|
1798
2034
|
`}`
|
|
1799
2035
|
].join("\n"));
|
|
1800
2036
|
}
|
|
1801
|
-
function emitNamedFnCall(ctx, fnName, pos
|
|
2037
|
+
function emitNamedFnCall(ctx, fnName, pos) {
|
|
1802
2038
|
const vv = v(ctx, "_pfv");
|
|
1803
2039
|
const ev = v(ctx, "_pfe");
|
|
1804
2040
|
return {
|
|
1805
2041
|
stmts: [
|
|
1806
2042
|
`${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
|
|
1807
|
-
|
|
2043
|
+
// ref/lazy returns the inner failure verbatim — propagate the recorded
|
|
2044
|
+
// deepest failure (shares _ctx with the named fn), not a "parser" label.
|
|
2045
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
|
|
1808
2046
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
1809
2047
|
],
|
|
1810
2048
|
valueVar: vv,
|
|
@@ -1914,6 +2152,27 @@ function firstSetCond(codeVar, fs) {
|
|
|
1914
2152
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1915
2153
|
).join(" || ");
|
|
1916
2154
|
}
|
|
2155
|
+
var SWITCH_RANGE_LIMIT = 4;
|
|
2156
|
+
var SWITCH_MAX_CASES = 48;
|
|
2157
|
+
var SWITCH_MIN_CASES = 3;
|
|
2158
|
+
var _forceDisjointIf = false;
|
|
2159
|
+
function planDisjointDispatch(parsers) {
|
|
2160
|
+
const cases = [];
|
|
2161
|
+
let total = 0;
|
|
2162
|
+
for (const p of parsers) {
|
|
2163
|
+
const fs = p._meta.firstSet;
|
|
2164
|
+
if (fs.kind !== "ranges") return { kind: "if" };
|
|
2165
|
+
const pts = [];
|
|
2166
|
+
for (const r of fs.ranges) {
|
|
2167
|
+
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2168
|
+
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2169
|
+
}
|
|
2170
|
+
total += pts.length;
|
|
2171
|
+
if (total > SWITCH_MAX_CASES) return { kind: "if" };
|
|
2172
|
+
cases.push(pts);
|
|
2173
|
+
}
|
|
2174
|
+
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2175
|
+
}
|
|
1917
2176
|
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1918
2177
|
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1919
2178
|
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
@@ -2130,6 +2389,22 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2130
2389
|
return { stmts, valueVar: vv, endVar };
|
|
2131
2390
|
}
|
|
2132
2391
|
function emitRegex(def, ctx, pos) {
|
|
2392
|
+
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2393
|
+
const shape = scanShapeFromRegex(def.source, def.flags);
|
|
2394
|
+
if (shape) {
|
|
2395
|
+
const vv2 = v(ctx);
|
|
2396
|
+
const scanned = emitScannableTerminal(shape, {
|
|
2397
|
+
ind: ind(ctx),
|
|
2398
|
+
pos,
|
|
2399
|
+
valueVar: vv2,
|
|
2400
|
+
failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
|
|
2401
|
+
fresh: (prefix) => v(ctx, prefix)
|
|
2402
|
+
});
|
|
2403
|
+
if (scanned) {
|
|
2404
|
+
const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
|
|
2405
|
+
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2133
2408
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2134
2409
|
const key = `${def.optimizedSource}/${flags}`;
|
|
2135
2410
|
let rName = ctx.regexMap.get(key);
|
|
@@ -2140,7 +2415,6 @@ function emitRegex(def, ctx, pos) {
|
|
|
2140
2415
|
}
|
|
2141
2416
|
const mv = v(ctx, "_m");
|
|
2142
2417
|
const vv = v(ctx);
|
|
2143
|
-
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2144
2418
|
const stmts = [
|
|
2145
2419
|
`${ind(ctx)}${rName}.lastIndex = ${pos}`,
|
|
2146
2420
|
`${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
|
|
@@ -2199,15 +2473,43 @@ function emitSeq(def, ctx, pos) {
|
|
|
2199
2473
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
2200
2474
|
return { stmts, valueVar: arrV, endVar };
|
|
2201
2475
|
}
|
|
2476
|
+
function staticExpectedArr(parsers) {
|
|
2477
|
+
return JSON.stringify(parsers.flatMap((p) => {
|
|
2478
|
+
const e = staticExpected(p);
|
|
2479
|
+
return e.length > 0 ? e : [p._tag];
|
|
2480
|
+
}));
|
|
2481
|
+
}
|
|
2482
|
+
function failsAtStart(p) {
|
|
2483
|
+
const d = p._def;
|
|
2484
|
+
switch (d.tag) {
|
|
2485
|
+
case "literal":
|
|
2486
|
+
case "regex":
|
|
2487
|
+
case "keywords":
|
|
2488
|
+
case "guard":
|
|
2489
|
+
case "not":
|
|
2490
|
+
return true;
|
|
2491
|
+
case "transform":
|
|
2492
|
+
case "label":
|
|
2493
|
+
return failsAtStart(d.parser);
|
|
2494
|
+
default:
|
|
2495
|
+
return false;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
2499
|
+
if (failsAtStart(p)) {
|
|
2500
|
+
const r = emit(p, ctx, pos);
|
|
2501
|
+
return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
|
|
2502
|
+
}
|
|
2503
|
+
const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
2504
|
+
return [
|
|
2505
|
+
...stmts,
|
|
2506
|
+
...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
|
|
2507
|
+
`${ind(ctx)}${valV} = ${valVar}`,
|
|
2508
|
+
`${ind(ctx)}${endV} = ${endVar}`
|
|
2509
|
+
];
|
|
2510
|
+
}
|
|
2202
2511
|
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
|
-
);
|
|
2512
|
+
const allExpected = staticExpectedArr(def.parsers);
|
|
2211
2513
|
if (def.disjoint) {
|
|
2212
2514
|
const codeV = v(ctx, "_code");
|
|
2213
2515
|
const valV = v(ctx, "_chv");
|
|
@@ -2216,6 +2518,27 @@ function emitChoice(def, ctx, pos) {
|
|
|
2216
2518
|
`${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
|
|
2217
2519
|
`${ind(ctx)}let ${valV}, ${endV} = ${pos}`
|
|
2218
2520
|
];
|
|
2521
|
+
const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
|
|
2522
|
+
if (plan.kind === "switch") {
|
|
2523
|
+
stmts.push(`${ind(ctx)}switch (${codeV}) {`);
|
|
2524
|
+
ctx.indent++;
|
|
2525
|
+
for (let i = 0; i < def.parsers.length; i++) {
|
|
2526
|
+
const p = def.parsers[i];
|
|
2527
|
+
for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
|
|
2528
|
+
stmts.push(`${ind(ctx)}{`);
|
|
2529
|
+
ctx.indent++;
|
|
2530
|
+
stmts.push(
|
|
2531
|
+
...emitDisjointArm(p, ctx, pos, valV, endV),
|
|
2532
|
+
`${ind(ctx)}break`
|
|
2533
|
+
);
|
|
2534
|
+
ctx.indent--;
|
|
2535
|
+
stmts.push(`${ind(ctx)}}`);
|
|
2536
|
+
}
|
|
2537
|
+
stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
|
|
2538
|
+
ctx.indent--;
|
|
2539
|
+
stmts.push(`${ind(ctx)}}`);
|
|
2540
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
2541
|
+
}
|
|
2219
2542
|
let first = true;
|
|
2220
2543
|
for (const p of def.parsers) {
|
|
2221
2544
|
const cond = firstSetCond(codeV, p._meta.firstSet);
|
|
@@ -2223,10 +2546,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2223
2546
|
first = false;
|
|
2224
2547
|
stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
|
|
2225
2548
|
ctx.indent++;
|
|
2226
|
-
|
|
2227
|
-
stmts.push(...r.stmts);
|
|
2228
|
-
stmts.push(`${ind(ctx)}${valV} = ${r.valueVar}`);
|
|
2229
|
-
stmts.push(`${ind(ctx)}${endV} = ${r.endVar}`);
|
|
2549
|
+
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
|
|
2230
2550
|
ctx.indent--;
|
|
2231
2551
|
stmts.push(`${ind(ctx)}}`);
|
|
2232
2552
|
}
|
|
@@ -2235,7 +2555,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2235
2555
|
}
|
|
2236
2556
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
2237
2557
|
}
|
|
2238
|
-
function emitGreedyClassify(def, superIndex,
|
|
2558
|
+
function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
2239
2559
|
const superParser = def.parsers[superIndex];
|
|
2240
2560
|
const regexDef = getCoreRegexDef(superParser);
|
|
2241
2561
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
@@ -2250,10 +2570,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
|
|
|
2250
2570
|
const wordV = v(ctx, "_gw");
|
|
2251
2571
|
const endV = v(ctx, "_ge");
|
|
2252
2572
|
const valV = v(ctx, "_gcv");
|
|
2573
|
+
const regexExpected = JSON.stringify(staticExpected(superParser));
|
|
2253
2574
|
const stmts = [
|
|
2254
2575
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
2255
2576
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
2256
|
-
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx,
|
|
2577
|
+
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
|
|
2257
2578
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
2258
2579
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
|
|
2259
2580
|
`${ind(ctx)}let ${valV}`
|
|
@@ -2311,13 +2632,15 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2311
2632
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
2312
2633
|
return { stmts, valueVar: valV, endVar: endV };
|
|
2313
2634
|
}
|
|
2314
|
-
function emitFirstMatch(def,
|
|
2635
|
+
function emitFirstMatch(def, ctx, pos) {
|
|
2315
2636
|
const resValV = v(ctx, "_crv");
|
|
2316
2637
|
const resEndV = v(ctx, "_cre");
|
|
2317
2638
|
const resOkV = v(ctx, "_crok");
|
|
2639
|
+
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2318
2640
|
const ind0 = ind(ctx);
|
|
2319
2641
|
const stmts = [
|
|
2320
|
-
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false
|
|
2642
|
+
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
2643
|
+
`${ind0}let ${slots.join(", ")}`
|
|
2321
2644
|
];
|
|
2322
2645
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2323
2646
|
const p = def.parsers[i];
|
|
@@ -2364,22 +2687,24 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2364
2687
|
stmts.push(`${ind0} ${resOkV} = true`);
|
|
2365
2688
|
stmts.push(`${ind0} }`);
|
|
2366
2689
|
stmts.push(`${ind0} }`);
|
|
2690
|
+
stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx }`);
|
|
2367
2691
|
if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
|
|
2368
2692
|
} else {
|
|
2369
2693
|
stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
2370
|
-
|
|
2694
|
+
stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
|
|
2371
2695
|
}
|
|
2372
2696
|
stmts.push(`${ind0}}`);
|
|
2373
2697
|
}
|
|
2374
|
-
|
|
2698
|
+
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
2699
|
+
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
2375
2700
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
2376
2701
|
}
|
|
2377
2702
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
2378
2703
|
if (strategy.tag === "greedyClassify")
|
|
2379
|
-
return emitGreedyClassify(def, strategy.superIndex,
|
|
2704
|
+
return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
|
|
2380
2705
|
if (strategy.tag === "literalsLongestFirst")
|
|
2381
2706
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
2382
|
-
return emitFirstMatch(def,
|
|
2707
|
+
return emitFirstMatch(def, ctx, pos);
|
|
2383
2708
|
}
|
|
2384
2709
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
2385
2710
|
const def = p._def;
|
|
@@ -2607,10 +2932,11 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2607
2932
|
}
|
|
2608
2933
|
function emitNot(def, ctx, pos) {
|
|
2609
2934
|
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
2935
|
+
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2610
2936
|
return {
|
|
2611
2937
|
stmts: [
|
|
2612
2938
|
...stmts,
|
|
2613
|
-
...emitIfFail(ctx, okVar, failBody(ctx,
|
|
2939
|
+
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
2614
2940
|
],
|
|
2615
2941
|
valueVar: "null",
|
|
2616
2942
|
endVar: pos
|
|
@@ -2642,7 +2968,7 @@ function emitNode(def, ctx, pos) {
|
|
|
2642
2968
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2643
2969
|
stmts.push(...innerStmts);
|
|
2644
2970
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2645
|
-
stmts.push(...emitIfFail(ctx, `!${okVar}`,
|
|
2971
|
+
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
2646
2972
|
let stV = "undefined";
|
|
2647
2973
|
if (clonesState) {
|
|
2648
2974
|
stV = v(ctx, "_nst");
|
|
@@ -2664,9 +2990,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2664
2990
|
const rv = v(ctx, "_rt");
|
|
2665
2991
|
const vv = v(ctx, "_rtv");
|
|
2666
2992
|
const ev = v(ctx, "_rte");
|
|
2993
|
+
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
2994
|
const stmts = [
|
|
2668
2995
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
2669
|
-
...emitIfFail(ctx, `!${rv}.ok`,
|
|
2996
|
+
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
2670
2997
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
2671
2998
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
2672
2999
|
];
|
|
@@ -2703,7 +3030,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2703
3030
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
2704
3031
|
}
|
|
2705
3032
|
const fnName = ctx.namedParsers.get(p);
|
|
2706
|
-
return emitNamedFnCall(ctx, fnName, pos
|
|
3033
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
2707
3034
|
}
|
|
2708
3035
|
function emitRecover(def, ctx, pos) {
|
|
2709
3036
|
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
@@ -2721,7 +3048,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
2721
3048
|
`${ind0} if (${sentOk}) break`,
|
|
2722
3049
|
`${ind0} ${scanV}++`,
|
|
2723
3050
|
`${ind0} }`,
|
|
2724
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected:
|
|
3051
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
|
|
2725
3052
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2726
3053
|
`${ind0} ${valVar} = ${errV}`,
|
|
2727
3054
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -2894,7 +3221,9 @@ function emit(p, ctx, pos) {
|
|
|
2894
3221
|
stmts: [
|
|
2895
3222
|
`${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
|
|
2896
3223
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
2897
|
-
|
|
3224
|
+
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3225
|
+
// and propagate the inner failure verbatim (interpreter parity).
|
|
3226
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
|
|
2898
3227
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
2899
3228
|
],
|
|
2900
3229
|
valueVar: vv,
|
|
@@ -2995,7 +3324,7 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
2995
3324
|
const recursive = /* @__PURE__ */ new Set();
|
|
2996
3325
|
const descended = /* @__PURE__ */ new Set();
|
|
2997
3326
|
const active = /* @__PURE__ */ new Set();
|
|
2998
|
-
function
|
|
3327
|
+
function walk2(p) {
|
|
2999
3328
|
const def = p._def;
|
|
3000
3329
|
if (def.tag === "lazy") {
|
|
3001
3330
|
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
@@ -3012,13 +3341,13 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3012
3341
|
return;
|
|
3013
3342
|
}
|
|
3014
3343
|
active.add(p);
|
|
3015
|
-
|
|
3344
|
+
walk2(resolved);
|
|
3016
3345
|
active.delete(p);
|
|
3017
3346
|
return;
|
|
3018
3347
|
}
|
|
3019
|
-
for (const child of childrenOf(def))
|
|
3348
|
+
for (const child of childrenOf(def)) walk2(child);
|
|
3020
3349
|
}
|
|
3021
|
-
for (const root of roots)
|
|
3350
|
+
for (const root of roots) walk2(root);
|
|
3022
3351
|
return { counts, recursive };
|
|
3023
3352
|
}
|
|
3024
3353
|
function compile(combinator, mapFnSources) {
|
|
@@ -3027,6 +3356,8 @@ function compile(combinator, mapFnSources) {
|
|
|
3027
3356
|
indent: 1,
|
|
3028
3357
|
regexDecls: [],
|
|
3029
3358
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3359
|
+
expectedDecls: [],
|
|
3360
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
3030
3361
|
mapFns: [],
|
|
3031
3362
|
mapFnSrcs: [],
|
|
3032
3363
|
buildFns: [],
|
|
@@ -3048,6 +3379,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3048
3379
|
const source = [
|
|
3049
3380
|
...emptyTlDecls,
|
|
3050
3381
|
...ctx.regexDecls,
|
|
3382
|
+
...ctx.expectedDecls,
|
|
3051
3383
|
"",
|
|
3052
3384
|
...namedPrelude,
|
|
3053
3385
|
ctx.namedFnDecls.join("\n\n"),
|
|
@@ -3060,6 +3392,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3060
3392
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3061
3393
|
...emptyTlDecls,
|
|
3062
3394
|
...ctx.regexDecls,
|
|
3395
|
+
...ctx.expectedDecls,
|
|
3063
3396
|
collatorDecl,
|
|
3064
3397
|
...namedPrelude,
|
|
3065
3398
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
@@ -3113,12 +3446,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3113
3446
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
3114
3447
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3115
3448
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3116
|
-
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3449
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3117
3450
|
if (!needsWrapper) return innerFn;
|
|
3118
3451
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3119
3452
|
const hoistedDecls = [
|
|
3120
3453
|
emptyTlDecl,
|
|
3121
3454
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
3455
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3122
3456
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3123
3457
|
mfDecl,
|
|
3124
3458
|
buildDecl,
|
|
@@ -3179,6 +3513,7 @@ function recover(combinator, sentinel) {
|
|
|
3179
3513
|
canMatchNewline: true,
|
|
3180
3514
|
isTrivia: false
|
|
3181
3515
|
};
|
|
3516
|
+
const expected = staticExpected(combinator);
|
|
3182
3517
|
return {
|
|
3183
3518
|
_tag: "recover",
|
|
3184
3519
|
_meta: meta,
|
|
@@ -3194,7 +3529,7 @@ function recover(combinator, sentinel) {
|
|
|
3194
3529
|
const error = {
|
|
3195
3530
|
_tag: "parseError",
|
|
3196
3531
|
span: { start: pos, end: scanPos },
|
|
3197
|
-
expected
|
|
3532
|
+
expected
|
|
3198
3533
|
};
|
|
3199
3534
|
ctx._errors?.push(error);
|
|
3200
3535
|
return { ok: true, value: error, span: { start: pos, end: scanPos } };
|
|
@@ -3205,58 +3540,6 @@ function isParseError(value) {
|
|
|
3205
3540
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
3206
3541
|
}
|
|
3207
3542
|
|
|
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
3543
|
// src/combinators/completions.ts
|
|
3261
3544
|
function completionsAt(combinator, input, offset) {
|
|
3262
3545
|
const probe = { offset, best: null };
|
|
@@ -3366,6 +3649,23 @@ function escapeClassChar(ch) {
|
|
|
3366
3649
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3367
3650
|
}
|
|
3368
3651
|
|
|
3652
|
+
// src/combinators/recover-list.ts
|
|
3653
|
+
function tolerantElement(item, sentinel, until) {
|
|
3654
|
+
const el = transform(
|
|
3655
|
+
sequence(not(until), recover(item, sentinel)),
|
|
3656
|
+
([, value]) => value
|
|
3657
|
+
);
|
|
3658
|
+
if (el._def.tag === "transform") el._def.fnSrc = "([, value]) => value";
|
|
3659
|
+
return el;
|
|
3660
|
+
}
|
|
3661
|
+
function sepByRecover(item, separator, until) {
|
|
3662
|
+
const sentinel = choice(separator, until);
|
|
3663
|
+
return sepBy(tolerantElement(item, sentinel, until), separator);
|
|
3664
|
+
}
|
|
3665
|
+
function manyRecover(item, until) {
|
|
3666
|
+
return many(tolerantElement(item, until, until));
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3369
3669
|
// src/functional/doc.ts
|
|
3370
3670
|
function isNode(x) {
|
|
3371
3671
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
@@ -3553,6 +3853,41 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
3553
3853
|
return { before, after };
|
|
3554
3854
|
}
|
|
3555
3855
|
|
|
3856
|
+
// src/cst/walk.ts
|
|
3857
|
+
function walk(root, visitor, ctx = void 0) {
|
|
3858
|
+
const go = (node2, parent) => {
|
|
3859
|
+
const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
|
|
3860
|
+
const children = node2.children;
|
|
3861
|
+
if (descend !== false && Array.isArray(children)) {
|
|
3862
|
+
for (const child of children) go(child, node2);
|
|
3863
|
+
}
|
|
3864
|
+
visitor.leave?.(node2, parent, ctx);
|
|
3865
|
+
};
|
|
3866
|
+
go(root, null);
|
|
3867
|
+
}
|
|
3868
|
+
function createVisitor(handlers) {
|
|
3869
|
+
const api = {
|
|
3870
|
+
visit(node2) {
|
|
3871
|
+
const handler = node2.type !== void 0 ? handlers[node2.type] : void 0;
|
|
3872
|
+
if (handler) return handler(node2, api);
|
|
3873
|
+
api.visitChildren(node2);
|
|
3874
|
+
return void 0;
|
|
3875
|
+
},
|
|
3876
|
+
visitChildren(node2) {
|
|
3877
|
+
const out = [];
|
|
3878
|
+
const children = node2.children;
|
|
3879
|
+
if (Array.isArray(children)) {
|
|
3880
|
+
for (const child of children) {
|
|
3881
|
+
const r = api.visit(child);
|
|
3882
|
+
if (r !== void 0) out.push(r);
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
return out;
|
|
3886
|
+
}
|
|
3887
|
+
};
|
|
3888
|
+
return (node2) => api.visit(node2);
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3556
3891
|
// src/cst/trivia-entries.ts
|
|
3557
3892
|
function entryOffset(i, stride) {
|
|
3558
3893
|
return i * stride;
|
|
@@ -3594,6 +3929,7 @@ export {
|
|
|
3594
3929
|
choice,
|
|
3595
3930
|
compile,
|
|
3596
3931
|
completionsAt,
|
|
3932
|
+
createVisitor,
|
|
3597
3933
|
expect,
|
|
3598
3934
|
guard,
|
|
3599
3935
|
isParseError,
|
|
@@ -3603,6 +3939,7 @@ export {
|
|
|
3603
3939
|
makeFunctionalDoc,
|
|
3604
3940
|
makeWord,
|
|
3605
3941
|
many,
|
|
3942
|
+
manyRecover,
|
|
3606
3943
|
noTrivia,
|
|
3607
3944
|
node,
|
|
3608
3945
|
not,
|
|
@@ -3617,12 +3954,14 @@ export {
|
|
|
3617
3954
|
rules,
|
|
3618
3955
|
scanTo,
|
|
3619
3956
|
sepBy,
|
|
3957
|
+
sepByRecover,
|
|
3620
3958
|
sequence,
|
|
3621
3959
|
skip,
|
|
3622
3960
|
staticExpected,
|
|
3623
3961
|
transform,
|
|
3624
3962
|
trivia,
|
|
3625
3963
|
triviaEntries,
|
|
3964
|
+
walk,
|
|
3626
3965
|
withCtx,
|
|
3627
3966
|
word
|
|
3628
3967
|
};
|