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.js
CHANGED
|
@@ -1278,13 +1278,15 @@ function noTrivia(root) {
|
|
|
1278
1278
|
function parse(combinator, input, opts = {}) {
|
|
1279
1279
|
const trackLines = opts.trackLines ?? false;
|
|
1280
1280
|
const _errors = opts.recover ? [] : void 0;
|
|
1281
|
+
const _probe = opts.recover ? { offset: input.length, best: null } : void 0;
|
|
1281
1282
|
const ctx = {
|
|
1282
1283
|
trackLines,
|
|
1283
|
-
..._errors !== void 0 ? { _errors } : {}
|
|
1284
|
+
..._errors !== void 0 ? { _errors } : {},
|
|
1285
|
+
..._probe !== void 0 ? { _probe } : {}
|
|
1284
1286
|
};
|
|
1285
1287
|
const result = combinator.parse(input, 0, ctx);
|
|
1286
1288
|
if (!result.ok) return result;
|
|
1287
|
-
const withErrors = _errors !== void 0 ? { ...result, errors: _errors } : result;
|
|
1289
|
+
const withErrors = _errors !== void 0 ? { ...result, errors: _errors, furthestFail: _probe?.best ?? null } : result;
|
|
1288
1290
|
if (trackLines) {
|
|
1289
1291
|
const idx = buildLineIndex(input);
|
|
1290
1292
|
return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
|
|
@@ -1292,14 +1294,140 @@ function parse(combinator, input, opts = {}) {
|
|
|
1292
1294
|
return withErrors;
|
|
1293
1295
|
}
|
|
1294
1296
|
|
|
1297
|
+
// src/compiler/scannable-run.ts
|
|
1298
|
+
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1299
|
+
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1300
|
+
function parseClassRanges(body) {
|
|
1301
|
+
const ranges = [];
|
|
1302
|
+
let i = 0;
|
|
1303
|
+
const readCp = () => {
|
|
1304
|
+
const ch = body[i];
|
|
1305
|
+
if (ch === void 0) return null;
|
|
1306
|
+
if (ch === "\\") {
|
|
1307
|
+
const e = body[i + 1];
|
|
1308
|
+
if (e === void 0) return null;
|
|
1309
|
+
i += 2;
|
|
1310
|
+
return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
|
|
1311
|
+
}
|
|
1312
|
+
i += ch.length;
|
|
1313
|
+
return ch.codePointAt(0);
|
|
1314
|
+
};
|
|
1315
|
+
while (i < body.length) {
|
|
1316
|
+
const lo = readCp();
|
|
1317
|
+
if (lo === null) return null;
|
|
1318
|
+
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1319
|
+
i += 1;
|
|
1320
|
+
const hi = readCp();
|
|
1321
|
+
if (hi === null) return null;
|
|
1322
|
+
ranges.push([lo, hi]);
|
|
1323
|
+
} else {
|
|
1324
|
+
ranges.push([lo, lo]);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
return ranges.length ? ranges : null;
|
|
1328
|
+
}
|
|
1329
|
+
function literalCodePoints(frag) {
|
|
1330
|
+
const out = [];
|
|
1331
|
+
let i = 0;
|
|
1332
|
+
while (i < frag.length) {
|
|
1333
|
+
const ch = frag[i];
|
|
1334
|
+
if (ch === "\\") {
|
|
1335
|
+
const e = frag[i + 1];
|
|
1336
|
+
if (e === void 0) return null;
|
|
1337
|
+
out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
|
|
1338
|
+
i += 2;
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
if (META.has(ch)) return null;
|
|
1342
|
+
out.push(ch.codePointAt(0));
|
|
1343
|
+
i += 1;
|
|
1344
|
+
}
|
|
1345
|
+
return out.length ? out : null;
|
|
1346
|
+
}
|
|
1347
|
+
function parseScanShape(source) {
|
|
1348
|
+
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1349
|
+
if (m) {
|
|
1350
|
+
if (m[1].startsWith("^")) return null;
|
|
1351
|
+
const ranges = parseClassRanges(m[1]);
|
|
1352
|
+
return ranges ? { kind: "chars", ranges } : null;
|
|
1353
|
+
}
|
|
1354
|
+
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1355
|
+
if (m) {
|
|
1356
|
+
const open = literalCodePoints(m[1]);
|
|
1357
|
+
const stop = parseClassRanges(m[2]);
|
|
1358
|
+
if (open && stop) return { kind: "until", open, stop };
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
if (!source.includes("\\\\")) {
|
|
1362
|
+
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
1363
|
+
if (m && m[1] && m[2]) {
|
|
1364
|
+
const open = literalCodePoints(m[1]);
|
|
1365
|
+
const close = literalCodePoints(m[2]);
|
|
1366
|
+
if (open && close) return { kind: "delimited", open, close };
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1372
|
+
var litCond = (base, cps, firstVar) => cps.map(
|
|
1373
|
+
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1374
|
+
).join(" && ");
|
|
1375
|
+
function advanceExpr(shape) {
|
|
1376
|
+
if (shape.kind === "chars") {
|
|
1377
|
+
return {
|
|
1378
|
+
setup: [
|
|
1379
|
+
` _e++`,
|
|
1380
|
+
` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
|
|
1381
|
+
],
|
|
1382
|
+
endVar: "_e"
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
if (shape.kind === "until") {
|
|
1386
|
+
return {
|
|
1387
|
+
setup: [
|
|
1388
|
+
` let j = _e + ${shape.open.length}`,
|
|
1389
|
+
` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
|
|
1390
|
+
],
|
|
1391
|
+
endVar: "j"
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
const n = shape.close.length;
|
|
1395
|
+
return {
|
|
1396
|
+
setup: [
|
|
1397
|
+
` let j = _e + ${shape.open.length}`,
|
|
1398
|
+
` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
|
|
1399
|
+
],
|
|
1400
|
+
endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
function guardLine(shape) {
|
|
1404
|
+
const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
|
|
1405
|
+
return ` if (${cond}) {`;
|
|
1406
|
+
}
|
|
1407
|
+
function scanBranch(shape) {
|
|
1408
|
+
if (shape.kind === "chars") {
|
|
1409
|
+
return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
|
|
1410
|
+
}
|
|
1411
|
+
const { setup, endVar } = advanceExpr(shape);
|
|
1412
|
+
return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
|
|
1413
|
+
}
|
|
1414
|
+
function captureChunk(startVar, kindIndex) {
|
|
1415
|
+
return [
|
|
1416
|
+
` if (_cap) {`,
|
|
1417
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${startVar}, _e, ${kindIndex})`,
|
|
1418
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${startVar}, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
1419
|
+
` }`
|
|
1420
|
+
];
|
|
1421
|
+
}
|
|
1422
|
+
function scanBranchLabeled(shape, kindIndex) {
|
|
1423
|
+
const { setup, endVar } = advanceExpr(shape);
|
|
1424
|
+
const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
|
|
1425
|
+
if (endVar !== "_e") lines.push(` _e = ${endVar}`);
|
|
1426
|
+
lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
|
|
1427
|
+
return lines.join("\n");
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1295
1430
|
// src/compiler/trivia-fast-path.ts
|
|
1296
|
-
var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
|
|
1297
|
-
var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
|
|
1298
|
-
String.raw`[ \t\n\r\f]+`,
|
|
1299
|
-
String.raw`[ \t\n]+`,
|
|
1300
|
-
String.raw`[ \t]+`,
|
|
1301
|
-
String.raw`[\t\n\f\r ]+`
|
|
1302
|
-
]);
|
|
1303
1431
|
function unwrapTrivia2(p) {
|
|
1304
1432
|
let cur = p;
|
|
1305
1433
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1315,36 +1443,30 @@ function choiceArms(p) {
|
|
|
1315
1443
|
if (p._def.tag === "choice") return p._def.parsers;
|
|
1316
1444
|
return null;
|
|
1317
1445
|
}
|
|
1318
|
-
function
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
function isBlockCommentSource(source) {
|
|
1322
|
-
return source === BLOCK_COMMENT;
|
|
1446
|
+
function armShape(arm) {
|
|
1447
|
+
const src = getCoreRegexDef(arm)?.source;
|
|
1448
|
+
return src ? parseScanShape(src) : null;
|
|
1323
1449
|
}
|
|
1324
1450
|
function analyzeTriviaFastPath(trivia2) {
|
|
1325
1451
|
const core = unwrapTrivia2(trivia2);
|
|
1326
|
-
const
|
|
1327
|
-
if (
|
|
1328
|
-
|
|
1329
|
-
return null;
|
|
1452
|
+
const directSrc = getCoreRegexDef(core)?.source;
|
|
1453
|
+
if (directSrc) {
|
|
1454
|
+
const shape = parseScanShape(directSrc);
|
|
1455
|
+
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1330
1456
|
}
|
|
1331
1457
|
const inner = oneOrMoreInner(core);
|
|
1332
1458
|
if (!inner) return null;
|
|
1333
|
-
const
|
|
1334
|
-
if (
|
|
1459
|
+
const innerShape = armShape(inner);
|
|
1460
|
+
if (innerShape) return innerShape.kind === "chars" ? [innerShape] : null;
|
|
1335
1461
|
const arms = choiceArms(inner);
|
|
1336
|
-
if (!arms || arms.length
|
|
1337
|
-
|
|
1338
|
-
let hasComment = false;
|
|
1462
|
+
if (!arms || arms.length < 2) return null;
|
|
1463
|
+
const shapes = [];
|
|
1339
1464
|
for (const arm of arms) {
|
|
1340
|
-
const
|
|
1341
|
-
if (!
|
|
1342
|
-
|
|
1343
|
-
else if (isBlockCommentSource(src)) hasComment = true;
|
|
1344
|
-
else return null;
|
|
1465
|
+
const shape = armShape(arm);
|
|
1466
|
+
if (!shape) return null;
|
|
1467
|
+
shapes.push(shape);
|
|
1345
1468
|
}
|
|
1346
|
-
|
|
1347
|
-
return null;
|
|
1469
|
+
return shapes;
|
|
1348
1470
|
}
|
|
1349
1471
|
var CAP_RECORD = [
|
|
1350
1472
|
` if (_cap && _e > _pos) {`,
|
|
@@ -1352,80 +1474,49 @@ var CAP_RECORD = [
|
|
|
1352
1474
|
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
|
|
1353
1475
|
` }`
|
|
1354
1476
|
].join("\n");
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1367
|
-
` let j = _e + 2`,
|
|
1368
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1369
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1370
|
-
` continue`,
|
|
1371
|
-
` }`,
|
|
1372
|
-
` break`,
|
|
1373
|
-
` }`
|
|
1374
|
-
].join("\n");
|
|
1375
|
-
var CAP_CHUNK = (wsKind, commentKind) => [
|
|
1376
|
-
` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
|
|
1377
|
-
` const _cs = _e`,
|
|
1378
|
-
` _e++`,
|
|
1379
|
-
` while (_e < input.length) {`,
|
|
1380
|
-
` const c2 = input.charCodeAt(_e)`,
|
|
1381
|
-
` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
|
|
1382
|
-
` else break`,
|
|
1383
|
-
` }`,
|
|
1384
|
-
` if (_cap) {`,
|
|
1385
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
|
|
1386
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
|
|
1387
|
-
` }`,
|
|
1388
|
-
` continue`,
|
|
1389
|
-
` }`,
|
|
1390
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1391
|
-
` const _cs = _e`,
|
|
1392
|
-
` let j = _e + 2`,
|
|
1393
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1394
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1395
|
-
` if (_cap) {`,
|
|
1396
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
|
|
1397
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
|
|
1398
|
-
` }`,
|
|
1399
|
-
` continue`,
|
|
1400
|
-
` }`
|
|
1401
|
-
].join("\n");
|
|
1402
|
-
var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
|
|
1403
|
-
` while (_e < input.length) {`,
|
|
1404
|
-
` const c = input.charCodeAt(_e)`,
|
|
1405
|
-
CAP_CHUNK(wsKind, commentKind),
|
|
1406
|
-
` break`,
|
|
1407
|
-
` }`
|
|
1408
|
-
].join("\n");
|
|
1409
|
-
function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
|
|
1410
|
-
const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
|
|
1411
|
-
const cap = kindIndices ? "" : CAP_RECORD;
|
|
1412
|
-
const lines = [
|
|
1477
|
+
function composeFastLoop(shapes) {
|
|
1478
|
+
return [
|
|
1479
|
+
` while (_e < input.length) {`,
|
|
1480
|
+
` const c = input.charCodeAt(_e)`,
|
|
1481
|
+
...shapes.map(scanBranch),
|
|
1482
|
+
` break`,
|
|
1483
|
+
` }`
|
|
1484
|
+
].join("\n");
|
|
1485
|
+
}
|
|
1486
|
+
function buildFastTriviaFnDecl(fnName, shapes) {
|
|
1487
|
+
return [
|
|
1413
1488
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1414
1489
|
` let _e = _pos`,
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1490
|
+
composeFastLoop(shapes),
|
|
1491
|
+
CAP_RECORD,
|
|
1492
|
+
` return _e`,
|
|
1493
|
+
`}`
|
|
1494
|
+
].join("\n");
|
|
1420
1495
|
}
|
|
1421
|
-
function
|
|
1496
|
+
function analyzeLabeledScannableRun(trivia2) {
|
|
1497
|
+
const spec = analyzeLabeledTrivia(trivia2);
|
|
1498
|
+
if (!spec) return null;
|
|
1499
|
+
const out = [];
|
|
1422
1500
|
for (const arm of spec.arms) {
|
|
1423
1501
|
const src = getCoreRegexDef(arm.parser)?.source;
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1502
|
+
const shape = src ? parseScanShape(src) : null;
|
|
1503
|
+
if (!shape) return null;
|
|
1504
|
+
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1427
1505
|
}
|
|
1428
|
-
return
|
|
1506
|
+
return out;
|
|
1507
|
+
}
|
|
1508
|
+
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
1509
|
+
return [
|
|
1510
|
+
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1511
|
+
` let _e = _pos`,
|
|
1512
|
+
` while (_e < input.length) {`,
|
|
1513
|
+
` const c = input.charCodeAt(_e)`,
|
|
1514
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
1515
|
+
` break`,
|
|
1516
|
+
` }`,
|
|
1517
|
+
` return _e`,
|
|
1518
|
+
`}`
|
|
1519
|
+
].join("\n");
|
|
1429
1520
|
}
|
|
1430
1521
|
function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
1431
1522
|
const tryArms = spec.arms.map((arm, i) => {
|
|
@@ -1458,14 +1549,6 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
|
1458
1549
|
`}`
|
|
1459
1550
|
].join("\n");
|
|
1460
1551
|
}
|
|
1461
|
-
function labeledTriviaKindIndices(trivia2) {
|
|
1462
|
-
const spec = analyzeLabeledTrivia(trivia2);
|
|
1463
|
-
if (!spec || spec.arms.length !== 2) return null;
|
|
1464
|
-
const ws = regexArmIndex(spec, false);
|
|
1465
|
-
const comment = regexArmIndex(spec, true);
|
|
1466
|
-
if (ws === null || comment === null) return null;
|
|
1467
|
-
return { ws, comment };
|
|
1468
|
-
}
|
|
1469
1552
|
function labeledTriviaRegexArms(trivia2) {
|
|
1470
1553
|
const spec = analyzeLabeledTrivia(trivia2);
|
|
1471
1554
|
if (!spec) return null;
|
|
@@ -1713,14 +1796,19 @@ function ensureTriviaFn(ctx) {
|
|
|
1713
1796
|
const fnName = `_tf${ctx.triviaFnNames.size}`;
|
|
1714
1797
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
1715
1798
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
1716
|
-
const fastKind = analyzeTriviaFastPath(trivia2);
|
|
1717
|
-
const kindIndices = labeledTriviaKindIndices(trivia2);
|
|
1718
|
-
if (fastKind) {
|
|
1719
|
-
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
|
|
1720
|
-
return fnName;
|
|
1721
|
-
}
|
|
1722
1799
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
1723
|
-
if (labeledSpec) {
|
|
1800
|
+
if (!labeledSpec) {
|
|
1801
|
+
const fastShapes = analyzeTriviaFastPath(trivia2);
|
|
1802
|
+
if (fastShapes) {
|
|
1803
|
+
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastShapes));
|
|
1804
|
+
return fnName;
|
|
1805
|
+
}
|
|
1806
|
+
} else {
|
|
1807
|
+
const labeledShapes = analyzeLabeledScannableRun(trivia2);
|
|
1808
|
+
if (labeledShapes) {
|
|
1809
|
+
ctx.namedFnDecls.push(buildLabeledScannableTriviaFnDecl(fnName, labeledShapes));
|
|
1810
|
+
return fnName;
|
|
1811
|
+
}
|
|
1724
1812
|
const regexSpec = labeledTriviaRegexArms(trivia2);
|
|
1725
1813
|
if (regexSpec) {
|
|
1726
1814
|
const reNames = [];
|
|
@@ -1780,6 +1868,119 @@ function firstSetCond(codeVar, fs) {
|
|
|
1780
1868
|
(r) => r.lo === r.hi ? `${codeVar} === ${r.lo}` : `(${codeVar} >= ${r.lo} && ${codeVar} <= ${r.hi})`
|
|
1781
1869
|
).join(" || ");
|
|
1782
1870
|
}
|
|
1871
|
+
function captureRestoreBody(mL, mR, mTl, mLg) {
|
|
1872
|
+
const base = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${mL}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${mR}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${mTl}`;
|
|
1873
|
+
return mLg ? `${base}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${mLg}` : base;
|
|
1874
|
+
}
|
|
1875
|
+
function mayLeavePartialCapture(p, seen = /* @__PURE__ */ new Set()) {
|
|
1876
|
+
if (seen.has(p)) return false;
|
|
1877
|
+
seen.add(p);
|
|
1878
|
+
const d = p._def;
|
|
1879
|
+
switch (d.tag) {
|
|
1880
|
+
// Atomic / non-capturing-then-failing: a failure happens before any push.
|
|
1881
|
+
case "literal":
|
|
1882
|
+
case "regex":
|
|
1883
|
+
case "keywords":
|
|
1884
|
+
case "guard":
|
|
1885
|
+
case "not":
|
|
1886
|
+
case "trivia":
|
|
1887
|
+
case "scanTo":
|
|
1888
|
+
case "unknown":
|
|
1889
|
+
return false;
|
|
1890
|
+
// node() captures into its own private buffers and rolls them back on
|
|
1891
|
+
// failure (emitNode restores _ctx.* and never pushes on the !ok path).
|
|
1892
|
+
case "node":
|
|
1893
|
+
return false;
|
|
1894
|
+
// choice/firstMatch already roll back each failed arm; on overall failure
|
|
1895
|
+
// nothing committed remains.
|
|
1896
|
+
case "choice":
|
|
1897
|
+
return false;
|
|
1898
|
+
// optional never fails; many/oneOrMore only "fail" with zero captured items.
|
|
1899
|
+
case "optional":
|
|
1900
|
+
case "many":
|
|
1901
|
+
case "oneOrMore":
|
|
1902
|
+
return false;
|
|
1903
|
+
// sepBy emits its own per-iteration rollback in emitSepBy.
|
|
1904
|
+
case "sepBy":
|
|
1905
|
+
return false;
|
|
1906
|
+
// A sequence is the real case: an earlier capturing term followed by a term
|
|
1907
|
+
// that can fail leaves the earlier captures buffered.
|
|
1908
|
+
case "sequence": {
|
|
1909
|
+
const parts = d.parsers;
|
|
1910
|
+
if (parts.length < 2) return parts.some((x) => mayLeavePartialCapture(x, seen));
|
|
1911
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1912
|
+
if (hasNodeDef(parts[i]) || capturesLeaf(parts[i])) return true;
|
|
1913
|
+
}
|
|
1914
|
+
return false;
|
|
1915
|
+
}
|
|
1916
|
+
// Delegating wrappers: defer to the wrapped parser.
|
|
1917
|
+
case "transform":
|
|
1918
|
+
case "label":
|
|
1919
|
+
case "expect":
|
|
1920
|
+
case "withCtx":
|
|
1921
|
+
case "grammar":
|
|
1922
|
+
return mayLeavePartialCapture(d.parser, seen);
|
|
1923
|
+
case "skip":
|
|
1924
|
+
return mayLeavePartialCapture(d.main, seen);
|
|
1925
|
+
case "recover":
|
|
1926
|
+
return mayLeavePartialCapture(d.parser, seen);
|
|
1927
|
+
case "lazy": {
|
|
1928
|
+
try {
|
|
1929
|
+
return mayLeavePartialCapture(d.thunk(), seen);
|
|
1930
|
+
} catch {
|
|
1931
|
+
return true;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
// Unknown shapes: be safe and keep the rollback.
|
|
1935
|
+
default:
|
|
1936
|
+
return true;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
function capturesLeaf(p, seen = /* @__PURE__ */ new Set()) {
|
|
1940
|
+
if (seen.has(p)) return false;
|
|
1941
|
+
seen.add(p);
|
|
1942
|
+
const d = p._def;
|
|
1943
|
+
switch (d.tag) {
|
|
1944
|
+
case "literal":
|
|
1945
|
+
case "regex":
|
|
1946
|
+
case "keywords":
|
|
1947
|
+
case "node":
|
|
1948
|
+
return true;
|
|
1949
|
+
case "not":
|
|
1950
|
+
case "guard":
|
|
1951
|
+
case "trivia":
|
|
1952
|
+
case "unknown":
|
|
1953
|
+
return false;
|
|
1954
|
+
case "sequence":
|
|
1955
|
+
case "choice":
|
|
1956
|
+
return d.parsers.some((x) => capturesLeaf(x, seen));
|
|
1957
|
+
case "sepBy":
|
|
1958
|
+
return capturesLeaf(d.parser, seen) || capturesLeaf(d.separator, seen);
|
|
1959
|
+
case "many":
|
|
1960
|
+
case "oneOrMore":
|
|
1961
|
+
case "optional":
|
|
1962
|
+
case "transform":
|
|
1963
|
+
case "label":
|
|
1964
|
+
case "expect":
|
|
1965
|
+
case "withCtx":
|
|
1966
|
+
case "grammar":
|
|
1967
|
+
case "recover":
|
|
1968
|
+
return capturesLeaf(d.parser, seen);
|
|
1969
|
+
case "skip":
|
|
1970
|
+
return capturesLeaf(d.main, seen);
|
|
1971
|
+
case "scanTo":
|
|
1972
|
+
return true;
|
|
1973
|
+
case "lazy": {
|
|
1974
|
+
try {
|
|
1975
|
+
return capturesLeaf(d.thunk(), seen);
|
|
1976
|
+
} catch {
|
|
1977
|
+
return true;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
default:
|
|
1981
|
+
return true;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1783
1984
|
function emitFallible(inner, ctx, pos) {
|
|
1784
1985
|
const lbl = v(ctx, "_lbl");
|
|
1785
1986
|
const okV = `${lbl}ok`;
|
|
@@ -1793,26 +1994,23 @@ function emitFallible(inner, ctx, pos) {
|
|
|
1793
1994
|
ctx.failLabel = savedLabel;
|
|
1794
1995
|
ctx.indent = savedIndent;
|
|
1795
1996
|
const ind0 = ind(ctx);
|
|
1796
|
-
const
|
|
1797
|
-
const
|
|
1798
|
-
const
|
|
1799
|
-
const
|
|
1997
|
+
const needsRollback = ctx.capturing && mayLeavePartialCapture(inner);
|
|
1998
|
+
const mL = needsRollback ? v(ctx, "_fcl") : null;
|
|
1999
|
+
const mR = needsRollback ? v(ctx, "_fcr") : null;
|
|
2000
|
+
const mTl = needsRollback ? v(ctx, "_fctl") : null;
|
|
1800
2001
|
const stmts = [
|
|
1801
2002
|
`${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
|
|
1802
2003
|
...mL ? [
|
|
1803
2004
|
`${ind0}const ${mL} = _ctx._cstLeaves?.length ?? 0`,
|
|
1804
2005
|
`${ind0}const ${mR} = _ctx._cstRawChildren?.length ?? 0`,
|
|
1805
|
-
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0
|
|
1806
|
-
`${ind0}const ${mLg} = _ctx._triviaLog?.length ?? 0`
|
|
2006
|
+
`${ind0}const ${mTl} = _ctx._cstTriviaLog?.length ?? 0`
|
|
1807
2007
|
] : [],
|
|
1808
2008
|
`${ind0}${lbl}: {`,
|
|
1809
2009
|
...r.stmts,
|
|
1810
|
-
`${ind0} ${valV} = ${r.valueVar}`,
|
|
1811
|
-
`${ind0} ${endV} = ${r.endVar}`,
|
|
1812
|
-
`${ind0} ${okV} = true`,
|
|
2010
|
+
`${ind0} ${valV} = ${r.valueVar}; ${endV} = ${r.endVar}; ${okV} = true`,
|
|
1813
2011
|
`${ind0}}`,
|
|
1814
2012
|
...mL ? [
|
|
1815
|
-
`${ind0}if (!${okV}) {
|
|
2013
|
+
`${ind0}if (!${okV}) { ${captureRestoreBody(mL, mR, mTl, null)} }`
|
|
1816
2014
|
] : []
|
|
1817
2015
|
];
|
|
1818
2016
|
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
@@ -2048,11 +2246,11 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2048
2246
|
const p = def.parsers[idx];
|
|
2049
2247
|
const litVal = getCoreLiteralValue(p);
|
|
2050
2248
|
const litLen = litVal.length;
|
|
2051
|
-
const
|
|
2249
|
+
const litCond2 = emitLiteralCondition(litVal, pos);
|
|
2052
2250
|
const kw = first ? "if" : "else if";
|
|
2053
2251
|
first = false;
|
|
2054
2252
|
const litEnd = `${pos} + ${litLen}`;
|
|
2055
|
-
stmts.push(`${ind(ctx)}${kw} (${
|
|
2253
|
+
stmts.push(`${ind(ctx)}${kw} (${litCond2}) {`);
|
|
2056
2254
|
ctx.indent++;
|
|
2057
2255
|
const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
|
|
2058
2256
|
stmts.push(
|
|
@@ -2086,11 +2284,13 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2086
2284
|
gateCond = `_mf[${gateIdx}](_ctx.state)`;
|
|
2087
2285
|
}
|
|
2088
2286
|
const skipCond = gateCond ? `!${resOkV} && ${gateCond}` : `!${resOkV}`;
|
|
2089
|
-
const
|
|
2090
|
-
const
|
|
2091
|
-
const
|
|
2092
|
-
const
|
|
2093
|
-
const
|
|
2287
|
+
const armHasAutoNot = !!(autoNot && autoNot.length > 0);
|
|
2288
|
+
const armNeedsRollback = ctx.capturing && (mayLeavePartialCapture(p) || armHasAutoNot && capturesLeaf(p));
|
|
2289
|
+
const markLeaves = armNeedsRollback ? v(ctx, "_cml") : null;
|
|
2290
|
+
const markRaw = armNeedsRollback ? v(ctx, "_cmr") : null;
|
|
2291
|
+
const markTl = armNeedsRollback ? v(ctx, "_cmtl") : null;
|
|
2292
|
+
const markLog = armNeedsRollback ? v(ctx, "_cmlg") : null;
|
|
2293
|
+
const rollback = markLeaves ? captureRestoreBody(markLeaves, markRaw, markTl, markLog) : "";
|
|
2094
2294
|
stmts.push(`${ind0}if (${skipCond}) {`);
|
|
2095
2295
|
ctx.indent++;
|
|
2096
2296
|
const ind1 = ind(ctx);
|
|
@@ -2120,11 +2320,7 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
2120
2320
|
stmts.push(`${ind0} }`);
|
|
2121
2321
|
if (rollback) stmts.push(`${ind0} if (!${resOkV}) { ${rollback} }`);
|
|
2122
2322
|
} else {
|
|
2123
|
-
stmts.push(`${ind0} if (${okVar}) {`);
|
|
2124
|
-
stmts.push(`${ind0} ${resValV} = ${valVar}`);
|
|
2125
|
-
stmts.push(`${ind0} ${resEndV} = ${endVar}`);
|
|
2126
|
-
stmts.push(`${ind0} ${resOkV} = true`);
|
|
2127
|
-
stmts.push(`${ind0} }`);
|
|
2323
|
+
stmts.push(`${ind0} if (${okVar}) { ${resValV} = ${valVar}; ${resEndV} = ${endVar}; ${resOkV} = true }`);
|
|
2128
2324
|
if (rollback) stmts.push(`${ind0} else { ${rollback} }`);
|
|
2129
2325
|
}
|
|
2130
2326
|
stmts.push(`${ind0}}`);
|
|
@@ -2422,6 +2618,16 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2422
2618
|
return { stmts, valueVar: vv, endVar: ev };
|
|
2423
2619
|
}
|
|
2424
2620
|
function emitLazy(p, def, ctx, pos) {
|
|
2621
|
+
const usage = ctx.lazyUsage;
|
|
2622
|
+
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
2623
|
+
let resolved;
|
|
2624
|
+
try {
|
|
2625
|
+
resolved = def.thunk();
|
|
2626
|
+
} catch {
|
|
2627
|
+
return emitRuntimeFallback(p, ctx, pos);
|
|
2628
|
+
}
|
|
2629
|
+
return emit(resolved, ctx, pos);
|
|
2630
|
+
}
|
|
2425
2631
|
if (!ctx.namedParsers.has(p)) {
|
|
2426
2632
|
const fnName2 = `_pf${ctx.namedParsers.size}`;
|
|
2427
2633
|
ctx.namedParsers.set(p, fnName2);
|
|
@@ -2469,6 +2675,22 @@ function emitRecover(def, ctx, pos) {
|
|
|
2469
2675
|
];
|
|
2470
2676
|
return { stmts, valueVar: valVar, endVar };
|
|
2471
2677
|
}
|
|
2678
|
+
function emitExpect(def, ctx, pos) {
|
|
2679
|
+
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2680
|
+
const ind0 = ind(ctx);
|
|
2681
|
+
const errV = v(ctx, "_err");
|
|
2682
|
+
const stmts = [
|
|
2683
|
+
...innerStmts,
|
|
2684
|
+
`${ind0}if (!${okVar}) {`,
|
|
2685
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${pos} }, expected: ${JSON.stringify(def.expected)} }`,
|
|
2686
|
+
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
2687
|
+
`${ind0} ${valVar} = ${errV}`,
|
|
2688
|
+
`${ind0} ${endVar} = ${pos}`,
|
|
2689
|
+
`${ind0} ${okVar} = true`,
|
|
2690
|
+
`${ind0}}`
|
|
2691
|
+
];
|
|
2692
|
+
return { stmts, valueVar: valVar, endVar };
|
|
2693
|
+
}
|
|
2472
2694
|
function emit(p, ctx, pos) {
|
|
2473
2695
|
const def = p._def;
|
|
2474
2696
|
switch (def.tag) {
|
|
@@ -2577,6 +2799,8 @@ function emit(p, ctx, pos) {
|
|
|
2577
2799
|
return emitScanTo(def, ctx, pos);
|
|
2578
2800
|
case "recover":
|
|
2579
2801
|
return emitRecover(def, ctx, pos);
|
|
2802
|
+
case "expect":
|
|
2803
|
+
return emitExpect(def, ctx, pos);
|
|
2580
2804
|
case "guard": {
|
|
2581
2805
|
const fnIdx = ctx.mapFns.length;
|
|
2582
2806
|
ctx.mapFns.push(def.predicate);
|
|
@@ -2660,12 +2884,88 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2660
2884
|
return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen));
|
|
2661
2885
|
case "recover":
|
|
2662
2886
|
return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
|
|
2887
|
+
case "expect":
|
|
2888
|
+
return hasNodeDef(d.parser, seen);
|
|
2663
2889
|
case "withCtx":
|
|
2664
2890
|
return hasNodeDef(d.parser, seen);
|
|
2665
2891
|
default:
|
|
2666
2892
|
return false;
|
|
2667
2893
|
}
|
|
2668
2894
|
}
|
|
2895
|
+
function childrenOf(def) {
|
|
2896
|
+
switch (def.tag) {
|
|
2897
|
+
case "sequence":
|
|
2898
|
+
case "choice":
|
|
2899
|
+
return def.parsers;
|
|
2900
|
+
case "many":
|
|
2901
|
+
case "optional":
|
|
2902
|
+
case "transform":
|
|
2903
|
+
case "trivia":
|
|
2904
|
+
case "label":
|
|
2905
|
+
case "grammar":
|
|
2906
|
+
case "not":
|
|
2907
|
+
case "node":
|
|
2908
|
+
case "withCtx":
|
|
2909
|
+
case "expect":
|
|
2910
|
+
return [def.parser];
|
|
2911
|
+
// emitMany's oneOrMore branch and emitSepBy both codegen `def.parser` TWICE
|
|
2912
|
+
// (a mandatory first match, then again inside the repeat loop) — two real
|
|
2913
|
+
// emit() call sites, so the usage analysis must see two edges here or it
|
|
2914
|
+
// undercounts a single-use `parser` ref as inline-safe when it's actually
|
|
2915
|
+
// referenced from two positions within this one compiled function.
|
|
2916
|
+
case "oneOrMore":
|
|
2917
|
+
return [def.parser, def.parser];
|
|
2918
|
+
case "sepBy":
|
|
2919
|
+
return [def.parser, def.parser, def.separator];
|
|
2920
|
+
case "skip":
|
|
2921
|
+
return [def.main, def.skipped];
|
|
2922
|
+
case "recover":
|
|
2923
|
+
return [def.parser, def.sentinel];
|
|
2924
|
+
case "scanTo":
|
|
2925
|
+
return [def.sentinel, ...def.skip];
|
|
2926
|
+
case "lazy":
|
|
2927
|
+
case "literal":
|
|
2928
|
+
case "regex":
|
|
2929
|
+
case "keywords":
|
|
2930
|
+
case "guard":
|
|
2931
|
+
case "unknown":
|
|
2932
|
+
return [];
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
function analyzeLazyUsage(root) {
|
|
2936
|
+
return analyzeLazyUsageMulti([root]);
|
|
2937
|
+
}
|
|
2938
|
+
function analyzeLazyUsageMulti(roots) {
|
|
2939
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2940
|
+
const recursive = /* @__PURE__ */ new Set();
|
|
2941
|
+
const descended = /* @__PURE__ */ new Set();
|
|
2942
|
+
const active = /* @__PURE__ */ new Set();
|
|
2943
|
+
function walk(p) {
|
|
2944
|
+
const def = p._def;
|
|
2945
|
+
if (def.tag === "lazy") {
|
|
2946
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
2947
|
+
if (active.has(p)) {
|
|
2948
|
+
recursive.add(p);
|
|
2949
|
+
return;
|
|
2950
|
+
}
|
|
2951
|
+
if (descended.has(p)) return;
|
|
2952
|
+
descended.add(p);
|
|
2953
|
+
let resolved;
|
|
2954
|
+
try {
|
|
2955
|
+
resolved = def.thunk();
|
|
2956
|
+
} catch {
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2959
|
+
active.add(p);
|
|
2960
|
+
walk(resolved);
|
|
2961
|
+
active.delete(p);
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
for (const child of childrenOf(def)) walk(child);
|
|
2965
|
+
}
|
|
2966
|
+
for (const root of roots) walk(root);
|
|
2967
|
+
return { counts, recursive };
|
|
2968
|
+
}
|
|
2669
2969
|
function compile(combinator, mapFnSources) {
|
|
2670
2970
|
const ctx = {
|
|
2671
2971
|
vars: 0,
|
|
@@ -2682,7 +2982,8 @@ function compile(combinator, mapFnSources) {
|
|
|
2682
2982
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
2683
2983
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
2684
2984
|
namedFnDecls: [],
|
|
2685
|
-
capturing: hasNodeDef(combinator)
|
|
2985
|
+
capturing: hasNodeDef(combinator),
|
|
2986
|
+
lazyUsage: analyzeLazyUsage(combinator)
|
|
2686
2987
|
};
|
|
2687
2988
|
const r = emit(combinator, ctx, "_pos");
|
|
2688
2989
|
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
@@ -2728,6 +3029,11 @@ function compile(combinator, mapFnSources) {
|
|
|
2728
3029
|
parseWithContext(input, parseCtx, pos = 0) {
|
|
2729
3030
|
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, parseCtx);
|
|
2730
3031
|
},
|
|
3032
|
+
// Note: collects recover()/expect() errors via _errors. Unlike interpreter
|
|
3033
|
+
// parse({recover:true}) it does NOT populate furthestFail — the compiled path
|
|
3034
|
+
// inlines failures for throughput and deliberately skips _probe bookkeeping.
|
|
3035
|
+
// Callers wanting a furthest-position diagnostic detect unconsumed input
|
|
3036
|
+
// (span.end < input.length) instead, which is mode-agnostic.
|
|
2731
3037
|
parseWithErrors(input, pos = 0) {
|
|
2732
3038
|
const errors = [];
|
|
2733
3039
|
const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
|
|
@@ -2839,6 +3145,58 @@ function isParseError(value) {
|
|
|
2839
3145
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
2840
3146
|
}
|
|
2841
3147
|
|
|
3148
|
+
// src/combinators/expect.ts
|
|
3149
|
+
function staticExpected(c) {
|
|
3150
|
+
const def = c._def;
|
|
3151
|
+
switch (def.tag) {
|
|
3152
|
+
case "literal":
|
|
3153
|
+
return [JSON.stringify(def.value)];
|
|
3154
|
+
case "regex":
|
|
3155
|
+
return [`/${def.source}/`];
|
|
3156
|
+
case "keywords":
|
|
3157
|
+
return def.words.map((w) => JSON.stringify(w));
|
|
3158
|
+
case "label":
|
|
3159
|
+
return [def.label];
|
|
3160
|
+
case "choice":
|
|
3161
|
+
return def.parsers.flatMap(staticExpected);
|
|
3162
|
+
case "sequence":
|
|
3163
|
+
return def.parsers.length > 0 ? staticExpected(def.parsers[0]) : [];
|
|
3164
|
+
case "node":
|
|
3165
|
+
case "grammar":
|
|
3166
|
+
case "trivia":
|
|
3167
|
+
case "optional":
|
|
3168
|
+
case "many":
|
|
3169
|
+
case "oneOrMore":
|
|
3170
|
+
case "transform":
|
|
3171
|
+
case "not":
|
|
3172
|
+
return staticExpected(def.parser);
|
|
3173
|
+
case "lazy":
|
|
3174
|
+
return staticExpected(def.thunk());
|
|
3175
|
+
default:
|
|
3176
|
+
return [];
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
function expect(combinator, label2) {
|
|
3180
|
+
const expected = label2 !== void 0 ? [label2] : staticExpected(combinator);
|
|
3181
|
+
const meta = {
|
|
3182
|
+
firstSet: combinator._meta.firstSet,
|
|
3183
|
+
canMatchNewline: combinator._meta.canMatchNewline,
|
|
3184
|
+
isTrivia: false
|
|
3185
|
+
};
|
|
3186
|
+
return {
|
|
3187
|
+
_tag: "expect",
|
|
3188
|
+
_meta: meta,
|
|
3189
|
+
_def: { tag: "expect", parser: combinator, label: label2, expected },
|
|
3190
|
+
parse(input, pos, ctx) {
|
|
3191
|
+
const result = combinator.parse(input, pos, ctx);
|
|
3192
|
+
if (result.ok) return result;
|
|
3193
|
+
const error = { _tag: "parseError", span: { start: pos, end: pos }, expected };
|
|
3194
|
+
ctx._errors?.push(error);
|
|
3195
|
+
return { ok: true, value: error, span: { start: pos, end: pos } };
|
|
3196
|
+
}
|
|
3197
|
+
};
|
|
3198
|
+
}
|
|
3199
|
+
|
|
2842
3200
|
// src/combinators/completions.ts
|
|
2843
3201
|
function completionsAt(combinator, input, offset) {
|
|
2844
3202
|
const probe = { offset, best: null };
|
|
@@ -2867,7 +3225,11 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
2867
3225
|
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
2868
3226
|
parse(input, pos, ctx) {
|
|
2869
3227
|
let cur = pos;
|
|
2870
|
-
const probeCtx = {
|
|
3228
|
+
const probeCtx = {
|
|
3229
|
+
trackLines: ctx.trackLines,
|
|
3230
|
+
state: ctx.state,
|
|
3231
|
+
...ctx._errors !== void 0 ? { _errors: ctx._errors } : {}
|
|
3232
|
+
};
|
|
2871
3233
|
const emit2 = (end) => {
|
|
2872
3234
|
if (end > pos && cstCaptureActive(ctx)) {
|
|
2873
3235
|
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
@@ -2903,20 +3265,46 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
2903
3265
|
}
|
|
2904
3266
|
function balanced(open, close, options = {}) {
|
|
2905
3267
|
const self = ref();
|
|
2906
|
-
const
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
3268
|
+
const skips = options.skip ?? [];
|
|
3269
|
+
const stop = /* @__PURE__ */ new Set([open, close]);
|
|
3270
|
+
let bounded = true;
|
|
3271
|
+
for (const sk of skips) {
|
|
3272
|
+
const cs = firstSetClassChars(sk);
|
|
3273
|
+
if (cs === null) {
|
|
3274
|
+
bounded = false;
|
|
3275
|
+
break;
|
|
3276
|
+
}
|
|
3277
|
+
for (const ch of cs) stop.add(ch);
|
|
3278
|
+
}
|
|
3279
|
+
const cls = [...stop].map(escapeClassChar).join("");
|
|
3280
|
+
const content = bounded ? regex(new RegExp(`[^${cls}]+`)) : regex(new RegExp(`[^${escapeClassChar(open)}${escapeClassChar(close)}]`));
|
|
3281
|
+
const inner = many(choice(self, ...skips, content));
|
|
2910
3282
|
const combi = transform(
|
|
2911
|
-
sequence(literal(open), inner, literal(close)),
|
|
2912
|
-
(
|
|
3283
|
+
sequence(literal(open), inner, expect(literal(close), close)),
|
|
3284
|
+
// parts: strings (content/self) or arrays (a sequence-shaped skip) or a
|
|
3285
|
+
// ParseError (recovered close). `c` is the close string or a ParseError.
|
|
3286
|
+
([o, parts, c]) => o + parts.map((p) => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")
|
|
2913
3287
|
);
|
|
2914
3288
|
if (combi._def.tag === "transform") {
|
|
2915
|
-
combi._def.fnSrc =
|
|
3289
|
+
combi._def.fnSrc = '([o, parts, c]) => o + parts.map(p => typeof p === "string" ? p : Array.isArray(p) ? p.join("") : "").join("") + (typeof c === "string" ? c : "")';
|
|
2916
3290
|
}
|
|
2917
3291
|
self.define(combi);
|
|
2918
3292
|
return combi;
|
|
2919
3293
|
}
|
|
3294
|
+
function firstSetClassChars(c) {
|
|
3295
|
+
const fs = c._meta.firstSet;
|
|
3296
|
+
if (fs.kind === "empty") return [];
|
|
3297
|
+
if (fs.kind !== "ranges") return null;
|
|
3298
|
+
const out = [];
|
|
3299
|
+
for (const { lo, hi } of fs.ranges) {
|
|
3300
|
+
if (hi - lo > 8) return null;
|
|
3301
|
+
for (let cp = lo; cp <= hi; cp++) out.push(String.fromCodePoint(cp));
|
|
3302
|
+
}
|
|
3303
|
+
return out;
|
|
3304
|
+
}
|
|
3305
|
+
function escapeClassChar(ch) {
|
|
3306
|
+
return ch.replace(/[\\\]^-]/g, "\\$&");
|
|
3307
|
+
}
|
|
2920
3308
|
|
|
2921
3309
|
// src/functional/doc.ts
|
|
2922
3310
|
function isNode(x) {
|
|
@@ -3116,6 +3504,7 @@ export {
|
|
|
3116
3504
|
choice,
|
|
3117
3505
|
compile,
|
|
3118
3506
|
completionsAt,
|
|
3507
|
+
expect,
|
|
3119
3508
|
guard,
|
|
3120
3509
|
isParseError,
|
|
3121
3510
|
keywords,
|
|
@@ -3140,6 +3529,7 @@ export {
|
|
|
3140
3529
|
sepBy,
|
|
3141
3530
|
sequence,
|
|
3142
3531
|
skip,
|
|
3532
|
+
staticExpected,
|
|
3143
3533
|
transform,
|
|
3144
3534
|
trivia,
|
|
3145
3535
|
triviaEntries,
|