parseman 0.8.0 → 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/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 +285 -114
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +285 -114
- 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 +453 -131
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +453 -131
- package/dist/plugin/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1294,14 +1294,140 @@ function parse(combinator, input, opts = {}) {
|
|
|
1294
1294
|
return withErrors;
|
|
1295
1295
|
}
|
|
1296
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
|
+
|
|
1297
1430
|
// src/compiler/trivia-fast-path.ts
|
|
1298
|
-
var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
|
|
1299
|
-
var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
|
|
1300
|
-
String.raw`[ \t\n\r\f]+`,
|
|
1301
|
-
String.raw`[ \t\n]+`,
|
|
1302
|
-
String.raw`[ \t]+`,
|
|
1303
|
-
String.raw`[\t\n\f\r ]+`
|
|
1304
|
-
]);
|
|
1305
1431
|
function unwrapTrivia2(p) {
|
|
1306
1432
|
let cur = p;
|
|
1307
1433
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1317,36 +1443,30 @@ function choiceArms(p) {
|
|
|
1317
1443
|
if (p._def.tag === "choice") return p._def.parsers;
|
|
1318
1444
|
return null;
|
|
1319
1445
|
}
|
|
1320
|
-
function
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
function isBlockCommentSource(source) {
|
|
1324
|
-
return source === BLOCK_COMMENT;
|
|
1446
|
+
function armShape(arm) {
|
|
1447
|
+
const src = getCoreRegexDef(arm)?.source;
|
|
1448
|
+
return src ? parseScanShape(src) : null;
|
|
1325
1449
|
}
|
|
1326
1450
|
function analyzeTriviaFastPath(trivia2) {
|
|
1327
1451
|
const core = unwrapTrivia2(trivia2);
|
|
1328
|
-
const
|
|
1329
|
-
if (
|
|
1330
|
-
|
|
1331
|
-
return null;
|
|
1452
|
+
const directSrc = getCoreRegexDef(core)?.source;
|
|
1453
|
+
if (directSrc) {
|
|
1454
|
+
const shape = parseScanShape(directSrc);
|
|
1455
|
+
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1332
1456
|
}
|
|
1333
1457
|
const inner = oneOrMoreInner(core);
|
|
1334
1458
|
if (!inner) return null;
|
|
1335
|
-
const
|
|
1336
|
-
if (
|
|
1459
|
+
const innerShape = armShape(inner);
|
|
1460
|
+
if (innerShape) return innerShape.kind === "chars" ? [innerShape] : null;
|
|
1337
1461
|
const arms = choiceArms(inner);
|
|
1338
|
-
if (!arms || arms.length
|
|
1339
|
-
|
|
1340
|
-
let hasComment = false;
|
|
1462
|
+
if (!arms || arms.length < 2) return null;
|
|
1463
|
+
const shapes = [];
|
|
1341
1464
|
for (const arm of arms) {
|
|
1342
|
-
const
|
|
1343
|
-
if (!
|
|
1344
|
-
|
|
1345
|
-
else if (isBlockCommentSource(src)) hasComment = true;
|
|
1346
|
-
else return null;
|
|
1465
|
+
const shape = armShape(arm);
|
|
1466
|
+
if (!shape) return null;
|
|
1467
|
+
shapes.push(shape);
|
|
1347
1468
|
}
|
|
1348
|
-
|
|
1349
|
-
return null;
|
|
1469
|
+
return shapes;
|
|
1350
1470
|
}
|
|
1351
1471
|
var CAP_RECORD = [
|
|
1352
1472
|
` if (_cap && _e > _pos) {`,
|
|
@@ -1354,80 +1474,49 @@ var CAP_RECORD = [
|
|
|
1354
1474
|
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
|
|
1355
1475
|
` }`
|
|
1356
1476
|
].join("\n");
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1369
|
-
` let j = _e + 2`,
|
|
1370
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1371
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1372
|
-
` continue`,
|
|
1373
|
-
` }`,
|
|
1374
|
-
` break`,
|
|
1375
|
-
` }`
|
|
1376
|
-
].join("\n");
|
|
1377
|
-
var CAP_CHUNK = (wsKind, commentKind) => [
|
|
1378
|
-
` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
|
|
1379
|
-
` const _cs = _e`,
|
|
1380
|
-
` _e++`,
|
|
1381
|
-
` while (_e < input.length) {`,
|
|
1382
|
-
` const c2 = input.charCodeAt(_e)`,
|
|
1383
|
-
` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
|
|
1384
|
-
` else break`,
|
|
1385
|
-
` }`,
|
|
1386
|
-
` if (_cap) {`,
|
|
1387
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
|
|
1388
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
|
|
1389
|
-
` }`,
|
|
1390
|
-
` continue`,
|
|
1391
|
-
` }`,
|
|
1392
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1393
|
-
` const _cs = _e`,
|
|
1394
|
-
` let j = _e + 2`,
|
|
1395
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1396
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1397
|
-
` if (_cap) {`,
|
|
1398
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
|
|
1399
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
|
|
1400
|
-
` }`,
|
|
1401
|
-
` continue`,
|
|
1402
|
-
` }`
|
|
1403
|
-
].join("\n");
|
|
1404
|
-
var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
|
|
1405
|
-
` while (_e < input.length) {`,
|
|
1406
|
-
` const c = input.charCodeAt(_e)`,
|
|
1407
|
-
CAP_CHUNK(wsKind, commentKind),
|
|
1408
|
-
` break`,
|
|
1409
|
-
` }`
|
|
1410
|
-
].join("\n");
|
|
1411
|
-
function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
|
|
1412
|
-
const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
|
|
1413
|
-
const cap = kindIndices ? "" : CAP_RECORD;
|
|
1414
|
-
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 [
|
|
1415
1488
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1416
1489
|
` let _e = _pos`,
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1490
|
+
composeFastLoop(shapes),
|
|
1491
|
+
CAP_RECORD,
|
|
1492
|
+
` return _e`,
|
|
1493
|
+
`}`
|
|
1494
|
+
].join("\n");
|
|
1422
1495
|
}
|
|
1423
|
-
function
|
|
1496
|
+
function analyzeLabeledScannableRun(trivia2) {
|
|
1497
|
+
const spec = analyzeLabeledTrivia(trivia2);
|
|
1498
|
+
if (!spec) return null;
|
|
1499
|
+
const out = [];
|
|
1424
1500
|
for (const arm of spec.arms) {
|
|
1425
1501
|
const src = getCoreRegexDef(arm.parser)?.source;
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1502
|
+
const shape = src ? parseScanShape(src) : null;
|
|
1503
|
+
if (!shape) return null;
|
|
1504
|
+
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1429
1505
|
}
|
|
1430
|
-
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");
|
|
1431
1520
|
}
|
|
1432
1521
|
function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
1433
1522
|
const tryArms = spec.arms.map((arm, i) => {
|
|
@@ -1460,14 +1549,6 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
|
1460
1549
|
`}`
|
|
1461
1550
|
].join("\n");
|
|
1462
1551
|
}
|
|
1463
|
-
function labeledTriviaKindIndices(trivia2) {
|
|
1464
|
-
const spec = analyzeLabeledTrivia(trivia2);
|
|
1465
|
-
if (!spec || spec.arms.length !== 2) return null;
|
|
1466
|
-
const ws = regexArmIndex(spec, false);
|
|
1467
|
-
const comment = regexArmIndex(spec, true);
|
|
1468
|
-
if (ws === null || comment === null) return null;
|
|
1469
|
-
return { ws, comment };
|
|
1470
|
-
}
|
|
1471
1552
|
function labeledTriviaRegexArms(trivia2) {
|
|
1472
1553
|
const spec = analyzeLabeledTrivia(trivia2);
|
|
1473
1554
|
if (!spec) return null;
|
|
@@ -1715,14 +1796,19 @@ function ensureTriviaFn(ctx) {
|
|
|
1715
1796
|
const fnName = `_tf${ctx.triviaFnNames.size}`;
|
|
1716
1797
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
1717
1798
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
1718
|
-
const fastKind = analyzeTriviaFastPath(trivia2);
|
|
1719
|
-
const kindIndices = labeledTriviaKindIndices(trivia2);
|
|
1720
|
-
if (fastKind) {
|
|
1721
|
-
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
|
|
1722
|
-
return fnName;
|
|
1723
|
-
}
|
|
1724
1799
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
1725
|
-
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
|
+
}
|
|
1726
1812
|
const regexSpec = labeledTriviaRegexArms(trivia2);
|
|
1727
1813
|
if (regexSpec) {
|
|
1728
1814
|
const reNames = [];
|
|
@@ -2160,11 +2246,11 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2160
2246
|
const p = def.parsers[idx];
|
|
2161
2247
|
const litVal = getCoreLiteralValue(p);
|
|
2162
2248
|
const litLen = litVal.length;
|
|
2163
|
-
const
|
|
2249
|
+
const litCond2 = emitLiteralCondition(litVal, pos);
|
|
2164
2250
|
const kw = first ? "if" : "else if";
|
|
2165
2251
|
first = false;
|
|
2166
2252
|
const litEnd = `${pos} + ${litLen}`;
|
|
2167
|
-
stmts.push(`${ind(ctx)}${kw} (${
|
|
2253
|
+
stmts.push(`${ind(ctx)}${kw} (${litCond2}) {`);
|
|
2168
2254
|
ctx.indent++;
|
|
2169
2255
|
const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
|
|
2170
2256
|
stmts.push(
|
|
@@ -2532,6 +2618,16 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2532
2618
|
return { stmts, valueVar: vv, endVar: ev };
|
|
2533
2619
|
}
|
|
2534
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
|
+
}
|
|
2535
2631
|
if (!ctx.namedParsers.has(p)) {
|
|
2536
2632
|
const fnName2 = `_pf${ctx.namedParsers.size}`;
|
|
2537
2633
|
ctx.namedParsers.set(p, fnName2);
|
|
@@ -2796,6 +2892,80 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2796
2892
|
return false;
|
|
2797
2893
|
}
|
|
2798
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
|
+
}
|
|
2799
2969
|
function compile(combinator, mapFnSources) {
|
|
2800
2970
|
const ctx = {
|
|
2801
2971
|
vars: 0,
|
|
@@ -2812,7 +2982,8 @@ function compile(combinator, mapFnSources) {
|
|
|
2812
2982
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
2813
2983
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
2814
2984
|
namedFnDecls: [],
|
|
2815
|
-
capturing: hasNodeDef(combinator)
|
|
2985
|
+
capturing: hasNodeDef(combinator),
|
|
2986
|
+
lazyUsage: analyzeLazyUsage(combinator)
|
|
2816
2987
|
};
|
|
2817
2988
|
const r = emit(combinator, ctx, "_pos");
|
|
2818
2989
|
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|