Files
hakorune/docs/development/current/main/phases/phase-142/BEFORE_AFTER.md
nyash-codex d605611a16 feat(canonicalizer): Phase 143-P0 - parse_number pattern support
Add parse_number pattern recognition to canonicalizer, expanding adaptation
range for digit collection loops with break in THEN clause.

## Changes

### New Recognizer (ast_feature_extractor.rs)
- `detect_parse_number_pattern()`: Detects `if invalid { break }` pattern
- `ParseNumberInfo`: Struct for extracted pattern info
- ~150 lines added

### Canonicalizer Integration (canonicalizer.rs)
- Parse_number pattern detection before skip_whitespace
- LoopSkeleton construction with 4 steps (Header + Body x2 + Update)
- Routes to Pattern2Break (has_break=true)
- ~60 lines modified

### Export Chain (6 files)
- patterns/mod.rs → joinir/mod.rs → control_flow/mod.rs
- builder.rs → mir/mod.rs
- 8 lines total

### Tests
- `test_parse_number_pattern_recognized()`: Unit test for recognition
- Strict parity verification: GREEN (canonical and router agree)
- ~130 lines added

## Pattern Comparison

| Aspect | Skip Whitespace | Parse Number |
|--------|----------------|--------------|
| Break location | ELSE clause | THEN clause |
| Pattern | `if cond { update } else { break }` | `if invalid { break } rest... update` |
| Body after if | None | Required (result append) |

## Results

-  Skeleton creation successful
-  RoutingDecision matches router (Pattern2Break)
-  Strict parity OK (canonicalizer ↔ router agreement)
-  Unit test PASS
-  Manual test: test_pattern2_parse_number.hako executes correctly

## Statistics

- New patterns: 1 (parse_number)
- Total patterns: 3 (skip_whitespace, parse_number, continue)
- Lines added: ~280
- Files modified: 8
- Parity status: Green 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-16 09:08:37 +09:00

8.0 KiB

Phase 142 P0: Before/After Comparison

Pattern Recognition Capability

Before Phase 142

Recognized Patterns: 1 pattern

  • skip_whitespace: p = p + 1 (Add operator only)

Code:

// Value must be: target + const
match value.as_ref() {
    ASTNode::BinaryOp {
        operator: BinaryOperator::Add,  // ← Only Add operator
        left,
        right,
        ..
    } => {
        // ... extraction logic
        let delta = *n;  // ← Always positive
    }
    _ => return None,
}

Limitation:

  • Rejected all Subtract operators
  • Could not recognize end = end - 1 patterns
  • No support for trim_trailing use cases

After Phase 142 P0

Recognized Patterns: 3 patterns

  • skip_whitespace: p = p + 1 (Add operator)
  • trim_leading: start = start + 1 (Add operator)
  • trim_trailing: end = end - 1 (Subtract operator)

Code:

// Value must be: target (+|-) const
match value.as_ref() {
    ASTNode::BinaryOp {
        operator,  // ← Both Add and Subtract
        left,
        right,
        ..
    } => {
        // Phase 142 P0: Accept both Add (+1) and Subtract (-1)
        let op_multiplier = match operator {
            BinaryOperator::Add => 1,
            BinaryOperator::Subtract => -1,
            _ => return None,
        };

        let delta = const_val * op_multiplier;  // ← Can be negative
    }
    _ => return None,
}

Improvement:

  • Accepts both Add and Subtract operators
  • Supports negative deltas (e.g., -1)
  • Enables trim_trailing and similar patterns

Test Coverage

Before Phase 142

Unit Tests: 5 tests

  • test_canonicalize_rejects_non_loop
  • test_skip_whitespace_pattern_recognition
  • test_skip_whitespace_with_body_statements
  • test_skip_whitespace_fails_without_else
  • test_skip_whitespace_fails_with_wrong_delta (tested Subtract as failure case)

Manual Tests: 0 tests for trim patterns

After Phase 142 P0

Unit Tests: 7 tests (+2 new)

  • test_canonicalize_rejects_non_loop
  • test_skip_whitespace_pattern_recognition
  • test_skip_whitespace_with_body_statements
  • test_skip_whitespace_fails_without_else
  • test_skip_whitespace_fails_with_wrong_delta (now tests Multiply as failure)
  • test_trim_leading_pattern_recognized (NEW)
  • test_trim_trailing_pattern_recognized (NEW)

Manual Tests: 2 test files

  • tools/selfhost/test_pattern3_trim_leading.hako
  • tools/selfhost/test_pattern3_trim_trailing.hako

Parity Verification

Before Phase 142

trim_leading.hako:

[loop_canonicalizer]   Decision: FAIL_FAST
[loop_canonicalizer]   Missing caps: [ConstStep]
❌ No parity check (pattern not recognized)

trim_trailing.hako:

[loop_canonicalizer]   Decision: FAIL_FAST
[loop_canonicalizer]   Missing caps: [ConstStep]
❌ No parity check (pattern not recognized)

After Phase 142 P0

trim_leading.hako:

