Files
hakorune/docs/development/current/main/phase224-digitpos-condition-normalizer.md
nyash-codex ed8e2d3142 feat(joinir): Phase 248 - Normalized JoinIR infrastructure
Major refactoring of JoinIR normalization pipeline:

Key changes:
- Structured→Normalized→MIR(direct) pipeline established
- ShapeGuard enhanced with Pattern2 loop validation
- dev_env.rs: New development fixtures and env control
- fixtures.rs: jsonparser_parse_number_real fixture
- normalized_bridge/direct.rs: Direct MIR generation from Normalized
- pattern2_step_schedule.rs: Extracted step scheduling logic

Files changed:
- normalized.rs: Enhanced NormalizedJoinModule with DevEnv support
- shape_guard.rs: Pattern2-specific validation (+300 lines)
- normalized_bridge.rs: Unified bridge with direct path
- loop_with_break_minimal.rs: Integrated step scheduling
- Deleted: step_schedule.rs (moved to pattern2_step_schedule.rs)

New files:
- param_guess.rs: Loop parameter inference
- pattern2_step_schedule.rs: Step scheduling for Pattern2
- phase43-norm-canon-p2-mid.md: Design doc

Tests: 937/937 PASS (+6 from baseline 931)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 03:15:45 +09:00

6.0 KiB
Raw Blame History

Phase 224-E: DigitPos Condition Normalizer

Status: ActiveDigitPos 条件正規化ラインの設計メモ / 実装ガイド)
Scope: digit_pos → is_digit_pos Carrier / ConditionEnv / ExprLowerer の正規化経路の SSOT ドキュメントだよ。Phase 26H / 34 系の JsonParser _parse_number / _atoi でもこの設計を前提にしている。

Problem Statement

Background

Phase 228-8 successfully implemented the DigitPos pattern promotion:

  • digit_pos: i32is_digit_pos: bool carrier
  • CarrierRole::ConditionOnly + CarrierInit::BoolConst(false)
  • ConditionEnv alias: digit_pos → is_digit_pos

Current Error

Type error: unsupported compare Lt on Bool(false) and Integer(0)

Root Cause: The break condition AST still contains digit_pos < 0, which after alias resolution becomes Bool(is_digit_pos) < Integer(0), causing a type mismatch.

Why alias isn't enough:

  • Alias only renames the variable reference
  • The comparison operator and integer literal remain unchanged
  • Need to transform the entire expression structure

Solution

Transformation

Transform the break condition AST from integer comparison to boolean negation:

Before: digit_pos < 0

BinaryOp {
    operator: Lt,
    left: Var("digit_pos"),
    right: Const(0)
}

After: !is_digit_pos

UnaryOp {
    operator: Not,
    expr: Var("is_digit_pos")
}

Semantic Equivalence

  • digit_pos < 0 means "indexOf() didn't find the character" (returns -1)
  • !is_digit_pos means "character is not a digit" (bool carrier is false)
  • Both express the same condition: "break when character not found/matched"

Design

Box: DigitPosConditionNormalizer

Responsibility: Transform digit_pos comparison patterns to boolean carrier expressions

API:

pub struct DigitPosConditionNormalizer;

impl DigitPosConditionNormalizer {
    /// Normalize digit_pos condition AST
    ///
    /// Transforms: `digit_pos < 0` → `!is_digit_pos`
    ///
    /// # Arguments
    /// * `cond` - Break/continue condition AST
    /// * `promoted_var` - Original variable name (e.g., "digit_pos")
    /// * `carrier_name` - Promoted carrier name (e.g., "is_digit_pos")
    ///
    /// # Returns
    /// Normalized AST (or original if pattern doesn't match)
    pub fn normalize(
        cond: &ASTNode,
        promoted_var: &str,
        carrier_name: &str,
    ) -> ASTNode;
}

Pattern Matching Logic

Match Pattern: <var> < 0 where:

  1. Operator is Lt (Less than)
  2. Left operand is Var(promoted_var)
  3. Right operand is Const(0)

Transformation: → UnaryOp { op: Not, expr: Var(carrier_name) }

Non-Match Behavior: Return original AST unchanged (Fail-Safe)

Integration Point

Location: src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs

Integration Steps:

  1. After LoopBodyCondPromoter::try_promote_for_condition() succeeds
  2. Extract promoted_var and carrier_name from promotion result
  3. Apply DigitPosConditionNormalizer::normalize() to break condition AST
  4. Use normalized AST in subsequent condition lowering

Code Position (around line 331):

ConditionPromotionResult::Promoted {
    carrier_info: promoted_carrier,
    promoted_var,
    carrier_name,
} => {
    // ... existing merge logic ...

    // Phase 224-E: Normalize digit_pos condition before lowering
    let normalized_break_condition = DigitPosConditionNormalizer::normalize(
        &break_condition_node,
        &promoted_var,
        &carrier_name,
    );

    // Use normalized_break_condition in subsequent processing
}

Testing Strategy

Unit Tests (3-4 tests)

  1. Happy path: digit_pos < 0!is_digit_pos
  2. Wrong operator: digit_pos >= 0 → No change
  3. Wrong variable: other_var < 0 → No change
  4. Wrong constant: digit_pos < 10 → No change

E2E Test

Test File: apps/tests/phase2235_p2_digit_pos_min.hako このテストは digit_pos 昇格と型整合性・SSA 安定性を確認するインフラ用途で、数値としての戻り値の意味論は今後の JsonParser 本体フェーズで定義する予定だよ。

Success Criteria:

  • No type error ("unsupported compare Lt on Bool and Integer")
  • Break condition correctly evaluates
  • Loop exits when digit not found

Debug Command:

NYASH_JOINIR_DEBUG=1 ./target/release/hakorune apps/tests/phase2235_p2_digit_pos_min.hako

Regression Tests

  • Verify existing Trim tests still pass
  • Verify skip_whitespace tests still pass
  • Check that 877/884 test count is maintained

Success Criteria

  1. cargo build --release succeeds
  2. Unit tests (3-4) pass
  3. Type error eliminated from phase2235_p2_digit_pos_min.hako
  4. Existing 877/884 tests remain passing
  5. No regressions in Trim/skip_whitespace patterns

Box-First Principles

Single Responsibility

  • DigitPosConditionNormalizer only handles AST transformation
  • No side effects, no state mutation
  • Pure pattern matching function

Fail-Safe Design

  • Non-matching patterns returned unchanged
  • No panics, no errors
  • Conservative transformation strategy

Boundary Clarity

  • Input: AST + promoted_var + carrier_name
  • Output: Transformed or original AST
  • Clear interface contract

Future Extensions

Pattern 4 Support: When Pattern 4 (continue) needs similar normalization, the same box can be reused with continue condition AST.

Other Comparison Operators: Currently handles < 0. Could extend to:

  • >= 0is_digit_pos (no NOT)
  • != -1is_digit_pos
  • == -1!is_digit_pos

Multiple Conditions: For complex boolean expressions with multiple promoted variables, apply normalization recursively.

References

  • Phase 228-8: DigitPos promotion implementation
  • Phase 229: Dynamic condition variable resolution
  • CarrierInfo: src/mir/join_ir/lowering/carrier_info.rs
  • ConditionEnv: src/mir/join_ir/lowering/condition_env.rs Status: Active
    Scope: digitpos condition 正規化ExprLowerer ライン)