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