refactor(mir): Phase 93 リファクタリング - 箱化モジュール化

## 概要
Phase 93 P0実装後のコード整理。スケジュール決定ロジックとbreak semanticsを
明確化し、デバッグログを統一。

## 変更内容

### 1. スケジュール決定ロジックの関数化 (step_schedule.rs)
- `ScheduleDecision`構造体追加(判定結果+理由+デバッグコンテキスト)
- `decide_pattern2_schedule()` - スケジュール決定のSSOT
- `build_pattern2_schedule_from_decision()` - 新しい決定ベースAPI
- 判定理由が4種類で明確化(ConditionOnly → body-local → loop-local → default)
- 後方互換性維持(`Pattern2ScheduleContext`はwrapperに)

### 2. ConditionOnlyRecipe強化 (condition_only_emitter.rs)
- `BreakSemantics` enum追加(WhenMatch vs WhenNotMatch)
- `generate_break_condition()` - semanticsに基づくAST生成
- `from_trim_helper_condition_only()` - factory method追加
- break semanticsがrecipeに明示的に含まれる

### 3. trim_loop_lowering.rs簡素化
- `generate_condition_only_break_condition()`削除(DRY原則)
- `recipe.generate_break_condition()`で統一
- break条件生成ロジックが1箇所に集約

### 4. デバッグログ統一
- `[phase93/schedule]` - スケジュール決定
- `[phase93/condition-only]` - ConditionOnlyレシピ作成
- `[phase93/break-cond]` - break条件生成
- 既存の`joinir_dev_enabled()`使用(新規env var不要)

## テスト結果
- step_schedule: 10 tests PASS
- condition_only_emitter: 4 tests PASS
- 後方互換性維持

## 統計
- 3ファイル変更
- +249行 / -57行 = +192 net

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-16 23:43:03 +09:00
parent 93e62b1433
commit c213ecc3c0
3 changed files with 249 additions and 57 deletions

View File

@ -369,29 +369,35 @@ impl TrimLoopLowerer {
);
// Step 7: Generate break condition based on pattern type
// Phase 93 P0: ConditionOnly uses non-negated condition (break when is_ch_match is TRUE)
// Normal Trim uses negated condition (break when !is_ch_match, i.e., ch is NOT whitespace)
let trim_break_condition = if condition_only_recipe.is_some() {
// ConditionOnly: "break when match" semantics
// Generate: is_ch_match (TRUE when we should break)
Self::generate_condition_only_break_condition(trim_helper)
// Phase 93 Refactoring: Use recipe.generate_break_condition() for unified logic
let trim_break_condition = if let Some(ref recipe) = condition_only_recipe {
// Use recipe's break semantics (WhenMatch or WhenNotMatch)
trace.emit_if(
"trim",
"break-cond",
&format!(
"Generated break condition from recipe: {} (semantics: {:?})",
trim_helper.carrier_name,
recipe.break_semantics
),
verbose,
);
recipe.generate_break_condition()
} else {
// Normal Trim: "break when NOT match" semantics
// Generate: !is_ch_match (TRUE when we should break)
trace.emit_if(
"trim",
"break-cond",
&format!(
"Generated normal trim break condition: !{}",
trim_helper.carrier_name
),
verbose,
);
Self::generate_trim_break_condition(trim_helper)
};
trace.emit_if(
"trim",
"cond",
&format!(
"Break condition: {} (ConditionOnly={})",
trim_helper.carrier_name,
condition_only_recipe.is_some()
),
verbose,
);
// Step 8: Return result with all updates
Ok(Some(TrimLoweringResult {
condition: trim_break_condition,
@ -533,27 +539,11 @@ impl TrimLoopLowerer {
TrimPatternLowerer::generate_trim_break_condition(trim_helper)
}
/// Generate ConditionOnly break condition
///
/// Phase 93 P0: For ConditionOnly patterns where break happens WHEN the condition is TRUE.
///
/// Returns: is_carrier (non-negated carrier check)
/// Used for "break when match" semantics (e.g., find-first pattern)
fn generate_condition_only_break_condition(
trim_helper: &crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper,
) -> ASTNode {
use crate::ast::Span;
// Return just the carrier variable (non-negated)
// When is_ch_match is TRUE, we should break
ASTNode::Variable {
name: trim_helper.carrier_name.clone(),
span: Span::unknown(),
}
}
/// Setup ConditionEnv bindings for Trim carrier
///
/// Phase 180-3: Extracted from Pattern2 (lines 345-377)
/// Phase 93 Refactoring: Use explicit factory methods for recipe creation
///
/// Creates bindings for:
/// 1. Carrier variable (e.g., "is_ch_match")
@ -569,15 +559,16 @@ impl TrimLoopLowerer {
let verbose = crate::config::env::joinir_dev_enabled() || trace.is_joinir_enabled();
// Phase 93 P0: Do NOT add is_ch_match to ConditionBinding
// Instead, create a ConditionOnlyRecipe for recalculation every iteration
let recipe = ConditionOnlyRecipe::from_trim_helper(trim_helper);
// Phase 93 Refactoring: Use explicit factory method for ConditionOnly pattern
let recipe = ConditionOnlyRecipe::from_trim_helper_condition_only(trim_helper);
trace.emit_if(
"trim",
"condition-only",
&format!(
"Phase 93 P0: Created ConditionOnlyRecipe for '{}' (will be recalculated each iteration, not carried via ConditionBinding)",
trim_helper.carrier_name
"[phase93/condition-only] Created ConditionOnlyRecipe for '{}' (semantics: {:?}, will be recalculated each iteration)",
trim_helper.carrier_name,
recipe.break_semantics
),
verbose,
);