//! ConditionPatternBox: if条件パターン判定 //! //! Phase 219 regression fix: if条件が「単純比較」かどうかを判定 //! //! ## 問題 //! //! Phase 219で `is_if_sum_pattern()` をAST-basedに変更した結果、 //! `loop_if_phi.hako` のような複雑条件 (`i % 2 == 1`) を //! if-sumパターンと誤判定してしまう問題が発生。 //! //! ## 解決策 //! //! ConditionPatternBox を導入し、if条件が「単純比較」かどうかを判定する。 //! AST-based lowerer は単純比較のみ処理可能とし、複雑条件はlegacy modeへフォールバック。 //! //! ## 単純比較の定義 //! //! 以下のパターンのみ if-sum lowerer で処理可能: //! - `var > literal` (e.g., `i > 0`) //! - `var < literal` (e.g., `i < 10`) //! - `var >= literal` //! - `var <= literal` //! - `var == literal` //! - `var != literal` //! //! ## 複雑条件(legacy mode へフォールバック) //! //! - `i % 2 == 1` (BinaryOp in LHS) //! - `a && b` (複合条件) //! - `method_call() > 0` (MethodCall) //! - その他 use crate::ast::{ASTNode, BinaryOperator}; /// if条件のパターン種別 #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConditionPattern { /// 単純比較: var CmpOp literal (e.g., i > 0) SimpleComparison, /// 複雑条件: BinaryOp, MethodCall, etc. Complex, } /// if条件ASTを分析してパターンを判定 /// /// # Arguments /// /// * `cond` - if条件のASTノード /// /// # Returns /// /// - `ConditionPattern::SimpleComparison` - 単純比較(AST-based lowerer で処理可能) /// - `ConditionPattern::Complex` - 複雑条件(legacy mode へフォールバック) /// /// # Examples /// /// ```rust /// // Simple: i > 0 /// let simple = ASTNode::BinaryOp { /// operator: BinaryOperator::Greater, /// left: Box::new(ASTNode::Variable { name: "i".to_string(), span: Span::unknown() }), /// right: Box::new(ASTNode::Literal { value: LiteralValue::Integer(0), span: Span::unknown() }), /// span: Span::unknown(), /// }; /// assert_eq!(analyze_condition_pattern(&simple), ConditionPattern::SimpleComparison); /// /// // Complex: i % 2 == 1 /// let complex = ASTNode::BinaryOp { /// operator: BinaryOperator::Equal, /// left: Box::new(ASTNode::BinaryOp { ... }), // BinaryOp in LHS /// right: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), /// span: Span::unknown(), /// }; /// assert_eq!(analyze_condition_pattern(&complex), ConditionPattern::Complex); /// ``` pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern { match cond { // Comparison operators: ==, !=, <, >, <=, >= ASTNode::BinaryOp { operator, left, right, .. } => { // Check if operator is a comparison let is_comparison = matches!( operator, BinaryOperator::Equal | BinaryOperator::NotEqual | BinaryOperator::Less | BinaryOperator::Greater | BinaryOperator::LessEqual | BinaryOperator::GreaterEqual ); if !is_comparison { // Not a comparison (e.g., And, Or) → Complex return ConditionPattern::Complex; } // Check if LHS is a simple variable let left_is_var = matches!(left.as_ref(), ASTNode::Variable { .. }); // Check if RHS is a literal let right_is_literal = matches!(right.as_ref(), ASTNode::Literal { .. }); if left_is_var && right_is_literal { ConditionPattern::SimpleComparison } else { // Complex LHS/RHS (e.g., i % 2 == 1, method_call() > 0) ConditionPattern::Complex } } // Any other node type → Complex _ => ConditionPattern::Complex, } } /// if条件が単純比較かどうか /// /// # Arguments /// /// * `cond` - if条件のASTノード /// /// # Returns /// /// `true` if 単純比較(AST-based lowerer で処理可能) /// /// # Examples /// /// ```rust /// // i > 0 → true /// assert!(is_simple_comparison(&simple_condition)); /// /// // i % 2 == 1 → false /// assert!(!is_simple_comparison(&complex_condition)); /// ``` pub fn is_simple_comparison(cond: &ASTNode) -> bool { analyze_condition_pattern(cond) == ConditionPattern::SimpleComparison } #[cfg(test)] mod tests { use super::*; use crate::ast::{LiteralValue, Span}; // Helper: Create a simple variable node fn var(name: &str) -> ASTNode { ASTNode::Variable { name: name.to_string(), span: Span::unknown(), } } // Helper: Create an integer literal node fn int_lit(value: i64) -> ASTNode { ASTNode::Literal { value: LiteralValue::Integer(value), span: Span::unknown(), } } // Helper: Create a BinaryOp node fn binop(op: BinaryOperator, left: ASTNode, right: ASTNode) -> ASTNode { ASTNode::BinaryOp { operator: op, left: Box::new(left), right: Box::new(right), span: Span::unknown(), } } #[test] fn test_simple_comparison_greater() { // i > 0 let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0)); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert!(is_simple_comparison(&cond)); } #[test] fn test_simple_comparison_less() { // i < 10 let cond = binop(BinaryOperator::Less, var("i"), int_lit(10)); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert!(is_simple_comparison(&cond)); } #[test] fn test_simple_comparison_equal() { // i == 5 let cond = binop(BinaryOperator::Equal, var("i"), int_lit(5)); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert!(is_simple_comparison(&cond)); } #[test] fn test_simple_comparison_not_equal() { // i != 0 let cond = binop(BinaryOperator::NotEqual, var("i"), int_lit(0)); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert!(is_simple_comparison(&cond)); } #[test] fn test_complex_binop_in_lhs() { // i % 2 == 1 (BinaryOp in LHS) let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2)); let cond = binop(BinaryOperator::Equal, lhs, int_lit(1)); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); assert!(!is_simple_comparison(&cond)); } #[test] fn test_complex_binop_in_rhs() { // i == a + b (BinaryOp in RHS) let rhs = binop(BinaryOperator::Add, var("a"), var("b")); let cond = binop(BinaryOperator::Equal, var("i"), rhs); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); assert!(!is_simple_comparison(&cond)); } #[test] fn test_complex_logical_and() { // a && b (logical And) let cond = binop(BinaryOperator::And, var("a"), var("b")); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); assert!(!is_simple_comparison(&cond)); } #[test] fn test_complex_logical_or() { // a || b (logical Or) let cond = binop(BinaryOperator::Or, var("a"), var("b")); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); assert!(!is_simple_comparison(&cond)); } #[test] fn test_complex_method_call() { // method_call() > 0 (MethodCall in LHS) let method_call = ASTNode::MethodCall { object: Box::new(var("obj")), method: "get".to_string(), arguments: vec![], span: Span::unknown(), }; let cond = binop(BinaryOperator::Greater, method_call, int_lit(0)); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); assert!(!is_simple_comparison(&cond)); } #[test] fn test_complex_non_binary_op() { // Just a variable (not a comparison) let cond = var("condition"); assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); assert!(!is_simple_comparison(&cond)); } }