1
use std::collections::HashMap;
2

            
3
use tracing::info;
4
use tracing_subscriber;
5
use tree_morph::prelude::*;
6
use tree_morph_macros::named_rule;
7
use uniplate::Uniplate;
8

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

            
17
#[named_rule("Eval Add")]
18
7
fn rule_eval_add(cmd: &mut Commands<Expr, Meta>, expr: &Expr, _: &Meta) -> Option<Expr> {
19
7
    match expr {
20
4
        Expr::Add(a, b) => match (a.as_ref(), b.as_ref()) {
21
2
            (Expr::Val(x), Expr::Val(y)) => {
22
2
                cmd.mut_meta(Box::new(|m: &mut Meta| m.num_applications += 1));
23
2
                Some(Expr::Val(x + y))
24
            }
25
2
            _ => None,
26
        },
27
3
        _ => None,
28
    }
29
7
}
30

            
31
#[named_rule("Eval Mul")]
32
7
fn rule_eval_mul(cmd: &mut Commands<Expr, Meta>, expr: &Expr, _: &Meta) -> Option<Expr> {
33
7
    match expr {
34
1
        Expr::Mul(a, b) => match (a.as_ref(), b.as_ref()) {
35
1
            (Expr::Val(x), Expr::Val(y)) => {
36
1
                cmd.mut_meta(Box::new(|m: &mut Meta| m.num_applications += 1));
37
1
                Some(Expr::Val(x * y))
38
            }
39
            _ => None,
40
        },
41
6
        _ => None,
42
    }
43
7
}
44

            
45
#[derive(Debug)]
46
struct Meta {
47
    num_applications: u32,
48
    attempted: HashMap<String, usize>,
49
    applicable: HashMap<String, usize>,
50
}
51

            
52
#[test]
53
1
fn left_branch_clean() {
54
    // Initialize tracing subscriber to see trace output
55
1
    let _ = tracing_subscriber::fmt()
56
1
        .with_max_level(tracing::Level::TRACE)
57
1
        .try_init();
58

            
59
    // Top Level is +
60
    // Left Branch has two Nested Subtractions which do not have any rules
61
    // So atoms
62
    // Right branch has Mul and Add which DO have rules
63
1
    let expr = Expr::Add(
64
1
        Box::new(Expr::Add(
65
1
            Box::new(Expr::Val(1)),
66
1
            Box::new(Expr::Val(1)),
67
            // Box::new(Expr::Sub(Box::new(Expr::Val(1)), Box::new(Expr::Val(2)))),
68
            // Box::new(Expr::Sub(Box::new(Expr::Val(3)), Box::new(Expr::Val(10)))),
69
1
        )),
70
1
        Box::new(Expr::Mul(Box::new(Expr::Val(10)), Box::new(Expr::Val(5)))),
71
1
    );
72

            
73
1
    let meta = Meta {
74
1
        num_applications: 0,
75
1
        attempted: HashMap::new(),
76
1
        applicable: HashMap::new(),
77
1
    };
78

            
79
1
    let mut engine = EngineBuilder::new()
80
1
        .add_rule_group(vec![rule_eval_add, rule_eval_mul])
81
14
        .add_before_rule(|_, meta, rule| {
82
14
            meta.num_applications += 1;
83
14
            let attempt = meta.attempted.entry(rule.name().to_owned()).or_insert(0);
84
14
            *attempt += 1
85
14
        })
86
14
        .add_after_rule(|_, meta, rule, success| {
87
14
            if success {
88
3
                let attempt = meta.applicable.entry(rule.name().to_owned()).or_insert(0);
89
3
                *attempt += 1
90
11
            }
91
14
        })
92
3
        .add_after_apply(|_, _, rule| info!("FROM TESTING: APPLYING RULE '{}'", rule.name()))
93
1
        .build();
94
1
    let (expr, meta) = engine.morph(expr, meta);
95

            
96
2
    for name in meta.attempted.keys() {
97
2
        let attempted = *meta.attempted.get(name).unwrap();
98
2
        let applicable = *meta.applicable.get(name).unwrap_or(&0);
99
2
        println!(
100
            "Rule: {}: {}/{} ({:.2}%)",
101
            name,
102
            applicable,
103
            attempted,
104
2
            applicable / attempted
105
        )
106
    }
107

            
108
1
    dbg!(expr);
109
1
}