Phase 182 discovered **Blocker 1**: LoopBodyLocal variables are currently always routed to Trim-specific carrier promotion logic, which is inappropriate for JsonParser integer loops where these variables are simple local computations.
This phase separates LoopBodyLocal variables into two categories based on their usage pattern:
1.**Condition LoopBodyLocal**: Used in loop conditions (header/break/continue) → Needs Trim promotion
2.**Body-only LoopBodyLocal**: Only used in loop body, never in conditions → No promotion needed
## Problem Statement
### Current Behavior (Phase 182 Blockers)
```nyash
// Example: _parse_number
loop(p <s.length()){
local digit_pos = "0123456789".indexOf(ch) // LoopBodyLocal: digit_pos
if (digit_pos <0){
break // digit_pos used in BREAK condition
}
num_str = num_str + ch
p = p + 1
}
```
**Current routing**:
-`digit_pos` is detected as LoopBodyLocal (defined in body)
- Pattern2 tries to apply Trim carrier promotion
- **Error**: Not a Trim pattern (`indexOf` vs `substring`)
**Desired behavior**:
-`digit_pos` used in condition → Should attempt Trim promotion (and fail gracefully)
- But if `digit_pos` were only in body → Should be allowed as pure local variable
### Use Cases
#### Case A: Condition LoopBodyLocal (Trim Pattern)
```nyash
// _trim_leading_whitespace
loop(pos <s.length()){
local ch = s.substring(pos, pos + 1) // LoopBodyLocal: ch
if (ch == " " || ch == "\t") { // ch in BREAK condition
pos = pos + 1
} else {
break
}
}
```
**Routing**: Needs Trim promotion (`ch` → `is_whitespace` carrier)
#### Case B: Body-only LoopBodyLocal (Pure Local)
```nyash
// Hypothetical simple loop
loop(i <n){
local temp = i * 2 // LoopBodyLocal: temp (not in any condition!)
result = result + temp
i = i + 1
}
```
**Routing**: No promotion needed (`temp` never used in conditions)
#### Case C: Condition LoopBodyLocal (Non-Trim)
```nyash
// _parse_number
loop(p <s.length()){
local digit_pos = "0123456789".indexOf(ch) // LoopBodyLocal: digit_pos
if (digit_pos <0){//digit_posinBREAKcondition
break
}
p = p + 1
}
```
**Current**: Tries Trim promotion → Fails
**Desired**: Recognize non-Trim pattern → **Block with clear error message**
## Design Solution
### Architecture: Two-Stage Check
```
LoopConditionScopeBox
↓
has_loop_body_local() ?
↓ YES
Check: Where is LoopBodyLocal used?
├─ In CONDITION (header/break/continue) → Try Trim promotion
│ ├─ Success → Pattern2/4 with Trim carrier
│ └─ Fail → Reject loop (not supported yet)
└─ Body-only (NOT in any condition) → Allow as pure local
```
### Implementation Strategy
#### Step 1: Extend LoopConditionScope Analysis
Add `is_in_condition()` check to differentiate:
- Condition LoopBodyLocal: Used in header/break/continue conditions
- Body-only LoopBodyLocal: Only in body assignments/expressions
```rust
impl LoopConditionScope {
/// Check if a LoopBodyLocal is used in any condition