Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Expression evaluation

Evaluate an AST bottom-up. vec_fold gives finalize access to the node and all child results — needed when different node types combine children differently.

#![allow(unused)]
fn main() {
//! Expression evaluation — AST fold with heterogeneous node types.

#[cfg(test)]
mod tests {
    use hylic::prelude::vec_fold::{vec_fold, VecHeap};
    use hylic::prelude::*;
    use insta::assert_snapshot;


    /// An arithmetic expression tree.
    /// Each variant defines both its meaning and its children.
    #[derive(Clone)]
    enum Expr {
        Num(f64),
        Add(Box<Expr>, Box<Expr>),
        Mul(Box<Expr>, Box<Expr>),
        Neg(Box<Expr>),
    }

    /// Convenience constructors for readable test data.
    fn num(v: f64) -> Expr { Expr::Num(v) }
    fn add(a: Expr, b: Expr) -> Expr { Expr::Add(Box::new(a), Box::new(b)) }
    fn mul(a: Expr, b: Expr) -> Expr { Expr::Mul(Box::new(a), Box::new(b)) }
    fn neg(a: Expr) -> Expr { Expr::Neg(Box::new(a)) }

    #[test]
    fn evaluate_expression() {
        let expr = mul(add(num(3.0), num(4.0)), neg(num(2.0)));

        let graph: Treeish<Expr> = treeish_visit(|e: &Expr, cb: &mut dyn FnMut(&Expr)| {
            match e {
                Expr::Num(_) => {}
                Expr::Add(a, b) | Expr::Mul(a, b) => { cb(a); cb(b); }
                Expr::Neg(a) => { cb(a); }
            }
        });

        // vec_fold collects children before finalize, so each variant can
        // combine its results differently (sum / product / negate).
        let eval: Fold<Expr, VecHeap<Expr, f64>, f64> = vec_fold(
            |heap: &VecHeap<Expr, f64>| match &heap.node {
                Expr::Num(v)        => *v,
                Expr::Add(_, _)     => heap.childresults.iter().sum(),
                Expr::Mul(_, _)     => heap.childresults.iter().product(),
                Expr::Neg(_)        => -heap.childresults[0],
            },
        );

        let result: f64 = FUSED.run(&eval, &graph, &expr);
        assert_eq!(result, -14.0);

        assert_snapshot!("expr_eval", format!("(3 + 4) * -(2) = {result}"));
    }
}
}

Output:

(3 + 4) * -(2) = -14