parseman 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +6 -6
- package/dist/combinators/recover-list.d.ts +41 -0
- package/dist/combinators/recover-list.d.ts.map +1 -0
- package/dist/combinators/recover.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +1 -0
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/scannable-run.d.ts +139 -9
- package/dist/compiler/scannable-run.d.ts.map +1 -1
- package/dist/compiler/scannable-terminal.d.ts +23 -0
- package/dist/compiler/scannable-terminal.d.ts.map +1 -0
- package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/cst/types.d.ts +1 -1
- package/dist/cst/walk.d.ts +72 -0
- package/dist/cst/walk.d.ts.map +1 -0
- package/dist/functional/doc.d.ts +6 -6
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/index.cjs +1201 -187
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1201 -187
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts +36 -2
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +1403 -232
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1403 -232
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -3
- package/dist/combinators/lazy.d.ts +0 -14
- package/dist/combinators/lazy.d.ts.map +0 -1
package/dist/plugin/index.cjs
CHANGED
|
@@ -34,8 +34,10 @@ __export(plugin_exports, {
|
|
|
34
34
|
transformMacro: () => transformMacro
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(plugin_exports);
|
|
37
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
37
38
|
var import_unplugin = require("unplugin");
|
|
38
39
|
var import_oxc_parser = require("oxc-parser");
|
|
40
|
+
var import_oxc_resolver = require("oxc-resolver");
|
|
39
41
|
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
40
42
|
|
|
41
43
|
// src/combinators/first-set.ts
|
|
@@ -363,15 +365,15 @@ function extractFirstSet(node2) {
|
|
|
363
365
|
case "Alternative": {
|
|
364
366
|
const exprs = node2.expressions;
|
|
365
367
|
if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
|
|
366
|
-
let
|
|
368
|
+
let fs2 = empty();
|
|
367
369
|
let canNL = false;
|
|
368
370
|
for (const expr of exprs) {
|
|
369
371
|
const r = extractFirstSet(expr);
|
|
370
|
-
|
|
372
|
+
fs2 = union(fs2, r.firstSet);
|
|
371
373
|
canNL = canNL || r.canMatchNewline;
|
|
372
374
|
if (!canBeEmpty(expr)) break;
|
|
373
375
|
}
|
|
374
|
-
return { firstSet:
|
|
376
|
+
return { firstSet: fs2, canMatchNewline: canNL };
|
|
375
377
|
}
|
|
376
378
|
case "Char": {
|
|
377
379
|
const kind = node2.kind;
|
|
@@ -398,22 +400,22 @@ function extractFirstSet(node2) {
|
|
|
398
400
|
}
|
|
399
401
|
case "CharacterClass": {
|
|
400
402
|
const expressions = node2.expressions;
|
|
401
|
-
let
|
|
403
|
+
let fs2 = empty();
|
|
402
404
|
let canNL = false;
|
|
403
405
|
for (const expr of expressions) {
|
|
404
406
|
if (expr.type === "ClassRange") {
|
|
405
407
|
const from = expr.from.codePoint;
|
|
406
408
|
const to = expr.to.codePoint;
|
|
407
|
-
|
|
409
|
+
fs2 = union(fs2, fromRange(from, to));
|
|
408
410
|
if (from <= 10 && 10 <= to) canNL = true;
|
|
409
411
|
} else if (expr.type === "Char") {
|
|
410
412
|
const r = extractFirstSet(expr);
|
|
411
|
-
|
|
413
|
+
fs2 = union(fs2, r.firstSet);
|
|
412
414
|
canNL = canNL || r.canMatchNewline;
|
|
413
415
|
}
|
|
414
416
|
}
|
|
415
417
|
if (node2.negative) return { firstSet: any(), canMatchNewline: true };
|
|
416
|
-
return { firstSet:
|
|
418
|
+
return { firstSet: fs2, canMatchNewline: canNL };
|
|
417
419
|
}
|
|
418
420
|
case "Repetition": {
|
|
419
421
|
return extractFirstSet(node2.expression);
|
|
@@ -722,10 +724,10 @@ function codesToFirstSet(codes) {
|
|
|
722
724
|
ranges.push({ lo, hi });
|
|
723
725
|
return { kind: "ranges", ranges };
|
|
724
726
|
}
|
|
725
|
-
function inFirstSet(code,
|
|
726
|
-
if (
|
|
727
|
-
if (
|
|
728
|
-
for (const r of
|
|
727
|
+
function inFirstSet(code, fs2) {
|
|
728
|
+
if (fs2.kind === "any") return true;
|
|
729
|
+
if (fs2.kind === "empty") return false;
|
|
730
|
+
for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
729
731
|
return false;
|
|
730
732
|
}
|
|
731
733
|
function areDisjoint(sets) {
|
|
@@ -813,12 +815,15 @@ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
|
|
|
813
815
|
continue;
|
|
814
816
|
}
|
|
815
817
|
if (c === 47 && input.charCodeAt(pos + 1) === 42) {
|
|
816
|
-
const start = pos;
|
|
817
818
|
let j = pos + 2;
|
|
818
819
|
while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
820
|
+
if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
|
|
821
|
+
const start = pos;
|
|
822
|
+
pos = j + 2;
|
|
823
|
+
chunks.push({ start, end: pos, kindIndex: commentKind });
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
break;
|
|
822
827
|
}
|
|
823
828
|
break;
|
|
824
829
|
}
|
|
@@ -1315,38 +1320,140 @@ function noTrivia(root) {
|
|
|
1315
1320
|
return parser({ trivia: null }, root);
|
|
1316
1321
|
}
|
|
1317
1322
|
|
|
1323
|
+
// src/combinators/expect.ts
|
|
1324
|
+
function staticExpected(c) {
|
|
1325
|
+
const def = c._def;
|
|
1326
|
+
switch (def.tag) {
|
|
1327
|
+
case "literal":
|
|
1328
|
+
return [JSON.stringify(def.value)];
|
|
1329
|
+
case "regex":
|
|
1330
|
+
return [`/${def.source}/`];
|
|
1331
|
+
case "keywords":
|
|
1332
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
1333
|
+
case "label":
|
|
1334
|
+
return [def.label];
|
|
1335
|
+
case "choice":
|
|
1336
|
+
return def.parsers.flatMap(staticExpected);
|
|
1337
|
+
case "sequence":
|
|
1338
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
1339
|
+
case "node":
|
|
1340
|
+
case "grammar":
|
|
1341
|
+
case "trivia":
|
|
1342
|
+
case "optional":
|
|
1343
|
+
case "many":
|
|
1344
|
+
case "oneOrMore":
|
|
1345
|
+
case "transform":
|
|
1346
|
+
case "not":
|
|
1347
|
+
return staticExpected(def.parser);
|
|
1348
|
+
case "lazy":
|
|
1349
|
+
return staticExpected(def.thunk());
|
|
1350
|
+
default:
|
|
1351
|
+
return [];
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
function expect(combinator, label2) {
|
|
1355
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
1356
|
+
const meta = {
|
|
1357
|
+
firstSet: combinator._meta.firstSet,
|
|
1358
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1359
|
+
isTrivia: false
|
|
1360
|
+
};
|
|
1361
|
+
return {
|
|
1362
|
+
_tag: "expect",
|
|
1363
|
+
_meta: meta,
|
|
1364
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
1365
|
+
parse(input, pos, ctx) {
|
|
1366
|
+
const result = combinator.parse(input, pos, ctx);
|
|
1367
|
+
if (result.ok) return result;
|
|
1368
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
1369
|
+
ctx._errors?.push(error);
|
|
1370
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1318
1375
|
// src/compiler/scannable-run.ts
|
|
1376
|
+
var BACKSLASH = 92;
|
|
1319
1377
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1320
1378
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1379
|
+
var SPACE_RANGES = [
|
|
1380
|
+
[9, 13],
|
|
1381
|
+
[32, 32],
|
|
1382
|
+
[160, 160],
|
|
1383
|
+
[5760, 5760],
|
|
1384
|
+
[8192, 8202],
|
|
1385
|
+
[8232, 8232],
|
|
1386
|
+
[8233, 8233],
|
|
1387
|
+
[8239, 8239],
|
|
1388
|
+
[8287, 8287],
|
|
1389
|
+
[12288, 12288],
|
|
1390
|
+
[65279, 65279]
|
|
1391
|
+
];
|
|
1392
|
+
function shorthandRanges(ch) {
|
|
1393
|
+
if (ch === "d") return [[48, 57]];
|
|
1394
|
+
if (ch === "s") return SPACE_RANGES;
|
|
1395
|
+
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1396
|
+
}
|
|
1397
|
+
function readUnicodeEscape(body, i) {
|
|
1398
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
1399
|
+
const hex = body.slice(i + 2, i + 6);
|
|
1400
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
1401
|
+
return { cp: Number.parseInt(hex, 16), next: i + 6 };
|
|
1402
|
+
}
|
|
1321
1403
|
function parseClassRanges(body) {
|
|
1322
1404
|
const ranges = [];
|
|
1323
1405
|
let i = 0;
|
|
1324
|
-
const
|
|
1406
|
+
const readAtom = () => {
|
|
1325
1407
|
const ch = body[i];
|
|
1326
1408
|
if (ch === void 0) return null;
|
|
1327
1409
|
if (ch === "\\") {
|
|
1410
|
+
const uni = readUnicodeEscape(body, i);
|
|
1411
|
+
if (uni) {
|
|
1412
|
+
i = uni.next;
|
|
1413
|
+
return { cp: uni.cp };
|
|
1414
|
+
}
|
|
1328
1415
|
const e = body[i + 1];
|
|
1329
1416
|
if (e === void 0) return null;
|
|
1330
1417
|
i += 2;
|
|
1331
|
-
|
|
1418
|
+
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1419
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
1420
|
+
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1421
|
+
return { cp: e.codePointAt(0) };
|
|
1332
1422
|
}
|
|
1333
1423
|
i += ch.length;
|
|
1334
|
-
return ch.codePointAt(0);
|
|
1424
|
+
return { cp: ch.codePointAt(0) };
|
|
1335
1425
|
};
|
|
1336
1426
|
while (i < body.length) {
|
|
1337
|
-
const lo =
|
|
1427
|
+
const lo = readAtom();
|
|
1338
1428
|
if (lo === null) return null;
|
|
1429
|
+
if ("set" in lo) {
|
|
1430
|
+
ranges.push(...lo.set);
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1339
1433
|
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1340
1434
|
i += 1;
|
|
1341
|
-
const hi =
|
|
1342
|
-
if (hi === null) return null;
|
|
1343
|
-
ranges.push([lo, hi]);
|
|
1435
|
+
const hi = readAtom();
|
|
1436
|
+
if (hi === null || "set" in hi) return null;
|
|
1437
|
+
ranges.push([lo.cp, hi.cp]);
|
|
1344
1438
|
} else {
|
|
1345
|
-
ranges.push([lo, lo]);
|
|
1439
|
+
ranges.push([lo.cp, lo.cp]);
|
|
1346
1440
|
}
|
|
1347
1441
|
}
|
|
1348
1442
|
return ranges.length ? ranges : null;
|
|
1349
1443
|
}
|
|
1444
|
+
function classToRanges(cls) {
|
|
1445
|
+
if (cls === "\\d") return shorthandRanges("d");
|
|
1446
|
+
if (cls === "\\w") return shorthandRanges("w");
|
|
1447
|
+
if (cls === "\\s") return shorthandRanges("s");
|
|
1448
|
+
const body = cls.slice(1, -1);
|
|
1449
|
+
if (body.startsWith("^")) return null;
|
|
1450
|
+
return parseClassRanges(body);
|
|
1451
|
+
}
|
|
1452
|
+
var foldEq = (cVar, cp) => {
|
|
1453
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
|
|
1454
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
|
|
1455
|
+
return `${cVar} === ${cp}`;
|
|
1456
|
+
};
|
|
1350
1457
|
function literalCodePoints(frag) {
|
|
1351
1458
|
const out = [];
|
|
1352
1459
|
let i = 0;
|
|
@@ -1365,19 +1472,482 @@ function literalCodePoints(frag) {
|
|
|
1365
1472
|
}
|
|
1366
1473
|
return out.length ? out : null;
|
|
1367
1474
|
}
|
|
1475
|
+
function parseStringShape(source) {
|
|
1476
|
+
const q = source[0];
|
|
1477
|
+
if (q === void 0 || q === "\\" || META.has(q)) return null;
|
|
1478
|
+
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
1479
|
+
const inner = source.slice(1, -1);
|
|
1480
|
+
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
1481
|
+
const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
1482
|
+
let body;
|
|
1483
|
+
let esc;
|
|
1484
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
|
|
1485
|
+
if (m) {
|
|
1486
|
+
body = m[1];
|
|
1487
|
+
esc = m[2];
|
|
1488
|
+
} else {
|
|
1489
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
|
|
1490
|
+
if (m) {
|
|
1491
|
+
esc = m[1];
|
|
1492
|
+
body = m[2];
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (body === void 0 || esc === void 0) return null;
|
|
1496
|
+
const excluded = parseClassRanges(body);
|
|
1497
|
+
if (!excluded) return null;
|
|
1498
|
+
const qcp = q.codePointAt(0);
|
|
1499
|
+
const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
|
|
1500
|
+
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1501
|
+
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1502
|
+
}
|
|
1503
|
+
function parseSeqParts(source) {
|
|
1504
|
+
const parts = [];
|
|
1505
|
+
let lit = [];
|
|
1506
|
+
const flush = () => {
|
|
1507
|
+
if (lit.length) {
|
|
1508
|
+
parts.push({ part: "lit", cps: lit, optional: false });
|
|
1509
|
+
lit = [];
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
const runFrom = (ranges, negated, q) => {
|
|
1513
|
+
if (q === "+") {
|
|
1514
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
|
|
1515
|
+
return 1;
|
|
1516
|
+
}
|
|
1517
|
+
if (q === "*") {
|
|
1518
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
|
|
1519
|
+
return 1;
|
|
1520
|
+
}
|
|
1521
|
+
if (q === "?") {
|
|
1522
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
|
|
1523
|
+
return 1;
|
|
1524
|
+
}
|
|
1525
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
|
|
1526
|
+
return 0;
|
|
1527
|
+
};
|
|
1528
|
+
let i = 0;
|
|
1529
|
+
while (i < source.length) {
|
|
1530
|
+
const ch = source[i];
|
|
1531
|
+
if (ch === "[") {
|
|
1532
|
+
let k = i + 1;
|
|
1533
|
+
const neg = source[k] === "^";
|
|
1534
|
+
if (neg) k++;
|
|
1535
|
+
let body = "";
|
|
1536
|
+
while (k < source.length && source[k] !== "]") {
|
|
1537
|
+
if (source[k] === "\\") {
|
|
1538
|
+
body += source[k] + (source[k + 1] ?? "");
|
|
1539
|
+
k += 2;
|
|
1540
|
+
} else {
|
|
1541
|
+
body += source[k];
|
|
1542
|
+
k++;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
if (source[k] !== "]") return null;
|
|
1546
|
+
const ranges = parseClassRanges(body);
|
|
1547
|
+
if (!ranges) return null;
|
|
1548
|
+
flush();
|
|
1549
|
+
i = k + 1;
|
|
1550
|
+
i += runFrom(ranges, neg, source[i]);
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
1553
|
+
if (ch === "\\") {
|
|
1554
|
+
const e = source[i + 1];
|
|
1555
|
+
if (e === "d" || e === "w" || e === "s") {
|
|
1556
|
+
flush();
|
|
1557
|
+
i += 2;
|
|
1558
|
+
i += runFrom(shorthandRanges(e), false, source[i]);
|
|
1559
|
+
continue;
|
|
1560
|
+
}
|
|
1561
|
+
const uni = readUnicodeEscape(source, i);
|
|
1562
|
+
let cp2;
|
|
1563
|
+
if (uni) {
|
|
1564
|
+
cp2 = uni.cp;
|
|
1565
|
+
i = uni.next;
|
|
1566
|
+
} else if (e === void 0) return null;
|
|
1567
|
+
else if (e in CLASS_ESCAPES) {
|
|
1568
|
+
cp2 = CLASS_ESCAPES[e];
|
|
1569
|
+
i += 2;
|
|
1570
|
+
} else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1571
|
+
else {
|
|
1572
|
+
cp2 = e.codePointAt(0);
|
|
1573
|
+
i += 2;
|
|
1574
|
+
}
|
|
1575
|
+
if (source[i] === "?") {
|
|
1576
|
+
flush();
|
|
1577
|
+
parts.push({ part: "lit", cps: [cp2], optional: true });
|
|
1578
|
+
i++;
|
|
1579
|
+
} else lit.push(cp2);
|
|
1580
|
+
continue;
|
|
1581
|
+
}
|
|
1582
|
+
if (ch === "(") {
|
|
1583
|
+
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
1584
|
+
let k = i + 3;
|
|
1585
|
+
let depth = 1;
|
|
1586
|
+
while (k < source.length && depth > 0) {
|
|
1587
|
+
const c2 = source[k];
|
|
1588
|
+
if (c2 === "\\") {
|
|
1589
|
+
k += 2;
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
if (c2 === "[") {
|
|
1593
|
+
let j = k + 1;
|
|
1594
|
+
if (source[j] === "^") j++;
|
|
1595
|
+
while (j < source.length && source[j] !== "]") {
|
|
1596
|
+
if (source[j] === "\\") j += 2;
|
|
1597
|
+
else j++;
|
|
1598
|
+
}
|
|
1599
|
+
if (source[j] !== "]") return null;
|
|
1600
|
+
k = j + 1;
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
if (c2 === "(") depth++;
|
|
1604
|
+
else if (c2 === ")") depth--;
|
|
1605
|
+
k++;
|
|
1606
|
+
}
|
|
1607
|
+
if (depth !== 0) return null;
|
|
1608
|
+
const body = source.slice(i + 3, k - 1);
|
|
1609
|
+
const inner = parseScanShape(body);
|
|
1610
|
+
if (!inner || !groupInnerSafe(inner)) return null;
|
|
1611
|
+
const q = source[k];
|
|
1612
|
+
if (q === "{") return null;
|
|
1613
|
+
flush();
|
|
1614
|
+
if (q === "+") {
|
|
1615
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1616
|
+
parts.push({ part: "group", inner, min: 1, unbounded: true });
|
|
1617
|
+
i = k + 1;
|
|
1618
|
+
} else if (q === "*") {
|
|
1619
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1620
|
+
parts.push({ part: "group", inner, min: 0, unbounded: true });
|
|
1621
|
+
i = k + 1;
|
|
1622
|
+
} else if (q === "?") {
|
|
1623
|
+
parts.push({ part: "group", inner, min: 0, unbounded: false });
|
|
1624
|
+
i = k + 1;
|
|
1625
|
+
} else {
|
|
1626
|
+
parts.push({ part: "group", inner, min: 1, unbounded: false });
|
|
1627
|
+
i = k;
|
|
1628
|
+
}
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
if (META.has(ch)) return null;
|
|
1632
|
+
const cp = ch.codePointAt(0);
|
|
1633
|
+
i += ch.length;
|
|
1634
|
+
if (source[i] === "?") {
|
|
1635
|
+
flush();
|
|
1636
|
+
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
1637
|
+
i++;
|
|
1638
|
+
} else lit.push(cp);
|
|
1639
|
+
}
|
|
1640
|
+
flush();
|
|
1641
|
+
return parts.length ? parts : null;
|
|
1642
|
+
}
|
|
1643
|
+
function seqIsUnambiguous(parts) {
|
|
1644
|
+
let hasMandatory = false;
|
|
1645
|
+
for (let k = 0; k < parts.length; k++) {
|
|
1646
|
+
const p = parts[k];
|
|
1647
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1648
|
+
if (mandatory) hasMandatory = true;
|
|
1649
|
+
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1650
|
+
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1651
|
+
if (skippableSingle || isUnboundedRepeat) {
|
|
1652
|
+
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
1653
|
+
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
return hasMandatory;
|
|
1657
|
+
}
|
|
1658
|
+
function rangesOverlap(a, b) {
|
|
1659
|
+
return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
|
|
1660
|
+
}
|
|
1661
|
+
function rangesSubset(sub, sup) {
|
|
1662
|
+
return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
|
|
1663
|
+
}
|
|
1664
|
+
function parseClassOperand(body) {
|
|
1665
|
+
if (body === "\\d" || body === "\\w" || body === "\\s") {
|
|
1666
|
+
return { ranges: shorthandRanges(body[1]), negated: false };
|
|
1667
|
+
}
|
|
1668
|
+
if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
|
|
1669
|
+
let inner = body.slice(1, -1);
|
|
1670
|
+
const negated = inner.startsWith("^");
|
|
1671
|
+
if (negated) inner = inner.slice(1);
|
|
1672
|
+
const ranges = parseClassRanges(inner);
|
|
1673
|
+
return ranges ? { ranges, negated } : null;
|
|
1674
|
+
}
|
|
1675
|
+
const cps = literalCodePoints(body);
|
|
1676
|
+
if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
|
|
1677
|
+
return null;
|
|
1678
|
+
}
|
|
1679
|
+
function classSubset(t, u) {
|
|
1680
|
+
if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1681
|
+
if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1682
|
+
if (t.negated && !u.negated) return false;
|
|
1683
|
+
return rangesSubset(u.ranges, t.ranges);
|
|
1684
|
+
}
|
|
1685
|
+
function classDisjoint(t, u) {
|
|
1686
|
+
if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1687
|
+
if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1688
|
+
if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
|
|
1689
|
+
return false;
|
|
1690
|
+
}
|
|
1691
|
+
function firstSetsDisjoint(a, b) {
|
|
1692
|
+
if (a === "any" || b === "any") return false;
|
|
1693
|
+
return classDisjoint(a, b);
|
|
1694
|
+
}
|
|
1695
|
+
function trailingBacktrackClass(shape) {
|
|
1696
|
+
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1697
|
+
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
1698
|
+
if (shape.kind === "litFold") return null;
|
|
1699
|
+
if (shape.kind === "seq") {
|
|
1700
|
+
const last = shape.parts[shape.parts.length - 1];
|
|
1701
|
+
if (last.part === "lit") return null;
|
|
1702
|
+
if (last.part === "run") {
|
|
1703
|
+
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
const innerWiggle = trailingBacktrackClass(last.inner);
|
|
1707
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1708
|
+
const requiredOnce = !last.unbounded && last.min === 1;
|
|
1709
|
+
if (requiredOnce) return innerWiggle;
|
|
1710
|
+
const dropExposed = shapeFirstAccept(last.inner);
|
|
1711
|
+
if (dropExposed === "any") return "unsupported";
|
|
1712
|
+
if (innerWiggle === null) return dropExposed;
|
|
1713
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1714
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1715
|
+
}
|
|
1716
|
+
if (shape.kind === "alt") {
|
|
1717
|
+
if (!shape.disjoint) return "unsupported";
|
|
1718
|
+
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1719
|
+
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1720
|
+
const concrete = classes.filter((c) => c !== null);
|
|
1721
|
+
if (concrete.length === 0) return null;
|
|
1722
|
+
const unioned = unionCharSets(concrete);
|
|
1723
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1724
|
+
}
|
|
1725
|
+
return "unsupported";
|
|
1726
|
+
}
|
|
1727
|
+
function lookaheadUnambiguous(inner, operand, negative) {
|
|
1728
|
+
const tail = trailingBacktrackClass(inner);
|
|
1729
|
+
if (tail === "unsupported") return false;
|
|
1730
|
+
if (tail === null) return true;
|
|
1731
|
+
return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
|
|
1732
|
+
}
|
|
1733
|
+
function stripTrailingLookahead(source) {
|
|
1734
|
+
if (!source.endsWith(")")) return null;
|
|
1735
|
+
for (const negative of [true, false]) {
|
|
1736
|
+
const lead = negative ? "(?!" : "(?=";
|
|
1737
|
+
const idx = source.lastIndexOf(lead);
|
|
1738
|
+
if (idx === -1) continue;
|
|
1739
|
+
const body = source.slice(idx + lead.length, -1);
|
|
1740
|
+
const operand = body.length ? parseClassOperand(body) : null;
|
|
1741
|
+
if (!operand) continue;
|
|
1742
|
+
return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
|
|
1743
|
+
}
|
|
1744
|
+
return null;
|
|
1745
|
+
}
|
|
1746
|
+
function unwrapOuterGroup(source) {
|
|
1747
|
+
while (source.startsWith("(?:")) {
|
|
1748
|
+
let depth = 1;
|
|
1749
|
+
let i = 3;
|
|
1750
|
+
let ok = true;
|
|
1751
|
+
while (i < source.length && depth > 0) {
|
|
1752
|
+
const ch = source[i];
|
|
1753
|
+
if (ch === "\\") {
|
|
1754
|
+
i += 2;
|
|
1755
|
+
continue;
|
|
1756
|
+
}
|
|
1757
|
+
if (ch === "[") {
|
|
1758
|
+
let j = i + 1;
|
|
1759
|
+
if (source[j] === "^") j++;
|
|
1760
|
+
while (j < source.length && source[j] !== "]") {
|
|
1761
|
+
if (source[j] === "\\") j += 2;
|
|
1762
|
+
else j++;
|
|
1763
|
+
}
|
|
1764
|
+
if (source[j] !== "]") {
|
|
1765
|
+
ok = false;
|
|
1766
|
+
break;
|
|
1767
|
+
}
|
|
1768
|
+
i = j + 1;
|
|
1769
|
+
continue;
|
|
1770
|
+
}
|
|
1771
|
+
if (ch === "(") depth++;
|
|
1772
|
+
else if (ch === ")") depth--;
|
|
1773
|
+
i++;
|
|
1774
|
+
}
|
|
1775
|
+
if (!ok || depth !== 0 || i !== source.length) break;
|
|
1776
|
+
source = source.slice(3, -1);
|
|
1777
|
+
}
|
|
1778
|
+
return source;
|
|
1779
|
+
}
|
|
1780
|
+
function splitTopLevelAlternation(source) {
|
|
1781
|
+
const body = unwrapOuterGroup(source);
|
|
1782
|
+
const parts = [];
|
|
1783
|
+
let depth = 0;
|
|
1784
|
+
let last = 0;
|
|
1785
|
+
let i = 0;
|
|
1786
|
+
while (i < body.length) {
|
|
1787
|
+
const ch = body[i];
|
|
1788
|
+
if (ch === "\\") {
|
|
1789
|
+
i += 2;
|
|
1790
|
+
continue;
|
|
1791
|
+
}
|
|
1792
|
+
if (ch === "[") {
|
|
1793
|
+
let j = i + 1;
|
|
1794
|
+
if (body[j] === "^") j++;
|
|
1795
|
+
while (j < body.length && body[j] !== "]") {
|
|
1796
|
+
if (body[j] === "\\") j += 2;
|
|
1797
|
+
else j++;
|
|
1798
|
+
}
|
|
1799
|
+
if (body[j] !== "]") return null;
|
|
1800
|
+
i = j + 1;
|
|
1801
|
+
continue;
|
|
1802
|
+
}
|
|
1803
|
+
if (ch === "(") {
|
|
1804
|
+
depth++;
|
|
1805
|
+
i++;
|
|
1806
|
+
continue;
|
|
1807
|
+
}
|
|
1808
|
+
if (ch === ")") {
|
|
1809
|
+
depth--;
|
|
1810
|
+
if (depth < 0) return null;
|
|
1811
|
+
i++;
|
|
1812
|
+
continue;
|
|
1813
|
+
}
|
|
1814
|
+
if (ch === "|" && depth === 0) {
|
|
1815
|
+
parts.push(body.slice(last, i));
|
|
1816
|
+
last = i + 1;
|
|
1817
|
+
i++;
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
i++;
|
|
1821
|
+
}
|
|
1822
|
+
if (depth !== 0) return null;
|
|
1823
|
+
parts.push(body.slice(last));
|
|
1824
|
+
if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
|
|
1825
|
+
return parts;
|
|
1826
|
+
}
|
|
1827
|
+
function shapeCanBeEmpty(shape) {
|
|
1828
|
+
if (shape.kind === "chars") return !shape.minOne;
|
|
1829
|
+
if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
|
|
1830
|
+
if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
|
|
1831
|
+
return false;
|
|
1832
|
+
}
|
|
1833
|
+
function unionCharSets(sets) {
|
|
1834
|
+
if (sets.some((s) => s === "any")) return "any";
|
|
1835
|
+
const concrete = sets;
|
|
1836
|
+
if (concrete.length === 1) return concrete[0];
|
|
1837
|
+
if (concrete.some((s) => s.negated)) return "any";
|
|
1838
|
+
return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
|
|
1839
|
+
}
|
|
1840
|
+
function partFirstAccept(part) {
|
|
1841
|
+
if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
|
|
1842
|
+
if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
|
|
1843
|
+
return shapeFirstAccept(part.inner);
|
|
1844
|
+
}
|
|
1845
|
+
function seqFirstAccept(parts) {
|
|
1846
|
+
const sets = [];
|
|
1847
|
+
for (const p of parts) {
|
|
1848
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1849
|
+
sets.push(partFirstAccept(p));
|
|
1850
|
+
if (mandatory) break;
|
|
1851
|
+
}
|
|
1852
|
+
return unionCharSets(sets);
|
|
1853
|
+
}
|
|
1854
|
+
function groupInnerSafe(shape) {
|
|
1855
|
+
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1856
|
+
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1857
|
+
if (shape.kind === "alt") return shape.disjoint;
|
|
1858
|
+
return false;
|
|
1859
|
+
}
|
|
1860
|
+
function shapeFirstAccept(shape) {
|
|
1861
|
+
if (shapeCanBeEmpty(shape)) return "any";
|
|
1862
|
+
switch (shape.kind) {
|
|
1863
|
+
case "chars":
|
|
1864
|
+
return { ranges: shape.ranges, negated: false };
|
|
1865
|
+
case "ident":
|
|
1866
|
+
return { ranges: shape.head, negated: false };
|
|
1867
|
+
case "until":
|
|
1868
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1869
|
+
case "delimited":
|
|
1870
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1871
|
+
case "string":
|
|
1872
|
+
return { ranges: [[shape.quote, shape.quote]], negated: false };
|
|
1873
|
+
case "litFold": {
|
|
1874
|
+
const cp = shape.open[0];
|
|
1875
|
+
if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
|
|
1876
|
+
if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
|
|
1877
|
+
return { ranges: [[cp, cp]], negated: false };
|
|
1878
|
+
}
|
|
1879
|
+
case "seq":
|
|
1880
|
+
return seqFirstAccept(shape.parts);
|
|
1881
|
+
case "lookahead":
|
|
1882
|
+
return shapeFirstAccept(shape.inner);
|
|
1883
|
+
// zero-width — doesn't change first-char acceptance
|
|
1884
|
+
case "alt": {
|
|
1885
|
+
if (!shape.disjoint) return "any";
|
|
1886
|
+
const sets = shape.firsts.filter((f) => f !== null);
|
|
1887
|
+
return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
function allPairsDisjoint(sets) {
|
|
1892
|
+
for (let i = 0; i < sets.length; i++) {
|
|
1893
|
+
const a = sets[i];
|
|
1894
|
+
if (a === "any") return false;
|
|
1895
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
1896
|
+
const b = sets[j];
|
|
1897
|
+
if (b === "any" || !classDisjoint(a, b)) return false;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
return true;
|
|
1901
|
+
}
|
|
1902
|
+
function parseAlternation(source) {
|
|
1903
|
+
const parts = splitTopLevelAlternation(source);
|
|
1904
|
+
if (!parts) return null;
|
|
1905
|
+
const arms = [];
|
|
1906
|
+
for (const p of parts) {
|
|
1907
|
+
const arm = parseScanShape(p);
|
|
1908
|
+
if (!arm) return null;
|
|
1909
|
+
arms.push(arm);
|
|
1910
|
+
}
|
|
1911
|
+
const firstSets = arms.map(shapeFirstAccept);
|
|
1912
|
+
const disjoint = allPairsDisjoint(firstSets);
|
|
1913
|
+
return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
|
|
1914
|
+
}
|
|
1368
1915
|
function parseScanShape(source) {
|
|
1916
|
+
const alt = parseAlternation(source);
|
|
1917
|
+
if (alt) return alt;
|
|
1918
|
+
const la = stripTrailingLookahead(source);
|
|
1919
|
+
if (la) {
|
|
1920
|
+
const inner = parseScanShapeCore(la.base);
|
|
1921
|
+
if (!inner) return null;
|
|
1922
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
1923
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
1924
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
1925
|
+
}
|
|
1926
|
+
return parseScanShapeCore(source);
|
|
1927
|
+
}
|
|
1928
|
+
function parseScanShapeCore(source) {
|
|
1369
1929
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1370
|
-
if (m) {
|
|
1371
|
-
if (m[1].startsWith("^")) return null;
|
|
1930
|
+
if (m && !m[1].startsWith("^")) {
|
|
1372
1931
|
const ranges = parseClassRanges(m[1]);
|
|
1373
|
-
return ranges ? { kind: "chars", ranges } : null;
|
|
1932
|
+
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1933
|
+
}
|
|
1934
|
+
m = /^\\([dws])([+*])$/.exec(source);
|
|
1935
|
+
if (m) {
|
|
1936
|
+
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1374
1937
|
}
|
|
1938
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
|
|
1939
|
+
if (m) {
|
|
1940
|
+
const head = classToRanges(m[1]);
|
|
1941
|
+
const tail = classToRanges(m[2]);
|
|
1942
|
+
if (head && tail) return { kind: "ident", head, tail };
|
|
1943
|
+
}
|
|
1944
|
+
const str = parseStringShape(source);
|
|
1945
|
+
if (str) return str;
|
|
1375
1946
|
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1376
1947
|
if (m) {
|
|
1377
1948
|
const open = literalCodePoints(m[1]);
|
|
1378
1949
|
const stop = parseClassRanges(m[2]);
|
|
1379
1950
|
if (open && stop) return { kind: "until", open, stop };
|
|
1380
|
-
return null;
|
|
1381
1951
|
}
|
|
1382
1952
|
if (!source.includes("\\\\")) {
|
|
1383
1953
|
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
@@ -1387,68 +1957,278 @@ function parseScanShape(source) {
|
|
|
1387
1957
|
if (open && close) return { kind: "delimited", open, close };
|
|
1388
1958
|
}
|
|
1389
1959
|
}
|
|
1960
|
+
const parts = parseSeqParts(source);
|
|
1961
|
+
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1390
1962
|
return null;
|
|
1391
1963
|
}
|
|
1964
|
+
function scanShapeFromRegex(source, flags) {
|
|
1965
|
+
if (/[msu]/.test(flags)) return null;
|
|
1966
|
+
if (/i/.test(flags)) {
|
|
1967
|
+
const open = literalCodePoints(source);
|
|
1968
|
+
return open ? { kind: "litFold", open } : null;
|
|
1969
|
+
}
|
|
1970
|
+
return parseScanShape(source);
|
|
1971
|
+
}
|
|
1392
1972
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1393
1973
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
1394
1974
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1395
1975
|
).join(" && ");
|
|
1396
|
-
|
|
1976
|
+
var LINE_TERMINATORS = [10, 13, 8232, 8233];
|
|
1977
|
+
function stringHardStop(shape) {
|
|
1978
|
+
return shape.excluded.filter(
|
|
1979
|
+
([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
|
|
1980
|
+
);
|
|
1981
|
+
}
|
|
1982
|
+
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1983
|
+
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
1984
|
+
if (shape.kind === "lookahead") {
|
|
1985
|
+
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
1986
|
+
const okV = mint("_ok");
|
|
1987
|
+
const endV2 = mint("_end");
|
|
1988
|
+
const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
|
|
1989
|
+
const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
|
|
1990
|
+
const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
|
|
1991
|
+
const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
|
|
1992
|
+
return {
|
|
1993
|
+
setup: [
|
|
1994
|
+
...inner.setup,
|
|
1995
|
+
`${ind2}let ${okV} = ${inner.ok}`,
|
|
1996
|
+
`${ind2}let ${endV2} = ${inner.end}`,
|
|
1997
|
+
`${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
|
|
1998
|
+
],
|
|
1999
|
+
ok: okV,
|
|
2000
|
+
end: endV2
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
if (shape.kind === "alt") {
|
|
2004
|
+
const okV = mint("_ok");
|
|
2005
|
+
const endV2 = mint("_end");
|
|
2006
|
+
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2007
|
+
if (shape.disjoint) {
|
|
2008
|
+
const c0 = codeAt(start, firstChar);
|
|
2009
|
+
shape.arms.forEach((arm, k) => {
|
|
2010
|
+
const fs2 = shape.firsts[k];
|
|
2011
|
+
const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
|
|
2012
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2013
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2014
|
+
lines.push(...m.setup);
|
|
2015
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2016
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2017
|
+
lines.push(`${ind2}}`);
|
|
2018
|
+
});
|
|
2019
|
+
} else {
|
|
2020
|
+
const lbl = mint("_alt");
|
|
2021
|
+
lines.push(`${ind2}${lbl}: {`);
|
|
2022
|
+
for (const arm of shape.arms) {
|
|
2023
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2024
|
+
lines.push(...m.setup);
|
|
2025
|
+
lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
2026
|
+
}
|
|
2027
|
+
lines.push(`${ind2}}`);
|
|
2028
|
+
}
|
|
2029
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2030
|
+
}
|
|
1397
2031
|
if (shape.kind === "chars") {
|
|
2032
|
+
const cur = mint("_e");
|
|
1398
2033
|
return {
|
|
1399
2034
|
setup: [
|
|
1400
|
-
|
|
1401
|
-
|
|
2035
|
+
`${ind2}let ${cur} = ${start}`,
|
|
2036
|
+
`${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
|
|
1402
2037
|
],
|
|
1403
|
-
|
|
2038
|
+
// `*` always matches (possibly empty); `+` needs at least one char.
|
|
2039
|
+
ok: shape.minOne ? `${cur} > ${start}` : "true",
|
|
2040
|
+
end: cur
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
if (shape.kind === "ident") {
|
|
2044
|
+
const cur = mint("_e");
|
|
2045
|
+
return {
|
|
2046
|
+
setup: [
|
|
2047
|
+
`${ind2}let ${cur} = ${start}`,
|
|
2048
|
+
`${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
|
|
2049
|
+
`${ind2} ${cur} = ${start} + 1`,
|
|
2050
|
+
`${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
|
|
2051
|
+
`${ind2}}`
|
|
2052
|
+
],
|
|
2053
|
+
ok: `${cur} > ${start}`,
|
|
2054
|
+
end: cur
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
if (shape.kind === "seq") {
|
|
2058
|
+
const endV2 = mint("_e");
|
|
2059
|
+
const okV = mint("_ok");
|
|
2060
|
+
const runCond = (p, at) => {
|
|
2061
|
+
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
2062
|
+
return p.negated ? `!(${c})` : `(${c})`;
|
|
2063
|
+
};
|
|
2064
|
+
const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
|
|
2065
|
+
let first = true;
|
|
2066
|
+
for (const p of shape.parts) {
|
|
2067
|
+
if (p.part === "lit") {
|
|
2068
|
+
const L = p.cps.length;
|
|
2069
|
+
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
2070
|
+
if (p.optional) {
|
|
2071
|
+
lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
2072
|
+
} else {
|
|
2073
|
+
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
2074
|
+
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
2075
|
+
}
|
|
2076
|
+
} else if (p.part === "run" && !p.unbounded) {
|
|
2077
|
+
if (p.min === 1) {
|
|
2078
|
+
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
2079
|
+
lines.push(`${ind2} ${endV2}++`);
|
|
2080
|
+
} else {
|
|
2081
|
+
lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2082
|
+
}
|
|
2083
|
+
} else if (p.part === "run") {
|
|
2084
|
+
const s = mint("_s");
|
|
2085
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
2086
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2087
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
2088
|
+
} else {
|
|
2089
|
+
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
2090
|
+
if (!p.unbounded) {
|
|
2091
|
+
lines.push(...inner.setup);
|
|
2092
|
+
if (p.min === 1) {
|
|
2093
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2094
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2095
|
+
} else {
|
|
2096
|
+
lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
2097
|
+
}
|
|
2098
|
+
} else {
|
|
2099
|
+
const progV = mint("_prog");
|
|
2100
|
+
lines.push(`${ind2} let ${progV} = false`);
|
|
2101
|
+
lines.push(`${ind2} while (true) {`);
|
|
2102
|
+
lines.push(...inner.setup);
|
|
2103
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2104
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2105
|
+
lines.push(`${ind2} ${progV} = true`);
|
|
2106
|
+
lines.push(`${ind2} }`);
|
|
2107
|
+
if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
first = false;
|
|
2111
|
+
}
|
|
2112
|
+
lines.push(`${ind2} ${okV} = true`);
|
|
2113
|
+
lines.push(`${ind2}} while (false)`);
|
|
2114
|
+
lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
|
|
2115
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2116
|
+
}
|
|
2117
|
+
if (shape.kind === "litFold") {
|
|
2118
|
+
const openLen2 = shape.open.length;
|
|
2119
|
+
const endV2 = mint("_end");
|
|
2120
|
+
const checks = shape.open.map((cp, k) => {
|
|
2121
|
+
const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
|
|
2122
|
+
return foldEq(cVar, cp);
|
|
2123
|
+
});
|
|
2124
|
+
return {
|
|
2125
|
+
setup: [
|
|
2126
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2127
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
|
|
2128
|
+
],
|
|
2129
|
+
ok: `${endV2} > ${start}`,
|
|
2130
|
+
end: endV2
|
|
1404
2131
|
};
|
|
1405
2132
|
}
|
|
1406
2133
|
if (shape.kind === "until") {
|
|
2134
|
+
const j2 = mint("_j");
|
|
2135
|
+
const openLen2 = shape.open.length;
|
|
2136
|
+
const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
2137
|
+
return {
|
|
2138
|
+
setup: [
|
|
2139
|
+
`${ind2}let ${j2} = ${start}`,
|
|
2140
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
|
|
2141
|
+
`${ind2} ${j2} = ${start} + ${openLen2}`,
|
|
2142
|
+
`${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
|
|
2143
|
+
`${ind2}}`
|
|
2144
|
+
],
|
|
2145
|
+
// An open-until-terminator always completes (stop char or EOF), so a matched
|
|
2146
|
+
// open literal is a full match; `end === start` iff the open didn't match.
|
|
2147
|
+
ok: `${j2} > ${start}`,
|
|
2148
|
+
end: j2
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
if (shape.kind === "string") {
|
|
2152
|
+
const okV = mint("_ok");
|
|
2153
|
+
const endV2 = mint("_end");
|
|
2154
|
+
const i = mint("_i");
|
|
2155
|
+
const c2 = mint("_c");
|
|
2156
|
+
const hard = stringHardStop(shape);
|
|
2157
|
+
const ltLines = shape.escLineTerm ? [] : (() => {
|
|
2158
|
+
const c3 = mint("_c");
|
|
2159
|
+
return [
|
|
2160
|
+
`${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
|
|
2161
|
+
`${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
|
|
2162
|
+
];
|
|
2163
|
+
})();
|
|
1407
2164
|
return {
|
|
1408
2165
|
setup: [
|
|
1409
|
-
|
|
1410
|
-
|
|
2166
|
+
`${ind2}let ${okV} = false`,
|
|
2167
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2168
|
+
`${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
|
|
2169
|
+
`${ind2} let ${i} = ${start} + 1`,
|
|
2170
|
+
`${ind2} while (${i} < input.length) {`,
|
|
2171
|
+
`${ind2} const ${c2} = input.charCodeAt(${i})`,
|
|
2172
|
+
`${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
|
|
2173
|
+
`${ind2} if (${c2} === ${BACKSLASH}) {`,
|
|
2174
|
+
`${ind2} if (${i} + 1 >= input.length) break`,
|
|
2175
|
+
...ltLines,
|
|
2176
|
+
`${ind2} ${i} += 2`,
|
|
2177
|
+
`${ind2} continue`,
|
|
2178
|
+
`${ind2} }`,
|
|
2179
|
+
...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
|
|
2180
|
+
`${ind2} ${i}++`,
|
|
2181
|
+
`${ind2} }`,
|
|
2182
|
+
`${ind2}}`
|
|
1411
2183
|
],
|
|
1412
|
-
|
|
2184
|
+
ok: okV,
|
|
2185
|
+
end: endV2
|
|
1413
2186
|
};
|
|
1414
2187
|
}
|
|
1415
|
-
const
|
|
2188
|
+
const j = mint("_j");
|
|
2189
|
+
const endV = mint("_end");
|
|
2190
|
+
const openLen = shape.open.length;
|
|
2191
|
+
const closeLen = shape.close.length;
|
|
2192
|
+
const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
2193
|
+
const closeChk = litCond(j, shape.close);
|
|
1416
2194
|
return {
|
|
1417
2195
|
setup: [
|
|
1418
|
-
|
|
1419
|
-
|
|
2196
|
+
`${ind2}let ${j} = ${start}`,
|
|
2197
|
+
`${ind2}let ${endV} = ${start}`,
|
|
2198
|
+
`${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
|
|
2199
|
+
`${ind2} ${j} = ${start} + ${openLen}`,
|
|
2200
|
+
`${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
|
|
2201
|
+
`${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
|
|
2202
|
+
`${ind2}}`
|
|
1420
2203
|
],
|
|
1421
|
-
|
|
2204
|
+
ok: `${endV} > ${start}`,
|
|
2205
|
+
end: endV
|
|
1422
2206
|
};
|
|
1423
2207
|
}
|
|
1424
|
-
function
|
|
1425
|
-
const
|
|
1426
|
-
return ` if (${
|
|
2208
|
+
function scanBranch(shape, mint) {
|
|
2209
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
2210
|
+
return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
|
|
1427
2211
|
}
|
|
1428
|
-
function
|
|
1429
|
-
|
|
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");
|
|
1434
|
-
}
|
|
1435
|
-
function captureChunk(startVar, kindIndex) {
|
|
2212
|
+
function scanBranchLabeled(shape, kindIndex, mint) {
|
|
2213
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1436
2214
|
return [
|
|
2215
|
+
...m.setup,
|
|
2216
|
+
` if (${m.end} > _e) {`,
|
|
1437
2217
|
` 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");
|
|
2218
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
|
|
2219
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
2220
|
+
` }`,
|
|
2221
|
+
` _e = ${m.end}`,
|
|
2222
|
+
` continue`,
|
|
2223
|
+
` }`
|
|
2224
|
+
].join("\n");
|
|
1449
2225
|
}
|
|
1450
2226
|
|
|
1451
2227
|
// src/compiler/trivia-fast-path.ts
|
|
2228
|
+
function makeMint() {
|
|
2229
|
+
let n = 0;
|
|
2230
|
+
return (prefix = "_v") => `${prefix}${n++}`;
|
|
2231
|
+
}
|
|
1452
2232
|
function unwrapTrivia2(p) {
|
|
1453
2233
|
let cur = p;
|
|
1454
2234
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1465,14 +2245,14 @@ function choiceArms(p) {
|
|
|
1465
2245
|
return null;
|
|
1466
2246
|
}
|
|
1467
2247
|
function armShape(arm) {
|
|
1468
|
-
const
|
|
1469
|
-
return
|
|
2248
|
+
const d = getCoreRegexDef(arm);
|
|
2249
|
+
return d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1470
2250
|
}
|
|
1471
2251
|
function analyzeTriviaFastPath(trivia2) {
|
|
1472
2252
|
const core = unwrapTrivia2(trivia2);
|
|
1473
|
-
const
|
|
1474
|
-
if (
|
|
1475
|
-
const shape =
|
|
2253
|
+
const directDef = getCoreRegexDef(core);
|
|
2254
|
+
if (directDef) {
|
|
2255
|
+
const shape = scanShapeFromRegex(directDef.source, directDef.flags);
|
|
1476
2256
|
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1477
2257
|
}
|
|
1478
2258
|
const inner = oneOrMoreInner(core);
|
|
@@ -1496,10 +2276,11 @@ var CAP_RECORD = [
|
|
|
1496
2276
|
` }`
|
|
1497
2277
|
].join("\n");
|
|
1498
2278
|
function composeFastLoop(shapes) {
|
|
2279
|
+
const mint = makeMint();
|
|
1499
2280
|
return [
|
|
1500
2281
|
` while (_e < input.length) {`,
|
|
1501
2282
|
` const c = input.charCodeAt(_e)`,
|
|
1502
|
-
...shapes.map(scanBranch),
|
|
2283
|
+
...shapes.map((s) => scanBranch(s, mint)),
|
|
1503
2284
|
` break`,
|
|
1504
2285
|
` }`
|
|
1505
2286
|
].join("\n");
|
|
@@ -1519,20 +2300,21 @@ function analyzeLabeledScannableRun(trivia2) {
|
|
|
1519
2300
|
if (!spec) return null;
|
|
1520
2301
|
const out = [];
|
|
1521
2302
|
for (const arm of spec.arms) {
|
|
1522
|
-
const
|
|
1523
|
-
const shape =
|
|
2303
|
+
const d = getCoreRegexDef(arm.parser);
|
|
2304
|
+
const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1524
2305
|
if (!shape) return null;
|
|
1525
2306
|
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1526
2307
|
}
|
|
1527
2308
|
return out;
|
|
1528
2309
|
}
|
|
1529
2310
|
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
2311
|
+
const mint = makeMint();
|
|
1530
2312
|
return [
|
|
1531
2313
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1532
2314
|
` let _e = _pos`,
|
|
1533
2315
|
` while (_e < input.length) {`,
|
|
1534
2316
|
` const c = input.charCodeAt(_e)`,
|
|
1535
|
-
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
2317
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
|
|
1536
2318
|
` break`,
|
|
1537
2319
|
` }`,
|
|
1538
2320
|
` return _e`,
|
|
@@ -1609,6 +2391,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
|
|
|
1609
2391
|
].join("\n");
|
|
1610
2392
|
}
|
|
1611
2393
|
|
|
2394
|
+
// src/compiler/scannable-terminal.ts
|
|
2395
|
+
function emitScannableTerminal(shape, c) {
|
|
2396
|
+
const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
|
|
2397
|
+
const stmts = [...m.setup];
|
|
2398
|
+
if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
|
|
2399
|
+
stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
|
|
2400
|
+
return { stmts, endVar: m.end };
|
|
2401
|
+
}
|
|
2402
|
+
|
|
1612
2403
|
// src/compiler/inline-build.ts
|
|
1613
2404
|
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
2405
|
function escapeRegExp(s) {
|
|
@@ -1730,14 +2521,45 @@ function failReturn(expected, posExpr) {
|
|
|
1730
2521
|
function failReturnArr(expectedArr, posExpr) {
|
|
1731
2522
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1732
2523
|
}
|
|
2524
|
+
function hoistExpected(ctx, constArrSource) {
|
|
2525
|
+
let name = ctx.expectedMap.get(constArrSource);
|
|
2526
|
+
if (name === void 0) {
|
|
2527
|
+
name = `_fx${ctx.expectedDecls.length}`;
|
|
2528
|
+
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
2529
|
+
ctx.expectedMap.set(constArrSource, name);
|
|
2530
|
+
}
|
|
2531
|
+
return name;
|
|
2532
|
+
}
|
|
1733
2533
|
function failBody(ctx, expected, posExpr) {
|
|
1734
|
-
if (ctx.failLabel)
|
|
2534
|
+
if (ctx.failLabel) {
|
|
2535
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2536
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
2537
|
+
}
|
|
1735
2538
|
return failReturn(expected, posExpr);
|
|
1736
2539
|
}
|
|
1737
|
-
function failArrBody(ctx, expectedArr, posExpr) {
|
|
1738
|
-
if (ctx.failLabel)
|
|
2540
|
+
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
2541
|
+
if (ctx.failLabel) {
|
|
2542
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2543
|
+
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
2544
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
2545
|
+
}
|
|
2546
|
+
if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1739
2547
|
return failReturnArr(expectedArr, posExpr);
|
|
1740
2548
|
}
|
|
2549
|
+
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
2550
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
2551
|
+
}
|
|
2552
|
+
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
2553
|
+
if (srcCtx !== "_ctx") {
|
|
2554
|
+
if (ctx.failLabel) {
|
|
2555
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2556
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
2557
|
+
}
|
|
2558
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
2559
|
+
}
|
|
2560
|
+
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
2561
|
+
return resultFromRecorded();
|
|
2562
|
+
}
|
|
1741
2563
|
function emitIfFail(ctx, cond, body) {
|
|
1742
2564
|
return [
|
|
1743
2565
|
`${ind(ctx)}if (${cond}) {`,
|
|
@@ -1773,13 +2595,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
|
1773
2595
|
`}`
|
|
1774
2596
|
].join("\n"));
|
|
1775
2597
|
}
|
|
1776
|
-
function emitNamedFnCall(ctx, fnName, pos
|
|
2598
|
+
function emitNamedFnCall(ctx, fnName, pos) {
|
|
1777
2599
|
const vv = v(ctx, "_pfv");
|
|
1778
2600
|
const ev = v(ctx, "_pfe");
|
|
1779
2601
|
return {
|
|
1780
2602
|
stmts: [
|
|
1781
2603
|
`${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
|
|
1782
|
-
|
|
2604
|
+
// ref/lazy returns the inner failure verbatim — propagate the recorded
|
|
2605
|
+
// deepest failure (shares _ctx with the named fn), not a "parser" label.
|
|
2606
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
|
|
1783
2607
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
1784
2608
|
],
|
|
1785
2609
|
valueVar: vv,
|
|
@@ -1882,13 +2706,34 @@ function ensureTriviaFn(ctx) {
|
|
|
1882
2706
|
function ensureTriviaCaptureFn(ctx) {
|
|
1883
2707
|
return ensureTriviaFn(ctx);
|
|
1884
2708
|
}
|
|
1885
|
-
function firstSetCond(codeVar,
|
|
1886
|
-
if (
|
|
1887
|
-
if (
|
|
1888
|
-
return
|
|
2709
|
+
function firstSetCond(codeVar, fs2) {
|
|
2710
|
+
if (fs2.kind === "any") return "true";
|
|
2711
|
+
if (fs2.kind === "empty") return "false";
|
|
2712
|
+
return fs2.ranges.map(
|
|
1889
2713
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1890
2714
|
).join(" || ");
|
|
1891
2715
|
}
|
|
2716
|
+
var SWITCH_RANGE_LIMIT = 4;
|
|
2717
|
+
var SWITCH_MAX_CASES = 48;
|
|
2718
|
+
var SWITCH_MIN_CASES = 3;
|
|
2719
|
+
var _forceDisjointIf = false;
|
|
2720
|
+
function planDisjointDispatch(parsers) {
|
|
2721
|
+
const cases = [];
|
|
2722
|
+
let total = 0;
|
|
2723
|
+
for (const p of parsers) {
|
|
2724
|
+
const fs2 = p._meta.firstSet;
|
|
2725
|
+
if (fs2.kind !== "ranges") return { kind: "if" };
|
|
2726
|
+
const pts = [];
|
|
2727
|
+
for (const r of fs2.ranges) {
|
|
2728
|
+
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2729
|
+
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2730
|
+
}
|
|
2731
|
+
total += pts.length;
|
|
2732
|
+
if (total > SWITCH_MAX_CASES) return { kind: "if" };
|
|
2733
|
+
cases.push(pts);
|
|
2734
|
+
}
|
|
2735
|
+
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2736
|
+
}
|
|
1892
2737
|
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1893
2738
|
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1894
2739
|
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
@@ -2002,18 +2847,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2002
2847
|
return true;
|
|
2003
2848
|
}
|
|
2004
2849
|
}
|
|
2005
|
-
function emitFallible(inner, ctx, pos) {
|
|
2850
|
+
function emitFallible(inner, ctx, pos, swallow = false) {
|
|
2006
2851
|
const lbl = v(ctx, "_lbl");
|
|
2007
2852
|
const okV = `${lbl}ok`;
|
|
2008
2853
|
const valV = `${lbl}v`;
|
|
2009
2854
|
const endV = `${lbl}e`;
|
|
2010
2855
|
const savedLabel = ctx.failLabel;
|
|
2011
2856
|
const savedIndent = ctx.indent;
|
|
2857
|
+
const savedRecord = ctx.recordFail;
|
|
2012
2858
|
ctx.failLabel = lbl;
|
|
2013
2859
|
ctx.indent = savedIndent + 1;
|
|
2860
|
+
if (swallow) ctx.recordFail = false;
|
|
2014
2861
|
const r = emit(inner, ctx, pos);
|
|
2015
2862
|
ctx.failLabel = savedLabel;
|
|
2016
2863
|
ctx.indent = savedIndent;
|
|
2864
|
+
ctx.recordFail = savedRecord;
|
|
2017
2865
|
const ind0 = ind(ctx);
|
|
2018
2866
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
2019
2867
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
@@ -2036,6 +2884,7 @@ function emitFallible(inner, ctx, pos) {
|
|
|
2036
2884
|
];
|
|
2037
2885
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
2038
2886
|
}
|
|
2887
|
+
var CHARCODE_CHAIN_MAX = 16;
|
|
2039
2888
|
function emitLit(def, ctx, pos) {
|
|
2040
2889
|
const { value, caseInsensitive } = def;
|
|
2041
2890
|
const len = value.length;
|
|
@@ -2058,7 +2907,7 @@ function emitLit(def, ctx, pos) {
|
|
|
2058
2907
|
...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
|
|
2059
2908
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
2060
2909
|
);
|
|
2061
|
-
} else if (len <=
|
|
2910
|
+
} else if (len <= CHARCODE_CHAIN_MAX) {
|
|
2062
2911
|
const checks = Array.from(
|
|
2063
2912
|
{ length: len },
|
|
2064
2913
|
(_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
|
|
@@ -2080,7 +2929,55 @@ function emitLit(def, ctx, pos) {
|
|
|
2080
2929
|
function escapeKeywordRe(s) {
|
|
2081
2930
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2082
2931
|
}
|
|
2932
|
+
function emitKeywordsFast(def, ctx, pos) {
|
|
2933
|
+
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
2934
|
+
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
2935
|
+
if (def.caseInsensitive && def.boundary) return null;
|
|
2936
|
+
let boundary = null;
|
|
2937
|
+
if (def.boundary) {
|
|
2938
|
+
let body = def.boundary;
|
|
2939
|
+
const negated = body.startsWith("^");
|
|
2940
|
+
if (negated) body = body.slice(1);
|
|
2941
|
+
const ranges = parseClassRanges(body);
|
|
2942
|
+
if (!ranges) return null;
|
|
2943
|
+
boundary = { ranges, negated };
|
|
2944
|
+
}
|
|
2945
|
+
const mint = (prefix = "_v") => v(ctx, prefix);
|
|
2946
|
+
const lbl = v(ctx, "_kwLbl");
|
|
2947
|
+
const valV = v(ctx, "_kwv");
|
|
2948
|
+
const endV = v(ctx, "_kwe");
|
|
2949
|
+
const bodyInd = ind(ctx) + " ";
|
|
2950
|
+
const tries = [];
|
|
2951
|
+
for (const w of def.words) {
|
|
2952
|
+
const cps = Array.from(w, (ch) => ch.codePointAt(0));
|
|
2953
|
+
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
2954
|
+
if (boundary) {
|
|
2955
|
+
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
2956
|
+
}
|
|
2957
|
+
const m = emitShapeMatch(shape, pos, mint, bodyInd);
|
|
2958
|
+
tries.push(
|
|
2959
|
+
...m.setup,
|
|
2960
|
+
// Slice from input rather than reusing the literal word: `caseInsensitive`
|
|
2961
|
+
// must return the text as it actually appeared (e.g. "ABC" for keyword
|
|
2962
|
+
// "abc"), matching what the original `RegExp.exec()[0]` returned.
|
|
2963
|
+
`${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
|
|
2964
|
+
);
|
|
2965
|
+
}
|
|
2966
|
+
const stmts = [
|
|
2967
|
+
`${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
|
|
2968
|
+
`${ind(ctx)}${lbl}: {`,
|
|
2969
|
+
...tries,
|
|
2970
|
+
`${ind(ctx)}}`,
|
|
2971
|
+
// Every word has length >= 1 (checked above), so a real match always
|
|
2972
|
+
// advances past `pos` — `endV === pos` only happens when no candidate matched.
|
|
2973
|
+
...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
|
|
2974
|
+
];
|
|
2975
|
+
stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
|
|
2976
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
2977
|
+
}
|
|
2083
2978
|
function emitKeywords(def, ctx, pos) {
|
|
2979
|
+
const fast = emitKeywordsFast(def, ctx, pos);
|
|
2980
|
+
if (fast) return fast;
|
|
2084
2981
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
2085
2982
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
2086
2983
|
const flags = def.caseInsensitive ? "iuy" : "uy";
|
|
@@ -2105,6 +3002,22 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2105
3002
|
return { stmts, valueVar: vv, endVar };
|
|
2106
3003
|
}
|
|
2107
3004
|
function emitRegex(def, ctx, pos) {
|
|
3005
|
+
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
3006
|
+
const shape = scanShapeFromRegex(def.source, def.flags);
|
|
3007
|
+
if (shape) {
|
|
3008
|
+
const vv2 = v(ctx);
|
|
3009
|
+
const scanned = emitScannableTerminal(shape, {
|
|
3010
|
+
ind: ind(ctx),
|
|
3011
|
+
pos,
|
|
3012
|
+
valueVar: vv2,
|
|
3013
|
+
failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
|
|
3014
|
+
fresh: (prefix) => v(ctx, prefix)
|
|
3015
|
+
});
|
|
3016
|
+
if (scanned) {
|
|
3017
|
+
const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
|
|
3018
|
+
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
2108
3021
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2109
3022
|
const key = `${def.optimizedSource}/${flags}`;
|
|
2110
3023
|
let rName = ctx.regexMap.get(key);
|
|
@@ -2115,7 +3028,6 @@ function emitRegex(def, ctx, pos) {
|
|
|
2115
3028
|
}
|
|
2116
3029
|
const mv = v(ctx, "_m");
|
|
2117
3030
|
const vv = v(ctx);
|
|
2118
|
-
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2119
3031
|
const stmts = [
|
|
2120
3032
|
`${ind(ctx)}${rName}.lastIndex = ${pos}`,
|
|
2121
3033
|
`${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
|
|
@@ -2174,15 +3086,68 @@ function emitSeq(def, ctx, pos) {
|
|
|
2174
3086
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
2175
3087
|
return { stmts, valueVar: arrV, endVar };
|
|
2176
3088
|
}
|
|
3089
|
+
function staticExpectedArr(parsers) {
|
|
3090
|
+
return JSON.stringify(parsers.flatMap((p) => {
|
|
3091
|
+
const e = staticExpected(p);
|
|
3092
|
+
return e.length > 0 ? e : [p._tag];
|
|
3093
|
+
}));
|
|
3094
|
+
}
|
|
3095
|
+
function failsAtStart(p) {
|
|
3096
|
+
const d = p._def;
|
|
3097
|
+
switch (d.tag) {
|
|
3098
|
+
case "literal":
|
|
3099
|
+
case "regex":
|
|
3100
|
+
case "keywords":
|
|
3101
|
+
case "guard":
|
|
3102
|
+
case "not":
|
|
3103
|
+
return true;
|
|
3104
|
+
case "transform":
|
|
3105
|
+
case "label":
|
|
3106
|
+
return failsAtStart(d.parser);
|
|
3107
|
+
default:
|
|
3108
|
+
return false;
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
function armStaticExpected(ctx, p) {
|
|
3112
|
+
return hoistExpected(ctx, staticExpectedArr([p]));
|
|
3113
|
+
}
|
|
3114
|
+
function canMatchEmptyAtStart(p) {
|
|
3115
|
+
const d = p._def;
|
|
3116
|
+
switch (d.tag) {
|
|
3117
|
+
case "regex":
|
|
3118
|
+
return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
|
|
3119
|
+
case "optional":
|
|
3120
|
+
return true;
|
|
3121
|
+
case "many":
|
|
3122
|
+
return true;
|
|
3123
|
+
case "transform":
|
|
3124
|
+
case "label":
|
|
3125
|
+
return canMatchEmptyAtStart(d.parser);
|
|
3126
|
+
case "literal":
|
|
3127
|
+
return d.value.length === 0;
|
|
3128
|
+
default:
|
|
3129
|
+
return false;
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
function needsFirstSetGuard(p) {
|
|
3133
|
+
const fs2 = p._meta.firstSet;
|
|
3134
|
+
return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
3135
|
+
}
|
|
3136
|
+
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
3137
|
+
if (failsAtStart(p)) {
|
|
3138
|
+
const r = emit(p, ctx, pos);
|
|
3139
|
+
return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
|
|
3140
|
+
}
|
|
3141
|
+
const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3142
|
+
return [
|
|
3143
|
+
...stmts,
|
|
3144
|
+
...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
|
|
3145
|
+
`${ind(ctx)}${valV} = ${valVar}`,
|
|
3146
|
+
`${ind(ctx)}${endV} = ${endVar}`
|
|
3147
|
+
];
|
|
3148
|
+
}
|
|
2177
3149
|
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
|
-
);
|
|
3150
|
+
const allExpected = staticExpectedArr(def.parsers);
|
|
2186
3151
|
if (def.disjoint) {
|
|
2187
3152
|
const codeV = v(ctx, "_code");
|
|
2188
3153
|
const valV = v(ctx, "_chv");
|
|
@@ -2191,6 +3156,27 @@ function emitChoice(def, ctx, pos) {
|
|
|
2191
3156
|
`${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
|
|
2192
3157
|
`${ind(ctx)}let ${valV}, ${endV} = ${pos}`
|
|
2193
3158
|
];
|
|
3159
|
+
const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
|
|
3160
|
+
if (plan.kind === "switch") {
|
|
3161
|
+
stmts.push(`${ind(ctx)}switch (${codeV}) {`);
|
|
3162
|
+
ctx.indent++;
|
|
3163
|
+
for (let i = 0; i < def.parsers.length; i++) {
|
|
3164
|
+
const p = def.parsers[i];
|
|
3165
|
+
for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
|
|
3166
|
+
stmts.push(`${ind(ctx)}{`);
|
|
3167
|
+
ctx.indent++;
|
|
3168
|
+
stmts.push(
|
|
3169
|
+
...emitDisjointArm(p, ctx, pos, valV, endV),
|
|
3170
|
+
`${ind(ctx)}break`
|
|
3171
|
+
);
|
|
3172
|
+
ctx.indent--;
|
|
3173
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3174
|
+
}
|
|
3175
|
+
stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
|
|
3176
|
+
ctx.indent--;
|
|
3177
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3178
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3179
|
+
}
|
|
2194
3180
|
let first = true;
|
|
2195
3181
|
for (const p of def.parsers) {
|
|
2196
3182
|
const cond = firstSetCond(codeV, p._meta.firstSet);
|
|
@@ -2198,10 +3184,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2198
3184
|
first = false;
|
|
2199
3185
|
stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
|
|
2200
3186
|
ctx.indent++;
|
|
2201
|
-
|
|
2202
|
-
stmts.push(...r.stmts);
|
|
2203
|
-
stmts.push(`${ind(ctx)}${valV} = ${r.valueVar}`);
|
|
2204
|
-
stmts.push(`${ind(ctx)}${endV} = ${r.endVar}`);
|
|
3187
|
+
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
|
|
2205
3188
|
ctx.indent--;
|
|
2206
3189
|
stmts.push(`${ind(ctx)}}`);
|
|
2207
3190
|
}
|
|
@@ -2210,7 +3193,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2210
3193
|
}
|
|
2211
3194
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
2212
3195
|
}
|
|
2213
|
-
function emitGreedyClassify(def, superIndex,
|
|
3196
|
+
function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
2214
3197
|
const superParser = def.parsers[superIndex];
|
|
2215
3198
|
const regexDef = getCoreRegexDef(superParser);
|
|
2216
3199
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
@@ -2225,10 +3208,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
|
|
|
2225
3208
|
const wordV = v(ctx, "_gw");
|
|
2226
3209
|
const endV = v(ctx, "_ge");
|
|
2227
3210
|
const valV = v(ctx, "_gcv");
|
|
3211
|
+
const regexExpected = JSON.stringify(staticExpected(superParser));
|
|
2228
3212
|
const stmts = [
|
|
2229
3213
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
2230
3214
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
2231
|
-
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx,
|
|
3215
|
+
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
|
|
2232
3216
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
2233
3217
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
|
|
2234
3218
|
`${ind(ctx)}let ${valV}`
|
|
@@ -2286,18 +3270,25 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2286
3270
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
2287
3271
|
return { stmts, valueVar: valV, endVar: endV };
|
|
2288
3272
|
}
|
|
2289
|
-
function emitFirstMatch(def,
|
|
3273
|
+
function emitFirstMatch(def, ctx, pos) {
|
|
2290
3274
|
const resValV = v(ctx, "_crv");
|
|
2291
3275
|
const resEndV = v(ctx, "_cre");
|
|
2292
3276
|
const resOkV = v(ctx, "_crok");
|
|
3277
|
+
const codeV = v(ctx, "_chcode");
|
|
3278
|
+
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2293
3279
|
const ind0 = ind(ctx);
|
|
2294
3280
|
const stmts = [
|
|
2295
|
-
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false
|
|
3281
|
+
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
3282
|
+
`${ind0}let ${slots.join(", ")}`,
|
|
3283
|
+
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
2296
3284
|
];
|
|
2297
3285
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2298
3286
|
const p = def.parsers[i];
|
|
2299
3287
|
const gate = def.gates[i];
|
|
2300
3288
|
const autoNot = def.autoNot[i];
|
|
3289
|
+
const atStart = failsAtStart(p);
|
|
3290
|
+
const staticFx = armStaticExpected(ctx, p);
|
|
3291
|
+
const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
2301
3292
|
let gateCond = null;
|
|
2302
3293
|
if (gate) {
|
|
2303
3294
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -2312,49 +3303,53 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2312
3303
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2313
3304
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2314
3305
|
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
3306
|
+
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
2315
3307
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2316
|
-
ctx
|
|
2317
|
-
|
|
3308
|
+
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
3309
|
+
ctx.indent += fsGuard ? 2 : 1;
|
|
2318
3310
|
if (markLeaves) {
|
|
2319
3311
|
stmts.push(
|
|
2320
|
-
`${
|
|
2321
|
-
`${
|
|
2322
|
-
`${
|
|
2323
|
-
`${
|
|
3312
|
+
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
3313
|
+
`${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
|
|
3314
|
+
`${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
3315
|
+
`${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
|
|
2324
3316
|
);
|
|
2325
3317
|
}
|
|
2326
|
-
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3318
|
+
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
|
|
2327
3319
|
stmts.push(...armStmts);
|
|
2328
|
-
ctx.indent--;
|
|
2329
3320
|
if (autoNot && autoNot.length > 0) {
|
|
2330
3321
|
const anCode = v(ctx, "_anc");
|
|
2331
3322
|
const rejectCond = autoNot.map(
|
|
2332
3323
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
|
|
2333
3324
|
).join(" || ");
|
|
2334
|
-
stmts.push(`${
|
|
2335
|
-
stmts.push(`${
|
|
2336
|
-
stmts.push(`${
|
|
2337
|
-
stmts.push(`${
|
|
2338
|
-
stmts.push(`${
|
|
2339
|
-
stmts.push(`${
|
|
2340
|
-
stmts.push(`${
|
|
2341
|
-
stmts.push(`${
|
|
2342
|
-
|
|
3325
|
+
stmts.push(`${ind(ctx)}if (${okVar}) {`);
|
|
3326
|
+
stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
|
|
3327
|
+
stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
|
|
3328
|
+
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
3329
|
+
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
3330
|
+
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
3331
|
+
stmts.push(`${ind(ctx)} }`);
|
|
3332
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3333
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
3334
|
+
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
2343
3335
|
} else {
|
|
2344
|
-
stmts.push(`${
|
|
2345
|
-
|
|
3336
|
+
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
3337
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
|
|
2346
3338
|
}
|
|
3339
|
+
ctx.indent -= fsGuard ? 2 : 1;
|
|
3340
|
+
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
2347
3341
|
stmts.push(`${ind0}}`);
|
|
2348
3342
|
}
|
|
2349
|
-
|
|
3343
|
+
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
3344
|
+
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
2350
3345
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
2351
3346
|
}
|
|
2352
3347
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
2353
3348
|
if (strategy.tag === "greedyClassify")
|
|
2354
|
-
return emitGreedyClassify(def, strategy.superIndex,
|
|
3349
|
+
return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
|
|
2355
3350
|
if (strategy.tag === "literalsLongestFirst")
|
|
2356
3351
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
2357
|
-
return emitFirstMatch(def,
|
|
3352
|
+
return emitFirstMatch(def, ctx, pos);
|
|
2358
3353
|
}
|
|
2359
3354
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
2360
3355
|
const def = p._def;
|
|
@@ -2375,7 +3370,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2375
3370
|
function emitLiteralCondition(litVal, pos) {
|
|
2376
3371
|
const len = litVal.length;
|
|
2377
3372
|
if (len === 0) return "true";
|
|
2378
|
-
if (len >
|
|
3373
|
+
if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
|
|
2379
3374
|
const checks = [`${pos} + ${len} <= input.length`];
|
|
2380
3375
|
for (let i = 0; i < len; i++) {
|
|
2381
3376
|
const code = litVal.codePointAt(i);
|
|
@@ -2424,7 +3419,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2424
3419
|
itemPos = npV;
|
|
2425
3420
|
}
|
|
2426
3421
|
}
|
|
2427
|
-
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
3422
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
2428
3423
|
stmts.push(
|
|
2429
3424
|
...iterStmts,
|
|
2430
3425
|
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
|
|
@@ -2438,7 +3433,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2438
3433
|
function emitOptional(def, ctx, pos) {
|
|
2439
3434
|
const valV = v(ctx, "_opt");
|
|
2440
3435
|
const endV = v(ctx, "_opte");
|
|
2441
|
-
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
3436
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2442
3437
|
const ind0 = ind(ctx);
|
|
2443
3438
|
const stmts = [
|
|
2444
3439
|
...lblStmts,
|
|
@@ -2450,7 +3445,7 @@ function emitOptional(def, ctx, pos) {
|
|
|
2450
3445
|
function emitSepBy(_p, def, ctx, pos) {
|
|
2451
3446
|
const arrV = v(ctx, "_arr");
|
|
2452
3447
|
const curV = v(ctx, "_cur");
|
|
2453
|
-
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
3448
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
2454
3449
|
const stmts = [
|
|
2455
3450
|
`${ind(ctx)}const ${arrV} = []`,
|
|
2456
3451
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
@@ -2482,11 +3477,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2482
3477
|
);
|
|
2483
3478
|
sepAtPos = spV;
|
|
2484
3479
|
const rollbackToSep = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${markTl}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${markLog}; `;
|
|
2485
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3480
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2486
3481
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
2487
3482
|
const npV = v(ctx, "_np");
|
|
2488
3483
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
2489
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3484
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2490
3485
|
stmts.push(
|
|
2491
3486
|
...nextStmts,
|
|
2492
3487
|
// item failed → unwind the separator too, back to the end of the last item
|
|
@@ -2499,11 +3494,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2499
3494
|
const spV = v(ctx, "_sp");
|
|
2500
3495
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
2501
3496
|
sepAtPos = spV;
|
|
2502
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3497
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2503
3498
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2504
3499
|
const npV = v(ctx, "_np");
|
|
2505
3500
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
2506
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3501
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2507
3502
|
stmts.push(
|
|
2508
3503
|
...nextStmts,
|
|
2509
3504
|
`${ind(ctx)}if (!${nextOk}) break`,
|
|
@@ -2520,10 +3515,10 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2520
3515
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
2521
3516
|
);
|
|
2522
3517
|
}
|
|
2523
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3518
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2524
3519
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2525
3520
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
2526
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
|
|
3521
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
2527
3522
|
stmts.push(
|
|
2528
3523
|
...nextStmts,
|
|
2529
3524
|
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
|
|
@@ -2581,11 +3576,12 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2581
3576
|
return { stmts, valueVar: valV, endVar: curV };
|
|
2582
3577
|
}
|
|
2583
3578
|
function emitNot(def, ctx, pos) {
|
|
2584
|
-
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3579
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
3580
|
+
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2585
3581
|
return {
|
|
2586
3582
|
stmts: [
|
|
2587
3583
|
...stmts,
|
|
2588
|
-
...emitIfFail(ctx, okVar, failBody(ctx,
|
|
3584
|
+
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
2589
3585
|
],
|
|
2590
3586
|
valueVar: "null",
|
|
2591
3587
|
endVar: pos
|
|
@@ -2617,7 +3613,7 @@ function emitNode(def, ctx, pos) {
|
|
|
2617
3613
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2618
3614
|
stmts.push(...innerStmts);
|
|
2619
3615
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2620
|
-
stmts.push(...emitIfFail(ctx, `!${okVar}`,
|
|
3616
|
+
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
2621
3617
|
let stV = "undefined";
|
|
2622
3618
|
if (clonesState) {
|
|
2623
3619
|
stV = v(ctx, "_nst");
|
|
@@ -2639,9 +3635,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2639
3635
|
const rv = v(ctx, "_rt");
|
|
2640
3636
|
const vv = v(ctx, "_rtv");
|
|
2641
3637
|
const ev = v(ctx, "_rte");
|
|
3638
|
+
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
3639
|
const stmts = [
|
|
2643
3640
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
2644
|
-
...emitIfFail(ctx, `!${rv}.ok`,
|
|
3641
|
+
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
2645
3642
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
2646
3643
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
2647
3644
|
];
|
|
@@ -2670,15 +3667,18 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2670
3667
|
}
|
|
2671
3668
|
const savedIndent = ctx.indent;
|
|
2672
3669
|
const savedFailLabel = ctx.failLabel;
|
|
3670
|
+
const savedRecord = ctx.recordFail;
|
|
2673
3671
|
ctx.indent = 1;
|
|
2674
3672
|
ctx.failLabel = "_pfail";
|
|
3673
|
+
ctx.recordFail = true;
|
|
2675
3674
|
const r = emit(resolved, ctx, "_pos");
|
|
2676
3675
|
ctx.indent = savedIndent;
|
|
2677
3676
|
ctx.failLabel = savedFailLabel;
|
|
3677
|
+
ctx.recordFail = savedRecord;
|
|
2678
3678
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
2679
3679
|
}
|
|
2680
3680
|
const fnName = ctx.namedParsers.get(p);
|
|
2681
|
-
return emitNamedFnCall(ctx, fnName, pos
|
|
3681
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
2682
3682
|
}
|
|
2683
3683
|
function emitRecover(def, ctx, pos) {
|
|
2684
3684
|
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
@@ -2696,7 +3696,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
2696
3696
|
`${ind0} if (${sentOk}) break`,
|
|
2697
3697
|
`${ind0} ${scanV}++`,
|
|
2698
3698
|
`${ind0} }`,
|
|
2699
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected:
|
|
3699
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
|
|
2700
3700
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2701
3701
|
`${ind0} ${valVar} = ${errV}`,
|
|
2702
3702
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -2854,11 +3854,14 @@ function emit(p, ctx, pos) {
|
|
|
2854
3854
|
ctx.namedParsers.set(innerParser, fnName);
|
|
2855
3855
|
const savedIndent = ctx.indent;
|
|
2856
3856
|
const savedFailLabel = ctx.failLabel;
|
|
3857
|
+
const savedRecord = ctx.recordFail;
|
|
2857
3858
|
ctx.indent = 1;
|
|
2858
3859
|
ctx.failLabel = "_pfail";
|
|
3860
|
+
ctx.recordFail = true;
|
|
2859
3861
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
2860
3862
|
ctx.indent = savedIndent;
|
|
2861
3863
|
ctx.failLabel = savedFailLabel;
|
|
3864
|
+
ctx.recordFail = savedRecord;
|
|
2862
3865
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
2863
3866
|
}
|
|
2864
3867
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -2869,7 +3872,9 @@ function emit(p, ctx, pos) {
|
|
|
2869
3872
|
stmts: [
|
|
2870
3873
|
`${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
|
|
2871
3874
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
2872
|
-
|
|
3875
|
+
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3876
|
+
// and propagate the inner failure verbatim (interpreter parity).
|
|
3877
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
|
|
2873
3878
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
2874
3879
|
],
|
|
2875
3880
|
valueVar: vv,
|
|
@@ -2970,7 +3975,7 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
2970
3975
|
const recursive = /* @__PURE__ */ new Set();
|
|
2971
3976
|
const descended = /* @__PURE__ */ new Set();
|
|
2972
3977
|
const active = /* @__PURE__ */ new Set();
|
|
2973
|
-
function
|
|
3978
|
+
function walk2(p) {
|
|
2974
3979
|
const def = p._def;
|
|
2975
3980
|
if (def.tag === "lazy") {
|
|
2976
3981
|
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
@@ -2987,13 +3992,13 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
2987
3992
|
return;
|
|
2988
3993
|
}
|
|
2989
3994
|
active.add(p);
|
|
2990
|
-
|
|
3995
|
+
walk2(resolved);
|
|
2991
3996
|
active.delete(p);
|
|
2992
3997
|
return;
|
|
2993
3998
|
}
|
|
2994
|
-
for (const child of childrenOf(def))
|
|
3999
|
+
for (const child of childrenOf(def)) walk2(child);
|
|
2995
4000
|
}
|
|
2996
|
-
for (const root of roots)
|
|
4001
|
+
for (const root of roots) walk2(root);
|
|
2997
4002
|
return { counts, recursive };
|
|
2998
4003
|
}
|
|
2999
4004
|
function compile(combinator, mapFnSources) {
|
|
@@ -3002,6 +4007,9 @@ function compile(combinator, mapFnSources) {
|
|
|
3002
4007
|
indent: 1,
|
|
3003
4008
|
regexDecls: [],
|
|
3004
4009
|
regexMap: /* @__PURE__ */ new Map(),
|
|
4010
|
+
expectedDecls: [],
|
|
4011
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4012
|
+
recordFail: true,
|
|
3005
4013
|
mapFns: [],
|
|
3006
4014
|
mapFnSrcs: [],
|
|
3007
4015
|
buildFns: [],
|
|
@@ -3023,6 +4031,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3023
4031
|
const source = [
|
|
3024
4032
|
...emptyTlDecls,
|
|
3025
4033
|
...ctx.regexDecls,
|
|
4034
|
+
...ctx.expectedDecls,
|
|
3026
4035
|
"",
|
|
3027
4036
|
...namedPrelude,
|
|
3028
4037
|
ctx.namedFnDecls.join("\n\n"),
|
|
@@ -3035,6 +4044,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3035
4044
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3036
4045
|
...emptyTlDecls,
|
|
3037
4046
|
...ctx.regexDecls,
|
|
4047
|
+
...ctx.expectedDecls,
|
|
3038
4048
|
collatorDecl,
|
|
3039
4049
|
...namedPrelude,
|
|
3040
4050
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
@@ -3080,6 +4090,9 @@ function compileRuleMap(ruleMap) {
|
|
|
3080
4090
|
indent: 1,
|
|
3081
4091
|
regexDecls: [],
|
|
3082
4092
|
regexMap: /* @__PURE__ */ new Map(),
|
|
4093
|
+
expectedDecls: [],
|
|
4094
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4095
|
+
recordFail: true,
|
|
3083
4096
|
mapFns: [],
|
|
3084
4097
|
mapFnSrcs: [],
|
|
3085
4098
|
buildFns: [],
|
|
@@ -3108,6 +4121,7 @@ function compileRuleMap(ruleMap) {
|
|
|
3108
4121
|
const hoistedDecls = [
|
|
3109
4122
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
3110
4123
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4124
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3111
4125
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3112
4126
|
mfDecl,
|
|
3113
4127
|
buildDecl,
|
|
@@ -3149,12 +4163,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3149
4163
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
3150
4164
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3151
4165
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3152
|
-
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
4166
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3153
4167
|
if (!needsWrapper) return innerFn;
|
|
3154
4168
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3155
4169
|
const hoistedDecls = [
|
|
3156
4170
|
emptyTlDecl,
|
|
3157
4171
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4172
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3158
4173
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3159
4174
|
mfDecl,
|
|
3160
4175
|
buildDecl,
|
|
@@ -3172,58 +4187,6 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3172
4187
|
].join("\n");
|
|
3173
4188
|
}
|
|
3174
4189
|
|
|
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
4190
|
// src/combinators/scanTo.ts
|
|
3228
4191
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
3229
4192
|
const meta = {
|
|
@@ -3304,11 +4267,11 @@ function balanced(open, close, options = {}) {
|
|
|
3304
4267
|
return combi;
|
|
3305
4268
|
}
|
|
3306
4269
|
function firstSetClassChars(c) {
|
|
3307
|
-
const
|
|
3308
|
-
if (
|
|
3309
|
-
if (
|
|
4270
|
+
const fs2 = c._meta.firstSet;
|
|
4271
|
+
if (fs2.kind === "empty") return [];
|
|
4272
|
+
if (fs2.kind !== "ranges") return null;
|
|
3310
4273
|
const out = [];
|
|
3311
|
-
for (const { lo, hi } of
|
|
4274
|
+
for (const { lo, hi } of fs2.ranges) {
|
|
3312
4275
|
if (hi - lo > 8) return null;
|
|
3313
4276
|
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3314
4277
|
}
|
|
@@ -3605,7 +4568,124 @@ function evaluateWordFactory(node2, scope, code) {
|
|
|
3605
4568
|
function evaluateExpr(node2, scope, code, mapFnSources) {
|
|
3606
4569
|
return exprToCombi(node2, scope, code, mapFnSources);
|
|
3607
4570
|
}
|
|
3608
|
-
function
|
|
4571
|
+
function evaluateCombinatorArray(node2, scope, code) {
|
|
4572
|
+
if (node2.type !== "ArrayExpression") return null;
|
|
4573
|
+
const val = anyValue(node2, scope, code, []);
|
|
4574
|
+
if (!Array.isArray(val) || val.length === 0) return null;
|
|
4575
|
+
if (!val.every(isCombinator)) return null;
|
|
4576
|
+
return val;
|
|
4577
|
+
}
|
|
4578
|
+
function asFragmentFactory(expr) {
|
|
4579
|
+
if (expr.type !== "ArrowFunctionExpression" && expr.type !== "FunctionExpression" && expr.type !== "FunctionDeclaration") return null;
|
|
4580
|
+
const fn = expr;
|
|
4581
|
+
return factoryBodyAndReturn(fn) ? fn : null;
|
|
4582
|
+
}
|
|
4583
|
+
var unwrapParen = (e) => e && e.type === "ParenthesizedExpression" ? e.expression : e;
|
|
4584
|
+
function factoryBodyAndReturn(fn) {
|
|
4585
|
+
const body = fn.body;
|
|
4586
|
+
if (!body) return null;
|
|
4587
|
+
const b = unwrapParen(body);
|
|
4588
|
+
if (b.type === "ObjectExpression") return { statements: [], retObj: b };
|
|
4589
|
+
if (b.type === "BlockStatement") {
|
|
4590
|
+
const statements = [];
|
|
4591
|
+
for (const stmt of b.body) {
|
|
4592
|
+
if (stmt.type === "ReturnStatement") {
|
|
4593
|
+
const a = unwrapParen(stmt.argument);
|
|
4594
|
+
if (!a || a.type !== "ObjectExpression") return null;
|
|
4595
|
+
return { statements, retObj: a };
|
|
4596
|
+
}
|
|
4597
|
+
if (stmt.type === "VariableDeclaration") statements.push(stmt);
|
|
4598
|
+
else return null;
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
return null;
|
|
4602
|
+
}
|
|
4603
|
+
function spreadFactoryCall(spread, resolve) {
|
|
4604
|
+
const call = spread.argument;
|
|
4605
|
+
if (!call || call.type !== "CallExpression") return null;
|
|
4606
|
+
if (!call.callee || call.callee.type !== "Identifier" || !call.callee.name) return null;
|
|
4607
|
+
const resolved = resolve(call.callee.name);
|
|
4608
|
+
return resolved ? { fn: resolved.fn, args: call.arguments ?? [], code: resolved.code } : null;
|
|
4609
|
+
}
|
|
4610
|
+
function factoryCallScope(fn, args, outer) {
|
|
4611
|
+
const s = new Map(outer);
|
|
4612
|
+
const params = fn.params ?? [];
|
|
4613
|
+
for (let i = 0; i < params.length; i++) {
|
|
4614
|
+
const pn = params[i];
|
|
4615
|
+
if (!pn || pn.type !== "Identifier" || !pn.name) continue;
|
|
4616
|
+
const arg = args[i];
|
|
4617
|
+
if (arg && arg.type === "Identifier" && arg.name && outer.has(arg.name)) {
|
|
4618
|
+
s.set(pn.name, outer.get(arg.name));
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
return s;
|
|
4622
|
+
}
|
|
4623
|
+
function propKey(prop) {
|
|
4624
|
+
if (prop.type !== "Property" || prop.computed || !prop.key) return null;
|
|
4625
|
+
if (prop.key.type === "Identifier") return prop.key.name ?? null;
|
|
4626
|
+
if (prop.key.type === "Literal") return String(prop.key.value);
|
|
4627
|
+
return null;
|
|
4628
|
+
}
|
|
4629
|
+
function collectRuleKeys(retObj, resolve, seen) {
|
|
4630
|
+
const out = [];
|
|
4631
|
+
for (const prop of retObj.properties) {
|
|
4632
|
+
if (prop.type === "SpreadElement") {
|
|
4633
|
+
const sf = spreadFactoryCall(prop, resolve);
|
|
4634
|
+
if (!sf || seen.has(sf.fn)) return null;
|
|
4635
|
+
const br = factoryBodyAndReturn(sf.fn);
|
|
4636
|
+
if (!br) return null;
|
|
4637
|
+
const nested = collectRuleKeys(br.retObj, resolve, new Set(seen).add(sf.fn));
|
|
4638
|
+
if (!nested) return null;
|
|
4639
|
+
out.push(...nested);
|
|
4640
|
+
continue;
|
|
4641
|
+
}
|
|
4642
|
+
const key = propKey(prop);
|
|
4643
|
+
if (!key) return null;
|
|
4644
|
+
out.push(key);
|
|
4645
|
+
}
|
|
4646
|
+
return out;
|
|
4647
|
+
}
|
|
4648
|
+
function flattenRuleEntries(retObj, scope, code, resolve, seen) {
|
|
4649
|
+
const out = [];
|
|
4650
|
+
for (const prop of retObj.properties) {
|
|
4651
|
+
if (prop.type === "SpreadElement") {
|
|
4652
|
+
const sf = spreadFactoryCall(prop, resolve);
|
|
4653
|
+
if (!sf || seen.has(sf.fn)) return null;
|
|
4654
|
+
const br = factoryBodyAndReturn(sf.fn);
|
|
4655
|
+
if (!br) return null;
|
|
4656
|
+
const fragScope = factoryCallScope(sf.fn, sf.args, scope);
|
|
4657
|
+
if (!evalBodyStatements(br.statements, fragScope, sf.code)) return null;
|
|
4658
|
+
const nested = flattenRuleEntries(br.retObj, fragScope, sf.code, resolve, new Set(seen).add(sf.fn));
|
|
4659
|
+
if (!nested) return null;
|
|
4660
|
+
out.push(...nested);
|
|
4661
|
+
continue;
|
|
4662
|
+
}
|
|
4663
|
+
const key = propKey(prop);
|
|
4664
|
+
if (!key) return null;
|
|
4665
|
+
out.push({ key, value: prop.value, scope, code });
|
|
4666
|
+
}
|
|
4667
|
+
return out;
|
|
4668
|
+
}
|
|
4669
|
+
function evalBodyStatements(statements, scope, code) {
|
|
4670
|
+
const bodyMfs = [];
|
|
4671
|
+
for (const stmt of statements) {
|
|
4672
|
+
for (const d of stmt.declarations) {
|
|
4673
|
+
const decl = d;
|
|
4674
|
+
if (!decl.init) return false;
|
|
4675
|
+
const id = decl.id;
|
|
4676
|
+
if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return false;
|
|
4677
|
+
const name = id.name;
|
|
4678
|
+
const before = bodyMfs.length;
|
|
4679
|
+
const val = anyValue(decl.init, scope, code, bodyMfs);
|
|
4680
|
+
if (val === null) return false;
|
|
4681
|
+
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
4682
|
+
if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
4683
|
+
else scope.set(name, val);
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4686
|
+
return true;
|
|
4687
|
+
}
|
|
4688
|
+
function evaluateParserFactory(factoryNode, scope, code, mapFnSources, resolveFactory = () => null) {
|
|
3609
4689
|
if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
|
|
3610
4690
|
const factory = factoryNode;
|
|
3611
4691
|
const params = factory.params;
|
|
@@ -3637,40 +4717,21 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
|
3637
4717
|
if (!returnExpr) return null;
|
|
3638
4718
|
const retObj = returnExpr.type === "ParenthesizedExpression" ? returnExpr.expression : returnExpr;
|
|
3639
4719
|
if (retObj.type !== "ObjectExpression") return null;
|
|
4720
|
+
const keys = collectRuleKeys(retObj, resolveFactory, /* @__PURE__ */ new Set());
|
|
4721
|
+
if (!keys) return null;
|
|
3640
4722
|
const ruleRefs = /* @__PURE__ */ new Map();
|
|
3641
|
-
for (const
|
|
3642
|
-
if (
|
|
3643
|
-
if (prop.computed) return null;
|
|
3644
|
-
const p = prop;
|
|
3645
|
-
const key = p.key.type === "Identifier" ? p.key.name : p.key.type === "Literal" ? String(p.key.value) : null;
|
|
3646
|
-
if (!key) return null;
|
|
3647
|
-
ruleRefs.set(key, ref());
|
|
4723
|
+
for (const key of keys) {
|
|
4724
|
+
if (!ruleRefs.has(key)) ruleRefs.set(key, ref());
|
|
3648
4725
|
}
|
|
3649
4726
|
const localScope = new Map(scope);
|
|
3650
4727
|
localScope.set(proxyName, Object.fromEntries(ruleRefs));
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
const name = id.name;
|
|
3659
|
-
const before = bodyMfs.length;
|
|
3660
|
-
const val = anyValue(decl.init, localScope, code, bodyMfs);
|
|
3661
|
-
if (val === null) return null;
|
|
3662
|
-
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
3663
|
-
if (isCombinator(val)) {
|
|
3664
|
-
localScope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
3665
|
-
} else {
|
|
3666
|
-
localScope.set(name, val);
|
|
3667
|
-
}
|
|
3668
|
-
}
|
|
3669
|
-
}
|
|
3670
|
-
for (const prop of retObj.properties) {
|
|
3671
|
-
const p = prop;
|
|
3672
|
-
const key = p.key.type === "Identifier" ? p.key.name : String(p.key.value);
|
|
3673
|
-
const val = anyValue(p.value, localScope, code, mapFnSources);
|
|
4728
|
+
if (!evalBodyStatements(statements, localScope, code)) return null;
|
|
4729
|
+
const entries = flattenRuleEntries(retObj, localScope, code, resolveFactory, /* @__PURE__ */ new Set());
|
|
4730
|
+
if (!entries) return null;
|
|
4731
|
+
const finalByKey = /* @__PURE__ */ new Map();
|
|
4732
|
+
for (const e of entries) finalByKey.set(e.key, e);
|
|
4733
|
+
for (const [key, e] of finalByKey) {
|
|
4734
|
+
const val = anyValue(e.value, e.scope, e.code, mapFnSources);
|
|
3674
4735
|
if (!isCombinator(val)) return null;
|
|
3675
4736
|
ruleRefs.get(key).define(val);
|
|
3676
4737
|
}
|
|
@@ -3728,6 +4789,40 @@ function referencesAny(node2, names, scope) {
|
|
|
3728
4789
|
|
|
3729
4790
|
// src/plugin/index.ts
|
|
3730
4791
|
var PARSEMAN_MODULE = "parseman";
|
|
4792
|
+
var _fragmentResolver = null;
|
|
4793
|
+
function getFragmentResolver() {
|
|
4794
|
+
return _fragmentResolver ??= new import_oxc_resolver.ResolverFactory({
|
|
4795
|
+
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"],
|
|
4796
|
+
// TS/NodeNext style: a `./x.js` import may point at `./x.ts` source.
|
|
4797
|
+
extensionAlias: {
|
|
4798
|
+
".js": [".ts", ".tsx", ".js", ".jsx"],
|
|
4799
|
+
".mjs": [".mts", ".mjs"],
|
|
4800
|
+
".cjs": [".cts", ".cjs"]
|
|
4801
|
+
},
|
|
4802
|
+
conditionNames: ["source", "development", "import", "require", "default"],
|
|
4803
|
+
mainFields: ["source", "module", "main"]
|
|
4804
|
+
});
|
|
4805
|
+
}
|
|
4806
|
+
var _moduleParseCache = /* @__PURE__ */ new Map();
|
|
4807
|
+
function parseModuleCached(filePath) {
|
|
4808
|
+
let mtimeMs;
|
|
4809
|
+
try {
|
|
4810
|
+
mtimeMs = fs.statSync(filePath).mtimeMs;
|
|
4811
|
+
} catch {
|
|
4812
|
+
return null;
|
|
4813
|
+
}
|
|
4814
|
+
const hit = _moduleParseCache.get(filePath);
|
|
4815
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
|
|
4816
|
+
let parsed = null;
|
|
4817
|
+
try {
|
|
4818
|
+
const src = fs.readFileSync(filePath, "utf8");
|
|
4819
|
+
const r = (0, import_oxc_parser.parseSync)(filePath, src);
|
|
4820
|
+
if (r.errors.length === 0) parsed = { body: r.program.body, src };
|
|
4821
|
+
} catch {
|
|
4822
|
+
}
|
|
4823
|
+
_moduleParseCache.set(filePath, { mtimeMs, parsed });
|
|
4824
|
+
return parsed;
|
|
4825
|
+
}
|
|
3731
4826
|
var plugin_default = (0, import_unplugin.createUnplugin)((opts = {}) => ({
|
|
3732
4827
|
name: "parseman",
|
|
3733
4828
|
// Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
|
|
@@ -3763,15 +4858,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3763
4858
|
const body = result.program.body;
|
|
3764
4859
|
const macroImports = [];
|
|
3765
4860
|
const allNames = /* @__PURE__ */ new Set();
|
|
4861
|
+
const importBindings = /* @__PURE__ */ new Map();
|
|
3766
4862
|
for (const stmt of body) {
|
|
3767
4863
|
if (stmt.type !== "ImportDeclaration") continue;
|
|
3768
4864
|
const s = stmt;
|
|
3769
|
-
|
|
3770
|
-
const isMacro = s.attributes.some((a) => {
|
|
4865
|
+
const isMacro = moduleAliases.has(s.source.value) && s.attributes.some((a) => {
|
|
3771
4866
|
const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
|
|
3772
4867
|
return key === "type" && a.value.value === "macro";
|
|
3773
4868
|
});
|
|
3774
|
-
if (!isMacro)
|
|
4869
|
+
if (!isMacro) {
|
|
4870
|
+
for (const spec of s.specifiers) {
|
|
4871
|
+
if (spec.type === "ImportSpecifier") {
|
|
4872
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
4873
|
+
importBindings.set(spec.local.name, { source: s.source.value, imported });
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
continue;
|
|
4877
|
+
}
|
|
3775
4878
|
const names = /* @__PURE__ */ new Set();
|
|
3776
4879
|
for (const spec of s.specifiers) {
|
|
3777
4880
|
if (spec.type === "ImportSpecifier") names.add(spec.local.name);
|
|
@@ -3781,6 +4884,63 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3781
4884
|
}
|
|
3782
4885
|
if (macroImports.length === 0) return null;
|
|
3783
4886
|
const scope = /* @__PURE__ */ new Map();
|
|
4887
|
+
const factories = /* @__PURE__ */ new Map();
|
|
4888
|
+
const resolver = getFragmentResolver();
|
|
4889
|
+
const importedFactoryCache = /* @__PURE__ */ new Map();
|
|
4890
|
+
const OUTPUT_DIR = /([\\/])(dist|lib|build|out|output|compiled|cjs|esm|es)([\\/])/;
|
|
4891
|
+
const resolveSourcePath = (spec) => {
|
|
4892
|
+
let resolved = null;
|
|
4893
|
+
try {
|
|
4894
|
+
resolved = resolver.resolveFileSync(id, spec).path ?? null;
|
|
4895
|
+
} catch {
|
|
4896
|
+
}
|
|
4897
|
+
if (!resolved) return null;
|
|
4898
|
+
if (OUTPUT_DIR.test(resolved)) {
|
|
4899
|
+
const srcCandidate = resolved.replace(OUTPUT_DIR, "$1src$3").replace(/\.(c|m)?js$/, ".ts");
|
|
4900
|
+
try {
|
|
4901
|
+
if (fs.statSync(srcCandidate).isFile()) return srcCandidate;
|
|
4902
|
+
} catch {
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
return resolved;
|
|
4906
|
+
};
|
|
4907
|
+
const resolveImportedFactory = (name) => {
|
|
4908
|
+
if (importedFactoryCache.has(name)) return importedFactoryCache.get(name);
|
|
4909
|
+
let result2 = null;
|
|
4910
|
+
const binding = importBindings.get(name);
|
|
4911
|
+
if (binding) {
|
|
4912
|
+
const filePath = resolveSourcePath(binding.source);
|
|
4913
|
+
const mod = filePath ? parseModuleCached(filePath) : null;
|
|
4914
|
+
if (mod) {
|
|
4915
|
+
for (const st of mod.body) {
|
|
4916
|
+
if (st.type !== "ExportNamedDeclaration" || !st.declaration) continue;
|
|
4917
|
+
const decl = st.declaration;
|
|
4918
|
+
if (decl.type === "VariableDeclaration") {
|
|
4919
|
+
for (const d of decl.declarations ?? []) {
|
|
4920
|
+
if (d.id?.type === "Identifier" && d.id.name === binding.imported && d.init) {
|
|
4921
|
+
const fac = asFragmentFactory(d.init);
|
|
4922
|
+
if (fac) {
|
|
4923
|
+
result2 = { fn: fac, code: mod.src };
|
|
4924
|
+
break;
|
|
4925
|
+
}
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
} else if (decl.type === "FunctionDeclaration" && decl.id?.type === "Identifier" && decl.id.name === binding.imported) {
|
|
4929
|
+
const fac = asFragmentFactory(decl);
|
|
4930
|
+
if (fac) result2 = { fn: fac, code: mod.src };
|
|
4931
|
+
}
|
|
4932
|
+
if (result2) break;
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
importedFactoryCache.set(name, result2);
|
|
4937
|
+
return result2;
|
|
4938
|
+
};
|
|
4939
|
+
const resolveFactory = (n) => {
|
|
4940
|
+
const local = factories.get(n);
|
|
4941
|
+
if (local) return { fn: local, code };
|
|
4942
|
+
return resolveImportedFactory(n);
|
|
4943
|
+
};
|
|
3784
4944
|
const replacements = [];
|
|
3785
4945
|
const warnings = [];
|
|
3786
4946
|
let anyUnresolved = false;
|
|
@@ -3801,7 +4961,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3801
4961
|
warn(init.start, `${label2}: rules() needs a factory argument`);
|
|
3802
4962
|
return null;
|
|
3803
4963
|
}
|
|
3804
|
-
const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
|
|
4964
|
+
const ruleMap = evaluateParserFactory(factoryArg, scope, code, [], resolveFactory);
|
|
3805
4965
|
if (!ruleMap) {
|
|
3806
4966
|
warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
|
|
3807
4967
|
return null;
|
|
@@ -3899,6 +5059,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3899
5059
|
});
|
|
3900
5060
|
continue;
|
|
3901
5061
|
}
|
|
5062
|
+
const frag = asFragmentFactory(init);
|
|
5063
|
+
if (frag) {
|
|
5064
|
+
factories.set(varName, frag);
|
|
5065
|
+
continue;
|
|
5066
|
+
}
|
|
3902
5067
|
const mapFnSources = [];
|
|
3903
5068
|
const parser2 = evaluateExpr(init, scope, code, mapFnSources);
|
|
3904
5069
|
if (parser2 === null) {
|
|
@@ -3908,6 +5073,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3908
5073
|
scope.set(varName, wordFactory);
|
|
3909
5074
|
continue;
|
|
3910
5075
|
}
|
|
5076
|
+
const combiArray = evaluateCombinatorArray(init, scope, code);
|
|
5077
|
+
if (combiArray) {
|
|
5078
|
+
;
|
|
5079
|
+
scope.set(varName, combiArray);
|
|
5080
|
+
continue;
|
|
5081
|
+
}
|
|
3911
5082
|
warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
|
|
3912
5083
|
continue;
|
|
3913
5084
|
}
|