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.
@@ -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 node(type, combinator, build) {
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: { tag: "node", type, parser: combinator, build },
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 state = ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
1154
- const built = build(children, rawChildren, r.span, triviaLog, state);
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 isWsClassSource(source) {
1296
- return WS_CLASS_SOURCES.has(source);
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 direct = getCoreRegexDef(core)?.source;
1304
- if (direct) {
1305
- if (isWsClassSource(direct)) return "wsOnly";
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 innerSrc = getCoreRegexDef(inner)?.source;
1311
- if (innerSrc && isWsClassSource(innerSrc)) return "wsOnly";
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 !== 2) return null;
1314
- let hasWs = false;
1315
- let hasComment = false;
1483
+ if (!arms || arms.length < 2) return null;
1484
+ const shapes = [];
1316
1485
  for (const arm of arms) {
1317
- const src = getCoreRegexDef(arm)?.source;
1318
- if (!src) return null;
1319
- if (isWsClassSource(src)) hasWs = true;
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
- if (hasWs && hasComment) return "wsComments";
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
- var WS_ONLY_LOOP = [
1333
- ` while (_e < input.length) {`,
1334
- ` const c = input.charCodeAt(_e)`,
1335
- ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
1336
- ` break`,
1337
- ` }`
1338
- ].join("\n");
1339
- var WS_COMMENTS_LOOP = [
1340
- ` while (_e < input.length) {`,
1341
- ` const c = input.charCodeAt(_e)`,
1342
- ` if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12) { _e++; continue }`,
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
- loop
1393
- ];
1394
- if (cap) lines.push(cap);
1395
- lines.push(` return _e`, `}`);
1396
- return lines.join("\n");
1511
+ composeFastLoop(shapes),
1512
+ CAP_RECORD,
1513
+ ` return _e`,
1514
+ `}`
1515
+ ].join("\n");
1397
1516
  }
1398
- function regexArmIndex(spec, isComment) {
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
- if (!src) return null;
1402
- const comment = src.includes("\\*") || src.startsWith("\\/\\/");
1403
- if (comment === isComment) return arm.kindIndex;
1523
+ const shape = src ? parseScanShape(src) : null;
1524
+ if (!shape) return null;
1525
+ out.push({ shape, kindIndex: arm.kindIndex });
1404
1526
  }
1405
- return null;
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 litCond = emitLiteralCondition(litVal, pos);
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} (${litCond}) {`);
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
- `${i}const ${chV} = [], ${rawV} = [], ${tlV} = []`,
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 = ${tlV}`
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
- const stV = v(ctx, "_nst");
2485
- stmts.push(`${i}const ${stV} = _ctx.state !== undefined ? Object.assign({}, _ctx.state) : undefined`);
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} = ${ndExpr}`,
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 needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl;
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 entries = [];
3464
- for (const [ruleKey, rule] of ruleMap) {
3465
- const compiled = compile(rule);
3466
- if (compiled.inlineExpression === null) {
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 { entries, ruleMap };
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 lines = [];
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
- const inlineExpr = byKey.get(ruleKey);
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
- lines.push(`${exportPrefix}${kind} ${localName} = ${inlineExpr}`);
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);