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

            
3
use tree_morph::prelude::*;
4
use uniplate::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
fn rule_eval_add(_: &mut Commands<Expr, Meta>, expr: &Expr, _: &Meta) -> Option<Expr> {
15
    match expr {
16
        Expr::Add(a, b) => match (a.as_ref(), b.as_ref()) {
17
            (Expr::Val(x), Expr::Val(y)) => Some(Expr::Val(x + y)),
18
            _ => None,
19
        },
20
        _ => None,
21
    }
22
}
23

            
24
fn rule_eval_mul(_: &mut Commands<Expr, Meta>, expr: &Expr, _: &Meta) -> Option<Expr> {
25
    match expr {
26
        Expr::Mul(a, b) => match (a.as_ref(), b.as_ref()) {
27
            (Expr::Val(x), Expr::Val(y)) => Some(Expr::Val(x * y)),
28
            _ => None,
29
        },
30
        _ => None,
31
    }
32
}
33

            
34
enum MyRule {
35
    EvalAdd,
36
    EvalMul,
37
}
38

            
39
impl Rule<Expr, Meta> for MyRule {
40
    fn apply(&self, cmd: &mut Commands<Expr, Meta>, expr: &Expr, meta: &Meta) -> Option<Expr> {
41
        cmd.mut_meta(Box::new(|m: &mut Meta| m.num_applications += 1)); // Only applied if successful
42
        match self {
43
            MyRule::EvalAdd => rule_eval_add(cmd, expr, meta),
44
            MyRule::EvalMul => rule_eval_mul(cmd, expr, meta),
45
        }
46
    }
47
}
48
struct Meta {
49
    num_applications: u32,
50
}
51

            
52
#[test]
53
fn single_var() {
54
    let expr = Expr::Val(42);
55
    let meta = Meta {
56
        num_applications: 0,
57
    };
58

            
59
    let engine = EngineBuilder::new()
60
        .add_rule_group(vec![MyRule::EvalAdd, MyRule::EvalMul])
61
        .build();
62
    let (expr, meta) = engine.morph(expr, meta);
63

            
64
    assert_eq!(expr, Expr::Val(42));
65
    assert_eq!(meta.num_applications, 0);
66
}
67

            
68
#[test]
69
fn add_zero() {
70
    let expr = Expr::Add(Box::new(Expr::Val(0)), Box::new(Expr::Val(42)));
71
    let meta = Meta {
72
        num_applications: 0,
73
    };
74

            
75
    let engine = EngineBuilder::new()
76
        .add_rule_group(vec![MyRule::EvalAdd, MyRule::EvalMul])
77
        .build();
78
    let (expr, meta) = engine.morph(expr, meta);
79

            
80
    assert_eq!(expr, Expr::Val(42));
81
    assert_eq!(meta.num_applications, 1);
82
}
83

            
84
#[test]
85
fn mul_one() {
86
    let expr = Expr::Mul(Box::new(Expr::Val(1)), Box::new(Expr::Val(42)));
87
    let meta = Meta {
88
        num_applications: 0,
89
    };
90

            
91
    let engine = EngineBuilder::new()
92
        .add_rule_group(vec![MyRule::EvalAdd, MyRule::EvalMul])
93
        .build();
94
    let (expr, meta) = engine.morph(expr, meta);
95

            
96
    assert_eq!(expr, Expr::Val(42));
97
    assert_eq!(meta.num_applications, 1);
98
}
99

            
100
#[test]
101
fn eval_add() {
102
    let expr = Expr::Add(Box::new(Expr::Val(1)), Box::new(Expr::Val(2)));
103
    let meta = Meta {
104
        num_applications: 0,
105
    };
106

            
107
    let engine = EngineBuilder::new()
108
        .add_rule_group(vec![MyRule::EvalAdd, MyRule::EvalMul])
109
        .build();
110
    let (expr, meta) = engine.morph(expr, meta);
111

            
112
    assert_eq!(expr, Expr::Val(3));
113
    assert_eq!(meta.num_applications, 1);
114
}
115

            
116
#[test]
117
fn eval_nested() {
118
    let expr = Expr::Mul(
119
        Box::new(Expr::Add(Box::new(Expr::Val(1)), Box::new(Expr::Val(2)))),
120
        Box::new(Expr::Val(3)),
121
    );
122
    let meta = Meta {
123
        num_applications: 0,
124
    };
125

            
126
    let engine = EngineBuilder::new()
127
        .add_rule_group(vec![MyRule::EvalAdd, MyRule::EvalMul])
128
        .build();
129
    let (expr, meta) = engine.morph(expr, meta);
130

            
131
    assert_eq!(expr, Expr::Val(9));
132
    assert_eq!(meta.num_applications, 2);
133
}