Skip to main content

sqlexpr_rust/
evaluator.rs

1//! Evaluator definitions for SQL Expression Evaluation
2//!
3//! This module defines the Evaluator structure that applies runtime values to an AST
4//! to determine if the boolean expression represented by the AST evaluates to true,
5//! false or results in a runtime error.  Runtime type checking is applied after
6//! value substitution to ensure type safety during evaluation.
7
8use crate::ast::*;
9use crate::parser::{parse, ParseError};
10
11use std::collections::HashMap;
12use std::fmt;
13
14// ============================================================================
15// PUBLIC API
16// ============================================================================
17
18/// User-provided values for variable substitution
19#[derive(Debug, Clone, PartialEq)]
20pub enum RuntimeValue {
21    Integer(i64),
22    Float(f64),
23    String(String),
24    Boolean(bool),
25    Null,
26}
27
28/// Comprehensive error type for evaluation failures
29#[derive(Debug, Clone, PartialEq)]
30pub enum EvalError {
31
32    /// Parse error from the parser
33    EvalParseError(String),
34
35    /// Variable referenced but not found in value map
36    UnboundVariable {
37        name: String
38    },
39
40    /// Type mismatch in operation
41    TypeError {
42        operation: String,
43        expected: String,
44        actual: String,
45        context: String,
46    },
47
48    /// NULL used in arithmetic or comparison operation (not IS NULL/IS NOT NULL)
49    NullInOperation {
50        operation: String,
51        context: String,
52    },
53
54    /// Division by zero
55    DivisionByZero {
56        expression: String,
57    },
58
59    /// Invalid literal format
60    InvalidLiteral {
61        literal: String,
62        literal_type: String,
63        error: String,
64    },
65}
66
67impl fmt::Display for EvalError {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self {
70            EvalError::EvalParseError(msg) => write!(f, "Parse error: {}", msg),
71            EvalError::UnboundVariable { name } => {
72                write!(f, "Unbound variable '{}' - not found in value map", name)
73            }
74            EvalError::TypeError { operation, expected, actual, context } => {
75                write!(f, "Type error in {}: expected {}, got {} (context: {})",
76                    operation, expected, actual, context)
77            }
78            EvalError::NullInOperation { operation, context } => {
79                write!(f, "NULL value in {} operation (context: {}). NULL is only allowed in IS NULL/IS NOT NULL",
80                    operation, context)
81            }
82            EvalError::DivisionByZero { expression } => {
83                write!(f, "Division by zero in expression: {}", expression)
84            }
85            EvalError::InvalidLiteral { literal, literal_type, error } => {
86                write!(f, "Invalid {} literal '{}': {}", literal_type, literal, error)
87            }
88        }
89    }
90}
91
92impl std::error::Error for EvalError {}
93
94impl From<String> for EvalError {
95    fn from(msg: String) -> Self {
96        EvalError::EvalParseError(msg)
97    }
98}
99
100impl From<&str> for EvalError {
101    fn from(msg: &str) -> Self {
102        EvalError::EvalParseError(msg.to_string())
103    }
104}
105
106impl From<ParseError> for EvalError {
107    fn from(msg: ParseError) -> Self {
108        EvalError::EvalParseError(msg.to_string())
109    }
110}
111
112
113/// Public evaluation function that calculates the value of an AST given a mpa variable bindings.
114///
115/// # Arguments
116/// * `input` - SQL expression string to evaluate (must be boolean-valued)
117/// * `map` - Variable name to value bindings for substitution
118///
119/// # Returns
120/// * `Ok(bool)` - The evaluated boolean result
121/// * `Err(EvalError)` - Error during parsing, variable resolution, type checking, or evaluation
122///
123/// # Examples
124/// ```
125/// use std::collections::HashMap;
126/// use sqlexpr_rust::{evaluate, RuntimeValue};
127///
128/// let mut map = HashMap::new();
129/// map.insert("x".to_string(), RuntimeValue::Integer(42));
130///
131/// let result = evaluate("x > 10", &map).unwrap();
132/// assert_eq!(result, true);
133/// ```
134pub fn evaluate(input: &str, map: &HashMap<String, RuntimeValue>) -> Result<bool, EvalError> {
135    let evaluator = Evaluator::new(input, map)?;
136    evaluator.eval_boolean(&evaluator.ast)
137
138}
139
140// ============================================================================
141// EVALUATOR
142// ============================================================================
143
144/// Private evaluator implementation
145struct Evaluator<'a> {
146    input: String,
147    ast: BooleanExpr,
148    value_map: &'a HashMap<String, RuntimeValue>,
149}
150
151impl<'a> Evaluator<'a> {
152    /// Create new evaluator by parsing input
153    fn new(input: &str, value_map: &'a HashMap<String, RuntimeValue>) -> Result<Self, EvalError> {
154        let ast = parse(input)?;
155        Ok(Evaluator {
156            input: input.to_string(),
157            ast,
158            value_map,
159        })
160    }
161
162    // ========================================================================
163    // BOOLEAN EXPRESSION EVALUATION
164    // ========================================================================
165
166    /// Evaluate a boolean expression
167    fn eval_boolean(&self, expr: &BooleanExpr) -> Result<bool, EvalError> {
168        match expr {
169            BooleanExpr::Literal(b) => Ok(*b),
170
171            BooleanExpr::Variable(name) => {
172                match self.value_map.get(name) {
173                    Some(RuntimeValue::Boolean(b)) => Ok(*b),
174                    Some(other) => Err(EvalError::TypeError {
175                        operation: "boolean variable".to_string(),
176                        expected: "boolean".to_string(),
177                        actual: Self::runtime_type_name(other),
178                        context: format!("variable '{}'", name),
179                    }),
180                    None => Err(EvalError::UnboundVariable {
181                        name: name.clone(),
182                    }),
183                }
184            }
185
186            BooleanExpr::And(left, right) => {
187                let l = self.eval_boolean(left)?;
188                // Short-circuit: if left is false, don't evaluate right
189                if !l {
190                    return Ok(false);
191                }
192                self.eval_boolean(right)
193            }
194
195            BooleanExpr::Or(left, right) => {
196                let l = self.eval_boolean(left)?;
197                // Short-circuit: if left is true, don't evaluate right
198                if l {
199                    return Ok(true);
200                }
201                self.eval_boolean(right)
202            }
203
204            BooleanExpr::Not(expr) => {
205                Ok(!self.eval_boolean(expr)?)
206            }
207
208            BooleanExpr::Relational(rel) => {
209                self.eval_relational(rel)
210            }
211        }
212    }
213
214    // ========================================================================
215    // RELATIONAL EXPRESSION EVALUATION
216    // ========================================================================
217
218    /// Evaluate a relational expression to boolean
219    fn eval_relational(&self, expr: &RelationalExpr) -> Result<bool, EvalError> {
220        match expr {
221            RelationalExpr::Equality { left, op, right } => {
222                self.eval_equality(left, right, *op)
223            }
224
225            RelationalExpr::Comparison { left, op, right } => {
226                self.eval_comparison(left, right, *op)
227            }
228
229            RelationalExpr::Like { expr, pattern, escape, negated } => {
230                self.eval_like(expr, pattern, escape.as_ref(), *negated)
231            }
232
233            RelationalExpr::Between { expr, lower, upper, negated } => {
234                self.eval_between(expr, lower, upper, *negated)
235            }
236
237            RelationalExpr::In { expr, values, negated } => {
238                self.eval_in(expr, values, *negated)
239            }
240
241            RelationalExpr::IsNull { expr, negated } => {
242                self.eval_is_null(expr, *negated)
243            }
244        }
245    }
246
247    /// Evaluate equality/inequality operators
248    fn eval_equality(&self, left: &ValueExpr, right: &ValueExpr, op: EqualityOp)
249        -> Result<bool, EvalError>
250    {
251        let l_val = self.eval_value(left)?;
252        let r_val = self.eval_value(right)?;
253
254        // NULL handling
255        if l_val.is_null() || r_val.is_null() {
256            return Err(EvalError::NullInOperation {
257                operation: format!("{:?}", op),
258                context: "cannot compare NULL values (use IS NULL instead)".to_string(),
259            });
260        }
261
262        let equal = match (&l_val, &r_val) {
263            // Numeric comparisons
264            (SubValue::Integer(a), SubValue::Integer(b)) => a == b,
265            (SubValue::Float(a), SubValue::Float(b)) => a == b,
266            (SubValue::Integer(a), SubValue::Float(b)) => (*a as f64) == *b,
267            (SubValue::Float(a), SubValue::Integer(b)) => *a == (*b as f64),
268
269            // String comparisons
270            (SubValue::String(a), SubValue::String(b)) => a == b,
271
272            // Boolean comparisons (only for equality)
273            (SubValue::Boolean(a), SubValue::Boolean(b)) => a == b,
274
275            // Type mismatch
276            _ => return Err(EvalError::TypeError {
277                operation: format!("{:?}", op),
278                expected: "matching types".to_string(),
279                actual: format!("{} vs {}", l_val.type_name(), r_val.type_name()),
280                context: "equality comparison".to_string(),
281            }),
282        };
283
284        Ok(match op {
285            EqualityOp::Equal => equal,
286            EqualityOp::NotEqual => !equal,
287        })
288    }
289
290    /// Evaluate comparison operators (>, <, >=, <=)
291    fn eval_comparison(&self, left: &ValueExpr, right: &ValueExpr, op: ComparisonOp)
292        -> Result<bool, EvalError>
293    {
294        let l_val = self.eval_value(left)?;
295        let r_val = self.eval_value(right)?;
296
297        // NULL handling
298        if l_val.is_null() || r_val.is_null() {
299            return Err(EvalError::NullInOperation {
300                operation: format!("{:?}", op),
301                context: "cannot compare NULL values".to_string(),
302            });
303        }
304
305        match (&l_val, &r_val) {
306            // Numeric comparisons
307            (SubValue::Integer(a), SubValue::Integer(b)) => {
308                Ok(Self::apply_comparison_op(*a, *b, op))
309            }
310            (SubValue::Float(a), SubValue::Float(b)) => {
311                Ok(Self::apply_comparison_op(*a, *b, op))
312            }
313            (SubValue::Integer(a), SubValue::Float(b)) => {
314                Ok(Self::apply_comparison_op(*a as f64, *b, op))
315            }
316            (SubValue::Float(a), SubValue::Integer(b)) => {
317                Ok(Self::apply_comparison_op(*a, *b as f64, op))
318            }
319
320            // String comparisons (lexicographic)
321            (SubValue::String(a), SubValue::String(b)) => {
322                Ok(Self::apply_comparison_op(a, b, op))
323            }
324
325            // Boolean not allowed in comparisons
326            (SubValue::Boolean(_), _) | (_, SubValue::Boolean(_)) => {
327                Err(EvalError::TypeError {
328                    operation: format!("{:?}", op),
329                    expected: "numeric or string".to_string(),
330                    actual: "boolean".to_string(),
331                    context: "comparison operand".to_string(),
332                })
333            }
334
335            // Type mismatch
336            _ => Err(EvalError::TypeError {
337                operation: format!("{:?}", op),
338                expected: "matching types".to_string(),
339                actual: format!("{} vs {}", l_val.type_name(), r_val.type_name()),
340                context: "comparison".to_string(),
341            }),
342        }
343    }
344
345    fn apply_comparison_op<T: PartialOrd>(a: T, b: T, op: ComparisonOp) -> bool {
346        match op {
347            ComparisonOp::GreaterThan => a > b,
348            ComparisonOp::GreaterOrEqual => a >= b,
349            ComparisonOp::LessThan => a < b,
350            ComparisonOp::LessOrEqual => a <= b,
351        }
352    }
353
354    /// Evaluate LIKE operator with wildcards
355    fn eval_like(&self, expr: &ValueExpr, pattern: &str, escape: Option<&String>, negated: bool)
356        -> Result<bool, EvalError>
357    {
358        let val = self.eval_value(expr)?;
359
360        let string_val = match val {
361            SubValue::String(s) => s,
362            SubValue::Null => {
363                return Err(EvalError::NullInOperation {
364                    operation: "LIKE".to_string(),
365                    context: "cannot apply LIKE to NULL".to_string(),
366                });
367            }
368            _ => {
369                return Err(EvalError::TypeError {
370                    operation: "LIKE".to_string(),
371                    expected: "string".to_string(),
372                    actual: val.type_name(),
373                    context: "left operand".to_string(),
374                });
375            }
376        };
377
378        let matches = Self::match_pattern(&string_val, pattern, escape)?;
379        Ok(if negated { !matches } else { matches })
380    }
381
382    /// Pattern matching with SQL wildcards (% = any chars, _ = single char)
383    fn match_pattern(s: &str, pattern: &str, escape: Option<&String>) -> Result<bool, EvalError> {
384        let escape_char = escape.and_then(|e| e.chars().next());
385
386        // Convert SQL pattern to regex
387        let mut regex_pattern = String::from("^");
388        let mut chars = pattern.chars().peekable();
389
390        while let Some(ch) = chars.next() {
391            if Some(ch) == escape_char {
392                // Escaped character - treat next character literally
393                if let Some(next) = chars.next() {
394                    regex_pattern.push_str(&regex::escape(&next.to_string()));
395                }
396            } else if ch == '%' {
397                regex_pattern.push_str(".*");
398            } else if ch == '_' {
399                regex_pattern.push('.');
400            } else {
401                regex_pattern.push_str(&regex::escape(&ch.to_string()));
402            }
403        }
404        regex_pattern.push('$');
405
406        let re = regex::Regex::new(&regex_pattern)
407            .map_err(|e| EvalError::InvalidLiteral {
408                literal: pattern.to_string(),
409                literal_type: "LIKE pattern".to_string(),
410                error: format!("{}", e),
411            })?;
412
413        Ok(re.is_match(s))
414    }
415
416    /// Evaluate BETWEEN operator
417    fn eval_between(&self, expr: &ValueExpr, lower: &ValueExpr, upper: &ValueExpr, negated: bool)
418        -> Result<bool, EvalError>
419    {
420        let val = self.eval_value(expr)?;
421        let low = self.eval_value(lower)?;
422        let high = self.eval_value(upper)?;
423
424        // Check for NULL
425        if val.is_null() || low.is_null() || high.is_null() {
426            return Err(EvalError::NullInOperation {
427                operation: "BETWEEN".to_string(),
428                context: "cannot use NULL in BETWEEN".to_string(),
429            });
430        }
431
432        // All must be same comparable type
433        let in_range = match (&val, &low, &high) {
434            (SubValue::Integer(v), SubValue::Integer(l), SubValue::Integer(h)) => {
435                v >= l && v <= h
436            }
437            (SubValue::Float(v), SubValue::Float(l), SubValue::Float(h)) => {
438                v >= l && v <= h
439            }
440            (SubValue::String(v), SubValue::String(l), SubValue::String(h)) => {
441                v >= l && v <= h
442            }
443            // Mixed numeric types need coercion
444            _ => {
445                // Try numeric comparison with coercion
446                let v_num = Self::to_numeric(&val)?;
447                let l_num = Self::to_numeric(&low)?;
448                let h_num = Self::to_numeric(&high)?;
449                v_num >= l_num && v_num <= h_num
450            }
451        };
452
453        Ok(if negated { !in_range } else { in_range })
454    }
455
456    /// Evaluate IN operator
457    fn eval_in(&self, expr: &ValueExpr, values: &[ValueLiteral], negated: bool)
458        -> Result<bool, EvalError>
459    {
460        let val = self.eval_value(expr)?;
461
462        if val.is_null() {
463            return Err(EvalError::NullInOperation {
464                operation: "IN".to_string(),
465                context: "cannot use NULL in IN".to_string(),
466            });
467        }
468
469        // Type consistency of the values list is now guaranteed by the parser,
470        // so we only need to check if the left operand is type-compatible with the list
471        if !values.is_empty() {
472            let first_list_val = SubValue::from_literal(&values[0]);
473            let is_compatible = Self::are_types_compatible_for_in(&val, &first_list_val);
474
475            // If list is incompatible with left value, it's a type error
476            if !is_compatible {
477                return Err(EvalError::TypeError {
478                    operation: "IN".to_string(),
479                    expected: first_list_val.type_name(),
480                    actual: val.type_name(),
481                    context: "left operand type doesn't match list element types".to_string(),
482                });
483            }
484
485            // Note: Type consistency check for list elements removed - now done at parse time
486        }
487
488        let mut found = false;
489        for lit_val in values {
490            let list_val = SubValue::from_literal(lit_val);
491
492            // Check if values match (with type compatibility)
493            let matches = match (&val, &list_val) {
494                (SubValue::Integer(a), SubValue::Integer(b)) => a == b,
495                (SubValue::Float(a), SubValue::Float(b)) => a == b,
496                (SubValue::Integer(a), SubValue::Float(b)) => (*a as f64) == *b,
497                (SubValue::Float(a), SubValue::Integer(b)) => *a == (*b as f64),
498                (SubValue::String(a), SubValue::String(b)) => a == b,
499                (SubValue::Boolean(a), SubValue::Boolean(b)) => a == b,
500                (SubValue::Null, SubValue::Null) => true,
501                _ => false,  // Means no match since type mismatches are caused above
502            };
503
504            if matches {
505                found = true;
506                break;
507            }
508        }
509
510        Ok(if negated { !found } else { found })
511    }
512
513    /// Evaluate IS NULL operator
514    fn eval_is_null(&self, expr: &ValueExpr, negated: bool) -> Result<bool, EvalError> {
515        let val = self.eval_value(expr)?;
516        let is_null = val.is_null();
517        Ok(if negated { !is_null } else { is_null })
518    }
519
520    // ========================================================================
521    // VALUE EXPRESSION EVALUATION
522    // ========================================================================
523
524    /// Evaluate a value expression to a concrete value
525    fn eval_value(&self, expr: &ValueExpr) -> Result<SubValue, EvalError> {
526        match expr {
527            ValueExpr::Literal(lit) => Ok(SubValue::from_literal(lit)),
528
529            ValueExpr::Variable(name) => {
530                match self.value_map.get(name) {
531                    Some(rv) => Ok(SubValue::from_runtime(rv)),
532                    None => Err(EvalError::UnboundVariable {
533                        name: name.clone(),
534                    }),
535                }
536            }
537
538            ValueExpr::Add(l, r) => self.eval_arithmetic_add(l, r),
539            ValueExpr::Subtract(l, r) => self.eval_arithmetic_subtract(l, r),
540            ValueExpr::Multiply(l, r) => self.eval_arithmetic_multiply(l, r),
541            ValueExpr::Divide(l, r) => self.eval_arithmetic_divide(l, r),
542            ValueExpr::Modulo(l, r) => self.eval_arithmetic_modulo(l, r),
543
544            ValueExpr::UnaryPlus(e) => {
545                let val = self.eval_value(e)?;
546                match val {
547                    SubValue::Integer(i) => Ok(SubValue::Integer(i)),
548                    SubValue::Float(f) => Ok(SubValue::Float(f)),
549                    SubValue::Null => Err(EvalError::NullInOperation {
550                        operation: "unary plus".to_string(),
551                        context: "cannot apply unary plus to NULL".to_string(),
552                    }),
553                    _ => Err(EvalError::TypeError {
554                        operation: "unary plus".to_string(),
555                        expected: "numeric".to_string(),
556                        actual: val.type_name(),
557                        context: "operand".to_string(),
558                    }),
559                }
560            }
561
562            ValueExpr::UnaryMinus(e) => {
563                let val = self.eval_value(e)?;
564                match val {
565                    SubValue::Integer(i) => Ok(SubValue::Integer(-i)),
566                    SubValue::Float(f) => Ok(SubValue::Float(-f)),
567                    SubValue::Null => Err(EvalError::NullInOperation {
568                        operation: "unary minus".to_string(),
569                        context: "cannot apply unary minus to NULL".to_string(),
570                    }),
571                    _ => Err(EvalError::TypeError {
572                        operation: "unary minus".to_string(),
573                        expected: "numeric".to_string(),
574                        actual: val.type_name(),
575                        context: "operand".to_string(),
576                    }),
577                }
578            }
579        }
580    }
581
582    /// Arithmetic addition with type checking and coercion
583    fn eval_arithmetic_add(&self, l: &ValueExpr, r: &ValueExpr) -> Result<SubValue, EvalError> {
584        let left = self.eval_value(l)?;
585        let right = self.eval_value(r)?;
586
587        // Check for NULL
588        if left.is_null() || right.is_null() {
589            return Err(EvalError::NullInOperation {
590                operation: "addition".to_string(),
591                context: "cannot add NULL values".to_string(),
592            });
593        }
594
595        match (&left, &right) {
596            (SubValue::Integer(a), SubValue::Integer(b)) => {
597                Ok(SubValue::Integer(a + b))
598            }
599            (SubValue::Float(a), SubValue::Float(b)) => {
600                Ok(SubValue::Float(a + b))
601            }
602            // Type coercion: int + float = float
603            (SubValue::Integer(a), SubValue::Float(b)) => {
604                Ok(SubValue::Float(*a as f64 + b))
605            }
606            (SubValue::Float(a), SubValue::Integer(b)) => {
607                Ok(SubValue::Float(a + *b as f64))
608            }
609            _ => Err(EvalError::TypeError {
610                operation: "addition".to_string(),
611                expected: "numeric types".to_string(),
612                actual: format!("{} and {}", left.type_name(), right.type_name()),
613                context: "arithmetic operation".to_string(),
614            }),
615        }
616    }
617
618    /// Arithmetic subtraction
619    fn eval_arithmetic_subtract(&self, l: &ValueExpr, r: &ValueExpr) -> Result<SubValue, EvalError> {
620        let left = self.eval_value(l)?;
621        let right = self.eval_value(r)?;
622
623        if left.is_null() || right.is_null() {
624            return Err(EvalError::NullInOperation {
625                operation: "subtraction".to_string(),
626                context: "cannot subtract NULL values".to_string(),
627            });
628        }
629
630        match (&left, &right) {
631            (SubValue::Integer(a), SubValue::Integer(b)) => {
632                Ok(SubValue::Integer(a - b))
633            }
634            (SubValue::Float(a), SubValue::Float(b)) => {
635                Ok(SubValue::Float(a - b))
636            }
637            (SubValue::Integer(a), SubValue::Float(b)) => {
638                Ok(SubValue::Float(*a as f64 - b))
639            }
640            (SubValue::Float(a), SubValue::Integer(b)) => {
641                Ok(SubValue::Float(a - *b as f64))
642            }
643            _ => Err(EvalError::TypeError {
644                operation: "subtraction".to_string(),
645                expected: "numeric types".to_string(),
646                actual: format!("{} and {}", left.type_name(), right.type_name()),
647                context: "arithmetic operation".to_string(),
648            }),
649        }
650    }
651
652    /// Arithmetic multiplication
653    fn eval_arithmetic_multiply(&self, l: &ValueExpr, r: &ValueExpr) -> Result<SubValue, EvalError> {
654        let left = self.eval_value(l)?;
655        let right = self.eval_value(r)?;
656
657        if left.is_null() || right.is_null() {
658            return Err(EvalError::NullInOperation {
659                operation: "multiplication".to_string(),
660                context: "cannot multiply NULL values".to_string(),
661            });
662        }
663
664        match (&left, &right) {
665            (SubValue::Integer(a), SubValue::Integer(b)) => {
666                Ok(SubValue::Integer(a * b))
667            }
668            (SubValue::Float(a), SubValue::Float(b)) => {
669                Ok(SubValue::Float(a * b))
670            }
671            (SubValue::Integer(a), SubValue::Float(b)) => {
672                Ok(SubValue::Float(*a as f64 * b))
673            }
674            (SubValue::Float(a), SubValue::Integer(b)) => {
675                Ok(SubValue::Float(a * *b as f64))
676            }
677            _ => Err(EvalError::TypeError {
678                operation: "multiplication".to_string(),
679                expected: "numeric types".to_string(),
680                actual: format!("{} and {}", left.type_name(), right.type_name()),
681                context: "arithmetic operation".to_string(),
682            }),
683        }
684    }
685
686    /// Division with mandatory float coercion
687    fn eval_arithmetic_divide(&self, l: &ValueExpr, r: &ValueExpr) -> Result<SubValue, EvalError> {
688        let left = self.eval_value(l)?;
689        let right = self.eval_value(r)?;
690
691        if left.is_null() || right.is_null() {
692            return Err(EvalError::NullInOperation {
693                operation: "division".to_string(),
694                context: "cannot divide NULL values".to_string(),
695            });
696        }
697
698        // Convert both to float for division
699        let left_float = match left {
700            SubValue::Integer(i) => i as f64,
701            SubValue::Float(f) => f,
702            _ => return Err(EvalError::TypeError {
703                operation: "division".to_string(),
704                expected: "numeric".to_string(),
705                actual: left.type_name(),
706                context: "left operand".to_string(),
707            }),
708        };
709
710        let right_float = match right {
711            SubValue::Integer(i) => i as f64,
712            SubValue::Float(f) => f,
713            _ => return Err(EvalError::TypeError {
714                operation: "division".to_string(),
715                expected: "numeric".to_string(),
716                actual: right.type_name(),
717                context: "right operand".to_string(),
718            }),
719        };
720
721        if right_float == 0.0 {
722            return Err(EvalError::DivisionByZero {
723                expression: self.input.clone(),
724            });
725        }
726
727        Ok(SubValue::Float(left_float / right_float))
728    }
729
730    /// Arithmetic modulo
731    fn eval_arithmetic_modulo(&self, l: &ValueExpr, r: &ValueExpr) -> Result<SubValue, EvalError> {
732        let left = self.eval_value(l)?;
733        let right = self.eval_value(r)?;
734
735        if left.is_null() || right.is_null() {
736            return Err(EvalError::NullInOperation {
737                operation: "modulo".to_string(),
738                context: "cannot modulo NULL values".to_string(),
739            });
740        }
741
742        match (&left, &right) {
743            (SubValue::Integer(a), SubValue::Integer(b)) => {
744                if *b == 0 {
745                    return Err(EvalError::DivisionByZero {
746                        expression: self.input.clone(),
747                    });
748                }
749                Ok(SubValue::Integer(a % b))
750            }
751            (SubValue::Float(a), SubValue::Float(b)) => {
752                if *b == 0.0 {
753                    return Err(EvalError::DivisionByZero {
754                        expression: self.input.clone(),
755                    });
756                }
757                Ok(SubValue::Float(a % b))
758            }
759            (SubValue::Integer(a), SubValue::Float(b)) => {
760                if *b == 0.0 {
761                    return Err(EvalError::DivisionByZero {
762                        expression: self.input.clone(),
763                    });
764                }
765                Ok(SubValue::Float((*a as f64) % b))
766            }
767            (SubValue::Float(a), SubValue::Integer(b)) => {
768                if *b == 0 {
769                    return Err(EvalError::DivisionByZero {
770                        expression: self.input.clone(),
771                    });
772                }
773                Ok(SubValue::Float(a % (*b as f64)))
774            }
775            _ => Err(EvalError::TypeError {
776                operation: "modulo".to_string(),
777                expected: "numeric types".to_string(),
778                actual: format!("{} and {}", left.type_name(), right.type_name()),
779                context: "arithmetic operation".to_string(),
780            }),
781        }
782    }
783
784    // ========================================================================
785    // HELPER FUNCTIONS
786    // ========================================================================
787
788    fn to_numeric(val: &SubValue) -> Result<f64, EvalError> {
789        match val {
790            SubValue::Integer(i) => Ok(*i as f64),
791            SubValue::Float(f) => Ok(*f),
792            _ => Err(EvalError::TypeError {
793                operation: "numeric comparison".to_string(),
794                expected: "numeric".to_string(),
795                actual: val.type_name(),
796                context: "operand".to_string(),
797            }),
798        }
799    }
800
801    /// Check if two types are compatible for IN operator
802    /// (allows int/float mixing, but not string/numeric, etc.)
803    fn are_types_compatible_for_in(left: &SubValue, right: &SubValue) -> bool {
804        match (left, right) {
805            // Exact matches
806            (SubValue::Integer(_), SubValue::Integer(_)) => true,
807            (SubValue::Float(_), SubValue::Float(_)) => true,
808            (SubValue::String(_), SubValue::String(_)) => true,
809            (SubValue::Boolean(_), SubValue::Boolean(_)) => true,
810            (SubValue::Null, SubValue::Null) => true,
811            // Numeric type mixing is allowed
812            (SubValue::Integer(_), SubValue::Float(_)) => true,
813            (SubValue::Float(_), SubValue::Integer(_)) => true,
814            // Everything else is incompatible
815            _ => false,
816        }
817    }
818
819    fn runtime_type_name(rv: &RuntimeValue) -> String {
820        match rv {
821            RuntimeValue::Integer(_) => "integer".to_string(),
822            RuntimeValue::Float(_) => "float".to_string(),
823            RuntimeValue::String(_) => "string".to_string(),
824            RuntimeValue::Boolean(_) => "boolean".to_string(),
825            RuntimeValue::Null => "NULL".to_string(),
826        }
827    }
828}
829
830// ============================================================================
831// INTERNAL TYPES
832// ============================================================================
833
834/// Substituted values - what AST nodes become after variable substitution
835#[derive(Debug, Clone, PartialEq)]
836enum SubValue {
837    Integer(i64),
838    Float(f64),
839    String(String),
840    Boolean(bool),
841    Null,
842}
843
844impl SubValue {
845    /// Convert from RuntimeValue
846    fn from_runtime(rv: &RuntimeValue) -> Self {
847        match rv {
848            RuntimeValue::Integer(i) => SubValue::Integer(*i),
849            RuntimeValue::Float(f) => SubValue::Float(*f),
850            RuntimeValue::String(s) => SubValue::String(s.clone()),
851            RuntimeValue::Boolean(b) => SubValue::Boolean(*b),
852            RuntimeValue::Null => SubValue::Null,
853        }
854    }
855
856    /// Convert from ValueLiteral
857    fn from_literal(lit: &ValueLiteral) -> Self {
858        match lit {
859            ValueLiteral::Integer(i) => SubValue::Integer(*i),
860            ValueLiteral::Float(f) => SubValue::Float(*f),
861            ValueLiteral::String(s) => SubValue::String(s.clone()),
862            ValueLiteral::Boolean(b) => SubValue::Boolean(*b),
863            ValueLiteral::Null => SubValue::Null,
864        }
865    }
866
867    fn type_name(&self) -> String {
868        match self {
869            SubValue::Integer(_) => "integer".to_string(),
870            SubValue::Float(_) => "float".to_string(),
871            SubValue::String(_) => "string".to_string(),
872            SubValue::Boolean(_) => "boolean".to_string(),
873            SubValue::Null => "NULL".to_string(),
874        }
875    }
876
877    fn is_null(&self) -> bool {
878        matches!(self, SubValue::Null)
879    }
880}
881
882