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.cjs
CHANGED
|
@@ -30,21 +30,36 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
OffsetIndex: () => OffsetIndex,
|
|
34
|
+
absoluteSpanAt: () => absoluteSpanAt,
|
|
35
|
+
absolutize: () => absolutize,
|
|
33
36
|
annotateSpan: () => annotateSpan,
|
|
37
|
+
applyEdit: () => applyEdit,
|
|
34
38
|
balanced: () => balanced,
|
|
39
|
+
blankLinesIn: () => blankLinesIn,
|
|
35
40
|
buildLineIndex: () => buildLineIndex,
|
|
41
|
+
buildOffsetIndex: () => buildOffsetIndex,
|
|
36
42
|
buildTriviaIndex: () => buildTriviaIndex,
|
|
37
43
|
choice: () => choice,
|
|
44
|
+
collectLeafSlots: () => collectLeafSlots,
|
|
45
|
+
commentsIn: () => commentsIn,
|
|
38
46
|
compile: () => compile,
|
|
39
47
|
completionsAt: () => completionsAt,
|
|
48
|
+
compose: () => compose,
|
|
40
49
|
createVisitor: () => createVisitor,
|
|
50
|
+
cstBuildHost: () => cstBuildHost,
|
|
41
51
|
expect: () => expect,
|
|
52
|
+
gapIsSignificant: () => gapIsSignificant,
|
|
53
|
+
gapText: () => gapText,
|
|
42
54
|
guard: () => guard,
|
|
55
|
+
indentMixed: () => indentMixed,
|
|
56
|
+
indentWidth: () => indentWidth,
|
|
43
57
|
isParseError: () => isParseError,
|
|
44
58
|
keywords: () => keywords,
|
|
45
59
|
label: () => label,
|
|
60
|
+
lineBreaksIn: () => lineBreaksIn,
|
|
61
|
+
lineStartWithin: () => lineStartWithin,
|
|
46
62
|
literal: () => literal,
|
|
47
|
-
makeFunctionalDoc: () => makeFunctionalDoc,
|
|
48
63
|
makeWord: () => makeWord,
|
|
49
64
|
many: () => many,
|
|
50
65
|
manyRecover: () => manyRecover,
|
|
@@ -55,15 +70,20 @@ __export(index_exports, {
|
|
|
55
70
|
oneOrMore: () => oneOrMore,
|
|
56
71
|
optional: () => optional,
|
|
57
72
|
parse: () => parse,
|
|
73
|
+
parseDoc: () => parseDoc,
|
|
58
74
|
parser: () => parser,
|
|
75
|
+
pick: () => pick,
|
|
59
76
|
recover: () => recover,
|
|
60
77
|
ref: () => ref,
|
|
61
78
|
regex: () => regex,
|
|
79
|
+
relativize: () => relativize,
|
|
62
80
|
rules: () => rules,
|
|
81
|
+
run: () => run,
|
|
63
82
|
scanTo: () => scanTo,
|
|
64
83
|
sepBy: () => sepBy,
|
|
65
84
|
sepByRecover: () => sepByRecover,
|
|
66
85
|
sequence: () => sequence,
|
|
86
|
+
shiftAbsolute: () => shiftAbsolute,
|
|
67
87
|
skip: () => skip,
|
|
68
88
|
staticExpected: () => staticExpected,
|
|
69
89
|
transform: () => transform,
|
|
@@ -328,6 +348,36 @@ function literal(value, opts = {}) {
|
|
|
328
348
|
}
|
|
329
349
|
|
|
330
350
|
// src/combinators/regex.ts
|
|
351
|
+
var firstSetAnalyzer = null;
|
|
352
|
+
function registerRegexAnalyzer(analyzer) {
|
|
353
|
+
firstSetAnalyzer = analyzer;
|
|
354
|
+
}
|
|
355
|
+
var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
|
|
356
|
+
function regex(pattern, flags = "") {
|
|
357
|
+
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
358
|
+
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
359
|
+
const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
360
|
+
const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
|
|
361
|
+
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
362
|
+
return {
|
|
363
|
+
_tag: "regex",
|
|
364
|
+
_meta: meta,
|
|
365
|
+
_def: { tag: "regex", source, flags: resolvedFlags },
|
|
366
|
+
parse(input, pos, ctx) {
|
|
367
|
+
anchored.lastIndex = pos;
|
|
368
|
+
const m = anchored.exec(input);
|
|
369
|
+
if (m === null) {
|
|
370
|
+
return failAt(ctx, [`/${source}/`], pos);
|
|
371
|
+
}
|
|
372
|
+
const span = { start: pos, end: pos + m[0].length };
|
|
373
|
+
const leaf = { _tag: "leaf", value: m[0], span };
|
|
374
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
375
|
+
return { ok: true, value: m[0], span };
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/combinators/regex-analyze.ts
|
|
331
381
|
var import_regexp_tree = __toESM(require("regexp-tree"), 1);
|
|
332
382
|
function firstSetFromRegex(pattern) {
|
|
333
383
|
try {
|
|
@@ -432,40 +482,6 @@ function extractFirstSet(node2) {
|
|
|
432
482
|
return { firstSet: any(), canMatchNewline: true };
|
|
433
483
|
}
|
|
434
484
|
}
|
|
435
|
-
function optimizeRegex(source, flags) {
|
|
436
|
-
try {
|
|
437
|
-
const result = import_regexp_tree.default.optimize(`/${source}/${flags}`);
|
|
438
|
-
const str = result.toString();
|
|
439
|
-
const lastSlash = str.lastIndexOf("/");
|
|
440
|
-
return str.slice(1, lastSlash);
|
|
441
|
-
} catch {
|
|
442
|
-
return source;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
function regex(pattern, flags = "") {
|
|
446
|
-
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
447
|
-
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
448
|
-
const optimizedSource = optimizeRegex(source, resolvedFlags);
|
|
449
|
-
const anchored = new RegExp(optimizedSource, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
450
|
-
const { firstSet, canMatchNewline } = firstSetFromRegex(source);
|
|
451
|
-
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
452
|
-
return {
|
|
453
|
-
_tag: "regex",
|
|
454
|
-
_meta: meta,
|
|
455
|
-
_def: { tag: "regex", source, flags: resolvedFlags, optimizedSource },
|
|
456
|
-
parse(input, pos, ctx) {
|
|
457
|
-
anchored.lastIndex = pos;
|
|
458
|
-
const m = anchored.exec(input);
|
|
459
|
-
if (m === null) {
|
|
460
|
-
return failAt(ctx, [`/${source}/`], pos);
|
|
461
|
-
}
|
|
462
|
-
const span = { start: pos, end: pos + m[0].length };
|
|
463
|
-
const leaf = { _tag: "leaf", value: m[0], span };
|
|
464
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
465
|
-
return { ok: true, value: m[0], span };
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
485
|
|
|
470
486
|
// src/combinators/keywords.ts
|
|
471
487
|
function word(str, boundary = "_0-9A-Za-z") {
|
|
@@ -1163,7 +1179,9 @@ function rules(factory) {
|
|
|
1163
1179
|
if (typeof key !== "string") return void 0;
|
|
1164
1180
|
const record = target;
|
|
1165
1181
|
if (!(key in record)) {
|
|
1166
|
-
|
|
1182
|
+
const r = ref();
|
|
1183
|
+
r._ruleName = key;
|
|
1184
|
+
record[key] = r;
|
|
1167
1185
|
}
|
|
1168
1186
|
return record[key];
|
|
1169
1187
|
}
|
|
@@ -1228,12 +1246,14 @@ function confirmedBuildArity(src) {
|
|
|
1228
1246
|
return parts.length;
|
|
1229
1247
|
}
|
|
1230
1248
|
function buildReadsTrivia(def) {
|
|
1249
|
+
if (!def.build) return true;
|
|
1231
1250
|
const src = def.buildSrc ?? def.build.toString();
|
|
1232
1251
|
const arity = confirmedBuildArity(src);
|
|
1233
1252
|
if (arity === null) return true;
|
|
1234
1253
|
return arity >= 4;
|
|
1235
1254
|
}
|
|
1236
1255
|
function buildReadsState(def) {
|
|
1256
|
+
if (!def.build) return true;
|
|
1237
1257
|
const src = def.buildSrc ?? def.build.toString();
|
|
1238
1258
|
const arity = confirmedBuildArity(src);
|
|
1239
1259
|
if (arity === null) return true;
|
|
@@ -1252,8 +1272,8 @@ function node(type, combinator, build, opts) {
|
|
|
1252
1272
|
};
|
|
1253
1273
|
const collapse = opts?.collapse === true;
|
|
1254
1274
|
const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
|
|
1255
|
-
const capturesTrivia = buildReadsTrivia(def);
|
|
1256
|
-
const clonesState = buildReadsState(def);
|
|
1275
|
+
const capturesTrivia = build ? buildReadsTrivia(def) : true;
|
|
1276
|
+
const clonesState = build ? buildReadsState(def) : true;
|
|
1257
1277
|
return {
|
|
1258
1278
|
_tag: "node",
|
|
1259
1279
|
_meta: meta,
|
|
@@ -1264,13 +1284,8 @@ function node(type, combinator, build, opts) {
|
|
|
1264
1284
|
const r = combinator.parse(input, pos, ctx);
|
|
1265
1285
|
const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
1266
1286
|
if (!r.ok) return r;
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
rawChildren,
|
|
1270
|
-
r.span,
|
|
1271
|
-
triviaLog,
|
|
1272
|
-
clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
|
|
1273
|
-
);
|
|
1287
|
+
const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
1288
|
+
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 };
|
|
1274
1289
|
const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
|
|
1275
1290
|
const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
|
|
1276
1291
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
@@ -1445,8 +1460,14 @@ function staticExpected(c) {
|
|
|
1445
1460
|
case "transform":
|
|
1446
1461
|
case "not":
|
|
1447
1462
|
return staticExpected(def.parser);
|
|
1448
|
-
case "lazy":
|
|
1449
|
-
|
|
1463
|
+
case "lazy": {
|
|
1464
|
+
const name = c._ruleName;
|
|
1465
|
+
try {
|
|
1466
|
+
return staticExpected(def.thunk());
|
|
1467
|
+
} catch {
|
|
1468
|
+
return name ? [name] : [];
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1450
1471
|
default:
|
|
1451
1472
|
return [];
|
|
1452
1473
|
}
|
|
@@ -1476,8 +1497,29 @@ function expect(combinator, label2) {
|
|
|
1476
1497
|
var BACKSLASH = 92;
|
|
1477
1498
|
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1478
1499
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1500
|
+
var SPACE_RANGES = [
|
|
1501
|
+
[9, 13],
|
|
1502
|
+
[32, 32],
|
|
1503
|
+
[160, 160],
|
|
1504
|
+
[5760, 5760],
|
|
1505
|
+
[8192, 8202],
|
|
1506
|
+
[8232, 8232],
|
|
1507
|
+
[8233, 8233],
|
|
1508
|
+
[8239, 8239],
|
|
1509
|
+
[8287, 8287],
|
|
1510
|
+
[12288, 12288],
|
|
1511
|
+
[65279, 65279]
|
|
1512
|
+
];
|
|
1479
1513
|
function shorthandRanges(ch) {
|
|
1480
|
-
|
|
1514
|
+
if (ch === "d") return [[48, 57]];
|
|
1515
|
+
if (ch === "s") return SPACE_RANGES;
|
|
1516
|
+
return [[48, 57], [65, 90], [97, 122], [95, 95]];
|
|
1517
|
+
}
|
|
1518
|
+
function readUnicodeEscape(body, i) {
|
|
1519
|
+
if (body[i] !== "\\" || body[i + 1] !== "u") return null;
|
|
1520
|
+
const hex = body.slice(i + 2, i + 6);
|
|
1521
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
|
|
1522
|
+
return { cp: Number.parseInt(hex, 16), next: i + 6 };
|
|
1481
1523
|
}
|
|
1482
1524
|
function parseClassRanges(body) {
|
|
1483
1525
|
const ranges = [];
|
|
@@ -1486,11 +1528,16 @@ function parseClassRanges(body) {
|
|
|
1486
1528
|
const ch = body[i];
|
|
1487
1529
|
if (ch === void 0) return null;
|
|
1488
1530
|
if (ch === "\\") {
|
|
1531
|
+
const uni = readUnicodeEscape(body, i);
|
|
1532
|
+
if (uni) {
|
|
1533
|
+
i = uni.next;
|
|
1534
|
+
return { cp: uni.cp };
|
|
1535
|
+
}
|
|
1489
1536
|
const e = body[i + 1];
|
|
1490
1537
|
if (e === void 0) return null;
|
|
1491
1538
|
i += 2;
|
|
1492
1539
|
if (e in CLASS_ESCAPES) return { cp: CLASS_ESCAPES[e] };
|
|
1493
|
-
if (e === "d" || e === "w") return { set: shorthandRanges(e) };
|
|
1540
|
+
if (e === "d" || e === "w" || e === "s") return { set: shorthandRanges(e) };
|
|
1494
1541
|
if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1495
1542
|
return { cp: e.codePointAt(0) };
|
|
1496
1543
|
}
|
|
@@ -1518,10 +1565,16 @@ function parseClassRanges(body) {
|
|
|
1518
1565
|
function classToRanges(cls) {
|
|
1519
1566
|
if (cls === "\\d") return shorthandRanges("d");
|
|
1520
1567
|
if (cls === "\\w") return shorthandRanges("w");
|
|
1568
|
+
if (cls === "\\s") return shorthandRanges("s");
|
|
1521
1569
|
const body = cls.slice(1, -1);
|
|
1522
1570
|
if (body.startsWith("^")) return null;
|
|
1523
1571
|
return parseClassRanges(body);
|
|
1524
1572
|
}
|
|
1573
|
+
var foldEq = (cVar, cp) => {
|
|
1574
|
+
if (cp >= 65 && cp <= 90) return `(${cVar} === ${cp} || ${cVar} === ${cp + 32})`;
|
|
1575
|
+
if (cp >= 97 && cp <= 122) return `(${cVar} === ${cp} || ${cVar} === ${cp - 32})`;
|
|
1576
|
+
return `${cVar} === ${cp}`;
|
|
1577
|
+
};
|
|
1525
1578
|
function literalCodePoints(frag) {
|
|
1526
1579
|
const out = [];
|
|
1527
1580
|
let i = 0;
|
|
@@ -1568,22 +1621,612 @@ function parseStringShape(source) {
|
|
|
1568
1621
|
if (!inSet(qcp) || !inSet(BACKSLASH)) return null;
|
|
1569
1622
|
return { kind: "string", quote: qcp, excluded, escLineTerm: esc !== "." };
|
|
1570
1623
|
}
|
|
1624
|
+
function parseSeqParts(source) {
|
|
1625
|
+
const parts = [];
|
|
1626
|
+
let lit = [];
|
|
1627
|
+
const flush = () => {
|
|
1628
|
+
if (lit.length) {
|
|
1629
|
+
parts.push({ part: "lit", cps: lit, optional: false });
|
|
1630
|
+
lit = [];
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
const runFrom = (ranges, negated, q) => {
|
|
1634
|
+
if (q === "+") {
|
|
1635
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: true });
|
|
1636
|
+
return 1;
|
|
1637
|
+
}
|
|
1638
|
+
if (q === "*") {
|
|
1639
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: true });
|
|
1640
|
+
return 1;
|
|
1641
|
+
}
|
|
1642
|
+
if (q === "?") {
|
|
1643
|
+
parts.push({ part: "run", ranges, negated, min: 0, unbounded: false });
|
|
1644
|
+
return 1;
|
|
1645
|
+
}
|
|
1646
|
+
parts.push({ part: "run", ranges, negated, min: 1, unbounded: false });
|
|
1647
|
+
return 0;
|
|
1648
|
+
};
|
|
1649
|
+
let i = 0;
|
|
1650
|
+
while (i < source.length) {
|
|
1651
|
+
const ch = source[i];
|
|
1652
|
+
if (ch === "[") {
|
|
1653
|
+
let k = i + 1;
|
|
1654
|
+
const neg = source[k] === "^";
|
|
1655
|
+
if (neg) k++;
|
|
1656
|
+
let body = "";
|
|
1657
|
+
while (k < source.length && source[k] !== "]") {
|
|
1658
|
+
if (source[k] === "\\") {
|
|
1659
|
+
body += source[k] + (source[k + 1] ?? "");
|
|
1660
|
+
k += 2;
|
|
1661
|
+
} else {
|
|
1662
|
+
body += source[k];
|
|
1663
|
+
k++;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
if (source[k] !== "]") return null;
|
|
1667
|
+
const ranges = parseClassRanges(body);
|
|
1668
|
+
if (!ranges) return null;
|
|
1669
|
+
flush();
|
|
1670
|
+
i = k + 1;
|
|
1671
|
+
i += runFrom(ranges, neg, source[i]);
|
|
1672
|
+
continue;
|
|
1673
|
+
}
|
|
1674
|
+
if (ch === "\\") {
|
|
1675
|
+
const e = source[i + 1];
|
|
1676
|
+
if (e === "d" || e === "w" || e === "s") {
|
|
1677
|
+
flush();
|
|
1678
|
+
i += 2;
|
|
1679
|
+
i += runFrom(shorthandRanges(e), false, source[i]);
|
|
1680
|
+
continue;
|
|
1681
|
+
}
|
|
1682
|
+
const uni = readUnicodeEscape(source, i);
|
|
1683
|
+
let cp2;
|
|
1684
|
+
if (uni) {
|
|
1685
|
+
cp2 = uni.cp;
|
|
1686
|
+
i = uni.next;
|
|
1687
|
+
} else if (e === void 0) return null;
|
|
1688
|
+
else if (e in CLASS_ESCAPES) {
|
|
1689
|
+
cp2 = CLASS_ESCAPES[e];
|
|
1690
|
+
i += 2;
|
|
1691
|
+
} else if (e >= "a" && e <= "z" || e >= "A" && e <= "Z") return null;
|
|
1692
|
+
else {
|
|
1693
|
+
cp2 = e.codePointAt(0);
|
|
1694
|
+
i += 2;
|
|
1695
|
+
}
|
|
1696
|
+
if (source[i] === "?") {
|
|
1697
|
+
flush();
|
|
1698
|
+
parts.push({ part: "lit", cps: [cp2], optional: true });
|
|
1699
|
+
i++;
|
|
1700
|
+
} else lit.push(cp2);
|
|
1701
|
+
continue;
|
|
1702
|
+
}
|
|
1703
|
+
if (ch === "(") {
|
|
1704
|
+
if (source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
1705
|
+
let k = i + 3;
|
|
1706
|
+
let depth = 1;
|
|
1707
|
+
while (k < source.length && depth > 0) {
|
|
1708
|
+
const c2 = source[k];
|
|
1709
|
+
if (c2 === "\\") {
|
|
1710
|
+
k += 2;
|
|
1711
|
+
continue;
|
|
1712
|
+
}
|
|
1713
|
+
if (c2 === "[") {
|
|
1714
|
+
let j = k + 1;
|
|
1715
|
+
if (source[j] === "^") j++;
|
|
1716
|
+
while (j < source.length && source[j] !== "]") {
|
|
1717
|
+
if (source[j] === "\\") j += 2;
|
|
1718
|
+
else j++;
|
|
1719
|
+
}
|
|
1720
|
+
if (source[j] !== "]") return null;
|
|
1721
|
+
k = j + 1;
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
if (c2 === "(") depth++;
|
|
1725
|
+
else if (c2 === ")") depth--;
|
|
1726
|
+
k++;
|
|
1727
|
+
}
|
|
1728
|
+
if (depth !== 0) return null;
|
|
1729
|
+
const body = source.slice(i + 3, k - 1);
|
|
1730
|
+
const inner = parseScanShape(body);
|
|
1731
|
+
if (!inner || !groupInnerSafe(inner)) return null;
|
|
1732
|
+
const q = source[k];
|
|
1733
|
+
if (q === "{") return null;
|
|
1734
|
+
flush();
|
|
1735
|
+
if (q === "+") {
|
|
1736
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1737
|
+
parts.push({ part: "group", inner, min: 1, unbounded: true });
|
|
1738
|
+
i = k + 1;
|
|
1739
|
+
} else if (q === "*") {
|
|
1740
|
+
if (shapeCanBeEmpty(inner)) return null;
|
|
1741
|
+
parts.push({ part: "group", inner, min: 0, unbounded: true });
|
|
1742
|
+
i = k + 1;
|
|
1743
|
+
} else if (q === "?") {
|
|
1744
|
+
parts.push({ part: "group", inner, min: 0, unbounded: false });
|
|
1745
|
+
i = k + 1;
|
|
1746
|
+
} else {
|
|
1747
|
+
parts.push({ part: "group", inner, min: 1, unbounded: false });
|
|
1748
|
+
i = k;
|
|
1749
|
+
}
|
|
1750
|
+
continue;
|
|
1751
|
+
}
|
|
1752
|
+
if (META.has(ch)) return null;
|
|
1753
|
+
const cp = ch.codePointAt(0);
|
|
1754
|
+
i += ch.length;
|
|
1755
|
+
if (source[i] === "?") {
|
|
1756
|
+
flush();
|
|
1757
|
+
parts.push({ part: "lit", cps: [cp], optional: true });
|
|
1758
|
+
i++;
|
|
1759
|
+
} else lit.push(cp);
|
|
1760
|
+
}
|
|
1761
|
+
flush();
|
|
1762
|
+
return parts.length ? parts : null;
|
|
1763
|
+
}
|
|
1764
|
+
function seqIsUnambiguous(parts) {
|
|
1765
|
+
let hasMandatory = false;
|
|
1766
|
+
for (let k = 0; k < parts.length; k++) {
|
|
1767
|
+
const p = parts[k];
|
|
1768
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1769
|
+
if (mandatory) hasMandatory = true;
|
|
1770
|
+
if (p.part === "group") {
|
|
1771
|
+
const exposure = groupPartExposure(p);
|
|
1772
|
+
if (exposure === "unsupported") {
|
|
1773
|
+
const trailingOnce = k === parts.length - 1 && p.min === 1 && !p.unbounded;
|
|
1774
|
+
if (!trailingOnce) return false;
|
|
1775
|
+
} else if (exposure !== null) {
|
|
1776
|
+
if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1780
|
+
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1781
|
+
if (skippableSingle || isUnboundedRepeat) {
|
|
1782
|
+
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
1783
|
+
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
return hasMandatory;
|
|
1787
|
+
}
|
|
1788
|
+
function rangesOverlap(a, b) {
|
|
1789
|
+
return a.some(([lo, hi]) => b.some(([lo2, hi2]) => lo <= hi2 && lo2 <= hi));
|
|
1790
|
+
}
|
|
1791
|
+
function rangesSubset(sub, sup) {
|
|
1792
|
+
return sub.every(([lo, hi]) => sup.some(([lo2, hi2]) => lo >= lo2 && hi <= hi2));
|
|
1793
|
+
}
|
|
1794
|
+
function parseClassOperand(body) {
|
|
1795
|
+
if (body === "\\d" || body === "\\w" || body === "\\s") {
|
|
1796
|
+
return { ranges: shorthandRanges(body[1]), negated: false };
|
|
1797
|
+
}
|
|
1798
|
+
if (body.length >= 2 && body[0] === "[" && body[body.length - 1] === "]") {
|
|
1799
|
+
let inner = body.slice(1, -1);
|
|
1800
|
+
const negated = inner.startsWith("^");
|
|
1801
|
+
if (negated) inner = inner.slice(1);
|
|
1802
|
+
const ranges = parseClassRanges(inner);
|
|
1803
|
+
return ranges ? { ranges, negated } : null;
|
|
1804
|
+
}
|
|
1805
|
+
const cps = literalCodePoints(body);
|
|
1806
|
+
if (cps && cps.length === 1) return { ranges: [[cps[0], cps[0]]], negated: false };
|
|
1807
|
+
return null;
|
|
1808
|
+
}
|
|
1809
|
+
function classSubset(t, u) {
|
|
1810
|
+
if (!t.negated && !u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1811
|
+
if (!t.negated && u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1812
|
+
if (t.negated && !u.negated) return false;
|
|
1813
|
+
return rangesSubset(u.ranges, t.ranges);
|
|
1814
|
+
}
|
|
1815
|
+
function classDisjoint(t, u) {
|
|
1816
|
+
if (!t.negated && !u.negated) return !rangesOverlap(t.ranges, u.ranges);
|
|
1817
|
+
if (!t.negated && u.negated) return rangesSubset(t.ranges, u.ranges);
|
|
1818
|
+
if (t.negated && !u.negated) return rangesSubset(u.ranges, t.ranges);
|
|
1819
|
+
return false;
|
|
1820
|
+
}
|
|
1821
|
+
function firstSetsDisjoint(a, b) {
|
|
1822
|
+
if (a === "any" || b === "any") return false;
|
|
1823
|
+
return classDisjoint(a, b);
|
|
1824
|
+
}
|
|
1825
|
+
function fixedClassSeq(shape) {
|
|
1826
|
+
if (shape.kind !== "seq") return null;
|
|
1827
|
+
const out = [];
|
|
1828
|
+
for (const p of shape.parts) {
|
|
1829
|
+
if (p.part === "lit") {
|
|
1830
|
+
if (p.optional) return null;
|
|
1831
|
+
for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
|
|
1832
|
+
} else if (p.part === "run" && !p.unbounded && p.min === 1) {
|
|
1833
|
+
out.push({ ranges: p.ranges, negated: p.negated });
|
|
1834
|
+
} else {
|
|
1835
|
+
return null;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
return out.length ? out : null;
|
|
1839
|
+
}
|
|
1840
|
+
function altFixedMutuallyExclusive(shape) {
|
|
1841
|
+
if (shape.kind !== "alt" || shape.disjoint) return false;
|
|
1842
|
+
const lists = [];
|
|
1843
|
+
for (const arm of shape.arms) {
|
|
1844
|
+
const f = fixedClassSeq(arm);
|
|
1845
|
+
if (!f) return false;
|
|
1846
|
+
lists.push(f);
|
|
1847
|
+
}
|
|
1848
|
+
for (let i = 0; i < lists.length; i++) {
|
|
1849
|
+
for (let j = 0; j < lists.length; j++) {
|
|
1850
|
+
if (i === j) continue;
|
|
1851
|
+
const a = lists[i];
|
|
1852
|
+
const b = lists[j];
|
|
1853
|
+
if (a.length > b.length) continue;
|
|
1854
|
+
let disjointAt = false;
|
|
1855
|
+
for (let p = 0; p < a.length; p++) {
|
|
1856
|
+
if (classDisjoint(a[p], b[p])) {
|
|
1857
|
+
disjointAt = true;
|
|
1858
|
+
break;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
if (!disjointAt) return false;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
return true;
|
|
1865
|
+
}
|
|
1866
|
+
function groupPartExposure(part) {
|
|
1867
|
+
const innerWiggle = trailingBacktrackClass(part.inner);
|
|
1868
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1869
|
+
const requiredOnce = !part.unbounded && part.min === 1;
|
|
1870
|
+
if (requiredOnce) return innerWiggle;
|
|
1871
|
+
const dropExposed = shapeFirstAccept(part.inner);
|
|
1872
|
+
if (dropExposed === "any") return "unsupported";
|
|
1873
|
+
if (innerWiggle === null) return dropExposed;
|
|
1874
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1875
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1876
|
+
}
|
|
1877
|
+
function trailingBacktrackClass(shape) {
|
|
1878
|
+
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1879
|
+
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
1880
|
+
if (shape.kind === "litFold") return null;
|
|
1881
|
+
if (shape.kind === "seq") {
|
|
1882
|
+
const last = shape.parts[shape.parts.length - 1];
|
|
1883
|
+
if (last.part === "lit") return null;
|
|
1884
|
+
if (last.part === "run") {
|
|
1885
|
+
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1886
|
+
return null;
|
|
1887
|
+
}
|
|
1888
|
+
return groupPartExposure(last);
|
|
1889
|
+
}
|
|
1890
|
+
if (shape.kind === "alt") {
|
|
1891
|
+
if (!shape.disjoint) return altFixedMutuallyExclusive(shape) ? null : "unsupported";
|
|
1892
|
+
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1893
|
+
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1894
|
+
const concrete = classes.filter((c) => c !== null);
|
|
1895
|
+
if (concrete.length === 0) return null;
|
|
1896
|
+
const unioned = unionCharSets(concrete);
|
|
1897
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1898
|
+
}
|
|
1899
|
+
return "unsupported";
|
|
1900
|
+
}
|
|
1901
|
+
function lookaheadUnambiguous(inner, operand, negative) {
|
|
1902
|
+
const tail = trailingBacktrackClass(inner);
|
|
1903
|
+
if (tail === "unsupported") return false;
|
|
1904
|
+
if (tail === null) return true;
|
|
1905
|
+
return negative ? classSubset(tail, operand) : classDisjoint(tail, operand);
|
|
1906
|
+
}
|
|
1907
|
+
function stripTrailingLookahead(source) {
|
|
1908
|
+
if (!source.endsWith(")")) return null;
|
|
1909
|
+
for (const negative of [true, false]) {
|
|
1910
|
+
const lead = negative ? "(?!" : "(?=";
|
|
1911
|
+
const idx = source.lastIndexOf(lead);
|
|
1912
|
+
if (idx === -1) continue;
|
|
1913
|
+
const body = source.slice(idx + lead.length, -1);
|
|
1914
|
+
const operand = body.length ? parseClassOperand(body) : null;
|
|
1915
|
+
if (!operand) continue;
|
|
1916
|
+
return { base: source.slice(0, idx), ranges: operand.ranges, classNegated: operand.negated, negative };
|
|
1917
|
+
}
|
|
1918
|
+
return null;
|
|
1919
|
+
}
|
|
1920
|
+
function unwrapOuterGroup(source) {
|
|
1921
|
+
while (source.startsWith("(?:")) {
|
|
1922
|
+
let depth = 1;
|
|
1923
|
+
let i = 3;
|
|
1924
|
+
let ok = true;
|
|
1925
|
+
while (i < source.length && depth > 0) {
|
|
1926
|
+
const ch = source[i];
|
|
1927
|
+
if (ch === "\\") {
|
|
1928
|
+
i += 2;
|
|
1929
|
+
continue;
|
|
1930
|
+
}
|
|
1931
|
+
if (ch === "[") {
|
|
1932
|
+
let j = i + 1;
|
|
1933
|
+
if (source[j] === "^") j++;
|
|
1934
|
+
while (j < source.length && source[j] !== "]") {
|
|
1935
|
+
if (source[j] === "\\") j += 2;
|
|
1936
|
+
else j++;
|
|
1937
|
+
}
|
|
1938
|
+
if (source[j] !== "]") {
|
|
1939
|
+
ok = false;
|
|
1940
|
+
break;
|
|
1941
|
+
}
|
|
1942
|
+
i = j + 1;
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
if (ch === "(") depth++;
|
|
1946
|
+
else if (ch === ")") depth--;
|
|
1947
|
+
i++;
|
|
1948
|
+
}
|
|
1949
|
+
if (!ok || depth !== 0 || i !== source.length) break;
|
|
1950
|
+
source = source.slice(3, -1);
|
|
1951
|
+
}
|
|
1952
|
+
return source;
|
|
1953
|
+
}
|
|
1954
|
+
function splitTopLevelAlternation(source) {
|
|
1955
|
+
const body = unwrapOuterGroup(source);
|
|
1956
|
+
const parts = [];
|
|
1957
|
+
let depth = 0;
|
|
1958
|
+
let last = 0;
|
|
1959
|
+
let i = 0;
|
|
1960
|
+
while (i < body.length) {
|
|
1961
|
+
const ch = body[i];
|
|
1962
|
+
if (ch === "\\") {
|
|
1963
|
+
i += 2;
|
|
1964
|
+
continue;
|
|
1965
|
+
}
|
|
1966
|
+
if (ch === "[") {
|
|
1967
|
+
let j = i + 1;
|
|
1968
|
+
if (body[j] === "^") j++;
|
|
1969
|
+
while (j < body.length && body[j] !== "]") {
|
|
1970
|
+
if (body[j] === "\\") j += 2;
|
|
1971
|
+
else j++;
|
|
1972
|
+
}
|
|
1973
|
+
if (body[j] !== "]") return null;
|
|
1974
|
+
i = j + 1;
|
|
1975
|
+
continue;
|
|
1976
|
+
}
|
|
1977
|
+
if (ch === "(") {
|
|
1978
|
+
depth++;
|
|
1979
|
+
i++;
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
if (ch === ")") {
|
|
1983
|
+
depth--;
|
|
1984
|
+
if (depth < 0) return null;
|
|
1985
|
+
i++;
|
|
1986
|
+
continue;
|
|
1987
|
+
}
|
|
1988
|
+
if (ch === "|" && depth === 0) {
|
|
1989
|
+
parts.push(body.slice(last, i));
|
|
1990
|
+
last = i + 1;
|
|
1991
|
+
i++;
|
|
1992
|
+
continue;
|
|
1993
|
+
}
|
|
1994
|
+
i++;
|
|
1995
|
+
}
|
|
1996
|
+
if (depth !== 0) return null;
|
|
1997
|
+
parts.push(body.slice(last));
|
|
1998
|
+
if (parts.length < 2 || parts.some((p) => p.length === 0)) return null;
|
|
1999
|
+
return parts;
|
|
2000
|
+
}
|
|
2001
|
+
function shapeCanBeEmpty(shape) {
|
|
2002
|
+
if (shape.kind === "chars") return !shape.minOne;
|
|
2003
|
+
if (shape.kind === "lookahead") return shapeCanBeEmpty(shape.inner);
|
|
2004
|
+
if (shape.kind === "alt") return shape.arms.some(shapeCanBeEmpty);
|
|
2005
|
+
return false;
|
|
2006
|
+
}
|
|
2007
|
+
function unionCharSets(sets) {
|
|
2008
|
+
if (sets.some((s) => s === "any")) return "any";
|
|
2009
|
+
const concrete = sets;
|
|
2010
|
+
if (concrete.length === 1) return concrete[0];
|
|
2011
|
+
if (concrete.some((s) => s.negated)) return "any";
|
|
2012
|
+
return { ranges: concrete.flatMap((s) => s.ranges), negated: false };
|
|
2013
|
+
}
|
|
2014
|
+
function partFirstAccept(part) {
|
|
2015
|
+
if (part.part === "lit") return { ranges: [[part.cps[0], part.cps[0]]], negated: false };
|
|
2016
|
+
if (part.part === "run") return { ranges: part.ranges, negated: part.negated };
|
|
2017
|
+
return shapeFirstAccept(part.inner);
|
|
2018
|
+
}
|
|
2019
|
+
function seqFirstAccept(parts) {
|
|
2020
|
+
const sets = [];
|
|
2021
|
+
for (const p of parts) {
|
|
2022
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
2023
|
+
sets.push(partFirstAccept(p));
|
|
2024
|
+
if (mandatory) break;
|
|
2025
|
+
}
|
|
2026
|
+
return unionCharSets(sets);
|
|
2027
|
+
}
|
|
2028
|
+
function groupInnerSafe(shape) {
|
|
2029
|
+
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
2030
|
+
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
2031
|
+
if (shape.kind === "alt") return true;
|
|
2032
|
+
return false;
|
|
2033
|
+
}
|
|
2034
|
+
function shapeFirstAccept(shape) {
|
|
2035
|
+
if (shapeCanBeEmpty(shape)) return "any";
|
|
2036
|
+
switch (shape.kind) {
|
|
2037
|
+
case "chars":
|
|
2038
|
+
return { ranges: shape.ranges, negated: false };
|
|
2039
|
+
case "ident":
|
|
2040
|
+
return { ranges: shape.head, negated: false };
|
|
2041
|
+
case "until":
|
|
2042
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
2043
|
+
case "delimited":
|
|
2044
|
+
return { ranges: [[shape.open[0], shape.open[0]]], negated: false };
|
|
2045
|
+
case "string":
|
|
2046
|
+
return { ranges: [[shape.quote, shape.quote]], negated: false };
|
|
2047
|
+
case "litFold": {
|
|
2048
|
+
const cp = shape.open[0];
|
|
2049
|
+
if (cp >= 65 && cp <= 90) return { ranges: [[cp, cp], [cp + 32, cp + 32]], negated: false };
|
|
2050
|
+
if (cp >= 97 && cp <= 122) return { ranges: [[cp, cp], [cp - 32, cp - 32]], negated: false };
|
|
2051
|
+
return { ranges: [[cp, cp]], negated: false };
|
|
2052
|
+
}
|
|
2053
|
+
case "seq":
|
|
2054
|
+
return seqFirstAccept(shape.parts);
|
|
2055
|
+
case "lookahead":
|
|
2056
|
+
return shapeFirstAccept(shape.inner);
|
|
2057
|
+
// zero-width — doesn't change first-char acceptance
|
|
2058
|
+
case "alt": {
|
|
2059
|
+
const sets = shape.firsts.map((f) => f === null ? "any" : f);
|
|
2060
|
+
return unionCharSets(sets);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
function allPairsDisjoint(sets) {
|
|
2065
|
+
for (let i = 0; i < sets.length; i++) {
|
|
2066
|
+
const a = sets[i];
|
|
2067
|
+
if (a === "any") return false;
|
|
2068
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
2069
|
+
const b = sets[j];
|
|
2070
|
+
if (b === "any" || !classDisjoint(a, b)) return false;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
return true;
|
|
2074
|
+
}
|
|
2075
|
+
function parseAlternation(source) {
|
|
2076
|
+
const parts = splitTopLevelAlternation(source);
|
|
2077
|
+
if (!parts) return null;
|
|
2078
|
+
const arms = [];
|
|
2079
|
+
for (const p of parts) {
|
|
2080
|
+
const arm = parseScanShape(p);
|
|
2081
|
+
if (!arm) return null;
|
|
2082
|
+
arms.push(arm);
|
|
2083
|
+
}
|
|
2084
|
+
const firstSets = arms.map(shapeFirstAccept);
|
|
2085
|
+
const disjoint = allPairsDisjoint(firstSets);
|
|
2086
|
+
return { kind: "alt", arms, disjoint, firsts: firstSets.map((f) => f === "any" ? null : f) };
|
|
2087
|
+
}
|
|
1571
2088
|
function parseScanShape(source) {
|
|
2089
|
+
const alt = parseAlternation(source);
|
|
2090
|
+
if (alt) return alt;
|
|
2091
|
+
const la = stripTrailingLookahead(source);
|
|
2092
|
+
if (la) {
|
|
2093
|
+
const inner = parseScanShapeCore(la.base);
|
|
2094
|
+
if (!inner) return null;
|
|
2095
|
+
const operand = { ranges: la.ranges, negated: la.classNegated };
|
|
2096
|
+
if (!lookaheadUnambiguous(inner, operand, la.negative)) return null;
|
|
2097
|
+
return { kind: "lookahead", inner, ranges: la.ranges, classNegated: la.classNegated, negative: la.negative };
|
|
2098
|
+
}
|
|
2099
|
+
return parseScanShapeCore(source);
|
|
2100
|
+
}
|
|
2101
|
+
function splitDelimArms(body) {
|
|
2102
|
+
const arms = [];
|
|
2103
|
+
let depth = 0;
|
|
2104
|
+
let last = 0;
|
|
2105
|
+
let i = 0;
|
|
2106
|
+
while (i < body.length) {
|
|
2107
|
+
const ch = body[i];
|
|
2108
|
+
if (ch === "\\") {
|
|
2109
|
+
i += 2;
|
|
2110
|
+
continue;
|
|
2111
|
+
}
|
|
2112
|
+
if (ch === "[") {
|
|
2113
|
+
let j = i + 1;
|
|
2114
|
+
if (body[j] === "^") j++;
|
|
2115
|
+
while (j < body.length && body[j] !== "]") {
|
|
2116
|
+
if (body[j] === "\\") j += 2;
|
|
2117
|
+
else j++;
|
|
2118
|
+
}
|
|
2119
|
+
if (body[j] !== "]") return null;
|
|
2120
|
+
i = j + 1;
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
if (ch === "(") {
|
|
2124
|
+
depth++;
|
|
2125
|
+
i++;
|
|
2126
|
+
continue;
|
|
2127
|
+
}
|
|
2128
|
+
if (ch === ")") {
|
|
2129
|
+
depth--;
|
|
2130
|
+
if (depth < 0) return null;
|
|
2131
|
+
i++;
|
|
2132
|
+
continue;
|
|
2133
|
+
}
|
|
2134
|
+
if (ch === "|" && depth === 0) {
|
|
2135
|
+
arms.push(body.slice(last, i));
|
|
2136
|
+
last = i + 1;
|
|
2137
|
+
}
|
|
2138
|
+
i++;
|
|
2139
|
+
}
|
|
2140
|
+
if (depth !== 0) return null;
|
|
2141
|
+
arms.push(body.slice(last));
|
|
2142
|
+
return arms;
|
|
2143
|
+
}
|
|
2144
|
+
function delimitedBodySound(body, close) {
|
|
2145
|
+
const l0 = close[0];
|
|
2146
|
+
if (close.length > 2) return false;
|
|
2147
|
+
const arms = splitDelimArms(body);
|
|
2148
|
+
if (!arms) return false;
|
|
2149
|
+
let bulkSeen = false;
|
|
2150
|
+
let guardSeen = false;
|
|
2151
|
+
for (const arm of arms) {
|
|
2152
|
+
const bulk = /^\[\^((?:\\.|[^\]])+)\]$/.exec(arm);
|
|
2153
|
+
if (bulk) {
|
|
2154
|
+
if (bulkSeen) return false;
|
|
2155
|
+
const ranges = parseClassRanges(bulk[1]);
|
|
2156
|
+
if (!ranges || ranges.length !== 1 || ranges[0][0] !== l0 || ranges[0][1] !== l0) return false;
|
|
2157
|
+
bulkSeen = true;
|
|
2158
|
+
continue;
|
|
2159
|
+
}
|
|
2160
|
+
if (close.length === 2 && arm.endsWith(")")) {
|
|
2161
|
+
const idx = arm.indexOf("(?!");
|
|
2162
|
+
if (idx === -1) return false;
|
|
2163
|
+
const prefix = literalCodePoints(arm.slice(0, idx));
|
|
2164
|
+
const op = parseClassOperand(arm.slice(idx + 3, -1));
|
|
2165
|
+
const prefixIsL0 = prefix?.length === 1 && prefix[0] === l0;
|
|
2166
|
+
const forbidsL1 = op && !op.negated && op.ranges.length === 1 && op.ranges[0][0] === close[1] && op.ranges[0][1] === close[1];
|
|
2167
|
+
if (!prefixIsL0 || !forbidsL1 || guardSeen) return false;
|
|
2168
|
+
guardSeen = true;
|
|
2169
|
+
continue;
|
|
2170
|
+
}
|
|
2171
|
+
return false;
|
|
2172
|
+
}
|
|
2173
|
+
return bulkSeen && guardSeen === (close.length === 2);
|
|
2174
|
+
}
|
|
2175
|
+
function parseDelimited(source) {
|
|
2176
|
+
if (source.includes("\\\\")) return null;
|
|
2177
|
+
let i = 0;
|
|
2178
|
+
while (i < source.length && source[i] !== "(") {
|
|
2179
|
+
i += source[i] === "\\" ? 2 : 1;
|
|
2180
|
+
}
|
|
2181
|
+
if (source[i] !== "(" || source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
2182
|
+
const openFrag = source.slice(0, i);
|
|
2183
|
+
let k = i + 3;
|
|
2184
|
+
let depth = 1;
|
|
2185
|
+
while (k < source.length && depth > 0) {
|
|
2186
|
+
const ch = source[k];
|
|
2187
|
+
if (ch === "\\") {
|
|
2188
|
+
k += 2;
|
|
2189
|
+
continue;
|
|
2190
|
+
}
|
|
2191
|
+
if (ch === "[") {
|
|
2192
|
+
let j = k + 1;
|
|
2193
|
+
if (source[j] === "^") j++;
|
|
2194
|
+
while (j < source.length && source[j] !== "]") {
|
|
2195
|
+
if (source[j] === "\\") j += 2;
|
|
2196
|
+
else j++;
|
|
2197
|
+
}
|
|
2198
|
+
if (source[j] !== "]") return null;
|
|
2199
|
+
k = j + 1;
|
|
2200
|
+
continue;
|
|
2201
|
+
}
|
|
2202
|
+
if (ch === "(") depth++;
|
|
2203
|
+
else if (ch === ")") depth--;
|
|
2204
|
+
k++;
|
|
2205
|
+
}
|
|
2206
|
+
if (depth !== 0) return null;
|
|
2207
|
+
const body = source.slice(i + 3, k - 1);
|
|
2208
|
+
if (source[k] !== "*") return null;
|
|
2209
|
+
const closeFrag = source.slice(k + 1);
|
|
2210
|
+
const open = literalCodePoints(openFrag);
|
|
2211
|
+
const close = literalCodePoints(closeFrag);
|
|
2212
|
+
if (!open || !close || !delimitedBodySound(body, close)) return null;
|
|
2213
|
+
return { kind: "delimited", open, close };
|
|
2214
|
+
}
|
|
2215
|
+
function parseScanShapeCore(source) {
|
|
1572
2216
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1573
|
-
if (m) {
|
|
1574
|
-
if (m[1].startsWith("^")) return null;
|
|
2217
|
+
if (m && !m[1].startsWith("^")) {
|
|
1575
2218
|
const ranges = parseClassRanges(m[1]);
|
|
1576
2219
|
return ranges ? { kind: "chars", ranges, minOne: m[2] === "+" } : null;
|
|
1577
2220
|
}
|
|
1578
|
-
m = /^\\([
|
|
2221
|
+
m = /^\\([dws])([+*])$/.exec(source);
|
|
1579
2222
|
if (m) {
|
|
1580
2223
|
return { kind: "chars", ranges: shorthandRanges(m[1]), minOne: m[2] === "+" };
|
|
1581
2224
|
}
|
|
1582
|
-
m = /^(\[(?:\\.|[^\]])+\]|\\[
|
|
2225
|
+
m = /^(\[(?:\\.|[^\]])+\]|\\[dws])(\[(?:\\.|[^\]])+\]|\\[dws])\*$/.exec(source);
|
|
1583
2226
|
if (m) {
|
|
1584
2227
|
const head = classToRanges(m[1]);
|
|
1585
2228
|
const tail = classToRanges(m[2]);
|
|
1586
|
-
|
|
2229
|
+
if (head && tail) return { kind: "ident", head, tail };
|
|
1587
2230
|
}
|
|
1588
2231
|
const str = parseStringShape(source);
|
|
1589
2232
|
if (str) return str;
|
|
@@ -1592,20 +2235,19 @@ function parseScanShape(source) {
|
|
|
1592
2235
|
const open = literalCodePoints(m[1]);
|
|
1593
2236
|
const stop = parseClassRanges(m[2]);
|
|
1594
2237
|
if (open && stop) return { kind: "until", open, stop };
|
|
1595
|
-
return null;
|
|
1596
|
-
}
|
|
1597
|
-
if (!source.includes("\\\\")) {
|
|
1598
|
-
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
1599
|
-
if (m && m[1] && m[2]) {
|
|
1600
|
-
const open = literalCodePoints(m[1]);
|
|
1601
|
-
const close = literalCodePoints(m[2]);
|
|
1602
|
-
if (open && close) return { kind: "delimited", open, close };
|
|
1603
|
-
}
|
|
1604
2238
|
}
|
|
2239
|
+
const delim = parseDelimited(source);
|
|
2240
|
+
if (delim) return delim;
|
|
2241
|
+
const parts = parseSeqParts(source);
|
|
2242
|
+
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1605
2243
|
return null;
|
|
1606
2244
|
}
|
|
1607
2245
|
function scanShapeFromRegex(source, flags) {
|
|
1608
|
-
if (/[
|
|
2246
|
+
if (/[msu]/.test(flags)) return null;
|
|
2247
|
+
if (/i/.test(flags)) {
|
|
2248
|
+
const open = literalCodePoints(source);
|
|
2249
|
+
return open ? { kind: "litFold", open } : null;
|
|
2250
|
+
}
|
|
1609
2251
|
return parseScanShape(source);
|
|
1610
2252
|
}
|
|
1611
2253
|
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
@@ -1620,6 +2262,53 @@ function stringHardStop(shape) {
|
|
|
1620
2262
|
}
|
|
1621
2263
|
var codeAt = (start, firstChar) => firstChar ?? `input.charCodeAt(${start})`;
|
|
1622
2264
|
function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
2265
|
+
if (shape.kind === "lookahead") {
|
|
2266
|
+
const inner = emitShapeMatch(shape.inner, start, mint, ind2, firstChar);
|
|
2267
|
+
const okV = mint("_ok");
|
|
2268
|
+
const endV2 = mint("_end");
|
|
2269
|
+
const rawIn = classCond(`input.charCodeAt(${inner.end})`, shape.ranges);
|
|
2270
|
+
const charInClass = shape.classNegated ? `!(${rawIn})` : `(${rawIn})`;
|
|
2271
|
+
const hasMatchingChar = `${inner.end} < input.length && ${charInClass}`;
|
|
2272
|
+
const guardFails = shape.negative ? hasMatchingChar : `!(${hasMatchingChar})`;
|
|
2273
|
+
return {
|
|
2274
|
+
setup: [
|
|
2275
|
+
...inner.setup,
|
|
2276
|
+
`${ind2}let ${okV} = ${inner.ok}`,
|
|
2277
|
+
`${ind2}let ${endV2} = ${inner.end}`,
|
|
2278
|
+
`${ind2}if (${okV} && (${guardFails})) { ${okV} = false; ${endV2} = ${start} }`
|
|
2279
|
+
],
|
|
2280
|
+
ok: okV,
|
|
2281
|
+
end: endV2
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
if (shape.kind === "alt") {
|
|
2285
|
+
const okV = mint("_ok");
|
|
2286
|
+
const endV2 = mint("_end");
|
|
2287
|
+
const lines = [`${ind2}let ${okV} = false`, `${ind2}let ${endV2} = ${start}`];
|
|
2288
|
+
if (shape.disjoint) {
|
|
2289
|
+
const c0 = codeAt(start, firstChar);
|
|
2290
|
+
shape.arms.forEach((arm, k) => {
|
|
2291
|
+
const fs = shape.firsts[k];
|
|
2292
|
+
const cond = fs.negated ? `!(${classCond(c0, fs.ranges)})` : `(${classCond(c0, fs.ranges)})`;
|
|
2293
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2294
|
+
lines.push(`${ind2}${k === 0 ? "if" : "else if"} (${start} < input.length && ${cond}) {`);
|
|
2295
|
+
lines.push(...m.setup);
|
|
2296
|
+
lines.push(`${ind2} ${okV} = ${m.ok}`);
|
|
2297
|
+
lines.push(`${ind2} ${endV2} = ${m.ok} ? ${m.end} : ${start}`);
|
|
2298
|
+
lines.push(`${ind2}}`);
|
|
2299
|
+
});
|
|
2300
|
+
} else {
|
|
2301
|
+
const lbl = mint("_alt");
|
|
2302
|
+
lines.push(`${ind2}${lbl}: {`);
|
|
2303
|
+
for (const arm of shape.arms) {
|
|
2304
|
+
const m = emitShapeMatch(arm, start, mint, `${ind2} `, firstChar);
|
|
2305
|
+
lines.push(...m.setup);
|
|
2306
|
+
lines.push(`${ind2} if (${m.ok}) { ${okV} = true; ${endV2} = ${m.end}; break ${lbl} }`);
|
|
2307
|
+
}
|
|
2308
|
+
lines.push(`${ind2}}`);
|
|
2309
|
+
}
|
|
2310
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2311
|
+
}
|
|
1623
2312
|
if (shape.kind === "chars") {
|
|
1624
2313
|
const cur = mint("_e");
|
|
1625
2314
|
return {
|
|
@@ -1646,6 +2335,82 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
1646
2335
|
end: cur
|
|
1647
2336
|
};
|
|
1648
2337
|
}
|
|
2338
|
+
if (shape.kind === "seq") {
|
|
2339
|
+
const endV2 = mint("_e");
|
|
2340
|
+
const okV = mint("_ok");
|
|
2341
|
+
const runCond = (p, at) => {
|
|
2342
|
+
const c = classCond(`input.charCodeAt(${at})`, p.ranges);
|
|
2343
|
+
return p.negated ? `!(${c})` : `(${c})`;
|
|
2344
|
+
};
|
|
2345
|
+
const lines = [`${ind2}let ${endV2} = ${start}`, `${ind2}let ${okV} = false`, `${ind2}do {`];
|
|
2346
|
+
let first = true;
|
|
2347
|
+
for (const p of shape.parts) {
|
|
2348
|
+
if (p.part === "lit") {
|
|
2349
|
+
const L = p.cps.length;
|
|
2350
|
+
const cond = litCond(endV2, p.cps, first ? firstChar : void 0);
|
|
2351
|
+
if (p.optional) {
|
|
2352
|
+
lines.push(`${ind2} if (${endV2} + ${L} <= input.length && (${cond})) ${endV2} += ${L}`);
|
|
2353
|
+
} else {
|
|
2354
|
+
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
2355
|
+
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
2356
|
+
}
|
|
2357
|
+
} else if (p.part === "run" && !p.unbounded) {
|
|
2358
|
+
if (p.min === 1) {
|
|
2359
|
+
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
2360
|
+
lines.push(`${ind2} ${endV2}++`);
|
|
2361
|
+
} else {
|
|
2362
|
+
lines.push(`${ind2} if (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2363
|
+
}
|
|
2364
|
+
} else if (p.part === "run") {
|
|
2365
|
+
const s = mint("_s");
|
|
2366
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
2367
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
2368
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
2369
|
+
} else {
|
|
2370
|
+
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
2371
|
+
if (!p.unbounded) {
|
|
2372
|
+
lines.push(...inner.setup);
|
|
2373
|
+
if (p.min === 1) {
|
|
2374
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2375
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2376
|
+
} else {
|
|
2377
|
+
lines.push(`${ind2} if (${inner.ok}) ${endV2} = ${inner.end}`);
|
|
2378
|
+
}
|
|
2379
|
+
} else {
|
|
2380
|
+
const progV = mint("_prog");
|
|
2381
|
+
lines.push(`${ind2} let ${progV} = false`);
|
|
2382
|
+
lines.push(`${ind2} while (true) {`);
|
|
2383
|
+
lines.push(...inner.setup);
|
|
2384
|
+
lines.push(`${ind2} if (!(${inner.ok})) break`);
|
|
2385
|
+
lines.push(`${ind2} ${endV2} = ${inner.end}`);
|
|
2386
|
+
lines.push(`${ind2} ${progV} = true`);
|
|
2387
|
+
lines.push(`${ind2} }`);
|
|
2388
|
+
if (p.min === 1) lines.push(`${ind2} if (!${progV}) break`);
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
first = false;
|
|
2392
|
+
}
|
|
2393
|
+
lines.push(`${ind2} ${okV} = true`);
|
|
2394
|
+
lines.push(`${ind2}} while (false)`);
|
|
2395
|
+
lines.push(`${ind2}if (!${okV}) ${endV2} = ${start}`);
|
|
2396
|
+
return { setup: lines, ok: okV, end: endV2 };
|
|
2397
|
+
}
|
|
2398
|
+
if (shape.kind === "litFold") {
|
|
2399
|
+
const openLen2 = shape.open.length;
|
|
2400
|
+
const endV2 = mint("_end");
|
|
2401
|
+
const checks = shape.open.map((cp, k) => {
|
|
2402
|
+
const cVar = k === 0 && firstChar ? firstChar : `input.charCodeAt(${start} + ${k})`;
|
|
2403
|
+
return foldEq(cVar, cp);
|
|
2404
|
+
});
|
|
2405
|
+
return {
|
|
2406
|
+
setup: [
|
|
2407
|
+
`${ind2}let ${endV2} = ${start}`,
|
|
2408
|
+
`${ind2}if (${start} + ${openLen2} <= input.length && (${checks.join(" && ")})) ${endV2} = ${start} + ${openLen2}`
|
|
2409
|
+
],
|
|
2410
|
+
ok: `${endV2} > ${start}`,
|
|
2411
|
+
end: endV2
|
|
2412
|
+
};
|
|
2413
|
+
}
|
|
1649
2414
|
if (shape.kind === "until") {
|
|
1650
2415
|
const j2 = mint("_j");
|
|
1651
2416
|
const openLen2 = shape.open.length;
|
|
@@ -1927,6 +2692,7 @@ function looseMkBuildRe(type) {
|
|
|
1927
2692
|
);
|
|
1928
2693
|
}
|
|
1929
2694
|
function analyzeMkInlineBuild(def) {
|
|
2695
|
+
if (!def.build) return null;
|
|
1930
2696
|
const src = (def.buildSrc ?? def.build.toString()).trim();
|
|
1931
2697
|
const strict = src.match(MK_BUILD_RE);
|
|
1932
2698
|
if (strict) {
|
|
@@ -2024,6 +2790,15 @@ function v(ctx, prefix = "_v") {
|
|
|
2024
2790
|
function ind(ctx) {
|
|
2025
2791
|
return " ".repeat(ctx.indent);
|
|
2026
2792
|
}
|
|
2793
|
+
function nsp(ctx) {
|
|
2794
|
+
return ctx.ns ?? "";
|
|
2795
|
+
}
|
|
2796
|
+
function mfRef(ctx) {
|
|
2797
|
+
return `${nsp(ctx)}_mf`;
|
|
2798
|
+
}
|
|
2799
|
+
function buildRef(ctx) {
|
|
2800
|
+
return `${nsp(ctx)}_build`;
|
|
2801
|
+
}
|
|
2027
2802
|
function reindentStmts(stmts, targetLevels) {
|
|
2028
2803
|
const nonEmpty = stmts.filter((s) => s.trim().length > 0);
|
|
2029
2804
|
if (nonEmpty.length === 0) return stmts;
|
|
@@ -2040,8 +2815,8 @@ function failReturnArr(expectedArr, posExpr) {
|
|
|
2040
2815
|
function hoistExpected(ctx, constArrSource) {
|
|
2041
2816
|
let name = ctx.expectedMap.get(constArrSource);
|
|
2042
2817
|
if (name === void 0) {
|
|
2043
|
-
name =
|
|
2044
|
-
ctx.expectedDecls.push(`const ${name} =
|
|
2818
|
+
name = `${nsp(ctx)}_fx${ctx.expectedDecls.length}`;
|
|
2819
|
+
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
2045
2820
|
ctx.expectedMap.set(constArrSource, name);
|
|
2046
2821
|
}
|
|
2047
2822
|
return name;
|
|
@@ -2139,13 +2914,13 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
|
|
|
2139
2914
|
`${i}}`
|
|
2140
2915
|
];
|
|
2141
2916
|
}
|
|
2142
|
-
function ensureRegexDecl(ctx,
|
|
2917
|
+
function ensureRegexDecl(ctx, source, flags) {
|
|
2143
2918
|
const f = "y" + flags.replace(/[gy]/g, "");
|
|
2144
|
-
const key = `${
|
|
2919
|
+
const key = `${source}/${f}`;
|
|
2145
2920
|
let rName = ctx.regexMap.get(key);
|
|
2146
2921
|
if (rName === void 0) {
|
|
2147
|
-
rName =
|
|
2148
|
-
ctx.regexDecls.push(`const ${rName} = /${
|
|
2922
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2923
|
+
ctx.regexDecls.push(`const ${rName} = /${source}/${f}`);
|
|
2149
2924
|
ctx.regexMap.set(key, rName);
|
|
2150
2925
|
}
|
|
2151
2926
|
return rName;
|
|
@@ -2154,7 +2929,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2154
2929
|
const trivia2 = ctx.activeTrivia;
|
|
2155
2930
|
const existing = ctx.triviaFnNames.get(trivia2);
|
|
2156
2931
|
if (existing) return existing;
|
|
2157
|
-
const fnName =
|
|
2932
|
+
const fnName = `${nsp(ctx)}_tf${ctx.triviaFnNames.size}`;
|
|
2158
2933
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
2159
2934
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
2160
2935
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
@@ -2176,7 +2951,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2176
2951
|
for (const arm of regexSpec.arms) {
|
|
2177
2952
|
const def = arm.parser._def;
|
|
2178
2953
|
if (def.tag !== "regex") break;
|
|
2179
|
-
reNames.push(ensureRegexDecl(ctx, def.
|
|
2954
|
+
reNames.push(ensureRegexDecl(ctx, def.source, def.flags));
|
|
2180
2955
|
}
|
|
2181
2956
|
if (reNames.length === regexSpec.arms.length) {
|
|
2182
2957
|
ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
|
|
@@ -2363,18 +3138,21 @@ function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2363
3138
|
return true;
|
|
2364
3139
|
}
|
|
2365
3140
|
}
|
|
2366
|
-
function emitFallible(inner, ctx, pos) {
|
|
3141
|
+
function emitFallible(inner, ctx, pos, swallow = false) {
|
|
2367
3142
|
const lbl = v(ctx, "_lbl");
|
|
2368
3143
|
const okV = `${lbl}ok`;
|
|
2369
3144
|
const valV = `${lbl}v`;
|
|
2370
3145
|
const endV = `${lbl}e`;
|
|
2371
3146
|
const savedLabel = ctx.failLabel;
|
|
2372
3147
|
const savedIndent = ctx.indent;
|
|
3148
|
+
const savedRecord = ctx.recordFail;
|
|
2373
3149
|
ctx.failLabel = lbl;
|
|
2374
3150
|
ctx.indent = savedIndent + 1;
|
|
3151
|
+
if (swallow) ctx.recordFail = false;
|
|
2375
3152
|
const r = emit(inner, ctx, pos);
|
|
2376
3153
|
ctx.failLabel = savedLabel;
|
|
2377
3154
|
ctx.indent = savedIndent;
|
|
3155
|
+
ctx.recordFail = savedRecord;
|
|
2378
3156
|
const ind0 = ind(ctx);
|
|
2379
3157
|
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
2380
3158
|
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
@@ -2397,6 +3175,7 @@ function emitFallible(inner, ctx, pos) {
|
|
|
2397
3175
|
];
|
|
2398
3176
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
2399
3177
|
}
|
|
3178
|
+
var CHARCODE_CHAIN_MAX = 16;
|
|
2400
3179
|
function emitLit(def, ctx, pos) {
|
|
2401
3180
|
const { value, caseInsensitive } = def;
|
|
2402
3181
|
const len = value.length;
|
|
@@ -2419,7 +3198,7 @@ function emitLit(def, ctx, pos) {
|
|
|
2419
3198
|
...emitIfFail(ctx, `${pos} >= input.length || input.charCodeAt(${pos}) !== ${code}`, failBody(ctx, expectedStr, pos)),
|
|
2420
3199
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
2421
3200
|
);
|
|
2422
|
-
} else if (len <=
|
|
3201
|
+
} else if (len <= CHARCODE_CHAIN_MAX) {
|
|
2423
3202
|
const checks = Array.from(
|
|
2424
3203
|
{ length: len },
|
|
2425
3204
|
(_, i) => `input.charCodeAt(${pos}${i > 0 ? ` + ${i}` : ""}) !== ${value.codePointAt(i)}`
|
|
@@ -2441,7 +3220,55 @@ function emitLit(def, ctx, pos) {
|
|
|
2441
3220
|
function escapeKeywordRe(s) {
|
|
2442
3221
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2443
3222
|
}
|
|
3223
|
+
function emitKeywordsFast(def, ctx, pos) {
|
|
3224
|
+
if (def.words.length === 0 || def.words.some((w) => w.length === 0)) return null;
|
|
3225
|
+
if (def.words.some((w) => Array.from(w).length !== w.length)) return null;
|
|
3226
|
+
if (def.caseInsensitive && def.boundary) return null;
|
|
3227
|
+
let boundary = null;
|
|
3228
|
+
if (def.boundary) {
|
|
3229
|
+
let body = def.boundary;
|
|
3230
|
+
const negated = body.startsWith("^");
|
|
3231
|
+
if (negated) body = body.slice(1);
|
|
3232
|
+
const ranges = parseClassRanges(body);
|
|
3233
|
+
if (!ranges) return null;
|
|
3234
|
+
boundary = { ranges, negated };
|
|
3235
|
+
}
|
|
3236
|
+
const mint = (prefix = "_v") => v(ctx, prefix);
|
|
3237
|
+
const lbl = v(ctx, "_kwLbl");
|
|
3238
|
+
const valV = v(ctx, "_kwv");
|
|
3239
|
+
const endV = v(ctx, "_kwe");
|
|
3240
|
+
const bodyInd = ind(ctx) + " ";
|
|
3241
|
+
const tries = [];
|
|
3242
|
+
for (const w of def.words) {
|
|
3243
|
+
const cps = Array.from(w, (ch) => ch.codePointAt(0));
|
|
3244
|
+
let shape = def.caseInsensitive ? { kind: "litFold", open: cps } : { kind: "seq", parts: [{ part: "lit", cps, optional: false }] };
|
|
3245
|
+
if (boundary) {
|
|
3246
|
+
shape = { kind: "lookahead", inner: shape, ranges: boundary.ranges, classNegated: boundary.negated, negative: true };
|
|
3247
|
+
}
|
|
3248
|
+
const m = emitShapeMatch(shape, pos, mint, bodyInd);
|
|
3249
|
+
tries.push(
|
|
3250
|
+
...m.setup,
|
|
3251
|
+
// Slice from input rather than reusing the literal word: `caseInsensitive`
|
|
3252
|
+
// must return the text as it actually appeared (e.g. "ABC" for keyword
|
|
3253
|
+
// "abc"), matching what the original `RegExp.exec()[0]` returned.
|
|
3254
|
+
`${bodyInd}if (${m.ok}) { ${valV} = input.slice(${pos}, ${m.end}); ${endV} = ${m.end}; break ${lbl} }`
|
|
3255
|
+
);
|
|
3256
|
+
}
|
|
3257
|
+
const stmts = [
|
|
3258
|
+
`${ind(ctx)}let ${valV} = '', ${endV} = ${pos}`,
|
|
3259
|
+
`${ind(ctx)}${lbl}: {`,
|
|
3260
|
+
...tries,
|
|
3261
|
+
`${ind(ctx)}}`,
|
|
3262
|
+
// Every word has length >= 1 (checked above), so a real match always
|
|
3263
|
+
// advances past `pos` — `endV === pos` only happens when no candidate matched.
|
|
3264
|
+
...emitIfFail(ctx, `${endV} === ${pos}`, failBody(ctx, '"keyword"', pos))
|
|
3265
|
+
];
|
|
3266
|
+
stmts.push(...emitLeafCapture(ctx, valV, pos, endV));
|
|
3267
|
+
return { stmts, valueVar: valV, endVar: endV };
|
|
3268
|
+
}
|
|
2444
3269
|
function emitKeywords(def, ctx, pos) {
|
|
3270
|
+
const fast = emitKeywordsFast(def, ctx, pos);
|
|
3271
|
+
if (fast) return fast;
|
|
2445
3272
|
const alt = def.words.map(escapeKeywordRe).join("|");
|
|
2446
3273
|
const boundary = def.boundary ? `(?![${def.boundary}])` : "";
|
|
2447
3274
|
const flags = def.caseInsensitive ? "iuy" : "uy";
|
|
@@ -2449,7 +3276,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
2449
3276
|
const key = `${source}/${flags}`;
|
|
2450
3277
|
let rName = ctx.regexMap.get(key);
|
|
2451
3278
|
if (rName === void 0) {
|
|
2452
|
-
rName =
|
|
3279
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2453
3280
|
ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
|
|
2454
3281
|
ctx.regexMap.set(key, rName);
|
|
2455
3282
|
}
|
|
@@ -2483,11 +3310,11 @@ function emitRegex(def, ctx, pos) {
|
|
|
2483
3310
|
}
|
|
2484
3311
|
}
|
|
2485
3312
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
2486
|
-
const key = `${def.
|
|
3313
|
+
const key = `${def.source}/${flags}`;
|
|
2487
3314
|
let rName = ctx.regexMap.get(key);
|
|
2488
3315
|
if (rName === void 0) {
|
|
2489
|
-
rName =
|
|
2490
|
-
ctx.regexDecls.push(`const ${rName} = /${def.
|
|
3316
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3317
|
+
ctx.regexDecls.push(`const ${rName} = /${def.source}/${flags}`);
|
|
2491
3318
|
ctx.regexMap.set(key, rName);
|
|
2492
3319
|
}
|
|
2493
3320
|
const mv = v(ctx, "_m");
|
|
@@ -2572,6 +3399,31 @@ function failsAtStart(p) {
|
|
|
2572
3399
|
return false;
|
|
2573
3400
|
}
|
|
2574
3401
|
}
|
|
3402
|
+
function armStaticExpected(ctx, p) {
|
|
3403
|
+
return hoistExpected(ctx, staticExpectedArr([p]));
|
|
3404
|
+
}
|
|
3405
|
+
function canMatchEmptyAtStart(p) {
|
|
3406
|
+
const d = p._def;
|
|
3407
|
+
switch (d.tag) {
|
|
3408
|
+
case "regex":
|
|
3409
|
+
return /(?:[*?]|\{0,|\{\d*,)/.test(d.source);
|
|
3410
|
+
case "optional":
|
|
3411
|
+
return true;
|
|
3412
|
+
case "many":
|
|
3413
|
+
return true;
|
|
3414
|
+
case "transform":
|
|
3415
|
+
case "label":
|
|
3416
|
+
return canMatchEmptyAtStart(d.parser);
|
|
3417
|
+
case "literal":
|
|
3418
|
+
return d.value.length === 0;
|
|
3419
|
+
default:
|
|
3420
|
+
return false;
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
function needsFirstSetGuard(p) {
|
|
3424
|
+
const fs = p._meta.firstSet;
|
|
3425
|
+
return fs.kind !== "any" && !canMatchEmptyAtStart(p);
|
|
3426
|
+
}
|
|
2575
3427
|
function emitDisjointArm(p, ctx, pos, valV, endV) {
|
|
2576
3428
|
if (failsAtStart(p)) {
|
|
2577
3429
|
const r = emit(p, ctx, pos);
|
|
@@ -2639,7 +3491,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
2639
3491
|
const reKey = `${regexDef.source}/${cleanFlags}`;
|
|
2640
3492
|
let reVar = ctx.regexMap.get(reKey);
|
|
2641
3493
|
if (reVar === void 0) {
|
|
2642
|
-
reVar =
|
|
3494
|
+
reVar = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2643
3495
|
ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
|
|
2644
3496
|
ctx.regexMap.set(reKey, reVar);
|
|
2645
3497
|
}
|
|
@@ -2713,21 +3565,26 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
2713
3565
|
const resValV = v(ctx, "_crv");
|
|
2714
3566
|
const resEndV = v(ctx, "_cre");
|
|
2715
3567
|
const resOkV = v(ctx, "_crok");
|
|
3568
|
+
const codeV = v(ctx, "_chcode");
|
|
2716
3569
|
const slots = def.parsers.map(() => v(ctx, "_cfx"));
|
|
2717
3570
|
const ind0 = ind(ctx);
|
|
2718
3571
|
const stmts = [
|
|
2719
3572
|
`${ind0}let ${resValV}, ${resEndV} = ${pos}, ${resOkV} = false`,
|
|
2720
|
-
`${ind0}let ${slots.join(", ")}
|
|
3573
|
+
`${ind0}let ${slots.join(", ")}`,
|
|
3574
|
+
`${ind0}const ${codeV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`
|
|
2721
3575
|
];
|
|
2722
3576
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
2723
3577
|
const p = def.parsers[i];
|
|
2724
3578
|
const gate = def.gates[i];
|
|
2725
3579
|
const autoNot = def.autoNot[i];
|
|
3580
|
+
const atStart = failsAtStart(p);
|
|
3581
|
+
const staticFx = armStaticExpected(ctx, p);
|
|
3582
|
+
const fsGuard = needsFirstSetGuard(p) ? firstSetCond(codeV, p._meta.firstSet) : null;
|
|
2726
3583
|
let gateCond = null;
|
|
2727
3584
|
if (gate) {
|
|
2728
3585
|
const gateIdx = ctx.mapFns.length;
|
|
2729
3586
|
ctx.mapFns.push(gate);
|
|
2730
|
-
gateCond =
|
|
3587
|
+
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
2731
3588
|
}
|
|
2732
3589
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
2733
3590
|
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
@@ -2737,39 +3594,41 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
2737
3594
|
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2738
3595
|
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2739
3596
|
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
3597
|
+
const failSlot = atStart ? staticFx : "_ctx._fx";
|
|
2740
3598
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2741
|
-
ctx
|
|
2742
|
-
|
|
3599
|
+
if (fsGuard) stmts.push(`${ind(ctx)}if (${fsGuard}) {`);
|
|
3600
|
+
ctx.indent += fsGuard ? 2 : 1;
|
|
2743
3601
|
if (markLeaves) {
|
|
2744
3602
|
stmts.push(
|
|
2745
|
-
`${
|
|
2746
|
-
`${
|
|
2747
|
-
`${
|
|
2748
|
-
`${
|
|
3603
|
+
`${ind(ctx)}const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
3604
|
+
`${ind(ctx)}const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`,
|
|
3605
|
+
`${ind(ctx)}const ${markTl} = _ctx._cstTriviaLog?.length ?? 0`,
|
|
3606
|
+
`${ind(ctx)}const ${markLog} = _ctx._triviaLog?.length ?? 0`
|
|
2749
3607
|
);
|
|
2750
3608
|
}
|
|
2751
|
-
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos);
|
|
3609
|
+
const { stmts: armStmts, okVar, valVar, endVar } = emitFallible(p, ctx, pos, atStart);
|
|
2752
3610
|
stmts.push(...armStmts);
|
|
2753
|
-
ctx.indent--;
|
|
2754
3611
|
if (autoNot && autoNot.length > 0) {
|
|
2755
3612
|
const anCode = v(ctx, "_anc");
|
|
2756
3613
|
const rejectCond = autoNot.map(
|
|
2757
3614
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${endVar})`
|
|
2758
3615
|
).join(" || ");
|
|
2759
|
-
stmts.push(`${
|
|
2760
|
-
stmts.push(`${
|
|
2761
|
-
stmts.push(`${
|
|
2762
|
-
stmts.push(`${
|
|
2763
|
-
stmts.push(`${
|
|
2764
|
-
stmts.push(`${
|
|
2765
|
-
stmts.push(`${
|
|
2766
|
-
stmts.push(`${
|
|
2767
|
-
stmts.push(`${
|
|
2768
|
-
if (rollback) stmts.push(`${
|
|
3616
|
+
stmts.push(`${ind(ctx)}if (${okVar}) {`);
|
|
3617
|
+
stmts.push(`${ind(ctx)} const ${anCode} = ${endVar} < input.length ? input.charCodeAt(${endVar}) : -1`);
|
|
3618
|
+
stmts.push(`${ind(ctx)} if (!(${rejectCond})) {`);
|
|
3619
|
+
stmts.push(`${ind(ctx)} ${resValV} = ${valVar}`);
|
|
3620
|
+
stmts.push(`${ind(ctx)} ${resEndV} = ${endVar}`);
|
|
3621
|
+
stmts.push(`${ind(ctx)} ${resOkV} = true`);
|
|
3622
|
+
stmts.push(`${ind(ctx)} }`);
|
|
3623
|
+
stmts.push(`${ind(ctx)}}`);
|
|
3624
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot} }`);
|
|
3625
|
+
if (rollback) stmts.push(`${ind(ctx)}if (!${resOkV}) { ${rollback} }`);
|
|
2769
3626
|
} else {
|
|
2770
|
-
stmts.push(`${
|
|
2771
|
-
stmts.push(`${
|
|
3627
|
+
stmts.push(`${ind(ctx)}if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
3628
|
+
stmts.push(`${ind(ctx)}else { ${slots[i]} = ${failSlot}${rollback ? `; ${rollback}` : ""} }`);
|
|
2772
3629
|
}
|
|
3630
|
+
ctx.indent -= fsGuard ? 2 : 1;
|
|
3631
|
+
if (fsGuard) stmts.push(`${ind(ctx)}} else { ${slots[i]} = ${staticFx} }`);
|
|
2773
3632
|
stmts.push(`${ind0}}`);
|
|
2774
3633
|
}
|
|
2775
3634
|
const concatExpr = `[${slots.map((s) => `...(${s} || [])`).join(", ")}]`;
|
|
@@ -2792,7 +3651,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2792
3651
|
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
2793
3652
|
const vv = v(ctx);
|
|
2794
3653
|
return {
|
|
2795
|
-
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} =
|
|
3654
|
+
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
2796
3655
|
valueVar: vv,
|
|
2797
3656
|
endVar: endV
|
|
2798
3657
|
};
|
|
@@ -2802,7 +3661,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
2802
3661
|
function emitLiteralCondition(litVal, pos) {
|
|
2803
3662
|
const len = litVal.length;
|
|
2804
3663
|
if (len === 0) return "true";
|
|
2805
|
-
if (len >
|
|
3664
|
+
if (len > CHARCODE_CHAIN_MAX) return `input.startsWith(${JSON.stringify(litVal)}, ${pos})`;
|
|
2806
3665
|
const checks = [`${pos} + ${len} <= input.length`];
|
|
2807
3666
|
for (let i = 0; i < len; i++) {
|
|
2808
3667
|
const code = litVal.codePointAt(i);
|
|
@@ -2851,7 +3710,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2851
3710
|
itemPos = npV;
|
|
2852
3711
|
}
|
|
2853
3712
|
}
|
|
2854
|
-
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
3713
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
2855
3714
|
stmts.push(
|
|
2856
3715
|
...iterStmts,
|
|
2857
3716
|
`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`,
|
|
@@ -2865,7 +3724,7 @@ function emitMany(def, ctx, pos) {
|
|
|
2865
3724
|
function emitOptional(def, ctx, pos) {
|
|
2866
3725
|
const valV = v(ctx, "_opt");
|
|
2867
3726
|
const endV = v(ctx, "_opte");
|
|
2868
|
-
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
3727
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos, true);
|
|
2869
3728
|
const ind0 = ind(ctx);
|
|
2870
3729
|
const stmts = [
|
|
2871
3730
|
...lblStmts,
|
|
@@ -2877,7 +3736,7 @@ function emitOptional(def, ctx, pos) {
|
|
|
2877
3736
|
function emitSepBy(_p, def, ctx, pos) {
|
|
2878
3737
|
const arrV = v(ctx, "_arr");
|
|
2879
3738
|
const curV = v(ctx, "_cur");
|
|
2880
|
-
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
3739
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
2881
3740
|
const stmts = [
|
|
2882
3741
|
`${ind(ctx)}const ${arrV} = []`,
|
|
2883
3742
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
@@ -2909,11 +3768,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2909
3768
|
);
|
|
2910
3769
|
sepAtPos = spV;
|
|
2911
3770
|
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}; `;
|
|
2912
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3771
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2913
3772
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollbackToSep}break }`);
|
|
2914
3773
|
const npV = v(ctx, "_np");
|
|
2915
3774
|
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx, 1)`);
|
|
2916
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3775
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2917
3776
|
stmts.push(
|
|
2918
3777
|
...nextStmts,
|
|
2919
3778
|
// item failed → unwind the separator too, back to the end of the last item
|
|
@@ -2926,11 +3785,11 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2926
3785
|
const spV = v(ctx, "_sp");
|
|
2927
3786
|
stmts.push(`${ind(ctx)}const ${spV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
2928
3787
|
sepAtPos = spV;
|
|
2929
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3788
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2930
3789
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2931
3790
|
const npV = v(ctx, "_np");
|
|
2932
3791
|
stmts.push(`${ind(ctx)}const ${npV} = ${trivFn}(input, ${sepEnd}, _ctx)`);
|
|
2933
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV);
|
|
3792
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
2934
3793
|
stmts.push(
|
|
2935
3794
|
...nextStmts,
|
|
2936
3795
|
`${ind(ctx)}if (!${nextOk}) break`,
|
|
@@ -2947,10 +3806,10 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
2947
3806
|
`${ind(ctx)}const ${markRw} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`
|
|
2948
3807
|
);
|
|
2949
3808
|
}
|
|
2950
|
-
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
3809
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos, true);
|
|
2951
3810
|
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) break`);
|
|
2952
3811
|
const nextRb = markLv ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLv}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRw}; ` : "";
|
|
2953
|
-
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd);
|
|
3812
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
2954
3813
|
stmts.push(
|
|
2955
3814
|
...nextStmts,
|
|
2956
3815
|
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`,
|
|
@@ -3008,7 +3867,7 @@ function emitScanTo(def, ctx, pos) {
|
|
|
3008
3867
|
return { stmts, valueVar: valV, endVar: curV };
|
|
3009
3868
|
}
|
|
3010
3869
|
function emitNot(def, ctx, pos) {
|
|
3011
|
-
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
3870
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos, true);
|
|
3012
3871
|
const label2 = JSON.stringify(`not(${def.parser._tag})`);
|
|
3013
3872
|
return {
|
|
3014
3873
|
stmts: [
|
|
@@ -3020,16 +3879,17 @@ function emitNot(def, ctx, pos) {
|
|
|
3020
3879
|
};
|
|
3021
3880
|
}
|
|
3022
3881
|
function emitNode(def, ctx, pos) {
|
|
3023
|
-
const
|
|
3882
|
+
const structural = def.build === void 0;
|
|
3883
|
+
const mkType = structural ? null : analyzeMkInlineBuild(def);
|
|
3024
3884
|
let buildIdx = null;
|
|
3025
|
-
if (!mkType) {
|
|
3885
|
+
if (!mkType && !structural) {
|
|
3026
3886
|
buildIdx = ctx.buildFns.length;
|
|
3027
3887
|
ctx.buildFns.push(def.build);
|
|
3028
3888
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3029
3889
|
}
|
|
3030
3890
|
const i = ind(ctx);
|
|
3031
|
-
const capturesTrivia = mkType !== null || buildReadsTrivia(def);
|
|
3032
|
-
const clonesState = buildReadsState(def);
|
|
3891
|
+
const capturesTrivia = mkType !== null || structural || buildReadsTrivia(def);
|
|
3892
|
+
const clonesState = structural || buildReadsState(def);
|
|
3033
3893
|
const chV = v(ctx, "_ch");
|
|
3034
3894
|
const rawV = v(ctx, "_raw");
|
|
3035
3895
|
const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
@@ -3052,7 +3912,8 @@ function emitNode(def, ctx, pos) {
|
|
|
3052
3912
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3053
3913
|
}
|
|
3054
3914
|
const ndV = v(ctx, "_nd");
|
|
3055
|
-
const
|
|
3915
|
+
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})`;
|
|
3916
|
+
const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
|
|
3056
3917
|
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;
|
|
3057
3918
|
stmts.push(
|
|
3058
3919
|
`${i}const ${ndV} = ${finalExpr}`,
|
|
@@ -3077,8 +3938,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
3077
3938
|
return { stmts, valueVar: vv, endVar: ev };
|
|
3078
3939
|
}
|
|
3079
3940
|
function emitLazy(p, def, ctx, pos) {
|
|
3941
|
+
const canonical = ctx.ruleNames?.get(p);
|
|
3080
3942
|
const usage = ctx.lazyUsage;
|
|
3081
|
-
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
3943
|
+
if (!canonical && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
3082
3944
|
let resolved;
|
|
3083
3945
|
try {
|
|
3084
3946
|
resolved = def.thunk();
|
|
@@ -3088,7 +3950,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
3088
3950
|
return emit(resolved, ctx, pos);
|
|
3089
3951
|
}
|
|
3090
3952
|
if (!ctx.namedParsers.has(p)) {
|
|
3091
|
-
const fnName2 =
|
|
3953
|
+
const fnName2 = canonical ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
3092
3954
|
ctx.namedParsers.set(p, fnName2);
|
|
3093
3955
|
let resolved;
|
|
3094
3956
|
try {
|
|
@@ -3099,11 +3961,14 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
3099
3961
|
}
|
|
3100
3962
|
const savedIndent = ctx.indent;
|
|
3101
3963
|
const savedFailLabel = ctx.failLabel;
|
|
3964
|
+
const savedRecord = ctx.recordFail;
|
|
3102
3965
|
ctx.indent = 1;
|
|
3103
3966
|
ctx.failLabel = "_pfail";
|
|
3967
|
+
ctx.recordFail = true;
|
|
3104
3968
|
const r = emit(resolved, ctx, "_pos");
|
|
3105
3969
|
ctx.indent = savedIndent;
|
|
3106
3970
|
ctx.failLabel = savedFailLabel;
|
|
3971
|
+
ctx.recordFail = savedRecord;
|
|
3107
3972
|
pushNamedFnDecl(ctx, fnName2, r.stmts, r.valueVar, r.endVar);
|
|
3108
3973
|
}
|
|
3109
3974
|
const fnName = ctx.namedParsers.get(p);
|
|
@@ -3203,7 +4068,7 @@ function emit(p, ctx, pos) {
|
|
|
3203
4068
|
return {
|
|
3204
4069
|
stmts: [
|
|
3205
4070
|
...inner.stmts,
|
|
3206
|
-
`${ind(ctx)}const ${mv} =
|
|
4071
|
+
`${ind(ctx)}const ${mv} = ${mfRef(ctx)}[${fnIdx}](${inner.valueVar}, { start: ${pos}, end: ${inner.endVar} })`
|
|
3207
4072
|
],
|
|
3208
4073
|
valueVar: mv,
|
|
3209
4074
|
endVar: inner.endVar
|
|
@@ -3266,7 +4131,7 @@ function emit(p, ctx, pos) {
|
|
|
3266
4131
|
const vv = v(ctx);
|
|
3267
4132
|
return {
|
|
3268
4133
|
stmts: [
|
|
3269
|
-
...emitIfFail(ctx,
|
|
4134
|
+
...emitIfFail(ctx, `!${mfRef(ctx)}[${fnIdx}](_ctx.state)`, failBody(ctx, '"gate"', pos)),
|
|
3270
4135
|
`${ind(ctx)}const ${vv} = null`
|
|
3271
4136
|
],
|
|
3272
4137
|
valueVar: vv,
|
|
@@ -3283,11 +4148,14 @@ function emit(p, ctx, pos) {
|
|
|
3283
4148
|
ctx.namedParsers.set(innerParser, fnName);
|
|
3284
4149
|
const savedIndent = ctx.indent;
|
|
3285
4150
|
const savedFailLabel = ctx.failLabel;
|
|
4151
|
+
const savedRecord = ctx.recordFail;
|
|
3286
4152
|
ctx.indent = 1;
|
|
3287
4153
|
ctx.failLabel = "_pfail";
|
|
4154
|
+
ctx.recordFail = true;
|
|
3288
4155
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
3289
4156
|
ctx.indent = savedIndent;
|
|
3290
4157
|
ctx.failLabel = savedFailLabel;
|
|
4158
|
+
ctx.recordFail = savedRecord;
|
|
3291
4159
|
pushNamedFnDecl(ctx, fnName, innerR.stmts, innerR.valueVar, innerR.endVar);
|
|
3292
4160
|
}
|
|
3293
4161
|
const fn = ctx.namedParsers.get(innerParser);
|
|
@@ -3296,7 +4164,7 @@ function emit(p, ctx, pos) {
|
|
|
3296
4164
|
const ev = v(ctx, "_wce");
|
|
3297
4165
|
return {
|
|
3298
4166
|
stmts: [
|
|
3299
|
-
`${ind(ctx)}const ${rv} = { ..._ctx, state:
|
|
4167
|
+
`${ind(ctx)}const ${rv} = { ..._ctx, state: ${mfRef(ctx)}[${evIdx}]() }`,
|
|
3300
4168
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
3301
4169
|
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3302
4170
|
// and propagate the inner failure verbatim (interpreter parity).
|
|
@@ -3427,6 +4295,39 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
3427
4295
|
for (const root of roots) walk2(root);
|
|
3428
4296
|
return { counts, recursive };
|
|
3429
4297
|
}
|
|
4298
|
+
function ruleDependencies(ruleMap) {
|
|
4299
|
+
const nameOf = /* @__PURE__ */ new Map();
|
|
4300
|
+
for (const [name, comb] of ruleMap) nameOf.set(comb, name);
|
|
4301
|
+
const deps = /* @__PURE__ */ new Map();
|
|
4302
|
+
for (const [name, comb] of ruleMap) {
|
|
4303
|
+
const found = /* @__PURE__ */ new Set();
|
|
4304
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4305
|
+
const walk2 = (p, isRoot) => {
|
|
4306
|
+
const def = p._def;
|
|
4307
|
+
const boundary = def.tag === "lazy" ? p._ruleName ?? nameOf.get(p) : nameOf.get(p);
|
|
4308
|
+
if (!isRoot && boundary !== void 0) {
|
|
4309
|
+
found.add(boundary);
|
|
4310
|
+
return;
|
|
4311
|
+
}
|
|
4312
|
+
if (seen.has(p)) return;
|
|
4313
|
+
seen.add(p);
|
|
4314
|
+
if (def.tag === "lazy") {
|
|
4315
|
+
let resolved;
|
|
4316
|
+
try {
|
|
4317
|
+
resolved = def.thunk();
|
|
4318
|
+
} catch {
|
|
4319
|
+
return;
|
|
4320
|
+
}
|
|
4321
|
+
walk2(resolved, false);
|
|
4322
|
+
return;
|
|
4323
|
+
}
|
|
4324
|
+
for (const child of childrenOf(def)) walk2(child, false);
|
|
4325
|
+
};
|
|
4326
|
+
walk2(comb, true);
|
|
4327
|
+
deps.set(name, [...found]);
|
|
4328
|
+
}
|
|
4329
|
+
return deps;
|
|
4330
|
+
}
|
|
3430
4331
|
function compile(combinator, mapFnSources) {
|
|
3431
4332
|
const ctx = {
|
|
3432
4333
|
vars: 0,
|
|
@@ -3435,6 +4336,7 @@ function compile(combinator, mapFnSources) {
|
|
|
3435
4336
|
regexMap: /* @__PURE__ */ new Map(),
|
|
3436
4337
|
expectedDecls: [],
|
|
3437
4338
|
expectedMap: /* @__PURE__ */ new Map(),
|
|
4339
|
+
recordFail: true,
|
|
3438
4340
|
mapFns: [],
|
|
3439
4341
|
mapFnSrcs: [],
|
|
3440
4342
|
buildFns: [],
|
|
@@ -3509,6 +4411,150 @@ function compile(combinator, mapFnSources) {
|
|
|
3509
4411
|
}
|
|
3510
4412
|
};
|
|
3511
4413
|
}
|
|
4414
|
+
function compileLinkable(ruleMapArg, ns) {
|
|
4415
|
+
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4416
|
+
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4417
|
+
const d = val._def;
|
|
4418
|
+
if (d.tag !== "lazy") return true;
|
|
4419
|
+
try {
|
|
4420
|
+
d.thunk();
|
|
4421
|
+
return true;
|
|
4422
|
+
} catch {
|
|
4423
|
+
return false;
|
|
4424
|
+
}
|
|
4425
|
+
});
|
|
4426
|
+
const ctx = {
|
|
4427
|
+
vars: 0,
|
|
4428
|
+
indent: 1,
|
|
4429
|
+
regexDecls: [],
|
|
4430
|
+
regexMap: /* @__PURE__ */ new Map(),
|
|
4431
|
+
expectedDecls: [],
|
|
4432
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4433
|
+
recordFail: true,
|
|
4434
|
+
mapFns: [],
|
|
4435
|
+
mapFnSrcs: [],
|
|
4436
|
+
buildFns: [],
|
|
4437
|
+
buildSrcs: [],
|
|
4438
|
+
runtimeParsers: [],
|
|
4439
|
+
needsCollator: false,
|
|
4440
|
+
namedParsers: /* @__PURE__ */ new Map(),
|
|
4441
|
+
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4442
|
+
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4443
|
+
namedFnDecls: [],
|
|
4444
|
+
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4445
|
+
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4446
|
+
ns
|
|
4447
|
+
};
|
|
4448
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4449
|
+
const ruleNames = /* @__PURE__ */ new Map();
|
|
4450
|
+
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4451
|
+
for (const [key, rule] of ruleMap) {
|
|
4452
|
+
const base = `_r_${key.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4453
|
+
const n = seen.get(base) ?? 0;
|
|
4454
|
+
seen.set(base, n + 1);
|
|
4455
|
+
const fn = n === 0 ? base : `${base}$${n}`;
|
|
4456
|
+
ruleNames.set(rule, fn);
|
|
4457
|
+
fnNameToKey.set(fn, key);
|
|
4458
|
+
}
|
|
4459
|
+
ctx.ruleNames = ruleNames;
|
|
4460
|
+
{
|
|
4461
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
4462
|
+
const scanExternal = (p) => {
|
|
4463
|
+
if (scanned.has(p)) return;
|
|
4464
|
+
scanned.add(p);
|
|
4465
|
+
const def = p._def;
|
|
4466
|
+
if (def.tag === "lazy") {
|
|
4467
|
+
let resolved;
|
|
4468
|
+
try {
|
|
4469
|
+
resolved = def.thunk();
|
|
4470
|
+
} catch {
|
|
4471
|
+
resolved = void 0;
|
|
4472
|
+
}
|
|
4473
|
+
if (resolved === void 0) {
|
|
4474
|
+
const name = p._ruleName;
|
|
4475
|
+
if (name && !ctx.namedParsers.has(p)) {
|
|
4476
|
+
const fn = `_r_${name.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4477
|
+
ruleNames.set(p, fn);
|
|
4478
|
+
ctx.namedParsers.set(p, fn);
|
|
4479
|
+
}
|
|
4480
|
+
return;
|
|
4481
|
+
}
|
|
4482
|
+
scanExternal(resolved);
|
|
4483
|
+
return;
|
|
4484
|
+
}
|
|
4485
|
+
for (const child of childrenOf(def)) scanExternal(child);
|
|
4486
|
+
};
|
|
4487
|
+
for (const [, rule] of ruleMap) scanExternal(rule);
|
|
4488
|
+
}
|
|
4489
|
+
const perEntry = [];
|
|
4490
|
+
for (const [key, rule] of ruleMap) {
|
|
4491
|
+
const fn = ruleNames.get(rule);
|
|
4492
|
+
if (!ctx.namedParsers.has(rule)) {
|
|
4493
|
+
ctx.namedParsers.set(rule, fn);
|
|
4494
|
+
let resolved = rule;
|
|
4495
|
+
const def = rule._def;
|
|
4496
|
+
if (def.tag === "lazy") {
|
|
4497
|
+
try {
|
|
4498
|
+
resolved = def.thunk();
|
|
4499
|
+
} catch {
|
|
4500
|
+
return null;
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
const savedIndent = ctx.indent, savedFail = ctx.failLabel, savedRec = ctx.recordFail;
|
|
4504
|
+
ctx.indent = 1;
|
|
4505
|
+
ctx.failLabel = "_pfail";
|
|
4506
|
+
ctx.recordFail = true;
|
|
4507
|
+
const body = emit(resolved, ctx, "_pos");
|
|
4508
|
+
ctx.indent = savedIndent;
|
|
4509
|
+
ctx.failLabel = savedFail;
|
|
4510
|
+
ctx.recordFail = savedRec;
|
|
4511
|
+
pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
|
|
4512
|
+
}
|
|
4513
|
+
perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
|
|
4514
|
+
}
|
|
4515
|
+
if (ctx.runtimeParsers.length !== 0) return null;
|
|
4516
|
+
const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
|
|
4517
|
+
const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
|
|
4518
|
+
const ruleFns = /* @__PURE__ */ new Map();
|
|
4519
|
+
const privateFns = [];
|
|
4520
|
+
for (const decl of ctx.namedFnDecls) {
|
|
4521
|
+
const fnName = decl.match(/^function (\S+?)\(/)?.[1];
|
|
4522
|
+
const key = fnName ? fnNameToKey.get(fnName) : void 0;
|
|
4523
|
+
if (key !== void 0) ruleFns.set(key, decl);
|
|
4524
|
+
else privateFns.push(decl);
|
|
4525
|
+
}
|
|
4526
|
+
const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
|
|
4527
|
+
const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
|
|
4528
|
+
const prelude = [
|
|
4529
|
+
...ctx.regexDecls,
|
|
4530
|
+
...ctx.expectedDecls,
|
|
4531
|
+
mfDecl,
|
|
4532
|
+
buildDecl,
|
|
4533
|
+
...privateFns
|
|
4534
|
+
].filter(Boolean);
|
|
4535
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
4536
|
+
for (const { key, r } of perEntry) {
|
|
4537
|
+
wrappers.set(key, [
|
|
4538
|
+
`function(input, _pos, _ctx) {`,
|
|
4539
|
+
` let pos = _pos`,
|
|
4540
|
+
...r.stmts,
|
|
4541
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
4542
|
+
`}`
|
|
4543
|
+
].join("\n"));
|
|
4544
|
+
}
|
|
4545
|
+
return {
|
|
4546
|
+
ns,
|
|
4547
|
+
keys: perEntry.map((e) => e.key),
|
|
4548
|
+
prelude,
|
|
4549
|
+
ruleFns,
|
|
4550
|
+
wrappers,
|
|
4551
|
+
deps: ruleDependencies(ruleMap),
|
|
4552
|
+
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4553
|
+
needsCollator: ctx.needsCollator,
|
|
4554
|
+
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4555
|
+
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4556
|
+
};
|
|
4557
|
+
}
|
|
3512
4558
|
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
3513
4559
|
const bodyLines = [
|
|
3514
4560
|
` let pos = _pos`,
|
|
@@ -3547,6 +4593,87 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
3547
4593
|
].join("\n");
|
|
3548
4594
|
}
|
|
3549
4595
|
|
|
4596
|
+
// src/compiler/linker.ts
|
|
4597
|
+
var _nsCounter = 0;
|
|
4598
|
+
function linkable(rulesMap, ns) {
|
|
4599
|
+
const pieces = compileLinkable([...Object.entries(rulesMap)], ns ?? `_lk${_nsCounter++}_`);
|
|
4600
|
+
if (!pieces) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
|
|
4601
|
+
return pieces;
|
|
4602
|
+
}
|
|
4603
|
+
function cstBuildHost(type, children, _rawChildren, span, _triviaLog, state) {
|
|
4604
|
+
return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
|
|
4605
|
+
}
|
|
4606
|
+
function pick(grammar, names) {
|
|
4607
|
+
const p = grammar.ruleFns instanceof Map ? grammar : linkable(grammar);
|
|
4608
|
+
const keep = /* @__PURE__ */ new Set();
|
|
4609
|
+
const has = new Set(p.keys);
|
|
4610
|
+
for (const n of names) {
|
|
4611
|
+
if (!has.has(n)) throw new Error(`pick: rule "${n}" is not in this grammar (available: ${p.keys.join(", ")})`);
|
|
4612
|
+
}
|
|
4613
|
+
const visit = (n) => {
|
|
4614
|
+
if (keep.has(n) || !has.has(n)) return;
|
|
4615
|
+
keep.add(n);
|
|
4616
|
+
for (const d of p.deps.get(n) ?? []) visit(d);
|
|
4617
|
+
};
|
|
4618
|
+
for (const n of names) visit(n);
|
|
4619
|
+
const filt = (m) => new Map([...m].filter(([k]) => keep.has(k)));
|
|
4620
|
+
return {
|
|
4621
|
+
...p,
|
|
4622
|
+
keys: [...keep],
|
|
4623
|
+
ruleFns: filt(p.ruleFns),
|
|
4624
|
+
wrappers: filt(p.wrappers),
|
|
4625
|
+
deps: filt(p.deps)
|
|
4626
|
+
};
|
|
4627
|
+
}
|
|
4628
|
+
function fusedBody(pieces) {
|
|
4629
|
+
const winner = /* @__PURE__ */ new Map();
|
|
4630
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4631
|
+
for (const [k, p] of winner) {
|
|
4632
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
4633
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
const contributing = new Set(winner.values());
|
|
4637
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4638
|
+
const needsCollator = [...contributing].some((p) => p.needsCollator);
|
|
4639
|
+
const lines = [
|
|
4640
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4641
|
+
"const _pfFail = {}",
|
|
4642
|
+
"let _pfEnd",
|
|
4643
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4644
|
+
...needsCollator ? ["const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })"] : [],
|
|
4645
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4646
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
4647
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4648
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4649
|
+
];
|
|
4650
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4651
|
+
const body = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4652
|
+
const env = {};
|
|
4653
|
+
for (const p of contributing) {
|
|
4654
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4655
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4656
|
+
}
|
|
4657
|
+
return { body, env };
|
|
4658
|
+
}
|
|
4659
|
+
function fuseRules(pieces) {
|
|
4660
|
+
const { body, env } = fusedBody(pieces);
|
|
4661
|
+
return new Function("_env", body)(env);
|
|
4662
|
+
}
|
|
4663
|
+
var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
|
|
4664
|
+
function itemPieces(item) {
|
|
4665
|
+
const carried = item[COMPOSED_PIECES];
|
|
4666
|
+
if (Array.isArray(carried)) return carried;
|
|
4667
|
+
if (item.ruleFns instanceof Map) return [item];
|
|
4668
|
+
return [linkable(item)];
|
|
4669
|
+
}
|
|
4670
|
+
function compose(items) {
|
|
4671
|
+
const pieces = items.flatMap(itemPieces);
|
|
4672
|
+
const map = fuseRules(pieces);
|
|
4673
|
+
Object.defineProperty(map, COMPOSED_PIECES, { value: pieces, enumerable: false });
|
|
4674
|
+
return map;
|
|
4675
|
+
}
|
|
4676
|
+
|
|
3550
4677
|
// src/combinators/guard.ts
|
|
3551
4678
|
function guard(predicate) {
|
|
3552
4679
|
const meta = {
|
|
@@ -3744,6 +4871,7 @@ function manyRecover(item, until) {
|
|
|
3744
4871
|
}
|
|
3745
4872
|
|
|
3746
4873
|
// src/functional/doc.ts
|
|
4874
|
+
var REENTRY_MAX_SPAN_FRACTION = 0.5;
|
|
3747
4875
|
function isNode(x) {
|
|
3748
4876
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
3749
4877
|
}
|
|
@@ -3804,7 +4932,48 @@ function graftAndShift(root, path, newNode, delta) {
|
|
|
3804
4932
|
children: newChildren
|
|
3805
4933
|
};
|
|
3806
4934
|
}
|
|
3807
|
-
|
|
4935
|
+
function structurallyEqual(a, b) {
|
|
4936
|
+
if (a === b) return true;
|
|
4937
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
4938
|
+
const at = a._tag;
|
|
4939
|
+
const bt = b._tag;
|
|
4940
|
+
if (at !== bt) return false;
|
|
4941
|
+
const as = a.span;
|
|
4942
|
+
const bs = b.span;
|
|
4943
|
+
if (as || bs) {
|
|
4944
|
+
if (!as || !bs || as.start !== bs.start || as.end !== bs.end) return false;
|
|
4945
|
+
}
|
|
4946
|
+
if (at === "leaf" || at === "trivia") {
|
|
4947
|
+
return a.value === b.value;
|
|
4948
|
+
}
|
|
4949
|
+
if (a.type !== b.type) return false;
|
|
4950
|
+
const ac = a.children;
|
|
4951
|
+
const bc = b.children;
|
|
4952
|
+
if (ac || bc) {
|
|
4953
|
+
if (!ac || !bc || ac.length !== bc.length) return false;
|
|
4954
|
+
for (let i = 0; i < ac.length; i++) if (!structurallyEqual(ac[i], bc[i])) return false;
|
|
4955
|
+
}
|
|
4956
|
+
return true;
|
|
4957
|
+
}
|
|
4958
|
+
function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced) {
|
|
4959
|
+
if (!produced.ok) return false;
|
|
4960
|
+
if (boundary >= newInput.length) return true;
|
|
4961
|
+
for (const sentinel of [" ", "\uFFFF"]) {
|
|
4962
|
+
if (newInput[boundary] === sentinel) continue;
|
|
4963
|
+
const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
|
|
4964
|
+
const ctx = { trackLines: false, state, build };
|
|
4965
|
+
let r;
|
|
4966
|
+
try {
|
|
4967
|
+
r = ruleFn(probed, start, ctx);
|
|
4968
|
+
} catch {
|
|
4969
|
+
return false;
|
|
4970
|
+
}
|
|
4971
|
+
if (!r.ok || r.span.end !== produced.span.end) return false;
|
|
4972
|
+
if (!structurallyEqual(r.value, produced.value)) return false;
|
|
4973
|
+
}
|
|
4974
|
+
return true;
|
|
4975
|
+
}
|
|
4976
|
+
var ParseDocImpl = class _ParseDocImpl {
|
|
3808
4977
|
_registry;
|
|
3809
4978
|
_rootRule;
|
|
3810
4979
|
_opts;
|
|
@@ -3821,7 +4990,7 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
|
3821
4990
|
}
|
|
3822
4991
|
edit(from, to, replacement) {
|
|
3823
4992
|
const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
|
|
3824
|
-
const reparse = () =>
|
|
4993
|
+
const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
|
|
3825
4994
|
if (!this.tree) return reparse();
|
|
3826
4995
|
const delta = replacement.length - (to - from);
|
|
3827
4996
|
const found = findContaining(this.tree, from);
|
|
@@ -3835,41 +5004,78 @@ var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
|
3835
5004
|
}
|
|
3836
5005
|
const rebuild = this._opts.rebuild ?? defaultRebuild;
|
|
3837
5006
|
for (const { node: node2, path } of candidates) {
|
|
5007
|
+
if (node2.span.end - node2.span.start > this.input.length * REENTRY_MAX_SPAN_FRACTION) break;
|
|
3838
5008
|
const ruleFn = this._registry[node2.type];
|
|
3839
5009
|
if (!ruleFn) continue;
|
|
3840
|
-
|
|
5010
|
+
if (!(node2.span.start <= from && to <= node2.span.end)) continue;
|
|
5011
|
+
const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
|
|
3841
5012
|
const r = ruleFn(newInput, node2.span.start, ctx);
|
|
3842
5013
|
if (!r.ok) continue;
|
|
3843
|
-
if (r.span.end
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
5014
|
+
if (r.span.end !== node2.span.end + delta) continue;
|
|
5015
|
+
if (!boundaryIsSafe(ruleFn, newInput, node2.span.start, node2.span.end + delta, node2.state, this._opts.build, r)) {
|
|
5016
|
+
continue;
|
|
5017
|
+
}
|
|
5018
|
+
if (delta === 0) {
|
|
5019
|
+
const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
|
|
5020
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
|
|
3851
5021
|
}
|
|
5022
|
+
if (this._opts.rebuild) return reparse();
|
|
5023
|
+
const newTree = graftAndShift(this.tree, path, r.value, delta);
|
|
5024
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
3852
5025
|
}
|
|
3853
5026
|
return reparse();
|
|
3854
5027
|
}
|
|
3855
5028
|
};
|
|
3856
|
-
function
|
|
5029
|
+
function parseDoc(registry, rootRule, input, opts = {}) {
|
|
3857
5030
|
const ruleFn = registry[rootRule];
|
|
3858
5031
|
if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
|
|
3859
|
-
const ctx = { trackLines: false, state: opts.state };
|
|
5032
|
+
const ctx = { trackLines: false, state: opts.state, build: opts.build };
|
|
3860
5033
|
const r = ruleFn(input, 0, ctx);
|
|
3861
5034
|
if (r.ok) {
|
|
3862
|
-
return new
|
|
5035
|
+
return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
|
|
5036
|
+
}
|
|
5037
|
+
return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
5038
|
+
}
|
|
5039
|
+
|
|
5040
|
+
// src/functional/run.ts
|
|
5041
|
+
var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
|
|
5042
|
+
function run(entry, input, options = {}) {
|
|
5043
|
+
const triviaLog = [];
|
|
5044
|
+
const errors = [];
|
|
5045
|
+
const ctx = {
|
|
5046
|
+
trackLines: false,
|
|
5047
|
+
_triviaLog: triviaLog,
|
|
5048
|
+
_errors: errors,
|
|
5049
|
+
build: options.build,
|
|
5050
|
+
state: options.state
|
|
5051
|
+
};
|
|
5052
|
+
const r = invoke(entry, input, 0, ctx);
|
|
5053
|
+
let unconsumedFrom = null;
|
|
5054
|
+
if (r.ok) {
|
|
5055
|
+
let pos = r.span?.end ?? 0;
|
|
5056
|
+
if (options.trivia && pos < input.length) {
|
|
5057
|
+
const t = invoke(options.trivia, input, pos, { trackLines: false });
|
|
5058
|
+
if (t.ok && t.span.end > pos) pos = t.span.end;
|
|
5059
|
+
}
|
|
5060
|
+
unconsumedFrom = pos < input.length ? pos : null;
|
|
3863
5061
|
}
|
|
3864
|
-
return
|
|
5062
|
+
return {
|
|
5063
|
+
ok: r.ok,
|
|
5064
|
+
value: r.ok ? r.value : void 0,
|
|
5065
|
+
span: r.span ?? { start: 0, end: 0 },
|
|
5066
|
+
expected: r.ok ? [] : r.expected ?? [],
|
|
5067
|
+
errors,
|
|
5068
|
+
triviaLog,
|
|
5069
|
+
unconsumedFrom
|
|
5070
|
+
};
|
|
3865
5071
|
}
|
|
3866
5072
|
|
|
3867
5073
|
// src/cst/trivia-index.ts
|
|
3868
|
-
function merge(map, key,
|
|
3869
|
-
if (
|
|
5074
|
+
function merge(map, key, run2) {
|
|
5075
|
+
if (run2.length === 0) return;
|
|
3870
5076
|
const existing = map.get(key);
|
|
3871
|
-
if (existing) existing.push(...
|
|
3872
|
-
else map.set(key, [...
|
|
5077
|
+
if (existing) existing.push(...run2);
|
|
5078
|
+
else map.set(key, [...run2]);
|
|
3873
5079
|
}
|
|
3874
5080
|
function buildTriviaIndex(root, input = "", opts) {
|
|
3875
5081
|
const before = /* @__PURE__ */ new Map();
|
|
@@ -3881,11 +5087,11 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
3881
5087
|
for (let i = 0; i < log.length; i += 3) {
|
|
3882
5088
|
const tStart = log[i], tEnd = log[i + 1], insertIdx = log[i + 2];
|
|
3883
5089
|
if (tEnd <= tStart) continue;
|
|
3884
|
-
const
|
|
5090
|
+
const run2 = [{ value: input.slice(tStart, tEnd), span: { start: tStart, end: tEnd } }];
|
|
3885
5091
|
const next = raw[insertIdx];
|
|
3886
|
-
if (next) merge(before, next.span.start,
|
|
5092
|
+
if (next) merge(before, next.span.start, run2);
|
|
3887
5093
|
const prev = insertIdx > 0 ? raw[insertIdx - 1] : void 0;
|
|
3888
|
-
if (prev) merge(after, prev.span.end,
|
|
5094
|
+
if (prev) merge(after, prev.span.end, run2);
|
|
3889
5095
|
}
|
|
3890
5096
|
}
|
|
3891
5097
|
const ch = node2.children;
|
|
@@ -3902,17 +5108,17 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
3902
5108
|
const rootSpan = root?.span;
|
|
3903
5109
|
const re = new RegExp(opts.trivia.source, opts.trivia.flags.replace(/[gy]/g, "") + "y");
|
|
3904
5110
|
const scanFrom = (from) => {
|
|
3905
|
-
const
|
|
5111
|
+
const run2 = [];
|
|
3906
5112
|
let pos = from;
|
|
3907
5113
|
re.lastIndex = pos;
|
|
3908
5114
|
let m = re.exec(input);
|
|
3909
5115
|
while (m && m.index === pos && m[0].length > 0) {
|
|
3910
|
-
|
|
5116
|
+
run2.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
|
|
3911
5117
|
pos += m[0].length;
|
|
3912
5118
|
re.lastIndex = pos;
|
|
3913
5119
|
m = re.exec(input);
|
|
3914
5120
|
}
|
|
3915
|
-
return
|
|
5121
|
+
return run2;
|
|
3916
5122
|
};
|
|
3917
5123
|
if (rootSpan && rootSpan.start > 0) {
|
|
3918
5124
|
const lead = scanFrom(0);
|
|
@@ -3998,4 +5204,263 @@ function triviaEntries(log, labels, opts) {
|
|
|
3998
5204
|
}
|
|
3999
5205
|
};
|
|
4000
5206
|
}
|
|
5207
|
+
|
|
5208
|
+
// src/cst/offset-model.ts
|
|
5209
|
+
var { isFinite } = Number;
|
|
5210
|
+
function floorIndex(arr, len, x) {
|
|
5211
|
+
let lo = 0;
|
|
5212
|
+
let hi = len - 1;
|
|
5213
|
+
let ans = -1;
|
|
5214
|
+
while (lo <= hi) {
|
|
5215
|
+
const mid = lo + hi >>> 1;
|
|
5216
|
+
if (arr[mid] <= x) {
|
|
5217
|
+
ans = mid;
|
|
5218
|
+
lo = mid + 1;
|
|
5219
|
+
} else {
|
|
5220
|
+
hi = mid - 1;
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5223
|
+
return ans;
|
|
5224
|
+
}
|
|
5225
|
+
var OffsetIndex = class {
|
|
5226
|
+
/** Slot starts, sorted ascending. Parallel to `_ends`. */
|
|
5227
|
+
_starts;
|
|
5228
|
+
_ends;
|
|
5229
|
+
count;
|
|
5230
|
+
docStart;
|
|
5231
|
+
docEnd;
|
|
5232
|
+
constructor(slots, docStart = 0, docEnd) {
|
|
5233
|
+
const sorted = [...slots].sort((a, b) => a.start - b.start);
|
|
5234
|
+
const starts = [];
|
|
5235
|
+
const ends = [];
|
|
5236
|
+
let prevEnd = docStart;
|
|
5237
|
+
for (const s of sorted) {
|
|
5238
|
+
if (!isFinite(s.start) || !isFinite(s.end) || s.end < s.start) {
|
|
5239
|
+
throw new RangeError(`OffsetIndex: invalid slot [${s.start}, ${s.end}]`);
|
|
5240
|
+
}
|
|
5241
|
+
if (s.start < prevEnd) {
|
|
5242
|
+
throw new RangeError(`OffsetIndex: overlapping/out-of-order slot [${s.start}, ${s.end}] after end ${prevEnd}`);
|
|
5243
|
+
}
|
|
5244
|
+
starts.push(s.start);
|
|
5245
|
+
ends.push(s.end);
|
|
5246
|
+
prevEnd = s.end;
|
|
5247
|
+
}
|
|
5248
|
+
this._starts = starts;
|
|
5249
|
+
this._ends = ends;
|
|
5250
|
+
this.count = starts.length;
|
|
5251
|
+
this.docStart = docStart;
|
|
5252
|
+
this.docEnd = docEnd ?? (this.count ? Math.max(prevEnd, ends[ends.length - 1]) : docStart);
|
|
5253
|
+
}
|
|
5254
|
+
/** The slot at index `i` (0-based, source order), or undefined. */
|
|
5255
|
+
slot(i) {
|
|
5256
|
+
if (i < 0 || i >= this.count) return void 0;
|
|
5257
|
+
return { start: this._starts[i], end: this._ends[i] };
|
|
5258
|
+
}
|
|
5259
|
+
/**
|
|
5260
|
+
* Index of the gap that CONTAINS `offset`, in `[0, count]`. Gap `i` spans
|
|
5261
|
+
* `[endOf(i-1), startOf(i))` where `endOf(-1)=docStart` and `startOf(count)=docEnd`.
|
|
5262
|
+
* An offset that lands inside a slot returns that slot's TRAILING gap index
|
|
5263
|
+
* (i.e. the gap after it) — callers querying at slot boundaries get the
|
|
5264
|
+
* intuitive answer via `gapBefore`/`gapAfter`.
|
|
5265
|
+
*/
|
|
5266
|
+
gapIndexAt(offset) {
|
|
5267
|
+
const i = floorIndex(this._starts, this.count, offset);
|
|
5268
|
+
if (i < 0) return 0;
|
|
5269
|
+
if (offset < this._ends[i]) return i + 1;
|
|
5270
|
+
return i + 1;
|
|
5271
|
+
}
|
|
5272
|
+
/** The gap at index `i` in `[0, count]`. */
|
|
5273
|
+
gap(i) {
|
|
5274
|
+
if (i < 0 || i > this.count) return void 0;
|
|
5275
|
+
const start = i === 0 ? this.docStart : this._ends[i - 1];
|
|
5276
|
+
const end = i === this.count ? this.docEnd : this._starts[i];
|
|
5277
|
+
return { start, end };
|
|
5278
|
+
}
|
|
5279
|
+
/**
|
|
5280
|
+
* The trivia gap immediately BEFORE `offset` (`[prevSlotEnd, offset)`), where
|
|
5281
|
+
* `prevSlotEnd` is the end of the last slot ending at or before `offset` (or
|
|
5282
|
+
* `docStart`). For a real slot start this is the gap preceding that slot; for an
|
|
5283
|
+
* adjacent slot the gap is empty and agrees with `gapAfter` on the same seam.
|
|
5284
|
+
*/
|
|
5285
|
+
gapBefore(offset) {
|
|
5286
|
+
const e = floorIndex(this._ends, this.count, offset);
|
|
5287
|
+
const start = e >= 0 ? this._ends[e] : this.docStart;
|
|
5288
|
+
return { start, end: offset };
|
|
5289
|
+
}
|
|
5290
|
+
/**
|
|
5291
|
+
* The trivia gap immediately AFTER `offset` (`[offset, nextSlotStart)`), where
|
|
5292
|
+
* `nextSlotStart` is the least slot start at or after `offset` (or `docEnd`).
|
|
5293
|
+
*/
|
|
5294
|
+
gapAfter(offset) {
|
|
5295
|
+
return { start: offset, end: this._ceilStart(offset) };
|
|
5296
|
+
}
|
|
5297
|
+
/** Least slot start `>= offset`, else `docEnd`. */
|
|
5298
|
+
_ceilStart(offset) {
|
|
5299
|
+
let lo = 0;
|
|
5300
|
+
let hi = this.count - 1;
|
|
5301
|
+
let ans = this.docEnd;
|
|
5302
|
+
while (lo <= hi) {
|
|
5303
|
+
const mid = lo + hi >>> 1;
|
|
5304
|
+
if (this._starts[mid] >= offset) {
|
|
5305
|
+
ans = this._starts[mid];
|
|
5306
|
+
hi = mid - 1;
|
|
5307
|
+
} else {
|
|
5308
|
+
lo = mid + 1;
|
|
5309
|
+
}
|
|
5310
|
+
}
|
|
5311
|
+
return ans;
|
|
5312
|
+
}
|
|
5313
|
+
/** Is this gap empty (adjacent slots, no trivia)? */
|
|
5314
|
+
isEmpty(gap) {
|
|
5315
|
+
return gap.end <= gap.start;
|
|
5316
|
+
}
|
|
5317
|
+
};
|
|
5318
|
+
var NL = 10;
|
|
5319
|
+
var CR = 13;
|
|
5320
|
+
var SP = 32;
|
|
5321
|
+
var TAB = 9;
|
|
5322
|
+
function gapText(source, gap) {
|
|
5323
|
+
return gap.end > gap.start ? source.slice(gap.start, gap.end) : "";
|
|
5324
|
+
}
|
|
5325
|
+
function lineBreaksIn(source, gap) {
|
|
5326
|
+
let n = 0;
|
|
5327
|
+
for (let i = gap.start; i < gap.end; i++) {
|
|
5328
|
+
const c = source.charCodeAt(i);
|
|
5329
|
+
if (c === NL) n++;
|
|
5330
|
+
else if (c === CR && source.charCodeAt(i + 1) !== NL) n++;
|
|
5331
|
+
}
|
|
5332
|
+
return n;
|
|
5333
|
+
}
|
|
5334
|
+
function blankLinesIn(source, gap) {
|
|
5335
|
+
return Math.max(0, lineBreaksIn(source, gap) - 1);
|
|
5336
|
+
}
|
|
5337
|
+
function lineStartWithin(source, gap) {
|
|
5338
|
+
for (let i = gap.end - 1; i >= gap.start; i--) {
|
|
5339
|
+
const c = source.charCodeAt(i);
|
|
5340
|
+
if (c === NL || c === CR) return i + 1;
|
|
5341
|
+
}
|
|
5342
|
+
return gap.start;
|
|
5343
|
+
}
|
|
5344
|
+
function indentWidth(source, gap) {
|
|
5345
|
+
const ls = lineStartWithin(source, gap);
|
|
5346
|
+
if (ls === gap.start) return 0;
|
|
5347
|
+
return gap.end - ls;
|
|
5348
|
+
}
|
|
5349
|
+
function indentMixed(source, gap) {
|
|
5350
|
+
const ls = lineStartWithin(source, gap);
|
|
5351
|
+
let sawSpace = false;
|
|
5352
|
+
let sawTab = false;
|
|
5353
|
+
for (let i = ls; i < gap.end; i++) {
|
|
5354
|
+
const c = source.charCodeAt(i);
|
|
5355
|
+
if (c === SP) sawSpace = true;
|
|
5356
|
+
else if (c === TAB) sawTab = true;
|
|
5357
|
+
}
|
|
5358
|
+
return sawSpace && sawTab;
|
|
5359
|
+
}
|
|
5360
|
+
function commentsIn(source, gap, comment) {
|
|
5361
|
+
const re = new RegExp(comment.source, comment.flags.replace(/[gy]/g, "") + "g");
|
|
5362
|
+
re.lastIndex = gap.start;
|
|
5363
|
+
const out = [];
|
|
5364
|
+
let m;
|
|
5365
|
+
while ((m = re.exec(source)) && m.index < gap.end) {
|
|
5366
|
+
if (m[0].length === 0) {
|
|
5367
|
+
re.lastIndex++;
|
|
5368
|
+
continue;
|
|
5369
|
+
}
|
|
5370
|
+
if (m.index + m[0].length > gap.end) break;
|
|
5371
|
+
out.push({ start: m.index, end: m.index + m[0].length });
|
|
5372
|
+
}
|
|
5373
|
+
return out;
|
|
5374
|
+
}
|
|
5375
|
+
function gapIsSignificant(source, gap, comment) {
|
|
5376
|
+
if (gap.end <= gap.start) return false;
|
|
5377
|
+
if (lineBreaksIn(source, gap) > 0) return true;
|
|
5378
|
+
if (comment && commentsIn(source, gap, comment).length > 0) return true;
|
|
5379
|
+
return false;
|
|
5380
|
+
}
|
|
5381
|
+
function collectLeafSlots(root) {
|
|
5382
|
+
const slots = [];
|
|
5383
|
+
const visit = (n) => {
|
|
5384
|
+
const kids = n.children;
|
|
5385
|
+
if (kids && kids.length) {
|
|
5386
|
+
for (const c of kids) visit(c);
|
|
5387
|
+
} else if (n.span) {
|
|
5388
|
+
slots.push({ start: n.span.start, end: n.span.end });
|
|
5389
|
+
}
|
|
5390
|
+
};
|
|
5391
|
+
if (root) visit(root);
|
|
5392
|
+
return slots;
|
|
5393
|
+
}
|
|
5394
|
+
function buildOffsetIndex(root, input = "") {
|
|
5395
|
+
return new OffsetIndex(collectLeafSlots(root), 0, input.length);
|
|
5396
|
+
}
|
|
5397
|
+
|
|
5398
|
+
// src/cst/relative-spans.ts
|
|
5399
|
+
function relativize(node2, parentBase = 0) {
|
|
5400
|
+
return {
|
|
5401
|
+
start: node2.start - parentBase,
|
|
5402
|
+
end: node2.end - parentBase,
|
|
5403
|
+
children: node2.children.map((c) => relativize(c, node2.start))
|
|
5404
|
+
};
|
|
5405
|
+
}
|
|
5406
|
+
function absolutize(node2, parentBase = 0) {
|
|
5407
|
+
const start = parentBase + node2.start;
|
|
5408
|
+
return {
|
|
5409
|
+
start,
|
|
5410
|
+
end: parentBase + node2.end,
|
|
5411
|
+
children: node2.children.map((c) => absolutize(c, start))
|
|
5412
|
+
};
|
|
5413
|
+
}
|
|
5414
|
+
function absoluteSpanAt(root, path) {
|
|
5415
|
+
let base = 0;
|
|
5416
|
+
let node2 = root;
|
|
5417
|
+
for (const idx of path) {
|
|
5418
|
+
base += node2.start;
|
|
5419
|
+
const child = node2.children[idx];
|
|
5420
|
+
if (!child) throw new RangeError(`absoluteSpanAt: no child ${idx} on path ${path.join(".")}`);
|
|
5421
|
+
node2 = child;
|
|
5422
|
+
}
|
|
5423
|
+
return { start: base + node2.start, end: base + node2.end };
|
|
5424
|
+
}
|
|
5425
|
+
function shiftAbsolute(node2, at, delta) {
|
|
5426
|
+
const s = node2.start >= at ? node2.start + delta : node2.start;
|
|
5427
|
+
const e = node2.end >= at ? node2.end + delta : node2.end;
|
|
5428
|
+
return {
|
|
5429
|
+
start: s,
|
|
5430
|
+
end: e,
|
|
5431
|
+
children: node2.children.map((c) => shiftAbsolute(c, at, delta))
|
|
5432
|
+
};
|
|
5433
|
+
}
|
|
5434
|
+
function applyEdit(root, at, delta, stats) {
|
|
5435
|
+
return editNode(root, at, delta, 0, 0, stats);
|
|
5436
|
+
}
|
|
5437
|
+
function editNode(node2, at, delta, parentBase, parentShift, stats) {
|
|
5438
|
+
const absStart = parentBase + node2.start;
|
|
5439
|
+
const absEnd = parentBase + node2.end;
|
|
5440
|
+
if (absEnd < at) return node2;
|
|
5441
|
+
const startShifts = absStart >= at;
|
|
5442
|
+
if (startShifts && parentShift === delta) return node2;
|
|
5443
|
+
if (stats) stats.allocated++;
|
|
5444
|
+
const newAbsStart = startShifts ? absStart + delta : absStart;
|
|
5445
|
+
const newAbsEnd = absEnd + delta;
|
|
5446
|
+
const newParentBase = parentBase + parentShift;
|
|
5447
|
+
const newStart = newAbsStart - newParentBase;
|
|
5448
|
+
const newEnd = newAbsEnd - newParentBase;
|
|
5449
|
+
const ownShift = newAbsStart - absStart;
|
|
5450
|
+
let changed = false;
|
|
5451
|
+
const newChildren = [];
|
|
5452
|
+
for (const c of node2.children) {
|
|
5453
|
+
const nc = editNode(c, at, delta, absStart, ownShift, stats);
|
|
5454
|
+
if (nc !== c) changed = true;
|
|
5455
|
+
newChildren.push(nc);
|
|
5456
|
+
}
|
|
5457
|
+
return {
|
|
5458
|
+
start: newStart,
|
|
5459
|
+
end: newEnd,
|
|
5460
|
+
children: changed ? newChildren : node2.children
|
|
5461
|
+
};
|
|
5462
|
+
}
|
|
5463
|
+
|
|
5464
|
+
// src/index.ts
|
|
5465
|
+
registerRegexAnalyzer(firstSetFromRegex);
|
|
4001
5466
|
//# sourceMappingURL=index.cjs.map
|