1
use super::{resolve_rules::RuleData, RewriteError, RuleSet};
2
use crate::{
3
    ast::{Expression as Expr, SubModel},
4
    bug,
5
    rule_engine::{
6
        get_rules_grouped,
7
        rewriter_common::{log_rule_application, RuleResult},
8
        submodel_zipper::submodel_ctx,
9
    },
10
    Model,
11
};
12

            
13
use itertools::Itertools;
14
use std::sync::Arc;
15
use tracing::trace;
16
use uniplate::Biplate;
17

            
18
/// A naive, exhaustive rewriter for development purposes. Applies rules in priority order,
19
/// favouring expressions found earlier during preorder traversal of the tree.
20
1818
pub fn rewrite_naive<'a>(
21
1818
    model: &Model,
22
1818
    rule_sets: &Vec<&'a RuleSet<'a>>,
23
1818
    prop_multiple_equally_applicable: bool,
24
1818
) -> Result<Model, RewriteError> {
25
1818
    let rules_grouped = get_rules_grouped(rule_sets)
26
        .unwrap_or_else(|_| bug!("get_rule_priorities() failed!"))
27
1818
        .into_iter()
28
1818
        .collect_vec();
29
1818

            
30
1818
    let mut model = model.clone();
31
1818
    let mut done_something = true;
32
1818

            
33
1818
    trace!(
34
        target: "rule_engine_human",
35
1782
        "Model before rewriting:\n\n{}\n--\n",
36
        model
37
    );
38

            
39
    // Rewrite until there are no more rules left to apply.
40
21420
    while done_something {
41
19602
        let mut new_model = None;
42
19602
        done_something = false;
43

            
44
        // Rewrite each sub-model in the tree, largest first.
45
19602
        for (mut submodel, ctx) in <_ as Biplate<SubModel>>::contexts_bi(&model) {
46
19602
            if try_rewrite_model(
47
19602
                &mut submodel,
48
19602
                &rules_grouped,
49
19602
                prop_multiple_equally_applicable,
50
19602
            )
51
19602
            .is_some()
52
            {
53
17784
                new_model = Some(ctx(submodel));
54
17784
                done_something = true;
55
17784
                break;
56
1818
            }
57
        }
58
19602
        if let Some(new_model) = new_model {
59
17784
            model = new_model;
60
17784
        }
61
    }
62

            
63
1818
    trace!(
64
        target: "rule_engine_human",
65
1782
        "Final model:\n\n{}",
66
        model
67
    );
68
1818
    Ok(model)
69
1818
}
70

            
71
// Tries to do a single rewrite on the model.
72
//
73
// Returns None if no change was made.
74
19602
fn try_rewrite_model(
75
19602
    submodel: &mut SubModel,
76
19602
    rules_grouped: &Vec<(u16, Vec<RuleData<'_>>)>,
77
19602
    prop_multiple_equally_applicable: bool,
78
19602
) -> Option<()> {
79
    type CtxFn = Arc<dyn Fn(Expr) -> SubModel>;
80
19602
    let mut results: Vec<(RuleResult<'_>, u16, Expr, CtxFn)> = vec![];
81

            
82
    // Iterate over rules by priority in descending order.
83
137196
    'top: for (priority, rules) in rules_grouped.iter() {
84
        // Using Biplate, rewrite both the expression tree, and any value lettings in the symbol
85
        // table.
86
4583628
        for (expr, ctx) in submodel_ctx(submodel.clone()) {
87
            // Clone expr and ctx so they can be reused
88
4583628
            let expr = expr.clone();
89
4583628
            let ctx = ctx.clone();
90
23208966
            for rd in rules {
91
18625338
                match (rd.rule.application)(&expr, &submodel.symbols()) {
92
17784
                    Ok(red) => {
93
17784
                        // Collect applicable rules
94
17784
                        results.push((
95
17784
                            RuleResult {
96
17784
                                rule_data: rd.clone(),
97
17784
                                reduction: red,
98
17784
                            },
99
17784
                            *priority,
100
17784
                            expr.clone(),
101
17784
                            ctx.clone(),
102
17784
                        ));
103
17784
                    }
104
                    Err(_) => {
105
                        // when called a lot, this becomes very expensive!
106
                        #[cfg(debug_assertions)]
107
18607554
                        tracing::trace!(
108
                            "Rule attempted but not applied: {} (priority {}, rule set {}), to expression: {}",
109
                            rd.rule.name,
110
                            priority,
111
                            rd.rule_set.name,
112
                            expr
113
                        );
114
                    }
115
                }
116
            }
117
            // This expression has the highest rule priority so far, so this is what we want to
118
            // rewrite.
119
4583628
            if !results.is_empty() {
120
17784
                break 'top;
121
4565844
            }
122
        }
123
    }
124

            
125
19602
    match results.as_slice() {
126
19602
        [] => {
127
1818
            return None;
128
        } // no rules are applicable.
129
17784
        [(result, _priority, expr, ctx), ..] => {
130
17784
            if prop_multiple_equally_applicable {
131
144
                assert_no_multiple_equally_applicable_rules(&results, rules_grouped);
132
17640
            }
133

            
134
            // Extract the single applicable rule and apply it
135
17784
            log_rule_application(result, expr, submodel);
136
17784

            
137
17784
            // Replace expr with new_expression
138
17784
            *submodel = ctx(result.reduction.new_expression.clone());
139
17784

            
140
17784
            // Apply new symbols and top level
141
17784
            result.reduction.clone().apply(submodel);
142
17784
        }
143
17784
    }
144
17784

            
145
17784
    Some(())
146
19602
}
147

            
148
// Exits with a bug if there are multiple equally applicable rules for an expression.
149
144
fn assert_no_multiple_equally_applicable_rules<CtxFnType>(
150
144
    results: &Vec<(RuleResult<'_>, u16, Expr, CtxFnType)>,
151
144
    rules_grouped: &Vec<(u16, Vec<RuleData<'_>>)>,
152
144
) {
153
144
    if results.len() <= 1 {
154
144
        return;
155
    }
156

            
157
    let names: Vec<_> = results
158
        .iter()
159
        .map(|(result, _, _, _)| result.rule_data.rule.name)
160
        .collect();
161

            
162
    // Extract the expression from the first result
163
    let expr = results[0].2.clone();
164

            
165
    // Construct a single string to display the names of the rules grouped by priority
166
    let mut rules_by_priority_string = String::new();
167
    rules_by_priority_string.push_str("Rules grouped by priority:\n");
168
    for (priority, rules) in rules_grouped.iter() {
169
        rules_by_priority_string.push_str(&format!("Priority {}:\n", priority));
170
        for rd in rules {
171
            rules_by_priority_string.push_str(&format!(
172
                "  - {} (from {})\n",
173
                rd.rule.name, rd.rule_set.name
174
            ));
175
        }
176
    }
177
    bug!("Multiple equally applicable rules for {expr}: {names:#?}\n\n{rules_by_priority_string}");
178
144
}