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
635012
pub fn as_bottom_up(
10
635012
    rule: impl Fn(&Expression, &SymbolTable) -> ApplicationResult,
11
635012
) -> impl Fn(&Expression, &SymbolTable) -> ApplicationResult {
12
635012
    Box::new(move |expr: &Expression, symbols: &SymbolTable| {
13
        // global rule
14
635012
        if !matches!(expr, Expression::Root(_, _)) {
15
610428
            return Err(RuleNotApplicable);
16
24584
        };
17

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

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

            
25
79142
        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
652243
            if zipper.go_right().is_some() {
32
573101
                while zipper.go_down().is_some() {}
33
298415
            } else if zipper.go_up().is_none() {
34
                // cannot go up anymore, at the root
35
24584
                break;
36
273831
            }
37

            
38
627659
            let expr = zipper.focus();
39

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

            
48
24584
        let root_expr = zipper.rebuild_root();
49

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