expr/
expr.rs

1mod expr1 {
2    use std::fmt::Display;
3
4    use recursion::*;
5    use recursion_visualize::visualize::*;
6    #[derive(Debug, PartialEq, Eq)]
7    pub enum Expr {
8        Add(Box<Expr>, Box<Expr>),
9        Sub(Box<Expr>, Box<Expr>),
10        Mul(Box<Expr>, Box<Expr>),
11        LiteralInt(i64),
12    }
13    impl Display for Expr {
14        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15            match self {
16                Expr::Add(a, b) => write!(f, "{} + {}", a, b),
17                Expr::Sub(a, b) => write!(f, "{} - {}", a, b),
18                Expr::Mul(a, b) => write!(f, "{} * {}", a, b),
19                Expr::LiteralInt(x) => write!(f, "{}", x),
20            }
21        }
22    }
23
24    pub fn add(a: Expr, b: Expr) -> Expr {
25        Expr::Add(Box::new(a), Box::new(b))
26    }
27    pub fn subtract(a: Expr, b: Expr) -> Expr {
28        Expr::Sub(Box::new(a), Box::new(b))
29    }
30    pub fn multiply(a: Expr, b: Expr) -> Expr {
31        Expr::Mul(Box::new(a), Box::new(b))
32    }
33    pub fn literal(n: i64) -> Expr {
34        Expr::LiteralInt(n)
35    }
36    pub enum ExprFrame<A> {
37        Add(A, A),
38        Sub(A, A),
39        Mul(A, A),
40        LiteralInt(i64),
41    }
42
43    impl Display for ExprFrame<()> {
44        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45            match self {
46                ExprFrame::Add(_, _) => write!(f, "_ + _",),
47                ExprFrame::Sub(_, _) => write!(f, "_ - _",),
48                ExprFrame::Mul(_, _) => write!(f, "_ * _",),
49                ExprFrame::LiteralInt(x) => write!(f, "{}", x),
50            }
51        }
52    }
53
54    impl MappableFrame for ExprFrame<PartiallyApplied> {
55        type Frame<X> = ExprFrame<X>;
56        fn map_frame<A, B>(input: Self::Frame<A>, mut f: impl FnMut(A) -> B) -> Self::Frame<B> {
57            match input {
58                ExprFrame::Add(a, b) => ExprFrame::Add(f(a), f(b)),
59                ExprFrame::Sub(a, b) => ExprFrame::Sub(f(a), f(b)),
60                ExprFrame::Mul(a, b) => ExprFrame::Mul(f(a), f(b)),
61                ExprFrame::LiteralInt(x) => ExprFrame::LiteralInt(x),
62            }
63        }
64    }
65    impl<'a> Collapsible for &'a Expr {
66        type FrameToken = ExprFrame<PartiallyApplied>;
67        fn into_frame(self) -> <Self::FrameToken as MappableFrame>::Frame<Self> {
68            match self {
69                Expr::Add(a, b) => ExprFrame::Add(a, b),
70                Expr::Sub(a, b) => ExprFrame::Sub(a, b),
71                Expr::Mul(a, b) => ExprFrame::Mul(a, b),
72                Expr::LiteralInt(x) => ExprFrame::LiteralInt(*x),
73            }
74        }
75    }
76    impl Expandable for Expr {
77        type FrameToken = ExprFrame<PartiallyApplied>;
78        fn from_frame(val: <Self::FrameToken as MappableFrame>::Frame<Self>) -> Self {
79            match val {
80                ExprFrame::Add(a, b) => Expr::Add(Box::new(a), Box::new(b)),
81                ExprFrame::Sub(a, b) => Expr::Sub(Box::new(a), Box::new(b)),
82                ExprFrame::Mul(a, b) => Expr::Mul(Box::new(a), Box::new(b)),
83                ExprFrame::LiteralInt(x) => Expr::LiteralInt(x),
84            }
85        }
86    }
87
88    pub fn eval(e: &Expr) -> (i64, Viz) {
89        e.collapse_frames_v(|frame| match frame {
90            ExprFrame::Add(a, b) => a + b,
91            ExprFrame::Sub(a, b) => a - b,
92            ExprFrame::Mul(a, b) => a * b,
93            ExprFrame::LiteralInt(x) => x,
94        })
95    }
96
97    pub fn build_expr(depth: usize) -> (Expr, Viz) {
98        Expr::expand_frames_v(depth, |depth| {
99            if depth > 0 {
100                ExprFrame::Add(depth - 1, depth - 1)
101            } else {
102                ExprFrame::LiteralInt(1)
103            }
104        })
105    }
106}
107
108mod expr2 {
109    use std::fmt::Display;
110
111    use recursion::*;
112    use recursion_visualize::visualize::{CollapsibleVizExt, Viz};
113    pub enum Expr {
114        Add(Box<Expr>, Box<Expr>),
115        Sub(Box<Expr>, Box<Expr>),
116        Mul(Box<Expr>, Box<Expr>),
117        Div(Box<Expr>, Box<Expr>),
118        LiteralInt(i64),
119    }
120    impl Display for Expr {
121        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122            match self {
123                Expr::Add(a, b) => write!(f, "{} + {}", a, b),
124                Expr::Sub(a, b) => write!(f, "{} - {}", a, b),
125                Expr::Mul(a, b) => write!(f, "{} * {}", a, b),
126                Expr::Div(a, b) => write!(f, "{} / {}", a, b),
127                Expr::LiteralInt(x) => write!(f, "{}", x),
128            }
129        }
130    }
131    pub fn add(a: Expr, b: Expr) -> Expr {
132        Expr::Add(Box::new(a), Box::new(b))
133    }
134    pub fn subtract(a: Expr, b: Expr) -> Expr {
135        Expr::Sub(Box::new(a), Box::new(b))
136    }
137    pub fn multiply(a: Expr, b: Expr) -> Expr {
138        Expr::Mul(Box::new(a), Box::new(b))
139    }
140    pub fn divide(a: Expr, b: Expr) -> Expr {
141        Expr::Div(Box::new(a), Box::new(b))
142    }
143    pub fn literal(n: i64) -> Expr {
144        Expr::LiteralInt(n)
145    }
146    pub enum ExprFrame<A> {
147        Add(A, A),
148        Sub(A, A),
149        Mul(A, A),
150        Div(A, A),
151        LiteralInt(i64),
152    }
153    impl Display for ExprFrame<()> {
154        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155            match self {
156                ExprFrame::Add(_, _) => write!(f, "_ + _",),
157                ExprFrame::Sub(_, _) => write!(f, "_ - _",),
158                ExprFrame::Mul(_, _) => write!(f, "_ * _",),
159                ExprFrame::Div(_, _) => write!(f, "_ / _",),
160                ExprFrame::LiteralInt(x) => write!(f, "{}", x),
161            }
162        }
163    }
164    impl MappableFrame for ExprFrame<PartiallyApplied> {
165        type Frame<X> = ExprFrame<X>;
166        fn map_frame<A, B>(input: Self::Frame<A>, mut f: impl FnMut(A) -> B) -> Self::Frame<B> {
167            match input {
168                ExprFrame::Add(a, b) => ExprFrame::Add(f(a), f(b)),
169                ExprFrame::Sub(a, b) => ExprFrame::Sub(f(a), f(b)),
170                ExprFrame::Mul(a, b) => ExprFrame::Mul(f(a), f(b)),
171                ExprFrame::Div(a, b) => ExprFrame::Div(f(a), f(b)),
172                ExprFrame::LiteralInt(x) => ExprFrame::LiteralInt(x),
173            }
174        }
175    }
176    impl<'a> Collapsible for &'a Expr {
177        type FrameToken = ExprFrame<PartiallyApplied>;
178        fn into_frame(self) -> <Self::FrameToken as MappableFrame>::Frame<Self> {
179            match self {
180                Expr::Add(a, b) => ExprFrame::Add(a, b),
181                Expr::Sub(a, b) => ExprFrame::Sub(a, b),
182                Expr::Mul(a, b) => ExprFrame::Mul(a, b),
183                Expr::Div(a, b) => ExprFrame::Div(a, b),
184                Expr::LiteralInt(x) => ExprFrame::LiteralInt(*x),
185            }
186        }
187    }
188
189    pub fn try_eval(e: &Expr) -> (Result<i64, &str>, Viz) {
190        e.try_collapse_frames_v(|frame| match frame {
191            ExprFrame::Add(a, b) => Ok(a + b),
192            ExprFrame::Sub(a, b) => Ok(a - b),
193            ExprFrame::Mul(a, b) => Ok(a * b),
194            ExprFrame::Div(a, b) => {
195                if b == 0 {
196                    Err("cannot divide by zero")
197                } else {
198                    Ok(a / b)
199                }
200            }
201            ExprFrame::LiteralInt(x) => Ok(x),
202        })
203    }
204}
205
206fn main() {
207    {
208        use expr1::*;
209        let expr = multiply(subtract(literal(1), literal(2)), literal(3));
210
211        let (evaluated, viz) = eval(&expr);
212        assert_eq!(evaluated, -3);
213
214        viz.label("Evaluate Expr".to_string(), "(1 - 2) * 3".to_string())
215            .write("eval.html".to_string());
216
217        let (built_expr, viz) = build_expr(2);
218        let expected = add(add(literal(1), literal(1)), add(literal(1), literal(1)));
219
220        assert_eq!(built_expr, expected);
221
222        viz.label("Build Expr".to_string(), "1 + 1 + 1 + 1".to_string())
223            .write("build_expr.html".to_string());
224    }
225
226    {
227        use expr2::*;
228
229        let valid_expr = divide(subtract(literal(1), literal(7)), literal(3));
230        let invalid_expr = divide(multiply(literal(2), literal(3)), literal(0));
231
232        let (valid_res, valid_viz) = try_eval(&valid_expr);
233        let (invalid_res, invalid_viz) = try_eval(&invalid_expr);
234
235        assert_eq!(valid_res, Ok(-2));
236        assert_eq!(invalid_res, Err("cannot divide by zero"));
237
238        valid_viz
239            .label("Try Eval Valid Expr".to_string(), "(1 - 7) / 3".to_string())
240            .write("try_eval_valid.html".to_string());
241        invalid_viz
242            .label("Try Eval Expr".to_string(), "(2 * 3) / 0".to_string())
243            .write("try_eval_invalid.html".to_string());
244    }
245}