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.js
CHANGED
|
@@ -778,12 +778,15 @@ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
|
|
|
778
778
|
continue;
|
|
779
779
|
}
|
|
780
780
|
if (c === 47 && input.charCodeAt(pos + 1) === 42) {
|
|
781
|
-
const start = pos;
|
|
782
781
|
let j = pos + 2;
|
|
783
782
|
while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
783
|
+
if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
|
|
784
|
+
const start = pos;
|
|
785
|
+
pos = j + 2;
|
|
786
|
+
chunks.push({ start, end: pos, kindIndex: commentKind });
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
break;
|
|
787
790
|
}
|
|
788
791
|
break;
|
|
789
792
|
}
|
|
@@ -1280,38 +1283,108 @@ function noTrivia(root) {
|
|
|
1280
1283
|
return parser({ trivia: null }, root);
|
|
1281
1284
|
}
|
|
1282
1285
|
|
|
1286
|
+
// src/combinators/expect.ts
|
|
1287
|
+
function staticExpected(c) {
|
|
1288
|
+
const def = c._def;
|
|
1289
|
+
switch (def.tag) {
|
|
1290
|
+
case "literal":
|
|
1291
|
+
return [JSON.stringify(def.value)];
|
|
1292
|
+
case "regex":
|
|
1293
|
+
return [`/${def.source}/`];
|
|
1294
|
+
case "keywords":
|
|
1295
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
1296
|
+
case "label":
|
|
1297
|
+
return [def.label];
|
|
1298
|
+
case "choice":
|
|
1299
|
+
return def.parsers.flatMap(staticExpected);
|
|
1300
|
+
case "sequence":
|
|
1301
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
1302
|
+
case "node":
|
|
1303
|
+
case "grammar":
|
|
1304
|
+
case "trivia":
|
|
1305
|
+
case "optional":
|
|
1306
|
+
case "many":
|
|
1307
|
+
case "oneOrMore":
|
|
1308
|
+
case "transform":
|
|
1309
|
+
case "not":
|
|
1310
|
+
return staticExpected(def.parser);
|
|
1311
|
+
case "lazy":
|
|
1312
|
+
return staticExpected(def.thunk());
|
|
1313
|
+
default:
|
|
1314
|
+
return [];
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
function expect(combinator, label2) {
|
|
1318
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
1319
|
+
const meta = {
|
|
1320
|
+
firstSet: combinator._meta.firstSet,
|
|
1321
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1322
|
+
isTrivia: false
|
|
1323
|
+
};
|
|
1324
|
+
return {
|
|
1325
|
+
_tag: "expect",
|
|
1326
|
+
_meta: meta,
|
|
1327
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
1328
|
+
parse(input, pos, ctx) {
|
|
1329
|
+
const result = combinator.parse(input, pos, ctx);
|
|
1330
|
+
if (result.ok) return result;
|
|
1331
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
1332
|
+
ctx._errors?.push(error);
|
|
1333
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1283
1338
|
// src/compiler/scannable-run.ts
|
|
1339
|
+
var BACKSLASH = 92;
|
|
1284
1340
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1285
1341
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1342
|
+
function shorthandRanges(ch) {
|
|
1343
|
+
return ch === "d" ? [[48, 57]] : [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1344
|
+
}
|
|
1286
1345
|
function parseClassRanges(body) {
|
|
1287
1346
|
const ranges = [];
|
|
1288
1347
|
let i = 0;
|
|
1289
|
-
const
|
|
1348
|
+
const readAtom = () => {
|
|
1290
1349
|
const ch = body[i];
|
|
1291
1350
|
if (ch === void 0) return null;
|
|
1292
1351
|
if (ch === "\\") {
|
|
1293
1352
|
const e = body[i + 1];
|
|
1294
1353
|
if (e === void 0) return null;
|
|
1295
1354
|
i += 2;
|
|
1296
|
-
|
|
1355
|
+
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1356
|
+
if (e === "d" || e === "w") return { set: shorthandRanges(e) };
|
|
1357
|
+
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1358
|
+
return { cp: e.codePointAt(0) };
|
|
1297
1359
|
}
|
|
1298
1360
|
i += ch.length;
|
|
1299
|
-
return ch.codePointAt(0);
|
|
1361
|
+
return { cp: ch.codePointAt(0) };
|
|
1300
1362
|
};
|
|
1301
1363
|
while (i < body.length) {
|
|
1302
|
-
const lo =
|
|
1364
|
+
const lo = readAtom();
|
|
1303
1365
|
if (lo === null) return null;
|
|
1366
|
+
if ("set" in lo) {
|
|
1367
|
+
ranges.push(...lo.set);
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1304
1370
|
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1305
1371
|
i += 1;
|
|
1306
|
-
const hi =
|
|
1307
|
-
if (hi === null) return null;
|
|
1308
|
-
ranges.push([lo, hi]);
|
|
1372
|
+
const hi = readAtom();
|
|
1373
|
+
if (hi === null || "set" in hi) return null;
|
|
1374
|
+
ranges.push([lo.cp, hi.cp]);
|
|
1309
1375
|
} else {
|
|
1310
|
-
ranges.push([lo, lo]);
|
|
1376
|
+
ranges.push([lo.cp, lo.cp]);
|
|
1311
1377
|
}
|
|
1312
1378
|
}
|
|
1313
1379
|
return ranges.length ? ranges : null;
|
|
1314
1380
|
}
|
|
1381
|
+
function classToRanges(cls) {
|
|
1382
|
+
if (cls === "\\d") return shorthandRanges("d");
|
|
1383
|
+
if (cls === "\\w") return shorthandRanges("w");
|
|
1384
|
+
const body = cls.slice(1, -1);
|
|
1385
|
+
if (body.startsWith("^")) return null;
|
|
1386
|
+
return parseClassRanges(body);
|
|
1387
|
+
}
|
|
1315
1388
|
function literalCodePoints(frag) {
|
|
1316
1389
|
const out = [];
|
|
1317
1390
|
let i = 0;
|
|
@@ -1330,13 +1403,53 @@ function literalCodePoints(frag) {
|
|
|
1330
1403
|
}
|
|
1331
1404
|
return out.length ? out : null;
|
|
1332
1405
|
}
|
|
1406
|
+
function parseStringShape(source) {
|
|
1407
|
+
const q = source[0];
|
|
1408
|
+
if (q === void 0 || q === "\\" || META.has(q)) return null;
|
|
1409
|
+
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
1410
|
+
const inner = source.slice(1, -1);
|
|
1411
|
+
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
1412
|
+
const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
1413
|
+
let body;
|
|
1414
|
+
let esc;
|
|
1415
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
|
|
1416
|
+
if (m) {
|
|
1417
|
+
body = m[1];
|
|
1418
|
+
esc = m[2];
|
|
1419
|
+
} else {
|
|
1420
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
|
|
1421
|
+
if (m) {
|
|
1422
|
+
esc = m[1];
|
|
1423
|
+
body = m[2];
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (body === void 0 || esc === void 0) return null;
|
|
1427
|
+
const excluded = parseClassRanges(body);
|
|
1428
|
+
if (!excluded) return null;
|
|
1429
|
+
const qcp = q.codePointAt(0);
|
|
1430
|
+
const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
|
|
1431
|
+
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1432
|
+
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1433
|
+
}
|
|
1333
1434
|
function parseScanShape(source) {
|
|
1334
1435
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1335
1436
|
if (m) {
|
|
1336
1437
|
if (m[1].startsWith("^")) return null;
|
|
1337
1438
|
const ranges = parseClassRanges(m[1]);
|
|
1338
|
-
return ranges ? { kind: "chars", ranges } : null;
|
|
1439
|
+
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1440
|
+
}
|
|
1441
|
+
m = /^\\([dw])([+*])$/.exec(source);
|
|
1442
|
+
if (m) {
|
|
1443
|
+
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1339
1444
|
}
|
|
1445
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dw])(\[(?:\\.|[^\]])+\]|\\[dw])\*$/.exec(source);
|
|
1446
|
+
if (m) {
|
|
1447
|
+
const head = classToRanges(m[1]);
|
|
1448
|
+
const tail = classToRanges(m[2]);
|
|
1449
|
+
return head && tail ? { kind: "ident", head, tail } : null;
|
|
1450
|
+
}
|
|
1451
|
+
const str = parseStringShape(source);
|
|
1452
|
+
if (str) return str;
|
|
1340
1453
|
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1341
1454
|
if (m) {
|
|
1342
1455
|
const open = literalCodePoints(m[1]);
|
|
@@ -1354,66 +1467,147 @@ function parseScanShape(source) {
|
|
|
1354
1467
|
}
|
|
1355
1468
|
return null;
|
|
1356
1469
|
}
|
|
1470
|
+
function scanShapeFromRegex(source, flags) {
|
|
1471
|
+
if (/[imsu]/.test(flags)) return null;
|
|
1472
|
+
return parseScanShape(source);
|
|
1473
|
+
}
|
|
1357
1474
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1358
1475
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
1359
1476
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1360
1477
|
).join(" && ");
|
|
1361
|
-
|
|
1478
|
+
var LINE_TERMINATORS = [10, 13, 8232, 8233];
|
|
1479
|
+
function stringHardStop(shape) {
|
|
1480
|
+
return shape.excluded.filter(
|
|
1481
|
+
([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1485
|
+
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
1362
1486
|
if (shape.kind === "chars") {
|
|
1487
|
+
const cur = mint("_e");
|
|
1363
1488
|
return {
|
|
1364
1489
|
setup: [
|
|
1365
|
-
|
|
1366
|
-
|
|
1490
|
+
`${ind2}let ${cur} = ${start}`,
|
|
1491
|
+
`${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
|
|
1367
1492
|
],
|
|
1368
|
-
|
|
1493
|
+
// `*` always matches (possibly empty); `+` needs at least one char.
|
|
1494
|
+
ok: shape.minOne ? `${cur} > ${start}` : "true",
|
|
1495
|
+
end: cur
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
if (shape.kind === "ident") {
|
|
1499
|
+
const cur = mint("_e");
|
|
1500
|
+
return {
|
|
1501
|
+
setup: [
|
|
1502
|
+
`${ind2}let ${cur} = ${start}`,
|
|
1503
|
+
`${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
|
|
1504
|
+
`${ind2} ${cur} = ${start} + 1`,
|
|
1505
|
+
`${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
|
|
1506
|
+
`${ind2}}`
|
|
1507
|
+
],
|
|
1508
|
+
ok: `${cur} > ${start}`,
|
|
1509
|
+
end: cur
|
|
1369
1510
|
};
|
|
1370
1511
|
}
|
|
1371
1512
|
if (shape.kind === "until") {
|
|
1513
|
+
const j2 = mint("_j");
|
|
1514
|
+
const openLen2 = shape.open.length;
|
|
1515
|
+
const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
1516
|
+
return {
|
|
1517
|
+
setup: [
|
|
1518
|
+
`${ind2}let ${j2} = ${start}`,
|
|
1519
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
|
|
1520
|
+
`${ind2} ${j2} = ${start} + ${openLen2}`,
|
|
1521
|
+
`${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
|
|
1522
|
+
`${ind2}}`
|
|
1523
|
+
],
|
|
1524
|
+
// An open-until-terminator always completes (stop char or EOF), so a matched
|
|
1525
|
+
// open literal is a full match; `end === start` iff the open didn't match.
|
|
1526
|
+
ok: `${j2} > ${start}`,
|
|
1527
|
+
end: j2
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
if (shape.kind === "string") {
|
|
1531
|
+
const okV = mint("_ok");
|
|
1532
|
+
const endV2 = mint("_end");
|
|
1533
|
+
const i = mint("_i");
|
|
1534
|
+
const c2 = mint("_c");
|
|
1535
|
+
const hard = stringHardStop(shape);
|
|
1536
|
+
const ltLines = shape.escLineTerm ? [] : (() => {
|
|
1537
|
+
const c3 = mint("_c");
|
|
1538
|
+
return [
|
|
1539
|
+
`${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
|
|
1540
|
+
`${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
|
|
1541
|
+
];
|
|
1542
|
+
})();
|
|
1372
1543
|
return {
|
|
1373
1544
|
setup: [
|
|
1374
|
-
|
|
1375
|
-
|
|
1545
|
+
`${ind2}let ${okV} = false`,
|
|
1546
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
1547
|
+
`${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
|
|
1548
|
+
`${ind2} let ${i} = ${start} + 1`,
|
|
1549
|
+
`${ind2} while (${i} < input.length) {`,
|
|
1550
|
+
`${ind2} const ${c2} = input.charCodeAt(${i})`,
|
|
1551
|
+
`${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
|
|
1552
|
+
`${ind2} if (${c2} === ${BACKSLASH}) {`,
|
|
1553
|
+
`${ind2} if (${i} + 1 >= input.length) break`,
|
|
1554
|
+
...ltLines,
|
|
1555
|
+
`${ind2} ${i} += 2`,
|
|
1556
|
+
`${ind2} continue`,
|
|
1557
|
+
`${ind2} }`,
|
|
1558
|
+
...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
|
|
1559
|
+
`${ind2} ${i}++`,
|
|
1560
|
+
`${ind2} }`,
|
|
1561
|
+
`${ind2}}`
|
|
1376
1562
|
],
|
|
1377
|
-
|
|
1563
|
+
ok: okV,
|
|
1564
|
+
end: endV2
|
|
1378
1565
|
};
|
|
1379
1566
|
}
|
|
1380
|
-
const
|
|
1567
|
+
const j = mint("_j");
|
|
1568
|
+
const endV = mint("_end");
|
|
1569
|
+
const openLen = shape.open.length;
|
|
1570
|
+
const closeLen = shape.close.length;
|
|
1571
|
+
const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
1572
|
+
const closeChk = litCond(j, shape.close);
|
|
1381
1573
|
return {
|
|
1382
1574
|
setup: [
|
|
1383
|
-
|
|
1384
|
-
|
|
1575
|
+
`${ind2}let ${j} = ${start}`,
|
|
1576
|
+
`${ind2}let ${endV} = ${start}`,
|
|
1577
|
+
`${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
|
|
1578
|
+
`${ind2} ${j} = ${start} + ${openLen}`,
|
|
1579
|
+
`${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
|
|
1580
|
+
`${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
|
|
1581
|
+
`${ind2}}`
|
|
1385
1582
|
],
|
|
1386
|
-
|
|
1583
|
+
ok: `${endV} > ${start}`,
|
|
1584
|
+
end: endV
|
|
1387
1585
|
};
|
|
1388
1586
|
}
|
|
1389
|
-
function
|
|
1390
|
-
const
|
|
1391
|
-
return ` if (${
|
|
1392
|
-
}
|
|
1393
|
-
function scanBranch(shape) {
|
|
1394
|
-
if (shape.kind === "chars") {
|
|
1395
|
-
return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
|
|
1396
|
-
}
|
|
1397
|
-
const { setup, endVar } = advanceExpr(shape);
|
|
1398
|
-
return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
|
|
1587
|
+
function scanBranch(shape, mint) {
|
|
1588
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1589
|
+
return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
|
|
1399
1590
|
}
|
|
1400
|
-
function
|
|
1591
|
+
function scanBranchLabeled(shape, kindIndex, mint) {
|
|
1592
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1401
1593
|
return [
|
|
1594
|
+
...m.setup,
|
|
1595
|
+
` if (${m.end} > _e) {`,
|
|
1402
1596
|
` if (_cap) {`,
|
|
1403
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${
|
|
1404
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${
|
|
1405
|
-
` }
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
|
|
1411
|
-
if (endVar !== "_e") lines.push(` _e = ${endVar}`);
|
|
1412
|
-
lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
|
|
1413
|
-
return lines.join("\n");
|
|
1597
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
|
|
1598
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
1599
|
+
` }`,
|
|
1600
|
+
` _e = ${m.end}`,
|
|
1601
|
+
` continue`,
|
|
1602
|
+
` }`
|
|
1603
|
+
].join("\n");
|
|
1414
1604
|
}
|
|
1415
1605
|
|
|
1416
1606
|
// src/compiler/trivia-fast-path.ts
|
|
1607
|
+
function makeMint() {
|
|
1608
|
+
let n = 0;
|
|
1609
|
+
return (prefix = "_v") => `${prefix}${n++}`;
|
|
1610
|
+
}
|
|
1417
1611
|
function unwrapTrivia2(p) {
|
|
1418
1612
|
let cur = p;
|
|
1419
1613
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1430,14 +1624,14 @@ function choiceArms(p) {
|
|
|
1430
1624
|
return null;
|
|
1431
1625
|
}
|
|
1432
1626
|
function armShape(arm) {
|
|
1433
|
-
const
|
|
1434
|
-
return
|
|
1627
|
+
const d = getCoreRegexDef(arm);
|
|
1628
|
+
return d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1435
1629
|
}
|
|
1436
1630
|
function analyzeTriviaFastPath(trivia2) {
|
|
1437
1631
|
const core = unwrapTrivia2(trivia2);
|
|
1438
|
-
const
|
|
1439
|
-
if (
|
|
1440
|
-
const shape =
|
|
1632
|
+
const directDef = getCoreRegexDef(core);
|
|
1633
|
+
if (directDef) {
|
|
1634
|
+
const shape = scanShapeFromRegex(directDef.source, directDef.flags);
|
|
1441
1635
|
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1442
1636
|
}
|
|
1443
1637
|
const inner = oneOrMoreInner(core);
|
|
@@ -1461,10 +1655,11 @@ var CAP_RECORD = [
|
|
|
1461
1655
|
` }`
|
|
1462
1656
|
].join("\n");
|
|
1463
1657
|
function composeFastLoop(shapes) {
|
|
1658
|
+
const mint = makeMint();
|
|
1464
1659
|
return [
|
|
1465
1660
|
` while (_e < input.length) {`,
|
|
1466
1661
|
` const c = input.charCodeAt(_e)`,
|
|
1467
|
-
...shapes.map(scanBranch),
|
|
1662
|
+
...shapes.map((s) => scanBranch(s, mint)),
|
|
1468
1663
|
` break`,
|
|
1469
1664
|
` }`
|
|
1470
1665
|
].join("\n");
|
|
@@ -1484,20 +1679,21 @@ function analyzeLabeledScannableRun(trivia2) {
|
|
|
1484
1679
|
if (!spec) return null;
|
|
1485
1680
|
const out = [];
|
|
1486
1681
|
for (const arm of spec.arms) {
|
|
1487
|
-
const
|
|
1488
|
-
const shape =
|
|
1682
|
+
const d = getCoreRegexDef(arm.parser);
|
|
1683
|
+
const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1489
1684
|
if (!shape) return null;
|
|
1490
1685
|
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1491
1686
|
}
|
|
1492
1687
|
return out;
|
|
1493
1688
|
}
|
|
1494
1689
|
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
1690
|
+
const mint = makeMint();
|
|
1495
1691
|
return [
|
|
1496
1692
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1497
1693
|
` let _e = _pos`,
|
|
1498
1694
|
` while (_e < input.length) {`,
|
|
1499
1695
|
` const c = input.charCodeAt(_e)`,
|
|
1500
|
-
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
1696
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
|
|
1501
1697
|
` break`,
|
|
1502
1698
|
` }`,
|
|
1503
1699
|
` return _e`,
|
|
@@ -1574,6 +1770,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
|
|
|
1574
1770
|
].join("\n");
|
|
1575
1771
|
}
|
|
1576
1772
|
|
|
1773
|
+
// src/compiler/scannable-terminal.ts
|
|
1774
|
+
function emitScannableTerminal(shape, c) {
|
|
1775
|
+
const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
|
|
1776
|
+
const stmts = [...m.setup];
|
|
1777
|
+
if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
|
|
1778
|
+
stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
|
|
1779
|
+
return { stmts, endVar: m.end };
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1577
1782
|
// src/compiler/inline-build.ts
|
|
1578
1783
|
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*$/;
|
|
1579
1784
|
function escapeRegExp(s) {
|
|
@@ -1695,14 +1900,45 @@ function failReturn(expected, posExpr) {
|
|
|
1695
1900
|
function failReturnArr(expectedArr, posExpr) {
|
|
1696
1901
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1697
1902
|
}
|
|
1903
|
+
function hoistExpected(ctx, constArrSource) {
|
|
1904
|
+
let name = ctx.expectedMap.get(constArrSource);
|
|
1905
|
+
if (name === void 0) {
|
|
1906
|
+
name = `_fx${ctx.expectedDecls.length}`;
|
|
1907
|
+
ctx.expectedDecls.push(`const ${name} = Object.freeze(${constArrSource})`);
|
|
1908
|
+
ctx.expectedMap.set(constArrSource, name);
|
|
1909
|
+
}
|
|
1910
|
+
return name;
|
|
1911
|
+
}
|
|
1698
1912
|
function failBody(ctx, expected, posExpr) {
|
|
1699
|
-
if (ctx.failLabel)
|
|
1913
|
+
if (ctx.failLabel) {
|
|
1914
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
1915
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
1916
|
+
}
|
|
1700
1917
|
return failReturn(expected, posExpr);
|
|
1701
1918
|
}
|
|
1702
|
-
function failArrBody(ctx, expectedArr, posExpr) {
|
|
1703
|
-
if (ctx.failLabel)
|
|
1919
|
+
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
1920
|
+
if (ctx.failLabel) {
|
|
1921
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
1922
|
+
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
1923
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
1924
|
+
}
|
|
1925
|
+
if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1704
1926
|
return failReturnArr(expectedArr, posExpr);
|
|
1705
1927
|
}
|
|
1928
|
+
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
1929
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
1930
|
+
}
|
|
1931
|
+
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
1932
|
+
if (srcCtx !== "_ctx") {
|
|
1933
|
+
if (ctx.failLabel) {
|
|
1934
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
1935
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
1936
|
+
}
|
|
1937
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
1938
|
+
}
|
|
1939
|
+
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
1940
|
+
return resultFromRecorded();
|
|
1941
|
+
}
|
|
1706
1942
|
function emitIfFail(ctx, cond, body) {
|
|
1707
1943
|
return [
|
|
1708
1944
|
`${ind(ctx)}if (${cond}) {`,
|
|
@@ -1738,13 +1974,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
|
1738
1974
|
`}`
|
|
1739
1975
|
].join("\n"));
|
|
1740
1976
|
}
|
|
1741
|
-
function emitNamedFnCall(ctx, fnName, pos
|
|
1977
|
+
function emitNamedFnCall(ctx, fnName, pos) {
|
|
1742
1978
|
const vv = v(ctx, "_pfv");
|
|
1743
1979
|
const ev = v(ctx, "_pfe");
|
|
1744
1980
|
return {
|
|
1745
1981
|
stmts: [
|
|
1746
1982
|
`${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
|
|
1747
|
-
|
|
1983
|
+
// ref/lazy returns the inner failure verbatim — propagate the recorded
|
|
1984
|
+
// deepest failure (shares _ctx with the named fn), not a "parser" label.
|
|
1985
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
|
|
1748
1986
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
1749
1987
|
],
|
|
1750
1988
|
valueVar: vv,
|
|
@@ -1854,6 +2092,27 @@ function firstSetCond(codeVar, fs) {
|
|
|
1854
2092
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1855
2093
|
).join(" || ");
|
|
1856
2094
|
}
|
|
2095
|
+
var SWITCH_RANGE_LIMIT = 4;
|
|
2096
|
+
var SWITCH_MAX_CASES = 48;
|
|
2097
|
+
var SWITCH_MIN_CASES = 3;
|
|
2098
|
+
var _forceDisjointIf = false;
|
|
2099
|
+
function planDisjointDispatch(parsers) {
|
|
2100
|
+
const cases = [];
|
|
2101
|
+
let total = 0;
|
|
2102
|
+
for (const p of parsers) {
|
|
2103
|
+
const fs = p._meta.firstSet;
|
|
2104
|
+
if (fs.kind !== "ranges") return { kind: "if" };
|
|
2105
|
+
const pts = [];
|
|
2106
|
+
for (const r of fs.ranges) {
|
|
2107
|
+
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2108
|
+
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2109
|
+
}
|
|
2110
|
+
total += pts.length;
|
|
2111
|
+
if (total > SWITCH_MAX_CASES) return { kind: "if" };
|
|
2112
|
+
cases.push(pts);
|
|
2113
|
+
}
|
|
2114
|
+
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2115
|
+
}
|
|
1857
2116
|
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1858
2117
|
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1859
2118
|
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
@@ -2070,6 +2329,22 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2070
2329
|
return { stmts, valueVar: vv, endVar };
|
|
2071
2330
|
}
|
|
2072
2331
|
function emitRegex(def, ctx, pos) {
|
|
2332
|
+
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2333
|
+
const shape = scanShapeFromRegex(def.source, def.flags);
|
|
2334
|
+
if (shape) {
|
|
2335
|
+
const vv2 = v(ctx);
|
|
2336
|
+
const scanned = emitScannableTerminal(shape, {
|
|
2337
|
+
ind: ind(ctx),
|
|
2338
|
+
pos,
|
|
2339
|
+
valueVar: vv2,
|
|
2340
|
+
failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
|
|
2341
|
+
fresh: (prefix) => v(ctx, prefix)
|
|
2342
|
+
});
|
|
2343
|
+
if (scanned) {
|
|
2344
|
+
const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
|
|
2345
|
+
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2073
2348
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2074
2349
|
const key = `${def.optimizedSource}/${flags}`;
|
|
2075
2350
|
let rName = ctx.regexMap.get(key);
|
|
@@ -2080,7 +2355,6 @@ function emitRegex(def, ctx, pos) {
|
|
|
2080
2355
|
}
|
|
2081
2356
|
const mv = v(ctx, "_m");
|
|
2082
2357
|
const vv = v(ctx);
|
|
2083
|
-
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2084
2358
|
const stmts = [
|
|
2085
2359
|
`${ind(ctx)}${rName}.lastIndex = ${pos}`,
|
|
2086
2360
|
`${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
|
|
@@ -2139,15 +2413,43 @@ function emitSeq(def, ctx, pos) {
|
|
|
2139
2413
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
2140
2414
|
return { stmts, valueVar: arrV, endVar };
|
|
2141
2415
|
}
|
|
2416
|
+
function staticExpectedArr(parsers) {
|
|
2417
|
+
return JSON.stringify(parsers.flatMap((p) => {
|
|
2418
|
+
const e = staticExpected(p);
|
|
2419
|
+
return e.length > 0 ? e : [p._tag];
|
|
2420
|
+
}));
|
|
2421
|
+
}
|
|
2422
|
+
function failsAtStart(p) {
|
|
2423
|
+
const d = p._def;
|
|
2424
|
+
switch (d.tag) {
|
|
2425
|
+
case "literal":
|
|
2426
|
+
case "regex":
|
|
2427
|
+
case "keywords":
|
|
2428
|
+
case "guard":
|
|
2429
|
+
case "not":
|
|
2430
|
+
return true;
|
|
2431
|
+
case "transform":
|
|
2432
|
+
case "label":
|
|
2433
|
+
return failsAtStart(d.parser);
|
|
2434
|
+
default:
|
|
2435
|
+
return false;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
2439
|
+
if (failsAtStart(p)) {
|
|
2440
|
+
const r = emit(p, ctx, pos);
|
|
2441
|
+
return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
|
|
2442
|
+
}
|
|
2443
|
+
const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
2444
|
+
return [
|
|
2445
|
+
...stmts,
|
|
2446
|
+
...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
|
|
2447
|
+
`${ind(ctx)}${valV} = ${valVar}`,
|
|
2448
|
+
`${ind(ctx)}${endV} = ${endVar}`
|
|
2449
|
+
];
|
|
2450
|
+
}
|
|
2142
2451
|
function emitChoice(def, ctx, pos) {
|
|
2143
|
-
const allExpected =
|
|
2144
|
-
def.parsers.map((p) => {
|
|
2145
|
-
const d = p._def;
|
|
2146
|
-
if (d.tag === "literal") return JSON.stringify(d.value);
|
|
2147
|
-
if (d.tag === "regex") return `/${d.source}/`;
|
|
2148
|
-
return p._tag;
|
|
2149
|
-
})
|
|
2150
|
-
);
|
|
2452
|
+
const allExpected = staticExpectedArr(def.parsers);
|
|
2151
2453
|
if (def.disjoint) {
|
|
2152
2454
|
const codeV = v(ctx, "_code");
|
|
2153
2455
|
const valV = v(ctx, "_chv");
|
|
@@ -2156,6 +2458,27 @@ function emitChoice(def, ctx, pos) {
|
|
|
2156
2458
|
`${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
|
|
2157
2459
|
`${ind(ctx)}let ${valV}, ${endV} = ${pos}`
|
|
2158
2460
|
];
|
|
2461
|
+
const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
|
|
2462
|
+
if (plan.kind === "switch") {
|
|
2463
|
+
stmts.push(`${ind(ctx)}switch (${codeV}) {`);
|
|
2464
|
+
ctx.indent++;
|
|
2465
|
+
for (let i = 0; i < def.parsers.length; i++) {
|
|
2466
|
+
const p = def.parsers[i];
|
|
2467
|
+
for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
|
|
2468
|
+
stmts.push(`${ind(ctx)}{`);
|
|
2469
|
+
ctx.indent++;
|
|
2470
|
+
stmts.push(
|
|
2471
|
+
...emitDisjointArm(p, ctx, pos, valV, endV),
|
|
2472
|
+
`${ind(ctx)}break`
|
|
2473
|
+
);
|
|
2474
|
+
ctx.indent--;
|
|
2475
|
+
stmts.push(`${ind(ctx)}}`);
|
|
2476
|
+
}
|
|
2477
|
+
stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
|
|
2478
|
+
ctx.indent--;
|
|
2479
|
+
stmts.push(`${ind(ctx)}}`);
|
|
2480
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
2481
|
+
}
|
|
2159
2482
|
let first = true;
|
|
2160
2483
|
for (const p of def.parsers) {
|
|
2161
2484
|
const cond = firstSetCond(codeV, p._meta.firstSet);
|
|
@@ -2163,10 +2486,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2163
2486
|
first = false;
|
|
2164
2487
|
stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
|
|
2165
2488
|
ctx.indent++;
|
|
2166
|
-
|
|
2167
|
-
stmts.push(...r.stmts);
|
|
2168
|
-
stmts.push(`${ind(ctx)}${valV} = ${r.valueVar}`);
|
|
2169
|
-
stmts.push(`${ind(ctx)}${endV} = ${r.endVar}`);
|
|
2489
|
+
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
|
|
2170
2490
|
ctx.indent--;
|
|
2171
2491
|
stmts.push(`${ind(ctx)}}`);
|
|
2172
2492
|
}
|
|
@@ -2175,7 +2495,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2175
2495
|
}
|
|
2176
2496
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
2177
2497
|
}
|
|
2178
|
-
function emitGreedyClassify(def, superIndex,
|
|
2498
|
+
function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
2179
2499
|
const superParser = def.parsers[superIndex];
|
|
2180
2500
|
const regexDef = getCoreRegexDef(superParser);
|
|
2181
2501
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
@@ -2190,10 +2510,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
|
|
|
2190
2510
|
const wordV = v(ctx, "_gw");
|
|
2191
2511
|
const endV = v(ctx, "_ge");
|
|
2192
2512
|
const valV = v(ctx, "_gcv");
|
|
2513
|
+
const regexExpected = JSON.stringify(staticExpected(superParser));
|
|
2193
2514
|
const stmts = [
|
|
2194
2515
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
2195
2516
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
2196
|
-
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx,
|
|
2517
|
+
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
|
|
2197
2518
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
2198
2519
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
|
|
2199
2520
|
`${ind(ctx)}let ${valV}`
|
|
@@ -2251,13 +2572,15 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2251
2572
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
2252
2573
|
return { stmts, valueVar: valV, endVar: endV };
|
|
2253
2574
|
}
|
|
2254
|
-
function emitFirstMatch(def,
|
|
2575
|
+
function emitFirstMatch(def, ctx, pos) {
|
|
2255
2576
|
const resValV = v(ctx, "_crv");
|
|
2256
2577
|
const resEndV = v(ctx, "_cre");
|
|
2257
2578
|
const resOkV = v(ctx, "_crok");
|
|
2579
|
+
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2258
2580
|
const ind0 = ind(ctx);
|
|
2259
2581
|
const stmts = [
|
|
2260
|
-
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false
|
|
2582
|
+
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
2583
|
+
`${ind0}let ${slots.join(", ")}`
|
|
2261
2584
|
];
|
|
2262
2585
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2263
2586
|
const p = def.parsers[i];
|
|
@@ -2304,22 +2627,24 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2304
2627
|
stmts.push(`${ind0} ${resOkV} = true`);
|
|
2305
2628
|
stmts.push(`${ind0} }`);
|
|
2306
2629
|
stmts.push(`${ind0} }`);
|
|
2630
|
+
stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx }`);
|
|
2307
2631
|
if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
|
|
2308
2632
|
} else {
|
|
2309
2633
|
stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
2310
|
-
|
|
2634
|
+
stmts.push(`${ind0} else { ${slots[i]} = _ctx._fx${rollback ? `; ${rollback}` : ""} }`);
|
|
2311
2635
|
}
|
|
2312
2636
|
stmts.push(`${ind0}}`);
|
|
2313
2637
|
}
|
|
2314
|
-
|
|
2638
|
+
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
2639
|
+
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
2315
2640
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
2316
2641
|
}
|
|
2317
2642
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
2318
2643
|
if (strategy.tag === "greedyClassify")
|
|
2319
|
-
return emitGreedyClassify(def, strategy.superIndex,
|
|
2644
|
+
return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
|
|
2320
2645
|
if (strategy.tag === "literalsLongestFirst")
|
|
2321
2646
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
2322
|
-
return emitFirstMatch(def,
|
|
2647
|
+
return emitFirstMatch(def, ctx, pos);
|
|
2323
2648
|
}
|
|
2324
2649
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
2325
2650
|
const def = p._def;
|
|
@@ -2547,10 +2872,11 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2547
2872
|
}
|
|
2548
2873
|
function emitNot(def, ctx, pos) {
|
|
2549
2874
|
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
2875
|
+
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2550
2876
|
return {
|
|
2551
2877
|
stmts: [
|
|
2552
2878
|
...stmts,
|
|
2553
|
-
...emitIfFail(ctx, okVar, failBody(ctx,
|
|
2879
|
+
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
2554
2880
|
],
|
|
2555
2881
|
valueVar: "null",
|
|
2556
2882
|
endVar: pos
|
|
@@ -2582,7 +2908,7 @@ function emitNode(def, ctx, pos) {
|
|
|
2582
2908
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2583
2909
|
stmts.push(...innerStmts);
|
|
2584
2910
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2585
|
-
stmts.push(...emitIfFail(ctx, `!${okVar}`,
|
|
2911
|
+
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
2586
2912
|
let stV = "undefined";
|
|
2587
2913
|
if (clonesState) {
|
|
2588
2914
|
stV = v(ctx, "_nst");
|
|
@@ -2604,9 +2930,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2604
2930
|
const rv = v(ctx, "_rt");
|
|
2605
2931
|
const vv = v(ctx, "_rtv");
|
|
2606
2932
|
const ev = v(ctx, "_rte");
|
|
2933
|
+
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 }`;
|
|
2607
2934
|
const stmts = [
|
|
2608
2935
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
2609
|
-
...emitIfFail(ctx, `!${rv}.ok`,
|
|
2936
|
+
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
2610
2937
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
2611
2938
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
2612
2939
|
];
|
|
@@ -2643,7 +2970,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2643
2970
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
2644
2971
|
}
|
|
2645
2972
|
const fnName = ctx.namedParsers.get(p);
|
|
2646
|
-
return emitNamedFnCall(ctx, fnName, pos
|
|
2973
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
2647
2974
|
}
|
|
2648
2975
|
function emitRecover(def, ctx, pos) {
|
|
2649
2976
|
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
@@ -2661,7 +2988,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
2661
2988
|
`${ind0} if (${sentOk}) break`,
|
|
2662
2989
|
`${ind0} ${scanV}++`,
|
|
2663
2990
|
`${ind0} }`,
|
|
2664
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected:
|
|
2991
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
|
|
2665
2992
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2666
2993
|
`${ind0} ${valVar} = ${errV}`,
|
|
2667
2994
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -2834,7 +3161,9 @@ function emit(p, ctx, pos) {
|
|
|
2834
3161
|
stmts: [
|
|
2835
3162
|
`${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
|
|
2836
3163
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
2837
|
-
|
|
3164
|
+
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3165
|
+
// and propagate the inner failure verbatim (interpreter parity).
|
|
3166
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
|
|
2838
3167
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
2839
3168
|
],
|
|
2840
3169
|
valueVar: vv,
|
|
@@ -2935,7 +3264,7 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
2935
3264
|
const recursive = /* @__PURE__ */ new Set();
|
|
2936
3265
|
const descended = /* @__PURE__ */ new Set();
|
|
2937
3266
|
const active = /* @__PURE__ */ new Set();
|
|
2938
|
-
function
|
|
3267
|
+
function walk2(p) {
|
|
2939
3268
|
const def = p._def;
|
|
2940
3269
|
if (def.tag === "lazy") {
|
|
2941
3270
|
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
@@ -2952,13 +3281,13 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
2952
3281
|
return;
|
|
2953
3282
|
}
|
|
2954
3283
|
active.add(p);
|
|
2955
|
-
|
|
3284
|
+
walk2(resolved);
|
|
2956
3285
|
active.delete(p);
|
|
2957
3286
|
return;
|
|
2958
3287
|
}
|
|
2959
|
-
for (const child of childrenOf(def))
|
|
3288
|
+
for (const child of childrenOf(def)) walk2(child);
|
|
2960
3289
|
}
|
|
2961
|
-
for (const root of roots)
|
|
3290
|
+
for (const root of roots) walk2(root);
|
|
2962
3291
|
return { counts, recursive };
|
|
2963
3292
|
}
|
|
2964
3293
|
function compile(combinator, mapFnSources) {
|
|
@@ -2967,6 +3296,8 @@ function compile(combinator, mapFnSources) {
|
|
|
2967
3296
|
indent: 1,
|
|
2968
3297
|
regexDecls: [],
|
|
2969
3298
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3299
|
+
expectedDecls: [],
|
|
3300
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
2970
3301
|
mapFns: [],
|
|
2971
3302
|
mapFnSrcs: [],
|
|
2972
3303
|
buildFns: [],
|
|
@@ -2988,6 +3319,7 @@ function compile(combinator, mapFnSources) {
|
|
|
2988
3319
|
const source = [
|
|
2989
3320
|
...emptyTlDecls,
|
|
2990
3321
|
...ctx.regexDecls,
|
|
3322
|
+
...ctx.expectedDecls,
|
|
2991
3323
|
"",
|
|
2992
3324
|
...namedPrelude,
|
|
2993
3325
|
ctx.namedFnDecls.join("\n\n"),
|
|
@@ -3000,6 +3332,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3000
3332
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3001
3333
|
...emptyTlDecls,
|
|
3002
3334
|
...ctx.regexDecls,
|
|
3335
|
+
...ctx.expectedDecls,
|
|
3003
3336
|
collatorDecl,
|
|
3004
3337
|
...namedPrelude,
|
|
3005
3338
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
@@ -3045,6 +3378,8 @@ function compileRuleMap(ruleMap) {
|
|
|
3045
3378
|
indent: 1,
|
|
3046
3379
|
regexDecls: [],
|
|
3047
3380
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3381
|
+
expectedDecls: [],
|
|
3382
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
3048
3383
|
mapFns: [],
|
|
3049
3384
|
mapFnSrcs: [],
|
|
3050
3385
|
buildFns: [],
|
|
@@ -3073,6 +3408,7 @@ function compileRuleMap(ruleMap) {
|
|
|
3073
3408
|
const hoistedDecls = [
|
|
3074
3409
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
3075
3410
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
3411
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3076
3412
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3077
3413
|
mfDecl,
|
|
3078
3414
|
buildDecl,
|
|
@@ -3114,12 +3450,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3114
3450
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
3115
3451
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3116
3452
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3117
|
-
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3453
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3118
3454
|
if (!needsWrapper) return innerFn;
|
|
3119
3455
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3120
3456
|
const hoistedDecls = [
|
|
3121
3457
|
emptyTlDecl,
|
|
3122
3458
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
3459
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3123
3460
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3124
3461
|
mfDecl,
|
|
3125
3462
|
buildDecl,
|
|
@@ -3137,58 +3474,6 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3137
3474
|
].join("\n");
|
|
3138
3475
|
}
|
|
3139
3476
|
|
|
3140
|
-
// src/combinators/expect.ts
|
|
3141
|
-
function staticExpected(c) {
|
|
3142
|
-
const def = c._def;
|
|
3143
|
-
switch (def.tag) {
|
|
3144
|
-
case "literal":
|
|
3145
|
-
return [JSON.stringify(def.value)];
|
|
3146
|
-
case "regex":
|
|
3147
|
-
return [`/${def.source}/`];
|
|
3148
|
-
case "keywords":
|
|
3149
|
-
return def.words.map((w) => JSON.stringify(w));
|
|
3150
|
-
case "label":
|
|
3151
|
-
return [def.label];
|
|
3152
|
-
case "choice":
|
|
3153
|
-
return def.parsers.flatMap(staticExpected);
|
|
3154
|
-
case "sequence":
|
|
3155
|
-
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
3156
|
-
case "node":
|
|
3157
|
-
case "grammar":
|
|
3158
|
-
case "trivia":
|
|
3159
|
-
case "optional":
|
|
3160
|
-
case "many":
|
|
3161
|
-
case "oneOrMore":
|
|
3162
|
-
case "transform":
|
|
3163
|
-
case "not":
|
|
3164
|
-
return staticExpected(def.parser);
|
|
3165
|
-
case "lazy":
|
|
3166
|
-
return staticExpected(def.thunk());
|
|
3167
|
-
default:
|
|
3168
|
-
return [];
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
function expect(combinator, label2) {
|
|
3172
|
-
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
3173
|
-
const meta = {
|
|
3174
|
-
firstSet: combinator._meta.firstSet,
|
|
3175
|
-
canMatchNewline: combinator._meta.canMatchNewline,
|
|
3176
|
-
isTrivia: false
|
|
3177
|
-
};
|
|
3178
|
-
return {
|
|
3179
|
-
_tag: "expect",
|
|
3180
|
-
_meta: meta,
|
|
3181
|
-
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
3182
|
-
parse(input, pos, ctx) {
|
|
3183
|
-
const result = combinator.parse(input, pos, ctx);
|
|
3184
|
-
if (result.ok) return result;
|
|
3185
|
-
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
3186
|
-
ctx._errors?.push(error);
|
|
3187
|
-
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
3188
|
-
}
|
|
3189
|
-
};
|
|
3190
|
-
}
|
|
3191
|
-
|
|
3192
3477
|
// src/combinators/scanTo.ts
|
|
3193
3478
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
3194
3479
|
const meta = {
|
|
@@ -3570,6 +3855,13 @@ function evaluateWordFactory(node2, scope, code) {
|
|
|
3570
3855
|
function evaluateExpr(node2, scope, code, mapFnSources) {
|
|
3571
3856
|
return exprToCombi(node2, scope, code, mapFnSources);
|
|
3572
3857
|
}
|
|
3858
|
+
function evaluateCombinatorArray(node2, scope, code) {
|
|
3859
|
+
if (node2.type !== "ArrayExpression") return null;
|
|
3860
|
+
const val = anyValue(node2, scope, code, []);
|
|
3861
|
+
if (!Array.isArray(val) || val.length === 0) return null;
|
|
3862
|
+
if (!val.every(isCombinator)) return null;
|
|
3863
|
+
return val;
|
|
3864
|
+
}
|
|
3573
3865
|
function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
3574
3866
|
if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
|
|
3575
3867
|
const factory = factoryNode;
|
|
@@ -3873,6 +4165,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3873
4165
|
scope.set(varName, wordFactory);
|
|
3874
4166
|
continue;
|
|
3875
4167
|
}
|
|
4168
|
+
const combiArray = evaluateCombinatorArray(init, scope, code);
|
|
4169
|
+
if (combiArray) {
|
|
4170
|
+
;
|
|
4171
|
+
scope.set(varName, combiArray);
|
|
4172
|
+
continue;
|
|
4173
|
+
}
|
|
3876
4174
|
warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
|
|
3877
4175
|
continue;
|
|
3878
4176
|
}
|