From f71d58a46f60a2ba126cc1c17ea08b138172b96c Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Fri, 5 Dec 2025 21:33:49 +0900 Subject: [PATCH] refactor: Extract skip_ws lowerer from generic_case_a (Phase 2) - Created skip_ws.rs with lower_case_a_skip_ws_with_scope() and helper - Extracted 233 lines of skip_ws-specific lowering logic - Added comprehensive module documentation - Uses EntryFunctionBuilder from helper module - ValueId allocation: entry(3000-3999), loop_step(4000-5999) Size: 233 lines (extracted from 1,056-line monolith) Ref: Phase 192 modularization effort --- .../lowering/generic_case_a/skip_ws.rs | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 src/mir/join_ir/lowering/generic_case_a/skip_ws.rs diff --git a/src/mir/join_ir/lowering/generic_case_a/skip_ws.rs b/src/mir/join_ir/lowering/generic_case_a/skip_ws.rs new file mode 100644 index 00000000..521a4d8e --- /dev/null +++ b/src/mir/join_ir/lowering/generic_case_a/skip_ws.rs @@ -0,0 +1,258 @@ +//! Skip Whitespace Loop Lowering (Case A) +//! +//! Phase 192: Extracted from generic_case_a.rs monolith. +//! +//! ## Responsibility +//! +//! Lowers Main.skip/1 whitespace skipping loop to JoinIR. +//! +//! ## Pattern Structure +//! +//! ```text +//! entry: skip(s) +//! i = 0 +//! n = s.length() +//! call loop_step(s, i, n) +//! +//! loop_step(s, i, n): +//! if i >= n { return i } // Exit condition +//! ch = s.substring(i, i+1) +//! if ch != " " { return i } // Non-space found +//! continue loop_step(s, i+1, n) // Skip space +//! ``` +//! +//! ## ValueId Allocation +//! +//! - Entry range: 3000-3999 (`value_id_ranges::skip_ws::entry`) +//! - Loop range: 4000-5999 (`value_id_ranges::skip_ws::loop_step`) +//! +//! ## See Also +//! +//! - `value_id_ranges::skip_ws` - ValueId allocation strategy +//! - `loop_scope_shape::CaseAContext` - Context extraction + +use std::collections::BTreeMap; + +use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext; +use crate::mir::join_ir::lowering::value_id_ranges; +use crate::mir::join_ir::lowering::value_id_ranges::skip_ws as vid; +use crate::mir::join_ir::{ + BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction, JoinInst, JoinModule, + LoopExitShape, LoopHeaderShape, MirLikeInst, +}; +use crate::mir::ValueId; + +use super::entry_builder::EntryFunctionBuilder; + +/// Phase 30: LoopScopeShape を直接受け取る skip_ws lowerer +/// +/// 呼び出し元で LoopScopeShape を明示的に構築し、この関数に渡す。 +/// CaseAContext::from_scope() 経由で ctx を作成。 +/// +/// # Arguments +/// +/// - `scope`: 事前に構築済みの LoopScopeShape +/// +/// # Returns +/// +/// Some(JoinModule) if successful, None if context building fails. +pub(crate) fn lower_case_a_skip_ws_with_scope( + scope: crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape, +) -> Option { + // CaseAContext::from_scope() で ctx を構築 + let ctx = CaseAContext::from_scope(scope, "skip_ws", |offset| vid::loop_step(offset))?; + + // JoinModule 構築(既存 _for_minimal_skip_ws と同一ロジック) + lower_case_a_skip_ws_core(&ctx) +} + +/// skip_ws JoinModule 構築のコア実装 +/// +/// CaseAContext から JoinModule を構築する共通ロジック。 +/// `_for_minimal_skip_ws` と `_with_scope` の両方から呼ばれる。 +fn lower_case_a_skip_ws_core(ctx: &CaseAContext) -> Option { + let string_key = ctx.pinned_name_or_first(0)?; + let len_key = ctx + .pinned_name_or_first(1) + .unwrap_or_else(|| string_key.clone()); + let index_key = ctx.carrier_name_or_first(0)?; + + let s_loop = ctx.get_loop_id(&string_key)?; + let i_loop = ctx.get_loop_id(&index_key)?; + let n_loop = ctx.get_loop_id(&len_key)?; + + let mut join_module = JoinModule::new(); + + // entry: skip(s) + let skip_id = JoinFuncId::new(0); + let s_param = vid::entry(0); // 3000 + let mut skip_func = JoinFunction::new(skip_id, "skip".to_string(), vec![s_param]); + + let i_init = vid::entry(1); // 3001 + let n_val = vid::entry(2); // 3002 + + // Phase 192: Use EntryFunctionBuilder for boilerplate initialization + let mut entry_builder = EntryFunctionBuilder::new(); + entry_builder.add_var(string_key.clone(), s_param); + entry_builder.add_var(index_key.clone(), i_init); + entry_builder.add_var(len_key.clone(), n_val); + let entry_name_to_id = entry_builder.get_map().clone(); + + skip_func.body.push(JoinInst::Compute(MirLikeInst::Const { + dst: i_init, + value: ConstValue::Integer(0), + })); + + skip_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall { + dst: Some(n_val), + box_name: "StringBox".to_string(), + method: "length".to_string(), + args: vec![s_param], + })); + + let loop_step_id = JoinFuncId::new(1); + let loop_call_args: Vec = ctx + .ordered_pinned + .iter() + .chain(ctx.ordered_carriers.iter()) + .map(|name| entry_name_to_id.get(name).copied()) + .collect::>()?; + + skip_func.body.push(JoinInst::Call { + func: loop_step_id, + args: loop_call_args, + k_next: None, + dst: None, + }); + + join_module.entry = Some(skip_id); + join_module.add_function(skip_func); + + // loop_step(s, i, n) + let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone()); + let loop_params = header_shape.to_loop_step_params(); + let mut loop_step_func = + JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone()); + + let cmp1_result = vid::loop_step(3); // 4003 + let ch = vid::loop_step(4); // 4004 + let cmp2_result = vid::loop_step(5); // 4005 + let i_plus_1 = vid::loop_step(6); // 4006 + let const_1 = vid::loop_step(7); // 4007 + let const_space = vid::loop_step(10); // 4010 + let bool_false = vid::loop_step(11); // 4011 + let cmp2_is_false = vid::loop_step(12); // 4012 + + // cmp1_result = (i >= n) + loop_step_func + .body + .push(JoinInst::Compute(MirLikeInst::Compare { + dst: cmp1_result, + op: CompareOp::Ge, + lhs: i_loop, + rhs: n_loop, + })); + + let _exit_shape = if ctx.exit_args.is_empty() { + LoopExitShape::new_manual(vec![i_loop]) + } else { + LoopExitShape::new_manual(ctx.exit_args.clone()) + }; // exit_args = [i] が期待値 + + // if i >= n { return i } + loop_step_func.body.push(JoinInst::Jump { + cont: JoinContId::new(0), + args: vec![i_loop], + cond: Some(cmp1_result), + }); + + // const 1 + loop_step_func + .body + .push(JoinInst::Compute(MirLikeInst::Const { + dst: const_1, + value: ConstValue::Integer(1), + })); + + // i_plus_1 = i + 1 + loop_step_func + .body + .push(JoinInst::Compute(MirLikeInst::BinOp { + dst: i_plus_1, + op: BinOpKind::Add, + lhs: i_loop, + rhs: const_1, + })); + + // ch = s.substring(i, i + 1) + loop_step_func + .body + .push(JoinInst::Compute(MirLikeInst::BoxCall { + dst: Some(ch), + box_name: "StringBox".to_string(), + method: "substring".to_string(), + args: vec![s_loop, i_loop, i_plus_1], + })); + + // const " " + loop_step_func + .body + .push(JoinInst::Compute(MirLikeInst::Const { + dst: const_space, + value: ConstValue::String(" ".to_string()), + })); + + // cmp2_result = (ch == " ") + loop_step_func + .body + .push(JoinInst::Compute(MirLikeInst::Compare { + dst: cmp2_result, + op: CompareOp::Eq, + lhs: ch, + rhs: const_space, + })); + + // bool false + loop_step_func + .body + .push(JoinInst::Compute(MirLikeInst::Const { + dst: bool_false, + value: ConstValue::Bool(false), + })); + + // cmp2_is_false = (cmp2_result == false) + loop_step_func + .body + .push(JoinInst::Compute(MirLikeInst::Compare { + dst: cmp2_is_false, + op: CompareOp::Eq, + lhs: cmp2_result, + rhs: bool_false, + })); + + // if ch != " " { return i } + loop_step_func.body.push(JoinInst::Jump { + cont: JoinContId::new(1), + args: vec![i_loop], + cond: Some(cmp2_is_false), + }); + + // continue: loop_step(s, i+1, n) + loop_step_func.body.push(JoinInst::Call { + func: loop_step_id, + args: vec![s_loop, i_plus_1, n_loop], + k_next: None, + dst: None, + }); + + join_module.add_function(loop_step_func); + + eprintln!( + "[joinir/generic_case_a/skip_ws] ✅ constructed JoinIR (functions={}, value_range={}..{})", + join_module.functions.len(), + value_id_ranges::base::SKIP_WS, + value_id_ranges::base::SKIP_WS + 1999 + ); + + Some(join_module) +}