Files
hakorune/docs/archive/phases/phase-170-197/phase171_jsonparserbox_implementation.md
nyash-codex a7dbc15878 feat(joinir): Phase 240-EX - Pattern2 header condition ExprLowerer integration
Implementation:
- Add make_pattern2_scope_manager() helper for DRY
- Header conditions use ExprLowerer for supported patterns
- Legacy fallback for unsupported patterns
- Fail-Fast on supported patterns that fail

Tests:
- 4 new tests (all pass)
- test_expr_lowerer_supports_simple_header_condition_i_less_literal
- test_expr_lowerer_supports_header_condition_var_less_var
- test_expr_lowerer_header_condition_generates_expected_instructions
- test_pattern2_header_condition_via_exprlowerer

Also: Archive old phase documentation (34k lines removed)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 00:33:04 +09:00

14 KiB
Raw Blame History

Phase 171: JsonParserBox 実装 & hako_check への導入

0. ゴール

Phase 170 で決めた API 草案どおりに .hako 純正 JSON パーサ BoxJsonParserBoxを実装する。

目的:

  • JSON 文字列をメモリ内オブジェクトに変換する Box を実装
  • hako_check (HC020) で Phase 156 の手書き JSON パーサ289行を置き換え
  • 将来の selfhost/Stage-B/解析ツールから再利用可能に

1. 背景と戦略

Phase 156 の課題

  • hako_check の analysis_consumer.hako に 289行の手書き JSON パーサ
  • 他の HC ルールHC021+)でも JSON 解析が必要になる
  • 共通ライブラリ化が急務

Phase 170 での決定

  • API 草案: parse(), parse_object(), parse_array()
  • 型定義: JsonValueBoxunion 相当), JsonObjectBox, JsonArrayBox
  • MVP スコープ: MIR/CFG JSON のみ対応Program JSON v0 は Phase 172+

Phase 171 の戦略

  1. 最小MVP実装: MIR/CFG JSON が使う型のみ
  2. 段階的導入: hako_check (HC020) が最初のユーザー
  3. 共通ライブラリ化: tools/hako_shared/ で shared 設計

2. Scope / Non-scope

やること

  1. JsonParserBox の実装

    • parse(json_str) -> JsonValue?
    • parse_object(json_str) -> JsonObjectBox?
    • parse_array(json_str) -> JsonArrayBox?
  2. 内部型の定義

    • JsonValueBoxkind で型判定)
    • JsonObjectBoxkey-value map
    • JsonArrayBoxvalue list
  3. エスケープシーケンス対応

    • MIR JSON が実際に使う範囲:\", \\, \n, \t など
    • Unicode (\uXXXX) は Phase 172+
  4. hako_check への導入

    • analysis_consumer.hako の 289行手書きパーサを削除
    • JsonParserBox を使用した 10行規模実装に置き換え
    • HC019/HC020 の出力が変わらないことを確認
  5. テスト

    • JsonParserBox 単体テスト(正常系・エラー系)
    • hako_check スモークテスト(回帰なし)

やらないこと

  • to_json() / serializationPhase 172+
  • スキーマ検証Phase 172+
  • Program JSON v0 対応Phase 172+
  • ストリーミングパーサPhase 173+

3. Task 1: 設計ドキュメントの確認・MVP の切り出し

やること

  1. Phase 170 設計の再確認

    • docs/private/roadmap2/phases/phase-170-hako-json-library/README.md を読む
    • API 草案を確認parse, parse_object, parse_array
    • 型定義案を確認JsonValueBox, JsonObjectBox, JsonArrayBox
  2. MVP スコープの確定

    • MIR/CFG JSON のサブセット:
      • 型: null, bool, number, string, object, array
      • number: 整数のみ(浮動小数点は未対応)
      • string: 基本的なエスケープのみ
      • object: キーは文字列固定
    • エラーハンドリング: null を返す
  3. 実装方針の決定

    • Phase 156 の手書きパーサを参照しつつ
    • StringBox.substring/indexOf で文字列走査
    • 状態機械を最小化(シンプル > 最適化)