[loop_canonicalizer]   Decision: SUCCESS
[loop_canonicalizer]   Chosen pattern: Pattern2Break
[loop_canonicalizer]   Missing caps: []
[choose_pattern_kind/PARITY] ✅ OK: canonical and actual agree on Pattern2Break
[loop_canonicalizer/PARITY] ✅ OK in function 'main': canonical and actual agree on Pattern2Break

trim_trailing.hako:

[loop_canonicalizer]   Decision: SUCCESS
[loop_canonicalizer]   Chosen pattern: Pattern2Break
[loop_canonicalizer]   Missing caps: []
[choose_pattern_kind/PARITY] ✅ OK: canonical and actual agree on Pattern2Break
[loop_canonicalizer/PARITY] ✅ OK in function 'main': canonical and actual agree on Pattern2Break

Skeleton Generation

Before Phase 142

trim_leading pattern:

// Pattern NOT recognized
// Returns: RoutingDecision::fail_fast(...)
 No skeleton generated

After Phase 142 P0

trim_leading pattern:

// Pattern recognized!
LoopSkeleton {
    steps: [
        SkeletonStep::HeaderCond { expr: start < end },
        SkeletonStep::Body { stmts: [/* body statements */] },
        SkeletonStep::Update {
            carrier_name: "start",
            update_kind: UpdateKind::ConstStep { delta: 1 }  // ← Positive
        }
    ],
    carriers: [
        CarrierSlot {
            name: "start",
            role: CarrierRole::Counter,
            update_kind: UpdateKind::ConstStep { delta: 1 }
        }
    ],
    exits: ExitContract {
        has_break: true,
        has_continue: false,
        has_return: false,
        break_has_value: false
    }
}

trim_trailing pattern:

// Pattern recognized!
LoopSkeleton {
    steps: [
        SkeletonStep::HeaderCond { expr: end > start },
        SkeletonStep::Body { stmts: [/* body statements */] },
        SkeletonStep::Update {
            carrier_name: "end",
            update_kind: UpdateKind::ConstStep { delta: -1 }  // ← Negative!
        }
    ],
    carriers: [
        CarrierSlot {
            name: "end",
            role: CarrierRole::Counter,
            update_kind: UpdateKind::ConstStep { delta: -1 }  // ← Negative!
        }
    ],
    exits: ExitContract {
        has_break: true,
        has_continue: false,
        has_return: false,
        break_has_value: false
    }
}

Routing Decision

Before Phase 142

All patterns:

if skip_whitespace (+1 only) → Pattern2Break
else → FAIL_FAST

After Phase 142 P0

All patterns:

if skip_whitespace (+1) → Pattern2Break
if trim_leading (+1) → Pattern2Break
if trim_trailing (-1) → Pattern2Break
else → FAIL_FAST

Documentation

Before Phase 142

  • No Phase 142 documentation
  • ast_feature_extractor.rs comments: "skip_whitespace pattern only"

After Phase 142 P0

  • docs/development/current/main/phases/phase-142/README.md (complete specification)
  • docs/development/current/main/phases/phase-142/IMPLEMENTATION_SUMMARY.md (detailed summary)
  • docs/development/current/main/phases/phase-142/BEFORE_AFTER.md (this file)
  • Updated comments in ast_feature_extractor.rs to reflect generalization

Impact Summary

Functionality

  • Pattern recognition: 1 → 3 patterns (+200%)
  • Operator support: Add only → Add + Subtract
  • Delta range: Positive only → Positive and Negative

Testing

  • Unit tests: 5 → 7 tests (+40%)
  • Manual tests: 0 → 2 test files
  • Parity checks: None → 2 passing

Code Quality

  • Lines of code: +206 lines (91% tests)
  • Warnings: 0 new warnings
  • Documentation: 3 new documents

Compatibility

  • Backward compatible: All existing patterns still work
  • No breaking changes: Default behavior unchanged
  • SSOT maintained: Single source of truth preserved

Verification

Quick Test

# Before Phase 142 (would fail)
NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1 ./target/release/hakorune \
  tools/selfhost/test_pattern3_trim_trailing.hako
# Expected: FAIL_FAST (pattern not recognized)

# After Phase 142 P0 (succeeds)
NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1 ./target/release/hakorune \
  tools/selfhost/test_pattern3_trim_trailing.hako
# Expected: [loop_canonicalizer/PARITY] OK: canonical and actual agree on Pattern2Break

Full Verification

# Build
cargo build --release --lib

# Unit tests
cargo test --release --lib loop_canonicalizer::canonicalizer::tests
# Expected: 7 passed

# Manual tests
NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1 ./target/release/hakorune \
  tools/selfhost/test_pattern3_trim_leading.hako
# Expected: PARITY OK

NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1 ./target/release/hakorune \
  tools/selfhost/test_pattern3_trim_trailing.hako
# Expected: PARITY OK

Conclusion

Phase 142 P0 successfully extends the Canonicalizer to handle trim patterns by:

  1. Generalizing operator support (Add + Subtract)
  2. Supporting negative deltas (-1)
  3. Maintaining backward compatibility
  4. Adding comprehensive tests
  5. Achieving strict parity green

The implementation is minimal, focused, and sets a solid foundation for future pattern extensions.


Status: Complete Impact: High (enables new pattern class) Risk: Low (backward compatible, well-tested) Next: Phase 142 P1 (A-3 Trim promotion)