parseman 0.50.3 → 0.50.5

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.
@@ -43,10 +43,20 @@ function classifyFinalChoice(arms, resolve3) {
43
43
  }
44
44
  return { exclusive, firstSets, nullable: nullable2 };
45
45
  }
46
- function resolveNamedRef(p, resolve3) {
46
+ function winnerForRef(p, resolve3) {
47
47
  if (resolve3 === void 0) return void 0;
48
48
  const name = p._ruleName;
49
- return name === void 0 ? void 0 : resolve3(name);
49
+ const w = name === void 0 ? void 0 : resolve3(name);
50
+ if (w === void 0 || w === p) return void 0;
51
+ let cur = w;
52
+ for (const seen = /* @__PURE__ */ new Set(); !seen.has(cur); ) {
53
+ if (cur === p) return void 0;
54
+ seen.add(cur);
55
+ const cd = cur._def;
56
+ if (cd.tag !== "grammar" && cd.tag !== "trivia") return w;
57
+ cur = cd.parser;
58
+ }
59
+ return w;
50
60
  }
51
61
  function union(a, b) {
52
62
  if (a.kind === "any" || b.kind === "any") return { kind: "any" };
@@ -144,13 +154,15 @@ function matchesEmptyBody(p, seen, resolve3) {
144
154
  case "grammar":
145
155
  case "recover":
146
156
  return me(d.parser);
147
- case "lazy":
157
+ case "lazy": {
158
+ const w = winnerForRef(p, resolve3);
159
+ if (w !== void 0) return me(w);
148
160
  try {
149
161
  return me(d.thunk());
150
162
  } catch {
151
- const t = resolveNamedRef(p, resolve3);
152
- return t ? me(t) : true;
163
+ return true;
153
164
  }
165
+ }
154
166
  default:
155
167
  return true;
156
168
  }
@@ -214,13 +226,15 @@ function firstSetBody(p, seen, resolve3) {
214
226
  // terminals: no refs, cached set is exact
215
227
  case "dispatch":
216
228
  return fs3(d.selector);
217
- case "lazy":
229
+ case "lazy": {
230
+ const w = winnerForRef(p, resolve3);
231
+ if (w !== void 0) return fs3(w);
218
232
  try {
219
233
  return fs3(d.thunk());
220
234
  } catch {
221
- const t = resolveNamedRef(p, resolve3);
222
- return t ? fs3(t) : any();
235
+ return any();
223
236
  }
237
+ }
224
238
  case "choice": {
225
239
  let out = empty();
226
240
  for (const arm of d.parsers) out = union(out, fs3(arm));
@@ -7665,7 +7679,7 @@ var Encoder = class {
7665
7679
  d.tags === void 0 ? -1 : this.constant(d.tags)
7666
7680
  );
7667
7681
  if (matchesEmpty(d.parser)) return body;
7668
- const fs3 = d.parser._meta.firstSet;
7682
+ const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver()) : d.parser._meta.firstSet;
7669
7683
  if (fs3.kind === "any") return body;
7670
7684
  const cls = this.charClass(fs3);
7671
7685
  if (cls < 0) return body;
@@ -7779,7 +7793,7 @@ var Encoder = class {
7779
7793
  case "attempt": {
7780
7794
  const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip);
7781
7795
  if (!this.failureNeedsRollback(d.parser, void 0)) this.failureRollbackCleanSites.add(inner);
7782
- const fs3 = d.parser._meta.firstSet;
7796
+ const fs3 = d.parser._def.tag === "lazy" ? firstSetOf(d.parser, /* @__PURE__ */ new Set(), this.refResolver()) : d.parser._meta.firstSet;
7783
7797
  if (fs3.kind === "any" || matchesEmpty(d.parser)) return inner;
7784
7798
  const cls = this.charClass(fs3);
7785
7799
  if (cls < 0) return inner;
@@ -16555,6 +16569,32 @@ var Serializer = class {
16555
16569
  body(c) {
16556
16570
  return c._def.tag === "lazy" && ruleNameOf3(c) !== void 0 ? lazyTarget(c) ?? c : c;
16557
16571
  }
16572
+ /**
16573
+ * The child a rule-entry grammar scope should serialize as its body, when that
16574
+ * scope merely WRAPS the rule's own named `lazy` proxy.
16575
+ *
16576
+ * `rules({ trackLines: true }, …)` wraps every rule VALUE — which is that rule's
16577
+ * own named lazy proxy — in a `grammarParser({ trackLines: true })` scope
16578
+ * (`parser.ts:229-242`). `body()` unwraps a BARE lazy entry to its defined target;
16579
+ * a lazy wrapped one level deep by that scope must be unwrapped INSIDE the scope
16580
+ * too. Left wrapped, the inner proxy serializes as a `g[name]` SELF-reference and
16581
+ * the rule's real body is dropped, so it re-lowers to `Name: parser(opts, g.Name)`
16582
+ * — a bodyless self-cycle the table encoder rejects with `alias cycle — lazy (rule
16583
+ * 'Name') encodes to its own recursion trampoline`. This is why the four
16584
+ * `*PositionsGrammar` exports fell back to a runtime `compose()` under trackLines.
16585
+ *
16586
+ * STRICT: only a scope that IS a rule entry (in `localRuleNames`) AND wraps ITS
16587
+ * OWN name unwraps. A scope wrapping another rule's ref (a genuine cross-rule
16588
+ * alias, `Name: parser(opts, g.Other)`) or a real body is untouched, so the
16589
+ * non-trackLines and non-self-wrap output stays byte-identical.
16590
+ */
16591
+ scopeSelfBody(c) {
16592
+ const name = this.localRuleNames.get(c);
16593
+ if (name === void 0 || c._def.tag !== "grammar") return void 0;
16594
+ const inner = c._def.parser;
16595
+ if (inner._def.tag !== "lazy" || ruleNameOf3(inner) !== name) return void 0;
16596
+ return lazyTarget(inner) ?? void 0;
16597
+ }
16558
16598
  run() {
16559
16599
  for (const [, c] of this.ruleMap) this.analyze(this.body(c), /* @__PURE__ */ new Set());
16560
16600
  for (const c of this.scanSkip) {
@@ -16624,7 +16664,9 @@ ${outerDecls.map((d) => " " + d).join("\n")}
16624
16664
  return;
16625
16665
  }
16626
16666
  active.add(c);
16627
- for (const child of childrenOf3(def)) this.analyze(child, active);
16667
+ const selfBody = def.tag === "grammar" ? this.scopeSelfBody(c) : void 0;
16668
+ const kids = selfBody === void 0 ? childrenOf3(def) : def.tag === "grammar" && def.triviaParser ? [selfBody, def.triviaParser] : [selfBody];
16669
+ for (const child of kids) this.analyze(child, active);
16628
16670
  active.delete(c);
16629
16671
  }
16630
16672
  /** Reference `c`: a const var if it has one (emitting its decl first), else inline.
@@ -16790,7 +16832,7 @@ ${outerDecls.map((d) => " " + d).join("\n")}
16790
16832
  const spec = classified && def.triviaParser ? analyzeLabeledTrivia(def.triviaParser) : null;
16791
16833
  if (classified && !spec) throw new Unserializable("classified trivia without labeled arms");
16792
16834
  const trivia2 = def.clearTrivia ? "null" : spec ? `classifiedTrivia({ ${spec.arms.map((arm) => `${JSON.stringify(arm.label)}: ${kid(arm.parser)}`).join(", ")} })` : def.triviaParser ? kid(def.triviaParser) : "undefined";
16793
- return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.rootCapture ? `, rootCapture: ${JSON.stringify(def.rootCapture)}` : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(def.parser)})`;
16835
+ return `parser({ trivia: ${trivia2}${def.captureTrivia ? ", captureTrivia: true" : ""}${def.rootCapture ? `, rootCapture: ${JSON.stringify(def.rootCapture)}` : ""}${def.trackLines ? ", trackLines: true" : ""} }, ${kid(this.scopeSelfBody(c) ?? def.parser)})`;
16794
16836
  }
16795
16837
  case "withCtx": {
16796
16838
  if (def.extraSrc === void 0) throw new Unserializable("withCtx without extraSrc");
@@ -16926,7 +16968,7 @@ function compileRuleMap(ruleMap, opts = {}) {
16926
16968
  }
16927
16969
 
16928
16970
  // src/version.ts
16929
- var PARSEMAN_VERSION = "0.50.3";
16971
+ var PARSEMAN_VERSION = "0.50.5";
16930
16972
 
16931
16973
  // src/compiler/compile-linkable-table.ts
16932
16974
  function isLocal(rule) {