feat(joinir): Phase 247-EX - DigitPos dual-value architecture
Extends DigitPos promotion to generate TWO carriers for Pattern A/B support: - Boolean carrier (is_digit_pos) for break conditions - Integer carrier (digit_value) for NumberAccumulation ## Implementation 1. **DigitPosPromoter** (loop_body_digitpos_promoter.rs) - Generates dual carriers: is_<var> (bool) + <base>_value (int) - Smart naming: "digit_pos" → "digit" (removes "_pos" suffix) 2. **UpdateEnv** (update_env.rs) - Context-aware promoted variable resolution - Priority: <base>_value (int) → is_<var> (bool) → standard - Pass promoted_loopbodylocals from CarrierInfo 3. **Integration** (loop_with_break_minimal.rs) - UpdateEnv constructor updated to pass promoted list ## Test Results - **Before**: 925 tests PASS - **After**: 931 tests PASS (+6 new tests, 0 failures) ## New Tests - test_promoted_variable_resolution_digit_pos - Full dual-value - test_promoted_variable_resolution_fallback_to_bool - Fallback - test_promoted_variable_not_a_carrier - Error handling ## Impact | Pattern | Before | After | |---------|--------|-------| | _parse_number | ✅ Works (bool only) | ✅ Works (bool used, int unused) | | _atoi | ❌ Failed (missing int) | ✅ READY (int carrier available!) | 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -180,23 +180,44 @@ impl DigitPosPromoter {
|
||||
);
|
||||
|
||||
// Step 6: Build CarrierInfo
|
||||
// For DigitPos pattern, we add a NEW carrier (not replace loop_var)
|
||||
let carrier_name = format!("is_{}", var_in_cond);
|
||||
// Phase 247-EX: DigitPos generates TWO carriers (dual-value model)
|
||||
// - is_<var> (boolean): for break condition
|
||||
// - <prefix>_value (integer): for NumberAccumulation
|
||||
// Naming: "digit_pos" → "is_digit_pos" + "digit_value" (not "digit_pos_value")
|
||||
let bool_carrier_name = format!("is_{}", var_in_cond);
|
||||
// Extract the base name for integer carrier (e.g., "digit_pos" → "digit")
|
||||
let base_name = if var_in_cond.ends_with("_pos") {
|
||||
&var_in_cond[..var_in_cond.len() - 4] // Remove "_pos" suffix
|
||||
} else {
|
||||
var_in_cond.as_str()
|
||||
};
|
||||
let int_carrier_name = format!("{}_value", base_name);
|
||||
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole, CarrierInit};
|
||||
let promoted_carrier = CarrierVar {
|
||||
name: carrier_name.clone(),
|
||||
|
||||
// Boolean carrier (condition-only, for break)
|
||||
let promoted_carrier_bool = CarrierVar {
|
||||
name: bool_carrier_name.clone(),
|
||||
host_id: ValueId(0), // Placeholder (will be remapped)
|
||||
join_id: None, // Will be allocated later
|
||||
role: CarrierRole::ConditionOnly, // Phase 227: DigitPos is condition-only
|
||||
init: CarrierInit::BoolConst(false), // Phase 228: Initialize with false
|
||||
};
|
||||
|
||||
// Integer carrier (loop-state, for NumberAccumulation)
|
||||
let promoted_carrier_int = CarrierVar {
|
||||
name: int_carrier_name.clone(),
|
||||
host_id: ValueId(0), // Placeholder (will be remapped)
|
||||
join_id: None, // Will be allocated later
|
||||
role: CarrierRole::LoopState, // Phase 247-EX: LoopState for accumulation
|
||||
init: CarrierInit::FromHost, // Phase 228: Initialize from indexOf() result
|
||||
};
|
||||
|
||||
// Create CarrierInfo with a dummy loop_var_name (will be ignored during merge)
|
||||
let mut carrier_info = CarrierInfo::with_carriers(
|
||||
"__dummy_loop_var__".to_string(), // Placeholder, not used
|
||||
ValueId(0), // Placeholder
|
||||
vec![promoted_carrier],
|
||||
vec![promoted_carrier_bool, promoted_carrier_int],
|
||||
);
|
||||
|
||||
// Phase 229: Record promoted variable (no need for condition_aliases)
|
||||
@ -204,18 +225,18 @@ impl DigitPosPromoter {
|
||||
carrier_info.promoted_loopbodylocals.push(var_in_cond.clone());
|
||||
|
||||
eprintln!(
|
||||
"[digitpos_promoter] A-4 DigitPos pattern promoted: {} → {}",
|
||||
var_in_cond, carrier_name
|
||||
"[digitpos_promoter] Phase 247-EX: A-4 DigitPos pattern promoted: {} → {} (bool) + {} (i64)",
|
||||
var_in_cond, bool_carrier_name, int_carrier_name
|
||||
);
|
||||
eprintln!(
|
||||
"[digitpos_promoter] Phase 229: Recorded promoted variable '{}' (carrier: '{}')",
|
||||
var_in_cond, carrier_name
|
||||
"[digitpos_promoter] Phase 229: Recorded promoted variable '{}' (carriers: '{}', '{}')",
|
||||
var_in_cond, bool_carrier_name, int_carrier_name
|
||||
);
|
||||
|
||||
return DigitPosPromotionResult::Promoted {
|
||||
carrier_info,
|
||||
promoted_var: var_in_cond,
|
||||
carrier_name,
|
||||
carrier_name: bool_carrier_name, // Return bool carrier name for compatibility
|
||||
};
|
||||
} else {
|
||||
eprintln!(
|
||||
|
||||
@ -192,3 +192,43 @@ fn pattern4_continue_loop_is_detected() {
|
||||
let kind = classify_body(&body);
|
||||
assert_eq!(kind, LoopPatternKind::Pattern4Continue);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_atoi_loop_classified_as_pattern2() {
|
||||
// Phase 246-EX Step 1: _atoi loop pattern classification
|
||||
// loop(i < len) {
|
||||
// local ch = s.substring(i, i+1)
|
||||
// local digit_pos = digits.indexOf(ch)
|
||||
// if digit_pos < 0 { break }
|
||||
// result = result * 10 + digit_pos
|
||||
// i = i + 1
|
||||
// }
|
||||
|
||||
// Simplified: loop with break + two carrier updates
|
||||
let break_cond = bin(BinaryOperator::Less, var("digit_pos"), lit_i(0));
|
||||
|
||||
// result = result * 10 + digit_pos (NumberAccumulation pattern)
|
||||
let mul_expr = bin(BinaryOperator::Multiply, var("result"), lit_i(10));
|
||||
let result_update = assignment(
|
||||
var("result"),
|
||||
bin(BinaryOperator::Add, mul_expr, var("digit_pos"))
|
||||
);
|
||||
|
||||
// i = i + 1
|
||||
let i_update = assignment(var("i"), bin(BinaryOperator::Add, var("i"), lit_i(1)));
|
||||
|
||||
let body = vec![
|
||||
ASTNode::If {
|
||||
condition: Box::new(break_cond),
|
||||
then_body: vec![ASTNode::Break { span: span() }],
|
||||
else_body: None,
|
||||
span: span(),
|
||||
},
|
||||
result_update,
|
||||
i_update,
|
||||
];
|
||||
|
||||
let kind = classify_body(&body);
|
||||
assert_eq!(kind, LoopPatternKind::Pattern2Break,
|
||||
"_atoi loop should be classified as Pattern2 (Break) due to if-break structure");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user