parseman 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -551
- package/dist/combinators/node.d.ts +15 -1
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/compiler/build-arity.d.ts +26 -0
- package/dist/compiler/build-arity.d.ts.map +1 -0
- 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/functional/doc.d.ts.map +1 -1
- package/dist/index.cjs +387 -126
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +387 -126
- 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 +542 -144
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +542 -144
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -1
package/dist/plugin/index.cjs
CHANGED
|
@@ -1134,24 +1134,70 @@ function not(combinator) {
|
|
|
1134
1134
|
};
|
|
1135
1135
|
}
|
|
1136
1136
|
|
|
1137
|
+
// src/compiler/build-arity.ts
|
|
1138
|
+
var PARAM_LIST_RE = (
|
|
1139
|
+
// (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
|
|
1140
|
+
/^(?:function\b[^(]*\(([^)]*)\)|\(([^)]*)\)\s*=>|([A-Za-z_$][\w$]*)\s*=>)/
|
|
1141
|
+
);
|
|
1142
|
+
var CONFIRMABLE_PARAM_RE = /^[A-Za-z_$][\w$]*\s*\??\s*(?::[^,=]+)?$/;
|
|
1143
|
+
function confirmedBuildArity(src) {
|
|
1144
|
+
const s = src.trim();
|
|
1145
|
+
const m = PARAM_LIST_RE.exec(s);
|
|
1146
|
+
if (!m) return null;
|
|
1147
|
+
if (m[3] !== void 0) return 1;
|
|
1148
|
+
const inner = (m[1] ?? m[2] ?? "").trim();
|
|
1149
|
+
if (inner === "") return 0;
|
|
1150
|
+
const parts = inner.split(",");
|
|
1151
|
+
for (const part of parts) {
|
|
1152
|
+
if (!CONFIRMABLE_PARAM_RE.test(part.trim())) return null;
|
|
1153
|
+
}
|
|
1154
|
+
if (/\barguments\b/.test(s)) return null;
|
|
1155
|
+
return parts.length;
|
|
1156
|
+
}
|
|
1157
|
+
function buildReadsTrivia(def) {
|
|
1158
|
+
const src = def.buildSrc ?? def.build.toString();
|
|
1159
|
+
const arity = confirmedBuildArity(src);
|
|
1160
|
+
if (arity === null) return true;
|
|
1161
|
+
return arity >= 4;
|
|
1162
|
+
}
|
|
1163
|
+
function buildReadsState(def) {
|
|
1164
|
+
const src = def.buildSrc ?? def.build.toString();
|
|
1165
|
+
const arity = confirmedBuildArity(src);
|
|
1166
|
+
if (arity === null) return true;
|
|
1167
|
+
return arity >= 5;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1137
1170
|
// src/combinators/node.ts
|
|
1138
|
-
function
|
|
1171
|
+
function collapseChild(child) {
|
|
1172
|
+
return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
|
|
1173
|
+
}
|
|
1174
|
+
function node(type, combinator, build, opts) {
|
|
1139
1175
|
const meta = {
|
|
1140
1176
|
firstSet: combinator._meta.firstSet,
|
|
1141
1177
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1142
1178
|
isTrivia: false
|
|
1143
1179
|
};
|
|
1180
|
+
const collapse = opts?.collapse === true;
|
|
1181
|
+
const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
|
|
1182
|
+
const capturesTrivia = buildReadsTrivia(def);
|
|
1183
|
+
const clonesState = buildReadsState(def);
|
|
1144
1184
|
return {
|
|
1145
1185
|
_tag: "node",
|
|
1146
1186
|
_meta: meta,
|
|
1147
|
-
_def:
|
|
1187
|
+
_def: def,
|
|
1148
1188
|
parse(input, pos, ctx) {
|
|
1149
1189
|
const saved = beginCstNodeCapture(ctx);
|
|
1190
|
+
if (!capturesTrivia) ctx.captureTrivia = false;
|
|
1150
1191
|
const r = combinator.parse(input, pos, ctx);
|
|
1151
1192
|
const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
1152
1193
|
if (!r.ok) return r;
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1194
|
+
const built = collapse && children.length === 1 ? collapseChild(children[0]) : build(
|
|
1195
|
+
children,
|
|
1196
|
+
rawChildren,
|
|
1197
|
+
r.span,
|
|
1198
|
+
triviaLog,
|
|
1199
|
+
clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
|
|
1200
|
+
);
|
|
1155
1201
|
const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
|
|
1156
1202
|
const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
|
|
1157
1203
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
@@ -1269,14 +1315,140 @@ function noTrivia(root) {
|
|
|
1269
1315
|
return parser({ trivia: null }, root);
|
|
1270
1316
|
}
|
|
1271
1317
|
|
|
1318
|
+
// src/compiler/scannable-run.ts
|
|
1319
|
+
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1320
|
+
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1321
|
+
function parseClassRanges(body) {
|
|
1322
|
+
const ranges = [];
|
|
1323
|
+
let i = 0;
|
|
1324
|
+
const readCp = () => {
|
|
1325
|
+
const ch = body[i];
|
|
1326
|
+
if (ch === void 0) return null;
|
|
1327
|
+
if (ch === "\\") {
|
|
1328
|
+
const e = body[i + 1];
|
|
1329
|
+
if (e === void 0) return null;
|
|
1330
|
+
i += 2;
|
|
1331
|
+
return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
|
|
1332
|
+
}
|
|
1333
|
+
i += ch.length;
|
|
1334
|
+
return ch.codePointAt(0);
|
|
1335
|
+
};
|
|
1336
|
+
while (i < body.length) {
|
|
1337
|
+
const lo = readCp();
|
|
1338
|
+
if (lo === null) return null;
|
|
1339
|
+
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1340
|
+
i += 1;
|
|
1341
|
+
const hi = readCp();
|
|
1342
|
+
if (hi === null) return null;
|
|
1343
|
+
ranges.push([lo, hi]);
|
|
1344
|
+
} else {
|
|
1345
|
+
ranges.push([lo, lo]);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
return ranges.length ? ranges : null;
|
|
1349
|
+
}
|
|
1350
|
+
function literalCodePoints(frag) {
|
|
1351
|
+
const out = [];
|
|
1352
|
+
let i = 0;
|
|
1353
|
+
while (i < frag.length) {
|
|
1354
|
+
const ch = frag[i];
|
|
1355
|
+
if (ch === "\\") {
|
|
1356
|
+
const e = frag[i + 1];
|
|
1357
|
+
if (e === void 0) return null;
|
|
1358
|
+
out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
|
|
1359
|
+
i += 2;
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
if (META.has(ch)) return null;
|
|
1363
|
+
out.push(ch.codePointAt(0));
|
|
1364
|
+
i += 1;
|
|
1365
|
+
}
|
|
1366
|
+
return out.length ? out : null;
|
|
1367
|
+
}
|
|
1368
|
+
function parseScanShape(source) {
|
|
1369
|
+
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1370
|
+
if (m) {
|
|
1371
|
+
if (m[1].startsWith("^")) return null;
|
|
1372
|
+
const ranges = parseClassRanges(m[1]);
|
|
1373
|
+
return ranges ? { kind: "chars", ranges } : null;
|
|
1374
|
+
}
|
|
1375
|
+
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1376
|
+
if (m) {
|
|
1377
|
+
const open = literalCodePoints(m[1]);
|
|
1378
|
+
const stop = parseClassRanges(m[2]);
|
|
1379
|
+
if (open && stop) return { kind: "until", open, stop };
|
|
1380
|
+
return null;
|
|
1381
|
+
}
|
|
1382
|
+
if (!source.includes("\\\\")) {
|
|
1383
|
+
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
1384
|
+
if (m && m[1] && m[2]) {
|
|
1385
|
+
const open = literalCodePoints(m[1]);
|
|
1386
|
+
const close = literalCodePoints(m[2]);
|
|
1387
|
+
if (open && close) return { kind: "delimited", open, close };
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
return null;
|
|
1391
|
+
}
|
|
1392
|
+
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1393
|
+
var litCond = (base, cps, firstVar) => cps.map(
|
|
1394
|
+
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1395
|
+
).join(" && ");
|
|
1396
|
+
function advanceExpr(shape) {
|
|
1397
|
+
if (shape.kind === "chars") {
|
|
1398
|
+
return {
|
|
1399
|
+
setup: [
|
|
1400
|
+
` _e++`,
|
|
1401
|
+
` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
|
|
1402
|
+
],
|
|
1403
|
+
endVar: "_e"
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
if (shape.kind === "until") {
|
|
1407
|
+
return {
|
|
1408
|
+
setup: [
|
|
1409
|
+
` let j = _e + ${shape.open.length}`,
|
|
1410
|
+
` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
|
|
1411
|
+
],
|
|
1412
|
+
endVar: "j"
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
const n = shape.close.length;
|
|
1416
|
+
return {
|
|
1417
|
+
setup: [
|
|
1418
|
+
` let j = _e + ${shape.open.length}`,
|
|
1419
|
+
` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
|
|
1420
|
+
],
|
|
1421
|
+
endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
function guardLine(shape) {
|
|
1425
|
+
const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
|
|
1426
|
+
return ` if (${cond}) {`;
|
|
1427
|
+
}
|
|
1428
|
+
function scanBranch(shape) {
|
|
1429
|
+
if (shape.kind === "chars") {
|
|
1430
|
+
return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
|
|
1431
|
+
}
|
|
1432
|
+
const { setup, endVar } = advanceExpr(shape);
|
|
1433
|
+
return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
|
|
1434
|
+
}
|
|
1435
|
+
function captureChunk(startVar, kindIndex) {
|
|
1436
|
+
return [
|
|
1437
|
+
` if (_cap) {`,
|
|
1438
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${startVar}, _e, ${kindIndex})`,
|
|
1439
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${startVar}, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
1440
|
+
` }`
|
|
1441
|
+
];
|
|
1442
|
+
}
|
|
1443
|
+
function scanBranchLabeled(shape, kindIndex) {
|
|
1444
|
+
const { setup, endVar } = advanceExpr(shape);
|
|
1445
|
+
const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
|
|
1446
|
+
if (endVar !== "_e") lines.push(` _e = ${endVar}`);
|
|
1447
|
+
lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
|
|
1448
|
+
return lines.join("\n");
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1272
1451
|
// src/compiler/trivia-fast-path.ts
|
|
1273
|
-
var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
|
|
1274
|
-
var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
|
|
1275
|
-
String.raw`[ \t\n\r\f]+`,
|
|
1276
|
-
String.raw`[ \t\n]+`,
|
|
1277
|
-
String.raw`[ \t]+`,
|
|
1278
|
-
String.raw`[\t\n\f\r ]+`
|
|
1279
|
-
]);
|
|
1280
1452
|
function unwrapTrivia2(p) {
|
|
1281
1453
|
let cur = p;
|
|
1282
1454
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1292,36 +1464,30 @@ function choiceArms(p) {
|
|
|
1292
1464
|
if (p._def.tag === "choice") return p._def.parsers;
|
|
1293
1465
|
return null;
|
|
1294
1466
|
}
|
|
1295
|
-
function
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
function isBlockCommentSource(source) {
|
|
1299
|
-
return source === BLOCK_COMMENT;
|
|
1467
|
+
function armShape(arm) {
|
|
1468
|
+
const src = getCoreRegexDef(arm)?.source;
|
|
1469
|
+
return src ? parseScanShape(src) : null;
|
|
1300
1470
|
}
|
|
1301
1471
|
function analyzeTriviaFastPath(trivia2) {
|
|
1302
1472
|
const core = unwrapTrivia2(trivia2);
|
|
1303
|
-
const
|
|
1304
|
-
if (
|
|
1305
|
-
|
|
1306
|
-
return null;
|
|
1473
|
+
const directSrc = getCoreRegexDef(core)?.source;
|
|
1474
|
+
if (directSrc) {
|
|
1475
|
+
const shape = parseScanShape(directSrc);
|
|
1476
|
+
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1307
1477
|
}
|
|
1308
1478
|
const inner = oneOrMoreInner(core);
|
|
1309
1479
|
if (!inner) return null;
|
|
1310
|
-
const
|
|
1311
|
-
if (
|
|
1480
|
+
const innerShape = armShape(inner);
|
|
1481
|
+
if (innerShape) return innerShape.kind === "chars" ? [innerShape] : null;
|
|
1312
1482
|
const arms = choiceArms(inner);
|
|
1313
|
-
if (!arms || arms.length
|
|
1314
|
-
|
|
1315
|
-
let hasComment = false;
|
|
1483
|
+
if (!arms || arms.length < 2) return null;
|
|
1484
|
+
const shapes = [];
|
|
1316
1485
|
for (const arm of arms) {
|
|
1317
|
-
const
|
|
1318
|
-
if (!
|
|
1319
|
-
|
|
1320
|
-
else if (isBlockCommentSource(src)) hasComment = true;
|
|
1321
|
-
else return null;
|
|
1486
|
+
const shape = armShape(arm);
|
|
1487
|
+
if (!shape) return null;
|
|
1488
|
+
shapes.push(shape);
|
|
1322
1489
|
}
|
|
1323
|
-
|
|
1324
|
-
return null;
|
|
1490
|
+
return shapes;
|
|
1325
1491
|
}
|
|
1326
1492
|
var CAP_RECORD = [
|
|
1327
1493
|
` if (_cap && _e > _pos) {`,
|
|
@@ -1329,80 +1495,49 @@ var CAP_RECORD = [
|
|
|
1329
1495
|
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
|
|
1330
1496
|
` }`
|
|
1331
1497
|
].join("\n");
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1344
|
-
` let j = _e + 2`,
|
|
1345
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1346
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1347
|
-
` continue`,
|
|
1348
|
-
` }`,
|
|
1349
|
-
` break`,
|
|
1350
|
-
` }`
|
|
1351
|
-
].join("\n");
|
|
1352
|
-
var CAP_CHUNK = (wsKind, commentKind) => [
|
|
1353
|
-
` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
|
|
1354
|
-
` const _cs = _e`,
|
|
1355
|
-
` _e++`,
|
|
1356
|
-
` while (_e < input.length) {`,
|
|
1357
|
-
` const c2 = input.charCodeAt(_e)`,
|
|
1358
|
-
` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
|
|
1359
|
-
` else break`,
|
|
1360
|
-
` }`,
|
|
1361
|
-
` if (_cap) {`,
|
|
1362
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
|
|
1363
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
|
|
1364
|
-
` }`,
|
|
1365
|
-
` continue`,
|
|
1366
|
-
` }`,
|
|
1367
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1368
|
-
` const _cs = _e`,
|
|
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
|
-
` if (_cap) {`,
|
|
1373
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
|
|
1374
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
|
|
1375
|
-
` }`,
|
|
1376
|
-
` continue`,
|
|
1377
|
-
` }`
|
|
1378
|
-
].join("\n");
|
|
1379
|
-
var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
|
|
1380
|
-
` while (_e < input.length) {`,
|
|
1381
|
-
` const c = input.charCodeAt(_e)`,
|
|
1382
|
-
CAP_CHUNK(wsKind, commentKind),
|
|
1383
|
-
` break`,
|
|
1384
|
-
` }`
|
|
1385
|
-
].join("\n");
|
|
1386
|
-
function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
|
|
1387
|
-
const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
|
|
1388
|
-
const cap = kindIndices ? "" : CAP_RECORD;
|
|
1389
|
-
const lines = [
|
|
1498
|
+
function composeFastLoop(shapes) {
|
|
1499
|
+
return [
|
|
1500
|
+
` while (_e < input.length) {`,
|
|
1501
|
+
` const c = input.charCodeAt(_e)`,
|
|
1502
|
+
...shapes.map(scanBranch),
|
|
1503
|
+
` break`,
|
|
1504
|
+
` }`
|
|
1505
|
+
].join("\n");
|
|
1506
|
+
}
|
|
1507
|
+
function buildFastTriviaFnDecl(fnName, shapes) {
|
|
1508
|
+
return [
|
|
1390
1509
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1391
1510
|
` let _e = _pos`,
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1511
|
+
composeFastLoop(shapes),
|
|
1512
|
+
CAP_RECORD,
|
|
1513
|
+
` return _e`,
|
|
1514
|
+
`}`
|
|
1515
|
+
].join("\n");
|
|
1397
1516
|
}
|
|
1398
|
-
function
|
|
1517
|
+
function analyzeLabeledScannableRun(trivia2) {
|
|
1518
|
+
const spec = analyzeLabeledTrivia(trivia2);
|
|
1519
|
+
if (!spec) return null;
|
|
1520
|
+
const out = [];
|
|
1399
1521
|
for (const arm of spec.arms) {
|
|
1400
1522
|
const src = getCoreRegexDef(arm.parser)?.source;
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1523
|
+
const shape = src ? parseScanShape(src) : null;
|
|
1524
|
+
if (!shape) return null;
|
|
1525
|
+
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1404
1526
|
}
|
|
1405
|
-
return
|
|
1527
|
+
return out;
|
|
1528
|
+
}
|
|
1529
|
+
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
1530
|
+
return [
|
|
1531
|
+
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1532
|
+
` let _e = _pos`,
|
|
1533
|
+
` while (_e < input.length) {`,
|
|
1534
|
+
` const c = input.charCodeAt(_e)`,
|
|
1535
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
1536
|
+
` break`,
|
|
1537
|
+
` }`,
|
|
1538
|
+
` return _e`,
|
|
1539
|
+
`}`
|
|
1540
|
+
].join("\n");
|
|
1406
1541
|
}
|
|
1407
1542
|
function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
1408
1543
|
const tryArms = spec.arms.map((arm, i) => {
|
|
@@ -1435,14 +1570,6 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
|
1435
1570
|
`}`
|
|
1436
1571
|
].join("\n");
|
|
1437
1572
|
}
|
|
1438
|
-
function labeledTriviaKindIndices(trivia2) {
|
|
1439
|
-
const spec = analyzeLabeledTrivia(trivia2);
|
|
1440
|
-
if (!spec || spec.arms.length !== 2) return null;
|
|
1441
|
-
const ws = regexArmIndex(spec, false);
|
|
1442
|
-
const comment = regexArmIndex(spec, true);
|
|
1443
|
-
if (ws === null || comment === null) return null;
|
|
1444
|
-
return { ws, comment };
|
|
1445
|
-
}
|
|
1446
1573
|
function labeledTriviaRegexArms(trivia2) {
|
|
1447
1574
|
const spec = analyzeLabeledTrivia(trivia2);
|
|
1448
1575
|
if (!spec) return null;
|
|
@@ -1690,14 +1817,19 @@ function ensureTriviaFn(ctx) {
|
|
|
1690
1817
|
const fnName = `_tf${ctx.triviaFnNames.size}`;
|
|
1691
1818
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
1692
1819
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
1693
|
-
const fastKind = analyzeTriviaFastPath(trivia2);
|
|
1694
|
-
const kindIndices = labeledTriviaKindIndices(trivia2);
|
|
1695
|
-
if (fastKind) {
|
|
1696
|
-
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
|
|
1697
|
-
return fnName;
|
|
1698
|
-
}
|
|
1699
1820
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
1700
|
-
if (labeledSpec) {
|
|
1821
|
+
if (!labeledSpec) {
|
|
1822
|
+
const fastShapes = analyzeTriviaFastPath(trivia2);
|
|
1823
|
+
if (fastShapes) {
|
|
1824
|
+
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastShapes));
|
|
1825
|
+
return fnName;
|
|
1826
|
+
}
|
|
1827
|
+
} else {
|
|
1828
|
+
const labeledShapes = analyzeLabeledScannableRun(trivia2);
|
|
1829
|
+
if (labeledShapes) {
|
|
1830
|
+
ctx.namedFnDecls.push(buildLabeledScannableTriviaFnDecl(fnName, labeledShapes));
|
|
1831
|
+
return fnName;
|
|
1832
|
+
}
|
|
1701
1833
|
const regexSpec = labeledTriviaRegexArms(trivia2);
|
|
1702
1834
|
if (regexSpec) {
|
|
1703
1835
|
const reNames = [];
|
|
@@ -2135,11 +2267,11 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2135
2267
|
const p = def.parsers[idx];
|
|
2136
2268
|
const litVal = getCoreLiteralValue(p);
|
|
2137
2269
|
const litLen = litVal.length;
|
|
2138
|
-
const
|
|
2270
|
+
const litCond2 = emitLiteralCondition(litVal, pos);
|
|
2139
2271
|
const kw = first ? "if" : "else if";
|
|
2140
2272
|
first = false;
|
|
2141
2273
|
const litEnd = `${pos} + ${litLen}`;
|
|
2142
|
-
stmts.push(`${ind(ctx)}${kw} (${
|
|
2274
|
+
stmts.push(`${ind(ctx)}${kw} (${litCond2}) {`);
|
|
2143
2275
|
ctx.indent++;
|
|
2144
2276
|
const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
|
|
2145
2277
|
stmts.push(
|
|
@@ -2468,25 +2600,34 @@ function emitNode(def, ctx, pos) {
|
|
|
2468
2600
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
2469
2601
|
}
|
|
2470
2602
|
const i = ind(ctx);
|
|
2603
|
+
const capturesTrivia = mkType !== null || buildReadsTrivia(def);
|
|
2604
|
+
const clonesState = buildReadsState(def);
|
|
2471
2605
|
const chV = v(ctx, "_ch");
|
|
2472
2606
|
const rawV = v(ctx, "_raw");
|
|
2473
|
-
const tlV = v(ctx, "_tl");
|
|
2607
|
+
const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
2608
|
+
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
2474
2609
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
2610
|
+
const allocStmt = capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
2611
|
+
const innerTl = capturesTrivia ? tlV : "undefined";
|
|
2475
2612
|
const stmts = [
|
|
2476
|
-
|
|
2613
|
+
allocStmt,
|
|
2477
2614
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
2478
|
-
`${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${
|
|
2615
|
+
`${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}`
|
|
2479
2616
|
];
|
|
2480
2617
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2481
2618
|
stmts.push(...innerStmts);
|
|
2482
2619
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2483
2620
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, failBody(ctx, '"node"', pos)));
|
|
2484
|
-
|
|
2485
|
-
|
|
2621
|
+
let stV = "undefined";
|
|
2622
|
+
if (clonesState) {
|
|
2623
|
+
stV = v(ctx, "_nst");
|
|
2624
|
+
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
2625
|
+
}
|
|
2486
2626
|
const ndV = v(ctx, "_nd");
|
|
2487
2627
|
const ndExpr = mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `_build[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
|
|
2628
|
+
const finalExpr = def.collapse ? `${chV}.length === 1 ? (${chV}[0] !== null && typeof ${chV}[0] === 'object' && ${chV}[0]._tag === 'leaf' ? ${chV}[0].value : ${chV}[0]) : (${ndExpr})` : ndExpr;
|
|
2488
2629
|
stmts.push(
|
|
2489
|
-
`${i}const ${ndV} = ${
|
|
2630
|
+
`${i}const ${ndV} = ${finalExpr}`,
|
|
2490
2631
|
`${i}if (${sc}) ${sc}.push(${ndV})`,
|
|
2491
2632
|
`${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && ${ndV}._tag === 'node') ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
|
|
2492
2633
|
);
|
|
@@ -2507,6 +2648,16 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2507
2648
|
return { stmts, valueVar: vv, endVar: ev };
|
|
2508
2649
|
}
|
|
2509
2650
|
function emitLazy(p, def, ctx, pos) {
|
|
2651
|
+
const usage = ctx.lazyUsage;
|
|
2652
|
+
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
2653
|
+
let resolved;
|
|
2654
|
+
try {
|
|
2655
|
+
resolved = def.thunk();
|
|
2656
|
+
} catch {
|
|
2657
|
+
return emitRuntimeFallback(p, ctx, pos);
|
|
2658
|
+
}
|
|
2659
|
+
return emit(resolved, ctx, pos);
|
|
2660
|
+
}
|
|
2510
2661
|
if (!ctx.namedParsers.has(p)) {
|
|
2511
2662
|
const fnName2 = `_pf${ctx.namedParsers.size}`;
|
|
2512
2663
|
ctx.namedParsers.set(p, fnName2);
|
|
@@ -2771,6 +2922,80 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2771
2922
|
return false;
|
|
2772
2923
|
}
|
|
2773
2924
|
}
|
|
2925
|
+
function childrenOf(def) {
|
|
2926
|
+
switch (def.tag) {
|
|
2927
|
+
case "sequence":
|
|
2928
|
+
case "choice":
|
|
2929
|
+
return def.parsers;
|
|
2930
|
+
case "many":
|
|
2931
|
+
case "optional":
|
|
2932
|
+
case "transform":
|
|
2933
|
+
case "trivia":
|
|
2934
|
+
case "label":
|
|
2935
|
+
case "grammar":
|
|
2936
|
+
case "not":
|
|
2937
|
+
case "node":
|
|
2938
|
+
case "withCtx":
|
|
2939
|
+
case "expect":
|
|
2940
|
+
return [def.parser];
|
|
2941
|
+
// emitMany's oneOrMore branch and emitSepBy both codegen `def.parser` TWICE
|
|
2942
|
+
// (a mandatory first match, then again inside the repeat loop) — two real
|
|
2943
|
+
// emit() call sites, so the usage analysis must see two edges here or it
|
|
2944
|
+
// undercounts a single-use `parser` ref as inline-safe when it's actually
|
|
2945
|
+
// referenced from two positions within this one compiled function.
|
|
2946
|
+
case "oneOrMore":
|
|
2947
|
+
return [def.parser, def.parser];
|
|
2948
|
+
case "sepBy":
|
|
2949
|
+
return [def.parser, def.parser, def.separator];
|
|
2950
|
+
case "skip":
|
|
2951
|
+
return [def.main, def.skipped];
|
|
2952
|
+
case "recover":
|
|
2953
|
+
return [def.parser, def.sentinel];
|
|
2954
|
+
case "scanTo":
|
|
2955
|
+
return [def.sentinel, ...def.skip];
|
|
2956
|
+
case "lazy":
|
|
2957
|
+
case "literal":
|
|
2958
|
+
case "regex":
|
|
2959
|
+
case "keywords":
|
|
2960
|
+
case "guard":
|
|
2961
|
+
case "unknown":
|
|
2962
|
+
return [];
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
function analyzeLazyUsage(root) {
|
|
2966
|
+
return analyzeLazyUsageMulti([root]);
|
|
2967
|
+
}
|
|
2968
|
+
function analyzeLazyUsageMulti(roots) {
|
|
2969
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2970
|
+
const recursive = /* @__PURE__ */ new Set();
|
|
2971
|
+
const descended = /* @__PURE__ */ new Set();
|
|
2972
|
+
const active = /* @__PURE__ */ new Set();
|
|
2973
|
+
function walk(p) {
|
|
2974
|
+
const def = p._def;
|
|
2975
|
+
if (def.tag === "lazy") {
|
|
2976
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
2977
|
+
if (active.has(p)) {
|
|
2978
|
+
recursive.add(p);
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
if (descended.has(p)) return;
|
|
2982
|
+
descended.add(p);
|
|
2983
|
+
let resolved;
|
|
2984
|
+
try {
|
|
2985
|
+
resolved = def.thunk();
|
|
2986
|
+
} catch {
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2989
|
+
active.add(p);
|
|
2990
|
+
walk(resolved);
|
|
2991
|
+
active.delete(p);
|
|
2992
|
+
return;
|
|
2993
|
+
}
|
|
2994
|
+
for (const child of childrenOf(def)) walk(child);
|
|
2995
|
+
}
|
|
2996
|
+
for (const root of roots) walk(root);
|
|
2997
|
+
return { counts, recursive };
|
|
2998
|
+
}
|
|
2774
2999
|
function compile(combinator, mapFnSources) {
|
|
2775
3000
|
const ctx = {
|
|
2776
3001
|
vars: 0,
|
|
@@ -2787,13 +3012,16 @@ function compile(combinator, mapFnSources) {
|
|
|
2787
3012
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
2788
3013
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
2789
3014
|
namedFnDecls: [],
|
|
2790
|
-
capturing: hasNodeDef(combinator)
|
|
3015
|
+
capturing: hasNodeDef(combinator),
|
|
3016
|
+
lazyUsage: analyzeLazyUsage(combinator)
|
|
2791
3017
|
};
|
|
2792
3018
|
const r = emit(combinator, ctx, "_pos");
|
|
2793
3019
|
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
2794
3020
|
` : "";
|
|
2795
3021
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
3022
|
+
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
2796
3023
|
const source = [
|
|
3024
|
+
...emptyTlDecls,
|
|
2797
3025
|
...ctx.regexDecls,
|
|
2798
3026
|
"",
|
|
2799
3027
|
...namedPrelude,
|
|
@@ -2805,6 +3033,7 @@ function compile(combinator, mapFnSources) {
|
|
|
2805
3033
|
`}`
|
|
2806
3034
|
].join("\n");
|
|
2807
3035
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3036
|
+
...emptyTlDecls,
|
|
2808
3037
|
...ctx.regexDecls,
|
|
2809
3038
|
collatorDecl,
|
|
2810
3039
|
...namedPrelude,
|
|
@@ -2845,6 +3074,67 @@ function compile(combinator, mapFnSources) {
|
|
|
2845
3074
|
}
|
|
2846
3075
|
};
|
|
2847
3076
|
}
|
|
3077
|
+
function compileRuleMap(ruleMap) {
|
|
3078
|
+
const ctx = {
|
|
3079
|
+
vars: 0,
|
|
3080
|
+
indent: 1,
|
|
3081
|
+
regexDecls: [],
|
|
3082
|
+
regexMap: /* @__PURE__ */ new Map(),
|
|
3083
|
+
mapFns: [],
|
|
3084
|
+
mapFnSrcs: [],
|
|
3085
|
+
buildFns: [],
|
|
3086
|
+
buildSrcs: [],
|
|
3087
|
+
runtimeParsers: [],
|
|
3088
|
+
needsCollator: false,
|
|
3089
|
+
namedParsers: /* @__PURE__ */ new Map(),
|
|
3090
|
+
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
3091
|
+
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
3092
|
+
namedFnDecls: [],
|
|
3093
|
+
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
3094
|
+
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule))
|
|
3095
|
+
};
|
|
3096
|
+
const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
|
|
3097
|
+
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
3098
|
+
` : "";
|
|
3099
|
+
const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
|
|
3100
|
+
const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
|
|
3101
|
+
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
3102
|
+
const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
|
|
3103
|
+
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
3104
|
+
if (!canInline) return null;
|
|
3105
|
+
const mfDecl = derivedSrcs?.length ? ` const _mf = [${derivedSrcs.join(", ")}]` : "";
|
|
3106
|
+
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3107
|
+
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3108
|
+
const hoistedDecls = [
|
|
3109
|
+
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
3110
|
+
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
3111
|
+
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3112
|
+
mfDecl,
|
|
3113
|
+
buildDecl,
|
|
3114
|
+
...namedPrelude.map((l) => ` ${l}`),
|
|
3115
|
+
...ctx.namedFnDecls.flatMap((decl, i) => {
|
|
3116
|
+
const lines = decl.split("\n").map((l) => ` ${l}`);
|
|
3117
|
+
return i > 0 ? ["", ...lines] : lines;
|
|
3118
|
+
})
|
|
3119
|
+
].filter(Boolean);
|
|
3120
|
+
const entryFnText = (r) => [
|
|
3121
|
+
`function(input, _pos, _ctx) {`,
|
|
3122
|
+
` let pos = _pos`,
|
|
3123
|
+
...r.stmts,
|
|
3124
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
3125
|
+
`}`
|
|
3126
|
+
].join("\n");
|
|
3127
|
+
const objBody = perEntry.map(({ key, r }) => ` ${JSON.stringify(key)}: ${entryFnText(r).split("\n").join("\n ")}`).join(",\n");
|
|
3128
|
+
const replacement = [
|
|
3129
|
+
`/* @__PURE__ */ (() => {`,
|
|
3130
|
+
...hoistedDecls,
|
|
3131
|
+
` return {`,
|
|
3132
|
+
objBody,
|
|
3133
|
+
` }`,
|
|
3134
|
+
`})()`
|
|
3135
|
+
].join("\n");
|
|
3136
|
+
return { keys: perEntry.map((e) => e.key), replacement };
|
|
3137
|
+
}
|
|
2848
3138
|
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
2849
3139
|
const bodyLines = [
|
|
2850
3140
|
` let pos = _pos`,
|
|
@@ -2858,10 +3148,12 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
2858
3148
|
].join("\n");
|
|
2859
3149
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
2860
3150
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
2861
|
-
const
|
|
3151
|
+
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3152
|
+
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
2862
3153
|
if (!needsWrapper) return innerFn;
|
|
2863
3154
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
2864
3155
|
const hoistedDecls = [
|
|
3156
|
+
emptyTlDecl,
|
|
2865
3157
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
2866
3158
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
2867
3159
|
mfDecl,
|
|
@@ -3062,6 +3354,20 @@ function scopeGet(scope, name, mfs) {
|
|
|
3062
3354
|
if (isCombinator(entry)) return entry;
|
|
3063
3355
|
return null;
|
|
3064
3356
|
}
|
|
3357
|
+
function staticCollapseOption(expr) {
|
|
3358
|
+
if (expr.type !== "ObjectExpression") return false;
|
|
3359
|
+
for (const prop of expr.properties) {
|
|
3360
|
+
const p = prop;
|
|
3361
|
+
if (p.computed) continue;
|
|
3362
|
+
const key = p.key;
|
|
3363
|
+
const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
|
|
3364
|
+
if (name === "collapse") {
|
|
3365
|
+
const val = p.value;
|
|
3366
|
+
return (val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true;
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
return false;
|
|
3370
|
+
}
|
|
3065
3371
|
function exprToCombi(node2, scope, code, mfs) {
|
|
3066
3372
|
if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
|
|
3067
3373
|
if (node2.type !== "CallExpression") return null;
|
|
@@ -3110,15 +3416,16 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
3110
3416
|
}
|
|
3111
3417
|
}
|
|
3112
3418
|
if (callee.name === "node" && code !== void 0) {
|
|
3113
|
-
const [typeArg, parserArg, buildArg] = node2.arguments;
|
|
3419
|
+
const [typeArg, parserArg, buildArg, optsArg] = node2.arguments;
|
|
3114
3420
|
if (!typeArg || !parserArg || !buildArg || typeArg.type === "SpreadElement" || parserArg.type === "SpreadElement" || buildArg.type === "SpreadElement") return null;
|
|
3115
3421
|
const typeVal = anyValue(typeArg, scope, code);
|
|
3116
3422
|
if (typeof typeVal !== "string") return null;
|
|
3117
3423
|
const inner = anyValue(parserArg, scope, code, mfs);
|
|
3118
3424
|
if (!isCombinator(inner)) return null;
|
|
3119
3425
|
const buildSrc = code.slice(buildArg.start, buildArg.end);
|
|
3426
|
+
const collapse = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticCollapseOption(optsArg) : false;
|
|
3120
3427
|
try {
|
|
3121
|
-
const combi = node(typeVal, inner, () => null);
|
|
3428
|
+
const combi = node(typeVal, inner, () => null, collapse ? { collapse: true } : void 0);
|
|
3122
3429
|
if (combi._def.tag === "node") combi._def.buildSrc = buildSrc;
|
|
3123
3430
|
return combi;
|
|
3124
3431
|
} catch {
|
|
@@ -3126,6 +3433,10 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
3126
3433
|
}
|
|
3127
3434
|
}
|
|
3128
3435
|
if (callee.name === "rules") return null;
|
|
3436
|
+
if (callee.name === "ref") {
|
|
3437
|
+
if (node2.arguments.length !== 0) return null;
|
|
3438
|
+
return ref();
|
|
3439
|
+
}
|
|
3129
3440
|
if (callee.name === "parser") {
|
|
3130
3441
|
const [optsArg, rootArg] = node2.arguments;
|
|
3131
3442
|
if (!optsArg || !rootArg || optsArg.type === "SpreadElement" || rootArg.type === "SpreadElement") return null;
|
|
@@ -3365,6 +3676,40 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
|
3365
3676
|
}
|
|
3366
3677
|
return ruleRefs;
|
|
3367
3678
|
}
|
|
3679
|
+
function isDefinableRef(v2) {
|
|
3680
|
+
return isCombinator(v2) && typeof v2.define === "function" && v2._def.tag === "lazy";
|
|
3681
|
+
}
|
|
3682
|
+
function evaluateRefDeclaration(init, name, scope) {
|
|
3683
|
+
if (init.type !== "CallExpression") return null;
|
|
3684
|
+
const callee = init.callee;
|
|
3685
|
+
if (callee.type !== "Identifier" || callee.name !== "ref") return null;
|
|
3686
|
+
if (init.arguments.length !== 0) return null;
|
|
3687
|
+
const slot = ref();
|
|
3688
|
+
scope.set(name, { combi: slot, mfSrcs: [] });
|
|
3689
|
+
return slot;
|
|
3690
|
+
}
|
|
3691
|
+
function applyDefineStatement(callExpr, scope, code) {
|
|
3692
|
+
if (callExpr.type !== "CallExpression") return false;
|
|
3693
|
+
const callee = callExpr.callee;
|
|
3694
|
+
if (callee.type !== "MemberExpression") return false;
|
|
3695
|
+
const mem = callee;
|
|
3696
|
+
if (mem.computed) return false;
|
|
3697
|
+
if (mem.property.type !== "Identifier" || mem.property.name !== "define") return false;
|
|
3698
|
+
if (mem.object.type !== "Identifier" || !mem.object.name) return false;
|
|
3699
|
+
const target = scope.get(mem.object.name);
|
|
3700
|
+
const refCombi = isScopeEntry(target) ? target.combi : isCombinator(target) ? target : null;
|
|
3701
|
+
if (!refCombi || !isDefinableRef(refCombi)) return false;
|
|
3702
|
+
const args = callExpr.arguments;
|
|
3703
|
+
if (args.length !== 1 || args[0].type === "SpreadElement") return false;
|
|
3704
|
+
const inner = anyValue(args[0], scope, code, []);
|
|
3705
|
+
if (!isCombinator(inner)) return false;
|
|
3706
|
+
try {
|
|
3707
|
+
refCombi.define(inner);
|
|
3708
|
+
} catch {
|
|
3709
|
+
return false;
|
|
3710
|
+
}
|
|
3711
|
+
return true;
|
|
3712
|
+
}
|
|
3368
3713
|
function referencesAny(node2, names, scope) {
|
|
3369
3714
|
if (node2.type === "Identifier") {
|
|
3370
3715
|
return names.has(node2.name) || scope.has(node2.name);
|
|
@@ -3439,6 +3784,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3439
3784
|
const replacements = [];
|
|
3440
3785
|
const warnings = [];
|
|
3441
3786
|
let anyUnresolved = false;
|
|
3787
|
+
let ruleMapHolderCounter = 0;
|
|
3442
3788
|
const lineOf = (pos) => {
|
|
3443
3789
|
let line = 1;
|
|
3444
3790
|
for (let i = 0; i < pos && i < code.length; i++) if (code.charCodeAt(i) === 10) line++;
|
|
@@ -3460,18 +3806,53 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3460
3806
|
warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
|
|
3461
3807
|
return null;
|
|
3462
3808
|
}
|
|
3463
|
-
const
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
warn(init.start, `${label2}: rule "${ruleKey}" couldn't be inlined`);
|
|
3468
|
-
return null;
|
|
3469
|
-
}
|
|
3470
|
-
entries.push([ruleKey, compiled.inlineExpression]);
|
|
3809
|
+
const compiled = compileRuleMap([...ruleMap]);
|
|
3810
|
+
if (!compiled) {
|
|
3811
|
+
warn(init.start, `${label2}: rule map couldn't be inlined`);
|
|
3812
|
+
return null;
|
|
3471
3813
|
}
|
|
3472
|
-
return {
|
|
3814
|
+
return { replacement: compiled.replacement, ruleMap };
|
|
3473
3815
|
};
|
|
3474
3816
|
const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
|
|
3817
|
+
const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
|
|
3818
|
+
const refNames = /* @__PURE__ */ new Set();
|
|
3819
|
+
for (const stmt of body) {
|
|
3820
|
+
const innerVd = unwrapVd(stmt);
|
|
3821
|
+
if (!innerVd) continue;
|
|
3822
|
+
for (const decl of innerVd.declarations) {
|
|
3823
|
+
const d = decl;
|
|
3824
|
+
if (!d.init || d.id.type !== "Identifier") continue;
|
|
3825
|
+
const init = d.init;
|
|
3826
|
+
if (init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "ref") {
|
|
3827
|
+
refNames.add(d.id.name);
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
const defineRemovals = [];
|
|
3832
|
+
if (refNames.size > 0) {
|
|
3833
|
+
for (const stmt of body) {
|
|
3834
|
+
if (stmt.type === "ExpressionStatement") {
|
|
3835
|
+
const expr = stmt.expression;
|
|
3836
|
+
if (applyDefineStatement(expr, scope, code)) {
|
|
3837
|
+
defineRemovals.push({ start: stmt.start, end: stmt.end });
|
|
3838
|
+
}
|
|
3839
|
+
continue;
|
|
3840
|
+
}
|
|
3841
|
+
const innerVd = unwrapVd(stmt);
|
|
3842
|
+
if (!innerVd) continue;
|
|
3843
|
+
for (const decl of innerVd.declarations) {
|
|
3844
|
+
const d = decl;
|
|
3845
|
+
if (!d.init || d.id.type !== "Identifier") continue;
|
|
3846
|
+
const name = d.id.name;
|
|
3847
|
+
const init = d.init;
|
|
3848
|
+
if (evaluateRefDeclaration(init, name, scope)) continue;
|
|
3849
|
+
if (scope.has(name)) continue;
|
|
3850
|
+
if (!referencesAny(init, allNames, scope)) continue;
|
|
3851
|
+
const combi = evaluateExpr(init, scope, code, []);
|
|
3852
|
+
if (combi) scope.set(name, { combi, mfSrcs: [] });
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3475
3856
|
for (const stmt of body) {
|
|
3476
3857
|
let vd = null;
|
|
3477
3858
|
let stmtStart = stmt.start;
|
|
@@ -3495,15 +3876,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3495
3876
|
if (d.id.type === "Identifier") {
|
|
3496
3877
|
const varName = d.id.name;
|
|
3497
3878
|
if (!referencesAny(init, allNames, scope)) continue;
|
|
3879
|
+
if (refNames.has(varName)) {
|
|
3880
|
+
const refEntry = scope.get(varName);
|
|
3881
|
+
const refCombi = refEntry?.combi ?? null;
|
|
3882
|
+
if (refCombi) {
|
|
3883
|
+
const compiled2 = compile(refCombi);
|
|
3884
|
+
if (compiled2.inlineExpression === null) {
|
|
3885
|
+
warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
|
|
3886
|
+
continue;
|
|
3887
|
+
}
|
|
3888
|
+
replacements.push({ start: init.start, end: init.end, replacement: compiled2.inlineExpression });
|
|
3889
|
+
continue;
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3498
3892
|
if (isRulesCall(init)) {
|
|
3499
3893
|
const compiledRules = compileRulesFactory(init, varName);
|
|
3500
3894
|
if (!compiledRules) continue;
|
|
3501
3895
|
replacements.push({
|
|
3502
3896
|
start: init.start,
|
|
3503
3897
|
end: init.end,
|
|
3504
|
-
replacement:
|
|
3505
|
-
${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
|
|
3506
|
-
}`
|
|
3898
|
+
replacement: compiledRules.replacement
|
|
3507
3899
|
});
|
|
3508
3900
|
continue;
|
|
3509
3901
|
}
|
|
@@ -3538,9 +3930,8 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
|
|
|
3538
3930
|
}
|
|
3539
3931
|
const compiledRules = compileRulesFactory(init, "{ \u2026 }");
|
|
3540
3932
|
if (!compiledRules) continue;
|
|
3541
|
-
const byKey = new Map(compiledRules.entries);
|
|
3542
3933
|
const pattern = d.id;
|
|
3543
|
-
const
|
|
3934
|
+
const bindings = [];
|
|
3544
3935
|
let allOk = true;
|
|
3545
3936
|
for (const prop of pattern.properties) {
|
|
3546
3937
|
const p = prop;
|
|
@@ -3555,17 +3946,21 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
|
|
|
3555
3946
|
allOk = false;
|
|
3556
3947
|
break;
|
|
3557
3948
|
}
|
|
3558
|
-
|
|
3559
|
-
if (inlineExpr === void 0) {
|
|
3949
|
+
if (!compiledRules.ruleMap.has(ruleKey)) {
|
|
3560
3950
|
warn(init.start, `destructured rule "${ruleKey}" isn't returned by the rules() factory`);
|
|
3561
3951
|
allOk = false;
|
|
3562
3952
|
break;
|
|
3563
3953
|
}
|
|
3564
|
-
|
|
3954
|
+
bindings.push({ ruleKey, localName });
|
|
3565
3955
|
const rule = compiledRules.ruleMap.get(ruleKey);
|
|
3566
3956
|
if (rule) scope.set(localName, { combi: rule, mfSrcs: [] });
|
|
3567
3957
|
}
|
|
3568
3958
|
if (!allOk) continue;
|
|
3959
|
+
const holderVar = `__rules${ruleMapHolderCounter++}`;
|
|
3960
|
+
const lines = [
|
|
3961
|
+
`const ${holderVar} = ${compiledRules.replacement}`,
|
|
3962
|
+
...bindings.map(({ ruleKey, localName }) => `${exportPrefix}${kind} ${localName} = ${holderVar}[${JSON.stringify(ruleKey)}]`)
|
|
3963
|
+
];
|
|
3569
3964
|
replacements.push({
|
|
3570
3965
|
start: stmtStart,
|
|
3571
3966
|
end: stmtEnd,
|
|
@@ -3579,6 +3974,9 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
|
|
|
3579
3974
|
imp.fullyResolved = !anyUnresolved;
|
|
3580
3975
|
}
|
|
3581
3976
|
const ms = new import_magic_string.default(code);
|
|
3977
|
+
if (!anyUnresolved) {
|
|
3978
|
+
for (const { start, end } of defineRemovals) ms.remove(start, end);
|
|
3979
|
+
}
|
|
3582
3980
|
for (const imp of macroImports) {
|
|
3583
3981
|
if (imp.fullyResolved) {
|
|
3584
3982
|
ms.remove(imp.start, imp.end);
|