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.
@@ -422,24 +422,35 @@ function literal(value, opts = {}) {
422
422
  { lo: firstUpper, hi: firstUpper }
423
423
  ] } : firstSet2;
424
424
  }
425
+ const expected = [JSON.stringify(value)];
426
+ const parse2 = !caseInsensitive && value.length === 1 ? (() => {
427
+ const code = value.charCodeAt(0);
428
+ return function parse3(input, pos, ctx) {
429
+ if (input.charCodeAt(pos) === code) {
430
+ const span = { start: pos, end: pos + 1 };
431
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
432
+ return { ok: true, value, span };
433
+ }
434
+ return failAt(ctx, expected, pos);
435
+ };
436
+ })() : function parse3(input, pos, ctx) {
437
+ const end = pos + value.length;
438
+ if (end > input.length) {
439
+ return failAt(ctx, expected, pos);
440
+ }
441
+ const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
442
+ if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
443
+ const span = { start: pos, end };
444
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: matchedValue, span });
445
+ return { ok: true, value: matchedValue, span };
446
+ }
447
+ return failAt(ctx, expected, pos);
448
+ };
425
449
  return {
426
450
  _tag: "literal",
427
451
  _meta: meta,
428
452
  _def: { tag: "literal", value, caseInsensitive },
429
- parse(input, pos, ctx) {
430
- const end = pos + value.length;
431
- if (end > input.length) {
432
- return failAt(ctx, [JSON.stringify(value)], pos);
433
- }
434
- const matchedValue = caseInsensitive ? input.slice(pos, end) : value;
435
- if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
436
- const span = { start: pos, end };
437
- const leaf = { _tag: "leaf", value: matchedValue, span };
438
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
439
- return { ok: true, value: matchedValue, span };
440
- }
441
- return failAt(ctx, [JSON.stringify(value)], pos);
442
- }
453
+ parse: parse2
443
454
  };
444
455
  }
445
456
 
@@ -574,8 +585,7 @@ function regex(pattern, flags = "") {
574
585
  if (scanEnd === null) return failAt(ctx, [`/${source}/`], pos);
575
586
  const value = input.slice(pos, scanEnd);
576
587
  const span2 = { start: pos, end: scanEnd };
577
- const leaf2 = { _tag: "leaf", value, span: span2 };
578
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf2);
588
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span: span2 });
579
589
  return { ok: true, value, span: span2 };
580
590
  }
581
591
  anchored.lastIndex = pos;
@@ -584,8 +594,7 @@ function regex(pattern, flags = "") {
584
594
  return failAt(ctx, [`/${source}/`], pos);
585
595
  }
586
596
  const span = { start: pos, end: pos + m[0].length };
587
- const leaf = { _tag: "leaf", value: m[0], span };
588
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
597
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value: m[0], span });
589
598
  return { ok: true, value: m[0], span };
590
599
  }
591
600
  };
@@ -890,8 +899,7 @@ function keywords(words, opts = {}) {
890
899
  }
891
900
  const value = m[0];
892
901
  const span = { start: pos, end: pos + value.length };
893
- const leaf = { _tag: "leaf", value, span };
894
- if (cstCaptureActive(ctx)) pushCstLeaf(ctx, leaf);
902
+ if (cstCaptureActive(ctx)) pushCstLeaf(ctx, { _tag: "leaf", value, span });
895
903
  return { ok: true, value, span };
896
904
  }
897
905
  };
@@ -1381,33 +1389,136 @@ function loopScanner(arms) {
1381
1389
  }
1382
1390
  function regexTriviaScanner(parser2) {
1383
1391
  if (parser2._def.tag !== "regex" || parser2._def.flags) return null;
1384
- const ws = whitespaceSource(parser2._def.source);
1385
- if (ws) return ws;
1386
- return blockCommentSource(parser2._def.source) ? scanBlockComment : null;
1392
+ const source = parser2._def.source;
1393
+ return classRunSource(source) ?? altStarSource(source) ?? (blockCommentSource(source) ? scanBlockComment : null);
1387
1394
  }
