parseman 0.22.0 → 0.24.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AAuBA,OAAO,WAAW,MAAM,cAAc,CAAA;AAiBtC,MAAM,MAAM,uBAAuB,GAAG;IACpC,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;;;AA4MD,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAA;IAC3C,kFAAkF;IAClF,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB,CAAA;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,aAAa,cAA6B,EAC1C,kBAAkB,UAAQ,GACzB,oBAAoB,GAAG,IAAI,CA0oB7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AAuBA,OAAO,WAAW,MAAM,cAAc,CAAA;AAiBtC,MAAM,MAAM,uBAAuB,GAAG;IACpC,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;;;AA4MD,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAA;IAC3C,kFAAkF;IAClF,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB,CAAA;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,aAAa,cAA6B,EAC1C,kBAAkB,UAAQ,GACzB,oBAAoB,GAAG,IAAI,CA8wB7B"}
@@ -387,24 +387,35 @@ function literal(value, opts = {}) {
387
387
  { lo: firstUpper, hi: firstUpper }
388
388
  ] } : firstSet2;
389
389
  }
390
+ const expected = [JSON.stringify(value)];
391
+ const parse2 = !caseInsensitive && value.length === 1 ? (() => {
392
+ const code = value.charCodeAt(0);
393
+ return function parse3(input, pos, ctx) {
394
+ if (input.charCodeAt(pos) === code) {
395
+ const span = { start: pos, end: pos + 1 };
396
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
397
+ return { ok: true, value, span };
398
+ }
399
+ return failAt(ctx, expected, pos);
400
+ };
401
+ })() : function parse3(input, pos, ctx) {
402
+ const end = pos + value.length;
403
+ if (end > input.length) {
404
+ return failAt(ctx, expected, pos);
405
+ }
406
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
407
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
408
+ const span = { start: pos, end };
409
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
410
+ return { ok: true, value: matchedValue, span };
411
+ }
412
+ return failAt(ctx, expected, pos);
413
+ };
390
414
  return {
391
415
  _tag: "literal",
392
416
  _meta: meta,
393
417
  _def: { tag: "literal", value, caseInsensitive },
394
- parse(input, pos, ctx) {
395
- const end = pos + value.length;
396
- if (end > input.length) {
397
- return failAt(ctx, [JSON.stringify(value)], pos);
398
- }
399
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
400
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
401
- const span = { start: pos, end };
402
- const leaf = { _tag: "leaf", value: matchedValue, span };
403
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
404
- return { ok: true, value: matchedValue, span };
405
- }
406
- return failAt(ctx, [JSON.stringify(value)], pos);
407
- }
418
+ parse: parse2
408
419
  };
409
420
  }
410
421
 
@@ -539,8 +550,7 @@ function regex(pattern, flags = "") {
539
550
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
540
551
  const value = input.slice(pos, scanEnd);
541
552
  const span2 = { start: pos, end: scanEnd };
542
- const leaf2 = { _tag: "leaf", value, span: span2 };
543
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf2);
553
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
544
554
  return { ok: true, value, span: span2 };
545
555
  }
546
556
  anchored.lastIndex = pos;
@@ -549,8 +559,7 @@ function regex(pattern, flags = "") {
549
559
  return failAt(ctx, [`/${source}/`], pos);
550
560
  }
551
561
  const span = { start: pos, end: pos + m[0].length };
552
- const leaf = { _tag: "leaf", value: m[0], span };
553
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
562
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
554
563
  return { ok: true, value: m[0], span };
555
564
  }
556
565
  };
@@ -855,8 +864,7 @@ function keywords(words, opts = {}) {
855
864
  }
856
865
  const value = m[0];
857
866
  const span = { start: pos, end: pos + value.length };
858
- const leaf = { _tag: "leaf", value, span };
859
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
867
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
860
868
  return { ok: true, value, span };
861
869
  }
862
870
  };
@@ -1346,33 +1354,136 @@ function loopScanner(arms) {
1346
1354
  }
