feat(joinir): Phase 222-2 ConditionPatternBox normalization implementation
Phase 222: If Condition Normalization - Part 2
Goal: Support '0 < i', 'i > j' patterns in addition to 'i > 0'
Changes:
1. condition_pattern.rs (+160 lines):
- Added ConditionValue enum (Variable | Literal)
- Added NormalizedCondition struct (left_var, op, right)
- Added flip_compare_op() for operator reversal
- Added binary_op_to_compare_op() converter
- Added normalize_comparison() main normalization function
- Extended analyze_condition_pattern() to accept 3 cases:
* Phase 219: var CmpOp literal (e.g., i > 0)
* Phase 222: literal CmpOp var (e.g., 0 < i) → normalized
* Phase 222: var CmpOp var (e.g., i > j)
- Added 9 unit tests (all passing)
2. loop_update_summary.rs (cleanup):
- Commented out obsolete test_typical_index_names
- Function is_typical_index_name() was removed in earlier phase
Test results:
- 7 normalization tests: PASS ✅
- 2 pattern analysis tests: PASS ✅
Next: Phase 222-3 - integrate normalization into is_if_sum_pattern()
Status: Ready for integration
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
//! ConditionPatternBox: if条件パターン判定
|
||||
//! ConditionPatternBox: if条件パターン判定と正規化
|
||||
//!
|
||||
//! Phase 219 regression fix: if条件が「単純比較」かどうかを判定
|
||||
//! Phase 222: 左辺変数・右辺変数の両方をサポートする正規化を追加
|
||||
//!
|
||||
//! ## 問題
|
||||
//!
|
||||
@ -8,20 +9,21 @@
|
||||
//! `loop_if_phi.hako` のような複雑条件 (`i % 2 == 1`) を
|
||||
//! if-sumパターンと誤判定してしまう問題が発生。
|
||||
//!
|
||||
//! Phase 221で発見した制約:if条件が `0 < i` や `i > j` のような形式を拒否。
|
||||
//!
|
||||
//! ## 解決策
|
||||
//!
|
||||
//! ConditionPatternBox を導入し、if条件が「単純比較」かどうかを判定する。
|
||||
//! AST-based lowerer は単純比較のみ処理可能とし、複雑条件はlegacy modeへフォールバック。
|
||||
//!
|
||||
//! ## 単純比較の定義
|
||||
//! Phase 222: 左右反転(literal on left → var on left)と変数同士の比較をサポート。
|
||||
//!
|
||||
//! 以下のパターンのみ if-sum lowerer で処理可能:
|
||||
//! - `var > literal` (e.g., `i > 0`)
|
||||
//! - `var < literal` (e.g., `i < 10`)
|
||||
//! - `var >= literal`
|
||||
//! - `var <= literal`
|
||||
//! - `var == literal`
|
||||
//! - `var != literal`
|
||||
//! ## 単純比較の定義(Phase 222拡張版)
|
||||
//!
|
||||
//! 以下のパターンを if-sum lowerer で処理可能:
|
||||
//! - **Phase 219**: `var > literal` (e.g., `i > 0`)
|
||||
//! - **Phase 222**: `literal < var` → `var > literal` に正規化
|
||||
//! - **Phase 222**: `var > var` (e.g., `i > j`) - 変数同士の比較
|
||||
//!
|
||||
//! ## 複雑条件(legacy mode へフォールバック)
|
||||
//!
|
||||
@ -30,7 +32,8 @@
|
||||
//! - `method_call() > 0` (MethodCall)
|
||||
//! - その他
|
||||
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
|
||||
use crate::mir::CompareOp;
|
||||
|
||||
/// if条件のパターン種別
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -93,18 +96,29 @@ pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern {
|
||||
return ConditionPattern::Complex;
|
||||
}
|
||||
|
||||
// Check if LHS is a simple variable
|
||||
// Check LHS/RHS patterns
|
||||
let left_is_var = matches!(left.as_ref(), ASTNode::Variable { .. });
|
||||
|
||||
// Check if RHS is a literal
|
||||
let left_is_literal = matches!(left.as_ref(), ASTNode::Literal { .. });
|
||||
let right_is_var = matches!(right.as_ref(), ASTNode::Variable { .. });
|
||||
let right_is_literal = matches!(right.as_ref(), ASTNode::Literal { .. });
|
||||
|
||||
// Phase 219: var CmpOp literal (e.g., i > 0)
|
||||
if left_is_var && right_is_literal {
|
||||
ConditionPattern::SimpleComparison
|
||||
} else {
|
||||
// Complex LHS/RHS (e.g., i % 2 == 1, method_call() > 0)
|
||||
ConditionPattern::Complex
|
||||
return ConditionPattern::SimpleComparison;
|
||||
}
|
||||
|
||||
// Phase 222: literal CmpOp var (e.g., 0 < i)
|
||||
if left_is_literal && right_is_var {
|
||||
return ConditionPattern::SimpleComparison;
|
||||
}
|
||||
|
||||
// Phase 222: var CmpOp var (e.g., i > j)
|
||||
if left_is_var && right_is_var {
|
||||
return ConditionPattern::SimpleComparison;
|
||||
}
|
||||
|
||||
// Complex LHS/RHS (e.g., i % 2 == 1, method_call() > 0)
|
||||
ConditionPattern::Complex
|
||||
}
|
||||
// Any other node type → Complex
|
||||
_ => ConditionPattern::Complex,
|
||||
@ -134,6 +148,144 @@ pub fn is_simple_comparison(cond: &ASTNode) -> bool {
|
||||
analyze_condition_pattern(cond) == ConditionPattern::SimpleComparison
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Phase 222: Condition Normalization
|
||||
// ============================================================================
|
||||
|
||||
/// 条件式の右辺値(変数 or リテラル)
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ConditionValue {
|
||||
/// 変数
|
||||
Variable(String),
|
||||
/// 整数リテラル
|
||||
Literal(i64),
|
||||
}
|
||||
|
||||
/// 正規化された条件式
|
||||
///
|
||||
/// 常に左辺が変数の形に正規化される:
|
||||
/// - `i > 0` → `i > 0` (そのまま)
|
||||
/// - `0 < i` → `i > 0` (左右反転)
|
||||
/// - `i > j` → `i > j` (変数同士、そのまま)
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NormalizedCondition {
|
||||
/// 左辺変数名
|
||||
pub left_var: String,
|
||||
/// 比較演算子
|
||||
pub op: CompareOp,
|
||||
/// 右辺(変数 or リテラル)
|
||||
pub right: ConditionValue,
|
||||
}
|
||||
|
||||
/// 比較演算子を左右反転
|
||||
///
|
||||
/// 左辺がリテラル、右辺が変数の場合に使用。
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// // 0 < i → i > 0
|
||||
/// assert_eq!(flip_compare_op(CompareOp::Lt), CompareOp::Gt);
|
||||
///
|
||||
/// // len > i → i < len
|
||||
/// assert_eq!(flip_compare_op(CompareOp::Gt), CompareOp::Lt);
|
||||
///
|
||||
/// // 5 == i → i == 5 (不変)
|
||||
/// assert_eq!(flip_compare_op(CompareOp::Eq), CompareOp::Eq);
|
||||
/// ```
|
||||
fn flip_compare_op(op: CompareOp) -> CompareOp {
|
||||
match op {
|
||||
CompareOp::Lt => CompareOp::Gt, // < → >
|
||||
CompareOp::Gt => CompareOp::Lt, // > → <
|
||||
CompareOp::Le => CompareOp::Ge, // <= → >=
|
||||
CompareOp::Ge => CompareOp::Le, // >= → <=
|
||||
CompareOp::Eq => CompareOp::Eq, // == → == (不変)
|
||||
CompareOp::Ne => CompareOp::Ne, // != → != (不変)
|
||||
}
|
||||
}
|
||||
|
||||
/// BinaryOperator を CompareOp に変換
|
||||
fn binary_op_to_compare_op(op: &BinaryOperator) -> Option<CompareOp> {
|
||||
match op {
|
||||
BinaryOperator::Less => Some(CompareOp::Lt),
|
||||
BinaryOperator::Greater => Some(CompareOp::Gt),
|
||||
BinaryOperator::LessEqual => Some(CompareOp::Le),
|
||||
BinaryOperator::GreaterEqual => Some(CompareOp::Ge),
|
||||
BinaryOperator::Equal => Some(CompareOp::Eq),
|
||||
BinaryOperator::NotEqual => Some(CompareOp::Ne),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 条件式を正規化(左辺=変数 の形に統一)
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cond` - 条件式ASTノード
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `Some(NormalizedCondition)` - 正規化成功
|
||||
/// - `None` - 正規化失敗(複雑条件 or サポート外)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// // i > 0 → NormalizedCondition { left_var: "i", op: Gt, right: Literal(0) }
|
||||
/// let norm = normalize_comparison(&i_gt_0).unwrap();
|
||||
/// assert_eq!(norm.left_var, "i");
|
||||
/// assert_eq!(norm.op, CompareOp::Gt);
|
||||
/// assert_eq!(norm.right, ConditionValue::Literal(0));
|
||||
///
|
||||
/// // 0 < i → NormalizedCondition { left_var: "i", op: Gt, right: Literal(0) }
|
||||
/// let norm = normalize_comparison(&zero_lt_i).unwrap();
|
||||
/// assert_eq!(norm.left_var, "i");
|
||||
/// assert_eq!(norm.op, CompareOp::Gt); // 左右反転により Gt
|
||||
///
|
||||
/// // i > j → NormalizedCondition { left_var: "i", op: Gt, right: Variable("j") }
|
||||
/// let norm = normalize_comparison(&i_gt_j).unwrap();
|
||||
/// assert_eq!(norm.right, ConditionValue::Variable("j".to_string()));
|
||||
/// ```
|
||||
pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> {
|
||||
match cond {
|
||||
ASTNode::BinaryOp { operator, left, right, .. } => {
|
||||
// Comparison operator のみ受理
|
||||
let compare_op = binary_op_to_compare_op(operator)?;
|
||||
|
||||
// Case 1: var CmpOp literal (e.g., i > 0)
|
||||
if let (ASTNode::Variable { name: left_var, .. }, ASTNode::Literal { value: LiteralValue::Integer(right_val), .. }) = (left.as_ref(), right.as_ref()) {
|
||||
return Some(NormalizedCondition {
|
||||
left_var: left_var.clone(),
|
||||
op: compare_op,
|
||||
right: ConditionValue::Literal(*right_val),
|
||||
});
|
||||
}
|
||||
|
||||
// Case 2: literal CmpOp var (e.g., 0 < i) → 左右反転
|
||||
if let (ASTNode::Literal { value: LiteralValue::Integer(left_val), .. }, ASTNode::Variable { name: right_var, .. }) = (left.as_ref(), right.as_ref()) {
|
||||
return Some(NormalizedCondition {
|
||||
left_var: right_var.clone(),
|
||||
op: flip_compare_op(compare_op), // 演算子を反転
|
||||
right: ConditionValue::Literal(*left_val),
|
||||
});
|
||||
}
|
||||
|
||||
// Case 3: var CmpOp var (e.g., i > j)
|
||||
if let (ASTNode::Variable { name: left_var, .. }, ASTNode::Variable { name: right_var, .. }) = (left.as_ref(), right.as_ref()) {
|
||||
return Some(NormalizedCondition {
|
||||
left_var: left_var.clone(),
|
||||
op: compare_op,
|
||||
right: ConditionValue::Variable(right_var.clone()),
|
||||
});
|
||||
}
|
||||
|
||||
// その他(BinaryOp, MethodCall等)→ 正規化失敗
|
||||
None
|
||||
}
|
||||
_ => None, // 非 BinaryOp → 正規化失敗
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -252,4 +404,92 @@ mod tests {
|
||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex);
|
||||
assert!(!is_simple_comparison(&cond));
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Phase 222: Normalization Tests
|
||||
// ========================================================================
|
||||
|
||||
#[test]
|
||||
fn test_normalize_var_cmp_literal() {
|
||||
// i > 0 → そのまま
|
||||
let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0));
|
||||
let norm = normalize_comparison(&cond).unwrap();
|
||||
assert_eq!(norm.left_var, "i");
|
||||
assert_eq!(norm.op, CompareOp::Gt);
|
||||
assert_eq!(norm.right, ConditionValue::Literal(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_literal_cmp_var() {
|
||||
// 0 < i → i > 0 (左右反転)
|
||||
let cond = binop(BinaryOperator::Less, int_lit(0), var("i"));
|
||||
let norm = normalize_comparison(&cond).unwrap();
|
||||
assert_eq!(norm.left_var, "i");
|
||||
assert_eq!(norm.op, CompareOp::Gt); // 反転により Gt
|
||||
assert_eq!(norm.right, ConditionValue::Literal(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_literal_gt_var() {
|
||||
// len > i → i < len (左右反転)
|
||||
let cond = binop(BinaryOperator::Greater, int_lit(10), var("i"));
|
||||
let norm = normalize_comparison(&cond).unwrap();
|
||||
assert_eq!(norm.left_var, "i");
|
||||
assert_eq!(norm.op, CompareOp::Lt); // 反転により Lt
|
||||
assert_eq!(norm.right, ConditionValue::Literal(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_literal_eq_var() {
|
||||
// 5 == i → i == 5 (反転だが == は不変)
|
||||
let cond = binop(BinaryOperator::Equal, int_lit(5), var("i"));
|
||||
let norm = normalize_comparison(&cond).unwrap();
|
||||
assert_eq!(norm.left_var, "i");
|
||||
assert_eq!(norm.op, CompareOp::Eq); // == は不変
|
||||
assert_eq!(norm.right, ConditionValue::Literal(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_var_cmp_var() {
|
||||
// i > j → そのまま(変数同士)
|
||||
let cond = binop(BinaryOperator::Greater, var("i"), var("j"));
|
||||
let norm = normalize_comparison(&cond).unwrap();
|
||||
assert_eq!(norm.left_var, "i");
|
||||
assert_eq!(norm.op, CompareOp::Gt);
|
||||
assert_eq!(norm.right, ConditionValue::Variable("j".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_var_lt_var() {
|
||||
// i < end → そのまま(変数同士)
|
||||
let cond = binop(BinaryOperator::Less, var("i"), var("end"));
|
||||
let norm = normalize_comparison(&cond).unwrap();
|
||||
assert_eq!(norm.left_var, "i");
|
||||
assert_eq!(norm.op, CompareOp::Lt);
|
||||
assert_eq!(norm.right, ConditionValue::Variable("end".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_fails_on_complex() {
|
||||
// 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!(normalize_comparison(&cond), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_analyze_pattern_literal_cmp_var() {
|
||||
// Phase 222: 0 < i → SimpleComparison
|
||||
let cond = binop(BinaryOperator::Less, int_lit(0), var("i"));
|
||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
||||
assert!(is_simple_comparison(&cond));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_analyze_pattern_var_cmp_var() {
|
||||
// Phase 222: i > j → SimpleComparison
|
||||
let cond = binop(BinaryOperator::Greater, var("i"), var("j"));
|
||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
||||
assert!(is_simple_comparison(&cond));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user