refactor(mir): Phase 140-P4-A - detect_skip_whitespace_pattern 共通化
- SkipWhitespaceInfo struct を定義(carrier_name, delta, body_stmts) - detect_skip_whitespace_pattern() を ast_feature_extractor.rs に実装 - pattern_recognizer.rs のロジックを移植(SSOT化の準備) - ビルド成功確認
This commit is contained in:
@ -19,7 +19,7 @@
|
|||||||
//! - This module now focuses on high-level feature extraction
|
//! - This module now focuses on high-level feature extraction
|
||||||
//! - Delegates to specialized analyzers for break/continue logic
|
//! - Delegates to specialized analyzers for break/continue logic
|
||||||
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
|
||||||
use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakConditionAnalyzer;
|
use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakConditionAnalyzer;
|
||||||
use crate::mir::loop_pattern_detection::LoopFeatures;
|
use crate::mir::loop_pattern_detection::LoopFeatures;
|
||||||
|
|
||||||
@ -352,3 +352,130 @@ mod tests {
|
|||||||
assert_eq!(count_carriers_in_body(&empty), 0);
|
assert_eq!(count_carriers_in_body(&empty), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Phase 140-P4-A: Skip Whitespace Pattern Detection (SSOT)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Skip whitespace pattern information
|
||||||
|
///
|
||||||
|
/// This struct holds the extracted information from a recognized skip_whitespace pattern.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct SkipWhitespaceInfo {
|
||||||
|
/// Carrier variable name (e.g., "p")
|
||||||
|
pub carrier_name: String,
|
||||||
|
/// Constant step increment (e.g., 1 for `p = p + 1`)
|
||||||
|
pub delta: i64,
|
||||||
|
/// Body statements before the if-else (may be empty)
|
||||||
|
pub body_stmts: Vec<ASTNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detect skip_whitespace pattern in loop body (Phase 140-P4-A SSOT)
|
||||||
|
///
|
||||||
|
/// Pattern structure:
|
||||||
|
/// ```
|
||||||
|
/// loop(cond) {
|
||||||
|
/// // ... optional body statements (Body)
|
||||||
|
/// if check_cond {
|
||||||
|
/// carrier = carrier + const
|
||||||
|
/// } else {
|
||||||
|
/// break
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `body` - Loop body statements to analyze
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// `Some(SkipWhitespaceInfo)` if the pattern matches, `None` otherwise
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
///
|
||||||
|
/// This is the SSOT for skip_whitespace pattern detection.
|
||||||
|
/// Used by both loop_canonicalizer (Phase 137) and future pattern analyzers.
|
||||||
|
pub fn detect_skip_whitespace_pattern(body: &[ASTNode]) -> Option<SkipWhitespaceInfo> {
|
||||||
|
if body.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last statement must be if-else with break
|
||||||
|
let last_stmt = &body[body.len() - 1];
|
||||||
|
|
||||||
|
let (then_body, else_body) = match last_stmt {
|
||||||
|
ASTNode::If {
|
||||||
|
then_body,
|
||||||
|
else_body: Some(else_body),
|
||||||
|
..
|
||||||
|
} => (then_body, else_body),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Then branch must be single assignment: carrier = carrier + const
|
||||||
|
if then_body.len() != 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (carrier_name, delta) = match &then_body[0] {
|
||||||
|
ASTNode::Assignment { target, value, .. } => {
|
||||||
|
// Extract target variable name
|
||||||
|
let target_name = match target.as_ref() {
|
||||||
|
ASTNode::Variable { name, .. } => name.clone(),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Value must be: target + const
|
||||||
|
match value.as_ref() {
|
||||||
|
ASTNode::BinaryOp {
|
||||||
|
operator: BinaryOperator::Add,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// Left must be same variable
|
||||||
|
let left_name = match left.as_ref() {
|
||||||
|
ASTNode::Variable { name, .. } => name,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if left_name != &target_name {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right must be integer literal
|
||||||
|
let delta = match right.as_ref() {
|
||||||
|
ASTNode::Literal {
|
||||||
|
value: LiteralValue::Integer(n),
|
||||||
|
..
|
||||||
|
} => *n,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
(target_name, delta)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Else branch must be single break
|
||||||
|
if else_body.len() != 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &else_body[0] {
|
||||||
|
ASTNode::Break { .. } => {
|
||||||
|
// Success! Extract body statements (all except last if)
|
||||||
|
let body_stmts = body[..body.len() - 1].to_vec();
|
||||||
|
Some(SkipWhitespaceInfo {
|
||||||
|
carrier_name,
|
||||||
|
delta,
|
||||||
|
body_stmts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user