1pub mod context;
2pub mod coercion;
3pub mod functions;
4pub mod resolver;
5
6pub use context::Context;
7pub use functions::{EvalCtx, FunctionMeta, Registry};
8pub use resolver::{extract_refs, Resolver};
9
10use crate::parser::ast::{BinaryOp, Expr, UnaryOp};
11use crate::types::{ErrorKind, Value};
12
13use coercion::{to_number, to_string_val};
14use functions::{FunctionKind, FN_NAME_PLACEHOLDER};
15
16fn finalize_call_result(v: Value, name: &str) -> Value {
23 match v {
24 Value::ErrorMsg(kind, msg) if msg.contains(FN_NAME_PLACEHOLDER) => {
25 Value::ErrorMsg(kind, msg.replace(FN_NAME_PLACEHOLDER, &name.to_uppercase()))
26 }
27 other => other,
28 }
29}
30
31pub fn evaluate_expr(expr: &Expr, ctx: &mut EvalCtx<'_>) -> Value {
38 match expr {
39 Expr::Number(n, _) => {
41 if n.is_finite() {
42 Value::Number(*n)
43 } else {
44 Value::Error(ErrorKind::Num)
45 }
46 }
47 Expr::Text(s, _) => Value::Text(s.clone()),
48 Expr::Bool(b, _) => Value::Bool(*b),
49 Expr::Variable(name, _) => match ctx.ctx.lookup(&name.replace('$', "")) {
58 Some(v) => v,
59 None => ctx.resolve_ref(&crate::parser::refs::Ref::classify(name)),
60 },
61 Expr::Reference(r, _) => match ctx.ctx.lookup(&r.relative_display()) {
67 Some(v) => v,
68 None => ctx.resolve_ref(r),
69 },
70
71 Expr::UnaryOp { op, operand, .. } => {
73 let val = evaluate_expr(operand, ctx);
74 match to_number(val) {
75 Err(e) => e,
76 Ok(n) => match op {
77 UnaryOp::Neg => Value::Number(-n),
78 UnaryOp::Percent => Value::Number(n / 100.0),
79 },
80 }
81 }
82
83 Expr::BinaryOp { op, left, right, .. } => {
85 let lv = evaluate_expr(left, ctx);
86 let rv = evaluate_expr(right, ctx);
87 eval_binary(op, lv, rv)
88 }
89
90 Expr::Array(elems, _) => {
92 let mut values = Vec::with_capacity(elems.len());
93 for elem in elems {
94 let v = evaluate_expr(elem, ctx);
95 values.push(v);
96 }
97 Value::Array(values)
98 }
99
100 Expr::Apply { func, call_args, .. } => {
102 eval_apply(func, call_args, ctx)
103 }
104
105 Expr::FunctionCall { name, args, .. } => {
107 match ctx.registry.get(name) {
108 None => Value::Error(ErrorKind::Name),
109 Some(FunctionKind::Lazy(f)) => {
110 let f: functions::LazyFn = *f;
113 finalize_call_result(f(args, ctx), name)
114 }
115 Some(FunctionKind::Eager(f)) => {
116 let f: functions::EagerFn = *f;
117 let mut evaluated = Vec::with_capacity(args.len());
119 for arg in args {
120 let v = evaluate_expr(arg, ctx);
121 if v.is_error() {
122 return v;
123 }
124 evaluated.push(v);
125 }
126 finalize_call_result(f(&evaluated), name)
127 }
128 }
129 }
130
131 }
132}
133
134fn eval_apply(func: &Expr, call_args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
136 let (lambda_params, body) = match func {
137 Expr::FunctionCall { name, args: lambda_args, .. } if name == "LAMBDA" => {
138 if lambda_args.is_empty() {
139 return Value::Error(ErrorKind::NA);
140 }
141 let param_count = lambda_args.len() - 1;
142 let mut params: Vec<String> = Vec::with_capacity(param_count);
143 for param_expr in &lambda_args[..param_count] {
144 match param_expr {
145 Expr::Variable(n, _) => params.push(n.to_uppercase().replace('$', "")),
149 _ => return Value::Error(ErrorKind::Name),
150 }
151 }
152 let body = &lambda_args[lambda_args.len() - 1];
153 (params, body)
154 }
155 _ => return Value::Error(ErrorKind::Value),
156 };
157
158 if call_args.len() != lambda_params.len() {
159 return Value::Error(ErrorKind::NA);
160 }
161
162 let mut evaluated_args: Vec<Value> = Vec::with_capacity(call_args.len());
163 for arg in call_args {
164 let v = evaluate_expr(arg, ctx);
165 if v.is_error() {
166 return v;
167 }
168 evaluated_args.push(v);
169 }
170
171 let mut saved: Vec<(String, Option<Value>)> = Vec::with_capacity(lambda_params.len());
172 for (param, val) in lambda_params.iter().zip(evaluated_args) {
173 let old = ctx.ctx.set(param.clone(), val);
174 saved.push((param.clone(), old));
175 }
176
177 let result = evaluate_expr(body, ctx);
178
179 for (name, old_val) in saved.into_iter().rev() {
180 match old_val {
181 Some(v) => { ctx.ctx.set(name, v); }
182 None => { ctx.ctx.remove(&name); }
183 }
184 }
185
186 result
187}
188
189fn type_rank(v: &Value) -> u8 {
192 match v {
193 Value::Number(_) | Value::Date(_) | Value::Empty | Value::Zoned(_) => 0,
194 Value::Text(_) => 1,
195 Value::Bool(_) => 2,
196 Value::Error(_) | Value::ErrorMsg(_, _) | Value::Array(_) => 3,
199 }
200}
201
202fn eval_binary(op: &BinaryOp, lv: Value, rv: Value) -> Value {
203 match (&lv, &rv) {
205 (Value::Array(lelems), Value::Array(relems)) => {
206 if lelems.len() != relems.len() {
208 return Value::Error(ErrorKind::Value);
209 }
210 let result: Vec<Value> = lelems
211 .iter()
212 .zip(relems.iter())
213 .map(|(l, r)| eval_binary(op, l.clone(), r.clone()))
214 .collect();
215 return Value::Array(result);
216 }
217 (Value::Array(elems), _) => {
218 let result: Vec<Value> = elems
219 .iter()
220 .map(|e| eval_binary(op, e.clone(), rv.clone()))
221 .collect();
222 return Value::Array(result);
223 }
224 (_, Value::Array(elems)) => {
225 let result: Vec<Value> = elems
226 .iter()
227 .map(|e| eval_binary(op, lv.clone(), e.clone()))
228 .collect();
229 return Value::Array(result);
230 }
231 _ => {}
232 }
233 match op {
234 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Pow => {
236 let date_typed = match op {
247 BinaryOp::Add => {
248 matches!(lv, Value::Date(_)) != matches!(rv, Value::Date(_))
249 }
250 BinaryOp::Sub => {
251 matches!(lv, Value::Date(_)) && !matches!(rv, Value::Date(_))
252 }
253 _ => false,
254 };
255 let ln = match to_number(lv) { Ok(n) => n, Err(e) => return e };
256 let rn = match to_number(rv) { Ok(n) => n, Err(e) => return e };
257 let result = match op {
258 BinaryOp::Add => ln + rn,
259 BinaryOp::Sub => ln - rn,
260 BinaryOp::Mul => ln * rn,
261 BinaryOp::Div => {
262 if rn == 0.0 {
263 return Value::Error(ErrorKind::DivByZero);
264 }
265 ln / rn
266 }
267 BinaryOp::Pow => libm::pow(ln, rn),
268 _ => unreachable!(),
270 };
271 if !result.is_finite() {
272 return Value::Error(ErrorKind::Num);
273 }
274 if date_typed {
275 Value::Date(result)
276 } else {
277 Value::Number(result)
278 }
279 }
280
281 BinaryOp::Concat => {
283 let ls = match to_string_val(lv) { Ok(s) => s, Err(e) => return e };
284 let rs = match to_string_val(rv) { Ok(s) => s, Err(e) => return e };
285 Value::Text(ls + &rs)
286 }
287
288 BinaryOp::Eq | BinaryOp::Ne
290 | BinaryOp::Lt | BinaryOp::Gt
291 | BinaryOp::Le | BinaryOp::Ge => {
292 if lv.is_error() { return lv; }
294 if rv.is_error() { return rv; }
295 if matches!(&lv, Value::Zoned(_)) ^ matches!(&rv, Value::Zoned(_)) {
298 return Value::Error(ErrorKind::Value);
299 }
300
301 let result = compare_values(op, &lv, &rv);
302 Value::Bool(result)
303 }
304 }
305}
306
307fn compare_values(op: &BinaryOp, lv: &Value, rv: &Value) -> bool {
309 match (lv, rv) {
310 (Value::Number(a), Value::Number(b)) => apply_cmp(op, a.partial_cmp(b)),
311 (Value::Date(a), Value::Date(b)) => apply_cmp(op, a.partial_cmp(b)),
312 (Value::Date(a), Value::Number(b)) => apply_cmp(op, a.partial_cmp(b)),
313 (Value::Number(a), Value::Date(b)) => apply_cmp(op, a.partial_cmp(b)),
314 (Value::Zoned(a), Value::Zoned(b)) => apply_cmp(op, Some(a.utc_nanos.cmp(&b.utc_nanos))),
317 (Value::Text(a), Value::Text(b)) => apply_cmp(op, Some(a.cmp(b))),
318 (Value::Bool(a), Value::Bool(b)) => apply_cmp(op, Some(a.cmp(b))),
319 (Value::Empty, Value::Empty) => apply_cmp(op, Some(std::cmp::Ordering::Equal)),
320 (Value::Empty, Value::Number(b)) => apply_cmp(op, 0.0f64.partial_cmp(b)),
322 (Value::Number(a), Value::Empty) => apply_cmp(op, a.partial_cmp(&0.0f64)),
323 _ => {
325 let lr = type_rank(lv);
326 let rr = type_rank(rv);
327 match op {
328 BinaryOp::Eq => false,
329 BinaryOp::Ne => true,
330 BinaryOp::Lt => lr < rr,
331 BinaryOp::Gt => lr > rr,
332 BinaryOp::Le => lr <= rr,
333 BinaryOp::Ge => lr >= rr,
334 _ => unreachable!(),
336 }
337 }
338 }
339}
340
341fn apply_cmp(op: &BinaryOp, ord: Option<std::cmp::Ordering>) -> bool {
342 match ord {
343 None => false,
346 Some(o) => match op {
347 BinaryOp::Eq => o.is_eq(),
348 BinaryOp::Ne => o.is_ne(),
349 BinaryOp::Lt => o.is_lt(),
350 BinaryOp::Gt => o.is_gt(),
351 BinaryOp::Le => o.is_le(),
352 BinaryOp::Ge => o.is_ge(),
353 _ => unreachable!(),
355 },
356 }
357}
358
359#[cfg(test)]
361mod tests;