parseman 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/combinators/expect.d.ts +36 -0
- package/dist/combinators/expect.d.ts.map +1 -0
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/compiler/codegen.d.ts +33 -0
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/compiler/scannable-run.d.ts +46 -0
- package/dist/compiler/scannable-run.d.ts.map +1 -0
- package/dist/compiler/trivia-fast-path.d.ts +27 -14
- package/dist/compiler/trivia-fast-path.d.ts.map +1 -1
- package/dist/index.cjs +534 -144
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +534 -144
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts +18 -0
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +701 -162
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +701 -162
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -2
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
choice: () => choice,
|
|
38
38
|
compile: () => compile,
|
|
39
39
|
completionsAt: () => completionsAt,
|
|
40
|
+
expect: () => expect,
|
|
40
41
|
guard: () => guard,
|
|
41
42
|
isParseError: () => isParseError,
|
|
42
43
|
keywords: () => keywords,
|
|
@@ -61,6 +62,7 @@ __export(index_exports, {
|
|
|
61
62
|
sepBy: () => sepBy,
|
|
62
63
|
sequence: () => sequence,
|
|
63
64
|
skip: () => skip,
|
|
65
|
+
staticExpected: () => staticExpected,
|
|
64
66
|
transform: () => transform,
|
|
65
67
|
trivia: () => trivia,
|
|
66
68
|
triviaEntries: () => triviaEntries,
|
|
@@ -1349,13 +1351,15 @@ function noTrivia(root) {
|
|
|
1349
1351
|
function parse(combinator, input, opts = {}) {
|
|
1350
1352
|
const trackLines = opts.trackLines ?? false;
|
|
1351
1353
|
const _errors = opts.recover ? [] : void 0;
|
|
1354
|
+
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
1352
1355
|
const ctx = {
|
|
1353
1356
|
trackLines,
|
|
1354
|
-
..._errors !== void 0 ? { _errors } : {}
|
|
1357
|
+
..._errors !== void 0 ? { _errors } : {},
|
|
1358
|
+
..._probe !== void 0 ? { _probe } : {}
|
|
1355
1359
|
};
|
|
1356
1360
|
const result = combinator.parse(input, 0, ctx);
|
|
1357
1361
|
if (!result.ok) return result;
|
|
1358
|
-
const withErrors = _errors !== void 0 ? { ...result, errors: _errors } : result;
|
|
1362
|
+
const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
|
|
1359
1363
|
if (trackLines) {
|
|
1360
1364
|
const idx = buildLineIndex(input);
|
|
1361
1365
|
return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
|
|
@@ -1363,14 +1367,140 @@ function parse(combinator, input, opts = {}) {
|
|
|
1363
1367
|
return withErrors;
|
|
1364
1368
|
}
|
|
1365
1369
|
|
|
1370
|
+
// src/compiler/scannable-run.ts
|
|
1371
|
+
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1372
|
+
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1373
|
+
function parseClassRanges(body) {
|
|
1374
|
+
const ranges = [];
|
|
1375
|
+
let i = 0;
|
|
1376
|
+
const readCp = () => {
|
|
1377
|
+
const ch = body[i];
|
|
1378
|
+
if (ch === void 0) return null;
|
|
1379
|
+
if (ch === "\\") {
|
|
1380
|
+
const e = body[i + 1];
|
|
1381
|
+
if (e === void 0) return null;
|
|
1382
|
+
i += 2;
|
|
1383
|
+
return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
|
|
1384
|
+
}
|
|
1385
|
+
i += ch.length;
|
|
1386
|
+
return ch.codePointAt(0);
|
|
1387
|
+
};
|
|
1388
|
+
while (i < body.length) {
|
|
1389
|
+
const lo = readCp();
|
|
1390
|
+
if (lo === null) return null;
|
|
1391
|
+
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1392
|
+
i += 1;
|
|
1393
|
+
const hi = readCp();
|
|
1394
|
+
if (hi === null) return null;
|
|
1395
|
+
ranges.push([lo, hi]);
|
|
1396
|
+
} else {
|
|
1397
|
+
ranges.push([lo, lo]);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
return ranges.length ? ranges : null;
|
|
1401
|
+
}
|
|
1402
|
+
function literalCodePoints(frag) {
|
|
1403
|
+
const out = [];
|
|
1404
|
+
let i = 0;
|
|
1405
|
+
while (i < frag.length) {
|
|
1406
|
+
const ch = frag[i];
|
|
1407
|
+
if (ch === "\\") {
|
|
1408
|
+
const e = frag[i + 1];
|
|
1409
|
+
if (e === void 0) return null;
|
|
1410
|
+
out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
|
|
1411
|
+
i += 2;
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
if (META.has(ch)) return null;
|
|
1415
|
+
out.push(ch.codePointAt(0));
|
|
1416
|
+
i += 1;
|
|
1417
|
+
}
|
|
1418
|
+
return out.length ? out : null;
|
|
1419
|
+
}
|
|
1420
|
+
function parseScanShape(source) {
|
|
1421
|
+
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1422
|
+
if (m) {
|
|
1423
|
+
if (m[1].startsWith("^")) return null;
|
|
1424
|
+
const ranges = parseClassRanges(m[1]);
|
|
1425
|
+
return ranges ? { kind: "chars", ranges } : null;
|
|
1426
|
+
}
|
|
1427
|
+
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1428
|
+
if (m) {
|
|
1429
|
+
const open = literalCodePoints(m[1]);
|
|
1430
|
+
const stop = parseClassRanges(m[2]);
|
|
1431
|
+
if (open && stop) return { kind: "until", open, stop };
|
|
1432
|
+
return null;
|
|
1433
|
+
}
|
|
1434
|
+
if (!source.includes("\\\\")) {
|
|
1435
|
+
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
1436
|
+
if (m && m[1] && m[2]) {
|
|
1437
|
+
const open = literalCodePoints(m[1]);
|
|
1438
|
+
const close = literalCodePoints(m[2]);
|
|
1439
|
+
if (open && close) return { kind: "delimited", open, close };
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return null;
|
|
1443
|
+
}
|
|
1444
|
+
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1445
|
+
var litCond = (base, cps, firstVar) => cps.map(
|
|
1446
|
+
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1447
|
+
).join(" && ");
|
|
1448
|
+
function advanceExpr(shape) {
|
|
1449
|
+
if (shape.kind === "chars") {
|
|
1450
|
+
return {
|
|
1451
|
+
setup: [
|
|
1452
|
+
` _e++`,
|
|
1453
|
+
` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
|
|
1454
|
+
],
|
|
1455
|
+
endVar: "_e"
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
if (shape.kind === "until") {
|
|
1459
|
+
return {
|
|
1460
|
+
setup: [
|
|
1461
|
+
` let j = _e + ${shape.open.length}`,
|
|
1462
|
+
` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
|
|
1463
|
+
],
|
|
1464
|
+
endVar: "j"
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
const n = shape.close.length;
|
|
1468
|
+
return {
|
|
1469
|
+
setup: [
|
|
1470
|
+
` let j = _e + ${shape.open.length}`,
|
|
1471
|
+
` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
|
|
1472
|
+
],
|
|
1473
|
+
endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
function guardLine(shape) {
|
|
1477
|
+
const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
|
|
1478
|
+
return ` if (${cond}) {`;
|
|
1479
|
+
}
|
|
1480
|
+
function scanBranch(shape) {
|
|
1481
|
+
if (shape.kind === "chars") {
|
|
1482
|
+
return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
|
|
1483
|
+
}
|
|
1484
|
+
const { setup, endVar } = advanceExpr(shape);
|
|
1485
|
+
return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
|
|
1486
|
+
}
|
|
1487
|
+
function captureChunk(startVar, kindIndex) {
|
|
1488
|
+
return [
|
|
1489
|
+
` if (_cap) {`,
|
|
1490
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${startVar}, _e, ${kindIndex})`,
|
|
1491
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${startVar}, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
1492
|
+
` }`
|
|
1493
|
+
];
|
|
1494
|
+
}
|
|
1495
|
+
function scanBranchLabeled(shape, kindIndex) {
|
|
1496
|
+
const { setup, endVar } = advanceExpr(shape);
|
|
1497
|
+
const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
|
|
1498
|
+
if (endVar !== "_e") lines.push(` _e = ${endVar}`);
|
|
1499
|
+
lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
|
|
1500
|
+
return lines.join("\n");
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1366
1503
|
// src/compiler/trivia-fast-path.ts
|
|
1367
|
-
var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
|
|
1368
|
-
var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
|
|
1369
|
-
String.raw`[ \t\n\r\f]+`,
|
|
1370
|
-
String.raw`[ \t\n]+`,
|
|
1371
|
-
String.raw`[ \t]+`,
|
|
1372
|
-
String.raw`[\t\n\f\r ]+`
|
|
1373
|
-
]);
|
|
1374
1504
|
function unwrapTrivia2(p) {
|
|
1375
1505
|
let cur = p;
|
|
1376
1506
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1386,36 +1516,30 @@ function choiceArms(p) {
|
|
|
1386
1516
|
if (p._def.tag === "choice") return p._def.parsers;
|
|
1387
1517
|
return null;
|
|
1388
1518
|
}
|
|
1389
|
-
function
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
function isBlockCommentSource(source) {
|
|
1393
|
-
return source === BLOCK_COMMENT;
|
|
1519
|
+
function armShape(arm) {
|
|
1520
|
+
const src = getCoreRegexDef(arm)?.source;
|
|
1521
|
+
return src ? parseScanShape(src) : null;
|
|
1394
1522
|
}
|
|
1395
1523
|
function analyzeTriviaFastPath(trivia2) {
|
|
1396
1524
|
const core = unwrapTrivia2(trivia2);
|
|
1397
|
-
const
|
|
1398
|
-
if (
|
|
1399
|
-
|
|
1400
|
-
return null;
|
|
1525
|
+
const directSrc = getCoreRegexDef(core)?.source;
|
|
1526
|
+
if (directSrc) {
|
|
1527
|
+
const shape = parseScanShape(directSrc);
|
|
1528
|
+
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1401
1529
|
}
|
|
1402
1530
|
const inner = oneOrMoreInner(core);
|
|
1403
1531
|
if (!inner) return null;
|
|
1404
|
-
const
|
|
1405
|
-
if (
|
|
1532
|
+
const innerShape = armShape(inner);
|
|
1533
|
+
if (innerShape) return innerShape.kind === "chars" ? [innerShape] : null;
|
|
1406
1534
|
const arms = choiceArms(inner);
|
|
1407
|
-
if (!arms || arms.length
|
|
1408
|
-
|
|
1409
|
-
let hasComment = false;
|
|
1535
|
+
if (!arms || arms.length < 2) return null;
|
|
1536
|
+
const shapes = [];
|
|
1410
1537
|
for (const arm of arms) {
|
|
1411
|
-
const
|
|
1412
|
-
if (!
|
|
1413
|
-
|
|
1414
|
-
else if (isBlockCommentSource(src)) hasComment = true;
|
|
1415
|
-
else return null;
|
|
1538
|
+
const shape = armShape(arm);
|
|
1539
|
+
if (!shape) return null;
|
|
1540
|
+
shapes.push(shape);
|
|
1416
1541
|
}
|
|
1417
|
-
|
|
1418
|
-
return null;
|
|
1542
|
+
return shapes;
|
|
1419
1543
|
}
|
|
1420
1544
|
var CAP_RECORD = [
|
|
1421
1545
|
` if (_cap && _e > _pos) {`,
|
|
@@ -1423,80 +1547,49 @@ var CAP_RECORD = [
|
|
|
1423
1547
|
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
|
|
1424
1548
|
` }`
|
|
1425
1549
|
].join("\n");
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1438
|
-
` let j = _e + 2`,
|
|
1439
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1440
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1441
|
-
` continue`,
|
|
1442
|
-
` }`,
|
|
1443
|
-
` break`,
|
|
1444
|
-
` }`
|
|
1445
|
-
].join("\n");
|
|
1446
|
-
var CAP_CHUNK = (wsKind, commentKind) => [
|
|
1447
|
-
` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
|
|
1448
|
-
` const _cs = _e`,
|
|
1449
|
-
` _e++`,
|
|
1450
|
-
` while (_e < input.length) {`,
|
|
1451
|
-
` const c2 = input.charCodeAt(_e)`,
|
|
1452
|
-
` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
|
|
1453
|
-
` else break`,
|
|
1454
|
-
` }`,
|
|
1455
|
-
` if (_cap) {`,
|
|
1456
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
|
|
1457
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
|
|
1458
|
-
` }`,
|
|
1459
|
-
` continue`,
|
|
1460
|
-
` }`,
|
|
1461
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1462
|
-
` const _cs = _e`,
|
|
1463
|
-
` let j = _e + 2`,
|
|
1464
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1465
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1466
|
-
` if (_cap) {`,
|
|
1467
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
|
|
1468
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
|
|
1469
|
-
` }`,
|
|
1470
|
-
` continue`,
|
|
1471
|
-
` }`
|
|
1472
|
-
].join("\n");
|
|
1473
|
-
var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
|
|
1474
|
-
` while (_e < input.length) {`,
|
|
1475
|
-
` const c = input.charCodeAt(_e)`,
|
|
1476
|
-
CAP_CHUNK(wsKind, commentKind),
|
|
1477
|
-
` break`,
|
|
1478
|
-
` }`
|
|
1479
|
-
].join("\n");
|
|
1480
|
-
function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
|
|
1481
|
-
const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
|
|
1482
|
-
const cap = kindIndices ? "" : CAP_RECORD;
|
|
1483
|
-
const lines = [
|
|
1550
|
+
function composeFastLoop(shapes) {
|
|
1551
|
+
return [
|
|
1552
|
+
` while (_e < input.length) {`,
|
|
1553
|
+
` const c = input.charCodeAt(_e)`,
|
|
1554
|
+
...shapes.map(scanBranch),
|
|
1555
|
+
` break`,
|
|
1556
|
+
` }`
|
|
1557
|
+
].join("\n");
|
|
1558
|
+
}
|
|
1559
|
+
function buildFastTriviaFnDecl(fnName, shapes) {
|
|
1560
|
+
return [
|
|
1484
1561
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1485
1562
|
` let _e = _pos`,
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1563
|
+
composeFastLoop(shapes),
|
|
1564
|
+
CAP_RECORD,
|
|
1565
|
+
` return _e`,
|
|
1566
|
+
`}`
|
|
1567
|
+
].join("\n");
|
|
1491
1568
|
}
|
|
1492
|
-
function
|
|
1569
|
+
function analyzeLabeledScannableRun(trivia2) {
|
|
1570
|
+
const spec = analyzeLabeledTrivia(trivia2);
|
|
1571
|
+
if (!spec) return null;
|
|
1572
|
+
const out = [];
|
|
1493
1573
|
for (const arm of spec.arms) {
|
|
1494
1574
|
const src = getCoreRegexDef(arm.parser)?.source;
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1575
|
+
const shape = src ? parseScanShape(src) : null;
|
|
1576
|
+
if (!shape) return null;
|
|
1577
|
+
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1498
1578
|
}
|
|
1499
|
-
return
|
|
1579
|
+
return out;
|
|
1580
|
+
}
|
|
1581
|
+
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
1582
|
+
return [
|
|
1583
|
+
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1584
|
+
` let _e = _pos`,
|
|
1585
|
+
` while (_e < input.length) {`,
|
|
1586
|
+
` const c = input.charCodeAt(_e)`,
|
|
1587
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
1588
|
+
` break`,
|
|
1589
|
+
` }`,
|
|
1590
|
+
` return _e`,
|
|
1591
|
+
`}`
|
|
1592
|
+
].join("\n");
|
|
1500
1593
|
}
|
|
1501
1594
|
function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
1502
1595
|
const tryArms = spec.arms.map((arm, i) => {
|
|
@@ -1529,14 +1622,6 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
|
1529
1622
|
`}`
|
|
1530
1623
|
].join("\n");
|
|
1531
1624
|
}
|
|
1532
|
-
function labeledTriviaKindIndices(trivia2) {
|
|
1533
|
-
const spec = analyzeLabeledTrivia(trivia2);
|
|
1534
|
-
if (!spec || spec.arms.length !== 2) return null;
|
|
1535
|
-
const ws = regexArmIndex(spec, false);
|
|
1536
|
-
const comment = regexArmIndex(spec, true);
|
|
1537
|
-
if (ws === null || comment === null) return null;
|
|
1538
|
-
return { ws, comment };
|
|
1539
|
-
}
|
|
1540
1625
|
function labeledTriviaRegexArms(trivia2) {
|
|
1541
1626
|
const spec = analyzeLabeledTrivia(trivia2);
|
|
1542
1627
|
if (!spec) return null;
|
|
@@ -1784,14 +1869,19 @@ function ensureTriviaFn(ctx) {
|
|
|
1784
1869
|
const fnName = `_tf${ctx.triviaFnNames.size}`;
|
|
1785
1870
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
1786
1871
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
1787
|
-
const fastKind = analyzeTriviaFastPath(trivia2);
|
|
1788
|
-
const kindIndices = labeledTriviaKindIndices(trivia2);
|
|
1789
|
-
if (fastKind) {
|
|
1790
|
-
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
|
|
1791
|
-
return fnName;
|
|
1792
|
-
}
|
|
1793
1872
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
1794
|
-
if (labeledSpec) {
|
|
1873
|
+
if (!labeledSpec) {
|
|
1874
|
+
const fastShapes = analyzeTriviaFastPath(trivia2);
|
|
1875
|
+
if (fastShapes) {
|
|
1876
|
+
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastShapes));
|
|
1877
|
+
return fnName;
|
|
1878
|
+
}
|
|
1879
|
+
} else {
|
|
1880
|
+
const labeledShapes = analyzeLabeledScannableRun(trivia2);
|
|
1881
|
+
if (labeledShapes) {
|
|
1882
|
+
ctx.namedFnDecls.push(buildLabeledScannableTriviaFnDecl(fnName, labeledShapes));
|
|
1883
|
+
return fnName;
|
|
1884
|
+
}
|
|
1795
1885
|
const regexSpec = labeledTriviaRegexArms(trivia2);
|
|
1796
1886
|
if (regexSpec) {
|
|
1797
1887
|
const reNames = [];
|
|
@@ -1851,6 +1941,119 @@ function firstSetCond(codeVar, fs) {
|
|
|
1851
1941
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1852
1942
|
).join(" || ");
|
|
1853
1943
|
}
|
|
1944
|
+
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1945
|
+
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1946
|
+
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
1947
|
+
}
|
|
1948
|
+
function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
1949
|
+
if (seen.has(p)) return false;
|
|
1950
|
+
seen.add(p);
|
|
1951
|
+
const d = p._def;
|
|
1952
|
+
switch (d.tag) {
|
|
1953
|
+
// Atomic / non-capturing-then-failing: a failure happens before any push.
|
|
1954
|
+
case "literal":
|
|
1955
|
+
case "regex":
|
|
1956
|
+
case "keywords":
|
|
1957
|
+
case "guard":
|
|
1958
|
+
case "not":
|
|
1959
|
+
case "trivia":
|
|
1960
|
+
case "scanTo":
|
|
1961
|
+
case "unknown":
|
|
1962
|
+
return false;
|
|
1963
|
+
// node() captures into its own private buffers and rolls them back on
|
|
1964
|
+
// failure (emitNode restores _ctx.* and never pushes on the !ok path).
|
|
1965
|
+
case "node":
|
|
1966
|
+
return false;
|
|
1967
|
+
// choice/firstMatch already roll back each failed arm; on overall failure
|
|
1968
|
+
// nothing committed remains.
|
|
1969
|
+
case "choice":
|
|
1970
|
+
return false;
|
|
1971
|
+
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
1972
|
+
case "optional":
|
|
1973
|
+
case "many":
|
|
1974
|
+
case "oneOrMore":
|
|
1975
|
+
return false;
|
|
1976
|
+
// sepBy emits its own per-iteration rollback in emitSepBy.
|
|
1977
|
+
case "sepBy":
|
|
1978
|
+
return false;
|
|
1979
|
+
// A sequence is the real case: an earlier capturing term followed by a term
|
|
1980
|
+
// that can fail leaves the earlier captures buffered.
|
|
1981
|
+
case "sequence": {
|
|
1982
|
+
const parts = d.parsers;
|
|
1983
|
+
if (parts.length < 2) return parts.some((x) => mayLeavePartialCapture(x, seen));
|
|
1984
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1985
|
+
if (hasNodeDef(parts[i]) || capturesLeaf(parts[i])) return true;
|
|
1986
|
+
}
|
|
1987
|
+
return false;
|
|
1988
|
+
}
|
|
1989
|
+
// Delegating wrappers: defer to the wrapped parser.
|
|
1990
|
+
case "transform":
|
|
1991
|
+
case "label":
|
|
1992
|
+
case "expect":
|
|
1993
|
+
case "withCtx":
|
|
1994
|
+
case "grammar":
|
|
1995
|
+
return mayLeavePartialCapture(d.parser, seen);
|
|
1996
|
+
case "skip":
|
|
1997
|
+
return mayLeavePartialCapture(d.main, seen);
|
|
1998
|
+
case "recover":
|
|
1999
|
+
return mayLeavePartialCapture(d.parser, seen);
|
|
2000
|
+
case "lazy": {
|
|
2001
|
+
try {
|
|
2002
|
+
return mayLeavePartialCapture(d.thunk(), seen);
|
|
2003
|
+
} catch {
|
|
2004
|
+
return true;
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
// Unknown shapes: be safe and keep the rollback.
|
|
2008
|
+
default:
|
|
2009
|
+
return true;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
2013
|
+
if (seen.has(p)) return false;
|
|
2014
|
+
seen.add(p);
|
|
2015
|
+
const d = p._def;
|
|
2016
|
+
switch (d.tag) {
|
|
2017
|
+
case "literal":
|
|
2018
|
+
case "regex":
|
|
2019
|
+
case "keywords":
|
|
2020
|
+
case "node":
|
|
2021
|
+
return true;
|
|
2022
|
+
case "not":
|
|
2023
|
+
case "guard":
|
|
2024
|
+
case "trivia":
|
|
2025
|
+
case "unknown":
|
|
2026
|
+
return false;
|
|
2027
|
+
case "sequence":
|
|
2028
|
+
case "choice":
|
|
2029
|
+
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
2030
|
+
case "sepBy":
|
|
2031
|
+
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
2032
|
+
case "many":
|
|
2033
|
+
case "oneOrMore":
|
|
2034
|
+
case "optional":
|
|
2035
|
+
case "transform":
|
|
2036
|
+
case "label":
|
|
2037
|
+
case "expect":
|
|
2038
|
+
case "withCtx":
|
|
2039
|
+
case "grammar":
|
|
2040
|
+
case "recover":
|
|
2041
|
+
return capturesLeaf(d.parser, seen);
|
|
2042
|
+
case "skip":
|
|
2043
|
+
return capturesLeaf(d.main, seen);
|
|
2044
|
+
case "scanTo":
|
|
2045
|
+
return true;
|
|
2046
|
+
case "lazy": {
|
|
2047
|
+
try {
|
|
2048
|
+
return capturesLeaf(d.thunk(), seen);
|
|
2049
|
+
} catch {
|
|
2050
|
+
return true;
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
default:
|
|
2054
|
+
return true;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
1854
2057
|
function emitFallible(inner, ctx, pos) {
|
|
1855
2058
|
const lbl = v(ctx, "_lbl");
|
|
1856
2059
|
const okV = `${lbl}ok`;
|
|
@@ -1864,26 +2067,23 @@ function emitFallible(inner, ctx, pos) {
|
|
|
1864
2067
|
ctx.failLabel = savedLabel;
|
|
1865
2068
|
ctx.indent = savedIndent;
|
|
1866
2069
|
const ind0 = ind(ctx);
|
|
1867
|
-
const
|
|
1868
|
-
const
|
|
1869
|
-
const
|
|
1870
|
-
const
|
|
2070
|
+
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
2071
|
+
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
2072
|
+
const mR = needsRollback ? v(ctx, "_fcr") : null;
|
|
2073
|
+
const mTl = needsRollback ? v(ctx, "_fctl") : null;
|
|
1871
2074
|
const stmts = [
|
|
1872
2075
|
`${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
|
|
1873
2076
|
...mL ? [
|
|
1874
2077
|
`${ind0}const ${mL} = _ctx._cstLeaves?.length ?? 0`,
|
|
1875
2078
|
`${ind0}const ${mR} = _ctx._cstRawChildren?.length ?? 0`,
|
|
1876
|
-
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0
|
|
1877
|
-
`${ind0}const ${mLg} = _ctx._triviaLog?.length ?? 0`
|
|
2079
|
+
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`
|
|
1878
2080
|
] : [],
|
|
1879
2081
|
`${ind0}${lbl}: {`,
|
|
1880
2082
|
...r.stmts,
|
|
1881
|
-
`${ind0} ${valV} = ${r.valueVar}`,
|
|
1882
|
-
`${ind0} ${endV} = ${r.endVar}`,
|
|
1883
|
-
`${ind0} ${okV} = true`,
|
|
2083
|
+
`${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
|
|
1884
2084
|
`${ind0}}`,
|
|
1885
2085
|
...mL ? [
|
|
1886
|
-
`${ind0}if (!${okV}) {
|
|
2086
|
+
`${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null)} }`
|
|
1887
2087
|
] : []
|
|
1888
2088
|
];
|
|
1889
2089
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
@@ -2119,11 +2319,11 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2119
2319
|
const p = def.parsers[idx];
|
|
2120
2320
|
const litVal = getCoreLiteralValue(p);
|
|
2121
2321
|
const litLen = litVal.length;
|
|
2122
|
-
const
|
|
2322
|
+
const litCond2 = emitLiteralCondition(litVal, pos);
|
|
2123
2323
|
const kw = first ? "if" : "else if";
|
|
2124
2324
|
first = false;
|
|
2125
2325
|
const litEnd = `${pos} + ${litLen}`;
|
|
2126
|
-
stmts.push(`${ind(ctx)}${kw} (${
|
|
2326
|
+
stmts.push(`${ind(ctx)}${kw} (${litCond2}) {`);
|
|
2127
2327
|
ctx.indent++;
|
|
2128
2328
|
const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
|
|
2129
2329
|
stmts.push(
|
|
@@ -2157,11 +2357,13 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2157
2357
|
gateCond = `_mf[${gateIdx}](_ctx.state)`;
|
|
2158
2358
|
}
|
|
2159
2359
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
2160
|
-
const
|
|
2161
|
-
const
|
|
2162
|
-
const
|
|
2163
|
-
const
|
|
2164
|
-
const
|
|
2360
|
+
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
2361
|
+
const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
|
|
2362
|
+
const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
|
|
2363
|
+
const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
|
|
2364
|
+
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2365
|
+
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2366
|
+
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
2165
2367
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2166
2368
|
ctx.indent++;
|
|
2167
2369
|
const ind1 = ind(ctx);
|
|
@@ -2191,11 +2393,7 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2191
2393
|
stmts.push(`${ind0} }`);
|
|
2192
2394
|
if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
|
|
2193
2395
|
} else {
|
|
2194
|
-
stmts.push(`${ind0} if (${okVar}) {`);
|
|
2195
|
-
stmts.push(`${ind0} ${resValV} = ${valVar}`);
|
|
2196
|
-
stmts.push(`${ind0} ${resEndV} = ${endVar}`);
|
|
2197
|
-
stmts.push(`${ind0} ${resOkV} = true`);
|
|
2198
|
-
stmts.push(`${ind0} }`);
|
|
2396
|
+
stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
2199
2397
|
if (rollback) stmts.push(`${ind0} else { ${rollback} }`);
|
|
2200
2398
|
}
|
|
2201
2399
|
stmts.push(`${ind0}}`);
|
|
@@ -2493,6 +2691,16 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2493
2691
|
return { stmts, valueVar: vv, endVar: ev };
|
|
2494
2692
|
}
|
|
2495
2693
|
function emitLazy(p, def, ctx, pos) {
|
|
2694
|
+
const usage = ctx.lazyUsage;
|
|
2695
|
+
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
2696
|
+
let resolved;
|
|
2697
|
+
try {
|
|
2698
|
+
resolved = def.thunk();
|
|
2699
|
+
} catch {
|
|
2700
|
+
return emitRuntimeFallback(p, ctx, pos);
|
|
2701
|
+
}
|
|
2702
|
+
return emit(resolved, ctx, pos);
|
|
2703
|
+
}
|
|
2496
2704
|
if (!ctx.namedParsers.has(p)) {
|
|
2497
2705
|
const fnName2 = `_pf${ctx.namedParsers.size}`;
|
|
2498
2706
|
ctx.namedParsers.set(p, fnName2);
|
|
@@ -2540,6 +2748,22 @@ function emitRecover(def, ctx, pos) {
|
|
|
2540
2748
|
];
|
|
2541
2749
|
return { stmts, valueVar: valVar, endVar };
|
|
2542
2750
|
}
|
|
2751
|
+
function emitExpect(def, ctx, pos) {
|
|
2752
|
+
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2753
|
+
const ind0 = ind(ctx);
|
|
2754
|
+
const errV = v(ctx, "_err");
|
|
2755
|
+
const stmts = [
|
|
2756
|
+
...innerStmts,
|
|
2757
|
+
`${ind0}if (!${okVar}) {`,
|
|
2758
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
|
|
2759
|
+
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2760
|
+
`${ind0} ${valVar} = ${errV}`,
|
|
2761
|
+
`${ind0} ${endVar} = ${pos}`,
|
|
2762
|
+
`${ind0} ${okVar} = true`,
|
|
2763
|
+
`${ind0}}`
|
|
2764
|
+
];
|
|
2765
|
+
return { stmts, valueVar: valVar, endVar };
|
|
2766
|
+
}
|
|
2543
2767
|
function emit(p, ctx, pos) {
|
|
2544
2768
|
const def = p._def;
|
|
2545
2769
|
switch (def.tag) {
|
|
@@ -2648,6 +2872,8 @@ function emit(p, ctx, pos) {
|
|
|
2648
2872
|
return emitScanTo(def, ctx, pos);
|
|
2649
2873
|
case "recover":
|
|
2650
2874
|
return emitRecover(def, ctx, pos);
|
|
2875
|
+
case "expect":
|
|
2876
|
+
return emitExpect(def, ctx, pos);
|
|
2651
2877
|
case "guard": {
|
|
2652
2878
|
const fnIdx = ctx.mapFns.length;
|
|
2653
2879
|
ctx.mapFns.push(def.predicate);
|
|
@@ -2731,12 +2957,88 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2731
2957
|
return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen));
|
|
2732
2958
|
case "recover":
|
|
2733
2959
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
|
|
2960
|
+
case "expect":
|
|
2961
|
+
return hasNodeDef(d.parser, seen);
|
|
2734
2962
|
case "withCtx":
|
|
2735
2963
|
return hasNodeDef(d.parser, seen);
|
|
2736
2964
|
default:
|
|
2737
2965
|
return false;
|
|
2738
2966
|
}
|
|
2739
2967
|
}
|
|
2968
|
+
function childrenOf(def) {
|
|
2969
|
+
switch (def.tag) {
|
|
2970
|
+
case "sequence":
|
|
2971
|
+
case "choice":
|
|
2972
|
+
return def.parsers;
|
|
2973
|
+
case "many":
|
|
2974
|
+
case "optional":
|
|
2975
|
+
case "transform":
|
|
2976
|
+
case "trivia":
|
|
2977
|
+
case "label":
|
|
2978
|
+
case "grammar":
|
|
2979
|
+
case "not":
|
|
2980
|
+
case "node":
|
|
2981
|
+
case "withCtx":
|
|
2982
|
+
case "expect":
|
|
2983
|
+
return [def.parser];
|
|
2984
|
+
// emitMany's oneOrMore branch and emitSepBy both codegen `def.parser` TWICE
|
|
2985
|
+
// (a mandatory first match, then again inside the repeat loop) — two real
|
|
2986
|
+
// emit() call sites, so the usage analysis must see two edges here or it
|
|
2987
|
+
// undercounts a single-use `parser` ref as inline-safe when it's actually
|
|
2988
|
+
// referenced from two positions within this one compiled function.
|
|
2989
|
+
case "oneOrMore":
|
|
2990
|
+
return [def.parser, def.parser];
|
|
2991
|
+
case "sepBy":
|
|
2992
|
+
return [def.parser, def.parser, def.separator];
|
|
2993
|
+
case "skip":
|
|
2994
|
+
return [def.main, def.skipped];
|
|
2995
|
+
case "recover":
|
|
2996
|
+
return [def.parser, def.sentinel];
|
|
2997
|
+
case "scanTo":
|
|
2998
|
+
return [def.sentinel, ...def.skip];
|
|
2999
|
+
case "lazy":
|
|
3000
|
+
case "literal":
|
|
3001
|
+
case "regex":
|
|
3002
|
+
case "keywords":
|
|
3003
|
+
case "guard":
|
|
3004
|
+
case "unknown":
|
|
3005
|
+
return [];
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
function analyzeLazyUsage(root) {
|
|
3009
|
+
return analyzeLazyUsageMulti([root]);
|
|
3010
|
+
}
|
|
3011
|
+
function analyzeLazyUsageMulti(roots) {
|
|
3012
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3013
|
+
const recursive = /* @__PURE__ */ new Set();
|
|
3014
|
+
const descended = /* @__PURE__ */ new Set();
|
|
3015
|
+
const active = /* @__PURE__ */ new Set();
|
|
3016
|
+
function walk(p) {
|
|
3017
|
+
const def = p._def;
|
|
3018
|
+
if (def.tag === "lazy") {
|
|
3019
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
3020
|
+
if (active.has(p)) {
|
|
3021
|
+
recursive.add(p);
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
3024
|
+
if (descended.has(p)) return;
|
|
3025
|
+
descended.add(p);
|
|
3026
|
+
let resolved;
|
|
3027
|
+
try {
|
|
3028
|
+
resolved = def.thunk();
|
|
3029
|
+
} catch {
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
active.add(p);
|
|
3033
|
+
walk(resolved);
|
|
3034
|
+
active.delete(p);
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
for (const child of childrenOf(def)) walk(child);
|
|
3038
|
+
}
|
|
3039
|
+
for (const root of roots) walk(root);
|
|
3040
|
+
return { counts, recursive };
|
|
3041
|
+
}
|
|
2740
3042
|
function compile(combinator, mapFnSources) {
|
|
2741
3043
|
const ctx = {
|
|
2742
3044
|
vars: 0,
|
|
@@ -2753,7 +3055,8 @@ function compile(combinator, mapFnSources) {
|
|
|
2753
3055
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
2754
3056
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
2755
3057
|
namedFnDecls: [],
|
|
2756
|
-
capturing: hasNodeDef(combinator)
|
|
3058
|
+
capturing: hasNodeDef(combinator),
|
|
3059
|
+
lazyUsage: analyzeLazyUsage(combinator)
|
|
2757
3060
|
};
|
|
2758
3061
|
const r = emit(combinator, ctx, "_pos");
|
|
2759
3062
|
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
@@ -2799,6 +3102,11 @@ function compile(combinator, mapFnSources) {
|
|
|
2799
3102
|
parseWithContext(input, parseCtx, pos = 0) {
|
|
2800
3103
|
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
|
|
2801
3104
|
},
|
|
3105
|
+
// Note: collects recover()/expect() errors via _errors. Unlike interpreter
|
|
3106
|
+
// parse({recover:true}) it does NOT populate furthestFail — the compiled path
|
|
3107
|
+
// inlines failures for throughput and deliberately skips _probe bookkeeping.
|
|
3108
|
+
// Callers wanting a furthest-position diagnostic detect unconsumed input
|
|
3109
|
+
// (span.end < input.length) instead, which is mode-agnostic.
|
|
2802
3110
|
parseWithErrors(input, pos = 0) {
|
|
2803
3111
|
const errors = [];
|
|
2804
3112
|
const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
|
|
@@ -2910,6 +3218,58 @@ function isParseError(value) {
|
|
|
2910
3218
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
2911
3219
|
}
|
|
2912
3220
|
|
|
3221
|
+
// src/combinators/expect.ts
|
|
3222
|
+
function staticExpected(c) {
|
|
3223
|
+
const def = c._def;
|
|
3224
|
+
switch (def.tag) {
|
|
3225
|
+
case "literal":
|
|
3226
|
+
return [JSON.stringify(def.value)];
|
|
3227
|
+
case "regex":
|
|
3228
|
+
return [`/${def.source}/`];
|
|
3229
|
+
case "keywords":
|
|
3230
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
3231
|
+
case "label":
|
|
3232
|
+
return [def.label];
|
|
3233
|
+
case "choice":
|
|
3234
|
+
return def.parsers.flatMap(staticExpected);
|
|
3235
|
+
case "sequence":
|
|
3236
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
3237
|
+
case "node":
|
|
3238
|
+
case "grammar":
|
|
3239
|
+
case "trivia":
|
|
3240
|
+
case "optional":
|
|
3241
|
+
case "many":
|
|
3242
|
+
case "oneOrMore":
|
|
3243
|
+
case "transform":
|
|
3244
|
+
case "not":
|
|
3245
|
+
return staticExpected(def.parser);
|
|
3246
|
+
case "lazy":
|
|
3247
|
+
return staticExpected(def.thunk());
|
|
3248
|
+
default:
|
|
3249
|
+
return [];
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
function expect(combinator, label2) {
|
|
3253
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
3254
|
+
const meta = {
|
|
3255
|
+
firstSet: combinator._meta.firstSet,
|
|
3256
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
3257
|
+
isTrivia: false
|
|
3258
|
+
};
|
|
3259
|
+
return {
|
|
3260
|
+
_tag: "expect",
|
|
3261
|
+
_meta: meta,
|
|
3262
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
3263
|
+
parse(input, pos, ctx) {
|
|
3264
|
+
const result = combinator.parse(input, pos, ctx);
|
|
3265
|
+
if (result.ok) return result;
|
|
3266
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
3267
|
+
ctx._errors?.push(error);
|
|
3268
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
3269
|
+
}
|
|
3270
|
+
};
|
|
3271
|
+
}
|
|
3272
|
+
|
|
2913
3273
|
// src/combinators/completions.ts
|
|
2914
3274
|
function completionsAt(combinator, input, offset) {
|
|
2915
3275
|
const probe = { offset, best: null };
|
|
@@ -2938,7 +3298,11 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
2938
3298
|
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
2939
3299
|
parse(input, pos, ctx) {
|
|
2940
3300
|
let cur = pos;
|
|
2941
|
-
const probeCtx = {
|
|
3301
|
+
const probeCtx = {
|
|
3302
|
+
trackLines: ctx.trackLines,
|
|
3303
|
+
state: ctx.state,
|
|
3304
|
+
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
3305
|
+
};
|
|
2942
3306
|
const emit2 = (end) => {
|
|
2943
3307
|
if (end > pos && cstCaptureActive(ctx)) {
|
|
2944
3308
|
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
@@ -2974,20 +3338,46 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
2974
3338
|
}
|
|
2975
3339
|
function balanced(open, close, options = {}) {
|
|
2976
3340
|
const self = ref();
|
|
2977
|
-
const
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
3341
|
+
const skips = options.skip ?? [];
|
|
3342
|
+
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
3343
|
+
let bounded = true;
|
|
3344
|
+
for (const sk of skips) {
|
|
3345
|
+
const cs = firstSetClassChars(sk);
|
|
3346
|
+
if (cs === null) {
|
|
3347
|
+
bounded = false;
|
|
3348
|
+
break;
|
|
3349
|
+
}
|
|
3350
|
+
for (const ch of cs) stop.add(ch);
|
|
3351
|
+
}
|
|
3352
|
+
const cls = [...stop].map(escapeClassChar).join("");
|
|
3353
|
+
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
3354
|
+
const inner = many(choice(self, ...skips, content));
|
|
2981
3355
|
const combi = transform(
|
|
2982
|
-
sequence(literal(open), inner, literal(close)),
|
|
2983
|
-
(
|
|
3356
|
+
sequence(literal(open), inner, expect(literal(close), close)),
|
|
3357
|
+
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
3358
|
+
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
3359
|
+
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
2984
3360
|
);
|
|
2985
3361
|
if (combi._def.tag === "transform") {
|
|
2986
|
-
combi._def.fnSrc =
|
|
3362
|
+
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
2987
3363
|
}
|
|
2988
3364
|
self.define(combi);
|
|
2989
3365
|
return combi;
|
|
2990
3366
|
}
|
|
3367
|
+
function firstSetClassChars(c) {
|
|
3368
|
+
const fs = c._meta.firstSet;
|
|
3369
|
+
if (fs.kind === "empty") return [];
|
|
3370
|
+
if (fs.kind !== "ranges") return null;
|
|
3371
|
+
const out = [];
|
|
3372
|
+
for (const { lo, hi } of fs.ranges) {
|
|
3373
|
+
if (hi - lo > 8) return null;
|
|
3374
|
+
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3375
|
+
}
|
|
3376
|
+
return out;
|
|
3377
|
+
}
|
|
3378
|
+
function escapeClassChar(ch) {
|
|
3379
|
+
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3380
|
+
}
|
|
2991
3381
|
|
|
2992
3382
|
// src/functional/doc.ts
|
|
2993
3383
|
function isNode(x) {
|