tan/optimize.rs
1use std::collections::HashMap;
2
3use crate::expr::{format_value, Expr};
4
5// #insight The optimizer does not err.
6
7// #insight
8// The optimizer converts general Expr::List expressions into execution-friendly
9// expressions like Expr::Array, Expr::Map, etc. It also strips unnecessary
10// annotations.
11
12// #todo What does optimize do? I think it just removes some annotations.
13
14// #todo #think hm, we NEED the annotations, especially in let expressions!
15
16// #todo combine in one pass with e.g. check?
17
18pub fn optimize_fn(expr: Expr) -> Expr {
19 // #todo let annotations are lost here.
20 match expr.unpack() {
21 Expr::List(ref terms) => {
22 if !terms.is_empty() {
23 if let Expr::Symbol(s) = &terms[0].unpack() {
24 if s == "Array" {
25 // #todo we loose support for (Array ...)
26 let items: Vec<Expr> =
27 terms[1..].iter().map(|ax| ax.unpack().clone()).collect();
28 return Expr::maybe_annotated(Expr::array(items), expr.annotations());
29 } else if s == "Map" {
30 // #todo we loose support for (Map ...)
31 let items: Vec<Expr> =
32 terms[1..].iter().map(|ax| ax.unpack().clone()).collect();
33 let mut map = HashMap::new();
34 for pair in items.chunks(2) {
35 let mut k = format_value(&pair[0]);
36 let v = pair[1].clone();
37 // #insight
38 // Map key inference:
39 // (let name "George" role :admin)
40 // (let user {_ name _ role})
41 // user ; => {:name "George" :role :admin}
42 // #todo should move to another place.
43 // #todo move inference to parser?
44 // #insight here it handles both {...} and (Map ...)
45 if k == "_" {
46 if let Expr::Symbol(sym) = &v {
47 k.clone_from(sym);
48 }
49 // #todo report error/warning if we cannot infere!
50 }
51 map.insert(k, v);
52 }
53 return Expr::maybe_annotated(Expr::map(map), expr.annotations());
54 }
55 }
56 }
57 // #insight no annotations stripped.
58 expr
59 }
60 _ => expr,
61 }
62}
63
64pub fn optimize(expr: Expr) -> Expr {
65 expr.transform(&optimize_fn)
66}
67
68#[cfg(test)]
69mod tests {
70 use crate::{api::parse_string, optimize::optimize};
71
72 #[test]
73 fn optimize_rewrites_array_expressions() {
74 let input = r#"(do (let a [1 2 3 4]) (writeln (+ 2 3)))"#;
75
76 let expr = parse_string(input).unwrap();
77
78 let expr_optimized = optimize(expr);
79
80 let s = format!("{expr_optimized:?}");
81
82 // #todo This is a _very_ unstable/error-prone check.
83 assert!(s.contains("Array([Int(1), Int(2), Int(3), Int(4)])"));
84 }
85
86 // #todo the test is flaky for some reason, temporarily disabled, investigate.
87 // #[test]
88 // fn optimize_rewrites_map_expressions() {
89 // let input = r#"(let a {:name "George" :age 25})"#;
90
91 // let expr = parse_string(input).unwrap();
92
93 // let expr_optimized = optimize(expr);
94
95 // let s = format!("{expr_optimized:?}");
96
97 // assert!(s.contains(r#"Map({"name": String("George"), "age": Int(25)})"#));
98 // }
99}