diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index 1f2c9067..2c78f230 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -60,6 +60,7 @@ pub mod skip_ws; pub mod stage1_using_resolver; pub mod stageb_body; pub mod stageb_funcscanner; +pub mod trim_loop_lowering; // Phase 180: Trim/P5 dedicated lowering module pub mod type_hint_policy; // Phase 65.5: 型ヒントポリシー箱化 pub mod type_inference; // Phase 65-2-A pub(crate) mod value_id_ranges; // Internal ValueId range management diff --git a/src/mir/join_ir/lowering/trim_loop_lowering.rs b/src/mir/join_ir/lowering/trim_loop_lowering.rs new file mode 100644 index 00000000..e0cc0a90 --- /dev/null +++ b/src/mir/join_ir/lowering/trim_loop_lowering.rs @@ -0,0 +1,221 @@ +//! Phase 180: Trim/P5 Dedicated Lowering Module +//! +//! Consolidates all Trim pattern lowering logic from Pattern2/Pattern4. +//! Pattern2/Pattern4 handle generic loop structures, while this module +//! handles Trim/CharComparison (Pattern 5) specific knowledge. +//! +//! ## Responsibilities +//! +//! - Detect Trim-like loops (whitespace skipping patterns) +//! - Promote LoopBodyLocal variables to carriers +//! - Generate carrier initialization code (substring + whitespace check) +//! - Replace break conditions with carrier checks +//! - Setup ConditionEnv bindings for JoinIR +//! +//! ## Design Philosophy +//! +//! Follows Box Theory principles: +//! - **Single Responsibility**: Only handles Trim/P5 lowering logic +//! - **Reusability**: Used by Pattern2, Pattern4, and future patterns +//! - **Testability**: Pure data transformations, easy to unit test +//! - **Fail-Fast**: Returns errors early, no silent fallbacks +//! +//! ## Example Use Case +//! +//! **Original pattern** (Pattern 2 with Trim): +//! ```nyash +//! loop(start < end) { +//! local ch = s.substring(start, start+1) +//! if ch == " " || ch == "\t" { start = start + 1 } else { break } +//! } +//! ``` +//! +//! **After TrimLoopLowerer processing**: +//! - LoopBodyLocal `ch` promoted to bool carrier `is_ch_match` +//! - Carrier initialized: `is_ch_match = (s.substring(start, start+1) == " " || ...)` +//! - Break condition replaced: `break on !is_ch_match` +//! - ConditionEnv binding: `ch` → JoinIR ValueId + +use crate::ast::ASTNode; +use crate::mir::builder::MirBuilder; +use crate::mir::join_ir::lowering::carrier_info::CarrierInfo; +use crate::mir::join_ir::lowering::condition_env::ConditionBinding; +use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; +use crate::mir::loop_pattern_detection::loop_body_carrier_promoter::{ + LoopBodyCarrierPromoter, PromotionRequest, PromotionResult, +}; +use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox; +use crate::mir::ValueId; + +/// Trim pattern lowering orchestrator +/// +/// Phase 180: Single entry point for all Trim/P5 lowering operations. +pub struct TrimLoopLowerer; + +/// Result of successful Trim lowering preprocessing +/// +/// Contains all data needed by Pattern2/4 to complete lowering: +/// - Replaced break condition +/// - Updated carrier info with promoted carrier +/// - Condition environment bindings +/// - Trim helper for pattern-specific operations +pub struct TrimLoweringResult { + /// Replaced break condition (e.g., `!is_carrier`) + /// + /// Pattern2/4 will use this instead of the original break condition + pub condition: ASTNode, + + /// Updated carrier info with promoted Trim carrier + /// + /// Pattern2/4 will use this for JoinIR lowering + pub carrier_info: CarrierInfo, + + /// Condition environment bindings for the carrier + /// + /// Pattern2/4 will extend their condition_bindings with these + pub condition_bindings: Vec, +} + +impl TrimLoopLowerer { + /// Try to lower a Trim-like loop pattern + /// + /// Phase 180: Main entry point for Trim pattern detection and lowering. + /// + /// # Algorithm + /// + /// 1. Check if condition references LoopBodyLocal variables + /// 2. Try to promote LoopBodyLocal to carrier (via LoopBodyCarrierPromoter) + /// 3. If promoted as Trim pattern: + /// - Generate carrier initialization code + /// - Replace break condition with carrier check + /// - Setup ConditionEnv bindings + /// 4. Return TrimLoweringResult with all updates + /// + /// # Arguments + /// + /// * `builder` - MirBuilder for code generation + /// * `scope` - Loop structure metadata + /// * `loop_cond` - Main loop condition (e.g., `start < end`) + /// * `break_cond` - Break condition from loop body + /// * `body` - Loop body AST nodes + /// * `loop_var_name` - Loop variable name (e.g., "start") + /// * `carrier_info` - Current carrier info (will be updated if Trim pattern) + /// * `alloc_join_value` - JoinIR ValueId allocator closure + /// + /// # Returns + /// + /// - `Ok(Some(TrimLoweringResult))` - Trim pattern detected and lowered + /// - `Ok(None)` - Not a Trim pattern (normal loop, no action taken) + /// - `Err(String)` - Trim pattern detected but lowering failed + /// + /// # Example + /// + /// ```ignore + /// if let Some(trim_result) = TrimLoopLowerer::try_lower_trim_like_loop( + /// self, + /// &scope, + /// condition, + /// &break_condition_node, + /// _body, + /// &loop_var_name, + /// &mut carrier_info, + /// &mut alloc_join_value, + /// )? { + /// // Use trim_result.condition, carrier_info, condition_bindings + /// } + /// ``` + pub fn try_lower_trim_like_loop( + builder: &mut MirBuilder, + scope: &LoopScopeShape, + loop_cond: &ASTNode, + break_cond: &ASTNode, + body: &[ASTNode], + loop_var_name: &str, + carrier_info: &mut CarrierInfo, + alloc_join_value: &mut dyn FnMut() -> ValueId, + ) -> Result, String> { + // Phase 180-2: Skeleton implementation + // TODO: Phase 180-3 will implement full logic from Pattern2 + + // Step 1: Check if condition references LoopBodyLocal variables + let cond_scope = LoopConditionScopeBox::analyze( + loop_var_name, + &[loop_cond, break_cond], + Some(scope), + ); + + eprintln!( + "[TrimLoopLowerer] Analyzing condition scope: {} variables", + cond_scope.vars.len() + ); + + if !cond_scope.has_loop_body_local() { + // Not a Trim pattern - normal loop + eprintln!("[TrimLoopLowerer] No LoopBodyLocal detected, skipping Trim lowering"); + return Ok(None); + } + + eprintln!("[TrimLoopLowerer] LoopBodyLocal detected in condition scope"); + + // Step 2: Try promotion via LoopBodyCarrierPromoter + let request = PromotionRequest { + scope, + cond_scope: &cond_scope, + break_cond: Some(break_cond), + loop_body: body, + }; + + match LoopBodyCarrierPromoter::try_promote(&request) { + PromotionResult::Promoted { trim_info } => { + eprintln!( + "[TrimLoopLowerer] LoopBodyLocal '{}' promoted to carrier '{}'", + trim_info.var_name, trim_info.carrier_name + ); + + // Step 3: Convert to CarrierInfo and merge + let promoted_carrier = trim_info.to_carrier_info(); + carrier_info.merge_from(&promoted_carrier); + + eprintln!( + "[TrimLoopLowerer] Merged carrier '{}' into CarrierInfo (total carriers: {})", + trim_info.carrier_name, + carrier_info.carrier_count() + ); + + // Step 4: Generate carrier initialization code + // TODO: Phase 180-3 will implement full logic + // For now, return minimal result to pass compilation + + // Placeholder: Return error to indicate incomplete implementation + return Err(format!( + "[TrimLoopLowerer] Phase 180-2 skeleton: Trim lowering not yet implemented (carrier: '{}')", + trim_info.carrier_name + )); + } + PromotionResult::CannotPromote { reason, vars } => { + // Phase 180: Fail-Fast on promotion failure + return Err(format!( + "[TrimLoopLowerer] Cannot promote LoopBodyLocal variables {:?}: {}", + vars, reason + )); + } + } + } + + // TODO: Phase 180-3 will add helper methods: + // - generate_carrier_initialization() + // - generate_trim_break_condition() + // - setup_condition_env_bindings() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_trim_loop_lowerer_skeleton() { + // Phase 180-2: Basic existence test + // Full tests will be added in Phase 180-3 + assert!(true, "TrimLoopLowerer skeleton compiles"); + } +}