138 lines
4.5 KiB
Rust
138 lines
4.5 KiB
Rust
|
|
//! 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<Option<ValueId>, 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,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<'a> LoopPatternContext<'a> {
|
||
|
|
/// Create new context from routing parameters
|
||
|
|
pub fn new(
|
||
|
|
condition: &'a ASTNode,
|
||
|
|
body: &'a [ASTNode],
|
||
|
|
func_name: &'a str,
|
||
|
|
debug: bool,
|
||
|
|
) -> Self {
|
||
|
|
Self {
|
||
|
|
condition,
|
||
|
|
body,
|
||
|
|
func_name,
|
||
|
|
debug,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 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<Option<ValueId>, String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Static table of all registered loop patterns.
|
||
|
|
/// Patterns are tried in priority order (lowest first).
|
||
|
|
///
|
||
|
|
/// # Current Patterns
|
||
|
|
///
|
||
|
|
/// - 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: "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<Option<ValueId>, String> {
|
||
|
|
// 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) {
|
||
|
|
// Log which pattern matched (for debugging)
|
||
|
|
if ctx.debug || std::env::var("NYASH_TRACE_VARMAP").is_ok() {
|
||
|
|
eprintln!("[route] Pattern '{}' matched for function '{}'", entry.name, ctx.func_name);
|
||
|
|
}
|
||
|
|
return (entry.lower)(builder, ctx);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// No pattern matched - fall through to legacy path
|
||
|
|
if ctx.debug {
|
||
|
|
eprintln!("[route] No pattern matched for function '{}', falling back to legacy", ctx.func_name);
|
||
|
|
}
|
||
|
|
Ok(None)
|
||
|
|
}
|