Files
hakorune/src/mir/loop_canonicalizer/pattern_recognizer.rs

180 lines
6.3 KiB
Rust
Raw Normal View History

//! Pattern Recognition Helpers
//!
//! Phase 140-P4-B: This module now delegates to SSOT implementations in ast_feature_extractor.
//! Provides backward-compatible wrappers for existing callsites.
use crate::ast::ASTNode;
use crate::mir::{detect_skip_whitespace_pattern as ast_detect, SkipWhitespaceInfo};
// ============================================================================
// Skip Whitespace Pattern (Phase 140-P4-B SSOT Wrapper)
// ============================================================================
/// Try to extract skip_whitespace pattern from loop
///
/// Pattern structure:
/// ```
/// loop(cond) {
/// // ... optional body statements (Body)
/// if check_cond {
/// carrier = carrier + const
/// } else {
/// break
/// }
/// }
/// ```
///
/// Returns (carrier_name, delta, body_stmts) if pattern matches.
///
/// # Phase 140-P4-B: SSOT Migration
///
/// This function now delegates to `ast_feature_extractor::detect_skip_whitespace_pattern`
/// for SSOT implementation. This wrapper maintains backward compatibility for existing callsites.
pub fn try_extract_skip_whitespace_pattern(
body: &[ASTNode],
) -> Option<(String, i64, Vec<ASTNode>)> {
ast_detect(body).map(|info| (info.carrier_name, info.delta, info.body_stmts))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{BinaryOperator, LiteralValue, Span};
#[test]
fn test_skip_whitespace_basic_pattern() {
// Build: if is_ws { p = p + 1 } else { break }
let body = vec![ASTNode::If {
condition: Box::new(ASTNode::Variable {
name: "is_ws".to_string(),
span: Span::unknown(),
}),
then_body: vec![ASTNode::Assignment {
target: Box::new(ASTNode::Variable {
name: "p".to_string(),
span: Span::unknown(),
}),
value: Box::new(ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable {
name: "p".to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
}),
span: Span::unknown(),
}],
else_body: Some(vec![ASTNode::Break {
span: Span::unknown(),
}]),
span: Span::unknown(),
}];
let result = try_extract_skip_whitespace_pattern(&body);
assert!(result.is_some());
let (carrier_name, delta, body_stmts) = result.unwrap();
assert_eq!(carrier_name, "p");
assert_eq!(delta, 1);
assert_eq!(body_stmts.len(), 0);
}
#[test]
fn test_skip_whitespace_with_body() {
// Build: local ch = get_char(p); if is_ws { p = p + 1 } else { break }
let body = vec![
ASTNode::Assignment {
target: Box::new(ASTNode::Variable {
name: "ch".to_string(),
span: Span::unknown(),
}),
value: Box::new(ASTNode::FunctionCall {
name: "get_char".to_string(),
arguments: vec![ASTNode::Variable {
name: "p".to_string(),
span: Span::unknown(),
}],
span: Span::unknown(),
}),
span: Span::unknown(),
},
ASTNode::If {
condition: Box::new(ASTNode::Variable {
name: "is_ws".to_string(),
span: Span::unknown(),
}),
then_body: vec![ASTNode::Assignment {
target: Box::new(ASTNode::Variable {
name: "p".to_string(),
span: Span::unknown(),
}),
value: Box::new(ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable {
name: "p".to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
}),
span: Span::unknown(),
}],
else_body: Some(vec![ASTNode::Break {
span: Span::unknown(),
}]),
span: Span::unknown(),
},
];
let result = try_extract_skip_whitespace_pattern(&body);
assert!(result.is_some());
let (carrier_name, delta, body_stmts) = result.unwrap();
assert_eq!(carrier_name, "p");
assert_eq!(delta, 1);
assert_eq!(body_stmts.len(), 1); // The assignment before the if
}
#[test]
fn test_skip_whitespace_rejects_no_else() {
// Build: if is_ws { p = p + 1 } (no else)
let body = vec![ASTNode::If {
condition: Box::new(ASTNode::Variable {
name: "is_ws".to_string(),
span: Span::unknown(),
}),
then_body: vec![ASTNode::Assignment {
target: Box::new(ASTNode::Variable {
name: "p".to_string(),
span: Span::unknown(),
}),
value: Box::new(ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable {
name: "p".to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
}),
span: Span::unknown(),
}],
else_body: None,
span: Span::unknown(),
}];
let result = try_extract_skip_whitespace_pattern(&body);
assert!(result.is_none());
}
}