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.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/plugin/index.ts
|
|
2
|
+
import * as fs from "node:fs";
|
|
2
3
|
import { createUnplugin } from "unplugin";
|
|
3
4
|
import { parseSync } from "oxc-parser";
|
|
5
|
+
import { ResolverFactory } from "oxc-resolver";
|
|
4
6
|
import MagicString from "magic-string";
|
|
5
7
|
|
|
6
8
|
// src/combinators/first-set.ts
|
|
@@ -328,15 +330,15 @@ function extractFirstSet(node2) {
|
|
|
328
330
|
case "Alternative": {
|
|
329
331
|
const exprs = node2.expressions;
|
|
330
332
|
if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
|
|
331
|
-
let
|
|
333
|
+
let fs2 = empty();
|
|
332
334
|
let canNL = false;
|
|
333
335
|
for (const expr of exprs) {
|
|
334
336
|
const r = extractFirstSet(expr);
|
|
335
|
-
|
|
337
|
+
fs2 = union(fs2, r.firstSet);
|
|
336
338
|
canNL = canNL || r.canMatchNewline;
|
|
337
339
|
if (!canBeEmpty(expr)) break;
|
|
338
340
|
}
|
|
339
|
-
return { firstSet:
|
|
341
|
+
return { firstSet: fs2, canMatchNewline: canNL };
|
|
340
342
|
}
|
|
341
343
|
case "Char": {
|
|
342
344
|
const kind = node2.kind;
|
|
@@ -363,22 +365,22 @@ function extractFirstSet(node2) {
|
|
|
363
365
|
}
|
|
364
366
|
case "CharacterClass": {
|
|
365
367
|
const expressions = node2.expressions;
|
|
366
|
-
let
|
|
368
|
+
let fs2 = empty();
|
|
367
369
|
let canNL = false;
|
|
368
370
|
for (const expr of expressions) {
|
|
369
371
|
if (expr.type === "ClassRange") {
|
|
370
372
|
const from = expr.from.codePoint;
|
|
371
373
|
const to = expr.to.codePoint;
|
|
372
|
-
|
|
374
|
+
fs2 = union(fs2, fromRange(from, to));
|
|
373
375
|
if (from <= 10 && 10 <= to) canNL = true;
|
|
374
376
|
} else if (expr.type === "Char") {
|
|
375
377
|
const r = extractFirstSet(expr);
|
|
376
|
-
|
|
378
|
+
fs2 = union(fs2, r.firstSet);
|
|
377
379
|
canNL = canNL || r.canMatchNewline;
|
|
378
380
|
}
|
|
379
381
|
}
|
|
380
382
|
if (node2.negative) return { firstSet: any(), canMatchNewline: true };
|
|
381
|
-
return { firstSet:
|
|
383
|
+
return { firstSet: fs2, canMatchNewline: canNL };
|
|
382
384
|
}
|
|
383
385
|
case "Repetition": {
|
|
384
386
|
return extractFirstSet(node2.expression);
|
|
@@ -687,10 +689,10 @@ function codesToFirstSet(codes) {
|
|
|
687
689
|
ranges.push({ lo, hi });
|
|
688
690
|
return { kind: "ranges", ranges };
|
|
689
691
|
}
|
|
690
|
-
function inFirstSet(code,
|
|
691
|
-
if (
|
|
692
|
-
if (
|
|
693
|
-
for (const r of
|
|
692
|
+
function inFirstSet(code, fs2) {
|
|
693
|
+
if (fs2.kind === "any") return true;
|
|
694
|
+
if (fs2.kind === "empty") return false;
|
|
695
|
+
for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
694
696
|
return false;
|
|
695
697
|
}
|
|
696
698
|
function areDisjoint(sets) {
|
|
@@ -778,12 +780,15 @@ function scanFastWsCommentsChunks(input, cur, wsKind, commentKind) {
|
|
|
778
780
|
continue;
|
|
779
781
|
}
|
|
780
782
|
if (c === 47 && input.charCodeAt(pos + 1) === 42) {
|
|
781
|
-
const start = pos;
|
|
782
783
|
let j = pos + 2;
|
|
783
784
|
while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
785
|
+
if (j + 1 < input.length && input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47) {
|
|
786
|
+
const start = pos;
|
|
787
|
+
pos = j + 2;
|
|
788
|
+
chunks.push({ start, end: pos, kindIndex: commentKind });
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
break;
|
|
787
792
|
}
|
|
788
793
|
break;
|
|
789
794
|
}
|
|
@@ -1280,38 +1285,140 @@ function noTrivia(root) {
|
|
|
1280
1285
|
return parser({ trivia: null }, root);
|
|
1281
1286
|
}
|
|
1282
1287
|
|
|
1288
|
+
// src/combinators/expect.ts
|
|
1289
|
+
function staticExpected(c) {
|
|
1290
|
+
const def = c._def;
|
|
1291
|
+
switch (def.tag) {
|
|
1292
|
+
case "literal":
|
|
1293
|
+
return [JSON.stringify(def.value)];
|
|
1294
|
+
case "regex":
|
|
1295
|
+
return [`/${def.source}/`];
|
|
1296
|
+
case "keywords":
|
|
1297
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
1298
|
+
case "label":
|
|
1299
|
+
return [def.label];
|
|
1300
|
+
case "choice":
|
|
1301
|
+
return def.parsers.flatMap(staticExpected);
|
|
1302
|
+
case "sequence":
|
|
1303
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
1304
|
+
case "node":
|
|
1305
|
+
case "grammar":
|
|
1306
|
+
case "trivia":
|
|
1307
|
+
case "optional":
|
|
1308
|
+
case "many":
|
|
1309
|
+
case "oneOrMore":
|
|
1310
|
+
case "transform":
|
|
1311
|
+
case "not":
|
|
1312
|
+
return staticExpected(def.parser);
|
|
1313
|
+
case "lazy":
|
|
1314
|
+
return staticExpected(def.thunk());
|
|
1315
|
+
default:
|
|
1316
|
+
return [];
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
function expect(combinator, label2) {
|
|
1320
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
1321
|
+
const meta = {
|
|
1322
|
+
firstSet: combinator._meta.firstSet,
|
|
1323
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1324
|
+
isTrivia: false
|
|
1325
|
+
};
|
|
1326
|
+
return {
|
|
1327
|
+
_tag: "expect",
|
|
1328
|
+
_meta: meta,
|
|
1329
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
1330
|
+
parse(input, pos, ctx) {
|
|
1331
|
+
const result = combinator.parse(input, pos, ctx);
|
|
1332
|
+
if (result.ok) return result;
|
|
1333
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
1334
|
+
ctx._errors?.push(error);
|
|
1335
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1283
1340
|
// src/compiler/scannable-run.ts
|
|
1341
|
+
var BACKSLASH = 92;
|
|
1284
1342
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1285
1343
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1344
|
+
var SPACE_RANGES = [
|
|
1345
|
+
[9, 13],
|
|
1346
|
+
[32, 32],
|
|
1347
|
+
[160, 160],
|
|
1348
|
+
[5760, 5760],
|
|
1349
|
+
[8192, 8202],
|
|
1350
|
+
[8232, 8232],
|
|
1351
|
+
[8233, 8233],
|
|
1352
|
+
[8239, 8239],
|
|
1353
|
+
[8287, 8287],
|
|
1354
|
+
[12288, 12288],
|
|
1355
|
+
[65279, 65279]
|
|
1356
|
+
];
|
|
1357
|
+
function shorthandRanges(ch) {
|
|
1358
|
+
if (ch === "d") return [[48, 57]];
|
|
1359
|
+
if (ch === "s") return SPACE_RANGES;
|
|
1360
|
+
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1361
|
+
}
|
|
1362
|
+
function readUnicodeEscape(body, i) {
|
|
1363
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
1364
|
+
const hex = body.slice(i + 2, i + 6);
|
|
1365
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
1366
|
+
return { cp: Number.parseInt(hex, 16), next: i + 6 };
|
|
1367
|
+
}
|
|
1286
1368
|
function parseClassRanges(body) {
|
|
1287
1369
|
const ranges = [];
|
|
1288
1370
|
let i = 0;
|
|
1289
|
-
const
|
|
1371
|
+
const readAtom = () => {
|
|
1290
1372
|
const ch = body[i];
|
|
1291
1373
|
if (ch === void 0) return null;
|
|
1292
1374
|
if (ch === "\\") {
|
|
1375
|
+
const uni = readUnicodeEscape(body, i);
|
|
1376
|
+
if (uni) {
|
|
1377
|
+
i = uni.next;
|
|
1378
|
+
return { cp: uni.cp };
|
|
1379
|
+
}
|
|
1293
1380
|
const e = body[i + 1];
|
|
1294
1381
|
if (e === void 0) return null;
|
|
1295
1382
|
i += 2;
|
|
1296
|
-
|
|
1383
|
+
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1384
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
1385
|
+
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1386
|
+
return { cp: e.codePointAt(0) };
|
|
1297
1387
|
}
|
|
1298
1388
|
i += ch.length;
|
|
1299
|
-
return ch.codePointAt(0);
|
|
1389
|
+
return { cp: ch.codePointAt(0) };
|
|
1300
1390
|
};
|
|
1301
1391
|
while (i < body.length) {
|
|
1302
|
-
const lo =
|
|
1392
|
+
const lo = readAtom();
|
|
1303
1393
|
if (lo === null) return null;
|
|
1394
|
+
if ("set" in lo) {
|
|
1395
|
+
ranges.push(...lo.set);
|
|
1396
|
+
continue;
|
|
1397
|
+
}
|
|
1304
1398
|
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1305
1399
|
i += 1;
|
|
1306
|
-
const hi =
|
|
1307
|
-
if (hi === null) return null;
|
|
1308
|
-
ranges.push([lo, hi]);
|
|
1400
|
+
const hi = readAtom();
|
|
1401
|
+
if (hi === null || "set" in hi) return null;
|
|
1402
|
+
ranges.push([lo.cp, hi.cp]);
|
|
1309
1403
|
} else {
|
|
1310
|
-
ranges.push([lo, lo]);
|
|
1404
|
+
ranges.push([lo.cp, lo.cp]);
|
|
1311
1405
|
}
|
|
1312
1406
|
}
|
|
1313
1407
|
return ranges.length ? ranges : null;
|
|
1314
1408
|
}
|
|
1409
|
+
function classToRanges(cls) {
|
|
1410
|
+
if (cls === "\\d") return shorthandRanges("d");
|
|
1411
|
+
if (cls === "\\w") return shorthandRanges("w");
|
|
1412
|
+
if (cls === "\\s") return shorthandRanges("s");
|
|
1413
|
+
const body = cls.slice(1, -1);
|
|
1414
|
+
if (body.startsWith("^")) return null;
|
|
1415
|
+
return parseClassRanges(body);
|
|
1416
|
+
}
|
|
1417
|
+
var foldEq = (cVar, cp) => {
|
|
1418
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
|
|
1419
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
|
|
1420
|
+
return `${cVar} === ${cp}`;
|
|
1421
|
+
};
|
|
1315
1422
|
function literalCodePoints(frag) {
|
|
1316
1423
|
const out = [];
|
|
1317
1424
|
let i = 0;
|
|
@@ -1330,19 +1437,482 @@ function literalCodePoints(frag) {
|
|
|
1330
1437
|
}
|
|
1331
1438
|
return out.length ? out : null;
|
|
1332
1439
|
}
|
|
1440
|
+
function parseStringShape(source) {
|
|
1441
|
+
const q = source[0];
|
|
1442
|
+
if (q === void 0 || q === "\\" || META.has(q)) return null;
|
|
1443
|
+
if (source.length < 3 || source[source.length - 1] !== q) return null;
|
|
1444
|
+
const inner = source.slice(1, -1);
|
|
1445
|
+
const CLS = String.raw`\[\^((?:\\.|[^\]])+)\]`;
|
|
1446
|
+
const ESC = String.raw`\\\\(\.|\[\\s\\S\]|\[\\S\\s\])`;
|
|
1447
|
+
let body;
|
|
1448
|
+
let esc;
|
|
1449
|
+
let m = new RegExp(`^\\((?:\\?:)?${CLS}\\|${ESC}\\)\\*$`).exec(inner);
|
|
1450
|
+
if (m) {
|
|
1451
|
+
body = m[1];
|
|
1452
|
+
esc = m[2];
|
|
1453
|
+
} else {
|
|
1454
|
+
m = new RegExp(`^\\((?:\\?:)?${ESC}\\|${CLS}\\)\\*$`).exec(inner);
|
|
1455
|
+
if (m) {
|
|
1456
|
+
esc = m[1];
|
|
1457
|
+
body = m[2];
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
if (body === void 0 || esc === void 0) return null;
|
|
1461
|
+
const excluded = parseClassRanges(body);
|
|
1462
|
+
if (!excluded) return null;
|
|
1463
|
+
const qcp = q.codePointAt(0);
|
|
1464
|
+
const inSet = (cp) => excluded.some(([lo, hi]) => cp >= lo && cp <= hi);
|
|
1465
|
+
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1466
|
+
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1467
|
+
}
|
|
1468
|
+
function parseSeqParts(source) {
|
|
1469
|
+
const parts = [];
|
|
1470
|
+
let lit = [];
|
|
1471
|
+
const flush = () => {
|
|
1472
|
+
if (lit.length) {
|
|
1473
|
+
parts.push({ part: "lit", cps: lit, optional: false });
|
|
1474
|
+
lit = [];
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
const runFrom = (ranges, negated, q) => {
|
|
1478
|
+
if (q === "+") {
|
|
1479
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
|
|
1480
|
+
return 1;
|
|
1481
|
+
}
|
|
1482
|
+
if (q === "*") {
|
|
1483
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
|
|
1484
|
+
return 1;
|
|
1485
|
+
}
|
|
1486
|
+
if (q === "?") {
|
|
1487
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
|
|
1488
|
+
return 1;
|
|
1489
|
+
}
|
|
1490
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
|
|
1491
|
+
return 0;
|
|
1492
|
+
};
|
|
1493
|
+
let i = 0;
|
|
1494
|
+
while (i < source.length) {
|
|
1495
|
+
const ch = source[i];
|
|
1496
|
+
if (ch === "[") {
|
|
1497
|
+
let k = i + 1;
|
|
1498
|
+
const neg = source[k] === "^";
|
|
1499
|
+
if (neg) k++;
|
|
1500
|
+
let body = "";
|
|
1501
|
+
while (k < source.length && source[k] !== "]") {
|
|
1502
|
+
if (source[k] === "\\") {
|
|
1503
|
+
body += source[k] + (source[k + 1] ?? "");
|
|
1504
|
+
k += 2;
|
|
1505
|
+
} else {
|
|
1506
|
+
body += source[k];
|
|
1507
|
+
k++;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
if (source[k] !== "]") return null;
|
|
1511
|
+
const ranges = parseClassRanges(body);
|
|
1512
|
+
if (!ranges) return null;
|
|
1513
|
+
flush();
|
|
1514
|
+
i = k + 1;
|
|
1515
|
+
i += runFrom(ranges, neg, source[i]);
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
if (ch === "\\") {
|
|
1519
|
+
const e = source[i + 1];
|
|
1520
|
+
if (e === "d" || e === "w" || e === "s") {
|
|
1521
|
+
flush();
|
|
1522
|
+
i += 2;
|
|
1523
|
+
i += runFrom(shorthandRanges(e), false, source[i]);
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
const uni = readUnicodeEscape(source, i);
|
|
1527
|
+
let cp2;
|
|
1528
|
+
if (uni) {
|
|
1529
|
+
cp2 = uni.cp;
|
|
1530
|
+
i = uni.next;
|
|
1531
|
+
} else if (e === void 0) return null;
|
|
1532
|
+
else if (e in CLASS_ESCAPES) {
|
|
1533
|
+
cp2 = CLASS_ESCAPES[e];
|
|
1534
|
+
i += 2;
|
|
1535
|
+
} else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1536
|
+
else {
|
|
1537
|
+
cp2 = e.codePointAt(0);
|
|
1538
|
+
i += 2;
|
|
1539
|
+
}
|
|
1540
|
+
if (source[i] === "?") {
|
|
1541
|
+
flush();
|
|
1542
|
+
parts.push({ part: "lit", cps: [cp2], optional: true });
|
|
1543
|
+
i++;
|
|
1544
|
+
} else lit.push(cp2);
|
|
1545
|
+
continue;
|
|
1546
|
+
}
|
|
1547
|
+
if (ch === "(") {
|
|
1548
|
+
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
1549
|
+
let k = i + 3;
|
|
1550
|
+
let depth = 1;
|
|
1551
|
+
while (k < source.length && depth > 0) {
|
|
1552
|
+
const c2 = source[k];
|
|
1553
|
+
if (c2 === "\\") {
|
|
1554
|
+
k += 2;
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
if (c2 === "[") {
|
|
1558
|
+
let j = k + 1;
|
|
1559
|
+
if (source[j] === "^") j++;
|
|
1560
|
+
while (j < source.length && source[j] !== "]") {
|
|
1561
|
+
if (source[j] === "\\") j += 2;
|
|
1562
|
+
else j++;
|
|
1563
|
+
}
|
|
1564
|
+
if (source[j] !== "]") return null;
|
|
1565
|
+
k = j + 1;
|
|
1566
|
+
continue;
|
|
1567
|
+
}
|
|
1568
|
+
if (c2 === "(") depth++;
|
|
1569
|
+
else if (c2 === ")") depth--;
|
|
1570
|
+
k++;
|
|
1571
|
+
}
|
|
1572
|
+
if (depth !== 0) return null;
|
|
1573
|
+
const body = source.slice(i + 3, k - 1);
|
|
1574
|
+
const inner = parseScanShape(body);
|
|
1575
|
+
if (!inner || !groupInnerSafe(inner)) return null;
|
|
1576
|
+
const q = source[k];
|
|
1577
|
+
if (q === "{") return null;
|
|
1578
|
+
flush();
|
|
1579
|
+
if (q === "+") {
|
|
1580
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1581
|
+
parts.push({ part: "group", inner, min: 1, unbounded: true });
|
|
1582
|
+
i = k + 1;
|
|
1583
|
+
} else if (q === "*") {
|
|
1584
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1585
|
+
parts.push({ part: "group", inner, min: 0, unbounded: true });
|
|
1586
|
+
i = k + 1;
|
|
1587
|
+
} else if (q === "?") {
|
|
1588
|
+
parts.push({ part: "group", inner, min: 0, unbounded: false });
|
|
1589
|
+
i = k + 1;
|
|
1590
|
+
} else {
|
|
1591
|
+
parts.push({ part: "group", inner, min: 1, unbounded: false });
|
|
1592
|
+
i = k;
|
|
1593
|
+
}
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
if (META.has(ch)) return null;
|
|
1597
|
+
const cp = ch.codePointAt(0);
|
|
1598
|
+
i += ch.length;
|
|
1599
|
+
if (source[i] === "?") {
|
|
1600
|
+
flush();
|
|
1601
|
+
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
1602
|
+
i++;
|
|
1603
|
+
} else lit.push(cp);
|
|
1604
|
+
}
|
|
1605
|
+
flush();
|
|
1606
|
+
return parts.length ? parts : null;
|
|
1607
|
+
}
|
|
1608
|
+
function seqIsUnambiguous(parts) {
|
|
1609
|
+
let hasMandatory = false;
|
|
1610
|
+
for (let k = 0; k < parts.length; k++) {
|
|
1611
|
+
const p = parts[k];
|
|
1612
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1613
|
+
if (mandatory) hasMandatory = true;
|
|
1614
|
+
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1615
|
+
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1616
|
+
if (skippableSingle || isUnboundedRepeat) {
|
|
1617
|
+
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
1618
|
+
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
return hasMandatory;
|
|
1622
|
+
}
|
|
1623
|
+
function rangesOverlap(a, b) {
|
|
1624
|
+
return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
|
|
1625
|
+
}
|
|
1626
|
+
function rangesSubset(sub, sup) {
|
|
1627
|
+
return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
|
|
1628
|
+
}
|
|
1629
|
+
function parseClassOperand(body) {
|
|
1630
|
+
if (body === "\\d" || body === "\\w" || body === "\\s") {
|
|
1631
|
+
return { ranges: shorthandRanges(body[1]), negated: false };
|
|
1632
|
+
}
|
|
1633
|
+
if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
|
|
1634
|
+
let inner = body.slice(1, -1);
|
|
1635
|
+
const negated = inner.startsWith("^");
|
|
1636
|
+
if (negated) inner = inner.slice(1);
|
|
1637
|
+
const ranges = parseClassRanges(inner);
|
|
1638
|
+
return ranges ? { ranges, negated } : null;
|
|
1639
|
+
}
|
|
1640
|
+
const cps = literalCodePoints(body);
|
|
1641
|
+
if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
|
|
1642
|
+
return null;
|
|
1643
|
+
}
|
|
1644
|
+
function classSubset(t, u) {
|
|
1645
|
+
if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1646
|
+
if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1647
|
+
if (t.negated && !u.negated) return false;
|
|
1648
|
+
return rangesSubset(u.ranges, t.ranges);
|
|
1649
|
+
}
|
|
1650
|
+
function classDisjoint(t, u) {
|
|
1651
|
+
if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1652
|
+
if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1653
|
+
if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
|
|
1654
|
+
return false;
|
|
1655
|
+
}
|
|
1656
|
+
function firstSetsDisjoint(a, b) {
|
|
1657
|
+
if (a === "any" || b === "any") return false;
|
|
1658
|
+
return classDisjoint(a, b);
|
|
1659
|
+
}
|
|
1660
|
+
function trailingBacktrackClass(shape) {
|
|
1661
|
+
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1662
|
+
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
1663
|
+
if (shape.kind === "litFold") return null;
|
|
1664
|
+
if (shape.kind === "seq") {
|
|
1665
|
+
const last = shape.parts[shape.parts.length - 1];
|
|
1666
|
+
if (last.part === "lit") return null;
|
|
1667
|
+
if (last.part === "run") {
|
|
1668
|
+
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1669
|
+
return null;
|
|
1670
|
+
}
|
|
1671
|
+
const innerWiggle = trailingBacktrackClass(last.inner);
|
|
1672
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1673
|
+
const requiredOnce = !last.unbounded && last.min === 1;
|
|
1674
|
+
if (requiredOnce) return innerWiggle;
|
|
1675
|
+
const dropExposed = shapeFirstAccept(last.inner);
|
|
1676
|
+
if (dropExposed === "any") return "unsupported";
|
|
1677
|
+
if (innerWiggle === null) return dropExposed;
|
|
1678
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1679
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1680
|
+
}
|
|
1681
|
+
if (shape.kind === "alt") {
|
|
1682
|
+
if (!shape.disjoint) return "unsupported";
|
|
1683
|
+
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1684
|
+
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1685
|
+
const concrete = classes.filter((c) => c !== null);
|
|
1686
|
+
if (concrete.length === 0) return null;
|
|
1687
|
+
const unioned = unionCharSets(concrete);
|
|
1688
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1689
|
+
}
|
|
1690
|
+
return "unsupported";
|
|
1691
|
+
}
|
|
1692
|
+
function lookaheadUnambiguous(inner, operand, negative) {
|
|
1693
|
+
const tail = trailingBacktrackClass(inner);
|
|
1694
|
+
if (tail === "unsupported") return false;
|
|
1695
|
+
if (tail === null) return true;
|
|
1696
|
+
return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
|
|
1697
|
+
}
|
|
1698
|
+
function stripTrailingLookahead(source) {
|
|
1699
|
+
if (!source.endsWith(")")) return null;
|
|
1700
|
+
for (const negative of [true, false]) {
|
|
1701
|
+
const lead = negative ? "(?!" : "(?=";
|
|
1702
|
+
const idx = source.lastIndexOf(lead);
|
|
1703
|
+
if (idx === -1) continue;
|
|
1704
|
+
const body = source.slice(idx + lead.length, -1);
|
|
1705
|
+
const operand = body.length ? parseClassOperand(body) : null;
|
|
1706
|
+
if (!operand) continue;
|
|
1707
|
+
return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
|
|
1708
|
+
}
|
|
1709
|
+
return null;
|
|
1710
|
+
}
|
|
1711
|
+
function unwrapOuterGroup(source) {
|
|
1712
|
+
while (source.startsWith("(?:")) {
|
|
1713
|
+
let depth = 1;
|
|
1714
|
+
let i = 3;
|
|
1715
|
+
let ok = true;
|
|
1716
|
+
while (i < source.length && depth > 0) {
|
|
1717
|
+
const ch = source[i];
|
|
1718
|
+
if (ch === "\\") {
|
|
1719
|
+
i += 2;
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
if (ch === "[") {
|
|
1723
|
+
let j = i + 1;
|
|
1724
|
+
if (source[j] === "^") j++;
|
|
1725
|
+
while (j < source.length && source[j] !== "]") {
|
|
1726
|
+
if (source[j] === "\\") j += 2;
|
|
1727
|
+
else j++;
|
|
1728
|
+
}
|
|
1729
|
+
if (source[j] !== "]") {
|
|
1730
|
+
ok = false;
|
|
1731
|
+
break;
|
|
1732
|
+
}
|
|
1733
|
+
i = j + 1;
|
|
1734
|
+
continue;
|
|
1735
|
+
}
|
|
1736
|
+
if (ch === "(") depth++;
|
|
1737
|
+
else if (ch === ")") depth--;
|
|
1738
|
+
i++;
|
|
1739
|
+
}
|
|
1740
|
+
if (!ok || depth !== 0 || i !== source.length) break;
|
|
1741
|
+
source = source.slice(3, -1);
|
|
1742
|
+
}
|
|
1743
|
+
return source;
|
|
1744
|
+
}
|
|
1745
|
+
function splitTopLevelAlternation(source) {
|
|
1746
|
+
const body = unwrapOuterGroup(source);
|
|
1747
|
+
const parts = [];
|
|
1748
|
+
let depth = 0;
|
|
1749
|
+
let last = 0;
|
|
1750
|
+
let i = 0;
|
|
1751
|
+
while (i < body.length) {
|
|
1752
|
+
const ch = body[i];
|
|
1753
|
+
if (ch === "\\") {
|
|
1754
|
+
i += 2;
|
|
1755
|
+
continue;
|
|
1756
|
+
}
|
|
1757
|
+
if (ch === "[") {
|
|
1758
|
+
let j = i + 1;
|
|
1759
|
+
if (body[j] === "^") j++;
|
|
1760
|
+
while (j < body.length && body[j] !== "]") {
|
|
1761
|
+
if (body[j] === "\\") j += 2;
|
|
1762
|
+
else j++;
|
|
1763
|
+
}
|
|
1764
|
+
if (body[j] !== "]") return null;
|
|
1765
|
+
i = j + 1;
|
|
1766
|
+
continue;
|
|
1767
|
+
}
|
|
1768
|
+
if (ch === "(") {
|
|
1769
|
+
depth++;
|
|
1770
|
+
i++;
|
|
1771
|
+
continue;
|
|
1772
|
+
}
|
|
1773
|
+
if (ch === ")") {
|
|
1774
|
+
depth--;
|
|
1775
|
+
if (depth < 0) return null;
|
|
1776
|
+
i++;
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
1779
|
+
if (ch === "|" && depth === 0) {
|
|
1780
|
+
parts.push(body.slice(last, i));
|
|
1781
|
+
last = i + 1;
|
|
1782
|
+
i++;
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
i++;
|
|
1786
|
+
}
|
|
1787
|
+
if (depth !== 0) return null;
|
|
1788
|
+
parts.push(body.slice(last));
|
|
1789
|
+
if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
|
|
1790
|
+
return parts;
|
|
1791
|
+
}
|
|
1792
|
+
function shapeCanBeEmpty(shape) {
|
|
1793
|
+
if (shape.kind === "chars") return !shape.minOne;
|
|
1794
|
+
if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
|
|
1795
|
+
if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
|
|
1796
|
+
return false;
|
|
1797
|
+
}
|
|
1798
|
+
function unionCharSets(sets) {
|
|
1799
|
+
if (sets.some((s) => s === "any")) return "any";
|
|
1800
|
+
const concrete = sets;
|
|
1801
|
+
if (concrete.length === 1) return concrete[0];
|
|
1802
|
+
if (concrete.some((s) => s.negated)) return "any";
|
|
1803
|
+
return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
|
|
1804
|
+
}
|
|
1805
|
+
function partFirstAccept(part) {
|
|
1806
|
+
if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
|
|
1807
|
+
if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
|
|
1808
|
+
return shapeFirstAccept(part.inner);
|
|
1809
|
+
}
|
|
1810
|
+
function seqFirstAccept(parts) {
|
|
1811
|
+
const sets = [];
|
|
1812
|
+
for (const p of parts) {
|
|
1813
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1814
|
+
sets.push(partFirstAccept(p));
|
|
1815
|
+
if (mandatory) break;
|
|
1816
|
+
}
|
|
1817
|
+
return unionCharSets(sets);
|
|
1818
|
+
}
|
|
1819
|
+
function groupInnerSafe(shape) {
|
|
1820
|
+
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1821
|
+
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1822
|
+
if (shape.kind === "alt") return shape.disjoint;
|
|
1823
|
+
return false;
|
|
1824
|
+
}
|
|
1825
|
+
function shapeFirstAccept(shape) {
|
|
1826
|
+
if (shapeCanBeEmpty(shape)) return "any";
|
|
1827
|
+
switch (shape.kind) {
|
|
1828
|
+
case "chars":
|
|
1829
|
+
return { ranges: shape.ranges, negated: false };
|
|
1830
|
+
case "ident":
|
|
1831
|
+
return { ranges: shape.head, negated: false };
|
|
1832
|
+
case "until":
|
|
1833
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1834
|
+
case "delimited":
|
|
1835
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1836
|
+
case "string":
|
|
1837
|
+
return { ranges: [[shape.quote, shape.quote]], negated: false };
|
|
1838
|
+
case "litFold": {
|
|
1839
|
+
const cp = shape.open[0];
|
|
1840
|
+
if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
|
|
1841
|
+
if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
|
|
1842
|
+
return { ranges: [[cp, cp]], negated: false };
|
|
1843
|
+
}
|
|
1844
|
+
case "seq":
|
|
1845
|
+
return seqFirstAccept(shape.parts);
|
|
1846
|
+
case "lookahead":
|
|
1847
|
+
return shapeFirstAccept(shape.inner);
|
|
1848
|
+
// zero-width — doesn't change first-char acceptance
|
|
1849
|
+
case "alt": {
|
|
1850
|
+
if (!shape.disjoint) return "any";
|
|
1851
|
+
const sets = shape.firsts.filter((f) => f !== null);
|
|
1852
|
+
return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
function allPairsDisjoint(sets) {
|
|
1857
|
+
for (let i = 0; i < sets.length; i++) {
|
|
1858
|
+
const a = sets[i];
|
|
1859
|
+
if (a === "any") return false;
|
|
1860
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
1861
|
+
const b = sets[j];
|
|
1862
|
+
if (b === "any" || !classDisjoint(a, b)) return false;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
return true;
|
|
1866
|
+
}
|
|
1867
|
+
function parseAlternation(source) {
|
|
1868
|
+
const parts = splitTopLevelAlternation(source);
|
|
1869
|
+
if (!parts) return null;
|
|
1870
|
+
const arms = [];
|
|
1871
|
+
for (const p of parts) {
|
|
1872
|
+
const arm = parseScanShape(p);
|
|
1873
|
+
if (!arm) return null;
|
|
1874
|
+
arms.push(arm);
|
|
1875
|
+
}
|
|
1876
|
+
const firstSets = arms.map(shapeFirstAccept);
|
|
1877
|
+
const disjoint = allPairsDisjoint(firstSets);
|
|
1878
|
+
return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
|
|
1879
|
+
}
|
|
1333
1880
|
function parseScanShape(source) {
|
|
1881
|
+
const alt = parseAlternation(source);
|
|
1882
|
+
if (alt) return alt;
|
|
1883
|
+
const la = stripTrailingLookahead(source);
|
|
1884
|
+
if (la) {
|
|
1885
|
+
const inner = parseScanShapeCore(la.base);
|
|
1886
|
+
if (!inner) return null;
|
|
1887
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
1888
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
1889
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
1890
|
+
}
|
|
1891
|
+
return parseScanShapeCore(source);
|
|
1892
|
+
}
|
|
1893
|
+
function parseScanShapeCore(source) {
|
|
1334
1894
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1335
|
-
if (m) {
|
|
1336
|
-
if (m[1].startsWith("^")) return null;
|
|
1895
|
+
if (m && !m[1].startsWith("^")) {
|
|
1337
1896
|
const ranges = parseClassRanges(m[1]);
|
|
1338
|
-
return ranges ? { kind: "chars", ranges } : null;
|
|
1897
|
+
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1898
|
+
}
|
|
1899
|
+
m = /^\\([dws])([+*])$/.exec(source);
|
|
1900
|
+
if (m) {
|
|
1901
|
+
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1339
1902
|
}
|
|
1903
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
|
|
1904
|
+
if (m) {
|
|
1905
|
+
const head = classToRanges(m[1]);
|
|
1906
|
+
const tail = classToRanges(m[2]);
|
|
1907
|
+
if (head && tail) return { kind: "ident", head, tail };
|
|
1908
|
+
}
|
|
1909
|
+
const str = parseStringShape(source);
|
|
1910
|
+
if (str) return str;
|
|
1340
1911
|
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1341
1912
|
if (m) {
|
|
1342
1913
|
const open = literalCodePoints(m[1]);
|
|
1343
1914
|
const stop = parseClassRanges(m[2]);
|
|
1344
1915
|
if (open && stop) return { kind: "until", open, stop };
|
|
1345
|
-
return null;
|
|
1346
1916
|
}
|
|
1347
1917
|
if (!source.includes("\\\\")) {
|
|
1348
1918
|
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
@@ -1352,68 +1922,278 @@ function parseScanShape(source) {
|
|
|
1352
1922
|
if (open && close) return { kind: "delimited", open, close };
|
|
1353
1923
|
}
|
|
1354
1924
|
}
|
|
1925
|
+
const parts = parseSeqParts(source);
|
|
1926
|
+
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1355
1927
|
return null;
|
|
1356
1928
|
}
|
|
1929
|
+
function scanShapeFromRegex(source, flags) {
|
|
1930
|
+
if (/[msu]/.test(flags)) return null;
|
|
1931
|
+
if (/i/.test(flags)) {
|
|
1932
|
+
const open = literalCodePoints(source);
|
|
1933
|
+
return open ? { kind: "litFold", open } : null;
|
|
1934
|
+
}
|
|
1935
|
+
return parseScanShape(source);
|
|
1936
|
+
}
|
|
1357
1937
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1358
1938
|
var litCond = (base, cps, firstVar) => cps.map(
|
|
1359
1939
|
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1360
1940
|
).join(" && ");
|
|
1361
|
-
|
|
1941
|
+
var LINE_TERMINATORS = [10, 13, 8232, 8233];
|
|
1942
|
+
function stringHardStop(shape) {
|
|
1943
|
+
return shape.excluded.filter(
|
|
1944
|
+
([lo, hi]) => !(lo === hi && (lo === shape.quote || lo === BACKSLASH))
|
|
1945
|
+
);
|
|
1946
|
+
}
|
|
1947
|
+
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1948
|
+
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
1949
|
+
if (shape.kind === "lookahead") {
|
|
1950
|
+
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
1951
|
+
const okV = mint("_ok");
|
|
1952
|
+
const endV2 = mint("_end");
|
|
1953
|
+
const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
|
|
1954
|
+
const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
|
|
1955
|
+
const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
|
|
1956
|
+
const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
|
|
1957
|
+
return {
|
|
1958
|
+
setup: [
|
|
1959
|
+
...inner.setup,
|
|
1960
|
+
`${ind2}let ${okV} = ${inner.ok}`,
|
|
1961
|
+
`${ind2}let ${endV2} = ${inner.end}`,
|
|
1962
|
+
`${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
|
|
1963
|
+
],
|
|
1964
|
+
ok: okV,
|
|
1965
|
+
end: endV2
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
if (shape.kind === "alt") {
|
|
1969
|
+
const okV = mint("_ok");
|
|
1970
|
+
const endV2 = mint("_end");
|
|
1971
|
+
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
1972
|
+
if (shape.disjoint) {
|
|
1973
|
+
const c0 = codeAt(start, firstChar);
|
|
1974
|
+
shape.arms.forEach((arm, k) => {
|
|
1975
|
+
const fs2 = shape.firsts[k];
|
|
1976
|
+
const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
|
|
1977
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
1978
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
1979
|
+
lines.push(...m.setup);
|
|
1980
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
1981
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
1982
|
+
lines.push(`${ind2}}`);
|
|
1983
|
+
});
|
|
1984
|
+
} else {
|
|
1985
|
+
const lbl = mint("_alt");
|
|
1986
|
+
lines.push(`${ind2}${lbl}: {`);
|
|
1987
|
+
for (const arm of shape.arms) {
|
|
1988
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
1989
|
+
lines.push(...m.setup);
|
|
1990
|
+
lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
1991
|
+
}
|
|
1992
|
+
lines.push(`${ind2}}`);
|
|
1993
|
+
}
|
|
1994
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
1995
|
+
}
|
|
1362
1996
|
if (shape.kind === "chars") {
|
|
1997
|
+
const cur = mint("_e");
|
|
1363
1998
|
return {
|
|
1364
1999
|
setup: [
|
|
1365
|
-
|
|
1366
|
-
|
|
2000
|
+
`${ind2}let ${cur} = ${start}`,
|
|
2001
|
+
`${ind2}while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.ranges)})) ${cur}++`
|
|
1367
2002
|
],
|
|
1368
|
-
|
|
2003
|
+
// `*` always matches (possibly empty); `+` needs at least one char.
|
|
2004
|
+
ok: shape.minOne ? `${cur} > ${start}` : "true",
|
|
2005
|
+
end: cur
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
if (shape.kind === "ident") {
|
|
2009
|
+
const cur = mint("_e");
|
|
2010
|
+
return {
|
|
2011
|
+
setup: [
|
|
2012
|
+
`${ind2}let ${cur} = ${start}`,
|
|
2013
|
+
`${ind2}if (${start} < input.length && (${classCond(codeAt(start, firstChar), shape.head)})) {`,
|
|
2014
|
+
`${ind2} ${cur} = ${start} + 1`,
|
|
2015
|
+
`${ind2} while (${cur} < input.length && (${classCond(`input.charCodeAt(${cur})`, shape.tail)})) ${cur}++`,
|
|
2016
|
+
`${ind2}}`
|
|
2017
|
+
],
|
|
2018
|
+
ok: `${cur} > ${start}`,
|
|
2019
|
+
end: cur
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
if (shape.kind === "seq") {
|
|
2023
|
+
const endV2 = mint("_e");
|
|
2024
|
+
const okV = mint("_ok");
|
|
2025
|
+
const runCond = (p, at) => {
|
|
2026
|
+
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
2027
|
+
return p.negated ? `!(${c})` : `(${c})`;
|
|
2028
|
+
};
|
|
2029
|
+
const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
|
|
2030
|
+
let first = true;
|
|
2031
|
+
for (const p of shape.parts) {
|
|
2032
|
+
if (p.part === "lit") {
|
|
2033
|
+
const L = p.cps.length;
|
|
2034
|
+
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
2035
|
+
if (p.optional) {
|
|
2036
|
+
lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
2037
|
+
} else {
|
|
2038
|
+
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
2039
|
+
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
2040
|
+
}
|
|
2041
|
+
} else if (p.part === "run" && !p.unbounded) {
|
|
2042
|
+
if (p.min === 1) {
|
|
2043
|
+
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
2044
|
+
lines.push(`${ind2} ${endV2}++`);
|
|
2045
|
+
} else {
|
|
2046
|
+
lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2047
|
+
}
|
|
2048
|
+
} else if (p.part === "run") {
|
|
2049
|
+
const s = mint("_s");
|
|
2050
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
2051
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2052
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
2053
|
+
} else {
|
|
2054
|
+
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
2055
|
+
if (!p.unbounded) {
|
|
2056
|
+
lines.push(...inner.setup);
|
|
2057
|
+
if (p.min === 1) {
|
|
2058
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2059
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2060
|
+
} else {
|
|
2061
|
+
lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
2062
|
+
}
|
|
2063
|
+
} else {
|
|
2064
|
+
const progV = mint("_prog");
|
|
2065
|
+
lines.push(`${ind2} let ${progV} = false`);
|
|
2066
|
+
lines.push(`${ind2} while (true) {`);
|
|
2067
|
+
lines.push(...inner.setup);
|
|
2068
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2069
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2070
|
+
lines.push(`${ind2} ${progV} = true`);
|
|
2071
|
+
lines.push(`${ind2} }`);
|
|
2072
|
+
if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
first = false;
|
|
2076
|
+
}
|
|
2077
|
+
lines.push(`${ind2} ${okV} = true`);
|
|
2078
|
+
lines.push(`${ind2}} while (false)`);
|
|
2079
|
+
lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
|
|
2080
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2081
|
+
}
|
|
2082
|
+
if (shape.kind === "litFold") {
|
|
2083
|
+
const openLen2 = shape.open.length;
|
|
2084
|
+
const endV2 = mint("_end");
|
|
2085
|
+
const checks = shape.open.map((cp, k) => {
|
|
2086
|
+
const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
|
|
2087
|
+
return foldEq(cVar, cp);
|
|
2088
|
+
});
|
|
2089
|
+
return {
|
|
2090
|
+
setup: [
|
|
2091
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2092
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
|
|
2093
|
+
],
|
|
2094
|
+
ok: `${endV2} > ${start}`,
|
|
2095
|
+
end: endV2
|
|
1369
2096
|
};
|
|
1370
2097
|
}
|
|
1371
2098
|
if (shape.kind === "until") {
|
|
2099
|
+
const j2 = mint("_j");
|
|
2100
|
+
const openLen2 = shape.open.length;
|
|
2101
|
+
const openChk2 = openLen2 === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
2102
|
+
return {
|
|
2103
|
+
setup: [
|
|
2104
|
+
`${ind2}let ${j2} = ${start}`,
|
|
2105
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${openChk2})) {`,
|
|
2106
|
+
`${ind2} ${j2} = ${start} + ${openLen2}`,
|
|
2107
|
+
`${ind2} while (${j2} < input.length && !(${classCond(`input.charCodeAt(${j2})`, shape.stop)})) ${j2}++`,
|
|
2108
|
+
`${ind2}}`
|
|
2109
|
+
],
|
|
2110
|
+
// An open-until-terminator always completes (stop char or EOF), so a matched
|
|
2111
|
+
// open literal is a full match; `end === start` iff the open didn't match.
|
|
2112
|
+
ok: `${j2} > ${start}`,
|
|
2113
|
+
end: j2
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
if (shape.kind === "string") {
|
|
2117
|
+
const okV = mint("_ok");
|
|
2118
|
+
const endV2 = mint("_end");
|
|
2119
|
+
const i = mint("_i");
|
|
2120
|
+
const c2 = mint("_c");
|
|
2121
|
+
const hard = stringHardStop(shape);
|
|
2122
|
+
const ltLines = shape.escLineTerm ? [] : (() => {
|
|
2123
|
+
const c3 = mint("_c");
|
|
2124
|
+
return [
|
|
2125
|
+
`${ind2} const ${c3} = input.charCodeAt(${i} + 1)`,
|
|
2126
|
+
`${ind2} if (${LINE_TERMINATORS.map((t) => `${c3} === ${t}`).join(" || ")}) break`
|
|
2127
|
+
];
|
|
2128
|
+
})();
|
|
1372
2129
|
return {
|
|
1373
2130
|
setup: [
|
|
1374
|
-
|
|
1375
|
-
|
|
2131
|
+
`${ind2}let ${okV} = false`,
|
|
2132
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2133
|
+
`${ind2}if (${start} < input.length && (${codeAt(start, firstChar)}) === ${shape.quote}) {`,
|
|
2134
|
+
`${ind2} let ${i} = ${start} + 1`,
|
|
2135
|
+
`${ind2} while (${i} < input.length) {`,
|
|
2136
|
+
`${ind2} const ${c2} = input.charCodeAt(${i})`,
|
|
2137
|
+
`${ind2} if (${c2} === ${shape.quote}) { ${okV} = true; ${endV2} = ${i} + 1; break }`,
|
|
2138
|
+
`${ind2} if (${c2} === ${BACKSLASH}) {`,
|
|
2139
|
+
`${ind2} if (${i} + 1 >= input.length) break`,
|
|
2140
|
+
...ltLines,
|
|
2141
|
+
`${ind2} ${i} += 2`,
|
|
2142
|
+
`${ind2} continue`,
|
|
2143
|
+
`${ind2} }`,
|
|
2144
|
+
...hard.length ? [`${ind2} if (${classCond(c2, hard)}) break`] : [],
|
|
2145
|
+
`${ind2} ${i}++`,
|
|
2146
|
+
`${ind2} }`,
|
|
2147
|
+
`${ind2}}`
|
|
1376
2148
|
],
|
|
1377
|
-
|
|
2149
|
+
ok: okV,
|
|
2150
|
+
end: endV2
|
|
1378
2151
|
};
|
|
1379
2152
|
}
|
|
1380
|
-
const
|
|
2153
|
+
const j = mint("_j");
|
|
2154
|
+
const endV = mint("_end");
|
|
2155
|
+
const openLen = shape.open.length;
|
|
2156
|
+
const closeLen = shape.close.length;
|
|
2157
|
+
const openChk = openLen === 1 ? `${codeAt(start, firstChar)} === ${shape.open[0]}` : litCond(start, shape.open, firstChar);
|
|
2158
|
+
const closeChk = litCond(j, shape.close);
|
|
1381
2159
|
return {
|
|
1382
2160
|
setup: [
|
|
1383
|
-
|
|
1384
|
-
|
|
2161
|
+
`${ind2}let ${j} = ${start}`,
|
|
2162
|
+
`${ind2}let ${endV} = ${start}`,
|
|
2163
|
+
`${ind2}if (${start} + ${openLen} <= input.length && (${openChk})) {`,
|
|
2164
|
+
`${ind2} ${j} = ${start} + ${openLen}`,
|
|
2165
|
+
`${ind2} while (${j} + ${closeLen - 1} < input.length && !(${closeChk})) ${j}++`,
|
|
2166
|
+
`${ind2} if (${j} + ${closeLen} <= input.length && (${closeChk})) ${endV} = ${j} + ${closeLen}`,
|
|
2167
|
+
`${ind2}}`
|
|
1385
2168
|
],
|
|
1386
|
-
|
|
2169
|
+
ok: `${endV} > ${start}`,
|
|
2170
|
+
end: endV
|
|
1387
2171
|
};
|
|
1388
2172
|
}
|
|
1389
|
-
function
|
|
1390
|
-
const
|
|
1391
|
-
return ` if (${
|
|
2173
|
+
function scanBranch(shape, mint) {
|
|
2174
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
2175
|
+
return [...m.setup, ` if (${m.end} > _e) { _e = ${m.end}; continue }`].join("\n");
|
|
1392
2176
|
}
|
|
1393
|
-
function
|
|
1394
|
-
|
|
1395
|
-
return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
|
|
1396
|
-
}
|
|
1397
|
-
const { setup, endVar } = advanceExpr(shape);
|
|
1398
|
-
return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
|
|
1399
|
-
}
|
|
1400
|
-
function captureChunk(startVar, kindIndex) {
|
|
2177
|
+
function scanBranchLabeled(shape, kindIndex, mint) {
|
|
2178
|
+
const m = emitShapeMatch(shape, "_e", mint, " ", "c");
|
|
1401
2179
|
return [
|
|
2180
|
+
...m.setup,
|
|
2181
|
+
` if (${m.end} > _e) {`,
|
|
1402
2182
|
` if (_cap) {`,
|
|
1403
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${
|
|
1404
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${
|
|
1405
|
-
` }
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
|
|
1411
|
-
if (endVar !== "_e") lines.push(` _e = ${endVar}`);
|
|
1412
|
-
lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
|
|
1413
|
-
return lines.join("\n");
|
|
2183
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_e, ${m.end}, ${kindIndex})`,
|
|
2184
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_e, ${m.end}, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
2185
|
+
` }`,
|
|
2186
|
+
` _e = ${m.end}`,
|
|
2187
|
+
` continue`,
|
|
2188
|
+
` }`
|
|
2189
|
+
].join("\n");
|
|
1414
2190
|
}
|
|
1415
2191
|
|
|
1416
2192
|
// src/compiler/trivia-fast-path.ts
|
|
2193
|
+
function makeMint() {
|
|
2194
|
+
let n = 0;
|
|
2195
|
+
return (prefix = "_v") => `${prefix}${n++}`;
|
|
2196
|
+
}
|
|
1417
2197
|
function unwrapTrivia2(p) {
|
|
1418
2198
|
let cur = p;
|
|
1419
2199
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1430,14 +2210,14 @@ function choiceArms(p) {
|
|
|
1430
2210
|
return null;
|
|
1431
2211
|
}
|
|
1432
2212
|
function armShape(arm) {
|
|
1433
|
-
const
|
|
1434
|
-
return
|
|
2213
|
+
const d = getCoreRegexDef(arm);
|
|
2214
|
+
return d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1435
2215
|
}
|
|
1436
2216
|
function analyzeTriviaFastPath(trivia2) {
|
|
1437
2217
|
const core = unwrapTrivia2(trivia2);
|
|
1438
|
-
const
|
|
1439
|
-
if (
|
|
1440
|
-
const shape =
|
|
2218
|
+
const directDef = getCoreRegexDef(core);
|
|
2219
|
+
if (directDef) {
|
|
2220
|
+
const shape = scanShapeFromRegex(directDef.source, directDef.flags);
|
|
1441
2221
|
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1442
2222
|
}
|
|
1443
2223
|
const inner = oneOrMoreInner(core);
|
|
@@ -1461,10 +2241,11 @@ var CAP_RECORD = [
|
|
|
1461
2241
|
` }`
|
|
1462
2242
|
].join("\n");
|
|
1463
2243
|
function composeFastLoop(shapes) {
|
|
2244
|
+
const mint = makeMint();
|
|
1464
2245
|
return [
|
|
1465
2246
|
` while (_e < input.length) {`,
|
|
1466
2247
|
` const c = input.charCodeAt(_e)`,
|
|
1467
|
-
...shapes.map(scanBranch),
|
|
2248
|
+
...shapes.map((s) => scanBranch(s, mint)),
|
|
1468
2249
|
` break`,
|
|
1469
2250
|
` }`
|
|
1470
2251
|
].join("\n");
|
|
@@ -1484,20 +2265,21 @@ function analyzeLabeledScannableRun(trivia2) {
|
|
|
1484
2265
|
if (!spec) return null;
|
|
1485
2266
|
const out = [];
|
|
1486
2267
|
for (const arm of spec.arms) {
|
|
1487
|
-
const
|
|
1488
|
-
const shape =
|
|
2268
|
+
const d = getCoreRegexDef(arm.parser);
|
|
2269
|
+
const shape = d ? scanShapeFromRegex(d.source, d.flags) : null;
|
|
1489
2270
|
if (!shape) return null;
|
|
1490
2271
|
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1491
2272
|
}
|
|
1492
2273
|
return out;
|
|
1493
2274
|
}
|
|
1494
2275
|
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
2276
|
+
const mint = makeMint();
|
|
1495
2277
|
return [
|
|
1496
2278
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1497
2279
|
` let _e = _pos`,
|
|
1498
2280
|
` while (_e < input.length) {`,
|
|
1499
2281
|
` const c = input.charCodeAt(_e)`,
|
|
1500
|
-
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
2282
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex, mint)),
|
|
1501
2283
|
` break`,
|
|
1502
2284
|
` }`,
|
|
1503
2285
|
` return _e`,
|
|
@@ -1574,6 +2356,15 @@ function buildLabeledRuntimeTriviaFnDecl(fnName, spec, rpStartIndex) {
|
|
|
1574
2356
|
].join("\n");
|
|
1575
2357
|
}
|
|
1576
2358
|
|
|
2359
|
+
// src/compiler/scannable-terminal.ts
|
|
2360
|
+
function emitScannableTerminal(shape, c) {
|
|
2361
|
+
const m = emitShapeMatch(shape, c.pos, c.fresh, c.ind);
|
|
2362
|
+
const stmts = [...m.setup];
|
|
2363
|
+
if (m.ok !== "true") stmts.push(...c.failIf(`!(${m.ok})`));
|
|
2364
|
+
stmts.push(`${c.ind}const ${c.valueVar} = input.slice(${c.pos}, ${m.end})`);
|
|
2365
|
+
return { stmts, endVar: m.end };
|
|
2366
|
+
}
|
|
2367
|
+
|
|
1577
2368
|
// src/compiler/inline-build.ts
|
|
1578
2369
|
var MK_BUILD_RE = /^\(\s*(?:\w+\s*,\s*){3}\w+\s*\)\s*=>\s*mk\s*\(\s*(['"])([^'"]+)\1\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*$/;
|
|
1579
2370
|
function escapeRegExp(s) {
|
|
@@ -1695,14 +2486,45 @@ function failReturn(expected, posExpr) {
|
|
|
1695
2486
|
function failReturnArr(expectedArr, posExpr) {
|
|
1696
2487
|
return `return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1697
2488
|
}
|
|
2489
|
+
function hoistExpected(ctx, constArrSource) {
|
|
2490
|
+
let name = ctx.expectedMap.get(constArrSource);
|
|
2491
|
+
if (name === void 0) {
|
|
2492
|
+
name = `_fx${ctx.expectedDecls.length}`;
|
|
2493
|
+
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
2494
|
+
ctx.expectedMap.set(constArrSource, name);
|
|
2495
|
+
}
|
|
2496
|
+
return name;
|
|
2497
|
+
}
|
|
1698
2498
|
function failBody(ctx, expected, posExpr) {
|
|
1699
|
-
if (ctx.failLabel)
|
|
2499
|
+
if (ctx.failLabel) {
|
|
2500
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2501
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
2502
|
+
}
|
|
1700
2503
|
return failReturn(expected, posExpr);
|
|
1701
2504
|
}
|
|
1702
|
-
function failArrBody(ctx, expectedArr, posExpr) {
|
|
1703
|
-
if (ctx.failLabel)
|
|
2505
|
+
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
2506
|
+
if (ctx.failLabel) {
|
|
2507
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2508
|
+
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
2509
|
+
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
2510
|
+
}
|
|
2511
|
+
if (!constant) return `return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1704
2512
|
return failReturnArr(expectedArr, posExpr);
|
|
1705
2513
|
}
|
|
2514
|
+
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
2515
|
+
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
2516
|
+
}
|
|
2517
|
+
function propagateFailBody(ctx, srcCtx = "_ctx") {
|
|
2518
|
+
if (srcCtx !== "_ctx") {
|
|
2519
|
+
if (ctx.failLabel) {
|
|
2520
|
+
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
2521
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; break ${ctx.failLabel} }`;
|
|
2522
|
+
}
|
|
2523
|
+
return `{ _ctx._fe = ${srcCtx}._fe; _ctx._fx = ${srcCtx}._fx; ${resultFromRecorded()} }`;
|
|
2524
|
+
}
|
|
2525
|
+
if (ctx.failLabel) return `break ${ctx.failLabel}`;
|
|
2526
|
+
return resultFromRecorded();
|
|
2527
|
+
}
|
|
1706
2528
|
function emitIfFail(ctx, cond, body) {
|
|
1707
2529
|
return [
|
|
1708
2530
|
`${ind(ctx)}if (${cond}) {`,
|
|
@@ -1738,13 +2560,15 @@ function pushNamedFnDecl(ctx, fnName, bodyStmts, valueVar, endVar) {
|
|
|
1738
2560
|
`}`
|
|
1739
2561
|
].join("\n"));
|
|
1740
2562
|
}
|
|
1741
|
-
function emitNamedFnCall(ctx, fnName, pos
|
|
2563
|
+
function emitNamedFnCall(ctx, fnName, pos) {
|
|
1742
2564
|
const vv = v(ctx, "_pfv");
|
|
1743
2565
|
const ev = v(ctx, "_pfe");
|
|
1744
2566
|
return {
|
|
1745
2567
|
stmts: [
|
|
1746
2568
|
`${ind(ctx)}const ${vv} = ${fnName}(input, ${pos}, _ctx)`,
|
|
1747
|
-
|
|
2569
|
+
// ref/lazy returns the inner failure verbatim — propagate the recorded
|
|
2570
|
+
// deepest failure (shares _ctx with the named fn), not a "parser" label.
|
|
2571
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx)),
|
|
1748
2572
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
1749
2573
|
],
|
|
1750
2574
|
valueVar: vv,
|
|
@@ -1847,13 +2671,34 @@ function ensureTriviaFn(ctx) {
|
|
|
1847
2671
|
function ensureTriviaCaptureFn(ctx) {
|
|
1848
2672
|
return ensureTriviaFn(ctx);
|
|
1849
2673
|
}
|
|
1850
|
-
function firstSetCond(codeVar,
|
|
1851
|
-
if (
|
|
1852
|
-
if (
|
|
1853
|
-
return
|
|
2674
|
+
function firstSetCond(codeVar, fs2) {
|
|
2675
|
+
if (fs2.kind === "any") return "true";
|
|
2676
|
+
if (fs2.kind === "empty") return "false";
|
|
2677
|
+
return fs2.ranges.map(
|
|
1854
2678
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1855
2679
|
).join(" || ");
|
|
1856
2680
|
}
|
|
2681
|
+
var SWITCH_RANGE_LIMIT = 4;
|
|
2682
|
+
var SWITCH_MAX_CASES = 48;
|
|
2683
|
+
var SWITCH_MIN_CASES = 3;
|
|
2684
|
+
var _forceDisjointIf = false;
|
|
2685
|
+
function planDisjointDispatch(parsers) {
|
|
2686
|
+
const cases = [];
|
|
2687
|
+
let total = 0;
|
|
2688
|
+
for (const p of parsers) {
|
|
2689
|
+
const fs2 = p._meta.firstSet;
|
|
2690
|
+
if (fs2.kind !== "ranges") return { kind: "if" };
|
|
2691
|
+
const pts = [];
|
|
2692
|
+
for (const r of fs2.ranges) {
|
|
2693
|
+
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2694
|
+
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2695
|
+
}
|
|
2696
|
+
total += pts.length;
|
|
2697
|
+
if (total > SWITCH_MAX_CASES) return { kind: "if" };
|
|
2698
|
+
cases.push(pts);
|
|
2699
|
+
}
|
|
2700
|
+
return total >= SWITCH_MIN_CASES ? { kind: "switch", cases } : { kind: "if" };
|
|
2701
|
+
}
|
|
1857
2702
|
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1858
2703
|
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1859
2704
|
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
@@ -1967,18 +2812,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
1967
2812
|
return true;
|
|
1968
2813
|
}
|
|
1969
2814
|
}
|
|
1970
|
-
function emitFallible(inner, ctx, pos) {
|
|
2815
|
+
function emitFallible(inner, ctx, pos, swallow = false) {
|
|
1971
2816
|
const lbl = v(ctx, "_lbl");
|
|
1972
2817
|
const okV = `${lbl}ok`;
|
|
1973
2818
|
const valV = `${lbl}v`;
|
|
1974
2819
|
const endV = `${lbl}e`;
|
|
1975
2820
|
const savedLabel = ctx.failLabel;
|
|
1976
2821
|
const savedIndent = ctx.indent;
|
|
2822
|
+
const savedRecord = ctx.recordFail;
|
|
1977
2823
|
ctx.failLabel = lbl;
|
|
1978
2824
|
ctx.indent = savedIndent + 1;
|
|
2825
|
+
if (swallow) ctx.recordFail = false;
|
|
1979
2826
|
const r = emit(inner, ctx, pos);
|
|
1980
2827
|
ctx.failLabel = savedLabel;
|
|
1981
2828
|
ctx.indent = savedIndent;
|
|
2829
|
+
ctx.recordFail = savedRecord;
|
|
1982
2830
|
const ind0 = ind(ctx);
|
|
1983
2831
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
1984
2832
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
@@ -2001,6 +2849,7 @@ function emitFallible(inner, ctx, pos) {
|
|
|
2001
2849
|
];
|
|
2002
2850
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
2003
2851
|
}
|
|
2852
|
+
var CHARCODE_CHAIN_MAX = 16;
|
|
2004
2853
|
function emitLit(def, ctx, pos) {
|
|
2005
2854
|
const { value, caseInsensitive } = def;
|
|
2006
2855
|
const len = value.length;
|
|
@@ -2023,7 +2872,7 @@ function emitLit(def, ctx, pos) {
|
|
|
2023
2872
|
...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
|
|
2024
2873
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
2025
2874
|
);
|
|
2026
|
-
} else if (len <=
|
|
2875
|
+
} else if (len <= CHARCODE_CHAIN_MAX) {
|
|
2027
2876
|
const checks = Array.from(
|
|
2028
2877
|
{ length: len },
|
|
2029
2878
|
(_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
|
|
@@ -2045,7 +2894,55 @@ function emitLit(def, ctx, pos) {
|
|
|
2045
2894
|
function escapeKeywordRe(s) {
|
|
2046
2895
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2047
2896
|
}
|
|
2897
|
+
function emitKeywordsFast(def, ctx, pos) {
|
|
2898
|
+
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
2899
|
+
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
2900
|
+
if (def.caseInsensitive && def.boundary) return null;
|
|
2901
|
+
let boundary = null;
|
|
2902
|
+
if (def.boundary) {
|
|
2903
|
+
let body = def.boundary;
|
|
2904
|
+
const negated = body.startsWith("^");
|
|
2905
|
+
if (negated) body = body.slice(1);
|
|
2906
|
+
const ranges = parseClassRanges(body);
|
|
2907
|
+
if (!ranges) return null;
|
|
2908
|
+
boundary = { ranges, negated };
|
|
2909
|
+
}
|
|
2910
|
+
const mint = (prefix = "_v") => v(ctx, prefix);
|
|
2911
|
+
const lbl = v(ctx, "_kwLbl");
|
|
2912
|
+
const valV = v(ctx, "_kwv");
|
|
2913
|
+
const endV = v(ctx, "_kwe");
|
|
2914
|
+
const bodyInd = ind(ctx) + " ";
|
|
2915
|
+
const tries = [];
|
|
2916
|
+
for (const w of def.words) {
|
|
2917
|
+
const cps = Array.from(w, (ch) => ch.codePointAt(0));
|
|
2918
|
+
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
2919
|
+
if (boundary) {
|
|
2920
|
+
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
2921
|
+
}
|
|
2922
|
+
const m = emitShapeMatch(shape, pos, mint, bodyInd);
|
|
2923
|
+
tries.push(
|
|
2924
|
+
...m.setup,
|
|
2925
|
+
// Slice from input rather than reusing the literal word: `caseInsensitive`
|
|
2926
|
+
// must return the text as it actually appeared (e.g. "ABC" for keyword
|
|
2927
|
+
// "abc"), matching what the original `RegExp.exec()[0]` returned.
|
|
2928
|
+
`${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
|
|
2929
|
+
);
|
|
2930
|
+
}
|
|
2931
|
+
const stmts = [
|
|
2932
|
+
`${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
|
|
2933
|
+
`${ind(ctx)}${lbl}: {`,
|
|
2934
|
+
...tries,
|
|
2935
|
+
`${ind(ctx)}}`,
|
|
2936
|
+
// Every word has length >= 1 (checked above), so a real match always
|
|
2937
|
+
// advances past `pos` — `endV === pos` only happens when no candidate matched.
|
|
2938
|
+
...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
|
|
2939
|
+
];
|
|
2940
|
+
stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
|
|
2941
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
2942
|
+
}
|
|
2048
2943
|
function emitKeywords(def, ctx, pos) {
|
|
2944
|
+
const fast = emitKeywordsFast(def, ctx, pos);
|
|
2945
|
+
if (fast) return fast;
|
|
2049
2946
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
2050
2947
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
2051
2948
|
const flags = def.caseInsensitive ? "iuy" : "uy";
|
|
@@ -2070,6 +2967,22 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2070
2967
|
return { stmts, valueVar: vv, endVar };
|
|
2071
2968
|
}
|
|
2072
2969
|
function emitRegex(def, ctx, pos) {
|
|
2970
|
+
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2971
|
+
const shape = scanShapeFromRegex(def.source, def.flags);
|
|
2972
|
+
if (shape) {
|
|
2973
|
+
const vv2 = v(ctx);
|
|
2974
|
+
const scanned = emitScannableTerminal(shape, {
|
|
2975
|
+
ind: ind(ctx),
|
|
2976
|
+
pos,
|
|
2977
|
+
valueVar: vv2,
|
|
2978
|
+
failIf: (cond) => emitIfFail(ctx, cond, failBody(ctx, expectedStr, pos)),
|
|
2979
|
+
fresh: (prefix) => v(ctx, prefix)
|
|
2980
|
+
});
|
|
2981
|
+
if (scanned) {
|
|
2982
|
+
const stmts2 = [...scanned.stmts, ...emitLeafCapture(ctx, vv2, pos, scanned.endVar)];
|
|
2983
|
+
return { stmts: stmts2, valueVar: vv2, endVar: scanned.endVar };
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2073
2986
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2074
2987
|
const key = `${def.optimizedSource}/${flags}`;
|
|
2075
2988
|
let rName = ctx.regexMap.get(key);
|
|
@@ -2080,7 +2993,6 @@ function emitRegex(def, ctx, pos) {
|
|
|
2080
2993
|
}
|
|
2081
2994
|
const mv = v(ctx, "_m");
|
|
2082
2995
|
const vv = v(ctx);
|
|
2083
|
-
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
2084
2996
|
const stmts = [
|
|
2085
2997
|
`${ind(ctx)}${rName}.lastIndex = ${pos}`,
|
|
2086
2998
|
`${ind(ctx)}const ${mv} = ${rName}.exec(input)`,
|
|
@@ -2139,15 +3051,68 @@ function emitSeq(def, ctx, pos) {
|
|
|
2139
3051
|
stmts.push(`${ind(ctx)}const ${arrV} = [${valueVars.join(", ")}]`);
|
|
2140
3052
|
return { stmts, valueVar: arrV, endVar };
|
|
2141
3053
|
}
|
|
3054
|
+
function staticExpectedArr(parsers) {
|
|
3055
|
+
return JSON.stringify(parsers.flatMap((p) => {
|
|
3056
|
+
const e = staticExpected(p);
|
|
3057
|
+
return e.length > 0 ? e : [p._tag];
|
|
3058
|
+
}));
|
|
3059
|
+
}
|
|
3060
|
+
function failsAtStart(p) {
|
|
3061
|
+
const d = p._def;
|
|
3062
|
+
switch (d.tag) {
|
|
3063
|
+
case "literal":
|
|
3064
|
+
case "regex":
|
|
3065
|
+
case "keywords":
|
|
3066
|
+
case "guard":
|
|
3067
|
+
case "not":
|
|
3068
|
+
return true;
|
|
3069
|
+
case "transform":
|
|
3070
|
+
case "label":
|
|
3071
|
+
return failsAtStart(d.parser);
|
|
3072
|
+
default:
|
|
3073
|
+
return false;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
function armStaticExpected(ctx, p) {
|
|
3077
|
+
return hoistExpected(ctx, staticExpectedArr([p]));
|
|
3078
|
+
}
|
|
3079
|
+
function canMatchEmptyAtStart(p) {
|
|
3080
|
+
const d = p._def;
|
|
3081
|
+
switch (d.tag) {
|
|
3082
|
+
case "regex":
|
|
3083
|
+
return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
|
|
3084
|
+
case "optional":
|
|
3085
|
+
return true;
|
|
3086
|
+
case "many":
|
|
3087
|
+
return true;
|
|
3088
|
+
case "transform":
|
|
3089
|
+
case "label":
|
|
3090
|
+
return canMatchEmptyAtStart(d.parser);
|
|
3091
|
+
case "literal":
|
|
3092
|
+
return d.value.length === 0;
|
|
3093
|
+
default:
|
|
3094
|
+
return false;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
function needsFirstSetGuard(p) {
|
|
3098
|
+
const fs2 = p._meta.firstSet;
|
|
3099
|
+
return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
3100
|
+
}
|
|
3101
|
+
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
3102
|
+
if (failsAtStart(p)) {
|
|
3103
|
+
const r = emit(p, ctx, pos);
|
|
3104
|
+
return [...r.stmts, `${ind(ctx)}${valV} = ${r.valueVar}`, `${ind(ctx)}${endV} = ${r.endVar}`];
|
|
3105
|
+
}
|
|
3106
|
+
const { stmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3107
|
+
return [
|
|
3108
|
+
...stmts,
|
|
3109
|
+
...emitIfFail(ctx, `!${okVar}`, failArrBody(ctx, "_ctx._fx", pos, false)),
|
|
3110
|
+
`${ind(ctx)}${valV} = ${valVar}`,
|
|
3111
|
+
`${ind(ctx)}${endV} = ${endVar}`
|
|
3112
|
+
];
|
|
3113
|
+
}
|
|
2142
3114
|
function emitChoice(def, ctx, pos) {
|
|
2143
|
-
const allExpected =
|
|
2144
|
-
def.parsers.map((p) => {
|
|
2145
|
-
const d = p._def;
|
|
2146
|
-
if (d.tag === "literal") return JSON.stringify(d.value);
|
|
2147
|
-
if (d.tag === "regex") return `/${d.source}/`;
|
|
2148
|
-
return p._tag;
|
|
2149
|
-
})
|
|
2150
|
-
);
|
|
3115
|
+
const allExpected = staticExpectedArr(def.parsers);
|
|
2151
3116
|
if (def.disjoint) {
|
|
2152
3117
|
const codeV = v(ctx, "_code");
|
|
2153
3118
|
const valV = v(ctx, "_chv");
|
|
@@ -2156,6 +3121,27 @@ function emitChoice(def, ctx, pos) {
|
|
|
2156
3121
|
`${ind(ctx)}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
|
|
2157
3122
|
`${ind(ctx)}let ${valV}, ${endV} = ${pos}`
|
|
2158
3123
|
];
|
|
3124
|
+
const plan = _forceDisjointIf ? { kind: "if" } : planDisjointDispatch(def.parsers);
|
|
3125
|
+
if (plan.kind === "switch") {
|
|
3126
|
+
stmts.push(`${ind(ctx)}switch (${codeV}) {`);
|
|
3127
|
+
ctx.indent++;
|
|
3128
|
+
for (let i = 0; i < def.parsers.length; i++) {
|
|
3129
|
+
const p = def.parsers[i];
|
|
3130
|
+
for (const cp of plan.cases[i]) stmts.push(`${ind(ctx)}case ${cp}:`);
|
|
3131
|
+
stmts.push(`${ind(ctx)}{`);
|
|
3132
|
+
ctx.indent++;
|
|
3133
|
+
stmts.push(
|
|
3134
|
+
...emitDisjointArm(p, ctx, pos, valV, endV),
|
|
3135
|
+
`${ind(ctx)}break`
|
|
3136
|
+
);
|
|
3137
|
+
ctx.indent--;
|
|
3138
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3139
|
+
}
|
|
3140
|
+
stmts.push(`${ind(ctx)}default: ${failArrBody(ctx, allExpected, pos)}`);
|
|
3141
|
+
ctx.indent--;
|
|
3142
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3143
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3144
|
+
}
|
|
2159
3145
|
let first = true;
|
|
2160
3146
|
for (const p of def.parsers) {
|
|
2161
3147
|
const cond = firstSetCond(codeV, p._meta.firstSet);
|
|
@@ -2163,10 +3149,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2163
3149
|
first = false;
|
|
2164
3150
|
stmts.push(`${ind(ctx)}${kw} (${cond}) {`);
|
|
2165
3151
|
ctx.indent++;
|
|
2166
|
-
|
|
2167
|
-
stmts.push(...r.stmts);
|
|
2168
|
-
stmts.push(`${ind(ctx)}${valV} = ${r.valueVar}`);
|
|
2169
|
-
stmts.push(`${ind(ctx)}${endV} = ${r.endVar}`);
|
|
3152
|
+
stmts.push(...emitDisjointArm(p, ctx, pos, valV, endV));
|
|
2170
3153
|
ctx.indent--;
|
|
2171
3154
|
stmts.push(`${ind(ctx)}}`);
|
|
2172
3155
|
}
|
|
@@ -2175,7 +3158,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
2175
3158
|
}
|
|
2176
3159
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
2177
3160
|
}
|
|
2178
|
-
function emitGreedyClassify(def, superIndex,
|
|
3161
|
+
function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
2179
3162
|
const superParser = def.parsers[superIndex];
|
|
2180
3163
|
const regexDef = getCoreRegexDef(superParser);
|
|
2181
3164
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
@@ -2190,10 +3173,11 @@ function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
|
|
|
2190
3173
|
const wordV = v(ctx, "_gw");
|
|
2191
3174
|
const endV = v(ctx, "_ge");
|
|
2192
3175
|
const valV = v(ctx, "_gcv");
|
|
3176
|
+
const regexExpected = JSON.stringify(staticExpected(superParser));
|
|
2193
3177
|
const stmts = [
|
|
2194
3178
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
2195
3179
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
2196
|
-
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx,
|
|
3180
|
+
...emitIfFail(ctx, `${matchV} === null`, failArrBody(ctx, regexExpected, pos)),
|
|
2197
3181
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
2198
3182
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`,
|
|
2199
3183
|
`${ind(ctx)}let ${valV}`
|
|
@@ -2251,18 +3235,25 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2251
3235
|
stmts.push(...emitElseFail(ctx, failArrBody(ctx, allExpected, pos)));
|
|
2252
3236
|
return { stmts, valueVar: valV, endVar: endV };
|
|
2253
3237
|
}
|
|
2254
|
-
function emitFirstMatch(def,
|
|
3238
|
+
function emitFirstMatch(def, ctx, pos) {
|
|
2255
3239
|
const resValV = v(ctx, "_crv");
|
|
2256
3240
|
const resEndV = v(ctx, "_cre");
|
|
2257
3241
|
const resOkV = v(ctx, "_crok");
|
|
3242
|
+
const codeV = v(ctx, "_chcode");
|
|
3243
|
+
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2258
3244
|
const ind0 = ind(ctx);
|
|
2259
3245
|
const stmts = [
|
|
2260
|
-
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false
|
|
3246
|
+
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
3247
|
+
`${ind0}let ${slots.join(", ")}`,
|
|
3248
|
+
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
2261
3249
|
];
|
|
2262
3250
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2263
3251
|
const p = def.parsers[i];
|
|
2264
3252
|
const gate = def.gates[i];
|
|
2265
3253
|
const autoNot = def.autoNot[i];
|
|
3254
|
+
const atStart = failsAtStart(p);
|
|
3255
|
+
const staticFx = armStaticExpected(ctx, p);
|
|
3256
|
+
const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
2266
3257
|
let gateCond = null;
|
|
2267
3258
|
if (gate) {
|
|
2268
3259
|
const gateIdx = ctx.mapFns.length;
|
|
@@ -2277,49 +3268,53 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2277
3268
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2278
3269
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2279
3270
|
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
3271
|
+
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
2280
3272
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2281
|
-
ctx
|
|
2282
|
-
|
|
3273
|
+
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
3274
|
+
ctx.indent += fsGuard ? 2 : 1;
|
|
2283
3275
|
if (markLeaves) {
|
|
2284
3276
|
stmts.push(
|
|
2285
|
-
`${
|
|
2286
|
-
`${
|
|
2287
|
-
`${
|
|
2288
|
-
`${
|
|
3277
|
+
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
3278
|
+
`${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
|
|
3279
|
+
`${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
3280
|
+
`${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
|
|
2289
3281
|
);
|
|
2290
3282
|
}
|
|
2291
|
-
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3283
|
+
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
|
|
2292
3284
|
stmts.push(...armStmts);
|
|
2293
|
-
ctx.indent--;
|
|
2294
3285
|
if (autoNot && autoNot.length > 0) {
|
|
2295
3286
|
const anCode = v(ctx, "_anc");
|
|
2296
3287
|
const rejectCond = autoNot.map(
|
|
2297
3288
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
|
|
2298
3289
|
).join(" || ");
|
|
2299
|
-
stmts.push(`${
|
|
2300
|
-
stmts.push(`${
|
|
2301
|
-
stmts.push(`${
|
|
2302
|
-
stmts.push(`${
|
|
2303
|
-
stmts.push(`${
|
|
2304
|
-
stmts.push(`${
|
|
2305
|
-
stmts.push(`${
|
|
2306
|
-
stmts.push(`${
|
|
2307
|
-
|
|
3290
|
+
stmts.push(`${ind(ctx)}if (${okVar}) {`);
|
|
3291
|
+
stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
|
|
3292
|
+
stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
|
|
3293
|
+
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
3294
|
+
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
3295
|
+
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
3296
|
+
stmts.push(`${ind(ctx)} }`);
|
|
3297
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3298
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
3299
|
+
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
2308
3300
|
} else {
|
|
2309
|
-
stmts.push(`${
|
|
2310
|
-
|
|
3301
|
+
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
3302
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
|
|
2311
3303
|
}
|
|
3304
|
+
ctx.indent -= fsGuard ? 2 : 1;
|
|
3305
|
+
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
2312
3306
|
stmts.push(`${ind0}}`);
|
|
2313
3307
|
}
|
|
2314
|
-
|
|
3308
|
+
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
3309
|
+
stmts.push(...emitIfFail(ctx, `!${resOkV}`, failArrBody(ctx, concatExpr, pos, false)));
|
|
2315
3310
|
return { stmts, valueVar: resValV, endVar: resEndV };
|
|
2316
3311
|
}
|
|
2317
3312
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
2318
3313
|
if (strategy.tag === "greedyClassify")
|
|
2319
|
-
return emitGreedyClassify(def, strategy.superIndex,
|
|
3314
|
+
return emitGreedyClassify(def, strategy.superIndex, ctx, pos);
|
|
2320
3315
|
if (strategy.tag === "literalsLongestFirst")
|
|
2321
3316
|
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
2322
|
-
return emitFirstMatch(def,
|
|
3317
|
+
return emitFirstMatch(def, ctx, pos);
|
|
2323
3318
|
}
|
|
2324
3319
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
2325
3320
|
const def = p._def;
|
|
@@ -2340,7 +3335,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2340
3335
|
function emitLiteralCondition(litVal, pos) {
|
|
2341
3336
|
const len = litVal.length;
|
|
2342
3337
|
if (len === 0) return "true";
|
|
2343
|
-
if (len >
|
|
3338
|
+
if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
|
|
2344
3339
|
const checks = [`${pos} + ${len} <= input.length`];
|
|
2345
3340
|
for (let i = 0; i < len; i++) {
|
|
2346
3341
|
const code = litVal.codePointAt(i);
|
|
@@ -2389,7 +3384,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2389
3384
|
itemPos = npV;
|
|
2390
3385
|
}
|
|
2391
3386
|
}
|
|
2392
|
-
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
3387
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
2393
3388
|
stmts.push(
|
|
2394
3389
|
...iterStmts,
|
|
2395
3390
|
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
|
|
@@ -2403,7 +3398,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2403
3398
|
function emitOptional(def, ctx, pos) {
|
|
2404
3399
|
const valV = v(ctx, "_opt");
|
|
2405
3400
|
const endV = v(ctx, "_opte");
|
|
2406
|
-
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
3401
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2407
3402
|
const ind0 = ind(ctx);
|
|
2408
3403
|
const stmts = [
|
|
2409
3404
|
...lblStmts,
|
|
@@ -2415,7 +3410,7 @@ function emitOptional(def, ctx, pos) {
|
|
|
2415
3410
|
function emitSepBy(_p, def, ctx, pos) {
|
|
2416
3411
|
const arrV = v(ctx, "_arr");
|
|
2417
3412
|
const curV = v(ctx, "_cur");
|
|
2418
|
-
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
3413
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
2419
3414
|
const stmts = [
|
|
2420
3415
|
`${ind(ctx)}const ${arrV} = []`,
|
|
2421
3416
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
@@ -2447,11 +3442,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2447
3442
|
);
|
|
2448
3443
|
sepAtPos = spV;
|
|
2449
3444
|
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}; `;
|
|
2450
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3445
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2451
3446
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
2452
3447
|
const npV = v(ctx, "_np");
|
|
2453
3448
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
2454
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3449
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2455
3450
|
stmts.push(
|
|
2456
3451
|
...nextStmts,
|
|
2457
3452
|
// item failed → unwind the separator too, back to the end of the last item
|
|
@@ -2464,11 +3459,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2464
3459
|
const spV = v(ctx, "_sp");
|
|
2465
3460
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
2466
3461
|
sepAtPos = spV;
|
|
2467
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3462
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2468
3463
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2469
3464
|
const npV = v(ctx, "_np");
|
|
2470
3465
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
2471
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3466
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2472
3467
|
stmts.push(
|
|
2473
3468
|
...nextStmts,
|
|
2474
3469
|
`${ind(ctx)}if (!${nextOk}) break`,
|
|
@@ -2485,10 +3480,10 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2485
3480
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
2486
3481
|
);
|
|
2487
3482
|
}
|
|
2488
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3483
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2489
3484
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2490
3485
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
2491
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
|
|
3486
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
2492
3487
|
stmts.push(
|
|
2493
3488
|
...nextStmts,
|
|
2494
3489
|
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
|
|
@@ -2546,11 +3541,12 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2546
3541
|
return { stmts, valueVar: valV, endVar: curV };
|
|
2547
3542
|
}
|
|
2548
3543
|
function emitNot(def, ctx, pos) {
|
|
2549
|
-
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3544
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
3545
|
+
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2550
3546
|
return {
|
|
2551
3547
|
stmts: [
|
|
2552
3548
|
...stmts,
|
|
2553
|
-
...emitIfFail(ctx, okVar, failBody(ctx,
|
|
3549
|
+
...emitIfFail(ctx, okVar, failBody(ctx, label2, pos))
|
|
2554
3550
|
],
|
|
2555
3551
|
valueVar: "null",
|
|
2556
3552
|
endVar: pos
|
|
@@ -2582,7 +3578,7 @@ function emitNode(def, ctx, pos) {
|
|
|
2582
3578
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2583
3579
|
stmts.push(...innerStmts);
|
|
2584
3580
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2585
|
-
stmts.push(...emitIfFail(ctx, `!${okVar}`,
|
|
3581
|
+
stmts.push(...emitIfFail(ctx, `!${okVar}`, propagateFailBody(ctx)));
|
|
2586
3582
|
let stV = "undefined";
|
|
2587
3583
|
if (clonesState) {
|
|
2588
3584
|
stV = v(ctx, "_nst");
|
|
@@ -2604,9 +3600,10 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2604
3600
|
const rv = v(ctx, "_rt");
|
|
2605
3601
|
const vv = v(ctx, "_rtv");
|
|
2606
3602
|
const ev = v(ctx, "_rte");
|
|
3603
|
+
const failStmt = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${rv}.span.start; _ctx._fx = ${rv}.expected; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : `return { ok: false, expected: ${rv}.expected, span: ${rv}.span }`;
|
|
2607
3604
|
const stmts = [
|
|
2608
3605
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
2609
|
-
...emitIfFail(ctx, `!${rv}.ok`,
|
|
3606
|
+
...emitIfFail(ctx, `!${rv}.ok`, failStmt),
|
|
2610
3607
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
2611
3608
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
2612
3609
|
];
|
|
@@ -2635,15 +3632,18 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2635
3632
|
}
|
|
2636
3633
|
const savedIndent = ctx.indent;
|
|
2637
3634
|
const savedFailLabel = ctx.failLabel;
|
|
3635
|
+
const savedRecord = ctx.recordFail;
|
|
2638
3636
|
ctx.indent = 1;
|
|
2639
3637
|
ctx.failLabel = "_pfail";
|
|
3638
|
+
ctx.recordFail = true;
|
|
2640
3639
|
const r = emit(resolved, ctx, "_pos");
|
|
2641
3640
|
ctx.indent = savedIndent;
|
|
2642
3641
|
ctx.failLabel = savedFailLabel;
|
|
3642
|
+
ctx.recordFail = savedRecord;
|
|
2643
3643
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
2644
3644
|
}
|
|
2645
3645
|
const fnName = ctx.namedParsers.get(p);
|
|
2646
|
-
return emitNamedFnCall(ctx, fnName, pos
|
|
3646
|
+
return emitNamedFnCall(ctx, fnName, pos);
|
|
2647
3647
|
}
|
|
2648
3648
|
function emitRecover(def, ctx, pos) {
|
|
2649
3649
|
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
@@ -2661,7 +3661,7 @@ function emitRecover(def, ctx, pos) {
|
|
|
2661
3661
|
`${ind0} if (${sentOk}) break`,
|
|
2662
3662
|
`${ind0} ${scanV}++`,
|
|
2663
3663
|
`${ind0} }`,
|
|
2664
|
-
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected:
|
|
3664
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: ${JSON.stringify(staticExpected(def.parser))} }`,
|
|
2665
3665
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2666
3666
|
`${ind0} ${valVar} = ${errV}`,
|
|
2667
3667
|
`${ind0} ${endVar} = ${scanV}`,
|
|
@@ -2819,11 +3819,14 @@ function emit(p, ctx, pos) {
|
|
|
2819
3819
|
ctx.namedParsers.set(innerParser, fnName);
|
|
2820
3820
|
const savedIndent = ctx.indent;
|
|
2821
3821
|
const savedFailLabel = ctx.failLabel;
|
|
3822
|
+
const savedRecord = ctx.recordFail;
|
|
2822
3823
|
ctx.indent = 1;
|
|
2823
3824
|
ctx.failLabel = "_pfail";
|
|
3825
|
+
ctx.recordFail = true;
|
|
2824
3826
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
2825
3827
|
ctx.indent = savedIndent;
|
|
2826
3828
|
ctx.failLabel = savedFailLabel;
|
|
3829
|
+
ctx.recordFail = savedRecord;
|
|
2827
3830
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
2828
3831
|
}
|
|
2829
3832
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -2834,7 +3837,9 @@ function emit(p, ctx, pos) {
|
|
|
2834
3837
|
stmts: [
|
|
2835
3838
|
`${ind(ctx)}const ${rv} = { ..._ctx, state: _mf[${evIdx}]() }`,
|
|
2836
3839
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
2837
|
-
|
|
3840
|
+
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3841
|
+
// and propagate the inner failure verbatim (interpreter parity).
|
|
3842
|
+
...emitIfFail(ctx, `${vv} === ${NAMED_FN_FAIL}`, propagateFailBody(ctx, rv)),
|
|
2838
3843
|
`${ind(ctx)}const ${ev} = ${NAMED_FN_END}`
|
|
2839
3844
|
],
|
|
2840
3845
|
valueVar: vv,
|
|
@@ -2935,7 +3940,7 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
2935
3940
|
const recursive = /* @__PURE__ */ new Set();
|
|
2936
3941
|
const descended = /* @__PURE__ */ new Set();
|
|
2937
3942
|
const active = /* @__PURE__ */ new Set();
|
|
2938
|
-
function
|
|
3943
|
+
function walk2(p) {
|
|
2939
3944
|
const def = p._def;
|
|
2940
3945
|
if (def.tag === "lazy") {
|
|
2941
3946
|
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
@@ -2952,13 +3957,13 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
2952
3957
|
return;
|
|
2953
3958
|
}
|
|
2954
3959
|
active.add(p);
|
|
2955
|
-
|
|
3960
|
+
walk2(resolved);
|
|
2956
3961
|
active.delete(p);
|
|
2957
3962
|
return;
|
|
2958
3963
|
}
|
|
2959
|
-
for (const child of childrenOf(def))
|
|
3964
|
+
for (const child of childrenOf(def)) walk2(child);
|
|
2960
3965
|
}
|
|
2961
|
-
for (const root of roots)
|
|
3966
|
+
for (const root of roots) walk2(root);
|
|
2962
3967
|
return { counts, recursive };
|
|
2963
3968
|
}
|
|
2964
3969
|
function compile(combinator, mapFnSources) {
|
|
@@ -2967,6 +3972,9 @@ function compile(combinator, mapFnSources) {
|
|
|
2967
3972
|
indent: 1,
|
|
2968
3973
|
regexDecls: [],
|
|
2969
3974
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3975
|
+
expectedDecls: [],
|
|
3976
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
3977
|
+
recordFail: true,
|
|
2970
3978
|
mapFns: [],
|
|
2971
3979
|
mapFnSrcs: [],
|
|
2972
3980
|
buildFns: [],
|
|
@@ -2988,6 +3996,7 @@ function compile(combinator, mapFnSources) {
|
|
|
2988
3996
|
const source = [
|
|
2989
3997
|
...emptyTlDecls,
|
|
2990
3998
|
...ctx.regexDecls,
|
|
3999
|
+
...ctx.expectedDecls,
|
|
2991
4000
|
"",
|
|
2992
4001
|
...namedPrelude,
|
|
2993
4002
|
ctx.namedFnDecls.join("\n\n"),
|
|
@@ -3000,6 +4009,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3000
4009
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3001
4010
|
...emptyTlDecls,
|
|
3002
4011
|
...ctx.regexDecls,
|
|
4012
|
+
...ctx.expectedDecls,
|
|
3003
4013
|
collatorDecl,
|
|
3004
4014
|
...namedPrelude,
|
|
3005
4015
|
...ctx.namedFnDecls.flatMap((decl, i) => i > 0 ? ["", decl] : [decl]),
|
|
@@ -3045,6 +4055,9 @@ function compileRuleMap(ruleMap) {
|
|
|
3045
4055
|
indent: 1,
|
|
3046
4056
|
regexDecls: [],
|
|
3047
4057
|
regexMap: /* @__PURE__ */ new Map(),
|
|
4058
|
+
expectedDecls: [],
|
|
4059
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4060
|
+
recordFail: true,
|
|
3048
4061
|
mapFns: [],
|
|
3049
4062
|
mapFnSrcs: [],
|
|
3050
4063
|
buildFns: [],
|
|
@@ -3073,6 +4086,7 @@ function compileRuleMap(ruleMap) {
|
|
|
3073
4086
|
const hoistedDecls = [
|
|
3074
4087
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
3075
4088
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4089
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3076
4090
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3077
4091
|
mfDecl,
|
|
3078
4092
|
buildDecl,
|
|
@@ -3114,12 +4128,13 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3114
4128
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
3115
4129
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3116
4130
|
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3117
|
-
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
4131
|
+
const needsWrapper = ctx.regexDecls.length > 0 || ctx.expectedDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
3118
4132
|
if (!needsWrapper) return innerFn;
|
|
3119
4133
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3120
4134
|
const hoistedDecls = [
|
|
3121
4135
|
emptyTlDecl,
|
|
3122
4136
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
4137
|
+
...ctx.expectedDecls.map((d) => ` ${d}`),
|
|
3123
4138
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3124
4139
|
mfDecl,
|
|
3125
4140
|
buildDecl,
|
|
@@ -3137,58 +4152,6 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3137
4152
|
].join("\n");
|
|
3138
4153
|
}
|
|
3139
4154
|
|
|
3140
|
-
// src/combinators/expect.ts
|
|
3141
|
-
function staticExpected(c) {
|
|
3142
|
-
const def = c._def;
|
|
3143
|
-
switch (def.tag) {
|
|
3144
|
-
case "literal":
|
|
3145
|
-
return [JSON.stringify(def.value)];
|
|
3146
|
-
case "regex":
|
|
3147
|
-
return [`/${def.source}/`];
|
|
3148
|
-
case "keywords":
|
|
3149
|
-
return def.words.map((w) => JSON.stringify(w));
|
|
3150
|
-
case "label":
|
|
3151
|
-
return [def.label];
|
|
3152
|
-
case "choice":
|
|
3153
|
-
return def.parsers.flatMap(staticExpected);
|
|
3154
|
-
case "sequence":
|
|
3155
|
-
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
3156
|
-
case "node":
|
|
3157
|
-
case "grammar":
|
|
3158
|
-
case "trivia":
|
|
3159
|
-
case "optional":
|
|
3160
|
-
case "many":
|
|
3161
|
-
case "oneOrMore":
|
|
3162
|
-
case "transform":
|
|
3163
|
-
case "not":
|
|
3164
|
-
return staticExpected(def.parser);
|
|
3165
|
-
case "lazy":
|
|
3166
|
-
return staticExpected(def.thunk());
|
|
3167
|
-
default:
|
|
3168
|
-
return [];
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
function expect(combinator, label2) {
|
|
3172
|
-
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
3173
|
-
const meta = {
|
|
3174
|
-
firstSet: combinator._meta.firstSet,
|
|
3175
|
-
canMatchNewline: combinator._meta.canMatchNewline,
|
|
3176
|
-
isTrivia: false
|
|
3177
|
-
};
|
|
3178
|
-
return {
|
|
3179
|
-
_tag: "expect",
|
|
3180
|
-
_meta: meta,
|
|
3181
|
-
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
3182
|
-
parse(input, pos, ctx) {
|
|
3183
|
-
const result = combinator.parse(input, pos, ctx);
|
|
3184
|
-
if (result.ok) return result;
|
|
3185
|
-
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
3186
|
-
ctx._errors?.push(error);
|
|
3187
|
-
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
3188
|
-
}
|
|
3189
|
-
};
|
|
3190
|
-
}
|
|
3191
|
-
|
|
3192
4155
|
// src/combinators/scanTo.ts
|
|
3193
4156
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
3194
4157
|
const meta = {
|
|
@@ -3269,11 +4232,11 @@ function balanced(open, close, options = {}) {
|
|
|
3269
4232
|
return combi;
|
|
3270
4233
|
}
|
|
3271
4234
|
function firstSetClassChars(c) {
|
|
3272
|
-
const
|
|
3273
|
-
if (
|
|
3274
|
-
if (
|
|
4235
|
+
const fs2 = c._meta.firstSet;
|
|
4236
|
+
if (fs2.kind === "empty") return [];
|
|
4237
|
+
if (fs2.kind !== "ranges") return null;
|
|
3275
4238
|
const out = [];
|
|
3276
|
-
for (const { lo, hi } of
|
|
4239
|
+
for (const { lo, hi } of fs2.ranges) {
|
|
3277
4240
|
if (hi - lo > 8) return null;
|
|
3278
4241
|
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3279
4242
|
}
|
|
@@ -3570,7 +4533,124 @@ function evaluateWordFactory(node2, scope, code) {
|
|
|
3570
4533
|
function evaluateExpr(node2, scope, code, mapFnSources) {
|
|
3571
4534
|
return exprToCombi(node2, scope, code, mapFnSources);
|
|
3572
4535
|
}
|
|
3573
|
-
function
|
|
4536
|
+
function evaluateCombinatorArray(node2, scope, code) {
|
|
4537
|
+
if (node2.type !== "ArrayExpression") return null;
|
|
4538
|
+
const val = anyValue(node2, scope, code, []);
|
|
4539
|
+
if (!Array.isArray(val) || val.length === 0) return null;
|
|
4540
|
+
if (!val.every(isCombinator)) return null;
|
|
4541
|
+
return val;
|
|
4542
|
+
}
|
|
4543
|
+
function asFragmentFactory(expr) {
|
|
4544
|
+
if (expr.type !== "ArrowFunctionExpression" && expr.type !== "FunctionExpression" && expr.type !== "FunctionDeclaration") return null;
|
|
4545
|
+
const fn = expr;
|
|
4546
|
+
return factoryBodyAndReturn(fn) ? fn : null;
|
|
4547
|
+
}
|
|
4548
|
+
var unwrapParen = (e) => e && e.type === "ParenthesizedExpression" ? e.expression : e;
|
|
4549
|
+
function factoryBodyAndReturn(fn) {
|
|
4550
|
+
const body = fn.body;
|
|
4551
|
+
if (!body) return null;
|
|
4552
|
+
const b = unwrapParen(body);
|
|
4553
|
+
if (b.type === "ObjectExpression") return { statements: [], retObj: b };
|
|
4554
|
+
if (b.type === "BlockStatement") {
|
|
4555
|
+
const statements = [];
|
|
4556
|
+
for (const stmt of b.body) {
|
|
4557
|
+
if (stmt.type === "ReturnStatement") {
|
|
4558
|
+
const a = unwrapParen(stmt.argument);
|
|
4559
|
+
if (!a || a.type !== "ObjectExpression") return null;
|
|
4560
|
+
return { statements, retObj: a };
|
|
4561
|
+
}
|
|
4562
|
+
if (stmt.type === "VariableDeclaration") statements.push(stmt);
|
|
4563
|
+
else return null;
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
return null;
|
|
4567
|
+
}
|
|
4568
|
+
function spreadFactoryCall(spread, resolve) {
|
|
4569
|
+
const call = spread.argument;
|
|
4570
|
+
if (!call || call.type !== "CallExpression") return null;
|
|
4571
|
+
if (!call.callee || call.callee.type !== "Identifier" || !call.callee.name) return null;
|
|
4572
|
+
const resolved = resolve(call.callee.name);
|
|
4573
|
+
return resolved ? { fn: resolved.fn, args: call.arguments ?? [], code: resolved.code } : null;
|
|
4574
|
+
}
|
|
4575
|
+
function factoryCallScope(fn, args, outer) {
|
|
4576
|
+
const s = new Map(outer);
|
|
4577
|
+
const params = fn.params ?? [];
|
|
4578
|
+
for (let i = 0; i < params.length; i++) {
|
|
4579
|
+
const pn = params[i];
|
|
4580
|
+
if (!pn || pn.type !== "Identifier" || !pn.name) continue;
|
|
4581
|
+
const arg = args[i];
|
|
4582
|
+
if (arg && arg.type === "Identifier" && arg.name && outer.has(arg.name)) {
|
|
4583
|
+
s.set(pn.name, outer.get(arg.name));
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
return s;
|
|
4587
|
+
}
|
|
4588
|
+
function propKey(prop) {
|
|
4589
|
+
if (prop.type !== "Property" || prop.computed || !prop.key) return null;
|
|
4590
|
+
if (prop.key.type === "Identifier") return prop.key.name ?? null;
|
|
4591
|
+
if (prop.key.type === "Literal") return String(prop.key.value);
|
|
4592
|
+
return null;
|
|
4593
|
+
}
|
|
4594
|
+
function collectRuleKeys(retObj, resolve, seen) {
|
|
4595
|
+
const out = [];
|
|
4596
|
+
for (const prop of retObj.properties) {
|
|
4597
|
+
if (prop.type === "SpreadElement") {
|
|
4598
|
+
const sf = spreadFactoryCall(prop, resolve);
|
|
4599
|
+
if (!sf || seen.has(sf.fn)) return null;
|
|
4600
|
+
const br = factoryBodyAndReturn(sf.fn);
|
|
4601
|
+
if (!br) return null;
|
|
4602
|
+
const nested = collectRuleKeys(br.retObj, resolve, new Set(seen).add(sf.fn));
|
|
4603
|
+
if (!nested) return null;
|
|
4604
|
+
out.push(...nested);
|
|
4605
|
+
continue;
|
|
4606
|
+
}
|
|
4607
|
+
const key = propKey(prop);
|
|
4608
|
+
if (!key) return null;
|
|
4609
|
+
out.push(key);
|
|
4610
|
+
}
|
|
4611
|
+
return out;
|
|
4612
|
+
}
|
|
4613
|
+
function flattenRuleEntries(retObj, scope, code, resolve, seen) {
|
|
4614
|
+
const out = [];
|
|
4615
|
+
for (const prop of retObj.properties) {
|
|
4616
|
+
if (prop.type === "SpreadElement") {
|
|
4617
|
+
const sf = spreadFactoryCall(prop, resolve);
|
|
4618
|
+
if (!sf || seen.has(sf.fn)) return null;
|
|
4619
|
+
const br = factoryBodyAndReturn(sf.fn);
|
|
4620
|
+
if (!br) return null;
|
|
4621
|
+
const fragScope = factoryCallScope(sf.fn, sf.args, scope);
|
|
4622
|
+
if (!evalBodyStatements(br.statements, fragScope, sf.code)) return null;
|
|
4623
|
+
const nested = flattenRuleEntries(br.retObj, fragScope, sf.code, resolve, new Set(seen).add(sf.fn));
|
|
4624
|
+
if (!nested) return null;
|
|
4625
|
+
out.push(...nested);
|
|
4626
|
+
continue;
|
|
4627
|
+
}
|
|
4628
|
+
const key = propKey(prop);
|
|
4629
|
+
if (!key) return null;
|
|
4630
|
+
out.push({ key, value: prop.value, scope, code });
|
|
4631
|
+
}
|
|
4632
|
+
return out;
|
|
4633
|
+
}
|
|
4634
|
+
function evalBodyStatements(statements, scope, code) {
|
|
4635
|
+
const bodyMfs = [];
|
|
4636
|
+
for (const stmt of statements) {
|
|
4637
|
+
for (const d of stmt.declarations) {
|
|
4638
|
+
const decl = d;
|
|
4639
|
+
if (!decl.init) return false;
|
|
4640
|
+
const id = decl.id;
|
|
4641
|
+
if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return false;
|
|
4642
|
+
const name = id.name;
|
|
4643
|
+
const before = bodyMfs.length;
|
|
4644
|
+
const val = anyValue(decl.init, scope, code, bodyMfs);
|
|
4645
|
+
if (val === null) return false;
|
|
4646
|
+
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
4647
|
+
if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
4648
|
+
else scope.set(name, val);
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
return true;
|
|
4652
|
+
}
|
|
4653
|
+
function evaluateParserFactory(factoryNode, scope, code, mapFnSources, resolveFactory = () => null) {
|
|
3574
4654
|
if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
|
|
3575
4655
|
const factory = factoryNode;
|
|
3576
4656
|
const params = factory.params;
|
|
@@ -3602,40 +4682,21 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
|
3602
4682
|
if (!returnExpr) return null;
|
|
3603
4683
|
const retObj = returnExpr.type === "ParenthesizedExpression" ? returnExpr.expression : returnExpr;
|
|
3604
4684
|
if (retObj.type !== "ObjectExpression") return null;
|
|
4685
|
+
const keys = collectRuleKeys(retObj, resolveFactory, /* @__PURE__ */ new Set());
|
|
4686
|
+
if (!keys) return null;
|
|
3605
4687
|
const ruleRefs = /* @__PURE__ */ new Map();
|
|
3606
|
-
for (const
|
|
3607
|
-
if (
|
|
3608
|
-
if (prop.computed) return null;
|
|
3609
|
-
const p = prop;
|
|
3610
|
-
const key = p.key.type === "Identifier" ? p.key.name : p.key.type === "Literal" ? String(p.key.value) : null;
|
|
3611
|
-
if (!key) return null;
|
|
3612
|
-
ruleRefs.set(key, ref());
|
|
4688
|
+
for (const key of keys) {
|
|
4689
|
+
if (!ruleRefs.has(key)) ruleRefs.set(key, ref());
|
|
3613
4690
|
}
|
|
3614
4691
|
const localScope = new Map(scope);
|
|
3615
4692
|
localScope.set(proxyName, Object.fromEntries(ruleRefs));
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
const name = id.name;
|
|
3624
|
-
const before = bodyMfs.length;
|
|
3625
|
-
const val = anyValue(decl.init, localScope, code, bodyMfs);
|
|
3626
|
-
if (val === null) return null;
|
|
3627
|
-
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
3628
|
-
if (isCombinator(val)) {
|
|
3629
|
-
localScope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
3630
|
-
} else {
|
|
3631
|
-
localScope.set(name, val);
|
|
3632
|
-
}
|
|
3633
|
-
}
|
|
3634
|
-
}
|
|
3635
|
-
for (const prop of retObj.properties) {
|
|
3636
|
-
const p = prop;
|
|
3637
|
-
const key = p.key.type === "Identifier" ? p.key.name : String(p.key.value);
|
|
3638
|
-
const val = anyValue(p.value, localScope, code, mapFnSources);
|
|
4693
|
+
if (!evalBodyStatements(statements, localScope, code)) return null;
|
|
4694
|
+
const entries = flattenRuleEntries(retObj, localScope, code, resolveFactory, /* @__PURE__ */ new Set());
|
|
4695
|
+
if (!entries) return null;
|
|
4696
|
+
const finalByKey = /* @__PURE__ */ new Map();
|
|
4697
|
+
for (const e of entries) finalByKey.set(e.key, e);
|
|
4698
|
+
for (const [key, e] of finalByKey) {
|
|
4699
|
+
const val = anyValue(e.value, e.scope, e.code, mapFnSources);
|
|
3639
4700
|
if (!isCombinator(val)) return null;
|
|
3640
4701
|
ruleRefs.get(key).define(val);
|
|
3641
4702
|
}
|
|
@@ -3693,6 +4754,40 @@ function referencesAny(node2, names, scope) {
|
|
|
3693
4754
|
|
|
3694
4755
|
// src/plugin/index.ts
|
|
3695
4756
|
var PARSEMAN_MODULE = "parseman";
|
|
4757
|
+
var _fragmentResolver = null;
|
|
4758
|
+
function getFragmentResolver() {
|
|
4759
|
+
return _fragmentResolver ??= new ResolverFactory({
|
|
4760
|
+
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"],
|
|
4761
|
+
// TS/NodeNext style: a `./x.js` import may point at `./x.ts` source.
|
|
4762
|
+
extensionAlias: {
|
|
4763
|
+
".js": [".ts", ".tsx", ".js", ".jsx"],
|
|
4764
|
+
".mjs": [".mts", ".mjs"],
|
|
4765
|
+
".cjs": [".cts", ".cjs"]
|
|
4766
|
+
},
|
|
4767
|
+
conditionNames: ["source", "development", "import", "require", "default"],
|
|
4768
|
+
mainFields: ["source", "module", "main"]
|
|
4769
|
+
});
|
|
4770
|
+
}
|
|
4771
|
+
var _moduleParseCache = /* @__PURE__ */ new Map();
|
|
4772
|
+
function parseModuleCached(filePath) {
|
|
4773
|
+
let mtimeMs;
|
|
4774
|
+
try {
|
|
4775
|
+
mtimeMs = fs.statSync(filePath).mtimeMs;
|
|
4776
|
+
} catch {
|
|
4777
|
+
return null;
|
|
4778
|
+
}
|
|
4779
|
+
const hit = _moduleParseCache.get(filePath);
|
|
4780
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
|
|
4781
|
+
let parsed = null;
|
|
4782
|
+
try {
|
|
4783
|
+
const src = fs.readFileSync(filePath, "utf8");
|
|
4784
|
+
const r = parseSync(filePath, src);
|
|
4785
|
+
if (r.errors.length === 0) parsed = { body: r.program.body, src };
|
|
4786
|
+
} catch {
|
|
4787
|
+
}
|
|
4788
|
+
_moduleParseCache.set(filePath, { mtimeMs, parsed });
|
|
4789
|
+
return parsed;
|
|
4790
|
+
}
|
|
3696
4791
|
var plugin_default = createUnplugin((opts = {}) => ({
|
|
3697
4792
|
name: "parseman",
|
|
3698
4793
|
// Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
|
|
@@ -3728,15 +4823,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3728
4823
|
const body = result.program.body;
|
|
3729
4824
|
const macroImports = [];
|
|
3730
4825
|
const allNames = /* @__PURE__ */ new Set();
|
|
4826
|
+
const importBindings = /* @__PURE__ */ new Map();
|
|
3731
4827
|
for (const stmt of body) {
|
|
3732
4828
|
if (stmt.type !== "ImportDeclaration") continue;
|
|
3733
4829
|
const s = stmt;
|
|
3734
|
-
|
|
3735
|
-
const isMacro = s.attributes.some((a) => {
|
|
4830
|
+
const isMacro = moduleAliases.has(s.source.value) && s.attributes.some((a) => {
|
|
3736
4831
|
const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
|
|
3737
4832
|
return key === "type" && a.value.value === "macro";
|
|
3738
4833
|
});
|
|
3739
|
-
if (!isMacro)
|
|
4834
|
+
if (!isMacro) {
|
|
4835
|
+
for (const spec of s.specifiers) {
|
|
4836
|
+
if (spec.type === "ImportSpecifier") {
|
|
4837
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
4838
|
+
importBindings.set(spec.local.name, { source: s.source.value, imported });
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
continue;
|
|
4842
|
+
}
|
|
3740
4843
|
const names = /* @__PURE__ */ new Set();
|
|
3741
4844
|
for (const spec of s.specifiers) {
|
|
3742
4845
|
if (spec.type === "ImportSpecifier") names.add(spec.local.name);
|
|
@@ -3746,6 +4849,63 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3746
4849
|
}
|
|
3747
4850
|
if (macroImports.length === 0) return null;
|
|
3748
4851
|
const scope = /* @__PURE__ */ new Map();
|
|
4852
|
+
const factories = /* @__PURE__ */ new Map();
|
|
4853
|
+
const resolver = getFragmentResolver();
|
|
4854
|
+
const importedFactoryCache = /* @__PURE__ */ new Map();
|
|
4855
|
+
const OUTPUT_DIR = /([\\/])(dist|lib|build|out|output|compiled|cjs|esm|es)([\\/])/;
|
|
4856
|
+
const resolveSourcePath = (spec) => {
|
|
4857
|
+
let resolved = null;
|
|
4858
|
+
try {
|
|
4859
|
+
resolved = resolver.resolveFileSync(id, spec).path ?? null;
|
|
4860
|
+
} catch {
|
|
4861
|
+
}
|
|
4862
|
+
if (!resolved) return null;
|
|
4863
|
+
if (OUTPUT_DIR.test(resolved)) {
|
|
4864
|
+
const srcCandidate = resolved.replace(OUTPUT_DIR, "$1src$3").replace(/\.(c|m)?js$/, ".ts");
|
|
4865
|
+
try {
|
|
4866
|
+
if (fs.statSync(srcCandidate).isFile()) return srcCandidate;
|
|
4867
|
+
} catch {
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
return resolved;
|
|
4871
|
+
};
|
|
4872
|
+
const resolveImportedFactory = (name) => {
|
|
4873
|
+
if (importedFactoryCache.has(name)) return importedFactoryCache.get(name);
|
|
4874
|
+
let result2 = null;
|
|
4875
|
+
const binding = importBindings.get(name);
|
|
4876
|
+
if (binding) {
|
|
4877
|
+
const filePath = resolveSourcePath(binding.source);
|
|
4878
|
+
const mod = filePath ? parseModuleCached(filePath) : null;
|
|
4879
|
+
if (mod) {
|
|
4880
|
+
for (const st of mod.body) {
|
|
4881
|
+
if (st.type !== "ExportNamedDeclaration" || !st.declaration) continue;
|
|
4882
|
+
const decl = st.declaration;
|
|
4883
|
+
if (decl.type === "VariableDeclaration") {
|
|
4884
|
+
for (const d of decl.declarations ?? []) {
|
|
4885
|
+
if (d.id?.type === "Identifier" && d.id.name === binding.imported && d.init) {
|
|
4886
|
+
const fac = asFragmentFactory(d.init);
|
|
4887
|
+
if (fac) {
|
|
4888
|
+
result2 = { fn: fac, code: mod.src };
|
|
4889
|
+
break;
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
} else if (decl.type === "FunctionDeclaration" && decl.id?.type === "Identifier" && decl.id.name === binding.imported) {
|
|
4894
|
+
const fac = asFragmentFactory(decl);
|
|
4895
|
+
if (fac) result2 = { fn: fac, code: mod.src };
|
|
4896
|
+
}
|
|
4897
|
+
if (result2) break;
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
}
|
|
4901
|
+
importedFactoryCache.set(name, result2);
|
|
4902
|
+
return result2;
|
|
4903
|
+
};
|
|
4904
|
+
const resolveFactory = (n) => {
|
|
4905
|
+
const local = factories.get(n);
|
|
4906
|
+
if (local) return { fn: local, code };
|
|
4907
|
+
return resolveImportedFactory(n);
|
|
4908
|
+
};
|
|
3749
4909
|
const replacements = [];
|
|
3750
4910
|
const warnings = [];
|
|
3751
4911
|
let anyUnresolved = false;
|
|
@@ -3766,7 +4926,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3766
4926
|
warn(init.start, `${label2}: rules() needs a factory argument`);
|
|
3767
4927
|
return null;
|
|
3768
4928
|
}
|
|
3769
|
-
const ruleMap = evaluateParserFactory(factoryArg, scope, code, []);
|
|
4929
|
+
const ruleMap = evaluateParserFactory(factoryArg, scope, code, [], resolveFactory);
|
|
3770
4930
|
if (!ruleMap) {
|
|
3771
4931
|
warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
|
|
3772
4932
|
return null;
|
|
@@ -3864,6 +5024,11 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3864
5024
|
});
|
|
3865
5025
|
continue;
|
|
3866
5026
|
}
|
|
5027
|
+
const frag = asFragmentFactory(init);
|
|
5028
|
+
if (frag) {
|
|
5029
|
+
factories.set(varName, frag);
|
|
5030
|
+
continue;
|
|
5031
|
+
}
|
|
3867
5032
|
const mapFnSources = [];
|
|
3868
5033
|
const parser2 = evaluateExpr(init, scope, code, mapFnSources);
|
|
3869
5034
|
if (parser2 === null) {
|
|
@@ -3873,6 +5038,12 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3873
5038
|
scope.set(varName, wordFactory);
|
|
3874
5039
|
continue;
|
|
3875
5040
|
}
|
|
5041
|
+
const combiArray = evaluateCombinatorArray(init, scope, code);
|
|
5042
|
+
if (combiArray) {
|
|
5043
|
+
;
|
|
5044
|
+
scope.set(varName, combiArray);
|
|
5045
|
+
continue;
|
|
5046
|
+
}
|
|
3876
5047
|
warn(init.start, `"${varName}" references a parseman macro import but isn't a statically-evaluable combinator`);
|
|
3877
5048
|
continue;
|
|
3878
5049
|
}
|