1
//! Here we test the `reduce_with_rule_groups` function.
2
//! Each rule group is applied to the whole tree as with `reduce_with_rules`, before the next group is tried.
3
//! Every time a change is made, the algorithm starts again with the first group.
4
//!
5
//! This lets us make powerful "evaluation" rules which greedily reduce the tree as much as possible, before other
6
//! "rewriting" rules are applied.
7

            
8
use tree_morph::{helpers::select_first, *};
9
use uniplate::derive::Uniplate;
10

            
11
/// A simple language of two literals and a wrapper
12
#[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
13
#[uniplate()]
14
enum Expr {
15
    A,               // a
16
    B,               // b
17
    Wrap(Box<Expr>), // [E]
18
}
19

            
20
/// Rule container: holds a primitive function and implements the Rule trait
21
struct Rl(fn(&Expr) -> Option<Expr>);
22

            
23
impl Rule<Expr, ()> for Rl {
24
12
    fn apply(&self, cmd: &mut Commands<Expr, ()>, expr: &Expr, _: &()) -> Option<Expr> {
25
12
        self.0(expr)
26
12
    }
27
}
28

            
29
mod rules {
30
    use super::*;
31

            
32
    /// [a] ~> a
33
5
    pub fn unwrap_a(expr: &Expr) -> Option<Expr> {
34
5
        if let Expr::Wrap(inner) = expr {
35
2
            if let Expr::A = **inner {
36
1
                return Some(Expr::A);
37
1
            }
38
3
        }
39
4
        None
40
5
    }
41

            
42
    /// a ~> b
43
7
    pub fn a_to_b(expr: &Expr) -> Option<Expr> {
44
7
        if let Expr::A = expr {
45
2
            return Some(Expr::B);
46
5
        }
47
5
        None
48
7
    }
49
}
50

            
51
#[test]
52
1
fn test_same_group() {
53
1
    // If the rules are in the same group, unwrap_a will apply higher in the tree
54
1

            
55
1
    // [a]
56
1
    let expr = Expr::Wrap(Box::new(Expr::A));
57
1

            
58
1
    let (expr, _) = reduce_with_rule_groups(
59
1
        &[&[Rl(rules::unwrap_a), Rl(rules::a_to_b)]],
60
1
        select_first,
61
1
        expr,
62
1
        (),
63
1
    );
64
1

            
65
1
    // [a] ~> a ~> b
66
1
    assert_eq!(expr, Expr::B);
67
1
}
68

            
69
#[test]
70
1
fn test_a_to_b_first() {
71
1
    // a_to_b is in a higher group than unwrap_a, so it will be applied first to the lower expression
72
1

            
73
1
    // [a]
74
1
    let expr = Expr::Wrap(Box::new(Expr::A));
75
1

            
76
1
    let (expr, _) = reduce_with_rule_groups(
77
1
        &[&[Rl(rules::a_to_b)], &[Rl(rules::unwrap_a)]],
78
1
        select_first,
79
1
        expr,
80
1
        (),
81
1
    );
82
1

            
83
1
    // [a] ~> [b]
84
1
    assert_eq!(expr, Expr::Wrap(Box::new(Expr::B)));
85
1
}