1388
- function whitespaceSource(source) {
1389
- const m = /^\[([ \\tnrf]+)\][*+]$/.exec(source);
1390
- if (!m) return null;
1391
- const chars = /* @__PURE__ */ new Set();
1392
- for (let i = 0; i < m[1].length; i++) {
1393
- const ch = m[1][i];
1394
- if (ch !== "\\") {
1395
- chars.add(ch.charCodeAt(0));
1396
- continue;
1395
+ function inRanges2(cp, ranges) {
1396
+ for (let i = 0; i < ranges.length; i++) {
1397
+ const r = ranges[i];
1398
+ if (cp >= r[0] && cp <= r[1]) return true;
1399
+ }
1400
+ return false;
1401
+ }
1402
+ function classScanner(classBody) {
1403
+ const ranges = parseClassRanges(classBody);
1404
+ if (!ranges) return null;
1405
+ return (input, cur) => {
1406
+ let pos = cur;
1407
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1408
+ return pos;
1409
+ };
1410
+ }
1411
+ function lineCommentScanner(commentCode) {
1412
+ return (input, cur) => {
1413
+ if (input.charCodeAt(cur) !== commentCode) return cur;
1414
+ let pos = cur + 1;
1415
+ while (pos < input.length) {
1416
+ const cc = input.charCodeAt(pos);
1417
+ if (cc === 10 || cc === 13) break;
1418
+ pos++;
1397
1419
  }
1398
- const esc = m[1][++i];
1399
- if (esc === "t") chars.add(9);
1400
- else if (esc === "n") chars.add(10);
1401
- else if (esc === "r") chars.add(13);
1402
- else if (esc === "f") chars.add(12);
1403
- else return null;
1420
+ return pos;
1421
+ };
1422
+ }
1423
+ function classRunSource(source) {
1424
+ const m = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]$/.exec(source);
1425
+ return m ? classScanner(m[1]) : null;
1426
+ }
1427
+ function classifyTriviaArm(arm) {
1428
+ const cls = /^\[([^\]^](?:[^\]]|\\.)*)\][*+]?$/.exec(arm);
1429
+ if (cls) {
1430
+ const ranges = parseClassRanges(cls[1]);
1431
+ return ranges ? { kind: "class", ranges } : null;
1432
+ }
1433
+ const lc = /^(\\?.)\[\^\\n\\r\]\*$/.exec(arm);
1434
+ if (lc) {
1435
+ const marker = lc[1];
1436
+ return { kind: "comment", code: (marker.length === 2 ? marker[1] : marker[0]).charCodeAt(0) };
1404
1437
  }
1438
+ return null;
1439
+ }
1440
+ function armScanner(arm) {
1441
+ if (arm.kind === "comment") return lineCommentScanner(arm.code);
1442
+ const ranges = arm.ranges;
1405
1443
  return (input, cur) => {
1406
1444
  let pos = cur;
1407
- while (pos < input.length && chars.has(input.charCodeAt(pos))) pos++;
1445
+ while (pos < input.length && inRanges2(input.charCodeAt(pos), ranges)) pos++;
1408
1446
  return pos;
1409
1447
  };
1410
1448
  }
