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.js
CHANGED
|
@@ -251,6 +251,36 @@ function literal(value, opts = {}) {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
// src/combinators/regex.ts
|
|
254
|
+
var firstSetAnalyzer = null;
|
|
255
|
+
function registerRegexAnalyzer(analyzer) {
|
|
256
|
+
firstSetAnalyzer = analyzer;
|
|
257
|
+
}
|
|
258
|
+
var permissiveFirstSet = () => ({ firstSet: any(), canMatchNewline: true });
|
|
259
|
+
function regex(pattern, flags = "") {
|
|
260
|
+
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
261
|
+
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
262
|
+
const anchored = new RegExp(source, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
263
|
+
const { firstSet, canMatchNewline } = (firstSetAnalyzer ?? permissiveFirstSet)(source);
|
|
264
|
+
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
265
|
+
return {
|
|
266
|
+
_tag: "regex",
|
|
267
|
+
_meta: meta,
|
|
268
|
+
_def: { tag: "regex", source, flags: resolvedFlags },
|
|
269
|
+
parse(input, pos, ctx) {
|
|
270
|
+
anchored.lastIndex = pos;
|
|
271
|
+
const m = anchored.exec(input);
|
|
272
|
+
if (m === null) {
|
|
273
|
+
return failAt(ctx, [`/${source}/`], pos);
|
|
274
|
+
}
|
|
275
|
+
const span = { start: pos, end: pos + m[0].length };
|
|
276
|
+
const leaf = { _tag: "leaf", value: m[0], span };
|
|
277
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
278
|
+
return { ok: true, value: m[0], span };
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/combinators/regex-analyze.ts
|
|
254
284
|
import regexpTree from "regexp-tree";
|
|
255
285
|
function firstSetFromRegex(pattern) {
|
|
256
286
|
try {
|
|
@@ -355,40 +385,6 @@ function extractFirstSet(node2) {
|
|
|
355
385
|
return { firstSet: any(), canMatchNewline: true };
|
|
356
386
|
}
|
|
357
387
|
}
|
|
358
|
-
function optimizeRegex(source, flags) {
|
|
359
|
-
try {
|
|
360
|
-
const result = regexpTree.optimize(`/${source}/${flags}`);
|
|
361
|
-
const str = result.toString();
|
|
362
|
-
const lastSlash = str.lastIndexOf("/");
|
|
363
|
-
return str.slice(1, lastSlash);
|
|
364
|
-
} catch {
|
|
365
|
-
return source;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
function regex(pattern, flags = "") {
|
|
369
|
-
const source = typeof pattern === "string" ? pattern : pattern.source;
|
|
370
|
-
const resolvedFlags = typeof pattern === "string" ? flags : pattern.flags;
|
|
371
|
-
const optimizedSource = optimizeRegex(source, resolvedFlags);
|
|
372
|
-
const anchored = new RegExp(optimizedSource, "y" + resolvedFlags.replace(/[gy]/g, ""));
|
|
373
|
-
const { firstSet, canMatchNewline } = firstSetFromRegex(source);
|
|
374
|
-
const meta = { firstSet, canMatchNewline, isTrivia: false };
|
|
375
|
-
return {
|
|
376
|
-
_tag: "regex",
|
|
377
|
-
_meta: meta,
|
|
378
|
-
_def: { tag: "regex", source, flags: resolvedFlags, optimizedSource },
|
|
379
|
-
parse(input, pos, ctx) {
|
|
380
|
-
anchored.lastIndex = pos;
|
|
381
|
-
const m = anchored.exec(input);
|
|
382
|
-
if (m === null) {
|
|
383
|
-
return failAt(ctx, [`/${source}/`], pos);
|
|
384
|
-
}
|
|
385
|
-
const span = { start: pos, end: pos + m[0].length };
|
|
386
|
-
const leaf = { _tag: "leaf", value: m[0], span };
|
|
387
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
388
|
-
return { ok: true, value: m[0], span };
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
388
|
|
|
393
389
|
// src/combinators/keywords.ts
|
|
394
390
|
function word(str, boundary = "_0-9A-Za-z") {
|
|
@@ -1086,7 +1082,9 @@ function rules(factory) {
|
|
|
1086
1082
|
if (typeof key !== "string") return void 0;
|
|
1087
1083
|
const record = target;
|
|
1088
1084
|
if (!(key in record)) {
|
|
1089
|
-
|
|
1085
|
+
const r = ref();
|
|
1086
|
+
r._ruleName = key;
|
|
1087
|
+
record[key] = r;
|
|
1090
1088
|
}
|
|
1091
1089
|
return record[key];
|
|
1092
1090
|
}
|
|
@@ -1151,12 +1149,14 @@ function confirmedBuildArity(src) {
|
|
|
1151
1149
|
return parts.length;
|
|
1152
1150
|
}
|
|
1153
1151
|
function buildReadsTrivia(def) {
|
|
1152
|
+
if (!def.build) return true;
|
|
1154
1153
|
const src = def.buildSrc ?? def.build.toString();
|
|
1155
1154
|
const arity = confirmedBuildArity(src);
|
|
1156
1155
|
if (arity === null) return true;
|
|
1157
1156
|
return arity >= 4;
|
|
1158
1157
|
}
|
|
1159
1158
|
function buildReadsState(def) {
|
|
1159
|
+
if (!def.build) return true;
|
|
1160
1160
|
const src = def.buildSrc ?? def.build.toString();
|
|
1161
1161
|
const arity = confirmedBuildArity(src);
|
|
1162
1162
|
if (arity === null) return true;
|
|
@@ -1175,8 +1175,8 @@ function node(type, combinator, build, opts) {
|
|
|
1175
1175
|
};
|
|
1176
1176
|
const collapse = opts?.collapse === true;
|
|
1177
1177
|
const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
|
|
1178
|
-
const capturesTrivia = buildReadsTrivia(def);
|
|
1179
|
-
const clonesState = buildReadsState(def);
|
|
1178
|
+
const capturesTrivia = build ? buildReadsTrivia(def) : true;
|
|
1179
|
+
const clonesState = build ? buildReadsState(def) : true;
|
|
1180
1180
|
return {
|
|
1181
1181
|
_tag: "node",
|
|
1182
1182
|
_meta: meta,
|
|
@@ -1187,13 +1187,8 @@ function node(type, combinator, build, opts) {
|
|
|
1187
1187
|
const r = combinator.parse(input, pos, ctx);
|
|
1188
1188
|
const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
1189
1189
|
if (!r.ok) return r;
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
rawChildren,
|
|
1193
|
-
r.span,
|
|
1194
|
-
triviaLog,
|
|
1195
|
-
clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
|
|
1196
|
-
);
|
|
1190
|
+
const st = clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
1191
|
+
const built = collapse && children.length === 1 ? collapseChild(children[0]) : build ? build(children, rawChildren, r.span, triviaLog, st) : ctx.build ? ctx.build(type, children, rawChildren, r.span, triviaLog, st) : { _tag: "node", type, span: { start: r.span.start, end: r.span.end }, state: st ?? null, children };
|
|
1197
1192
|
const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
|
|
1198
1193
|
const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
|
|
1199
1194
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
@@ -1368,8 +1363,14 @@ function staticExpected(c) {
|
|
|
1368
1363
|
case "transform":
|
|
1369
1364
|
case "not":
|
|
1370
1365
|
return staticExpected(def.parser);
|
|
1371
|
-
case "lazy":
|
|
1372
|
-
|
|
1366
|
+
case "lazy": {
|
|
1367
|
+
const name = c._ruleName;
|
|
1368
|
+
try {
|
|
1369
|
+
return staticExpected(def.thunk());
|
|
1370
|
+
} catch {
|
|
1371
|
+
return name ? [name] : [];
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1373
1374
|
default:
|
|
1374
1375
|
return [];
|
|
1375
1376
|
}
|
|
@@ -1669,6 +1670,15 @@ function seqIsUnambiguous(parts) {
|
|
|
1669
1670
|
const p = parts[k];
|
|
1670
1671
|
const mandatory = p.part === "lit" ? !p.optional : p.min === 1;
|
|
1671
1672
|
if (mandatory) hasMandatory = true;
|
|
1673
|
+
if (p.part === "group") {
|
|
1674
|
+
const exposure = groupPartExposure(p);
|
|
1675
|
+
if (exposure === "unsupported") {
|
|
1676
|
+
const trailingOnce = k === parts.length - 1 && p.min === 1 && !p.unbounded;
|
|
1677
|
+
if (!trailingOnce) return false;
|
|
1678
|
+
} else if (exposure !== null) {
|
|
1679
|
+
if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1672
1682
|
const skippableSingle = p.part === "lit" && p.optional || p.part !== "lit" && !p.unbounded && p.min === 0;
|
|
1673
1683
|
const isUnboundedRepeat = p.part !== "lit" && p.unbounded;
|
|
1674
1684
|
if (skippableSingle || isUnboundedRepeat) {
|
|
@@ -1715,6 +1725,58 @@ function firstSetsDisjoint(a, b) {
|
|
|
1715
1725
|
if (a === "any" || b === "any") return false;
|
|
1716
1726
|
return classDisjoint(a, b);
|
|
1717
1727
|
}
|
|
1728
|
+
function fixedClassSeq(shape) {
|
|
1729
|
+
if (shape.kind !== "seq") return null;
|
|
1730
|
+
const out = [];
|
|
1731
|
+
for (const p of shape.parts) {
|
|
1732
|
+
if (p.part === "lit") {
|
|
1733
|
+
if (p.optional) return null;
|
|
1734
|
+
for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
|
|
1735
|
+
} else if (p.part === "run" && !p.unbounded && p.min === 1) {
|
|
1736
|
+
out.push({ ranges: p.ranges, negated: p.negated });
|
|
1737
|
+
} else {
|
|
1738
|
+
return null;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
return out.length ? out : null;
|
|
1742
|
+
}
|
|
1743
|
+
function altFixedMutuallyExclusive(shape) {
|
|
1744
|
+
if (shape.kind !== "alt" || shape.disjoint) return false;
|
|
1745
|
+
const lists = [];
|
|
1746
|
+
for (const arm of shape.arms) {
|
|
1747
|
+
const f = fixedClassSeq(arm);
|
|
1748
|
+
if (!f) return false;
|
|
1749
|
+
lists.push(f);
|
|
1750
|
+
}
|
|
1751
|
+
for (let i = 0; i < lists.length; i++) {
|
|
1752
|
+
for (let j = 0; j < lists.length; j++) {
|
|
1753
|
+
if (i === j) continue;
|
|
1754
|
+
const a = lists[i];
|
|
1755
|
+
const b = lists[j];
|
|
1756
|
+
if (a.length > b.length) continue;
|
|
1757
|
+
let disjointAt = false;
|
|
1758
|
+
for (let p = 0; p < a.length; p++) {
|
|
1759
|
+
if (classDisjoint(a[p], b[p])) {
|
|
1760
|
+
disjointAt = true;
|
|
1761
|
+
break;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
if (!disjointAt) return false;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return true;
|
|
1768
|
+
}
|
|
1769
|
+
function groupPartExposure(part) {
|
|
1770
|
+
const innerWiggle = trailingBacktrackClass(part.inner);
|
|
1771
|
+
if (innerWiggle === "unsupported") return "unsupported";
|
|
1772
|
+
const requiredOnce = !part.unbounded && part.min === 1;
|
|
1773
|
+
if (requiredOnce) return innerWiggle;
|
|
1774
|
+
const dropExposed = shapeFirstAccept(part.inner);
|
|
1775
|
+
if (dropExposed === "any") return "unsupported";
|
|
1776
|
+
if (innerWiggle === null) return dropExposed;
|
|
1777
|
+
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1778
|
+
return unioned === "any" ? "unsupported" : unioned;
|
|
1779
|
+
}
|
|
1718
1780
|
function trailingBacktrackClass(shape) {
|
|
1719
1781
|
if (shape.kind === "chars") return { ranges: shape.ranges, negated: false };
|
|
1720
1782
|
if (shape.kind === "ident") return { ranges: shape.tail, negated: false };
|
|
@@ -1726,18 +1788,10 @@ function trailingBacktrackClass(shape) {
|
|
|
1726
1788
|
if (last.unbounded || last.min === 0) return { ranges: last.ranges, negated: last.negated };
|
|
1727
1789
|
return null;
|
|
1728
1790
|
}
|
|
1729
|
-
|
|
1730
|
-
if (innerWiggle === "unsupported") return "unsupported";
|
|
1731
|
-
const requiredOnce = !last.unbounded && last.min === 1;
|
|
1732
|
-
if (requiredOnce) return innerWiggle;
|
|
1733
|
-
const dropExposed = shapeFirstAccept(last.inner);
|
|
1734
|
-
if (dropExposed === "any") return "unsupported";
|
|
1735
|
-
if (innerWiggle === null) return dropExposed;
|
|
1736
|
-
const unioned = unionCharSets([innerWiggle, dropExposed]);
|
|
1737
|
-
return unioned === "any" ? "unsupported" : unioned;
|
|
1791
|
+
return groupPartExposure(last);
|
|
1738
1792
|
}
|
|
1739
1793
|
if (shape.kind === "alt") {
|
|
1740
|
-
if (!shape.disjoint) return "unsupported";
|
|
1794
|
+
if (!shape.disjoint) return altFixedMutuallyExclusive(shape) ? null : "unsupported";
|
|
1741
1795
|
const classes = shape.arms.map(trailingBacktrackClass);
|
|
1742
1796
|
if (classes.some((c) => c === "unsupported")) return "unsupported";
|
|
1743
1797
|
const concrete = classes.filter((c) => c !== null);
|
|
@@ -1877,7 +1931,7 @@ function seqFirstAccept(parts) {
|
|
|
1877
1931
|
function groupInnerSafe(shape) {
|
|
1878
1932
|
if (shape.kind === "chars" || shape.kind === "ident" || shape.kind === "seq" || shape.kind === "litFold") return true;
|
|
1879
1933
|
if (shape.kind === "lookahead") return groupInnerSafe(shape.inner);
|
|
1880
|
-
if (shape.kind === "alt") return
|
|
1934
|
+
if (shape.kind === "alt") return true;
|
|
1881
1935
|
return false;
|
|
1882
1936
|
}
|
|
1883
1937
|
function shapeFirstAccept(shape) {
|
|
@@ -1905,9 +1959,8 @@ function shapeFirstAccept(shape) {
|
|
|
1905
1959
|
return shapeFirstAccept(shape.inner);
|
|
1906
1960
|
// zero-width — doesn't change first-char acceptance
|
|
1907
1961
|
case "alt": {
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
return sets.length === shape.arms.length ? unionCharSets(sets) : "any";
|
|
1962
|
+
const sets = shape.firsts.map((f) => f === null ? "any" : f);
|
|
1963
|
+
return unionCharSets(sets);
|
|
1911
1964
|
}
|
|
1912
1965
|
}
|
|
1913
1966
|
}
|
|
@@ -1948,6 +2001,120 @@ function parseScanShape(source) {
|
|
|
1948
2001
|
}
|
|
1949
2002
|
return parseScanShapeCore(source);
|
|
1950
2003
|
}
|
|
2004
|
+
function splitDelimArms(body) {
|
|
2005
|
+
const arms = [];
|
|
2006
|
+
let depth = 0;
|
|
2007
|
+
let last = 0;
|
|
2008
|
+
let i = 0;
|
|
2009
|
+
while (i < body.length) {
|
|
2010
|
+
const ch = body[i];
|
|
2011
|
+
if (ch === "\\") {
|
|
2012
|
+
i += 2;
|
|
2013
|
+
continue;
|
|
2014
|
+
}
|
|
2015
|
+
if (ch === "[") {
|
|
2016
|
+
let j = i + 1;
|
|
2017
|
+
if (body[j] === "^") j++;
|
|
2018
|
+
while (j < body.length && body[j] !== "]") {
|
|
2019
|
+
if (body[j] === "\\") j += 2;
|
|
2020
|
+
else j++;
|
|
2021
|
+
}
|
|
2022
|
+
if (body[j] !== "]") return null;
|
|
2023
|
+
i = j + 1;
|
|
2024
|
+
continue;
|
|
2025
|
+
}
|
|
2026
|
+
if (ch === "(") {
|
|
2027
|
+
depth++;
|
|
2028
|
+
i++;
|
|
2029
|
+
continue;
|
|
2030
|
+
}
|
|
2031
|
+
if (ch === ")") {
|
|
2032
|
+
depth--;
|
|
2033
|
+
if (depth < 0) return null;
|
|
2034
|
+
i++;
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
if (ch === "|" && depth === 0) {
|
|
2038
|
+
arms.push(body.slice(last, i));
|
|
2039
|
+
last = i + 1;
|
|
2040
|
+
}
|
|
2041
|
+
i++;
|
|
2042
|
+
}
|
|
2043
|
+
if (depth !== 0) return null;
|
|
2044
|
+
arms.push(body.slice(last));
|
|
2045
|
+
return arms;
|
|
2046
|
+
}
|
|
2047
|
+
function delimitedBodySound(body, close) {
|
|
2048
|
+
const l0 = close[0];
|
|
2049
|
+
if (close.length > 2) return false;
|
|
2050
|
+
const arms = splitDelimArms(body);
|
|
2051
|
+
if (!arms) return false;
|
|
2052
|
+
let bulkSeen = false;
|
|
2053
|
+
let guardSeen = false;
|
|
2054
|
+
for (const arm of arms) {
|
|
2055
|
+
const bulk = /^\[\^((?:\\.|[^\]])+)\]$/.exec(arm);
|
|
2056
|
+
if (bulk) {
|
|
2057
|
+
if (bulkSeen) return false;
|
|
2058
|
+
const ranges = parseClassRanges(bulk[1]);
|
|
2059
|
+
if (!ranges || ranges.length !== 1 || ranges[0][0] !== l0 || ranges[0][1] !== l0) return false;
|
|
2060
|
+
bulkSeen = true;
|
|
2061
|
+
continue;
|
|
2062
|
+
}
|
|
2063
|
+
if (close.length === 2 && arm.endsWith(")")) {
|
|
2064
|
+
const idx = arm.indexOf("(?!");
|
|
2065
|
+
if (idx === -1) return false;
|
|
2066
|
+
const prefix = literalCodePoints(arm.slice(0, idx));
|
|
2067
|
+
const op = parseClassOperand(arm.slice(idx + 3, -1));
|
|
2068
|
+
const prefixIsL0 = prefix?.length === 1 && prefix[0] === l0;
|
|
2069
|
+
const forbidsL1 = op && !op.negated && op.ranges.length === 1 && op.ranges[0][0] === close[1] && op.ranges[0][1] === close[1];
|
|
2070
|
+
if (!prefixIsL0 || !forbidsL1 || guardSeen) return false;
|
|
2071
|
+
guardSeen = true;
|
|
2072
|
+
continue;
|
|
2073
|
+
}
|
|
2074
|
+
return false;
|
|
2075
|
+
}
|
|
2076
|
+
return bulkSeen && guardSeen === (close.length === 2);
|
|
2077
|
+
}
|
|
2078
|
+
function parseDelimited(source) {
|
|
2079
|
+
if (source.includes("\\\\")) return null;
|
|
2080
|
+
let i = 0;
|
|
2081
|
+
while (i < source.length && source[i] !== "(") {
|
|
2082
|
+
i += source[i] === "\\" ? 2 : 1;
|
|
2083
|
+
}
|
|
2084
|
+
if (source[i] !== "(" || source[i + 1] !== "?" || source[i + 2] !== ":") return null;
|
|
2085
|
+
const openFrag = source.slice(0, i);
|
|
2086
|
+
let k = i + 3;
|
|
2087
|
+
let depth = 1;
|
|
2088
|
+
while (k < source.length && depth > 0) {
|
|
2089
|
+
const ch = source[k];
|
|
2090
|
+
if (ch === "\\") {
|
|
2091
|
+
k += 2;
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
if (ch === "[") {
|
|
2095
|
+
let j = k + 1;
|
|
2096
|
+
if (source[j] === "^") j++;
|
|
2097
|
+
while (j < source.length && source[j] !== "]") {
|
|
2098
|
+
if (source[j] === "\\") j += 2;
|
|
2099
|
+
else j++;
|
|
2100
|
+
}
|
|
2101
|
+
if (source[j] !== "]") return null;
|
|
2102
|
+
k = j + 1;
|
|
2103
|
+
continue;
|
|
2104
|
+
}
|
|
2105
|
+
if (ch === "(") depth++;
|
|
2106
|
+
else if (ch === ")") depth--;
|
|
2107
|
+
k++;
|
|
2108
|
+
}
|
|
2109
|
+
if (depth !== 0) return null;
|
|
2110
|
+
const body = source.slice(i + 3, k - 1);
|
|
2111
|
+
if (source[k] !== "*") return null;
|
|
2112
|
+
const closeFrag = source.slice(k + 1);
|
|
2113
|
+
const open = literalCodePoints(openFrag);
|
|
2114
|
+
const close = literalCodePoints(closeFrag);
|
|
2115
|
+
if (!open || !close || !delimitedBodySound(body, close)) return null;
|
|
2116
|
+
return { kind: "delimited", open, close };
|
|
2117
|
+
}
|
|
1951
2118
|
function parseScanShapeCore(source) {
|
|
1952
2119
|
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1953
2120
|
if (m && !m[1].startsWith("^")) {
|
|
@@ -1972,14 +2139,8 @@ function parseScanShapeCore(source) {
|
|
|
1972
2139
|
const stop = parseClassRanges(m[2]);
|
|
1973
2140
|
if (open && stop) return { kind: "until", open, stop };
|
|
1974
2141
|
}
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
if (m && m[1] && m[2]) {
|
|
1978
|
-
const open = literalCodePoints(m[1]);
|
|
1979
|
-
const close = literalCodePoints(m[2]);
|
|
1980
|
-
if (open && close) return { kind: "delimited", open, close };
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
2142
|
+
const delim = parseDelimited(source);
|
|
2143
|
+
if (delim) return delim;
|
|
1983
2144
|
const parts = parseSeqParts(source);
|
|
1984
2145
|
if (parts && seqIsUnambiguous(parts)) return { kind: "seq", parts };
|
|
1985
2146
|
return null;
|
|
@@ -2434,6 +2595,7 @@ function looseMkBuildRe(type) {
|
|
|
2434
2595
|
);
|
|
2435
2596
|
}
|
|
2436
2597
|
function analyzeMkInlineBuild(def) {
|
|
2598
|
+
if (!def.build) return null;
|
|
2437
2599
|
const src = (def.buildSrc ?? def.build.toString()).trim();
|
|
2438
2600
|
const strict = src.match(MK_BUILD_RE);
|
|
2439
2601
|
if (strict) {
|
|
@@ -2531,6 +2693,15 @@ function v(ctx, prefix = "_v") {
|
|
|
2531
2693
|
function ind(ctx) {
|
|
2532
2694
|
return " ".repeat(ctx.indent);
|
|
2533
2695
|
}
|
|
2696
|
+
function nsp(ctx) {
|
|
2697
|
+
return ctx.ns ?? "";
|
|
2698
|
+
}
|
|
2699
|
+
function mfRef(ctx) {
|
|
2700
|
+
return `${nsp(ctx)}_mf`;
|
|
2701
|
+
}
|
|
2702
|
+
function buildRef(ctx) {
|
|
2703
|
+
return `${nsp(ctx)}_build`;
|
|
2704
|
+
}
|
|
2534
2705
|
function reindentStmts(stmts, targetLevels) {
|
|
2535
2706
|
const nonEmpty = stmts.filter((s) => s.trim().length > 0);
|
|
2536
2707
|
if (nonEmpty.length === 0) return stmts;
|
|
@@ -2547,7 +2718,7 @@ function failReturnArr(expectedArr, posExpr) {
|
|
|
2547
2718
|
function hoistExpected(ctx, constArrSource) {
|
|
2548
2719
|
let name = ctx.expectedMap.get(constArrSource);
|
|
2549
2720
|
if (name === void 0) {
|
|
2550
|
-
name =
|
|
2721
|
+
name = `${nsp(ctx)}_fx${ctx.expectedDecls.length}`;
|
|
2551
2722
|
ctx.expectedDecls.push(`const ${name} = ${constArrSource}`);
|
|
2552
2723
|
ctx.expectedMap.set(constArrSource, name);
|
|
2553
2724
|
}
|
|
@@ -2646,13 +2817,13 @@ function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
|
|
|
2646
2817
|
`${i}}`
|
|
2647
2818
|
];
|
|
2648
2819
|
}
|
|
2649
|
-
function ensureRegexDecl(ctx,
|
|
2820
|
+
function ensureRegexDecl(ctx, source, flags) {
|
|
2650
2821
|
const f = "y" + flags.replace(/[gy]/g, "");
|
|
2651
|
-
const key = `${
|
|
2822
|
+
const key = `${source}/${f}`;
|
|
2652
2823
|
let rName = ctx.regexMap.get(key);
|
|
2653
2824
|
if (rName === void 0) {
|
|
2654
|
-
rName =
|
|
2655
|
-
ctx.regexDecls.push(`const ${rName} = /${
|
|
2825
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
2826
|
+
ctx.regexDecls.push(`const ${rName} = /${source}/${f}`);
|
|
2656
2827
|
ctx.regexMap.set(key, rName);
|
|
2657
2828
|
}
|
|
2658
2829
|
return rName;
|
|
@@ -2661,7 +2832,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2661
2832
|
const trivia2 = ctx.activeTrivia;
|
|
2662
2833
|
const existing = ctx.triviaFnNames.get(trivia2);
|
|
2663
2834
|
if (existing) return existing;
|
|
2664
|
-
const fnName =
|
|
2835
|
+
const fnName = `${nsp(ctx)}_tf${ctx.triviaFnNames.size}`;
|
|
2665
2836
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
2666
2837
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
2667
2838
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
@@ -2683,7 +2854,7 @@ function ensureTriviaFn(ctx) {
|
|
|
2683
2854
|
for (const arm of regexSpec.arms) {
|
|
2684
2855
|
const def = arm.parser._def;
|
|
2685
2856
|
if (def.tag !== "regex") break;
|
|
2686
|
-
reNames.push(ensureRegexDecl(ctx, def.
|
|
2857
|
+
reNames.push(ensureRegexDecl(ctx, def.source, def.flags));
|
|
2687
2858
|
}
|
|
2688
2859
|
if (reNames.length === regexSpec.arms.length) {
|
|
2689
2860
|
ctx.namedFnDecls.push(buildLabeledRegexTriviaFnDecl(fnName, regexSpec, reNames));
|
|
@@ -3008,7 +3179,7 @@ function emitKeywords(def, ctx, pos) {
|
|
|
3008
3179
|
const key = `${source}/${flags}`;
|
|
3009
3180
|
let rName = ctx.regexMap.get(key);
|
|
3010
3181
|
if (rName === void 0) {
|
|
3011
|
-
rName =
|
|
3182
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3012
3183
|
ctx.regexDecls.push(`const ${rName} = /${source}/${flags}`);
|
|
3013
3184
|
ctx.regexMap.set(key, rName);
|
|
3014
3185
|
}
|
|
@@ -3042,11 +3213,11 @@ function emitRegex(def, ctx, pos) {
|
|
|
3042
3213
|
}
|
|
3043
3214
|
}
|
|
3044
3215
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
3045
|
-
const key = `${def.
|
|
3216
|
+
const key = `${def.source}/${flags}`;
|
|
3046
3217
|
let rName = ctx.regexMap.get(key);
|
|
3047
3218
|
if (rName === void 0) {
|
|
3048
|
-
rName =
|
|
3049
|
-
ctx.regexDecls.push(`const ${rName} = /${def.
|
|
3219
|
+
rName = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3220
|
+
ctx.regexDecls.push(`const ${rName} = /${def.source}/${flags}`);
|
|
3050
3221
|
ctx.regexMap.set(key, rName);
|
|
3051
3222
|
}
|
|
3052
3223
|
const mv = v(ctx, "_m");
|
|
@@ -3223,7 +3394,7 @@ function emitGreedyClassify(def, superIndex, ctx, pos) {
|
|
|
3223
3394
|
const reKey = `${regexDef.source}/${cleanFlags}`;
|
|
3224
3395
|
let reVar = ctx.regexMap.get(reKey);
|
|
3225
3396
|
if (reVar === void 0) {
|
|
3226
|
-
reVar =
|
|
3397
|
+
reVar = `${nsp(ctx)}_re${ctx.regexDecls.length}`;
|
|
3227
3398
|
ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
|
|
3228
3399
|
ctx.regexMap.set(reKey, reVar);
|
|
3229
3400
|
}
|
|
@@ -3316,7 +3487,7 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
3316
3487
|
if (gate) {
|
|
3317
3488
|
const gateIdx = ctx.mapFns.length;
|
|
3318
3489
|
ctx.mapFns.push(gate);
|
|
3319
|
-
gateCond =
|
|
3490
|
+
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
3320
3491
|
}
|
|
3321
3492
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
3322
3493
|
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
@@ -3383,7 +3554,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
3383
3554
|
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
3384
3555
|
const vv = v(ctx);
|
|
3385
3556
|
return {
|
|
3386
|
-
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} =
|
|
3557
|
+
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = ${mfRef(ctx)}[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
3387
3558
|
valueVar: vv,
|
|
3388
3559
|
endVar: endV
|
|
3389
3560
|
};
|
|
@@ -3611,16 +3782,17 @@ function emitNot(def, ctx, pos) {
|
|
|
3611
3782
|
};
|
|
3612
3783
|
}
|
|
3613
3784
|
function emitNode(def, ctx, pos) {
|
|
3614
|
-
const
|
|
3785
|
+
const structural = def.build === void 0;
|
|
3786
|
+
const mkType = structural ? null : analyzeMkInlineBuild(def);
|
|
3615
3787
|
let buildIdx = null;
|
|
3616
|
-
if (!mkType) {
|
|
3788
|
+
if (!mkType && !structural) {
|
|
3617
3789
|
buildIdx = ctx.buildFns.length;
|
|
3618
3790
|
ctx.buildFns.push(def.build);
|
|
3619
3791
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
3620
3792
|
}
|
|
3621
3793
|
const i = ind(ctx);
|
|
3622
|
-
const capturesTrivia = mkType !== null || buildReadsTrivia(def);
|
|
3623
|
-
const clonesState = buildReadsState(def);
|
|
3794
|
+
const capturesTrivia = mkType !== null || structural || buildReadsTrivia(def);
|
|
3795
|
+
const clonesState = structural || buildReadsState(def);
|
|
3624
3796
|
const chV = v(ctx, "_ch");
|
|
3625
3797
|
const rawV = v(ctx, "_raw");
|
|
3626
3798
|
const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
@@ -3643,7 +3815,8 @@ function emitNode(def, ctx, pos) {
|
|
|
3643
3815
|
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
3644
3816
|
}
|
|
3645
3817
|
const ndV = v(ctx, "_nd");
|
|
3646
|
-
const
|
|
3818
|
+
const buildExpr = structural ? `{ _tag: 'node', type: ${JSON.stringify(def.type)}, span: { start: ${pos}, end: ${endVar} }, state: ${stV} ?? null, children: ${chV} }` : mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `${buildRef(ctx)}[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
|
|
3819
|
+
const ndExpr = ctx.ns || structural ? `_ctx.build !== undefined ? _ctx.build(${JSON.stringify(def.type)}, ${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV}) : (${buildExpr})` : buildExpr;
|
|
3647
3820
|
const finalExpr = def.collapse ? `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})` : ndExpr;
|
|
3648
3821
|
stmts.push(
|
|
3649
3822
|
`${i}const ${ndV} = ${finalExpr}`,
|
|
@@ -3668,8 +3841,9 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
3668
3841
|
return { stmts, valueVar: vv, endVar: ev };
|
|
3669
3842
|
}
|
|
3670
3843
|
function emitLazy(p, def, ctx, pos) {
|
|
3844
|
+
const canonical = ctx.ruleNames?.get(p);
|
|
3671
3845
|
const usage = ctx.lazyUsage;
|
|
3672
|
-
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
3846
|
+
if (!canonical && usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
3673
3847
|
let resolved;
|
|
3674
3848
|
try {
|
|
3675
3849
|
resolved = def.thunk();
|
|
@@ -3679,7 +3853,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
3679
3853
|
return emit(resolved, ctx, pos);
|
|
3680
3854
|
}
|
|
3681
3855
|
if (!ctx.namedParsers.has(p)) {
|
|
3682
|
-
const fnName2 =
|
|
3856
|
+
const fnName2 = canonical ?? `${nsp(ctx)}_pf${ctx.namedParsers.size}`;
|
|
3683
3857
|
ctx.namedParsers.set(p, fnName2);
|
|
3684
3858
|
let resolved;
|
|
3685
3859
|
try {
|
|
@@ -3797,7 +3971,7 @@ function emit(p, ctx, pos) {
|
|
|
3797
3971
|
return {
|
|
3798
3972
|
stmts: [
|
|
3799
3973
|
...inner.stmts,
|
|
3800
|
-
`${ind(ctx)}const ${mv} =
|
|
3974
|
+
`${ind(ctx)}const ${mv} = ${mfRef(ctx)}[${fnIdx}](${inner.valueVar}, { start: ${pos}, end: ${inner.endVar} })`
|
|
3801
3975
|
],
|
|
3802
3976
|
valueVar: mv,
|
|
3803
3977
|
endVar: inner.endVar
|
|
@@ -3860,7 +4034,7 @@ function emit(p, ctx, pos) {
|
|
|
3860
4034
|
const vv = v(ctx);
|
|
3861
4035
|
return {
|
|
3862
4036
|
stmts: [
|
|
3863
|
-
...emitIfFail(ctx,
|
|
4037
|
+
...emitIfFail(ctx, `!${mfRef(ctx)}[${fnIdx}](_ctx.state)`, failBody(ctx, '"gate"', pos)),
|
|
3864
4038
|
`${ind(ctx)}const ${vv} = null`
|
|
3865
4039
|
],
|
|
3866
4040
|
valueVar: vv,
|
|
@@ -3893,7 +4067,7 @@ function emit(p, ctx, pos) {
|
|
|
3893
4067
|
const ev = v(ctx, "_wce");
|
|
3894
4068
|
return {
|
|
3895
4069
|
stmts: [
|
|
3896
|
-
`${ind(ctx)}const ${rv} = { ..._ctx, state:
|
|
4070
|
+
`${ind(ctx)}const ${rv} = { ..._ctx, state: ${mfRef(ctx)}[${evIdx}]() }`,
|
|
3897
4071
|
`${ind(ctx)}const ${vv} = ${fn}(input, ${pos}, ${rv})`,
|
|
3898
4072
|
// withCtx runs on a spread child ctx — copy its recorded failure back
|
|
3899
4073
|
// and propagate the inner failure verbatim (interpreter parity).
|
|
@@ -4024,6 +4198,39 @@ function analyzeLazyUsageMulti(roots) {
|
|
|
4024
4198
|
for (const root of roots) walk2(root);
|
|
4025
4199
|
return { counts, recursive };
|
|
4026
4200
|
}
|
|
4201
|
+
function ruleDependencies(ruleMap) {
|
|
4202
|
+
const nameOf = /* @__PURE__ */ new Map();
|
|
4203
|
+
for (const [name, comb] of ruleMap) nameOf.set(comb, name);
|
|
4204
|
+
const deps = /* @__PURE__ */ new Map();
|
|
4205
|
+
for (const [name, comb] of ruleMap) {
|
|
4206
|
+
const found = /* @__PURE__ */ new Set();
|
|
4207
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4208
|
+
const walk2 = (p, isRoot) => {
|
|
4209
|
+
const def = p._def;
|
|
4210
|
+
const boundary = def.tag === "lazy" ? p._ruleName ?? nameOf.get(p) : nameOf.get(p);
|
|
4211
|
+
if (!isRoot && boundary !== void 0) {
|
|
4212
|
+
found.add(boundary);
|
|
4213
|
+
return;
|
|
4214
|
+
}
|
|
4215
|
+
if (seen.has(p)) return;
|
|
4216
|
+
seen.add(p);
|
|
4217
|
+
if (def.tag === "lazy") {
|
|
4218
|
+
let resolved;
|
|
4219
|
+
try {
|
|
4220
|
+
resolved = def.thunk();
|
|
4221
|
+
} catch {
|
|
4222
|
+
return;
|
|
4223
|
+
}
|
|
4224
|
+
walk2(resolved, false);
|
|
4225
|
+
return;
|
|
4226
|
+
}
|
|
4227
|
+
for (const child of childrenOf(def)) walk2(child, false);
|
|
4228
|
+
};
|
|
4229
|
+
walk2(comb, true);
|
|
4230
|
+
deps.set(name, [...found]);
|
|
4231
|
+
}
|
|
4232
|
+
return deps;
|
|
4233
|
+
}
|
|
4027
4234
|
function compile(combinator, mapFnSources) {
|
|
4028
4235
|
const ctx = {
|
|
4029
4236
|
vars: 0,
|
|
@@ -4107,6 +4314,150 @@ function compile(combinator, mapFnSources) {
|
|
|
4107
4314
|
}
|
|
4108
4315
|
};
|
|
4109
4316
|
}
|
|
4317
|
+
function compileLinkable(ruleMapArg, ns) {
|
|
4318
|
+
if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
|
|
4319
|
+
const ruleMap = ruleMapArg.filter(([, val]) => {
|
|
4320
|
+
const d = val._def;
|
|
4321
|
+
if (d.tag !== "lazy") return true;
|
|
4322
|
+
try {
|
|
4323
|
+
d.thunk();
|
|
4324
|
+
return true;
|
|
4325
|
+
} catch {
|
|
4326
|
+
return false;
|
|
4327
|
+
}
|
|
4328
|
+
});
|
|
4329
|
+
const ctx = {
|
|
4330
|
+
vars: 0,
|
|
4331
|
+
indent: 1,
|
|
4332
|
+
regexDecls: [],
|
|
4333
|
+
regexMap: /* @__PURE__ */ new Map(),
|
|
4334
|
+
expectedDecls: [],
|
|
4335
|
+
expectedMap: /* @__PURE__ */ new Map(),
|
|
4336
|
+
recordFail: true,
|
|
4337
|
+
mapFns: [],
|
|
4338
|
+
mapFnSrcs: [],
|
|
4339
|
+
buildFns: [],
|
|
4340
|
+
buildSrcs: [],
|
|
4341
|
+
runtimeParsers: [],
|
|
4342
|
+
needsCollator: false,
|
|
4343
|
+
namedParsers: /* @__PURE__ */ new Map(),
|
|
4344
|
+
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
4345
|
+
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
4346
|
+
namedFnDecls: [],
|
|
4347
|
+
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
4348
|
+
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
4349
|
+
ns
|
|
4350
|
+
};
|
|
4351
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4352
|
+
const ruleNames = /* @__PURE__ */ new Map();
|
|
4353
|
+
const fnNameToKey = /* @__PURE__ */ new Map();
|
|
4354
|
+
for (const [key, rule] of ruleMap) {
|
|
4355
|
+
const base = `_r_${key.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4356
|
+
const n = seen.get(base) ?? 0;
|
|
4357
|
+
seen.set(base, n + 1);
|
|
4358
|
+
const fn = n === 0 ? base : `${base}$${n}`;
|
|
4359
|
+
ruleNames.set(rule, fn);
|
|
4360
|
+
fnNameToKey.set(fn, key);
|
|
4361
|
+
}
|
|
4362
|
+
ctx.ruleNames = ruleNames;
|
|
4363
|
+
{
|
|
4364
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
4365
|
+
const scanExternal = (p) => {
|
|
4366
|
+
if (scanned.has(p)) return;
|
|
4367
|
+
scanned.add(p);
|
|
4368
|
+
const def = p._def;
|
|
4369
|
+
if (def.tag === "lazy") {
|
|
4370
|
+
let resolved;
|
|
4371
|
+
try {
|
|
4372
|
+
resolved = def.thunk();
|
|
4373
|
+
} catch {
|
|
4374
|
+
resolved = void 0;
|
|
4375
|
+
}
|
|
4376
|
+
if (resolved === void 0) {
|
|
4377
|
+
const name = p._ruleName;
|
|
4378
|
+
if (name && !ctx.namedParsers.has(p)) {
|
|
4379
|
+
const fn = `_r_${name.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
4380
|
+
ruleNames.set(p, fn);
|
|
4381
|
+
ctx.namedParsers.set(p, fn);
|
|
4382
|
+
}
|
|
4383
|
+
return;
|
|
4384
|
+
}
|
|
4385
|
+
scanExternal(resolved);
|
|
4386
|
+
return;
|
|
4387
|
+
}
|
|
4388
|
+
for (const child of childrenOf(def)) scanExternal(child);
|
|
4389
|
+
};
|
|
4390
|
+
for (const [, rule] of ruleMap) scanExternal(rule);
|
|
4391
|
+
}
|
|
4392
|
+
const perEntry = [];
|
|
4393
|
+
for (const [key, rule] of ruleMap) {
|
|
4394
|
+
const fn = ruleNames.get(rule);
|
|
4395
|
+
if (!ctx.namedParsers.has(rule)) {
|
|
4396
|
+
ctx.namedParsers.set(rule, fn);
|
|
4397
|
+
let resolved = rule;
|
|
4398
|
+
const def = rule._def;
|
|
4399
|
+
if (def.tag === "lazy") {
|
|
4400
|
+
try {
|
|
4401
|
+
resolved = def.thunk();
|
|
4402
|
+
} catch {
|
|
4403
|
+
return null;
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
const savedIndent = ctx.indent, savedFail = ctx.failLabel, savedRec = ctx.recordFail;
|
|
4407
|
+
ctx.indent = 1;
|
|
4408
|
+
ctx.failLabel = "_pfail";
|
|
4409
|
+
ctx.recordFail = true;
|
|
4410
|
+
const body = emit(resolved, ctx, "_pos");
|
|
4411
|
+
ctx.indent = savedIndent;
|
|
4412
|
+
ctx.failLabel = savedFail;
|
|
4413
|
+
ctx.recordFail = savedRec;
|
|
4414
|
+
pushNamedFnDecl(ctx, fn, body.stmts, body.valueVar, body.endVar);
|
|
4415
|
+
}
|
|
4416
|
+
perEntry.push({ key, r: emitNamedFnCall(ctx, fn, "_pos") });
|
|
4417
|
+
}
|
|
4418
|
+
if (ctx.runtimeParsers.length !== 0) return null;
|
|
4419
|
+
const mfSrcs = ctx.mapFns.length > 0 && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : null;
|
|
4420
|
+
const buildSrcs = ctx.buildFns.length > 0 && ctx.buildSrcs.every((s) => s !== null) ? ctx.buildSrcs : null;
|
|
4421
|
+
const ruleFns = /* @__PURE__ */ new Map();
|
|
4422
|
+
const privateFns = [];
|
|
4423
|
+
for (const decl of ctx.namedFnDecls) {
|
|
4424
|
+
const fnName = decl.match(/^function (\S+?)\(/)?.[1];
|
|
4425
|
+
const key = fnName ? fnNameToKey.get(fnName) : void 0;
|
|
4426
|
+
if (key !== void 0) ruleFns.set(key, decl);
|
|
4427
|
+
else privateFns.push(decl);
|
|
4428
|
+
}
|
|
4429
|
+
const mfDecl = ctx.mapFns.length === 0 ? "" : mfSrcs ? `const ${mfRef(ctx)} = [${mfSrcs.join(", ")}]` : `const ${mfRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "mf")}]`;
|
|
4430
|
+
const buildDecl = ctx.buildFns.length === 0 ? "" : buildSrcs ? `const ${buildRef(ctx)} = [${buildSrcs.join(", ")}]` : `const ${buildRef(ctx)} = _env[${JSON.stringify(nsp(ctx) + "build")}]`;
|
|
4431
|
+
const prelude = [
|
|
4432
|
+
...ctx.regexDecls,
|
|
4433
|
+
...ctx.expectedDecls,
|
|
4434
|
+
mfDecl,
|
|
4435
|
+
buildDecl,
|
|
4436
|
+
...privateFns
|
|
4437
|
+
].filter(Boolean);
|
|
4438
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
4439
|
+
for (const { key, r } of perEntry) {
|
|
4440
|
+
wrappers.set(key, [
|
|
4441
|
+
`function(input, _pos, _ctx) {`,
|
|
4442
|
+
` let pos = _pos`,
|
|
4443
|
+
...r.stmts,
|
|
4444
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
4445
|
+
`}`
|
|
4446
|
+
].join("\n"));
|
|
4447
|
+
}
|
|
4448
|
+
return {
|
|
4449
|
+
ns,
|
|
4450
|
+
keys: perEntry.map((e) => e.key),
|
|
4451
|
+
prelude,
|
|
4452
|
+
ruleFns,
|
|
4453
|
+
wrappers,
|
|
4454
|
+
deps: ruleDependencies(ruleMap),
|
|
4455
|
+
needsEmptyTl: !!ctx.needsEmptyTl,
|
|
4456
|
+
needsCollator: ctx.needsCollator,
|
|
4457
|
+
mfFns: mfSrcs ? [] : ctx.mapFns,
|
|
4458
|
+
buildFns: buildSrcs ? [] : ctx.buildFns
|
|
4459
|
+
};
|
|
4460
|
+
}
|
|
4110
4461
|
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
4111
4462
|
const bodyLines = [
|
|
4112
4463
|
` let pos = _pos`,
|
|
@@ -4145,6 +4496,87 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
4145
4496
|
].join("\n");
|
|
4146
4497
|
}
|
|
4147
4498
|
|
|
4499
|
+
// src/compiler/linker.ts
|
|
4500
|
+
var _nsCounter = 0;
|
|
4501
|
+
function linkable(rulesMap, ns) {
|
|
4502
|
+
const pieces = compileLinkable([...Object.entries(rulesMap)], ns ?? `_lk${_nsCounter++}_`);
|
|
4503
|
+
if (!pieces) throw new Error("linkable(): this grammar cannot be compiled to a linkable artifact (contains a runtime-only parser fallback)");
|
|
4504
|
+
return pieces;
|
|
4505
|
+
}
|
|
4506
|
+
function cstBuildHost(type, children, _rawChildren, span, _triviaLog, state) {
|
|
4507
|
+
return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
|
|
4508
|
+
}
|
|
4509
|
+
function pick(grammar, names) {
|
|
4510
|
+
const p = grammar.ruleFns instanceof Map ? grammar : linkable(grammar);
|
|
4511
|
+
const keep = /* @__PURE__ */ new Set();
|
|
4512
|
+
const has = new Set(p.keys);
|
|
4513
|
+
for (const n of names) {
|
|
4514
|
+
if (!has.has(n)) throw new Error(`pick: rule "${n}" is not in this grammar (available: ${p.keys.join(", ")})`);
|
|
4515
|
+
}
|
|
4516
|
+
const visit = (n) => {
|
|
4517
|
+
if (keep.has(n) || !has.has(n)) return;
|
|
4518
|
+
keep.add(n);
|
|
4519
|
+
for (const d of p.deps.get(n) ?? []) visit(d);
|
|
4520
|
+
};
|
|
4521
|
+
for (const n of names) visit(n);
|
|
4522
|
+
const filt = (m) => new Map([...m].filter(([k]) => keep.has(k)));
|
|
4523
|
+
return {
|
|
4524
|
+
...p,
|
|
4525
|
+
keys: [...keep],
|
|
4526
|
+
ruleFns: filt(p.ruleFns),
|
|
4527
|
+
wrappers: filt(p.wrappers),
|
|
4528
|
+
deps: filt(p.deps)
|
|
4529
|
+
};
|
|
4530
|
+
}
|
|
4531
|
+
function fusedBody(pieces) {
|
|
4532
|
+
const winner = /* @__PURE__ */ new Map();
|
|
4533
|
+
for (const p of pieces) for (const k of p.keys) winner.set(k, p);
|
|
4534
|
+
for (const [k, p] of winner) {
|
|
4535
|
+
for (const d of p.deps.get(k) ?? []) {
|
|
4536
|
+
if (!winner.has(d)) throw new Error(`compose: rule "${k}" references missing rule "${d}"`);
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
const contributing = new Set(winner.values());
|
|
4540
|
+
const needsEmptyTl = [...contributing].some((p) => p.needsEmptyTl);
|
|
4541
|
+
const needsCollator = [...contributing].some((p) => p.needsCollator);
|
|
4542
|
+
const lines = [
|
|
4543
|
+
// Shared sentinel protocol (must match NAMED_FN_FAIL / NAMED_FN_END in codegen).
|
|
4544
|
+
"const _pfFail = {}",
|
|
4545
|
+
"let _pfEnd",
|
|
4546
|
+
...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
|
|
4547
|
+
...needsCollator ? ["const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })"] : [],
|
|
4548
|
+
// Each contributing artifact's namespaced private prelude (regexes, _pf, …).
|
|
4549
|
+
...[...contributing].flatMap((p) => p.prelude),
|
|
4550
|
+
// The winning `_r_<Name>` function for each rule (one per name → no redeclare).
|
|
4551
|
+
...[...winner].map(([k, p]) => p.ruleFns.get(k))
|
|
4552
|
+
];
|
|
4553
|
+
const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
|
|
4554
|
+
const body = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
|
|
4555
|
+
const env = {};
|
|
4556
|
+
for (const p of contributing) {
|
|
4557
|
+
if (p.mfFns.length) env[`${p.ns}mf`] = p.mfFns;
|
|
4558
|
+
if (p.buildFns.length) env[`${p.ns}build`] = p.buildFns;
|
|
4559
|
+
}
|
|
4560
|
+
return { body, env };
|
|
4561
|
+
}
|
|
4562
|
+
function fuseRules(pieces) {
|
|
4563
|
+
const { body, env } = fusedBody(pieces);
|
|
4564
|
+
return new Function("_env", body)(env);
|
|
4565
|
+
}
|
|
4566
|
+
var COMPOSED_PIECES = /* @__PURE__ */ Symbol.for("parseman.composedPieces");
|
|
4567
|
+
function itemPieces(item) {
|
|
4568
|
+
const carried = item[COMPOSED_PIECES];
|
|
4569
|
+
if (Array.isArray(carried)) return carried;
|
|
4570
|
+
if (item.ruleFns instanceof Map) return [item];
|
|
4571
|
+
return [linkable(item)];
|
|
4572
|
+
}
|
|
4573
|
+
function compose(items) {
|
|
4574
|
+
const pieces = items.flatMap(itemPieces);
|
|
4575
|
+
const map = fuseRules(pieces);
|
|
4576
|
+
Object.defineProperty(map, COMPOSED_PIECES, { value: pieces, enumerable: false });
|
|
4577
|
+
return map;
|
|
4578
|
+
}
|
|
4579
|
+
|
|
4148
4580
|
// src/combinators/guard.ts
|
|
4149
4581
|
function guard(predicate) {
|
|
4150
4582
|
const meta = {
|
|
@@ -4342,6 +4774,7 @@ function manyRecover(item, until) {
|
|
|
4342
4774
|
}
|
|
4343
4775
|
|
|
4344
4776
|
// src/functional/doc.ts
|
|
4777
|
+
var REENTRY_MAX_SPAN_FRACTION = 0.5;
|
|
4345
4778
|
function isNode(x) {
|
|
4346
4779
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
4347
4780
|
}
|
|
@@ -4402,6 +4835,47 @@ function graftAndShift(root, path, newNode, delta) {
|
|
|
4402
4835
|
children: newChildren
|
|
4403
4836
|
};
|
|
4404
4837
|
}
|
|
4838
|
+
function structurallyEqual(a, b) {
|
|
4839
|
+
if (a === b) return true;
|
|
4840
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
4841
|
+
const at = a._tag;
|
|
4842
|
+
const bt = b._tag;
|
|
4843
|
+
if (at !== bt) return false;
|
|
4844
|
+
const as = a.span;
|
|
4845
|
+
const bs = b.span;
|
|
4846
|
+
if (as || bs) {
|
|
4847
|
+
if (!as || !bs || as.start !== bs.start || as.end !== bs.end) return false;
|
|
4848
|
+
}
|
|
4849
|
+
if (at === "leaf" || at === "trivia") {
|
|
4850
|
+
return a.value === b.value;
|
|
4851
|
+
}
|
|
4852
|
+
if (a.type !== b.type) return false;
|
|
4853
|
+
const ac = a.children;
|
|
4854
|
+
const bc = b.children;
|
|
4855
|
+
if (ac || bc) {
|
|
4856
|
+
if (!ac || !bc || ac.length !== bc.length) return false;
|
|
4857
|
+
for (let i = 0; i < ac.length; i++) if (!structurallyEqual(ac[i], bc[i])) return false;
|
|
4858
|
+
}
|
|
4859
|
+
return true;
|
|
4860
|
+
}
|
|
4861
|
+
function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced) {
|
|
4862
|
+
if (!produced.ok) return false;
|
|
4863
|
+
if (boundary >= newInput.length) return true;
|
|
4864
|
+
for (const sentinel of [" ", "\uFFFF"]) {
|
|
4865
|
+
if (newInput[boundary] === sentinel) continue;
|
|
4866
|
+
const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
|
|
4867
|
+
const ctx = { trackLines: false, state, build };
|
|
4868
|
+
let r;
|
|
4869
|
+
try {
|
|
4870
|
+
r = ruleFn(probed, start, ctx);
|
|
4871
|
+
} catch {
|
|
4872
|
+
return false;
|
|
4873
|
+
}
|
|
4874
|
+
if (!r.ok || r.span.end !== produced.span.end) return false;
|
|
4875
|
+
if (!structurallyEqual(r.value, produced.value)) return false;
|
|
4876
|
+
}
|
|
4877
|
+
return true;
|
|
4878
|
+
}
|
|
4405
4879
|
var ParseDocImpl = class _ParseDocImpl {
|
|
4406
4880
|
_registry;
|
|
4407
4881
|
_rootRule;
|
|
@@ -4433,20 +4907,24 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
4433
4907
|
}
|
|
4434
4908
|
const rebuild = this._opts.rebuild ?? defaultRebuild;
|
|
4435
4909
|
for (const { node: node2, path } of candidates) {
|
|
4910
|
+
if (node2.span.end - node2.span.start > this.input.length * REENTRY_MAX_SPAN_FRACTION) break;
|
|
4436
4911
|
const ruleFn = this._registry[node2.type];
|
|
4437
4912
|
if (!ruleFn) continue;
|
|
4438
|
-
|
|
4913
|
+
if (!(node2.span.start <= from && to <= node2.span.end)) continue;
|
|
4914
|
+
const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
|
|
4439
4915
|
const r = ruleFn(newInput, node2.span.start, ctx);
|
|
4440
4916
|
if (!r.ok) continue;
|
|
4441
|
-
if (r.span.end
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
4917
|
+
if (r.span.end !== node2.span.end + delta) continue;
|
|
4918
|
+
if (!boundaryIsSafe(ruleFn, newInput, node2.span.start, node2.span.end + delta, node2.state, this._opts.build, r)) {
|
|
4919
|
+
continue;
|
|
4920
|
+
}
|
|
4921
|
+
if (delta === 0) {
|
|
4922
|
+
const newTree2 = replaceAtPath(rebuild, this.tree, path, r.value);
|
|
4923
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
|
|
4449
4924
|
}
|
|
4925
|
+
if (this._opts.rebuild) return reparse();
|
|
4926
|
+
const newTree = graftAndShift(this.tree, path, r.value, delta);
|
|
4927
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
4450
4928
|
}
|
|
4451
4929
|
return reparse();
|
|
4452
4930
|
}
|
|
@@ -4454,7 +4932,7 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
4454
4932
|
function parseDoc(registry, rootRule, input, opts = {}) {
|
|
4455
4933
|
const ruleFn = registry[rootRule];
|
|
4456
4934
|
if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
|
|
4457
|
-
const ctx = { trackLines: false, state: opts.state };
|
|
4935
|
+
const ctx = { trackLines: false, state: opts.state, build: opts.build };
|
|
4458
4936
|
const r = ruleFn(input, 0, ctx);
|
|
4459
4937
|
if (r.ok) {
|
|
4460
4938
|
return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
|
|
@@ -4462,12 +4940,45 @@ function parseDoc(registry, rootRule, input, opts = {}) {
|
|
|
4462
4940
|
return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
4463
4941
|
}
|
|
4464
4942
|
|
|
4943
|
+
// src/functional/run.ts
|
|
4944
|
+
var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
|
|
4945
|
+
function run(entry, input, options = {}) {
|
|
4946
|
+
const triviaLog = [];
|
|
4947
|
+
const errors = [];
|
|
4948
|
+
const ctx = {
|
|
4949
|
+
trackLines: false,
|
|
4950
|
+
_triviaLog: triviaLog,
|
|
4951
|
+
_errors: errors,
|
|
4952
|
+
build: options.build,
|
|
4953
|
+
state: options.state
|
|
4954
|
+
};
|
|
4955
|
+
const r = invoke(entry, input, 0, ctx);
|
|
4956
|
+
let unconsumedFrom = null;
|
|
4957
|
+
if (r.ok) {
|
|
4958
|
+
let pos = r.span?.end ?? 0;
|
|
4959
|
+
if (options.trivia && pos < input.length) {
|
|
4960
|
+
const t = invoke(options.trivia, input, pos, { trackLines: false });
|
|
4961
|
+
if (t.ok && t.span.end > pos) pos = t.span.end;
|
|
4962
|
+
}
|
|
4963
|
+
unconsumedFrom = pos < input.length ? pos : null;
|
|
4964
|
+
}
|
|
4965
|
+
return {
|
|
4966
|
+
ok: r.ok,
|
|
4967
|
+
value: r.ok ? r.value : void 0,
|
|
4968
|
+
span: r.span ?? { start: 0, end: 0 },
|
|
4969
|
+
expected: r.ok ? [] : r.expected ?? [],
|
|
4970
|
+
errors,
|
|
4971
|
+
triviaLog,
|
|
4972
|
+
unconsumedFrom
|
|
4973
|
+
};
|
|
4974
|
+
}
|
|
4975
|
+
|
|
4465
4976
|
// src/cst/trivia-index.ts
|
|
4466
|
-
function merge(map, key,
|
|
4467
|
-
if (
|
|
4977
|
+
function merge(map, key, run2) {
|
|
4978
|
+
if (run2.length === 0) return;
|
|
4468
4979
|
const existing = map.get(key);
|
|
4469
|
-
if (existing) existing.push(...
|
|
4470
|
-
else map.set(key, [...
|
|
4980
|
+
if (existing) existing.push(...run2);
|
|
4981
|
+
else map.set(key, [...run2]);
|
|
4471
4982
|
}
|
|
4472
4983
|
function buildTriviaIndex(root, input = "", opts) {
|
|
4473
4984
|
const before = /* @__PURE__ */ new Map();
|
|
@@ -4479,11 +4990,11 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
4479
4990
|
for (let i = 0; i < log.length; i += 3) {
|
|
4480
4991
|
const tStart = log[i], tEnd = log[i + 1], insertIdx = log[i + 2];
|
|
4481
4992
|
if (tEnd <= tStart) continue;
|
|
4482
|
-
const
|
|
4993
|
+
const run2 = [{ value: input.slice(tStart, tEnd), span: { start: tStart, end: tEnd } }];
|
|
4483
4994
|
const next = raw[insertIdx];
|
|
4484
|
-
if (next) merge(before, next.span.start,
|
|
4995
|
+
if (next) merge(before, next.span.start, run2);
|
|
4485
4996
|
const prev = insertIdx > 0 ? raw[insertIdx - 1] : void 0;
|
|
4486
|
-
if (prev) merge(after, prev.span.end,
|
|
4997
|
+
if (prev) merge(after, prev.span.end, run2);
|
|
4487
4998
|
}
|
|
4488
4999
|
}
|
|
4489
5000
|
const ch = node2.children;
|
|
@@ -4500,17 +5011,17 @@ function buildTriviaIndex(root, input = "", opts) {
|
|
|
4500
5011
|
const rootSpan = root?.span;
|
|
4501
5012
|
const re = new RegExp(opts.trivia.source, opts.trivia.flags.replace(/[gy]/g, "") + "y");
|
|
4502
5013
|
const scanFrom = (from) => {
|
|
4503
|
-
const
|
|
5014
|
+
const run2 = [];
|
|
4504
5015
|
let pos = from;
|
|
4505
5016
|
re.lastIndex = pos;
|
|
4506
5017
|
let m = re.exec(input);
|
|
4507
5018
|
while (m && m.index === pos && m[0].length > 0) {
|
|
4508
|
-
|
|
5019
|
+
run2.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
|
|
4509
5020
|
pos += m[0].length;
|
|
4510
5021
|
re.lastIndex = pos;
|
|
4511
5022
|
m = re.exec(input);
|
|
4512
5023
|
}
|
|
4513
|
-
return
|
|
5024
|
+
return run2;
|
|
4514
5025
|
};
|
|
4515
5026
|
if (rootSpan && rootSpan.start > 0) {
|
|
4516
5027
|
const lead = scanFrom(0);
|
|
@@ -4596,20 +5107,295 @@ function triviaEntries(log, labels, opts) {
|
|
|
4596
5107
|
}
|
|
4597
5108
|
};
|
|
4598
5109
|
}
|
|
5110
|
+
|
|
5111
|
+
// src/cst/offset-model.ts
|
|
5112
|
+
var { isFinite } = Number;
|
|
5113
|
+
function floorIndex(arr, len, x) {
|
|
5114
|
+
let lo = 0;
|
|
5115
|
+
let hi = len - 1;
|
|
5116
|
+
let ans = -1;
|
|
5117
|
+
while (lo <= hi) {
|
|
5118
|
+
const mid = lo + hi >>> 1;
|
|
5119
|
+
if (arr[mid] <= x) {
|
|
5120
|
+
ans = mid;
|
|
5121
|
+
lo = mid + 1;
|
|
5122
|
+
} else {
|
|
5123
|
+
hi = mid - 1;
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
return ans;
|
|
5127
|
+
}
|
|
5128
|
+
var OffsetIndex = class {
|
|
5129
|
+
/** Slot starts, sorted ascending. Parallel to `_ends`. */
|
|
5130
|
+
_starts;
|
|
5131
|
+
_ends;
|
|
5132
|
+
count;
|
|
5133
|
+
docStart;
|
|
5134
|
+
docEnd;
|
|
5135
|
+
constructor(slots, docStart = 0, docEnd) {
|
|
5136
|
+
const sorted = [...slots].sort((a, b) => a.start - b.start);
|
|
5137
|
+
const starts = [];
|
|
5138
|
+
const ends = [];
|
|
5139
|
+
let prevEnd = docStart;
|
|
5140
|
+
for (const s of sorted) {
|
|
5141
|
+
if (!isFinite(s.start) || !isFinite(s.end) || s.end < s.start) {
|
|
5142
|
+
throw new RangeError(`OffsetIndex: invalid slot [${s.start}, ${s.end}]`);
|
|
5143
|
+
}
|
|
5144
|
+
if (s.start < prevEnd) {
|
|
5145
|
+
throw new RangeError(`OffsetIndex: overlapping/out-of-order slot [${s.start}, ${s.end}] after end ${prevEnd}`);
|
|
5146
|
+
}
|
|
5147
|
+
starts.push(s.start);
|
|
5148
|
+
ends.push(s.end);
|
|
5149
|
+
prevEnd = s.end;
|
|
5150
|
+
}
|
|
5151
|
+
this._starts = starts;
|
|
5152
|
+
this._ends = ends;
|
|
5153
|
+
this.count = starts.length;
|
|
5154
|
+
this.docStart = docStart;
|
|
5155
|
+
this.docEnd = docEnd ?? (this.count ? Math.max(prevEnd, ends[ends.length - 1]) : docStart);
|
|
5156
|
+
}
|
|
5157
|
+
/** The slot at index `i` (0-based, source order), or undefined. */
|
|
5158
|
+
slot(i) {
|
|
5159
|
+
if (i < 0 || i >= this.count) return void 0;
|
|
5160
|
+
return { start: this._starts[i], end: this._ends[i] };
|
|
5161
|
+
}
|
|
5162
|
+
/**
|
|
5163
|
+
* Index of the gap that CONTAINS `offset`, in `[0, count]`. Gap `i` spans
|
|
5164
|
+
* `[endOf(i-1), startOf(i))` where `endOf(-1)=docStart` and `startOf(count)=docEnd`.
|
|
5165
|
+
* An offset that lands inside a slot returns that slot's TRAILING gap index
|
|
5166
|
+
* (i.e. the gap after it) — callers querying at slot boundaries get the
|
|
5167
|
+
* intuitive answer via `gapBefore`/`gapAfter`.
|
|
5168
|
+
*/
|
|
5169
|
+
gapIndexAt(offset) {
|
|
5170
|
+
const i = floorIndex(this._starts, this.count, offset);
|
|
5171
|
+
if (i < 0) return 0;
|
|
5172
|
+
if (offset < this._ends[i]) return i + 1;
|
|
5173
|
+
return i + 1;
|
|
5174
|
+
}
|
|
5175
|
+
/** The gap at index `i` in `[0, count]`. */
|
|
5176
|
+
gap(i) {
|
|
5177
|
+
if (i < 0 || i > this.count) return void 0;
|
|
5178
|
+
const start = i === 0 ? this.docStart : this._ends[i - 1];
|
|
5179
|
+
const end = i === this.count ? this.docEnd : this._starts[i];
|
|
5180
|
+
return { start, end };
|
|
5181
|
+
}
|
|
5182
|
+
/**
|
|
5183
|
+
* The trivia gap immediately BEFORE `offset` (`[prevSlotEnd, offset)`), where
|
|
5184
|
+
* `prevSlotEnd` is the end of the last slot ending at or before `offset` (or
|
|
5185
|
+
* `docStart`). For a real slot start this is the gap preceding that slot; for an
|
|
5186
|
+
* adjacent slot the gap is empty and agrees with `gapAfter` on the same seam.
|
|
5187
|
+
*/
|
|
5188
|
+
gapBefore(offset) {
|
|
5189
|
+
const e = floorIndex(this._ends, this.count, offset);
|
|
5190
|
+
const start = e >= 0 ? this._ends[e] : this.docStart;
|
|
5191
|
+
return { start, end: offset };
|
|
5192
|
+
}
|
|
5193
|
+
/**
|
|
5194
|
+
* The trivia gap immediately AFTER `offset` (`[offset, nextSlotStart)`), where
|
|
5195
|
+
* `nextSlotStart` is the least slot start at or after `offset` (or `docEnd`).
|
|
5196
|
+
*/
|
|
5197
|
+
gapAfter(offset) {
|
|
5198
|
+
return { start: offset, end: this._ceilStart(offset) };
|
|
5199
|
+
}
|
|
5200
|
+
/** Least slot start `>= offset`, else `docEnd`. */
|
|
5201
|
+
_ceilStart(offset) {
|
|
5202
|
+
let lo = 0;
|
|
5203
|
+
let hi = this.count - 1;
|
|
5204
|
+
let ans = this.docEnd;
|
|
5205
|
+
while (lo <= hi) {
|
|
5206
|
+
const mid = lo + hi >>> 1;
|
|
5207
|
+
if (this._starts[mid] >= offset) {
|
|
5208
|
+
ans = this._starts[mid];
|
|
5209
|
+
hi = mid - 1;
|
|
5210
|
+
} else {
|
|
5211
|
+
lo = mid + 1;
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
return ans;
|
|
5215
|
+
}
|
|
5216
|
+
/** Is this gap empty (adjacent slots, no trivia)? */
|
|
5217
|
+
isEmpty(gap) {
|
|
5218
|
+
return gap.end <= gap.start;
|
|
5219
|
+
}
|
|
5220
|
+
};
|
|
5221
|
+
var NL = 10;
|
|
5222
|
+
var CR = 13;
|
|
5223
|
+
var SP = 32;
|
|
5224
|
+
var TAB = 9;
|
|
5225
|
+
function gapText(source, gap) {
|
|
5226
|
+
return gap.end > gap.start ? source.slice(gap.start, gap.end) : "";
|
|
5227
|
+
}
|
|
5228
|
+
function lineBreaksIn(source, gap) {
|
|
5229
|
+
let n = 0;
|
|
5230
|
+
for (let i = gap.start; i < gap.end; i++) {
|
|
5231
|
+
const c = source.charCodeAt(i);
|
|
5232
|
+
if (c === NL) n++;
|
|
5233
|
+
else if (c === CR && source.charCodeAt(i + 1) !== NL) n++;
|
|
5234
|
+
}
|
|
5235
|
+
return n;
|
|
5236
|
+
}
|
|
5237
|
+
function blankLinesIn(source, gap) {
|
|
5238
|
+
return Math.max(0, lineBreaksIn(source, gap) - 1);
|
|
5239
|
+
}
|
|
5240
|
+
function lineStartWithin(source, gap) {
|
|
5241
|
+
for (let i = gap.end - 1; i >= gap.start; i--) {
|
|
5242
|
+
const c = source.charCodeAt(i);
|
|
5243
|
+
if (c === NL || c === CR) return i + 1;
|
|
5244
|
+
}
|
|
5245
|
+
return gap.start;
|
|
5246
|
+
}
|
|
5247
|
+
function indentWidth(source, gap) {
|
|
5248
|
+
const ls = lineStartWithin(source, gap);
|
|
5249
|
+
if (ls === gap.start) return 0;
|
|
5250
|
+
return gap.end - ls;
|
|
5251
|
+
}
|
|
5252
|
+
function indentMixed(source, gap) {
|
|
5253
|
+
const ls = lineStartWithin(source, gap);
|
|
5254
|
+
let sawSpace = false;
|
|
5255
|
+
let sawTab = false;
|
|
5256
|
+
for (let i = ls; i < gap.end; i++) {
|
|
5257
|
+
const c = source.charCodeAt(i);
|
|
5258
|
+
if (c === SP) sawSpace = true;
|
|
5259
|
+
else if (c === TAB) sawTab = true;
|
|
5260
|
+
}
|
|
5261
|
+
return sawSpace && sawTab;
|
|
5262
|
+
}
|
|
5263
|
+
function commentsIn(source, gap, comment) {
|
|
5264
|
+
const re = new RegExp(comment.source, comment.flags.replace(/[gy]/g, "") + "g");
|
|
5265
|
+
re.lastIndex = gap.start;
|
|
5266
|
+
const out = [];
|
|
5267
|
+
let m;
|
|
5268
|
+
while ((m = re.exec(source)) && m.index < gap.end) {
|
|
5269
|
+
if (m[0].length === 0) {
|
|
5270
|
+
re.lastIndex++;
|
|
5271
|
+
continue;
|
|
5272
|
+
}
|
|
5273
|
+
if (m.index + m[0].length > gap.end) break;
|
|
5274
|
+
out.push({ start: m.index, end: m.index + m[0].length });
|
|
5275
|
+
}
|
|
5276
|
+
return out;
|
|
5277
|
+
}
|
|
5278
|
+
function gapIsSignificant(source, gap, comment) {
|
|
5279
|
+
if (gap.end <= gap.start) return false;
|
|
5280
|
+
if (lineBreaksIn(source, gap) > 0) return true;
|
|
5281
|
+
if (comment && commentsIn(source, gap, comment).length > 0) return true;
|
|
5282
|
+
return false;
|
|
5283
|
+
}
|
|
5284
|
+
function collectLeafSlots(root) {
|
|
5285
|
+
const slots = [];
|
|
5286
|
+
const visit = (n) => {
|
|
5287
|
+
const kids = n.children;
|
|
5288
|
+
if (kids && kids.length) {
|
|
5289
|
+
for (const c of kids) visit(c);
|
|
5290
|
+
} else if (n.span) {
|
|
5291
|
+
slots.push({ start: n.span.start, end: n.span.end });
|
|
5292
|
+
}
|
|
5293
|
+
};
|
|
5294
|
+
if (root) visit(root);
|
|
5295
|
+
return slots;
|
|
5296
|
+
}
|
|
5297
|
+
function buildOffsetIndex(root, input = "") {
|
|
5298
|
+
return new OffsetIndex(collectLeafSlots(root), 0, input.length);
|
|
5299
|
+
}
|
|
5300
|
+
|
|
5301
|
+
// src/cst/relative-spans.ts
|
|
5302
|
+
function relativize(node2, parentBase = 0) {
|
|
5303
|
+
return {
|
|
5304
|
+
start: node2.start - parentBase,
|
|
5305
|
+
end: node2.end - parentBase,
|
|
5306
|
+
children: node2.children.map((c) => relativize(c, node2.start))
|
|
5307
|
+
};
|
|
5308
|
+
}
|
|
5309
|
+
function absolutize(node2, parentBase = 0) {
|
|
5310
|
+
const start = parentBase + node2.start;
|
|
5311
|
+
return {
|
|
5312
|
+
start,
|
|
5313
|
+
end: parentBase + node2.end,
|
|
5314
|
+
children: node2.children.map((c) => absolutize(c, start))
|
|
5315
|
+
};
|
|
5316
|
+
}
|
|
5317
|
+
function absoluteSpanAt(root, path) {
|
|
5318
|
+
let base = 0;
|
|
5319
|
+
let node2 = root;
|
|
5320
|
+
for (const idx of path) {
|
|
5321
|
+
base += node2.start;
|
|
5322
|
+
const child = node2.children[idx];
|
|
5323
|
+
if (!child) throw new RangeError(`absoluteSpanAt: no child ${idx} on path ${path.join(".")}`);
|
|
5324
|
+
node2 = child;
|
|
5325
|
+
}
|
|
5326
|
+
return { start: base + node2.start, end: base + node2.end };
|
|
5327
|
+
}
|
|
5328
|
+
function shiftAbsolute(node2, at, delta) {
|
|
5329
|
+
const s = node2.start >= at ? node2.start + delta : node2.start;
|
|
5330
|
+
const e = node2.end >= at ? node2.end + delta : node2.end;
|
|
5331
|
+
return {
|
|
5332
|
+
start: s,
|
|
5333
|
+
end: e,
|
|
5334
|
+
children: node2.children.map((c) => shiftAbsolute(c, at, delta))
|
|
5335
|
+
};
|
|
5336
|
+
}
|
|
5337
|
+
function applyEdit(root, at, delta, stats) {
|
|
5338
|
+
return editNode(root, at, delta, 0, 0, stats);
|
|
5339
|
+
}
|
|
5340
|
+
function editNode(node2, at, delta, parentBase, parentShift, stats) {
|
|
5341
|
+
const absStart = parentBase + node2.start;
|
|
5342
|
+
const absEnd = parentBase + node2.end;
|
|
5343
|
+
if (absEnd < at) return node2;
|
|
5344
|
+
const startShifts = absStart >= at;
|
|
5345
|
+
if (startShifts && parentShift === delta) return node2;
|
|
5346
|
+
if (stats) stats.allocated++;
|
|
5347
|
+
const newAbsStart = startShifts ? absStart + delta : absStart;
|
|
5348
|
+
const newAbsEnd = absEnd + delta;
|
|
5349
|
+
const newParentBase = parentBase + parentShift;
|
|
5350
|
+
const newStart = newAbsStart - newParentBase;
|
|
5351
|
+
const newEnd = newAbsEnd - newParentBase;
|
|
5352
|
+
const ownShift = newAbsStart - absStart;
|
|
5353
|
+
let changed = false;
|
|
5354
|
+
const newChildren = [];
|
|
5355
|
+
for (const c of node2.children) {
|
|
5356
|
+
const nc = editNode(c, at, delta, absStart, ownShift, stats);
|
|
5357
|
+
if (nc !== c) changed = true;
|
|
5358
|
+
newChildren.push(nc);
|
|
5359
|
+
}
|
|
5360
|
+
return {
|
|
5361
|
+
start: newStart,
|
|
5362
|
+
end: newEnd,
|
|
5363
|
+
children: changed ? newChildren : node2.children
|
|
5364
|
+
};
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5367
|
+
// src/index.ts
|
|
5368
|
+
registerRegexAnalyzer(firstSetFromRegex);
|
|
4599
5369
|
export {
|
|
5370
|
+
OffsetIndex,
|
|
5371
|
+
absoluteSpanAt,
|
|
5372
|
+
absolutize,
|
|
4600
5373
|
annotateSpan,
|
|
5374
|
+
applyEdit,
|
|
4601
5375
|
balanced,
|
|
5376
|
+
blankLinesIn,
|
|
4602
5377
|
buildLineIndex,
|
|
5378
|
+
buildOffsetIndex,
|
|
4603
5379
|
buildTriviaIndex,
|
|
4604
5380
|
choice,
|
|
5381
|
+
collectLeafSlots,
|
|
5382
|
+
commentsIn,
|
|
4605
5383
|
compile,
|
|
4606
5384
|
completionsAt,
|
|
5385
|
+
compose,
|
|
4607
5386
|
createVisitor,
|
|
5387
|
+
cstBuildHost,
|
|
4608
5388
|
expect,
|
|
5389
|
+
gapIsSignificant,
|
|
5390
|
+
gapText,
|
|
4609
5391
|
guard,
|
|
5392
|
+
indentMixed,
|
|
5393
|
+
indentWidth,
|
|
4610
5394
|
isParseError,
|
|
4611
5395
|
keywords,
|
|
4612
5396
|
label,
|
|
5397
|
+
lineBreaksIn,
|
|
5398
|
+
lineStartWithin,
|
|
4613
5399
|
literal,
|
|
4614
5400
|
makeWord,
|
|
4615
5401
|
many,
|
|
@@ -4623,14 +5409,18 @@ export {
|
|
|
4623
5409
|
parse,
|
|
4624
5410
|
parseDoc,
|
|
4625
5411
|
parser,
|
|
5412
|
+
pick,
|
|
4626
5413
|
recover,
|
|
4627
5414
|
ref,
|
|
4628
5415
|
regex,
|
|
5416
|
+
relativize,
|
|
4629
5417
|
rules,
|
|
5418
|
+
run,
|
|
4630
5419
|
scanTo,
|
|
4631
5420
|
sepBy,
|
|
4632
5421
|
sepByRecover,
|
|
4633
5422
|
sequence,
|
|
5423
|
+
shiftAbsolute,
|
|
4634
5424
|
skip,
|
|
4635
5425
|
staticExpected,
|
|
4636
5426
|
transform,
|