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(elems), _) => {
166 let result: Vec<Value> = elems
167 .iter()
168 .map(|e| eval_binary(op, e.clone(), rv.clone()))
169 .collect();
170 return Value::Array(result);
171 }
172 (_, Value::Array(elems)) => {
173 let result: Vec<Value> = elems
174 .iter()
175 .map(|e| eval_binary(op, lv.clone(), e.clone()))
176 .collect();
177 return Value::Array(result);
178 }
179 _ => {}
180 }
181 match op {
182 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Pow => {
184 let ln = match to_number(lv) { Ok(n) => n, Err(e) => return e };
185 let rn = match to_number(rv) { Ok(n) => n, Err(e) => return e };
186 let result = match op {
187 BinaryOp::Add => ln + rn,
188 BinaryOp::Sub => ln - rn,
189 BinaryOp::Mul => ln * rn,
190 BinaryOp::Div => {
191 if rn == 0.0 {
192 return Value::Error(ErrorKind::DivByZero);
193 }
194 ln / rn
195 }
196 BinaryOp::Pow => ln.powf(rn),
197 _ => unreachable!(),
199 };
200 if !result.is_finite() {
201 return Value::Error(ErrorKind::Num);
202 }
203 Value::Number(result)
204 }
205
206 BinaryOp::Concat => {
208 let ls = match to_string_val(lv) { Ok(s) => s, Err(e) => return e };
209 let rs = match to_string_val(rv) { Ok(s) => s, Err(e) => return e };
210 Value::Text(ls + &rs)
211 }
212
213 BinaryOp::Eq | BinaryOp::Ne
215 | BinaryOp::Lt | BinaryOp::Gt
216 | BinaryOp::Le | BinaryOp::Ge => {
217 if let Value::Error(_) = &lv { return lv; }
219 if let Value::Error(_) = &rv { return rv; }
220
221 let result = compare_values(op, &lv, &rv);
222 Value::Bool(result)
223 }
224 }
225}
226
227fn compare_values(op: &BinaryOp, lv: &Value, rv: &Value) -> bool {
229 match (lv, rv) {
230 (Value::Number(a), Value::Number(b)) => apply_cmp(op, a.partial_cmp(b)),
231 (Value::Date(a), Value::Date(b)) => apply_cmp(op, a.partial_cmp(b)),
232 (Value::Date(a), Value::Number(b)) => apply_cmp(op, a.partial_cmp(b)),
233 (Value::Number(a), Value::Date(b)) => apply_cmp(op, a.partial_cmp(b)),
234 (Value::Text(a), Value::Text(b)) => apply_cmp(op, Some(a.cmp(b))),
235 (Value::Bool(a), Value::Bool(b)) => apply_cmp(op, Some(a.cmp(b))),
236 (Value::Empty, Value::Empty) => apply_cmp(op, Some(std::cmp::Ordering::Equal)),
237 (Value::Empty, Value::Number(b)) => apply_cmp(op, 0.0f64.partial_cmp(b)),
239 (Value::Number(a), Value::Empty) => apply_cmp(op, a.partial_cmp(&0.0f64)),
240 _ => {
242 let lr = type_rank(lv);
243 let rr = type_rank(rv);
244 match op {
245 BinaryOp::Eq => false,
246 BinaryOp::Ne => true,
247 BinaryOp::Lt => lr < rr,
248 BinaryOp::Gt => lr > rr,
249 BinaryOp::Le => lr <= rr,
250 BinaryOp::Ge => lr >= rr,
251 _ => unreachable!(),
253 }
254 }
255 }
256}
257
258fn apply_cmp(op: &BinaryOp, ord: Option<std::cmp::Ordering>) -> bool {
259 match ord {
260 None => false,
263 Some(o) => match op {
264 BinaryOp::Eq => o.is_eq(),
265 BinaryOp::Ne => o.is_ne(),
266 BinaryOp::Lt => o.is_lt(),
267 BinaryOp::Gt => o.is_gt(),
268 BinaryOp::Le => o.is_le(),
269 BinaryOp::Ge => o.is_ge(),
270 _ => unreachable!(),
272 },
273 }
274}
275
276#[cfg(test)]
278mod tests;