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
11
    fn apply(&self, cmd: &mut Commands<Expr, Meta>, expr: &Expr, _: &Meta) -> Option<Expr> {
26
        use Expr::*;
27
        use ReductionRule::*;
28

            
29
11
        let result = match self {
30
            AddZero => match expr {
31
1
                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
1
                _ => None,
34
            },
35
            MulOne => match expr {
36
1
                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
1
                _ => None,
39
            },
40
7
            Eval => match expr {
41
2
                Add(a, b) => match (a.as_ref(), b.as_ref()) {
42
2
                    (Val(x), Val(y)) => Some(Val(x + y)),
43
                    _ => None,
44
                },
45
2
                Mul(a, b) => match (a.as_ref(), b.as_ref()) {
46
1
                    (Val(x), Val(y)) => Some(Val(x * y)),
47
1
                    _ => None,
48
                },
49
3
                _ => None,
50
            },
51
        };
52

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

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

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

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

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

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