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.
package/dist/cli/index.js CHANGED
@@ -6107,19 +6107,11 @@ var Encoder = class {
6107
6107
  nonCommittingChoiceSites = /* @__PURE__ */ new Set();
6108
6108
  failureRollbackCleanSites = /* @__PURE__ */ new Set();
6109
6109
  /**
6110
- * `hasSideWriter` memo for `failureNeedsRollback`, shared across every call
6111
- * for the lifetime of this `Encoder`. A cached entry is written only when
6112
- * `complete` is true, which means the answer is a path-independent fact
6113
- * about that combinator node — it does not depend on which root's traversal
6114
- * asked, only on the node's own reachable subgraph. Scoping the memo per call
6115
- * (as a local inside `failureNeedsRollback`) was correct but discarded that
6116
- * fact after every arm, so a grammar with many ordered-choice arms / optional
6117
- * / attempt sites re-walked the SAME shared rule graph from scratch for each
6118
- * site — O(sites × graph size) instead of O(graph size). Measured on the
6119
- * scss-parser grammar (composed CSS-base superset, the deepest/largest of the
6120
- * four grammars): 47-50s for one `compileRuleMap` with the per-call memo,
6121
- * versus the whole macro transform completing in under a second once this
6122
- * memo is hoisted here.
6110
+ * `reachesSideWriter`'s answers, shared by every `failureNeedsRollback` call
6111
+ * for the lifetime of this `Encoder`. Every entry is exact — it depends only on
6112
+ * the node's own reachable subgraph, never on which root's walk asked — so each
6113
+ * combinator is walked once per encode however many choice arms, optionals and
6114
+ * attempt sites reach it.
6123
6115
  */
6124
6116
  sideWriterMemo = /* @__PURE__ */ new Map();
6125
6117
  labels = void 0;
@@ -6552,63 +6544,96 @@ var Encoder = class {
6552
6544
  * writer census covers the side sinks that proof deliberately does not model:
6553
6545
  * fields, recovered errors, and interpreted scanners. Strict assemblies do
6554
6546
  * not run repeat recovery, but these rows can still publish through a caller's
6555
- * context. Cycles are safe to stop: revisiting the same combinator cannot
6556
- * discover a writer the first traversal did not already reach.
6547
+ * context.
6557
6548
  */
6558
6549
  failureNeedsRollback(p, autoNot) {
6559
6550
  const refName = p._ruleName;
6560
6551
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
6561
6552
  const root = winner !== void 0 && !winnerWrapsReference2(winner, p) ? winner : p;
6562
- const sideWriterMemo = this.sideWriterMemo;
6563
- const seen = /* @__PURE__ */ new Set();
6564
- const hasSideWriter = (current) => {
6565
- const cached = sideWriterMemo.get(current);
6566
- if (cached !== void 0) return { found: cached, complete: true };
6567
- if (seen.has(current)) return { found: false, complete: false };
6568
- seen.add(current);
6569
- const def = current._def;
6570
- let result;
6571
- switch (def.tag) {
6572
- case "field":
6573
- case "expect":
6574
- case "recover":
6575
- case "scanTo":
6576
- case "unknown":
6577
- result = { found: true, complete: true };
6578
- break;
6579
- case "lazy": {
6580
- const name = current._ruleName;
6581
- const resolvedWinner = name === void 0 ? void 0 : this.winners?.[name];
6582
- if (resolvedWinner !== void 0 && !winnerWrapsReference2(resolvedWinner, current)) {
6583
- result = hasSideWriter(resolvedWinner);
6584
- break;
6585
- }
6586
- try {
6587
- result = hasSideWriter(def.thunk());
6588
- } catch {
6589
- result = { found: true, complete: true };
6553
+ return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || this.reachesSideWriter(root) || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
6554
+ }
6555
+ /**
6556
+ * Whether a field, recovered error, interpreted scanner or unresolvable shape
6557
+ * is reachable from `root` — the writer census of `failureNeedsRollback`.
6558
+ *
6559
+ * Reachability over a cyclic graph, decided with Tarjan's strongly connected
6560
+ * components so that every node the walk enters leaves with its exact answer
6561
+ * memoized: the members of one component reach each other, so they share one
6562
+ * answer, settled when the walk returns to the component's first node. A DFS
6563
+ * that instead cuts a cycle at its own ancestor path learns nothing it can
6564
+ * keep about a node whose walk hit the cut, and re-walks that node along
6565
+ * every path that reaches it — exponential over recursive value rules (linking
6566
+ * jess's SCSS grammar cost ~290G instructions). This walk enters each node
6567
+ * once per `Encoder`.
6568
+ */
6569
+ reachesSideWriter(root) {
6570
+ const memo = this.sideWriterMemo;
6571
+ const known = memo.get(root);
6572
+ if (known !== void 0) return known;
6573
+ const order = /* @__PURE__ */ new Map();
6574
+ const low = [];
6575
+ const found = [];
6576
+ const stack = [];
6577
+ const visit = (node2) => {
6578
+ const at = low.length;
6579
+ order.set(node2, at);
6580
+ low.push(at);
6581
+ found.push(false);
6582
+ stack.push(node2);
6583
+ const next = this.sideWriterSuccessors(node2);
6584
+ if (next === true) found[at] = true;
6585
+ else {
6586
+ for (const child of next) {
6587
+ const hit = memo.get(child);
6588
+ if (hit !== void 0) {
6589
+ if (hit) found[at] = true;
6590
+ continue;
6590
6591
  }
6591
- break;
6592
- }
6593
- default: {
6594
- let complete = true;
6595
- result = { found: false, complete: true };
6596
- for (const child of childrenOf(def)) {
6597
- const childResult = hasSideWriter(child);
6598
- if (childResult.found) {
6599
- result = { found: true, complete: true };
6600
- break;
6601
- }
6602
- complete &&= childResult.complete;
6592
+ const seen = order.get(child);
6593
+ if (seen !== void 0) {
6594
+ if (seen < low[at]) low[at] = seen;
6595
+ continue;
6603
6596
  }
6604
- if (!result.found) result = { found: false, complete };
6597
+ const k = visit(child);
6598
+ if (low[k] < low[at]) low[at] = low[k];
6599
+ if (found[k]) found[at] = true;
6605
6600
  }
6606
6601
  }
6607
- seen.delete(current);
6608
- if (result.found || result.complete) sideWriterMemo.set(current, result.found);
6609
- return result;
6602
+ if (low[at] === at) {
6603
+ const answer = found[at];
6604
+ let member;
6605
+ do {
6606
+ member = stack.pop();
6607
+ memo.set(member, answer);
6608
+ } while (member !== node2);
6609
+ }
6610
+ return at;
6610
6611
  };
6611
- return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || hasSideWriter(root).found || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
6612
+ return found[visit(root)];
6613
+ }
6614
+ /** The census's edges out of `node`, or `true` when `node` is itself a writer. */
6615
+ sideWriterSuccessors(node2) {
6616
+ const def = node2._def;
6617
+ switch (def.tag) {
6618
+ case "field":
6619
+ case "expect":
6620
+ case "recover":
6621
+ case "scanTo":
6622
+ case "unknown":
6623
+ return true;
6624
+ case "lazy": {
6625
+ const name = node2._ruleName;
6626
+ const winner = name === void 0 ? void 0 : this.winners?.[name];
6627
+ if (winner !== void 0 && !winnerWrapsReference2(winner, node2)) return [winner];
6628
+ try {
6629
+ return [def.thunk()];
6630
+ } catch {
6631
+ return true;
6632
+ }
6633
+ }
6634
+ default:
6635
+ return childrenOf(def);
6636
+ }
6612
6637
  }
6613
6638
  /** A first set becomes a char class string, or −1 for `any`. */
6614
6639
  charClass(fs) {
@@ -7125,6 +7150,9 @@ var Encoder = class {
7125
7150
  this.expected([adjacencyExpected(d)])
7126
7151
  );
7127
7152
  case "withCtx": {
7153
+ if (typeof d.extra === "function") {
7154
+ return this.emit(OP_WITHCTX, ~this.fn(d.extra, d.extraSrc ?? null), this.node(d.parser).ip);
7155
+ }
7128
7156
  if (!emittableConst(d.extra)) {
7129
7157
  this.runtimeOnly.add("withCtx(extra) \u2014 the state object is not serialisable");
7130
7158
  }
@@ -12470,8 +12498,21 @@ function assemble(t2, prog, cfg) {
12470
12498
  };
12471
12499
  }
12472
12500
  case OP_WITHCTX: {
12473
- const extra = k[code[ip + 1]];
12501
+ const ki = code[ip + 1];
12474
12502
  const child = link(code[ip + 2]);
12503
+ if (ki < 0) {
12504
+ const derive2 = fns[~ki];
12505
+ return (input, pos, ctx) => {
12506
+ const saved = ctx.state;
12507
+ ctx.state = derive2(saved);
12508
+ try {
12509
+ return child(input, pos, ctx);
12510
+ } finally {
12511
+ ctx.state = saved;
12512
+ }
12513
+ };
12514
+ }
12515
+ const extra = k[ki];
12475
12516
  return (input, pos, ctx) => {
12476
12517
  const saved = ctx.state;
12477
12518
  ctx.state = extra;