gent/interpreter/
expr_eval.rs

1//! Expression evaluation module
2//!
3//! This module provides synchronous expression evaluation without tool calls.
4
5use crate::errors::{GentError, GentResult};
6use crate::interpreter::types::{EnumConstructor, EnumValue};
7use crate::interpreter::{Environment, Value};
8use crate::parser::ast::{BinaryOp, Expression, StringPart, UnaryOp};
9use std::collections::HashMap;
10
11/// Evaluate an expression in the given environment
12pub fn evaluate_expr(expr: &Expression, env: &Environment) -> GentResult<Value> {
13    match expr {
14        // Literals
15        Expression::String(parts, _span) => {
16            // Evaluate each part and concatenate
17            let mut result = String::new();
18            for part in parts {
19                match part {
20                    StringPart::Literal(s) => result.push_str(s),
21                    StringPart::Expr(expr) => {
22                        let value = evaluate_expr(expr, env)?;
23                        result.push_str(&value.to_string());
24                    }
25                }
26            }
27            Ok(Value::String(result))
28        }
29        Expression::Number(n, _) => Ok(Value::Number(*n)),
30        Expression::Boolean(b, _) => Ok(Value::Boolean(*b)),
31        Expression::Null(_) => Ok(Value::Null),
32
33        // Identifier
34        Expression::Identifier(name, span) => {
35            env.get(name)
36                .cloned()
37                .ok_or_else(|| GentError::UndefinedVariable {
38                    name: name.clone(),
39                    span: span.clone(),
40                })
41        }
42
43        // Array literal
44        Expression::Array(elements, _) => {
45            let mut values = Vec::new();
46            for elem in elements {
47                values.push(evaluate_expr(elem, env)?);
48            }
49            Ok(Value::Array(values))
50        }
51
52        // Object literal
53        Expression::Object(fields, _) => {
54            let mut map = HashMap::new();
55            for (key, value_expr) in fields {
56                let value = evaluate_expr(value_expr, env)?;
57                map.insert(key.clone(), value);
58            }
59            Ok(Value::Object(map))
60        }
61
62        // Binary operations
63        Expression::Binary(op, left, right, span) => {
64            let left_val = evaluate_expr(left, env)?;
65            let right_val = evaluate_expr(right, env)?;
66            evaluate_binary_op(op, left_val, right_val, span)
67        }
68
69        // Unary operations
70        Expression::Unary(op, operand, span) => {
71            let val = evaluate_expr(operand, env)?;
72            evaluate_unary_op(op, val, span)
73        }
74
75        // Member access: obj.prop or EnumName.Variant
76        Expression::Member(object_expr, property, span) => {
77            // Check if this is an enum construction: EnumName.Variant
78            if let Expression::Identifier(name, _) = object_expr.as_ref() {
79                if let Some(enum_def) = env.get_enum(name) {
80                    // Find the variant
81                    let variant = enum_def.variants.iter().find(|v| v.name == *property);
82                    if let Some(v) = variant {
83                        if v.fields.is_empty() {
84                            // Unit variant - return EnumValue directly
85                            return Ok(Value::Enum(EnumValue {
86                                enum_name: name.clone(),
87                                variant: property.clone(),
88                                data: vec![],
89                            }));
90                        } else {
91                            // Variant with fields - return a callable constructor
92                            return Ok(Value::EnumConstructor(EnumConstructor {
93                                enum_name: name.clone(),
94                                variant: property.clone(),
95                                expected_fields: v.fields.len(),
96                            }));
97                        }
98                    } else {
99                        return Err(GentError::TypeError {
100                            expected: format!("valid variant of enum '{}'", name),
101                            got: property.clone(),
102                            span: span.clone(),
103                        });
104                    }
105                }
106            }
107
108            // Regular member access
109            let object = evaluate_expr(object_expr, env)?;
110            match object {
111                Value::Object(map) => {
112                    map.get(property)
113                        .cloned()
114                        .ok_or_else(|| GentError::UndefinedProperty {
115                            property: property.clone(),
116                            type_name: "Object".to_string(),
117                            span: span.clone(),
118                        })
119                }
120                _ => Err(GentError::UndefinedProperty {
121                    property: property.clone(),
122                    type_name: object.type_name().to_string(),
123                    span: span.clone(),
124                }),
125            }
126        }
127
128        // Index access: arr[i] or obj["key"]
129        Expression::Index(target_expr, index_expr, span) => {
130            let target = evaluate_expr(target_expr, env)?;
131            let index = evaluate_expr(index_expr, env)?;
132
133            match target {
134                Value::Array(ref items) => {
135                    // Array indexing requires a number
136                    if let Value::Number(n) = index {
137                        let idx = n as i64;
138                        if idx < 0 || idx >= items.len() as i64 {
139                            return Err(GentError::IndexOutOfBounds {
140                                index: idx,
141                                length: items.len(),
142                                span: span.clone(),
143                            });
144                        }
145                        Ok(items[idx as usize].clone())
146                    } else {
147                        Err(GentError::NotIndexable {
148                            type_name: format!("Array with {} index", index.type_name()),
149                            span: span.clone(),
150                        })
151                    }
152                }
153                Value::Object(ref map) => {
154                    // Object indexing requires a string
155                    if let Value::String(key) = index {
156                        map.get(&key)
157                            .cloned()
158                            .ok_or_else(|| GentError::UndefinedProperty {
159                                property: key.clone(),
160                                type_name: "Object".to_string(),
161                                span: span.clone(),
162                            })
163                    } else {
164                        Err(GentError::NotIndexable {
165                            type_name: format!("Object with {} index", index.type_name()),
166                            span: span.clone(),
167                        })
168                    }
169                }
170                _ => Err(GentError::NotIndexable {
171                    type_name: target.type_name().to_string(),
172                    span: span.clone(),
173                }),
174            }
175        }
176
177        // Function calls require async context
178        Expression::Call(_, _, span) => Err(GentError::TypeError {
179            expected: "synchronous expression".to_string(),
180            got: "function call (requires async context)".to_string(),
181            span: span.clone(),
182        }),
183
184        // Range expressions - produce an array of numbers
185        Expression::Range(start, end, span) => {
186            let start_val = evaluate_expr(start, env)?;
187            let end_val = evaluate_expr(end, env)?;
188
189            match (start_val, end_val) {
190                (Value::Number(s), Value::Number(e)) => {
191                    let range: Vec<Value> = (s as i64..e as i64)
192                        .map(|n| Value::Number(n as f64))
193                        .collect();
194                    Ok(Value::Array(range))
195                }
196                _ => Err(GentError::TypeError {
197                    expected: "Number".to_string(),
198                    got: "non-number".to_string(),
199                    span: span.clone(),
200                }),
201            }
202        }
203
204        // Lambda expressions - evaluate to LambdaValue
205        Expression::Lambda(lambda) => {
206            Ok(Value::Lambda(crate::interpreter::types::LambdaValue {
207                params: lambda.params.clone(),
208                body: lambda.body.clone(),
209            }))
210        }
211
212        // Match expressions require async context for body evaluation
213        Expression::Match(match_expr) => Err(GentError::TypeError {
214            expected: "synchronous expression".to_string(),
215            got: "match expression (requires async context)".to_string(),
216            span: match_expr.span.clone(),
217        }),
218    }
219}
220
221/// Evaluate a binary operation
222fn evaluate_binary_op(
223    op: &BinaryOp,
224    left: Value,
225    right: Value,
226    span: &crate::Span,
227) -> GentResult<Value> {
228    match op {
229        // Arithmetic
230        BinaryOp::Add => {
231            match (&left, &right) {
232                // String concatenation
233                (Value::String(s1), Value::String(s2)) => {
234                    Ok(Value::String(format!("{}{}", s1, s2)))
235                }
236                // Number addition
237                (Value::Number(n1), Value::Number(n2)) => Ok(Value::Number(n1 + n2)),
238                // String + Any = concatenate
239                (Value::String(s), other) => Ok(Value::String(format!("{}{}", s, other))),
240                (other, Value::String(s)) => Ok(Value::String(format!("{}{}", other, s))),
241                _ => Err(GentError::InvalidOperands {
242                    op: "+".to_string(),
243                    left: left.type_name().to_string(),
244                    right: right.type_name().to_string(),
245                    span: span.clone(),
246                }),
247            }
248        }
249        BinaryOp::Sub => binary_arithmetic_op(left, right, span, "-", |a, b| a - b),
250        BinaryOp::Mul => binary_arithmetic_op(left, right, span, "*", |a, b| a * b),
251        BinaryOp::Div => {
252            if let (Value::Number(a), Value::Number(b)) = (&left, &right) {
253                if *b == 0.0 {
254                    return Err(GentError::DivisionByZero { span: span.clone() });
255                }
256                Ok(Value::Number(a / b))
257            } else {
258                Err(GentError::InvalidOperands {
259                    op: "/".to_string(),
260                    left: left.type_name().to_string(),
261                    right: right.type_name().to_string(),
262                    span: span.clone(),
263                })
264            }
265        }
266        BinaryOp::Mod => {
267            if let (Value::Number(a), Value::Number(b)) = (&left, &right) {
268                if *b == 0.0 {
269                    return Err(GentError::DivisionByZero { span: span.clone() });
270                }
271                Ok(Value::Number(a % b))
272            } else {
273                Err(GentError::InvalidOperands {
274                    op: "%".to_string(),
275                    left: left.type_name().to_string(),
276                    right: right.type_name().to_string(),
277                    span: span.clone(),
278                })
279            }
280        }
281
282        // Comparison
283        BinaryOp::Eq => Ok(Value::Boolean(values_equal(&left, &right))),
284        BinaryOp::Ne => Ok(Value::Boolean(!values_equal(&left, &right))),
285        BinaryOp::Lt => binary_comparison_op(left, right, span, "<", |a, b| a < b),
286        BinaryOp::Le => binary_comparison_op(left, right, span, "<=", |a, b| a <= b),
287        BinaryOp::Gt => binary_comparison_op(left, right, span, ">", |a, b| a > b),
288        BinaryOp::Ge => binary_comparison_op(left, right, span, ">=", |a, b| a >= b),
289
290        // Logical
291        BinaryOp::And => Ok(Value::Boolean(left.is_truthy() && right.is_truthy())),
292        BinaryOp::Or => Ok(Value::Boolean(left.is_truthy() || right.is_truthy())),
293    }
294}
295
296/// Helper for arithmetic operations
297fn binary_arithmetic_op<F>(
298    left: Value,
299    right: Value,
300    span: &crate::Span,
301    op_str: &str,
302    op: F,
303) -> GentResult<Value>
304where
305    F: FnOnce(f64, f64) -> f64,
306{
307    if let (Value::Number(a), Value::Number(b)) = (&left, &right) {
308        Ok(Value::Number(op(*a, *b)))
309    } else {
310        Err(GentError::InvalidOperands {
311            op: op_str.to_string(),
312            left: left.type_name().to_string(),
313            right: right.type_name().to_string(),
314            span: span.clone(),
315        })
316    }
317}
318
319/// Helper for comparison operations
320fn binary_comparison_op<F>(
321    left: Value,
322    right: Value,
323    span: &crate::Span,
324    op_str: &str,
325    op: F,
326) -> GentResult<Value>
327where
328    F: FnOnce(f64, f64) -> bool,
329{
330    if let (Value::Number(a), Value::Number(b)) = (&left, &right) {
331        Ok(Value::Boolean(op(*a, *b)))
332    } else {
333        Err(GentError::InvalidOperands {
334            op: op_str.to_string(),
335            left: left.type_name().to_string(),
336            right: right.type_name().to_string(),
337            span: span.clone(),
338        })
339    }
340}
341
342/// Check if two values are equal
343fn values_equal(left: &Value, right: &Value) -> bool {
344    match (left, right) {
345        (Value::String(a), Value::String(b)) => a == b,
346        (Value::Number(a), Value::Number(b)) => a == b,
347        (Value::Boolean(a), Value::Boolean(b)) => a == b,
348        (Value::Null, Value::Null) => true,
349        (Value::Array(a), Value::Array(b)) => {
350            if a.len() != b.len() {
351                return false;
352            }
353            a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
354        }
355        (Value::Object(a), Value::Object(b)) => {
356            if a.len() != b.len() {
357                return false;
358            }
359            a.iter()
360                .all(|(k, v)| b.get(k).is_some_and(|v2| values_equal(v, v2)))
361        }
362        _ => false, // Different types are never equal
363    }
364}
365
366/// Evaluate a unary operation
367fn evaluate_unary_op(op: &UnaryOp, operand: Value, span: &crate::Span) -> GentResult<Value> {
368    match op {
369        UnaryOp::Not => Ok(Value::Boolean(!operand.is_truthy())),
370        UnaryOp::Neg => {
371            if let Value::Number(n) = operand {
372                Ok(Value::Number(-n))
373            } else {
374                Err(GentError::InvalidOperands {
375                    op: "-".to_string(),
376                    left: operand.type_name().to_string(),
377                    right: "".to_string(),
378                    span: span.clone(),
379                })
380            }
381        }
382    }
383}