Implements the Trim pattern detection logic for carrier promotion:
- find_definition_in_body(): Iterative AST traversal to locate variable definitions
- is_substring_method_call(): Detects substring() method calls
- extract_equality_literals(): Extracts string literals from OR chains (ch == " " || ch == "\t")
- TrimPatternInfo: Captures detected pattern details for carrier promotion
This enables Pattern 5 to detect trim-style loops:
```hako
loop(start < end) {
local ch = s.substring(start, start+1)
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
start = start + 1
} else {
break
}
}
```
Unit tests cover:
- Simple and nested definition detection
- substring method call detection
- Single and chained equality literal extraction
- Full Trim pattern detection with 2-4 whitespace characters
Next: Phase 171-C-3 integration with Pattern 2/4 routing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
631 lines
19 KiB
Markdown
631 lines
19 KiB
Markdown
# Phase 166-impl-2: JsonParser/Trim Loop Re-inventory
|
|
|
|
**Status**: ✅ Complete
|
|
**Last Updated**: 2025-12-07
|
|
**Phase**: 166-impl-2 (Post-LoopConditionScopeBox validation)
|
|
|
|
## Overview
|
|
|
|
This document analyzes all loops in JsonParserBox and TrimTest to determine what the current JoinIR implementation (Pattern 1-4 + LoopConditionScopeBox) can handle.
|
|
|
|
**Key Finding**: The current implementation successfully compiles all JsonParserBox loops but fails on TrimTest due to LoopBodyLocal variable usage in loop conditions.
|
|
|
|
---
|
|
|
|
## Loop Inventory
|
|
|
|
### 1. JsonParserBox Loops (tools/hako_shared/json_parser.hako)
|
|
|
|
| Line | Function | Pattern | Condition Variables | ConditionScope | Status | Notes |
|
|
|------|----------|---------|---------------------|----------------|--------|-------|
|
|
| 121 | `_parse_number` | Pattern2 | `p` (LoopParam) | LoopParam only | ✅ PASS | Simple increment loop with break |
|
|
| 150 | `_parse_string` | Pattern2 | `p` (LoopParam) | LoopParam only | ✅ PASS | Parse loop with break/continue |
|
|
| 203 | `_parse_array` | Pattern2 | `p` (LoopParam) | LoopParam only | ✅ PASS | Array element parsing loop |
|
|
| 256 | `_parse_object` | Pattern2 | `p` (LoopParam) | LoopParam only | ✅ PASS | Object key-value parsing loop |
|
|
| 312 | `_skip_whitespace` | Pattern2 | `p` (LoopParam) | LoopParam only | ✅ PASS | Whitespace skipping loop |
|
|
| 330 | `_trim` (leading) | Pattern2 | `start` (LoopParam), `end` (OuterLocal) | LoopParam + OuterLocal | ⚠️ BLOCKED | Uses loop-body `ch` in break condition |
|
|
| 340 | `_trim` (trailing) | Pattern2 | `end` (LoopParam), `start` (OuterLocal) | LoopParam + OuterLocal | ⚠️ BLOCKED | Uses loop-body `ch` in break condition |
|
|
| 357 | `_match_literal` | Pattern1 | `i` (LoopParam), `len` (OuterLocal) | LoopParam + OuterLocal | ✅ PASS | Simple iteration with early return |
|
|
| 373 | `_unescape_string` | Pattern2 | `i` (LoopParam) | LoopParam only | ✅ PASS | Complex escape processing with continue |
|
|
| 453 | `_atoi` | Pattern2 | `i` (LoopParam), `n` (OuterLocal) | LoopParam + OuterLocal | ✅ PASS | Number parsing with break |
|
|
|
|
**Summary**:
|
|
- **Total Loops**: 10
|
|
- **❌ FAIL**: 1+ loops (JsonParserBox fails to compile with JoinIR)
|
|
- **⚠️ FALSE POSITIVE**: Previous analysis was incorrect - JsonParserBox does NOT compile successfully
|
|
|
|
### 2. TrimTest Loops (local_tests/test_trim_main_pattern.hako)
|
|
|
|
| Line | Function | Pattern | Condition Variables | ConditionScope | Status | Notes |
|
|
|------|----------|---------|---------------------|----------------|--------|-------|
|
|
| 20 | `trim` (leading) | Pattern2 | `start` (LoopParam), `end` (OuterLocal) | LoopParam + OuterLocal | ❌ FAIL | `ch` is LoopBodyLocal used in break |
|
|
| 30 | `trim` (trailing) | Pattern2 | `end` (LoopParam), `start` (OuterLocal) | LoopParam + OuterLocal | ❌ FAIL | `ch` is LoopBodyLocal used in break |
|
|
|
|
**Summary**:
|
|
- **Total Loops**: 2
|
|
- **✅ PASS**: 0 loops (0%)
|
|
- **❌ FAIL**: 2 loops (100%) - UnsupportedPattern error
|
|
|
|
---
|
|
|
|
## Execution Results
|
|
|
|
### JsonParserBox Tests
|
|
|
|
**Compilation Status**: ❌ FAIL (UnsupportedPattern error in `_parse_object`)
|
|
|
|
**Test Command**:
|
|
```bash
|
|
NYASH_JOINIR_STRUCTURE_ONLY=1 ./target/release/hakorune tools/hako_shared/json_parser.hako
|
|
```
|
|
|
|
**Error**:
|
|
```
|
|
[ERROR] ❌ MIR compilation error: [cf_loop/pattern4] Lowering failed:
|
|
[joinir/pattern4] Unsupported condition: uses loop-body-local variables: ["s"].
|
|
Pattern 4 supports only loop parameters and outer-scope variables.
|
|
Consider using Pattern 5+ for complex loop conditions.
|
|
```
|
|
|
|
**Analysis**:
|
|
- Compilation fails on `_parse_object` loop (line 256)
|
|
- Error claims `s` is a LoopBodyLocal, but `s` is a function parameter
|
|
- **POTENTIAL BUG**: Variable scope detection may incorrectly classify function parameters
|
|
- Previous test success was misleading - we only saw tail output which showed runtime error, not compilation error
|
|
|
|
### TrimTest
|
|
|
|
**Compilation Status**: ❌ FAIL (UnsupportedPattern error)
|
|
|
|
**Test Command**:
|
|
```bash
|
|
NYASH_JOINIR_STRUCTURE_ONLY=1 ./target/release/hakorune local_tests/test_trim_main_pattern.hako
|
|
```
|
|
|
|
**Error**:
|
|
```
|
|
[ERROR] ❌ MIR compilation error: [cf_loop/pattern2] Lowering failed:
|
|
[joinir/pattern2] Unsupported condition: uses loop-body-local variables: ["ch", "end"].
|
|
Pattern 2 supports only loop parameters and outer-scope variables.
|
|
Consider using Pattern 5+ for complex loop conditions.
|
|
```
|
|
|
|
**Problematic Loop** (line 20-27):
|
|
```hako
|
|
loop(start < end) {
|
|
local ch = s.substring(start, start+1) // LoopBodyLocal
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|
start = start + 1
|
|
} else {
|
|
break // ⚠️ break condition implicitly depends on 'ch'
|
|
}
|
|
}
|
|
```
|
|
|
|
**Analysis**:
|
|
- Variable `ch` is declared inside loop body
|
|
- The `break` is inside an `if` that checks `ch`
|
|
- LoopConditionScopeBox correctly detects `ch` as LoopBodyLocal
|
|
- Pattern 2 cannot handle this case
|
|
|
|
---
|
|
|
|
## Detailed Loop Analysis
|
|
|
|
### ✅ Pattern 1: Simple Iteration (`_match_literal` line 357)
|
|
|
|
```hako
|
|
loop(i < len) {
|
|
if s.substring(pos + i, pos + i + 1) != literal.substring(i, i + 1) {
|
|
return 0 // Early return (not break)
|
|
}
|
|
i = i + 1
|
|
}
|
|
```
|
|
|
|
**Why it works**:
|
|
- Condition uses only LoopParam (`i`) and OuterLocal (`len`)
|
|
- No break/continue (early return is handled differently)
|
|
- Pattern 1 minimal structure
|
|
|
|
### ✅ Pattern 2: Break with LoopParam only (`_skip_whitespace` line 312)
|
|
|
|
```hako
|
|
loop(p < s.length()) {
|
|
local ch = s.substring(p, p+1)
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|
p = p + 1
|
|
} else {
|
|
break // ✅ No problem - 'ch' not used in loop condition
|
|
}
|
|
}
|
|
```
|
|
|
|
**Why it works**:
|
|
- Loop condition `p < s.length()` uses only LoopParam (`p`)
|
|
- Variable `ch` is LoopBodyLocal but NOT used in loop condition
|
|
- Break is inside loop body, not affecting condition scope
|
|
- **Key insight**: LoopConditionScopeBox analyzes the loop condition expression `p < s.length()`, not the break condition `ch == " "`
|
|
|
|
### ⚠️ Pattern 2: Break with LoopBodyLocal (`_trim` line 330)
|
|
|
|
```hako
|
|
loop(start < end) {
|
|
local ch = s.substring(start, start+1)
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|
start = start + 1
|
|
} else {
|
|
break // ❌ Problem - TrimTest version detects 'ch' in condition scope
|
|
}
|
|
}
|
|
```
|
|
|
|
**Why it's blocked in TrimTest but passes in JsonParserBox**:
|
|
- **JsonParserBox version**: Compiles successfully (see line 330-337)
|
|
- **TrimTest version**: Fails with UnsupportedPattern error
|
|
- **Difference**: The error message shows `["ch", "end"]` for TrimTest
|
|
- **Hypothesis**: There may be a subtle difference in how the condition scope is analyzed between the two files
|
|
|
|
**Investigation needed**: Why does the identical loop structure pass in JsonParserBox but fail in TrimTest?
|
|
|
|
### ✅ Pattern 2: Complex continue (`_unescape_string` line 373)
|
|
|
|
```hako
|
|
loop(i < s.length()) {
|
|
local ch = s.substring(i, i+1)
|
|
// ... complex nested if with multiple continue statements
|
|
if process_escape == 1 {
|
|
if next == "n" {
|
|
result = result + "\n"
|
|
i = i + 2
|
|
continue // ✅ Works fine
|
|
}
|
|
// ... more continue cases
|
|
}
|
|
result = result + ch
|
|
i = i + 1
|
|
}
|
|
```
|
|
|
|
**Why it works**:
|
|
- Loop condition `i < s.length()` uses only LoopParam
|
|
- All LoopBodyLocal variables (`ch`, `next`, etc.) are NOT in condition scope
|
|
- Continue statements are fine as long as condition is simple
|
|
|
|
---
|
|
|
|
## Pattern 1-4 Scope Definition
|
|
|
|
### What LoopConditionScopeBox Checks
|
|
|
|
**LoopConditionScopeBox** (from Phase 170-D) analyzes the **loop condition expression** only, not the entire loop body:
|
|
|
|
```rust
|
|
// Loop condition analysis
|
|
loop(start < end) { // ← Analyzes THIS expression only
|
|
local ch = ... // ← Does NOT analyze loop body
|
|
if ch == " " { // ← Does NOT analyze if conditions
|
|
break
|
|
}
|
|
}
|
|
```
|
|
|
|
**Supported Variable Scopes**:
|
|
1. **LoopParam**: Variables that are loop parameters (`start`, `end`, `i`, `p`)
|
|
2. **OuterLocal**: Variables defined before the loop in outer scope
|
|
3. **LoopBodyLocal**: ❌ Variables defined inside loop body (NOT supported)
|
|
|
|
### Pattern 1-4 Official Support Matrix
|
|
|
|
| Pattern | Condition Variables | Break | Continue | Support Status |
|
|
|---------|-------------------|-------|----------|----------------|
|
|
| Pattern 1 | LoopParam + OuterLocal | ❌ No | ❌ No | ✅ Full support |
|
|
| Pattern 2 | LoopParam + OuterLocal | ✅ Yes | ❌ No | ✅ Full support |
|
|
| Pattern 3 | LoopParam + OuterLocal | ✅ Yes | ✅ Yes | ✅ Full support (with ExitPHI) |
|
|
| Pattern 4 | LoopParam + OuterLocal | ✅ Yes | ✅ Yes | ✅ Full support (with continue carrier) |
|
|
|
|
**Key Constraint**: All patterns require that the loop condition expression uses ONLY LoopParam and OuterLocal variables.
|
|
|
|
---
|
|
|
|
## Critical Bug Discovery
|
|
|
|
### Variable Scope Classification Error
|
|
|
|
**Issue**: LoopConditionScopeBox incorrectly classifies function parameters as LoopBodyLocal variables.
|
|
|
|
**Evidence**:
|
|
1. `_parse_object(s, pos)` - function parameter `s` reported as LoopBodyLocal
|
|
2. Error message: `uses loop-body-local variables: ["s"]`
|
|
3. Expected: Function parameters should be classified as OuterLocal or function-scope
|
|
|
|
**Impact**:
|
|
- **FALSE NEGATIVE**: Legitimate loops are rejected
|
|
- **JsonParserBox completely non-functional** with JoinIR due to this bug
|
|
- Previous 80% success claim was INCORRECT
|
|
|
|
**Root Cause Analysis Needed**:
|
|
```rust
|
|
// File: src/mir/join_ir/lowering/loop_scope_shape/condition.rs
|
|
// Suspected: detect_variable_scope() logic
|
|
|
|
// Function parameters should be:
|
|
// - Available in loop scope
|
|
// - NOT classified as LoopBodyLocal
|
|
// - Should be OuterLocal or special FunctionParam category
|
|
```
|
|
|
|
**Test Case**:
|
|
```hako
|
|
_parse_object(s, pos) { // s and pos are function parameters
|
|
local p = pos + 1
|
|
loop(p < s.length()) { // ERROR: claims 's' is LoopBodyLocal
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
**Expected Behavior**:
|
|
- `s`: Function parameter → OuterLocal (available before loop)
|
|
- `p`: Local variable → OuterLocal (defined before loop)
|
|
- Loop condition `p < s.length()` should be ✅ VALID
|
|
|
|
**Actual Behavior**:
|
|
- `s`: Incorrectly classified as LoopBodyLocal
|
|
- Loop rejected with UnsupportedPattern error
|
|
|
|
### Recommendation
|
|
|
|
**PRIORITY 1**: Fix LoopConditionScopeBox variable scope detection
|
|
- Function parameters must be recognized as outer-scope
|
|
- Update `detect_variable_scope()` to handle function parameters correctly
|
|
- Add test case for function parameters in loop conditions
|
|
|
|
**PRIORITY 2**: Re-run this analysis after fix
|
|
- All 10 JsonParserBox loops may actually be Pattern 1-4 compatible
|
|
- Current analysis is invalidated by this bug
|
|
|
|
---
|
|
|
|
## Conclusion
|
|
|
|
### Pattern 1-4 Coverage for JsonParser/Trim (INVALIDATED BY BUG)
|
|
|
|
**⚠️ WARNING**: The following analysis is INCORRECT due to the variable scope classification bug.
|
|
|
|
**Claimed Supported Loops** (8/10 = 80%) - INVALID:
|
|
1. ❌ `_parse_number` - Fails due to function parameter misclassification
|
|
2. ❌ `_parse_string` - Likely fails (not tested)
|
|
3. ❌ `_parse_array` - Likely fails (not tested)
|
|
4. ❌ `_parse_object` - **CONFIRMED FAIL**: `s` parameter classified as LoopBodyLocal
|
|
5. ❓ `_skip_whitespace` - Unknown
|
|
6. ❓ `_match_literal` - Unknown
|
|
7. ❓ `_unescape_string` - Unknown
|
|
8. ❓ `_atoi` - Unknown
|
|
|
|
**Blocked Loops** (2/10 = 20%):
|
|
1. `_trim` (leading whitespace) - Uses LoopBodyLocal `ch` in break logic (legitimately blocked)
|
|
2. `_trim` (trailing whitespace) - Uses LoopBodyLocal `ch` in break logic (legitimately blocked)
|
|
|
|
**Actual Coverage**: UNKNOWN - Cannot determine until bug is fixed
|
|
|
|
### Technical Insight (REVISED)
|
|
|
|
**Original Hypothesis** (INCORRECT):
|
|
- "80% success rate demonstrates Pattern 1-4 effectiveness"
|
|
- "Only trim loops need Pattern 5+"
|
|
|
|
**Actual Reality** (DISCOVERED):
|
|
- **BUG**: Function parameters incorrectly classified as LoopBodyLocal
|
|
- **BLOCKER**: Cannot evaluate Pattern 1-4 coverage until bug is fixed
|
|
- **TRUE UNKNOWNS**:
|
|
- How many loops actually work with current implementation?
|
|
- Are there other variable scope bugs beyond function parameters?
|
|
|
|
**Confirmed Issues**:
|
|
1. **Bug**: Function parameters classified as LoopBodyLocal (Priority 1 fix needed)
|
|
2. **Legitimate Block**: `_trim` loops use loop-body `ch` variable in break conditions (needs Pattern 5+ or .hako rewrite)
|
|
|
|
### Recommendations
|
|
|
|
#### IMMEDIATE: Fix Variable Scope Bug (Phase 166-bugfix)
|
|
|
|
**Priority**: 🔥 CRITICAL - Blocks all JoinIR loop development
|
|
|
|
**Action Items**:
|
|
1. Investigate `LoopConditionScopeBox::detect_variable_scope()` in `src/mir/join_ir/lowering/loop_scope_shape/condition.rs`
|
|
2. Ensure function parameters are classified as OuterLocal, not LoopBodyLocal
|
|
3. Add test case: function with parameters used in loop conditions
|
|
4. Re-run this analysis after fix
|
|
|
|
**Test Case**:
|
|
```hako
|
|
static box FunctionParamTest {
|
|
method test(s, pos) {
|
|
local p = pos
|
|
loop(p < s.length()) { // Should work: 's' is function param
|
|
p = p + 1
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### For JsonParserBox (AFTER BUG FIX)
|
|
|
|
**Current Status**: ❌ BLOCKED by variable scope bug
|
|
|
|
**Post-Fix Expectations**:
|
|
- Most loops should work (likely 8-9 out of 10)
|
|
- Only `_trim` loops legitimately blocked
|
|
|
|
**Short-term Fix** (.hako rewrite for `_trim`):
|
|
```hako
|
|
// ❌ Current (blocked)
|
|
loop(start < end) {
|
|
local ch = s.substring(start, start+1)
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|
start = start + 1
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
// ✅ Option A: Hoist peek outside loop
|
|
local ch = ""
|
|
loop(start < end) {
|
|
ch = s.substring(start, start+1) // No local declaration
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|
start = start + 1
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
// ✅ Option B: Use helper method
|
|
loop(start < end && me._is_whitespace_at(s, start)) {
|
|
start = start + 1
|
|
}
|
|
```
|
|
|
|
**Long-term Solution** (Pattern 5+):
|
|
- Implement BoolExprLowerer for OR/AND chains in loop conditions
|
|
- Support LoopBodyLocal in condition scope
|
|
- Target: Phase 167+
|
|
|
|
#### For Phase 167+ (Pattern 5+ Implementation)
|
|
|
|
**Delegation Scope**:
|
|
1. **LoopBodyLocal in conditions**: Support variables defined in loop body used in break/continue conditions
|
|
2. **Complex boolean expressions**: OR/AND chains, short-circuit evaluation
|
|
3. **Nested control flow**: break inside if-else chains with multiple variables
|
|
|
|
**Not Needed for JsonParser**:
|
|
- The current 80% coverage is sufficient for MVP
|
|
- The 2 failing loops can be fixed with simple .hako rewrites
|
|
|
|
---
|
|
|
|
## Investigation: JsonParserBox vs TrimTest Discrepancy
|
|
|
|
### Mystery: Identical Loop Structure, Different Results
|
|
|
|
**JsonParserBox `_trim` (line 330)**: ✅ Compiles successfully
|
|
**TrimTest `trim` (line 20)**: ❌ Fails with UnsupportedPattern
|
|
|
|
**Hypothesis 1**: File-level difference
|
|
- Different `using` statements?
|
|
- Different compiler flags?
|
|
|
|
**Hypothesis 2**: Context difference
|
|
- Static box vs regular method?
|
|
- Different variable scoping?
|
|
|
|
**Hypothesis 3**: Error detection timing
|
|
- JsonParserBox error not shown in tail output?
|
|
- Need to check full compilation log?
|
|
|
|
**Action Item**: Re-run JsonParserBox test with full error logging:
|
|
```bash
|
|
NYASH_JOINIR_STRUCTURE_ONLY=1 NYASH_JOINIR_DEBUG=1 \
|
|
./target/release/hakorune tools/hako_shared/json_parser.hako 2>&1 | \
|
|
grep -E "Unsupported|LoopBodyLocal"
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
### Immediate Actions
|
|
|
|
1. **✅ DONE**: Document current Pattern 1-4 coverage (this file)
|
|
2. **✅ DONE**: Identify blocked loops and their patterns
|
|
3. **TODO**: Investigate JsonParserBox vs TrimTest discrepancy
|
|
4. **TODO**: Update CURRENT_TASK.md with findings
|
|
5. **TODO**: Update joinir-architecture-overview.md with LoopConditionScopeBox
|
|
|
|
### Phase 167+ Planning
|
|
|
|
**Pattern 5+ Requirements** (based on blocked loops):
|
|
1. LoopBodyLocal support in condition scope
|
|
2. BoolExprLowerer for complex OR/AND chains
|
|
3. Proper PHI generation for loop-body variables
|
|
|
|
**Alternative Path** (.hako rewrites):
|
|
- Rewrite `_trim` to hoist variable declarations
|
|
- Use helper methods for complex conditions
|
|
- Target: 100% Pattern 1-4 coverage with minimal changes
|
|
|
|
---
|
|
|
|
## Appendix: Full Loop Catalog
|
|
|
|
### All 10 JsonParserBox Loops
|
|
|
|
#### Loop 1: _parse_number (line 121-133)
|
|
```hako
|
|
loop(p < s.length()) {
|
|
local ch = s.substring(p, p+1)
|
|
local digit_pos = digits.indexOf(ch)
|
|
if digit_pos < 0 { break }
|
|
num_str = num_str + ch
|
|
p = p + 1
|
|
}
|
|
```
|
|
- **Pattern**: 2 (break only)
|
|
- **Condition**: `p` (LoopParam) only
|
|
- **Status**: ✅ PASS
|
|
|
|
#### Loop 2: _parse_string (line 150-178)
|
|
```hako
|
|
loop(p < s.length()) {
|
|
local ch = s.substring(p, p+1)
|
|
if ch == '"' { return ... }
|
|
if ch == "\\" {
|
|
// escape handling
|
|
continue
|
|
}
|
|
str = str + ch
|
|
p = p + 1
|
|
}
|
|
```
|
|
- **Pattern**: 2 (break + continue)
|
|
- **Condition**: `p` (LoopParam) only
|
|
- **Status**: ✅ PASS
|
|
|
|
#### Loop 3: _parse_array (line 203-231)
|
|
```hako
|
|
loop(p < s.length()) {
|
|
local elem_result = me._parse_value(s, p)
|
|
if elem_result == null { return null }
|
|
arr.push(elem_result.get("value"))
|
|
p = elem_result.get("pos")
|
|
// ... more processing
|
|
if ch == "]" { return ... }
|
|
if ch == "," { continue }
|
|
return null
|
|
}
|
|
```
|
|
- **Pattern**: 2 (continue + early return)
|
|
- **Condition**: `p` (LoopParam) only
|
|
- **Status**: ✅ PASS
|
|
|
|
#### Loop 4: _parse_object (line 256-304)
|
|
```hako
|
|
loop(p < s.length()) {
|
|
// Parse key-value pairs
|
|
local key_result = me._parse_string(s, p)
|
|
if key_result == null { return null }
|
|
// ... more processing
|
|
if ch == "}" { return ... }
|
|
if ch == "," { continue }
|
|
return null
|
|
}
|
|
```
|
|
- **Pattern**: 2 (continue + early return)
|
|
- **Condition**: `p` (LoopParam) only
|
|
- **Status**: ✅ PASS
|
|
|
|
#### Loop 5: _skip_whitespace (line 312-320)
|
|
```hako
|
|
loop(p < s.length()) {
|
|
local ch = s.substring(p, p+1)
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|
p = p + 1
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
```
|
|
- **Pattern**: 2 (break only)
|
|
- **Condition**: `p` (LoopParam) only
|
|
- **Status**: ✅ PASS
|
|
|
|
#### Loop 6: _trim leading (line 330-337)
|
|
```hako
|
|
loop(start < end) {
|
|
local ch = s.substring(start, start+1)
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|
start = start + 1
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
```
|
|
- **Pattern**: 2 (break only)
|
|
- **Condition**: `start` (LoopParam) + `end` (OuterLocal)
|
|
- **Status**: ⚠️ BLOCKED in TrimTest, ✅ PASS in JsonParserBox (needs investigation)
|
|
|
|
#### Loop 7: _trim trailing (line 340-347)
|
|
```hako
|
|
loop(end > start) {
|
|
local ch = s.substring(end-1, end)
|
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
|
end = end - 1
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
```
|
|
- **Pattern**: 2 (break only)
|
|
- **Condition**: `end` (LoopParam) + `start` (OuterLocal)
|
|
- **Status**: ⚠️ BLOCKED in TrimTest, ✅ PASS in JsonParserBox (needs investigation)
|
|
|
|
#### Loop 8: _match_literal (line 357-362)
|
|
```hako
|
|
loop(i < len) {
|
|
if s.substring(pos + i, pos + i + 1) != literal.substring(i, i + 1) {
|
|
return 0
|
|
}
|
|
i = i + 1
|
|
}
|
|
```
|
|
- **Pattern**: 1 (no break/continue)
|
|
- **Condition**: `i` (LoopParam) + `len` (OuterLocal)
|
|
- **Status**: ✅ PASS
|
|
|
|
#### Loop 9: _unescape_string (line 373-431)
|
|
```hako
|
|
loop(i < s.length()) {
|
|
local ch = s.substring(i, i+1)
|
|
// Complex escape processing
|
|
if process_escape == 1 {
|
|
if next == "n" {
|
|
result = result + "\n"
|
|
i = i + 2
|
|
continue
|
|
}
|
|
// ... more continue cases
|
|
}
|
|
result = result + ch
|
|
i = i + 1
|
|
}
|
|
```
|
|
- **Pattern**: 2 (continue only)
|
|
- **Condition**: `i` (LoopParam) only
|
|
- **Status**: ✅ PASS
|
|
|
|
#### Loop 10: _atoi (line 453-460)
|
|
```hako
|
|
loop(i < n) {
|
|
local ch = s.substring(i, i+1)
|
|
if ch < "0" || ch > "9" { break }
|
|
local pos = digits.indexOf(ch)
|
|
if pos < 0 { break }
|
|
v = v * 10 + pos
|
|
i = i + 1
|
|
}
|
|
```
|
|
- **Pattern**: 2 (break only)
|
|
- **Condition**: `i` (LoopParam) + `n` (OuterLocal)
|
|
- **Status**: ✅ PASS
|
|
|
|
---
|
|
|
|
**Document Version**: 1.0
|
|
**Author**: Claude Code (Phase 166-impl-2 analysis)
|
|
**References**:
|
|
- Phase 170-D: LoopConditionScopeBox implementation
|
|
- Phase 166: Loop pattern detection
|
|
- tools/hako_shared/json_parser.hako
|
|
- local_tests/test_trim_main_pattern.hako
|