1use crate::binding::{BindingValue, UiBindable};
4use crate::expr::error::{BindingError, BindingErrorKind};
5use crate::expr::{
6 BinaryOp, BinaryOpExpr, ConditionalExpr, Expr, FieldAccessExpr, LiteralExpr, MethodCallExpr,
7 UnaryOp, UnaryOpExpr,
8};
9
10pub fn evaluate_expr(expr: &Expr, model: &dyn UiBindable) -> Result<BindingValue, BindingError> {
12 match expr {
13 Expr::FieldAccess(field_expr) => evaluate_field_access(field_expr, model),
14 Expr::MethodCall(method_expr) => evaluate_method_call(method_expr, model),
15 Expr::BinaryOp(binary_expr) => evaluate_binary_op(binary_expr, model),
16 Expr::UnaryOp(unary_expr) => evaluate_unary_op(unary_expr, model),
17 Expr::Conditional(conditional_expr) => evaluate_conditional(conditional_expr, model),
18 Expr::Literal(literal_expr) => Ok(evaluate_literal(literal_expr)),
19 }
20}
21
22fn evaluate_field_access(
24 field_expr: &FieldAccessExpr,
25 model: &dyn UiBindable,
26) -> Result<BindingValue, BindingError> {
27 let path: Vec<&str> = field_expr.path.iter().map(|s| s.as_str()).collect();
28
29 model.get_field(&path).ok_or_else(|| {
30 let field_name = field_expr.path.join(".");
31 BindingError {
32 kind: BindingErrorKind::UnknownField,
33 message: format!("Field '{}' not found", field_name),
34 span: crate::ir::span::Span::new(0, 0, 0, 0), suggestion: None,
36 }
37 })
38}
39
40fn evaluate_method_call(
42 method_expr: &MethodCallExpr,
43 model: &dyn UiBindable,
44) -> Result<BindingValue, BindingError> {
45 let receiver = evaluate_expr(&method_expr.receiver, model)?;
46 let method = &method_expr.method;
47
48 let _args: Vec<BindingValue> = method_expr
50 .args
51 .iter()
52 .map(|arg| evaluate_expr(arg, model))
53 .collect::<Result<Vec<_>, _>>()?;
54
55 match (receiver.clone(), method.as_str()) {
56 (BindingValue::String(s), "len") => Ok(BindingValue::Integer(s.len() as i64)),
58 (BindingValue::String(s), "to_uppercase") => Ok(BindingValue::String(s.to_uppercase())),
59 (BindingValue::String(s), "to_lowercase") => Ok(BindingValue::String(s.to_lowercase())),
60 (BindingValue::String(s), "trim") => Ok(BindingValue::String(s.trim().to_string())),
61
62 (BindingValue::List(l), "len") => Ok(BindingValue::Integer(l.len() as i64)),
64 (BindingValue::List(l), "is_empty") => Ok(BindingValue::Bool(l.is_empty())),
65
66 (BindingValue::Integer(i), "to_string") => Ok(BindingValue::String(i.to_string())),
68
69 (BindingValue::Float(f), "to_string") => Ok(BindingValue::String(f.to_string())),
71 (BindingValue::Float(f), "round") => Ok(BindingValue::Float(f.round())),
72 (BindingValue::Float(f), "floor") => Ok(BindingValue::Float(f.floor())),
73 (BindingValue::Float(f), "ceil") => Ok(BindingValue::Float(f.ceil())),
74
75 (BindingValue::Bool(b), "to_string") => Ok(BindingValue::String(b.to_string())),
77
78 _ => Err(BindingError {
79 kind: BindingErrorKind::UnknownMethod,
80 message: format!("Method '{}' not supported on {:?}", method, receiver),
81 span: crate::ir::span::Span::new(0, 0, 0, 0),
82 suggestion: None,
83 }),
84 }
85}
86
87fn evaluate_binary_op(
89 binary_expr: &BinaryOpExpr,
90 model: &dyn UiBindable,
91) -> Result<BindingValue, BindingError> {
92 let left = evaluate_expr(&binary_expr.left, model)?;
93 let right = evaluate_expr(&binary_expr.right, model)?;
94
95 match binary_expr.op {
96 BinaryOp::Add => match (left, right) {
98 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
99 Ok(BindingValue::Integer(a + b))
100 }
101 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a + b)),
102 (BindingValue::String(a), BindingValue::String(b)) => Ok(BindingValue::String(a + &b)),
103 _ => Err(BindingError {
104 kind: BindingErrorKind::InvalidOperation,
105 message: "Cannot add these types".to_string(),
106 span: crate::ir::span::Span::new(0, 0, 0, 0),
107 suggestion: None,
108 }),
109 },
110 BinaryOp::Sub => match (left, right) {
111 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
112 Ok(BindingValue::Integer(a - b))
113 }
114 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a - b)),
115 _ => Err(BindingError {
116 kind: BindingErrorKind::InvalidOperation,
117 message: "Cannot subtract these types".to_string(),
118 span: crate::ir::span::Span::new(0, 0, 0, 0),
119 suggestion: None,
120 }),
121 },
122 BinaryOp::Mul => match (left, right) {
123 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
124 Ok(BindingValue::Integer(a * b))
125 }
126 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a * b)),
127 _ => Err(BindingError {
128 kind: BindingErrorKind::InvalidOperation,
129 message: "Cannot multiply these types".to_string(),
130 span: crate::ir::span::Span::new(0, 0, 0, 0),
131 suggestion: None,
132 }),
133 },
134 BinaryOp::Div => match (left, right) {
135 (BindingValue::Integer(a), BindingValue::Integer(b)) if b != 0 => {
136 Ok(BindingValue::Integer(a / b))
137 }
138 (BindingValue::Float(a), BindingValue::Float(b)) if b != 0.0 => {
139 Ok(BindingValue::Float(a / b))
140 }
141 _ => Err(BindingError {
142 kind: BindingErrorKind::InvalidOperation,
143 message: "Cannot divide these types or division by zero".to_string(),
144 span: crate::ir::span::Span::new(0, 0, 0, 0),
145 suggestion: None,
146 }),
147 },
148
149 BinaryOp::Eq => Ok(BindingValue::Bool(left == right)),
151 BinaryOp::Ne => Ok(BindingValue::Bool(left != right)),
152 BinaryOp::Lt => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
153 a < b
154 }))),
155 BinaryOp::Le => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
156 a <= b
157 }))),
158 BinaryOp::Gt => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
159 a > b
160 }))),
161 BinaryOp::Ge => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
162 a >= b
163 }))),
164
165 BinaryOp::And => {
167 let left_bool = left.to_bool();
168 let right_bool = right.to_bool();
169 Ok(BindingValue::Bool(left_bool && right_bool))
170 }
171 BinaryOp::Or => {
172 let left_bool = left.to_bool();
173 let right_bool = right.to_bool();
174 Ok(BindingValue::Bool(left_bool || right_bool))
175 }
176 }
177}
178
179fn compare_values<F>(left: &BindingValue, right: &BindingValue, cmp: F) -> bool
181where
182 F: Fn(f64, f64) -> bool,
183{
184 match (left, right) {
185 (BindingValue::Integer(a), BindingValue::Integer(b)) => cmp(*a as f64, *b as f64),
186 (BindingValue::Float(a), BindingValue::Float(b)) => cmp(*a, *b),
187 (BindingValue::String(a), BindingValue::String(b)) => cmp(a.len() as f64, b.len() as f64),
188 (BindingValue::List(a), BindingValue::List(b)) => cmp(a.len() as f64, b.len() as f64),
189 _ => false,
190 }
191}
192
193fn evaluate_unary_op(
195 unary_expr: &UnaryOpExpr,
196 model: &dyn UiBindable,
197) -> Result<BindingValue, BindingError> {
198 let operand = evaluate_expr(&unary_expr.operand, model)?;
199
200 match unary_expr.op {
201 UnaryOp::Not => Ok(BindingValue::Bool(!operand.to_bool())),
202 UnaryOp::Neg => match operand {
203 BindingValue::Integer(i) => Ok(BindingValue::Integer(-i)),
204 BindingValue::Float(f) => Ok(BindingValue::Float(-f)),
205 _ => Err(BindingError {
206 kind: BindingErrorKind::InvalidOperation,
207 message: "Cannot negate this type".to_string(),
208 span: crate::ir::span::Span::new(0, 0, 0, 0),
209 suggestion: None,
210 }),
211 },
212 }
213}
214
215fn evaluate_conditional(
217 conditional_expr: &ConditionalExpr,
218 model: &dyn UiBindable,
219) -> Result<BindingValue, BindingError> {
220 let condition = evaluate_expr(&conditional_expr.condition, model)?;
221
222 if condition.to_bool() {
223 evaluate_expr(&conditional_expr.then_branch, model)
224 } else {
225 evaluate_expr(&conditional_expr.else_branch, model)
226 }
227}
228
229fn evaluate_literal(literal_expr: &LiteralExpr) -> BindingValue {
231 match literal_expr {
232 LiteralExpr::String(s) => BindingValue::String(s.clone()),
233 LiteralExpr::Integer(i) => BindingValue::Integer(*i),
234 LiteralExpr::Float(f) => BindingValue::Float(*f),
235 LiteralExpr::Bool(b) => BindingValue::Bool(*b),
236 }
237}
238
239pub fn evaluate_binding_expr(
241 binding_expr: &crate::expr::BindingExpr,
242 model: &dyn UiBindable,
243) -> Result<BindingValue, BindingError> {
244 match evaluate_expr(&binding_expr.expr, model) {
245 Ok(result) => Ok(result),
246 Err(mut err) => {
247 err.span = binding_expr.span;
248 Err(err)
249 }
250 }
251}
252
253pub fn evaluate_formatted(
255 parts: &[crate::ir::InterpolatedPart],
256 model: &dyn UiBindable,
257) -> Result<String, BindingError> {
258 let mut result = String::new();
259
260 for part in parts {
261 match part {
262 crate::ir::InterpolatedPart::Literal(literal) => {
263 result.push_str(literal);
264 }
265 crate::ir::InterpolatedPart::Binding(binding_expr) => {
266 let value = evaluate_binding_expr(binding_expr, model)?;
267 result.push_str(&value.to_display_string());
268 }
269 }
270 }
271
272 Ok(result)
273}