parseman 0.28.0 → 0.29.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.
@@ -4151,6 +4151,61 @@ function tryInlineDestructureTransform(src, valueVars) {
4151
4151
  }
4152
4152
 
4153
4153
  // src/compiler/codegen.ts
4154
+ function leadingFirstSetRecipe(p, seen = /* @__PURE__ */ new Set()) {
4155
+ if (seen.has(p)) return { concrete: empty(), refs: [] };
4156
+ seen.add(p);
4157
+ const d = p._def;
4158
+ const rec = (c) => leadingFirstSetRecipe(c, seen);
4159
+ const merge = (a, b) => ({ concrete: union(a.concrete, b.concrete), refs: [...a.refs, ...b.refs] });
4160
+ switch (d.tag) {
4161
+ case "lazy": {
4162
+ const name = p._ruleName;
4163
+ if (name !== void 0) return { concrete: empty(), refs: [name] };
4164
+ try {
4165
+ return rec(d.thunk());
4166
+ } catch {
4167
+ return { concrete: any(), refs: [] };
4168
+ }
4169
+ }
4170
+ case "literal":
4171
+ case "regex":
4172
+ case "keywords":
4173
+ return { concrete: p._meta.firstSet, refs: [] };
4174
+ case "choice": {
4175
+ let out = { concrete: empty(), refs: [] };
4176
+ for (const arm of d.parsers) out = merge(out, rec(arm));
4177
+ return out;
4178
+ }
4179
+ case "sequence": {
4180
+ let out = { concrete: empty(), refs: [] };
4181
+ for (const term of d.parsers) {
4182
+ out = merge(out, rec(term));
4183
+ if (!matchesEmpty(term)) return out;
4184
+ }
4185
+ return out;
4186
+ }
4187
+ case "oneOrMore":
4188
+ case "many":
4189
+ case "optional":
4190
+ case "transform":
4191
+ case "label":
4192
+ case "field":
4193
+ case "trivia":
4194
+ case "token":
4195
+ case "leaf":
4196
+ case "node":
4197
+ case "grammar":
4198
+ case "expect":
4199
+ return rec(d.parser);
4200
+ case "sepBy":
4201
+ return rec(d.parser);
4202
+ case "skip":
4203
+ return rec(d.main);
4204
+ // not / scanTo / guard / withCtx / recover / unknown → shallow set (safe).
4205
+ default:
4206
+ return { concrete: p._meta.firstSet, refs: [] };
4207
+ }
4208
+ }
4154
4209
  var HOST_READS_DECL = "const _hostReads = (b, n) => { if (b === undefined) return false; let s; try { s = Function.prototype.toString.call(b) } catch (e) { return true } if (/\\barguments\\b/.test(s)) return true; const m = /^[^(]*\\(([\\s\\S]*?)\\)/.exec(s); if (m && /\\.\\.\\.|=/.test(m[1])) return true; return b.length > n }";
4155
4210
  var _loweringSink = null;
4156
4211
  function beginLoweringCapture() {
@@ -5205,6 +5260,13 @@ function emitMany(def, ctx, pos) {
5205
5260
  itemPos = npV;
5206
5261
  }
5207
5262
  }
5263
+ if (!ctx.recovery && !ctx.coverage && !failsAtStart(def.parser) && needsFirstSetGuard(def.parser)) {
5264
+ const lcV = v(ctx, "_lc");
5265
+ stmts.push(
5266
+ `${ind(ctx)}const ${lcV} = ${itemPos} < input.length ? (input.codePointAt(${itemPos}) ?? -1) : -1`,
5267
+ `${ind(ctx)}if (!(${firstSetCond(lcV, def.parser._meta.firstSet)})) { ${rollback}break }`
5268
+ );
5269
+ }
5208
5270
  const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos, true);
5209
5271
  stmts.push(...iterStmts);
