parseman 0.55.0 → 0.55.1

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.
package/dist/index.js CHANGED
@@ -7259,19 +7259,11 @@ var Encoder = class {
7259
7259
  nonCommittingChoiceSites = /* @__PURE__ */ new Set();
7260
7260
  failureRollbackCleanSites = /* @__PURE__ */ new Set();
7261
7261
  /**
7262
- * `hasSideWriter` memo for `failureNeedsRollback`, shared across every call
7263
- * for the lifetime of this `Encoder`. A cached entry is written only when
7264
- * `complete` is true, which means the answer is a path-independent fact
7265
- * about that combinator node — it does not depend on which root's traversal
7266
- * asked, only on the node's own reachable subgraph. Scoping the memo per call
7267
- * (as a local inside `failureNeedsRollback`) was correct but discarded that
7268
- * fact after every arm, so a grammar with many ordered-choice arms / optional
7269
- * / attempt sites re-walked the SAME shared rule graph from scratch for each
7270
- * site — O(sites × graph size) instead of O(graph size). Measured on the
7271
- * scss-parser grammar (composed CSS-base superset, the deepest/largest of the
7272
- * four grammars): 47-50s for one `compileRuleMap` with the per-call memo,
7273
- * versus the whole macro transform completing in under a second once this
7274
- * memo is hoisted here.
7262
+ * `reachesSideWriter`'s answers, shared by every `failureNeedsRollback` call
7263
+ * for the lifetime of this `Encoder`. Every entry is exact — it depends only on
7264
+ * the node's own reachable subgraph, never on which root's walk asked — so each
7265
+ * combinator is walked once per encode however many choice arms, optionals and
7266
+ * attempt sites reach it.
7275
7267
  */
7276
7268
  sideWriterMemo = /* @__PURE__ */ new Map();
7277
7269
  labels = void 0;
@@ -7704,63 +7696,96 @@ var Encoder = class {
7704
7696
  * writer census covers the side sinks that proof deliberately does not model:
7705
7697
  * fields, recovered errors, and interpreted scanners. Strict assemblies do
7706
7698
  * not run repeat recovery, but these rows can still publish through a caller's
7707
- * context. Cycles are safe to stop: revisiting the same combinator cannot
7708
- * discover a writer the first traversal did not already reach.
7699
+ * context.
7709
7700
  */
7710
7701
  failureNeedsRollback(p, autoNot) {
7711
7702
  const refName = p._ruleName;
7712
7703
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
7713
7704
  const root = winner !== void 0 && !winnerWrapsReference2(winner, p) ? winner : p;
7714
- const sideWriterMemo = this.sideWriterMemo;
7715
- const seen = /* @__PURE__ */ new Set();
7716
- const hasSideWriter = (current) => {
7717
- const cached = sideWriterMemo.get(current);
7718
- if (cached !== void 0) return { found: cached, complete: true };
7719
- if (seen.has(current)) return { found: false, complete: false };
7720
- seen.add(current);
7721
- const def = current._def;
7722
- let result;
7723
- switch (def.tag) {
7724
- case "field":
7725
- case "expect":
7726
- case "recover":
7727
- case "scanTo":
7728
- case "unknown":
7729
- result = { found: true, complete: true };
7730
- break;
7731
- case "lazy": {
7732
- const name = current._ruleName;
7733
- const resolvedWinner = name === void 0 ? void 0 : this.winners?.[name];
7734
- if (resolvedWinner !== void 0 && !winnerWrapsReference2(resolvedWinner, current)) {
7735
- result = hasSideWriter(resolvedWinner);
7736
- break;
7737
- }
7738
- try {
7739
- result = hasSideWriter(def.thunk());
7740
- } catch {
7741
- result = { found: true, complete: true };
7705
+ return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || this.reachesSideWriter(root) || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
7706
+ }
7707
+ /**
7708
+ * Whether a field, recovered error, interpreted scanner or unresolvable shape
7709
+ * is reachable from `root` — the writer census of `failureNeedsRollback`.
7710
+ *
7711
+ * Reachability over a cyclic graph, decided with Tarjan's strongly connected
7712
+ * components so that every node the walk enters leaves with its exact answer
7713
+ * memoized: the members of one component reach each other, so they share one
7714
+ * answer, settled when the walk returns to the component's first node. A DFS
7715
+ * that instead cuts a cycle at its own ancestor path learns nothing it can
7716
+ * keep about a node whose walk hit the cut, and re-walks that node along
7717
+ * every path that reaches it — exponential over recursive value rules (linking
7718
+ * jess's SCSS grammar cost ~290G instructions). This walk enters each node
7719
+ * once per `Encoder`.
7720
+ */
7721
+ reachesSideWriter(root) {
7722
+ const memo = this.sideWriterMemo;
7723
+ const known = memo.get(root);
7724
+ if (known !== void 0) return known;
7725
+ const order = /* @__PURE__ */ new Map();
7726
+ const low = [];
7727
+ const found = [];
7728
+ const stack = [];
7729
+ const visit = (node2) => {
7730
+ const at = low.length;
7731
+ order.set(node2, at);
7732
+ low.push(at);
7733
+ found.push(false);
7734
+ stack.push(node2);
7735
+ const next = this.sideWriterSuccessors(node2);
7736
+ if (next === true) found[at] = true;
7737
+ else {
7738
+ for (const child of next) {
7739
+ const hit = memo.get(child);
7740
+ if (hit !== void 0) {
7741
+ if (hit) found[at] = true;
7742
+ continue;
7742
7743
  }
7743
- break;
7744
- }
7745
- default: {
7746
- let complete = true;
7747
- result = { found: false, complete: true };
7748
- for (const child of childrenOf2(def)) {
7749
- const childResult = hasSideWriter(child);
7750
- if (childResult.found) {
7751
- result = { found: true, complete: true };
7752
- break;
7753
- }
7754
- complete &&= childResult.complete;
7744
+ const seen = order.get(child);
7745
+ if (seen !== void 0) {
7746
+ if (seen < low[at]) low[at] = seen;
7747
+ continue;
7755
7748
  }
7756
- if (!result.found) result = { found: false, complete };
7749
+ const k = visit(child);
7750
+ if (low[k] < low[at]) low[at] = low[k];
7751
+ if (found[k]) found[at] = true;
7757
7752
  }
7758
7753
  }
7759
- seen.delete(current);
7760
- if (result.found || result.complete) sideWriterMemo.set(current, result.found);
7761
- return result;
7754
+ if (low[at] === at) {
7755
+ const answer = found[at];
7756
+ let member;
7757
+ do {
7758
+ member = stack.pop();
7759
+ memo.set(member, answer);
7760
+ } while (member !== node2);
7761
+ }
7762
+ return at;
7762
7763
  };
7763
- return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || hasSideWriter(root).found || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
7764
+ return found[visit(root)];
7765
+ }
7766
+ /** The census's edges out of `node`, or `true` when `node` is itself a writer. */
7767
+ sideWriterSuccessors(node2) {
7768
+ const def = node2._def;
7769
+ switch (def.tag) {
7770
+ case "field":
7771
+ case "expect":
7772
+ case "recover":
7773
+ case "scanTo":
7774
+ case "unknown":
7775
+ return true;
7776
+ case "lazy": {
7777
+ const name = node2._ruleName;
7778
+ const winner = name === void 0 ? void 0 : this.winners?.[name];
7779
+ if (winner !== void 0 && !winnerWrapsReference2(winner, node2)) return [winner];
7780
+ try {
7781
+ return [def.thunk()];
7782
+ } catch {
7783
+ return true;
7784
+ }
7785
+ }
7786
+ default:
7787
+ return childrenOf2(def);
7788
+ }
7764
7789
  }
7765
7790
  /** A first set becomes a char class string, or −1 for `any`. */
7766
7791
  charClass(fs) {
@@ -17741,7 +17766,7 @@ function fieldOptionsSrc(def) {
17741
17766
  }
17742
17767
 
17743
17768
  // src/version.ts
17744
- var PARSEMAN_VERSION = "0.55.0";
17769
+ var PARSEMAN_VERSION = "0.55.1";
17745
17770
 
17746
17771
  // src/compiler/compile-linkable-table.ts
17747
17772
  function isLocal(rule) {