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.
package/dist/index.js CHANGED
@@ -6025,19 +6025,22 @@ function hasDirectBuildDef(p, seen = /* @__PURE__ */ new Set()) {
6025
6025
  return false;
6026
6026
  }
6027
6027
  }
6028
- function mayCommitFailure(p, seen = /* @__PURE__ */ new Set(), resolve) {
6029
- if (seen.has(p)) return false;
6030
- seen.add(p);
6028
+ function mayCommitFailure(p, resolve, memo = /* @__PURE__ */ new Map()) {
6029
+ return reachesMarked(p, (node2) => commitFailureSuccessors(node2, resolve), memo);
6030
+ }
6031
+ var NO_EDGES = [];
6032
+ function commitFailureSuccessors(p, resolve) {
6031
6033
  const d = p._def;
6032
6034
  switch (d.tag) {
6033
6035
  case "dispatch":
6036
+ case "unknown":
6034
6037
  return true;
6035
6038
  case "choice":
6036
6039
  case "sequence":
6037
- return d.parsers.some((x) => mayCommitFailure(x, seen, resolve));
6040
+ return d.parsers;
6038
6041
  // A contained transaction reports every failure uncommitted.
6039
6042
  case "attempt":
6040
- return d.contain !== true && mayCommitFailure(d.parser, seen, resolve);
6043
+ return d.contain === true ? NO_EDGES : [d.parser];
6041
6044
  case "many":
6042
6045
  case "oneOrMore":
6043
6046
  case "optional":
@@ -6046,31 +6049,77 @@ function mayCommitFailure(p, seen = /* @__PURE__ */ new Set(), resolve) {
6046
6049
  case "field":
6047
6050
  case "grammar":
6048
6051
  case "node":
6049
- return mayCommitFailure(d.parser, seen, resolve);
6050
- case "sepBy":
6051
- return mayCommitFailure(d.parser, seen, resolve) || mayCommitFailure(d.separator, seen, resolve);
6052
6052
  case "token":
6053
6053
  case "leaf":
6054
- return mayCommitFailure(d.parser, seen, resolve);
6055
6054
  case "withCtx":
6056
- return mayCommitFailure(d.parser, seen, resolve);
6055
+ return [d.parser];
6056
+ case "sepBy":
6057
+ return [d.parser, d.separator];
6057
6058
  case "scanTo":
6058
- return mayCommitFailure(d.sentinel, seen, resolve) || d.skip.some((x) => mayCommitFailure(x, seen, resolve)) || d.recoverAt !== void 0 && mayCommitFailure(d.recoverAt, seen, resolve) || d.stopAt !== void 0 && mayCommitFailure(d.stopAt, seen, resolve);
6059
+ return [
6060
+ d.sentinel,
6061
+ ...d.skip,
6062
+ ...d.recoverAt === void 0 ? NO_EDGES : [d.recoverAt],
6063
+ ...d.stopAt === void 0 ? NO_EDGES : [d.stopAt]
6064
+ ];
6059
6065
  case "lazy": {
6060
6066
  try {
6061
- return mayCommitFailure(d.thunk(), seen, resolve);
6067
+ return [d.thunk()];
6062
6068
  } catch {
6063
6069
  const name = p._ruleName;
6064
6070
  const target = name === void 0 ? void 0 : resolve?.(name);
6065
- return target === void 0 ? true : mayCommitFailure(target, seen, resolve);
6071
+ return target === void 0 ? true : [target];
6066
6072
  }
6067
6073
  }
6068
- case "unknown":
6069
- return true;
6070
6074
  default:
6071
- return false;
6075
+ return NO_EDGES;
6072
6076
  }
6073
6077
  }
6078
+ function reachesMarked(root, successors, memo) {
6079
+ const known = memo.get(root);
6080
+ if (known !== void 0) return known;
6081
+ const order = /* @__PURE__ */ new Map();
6082
+ const low = [];
6083
+ const found = [];
6084
+ const stack = [];
6085
+ const visit = (node2) => {
6086
+ const at = low.length;
6087
+ order.set(node2, at);
6088
+ low.push(at);
6089
+ found.push(false);
6090
+ stack.push(node2);
6091
+ const next = successors(node2);
6092
+ if (next === true) found[at] = true;
6093
+ else {
6094
+ for (const child of next) {
6095
+ if (found[at]) break;
6096
+ const hit = memo.get(child);
6097
+ if (hit !== void 0) {
6098
+ if (hit) found[at] = true;
6099
+ continue;
6100
+ }
6101
+ const seen = order.get(child);
6102
+ if (seen !== void 0) {
6103
+ if (seen < low[at]) low[at] = seen;
6104
+ continue;
6105
+ }
6106
+ const k = visit(child);
6107
+ if (low[k] < low[at]) low[at] = low[k];
6108
+ if (found[k]) found[at] = true;
6109
+ }
6110
+ }
6111
+ if (low[at] === at) {
6112
+ const answer = found[at];
6113
+ let member;
6114
+ do {
6115
+ member = stack.pop();
6116
+ memo.set(member, answer);
6117
+ } while (member !== node2);
6118
+ }
6119
+ return at;
6120
+ };
6121
+ return found[visit(root)];
6122
+ }
6074
6123
  function hasSemanticReduction(roots, externalRefs) {
6075
6124
  const seen = /* @__PURE__ */ new Set();
6076
6125
  const visit = (parser2) => {
@@ -7259,21 +7308,20 @@ var Encoder = class {
7259
7308
  nonCommittingChoiceSites = /* @__PURE__ */ new Set();
7260
7309
  failureRollbackCleanSites = /* @__PURE__ */ new Set();
7261
7310
  /**
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.
7311
+ * `reachesSideWriter`'s answers, shared by every `failureNeedsRollback` call
7312
+ * for the lifetime of this `Encoder`. Every entry is exact — it depends only on
7313
+ * the node's own reachable subgraph, never on which root's walk asked — so each
7314
+ * combinator is walked once per encode however many choice arms, optionals and
7315
+ * attempt sites reach it.
7275
7316
  */
7276
7317
  sideWriterMemo = /* @__PURE__ */ new Map();
7318
+ /**
7319
+ * `mayCommitFailure`'s answers, shared by every choice site for the lifetime
7320
+ * of this `Encoder` — exact for the graph and the `winners` resolver it is
7321
+ * encoding, so the rule graph is walked once per encode rather than once per
7322
+ * choice site.
7323
+ */
7324
+ commitFailureMemo = /* @__PURE__ */ new Map();
7277
7325
  labels = void 0;
7278
7326
  /** Trivia table in scope at the row being encoded — codegen's `ctx.activeTrivia`. */
7279
7327
  activeTrivia = void 0;
@@ -7704,63 +7752,45 @@ var Encoder = class {
7704
7752
  * writer census covers the side sinks that proof deliberately does not model:
7705
7753
  * fields, recovered errors, and interpreted scanners. Strict assemblies do
7706
7754
  * 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.
7755
+ * context.
7709
7756
  */
7710
7757
  failureNeedsRollback(p, autoNot) {
7711
7758
  const refName = p._ruleName;
7712
7759
  const winner = refName === void 0 ? void 0 : this.winners?.[refName];
7713
7760
  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 };
7742
- }
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;
7755
- }
7756
- if (!result.found) result = { found: false, complete };
7761
+ return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || this.reachesSideWriter(root) || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
7762
+ }
7763
+ /**
7764
+ * Whether a field, recovered error, interpreted scanner or unresolvable shape
7765
+ * is reachable from `root` — the writer census of `failureNeedsRollback`,
7766
+ * entering each combinator once per `Encoder` (see `reachesMarked`).
7767
+ */
7768
+ reachesSideWriter(root) {
7769
+ return reachesMarked(root, (node2) => this.sideWriterSuccessors(node2), this.sideWriterMemo);
7770
+ }
7771
+ /** The census's edges out of `node`, or `true` when `node` is itself a writer. */
7772
+ sideWriterSuccessors(node2) {
7773
+ const def = node2._def;
7774
+ switch (def.tag) {
7775
+ case "field":
7776
+ case "expect":
7777
+ case "recover":
7778
+ case "scanTo":
7779
+ case "unknown":
7780
+ return true;
7781
+ case "lazy": {
7782
+ const name = node2._ruleName;
7783
+ const winner = name === void 0 ? void 0 : this.winners?.[name];
7784
+ if (winner !== void 0 && !winnerWrapsReference2(winner, node2)) return [winner];
7785
+ try {
7786
+ return [def.thunk()];
7787
+ } catch {
7788
+ return true;
7757
7789
  }
7758
7790
  }
7759
- seen.delete(current);
7760
- if (result.found || result.complete) sideWriterMemo.set(current, result.found);
7761
- return result;
7762
- };
7763
- return mayLeavePartialCapture(root, /* @__PURE__ */ new Set(), true) || hasSideWriter(root).found || (autoNot?.length ?? 0) > 0 && capturesLeaf(root);
7791
+ default:
7792
+ return childrenOf2(def);
7793
+ }
7764
7794
  }
7765
7795
  /** A first set becomes a char class string, or −1 for `any`. */
7766
7796
  charClass(fs) {
@@ -8091,7 +8121,7 @@ var Encoder = class {
8091
8121
  this.code[head + 4 + kids.length + i] = this.expected(deriveExpected(arms[i]));
8092
8122
  }
8093
8123
  this.choiceRollbackMasks.set(head, rollbackMask);
8094
- if (arms.every((arm) => !mayCommitFailure(arm, /* @__PURE__ */ new Set(), rr))) {
8124
+ if (arms.every((arm) => !mayCommitFailure(arm, rr, this.commitFailureMemo))) {
8095
8125
  this.nonCommittingChoiceSites.add(head);
8096
8126
  }
8097
8127
  return head;
@@ -17741,7 +17771,7 @@ function fieldOptionsSrc(def) {
17741
17771
  }
17742
17772
 
17743
17773
  // src/version.ts
17744
- var PARSEMAN_VERSION = "0.55.0";
17774
+ var PARSEMAN_VERSION = "0.55.2";
17745
17775
 
17746
17776
  // src/compiler/compile-linkable-table.ts
17747
17777
  function isLocal(rule) {