sqlexpr_rust/
evaluator.rs

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