1
#![allow(dead_code)]
2
use conjure_cp::ast::eval::vec_op;
3
use conjure_cp::ast::{
4
    AbstractLiteral, Atom, Expression as Expr, Literal, Metadata, SymbolTable, eval_constant,
5
    run_partial_evaluator,
6
};
7
use conjure_cp::rule_engine::{
8
    ApplicationError::RuleNotApplicable, ApplicationResult, Reduction, register_rule,
9
    register_rule_set,
10
};
11
use std::sync::Arc;
12
use std::sync::atomic::{AtomicBool, Ordering};
13
use uniplate::Biplate;
14

            
15
register_rule_set!("Constant", ());
16

            
17
/// Constant-folds `expr` unless doing so would inline a referenced matrix literal.
18
4175265
fn fold_constant_expression(expr: &Expr) -> Option<Expr> {
19
4175265
    let constant = eval_constant(expr)?;
20

            
21
350760
    if matches!(
22
351820
        (expr, &constant),
23
        (
24
            Expr::Atomic(_, Atom::Reference(_)),
25
            Literal::AbstractLiteral(AbstractLiteral::Matrix(_, _))
26
        )
27
    ) {
28
1060
        return None;
29
350760
    }
30

            
31
350760
    Some(Expr::Atomic(Metadata::new(), Atom::Literal(constant)))
32
4175265
}
33

            
34
#[register_rule("Base", 9000)]
35
2520406
fn partial_evaluator(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
36
2520406
    run_partial_evaluator(expr)
37
2520406
}
38

            
39
#[register_rule("Constant", 9001, [Root])]
40
2542293
fn constant_evaluator(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
41
    // I break the rules a bit here: this is a global rule on roots.
42
    //
43
    // This rule is really really hot when expanding comprehensions.. Also, at time of writing, we
44
    // have the naive rewriter, which is slow on large trees....
45
    //
46
    // Also, constant_evaluating bottom up vs top down does things in less passes: the rewriter,
47
    // however, favour doing this top-down!
48
    //
49
    // e.g. or([(1=1),(2=2),(3+3 = 6)])
50
    //
51
    // We also reuse it as a plain expression simplifier when rewriting value lettings so shared
52
    // declaration pointers observe the rewritten letting body.
53
374374
    match expr {
54
        Expr::Root(_, _) => {
55
77827
            let has_changed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
56
77827
            let has_changed_2 = Arc::clone(&has_changed);
57

            
58
4283153
            let new_expr = expr.transform_bi(&move |x| {
59
2041733
                if matches!(
60
2241420
                    x,
61
                    Expr::Atomic(_, Atom::Literal(_)) | Expr::Atomic(_, Atom::Reference(_))
62
                ) {
63
2241420
                    return x;
64
2041733
                }
65

            
66
2041733
                match fold_constant_expression(&x)
67
2041733
                    .or_else(|| run_partial_evaluator(&x).ok().map(|r| r.new_expression))
68
                {
69
21447
                    Some(new_expr) => {
70
21447
                        has_changed.store(true, Ordering::Relaxed);
71
21447
                        new_expr
72
                    }
73

            
74
2020286
                    None => x,
75
                }
76
4283153
            });
77

            
78
77827
            if has_changed_2.load(Ordering::Relaxed) {
79
13195
                Ok(Reduction::pure(new_expr))
80
            } else {
81
64632
                Err(RuleNotApplicable)
82
            }
83
        }
84
        Expr::AbstractLiteral(_, _)
85
        | Expr::Atomic(_, Atom::Literal(conjure_cp::ast::Literal::AbstractLiteral(_))) => {
86
330934
            Err(RuleNotApplicable)
87
        }
88
2133532
        _ => match fold_constant_expression(expr)
89
2133532
            .or_else(|| run_partial_evaluator(expr).ok().map(|r| r.new_expression))
90
        {
91
340004
            Some(new_expr) if &new_expr != expr => Ok(Reduction::pure(new_expr)),
92
2132868
            _ => Err(RuleNotApplicable),
93
        },
94
    }
95
2542293
}
96

            
97
/// Evaluate the root expression.
98
///
99
/// This returns either Expr::Root([true]) or Expr::Root([false]).
100
#[register_rule("Constant", 9001, [Root])]
101
2542293
fn eval_root(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
102
    // this is its own rule not part of apply_eval_constant, because root should return a new root
103
    // with a literal inside it, not just a literal
104

            
105
2542293
    let Expr::Root(_, exprs) = expr else {
106
2464466
        return Err(RuleNotApplicable);
107
    };
108

            
109
77827
    match exprs.len() {
110
746
        0 => Ok(Reduction::pure(Expr::Root(
111
746
            Metadata::new(),
112
746
            vec![true.into()],
113
746
        ))),
114
24637
        1 => Err(RuleNotApplicable),
115
        _ => {
116
24
            let lit =
117
52444
                vec_op::<bool, bool>(|e| e.iter().all(|&e| e), exprs).ok_or(RuleNotApplicable)?;
118

            
119
24
            Ok(Reduction::pure(Expr::Root(
120
24
                Metadata::new(),
121
24
                vec![lit.into()],
122
24
            )))
123
        }
124
    }
125
2542293
}