1347
1355
  function regexTriviaScanner(parser2) {
1348
1356
  if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1349
- const ws = whitespaceSource(parser2._def.source);
1350
- if (ws) return ws;
1351
- return blockCommentSource(parser2._def.source) ? scanBlockComment : null;
1357
+ const source = parser2._def.source;
1358
+ return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1352
1359
  }
1353
- function whitespaceSource(source) {
1354
- const m = /^\[([ \\tnrf]+)\][*+]$/.exec(source);
1355
- if (!m) return null;
1356
- const chars = /* @__PURE__ */ new Set();
1357
- for (let i = 0; i < m[1].length; i++) {
1358
- const ch = m[1][i];
1359
- if (ch !== "\\") {
1360
- chars.add(ch.charCodeAt(0));
1361
- continue;
1360
+ function inRanges2(cp, ranges) {
1361
+ for (let i = 0; i < ranges.length; i++) {
1362
+ const r = ranges[i];
1363
+ if (cp >= r[0] && cp <= r[1]) return true;
1364
+ }
1365
+ return false;
1366
+ }
1367
+ function classScanner(classBody) {
1368
+ const ranges = parseClassRanges(classBody);
1369
+ if (!ranges) return null;
1370
+ return (input, cur) => {
1371
+ let pos = cur;
1372
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1373
+ return pos;
1374
+ };
1375
+ }
1376
+ function lineCommentScanner(commentCode) {
1377
+ return (input, cur) => {
1378
+ if (input.charCodeAt(cur) !== commentCode) return cur;
1379
+ let pos = cur + 1;
1380
+ while (pos < input.length) {
1381
+ const cc = input.charCodeAt(pos);
1382
+ if (cc === 10 || cc === 13) break;
1383
+ pos++;
1362
1384
  }
1363
- const esc = m[1][++i];
1364
- if (esc === "t") chars.add(9);
1365
- else if (esc === "n") chars.add(10);
1366
- else if (esc === "r") chars.add(13);
1367
- else if (esc === "f") chars.add(12);
1368
- else return null;
1385
+ return pos;
1386
+ };
1387
+ }
1388
+ function classRunSource(source) {
1389
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1390
+ return m ? classScanner(m[1]) : null;
1391
+ }
1392
+ function classifyTriviaArm(arm) {
1393
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1394
+ if (cls) {
1395
+ const ranges = parseClassRanges(cls[1]);
1396
+ return ranges ? { kind: "class", ranges } : null;
1397
+ }
1398
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1399
+ if (lc) {
1400
+ const marker = lc[1];
1401
+ return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1369
1402
  }
1403
+ return null;
1404
+ }
1405
+ function armScanner(arm) {
1406
+ if (arm.kind === "comment") return lineCommentScanner(arm.code);
1407
+ const ranges = arm.ranges;
1370
1408
  return (input, cur) => {
1371
1409
  let pos = cur;
1372
- while (pos < input.length && chars.has(input.charCodeAt(pos))) pos++;
1410
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1373
1411
  return pos;
1374
1412
  };
1375
1413
  }
1414
+ function fusedTriviaScanner(ranges, commentCodes) {
1415
+ const c0 = commentCodes[0];
1416
+ const single = commentCodes.length === 1;
1417
+ return (input, cur) => {
1418
+ let pos = cur;
1419
+ const len = input.length;
1420
+ for (; ; ) {
1421
+ const c = input.charCodeAt(pos);
1422
+ if (pos < len && inRanges2(c, ranges)) {
1423
+ pos++;
1424
+ continue;
1425
+ }
1426
+ if (single ? c === c0 : commentCodes.includes(c)) {
1427
+ pos++;
1428
+ while (pos < len) {
1429
+ const cc = input.charCodeAt(pos);
1430
+ if (cc === 10 || cc === 13) break;
1431
+ pos++;
1432
+ }
1433
+ continue;
1434
+ }
1435
+ break;
1436
+ }
1437
+ return pos;
1438
+ };
1439
+ }
1440
+ function splitTopLevelAlts(body) {
1441
+ const arms = [];
1442
+ let inClass = false;
1443
+ let start = 0;
1444
+ for (let i = 0; i < body.length; i++) {
1445
+ const c = body[i];
1446
+ if (c === "\\") {
1447
+ i++;
1448
+ continue;
1449
+ }
1450
+ if (inClass) {
1451
+ if (c === "]") inClass = false;
1452
+ continue;
1453
+ }
1454
+ if (c === "[") inClass = true;
1455
+ else if (c === "(") return null;
1456
+ else if (c === "|") {
1457
+ arms.push(body.slice(start, i));
1458
+ start = i + 1;
1459
+ }
1460
+ }
1461
+ arms.push(body.slice(start));
1462
+ return arms;
1463
+ }
1464
+ function altStarSource(source) {
1465
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1466
+ if (!m) return null;
1467
+ const armSrcs = splitTopLevelAlts(m[1]);
1468
+ if (!armSrcs || armSrcs.length < 2) return null;
1469
+ const arms = [];
1470
+ for (const src of armSrcs) {
1471
+ const arm = classifyTriviaArm(src);
1472
+ if (!arm) return null;
1473
+ arms.push(arm);
1474
+ }
1475
+ const ranges = [];
1476
+ const commentCodes = [];
1477
+ for (const arm of arms) {
1478
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1479
+ else commentCodes.push(arm.code);
1480
+ }
1481
+ if (commentCodes.some((code) => inRanges2(code, ranges))) {
1482
+ return loopScanner(arms.map(armScanner));
1483
+ }
1484
+ if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
1485
+ return fusedTriviaScanner(ranges, commentCodes);
1486
+ }
1376
1487
  function blockCommentSource(source) {
1377
1488
  return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
1378
1489
  }
@@ -1689,6 +1800,7 @@ function markUnusedValues(root) {
1689
1800
  }
1690
1801
 
1691
1802
  // src/combinators/parser.ts
1803
+ var RULE_ORDER = "__parsemanRuleOrder";
1692
1804
  function tagRule(r, key) {
1693
1805
  ;
1694
1806
  r._ruleName = key;
@@ -1733,6 +1845,11 @@ function rules(a, b) {
1733
1845
  for (const key of Object.keys(definitions)) {
1734
1846
  markUnusedValues(cache[key]);
1735
1847
  }
1848
+ Object.defineProperty(cache, RULE_ORDER, {
1849
+ value: Object.keys(definitions),
1850
+ enumerable: false,
1851
+ configurable: true
1852
+ });
1736
1853
  return cache;
1737
1854
  }
1738
1855
 
@@ -5504,9 +5621,10 @@ function compileRuleMap(ruleMap, opts) {
5504
5621
  ].join("\n");
5505
5622
  return { keys: perEntry.map((e) => e.key), replacement };
5506
5623
  }
5507
- function compileLinkable(ruleMapArg, ns) {
5624
+ function compileLinkable(ruleMapArg, ns, opts) {
5508
5625
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
5509
5626
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
5627
+ const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
5510
5628
  const ruleMap = ruleMapArg.filter(([, val]) => {
5511
5629
  const d = val._def;
5512
5630
  if (d.tag !== "lazy") return true;
@@ -5537,7 +5655,8 @@ function compileLinkable(ruleMapArg, ns) {
5537
5655
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
5538
5656
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5539
5657
  ns,
5540
- deferFirstSetRefs: true
5658
+ deferFirstSetRefs: true,
5659
+ ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5541
5660
  };
5542
5661
  const seen = /* @__PURE__ */ new Map();
5543
5662
  const ruleNames = /* @__PURE__ */ new Map();
@@ -5600,7 +5719,10 @@ function compileLinkable(ruleMapArg, ns) {
5600
5719
  ctx.indent = 1;
5601
5720
  ctx.failLabel = "_pfail";
5602
5721
  ctx.recordFail = true;
5722
+ const savedTrivia = ctx.activeTrivia;
5723
+ if (rule._meta.isTrivia) ctx.activeTrivia = void 0;
5603
5724
  const body = emit(resolved, ctx, "_pos");
5725
+ ctx.activeTrivia = savedTrivia;
5604
5726
  ctx.indent = savedIndent;
5605
5727
  ctx.failLabel = savedFail;
5606
5728
  ctx.recordFail = savedRec;
@@ -6094,6 +6216,23 @@ ${body}
6094
6216
  };
6095
6217
 
6096
6218
  // src/compiler/linker.ts
6219
+ function pickPieces(pieces, names) {
6220
+ const winner = /* @__PURE__ */ new Map();
6221
+ for (const pc of pieces) for (const k of pc.keys) winner.set(k, pc);
6222
+ for (const n of names) {
6223
+ if (!winner.has(n)) throw new Error(`pick: rule "${n}" is not in this grammar (available: ${[...winner.keys()].join(", ")})`);
6224
+ }
6225
+ const keep = /* @__PURE__ */ new Set();
6226
+ const visit = (n) => {
6227
+ const w = winner.get(n);
6228
+ if (keep.has(n) || !w) return;
6229
+ keep.add(n);
6230
+ for (const d of w.deps.get(n) ?? []) visit(d);
6231
+ };
6232
+ for (const n of names) visit(n);
6233
+ const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
6234
+ return pieces.map((pc) => ({ ...pc, keys: pc.keys.filter((k) => keep.has(k) && winner.get(k) === pc), ruleFns: filt(pc.ruleFns, pc), wrappers: filt(pc.wrappers, pc), deps: filt(pc.deps, pc) })).filter((pc) => pc.keys.length > 0);
6235
+ }
6097
6236
  function fusedBody(pieces) {
6098
6237
  const winner = /* @__PURE__ */ new Map();
6099
6238
  for (const p of pieces) for (const k of p.keys) winner.set(k, p);
@@ -6113,7 +6252,7 @@ function fusedBody(pieces) {
6113
6252
  ...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
6114
6253
  ...needsHostReads ? [HOST_READS_DECL] : [],
6115
6254
  // Each contributing artifact's namespaced private prelude (regexes, _pf, …).
6116
- ...[...new Set(contributingPieces.flatMap((p) => p.prelude))],
6255
+ ...new Set(contributingPieces.flatMap((p) => p.prelude)),
6117
6256
  // The winning `_r_<Name>` function for each rule (one per name → no redeclare).
6118
6257
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
6119
6258
  ];
@@ -6154,9 +6293,9 @@ ${body}
6154
6293
  function isIRPiece(p) {
6155
6294
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
6156
6295
  }
6157
- function materializePiece(p) {
6296
+ function materializePiece(p, trivia2) {
6158
6297
  if (!isIRPiece(p)) return p;
6159
- const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
6298
+ const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, trivia2 ? { trivia: trivia2 } : void 0);
6160
6299
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
6161
6300
  return pieces;
6162
6301
  }
@@ -6862,11 +7001,13 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6862
7001
  warn(init.start, `${label2}: rule map couldn't be inlined`);
6863
7002
  return null;
6864
7003
  }
6865
- return { replacement: compiled.replacement, ruleMap };
7004
+ return { replacement: compiled.replacement, ruleMap, ...gTrivia ? { trivia: gTrivia } : {} };
6866
7005
  };
6867
7006
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
6868
7007
  const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
7008
+ const isPickCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "pick";
6869
7009
  const localRuleMaps = /* @__PURE__ */ new Map();
7010
+ const localGrammarTrivia = /* @__PURE__ */ new Map();
6870
7011
  const nsFor = (label2) => `_${createHash("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
6871
7012
  const stripIndent = (s) => s.replace(/\n[ \t]+/g, "\n");
6872
7013
  const serializePieces = (p) => {
@@ -6880,7 +7021,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6880
7021
  const serializeItem = (it) => isSpread(it) ? `...(${it.__spreadLocal}[Symbol.for('parseman.composedPieces')] ?? [])` : isIR(it) ? `{ ns: ${JSON.stringify(it.ns)}, ir: ${JSON.stringify(it.ir)} }` : serializePieces(it);
6881
7022
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
6882
7023
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
6883
- const localComposedPieces = /* @__PURE__ */ new Map();
7024
+ const localComposedCarried = /* @__PURE__ */ new Map();
7025
+ const localComposedTrivia = /* @__PURE__ */ new Map();
6884
7026
  const importedPiecesCache = /* @__PURE__ */ new Map();
6885
7027
  const resolveModulePieces = (file, exportName, seen) => {
6886
7028
  const mod = parseModuleCached(file);
@@ -6914,7 +7056,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6914
7056
  }
6915
7057
  try {
6916
7058
  const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
6917
- return list.map(materializePiece);
7059
+ return list;
6918
7060
  } catch {
6919
7061
  return null;
6920
7062
  }
@@ -6934,32 +7076,108 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6934
7076
  importedPiecesCache.set(localName, result2);
6935
7077
  return result2;
6936
7078
  };
6937
- const localArg = (rm, label2) => {
7079
+ const materializeCarried = (items, composing) => {
7080
+ const out = [];
7081
+ for (const it of items) {
7082
+ if (isSpread(it)) {
7083
+ for (const p of importedPieces(it.__spreadLocal) ?? []) out.push(materializePiece(p, composing));
7084
+ } else if (isIR(it)) {
7085
+ out.push(materializePiece(it, composing));
7086
+ } else {
7087
+ out.push(it);
7088
+ }
7089
+ }
7090
+ return out;
7091
+ };
7092
+ const localCarried = (rm, label2, composing) => {
6938
7093
  const entries = [...rm];
6939
7094
  const ns = nsFor(label2);
6940
- const p = compileLinkable(entries, ns);
6941
- if (!p) return null;
6942
7095
  const ir = serializeRuleMap(entries);
6943
- return ir ? { pieces: [p], ir: { ns, ir } } : { pieces: [p] };
7096
+ if (ir) return { carried: [{ ns, ir }] };
7097
+ const p = compileLinkable(entries, ns, composing ? { trivia: composing } : void 0);
7098
+ return p ? { carried: [p] } : null;
6944
7099
  };
6945
- const argPieces = (arg, label2) => {
7100
+ const argPieces = (arg, label2, composing) => {
7101
+ if (isPickCall(arg)) {
7102
+ const pargs = arg.arguments;
7103
+ const inner = pargs[0];
7104
+ const namesArg = pargs[1];
7105
+ if (!inner || namesArg?.type !== "ArrayExpression") return null;
7106
+ const names = [];
7107
+ for (const el of namesArg.elements ?? []) {
7108
+ const v2 = el?.value;
7109
+ if ((el?.type === "Literal" || el?.type === "StringLiteral") && typeof v2 === "string") names.push(v2);
7110
+ else return null;
7111
+ }
7112
+ const innerArg = argPieces(inner, `${label2}_pick`);
7113
+ if (!innerArg) return null;
7114
+ try {
7115
+ return { carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names) };
7116
+ } catch (e) {
7117
+ warn(arg.start, `pick(): ${e.message}`);
7118
+ return null;
7119
+ }
7120
+ }
6946
7121
  if (isRulesCall(arg)) {
6947
- const factory = arg.arguments[0];
7122
+ const rulesArgs = arg.arguments;
7123
+ const a0 = rulesArgs[0];
7124
+ const a1 = rulesArgs[1];
7125
+ const optionsFirst = a0?.type === "ObjectExpression";
7126
+ const factory = optionsFirst ? a1 : a0;
6948
7127
  const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
6949
- return rm ? localArg(rm, label2) : null;
7128
+ return rm ? localCarried(rm, label2, composing) : null;
6950
7129
  }
6951
7130
  if (arg.type === "Identifier") {
6952
7131
  const name = arg.name;
6953
7132
  const rm = localRuleMaps.get(name);
6954
- if (rm) return localArg(rm, label2);
6955
- const composed = localComposedPieces.get(name);
6956
- if (composed) return { pieces: composed };
7133
+ if (rm) return localCarried(rm, label2, composing);
7134
+ const composed = localComposedCarried.get(name);
7135
+ if (composed) return { carried: composed };
6957
7136
  const pieces = importedPieces(name);
6958
7137
  if (!pieces) return null;
6959
- return importBindings.has(name) ? { pieces, spreadLocal: name } : { pieces };
7138
+ return { carried: importBindings.has(name) ? [{ __spreadLocal: name }] : [...pieces] };
6960
7139
  }
6961
7140
  return null;
6962
7141
  };
7142
+ const rulesCallTrivia = (el) => {
7143
+ const rulesArgs = el.arguments;
7144
+ const a0 = rulesArgs[0];
7145
+ const a1 = rulesArgs[1];
7146
+ const optExpr = a0?.type === "ObjectExpression" ? a0 : a1?.type === "ObjectExpression" ? a1 : void 0;
7147
+ const triviaProp = (optExpr?.properties ?? []).find(
7148
+ (p) => p.key?.name === "trivia"
7149
+ );
7150
+ if (!triviaProp?.value) return void 0;
7151
+ return evaluateExpr(triviaProp.value, scope, code, []) ?? void 0;
7152
+ };
7153
+ const composingTrivia = (elements) => {
7154
+ for (let i = elements.length - 1; i >= 0; i--) {
7155
+ const el = elements[i];
7156
+ if (!el) continue;
7157
+ if (isRulesCall(el)) {
7158
+ const t = rulesCallTrivia(el);
7159
+ if (t) return t;
7160
+ continue;
7161
+ }
7162
+ if (el.type === "Identifier") {
7163
+ const t = localGrammarTrivia.get(el.name);
7164
+ if (t) return t;
7165
+ }
7166
+ }
7167
+ return void 0;
7168
+ };
7169
+ const ownTrivia = (arg) => {
7170
+ if (isRulesCall(arg)) return rulesCallTrivia(arg);
7171
+ if (isPickCall(arg)) {
7172
+ const inner = arg.arguments[0];
7173
+ return inner ? ownTrivia(inner) : void 0;
7174
+ }
7175
+ if (arg.type === "Identifier") {
7176
+ const name = arg.name;
7177
+ return localGrammarTrivia.get(name) ?? localComposedTrivia.get(name);
7178
+ }
7179
+ return void 0;
7180
+ };
6963
7181
  const compileComposeCall = (init) => {
6964
7182
  const args = init.arguments;
6965
7183
  const arr = args[0];
@@ -6968,21 +7186,19 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6968
7186
  return null;
6969
7187
  }
6970
7188
  const elements = arr.elements;
6971
- const pieces = [];
7189
+ const composing = composingTrivia(elements);
6972
7190
  const carried = [];
6973
7191
  for (let i = 0; i < elements.length; i++) {
6974
- const r = argPieces(elements[i], `compose${init.start}_${i}`);
7192
+ const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
6975
7193
  if (!r) {
6976
7194
  warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
6977
7195
  return null;
6978
7196
  }
6979
- pieces.push(...r.pieces);
6980
- if (r.spreadLocal) carried.push({ __spreadLocal: r.spreadLocal });
6981
- else if (r.ir) carried.push(r.ir);
6982
- else carried.push(...r.pieces);
7197
+ carried.push(...r.carried);
6983
7198
  }
7199
+ const pieces = materializeCarried(carried, composing);
6984
7200
  try {
6985
- return { replacement: emitFusedSource(pieces), pieces, carried };
7201
+ return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
6986
7202
  } catch (e) {
6987
7203
  warn(init.start, `compose(): ${e.message}; falling back to runtime`);
6988
7204
  return null;
@@ -7067,6 +7283,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7067
7283
  const compiledRules = compileRulesFactory(init, varName);
7068
7284
  if (!compiledRules) continue;
7069
7285
  localRuleMaps.set(varName, compiledRules.ruleMap);
7286
+ if (compiledRules.trivia) localGrammarTrivia.set(varName, compiledRules.trivia);
7070
7287
  let replacement = compiledRules.replacement;
7071
7288
  if (exportPrefix) {
7072
7289
  const ns = nsFor(varName);
@@ -7082,7 +7299,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7082
7299
  if (isComposeCall(init)) {
7083
7300
  const fused = compileComposeCall(init);
7084
7301
  if (!fused) continue;
7085
- localComposedPieces.set(varName, fused.pieces);
7302
+ localComposedCarried.set(varName, fused.carried);
7303
+ if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
7086
7304
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
7087
7305
  replacements.push({ start: init.start, end: init.end, replacement });
7088
7306
  continue;