feat(joinir): Phase 220-B ConditionEnv integration for if-sum lowerer

Integrates ConditionEnv infrastructure into if-sum lowerer to support
variable conditions like `loop(i < len)`.

## Implementation (Following Pattern 2)

### ConditionEnv Construction
- Added early construction via ConditionEnvBuilder::build_for_break_condition_v2()
- Extracts condition variables from loop condition AST
- Creates ConditionBindings for HOST↔JoinIR ValueId mapping

### Variable Support
- Created ValueOrLiteral enum (Literal(i64) | Variable(String, ValueId))
- Added extract_value_or_variable() with ConditionEnv lookup
- Updated extract_loop_condition() and extract_if_condition()

### JoinIR Generation Updates
- Condition-only variables as loop_step function parameters
- Proper parameter passing in recursive calls
- Condition ValueIds in JoinIR param region (100+)

### Boundary Builder Wiring
- Pass condition_bindings to JoinInlineBoundaryBuilder
- Updated call site in pattern3_with_if_phi.rs
- Added variable_map, loop_var_name, loop_var_id parameters

## Build Status

 Compilation successful (0 errors, 3 warnings)

## Test Status

 Tests not yet passing - remap issue identified:
- HOST: len = ValueId(6)
- JoinIR: len = ValueId(101)
- After Merge: len = ValueId(10) 

Root cause: JoinIRConversionPipeline remap needs investigation

## Files Modified

- src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs (+80 lines)
  - ConditionEnv construction
  - Variable support in conditions
  - Updated function signatures

- src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs (+10 lines)
  - Pass builder/loop_var to lowerer
  - Wire condition_bindings

## Design Principles

1. Reuse Existing Boxes (ConditionEnv/ConditionBinding)
2. Follow Pattern 2 Structure (proven blueprint)
3. Fail-Fast (variable not in ConditionEnv → error)
4. ParamRole::Condition Routing (separate from carriers)

## Next Steps: Phase 220-C

Fix remap issue in JoinIRConversionPipeline to properly use
condition_bindings.host_value during merge.

🤖 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-10 02:54:56 +09:00
parent e6e306c020
commit 2fc2cf74d1
3 changed files with 366 additions and 47 deletions

View File

