diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index e9377d1b..92f23123 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -13,9 +13,10 @@ - LoopBuilder は完全削除済みで、ループは JoinIR Pattern1–4(while / break / if‑PHI / continue)+ P5(Trim系) で統一。 - JoinValueSpace / LoopHeaderPhi / ExitLine / JoinInlineBoundary / JoinIRVerifier まで含めた 「Loop → JoinIR → MIR → return」のパイプラインは、代表パターンと JsonParser ミニケースで安定している。 -- **Phase 213–216 完了**: P3(if‑PHI) は if‑sum 最小パターン(`phase212_if_sum_min.hako`)まで AST ベースで一般化済みで、 +- **Phase 213–217 完了**: P3(if‑PHI) は if‑sum 最小パターン(`phase212_if_sum_min.hako`)まで AST ベースで一般化済みで、 Phase 215 で ExprResult の出口契約を Pattern2 と同じ形に揃えて RC=2 まで通すようになった。 Phase 216 で selfhost 側の production test も検証完了。 + **Phase 217 でマルチキャリア if‑sum(sum+count)も追加実装0行で動作確認** - Phase 195/214/215 の箱組合せで完璧動作。 ### 2. JsonParser / Trim / selfhost への適用状況 diff --git a/apps/tests/phase217_if_sum_multi_min.hako b/apps/tests/phase217_if_sum_multi_min.hako new file mode 100644 index 00000000..2a689221 --- /dev/null +++ b/apps/tests/phase217_if_sum_multi_min.hako @@ -0,0 +1,52 @@ +// Phase 217: Multi-carrier if-sum pattern test +// Tests: Loop with 2 accumulators (sum + count) updated conditionally +// Target: selfhost multi-carrier if-sum pattern (e.g., sum definitions + count items) +// +// Expected behavior: +// Array has 3 items: [null, "a", "b"] +// null items are skipped (i=0) +// Valid items are counted (i=1, i=2) +// sum += 1, count += 1 when item != null +// +// Expected result: sum=2, count=2 + +static box IfSumMultiTest { + sum_and_count(defs) { + local sum + sum = 0 + local count + count = 0 + local i + i = 0 + local len + len = 3 + + loop(i < len) { + // Simulate array: [null, "a", "b"] + // i=0 → null (skip both updates) + // i=1 → "a" (increment both) + // i=2 → "b" (increment both) + + if i > 0 { + // Conditional updates: both sum and count + sum = sum + 1 + count = count + 1 + print(sum) // Force if to stay in MIR + print(count) + } else { + print(0) // Ensure else branch exists + } + + i = i + 1 + } + + return sum // Return sum (count is also computed but not returned) + } + + main() { + // Test: Array [null, "a", "b"] → sum=2, count=2 + local result + result = IfSumMultiTest.sum_and_count(0) // dummy arg + return result + } +} diff --git a/docs/development/current/main/phase217-if-sum-multi.md b/docs/development/current/main/phase217-if-sum-multi.md new file mode 100644 index 00000000..66776f69 --- /dev/null +++ b/docs/development/current/main/phase217-if-sum-multi.md @@ -0,0 +1,272 @@ +# Phase 217: Multi-Carrier If-Sum Complete + +## Overview + +Phase 217 validates that Pattern 3 if-sum implementation supports **multiple accumulators** (sum + count) with **zero additional code**. + +**Status**: ✅ **COMPLETE** - Multi-carrier if-sum works out-of-the-box + +## The Surprise: Zero Implementation Needed + +### Expected Work (Task 217-3) +- Modify `loop_with_if_phi_if_sum.rs` for multi-carrier +- Update AST extraction to handle 2+ updates +- Wire multiple carriers through JoinIR generation + +### Actual Work +**ZERO LINES OF CODE CHANGED** 🎉 + +### Why It Just Worked + +Phase 217 is the culmination of three previous phases working in perfect harmony: + +1. **Phase 195 (Multi-Carrier PHI Foundation)** + - `CarrierInfo` tracks arbitrary number of carriers + - Exit PHI generation handles N carriers + - ExitLineReconnector updates variable_map for all carriers + +2. **Phase 214 (Dynamic Join Inputs)** + - Removed hardcoded 3-input assumption + - `join_inputs = (0..total_inputs).map(|i| ValueId(i))` + - Automatically scales: 1 loop_var + N carriers = N+1 inputs + +3. **Phase 215 (ExprResult Exit Contract)** + - Marks first carrier as expr_result + - Propagates through Boundary → ExitLine → Return + - Works regardless of carrier count + +**Result**: The "boxes" compose perfectly! + +## Test Case + +### Multi-Carrier If-Sum Pattern + +**File**: `apps/tests/phase217_if_sum_multi_min.hako` + +```hako +static box IfSumMultiTest { + sum_and_count(defs) { + local sum = 0 + local count = 0 + local i = 0 + local len = 3 + + loop(i < len) { + if i > 0 { + sum = sum + 1 // Carrier 1 (conditional) + count = count + 1 // Carrier 2 (conditional) + print(sum) + print(count) + } else { + print(0) + } + i = i + 1 + } + + return sum // Returns first carrier + } + + main() { + local result = IfSumMultiTest.sum_and_count(0) + return result + } +} +``` + +**Pattern**: +- Counter: `i` (0 to 2) +- Accumulator 1: `sum` (incremented at i=1, i=2) +- Accumulator 2: `count` (incremented at i=1, i=2) + +**Expected**: RC=2 (sum value) +**Actual**: **RC=2** ✅ + +## Test Results + +### Primary Target + +| Test File | Carriers | Loop Var | Expected | Actual | Status | +|-----------|----------|----------|----------|--------|--------| +| phase217_if_sum_multi_min.hako | sum+count (2) | i | RC=2 | RC=2 | ✅ PASS | + +**Verification**: +```bash +./target/release/nyash apps/tests/phase217_if_sum_multi_min.hako +# Output: (prints 1, 1, 2, 2) +# RC=2 +``` + +### Regression Tests (All Passing) + +| Test File | Pattern | Carriers | Expected | Actual | Status | +|-----------|---------|----------|----------|--------|--------| +| loop_if_phi.hako | P3 | sum (1) | RC=2 | RC=2 | ✅ PASS | +| phase212_if_sum_min.hako | P3 | sum (1) | RC=2 | RC=2 | ✅ PASS | +| loop_min_while.hako | P1 | i (1) | RC=2 | RC=2 | ✅ PASS | + +## Architecture Verification + +### Carrier Detection (Automatic) + +Phase 195's `CarrierInfo` automatically detected: +- Carrier 1: `sum` (UpdateKind::AccumulationLike) +- Carrier 2: `count` (UpdateKind::AccumulationLike) +- Loop var: `i` (UpdateKind::CounterLike) + +### Dynamic Input Generation (Phase 214) + +```rust +// Automatic scaling +let total_inputs = 1 + exit_bindings.len(); // 1 + 2 = 3 +let join_inputs: Vec = (0..total_inputs) + .map(|i| ValueId(i as u32)) + .collect(); // [ValueId(0), ValueId(1), ValueId(2)] + +let host_inputs = vec![ + ctx.loop_var_id, // i + exit_binding_0, // sum + exit_binding_1, // count +]; +``` + +### Exit PHI Generation (Phase 195) + +``` +Loop Header (bb4): + %i_phi = phi [0, bb3], [%i_next, bb14] // Loop variable + %sum_phi = phi [0, bb3], [%sum_exit, bb14] // Carrier 1 + %count_phi = phi [0, bb3], [%count_exit, bb14] // Carrier 2 + +Loop Body (bb5-bb13): + (if-else branches update sum/count conditionally) + +Loop Exit (bb14): + %sum_exit = phi [%sum_then, bb_then], [%sum_phi, bb_else] + %count_exit = phi [%count_then, bb_then], [%count_phi, bb_else] + %i_next = add %i_phi, 1 + branch bb4 // Loop back with all 3 values +``` + +### ExprResult Selection (Phase 215) + +```rust +// Phase 215: First carrier becomes expr_result +let fragment_meta = JoinFragmentMeta::with_expr_result( + sum_exit, // First carrier (sum) + exit_meta // Contains both sum and count +); + +// Result: sum reaches return statement (RC=2) +// count updates variable_map only +``` + +## Box Theory Validation + +Phase 217 proves the **"Everything is Box"** philosophy: + +### Box Composition +``` +┌─────────────────────────────────────────────┐ +│ Phase 195: Multi-Carrier PHI (Box A) │ +│ - Handles N carriers automatically │ +│ - CarrierInfo + ExitMeta │ +└─────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────┐ +│ Phase 214: Dynamic Inputs (Box B) │ +│ - Scales to N+1 inputs automatically │ +│ - join_inputs generation │ +└─────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────┐ +│ Phase 215: ExprResult Contract (Box C) │ +│ - Selects first carrier for return │ +│ - Works regardless of carrier count │ +└─────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────┐ +│ Phase 217: Multi-Carrier If-Sum │ +│ - Boxes A + B + C compose perfectly │ +│ - ZERO additional code needed │ +└─────────────────────────────────────────────┘ +``` + +### Key Insight + +**Well-designed boxes compose without modification.** + +Each phase created a **reusable box** with clear contracts: +- Box A: "I handle N carriers" +- Box B: "I generate N+1 inputs" +- Box C: "I select expr_result from carriers" + +When combined, these boxes **just work** for multi-carrier patterns. + +## Lessons Learned + +### 1. Fail-Fast Design Pays Off + +Phase 195's strict assertions caught problems early: +```rust +debug_assert_eq!( + join_inputs.len(), + host_inputs.len(), + "join_inputs != host_inputs" +); +``` + +This forced Phase 214 to fix the root cause (hardcoded inputs), which then made Phase 217 trivial. + +### 2. Single Responsibility Principle + +Each phase had one clear job: +- Phase 195: Multi-carrier **detection and PHI generation** +- Phase 214: Multi-carrier **input scaling** +- Phase 215: Multi-carrier **expr_result selection** + +No phase tried to solve everything at once. + +### 3. Box-First Development + +By treating each capability as a "box" (module with clear interface), we: +- Avoided tight coupling +- Enabled composition +- Made testing independent +- Reduced implementation risk + +## Future Work + +### Phase 218: Nested If-Else Patterns +- Target: `esc_json()` with nested conditions +- Validates complex if-else inside loops +- Likely needs: ConditionEnv or BoolExprLowerer enhancements + +### Phase 219: JsonParser Integration +- Apply multi-carrier if-sum to actual JsonParser loops +- First target: numeric processing with flags +- Validates selfhost compiler real use case + +### Phase 220: Variable Limit Conditions +- Support: `loop(i < len)` where `len` is variable (not literal) +- Currently only integer literals supported in if-sum lowerer +- Needs: AST extraction enhancement for variable references + +## Files Modified + +**ZERO source code files modified** in Phase 217. + +**Files Added**: +- `apps/tests/phase217_if_sum_multi_min.hako` (test case) +- `docs/development/current/main/phase217-if-sum-multi.md` (this doc) + +## Summary + +Phase 217 is a **validation phase** that proves the correctness of the Phase 195/214/215 architecture: +- ✅ Multi-carrier if-sum works with **zero additional code** +- ✅ All regression tests passing +- ✅ Box composition working perfectly +- ✅ Ready for more complex patterns (Phase 218+) + +The fact that Phase 217 required **no implementation** is not a bug—it's a **feature** of good box-first design. When boxes compose correctly, new capabilities emerge naturally. + +**「箱理論」の勝利!** 🎉