1
use conjure_core::ast::{Expression, ReturnType};
2
use conjure_core::metadata::Metadata;
3
use conjure_core::rule_engine::{
4
    register_rule, register_rule_set, ApplicationError, ApplicationResult, Reduction,
5
};
6
use conjure_core::Model;
7
use uniplate::Uniplate;
8

            
9
register_rule_set!("Bubble", 254, ("Base"));
10

            
11
// Bubble reduction rules
12

            
13
/*
14
    Reduce bubbles with a boolean expression to a conjunction with their condition.
15

            
16
    e.g. (a / b = c) @ (b != 0) => (a / b = c) & (b != 0)
17
*/
18
#[register_rule(("Bubble", 100))]
19
fn expand_bubble(expr: &Expression, _: &Model) -> ApplicationResult {
20
    match expr {
21
        Expression::Bubble(_, a, b) if a.return_type() == Some(ReturnType::Bool) => {
22
            Ok(Reduction::pure(Expression::And(
23
                Metadata::new(),
24
                vec![*a.clone(), *b.clone()],
25
            )))
26
        }
27
        _ => Err(ApplicationError::RuleNotApplicable),
28
    }
29
}
30

            
31
/*
32
    Bring bubbles with a non-boolean expression higher up the tree.
33

            
34
    E.g. ((a / b) @ (b != 0)) = c => (a / b = c) @ (b != 0)
35
*/
36
#[register_rule(("Bubble", 100))]
37
fn bubble_up(expr: &Expression, _: &Model) -> ApplicationResult {
38
    let mut sub = expr.children();
39
    let mut bubbled_conditions = vec![];
40
    for e in sub.iter_mut() {
41
        if let Expression::Bubble(_, a, b) = e {
42
            if a.return_type() != Some(ReturnType::Bool) {
43
                bubbled_conditions.push(*b.clone());
44
                *e = *a.clone();
45
            }
46
        }
47
    }
48
    if bubbled_conditions.is_empty() {
49
        return Err(ApplicationError::RuleNotApplicable);
50
    }
51
    return Ok(Reduction::pure(Expression::Bubble(
52
        Metadata::new(),
53
        Box::new(expr.with_children(sub)),
54
        Box::new(Expression::And(Metadata::new(), bubbled_conditions)),
55
    )));
56
}
57

            
58
// Bubble applications
59

            
60
/*
61
    Convert an unsafe division to a safe division with a bubble condition.
62

            
63
    Division by zero is undefined and therefore not allowed, so we add a condition to check for it.
64
    This condition is brought up the tree and expanded into a conjunction with the first boolean-type expression it is paired with.
65

            
66
    E.g. a / b => (a / b) @ (b != 0)
67

            
68
*/
69
#[register_rule(("Bubble", 100))]
70
fn div_to_bubble(expr: &Expression, _: &Model) -> ApplicationResult {
71
    if let Expression::UnsafeDiv(_, a, b) = expr {
72
        return Ok(Reduction::pure(Expression::Bubble(
73
            Metadata::new(),
74
            Box::new(Expression::SafeDiv(Metadata::new(), a.clone(), b.clone())),
75
            Box::new(Expression::Neq(
76
                Metadata::new(),
77
                b.clone(),
78
                Box::new(Expression::from(0)),
79
            )),
80
        )));
81
    }
82
    return Err(ApplicationError::RuleNotApplicable);
83
}