mod expr1 { use std::fmt::Display; use recursion::*; use recursion_visualize::visualize::*; #[derive(Debug, PartialEq, Eq)] pub enum Expr { Add(Box, Box), Sub(Box, Box), Mul(Box, Box), LiteralInt(i64), } impl Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Expr::Add(a, b) => write!(f, "{} + {}", a, b), Expr::Sub(a, b) => write!(f, "{} - {}", a, b), Expr::Mul(a, b) => write!(f, "{} * {}", a, b), Expr::LiteralInt(x) => write!(f, "{}", x), } } } pub fn add(a: Expr, b: Expr) -> Expr { Expr::Add(Box::new(a), Box::new(b)) } pub fn subtract(a: Expr, b: Expr) -> Expr { Expr::Sub(Box::new(a), Box::new(b)) } pub fn multiply(a: Expr, b: Expr) -> Expr { Expr::Mul(Box::new(a), Box::new(b)) } pub fn literal(n: i64) -> Expr { Expr::LiteralInt(n) } pub enum ExprFrame { Add(A, A), Sub(A, A), Mul(A, A), LiteralInt(i64), } impl Display for ExprFrame<()> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ExprFrame::Add(_, _) => write!(f, "_ + _",), ExprFrame::Sub(_, _) => write!(f, "_ - _",), ExprFrame::Mul(_, _) => write!(f, "_ * _",), ExprFrame::LiteralInt(x) => write!(f, "{}", x), } } } impl MappableFrame for ExprFrame { type Frame = ExprFrame; fn map_frame(input: Self::Frame, mut f: impl FnMut(A) -> B) -> Self::Frame { match input { ExprFrame::Add(a, b) => ExprFrame::Add(f(a), f(b)), ExprFrame::Sub(a, b) => ExprFrame::Sub(f(a), f(b)), ExprFrame::Mul(a, b) => ExprFrame::Mul(f(a), f(b)), ExprFrame::LiteralInt(x) => ExprFrame::LiteralInt(x), } } } impl<'a> Collapsible for &'a Expr { type FrameToken = ExprFrame; fn into_frame(self) -> ::Frame { match self { Expr::Add(a, b) => ExprFrame::Add(a, b), Expr::Sub(a, b) => ExprFrame::Sub(a, b), Expr::Mul(a, b) => ExprFrame::Mul(a, b), Expr::LiteralInt(x) => ExprFrame::LiteralInt(*x), } } } impl Expandable for Expr { type FrameToken = ExprFrame; fn from_frame(val: ::Frame) -> Self { match val { ExprFrame::Add(a, b) => Expr::Add(Box::new(a), Box::new(b)), ExprFrame::Sub(a, b) => Expr::Sub(Box::new(a), Box::new(b)), ExprFrame::Mul(a, b) => Expr::Mul(Box::new(a), Box::new(b)), ExprFrame::LiteralInt(x) => Expr::LiteralInt(x), } } } pub fn eval(e: &Expr) -> (i64, Viz) { e.collapse_frames_v(|frame| match frame { ExprFrame::Add(a, b) => a + b, ExprFrame::Sub(a, b) => a - b, ExprFrame::Mul(a, b) => a * b, ExprFrame::LiteralInt(x) => x, }) } pub fn build_expr(depth: usize) -> (Expr, Viz) { Expr::expand_frames_v(depth, |depth| { if depth > 0 { ExprFrame::Add(depth - 1, depth - 1) } else { ExprFrame::LiteralInt(1) } }) } } mod expr2 { use std::fmt::Display; use recursion::*; use recursion_visualize::visualize::{CollapsibleVizExt, Viz}; pub enum Expr { Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), LiteralInt(i64), } impl Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Expr::Add(a, b) => write!(f, "{} + {}", a, b), Expr::Sub(a, b) => write!(f, "{} - {}", a, b), Expr::Mul(a, b) => write!(f, "{} * {}", a, b), Expr::Div(a, b) => write!(f, "{} / {}", a, b), Expr::LiteralInt(x) => write!(f, "{}", x), } } } pub fn add(a: Expr, b: Expr) -> Expr { Expr::Add(Box::new(a), Box::new(b)) } pub fn subtract(a: Expr, b: Expr) -> Expr { Expr::Sub(Box::new(a), Box::new(b)) } pub fn multiply(a: Expr, b: Expr) -> Expr { Expr::Mul(Box::new(a), Box::new(b)) } pub fn divide(a: Expr, b: Expr) -> Expr { Expr::Div(Box::new(a), Box::new(b)) } pub fn literal(n: i64) -> Expr { Expr::LiteralInt(n) } pub enum ExprFrame { Add(A, A), Sub(A, A), Mul(A, A), Div(A, A), LiteralInt(i64), } impl Display for ExprFrame<()> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ExprFrame::Add(_, _) => write!(f, "_ + _",), ExprFrame::Sub(_, _) => write!(f, "_ - _",), ExprFrame::Mul(_, _) => write!(f, "_ * _",), ExprFrame::Div(_, _) => write!(f, "_ / _",), ExprFrame::LiteralInt(x) => write!(f, "{}", x), } } } impl MappableFrame for ExprFrame { type Frame = ExprFrame; fn map_frame(input: Self::Frame, mut f: impl FnMut(A) -> B) -> Self::Frame { match input { ExprFrame::Add(a, b) => ExprFrame::Add(f(a), f(b)), ExprFrame::Sub(a, b) => ExprFrame::Sub(f(a), f(b)), ExprFrame::Mul(a, b) => ExprFrame::Mul(f(a), f(b)), ExprFrame::Div(a, b) => ExprFrame::Div(f(a), f(b)), ExprFrame::LiteralInt(x) => ExprFrame::LiteralInt(x), } } } impl<'a> Collapsible for &'a Expr { type FrameToken = ExprFrame; fn into_frame(self) -> ::Frame { match self { Expr::Add(a, b) => ExprFrame::Add(a, b), Expr::Sub(a, b) => ExprFrame::Sub(a, b), Expr::Mul(a, b) => ExprFrame::Mul(a, b), Expr::Div(a, b) => ExprFrame::Div(a, b), Expr::LiteralInt(x) => ExprFrame::LiteralInt(*x), } } } pub fn try_eval(e: &Expr) -> (Result, Viz) { e.try_collapse_frames_v(|frame| match frame { ExprFrame::Add(a, b) => Ok(a + b), ExprFrame::Sub(a, b) => Ok(a - b), ExprFrame::Mul(a, b) => Ok(a * b), ExprFrame::Div(a, b) => { if b == 0 { Err("cannot divide by zero") } else { Ok(a / b) } } ExprFrame::LiteralInt(x) => Ok(x), }) } } fn main() { { use expr1::*; let expr = multiply(subtract(literal(1), literal(2)), literal(3)); let (evaluated, viz) = eval(&expr); assert_eq!(evaluated, -3); viz.label("Evaluate Expr".to_string(), "(1 - 2) * 3".to_string()) .write("eval.html".to_string()); let (built_expr, viz) = build_expr(2); let expected = add(add(literal(1), literal(1)), add(literal(1), literal(1))); assert_eq!(built_expr, expected); viz.label("Build Expr".to_string(), "1 + 1 + 1 + 1".to_string()) .write("build_expr.html".to_string()); } { use expr2::*; let valid_expr = divide(subtract(literal(1), literal(7)), literal(3)); let invalid_expr = divide(multiply(literal(2), literal(3)), literal(0)); let (valid_res, valid_viz) = try_eval(&valid_expr); let (invalid_res, invalid_viz) = try_eval(&invalid_expr); assert_eq!(valid_res, Ok(-2)); assert_eq!(invalid_res, Err("cannot divide by zero")); valid_viz .label("Try Eval Valid Expr".to_string(), "(1 - 7) / 3".to_string()) .write("try_eval_valid.html".to_string()); invalid_viz .label("Try Eval Expr".to_string(), "(2 * 3) / 0".to_string()) .write("try_eval_invalid.html".to_string()); } }