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

            
12
use itertools::Itertools;
13
use std::sync::Arc;
14
use uniplate::Biplate;
15

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

            
28
1530
    let mut model = model.clone();
29

            
30
    // Rewrite until there are no more rules left to apply.
31
    while let Some(()) =
32
13838
        try_rewrite_model(&mut model, &rules_grouped, prop_multiple_equally_applicable)
33
12308
    {}
34

            
35
1530
    Ok(model)
36
1530
}
37

            
38
// Tries to do a single rewrite on the model.
39
//
40
// Returns None if no change was made.
41
13838
fn try_rewrite_model(
42
13838
    model: &mut Model,
43
13838
    rules_grouped: &Vec<(u16, Vec<RuleData<'_>>)>,
44
13838
    prop_multiple_equally_applicable: bool,
45
13838
) -> Option<()> {
46
    type CtxFn = Arc<dyn Fn(Expr) -> Model>;
47
13838
    let mut results: Vec<(RuleResult<'_>, u16, Expr, CtxFn)> = vec![];
48

            
49
    // Iterate over rules by priority in descending order.
50
101133
    'top: for (priority, rules) in rules_grouped.iter() {
51
        // Using Biplate, rewrite both the expression tree, and any value lettings in the symbol
52
        // table.
53
3150270
        for (expr, ctx) in <_ as Biplate<Expr>>::contexts_bi(model) {
54
            // Clone expr and ctx so they can be reused
55
3150270
            let expr = expr.clone();
56
3150270
            let ctx = ctx.clone();
57
15562140
            for rd in rules {
58
12411870
                match (rd.rule.application)(&expr, &model.symbols()) {
59
12308
                    Ok(red) => {
60
12308
                        // Collect applicable rules
61
12308
                        results.push((
62
12308
                            RuleResult {
63
12308
                                rule_data: rd.clone(),
64
12308
                                reduction: red,
65
12308
                            },
66
12308
                            *priority,
67
12308
                            expr.clone(),
68
12308
                            ctx.clone(),
69
12308
                        ));
70
12308
                    }
71
                    Err(_) => {
72
                        // when called a lot, this becomes very expensive!
73
                        #[cfg(debug_assertions)]
74
12399562
                        tracing::trace!(
75
                            "Rule attempted but not applied: {} (priority {}, rule set {}), to expression: {}",
76
                            rd.rule.name,
77
                            priority,
78
                            rd.rule_set.name,
79
                            expr
80
                        );
81
                    }
82
                }
83
            }
84
            // This expression has the highest rule priority so far, so this is what we want to
85
            // rewrite.
86
3150270
            if !results.is_empty() {
87
12308
                break 'top;
88
3137962
            }
89
        }
90
    }
91

            
92
13838
    match results.as_slice() {
93
13838
        [] => {
94
1530
            return None;
95
        } // no rules are applicable.
96
12308
        [(result, _priority, expr, ctx), ..] => {
97
12308
            if prop_multiple_equally_applicable {
98
12308
                assert_no_multiple_equally_applicable_rules(&results, rules_grouped);
99
12308
            }
100

            
101
            // Extract the single applicable rule and apply it
102
12308
            log_rule_application(result, expr, model);
103
12308

            
104
12308
            // Replace expr with new_expression
105
12308
            *model = ctx(result.reduction.new_expression.clone());
106
12308

            
107
12308
            // Apply new symbols and top level
108
12308
            result.reduction.clone().apply(model);
109
12308

            
110
12308
            println!("{}", &model);
111
12308
        }
112
12308
    }
113
12308

            
114
12308
    Some(())
115
13838
}
116

            
117
// Exits with a bug if there are multiple equally applicable rules for an expression.
118
12308
fn assert_no_multiple_equally_applicable_rules<CtxFnType>(
119
12308
    results: &Vec<(RuleResult<'_>, u16, Expr, CtxFnType)>,
120
12308
    rules_grouped: &Vec<(u16, Vec<RuleData<'_>>)>,
121
12308
) {
122
12308
    if results.len() <= 1 {
123
12308
        return;
124
    }
125

            
126
    let names: Vec<_> = results
127
        .iter()
128
        .map(|(result, _, _, _)| result.rule_data.rule.name)
129
        .collect();
130

            
131
    // Extract the expression from the first result
132
    let expr = results[0].2.clone();
133

            
134
    // Construct a single string to display the names of the rules grouped by priority
135
    let mut rules_by_priority_string = String::new();
136
    rules_by_priority_string.push_str("Rules grouped by priority:\n");
137
    for (priority, rules) in rules_grouped.iter() {
138
        rules_by_priority_string.push_str(&format!("Priority {}:\n", priority));
139
        for rd in rules {
140
            rules_by_priority_string.push_str(&format!(
141
                "  - {} (from {})\n",
142
                rd.rule.name, rd.rule_set.name
143
            ));
144
        }
145
    }
146
    bug!("Multiple equally applicable rules for {expr}: {names:#?}\n\n{rules_by_priority_string}");
147
12308
}