成果物

  • MVP の詳細仕様書Task 2 への引き継ぎ)

4. Task 2: JsonParserBox の実装 (.hako)

ファイル位置

tools/hako_shared/
├── json_parser.hako      ← 新規作成
└── tests/
    └── json_parser_test.hako  ← 新規作成

やること

  1. Box 構造の定義
// JsonValueBox: union 相当の型
static box JsonValueBox {
  kind: StringBox           # "null", "bool", "number", "string", "array", "object"
  boolVal: BoolBox
  numVal: IntegerBox
  strVal: StringBox
  arrVal: JsonArrayBox
  objVal: JsonObjectBox

  method is_null() { return me.kind == "null" }
  method is_bool() { return me.kind == "bool" }
  method is_number() { return me.kind == "number" }
  method is_string() { return me.kind == "string" }
  method is_array() { return me.kind == "array" }
  method is_object() { return me.kind == "object" }
}

// JsonObjectBox: key-value map
static box JsonObjectBox {
  pairs: ArrayBox   # [{"key": String, "value": JsonValue}, ...]

  method get(key: String) -> JsonValue? { ... }
  method keys() -> ArrayBox { ... }
  method values() -> ArrayBox { ... }
}

// JsonArrayBox: value list
static box JsonArrayBox {
  elements: ArrayBox    # [JsonValue, ...]

  method get(index: Integer) -> JsonValue? { ... }
  method length() -> Integer { ... }
}

// JsonParserBox: メインパーサ
static box JsonParserBox {
  method parse(json_str: String) -> JsonValue? { ... }
  method parse_object(json_str: String) -> JsonObjectBox? { ... }
  method parse_array(json_str: String) -> JsonArrayBox? { ... }
}
  1. JSON パーサ実装
static box JsonParserBox {
  method parse(json_str: String) -> JsonValue? {
    // 前後の空白を削除
    local s = me._trim(json_str)
    if s.length() == 0 { return null }

    local result = null
    local pos = me._parse_value(s, 0, result)
    if pos < 0 { return null }

    return result
  }

  method parse_object(json_str: String) -> JsonObjectBox? {
    local val = me.parse(json_str)
    if val == null or not val.is_object() { return null }
    return val.objVal
  }

  method parse_array(json_str: String) -> JsonArrayBox? {
    local val = me.parse(json_str)
    if val == null or not val.is_array() { return null }
    return val.arrVal
  }

  // 内部ヘルパー
  method _parse_value(s: String, pos: Integer, out: JsonValue) -> Integer { ... }
  method _parse_null(s: String, pos: Integer, out: JsonValue) -> Integer { ... }
  method _parse_bool(s: String, pos: Integer, out: JsonValue) -> Integer { ... }
  method _parse_number(s: String, pos: Integer, out: JsonValue) -> Integer { ... }
  method _parse_string(s: String, pos: Integer, out: JsonValue) -> Integer { ... }
  method _parse_array(s: String, pos: Integer, out: JsonValue) -> Integer { ... }
  method _parse_object(s: String, pos: Integer, out: JsonValue) -> Integer { ... }
  method _skip_whitespace(s: String, pos: Integer) -> Integer { ... }
  method _trim(s: String) -> String { ... }
  method _unescape_string(s: String) -> String { ... }
}
  1. エスケープシーケンス対応

MIR JSON で使われるもの:

  • \""
  • \\\
  • \n → newline
  • \t → tab
  • \r → carriage return
  • \b, \f → 対応可Phase 171
  • \uXXXX → 未対応Phase 172+

成果物

  • JsonParserBox 実装約300-400行推定
  • 単体テストケース

5. Task 3: hako_check (HC020) から JsonParserBox を使うように置き換え

