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/analysis/diagnostics.js +85 -60
- package/dist/analysis/diagnostics.js.map +1 -1
- package/dist/cli/index.js +85 -60
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +86 -61
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.js +86 -61
- package/dist/plugin/index.js.map +1 -1
- package/dist/table/encode.d.ts.map +1 -1
- package/dist/table/index.js +85 -60
- package/dist/table/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/table/encode.ts +89 -65
- package/src/version.ts +1 -1
package/dist/version.d.ts
CHANGED
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
* Kept in sync with `package.json` by `test/unit/version-sync.test.ts`, which fails
|
|
14
14
|
* the build if this constant drifts from the published version.
|
|
15
15
|
*/
|
|
16
|
-
export declare const PARSEMAN_VERSION = "0.55.
|
|
16
|
+
export declare const PARSEMAN_VERSION = "0.55.1";
|
|
17
17
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
package/src/table/encode.ts
CHANGED
|
@@ -147,19 +147,11 @@ class Encoder {
|
|
|
147
147
|
private nonCommittingChoiceSites = new Set<number>()
|
|
148
148
|
private failureRollbackCleanSites = new Set<number>()
|
|
149
149
|
/**
|
|
150
|
-
* `
|
|
151
|
-
* for the lifetime of this `Encoder`.
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* (as a local inside `failureNeedsRollback`) was correct but discarded that
|
|
156
|
-
* fact after every arm, so a grammar with many ordered-choice arms / optional
|
|
157
|
-
* / attempt sites re-walked the SAME shared rule graph from scratch for each
|
|
158
|
-
* site — O(sites × graph size) instead of O(graph size). Measured on the
|
|
159
|
-
* scss-parser grammar (composed CSS-base superset, the deepest/largest of the
|
|
160
|
-
* four grammars): 47-50s for one `compileRuleMap` with the per-call memo,
|
|
161
|
-
* versus the whole macro transform completing in under a second once this
|
|
162
|
-
* memo is hoisted here.
|
|
150
|
+
* `reachesSideWriter`'s answers, shared by every `failureNeedsRollback` call
|
|
151
|
+
* for the lifetime of this `Encoder`. Every entry is exact — it depends only on
|
|
152
|
+
* the node's own reachable subgraph, never on which root's walk asked — so each
|
|
153
|
+
* combinator is walked once per encode however many choice arms, optionals and
|
|
154
|
+
* attempt sites reach it.
|
|
163
155
|
*/
|
|
164
156
|
private sideWriterMemo = new Map<Combinator<unknown>, boolean>()
|
|
165
157
|
labels: readonly string[] | undefined = undefined
|
|
@@ -653,8 +645,7 @@ class Encoder {
|
|
|
653
645
|
* writer census covers the side sinks that proof deliberately does not model:
|
|
654
646
|
* fields, recovered errors, and interpreted scanners. Strict assemblies do
|
|
655
647
|
* not run repeat recovery, but these rows can still publish through a caller's
|
|
656
|
-
* context.
|
|
657
|
-
* discover a writer the first traversal did not already reach.
|
|
648
|
+
* context.
|
|
658
649
|
*/
|
|
659
650
|
private failureNeedsRollback(
|
|
660
651
|
p: Combinator<unknown>,
|
|
@@ -663,61 +654,94 @@ class Encoder {
|
|
|
663
654
|
const refName = (p as unknown as { _ruleName?: string })._ruleName
|
|
664
655
|
const winner = refName === undefined ? undefined : this.winners?.[refName]
|
|
665
656
|
const root = winner !== undefined && !winnerWrapsReference(winner, p) ? winner : p
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
657
|
+
return mayLeavePartialCapture(root, new Set(), true)
|
|
658
|
+
|| this.reachesSideWriter(root)
|
|
659
|
+
|| ((autoNot?.length ?? 0) > 0 && capturesLeaf(root))
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Whether a field, recovered error, interpreted scanner or unresolvable shape
|
|
664
|
+
* is reachable from `root` — the writer census of `failureNeedsRollback`.
|
|
665
|
+
*
|
|
666
|
+
* Reachability over a cyclic graph, decided with Tarjan's strongly connected
|
|
667
|
+
* components so that every node the walk enters leaves with its exact answer
|
|
668
|
+
* memoized: the members of one component reach each other, so they share one
|
|
669
|
+
* answer, settled when the walk returns to the component's first node. A DFS
|
|
670
|
+
* that instead cuts a cycle at its own ancestor path learns nothing it can
|
|
671
|
+
* keep about a node whose walk hit the cut, and re-walks that node along
|
|
672
|
+
* every path that reaches it — exponential over recursive value rules (linking
|
|
673
|
+
* jess's SCSS grammar cost ~290G instructions). This walk enters each node
|
|
674
|
+
* once per `Encoder`.
|
|
675
|
+
*/
|
|
676
|
+
private reachesSideWriter(root: Combinator<unknown>): boolean {
|
|
677
|
+
const memo = this.sideWriterMemo
|
|
678
|
+
const known = memo.get(root)
|
|
679
|
+
if (known !== undefined) return known
|
|
680
|
+
const order = new Map<Combinator<unknown>, number>()
|
|
681
|
+
const low: number[] = []
|
|
682
|
+
const found: boolean[] = []
|
|
683
|
+
const stack: Combinator<unknown>[] = []
|
|
684
|
+
const visit = (node: Combinator<unknown>): number => {
|
|
685
|
+
const at = low.length
|
|
686
|
+
order.set(node, at)
|
|
687
|
+
low.push(at)
|
|
688
|
+
found.push(false)
|
|
689
|
+
stack.push(node)
|
|
690
|
+
const next = this.sideWriterSuccessors(node)
|
|
691
|
+
if (next === true) found[at] = true
|
|
692
|
+
else {
|
|
693
|
+
for (const child of next) {
|
|
694
|
+
const hit = memo.get(child)
|
|
695
|
+
if (hit !== undefined) {
|
|
696
|
+
if (hit) found[at] = true
|
|
697
|
+
continue
|
|
695
698
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
for (const child of childrenOf(def)) {
|
|
704
|
-
const childResult = hasSideWriter(child)
|
|
705
|
-
if (childResult.found) {
|
|
706
|
-
result = { found: true, complete: true }
|
|
707
|
-
break
|
|
708
|
-
}
|
|
709
|
-
complete &&= childResult.complete
|
|
699
|
+
const seen = order.get(child)
|
|
700
|
+
if (seen !== undefined) {
|
|
701
|
+
// Unmemoized but entered: still on the stack, so in this node's
|
|
702
|
+
// component. Its answer reaches the component's first node by the
|
|
703
|
+
// tree edges, not through here.
|
|
704
|
+
if (seen < low[at]!) low[at] = seen
|
|
705
|
+
continue
|
|
710
706
|
}
|
|
711
|
-
|
|
707
|
+
const k = visit(child)
|
|
708
|
+
if (low[k]! < low[at]!) low[at] = low[k]!
|
|
709
|
+
if (found[k]) found[at] = true
|
|
712
710
|
}
|
|
713
711
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
712
|
+
if (low[at] === at) {
|
|
713
|
+
const answer = found[at]!
|
|
714
|
+
let member: Combinator<unknown>
|
|
715
|
+
do {
|
|
716
|
+
member = stack.pop()!
|
|
717
|
+
memo.set(member, answer)
|
|
718
|
+
} while (member !== node)
|
|
719
|
+
}
|
|
720
|
+
return at
|
|
721
|
+
}
|
|
722
|
+
return found[visit(root)]!
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/** The census's edges out of `node`, or `true` when `node` is itself a writer. */
|
|
726
|
+
private sideWriterSuccessors(node: Combinator<unknown>): readonly Combinator<unknown>[] | true {
|
|
727
|
+
const def = node._def
|
|
728
|
+
switch (def.tag) {
|
|
729
|
+
case 'field':
|
|
730
|
+
case 'expect':
|
|
731
|
+
case 'recover':
|
|
732
|
+
case 'scanTo':
|
|
733
|
+
case 'unknown':
|
|
734
|
+
return true
|
|
735
|
+
case 'lazy': {
|
|
736
|
+
const name = (node as unknown as { _ruleName?: string })._ruleName
|
|
737
|
+
const winner = name === undefined ? undefined : this.winners?.[name]
|
|
738
|
+
if (winner !== undefined && !winnerWrapsReference(winner, node)) return [winner]
|
|
739
|
+
try { return [def.thunk()] }
|
|
740
|
+
catch { return true }
|
|
741
|
+
}
|
|
742
|
+
default:
|
|
743
|
+
return childrenOf(def)
|
|
717
744
|
}
|
|
718
|
-
return mayLeavePartialCapture(root, new Set(), true)
|
|
719
|
-
|| hasSideWriter(root).found
|
|
720
|
-
|| ((autoNot?.length ?? 0) > 0 && capturesLeaf(root))
|
|
721
745
|
}
|
|
722
746
|
|
|
723
747
|
/** A first set becomes a char class string, or −1 for `any`. */
|
package/src/version.ts
CHANGED