parseman 0.25.0 → 0.26.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/completions.d.ts +14 -4
- package/dist/combinators/completions.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts +2 -0
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts +5 -5
- package/dist/combinators/not.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts +3 -2
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +1 -0
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +9 -4
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/scannable-run.d.ts +2 -2
- package/dist/compiler/scannable-run.d.ts.map +1 -1
- package/dist/cst/capture-buffer.d.ts +1 -0
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/cst/types.d.ts +7 -7
- package/dist/cst/types.d.ts.map +1 -1
- package/dist/functional/doc.d.ts +37 -2
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/functional/run.d.ts +12 -1
- package/dist/functional/run.d.ts.map +1 -1
- package/dist/index.cjs +505 -195
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +505 -195
- package/dist/index.js.map +4 -4
- package/dist/language-service/index.cjs +776 -0
- package/dist/language-service/index.cjs.map +7 -0
- package/dist/language-service/index.d.ts +94 -0
- package/dist/language-service/index.d.ts.map +1 -0
- package/dist/language-service/index.js +753 -0
- package/dist/language-service/index.js.map +7 -0
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +568 -129
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts +11 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +568 -129
- package/dist/plugin/index.js.map +4 -4
- package/dist/recovery/scan.d.ts +54 -0
- package/dist/recovery/scan.d.ts.map +1 -0
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.js.map +1 -1
- package/dist/types.d.ts +60 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -1
- package/dist/combinators/recover-list.d.ts +0 -41
- package/dist/combinators/recover-list.d.ts.map +0 -1
- package/dist/combinators/recover.d.ts +0 -20
- package/dist/combinators/recover.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -253,7 +253,13 @@ function saveCstMark(ctx) {
|
|
|
253
253
|
raw: cstRawLen(ctx),
|
|
254
254
|
tlog: cstTlLen(ctx),
|
|
255
255
|
leaves: cstLeavesLen(ctx),
|
|
256
|
-
fields: ctx._fields?.length ?? 0
|
|
256
|
+
fields: ctx._fields?.length ?? 0,
|
|
257
|
+
// Speculative rollback must truncate the flat recovery-error sink in lockstep
|
|
258
|
+
// with the CST leaves: a branch that recovered (pushing a ParseError to
|
|
259
|
+
// ctx._errors AND embedding it as a CST child via captureError) then failed
|
|
260
|
+
// must remove BOTH, or the flat error survives as a GHOST that a tree walk
|
|
261
|
+
// can't see (flat run().errors would disagree with the embedded set).
|
|
262
|
+
errors: ctx._errors?.length ?? 0
|
|
257
263
|
};
|
|
258
264
|
}
|
|
259
265
|
function rollbackBufList(b, keyMulti, keySingle, len) {
|
|
@@ -269,6 +275,7 @@ function rollbackBufList(b, keyMulti, keySingle, len) {
|
|
|
269
275
|
if (len === 0) b[keySingle] = void 0;
|
|
270
276
|
}
|
|
271
277
|
function rollbackCstCapture(ctx, mark) {
|
|
278
|
+
if (ctx._errors && mark.errors !== void 0) ctx._errors.length = mark.errors;
|
|
272
279
|
const b = ctx._cstBuf;
|
|
273
280
|
if (b) {
|
|
274
281
|
rollbackBufList(b, "raw", "rawSingle", mark.raw);
|
|
@@ -1215,10 +1222,10 @@ function needsDeferredTriviaCommit(ctx) {
|
|
|
1215
1222
|
}
|
|
1216
1223
|
function saveTriviaMark(ctx) {
|
|
1217
1224
|
const m = saveCstMark(ctx);
|
|
1218
|
-
return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
|
|
1225
|
+
return { raw: m.raw, tlog: m.tlog, leaves: m.leaves, fields: m.fields, errors: m.errors, log: ctx._triviaLog ? ctx._triviaLog.length : 0 };
|
|
1219
1226
|
}
|
|
1220
1227
|
function rollbackTrivia(ctx, mark) {
|
|
1221
|
-
rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields });
|
|
1228
|
+
rollbackCstCapture(ctx, { raw: mark.raw, tlog: mark.tlog, leaves: mark.leaves, fields: mark.fields, errors: mark.errors });
|
|
1222
1229
|
if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
|
|
1223
1230
|
}
|
|
1224
1231
|
function scanWithLabels(input, cur, ctx) {
|
|
@@ -1452,6 +1459,94 @@ function scanBlockComment(input, cur) {
|
|
|
1452
1459
|
return close === -1 ? cur : close + 2;
|
|
1453
1460
|
}
|
|
1454
1461
|
|
|
1462
|
+
// src/recovery/scan.ts
|
|
1463
|
+
function probeAt(sentinel, input, pos, ctx) {
|
|
1464
|
+
const s = {
|
|
1465
|
+
_probe: ctx._probe,
|
|
1466
|
+
_errors: ctx._errors,
|
|
1467
|
+
_cstLeaves: ctx._cstLeaves,
|
|
1468
|
+
_cstChildren: ctx._cstChildren,
|
|
1469
|
+
_cstRawChildren: ctx._cstRawChildren,
|
|
1470
|
+
_cstTriviaLog: ctx._cstTriviaLog,
|
|
1471
|
+
_triviaLog: ctx._triviaLog,
|
|
1472
|
+
_fields: ctx._fields,
|
|
1473
|
+
_cstBuf: ctx._cstBuf,
|
|
1474
|
+
_tolerant: ctx._tolerant,
|
|
1475
|
+
_sync: ctx._sync
|
|
1476
|
+
};
|
|
1477
|
+
ctx._probe = void 0;
|
|
1478
|
+
ctx._errors = void 0;
|
|
1479
|
+
ctx._cstLeaves = void 0;
|
|
1480
|
+
ctx._cstChildren = void 0;
|
|
1481
|
+
ctx._cstRawChildren = void 0;
|
|
1482
|
+
ctx._cstTriviaLog = void 0;
|
|
1483
|
+
ctx._triviaLog = void 0;
|
|
1484
|
+
ctx._fields = void 0;
|
|
1485
|
+
ctx._cstBuf = void 0;
|
|
1486
|
+
ctx._tolerant = void 0;
|
|
1487
|
+
ctx._sync = void 0;
|
|
1488
|
+
try {
|
|
1489
|
+
return sentinel.parse(input, pos, ctx).ok;
|
|
1490
|
+
} finally {
|
|
1491
|
+
ctx._probe = s._probe;
|
|
1492
|
+
ctx._errors = s._errors;
|
|
1493
|
+
ctx._cstLeaves = s._cstLeaves;
|
|
1494
|
+
ctx._cstChildren = s._cstChildren;
|
|
1495
|
+
ctx._cstRawChildren = s._cstRawChildren;
|
|
1496
|
+
ctx._cstTriviaLog = s._cstTriviaLog;
|
|
1497
|
+
ctx._triviaLog = s._triviaLog;
|
|
1498
|
+
ctx._fields = s._fields;
|
|
1499
|
+
ctx._cstBuf = s._cstBuf;
|
|
1500
|
+
ctx._tolerant = s._tolerant;
|
|
1501
|
+
ctx._sync = s._sync;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
function orSentinel(a, b) {
|
|
1505
|
+
if (b === void 0) return a;
|
|
1506
|
+
return {
|
|
1507
|
+
_tag: "orSentinel",
|
|
1508
|
+
_meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
|
|
1509
|
+
_def: { tag: "unknown" },
|
|
1510
|
+
parse(input, pos, ctx) {
|
|
1511
|
+
const ra = a.parse(input, pos, ctx);
|
|
1512
|
+
if (ra.ok) return ra;
|
|
1513
|
+
return b.parse(input, pos, ctx);
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
function firstSetSentinel(fs) {
|
|
1518
|
+
if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
|
|
1519
|
+
const ranges = fs.ranges;
|
|
1520
|
+
return {
|
|
1521
|
+
_tag: "firstSetSentinel",
|
|
1522
|
+
_meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
|
|
1523
|
+
_def: { tag: "unknown" },
|
|
1524
|
+
parse(input, pos) {
|
|
1525
|
+
const code = pos < input.length ? input.codePointAt(pos) : void 0;
|
|
1526
|
+
if (code !== void 0) {
|
|
1527
|
+
for (const r of ranges) {
|
|
1528
|
+
if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return { ok: false, expected: [], span: { start: pos, end: pos } };
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
function recoverScan(input, from, ctx, sync, expected) {
|
|
1536
|
+
let scanPos = from;
|
|
1537
|
+
while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
|
|
1538
|
+
const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
|
|
1539
|
+
ctx._errors?.push(error);
|
|
1540
|
+
return { error, end: scanPos };
|
|
1541
|
+
}
|
|
1542
|
+
function matchesAt(sentinel, input, pos, ctx) {
|
|
1543
|
+
return probeAt(sentinel, input, pos, ctx);
|
|
1544
|
+
}
|
|
1545
|
+
function captureError(ctx, error) {
|
|
1546
|
+
if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
|
|
1547
|
+
}
|
|
1548
|
+
var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
|
|
1549
|
+
|
|
1455
1550
|
// src/combinators/sequence.ts
|
|
1456
1551
|
function sequence(...parsers) {
|
|
1457
1552
|
const meta = {
|
|
@@ -1462,11 +1557,59 @@ function sequence(...parsers) {
|
|
|
1462
1557
|
isTrivia: false
|
|
1463
1558
|
};
|
|
1464
1559
|
const def = { tag: "sequence", parsers };
|
|
1560
|
+
let followSentinels;
|
|
1561
|
+
function parseTolerant(input, pos, ctx) {
|
|
1562
|
+
followSentinels ??= parsers.map((_, i) => {
|
|
1563
|
+
const fs = parsers.slice(i + 1).reduce(
|
|
1564
|
+
(acc, p) => union(acc, firstSetOf(p)),
|
|
1565
|
+
{ kind: "empty" }
|
|
1566
|
+
);
|
|
1567
|
+
return firstSetSentinel(fs);
|
|
1568
|
+
});
|
|
1569
|
+
const values = def.valueUnused ? void 0 : [];
|
|
1570
|
+
let cur = pos;
|
|
1571
|
+
const inheritedSync = ctx._sync;
|
|
1572
|
+
try {
|
|
1573
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
1574
|
+
ctx._sync = followSentinels[i] ?? inheritedSync;
|
|
1575
|
+
if (ctx.trivia && i > 0) {
|
|
1576
|
+
const mark = saveTriviaMark(ctx);
|
|
1577
|
+
let scanEnd;
|
|
1578
|
+
if (needsDeferredTriviaCommit(ctx)) {
|
|
1579
|
+
const scan = scanTrivia(input, cur, ctx);
|
|
1580
|
+
scan.commit();
|
|
1581
|
+
scanEnd = scan.end;
|
|
1582
|
+
} else {
|
|
1583
|
+
scanEnd = advanceTrivia(input, cur, ctx);
|
|
1584
|
+
}
|
|
1585
|
+
const result2 = parsers[i].parse(input, scanEnd, ctx);
|
|
1586
|
+
if (!result2.ok) return result2;
|
|
1587
|
+
if (result2.span.end > scanEnd) cur = result2.span.end;
|
|
1588
|
+
else rollbackTrivia(ctx, mark);
|
|
1589
|
+
if (values !== void 0) values.push(result2.value);
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
const result = parsers[i].parse(input, cur, ctx);
|
|
1593
|
+
if (!result.ok) return result;
|
|
1594
|
+
if (values !== void 0) values.push(result.value);
|
|
1595
|
+
cur = result.span.end;
|
|
1596
|
+
}
|
|
1597
|
+
} finally {
|
|
1598
|
+
ctx._sync = inheritedSync;
|
|
1599
|
+
}
|
|
1600
|
+
return {
|
|
1601
|
+
ok: true,
|
|
1602
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
1603
|
+
value: values ?? void 0,
|
|
1604
|
+
span: { start: pos, end: cur }
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1465
1607
|
return {
|
|
1466
1608
|
_tag: "sequence",
|
|
1467
1609
|
_meta: meta,
|
|
1468
1610
|
_def: def,
|
|
1469
1611
|
parse(input, pos, ctx) {
|
|
1612
|
+
if (ctx._tolerant) return parseTolerant(input, pos, ctx);
|
|
1470
1613
|
const values = def.valueUnused ? void 0 : [];
|
|
1471
1614
|
let cur = pos;
|
|
1472
1615
|
for (let i = 0; i < parsers.length; i++) {
|
|
@@ -1505,6 +1648,69 @@ function sequence(...parsers) {
|
|
|
1505
1648
|
};
|
|
1506
1649
|
}
|
|
1507
1650
|
|
|
1651
|
+
// src/combinators/expect.ts
|
|
1652
|
+
function deriveExpected(c) {
|
|
1653
|
+
const def = c._def;
|
|
1654
|
+
switch (def.tag) {
|
|
1655
|
+
case "literal":
|
|
1656
|
+
return [JSON.stringify(def.value)];
|
|
1657
|
+
case "regex":
|
|
1658
|
+
return [`/${def.source}/`];
|
|
1659
|
+
case "keywords":
|
|
1660
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
1661
|
+
case "label":
|
|
1662
|
+
return [def.label];
|
|
1663
|
+
case "choice":
|
|
1664
|
+
return def.parsers.flatMap(deriveExpected);
|
|
1665
|
+
case "sequence":
|
|
1666
|
+
return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
|
|
1667
|
+
case "node":
|
|
1668
|
+
case "grammar":
|
|
1669
|
+
case "trivia":
|
|
1670
|
+
case "token":
|
|
1671
|
+
case "optional":
|
|
1672
|
+
case "many":
|
|
1673
|
+
case "oneOrMore":
|
|
1674
|
+
case "transform":
|
|
1675
|
+
case "not":
|
|
1676
|
+
return deriveExpected(def.parser);
|
|
1677
|
+
case "lazy": {
|
|
1678
|
+
const name = c._ruleName;
|
|
1679
|
+
try {
|
|
1680
|
+
return deriveExpected(def.thunk());
|
|
1681
|
+
} catch {
|
|
1682
|
+
return name ? [name] : [];
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
default:
|
|
1686
|
+
return [];
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
function expect(combinator, label2) {
|
|
1690
|
+
const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
|
|
1691
|
+
const meta = {
|
|
1692
|
+
firstSet: combinator._meta.firstSet,
|
|
1693
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1694
|
+
isTrivia: false
|
|
1695
|
+
};
|
|
1696
|
+
return {
|
|
1697
|
+
_tag: "expect",
|
|
1698
|
+
_meta: meta,
|
|
1699
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
1700
|
+
parse(input, pos, ctx) {
|
|
1701
|
+
const result = combinator.parse(input, pos, ctx);
|
|
1702
|
+
if (result.ok) return result;
|
|
1703
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
1704
|
+
ctx._errors?.push(error);
|
|
1705
|
+
if (ctx._tolerant) captureError(ctx, error);
|
|
1706
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
1707
|
+
}
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
function isParseError(value) {
|
|
1711
|
+
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1508
1714
|
// src/combinators/repeat.ts
|
|
1509
1715
|
function repItem(combinator, input, cur, ctx) {
|
|
1510
1716
|
const mark = saveTriviaMark(ctx);
|
|
@@ -1525,7 +1731,7 @@ function repItem(combinator, input, cur, ctx) {
|
|
|
1525
1731
|
const result = combinator.parse(input, pos, ctx);
|
|
1526
1732
|
if (!result.ok) {
|
|
1527
1733
|
rollbackTrivia(ctx, mark);
|
|
1528
|
-
return { fail: result };
|
|
1734
|
+
return { fail: result, failPos: pos };
|
|
1529
1735
|
}
|
|
1530
1736
|
if (result.span.end === pos) {
|
|
1531
1737
|
rollbackTrivia(ctx, mark);
|
|
@@ -1540,6 +1746,7 @@ function many(combinator) {
|
|
|
1540
1746
|
isTrivia: false
|
|
1541
1747
|
};
|
|
1542
1748
|
const def = { tag: "many", parser: combinator, min: 0 };
|
|
1749
|
+
let expected;
|
|
1543
1750
|
return {
|
|
1544
1751
|
_tag: "many",
|
|
1545
1752
|
_meta: meta,
|
|
@@ -1549,7 +1756,18 @@ function many(combinator) {
|
|
|
1549
1756
|
let cur = pos;
|
|
1550
1757
|
while (cur < input.length) {
|
|
1551
1758
|
const item = repItem(combinator, input, cur, ctx);
|
|
1552
|
-
if (item === "stop"
|
|
1759
|
+
if (item === "stop") break;
|
|
1760
|
+
if ("fail" in item) {
|
|
1761
|
+
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1762
|
+
if (sync === void 0) break;
|
|
1763
|
+
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
1764
|
+
expected ??= deriveExpected(combinator);
|
|
1765
|
+
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
1766
|
+
if (values !== void 0) values.push(error);
|
|
1767
|
+
captureError(ctx, error);
|
|
1768
|
+
cur = end;
|
|
1769
|
+
continue;
|
|
1770
|
+
}
|
|
1553
1771
|
if (values !== void 0) values.push(item.value);
|
|
1554
1772
|
cur = item.end;
|
|
1555
1773
|
}
|
|
@@ -1564,6 +1782,7 @@ function oneOrMore(combinator) {
|
|
|
1564
1782
|
isTrivia: false
|
|
1565
1783
|
};
|
|
1566
1784
|
const def = { tag: "oneOrMore", parser: combinator, min: 1 };
|
|
1785
|
+
let expected;
|
|
1567
1786
|
return {
|
|
1568
1787
|
_tag: "oneOrMore",
|
|
1569
1788
|
_meta: meta,
|
|
@@ -1575,7 +1794,18 @@ function oneOrMore(combinator) {
|
|
|
1575
1794
|
let cur = first.span.end;
|
|
1576
1795
|
while (cur < input.length) {
|
|
1577
1796
|
const item = repItem(combinator, input, cur, ctx);
|
|
1578
|
-
if (item === "stop"
|
|
1797
|
+
if (item === "stop") break;
|
|
1798
|
+
if ("fail" in item) {
|
|
1799
|
+
const sync = ctx._tolerant ? ctx._sync : void 0;
|
|
1800
|
+
if (sync === void 0) break;
|
|
1801
|
+
if (matchesAt(sync, input, item.failPos, ctx)) break;
|
|
1802
|
+
expected ??= deriveExpected(combinator);
|
|
1803
|
+
const { error, end } = recoverScan(input, item.failPos, ctx, sync, expected);
|
|
1804
|
+
if (values !== void 0) values.push(error);
|
|
1805
|
+
captureError(ctx, error);
|
|
1806
|
+
cur = end;
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1579
1809
|
if (values !== void 0) values.push(item.value);
|
|
1580
1810
|
cur = item.end;
|
|
1581
1811
|
}
|
|
@@ -1621,15 +1851,29 @@ function sepBy(combinator, separator) {
|
|
|
1621
1851
|
canMatchNewline: combinator._meta.canMatchNewline || separator._meta.canMatchNewline,
|
|
1622
1852
|
isTrivia: false
|
|
1623
1853
|
};
|
|
1854
|
+
let expected;
|
|
1624
1855
|
return {
|
|
1625
1856
|
_tag: "sepBy",
|
|
1626
1857
|
_meta: meta,
|
|
1627
1858
|
_def: { tag: "sepBy", parser: combinator, separator },
|
|
1628
1859
|
parse(input, pos, ctx) {
|
|
1629
1860
|
const first = combinator.parse(input, pos, ctx);
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1861
|
+
const values = [];
|
|
1862
|
+
let cur;
|
|
1863
|
+
if (first.ok) {
|
|
1864
|
+
values.push(first.value);
|
|
1865
|
+
cur = first.span.end;
|
|
1866
|
+
} else {
|
|
1867
|
+
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
1868
|
+
if (term === void 0 || matchesAt(term, input, pos, ctx)) {
|
|
1869
|
+
return { ok: true, value: [], span: { start: pos, end: pos } };
|
|
1870
|
+
}
|
|
1871
|
+
expected ??= deriveExpected(combinator);
|
|
1872
|
+
const rec = recoverScan(input, pos, ctx, orSentinel(separator, term), expected);
|
|
1873
|
+
values.push(rec.error);
|
|
1874
|
+
captureError(ctx, rec.error);
|
|
1875
|
+
cur = rec.end;
|
|
1876
|
+
}
|
|
1633
1877
|
while (cur < input.length) {
|
|
1634
1878
|
const loopMark = saveTriviaMark(ctx);
|
|
1635
1879
|
let sepPos = cur;
|
|
@@ -1659,6 +1903,15 @@ function sepBy(combinator, separator) {
|
|
|
1659
1903
|
}
|
|
1660
1904
|
const next = combinator.parse(input, nextPos, ctx);
|
|
1661
1905
|
if (!next.ok) {
|
|
1906
|
+
const term = ctx._tolerant ? ctx._sync : void 0;
|
|
1907
|
+
if (term !== void 0 && !matchesAt(term, input, nextPos, ctx)) {
|
|
1908
|
+
expected ??= deriveExpected(combinator);
|
|
1909
|
+
const rec = recoverScan(input, nextPos, ctx, orSentinel(separator, term), expected);
|
|
1910
|
+
values.push(rec.error);
|
|
1911
|
+
captureError(ctx, rec.error);
|
|
1912
|
+
cur = rec.end;
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1662
1915
|
rollbackTrivia(ctx, loopMark);
|
|
1663
1916
|
break;
|
|
1664
1917
|
}
|
|
@@ -1832,7 +2085,7 @@ function rules(a, b) {
|
|
|
1832
2085
|
cache[key] = parser2;
|
|
1833
2086
|
}
|
|
1834
2087
|
}
|
|
1835
|
-
if (options?.trivia
|
|
2088
|
+
if (options?.trivia != null) {
|
|
1836
2089
|
for (const key of Object.keys(definitions)) {
|
|
1837
2090
|
const rule = cache[key];
|
|
1838
2091
|
if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
|
|
@@ -1862,7 +2115,9 @@ function not(combinator) {
|
|
|
1862
2115
|
_meta: meta,
|
|
1863
2116
|
_def: { tag: "not", parser: combinator },
|
|
1864
2117
|
parse(input, pos, ctx) {
|
|
2118
|
+
const mark = saveCstMark(ctx);
|
|
1865
2119
|
const result = combinator.parse(input, pos, ctx);
|
|
2120
|
+
rollbackCstCapture(ctx, mark);
|
|
1866
2121
|
if (result.ok) {
|
|
1867
2122
|
return { ok: false, expected: [`not(${combinator._tag})`], span: { start: pos, end: pos } };
|
|
1868
2123
|
}
|
|
@@ -1976,7 +2231,7 @@ function unwrapChild(child) {
|
|
|
1976
2231
|
return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
|
|
1977
2232
|
}
|
|
1978
2233
|
function isCstChild(value) {
|
|
1979
|
-
return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "
|
|
2234
|
+
return typeof value === "object" && value !== null && (value._tag === "node" || value._tag === "leaf" || value._tag === "parseError");
|
|
1980
2235
|
}
|
|
1981
2236
|
function missingInferredType() {
|
|
1982
2237
|
throw new Error('node(): inferred node type requires a rules() key; pass node("Type", parser) outside rules()');
|
|
@@ -2254,65 +2509,6 @@ function token(root) {
|
|
|
2254
2509
|
};
|
|
2255
2510
|
}
|
|
2256
2511
|
|
|
2257
|
-
// src/combinators/expect.ts
|
|
2258
|
-
function deriveExpected(c) {
|
|
2259
|
-
const def = c._def;
|
|
2260
|
-
switch (def.tag) {
|
|
2261
|
-
case "literal":
|
|
2262
|
-
return [JSON.stringify(def.value)];
|
|
2263
|
-
case "regex":
|
|
2264
|
-
return [`/${def.source}/`];
|
|
2265
|
-
case "keywords":
|
|
2266
|
-
return def.words.map((w) => JSON.stringify(w));
|
|
2267
|
-
case "label":
|
|
2268
|
-
return [def.label];
|
|
2269
|
-
case "choice":
|
|
2270
|
-
return def.parsers.flatMap(deriveExpected);
|
|
2271
|
-
case "sequence":
|
|
2272
|
-
return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
|
|
2273
|
-
case "node":
|
|
2274
|
-
case "grammar":
|
|
2275
|
-
case "trivia":
|
|
2276
|
-
case "token":
|
|
2277
|
-
case "optional":
|
|
2278
|
-
case "many":
|
|
2279
|
-
case "oneOrMore":
|
|
2280
|
-
case "transform":
|
|
2281
|
-
case "not":
|
|
2282
|
-
return deriveExpected(def.parser);
|
|
2283
|
-
case "lazy": {
|
|
2284
|
-
const name = c._ruleName;
|
|
2285
|
-
try {
|
|
2286
|
-
return deriveExpected(def.thunk());
|
|
2287
|
-
} catch {
|
|
2288
|
-
return name ? [name] : [];
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
default:
|
|
2292
|
-
return [];
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2295
|
-
function expect(combinator, label2) {
|
|
2296
|
-
const expected = label2 !== void 0 ? [label2] : deriveExpected(combinator);
|
|
2297
|
-
const meta = {
|
|
2298
|
-
firstSet: combinator._meta.firstSet,
|
|
2299
|
-
canMatchNewline: combinator._meta.canMatchNewline,
|
|
2300
|
-
isTrivia: false
|
|
2301
|
-
};
|
|
2302
|
-
return {
|
|
2303
|
-
_tag: "expect",
|
|
2304
|
-
_meta: meta,
|
|
2305
|
-
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
2306
|
-
parse(input, pos, ctx) {
|
|
2307
|
-
const result = combinator.parse(input, pos, ctx);
|
|
2308
|
-
if (result.ok) return result;
|
|
2309
|
-
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
2310
|
-
ctx._errors?.push(error);
|
|
2311
|
-
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
2312
|
-
}
|
|
2313
|
-
};
|
|
2314
|
-
}
|
|
2315
|
-
|
|
2316
2512
|
// src/compiler/scannable-run.ts
|
|
2317
2513
|
var BACKSLASH = 92;
|
|
2318
2514
|
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
@@ -2384,20 +2580,39 @@ function parseSeqParts(source) {
|
|
|
2384
2580
|
lit = [];
|
|
2385
2581
|
}
|
|
2386
2582
|
};
|
|
2387
|
-
const
|
|
2583
|
+
const braceQuant = (at) => {
|
|
2584
|
+
const close = source.indexOf("}", at);
|
|
2585
|
+
if (close === -1) return null;
|
|
2586
|
+
const body = source.slice(at + 1, close);
|
|
2587
|
+
const m = /^(\d+)(,(\d*)?)?$/.exec(body);
|
|
2588
|
+
if (!m) return null;
|
|
2589
|
+
const min = Number(m[1]);
|
|
2590
|
+
const max = m[2] === void 0 ? min : m[3] ? Number(m[3]) : Infinity;
|
|
2591
|
+
if (max < 1 || min > max) return null;
|
|
2592
|
+
return { min, max, len: close - at + 1 };
|
|
2593
|
+
};
|
|
2594
|
+
const runFrom = (ranges, negated, at) => {
|
|
2595
|
+
const q = source[at];
|
|
2388
2596
|
if (q === "+") {
|
|
2389
|
-
parts.push({ part: "run", ranges, negated, min: 1,
|
|
2597
|
+
parts.push({ part: "run", ranges, negated, min: 1, max: Infinity });
|
|
2390
2598
|
return 1;
|
|
2391
2599
|
}
|
|
2392
2600
|
if (q === "*") {
|
|
2393
|
-
parts.push({ part: "run", ranges, negated, min: 0,
|
|
2601
|
+
parts.push({ part: "run", ranges, negated, min: 0, max: Infinity });
|
|
2394
2602
|
return 1;
|
|
2395
2603
|
}
|
|
2396
2604
|
if (q === "?") {
|
|
2397
|
-
parts.push({ part: "run", ranges, negated, min: 0,
|
|
2605
|
+
parts.push({ part: "run", ranges, negated, min: 0, max: 1 });
|
|
2398
2606
|
return 1;
|
|
2399
2607
|
}
|
|
2400
|
-
|
|
2608
|
+
if (q === "{") {
|
|
2609
|
+
const b = braceQuant(at);
|
|
2610
|
+
if (b) {
|
|
2611
|
+
parts.push({ part: "run", ranges, negated, min: b.min, max: b.max });
|
|
2612
|
+
return b.len;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
parts.push({ part: "run", ranges, negated, min: 1, max: 1 });
|
|
2401
2616
|
return 0;
|
|
2402
2617
|
};
|
|
2403
2618
|
let i = 0;
|
|
@@ -2422,7 +2637,7 @@ function parseSeqParts(source) {
|
|
|
2422
2637
|
if (!ranges) return null;
|
|
2423
2638
|
flush();
|
|
2424
2639
|
i = k + 1;
|
|
2425
|
-
i += runFrom(ranges, neg,
|
|
2640
|
+
i += runFrom(ranges, neg, i);
|
|
2426
2641
|
continue;
|
|
2427
2642
|
}
|
|
2428
2643
|
if (ch === "\\") {
|
|
@@ -2430,7 +2645,7 @@ function parseSeqParts(source) {
|
|
|
2430
2645
|
if (e === "d" || e === "w" || e === "s") {
|
|
2431
2646
|
flush();
|
|
2432
2647
|
i += 2;
|
|
2433
|
-
i += runFrom(shorthandRanges(e), false,
|
|
2648
|
+
i += runFrom(shorthandRanges(e), false, i);
|
|
2434
2649
|
continue;
|
|
2435
2650
|
}
|
|
2436
2651
|
const uni = readUnicodeEscape(source, i);
|
|
@@ -2519,7 +2734,7 @@ function seqIsUnambiguous(parts) {
|
|
|
2519
2734
|
let hasMandatory = false;
|
|
2520
2735
|
for (let k = 0; k < parts.length; k++) {
|
|
2521
2736
|
const p = parts[k];
|
|
2522
|
-
const mandatory = p.part === "lit" ? !p.optional : p.min
|
|
2737
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min >= 1;
|
|
2523
2738
|
if (mandatory) hasMandatory = true;
|
|
2524
2739
|
if (p.part === "group") {
|
|
2525
2740
|
const exposure = groupPartExposure(p);
|
|
@@ -2530,9 +2745,8 @@ function seqIsUnambiguous(parts) {
|
|
|
2530
2745
|
if (!firstSetsDisjoint(exposure, seqFirstAccept(parts.slice(k + 1)))) return false;
|
|
2531
2746
|
}
|
|
2532
2747
|
}
|
|
2533
|
-
const
|
|
2534
|
-
|
|
2535
|
-
if (skippableSingle || isUnboundedRepeat) {
|
|
2748
|
+
const needsDisjoint = p.part === "lit" ? p.optional : p.part === "run" ? p.max > p.min : p.unbounded || p.min === 0;
|
|
2749
|
+
if (needsDisjoint) {
|
|
2536
2750
|
const rest = seqFirstAccept(parts.slice(k + 1));
|
|
2537
2751
|
if (!firstSetsDisjoint(partFirstAccept(p), rest)) return false;
|
|
2538
2752
|
}
|
|
@@ -2589,8 +2803,8 @@ function fixedClassSeq(shape) {
|
|
|
2589
2803
|
if (p.part === "lit") {
|
|
2590
2804
|
if (p.optional) return null;
|
|
2591
2805
|
for (const cp of p.cps) out.push({ ranges: [[cp, cp]], negated: false });
|
|
2592
|
-
} else if (p.part === "run" &&
|
|
2593
|
-
out.push({ ranges: p.ranges, negated: p.negated });
|
|
2806
|
+
} else if (p.part === "run" && p.max === p.min && Number.isFinite(p.max)) {
|
|
2807
|
+
for (let c = 0; c < p.max; c++) out.push({ ranges: p.ranges, negated: p.negated });
|
|
2594
2808
|
} else {
|
|
2595
2809
|
return null;
|
|
2596
2810
|
}
|
|
@@ -2642,7 +2856,7 @@ function trailingBacktrackClass(shape) {
|
|
|
2642
2856
|
const last = shape.parts[shape.parts.length - 1];
|
|
2643
2857
|
if (last.part === "lit") return null;
|
|
2644
2858
|
if (last.part === "run") {
|
|
2645
|
-
if (last.
|
|
2859
|
+
if (last.max > last.min) return { ranges: last.ranges, negated: last.negated };
|
|
2646
2860
|
return null;
|
|
2647
2861
|
}
|
|
2648
2862
|
return groupPartExposure(last);
|
|
@@ -2779,7 +2993,7 @@ function partFirstAccept(part) {
|
|
|
2779
2993
|
function seqFirstAccept(parts) {
|
|
2780
2994
|
const sets = [];
|
|
2781
2995
|
for (const p of parts) {
|
|
2782
|
-
const mandatory = p.part === "lit" ? !p.optional : p.min
|
|
2996
|
+
const mandatory = p.part === "lit" ? !p.optional : p.min >= 1;
|
|
2783
2997
|
sets.push(partFirstAccept(p));
|
|
2784
2998
|
if (mandatory) break;
|
|
2785
2999
|
}
|
|
@@ -3194,7 +3408,13 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
3194
3408
|
lines.push(`${ind2} if (!(${endV2} + ${L} <= input.length && (${cond}))) break`);
|
|
3195
3409
|
lines.push(`${ind2} ${endV2} += ${L}`);
|
|
3196
3410
|
}
|
|
3197
|
-
} else if (p.part === "run" &&
|
|
3411
|
+
} else if (p.part === "run" && p.max === Infinity) {
|
|
3412
|
+
const s = mint("_s");
|
|
3413
|
+
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
3414
|
+
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
3415
|
+
if (p.min === 1) lines.push(`${ind2} if (${endV2} === ${s}) break`);
|
|
3416
|
+
else if (p.min > 1) lines.push(`${ind2} if (${endV2} - ${s} < ${p.min}) break`);
|
|
3417
|
+
} else if (p.part === "run" && p.max === 1) {
|
|
3198
3418
|
if (p.min === 1) {
|
|
3199
3419
|
lines.push(`${ind2} if (!(${endV2} < input.length && ${runCond(p, endV2)})) break`);
|
|
3200
3420
|
lines.push(`${ind2} ${endV2}++`);
|
|
@@ -3204,8 +3424,8 @@ function emitShapeMatch(shape, start, mint, ind2, firstChar) {
|
|
|
3204
3424
|
} else if (p.part === "run") {
|
|
3205
3425
|
const s = mint("_s");
|
|
3206
3426
|
lines.push(`${ind2} const ${s} = ${endV2}`);
|
|
3207
|
-
lines.push(`${ind2} while (${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
3208
|
-
if (p.min
|
|
3427
|
+
lines.push(`${ind2} while (${endV2} - ${s} < ${p.max} && ${endV2} < input.length && ${runCond(p, endV2)}) ${endV2}++`);
|
|
3428
|
+
if (p.min > 0) lines.push(`${ind2} if (${endV2} - ${s} < ${p.min}) break`);
|
|
3209
3429
|
} else {
|
|
3210
3430
|
const inner = emitShapeMatch(p.inner, endV2, mint, `${ind2} `, first ? firstChar : void 0);
|
|
3211
3431
|
if (!p.unbounded) {
|
|
@@ -3664,21 +3884,29 @@ function hoistExpected(ctx, constArrSource) {
|
|
|
3664
3884
|
}
|
|
3665
3885
|
return name;
|
|
3666
3886
|
}
|
|
3887
|
+
function probeUpdate(ctx, expectedArr, posExpr, constant = true) {
|
|
3888
|
+
if (!ctx.recovery) return "";
|
|
3889
|
+
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
3890
|
+
const pb = v(ctx, "_pb");
|
|
3891
|
+
return `if (_ctx._probe !== undefined && ${posExpr} <= _ctx._probe.offset) { const ${pb} = _ctx._probe.best; if (${pb} === null || ${posExpr} > ${pb}.span.start) _ctx._probe.best = { ok: false, expected: [...${fx}], span: { start: ${posExpr}, end: ${posExpr} } }; else if (${posExpr} === ${pb}.span.start) _ctx._probe.best = { ok: false, expected: [...${pb}.expected, ...${fx}], span: { start: ${posExpr}, end: ${posExpr} } } } `;
|
|
3892
|
+
}
|
|
3667
3893
|
function failBody(ctx, expected, posExpr) {
|
|
3894
|
+
const probe = probeUpdate(ctx, `[${expected}]`, posExpr);
|
|
3668
3895
|
if (ctx.failLabel) {
|
|
3669
|
-
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
3670
|
-
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
3896
|
+
if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
|
|
3897
|
+
return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${hoistExpected(ctx, `[${expected}]`)}; break ${ctx.failLabel} }`;
|
|
3671
3898
|
}
|
|
3672
|
-
return failReturn(expected, posExpr);
|
|
3899
|
+
return probe + failReturn(expected, posExpr);
|
|
3673
3900
|
}
|
|
3674
3901
|
function failArrBody(ctx, expectedArr, posExpr, constant = true) {
|
|
3902
|
+
const probe = probeUpdate(ctx, expectedArr, posExpr, constant);
|
|
3675
3903
|
if (ctx.failLabel) {
|
|
3676
|
-
if (!ctx.recordFail) return `break ${ctx.failLabel}`;
|
|
3904
|
+
if (!ctx.recordFail) return probe ? `{ ${probe}break ${ctx.failLabel} }` : `break ${ctx.failLabel}`;
|
|
3677
3905
|
const fx = constant ? hoistExpected(ctx, expectedArr) : expectedArr;
|
|
3678
|
-
return `{ _ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
3906
|
+
return `{ ${probe}_ctx._fe = ${posExpr}; _ctx._fx = ${fx}; break ${ctx.failLabel} }`;
|
|
3679
3907
|
}
|
|
3680
|
-
if (!constant) return
|
|
3681
|
-
return failReturnArr(expectedArr, posExpr);
|
|
3908
|
+
if (!constant) return `${probe}return { ok: false, expected: [...${expectedArr}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
3909
|
+
return probe + failReturnArr(expectedArr, posExpr);
|
|
3682
3910
|
}
|
|
3683
3911
|
function resultFromRecorded(feExpr = "_ctx._fe", fxExpr = "_ctx._fx") {
|
|
3684
3912
|
return `return { ok: false, expected: [...${fxExpr}], span: { start: ${feExpr}, end: ${feExpr} } }`;
|
|
@@ -4178,6 +4406,10 @@ function emitRegex(def, ctx, pos) {
|
|
|
4178
4406
|
stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
|
|
4179
4407
|
return { stmts, valueVar: vv, endVar };
|
|
4180
4408
|
}
|
|
4409
|
+
function syncSentinelExpr(fs) {
|
|
4410
|
+
if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
|
|
4411
|
+
return `_ctx._rec.sentinel({ kind: 'ranges', ranges: ${JSON.stringify(fs.ranges)} })`;
|
|
4412
|
+
}
|
|
4181
4413
|
function emitSeqValues(def, ctx, pos) {
|
|
4182
4414
|
const startV = v(ctx, "_start");
|
|
4183
4415
|
const curV = v(ctx, "_cur");
|
|
@@ -4186,7 +4418,17 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
4186
4418
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
4187
4419
|
];
|
|
4188
4420
|
const valueVars = [];
|
|
4421
|
+
const syncInV = ctx.recovery ? v(ctx, "_syncIn") : "";
|
|
4422
|
+
if (ctx.recovery) stmts.push(`${ind(ctx)}const ${syncInV} = _ctx._sync`);
|
|
4423
|
+
const publishSync = (i) => {
|
|
4424
|
+
if (!ctx.recovery) return "";
|
|
4425
|
+
const fs = def.parsers.slice(i + 1).reduce((acc, p) => union(acc, firstSetOf(p)), { kind: "empty" });
|
|
4426
|
+
const ref2 = syncSentinelExpr(fs);
|
|
4427
|
+
return `${ind(ctx)}if (_ctx._tolerant) _ctx._sync = ${ref2 ?? syncInV}`;
|
|
4428
|
+
};
|
|
4189
4429
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
4430
|
+
const syncPub = publishSync(i);
|
|
4431
|
+
if (syncPub) stmts.push(syncPub);
|
|
4190
4432
|
if (i > 0 && ctx.activeTrivia) {
|
|
4191
4433
|
if (ctx.capturing) {
|
|
4192
4434
|
const capFn = ensureTriviaCaptureFn(ctx);
|
|
@@ -4211,7 +4453,17 @@ function emitSeqValues(def, ctx, pos) {
|
|
|
4211
4453
|
continue;
|
|
4212
4454
|
} else {
|
|
4213
4455
|
const trivFn = ensureTriviaFn(ctx);
|
|
4214
|
-
|
|
4456
|
+
const scanEndV = v(ctx, "_sne");
|
|
4457
|
+
stmts.push(`${ind(ctx)}const ${scanEndV} = ${trivFn}(input, ${curV}, _ctx)`);
|
|
4458
|
+
const r2 = emit(def.parsers[i], ctx, scanEndV);
|
|
4459
|
+
stmts.push(...r2.stmts);
|
|
4460
|
+
const endAfterV = v(ctx, "_sea");
|
|
4461
|
+
stmts.push(
|
|
4462
|
+
`${ind(ctx)}const ${endAfterV} = ${r2.endVar}`,
|
|
4463
|
+
`${ind(ctx)}if (${endAfterV} > ${scanEndV}) ${curV} = ${endAfterV}`
|
|
4464
|
+
);
|
|
4465
|
+
valueVars.push(r2.valueVar);
|
|
4466
|
+
continue;
|
|
4215
4467
|
}
|
|
4216
4468
|
}
|
|
4217
4469
|
const r = emit(def.parsers[i], ctx, curV);
|
|
@@ -4441,7 +4693,7 @@ function emitFirstMatch(def, ctx, pos) {
|
|
|
4441
4693
|
if (gate) {
|
|
4442
4694
|
const gateIdx = ctx.mapFns.length;
|
|
4443
4695
|
ctx.mapFns.push(gate);
|
|
4444
|
-
ctx.mapFnSrcs.push(null);
|
|
4696
|
+
ctx.mapFnSrcs.push(def.gateSrcs?.[i] ?? null);
|
|
4445
4697
|
gateCond = `${mfRef(ctx)}[${gateIdx}](_ctx.state)`;
|
|
4446
4698
|
}
|
|
4447
4699
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
@@ -4546,6 +4798,8 @@ function emitMany(def, ctx, pos) {
|
|
|
4546
4798
|
...wantValue ? [`${ind(ctx)}const ${arrV} = []`] : [],
|
|
4547
4799
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
4548
4800
|
];
|
|
4801
|
+
const mySyncV = ctx.recovery ? v(ctx, "_mysy") : "";
|
|
4802
|
+
if (ctx.recovery) stmts.push(`${ind(ctx)}const ${mySyncV} = _ctx._sync`);
|
|
4549
4803
|
if (def.min === 1) {
|
|
4550
4804
|
const firstR = emit(def.parser, ctx, curV);
|
|
4551
4805
|
stmts.push(...firstR.stmts);
|
|
@@ -4579,10 +4833,26 @@ function emitMany(def, ctx, pos) {
|
|
|
4579
4833
|
}
|
|
4580
4834
|
}
|
|
4581
4835
|
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
|
|
4582
|
-
stmts.push(
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4836
|
+
stmts.push(...iterStmts);
|
|
4837
|
+
if (ctx.recovery) {
|
|
4838
|
+
const rrV = v(ctx, "_rr");
|
|
4839
|
+
const exp = hoistExpected(ctx, deriveExpectedArr([def.parser]));
|
|
4840
|
+
stmts.push(
|
|
4841
|
+
`${ind(ctx)}if (!${iterOk}) {`,
|
|
4842
|
+
`${ind(ctx)} ${rollback}if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPos}, _ctx)) {`,
|
|
4843
|
+
`${ind(ctx)} const ${rrV} = _ctx._rec.scan(input, ${itemPos}, _ctx, ${mySyncV}, ${exp})`,
|
|
4844
|
+
...wantValue ? [`${ind(ctx)} ${arrV}.push(${rrV}.error)`] : [],
|
|
4845
|
+
`${ind(ctx)} _ctx._rec.capture(_ctx, ${rrV}.error)`,
|
|
4846
|
+
`${ind(ctx)} ${curV} = ${rrV}.end`,
|
|
4847
|
+
`${ind(ctx)} continue`,
|
|
4848
|
+
`${ind(ctx)} }`,
|
|
4849
|
+
`${ind(ctx)} break`,
|
|
4850
|
+
`${ind(ctx)}}`,
|
|
4851
|
+
`${ind(ctx)}if (${iterEnd} <= ${itemPos}) { ${rollback}break }`
|
|
4852
|
+
);
|
|
4853
|
+
} else {
|
|
4854
|
+
stmts.push(`${ind(ctx)}if (!${iterOk} || ${iterEnd} <= ${itemPos}) { ${rollback}break }`);
|
|
4855
|
+
}
|
|
4586
4856
|
if (wantValue) stmts.push(`${ind(ctx)}${arrV}.push(${iterVal})`);
|
|
4587
4857
|
stmts.push(`${ind(ctx)}${curV} = ${iterEnd}`);
|
|
4588
4858
|
ctx.indent--;
|
|
@@ -4604,20 +4874,56 @@ function emitOptional(def, ctx, pos) {
|
|
|
4604
4874
|
function emitSepBy(_p, def, ctx, pos) {
|
|
4605
4875
|
const arrV = v(ctx, "_arr");
|
|
4606
4876
|
const curV = v(ctx, "_cur");
|
|
4877
|
+
const rec = !!ctx.recovery;
|
|
4878
|
+
const mySyncV = rec ? v(ctx, "_mysy") : "";
|
|
4879
|
+
const exp = rec ? hoistExpected(ctx, deriveExpectedArr([def.parser])) : "";
|
|
4880
|
+
const sepSent = rec ? syncSentinelExpr(firstSetOf(def.separator)) : null;
|
|
4881
|
+
const scanSync = sepSent ? `_ctx._rec.or(${sepSent}, ${mySyncV})` : mySyncV;
|
|
4607
4882
|
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos, true);
|
|
4608
4883
|
const stmts = [
|
|
4609
4884
|
`${ind(ctx)}const ${arrV} = []`,
|
|
4610
4885
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
4611
|
-
...
|
|
4612
|
-
|
|
4886
|
+
...rec ? [`${ind(ctx)}const ${mySyncV} = _ctx._sync`] : [],
|
|
4887
|
+
...firstStmts
|
|
4613
4888
|
];
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
)
|
|
4620
|
-
|
|
4889
|
+
const failItem = (nextOkVar, itemPosVar, rb, originalFail) => {
|
|
4890
|
+
if (!rec) return [originalFail];
|
|
4891
|
+
const rr = v(ctx, "_rr");
|
|
4892
|
+
return [
|
|
4893
|
+
`${ind(ctx)}if (!${nextOkVar}) {`,
|
|
4894
|
+
`${ind(ctx)} if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${itemPosVar}, _ctx)) {`,
|
|
4895
|
+
`${ind(ctx)} const ${rr} = _ctx._rec.scan(input, ${itemPosVar}, _ctx, ${scanSync}, ${exp})`,
|
|
4896
|
+
`${ind(ctx)} ${arrV}.push(${rr}.error); _ctx._rec.capture(_ctx, ${rr}.error); ${curV} = ${rr}.end; continue`,
|
|
4897
|
+
`${ind(ctx)} }`,
|
|
4898
|
+
`${ind(ctx)} ${rb}break`,
|
|
4899
|
+
`${ind(ctx)}}`
|
|
4900
|
+
];
|
|
4901
|
+
};
|
|
4902
|
+
if (rec) {
|
|
4903
|
+
const rr0 = v(ctx, "_rr0");
|
|
4904
|
+
const didV = v(ctx, "_did");
|
|
4905
|
+
stmts.push(
|
|
4906
|
+
`${ind(ctx)}let ${didV} = false`,
|
|
4907
|
+
`${ind(ctx)}if (${firstOk}) { ${arrV}.push(${firstVal}); ${curV} = ${firstEnd}; ${didV} = true }`,
|
|
4908
|
+
`${ind(ctx)}else if (_ctx._tolerant && ${mySyncV} !== undefined && !_ctx._rec.at(${mySyncV}, input, ${pos}, _ctx)) {`,
|
|
4909
|
+
`${ind(ctx)} const ${rr0} = _ctx._rec.scan(input, ${pos}, _ctx, ${scanSync}, ${exp})`,
|
|
4910
|
+
`${ind(ctx)} ${arrV}.push(${rr0}.error); _ctx._rec.capture(_ctx, ${rr0}.error); ${curV} = ${rr0}.end; ${didV} = true`,
|
|
4911
|
+
`${ind(ctx)}}`,
|
|
4912
|
+
`${ind(ctx)}if (${didV}) {`
|
|
4913
|
+
);
|
|
4914
|
+
ctx.indent++;
|
|
4915
|
+
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
4916
|
+
ctx.indent++;
|
|
4917
|
+
} else {
|
|
4918
|
+
stmts.push(`${ind(ctx)}if (${firstOk}) {`);
|
|
4919
|
+
ctx.indent++;
|
|
4920
|
+
stmts.push(
|
|
4921
|
+
`${ind(ctx)}${arrV}.push(${firstVal})`,
|
|
4922
|
+
`${ind(ctx)}${curV} = ${firstEnd}`,
|
|
4923
|
+
`${ind(ctx)}while (${curV} < input.length) {`
|
|
4924
|
+
);
|
|
4925
|
+
ctx.indent++;
|
|
4926
|
+
}
|
|
4621
4927
|
let sepAtPos = curV;
|
|
4622
4928
|
if (ctx.activeTrivia) {
|
|
4623
4929
|
if (ctx.capturing) {
|
|
@@ -4643,8 +4949,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
4643
4949
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
4644
4950
|
stmts.push(
|
|
4645
4951
|
...nextStmts,
|
|
4646
|
-
|
|
4647
|
-
`${ind(ctx)}if (!${nextOk}) { ${rollbackToSep}break }`,
|
|
4952
|
+
...failItem(nextOk, npV, rollbackToSep, `${ind(ctx)}if (!${nextOk}) { ${rollbackToSep}break }`),
|
|
4648
4953
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
4649
4954
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
4650
4955
|
);
|
|
@@ -4660,7 +4965,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
4660
4965
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, npV, true);
|
|
4661
4966
|
stmts.push(
|
|
4662
4967
|
...nextStmts,
|
|
4663
|
-
`${ind(ctx)}if (!${nextOk}) break
|
|
4968
|
+
...failItem(nextOk, npV, "", `${ind(ctx)}if (!${nextOk}) break`),
|
|
4664
4969
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
4665
4970
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
4666
4971
|
);
|
|
@@ -4680,7 +4985,7 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
4680
4985
|
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, sepEnd, true);
|
|
4681
4986
|
stmts.push(
|
|
4682
4987
|
...nextStmts,
|
|
4683
|
-
`${ind(ctx)}if (!${nextOk}) { ${nextRb}break }
|
|
4988
|
+
...failItem(nextOk, sepEnd, nextRb, `${ind(ctx)}if (!${nextOk}) { ${nextRb}break }`),
|
|
4684
4989
|
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
4685
4990
|
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
4686
4991
|
);
|
|
@@ -4937,7 +5242,7 @@ ${i}const ${chV} = [], ${rawV} = [], ${tlV} = ${capTLv} ? [] : _EMPTY_TL` : capt
|
|
|
4937
5242
|
stmts.push(
|
|
4938
5243
|
`${i}const ${ndV} = ${finalExpr}`,
|
|
4939
5244
|
`${i}if (${sc}) ${sc}.push(${ndV})`,
|
|
4940
|
-
`${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === '
|
|
5245
|
+
`${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && (${ndV}._tag === 'node' || ${ndV}._tag === 'leaf' || ${ndV}._tag === 'parseError')) ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
|
|
4941
5246
|
);
|
|
4942
5247
|
return { stmts, valueVar: ndV, endVar };
|
|
4943
5248
|
}
|
|
@@ -5027,6 +5332,10 @@ function emitExpect(def, ctx, pos) {
|
|
|
5027
5332
|
`${ind0}if (!${okVar}) {`,
|
|
5028
5333
|
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
|
|
5029
5334
|
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
5335
|
+
// Embed the missing-token error as a `parseError` CST child, mirroring the
|
|
5336
|
+
// interpreter (expect.ts) and the list-recovery emit sites — guarded on
|
|
5337
|
+
// `_ctx._tolerant`, under which the driver always installs `_ctx._rec`.
|
|
5338
|
+
`${ind0} if (_ctx._tolerant) _ctx._rec.capture(_ctx, ${errV})`,
|
|
5030
5339
|
`${ind0} ${valVar} = ${errV}`,
|
|
5031
5340
|
`${ind0} ${endVar} = ${pos}`,
|
|
5032
5341
|
`${ind0} ${okVar} = true`,
|
|
@@ -5193,7 +5502,7 @@ function emitDispatch(p, ctx, pos) {
|
|
|
5193
5502
|
case "guard": {
|
|
5194
5503
|
const fnIdx = ctx.mapFns.length;
|
|
5195
5504
|
ctx.mapFns.push(def.predicate);
|
|
5196
|
-
ctx.mapFnSrcs.push(null);
|
|
5505
|
+
ctx.mapFnSrcs.push(def.predSrc ?? null);
|
|
5197
5506
|
const vv = v(ctx);
|
|
5198
5507
|
return {
|
|
5199
5508
|
stmts: [
|
|
@@ -5208,7 +5517,7 @@ function emitDispatch(p, ctx, pos) {
|
|
|
5208
5517
|
const evIdx = ctx.mapFns.length;
|
|
5209
5518
|
const extra = def.extra;
|
|
5210
5519
|
ctx.mapFns.push((() => extra));
|
|
5211
|
-
ctx.mapFnSrcs.push(null);
|
|
5520
|
+
ctx.mapFnSrcs.push(def.extraSrc !== void 0 ? `() => (${def.extraSrc})` : null);
|
|
5212
5521
|
const innerParser = def.parser;
|
|
5213
5522
|
if (!ctx.namedParsers.has(innerParser)) {
|
|
5214
5523
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
@@ -5452,7 +5761,7 @@ function ruleDependencies(ruleMap) {
|
|
|
5452
5761
|
}
|
|
5453
5762
|
return deps;
|
|
5454
5763
|
}
|
|
5455
|
-
function compile(combinator, mapFnSources) {
|
|
5764
|
+
function compile(combinator, mapFnSources, opts) {
|
|
5456
5765
|
markUnusedValues(combinator);
|
|
5457
5766
|
const grammarTrivia = combinator._meta.grammarTrivia;
|
|
5458
5767
|
const ctx = {
|
|
@@ -5473,6 +5782,7 @@ function compile(combinator, mapFnSources) {
|
|
|
5473
5782
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
5474
5783
|
namedFnDecls: [],
|
|
5475
5784
|
capturing: hasNodeDef(combinator),
|
|
5785
|
+
recovery: opts?.recovery ?? false,
|
|
5476
5786
|
lazyUsage: analyzeLazyUsage(combinator),
|
|
5477
5787
|
...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
|
|
5478
5788
|
};
|
|
@@ -5525,7 +5835,7 @@ function compile(combinator, mapFnSources) {
|
|
|
5525
5835
|
parseWithContext(input, parseCtx, pos = 0) {
|
|
5526
5836
|
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
|
|
5527
5837
|
},
|
|
5528
|
-
// Note: collects
|
|
5838
|
+
// Note: collects expect() errors via _errors. Unlike interpreter
|
|
5529
5839
|
// parse({recover:true}) it does NOT populate furthestFail — the compiled path
|
|
5530
5840
|
// inlines failures for throughput and deliberately skips _probe bookkeeping.
|
|
5531
5841
|
// Callers wanting a furthest-position diagnostic detect unconsumed input
|
|
@@ -5582,6 +5892,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
|
|
|
5582
5892
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
5583
5893
|
namedFnDecls: [],
|
|
5584
5894
|
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
5895
|
+
recovery: opts?.recovery ?? false,
|
|
5585
5896
|
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
|
|
5586
5897
|
ns,
|
|
5587
5898
|
deferFirstSetRefs: true,
|
|
@@ -6327,46 +6638,13 @@ function withCtx(extra, combinator) {
|
|
|
6327
6638
|
};
|
|
6328
6639
|
}
|
|
6329
6640
|
|
|
6330
|
-
// src/combinators/recover.ts
|
|
6331
|
-
function recover(combinator, sentinel) {
|
|
6332
|
-
const meta = {
|
|
6333
|
-
firstSet: { kind: "any" },
|
|
6334
|
-
canMatchNewline: true,
|
|
6335
|
-
isTrivia: false
|
|
6336
|
-
};
|
|
6337
|
-
const expected = deriveExpected(combinator);
|
|
6338
|
-
return {
|
|
6339
|
-
_tag: "recover",
|
|
6340
|
-
_meta: meta,
|
|
6341
|
-
_def: { tag: "recover", parser: combinator, sentinel },
|
|
6342
|
-
parse(input, pos, ctx) {
|
|
6343
|
-
const result = combinator.parse(input, pos, ctx);
|
|
6344
|
-
if (result.ok) return result;
|
|
6345
|
-
let scanPos = pos;
|
|
6346
|
-
while (scanPos < input.length) {
|
|
6347
|
-
if (sentinel.parse(input, scanPos, ctx).ok) break;
|
|
6348
|
-
scanPos++;
|
|
6349
|
-
}
|
|
6350
|
-
const error = {
|
|
6351
|
-
_tag: "parseError",
|
|
6352
|
-
span: { start: pos, end: scanPos },
|
|
6353
|
-
expected
|
|
6354
|
-
};
|
|
6355
|
-
ctx._errors?.push(error);
|
|
6356
|
-
return { ok: true, value: error, span: { start: pos, end: scanPos } };
|
|
6357
|
-
}
|
|
6358
|
-
};
|
|
6359
|
-
}
|
|
6360
|
-
function isParseError(value) {
|
|
6361
|
-
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
6362
|
-
}
|
|
6363
|
-
|
|
6364
6641
|
// src/combinators/completions.ts
|
|
6365
|
-
function completionsAt(
|
|
6642
|
+
function completionsAt(target, input, offset, options = {}) {
|
|
6366
6643
|
const probe = { offset, best: null };
|
|
6367
|
-
const ctx = { trackLines: false, _probe: probe };
|
|
6368
|
-
const
|
|
6369
|
-
|
|
6644
|
+
const ctx = { trackLines: false, _probe: probe, ...options.tolerant ? { _tolerant: true, _rec: REC } : {} };
|
|
6645
|
+
const sliced = input.slice(0, offset);
|
|
6646
|
+
const result = "parseWithContext" in target ? target.parseWithContext(sliced, ctx, 0) : target.parse(sliced, 0, ctx);
|
|
6647
|
+
if (result.ok) return options.tolerant ? probe.best?.expected ?? [] : [];
|
|
6370
6648
|
const best = deeperFail(probe.best, result);
|
|
6371
6649
|
return best?.expected ?? [];
|
|
6372
6650
|
}
|
|
@@ -6376,23 +6654,6 @@ function deeperFail(a, b) {
|
|
|
6376
6654
|
return a.span.start >= b.span.start ? a : b;
|
|
6377
6655
|
}
|
|
6378
6656
|
|
|
6379
|
-
// src/combinators/recover-list.ts
|
|
6380
|
-
function tolerantElement(item, sentinel, until) {
|
|
6381
|
-
const el = transform(
|
|
6382
|
-
sequence(not(until), recover(item, sentinel)),
|
|
6383
|
-
([, value]) => value
|
|
6384
|
-
);
|
|
6385
|
-
if (el._def.tag === "transform") el._def.fnSrc = "([, value]) => value";
|
|
6386
|
-
return el;
|
|
6387
|
-
}
|
|
6388
|
-
function sepByRecover(item, separator, until) {
|
|
6389
|
-
const sentinel = choice(separator, until);
|
|
6390
|
-
return sepBy(tolerantElement(item, sentinel, until), separator);
|
|
6391
|
-
}
|
|
6392
|
-
function manyRecover(item, until) {
|
|
6393
|
-
return many(tolerantElement(item, until, until));
|
|
6394
|
-
}
|
|
6395
|
-
|
|
6396
6657
|
// src/cst/relative-spans.ts
|
|
6397
6658
|
function relativize(node2, parentBase = 0) {
|
|
6398
6659
|
return {
|
|
@@ -6487,6 +6748,9 @@ function editNode(node2, at, delta, parentBase, parentShift, stats) {
|
|
|
6487
6748
|
}
|
|
6488
6749
|
|
|
6489
6750
|
// src/functional/doc.ts
|
|
6751
|
+
function mkCtx(state, build, tolerant) {
|
|
6752
|
+
return tolerant ? { trackLines: false, state, build, _tolerant: true, _rec: REC, _errors: [] } : { trackLines: false, state, build };
|
|
6753
|
+
}
|
|
6490
6754
|
function asRuleFn(entry) {
|
|
6491
6755
|
return typeof entry === "function" ? entry : (input, pos, ctx) => entry.parse(input, pos, ctx);
|
|
6492
6756
|
}
|
|
@@ -6593,8 +6857,8 @@ function graftRelative(root, path, newNode, delta) {
|
|
|
6593
6857
|
children: newChildren
|
|
6594
6858
|
};
|
|
6595
6859
|
}
|
|
6596
|
-
function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
|
|
6597
|
-
const ctx =
|
|
6860
|
+
function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build, tolerant) {
|
|
6861
|
+
const ctx = mkCtx(null, build, tolerant);
|
|
6598
6862
|
const out = [];
|
|
6599
6863
|
let pos = reStart;
|
|
6600
6864
|
let guard2 = 0;
|
|
@@ -6618,7 +6882,7 @@ function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build) {
|
|
|
6618
6882
|
}
|
|
6619
6883
|
return pos === reEnd ? out : null;
|
|
6620
6884
|
}
|
|
6621
|
-
function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build) {
|
|
6885
|
+
function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build, tolerant) {
|
|
6622
6886
|
const kids = C.children;
|
|
6623
6887
|
if (kids.length === 0) return null;
|
|
6624
6888
|
let h = 0;
|
|
@@ -6659,7 +6923,7 @@ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registr
|
|
|
6659
6923
|
const reStart = h > 0 ? cStart + kids[h - 1].span.end : cStart;
|
|
6660
6924
|
const reEnd = cStart + kids[t].span.start + delta;
|
|
6661
6925
|
if (reEnd < reStart) return null;
|
|
6662
|
-
const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build);
|
|
6926
|
+
const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
|
|
6663
6927
|
if (!middle) return null;
|
|
6664
6928
|
if (middle.length > 0) {
|
|
6665
6929
|
const last2 = middle[middle.length - 1];
|
|
@@ -6669,7 +6933,7 @@ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registr
|
|
|
6669
6933
|
for (const sentinel of [" ", "\uFFFF"]) {
|
|
6670
6934
|
if (newInput[reEnd] === sentinel) continue;
|
|
6671
6935
|
const probed = newInput.slice(0, reEnd) + sentinel.repeat(newInput.length - reEnd);
|
|
6672
|
-
const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build);
|
|
6936
|
+
const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
|
|
6673
6937
|
if (!probe || probe.length !== middle.length) return null;
|
|
6674
6938
|
for (let i = 0; i < middle.length; i++) if (!structurallyEqual(probe[i], middle[i])) return null;
|
|
6675
6939
|
}
|
|
@@ -6697,6 +6961,11 @@ function structurallyEqual(a, b) {
|
|
|
6697
6961
|
if (at === "leaf" || at === "trivia") {
|
|
6698
6962
|
return a.value === b.value;
|
|
6699
6963
|
}
|
|
6964
|
+
if (at === "parseError") {
|
|
6965
|
+
const ae = a.expected ?? [];
|
|
6966
|
+
const be = b.expected ?? [];
|
|
6967
|
+
return ae.length === be.length && ae.every((x, i) => x === be[i]);
|
|
6968
|
+
}
|
|
6700
6969
|
if (a.type !== b.type) return false;
|
|
6701
6970
|
const ac = a.children;
|
|
6702
6971
|
const bc = b.children;
|
|
@@ -6706,13 +6975,13 @@ function structurallyEqual(a, b) {
|
|
|
6706
6975
|
}
|
|
6707
6976
|
return true;
|
|
6708
6977
|
}
|
|
6709
|
-
function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced) {
|
|
6978
|
+
function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced, tolerant) {
|
|
6710
6979
|
if (!produced.ok) return false;
|
|
6711
6980
|
if (boundary >= newInput.length) return true;
|
|
6712
6981
|
for (const sentinel of [" ", "\uFFFF"]) {
|
|
6713
6982
|
if (newInput[boundary] === sentinel) continue;
|
|
6714
6983
|
const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
|
|
6715
|
-
const ctx =
|
|
6984
|
+
const ctx = mkCtx(state, build, tolerant);
|
|
6716
6985
|
let r;
|
|
6717
6986
|
try {
|
|
6718
6987
|
r = ruleFn(probed, start, ctx);
|
|
@@ -6744,8 +7013,9 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
6744
7013
|
_abs;
|
|
6745
7014
|
_rel;
|
|
6746
7015
|
errors;
|
|
7016
|
+
unconsumedFrom;
|
|
6747
7017
|
input;
|
|
6748
|
-
constructor(registry, rootRule, opts, trees, errors, input) {
|
|
7018
|
+
constructor(registry, rootRule, opts, trees, errors, unconsumedFrom, input) {
|
|
6749
7019
|
this._registry = registry;
|
|
6750
7020
|
this._fns = {};
|
|
6751
7021
|
this._reps = /* @__PURE__ */ new Set();
|
|
@@ -6758,6 +7028,7 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
6758
7028
|
this._abs = trees.abs;
|
|
6759
7029
|
this._rel = trees.rel;
|
|
6760
7030
|
this.errors = errors;
|
|
7031
|
+
this.unconsumedFrom = unconsumedFrom;
|
|
6761
7032
|
this.input = input;
|
|
6762
7033
|
}
|
|
6763
7034
|
/**
|
|
@@ -6780,6 +7051,19 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
6780
7051
|
if (!rel) throw new Error("spanAt on a failed parse (tree is null)");
|
|
6781
7052
|
return absoluteSpanCST(rel, path);
|
|
6782
7053
|
}
|
|
7054
|
+
/**
|
|
7055
|
+
* Wrap a reused (grafted/spliced) RELATIVE tree, recomputing `errors` and
|
|
7056
|
+
* `unconsumedFrom` from it so both stay consistent with the tree across the
|
|
7057
|
+
* edit (the flat errors are recovered errors embedded in the reused subtrees;
|
|
7058
|
+
* unconsumedFrom re-derives trailing junk over the new input).
|
|
7059
|
+
*/
|
|
7060
|
+
wrapReuse(newTree, newInput) {
|
|
7061
|
+
const trivia2 = triviaOf(this._registry[this._rootRule], this._opts);
|
|
7062
|
+
const end = newTree.span.end;
|
|
7063
|
+
const errors = collectEmbeddedErrors(newTree, 0, []);
|
|
7064
|
+
const unconsumedFrom = unconsumedAfter(end, newInput, trivia2);
|
|
7065
|
+
return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, errors, unconsumedFrom, newInput);
|
|
7066
|
+
}
|
|
6783
7067
|
edit(from, to, replacement) {
|
|
6784
7068
|
const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
|
|
6785
7069
|
const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
|
|
@@ -6801,22 +7085,22 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
6801
7085
|
const ruleFn = this._fns[node2.type];
|
|
6802
7086
|
if (!ruleFn) continue;
|
|
6803
7087
|
if (!(absStart <= from && to <= absEnd)) continue;
|
|
6804
|
-
const ctx =
|
|
7088
|
+
const ctx = mkCtx(node2.state, this._opts.build, !!this._opts.tolerant);
|
|
6805
7089
|
const r = ruleFn(newInput, absStart, ctx);
|
|
6806
7090
|
if (!r.ok) continue;
|
|
6807
7091
|
if (r.span.end !== absEnd + delta) continue;
|
|
6808
|
-
if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r)) {
|
|
7092
|
+
if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r, !!this._opts.tolerant)) {
|
|
6809
7093
|
continue;
|
|
6810
7094
|
}
|
|
6811
7095
|
const parentBase = absStart - node2.span.start;
|
|
6812
7096
|
const newRel = relativizeCST(r.value, parentBase);
|
|
6813
7097
|
if (delta === 0) {
|
|
6814
7098
|
const newTree2 = replaceAtPath(rebuild, root, path, newRel);
|
|
6815
|
-
return
|
|
7099
|
+
return this.wrapReuse(newTree2, newInput);
|
|
6816
7100
|
}
|
|
6817
7101
|
if (this._opts.rebuild) return reparse();
|
|
6818
7102
|
const newTree = graftRelative(root, path, newRel, delta);
|
|
6819
|
-
return
|
|
7103
|
+
return this.wrapReuse(newTree, newInput);
|
|
6820
7104
|
}
|
|
6821
7105
|
if (this._opts.structuralReuse && delta !== 0 && !this._opts.rebuild) {
|
|
6822
7106
|
const spliceCandidates = [...candidates, { node: root, path: [] }];
|
|
@@ -6824,22 +7108,50 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
6824
7108
|
if (!this._reps.has(node2.type)) continue;
|
|
6825
7109
|
const { start: cStart, end: cEnd } = absoluteSpanCST(root, path);
|
|
6826
7110
|
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
|
|
7111
|
+
const spliced = tryListSplice(root, path, node2, cStart, newInput, from, to, delta, this._fns, this._opts.build, !!this._opts.tolerant);
|
|
7112
|
+
if (spliced) return this.wrapReuse(spliced, newInput);
|
|
6829
7113
|
}
|
|
6830
7114
|
}
|
|
6831
7115
|
return reparse();
|
|
6832
7116
|
}
|
|
6833
7117
|
};
|
|
7118
|
+
function triviaOf(entry, opts) {
|
|
7119
|
+
if (opts.trivia) return opts.trivia;
|
|
7120
|
+
if (entry === void 0 || typeof entry === "function") return void 0;
|
|
7121
|
+
const meta = entry._meta;
|
|
7122
|
+
return meta ? meta.grammarTrivia : void 0;
|
|
7123
|
+
}
|
|
7124
|
+
function unconsumedAfter(end, input, trivia2) {
|
|
7125
|
+
let pos = end;
|
|
7126
|
+
if (trivia2 && pos < input.length) {
|
|
7127
|
+
const t = trivia2.parse(input, pos, { trackLines: false });
|
|
7128
|
+
if (t.ok && t.span.end > pos) pos = t.span.end;
|
|
7129
|
+
}
|
|
7130
|
+
return pos < input.length ? pos : null;
|
|
7131
|
+
}
|
|
7132
|
+
function collectEmbeddedErrors(node2, base, out) {
|
|
7133
|
+
const c = node2;
|
|
7134
|
+
if (!c || typeof c !== "object" || !c.span) return out;
|
|
7135
|
+
const start = base + c.span.start;
|
|
7136
|
+
if (c._tag === "parseError") {
|
|
7137
|
+
out.push({ _tag: "parseError", span: { start, end: base + c.span.end }, expected: c.expected ?? [] });
|
|
7138
|
+
return out;
|
|
7139
|
+
}
|
|
7140
|
+
if (Array.isArray(c.children)) for (const k of c.children) collectEmbeddedErrors(k, start, out);
|
|
7141
|
+
return out;
|
|
7142
|
+
}
|
|
6834
7143
|
function parseDoc(registry, rootRule, input, opts = {}) {
|
|
6835
7144
|
const entry = registry[rootRule];
|
|
6836
7145
|
if (!entry) throw new Error(`No rule '${rootRule}' in registry`);
|
|
6837
|
-
const ctx =
|
|
7146
|
+
const ctx = mkCtx(opts.state, opts.build, !!opts.tolerant);
|
|
6838
7147
|
const r = asRuleFn(entry)(input, 0, ctx);
|
|
6839
7148
|
if (r.ok) {
|
|
6840
|
-
|
|
7149
|
+
const errors = ctx._errors ? [...ctx._errors] : [];
|
|
7150
|
+
const unconsumedFrom = unconsumedAfter(r.span.end, input, triviaOf(entry, opts));
|
|
7151
|
+
return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, errors, unconsumedFrom, input);
|
|
6841
7152
|
}
|
|
6842
|
-
|
|
7153
|
+
const fail = { _tag: "parseError", span: r.span, expected: r.expected };
|
|
7154
|
+
return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input);
|
|
6843
7155
|
}
|
|
6844
7156
|
|
|
6845
7157
|
// src/functional/run.ts
|
|
@@ -6860,7 +7172,8 @@ function run(entry, input, options = {}) {
|
|
|
6860
7172
|
build: options.build,
|
|
6861
7173
|
state: options.state,
|
|
6862
7174
|
...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
|
|
6863
|
-
...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {}
|
|
7175
|
+
...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
|
|
7176
|
+
...options.tolerant ? { _tolerant: true, _rec: REC } : {}
|
|
6864
7177
|
};
|
|
6865
7178
|
const r = invoke(entry, input, 0, ctx);
|
|
6866
7179
|
let unconsumedFrom = null;
|
|
@@ -7246,7 +7559,6 @@ export {
|
|
|
7246
7559
|
literal,
|
|
7247
7560
|
makeWord,
|
|
7248
7561
|
many,
|
|
7249
|
-
manyRecover,
|
|
7250
7562
|
noTrivia,
|
|
7251
7563
|
node,
|
|
7252
7564
|
not,
|
|
@@ -7256,7 +7568,6 @@ export {
|
|
|
7256
7568
|
parse,
|
|
7257
7569
|
parseDoc,
|
|
7258
7570
|
parser,
|
|
7259
|
-
recover,
|
|
7260
7571
|
ref,
|
|
7261
7572
|
regex,
|
|
7262
7573
|
relativize,
|
|
@@ -7265,7 +7576,6 @@ export {
|
|
|
7265
7576
|
run,
|
|
7266
7577
|
scanTo,
|
|
7267
7578
|
sepBy,
|
|
7268
|
-
sepByRecover,
|
|
7269
7579
|
sequence,
|
|
7270
7580
|
shiftAbsolute,
|
|
7271
7581
|
skip,
|