1449
+ function fusedTriviaScanner(ranges, commentCodes) {
1450
+ const c0 = commentCodes[0];
1451
+ const single = commentCodes.length === 1;
1452
+ return (input, cur) => {
1453
+ let pos = cur;
1454
+ const len = input.length;
1455
+ for (; ; ) {
1456
+ const c = input.charCodeAt(pos);
1457
+ if (pos < len && inRanges2(c, ranges)) {
1458
+ pos++;
1459
+ continue;
1460
+ }
1461
+ if (single ? c === c0 : commentCodes.includes(c)) {
1462
+ pos++;
1463
+ while (pos < len) {
1464
+ const cc = input.charCodeAt(pos);
1465
+ if (cc === 10 || cc === 13) break;
1466
+ pos++;
1467
+ }
1468
+ continue;
1469
+ }
1470
+ break;
1471
+ }
1472
+ return pos;
1473
+ };
1474
+ }
1475
+ function splitTopLevelAlts(body) {
1476
+ const arms = [];
1477
+ let inClass = false;
1478
+ let start = 0;
1479
+ for (let i = 0; i < body.length; i++) {
1480
+ const c = body[i];
1481
+ if (c === "\\") {
1482
+ i++;
1483
+ continue;
1484
+ }
1485
+ if (inClass) {
1486
+ if (c === "]") inClass = false;
1487
+ continue;
1488
+ }
1489
+ if (c === "[") inClass = true;
1490
+ else if (c === "(") return null;
1491
+ else if (c === "|") {
1492
+ arms.push(body.slice(start, i));
1493
+ start = i + 1;
1494
+ }
1495
+ }
1496
+ arms.push(body.slice(start));
1497
+ return arms;
1498
+ }
1499
+ function altStarSource(source) {
1500
+ const m = /^\(\?:(.*)\)[*+]$/.exec(source);
1501
+ if (!m) return null;
1502
+ const armSrcs = splitTopLevelAlts(m[1]);
1503
+ if (!armSrcs || armSrcs.length < 2) return null;
1504
+ const arms = [];
1505
+ for (const src of armSrcs) {
1506
+ const arm = classifyTriviaArm(src);
1507
+ if (!arm) return null;
1508
+ arms.push(arm);
1509
+ }
1510
+ const ranges = [];
1511
+ const commentCodes = [];
1512
+ for (const arm of arms) {
1513
+ if (arm.kind === "class") ranges.push(...arm.ranges);
1514
+ else commentCodes.push(arm.code);
1515
+ }
1516
+ if (commentCodes.some((code) => inRanges2(code, ranges))) {
1517
+ return loopScanner(arms.map(armScanner));
1518
+ }
1519
+ if (commentCodes.length === 0) return armScanner({ kind: "class", ranges });
1520
+ return fusedTriviaScanner(ranges, commentCodes);
1521
+ }
1411
1522
  function blockCommentSource(source) {
1412
1523
  return source === "\\/\\*(?:[^*]|\\*(?!\\/))*\\*\\/" || source === "\\/\\*[^]*?\\*\\/";
1413
1524
  }
@@ -1724,6 +1835,7 @@ function markUnusedValues(root) {
1724
1835
  }
1725
1836
 
1726
1837
  // src/combinators/parser.ts
1838
+ var RULE_ORDER = "__parsemanRuleOrder";
1727
1839
  function tagRule(r, key) {
1728
1840
  ;
1729
1841
  r._ruleName = key;
@@ -1768,6 +1880,11 @@ function rules(a, b) {
1768
1880
  for (const key of Object.keys(definitions)) {
1769
1881
  markUnusedValues(cache[key]);
1770
1882
  }
1883
+ Object.defineProperty(cache, RULE_ORDER, {
1884
+ value: Object.keys(definitions),
1885
+ enumerable: false,
1886
+ configurable: true
1887
+ });
1771
1888
  return cache;
1772
1889
  }
1773
1890
 
@@ -5539,9 +5656,10 @@ function compileRuleMap(ruleMap, opts) {
5539
5656
  ].join("\n");
5540
5657
  return { keys: perEntry.map((e) => e.key), replacement };
5541
5658
  }
