diff --git a/src/mir/join_ir/frontend/ast_lowerer/analysis.rs b/src/mir/join_ir/frontend/ast_lowerer/analysis.rs index 6a61678e..acdd4ddf 100644 --- a/src/mir/join_ir/frontend/ast_lowerer/analysis.rs +++ b/src/mir/join_ir/frontend/ast_lowerer/analysis.rs @@ -251,7 +251,7 @@ impl AstToJoinIrLowerer { /// Phase 40-1実験用: array_ext.filter パターン専用lowering /// - /// 通常の lower_loop_case_a_simple() に加えて、 + /// 通常の Simple パターンループ lowering に加えて、 /// if-in-loop modified varsをJoinFuncMetaMapとして返す。 /// /// # Returns @@ -260,14 +260,19 @@ impl AstToJoinIrLowerer { /// /// # Phase 40-1専用 /// この関数はPhase 40-1 A/Bテスト専用。 - /// 本番パスでは使わない(従来のlower_loop_case_a_simple()を使う)。 + /// 本番パスでは使わない(新しいloop_patterns::simple::lower()を使う)。 + /// + /// # Phase 85 Note + /// loop_patterns_old.rs削除に伴い、loop_patterns::simple::lower()に委譲 #[allow(dead_code)] pub fn lower_loop_with_if_meta( &mut self, program_json: &serde_json::Value, ) -> (JoinModule, JoinFuncMetaMap) { - // 1. 通常のJoinModule生成(既存ロジック流用) - let module = self.lower_loop_case_a_simple(program_json); + // 1. 通常のJoinModule生成(新パターンシステムに委譲) + use super::loop_patterns; + let module = loop_patterns::simple::lower(self, program_json) + .expect("Simple pattern lowering failed in lower_loop_with_if_meta"); // 2. loop body ASTからif-in-loop modified varsを抽出 let loop_body = self.extract_loop_body_from_program(program_json); @@ -339,4 +344,72 @@ impl AstToJoinIrLowerer { } vars } + + /// Phase 85: Loop body に Break があるかチェック + /// + /// ループパターン検出(loop_frontend_binding)で使用される。 + /// If文内のBreakステートメントを検出する。 + /// + /// # Arguments + /// * `loop_body` - ループ本体のステートメント配列 + /// + /// # Returns + /// ループ内にBreakがあればtrue + pub(crate) fn has_break_in_loop_body(loop_body: &[serde_json::Value]) -> bool { + loop_body.iter().any(|stmt| { + if stmt["type"].as_str() == Some("If") { + let then_has = stmt["then"] + .as_array() + .map(|body| { + body.iter() + .any(|s| s["type"].as_str() == Some("Break")) + }) + .unwrap_or(false); + let else_has = stmt["else"] + .as_array() + .map(|body| { + body.iter() + .any(|s| s["type"].as_str() == Some("Break")) + }) + .unwrap_or(false); + then_has || else_has + } else { + false + } + }) + } + + /// Phase 85: Loop body に Continue があるかチェック + /// + /// ループパターン検出(loop_frontend_binding)で使用される。 + /// If文内のContinueステートメントを検出する。 + /// + /// # Arguments + /// * `loop_body` - ループ本体のステートメント配列 + /// + /// # Returns + /// ループ内にContinueがあればtrue + pub(crate) fn has_continue_in_loop_body(loop_body: &[serde_json::Value]) -> bool { + loop_body.iter().any(|stmt| { + if stmt["type"].as_str() == Some("If") { + let then_has = stmt["then"] + .as_array() + .map(|body| { + body.iter() + .any(|s| s["type"].as_str() == Some("Continue")) + }) + .unwrap_or(false); + let else_has = stmt["else"] + .as_array() + .map(|body| { + body.iter() + .any(|s| s["type"].as_str() == Some("Continue")) + }) + .unwrap_or(false); + then_has || else_has + } else { + false + } + }) + } } diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs index 9931b5f7..f07450cd 100644 --- a/src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs +++ b/src/mir/join_ir/frontend/ast_lowerer/loop_frontend_binding.rs @@ -23,7 +23,7 @@ //! Bridge/VM → Box 名・メソッド名での最適化 //! ``` -use super::loop_patterns::{self, LoopPattern, LoweringError}; +use super::loop_patterns::{self, LoopPattern}; use super::{AstToJoinIrLowerer, JoinModule}; /// 関数名から LoopPattern を検出 @@ -114,14 +114,6 @@ pub fn lower_loop_by_function_name( // 4. loop_patterns 層に委譲 match loop_patterns::lower_loop_with_pattern(pattern.clone(), lowerer, program_json) { Ok(module) => module, - Err(LoweringError::UnimplementedPattern { pattern, reason }) => { - // 未実装パターンは loop_patterns_old にフォールバック - eprintln!( - "[LoopFrontendBinding] Pattern {:?} not implemented: {}. Falling back to old route.", - pattern, reason - ); - lowerer.lower_loop_with_break_continue(program_json) - } Err(e) => panic!("LoopFrontendBinding error: {:?}", e), } } diff --git a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns_old.rs b/src/mir/join_ir/frontend/ast_lowerer/loop_patterns_old.rs deleted file mode 100644 index 092c6835..00000000 --- a/src/mir/join_ir/frontend/ast_lowerer/loop_patterns_old.rs +++ /dev/null @@ -1,914 +0,0 @@ -use super::BTreeMap; -use super::{AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule}; -use crate::mir::join_ir::JoinIrPhase; - -impl AstToJoinIrLowerer { - /// Phase 34-8: Break/Continue 付きループの lowering(パターン検出) - /// - /// Loop body を解析して Break/Continue を検出し、適切な lowering 関数にディスパッチ - pub(super) fn lower_loop_with_break_continue( - &mut self, - program_json: &serde_json::Value, - ) -> JoinModule { - // 1. Program(JSON) から defs を取得 - let defs = program_json["defs"] - .as_array() - .expect("Program(JSON v0) must have 'defs' array"); - - let func_def = defs - .get(0) - .expect("At least one function definition required"); - - // 2. body を解析: Local 初期化 + Loop + Return - let body = &func_def["body"]["body"]; - let stmts = body.as_array().expect("Function body must be array"); - - // Loop ノードを探す - let loop_node = stmts - .iter() - .find(|stmt| stmt["type"].as_str() == Some("Loop")) - .expect("Loop node not found"); - - let loop_body = loop_node["body"] - .as_array() - .expect("Loop must have 'body' array"); - - // Break/Continue を検出 - let has_break = Self::has_break_in_loop_body(loop_body); - let has_continue = Self::has_continue_in_loop_body(loop_body); - - // パターンに応じてディスパッチ - if has_break && !has_continue { - self.lower_loop_break_pattern(program_json) - } else if has_continue && !has_break { - self.lower_loop_continue_pattern(program_json) - } else if has_break && has_continue { - panic!("Mixed Break/Continue pattern not yet supported in Phase 34-8"); - } else { - // Phase 34-7 の simple pattern - self.lower_loop_case_a_simple(program_json) - } - } - - /// Loop body に Break があるかチェック - pub(crate) fn has_break_in_loop_body(loop_body: &[serde_json::Value]) -> bool { - loop_body.iter().any(|stmt| { - if stmt["type"].as_str() == Some("If") { - let then_has = stmt["then"] - .as_array() - .map(|body| { - body.iter() - .any(|s| s["type"].as_str() == Some("Break")) - }) - .unwrap_or(false); - let else_has = stmt["else"] - .as_array() - .map(|body| { - body.iter() - .any(|s| s["type"].as_str() == Some("Break")) - }) - .unwrap_or(false); - then_has || else_has - } else { - false - } - }) - } - - /// Loop body に Continue があるかチェック - pub(crate) fn has_continue_in_loop_body(loop_body: &[serde_json::Value]) -> bool { - loop_body.iter().any(|stmt| { - if stmt["type"].as_str() == Some("If") { - let then_has = stmt["then"] - .as_array() - .map(|body| { - body.iter() - .any(|s| s["type"].as_str() == Some("Continue")) - }) - .unwrap_or(false); - let else_has = stmt["else"] - .as_array() - .map(|body| { - body.iter() - .any(|s| s["type"].as_str() == Some("Continue")) - }) - .unwrap_or(false); - then_has || else_has - } else { - false - } - }) - } - - /// Phase 34-7: Loop pattern の lowering(Case-A: tiny while loop) - /// - /// 対象: `LoopFrontendTest.simple(n)` 相当 - /// - local i = 0; local acc = 0; - /// - loop(i < n) { acc = acc + 1; i = i + 1; } - /// - return acc - /// - /// 目標 JoinIR: Phase 31 の LoopToJoinLowerer と同型 - /// - entry: 初期化 → loop_step 呼び出し - /// - loop_step: 条件チェック → 再帰 or k_exit - /// - k_exit: 結果を return - /// - /// Phase 34-7.4b: Local ノード処理(同名再宣言 = 再代入) - /// ループ本体lowering(Phase 34実装) - /// - /// # Phase 40拡張予定 - /// - /// この関数に以下を追加: - /// 1. `extract_if_in_loop_modified_vars()`呼び出し - /// 2. 検出された変数をloop exit PHI生成に使用 - /// - /// ```rust,ignore - /// // TODO(Phase 40-1): Add if-in-loop variable tracking - /// // let loop_vars = self.get_loop_carried_vars(&loop_ctx); - /// // let modified_in_if = self.extract_if_in_loop_modified_vars(&loop_body, &loop_vars); - /// // Pass modified_in_if to create_exit_function() for PHI generation - /// ``` - pub(super) fn lower_loop_case_a_simple( - &mut self, - program_json: &serde_json::Value, - ) -> JoinModule { - // TODO(Phase 40-1): Add if-in-loop variable tracking here - // Integration point for extract_if_in_loop_modified_vars() - - // 1. Program(JSON) から defs を取得 - let defs = program_json["defs"] - .as_array() - .expect("Program(JSON v0) must have 'defs' array"); - - let func_def = defs - .get(0) - .expect("At least one function definition required"); - - let func_name = func_def["name"] - .as_str() - .expect("Function must have 'name'"); - - let params = func_def["params"] - .as_array() - .expect("Function must have 'params' array"); - - // 2. ExtractCtx 作成とパラメータ登録 - let mut ctx = ExtractCtx::new(params.len() as u32); - for (i, param) in params.iter().enumerate() { - let param_name = param - .as_str() - .expect("Parameter must be string") - .to_string(); - ctx.register_param(param_name, crate::mir::ValueId(i as u32)); - } - - // 3. body を解析: Local 初期化 + Loop + Return - let body = &func_def["body"]["body"]; - let stmts = body.as_array().expect("Function body must be array"); - - // Phase 34-7.4b: Local ノード処理(初期化) - // stmts[0]: Local i = 0 - // stmts[1]: Local acc = 0 - // stmts[2]: Loop { cond, body } - // stmts[3]: Return acc - - let mut init_insts = Vec::new(); - let mut loop_node_idx = None; - - for (idx, stmt) in stmts.iter().enumerate() { - let stmt_type = stmt["type"].as_str().expect("Statement must have type"); - - match stmt_type { - "Local" => { - // Phase 34-7.4b: Local ノード処理 - let var_name = stmt["name"] - .as_str() - .expect("Local must have 'name'") - .to_string(); - let expr = &stmt["expr"]; - - // extract_value で式を評価 - let (var_id, insts) = self.extract_value(expr, &mut ctx); - init_insts.extend(insts); - - // 同名再宣言 = var_map を更新(再代入の意味論) - ctx.register_param(var_name, var_id); - } - "Loop" => { - loop_node_idx = Some(idx); - break; // Loop 以降は別処理 - } - _ => panic!("Unexpected statement type before Loop: {}", stmt_type), - } - } - - let loop_node_idx = loop_node_idx.expect("Loop node not found"); - let loop_node = &stmts[loop_node_idx]; - - // 4. Loop の cond と body を抽出 - let loop_cond_expr = &loop_node["cond"]; - let loop_body_stmts = loop_node["body"] - .as_array() - .expect("Loop must have 'body' array"); - - // 5. Return 文を抽出(Loop の次) - let return_stmt = &stmts[loop_node_idx + 1]; - assert_eq!( - return_stmt["type"].as_str(), - Some("Return"), - "Expected Return after Loop" - ); - - // 6. JoinIR 生成: entry / loop_step / k_exit(Phase 31 と同じパターン) - // - // Phase 34-7: Jump = 早期 return、Call = 末尾再帰 - // - entry: 初期化 → Call(loop_step) - // - loop_step: - // Jump(k_exit, cond=!(i = params - .iter() - .enumerate() - .filter_map(|(idx, param)| { - param.as_str().and_then(|name| { - if !reserved_vars.contains(&name) { - Some((name.to_string(), crate::mir::ValueId(idx as u32))) - } else { - None - } - }) - }) - .collect(); - - let entry_id = self.next_func_id(); - let loop_step_id = self.next_func_id(); - let k_exit_id = self.next_func_id(); - - // entry 関数: 初期化命令 + Call(loop_step) - let i_init = ctx.get_var("i").expect("i must be initialized"); - let acc_init = ctx.get_var("acc").expect("acc must be initialized"); - let n_param = ctx.get_var("n").expect("n must be parameter"); - - // Phase 52: Get me from context if it was registered as a param - let me_param = ctx.get_var("me"); - - let loop_result = ctx.alloc_var(); - - // Phase 52/56: Include me and external_refs in args when present - let mut entry_call_args = if let Some(me_id) = me_param { - vec![me_id, i_init, acc_init, n_param] - } else { - vec![i_init, acc_init, n_param] - }; - // Phase 56: Add external_refs to entry call args - // Get them from ctx using their original names - for (name, _original_id) in &external_refs { - if let Some(var_id) = ctx.get_var(name) { - entry_call_args.push(var_id); - } - } - - let mut entry_body = init_insts; - entry_body.push(JoinInst::Call { - func: loop_step_id, - args: entry_call_args, - k_next: None, - dst: Some(loop_result), - }); - entry_body.push(JoinInst::Ret { - value: Some(loop_result), - }); - - let entry_func = JoinFunction { - id: entry_id, - name: func_name.to_string(), - params: (0..params.len()) - .map(|i| crate::mir::ValueId(i as u32)) - .collect(), - body: entry_body, - exit_cont: None, - }; - - // loop_step 関数: (i, acc, n) → Jump(k_exit, cond=!(i= n なら抜ける) - let false_const = step_ctx.alloc_var(); - let exit_cond = step_ctx.alloc_var(); - - let mut loop_step_body = cond_insts; - loop_step_body.push(JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const { - dst: false_const, - value: ConstValue::Bool(false), - })); - loop_step_body.push(JoinInst::Compute( - crate::mir::join_ir::MirLikeInst::Compare { - dst: exit_cond, - op: crate::mir::join_ir::CompareOp::Eq, - lhs: cond_var, - rhs: false_const, - }, - )); - - // 早期 return: exit_cond が true(i >= n)なら k_exit へ Jump - loop_step_body.push(JoinInst::Jump { - cont: k_exit_id.as_cont(), // Phase 34-7.5: 型変換ヘルパー使用 - args: vec![step_acc], - cond: Some(exit_cond), - }); - - // Phase 53: loop body を処理(汎用 statement handler を使用) - // Jump で抜けなかった場合のみ実行される - for body_stmt in loop_body_stmts { - let (insts, _effect) = self.lower_statement(body_stmt, &mut step_ctx); - loop_step_body.extend(insts); - // Note: lower_statement 内で ctx.register_param() が呼ばれるため、 - // ここでの追加登録は不要 - } - - // body 処理後の i_next, acc_next を取得 - let i_next = step_ctx - .get_var("i") - .expect("i must be updated in loop body"); - let acc_next = step_ctx - .get_var("acc") - .expect("acc must be updated in loop body"); - - // loop_step を再帰的に Call(末尾再帰) - // Phase 52/56: Include me and external_refs in args when present - let mut recurse_args = if let Some(me_id) = step_me { - vec![me_id, i_next, acc_next, step_n] - } else { - vec![i_next, acc_next, step_n] - }; - // Phase 56: Add external_refs to recurse args (they are passed through unchanged) - for (name, _) in &external_refs { - if let Some(var_id) = step_ctx.get_var(name) { - recurse_args.push(var_id); - } - } - - let recurse_result = step_ctx.alloc_var(); - loop_step_body.push(JoinInst::Call { - func: loop_step_id, - args: recurse_args, - k_next: None, - dst: Some(recurse_result), - }); - - loop_step_body.push(JoinInst::Ret { - value: Some(recurse_result), - }); - - // Phase 52/56: Include me and external_refs in params when present - let ext_offset = if has_me { 4 } else { 3 }; - let mut loop_step_params = if let Some(me_id) = step_me { - vec![me_id, step_i, step_acc, step_n] - } else { - vec![step_i, step_acc, step_n] - }; - // Phase 56: Add external_refs to step params - for (i, _) in external_refs.iter().enumerate() { - loop_step_params.push(crate::mir::ValueId(ext_offset + i as u32)); - } - - let loop_step_func = JoinFunction { - id: loop_step_id, - name: format!("{}_loop_step", func_name), - params: loop_step_params, - body: loop_step_body, - exit_cont: None, - }; - - // k_exit 関数: (acc) → Ret acc - let k_exit_acc = crate::mir::ValueId(0); - - let k_exit_func = JoinFunction { - id: k_exit_id, - name: format!("{}_k_exit", func_name), - params: vec![k_exit_acc], - body: vec![JoinInst::Ret { - value: Some(k_exit_acc), - }], - exit_cont: None, - }; - - // JoinModule を構築 - let mut functions = BTreeMap::new(); - functions.insert(entry_id, entry_func); - functions.insert(loop_step_id, loop_step_func); - functions.insert(k_exit_id, k_exit_func); - - JoinModule { - functions, - entry: Some(entry_id), - phase: JoinIrPhase::Structured, - } - } - - /// Phase 34-8: Break pattern の lowering - /// - /// パターン: `loop { if i >= n { break }; acc = acc + i; i = i + 1 }` - /// 目標: Jump(k_exit, cond=i>=n) で早期 return を実現 - pub(super) fn lower_loop_break_pattern( - &mut self, - program_json: &serde_json::Value, - ) -> JoinModule { - // 1. Program(JSON) から基本情報を取得(Phase 34-7 と同じ) - let defs = program_json["defs"] - .as_array() - .expect("Program(JSON v0) must have 'defs' array"); - - let func_def = defs - .get(0) - .expect("At least one function definition required"); - - let func_name = func_def["name"] - .as_str() - .expect("Function must have 'name'"); - - let params = func_def["params"] - .as_array() - .expect("Function must have 'params' array"); - - // 2. ExtractCtx 作成とパラメータ登録 - let mut ctx = ExtractCtx::new(params.len() as u32); - for (i, param) in params.iter().enumerate() { - let param_name = param - .as_str() - .expect("Parameter must be string") - .to_string(); - ctx.register_param(param_name, crate::mir::ValueId(i as u32)); - } - - // 3. body を解析: Local 初期化 + Loop + Return - let body = &func_def["body"]["body"]; - let stmts = body.as_array().expect("Function body must be array"); - - // Local ノード処理(初期化) - let mut init_insts = Vec::new(); - let mut loop_node_idx = None; - - for (idx, stmt) in stmts.iter().enumerate() { - let stmt_type = stmt["type"].as_str().expect("Statement must have type"); - - match stmt_type { - "Local" => { - let var_name = stmt["name"] - .as_str() - .expect("Local must have 'name'") - .to_string(); - let expr = &stmt["expr"]; - - let (var_id, insts) = self.extract_value(expr, &mut ctx); - init_insts.extend(insts); - ctx.register_param(var_name, var_id); - } - "Loop" => { - loop_node_idx = Some(idx); - break; - } - _ => panic!("Unexpected statement type before Loop: {}", stmt_type), - } - } - - let loop_node_idx = loop_node_idx.expect("Loop node not found"); - let loop_node = &stmts[loop_node_idx]; - - // 4. Loop body から Break If を探す - let loop_body = loop_node["body"] - .as_array() - .expect("Loop must have 'body' array"); - - let break_if_stmt = loop_body - .iter() - .find(|stmt| { - stmt["type"].as_str() == Some("If") - && stmt["then"].as_array().map_or(false, |then| { - then.iter().any(|s| s["type"].as_str() == Some("Break")) - }) - }) - .expect("Break pattern must have If + Break"); - - let break_cond_expr = &break_if_stmt["cond"]; - - // 5. JoinIR 生成: entry / loop_step / k_exit(3関数構造) - let entry_id = self.next_func_id(); - let loop_step_id = self.next_func_id(); - let k_exit_id = self.next_func_id(); - - // entry 関数 - let i_init = ctx.get_var("i").expect("i must be initialized"); - let acc_init = ctx.get_var("acc").expect("acc must be initialized"); - let n_param = ctx.get_var("n").expect("n must be parameter"); - - let loop_result = ctx.alloc_var(); - - let mut entry_body = init_insts; - entry_body.push(JoinInst::Call { - func: loop_step_id, - args: vec![i_init, acc_init, n_param], - k_next: None, - dst: Some(loop_result), - }); - entry_body.push(JoinInst::Ret { - value: Some(loop_result), - }); - - let entry_func = JoinFunction { - id: entry_id, - name: func_name.to_string(), - params: (0..params.len()) - .map(|i| crate::mir::ValueId(i as u32)) - .collect(), - body: entry_body, - exit_cont: None, - }; - - // loop_step 関数: (i, acc, n) → Jump(k_exit, cond=break_cond) → body → Call(loop_step) - let step_i = crate::mir::ValueId(0); - let step_acc = crate::mir::ValueId(1); - let step_n = crate::mir::ValueId(2); - - let mut step_ctx = ExtractCtx::new(3); - step_ctx.register_param("i".to_string(), step_i); - step_ctx.register_param("acc".to_string(), step_acc); - step_ctx.register_param("n".to_string(), step_n); - - // Break 条件を評価 - let (break_cond_var, break_cond_insts) = self.extract_value(break_cond_expr, &mut step_ctx); - - let mut loop_step_body = break_cond_insts; - - // 早期 return: break_cond が true なら k_exit へ Jump - loop_step_body.push(JoinInst::Jump { - cont: k_exit_id.as_cont(), - args: vec![step_acc], - cond: Some(break_cond_var), - }); - - // Phase 53: loop body を処理(汎用 statement handler を使用) - // Break の後のステートメント群 - for body_stmt in loop_body { - // If + Break はスキップ(Jump で処理済み) - if body_stmt["type"].as_str() == Some("If") { - continue; - } - - let (insts, _effect) = self.lower_statement(body_stmt, &mut step_ctx); - loop_step_body.extend(insts); - } - - // body 処理後の i_next, acc_next を取得 - let i_next = step_ctx - .get_var("i") - .expect("i must be updated in loop body"); - let acc_next = step_ctx - .get_var("acc") - .expect("acc must be updated in loop body"); - - // loop_step を再帰的に Call(末尾再帰) - let recurse_result = step_ctx.alloc_var(); - loop_step_body.push(JoinInst::Call { - func: loop_step_id, - args: vec![i_next, acc_next, step_n], - k_next: None, - dst: Some(recurse_result), - }); - - loop_step_body.push(JoinInst::Ret { - value: Some(recurse_result), - }); - - let loop_step_func = JoinFunction { - id: loop_step_id, - name: format!("{}_loop_step", func_name), - params: vec![step_i, step_acc, step_n], - body: loop_step_body, - exit_cont: None, - }; - - // k_exit 関数 - let k_exit_acc = crate::mir::ValueId(0); - - let k_exit_func = JoinFunction { - id: k_exit_id, - name: format!("{}_k_exit", func_name), - params: vec![k_exit_acc], - body: vec![JoinInst::Ret { - value: Some(k_exit_acc), - }], - exit_cont: None, - }; - - // JoinModule を構築 - let mut functions = BTreeMap::new(); - functions.insert(entry_id, entry_func); - functions.insert(loop_step_id, loop_step_func); - functions.insert(k_exit_id, k_exit_func); - - JoinModule { - functions, - entry: Some(entry_id), - phase: JoinIrPhase::Structured, - } - } - - /// Phase 34-8: Continue pattern の lowering - /// - /// パターン: `loop { i = i + 1; if i == 3 { continue }; acc = acc + i }` - /// 目標: Select(条件付き値更新)+ Call(末尾再帰)で実現 - pub(super) fn lower_loop_continue_pattern( - &mut self, - program_json: &serde_json::Value, - ) -> JoinModule { - // 1. Program(JSON) から基本情報を取得 - let defs = program_json["defs"] - .as_array() - .expect("Program(JSON v0) must have 'defs' array"); - - let func_def = defs - .get(0) - .expect("At least one function definition required"); - - let func_name = func_def["name"] - .as_str() - .expect("Function must have 'name'"); - - let params = func_def["params"] - .as_array() - .expect("Function must have 'params' array"); - - // 2. ExtractCtx 作成とパラメータ登録 - let mut ctx = ExtractCtx::new(params.len() as u32); - for (i, param) in params.iter().enumerate() { - let param_name = param - .as_str() - .expect("Parameter must be string") - .to_string(); - ctx.register_param(param_name, crate::mir::ValueId(i as u32)); - } - - // 3. body を解析: Local 初期化 + Loop + Return - let body = &func_def["body"]["body"]; - let stmts = body.as_array().expect("Function body must be array"); - - // Local ノード処理(初期化) - let mut init_insts = Vec::new(); - let mut loop_node_idx = None; - - for (idx, stmt) in stmts.iter().enumerate() { - let stmt_type = stmt["type"].as_str().expect("Statement must have type"); - - match stmt_type { - "Local" => { - let var_name = stmt["name"] - .as_str() - .expect("Local must have 'name'") - .to_string(); - let expr = &stmt["expr"]; - - let (var_id, insts) = self.extract_value(expr, &mut ctx); - init_insts.extend(insts); - ctx.register_param(var_name, var_id); - } - "Loop" => { - loop_node_idx = Some(idx); - break; - } - _ => panic!("Unexpected statement type before Loop: {}", stmt_type), - } - } - - let loop_node_idx = loop_node_idx.expect("Loop node not found"); - let loop_node = &stmts[loop_node_idx]; - - // 4. Loop の cond を取得 - let loop_cond_expr = &loop_node["cond"]; - - // 5. Loop body から Continue If を探す - let loop_body = loop_node["body"] - .as_array() - .expect("Loop must have 'body' array"); - - let continue_if_stmt = loop_body - .iter() - .find(|stmt| { - stmt["type"].as_str() == Some("If") - && stmt["then"].as_array().map_or(false, |then| { - then.iter().any(|s| s["type"].as_str() == Some("Continue")) - }) - }) - .expect("Continue pattern must have If + Continue"); - - let continue_cond_expr = &continue_if_stmt["cond"]; - - // 6. JoinIR 生成: entry / loop_step / k_exit(3関数構造) - let entry_id = self.next_func_id(); - let loop_step_id = self.next_func_id(); - let k_exit_id = self.next_func_id(); - - // entry 関数 - let i_init = ctx.get_var("i").expect("i must be initialized"); - let acc_init = ctx.get_var("acc").expect("acc must be initialized"); - let n_param = ctx.get_var("n").expect("n must be parameter"); - - let loop_result = ctx.alloc_var(); - - let mut entry_body = init_insts; - entry_body.push(JoinInst::Call { - func: loop_step_id, - args: vec![i_init, acc_init, n_param], - k_next: None, - dst: Some(loop_result), - }); - entry_body.push(JoinInst::Ret { - value: Some(loop_result), - }); - - let entry_func = JoinFunction { - id: entry_id, - name: func_name.to_string(), - params: (0..params.len()) - .map(|i| crate::mir::ValueId(i as u32)) - .collect(), - body: entry_body, - exit_cont: None, - }; - - // loop_step 関数: (i, acc, n) → exit check → i++ → Select → Call(loop_step) - let step_i = crate::mir::ValueId(0); - let step_acc = crate::mir::ValueId(1); - let step_n = crate::mir::ValueId(2); - - let mut step_ctx = ExtractCtx::new(3); - step_ctx.register_param("i".to_string(), step_i); - step_ctx.register_param("acc".to_string(), step_acc); - step_ctx.register_param("n".to_string(), step_n); - - let mut loop_step_body = Vec::new(); - - // 1. exit 条件チェック(!(i < n) = i >= n で抜ける) - let (cond_var, cond_insts) = self.extract_value(loop_cond_expr, &mut step_ctx); - loop_step_body.extend(cond_insts); - - let false_const = step_ctx.alloc_var(); - let exit_cond = step_ctx.alloc_var(); - - loop_step_body.push(JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const { - dst: false_const, - value: ConstValue::Bool(false), - })); - loop_step_body.push(JoinInst::Compute( - crate::mir::join_ir::MirLikeInst::Compare { - dst: exit_cond, - op: crate::mir::join_ir::CompareOp::Eq, - lhs: cond_var, - rhs: false_const, - }, - )); - - loop_step_body.push(JoinInst::Jump { - cont: k_exit_id.as_cont(), - args: vec![step_acc], - cond: Some(exit_cond), - }); - - // 2. Continue pattern 特有の処理: i のインクリメントが先 - // Loop body の最初の Local(i) を処理 - let first_local = loop_body - .iter() - .find(|stmt| { - stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("i") - }) - .expect("Continue pattern must have i increment as first Local"); - - let i_expr = &first_local["expr"]; - let (i_next, i_insts) = self.extract_value(i_expr, &mut step_ctx); - loop_step_body.extend(i_insts); - step_ctx.register_param("i".to_string(), i_next); - - // 3. Continue 条件を評価 - let (continue_cond_var, continue_cond_insts) = - self.extract_value(continue_cond_expr, &mut step_ctx); - loop_step_body.extend(continue_cond_insts); - - // 4. acc の更新値を計算(If の後の Local(acc) から) - let acc_update_local = loop_body - .iter() - .find(|stmt| { - stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc") - }) - .expect("Continue pattern must have acc update Local"); - - let acc_expr = &acc_update_local["expr"]; - let (acc_increment, acc_insts) = self.extract_value(acc_expr, &mut step_ctx); - loop_step_body.extend(acc_insts); - - // 5. Select: Continue なら acc そのまま、そうでなければ acc の更新値 - let acc_next = step_ctx.alloc_var(); - loop_step_body.push(JoinInst::Select { - dst: acc_next, - cond: continue_cond_var, - then_val: step_acc, // Continue: 更新しない - else_val: acc_increment, // 通常: 更新 - type_hint: None, // Phase 63-3 - }); - - // 6. 末尾再帰 - let recurse_result = step_ctx.alloc_var(); - loop_step_body.push(JoinInst::Call { - func: loop_step_id, - args: vec![i_next, acc_next, step_n], - k_next: None, - dst: Some(recurse_result), - }); - - loop_step_body.push(JoinInst::Ret { - value: Some(recurse_result), - }); - - let loop_step_func = JoinFunction { - id: loop_step_id, - name: format!("{}_loop_step", func_name), - params: vec![step_i, step_acc, step_n], - body: loop_step_body, - exit_cont: None, - }; - - // k_exit 関数 - let k_exit_acc = crate::mir::ValueId(0); - - let k_exit_func = JoinFunction { - id: k_exit_id, - name: format!("{}_k_exit", func_name), - params: vec![k_exit_acc], - body: vec![JoinInst::Ret { - value: Some(k_exit_acc), - }], - exit_cont: None, - }; - - // JoinModule を構築 - let mut functions = BTreeMap::new(); - functions.insert(entry_id, entry_func); - functions.insert(loop_step_id, loop_step_func); - functions.insert(k_exit_id, k_exit_func); - - JoinModule { - functions, - entry: Some(entry_id), - phase: JoinIrPhase::Structured, - } - } -} diff --git a/src/mir/join_ir/frontend/ast_lowerer/mod.rs b/src/mir/join_ir/frontend/ast_lowerer/mod.rs index 284b6a94..8c095db9 100644 --- a/src/mir/join_ir/frontend/ast_lowerer/mod.rs +++ b/src/mir/join_ir/frontend/ast_lowerer/mod.rs @@ -32,7 +32,7 @@ mod if_in_loop; mod if_return; mod loop_frontend_binding; mod loop_patterns; -mod loop_patterns_old; +// Removed: loop_patterns_old (obsolete legacy dispatcher, all patterns now in loop_patterns/) mod nested_if; mod read_quoted; mod stmt_handlers; diff --git a/src/mir/join_ir/lowering/carrier_binding_assigner.rs b/src/mir/join_ir/lowering/carrier_binding_assigner.rs index 40824f25..99c99009 100644 --- a/src/mir/join_ir/lowering/carrier_binding_assigner.rs +++ b/src/mir/join_ir/lowering/carrier_binding_assigner.rs @@ -138,16 +138,14 @@ impl CarrierBindingAssigner { }; carrier.binding_id = Some(promoted_bid); - use crate::config::env::is_joinir_debug; - if is_joinir_debug() || std::env::var("JOINIR_TEST_DEBUG").is_ok() { - eprintln!( - "[phase78/carrier_assigner] '{}' (BindingId({})) → '{}' (BindingId({}))", - original_name, - original_bid.0, - promoted_carrier_name, - promoted_bid.0 - ); - } + use super::debug_output_box::DebugOutputBox; + let debug = DebugOutputBox::new("phase78/carrier_assigner"); + debug.log_if_enabled(|| { + format!( + "'{}' (BindingId({})) → '{}' (BindingId({}))", + original_name, original_bid.0, promoted_carrier_name, promoted_bid.0 + ) + }); // Restore builder.binding_map to avoid leaking synthetic names into the source map. match prev_original { diff --git a/src/mir/join_ir/lowering/condition_env.rs b/src/mir/join_ir/lowering/condition_env.rs index 91608e9a..ccbc1504 100644 --- a/src/mir/join_ir/lowering/condition_env.rs +++ b/src/mir/join_ir/lowering/condition_env.rs @@ -307,37 +307,30 @@ impl ConditionEnv { binding_id: Option, name: &str, ) -> Option { - use crate::config::env::is_joinir_debug; + use super::debug_output_box::DebugOutputBox; + let debug = DebugOutputBox::new("binding_pilot"); + if let Some(bid) = binding_id { // Try BindingId lookup first if let Some(&value_id) = self.binding_id_map.get(&bid) { - if is_joinir_debug() { - eprintln!( - "[binding_pilot/hit] BindingId({}) -> ValueId({}) for '{}'", - bid.0, value_id.0, name - ); - } + debug.log( + "hit", + &format!("BindingId({}) -> ValueId({}) for '{}'", bid.0, value_id.0, name), + ); return Some(value_id); } else { // BindingId miss, fall back to name let result = self.get(name); - if is_joinir_debug() { - eprintln!( - "[binding_pilot/fallback] BindingId({}) miss, name '{}' -> {:?}", - bid.0, name, result - ); - } + debug.log( + "fallback", + &format!("BindingId({}) miss, name '{}' -> {:?}", bid.0, name, result), + ); return result; } } else { // Legacy: no BindingId, use name lookup let result = self.get(name); - if is_joinir_debug() { - eprintln!( - "[binding_pilot/legacy] No BindingId, name '{}' -> {:?}", - name, result - ); - } + debug.log("legacy", &format!("No BindingId, name '{}' -> {:?}", name, result)); return result; } } diff --git a/src/mir/join_ir/lowering/debug_output_box.rs b/src/mir/join_ir/lowering/debug_output_box.rs new file mode 100644 index 00000000..66a0da15 --- /dev/null +++ b/src/mir/join_ir/lowering/debug_output_box.rs @@ -0,0 +1,164 @@ +//! Phase 85: DebugOutputBox - Centralized debug output management for JoinIR +//! +//! ## Purpose +//! Provides structured debug output with automatic flag checking to eliminate +//! scattered `if is_joinir_debug() { eprintln!(...) }` patterns. +//! +//! ## Usage +//! ```rust,ignore +//! // Before: +//! if is_joinir_debug() { +//! eprintln!("[phase80/p3] Registered loop var..."); +//! } +//! +//! // After: +//! let debug = DebugOutputBox::new("phase80/p3"); +//! debug.log("register", "Registered loop var..."); +//! ``` +//! +//! ## Benefits +//! - Centralized debug output control +//! - Consistent log formatting +//! - Feature-gated (no-op in production) +//! - Zero runtime cost when disabled + +use crate::config::env::is_joinir_debug; + +/// DebugOutputBox: Centralized debug output for JoinIR lowering +/// +/// Automatically checks HAKO_JOINIR_DEBUG flag and formats output consistently. +#[derive(Debug)] +pub struct DebugOutputBox { + enabled: bool, + context_tag: String, +} + +impl DebugOutputBox { + /// Create a new DebugOutputBox with the given context tag + /// + /// # Arguments + /// * `context_tag` - Identifies the subsystem (e.g., "phase80/p3", "carrier_info") + /// + /// # Example + /// ```rust,ignore + /// let debug = DebugOutputBox::new("phase80/p3"); + /// ``` + pub fn new(context_tag: impl Into) -> Self { + Self { + enabled: is_joinir_debug(), + context_tag: context_tag.into(), + } + } + + /// Log a debug message with category + /// + /// Output format: `[context_tag/category] message` + /// + /// # Arguments + /// * `category` - Sub-category (e.g., "register", "promote", "bind") + /// * `message` - Debug message + /// + /// # Example + /// ```rust,ignore + /// debug.log("register", "loop var 'i' BindingId(1) -> ValueId(5)"); + /// // Output: [phase80/p3/register] loop var 'i' BindingId(1) -> ValueId(5) + /// ``` + pub fn log(&self, category: &str, message: &str) { + if self.enabled { + eprintln!("[{}/{}] {}", self.context_tag, category, message); + } + } + + /// Log a message without category + /// + /// Output format: `[context_tag] message` + /// + /// # Example + /// ```rust,ignore + /// debug.log_simple("Processing loop body"); + /// // Output: [phase80/p3] Processing loop body + /// ``` + pub fn log_simple(&self, message: &str) { + if self.enabled { + eprintln!("[{}] {}", self.context_tag, message); + } + } + + /// Log only if enabled (with lazy message generation) + /// + /// Useful when message construction is expensive. + /// + /// # Example + /// ```rust,ignore + /// debug.log_if_enabled(|| { + /// format!("Complex value: {:?}", expensive_computation()) + /// }); + /// ``` + pub fn log_if_enabled(&self, f: impl FnOnce() -> String) { + if self.enabled { + let msg = f(); + eprintln!("[{}] {}", self.context_tag, msg); + } + } + + /// Check if debug output is enabled + /// + /// Useful for conditional code that shouldn't run in production. + /// + /// # Example + /// ```rust,ignore + /// if debug.is_enabled() { + /// // Expensive debug-only validation + /// } + /// ``` + pub fn is_enabled(&self) -> bool { + self.enabled + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_debug_output_box_creation() { + let debug = DebugOutputBox::new("test/context"); + assert_eq!(debug.context_tag, "test/context"); + // Note: is_enabled() depends on env var HAKO_JOINIR_DEBUG + } + + #[test] + fn test_log_methods_dont_panic() { + let debug = DebugOutputBox::new("test"); + + // These should never panic, even if disabled + debug.log("category", "message"); + debug.log_simple("simple message"); + debug.log_if_enabled(|| "lazy message".to_string()); + } + + #[test] + fn test_is_enabled_returns_bool() { + let debug = DebugOutputBox::new("test"); + let enabled = debug.is_enabled(); + + // Should return a boolean (either true or false) + assert!(enabled == true || enabled == false); + } + + #[test] + fn test_lazy_message_only_called_if_enabled() { + let debug = DebugOutputBox::new("test"); + let mut called = false; + + debug.log_if_enabled(|| { + called = true; + "message".to_string() + }); + + // If debug is disabled, called should still be false + // If debug is enabled, called will be true + // Either outcome is valid - we just verify no panic + let _ = called; // Use the variable to avoid warning + } +} diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index e88f14c7..2149a21f 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -25,6 +25,7 @@ pub mod carrier_info; // Phase 196: Carrier metadata for loop lowering pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission pub(crate) mod common; // Internal lowering utilities pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing) +pub mod debug_output_box; // Phase 85: Centralized debug output management pub mod condition_env; // Phase 171-fix: Condition expression environment pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based) diff --git a/src/mir/join_ir/lowering/scope_manager.rs b/src/mir/join_ir/lowering/scope_manager.rs index 2ea34908..1c3d59ad 100644 --- a/src/mir/join_ir/lowering/scope_manager.rs +++ b/src/mir/join_ir/lowering/scope_manager.rs @@ -266,16 +266,16 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> { /// promoters populate promoted_bindings map and all call sites provide BindingId. #[cfg(feature = "normalized_dev")] fn lookup_with_binding(&self, binding_id: Option, name: &str) -> Option { - use crate::config::env::is_joinir_debug; + use super::debug_output_box::DebugOutputBox; + let debug = DebugOutputBox::new("phase76"); + if let Some(bid) = binding_id { // Step 1: Try direct BindingId lookup in ConditionEnv (Phase 75) if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(bid), name) { - if is_joinir_debug() { - eprintln!( - "[phase76/direct] BindingId({}) -> ValueId({}) for '{}'", - bid.0, value_id.0, name - ); - } + debug.log( + "direct", + &format!("BindingId({}) -> ValueId({}) for '{}'", bid.0, value_id.0, name), + ); return Some(value_id); } @@ -283,12 +283,13 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> { if let Some(promoted_bid) = self.carrier_info.resolve_promoted_with_binding(bid) { // Promoted BindingId found, lookup in ConditionEnv if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(promoted_bid), name) { - if is_joinir_debug() { - eprintln!( - "[phase76/promoted] BindingId({}) promoted to BindingId({}) -> ValueId({}) for '{}'", + debug.log( + "promoted", + &format!( + "BindingId({}) promoted to BindingId({}) -> ValueId({}) for '{}'", bid.0, promoted_bid.0, value_id.0, name - ); - } + ), + ); return Some(value_id); } } @@ -301,12 +302,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> { bid.0, name ); #[cfg(not(feature = "normalized_dev"))] - if is_joinir_debug() { - eprintln!( - "[phase76/fallback] BindingId({}) miss, falling back to name '{}' lookup", - bid.0, name - ); - } + debug.log( + "fallback", + &format!("BindingId({}) miss, falling back to name '{}' lookup", bid.0, name), + ); } // Step 4: Legacy name-based lookup (Phase 75 behavior)