@ -0,0 +1,135 @@
# Phase 220-B: ConditionEnv Integration for If-Sum
## Overview
Integrated ConditionEnv infrastructure into if-sum lowerer to support
variable conditions like `loop(i < len)`.
## Implementation Status
### Completed
- ✅ ConditionEnv construction in `lower_if_sum_pattern()`
-`extract_value_or_variable()` function (supports both literals and variables)
- ✅ Updated `extract_loop_condition()` and `extract_if_condition()` to use ConditionEnv
- ✅ JoinIR generation includes condition-only variables as parameters
- ✅ Condition bindings wired to boundary builder
- ✅ Updated call sites in `pattern3_with_if_phi.rs`
### In Progress
- 🔧 **Issue**: phase212_if_sum_min.hako returns RC=0 instead of RC=2
- 🔧 **Root Cause**: Condition variable (`len`) remapping issue during merge
- HOST ValueId(6) → JoinIR ValueId(101) → After merge ValueId(10) ❌
- Expected: HOST ValueId(6) → JoinIR ValueId(101) → After merge ValueId(6) ✅
- 🔧 **Next Step**: Investigate condition_bindings handling in merge pipeline
## Implementation
### Pattern 2 Reference
Followed proven Pattern 2 (break condition) integration pattern:
1. Build ConditionEnv early with extract_condition_variables
2. Pass to condition lowering functions
3. Lookup variables via cond_env.get()
4. Wire condition_bindings to JoinInlineBoundary
### Key Changes
**File**: `src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs`
- Added ConditionEnv construction (lines 70-127)
- Updated signature to accept `variable_map`, `loop_var_name`, `loop_var_id`
- Return type changed to include `Vec<ConditionBinding>`
- JoinIR generation updated to include condition variables as parameters
**File**: `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
- Updated call site to pass variable_map and loop context
- Wired condition_bindings to boundary builder (line 189)
### ValueOrLiteral Enum
```rust
enum ValueOrLiteral {
Literal(i64),
Variable(String, ValueId),
}
```
### Function Signatures Updated
```rust
fn extract_value_or_variable(
node: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<ValueOrLiteral, String>
fn extract_loop_condition(
cond: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<(String, CompareOp, ValueOrLiteral), String>
```
## Test Results (Preliminary)
### phase212_if_sum_min.hako
- Expected: RC=2
- Actual: RC=0 ❌
- Issue: Loop not executing (no print output)
- Debug: Condition variable `len` extracted correctly but remapping issue
### Debug Trace
```
[joinir/pattern3/if-sum] Extracted 1 condition variables: ["len"]
[joinir/pattern3/if-sum] Condition variable 'len': host=ValueId(6), join=ValueId(101)
[if-sum/joinir] loop_step params: 4 total (3 carriers + 1 condition vars)
[DEBUG-177] 'len': JoinIR ValueId(101) → Some(ValueId(10)) ← Should be ValueId(6)
```
## Design Principles
1. **Reuse Existing Boxes** (ConditionEnv/ConditionBinding)
2. **Follow Pattern 2 Structure** (proven blueprint)
3. **Fail-Fast** (variable not in ConditionEnv → error)
4. **ParamRole::Condition Routing** (separate from carriers)
## Next Steps
1. **Investigate Merge Pipeline**:
- Check how condition_bindings are processed in JoinIRConversionPipeline
- Verify that condition variables are loaded at loop entry
- Ensure remapping uses host_value from ConditionBinding
2. **Add Load Instructions**:
- May need to generate Load instructions for condition variables at loop entry
- Similar to how Pattern 2 handles break condition variables
3. **Test Additional Cases**:
- phase218_json_if_sum_min.hako (expect RC=10)
- Regression: loop_if_phi.hako (expect RC=2)
- Regression: phase217_if_sum_multi_min.hako (expect RC=2)
## Files Modified
**Primary**:
- `src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs` (ConditionEnv integration)
- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs` (call site update)
**Documentation**:
- `docs/development/current/main/phase220-condition-env-integration.md` (this file)
## Important Notes
- **Fail-Fast Maintained**: Variables not in ConditionEnv cause explicit errors
- **Method Call Limitation**: `defs.len()` style not supported yet (Phase 221+)
- **Pattern 2 Parity**: Uses identical ConditionEnvBuilder API
- **ParamRole Separation**: Condition variables kept separate from carriers
## Known Issues
1. **Condition Variable Remapping** (Critical):
- Condition variables get remapped to wrong ValueIds during merge
- Need to verify condition_bindings handling in merge pipeline
2. **No Test Passing Yet**:
- phase212: RC=0 (expected 2) ❌
- Other tests not yet verified
## References
- [Phase 171-fix: ConditionEnv](../../../reference/joinir/condition-env.md)
- [Phase 200-A/B: ConditionBinding](../../../reference/joinir/condition-binding.md)
- [Phase 201: JoinValueSpace](../../../reference/joinir/join-value-space.md)
- [Pattern 2: Break Condition](./pattern2-break-condition.md)

View File

@ -112,12 +112,15 @@ impl MirBuilder {
"[cf_loop/pattern3] if-sum pattern detected but no if statement found".to_string() "[cf_loop/pattern3] if-sum pattern detected but no if statement found".to_string()
})?; })?;
// Call AST-based if-sum lowerer // Phase 220-B: Call AST-based if-sum lowerer with ConditionEnv support
let (join_module, fragment_meta) = lower_if_sum_pattern( let (join_module, fragment_meta, cond_bindings) = lower_if_sum_pattern(
condition, condition,
if_stmt, if_stmt,
body, body,
&mut join_value_space, &mut join_value_space,
&self.variable_map, // Phase 220-B: Pass variable_map for ConditionEnv
&ctx.loop_var_name, // Phase 220-B: Pass loop variable name
ctx.loop_var_id, // Phase 220-B: Pass loop variable ValueId
)?; )?;
let exit_meta = &fragment_meta.exit_meta; let exit_meta = &fragment_meta.exit_meta;
@ -173,11 +176,17 @@ impl MirBuilder {
) )
); );
// Phase 215-2: Pass expr_result to boundary // Phase 220-B: Wire condition_bindings to boundary builder
trace::trace().debug(
"pattern3/if-sum",
&format!("Wiring {} condition bindings to boundary", cond_bindings.len())
);
let mut boundary_builder = JoinInlineBoundaryBuilder::new() let mut boundary_builder = JoinInlineBoundaryBuilder::new()
.with_inputs(join_inputs, host_inputs) .with_inputs(join_inputs, host_inputs)
.with_exit_bindings(exit_bindings) .with_exit_bindings(exit_bindings)
.with_loop_var_name(Some(ctx.loop_var_name.clone())); .with_loop_var_name(Some(ctx.loop_var_name.clone()))
.with_condition_bindings(cond_bindings); // Phase 220-B: Add condition bindings
// Add expr_result if present // Add expr_result if present
if let Some(expr_id) = fragment_meta.expr_result { if let Some(expr_id) = fragment_meta.expr_result {

View File

@ -30,13 +30,17 @@
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta}; use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta};
use crate::mir::join_ir::lowering::condition_env::{ConditionBinding, ConditionEnv};
use crate::mir::join_ir::lowering::condition_to_joinir::extract_condition_variables;
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::{ use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
MirLikeInst, UnaryOp, MirLikeInst, UnaryOp,
}; };
use crate::mir::ValueId;
use std::collections::BTreeMap;
/// Phase 213: Lower if-sum pattern to JoinIR using AST /// Phase 220-B: Lower if-sum pattern to JoinIR using AST (with ConditionEnv support)
/// ///
/// # Arguments /// # Arguments
/// ///
@ -44,26 +48,95 @@ use crate::mir::join_ir::{
/// * `if_stmt` - If statement AST from loop body /// * `if_stmt` - If statement AST from loop body
/// * `body` - Full loop body AST (for finding counter update) /// * `body` - Full loop body AST (for finding counter update)
/// * `join_value_space` - Unified ValueId allocator /// * `join_value_space` - Unified ValueId allocator
/// * `variable_map` - HOST function's variable_map for ConditionEnv construction
/// * `loop_var_name` - Loop variable name (e.g., "i")
/// * `loop_var_id` - HOST ValueId for loop variable
/// ///
/// # Returns /// # Returns
/// ///
/// * `Ok((JoinModule, JoinFragmentMeta))` - JoinIR module with exit metadata /// * `Ok((JoinModule, JoinFragmentMeta, Vec<ConditionBinding>))` - JoinIR module, exit metadata, and condition bindings
/// * `Err(String)` - Pattern not supported or extraction failed /// * `Err(String)` - Pattern not supported or extraction failed
pub fn lower_if_sum_pattern( pub fn lower_if_sum_pattern(
loop_condition: &ASTNode, loop_condition: &ASTNode,
if_stmt: &ASTNode, if_stmt: &ASTNode,
body: &[ASTNode], body: &[ASTNode],
join_value_space: &mut JoinValueSpace, join_value_space: &mut JoinValueSpace,
) -> Result<(JoinModule, JoinFragmentMeta), String> { variable_map: &BTreeMap<String, ValueId>,
loop_var_name: &str,
loop_var_id: ValueId,
) -> Result<(JoinModule, JoinFragmentMeta, Vec<ConditionBinding>), String> {
eprintln!("[joinir/pattern3/if-sum] Starting AST-based if-sum lowering"); eprintln!("[joinir/pattern3/if-sum] Starting AST-based if-sum lowering");
// Step 1: Extract loop condition info (e.g., i < len → var="i", op=Lt, limit=len) // Phase 220-B Step 1: Build ConditionEnv (following Pattern 2 style)
let (loop_var, loop_op, loop_limit) = extract_loop_condition(loop_condition)?; let cond_vars = extract_condition_variables(
eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} {}", loop_var, loop_op, loop_limit); loop_condition,
&[loop_var_name.to_string()], // Exclude loop variable itself
);
// Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=0) eprintln!(
let (if_var, if_op, if_value) = extract_if_condition(if_stmt)?; "[joinir/pattern3/if-sum] Extracted {} condition variables: {:?}",
eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} {}", if_var, if_op, if_value); cond_vars.len(),
cond_vars
);
// Phase 220-B Step 2: Build ConditionEnv using ConditionEnvBuilder pattern
let mut cond_env = ConditionEnv::new();
let mut cond_bindings = Vec::new();
// Add loop variable to environment
let loop_var_join_id = join_value_space.alloc_param();
cond_env.insert(loop_var_name.to_string(), loop_var_join_id);
eprintln!(
"[joinir/pattern3/if-sum] Loop variable '{}': host={:?}, join={:?}",
loop_var_name, loop_var_id, loop_var_join_id
);
// Add condition-only variables
for var_name in &cond_vars {
let host_id = variable_map
.get(var_name)
.copied()
.ok_or_else(|| {
format!(
"[if-sum] Condition variable '{}' not found in variable_map. \
Available: {:?}",
var_name,
variable_map.keys().collect::<Vec<_>>()
)
})?;
let join_id = join_value_space.alloc_param();
cond_env.insert(var_name.clone(), join_id);
cond_bindings.push(ConditionBinding {
name: var_name.clone(),
host_value: host_id,
join_value: join_id,
});
eprintln!(
"[joinir/pattern3/if-sum] Condition variable '{}': host={:?}, join={:?}",
var_name, host_id, join_id
);
}
eprintln!(
"[joinir/pattern3/if-sum] ConditionEnv built: {} variables, {} bindings",
cond_env.len(),
cond_bindings.len()
);
// Step 3: Extract loop condition info (e.g., i < len → var="i", op=Lt, limit=len/ValueId)
let (loop_var, loop_op, loop_limit) = extract_loop_condition(loop_condition, &cond_env)?;
eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} {:?}", loop_var, loop_op, loop_limit);
// Step 4: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=0)
let (if_var, if_op, if_value) = extract_if_condition(if_stmt, &cond_env)?;
let if_value_desc = match &if_value {
ValueOrLiteral::Literal(n) => format!("literal {}", n),
ValueOrLiteral::Variable(name, id) => format!("variable '{}' ({:?})", name, id),
};
eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} {}", if_var, if_op, if_value_desc);
// Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=1) // Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=1)
let (update_var, update_addend) = extract_then_update(if_stmt)?; let (update_var, update_addend) = extract_then_update(if_stmt)?;
@ -137,10 +210,16 @@ pub fn lower_if_sum_pattern(
value: ConstValue::Integer(0), value: ConstValue::Integer(0),
})); }));
// result = loop_step(i_init, sum_init, count_init) // Phase 220-B: Build call args including condition-only variables
// result = loop_step(i_init, sum_init, count_init, ...condition_vars)
let mut loop_call_args = vec![i_init_val, sum_init_val, count_init_val];
for binding in &cond_bindings {
loop_call_args.push(binding.join_value); // Include condition vars
}
main_func.body.push(JoinInst::Call { main_func.body.push(JoinInst::Call {
func: loop_step_id, func: loop_step_id,
args: vec![i_init_val, sum_init_val, count_init_val], args: loop_call_args,
k_next: None, k_next: None,
dst: Some(loop_result), dst: Some(loop_result),
}); });
@ -151,26 +230,53 @@ pub fn lower_if_sum_pattern(
join_module.add_function(main_func); join_module.add_function(main_func);
// === loop_step(i, sum, count) function === // === loop_step function ===
// Phase 220-B: Include condition-only variables as parameters
let mut loop_step_params = vec![loop_var_join_id, sum_param, count_param];
// Add condition-only variables as parameters
for binding in &cond_bindings {
loop_step_params.push(binding.join_value);
}
eprintln!(
"[if-sum/joinir] loop_step params: {} total ({} carriers + {} condition vars)",
loop_step_params.len(),
3, // i, sum, count
cond_bindings.len()
);
let mut loop_step_func = JoinFunction::new( let mut loop_step_func = JoinFunction::new(
loop_step_id, loop_step_id,
"loop_step".to_string(), "loop_step".to_string(),
vec![i_param, sum_param, count_param], loop_step_params,
); );
// --- Exit Condition Check --- // --- Exit Condition Check ---
// Load loop limit from AST // Phase 220-B: Handle both literal and variable loop limits
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { let loop_limit_value_id = match loop_limit {
dst: loop_limit_val, ValueOrLiteral::Literal(n) => {
value: ConstValue::Integer(loop_limit), // Literal: Generate Const instruction
})); loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: loop_limit_val,
value: ConstValue::Integer(n),
}));
loop_limit_val
}
ValueOrLiteral::Variable(ref var_name, value_id) => {
// Variable: Use ValueId from ConditionEnv (already a parameter)
eprintln!("[if-sum/joinir] Loop limit is variable '{}' = {:?}", var_name, value_id);
value_id
}
};
// Compare: i < limit (or other op from AST) // Compare: i < limit (or other op from AST)
// Phase 220-B: Use loop_var_join_id (from ConditionEnv) instead of i_param
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
dst: cmp_loop, dst: cmp_loop,
op: loop_op, op: loop_op,
lhs: i_param, lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId
rhs: loop_limit_val, rhs: loop_limit_value_id,
})); }));
// exit_cond = !cmp_loop // exit_cond = !cmp_loop
@ -188,18 +294,30 @@ pub fn lower_if_sum_pattern(
}); });
// --- If Condition (AST-based) --- // --- If Condition (AST-based) ---
// Load if constant // Phase 220-B: Handle both literal and variable if conditions
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { let if_value_id = match if_value {
dst: if_const, ValueOrLiteral::Literal(n) => {
value: ConstValue::Integer(if_value), // Literal: Generate Const instruction
})); loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: if_const,
value: ConstValue::Integer(n),
}));
if_const
}
ValueOrLiteral::Variable(ref var_name, value_id) => {
// Variable: Use ValueId from ConditionEnv (already a parameter)
eprintln!("[if-sum/joinir] If condition value is variable '{}' = {:?}", var_name, value_id);
value_id
}
};
// Compare: if_var <op> if_value // Compare: if_var <op> if_value
// Phase 220-B: Use loop_var_join_id (from ConditionEnv)
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
dst: if_cmp, dst: if_cmp,
op: if_op, op: if_op,
lhs: i_param, // Assuming if_var == loop_var (common case) lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId
rhs: if_const, rhs: if_value_id,
})); }));
// --- Then Branch --- // --- Then Branch ---
@ -269,17 +387,24 @@ pub fn lower_if_sum_pattern(
dst: step_const2, dst: step_const2,
value: ConstValue::Integer(counter_step), value: ConstValue::Integer(counter_step),
})); }));
// Phase 220-B: Use loop_var_join_id for counter increment
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: i_next, dst: i_next,
op: BinOpKind::Add, op: BinOpKind::Add,
lhs: i_param, lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId
rhs: step_const2, rhs: step_const2,
})); }));
// --- Tail Recursion --- // --- Tail Recursion ---
// Phase 220-B: Include condition-only variables in recursive call
let mut recursive_args = vec![i_next, sum_new, count_new];
for binding in &cond_bindings {
recursive_args.push(binding.join_value); // Pass condition vars through
}
loop_step_func.body.push(JoinInst::Call { loop_step_func.body.push(JoinInst::Call {
func: loop_step_id, func: loop_step_id,
args: vec![i_next, sum_new, count_new], args: recursive_args,
k_next: None, k_next: None,
dst: None, dst: None,
}); });
@ -312,21 +437,29 @@ pub fn lower_if_sum_pattern(
let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta); let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta);
eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR"); eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR");
eprintln!("[joinir/pattern3/if-sum] Loop: {} {:?} {}", loop_var, loop_op, loop_limit); let loop_limit_desc = match &loop_limit {
eprintln!("[joinir/pattern3/if-sum] If: {} {:?} {}", if_var, if_op, if_value); ValueOrLiteral::Literal(n) => format!("literal {}", n),
ValueOrLiteral::Variable(name, id) => format!("variable '{}' ({:?})", name, id),
};
eprintln!("[joinir/pattern3/if-sum] Loop: {} {:?} {}", loop_var, loop_op, loop_limit_desc);
eprintln!("[joinir/pattern3/if-sum] If: {} {:?} {}", if_var, if_op, if_value_desc);
eprintln!("[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}", sum_final); eprintln!("[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}", sum_final);
eprintln!("[joinir/pattern3/if-sum] Phase 220-B: Returning {} condition bindings", cond_bindings.len());
Ok((join_module, fragment_meta)) Ok((join_module, fragment_meta, cond_bindings))
} }
/// Extract loop condition: variable, operator, and limit /// Phase 220-B: Extract loop condition with variable support
/// ///
/// Supports: `var < lit`, `var <= lit`, `var > lit`, `var >= lit` /// Supports: `var < lit/var`, `var <= lit/var`, `var > lit/var`, `var >= lit/var`
fn extract_loop_condition(cond: &ASTNode) -> Result<(String, CompareOp, i64), String> { fn extract_loop_condition(
cond: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<(String, CompareOp, ValueOrLiteral), String> {
match cond { match cond {
ASTNode::BinaryOp { operator, left, right, .. } => { ASTNode::BinaryOp { operator, left, right, .. } => {
let var_name = extract_variable_name(left)?; let var_name = extract_variable_name(left)?;
let limit = extract_integer_literal(right)?; let limit = extract_value_or_variable(right, cond_env)?;
let op = match operator { let op = match operator {
crate::ast::BinaryOperator::Less => CompareOp::Lt, crate::ast::BinaryOperator::Less => CompareOp::Lt,
crate::ast::BinaryOperator::LessEqual => CompareOp::Le, crate::ast::BinaryOperator::LessEqual => CompareOp::Le,
@ -340,11 +473,14 @@ fn extract_loop_condition(cond: &ASTNode) -> Result<(String, CompareOp, i64), St
} }
} }
/// Extract if condition: variable, operator, and value /// Phase 220-B: Extract if condition with variable support
fn extract_if_condition(if_stmt: &ASTNode) -> Result<(String, CompareOp, i64), String> { fn extract_if_condition(
if_stmt: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<(String, CompareOp, ValueOrLiteral), String> {
match if_stmt { match if_stmt {
ASTNode::If { condition, .. } => { ASTNode::If { condition, .. } => {
extract_loop_condition(condition) // Same format extract_loop_condition(condition, cond_env) // Same format
} }
_ => Err("[if-sum] Expected If statement".to_string()), _ => Err("[if-sum] Expected If statement".to_string()),
} }
@ -406,15 +542,54 @@ fn extract_variable_name(node: &ASTNode) -> Result<String, String> {
} }
} }
/// Extract integer literal from AST node /// Phase 220-B: Value or literal enum for condition expressions
#[derive(Debug)]
enum ValueOrLiteral {
Literal(i64),
Variable(String, ValueId),
}
/// Helper: Extract integer literal (for non-condition contexts like arithmetic)
fn extract_integer_literal(node: &ASTNode) -> Result<i64, String> { fn extract_integer_literal(node: &ASTNode) -> Result<i64, String> {
match node { match node {
ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => Ok(*n), ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => Ok(*n),
ASTNode::Variable { name, .. } => { ASTNode::Variable { name, .. } => {
// Handle variable reference (e.g., `len`) Err(format!("[if-sum] Variable '{}' in arithmetic expression not supported yet", name))
// For Phase 213, we only support literals. Variables need Phase 214+
Err(format!("[if-sum] Variable '{}' in condition not supported yet (Phase 214+)", name))
} }
_ => Err(format!("[if-sum] Expected integer literal, got {:?}", node)), _ => Err(format!("[if-sum] Expected integer literal, got {:?}", node)),
} }
} }
/// Phase 220-B: Extract value or variable from AST node (with ConditionEnv support)
fn extract_value_or_variable(
node: &ASTNode,
cond_env: &ConditionEnv,
) -> Result<ValueOrLiteral, String> {
match node {
// Literal: Return as immediate value
ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => {
Ok(ValueOrLiteral::Literal(*n))
}
// Variable: Lookup in ConditionEnv
ASTNode::Variable { name, .. } => {
if let Some(value_id) = cond_env.get(name) {
eprintln!("[if-sum/extract] Variable '{}' resolved to {:?}", name, value_id);
Ok(ValueOrLiteral::Variable(name.clone(), value_id))
} else {
// Fail-Fast: Variable not in ConditionEnv
Err(format!(
"[if-sum] Variable '{}' not found in ConditionEnv (available: {:?})",
name, cond_env.names()
))
}
}
// Method call: Not supported yet (defer to Phase 221+)
ASTNode::MethodCall { .. } => {
Err("[if-sum] Method calls in conditions not supported yet (Phase 221+)".to_string())
}
_ => Err(format!("[if-sum] Expected integer literal or variable, got {:?}", node)),
}
}