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.js
CHANGED
|
@@ -1099,24 +1099,70 @@ function not(combinator) {
|
|
|
1099
1099
|
};
|
|
1100
1100
|
}
|
|
1101
1101
|
|
|
1102
|
+
// src/compiler/build-arity.ts
|
|
1103
|
+
var PARAM_LIST_RE = (
|
|
1104
|
+
// (params) => ... | function name?(params) ... | single-ident arrow `x => ...`
|
|
1105
|
+
/^(?:function\b[^(]*\(([^)]*)\)|\(([^)]*)\)\s*=>|([A-Za-z_$][\w$]*)\s*=>)/
|
|
1106
|
+
);
|
|
1107
|
+
var CONFIRMABLE_PARAM_RE = /^[A-Za-z_$][\w$]*\s*\??\s*(?::[^,=]+)?$/;
|
|
1108
|
+
function confirmedBuildArity(src) {
|
|
1109
|
+
const s = src.trim();
|
|
1110
|
+
const m = PARAM_LIST_RE.exec(s);
|
|
1111
|
+
if (!m) return null;
|
|
1112
|
+
if (m[3] !== void 0) return 1;
|
|
1113
|
+
const inner = (m[1] ?? m[2] ?? "").trim();
|
|
1114
|
+
if (inner === "") return 0;
|
|
1115
|
+
const parts = inner.split(",");
|
|
1116
|
+
for (const part of parts) {
|
|
1117
|
+
if (!CONFIRMABLE_PARAM_RE.test(part.trim())) return null;
|
|
1118
|
+
}
|
|
1119
|
+
if (/\barguments\b/.test(s)) return null;
|
|
1120
|
+
return parts.length;
|
|
1121
|
+
}
|
|
1122
|
+
function buildReadsTrivia(def) {
|
|
1123
|
+
const src = def.buildSrc ?? def.build.toString();
|
|
1124
|
+
const arity = confirmedBuildArity(src);
|
|
1125
|
+
if (arity === null) return true;
|
|
1126
|
+
return arity >= 4;
|
|
1127
|
+
}
|
|
1128
|
+
function buildReadsState(def) {
|
|
1129
|
+
const src = def.buildSrc ?? def.build.toString();
|
|
1130
|
+
const arity = confirmedBuildArity(src);
|
|
1131
|
+
if (arity === null) return true;
|
|
1132
|
+
return arity >= 5;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1102
1135
|
// src/combinators/node.ts
|
|
1103
|
-
function
|
|
1136
|
+
function collapseChild(child) {
|
|
1137
|
+
return child !== null && typeof child === "object" && child._tag === "leaf" ? child.value : child;
|
|
1138
|
+
}
|
|
1139
|
+
function node(type, combinator, build, opts) {
|
|
1104
1140
|
const meta = {
|
|
1105
1141
|
firstSet: combinator._meta.firstSet,
|
|
1106
1142
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
1107
1143
|
isTrivia: false
|
|
1108
1144
|
};
|
|
1145
|
+
const collapse = opts?.collapse === true;
|
|
1146
|
+
const def = collapse ? { tag: "node", type, parser: combinator, build, collapse: true } : { tag: "node", type, parser: combinator, build };
|
|
1147
|
+
const capturesTrivia = buildReadsTrivia(def);
|
|
1148
|
+
const clonesState = buildReadsState(def);
|
|
1109
1149
|
return {
|
|
1110
1150
|
_tag: "node",
|
|
1111
1151
|
_meta: meta,
|
|
1112
|
-
_def:
|
|
1152
|
+
_def: def,
|
|
1113
1153
|
parse(input, pos, ctx) {
|
|
1114
1154
|
const saved = beginCstNodeCapture(ctx);
|
|
1155
|
+
if (!capturesTrivia) ctx.captureTrivia = false;
|
|
1115
1156
|
const r = combinator.parse(input, pos, ctx);
|
|
1116
1157
|
const { children, rawChildren, triviaLog } = endCstNodeCapture(ctx, saved);
|
|
1117
1158
|
if (!r.ok) return r;
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1159
|
+
const built = collapse && children.length === 1 ? collapseChild(children[0]) : build(
|
|
1160
|
+
children,
|
|
1161
|
+
rawChildren,
|
|
1162
|
+
r.span,
|
|
1163
|
+
triviaLog,
|
|
1164
|
+
clonesState && ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0
|
|
1165
|
+
);
|
|
1120
1166
|
const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
|
|
1121
1167
|
const rawEntry = isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span };
|
|
1122
1168
|
if (saved.buf !== void 0 || saved.ch !== void 0) {
|
|
@@ -1234,14 +1280,140 @@ function noTrivia(root) {
|
|
|
1234
1280
|
return parser({ trivia: null }, root);
|
|
1235
1281
|
}
|
|
1236
1282
|
|
|
1283
|
+
// src/compiler/scannable-run.ts
|
|
1284
|
+
var CLASS_ESCAPES = { t: 9, n: 10, r: 13, f: 12, v: 11, "0": 0 };
|
|
1285
|
+
var META = new Set("()[]{}*+?|^$.".split(""));
|
|
1286
|
+
function parseClassRanges(body) {
|
|
1287
|
+
const ranges = [];
|
|
1288
|
+
let i = 0;
|
|
1289
|
+
const readCp = () => {
|
|
1290
|
+
const ch = body[i];
|
|
1291
|
+
if (ch === void 0) return null;
|
|
1292
|
+
if (ch === "\\") {
|
|
1293
|
+
const e = body[i + 1];
|
|
1294
|
+
if (e === void 0) return null;
|
|
1295
|
+
i += 2;
|
|
1296
|
+
return e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0);
|
|
1297
|
+
}
|
|
1298
|
+
i += ch.length;
|
|
1299
|
+
return ch.codePointAt(0);
|
|
1300
|
+
};
|
|
1301
|
+
while (i < body.length) {
|
|
1302
|
+
const lo = readCp();
|
|
1303
|
+
if (lo === null) return null;
|
|
1304
|
+
if (body[i] === "-" && body[i + 1] !== void 0 && body[i + 1] !== "]") {
|
|
1305
|
+
i += 1;
|
|
1306
|
+
const hi = readCp();
|
|
1307
|
+
if (hi === null) return null;
|
|
1308
|
+
ranges.push([lo, hi]);
|
|
1309
|
+
} else {
|
|
1310
|
+
ranges.push([lo, lo]);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
return ranges.length ? ranges : null;
|
|
1314
|
+
}
|
|
1315
|
+
function literalCodePoints(frag) {
|
|
1316
|
+
const out = [];
|
|
1317
|
+
let i = 0;
|
|
1318
|
+
while (i < frag.length) {
|
|
1319
|
+
const ch = frag[i];
|
|
1320
|
+
if (ch === "\\") {
|
|
1321
|
+
const e = frag[i + 1];
|
|
1322
|
+
if (e === void 0) return null;
|
|
1323
|
+
out.push(e in CLASS_ESCAPES ? CLASS_ESCAPES[e] : e.codePointAt(0));
|
|
1324
|
+
i += 2;
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
if (META.has(ch)) return null;
|
|
1328
|
+
out.push(ch.codePointAt(0));
|
|
1329
|
+
i += 1;
|
|
1330
|
+
}
|
|
1331
|
+
return out.length ? out : null;
|
|
1332
|
+
}
|
|
1333
|
+
function parseScanShape(source) {
|
|
1334
|
+
let m = /^\[((?:\\.|[^\]])+)\]([+*])$/.exec(source);
|
|
1335
|
+
if (m) {
|
|
1336
|
+
if (m[1].startsWith("^")) return null;
|
|
1337
|
+
const ranges = parseClassRanges(m[1]);
|
|
1338
|
+
return ranges ? { kind: "chars", ranges } : null;
|
|
1339
|
+
}
|
|
1340
|
+
m = /^(.*?)\[\^((?:\\.|[^\]])+)\]\*$/.exec(source);
|
|
1341
|
+
if (m) {
|
|
1342
|
+
const open = literalCodePoints(m[1]);
|
|
1343
|
+
const stop = parseClassRanges(m[2]);
|
|
1344
|
+
if (open && stop) return { kind: "until", open, stop };
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
if (!source.includes("\\\\")) {
|
|
1348
|
+
m = /^(.*?)\((?:\?:)?[\s\S]*\)\*(.*?)$/.exec(source);
|
|
1349
|
+
if (m && m[1] && m[2]) {
|
|
1350
|
+
const open = literalCodePoints(m[1]);
|
|
1351
|
+
const close = literalCodePoints(m[2]);
|
|
1352
|
+
if (open && close) return { kind: "delimited", open, close };
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return null;
|
|
1356
|
+
}
|
|
1357
|
+
var classCond = (cVar, ranges) => ranges.map(([lo, hi]) => lo === hi ? `${cVar} === ${lo}` : `(${cVar} >= ${lo} && ${cVar} <= ${hi})`).join(" || ");
|
|
1358
|
+
var litCond = (base, cps, firstVar) => cps.map(
|
|
1359
|
+
(cp, k) => k === 0 && firstVar ? `${firstVar} === ${cp}` : `input.charCodeAt(${base}${k ? ` + ${k}` : ""}) === ${cp}`
|
|
1360
|
+
).join(" && ");
|
|
1361
|
+
function advanceExpr(shape) {
|
|
1362
|
+
if (shape.kind === "chars") {
|
|
1363
|
+
return {
|
|
1364
|
+
setup: [
|
|
1365
|
+
` _e++`,
|
|
1366
|
+
` while (_e < input.length) { const c2 = input.charCodeAt(_e); if (${classCond("c2", shape.ranges)}) _e++; else break }`
|
|
1367
|
+
],
|
|
1368
|
+
endVar: "_e"
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
if (shape.kind === "until") {
|
|
1372
|
+
return {
|
|
1373
|
+
setup: [
|
|
1374
|
+
` let j = _e + ${shape.open.length}`,
|
|
1375
|
+
` while (j < input.length && !(${classCond("input.charCodeAt(j)", shape.stop)})) j++`
|
|
1376
|
+
],
|
|
1377
|
+
endVar: "j"
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
const n = shape.close.length;
|
|
1381
|
+
return {
|
|
1382
|
+
setup: [
|
|
1383
|
+
` let j = _e + ${shape.open.length}`,
|
|
1384
|
+
` while (j + ${n - 1} < input.length && !(${litCond("j", shape.close)})) j++`
|
|
1385
|
+
],
|
|
1386
|
+
endVar: n === 1 ? "(j + 1 <= input.length ? j + 1 : input.length)" : `(j + ${n} <= input.length ? j + ${n} : input.length)`
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
function guardLine(shape) {
|
|
1390
|
+
const cond = shape.kind === "chars" ? classCond("c", shape.ranges) : litCond("_e", shape.open, "c");
|
|
1391
|
+
return ` if (${cond}) {`;
|
|
1392
|
+
}
|
|
1393
|
+
function scanBranch(shape) {
|
|
1394
|
+
if (shape.kind === "chars") {
|
|
1395
|
+
return ` if (${classCond("c", shape.ranges)}) { _e++; continue }`;
|
|
1396
|
+
}
|
|
1397
|
+
const { setup, endVar } = advanceExpr(shape);
|
|
1398
|
+
return [guardLine(shape), ...setup, ` _e = ${endVar}`, ` continue`, ` }`].join("\n");
|
|
1399
|
+
}
|
|
1400
|
+
function captureChunk(startVar, kindIndex) {
|
|
1401
|
+
return [
|
|
1402
|
+
` if (_cap) {`,
|
|
1403
|
+
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(${startVar}, _e, ${kindIndex})`,
|
|
1404
|
+
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(${startVar}, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${kindIndex})`,
|
|
1405
|
+
` }`
|
|
1406
|
+
];
|
|
1407
|
+
}
|
|
1408
|
+
function scanBranchLabeled(shape, kindIndex) {
|
|
1409
|
+
const { setup, endVar } = advanceExpr(shape);
|
|
1410
|
+
const lines = [guardLine(shape), ` const _cs = _e`, ...setup];
|
|
1411
|
+
if (endVar !== "_e") lines.push(` _e = ${endVar}`);
|
|
1412
|
+
lines.push(...captureChunk("_cs", kindIndex), ` continue`, ` }`);
|
|
1413
|
+
return lines.join("\n");
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1237
1416
|
// src/compiler/trivia-fast-path.ts
|
|
1238
|
-
var BLOCK_COMMENT = String.raw`\/\*(?:[^*]|\*(?!\/))*\*\/`;
|
|
1239
|
-
var WS_CLASS_SOURCES = /* @__PURE__ */ new Set([
|
|
1240
|
-
String.raw`[ \t\n\r\f]+`,
|
|
1241
|
-
String.raw`[ \t\n]+`,
|
|
1242
|
-
String.raw`[ \t]+`,
|
|
1243
|
-
String.raw`[\t\n\f\r ]+`
|
|
1244
|
-
]);
|
|
1245
1417
|
function unwrapTrivia2(p) {
|
|
1246
1418
|
let cur = p;
|
|
1247
1419
|
while (cur._def.tag === "trivia") cur = cur._def.parser;
|
|
@@ -1257,36 +1429,30 @@ function choiceArms(p) {
|
|
|
1257
1429
|
if (p._def.tag === "choice") return p._def.parsers;
|
|
1258
1430
|
return null;
|
|
1259
1431
|
}
|
|
1260
|
-
function
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
function isBlockCommentSource(source) {
|
|
1264
|
-
return source === BLOCK_COMMENT;
|
|
1432
|
+
function armShape(arm) {
|
|
1433
|
+
const src = getCoreRegexDef(arm)?.source;
|
|
1434
|
+
return src ? parseScanShape(src) : null;
|
|
1265
1435
|
}
|
|
1266
1436
|
function analyzeTriviaFastPath(trivia2) {
|
|
1267
1437
|
const core = unwrapTrivia2(trivia2);
|
|
1268
|
-
const
|
|
1269
|
-
if (
|
|
1270
|
-
|
|
1271
|
-
return null;
|
|
1438
|
+
const directSrc = getCoreRegexDef(core)?.source;
|
|
1439
|
+
if (directSrc) {
|
|
1440
|
+
const shape = parseScanShape(directSrc);
|
|
1441
|
+
return shape && shape.kind === "chars" ? [shape] : null;
|
|
1272
1442
|
}
|
|
1273
1443
|
const inner = oneOrMoreInner(core);
|
|
1274
1444
|
if (!inner) return null;
|
|
1275
|
-
const
|
|
1276
|
-
if (
|
|
1445
|
+
const innerShape = armShape(inner);
|
|
1446
|
+
if (innerShape) return innerShape.kind === "chars" ? [innerShape] : null;
|
|
1277
1447
|
const arms = choiceArms(inner);
|
|
1278
|
-
if (!arms || arms.length
|
|
1279
|
-
|
|
1280
|
-
let hasComment = false;
|
|
1448
|
+
if (!arms || arms.length < 2) return null;
|
|
1449
|
+
const shapes = [];
|
|
1281
1450
|
for (const arm of arms) {
|
|
1282
|
-
const
|
|
1283
|
-
if (!
|
|
1284
|
-
|
|
1285
|
-
else if (isBlockCommentSource(src)) hasComment = true;
|
|
1286
|
-
else return null;
|
|
1451
|
+
const shape = armShape(arm);
|
|
1452
|
+
if (!shape) return null;
|
|
1453
|
+
shapes.push(shape);
|
|
1287
1454
|
}
|
|
1288
|
-
|
|
1289
|
-
return null;
|
|
1455
|
+
return shapes;
|
|
1290
1456
|
}
|
|
1291
1457
|
var CAP_RECORD = [
|
|
1292
1458
|
` if (_cap && _e > _pos) {`,
|
|
@@ -1294,80 +1460,49 @@ var CAP_RECORD = [
|
|
|
1294
1460
|
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
|
|
1295
1461
|
` }`
|
|
1296
1462
|
].join("\n");
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1309
|
-
` let j = _e + 2`,
|
|
1310
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1311
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1312
|
-
` continue`,
|
|
1313
|
-
` }`,
|
|
1314
|
-
` break`,
|
|
1315
|
-
` }`
|
|
1316
|
-
].join("\n");
|
|
1317
|
-
var CAP_CHUNK = (wsKind, commentKind) => [
|
|
1318
|
-
` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) {`,
|
|
1319
|
-
` const _cs = _e`,
|
|
1320
|
-
` _e++`,
|
|
1321
|
-
` while (_e < input.length) {`,
|
|
1322
|
-
` const c2 = input.charCodeAt(_e)`,
|
|
1323
|
-
` if (c2 === 32 || c2 === 9 || c2 === 10 || c2 === 13 || c2 === 12) _e++`,
|
|
1324
|
-
` else break`,
|
|
1325
|
-
` }`,
|
|
1326
|
-
` if (_cap) {`,
|
|
1327
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${wsKind})`,
|
|
1328
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${wsKind})`,
|
|
1329
|
-
` }`,
|
|
1330
|
-
` continue`,
|
|
1331
|
-
` }`,
|
|
1332
|
-
` if (c === 47 && input.charCodeAt(_e + 1) === 42) {`,
|
|
1333
|
-
` const _cs = _e`,
|
|
1334
|
-
` let j = _e + 2`,
|
|
1335
|
-
` while (j + 1 < input.length && !(input.charCodeAt(j) === 42 && input.charCodeAt(j + 1) === 47)) j++`,
|
|
1336
|
-
` _e = j + 2 <= input.length ? j + 2 : input.length`,
|
|
1337
|
-
` if (_cap) {`,
|
|
1338
|
-
` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_cs, _e, ${commentKind})`,
|
|
1339
|
-
` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_cs, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0, ${commentKind})`,
|
|
1340
|
-
` }`,
|
|
1341
|
-
` continue`,
|
|
1342
|
-
` }`
|
|
1343
|
-
].join("\n");
|
|
1344
|
-
var WS_COMMENTS_LOOP_LABELED = (wsKind, commentKind) => [
|
|
1345
|
-
` while (_e < input.length) {`,
|
|
1346
|
-
` const c = input.charCodeAt(_e)`,
|
|
1347
|
-
CAP_CHUNK(wsKind, commentKind),
|
|
1348
|
-
` break`,
|
|
1349
|
-
` }`
|
|
1350
|
-
].join("\n");
|
|
1351
|
-
function buildFastTriviaFnDecl(fnName, kind, kindIndices) {
|
|
1352
|
-
const loop = kind === "wsComments" && kindIndices ? WS_COMMENTS_LOOP_LABELED(kindIndices.ws, kindIndices.comment) : kind === "wsComments" ? WS_COMMENTS_LOOP : WS_ONLY_LOOP;
|
|
1353
|
-
const cap = kindIndices ? "" : CAP_RECORD;
|
|
1354
|
-
const lines = [
|
|
1463
|
+
function composeFastLoop(shapes) {
|
|
1464
|
+
return [
|
|
1465
|
+
` while (_e < input.length) {`,
|
|
1466
|
+
` const c = input.charCodeAt(_e)`,
|
|
1467
|
+
...shapes.map(scanBranch),
|
|
1468
|
+
` break`,
|
|
1469
|
+
` }`
|
|
1470
|
+
].join("\n");
|
|
1471
|
+
}
|
|
1472
|
+
function buildFastTriviaFnDecl(fnName, shapes) {
|
|
1473
|
+
return [
|
|
1355
1474
|
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1356
1475
|
` let _e = _pos`,
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1476
|
+
composeFastLoop(shapes),
|
|
1477
|
+
CAP_RECORD,
|
|
1478
|
+
` return _e`,
|
|
1479
|
+
`}`
|
|
1480
|
+
].join("\n");
|
|
1362
1481
|
}
|
|
1363
|
-
function
|
|
1482
|
+
function analyzeLabeledScannableRun(trivia2) {
|
|
1483
|
+
const spec = analyzeLabeledTrivia(trivia2);
|
|
1484
|
+
if (!spec) return null;
|
|
1485
|
+
const out = [];
|
|
1364
1486
|
for (const arm of spec.arms) {
|
|
1365
1487
|
const src = getCoreRegexDef(arm.parser)?.source;
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1488
|
+
const shape = src ? parseScanShape(src) : null;
|
|
1489
|
+
if (!shape) return null;
|
|
1490
|
+
out.push({ shape, kindIndex: arm.kindIndex });
|
|
1369
1491
|
}
|
|
1370
|
-
return
|
|
1492
|
+
return out;
|
|
1493
|
+
}
|
|
1494
|
+
function buildLabeledScannableTriviaFnDecl(fnName, arms) {
|
|
1495
|
+
return [
|
|
1496
|
+
`function ${fnName}(input, _pos, _ctx, _cap) {`,
|
|
1497
|
+
` let _e = _pos`,
|
|
1498
|
+
` while (_e < input.length) {`,
|
|
1499
|
+
` const c = input.charCodeAt(_e)`,
|
|
1500
|
+
...arms.map((a) => scanBranchLabeled(a.shape, a.kindIndex)),
|
|
1501
|
+
` break`,
|
|
1502
|
+
` }`,
|
|
1503
|
+
` return _e`,
|
|
1504
|
+
`}`
|
|
1505
|
+
].join("\n");
|
|
1371
1506
|
}
|
|
1372
1507
|
function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
1373
1508
|
const tryArms = spec.arms.map((arm, i) => {
|
|
@@ -1400,14 +1535,6 @@ function buildLabeledRegexTriviaFnDecl(fnName, spec, reNames) {
|
|
|
1400
1535
|
`}`
|
|
1401
1536
|
].join("\n");
|
|
1402
1537
|
}
|
|
1403
|
-
function labeledTriviaKindIndices(trivia2) {
|
|
1404
|
-
const spec = analyzeLabeledTrivia(trivia2);
|
|
1405
|
-
if (!spec || spec.arms.length !== 2) return null;
|
|
1406
|
-
const ws = regexArmIndex(spec, false);
|
|
1407
|
-
const comment = regexArmIndex(spec, true);
|
|
1408
|
-
if (ws === null || comment === null) return null;
|
|
1409
|
-
return { ws, comment };
|
|
1410
|
-
}
|
|
1411
1538
|
function labeledTriviaRegexArms(trivia2) {
|
|
1412
1539
|
const spec = analyzeLabeledTrivia(trivia2);
|
|
1413
1540
|
if (!spec) return null;
|
|
@@ -1655,14 +1782,19 @@ function ensureTriviaFn(ctx) {
|
|
|
1655
1782
|
const fnName = `_tf${ctx.triviaFnNames.size}`;
|
|
1656
1783
|
ctx.triviaFnNames.set(trivia2, fnName);
|
|
1657
1784
|
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
1658
|
-
const fastKind = analyzeTriviaFastPath(trivia2);
|
|
1659
|
-
const kindIndices = labeledTriviaKindIndices(trivia2);
|
|
1660
|
-
if (fastKind) {
|
|
1661
|
-
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastKind, kindIndices ?? void 0));
|
|
1662
|
-
return fnName;
|
|
1663
|
-
}
|
|
1664
1785
|
const labeledSpec = analyzeLabeledTrivia(trivia2);
|
|
1665
|
-
if (labeledSpec) {
|
|
1786
|
+
if (!labeledSpec) {
|
|
1787
|
+
const fastShapes = analyzeTriviaFastPath(trivia2);
|
|
1788
|
+
if (fastShapes) {
|
|
1789
|
+
ctx.namedFnDecls.push(buildFastTriviaFnDecl(fnName, fastShapes));
|
|
1790
|
+
return fnName;
|
|
1791
|
+
}
|
|
1792
|
+
} else {
|
|
1793
|
+
const labeledShapes = analyzeLabeledScannableRun(trivia2);
|
|
1794
|
+
if (labeledShapes) {
|
|
1795
|
+
ctx.namedFnDecls.push(buildLabeledScannableTriviaFnDecl(fnName, labeledShapes));
|
|
1796
|
+
return fnName;
|
|
1797
|
+
}
|
|
1666
1798
|
const regexSpec = labeledTriviaRegexArms(trivia2);
|
|
1667
1799
|
if (regexSpec) {
|
|
1668
1800
|
const reNames = [];
|
|
@@ -2100,11 +2232,11 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
2100
2232
|
const p = def.parsers[idx];
|
|
2101
2233
|
const litVal = getCoreLiteralValue(p);
|
|
2102
2234
|
const litLen = litVal.length;
|
|
2103
|
-
const
|
|
2235
|
+
const litCond2 = emitLiteralCondition(litVal, pos);
|
|
2104
2236
|
const kw = first ? "if" : "else if";
|
|
2105
2237
|
first = false;
|
|
2106
2238
|
const litEnd = `${pos} + ${litLen}`;
|
|
2107
|
-
stmts.push(`${ind(ctx)}${kw} (${
|
|
2239
|
+
stmts.push(`${ind(ctx)}${kw} (${litCond2}) {`);
|
|
2108
2240
|
ctx.indent++;
|
|
2109
2241
|
const tR = emitTransformChain(p, JSON.stringify(litVal), litEnd, pos, ctx);
|
|
2110
2242
|
stmts.push(
|
|
@@ -2433,25 +2565,34 @@ function emitNode(def, ctx, pos) {
|
|
|
2433
2565
|
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
2434
2566
|
}
|
|
2435
2567
|
const i = ind(ctx);
|
|
2568
|
+
const capturesTrivia = mkType !== null || buildReadsTrivia(def);
|
|
2569
|
+
const clonesState = buildReadsState(def);
|
|
2436
2570
|
const chV = v(ctx, "_ch");
|
|
2437
2571
|
const rawV = v(ctx, "_raw");
|
|
2438
|
-
const tlV = v(ctx, "_tl");
|
|
2572
|
+
const tlV = capturesTrivia ? v(ctx, "_tl") : "_EMPTY_TL";
|
|
2573
|
+
if (!capturesTrivia) ctx.needsEmptyTl = true;
|
|
2439
2574
|
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st"), stl = v(ctx, "_stl");
|
|
2575
|
+
const allocStmt = capturesTrivia ? `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []` : `${i}const ${chV} = [], ${rawV} = []`;
|
|
2576
|
+
const innerTl = capturesTrivia ? tlV : "undefined";
|
|
2440
2577
|
const stmts = [
|
|
2441
|
-
|
|
2578
|
+
allocStmt,
|
|
2442
2579
|
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog`,
|
|
2443
|
-
`${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${
|
|
2580
|
+
`${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true; _ctx._cstTriviaLog = ${innerTl}`
|
|
2444
2581
|
];
|
|
2445
2582
|
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
2446
2583
|
stmts.push(...innerStmts);
|
|
2447
2584
|
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}`);
|
|
2448
2585
|
stmts.push(...emitIfFail(ctx, `!${okVar}`, failBody(ctx, '"node"', pos)));
|
|
2449
|
-
|
|
2450
|
-
|
|
2586
|
+
let stV = "undefined";
|
|
2587
|
+
if (clonesState) {
|
|
2588
|
+
stV = v(ctx, "_nst");
|
|
2589
|
+
stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
|
|
2590
|
+
}
|
|
2451
2591
|
const ndV = v(ctx, "_nd");
|
|
2452
2592
|
const ndExpr = mkType ? emitInlineMkNodeExpr(mkType, chV, rawV, pos, endVar, tlV) : `_build[${buildIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} }, ${tlV}, ${stV})`;
|
|
2593
|
+
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;
|
|
2453
2594
|
stmts.push(
|
|
2454
|
-
`${i}const ${ndV} = ${
|
|
2595
|
+
`${i}const ${ndV} = ${finalExpr}`,
|
|
2455
2596
|
`${i}if (${sc}) ${sc}.push(${ndV})`,
|
|
2456
2597
|
`${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} } })`
|
|
2457
2598
|
);
|
|
@@ -2472,6 +2613,16 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
2472
2613
|
return { stmts, valueVar: vv, endVar: ev };
|
|
2473
2614
|
}
|
|
2474
2615
|
function emitLazy(p, def, ctx, pos) {
|
|
2616
|
+
const usage = ctx.lazyUsage;
|
|
2617
|
+
if (usage && (usage.counts.get(p) ?? 0) <= 1 && !usage.recursive.has(p)) {
|
|
2618
|
+
let resolved;
|
|
2619
|
+
try {
|
|
2620
|
+
resolved = def.thunk();
|
|
2621
|
+
} catch {
|
|
2622
|
+
return emitRuntimeFallback(p, ctx, pos);
|
|
2623
|
+
}
|
|
2624
|
+
return emit(resolved, ctx, pos);
|
|
2625
|
+
}
|
|
2475
2626
|
if (!ctx.namedParsers.has(p)) {
|
|
2476
2627
|
const fnName2 = `_pf${ctx.namedParsers.size}`;
|
|
2477
2628
|
ctx.namedParsers.set(p, fnName2);
|
|
@@ -2736,6 +2887,80 @@ function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
|
2736
2887
|
return false;
|
|
2737
2888
|
}
|
|
2738
2889
|
}
|
|
2890
|
+
function childrenOf(def) {
|
|
2891
|
+
switch (def.tag) {
|
|
2892
|
+
case "sequence":
|
|
2893
|
+
case "choice":
|
|
2894
|
+
return def.parsers;
|
|
2895
|
+
case "many":
|
|
2896
|
+
case "optional":
|
|
2897
|
+
case "transform":
|
|
2898
|
+
case "trivia":
|
|
2899
|
+
case "label":
|
|
2900
|
+
case "grammar":
|
|
2901
|
+
case "not":
|
|
2902
|
+
case "node":
|
|
2903
|
+
case "withCtx":
|
|
2904
|
+
case "expect":
|
|
2905
|
+
return [def.parser];
|
|
2906
|
+
// emitMany's oneOrMore branch and emitSepBy both codegen `def.parser` TWICE
|
|
2907
|
+
// (a mandatory first match, then again inside the repeat loop) — two real
|
|
2908
|
+
// emit() call sites, so the usage analysis must see two edges here or it
|
|
2909
|
+
// undercounts a single-use `parser` ref as inline-safe when it's actually
|
|
2910
|
+
// referenced from two positions within this one compiled function.
|
|
2911
|
+
case "oneOrMore":
|
|
2912
|
+
return [def.parser, def.parser];
|
|
2913
|
+
case "sepBy":
|
|
2914
|
+
return [def.parser, def.parser, def.separator];
|
|
2915
|
+
case "skip":
|
|
2916
|
+
return [def.main, def.skipped];
|
|
2917
|
+
case "recover":
|
|
2918
|
+
return [def.parser, def.sentinel];
|
|
2919
|
+
case "scanTo":
|
|
2920
|
+
return [def.sentinel, ...def.skip];
|
|
2921
|
+
case "lazy":
|
|
2922
|
+
case "literal":
|
|
2923
|
+
case "regex":
|
|
2924
|
+
case "keywords":
|
|
2925
|
+
case "guard":
|
|
2926
|
+
case "unknown":
|
|
2927
|
+
return [];
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
function analyzeLazyUsage(root) {
|
|
2931
|
+
return analyzeLazyUsageMulti([root]);
|
|
2932
|
+
}
|
|
2933
|
+
function analyzeLazyUsageMulti(roots) {
|
|
2934
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2935
|
+
const recursive = /* @__PURE__ */ new Set();
|
|
2936
|
+
const descended = /* @__PURE__ */ new Set();
|
|
2937
|
+
const active = /* @__PURE__ */ new Set();
|
|
2938
|
+
function walk(p) {
|
|
2939
|
+
const def = p._def;
|
|
2940
|
+
if (def.tag === "lazy") {
|
|
2941
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
2942
|
+
if (active.has(p)) {
|
|
2943
|
+
recursive.add(p);
|
|
2944
|
+
return;
|
|
2945
|
+
}
|
|
2946
|
+
if (descended.has(p)) return;
|
|
2947
|
+
descended.add(p);
|
|
2948
|
+
let resolved;
|
|
2949
|
+
try {
|
|
2950
|
+
resolved = def.thunk();
|
|
2951
|
+
} catch {
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
active.add(p);
|
|
2955
|
+
walk(resolved);
|
|
2956
|
+
active.delete(p);
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2959
|
+
for (const child of childrenOf(def)) walk(child);
|
|
2960
|
+
}
|
|
2961
|
+
for (const root of roots) walk(root);
|
|
2962
|
+
return { counts, recursive };
|
|
2963
|
+
}
|
|
2739
2964
|
function compile(combinator, mapFnSources) {
|
|
2740
2965
|
const ctx = {
|
|
2741
2966
|
vars: 0,
|
|
@@ -2752,13 +2977,16 @@ function compile(combinator, mapFnSources) {
|
|
|
2752
2977
|
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
2753
2978
|
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
2754
2979
|
namedFnDecls: [],
|
|
2755
|
-
capturing: hasNodeDef(combinator)
|
|
2980
|
+
capturing: hasNodeDef(combinator),
|
|
2981
|
+
lazyUsage: analyzeLazyUsage(combinator)
|
|
2756
2982
|
};
|
|
2757
2983
|
const r = emit(combinator, ctx, "_pos");
|
|
2758
2984
|
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
2759
2985
|
` : "";
|
|
2760
2986
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
|
|
2987
|
+
const emptyTlDecls = ctx.needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [];
|
|
2761
2988
|
const source = [
|
|
2989
|
+
...emptyTlDecls,
|
|
2762
2990
|
...ctx.regexDecls,
|
|
2763
2991
|
"",
|
|
2764
2992
|
...namedPrelude,
|
|
@@ -2770,6 +2998,7 @@ function compile(combinator, mapFnSources) {
|
|
|
2770
2998
|
`}`
|
|
2771
2999
|
].join("\n");
|
|
2772
3000
|
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
3001
|
+
...emptyTlDecls,
|
|
2773
3002
|
...ctx.regexDecls,
|
|
2774
3003
|
collatorDecl,
|
|
2775
3004
|
...namedPrelude,
|
|
@@ -2810,6 +3039,67 @@ function compile(combinator, mapFnSources) {
|
|
|
2810
3039
|
}
|
|
2811
3040
|
};
|
|
2812
3041
|
}
|
|
3042
|
+
function compileRuleMap(ruleMap) {
|
|
3043
|
+
const ctx = {
|
|
3044
|
+
vars: 0,
|
|
3045
|
+
indent: 1,
|
|
3046
|
+
regexDecls: [],
|
|
3047
|
+
regexMap: /* @__PURE__ */ new Map(),
|
|
3048
|
+
mapFns: [],
|
|
3049
|
+
mapFnSrcs: [],
|
|
3050
|
+
buildFns: [],
|
|
3051
|
+
buildSrcs: [],
|
|
3052
|
+
runtimeParsers: [],
|
|
3053
|
+
needsCollator: false,
|
|
3054
|
+
namedParsers: /* @__PURE__ */ new Map(),
|
|
3055
|
+
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
3056
|
+
triviaFnNames: /* @__PURE__ */ new Map(),
|
|
3057
|
+
namedFnDecls: [],
|
|
3058
|
+
capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
|
|
3059
|
+
lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule))
|
|
3060
|
+
};
|
|
3061
|
+
const perEntry = ruleMap.map(([key, rule]) => ({ key, r: emit(rule, ctx, "_pos") }));
|
|
3062
|
+
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
3063
|
+
` : "";
|
|
3064
|
+
const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
|
|
3065
|
+
const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
|
|
3066
|
+
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
3067
|
+
const mfCovered = ctx.mapFns.length === 0 || derivedSrcs !== void 0;
|
|
3068
|
+
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
3069
|
+
if (!canInline) return null;
|
|
3070
|
+
const mfDecl = derivedSrcs?.length ? ` const _mf = [${derivedSrcs.join(", ")}]` : "";
|
|
3071
|
+
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
3072
|
+
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
3073
|
+
const hoistedDecls = [
|
|
3074
|
+
ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "",
|
|
3075
|
+
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
3076
|
+
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
3077
|
+
mfDecl,
|
|
3078
|
+
buildDecl,
|
|
3079
|
+
...namedPrelude.map((l) => ` ${l}`),
|
|
3080
|
+
...ctx.namedFnDecls.flatMap((decl, i) => {
|
|
3081
|
+
const lines = decl.split("\n").map((l) => ` ${l}`);
|
|
3082
|
+
return i > 0 ? ["", ...lines] : lines;
|
|
3083
|
+
})
|
|
3084
|
+
].filter(Boolean);
|
|
3085
|
+
const entryFnText = (r) => [
|
|
3086
|
+
`function(input, _pos, _ctx) {`,
|
|
3087
|
+
` let pos = _pos`,
|
|
3088
|
+
...r.stmts,
|
|
3089
|
+
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
3090
|
+
`}`
|
|
3091
|
+
].join("\n");
|
|
3092
|
+
const objBody = perEntry.map(({ key, r }) => ` ${JSON.stringify(key)}: ${entryFnText(r).split("\n").join("\n ")}`).join(",\n");
|
|
3093
|
+
const replacement = [
|
|
3094
|
+
`/* @__PURE__ */ (() => {`,
|
|
3095
|
+
...hoistedDecls,
|
|
3096
|
+
` return {`,
|
|
3097
|
+
objBody,
|
|
3098
|
+
` }`,
|
|
3099
|
+
`})()`
|
|
3100
|
+
].join("\n");
|
|
3101
|
+
return { keys: perEntry.map((e) => e.key), replacement };
|
|
3102
|
+
}
|
|
2813
3103
|
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
2814
3104
|
const bodyLines = [
|
|
2815
3105
|
` let pos = _pos`,
|
|
@@ -2823,10 +3113,12 @@ function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources)
|
|
|
2823
3113
|
].join("\n");
|
|
2824
3114
|
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
2825
3115
|
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
2826
|
-
const
|
|
3116
|
+
const emptyTlDecl = ctx.needsEmptyTl ? ` const _EMPTY_TL = Object.freeze([])` : "";
|
|
3117
|
+
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl || !!emptyTlDecl;
|
|
2827
3118
|
if (!needsWrapper) return innerFn;
|
|
2828
3119
|
const namedPrelude = ctx.namedFnDecls.length > 0 ? namedFnPrelude() : [];
|
|
2829
3120
|
const hoistedDecls = [
|
|
3121
|
+
emptyTlDecl,
|
|
2830
3122
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
2831
3123
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
2832
3124
|
mfDecl,
|
|
@@ -3027,6 +3319,20 @@ function scopeGet(scope, name, mfs) {
|
|
|
3027
3319
|
if (isCombinator(entry)) return entry;
|
|
3028
3320
|
return null;
|
|
3029
3321
|
}
|
|
3322
|
+
function staticCollapseOption(expr) {
|
|
3323
|
+
if (expr.type !== "ObjectExpression") return false;
|
|
3324
|
+
for (const prop of expr.properties) {
|
|
3325
|
+
const p = prop;
|
|
3326
|
+
if (p.computed) continue;
|
|
3327
|
+
const key = p.key;
|
|
3328
|
+
const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? String(key.value) : void 0;
|
|
3329
|
+
if (name === "collapse") {
|
|
3330
|
+
const val = p.value;
|
|
3331
|
+
return (val.type === "Literal" || val.type === "BooleanLiteral") && val.value === true;
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
return false;
|
|
3335
|
+
}
|
|
3030
3336
|
function exprToCombi(node2, scope, code, mfs) {
|
|
3031
3337
|
if (node2.type === "Identifier") return scopeGet(scope, node2.name, mfs);
|
|
3032
3338
|
if (node2.type !== "CallExpression") return null;
|
|
@@ -3075,15 +3381,16 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
3075
3381
|
}
|
|
3076
3382
|
}
|
|
3077
3383
|
if (callee.name === "node" && code !== void 0) {
|
|
3078
|
-
const [typeArg, parserArg, buildArg] = node2.arguments;
|
|
3384
|
+
const [typeArg, parserArg, buildArg, optsArg] = node2.arguments;
|
|
3079
3385
|
if (!typeArg || !parserArg || !buildArg || typeArg.type === "SpreadElement" || parserArg.type === "SpreadElement" || buildArg.type === "SpreadElement") return null;
|
|
3080
3386
|
const typeVal = anyValue(typeArg, scope, code);
|
|
3081
3387
|
if (typeof typeVal !== "string") return null;
|
|
3082
3388
|
const inner = anyValue(parserArg, scope, code, mfs);
|
|
3083
3389
|
if (!isCombinator(inner)) return null;
|
|
3084
3390
|
const buildSrc = code.slice(buildArg.start, buildArg.end);
|
|
3391
|
+
const collapse = optsArg !== void 0 && optsArg.type !== "SpreadElement" ? staticCollapseOption(optsArg) : false;
|
|
3085
3392
|
try {
|
|
3086
|
-
const combi = node(typeVal, inner, () => null);
|
|
3393
|
+
const combi = node(typeVal, inner, () => null, collapse ? { collapse: true } : void 0);
|
|
3087
3394
|
if (combi._def.tag === "node") combi._def.buildSrc = buildSrc;
|
|
3088
3395
|
return combi;
|
|
3089
3396
|
} catch {
|
|
@@ -3091,6 +3398,10 @@ function exprToCombi(node2, scope, code, mfs) {
|
|
|
3091
3398
|
}
|
|
3092
3399
|
}
|
|
3093
3400
|
if (callee.name === "rules") return null;
|
|
3401
|
+
if (callee.name === "ref") {
|
|
3402
|
+
if (node2.arguments.length !== 0) return null;
|
|
3403
|
+
return ref();
|
|
3404
|
+
}
|
|
3094
3405
|
if (callee.name === "parser") {
|
|
3095
3406
|
const [optsArg, rootArg] = node2.arguments;
|
|
3096
3407
|
if (!optsArg || !rootArg || optsArg.type === "SpreadElement" || rootArg.type === "SpreadElement") return null;
|
|
@@ -3330,6 +3641,40 @@ function evaluateParserFactory(factoryNode, scope, code, mapFnSources) {
|
|
|
3330
3641
|
}
|
|
3331
3642
|
return ruleRefs;
|
|
3332
3643
|
}
|
|
3644
|
+
function isDefinableRef(v2) {
|
|
3645
|
+
return isCombinator(v2) && typeof v2.define === "function" && v2._def.tag === "lazy";
|
|
3646
|
+
}
|
|
3647
|
+
function evaluateRefDeclaration(init, name, scope) {
|
|
3648
|
+
if (init.type !== "CallExpression") return null;
|
|
3649
|
+
const callee = init.callee;
|
|
3650
|
+
if (callee.type !== "Identifier" || callee.name !== "ref") return null;
|
|
3651
|
+
if (init.arguments.length !== 0) return null;
|
|
3652
|
+
const slot = ref();
|
|
3653
|
+
scope.set(name, { combi: slot, mfSrcs: [] });
|
|
3654
|
+
return slot;
|
|
3655
|
+
}
|
|
3656
|
+
function applyDefineStatement(callExpr, scope, code) {
|
|
3657
|
+
if (callExpr.type !== "CallExpression") return false;
|
|
3658
|
+
const callee = callExpr.callee;
|
|
3659
|
+
if (callee.type !== "MemberExpression") return false;
|
|
3660
|
+
const mem = callee;
|
|
3661
|
+
if (mem.computed) return false;
|
|
3662
|
+
if (mem.property.type !== "Identifier" || mem.property.name !== "define") return false;
|
|
3663
|
+
if (mem.object.type !== "Identifier" || !mem.object.name) return false;
|
|
3664
|
+
const target = scope.get(mem.object.name);
|
|
3665
|
+
const refCombi = isScopeEntry(target) ? target.combi : isCombinator(target) ? target : null;
|
|
3666
|
+
if (!refCombi || !isDefinableRef(refCombi)) return false;
|
|
3667
|
+
const args = callExpr.arguments;
|
|
3668
|
+
if (args.length !== 1 || args[0].type === "SpreadElement") return false;
|
|
3669
|
+
const inner = anyValue(args[0], scope, code, []);
|
|
3670
|
+
if (!isCombinator(inner)) return false;
|
|
3671
|
+
try {
|
|
3672
|
+
refCombi.define(inner);
|
|
3673
|
+
} catch {
|
|
3674
|
+
return false;
|
|
3675
|
+
}
|
|
3676
|
+
return true;
|
|
3677
|
+
}
|
|
3333
3678
|
function referencesAny(node2, names, scope) {
|
|
3334
3679
|
if (node2.type === "Identifier") {
|
|
3335
3680
|
return names.has(node2.name) || scope.has(node2.name);
|
|
@@ -3404,6 +3749,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3404
3749
|
const replacements = [];
|
|
3405
3750
|
const warnings = [];
|
|
3406
3751
|
let anyUnresolved = false;
|
|
3752
|
+
let ruleMapHolderCounter = 0;
|
|
3407
3753
|
const lineOf = (pos) => {
|
|
3408
3754
|
let line = 1;
|
|
3409
3755
|
for (let i = 0; i < pos && i < code.length; i++) if (code.charCodeAt(i) === 10) line++;
|
|
@@ -3425,18 +3771,53 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3425
3771
|
warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
|
|
3426
3772
|
return null;
|
|
3427
3773
|
}
|
|
3428
|
-
const
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
warn(init.start, `${label2}: rule "${ruleKey}" couldn't be inlined`);
|
|
3433
|
-
return null;
|
|
3434
|
-
}
|
|
3435
|
-
entries.push([ruleKey, compiled.inlineExpression]);
|
|
3774
|
+
const compiled = compileRuleMap([...ruleMap]);
|
|
3775
|
+
if (!compiled) {
|
|
3776
|
+
warn(init.start, `${label2}: rule map couldn't be inlined`);
|
|
3777
|
+
return null;
|
|
3436
3778
|
}
|
|
3437
|
-
return {
|
|
3779
|
+
return { replacement: compiled.replacement, ruleMap };
|
|
3438
3780
|
};
|
|
3439
3781
|
const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
|
|
3782
|
+
const unwrapVd = (stmt) => stmt.type === "VariableDeclaration" ? stmt : stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration" ? stmt.declaration : null;
|
|
3783
|
+
const refNames = /* @__PURE__ */ new Set();
|
|
3784
|
+
for (const stmt of body) {
|
|
3785
|
+
const innerVd = unwrapVd(stmt);
|
|
3786
|
+
if (!innerVd) continue;
|
|
3787
|
+
for (const decl of innerVd.declarations) {
|
|
3788
|
+
const d = decl;
|
|
3789
|
+
if (!d.init || d.id.type !== "Identifier") continue;
|
|
3790
|
+
const init = d.init;
|
|
3791
|
+
if (init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "ref") {
|
|
3792
|
+
refNames.add(d.id.name);
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
const defineRemovals = [];
|
|
3797
|
+
if (refNames.size > 0) {
|
|
3798
|
+
for (const stmt of body) {
|
|
3799
|
+
if (stmt.type === "ExpressionStatement") {
|
|
3800
|
+
const expr = stmt.expression;
|
|
3801
|
+
if (applyDefineStatement(expr, scope, code)) {
|
|
3802
|
+
defineRemovals.push({ start: stmt.start, end: stmt.end });
|
|
3803
|
+
}
|
|
3804
|
+
continue;
|
|
3805
|
+
}
|
|
3806
|
+
const innerVd = unwrapVd(stmt);
|
|
3807
|
+
if (!innerVd) continue;
|
|
3808
|
+
for (const decl of innerVd.declarations) {
|
|
3809
|
+
const d = decl;
|
|
3810
|
+
if (!d.init || d.id.type !== "Identifier") continue;
|
|
3811
|
+
const name = d.id.name;
|
|
3812
|
+
const init = d.init;
|
|
3813
|
+
if (evaluateRefDeclaration(init, name, scope)) continue;
|
|
3814
|
+
if (scope.has(name)) continue;
|
|
3815
|
+
if (!referencesAny(init, allNames, scope)) continue;
|
|
3816
|
+
const combi = evaluateExpr(init, scope, code, []);
|
|
3817
|
+
if (combi) scope.set(name, { combi, mfSrcs: [] });
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3440
3821
|
for (const stmt of body) {
|
|
3441
3822
|
let vd = null;
|
|
3442
3823
|
let stmtStart = stmt.start;
|
|
@@ -3460,15 +3841,26 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
|
|
|
3460
3841
|
if (d.id.type === "Identifier") {
|
|
3461
3842
|
const varName = d.id.name;
|
|
3462
3843
|
if (!referencesAny(init, allNames, scope)) continue;
|
|
3844
|
+
if (refNames.has(varName)) {
|
|
3845
|
+
const refEntry = scope.get(varName);
|
|
3846
|
+
const refCombi = refEntry?.combi ?? null;
|
|
3847
|
+
if (refCombi) {
|
|
3848
|
+
const compiled2 = compile(refCombi);
|
|
3849
|
+
if (compiled2.inlineExpression === null) {
|
|
3850
|
+
warn(init.start, `"${varName}" is a ref() that couldn't be inlined (was .define() called with a static combinator?)`);
|
|
3851
|
+
continue;
|
|
3852
|
+
}
|
|
3853
|
+
replacements.push({ start: init.start, end: init.end, replacement: compiled2.inlineExpression });
|
|
3854
|
+
continue;
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3463
3857
|
if (isRulesCall(init)) {
|
|
3464
3858
|
const compiledRules = compileRulesFactory(init, varName);
|
|
3465
3859
|
if (!compiledRules) continue;
|
|
3466
3860
|
replacements.push({
|
|
3467
3861
|
start: init.start,
|
|
3468
3862
|
end: init.end,
|
|
3469
|
-
replacement:
|
|
3470
|
-
${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
|
|
3471
|
-
}`
|
|
3863
|
+
replacement: compiledRules.replacement
|
|
3472
3864
|
});
|
|
3473
3865
|
continue;
|
|
3474
3866
|
}
|
|
@@ -3503,9 +3895,8 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
|
|
|
3503
3895
|
}
|
|
3504
3896
|
const compiledRules = compileRulesFactory(init, "{ \u2026 }");
|
|
3505
3897
|
if (!compiledRules) continue;
|
|
3506
|
-
const byKey = new Map(compiledRules.entries);
|
|
3507
3898
|
const pattern = d.id;
|
|
3508
|
-
const
|
|
3899
|
+
const bindings = [];
|
|
3509
3900
|
let allOk = true;
|
|
3510
3901
|
for (const prop of pattern.properties) {
|
|
3511
3902
|
const p = prop;
|
|
@@ -3520,17 +3911,21 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
|
|
|
3520
3911
|
allOk = false;
|
|
3521
3912
|
break;
|
|
3522
3913
|
}
|
|
3523
|
-
|
|
3524
|
-
if (inlineExpr === void 0) {
|
|
3914
|
+
if (!compiledRules.ruleMap.has(ruleKey)) {
|
|
3525
3915
|
warn(init.start, `destructured rule "${ruleKey}" isn't returned by the rules() factory`);
|
|
3526
3916
|
allOk = false;
|
|
3527
3917
|
break;
|
|
3528
3918
|
}
|
|
3529
|
-
|
|
3919
|
+
bindings.push({ ruleKey, localName });
|
|
3530
3920
|
const rule = compiledRules.ruleMap.get(ruleKey);
|
|
3531
3921
|
if (rule) scope.set(localName, { combi: rule, mfSrcs: [] });
|
|
3532
3922
|
}
|
|
3533
3923
|
if (!allOk) continue;
|
|
3924
|
+
const holderVar = `__rules${ruleMapHolderCounter++}`;
|
|
3925
|
+
const lines = [
|
|
3926
|
+
`const ${holderVar} = ${compiledRules.replacement}`,
|
|
3927
|
+
...bindings.map(({ ruleKey, localName }) => `${exportPrefix}${kind} ${localName} = ${holderVar}[${JSON.stringify(ruleKey)}]`)
|
|
3928
|
+
];
|
|
3534
3929
|
replacements.push({
|
|
3535
3930
|
start: stmtStart,
|
|
3536
3931
|
end: stmtEnd,
|
|
@@ -3544,6 +3939,9 @@ ${compiledRules.entries.map(([k, expr]) => ` ${k}: ${expr}`).join(",\n")}
|
|
|
3544
3939
|
imp.fullyResolved = !anyUnresolved;
|
|
3545
3940
|
}
|
|
3546
3941
|
const ms = new MagicString(code);
|
|
3942
|
+
if (!anyUnresolved) {
|
|
3943
|
+
for (const { start, end } of defineRemovals) ms.remove(start, end);
|
|
3944
|
+
}
|
|
3547
3945
|
for (const imp of macroImports) {
|
|
3548
3946
|
if (imp.fullyResolved) {
|
|
3549
3947
|
ms.remove(imp.start, imp.end);
|