//! Pattern Router - Table-driven dispatch for loop patterns //! //! Phase 194: Replace if/else chain with table-driven routing //! //! # Architecture //! //! - Each pattern registers a detect function and a lower function //! - Patterns are tried in priority order (lower = tried first) //! - First matching pattern wins //! //! # Adding New Patterns //! //! 1. Create a new module in `patterns/` (e.g., `pattern4_your_name.rs`) //! 2. Implement `pub fn can_lower(ctx: &LoopPatternContext) -> bool` //! 3. Implement `pub fn lower(builder: &mut MirBuilder, ctx: &LoopPatternContext) -> Result, String>` //! 4. Add entry to `LOOP_PATTERNS` table below //! //! That's it! No need to modify routing logic. use crate::ast::ASTNode; use crate::mir::builder::MirBuilder; use crate::mir::ValueId; /// Context passed to pattern detect/lower functions pub struct LoopPatternContext<'a> { /// Loop condition AST node pub condition: &'a ASTNode, /// Loop body statements pub body: &'a [ASTNode], /// Current function name (for routing) pub func_name: &'a str, /// Debug logging enabled pub debug: bool, /// Has continue statement(s) in body? (Phase 194+) pub has_continue: bool, /// Has break statement(s) in body? (Phase 194+) pub has_break: bool, } impl<'a> LoopPatternContext<'a> { /// Create new context from routing parameters /// /// Phase 194+: Automatically detects continue/break statements in body pub fn new( condition: &'a ASTNode, body: &'a [ASTNode], func_name: &'a str, debug: bool, ) -> Self { // Phase 194+: Detect continue/break statements in AST let has_continue = detect_continue_in_ast(body); let has_break = detect_break_in_ast(body); Self { condition, body, func_name, debug, has_continue, has_break, } } } /// Phase 194+: Detect continue statements in AST /// /// This is a simple recursive scan of the AST looking for Continue nodes. /// It's not perfect (doesn't handle nested loops correctly) but sufficient /// for initial implementation. fn detect_continue_in_ast(body: &[ASTNode]) -> bool { for stmt in body { if has_continue_node(stmt) { return true; } } false } /// Phase 194+: Detect break statements in AST /// /// Similar to detect_continue_in_ast, scans for Break nodes. fn detect_break_in_ast(body: &[ASTNode]) -> bool { for stmt in body { if has_break_node(stmt) { return true; } } false } /// Recursive helper to check if AST node contains Continue fn has_continue_node(node: &ASTNode) -> bool { match node { ASTNode::Continue { .. } => true, ASTNode::If { then_body, else_body, .. } => { then_body.iter().any(has_continue_node) || else_body.as_ref().map_or(false, |e| e.iter().any(has_continue_node)) } ASTNode::Loop { body, .. } => body.iter().any(has_continue_node), _ => false, } } /// Recursive helper to check if AST node contains Break fn has_break_node(node: &ASTNode) -> bool { match node { ASTNode::Break { .. } => true, ASTNode::If { then_body, else_body, .. } => { then_body.iter().any(has_break_node) || else_body.as_ref().map_or(false, |e| e.iter().any(has_break_node)) } ASTNode::Loop { body, .. } => body.iter().any(has_break_node), _ => false, } } /// Entry in the loop pattern router table. /// Each pattern registers a detect function and a lower function. pub struct LoopPatternEntry { /// Human-readable pattern name for debugging pub name: &'static str, /// Priority (lower = tried first). Pattern1=10, Pattern2=20, Pattern3=30 pub priority: u8, /// Detection function: returns true if this pattern matches pub detect: fn(&MirBuilder, &LoopPatternContext) -> bool, /// Lowering function: performs the actual JoinIR generation pub lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result, String>, } /// Static table of all registered loop patterns. /// Patterns are tried in priority order (lowest first). /// /// # Current Patterns /// /// - Pattern 4 (priority 5): Loop with Continue (loop_continue_pattern4.hako) [Phase 194+] /// - Structure-based detection: has_continue == true /// - TODO: Implement lowering logic /// /// - Pattern 1 (priority 10): Simple While Loop (loop_min_while.hako) /// - Function: "main" without 'sum' variable /// - Detection: func_name == "main" && !has_sum_var /// /// - Pattern 2 (priority 20): Loop with Conditional Break (joinir_min_loop.hako) /// - Function: "JoinIrMin.main/0" /// - Detection: func_name == "JoinIrMin.main/0" /// /// - Pattern 3 (priority 30): Loop with If-Else PHI (loop_if_phi.hako) /// - Function: "main" with 'sum' variable /// - Detection: func_name == "main" && has_sum_var pub static LOOP_PATTERNS: &[LoopPatternEntry] = &[ LoopPatternEntry { name: "Pattern4_WithContinue", priority: 5, // Highest priority - continue is most specific detect: super::pattern4_with_continue::can_lower, lower: super::pattern4_with_continue::lower, }, LoopPatternEntry { name: "Pattern3_WithIfPhi", priority: 30, // NOTE: Pattern 3 must be checked BEFORE Pattern 1 (both use "main") detect: super::pattern3_with_if_phi::can_lower, lower: super::pattern3_with_if_phi::lower, }, LoopPatternEntry { name: "Pattern1_Minimal", priority: 10, detect: super::pattern1_minimal::can_lower, lower: super::pattern1_minimal::lower, }, LoopPatternEntry { name: "Pattern2_WithBreak", priority: 20, detect: super::pattern2_with_break::can_lower, lower: super::pattern2_with_break::lower, }, ]; /// Try all registered patterns in priority order. /// /// Returns Ok(Some(value_id)) if a pattern matched and lowered successfully. /// Returns Ok(None) if no pattern matched. /// Returns Err if a pattern matched but lowering failed. pub fn route_loop_pattern( builder: &mut MirBuilder, ctx: &LoopPatternContext, ) -> Result, String> { use super::super::trace; // Patterns are already sorted by priority in the table // (Pattern 3 with priority 30 comes first, then Pattern 1 with priority 10, etc.) // This ensures Pattern 3 is checked before Pattern 1, avoiding incorrect routing. for entry in LOOP_PATTERNS { if (entry.detect)(builder, ctx) { // Phase 195: Use unified trace for pattern matching trace::trace().pattern("route", entry.name, true); return (entry.lower)(builder, ctx); } } // No pattern matched - fall through to legacy path if ctx.debug { trace::trace().debug("route", &format!("No pattern matched for function '{}', falling back to legacy", ctx.func_name)); } Ok(None) }