parseman 0.54.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.
@@ -6225,19 +6225,11 @@ var Encoder = class {
6225
6225
  nonCommittingChoiceSites = /* @__PURE__ */ new Set();
6226
6226
  failureRollbackCleanSites = /* @__PURE__ */ new Set();
6227
6227
  /**
6228
- * `hasSideWriter` memo for `failureNeedsRollback`, shared across every call
6229
- * for the lifetime of this `Encoder`. A cached entry is written only when
6230
- * `complete` is true, which means the answer is a path-independent fact
6231
- * about that combinator node — it does not depend on which root's traversal
6232
- * asked, only on the node's own reachable subgraph. Scoping the memo per call
6233
- * (as a local inside `failureNeedsRollback`) was correct but discarded that
6234
- * fact after every arm, so a grammar with many ordered-choice arms / optional
6235
- * / attempt sites re-walked the SAME shared rule graph from scratch for each
6236
- * site — O(sites × graph size) instead of O(graph size). Measured on the
6237
- * scss-parser grammar (composed CSS-base superset, the deepest/largest of the
6238
- * four grammars): 47-50s for one `compileRuleMap` with the per-call memo,
6239
- * versus the whole macro transform completing in under a second once this
6240
- * memo is hoisted here.
6228
+ * `reachesSideWriter`'s answers, shared by every `failureNeedsRollback` call
6229
+ * for the lifetime of this `Encoder`. Every entry is exact — it depends only on
6230
+ * the node's own reachable subgraph, never on which root's walk asked — so each
6231
+ * combinator is walked once per encode however many choice arms, optionals and
6232
+ * attempt sites reach it.
6241
6233
  */
6242
6234
  sideWriterMemo = /* @__PURE__ */ new Map();
6243
6235
  labels = void 0;
@@ -6670,63 +6662,96 @@ var Encoder = class {
6670
6662
  * writer census covers the side sinks that proof deliberately does not model:
6671
6663
  * fields, recovered errors, and interpreted scanners. Strict assemblies do
6672
6664
  * not run repeat recovery, but these rows can still publish through a caller's
6673
- * context. Cycles are safe to stop: revisiting the same combinator cannot
6674
- * discover a writer the first traversal did not already reach.
6665
+ * context.
6675
6666
  */
6676
6667
  failureNeedsRollback(p, autoNot) {
6677
6668
  const refName = p._ruleName;
6678
6669
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
6679
6670
  const root = winner !== void 0 && !winnerWrapsReference2(winner, p) ? winner : p;
6680
- const sideWriterMemo = this.sideWriterMemo;
6681
- const seen = /* @__PURE__ */ new Set();
6682
- const hasSideWriter = (current) => {
6683
- const cached = sideWriterMemo.get(current);
6684
- if (cached !== void 0) return { found: cached, complete: true };
6685
- if (seen.has(current)) return { found: false, complete: false };
6686
- seen.add(current);
6687
- const def = current._def;
6688
- let result;
6689
- switch (def.tag) {
6690
- case "field":
6691
- case "expect":
6692
- case "recover":
6693
- case "scanTo":
6694
- case "unknown":
6695
- result = { found: true, complete: true };
6696
- break;
6697
- case "lazy": {
6698
- const name = current._ruleName;
6699
- const resolvedWinner = name === void 0 ? void 0 : this.winners?.[name];
6700
- if (resolvedWinner !== void 0 && !winnerWrapsReference2(resolvedWinner, current)) {
6701
- result = hasSideWriter(resolvedWinner);
6702
- break;
6703
- }
6704
- try {
6705
- result = hasSideWriter(def.thunk());
6706
- } catch {
6707
- result = { found: true, complete: true };
6671
+ return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || this.reachesSideWriter(root) || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
6672
+ }
6673
+ /**
6674
+ * Whether a field, recovered error, interpreted scanner or unresolvable shape
6675
+ * is reachable from `root` — the writer census of `failureNeedsRollback`.
6676
+ *
6677
+ * Reachability over a cyclic graph, decided with Tarjan's strongly connected
6678
+ * components so that every node the walk enters leaves with its exact answer
6679
+ * memoized: the members of one component reach each other, so they share one
6680
+ * answer, settled when the walk returns to the component's first node. A DFS
6681
+ * that instead cuts a cycle at its own ancestor path learns nothing it can
6682
+ * keep about a node whose walk hit the cut, and re-walks that node along
6683
+ * every path that reaches it — exponential over recursive value rules (linking
6684
+ * jess's SCSS grammar cost ~290G instructions). This walk enters each node
6685
+ * once per `Encoder`.
6686
+ */
6687
+ reachesSideWriter(root) {
6688
+ const memo = this.sideWriterMemo;
6689
+ const known = memo.get(root);
6690
+ if (known !== void 0) return known;
6691
+ const order = /* @__PURE__ */ new Map();
6692
+ const low = [];
6693
+ const found = [];
6694
+ const stack = [];
6695
+ const visit = (node2) => {
6696
+ const at = low.length;
6697
+ order.set(node2, at);
6698
+ low.push(at);
6699
+ found.push(false);
6700
+ stack.push(node2);
6701
+ const next = this.sideWriterSuccessors(node2);
6702
+ if (next === true) found[at] = true;
6703
+ else {
6704
+ for (const child of next) {
6705
+ const hit = memo.get(child);
6706
+ if (hit !== void 0) {
6707
+ if (hit) found[at] = true;
6708
+ continue;
6708
6709
  }
6709
- break;
6710
- }
6711
- default: {
6712
- let complete = true;
6713
- result = { found: false, complete: true };
6714
- for (const child of childrenOf(def)) {
6715
- const childResult = hasSideWriter(child);
6716
- if (childResult.found) {
6717
- result = { found: true, complete: true };
6718
- break;
6719
- }
6720
- complete &&= childResult.complete;
6710
+ const seen = order.get(child);
6711
+ if (seen !== void 0) {
6712
+ if (seen < low[at]) low[at] = seen;
6713
+ continue;
6721
6714
  }
6722
- if (!result.found) result = { found: false, complete };
6715
+ const k = visit(child);
6716
+ if (low[k] < low[at]) low[at] = low[k];
6717
+ if (found[k]) found[at] = true;
6723
6718
  }
6724
6719
  }
6725
- seen.delete(current);
6726
- if (result.found || result.complete) sideWriterMemo.set(current, result.found);
6727
- return result;
6720
+ if (low[at] === at) {
6721
+ const answer = found[at];
6722
+ let member;
6723
+ do {
6724
+ member = stack.pop();
6725
+ memo.set(member, answer);
6726
+ } while (member !== node2);
6727
+ }
6728
+ return at;
6728
6729
  };
6729
- return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || hasSideWriter(root).found || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
6730
+ return found[visit(root)];
6731
+ }
6732
+ /** The census's edges out of `node`, or `true` when `node` is itself a writer. */
6733
+ sideWriterSuccessors(node2) {
6734
+ const def = node2._def;
6735
+ switch (def.tag) {
6736
+ case "field":
6737
+ case "expect":
6738
+ case "recover":
6739
+ case "scanTo":
6740
+ case "unknown":
6741
+ return true;
6742
+ case "lazy": {
6743
+ const name = node2._ruleName;
6744
+ const winner = name === void 0 ? void 0 : this.winners?.[name];
6745
+ if (winner !== void 0 && !winnerWrapsReference2(winner, node2)) return [winner];
6746
+ try {
6747
+ return [def.thunk()];
6748
+ } catch {
6749
+ return true;
6750
+ }
6751
+ }
6752
+ default:
6753
+ return childrenOf(def);
6754
+ }
6730
6755
  }
6731
6756
  /** A first set becomes a char class string, or −1 for `any`. */
6732
6757
  charClass(fs) {
@@ -7243,6 +7268,9 @@ var Encoder = class {
7243
7268
  this.expected([adjacencyExpected(d)])
7244
7269
  );
7245
7270
  case "withCtx": {
7271
+ if (typeof d.extra === "function") {
7272
+ return this.emit(OP_WITHCTX, ~this.fn(d.extra, d.extraSrc ?? null), this.node(d.parser).ip);
7273
+ }
7246
7274
  if (!emittableConst(d.extra)) {
7247
7275
  this.runtimeOnly.add("withCtx(extra) \u2014 the state object is not serialisable");
7248
7276
  }
@@ -12586,8 +12614,21 @@ function assemble(t2, prog, cfg) {
12586
12614
  };
12587
12615
  }
12588
12616
  case OP_WITHCTX: {
12589
- const extra = k[code[ip + 1]];
12617
+ const ki = code[ip + 1];
12590
12618
  const child = link(code[ip + 2]);
12619
+ if (ki < 0) {
12620
+ const derive2 = fns[~ki];
12621
+ return (input, pos, ctx) => {
12622
+ const saved = ctx.state;
12623
+ ctx.state = derive2(saved);
12624
+ try {
12625
+ return child(input, pos, ctx);
12626
+ } finally {
12627
+ ctx.state = saved;
12628
+ }
12629
+ };
12630
+ }
12631
+ const extra = k[ki];
12591
12632
  return (input, pos, ctx) => {
12592
12633
  const saved = ctx.state;
12593
12634
  ctx.state = extra;