1use crate::ast::{BinOp, Expr, UnaryOp};
4use crate::builtins::eval_builtin;
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum Value {
10 Number(f64),
11 Text(String),
12 Boolean(bool),
13 Array(Vec<Value>),
14 Null,
15 Error(String),
16}
17
18pub type DataContext = HashMap<(usize, usize), Value>;
20
21pub fn evaluate(expr: &Expr, ctx: &DataContext) -> Result<Value, String> {
23 match expr {
24 Expr::Number(n) => Ok(Value::Number(*n)),
25 Expr::String(s) => Ok(Value::Text(s.clone())),
26 Expr::Boolean(b) => Ok(Value::Boolean(*b)),
27 Expr::CellRef(cell) => {
28 let key = (cell.row, cell.col);
29 Ok(ctx.get(&key).cloned().unwrap_or(Value::Number(0.0)))
30 }
31 Expr::Range(range) => {
32 let values: Vec<Value> = range
33 .iter_cells()
34 .filter_map(|(r, c)| ctx.get(&(r, c)).cloned())
35 .collect();
36 Ok(Value::Array(values))
37 }
38 Expr::BinaryOp { op, left, right } => {
39 let lval = evaluate(left, ctx)?;
40 let rval = evaluate(right, ctx)?;
41 eval_binary(*op, lval, rval)
42 }
43 Expr::UnaryOp { op: UnaryOp::Negate, operand } => {
44 let val = evaluate(operand, ctx)?;
45 match val {
46 Value::Number(n) => Ok(Value::Number(-n)),
47 _ => Err("Cannot negate non-number".into()),
48 }
49 }
50 Expr::Percent(expr) => {
51 let val = evaluate(expr, ctx)?;
52 match val {
53 Value::Number(n) => Ok(Value::Number(n / 100.0)),
54 _ => Err("Cannot apply % to non-number".into()),
55 }
56 }
57 Expr::FunctionCall { name, args } => {
58 let evaluated: Result<Vec<Value>, String> = args
59 .iter()
60 .map(|a| evaluate(a, ctx))
61 .collect();
62 eval_builtin(name, &evaluated?)
63 }
64 }
65}
66
67fn to_number(v: &Value) -> Result<f64, String> {
68 match v {
69 Value::Number(n) => Ok(*n),
70 Value::Boolean(b) => Ok(if *b { 1.0 } else { 0.0 }),
71 Value::Null => Ok(0.0),
72 Value::Text(s) => s.parse::<f64>().map_err(|_| format!("Cannot convert '{}' to number", s)),
73 Value::Error(e) => Err(e.clone()),
74 Value::Array(_) => Err("Cannot convert array to number".into()),
75 }
76}
77
78fn eval_binary(op: BinOp, left: Value, right: Value) -> Result<Value, String> {
79 match op {
80 BinOp::Add => {
81 let l = to_number(&left)?;
82 let r = to_number(&right)?;
83 Ok(Value::Number(l + r))
84 }
85 BinOp::Sub => {
86 let l = to_number(&left)?;
87 let r = to_number(&right)?;
88 Ok(Value::Number(l - r))
89 }
90 BinOp::Mul => {
91 let l = to_number(&left)?;
92 let r = to_number(&right)?;
93 Ok(Value::Number(l * r))
94 }
95 BinOp::Div => {
96 let l = to_number(&left)?;
97 let r = to_number(&right)?;
98 if r == 0.0 {
99 return Err("Division by zero".into());
100 }
101 Ok(Value::Number(l / r))
102 }
103 BinOp::Pow => {
104 let l = to_number(&left)?;
105 let r = to_number(&right)?;
106 Ok(Value::Number(l.powf(r)))
107 }
108 BinOp::Concat => {
109 let ls = value_to_string(&left);
110 let rs = value_to_string(&right);
111 Ok(Value::Text(format!("{}{}", ls, rs)))
112 }
113 BinOp::Eq => Ok(Value::Boolean(values_eq(&left, &right))),
114 BinOp::Neq => Ok(Value::Boolean(!values_eq(&left, &right))),
115 BinOp::Lt => {
116 let l = to_number(&left)?;
117 let r = to_number(&right)?;
118 Ok(Value::Boolean(l < r))
119 }
120 BinOp::Gt => {
121 let l = to_number(&left)?;
122 let r = to_number(&right)?;
123 Ok(Value::Boolean(l > r))
124 }
125 BinOp::Lte => {
126 let l = to_number(&left)?;
127 let r = to_number(&right)?;
128 Ok(Value::Boolean(l <= r))
129 }
130 BinOp::Gte => {
131 let l = to_number(&left)?;
132 let r = to_number(&right)?;
133 Ok(Value::Boolean(l >= r))
134 }
135 }
136}
137
138fn values_eq(a: &Value, b: &Value) -> bool {
139 match (a, b) {
140 (Value::Number(x), Value::Number(y)) => (x - y).abs() < f64::EPSILON,
141 (Value::Text(x), Value::Text(y)) => x == y,
142 (Value::Boolean(x), Value::Boolean(y)) => x == y,
143 (Value::Null, Value::Null) => true,
144 _ => false,
145 }
146}
147
148fn value_to_string(v: &Value) -> String {
149 match v {
150 Value::Number(n) => format!("{}", n),
151 Value::Text(s) => s.clone(),
152 Value::Boolean(b) => if *b { "TRUE".into() } else { "FALSE".into() },
153 Value::Null => String::new(),
154 Value::Error(e) => format!("#ERR: {}", e),
155 Value::Array(arr) => {
156 let parts: Vec<String> = arr.iter().map(value_to_string).collect();
157 format!("[{}]", parts.join(", "))
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use crate::parser::Parser;
166
167 fn eval_str(formula: &str, ctx: &DataContext) -> Result<Value, String> {
168 let expr = Parser::parse_formula(formula)?;
169 evaluate(&expr, ctx)
170 }
171
172 #[test]
173 fn eval_arithmetic() {
174 let ctx = DataContext::new();
175 assert_eq!(eval_str("1+2*3", &ctx).unwrap(), Value::Number(7.0));
176 }
177
178 #[test]
179 fn eval_cell_ref() {
180 let mut ctx = DataContext::new();
181 ctx.insert((0, 0), Value::Number(42.0)); assert_eq!(eval_str("A1", &ctx).unwrap(), Value::Number(42.0));
183 }
184
185 #[test]
186 fn eval_range_sum() {
187 let mut ctx = DataContext::new();
188 for i in 0..5 {
189 ctx.insert((i, 0), Value::Number((i + 1) as f64)); }
191 assert_eq!(eval_str("SUM(A1:A5)", &ctx).unwrap(), Value::Number(15.0));
192 }
193
194 #[test]
195 fn eval_division_by_zero() {
196 let ctx = DataContext::new();
197 assert!(eval_str("1/0", &ctx).is_err());
198 }
199
200 #[test]
201 fn eval_concat() {
202 let ctx = DataContext::new();
203 assert_eq!(eval_str("\"hello\"&\" world\"", &ctx).unwrap(), Value::Text("hello world".into()));
204 }
205
206 #[test]
207 fn eval_negate() {
208 let ctx = DataContext::new();
209 assert_eq!(eval_str("-(3+2)", &ctx).unwrap(), Value::Number(-5.0));
210 }
211
212 #[test]
213 fn eval_comparison() {
214 let ctx = DataContext::new();
215 assert_eq!(eval_str("5>3", &ctx).unwrap(), Value::Boolean(true));
216 assert_eq!(eval_str("2=2", &ctx).unwrap(), Value::Boolean(true));
217 }
218
219 #[test]
220 fn eval_percent() {
221 let ctx = DataContext::new();
222 assert_eq!(eval_str("50%", &ctx).unwrap(), Value::Number(0.5));
223 }
224
225 #[test]
226 fn eval_evolve() {
227 let mut ctx = DataContext::new();
228 for i in 0..10 {
229 ctx.insert((i, 0), Value::Number(i as f64));
230 }
231 let result = eval_str("EVOLVE(A1:A10, 100)", &ctx).unwrap();
232 assert!(matches!(result, Value::Number(_)));
233 }
234
235 #[test]
236 fn eval_entropy() {
237 let mut ctx = DataContext::new();
238 for i in 0..20 {
239 ctx.insert((i, 2), Value::Number((i % 4) as f64));
240 }
241 let result = eval_str("ENTROPY(C1:C20)", &ctx).unwrap();
242 assert!(matches!(result, Value::Number(_)));
243 }
244
245 #[test]
246 fn eval_power() {
247 let ctx = DataContext::new();
248 assert_eq!(eval_str("2^10", &ctx).unwrap(), Value::Number(1024.0));
249 }
250}