1#![allow(clippy::only_used_in_recursion)]
31
32use radixdb_functions::registry::global_registry;
33use radixdb_functions::FunctionRegistry;
34use radixdb_sql::ast::{
35 BooleanLiteral, Expression, InfixExpression, InfixOperator, IntegerLiteral, PrefixExpression,
36 PrefixOperator,
37};
38use radixdb_sql::token::{Position, Token, TokenType};
39
40pub struct ExpressionSimplifier<'a> {
42 simplified: bool,
44 _marker: std::marker::PhantomData<&'a FunctionRegistry>,
45}
46
47impl Default for ExpressionSimplifier<'static> {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl ExpressionSimplifier<'static> {
54 pub fn new() -> Self {
56 Self::with_registry(global_registry())
57 }
58}
59
60impl<'a> ExpressionSimplifier<'a> {
61 pub fn with_registry(_function_registry: &'a FunctionRegistry) -> Self {
63 Self {
64 simplified: false,
65 _marker: std::marker::PhantomData,
66 }
67 }
68
69 pub fn was_simplified(&self) -> bool {
71 self.simplified
72 }
73
74 pub fn try_simplify(&mut self, expr: &Expression) -> Option<Expression> {
78 self.simplified = false;
79 let result = self.simplify_recursive(expr);
80 if self.simplified {
81 Some(result.unwrap_or_else(|| expr.clone()))
82 } else {
83 None
84 }
85 }
86
87 pub fn simplify(&mut self, expr: &Expression) -> Expression {
90 self.simplified = false;
91 self.simplify_recursive(expr)
92 .unwrap_or_else(|| expr.clone())
93 }
94
95 fn simplify_recursive(&mut self, expr: &Expression) -> Option<Expression> {
98 match expr {
99 Expression::Infix(infix) => self.simplify_infix(infix),
100 Expression::Prefix(prefix) => self.simplify_prefix(prefix),
101 Expression::Between(between) => self.simplify_between(between),
102 Expression::In(in_expr) => self.simplify_in(in_expr),
103 Expression::Exists(_) | Expression::ScalarSubquery(_) => None,
106 _ => None,
108 }
109 }
110
111 fn simplify_between(
113 &mut self,
114 between: &radixdb_sql::ast::BetweenExpression,
115 ) -> Option<Expression> {
116 let value_simplified = self.simplify_recursive(&between.expr);
117 let lower_simplified = self.simplify_recursive(&between.lower);
118 let upper_simplified = self.simplify_recursive(&between.upper);
119
120 let value = value_simplified.as_ref().unwrap_or(&between.expr);
121 let lower = lower_simplified.as_ref().unwrap_or(&between.lower);
122 let upper = upper_simplified.as_ref().unwrap_or(&between.upper);
123
124 if let (
126 Expression::IntegerLiteral(v),
127 Expression::IntegerLiteral(l),
128 Expression::IntegerLiteral(h),
129 ) = (value, lower, upper)
130 {
131 self.simplified = true;
132 let result = v.value >= l.value && v.value <= h.value;
133 return Some(if between.not {
134 self.make_bool(!result)
135 } else {
136 self.make_bool(result)
137 });
138 }
139
140 if value_simplified.is_some() || lower_simplified.is_some() || upper_simplified.is_some() {
142 Some(Expression::Between(radixdb_sql::ast::BetweenExpression {
143 token: between.token.clone(),
144 expr: Box::new(value_simplified.unwrap_or_else(|| (*between.expr).clone())),
145 lower: Box::new(lower_simplified.unwrap_or_else(|| (*between.lower).clone())),
146 upper: Box::new(upper_simplified.unwrap_or_else(|| (*between.upper).clone())),
147 not: between.not,
148 }))
149 } else {
150 None
151 }
152 }
153
154 fn simplify_in(&mut self, in_expr: &radixdb_sql::ast::InExpression) -> Option<Expression> {
156 let value_simplified = self.simplify_recursive(&in_expr.left);
157 let list_simplified = self.simplify_recursive(&in_expr.right);
158
159 if value_simplified.is_some() || list_simplified.is_some() {
161 Some(Expression::In(radixdb_sql::ast::InExpression {
162 token: in_expr.token.clone(),
163 left: Box::new(value_simplified.unwrap_or_else(|| (*in_expr.left).clone())),
164 right: Box::new(list_simplified.unwrap_or_else(|| (*in_expr.right).clone())),
165 not: in_expr.not,
166 }))
167 } else {
168 None
169 }
170 }
171
172 fn simplify_infix(&mut self, infix: &InfixExpression) -> Option<Expression> {
174 let left_simplified = self.simplify_recursive(&infix.left);
176 let right_simplified = self.simplify_recursive(&infix.right);
177
178 let left = left_simplified.as_ref().unwrap_or(&infix.left);
179 let right = right_simplified.as_ref().unwrap_or(&infix.right);
180
181 let simplified_result = match infix.op_type {
183 InfixOperator::And => self.simplify_and(left, right),
184 InfixOperator::Or => self.simplify_or(left, right),
185 InfixOperator::Equal => self.simplify_equal(left, right),
186 InfixOperator::NotEqual => self.simplify_not_equal(left, right),
187 InfixOperator::LessThan => self.simplify_less_than(left, right),
188 InfixOperator::LessEqual => self.simplify_less_equal(left, right),
189 InfixOperator::GreaterThan => self.simplify_greater_than(left, right),
190 InfixOperator::GreaterEqual => self.simplify_greater_equal(left, right),
191 InfixOperator::Add => self.simplify_add(left, right),
192 InfixOperator::Subtract => self.simplify_subtract(left, right),
193 InfixOperator::Multiply => self.simplify_multiply(left, right),
194 InfixOperator::Divide => self.simplify_divide(left, right),
195 _ => None,
196 };
197
198 if let Some(result) = simplified_result {
199 self.simplified = true;
200 return Some(result);
201 }
202
203 if left_simplified.is_some() || right_simplified.is_some() {
205 Some(Expression::Infix(InfixExpression {
206 token: infix.token.clone(),
207 left: Box::new(left_simplified.unwrap_or_else(|| (*infix.left).clone())),
208 operator: infix.operator.clone(),
209 op_type: infix.op_type,
210 right: Box::new(right_simplified.unwrap_or_else(|| (*infix.right).clone())),
211 }))
212 } else {
213 None
214 }
215 }
216
217 fn simplify_and(&self, left: &Expression, right: &Expression) -> Option<Expression> {
219 if self.is_always_true(left) {
221 return Some(right.clone());
222 }
223 if self.is_always_true(right) {
225 return Some(left.clone());
226 }
227 if self.is_always_false(left) {
229 return Some(left.clone());
230 }
231 if self.is_always_false(right) {
233 return Some(right.clone());
234 }
235 if self.expr_equals(left, right) {
237 return Some(left.clone());
238 }
239 self.try_merge_range_predicates(left, right)
241 }
242
243 fn simplify_or(&self, left: &Expression, right: &Expression) -> Option<Expression> {
245 if self.is_always_false(left) {
247 return Some(right.clone());
248 }
249 if self.is_always_false(right) {
251 return Some(left.clone());
252 }
253 if self.is_always_true(left) {
255 return Some(left.clone());
256 }
257 if self.is_always_true(right) {
259 return Some(right.clone());
260 }
261 if self.expr_equals(left, right) {
263 return Some(left.clone());
264 }
265 None
266 }
267
268 fn simplify_equal(&self, left: &Expression, right: &Expression) -> Option<Expression> {
270 if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::Equal) {
272 return Some(self.make_bool(result));
273 }
274 None
275 }
276
277 fn simplify_not_equal(&self, left: &Expression, right: &Expression) -> Option<Expression> {
279 if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::NotEqual) {
280 return Some(self.make_bool(result));
281 }
282 None
283 }
284
285 fn simplify_less_than(&self, left: &Expression, right: &Expression) -> Option<Expression> {
287 if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::LessThan) {
288 return Some(self.make_bool(result));
289 }
290 None
291 }
292
293 fn simplify_less_equal(&self, left: &Expression, right: &Expression) -> Option<Expression> {
295 if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::LessEqual) {
296 return Some(self.make_bool(result));
297 }
298 None
299 }
300
301 fn simplify_greater_than(&self, left: &Expression, right: &Expression) -> Option<Expression> {
303 if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::GreaterThan) {
304 return Some(self.make_bool(result));
305 }
306 None
307 }
308
309 fn simplify_greater_equal(&self, left: &Expression, right: &Expression) -> Option<Expression> {
311 if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::GreaterEqual) {
312 return Some(self.make_bool(result));
313 }
314 None
315 }
316
317 fn simplify_add(&self, left: &Expression, right: &Expression) -> Option<Expression> {
319 self.try_eval_arithmetic(left, right, InfixOperator::Add)
320 }
321
322 fn simplify_subtract(&self, left: &Expression, right: &Expression) -> Option<Expression> {
324 self.try_eval_arithmetic(left, right, InfixOperator::Subtract)
325 }
326
327 fn simplify_multiply(&self, left: &Expression, right: &Expression) -> Option<Expression> {
329 self.try_eval_arithmetic(left, right, InfixOperator::Multiply)
330 }
331
332 fn simplify_divide(&self, left: &Expression, right: &Expression) -> Option<Expression> {
334 self.try_eval_arithmetic(left, right, InfixOperator::Divide)
335 }
336
337 fn simplify_prefix(&mut self, prefix: &PrefixExpression) -> Option<Expression> {
339 let operand_simplified = self.simplify_recursive(&prefix.right);
340 let operand = operand_simplified.as_ref().unwrap_or(&prefix.right);
341
342 let simplified_result = match prefix.op_type {
343 PrefixOperator::Not => {
344 if self.is_always_true(operand) {
346 Some(self.make_bool(false))
347 }
348 else if self.is_always_false(operand) {
350 Some(self.make_bool(true))
351 }
352 else if let Expression::Prefix(inner) = operand {
354 if inner.op_type == PrefixOperator::Not {
355 Some((*inner.right).clone())
356 } else {
357 None
358 }
359 } else {
360 None
361 }
362 }
363 PrefixOperator::Negate => {
364 if let Expression::IntegerLiteral(lit) = operand {
366 lit.value.checked_neg().map(|value| self.make_int(value))
367 } else {
368 None
369 }
370 }
371 PrefixOperator::Plus => {
372 match operand {
374 Expression::IntegerLiteral(_) | Expression::FloatLiteral(_) => {
375 Some(operand.clone())
376 }
377 _ => None,
378 }
379 }
380 _ => None,
381 };
382
383 if let Some(result) = simplified_result {
384 self.simplified = true;
385 return Some(result);
386 }
387
388 operand_simplified.map(|simplified_operand| {
390 Expression::Prefix(PrefixExpression {
391 token: prefix.token.clone(),
392 operator: prefix.operator.clone(),
393 op_type: prefix.op_type,
394 right: Box::new(simplified_operand),
395 })
396 })
397 }
398
399 fn is_always_true(&self, expr: &Expression) -> bool {
401 match expr {
402 Expression::BooleanLiteral(b) => b.value,
403 _ => false,
404 }
405 }
406
407 fn is_always_false(&self, expr: &Expression) -> bool {
409 match expr {
410 Expression::BooleanLiteral(b) => !b.value,
411 Expression::Infix(infix) => {
412 if infix.op_type == InfixOperator::Equal {
414 if let (Expression::IntegerLiteral(l), Expression::IntegerLiteral(r)) =
415 (infix.left.as_ref(), infix.right.as_ref())
416 {
417 return l.value != r.value;
418 }
419 }
420 false
421 }
422 _ => false,
423 }
424 }
425
426 fn expr_equals(&self, a: &Expression, b: &Expression) -> bool {
428 match (a, b) {
429 (Expression::Identifier(a), Expression::Identifier(b)) => a.value == b.value,
430 (Expression::QualifiedIdentifier(a), Expression::QualifiedIdentifier(b)) => {
431 a.qualifier.value == b.qualifier.value && a.name.value == b.name.value
432 }
433 (Expression::IntegerLiteral(a), Expression::IntegerLiteral(b)) => a.value == b.value,
434 (Expression::FloatLiteral(a), Expression::FloatLiteral(b)) => a.value == b.value,
435 (Expression::StringLiteral(a), Expression::StringLiteral(b)) => a.value == b.value,
436 (Expression::BooleanLiteral(a), Expression::BooleanLiteral(b)) => a.value == b.value,
437 (Expression::NullLiteral(_), Expression::NullLiteral(_)) => true,
438 (Expression::Infix(a), Expression::Infix(b)) => {
439 a.op_type == b.op_type
440 && self.expr_equals(&a.left, &b.left)
441 && self.expr_equals(&a.right, &b.right)
442 }
443 (Expression::Prefix(a), Expression::Prefix(b)) => {
444 a.op_type == b.op_type && self.expr_equals(&a.right, &b.right)
445 }
446 _ => false,
447 }
448 }
449
450 fn try_eval_comparison(
452 &self,
453 left: &Expression,
454 right: &Expression,
455 op: InfixOperator,
456 ) -> Option<bool> {
457 match (left, right) {
458 (Expression::IntegerLiteral(l), Expression::IntegerLiteral(r)) => Some(match op {
459 InfixOperator::Equal => l.value == r.value,
460 InfixOperator::NotEqual => l.value != r.value,
461 InfixOperator::LessThan => l.value < r.value,
462 InfixOperator::LessEqual => l.value <= r.value,
463 InfixOperator::GreaterThan => l.value > r.value,
464 InfixOperator::GreaterEqual => l.value >= r.value,
465 _ => return None,
466 }),
467 (Expression::FloatLiteral(l), Expression::FloatLiteral(r)) => {
468 let left = radixdb_core::Value::Float(l.value);
469 let right = radixdb_core::Value::Float(r.value);
470 Some(match op {
471 InfixOperator::Equal => left == right,
472 InfixOperator::NotEqual => left != right,
473 InfixOperator::LessThan => left < right,
474 InfixOperator::LessEqual => left <= right,
475 InfixOperator::GreaterThan => left > right,
476 InfixOperator::GreaterEqual => left >= right,
477 _ => return None,
478 })
479 }
480 (Expression::StringLiteral(l), Expression::StringLiteral(r)) => Some(match op {
481 InfixOperator::Equal => l.value == r.value,
482 InfixOperator::NotEqual => l.value != r.value,
483 InfixOperator::LessThan => l.value < r.value,
484 InfixOperator::LessEqual => l.value <= r.value,
485 InfixOperator::GreaterThan => l.value > r.value,
486 InfixOperator::GreaterEqual => l.value >= r.value,
487 _ => return None,
488 }),
489 (Expression::BooleanLiteral(l), Expression::BooleanLiteral(r)) => Some(match op {
490 InfixOperator::Equal => l.value == r.value,
491 InfixOperator::NotEqual => l.value != r.value,
492 _ => return None,
493 }),
494 _ => None,
495 }
496 }
497
498 fn try_eval_arithmetic(
500 &self,
501 left: &Expression,
502 right: &Expression,
503 op: InfixOperator,
504 ) -> Option<Expression> {
505 match (left, right) {
506 (Expression::IntegerLiteral(l), Expression::IntegerLiteral(r)) => {
507 let result = match op {
508 InfixOperator::Add => l.value.checked_add(r.value)?,
509 InfixOperator::Subtract => l.value.checked_sub(r.value)?,
510 InfixOperator::Multiply => l.value.checked_mul(r.value)?,
511 InfixOperator::Divide => {
512 if r.value == 0 {
513 return None;
514 }
515 l.value.checked_div(r.value)?
516 }
517 InfixOperator::Modulo => {
518 if r.value == 0 {
519 return None;
520 }
521 l.value.checked_rem(r.value)?
522 }
523 _ => return None,
524 };
525 Some(self.make_int(result))
526 }
527 _ => None,
528 }
529 }
530
531 fn try_merge_range_predicates(
535 &self,
536 left: &Expression,
537 right: &Expression,
538 ) -> Option<Expression> {
539 let (left_infix, right_infix) = match (left, right) {
540 (Expression::Infix(l), Expression::Infix(r)) => (l, r),
541 _ => return None,
542 };
543
544 if !self.same_column(&left_infix.left, &right_infix.left) {
546 return None;
547 }
548
549 let left_val = self.extract_int_literal(&left_infix.right)?;
551 let right_val = self.extract_int_literal(&right_infix.right)?;
552
553 match (left_infix.op_type, right_infix.op_type) {
555 (InfixOperator::GreaterThan, InfixOperator::GreaterThan)
557 | (InfixOperator::GreaterEqual, InfixOperator::GreaterEqual) => {
558 if left_val >= right_val {
559 Some(left.clone())
560 } else {
561 Some(right.clone())
562 }
563 }
564 (InfixOperator::LessThan, InfixOperator::LessThan)
566 | (InfixOperator::LessEqual, InfixOperator::LessEqual) => {
567 if left_val <= right_val {
568 Some(left.clone())
569 } else {
570 Some(right.clone())
571 }
572 }
573 (InfixOperator::GreaterThan, InfixOperator::GreaterEqual) if left_val == right_val => {
575 Some(left.clone())
576 }
577 (InfixOperator::GreaterEqual, InfixOperator::GreaterThan) if left_val == right_val => {
578 Some(right.clone())
579 }
580 (InfixOperator::LessThan, InfixOperator::LessEqual) if left_val == right_val => {
582 Some(left.clone())
583 }
584 (InfixOperator::LessEqual, InfixOperator::LessThan) if left_val == right_val => {
585 Some(right.clone())
586 }
587 _ => None,
588 }
589 }
590
591 fn same_column(&self, left: &Expression, right: &Expression) -> bool {
592 match (left, right) {
593 (Expression::Identifier(left), Expression::Identifier(right)) => {
594 Self::same_identifier(left, right)
595 }
596 (Expression::QualifiedIdentifier(left), Expression::QualifiedIdentifier(right)) => {
597 Self::same_identifier(&left.qualifier, &right.qualifier)
598 && Self::same_identifier(&left.name, &right.name)
599 }
600 _ => false,
601 }
602 }
603
604 fn same_identifier(
605 left: &radixdb_sql::ast::Identifier,
606 right: &radixdb_sql::ast::Identifier,
607 ) -> bool {
608 left.token.quoted == right.token.quoted
609 && if left.token.quoted {
610 left.value == right.value
611 } else {
612 left.value_lower == right.value_lower
613 }
614 }
615
616 fn extract_int_literal(&self, expr: &Expression) -> Option<i64> {
618 match expr {
619 Expression::IntegerLiteral(lit) => Some(lit.value),
620 _ => None,
621 }
622 }
623
624 fn make_bool(&self, value: bool) -> Expression {
626 Expression::BooleanLiteral(BooleanLiteral {
627 token: Token::new(
628 TokenType::Keyword,
629 if value { "TRUE" } else { "FALSE" },
630 Position::default(),
631 ),
632 value,
633 })
634 }
635
636 fn make_int(&self, value: i64) -> Expression {
638 Expression::IntegerLiteral(IntegerLiteral {
639 token: Token::new(TokenType::Integer, value.to_string(), Position::default()),
640 value,
641 })
642 }
643}
644
645pub fn simplify_expression(expr: &Expression) -> Expression {
647 let mut simplifier = ExpressionSimplifier::new();
648 simplifier.simplify(expr)
649}
650
651pub fn simplify_expression_fixed_point(expr: &Expression) -> Expression {
653 let mut simplifier = ExpressionSimplifier::new();
654 let mut current = expr.clone();
655 let mut iterations = 0;
656 const MAX_ITERATIONS: usize = 10;
657
658 loop {
659 let simplified = simplifier.try_simplify(¤t);
660 if simplified.is_none() || iterations >= MAX_ITERATIONS {
661 break;
662 }
663 current = simplified.unwrap();
664 iterations += 1;
665 }
666
667 current
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673 use radixdb_sql::ast::{Identifier, QualifiedIdentifier};
674
675 fn make_int_lit(value: i64) -> Expression {
676 Expression::IntegerLiteral(IntegerLiteral {
677 token: Token::new(TokenType::Integer, value.to_string(), Position::default()),
678 value,
679 })
680 }
681
682 fn make_bool_lit(value: bool) -> Expression {
683 Expression::BooleanLiteral(BooleanLiteral {
684 token: Token::new(
685 TokenType::Keyword,
686 if value { "TRUE" } else { "FALSE" },
687 Position::default(),
688 ),
689 value,
690 })
691 }
692
693 fn make_float_lit(value: f64) -> Expression {
694 Expression::FloatLiteral(radixdb_sql::ast::FloatLiteral {
695 token: Token::new(TokenType::Float, value.to_string(), Position::default()),
696 value,
697 })
698 }
699
700 fn make_identifier(name: &str) -> Expression {
701 Expression::Identifier(Identifier::new(
702 Token::new(TokenType::Identifier, name, Position::default()),
703 name.to_string(),
704 ))
705 }
706
707 fn make_qualified_identifier(qualifier: &str, name: &str) -> Expression {
708 Expression::QualifiedIdentifier(QualifiedIdentifier {
709 token: Token::new(
710 TokenType::Identifier,
711 format!("{qualifier}.{name}"),
712 Position::default(),
713 ),
714 qualifier: Box::new(Identifier::new(
715 Token::new(TokenType::Identifier, qualifier, Position::default()),
716 qualifier.to_string(),
717 )),
718 intermediate: None,
719 name: Box::new(Identifier::new(
720 Token::new(TokenType::Identifier, name, Position::default()),
721 name.to_string(),
722 )),
723 })
724 }
725
726 fn make_prefix(op: PrefixOperator, right: Expression) -> Expression {
727 let operator = match op {
728 PrefixOperator::Negate => "-",
729 PrefixOperator::Plus => "+",
730 PrefixOperator::Not => "NOT",
731 _ => "?",
732 };
733 Expression::Prefix(PrefixExpression {
734 token: Token::new(TokenType::Operator, operator, Position::default()),
735 operator: operator.into(),
736 op_type: op,
737 right: Box::new(right),
738 })
739 }
740
741 fn make_infix(left: Expression, op: InfixOperator, right: Expression) -> Expression {
742 let op_str = match op {
743 InfixOperator::And => "AND",
744 InfixOperator::Or => "OR",
745 InfixOperator::Equal => "=",
746 InfixOperator::NotEqual => "<>",
747 InfixOperator::LessThan => "<",
748 InfixOperator::GreaterThan => ">",
749 InfixOperator::Add => "+",
750 InfixOperator::Subtract => "-",
751 InfixOperator::Multiply => "*",
752 _ => "?",
753 };
754 Expression::Infix(InfixExpression {
755 token: Token::new(TokenType::Operator, op_str, Position::default()),
756 left: Box::new(left),
757 operator: op_str.into(),
758 op_type: op,
759 right: Box::new(right),
760 })
761 }
762
763 #[test]
764 fn test_constant_folding_arithmetic() {
765 let expr = make_infix(make_int_lit(2), InfixOperator::Add, make_int_lit(3));
766 let result = simplify_expression(&expr);
767
768 if let Expression::IntegerLiteral(lit) = result {
769 assert_eq!(lit.value, 5);
770 } else {
771 panic!("Expected IntegerLiteral");
772 }
773 }
774
775 #[test]
776 fn test_constant_folding_comparison() {
777 let expr = make_infix(make_int_lit(1), InfixOperator::Equal, make_int_lit(1));
779 let result = simplify_expression(&expr);
780
781 if let Expression::BooleanLiteral(lit) = result {
782 assert!(lit.value);
783 } else {
784 panic!("Expected BooleanLiteral(true)");
785 }
786
787 let expr = make_infix(make_int_lit(1), InfixOperator::Equal, make_int_lit(2));
789 let result = simplify_expression(&expr);
790
791 if let Expression::BooleanLiteral(lit) = result {
792 assert!(!lit.value);
793 } else {
794 panic!("Expected BooleanLiteral(false)");
795 }
796 }
797
798 #[test]
799 fn test_boolean_and_simplification() {
800 let x = make_identifier("x");
802 let expr = make_infix(make_bool_lit(true), InfixOperator::And, x.clone());
803 let result = simplify_expression(&expr);
804
805 if let Expression::Identifier(id) = result {
806 assert_eq!(id.value, "x");
807 } else {
808 panic!("Expected Identifier 'x'");
809 }
810
811 let expr = make_infix(make_bool_lit(false), InfixOperator::And, x.clone());
813 let result = simplify_expression(&expr);
814
815 if let Expression::BooleanLiteral(lit) = result {
816 assert!(!lit.value);
817 } else {
818 panic!("Expected BooleanLiteral(false)");
819 }
820 }
821
822 #[test]
823 fn test_boolean_or_simplification() {
824 let x = make_identifier("x");
826 let expr = make_infix(make_bool_lit(false), InfixOperator::Or, x.clone());
827 let result = simplify_expression(&expr);
828
829 if let Expression::Identifier(id) = result {
830 assert_eq!(id.value, "x");
831 } else {
832 panic!("Expected Identifier 'x'");
833 }
834
835 let expr = make_infix(make_bool_lit(true), InfixOperator::Or, x.clone());
837 let result = simplify_expression(&expr);
838
839 if let Expression::BooleanLiteral(lit) = result {
840 assert!(lit.value);
841 } else {
842 panic!("Expected BooleanLiteral(true)");
843 }
844 }
845
846 #[test]
847 fn test_arithmetic_identity() {
848 let x = make_identifier("x");
849
850 let expr = make_infix(x.clone(), InfixOperator::Add, make_int_lit(0));
852 let result = simplify_expression(&expr);
853 assert_eq!(result, expr);
854
855 let expr = make_infix(x.clone(), InfixOperator::Multiply, make_int_lit(1));
856 let result = simplify_expression(&expr);
857 assert_eq!(result, expr);
858
859 let expr = make_infix(x.clone(), InfixOperator::Multiply, make_int_lit(0));
861 let result = simplify_expression(&expr);
862 assert_eq!(result, expr);
863 }
864
865 #[test]
866 fn test_range_predicate_merge() {
867 let a = make_identifier("a");
868
869 let left = make_infix(a.clone(), InfixOperator::GreaterThan, make_int_lit(5));
871 let right = make_infix(a.clone(), InfixOperator::GreaterThan, make_int_lit(3));
872 let expr = make_infix(left, InfixOperator::And, right);
873 let result = simplify_expression(&expr);
874
875 if let Expression::Infix(infix) = result {
877 if let Expression::IntegerLiteral(lit) = infix.right.as_ref() {
878 assert_eq!(lit.value, 5);
879 } else {
880 panic!("Expected IntegerLiteral(5)");
881 }
882 } else {
883 panic!("Expected Infix expression");
884 }
885 }
886
887 #[test]
888 fn test_idempotent_and() {
889 let x = make_identifier("x");
890
891 let expr = make_infix(x.clone(), InfixOperator::And, x.clone());
893 let result = simplify_expression(&expr);
894 assert!(matches!(result, Expression::Identifier(_)));
895 }
896
897 #[test]
898 fn test_idempotent_or() {
899 let x = make_identifier("x");
900
901 let expr = make_infix(x.clone(), InfixOperator::Or, x.clone());
903 let result = simplify_expression(&expr);
904 assert!(matches!(result, Expression::Identifier(_)));
905 }
906
907 #[test]
908 fn test_self_comparison() {
909 let x = make_identifier("x");
910
911 let expr = make_infix(x.clone(), InfixOperator::Equal, x.clone());
913 let result = simplify_expression(&expr);
914 assert_eq!(result, expr);
915
916 let expr = make_infix(x.clone(), InfixOperator::LessThan, x.clone());
918 let result = simplify_expression(&expr);
919 assert_eq!(result, expr);
920 }
921
922 #[test]
923 fn test_subtraction_identity() {
924 let x = make_identifier("x");
925
926 let expr = make_infix(x.clone(), InfixOperator::Subtract, x.clone());
928 let result = simplify_expression(&expr);
929 assert_eq!(result, expr);
930 }
931
932 #[test]
933 fn test_no_clone_for_unchanged() {
934 let mut simplifier = ExpressionSimplifier::new();
936 let x = make_identifier("x");
937
938 let result = simplifier.try_simplify(&x);
940 assert!(result.is_none());
941 assert!(!simplifier.was_simplified());
942 }
943
944 #[test]
945 fn test_r5_l03_simplifier_preserves_unproven_semantics() {
946 let x = make_identifier("x");
947 let schema_dependent = [
948 make_infix(x.clone(), InfixOperator::Equal, x.clone()),
949 make_infix(x.clone(), InfixOperator::NotEqual, x.clone()),
950 make_infix(x.clone(), InfixOperator::Subtract, x.clone()),
951 make_infix(x.clone(), InfixOperator::Multiply, make_int_lit(0)),
952 ];
953 for expression in schema_dependent {
954 assert_eq!(simplify_expression(&expression), expression);
955 }
956
957 let double_min = make_prefix(
958 PrefixOperator::Negate,
959 make_prefix(PrefixOperator::Negate, make_int_lit(i64::MIN)),
960 );
961 let simplified = std::panic::catch_unwind(|| simplify_expression(&double_min));
962 assert_eq!(simplified.ok(), Some(double_min));
963
964 let left = make_infix(
965 make_qualified_identifier("a", "id"),
966 InfixOperator::GreaterThan,
967 make_int_lit(5),
968 );
969 let right = make_infix(
970 make_qualified_identifier("b", "id"),
971 InfixOperator::GreaterThan,
972 make_int_lit(3),
973 );
974 let qualified_range = make_infix(left, InfixOperator::And, right);
975 assert_eq!(simplify_expression(&qualified_range), qualified_range);
976 }
977
978 #[test]
979 fn v2_r5_float_folding_uses_canonical_nan_and_zero_contract() {
980 let nan_a = make_float_lit(f64::from_bits(0x7ff8_0000_0000_0001));
981 let nan_b = make_float_lit(f64::from_bits(0x7ff8_0000_0000_0002));
982 let equal = simplify_expression(&make_infix(
983 nan_a.clone(),
984 InfixOperator::Equal,
985 nan_b.clone(),
986 ));
987 assert!(matches!(
988 equal,
989 Expression::BooleanLiteral(BooleanLiteral { value: true, .. })
990 ));
991
992 let less = simplify_expression(&make_infix(
993 make_float_lit(1.0),
994 InfixOperator::LessThan,
995 nan_a,
996 ));
997 assert!(matches!(
998 less,
999 Expression::BooleanLiteral(BooleanLiteral { value: true, .. })
1000 ));
1001
1002 let zero_equal = simplify_expression(&make_infix(
1003 make_float_lit(-0.0),
1004 InfixOperator::Equal,
1005 make_float_lit(0.0),
1006 ));
1007 assert!(matches!(
1008 zero_equal,
1009 Expression::BooleanLiteral(BooleanLiteral { value: true, .. })
1010 ));
1011 }
1012}