refactor(joinir): Extract legacy binding path to routing_legacy_binding.rs

Phase 179-A Step 2: Separate LoopFrontendBinding JSON construction logic
into dedicated module for better organization.

Changes:
- New file: routing_legacy_binding.rs (223 lines)
- routing.rs: cf_loop_joinir_impl() simplified to 15 lines (delegates to legacy path)
- Routing now clearly separates pattern-based vs. legacy binding paths

Benefits:
- Clear separation of concerns (pattern router vs. legacy whitelist)
- routing.rs reduced from 364 to 146 lines (60% reduction)
- Legacy path isolated for future deprecation
This commit is contained in:
nyash-codex
2025-12-08 18:36:13 +09:00
parent 7a01ffe522
commit 95f3aa429e
25 changed files with 626 additions and 755 deletions

View File

@ -14,8 +14,16 @@ use std::collections::HashMap;
pub struct CarrierVar {
/// Variable name (e.g., "sum", "printed")
pub name: String,
/// Host ValueId for this variable
/// Host ValueId for this variable (MIR側)
pub host_id: ValueId,
/// Phase 177-STRUCT: JoinIR側でこのキャリアを表すValueId
///
/// ヘッダPHIのdstや、exitで使う値を記録する。
/// これにより、index ベースのマッチングを名前ベースに置き換えられる。
///
/// - `Some(vid)`: Header PHI生成後にセットされる
/// - `None`: まだPHI生成前、または該当なし
pub join_id: Option<ValueId>,
}
/// Complete carrier information for a loop
@ -77,6 +85,7 @@ impl CarrierInfo {
.map(|(name, &id)| CarrierVar {
name: name.clone(),
host_id: id,
join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation
})
.collect();
@ -133,6 +142,7 @@ impl CarrierInfo {
carriers.push(CarrierVar {
name,
host_id,
join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation
});
}
@ -432,6 +442,7 @@ mod tests {
CarrierVar {
name: name.to_string(),
host_id: ValueId(id),
join_id: None, // Phase 177-STRUCT-1
}
}

View File

@ -38,10 +38,20 @@ pub enum UpdateExpr {
}
/// Right-hand side of update expression
///
/// Phase 178: Extended to detect string updates for multi-carrier loops.
/// The goal is "carrier detection", not full semantic understanding.
#[derive(Debug, Clone)]
pub enum UpdateRhs {
/// Numeric constant: count + 1
Const(i64),
/// Variable reference: sum + i
Variable(String),
/// Phase 178: String literal: result + "x"
StringLiteral(String),
/// Phase 178: Other expression (method call, complex expr)
/// Used to detect "carrier has an update" without understanding semantics
Other,
}
pub struct LoopUpdateAnalyzer;
@ -157,6 +167,9 @@ impl LoopUpdateAnalyzer {
}
/// Analyze right-hand side of update expression
///
/// Phase 178: Extended to detect string updates.
/// Goal: "carrier has update" detection, not semantic understanding.
fn analyze_rhs(node: &ASTNode) -> Option<UpdateRhs> {
match node {
// Constant: count + 1
@ -165,11 +178,24 @@ impl LoopUpdateAnalyzer {
..
} => Some(UpdateRhs::Const(*n)),
// Variable: sum + i
// Phase 178: String literal: result + "x"
ASTNode::Literal {
value: LiteralValue::String(s),
..
} => Some(UpdateRhs::StringLiteral(s.clone())),
// Variable: sum + i (also handles: result + ch)
ASTNode::Variable { name, .. } => {
Some(UpdateRhs::Variable(name.clone()))
}
// Phase 178: Method call or other complex expression
// e.g., result + s.substring(pos, end)
ASTNode::Call { .. }
| ASTNode::MethodCall { .. }
| ASTNode::BinaryOp { .. }
| ASTNode::UnaryOp { .. } => Some(UpdateRhs::Other),
_ => None,
}
}
@ -218,6 +244,7 @@ mod tests {
let carriers = vec![CarrierVar {
name: "count".to_string(),
host_id: crate::mir::ValueId(0),
join_id: None, // Phase 177-STRUCT-1
}];
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);

View File

@ -180,6 +180,16 @@ fn emit_carrier_update(
)
})?
}
// Phase 178: String updates detected but not lowered to JoinIR yet
// The Rust MIR path handles string concatenation
// For JoinIR: just pass through the carrier param (no JoinIR update)
UpdateRhs::StringLiteral(_) | UpdateRhs::Other => {
eprintln!(
"[joinir/pattern2] Phase 178: Carrier '{}' has string/complex update - skipping JoinIR emit, using param passthrough",
carrier.name
);
return Ok(carrier_param); // Pass-through: no JoinIR update
}
};
// Allocate result ValueId
@ -737,6 +747,7 @@ mod tests {
CarrierVar {
name: name.to_string(),
host_id: ValueId(host_id),
join_id: None, // Phase 177-STRUCT-1
}
}

View File

@ -387,6 +387,24 @@ pub fn lower_loop_with_continue_minimal(
const_1
}
}
// Phase 178: String updates detected but not lowered to JoinIR yet
// Skip JoinIR update - use Select passthrough to keep carrier_merged defined
UpdateRhs::StringLiteral(_) | UpdateRhs::Other => {
eprintln!(
"[loop_with_continue_minimal] Phase 178: Carrier '{}' has string/complex update - skipping BinOp, using Select passthrough",
carrier_name
);
// Emit Select with same values: carrier_merged = Select(_, carrier_param, carrier_param)
// This is effectively a passthrough (no JoinIR update)
loop_step_func.body.push(JoinInst::Select {
dst: carrier_merged,
cond: continue_cond, // Condition doesn't matter when both values are same
then_val: carrier_param,
else_val: carrier_param,
type_hint: None,
});
continue; // Skip the BinOp and normal Select below
}
}
}
UpdateExpr::Const(n) => {