1
//! These tests use a simple constant expression tree to demonstrate the use of the `gen_reduce` crate.
2

            
3
use tree_morph::{helpers::select_first, *};
4
use uniplate::derive::Uniplate;
5

            
6
#[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
7
#[uniplate()]
8
enum Expr {
9
    Add(Box<Expr>, Box<Expr>),
10
    Mul(Box<Expr>, Box<Expr>),
11
    Val(i32),
12
}
13

            
14
struct Meta {
15
    num_applications: u32,
16
}
17

            
18
enum ReductionRule {
19
    AddZero,
20
    MulOne,
21
    Eval,
22
}
23

            
24
impl Rule<Expr, Meta> for ReductionRule {
25
    fn apply(&self, cmd: &mut Commands<Expr, Meta>, expr: &Expr, _: &Meta) -> Option<Expr> {
26
        use Expr::*;
27
        use ReductionRule::*;
28

            
29
        let result = match self {
30
            AddZero => match expr {
31
                Add(a, b) if matches!(a.as_ref(), Val(0)) => Some(*b.clone()),
32
                Add(a, b) if matches!(b.as_ref(), Val(0)) => Some(*a.clone()),
33
                _ => None,
34
            },
35
            MulOne => match expr {
36
                Mul(a, b) if matches!(a.as_ref(), Val(1)) => Some(*b.clone()),
37
                Mul(a, b) if matches!(b.as_ref(), Val(1)) => Some(*a.clone()),
38
                _ => None,
39
            },
40
            Eval => match expr {
41
                Add(a, b) => match (a.as_ref(), b.as_ref()) {
42
                    (Val(x), Val(y)) => Some(Val(x + y)),
43
                    _ => None,
44
                },
45
                Mul(a, b) => match (a.as_ref(), b.as_ref()) {
46
                    (Val(x), Val(y)) => Some(Val(x * y)),
47
                    _ => None,
48
                },
49
                _ => None,
50
            },
51
        };
52

            
53
        if result.is_some() {
54
            cmd.mut_meta(|m| m.num_applications += 1);
55
        }
56
        result
57
    }
58
}
59

            
60
#[test]
61
fn test_single_var() {
62
    let expr = Expr::Val(42);
63
    let meta = Meta {
64
        num_applications: 0,
65
    };
66
    let (expr, meta) = reduce_with_rules(&[ReductionRule::Eval], select_first, expr, meta);
67
    assert_eq!(expr, Expr::Val(42));
68
    assert_eq!(meta.num_applications, 0);
69
}
70

            
71
#[test]
72
fn test_add_zero() {
73
    let expr = Expr::Add(Box::new(Expr::Val(0)), Box::new(Expr::Val(42)));
74
    let meta = Meta {
75
        num_applications: 0,
76
    };
77
    let (expr, meta) = reduce_with_rules(&[ReductionRule::AddZero], select_first, expr, meta);
78
    assert_eq!(expr, Expr::Val(42));
79
    assert_eq!(meta.num_applications, 1);
80
}
81

            
82
#[test]
83
fn test_mul_one() {
84
    let expr = Expr::Mul(Box::new(Expr::Val(1)), Box::new(Expr::Val(42)));
85
    let meta = Meta {
86
        num_applications: 0,
87
    };
88
    let (expr, meta) = reduce_with_rules(&[ReductionRule::MulOne], select_first, expr, meta);
89
    assert_eq!(expr, Expr::Val(42));
90
    assert_eq!(meta.num_applications, 1);
91
}
92

            
93
#[test]
94
fn test_eval_add() {
95
    let expr = Expr::Add(Box::new(Expr::Val(1)), Box::new(Expr::Val(2)));
96
    let meta = Meta {
97
        num_applications: 0,
98
    };
99
    let (expr, meta) = reduce_with_rules(&[ReductionRule::Eval], select_first, expr, meta);
100
    assert_eq!(expr, Expr::Val(3));
101
    assert_eq!(meta.num_applications, 1);
102
}
103

            
104
#[test]
105
fn test_eval_nested() {
106
    let expr = Expr::Mul(
107
        Box::new(Expr::Add(Box::new(Expr::Val(1)), Box::new(Expr::Val(2)))),
108
        Box::new(Expr::Val(3)),
109
    );
110
    let meta = Meta {
111
        num_applications: 0,
112
    };
113
    let (expr, meta) = reduce_with_rules(&[ReductionRule::Eval], select_first, expr, meta);
114
    assert_eq!(expr, Expr::Val(9));
115
    assert_eq!(meta.num_applications, 2);
116
}