Skip to content

Commit c333e51

Browse files
committed
chore(query): combine walks into walk bottomup/preorder functions
1 parent 266cbb1 commit c333e51

File tree

3 files changed

+75
-67
lines changed

3 files changed

+75
-67
lines changed

pkg/query/advisor.go

Lines changed: 28 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -58,64 +58,43 @@ func ApplyAdvisor(co CanonicalOutline, advisor PlanAdvisor) (CanonicalOutline, e
5858
}, nil
5959
}
6060

61-
// applyAdvisorMutations recursively walks the outline tree bottom-up.
61+
// applyAdvisorMutations walks the outline tree bottom-up via WalkOutlineBottomUp.
6262
// For each node it calls GetMutations on the advisor and applies the returned
6363
// mutations in sequence. After each mutation it verifies that the resulting
6464
// node's ID matches the original node's ID; a mismatch is a programmer bug.
6565
func applyAdvisorMutations(outline Outline, co CanonicalOutline, advisor PlanAdvisor) (Outline, error) {
66-
// Recurse into children first (bottom-up).
67-
if len(outline.SubOutlines) > 0 {
68-
newSubs := make([]Outline, len(outline.SubOutlines))
69-
for i, sub := range outline.SubOutlines {
70-
mutated, err := applyAdvisorMutations(sub, co, advisor)
71-
if err != nil {
72-
return Outline{}, err
73-
}
74-
newSubs[i] = mutated
75-
}
76-
outline = Outline{
77-
Type: outline.Type,
78-
Args: outline.Args,
79-
SubOutlines: newSubs,
80-
ID: outline.ID,
66+
return WalkOutlineBottomUp(outline, func(node Outline) (Outline, error) {
67+
mutations, err := advisor.GetMutations(node, co)
68+
if err != nil {
69+
return Outline{}, err
8170
}
82-
}
83-
84-
mutations, err := advisor.GetMutations(outline, co)
85-
if err != nil {
86-
return Outline{}, err
87-
}
88-
89-
result := outline
90-
for _, fn := range mutations {
91-
next := fn(result)
92-
if next.ID != result.ID {
93-
return Outline{}, spiceerrors.MustBugf(
94-
"advisor mutation changed outline node ID from %d to %d; mutations must preserve the root node ID",
95-
result.ID, next.ID,
96-
)
71+
result := node
72+
for _, fn := range mutations {
73+
next := fn(result)
74+
if next.ID != result.ID {
75+
return Outline{}, spiceerrors.MustBugf(
76+
"advisor mutation changed outline node ID from %d to %d; mutations must preserve the root node ID",
77+
result.ID, next.ID,
78+
)
79+
}
80+
result = next
9781
}
98-
result = next
99-
}
100-
return result, nil
82+
return result, nil
83+
})
10184
}
10285

103-
// collectAdvisorHints recursively walks the outline tree and calls GetHints on
104-
// the advisor for each node, storing any returned hints in the hints map keyed
105-
// by the node's ID.
86+
// collectAdvisorHints walks the outline tree pre-order via WalkOutlinePreOrder,
87+
// calling GetHints on the advisor for each node and storing any returned hints
88+
// in the hints map keyed by the node's ID.
10689
func collectAdvisorHints(outline Outline, co CanonicalOutline, advisor PlanAdvisor, hints map[OutlineNodeID][]Hint) error {
107-
nodeHints, err := advisor.GetHints(outline, co)
108-
if err != nil {
109-
return err
110-
}
111-
if len(nodeHints) > 0 {
112-
hints[outline.ID] = nodeHints
113-
}
114-
115-
for _, sub := range outline.SubOutlines {
116-
if err := collectAdvisorHints(sub, co, advisor, hints); err != nil {
90+
return WalkOutlinePreOrder(outline, func(node Outline) error {
91+
nodeHints, err := advisor.GetHints(node, co)
92+
if err != nil {
11793
return err
11894
}
119-
}
120-
return nil
95+
if len(nodeHints) > 0 {
96+
hints[node.ID] = nodeHints
97+
}
98+
return nil
99+
})
121100
}

pkg/query/mutations.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,14 @@ type OutlineMutation func(Outline) Outline
77
// MutateOutline performs a bottom-up traversal of the outline tree, applying
88
// all the given transformation functions to each node after processing its children.
99
func MutateOutline(outline Outline, fns []OutlineMutation) Outline {
10-
// Recurse on children first (bottom-up)
11-
if len(outline.SubOutlines) > 0 {
12-
newSubs := make([]Outline, len(outline.SubOutlines))
13-
for i, sub := range outline.SubOutlines {
14-
newSubs[i] = MutateOutline(sub, fns)
10+
// WalkOutlineBottomUp cannot fail when the callback never returns an error,
11+
// so the error return is safe to ignore here.
12+
result, _ := WalkOutlineBottomUp(outline, func(node Outline) (Outline, error) {
13+
for _, fn := range fns {
14+
node = fn(node)
1515
}
16-
outline = Outline{
17-
Type: outline.Type,
18-
Args: outline.Args,
19-
SubOutlines: newSubs,
20-
ID: outline.ID,
21-
}
22-
}
23-
24-
// Then apply all mutation functions in sequence to current node
25-
result := outline
26-
for _, fn := range fns {
27-
result = fn(result)
28-
}
16+
return node, nil
17+
})
2918
return result
3019
}
3120

pkg/query/outline.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,46 @@ type Outline struct {
7575
ID OutlineNodeID // Populated only inside a CanonicalOutline
7676
}
7777

78+
// WalkOutlineBottomUp performs a bottom-up traversal of an Outline tree,
79+
// rebuilding each node with its processed children before passing it to fn.
80+
// fn receives the node (with already-processed children) and returns a
81+
// replacement node and an optional error. The traversal stops and the error
82+
// is propagated on the first failure.
83+
func WalkOutlineBottomUp(outline Outline, fn func(Outline) (Outline, error)) (Outline, error) {
84+
if len(outline.SubOutlines) > 0 {
85+
newSubs := make([]Outline, len(outline.SubOutlines))
86+
for i, sub := range outline.SubOutlines {
87+
processed, err := WalkOutlineBottomUp(sub, fn)
88+
if err != nil {
89+
return Outline{}, err
90+
}
91+
newSubs[i] = processed
92+
}
93+
outline = Outline{
94+
Type: outline.Type,
95+
Args: outline.Args,
96+
SubOutlines: newSubs,
97+
ID: outline.ID,
98+
}
99+
}
100+
return fn(outline)
101+
}
102+
103+
// WalkOutlinePreOrder performs a pre-order traversal of an Outline tree,
104+
// calling fn on each node before visiting its children. fn returns an error
105+
// to abort the traversal early.
106+
func WalkOutlinePreOrder(outline Outline, fn func(Outline) error) error {
107+
if err := fn(outline); err != nil {
108+
return err
109+
}
110+
for _, sub := range outline.SubOutlines {
111+
if err := WalkOutlinePreOrder(sub, fn); err != nil {
112+
return err
113+
}
114+
}
115+
return nil
116+
}
117+
78118
// CanonicalOutline is an Outline tree that has been fully canonicalized.
79119
// It pairs the root Outline (with every node's ID assigned) with a map from
80120
// those IDs to their CanonicalKeys. Only CanonicalOutlines can be Compiled,

0 commit comments

Comments
 (0)