parseman 0.13.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.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/functional/doc.d.ts +20 -0
- 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 +902 -112
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +902 -112
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts +2 -28
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +710 -250
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +710 -250
- 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 +3 -1
package/dist/index.cjs
CHANGED
|
@@ -30,19 +30,35 @@ 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
63
|
makeWord: () => makeWord,
|
|
48
64
|
many: () => many,
|
|
@@ -56,14 +72,18 @@ __export(index_exports, {
|
|
|
56
72
|
parse: () => parse,
|
|
57
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
|
}
|
|
@@ -1746,6 +1767,15 @@ function seqIsUnambiguous(parts) {
|
|
|
1746
1767
|
const p = parts[k];
|
|
1747
1768
|
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1748
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
|
+
}
|
|
1749
1779
|
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1750
1780
|
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1751
1781
|
if (skippableSingle || isUnboundedRepeat) {
|
|
@@ -1792,6 +1822,58 @@ function firstSetsDisjoint(a, b) {
|
|
|
1792
1822
|
if (a === "any" || b === "any") return false;
|
|
1793
1823
|
return classDisjoint(a, b);
|
|
1794
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
|
+
}
|
|
1795
1877
|
function trailingBacktrackClass(shape) {
|
|
1796
1878
|
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1797
1879
|
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
@@ -1803,18 +1885,10 @@ function trailingBacktrackClass(shape) {
|
|
|
1803
1885
|
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1804
1886
|
return null;
|
|
1805
1887
|
}
|
|
1806
|
-
|
|
1807
|
-
if (innerWiggle === "unsupported") return "unsupported";
|
|
1808
|
-
const requiredOnce = !last.unbounded && last.min === 1;
|
|
1809
|
-
if (requiredOnce) return innerWiggle;
|
|
1810
|
-
const dropExposed = shapeFirstAccept(last.inner);
|
|
1811
|
-
if (dropExposed === "any") return "unsupported";
|
|
1812
|
-
if (innerWiggle === null) return dropExposed;
|
|
1813
|
-
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1814
|
-
return unioned === "any" ? "unsupported" : unioned;
|
|
1888
|
+
return groupPartExposure(last);
|
|
1815
1889
|
}
|
|
1816
1890
|
if (shape.kind === "alt") {
|
|
1817
|
-
if (!shape.disjoint) return "unsupported";
|
|
1891
|
+
if (!shape.disjoint) return altFixedMutuallyExclusive(shape) ? null : "unsupported";
|
|
1818
1892
|
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1819
1893
|
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1820
1894
|
const concrete = classes.filter((c) => c !== null);
|
|
@@ -1954,7 +2028,7 @@ function seqFirstAccept(parts) {
|
|
|
1954
2028
|
function groupInnerSafe(shape) {
|
|
1955
2029
|
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1956
2030
|
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1957
|
-
if (shape.kind === "alt") return
|
|
2031
|
+
if (shape.kind === "alt") return true;
|
|
1958
2032
|
return false;
|
|
1959
2033
|
}
|
|
1960
2034
|
function shapeFirstAccept(shape) {
|
|
@@ -1982,9 +2056,8 @@ function shapeFirstAccept(shape) {
|
|
|
1982
2056
|
return shapeFirstAccept(shape.inner);
|
|
1983
2057
|
// zero-width — doesn't change first-char acceptance
|
|
1984
2058
|
case "alt": {
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
|
|
2059
|
+
const sets = shape.firsts.map((f) => f === null ? "any" : f);
|
|
2060
|
+
return unionCharSets(sets);
|
|
1988
2061
|
}
|
|
1989
2062
|
}
|
|
1990
2063
|
}
|
|
@@ -2025,6 +2098,120 @@ function parseScanShape(source) {
|
|
|
2025
2098
|
}
|
|
2026
2099
|
return parseScanShapeCore(source);
|
|
2027
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
|
+
}
|
|
2028
2215
|
function parseScanShapeCore(source) {
|
|
2029
2216
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
2030
2217
|
if (m && !m[1].startsWith("^")) {
|
|
@@ -2049,14 +2236,8 @@ function parseScanShapeCore(source) {
|
|
|
2049
2236
|
const stop = parseClassRanges(m[2]);
|
|
2050
2237
|
if (open && stop) return { kind: "until", open, stop };
|
|
2051
2238
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
if (m && m[1] && m[2]) {
|
|
2055
|
-
const open = literalCodePoints(m[1]);
|
|
2056
|
-
const close = literalCodePoints(m[2]);
|
|
2057
|
-
if (open && close) return { kind: "delimited", open, close };
|
|
2058
|
-
}
|
|
2059
|
-
}
|
|
2239
|
+
const delim = parseDelimited(source);
|
|
2240
|
+
if (delim) return delim;
|
|
2060
2241
|
const parts = parseSeqParts(source);
|
|
2061
2242
|
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
2062
2243
|
return null;
|
|
@@ -2511,6 +2692,7 @@ function looseMkBuildRe(type) {
|
|
|
2511
2692
|
);
|
|
2512
2693
|
}
|
|
2513
2694
|
function analyzeMkInlineBuild(def) {
|
|
2695
|
+
if (!def.build) return null;
|
|
2514
2696
|
const src = (def.buildSrc ?? def.build.toString()).trim();
|
|
2515
2697
|
const strict = src.match(MK_BUILD_RE);
|
|
2516
2698
|
if (strict) {
|
|
@@ -2608,6 +2790,15 @@ function v(ctx, prefix = "_v") {
|
|
|
2608
2790
|
function ind(ctx) {
|
|
2609
2791
|
return " ".repeat(ctx.indent);
|
|
2610
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
|
+
}
|
|
2611
2802
|
function reindentStmts(stmts, targetLevels) {
|
|
2612
2803
|
const nonEmpty = stmts.filter((s) => s.trim().length > 0);
|
|
2613
2804
|
if (nonEmpty.length === 0) return stmts;
|
|
@@ -2624,7 +2815,7 @@ function failReturnArr(expectedArr, posExpr) {
|
|
|
2624
2815
|
function hoistExpected(ctx, constArrSource) {
|
|
2625
2816
|
let name = ctx.expectedMap.get(constArrSource);
|
|
2626
2817
|
if (name === void 0) {
|
|
2627
|
-
name =
|
|
2818
|
+
name = `${nsp(ctx)}_fx${ctx.expectedDecls.length}`;
|
|
2628
2819
|
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
2629
2820
|
ctx.expectedMap.set(constArrSource, name);
|
|
2630
2821
|
}
|
|
@@ -2723,13 +2914,13 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
|
|
|
2723
2914
|
`${i}}`
|
|
2724
2915
|
];
|
|
2725
2916
|
}
|
|
2726
|
-
function ensureRegexDecl(ctx,
|
|
2917
|
+
function ensureRegexDecl(ctx, source, flags) {
|
|
2727
2918
|
const f = "y" + flags.replace(/[gy]/g, "");
|
|
2728
|
-
const key = `${
|
|
2919
|
+
const key = `${source}/${f}`;
|
|
2729
2920
|
let rName = ctx.regexMap.get(key);
|
|
2730
2921
|
if (rName === void 0) {
|
|
2731
|
-
rName =
|
|
2732
|
-
ctx.regexDecls.push(`const ${rName} = /${
|
|
2922
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2923
|
+
ctx.regexDecls.push(`const ${rName} = /${source}/${f}`);
|
|
2733
2924
|
ctx.regexMap.set(key, rName);
|
|
2734
2925
|
}
|
|
2735
2926
|
return rName;
|
|
@@ -2738,7 +2929,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2738
2929
|
const trivia2 = ctx.activeTrivia;
|
|
2739
2930
|
const existing = ctx.triviaFnNames.get(trivia2);
|
|
2740
2931
|
if (existing) return existing;
|
|
2741
|
-
const fnName =
|
|
2932
|
+
const fnName = `${nsp(ctx)}_tf${ctx.triviaFnNames.size}`;
|
|
2742
2933
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
2743
2934
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
2744
2935
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
@@ -2760,7 +2951,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2760
2951
|
for (const arm of regexSpec.arms) {
|
|
2761
2952
|
const def = arm.parser._def;
|
|
2762
2953
|
if (def.tag !== "regex") break;
|
|
2763
|
-
reNames.push(ensureRegexDecl(ctx, def.
|
|
2954
|
+
reNames.push(ensureRegexDecl(ctx, def.source, def.flags));
|
|
2764
2955
|
}
|
|
2765
2956
|
if (reNames.length === regexSpec.arms.length) {
|
|
2766
2957
|
ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
|
|
@@ -3085,7 +3276,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
3085
3276
|
const key = `${source}/${flags}`;
|
|
3086
3277
|
let rName = ctx.regexMap.get(key);
|
|
3087
3278
|
if (rName === void 0) {
|
|
3088
|
-
rName =
|
|
3279
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3089
3280
|
ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
|
|
3090
3281
|
ctx.regexMap.set(key, rName);
|
|
3091
3282
|
}
|
|
@@ -3119,11 +3310,11 @@ function emitRegex(def, ctx, pos) {
|
|
|
3119
3310
|
}
|
|
3120
3311
|
}
|
|
3121
3312
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3122
|
-
const key = `${def.
|
|
3313
|
+
const key = `${def.source}/${flags}`;
|
|
3123
3314
|
let rName = ctx.regexMap.get(key);
|
|
3124
3315
|
if (rName === void 0) {
|
|
3125
|
-
rName =
|
|
3126
|
-
ctx.regexDecls.push(`const ${rName} = /${def.
|
|
3316
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3317
|
+
ctx.regexDecls.push(`const ${rName} = /${def.source}/${flags}`);
|
|
3127
3318
|
ctx.regexMap.set(key, rName);
|
|
3128
3319
|
}
|
|
3129
3320
|
const mv = v(ctx, "_m");
|
|
@@ -3300,7 +3491,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
3300
3491
|
const reKey = `${regexDef.source}/${cleanFlags}`;
|
|
3301
3492
|
let reVar = ctx.regexMap.get(reKey);
|
|
3302
3493
|
if (reVar === void 0) {
|
|
3303
|
-
reVar =
|
|
3494
|
+
reVar = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3304
3495
|
ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
|
|
3305
3496
|
ctx.regexMap.set(reKey, reVar);
|
|
3306
3497
|
}
|
|
@@ -3393,7 +3584,7 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3393
3584
|
if (gate) {
|
|
3394
3585
|
const gateIdx = ctx.mapFns.length;
|
|
3395
3586
|
ctx.mapFns.push(gate);
|
|
3396
|
-
gateCond =
|
|
3587
|
+
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3397
3588
|
}
|
|
3398
3589
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
3399
3590
|
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
@@ -3460,7 +3651,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
3460
3651
|
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3461
3652
|
const vv = v(ctx);
|
|
3462
3653
|
return {
|
|
3463
|
-
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} =
|
|
3654
|
+
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
3464
3655
|
valueVar: vv,
|
|
3465
3656
|
endVar: endV
|
|
3466
3657
|
};
|
|
@@ -3688,16 +3879,17 @@ function emitNot(def, ctx, pos) {
|
|
|
3688
3879
|
};
|
|
3689
3880
|
}
|
|
3690
3881
|
function emitNode(def, ctx, pos) {
|
|
3691
|
-
const
|
|
3882
|
+
const structural = def.build === void 0;
|
|
3883
|
+
const mkType = structural ? null : analyzeMkInlineBuild(def);
|
|
3692
3884
|
let buildIdx = null;
|
|
3693
|
-
if (!mkType) {
|
|
3885
|
+
if (!mkType && !structural) {
|
|
3694
3886
|
buildIdx = ctx.buildFns.length;
|
|
3695
3887
|
ctx.buildFns.push(def.build);
|
|
3696
3888
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3697
3889
|
}
|
|
3698
3890
|
const i = ind(ctx);
|
|
3699
|
-
const capturesTrivia = mkType !== null || buildReadsTrivia(def);
|
|
3700
|
-
const clonesState = buildReadsState(def);
|
|
3891
|
+
const capturesTrivia = mkType !== null || structural || buildReadsTrivia(def);
|
|
3892
|
+
const clonesState = structural || buildReadsState(def);
|
|
3701
3893
|
const chV = v(ctx, "_ch");
|
|
3702
3894
|
const rawV = v(ctx, "_raw");
|
|
3703
3895
|
const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
@@ -3720,7 +3912,8 @@ function emitNode(def, ctx, pos) {
|
|
|
3720
3912
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3721
3913
|
}
|
|
3722
3914
|
const ndV = v(ctx, "_nd");
|
|
3723
|
-
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;
|
|
3724
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;
|
|
3725
3918
|
stmts.push(
|
|
3726
3919
|
`${i}const ${ndV} = ${finalExpr}`,
|
|
@@ -3745,8 +3938,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
3745
3938
|
return { stmts, valueVar: vv, endVar: ev };
|
|
3746
3939
|
}
|
|
3747
3940
|
function emitLazy(p, def, ctx, pos) {
|
|
3941
|
+
const canonical = ctx.ruleNames?.get(p);
|
|
3748
3942
|
const usage = ctx.lazyUsage;
|
|
3749
|
-
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)) {
|
|
3750
3944
|
let resolved;
|
|
3751
3945
|
try {
|
|
3752
3946
|
resolved = def.thunk();
|
|
@@ -3756,7 +3950,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
3756
3950
|
return emit(resolved, ctx, pos);
|
|
3757
3951
|
}
|
|
3758
3952
|
if (!ctx.namedParsers.has(p)) {
|
|
3759
|
-
const fnName2 =
|
|
3953
|
+
const fnName2 = canonical ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
3760
3954
|
ctx.namedParsers.set(p, fnName2);
|
|
3761
3955
|
let resolved;
|
|
3762
3956
|
try {
|
|
@@ -3874,7 +4068,7 @@ function emit(p, ctx, pos) {
|
|
|
3874
4068
|
return {
|
|
3875
4069
|
stmts: [
|
|
3876
4070
|
...inner.stmts,
|
|
3877
|
-
`${ind(ctx)}const ${mv} =
|
|
4071
|
+
`${ind(ctx)}const ${mv} = ${mfRef(ctx)}[${fnIdx}](${inner.valueVar}, { start: ${pos}, end: ${inner.endVar} })`
|
|
3878
4072
|
],
|
|
3879
4073
|
valueVar: mv,
|
|
3880
4074
|
endVar: inner.endVar
|
|
@@ -3937,7 +4131,7 @@ function emit(p, ctx, pos) {
|
|
|
3937
4131
|
const vv = v(ctx);
|
|
3938
4132
|
return {
|
|
3939
4133
|
stmts: [
|
|
3940
|
-
...emitIfFail(ctx,
|
|
4134
|
+
...emitIfFail(ctx, `!${mfRef(ctx)}[${fnIdx}](_ctx.state)`, failBody(ctx, '"gate"', pos)),
|
|
3941
4135
|
`${ind(ctx)}const ${vv} = null`
|
|
3942
4136
|
],
|
|
3943
4137
|
valueVar: vv,
|
|
@@ -3970,7 +4164,7 @@ function emit(p, ctx, pos) {
|
|
|
3970
4164
|
const ev = v(ctx, "_wce");
|
|
3971
4165
|
return {
|
|
3972
4166
|
stmts: [
|
|
3973
|
-
`${ind(ctx)}const ${rv} = { ..._ctx, state:
|
|
4167
|
+
`${ind(ctx)}const ${rv} = { ..._ctx, state: ${mfRef(ctx)}[${evIdx}]() }`,
|
|
3974
4168
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
3975
4169
|
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3976
4170
|
// and propagate the inner failure verbatim (interpreter parity).
|
|
@@ -4101,6 +4295,39 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4101
4295
|
for (const root of roots) walk2(root);
|
|
4102
4296
|
return { counts, recursive };
|
|
4103
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
|
+
}
|
|
4104
4331
|
function compile(combinator, mapFnSources) {
|
|
4105
4332
|
const ctx = {
|
|
4106
4333
|
vars: 0,
|
|
@@ -4184,6 +4411,150 @@ function compile(combinator, mapFnSources) {
|
|
|
4184
4411
|
}
|
|
4185
4412
|
};
|
|
4186
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
|
+
}
|
|
4187
4558
|
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
4188
4559
|
const bodyLines = [
|
|
4189
4560
|
` let pos = _pos`,
|
|
@@ -4222,6 +4593,87 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4222
4593
|
].join("\n");
|
|
4223
4594
|
}
|
|
4224
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
|
+
|
|
4225
4677
|
// src/combinators/guard.ts
|
|
4226
4678
|
function guard(predicate) {
|
|
4227
4679
|
const meta = {
|
|
@@ -4419,6 +4871,7 @@ function manyRecover(item, until) {
|
|
|
4419
4871
|
}
|
|
4420
4872
|
|
|
4421
4873
|
// src/functional/doc.ts
|
|
4874
|
+
var REENTRY_MAX_SPAN_FRACTION = 0.5;
|
|
4422
4875
|
function isNode(x) {
|
|
4423
4876
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
4424
4877
|
}
|
|
@@ -4479,6 +4932,47 @@ function graftAndShift(root, path, newNode, delta) {
|
|
|
4479
4932
|
children: newChildren
|
|
4480
4933
|
};
|
|
4481
4934
|
}
|
|
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
|
+
}
|
|
4482
4976
|
var ParseDocImpl = class _ParseDocImpl {
|
|
4483
4977
|
_registry;
|
|
4484
4978
|
_rootRule;
|
|
@@ -4510,20 +5004,24 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
4510
5004
|
}
|
|
4511
5005
|
const rebuild = this._opts.rebuild ?? defaultRebuild;
|
|
4512
5006
|
for (const { node: node2, path } of candidates) {
|
|
5007
|
+
if (node2.span.end - node2.span.start > this.input.length * REENTRY_MAX_SPAN_FRACTION) break;
|
|
4513
5008
|
const ruleFn = this._registry[node2.type];
|
|
4514
5009
|
if (!ruleFn) continue;
|
|
4515
|
-
|
|
5010
|
+
if (!(node2.span.start <= from && to <= node2.span.end)) continue;
|
|
5011
|
+
const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
|
|
4516
5012
|
const r = ruleFn(newInput, node2.span.start, ctx);
|
|
4517
5013
|
if (!r.ok) continue;
|
|
4518
|
-
if (r.span.end
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
|
|
4522
|
-
}
|
|
4523
|
-
if (this._opts.rebuild) return reparse();
|
|
4524
|
-
const newTree = graftAndShift(this.tree, path, r.value, delta);
|
|
4525
|
-
return new _ParseDocImpl(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;
|
|
4526
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);
|
|
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);
|
|
4527
5025
|
}
|
|
4528
5026
|
return reparse();
|
|
4529
5027
|
}
|
|
@@ -4531,7 +5029,7 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
4531
5029
|
function parseDoc(registry, rootRule, input, opts = {}) {
|
|
4532
5030
|
const ruleFn = registry[rootRule];
|
|
4533
5031
|
if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
|
|
4534
|
-
const ctx = { trackLines: false, state: opts.state };
|
|
5032
|
+
const ctx = { trackLines: false, state: opts.state, build: opts.build };
|
|
4535
5033
|
const r = ruleFn(input, 0, ctx);
|
|
4536
5034
|
if (r.ok) {
|
|
4537
5035
|
return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
|
|
@@ -4539,12 +5037,45 @@ function parseDoc(registry, rootRule, input, opts = {}) {
|
|
|
4539
5037
|
return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
4540
5038
|
}
|
|
4541
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;
|
|
5061
|
+
}
|
|
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
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
|
|
4542
5073
|
// src/cst/trivia-index.ts
|
|
4543
|
-
function merge(map, key,
|
|
4544
|
-
if (
|
|
5074
|
+
function merge(map, key, run2) {
|
|
5075
|
+
if (run2.length === 0) return;
|
|
4545
5076
|
const existing = map.get(key);
|
|
4546
|
-
if (existing) existing.push(...
|
|
4547
|
-
else map.set(key, [...
|
|
5077
|
+
if (existing) existing.push(...run2);
|
|
5078
|
+
else map.set(key, [...run2]);
|
|
4548
5079
|
}
|
|
4549
5080
|
function buildTriviaIndex(root, input = "", opts) {
|
|
4550
5081
|
const before = /* @__PURE__ */ new Map();
|
|
@@ -4556,11 +5087,11 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
4556
5087
|
for (let i = 0; i < log.length; i += 3) {
|
|
4557
5088
|
const tStart = log[i], tEnd = log[i + 1], insertIdx = log[i + 2];
|
|
4558
5089
|
if (tEnd <= tStart) continue;
|
|
4559
|
-
const
|
|
5090
|
+
const run2 = [{ value: input.slice(tStart, tEnd), span: { start: tStart, end: tEnd } }];
|
|
4560
5091
|
const next = raw[insertIdx];
|
|
4561
|
-
if (next) merge(before, next.span.start,
|
|
5092
|
+
if (next) merge(before, next.span.start, run2);
|
|
4562
5093
|
const prev = insertIdx > 0 ? raw[insertIdx - 1] : void 0;
|
|
4563
|
-
if (prev) merge(after, prev.span.end,
|
|
5094
|
+
if (prev) merge(after, prev.span.end, run2);
|
|
4564
5095
|
}
|
|
4565
5096
|
}
|
|
4566
5097
|
const ch = node2.children;
|
|
@@ -4577,17 +5108,17 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
4577
5108
|
const rootSpan = root?.span;
|
|
4578
5109
|
const re = new RegExp(opts.trivia.source, opts.trivia.flags.replace(/[gy]/g, "") + "y");
|
|
4579
5110
|
const scanFrom = (from) => {
|
|
4580
|
-
const
|
|
5111
|
+
const run2 = [];
|
|
4581
5112
|
let pos = from;
|
|
4582
5113
|
re.lastIndex = pos;
|
|
4583
5114
|
let m = re.exec(input);
|
|
4584
5115
|
while (m && m.index === pos && m[0].length > 0) {
|
|
4585
|
-
|
|
5116
|
+
run2.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
|
|
4586
5117
|
pos += m[0].length;
|
|
4587
5118
|
re.lastIndex = pos;
|
|
4588
5119
|
m = re.exec(input);
|
|
4589
5120
|
}
|
|
4590
|
-
return
|
|
5121
|
+
return run2;
|
|
4591
5122
|
};
|
|
4592
5123
|
if (rootSpan && rootSpan.start > 0) {
|
|
4593
5124
|
const lead = scanFrom(0);
|
|
@@ -4673,4 +5204,263 @@ function triviaEntries(log, labels, opts) {
|
|
|
4673
5204
|
}
|
|
4674
5205
|
};
|
|
4675
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);
|
|
4676
5466
|
//# sourceMappingURL=index.cjs.map
|