Skip to content

Commit d313582

Browse files
feat[expr]: add back the expression folder, implemented over Node (#4230)
Adds back a Expression Folder --------- Signed-off-by: Joe Isaacs <[email protected]>
1 parent 5b99dec commit d313582

File tree

11 files changed

+292
-13
lines changed

11 files changed

+292
-13
lines changed

vortex-expr/src/exprs/dynamic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use vortex_error::{VortexExpect, VortexResult, vortex_bail};
1414
use vortex_proto::expr as pb;
1515
use vortex_scalar::{Scalar, ScalarValue};
1616

17-
use crate::traversal::{Node, NodeVisitor, TraversalOrder};
17+
use crate::traversal::{NodeExt, NodeVisitor, TraversalOrder};
1818
use crate::{
1919
AnalysisExpr, ExprEncodingRef, ExprId, ExprRef, IntoExpr, Scope, StatsCatalog, VTable, vtable,
2020
};

vortex-expr/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use vortex_utils::aliases::hash_set::HashSet;
5151
pub use vtable::*;
5252

5353
use crate::dyn_traits::DynEq;
54-
use crate::traversal::{Node, ReferenceCollector};
54+
use crate::traversal::{NodeExt, ReferenceCollector};
5555

5656
pub trait IntoExpr {
5757
/// Convert this type into an expression reference.

vortex-expr/src/transform/annotations.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use vortex_utils::aliases::hash_map::HashMap;
88
use vortex_utils::aliases::hash_set::HashSet;
99

1010
use crate::ExprRef;
11-
use crate::traversal::{Node, NodeVisitor, TraversalOrder};
11+
use crate::traversal::{NodeExt, NodeVisitor, TraversalOrder};
1212

1313
pub trait Annotation: Clone + Hash + Eq {}
1414

vortex-expr/src/transform/partition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::transform::annotations::{
1212
Annotation, AnnotationFn, Annotations, descendent_annotations,
1313
};
1414
use crate::transform::simplify_typed::simplify_typed;
15-
use crate::traversal::{Node, NodeRewriter, Transformed, TraversalOrder};
15+
use crate::traversal::{NodeExt, NodeRewriter, Transformed, TraversalOrder};
1616
use crate::{ExprRef, get_item, pack, root};
1717

1818
/// Partition an expression into sub-expressions that are uniquely associated with an annotation.

vortex-expr/src/transform/remove_merge.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use vortex_error::{VortexExpect, VortexResult, vortex_err};
55

6-
use crate::traversal::{Node, Transformed};
6+
use crate::traversal::{NodeExt, Transformed};
77
use crate::{DType, ExprRef, MergeVTable, get_item, pack};
88

99
/// Replaces [crate::MergeExpr] with combination of [crate::GetItem] and [crate::Pack] expressions.

vortex-expr/src/transform/remove_select.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use vortex_error::{VortexResult, vortex_err};
55

6-
use crate::traversal::{Node, Transformed};
6+
use crate::traversal::{NodeExt, Transformed};
77
use crate::{DType, ExprRef, SelectVTable, get_item, pack};
88

99
/// Replaces [crate::SelectExpr] with combination of [crate::GetItem] and [crate::Pack] expressions.

vortex-expr/src/transform/replace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use vortex_dtype::{Nullability, StructFields};
55
use vortex_error::{VortexExpect, VortexResult};
66

7-
use crate::traversal::{Node, Transformed};
7+
use crate::traversal::{NodeExt, Transformed};
88
use crate::{ExprRef, col, pack, root};
99

1010
/// Replaces all occurrences of `needle` in the expression `expr` with `replacement`.

vortex-expr/src/transform/simplify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use vortex_error::VortexResult;
55

66
use crate::transform::match_between::find_between;
77
// use crate::transform::match_between::find_between;
8-
use crate::traversal::{Node, Transformed};
8+
use crate::traversal::{NodeExt, Transformed};
99
use crate::{ExprRef, GetItemVTable, PackVTable};
1010

1111
/// Simplifies an expression into an equivalent expression which is faster and easier to analyze.

vortex-expr/src/traversal/fold.rs

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_error::VortexResult;
5+
6+
use crate::traversal::Node;
7+
8+
pub enum FoldDownContext<C, R> {
9+
Continue(C),
10+
Stop(R),
11+
Skip(R),
12+
}
13+
14+
pub enum FoldDown<R> {
15+
Continue,
16+
Stop(R),
17+
Skip(R),
18+
}
19+
20+
pub enum FoldUp<R> {
21+
Continue(R),
22+
Stop(R),
23+
}
24+
25+
impl<R> FoldUp<R> {
26+
pub fn value(self) -> R {
27+
match self {
28+
Self::Continue(r) => r,
29+
Self::Stop(r) => r,
30+
}
31+
}
32+
}
33+
34+
pub trait NodeFolderContext {
35+
type NodeTy: Node;
36+
type Result;
37+
type Context;
38+
39+
/// visit_down is called when a node is first encountered, in a pre-order traversal.
40+
/// If the node's children are to be skipped, return Skip.
41+
/// If the node should stop traversal, return Stop.
42+
/// Otherwise, return Continue.
43+
fn visit_down(
44+
&mut self,
45+
_ctx: &Self::Context,
46+
_node: &Self::NodeTy,
47+
) -> VortexResult<FoldDownContext<Self::Context, Self::Result>>;
48+
49+
/// visit_up is called when a node is last encountered, in a pre-order traversal.
50+
/// If the node should stop traversal, return Stop.
51+
/// Otherwise, return Continue.
52+
fn visit_up(
53+
&mut self,
54+
_node: Self::NodeTy,
55+
_context: &Self::Context,
56+
_children: Vec<Self::Result>,
57+
) -> VortexResult<FoldUp<Self::Result>>;
58+
}
59+
60+
pub trait NodeFolder {
61+
type NodeTy: Node;
62+
type Result;
63+
64+
/// visit_down is called when a node is first encountered, in a pre-order traversal.
65+
/// If the node's children are to be skipped, return Skip.
66+
/// If the node should stop traversal, return Stop.
67+
/// Otherwise, return Continue.
68+
fn visit_down(&mut self, _node: &Self::NodeTy) -> VortexResult<FoldDown<Self::Result>>;
69+
70+
/// visit_up is called when a node is last encountered, in a pre-order traversal.
71+
/// If the node should stop traversal, return Stop.
72+
/// Otherwise, return Continue.
73+
fn visit_up(
74+
&mut self,
75+
_node: Self::NodeTy,
76+
_children: Vec<Self::Result>,
77+
) -> VortexResult<FoldUp<Self::Result>>;
78+
}
79+
80+
pub struct NodeFolderContextWrapper<'a, T>
81+
where
82+
T: NodeFolder,
83+
{
84+
pub inner: &'a mut T,
85+
}
86+
87+
impl<T: NodeFolder> NodeFolderContext for NodeFolderContextWrapper<'_, T> {
88+
type NodeTy = T::NodeTy;
89+
type Result = T::Result;
90+
type Context = ();
91+
92+
fn visit_down(
93+
&mut self,
94+
_ctx: &Self::Context,
95+
_node: &Self::NodeTy,
96+
) -> VortexResult<FoldDownContext<Self::Context, Self::Result>> {
97+
match self.inner.visit_down(_node)? {
98+
FoldDown::Continue => Ok(FoldDownContext::Continue(())),
99+
FoldDown::Stop(r) => Ok(FoldDownContext::Stop(r)),
100+
FoldDown::Skip(r) => Ok(FoldDownContext::Skip(r)),
101+
}
102+
}
103+
104+
fn visit_up(
105+
&mut self,
106+
_node: Self::NodeTy,
107+
_context: &Self::Context,
108+
_children: Vec<Self::Result>,
109+
) -> VortexResult<FoldUp<Self::Result>> {
110+
self.inner.visit_up(_node, _children)
111+
}
112+
}
113+
114+
#[cfg(test)]
115+
mod tests {
116+
use vortex_error::VortexExpect;
117+
118+
use super::*;
119+
use crate::traversal::NodeExt;
120+
use crate::{
121+
BinaryVTable, ExprRef, LiteralVTable, Operator, checked_add, gt, lit, vortex_bail,
122+
};
123+
124+
struct AddFold;
125+
impl NodeFolder for AddFold {
126+
type NodeTy = ExprRef;
127+
type Result = i32;
128+
129+
fn visit_down(&mut self, node: &'_ Self::NodeTy) -> VortexResult<FoldDown<Self::Result>> {
130+
if let Some(lit) = node.as_opt::<LiteralVTable>() {
131+
let v = lit
132+
.value()
133+
.as_primitive()
134+
.typed_value::<i32>()
135+
.vortex_expect("i32");
136+
137+
if v == 5 {
138+
return Ok(FoldDown::Stop(5));
139+
}
140+
}
141+
142+
if let Some(binary) = node.as_opt::<BinaryVTable>()
143+
&& binary.op() == Operator::Gt
144+
{
145+
return Ok(FoldDown::Skip(0));
146+
}
147+
148+
Ok(FoldDown::Continue)
149+
}
150+
151+
fn visit_up(
152+
&mut self,
153+
node: Self::NodeTy,
154+
children: Vec<Self::Result>,
155+
) -> VortexResult<FoldUp<Self::Result>> {
156+
if let Some(lit) = node.as_opt::<LiteralVTable>() {
157+
let v = lit
158+
.value()
159+
.as_primitive()
160+
.typed_value::<i32>()
161+
.vortex_expect("i32");
162+
Ok(FoldUp::Continue(v))
163+
} else if let Some(binary) = node.as_opt::<BinaryVTable>() {
164+
if binary.op() == Operator::Add {
165+
Ok(FoldUp::Continue(children[0] + children[1]))
166+
} else {
167+
vortex_bail!("not a valid operator")
168+
}
169+
} else {
170+
vortex_bail!("not a valid type")
171+
}
172+
}
173+
}
174+
175+
#[test]
176+
fn test_fold() {
177+
let expr = checked_add(checked_add(lit(1), lit(2)), lit(3));
178+
179+
let mut folder = AddFold;
180+
let result = expr.fold(&mut folder).unwrap().value();
181+
assert_eq!(result, 6);
182+
}
183+
184+
#[test]
185+
fn test_stop_value() {
186+
let expr = checked_add(checked_add(lit(1), lit(5)), lit(3));
187+
188+
let mut folder = AddFold;
189+
let result = expr.fold(&mut folder).unwrap().value();
190+
assert_eq!(result, 5);
191+
}
192+
193+
#[test]
194+
fn test_skip_value() {
195+
let expr = checked_add(gt(lit(1), lit(2)), lit(3));
196+
197+
let mut folder = AddFold;
198+
let result = expr.fold(&mut folder).unwrap().value();
199+
assert_eq!(result, 3);
200+
}
201+
202+
#[test]
203+
fn test_control_flow_value() {
204+
let expr = checked_add(gt(lit(1), lit(5)), lit(3));
205+
206+
let mut folder = AddFold;
207+
let result = expr.fold(&mut folder).unwrap().value();
208+
assert_eq!(result, 3);
209+
}
210+
}

0 commit comments

Comments
 (0)