1
use conjure_cp::rule_engine::SubmodelZipper;
2
use conjure_cp::{
3
    ast::{Expression, SymbolTable},
4
    rule_engine::{ApplicationError::RuleNotApplicable, ApplicationResult, Reduction},
5
};
6

            
7
/// Converts the rule function `rule` to a rule that applies `rule` bottom-up to every expression
8
/// in the current sub-model.
9
pub fn as_bottom_up(
10
    rule: impl Fn(&Expression, &SymbolTable) -> ApplicationResult,
11
) -> impl Fn(&Expression, &SymbolTable) -> ApplicationResult {
12
    Box::new(move |expr: &Expression, symbols: &SymbolTable| {
13
        // global rule
14
        if !matches!(expr, Expression::Root(_, _)) {
15
            return Err(RuleNotApplicable);
16
        };
17

            
18
        // traverse bottom up within the current sub-model, applying the rule.
19
        let mut symbols = symbols.clone();
20
        let mut new_tops = vec![];
21
        let mut done_something = false;
22

            
23
        let mut zipper = SubmodelZipper::new(expr.clone());
24

            
25
        while zipper.go_down().is_some() {}
26

            
27
        loop {
28
            // go right and to the bottom of that subtree
29
            //
30
            // once we have ran out of siblings, go_up.
31
            if zipper.go_right().is_some() {
32
                while zipper.go_down().is_some() {}
33
            } else if zipper.go_up().is_none() {
34
                // cannot go up anymore, at the root
35
                break;
36
            }
37

            
38
            let expr = zipper.focus();
39

            
40
            if let Ok(mut reduction) = rule(expr, &symbols) {
41
                zipper.replace_focus(reduction.new_expression);
42
                symbols.extend(reduction.symbols);
43
                new_tops.append(&mut reduction.new_top);
44
                done_something = true;
45
            }
46
        }
47

            
48
        let root_expr = zipper.rebuild_root();
49

            
50
        if done_something {
51
            Ok(Reduction::new(root_expr, new_tops, symbols))
52
        } else {
53
            Err(RuleNotApplicable)
54
        }
55
    })
56
}