Skip to main content

ravenrs_sheet/calc/eval/
lambda.rs

1use super::MAX_LAMBDA_CALLS;
2use super::array::{broadcast_len, single};
3use super::{Array, CellError, Ev, Evaluator, Expr, Lambda, MAX_CELLS, Value, err};
4use std::sync::Arc;
5
6impl<'h> Evaluator<'h> {
7    pub fn apply_lambda(&mut self, l: &Lambda, args: &[Expr]) -> Ev {
8        // Too many or too few argument positions is #VALUE!. An explicitly empty position, as in
9        // `(1,)`, is `Expr::Missing` and still counts as a position (it binds as omitted). `f()`
10        // parses to no arguments, which for a one-parameter lambda can also be read as one empty
11        // position, so that call still binds the parameter as omitted.
12        let positions_ok = args.len() == l.params.len() || (args.is_empty() && l.params.len() == 1);
13        if !positions_ok {
14            return err(CellError::Value);
15        }
16        let mut bound = Vec::with_capacity(l.params.len());
17        for (i, p) in l.params.iter().enumerate() {
18            let v = match args.get(i) {
19                Some(Expr::Missing) | None => Ev::V(Value::Empty),
20                Some(e) => self.eval(e),
21            };
22            bound.push((p.clone(), v));
23        }
24        self.call_lambda_values(l, bound)
25    }
26
27    pub(super) fn call_lambda_values(&mut self, l: &Lambda, bound: Vec<(String, Ev)>) -> Ev {
28        if self.calls >= MAX_LAMBDA_CALLS {
29            return err(CellError::Num);
30        }
31        // #2382: one charge per invocation, so a cell's total work stays bounded however the
32        // lambdas nest. Foregrounding the charge here means every path into a body (direct
33        // `(2,3)` application, `MAP`/`REDUCE`/`BYROW` helpers, D-function criteria) meters.
34        self.fuel = match self.fuel.checked_sub(1) {
35            Some(f) => f,
36            None => return err(CellError::Calc),
37        };
38        self.calls += 1;
39        let saved = std::mem::replace(&mut self.env, l.env.clone());
40        self.env.extend(bound);
41        let r = self.eval(&l.body);
42        self.env = saved;
43        self.calls -= 1;
44        r
45    }
46
47    pub(super) fn let_(&mut self, args: &[Expr]) -> Ev {
48        if args.len() < 3 || args.len().is_multiple_of(2) {
49            return err(CellError::Value);
50        }
51        let base = self.env.len();
52        for pair in args[..args.len() - 1].chunks(2) {
53            let [Expr::Name(n), v] = pair else {
54                self.env.truncate(base);
55                return err(CellError::Value);
56            };
57            let val = self.eval(v);
58            self.env.push((n.clone(), val));
59        }
60        let r = match args.last() {
61            Some(body) => self.eval(body),
62            None => err(CellError::Value),
63        };
64        self.env.truncate(base);
65        r
66    }
67
68    /// The function argument of a LAMBDA helper; a function given by name takes `arity`
69    /// arguments.
70    fn lambda_arg(&mut self, e: Option<&Expr>, arity: usize) -> Option<Arc<Lambda>> {
71        if let Some(f) = e.and_then(|x| self.function_by_name(x)) {
72            let params: Vec<String> = (0..arity).map(|i| format!("_xleta.{i}")).collect();
73            let body = Expr::Call(f, params.iter().map(|p| Expr::Name(p.clone())).collect());
74            return Some(Arc::new(Lambda { params, body, env: Vec::new() }));
75        }
76        match e.map(|x| self.eval(x)) {
77            Some(Ev::L(l)) => Some(l),
78            _ => None,
79        }
80    }
81
82    pub(super) fn higher_order(&mut self, name: &str, args: &[Expr]) -> Ev {
83        let as_array = |v: Value| -> Arc<Array> {
84            match v {
85                Value::Array(a) => a,
86                other => Arc::new(Array::scalar(other)),
87            }
88        };
89        match name {
90            "MAP" => {
91                if args.len() < 2 {
92                    return err(CellError::Value);
93                }
94                let Some(l) = self.lambda_arg(args.last(), args.len() - 1) else { return err(CellError::Value) };
95                let arrays: Vec<Arc<Array>> = args[..args.len() - 1].iter().map(|a| as_array(self.value(a))).collect();
96                let rows = arrays.iter().map(|a| a.rows).max().unwrap_or(1);
97                let cols = arrays.iter().map(|a| a.cols).max().unwrap_or(1);
98                let Some(n) = broadcast_len(rows, cols) else { return err(CellError::Num) };
99                // The LAMBDA takes one parameter per array.
100                if l.params.len() != arrays.len() {
101                    return err(CellError::Value);
102                }
103                let mut out = Vec::with_capacity(n);
104                for r in 0..rows {
105                    for c in 0..cols {
106                        let bound = l.params.iter().zip(arrays.iter()).map(|(p, a)| (p.clone(), Ev::V(a.get_broadcast(r, c)))).collect();
107                        let v = self.call_lambda_values(&l, bound);
108                        let Some(one) = single(self.deref(v)) else { return err(CellError::Calc) };
109                        out.push(one);
110                    }
111                }
112                Ev::V(Array::new(rows, cols, out).map(Value::from).unwrap_or(Value::Error(CellError::Calc)))
113            }
114            "REDUCE" | "SCAN" => {
115                if args.len() != 3 {
116                    return err(CellError::Value);
117                }
118                let mut acc = self.value(&args[0]);
119                let arr = as_array(self.value(&args[1]));
120                let Some(l) = self.lambda_arg(args.get(2), 2) else { return err(CellError::Value) };
121                if l.params.len() != 2 {
122                    return err(CellError::Value);
123                }
124                let mut scan = Vec::with_capacity(arr.data.len());
125                for v in arr.data.iter() {
126                    let bound = vec![(l.params[0].clone(), Ev::V(acc.clone())), (l.params[1].clone(), Ev::V(v.clone()))];
127                    let r = self.call_lambda_values(&l, bound);
128                    acc = self.deref(r);
129                    if name == "SCAN" {
130                        let Some(one) = single(acc.clone()) else { return err(CellError::Calc) };
131                        scan.push(one);
132                    }
133                }
134                if name == "SCAN" {
135                    Ev::V(Array::new(arr.rows, arr.cols, scan).map(Value::from).unwrap_or(Value::Error(CellError::Calc)))
136                } else {
137                    Ev::V(acc)
138                }
139            }
140            "BYROW" | "BYCOL" => {
141                if args.len() != 2 {
142                    return err(CellError::Value);
143                }
144                let arr = as_array(self.value(&args[0]));
145                let Some(l) = self.lambda_arg(args.get(1), 1) else { return err(CellError::Value) };
146                let Some(p) = l.params.first().cloned() else { return err(CellError::Value) };
147                if l.params.len() != 1 {
148                    return err(CellError::Value);
149                }
150                let by_row = name == "BYROW";
151                let n = if by_row { arr.rows } else { arr.cols };
152                let mut out = Vec::with_capacity(n);
153                for i in 0..n {
154                    let slice: Vec<Value> = if by_row {
155                        (0..arr.cols).map(|c| arr.get(i, c).cloned().unwrap_or_default()).collect()
156                    } else {
157                        (0..arr.rows).map(|r| arr.get(r, i).cloned().unwrap_or_default()).collect()
158                    };
159                    let a = if by_row { Array::row(slice) } else { Array::column(slice) };
160                    let r = self.call_lambda_values(&l, vec![(p.clone(), Ev::V(a.into()))]);
161                    let Some(one) = single(self.deref(r)) else { return err(CellError::Calc) };
162                    out.push(one);
163                }
164                let a = if by_row { Array::column(out) } else { Array::row(out) };
165                Ev::V(a.into())
166            }
167            "MAKEARRAY" => {
168                if args.len() != 3 {
169                    return err(CellError::Value);
170                }
171                let (rows, cols) = match (self.arg_num(args, 0, 0.0), self.arg_num(args, 1, 0.0)) {
172                    (Ok(r), Ok(c)) => (r.trunc(), c.trunc()),
173                    (Err(e), _) | (_, Err(e)) => return err(e),
174                };
175                if rows < 1.0 || cols < 1.0 || rows * cols > MAX_CELLS as f64 {
176                    return err(CellError::Value);
177                }
178                let Some(l) = self.lambda_arg(args.get(2), 2) else { return err(CellError::Value) };
179                if l.params.len() != 2 {
180                    return err(CellError::Value);
181                }
182                let (rows, cols) = (rows as usize, cols as usize);
183                let mut out = Vec::with_capacity(rows * cols);
184                for r in 0..rows {
185                    for c in 0..cols {
186                        let bound = vec![
187                            (l.params[0].clone(), Ev::V(Value::number(r as f64 + 1.0))),
188                            (l.params[1].clone(), Ev::V(Value::number(c as f64 + 1.0))),
189                        ];
190                        let v = self.call_lambda_values(&l, bound);
191                        let Some(one) = single(self.deref(v)) else { return err(CellError::Calc) };
192                        out.push(one);
193                    }
194                }
195                Ev::V(Array::new(rows, cols, out).map(Value::from).unwrap_or(Value::Error(CellError::Calc)))
196            }
197            _ => err(CellError::Name),
198        }
199    }
200}