1pub mod context;
2pub mod coercion;
3pub mod functions;
4
5pub use context::Context;
6pub use functions::{EvalCtx, FunctionMeta, Registry};
7
8use crate::parser::ast::{BinaryOp, Expr, UnaryOp};
9use crate::types::{ErrorKind, Value};
10
11use coercion::{to_number, to_string_val};
12use functions::FunctionKind;
13
14pub fn evaluate_expr(expr: &Expr, ctx: &mut EvalCtx<'_>) -> Value {
21 match expr {
22 Expr::Number(n, _) => {
24 if n.is_finite() {
25 Value::Number(*n)
26 } else {
27 Value::Error(ErrorKind::Num)
28 }
29 }
30 Expr::Text(s, _) => Value::Text(s.clone()),
31 Expr::Bool(b, _) => Value::Bool(*b),
32 Expr::Variable(name, _) => ctx.ctx.get(name),
33
34 Expr::UnaryOp { op, operand, .. } => {
36 let val = evaluate_expr(operand, ctx);
37 match to_number(val) {
38 Err(e) => e,
39 Ok(n) => match op {
40 UnaryOp::Neg => Value::Number(-n),
41 UnaryOp::Percent => Value::Number(n / 100.0),
42 },
43 }
44 }
45
46 Expr::BinaryOp { op, left, right, .. } => {
48 let lv = evaluate_expr(left, ctx);
49 let rv = evaluate_expr(right, ctx);
50 eval_binary(op, lv, rv)
51 }
52
53 Expr::Array(elems, _) => {
55 let mut values = Vec::with_capacity(elems.len());
56 for elem in elems {
57 let v = evaluate_expr(elem, ctx);
58 values.push(v);
59 }
60 Value::Array(values)
61 }
62
63 Expr::Apply { func, call_args, .. } => {
65 eval_apply(func, call_args, ctx)
66 }
67
68 Expr::FunctionCall { name, args, .. } => {
70 match ctx.registry.get(name) {
71 None => Value::Error(ErrorKind::Name),
72 Some(FunctionKind::Lazy(f)) => {
73 let f: functions::LazyFn = *f;
76 f(args, ctx)
77 }
78 Some(FunctionKind::Eager(f)) => {
79 let f: functions::EagerFn = *f;
80 let mut evaluated = Vec::with_capacity(args.len());
82 for arg in args {
83 let v = evaluate_expr(arg, ctx);
84 if matches!(v, Value::Error(_)) {
85 return v;
86 }
87 evaluated.push(v);
88 }
89 f(&evaluated)
90 }
91 }
92 }
93
94 }
95}
96
97fn eval_apply(func: &Expr, call_args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
99 let (lambda_params, body) = match func {
100 Expr::FunctionCall { name, args: lambda_args, .. } if name == "LAMBDA" => {
101 if lambda_args.is_empty() {
102 return Value::Error(ErrorKind::NA);
103 }
104 let param_count = lambda_args.len() - 1;
105 let mut params: Vec<String> = Vec::with_capacity(param_count);
106 for param_expr in &lambda_args[..param_count] {
107 match param_expr {
108 Expr::Variable(n, _) => params.push(n.to_uppercase()),
109 _ => return Value::Error(ErrorKind::Value),
110 }
111 }
112 let body = &lambda_args[lambda_args.len() - 1];
113 (params, body)
114 }
115 _ => return Value::Error(ErrorKind::Value),
116 };
117
118 if call_args.len() != lambda_params.len() {
119 return Value::Error(ErrorKind::NA);
120 }
121
122 let mut evaluated_args: Vec<Value> = Vec::with_capacity(call_args.len());
123 for arg in call_args {
124 let v = evaluate_expr(arg, ctx);
125 if matches!(v, Value::Error(_)) {
126 return v;
127 }
128 evaluated_args.push(v);
129 }
130
131 let mut saved: Vec<(String, Option<Value>)> = Vec::with_capacity(lambda_params.len());
132 for (param, val) in lambda_params.iter().zip(evaluated_args) {
133 let old = ctx.ctx.set(param.clone(), val);
134 saved.push((param.clone(), old));
135 }
136
137 let result = evaluate_expr(body, ctx);
138
139 for (name, old_val) in saved.into_iter().rev() {
140 match old_val {
141 Some(v) => { ctx.ctx.set(name, v); }
142 None => { ctx.ctx.remove(&name); }
143 }
144 }
145
146 result
147}
148
149fn type_rank(v: &Value) -> u8 {
152 match v {
153 Value::Number(_) | Value::Date(_) | Value::Empty => 0,
154 Value::Text(_) => 1,
155 Value::Bool(_) => 2,
156 Value::Error(_) | Value::Array(_) => 3,
159 }
160}
161
162fn eval_binary(op: &BinaryOp, lv: Value, rv: Value) -> Value {
163 match (&lv, &rv) {
165 (Value::Array(lelems), Value::Array(relems)) => {
166 if lelems.len() != relems.len() {
168 return Value::Error(ErrorKind::Value);
169 }
170 let result: Vec<Value> = lelems
171 .iter()
172 .zip(relems.iter())
173 .map(|(l, r)| eval_binary(op, l.clone(), r.clone()))
174 .collect();
175 return Value::Array(result);
176 }
177 (Value::Array(elems), _) => {
178 let result: Vec<Value> = elems
179 .iter()
180 .map(|e| eval_binary(op, e.clone(), rv.clone()))
181 .collect();
182 return Value::Array(result);
183 }
184 (_, Value::Array(elems)) => {
185 let result: Vec<Value> = elems
186 .iter()
187 .map(|e| eval_binary(op, lv.clone(), e.clone()))
188 .collect();
189 return Value::Array(result);
190 }
191 _ => {}
192 }
193 match op {
194 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Pow => {
196 let ln = match to_number(lv) { Ok(n) => n, Err(e) => return e };
197 let rn = match to_number(rv) { Ok(n) => n, Err(e) => return e };
198 let result = match op {
199 BinaryOp::Add => ln + rn,
200 BinaryOp::Sub => ln - rn,
201 BinaryOp::Mul => ln * rn,
202 BinaryOp::Div => {
203 if rn == 0.0 {
204 return Value::Error(ErrorKind::DivByZero);
205 }
206 ln / rn
207 }
208 BinaryOp::Pow => ln.powf(rn),
209 _ => unreachable!(),
211 };
212 if !result.is_finite() {
213 return Value::Error(ErrorKind::Num);
214 }
215 Value::Number(result)
216 }
217
218 BinaryOp::Concat => {
220 let ls = match to_string_val(lv) { Ok(s) => s, Err(e) => return e };
221 let rs = match to_string_val(rv) { Ok(s) => s, Err(e) => return e };
222 Value::Text(ls + &rs)
223 }
224
225 BinaryOp::Eq | BinaryOp::Ne
227 | BinaryOp::Lt | BinaryOp::Gt
228 | BinaryOp::Le | BinaryOp::Ge => {
229 if let Value::Error(_) = &lv { return lv; }
231 if let Value::Error(_) = &rv { return rv; }
232
233 let result = compare_values(op, &lv, &rv);
234 Value::Bool(result)
235 }
236 }
237}
238
239fn compare_values(op: &BinaryOp, lv: &Value, rv: &Value) -> bool {
241 match (lv, rv) {
242 (Value::Number(a), Value::Number(b)) => apply_cmp(op, a.partial_cmp(b)),
243 (Value::Date(a), Value::Date(b)) => apply_cmp(op, a.partial_cmp(b)),
244 (Value::Date(a), Value::Number(b)) => apply_cmp(op, a.partial_cmp(b)),
245 (Value::Number(a), Value::Date(b)) => apply_cmp(op, a.partial_cmp(b)),
246 (Value::Text(a), Value::Text(b)) => apply_cmp(op, Some(a.cmp(b))),
247 (Value::Bool(a), Value::Bool(b)) => apply_cmp(op, Some(a.cmp(b))),
248 (Value::Empty, Value::Empty) => apply_cmp(op, Some(std::cmp::Ordering::Equal)),
249 (Value::Empty, Value::Number(b)) => apply_cmp(op, 0.0f64.partial_cmp(b)),
251 (Value::Number(a), Value::Empty) => apply_cmp(op, a.partial_cmp(&0.0f64)),
252 _ => {
254 let lr = type_rank(lv);
255 let rr = type_rank(rv);
256 match op {
257 BinaryOp::Eq => false,
258 BinaryOp::Ne => true,
259 BinaryOp::Lt => lr < rr,
260 BinaryOp::Gt => lr > rr,
261 BinaryOp::Le => lr <= rr,
262 BinaryOp::Ge => lr >= rr,
263 _ => unreachable!(),
265 }
266 }
267 }
268}
269
270fn apply_cmp(op: &BinaryOp, ord: Option<std::cmp::Ordering>) -> bool {
271 match ord {
272 None => false,
275 Some(o) => match op {
276 BinaryOp::Eq => o.is_eq(),
277 BinaryOp::Ne => o.is_ne(),
278 BinaryOp::Lt => o.is_lt(),
279 BinaryOp::Gt => o.is_gt(),
280 BinaryOp::Le => o.is_le(),
281 BinaryOp::Ge => o.is_ge(),
282 _ => unreachable!(),
284 },
285 }
286}
287
288#[cfg(test)]
290mod tests;