5542
- function compileLinkable(ruleMapArg, ns) {
5659
+ function compileLinkable(ruleMapArg, ns, opts) {
5543
5660
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
5544
5661
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
5662
+ const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
5545
5663
  const ruleMap = ruleMapArg.filter(([, val]) => {
5546
5664
  const d = val._def;
5547
5665
  if (d.tag !== "lazy") return true;
@@ -5572,7 +5690,8 @@ function compileLinkable(ruleMapArg, ns) {
5572
5690
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
5573
5691
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5574
5692
  ns,
5575
- deferFirstSetRefs: true
5693
+ deferFirstSetRefs: true,
5694
+ ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5576
5695
  };
5577
5696
  const seen = /* @__PURE__ */ new Map();
5578
5697
  const ruleNames = /* @__PURE__ */ new Map();
@@ -5635,7 +5754,10 @@ function compileLinkable(ruleMapArg, ns) {
5635
5754
  ctx.indent = 1;
5636
5755
  ctx.failLabel = "_pfail";
5637
5756
  ctx.recordFail = true;
5757
+ const savedTrivia = ctx.activeTrivia;
5758
+ if (rule._meta.isTrivia) ctx.activeTrivia = void 0;
5638
5759
  const body = emit(resolved, ctx, "_pos");
5760
+ ctx.activeTrivia = savedTrivia;
5639
5761
  ctx.indent = savedIndent;
5640
5762
  ctx.failLabel = savedFail;
5641
5763
  ctx.recordFail = savedRec;
@@ -6129,6 +6251,23 @@ ${body}
6129
6251
  };
6130
6252
 
6131
6253
  // src/compiler/linker.ts
6254
+ function pickPieces(pieces, names) {
6255
+ const winner = /* @__PURE__ */ new Map();
6256
+ for (const pc of pieces) for (const k of pc.keys) winner.set(k, pc);
6257
+ for (const n of names) {
6258
+ if (!winner.has(n)) throw new Error(`pick: rule "${n}" is not in this grammar (available: ${[...winner.keys()].join(", ")})`);
6259
+ }
6260
+ const keep = /* @__PURE__ */ new Set();
6261
+ const visit = (n) => {
6262
+ const w = winner.get(n);
6263
+ if (keep.has(n) || !w) return;
6264
+ keep.add(n);
6265
+ for (const d of w.deps.get(n) ?? []) visit(d);
6266
+ };
6267
+ for (const n of names) visit(n);
6268
+ const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
6269
+ 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);
6270
+ }
6132
6271
  function fusedBody(pieces) {
6133
6272
  const winner = /* @__PURE__ */ new Map();
6134
6273
  for (const p of pieces) for (const k of p.keys) winner.set(k, p);
@@ -6148,7 +6287,7 @@ function fusedBody(pieces) {
6148
6287
  ...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
6149
6288
  ...needsHostReads ? [HOST_READS_DECL] : [],
6150
6289
  // Each contributing artifact's namespaced private prelude (regexes, _pf, …).
6151
- ...[...new Set(contributingPieces.flatMap((p) => p.prelude))],
6290
+ ...new Set(contributingPieces.flatMap((p) => p.prelude)),
6152
6291
  // The winning `_r_<Name>` function for each rule (one per name → no redeclare).
6153
6292
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
6154
6293
  ];
@@ -6189,9 +6328,9 @@ ${body}
6189
6328
  function isIRPiece(p) {
6190
6329
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
6191
6330
  }
6192
- function materializePiece(p) {
6331
+ function materializePiece(p, trivia2) {
6193
6332
  if (!isIRPiece(p)) return p;
6194
- const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
6333
+ const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, trivia2 ? { trivia: trivia2 } : void 0);
6195
6334
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
6196
6335
  return pieces;
6197
6336
  }
@@ -6897,11 +7036,13 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6897
7036
  warn(init.start, `${label2}: rule map couldn't be inlined`);
6898
7037
  return null;
6899
7038
  }
6900
- return { replacement: compiled.replacement, ruleMap };
7039
+ return { replacement: compiled.replacement, ruleMap, ...gTrivia ? { trivia: gTrivia } : {} };
6901
7040
  };
6902
7041
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
6903
7042
  const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
7043
+ const isPickCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "pick";
6904
7044
  const localRuleMaps = /* @__PURE__ */ new Map();
7045
+ const localGrammarTrivia = /* @__PURE__ */ new Map();
6905
7046
  const nsFor = (label2) => `_${(0, import_node_crypto.createHash)("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
6906
7047
  const stripIndent = (s) => s.replace(/\n[ \t]+/g, "\n");
6907
7048
  const serializePieces = (p) => {
@@ -6915,7 +7056,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6915
7056
  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);
6916
7057
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
6917
7058
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
6918
- const localComposedPieces = /* @__PURE__ */ new Map();
7059
+ const localComposedCarried = /* @__PURE__ */ new Map();
7060
+ const localComposedTrivia = /* @__PURE__ */ new Map();
6919
7061
  const importedPiecesCache = /* @__PURE__ */ new Map();
6920
7062
  const resolveModulePieces = (file, exportName, seen) => {
6921
7063
  const mod = parseModuleCached(file);
@@ -6949,7 +7091,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6949
7091
  }
6950
7092
  try {
6951
7093
  const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
6952
- return list.map(materializePiece);
7094
+ return list;
6953
7095
  } catch {
6954
7096
  return null;
6955
7097
  }
@@ -6969,32 +7111,108 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6969
7111
  importedPiecesCache.set(localName, result2);
6970
7112
  return result2;
6971
7113
  };
6972
- const localArg = (rm, label2) => {
7114
+ const materializeCarried = (items, composing) => {
7115
+ const out = [];
7116
+ for (const it of items) {
7117
+ if (isSpread(it)) {
7118
+ for (const p of importedPieces(it.__spreadLocal) ?? []) out.push(materializePiece(p, composing));
7119
+ } else if (isIR(it)) {
7120
+ out.push(materializePiece(it, composing));
7121
+ } else {
7122
+ out.push(it);
7123
+ }
7124
+ }
7125
+ return out;
7126
+ };
7127
+ const localCarried = (rm, label2, composing) => {
6973
7128
  const entries = [...rm];
6974
7129
  const ns = nsFor(label2);
6975
- const p = compileLinkable(entries, ns);
6976
- if (!p) return null;
6977
7130
  const ir = serializeRuleMap(entries);
6978
- return ir ? { pieces: [p], ir: { ns, ir } } : { pieces: [p] };
7131
+ if (ir) return { carried: [{ ns, ir }] };
7132
+ const p = compileLinkable(entries, ns, composing ? { trivia: composing } : void 0);
7133
+ return p ? { carried: [p] } : null;
6979
7134
  };
6980
- const argPieces = (arg, label2) => {
7135
+ const argPieces = (arg, label2, composing) => {
7136
+ if (isPickCall(arg)) {
7137
+ const pargs = arg.arguments;
7138
+ const inner = pargs[0];
7139
+ const namesArg = pargs[1];
7140
+ if (!inner || namesArg?.type !== "ArrayExpression") return null;
7141
+ const names = [];
7142
+ for (const el of namesArg.elements ?? []) {
7143
+ const v2 = el?.value;
7144
+ if ((el?.type === "Literal" || el?.type === "StringLiteral") && typeof v2 === "string") names.push(v2);
7145
+ else return null;
7146
+ }
7147
+ const innerArg = argPieces(inner, `${label2}_pick`);
7148
+ if (!innerArg) return null;
7149
+ try {
7150
+ return { carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names) };
7151
+ } catch (e) {
7152
+ warn(arg.start, `pick(): ${e.message}`);
7153
+ return null;
7154
+ }
7155
+ }
6981
7156
  if (isRulesCall(arg)) {
6982
- const factory = arg.arguments[0];
7157
+ const rulesArgs = arg.arguments;
7158
+ const a0 = rulesArgs[0];
7159
+ const a1 = rulesArgs[1];
7160
+ const optionsFirst = a0?.type === "ObjectExpression";
7161
+ const factory = optionsFirst ? a1 : a0;
6983
7162
  const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
6984
- return rm ? localArg(rm, label2) : null;
7163
+ return rm ? localCarried(rm, label2, composing) : null;
6985
7164
  }
6986
7165
  if (arg.type === "Identifier") {
6987
7166
  const name = arg.name;
6988
7167
  const rm = localRuleMaps.get(name);
6989
- if (rm) return localArg(rm, label2);
6990
- const composed = localComposedPieces.get(name);
6991
- if (composed) return { pieces: composed };
7168
+ if (rm) return localCarried(rm, label2, composing);
7169
+ const composed = localComposedCarried.get(name);
7170
+ if (composed) return { carried: composed };
6992
7171
  const pieces = importedPieces(name);
6993
7172
  if (!pieces) return null;
6994
- return importBindings.has(name) ? { pieces, spreadLocal: name } : { pieces };
7173
+ return { carried: importBindings.has(name) ? [{ __spreadLocal: name }] : [...pieces] };
6995
7174
  }
6996
7175
  return null;
6997
7176
  };
7177
+ const rulesCallTrivia = (el) => {
7178
+ const rulesArgs = el.arguments;
7179
+ const a0 = rulesArgs[0];
7180
+ const a1 = rulesArgs[1];
7181
+ const optExpr = a0?.type === "ObjectExpression" ? a0 : a1?.type === "ObjectExpression" ? a1 : void 0;
7182
+ const triviaProp = (optExpr?.properties ?? []).find(
7183
+ (p) => p.key?.name === "trivia"
7184
+ );
7185
+ if (!triviaProp?.value) return void 0;
7186
+ return evaluateExpr(triviaProp.value, scope, code, []) ?? void 0;
7187
+ };
7188
+ const composingTrivia = (elements) => {
7189
+ for (let i = elements.length - 1; i >= 0; i--) {
7190
+ const el = elements[i];
7191
+ if (!el) continue;
7192
+ if (isRulesCall(el)) {
7193
+ const t = rulesCallTrivia(el);
7194
+ if (t) return t;
7195
+ continue;
7196
+ }
7197
+ if (el.type === "Identifier") {
7198
+ const t = localGrammarTrivia.get(el.name);
7199
+ if (t) return t;
7200
+ }
7201
+ }
7202
+ return void 0;
7203
+ };
7204
+ const ownTrivia = (arg) => {
7205
+ if (isRulesCall(arg)) return rulesCallTrivia(arg);
7206
+ if (isPickCall(arg)) {
7207
+ const inner = arg.arguments[0];
7208
+ return inner ? ownTrivia(inner) : void 0;
7209
+ }
7210
+ if (arg.type === "Identifier") {
7211
+ const name = arg.name;
7212
+ return localGrammarTrivia.get(name) ?? localComposedTrivia.get(name);
7213
+ }
7214
+ return void 0;
7215
+ };
6998
7216
  const compileComposeCall = (init) => {
6999
7217
  const args = init.arguments;
7000
7218
  const arr = args[0];
@@ -7003,21 +7221,19 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7003
7221
  return null;
7004
7222
  }
7005
7223
  const elements = arr.elements;
7006
- const pieces = [];
7224
+ const composing = composingTrivia(elements);
7007
7225
  const carried = [];
7008
7226
  for (let i = 0; i < elements.length; i++) {
7009
- const r = argPieces(elements[i], `compose${init.start}_${i}`);
7227
+ const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
7010
7228
  if (!r) {
7011
7229
  warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
7012
7230
  return null;
7013
7231
  }
7014
- pieces.push(...r.pieces);
7015
- if (r.spreadLocal) carried.push({ __spreadLocal: r.spreadLocal });
7016
- else if (r.ir) carried.push(r.ir);
7017
- else carried.push(...r.pieces);
7232
+ carried.push(...r.carried);
7018
7233
  }
7234
+ const pieces = materializeCarried(carried, composing);
7019
7235
  try {
7020
- return { replacement: emitFusedSource(pieces), pieces, carried };
7236
+ return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
7021
7237
  } catch (e) {
7022
7238
  warn(init.start, `compose(): ${e.message}; falling back to runtime`);
7023
7239
  return null;
@@ -7102,6 +7318,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7102
7318
  const compiledRules = compileRulesFactory(init, varName);
7103
7319
  if (!compiledRules) continue;
7104
7320
  localRuleMaps.set(varName, compiledRules.ruleMap);
7321
+ if (compiledRules.trivia) localGrammarTrivia.set(varName, compiledRules.trivia);
7105
7322
  let replacement = compiledRules.replacement;
7106
7323
  if (exportPrefix) {
7107
7324
  const ns = nsFor(varName);
@@ -7117,7 +7334,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7117
7334
  if (isComposeCall(init)) {
7118
7335
  const fused = compileComposeCall(init);
7119
7336
  if (!fused) continue;
7120
- localComposedPieces.set(varName, fused.pieces);
7337
+ localComposedCarried.set(varName, fused.carried);
7338
+ if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
7121
7339
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
7122
7340
  replacements.push({ start: init.start, end: init.end, replacement });
7123
7341
  continue;