feat(joinir): Add TrimLoopLowerer skeleton for P5 module

Phase 180-2: Create dedicated Trim/CharComparison lowering module

- New module: src/mir/join_ir/lowering/trim_loop_lowering.rs
- TrimLoopLowerer::try_lower_trim_like_loop() skeleton
- Integrates LoopConditionScopeBox + LoopBodyCarrierPromoter
- Returns TrimLoweringResult with updated condition/carrier/bindings
- TODO: Phase 180-3 will implement full logic from Pattern2
This commit is contained in:
nyash-codex
2025-12-08 21:02:13 +09:00
parent 28512b4bb4
commit 2bbee79adf
2 changed files with 222 additions and 0 deletions

View File

@ -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

View File

@ -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<ConditionBinding>,
}
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<Option<TrimLoweringResult>, 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");
}
}