5210
5272
  if (ctx.recovery) {
@@ -5255,8 +5317,19 @@ function emitAttempt(p, def, ctx, pos) {
5255
5317
  const rollback = `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${leaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${raw}; if (_ctx._cstTriviaLog) _ctx._cstTriviaLog.length = ${trivia2}; if (_ctx._triviaLog) _ctx._triviaLog.length = ${log}; if (_ctx._fields) _ctx._fields.length = ${fields}; if (_ctx._errors) _ctx._errors.length = ${errors}`;
5256
5318
  const traceId = ctx.coverage?.plan.attempts.get(p);
5257
5319
  const traceRollback = traceId === void 0 ? "" : ` _ctx._grammarTrace?.write({ id: ${JSON.stringify(traceId)}, phase: 'rollback', offset: ${pos} });`;
5320
+ const preGuard = [];
5321
+ if (!ctx.recovery && !ctx.coverage && needsFirstSetGuard(def.parser)) {
5322
+ const gcV = v(ctx, "_agc");
5323
+ const gExp = armStaticExpected(ctx, def.parser);
5324
+ const gFail = ctx.failLabel ? `{ _ctx._fe = ${pos};${ctx.recordFail ? ` _ctx._fx = ${gExp};` : ""} break ${ctx.failLabel} }` : `{ _ctx._fe = ${pos}; ${failReturnArr(gExp, pos)} }`;
5325
+ preGuard.push(
5326
+ `${ind(ctx)}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
5327
+ `${ind(ctx)}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
5328
+ );
5329
+ }
5258
5330
  return {
5259
5331
  stmts: [
5332
+ ...preGuard,
5260
5333
  `${ind(ctx)}const ${leaves} = _ctx._cstLeaves?.length ?? 0, ${raw} = _ctx._cstRawChildren?.length ?? 0, ${trivia2} = _ctx._cstTriviaLog?.length ?? 0, ${log} = _ctx._triviaLog?.length ?? 0, ${fields} = _ctx._fields?.length ?? 0, ${errors} = _ctx._errors?.length ?? 0`,
5261
5334
  ...inner.stmts,
5262
5335
  ...emitIfFail(ctx, `!${inner.okVar}`, `{ ${rollback};${traceRollback} _ctx._fe = ${pos}; ${propagateFailBody(ctx)} }`)
@@ -5636,7 +5709,18 @@ ${i}const ${chV} = ${profileRecognizer} ? undefined : [], ${rawV} = ${profileRec
5636
5709
  const saveTrivia = needsTriviaFrame ? `, ${st} = _ctx.captureTrivia, ${stl} = _ctx._cstTriviaLog${smk ? `, ${smk} = _ctx._triviaCaptureMask` : ""}` : "";
5637
5710
  const installTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${capTLv ?? "false"}; _ctx._cstTriviaLog = ${innerTl}${smk ? `; _ctx._triviaCaptureMask = ${capTLv} && _ctx.build !== undefined && _ctx.build._parsemanTriviaKinds !== undefined ? _ctx.build._parsemanTriviaKinds(${JSON.stringify(def.type)}) : ${smk}` : ""}` : "";
5638
5711
  const restoreTrivia = needsTriviaFrame ? `; _ctx.captureTrivia = ${st}; _ctx._cstTriviaLog = ${stl}${smk ? `; _ctx._triviaCaptureMask = ${smk}` : ""}` : "";
5712
+ const preGuard = [];
5713
+ if (!ctx.recovery && !ctx.coverage && (capturesChildren || structural) && needsFirstSetGuard(def.parser)) {
5714
+ const gcV = v(ctx, "_ngc");
5715
+ const gExp = armStaticExpected(ctx, def.parser);
5716
+ const gFail = ctx.failLabel ? ctx.recordFail ? `{ _ctx._fe = ${pos}; _ctx._fx = ${gExp}; break ${ctx.failLabel} }` : `break ${ctx.failLabel}` : failReturnArr(gExp, pos);
5717
+ preGuard.push(
5718
+ `${i}const ${gcV} = ${pos} < input.length ? (input.codePointAt(${pos}) ?? -1) : -1`,
5719
+ `${i}if (!(${firstSetCond(gcV, def.parser._meta.firstSet)})) ${gFail}`
5720
+ );
5721
+ }
5639
5722
  const stmts = [
5723
+ ...preGuard,
5640
5724
  profHoist,
5641
5725
  allocStmt,
5642
5726
  `${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren${saveTrivia}${sf ? `, ${sf} = _ctx._fields` : ""}`,
@@ -6663,6 +6747,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
6663
6747
  ].join("\n")));
6664
6748
  }
6665
6749
  const firstSets = /* @__PURE__ */ new Map();
6750
+ const firstSetRecipes = /* @__PURE__ */ new Map();
6666
6751
  for (const [key, rule] of ruleMap) {
6667
6752
  let resolved = rule;
6668
6753
  const d = rule._def;
@@ -6674,6 +6759,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
6674
6759
  }
6675
6760
  }
6676
6761
  firstSets.set(key, canMatchEmptyAtStart(resolved) ? { kind: "any" } : resolved._meta.firstSet);
6762
+ firstSetRecipes.set(key, canMatchEmptyAtStart(resolved) ? { concrete: any(), refs: [] } : leadingFirstSetRecipe(resolved));
6677
6763
  }
6678
6764
  return {
6679
6765
  ns,
@@ -6682,6 +6768,7 @@ function compileLinkable(ruleMapArg, ns, opts) {
6682
6768
  ruleFns,
6683
6769
  wrappers,
6684
6770
  firstSets,
6771
+ firstSetRecipes,
6685
6772
  deps: ruleDependencies(ruleMap),
6686
6773
  needsEmptyTl: !!ctx.needsEmptyTl,
6687
6774
  needsHostReads: !!ctx.needsHostReads,
@@ -7328,10 +7415,30 @@ function fusedBody(pieces) {
7328
7415
  ];
7329
7416
  const wrapperEntries = [...winner].map(([k, p]) => `${JSON.stringify(k)}: ${p.wrappers.get(k)}`);
7330
7417
  const rawBody = [...lines, "return {", wrapperEntries.join(",\n"), "}"].join("\n");
7331
- const finalFS = /* @__PURE__ */ new Map();
7418
+ const ANY2 = { kind: "any" };
7419
+ const recipes = /* @__PURE__ */ new Map();
7420
+ const shallow = /* @__PURE__ */ new Map();
7332
7421
  for (const [k, p] of winner) {
7422
+ const r = p.firstSetRecipes?.get(k);
7423
+ if (r) recipes.set(k, r);
7333
7424
  const fs2 = p.firstSets?.get(k);
7334
- if (fs2) finalFS.set(k, fs2);
7425
+ if (fs2) shallow.set(k, fs2);
7426
+ }
7427
+ const finalFS = /* @__PURE__ */ new Map();
7428
+ for (const [k] of winner) finalFS.set(k, recipes.get(k)?.concrete ?? shallow.get(k) ?? ANY2);
7429
+ for (let iter = 0; iter <= recipes.size; iter++) {
7430
+ let changed = false;
7431
+ for (const [k, r] of recipes) {
7432
+ let fs2 = r.concrete;
7433
+ for (const ref2 of r.refs) {
7434
+ fs2 = union(fs2, recipes.has(ref2) || shallow.has(ref2) ? finalFS.get(ref2) ?? ANY2 : ANY2);
7435
+ }
7436
+ if (JSON.stringify(finalFS.get(k)) !== JSON.stringify(fs2)) {
7437
+ finalFS.set(k, fs2);
7438
+ changed = true;
7439
+ }
7440
+ }
7441
+ if (!changed) break;
7335
7442
  }
7336
7443
  const body = rawBody.replace(
7337
7444
  // Rule name + code-point var are both JS identifiers (rule names are validated
@@ -8502,7 +8609,7 @@ function transformMacro(code, id, moduleAliases = /* @__PURE__ */ new Set([PARSE
8502
8609
  const mapLit = (m, stripVals = false) => `new Map([${[...m].map(
8503
8610
  ([k, v2]) => `[${JSON.stringify(k)}, ${JSON.stringify(stripVals && typeof v2 === "string" ? stripIndent(v2) : v2)}]`
8504
8611
  ).join(", ")}])`;
8505
- return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
8612
+ return `{ ns: ${JSON.stringify(p.ns)}, keys: ${JSON.stringify(p.keys)}, prelude: ${JSON.stringify(p.prelude.map(stripIndent))}, ruleFns: ${mapLit(p.ruleFns, true)}, wrappers: ${mapLit(p.wrappers, true)}, firstSets: ${mapLit(p.firstSets)}, firstSetRecipes: ${p.firstSetRecipes ? mapLit(p.firstSetRecipes) : "new Map()"}, deps: ${mapLit(p.deps)}, needsEmptyTl: ${p.needsEmptyTl}, needsHostReads: ${p.needsHostReads}, hasDirectBuilders: ${p.hasDirectBuilders === true}, isRecognitionOnly: ${p.isRecognitionOnly === true}, mfFns: [], buildFns: [] }`;
8506
8613
  };
8507
8614
  const isSpread = (it) => "__spreadLocal" in it;
8508
8615
  const isIR = (it) => "ir" in it && !("ruleFns" in it);