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.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/plugin/index.ts
|
|
2
|
+
import * as fs from "node:fs";
|
|
2
3
|
import { createUnplugin } from "unplugin";
|
|
3
4
|
import { parseSync } from "oxc-parser";
|
|
5
|
+
import { ResolverFactory } from "oxc-resolver";
|
|
4
6
|
import MagicString from "magic-string";
|
|
5
7
|
|
|
6
8
|
// src/combinators/first-set.ts
|
|
@@ -286,6 +288,36 @@ function literal(value, opts = {}) {
|
|
|
286
288
|
}
|
|
287
289
|
|
|
288
290
|
// src/combinators/regex.ts
|
|
291
|
+
var firstSetAnalyzer = null;
|
|
292
|
+
function registerRegexAnalyzer(analyzer) {
|
|
293
|
+
firstSetAnalyzer = analyzer;
|
|
294
|
+
}
|
|
295
|
+
var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
|
|
296
|
+
function regex(pattern, flags = "") {
|
|
297
|
+
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
298
|
+
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
299
|
+
const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
300
|
+
const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
|
|
301
|
+
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
302
|
+
return {
|
|
303
|
+
_tag: "regex",
|
|
304
|
+
_meta: meta,
|
|
305
|
+
_def: { tag: "regex", source, flags: resolvedFlags },
|
|
306
|
+
parse(input, pos, ctx) {
|
|
307
|
+
anchored.lastIndex = pos;
|
|
308
|
+
const m = anchored.exec(input);
|
|
309
|
+
if (m === null) {
|
|
310
|
+
return failAt(ctx, [`/${source}/`], pos);
|
|
311
|
+
}
|
|
312
|
+
const span = { start: pos, end: pos + m[0].length };
|
|
313
|
+
const leaf = { _tag: "leaf", value: m[0], span };
|
|
314
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
315
|
+
return { ok: true, value: m[0], span };
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/combinators/regex-analyze.ts
|
|
289
321
|
import regexpTree from "regexp-tree";
|
|
290
322
|
function firstSetFromRegex(pattern) {
|
|
291
323
|
try {
|
|
@@ -328,15 +360,15 @@ function extractFirstSet(node2) {
|
|
|
328
360
|
case "Alternative": {
|
|
329
361
|
const exprs = node2.expressions;
|
|
330
362
|
if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
|
|
331
|
-
let
|
|
363
|
+
let fs2 = empty();
|
|
332
364
|
let canNL = false;
|
|
333
365
|
for (const expr of exprs) {
|
|
334
366
|
const r = extractFirstSet(expr);
|
|
335
|
-
|
|
367
|
+
fs2 = union(fs2, r.firstSet);
|
|
336
368
|
canNL = canNL || r.canMatchNewline;
|
|
337
369
|
if (!canBeEmpty(expr)) break;
|
|
338
370
|
}
|
|
339
|
-
return { firstSet:
|
|
371
|
+
return { firstSet: fs2, canMatchNewline: canNL };
|
|
340
372
|
}
|
|
341
373
|
case "Char": {
|
|
342
374
|
const kind = node2.kind;
|
|
@@ -363,22 +395,22 @@ function extractFirstSet(node2) {
|
|
|
363
395
|
}
|
|
364
396
|
case "CharacterClass": {
|
|
365
397
|
const expressions = node2.expressions;
|
|
366
|
-
let
|
|
398
|
+
let fs2 = empty();
|
|
367
399
|
let canNL = false;
|
|
368
400
|
for (const expr of expressions) {
|
|
369
401
|
if (expr.type === "ClassRange") {
|
|
370
402
|
const from = expr.from.codePoint;
|
|
371
403
|
const to = expr.to.codePoint;
|
|
372
|
-
|
|
404
|
+
fs2 = union(fs2, fromRange(from, to));
|
|
373
405
|
if (from <= 10 && 10 <= to) canNL = true;
|
|
374
406
|
} else if (expr.type === "Char") {
|
|
375
407
|
const r = extractFirstSet(expr);
|
|
376
|
-
|
|
408
|
+
fs2 = union(fs2, r.firstSet);
|
|
377
409
|
canNL = canNL || r.canMatchNewline;
|
|
378
410
|
}
|
|
379
411
|
}
|
|
380
412
|
if (node2.negative) return { firstSet: any(), canMatchNewline: true };
|
|
381
|
-
return { firstSet:
|
|
413
|
+
return { firstSet: fs2, canMatchNewline: canNL };
|
|
382
414
|
}
|
|
383
415
|
case "Repetition": {
|
|
384
416
|
return extractFirstSet(node2.expression);
|
|
@@ -390,40 +422,6 @@ function extractFirstSet(node2) {
|
|
|
390
422
|
return { firstSet: any(), canMatchNewline: true };
|
|
391
423
|
}
|
|
392
424
|
}
|
|
393
|
-
function optimizeRegex(source, flags) {
|
|
394
|
-
try {
|
|
395
|
-
const result = regexpTree.optimize(`/${source}/${flags}`);
|
|
396
|
-
const str = result.toString();
|
|
397
|
-
const lastSlash = str.lastIndexOf("/");
|
|
398
|
-
return str.slice(1, lastSlash);
|
|
399
|
-
} catch {
|
|
400
|
-
return source;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
function regex(pattern, flags = "") {
|
|
404
|
-
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
405
|
-
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
406
|
-
const optimizedSource = optimizeRegex(source, resolvedFlags);
|
|
407
|
-
const anchored = new RegExp(optimizedSource, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
408
|
-
const { firstSet, canMatchNewline } = firstSetFromRegex(source);
|
|
409
|
-
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
410
|
-
return {
|
|
411
|
-
_tag: "regex",
|
|
412
|
-
_meta: meta,
|
|
413
|
-
_def: { tag: "regex", source, flags: resolvedFlags, optimizedSource },
|
|
414
|
-
parse(input, pos, ctx) {
|
|
415
|
-
anchored.lastIndex = pos;
|
|
416
|
-
const m = anchored.exec(input);
|
|
417
|
-
if (m === null) {
|
|
418
|
-
return failAt(ctx, [`/${source}/`], pos);
|
|
419
|
-
}
|
|
420
|
-
const span = { start: pos, end: pos + m[0].length };
|
|
421
|
-
const leaf = { _tag: "leaf", value: m[0], span };
|
|
422
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
423
|
-
return { ok: true, value: m[0], span };
|
|
424
|
-
}
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
425
|
|
|
428
426
|
// src/combinators/keywords.ts
|
|
429
427
|
function word(str, boundary = "_0-9A-Za-z") {
|
|
@@ -687,10 +685,10 @@ function codesToFirstSet(codes) {
|
|
|
687
685
|
ranges.push({ lo, hi });
|
|
688
686
|
return { kind: "ranges", ranges };
|
|
689
687
|
}
|
|
690
|
-
function inFirstSet(code,
|
|
691
|
-
if (
|
|
692
|
-
if (
|
|
693
|
-
for (const r of
|
|
688
|
+
function inFirstSet(code, fs2) {
|
|
689
|
+
if (fs2.kind === "any") return true;
|
|
690
|
+
if (fs2.kind === "empty") return false;
|
|
691
|
+
for (const r of fs2.ranges) if (code >= r.lo && code <= r.hi) return true;
|
|
694
692
|
return false;
|
|
695
693
|
}
|
|
696
694
|
function areDisjoint(sets) {
|
|
@@ -1123,12 +1121,14 @@ function confirmedBuildArity(src) {
|
|
|
1123
1121
|
return parts.length;
|
|
1124
1122
|
}
|
|
1125
1123
|
function buildReadsTrivia(def) {
|
|
1124
|
+
if (!def.build) return true;
|
|
1126
1125
|
const src = def.buildSrc ?? def.build.toString();
|
|
1127
1126
|
const arity = confirmedBuildArity(src);
|
|
1128
1127
|
if (arity === null) return true;
|
|
1129
1128
|
return arity >= 4;
|
|
1130
1129
|
}
|
|
1131
1130
|
function buildReadsState(def) {
|
|
1131
|
+
if (!def.build) return true;
|
|
1132
1132
|
const src = def.buildSrc ?? def.build.toString();
|
|
1133
1133
|
const arity = confirmedBuildArity(src);
|
|
1134
1134
|
if (arity === null) return true;
|
|
@@ -1147,8 +1147,8 @@ function node(type, combinator, build, opts) {
|
|
|
1147
1147
|
};
|
|
1148
1148
|
const collapse = opts?.collapse === true;
|
|
1149
1149
|
const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
|
|
1150
|
-
const capturesTrivia = buildReadsTrivia(def);
|
|
1151
|
-
const clonesState = buildReadsState(def);
|
|
1150
|
+
const capturesTrivia = build ? buildReadsTrivia(def) : true;
|
|
1151
|
+
const clonesState = build ? buildReadsState(def) : true;
|
|
1152
1152
|
return {
|
|
1153
1153
|
_tag: "node",
|
|
1154
1154
|
_meta: meta,
|
|
@@ -1159,13 +1159,8 @@ function node(type, combinator, build, opts) {
|
|
|
1159
1159
|
const r = combinator.parse(input, pos, ctx);
|
|
1160
1160
|
const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
1161
1161
|
if (!r.ok) return r;
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
rawChildren,
|
|
1165
|
-
r.span,
|
|
1166
|
-
triviaLog,
|
|
1167
|
-
clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
|
|
1168
|
-
);
|
|
1162
|
+
const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
1163
|
+
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 };
|
|
1169
1164
|
const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
|
|
1170
1165
|
const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
|
|
1171
1166
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
@@ -1308,8 +1303,14 @@ function staticExpected(c) {
|
|
|
1308
1303
|
case "transform":
|
|
1309
1304
|
case "not":
|
|
1310
1305
|
return staticExpected(def.parser);
|
|
1311
|
-
case "lazy":
|
|
1312
|
-
|
|
1306
|
+
case "lazy": {
|
|
1307
|
+
const name = c._ruleName;
|
|
1308
|
+
try {
|
|
1309
|
+
return staticExpected(def.thunk());
|
|
1310
|
+
} catch {
|
|
1311
|
+
return name ? [name] : [];
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1313
1314
|
default:
|
|
1314
1315
|
return [];
|
|
1315
1316
|
}
|
|
@@ -1339,8 +1340,29 @@ function expect(combinator, label2) {
|
|
|
1339
1340
|
var BACKSLASH = 92;
|
|
1340
1341
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1341
1342
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1343
|
+
var SPACE_RANGES = [
|
|
1344
|
+
[9, 13],
|
|
1345
|
+
[32, 32],
|
|
1346
|
+
[160, 160],
|
|
1347
|
+
[5760, 5760],
|
|
1348
|
+
[8192, 8202],
|
|
1349
|
+
[8232, 8232],
|
|
1350
|
+
[8233, 8233],
|
|
1351
|
+
[8239, 8239],
|
|
1352
|
+
[8287, 8287],
|
|
1353
|
+
[12288, 12288],
|
|
1354
|
+
[65279, 65279]
|
|
1355
|
+
];
|
|
1342
1356
|
function shorthandRanges(ch) {
|
|
1343
|
-
|
|
1357
|
+
if (ch === "d") return [[48, 57]];
|
|
1358
|
+
if (ch === "s") return SPACE_RANGES;
|
|
1359
|
+
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1360
|
+
}
|
|
1361
|
+
function readUnicodeEscape(body, i) {
|
|
1362
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
1363
|
+
const hex = body.slice(i + 2, i + 6);
|
|
1364
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
1365
|
+
return { cp: Number.parseInt(hex, 16), next: i + 6 };
|
|
1344
1366
|
}
|
|
1345
1367
|
function parseClassRanges(body) {
|
|
1346
1368
|
const ranges = [];
|
|
@@ -1349,11 +1371,16 @@ function parseClassRanges(body) {
|
|
|
1349
1371
|
const ch = body[i];
|
|
1350
1372
|
if (ch === void 0) return null;
|
|
1351
1373
|
if (ch === "\\") {
|
|
1374
|
+
const uni = readUnicodeEscape(body, i);
|
|
1375
|
+
if (uni) {
|
|
1376
|
+
i = uni.next;
|
|
1377
|
+
return { cp: uni.cp };
|
|
1378
|
+
}
|
|
1352
1379
|
const e = body[i + 1];
|
|
1353
1380
|
if (e === void 0) return null;
|
|
1354
1381
|
i += 2;
|
|
1355
1382
|
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1356
|
-
if (e === "d" || e === "w") return { set: shorthandRanges(e) };
|
|
1383
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
1357
1384
|
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1358
1385
|
return { cp: e.codePointAt(0) };
|
|
1359
1386
|
}
|
|
@@ -1381,10 +1408,16 @@ function parseClassRanges(body) {
|
|
|
1381
1408
|
function classToRanges(cls) {
|
|
1382
1409
|
if (cls === "\\d") return shorthandRanges("d");
|
|
1383
1410
|
if (cls === "\\w") return shorthandRanges("w");
|
|
1411
|
+
if (cls === "\\s") return shorthandRanges("s");
|
|
1384
1412
|
const body = cls.slice(1, -1);
|
|
1385
1413
|
if (body.startsWith("^")) return null;
|
|
1386
1414
|
return parseClassRanges(body);
|
|
1387
1415
|
}
|
|
1416
|
+
var foldEq = (cVar, cp) => {
|
|
1417
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
|
|
1418
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
|
|
1419
|
+
return `${cVar} === ${cp}`;
|
|
1420
|
+
};
|
|
1388
1421
|
function literalCodePoints(frag) {
|
|
1389
1422
|
const out = [];
|
|
1390
1423
|
let i = 0;
|
|
@@ -1431,22 +1464,612 @@ function parseStringShape(source) {
|
|
|
1431
1464
|
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1432
1465
|
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1433
1466
|
}
|
|
1467
|
+
function parseSeqParts(source) {
|
|
1468
|
+
const parts = [];
|
|
1469
|
+
let lit = [];
|
|
1470
|
+
const flush = () => {
|
|
1471
|
+
if (lit.length) {
|
|
1472
|
+
parts.push({ part: "lit", cps: lit, optional: false });
|
|
1473
|
+
lit = [];
|
|
1474
|
+
}
|
|
1475
|
+
};
|
|
1476
|
+
const runFrom = (ranges, negated, q) => {
|
|
1477
|
+
if (q === "+") {
|
|
1478
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
|
|
1479
|
+
return 1;
|
|
1480
|
+
}
|
|
1481
|
+
if (q === "*") {
|
|
1482
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
|
|
1483
|
+
return 1;
|
|
1484
|
+
}
|
|
1485
|
+
if (q === "?") {
|
|
1486
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
|
|
1487
|
+
return 1;
|
|
1488
|
+
}
|
|
1489
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
|
|
1490
|
+
return 0;
|
|
1491
|
+
};
|
|
1492
|
+
let i = 0;
|
|
1493
|
+
while (i < source.length) {
|
|
1494
|
+
const ch = source[i];
|
|
1495
|
+
if (ch === "[") {
|
|
1496
|
+
let k = i + 1;
|
|
1497
|
+
const neg = source[k] === "^";
|
|
1498
|
+
if (neg) k++;
|
|
1499
|
+
let body = "";
|
|
1500
|
+
while (k < source.length && source[k] !== "]") {
|
|
1501
|
+
if (source[k] === "\\") {
|
|
1502
|
+
body += source[k] + (source[k + 1] ?? "");
|
|
1503
|
+
k += 2;
|
|
1504
|
+
} else {
|
|
1505
|
+
body += source[k];
|
|
1506
|
+
k++;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
if (source[k] !== "]") return null;
|
|
1510
|
+
const ranges = parseClassRanges(body);
|
|
1511
|
+
if (!ranges) return null;
|
|
1512
|
+
flush();
|
|
1513
|
+
i = k + 1;
|
|
1514
|
+
i += runFrom(ranges, neg, source[i]);
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
if (ch === "\\") {
|
|
1518
|
+
const e = source[i + 1];
|
|
1519
|
+
if (e === "d" || e === "w" || e === "s") {
|
|
1520
|
+
flush();
|
|
1521
|
+
i += 2;
|
|
1522
|
+
i += runFrom(shorthandRanges(e), false, source[i]);
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
const uni = readUnicodeEscape(source, i);
|
|
1526
|
+
let cp2;
|
|
1527
|
+
if (uni) {
|
|
1528
|
+
cp2 = uni.cp;
|
|
1529
|
+
i = uni.next;
|
|
1530
|
+
} else if (e === void 0) return null;
|
|
1531
|
+
else if (e in CLASS_ESCAPES) {
|
|
1532
|
+
cp2 = CLASS_ESCAPES[e];
|
|
1533
|
+
i += 2;
|
|
1534
|
+
} else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1535
|
+
else {
|
|
1536
|
+
cp2 = e.codePointAt(0);
|
|
1537
|
+
i += 2;
|
|
1538
|
+
}
|
|
1539
|
+
if (source[i] === "?") {
|
|
1540
|
+
flush();
|
|
1541
|
+
parts.push({ part: "lit", cps: [cp2], optional: true });
|
|
1542
|
+
i++;
|
|
1543
|
+
} else lit.push(cp2);
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
if (ch === "(") {
|
|
1547
|
+
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
1548
|
+
let k = i + 3;
|
|
1549
|
+
let depth = 1;
|
|
1550
|
+
while (k < source.length && depth > 0) {
|
|
1551
|
+
const c2 = source[k];
|
|
1552
|
+
if (c2 === "\\") {
|
|
1553
|
+
k += 2;
|
|
1554
|
+
continue;
|
|
1555
|
+
}
|
|
1556
|
+
if (c2 === "[") {
|
|
1557
|
+
let j = k + 1;
|
|
1558
|
+
if (source[j] === "^") j++;
|
|
1559
|
+
while (j < source.length && source[j] !== "]") {
|
|
1560
|
+
if (source[j] === "\\") j += 2;
|
|
1561
|
+
else j++;
|
|
1562
|
+
}
|
|
1563
|
+
if (source[j] !== "]") return null;
|
|
1564
|
+
k = j + 1;
|
|
1565
|
+
continue;
|
|
1566
|
+
}
|
|
1567
|
+
if (c2 === "(") depth++;
|
|
1568
|
+
else if (c2 === ")") depth--;
|
|
1569
|
+
k++;
|
|
1570
|
+
}
|
|
1571
|
+
if (depth !== 0) return null;
|
|
1572
|
+
const body = source.slice(i + 3, k - 1);
|
|
1573
|
+
const inner = parseScanShape(body);
|
|
1574
|
+
if (!inner || !groupInnerSafe(inner)) return null;
|
|
1575
|
+
const q = source[k];
|
|
1576
|
+
if (q === "{") return null;
|
|
1577
|
+
flush();
|
|
1578
|
+
if (q === "+") {
|
|
1579
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1580
|
+
parts.push({ part: "group", inner, min: 1, unbounded: true });
|
|
1581
|
+
i = k + 1;
|
|
1582
|
+
} else if (q === "*") {
|
|
1583
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1584
|
+
parts.push({ part: "group", inner, min: 0, unbounded: true });
|
|
1585
|
+
i = k + 1;
|
|
1586
|
+
} else if (q === "?") {
|
|
1587
|
+
parts.push({ part: "group", inner, min: 0, unbounded: false });
|
|
1588
|
+
i = k + 1;
|
|
1589
|
+
} else {
|
|
1590
|
+
parts.push({ part: "group", inner, min: 1, unbounded: false });
|
|
1591
|
+
i = k;
|
|
1592
|
+
}
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
if (META.has(ch)) return null;
|
|
1596
|
+
const cp = ch.codePointAt(0);
|
|
1597
|
+
i += ch.length;
|
|
1598
|
+
if (source[i] === "?") {
|
|
1599
|
+
flush();
|
|
1600
|
+
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
1601
|
+
i++;
|
|
1602
|
+
} else lit.push(cp);
|
|
1603
|
+
}
|
|
1604
|
+
flush();
|
|
1605
|
+
return parts.length ? parts : null;
|
|
1606
|
+
}
|
|
1607
|
+
function seqIsUnambiguous(parts) {
|
|
1608
|
+
let hasMandatory = false;
|
|
1609
|
+
for (let k = 0; k < parts.length; k++) {
|
|
1610
|
+
const p = parts[k];
|
|
1611
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1612
|
+
if (mandatory) hasMandatory = true;
|
|
1613
|
+
if (p.part === "group") {
|
|
1614
|
+
const exposure = groupPartExposure(p);
|
|
1615
|
+
if (exposure === "unsupported") {
|
|
1616
|
+
const trailingOnce = k === parts.length - 1 && p.min === 1 && !p.unbounded;
|
|
1617
|
+
if (!trailingOnce) return false;
|
|
1618
|
+
} else if (exposure !== null) {
|
|
1619
|
+
if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1623
|
+
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1624
|
+
if (skippableSingle || isUnboundedRepeat) {
|
|
1625
|
+
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
1626
|
+
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
return hasMandatory;
|
|
1630
|
+
}
|
|
1631
|
+
function rangesOverlap(a, b) {
|
|
1632
|
+
return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
|
|
1633
|
+
}
|
|
1634
|
+
function rangesSubset(sub, sup) {
|
|
1635
|
+
return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
|
|
1636
|
+
}
|
|
1637
|
+
function parseClassOperand(body) {
|
|
1638
|
+
if (body === "\\d" || body === "\\w" || body === "\\s") {
|
|
1639
|
+
return { ranges: shorthandRanges(body[1]), negated: false };
|
|
1640
|
+
}
|
|
1641
|
+
if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
|
|
1642
|
+
let inner = body.slice(1, -1);
|
|
1643
|
+
const negated = inner.startsWith("^");
|
|
1644
|
+
if (negated) inner = inner.slice(1);
|
|
1645
|
+
const ranges = parseClassRanges(inner);
|
|
1646
|
+
return ranges ? { ranges, negated } : null;
|
|
1647
|
+
}
|
|
1648
|
+
const cps = literalCodePoints(body);
|
|
1649
|
+
if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
|
|
1650
|
+
return null;
|
|
1651
|
+
}
|
|
1652
|
+
function classSubset(t, u) {
|
|
1653
|
+
if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1654
|
+
if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1655
|
+
if (t.negated && !u.negated) return false;
|
|
1656
|
+
return rangesSubset(u.ranges, t.ranges);
|
|
1657
|
+
}
|
|
1658
|
+
function classDisjoint(t, u) {
|
|
1659
|
+
if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1660
|
+
if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1661
|
+
if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
|
|
1662
|
+
return false;
|
|
1663
|
+
}
|
|
1664
|
+
function firstSetsDisjoint(a, b) {
|
|
1665
|
+
if (a === "any" || b === "any") return false;
|
|
1666
|
+
return classDisjoint(a, b);
|
|
1667
|
+
}
|
|
1668
|
+
function fixedClassSeq(shape) {
|
|
1669
|
+
if (shape.kind !== "seq") return null;
|
|
1670
|
+
const out = [];
|
|
1671
|
+
for (const p of shape.parts) {
|
|
1672
|
+
if (p.part === "lit") {
|
|
1673
|
+
if (p.optional) return null;
|
|
1674
|
+
for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
|
|
1675
|
+
} else if (p.part === "run" && !p.unbounded && p.min === 1) {
|
|
1676
|
+
out.push({ ranges: p.ranges, negated: p.negated });
|
|
1677
|
+
} else {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
return out.length ? out : null;
|
|
1682
|
+
}
|
|
1683
|
+
function altFixedMutuallyExclusive(shape) {
|
|
1684
|
+
if (shape.kind !== "alt" || shape.disjoint) return false;
|
|
1685
|
+
const lists = [];
|
|
1686
|
+
for (const arm of shape.arms) {
|
|
1687
|
+
const f = fixedClassSeq(arm);
|
|
1688
|
+
if (!f) return false;
|
|
1689
|
+
lists.push(f);
|
|
1690
|
+
}
|
|
1691
|
+
for (let i = 0; i < lists.length; i++) {
|
|
1692
|
+
for (let j = 0; j < lists.length; j++) {
|
|
1693
|
+
if (i === j) continue;
|
|
1694
|
+
const a = lists[i];
|
|
1695
|
+
const b = lists[j];
|
|
1696
|
+
if (a.length > b.length) continue;
|
|
1697
|
+
let disjointAt = false;
|
|
1698
|
+
for (let p = 0; p < a.length; p++) {
|
|
1699
|
+
if (classDisjoint(a[p], b[p])) {
|
|
1700
|
+
disjointAt = true;
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (!disjointAt) return false;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
return true;
|
|
1708
|
+
}
|
|
1709
|
+
function groupPartExposure(part) {
|
|
1710
|
+
const innerWiggle = trailingBacktrackClass(part.inner);
|
|
1711
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1712
|
+
const requiredOnce = !part.unbounded && part.min === 1;
|
|
1713
|
+
if (requiredOnce) return innerWiggle;
|
|
1714
|
+
const dropExposed = shapeFirstAccept(part.inner);
|
|
1715
|
+
if (dropExposed === "any") return "unsupported";
|
|
1716
|
+
if (innerWiggle === null) return dropExposed;
|
|
1717
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1718
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1719
|
+
}
|
|
1720
|
+
function trailingBacktrackClass(shape) {
|
|
1721
|
+
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1722
|
+
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
1723
|
+
if (shape.kind === "litFold") return null;
|
|
1724
|
+
if (shape.kind === "seq") {
|
|
1725
|
+
const last = shape.parts[shape.parts.length - 1];
|
|
1726
|
+
if (last.part === "lit") return null;
|
|
1727
|
+
if (last.part === "run") {
|
|
1728
|
+
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
return groupPartExposure(last);
|
|
1732
|
+
}
|
|
1733
|
+
if (shape.kind === "alt") {
|
|
1734
|
+
if (!shape.disjoint) return altFixedMutuallyExclusive(shape) ? null : "unsupported";
|
|
1735
|
+
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1736
|
+
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1737
|
+
const concrete = classes.filter((c) => c !== null);
|
|
1738
|
+
if (concrete.length === 0) return null;
|
|
1739
|
+
const unioned = unionCharSets(concrete);
|
|
1740
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1741
|
+
}
|
|
1742
|
+
return "unsupported";
|
|
1743
|
+
}
|
|
1744
|
+
function lookaheadUnambiguous(inner, operand, negative) {
|
|
1745
|
+
const tail = trailingBacktrackClass(inner);
|
|
1746
|
+
if (tail === "unsupported") return false;
|
|
1747
|
+
if (tail === null) return true;
|
|
1748
|
+
return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
|
|
1749
|
+
}
|
|
1750
|
+
function stripTrailingLookahead(source) {
|
|
1751
|
+
if (!source.endsWith(")")) return null;
|
|
1752
|
+
for (const negative of [true, false]) {
|
|
1753
|
+
const lead = negative ? "(?!" : "(?=";
|
|
1754
|
+
const idx = source.lastIndexOf(lead);
|
|
1755
|
+
if (idx === -1) continue;
|
|
1756
|
+
const body = source.slice(idx + lead.length, -1);
|
|
1757
|
+
const operand = body.length ? parseClassOperand(body) : null;
|
|
1758
|
+
if (!operand) continue;
|
|
1759
|
+
return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
|
|
1760
|
+
}
|
|
1761
|
+
return null;
|
|
1762
|
+
}
|
|
1763
|
+
function unwrapOuterGroup(source) {
|
|
1764
|
+
while (source.startsWith("(?:")) {
|
|
1765
|
+
let depth = 1;
|
|
1766
|
+
let i = 3;
|
|
1767
|
+
let ok = true;
|
|
1768
|
+
while (i < source.length && depth > 0) {
|
|
1769
|
+
const ch = source[i];
|
|
1770
|
+
if (ch === "\\") {
|
|
1771
|
+
i += 2;
|
|
1772
|
+
continue;
|
|
1773
|
+
}
|
|
1774
|
+
if (ch === "[") {
|
|
1775
|
+
let j = i + 1;
|
|
1776
|
+
if (source[j] === "^") j++;
|
|
1777
|
+
while (j < source.length && source[j] !== "]") {
|
|
1778
|
+
if (source[j] === "\\") j += 2;
|
|
1779
|
+
else j++;
|
|
1780
|
+
}
|
|
1781
|
+
if (source[j] !== "]") {
|
|
1782
|
+
ok = false;
|
|
1783
|
+
break;
|
|
1784
|
+
}
|
|
1785
|
+
i = j + 1;
|
|
1786
|
+
continue;
|
|
1787
|
+
}
|
|
1788
|
+
if (ch === "(") depth++;
|
|
1789
|
+
else if (ch === ")") depth--;
|
|
1790
|
+
i++;
|
|
1791
|
+
}
|
|
1792
|
+
if (!ok || depth !== 0 || i !== source.length) break;
|
|
1793
|
+
source = source.slice(3, -1);
|
|
1794
|
+
}
|
|
1795
|
+
return source;
|
|
1796
|
+
}
|
|
1797
|
+
function splitTopLevelAlternation(source) {
|
|
1798
|
+
const body = unwrapOuterGroup(source);
|
|
1799
|
+
const parts = [];
|
|
1800
|
+
let depth = 0;
|
|
1801
|
+
let last = 0;
|
|
1802
|
+
let i = 0;
|
|
1803
|
+
while (i < body.length) {
|
|
1804
|
+
const ch = body[i];
|
|
1805
|
+
if (ch === "\\") {
|
|
1806
|
+
i += 2;
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
if (ch === "[") {
|
|
1810
|
+
let j = i + 1;
|
|
1811
|
+
if (body[j] === "^") j++;
|
|
1812
|
+
while (j < body.length && body[j] !== "]") {
|
|
1813
|
+
if (body[j] === "\\") j += 2;
|
|
1814
|
+
else j++;
|
|
1815
|
+
}
|
|
1816
|
+
if (body[j] !== "]") return null;
|
|
1817
|
+
i = j + 1;
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
if (ch === "(") {
|
|
1821
|
+
depth++;
|
|
1822
|
+
i++;
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
if (ch === ")") {
|
|
1826
|
+
depth--;
|
|
1827
|
+
if (depth < 0) return null;
|
|
1828
|
+
i++;
|
|
1829
|
+
continue;
|
|
1830
|
+
}
|
|
1831
|
+
if (ch === "|" && depth === 0) {
|
|
1832
|
+
parts.push(body.slice(last, i));
|
|
1833
|
+
last = i + 1;
|
|
1834
|
+
i++;
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
i++;
|
|
1838
|
+
}
|
|
1839
|
+
if (depth !== 0) return null;
|
|
1840
|
+
parts.push(body.slice(last));
|
|
1841
|
+
if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
|
|
1842
|
+
return parts;
|
|
1843
|
+
}
|
|
1844
|
+
function shapeCanBeEmpty(shape) {
|
|
1845
|
+
if (shape.kind === "chars") return !shape.minOne;
|
|
1846
|
+
if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
|
|
1847
|
+
if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
|
|
1848
|
+
return false;
|
|
1849
|
+
}
|
|
1850
|
+
function unionCharSets(sets) {
|
|
1851
|
+
if (sets.some((s) => s === "any")) return "any";
|
|
1852
|
+
const concrete = sets;
|
|
1853
|
+
if (concrete.length === 1) return concrete[0];
|
|
1854
|
+
if (concrete.some((s) => s.negated)) return "any";
|
|
1855
|
+
return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
|
|
1856
|
+
}
|
|
1857
|
+
function partFirstAccept(part) {
|
|
1858
|
+
if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
|
|
1859
|
+
if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
|
|
1860
|
+
return shapeFirstAccept(part.inner);
|
|
1861
|
+
}
|
|
1862
|
+
function seqFirstAccept(parts) {
|
|
1863
|
+
const sets = [];
|
|
1864
|
+
for (const p of parts) {
|
|
1865
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1866
|
+
sets.push(partFirstAccept(p));
|
|
1867
|
+
if (mandatory) break;
|
|
1868
|
+
}
|
|
1869
|
+
return unionCharSets(sets);
|
|
1870
|
+
}
|
|
1871
|
+
function groupInnerSafe(shape) {
|
|
1872
|
+
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1873
|
+
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1874
|
+
if (shape.kind === "alt") return true;
|
|
1875
|
+
return false;
|
|
1876
|
+
}
|
|
1877
|
+
function shapeFirstAccept(shape) {
|
|
1878
|
+
if (shapeCanBeEmpty(shape)) return "any";
|
|
1879
|
+
switch (shape.kind) {
|
|
1880
|
+
case "chars":
|
|
1881
|
+
return { ranges: shape.ranges, negated: false };
|
|
1882
|
+
case "ident":
|
|
1883
|
+
return { ranges: shape.head, negated: false };
|
|
1884
|
+
case "until":
|
|
1885
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1886
|
+
case "delimited":
|
|
1887
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1888
|
+
case "string":
|
|
1889
|
+
return { ranges: [[shape.quote, shape.quote]], negated: false };
|
|
1890
|
+
case "litFold": {
|
|
1891
|
+
const cp = shape.open[0];
|
|
1892
|
+
if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
|
|
1893
|
+
if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
|
|
1894
|
+
return { ranges: [[cp, cp]], negated: false };
|
|
1895
|
+
}
|
|
1896
|
+
case "seq":
|
|
1897
|
+
return seqFirstAccept(shape.parts);
|
|
1898
|
+
case "lookahead":
|
|
1899
|
+
return shapeFirstAccept(shape.inner);
|
|
1900
|
+
// zero-width — doesn't change first-char acceptance
|
|
1901
|
+
case "alt": {
|
|
1902
|
+
const sets = shape.firsts.map((f) => f === null ? "any" : f);
|
|
1903
|
+
return unionCharSets(sets);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
function allPairsDisjoint(sets) {
|
|
1908
|
+
for (let i = 0; i < sets.length; i++) {
|
|
1909
|
+
const a = sets[i];
|
|
1910
|
+
if (a === "any") return false;
|
|
1911
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
1912
|
+
const b = sets[j];
|
|
1913
|
+
if (b === "any" || !classDisjoint(a, b)) return false;
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return true;
|
|
1917
|
+
}
|
|
1918
|
+
function parseAlternation(source) {
|
|
1919
|
+
const parts = splitTopLevelAlternation(source);
|
|
1920
|
+
if (!parts) return null;
|
|
1921
|
+
const arms = [];
|
|
1922
|
+
for (const p of parts) {
|
|
1923
|
+
const arm = parseScanShape(p);
|
|
1924
|
+
if (!arm) return null;
|
|
1925
|
+
arms.push(arm);
|
|
1926
|
+
}
|
|
1927
|
+
const firstSets = arms.map(shapeFirstAccept);
|
|
1928
|
+
const disjoint = allPairsDisjoint(firstSets);
|
|
1929
|
+
return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
|
|
1930
|
+
}
|
|
1434
1931
|
function parseScanShape(source) {
|
|
1932
|
+
const alt = parseAlternation(source);
|
|
1933
|
+
if (alt) return alt;
|
|
1934
|
+
const la = stripTrailingLookahead(source);
|
|
1935
|
+
if (la) {
|
|
1936
|
+
const inner = parseScanShapeCore(la.base);
|
|
1937
|
+
if (!inner) return null;
|
|
1938
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
1939
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
1940
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
1941
|
+
}
|
|
1942
|
+
return parseScanShapeCore(source);
|
|
1943
|
+
}
|
|
1944
|
+
function splitDelimArms(body) {
|
|
1945
|
+
const arms = [];
|
|
1946
|
+
let depth = 0;
|
|
1947
|
+
let last = 0;
|
|
1948
|
+
let i = 0;
|
|
1949
|
+
while (i < body.length) {
|
|
1950
|
+
const ch = body[i];
|
|
1951
|
+
if (ch === "\\") {
|
|
1952
|
+
i += 2;
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
if (ch === "[") {
|
|
1956
|
+
let j = i + 1;
|
|
1957
|
+
if (body[j] === "^") j++;
|
|
1958
|
+
while (j < body.length && body[j] !== "]") {
|
|
1959
|
+
if (body[j] === "\\") j += 2;
|
|
1960
|
+
else j++;
|
|
1961
|
+
}
|
|
1962
|
+
if (body[j] !== "]") return null;
|
|
1963
|
+
i = j + 1;
|
|
1964
|
+
continue;
|
|
1965
|
+
}
|
|
1966
|
+
if (ch === "(") {
|
|
1967
|
+
depth++;
|
|
1968
|
+
i++;
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
if (ch === ")") {
|
|
1972
|
+
depth--;
|
|
1973
|
+
if (depth < 0) return null;
|
|
1974
|
+
i++;
|
|
1975
|
+
continue;
|
|
1976
|
+
}
|
|
1977
|
+
if (ch === "|" && depth === 0) {
|
|
1978
|
+
arms.push(body.slice(last, i));
|
|
1979
|
+
last = i + 1;
|
|
1980
|
+
}
|
|
1981
|
+
i++;
|
|
1982
|
+
}
|
|
1983
|
+
if (depth !== 0) return null;
|
|
1984
|
+
arms.push(body.slice(last));
|
|
1985
|
+
return arms;
|
|
1986
|
+
}
|
|
1987
|
+
function delimitedBodySound(body, close) {
|
|
1988
|
+
const l0 = close[0];
|
|
1989
|
+
if (close.length > 2) return false;
|
|
1990
|
+
const arms = splitDelimArms(body);
|
|
1991
|
+
if (!arms) return false;
|
|
1992
|
+
let bulkSeen = false;
|
|
1993
|
+
let guardSeen = false;
|
|
1994
|
+
for (const arm of arms) {
|
|
1995
|
+
const bulk = /^\[\^((?:\\.|[^\]])+)\]$/.exec(arm);
|
|
1996
|
+
if (bulk) {
|
|
1997
|
+
if (bulkSeen) return false;
|
|
1998
|
+
const ranges = parseClassRanges(bulk[1]);
|
|
1999
|
+
if (!ranges || ranges.length !== 1 || ranges[0][0] !== l0 || ranges[0][1] !== l0) return false;
|
|
2000
|
+
bulkSeen = true;
|
|
2001
|
+
continue;
|
|
2002
|
+
}
|
|
2003
|
+
if (close.length === 2 && arm.endsWith(")")) {
|
|
2004
|
+
const idx = arm.indexOf("(?!");
|
|
2005
|
+
if (idx === -1) return false;
|
|
2006
|
+
const prefix = literalCodePoints(arm.slice(0, idx));
|
|
2007
|
+
const op = parseClassOperand(arm.slice(idx + 3, -1));
|
|
2008
|
+
const prefixIsL0 = prefix?.length === 1 && prefix[0] === l0;
|
|
2009
|
+
const forbidsL1 = op && !op.negated && op.ranges.length === 1 && op.ranges[0][0] === close[1] && op.ranges[0][1] === close[1];
|
|
2010
|
+
if (!prefixIsL0 || !forbidsL1 || guardSeen) return false;
|
|
2011
|
+
guardSeen = true;
|
|
2012
|
+
continue;
|
|
2013
|
+
}
|
|
2014
|
+
return false;
|
|
2015
|
+
}
|
|
2016
|
+
return bulkSeen && guardSeen === (close.length === 2);
|
|
2017
|
+
}
|
|
2018
|
+
function parseDelimited(source) {
|
|
2019
|
+
if (source.includes("\\\\")) return null;
|
|
2020
|
+
let i = 0;
|
|
2021
|
+
while (i < source.length && source[i] !== "(") {
|
|
2022
|
+
i += source[i] === "\\" ? 2 : 1;
|
|
2023
|
+
}
|
|
2024
|
+
if (source[i] !== "(" || source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
2025
|
+
const openFrag = source.slice(0, i);
|
|
2026
|
+
let k = i + 3;
|
|
2027
|
+
let depth = 1;
|
|
2028
|
+
while (k < source.length && depth > 0) {
|
|
2029
|
+
const ch = source[k];
|
|
2030
|
+
if (ch === "\\") {
|
|
2031
|
+
k += 2;
|
|
2032
|
+
continue;
|
|
2033
|
+
}
|
|
2034
|
+
if (ch === "[") {
|
|
2035
|
+
let j = k + 1;
|
|
2036
|
+
if (source[j] === "^") j++;
|
|
2037
|
+
while (j < source.length && source[j] !== "]") {
|
|
2038
|
+
if (source[j] === "\\") j += 2;
|
|
2039
|
+
else j++;
|
|
2040
|
+
}
|
|
2041
|
+
if (source[j] !== "]") return null;
|
|
2042
|
+
k = j + 1;
|
|
2043
|
+
continue;
|
|
2044
|
+
}
|
|
2045
|
+
if (ch === "(") depth++;
|
|
2046
|
+
else if (ch === ")") depth--;
|
|
2047
|
+
k++;
|
|
2048
|
+
}
|
|
2049
|
+
if (depth !== 0) return null;
|
|
2050
|
+
const body = source.slice(i + 3, k - 1);
|
|
2051
|
+
if (source[k] !== "*") return null;
|
|
2052
|
+
const closeFrag = source.slice(k + 1);
|
|
2053
|
+
const open = literalCodePoints(openFrag);
|
|
2054
|
+
const close = literalCodePoints(closeFrag);
|
|
2055
|
+
if (!open || !close || !delimitedBodySound(body, close)) return null;
|
|
2056
|
+
return { kind: "delimited", open, close };
|
|
2057
|
+
}
|
|
2058
|
+
function parseScanShapeCore(source) {
|
|
1435
2059
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1436
|
-
if (m) {
|
|
1437
|
-
if (m[1].startsWith("^")) return null;
|
|
2060
|
+
if (m && !m[1].startsWith("^")) {
|
|
1438
2061
|
const ranges = parseClassRanges(m[1]);
|
|
1439
2062
|
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1440
2063
|
}
|
|
1441
|
-
m = /^\\([
|
|
2064
|
+
m = /^\\([dws])([+*])$/.exec(source);
|
|
1442
2065
|
if (m) {
|
|
1443
2066
|
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1444
2067
|
}
|
|
1445
|
-
m = /^(\[(?:\\.|[^\]])+\]|\\[
|
|
2068
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
|
|
1446
2069
|
if (m) {
|
|
1447
2070
|
const head = classToRanges(m[1]);
|
|
1448
2071
|
const tail = classToRanges(m[2]);
|
|
1449
|
-
|
|
2072
|
+
if (head && tail) return { kind: "ident", head, tail };
|
|
1450
2073
|
}
|
|
1451
2074
|
const str = parseStringShape(source);
|
|
1452
2075
|
if (str) return str;
|
|
@@ -1455,20 +2078,19 @@ function parseScanShape(source) {
|
|
|
1455
2078
|
const open = literalCodePoints(m[1]);
|
|
1456
2079
|
const stop = parseClassRanges(m[2]);
|
|
1457
2080
|
if (open && stop) return { kind: "until", open, stop };
|
|
1458
|
-
return null;
|
|
1459
|
-
}
|
|
1460
|
-
if (!source.includes("\\\\")) {
|
|
1461
|
-
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
1462
|
-
if (m && m[1] && m[2]) {
|
|
1463
|
-
const open = literalCodePoints(m[1]);
|
|
1464
|
-
const close = literalCodePoints(m[2]);
|
|
1465
|
-
if (open && close) return { kind: "delimited", open, close };
|
|
1466
|
-
}
|
|
1467
2081
|
}
|
|
2082
|
+
const delim = parseDelimited(source);
|
|
2083
|
+
if (delim) return delim;
|
|
2084
|
+
const parts = parseSeqParts(source);
|
|
2085
|
+
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1468
2086
|
return null;
|
|
1469
2087
|
}
|
|
1470
2088
|
function scanShapeFromRegex(source, flags) {
|
|
1471
|
-
if (/[
|
|
2089
|
+
if (/[msu]/.test(flags)) return null;
|
|
2090
|
+
if (/i/.test(flags)) {
|
|
2091
|
+
const open = literalCodePoints(source);
|
|
2092
|
+
return open ? { kind: "litFold", open } : null;
|
|
2093
|
+
}
|
|
1472
2094
|
return parseScanShape(source);
|
|
1473
2095
|
}
|
|
1474
2096
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
@@ -1483,6 +2105,53 @@ function stringHardStop(shape) {
|
|
|
1483
2105
|
}
|
|
1484
2106
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1485
2107
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2108
|
+
if (shape.kind === "lookahead") {
|
|
2109
|
+
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
2110
|
+
const okV = mint("_ok");
|
|
2111
|
+
const endV2 = mint("_end");
|
|
2112
|
+
const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
|
|
2113
|
+
const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
|
|
2114
|
+
const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
|
|
2115
|
+
const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
|
|
2116
|
+
return {
|
|
2117
|
+
setup: [
|
|
2118
|
+
...inner.setup,
|
|
2119
|
+
`${ind2}let ${okV} = ${inner.ok}`,
|
|
2120
|
+
`${ind2}let ${endV2} = ${inner.end}`,
|
|
2121
|
+
`${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
|
|
2122
|
+
],
|
|
2123
|
+
ok: okV,
|
|
2124
|
+
end: endV2
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
if (shape.kind === "alt") {
|
|
2128
|
+
const okV = mint("_ok");
|
|
2129
|
+
const endV2 = mint("_end");
|
|
2130
|
+
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2131
|
+
if (shape.disjoint) {
|
|
2132
|
+
const c0 = codeAt(start, firstChar);
|
|
2133
|
+
shape.arms.forEach((arm, k) => {
|
|
2134
|
+
const fs2 = shape.firsts[k];
|
|
2135
|
+
const cond = fs2.negated ? `!(${classCond(c0, fs2.ranges)})` : `(${classCond(c0, fs2.ranges)})`;
|
|
2136
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2137
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2138
|
+
lines.push(...m.setup);
|
|
2139
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2140
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2141
|
+
lines.push(`${ind2}}`);
|
|
2142
|
+
});
|
|
2143
|
+
} else {
|
|
2144
|
+
const lbl = mint("_alt");
|
|
2145
|
+
lines.push(`${ind2}${lbl}: {`);
|
|
2146
|
+
for (const arm of shape.arms) {
|
|
2147
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2148
|
+
lines.push(...m.setup);
|
|
2149
|
+
lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
2150
|
+
}
|
|
2151
|
+
lines.push(`${ind2}}`);
|
|
2152
|
+
}
|
|
2153
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2154
|
+
}
|
|
1486
2155
|
if (shape.kind === "chars") {
|
|
1487
2156
|
const cur = mint("_e");
|
|
1488
2157
|
return {
|
|
@@ -1509,6 +2178,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
1509
2178
|
end: cur
|
|
1510
2179
|
};
|
|
1511
2180
|
}
|
|
2181
|
+
if (shape.kind === "seq") {
|
|
2182
|
+
const endV2 = mint("_e");
|
|
2183
|
+
const okV = mint("_ok");
|
|
2184
|
+
const runCond = (p, at) => {
|
|
2185
|
+
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
2186
|
+
return p.negated ? `!(${c})` : `(${c})`;
|
|
2187
|
+
};
|
|
2188
|
+
const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
|
|
2189
|
+
let first = true;
|
|
2190
|
+
for (const p of shape.parts) {
|
|
2191
|
+
if (p.part === "lit") {
|
|
2192
|
+
const L = p.cps.length;
|
|
2193
|
+
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
2194
|
+
if (p.optional) {
|
|
2195
|
+
lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
2196
|
+
} else {
|
|
2197
|
+
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
2198
|
+
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
2199
|
+
}
|
|
2200
|
+
} else if (p.part === "run" && !p.unbounded) {
|
|
2201
|
+
if (p.min === 1) {
|
|
2202
|
+
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
2203
|
+
lines.push(`${ind2} ${endV2}++`);
|
|
2204
|
+
} else {
|
|
2205
|
+
lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2206
|
+
}
|
|
2207
|
+
} else if (p.part === "run") {
|
|
2208
|
+
const s = mint("_s");
|
|
2209
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
2210
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2211
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
2212
|
+
} else {
|
|
2213
|
+
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
2214
|
+
if (!p.unbounded) {
|
|
2215
|
+
lines.push(...inner.setup);
|
|
2216
|
+
if (p.min === 1) {
|
|
2217
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2218
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2219
|
+
} else {
|
|
2220
|
+
lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
2221
|
+
}
|
|
2222
|
+
} else {
|
|
2223
|
+
const progV = mint("_prog");
|
|
2224
|
+
lines.push(`${ind2} let ${progV} = false`);
|
|
2225
|
+
lines.push(`${ind2} while (true) {`);
|
|
2226
|
+
lines.push(...inner.setup);
|
|
2227
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2228
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2229
|
+
lines.push(`${ind2} ${progV} = true`);
|
|
2230
|
+
lines.push(`${ind2} }`);
|
|
2231
|
+
if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
first = false;
|
|
2235
|
+
}
|
|
2236
|
+
lines.push(`${ind2} ${okV} = true`);
|
|
2237
|
+
lines.push(`${ind2}} while (false)`);
|
|
2238
|
+
lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
|
|
2239
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2240
|
+
}
|
|
2241
|
+
if (shape.kind === "litFold") {
|
|
2242
|
+
const openLen2 = shape.open.length;
|
|
2243
|
+
const endV2 = mint("_end");
|
|
2244
|
+
const checks = shape.open.map((cp, k) => {
|
|
2245
|
+
const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
|
|
2246
|
+
return foldEq(cVar, cp);
|
|
2247
|
+
});
|
|
2248
|
+
return {
|
|
2249
|
+
setup: [
|
|
2250
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2251
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
|
|
2252
|
+
],
|
|
2253
|
+
ok: `${endV2} > ${start}`,
|
|
2254
|
+
end: endV2
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
1512
2257
|
if (shape.kind === "until") {
|
|
1513
2258
|
const j2 = mint("_j");
|
|
1514
2259
|
const openLen2 = shape.open.length;
|
|
@@ -1790,6 +2535,7 @@ function looseMkBuildRe(type) {
|
|
|
1790
2535
|
);
|
|
1791
2536
|
}
|
|
1792
2537
|
function analyzeMkInlineBuild(def) {
|
|
2538
|
+
if (!def.build) return null;
|
|
1793
2539
|
const src = (def.buildSrc ?? def.build.toString()).trim();
|
|
1794
2540
|
const strict = src.match(MK_BUILD_RE);
|
|
1795
2541
|
if (strict) {
|
|
@@ -1887,6 +2633,15 @@ function v(ctx, prefix = "_v") {
|
|
|
1887
2633
|
function ind(ctx) {
|
|
1888
2634
|
return " ".repeat(ctx.indent);
|
|
1889
2635
|
}
|
|
2636
|
+
function nsp(ctx) {
|
|
2637
|
+
return ctx.ns ?? "";
|
|
2638
|
+
}
|
|
2639
|
+
function mfRef(ctx) {
|
|
2640
|
+
return `${nsp(ctx)}_mf`;
|
|
2641
|
+
}
|
|
2642
|
+
function buildRef(ctx) {
|
|
2643
|
+
return `${nsp(ctx)}_build`;
|
|
2644
|
+
}
|
|
1890
2645
|
function reindentStmts(stmts, targetLevels) {
|
|
1891
2646
|
const nonEmpty = stmts.filter((s) => s.trim().length > 0);
|
|
1892
2647
|
if (nonEmpty.length === 0) return stmts;
|
|
@@ -1903,8 +2658,8 @@ function failReturnArr(expectedArr, posExpr) {
|
|
|
1903
2658
|
function hoistExpected(ctx, constArrSource) {
|
|
1904
2659
|
let name = ctx.expectedMap.get(constArrSource);
|
|
1905
2660
|
if (name === void 0) {
|
|
1906
|
-
name =
|
|
1907
|
-
ctx.expectedDecls.push(`const ${name} =
|
|
2661
|
+
name = `${nsp(ctx)}_fx${ctx.expectedDecls.length}`;
|
|
2662
|
+
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
1908
2663
|
ctx.expectedMap.set(constArrSource, name);
|
|
1909
2664
|
}
|
|
1910
2665
|
return name;
|
|
@@ -2002,13 +2757,13 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
|
|
|
2002
2757
|
`${i}}`
|
|
2003
2758
|
];
|
|
2004
2759
|
}
|
|
2005
|
-
function ensureRegexDecl(ctx,
|
|
2760
|
+
function ensureRegexDecl(ctx, source, flags) {
|
|
2006
2761
|
const f = "y" + flags.replace(/[gy]/g, "");
|
|
2007
|
-
const key = `${
|
|
2762
|
+
const key = `${source}/${f}`;
|
|
2008
2763
|
let rName = ctx.regexMap.get(key);
|
|
2009
2764
|
if (rName === void 0) {
|
|
2010
|
-
rName =
|
|
2011
|
-
ctx.regexDecls.push(`const ${rName} = /${
|
|
2765
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2766
|
+
ctx.regexDecls.push(`const ${rName} = /${source}/${f}`);
|
|
2012
2767
|
ctx.regexMap.set(key, rName);
|
|
2013
2768
|
}
|
|
2014
2769
|
return rName;
|
|
@@ -2017,7 +2772,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2017
2772
|
const trivia2 = ctx.activeTrivia;
|
|
2018
2773
|
const existing = ctx.triviaFnNames.get(trivia2);
|
|
2019
2774
|
if (existing) return existing;
|
|
2020
|
-
const fnName =
|
|
2775
|
+
const fnName = `${nsp(ctx)}_tf${ctx.triviaFnNames.size}`;
|
|
2021
2776
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
2022
2777
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
2023
2778
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
@@ -2039,7 +2794,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2039
2794
|
for (const arm of regexSpec.arms) {
|
|
2040
2795
|
const def = arm.parser._def;
|
|
2041
2796
|
if (def.tag !== "regex") break;
|
|
2042
|
-
reNames.push(ensureRegexDecl(ctx, def.
|
|
2797
|
+
reNames.push(ensureRegexDecl(ctx, def.source, def.flags));
|
|
2043
2798
|
}
|
|
2044
2799
|
if (reNames.length === regexSpec.arms.length) {
|
|
2045
2800
|
ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
|
|
@@ -2085,10 +2840,10 @@ function ensureTriviaFn(ctx) {
|
|
|
2085
2840
|
function ensureTriviaCaptureFn(ctx) {
|
|
2086
2841
|
return ensureTriviaFn(ctx);
|
|
2087
2842
|
}
|
|
2088
|
-
function firstSetCond(codeVar,
|
|
2089
|
-
if (
|
|
2090
|
-
if (
|
|
2091
|
-
return
|
|
2843
|
+
function firstSetCond(codeVar, fs2) {
|
|
2844
|
+
if (fs2.kind === "any") return "true";
|
|
2845
|
+
if (fs2.kind === "empty") return "false";
|
|
2846
|
+
return fs2.ranges.map(
|
|
2092
2847
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
2093
2848
|
).join(" || ");
|
|
2094
2849
|
}
|
|
@@ -2100,10 +2855,10 @@ function planDisjointDispatch(parsers) {
|
|
|
2100
2855
|
const cases = [];
|
|
2101
2856
|
let total = 0;
|
|
2102
2857
|
for (const p of parsers) {
|
|
2103
|
-
const
|
|
2104
|
-
if (
|
|
2858
|
+
const fs2 = p._meta.firstSet;
|
|
2859
|
+
if (fs2.kind !== "ranges") return { kind: "if" };
|
|
2105
2860
|
const pts = [];
|
|
2106
|
-
for (const r of
|
|
2861
|
+
for (const r of fs2.ranges) {
|
|
2107
2862
|
if (r.hi - r.lo + 1 > SWITCH_RANGE_LIMIT) return { kind: "if" };
|
|
2108
2863
|
for (let cp = r.lo; cp <= r.hi; cp++) pts.push(cp);
|
|
2109
2864
|
}
|
|
@@ -2226,18 +2981,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2226
2981
|
return true;
|
|
2227
2982
|
}
|
|
2228
2983
|
}
|
|
2229
|
-
function emitFallible(inner, ctx, pos) {
|
|
2984
|
+
function emitFallible(inner, ctx, pos, swallow = false) {
|
|
2230
2985
|
const lbl = v(ctx, "_lbl");
|
|
2231
2986
|
const okV = `${lbl}ok`;
|
|
2232
2987
|
const valV = `${lbl}v`;
|
|
2233
2988
|
const endV = `${lbl}e`;
|
|
2234
2989
|
const savedLabel = ctx.failLabel;
|
|
2235
2990
|
const savedIndent = ctx.indent;
|
|
2991
|
+
const savedRecord = ctx.recordFail;
|
|
2236
2992
|
ctx.failLabel = lbl;
|
|
2237
2993
|
ctx.indent = savedIndent + 1;
|
|
2994
|
+
if (swallow) ctx.recordFail = false;
|
|
2238
2995
|
const r = emit(inner, ctx, pos);
|
|
2239
2996
|
ctx.failLabel = savedLabel;
|
|
2240
2997
|
ctx.indent = savedIndent;
|
|
2998
|
+
ctx.recordFail = savedRecord;
|
|
2241
2999
|
const ind0 = ind(ctx);
|
|
2242
3000
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
2243
3001
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
@@ -2260,6 +3018,7 @@ function emitFallible(inner, ctx, pos) {
|
|
|
2260
3018
|
];
|
|
2261
3019
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
2262
3020
|
}
|
|
3021
|
+
var CHARCODE_CHAIN_MAX = 16;
|
|
2263
3022
|
function emitLit(def, ctx, pos) {
|
|
2264
3023
|
const { value, caseInsensitive } = def;
|
|
2265
3024
|
const len = value.length;
|
|
@@ -2282,7 +3041,7 @@ function emitLit(def, ctx, pos) {
|
|
|
2282
3041
|
...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
|
|
2283
3042
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
2284
3043
|
);
|
|
2285
|
-
} else if (len <=
|
|
3044
|
+
} else if (len <= CHARCODE_CHAIN_MAX) {
|
|
2286
3045
|
const checks = Array.from(
|
|
2287
3046
|
{ length: len },
|
|
2288
3047
|
(_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
|
|
@@ -2304,7 +3063,55 @@ function emitLit(def, ctx, pos) {
|
|
|
2304
3063
|
function escapeKeywordRe(s) {
|
|
2305
3064
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2306
3065
|
}
|
|
3066
|
+
function emitKeywordsFast(def, ctx, pos) {
|
|
3067
|
+
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
3068
|
+
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
3069
|
+
if (def.caseInsensitive && def.boundary) return null;
|
|
3070
|
+
let boundary = null;
|
|
3071
|
+
if (def.boundary) {
|
|
3072
|
+
let body = def.boundary;
|
|
3073
|
+
const negated = body.startsWith("^");
|
|
3074
|
+
if (negated) body = body.slice(1);
|
|
3075
|
+
const ranges = parseClassRanges(body);
|
|
3076
|
+
if (!ranges) return null;
|
|
3077
|
+
boundary = { ranges, negated };
|
|
3078
|
+
}
|
|
3079
|
+
const mint = (prefix = "_v") => v(ctx, prefix);
|
|
3080
|
+
const lbl = v(ctx, "_kwLbl");
|
|
3081
|
+
const valV = v(ctx, "_kwv");
|
|
3082
|
+
const endV = v(ctx, "_kwe");
|
|
3083
|
+
const bodyInd = ind(ctx) + " ";
|
|
3084
|
+
const tries = [];
|
|
3085
|
+
for (const w of def.words) {
|
|
3086
|
+
const cps = Array.from(w, (ch) => ch.codePointAt(0));
|
|
3087
|
+
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
3088
|
+
if (boundary) {
|
|
3089
|
+
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
3090
|
+
}
|
|
3091
|
+
const m = emitShapeMatch(shape, pos, mint, bodyInd);
|
|
3092
|
+
tries.push(
|
|
3093
|
+
...m.setup,
|
|
3094
|
+
// Slice from input rather than reusing the literal word: `caseInsensitive`
|
|
3095
|
+
// must return the text as it actually appeared (e.g. "ABC" for keyword
|
|
3096
|
+
// "abc"), matching what the original `RegExp.exec()[0]` returned.
|
|
3097
|
+
`${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
|
|
3098
|
+
);
|
|
3099
|
+
}
|
|
3100
|
+
const stmts = [
|
|
3101
|
+
`${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
|
|
3102
|
+
`${ind(ctx)}${lbl}: {`,
|
|
3103
|
+
...tries,
|
|
3104
|
+
`${ind(ctx)}}`,
|
|
3105
|
+
// Every word has length >= 1 (checked above), so a real match always
|
|
3106
|
+
// advances past `pos` — `endV === pos` only happens when no candidate matched.
|
|
3107
|
+
...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
|
|
3108
|
+
];
|
|
3109
|
+
stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
|
|
3110
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3111
|
+
}
|
|
2307
3112
|
function emitKeywords(def, ctx, pos) {
|
|
3113
|
+
const fast = emitKeywordsFast(def, ctx, pos);
|
|
3114
|
+
if (fast) return fast;
|
|
2308
3115
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
2309
3116
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
2310
3117
|
const flags = def.caseInsensitive ? "iuy" : "uy";
|
|
@@ -2312,7 +3119,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2312
3119
|
const key = `${source}/${flags}`;
|
|
2313
3120
|
let rName = ctx.regexMap.get(key);
|
|
2314
3121
|
if (rName === void 0) {
|
|
2315
|
-
rName =
|
|
3122
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2316
3123
|
ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
|
|
2317
3124
|
ctx.regexMap.set(key, rName);
|
|
2318
3125
|
}
|
|
@@ -2346,11 +3153,11 @@ function emitRegex(def, ctx, pos) {
|
|
|
2346
3153
|
}
|
|
2347
3154
|
}
|
|
2348
3155
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2349
|
-
const key = `${def.
|
|
3156
|
+
const key = `${def.source}/${flags}`;
|
|
2350
3157
|
let rName = ctx.regexMap.get(key);
|
|
2351
3158
|
if (rName === void 0) {
|
|
2352
|
-
rName =
|
|
2353
|
-
ctx.regexDecls.push(`const ${rName} = /${def.
|
|
3159
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3160
|
+
ctx.regexDecls.push(`const ${rName} = /${def.source}/${flags}`);
|
|
2354
3161
|
ctx.regexMap.set(key, rName);
|
|
2355
3162
|
}
|
|
2356
3163
|
const mv = v(ctx, "_m");
|
|
@@ -2435,6 +3242,31 @@ function failsAtStart(p) {
|
|
|
2435
3242
|
return false;
|
|
2436
3243
|
}
|
|
2437
3244
|
}
|
|
3245
|
+
function armStaticExpected(ctx, p) {
|
|
3246
|
+
return hoistExpected(ctx, staticExpectedArr([p]));
|
|
3247
|
+
}
|
|
3248
|
+
function canMatchEmptyAtStart(p) {
|
|
3249
|
+
const d = p._def;
|
|
3250
|
+
switch (d.tag) {
|
|
3251
|
+
case "regex":
|
|
3252
|
+
return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
|
|
3253
|
+
case "optional":
|
|
3254
|
+
return true;
|
|
3255
|
+
case "many":
|
|
3256
|
+
return true;
|
|
3257
|
+
case "transform":
|
|
3258
|
+
case "label":
|
|
3259
|
+
return canMatchEmptyAtStart(d.parser);
|
|
3260
|
+
case "literal":
|
|
3261
|
+
return d.value.length === 0;
|
|
3262
|
+
default:
|
|
3263
|
+
return false;
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
function needsFirstSetGuard(p) {
|
|
3267
|
+
const fs2 = p._meta.firstSet;
|
|
3268
|
+
return fs2.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
3269
|
+
}
|
|
2438
3270
|
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
2439
3271
|
if (failsAtStart(p)) {
|
|
2440
3272
|
const r = emit(p, ctx, pos);
|
|
@@ -2502,7 +3334,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
2502
3334
|
const reKey = `${regexDef.source}/${cleanFlags}`;
|
|
2503
3335
|
let reVar = ctx.regexMap.get(reKey);
|
|
2504
3336
|
if (reVar === void 0) {
|
|
2505
|
-
reVar =
|
|
3337
|
+
reVar = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2506
3338
|
ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
|
|
2507
3339
|
ctx.regexMap.set(reKey, reVar);
|
|
2508
3340
|
}
|
|
@@ -2576,21 +3408,26 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
2576
3408
|
const resValV = v(ctx, "_crv");
|
|
2577
3409
|
const resEndV = v(ctx, "_cre");
|
|
2578
3410
|
const resOkV = v(ctx, "_crok");
|
|
3411
|
+
const codeV = v(ctx, "_chcode");
|
|
2579
3412
|
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2580
3413
|
const ind0 = ind(ctx);
|
|
2581
3414
|
const stmts = [
|
|
2582
3415
|
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
2583
|
-
`${ind0}let ${slots.join(", ")}
|
|
3416
|
+
`${ind0}let ${slots.join(", ")}`,
|
|
3417
|
+
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
2584
3418
|
];
|
|
2585
3419
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2586
3420
|
const p = def.parsers[i];
|
|
2587
3421
|
const gate = def.gates[i];
|
|
2588
3422
|
const autoNot = def.autoNot[i];
|
|
3423
|
+
const atStart = failsAtStart(p);
|
|
3424
|
+
const staticFx = armStaticExpected(ctx, p);
|
|
3425
|
+
const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
2589
3426
|
let gateCond = null;
|
|
2590
3427
|
if (gate) {
|
|
2591
3428
|
const gateIdx = ctx.mapFns.length;
|
|
2592
3429
|
ctx.mapFns.push(gate);
|
|
2593
|
-
gateCond =
|
|
3430
|
+
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
2594
3431
|
}
|
|
2595
3432
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
2596
3433
|
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
@@ -2600,39 +3437,41 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
2600
3437
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2601
3438
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2602
3439
|
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
3440
|
+
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
2603
3441
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2604
|
-
ctx
|
|
2605
|
-
|
|
3442
|
+
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
3443
|
+
ctx.indent += fsGuard ? 2 : 1;
|
|
2606
3444
|
if (markLeaves) {
|
|
2607
3445
|
stmts.push(
|
|
2608
|
-
`${
|
|
2609
|
-
`${
|
|
2610
|
-
`${
|
|
2611
|
-
`${
|
|
3446
|
+
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
3447
|
+
`${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
|
|
3448
|
+
`${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
3449
|
+
`${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
|
|
2612
3450
|
);
|
|
2613
3451
|
}
|
|
2614
|
-
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3452
|
+
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
|
|
2615
3453
|
stmts.push(...armStmts);
|
|
2616
|
-
ctx.indent--;
|
|
2617
3454
|
if (autoNot && autoNot.length > 0) {
|
|
2618
3455
|
const anCode = v(ctx, "_anc");
|
|
2619
3456
|
const rejectCond = autoNot.map(
|
|
2620
3457
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
|
|
2621
3458
|
).join(" || ");
|
|
2622
|
-
stmts.push(`${
|
|
2623
|
-
stmts.push(`${
|
|
2624
|
-
stmts.push(`${
|
|
2625
|
-
stmts.push(`${
|
|
2626
|
-
stmts.push(`${
|
|
2627
|
-
stmts.push(`${
|
|
2628
|
-
stmts.push(`${
|
|
2629
|
-
stmts.push(`${
|
|
2630
|
-
stmts.push(`${
|
|
2631
|
-
if (rollback) stmts.push(`${
|
|
3459
|
+
stmts.push(`${ind(ctx)}if (${okVar}) {`);
|
|
3460
|
+
stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
|
|
3461
|
+
stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
|
|
3462
|
+
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
3463
|
+
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
3464
|
+
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
3465
|
+
stmts.push(`${ind(ctx)} }`);
|
|
3466
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3467
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
3468
|
+
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
2632
3469
|
} else {
|
|
2633
|
-
stmts.push(`${
|
|
2634
|
-
stmts.push(`${
|
|
3470
|
+
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
3471
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
|
|
2635
3472
|
}
|
|
3473
|
+
ctx.indent -= fsGuard ? 2 : 1;
|
|
3474
|
+
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
2636
3475
|
stmts.push(`${ind0}}`);
|
|
2637
3476
|
}
|
|
2638
3477
|
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
@@ -2655,7 +3494,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2655
3494
|
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
2656
3495
|
const vv = v(ctx);
|
|
2657
3496
|
return {
|
|
2658
|
-
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} =
|
|
3497
|
+
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
2659
3498
|
valueVar: vv,
|
|
2660
3499
|
endVar: endV
|
|
2661
3500
|
};
|
|
@@ -2665,7 +3504,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2665
3504
|
function emitLiteralCondition(litVal, pos) {
|
|
2666
3505
|
const len = litVal.length;
|
|
2667
3506
|
if (len === 0) return "true";
|
|
2668
|
-
if (len >
|
|
3507
|
+
if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
|
|
2669
3508
|
const checks = [`${pos} + ${len} <= input.length`];
|
|
2670
3509
|
for (let i = 0; i < len; i++) {
|
|
2671
3510
|
const code = litVal.codePointAt(i);
|
|
@@ -2714,7 +3553,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2714
3553
|
itemPos = npV;
|
|
2715
3554
|
}
|
|
2716
3555
|
}
|
|
2717
|
-
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
3556
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
2718
3557
|
stmts.push(
|
|
2719
3558
|
...iterStmts,
|
|
2720
3559
|
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
|
|
@@ -2728,7 +3567,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2728
3567
|
function emitOptional(def, ctx, pos) {
|
|
2729
3568
|
const valV = v(ctx, "_opt");
|
|
2730
3569
|
const endV = v(ctx, "_opte");
|
|
2731
|
-
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
3570
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2732
3571
|
const ind0 = ind(ctx);
|
|
2733
3572
|
const stmts = [
|
|
2734
3573
|
...lblStmts,
|
|
@@ -2740,7 +3579,7 @@ function emitOptional(def, ctx, pos) {
|
|
|
2740
3579
|
function emitSepBy(_p, def, ctx, pos) {
|
|
2741
3580
|
const arrV = v(ctx, "_arr");
|
|
2742
3581
|
const curV = v(ctx, "_cur");
|
|
2743
|
-
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
3582
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
2744
3583
|
const stmts = [
|
|
2745
3584
|
`${ind(ctx)}const ${arrV} = []`,
|
|
2746
3585
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
@@ -2772,11 +3611,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2772
3611
|
);
|
|
2773
3612
|
sepAtPos = spV;
|
|
2774
3613
|
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}; `;
|
|
2775
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3614
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2776
3615
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
2777
3616
|
const npV = v(ctx, "_np");
|
|
2778
3617
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
2779
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3618
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2780
3619
|
stmts.push(
|
|
2781
3620
|
...nextStmts,
|
|
2782
3621
|
// item failed → unwind the separator too, back to the end of the last item
|
|
@@ -2789,11 +3628,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2789
3628
|
const spV = v(ctx, "_sp");
|
|
2790
3629
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
2791
3630
|
sepAtPos = spV;
|
|
2792
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3631
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2793
3632
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2794
3633
|
const npV = v(ctx, "_np");
|
|
2795
3634
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
2796
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3635
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2797
3636
|
stmts.push(
|
|
2798
3637
|
...nextStmts,
|
|
2799
3638
|
`${ind(ctx)}if (!${nextOk}) break`,
|
|
@@ -2810,10 +3649,10 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2810
3649
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
2811
3650
|
);
|
|
2812
3651
|
}
|
|
2813
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3652
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2814
3653
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2815
3654
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
2816
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
|
|
3655
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
2817
3656
|
stmts.push(
|
|
2818
3657
|
...nextStmts,
|
|
2819
3658
|
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
|
|
@@ -2871,7 +3710,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2871
3710
|
return { stmts, valueVar: valV, endVar: curV };
|
|
2872
3711
|
}
|
|
2873
3712
|
function emitNot(def, ctx, pos) {
|
|
2874
|
-
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3713
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2875
3714
|
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2876
3715
|
return {
|
|
2877
3716
|
stmts: [
|
|
@@ -2883,16 +3722,17 @@ function emitNot(def, ctx, pos) {
|
|
|
2883
3722
|
};
|
|
2884
3723
|
}
|
|
2885
3724
|
function emitNode(def, ctx, pos) {
|
|
2886
|
-
const
|
|
3725
|
+
const structural = def.build === void 0;
|
|
3726
|
+
const mkType = structural ? null : analyzeMkInlineBuild(def);
|
|
2887
3727
|
let buildIdx = null;
|
|
2888
|
-
if (!mkType) {
|
|
3728
|
+
if (!mkType && !structural) {
|
|
2889
3729
|
buildIdx = ctx.buildFns.length;
|
|
2890
3730
|
ctx.buildFns.push(def.build);
|
|
2891
3731
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
2892
3732
|
}
|
|
2893
3733
|
const i = ind(ctx);
|
|
2894
|
-
const capturesTrivia = mkType !== null || buildReadsTrivia(def);
|
|
2895
|
-
const clonesState = buildReadsState(def);
|
|
3734
|
+
const capturesTrivia = mkType !== null || structural || buildReadsTrivia(def);
|
|
3735
|
+
const clonesState = structural || buildReadsState(def);
|
|
2896
3736
|
const chV = v(ctx, "_ch");
|
|
2897
3737
|
const rawV = v(ctx, "_raw");
|
|
2898
3738
|
const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
@@ -2915,7 +3755,8 @@ function emitNode(def, ctx, pos) {
|
|
|
2915
3755
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
2916
3756
|
}
|
|
2917
3757
|
const ndV = v(ctx, "_nd");
|
|
2918
|
-
const
|
|
3758
|
+
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})`;
|
|
3759
|
+
const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
|
|
2919
3760
|
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;
|
|
2920
3761
|
stmts.push(
|
|
2921
3762
|
`${i}const ${ndV} = ${finalExpr}`,
|
|
@@ -2940,8 +3781,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2940
3781
|
return { stmts, valueVar: vv, endVar: ev };
|
|
2941
3782
|
}
|
|
2942
3783
|
function emitLazy(p, def, ctx, pos) {
|
|
3784
|
+
const canonical = ctx.ruleNames?.get(p);
|
|
2943
3785
|
const usage = ctx.lazyUsage;
|
|
2944
|
-
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
3786
|
+
if (!canonical && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
2945
3787
|
let resolved;
|
|
2946
3788
|
try {
|
|
2947
3789
|
resolved = def.thunk();
|
|
@@ -2951,7 +3793,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2951
3793
|
return emit(resolved, ctx, pos);
|
|
2952
3794
|
}
|
|
2953
3795
|
if (!ctx.namedParsers.has(p)) {
|
|
2954
|
-
const fnName2 =
|
|
3796
|
+
const fnName2 = canonical ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
2955
3797
|
ctx.namedParsers.set(p, fnName2);
|
|
2956
3798
|
let resolved;
|
|
2957
3799
|
try {
|
|
@@ -2962,11 +3804,14 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
2962
3804
|
}
|
|
2963
3805
|
const savedIndent = ctx.indent;
|
|
2964
3806
|
const savedFailLabel = ctx.failLabel;
|
|
3807
|
+
const savedRecord = ctx.recordFail;
|
|
2965
3808
|
ctx.indent = 1;
|
|
2966
3809
|
ctx.failLabel = "_pfail";
|
|
3810
|
+
ctx.recordFail = true;
|
|
2967
3811
|
const r = emit(resolved, ctx, "_pos");
|
|
2968
3812
|
ctx.indent = savedIndent;
|
|
2969
3813
|
ctx.failLabel = savedFailLabel;
|
|
3814
|
+
ctx.recordFail = savedRecord;
|
|
2970
3815
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
2971
3816
|
}
|
|
2972
3817
|
const fnName = ctx.namedParsers.get(p);
|
|
@@ -3066,7 +3911,7 @@ function emit(p, ctx, pos) {
|
|
|
3066
3911
|
return {
|
|
3067
3912
|
stmts: [
|
|
3068
3913
|
...inner.stmts,
|
|
3069
|
-
`${ind(ctx)}const ${mv} =
|
|
3914
|
+
`${ind(ctx)}const ${mv} = ${mfRef(ctx)}[${fnIdx}](${inner.valueVar}, { start: ${pos}, end: ${inner.endVar} })`
|
|
3070
3915
|
],
|
|
3071
3916
|
valueVar: mv,
|
|
3072
3917
|
endVar: inner.endVar
|
|
@@ -3129,7 +3974,7 @@ function emit(p, ctx, pos) {
|
|
|
3129
3974
|
const vv = v(ctx);
|
|
3130
3975
|
return {
|
|
3131
3976
|
stmts: [
|
|
3132
|
-
...emitIfFail(ctx,
|
|
3977
|
+
...emitIfFail(ctx, `!${mfRef(ctx)}[${fnIdx}](_ctx.state)`, failBody(ctx, '"gate"', pos)),
|
|
3133
3978
|
`${ind(ctx)}const ${vv} = null`
|
|
3134
3979
|
],
|
|
3135
3980
|
valueVar: vv,
|
|
@@ -3146,11 +3991,14 @@ function emit(p, ctx, pos) {
|
|
|
3146
3991
|
ctx.namedParsers.set(innerParser, fnName);
|
|
3147
3992
|
const savedIndent = ctx.indent;
|
|
3148
3993
|
const savedFailLabel = ctx.failLabel;
|
|
3994
|
+
const savedRecord = ctx.recordFail;
|
|
3149
3995
|
ctx.indent = 1;
|
|
3150
3996
|
ctx.failLabel = "_pfail";
|
|
3997
|
+
ctx.recordFail = true;
|
|
3151
3998
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
3152
3999
|
ctx.indent = savedIndent;
|
|
3153
4000
|
ctx.failLabel = savedFailLabel;
|
|
4001
|
+
ctx.recordFail = savedRecord;
|
|
3154
4002
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
3155
4003
|
}
|
|
3156
4004
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -3159,7 +4007,7 @@ function emit(p, ctx, pos) {
|
|
|
3159
4007
|
const ev = v(ctx, "_wce");
|
|
3160
4008
|
return {
|
|
3161
4009
|
stmts: [
|
|
3162
|
-
`${ind(ctx)}const ${rv} = { ..._ctx, state:
|
|
4010
|
+
`${ind(ctx)}const ${rv} = { ..._ctx, state: ${mfRef(ctx)}[${evIdx}]() }`,
|
|
3163
4011
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
3164
4012
|
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3165
4013
|
// and propagate the inner failure verbatim (interpreter parity).
|
|
@@ -3290,6 +4138,39 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3290
4138
|
for (const root of roots) walk2(root);
|
|
3291
4139
|
return { counts, recursive };
|
|
3292
4140
|
}
|
|
4141
|
+
function ruleDependencies(ruleMap) {
|
|
4142
|
+
const nameOf = /* @__PURE__ */ new Map();
|
|
4143
|
+
for (const [name, comb] of ruleMap) nameOf.set(comb, name);
|
|
4144
|
+
const deps = /* @__PURE__ */ new Map();
|
|
4145
|
+
for (const [name, comb] of ruleMap) {
|
|
4146
|
+
const found = /* @__PURE__ */ new Set();
|
|
4147
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4148
|
+
const walk2 = (p, isRoot) => {
|
|
4149
|
+
const def = p._def;
|
|
4150
|
+
const boundary = def.tag === "lazy" ? p._ruleName ?? nameOf.get(p) : nameOf.get(p);
|
|
4151
|
+
if (!isRoot && boundary !== void 0) {
|
|
4152
|
+
found.add(boundary);
|
|
4153
|
+
return;
|
|
4154
|
+
}
|
|
4155
|
+
if (seen.has(p)) return;
|
|
4156
|
+
seen.add(p);
|
|
4157
|
+
if (def.tag === "lazy") {
|
|
4158
|
+
let resolved;
|
|
4159
|
+
try {
|
|
4160
|
+
resolved = def.thunk();
|
|
4161
|
+
} catch {
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
walk2(resolved, false);
|
|
4165
|
+
return;
|
|
4166
|
+
}
|
|
4167
|
+
for (const child of childrenOf(def)) walk2(child, false);
|
|
4168
|
+
};
|
|
4169
|
+
walk2(comb, true);
|
|
4170
|
+
deps.set(name, [...found]);
|
|
4171
|
+
}
|
|
4172
|
+
return deps;
|
|
4173
|
+
}
|
|
3293
4174
|
function compile(combinator, mapFnSources) {
|
|
3294
4175
|
const ctx = {
|
|
3295
4176
|
vars: 0,
|
|
@@ -3298,6 +4179,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3298
4179
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3299
4180
|
expectedDecls: [],
|
|
3300
4181
|
expectedMap: /* @__PURE__ */ new Map(),
|
|
4182
|
+
recordFail: true,
|
|
3301
4183
|
mapFns: [],
|
|
3302
4184
|
mapFnSrcs: [],
|
|
3303
4185
|
buildFns: [],
|
|
@@ -3380,6 +4262,7 @@ function compileRuleMap(ruleMap) {
|
|
|
3380
4262
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3381
4263
|
expectedDecls: [],
|
|
3382
4264
|
expectedMap: /* @__PURE__ */ new Map(),
|
|
4265
|
+
recordFail: true,
|
|
3383
4266
|
mapFns: [],
|
|
3384
4267
|
mapFnSrcs: [],
|
|
3385
4268
|
buildFns: [],
|
|
@@ -3393,6 +4276,15 @@ function compileRuleMap(ruleMap) {
|
|
|
3393
4276
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
3394
4277
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule))
|
|
3395
4278
|
};
|
|
4279
|
+
const seenNames = /* @__PURE__ */ new Map();
|
|
4280
|
+
const ruleNames = /* @__PURE__ */ new Map();
|
|
4281
|
+
for (const [key, rule] of ruleMap) {
|
|
4282
|
+
const base = `_r_${key.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4283
|
+
const n = seenNames.get(base) ?? 0;
|
|
4284
|
+
seenNames.set(base, n + 1);
|
|
4285
|
+
ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
|
|
4286
|
+
}
|
|
4287
|
+
ctx.ruleNames = ruleNames;
|
|
3396
4288
|
const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
|
|
3397
4289
|
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
3398
4290
|
` : "";
|
|
@@ -3402,8 +4294,8 @@ function compileRuleMap(ruleMap) {
|
|
|
3402
4294
|
const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
|
|
3403
4295
|
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
3404
4296
|
if (!canInline) return null;
|
|
3405
|
-
const mfDecl = derivedSrcs?.length ? ` const
|
|
3406
|
-
const buildDecl = buildSources?.length ? ` const
|
|
4297
|
+
const mfDecl = derivedSrcs?.length ? ` const ${mfRef(ctx)} = [${derivedSrcs.join(", ")}]` : "";
|
|
4298
|
+
const buildDecl = buildSources?.length ? ` const ${buildRef(ctx)} = [${buildSources.join(", ")}]` : "";
|
|
3407
4299
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3408
4300
|
const hoistedDecls = [
|
|
3409
4301
|
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
@@ -3436,6 +4328,150 @@ function compileRuleMap(ruleMap) {
|
|
|
3436
4328
|
].join("\n");
|
|
3437
4329
|
return { keys: perEntry.map((e) => e.key), replacement };
|
|
3438
4330
|
}
|
|
4331
|
+
function compileLinkable(ruleMapArg, ns) {
|
|
4332
|
+
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4333
|
+
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4334
|
+
const d = val._def;
|
|
4335
|
+
if (d.tag !== "lazy") return true;
|
|
4336
|
+
try {
|
|
4337
|
+
d.thunk();
|
|
4338
|
+
return true;
|
|
4339
|
+
} catch {
|
|
4340
|
+
return false;
|
|
4341
|
+
}
|
|
4342
|
+
});
|
|
4343
|
+
const ctx = {
|
|
4344
|
+
vars: 0,
|
|
4345
|
+
indent: 1,
|
|
4346
|
+
regexDecls: [],
|
|
4347
|
+
regexMap: /* @__PURE__ */ new Map(),
|
|
4348
|
+
expectedDecls: [],
|
|
4349
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4350
|
+
recordFail: true,
|
|
4351
|
+
mapFns: [],
|
|
4352
|
+
mapFnSrcs: [],
|
|
4353
|
+
buildFns: [],
|
|
4354
|
+
buildSrcs: [],
|
|
4355
|
+
runtimeParsers: [],
|
|
4356
|
+
needsCollator: false,
|
|
4357
|
+
namedParsers: /* @__PURE__ */ new Map(),
|
|
4358
|
+
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4359
|
+
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4360
|
+
namedFnDecls: [],
|
|
4361
|
+
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4362
|
+
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4363
|
+
ns
|
|
4364
|
+
};
|
|
4365
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4366
|
+
const ruleNames = /* @__PURE__ */ new Map();
|
|
4367
|
+
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4368
|
+
for (const [key, rule] of ruleMap) {
|
|
4369
|
+
const base = `_r_${key.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4370
|
+
const n = seen.get(base) ?? 0;
|
|
4371
|
+
seen.set(base, n + 1);
|
|
4372
|
+
const fn = n === 0 ? base : `${base}$${n}`;
|
|
4373
|
+
ruleNames.set(rule, fn);
|
|
4374
|
+
fnNameToKey.set(fn, key);
|
|
4375
|
+
}
|
|
4376
|
+
ctx.ruleNames = ruleNames;
|
|
4377
|
+
{
|
|
4378
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
4379
|
+
const scanExternal = (p) => {
|
|
4380
|
+
if (scanned.has(p)) return;
|
|
4381
|
+
scanned.add(p);
|
|
4382
|
+
const def = p._def;
|
|
4383
|
+
if (def.tag === "lazy") {
|
|
4384
|
+
let resolved;
|
|
4385
|
+
try {
|
|
4386
|
+
resolved = def.thunk();
|
|
4387
|
+
} catch {
|
|
4388
|
+
resolved = void 0;
|
|
4389
|
+
}
|
|
4390
|
+
if (resolved === void 0) {
|
|
4391
|
+
const name = p._ruleName;
|
|
4392
|
+
if (name && !ctx.namedParsers.has(p)) {
|
|
4393
|
+
const fn = `_r_${name.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4394
|
+
ruleNames.set(p, fn);
|
|
4395
|
+
ctx.namedParsers.set(p, fn);
|
|
4396
|
+
}
|
|
4397
|
+
return;
|
|
4398
|
+
}
|
|
4399
|
+
scanExternal(resolved);
|
|
4400
|
+
return;
|
|
4401
|
+
}
|
|
4402
|
+
for (const child of childrenOf(def)) scanExternal(child);
|
|
4403
|
+
};
|
|
4404
|
+
for (const [, rule] of ruleMap) scanExternal(rule);
|
|
4405
|
+
}
|
|
4406
|
+
const perEntry = [];
|
|
4407
|
+
for (const [key, rule] of ruleMap) {
|
|
4408
|
+
const fn = ruleNames.get(rule);
|
|
4409
|
+
if (!ctx.namedParsers.has(rule)) {
|
|
4410
|
+
ctx.namedParsers.set(rule, fn);
|
|
4411
|
+
let resolved = rule;
|
|
4412
|
+
const def = rule._def;
|
|
4413
|
+
if (def.tag === "lazy") {
|
|
4414
|
+
try {
|
|
4415
|
+
resolved = def.thunk();
|
|
4416
|
+
} catch {
|
|
4417
|
+
return null;
|
|
4418
|
+
}
|
|
4419
|
+
}
|
|
4420
|
+
const savedIndent = ctx.indent, savedFail = ctx.failLabel, savedRec = ctx.recordFail;
|
|
4421
|
+
ctx.indent = 1;
|
|
4422
|
+
ctx.failLabel = "_pfail";
|
|
4423
|
+
ctx.recordFail = true;
|
|
4424
|
+
const body = emit(resolved, ctx, "_pos");
|
|
4425
|
+
ctx.indent = savedIndent;
|
|
4426
|
+
ctx.failLabel = savedFail;
|
|
4427
|
+
ctx.recordFail = savedRec;
|
|
4428
|
+
pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
|
|
4429
|
+
}
|
|
4430
|
+
perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
|
|
4431
|
+
}
|
|
4432
|
+
if (ctx.runtimeParsers.length !== 0) return null;
|
|
4433
|
+
const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
|
|
4434
|
+
const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
|
|
4435
|
+
const ruleFns = /* @__PURE__ */ new Map();
|
|
4436
|
+
const privateFns = [];
|
|
4437
|
+
for (const decl of ctx.namedFnDecls) {
|
|
4438
|
+
const fnName = decl.match(/^function (\S+?)\(/)?.[1];
|
|
4439
|
+
const key = fnName ? fnNameToKey.get(fnName) : void 0;
|
|
4440
|
+
if (key !== void 0) ruleFns.set(key, decl);
|
|
4441
|
+
else privateFns.push(decl);
|
|
4442
|
+
}
|
|
4443
|
+
const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
|
|
4444
|
+
const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
|
|
4445
|
+
const prelude = [
|
|
4446
|
+
...ctx.regexDecls,
|
|
4447
|
+
...ctx.expectedDecls,
|
|
4448
|
+
mfDecl,
|
|
4449
|
+
buildDecl,
|
|
4450
|
+
...privateFns
|
|
4451
|
+
].filter(Boolean);
|
|
4452
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
4453
|
+
for (const { key, r } of perEntry) {
|
|
4454
|
+
wrappers.set(key, [
|
|
4455
|
+
`function(input, _pos, _ctx) {`,
|
|
4456
|
+
` let pos = _pos`,
|
|
4457
|
+
...r.stmts,
|
|
4458
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
4459
|
+
`}`
|
|
4460
|
+
].join("\n"));
|
|
4461
|
+
}
|
|
4462
|
+
return {
|
|
4463
|
+
ns,
|
|
4464
|
+
keys: perEntry.map((e) => e.key),
|
|
4465
|
+
prelude,
|
|
4466
|
+
ruleFns,
|
|
4467
|
+
wrappers,
|
|
4468
|
+
deps: ruleDependencies(ruleMap),
|
|
4469
|
+
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4470
|
+
needsCollator: ctx.needsCollator,
|
|
4471
|
+
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4472
|
+
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4473
|
+
};
|
|
4474
|
+
}
|
|
3439
4475
|
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
3440
4476
|
const bodyLines = [
|
|
3441
4477
|
` let pos = _pos`,
|
|
@@ -3474,6 +4510,48 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3474
4510
|
].join("\n");
|
|
3475
4511
|
}
|
|
3476
4512
|
|
|
4513
|
+
// src/compiler/linker.ts
|
|
4514
|
+
function fusedBody(pieces) {
|
|
4515
|
+
const winner = /* @__PURE__ */ new Map();
|
|
4516
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4517
|
+
for (const [k, p] of winner) {
|
|
4518
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
4519
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4522
|
+
const contributing = new Set(winner.values());
|
|
4523
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4524
|
+
const needsCollator = [...contributing].some((p) => p.needsCollator);
|
|
4525
|
+
const lines = [
|
|
4526
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4527
|
+
"const _pfFail = {}",
|
|
4528
|
+
"let _pfEnd",
|
|
4529
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4530
|
+
...needsCollator ? ["const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })"] : [],
|
|
4531
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4532
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
4533
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4534
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4535
|
+
];
|
|
4536
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4537
|
+
const body = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4538
|
+
const env = {};
|
|
4539
|
+
for (const p of contributing) {
|
|
4540
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4541
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4542
|
+
}
|
|
4543
|
+
return { body, env };
|
|
4544
|
+
}
|
|
4545
|
+
function emitFusedSource(pieces) {
|
|
4546
|
+
const { body, env } = fusedBody(pieces);
|
|
4547
|
+
if (Object.keys(env).length > 0) {
|
|
4548
|
+
throw new Error("emitFusedSource: artifact carries non-static callbacks (runtime-only); cannot emit static source");
|
|
4549
|
+
}
|
|
4550
|
+
return `/* @__PURE__ */ (() => {
|
|
4551
|
+
${body}
|
|
4552
|
+
})()`;
|
|
4553
|
+
}
|
|
4554
|
+
|
|
3477
4555
|
// src/combinators/scanTo.ts
|
|
3478
4556
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
3479
4557
|
const meta = {
|
|
@@ -3554,11 +4632,11 @@ function balanced(open, close, options = {}) {
|
|
|
3554
4632
|
return combi;
|
|
3555
4633
|
}
|
|
3556
4634
|
function firstSetClassChars(c) {
|
|
3557
|
-
const
|
|
3558
|
-
if (
|
|
3559
|
-
if (
|
|
4635
|
+
const fs2 = c._meta.firstSet;
|
|
4636
|
+
if (fs2.kind === "empty") return [];
|
|
4637
|
+
if (fs2.kind !== "ranges") return null;
|
|
3560
4638
|
const out = [];
|
|
3561
|
-
for (const { lo, hi } of
|
|
4639
|
+
for (const { lo, hi } of fs2.ranges) {
|
|
3562
4640
|
if (hi - lo > 8) return null;
|
|
3563
4641
|
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3564
4642
|
}
|
|
@@ -3568,6 +4646,12 @@ function escapeClassChar(ch) {
|
|
|
3568
4646
|
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3569
4647
|
}
|
|
3570
4648
|
|
|
4649
|
+
// src/cst/offset-model.ts
|
|
4650
|
+
var { isFinite } = Number;
|
|
4651
|
+
|
|
4652
|
+
// src/index.ts
|
|
4653
|
+
registerRegexAnalyzer(firstSetFromRegex);
|
|
4654
|
+
|
|
3571
4655
|
// src/plugin/evaluator.ts
|
|
3572
4656
|
function isWordFactory(v2) {
|
|
3573
4657
|
return !!v2 && typeof v2 === "object" && v2.tag === "wordFactory";
|
|
@@ -3667,16 +4751,18 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
3667
4751
|
}
|
|
3668
4752
|
if (callee.name === "node" && code !== void 0) {
|
|
3669
4753
|
const [typeArg, parserArg, buildArg, optsArg] = node2.arguments;
|
|
3670
|
-
if (!typeArg || !parserArg ||
|
|
4754
|
+
if (!typeArg || !parserArg || typeArg.type === "SpreadElement" || parserArg.type === "SpreadElement") return null;
|
|
3671
4755
|
const typeVal = anyValue(typeArg, scope, code);
|
|
3672
4756
|
if (typeof typeVal !== "string") return null;
|
|
3673
4757
|
const inner = anyValue(parserArg, scope, code, mfs);
|
|
3674
4758
|
if (!isCombinator(inner)) return null;
|
|
3675
|
-
const
|
|
4759
|
+
const be = buildArg;
|
|
4760
|
+
const hasBuild = be !== void 0 && be.type !== "SpreadElement" && !(be.type === "Identifier" && be.name === "undefined");
|
|
4761
|
+
const buildSrc = hasBuild ? code.slice(be.start, be.end) : void 0;
|
|
3676
4762
|
const collapse = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticCollapseOption(optsArg) : false;
|
|
3677
4763
|
try {
|
|
3678
|
-
const combi = node(typeVal, inner, () => null, collapse ? { collapse: true } : void 0);
|
|
3679
|
-
if (combi._def.tag === "node") combi._def.buildSrc = buildSrc;
|
|
4764
|
+
const combi = node(typeVal, inner, hasBuild ? () => null : void 0, collapse ? { collapse: true } : void 0);
|
|
4765
|
+
if (combi._def.tag === "node" && buildSrc !== void 0) combi._def.buildSrc = buildSrc;
|
|
3680
4766
|
return combi;
|
|
3681
4767
|
} catch {
|
|
3682
4768
|
return null;
|
|
@@ -3862,6 +4948,49 @@ function evaluateCombinatorArray(node2, scope, code) {
|
|
|
3862
4948
|
if (!val.every(isCombinator)) return null;
|
|
3863
4949
|
return val;
|
|
3864
4950
|
}
|
|
4951
|
+
function propKey(prop) {
|
|
4952
|
+
if (prop.type !== "Property" || prop.computed || !prop.key) return null;
|
|
4953
|
+
if (prop.key.type === "Identifier") return prop.key.name ?? null;
|
|
4954
|
+
if (prop.key.type === "Literal") return String(prop.key.value);
|
|
4955
|
+
return null;
|
|
4956
|
+
}
|
|
4957
|
+
function collectRuleKeys(retObj) {
|
|
4958
|
+
const out = [];
|
|
4959
|
+
for (const prop of retObj.properties) {
|
|
4960
|
+
const key = propKey(prop);
|
|
4961
|
+
if (!key) return null;
|
|
4962
|
+
out.push(key);
|
|
4963
|
+
}
|
|
4964
|
+
return out;
|
|
4965
|
+
}
|
|
4966
|
+
function flattenRuleEntries(retObj, scope, code) {
|
|
4967
|
+
const out = [];
|
|
4968
|
+
for (const prop of retObj.properties) {
|
|
4969
|
+
const key = propKey(prop);
|
|
4970
|
+
if (!key) return null;
|
|
4971
|
+
out.push({ key, value: prop.value, scope, code });
|
|
4972
|
+
}
|
|
4973
|
+
return out;
|
|
4974
|
+
}
|
|
4975
|
+
function evalBodyStatements(statements, scope, code) {
|
|
4976
|
+
const bodyMfs = [];
|
|
4977
|
+
for (const stmt of statements) {
|
|
4978
|
+
for (const d of stmt.declarations) {
|
|
4979
|
+
const decl = d;
|
|
4980
|
+
if (!decl.init) return false;
|
|
4981
|
+
const id = decl.id;
|
|
4982
|
+
if (id.type !== "Identifier" && id.type !== "BindingIdentifier") return false;
|
|
4983
|
+
const name = id.name;
|
|
4984
|
+
const before = bodyMfs.length;
|
|
4985
|
+
const val = anyValue(decl.init, scope, code, bodyMfs);
|
|
4986
|
+
if (val === null) return false;
|
|
4987
|
+
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
4988
|
+
if (isCombinator(val)) scope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
4989
|
+
else scope.set(name, val);
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
return true;
|
|
4993
|
+
}
|
|
3865
4994
|
function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
3866
4995
|
if (factoryNode.type !== "ArrowFunctionExpression" && factoryNode.type !== "FunctionDeclaration" && factoryNode.type !== "FunctionExpression") return null;
|
|
3867
4996
|
const factory = factoryNode;
|
|
@@ -3894,40 +5023,41 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
|
3894
5023
|
if (!returnExpr) return null;
|
|
3895
5024
|
const retObj = returnExpr.type === "ParenthesizedExpression" ? returnExpr.expression : returnExpr;
|
|
3896
5025
|
if (retObj.type !== "ObjectExpression") return null;
|
|
5026
|
+
const keys = collectRuleKeys(retObj);
|
|
5027
|
+
if (!keys) return null;
|
|
3897
5028
|
const ruleRefs = /* @__PURE__ */ new Map();
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
5029
|
+
const tagRef = (r, name) => {
|
|
5030
|
+
r._ruleName = name;
|
|
5031
|
+
};
|
|
5032
|
+
for (const key of keys) {
|
|
5033
|
+
if (!ruleRefs.has(key)) {
|
|
5034
|
+
const r = ref();
|
|
5035
|
+
tagRef(r, key);
|
|
5036
|
+
ruleRefs.set(key, r);
|
|
5037
|
+
}
|
|
3905
5038
|
}
|
|
3906
|
-
const
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
const name = id.name;
|
|
3916
|
-
const before = bodyMfs.length;
|
|
3917
|
-
const val = anyValue(decl.init, localScope, code, bodyMfs);
|
|
3918
|
-
if (val === null) return null;
|
|
3919
|
-
const thisDeclMfSrcs = bodyMfs.slice(before);
|
|
3920
|
-
if (isCombinator(val)) {
|
|
3921
|
-
localScope.set(name, { combi: val, mfSrcs: thisDeclMfSrcs });
|
|
3922
|
-
} else {
|
|
3923
|
-
localScope.set(name, val);
|
|
5039
|
+
const externalRefs = /* @__PURE__ */ new Map();
|
|
5040
|
+
const gProxy = new Proxy(Object.fromEntries(ruleRefs), {
|
|
5041
|
+
get(target, prop) {
|
|
5042
|
+
if (typeof prop !== "string" || prop in target) return target[prop];
|
|
5043
|
+
let ext = externalRefs.get(prop);
|
|
5044
|
+
if (!ext) {
|
|
5045
|
+
ext = ref();
|
|
5046
|
+
tagRef(ext, prop);
|
|
5047
|
+
externalRefs.set(prop, ext);
|
|
3924
5048
|
}
|
|
5049
|
+
return ext;
|
|
3925
5050
|
}
|
|
3926
|
-
}
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
5051
|
+
});
|
|
5052
|
+
const localScope = new Map(scope);
|
|
5053
|
+
localScope.set(proxyName, gProxy);
|
|
5054
|
+
if (!evalBodyStatements(statements, localScope, code)) return null;
|
|
5055
|
+
const entries = flattenRuleEntries(retObj, localScope, code);
|
|
5056
|
+
if (!entries) return null;
|
|
5057
|
+
const finalByKey = /* @__PURE__ */ new Map();
|
|
5058
|
+
for (const e of entries) finalByKey.set(e.key, e);
|
|
5059
|
+
for (const [key, e] of finalByKey) {
|
|
5060
|
+
const val = anyValue(e.value, e.scope, e.code, mapFnSources);
|
|
3931
5061
|
if (!isCombinator(val)) return null;
|
|
3932
5062
|
ruleRefs.get(key).define(val);
|
|
3933
5063
|
}
|
|
@@ -3984,7 +5114,36 @@ function referencesAny(node2, names, scope) {
|
|
|
3984
5114
|
}
|
|
3985
5115
|
|
|
3986
5116
|
// src/plugin/index.ts
|
|
5117
|
+
import { createHash } from "node:crypto";
|
|
3987
5118
|
var PARSEMAN_MODULE = "parseman";
|
|
5119
|
+
var _compiledResolver = null;
|
|
5120
|
+
function getCompiledResolver() {
|
|
5121
|
+
return _compiledResolver ??= new ResolverFactory({
|
|
5122
|
+
extensions: [".js", ".mjs", ".cjs"],
|
|
5123
|
+
conditionNames: ["import", "require", "default"],
|
|
5124
|
+
mainFields: ["module", "main"]
|
|
5125
|
+
});
|
|
5126
|
+
}
|
|
5127
|
+
var _moduleParseCache = /* @__PURE__ */ new Map();
|
|
5128
|
+
function parseModuleCached(filePath) {
|
|
5129
|
+
let mtimeMs;
|
|
5130
|
+
try {
|
|
5131
|
+
mtimeMs = fs.statSync(filePath).mtimeMs;
|
|
5132
|
+
} catch {
|
|
5133
|
+
return null;
|
|
5134
|
+
}
|
|
5135
|
+
const hit = _moduleParseCache.get(filePath);
|
|
5136
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.parsed;
|
|
5137
|
+
let parsed = null;
|
|
5138
|
+
try {
|
|
5139
|
+
const src = fs.readFileSync(filePath, "utf8");
|
|
5140
|
+
const r = parseSync(filePath, src);
|
|
5141
|
+
if (r.errors.length === 0) parsed = { body: r.program.body, src };
|
|
5142
|
+
} catch {
|
|
5143
|
+
}
|
|
5144
|
+
_moduleParseCache.set(filePath, { mtimeMs, parsed });
|
|
5145
|
+
return parsed;
|
|
5146
|
+
}
|
|
3988
5147
|
var plugin_default = createUnplugin((opts = {}) => ({
|
|
3989
5148
|
name: "parseman",
|
|
3990
5149
|
// Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
|
|
@@ -4009,6 +5168,77 @@ var plugin_default = createUnplugin((opts = {}) => ({
|
|
|
4009
5168
|
return { code: result.code, map: result.map };
|
|
4010
5169
|
}
|
|
4011
5170
|
}));
|
|
5171
|
+
function exportLocalName(body, exported) {
|
|
5172
|
+
for (const st of body) {
|
|
5173
|
+
if (st.type !== "ExportNamedDeclaration") continue;
|
|
5174
|
+
const decl = st.declaration;
|
|
5175
|
+
if (decl?.type === "VariableDeclaration") {
|
|
5176
|
+
for (const d of decl.declarations ?? []) {
|
|
5177
|
+
const idn = d.id;
|
|
5178
|
+
if (idn?.type === "Identifier" && idn.name === exported) return exported;
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
for (const sp of st.specifiers ?? []) {
|
|
5182
|
+
const exp = sp.exported?.name;
|
|
5183
|
+
const loc = sp.local?.name;
|
|
5184
|
+
if (exp === exported && loc) return loc;
|
|
5185
|
+
}
|
|
5186
|
+
}
|
|
5187
|
+
return null;
|
|
5188
|
+
}
|
|
5189
|
+
function topLevelInit(body, name) {
|
|
5190
|
+
for (const st of body) {
|
|
5191
|
+
const vd = st.type === "VariableDeclaration" ? st : st.type === "ExportNamedDeclaration" && st.declaration?.type === "VariableDeclaration" ? st.declaration : null;
|
|
5192
|
+
if (!vd) continue;
|
|
5193
|
+
for (const d of vd.declarations ?? []) {
|
|
5194
|
+
const idn = d.id;
|
|
5195
|
+
if (idn?.type === "Identifier" && idn.name === name && d.init) return d.init;
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
return null;
|
|
5199
|
+
}
|
|
5200
|
+
function isComposedPiecesSymbol(n) {
|
|
5201
|
+
if (!n || n.type !== "CallExpression") return false;
|
|
5202
|
+
const callee = n.callee;
|
|
5203
|
+
const obj = callee?.object?.name;
|
|
5204
|
+
const prop = callee?.property?.name;
|
|
5205
|
+
const arg0 = n.arguments?.[0];
|
|
5206
|
+
return obj === "Symbol" && prop === "for" && arg0?.value === "parseman.composedPieces";
|
|
5207
|
+
}
|
|
5208
|
+
function findCarriedPiecesLiteral(root) {
|
|
5209
|
+
let found = null;
|
|
5210
|
+
const visit = (n) => {
|
|
5211
|
+
if (found || !n || typeof n !== "object") return;
|
|
5212
|
+
if (Array.isArray(n)) {
|
|
5213
|
+
for (const c of n) visit(c);
|
|
5214
|
+
return;
|
|
5215
|
+
}
|
|
5216
|
+
const node2 = n;
|
|
5217
|
+
if (node2.type === "CallExpression") {
|
|
5218
|
+
const callee = node2.callee;
|
|
5219
|
+
const obj = callee?.object?.name;
|
|
5220
|
+
const prop = callee?.property?.name;
|
|
5221
|
+
const args = node2.arguments;
|
|
5222
|
+
if (obj === "Object" && prop === "defineProperty" && args && isComposedPiecesSymbol(args[1])) {
|
|
5223
|
+
const descriptor = args[2];
|
|
5224
|
+
for (const p of descriptor?.properties ?? []) {
|
|
5225
|
+
const key = p.key;
|
|
5226
|
+
if ((key?.name === "value" || key?.value === "value") && p.value) {
|
|
5227
|
+
found = { start: p.value.start, end: p.value.end };
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
}
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
for (const k in node2) {
|
|
5234
|
+
if (k === "type" || k === "start" || k === "end") continue;
|
|
5235
|
+
const v2 = node2[k];
|
|
5236
|
+
if (v2 && typeof v2 === "object") visit(v2);
|
|
5237
|
+
}
|
|
5238
|
+
};
|
|
5239
|
+
visit(root);
|
|
5240
|
+
return found;
|
|
5241
|
+
}
|
|
4012
5242
|
function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSEMAN_MODULE])) {
|
|
4013
5243
|
let result;
|
|
4014
5244
|
try {
|
|
@@ -4020,15 +5250,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
4020
5250
|
const body = result.program.body;
|
|
4021
5251
|
const macroImports = [];
|
|
4022
5252
|
const allNames = /* @__PURE__ */ new Set();
|
|
5253
|
+
const importBindings = /* @__PURE__ */ new Map();
|
|
4023
5254
|
for (const stmt of body) {
|
|
4024
5255
|
if (stmt.type !== "ImportDeclaration") continue;
|
|
4025
5256
|
const s = stmt;
|
|
4026
|
-
|
|
4027
|
-
const isMacro = s.attributes.some((a) => {
|
|
5257
|
+
const isMacro = moduleAliases.has(s.source.value) && s.attributes.some((a) => {
|
|
4028
5258
|
const key = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
|
|
4029
5259
|
return key === "type" && a.value.value === "macro";
|
|
4030
5260
|
});
|
|
4031
|
-
if (!isMacro)
|
|
5261
|
+
if (!isMacro) {
|
|
5262
|
+
for (const spec of s.specifiers) {
|
|
5263
|
+
if (spec.type === "ImportSpecifier") {
|
|
5264
|
+
const imported = spec.imported.name ?? spec.local.name;
|
|
5265
|
+
importBindings.set(spec.local.name, { source: s.source.value, imported });
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
continue;
|
|
5269
|
+
}
|
|
4032
5270
|
const names = /* @__PURE__ */ new Set();
|
|
4033
5271
|
for (const spec of s.specifiers) {
|
|
4034
5272
|
if (spec.type === "ImportSpecifier") names.add(spec.local.name);
|
|
@@ -4071,6 +5309,89 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
4071
5309
|
return { replacement: compiled.replacement, ruleMap };
|
|
4072
5310
|
};
|
|
4073
5311
|
const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
|
|
5312
|
+
const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
|
|
5313
|
+
const localRuleMaps = /* @__PURE__ */ new Map();
|
|
5314
|
+
const nsFor = (label2) => `_${createHash("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
|
|
5315
|
+
const serializePieces = (p) => {
|
|
5316
|
+
const mapLit = (m) => `new Map([${[...m].map(([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(v2)}]`).join(", ")}])`;
|
|
5317
|
+
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: [] }`;
|
|
5318
|
+
};
|
|
5319
|
+
const serializeList = (list) => `[${list.map(serializePieces).join(", ")}]`;
|
|
5320
|
+
const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ (() => { const _g = ${grammarExpr}; Object.defineProperty(_g, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)} }); return _g })()`;
|
|
5321
|
+
const localComposedPieces = /* @__PURE__ */ new Map();
|
|
5322
|
+
const importedPiecesCache = /* @__PURE__ */ new Map();
|
|
5323
|
+
const importedPieces = (localName) => {
|
|
5324
|
+
if (importedPiecesCache.has(localName)) return importedPiecesCache.get(localName);
|
|
5325
|
+
let result2 = null;
|
|
5326
|
+
const binding = importBindings.get(localName);
|
|
5327
|
+
if (binding) {
|
|
5328
|
+
let file = null;
|
|
5329
|
+
try {
|
|
5330
|
+
file = getCompiledResolver().resolveFileSync(id, binding.source).path ?? null;
|
|
5331
|
+
} catch {
|
|
5332
|
+
}
|
|
5333
|
+
const mod = file ? parseModuleCached(file) : null;
|
|
5334
|
+
if (mod) {
|
|
5335
|
+
const localFor = exportLocalName(mod.body, binding.imported);
|
|
5336
|
+
const initNode = localFor ? topLevelInit(mod.body, localFor) : null;
|
|
5337
|
+
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null;
|
|
5338
|
+
if (literalRange) {
|
|
5339
|
+
const literal2 = mod.src.slice(literalRange.start, literalRange.end);
|
|
5340
|
+
try {
|
|
5341
|
+
result2 = new Function(`return (${literal2})`)();
|
|
5342
|
+
} catch {
|
|
5343
|
+
result2 = null;
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
5348
|
+
importedPiecesCache.set(localName, result2);
|
|
5349
|
+
return result2;
|
|
5350
|
+
};
|
|
5351
|
+
const argPieces = (arg, label2) => {
|
|
5352
|
+
if (isRulesCall(arg)) {
|
|
5353
|
+
const factory = arg.arguments[0];
|
|
5354
|
+
const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
|
|
5355
|
+
const p = rm ? compileLinkable([...rm], nsFor(label2)) : null;
|
|
5356
|
+
return p ? [p] : null;
|
|
5357
|
+
}
|
|
5358
|
+
if (arg.type === "Identifier") {
|
|
5359
|
+
const name = arg.name;
|
|
5360
|
+
const rm = localRuleMaps.get(name);
|
|
5361
|
+
if (rm) {
|
|
5362
|
+
const p = compileLinkable([...rm], nsFor(label2));
|
|
5363
|
+
return p ? [p] : null;
|
|
5364
|
+
}
|
|
5365
|
+
const composed = localComposedPieces.get(name);
|
|
5366
|
+
if (composed) return composed;
|
|
5367
|
+
return importedPieces(name);
|
|
5368
|
+
}
|
|
5369
|
+
return null;
|
|
5370
|
+
};
|
|
5371
|
+
const compileComposeCall = (init) => {
|
|
5372
|
+
const args = init.arguments;
|
|
5373
|
+
const arr = args[0];
|
|
5374
|
+
if (!arr || arr.type !== "ArrayExpression") {
|
|
5375
|
+
warn(init.start, "compose(): expected a static array of grammars/artifacts");
|
|
5376
|
+
return null;
|
|
5377
|
+
}
|
|
5378
|
+
const elements = arr.elements;
|
|
5379
|
+
const pieces = [];
|
|
5380
|
+
for (let i = 0; i < elements.length; i++) {
|
|
5381
|
+
const list = argPieces(elements[i], `compose${init.start}_${i}`);
|
|
5382
|
+
if (!list) {
|
|
5383
|
+
warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
|
|
5384
|
+
return null;
|
|
5385
|
+
}
|
|
5386
|
+
pieces.push(...list);
|
|
5387
|
+
}
|
|
5388
|
+
try {
|
|
5389
|
+
return { replacement: emitFusedSource(pieces), pieces };
|
|
5390
|
+
} catch (e) {
|
|
5391
|
+
warn(init.start, `compose(): ${e.message}; falling back to runtime`);
|
|
5392
|
+
return null;
|
|
5393
|
+
}
|
|
5394
|
+
};
|
|
4074
5395
|
const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
|
|
4075
5396
|
const refNames = /* @__PURE__ */ new Set();
|
|
4076
5397
|
for (const stmt of body) {
|
|
@@ -4149,11 +5470,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
4149
5470
|
if (isRulesCall(init)) {
|
|
4150
5471
|
const compiledRules = compileRulesFactory(init, varName);
|
|
4151
5472
|
if (!compiledRules) continue;
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
5473
|
+
localRuleMaps.set(varName, compiledRules.ruleMap);
|
|
5474
|
+
let replacement = compiledRules.replacement;
|
|
5475
|
+
if (exportPrefix) {
|
|
5476
|
+
const pieces = compileLinkable([...compiledRules.ruleMap], nsFor(varName));
|
|
5477
|
+
if (pieces && !pieces.mfFns.length && !pieces.buildFns.length) {
|
|
5478
|
+
replacement = withCarriedPieces(replacement, [pieces]);
|
|
5479
|
+
}
|
|
5480
|
+
}
|
|
5481
|
+
replacements.push({ start: init.start, end: init.end, replacement });
|
|
5482
|
+
continue;
|
|
5483
|
+
}
|
|
5484
|
+
if (isComposeCall(init)) {
|
|
5485
|
+
const fused = compileComposeCall(init);
|
|
5486
|
+
if (!fused) continue;
|
|
5487
|
+
localComposedPieces.set(varName, fused.pieces);
|
|
5488
|
+
const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.pieces) : fused.replacement;
|
|
5489
|
+
replacements.push({ start: init.start, end: init.end, replacement });
|
|
4157
5490
|
continue;
|
|
4158
5491
|
}
|
|
4159
5492
|
const mapFnSources = [];
|