Files
hakorune/docs/private/roadmap/phases/phase-32/PLAN.md

788 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 32: Hakorune Static Analyzer - 実装計画(改訂版)
**期間**: 2025-10-20 → 2025-10-27 (7日間)
**配置**: selfhost/analyzer/Ring-1: Meta/自己ホスト)
**構造**: 責務分離driver/ast_scan/mir_scan/report
---
## Day 1: スケルトン作成 + README (2025-10-20)
### 🎯 目標
- ディレクトリ構造作成
- 4つのBoxスケルトン
- hako_module.toml設定
- README.md責務/入出力/ENV
### タスク
#### ディレクトリ作成 (10分)
```bash
mkdir -p selfhost/analyzer/{boxes,smokes}
```
#### hako_module.toml作成 (20分)
```toml
# selfhost/analyzer/hako_module.toml
[package]
name = "selfhost.analyzer"
version = "0.1.0"
[exports]
driver = "boxes/driver_box.hako"
ast_scan = "boxes/ast_scan_box.hako"
mir_scan = "boxes/mir_scan_box.hako"
report = "boxes/report_box.hako"
[dependencies]
BoxHelpers = "selfhost.shared.box_helpers"
MirSchema = "selfhost.shared.mir.schema"
JsonEmit = "selfhost.shared.json.emit"
```
#### driver_box.hako スケルトン (30分)
```hako
# selfhost/analyzer/boxes/driver_box.hako
using selfhost.analyzer.mir_scan as MirScanBox
using selfhost.analyzer.report as ReportBox
static box DriverBox {
birth() {
# 空のbirthstatic boxなので状態なし
}
# 文字列入力→文字列出力
run_from_string(input, kind) {
# TODO: Day 2-3で実装
return "[DriverBox] Not implemented yet"
}
# MapBox入力→MapBox出力
run_from_box(input, kind) {
# TODO: Day 2-3で実装
local result
result = new MapBox()
return result
}
}
```
#### mir_scan_box.hako スケルトン (30分)
```hako
# selfhost/analyzer/boxes/mir_scan_box.hako
using selfhost.shared.box_helpers as BoxHelpers
using selfhost.shared.mir.schema as MirSchema
static box MirScanBox {
birth() {
# 空のbirth
}
# MIR JSON → 関数リスト抽出
extract_functions(mir_json) {
# TODO: Day 2で実装
local result
result = new ArrayBox()
return result
}
# 呼び出しグラフ構築
build_call_graph(mir_json) {
# TODO: Day 3で実装
local result
result = new MapBox()
return result
}
# BFS到達可能性分析
find_dead_code(call_graph, entry_points) {
# TODO: Day 4で実装
local result
result = new ArrayBox()
return result
}
}
```
#### report_box.hako スケルトン (20分)
```hako
# selfhost/analyzer/boxes/report_box.hako
static box ReportBox {
birth() {
# 空のbirth
}
# 診断サマリ文字列生成
generate_text_report(dead_functions, all_functions) {
# TODO: Day 5で実装
return "[ReportBox] Analysis complete"
}
# MapBox診断生成
generate_map_report(dead_functions, reachable_functions) {
# TODO: Day 5で実装
local result
result = new MapBox()
return result
}
}
```
#### ast_scan_box.hako スケルトン (10分)
```hako
# selfhost/analyzer/boxes/ast_scan_box.hako
static box AstScanBox {
birth() {
# 空のbirth
}
# AST走査Phase 32外、将来実装
scan(ast_json) {
return "[AstScanBox] Not implemented in Phase 32"
}
}
```
#### README.md作成 (40分)
```markdown
# selfhost/analyzer - Hakorune Static Analyzer
## 責務
HakoruneでHakoruneコードを解析する静的解析ツールPhase 32: デッドコード検出MVP
## 入出力
### 入力
- MIR JSON文字列Gate-C Strict前提
- またはMapBoxMIR構造
### 出力
- テキストレポート(診断サマリ)
- またはMapBox構造化診断
## ENV変数
```bash
# 追加チェックON
NYASH_ANALYZER_STRICT=1
# 将来の切替(既定=mir
HAKO_ANALYZER_ENTRY=ast|mir
# Gate-C/AST Strict既定ON
NYASH_GATE_C_STRICT=1
NYASH_AST_STRICT=1
```
## 非対象
- 外部I/Oファイル読み書き
- プラグイン化当面VM経由のみ
- CLI将来tools/に追加可能)
## 使用例
```hako
using selfhost.analyzer.driver as DriverBox
local mir_json = "{ ... }" // MIR JSON文字列
local result = DriverBox.run_from_string(mir_json, "mir")
print(result)
```
```
### 成果物
- ✅ selfhost/analyzer/hako_module.toml: 20行
- ✅ driver_box.hako: 30行
- ✅ mir_scan_box.hako: 40行
- ✅ report_box.hako: 25行
- ✅ ast_scan_box.hako: 15行
- ✅ README.md: 50行
- **合計**: 180行
### 完了基準
- [ ] 全ファイル作成完了
- [ ] hako_module.toml が正しく読み込まれる
- [ ] スケルトンBoxがインポート可能
---
## Day 2: MIR走査基盤関数抽出 (2025-10-21)
### 🎯 目標
- extract_functions 実装
- MIR JSON → 関数リスト変換
- 基本的な走査パターン確立
### タスク
#### extract_functions 実装 (2時間)
```hako
# selfhost/analyzer/boxes/mir_scan_box.hako
extract_functions(mir_json) {
local functions_key
functions_key = "functions"
local has_key
has_key = mir_json.has(functions_key)
if has_key == 0 {
print("[MirScanBox] ERROR: Missing 'functions' key")
return null
}
local functions_array
functions_array = mir_json.get(functions_key)
local result
result = new ArrayBox()
local i
i = 0
loop(i < functions_array.size()) {
local func
func = functions_array.get(i)
local name
name = me.extract_function_name(func)
if name != null {
result.push(name)
}
i = i + 1
}
return result
}
extract_function_name(func_obj) {
local name_key
name_key = "name"
local has_name
has_name = func_obj.has(name_key)
if has_name == 0 {
return null
}
local name
name = func_obj.get(name_key)
local arity_key
arity_key = "arity"
local arity
arity = func_obj.get(arity_key)
# "Main.main/0" 形式
local slash
slash = "/"
local arity_str
arity_str = arity.to_string()
local qualified
qualified = name.concat(slash)
qualified = qualified.concat(arity_str)
return qualified
}
```
#### テストケース作成 (1時間)
- 簡単なMIR JSON2関数
- 関数リスト抽出確認
### 成果物
- ✅ mir_scan_box.hako拡張: 100行
- ✅ テストケース: 50行
- **合計**: 150行
### 完了基準
- [ ] extract_functions が動作
- [ ] 実際のMIR JSONから関数リスト取得可能
---
## Day 3: 呼び出しグラフ構築 (2025-10-22)
### 🎯 目標
- build_call_graph 実装
- mir_call命令から呼び出し先抽出
- caller → [callees] マップ生成
### タスク
#### build_call_graph 実装 (3時間)
- 各関数のinstructionsをスキャン
- mir_call命令のcalleeを抽出
- MapBoxcaller → ArrayBox<callee>)構築
#### Callee型パース実装 (1時間)
```hako
parse_callee(callee_obj) {
# "function:Main.helper/1"
# "extern:env.console.log"
# "constructor:ArrayBox"
local callee_type
callee_type = callee_obj.get("type")
if callee_type == "ModuleFunction" {
local module
module = callee_obj.get("module")
local func
func = callee_obj.get("function")
local arity
arity = callee_obj.get("arity")
local dot
dot = "."
local slash
slash = "/"
local result
result = module.concat(dot)
result = result.concat(func)
result = result.concat(slash)
local arity_str
arity_str = arity.to_string()
result = result.concat(arity_str)
return result
}
# TODO: Constructor/Extern対応
return null
}
```
### 成果物
- ✅ mir_scan_box.hako拡張: 150行
- ✅ テストケース: 100行
- **合計**: 250行
### 完了基準
- [ ] build_call_graph が動作
- [ ] 実際のMIR JSONから呼び出しグラフ生成可能
---
## Day 4: BFS到達可能性分析 (2025-10-23)
### 🎯 目標
- find_dead_code 実装
- BFS幅優先探索アルゴリズム
- デッドコード検出
### タスク
#### find_dead_code 実装 (2時間)
```hako
find_dead_code(call_graph, entry_points) {
local reachable
reachable = me.mark_reachable(call_graph, entry_points)
local all_funcs
all_funcs = call_graph.keys()
local dead
dead = new ArrayBox()
local i
i = 0
loop(i < all_funcs.size()) {
local func
func = all_funcs.get(i)
local is_reachable
is_reachable = reachable.has(func)
if is_reachable == 0 {
dead.push(func)
}
i = i + 1
}
return dead
}
mark_reachable(graph, entry_points) {
local visited
visited = new MapBox() # Set代わり
local queue
queue = new ArrayBox() # 簡易キュー
# エントリポイント追加
local i
i = 0
loop(i < entry_points.size()) {
local entry
entry = entry_points.get(i)
queue.push(entry)
i = i + 1
}
# BFS
loop(queue.size() > 0) {
local current
current = queue.get(0)
# 簡易shift先頭削除の代わりに新しいArrayBoxを作る
local new_queue
new_queue = new ArrayBox()
local j
j = 1
loop(j < queue.size()) {
local item
item = queue.get(j)
new_queue.push(item)
j = j + 1
}
queue = new_queue
local already_visited
already_visited = visited.has(current)
if already_visited == 1 {
continue # ✅ Phase 12.7で実装済み
}
visited.set(current, 1)
local callees
callees = graph.get(current)
if callees != null {
local ci
ci = 0
loop(ci < callees.size()) {
local callee
callee = callees.get(ci)
local callee_visited
callee_visited = visited.has(callee)
if callee_visited == 0 {
queue.push(callee)
}
ci = ci + 1
}
}
}
return visited
}
```
### 成果物
- ✅ mir_scan_box.hako完成: 200行
- ✅ テストケース: 100行
- **合計**: 300行
### 完了基準
- [ ] find_dead_code が動作
- [ ] BFS正確性確認既知デッドコード検出
---
## Day 5: レポート生成 (2025-10-24)
### 🎯 目標
- generate_text_report 実装
- generate_map_report 実装
- 人間可読・機械可読両対応
### タスク
#### generate_text_report 実装 (1.5時間)
```hako
generate_text_report(dead_functions, all_functions) {
local header
header = "Hakorune Dead Code Analyzer\n"
local separator
separator = "=============================\n"
local result
result = header.concat(separator)
local all_count
all_count = all_functions.size()
local all_str
all_str = all_count.to_string()
local all_line
all_line = "Functions found: "
all_line = all_line.concat(all_str)
all_line = all_line.concat("\n")
result = result.concat(all_line)
local dead_count
dead_count = dead_functions.size()
local dead_str
dead_str = dead_count.to_string()
local dead_line
dead_line = "Dead functions: "
dead_line = dead_line.concat(dead_str)
dead_line = dead_line.concat("\n")
result = result.concat(dead_line)
# 個別リスト表示
local i
i = 0
loop(i < dead_functions.size()) {
local func
func = dead_functions.get(i)
local prefix
prefix = " [DEAD] "
local line
line = prefix.concat(func)
line = line.concat("\n")
result = result.concat(line)
i = i + 1
}
return result
}
```
#### generate_map_report 実装 (1時間)
```hako
generate_map_report(dead_functions, reachable_functions) {
local result
result = new MapBox()
result.set("dead_functions", dead_functions)
result.set("reachable_functions", reachable_functions)
local dead_count
dead_count = dead_functions.size()
result.set("dead_count", dead_count)
local reachable_count
reachable_count = reachable_functions.size()
result.set("reachable_count", reachable_count)
return result
}
```
### 成果物
- ✅ report_box.hako完成: 120行
- **合計**: 120行
### 完了基準
- [ ] レポートが見やすい
- [ ] 統計情報が正確
---
## Day 6: スモークテスト整備 (2025-10-25)
### 🎯 目標
- 3本のスモークテスト作成
- happy path 2本 + 負例1本
- quick-lite/quick/integrationプロファイル対応
### タスク
#### happy path スモーク1 (1時間)
```bash
# selfhost/analyzer/smokes/analyzer_mir_gatec_ok_vm.sh
#!/bin/bash
set -euo pipefail
# Gate-C MIR JSON入力正常系
MIR_JSON='{"functions":[{"name":"Main.main","arity":0,"blocks":[]}]}'
RESULT=$(NYASH_GATE_C_STRICT=1 ./target/release/hakorune -c "
using selfhost.analyzer.driver as DriverBox
local mir = \"$MIR_JSON\"
local result = DriverBox.run_from_string(mir, \"mir\")
print(result)
")
if echo "$RESULT" | grep -q "Functions found: 1"; then
echo "✅ PASS: analyzer_mir_gatec_ok_vm"
exit 0
else
echo "❌ FAIL: Expected 'Functions found: 1'"
exit 1
fi
```
#### 負例スモーク (1時間)
```bash
# selfhost/analyzer/smokes/analyzer_bad_mir_fail_vm.sh
#!/bin/bash
set -euo pipefail
# 不正MIR: 余剰キー/型不一致
BAD_MIR='{"functions":[], "extra_key": "should fail"}'
set +e
RESULT=$(NYASH_GATE_C_STRICT=1 ./target/release/hakorune -c "
using selfhost.analyzer.driver as DriverBox
local mir = \"$BAD_MIR\"
local result = DriverBox.run_from_string(mir, \"mir\")
print(result)
" 2>&1)
EXIT_CODE=$?
set -e
if [ $EXIT_CODE -ne 0 ]; then
echo "✅ PASS: analyzer_bad_mir_fail_vm (expected failure)"
exit 0
else
echo "❌ FAIL: Should have failed on bad MIR"
exit 1
fi
```
#### プロファイル統合 (1時間)
- quick-lite: 負例のみ
- quick: 全3本
- integration: セルフホスト解析追加
### 成果物
- ✅ スモークテスト3本: 150行
- ✅ プロファイル設定: 30行
- **合計**: 180行
### 完了基準
- [ ] 全スモークテストPASS
- [ ] quick-liteで負例Fail-Fast確認
---
## Day 7: セルフホスト解析 + ドキュメント (2025-10-26)
### 🎯 目標
- セルフホストコードでの実用検証
- 精度95%以上確認
- ドキュメント整備
### タスク
#### セルフホスト解析 (2時間)
```bash
# selfhost/compiler/builder/rewrite/known.hako を解析
NYASH_GATE_C_STRICT=1 ./target/release/hakorune --emit-mir-json /tmp/known.json \
apps/selfhost-compiler/builder/rewrite/known.hako
# Analyzerで解析
./target/release/hakorune -c "
using selfhost.analyzer.driver as DriverBox
# /tmp/known.json の内容を読み込み(簡易版)
local result = DriverBox.run_from_string(mir_json, \"mir\")
print(result)
"
```
#### ドキュメント整備 (1時間)
- Phase 32 README.md 更新(実装完了マーク)
- PLAN.md 実績LOC記録
#### 精度検証 (30分)
- 手動でデッドコード候補確認
- 偽陽性/偽陰性チェック
### 成果物
- ✅ 実用検証レポート: 50行
- ✅ ドキュメント更新
- **合計**: 50行
### 完了基準
- [ ] セルフホストコードで95%以上の精度
- [ ] ドキュメント完備
- [ ] 1,000行ファイルを5秒以内で解析
---
## 📊 進捗トラッキング
| Day | 計画LOC | 実績LOC | 状態 | 備考 |
|-----|--------|--------|------|------|
| Day 1 | 180 | - | 🟡 | スケルトン + README |
| Day 2 | 150 | - | 🟡 | MIR走査基盤 |
| Day 3 | 250 | - | 🟡 | 呼び出しグラフ |
| Day 4 | 300 | - | 🟡 | BFS到達可能性 |
| Day 5 | 120 | - | 🟡 | レポート生成 |
| Day 6 | 180 | - | 🟡 | スモークテスト |
| Day 7 | 50 | - | 🟡 | セルフホスト解析 |
| **Total** | **1,230** | **-** | | |
---
## 🎯 成功基準(最終確認)
- [ ] **精度**: >95%(セルフホストコードで手動検証)
- [ ] **再現率**: >90%(既知デッドコード検出)
- [ ] **処理速度**: <5秒1,000行ファイル
- [ ] **使いやすさ**: 1コマンドで実行可能
- [ ] **テストカバレッジ**: 3件のスモークテスト全PASS
---
## 🚨 リスク & 対策
### リスク1: selfhost/shared依存の複雑さ
- **対策**: Day 1でimport確認早期検証
- **バックアップ**: 簡易JSON解析を自前実装
### リスク2: BFS パフォーマンス
- **対策**: ArrayBox簡易キュー小規模グラフで十分
- **バックアップ**: 将来QueueBox追加可能
### リスク3: セルフホスト解析で想定外の問題
- **対策**: Day 7に余裕を持たせる
- **バックアップ**: 簡単な例で先に検証
---
**作成日**: 2025-10-19
**改訂日**: 2025-10-19構造改訂版
**開始予定**: 2025-10-20
**完了予定**: 2025-10-27