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

375 lines
11 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 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開始