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
use std::collections::HashMap;

use crate::expr::{format_value, Expr};

// #insight The optimizer does not err.

// #insight
// The optimizer converts general Expr::List expressions into execution-friendly
// expressions like Expr::Array, Expr::Dict, etc. It also strips unnecessary
// annotations.

// #todo what does optimize do? I think it just removes some annotations.

pub fn optimize_fn(expr: Expr) -> Expr {
    match expr.unpack() {
        Expr::List(ref terms) => {
            if !terms.is_empty() {
                if let Expr::Symbol(s) = &terms[0].unpack() {
                    if s == "Array" {
                        let items = terms[1..].iter().map(|ax| ax.unpack().clone()).collect();
                        return Expr::maybe_annotated(Expr::Array(items), expr.annotations());
                    } else if s == "Dict" {
                        let items: Vec<Expr> =
                            terms[1..].iter().map(|ax| ax.unpack().clone()).collect();
                        let mut dict = HashMap::new();
                        for pair in items.chunks(2) {
                            let k = pair[0].clone();
                            let v = pair[1].clone();
                            dict.insert(format_value(k), v);
                        }
                        return Expr::maybe_annotated(Expr::Dict(dict), expr.annotations());
                    }
                }
            }
            expr
        }
        _ => expr,
    }
}

pub fn optimize(expr: Expr) -> Expr {
    expr.transform(&optimize_fn)
}

#[cfg(test)]
mod tests {
    use crate::{api::parse_string, optimize::optimize};

    #[test]
    fn optimize_rewrites_array_expressions() {
        let input = r#"(do (let a [1 2 3 4]) (writeln (+ 2 3)))"#;

        let expr = parse_string(input).unwrap();

        let expr_optimized = optimize(expr);

        let s = format!("{expr_optimized:?}");

        assert!(s.contains("Array([Int(1), Int(2), Int(3), Int(4)])"));
    }

    // #todo the test is flaky for some reason, temporarily disabled, investigate.
    // #[test]
    // fn optimize_rewrites_dict_expressions() {
    //     let input = r#"(let a {:name "George" :age 25})"#;

    //     let expr = parse_string(input).unwrap();

    //     let expr_optimized = optimize(expr);

    //     let s = format!("{expr_optimized:?}");

    //     assert!(s.contains(r#"Dict({"name": String("George"), "age": Int(25)})"#));
    // }
}