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

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

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

            
25
47179
        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
385118
            if zipper.go_right().is_some() {
32
337939
                while zipper.go_down().is_some() {}
33
175048
            } else if zipper.go_up().is_none() {
34
                // cannot go up anymore, at the root
35
14617
                break;
36
160431
            }
37

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

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

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

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