1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use super::Environment;
use ast::*;
use std::fmt::Debug;
use tokenizer::Operator;

#[allow(unused)]
pub fn optimize<
    'a,
    NumEnum: 'a + Send + Sync,
    StrEnum: 'a + Send + Sync + Debug + PartialEq,
    FilterEnum: 'a + Send + Sync,
    Env: Environment<'a, NumEnum, StrEnum, FilterEnum>,
>(
    ast: Vec<Expr<'a>>,
    env: &'a Env,
) -> Vec<Expr<'a>> {
    ast.into_iter()
        .map(|tree| optimize_tree(tree, env, 20))
        .map(|tree| optimize_tree(tree, env, 20))
        .fold(Vec::new(), |mut acc, v| {
            if let Some(t) = acc.pop() {
                match (t, v) {
                    (Expr::Raw(raw_str), Expr::StringLiteral(lit)) => {
                        acc.push(Expr::StringLiteral((raw_str.to_string() + &lit).into()));
                    }
                    (Expr::StringLiteral(lit1), Expr::StringLiteral(lit2)) => {
                        acc.push(Expr::StringLiteral((lit1.to_string() + &lit2).into()));
                    }
                    (Expr::StringLiteral(lit), Expr::Raw(raw_str)) => {
                        acc.push(Expr::StringLiteral((lit.to_string() + raw_str).into()));
                    }
                    (t, v) => {
                        acc.push(t);
                        acc.push(v);
                    }
                }
            } else {
                acc.push(v);
            }
            acc
        })
}

pub fn optimize_tree<
    'a,
    NumEnum: 'a + Send + Sync,
    StrEnum: 'a + Send + Sync + Debug + PartialEq,
    FilterEnum: 'a + Send + Sync,
    Env: Environment<'a, NumEnum, StrEnum, FilterEnum>,
>(
    tree: Expr<'a>,
    env: &'a Env,
    effort: u32,
) -> Expr<'a> {
    if effort == 0 {
        return tree;
    }
    let effort = effort - 1;
    match tree {
        Expr::Identifier(id) => {
            if let Some(val) = env.num_constant(id) {
                Expr::Numeric(Numeric::Raw(val))
            } else if let Some(val) = env.str_constant(id) {
                Expr::StringLiteral(val)
            } else {
                Expr::Identifier(id)
            }
        }
        Expr::Numeric(Numeric::Raw(val)) => Expr::StringLiteral(val.to_string().into()),
        Expr::Numeric(numeric) => Expr::Numeric(optimize_numeric(numeric, env, effort)),
        Expr::Filter(id, expr, args) => {
            let expr = optimize_tree(*expr, env, effort);
            Expr::Filter(id, Box::new(expr), args)
        }
        expr => expr,
    }
}

pub fn optimize_numeric<
    'a,
    NumEnum: 'a + Send + Sync,
    StrEnum: 'a + Send + Sync + Debug + PartialEq,
    FilterEnum: 'a + Send + Sync,
    Env: Environment<'a, NumEnum, StrEnum, FilterEnum>,
>(
    numeric: Numeric<'a>,
    env: &'a Env,
    effort: u32,
) -> Numeric<'a> {
    if effort == 0 {
        return numeric;
    }
    let effort = effort - 1;
    match numeric {
        Numeric::Identifier(id) => {
            if let Some(val) = env.num_constant(id) {
                Numeric::Raw(val)
            } else {
                Numeric::Identifier(id)
            }
        }
        Numeric::Binary(op, left, right) => {
            let left = optimize_numeric(*left, env, effort);
            let right = optimize_numeric(*right, env, effort);
            match (op, left, right) {
                (Operator::Plus, Numeric::Raw(left), Numeric::Raw(right)) => {
                    Numeric::Raw(left + right)
                }
                (Operator::Dash, Numeric::Raw(left), Numeric::Raw(right)) => {
                    Numeric::Raw(left - right)
                }
                (Operator::Slash, Numeric::Raw(left), Numeric::Raw(right)) => {
                    Numeric::Raw(left / right)
                }
                (Operator::Asterisk, Numeric::Raw(left), Numeric::Raw(right)) => {
                    Numeric::Raw(left * right)
                }
                (op, left, right) => Numeric::Binary(op, Box::new(left), Box::new(right)),
            }
        }
        Numeric::Negate(expr) => {
            let expr = optimize_numeric(*expr, env, effort);
            match expr {
                Numeric::Raw(val) => Numeric::Raw(-val),
                expr => Numeric::Negate(Box::new(expr)),
            }
        }
        Numeric::Parentheses(expr) => {
            let expr = optimize_numeric(*expr, env, effort);
            match expr {
                Numeric::Raw(val) => Numeric::Raw(val),
                expr => Numeric::Parentheses(Box::new(expr)),
            }
        }
        Numeric::Raw(raw) => Numeric::Raw(raw),
    }
}