Files
hakorune/docs/development/current/main/phase171-pattern5-loop-inventory.md
nyash-codex 88400e7e22 feat(joinir): Phase 171-C-2 Trim pattern detection in LoopBodyCarrierPromoter
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>
2025-12-07 23:09:25 +09:00

6.5 KiB

Phase 171-A: Blocked Loop Inventory

Date: 2025-12-07 Status: Initial inventory complete Purpose: Identify loops blocked by LoopBodyLocal variables in break conditions


Overview

This document catalogs loops that cannot be lowered by Pattern 2/4 because they use LoopBodyLocal variables in their break conditions. These are candidates for Pattern 5 carrier promotion.


Blocked Loops Found

1. TrimTest.trim/1 - Leading Whitespace Trim

File: local_tests/test_trim_main_pattern.hako Lines: 20-27

loop(start < end) {
    local ch = s.substring(start, start+1)
    if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
        start = start + 1
    } else {
        break
    }
}

Blocking Variable: ch (LoopBodyLocal)

Analysis:

  • Loop parameter: start (LoopParam)
  • Outer variable: end (OuterLocal)
  • Break condition uses: ch (LoopBodyLocal)
  • ch is defined inside loop body as s.substring(start, start+1)

Error Message:

[trace:debug] pattern2: Pattern 2 lowerer failed: Variable 'ch' not bound in ConditionEnv

Why Blocked: Pattern 2 expects break conditions to only use:

  • LoopParam (start)
  • OuterLocal (end, s)

But the break condition ch == " " || ... uses ch, which is defined inside the loop body.


2. TrimTest.trim/1 - Trailing Whitespace Trim

File: local_tests/test_trim_main_pattern.hako Lines: 30-37

loop(end > start) {
    local ch = s.substring(end-1, end)
    if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
        end = end - 1
    } else {
        break
    }
}

Blocking Variable: ch (LoopBodyLocal)

Analysis:

  • Loop parameter: end (LoopParam)
  • Outer variable: start (OuterLocal)
  • Break condition uses: ch (LoopBodyLocal)
  • ch is defined inside loop body as s.substring(end-1, end)

Same blocking reason as Loop 1.


3. JsonParserBox - MethodCall in Condition

File: tools/hako_shared/json_parser.hako

loop(i < s.length()) {
    // ...
}

Blocking Issue: s.length() is a MethodCall in the condition expression.

Error Message:

[ERROR] ❌ MIR compilation error: [cf_loop/pattern4] Lowering failed:
Unsupported expression in value context: MethodCall {
    object: Variable { name: "s", ... },
    method: "length",
    arguments: [],
    ...
}

Why Blocked: Pattern 4's value context lowering doesn't support MethodCall expressions yet.

Note: This is not a LoopBodyLocal issue, but a MethodCall limitation. May be addressed in Phase 171-D (Optional).


Pattern5-A Target Decision

Selected Target: TrimTest Loop 1 (Leading Whitespace)

We select the first loop from TrimTest as Pattern5-A target for the following reasons:

  1. Clear structure: Simple substring + equality checks
  2. Representative: Same pattern as many real-world parsers
  3. Self-contained: Doesn't depend on complex outer state
  4. Testable: Easy to write unit tests

Pattern5-A Specification

Loop Structure:

loop(start < end) {
    local ch = s.substring(start, start+1)
    if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
        start = start + 1
    } else {
        break
    }
}

Variables:

  • start: LoopParam (carrier, mutated)
  • end: OuterLocal (condition-only)
  • s: OuterLocal (used in body)
  • ch: LoopBodyLocal (blocking variable)

Break Condition:

!(ch == " " || ch == "\t" || ch == "\n" || ch == "\r")

Promotion Strategy: Design D (Evaluated Bool Carrier)

Rationale

We choose Design D (Evaluated Bool Carrier) over other options:

Why not carry ch directly?

  • ch is a StringBox, not a primitive value
  • Would require complex carrier type system
  • Would break existing Pattern 2/4 assumptions

Design D approach:

  • Introduce a new carrier: is_whitespace (bool)
  • Evaluate ch == " " || ... in loop body
  • Store result in is_whitespace carrier
  • Use is_whitespace in break condition

Transformed Structure

Before (Pattern5-A):

loop(start < end) {
    local ch = s.substring(start, start+1)
    if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
        start = start + 1
    } else {
        break
    }
}

After (Pattern2 compatible):

// Initialization (before loop)
local is_whitespace = true  // Initial assumption

loop(start < end && is_whitespace) {
    local ch = s.substring(start, start+1)
    is_whitespace = (ch == " " || ch == "\t" || ch == "\n" || ch == "\r")

    if is_whitespace {
        start = start + 1
    } else {
        break  // Now redundant, but kept for clarity
    }
}

Key transformations:

  1. Add is_whitespace carrier initialization
  2. Update loop condition to include is_whitespace
  3. Compute is_whitespace in loop body
  4. Original if-else becomes simpler (could be optimized away)

Next Steps (Phase 171-C)

Phase 171-C-1: Skeleton Implementation

  • Create LoopBodyCarrierPromoter box
  • Define PromotionRequest / PromotionResult types
  • Implement skeleton try_promote() method
  • Add find_definition_in_body() helper

Phase 171-C-2: Trim Pattern Promotion Logic

  • Detect substring + equality pattern
  • Generate is_whitespace carrier
  • Generate initialization statement
  • Generate update statement

Phase 171-C-3: Integration with Pattern 2/4

  • Call LoopBodyCarrierPromoter::try_promote() in routing
  • If promotion succeeds, route to Pattern 2
  • If promotion fails, return UnsupportedPattern

Phase 171-D: MethodCall Support (Optional)

  • Handle s.length() in loop conditions
  • May require carrier promotion for method results
  • Lower priority than Trim pattern

Summary

Blocked Loops:

  • 2 loops in TrimTest (LoopBodyLocal ch)
  • 1+ loops in JsonParser (MethodCall in condition)

Pattern5-A Target:

  • TrimTest leading whitespace trim loop
  • Clear, representative, testable

Promotion Strategy:

  • Design D: Evaluated Bool Carrier
  • Transform ch checks → is_whitespace carrier
  • Make compatible with Pattern 2

Implementation Status:

  • Phase 171-A: Inventory complete
  • Phase 171-B: Target selected
  • Phase 171-C-1: Skeleton implementation complete
  • Phase 171-C-2: Trim pattern detection implemented
    • find_definition_in_body(): AST traversal for variable definitions
    • is_substring_method_call(): Detects substring() method calls
    • extract_equality_literals(): Extracts string literals from OR chains
    • TrimPatternInfo: Captures pattern details for carrier promotion
  • Phase 171-C-3: Integration with Pattern 2/4 routing