parseman 0.55.0 → 0.55.2

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.
@@ -5624,19 +5624,22 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
5624
5624
  return false;
5625
5625
  }
5626
5626
  }
5627
- function mayCommitFailure(p, seen = /* @__PURE__ */ new Set(), resolve3) {
5628
- if (seen.has(p)) return false;
5629
- seen.add(p);
5627
+ function mayCommitFailure(p, resolve3, memo = /* @__PURE__ */ new Map()) {
5628
+ return reachesMarked(p, (node2) => commitFailureSuccessors(node2, resolve3), memo);
5629
+ }
5630
+ var NO_EDGES = [];
5631
+ function commitFailureSuccessors(p, resolve3) {
5630
5632
  const d = p._def;
5631
5633
  switch (d.tag) {
5632
5634
  case "dispatch":
5635
+ case "unknown":
5633
5636
  return true;
5634
5637
  case "choice":
5635
5638
  case "sequence":
5636
- return d.parsers.some((x) => mayCommitFailure(x, seen, resolve3));
5639
+ return d.parsers;
5637
5640
  // A contained transaction reports every failure uncommitted.
5638
5641
  case "attempt":
5639
- return d.contain !== true && mayCommitFailure(d.parser, seen, resolve3);
5642
+ return d.contain === true ? NO_EDGES : [d.parser];
5640
5643
  case "many":
5641
5644
  case "oneOrMore":
5642
5645
  case "optional":
@@ -5645,31 +5648,77 @@ function mayCommitFailure(p, seen = /* @__PURE__ */ new Set(), resolve3) {
5645
5648
  case "field":
5646
5649
  case "grammar":
5647
5650
  case "node":
5648
- return mayCommitFailure(d.parser, seen, resolve3);
5649
- case "sepBy":
5650
- return mayCommitFailure(d.parser, seen, resolve3) || mayCommitFailure(d.separator, seen, resolve3);
5651
5651
  case "token":
5652
5652
  case "leaf":
5653
- return mayCommitFailure(d.parser, seen, resolve3);
5654
5653
  case "withCtx":
5655
- return mayCommitFailure(d.parser, seen, resolve3);
5654
+ return [d.parser];
5655
+ case "sepBy":
5656
+ return [d.parser, d.separator];
5656
5657
  case "scanTo":
5657
- return mayCommitFailure(d.sentinel, seen, resolve3) || d.skip.some((x) => mayCommitFailure(x, seen, resolve3)) || d.recoverAt !== void 0 && mayCommitFailure(d.recoverAt, seen, resolve3) || d.stopAt !== void 0 && mayCommitFailure(d.stopAt, seen, resolve3);
5658
+ return [
5659
+ d.sentinel,
5660
+ ...d.skip,
5661
+ ...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
5662
+ ...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
5663
+ ];
5658
5664
  case "lazy": {
5659
5665
  try {
5660
- return mayCommitFailure(d.thunk(), seen, resolve3);
5666
+ return [d.thunk()];
5661
5667
  } catch {
5662
5668
  const name = p._ruleName;
5663
5669
  const target = name === void 0 ? void 0 : resolve3?.(name);
5664
- return target === void 0 ? true : mayCommitFailure(target, seen, resolve3);
5670
+ return target === void 0 ? true : [target];
5665
5671
  }
5666
5672
  }
5667
- case "unknown":
5668
- return true;
5669
5673
  default:
5670
- return false;
5674
+ return NO_EDGES;
5671
5675
  }
5672
5676
  }
5677
+ function reachesMarked(root, successors, memo) {
5678
+ const known = memo.get(root);
5679
+ if (known !== void 0) return known;
5680
+ const order = /* @__PURE__ */ new Map();
5681
+ const low = [];
5682
+ const found = [];
5683
+ const stack = [];
5684
+ const visit = (node2) => {
5685
+ const at = low.length;
5686
+ order.set(node2, at);
5687
+ low.push(at);
5688
+ found.push(false);
5689
+ stack.push(node2);
5690
+ const next = successors(node2);
5691
+ if (next === true) found[at] = true;
5692
+ else {
5693
+ for (const child of next) {
5694
+ if (found[at]) break;
5695
+ const hit = memo.get(child);
5696
+ if (hit !== void 0) {
5697
+ if (hit) found[at] = true;
5698
+ continue;
5699
+ }
5700
+ const seen = order.get(child);
5701
+ if (seen !== void 0) {
5702
+ if (seen < low[at]) low[at] = seen;
5703
+ continue;
5704
+ }
5705
+ const k = visit(child);
5706
+ if (low[k] < low[at]) low[at] = low[k];
5707
+ if (found[k]) found[at] = true;
5708
+ }
5709
+ }
5710
+ if (low[at] === at) {
5711
+ const answer = found[at];
5712
+ let member;
5713
+ do {
5714
+ member = stack.pop();
5715
+ memo.set(member, answer);
5716
+ } while (member !== node2);
5717
+ }
5718
+ return at;
5719
+ };
5720
+ return found[visit(root)];
5721
+ }
5673
5722
  function hasSemanticReduction(roots, externalRefs) {
5674
5723
  const seen = /* @__PURE__ */ new Set();
5675
5724
  const visit = (parser2) => {
@@ -6858,21 +6907,20 @@ var Encoder = class {
6858
6907
  nonCommittingChoiceSites = /* @__PURE__ */ new Set();
6859
6908
  failureRollbackCleanSites = /* @__PURE__ */ new Set();
6860
6909
  /**
6861
- * `hasSideWriter` memo for `failureNeedsRollback`, shared across every call
6862
- * for the lifetime of this `Encoder`. A cached entry is written only when
6863
- * `complete` is true, which means the answer is a path-independent fact
6864
- * about that combinator node — it does not depend on which root's traversal
6865
- * asked, only on the node's own reachable subgraph. Scoping the memo per call
6866
- * (as a local inside `failureNeedsRollback`) was correct but discarded that
6867
- * fact after every arm, so a grammar with many ordered-choice arms / optional
6868
- * / attempt sites re-walked the SAME shared rule graph from scratch for each
6869
- * site — O(sites × graph size) instead of O(graph size). Measured on the
6870
- * scss-parser grammar (composed CSS-base superset, the deepest/largest of the
6871
- * four grammars): 47-50s for one `compileRuleMap` with the per-call memo,
6872
- * versus the whole macro transform completing in under a second once this
6873
- * memo is hoisted here.
6910
+ * `reachesSideWriter`'s answers, shared by every `failureNeedsRollback` call
6911
+ * for the lifetime of this `Encoder`. Every entry is exact — it depends only on
6912
+ * the node's own reachable subgraph, never on which root's walk asked — so each
6913
+ * combinator is walked once per encode however many choice arms, optionals and
6914
+ * attempt sites reach it.
6874
6915
  */
6875
6916
  sideWriterMemo = /* @__PURE__ */ new Map();
6917
+ /**
6918
+ * `mayCommitFailure`'s answers, shared by every choice site for the lifetime
6919
+ * of this `Encoder` — exact for the graph and the `winners` resolver it is
6920
+ * encoding, so the rule graph is walked once per encode rather than once per
6921
+ * choice site.
6922
+ */
6923
+ commitFailureMemo = /* @__PURE__ */ new Map();
6876
6924
  labels = void 0;
6877
6925
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
6878
6926
  activeTrivia = void 0;
@@ -7303,63 +7351,45 @@ var Encoder = class {
7303
7351
  * writer census covers the side sinks that proof deliberately does not model:
7304
7352
  * fields, recovered errors, and interpreted scanners. Strict assemblies do
7305
7353
  * not run repeat recovery, but these rows can still publish through a caller's
7306
- * context. Cycles are safe to stop: revisiting the same combinator cannot
7307
- * discover a writer the first traversal did not already reach.
7354
+ * context.
7308
7355
  */
7309
7356
  failureNeedsRollback(p, autoNot) {
7310
7357
  const refName = p._ruleName;
7311
7358
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
7312
7359
  const root = winner !== void 0 && !winnerWrapsReference2(winner, p) ? winner : p;
7313
- const sideWriterMemo = this.sideWriterMemo;
7314
- const seen = /* @__PURE__ */ new Set();
7315
- const hasSideWriter = (current) => {
7316
- const cached = sideWriterMemo.get(current);
7317
- if (cached !== void 0) return { found: cached, complete: true };
7318
- if (seen.has(current)) return { found: false, complete: false };
7319
- seen.add(current);
7320
- const def = current._def;
7321
- let result;
7322
- switch (def.tag) {
7323
- case "field":
7324
- case "expect":
7325
- case "recover":
7326
- case "scanTo":
7327
- case "unknown":
7328
- result = { found: true, complete: true };
7329
- break;
7330
- case "lazy": {
7331
- const name = current._ruleName;
7332
- const resolvedWinner = name === void 0 ? void 0 : this.winners?.[name];
7333
- if (resolvedWinner !== void 0 && !winnerWrapsReference2(resolvedWinner, current)) {
7334
- result = hasSideWriter(resolvedWinner);
7335
- break;
7336
- }
7337
- try {
7338
- result = hasSideWriter(def.thunk());
7339
- } catch {
7340
- result = { found: true, complete: true };
7341
- }
7342
- break;
7343
- }
7344
- default: {
7345
- let complete = true;
7346
- result = { found: false, complete: true };
7347
- for (const child of childrenOf2(def)) {
7348
- const childResult = hasSideWriter(child);
7349
- if (childResult.found) {
7350
- result = { found: true, complete: true };
7351
- break;
7352
- }
7353
- complete &&= childResult.complete;
7354
- }
7355
- if (!result.found) result = { found: false, complete };
7360
+ return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || this.reachesSideWriter(root) || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
7361
+ }
7362
+ /**
7363
+ * Whether a field, recovered error, interpreted scanner or unresolvable shape
7364
+ * is reachable from `root` — the writer census of `failureNeedsRollback`,
7365
+ * entering each combinator once per `Encoder` (see `reachesMarked`).
7366
+ */
7367
+ reachesSideWriter(root) {
7368
+ return reachesMarked(root, (node2) => this.sideWriterSuccessors(node2), this.sideWriterMemo);
7369
+ }
7370
+ /** The census's edges out of `node`, or `true` when `node` is itself a writer. */
7371
+ sideWriterSuccessors(node2) {
7372
+ const def = node2._def;
7373
+ switch (def.tag) {
7374
+ case "field":
7375
+ case "expect":
7376
+ case "recover":
7377
+ case "scanTo":
7378
+ case "unknown":
7379
+ return true;
7380
+ case "lazy": {
7381
+ const name = node2._ruleName;
7382
+ const winner = name === void 0 ? void 0 : this.winners?.[name];
7383
+ if (winner !== void 0 && !winnerWrapsReference2(winner, node2)) return [winner];
7384
+ try {
7385
+ return [def.thunk()];
7386
+ } catch {
7387
+ return true;
7356
7388
  }
7357
7389
  }
7358
- seen.delete(current);
7359
- if (result.found || result.complete) sideWriterMemo.set(current, result.found);
7360
- return result;
7361
- };
7362
- return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || hasSideWriter(root).found || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
7390
+ default:
7391
+ return childrenOf2(def);
7392
+ }
7363
7393
  }
7364
7394
  /** A first set becomes a char class string, or −1 for `any`. */
7365
7395
  charClass(fs3) {
@@ -7690,7 +7720,7 @@ var Encoder = class {
7690
7720
  this.code[head + 4 + kids.length + i] = this.expected(deriveExpected(arms[i]));
7691
7721
  }
7692
7722
  this.choiceRollbackMasks.set(head, rollbackMask);
7693
- if (arms.every((arm) => !mayCommitFailure(arm, /* @__PURE__ */ new Set(), rr))) {
7723
+ if (arms.every((arm) => !mayCommitFailure(arm, rr, this.commitFailureMemo))) {
7694
7724
  this.nonCommittingChoiceSites.add(head);
7695
7725
  }
7696
7726
  return head;
@@ -17327,7 +17357,7 @@ function fieldOptionsSrc(def) {
17327
17357
  }
17328
17358
 
17329
17359
  // src/version.ts
17330
- var PARSEMAN_VERSION = "0.55.0";
17360
+ var PARSEMAN_VERSION = "0.55.2";
17331
17361
 
17332
17362
  // src/compiler/compile-linkable-table.ts
17333
17363
  function isLocal(rule) {