Files
hakorune/src/mir/builder/control_flow/joinir/patterns/router.rs

262 lines
9.2 KiB
Rust

//! Pattern Router - Plan/Composer routing for loop patterns
//!
//! Phase 29ap P12: Legacy loop table removed (plan/composer SSOT only)
//!
//! # Architecture
//!
//! - single_planner derives a DomainPlan + facts outcome (SSOT)
//! - composer adopts CorePlan (strict/dev shadow or release adopt)
//! - PlanLowerer emits MIR from CorePlan (emit_frag SSOT)
//!
//! # Adding New Patterns
//!
//! 1. Add Facts/Planner extraction in plan layer
//! 2. Normalize/verify in plan normalizer/verifier
//! 3. Compose CorePlan in composer (shadow/release adopt as needed)
//! 4. Keep router unchanged (it only delegates to plan/composer)
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind};
// Phase 273 P1: Import Plan components (DomainPlan → Normalizer → Verifier → Lowerer)
use crate::mir::builder::control_flow::plan::lowerer::PlanLowerer;
use crate::mir::builder::control_flow::plan::normalizer::PlanNormalizer;
use crate::mir::builder::control_flow::plan::verifier::PlanVerifier;
use crate::mir::builder::control_flow::plan::composer;
use crate::mir::builder::control_flow::plan::single_planner;
/// AST Feature Extractor (declared in mod.rs as pub module, import from parent)
use super::ast_feature_extractor as ast_features;
/// Phase 92 P0-2: Import LoopSkeleton for Option A
use crate::mir::loop_canonicalizer::LoopSkeleton;
/// Context passed to pattern detect/lower functions
pub(crate) 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,
/// In static box context? (affects Pattern8 routing)
pub in_static_box: bool,
/// Has continue statement(s) in body? (Phase 194+)
#[allow(dead_code)]
pub has_continue: bool,
/// Has break statement(s) in body? (Phase 194+)
#[allow(dead_code)]
pub has_break: bool,
/// Phase 192: Loop features extracted from AST
#[allow(dead_code)]
pub features: LoopFeatures,
/// Phase 192: Pattern classification based on features
pub pattern_kind: LoopPatternKind,
/// Phase 200-C: Optional function body AST for capture analysis
/// None if not available, Some(&[ASTNode]) if function body is accessible
pub fn_body: Option<&'a [ASTNode]>,
/// Phase 92 P0-2: Optional LoopSkeleton from canonicalizer
/// This provides ConditionalStep information for Pattern2 lowering.
/// None if canonicalizer hasn't run yet (backward compatibility).
/// SSOT Principle: Avoid re-detecting ConditionalStep in lowering phase.
#[allow(dead_code)]
pub skeleton: Option<&'a LoopSkeleton>,
/// Phase 188.3: Cached StepTree max_loop_depth for Pattern6
/// None if not computed, Some(depth) if Pattern6 candidate
/// Avoids re-building StepTree in lowering phase
pub step_tree_max_loop_depth: Option<u32>,
}
impl<'a> LoopPatternContext<'a> {
/// Create new context from routing parameters
///
/// Automatically detects continue/break statements in body
/// Extracts features and classifies pattern from AST
/// Detects infinite loop condition
/// Uses choose_pattern_kind() SSOT entry point
pub(crate) fn new(
condition: &'a ASTNode,
body: &'a [ASTNode],
func_name: &'a str,
debug: bool,
in_static_box: bool,
) -> Self {
// Use AST Feature Extractor for break/continue detection
let has_continue = ast_features::detect_continue_in_body(body);
let has_break = ast_features::detect_break_in_body(body);
// Extract features (includes infinite loop detection)
let features = ast_features::extract_features(condition, body, has_continue, has_break);
// Phase 137-6-S1: Use SSOT pattern selection entry point
use crate::mir::builder::control_flow::joinir::routing::choose_pattern_kind;
let pattern_kind = choose_pattern_kind(condition, body);
Self {
condition,
body,
func_name,
debug,
in_static_box,
has_continue,
has_break,
features,
pattern_kind,
fn_body: None, // Phase 200-C: Default to None
skeleton: None, // Phase 92 P0-2: Default to None
step_tree_max_loop_depth: None, // Phase 188.3: Default to None
}
}
/// Phase 200-C: Create context with fn_body for capture analysis
pub(crate) fn with_fn_body(
condition: &'a ASTNode,
body: &'a [ASTNode],
func_name: &'a str,
debug: bool,
in_static_box: bool,
fn_body: &'a [ASTNode],
) -> Self {
let mut ctx = Self::new(condition, body, func_name, debug, in_static_box);
ctx.fn_body = Some(fn_body);
ctx
}
/// Phase 92 P0-2: Set skeleton (for canonicalizer integration)
#[allow(dead_code)]
pub(crate) fn with_skeleton(mut self, skeleton: &'a LoopSkeleton) -> Self {
self.skeleton = Some(skeleton);
self
}
}
/// Phase 286 P2.2: Common helper for Plan line lowering
///
/// Extracts the common 3-line pattern used by Plan-based routing:
/// 1. Normalize DomainPlan → CorePlan
/// 2. Verify CorePlan invariants (fail-fast)
/// 3. Lower CorePlan → MIR
///
/// This eliminates repetition across 4 pattern routing blocks.
fn lower_via_plan(
builder: &mut MirBuilder,
domain_plan: crate::mir::builder::control_flow::plan::DomainPlan,
ctx: &LoopPatternContext,
) -> Result<Option<ValueId>, String> {
let core_plan = PlanNormalizer::normalize(builder, domain_plan, ctx)?;
PlanVerifier::verify(&core_plan)?;
PlanLowerer::lower(builder, core_plan, ctx)
}
// Phase 29ai P5: Plan extractor routing moved to `plan::single_planner`.
/// Route loop patterns via plan/composer SSOT.
///
/// Returns Ok(Some(value_id)) if a plan matched and lowered successfully.
/// Returns Ok(None) if no plan matched.
/// Returns Err if a plan matched but lowering failed.
///
/// # Router Architecture (Plan/Composer SSOT)
///
/// The plan line (Extractor → Normalizer → Verifier → Lowerer) is the
/// operational SSOT for loop routing (Phase 273+).
///
/// Plan-based architecture (Phase 273 P1-P3):
/// - extract_*_plan() → DomainPlan (pure extraction, no builder)
/// - PlanNormalizer::normalize() → CorePlan (pattern knowledge expansion, SSOT)
/// - PlanVerifier::verify() → fail-fast validation
/// - PlanLowerer::lower() → MIR emission (pattern-agnostic, emit_frag SSOT)
///
/// SSOT Entry Points:
/// - Pattern6: src/mir/builder/control_flow/plan/normalizer.rs (ScanWithInit normalization)
/// - Pattern7: src/mir/builder/control_flow/plan/normalizer.rs (SplitScan normalization)
pub(crate) fn route_loop_pattern(
builder: &mut MirBuilder,
ctx: &LoopPatternContext,
) -> Result<Option<ValueId>, String> {
use super::super::trace;
// Phase 29ai P5: Single entrypoint for plan extraction (router has no rule table).
let (domain_plan, outcome) = single_planner::try_build_domain_plan_with_outcome(ctx)?;
let strict_or_dev = crate::config::env::joinir_dev::strict_enabled()
|| crate::config::env::joinir_dev_enabled();
if strict_or_dev {
if let Some(adopt) = composer::try_shadow_adopt_nested_minimal(builder, ctx, &outcome)? {
let composer::ShadowAdoptOutcome { core_plan, tag } = adopt;
PlanVerifier::verify(&core_plan)?;
eprintln!("{}", tag);
return PlanLowerer::lower(builder, core_plan, ctx);
}
if let Some(err) = composer::strict_nested_loop_guard(&outcome, ctx) {
eprintln!("{}", err);
return Err(err);
}
}
if !strict_or_dev {
if let Some(core_plan) =
composer::try_release_adopt_nested_minimal(builder, ctx, &outcome)?
{
PlanVerifier::verify(&core_plan)?;
return PlanLowerer::lower(builder, core_plan, ctx);
}
}
if let Some(domain_plan) = domain_plan {
if let Some(adopt) = composer::try_shadow_adopt_core_plan(
builder,
ctx,
strict_or_dev,
&domain_plan,
&outcome,
)? {
let composer::ShadowAdoptOutcome { core_plan, tag } = adopt;
PlanVerifier::verify(&core_plan)?;
eprintln!("{}", tag);
return PlanLowerer::lower(builder, core_plan, ctx);
}
if !strict_or_dev {
if let Some(core_plan) =
composer::try_release_adopt_core_plan(builder, ctx, &domain_plan, &outcome)?
{
PlanVerifier::verify(&core_plan)?;
return PlanLowerer::lower(builder, core_plan, ctx);
}
}
return lower_via_plan(builder, domain_plan, ctx);
}
// No pattern matched - return None (caller will handle error)
if ctx.debug {
trace::trace().debug(
"route",
&format!(
"route=none (no pattern matched) func='{}' pattern_kind={:?} (exhausted: plan+joinir)",
ctx.func_name, ctx.pattern_kind
),
);
}
Ok(None)
}