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/index.js
CHANGED
|
@@ -251,6 +251,36 @@ function literal(value, opts = {}) {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
// src/combinators/regex.ts
|
|
254
|
+
var firstSetAnalyzer = null;
|
|
255
|
+
function registerRegexAnalyzer(analyzer) {
|
|
256
|
+
firstSetAnalyzer = analyzer;
|
|
257
|
+
}
|
|
258
|
+
var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
|
|
259
|
+
function regex(pattern, flags = "") {
|
|
260
|
+
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
261
|
+
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
262
|
+
const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
263
|
+
const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
|
|
264
|
+
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
265
|
+
return {
|
|
266
|
+
_tag: "regex",
|
|
267
|
+
_meta: meta,
|
|
268
|
+
_def: { tag: "regex", source, flags: resolvedFlags },
|
|
269
|
+
parse(input, pos, ctx) {
|
|
270
|
+
anchored.lastIndex = pos;
|
|
271
|
+
const m = anchored.exec(input);
|
|
272
|
+
if (m === null) {
|
|
273
|
+
return failAt(ctx, [`/${source}/`], pos);
|
|
274
|
+
}
|
|
275
|
+
const span = { start: pos, end: pos + m[0].length };
|
|
276
|
+
const leaf = { _tag: "leaf", value: m[0], span };
|
|
277
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
278
|
+
return { ok: true, value: m[0], span };
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/combinators/regex-analyze.ts
|
|
254
284
|
import regexpTree from "regexp-tree";
|
|
255
285
|
function firstSetFromRegex(pattern) {
|
|
256
286
|
try {
|
|
@@ -355,40 +385,6 @@ function extractFirstSet(node2) {
|
|
|
355
385
|
return { firstSet: any(), canMatchNewline: true };
|
|
356
386
|
}
|
|
357
387
|
}
|
|
358
|
-
function optimizeRegex(source, flags) {
|
|
359
|
-
try {
|
|
360
|
-
const result = regexpTree.optimize(`/${source}/${flags}`);
|
|
361
|
-
const str = result.toString();
|
|
362
|
-
const lastSlash = str.lastIndexOf("/");
|
|
363
|
-
return str.slice(1, lastSlash);
|
|
364
|
-
} catch {
|
|
365
|
-
return source;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
function regex(pattern, flags = "") {
|
|
369
|
-
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
370
|
-
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
371
|
-
const optimizedSource = optimizeRegex(source, resolvedFlags);
|
|
372
|
-
const anchored = new RegExp(optimizedSource, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
373
|
-
const { firstSet, canMatchNewline } = firstSetFromRegex(source);
|
|
374
|
-
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
375
|
-
return {
|
|
376
|
-
_tag: "regex",
|
|
377
|
-
_meta: meta,
|
|
378
|
-
_def: { tag: "regex", source, flags: resolvedFlags, optimizedSource },
|
|
379
|
-
parse(input, pos, ctx) {
|
|
380
|
-
anchored.lastIndex = pos;
|
|
381
|
-
const m = anchored.exec(input);
|
|
382
|
-
if (m === null) {
|
|
383
|
-
return failAt(ctx, [`/${source}/`], pos);
|
|
384
|
-
}
|
|
385
|
-
const span = { start: pos, end: pos + m[0].length };
|
|
386
|
-
const leaf = { _tag: "leaf", value: m[0], span };
|
|
387
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
388
|
-
return { ok: true, value: m[0], span };
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
388
|
|
|
393
389
|
// src/combinators/keywords.ts
|
|
394
390
|
function word(str, boundary = "_0-9A-Za-z") {
|
|
@@ -1086,7 +1082,9 @@ function rules(factory) {
|
|
|
1086
1082
|
if (typeof key !== "string") return void 0;
|
|
1087
1083
|
const record = target;
|
|
1088
1084
|
if (!(key in record)) {
|
|
1089
|
-
|
|
1085
|
+
const r = ref();
|
|
1086
|
+
r._ruleName = key;
|
|
1087
|
+
record[key] = r;
|
|
1090
1088
|
}
|
|
1091
1089
|
return record[key];
|
|
1092
1090
|
}
|
|
@@ -1151,12 +1149,14 @@ function confirmedBuildArity(src) {
|
|
|
1151
1149
|
return parts.length;
|
|
1152
1150
|
}
|
|
1153
1151
|
function buildReadsTrivia(def) {
|
|
1152
|
+
if (!def.build) return true;
|
|
1154
1153
|
const src = def.buildSrc ?? def.build.toString();
|
|
1155
1154
|
const arity = confirmedBuildArity(src);
|
|
1156
1155
|
if (arity === null) return true;
|
|
1157
1156
|
return arity >= 4;
|
|
1158
1157
|
}
|
|
1159
1158
|
function buildReadsState(def) {
|
|
1159
|
+
if (!def.build) return true;
|
|
1160
1160
|
const src = def.buildSrc ?? def.build.toString();
|
|
1161
1161
|
const arity = confirmedBuildArity(src);
|
|
1162
1162
|
if (arity === null) return true;
|
|
@@ -1175,8 +1175,8 @@ function node(type, combinator, build, opts) {
|
|
|
1175
1175
|
};
|
|
1176
1176
|
const collapse = opts?.collapse === true;
|
|
1177
1177
|
const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
|
|
1178
|
-
const capturesTrivia = buildReadsTrivia(def);
|
|
1179
|
-
const clonesState = buildReadsState(def);
|
|
1178
|
+
const capturesTrivia = build ? buildReadsTrivia(def) : true;
|
|
1179
|
+
const clonesState = build ? buildReadsState(def) : true;
|
|
1180
1180
|
return {
|
|
1181
1181
|
_tag: "node",
|
|
1182
1182
|
_meta: meta,
|
|
@@ -1187,13 +1187,8 @@ function node(type, combinator, build, opts) {
|
|
|
1187
1187
|
const r = combinator.parse(input, pos, ctx);
|
|
1188
1188
|
const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
1189
1189
|
if (!r.ok) return r;
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
rawChildren,
|
|
1193
|
-
r.span,
|
|
1194
|
-
triviaLog,
|
|
1195
|
-
clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
|
|
1196
|
-
);
|
|
1190
|
+
const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
1191
|
+
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 };
|
|
1197
1192
|
const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
|
|
1198
1193
|
const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
|
|
1199
1194
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
@@ -1368,8 +1363,14 @@ function staticExpected(c) {
|
|
|
1368
1363
|
case "transform":
|
|
1369
1364
|
case "not":
|
|
1370
1365
|
return staticExpected(def.parser);
|
|
1371
|
-
case "lazy":
|
|
1372
|
-
|
|
1366
|
+
case "lazy": {
|
|
1367
|
+
const name = c._ruleName;
|
|
1368
|
+
try {
|
|
1369
|
+
return staticExpected(def.thunk());
|
|
1370
|
+
} catch {
|
|
1371
|
+
return name ? [name] : [];
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1373
1374
|
default:
|
|
1374
1375
|
return [];
|
|
1375
1376
|
}
|
|
@@ -1399,8 +1400,29 @@ function expect(combinator, label2) {
|
|
|
1399
1400
|
var BACKSLASH = 92;
|
|
1400
1401
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1401
1402
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1403
|
+
var SPACE_RANGES = [
|
|
1404
|
+
[9, 13],
|
|
1405
|
+
[32, 32],
|
|
1406
|
+
[160, 160],
|
|
1407
|
+
[5760, 5760],
|
|
1408
|
+
[8192, 8202],
|
|
1409
|
+
[8232, 8232],
|
|
1410
|
+
[8233, 8233],
|
|
1411
|
+
[8239, 8239],
|
|
1412
|
+
[8287, 8287],
|
|
1413
|
+
[12288, 12288],
|
|
1414
|
+
[65279, 65279]
|
|
1415
|
+
];
|
|
1402
1416
|
function shorthandRanges(ch) {
|
|
1403
|
-
|
|
1417
|
+
if (ch === "d") return [[48, 57]];
|
|
1418
|
+
if (ch === "s") return SPACE_RANGES;
|
|
1419
|
+
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1420
|
+
}
|
|
1421
|
+
function readUnicodeEscape(body, i) {
|
|
1422
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
1423
|
+
const hex = body.slice(i + 2, i + 6);
|
|
1424
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
1425
|
+
return { cp: Number.parseInt(hex, 16), next: i + 6 };
|
|
1404
1426
|
}
|
|
1405
1427
|
function parseClassRanges(body) {
|
|
1406
1428
|
const ranges = [];
|
|
@@ -1409,11 +1431,16 @@ function parseClassRanges(body) {
|
|
|
1409
1431
|
const ch = body[i];
|
|
1410
1432
|
if (ch === void 0) return null;
|
|
1411
1433
|
if (ch === "\\") {
|
|
1434
|
+
const uni = readUnicodeEscape(body, i);
|
|
1435
|
+
if (uni) {
|
|
1436
|
+
i = uni.next;
|
|
1437
|
+
return { cp: uni.cp };
|
|
1438
|
+
}
|
|
1412
1439
|
const e = body[i + 1];
|
|
1413
1440
|
if (e === void 0) return null;
|
|
1414
1441
|
i += 2;
|
|
1415
1442
|
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1416
|
-
if (e === "d" || e === "w") return { set: shorthandRanges(e) };
|
|
1443
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
1417
1444
|
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1418
1445
|
return { cp: e.codePointAt(0) };
|
|
1419
1446
|
}
|
|
@@ -1441,10 +1468,16 @@ function parseClassRanges(body) {
|
|
|
1441
1468
|
function classToRanges(cls) {
|
|
1442
1469
|
if (cls === "\\d") return shorthandRanges("d");
|
|
1443
1470
|
if (cls === "\\w") return shorthandRanges("w");
|
|
1471
|
+
if (cls === "\\s") return shorthandRanges("s");
|
|
1444
1472
|
const body = cls.slice(1, -1);
|
|
1445
1473
|
if (body.startsWith("^")) return null;
|
|
1446
1474
|
return parseClassRanges(body);
|
|
1447
1475
|
}
|
|
1476
|
+
var foldEq = (cVar, cp) => {
|
|
1477
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
|
|
1478
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
|
|
1479
|
+
return `${cVar} === ${cp}`;
|
|
1480
|
+
};
|
|
1448
1481
|
function literalCodePoints(frag) {
|
|
1449
1482
|
const out = [];
|
|
1450
1483
|
let i = 0;
|
|
@@ -1491,22 +1524,612 @@ function parseStringShape(source) {
|
|
|
1491
1524
|
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1492
1525
|
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1493
1526
|
}
|
|
1527
|
+
function parseSeqParts(source) {
|
|
1528
|
+
const parts = [];
|
|
1529
|
+
let lit = [];
|
|
1530
|
+
const flush = () => {
|
|
1531
|
+
if (lit.length) {
|
|
1532
|
+
parts.push({ part: "lit", cps: lit, optional: false });
|
|
1533
|
+
lit = [];
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
const runFrom = (ranges, negated, q) => {
|
|
1537
|
+
if (q === "+") {
|
|
1538
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
|
|
1539
|
+
return 1;
|
|
1540
|
+
}
|
|
1541
|
+
if (q === "*") {
|
|
1542
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
|
|
1543
|
+
return 1;
|
|
1544
|
+
}
|
|
1545
|
+
if (q === "?") {
|
|
1546
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
|
|
1547
|
+
return 1;
|
|
1548
|
+
}
|
|
1549
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
|
|
1550
|
+
return 0;
|
|
1551
|
+
};
|
|
1552
|
+
let i = 0;
|
|
1553
|
+
while (i < source.length) {
|
|
1554
|
+
const ch = source[i];
|
|
1555
|
+
if (ch === "[") {
|
|
1556
|
+
let k = i + 1;
|
|
1557
|
+
const neg = source[k] === "^";
|
|
1558
|
+
if (neg) k++;
|
|
1559
|
+
let body = "";
|
|
1560
|
+
while (k < source.length && source[k] !== "]") {
|
|
1561
|
+
if (source[k] === "\\") {
|
|
1562
|
+
body += source[k] + (source[k + 1] ?? "");
|
|
1563
|
+
k += 2;
|
|
1564
|
+
} else {
|
|
1565
|
+
body += source[k];
|
|
1566
|
+
k++;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
if (source[k] !== "]") return null;
|
|
1570
|
+
const ranges = parseClassRanges(body);
|
|
1571
|
+
if (!ranges) return null;
|
|
1572
|
+
flush();
|
|
1573
|
+
i = k + 1;
|
|
1574
|
+
i += runFrom(ranges, neg, source[i]);
|
|
1575
|
+
continue;
|
|
1576
|
+
}
|
|
1577
|
+
if (ch === "\\") {
|
|
1578
|
+
const e = source[i + 1];
|
|
1579
|
+
if (e === "d" || e === "w" || e === "s") {
|
|
1580
|
+
flush();
|
|
1581
|
+
i += 2;
|
|
1582
|
+
i += runFrom(shorthandRanges(e), false, source[i]);
|
|
1583
|
+
continue;
|
|
1584
|
+
}
|
|
1585
|
+
const uni = readUnicodeEscape(source, i);
|
|
1586
|
+
let cp2;
|
|
1587
|
+
if (uni) {
|
|
1588
|
+
cp2 = uni.cp;
|
|
1589
|
+
i = uni.next;
|
|
1590
|
+
} else if (e === void 0) return null;
|
|
1591
|
+
else if (e in CLASS_ESCAPES) {
|
|
1592
|
+
cp2 = CLASS_ESCAPES[e];
|
|
1593
|
+
i += 2;
|
|
1594
|
+
} else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1595
|
+
else {
|
|
1596
|
+
cp2 = e.codePointAt(0);
|
|
1597
|
+
i += 2;
|
|
1598
|
+
}
|
|
1599
|
+
if (source[i] === "?") {
|
|
1600
|
+
flush();
|
|
1601
|
+
parts.push({ part: "lit", cps: [cp2], optional: true });
|
|
1602
|
+
i++;
|
|
1603
|
+
} else lit.push(cp2);
|
|
1604
|
+
continue;
|
|
1605
|
+
}
|
|
1606
|
+
if (ch === "(") {
|
|
1607
|
+
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
1608
|
+
let k = i + 3;
|
|
1609
|
+
let depth = 1;
|
|
1610
|
+
while (k < source.length && depth > 0) {
|
|
1611
|
+
const c2 = source[k];
|
|
1612
|
+
if (c2 === "\\") {
|
|
1613
|
+
k += 2;
|
|
1614
|
+
continue;
|
|
1615
|
+
}
|
|
1616
|
+
if (c2 === "[") {
|
|
1617
|
+
let j = k + 1;
|
|
1618
|
+
if (source[j] === "^") j++;
|
|
1619
|
+
while (j < source.length && source[j] !== "]") {
|
|
1620
|
+
if (source[j] === "\\") j += 2;
|
|
1621
|
+
else j++;
|
|
1622
|
+
}
|
|
1623
|
+
if (source[j] !== "]") return null;
|
|
1624
|
+
k = j + 1;
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
if (c2 === "(") depth++;
|
|
1628
|
+
else if (c2 === ")") depth--;
|
|
1629
|
+
k++;
|
|
1630
|
+
}
|
|
1631
|
+
if (depth !== 0) return null;
|
|
1632
|
+
const body = source.slice(i + 3, k - 1);
|
|
1633
|
+
const inner = parseScanShape(body);
|
|
1634
|
+
if (!inner || !groupInnerSafe(inner)) return null;
|
|
1635
|
+
const q = source[k];
|
|
1636
|
+
if (q === "{") return null;
|
|
1637
|
+
flush();
|
|
1638
|
+
if (q === "+") {
|
|
1639
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1640
|
+
parts.push({ part: "group", inner, min: 1, unbounded: true });
|
|
1641
|
+
i = k + 1;
|
|
1642
|
+
} else if (q === "*") {
|
|
1643
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1644
|
+
parts.push({ part: "group", inner, min: 0, unbounded: true });
|
|
1645
|
+
i = k + 1;
|
|
1646
|
+
} else if (q === "?") {
|
|
1647
|
+
parts.push({ part: "group", inner, min: 0, unbounded: false });
|
|
1648
|
+
i = k + 1;
|
|
1649
|
+
} else {
|
|
1650
|
+
parts.push({ part: "group", inner, min: 1, unbounded: false });
|
|
1651
|
+
i = k;
|
|
1652
|
+
}
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
if (META.has(ch)) return null;
|
|
1656
|
+
const cp = ch.codePointAt(0);
|
|
1657
|
+
i += ch.length;
|
|
1658
|
+
if (source[i] === "?") {
|
|
1659
|
+
flush();
|
|
1660
|
+
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
1661
|
+
i++;
|
|
1662
|
+
} else lit.push(cp);
|
|
1663
|
+
}
|
|
1664
|
+
flush();
|
|
1665
|
+
return parts.length ? parts : null;
|
|
1666
|
+
}
|
|
1667
|
+
function seqIsUnambiguous(parts) {
|
|
1668
|
+
let hasMandatory = false;
|
|
1669
|
+
for (let k = 0; k < parts.length; k++) {
|
|
1670
|
+
const p = parts[k];
|
|
1671
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1672
|
+
if (mandatory) hasMandatory = true;
|
|
1673
|
+
if (p.part === "group") {
|
|
1674
|
+
const exposure = groupPartExposure(p);
|
|
1675
|
+
if (exposure === "unsupported") {
|
|
1676
|
+
const trailingOnce = k === parts.length - 1 && p.min === 1 && !p.unbounded;
|
|
1677
|
+
if (!trailingOnce) return false;
|
|
1678
|
+
} else if (exposure !== null) {
|
|
1679
|
+
if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1683
|
+
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1684
|
+
if (skippableSingle || isUnboundedRepeat) {
|
|
1685
|
+
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
1686
|
+
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
return hasMandatory;
|
|
1690
|
+
}
|
|
1691
|
+
function rangesOverlap(a, b) {
|
|
1692
|
+
return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
|
|
1693
|
+
}
|
|
1694
|
+
function rangesSubset(sub, sup) {
|
|
1695
|
+
return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
|
|
1696
|
+
}
|
|
1697
|
+
function parseClassOperand(body) {
|
|
1698
|
+
if (body === "\\d" || body === "\\w" || body === "\\s") {
|
|
1699
|
+
return { ranges: shorthandRanges(body[1]), negated: false };
|
|
1700
|
+
}
|
|
1701
|
+
if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
|
|
1702
|
+
let inner = body.slice(1, -1);
|
|
1703
|
+
const negated = inner.startsWith("^");
|
|
1704
|
+
if (negated) inner = inner.slice(1);
|
|
1705
|
+
const ranges = parseClassRanges(inner);
|
|
1706
|
+
return ranges ? { ranges, negated } : null;
|
|
1707
|
+
}
|
|
1708
|
+
const cps = literalCodePoints(body);
|
|
1709
|
+
if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
function classSubset(t, u) {
|
|
1713
|
+
if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1714
|
+
if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1715
|
+
if (t.negated && !u.negated) return false;
|
|
1716
|
+
return rangesSubset(u.ranges, t.ranges);
|
|
1717
|
+
}
|
|
1718
|
+
function classDisjoint(t, u) {
|
|
1719
|
+
if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1720
|
+
if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1721
|
+
if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
|
|
1722
|
+
return false;
|
|
1723
|
+
}
|
|
1724
|
+
function firstSetsDisjoint(a, b) {
|
|
1725
|
+
if (a === "any" || b === "any") return false;
|
|
1726
|
+
return classDisjoint(a, b);
|
|
1727
|
+
}
|
|
1728
|
+
function fixedClassSeq(shape) {
|
|
1729
|
+
if (shape.kind !== "seq") return null;
|
|
1730
|
+
const out = [];
|
|
1731
|
+
for (const p of shape.parts) {
|
|
1732
|
+
if (p.part === "lit") {
|
|
1733
|
+
if (p.optional) return null;
|
|
1734
|
+
for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
|
|
1735
|
+
} else if (p.part === "run" && !p.unbounded && p.min === 1) {
|
|
1736
|
+
out.push({ ranges: p.ranges, negated: p.negated });
|
|
1737
|
+
} else {
|
|
1738
|
+
return null;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
return out.length ? out : null;
|
|
1742
|
+
}
|
|
1743
|
+
function altFixedMutuallyExclusive(shape) {
|
|
1744
|
+
if (shape.kind !== "alt" || shape.disjoint) return false;
|
|
1745
|
+
const lists = [];
|
|
1746
|
+
for (const arm of shape.arms) {
|
|
1747
|
+
const f = fixedClassSeq(arm);
|
|
1748
|
+
if (!f) return false;
|
|
1749
|
+
lists.push(f);
|
|
1750
|
+
}
|
|
1751
|
+
for (let i = 0; i < lists.length; i++) {
|
|
1752
|
+
for (let j = 0; j < lists.length; j++) {
|
|
1753
|
+
if (i === j) continue;
|
|
1754
|
+
const a = lists[i];
|
|
1755
|
+
const b = lists[j];
|
|
1756
|
+
if (a.length > b.length) continue;
|
|
1757
|
+
let disjointAt = false;
|
|
1758
|
+
for (let p = 0; p < a.length; p++) {
|
|
1759
|
+
if (classDisjoint(a[p], b[p])) {
|
|
1760
|
+
disjointAt = true;
|
|
1761
|
+
break;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
if (!disjointAt) return false;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return true;
|
|
1768
|
+
}
|
|
1769
|
+
function groupPartExposure(part) {
|
|
1770
|
+
const innerWiggle = trailingBacktrackClass(part.inner);
|
|
1771
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1772
|
+
const requiredOnce = !part.unbounded && part.min === 1;
|
|
1773
|
+
if (requiredOnce) return innerWiggle;
|
|
1774
|
+
const dropExposed = shapeFirstAccept(part.inner);
|
|
1775
|
+
if (dropExposed === "any") return "unsupported";
|
|
1776
|
+
if (innerWiggle === null) return dropExposed;
|
|
1777
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1778
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1779
|
+
}
|
|
1780
|
+
function trailingBacktrackClass(shape) {
|
|
1781
|
+
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1782
|
+
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
1783
|
+
if (shape.kind === "litFold") return null;
|
|
1784
|
+
if (shape.kind === "seq") {
|
|
1785
|
+
const last = shape.parts[shape.parts.length - 1];
|
|
1786
|
+
if (last.part === "lit") return null;
|
|
1787
|
+
if (last.part === "run") {
|
|
1788
|
+
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1789
|
+
return null;
|
|
1790
|
+
}
|
|
1791
|
+
return groupPartExposure(last);
|
|
1792
|
+
}
|
|
1793
|
+
if (shape.kind === "alt") {
|
|
1794
|
+
if (!shape.disjoint) return altFixedMutuallyExclusive(shape) ? null : "unsupported";
|
|
1795
|
+
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1796
|
+
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1797
|
+
const concrete = classes.filter((c) => c !== null);
|
|
1798
|
+
if (concrete.length === 0) return null;
|
|
1799
|
+
const unioned = unionCharSets(concrete);
|
|
1800
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1801
|
+
}
|
|
1802
|
+
return "unsupported";
|
|
1803
|
+
}
|
|
1804
|
+
function lookaheadUnambiguous(inner, operand, negative) {
|
|
1805
|
+
const tail = trailingBacktrackClass(inner);
|
|
1806
|
+
if (tail === "unsupported") return false;
|
|
1807
|
+
if (tail === null) return true;
|
|
1808
|
+
return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
|
|
1809
|
+
}
|
|
1810
|
+
function stripTrailingLookahead(source) {
|
|
1811
|
+
if (!source.endsWith(")")) return null;
|
|
1812
|
+
for (const negative of [true, false]) {
|
|
1813
|
+
const lead = negative ? "(?!" : "(?=";
|
|
1814
|
+
const idx = source.lastIndexOf(lead);
|
|
1815
|
+
if (idx === -1) continue;
|
|
1816
|
+
const body = source.slice(idx + lead.length, -1);
|
|
1817
|
+
const operand = body.length ? parseClassOperand(body) : null;
|
|
1818
|
+
if (!operand) continue;
|
|
1819
|
+
return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
|
|
1820
|
+
}
|
|
1821
|
+
return null;
|
|
1822
|
+
}
|
|
1823
|
+
function unwrapOuterGroup(source) {
|
|
1824
|
+
while (source.startsWith("(?:")) {
|
|
1825
|
+
let depth = 1;
|
|
1826
|
+
let i = 3;
|
|
1827
|
+
let ok = true;
|
|
1828
|
+
while (i < source.length && depth > 0) {
|
|
1829
|
+
const ch = source[i];
|
|
1830
|
+
if (ch === "\\") {
|
|
1831
|
+
i += 2;
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
if (ch === "[") {
|
|
1835
|
+
let j = i + 1;
|
|
1836
|
+
if (source[j] === "^") j++;
|
|
1837
|
+
while (j < source.length && source[j] !== "]") {
|
|
1838
|
+
if (source[j] === "\\") j += 2;
|
|
1839
|
+
else j++;
|
|
1840
|
+
}
|
|
1841
|
+
if (source[j] !== "]") {
|
|
1842
|
+
ok = false;
|
|
1843
|
+
break;
|
|
1844
|
+
}
|
|
1845
|
+
i = j + 1;
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
if (ch === "(") depth++;
|
|
1849
|
+
else if (ch === ")") depth--;
|
|
1850
|
+
i++;
|
|
1851
|
+
}
|
|
1852
|
+
if (!ok || depth !== 0 || i !== source.length) break;
|
|
1853
|
+
source = source.slice(3, -1);
|
|
1854
|
+
}
|
|
1855
|
+
return source;
|
|
1856
|
+
}
|
|
1857
|
+
function splitTopLevelAlternation(source) {
|
|
1858
|
+
const body = unwrapOuterGroup(source);
|
|
1859
|
+
const parts = [];
|
|
1860
|
+
let depth = 0;
|
|
1861
|
+
let last = 0;
|
|
1862
|
+
let i = 0;
|
|
1863
|
+
while (i < body.length) {
|
|
1864
|
+
const ch = body[i];
|
|
1865
|
+
if (ch === "\\") {
|
|
1866
|
+
i += 2;
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
if (ch === "[") {
|
|
1870
|
+
let j = i + 1;
|
|
1871
|
+
if (body[j] === "^") j++;
|
|
1872
|
+
while (j < body.length && body[j] !== "]") {
|
|
1873
|
+
if (body[j] === "\\") j += 2;
|
|
1874
|
+
else j++;
|
|
1875
|
+
}
|
|
1876
|
+
if (body[j] !== "]") return null;
|
|
1877
|
+
i = j + 1;
|
|
1878
|
+
continue;
|
|
1879
|
+
}
|
|
1880
|
+
if (ch === "(") {
|
|
1881
|
+
depth++;
|
|
1882
|
+
i++;
|
|
1883
|
+
continue;
|
|
1884
|
+
}
|
|
1885
|
+
if (ch === ")") {
|
|
1886
|
+
depth--;
|
|
1887
|
+
if (depth < 0) return null;
|
|
1888
|
+
i++;
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
if (ch === "|" && depth === 0) {
|
|
1892
|
+
parts.push(body.slice(last, i));
|
|
1893
|
+
last = i + 1;
|
|
1894
|
+
i++;
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
i++;
|
|
1898
|
+
}
|
|
1899
|
+
if (depth !== 0) return null;
|
|
1900
|
+
parts.push(body.slice(last));
|
|
1901
|
+
if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
|
|
1902
|
+
return parts;
|
|
1903
|
+
}
|
|
1904
|
+
function shapeCanBeEmpty(shape) {
|
|
1905
|
+
if (shape.kind === "chars") return !shape.minOne;
|
|
1906
|
+
if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
|
|
1907
|
+
if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
|
|
1908
|
+
return false;
|
|
1909
|
+
}
|
|
1910
|
+
function unionCharSets(sets) {
|
|
1911
|
+
if (sets.some((s) => s === "any")) return "any";
|
|
1912
|
+
const concrete = sets;
|
|
1913
|
+
if (concrete.length === 1) return concrete[0];
|
|
1914
|
+
if (concrete.some((s) => s.negated)) return "any";
|
|
1915
|
+
return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
|
|
1916
|
+
}
|
|
1917
|
+
function partFirstAccept(part) {
|
|
1918
|
+
if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
|
|
1919
|
+
if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
|
|
1920
|
+
return shapeFirstAccept(part.inner);
|
|
1921
|
+
}
|
|
1922
|
+
function seqFirstAccept(parts) {
|
|
1923
|
+
const sets = [];
|
|
1924
|
+
for (const p of parts) {
|
|
1925
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1926
|
+
sets.push(partFirstAccept(p));
|
|
1927
|
+
if (mandatory) break;
|
|
1928
|
+
}
|
|
1929
|
+
return unionCharSets(sets);
|
|
1930
|
+
}
|
|
1931
|
+
function groupInnerSafe(shape) {
|
|
1932
|
+
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1933
|
+
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1934
|
+
if (shape.kind === "alt") return true;
|
|
1935
|
+
return false;
|
|
1936
|
+
}
|
|
1937
|
+
function shapeFirstAccept(shape) {
|
|
1938
|
+
if (shapeCanBeEmpty(shape)) return "any";
|
|
1939
|
+
switch (shape.kind) {
|
|
1940
|
+
case "chars":
|
|
1941
|
+
return { ranges: shape.ranges, negated: false };
|
|
1942
|
+
case "ident":
|
|
1943
|
+
return { ranges: shape.head, negated: false };
|
|
1944
|
+
case "until":
|
|
1945
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1946
|
+
case "delimited":
|
|
1947
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
1948
|
+
case "string":
|
|
1949
|
+
return { ranges: [[shape.quote, shape.quote]], negated: false };
|
|
1950
|
+
case "litFold": {
|
|
1951
|
+
const cp = shape.open[0];
|
|
1952
|
+
if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
|
|
1953
|
+
if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
|
|
1954
|
+
return { ranges: [[cp, cp]], negated: false };
|
|
1955
|
+
}
|
|
1956
|
+
case "seq":
|
|
1957
|
+
return seqFirstAccept(shape.parts);
|
|
1958
|
+
case "lookahead":
|
|
1959
|
+
return shapeFirstAccept(shape.inner);
|
|
1960
|
+
// zero-width — doesn't change first-char acceptance
|
|
1961
|
+
case "alt": {
|
|
1962
|
+
const sets = shape.firsts.map((f) => f === null ? "any" : f);
|
|
1963
|
+
return unionCharSets(sets);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
function allPairsDisjoint(sets) {
|
|
1968
|
+
for (let i = 0; i < sets.length; i++) {
|
|
1969
|
+
const a = sets[i];
|
|
1970
|
+
if (a === "any") return false;
|
|
1971
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
1972
|
+
const b = sets[j];
|
|
1973
|
+
if (b === "any" || !classDisjoint(a, b)) return false;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
return true;
|
|
1977
|
+
}
|
|
1978
|
+
function parseAlternation(source) {
|
|
1979
|
+
const parts = splitTopLevelAlternation(source);
|
|
1980
|
+
if (!parts) return null;
|
|
1981
|
+
const arms = [];
|
|
1982
|
+
for (const p of parts) {
|
|
1983
|
+
const arm = parseScanShape(p);
|
|
1984
|
+
if (!arm) return null;
|
|
1985
|
+
arms.push(arm);
|
|
1986
|
+
}
|
|
1987
|
+
const firstSets = arms.map(shapeFirstAccept);
|
|
1988
|
+
const disjoint = allPairsDisjoint(firstSets);
|
|
1989
|
+
return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
|
|
1990
|
+
}
|
|
1494
1991
|
function parseScanShape(source) {
|
|
1992
|
+
const alt = parseAlternation(source);
|
|
1993
|
+
if (alt) return alt;
|
|
1994
|
+
const la = stripTrailingLookahead(source);
|
|
1995
|
+
if (la) {
|
|
1996
|
+
const inner = parseScanShapeCore(la.base);
|
|
1997
|
+
if (!inner) return null;
|
|
1998
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
1999
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2000
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2001
|
+
}
|
|
2002
|
+
return parseScanShapeCore(source);
|
|
2003
|
+
}
|
|
2004
|
+
function splitDelimArms(body) {
|
|
2005
|
+
const arms = [];
|
|
2006
|
+
let depth = 0;
|
|
2007
|
+
let last = 0;
|
|
2008
|
+
let i = 0;
|
|
2009
|
+
while (i < body.length) {
|
|
2010
|
+
const ch = body[i];
|
|
2011
|
+
if (ch === "\\") {
|
|
2012
|
+
i += 2;
|
|
2013
|
+
continue;
|
|
2014
|
+
}
|
|
2015
|
+
if (ch === "[") {
|
|
2016
|
+
let j = i + 1;
|
|
2017
|
+
if (body[j] === "^") j++;
|
|
2018
|
+
while (j < body.length && body[j] !== "]") {
|
|
2019
|
+
if (body[j] === "\\") j += 2;
|
|
2020
|
+
else j++;
|
|
2021
|
+
}
|
|
2022
|
+
if (body[j] !== "]") return null;
|
|
2023
|
+
i = j + 1;
|
|
2024
|
+
continue;
|
|
2025
|
+
}
|
|
2026
|
+
if (ch === "(") {
|
|
2027
|
+
depth++;
|
|
2028
|
+
i++;
|
|
2029
|
+
continue;
|
|
2030
|
+
}
|
|
2031
|
+
if (ch === ")") {
|
|
2032
|
+
depth--;
|
|
2033
|
+
if (depth < 0) return null;
|
|
2034
|
+
i++;
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
if (ch === "|" && depth === 0) {
|
|
2038
|
+
arms.push(body.slice(last, i));
|
|
2039
|
+
last = i + 1;
|
|
2040
|
+
}
|
|
2041
|
+
i++;
|
|
2042
|
+
}
|
|
2043
|
+
if (depth !== 0) return null;
|
|
2044
|
+
arms.push(body.slice(last));
|
|
2045
|
+
return arms;
|
|
2046
|
+
}
|
|
2047
|
+
function delimitedBodySound(body, close) {
|
|
2048
|
+
const l0 = close[0];
|
|
2049
|
+
if (close.length > 2) return false;
|
|
2050
|
+
const arms = splitDelimArms(body);
|
|
2051
|
+
if (!arms) return false;
|
|
2052
|
+
let bulkSeen = false;
|
|
2053
|
+
let guardSeen = false;
|
|
2054
|
+
for (const arm of arms) {
|
|
2055
|
+
const bulk = /^\[\^((?:\\.|[^\]])+)\]$/.exec(arm);
|
|
2056
|
+
if (bulk) {
|
|
2057
|
+
if (bulkSeen) return false;
|
|
2058
|
+
const ranges = parseClassRanges(bulk[1]);
|
|
2059
|
+
if (!ranges || ranges.length !== 1 || ranges[0][0] !== l0 || ranges[0][1] !== l0) return false;
|
|
2060
|
+
bulkSeen = true;
|
|
2061
|
+
continue;
|
|
2062
|
+
}
|
|
2063
|
+
if (close.length === 2 && arm.endsWith(")")) {
|
|
2064
|
+
const idx = arm.indexOf("(?!");
|
|
2065
|
+
if (idx === -1) return false;
|
|
2066
|
+
const prefix = literalCodePoints(arm.slice(0, idx));
|
|
2067
|
+
const op = parseClassOperand(arm.slice(idx + 3, -1));
|
|
2068
|
+
const prefixIsL0 = prefix?.length === 1 && prefix[0] === l0;
|
|
2069
|
+
const forbidsL1 = op && !op.negated && op.ranges.length === 1 && op.ranges[0][0] === close[1] && op.ranges[0][1] === close[1];
|
|
2070
|
+
if (!prefixIsL0 || !forbidsL1 || guardSeen) return false;
|
|
2071
|
+
guardSeen = true;
|
|
2072
|
+
continue;
|
|
2073
|
+
}
|
|
2074
|
+
return false;
|
|
2075
|
+
}
|
|
2076
|
+
return bulkSeen && guardSeen === (close.length === 2);
|
|
2077
|
+
}
|
|
2078
|
+
function parseDelimited(source) {
|
|
2079
|
+
if (source.includes("\\\\")) return null;
|
|
2080
|
+
let i = 0;
|
|
2081
|
+
while (i < source.length && source[i] !== "(") {
|
|
2082
|
+
i += source[i] === "\\" ? 2 : 1;
|
|
2083
|
+
}
|
|
2084
|
+
if (source[i] !== "(" || source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
2085
|
+
const openFrag = source.slice(0, i);
|
|
2086
|
+
let k = i + 3;
|
|
2087
|
+
let depth = 1;
|
|
2088
|
+
while (k < source.length && depth > 0) {
|
|
2089
|
+
const ch = source[k];
|
|
2090
|
+
if (ch === "\\") {
|
|
2091
|
+
k += 2;
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
if (ch === "[") {
|
|
2095
|
+
let j = k + 1;
|
|
2096
|
+
if (source[j] === "^") j++;
|
|
2097
|
+
while (j < source.length && source[j] !== "]") {
|
|
2098
|
+
if (source[j] === "\\") j += 2;
|
|
2099
|
+
else j++;
|
|
2100
|
+
}
|
|
2101
|
+
if (source[j] !== "]") return null;
|
|
2102
|
+
k = j + 1;
|
|
2103
|
+
continue;
|
|
2104
|
+
}
|
|
2105
|
+
if (ch === "(") depth++;
|
|
2106
|
+
else if (ch === ")") depth--;
|
|
2107
|
+
k++;
|
|
2108
|
+
}
|
|
2109
|
+
if (depth !== 0) return null;
|
|
2110
|
+
const body = source.slice(i + 3, k - 1);
|
|
2111
|
+
if (source[k] !== "*") return null;
|
|
2112
|
+
const closeFrag = source.slice(k + 1);
|
|
2113
|
+
const open = literalCodePoints(openFrag);
|
|
2114
|
+
const close = literalCodePoints(closeFrag);
|
|
2115
|
+
if (!open || !close || !delimitedBodySound(body, close)) return null;
|
|
2116
|
+
return { kind: "delimited", open, close };
|
|
2117
|
+
}
|
|
2118
|
+
function parseScanShapeCore(source) {
|
|
1495
2119
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1496
|
-
if (m) {
|
|
1497
|
-
if (m[1].startsWith("^")) return null;
|
|
2120
|
+
if (m && !m[1].startsWith("^")) {
|
|
1498
2121
|
const ranges = parseClassRanges(m[1]);
|
|
1499
2122
|
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1500
2123
|
}
|
|
1501
|
-
m = /^\\([
|
|
2124
|
+
m = /^\\([dws])([+*])$/.exec(source);
|
|
1502
2125
|
if (m) {
|
|
1503
2126
|
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1504
2127
|
}
|
|
1505
|
-
m = /^(\[(?:\\.|[^\]])+\]|\\[
|
|
2128
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
|
|
1506
2129
|
if (m) {
|
|
1507
2130
|
const head = classToRanges(m[1]);
|
|
1508
2131
|
const tail = classToRanges(m[2]);
|
|
1509
|
-
|
|
2132
|
+
if (head && tail) return { kind: "ident", head, tail };
|
|
1510
2133
|
}
|
|
1511
2134
|
const str = parseStringShape(source);
|
|
1512
2135
|
if (str) return str;
|
|
@@ -1515,20 +2138,19 @@ function parseScanShape(source) {
|
|
|
1515
2138
|
const open = literalCodePoints(m[1]);
|
|
1516
2139
|
const stop = parseClassRanges(m[2]);
|
|
1517
2140
|
if (open && stop) return { kind: "until", open, stop };
|
|
1518
|
-
return null;
|
|
1519
|
-
}
|
|
1520
|
-
if (!source.includes("\\\\")) {
|
|
1521
|
-
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
1522
|
-
if (m && m[1] && m[2]) {
|
|
1523
|
-
const open = literalCodePoints(m[1]);
|
|
1524
|
-
const close = literalCodePoints(m[2]);
|
|
1525
|
-
if (open && close) return { kind: "delimited", open, close };
|
|
1526
|
-
}
|
|
1527
2141
|
}
|
|
2142
|
+
const delim = parseDelimited(source);
|
|
2143
|
+
if (delim) return delim;
|
|
2144
|
+
const parts = parseSeqParts(source);
|
|
2145
|
+
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1528
2146
|
return null;
|
|
1529
2147
|
}
|
|
1530
2148
|
function scanShapeFromRegex(source, flags) {
|
|
1531
|
-
if (/[
|
|
2149
|
+
if (/[msu]/.test(flags)) return null;
|
|
2150
|
+
if (/i/.test(flags)) {
|
|
2151
|
+
const open = literalCodePoints(source);
|
|
2152
|
+
return open ? { kind: "litFold", open } : null;
|
|
2153
|
+
}
|
|
1532
2154
|
return parseScanShape(source);
|
|
1533
2155
|
}
|
|
1534
2156
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
@@ -1543,6 +2165,53 @@ function stringHardStop(shape) {
|
|
|
1543
2165
|
}
|
|
1544
2166
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1545
2167
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2168
|
+
if (shape.kind === "lookahead") {
|
|
2169
|
+
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
2170
|
+
const okV = mint("_ok");
|
|
2171
|
+
const endV2 = mint("_end");
|
|
2172
|
+
const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
|
|
2173
|
+
const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
|
|
2174
|
+
const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
|
|
2175
|
+
const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
|
|
2176
|
+
return {
|
|
2177
|
+
setup: [
|
|
2178
|
+
...inner.setup,
|
|
2179
|
+
`${ind2}let ${okV} = ${inner.ok}`,
|
|
2180
|
+
`${ind2}let ${endV2} = ${inner.end}`,
|
|
2181
|
+
`${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
|
|
2182
|
+
],
|
|
2183
|
+
ok: okV,
|
|
2184
|
+
end: endV2
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
if (shape.kind === "alt") {
|
|
2188
|
+
const okV = mint("_ok");
|
|
2189
|
+
const endV2 = mint("_end");
|
|
2190
|
+
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2191
|
+
if (shape.disjoint) {
|
|
2192
|
+
const c0 = codeAt(start, firstChar);
|
|
2193
|
+
shape.arms.forEach((arm, k) => {
|
|
2194
|
+
const fs = shape.firsts[k];
|
|
2195
|
+
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
2196
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2197
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2198
|
+
lines.push(...m.setup);
|
|
2199
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2200
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2201
|
+
lines.push(`${ind2}}`);
|
|
2202
|
+
});
|
|
2203
|
+
} else {
|
|
2204
|
+
const lbl = mint("_alt");
|
|
2205
|
+
lines.push(`${ind2}${lbl}: {`);
|
|
2206
|
+
for (const arm of shape.arms) {
|
|
2207
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2208
|
+
lines.push(...m.setup);
|
|
2209
|
+
lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
2210
|
+
}
|
|
2211
|
+
lines.push(`${ind2}}`);
|
|
2212
|
+
}
|
|
2213
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2214
|
+
}
|
|
1546
2215
|
if (shape.kind === "chars") {
|
|
1547
2216
|
const cur = mint("_e");
|
|
1548
2217
|
return {
|
|
@@ -1569,6 +2238,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
1569
2238
|
end: cur
|
|
1570
2239
|
};
|
|
1571
2240
|
}
|
|
2241
|
+
if (shape.kind === "seq") {
|
|
2242
|
+
const endV2 = mint("_e");
|
|
2243
|
+
const okV = mint("_ok");
|
|
2244
|
+
const runCond = (p, at) => {
|
|
2245
|
+
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
2246
|
+
return p.negated ? `!(${c})` : `(${c})`;
|
|
2247
|
+
};
|
|
2248
|
+
const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
|
|
2249
|
+
let first = true;
|
|
2250
|
+
for (const p of shape.parts) {
|
|
2251
|
+
if (p.part === "lit") {
|
|
2252
|
+
const L = p.cps.length;
|
|
2253
|
+
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
2254
|
+
if (p.optional) {
|
|
2255
|
+
lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
2256
|
+
} else {
|
|
2257
|
+
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
2258
|
+
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
2259
|
+
}
|
|
2260
|
+
} else if (p.part === "run" && !p.unbounded) {
|
|
2261
|
+
if (p.min === 1) {
|
|
2262
|
+
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
2263
|
+
lines.push(`${ind2} ${endV2}++`);
|
|
2264
|
+
} else {
|
|
2265
|
+
lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2266
|
+
}
|
|
2267
|
+
} else if (p.part === "run") {
|
|
2268
|
+
const s = mint("_s");
|
|
2269
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
2270
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2271
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
2272
|
+
} else {
|
|
2273
|
+
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
2274
|
+
if (!p.unbounded) {
|
|
2275
|
+
lines.push(...inner.setup);
|
|
2276
|
+
if (p.min === 1) {
|
|
2277
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2278
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2279
|
+
} else {
|
|
2280
|
+
lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
2281
|
+
}
|
|
2282
|
+
} else {
|
|
2283
|
+
const progV = mint("_prog");
|
|
2284
|
+
lines.push(`${ind2} let ${progV} = false`);
|
|
2285
|
+
lines.push(`${ind2} while (true) {`);
|
|
2286
|
+
lines.push(...inner.setup);
|
|
2287
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2288
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2289
|
+
lines.push(`${ind2} ${progV} = true`);
|
|
2290
|
+
lines.push(`${ind2} }`);
|
|
2291
|
+
if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
first = false;
|
|
2295
|
+
}
|
|
2296
|
+
lines.push(`${ind2} ${okV} = true`);
|
|
2297
|
+
lines.push(`${ind2}} while (false)`);
|
|
2298
|
+
lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
|
|
2299
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2300
|
+
}
|
|
2301
|
+
if (shape.kind === "litFold") {
|
|
2302
|
+
const openLen2 = shape.open.length;
|
|
2303
|
+
const endV2 = mint("_end");
|
|
2304
|
+
const checks = shape.open.map((cp, k) => {
|
|
2305
|
+
const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
|
|
2306
|
+
return foldEq(cVar, cp);
|
|
2307
|
+
});
|
|
2308
|
+
return {
|
|
2309
|
+
setup: [
|
|
2310
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2311
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
|
|
2312
|
+
],
|
|
2313
|
+
ok: `${endV2} > ${start}`,
|
|
2314
|
+
end: endV2
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
1572
2317
|
if (shape.kind === "until") {
|
|
1573
2318
|
const j2 = mint("_j");
|
|
1574
2319
|
const openLen2 = shape.open.length;
|
|
@@ -1850,6 +2595,7 @@ function looseMkBuildRe(type) {
|
|
|
1850
2595
|
);
|
|
1851
2596
|
}
|
|
1852
2597
|
function analyzeMkInlineBuild(def) {
|
|
2598
|
+
if (!def.build) return null;
|
|
1853
2599
|
const src = (def.buildSrc ?? def.build.toString()).trim();
|
|
1854
2600
|
const strict = src.match(MK_BUILD_RE);
|
|
1855
2601
|
if (strict) {
|
|
@@ -1947,6 +2693,15 @@ function v(ctx, prefix = "_v") {
|
|
|
1947
2693
|
function ind(ctx) {
|
|
1948
2694
|
return " ".repeat(ctx.indent);
|
|
1949
2695
|
}
|
|
2696
|
+
function nsp(ctx) {
|
|
2697
|
+
return ctx.ns ?? "";
|
|
2698
|
+
}
|
|
2699
|
+
function mfRef(ctx) {
|
|
2700
|
+
return `${nsp(ctx)}_mf`;
|
|
2701
|
+
}
|
|
2702
|
+
function buildRef(ctx) {
|
|
2703
|
+
return `${nsp(ctx)}_build`;
|
|
2704
|
+
}
|
|
1950
2705
|
function reindentStmts(stmts, targetLevels) {
|
|
1951
2706
|
const nonEmpty = stmts.filter((s) => s.trim().length > 0);
|
|
1952
2707
|
if (nonEmpty.length === 0) return stmts;
|
|
@@ -1963,8 +2718,8 @@ function failReturnArr(expectedArr, posExpr) {
|
|
|
1963
2718
|
function hoistExpected(ctx, constArrSource) {
|
|
1964
2719
|
let name = ctx.expectedMap.get(constArrSource);
|
|
1965
2720
|
if (name === void 0) {
|
|
1966
|
-
name =
|
|
1967
|
-
ctx.expectedDecls.push(`const ${name} =
|
|
2721
|
+
name = `${nsp(ctx)}_fx${ctx.expectedDecls.length}`;
|
|
2722
|
+
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
1968
2723
|
ctx.expectedMap.set(constArrSource, name);
|
|
1969
2724
|
}
|
|
1970
2725
|
return name;
|
|
@@ -2062,13 +2817,13 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
|
|
|
2062
2817
|
`${i}}`
|
|
2063
2818
|
];
|
|
2064
2819
|
}
|
|
2065
|
-
function ensureRegexDecl(ctx,
|
|
2820
|
+
function ensureRegexDecl(ctx, source, flags) {
|
|
2066
2821
|
const f = "y" + flags.replace(/[gy]/g, "");
|
|
2067
|
-
const key = `${
|
|
2822
|
+
const key = `${source}/${f}`;
|
|
2068
2823
|
let rName = ctx.regexMap.get(key);
|
|
2069
2824
|
if (rName === void 0) {
|
|
2070
|
-
rName =
|
|
2071
|
-
ctx.regexDecls.push(`const ${rName} = /${
|
|
2825
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2826
|
+
ctx.regexDecls.push(`const ${rName} = /${source}/${f}`);
|
|
2072
2827
|
ctx.regexMap.set(key, rName);
|
|
2073
2828
|
}
|
|
2074
2829
|
return rName;
|
|
@@ -2077,7 +2832,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2077
2832
|
const trivia2 = ctx.activeTrivia;
|
|
2078
2833
|
const existing = ctx.triviaFnNames.get(trivia2);
|
|
2079
2834
|
if (existing) return existing;
|
|
2080
|
-
const fnName =
|
|
2835
|
+
const fnName = `${nsp(ctx)}_tf${ctx.triviaFnNames.size}`;
|
|
2081
2836
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
2082
2837
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
2083
2838
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
@@ -2099,7 +2854,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2099
2854
|
for (const arm of regexSpec.arms) {
|
|
2100
2855
|
const def = arm.parser._def;
|
|
2101
2856
|
if (def.tag !== "regex") break;
|
|
2102
|
-
reNames.push(ensureRegexDecl(ctx, def.
|
|
2857
|
+
reNames.push(ensureRegexDecl(ctx, def.source, def.flags));
|
|
2103
2858
|
}
|
|
2104
2859
|
if (reNames.length === regexSpec.arms.length) {
|
|
2105
2860
|
ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
|
|
@@ -2286,18 +3041,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2286
3041
|
return true;
|
|
2287
3042
|
}
|
|
2288
3043
|
}
|
|
2289
|
-
function emitFallible(inner, ctx, pos) {
|
|
3044
|
+
function emitFallible(inner, ctx, pos, swallow = false) {
|
|
2290
3045
|
const lbl = v(ctx, "_lbl");
|
|
2291
3046
|
const okV = `${lbl}ok`;
|
|
2292
3047
|
const valV = `${lbl}v`;
|
|
2293
3048
|
const endV = `${lbl}e`;
|
|
2294
3049
|
const savedLabel = ctx.failLabel;
|
|
2295
3050
|
const savedIndent = ctx.indent;
|
|
3051
|
+
const savedRecord = ctx.recordFail;
|
|
2296
3052
|
ctx.failLabel = lbl;
|
|
2297
3053
|
ctx.indent = savedIndent + 1;
|
|
3054
|
+
if (swallow) ctx.recordFail = false;
|
|
2298
3055
|
const r = emit(inner, ctx, pos);
|
|
2299
3056
|
ctx.failLabel = savedLabel;
|
|
2300
3057
|
ctx.indent = savedIndent;
|
|
3058
|
+
ctx.recordFail = savedRecord;
|
|
2301
3059
|
const ind0 = ind(ctx);
|
|
2302
3060
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
2303
3061
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
@@ -2320,6 +3078,7 @@ function emitFallible(inner, ctx, pos) {
|
|
|
2320
3078
|
];
|
|
2321
3079
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
2322
3080
|
}
|
|
3081
|
+
var CHARCODE_CHAIN_MAX = 16;
|
|
2323
3082
|
function emitLit(def, ctx, pos) {
|
|
2324
3083
|
const { value, caseInsensitive } = def;
|
|
2325
3084
|
const len = value.length;
|
|
@@ -2342,7 +3101,7 @@ function emitLit(def, ctx, pos) {
|
|
|
2342
3101
|
...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
|
|
2343
3102
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
2344
3103
|
);
|
|
2345
|
-
} else if (len <=
|
|
3104
|
+
} else if (len <= CHARCODE_CHAIN_MAX) {
|
|
2346
3105
|
const checks = Array.from(
|
|
2347
3106
|
{ length: len },
|
|
2348
3107
|
(_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
|
|
@@ -2364,7 +3123,55 @@ function emitLit(def, ctx, pos) {
|
|
|
2364
3123
|
function escapeKeywordRe(s) {
|
|
2365
3124
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2366
3125
|
}
|
|
3126
|
+
function emitKeywordsFast(def, ctx, pos) {
|
|
3127
|
+
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
3128
|
+
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
3129
|
+
if (def.caseInsensitive && def.boundary) return null;
|
|
3130
|
+
let boundary = null;
|
|
3131
|
+
if (def.boundary) {
|
|
3132
|
+
let body = def.boundary;
|
|
3133
|
+
const negated = body.startsWith("^");
|
|
3134
|
+
if (negated) body = body.slice(1);
|
|
3135
|
+
const ranges = parseClassRanges(body);
|
|
3136
|
+
if (!ranges) return null;
|
|
3137
|
+
boundary = { ranges, negated };
|
|
3138
|
+
}
|
|
3139
|
+
const mint = (prefix = "_v") => v(ctx, prefix);
|
|
3140
|
+
const lbl = v(ctx, "_kwLbl");
|
|
3141
|
+
const valV = v(ctx, "_kwv");
|
|
3142
|
+
const endV = v(ctx, "_kwe");
|
|
3143
|
+
const bodyInd = ind(ctx) + " ";
|
|
3144
|
+
const tries = [];
|
|
3145
|
+
for (const w of def.words) {
|
|
3146
|
+
const cps = Array.from(w, (ch) => ch.codePointAt(0));
|
|
3147
|
+
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
3148
|
+
if (boundary) {
|
|
3149
|
+
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
3150
|
+
}
|
|
3151
|
+
const m = emitShapeMatch(shape, pos, mint, bodyInd);
|
|
3152
|
+
tries.push(
|
|
3153
|
+
...m.setup,
|
|
3154
|
+
// Slice from input rather than reusing the literal word: `caseInsensitive`
|
|
3155
|
+
// must return the text as it actually appeared (e.g. "ABC" for keyword
|
|
3156
|
+
// "abc"), matching what the original `RegExp.exec()[0]` returned.
|
|
3157
|
+
`${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
const stmts = [
|
|
3161
|
+
`${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
|
|
3162
|
+
`${ind(ctx)}${lbl}: {`,
|
|
3163
|
+
...tries,
|
|
3164
|
+
`${ind(ctx)}}`,
|
|
3165
|
+
// Every word has length >= 1 (checked above), so a real match always
|
|
3166
|
+
// advances past `pos` — `endV === pos` only happens when no candidate matched.
|
|
3167
|
+
...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
|
|
3168
|
+
];
|
|
3169
|
+
stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
|
|
3170
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3171
|
+
}
|
|
2367
3172
|
function emitKeywords(def, ctx, pos) {
|
|
3173
|
+
const fast = emitKeywordsFast(def, ctx, pos);
|
|
3174
|
+
if (fast) return fast;
|
|
2368
3175
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
2369
3176
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
2370
3177
|
const flags = def.caseInsensitive ? "iuy" : "uy";
|
|
@@ -2372,7 +3179,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2372
3179
|
const key = `${source}/${flags}`;
|
|
2373
3180
|
let rName = ctx.regexMap.get(key);
|
|
2374
3181
|
if (rName === void 0) {
|
|
2375
|
-
rName =
|
|
3182
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2376
3183
|
ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
|
|
2377
3184
|
ctx.regexMap.set(key, rName);
|
|
2378
3185
|
}
|
|
@@ -2406,11 +3213,11 @@ function emitRegex(def, ctx, pos) {
|
|
|
2406
3213
|
}
|
|
2407
3214
|
}
|
|
2408
3215
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2409
|
-
const key = `${def.
|
|
3216
|
+
const key = `${def.source}/${flags}`;
|
|
2410
3217
|
let rName = ctx.regexMap.get(key);
|
|
2411
3218
|
if (rName === void 0) {
|
|
2412
|
-
rName =
|
|
2413
|
-
ctx.regexDecls.push(`const ${rName} = /${def.
|
|
3219
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3220
|
+
ctx.regexDecls.push(`const ${rName} = /${def.source}/${flags}`);
|
|
2414
3221
|
ctx.regexMap.set(key, rName);
|
|
2415
3222
|
}
|
|
2416
3223
|
const mv = v(ctx, "_m");
|
|
@@ -2495,6 +3302,31 @@ function failsAtStart(p) {
|
|
|
2495
3302
|
return false;
|
|
2496
3303
|
}
|
|
2497
3304
|
}
|
|
3305
|
+
function armStaticExpected(ctx, p) {
|
|
3306
|
+
return hoistExpected(ctx, staticExpectedArr([p]));
|
|
3307
|
+
}
|
|
3308
|
+
function canMatchEmptyAtStart(p) {
|
|
3309
|
+
const d = p._def;
|
|
3310
|
+
switch (d.tag) {
|
|
3311
|
+
case "regex":
|
|
3312
|
+
return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
|
|
3313
|
+
case "optional":
|
|
3314
|
+
return true;
|
|
3315
|
+
case "many":
|
|
3316
|
+
return true;
|
|
3317
|
+
case "transform":
|
|
3318
|
+
case "label":
|
|
3319
|
+
return canMatchEmptyAtStart(d.parser);
|
|
3320
|
+
case "literal":
|
|
3321
|
+
return d.value.length === 0;
|
|
3322
|
+
default:
|
|
3323
|
+
return false;
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
function needsFirstSetGuard(p) {
|
|
3327
|
+
const fs = p._meta.firstSet;
|
|
3328
|
+
return fs.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
3329
|
+
}
|
|
2498
3330
|
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
2499
3331
|
if (failsAtStart(p)) {
|
|
2500
3332
|
const r = emit(p, ctx, pos);
|
|
@@ -2562,7 +3394,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
2562
3394
|
const reKey = `${regexDef.source}/${cleanFlags}`;
|
|
2563
3395
|
let reVar = ctx.regexMap.get(reKey);
|
|
2564
3396
|
if (reVar === void 0) {
|
|
2565
|
-
reVar =
|
|
3397
|
+
reVar = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2566
3398
|
ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
|
|
2567
3399
|
ctx.regexMap.set(reKey, reVar);
|
|
2568
3400
|
}
|
|
@@ -2636,21 +3468,26 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
2636
3468
|
const resValV = v(ctx, "_crv");
|
|
2637
3469
|
const resEndV = v(ctx, "_cre");
|
|
2638
3470
|
const resOkV = v(ctx, "_crok");
|
|
3471
|
+
const codeV = v(ctx, "_chcode");
|
|
2639
3472
|
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2640
3473
|
const ind0 = ind(ctx);
|
|
2641
3474
|
const stmts = [
|
|
2642
3475
|
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
2643
|
-
`${ind0}let ${slots.join(", ")}
|
|
3476
|
+
`${ind0}let ${slots.join(", ")}`,
|
|
3477
|
+
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
2644
3478
|
];
|
|
2645
3479
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2646
3480
|
const p = def.parsers[i];
|
|
2647
3481
|
const gate = def.gates[i];
|
|
2648
3482
|
const autoNot = def.autoNot[i];
|
|
3483
|
+
const atStart = failsAtStart(p);
|
|
3484
|
+
const staticFx = armStaticExpected(ctx, p);
|
|
3485
|
+
const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
2649
3486
|
let gateCond = null;
|
|
2650
3487
|
if (gate) {
|
|
2651
3488
|
const gateIdx = ctx.mapFns.length;
|
|
2652
3489
|
ctx.mapFns.push(gate);
|
|
2653
|
-
gateCond =
|
|
3490
|
+
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
2654
3491
|
}
|
|
2655
3492
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
2656
3493
|
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
@@ -2660,39 +3497,41 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
2660
3497
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2661
3498
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2662
3499
|
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
3500
|
+
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
2663
3501
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2664
|
-
ctx
|
|
2665
|
-
|
|
3502
|
+
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
3503
|
+
ctx.indent += fsGuard ? 2 : 1;
|
|
2666
3504
|
if (markLeaves) {
|
|
2667
3505
|
stmts.push(
|
|
2668
|
-
`${
|
|
2669
|
-
`${
|
|
2670
|
-
`${
|
|
2671
|
-
`${
|
|
3506
|
+
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
3507
|
+
`${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
|
|
3508
|
+
`${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
3509
|
+
`${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
|
|
2672
3510
|
);
|
|
2673
3511
|
}
|
|
2674
|
-
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3512
|
+
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
|
|
2675
3513
|
stmts.push(...armStmts);
|
|
2676
|
-
ctx.indent--;
|
|
2677
3514
|
if (autoNot && autoNot.length > 0) {
|
|
2678
3515
|
const anCode = v(ctx, "_anc");
|
|
2679
3516
|
const rejectCond = autoNot.map(
|
|
2680
3517
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
|
|
2681
3518
|
).join(" || ");
|
|
2682
|
-
stmts.push(`${
|
|
2683
|
-
stmts.push(`${
|
|
2684
|
-
stmts.push(`${
|
|
2685
|
-
stmts.push(`${
|
|
2686
|
-
stmts.push(`${
|
|
2687
|
-
stmts.push(`${
|
|
2688
|
-
stmts.push(`${
|
|
2689
|
-
stmts.push(`${
|
|
2690
|
-
stmts.push(`${
|
|
2691
|
-
if (rollback) stmts.push(`${
|
|
3519
|
+
stmts.push(`${ind(ctx)}if (${okVar}) {`);
|
|
3520
|
+
stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
|
|
3521
|
+
stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
|
|
3522
|
+
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
3523
|
+
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
3524
|
+
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
3525
|
+
stmts.push(`${ind(ctx)} }`);
|
|
3526
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3527
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
3528
|
+
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
2692
3529
|
} else {
|
|
2693
|
-
stmts.push(`${
|
|
2694
|
-
stmts.push(`${
|
|
3530
|
+
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
3531
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
|
|
2695
3532
|
}
|
|
3533
|
+
ctx.indent -= fsGuard ? 2 : 1;
|
|
3534
|
+
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
2696
3535
|
stmts.push(`${ind0}}`);
|
|
2697
3536
|
}
|
|
2698
3537
|
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
@@ -2715,7 +3554,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2715
3554
|
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
2716
3555
|
const vv = v(ctx);
|
|
2717
3556
|
return {
|
|
2718
|
-
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} =
|
|
3557
|
+
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
2719
3558
|
valueVar: vv,
|
|
2720
3559
|
endVar: endV
|
|
2721
3560
|
};
|
|
@@ -2725,7 +3564,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2725
3564
|
function emitLiteralCondition(litVal, pos) {
|
|
2726
3565
|
const len = litVal.length;
|
|
2727
3566
|
if (len === 0) return "true";
|
|
2728
|
-
if (len >
|
|
3567
|
+
if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
|
|
2729
3568
|
const checks = [`${pos} + ${len} <= input.length`];
|
|
2730
3569
|
for (let i = 0; i < len; i++) {
|
|
2731
3570
|
const code = litVal.codePointAt(i);
|
|
@@ -2774,7 +3613,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2774
3613
|
itemPos = npV;
|
|
2775
3614
|
}
|
|
2776
3615
|
}
|
|
2777
|
-
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
3616
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
2778
3617
|
stmts.push(
|
|
2779
3618
|
...iterStmts,
|
|
2780
3619
|
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
|
|
@@ -2788,7 +3627,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2788
3627
|
function emitOptional(def, ctx, pos) {
|
|
2789
3628
|
const valV = v(ctx, "_opt");
|
|
2790
3629
|
const endV = v(ctx, "_opte");
|
|
2791
|
-
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
3630
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2792
3631
|
const ind0 = ind(ctx);
|
|
2793
3632
|
const stmts = [
|
|
2794
3633
|
...lblStmts,
|
|
@@ -2800,7 +3639,7 @@ function emitOptional(def, ctx, pos) {
|
|
|
2800
3639
|
function emitSepBy(_p, def, ctx, pos) {
|
|
2801
3640
|
const arrV = v(ctx, "_arr");
|
|
2802
3641
|
const curV = v(ctx, "_cur");
|
|
2803
|
-
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
3642
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
2804
3643
|
const stmts = [
|
|
2805
3644
|
`${ind(ctx)}const ${arrV} = []`,
|
|
2806
3645
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
@@ -2832,11 +3671,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2832
3671
|
);
|
|
2833
3672
|
sepAtPos = spV;
|
|
2834
3673
|
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}; `;
|
|
2835
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3674
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2836
3675
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
2837
3676
|
const npV = v(ctx, "_np");
|
|
2838
3677
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
2839
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3678
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2840
3679
|
stmts.push(
|
|
2841
3680
|
...nextStmts,
|
|
2842
3681
|
// item failed → unwind the separator too, back to the end of the last item
|
|
@@ -2849,11 +3688,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2849
3688
|
const spV = v(ctx, "_sp");
|
|
2850
3689
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
2851
3690
|
sepAtPos = spV;
|
|
2852
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3691
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2853
3692
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2854
3693
|
const npV = v(ctx, "_np");
|
|
2855
3694
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
2856
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3695
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2857
3696
|
stmts.push(
|
|
2858
3697
|
...nextStmts,
|
|
2859
3698
|
`${ind(ctx)}if (!${nextOk}) break`,
|
|
@@ -2870,10 +3709,10 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2870
3709
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
2871
3710
|
);
|
|
2872
3711
|
}
|
|
2873
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3712
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2874
3713
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2875
3714
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
2876
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
|
|
3715
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
2877
3716
|
stmts.push(
|
|
2878
3717
|
...nextStmts,
|
|
2879
3718
|
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
|
|
@@ -2931,7 +3770,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
2931
3770
|
return { stmts, valueVar: valV, endVar: curV };
|
|
2932
3771
|
}
|
|
2933
3772
|
function emitNot(def, ctx, pos) {
|
|
2934
|
-
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3773
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2935
3774
|
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
2936
3775
|
return {
|
|
2937
3776
|
stmts: [
|
|
@@ -2943,16 +3782,17 @@ function emitNot(def, ctx, pos) {
|
|
|
2943
3782
|
};
|
|
2944
3783
|
}
|
|
2945
3784
|
function emitNode(def, ctx, pos) {
|
|
2946
|
-
const
|
|
3785
|
+
const structural = def.build === void 0;
|
|
3786
|
+
const mkType = structural ? null : analyzeMkInlineBuild(def);
|
|
2947
3787
|
let buildIdx = null;
|
|
2948
|
-
if (!mkType) {
|
|
3788
|
+
if (!mkType && !structural) {
|
|
2949
3789
|
buildIdx = ctx.buildFns.length;
|
|
2950
3790
|
ctx.buildFns.push(def.build);
|
|
2951
3791
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
2952
3792
|
}
|
|
2953
3793
|
const i = ind(ctx);
|
|
2954
|
-
const capturesTrivia = mkType !== null || buildReadsTrivia(def);
|
|
2955
|
-
const clonesState = buildReadsState(def);
|
|
3794
|
+
const capturesTrivia = mkType !== null || structural || buildReadsTrivia(def);
|
|
3795
|
+
const clonesState = structural || buildReadsState(def);
|
|
2956
3796
|
const chV = v(ctx, "_ch");
|
|
2957
3797
|
const rawV = v(ctx, "_raw");
|
|
2958
3798
|
const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
@@ -2975,7 +3815,8 @@ function emitNode(def, ctx, pos) {
|
|
|
2975
3815
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
2976
3816
|
}
|
|
2977
3817
|
const ndV = v(ctx, "_nd");
|
|
2978
|
-
const
|
|
3818
|
+
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})`;
|
|
3819
|
+
const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
|
|
2979
3820
|
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;
|
|
2980
3821
|
stmts.push(
|
|
2981
3822
|
`${i}const ${ndV} = ${finalExpr}`,
|
|
@@ -3000,8 +3841,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
3000
3841
|
return { stmts, valueVar: vv, endVar: ev };
|
|
3001
3842
|
}
|
|
3002
3843
|
function emitLazy(p, def, ctx, pos) {
|
|
3844
|
+
const canonical = ctx.ruleNames?.get(p);
|
|
3003
3845
|
const usage = ctx.lazyUsage;
|
|
3004
|
-
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
3846
|
+
if (!canonical && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
3005
3847
|
let resolved;
|
|
3006
3848
|
try {
|
|
3007
3849
|
resolved = def.thunk();
|
|
@@ -3011,7 +3853,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
3011
3853
|
return emit(resolved, ctx, pos);
|
|
3012
3854
|
}
|
|
3013
3855
|
if (!ctx.namedParsers.has(p)) {
|
|
3014
|
-
const fnName2 =
|
|
3856
|
+
const fnName2 = canonical ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
3015
3857
|
ctx.namedParsers.set(p, fnName2);
|
|
3016
3858
|
let resolved;
|
|
3017
3859
|
try {
|
|
@@ -3022,11 +3864,14 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
3022
3864
|
}
|
|
3023
3865
|
const savedIndent = ctx.indent;
|
|
3024
3866
|
const savedFailLabel = ctx.failLabel;
|
|
3867
|
+
const savedRecord = ctx.recordFail;
|
|
3025
3868
|
ctx.indent = 1;
|
|
3026
3869
|
ctx.failLabel = "_pfail";
|
|
3870
|
+
ctx.recordFail = true;
|
|
3027
3871
|
const r = emit(resolved, ctx, "_pos");
|
|
3028
3872
|
ctx.indent = savedIndent;
|
|
3029
3873
|
ctx.failLabel = savedFailLabel;
|
|
3874
|
+
ctx.recordFail = savedRecord;
|
|
3030
3875
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
3031
3876
|
}
|
|
3032
3877
|
const fnName = ctx.namedParsers.get(p);
|
|
@@ -3126,7 +3971,7 @@ function emit(p, ctx, pos) {
|
|
|
3126
3971
|
return {
|
|
3127
3972
|
stmts: [
|
|
3128
3973
|
...inner.stmts,
|
|
3129
|
-
`${ind(ctx)}const ${mv} =
|
|
3974
|
+
`${ind(ctx)}const ${mv} = ${mfRef(ctx)}[${fnIdx}](${inner.valueVar}, { start: ${pos}, end: ${inner.endVar} })`
|
|
3130
3975
|
],
|
|
3131
3976
|
valueVar: mv,
|
|
3132
3977
|
endVar: inner.endVar
|
|
@@ -3189,7 +4034,7 @@ function emit(p, ctx, pos) {
|
|
|
3189
4034
|
const vv = v(ctx);
|
|
3190
4035
|
return {
|
|
3191
4036
|
stmts: [
|
|
3192
|
-
...emitIfFail(ctx,
|
|
4037
|
+
...emitIfFail(ctx, `!${mfRef(ctx)}[${fnIdx}](_ctx.state)`, failBody(ctx, '"gate"', pos)),
|
|
3193
4038
|
`${ind(ctx)}const ${vv} = null`
|
|
3194
4039
|
],
|
|
3195
4040
|
valueVar: vv,
|
|
@@ -3206,11 +4051,14 @@ function emit(p, ctx, pos) {
|
|
|
3206
4051
|
ctx.namedParsers.set(innerParser, fnName);
|
|
3207
4052
|
const savedIndent = ctx.indent;
|
|
3208
4053
|
const savedFailLabel = ctx.failLabel;
|
|
4054
|
+
const savedRecord = ctx.recordFail;
|
|
3209
4055
|
ctx.indent = 1;
|
|
3210
4056
|
ctx.failLabel = "_pfail";
|
|
4057
|
+
ctx.recordFail = true;
|
|
3211
4058
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
3212
4059
|
ctx.indent = savedIndent;
|
|
3213
4060
|
ctx.failLabel = savedFailLabel;
|
|
4061
|
+
ctx.recordFail = savedRecord;
|
|
3214
4062
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
3215
4063
|
}
|
|
3216
4064
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -3219,7 +4067,7 @@ function emit(p, ctx, pos) {
|
|
|
3219
4067
|
const ev = v(ctx, "_wce");
|
|
3220
4068
|
return {
|
|
3221
4069
|
stmts: [
|
|
3222
|
-
`${ind(ctx)}const ${rv} = { ..._ctx, state:
|
|
4070
|
+
`${ind(ctx)}const ${rv} = { ..._ctx, state: ${mfRef(ctx)}[${evIdx}]() }`,
|
|
3223
4071
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
3224
4072
|
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3225
4073
|
// and propagate the inner failure verbatim (interpreter parity).
|
|
@@ -3350,6 +4198,39 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3350
4198
|
for (const root of roots) walk2(root);
|
|
3351
4199
|
return { counts, recursive };
|
|
3352
4200
|
}
|
|
4201
|
+
function ruleDependencies(ruleMap) {
|
|
4202
|
+
const nameOf = /* @__PURE__ */ new Map();
|
|
4203
|
+
for (const [name, comb] of ruleMap) nameOf.set(comb, name);
|
|
4204
|
+
const deps = /* @__PURE__ */ new Map();
|
|
4205
|
+
for (const [name, comb] of ruleMap) {
|
|
4206
|
+
const found = /* @__PURE__ */ new Set();
|
|
4207
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4208
|
+
const walk2 = (p, isRoot) => {
|
|
4209
|
+
const def = p._def;
|
|
4210
|
+
const boundary = def.tag === "lazy" ? p._ruleName ?? nameOf.get(p) : nameOf.get(p);
|
|
4211
|
+
if (!isRoot && boundary !== void 0) {
|
|
4212
|
+
found.add(boundary);
|
|
4213
|
+
return;
|
|
4214
|
+
}
|
|
4215
|
+
if (seen.has(p)) return;
|
|
4216
|
+
seen.add(p);
|
|
4217
|
+
if (def.tag === "lazy") {
|
|
4218
|
+
let resolved;
|
|
4219
|
+
try {
|
|
4220
|
+
resolved = def.thunk();
|
|
4221
|
+
} catch {
|
|
4222
|
+
return;
|
|
4223
|
+
}
|
|
4224
|
+
walk2(resolved, false);
|
|
4225
|
+
return;
|
|
4226
|
+
}
|
|
4227
|
+
for (const child of childrenOf(def)) walk2(child, false);
|
|
4228
|
+
};
|
|
4229
|
+
walk2(comb, true);
|
|
4230
|
+
deps.set(name, [...found]);
|
|
4231
|
+
}
|
|
4232
|
+
return deps;
|
|
4233
|
+
}
|
|
3353
4234
|
function compile(combinator, mapFnSources) {
|
|
3354
4235
|
const ctx = {
|
|
3355
4236
|
vars: 0,
|
|
@@ -3358,6 +4239,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3358
4239
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3359
4240
|
expectedDecls: [],
|
|
3360
4241
|
expectedMap: /* @__PURE__ */ new Map(),
|
|
4242
|
+
recordFail: true,
|
|
3361
4243
|
mapFns: [],
|
|
3362
4244
|
mapFnSrcs: [],
|
|
3363
4245
|
buildFns: [],
|
|
@@ -3432,6 +4314,150 @@ function compile(combinator, mapFnSources) {
|
|
|
3432
4314
|
}
|
|
3433
4315
|
};
|
|
3434
4316
|
}
|
|
4317
|
+
function compileLinkable(ruleMapArg, ns) {
|
|
4318
|
+
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4319
|
+
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4320
|
+
const d = val._def;
|
|
4321
|
+
if (d.tag !== "lazy") return true;
|
|
4322
|
+
try {
|
|
4323
|
+
d.thunk();
|
|
4324
|
+
return true;
|
|
4325
|
+
} catch {
|
|
4326
|
+
return false;
|
|
4327
|
+
}
|
|
4328
|
+
});
|
|
4329
|
+
const ctx = {
|
|
4330
|
+
vars: 0,
|
|
4331
|
+
indent: 1,
|
|
4332
|
+
regexDecls: [],
|
|
4333
|
+
regexMap: /* @__PURE__ */ new Map(),
|
|
4334
|
+
expectedDecls: [],
|
|
4335
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4336
|
+
recordFail: true,
|
|
4337
|
+
mapFns: [],
|
|
4338
|
+
mapFnSrcs: [],
|
|
4339
|
+
buildFns: [],
|
|
4340
|
+
buildSrcs: [],
|
|
4341
|
+
runtimeParsers: [],
|
|
4342
|
+
needsCollator: false,
|
|
4343
|
+
namedParsers: /* @__PURE__ */ new Map(),
|
|
4344
|
+
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4345
|
+
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4346
|
+
namedFnDecls: [],
|
|
4347
|
+
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4348
|
+
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4349
|
+
ns
|
|
4350
|
+
};
|
|
4351
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4352
|
+
const ruleNames = /* @__PURE__ */ new Map();
|
|
4353
|
+
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4354
|
+
for (const [key, rule] of ruleMap) {
|
|
4355
|
+
const base = `_r_${key.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4356
|
+
const n = seen.get(base) ?? 0;
|
|
4357
|
+
seen.set(base, n + 1);
|
|
4358
|
+
const fn = n === 0 ? base : `${base}$${n}`;
|
|
4359
|
+
ruleNames.set(rule, fn);
|
|
4360
|
+
fnNameToKey.set(fn, key);
|
|
4361
|
+
}
|
|
4362
|
+
ctx.ruleNames = ruleNames;
|
|
4363
|
+
{
|
|
4364
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
4365
|
+
const scanExternal = (p) => {
|
|
4366
|
+
if (scanned.has(p)) return;
|
|
4367
|
+
scanned.add(p);
|
|
4368
|
+
const def = p._def;
|
|
4369
|
+
if (def.tag === "lazy") {
|
|
4370
|
+
let resolved;
|
|
4371
|
+
try {
|
|
4372
|
+
resolved = def.thunk();
|
|
4373
|
+
} catch {
|
|
4374
|
+
resolved = void 0;
|
|
4375
|
+
}
|
|
4376
|
+
if (resolved === void 0) {
|
|
4377
|
+
const name = p._ruleName;
|
|
4378
|
+
if (name && !ctx.namedParsers.has(p)) {
|
|
4379
|
+
const fn = `_r_${name.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4380
|
+
ruleNames.set(p, fn);
|
|
4381
|
+
ctx.namedParsers.set(p, fn);
|
|
4382
|
+
}
|
|
4383
|
+
return;
|
|
4384
|
+
}
|
|
4385
|
+
scanExternal(resolved);
|
|
4386
|
+
return;
|
|
4387
|
+
}
|
|
4388
|
+
for (const child of childrenOf(def)) scanExternal(child);
|
|
4389
|
+
};
|
|
4390
|
+
for (const [, rule] of ruleMap) scanExternal(rule);
|
|
4391
|
+
}
|
|
4392
|
+
const perEntry = [];
|
|
4393
|
+
for (const [key, rule] of ruleMap) {
|
|
4394
|
+
const fn = ruleNames.get(rule);
|
|
4395
|
+
if (!ctx.namedParsers.has(rule)) {
|
|
4396
|
+
ctx.namedParsers.set(rule, fn);
|
|
4397
|
+
let resolved = rule;
|
|
4398
|
+
const def = rule._def;
|
|
4399
|
+
if (def.tag === "lazy") {
|
|
4400
|
+
try {
|
|
4401
|
+
resolved = def.thunk();
|
|
4402
|
+
} catch {
|
|
4403
|
+
return null;
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
const savedIndent = ctx.indent, savedFail = ctx.failLabel, savedRec = ctx.recordFail;
|
|
4407
|
+
ctx.indent = 1;
|
|
4408
|
+
ctx.failLabel = "_pfail";
|
|
4409
|
+
ctx.recordFail = true;
|
|
4410
|
+
const body = emit(resolved, ctx, "_pos");
|
|
4411
|
+
ctx.indent = savedIndent;
|
|
4412
|
+
ctx.failLabel = savedFail;
|
|
4413
|
+
ctx.recordFail = savedRec;
|
|
4414
|
+
pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
|
|
4415
|
+
}
|
|
4416
|
+
perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
|
|
4417
|
+
}
|
|
4418
|
+
if (ctx.runtimeParsers.length !== 0) return null;
|
|
4419
|
+
const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
|
|
4420
|
+
const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
|
|
4421
|
+
const ruleFns = /* @__PURE__ */ new Map();
|
|
4422
|
+
const privateFns = [];
|
|
4423
|
+
for (const decl of ctx.namedFnDecls) {
|
|
4424
|
+
const fnName = decl.match(/^function (\S+?)\(/)?.[1];
|
|
4425
|
+
const key = fnName ? fnNameToKey.get(fnName) : void 0;
|
|
4426
|
+
if (key !== void 0) ruleFns.set(key, decl);
|
|
4427
|
+
else privateFns.push(decl);
|
|
4428
|
+
}
|
|
4429
|
+
const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
|
|
4430
|
+
const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
|
|
4431
|
+
const prelude = [
|
|
4432
|
+
...ctx.regexDecls,
|
|
4433
|
+
...ctx.expectedDecls,
|
|
4434
|
+
mfDecl,
|
|
4435
|
+
buildDecl,
|
|
4436
|
+
...privateFns
|
|
4437
|
+
].filter(Boolean);
|
|
4438
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
4439
|
+
for (const { key, r } of perEntry) {
|
|
4440
|
+
wrappers.set(key, [
|
|
4441
|
+
`function(input, _pos, _ctx) {`,
|
|
4442
|
+
` let pos = _pos`,
|
|
4443
|
+
...r.stmts,
|
|
4444
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
4445
|
+
`}`
|
|
4446
|
+
].join("\n"));
|
|
4447
|
+
}
|
|
4448
|
+
return {
|
|
4449
|
+
ns,
|
|
4450
|
+
keys: perEntry.map((e) => e.key),
|
|
4451
|
+
prelude,
|
|
4452
|
+
ruleFns,
|
|
4453
|
+
wrappers,
|
|
4454
|
+
deps: ruleDependencies(ruleMap),
|
|
4455
|
+
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4456
|
+
needsCollator: ctx.needsCollator,
|
|
4457
|
+
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4458
|
+
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4459
|
+
};
|
|
4460
|
+
}
|
|
3435
4461
|
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
3436
4462
|
const bodyLines = [
|
|
3437
4463
|
` let pos = _pos`,
|
|
@@ -3470,6 +4496,87 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3470
4496
|
].join("\n");
|
|
3471
4497
|
}
|
|
3472
4498
|
|
|
4499
|
+
// src/compiler/linker.ts
|
|
4500
|
+
var _nsCounter = 0;
|
|
4501
|
+
function linkable(rulesMap, ns) {
|
|
4502
|
+
const pieces = compileLinkable([...Object.entries(rulesMap)], ns ?? `_lk${_nsCounter++}_`);
|
|
4503
|
+
if (!pieces) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
|
|
4504
|
+
return pieces;
|
|
4505
|
+
}
|
|
4506
|
+
function cstBuildHost(type, children, _rawChildren, span, _triviaLog, state) {
|
|
4507
|
+
return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
|
|
4508
|
+
}
|
|
4509
|
+
function pick(grammar, names) {
|
|
4510
|
+
const p = grammar.ruleFns instanceof Map ? grammar : linkable(grammar);
|
|
4511
|
+
const keep = /* @__PURE__ */ new Set();
|
|
4512
|
+
const has = new Set(p.keys);
|
|
4513
|
+
for (const n of names) {
|
|
4514
|
+
if (!has.has(n)) throw new Error(`pick: rule "${n}" is not in this grammar (available: ${p.keys.join(", ")})`);
|
|
4515
|
+
}
|
|
4516
|
+
const visit = (n) => {
|
|
4517
|
+
if (keep.has(n) || !has.has(n)) return;
|
|
4518
|
+
keep.add(n);
|
|
4519
|
+
for (const d of p.deps.get(n) ?? []) visit(d);
|
|
4520
|
+
};
|
|
4521
|
+
for (const n of names) visit(n);
|
|
4522
|
+
const filt = (m) => new Map([...m].filter(([k]) => keep.has(k)));
|
|
4523
|
+
return {
|
|
4524
|
+
...p,
|
|
4525
|
+
keys: [...keep],
|
|
4526
|
+
ruleFns: filt(p.ruleFns),
|
|
4527
|
+
wrappers: filt(p.wrappers),
|
|
4528
|
+
deps: filt(p.deps)
|
|
4529
|
+
};
|
|
4530
|
+
}
|
|
4531
|
+
function fusedBody(pieces) {
|
|
4532
|
+
const winner = /* @__PURE__ */ new Map();
|
|
4533
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4534
|
+
for (const [k, p] of winner) {
|
|
4535
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
4536
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
const contributing = new Set(winner.values());
|
|
4540
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4541
|
+
const needsCollator = [...contributing].some((p) => p.needsCollator);
|
|
4542
|
+
const lines = [
|
|
4543
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4544
|
+
"const _pfFail = {}",
|
|
4545
|
+
"let _pfEnd",
|
|
4546
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4547
|
+
...needsCollator ? ["const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })"] : [],
|
|
4548
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4549
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
4550
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4551
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4552
|
+
];
|
|
4553
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4554
|
+
const body = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4555
|
+
const env = {};
|
|
4556
|
+
for (const p of contributing) {
|
|
4557
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4558
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4559
|
+
}
|
|
4560
|
+
return { body, env };
|
|
4561
|
+
}
|
|
4562
|
+
function fuseRules(pieces) {
|
|
4563
|
+
const { body, env } = fusedBody(pieces);
|
|
4564
|
+
return new Function("_env", body)(env);
|
|
4565
|
+
}
|
|
4566
|
+
var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
|
|
4567
|
+
function itemPieces(item) {
|
|
4568
|
+
const carried = item[COMPOSED_PIECES];
|
|
4569
|
+
if (Array.isArray(carried)) return carried;
|
|
4570
|
+
if (item.ruleFns instanceof Map) return [item];
|
|
4571
|
+
return [linkable(item)];
|
|
4572
|
+
}
|
|
4573
|
+
function compose(items) {
|
|
4574
|
+
const pieces = items.flatMap(itemPieces);
|
|
4575
|
+
const map = fuseRules(pieces);
|
|
4576
|
+
Object.defineProperty(map, COMPOSED_PIECES, { value: pieces, enumerable: false });
|
|
4577
|
+
return map;
|
|
4578
|
+
}
|
|
4579
|
+
|
|
3473
4580
|
// src/combinators/guard.ts
|
|
3474
4581
|
function guard(predicate) {
|
|
3475
4582
|
const meta = {
|
|
@@ -3667,6 +4774,7 @@ function manyRecover(item, until) {
|
|
|
3667
4774
|
}
|
|
3668
4775
|
|
|
3669
4776
|
// src/functional/doc.ts
|
|
4777
|
+
var REENTRY_MAX_SPAN_FRACTION = 0.5;
|
|
3670
4778
|
function isNode(x) {
|
|
3671
4779
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
3672
4780
|
}
|
|
@@ -3727,7 +4835,48 @@ function graftAndShift(root, path, newNode, delta) {
|
|
|
3727
4835
|
children: newChildren
|
|
3728
4836
|
};
|
|
3729
4837
|
}
|
|
3730
|
-
|
|
4838
|
+
function structurallyEqual(a, b) {
|
|
4839
|
+
if (a === b) return true;
|
|
4840
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
4841
|
+
const at = a._tag;
|
|
4842
|
+
const bt = b._tag;
|
|
4843
|
+
if (at !== bt) return false;
|
|
4844
|
+
const as = a.span;
|
|
4845
|
+
const bs = b.span;
|
|
4846
|
+
if (as || bs) {
|
|
4847
|
+
if (!as || !bs || as.start !== bs.start || as.end !== bs.end) return false;
|
|
4848
|
+
}
|
|
4849
|
+
if (at === "leaf" || at === "trivia") {
|
|
4850
|
+
return a.value === b.value;
|
|
4851
|
+
}
|
|
4852
|
+
if (a.type !== b.type) return false;
|
|
4853
|
+
const ac = a.children;
|
|
4854
|
+
const bc = b.children;
|
|
4855
|
+
if (ac || bc) {
|
|
4856
|
+
if (!ac || !bc || ac.length !== bc.length) return false;
|
|
4857
|
+
for (let i = 0; i < ac.length; i++) if (!structurallyEqual(ac[i], bc[i])) return false;
|
|
4858
|
+
}
|
|
4859
|
+
return true;
|
|
4860
|
+
}
|
|
4861
|
+
function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced) {
|
|
4862
|
+
if (!produced.ok) return false;
|
|
4863
|
+
if (boundary >= newInput.length) return true;
|
|
4864
|
+
for (const sentinel of [" ", "\uFFFF"]) {
|
|
4865
|
+
if (newInput[boundary] === sentinel) continue;
|
|
4866
|
+
const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
|
|
4867
|
+
const ctx = { trackLines: false, state, build };
|
|
4868
|
+
let r;
|
|
4869
|
+
try {
|
|
4870
|
+
r = ruleFn(probed, start, ctx);
|
|
4871
|
+
} catch {
|
|
4872
|
+
return false;
|
|
4873
|
+
}
|
|
4874
|
+
if (!r.ok || r.span.end !== produced.span.end) return false;
|
|
4875
|
+
if (!structurallyEqual(r.value, produced.value)) return false;
|
|
4876
|
+
}
|
|
4877
|
+
return true;
|
|
4878
|
+
}
|
|
4879
|
+
var ParseDocImpl = class _ParseDocImpl {
|
|
3731
4880
|
_registry;
|
|
3732
4881
|
_rootRule;
|
|
3733
4882
|
_opts;
|
|
@@ -3744,7 +4893,7 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
|
3744
4893
|
}
|
|
3745
4894
|
edit(from, to, replacement) {
|
|
3746
4895
|
const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
|
|
3747
|
-
const reparse = () =>
|
|
4896
|
+
const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
|
|
3748
4897
|
if (!this.tree) return reparse();
|
|
3749
4898
|
const delta = replacement.length - (to - from);
|
|
3750
4899
|
const found = findContaining(this.tree, from);
|
|
@@ -3758,41 +4907,78 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
|
3758
4907
|
}
|
|
3759
4908
|
const rebuild = this._opts.rebuild ?? defaultRebuild;
|
|
3760
4909
|
for (const { node: node2, path } of candidates) {
|
|
4910
|
+
if (node2.span.end - node2.span.start > this.input.length * REENTRY_MAX_SPAN_FRACTION) break;
|
|
3761
4911
|
const ruleFn = this._registry[node2.type];
|
|
3762
4912
|
if (!ruleFn) continue;
|
|
3763
|
-
|
|
4913
|
+
if (!(node2.span.start <= from && to <= node2.span.end)) continue;
|
|
4914
|
+
const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
|
|
3764
4915
|
const r = ruleFn(newInput, node2.span.start, ctx);
|
|
3765
4916
|
if (!r.ok) continue;
|
|
3766
|
-
if (r.span.end
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
4917
|
+
if (r.span.end !== node2.span.end + delta) continue;
|
|
4918
|
+
if (!boundaryIsSafe(ruleFn, newInput, node2.span.start, node2.span.end + delta, node2.state, this._opts.build, r)) {
|
|
4919
|
+
continue;
|
|
4920
|
+
}
|
|
4921
|
+
if (delta === 0) {
|
|
4922
|
+
const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
|
|
4923
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
|
|
3774
4924
|
}
|
|
4925
|
+
if (this._opts.rebuild) return reparse();
|
|
4926
|
+
const newTree = graftAndShift(this.tree, path, r.value, delta);
|
|
4927
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
3775
4928
|
}
|
|
3776
4929
|
return reparse();
|
|
3777
4930
|
}
|
|
3778
4931
|
};
|
|
3779
|
-
function
|
|
4932
|
+
function parseDoc(registry, rootRule, input, opts = {}) {
|
|
3780
4933
|
const ruleFn = registry[rootRule];
|
|
3781
4934
|
if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
|
|
3782
|
-
const ctx = { trackLines: false, state: opts.state };
|
|
4935
|
+
const ctx = { trackLines: false, state: opts.state, build: opts.build };
|
|
3783
4936
|
const r = ruleFn(input, 0, ctx);
|
|
3784
4937
|
if (r.ok) {
|
|
3785
|
-
return new
|
|
4938
|
+
return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
|
|
4939
|
+
}
|
|
4940
|
+
return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
4941
|
+
}
|
|
4942
|
+
|
|
4943
|
+
// src/functional/run.ts
|
|
4944
|
+
var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
|
|
4945
|
+
function run(entry, input, options = {}) {
|
|
4946
|
+
const triviaLog = [];
|
|
4947
|
+
const errors = [];
|
|
4948
|
+
const ctx = {
|
|
4949
|
+
trackLines: false,
|
|
4950
|
+
_triviaLog: triviaLog,
|
|
4951
|
+
_errors: errors,
|
|
4952
|
+
build: options.build,
|
|
4953
|
+
state: options.state
|
|
4954
|
+
};
|
|
4955
|
+
const r = invoke(entry, input, 0, ctx);
|
|
4956
|
+
let unconsumedFrom = null;
|
|
4957
|
+
if (r.ok) {
|
|
4958
|
+
let pos = r.span?.end ?? 0;
|
|
4959
|
+
if (options.trivia && pos < input.length) {
|
|
4960
|
+
const t = invoke(options.trivia, input, pos, { trackLines: false });
|
|
4961
|
+
if (t.ok && t.span.end > pos) pos = t.span.end;
|
|
4962
|
+
}
|
|
4963
|
+
unconsumedFrom = pos < input.length ? pos : null;
|
|
3786
4964
|
}
|
|
3787
|
-
return
|
|
4965
|
+
return {
|
|
4966
|
+
ok: r.ok,
|
|
4967
|
+
value: r.ok ? r.value : void 0,
|
|
4968
|
+
span: r.span ?? { start: 0, end: 0 },
|
|
4969
|
+
expected: r.ok ? [] : r.expected ?? [],
|
|
4970
|
+
errors,
|
|
4971
|
+
triviaLog,
|
|
4972
|
+
unconsumedFrom
|
|
4973
|
+
};
|
|
3788
4974
|
}
|
|
3789
4975
|
|
|
3790
4976
|
// src/cst/trivia-index.ts
|
|
3791
|
-
function merge(map, key,
|
|
3792
|
-
if (
|
|
4977
|
+
function merge(map, key, run2) {
|
|
4978
|
+
if (run2.length === 0) return;
|
|
3793
4979
|
const existing = map.get(key);
|
|
3794
|
-
if (existing) existing.push(...
|
|
3795
|
-
else map.set(key, [...
|
|
4980
|
+
if (existing) existing.push(...run2);
|
|
4981
|
+
else map.set(key, [...run2]);
|
|
3796
4982
|
}
|
|
3797
4983
|
function buildTriviaIndex(root, input = "", opts) {
|
|
3798
4984
|
const before = /* @__PURE__ */ new Map();
|
|
@@ -3804,11 +4990,11 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
3804
4990
|
for (let i = 0; i < log.length; i += 3) {
|
|
3805
4991
|
const tStart = log[i], tEnd = log[i + 1], insertIdx = log[i + 2];
|
|
3806
4992
|
if (tEnd <= tStart) continue;
|
|
3807
|
-
const
|
|
4993
|
+
const run2 = [{ value: input.slice(tStart, tEnd), span: { start: tStart, end: tEnd } }];
|
|
3808
4994
|
const next = raw[insertIdx];
|
|
3809
|
-
if (next) merge(before, next.span.start,
|
|
4995
|
+
if (next) merge(before, next.span.start, run2);
|
|
3810
4996
|
const prev = insertIdx > 0 ? raw[insertIdx - 1] : void 0;
|
|
3811
|
-
if (prev) merge(after, prev.span.end,
|
|
4997
|
+
if (prev) merge(after, prev.span.end, run2);
|
|
3812
4998
|
}
|
|
3813
4999
|
}
|
|
3814
5000
|
const ch = node2.children;
|
|
@@ -3825,17 +5011,17 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
3825
5011
|
const rootSpan = root?.span;
|
|
3826
5012
|
const re = new RegExp(opts.trivia.source, opts.trivia.flags.replace(/[gy]/g, "") + "y");
|
|
3827
5013
|
const scanFrom = (from) => {
|
|
3828
|
-
const
|
|
5014
|
+
const run2 = [];
|
|
3829
5015
|
let pos = from;
|
|
3830
5016
|
re.lastIndex = pos;
|
|
3831
5017
|
let m = re.exec(input);
|
|
3832
5018
|
while (m && m.index === pos && m[0].length > 0) {
|
|
3833
|
-
|
|
5019
|
+
run2.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
|
|
3834
5020
|
pos += m[0].length;
|
|
3835
5021
|
re.lastIndex = pos;
|
|
3836
5022
|
m = re.exec(input);
|
|
3837
5023
|
}
|
|
3838
|
-
return
|
|
5024
|
+
return run2;
|
|
3839
5025
|
};
|
|
3840
5026
|
if (rootSpan && rootSpan.start > 0) {
|
|
3841
5027
|
const lead = scanFrom(0);
|
|
@@ -3921,22 +5107,296 @@ function triviaEntries(log, labels, opts) {
|
|
|
3921
5107
|
}
|
|
3922
5108
|
};
|
|
3923
5109
|
}
|
|
5110
|
+
|
|
5111
|
+
// src/cst/offset-model.ts
|
|
5112
|
+
var { isFinite } = Number;
|
|
5113
|
+
function floorIndex(arr, len, x) {
|
|
5114
|
+
let lo = 0;
|
|
5115
|
+
let hi = len - 1;
|
|
5116
|
+
let ans = -1;
|
|
5117
|
+
while (lo <= hi) {
|
|
5118
|
+
const mid = lo + hi >>> 1;
|
|
5119
|
+
if (arr[mid] <= x) {
|
|
5120
|
+
ans = mid;
|
|
5121
|
+
lo = mid + 1;
|
|
5122
|
+
} else {
|
|
5123
|
+
hi = mid - 1;
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
return ans;
|
|
5127
|
+
}
|
|
5128
|
+
var OffsetIndex = class {
|
|
5129
|
+
/** Slot starts, sorted ascending. Parallel to `_ends`. */
|
|
5130
|
+
_starts;
|
|
5131
|
+
_ends;
|
|
5132
|
+
count;
|
|
5133
|
+
docStart;
|
|
5134
|
+
docEnd;
|
|
5135
|
+
constructor(slots, docStart = 0, docEnd) {
|
|
5136
|
+
const sorted = [...slots].sort((a, b) => a.start - b.start);
|
|
5137
|
+
const starts = [];
|
|
5138
|
+
const ends = [];
|
|
5139
|
+
let prevEnd = docStart;
|
|
5140
|
+
for (const s of sorted) {
|
|
5141
|
+
if (!isFinite(s.start) || !isFinite(s.end) || s.end < s.start) {
|
|
5142
|
+
throw new RangeError(`OffsetIndex: invalid slot [${s.start}, ${s.end}]`);
|
|
5143
|
+
}
|
|
5144
|
+
if (s.start < prevEnd) {
|
|
5145
|
+
throw new RangeError(`OffsetIndex: overlapping/out-of-order slot [${s.start}, ${s.end}] after end ${prevEnd}`);
|
|
5146
|
+
}
|
|
5147
|
+
starts.push(s.start);
|
|
5148
|
+
ends.push(s.end);
|
|
5149
|
+
prevEnd = s.end;
|
|
5150
|
+
}
|
|
5151
|
+
this._starts = starts;
|
|
5152
|
+
this._ends = ends;
|
|
5153
|
+
this.count = starts.length;
|
|
5154
|
+
this.docStart = docStart;
|
|
5155
|
+
this.docEnd = docEnd ?? (this.count ? Math.max(prevEnd, ends[ends.length - 1]) : docStart);
|
|
5156
|
+
}
|
|
5157
|
+
/** The slot at index `i` (0-based, source order), or undefined. */
|
|
5158
|
+
slot(i) {
|
|
5159
|
+
if (i < 0 || i >= this.count) return void 0;
|
|
5160
|
+
return { start: this._starts[i], end: this._ends[i] };
|
|
5161
|
+
}
|
|
5162
|
+
/**
|
|
5163
|
+
* Index of the gap that CONTAINS `offset`, in `[0, count]`. Gap `i` spans
|
|
5164
|
+
* `[endOf(i-1), startOf(i))` where `endOf(-1)=docStart` and `startOf(count)=docEnd`.
|
|
5165
|
+
* An offset that lands inside a slot returns that slot's TRAILING gap index
|
|
5166
|
+
* (i.e. the gap after it) — callers querying at slot boundaries get the
|
|
5167
|
+
* intuitive answer via `gapBefore`/`gapAfter`.
|
|
5168
|
+
*/
|
|
5169
|
+
gapIndexAt(offset) {
|
|
5170
|
+
const i = floorIndex(this._starts, this.count, offset);
|
|
5171
|
+
if (i < 0) return 0;
|
|
5172
|
+
if (offset < this._ends[i]) return i + 1;
|
|
5173
|
+
return i + 1;
|
|
5174
|
+
}
|
|
5175
|
+
/** The gap at index `i` in `[0, count]`. */
|
|
5176
|
+
gap(i) {
|
|
5177
|
+
if (i < 0 || i > this.count) return void 0;
|
|
5178
|
+
const start = i === 0 ? this.docStart : this._ends[i - 1];
|
|
5179
|
+
const end = i === this.count ? this.docEnd : this._starts[i];
|
|
5180
|
+
return { start, end };
|
|
5181
|
+
}
|
|
5182
|
+
/**
|
|
5183
|
+
* The trivia gap immediately BEFORE `offset` (`[prevSlotEnd, offset)`), where
|
|
5184
|
+
* `prevSlotEnd` is the end of the last slot ending at or before `offset` (or
|
|
5185
|
+
* `docStart`). For a real slot start this is the gap preceding that slot; for an
|
|
5186
|
+
* adjacent slot the gap is empty and agrees with `gapAfter` on the same seam.
|
|
5187
|
+
*/
|
|
5188
|
+
gapBefore(offset) {
|
|
5189
|
+
const e = floorIndex(this._ends, this.count, offset);
|
|
5190
|
+
const start = e >= 0 ? this._ends[e] : this.docStart;
|
|
5191
|
+
return { start, end: offset };
|
|
5192
|
+
}
|
|
5193
|
+
/**
|
|
5194
|
+
* The trivia gap immediately AFTER `offset` (`[offset, nextSlotStart)`), where
|
|
5195
|
+
* `nextSlotStart` is the least slot start at or after `offset` (or `docEnd`).
|
|
5196
|
+
*/
|
|
5197
|
+
gapAfter(offset) {
|
|
5198
|
+
return { start: offset, end: this._ceilStart(offset) };
|
|
5199
|
+
}
|
|
5200
|
+
/** Least slot start `>= offset`, else `docEnd`. */
|
|
5201
|
+
_ceilStart(offset) {
|
|
5202
|
+
let lo = 0;
|
|
5203
|
+
let hi = this.count - 1;
|
|
5204
|
+
let ans = this.docEnd;
|
|
5205
|
+
while (lo <= hi) {
|
|
5206
|
+
const mid = lo + hi >>> 1;
|
|
5207
|
+
if (this._starts[mid] >= offset) {
|
|
5208
|
+
ans = this._starts[mid];
|
|
5209
|
+
hi = mid - 1;
|
|
5210
|
+
} else {
|
|
5211
|
+
lo = mid + 1;
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
return ans;
|
|
5215
|
+
}
|
|
5216
|
+
/** Is this gap empty (adjacent slots, no trivia)? */
|
|
5217
|
+
isEmpty(gap) {
|
|
5218
|
+
return gap.end <= gap.start;
|
|
5219
|
+
}
|
|
5220
|
+
};
|
|
5221
|
+
var NL = 10;
|
|
5222
|
+
var CR = 13;
|
|
5223
|
+
var SP = 32;
|
|
5224
|
+
var TAB = 9;
|
|
5225
|
+
function gapText(source, gap) {
|
|
5226
|
+
return gap.end > gap.start ? source.slice(gap.start, gap.end) : "";
|
|
5227
|
+
}
|
|
5228
|
+
function lineBreaksIn(source, gap) {
|
|
5229
|
+
let n = 0;
|
|
5230
|
+
for (let i = gap.start; i < gap.end; i++) {
|
|
5231
|
+
const c = source.charCodeAt(i);
|
|
5232
|
+
if (c === NL) n++;
|
|
5233
|
+
else if (c === CR && source.charCodeAt(i + 1) !== NL) n++;
|
|
5234
|
+
}
|
|
5235
|
+
return n;
|
|
5236
|
+
}
|
|
5237
|
+
function blankLinesIn(source, gap) {
|
|
5238
|
+
return Math.max(0, lineBreaksIn(source, gap) - 1);
|
|
5239
|
+
}
|
|
5240
|
+
function lineStartWithin(source, gap) {
|
|
5241
|
+
for (let i = gap.end - 1; i >= gap.start; i--) {
|
|
5242
|
+
const c = source.charCodeAt(i);
|
|
5243
|
+
if (c === NL || c === CR) return i + 1;
|
|
5244
|
+
}
|
|
5245
|
+
return gap.start;
|
|
5246
|
+
}
|
|
5247
|
+
function indentWidth(source, gap) {
|
|
5248
|
+
const ls = lineStartWithin(source, gap);
|
|
5249
|
+
if (ls === gap.start) return 0;
|
|
5250
|
+
return gap.end - ls;
|
|
5251
|
+
}
|
|
5252
|
+
function indentMixed(source, gap) {
|
|
5253
|
+
const ls = lineStartWithin(source, gap);
|
|
5254
|
+
let sawSpace = false;
|
|
5255
|
+
let sawTab = false;
|
|
5256
|
+
for (let i = ls; i < gap.end; i++) {
|
|
5257
|
+
const c = source.charCodeAt(i);
|
|
5258
|
+
if (c === SP) sawSpace = true;
|
|
5259
|
+
else if (c === TAB) sawTab = true;
|
|
5260
|
+
}
|
|
5261
|
+
return sawSpace && sawTab;
|
|
5262
|
+
}
|
|
5263
|
+
function commentsIn(source, gap, comment) {
|
|
5264
|
+
const re = new RegExp(comment.source, comment.flags.replace(/[gy]/g, "") + "g");
|
|
5265
|
+
re.lastIndex = gap.start;
|
|
5266
|
+
const out = [];
|
|
5267
|
+
let m;
|
|
5268
|
+
while ((m = re.exec(source)) && m.index < gap.end) {
|
|
5269
|
+
if (m[0].length === 0) {
|
|
5270
|
+
re.lastIndex++;
|
|
5271
|
+
continue;
|
|
5272
|
+
}
|
|
5273
|
+
if (m.index + m[0].length > gap.end) break;
|
|
5274
|
+
out.push({ start: m.index, end: m.index + m[0].length });
|
|
5275
|
+
}
|
|
5276
|
+
return out;
|
|
5277
|
+
}
|
|
5278
|
+
function gapIsSignificant(source, gap, comment) {
|
|
5279
|
+
if (gap.end <= gap.start) return false;
|
|
5280
|
+
if (lineBreaksIn(source, gap) > 0) return true;
|
|
5281
|
+
if (comment && commentsIn(source, gap, comment).length > 0) return true;
|
|
5282
|
+
return false;
|
|
5283
|
+
}
|
|
5284
|
+
function collectLeafSlots(root) {
|
|
5285
|
+
const slots = [];
|
|
5286
|
+
const visit = (n) => {
|
|
5287
|
+
const kids = n.children;
|
|
5288
|
+
if (kids && kids.length) {
|
|
5289
|
+
for (const c of kids) visit(c);
|
|
5290
|
+
} else if (n.span) {
|
|
5291
|
+
slots.push({ start: n.span.start, end: n.span.end });
|
|
5292
|
+
}
|
|
5293
|
+
};
|
|
5294
|
+
if (root) visit(root);
|
|
5295
|
+
return slots;
|
|
5296
|
+
}
|
|
5297
|
+
function buildOffsetIndex(root, input = "") {
|
|
5298
|
+
return new OffsetIndex(collectLeafSlots(root), 0, input.length);
|
|
5299
|
+
}
|
|
5300
|
+
|
|
5301
|
+
// src/cst/relative-spans.ts
|
|
5302
|
+
function relativize(node2, parentBase = 0) {
|
|
5303
|
+
return {
|
|
5304
|
+
start: node2.start - parentBase,
|
|
5305
|
+
end: node2.end - parentBase,
|
|
5306
|
+
children: node2.children.map((c) => relativize(c, node2.start))
|
|
5307
|
+
};
|
|
5308
|
+
}
|
|
5309
|
+
function absolutize(node2, parentBase = 0) {
|
|
5310
|
+
const start = parentBase + node2.start;
|
|
5311
|
+
return {
|
|
5312
|
+
start,
|
|
5313
|
+
end: parentBase + node2.end,
|
|
5314
|
+
children: node2.children.map((c) => absolutize(c, start))
|
|
5315
|
+
};
|
|
5316
|
+
}
|
|
5317
|
+
function absoluteSpanAt(root, path) {
|
|
5318
|
+
let base = 0;
|
|
5319
|
+
let node2 = root;
|
|
5320
|
+
for (const idx of path) {
|
|
5321
|
+
base += node2.start;
|
|
5322
|
+
const child = node2.children[idx];
|
|
5323
|
+
if (!child) throw new RangeError(`absoluteSpanAt: no child ${idx} on path ${path.join(".")}`);
|
|
5324
|
+
node2 = child;
|
|
5325
|
+
}
|
|
5326
|
+
return { start: base + node2.start, end: base + node2.end };
|
|
5327
|
+
}
|
|
5328
|
+
function shiftAbsolute(node2, at, delta) {
|
|
5329
|
+
const s = node2.start >= at ? node2.start + delta : node2.start;
|
|
5330
|
+
const e = node2.end >= at ? node2.end + delta : node2.end;
|
|
5331
|
+
return {
|
|
5332
|
+
start: s,
|
|
5333
|
+
end: e,
|
|
5334
|
+
children: node2.children.map((c) => shiftAbsolute(c, at, delta))
|
|
5335
|
+
};
|
|
5336
|
+
}
|
|
5337
|
+
function applyEdit(root, at, delta, stats) {
|
|
5338
|
+
return editNode(root, at, delta, 0, 0, stats);
|
|
5339
|
+
}
|
|
5340
|
+
function editNode(node2, at, delta, parentBase, parentShift, stats) {
|
|
5341
|
+
const absStart = parentBase + node2.start;
|
|
5342
|
+
const absEnd = parentBase + node2.end;
|
|
5343
|
+
if (absEnd < at) return node2;
|
|
5344
|
+
const startShifts = absStart >= at;
|
|
5345
|
+
if (startShifts && parentShift === delta) return node2;
|
|
5346
|
+
if (stats) stats.allocated++;
|
|
5347
|
+
const newAbsStart = startShifts ? absStart + delta : absStart;
|
|
5348
|
+
const newAbsEnd = absEnd + delta;
|
|
5349
|
+
const newParentBase = parentBase + parentShift;
|
|
5350
|
+
const newStart = newAbsStart - newParentBase;
|
|
5351
|
+
const newEnd = newAbsEnd - newParentBase;
|
|
5352
|
+
const ownShift = newAbsStart - absStart;
|
|
5353
|
+
let changed = false;
|
|
5354
|
+
const newChildren = [];
|
|
5355
|
+
for (const c of node2.children) {
|
|
5356
|
+
const nc = editNode(c, at, delta, absStart, ownShift, stats);
|
|
5357
|
+
if (nc !== c) changed = true;
|
|
5358
|
+
newChildren.push(nc);
|
|
5359
|
+
}
|
|
5360
|
+
return {
|
|
5361
|
+
start: newStart,
|
|
5362
|
+
end: newEnd,
|
|
5363
|
+
children: changed ? newChildren : node2.children
|
|
5364
|
+
};
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5367
|
+
// src/index.ts
|
|
5368
|
+
registerRegexAnalyzer(firstSetFromRegex);
|
|
3924
5369
|
export {
|
|
5370
|
+
OffsetIndex,
|
|
5371
|
+
absoluteSpanAt,
|
|
5372
|
+
absolutize,
|
|
3925
5373
|
annotateSpan,
|
|
5374
|
+
applyEdit,
|
|
3926
5375
|
balanced,
|
|
5376
|
+
blankLinesIn,
|
|
3927
5377
|
buildLineIndex,
|
|
5378
|
+
buildOffsetIndex,
|
|
3928
5379
|
buildTriviaIndex,
|
|
3929
5380
|
choice,
|
|
5381
|
+
collectLeafSlots,
|
|
5382
|
+
commentsIn,
|
|
3930
5383
|
compile,
|
|
3931
5384
|
completionsAt,
|
|
5385
|
+
compose,
|
|
3932
5386
|
createVisitor,
|
|
5387
|
+
cstBuildHost,
|
|
3933
5388
|
expect,
|
|
5389
|
+
gapIsSignificant,
|
|
5390
|
+
gapText,
|
|
3934
5391
|
guard,
|
|
5392
|
+
indentMixed,
|
|
5393
|
+
indentWidth,
|
|
3935
5394
|
isParseError,
|
|
3936
5395
|
keywords,
|
|
3937
5396
|
label,
|
|
5397
|
+
lineBreaksIn,
|
|
5398
|
+
lineStartWithin,
|
|
3938
5399
|
literal,
|
|
3939
|
-
makeFunctionalDoc,
|
|
3940
5400
|
makeWord,
|
|
3941
5401
|
many,
|
|
3942
5402
|
manyRecover,
|
|
@@ -3947,15 +5407,20 @@ export {
|
|
|
3947
5407
|
oneOrMore,
|
|
3948
5408
|
optional,
|
|
3949
5409
|
parse,
|
|
5410
|
+
parseDoc,
|
|
3950
5411
|
parser,
|
|
5412
|
+
pick,
|
|
3951
5413
|
recover,
|
|
3952
5414
|
ref,
|
|
3953
5415
|
regex,
|
|
5416
|
+
relativize,
|
|
3954
5417
|
rules,
|
|
5418
|
+
run,
|
|
3955
5419
|
scanTo,
|
|
3956
5420
|
sepBy,
|
|
3957
5421
|
sepByRecover,
|
|
3958
5422
|
sequence,
|
|
5423
|
+
shiftAbsolute,
|
|
3959
5424
|
skip,
|
|
3960
5425
|
staticExpected,
|
|
3961
5426
|
transform,
|