Files
hakorune/docs/private/roadmap/phases/phase-20-macro-full-features/MIGRATION.md

375 lines
11 KiB
Markdown
Raw Normal View History

# Phase 20: Rust → Hakorune マクロ移行ガイド
Date: 2025-10-06
Status: **REFERENCE** - Phase 20実装時に参照
## 🎯 **移行の目的**
Phase 16で実装したRust版マクロシステムの主要機能を、
Hakoruneでセルフホスト実装に書き直す。
## 📋 **移行対象機能一覧**
### **優先度 HIGH: 即座に便利**
| 機能 | Rust実装場所 | 移行先(案) | 難易度 |
|------|-------------|-------------|--------|
| @derive(Equals) | `src/macro/engine.rs:151-174` | `apps/macros/derive/derive_equals.hako` | ⭐⭐ |
| @derive(ToString) | `src/macro/engine.rs:176-194` | `apps/macros/derive/derive_tostring.hako` | ⭐⭐ |
| @test ランナー | `src/macro/mod.rs:61-401` | `apps/macros/test/test_runner.hako` | ⭐⭐⭐ |
### **優先度 MEDIUM: 複雑なマクロ用基盤**
| 機能 | Rust実装場所 | 移行先(案) | 難易度 |
|------|-------------|-------------|--------|
| TemplatePattern | `src/macro/pattern.rs:116-238` | `apps/macros/pattern/template_pattern.hako` | ⭐⭐⭐⭐ |
| Quote/Unquote | `src/macro/pattern.rs:9-114` | `apps/macros/quote/quote_system.hako` | ⭐⭐⭐⭐ |
| MacroEngine | `src/macro/engine.rs:11-101` | `apps/macros/engine/macro_engine.hako` | ⭐⭐⭐ |
### **優先度 LOW: 構文サポートParser側**
| 機能 | Rust実装場所 | 移行判断 |
|------|-------------|----------|
| Match式構文 | `src/parser/expr/match_expr.rs` | **移行不要**Rust Parserのまま維持 |
| PatternAst | `src/ast.rs` | **移行不要**AST定義はRust |
## 🔄 **移行戦略: 3つのアプローチ**
### **Strategy 1: JSON AST直接操作推奨**
**使用場面**: @derive, @test
**難易度**: ⭐⭐
```hakorune
// apps/macros/derive/derive_equals.hako
static box DeriveEqualsMacro {
expand(json_ast, ctx) {
// 1. JSON文字列をパース
local ast, box_decl, fields, new_method
ast = JSON.parse(json_ast)
// 2. BoxDeclaration検出
if ast.kind != "Program" {
return json_ast
}
// 3. public_fieldsを取得
fields = box_decl.public_fields
// 4. equals()メソッドJSON生成
new_method = me.build_equals_method(fields)
// 5. methodsに追加
box_decl.methods.set("equals", new_method)
// 6. JSON文字列化
return JSON.stringify(ast)
}
build_equals_method(fields) {
// JSON AST構築詳細は後述
return method_json
}
}
```
**利点**:
- ✅ 現在の暫定実装JSON文字列操作の延長
- ✅ Hakoruneの機能だけで完結
- ✅ バグが少ない
**欠点**:
- ❌ JSON操作が冗長
- ❌ 複雑なマクロは困難
### **Strategy 2: AstBuilderBox実装中間層**
**使用場面**: Pattern Matching, Quote/Unquote
**難易度**: ⭐⭐⭐⭐
```hakorune
// apps/macros/ast/ast_builder.hako
box AstBuilderBox {
// JSON ASTをHakoruneオブジェクトにラップ
root: MapBox
birth(json_str) {
from MapBox.birth()
me.root = JSON.parse(json_str)
}
// ヘルパーメソッド
find_box_decl(name) {
// AST走査してBoxDeclaration検出
}
add_method(box_name, method_name, body_ast) {
// メソッド追加
}
to_json() {
return JSON.stringify(me.root)
}
}
```
**利点**:
- ✅ API設計でRust版に近い書き方が可能
- ✅ 複雑なマクロ実装が楽
**欠点**:
- ❌ AstBuilderBox実装コストが大きい
- ❌ JSON↔オブジェクト変換オーバーヘッド
### **Strategy 3: Rust Hybrid非推奨**
Rust側にヘルパー関数を残し、Hakoruneから呼ぶ。
**使用場面**: なし(セルフホスティングの意義を損なう)
**難易度**: ⭐
**推奨しない理由**:
- Phase 20の目的は「Rust依存を減らす」こと
- ハイブリッドは複雑性が増すだけ
## 📝 **移行実装例: @derive(Equals)**
### **Rust版参考**
```rust
// src/macro/engine.rs:151-174
fn build_equals_method(_box_name: &str, fields: &Vec<String>) -> ASTNode {
let cond = if fields.is_empty() {
ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown() }
} else {
let mut it = fields.iter();
let first = it.next().unwrap();
let mut expr = bin_eq(me_field(first), var_field("__ny_other", first));
for f in it {
expr = bin_and(expr, bin_eq(me_field(f), var_field("__ny_other", f)));
}
expr
};
ASTNode::FunctionDeclaration {
name: "equals".to_string(),
params: vec!["__ny_other".to_string()],
body: vec![ASTNode::Return { value: Some(Box::new(cond)), span: Span::unknown() }],
is_static: false,
is_override: false,
span: Span::unknown(),
}
}
```
### **Hakorune版Strategy 1: JSON直接操作**
```hakorune
// apps/macros/derive/derive_equals.hako
static box DeriveEqualsMacro {
expand(json_str, ctx) {
local ast, stmts, i, modified
ast = JSON.parse(json_str)
if ast.kind != "Program" {
return json_str
}
stmts = ast.statements
modified = false
i = 0
loop(i < stmts.length()) {
local stmt
stmt = stmts.get(i)
if stmt.kind == "BoxDeclaration" {
// public_fieldsチェック
if stmt.public_fields && stmt.public_fields.length() > 0 {
// equals()メソッドが存在しないか確認
if !stmt.methods || !stmt.methods.has("equals") {
// equals()生成
local equals_method
equals_method = me.build_equals_json(stmt.name, stmt.public_fields)
// methodsに追加
if !stmt.methods {
stmt.methods = json({})
}
stmt.methods.set("equals", equals_method)
modified = true
}
}
}
i = i + 1
}
if modified {
return JSON.stringify(ast)
} else {
return json_str
}
}
build_equals_json(box_name, fields) {
// JSON AST for: equals(__ny_other) { return me.f1 == __ny_other.f1 && ... }
local body, cond, i
if fields.length() == 0 {
cond = json({
"kind": "Literal",
"value": true,
"span": json({})
})
} else {
i = 0
cond = null
loop(i < fields.length()) {
local field_name, eq_expr
field_name = fields.get(i)
// me.field == __ny_other.field
eq_expr = json({
"kind": "BinaryOp",
"operator": "Equal",
"left": json({
"kind": "FieldAccess",
"object": json({ "kind": "Me", "span": json({}) }),
"field": field_name,
"span": json({})
}),
"right": json({
"kind": "FieldAccess",
"object": json({ "kind": "Variable", "name": "__ny_other", "span": json({}) }),
"field": field_name,
"span": json({})
}),
"span": json({})
})
if !cond {
cond = eq_expr
} else {
// cond && eq_expr
cond = json({
"kind": "BinaryOp",
"operator": "And",
"left": cond,
"right": eq_expr,
"span": json({})
})
}
i = i + 1
}
}
// FunctionDeclaration
return json({
"kind": "FunctionDeclaration",
"name": "equals",
"params": arr(["__ny_other"]),
"body": arr([
json({
"kind": "Return",
"value": cond,
"span": json({})
})
]),
"is_static": false,
"is_override": false,
"span": json({})
})
}
}
```
## 🧪 **テスト戦略**
### **1. ユニットテストHakorune版**
```hakorune
// apps/macros/derive/test_derive_equals.hako
static box TestDeriveEquals {
test_simple_box() {
local input, expected, actual
input = json({
"kind": "Program",
"statements": arr([
json({
"kind": "BoxDeclaration",
"name": "Person",
"public_fields": arr(["name", "age"]),
"methods": json({})
})
])
})
actual = DeriveEqualsMacro.expand(JSON.stringify(input), "{}")
// equals()メソッドが追加されていることを確認
local result
result = JSON.parse(actual)
assert(result.statements.get(0).methods.has("equals"))
return true
}
}
```
### **2. 統合テスト(スモークテスト)**
```bash
# tools/smokes/v2/profiles/quick/macro/derive_equals_vm.sh
#!/usr/bin/env bash
# Smoke: @derive(Equals) basic functionality
NYASH_MACRO_ENABLE=1 \
NYASH_MACRO_PATHS=apps/macros/derive/derive_equals.hako \
./target/release/hako apps/tests/macro_derive_person.hako
```
### **3. パリティテストRust版 vs Hakorune版**
```bash
# 同じ入力に対して同じ出力を生成するか確認
diff <(NYASH_MACRO_DERIVE_BACKEND=rust ./hako test.hako --dump-ast) \
<(NYASH_MACRO_DERIVE_BACKEND=hako ./hako test.hako --dump-ast)
```
## 📊 **進捗管理**
### **Phase 20.1: @derive(Equals)実装**
- [ ] DeriveEqualsMacro.build_equals_json() 実装
- [ ] ユニットテスト作成・通過
- [ ] スモークテスト作成・通過
- [ ] パリティテスト通過Rust版と同一出力
- [ ] ドキュメント作成
### **Phase 20.2: @derive拡張**
- [ ] DeriveToStringMacro 実装
- [ ] DeriveCloneMacro 実装
- [ ] DeriveDebugMacro 実装
### **Phase 20.3: @test ランナー**
- [ ] TestCollectorBox 実装
- [ ] TestRunnerBox 実装
- [ ] JSON引数注入対応
## 🚨 **リスク管理**
### **Risk 1: JSON操作の複雑性**
**対策**: AstBuilderBoxヘルパーを段階的に実装
### **Risk 2: パフォーマンス劣化**
**対策**: ベンチマーク必須(目標: Rust版の2倍以内
### **Risk 3: バグ再発**
**対策**: Rust版の既知バグを事前に洗い出し、テストケース化
## 🔗 **参考資料**
- [Phase 16 README](../phase-16-macro-revolution/README.md) - Rust版設計書
- [Phase 16 IMPLEMENTATION](../phase-16-macro-revolution/IMPLEMENTATION.md) - Rust版実装ガイド
- `src/macro/engine.rs` - derive実装参考
- `src/macro/pattern.rs` - Pattern/Quote実装参考
- `src/macro/mod.rs` - @test実装参考
---
**Next**: Phase 15.7完了後、Phase 20.1開始