parseman 0.12.0 → 0.14.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/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +1 -1
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/regex-analyze.d.ts +6 -0
- package/dist/combinators/regex-analyze.d.ts.map +1 -0
- package/dist/combinators/regex.d.ts +16 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/compiler/build-arity.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +45 -0
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/inline-build.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +44 -0
- package/dist/compiler/linker.d.ts.map +1 -0
- package/dist/compiler/scannable-run.d.ts +80 -2
- package/dist/compiler/scannable-run.d.ts.map +1 -1
- package/dist/cst/offset-model.d.ts +119 -0
- package/dist/cst/offset-model.d.ts.map +1 -0
- package/dist/cst/relative-spans.d.ts +56 -0
- package/dist/cst/relative-spans.d.ts.map +1 -0
- package/dist/cst/types.d.ts +1 -1
- package/dist/functional/doc.d.ts +26 -6
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/functional/run.d.ts +63 -0
- package/dist/functional/run.d.ts.map +1 -0
- package/dist/index.cjs +1613 -148
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1613 -148
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +1520 -187
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1520 -187
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +12 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -4
- package/dist/combinators/lazy.d.ts +0 -14
- package/dist/combinators/lazy.d.ts.map +0 -1
package/dist/plugin/index.cjs
CHANGED
|
@@ -34,8 +34,10 @@ __export(plugin_exports, {
|
|
|
34
34
|
transformMacro: () => transformMacro
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(plugin_exports);
|
|
37
|
+
var fs = __toESM(require("node:fs"), 1);
|
|
37
38
|
var import_unplugin = require("unplugin");
|
|
38
39
|
var import_oxc_parser = require("oxc-parser");
|
|
40
|
+
var import_oxc_resolver = require("oxc-resolver");
|
|
39
41
|
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
40
42
|
|
|
41
43
|
// src/combinators/first-set.ts
|
|
@@ -321,6 +323,36 @@ function literal(value, opts = {}) {
|
|
|
321
323
|
}
|
|
322
324
|
|
|
323
325
|
// src/combinators/regex.ts
|
|
326
|
+
var firstSetAnalyzer = null;
|
|
327
|
+
function registerRegexAnalyzer(analyzer) {
|
|
328
|
+
firstSetAnalyzer = analyzer;
|
|
329
|
+
}
|
|
330
|
+
var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
|
|
331
|
+
function regex(pattern, flags = "") {
|
|
332
|
+
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
333
|
+
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
334
|
+
const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
335
|
+
const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
|
|
336
|
+
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
337
|
+
return {
|
|
338
|
+
_tag: "regex",
|
|
339
|
+
_meta: meta,
|
|
340
|
+
_def: { tag: "regex", source, flags: resolvedFlags },
|
|
341
|
+
parse(input, pos, ctx) {
|
|
342
|
+
anchored.lastIndex = pos;
|
|
343
|
+
const m = anchored.exec(input);
|
|
344
|
+
if (m === null) {
|
|
345
|
+
return failAt(ctx, [`/${source}/`], pos);
|
|
346
|
+
}
|
|
347
|
+
const span = { start: pos, end: pos + m[0].length };
|
|
348
|
+
const leaf = { _tag: "leaf", value: m[0], span };
|
|
349
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
350
|
+
return { ok: true, value: m[0], span };
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/combinators/regex-analyze.ts
|
|
324
356
|
var import_regexp_tree = __toESM(require("regexp-tree"), 1);
|
|
325
357
|
function firstSetFromRegex(pattern) {
|
|
326
358
|
try {
|
|
@@ -363,15 +395,15 @@ function extractFirstSet(node2) {
|
|
|
363
395
|
case "Alternative": {
|
|
364
396
|
const exprs = node2.expressions;
|
|
365
397
|
if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
|
|
366
|
-
let
|
|
398
|
+
let fs2 = empty();
|
|
367
399
|
let canNL = false;
|
|
368
400
|
for (const expr of exprs) {
|
|
369
401
|
const r = extractFirstSet(expr);
|
|
370
|
-
|
|
402
|
+
fs2 = union(fs2, r.firstSet);
|
|
371
403
|
canNL = canNL || r.canMatchNewline;
|
|
372
404
|
if (!canBeEmpty(expr)) break;
|
|
373
405
|
}
|
|
374
|
-
return { firstSet:
|
|
406
|
+
return { firstSet: fs2, canMatchNewline: canNL };
|
|
375
407
|
}
|
|
376
408
|
case "Char": {
|
|
377
409
|
const kind = node2.kind;
|
|
@@ -398,22 +430,22 @@ function extractFirstSet(node2) {
|
|
|
398
430
|
}
|
|
399
431
|
case "CharacterClass": {
|
|
400
432
|
const expressions = node2.expressions;
|
|
401
|
-
let
|
|
433
|
+
let fs2 = empty();
|
|
402
434
|
let canNL = false;
|
|
403
435
|
for (const expr of expressions) {
|
|
404
436
|
if (expr.type === "ClassRange") {
|
|
405
437
|
const from = expr.from.codePoint;
|
|
406
438
|
const to = expr.to.codePoint;
|
|
407
|
-
|
|
439
|
+
fs2 = union(fs2, fromRange(from, to));
|
|
408
440
|
if (from <= 10 && 10 <= to) canNL = true;
|
|
409
441
|
} else if (expr.type === "Char") {
|
|
410
442
|
const r = extractFirstSet(expr);
|
|
411
|
-
|
|
443
|
+
fs2 = union(fs2, r.firstSet);
|
|
412
444
|
canNL = canNL || r.canMatchNewline;
|
|
413
445
|
}
|
|
414
446
|
}
|
|
415
447
|
if (node2.negative) return { firstSet: any(), canMatchNewline: true };
|
|
416
|
-
return { firstSet:
|
|
448
|
+
return { firstSet: fs2, canMatchNewline: canNL };
|
|
417
449
|
}
|
|
418
450
|
case "Repetition": {
|
|
419
451
|
return extractFirstSet(node2.expression);
|
|
@@ -425,40 +457,6 @@ function extractFirstSet(node2) {
|
|
|
425
457
|
return { firstSet: any(), canMatchNewline: true };
|
|
426
458
|
}
|
|
427
459
|
}
|
|
428
|
-
function optimizeRegex(source, flags) {
|
|
429
|
-
try {
|
|
430
|
-
const result = import_regexp_tree.default.optimize(`/${source}/${flags}`);
|
|
431
|
-
const str = result.toString();
|
|
432
|
-
const lastSlash = str.lastIndexOf("/");
|
|
433
|
-
return str.slice(1, lastSlash);
|
|
434
|
-
} catch {
|
|
435
|
-
return source;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
function regex(pattern, flags = "") {
|
|
439
|
-
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
440
|
-
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
441
|
-
const optimizedSource = optimizeRegex(source, resolvedFlags);
|
|
442
|
-
const anchored = new RegExp(optimizedSource, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
443
|
-
const { firstSet, canMatchNewline } = firstSetFromRegex(source);
|
|
444
|
-
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
445
|
-
return {
|
|
446
|
-
_tag: "regex",
|
|
447
|
-
_meta: meta,
|
|
448
|
-
_def: { tag: "regex", source, flags: resolvedFlags, optimizedSource },
|
|
449
|
-
parse(input, pos, ctx) {
|
|
450
|
-
anchored.lastIndex = pos;
|
|
451
|
-
const m = anchored.exec(input);
|
|
452
|
-
if (m === null) {
|
|
453
|
-
return failAt(ctx, [`/${source}/`], pos);
|
|
454
|
-
}
|
|
455
|
-
const span = { start: pos, end: pos + m[0].length };
|
|
456
|
-
const leaf = { _tag: "leaf", value: m[0], span };
|
|
457
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
458
|
-
return { ok: true, value: m[0], span };
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
460
|
|
|
463
461
|
// src/combinators/keywords.ts
|
|
464
462
|
function word(str, boundary = "_0-9A-Za-z") {
|
|
@@ -722,10 +720,10 @@ function codesToFirstSet(codes) {
|
|
|
722
720
|
ranges.push({ lo, hi });
|
|
723
721
|
return { kind: "ranges", ranges };
|
|
724
722
|
}
|
|
725
|
-
function inFirstSet(code,
|
|
726
|
-
if (
|
|
727
|
-
if (
|
|
728
|
-
for (const r of
|
|
723
|
+
function inFirstSet(code, fs2) {
|
|
724
|
+
if (fs2.kind === "any") return true;
|
|
725
|
+
if (fs2.kind === "empty") return false;
|
|
726
|
+
for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
729
727
|
return false;
|
|
730
728
|
}
|
|
731
729
|
function areDisjoint(sets) {
|
|
@@ -1158,12 +1156,14 @@ function confirmedBuildArity(src) {
|
|
|
1158
1156
|
return parts.length;
|
|
1159
1157
|
}
|
|
1160
1158
|
function buildReadsTrivia(def) {
|
|
1159
|
+
if (!def.build) return true;
|
|
1161
1160
|
const src = def.buildSrc ?? def.build.toString();
|
|
1162
1161
|
const arity = confirmedBuildArity(src);
|
|
1163
1162
|
if (arity === null) return true;
|
|
1164
1163
|
return arity >= 4;
|
|
1165
1164
|
}
|
|
1166
1165
|
function buildReadsState(def) {
|
|
1166
|
+
if (!def.build) return true;
|
|
1167
1167
|
const src = def.buildSrc ?? def.build.toString();
|
|
1168
1168
|
const arity = confirmedBuildArity(src);
|
|
1169
1169
|
if (arity === null) return true;
|
|
@@ -1182,8 +1182,8 @@ function node(type, combinator, build, opts) {
|
|
|
1182
1182
|
};
|
|
1183
1183
|
const collapse = opts?.collapse === true;
|
|
1184
1184
|
const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
|
|
1185
|
-
const capturesTrivia = buildReadsTrivia(def);
|
|
1186
|
-
const clonesState = buildReadsState(def);
|
|
1185
|
+
const capturesTrivia = build ? buildReadsTrivia(def) : true;
|
|
1186
|
+
const clonesState = build ? buildReadsState(def) : true;
|
|
1187
1187
|
return {
|
|
1188
1188
|
_tag: "node",
|
|
1189
1189
|
_meta: meta,
|
|
@@ -1194,13 +1194,8 @@ function node(type, combinator, build, opts) {
|
|
|
1194
1194
|
const r = combinator.parse(input, pos, ctx);
|
|
1195
1195
|
const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
1196
1196
|
if (!r.ok) return r;
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1199
|
-
rawChildren,
|
|
1200
|
-
r.span,
|
|
1201
|
-
triviaLog,
|
|
1202
|
-
clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
|
|
1203
|
-
);
|
|
1197
|
+
const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
1198
|
+
const built = collapse && children.length === 1 ? collapseChild(children[0]) : build ? build(children, rawChildren, r.span, triviaLog, st) : ctx.build ? ctx.build(type, children, rawChildren, r.span, triviaLog, st) : { _tag: "node", type, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
|
|
1204
1199
|
const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
|
|
1205
1200
|
const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
|
|
1206
1201
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
@@ -1343,8 +1338,14 @@ function staticExpected(c) {
|
|
|
1343
1338
|
case "transform":
|
|
1344
1339
|
case "not":
|
|
1345
1340
|
return staticExpected(def.parser);
|
|
1346
|
-
case "lazy":
|
|
1347
|
-
|
|
1341
|
+
case "lazy": {
|
|
1342
|
+
const name = c._ruleName;
|
|
1343
|
+
try {
|
|
1344
|
+
return staticExpected(def.thunk());
|
|
1345
|
+
} catch {
|
|
1346
|
+
return name ? [name] : [];
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1348
1349
|
default:
|
|
1349
1350
|
return [];
|
|
1350
1351
|
}
|
|
@@ -1374,8 +1375,29 @@ function expect(combinator, label2) {
|
|
|
1374
1375
|
var BACKSLASH = 92;
|
|
1375
1376
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1376
1377
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1378
|
+
var SPACE_RANGES = [
|
|
1379
|
+
[9, 13],
|
|
1380
|
+
[32, 32],
|
|
1381
|
+
[160, 160],
|
|
1382
|
+
[5760, 5760],
|
|
1383
|
+
[8192, 8202],
|
|
1384
|
+
[8232, 8232],
|
|
1385
|
+
[8233, 8233],
|
|
1386
|
+
[8239, 8239],
|
|
1387
|
+
[8287, 8287],
|
|
1388
|
+
[12288, 12288],
|
|
1389
|
+
[65279, 65279]
|
|
1390
|
+
];
|
|
1377
1391
|
function shorthandRanges(ch) {
|
|
1378
|
-
|
|
1392
|
+
if (ch === "d") return [[48, 57]];
|
|
1393
|
+
if (ch === "s") return SPACE_RANGES;
|
|
1394
|
+
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1395
|
+
}
|
|
1396
|
+
function readUnicodeEscape(body, i) {
|
|
1397
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
1398
|
+
const hex = body.slice(i + 2, i + 6);
|
|
1399
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
1400
|
+
return { cp: Number.parseInt(hex, 16), next: i + 6 };
|
|
1379
1401
|
}
|
|
1380
1402
|
function parseClassRanges(body) {
|
|
1381
1403
|
const ranges = [];
|
|
@@ -1384,11 +1406,16 @@ function parseClassRanges(body) {
|
|
|
1384
1406
|
const ch = body[i];
|
|
1385
1407
|
if (ch === void 0) return null;
|
|
1386
1408
|
if (ch === "\\") {
|
|
1409
|
+
const uni = readUnicodeEscape(body, i);
|
|
1410
|
+
if (uni) {
|
|
1411
|
+
i = uni.next;
|
|
1412
|
+
return { cp: uni.cp };
|
|
1413
|
+
}
|
|
1387
1414
|
const e = body[i + 1];
|
|
1388
1415
|
if (e === void 0) return null;
|
|
1389
1416
|
i += 2;
|
|
1390
1417
|
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1391
|
-
if (e === "d" || e === "w") return { set: shorthandRanges(e) };
|
|
1418
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
1392
1419
|
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1393
1420
|
return { cp: e.codePointAt(0) };
|
|
1394
1421
|
}
|
|
@@ -1416,10 +1443,16 @@ function parseClassRanges(body) {
|
|
|
1416
1443
|
function classToRanges(cls) {
|
|
1417
1444
|
if (cls === "\\d") return shorthandRanges("d");
|
|
1418
1445
|
if (cls === "\\w") return shorthandRanges("w");
|
|
1446
|
+
if (cls === "\\s") return shorthandRanges("s");
|
|
1419
1447
|
const body = cls.slice(1, -1);
|
|
1420
1448
|
if (body.startsWith("^")) return null;
|
|
1421
1449
|
return parseClassRanges(body);
|
|
1422
1450
|
}
|
|
1451
|
+
var foldEq = (cVar, cp) => {
|
|
1452
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
|
|
1453
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
|
|
1454
|
+
return `${cVar} === ${cp}`;
|
|
1455
|
+
};
|
|
1423
1456
|
function literalCodePoints(frag) {
|
|
1424
1457
|
const out = [];
|
|
1425
1458
|
let i = 0;
|
|
@@ -1466,22 +1499,612 @@ function parseStringShape(source) {
|
|
|
1466
1499
|
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1467
1500
|
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1468
1501
|
}
|
|
1502
|
+
function parseSeqParts(source) {
|
|
1503
|
+
const parts = [];
|
|
1504
|
+
let lit = [];
|
|
1505
|
+
const flush = () => {
|
|
1506
|
+
if (lit.length) {
|
|
1507
|
+
parts.push({ part: "lit", cps: lit, optional: false });
|
|
1508
|
+
lit = [];
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
const runFrom = (ranges, negated, q) => {
|
|
1512
|
+
if (q === "+") {
|
|
1513
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
|
|
1514
|
+
return 1;
|
|
1515
|
+
}
|
|
1516
|
+
if (q === "*") {
|
|
1517
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
|
|
1518
|
+
return 1;
|
|
1519
|
+
}
|
|
1520
|
+
if (q === "?") {
|
|
1521
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
|
|
1522
|
+
return 1;
|
|
1523
|
+
}
|
|
1524
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
|
|
1525
|
+
return 0;
|
|
1526
|
+
};
|
|
1527
|
+
let i = 0;
|
|
1528
|
+
while (i < source.length) {
|
|
1529
|
+
const ch = source[i];
|
|
1530
|
+
if (ch === "[") {
|
|
1531
|
+
let k = i + 1;
|
|
1532
|
+
const neg = source[k] === "^";
|
|
1533
|
+
if (neg) k++;
|
|
1534
|
+
let body = "";
|
|
1535
|
+
while (k < source.length && source[k] !== "]") {
|
|
1536
|
+
if (source[k] === "\\") {
|
|
1537
|
+
body += source[k] + (source[k + 1] ?? "");
|
|
1538
|
+
k += 2;
|
|
1539
|
+
} else {
|
|
1540
|
+
body += source[k];
|
|
1541
|
+
k++;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
if (source[k] !== "]") return null;
|
|
1545
|
+
const ranges = parseClassRanges(body);
|
|
1546
|
+
if (!ranges) return null;
|
|
1547
|
+
flush();
|
|
1548
|
+
i = k + 1;
|
|
1549
|
+
i += runFrom(ranges, neg, source[i]);
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
if (ch === "\\") {
|
|
1553
|
+
const e = source[i + 1];
|
|
1554
|
+
if (e === "d" || e === "w" || e === "s") {
|
|
1555
|
+
flush();
|
|
1556
|
+
i += 2;
|
|
1557
|
+
i += runFrom(shorthandRanges(e), false, source[i]);
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
const uni = readUnicodeEscape(source, i);
|
|
1561
|
+
let cp2;
|
|
1562
|
+
if (uni) {
|
|
1563
|
+
cp2 = uni.cp;
|
|
1564
|
+
i = uni.next;
|
|
1565
|
+
} else if (e === void 0) return null;
|
|
1566
|
+
else if (e in CLASS_ESCAPES) {
|
|
1567
|
+
cp2 = CLASS_ESCAPES[e];
|
|
1568
|
+
i += 2;
|
|
1569
|
+
} else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1570
|
+
else {
|
|
1571
|
+
cp2 = e.codePointAt(0);
|
|
1572
|
+
i += 2;
|
|
1573
|
+
}
|
|
1574
|
+
if (source[i] === "?") {
|
|
1575
|
+
flush();
|
|
1576
|
+
parts.push({ part: "lit", cps: [cp2], optional: true });
|
|
1577
|
+
i++;
|
|
1578
|
+
} else lit.push(cp2);
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
if (ch === "(") {
|
|
1582
|
+
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
1583
|
+
let k = i + 3;
|
|
1584
|
+
let depth = 1;
|
|
1585
|
+
while (k < source.length && depth > 0) {
|
|
1586
|
+
const c2 = source[k];
|
|
1587
|
+
if (c2 === "\\") {
|
|
1588
|
+
k += 2;
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (c2 === "[") {
|
|
1592
|
+
let j = k + 1;
|
|
1593
|
+
if (source[j] === "^") j++;
|
|
1594
|
+
while (j < source.length && source[j] !== "]") {
|
|
1595
|
+
if (source[j] === "\\") j += 2;
|
|
1596
|
+
else j++;
|
|
1597
|
+
}
|
|
1598
|
+
if (source[j] !== "]") return null;
|
|
1599
|
+
k = j + 1;
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
if (c2 === "(") depth++;
|
|
1603
|
+
else if (c2 === ")") depth--;
|
|
1604
|
+
k++;
|
|
1605
|
+
}
|
|
1606
|
+
if (depth !== 0) return null;
|
|
1607
|
+
const body = source.slice(i + 3, k - 1);
|
|
1608
|
+
const inner = parseScanShape(body);
|
|
1609
|
+
if (!inner || !groupInnerSafe(inner)) return null;
|
|
1610
|
+
const q = source[k];
|
|
1611
|
+
if (q === "{") return null;
|
|
1612
|
+
flush();
|
|
1613
|
+
if (q === "+") {
|
|
1614
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1615
|
+
parts.push({ part: "group", inner, min: 1, unbounded: true });
|
|
1616
|
+
i = k + 1;
|
|
1617
|
+
} else if (q === "*") {
|
|
1618
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1619
|
+
parts.push({ part: "group", inner, min: 0, unbounded: true });
|
|
1620
|
+
i = k + 1;
|
|
1621
|
+
} else if (q === "?") {
|
|
1622
|
+
parts.push({ part: "group", inner, min: 0, unbounded: false });
|
|
1623
|
+
i = k + 1;
|
|
1624
|
+
} else {
|
|
1625
|
+
parts.push({ part: "group", inner, min: 1, unbounded: false });
|
|
1626
|
+
i = k;
|
|
1627
|
+
}
|
|
1628
|
+
continue;
|
|
1629
|
+
}
|
|
1630
|
+
if (META.has(ch)) return null;
|
|
1631
|
+
const cp = ch.codePointAt(0);
|
|
1632
|
+
i += ch.length;
|
|
1633
|
+
if (source[i] === "?") {
|
|
1634
|
+
flush();
|
|
1635
|
+
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
1636
|
+
i++;
|
|
1637
|
+
} else lit.push(cp);
|
|
1638
|
+
}
|
|
1639
|
+
flush();
|
|
1640
|
+
return parts.length ? parts : null;
|
|
1641
|
+
}
|
|
1642
|
+
function seqIsUnambiguous(parts) {
|
|
1643
|
+
let hasMandatory = false;
|
|
1644
|
+
for (let k = 0; k < parts.length; k++) {
|
|
1645
|
+
const p = parts[k];
|
|
1646
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1647
|
+
if (mandatory) hasMandatory = true;
|
|
1648
|
+
if (p.part === "group") {
|
|
1649
|
+
const exposure = groupPartExposure(p);
|
|
1650
|
+
if (exposure === "unsupported") {
|
|
1651
|
+
const trailingOnce = k === parts.length - 1 && p.min === 1 && !p.unbounded;
|
|
1652
|
+
if (!trailingOnce) return false;
|
|
1653
|
+
} else if (exposure !== null) {
|
|
1654
|
+
if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1658
|
+
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1659
|
+
if (skippableSingle || isUnboundedRepeat) {
|
|
1660
|
+
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
1661
|
+
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return hasMandatory;
|
|
1665
|
+
}
|
|
1666
|
+
function rangesOverlap(a, b) {
|
|
1667
|
+
return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
|
|
1668
|
+
}
|
|
1669
|
+
function rangesSubset(sub, sup) {
|
|
1670
|
+
return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
|
|
1671
|
+
}
|
|
1672
|
+
function parseClassOperand(body) {
|
|
1673
|
+
if (body === "\\d" || body === "\\w" || body === "\\s") {
|
|
1674
|
+
return { ranges: shorthandRanges(body[1]), negated: false };
|
|
1675
|
+
}
|
|
1676
|
+
if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
|
|
1677
|
+
let inner = body.slice(1, -1);
|
|
1678
|
+
const negated = inner.startsWith("^");
|
|
1679
|
+
if (negated) inner = inner.slice(1);
|
|
1680
|
+
const ranges = parseClassRanges(inner);
|
|
1681
|
+
return ranges ? { ranges, negated } : null;
|
|
1682
|
+
}
|
|
1683
|
+
const cps = literalCodePoints(body);
|
|
1684
|
+
if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
|
|
1685
|
+
return null;
|
|
1686
|
+
}
|
|
1687
|
+
function classSubset(t, u) {
|
|
1688
|
+
if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1689
|
+
if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1690
|
+
if (t.negated && !u.negated) return false;
|
|
1691
|
+
return rangesSubset(u.ranges, t.ranges);
|
|
1692
|
+
}
|
|
1693
|
+
function classDisjoint(t, u) {
|
|
1694
|
+
if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1695
|
+
if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1696
|
+
if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
|
|
1697
|
+
return false;
|
|
1698
|
+
}
|
|
1699
|
+
function firstSetsDisjoint(a, b) {
|
|
1700
|
+
if (a === "any" || b === "any") return false;
|
|
1701
|
+
return classDisjoint(a, b);
|
|
1702
|
+
}
|
|
1703
|
+
function fixedClassSeq(shape) {
|
|
1704
|
+
if (shape.kind !== "seq") return null;
|
|
1705
|
+
const out = [];
|
|
1706
|
+
for (const p of shape.parts) {
|
|
1707
|
+
if (p.part === "lit") {
|
|
1708
|
+
if (p.optional) return null;
|
|
1709
|
+
for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
|
|
1710
|
+
} else if (p.part === "run" && !p.unbounded && p.min === 1) {
|
|
1711
|
+
out.push({ ranges: p.ranges, negated: p.negated });
|
|
1712
|
+
} else {
|
|
1713
|
+
return null;
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
return out.length ? out : null;
|
|
1717
|
+
}
|
|
1718
|
+
function altFixedMutuallyExclusive(shape) {
|
|
1719
|
+
if (shape.kind !== "alt" || shape.disjoint) return false;
|
|
1720
|
+
const lists = [];
|
|
1721
|
+
for (const arm of shape.arms) {
|
|
1722
|
+
const f = fixedClassSeq(arm);
|
|
1723
|
+
if (!f) return false;
|
|
1724
|
+
lists.push(f);
|
|
1725
|
+
}
|
|
1726
|
+
for (let i = 0; i < lists.length; i++) {
|
|
1727
|
+
for (let j = 0; j < lists.length; j++) {
|
|
1728
|
+
if (i === j) continue;
|
|
1729
|
+
const a = lists[i];
|
|
1730
|
+
const b = lists[j];
|
|
1731
|
+
if (a.length > b.length) continue;
|
|
1732
|
+
let disjointAt = false;
|
|
1733
|
+
for (let p = 0; p < a.length; p++) {
|
|
1734
|
+
if (classDisjoint(a[p], b[p])) {
|
|
1735
|
+
disjointAt = true;
|
|
1736
|
+
break;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
if (!disjointAt) return false;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
return true;
|
|
1743
|
+
}
|
|
1744
|
+
function groupPartExposure(part) {
|
|
1745
|
+
const innerWiggle = trailingBacktrackClass(part.inner);
|
|
1746
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1747
|
+
const requiredOnce = !part.unbounded && part.min === 1;
|
|
1748
|
+
if (requiredOnce) return innerWiggle;
|
|
1749
|
+
const dropExposed = shapeFirstAccept(part.inner);
|
|
1750
|
+
if (dropExposed === "any") return "unsupported";
|
|
1751
|
+
if (innerWiggle === null) return dropExposed;
|
|
1752
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1753
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1754
|
+
}
|
|
1755
|
+
function trailingBacktrackClass(shape) {
|
|
1756
|
+
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1757
|
+
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
1758
|
+
if (shape.kind === "litFold") return null;
|
|
1759
|
+
if (shape.kind === "seq") {
|
|
1760
|
+
const last = shape.parts[shape.parts.length - 1];
|
|
1761
|
+
if (last.part === "lit") return null;
|
|
1762
|
+
if (last.part === "run") {
|
|
1763
|
+
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1764
|
+
return null;
|
|
1765
|
+
}
|
|
1766
|
+
return groupPartExposure(last);
|
|
1767
|
+
}
|
|
1768
|
+
if (shape.kind === "alt") {
|
|
1769
|
+
if (!shape.disjoint) return altFixedMutuallyExclusive(shape) ? null : "unsupported";
|
|
1770
|
+
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1771
|
+
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1772
|
+
const concrete = classes.filter((c) => c !== null);
|
|
1773
|
+
if (concrete.length === 0) return null;
|
|
1774
|
+
const unioned = unionCharSets(concrete);
|
|
1775
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1776
|
+
}
|
|
1777
|
+
return "unsupported";
|
|
1778
|
+
}
|
|
1779
|
+
function lookaheadUnambiguous(inner, operand, negative) {
|
|
1780
|
+
const tail = trailingBacktrackClass(inner);
|
|
1781
|
+
if (tail === "unsupported") return false;
|
|
1782
|
+
if (tail === null) return true;
|
|
1783
|
+
return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
|
|
1784
|
+
}
|
|
1785
|
+
function stripTrailingLookahead(source) {
|
|
1786
|
+
if (!source.endsWith(")")) return null;
|
|
1787
|
+
for (const negative of [true, false]) {
|
|
1788
|
+
const lead = negative ? "(?!" : "(?=";
|
|
1789
|
+
const idx = source.lastIndexOf(lead);
|
|
1790
|
+
if (idx === -1) continue;
|
|
1791
|
+
const body = source.slice(idx + lead.length, -1);
|
|
1792
|
+
const operand = body.length ? parseClassOperand(body) : null;
|
|
1793
|
+
if (!operand) continue;
|
|
1794
|
+
return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
|
|
1795
|
+
}
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
function unwrapOuterGroup(source) {
|
|
1799
|
+
while (source.startsWith("(?:")) {
|
|
1800
|
+
let depth = 1;
|
|
1801
|
+
let i = 3;
|
|
1802
|
+
let ok = true;
|
|
1803
|
+
while (i < source.length && depth > 0) {
|
|
1804
|
+
const ch = source[i];
|
|
1805
|
+
if (ch === "\\") {
|
|
1806
|
+
i += 2;
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
if (ch === "[") {
|
|
1810
|
+
let j = i + 1;
|
|
1811
|
+
if (source[j] === "^") j++;
|
|
1812
|
+
while (j < source.length && source[j] !== "]") {
|
|
1813
|
+
if (source[j] === "\\") j += 2;
|
|
1814
|
+
else j++;
|
|
1815
|
+
}
|
|
1816
|
+
if (source[j] !== "]") {
|
|
1817
|
+
ok = false;
|
|
1818
|
+
break;
|
|
1819
|
+
}
|
|
1820
|
+
i = j + 1;
|
|
1821
|
+
continue;
|
|
1822
|
+
}
|
|
1823
|
+
if (ch === "(") depth++;
|
|
1824
|
+
else if (ch === ")") depth--;
|
|
1825
|
+
i++;
|
|
1826
|
+
}
|
|
1827
|
+
if (!ok || depth !== 0 || i !== source.length) break;
|
|
1828
|
+
source = source.slice(3, -1);
|
|
1829
|
+
}
|
|
1830
|
+
return source;
|
|
1831
|
+
}
|
|
1832
|
+
function splitTopLevelAlternation(source) {
|
|
1833
|
+
const body = unwrapOuterGroup(source);
|
|
1834
|
+
const parts = [];
|
|
1835
|
+
let depth = 0;
|
|
1836
|
+
let last = 0;
|
|
1837
|
+
let i = 0;
|
|
1838
|
+
while (i < body.length) {
|
|
1839
|
+
const ch = body[i];
|
|
1840
|
+
if (ch === "\\") {
|
|
1841
|
+
i += 2;
|
|
1842
|
+
continue;
|
|
1843
|
+
}
|
|
1844
|
+
if (ch === "[") {
|
|
1845
|
+
let j = i + 1;
|
|
1846
|
+
if (body[j] === "^") j++;
|
|
1847
|
+
while (j < body.length && body[j] !== "]") {
|
|
1848
|
+
if (body[j] === "\\") j += 2;
|
|
1849
|
+
else j++;
|
|
1850
|
+
}
|
|
1851
|
+
if (body[j] !== "]") return null;
|
|
1852
|
+
i = j + 1;
|
|
1853
|
+
continue;
|
|
1854
|
+
}
|
|
1855
|
+
if (ch === "(") {
|
|
1856
|
+
depth++;
|
|
1857
|
+
i++;
|
|
1858
|
+
continue;
|
|
1859
|
+
}
|
|
1860
|
+
if (ch === ")") {
|
|
1861
|
+
depth--;
|
|
1862
|
+
if (depth < 0) return null;
|
|
1863
|
+
i++;
|
|
1864
|
+
continue;
|
|
1865
|
+
}
|
|
1866
|
+
if (ch === "|" && depth === 0) {
|
|
1867
|
+
parts.push(body.slice(last, i));
|
|
1868
|
+
last = i + 1;
|
|
1869
|
+
i++;
|
|
1870
|
+
continue;
|
|
1871
|
+
}
|
|
1872
|
+
i++;
|
|
1873
|
+
}
|
|
1874
|
+
if (depth !== 0) return null;
|
|
1875
|
+
parts.push(body.slice(last));
|
|
1876
|
+
if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
|
|
1877
|
+
return parts;
|
|
1878
|
+
}
|
|
1879
|
+
function shapeCanBeEmpty(shape) {
|
|
1880
|
+
if (shape.kind === "chars") return !shape.minOne;
|
|
1881
|
+
if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
|
|
1882
|
+
if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
|
|
1883
|
+
return false;
|
|
1884
|
+
}
|
|
1885
|
+
function unionCharSets(sets) {
|
|
1886
|
+
if (sets.some((s) => s === "any")) return "any";
|
|
1887
|
+
const concrete = sets;
|
|
1888
|
+
if (concrete.length === 1) return concrete[0];
|
|
1889
|
+
if (concrete.some((s) => s.negated)) return "any";
|
|
1890
|
+
return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
|
|
1891
|
+
}
|
|
1892
|
+
function partFirstAccept(part) {
|
|
1893
|
+
if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
|
|
1894
|
+
if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
|
|
1895
|
+
return shapeFirstAccept(part.inner);
|
|
1896
|
+
}
|
|
1897
|
+
function seqFirstAccept(parts) {
|
|
1898
|
+
const sets = [];
|
|
1899
|
+
for (const p of parts) {
|
|
1900
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1901
|
+
sets.push(partFirstAccept(p));
|
|
1902
|
+
if (mandatory) break;
|
|
1903
|
+
}
|
|
1904
|
+
return unionCharSets(sets);
|
|
1905
|
+
}
|
|
1906
|
+
function groupInnerSafe(shape) {
|
|
1907
|
+
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1908
|
+
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1909
|
+
if (shape.kind === "alt") return true;
|
|
1910
|
+
return false;
|
|
1911
|
+
}
|
|
1912
|
+
function shapeFirstAccept(shape) {
|
|
1913
|
+
if (shapeCanBeEmpty(shape)) return "any";
|
|
1914
|
+
switch (shape.kind) {
|
|
1915
|
+
case "chars":
|
|
1916
|
+
return { ranges: shape.ranges, negated: false };
|
|
1917
|
+
case "ident":
|
|
1918
|
+
return { ranges: shape.head, negated: false };
|
|
1919
|
+
case "until":
|
|
1920
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1921
|
+
case "delimited":
|
|
1922
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1923
|
+
case "string":
|
|
1924
|
+
return { ranges: [[shape.quote, shape.quote]], negated: false };
|
|
1925
|
+
case "litFold": {
|
|
1926
|
+
const cp = shape.open[0];
|
|
1927
|
+
if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
|
|
1928
|
+
if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
|
|
1929
|
+
return { ranges: [[cp, cp]], negated: false };
|
|
1930
|
+
}
|
|
1931
|
+
case "seq":
|
|
1932
|
+
return seqFirstAccept(shape.parts);
|
|
1933
|
+
case "lookahead":
|
|
1934
|
+
return shapeFirstAccept(shape.inner);
|
|
1935
|
+
// zero-width — doesn't change first-char acceptance
|
|
1936
|
+
case "alt": {
|
|
1937
|
+
const sets = shape.firsts.map((f) => f === null ? "any" : f);
|
|
1938
|
+
return unionCharSets(sets);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
function allPairsDisjoint(sets) {
|
|
1943
|
+
for (let i = 0; i < sets.length; i++) {
|
|
1944
|
+
const a = sets[i];
|
|
1945
|
+
if (a === "any") return false;
|
|
1946
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
1947
|
+
const b = sets[j];
|
|
1948
|
+
if (b === "any" || !classDisjoint(a, b)) return false;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
return true;
|
|
1952
|
+
}
|
|
1953
|
+
function parseAlternation(source) {
|
|
1954
|
+
const parts = splitTopLevelAlternation(source);
|
|
1955
|
+
if (!parts) return null;
|
|
1956
|
+
const arms = [];
|
|
1957
|
+
for (const p of parts) {
|
|
1958
|
+
const arm = parseScanShape(p);
|
|
1959
|
+
if (!arm) return null;
|
|
1960
|
+
arms.push(arm);
|
|
1961
|
+
}
|
|
1962
|
+
const firstSets = arms.map(shapeFirstAccept);
|
|
1963
|
+
const disjoint = allPairsDisjoint(firstSets);
|
|
1964
|
+
return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
|
|
1965
|
+
}
|
|
1469
1966
|
function parseScanShape(source) {
|
|
1967
|
+
const alt = parseAlternation(source);
|
|
1968
|
+
if (alt) return alt;
|
|
1969
|
+
const la = stripTrailingLookahead(source);
|
|
1970
|
+
if (la) {
|
|
1971
|
+
const inner = parseScanShapeCore(la.base);
|
|
1972
|
+
if (!inner) return null;
|
|
1973
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
1974
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
1975
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
1976
|
+
}
|
|
1977
|
+
return parseScanShapeCore(source);
|
|
1978
|
+
}
|
|
1979
|
+
function splitDelimArms(body) {
|
|
1980
|
+
const arms = [];
|
|
1981
|
+
let depth = 0;
|
|
1982
|
+
let last = 0;
|
|
1983
|
+
let i = 0;
|
|
1984
|
+
while (i < body.length) {
|
|
1985
|
+
const ch = body[i];
|
|
1986
|
+
if (ch === "\\") {
|
|
1987
|
+
i += 2;
|
|
1988
|
+
continue;
|
|
1989
|
+
}
|
|
1990
|
+
if (ch === "[") {
|
|
1991
|
+
let j = i + 1;
|
|
1992
|
+
if (body[j] === "^") j++;
|
|
1993
|
+
while (j < body.length && body[j] !== "]") {
|
|
1994
|
+
if (body[j] === "\\") j += 2;
|
|
1995
|
+
else j++;
|
|
1996
|
+
}
|
|
1997
|
+
if (body[j] !== "]") return null;
|
|
1998
|
+
i = j + 1;
|
|
1999
|
+
continue;
|
|
2000
|
+
}
|
|
2001
|
+
if (ch === "(") {
|
|
2002
|
+
depth++;
|
|
2003
|
+
i++;
|
|
2004
|
+
continue;
|
|
2005
|
+
}
|
|
2006
|
+
if (ch === ")") {
|
|
2007
|
+
depth--;
|
|
2008
|
+
if (depth < 0) return null;
|
|
2009
|
+
i++;
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
if (ch === "|" && depth === 0) {
|
|
2013
|
+
arms.push(body.slice(last, i));
|
|
2014
|
+
last = i + 1;
|
|
2015
|
+
}
|
|
2016
|
+
i++;
|
|
2017
|
+
}
|
|
2018
|
+
if (depth !== 0) return null;
|
|
2019
|
+
arms.push(body.slice(last));
|
|
2020
|
+
return arms;
|
|
2021
|
+
}
|
|
2022
|
+
function delimitedBodySound(body, close) {
|
|
2023
|
+
const l0 = close[0];
|
|
2024
|
+
if (close.length > 2) return false;
|
|
2025
|
+
const arms = splitDelimArms(body);
|
|
2026
|
+
if (!arms) return false;
|
|
2027
|
+
let bulkSeen = false;
|
|
2028
|
+
let guardSeen = false;
|
|
2029
|
+
for (const arm of arms) {
|
|
2030
|
+
const bulk = /^\[\^((?:\\.|[^\]])+)\]$/.exec(arm);
|
|
2031
|
+
if (bulk) {
|
|
2032
|
+
if (bulkSeen) return false;
|
|
2033
|
+
const ranges = parseClassRanges(bulk[1]);
|
|
2034
|
+
if (!ranges || ranges.length !== 1 || ranges[0][0] !== l0 || ranges[0][1] !== l0) return false;
|
|
2035
|
+
bulkSeen = true;
|
|
2036
|
+
continue;
|
|
2037
|
+
}
|
|
2038
|
+
if (close.length === 2 && arm.endsWith(")")) {
|
|
2039
|
+
const idx = arm.indexOf("(?!");
|
|
2040
|
+
if (idx === -1) return false;
|
|
2041
|
+
const prefix = literalCodePoints(arm.slice(0, idx));
|
|
2042
|
+
const op = parseClassOperand(arm.slice(idx + 3, -1));
|
|
2043
|
+
const prefixIsL0 = prefix?.length === 1 && prefix[0] === l0;
|
|
2044
|
+
const forbidsL1 = op && !op.negated && op.ranges.length === 1 && op.ranges[0][0] === close[1] && op.ranges[0][1] === close[1];
|
|
2045
|
+
if (!prefixIsL0 || !forbidsL1 || guardSeen) return false;
|
|
2046
|
+
guardSeen = true;
|
|
2047
|
+
continue;
|
|
2048
|
+
}
|
|
2049
|
+
return false;
|
|
2050
|
+
}
|
|
2051
|
+
return bulkSeen && guardSeen === (close.length === 2);
|
|
2052
|
+
}
|
|
2053
|
+
function parseDelimited(source) {
|
|
2054
|
+
if (source.includes("\\\\")) return null;
|
|
2055
|
+
let i = 0;
|
|
2056
|
+
while (i < source.length && source[i] !== "(") {
|
|
2057
|
+
i += source[i] === "\\" ? 2 : 1;
|
|
2058
|
+
}
|
|
2059
|
+
if (source[i] !== "(" || source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
2060
|
+
const openFrag = source.slice(0, i);
|
|
2061
|
+
let k = i + 3;
|
|
2062
|
+
let depth = 1;
|
|
2063
|
+
while (k < source.length && depth > 0) {
|
|
2064
|
+
const ch = source[k];
|
|
2065
|
+
if (ch === "\\") {
|
|
2066
|
+
k += 2;
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2069
|
+
if (ch === "[") {
|
|
2070
|
+
let j = k + 1;
|
|
2071
|
+
if (source[j] === "^") j++;
|
|
2072
|
+
while (j < source.length && source[j] !== "]") {
|
|
2073
|
+
if (source[j] === "\\") j += 2;
|
|
2074
|
+
else j++;
|
|
2075
|
+
}
|
|
2076
|
+
if (source[j] !== "]") return null;
|
|
2077
|
+
k = j + 1;
|
|
2078
|
+
continue;
|
|
2079
|
+
}
|
|
2080
|
+
if (ch === "(") depth++;
|
|
2081
|
+
else if (ch === ")") depth--;
|
|
2082
|
+
k++;
|
|
2083
|
+
}
|
|
2084
|
+
if (depth !== 0) return null;
|
|
2085
|
+
const body = source.slice(i + 3, k - 1);
|
|
2086
|
+
if (source[k] !== "*") return null;
|
|
2087
|
+
const closeFrag = source.slice(k + 1);
|
|
2088
|
+
const open = literalCodePoints(openFrag);
|
|
2089
|
+
const close = literalCodePoints(closeFrag);
|
|
2090
|
+
if (!open || !close || !delimitedBodySound(body, close)) return null;
|
|
2091
|
+
return { kind: "delimited", open, close };
|
|
2092
|
+
}
|
|
2093
|
+
function parseScanShapeCore(source) {
|
|
1470
2094
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1471
|
-
if (m) {
|
|
1472
|
-
if (m[1].startsWith("^")) return null;
|
|
2095
|
+
if (m && !m[1].startsWith("^")) {
|
|
1473
2096
|
const ranges = parseClassRanges(m[1]);
|
|
1474
2097
|
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1475
2098
|
}
|
|
1476
|
-
m = /^\\([
|
|
2099
|
+
m = /^\\([dws])([+*])$/.exec(source);
|
|
1477
2100
|
if (m) {
|
|
1478
2101
|
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1479
2102
|
}
|
|
1480
|
-
m = /^(\[(?:\\.|[^\]])+\]|\\[
|
|
2103
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
|
|
1481
2104
|
if (m) {
|
|
1482
2105
|
const head = classToRanges(m[1]);
|
|
1483
2106
|
const tail = classToRanges(m[2]);
|
|
1484
|
-
|
|
2107
|
+
if (head && tail) return { kind: "ident", head, tail };
|
|
1485
2108
|
}
|
|
1486
2109
|
const str = parseStringShape(source);
|
|
1487
2110
|
if (str) return str;
|
|
@@ -1490,20 +2113,19 @@ function parseScanShape(source) {
|
|
|
1490
2113
|
const open = literalCodePoints(m[1]);
|
|
1491
2114
|
const stop = parseClassRanges(m[2]);
|
|
1492
2115
|
if (open && stop) return { kind: "until", open, stop };
|
|
1493
|
-
return null;
|
|
1494
|
-
}
|
|
1495
|
-
if (!source.includes("\\\\")) {
|
|
1496
|
-
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
1497
|
-
if (m && m[1] && m[2]) {
|
|
1498
|
-
const open = literalCodePoints(m[1]);
|
|
1499
|
-
const close = literalCodePoints(m[2]);
|
|
1500
|
-
if (open && close) return { kind: "delimited", open, close };
|
|
1501
|
-
}
|
|
1502
2116
|
}
|
|
2117
|
+
const delim = parseDelimited(source);
|
|
2118
|
+
if (delim) return delim;
|
|
2119
|
+
const parts = parseSeqParts(source);
|
|
2120
|
+
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1503
2121
|
return null;
|
|
1504
2122
|
}
|
|
1505
2123
|
function scanShapeFromRegex(source, flags) {
|
|
1506
|
-
if (/[
|
|
2124
|
+
if (/[msu]/.test(flags)) return null;
|
|
2125
|
+
if (/i/.test(flags)) {
|
|
2126
|
+
const open = literalCodePoints(source);
|
|
2127
|
+
return open ? { kind: "litFold", open } : null;
|
|
2128
|
+
}
|
|
1507
2129
|
return parseScanShape(source);
|
|
1508
2130
|
}
|
|
1509
2131
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
@@ -1518,6 +2140,53 @@ function stringHardStop(shape) {
|
|
|
1518
2140
|
}
|
|
1519
2141
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1520
2142
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2143
|
+
if (shape.kind === "lookahead") {
|
|
2144
|
+
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
2145
|
+
const okV = mint("_ok");
|
|
2146
|
+
const endV2 = mint("_end");
|
|
2147
|
+
const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
|
|
2148
|
+
const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
|
|
2149
|
+
const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
|
|
2150
|
+
const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
|
|
2151
|
+
return {
|
|
2152
|
+
setup: [
|
|
2153
|
+
...inner.setup,
|
|
2154
|
+
`${ind2}let ${okV} = ${inner.ok}`,
|
|
2155
|
+
`${ind2}let ${endV2} = ${inner.end}`,
|
|
2156
|
+
`${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
|
|
2157
|
+
],
|
|
2158
|
+
ok: okV,
|
|
2159
|
+
end: endV2
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
2162
|
+
if (shape.kind === "alt") {
|
|
2163
|
+
const okV = mint("_ok");
|
|
2164
|
+
const endV2 = mint("_end");
|
|
2165
|
+
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2166
|
+
if (shape.disjoint) {
|
|
2167
|
+
const c0 = codeAt(start, firstChar);
|
|
2168
|
+
shape.arms.forEach((arm, k) => {
|
|
2169
|
+
const fs2 = shape.firsts[k];
|
|
2170
|
+
const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
|
|
2171
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2172
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2173
|
+
lines.push(...m.setup);
|
|
2174
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2175
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2176
|
+
lines.push(`${ind2}}`);
|
|
2177
|
+
});
|
|
2178
|
+
} else {
|
|
2179
|
+
const lbl = mint("_alt");
|
|
2180
|
+
lines.push(`${ind2}${lbl}: {`);
|
|
2181
|
+
for (const arm of shape.arms) {
|
|
2182
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2183
|
+
lines.push(...m.setup);
|
|
2184
|
+
lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
2185
|
+
}
|
|
2186
|
+
lines.push(`${ind2}}`);
|
|
2187
|
+
}
|
|
2188
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2189
|
+
}
|
|
1521
2190
|
if (shape.kind === "chars") {
|
|
1522
2191
|
const cur = mint("_e");
|
|
1523
2192
|
return {
|
|
@@ -1544,6 +2213,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
1544
2213
|
end: cur
|
|
1545
2214
|
};
|
|
1546
2215
|
}
|
|
2216
|
+
if (shape.kind === "seq") {
|
|
2217
|
+
const endV2 = mint("_e");
|
|
2218
|
+
const okV = mint("_ok");
|
|
2219
|
+
const runCond = (p, at) => {
|
|
2220
|
+
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
2221
|
+
return p.negated ? `!(${c})` : `(${c})`;
|
|
2222
|
+
};
|
|
2223
|
+
const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
|
|
2224
|
+
let first = true;
|
|
2225
|
+
for (const p of shape.parts) {
|
|
2226
|
+
if (p.part === "lit") {
|
|
2227
|
+
const L = p.cps.length;
|
|
2228
|
+
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
2229
|
+
if (p.optional) {
|
|
2230
|
+
lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
2231
|
+
} else {
|
|
2232
|
+
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
2233
|
+
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
2234
|
+
}
|
|
2235
|
+
} else if (p.part === "run" && !p.unbounded) {
|
|
2236
|
+
if (p.min === 1) {
|
|
2237
|
+
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
2238
|
+
lines.push(`${ind2} ${endV2}++`);
|
|
2239
|
+
} else {
|
|
2240
|
+
lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2241
|
+
}
|
|
2242
|
+
} else if (p.part === "run") {
|
|
2243
|
+
const s = mint("_s");
|
|
2244
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
2245
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2246
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
2247
|
+
} else {
|
|
2248
|
+
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
2249
|
+
if (!p.unbounded) {
|
|
2250
|
+
lines.push(...inner.setup);
|
|
2251
|
+
if (p.min === 1) {
|
|
2252
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2253
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2254
|
+
} else {
|
|
2255
|
+
lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
2256
|
+
}
|
|
2257
|
+
} else {
|
|
2258
|
+
const progV = mint("_prog");
|
|
2259
|
+
lines.push(`${ind2} let ${progV} = false`);
|
|
2260
|
+
lines.push(`${ind2} while (true) {`);
|
|
2261
|
+
lines.push(...inner.setup);
|
|
2262
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2263
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2264
|
+
lines.push(`${ind2} ${progV} = true`);
|
|
2265
|
+
lines.push(`${ind2} }`);
|
|
2266
|
+
if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
first = false;
|
|
2270
|
+
}
|
|
2271
|
+
lines.push(`${ind2} ${okV} = true`);
|
|
2272
|
+
lines.push(`${ind2}} while (false)`);
|
|
2273
|
+
lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
|
|
2274
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2275
|
+
}
|
|
2276
|
+
if (shape.kind === "litFold") {
|
|
2277
|
+
const openLen2 = shape.open.length;
|
|
2278
|
+
const endV2 = mint("_end");
|
|
2279
|
+
const checks = shape.open.map((cp, k) => {
|
|
2280
|
+
const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
|
|
2281
|
+
return foldEq(cVar, cp);
|
|
2282
|
+
});
|
|
2283
|
+
return {
|
|
2284
|
+
setup: [
|
|
2285
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2286
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
|
|
2287
|
+
],
|
|
2288
|
+
ok: `${endV2} > ${start}`,
|
|
2289
|
+
end: endV2
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
1547
2292
|
if (shape.kind === "until") {
|
|
1548
2293
|
const j2 = mint("_j");
|
|
1549
2294
|
const openLen2 = shape.open.length;
|
|
@@ -1825,6 +2570,7 @@ function looseMkBuildRe(type) {
|
|
|
1825
2570
|
);
|
|
1826
2571
|
}
|
|
1827
2572
|
function analyzeMkInlineBuild(def) {
|
|
2573
|
+
if (!def.build) return null;
|
|
1828
2574
|
const src = (def.buildSrc ?? def.build.toString()).trim();
|
|
1829
2575
|
const strict = src.match(MK_BUILD_RE);
|
|
1830
2576
|
if (strict) {
|
|
@@ -1922,6 +2668,15 @@ function v(ctx, prefix = "_v") {
|
|
|
1922
2668
|
function ind(ctx) {
|
|
1923
2669
|
return " ".repeat(ctx.indent);
|
|
1924
2670
|
}
|
|
2671
|
+
function nsp(ctx) {
|
|
2672
|
+
return ctx.ns ?? "";
|
|
2673
|
+
}
|
|
2674
|
+
function mfRef(ctx) {
|
|
2675
|
+
return `${nsp(ctx)}_mf`;
|
|
2676
|
+
}
|
|
2677
|
+
function buildRef(ctx) {
|
|
2678
|
+
return `${nsp(ctx)}_build`;
|
|
2679
|
+
}
|
|
1925
2680
|
function reindentStmts(stmts, targetLevels) {
|
|
1926
2681
|
const nonEmpty = stmts.filter((s) => s.trim().length > 0);
|
|
1927
2682
|
if (nonEmpty.length === 0) return stmts;
|
|
@@ -1938,8 +2693,8 @@ function failReturnArr(expectedArr, posExpr) {
|
|
|
1938
2693
|
function hoistExpected(ctx, constArrSource) {
|
|
1939
2694
|
let name = ctx.expectedMap.get(constArrSource);
|
|
1940
2695
|
if (name === void 0) {
|
|
1941
|
-
name =
|
|
1942
|
-
ctx.expectedDecls.push(`const ${name} =
|
|
2696
|
+
name = `${nsp(ctx)}_fx${ctx.expectedDecls.length}`;
|
|
2697
|
+
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
1943
2698
|
ctx.expectedMap.set(constArrSource, name);
|
|
1944
2699
|
}
|
|
1945
2700
|
return name;
|
|
@@ -2037,13 +2792,13 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
|
|
|
2037
2792
|
`${i}}`
|
|
2038
2793
|
];
|
|
2039
2794
|
}
|
|
2040
|
-
function ensureRegexDecl(ctx,
|
|
2795
|
+
function ensureRegexDecl(ctx, source, flags) {
|
|
2041
2796
|
const f = "y" + flags.replace(/[gy]/g, "");
|
|
2042
|
-
const key = `${
|
|
2797
|
+
const key = `${source}/${f}`;
|
|
2043
2798
|
let rName = ctx.regexMap.get(key);
|
|
2044
2799
|
if (rName === void 0) {
|
|
2045
|
-
rName =
|
|
2046
|
-
ctx.regexDecls.push(`const ${rName} = /${
|
|
2800
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2801
|
+
ctx.regexDecls.push(`const ${rName} = /${source}/${f}`);
|
|
2047
2802
|
ctx.regexMap.set(key, rName);
|
|
2048
2803
|
}
|
|
2049
2804
|
return rName;
|
|
@@ -2052,7 +2807,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2052
2807
|
const trivia2 = ctx.activeTrivia;
|
|
2053
2808
|
const existing = ctx.triviaFnNames.get(trivia2);
|
|
2054
2809
|
if (existing) return existing;
|
|
2055
|
-
const fnName =
|
|
2810
|
+
const fnName = `${nsp(ctx)}_tf${ctx.triviaFnNames.size}`;
|
|
2056
2811
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
2057
2812
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
2058
2813
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
@@ -2074,7 +2829,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2074
2829
|
for (const arm of regexSpec.arms) {
|
|
2075
2830
|
const def = arm.parser._def;
|
|
2076
2831
|
if (def.tag !== "regex") break;
|
|
2077
|
-
reNames.push(ensureRegexDecl(ctx, def.
|
|
2832
|
+
reNames.push(ensureRegexDecl(ctx, def.source, def.flags));
|
|
2078
2833
|
}
|
|
2079
2834
|
if (reNames.length === regexSpec.arms.length) {
|
|
2080
2835
|
ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
|
|
@@ -2120,10 +2875,10 @@ function ensureTriviaFn(ctx) {
|
|
|
2120
2875
|
function ensureTriviaCaptureFn(ctx) {
|
|
2121
2876
|
return ensureTriviaFn(ctx);
|
|
2122
2877
|
}
|
|
2123
|
-
function firstSetCond(codeVar,
|
|
2124
|
-
if (
|
|
2125
|
-
if (
|
|
2126
|
-
return
|
|
2878
|
+
function firstSetCond(codeVar, fs2) {
|
|
2879
|
+
if (fs2.kind === "any") return "true";
|
|
2880
|
+
if (fs2.kind === "empty") return "false";
|
|
2881
|
+
return fs2.ranges.map(
|
|
2127
2882
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
2128
2883
|
).join(" || ");
|
|
2129
2884
|
}
|
|
@@ -2135,10 +2890,10 @@ function planDisjointDispatch(parsers) {
|
|
|
2135
2890
|
const cases = [];
|
|
2136
2891
|
let total = 0;
|
|
2137
2892
|
for (const p of parsers) {
|
|
2138
|
-
const
|
|
2139
|
-
if (
|
|
2893
|
+
const fs2 = p._meta.firstSet;
|
|
2894
|
+
if (fs2.kind !== "ranges") return { kind: "if" };
|
|
2140
2895
|
const pts = [];
|
|
2141
|
-
for (const r of
|
|
2896
|
+
for (const r of fs2.ranges) {
|
|
2142
2897
|
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2143
2898
|
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2144
2899
|
}
|
|
@@ -2261,18 +3016,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2261
3016
|
return true;
|
|
2262
3017
|
}
|
|
2263
3018
|
}
|
|
2264
|
-
function emitFallible(inner, ctx, pos) {
|
|
3019
|
+
function emitFallible(inner, ctx, pos, swallow = false) {
|
|
2265
3020
|
const lbl = v(ctx, "_lbl");
|
|
2266
3021
|
const okV = `${lbl}ok`;
|
|
2267
3022
|
const valV = `${lbl}v`;
|
|
2268
3023
|
const endV = `${lbl}e`;
|
|
2269
3024
|
const savedLabel = ctx.failLabel;
|
|
2270
3025
|
const savedIndent = ctx.indent;
|
|
3026
|
+
const savedRecord = ctx.recordFail;
|
|
2271
3027
|
ctx.failLabel = lbl;
|
|
2272
3028
|
ctx.indent = savedIndent + 1;
|
|
3029
|
+
if (swallow) ctx.recordFail = false;
|
|
2273
3030
|
const r = emit(inner, ctx, pos);
|
|
2274
3031
|
ctx.failLabel = savedLabel;
|
|
2275
3032
|
ctx.indent = savedIndent;
|
|
3033
|
+
ctx.recordFail = savedRecord;
|
|
2276
3034
|
const ind0 = ind(ctx);
|
|
2277
3035
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
2278
3036
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
@@ -2295,6 +3053,7 @@ function emitFallible(inner, ctx, pos) {
|
|
|
2295
3053
|
];
|
|
2296
3054
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
2297
3055
|
}
|
|
3056
|
+
var CHARCODE_CHAIN_MAX = 16;
|
|
2298
3057
|
function emitLit(def, ctx, pos) {
|
|
2299
3058
|
const { value, caseInsensitive } = def;
|
|
2300
3059
|
const len = value.length;
|
|
@@ -2317,7 +3076,7 @@ function emitLit(def, ctx, pos) {
|
|
|
2317
3076
|
...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
|
|
2318
3077
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
2319
3078
|
);
|
|
2320
|
-
} else if (len <=
|
|
3079
|
+
} else if (len <= CHARCODE_CHAIN_MAX) {
|
|
2321
3080
|
const checks = Array.from(
|
|
2322
3081
|
{ length: len },
|
|
2323
3082
|
(_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
|
|
@@ -2339,7 +3098,55 @@ function emitLit(def, ctx, pos) {
|
|
|
2339
3098
|
function escapeKeywordRe(s) {
|
|
2340
3099
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2341
3100
|
}
|
|
3101
|
+
function emitKeywordsFast(def, ctx, pos) {
|
|
3102
|
+
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
3103
|
+
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
3104
|
+
if (def.caseInsensitive && def.boundary) return null;
|
|
3105
|
+
let boundary = null;
|
|
3106
|
+
if (def.boundary) {
|
|
3107
|
+
let body = def.boundary;
|
|
3108
|
+
const negated = body.startsWith("^");
|
|
3109
|
+
if (negated) body = body.slice(1);
|
|
3110
|
+
const ranges = parseClassRanges(body);
|
|
3111
|
+
if (!ranges) return null;
|
|
3112
|
+
boundary = { ranges, negated };
|
|
3113
|
+
}
|
|
3114
|
+
const mint = (prefix = "_v") => v(ctx, prefix);
|
|
3115
|
+
const lbl = v(ctx, "_kwLbl");
|
|
3116
|
+
const valV = v(ctx, "_kwv");
|
|
3117
|
+
const endV = v(ctx, "_kwe");
|
|
3118
|
+
const bodyInd = ind(ctx) + " ";
|
|
3119
|
+
const tries = [];
|
|
3120
|
+
for (const w of def.words) {
|
|
3121
|
+
const cps = Array.from(w, (ch) => ch.codePointAt(0));
|
|
3122
|
+
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
3123
|
+
if (boundary) {
|
|
3124
|
+
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
3125
|
+
}
|
|
3126
|
+
const m = emitShapeMatch(shape, pos, mint, bodyInd);
|
|
3127
|
+
tries.push(
|
|
3128
|
+
...m.setup,
|
|
3129
|
+
// Slice from input rather than reusing the literal word: `caseInsensitive`
|
|
3130
|
+
// must return the text as it actually appeared (e.g. "ABC" for keyword
|
|
3131
|
+
// "abc"), matching what the original `RegExp.exec()[0]` returned.
|
|
3132
|
+
`${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
|
|
3133
|
+
);
|
|
3134
|
+
}
|
|
3135
|
+
const stmts = [
|
|
3136
|
+
`${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
|
|
3137
|
+
`${ind(ctx)}${lbl}: {`,
|
|
3138
|
+
...tries,
|
|
3139
|
+
`${ind(ctx)}}`,
|
|
3140
|
+
// Every word has length >= 1 (checked above), so a real match always
|
|
3141
|
+
// advances past `pos` — `endV === pos` only happens when no candidate matched.
|
|
3142
|
+
...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
|
|
3143
|
+
];
|
|
3144
|
+
stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
|
|
3145
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3146
|
+
}
|
|
2342
3147
|
function emitKeywords(def, ctx, pos) {
|
|
3148
|
+
const fast = emitKeywordsFast(def, ctx, pos);
|
|
3149
|
+
if (fast) return fast;
|
|
2343
3150
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
2344
3151
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
2345
3152
|
const flags = def.caseInsensitive ? "iuy" : "uy";
|
|
@@ -2347,7 +3154,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2347
3154
|
const key = `${source}/${flags}`;
|
|
2348
3155
|
let rName = ctx.regexMap.get(key);
|
|
2349
3156
|
if (rName === void 0) {
|
|
2350
|
-
rName =
|
|
3157
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2351
3158
|
ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
|
|
2352
3159
|
ctx.regexMap.set(key, rName);
|
|
2353
3160
|
}
|
|
@@ -2381,11 +3188,11 @@ function emitRegex(def, ctx, pos) {
|
|
|
2381
3188
|
}
|
|
2382
3189
|
}
|
|
2383
3190
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2384
|
-
const key = `${def.
|
|
3191
|
+
const key = `${def.source}/${flags}`;
|
|
2385
3192
|
let rName = ctx.regexMap.get(key);
|
|
2386
3193
|
if (rName === void 0) {
|
|
2387
|
-
rName =
|
|
2388
|
-
ctx.regexDecls.push(`const ${rName} = /${def.
|
|
3194
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3195
|
+
ctx.regexDecls.push(`const ${rName} = /${def.source}/${flags}`);
|
|
2389
3196
|
ctx.regexMap.set(key, rName);
|
|
2390
3197
|
}
|
|
2391
3198
|
const mv = v(ctx, "_m");
|
|
@@ -2470,6 +3277,31 @@ function failsAtStart(p) {
|
|
|
2470
3277
|
return false;
|
|
2471
3278
|
}
|
|
2472
3279
|
}
|
|
3280
|
+
function armStaticExpected(ctx, p) {
|
|
3281
|
+
return hoistExpected(ctx, staticExpectedArr([p]));
|
|
3282
|
+
}
|
|
3283
|
+
function canMatchEmptyAtStart(p) {
|
|
3284
|
+
const d = p._def;
|
|
3285
|
+
switch (d.tag) {
|
|
3286
|
+
case "regex":
|
|
3287
|
+
return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
|
|
3288
|
+
case "optional":
|
|
3289
|
+
return true;
|
|
3290
|
+
case "many":
|
|
3291
|
+
return true;
|
|
3292
|
+
case "transform":
|
|
3293
|
+
case "label":
|
|
3294
|
+
return canMatchEmptyAtStart(d.parser);
|
|
3295
|
+
case "literal":
|
|
3296
|
+
return d.value.length === 0;
|
|
3297
|
+
default:
|
|
3298
|
+
return false;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
function needsFirstSetGuard(p) {
|
|
3302
|
+
const fs2 = p._meta.firstSet;
|
|
3303
|
+
return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
3304
|
+
}
|
|
2473
3305
|
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
2474
3306
|
if (failsAtStart(p)) {
|
|
2475
3307
|
const r = emit(p, ctx, pos);
|
|
@@ -2537,7 +3369,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
2537
3369
|
const reKey = `${regexDef.source}/${cleanFlags}`;
|
|
2538
3370
|
let reVar = ctx.regexMap.get(reKey);
|
|
2539
3371
|
if (reVar === void 0) {
|
|
2540
|
-
reVar =
|
|
3372
|
+
reVar = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2541
3373
|
ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
|
|
2542
3374
|
ctx.regexMap.set(reKey, reVar);
|
|
2543
3375
|
}
|
|
@@ -2611,21 +3443,26 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
2611
3443
|
const resValV = v(ctx, "_crv");
|
|
2612
3444
|
const resEndV = v(ctx, "_cre");
|
|
2613
3445
|
const resOkV = v(ctx, "_crok");
|
|
3446
|
+
const codeV = v(ctx, "_chcode");
|
|
2614
3447
|
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2615
3448
|
const ind0 = ind(ctx);
|
|
2616
3449
|
const stmts = [
|
|
2617
3450
|
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
2618
|
-
`${ind0}let ${slots.join(", ")}
|
|
3451
|
+
`${ind0}let ${slots.join(", ")}`,
|
|
3452
|
+
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
2619
3453
|
];
|
|
2620
3454
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2621
3455
|
const p = def.parsers[i];
|
|
2622
3456
|
const gate = def.gates[i];
|
|
2623
3457
|
const autoNot = def.autoNot[i];
|
|
3458
|
+
const atStart = failsAtStart(p);
|
|
3459
|
+
const staticFx = armStaticExpected(ctx, p);
|
|
3460
|
+
const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
2624
3461
|
let gateCond = null;
|
|
2625
3462
|
if (gate) {
|
|
2626
3463
|
const gateIdx = ctx.mapFns.length;
|
|
2627
3464
|
ctx.mapFns.push(gate);
|
|
2628
|
-
gateCond =
|
|
3465
|
+
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
2629
3466
|
}
|
|
2630
3467
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
2631
3468
|
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
@@ -2635,39 +3472,41 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
2635
3472
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2636
3473
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2637
3474
|
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
3475
|
+
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
2638
3476
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2639
|
-
ctx
|
|
2640
|
-
|
|
3477
|
+
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
3478
|
+
ctx.indent += fsGuard ? 2 : 1;
|
|
2641
3479
|
if (markLeaves) {
|
|
2642
3480
|
stmts.push(
|
|
2643
|
-
`${
|
|
2644
|
-
`${
|
|
2645
|
-
`${
|
|
2646
|
-
`${
|
|
3481
|
+
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
3482
|
+
`${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
|
|
3483
|
+
`${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
3484
|
+
`${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
|
|
2647
3485
|
);
|
|
2648
3486
|
}
|
|
2649
|
-
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3487
|
+
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
|
|
2650
3488
|
stmts.push(...armStmts);
|
|
2651
|
-
ctx.indent--;
|
|
2652
3489
|
if (autoNot && autoNot.length > 0) {
|
|
2653
3490
|
const anCode = v(ctx, "_anc");
|
|
2654
3491
|
const rejectCond = autoNot.map(
|
|
2655
3492
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
|
|
2656
3493
|
).join(" || ");
|
|
2657
|
-
stmts.push(`${
|
|
2658
|
-
stmts.push(`${
|
|
2659
|
-
stmts.push(`${
|
|
2660
|
-
stmts.push(`${
|
|
2661
|
-
stmts.push(`${
|
|
2662
|
-
stmts.push(`${
|
|
2663
|
-
stmts.push(`${
|
|
2664
|
-
stmts.push(`${
|
|
2665
|
-
stmts.push(`${
|
|
2666
|
-
if (rollback) stmts.push(`${
|
|
3494
|
+
stmts.push(`${ind(ctx)}if (${okVar}) {`);
|
|
3495
|
+
stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
|
|
3496
|
+
stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
|
|
3497
|
+
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
3498
|
+
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
3499
|
+
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
3500
|
+
stmts.push(`${ind(ctx)} }`);
|
|
3501
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3502
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
3503
|
+
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
2667
3504
|
} else {
|
|
2668
|
-
stmts.push(`${
|
|
2669
|
-
stmts.push(`${
|
|
3505
|
+
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
3506
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
|
|
2670
3507
|
}
|
|
3508
|
+
ctx.indent -= fsGuard ? 2 : 1;
|
|
3509
|
+
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
2671
3510
|
stmts.push(`${ind0}}`);
|
|
2672
3511
|
}
|
|
2673
3512
|
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
@@ -2690,7 +3529,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2690
3529
|
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
2691
3530
|
const vv = v(ctx);
|
|
2692
3531
|
return {
|
|
2693
|
-
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} =
|
|
3532
|
+
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
2694
3533
|
valueVar: vv,
|
|
2695
3534
|
endVar: endV
|
|
2696
3535
|
};
|
|
@@ -2700,7 +3539,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2700
3539
|
function emitLiteralCondition(litVal, pos) {
|
|
2701
3540
|
const len = litVal.length;
|
|
2702
3541
|
if (len === 0) return "true";
|
|
2703
|
-
if (len >
|
|
3542
|
+
if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
|
|
2704
3543
|
const checks = [`${pos} + ${len} <= input.length`];
|
|
2705
3544
|
for (let i = 0; i < len; i++) {
|
|
2706
3545
|
const code = litVal.codePointAt(i);
|
|
@@ -2749,7 +3588,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2749
3588
|
itemPos = npV;
|
|
2750
3589
|
}
|
|
2751
3590
|
}
|
|
2752
|
-
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
3591
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
2753
3592
|
stmts.push(
|
|
2754
3593
|
...iterStmts,
|
|
2755
3594
|
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
|
|
@@ -2763,7 +3602,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2763
3602
|
function emitOptional(def, ctx, pos) {
|
|
2764
3603
|
const valV = v(ctx, "_opt");
|
|
2765
3604
|
const endV = v(ctx, "_opte");
|
|
2766
|
-
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
3605
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2767
3606
|
const ind0 = ind(ctx);
|
|
2768
3607
|
const stmts = [
|
|
2769
3608
|
...lblStmts,
|
|
@@ -2775,7 +3614,7 @@ function emitOptional(def, ctx, pos) {
|
|
|
2775
3614
|
function emitSepBy(_p, def, ctx, pos) {
|
|
2776
3615
|
const arrV = v(ctx, "_arr");
|
|
2777
3616
|
const curV = v(ctx, "_cur");
|
|
2778
|
-
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
3617
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
2779
3618
|
const stmts = [
|
|
2780
3619
|
`${ind(ctx)}const ${arrV} = []`,
|
|
2781
3620
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
@@ -2807,11 +3646,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2807
3646
|
);
|
|
2808
3647
|
sepAtPos = spV;
|
|
2809
3648
|
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}; `;
|
|
2810
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3649
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2811
3650
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
2812
3651
|
const npV = v(ctx, "_np");
|
|
2813
3652
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
2814
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3653
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2815
3654
|
stmts.push(
|
|
2816
3655
|
...nextStmts,
|
|
2817
3656
|
// item failed → unwind the separator too, back to the end of the last item
|
|
@@ -2824,11 +3663,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2824
3663
|
const spV = v(ctx, "_sp");
|
|
2825
3664
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
2826
3665
|
sepAtPos = spV;
|
|
2827
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3666
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2828
3667
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2829
3668
|
const npV = v(ctx, "_np");
|
|
2830
3669
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
2831
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3670
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2832
3671
|
stmts.push(
|
|
2833
3672
|
...nextStmts,
|
|
2834
3673
|
`${ind(ctx)}if (!${nextOk}) break`,
|
|
@@ -2845,10 +3684,10 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2845
3684
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
2846
3685
|
);
|
|
2847
3686
|
}
|
|
2848
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3687
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2849
3688
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2850
3689
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
2851
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
|
|
3690
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
2852
3691
|
stmts.push(
|
|
2853
3692
|
...nextStmts,
|
|
2854
3693
|
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
|
|
@@ -2906,7 +3745,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2906
3745
|
return { stmts, valueVar: valV, endVar: curV };
|
|
2907
3746
|
}
|
|
2908
3747
|
function emitNot(def, ctx, pos) {
|
|
2909
|
-
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3748
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2910
3749
|
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2911
3750
|
return {
|
|
2912
3751
|
stmts: [
|
|
@@ -2918,16 +3757,17 @@ function emitNot(def, ctx, pos) {
|
|
|
2918
3757
|
};
|
|
2919
3758
|
}
|
|
2920
3759
|
function emitNode(def, ctx, pos) {
|
|
2921
|
-
const
|
|
3760
|
+
const structural = def.build === void 0;
|
|
3761
|
+
const mkType = structural ? null : analyzeMkInlineBuild(def);
|
|
2922
3762
|
let buildIdx = null;
|
|
2923
|
-
if (!mkType) {
|
|
3763
|
+
if (!mkType && !structural) {
|
|
2924
3764
|
buildIdx = ctx.buildFns.length;
|
|
2925
3765
|
ctx.buildFns.push(def.build);
|
|
2926
3766
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
2927
3767
|
}
|
|
2928
3768
|
const i = ind(ctx);
|
|
2929
|
-
const capturesTrivia = mkType !== null || buildReadsTrivia(def);
|
|
2930
|
-
const clonesState = buildReadsState(def);
|
|
3769
|
+
const capturesTrivia = mkType !== null || structural || buildReadsTrivia(def);
|
|
3770
|
+
const clonesState = structural || buildReadsState(def);
|
|
2931
3771
|
const chV = v(ctx, "_ch");
|
|
2932
3772
|
const rawV = v(ctx, "_raw");
|
|
2933
3773
|
const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
@@ -2950,7 +3790,8 @@ function emitNode(def, ctx, pos) {
|
|
|
2950
3790
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
2951
3791
|
}
|
|
2952
3792
|
const ndV = v(ctx, "_nd");
|
|
2953
|
-
const
|
|
3793
|
+
const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
|
|
3794
|
+
const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
|
|
2954
3795
|
const finalExpr = def.collapse ? `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})` : ndExpr;
|
|
2955
3796
|
stmts.push(
|
|
2956
3797
|
`${i}const ${ndV} = ${finalExpr}`,
|
|
@@ -2975,8 +3816,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2975
3816
|
return { stmts, valueVar: vv, endVar: ev };
|
|
2976
3817
|
}
|
|
2977
3818
|
function emitLazy(p, def, ctx, pos) {
|
|
3819
|
+
const canonical = ctx.ruleNames?.get(p);
|
|
2978
3820
|
const usage = ctx.lazyUsage;
|
|
2979
|
-
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
3821
|
+
if (!canonical && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
2980
3822
|
let resolved;
|
|
2981
3823
|
try {
|
|
2982
3824
|
resolved = def.thunk();
|
|
@@ -2986,7 +3828,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2986
3828
|
return emit(resolved, ctx, pos);
|
|
2987
3829
|
}
|
|
2988
3830
|
if (!ctx.namedParsers.has(p)) {
|
|
2989
|
-
const fnName2 =
|
|
3831
|
+
const fnName2 = canonical ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
2990
3832
|
ctx.namedParsers.set(p, fnName2);
|
|
2991
3833
|
let resolved;
|
|
2992
3834
|
try {
|
|
@@ -2997,11 +3839,14 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2997
3839
|
}
|
|
2998
3840
|
const savedIndent = ctx.indent;
|
|
2999
3841
|
const savedFailLabel = ctx.failLabel;
|
|
3842
|
+
const savedRecord = ctx.recordFail;
|
|
3000
3843
|
ctx.indent = 1;
|
|
3001
3844
|
ctx.failLabel = "_pfail";
|
|
3845
|
+
ctx.recordFail = true;
|
|
3002
3846
|
const r = emit(resolved, ctx, "_pos");
|
|
3003
3847
|
ctx.indent = savedIndent;
|
|
3004
3848
|
ctx.failLabel = savedFailLabel;
|
|
3849
|
+
ctx.recordFail = savedRecord;
|
|
3005
3850
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
3006
3851
|
}
|
|
3007
3852
|
const fnName = ctx.namedParsers.get(p);
|
|
@@ -3101,7 +3946,7 @@ function emit(p, ctx, pos) {
|
|
|
3101
3946
|
return {
|
|
3102
3947
|
stmts: [
|
|
3103
3948
|
...inner.stmts,
|
|
3104
|
-
`${ind(ctx)}const ${mv} =
|
|
3949
|
+
`${ind(ctx)}const ${mv} = ${mfRef(ctx)}[${fnIdx}](${inner.valueVar}, { start: ${pos}, end: ${inner.endVar} })`
|
|
3105
3950
|
],
|
|
3106
3951
|
valueVar: mv,
|
|
3107
3952
|
endVar: inner.endVar
|
|
@@ -3164,7 +4009,7 @@ function emit(p, ctx, pos) {
|
|
|
3164
4009
|
const vv = v(ctx);
|
|
3165
4010
|
return {
|
|
3166
4011
|
stmts: [
|
|
3167
|
-
...emitIfFail(ctx,
|
|
4012
|
+
...emitIfFail(ctx, `!${mfRef(ctx)}[${fnIdx}](_ctx.state)`, failBody(ctx, '"gate"', pos)),
|
|
3168
4013
|
`${ind(ctx)}const ${vv} = null`
|
|
3169
4014
|
],
|
|
3170
4015
|
valueVar: vv,
|
|
@@ -3181,11 +4026,14 @@ function emit(p, ctx, pos) {
|
|
|
3181
4026
|
ctx.namedParsers.set(innerParser, fnName);
|
|
3182
4027
|
const savedIndent = ctx.indent;
|
|
3183
4028
|
const savedFailLabel = ctx.failLabel;
|
|
4029
|
+
const savedRecord = ctx.recordFail;
|
|
3184
4030
|
ctx.indent = 1;
|
|
3185
4031
|
ctx.failLabel = "_pfail";
|
|
4032
|
+
ctx.recordFail = true;
|
|
3186
4033
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
3187
4034
|
ctx.indent = savedIndent;
|
|
3188
4035
|
ctx.failLabel = savedFailLabel;
|
|
4036
|
+
ctx.recordFail = savedRecord;
|
|
3189
4037
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
3190
4038
|
}
|
|
3191
4039
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -3194,7 +4042,7 @@ function emit(p, ctx, pos) {
|
|
|
3194
4042
|
const ev = v(ctx, "_wce");
|
|
3195
4043
|
return {
|
|
3196
4044
|
stmts: [
|
|
3197
|
-
`${ind(ctx)}const ${rv} = { ..._ctx, state:
|
|
4045
|
+
`${ind(ctx)}const ${rv} = { ..._ctx, state: ${mfRef(ctx)}[${evIdx}]() }`,
|
|
3198
4046
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
3199
4047
|
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3200
4048
|
// and propagate the inner failure verbatim (interpreter parity).
|
|
@@ -3325,6 +4173,39 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3325
4173
|
for (const root of roots) walk2(root);
|
|
3326
4174
|
return { counts, recursive };
|
|
3327
4175
|
}
|
|
4176
|
+
function ruleDependencies(ruleMap) {
|
|
4177
|
+
const nameOf = /* @__PURE__ */ new Map();
|
|
4178
|
+
for (const [name, comb] of ruleMap) nameOf.set(comb, name);
|
|
4179
|
+
const deps = /* @__PURE__ */ new Map();
|
|
4180
|
+
for (const [name, comb] of ruleMap) {
|
|
4181
|
+
const found = /* @__PURE__ */ new Set();
|
|
4182
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4183
|
+
const walk2 = (p, isRoot) => {
|
|
4184
|
+
const def = p._def;
|
|
4185
|
+
const boundary = def.tag === "lazy" ? p._ruleName ?? nameOf.get(p) : nameOf.get(p);
|
|
4186
|
+
if (!isRoot && boundary !== void 0) {
|
|
4187
|
+
found.add(boundary);
|
|
4188
|
+
return;
|
|
4189
|
+
}
|
|
4190
|
+
if (seen.has(p)) return;
|
|
4191
|
+
seen.add(p);
|
|
4192
|
+
if (def.tag === "lazy") {
|
|
4193
|
+
let resolved;
|
|
4194
|
+
try {
|
|
4195
|
+
resolved = def.thunk();
|
|
4196
|
+
} catch {
|
|
4197
|
+
return;
|
|
4198
|
+
}
|
|
4199
|
+
walk2(resolved, false);
|
|
4200
|
+
return;
|
|
4201
|
+
}
|
|
4202
|
+
for (const child of childrenOf(def)) walk2(child, false);
|
|
4203
|
+
};
|
|
4204
|
+
walk2(comb, true);
|
|
4205
|
+
deps.set(name, [...found]);
|
|
4206
|
+
}
|
|
4207
|
+
return deps;
|
|
4208
|
+
}
|
|
3328
4209
|
function compile(combinator, mapFnSources) {
|
|
3329
4210
|
const ctx = {
|
|
3330
4211
|
vars: 0,
|
|
@@ -3333,6 +4214,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3333
4214
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3334
4215
|
expectedDecls: [],
|
|
3335
4216
|
expectedMap: /* @__PURE__ */ new Map(),
|
|
4217
|
+
recordFail: true,
|
|
3336
4218
|
mapFns: [],
|
|
3337
4219
|
mapFnSrcs: [],
|
|
3338
4220
|
buildFns: [],
|
|
@@ -3415,6 +4297,7 @@ function compileRuleMap(ruleMap) {
|
|
|
3415
4297
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3416
4298
|
expectedDecls: [],
|
|
3417
4299
|
expectedMap: /* @__PURE__ */ new Map(),
|
|
4300
|
+
recordFail: true,
|
|
3418
4301
|
mapFns: [],
|
|
3419
4302
|
mapFnSrcs: [],
|
|
3420
4303
|
buildFns: [],
|
|
@@ -3428,6 +4311,15 @@ function compileRuleMap(ruleMap) {
|
|
|
3428
4311
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
3429
4312
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule))
|
|
3430
4313
|
};
|
|
4314
|
+
const seenNames = /* @__PURE__ */ new Map();
|
|
4315
|
+
const ruleNames = /* @__PURE__ */ new Map();
|
|
4316
|
+
for (const [key, rule] of ruleMap) {
|
|
4317
|
+
const base = `_r_${key.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4318
|
+
const n = seenNames.get(base) ?? 0;
|
|
4319
|
+
seenNames.set(base, n + 1);
|
|
4320
|
+
ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
|
|
4321
|
+
}
|
|
4322
|
+
ctx.ruleNames = ruleNames;
|
|
3431
4323
|
const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
|
|
3432
4324
|
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
3433
4325
|
` : "";
|
|
@@ -3437,8 +4329,8 @@ function compileRuleMap(ruleMap) {
|
|
|
3437
4329
|
const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
|
|
3438
4330
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
3439
4331
|
if (!canInline) return null;
|
|
3440
|
-
const mfDecl = derivedSrcs?.length ? ` const
|
|
3441
|
-
const buildDecl = buildSources?.length ? ` const
|
|
4332
|
+
const mfDecl = derivedSrcs?.length ? ` const ${mfRef(ctx)} = [${derivedSrcs.join(", ")}]` : "";
|
|
4333
|
+
const buildDecl = buildSources?.length ? ` const ${buildRef(ctx)} = [${buildSources.join(", ")}]` : "";
|
|
3442
4334
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3443
4335
|
const hoistedDecls = [
|
|
3444
4336
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
@@ -3471,6 +4363,150 @@ function compileRuleMap(ruleMap) {
|
|
|
3471
4363
|
].join("\n");
|
|
3472
4364
|
return { keys: perEntry.map((e) => e.key), replacement };
|
|
3473
4365
|
}
|
|
4366
|
+
function compileLinkable(ruleMapArg, ns) {
|
|
4367
|
+
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4368
|
+
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4369
|
+
const d = val._def;
|
|
4370
|
+
if (d.tag !== "lazy") return true;
|
|
4371
|
+
try {
|
|
4372
|
+
d.thunk();
|
|
4373
|
+
return true;
|
|
4374
|
+
} catch {
|
|
4375
|
+
return false;
|
|
4376
|
+
}
|
|
4377
|
+
});
|
|
4378
|
+
const ctx = {
|
|
4379
|
+
vars: 0,
|
|
4380
|
+
indent: 1,
|
|
4381
|
+
regexDecls: [],
|
|
4382
|
+
regexMap: /* @__PURE__ */ new Map(),
|
|
4383
|
+
expectedDecls: [],
|
|
4384
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4385
|
+
recordFail: true,
|
|
4386
|
+
mapFns: [],
|
|
4387
|
+
mapFnSrcs: [],
|
|
4388
|
+
buildFns: [],
|
|
4389
|
+
buildSrcs: [],
|
|
4390
|
+
runtimeParsers: [],
|
|
4391
|
+
needsCollator: false,
|
|
4392
|
+
namedParsers: /* @__PURE__ */ new Map(),
|
|
4393
|
+
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4394
|
+
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4395
|
+
namedFnDecls: [],
|
|
4396
|
+
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4397
|
+
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4398
|
+
ns
|
|
4399
|
+
};
|
|
4400
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4401
|
+
const ruleNames = /* @__PURE__ */ new Map();
|
|
4402
|
+
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4403
|
+
for (const [key, rule] of ruleMap) {
|
|
4404
|
+
const base = `_r_${key.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4405
|
+
const n = seen.get(base) ?? 0;
|
|
4406
|
+
seen.set(base, n + 1);
|
|
4407
|
+
const fn = n === 0 ? base : `${base}$${n}`;
|
|
4408
|
+
ruleNames.set(rule, fn);
|
|
4409
|
+
fnNameToKey.set(fn, key);
|
|
4410
|
+
}
|
|
4411
|
+
ctx.ruleNames = ruleNames;
|
|
4412
|
+
{
|
|
4413
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
4414
|
+
const scanExternal = (p) => {
|
|
4415
|
+
if (scanned.has(p)) return;
|
|
4416
|
+
scanned.add(p);
|
|
4417
|
+
const def = p._def;
|
|
4418
|
+
if (def.tag === "lazy") {
|
|
4419
|
+
let resolved;
|
|
4420
|
+
try {
|
|
4421
|
+
resolved = def.thunk();
|
|
4422
|
+
} catch {
|
|
4423
|
+
resolved = void 0;
|
|
4424
|
+
}
|
|
4425
|
+
if (resolved === void 0) {
|
|
4426
|
+
const name = p._ruleName;
|
|
4427
|
+
if (name && !ctx.namedParsers.has(p)) {
|
|
4428
|
+
const fn = `_r_${name.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4429
|
+
ruleNames.set(p, fn);
|
|
4430
|
+
ctx.namedParsers.set(p, fn);
|
|
4431
|
+
}
|
|
4432
|
+
return;
|
|
4433
|
+
}
|
|
4434
|
+
scanExternal(resolved);
|
|
4435
|
+
return;
|
|
4436
|
+
}
|
|
4437
|
+
for (const child of childrenOf(def)) scanExternal(child);
|
|
4438
|
+
};
|
|
4439
|
+
for (const [, rule] of ruleMap) scanExternal(rule);
|
|
4440
|
+
}
|
|
4441
|
+
const perEntry = [];
|
|
4442
|
+
for (const [key, rule] of ruleMap) {
|
|
4443
|
+
const fn = ruleNames.get(rule);
|
|
4444
|
+
if (!ctx.namedParsers.has(rule)) {
|
|
4445
|
+
ctx.namedParsers.set(rule, fn);
|
|
4446
|
+
let resolved = rule;
|
|
4447
|
+
const def = rule._def;
|
|
4448
|
+
if (def.tag === "lazy") {
|
|
4449
|
+
try {
|
|
4450
|
+
resolved = def.thunk();
|
|
4451
|
+
} catch {
|
|
4452
|
+
return null;
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
const savedIndent = ctx.indent, savedFail = ctx.failLabel, savedRec = ctx.recordFail;
|
|
4456
|
+
ctx.indent = 1;
|
|
4457
|
+
ctx.failLabel = "_pfail";
|
|
4458
|
+
ctx.recordFail = true;
|
|
4459
|
+
const body = emit(resolved, ctx, "_pos");
|
|
4460
|
+
ctx.indent = savedIndent;
|
|
4461
|
+
ctx.failLabel = savedFail;
|
|
4462
|
+
ctx.recordFail = savedRec;
|
|
4463
|
+
pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
|
|
4464
|
+
}
|
|
4465
|
+
perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
|
|
4466
|
+
}
|
|
4467
|
+
if (ctx.runtimeParsers.length !== 0) return null;
|
|
4468
|
+
const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
|
|
4469
|
+
const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
|
|
4470
|
+
const ruleFns = /* @__PURE__ */ new Map();
|
|
4471
|
+
const privateFns = [];
|
|
4472
|
+
for (const decl of ctx.namedFnDecls) {
|
|
4473
|
+
const fnName = decl.match(/^function (\S+?)\(/)?.[1];
|
|
4474
|
+
const key = fnName ? fnNameToKey.get(fnName) : void 0;
|
|
4475
|
+
if (key !== void 0) ruleFns.set(key, decl);
|
|
4476
|
+
else privateFns.push(decl);
|
|
4477
|
+
}
|
|
4478
|
+
const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
|
|
4479
|
+
const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
|
|
4480
|
+
const prelude = [
|
|
4481
|
+
...ctx.regexDecls,
|
|
4482
|
+
...ctx.expectedDecls,
|
|
4483
|
+
mfDecl,
|
|
4484
|
+
buildDecl,
|
|
4485
|
+
...privateFns
|
|
4486
|
+
].filter(Boolean);
|
|
4487
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
4488
|
+
for (const { key, r } of perEntry) {
|
|
4489
|
+
wrappers.set(key, [
|
|
4490
|
+
`function(input, _pos, _ctx) {`,
|
|
4491
|
+
` let pos = _pos`,
|
|
4492
|
+
...r.stmts,
|
|
4493
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
4494
|
+
`}`
|
|
4495
|
+
].join("\n"));
|
|
4496
|
+
}
|
|
4497
|
+
return {
|
|
4498
|
+
ns,
|
|
4499
|
+
keys: perEntry.map((e) => e.key),
|
|
4500
|
+
prelude,
|
|
4501
|
+
ruleFns,
|
|
4502
|
+
wrappers,
|
|
4503
|
+
deps: ruleDependencies(ruleMap),
|
|
4504
|
+
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4505
|
+
needsCollator: ctx.needsCollator,
|
|
4506
|
+
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4507
|
+
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4508
|
+
};
|
|
4509
|
+
}
|
|
3474
4510
|
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
3475
4511
|
const bodyLines = [
|
|
3476
4512
|
` let pos = _pos`,
|
|
@@ -3509,6 +4545,48 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3509
4545
|
].join("\n");
|
|
3510
4546
|
}
|
|
3511
4547
|
|
|
4548
|
+
// src/compiler/linker.ts
|
|
4549
|
+
function fusedBody(pieces) {
|
|
4550
|
+
const winner = /* @__PURE__ */ new Map();
|
|
4551
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4552
|
+
for (const [k, p] of winner) {
|
|
4553
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
4554
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4557
|
+
const contributing = new Set(winner.values());
|
|
4558
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4559
|
+
const needsCollator = [...contributing].some((p) => p.needsCollator);
|
|
4560
|
+
const lines = [
|
|
4561
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4562
|
+
"const _pfFail = {}",
|
|
4563
|
+
"let _pfEnd",
|
|
4564
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4565
|
+
...needsCollator ? ["const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })"] : [],
|
|
4566
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4567
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
4568
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4569
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4570
|
+
];
|
|
4571
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4572
|
+
const body = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4573
|
+
const env = {};
|
|
4574
|
+
for (const p of contributing) {
|
|
4575
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4576
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4577
|
+
}
|
|
4578
|
+
return { body, env };
|
|
4579
|
+
}
|
|
4580
|
+
function emitFusedSource(pieces) {
|
|
4581
|
+
const { body, env } = fusedBody(pieces);
|
|
4582
|
+
if (Object.keys(env).length > 0) {
|
|
4583
|
+
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
4584
|
+
}
|
|
4585
|
+
return `/* @__PURE__ */ (() => {
|
|
4586
|
+
${body}
|
|
4587
|
+
})()`;
|
|
4588
|
+
}
|
|
4589
|
+
|
|
3512
4590
|
// src/combinators/scanTo.ts
|
|
3513
4591
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
3514
4592
|
const meta = {
|
|
@@ -3589,11 +4667,11 @@ function balanced(open, close, options = {}) {
|
|
|
3589
4667
|
return combi;
|
|
3590
4668
|
}
|
|
3591
4669
|
function firstSetClassChars(c) {
|
|
3592
|
-
const
|
|
3593
|
-
if (
|
|
3594
|
-
if (
|
|
4670
|
+
const fs2 = c._meta.firstSet;
|
|
4671
|
+
if (fs2.kind === "empty") return [];
|
|
4672
|
+
if (fs2.kind !== "ranges") return null;
|
|
3595
4673
|
const out = [];
|
|
3596
|
-
for (const { lo, hi } of
|
|
4674
|
+
for (const { lo, hi } of fs2.ranges) {
|
|
3597
4675
|
if (hi - lo > 8) return null;
|
|
3598
4676
|
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3599
4677
|
}
|
|
@@ -3603,6 +4681,12 @@ function escapeClassChar(ch) {
|
|
|
3603
4681
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3604
4682
|
}
|
|
3605
4683
|
|
|
4684
|
+
// src/cst/offset-model.ts
|
|
4685
|
+
var { isFinite } = Number;
|
|
4686
|
+
|
|
4687
|
+
// src/index.ts
|
|
4688
|
+
registerRegexAnalyzer(firstSetFromRegex);
|
|
4689
|
+
|
|
3606
4690
|
// src/plugin/evaluator.ts
|
|
3607
4691
|
function isWordFactory(v2) {
|
|
3608
4692
|
return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
|
|
@@ -3702,16 +4786,18 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
3702
4786
|
}
|
|
3703
4787
|
if (callee.name === "node" && code !== void 0) {
|
|
3704
4788
|
const [typeArg, parserArg, buildArg, optsArg] = node2.arguments;
|
|
3705
|
-
if (!typeArg || !parserArg ||
|
|
4789
|
+
if (!typeArg || !parserArg || typeArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
|
|
3706
4790
|
const typeVal = anyValue(typeArg, scope, code);
|
|
3707
4791
|
if (typeof typeVal !== "string") return null;
|
|
3708
4792
|
const inner = anyValue(parserArg, scope, code, mfs);
|
|
3709
4793
|
if (!isCombinator(inner)) return null;
|
|
3710
|
-
const
|
|
4794
|
+
const be = buildArg;
|
|
4795
|
+
const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !(be.type === "Identifier" && be.name === "undefined");
|
|
4796
|
+
const buildSrc = hasBuild ? code.slice(be.start, be.end) : void 0;
|
|
3711
4797
|
const collapse = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticCollapseOption(optsArg) : false;
|
|
3712
4798
|
try {
|
|
3713
|
-
const combi = node(typeVal, inner, () => null, collapse ? { collapse: true } : void 0);
|
|
3714
|
-
if (combi._def.tag === "node") combi._def.buildSrc = buildSrc;
|
|
4799
|
+
const combi = node(typeVal, inner, hasBuild ? () => null : void 0, collapse ? { collapse: true } : void 0);
|
|
4800
|
+
if (combi._def.tag === "node" && buildSrc !== void 0) combi._def.buildSrc = buildSrc;
|
|
3715
4801
|
return combi;
|
|
3716
4802
|
} catch {
|
|
3717
4803
|
return null;
|
|
@@ -3897,6 +4983,49 @@ function evaluateCombinatorArray(node2, scope, code) {
|
|
|
3897
4983
|
if (!val.every(isCombinator)) return null;
|
|
3898
4984
|
return val;
|
|
3899
4985
|
}
|
|
4986
|
+
function propKey(prop) {
|
|
4987
|
+
if (prop.type !== "Property" || prop.computed || !prop.key) return null;
|
|
4988
|
+
if (prop.key.type === "Identifier") return prop.key.name ?? null;
|
|
4989
|
+
if (prop.key.type === "Literal") return String(prop.key.value);
|
|
4990
|
+
return null;
|
|
4991
|
+
}
|
|
4992
|
+
function collectRuleKeys(retObj) {
|
|
4993
|
+
const out = [];
|
|
4994
|
+
for (const prop of retObj.properties) {
|
|
4995
|
+
const key = propKey(prop);
|
|
4996
|
+
if (!key) return null;
|
|
4997
|
+
out.push(key);
|
|
4998
|
+
}
|
|
4999
|
+
return out;
|
|
5000
|
+
}
|
|
5001
|
+
function flattenRuleEntries(retObj, scope, code) {
|
|
5002
|
+
const out = [];
|
|
5003
|
+
for (const prop of retObj.properties) {
|
|
5004
|
+
const key = propKey(prop);
|
|
5005
|
+
if (!key) return null;
|
|
5006
|
+
out.push({ key, value: prop.value, scope, code });
|
|
5007
|
+
}
|
|
5008
|
+
return out;
|
|
5009
|
+
}
|
|
5010
|
+
function evalBodyStatements(statements, scope, code) {
|
|
5011
|
+
const bodyMfs = [];
|
|
5012
|
+
for (const stmt of statements) {
|
|
5013
|
+
for (const d of stmt.declarations) {
|
|
5014
|
+
const decl = d;
|
|
5015
|
+
if (!decl.init) return false;
|
|
5016
|
+
const id = decl.id;
|
|
5017
|
+
if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return false;
|
|
5018
|
+
const name = id.name;
|
|
5019
|
+
const before = bodyMfs.length;
|
|
5020
|
+
const val = anyValue(decl.init, scope, code, bodyMfs);
|
|
5021
|
+
if (val === null) return false;
|
|
5022
|
+
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
5023
|
+
if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
5024
|
+
else scope.set(name, val);
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
return true;
|
|
5028
|
+
}
|
|
3900
5029
|
function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
3901
5030
|
if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
|
|
3902
5031
|
const factory = factoryNode;
|
|
@@ -3929,40 +5058,41 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
|
3929
5058
|
if (!returnExpr) return null;
|
|
3930
5059
|
const retObj = returnExpr.type === "ParenthesizedExpression" ? returnExpr.expression : returnExpr;
|
|
3931
5060
|
if (retObj.type !== "ObjectExpression") return null;
|
|
5061
|
+
const keys = collectRuleKeys(retObj);
|
|
5062
|
+
if (!keys) return null;
|
|
3932
5063
|
const ruleRefs = /* @__PURE__ */ new Map();
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
5064
|
+
const tagRef = (r, name) => {
|
|
5065
|
+
r._ruleName = name;
|
|
5066
|
+
};
|
|
5067
|
+
for (const key of keys) {
|
|
5068
|
+
if (!ruleRefs.has(key)) {
|
|
5069
|
+
const r = ref();
|
|
5070
|
+
tagRef(r, key);
|
|
5071
|
+
ruleRefs.set(key, r);
|
|
5072
|
+
}
|
|
3940
5073
|
}
|
|
3941
|
-
const
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
const name = id.name;
|
|
3951
|
-
const before = bodyMfs.length;
|
|
3952
|
-
const val = anyValue(decl.init, localScope, code, bodyMfs);
|
|
3953
|
-
if (val === null) return null;
|
|
3954
|
-
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
3955
|
-
if (isCombinator(val)) {
|
|
3956
|
-
localScope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
3957
|
-
} else {
|
|
3958
|
-
localScope.set(name, val);
|
|
5074
|
+
const externalRefs = /* @__PURE__ */ new Map();
|
|
5075
|
+
const gProxy = new Proxy(Object.fromEntries(ruleRefs), {
|
|
5076
|
+
get(target, prop) {
|
|
5077
|
+
if (typeof prop !== "string" || prop in target) return target[prop];
|
|
5078
|
+
let ext = externalRefs.get(prop);
|
|
5079
|
+
if (!ext) {
|
|
5080
|
+
ext = ref();
|
|
5081
|
+
tagRef(ext, prop);
|
|
5082
|
+
externalRefs.set(prop, ext);
|
|
3959
5083
|
}
|
|
5084
|
+
return ext;
|
|
3960
5085
|
}
|
|
3961
|
-
}
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
5086
|
+
});
|
|
5087
|
+
const localScope = new Map(scope);
|
|
5088
|
+
localScope.set(proxyName, gProxy);
|
|
5089
|
+
if (!evalBodyStatements(statements, localScope, code)) return null;
|
|
5090
|
+
const entries = flattenRuleEntries(retObj, localScope, code);
|
|
5091
|
+
if (!entries) return null;
|
|
5092
|
+
const finalByKey = /* @__PURE__ */ new Map();
|
|
5093
|
+
for (const e of entries) finalByKey.set(e.key, e);
|
|
5094
|
+
for (const [key, e] of finalByKey) {
|
|
5095
|
+
const val = anyValue(e.value, e.scope, e.code, mapFnSources);
|
|
3966
5096
|
if (!isCombinator(val)) return null;
|
|
3967
5097
|
ruleRefs.get(key).define(val);
|
|
3968
5098
|
}
|
|
@@ -4019,7 +5149,36 @@ function referencesAny(node2, names, scope) {
|
|
|
4019
5149
|
}
|
|
4020
5150
|
|
|
4021
5151
|
// src/plugin/index.ts
|
|
5152
|
+
var import_node_crypto = require("node:crypto");
|
|
4022
5153
|
var PARSEMAN_MODULE = "parseman";
|
|
5154
|
+
var _compiledResolver = null;
|
|
5155
|
+
function getCompiledResolver() {
|
|
5156
|
+
return _compiledResolver ??= new import_oxc_resolver.ResolverFactory({
|
|
5157
|
+
extensions: [".js", ".mjs", ".cjs"],
|
|
5158
|
+
conditionNames: ["import", "require", "default"],
|
|
5159
|
+
mainFields: ["module", "main"]
|
|
5160
|
+
});
|
|
5161
|
+
}
|
|
5162
|
+
var _moduleParseCache = /* @__PURE__ */ new Map();
|
|
5163
|
+
function parseModuleCached(filePath) {
|
|
5164
|
+
let mtimeMs;
|
|
5165
|
+
try {
|
|
5166
|
+
mtimeMs = fs.statSync(filePath).mtimeMs;
|
|
5167
|
+
} catch {
|
|
5168
|
+
return null;
|
|
5169
|
+
}
|
|
5170
|
+
const hit = _moduleParseCache.get(filePath);
|
|
5171
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
|
|
5172
|
+
let parsed = null;
|
|
5173
|
+
try {
|
|
5174
|
+
const src = fs.readFileSync(filePath, "utf8");
|
|
5175
|
+
const r = (0, import_oxc_parser.parseSync)(filePath, src);
|
|
5176
|
+
if (r.errors.length === 0) parsed = { body: r.program.body, src };
|
|
5177
|
+
} catch {
|
|
5178
|
+
}
|
|
5179
|
+
_moduleParseCache.set(filePath, { mtimeMs, parsed });
|
|
5180
|
+
return parsed;
|
|
5181
|
+
}
|
|
4023
5182
|
var plugin_default = (0, import_unplugin.createUnplugin)((opts = {}) => ({
|
|
4024
5183
|
name: "parseman",
|
|
4025
5184
|
// Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
|
|
@@ -4044,6 +5203,77 @@ var plugin_default = (0, import_unplugin.createUnplugin)((opts = {}) => ({
|
|
|
4044
5203
|
return { code: result.code, map: result.map };
|
|
4045
5204
|
}
|
|
4046
5205
|
}));
|
|
5206
|
+
function exportLocalName(body, exported) {
|
|
5207
|
+
for (const st of body) {
|
|
5208
|
+
if (st.type !== "ExportNamedDeclaration") continue;
|
|
5209
|
+
const decl = st.declaration;
|
|
5210
|
+
if (decl?.type === "VariableDeclaration") {
|
|
5211
|
+
for (const d of decl.declarations ?? []) {
|
|
5212
|
+
const idn = d.id;
|
|
5213
|
+
if (idn?.type === "Identifier" && idn.name === exported) return exported;
|
|
5214
|
+
}
|
|
5215
|
+
}
|
|
5216
|
+
for (const sp of st.specifiers ?? []) {
|
|
5217
|
+
const exp = sp.exported?.name;
|
|
5218
|
+
const loc = sp.local?.name;
|
|
5219
|
+
if (exp === exported && loc) return loc;
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5222
|
+
return null;
|
|
5223
|
+
}
|
|
5224
|
+
function topLevelInit(body, name) {
|
|
5225
|
+
for (const st of body) {
|
|
5226
|
+
const vd = st.type === "VariableDeclaration" ? st : st.type === "ExportNamedDeclaration" && st.declaration?.type === "VariableDeclaration" ? st.declaration : null;
|
|
5227
|
+
if (!vd) continue;
|
|
5228
|
+
for (const d of vd.declarations ?? []) {
|
|
5229
|
+
const idn = d.id;
|
|
5230
|
+
if (idn?.type === "Identifier" && idn.name === name && d.init) return d.init;
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
return null;
|
|
5234
|
+
}
|
|
5235
|
+
function isComposedPiecesSymbol(n) {
|
|
5236
|
+
if (!n || n.type !== "CallExpression") return false;
|
|
5237
|
+
const callee = n.callee;
|
|
5238
|
+
const obj = callee?.object?.name;
|
|
5239
|
+
const prop = callee?.property?.name;
|
|
5240
|
+
const arg0 = n.arguments?.[0];
|
|
5241
|
+
return obj === "Symbol" && prop === "for" && arg0?.value === "parseman.composedPieces";
|
|
5242
|
+
}
|
|
5243
|
+
function findCarriedPiecesLiteral(root) {
|
|
5244
|
+
let found = null;
|
|
5245
|
+
const visit = (n) => {
|
|
5246
|
+
if (found || !n || typeof n !== "object") return;
|
|
5247
|
+
if (Array.isArray(n)) {
|
|
5248
|
+
for (const c of n) visit(c);
|
|
5249
|
+
return;
|
|
5250
|
+
}
|
|
5251
|
+
const node2 = n;
|
|
5252
|
+
if (node2.type === "CallExpression") {
|
|
5253
|
+
const callee = node2.callee;
|
|
5254
|
+
const obj = callee?.object?.name;
|
|
5255
|
+
const prop = callee?.property?.name;
|
|
5256
|
+
const args = node2.arguments;
|
|
5257
|
+
if (obj === "Object" && prop === "defineProperty" && args && isComposedPiecesSymbol(args[1])) {
|
|
5258
|
+
const descriptor = args[2];
|
|
5259
|
+
for (const p of descriptor?.properties ?? []) {
|
|
5260
|
+
const key = p.key;
|
|
5261
|
+
if ((key?.name === "value" || key?.value === "value") && p.value) {
|
|
5262
|
+
found = { start: p.value.start, end: p.value.end };
|
|
5263
|
+
return;
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
for (const k in node2) {
|
|
5269
|
+
if (k === "type" || k === "start" || k === "end") continue;
|
|
5270
|
+
const v2 = node2[k];
|
|
5271
|
+
if (v2 && typeof v2 === "object") visit(v2);
|
|
5272
|
+
}
|
|
5273
|
+
};
|
|
5274
|
+
visit(root);
|
|
5275
|
+
return found;
|
|
5276
|
+
}
|
|
4047
5277
|
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE])) {
|
|
4048
5278
|
let result;
|
|
4049
5279
|
try {
|
|
@@ -4055,15 +5285,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
4055
5285
|
const body = result.program.body;
|
|
4056
5286
|
const macroImports = [];
|
|
4057
5287
|
const allNames = /* @__PURE__ */ new Set();
|
|
5288
|
+
const importBindings = /* @__PURE__ */ new Map();
|
|
4058
5289
|
for (const stmt of body) {
|
|
4059
5290
|
if (stmt.type !== "ImportDeclaration") continue;
|
|
4060
5291
|
const s = stmt;
|
|
4061
|
-
|
|
4062
|
-
const isMacro = s.attributes.some((a) => {
|
|
5292
|
+
const isMacro = moduleAliases.has(s.source.value) && s.attributes.some((a) => {
|
|
4063
5293
|
const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
|
|
4064
5294
|
return key === "type" && a.value.value === "macro";
|
|
4065
5295
|
});
|
|
4066
|
-
if (!isMacro)
|
|
5296
|
+
if (!isMacro) {
|
|
5297
|
+
for (const spec of s.specifiers) {
|
|
5298
|
+
if (spec.type === "ImportSpecifier") {
|
|
5299
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
5300
|
+
importBindings.set(spec.local.name, { source: s.source.value, imported });
|
|
5301
|
+
}
|
|
5302
|
+
}
|
|
5303
|
+
continue;
|
|
5304
|
+
}
|
|
4067
5305
|
const names = /* @__PURE__ */ new Set();
|
|
4068
5306
|
for (const spec of s.specifiers) {
|
|
4069
5307
|
if (spec.type === "ImportSpecifier") names.add(spec.local.name);
|
|
@@ -4106,6 +5344,89 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
4106
5344
|
return { replacement: compiled.replacement, ruleMap };
|
|
4107
5345
|
};
|
|
4108
5346
|
const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
|
|
5347
|
+
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
5348
|
+
const localRuleMaps = /* @__PURE__ */ new Map();
|
|
5349
|
+
const nsFor = (label2) => `_${(0, import_node_crypto.createHash)("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
5350
|
+
const serializePieces = (p) => {
|
|
5351
|
+
const mapLit = (m) => `new Map([${[...m].map(([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(v2)}]`).join(", ")}])`;
|
|
5352
|
+
return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude)}, ruleFns: ${mapLit(p.ruleFns)}, wrappers: ${mapLit(p.wrappers)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsCollator: ${p.needsCollator}, mfFns: [], buildFns: [] }`;
|
|
5353
|
+
};
|
|
5354
|
+
const serializeList = (list) => `[${list.map(serializePieces).join(", ")}]`;
|
|
5355
|
+
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ (() => { const _g = ${grammarExpr}; Object.defineProperty(_g, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)} }); return _g })()`;
|
|
5356
|
+
const localComposedPieces = /* @__PURE__ */ new Map();
|
|
5357
|
+
const importedPiecesCache = /* @__PURE__ */ new Map();
|
|
5358
|
+
const importedPieces = (localName) => {
|
|
5359
|
+
if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
|
|
5360
|
+
let result2 = null;
|
|
5361
|
+
const binding = importBindings.get(localName);
|
|
5362
|
+
if (binding) {
|
|
5363
|
+
let file = null;
|
|
5364
|
+
try {
|
|
5365
|
+
file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
|
|
5366
|
+
} catch {
|
|
5367
|
+
}
|
|
5368
|
+
const mod = file ? parseModuleCached(file) : null;
|
|
5369
|
+
if (mod) {
|
|
5370
|
+
const localFor = exportLocalName(mod.body, binding.imported);
|
|
5371
|
+
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
5372
|
+
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
5373
|
+
if (literalRange) {
|
|
5374
|
+
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
5375
|
+
try {
|
|
5376
|
+
result2 = new Function(`return (${literal2})`)();
|
|
5377
|
+
} catch {
|
|
5378
|
+
result2 = null;
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
importedPiecesCache.set(localName, result2);
|
|
5384
|
+
return result2;
|
|
5385
|
+
};
|
|
5386
|
+
const argPieces = (arg, label2) => {
|
|
5387
|
+
if (isRulesCall(arg)) {
|
|
5388
|
+
const factory = arg.arguments[0];
|
|
5389
|
+
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
5390
|
+
const p = rm ? compileLinkable([...rm], nsFor(label2)) : null;
|
|
5391
|
+
return p ? [p] : null;
|
|
5392
|
+
}
|
|
5393
|
+
if (arg.type === "Identifier") {
|
|
5394
|
+
const name = arg.name;
|
|
5395
|
+
const rm = localRuleMaps.get(name);
|
|
5396
|
+
if (rm) {
|
|
5397
|
+
const p = compileLinkable([...rm], nsFor(label2));
|
|
5398
|
+
return p ? [p] : null;
|
|
5399
|
+
}
|
|
5400
|
+
const composed = localComposedPieces.get(name);
|
|
5401
|
+
if (composed) return composed;
|
|
5402
|
+
return importedPieces(name);
|
|
5403
|
+
}
|
|
5404
|
+
return null;
|
|
5405
|
+
};
|
|
5406
|
+
const compileComposeCall = (init) => {
|
|
5407
|
+
const args = init.arguments;
|
|
5408
|
+
const arr = args[0];
|
|
5409
|
+
if (!arr || arr.type !== "ArrayExpression") {
|
|
5410
|
+
warn(init.start, "compose(): expected a static array of grammars/artifacts");
|
|
5411
|
+
return null;
|
|
5412
|
+
}
|
|
5413
|
+
const elements = arr.elements;
|
|
5414
|
+
const pieces = [];
|
|
5415
|
+
for (let i = 0; i < elements.length; i++) {
|
|
5416
|
+
const list = argPieces(elements[i], `compose${init.start}_${i}`);
|
|
5417
|
+
if (!list) {
|
|
5418
|
+
warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
|
|
5419
|
+
return null;
|
|
5420
|
+
}
|
|
5421
|
+
pieces.push(...list);
|
|
5422
|
+
}
|
|
5423
|
+
try {
|
|
5424
|
+
return { replacement: emitFusedSource(pieces), pieces };
|
|
5425
|
+
} catch (e) {
|
|
5426
|
+
warn(init.start, `compose(): ${e.message}; falling back to runtime`);
|
|
5427
|
+
return null;
|
|
5428
|
+
}
|
|
5429
|
+
};
|
|
4109
5430
|
const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
|
|
4110
5431
|
const refNames = /* @__PURE__ */ new Set();
|
|
4111
5432
|
for (const stmt of body) {
|
|
@@ -4184,11 +5505,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
4184
5505
|
if (isRulesCall(init)) {
|
|
4185
5506
|
const compiledRules = compileRulesFactory(init, varName);
|
|
4186
5507
|
if (!compiledRules) continue;
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
5508
|
+
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
5509
|
+
let replacement = compiledRules.replacement;
|
|
5510
|
+
if (exportPrefix) {
|
|
5511
|
+
const pieces = compileLinkable([...compiledRules.ruleMap], nsFor(varName));
|
|
5512
|
+
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
5513
|
+
replacement = withCarriedPieces(replacement, [pieces]);
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
replacements.push({ start: init.start, end: init.end, replacement });
|
|
5517
|
+
continue;
|
|
5518
|
+
}
|
|
5519
|
+
if (isComposeCall(init)) {
|
|
5520
|
+
const fused = compileComposeCall(init);
|
|
5521
|
+
if (!fused) continue;
|
|
5522
|
+
localComposedPieces.set(varName, fused.pieces);
|
|
5523
|
+
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.pieces) : fused.replacement;
|
|
5524
|
+
replacements.push({ start: init.start, end: init.end, replacement });
|
|
4192
5525
|
continue;
|
|
4193
5526
|
}
|
|
4194
5527
|
const mapFnSources = [];
|