parseman 0.23.0 → 0.25.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/README.md +1 -1
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts +6 -0
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/cst/relative-spans.d.ts +32 -0
- package/dist/cst/relative-spans.d.ts.map +1 -1
- package/dist/functional/doc.d.ts +53 -3
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/index.cjs +475 -139
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +475 -139
- package/dist/index.js.map +4 -4
- package/dist/plugin/index.cjs +156 -39
- package/dist/plugin/index.cjs.map +3 -3
- package/dist/plugin/index.js +156 -39
- package/dist/plugin/index.js.map +3 -3
- package/dist/spec/ebnf.d.ts +13 -0
- package/dist/spec/ebnf.d.ts.map +1 -0
- package/dist/spec/index.cjs +1038 -0
- package/dist/spec/index.cjs.map +7 -0
- package/dist/spec/index.d.ts +23 -0
- package/dist/spec/index.d.ts.map +1 -0
- package/dist/spec/index.js +1015 -0
- package/dist/spec/index.js.map +7 -0
- package/dist/spec/model.d.ts +117 -0
- package/dist/spec/model.d.ts.map +1 -0
- package/dist/spec/railroad-lib.d.ts +11 -0
- package/dist/spec/railroad-lib.d.ts.map +1 -0
- package/dist/spec/railroad.d.ts +30 -0
- package/dist/spec/railroad.d.ts.map +1 -0
- package/package.json +8 -2
package/dist/index.js
CHANGED
|
@@ -343,24 +343,35 @@ function literal(value, opts = {}) {
|
|
|
343
343
|
{ lo: firstUpper, hi: firstUpper }
|
|
344
344
|
] } : firstSet2;
|
|
345
345
|
}
|
|
346
|
+
const expected = [JSON.stringify(value)];
|
|
347
|
+
const parse2 = !caseInsensitive && value.length === 1 ? (() => {
|
|
348
|
+
const code = value.charCodeAt(0);
|
|
349
|
+
return function parse3(input, pos, ctx) {
|
|
350
|
+
if (input.charCodeAt(pos) === code) {
|
|
351
|
+
const span = { start: pos, end: pos + 1 };
|
|
352
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
|
|
353
|
+
return { ok: true, value, span };
|
|
354
|
+
}
|
|
355
|
+
return failAt(ctx, expected, pos);
|
|
356
|
+
};
|
|
357
|
+
})() : function parse3(input, pos, ctx) {
|
|
358
|
+
const end = pos + value.length;
|
|
359
|
+
if (end > input.length) {
|
|
360
|
+
return failAt(ctx, expected, pos);
|
|
361
|
+
}
|
|
362
|
+
const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
|
|
363
|
+
if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
|
|
364
|
+
const span = { start: pos, end };
|
|
365
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
|
|
366
|
+
return { ok: true, value: matchedValue, span };
|
|
367
|
+
}
|
|
368
|
+
return failAt(ctx, expected, pos);
|
|
369
|
+
};
|
|
346
370
|
return {
|
|
347
371
|
_tag: "literal",
|
|
348
372
|
_meta: meta,
|
|
349
373
|
_def: { tag: "literal", value, caseInsensitive },
|
|
350
|
-
parse
|
|
351
|
-
const end = pos + value.length;
|
|
352
|
-
if (end > input.length) {
|
|
353
|
-
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
354
|
-
}
|
|
355
|
-
const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
|
|
356
|
-
if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
|
|
357
|
-
const span = { start: pos, end };
|
|
358
|
-
const leaf = { _tag: "leaf", value: matchedValue, span };
|
|
359
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
360
|
-
return { ok: true, value: matchedValue, span };
|
|
361
|
-
}
|
|
362
|
-
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
363
|
-
}
|
|
374
|
+
parse: parse2
|
|
364
375
|
};
|
|
365
376
|
}
|
|
366
377
|
|
|
@@ -495,8 +506,7 @@ function regex(pattern, flags = "") {
|
|
|
495
506
|
if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
|
|
496
507
|
const value = input.slice(pos, scanEnd);
|
|
497
508
|
const span2 = { start: pos, end: scanEnd };
|
|
498
|
-
|
|
499
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf2);
|
|
509
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
|
|
500
510
|
return { ok: true, value, span: span2 };
|
|
501
511
|
}
|
|
502
512
|
anchored.lastIndex = pos;
|
|
@@ -505,8 +515,7 @@ function regex(pattern, flags = "") {
|
|
|
505
515
|
return failAt(ctx, [`/${source}/`], pos);
|
|
506
516
|
}
|
|
507
517
|
const span = { start: pos, end: pos + m[0].length };
|
|
508
|
-
|
|
509
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
518
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
|
|
510
519
|
return { ok: true, value: m[0], span };
|
|
511
520
|
}
|
|
512
521
|
};
|
|
@@ -814,8 +823,7 @@ function keywords(words, opts = {}) {
|
|
|
814
823
|
}
|
|
815
824
|
const value = m[0];
|
|
816
825
|
const span = { start: pos, end: pos + value.length };
|
|
817
|
-
|
|
818
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
|
|
826
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
|
|
819
827
|
return { ok: true, value, span };
|
|
820
828
|
}
|
|
821
829
|
};
|
|
@@ -1305,33 +1313,136 @@ function loopScanner(arms) {
|
|
|
1305
1313
|
}
|
|
1306
1314
|
function regexTriviaScanner(parser2) {
|
|
1307
1315
|
if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1310
|
-
return blockCommentSource(parser2._def.source) ? scanBlockComment : null;
|
|
1316
|
+
const source = parser2._def.source;
|
|
1317
|
+
return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
|
|
1311
1318
|
}
|
|
1312
|
-
function
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1319
|
+
function inRanges2(cp, ranges) {
|
|
1320
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
1321
|
+
const r = ranges[i];
|
|
1322
|
+
if (cp >= r[0] && cp <= r[1]) return true;
|
|
1323
|
+
}
|
|
1324
|
+
return false;
|
|
1325
|
+
}
|
|
1326
|
+
function classScanner(classBody) {
|
|
1327
|
+
const ranges = parseClassRanges(classBody);
|
|
1328
|
+
if (!ranges) return null;
|
|
1329
|
+
return (input, cur) => {
|
|
1330
|
+
let pos = cur;
|
|
1331
|
+
while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
|
|
1332
|
+
return pos;
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
function lineCommentScanner(commentCode) {
|
|
1336
|
+
return (input, cur) => {
|
|
1337
|
+
if (input.charCodeAt(cur) !== commentCode) return cur;
|
|
1338
|
+
let pos = cur + 1;
|
|
1339
|
+
while (pos < input.length) {
|
|
1340
|
+
const cc = input.charCodeAt(pos);
|
|
1341
|
+
if (cc === 10 || cc === 13) break;
|
|
1342
|
+
pos++;
|
|
1321
1343
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1344
|
+
return pos;
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
function classRunSource(source) {
|
|
1348
|
+
const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
|
|
1349
|
+
return m ? classScanner(m[1]) : null;
|
|
1350
|
+
}
|
|
1351
|
+
function classifyTriviaArm(arm) {
|
|
1352
|
+
const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
|
|
1353
|
+
if (cls) {
|
|
1354
|
+
const ranges = parseClassRanges(cls[1]);
|
|
1355
|
+
return ranges ? { kind: "class", ranges } : null;
|
|
1328
1356
|
}
|
|
1357
|
+
const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
|
|
1358
|
+
if (lc) {
|
|
1359
|
+
const marker = lc[1];
|
|
1360
|
+
return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
|
|
1361
|
+
}
|
|
1362
|
+
return null;
|
|
1363
|
+
}
|
|
1364
|
+
function armScanner(arm) {
|
|
1365
|
+
if (arm.kind === "comment") return lineCommentScanner(arm.code);
|
|
1366
|
+
const ranges = arm.ranges;
|
|
1329
1367
|
return (input, cur) => {
|
|
1330
1368
|
let pos = cur;
|
|
1331
|
-
while (pos < input.length &&
|
|
1369
|
+
while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
|
|
1332
1370
|
return pos;
|
|
1333
1371
|
};
|
|
1334
1372
|
}
|
|
1373
|
+
function fusedTriviaScanner(ranges, commentCodes) {
|
|
1374
|
+
const c0 = commentCodes[0];
|
|
1375
|
+
const single = commentCodes.length === 1;
|
|
1376
|
+
return (input, cur) => {
|
|
1377
|
+
let pos = cur;
|
|
1378
|
+
const len = input.length;
|
|
1379
|
+
for (; ; ) {
|
|
1380
|
+
const c = input.charCodeAt(pos);
|
|
1381
|
+
if (pos < len && inRanges2(c, ranges)) {
|
|
1382
|
+
pos++;
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (single ? c === c0 : commentCodes.includes(c)) {
|
|
1386
|
+
pos++;
|
|
1387
|
+
while (pos < len) {
|
|
1388
|
+
const cc = input.charCodeAt(pos);
|
|
1389
|
+
if (cc === 10 || cc === 13) break;
|
|
1390
|
+
pos++;
|
|
1391
|
+
}
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
break;
|
|
1395
|
+
}
|
|
1396
|
+
return pos;
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
function splitTopLevelAlts(body) {
|
|
1400
|
+
const arms = [];
|
|
1401
|
+
let inClass = false;
|
|
1402
|
+
let start = 0;
|
|
1403
|
+
for (let i = 0; i < body.length; i++) {
|
|
1404
|
+
const c = body[i];
|
|
1405
|
+
if (c === "\\") {
|
|
1406
|
+
i++;
|
|
1407
|
+
continue;
|
|
1408
|
+
}
|
|
1409
|
+
if (inClass) {
|
|
1410
|
+
if (c === "]") inClass = false;
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
if (c === "[") inClass = true;
|
|
1414
|
+
else if (c === "(") return null;
|
|
1415
|
+
else if (c === "|") {
|
|
1416
|
+
arms.push(body.slice(start, i));
|
|
1417
|
+
start = i + 1;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
arms.push(body.slice(start));
|
|
1421
|
+
return arms;
|
|
1422
|
+
}
|
|
1423
|
+
function altStarSource(source) {
|
|
1424
|
+
const m = /^\(\?:(.*)\)[*+]$/.exec(source);
|
|
1425
|
+
if (!m) return null;
|
|
1426
|
+
const armSrcs = splitTopLevelAlts(m[1]);
|
|
1427
|
+
if (!armSrcs || armSrcs.length < 2) return null;
|
|
1428
|
+
const arms = [];
|
|
1429
|
+
for (const src of armSrcs) {
|
|
1430
|
+
const arm = classifyTriviaArm(src);
|
|
1431
|
+
if (!arm) return null;
|
|
1432
|
+
arms.push(arm);
|
|
1433
|
+
}
|
|
1434
|
+
const ranges = [];
|
|
1435
|
+
const commentCodes = [];
|
|
1436
|
+
for (const arm of arms) {
|
|
1437
|
+
if (arm.kind === "class") ranges.push(...arm.ranges);
|
|
1438
|
+
else commentCodes.push(arm.code);
|
|
1439
|
+
}
|
|
1440
|
+
if (commentCodes.some((code) => inRanges2(code, ranges))) {
|
|
1441
|
+
return loopScanner(arms.map(armScanner));
|
|
1442
|
+
}
|
|
1443
|
+
if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
|
|
1444
|
+
return fusedTriviaScanner(ranges, commentCodes);
|
|
1445
|
+
}
|
|
1335
1446
|
function blockCommentSource(source) {
|
|
1336
1447
|
return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
|
|
1337
1448
|
}
|
|
@@ -1685,6 +1796,7 @@ function markUnusedValues(root) {
|
|
|
1685
1796
|
}
|
|
1686
1797
|
|
|
1687
1798
|
// src/combinators/parser.ts
|
|
1799
|
+
var RULE_ORDER = "__parsemanRuleOrder";
|
|
1688
1800
|
function tagRule(r, key) {
|
|
1689
1801
|
;
|
|
1690
1802
|
r._ruleName = key;
|
|
@@ -1729,6 +1841,11 @@ function rules(a, b) {
|
|
|
1729
1841
|
for (const key of Object.keys(definitions)) {
|
|
1730
1842
|
markUnusedValues(cache[key]);
|
|
1731
1843
|
}
|
|
1844
|
+
Object.defineProperty(cache, RULE_ORDER, {
|
|
1845
|
+
value: Object.keys(definitions),
|
|
1846
|
+
enumerable: false,
|
|
1847
|
+
configurable: true
|
|
1848
|
+
});
|
|
1732
1849
|
return cache;
|
|
1733
1850
|
}
|
|
1734
1851
|
|
|
@@ -6276,17 +6393,159 @@ function manyRecover(item, until) {
|
|
|
6276
6393
|
return many(tolerantElement(item, until, until));
|
|
6277
6394
|
}
|
|
6278
6395
|
|
|
6396
|
+
// src/cst/relative-spans.ts
|
|
6397
|
+
function relativize(node2, parentBase = 0) {
|
|
6398
|
+
return {
|
|
6399
|
+
start: node2.start - parentBase,
|
|
6400
|
+
end: node2.end - parentBase,
|
|
6401
|
+
children: node2.children.map((c) => relativize(c, node2.start))
|
|
6402
|
+
};
|
|
6403
|
+
}
|
|
6404
|
+
function absolutize(node2, parentBase = 0) {
|
|
6405
|
+
const start = parentBase + node2.start;
|
|
6406
|
+
return {
|
|
6407
|
+
start,
|
|
6408
|
+
end: parentBase + node2.end,
|
|
6409
|
+
children: node2.children.map((c) => absolutize(c, start))
|
|
6410
|
+
};
|
|
6411
|
+
}
|
|
6412
|
+
function absoluteSpanAt(root, path) {
|
|
6413
|
+
let base = 0;
|
|
6414
|
+
let node2 = root;
|
|
6415
|
+
for (const idx of path) {
|
|
6416
|
+
base += node2.start;
|
|
6417
|
+
const child = node2.children[idx];
|
|
6418
|
+
if (!child) throw new RangeError(`absoluteSpanAt: no child ${idx} on path ${path.join(".")}`);
|
|
6419
|
+
node2 = child;
|
|
6420
|
+
}
|
|
6421
|
+
return { start: base + node2.start, end: base + node2.end };
|
|
6422
|
+
}
|
|
6423
|
+
function relativizeCST(node2, parentBase = 0) {
|
|
6424
|
+
const span = { start: node2.span.start - parentBase, end: node2.span.end - parentBase };
|
|
6425
|
+
if (node2.children) {
|
|
6426
|
+
const base = node2.span.start;
|
|
6427
|
+
return { ...node2, span, children: node2.children.map((c) => relativizeCST(c, base)) };
|
|
6428
|
+
}
|
|
6429
|
+
return { ...node2, span };
|
|
6430
|
+
}
|
|
6431
|
+
function absolutizeCST(node2, parentBase = 0) {
|
|
6432
|
+
const start = parentBase + node2.span.start;
|
|
6433
|
+
const span = { start, end: parentBase + node2.span.end };
|
|
6434
|
+
if (node2.children) {
|
|
6435
|
+
return { ...node2, span, children: node2.children.map((c) => absolutizeCST(c, start)) };
|
|
6436
|
+
}
|
|
6437
|
+
return { ...node2, span };
|
|
6438
|
+
}
|
|
6439
|
+
function absoluteSpanCST(root, path) {
|
|
6440
|
+
let base = 0;
|
|
6441
|
+
let node2 = root;
|
|
6442
|
+
for (const idx of path) {
|
|
6443
|
+
base += node2.span.start;
|
|
6444
|
+
const child = node2.children?.[idx];
|
|
6445
|
+
if (!child) throw new RangeError(`absoluteSpanCST: no child ${idx} on path ${path.join(".")}`);
|
|
6446
|
+
node2 = child;
|
|
6447
|
+
}
|
|
6448
|
+
return { start: base + node2.span.start, end: base + node2.span.end };
|
|
6449
|
+
}
|
|
6450
|
+
function shiftAbsolute(node2, at, delta) {
|
|
6451
|
+
const s = node2.start >= at ? node2.start + delta : node2.start;
|
|
6452
|
+
const e = node2.end >= at ? node2.end + delta : node2.end;
|
|
6453
|
+
return {
|
|
6454
|
+
start: s,
|
|
6455
|
+
end: e,
|
|
6456
|
+
children: node2.children.map((c) => shiftAbsolute(c, at, delta))
|
|
6457
|
+
};
|
|
6458
|
+
}
|
|
6459
|
+
function applyEdit(root, at, delta, stats) {
|
|
6460
|
+
return editNode(root, at, delta, 0, 0, stats);
|
|
6461
|
+
}
|
|
6462
|
+
function editNode(node2, at, delta, parentBase, parentShift, stats) {
|
|
6463
|
+
const absStart = parentBase + node2.start;
|
|
6464
|
+
const absEnd = parentBase + node2.end;
|
|
6465
|
+
if (absEnd < at) return node2;
|
|
6466
|
+
const startShifts = absStart >= at;
|
|
6467
|
+
if (startShifts && parentShift === delta) return node2;
|
|
6468
|
+
if (stats) stats.allocated++;
|
|
6469
|
+
const newAbsStart = startShifts ? absStart + delta : absStart;
|
|
6470
|
+
const newAbsEnd = absEnd + delta;
|
|
6471
|
+
const newParentBase = parentBase + parentShift;
|
|
6472
|
+
const newStart = newAbsStart - newParentBase;
|
|
6473
|
+
const newEnd = newAbsEnd - newParentBase;
|
|
6474
|
+
const ownShift = newAbsStart - absStart;
|
|
6475
|
+
let changed = false;
|
|
6476
|
+
const newChildren = [];
|
|
6477
|
+
for (const c of node2.children) {
|
|
6478
|
+
const nc = editNode(c, at, delta, absStart, ownShift, stats);
|
|
6479
|
+
if (nc !== c) changed = true;
|
|
6480
|
+
newChildren.push(nc);
|
|
6481
|
+
}
|
|
6482
|
+
return {
|
|
6483
|
+
start: newStart,
|
|
6484
|
+
end: newEnd,
|
|
6485
|
+
children: changed ? newChildren : node2.children
|
|
6486
|
+
};
|
|
6487
|
+
}
|
|
6488
|
+
|
|
6279
6489
|
// src/functional/doc.ts
|
|
6490
|
+
function asRuleFn(entry) {
|
|
6491
|
+
return typeof entry === "function" ? entry : (input, pos, ctx) => entry.parse(input, pos, ctx);
|
|
6492
|
+
}
|
|
6493
|
+
function defOf(entry) {
|
|
6494
|
+
return typeof entry === "object" && entry !== null && "_def" in entry ? entry._def : void 0;
|
|
6495
|
+
}
|
|
6496
|
+
function producesRepetition(def, depth = 0) {
|
|
6497
|
+
if (!def || depth > 24) return false;
|
|
6498
|
+
const d = def;
|
|
6499
|
+
switch (d.tag) {
|
|
6500
|
+
case "sepBy":
|
|
6501
|
+
case "many":
|
|
6502
|
+
case "oneOrMore":
|
|
6503
|
+
return true;
|
|
6504
|
+
// Rule entries and `ref`s wrap their body in a `lazy` thunk — resolve it. The
|
|
6505
|
+
// `depth` cap bounds any self-referential cycle (a repetition, if present, is
|
|
6506
|
+
// found shallowly before recursion goes deep).
|
|
6507
|
+
case "lazy": {
|
|
6508
|
+
const thunk = d.thunk;
|
|
6509
|
+
let inner;
|
|
6510
|
+
try {
|
|
6511
|
+
inner = typeof thunk === "function" ? thunk() : void 0;
|
|
6512
|
+
} catch {
|
|
6513
|
+
return false;
|
|
6514
|
+
}
|
|
6515
|
+
return producesRepetition(inner?._def, depth + 1);
|
|
6516
|
+
}
|
|
6517
|
+
// Transparent wrappers — look through to the inner parser.
|
|
6518
|
+
case "node":
|
|
6519
|
+
case "transform":
|
|
6520
|
+
case "token":
|
|
6521
|
+
case "label":
|
|
6522
|
+
case "field":
|
|
6523
|
+
case "expect":
|
|
6524
|
+
case "withCtx":
|
|
6525
|
+
case "grammar":
|
|
6526
|
+
case "optional":
|
|
6527
|
+
return producesRepetition(defOf(d.parser), depth + 1);
|
|
6528
|
+
case "skip":
|
|
6529
|
+
return producesRepetition(defOf(d.main), depth + 1);
|
|
6530
|
+
// A bracketed/anchored list is a sequence whose element run is a repetition.
|
|
6531
|
+
case "sequence":
|
|
6532
|
+
return d.parsers.some((p) => producesRepetition(defOf(p), depth + 1));
|
|
6533
|
+
default:
|
|
6534
|
+
return false;
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
6280
6537
|
var REENTRY_MAX_SPAN_FRACTION = 0.5;
|
|
6281
6538
|
function isNode(x) {
|
|
6282
6539
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
6283
6540
|
}
|
|
6284
|
-
function findContaining(node2, pos, path = []) {
|
|
6541
|
+
function findContaining(node2, nodeAbsStart, pos, path = []) {
|
|
6285
6542
|
for (let i = 0; i < node2.children.length; i++) {
|
|
6286
6543
|
const child = node2.children[i];
|
|
6287
6544
|
if (!isNode(child)) continue;
|
|
6288
|
-
|
|
6289
|
-
|
|
6545
|
+
const childAbsStart = nodeAbsStart + child.span.start;
|
|
6546
|
+
const childAbsEnd = nodeAbsStart + child.span.end;
|
|
6547
|
+
if (childAbsStart <= pos && pos < childAbsEnd) {
|
|
6548
|
+
const inner = findContaining(child, childAbsStart, pos, [...path, i]);
|
|
6290
6549
|
return inner ?? { node: child, path: [...path, i] };
|
|
6291
6550
|
}
|
|
6292
6551
|
}
|
|
@@ -6313,24 +6572,20 @@ function replaceAtPath(rebuild, root, path, newNode) {
|
|
|
6313
6572
|
function defaultRebuild(node2, children) {
|
|
6314
6573
|
return { ...node2, children };
|
|
6315
6574
|
}
|
|
6316
|
-
function
|
|
6575
|
+
function shiftRelStart(child, delta) {
|
|
6317
6576
|
const c = child;
|
|
6318
|
-
|
|
6319
|
-
if (Array.isArray(c.children)) {
|
|
6320
|
-
return { ...c, span, children: c.children.map((g) => shiftSpans(g, delta)) };
|
|
6321
|
-
}
|
|
6322
|
-
return { ...c, span };
|
|
6577
|
+
return { ...c, span: { start: c.span.start + delta, end: c.span.end + delta } };
|
|
6323
6578
|
}
|
|
6324
|
-
function
|
|
6579
|
+
function graftRelative(root, path, newNode, delta) {
|
|
6325
6580
|
if (path.length === 0) return newNode;
|
|
6326
6581
|
const [idx, ...rest] = path;
|
|
6327
6582
|
const oldChildren = root.children;
|
|
6328
6583
|
const newChildren = oldChildren.map((child, i) => {
|
|
6329
6584
|
if (i < idx) return child;
|
|
6330
6585
|
if (i === idx) {
|
|
6331
|
-
return rest.length === 0 ? newNode :
|
|
6586
|
+
return rest.length === 0 ? newNode : graftRelative(child, rest, newNode, delta);
|
|
6332
6587
|
}
|
|
6333
|
-
return
|
|
6588
|
+
return shiftRelStart(child, delta);
|
|
6334
6589
|
});
|
|
6335
6590
|
return {
|
|
6336
6591
|
...root,
|
|
@@ -6338,6 +6593,96 @@ function graftAndShift(root, path, newNode, delta) {
|
|
|
6338
6593
|
children: newChildren
|
|
6339
6594
|
};
|
|
6340
6595
|
}
|
|
6596
|
+
function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
|
|
6597
|
+
const ctx = { trackLines: false, state: null, build };
|
|
6598
|
+
const out = [];
|
|
6599
|
+
let pos = reStart;
|
|
6600
|
+
let guard2 = 0;
|
|
6601
|
+
while (pos < reEnd) {
|
|
6602
|
+
if (++guard2 > reEnd - reStart + 2) return null;
|
|
6603
|
+
if (separator && out.length > 0 && out[out.length - 1]._tag === "node") {
|
|
6604
|
+
if (pos + separator.length > reEnd || !input.startsWith(separator, pos)) return null;
|
|
6605
|
+
out.push({ _tag: "leaf", value: separator, span: { start: pos - cStart, end: pos + separator.length - cStart } });
|
|
6606
|
+
pos += separator.length;
|
|
6607
|
+
continue;
|
|
6608
|
+
}
|
|
6609
|
+
let r;
|
|
6610
|
+
try {
|
|
6611
|
+
r = ruleFn(input, pos, ctx);
|
|
6612
|
+
} catch {
|
|
6613
|
+
return null;
|
|
6614
|
+
}
|
|
6615
|
+
if (!r.ok || r.span.end <= pos || r.span.end > reEnd) return null;
|
|
6616
|
+
out.push(relativizeCST(r.value, cStart));
|
|
6617
|
+
pos = r.span.end;
|
|
6618
|
+
}
|
|
6619
|
+
return pos === reEnd ? out : null;
|
|
6620
|
+
}
|
|
6621
|
+
function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build) {
|
|
6622
|
+
const kids = C.children;
|
|
6623
|
+
if (kids.length === 0) return null;
|
|
6624
|
+
let h = 0;
|
|
6625
|
+
while (h < kids.length && cStart + kids[h].span.end <= from) h++;
|
|
6626
|
+
while (h > 0 && kids[h - 1]._tag === "node") h--;
|
|
6627
|
+
let t = kids.length;
|
|
6628
|
+
while (t > h && cStart + kids[t - 1].span.start >= to) t--;
|
|
6629
|
+
while (t < kids.length && kids[t]._tag !== "node") t++;
|
|
6630
|
+
if (t >= kids.length) return null;
|
|
6631
|
+
for (let i = t; i < kids.length; i++) {
|
|
6632
|
+
if (kids[i]._tag === "node" && kids[i].state != null) return null;
|
|
6633
|
+
}
|
|
6634
|
+
let elemType;
|
|
6635
|
+
let elemCount = 0;
|
|
6636
|
+
for (const k of kids) {
|
|
6637
|
+
if (k._tag !== "node") continue;
|
|
6638
|
+
elemCount++;
|
|
6639
|
+
if (elemType === void 0) elemType = k.type;
|
|
6640
|
+
else if (k.type !== elemType) return null;
|
|
6641
|
+
}
|
|
6642
|
+
if (elemType === void 0 || elemCount < 2) return null;
|
|
6643
|
+
const ruleFn = registry[elemType];
|
|
6644
|
+
if (!ruleFn) return null;
|
|
6645
|
+
let separator = null;
|
|
6646
|
+
for (let i = 1; i < kids.length - 1; i++) {
|
|
6647
|
+
if (kids[i - 1]._tag === "node" && kids[i + 1]._tag === "node") {
|
|
6648
|
+
if (kids[i]._tag !== "leaf") return null;
|
|
6649
|
+
const sep = kids[i].value ?? null;
|
|
6650
|
+
if (separator === null) separator = sep;
|
|
6651
|
+
else if (sep !== separator) return null;
|
|
6652
|
+
}
|
|
6653
|
+
}
|
|
6654
|
+
if (separator === null) return null;
|
|
6655
|
+
const first = kids[0];
|
|
6656
|
+
const last = kids[kids.length - 1];
|
|
6657
|
+
if (first._tag === "leaf" && first.value !== separator && from < cStart + first.span.end) return null;
|
|
6658
|
+
if (last._tag === "leaf" && last.value !== separator && to > cStart + last.span.start) return null;
|
|
6659
|
+
const reStart = h > 0 ? cStart + kids[h - 1].span.end : cStart;
|
|
6660
|
+
const reEnd = cStart + kids[t].span.start + delta;
|
|
6661
|
+
if (reEnd < reStart) return null;
|
|
6662
|
+
const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build);
|
|
6663
|
+
if (!middle) return null;
|
|
6664
|
+
if (middle.length > 0) {
|
|
6665
|
+
const last2 = middle[middle.length - 1];
|
|
6666
|
+
if (!(last2._tag === "leaf" && last2.value === separator)) return null;
|
|
6667
|
+
}
|
|
6668
|
+
if (reEnd < newInput.length) {
|
|
6669
|
+
for (const sentinel of [" ", "\uFFFF"]) {
|
|
6670
|
+
if (newInput[reEnd] === sentinel) continue;
|
|
6671
|
+
const probed = newInput.slice(0, reEnd) + sentinel.repeat(newInput.length - reEnd);
|
|
6672
|
+
const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build);
|
|
6673
|
+
if (!probe || probe.length !== middle.length) return null;
|
|
6674
|
+
for (let i = 0; i < middle.length; i++) if (!structurallyEqual(probe[i], middle[i])) return null;
|
|
6675
|
+
}
|
|
6676
|
+
}
|
|
6677
|
+
const head = kids.slice(0, h);
|
|
6678
|
+
const tail = kids.slice(t).map((k) => shiftRelStart(k, delta));
|
|
6679
|
+
const newC = {
|
|
6680
|
+
...C,
|
|
6681
|
+
span: { start: C.span.start, end: C.span.end + delta },
|
|
6682
|
+
children: [...head, ...middle, ...tail]
|
|
6683
|
+
};
|
|
6684
|
+
return graftRelative(root, path, newC, delta);
|
|
6685
|
+
}
|
|
6341
6686
|
function structurallyEqual(a, b) {
|
|
6342
6687
|
if (a === b) return true;
|
|
6343
6688
|
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
@@ -6381,27 +6726,68 @@ function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produce
|
|
|
6381
6726
|
}
|
|
6382
6727
|
var ParseDocImpl = class _ParseDocImpl {
|
|
6383
6728
|
_registry;
|
|
6729
|
+
/** Registry entries normalized to callable parse functions (memoized once). */
|
|
6730
|
+
_fns;
|
|
6731
|
+
/** Rule names the grammar proves are genuine repetitions — the ONLY splice-safe types. */
|
|
6732
|
+
_reps;
|
|
6384
6733
|
_rootRule;
|
|
6385
6734
|
_opts;
|
|
6386
|
-
|
|
6735
|
+
/**
|
|
6736
|
+
* The tree, held in whichever coordinate system it arrived in. A fresh parse
|
|
6737
|
+
* arrives ABSOLUTE (`_abs` set); an incremental graft arrives PARENT-RELATIVE
|
|
6738
|
+
* (`_rel` set). The public `tree` is always the RELATIVE form (so untouched
|
|
6739
|
+
* subtrees stay shared by identity across edits and reads are O(1)); it's
|
|
6740
|
+
* materialized from `_abs` once, on demand, and memoized. `undefined` = not yet
|
|
6741
|
+
* computed; `null` = failed parse. A fresh parse that's never edited nor
|
|
6742
|
+
* tree-read pays no conversion — important for the full-reparse fallback path.
|
|
6743
|
+
*/
|
|
6744
|
+
_abs;
|
|
6745
|
+
_rel;
|
|
6387
6746
|
errors;
|
|
6388
6747
|
input;
|
|
6389
|
-
constructor(registry, rootRule, opts,
|
|
6748
|
+
constructor(registry, rootRule, opts, trees, errors, input) {
|
|
6390
6749
|
this._registry = registry;
|
|
6750
|
+
this._fns = {};
|
|
6751
|
+
this._reps = /* @__PURE__ */ new Set();
|
|
6752
|
+
for (const [name, entry] of Object.entries(registry)) {
|
|
6753
|
+
this._fns[name] = asRuleFn(entry);
|
|
6754
|
+
if (producesRepetition(defOf(entry))) this._reps.add(name);
|
|
6755
|
+
}
|
|
6391
6756
|
this._rootRule = rootRule;
|
|
6392
6757
|
this._opts = opts;
|
|
6393
|
-
this.
|
|
6758
|
+
this._abs = trees.abs;
|
|
6759
|
+
this._rel = trees.rel;
|
|
6394
6760
|
this.errors = errors;
|
|
6395
6761
|
this.input = input;
|
|
6396
6762
|
}
|
|
6763
|
+
/**
|
|
6764
|
+
* The parse tree with PARENT-RELATIVE spans — a node's `span` is relative to
|
|
6765
|
+
* its parent's start (root base 0). This is the shareable representation:
|
|
6766
|
+
* untouched subtrees keep the same identity across `.edit()`s, and reading the
|
|
6767
|
+
* tree after a length-changing edit is O(1) (no offset rewrite). For absolute
|
|
6768
|
+
* positions use the O(depth) cursor `spanAt(path)`, or `absolutizeCST(tree)` to
|
|
6769
|
+
* materialize the whole absolute tree. (A fresh non-incremental `node().parse()`
|
|
6770
|
+
* result is unchanged — still absolute.)
|
|
6771
|
+
*/
|
|
6772
|
+
get tree() {
|
|
6773
|
+
if (this._rel === void 0) {
|
|
6774
|
+
this._rel = this._abs ? relativizeCST(this._abs, 0) : null;
|
|
6775
|
+
}
|
|
6776
|
+
return this._rel;
|
|
6777
|
+
}
|
|
6778
|
+
spanAt(path) {
|
|
6779
|
+
const rel = this.tree;
|
|
6780
|
+
if (!rel) throw new Error("spanAt on a failed parse (tree is null)");
|
|
6781
|
+
return absoluteSpanCST(rel, path);
|
|
6782
|
+
}
|
|
6397
6783
|
edit(from, to, replacement) {
|
|
6398
6784
|
const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
|
|
6399
6785
|
const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
|
|
6400
|
-
|
|
6786
|
+
const root = this.tree;
|
|
6787
|
+
if (!root) return reparse();
|
|
6401
6788
|
const delta = replacement.length - (to - from);
|
|
6402
|
-
const found = findContaining(
|
|
6403
|
-
|
|
6404
|
-
const ancestors = ancestorsAt(this.tree, found.path);
|
|
6789
|
+
const found = findContaining(root, 0, from) ?? { node: root, path: [] };
|
|
6790
|
+
const ancestors = ancestorsAt(root, found.path);
|
|
6405
6791
|
const candidates = [found];
|
|
6406
6792
|
const pathCopy = [...found.path];
|
|
6407
6793
|
for (let i = ancestors.length - 2; i >= 0; i--) {
|
|
@@ -6410,37 +6796,50 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
6410
6796
|
}
|
|
6411
6797
|
const rebuild = this._opts.rebuild ?? defaultRebuild;
|
|
6412
6798
|
for (const { node: node2, path } of candidates) {
|
|
6413
|
-
|
|
6414
|
-
|
|
6799
|
+
const { start: absStart, end: absEnd } = absoluteSpanCST(root, path);
|
|
6800
|
+
if (absEnd - absStart > this.input.length * REENTRY_MAX_SPAN_FRACTION) break;
|
|
6801
|
+
const ruleFn = this._fns[node2.type];
|
|
6415
6802
|
if (!ruleFn) continue;
|
|
6416
|
-
if (!(
|
|
6803
|
+
if (!(absStart <= from && to <= absEnd)) continue;
|
|
6417
6804
|
const ctx = { trackLines: false, state: node2.state, build: this._opts.build };
|
|
6418
|
-
const r = ruleFn(newInput,
|
|
6805
|
+
const r = ruleFn(newInput, absStart, ctx);
|
|
6419
6806
|
if (!r.ok) continue;
|
|
6420
|
-
if (r.span.end !==
|
|
6421
|
-
if (!boundaryIsSafe(ruleFn, newInput,
|
|
6807
|
+
if (r.span.end !== absEnd + delta) continue;
|
|
6808
|
+
if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r)) {
|
|
6422
6809
|
continue;
|
|
6423
6810
|
}
|
|
6811
|
+
const parentBase = absStart - node2.span.start;
|
|
6812
|
+
const newRel = relativizeCST(r.value, parentBase);
|
|
6424
6813
|
if (delta === 0) {
|
|
6425
|
-
const newTree2 = replaceAtPath(rebuild,
|
|
6426
|
-
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree2, [], newInput);
|
|
6814
|
+
const newTree2 = replaceAtPath(rebuild, root, path, newRel);
|
|
6815
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree2 }, [], newInput);
|
|
6427
6816
|
}
|
|
6428
6817
|
if (this._opts.rebuild) return reparse();
|
|
6429
|
-
const newTree =
|
|
6430
|
-
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
6818
|
+
const newTree = graftRelative(root, path, newRel, delta);
|
|
6819
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, [], newInput);
|
|
6820
|
+
}
|
|
6821
|
+
if (this._opts.structuralReuse && delta !== 0 && !this._opts.rebuild) {
|
|
6822
|
+
const spliceCandidates = [...candidates, { node: root, path: [] }];
|
|
6823
|
+
for (const { node: node2, path } of spliceCandidates) {
|
|
6824
|
+
if (!this._reps.has(node2.type)) continue;
|
|
6825
|
+
const { start: cStart, end: cEnd } = absoluteSpanCST(root, path);
|
|
6826
|
+
if (!(cStart <= from && to <= cEnd)) continue;
|
|
6827
|
+
const spliced = tryListSplice(root, path, node2, cStart, newInput, from, to, delta, this._fns, this._opts.build);
|
|
6828
|
+
if (spliced) return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: spliced }, [], newInput);
|
|
6829
|
+
}
|
|
6431
6830
|
}
|
|
6432
6831
|
return reparse();
|
|
6433
6832
|
}
|
|
6434
6833
|
};
|
|
6435
6834
|
function parseDoc(registry, rootRule, input, opts = {}) {
|
|
6436
|
-
const
|
|
6437
|
-
if (!
|
|
6835
|
+
const entry = registry[rootRule];
|
|
6836
|
+
if (!entry) throw new Error(`No rule '${rootRule}' in registry`);
|
|
6438
6837
|
const ctx = { trackLines: false, state: opts.state, build: opts.build };
|
|
6439
|
-
const r =
|
|
6838
|
+
const r = asRuleFn(entry)(input, 0, ctx);
|
|
6440
6839
|
if (r.ok) {
|
|
6441
|
-
return new ParseDocImpl(registry, rootRule, opts, r.value, [], input);
|
|
6840
|
+
return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, [], input);
|
|
6442
6841
|
}
|
|
6443
|
-
return new ParseDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
6842
|
+
return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
6444
6843
|
}
|
|
6445
6844
|
|
|
6446
6845
|
// src/functional/run.ts
|
|
@@ -6809,78 +7208,14 @@ function buildOffsetIndex(root, input = "") {
|
|
|
6809
7208
|
return new OffsetIndex(collectLeafSlots(root), 0, input.length);
|
|
6810
7209
|
}
|
|
6811
7210
|
|
|
6812
|
-
// src/cst/relative-spans.ts
|
|
6813
|
-
function relativize(node2, parentBase = 0) {
|
|
6814
|
-
return {
|
|
6815
|
-
start: node2.start - parentBase,
|
|
6816
|
-
end: node2.end - parentBase,
|
|
6817
|
-
children: node2.children.map((c) => relativize(c, node2.start))
|
|
6818
|
-
};
|
|
6819
|
-
}
|
|
6820
|
-
function absolutize(node2, parentBase = 0) {
|
|
6821
|
-
const start = parentBase + node2.start;
|
|
6822
|
-
return {
|
|
6823
|
-
start,
|
|
6824
|
-
end: parentBase + node2.end,
|
|
6825
|
-
children: node2.children.map((c) => absolutize(c, start))
|
|
6826
|
-
};
|
|
6827
|
-
}
|
|
6828
|
-
function absoluteSpanAt(root, path) {
|
|
6829
|
-
let base = 0;
|
|
6830
|
-
let node2 = root;
|
|
6831
|
-
for (const idx of path) {
|
|
6832
|
-
base += node2.start;
|
|
6833
|
-
const child = node2.children[idx];
|
|
6834
|
-
if (!child) throw new RangeError(`absoluteSpanAt: no child ${idx} on path ${path.join(".")}`);
|
|
6835
|
-
node2 = child;
|
|
6836
|
-
}
|
|
6837
|
-
return { start: base + node2.start, end: base + node2.end };
|
|
6838
|
-
}
|
|
6839
|
-
function shiftAbsolute(node2, at, delta) {
|
|
6840
|
-
const s = node2.start >= at ? node2.start + delta : node2.start;
|
|
6841
|
-
const e = node2.end >= at ? node2.end + delta : node2.end;
|
|
6842
|
-
return {
|
|
6843
|
-
start: s,
|
|
6844
|
-
end: e,
|
|
6845
|
-
children: node2.children.map((c) => shiftAbsolute(c, at, delta))
|
|
6846
|
-
};
|
|
6847
|
-
}
|
|
6848
|
-
function applyEdit(root, at, delta, stats) {
|
|
6849
|
-
return editNode(root, at, delta, 0, 0, stats);
|
|
6850
|
-
}
|
|
6851
|
-
function editNode(node2, at, delta, parentBase, parentShift, stats) {
|
|
6852
|
-
const absStart = parentBase + node2.start;
|
|
6853
|
-
const absEnd = parentBase + node2.end;
|
|
6854
|
-
if (absEnd < at) return node2;
|
|
6855
|
-
const startShifts = absStart >= at;
|
|
6856
|
-
if (startShifts && parentShift === delta) return node2;
|
|
6857
|
-
if (stats) stats.allocated++;
|
|
6858
|
-
const newAbsStart = startShifts ? absStart + delta : absStart;
|
|
6859
|
-
const newAbsEnd = absEnd + delta;
|
|
6860
|
-
const newParentBase = parentBase + parentShift;
|
|
6861
|
-
const newStart = newAbsStart - newParentBase;
|
|
6862
|
-
const newEnd = newAbsEnd - newParentBase;
|
|
6863
|
-
const ownShift = newAbsStart - absStart;
|
|
6864
|
-
let changed = false;
|
|
6865
|
-
const newChildren = [];
|
|
6866
|
-
for (const c of node2.children) {
|
|
6867
|
-
const nc = editNode(c, at, delta, absStart, ownShift, stats);
|
|
6868
|
-
if (nc !== c) changed = true;
|
|
6869
|
-
newChildren.push(nc);
|
|
6870
|
-
}
|
|
6871
|
-
return {
|
|
6872
|
-
start: newStart,
|
|
6873
|
-
end: newEnd,
|
|
6874
|
-
children: changed ? newChildren : node2.children
|
|
6875
|
-
};
|
|
6876
|
-
}
|
|
6877
|
-
|
|
6878
7211
|
// src/index.ts
|
|
6879
7212
|
registerRegexAnalyzer(firstSetFromRegex);
|
|
6880
7213
|
export {
|
|
6881
7214
|
OffsetIndex,
|
|
6882
7215
|
absoluteSpanAt,
|
|
7216
|
+
absoluteSpanCST,
|
|
6883
7217
|
absolutize,
|
|
7218
|
+
absolutizeCST,
|
|
6884
7219
|
annotateSpan,
|
|
6885
7220
|
applyEdit,
|
|
6886
7221
|
balanced,
|
|
@@ -6925,6 +7260,7 @@ export {
|
|
|
6925
7260
|
ref,
|
|
6926
7261
|
regex,
|
|
6927
7262
|
relativize,
|
|
7263
|
+
relativizeCST,
|
|
6928
7264
|
rules,
|
|
6929
7265
|
run,
|
|
6930
7266
|
scanTo,
|