diff --git a/src/mir/join_ir/lowering/generic_case_a.rs b/src/mir/join_ir/lowering/generic_case_a.rs index bb5b989d..0b8136c2 100644 --- a/src/mir/join_ir/lowering/generic_case_a.rs +++ b/src/mir/join_ir/lowering/generic_case_a.rs @@ -8,6 +8,7 @@ use std::collections::BTreeMap; +use crate::mir::join_ir::lowering::generic_case_a_entry_builder::EntryFunctionBuilder; 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; @@ -65,10 +66,12 @@ fn lower_case_a_skip_ws_core(ctx: &CaseAContext) -> Option { let i_init = vid::entry(1); // 3001 let n_val = vid::entry(2); // 3002 - let mut entry_name_to_id: BTreeMap = BTreeMap::new(); - entry_name_to_id.insert(string_key.clone(), s_param); - entry_name_to_id.insert(index_key.clone(), i_init); - entry_name_to_id.insert(len_key.clone(), n_val); + // 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, @@ -318,10 +321,12 @@ fn lower_case_a_trim_core(ctx: &CaseAContext) -> Option { rhs: const_zero, })); - let mut entry_name_to_id: BTreeMap = BTreeMap::new(); - entry_name_to_id.insert(string_key.clone(), str_val); - entry_name_to_id.insert(base_key.clone(), b_val); - entry_name_to_id.insert(carrier_key.clone(), e_init); + // Phase 192: Use EntryFunctionBuilder for boilerplate initialization + let mut entry_builder = EntryFunctionBuilder::new(); + entry_builder.add_var(string_key.clone(), str_val); + entry_builder.add_var(base_key.clone(), b_val); + entry_builder.add_var(carrier_key.clone(), e_init); + let entry_name_to_id = entry_builder.get_map().clone(); let loop_call_args: Vec = ctx .ordered_pinned @@ -757,13 +762,15 @@ fn lower_case_a_append_defs_core(ctx: &CaseAContext) -> Option { dst: i_init, value: ConstValue::Integer(0), })); - let mut entry_name_to_id: BTreeMap = BTreeMap::new(); - entry_name_to_id.insert("s".to_string(), dst_param); // intake で param0 を "s" にするため - entry_name_to_id.insert("dst".to_string(), dst_param); - entry_name_to_id.insert("param1".to_string(), defs_box_param); - entry_name_to_id.insert("defs_box".to_string(), defs_box_param); - entry_name_to_id.insert("n".to_string(), n_param); - entry_name_to_id.insert("i".to_string(), i_init); + // Phase 192: Use EntryFunctionBuilder for boilerplate initialization + let mut entry_builder = EntryFunctionBuilder::new(); + entry_builder.add_var("s".to_string(), dst_param); // intake で param0 を "s" にするため + entry_builder.add_var("dst".to_string(), dst_param); + entry_builder.add_var("param1".to_string(), defs_box_param); + entry_builder.add_var("defs_box".to_string(), defs_box_param); + entry_builder.add_var("n".to_string(), n_param); + entry_builder.add_var("i".to_string(), i_init); + let entry_name_to_id = entry_builder.get_map().clone(); let loop_call_args: Vec = ctx .ordered_pinned @@ -931,13 +938,15 @@ fn lower_case_a_stage1_usingresolver_core(ctx: &CaseAContext) -> Option = BTreeMap::new(); - entry_name_to_id.insert(entries_key.clone(), entries_param); - entry_name_to_id.insert(n_key.clone(), n_param); - entry_name_to_id.insert(modules_key.clone(), modules_param); - entry_name_to_id.insert(seen_key.clone(), seen_param); - entry_name_to_id.insert(prefix_key.clone(), prefix_param); - entry_name_to_id.insert(i_key.clone(), i_init); + // Phase 192: Use EntryFunctionBuilder for boilerplate initialization + let mut entry_builder = EntryFunctionBuilder::new(); + entry_builder.add_var(entries_key.clone(), entries_param); + entry_builder.add_var(n_key.clone(), n_param); + entry_builder.add_var(modules_key.clone(), modules_param); + entry_builder.add_var(seen_key.clone(), seen_param); + entry_builder.add_var(prefix_key.clone(), prefix_param); + entry_builder.add_var(i_key.clone(), i_init); + let entry_name_to_id = entry_builder.get_map().clone(); let loop_call_args: Vec = ctx .ordered_pinned diff --git a/src/mir/join_ir/lowering/generic_case_a_entry_builder.rs b/src/mir/join_ir/lowering/generic_case_a_entry_builder.rs new file mode 100644 index 00000000..6374a3af --- /dev/null +++ b/src/mir/join_ir/lowering/generic_case_a_entry_builder.rs @@ -0,0 +1,165 @@ +//! Phase 192: Generic Case A - Entry Function Builder +//! +//! 責務: 4つのループパターン共通のEntry関数構築処理 +//! - ValueId/BTreeMap の初期化ボイラープレート統一化 +//! - Pinned/Carrier 変数の一元管理 + +use std::collections::BTreeMap; +use crate::mir::ValueId; + +/// Entry関数構築用の統一ビルダー +/// +/// 4つのループパターン(skip_ws, trim, append_defs, stage1)で +/// 共通するボイラープレート処理を集約 +#[derive(Clone, Debug)] +pub struct EntryFunctionBuilder { + /// 変数名 → ValueId のマッピング(決定性重視で BTreeMap使用) + name_to_id: BTreeMap, + /// Pinned変数のリスト + pinned_vars: Vec, + /// Carrier変数のリスト + carrier_vars: Vec, +} + +impl EntryFunctionBuilder { + /// 新しいビルダーを作成 + pub fn new() -> Self { + Self { + name_to_id: BTreeMap::new(), + pinned_vars: Vec::new(), + carrier_vars: Vec::new(), + } + } + + /// Pinned変数を追加 + pub fn add_pinned(&mut self, name: String, id: ValueId) { + self.name_to_id.insert(name.clone(), id); + self.pinned_vars.push(name); + } + + /// Carrier変数を追加 + pub fn add_carrier(&mut self, name: String, id: ValueId) { + self.name_to_id.insert(name.clone(), id); + self.carrier_vars.push(name); + } + + /// 一般的な変数を追加(pinned/carrier以外) + pub fn add_var(&mut self, name: String, id: ValueId) { + self.name_to_id.insert(name, id); + } + + /// ループ開始時の引数リストを構築 + pub fn build_loop_args(&self) -> Option> { + // Pinned変数をループ開始時の引数として返す + if self.pinned_vars.is_empty() { + return None; + } + + let args = self + .pinned_vars + .iter() + .filter_map(|name| self.name_to_id.get(name).copied()) + .collect::>(); + + if args.is_empty() { + None + } else { + Some(args) + } + } + + /// 指定された変数のValueIdを取得 + pub fn get_id(&self, name: &str) -> Option { + self.name_to_id.get(name).copied() + } + + /// すべての変数IDを取得 + pub fn get_all_ids(&self) -> Vec { + self.name_to_id.values().copied().collect() + } + + /// マッピング全体を取得 + pub fn get_map(&self) -> &BTreeMap { + &self.name_to_id + } + + /// Pinned変数の数 + pub fn pinned_count(&self) -> usize { + self.pinned_vars.len() + } + + /// Carrier変数の数 + pub fn carrier_count(&self) -> usize { + self.carrier_vars.len() + } +} + +impl Default for EntryFunctionBuilder { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_entry_builder_new() { + let builder = EntryFunctionBuilder::new(); + assert_eq!(builder.pinned_count(), 0); + assert_eq!(builder.carrier_count(), 0); + } + + #[test] + fn test_entry_builder_add_pinned() { + let mut builder = EntryFunctionBuilder::new(); + builder.add_pinned("i".to_string(), ValueId(0)); + assert_eq!(builder.pinned_count(), 1); + assert_eq!(builder.get_id("i"), Some(ValueId(0))); + } + + #[test] + fn test_entry_builder_add_carrier() { + let mut builder = EntryFunctionBuilder::new(); + builder.add_carrier("j".to_string(), ValueId(1)); + assert_eq!(builder.carrier_count(), 1); + assert_eq!(builder.get_id("j"), Some(ValueId(1))); + } + + #[test] + fn test_entry_builder_loop_args() { + let mut builder = EntryFunctionBuilder::new(); + builder.add_pinned("i".to_string(), ValueId(0)); + builder.add_pinned("j".to_string(), ValueId(1)); + + let args = builder.build_loop_args(); + assert!(args.is_some()); + assert_eq!(args.unwrap().len(), 2); + } + + #[test] + fn test_entry_builder_no_pinned() { + let builder = EntryFunctionBuilder::new(); + assert_eq!(builder.build_loop_args(), None); + } + + #[test] + fn test_entry_builder_add_var() { + let mut builder = EntryFunctionBuilder::new(); + builder.add_var("temp".to_string(), ValueId(10)); + assert_eq!(builder.get_id("temp"), Some(ValueId(10))); + } + + #[test] + fn test_entry_builder_get_map() { + let mut builder = EntryFunctionBuilder::new(); + builder.add_pinned("x".to_string(), ValueId(5)); + builder.add_carrier("y".to_string(), ValueId(6)); + + let map = builder.get_map(); + assert_eq!(map.len(), 2); + assert_eq!(map.get("x"), Some(&ValueId(5))); + assert_eq!(map.get("y"), Some(&ValueId(6))); + } +} diff --git a/src/mir/join_ir/lowering/generic_case_a_whitespace_check.rs b/src/mir/join_ir/lowering/generic_case_a_whitespace_check.rs new file mode 100644 index 00000000..e760d01a --- /dev/null +++ b/src/mir/join_ir/lowering/generic_case_a_whitespace_check.rs @@ -0,0 +1,151 @@ +//! Phase 192: Generic Case A - Whitespace Character Detection +//! +//! 責務: trim操作での空白文字判定ロジックの集約 +//! - Space/Tab/Newline/CR の判定を統一 +//! - skip_leading と loop_step で重複していた処理を統一 + +use crate::mir::ValueId; + +/// Whitespace判定フラグ +#[derive(Clone, Debug)] +pub struct WhitespaceCheckResult { + /// Space (0x20) + pub is_space: bool, + /// Tab (0x09) + pub is_tab: bool, + /// Newline (0x0A) + pub is_newline: bool, + /// Carriage return (0x0D) + pub is_carriage_return: bool, +} + +impl WhitespaceCheckResult { + /// いずれかの空白判定がtrueか確認 + pub fn is_whitespace(&self) -> bool { + self.is_space || self.is_tab || self.is_newline || self.is_carriage_return + } + + /// 空白判定を実行 + pub fn check(ch: char) -> Self { + Self { + is_space: ch == ' ', + is_tab: ch == '\t', + is_newline: ch == '\n', + is_carriage_return: ch == '\r', + } + } + + /// 複数文字をチェック(shorthand) + pub fn check_byte(byte: u8) -> Self { + Self::check(byte as char) + } +} + +/// Whitespace検出処理の共通ユーティリティ +pub struct WhitespaceDetector; + +impl WhitespaceDetector { + /// 指定されたValueIdが空白文字かどうかを判定する式を構築 + /// + /// この関数は複数の条件(Space/Tab/Newline/CR)をORで繋いで + /// 統一的な空白判定式を生成する + /// + /// # Note + /// 具体的な JoinInst 生成は呼び出し側で行う。 + /// ここは判定ロジック(どの文字を空白と判定するか)を記録する。 + pub fn build_whitespace_check_expr( + ch_value: ValueId, + _debug: bool, + ) -> Option { + // NOTE: JoinInst を生成する実装は呼び出し側で行う + // ここは判定ロジック(どの文字を空白と判定するか)を記録 + + // Space (0x20) + let _space_check = ch_value; + // Tab (0x09) + let _tab_check = ch_value; + // Newline (0x0A) + let _newline_check = ch_value; + // Carriage return (0x0D) + let _cr_check = ch_value; + + // これらを OR で繋ぐ(具体的な JoinInst 生成は呼び出し側) + Some(ch_value) // Placeholder + } + + /// Whitespace判定に必要な文字リスト + pub fn whitespace_chars() -> &'static [u8] { + b" \t\n\r" + } + + /// Whitespace判定で使用される文字定数のリスト(JoinIR生成用) + pub fn whitespace_string_constants() -> Vec<&'static str> { + vec![" ", "\\t", "\\n", "\\r"] + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_whitespace_check_space() { + let result = WhitespaceCheckResult::check(' '); + assert!(result.is_space); + assert!(result.is_whitespace()); + } + + #[test] + fn test_whitespace_check_tab() { + let result = WhitespaceCheckResult::check('\t'); + assert!(result.is_tab); + assert!(result.is_whitespace()); + } + + #[test] + fn test_whitespace_check_newline() { + let result = WhitespaceCheckResult::check('\n'); + assert!(result.is_newline); + assert!(result.is_whitespace()); + } + + #[test] + fn test_whitespace_check_carriage_return() { + let result = WhitespaceCheckResult::check('\r'); + assert!(result.is_carriage_return); + assert!(result.is_whitespace()); + } + + #[test] + fn test_whitespace_check_non_whitespace() { + let result = WhitespaceCheckResult::check('x'); + assert!(!result.is_whitespace()); + } + + #[test] + fn test_whitespace_check_byte() { + let result = WhitespaceCheckResult::check_byte(b' '); + assert!(result.is_space); + assert!(result.is_whitespace()); + } + + #[test] + fn test_whitespace_detector_chars() { + let chars = WhitespaceDetector::whitespace_chars(); + assert!(chars.contains(&b' ')); + assert!(chars.contains(&b'\t')); + assert!(chars.contains(&b'\n')); + assert!(chars.contains(&b'\r')); + assert_eq!(chars.len(), 4); + } + + #[test] + fn test_whitespace_detector_constants() { + let constants = WhitespaceDetector::whitespace_string_constants(); + assert_eq!(constants.len(), 4); + assert!(constants.contains(&" ")); + assert!(constants.contains(&"\\t")); + assert!(constants.contains(&"\\n")); + assert!(constants.contains(&"\\r")); + } +} diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index 54fd9668..313787f9 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -20,6 +20,8 @@ pub mod exit_args_resolver; pub mod funcscanner_append_defs; pub mod funcscanner_trim; pub mod generic_case_a; +pub mod generic_case_a_entry_builder; // Phase 192: Entry function builder +pub mod generic_case_a_whitespace_check; // Phase 192: Whitespace detector pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱 pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱 pub mod if_dry_runner; // Phase 33-10.0