対象ファイル

  • tools/hako_check/analysis_consumer.hako (Lines 206-494: 手書きパーサ)
  • tools/hako_check/rules/rule_dead_blocks.hako

やること

  1. JSON パーサ部分の削除

    • analysis_consumer.hako の手書きパーサ関数を全削除(~289行
    • 代わりに JsonParserBox を import
  2. CFG 処理の修正

// 修正前Phase 156
local mir_json_text = ir.get("_mir_json_text")
local cfg_obj = me._parse_json_object(mir_json_text)  // 手写

// 修正後Phase 171
local mir_json_text = ir.get("_mir_json_text")
local cfg_val = JsonParserBox.parse(mir_json_text)
local cfg_obj = cfg_val.objVal  // JsonObjectBox を取得
  1. HC020 ルール側の確認

    • rule_dead_blocks.hako が CFG を正しく解析できるか確認
    • JsonObjectBox/.get() メソッドとの互換性確認
  2. 目標

    • analysis_consumer.hako の行数を 500+ → 210 に削減約60%削減)
    • Phase 156 の 289行手書きパーサの削除

成果物

  • 修正済み analysis_consumer.hako
  • 修正済み tools/hako_check/ 関連ファイル

6. Task 4: 単体テスト & スモークテスト

JsonParserBox 単体テスト

新規作成: tools/hako_shared/tests/json_parser_test.hako

テストケース:

static box JsonParserTest {
  main() {
    me.test_null()
    me.test_bool()
    me.test_number()
    me.test_string()
    me.test_array()
    me.test_object()
    me.test_error_cases()
    print("All tests passed!")
  }

  test_null() {
    local val = JsonParserBox.parse("null")
    assert(val != null && val.is_null())
  }

  test_bool() {
    local t = JsonParserBox.parse("true")
    local f = JsonParserBox.parse("false")
    assert(t.is_bool() && t.boolVal == true)
    assert(f.is_bool() && f.boolVal == false)
  }

  test_number() {
    local n = JsonParserBox.parse("123")
    assert(n.is_number() && n.numVal == 123)
  }

  test_string() {
    local s = JsonParserBox.parse('"hello"')
    assert(s.is_string() && s.strVal == "hello")
  }

  test_array() {
    local arr = JsonParserBox.parse("[1, 2, 3]")
    assert(arr.is_array() && arr.arrVal.length() == 3)
  }

  test_object() {
    local obj = JsonParserBox.parse('{"key": "value"}')
    assert(obj.is_object())
    local val = obj.objVal.get("key")
    assert(val != null && val.is_string())
  }

  test_error_cases() {
    local inv1 = JsonParserBox.parse("{")
    assert(inv1 == null)

    local inv2 = JsonParserBox.parse("[1,2,]")
    assert(inv2 == null)
  }
}

hako_check スモークテスト

既存のスモークテストを実行:

# HC020 スモーク
./tools/hako_check_deadblocks_smoke.sh

# HC019 スモーク(回帰なし)
./tools/hako_check_deadcode_smoke.sh

期待:

  • HC020 出力が Phase 156 と変わらない
  • HC019 出力が変わらない
  • エラーが出ない

成果物

  • JsonParserBox テストファイル
  • スモークテスト成功確認

7. Task 5: ドキュメント & CURRENT_TASK 更新

