parseman 0.21.0 → 0.23.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.
@@ -1729,7 +1729,9 @@ function tagRule(r, key) {
1729
1729
  r._ruleName = key;
1730
1730
  if (r._def.tag === "node" && r._def.type === void 0) r._def.type = key;
1731
1731
  }
1732
- function rules(factory) {
1732
+ function rules(a, b) {
1733
+ const factory = typeof a === "function" ? a : b;
1734
+ const options = typeof a === "function" ? b : a;
1733
1735
  const cache = {};
1734
1736
  const proxy = new Proxy(cache, {
1735
1737
  get(target, key) {
@@ -1757,6 +1759,12 @@ function rules(factory) {
1757
1759
  cache[key] = parser2;
1758
1760
  }
1759
1761
  }
1762
+ if (options?.trivia !== void 0) {
1763
+ for (const key of Object.keys(definitions)) {
1764
+ const rule = cache[key];
1765
+ if (rule && !rule._meta.isTrivia) rule._meta.grammarTrivia = options.trivia;
1766
+ }
1767
+ }
1760
1768
  for (const key of Object.keys(definitions)) {
1761
1769
  markUnusedValues(cache[key]);
1762
1770
  }
@@ -5351,6 +5359,7 @@ function ruleDependencies(ruleMap) {
5351
5359
  }
5352
5360
  function compile(combinator, mapFnSources) {
5353
5361
  markUnusedValues(combinator);
5362
+ const grammarTrivia = combinator._meta.grammarTrivia;
5354
5363
  const ctx = {
5355
5364
  vars: 0,
5356
5365
  indent: 1,
@@ -5369,7 +5378,8 @@ function compile(combinator, mapFnSources) {
5369
5378
  triviaFnNames: /* @__PURE__ */ new Map(),
5370
5379
  namedFnDecls: [],
5371
5380
  capturing: hasNodeDef(combinator),
5372
- lazyUsage: analyzeLazyUsage(combinator)
5381
+ lazyUsage: analyzeLazyUsage(combinator),
5382
+ ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5373
5383
  };
5374
5384
  const r = emit(combinator, ctx, "_pos");
5375
5385
  const namedPrelude = ctx.namedFnDecls.length > 0 ? [...namedFnPrelude(), ""] : [];
@@ -5445,8 +5455,9 @@ function publicRuleWrapperSource(rule, fnSource) {
5445
5455
  if (labels === void 0) return fnSource;
5446
5456
  return `Object.assign(${fnSource}, { _meta: { triviaKindLabels: ${JSON.stringify(labels)} } })`;
5447
5457
  }
5448
- function compileRuleMap(ruleMap) {
5458
+ function compileRuleMap(ruleMap, opts) {
5449
5459
  for (const [, rule] of ruleMap) markUnusedValues(rule);
5460
+ const grammarTrivia = opts?.trivia;
5450
5461
  const ctx = {
5451
5462
  vars: 0,
5452
5463
  indent: 1,
@@ -5465,7 +5476,8 @@ function compileRuleMap(ruleMap) {
5465
5476
  triviaFnNames: /* @__PURE__ */ new Map(),
5466
5477
  namedFnDecls: [],
5467
5478
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
5468
- lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule))
5479
+ lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5480
+ ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5469
5481
  };
5470
5482
  const seenNames = /* @__PURE__ */ new Map();
5471
5483
  const ruleNames = /* @__PURE__ */ new Map();
@@ -5477,7 +5489,13 @@ function compileRuleMap(ruleMap) {
5477
5489
  ruleNames.set(rule, n === 0 ? base : `${base}$${n}`);
5478
5490
  }
5479
5491
  ctx.ruleNames = ruleNames;
5480
- const perEntry = ruleMap.map(([key, rule]) => ({ key, rule, r: emit(rule, ctx, "_pos") }));
5492
+ const perEntry = ruleMap.map(([key, rule]) => {
5493
+ const savedTrivia = ctx.activeTrivia;
5494
+ if (rule._meta.isTrivia) ctx.activeTrivia = void 0;
5495
+ const r = emit(rule, ctx, "_pos");
5496
+ ctx.activeTrivia = savedTrivia;
5497
+ return { key, rule, r };
5498
+ });
5481
5499
  const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
5482
5500
  const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
5483
5501
  const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
@@ -5521,9 +5539,10 @@ function compileRuleMap(ruleMap) {
5521
5539
  ].join("\n");
5522
5540
  return { keys: perEntry.map((e) => e.key), replacement };
5523
5541
  }
5524
- function compileLinkable(ruleMapArg, ns) {
5542
+ function compileLinkable(ruleMapArg, ns, opts) {
5525
5543
  if (!ns) throw new Error("compileLinkable: ns must be a non-empty namespace");
5526
5544
  for (const [, rule] of ruleMapArg) markUnusedValues(rule);
5545
+ const grammarTrivia = opts?.trivia ?? ruleMapArg.map(([, r]) => r._meta.grammarTrivia).find(Boolean);
5527
5546
  const ruleMap = ruleMapArg.filter(([, val]) => {
5528
5547
  const d = val._def;
5529
5548
  if (d.tag !== "lazy") return true;
@@ -5554,7 +5573,8 @@ function compileLinkable(ruleMapArg, ns) {
5554
5573
  capturing: ruleMap.some(([, rule]) => hasNodeDef(rule)),
5555
5574
  lazyUsage: analyzeLazyUsageMulti(ruleMap.map(([, rule]) => rule)),
5556
5575
  ns,
5557
- deferFirstSetRefs: true
5576
+ deferFirstSetRefs: true,
5577
+ ...grammarTrivia ? { activeTrivia: grammarTrivia, triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {}
5558
5578
  };
5559
5579
  const seen = /* @__PURE__ */ new Map();
5560
5580
  const ruleNames = /* @__PURE__ */ new Map();
@@ -5617,7 +5637,10 @@ function compileLinkable(ruleMapArg, ns) {
5617
5637
  ctx.indent = 1;
5618
5638
  ctx.failLabel = "_pfail";
5619
5639
  ctx.recordFail = true;
5640
+ const savedTrivia = ctx.activeTrivia;
5641
+ if (rule._meta.isTrivia) ctx.activeTrivia = void 0;
5620
5642
  const body = emit(resolved, ctx, "_pos");
5643
+ ctx.activeTrivia = savedTrivia;
5621
5644
  ctx.indent = savedIndent;
5622
5645
  ctx.failLabel = savedFail;
5623
5646
  ctx.recordFail = savedRec;
@@ -6111,6 +6134,23 @@ ${body}
6111
6134
  };
6112
6135
 
6113
6136
  // src/compiler/linker.ts
6137
+ function pickPieces(pieces, names) {
6138
+ const winner = /* @__PURE__ */ new Map();
6139
+ for (const pc of pieces) for (const k of pc.keys) winner.set(k, pc);
6140
+ for (const n of names) {
6141
+ if (!winner.has(n)) throw new Error(`pick: rule "${n}" is not in this grammar (available: ${[...winner.keys()].join(", ")})`);
6142
+ }
6143
+ const keep = /* @__PURE__ */ new Set();
6144
+ const visit = (n) => {
6145
+ const w = winner.get(n);
6146
+ if (keep.has(n) || !w) return;
6147
+ keep.add(n);
6148
+ for (const d of w.deps.get(n) ?? []) visit(d);
6149
+ };
6150
+ for (const n of names) visit(n);
6151
+ const filt = (m, pc) => new Map([...m].filter(([k]) => keep.has(k) && winner.get(k) === pc));
6152
+ 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);
6153
+ }
6114
6154
  function fusedBody(pieces) {
6115
6155
  const winner = /* @__PURE__ */ new Map();
6116
6156
  for (const p of pieces) for (const k of p.keys) winner.set(k, p);
@@ -6130,7 +6170,7 @@ function fusedBody(pieces) {
6130
6170
  ...needsEmptyTl ? ["const _EMPTY_TL = Object.freeze([])"] : [],
6131
6171
  ...needsHostReads ? [HOST_READS_DECL] : [],
6132
6172
  // Each contributing artifact's namespaced private prelude (regexes, _pf, …).
6133
- ...[...new Set(contributingPieces.flatMap((p) => p.prelude))],
6173
+ ...new Set(contributingPieces.flatMap((p) => p.prelude)),
6134
6174
  // The winning `_r_<Name>` function for each rule (one per name → no redeclare).
6135
6175
  ...[...winner].map(([k, p]) => p.ruleFns.get(k))
6136
6176
  ];
@@ -6171,9 +6211,9 @@ ${body}
6171
6211
  function isIRPiece(p) {
6172
6212
  return !!p && typeof p === "object" && typeof p.ir === "string" && typeof p.ns === "string" && !("ruleFns" in p);
6173
6213
  }
6174
- function materializePiece(p) {
6214
+ function materializePiece(p, trivia2) {
6175
6215
  if (!isIRPiece(p)) return p;
6176
- const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns);
6216
+ const pieces = compileLinkable(evalRuleMapIR(p.ir), p.ns, trivia2 ? { trivia: trivia2 } : void 0);
6177
6217
  if (!pieces) throw new Error(`compose: carried IR for ns "${p.ns}" could not be re-lowered`);
6178
6218
  return pieces;
6179
6219
  }
@@ -6856,7 +6896,10 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6856
6896
  };
6857
6897
  const compileRulesFactory = (init, label2) => {
6858
6898
  const args = init.arguments;
6859
- const factoryArg = args[0];
6899
+ const arg0 = args[0];
6900
+ const arg1 = args[1];
6901
+ const optionsFirst = arg0?.type === "ObjectExpression";
6902
+ const factoryArg = optionsFirst ? arg1 : arg0;
6860
6903
  if (!factoryArg) {
6861
6904
  warn(init.start, `${label2}: rules() needs a factory argument`);
6862
6905
  return null;
@@ -6866,16 +6909,23 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6866
6909
  warn(init.start, `${label2}: rules(...) factory isn't statically evaluable`);
6867
6910
  return null;
6868
6911
  }
6869
- const compiled = compileRuleMap([...ruleMap]);
6912
+ const optionsArg = optionsFirst ? arg0 : arg1;
6913
+ const triviaValue = optionsArg?.type === "ObjectExpression" ? (optionsArg.properties ?? []).find(
6914
+ (p) => p.key?.name === "trivia"
6915
+ )?.value : void 0;
6916
+ const gTrivia = triviaValue ? evaluateExpr(triviaValue, scope, code, []) : void 0;
6917
+ const compiled = compileRuleMap([...ruleMap], gTrivia ? { trivia: gTrivia } : void 0);
6870
6918
  if (!compiled) {
6871
6919
  warn(init.start, `${label2}: rule map couldn't be inlined`);
6872
6920
  return null;
6873
6921
  }
6874
- return { replacement: compiled.replacement, ruleMap };
6922
+ return { replacement: compiled.replacement, ruleMap, ...gTrivia ? { trivia: gTrivia } : {} };
6875
6923
  };
6876
6924
  const isRulesCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "rules";
6877
6925
  const isComposeCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "compose";
6926
+ const isPickCall = (init) => init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "pick";
6878
6927
  const localRuleMaps = /* @__PURE__ */ new Map();
6928
+ const localGrammarTrivia = /* @__PURE__ */ new Map();
6879
6929
  const nsFor = (label2) => `_${(0, import_node_crypto.createHash)("sha1").update(`${id}#${label2}`).digest("hex").slice(0, 8)}_`;
6880
6930
  const stripIndent = (s) => s.replace(/\n[ \t]+/g, "\n");
6881
6931
  const serializePieces = (p) => {
@@ -6889,7 +6939,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6889
6939
  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);
6890
6940
  const serializeList = (list) => `[${list.map(serializeItem).join(", ")}]`;
6891
6941
  const withCarriedPieces = (grammarExpr, list) => `/* @__PURE__ */ Object.defineProperty(${grammarExpr}, Symbol.for('parseman.composedPieces'), { value: ${serializeList(list)}, enumerable: false })`;
6892
- const localComposedPieces = /* @__PURE__ */ new Map();
6942
+ const localComposedCarried = /* @__PURE__ */ new Map();
6943
+ const localComposedTrivia = /* @__PURE__ */ new Map();
6893
6944
  const importedPiecesCache = /* @__PURE__ */ new Map();
6894
6945
  const resolveModulePieces = (file, exportName, seen) => {
6895
6946
  const mod = parseModuleCached(file);
@@ -6923,7 +6974,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6923
6974
  }
6924
6975
  try {
6925
6976
  const list = new Function(...stubNames, `return (${literal2})`)(...stubVals);
6926
- return list.map(materializePiece);
6977
+ return list;
6927
6978
  } catch {
6928
6979
  return null;
6929
6980
  }
@@ -6943,32 +6994,108 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6943
6994
  importedPiecesCache.set(localName, result2);
6944
6995
  return result2;
6945
6996
  };
6946
- const localArg = (rm, label2) => {
6997
+ const materializeCarried = (items, composing) => {
6998
+ const out = [];
6999
+ for (const it of items) {
7000
+ if (isSpread(it)) {
7001
+ for (const p of importedPieces(it.__spreadLocal) ?? []) out.push(materializePiece(p, composing));
7002
+ } else if (isIR(it)) {
7003
+ out.push(materializePiece(it, composing));
7004
+ } else {
7005
+ out.push(it);
7006
+ }
7007
+ }
7008
+ return out;
7009
+ };
7010
+ const localCarried = (rm, label2, composing) => {
6947
7011
  const entries = [...rm];
6948
7012
  const ns = nsFor(label2);
6949
- const p = compileLinkable(entries, ns);
6950
- if (!p) return null;
6951
7013
  const ir = serializeRuleMap(entries);
6952
- return ir ? { pieces: [p], ir: { ns, ir } } : { pieces: [p] };
7014
+ if (ir) return { carried: [{ ns, ir }] };
7015
+ const p = compileLinkable(entries, ns, composing ? { trivia: composing } : void 0);
7016
+ return p ? { carried: [p] } : null;
6953
7017
  };
6954
- const argPieces = (arg, label2) => {
7018
+ const argPieces = (arg, label2, composing) => {
7019
+ if (isPickCall(arg)) {
7020
+ const pargs = arg.arguments;
7021
+ const inner = pargs[0];
7022
+ const namesArg = pargs[1];
7023
+ if (!inner || namesArg?.type !== "ArrayExpression") return null;
7024
+ const names = [];
7025
+ for (const el of namesArg.elements ?? []) {
7026
+ const v2 = el?.value;
7027
+ if ((el?.type === "Literal" || el?.type === "StringLiteral") && typeof v2 === "string") names.push(v2);
7028
+ else return null;
7029
+ }
7030
+ const innerArg = argPieces(inner, `${label2}_pick`);
7031
+ if (!innerArg) return null;
7032
+ try {
7033
+ return { carried: pickPieces(materializeCarried(innerArg.carried, ownTrivia(inner)), names) };
7034
+ } catch (e) {
7035
+ warn(arg.start, `pick(): ${e.message}`);
7036
+ return null;
7037
+ }
7038
+ }
6955
7039
  if (isRulesCall(arg)) {
6956
- const factory = arg.arguments[0];
7040
+ const rulesArgs = arg.arguments;
7041
+ const a0 = rulesArgs[0];
7042
+ const a1 = rulesArgs[1];
7043
+ const optionsFirst = a0?.type === "ObjectExpression";
7044
+ const factory = optionsFirst ? a1 : a0;
6957
7045
  const rm = factory ? evaluateParserFactory(factory, scope, code, []) : null;
6958
- return rm ? localArg(rm, label2) : null;
7046
+ return rm ? localCarried(rm, label2, composing) : null;
6959
7047
  }
6960
7048
  if (arg.type === "Identifier") {
6961
7049
  const name = arg.name;
6962
7050
  const rm = localRuleMaps.get(name);
6963
- if (rm) return localArg(rm, label2);
6964
- const composed = localComposedPieces.get(name);
6965
- if (composed) return { pieces: composed };
7051
+ if (rm) return localCarried(rm, label2, composing);
7052
+ const composed = localComposedCarried.get(name);
7053
+ if (composed) return { carried: composed };
6966
7054
  const pieces = importedPieces(name);
6967
7055
  if (!pieces) return null;
6968
- return importBindings.has(name) ? { pieces, spreadLocal: name } : { pieces };
7056
+ return { carried: importBindings.has(name) ? [{ __spreadLocal: name }] : [...pieces] };
6969
7057
  }
6970
7058
  return null;
6971
7059
  };
7060
+ const rulesCallTrivia = (el) => {
7061
+ const rulesArgs = el.arguments;
7062
+ const a0 = rulesArgs[0];
7063
+ const a1 = rulesArgs[1];
7064
+ const optExpr = a0?.type === "ObjectExpression" ? a0 : a1?.type === "ObjectExpression" ? a1 : void 0;
7065
+ const triviaProp = (optExpr?.properties ?? []).find(
7066
+ (p) => p.key?.name === "trivia"
7067
+ );
7068
+ if (!triviaProp?.value) return void 0;
7069
+ return evaluateExpr(triviaProp.value, scope, code, []) ?? void 0;
7070
+ };
7071
+ const composingTrivia = (elements) => {
7072
+ for (let i = elements.length - 1; i >= 0; i--) {
7073
+ const el = elements[i];
7074
+ if (!el) continue;
7075
+ if (isRulesCall(el)) {
7076
+ const t = rulesCallTrivia(el);
7077
+ if (t) return t;
7078
+ continue;
7079
+ }
7080
+ if (el.type === "Identifier") {
7081
+ const t = localGrammarTrivia.get(el.name);
7082
+ if (t) return t;
7083
+ }
7084
+ }
7085
+ return void 0;
7086
+ };
7087
+ const ownTrivia = (arg) => {
7088
+ if (isRulesCall(arg)) return rulesCallTrivia(arg);
7089
+ if (isPickCall(arg)) {
7090
+ const inner = arg.arguments[0];
7091
+ return inner ? ownTrivia(inner) : void 0;
7092
+ }
7093
+ if (arg.type === "Identifier") {
7094
+ const name = arg.name;
7095
+ return localGrammarTrivia.get(name) ?? localComposedTrivia.get(name);
7096
+ }
7097
+ return void 0;
7098
+ };
6972
7099
  const compileComposeCall = (init) => {
6973
7100
  const args = init.arguments;
6974
7101
  const arr = args[0];
@@ -6977,21 +7104,19 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
6977
7104
  return null;
6978
7105
  }
6979
7106
  const elements = arr.elements;
6980
- const pieces = [];
7107
+ const composing = composingTrivia(elements);
6981
7108
  const carried = [];
6982
7109
  for (let i = 0; i < elements.length; i++) {
6983
- const r = argPieces(elements[i], `compose${init.start}_${i}`);
7110
+ const r = argPieces(elements[i], `compose${init.start}_${i}`, composing);
6984
7111
  if (!r) {
6985
7112
  warn(init.start, `compose(): argument ${i} isn't a build-resolvable grammar; falling back to runtime`);
6986
7113
  return null;
6987
7114
  }
6988
- pieces.push(...r.pieces);
6989
- if (r.spreadLocal) carried.push({ __spreadLocal: r.spreadLocal });
6990
- else if (r.ir) carried.push(r.ir);
6991
- else carried.push(...r.pieces);
7115
+ carried.push(...r.carried);
6992
7116
  }
7117
+ const pieces = materializeCarried(carried, composing);
6993
7118
  try {
6994
- return { replacement: emitFusedSource(pieces), pieces, carried };
7119
+ return { replacement: emitFusedSource(pieces), carried, ...composing ? { trivia: composing } : {} };
6995
7120
  } catch (e) {
6996
7121
  warn(init.start, `compose(): ${e.message}; falling back to runtime`);
6997
7122
  return null;
@@ -7076,6 +7201,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7076
7201
  const compiledRules = compileRulesFactory(init, varName);
7077
7202
  if (!compiledRules) continue;
7078
7203
  localRuleMaps.set(varName, compiledRules.ruleMap);
7204
+ if (compiledRules.trivia) localGrammarTrivia.set(varName, compiledRules.trivia);
7079
7205
  let replacement = compiledRules.replacement;
7080
7206
  if (exportPrefix) {
7081
7207
  const ns = nsFor(varName);
@@ -7091,7 +7217,8 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
7091
7217
  if (isComposeCall(init)) {
7092
7218
  const fused = compileComposeCall(init);
7093
7219
  if (!fused) continue;
7094
- localComposedPieces.set(varName, fused.pieces);
7220
+ localComposedCarried.set(varName, fused.carried);
7221
+ if (fused.trivia) localComposedTrivia.set(varName, fused.trivia);
7095
7222
  const replacement = exportPrefix ? withCarriedPieces(fused.replacement, fused.carried) : fused.replacement;
7096
7223
  replacements.push({ start: init.start, end: init.end, replacement });
7097
7224
  continue;