ドキュメント更新

  1. phase170_hako_json_library_design.md に追記

    ## Phase 171 実装結果
    
    ✅ JsonParserBox 実装完了
    - API: parse(), parse_object(), parse_array()
    - サポート型: null, bool, number, string, array, object
    - エスケープ: \", \\, \n, \t, \r, \b, \f
    
    ✅ hako_check への導入完了
    - analysis_consumer.hako: 500+ → 210 行(~60%削減)
    - Phase 156 の 289行手書きパーサを削除
    - HC020 が JsonParserBox を使用
    
    📊 削減実績:
    - hako_check パーサ: 289行 → JsonParserBox 呼び出し(~10行
    - 共通ライブラリ化: hako_check/selfhost/ツールから再利用可能
    
  2. hako_check_design.md を更新

    ### JSON 解析
    
    - Phase 156 まで: analysis_consumer.hako に手書きパーサ289行
    - Phase 171 から: JsonParserBox を使用
    - 場所: tools/hako_shared/json_parser.hako
    
  3. CURRENT_TASK.md に Phase 171 セクション追加

    ### Phase 171: JsonParserBox 実装 & hako_check 導入 ✅
    
    **完了内容**:
    - JsonParserBox 実装tools/hako_shared/json_parser.hako
    - hako_check (HC020) が JsonParserBox を使用
    - 手書きパーサ 289行 → 共通ライブラリに統合
    
    **成果**:
    - hako_check 行数削減: 60% (500+ → 210 行)
    - 再利用性: HC021+, selfhost, Stage-B で活用可
    - 箱化: .hako 純正 JSON パーサ Box 確立
    
    **次フェーズ**: Phase 172 で selfhost/Stage-B への導入予定
    

git commit

feat(hako_check): Phase 171 JsonParserBox implementation

✨ .hako 純正 JSON パーサ Box 実装完了!

🎯 実装内容:
- JsonParserBox: parse/parse_object/parse_array メソッド
- JsonValueBox, JsonObjectBox, JsonArrayBox 型定義
- エスケープシーケンス対応(\", \\, \n, \t など)

📊 hako_check 統合:
- analysis_consumer.hako: 289行手書きパーサを削除
- JsonParserBox を使用した軽量実装に置き換え
- HC019/HC020 の出力は変わらず(回帰なし)

✅ テスト:
- JsonParserBox 単体テスト全 PASS
- hako_check スモークテスト全 PASS
- Phase 156 との後方互換性確認

🏗️ 次ステップ:
- Phase 172: selfhost/Stage-B への導入
- Phase 172+: Program JSON v0 対応
- Phase 173+: to_json() 逆変換実装

完成チェックリストPhase 171

  • Task 1: 設計ドキュメント確認・MVP 切り出し
    • Phase 170 設計再確認
    • MVP スコープ確定
    • 実装方針決定
  • Task 2: JsonParserBox 実装
    • Box 構造定義
    • パーサ実装
    • エスケープシーケンス対応
  • Task 3: hako_check への導入
    • analysis_consumer.hako 修正
    • 手書きパーサ削除
    • 行数削減確認500+ → 210
  • Task 4: テスト & スモーク
    • JsonParserBox 単体テスト
    • hako_check スモークテスト
    • 回帰なし確認
  • Task 5: ドキュメント更新
    • phase170 に追記
    • hako_check_design.md 更新
    • CURRENT_TASK.md 追加
    • git commit

技術的ポイント

JSON 解析の状態機械

parse_value:
  | "null" → parse_null
  | "true"|"false" → parse_bool
  | digit → parse_number
  | '"' → parse_string
  | '[' → parse_array
  | '{' → parse_object
  | else → error (null)

ArrayBox / MapBox との互換性

// JsonArrayBox.get() は ArrayBox.get() と同じ感覚で
local elem = json_array.get(0)

// JsonObjectBox.get() は MapBox.get() と同じ感覚で
local val = json_obj.get("key")

エラーハンドリング

  • 構文エラー → null を返すNyash 的な nil
  • 期待値と異なる型 → null を返す

パフォーマンス

Phase 171 MVP は正確性 > 速度を優先。最適化は Phase 173+ へ。


作成日: 2025-12-04 Phase: 171JsonParserBox 実装 & hako_check 導入) 予定工数: 3-4 時間 難易度: 中JSON パーサ実装 + .hako での Box 設計) 期待削減: hako_check 行数 60%、コード共通化 100% Status: Historical