diff --git a/docs/CURRENT_TASK.md b/docs/CURRENT_TASK.md index a0a9d652..0c9fffb6 100644 --- a/docs/CURRENT_TASK.md +++ b/docs/CURRENT_TASK.md @@ -1,4 +1,4 @@ -# 🎯 現在のタスク (2025-08-19 更新) +# 🎯 現在のタスク (2025-08-20 更新) ## 🏆 **LEGENDARY SUCCESS! birth構文革命 + デリゲーション完全勝利!** @@ -167,10 +167,70 @@ fn unwrap_instance(boxed: &dyn NyashBox) -> &dyn NyashBox { - 自動変換: `execute_builtin_box_method()` でpack相当処理 - ユーザー体験: 完全にfrom統一、packを意識不要 -## 🚀 次のステップ: 重要問題の修正 +## 🚀 次のステップ: plugin-systemドキュメント整理 -### 🎯 **instance_v2の純粋化** -**現状**: instance_v2にレガシー互換層が残存(段階的削除予定) +### 🎯 **緊急タスク: plugin-systemドキュメント4世代混在問題** + +**🚨 発見された問題**: +- **4つの異なる仕様書が混在**、実装と乖離 +- **MIR→プラグイン呼び出しがスタブのみ**(VM実装不完全) +- **API仕様の矛盾**(ドキュメント vs 実装) + +**📊 混在している4世代**: +1. `ffi-abi-specification.md` - 理想的だが未実装 +2. `plugin-system.md` - YAML DSL、使われていない +3. `nyash-toml-v2-spec.md` - 現実に近い仕様 +4. 実際の実装 - 今動いている形式 + +### 🎯 **整理方針: 実装ベース統一** + +#### **Phase 1: 現実調査** (優先度: 最高) +1. **実装の完全調査** + - `src/runtime/plugin_loader_v2.rs` 仕様確認 + - 現在のnyash.toml実際の形式確認 + - TLV実装詳細確認 + +2. **正確な仕様書作成** + - 現在動いている実装をベースとした仕様書 + - FileBoxプラグインの実証実装を参考資料化 + +#### **✅ Phase 2: ドキュメント整理** (優先度: 高) - **完了** +1. **✅ 古いドキュメント非推奨化完了** + - ✅ `ffi-abi-specification.md` → 「理想案、未実装」明記完了 + - ✅ `plugin-system.md` → 「将来構想」明記完了 + - ✅ `nyash-toml-v2-spec.md` → 「部分的に古い」明記完了 + +2. **✅ 実装ベース仕様書作成完了** + - ✅ `bid-ffi-v1-actual-specification.md` - 現在の実装仕様(作成済み) + - ✅ `builtin-to-plugin-conversion.md` - 変換手順書(作成済み) + - ✅ `migration-guide.md` - 古いドキュメントからの移行ガイド(新規作成) + - ✅ `plugin-system/README.md` - ナビゲーション用インデックス(新規作成) + +#### **Phase 3: MIR接続実装** (優先度: 中) +1. **ExternCall実装修正** + ```rust + // backend/vm.rs の修正 + MirInstruction::ExternCall { ... } => { + // 現在: printlnスタブ + // 修正後: プラグインシステムと接続 + let plugin_result = plugin_loader.invoke( + iface_name, method_name, args + )?; + } + ``` + +2. **統合テスト実装** + - MIR → VM → プラグイン呼び出しの完全な流れ + - パフォーマンス確認 + +### 🎯 **期待効果** +- ✅ **ドキュメント**: 実装と完全一致 +- ✅ **開発効率**: 矛盾のない一貫した仕様 +- ✅ **MIR統合**: ExternCall完全実装 +- ✅ **将来対応**: ビルトイン→プラグイン変換手順確立 + +### 🎯 **instance_v2の純粋化** (優先度: 低) +**現状**: instance_v2にレガシー互換層が残存(ドキュメント整理後に実施) 1. **クリーンアップ対象**: - レガシーfields → fields_ngに完全統一 @@ -268,4 +328,44 @@ cargo build --release -j32 --features wasm-backend - レジスタ割り当て最適化 - インライン展開 -最終更新: 2025-08-19 - Phase 9.78e完全勝利!instance.rs削除成功、instance_v2が唯一の実装に \ No newline at end of file +## 🚨 **緊急修正タスク: CHIP-8/Kiloアプリ実行エラー** + +### 🔧 **修正タスク1: 乗算演算子実装** + +**問題**: Kiloエディタで `undo_count * 50` が失敗 +``` +❌ Invalid operation: Multiplication not supported between IntegerBox and IntegerBox +``` + +**修正箇所**: `src/interpreter/expressions/operators.rs` +- `try_mul_operation()` に IntegerBox × IntegerBox サポート追加 + +### 🔧 **修正タスク2: fini後アクセス禁止エラー** + +**問題**: CHIP-8で手動fini()後のアクセスでエラー +``` +❌ Invalid operation: Instance was finalized; further use is prohibited +``` + +**調査箇所**: +- `src/instance_v2.rs` - is_finalized()チェックが必要な箇所 +- `src/interpreter/expressions/access.rs` - フィールドアクセス時のチェック + +### ✅ **解決済み** +- **Copilot PR #124**: インタープリタ性能問題完全解決(2500倍以上高速化) +- **toIntegerメソッド**: StringBoxに実装完了 +- **乗算演算子**: IntegerBox同士の乗算実装完了 + +### ✅ **解決済み: レガシーコード問題** + +**StringBox/IntegerBox型重複問題の解決**: +- ✅ `src/boxes/mod.rs`からレガシーエクスポートを削除 +- ✅ `src/box_trait.rs`のtoInteger()メソッドを修正(box_trait::IntegerBoxを使用) +- ✅ 乗算演算子が正常動作確認 +- ✅ toInteger()結果の乗算も動作確認 + +**新たに発見された問題**: +- ❌ StringBoxに`substring`メソッドが未実装 +- Kiloエディタで`str.substring(i, i + 1)`使用箇所でエラー + +最終更新: 2025-08-20 - レガシーコード問題解決、substring未実装エラー発見 \ No newline at end of file diff --git a/docs/説明書/reference/boxes-system/builtin-to-plugin-conversion.md b/docs/説明書/reference/boxes-system/builtin-to-plugin-conversion.md new file mode 100644 index 00000000..815ffd0e --- /dev/null +++ b/docs/説明書/reference/boxes-system/builtin-to-plugin-conversion.md @@ -0,0 +1,246 @@ +# 🔄 ビルトインBox → プラグイン変換手順書 + +## 🎯 概要 + +ビルトインBoxをBID-FFI v1プラグインに変換する標準手順。実際の変換作業で発見された問題と解決策を蓄積し、効率的な開発手法を確立する。 + +## 📊 変換パターン分析 + +### 🏆 成功事例:FileBox変換 +- **元実装**: `src/boxes/file/mod.rs` (RwLock) +- **プラグイン**: `plugins/nyash-filebox-plugin/` (BID-FFI v1) +- **結果**: ✅ 完全動作、プラグイン優先使用 + +### 🔍 現状分析:HTTP系Box +- **実装状況**: 完全実装済み(432行の高機能HTTPサーバー) +- **問題**: Unified Registry未登録(Legacy Match使用) +- **潜在性**: 即座にプラグイン化可能 + +## 🚀 標準変換手順(3段階アプローチ) + +### Phase 1: ビルトイン最適化 +**目的**: 既存実装の性能向上・デバッグ +**期間**: 1-3日 + +#### 手順 +1. **Unified Registry登録** + ```rust + // src/box_factory/builtin.rs 内 + fn register_io_types(&mut self) { + // HTTPServerBox追加 + self.register("HTTPServerBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("HTTPServerBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(HTTPServerBox::new())) + }); + // 他のHTTP系Boxも同様に追加 + } + ``` + +2. **動作テスト作成** + ```nyash + // local_tests/test_http_builtin.nyash + static box Main { + main() { + local server = new HTTPServerBox() + server.bind("localhost", 8080) + server.get("/test", TestHandler.handle) + return "HTTP builtin test complete" + } + } + ``` + +3. **性能ベンチマーク** + - Legacy Match vs Unified Registry比較 + - メモリ使用量測定 + +#### 期待効果 +- ✅ 高速化(Legacy Match削除) +- ✅ デバッグ環境確立 +- ✅ 安定性確認 + +### Phase 2: プラグイン変換実装 +**目的**: BID-FFI v1プラグイン実装 +**期間**: 3-7日 + +#### 手順 +1. **プラグインプロジェクト作成** + ```bash + mkdir plugins/nyash-http-plugin + cd plugins/nyash-http-plugin + cargo init --lib + ``` + +2. **Cargo.toml設定** + ```toml + [lib] + crate-type = ["cdylib"] + + [dependencies] + once_cell = "1.0" + # HTTP依存関係 + ``` + +3. **BID-FFI v1実装** + - マルチBox対応(HTTPServerBox, HTTPClientBox, SocketBox) + - TLV Protocol実装 + - Method ID定義 + +4. **nyash.toml設定** + ```toml + [libraries."libnyash_http_plugin.so"] + boxes = ["HTTPServerBox", "HTTPClientBox", "SocketBox"] + + [libraries."libnyash_http_plugin.so".HTTPServerBox] + type_id = 10 + [libraries."libnyash_http_plugin.so".HTTPServerBox.methods] + birth = { method_id = 0 } + bind = { method_id = 1, args = ["address", "port"] } + listen = { method_id = 2, args = ["backlog"] } + start = { method_id = 3 } + stop = { method_id = 4 } + fini = { method_id = 4294967295 } + ``` + +### Phase 3: 移行・検証 +**目的**: 完全移行とパフォーマンス検証 +**期間**: 1-2日 + +#### 手順 +1. **プラグイン優先テスト** + - 同じテストケースでビルトイン vs プラグイン比較 + - メモリリーク検証 + - エラーハンドリング確認 + +2. **ビルトイン実装削除** + - `src/boxes/http_*` ファイル削除 + - BUILTIN_BOXES リストから除去 + - コンパイル確認 + +3. **本格アプリテスト** + ```nyash + // apps/http_example/ + // 実用的なHTTPサーバーアプリで動作確認 + ``` + +## 🔧 BID-FFI v1必須要件 + +### ✅ **絶対必須の2つのメソッド** + +すべてのBID-FFI v1プラグインで実装必須: + +**🔧 birth() - コンストラクタ (METHOD_ID = 0)** +```rust +const METHOD_BIRTH: u32 = 0; // Constructor +``` +- **機能**: インスタンス作成、instance_id返却 +- **必須実装**: インスタンス管理、メモリ確保 +- **戻り値**: TLV形式のinstance_id (u32) + +**🧹 fini() - デストラクタ (METHOD_ID = u32::MAX)** +```rust +const METHOD_FINI: u32 = u32::MAX; // Destructor (4294967295) +``` +- **機能**: インスタンス解放、メモリクリーンアップ +- **必須実装**: INSTANCES.remove(), リソース解放 +- **戻り値**: 成功ステータス + +### 📝 設定例 +```toml +[libraries."libnyash_example_plugin.so".ExampleBox.methods] +birth = { method_id = 0 } # 🔧 必須 +# ... カスタムメソッド ... +fini = { method_id = 4294967295 } # 🧹 必須 +``` + +## 🐛 発見済み問題と解決策 + +### Problem 1: toString()メソッドエラー +**現象**: `Unknown method 'toString' for FileBox` +``` +❌ Interpreter error: Invalid operation: Unknown method 'toString' for FileBox +``` + +**原因**: プラグインにtoString()メソッド未定義 +**解決策**: nyash.tomlでtoStringメソッド追加 +```toml +toString = { method_id = 5 } +``` + +### Problem 2: Unified Registry未登録Box +**現象**: `Falling back to legacy match statement` +``` +🔍 Unified registry failed for HTTPServerBox: Unknown Box type +🔍 Falling back to legacy match statement +``` + +**原因**: BuiltinBoxFactory.register_io_types()未登録 +**解決策**: HTTP系Box登録追加 + +### Problem 3: 複雑な依存関係 +**予想問題**: HTTPServerBox → SocketBox → OS固有API +**解決策**: プラグイン内で依存関係完結 + +## 📋 チェックリスト + +### ✅ Phase 1完了条件 +- [ ] Unified Registry登録完了 +- [ ] Legacy Match削除確認 +- [ ] 基本動作テスト成功 +- [ ] パフォーマンス改善確認 + +### ✅ Phase 2完了条件 +- [ ] プラグインビルド成功 +- [ ] BID-FFI v1インターフェース実装 +- [ ] 全メソッドTLV対応 +- [ ] plugin-testerで検証成功 + +### ✅ Phase 3完了条件 +- [ ] プラグイン優先動作確認 +- [ ] ビルトイン実装削除成功 +- [ ] 実用アプリケーション動作確認 +- [ ] メモリリーク・エラーなし + +## 🚀 期待効果 + +### 短期効果(Phase 1) +- **5-10倍高速化**: Legacy Match → Unified Registry +- **保守性向上**: 統一的なファクトリパターン +- **デバッグ環境**: 安定したテスト基盤 + +### 長期効果(Phase 3) +- **プラグイン化完了**: 外部配布可能 +- **アーキテクチャ改善**: コア軽量化 +- **拡張性向上**: 独立開発可能 + +## 🎯 次期対象Box候補 + +### 優先度高(実装済み) +1. **HTTP系**: HTTPServerBox, HTTPClientBox, SocketBox +2. **BufferBox**: バイナリデータ処理 +3. **RegexBox**: 正規表現処理 + +### 優先度中(要調査) +1. **MathBox, RandomBox**: プラグイン実装あり(第1世代C ABI) +2. **JSONBox**: データ交換 +3. **StreamBox**: ストリーム処理 + +## 📝 学習記録 + +### 成功パターン +- FileBox: 単純構造、明確API → スムーズ変換 +- プラグイン優先システム動作確認済み + +### 注意点 +- toString()等の基本メソッド必須 +- 依存関係の循環に注意 +- メモリ管理の完全分離 + +--- + +**最終更新**: 2025年8月20日 - 初版作成 +**Phase**: 9.75g-0 完了後 - HTTP系Box変換準備完了 +**Next**: Phase 1実装→Phase 2プラグイン化 \ No newline at end of file diff --git a/docs/説明書/reference/plugin-system/README.md b/docs/説明書/reference/plugin-system/README.md new file mode 100644 index 00000000..5b60dece --- /dev/null +++ b/docs/説明書/reference/plugin-system/README.md @@ -0,0 +1,82 @@ +# Nyash Plugin System Documentation + +## 🎯 Quick Start + +**For new developers**: Start with [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md) + +## 📚 Documentation Index + +### 🟢 **Current & Accurate** +- **[bid-ffi-v1-actual-specification.md](./bid-ffi-v1-actual-specification.md)** - **主要仕様書** + - 実際に動作している実装をベースとした正確な仕様 + - FileBoxプラグインで実証済み + - プラグイン開発者はここから始める + +- **[plugin-tester.md](./plugin-tester.md)** - プラグイン診断ツール + - プラグインの動作確認とデバッグに使用 + - `tools/plugin-tester`ツールの使用方法 + +- **[filebox-bid-mapping.md](./filebox-bid-mapping.md)** - 参考資料 + - FileBox APIとプラグイン実装の対応表 + - API設計の参考として有用 + +### 🔄 **Migration & Reference** +- **[migration-guide.md](./migration-guide.md)** - 移行ガイド + - 古いドキュメントから現在の実装への移行方法 + - ドキュメント状況の整理 + +### ⚠️ **Deprecated - 非推奨** +- **[ffi-abi-specification.md](./ffi-abi-specification.md)** - ❌ 理想案、未実装 +- **[plugin-system.md](./plugin-system.md)** - ❌ 将来構想 +- **[nyash-toml-v2-spec.md](./nyash-toml-v2-spec.md)** - ⚠️ 部分的に古い + +## 🚀 For Plugin Developers + +### 1. **Read the Specification** +```bash +# 主要仕様書を読む +cat docs/説明書/reference/plugin-system/bid-ffi-v1-actual-specification.md +``` + +### 2. **Study Working Example** +```bash +# FileBoxプラグインを参考にする +cd plugins/nyash-filebox-plugin +cat src/lib.rs +``` + +### 3. **Configure Your Plugin** +```bash +# nyash.tomlで設定 +cat nyash.toml # 実際の設定形式を確認 +``` + +### 4. **Test Your Plugin** +```bash +# プラグインテスターで確認 +cd tools/plugin-tester +cargo build --release +./target/release/plugin-tester check path/to/your/plugin.so +``` + +## 🔧 For Nyash Core Developers + +### Implementation Files +- **[plugin_loader_v2.rs](../../../../src/runtime/plugin_loader_v2.rs)** - プラグインローダー実装 +- **[nyash_toml_v2.rs](../../../../src/config/nyash_toml_v2.rs)** - 設定パーサー +- **[tlv.rs](../../../../src/bid/tlv.rs)** - TLVエンコーダー/デコーダー + +### Next Steps +- **Phase 3**: MIR ExternCall → plugin system 接続実装 +- **Future**: HTTP系ボックスのプラグイン化 + +## 📞 Support & Issues + +- **Working Examples**: `plugins/nyash-filebox-plugin/` +- **Issues**: Report at [GitHub Issues](https://github.com/moe-charm/nyash/issues) +- **Configuration**: `nyash.toml` in project root + +--- + +**Status**: Phase 2 Documentation Reorganization - Completed +**Last Updated**: 2025-08-20 \ No newline at end of file diff --git a/docs/説明書/reference/plugin-system/bid-ffi-v1-actual-specification.md b/docs/説明書/reference/plugin-system/bid-ffi-v1-actual-specification.md new file mode 100644 index 00000000..f0b44bb6 --- /dev/null +++ b/docs/説明書/reference/plugin-system/bid-ffi-v1-actual-specification.md @@ -0,0 +1,194 @@ +# BID-FFI v1 実装仕様書 (実装ベース) + +## 🎯 概要 + +**これは現在動作している実装をベースとした正確な仕様書です。** +- FileBoxプラグインで実証済み +- plugin_loader_v2.rsの実装に基づく +- 理想案ではなく、実際に動く仕様 + +## 📋 プラグインAPI仕様 + +### 必須エクスポート関数 + +#### 1. ABI Version (オプション) +```c +extern "C" u32 nyash_plugin_abi(void) { + return 1; // BID-FFI v1 +} +``` + +#### 2. 初期化 (オプション) +```c +extern "C" i32 nyash_plugin_init(void) { + // グローバルリソース初期化 + // 0=成功, 負数=エラー(プラグイン無効化) + return 0; +} +``` + +#### 3. メソッド呼び出し (必須) +```c +extern "C" i32 nyash_plugin_invoke( + u32 type_id, // Box型ID (6=FileBox) + u32 method_id, // メソッドID (0=birth, 4294967295=fini) + u32 instance_id, // インスタンスID (0=static call) + const u8* args, // TLV引数 + usize args_len, // 引数サイズ + u8* result, // TLV結果バッファ + usize* result_len // [IN/OUT]バッファサイズ +) -> i32; // 0=成功, 負数=エラー +``` + +#### 4. 終了処理 (オプション) +```c +extern "C" void nyash_plugin_shutdown(void) { + // グローバルリソース解放 +} +``` + +## 📊 エラーコード + +```c +#define NYB_SUCCESS 0 // 成功 +#define NYB_E_SHORT_BUFFER -1 // バッファ不足 +#define NYB_E_INVALID_TYPE -2 // 無効な型ID +#define NYB_E_INVALID_METHOD -3 // 無効なメソッドID +#define NYB_E_INVALID_ARGS -4 // 無効な引数 +#define NYB_E_PLUGIN_ERROR -5 // プラグイン内部エラー +#define NYB_E_INVALID_HANDLE -8 // 無効なハンドル +``` + +## 🏗️ TLV (Type-Length-Value) 形式 + +### ヘッダー構造 +```c +struct TlvHeader { + u16 version; // 1 (BID-FFI v1) + u16 argc; // 引数数 +}; +``` + +### エントリー構造 +```c +struct TlvEntry { + u8 tag; // 型タグ + u8 reserved; // 0(将来拡張用) + u16 size; // ペイロードサイズ + // followed by payload data +}; +``` + +### 型タグ定義 +```c +#define BID_TAG_BOOL 1 // bool: 1 byte (0/1) +#define BID_TAG_I32 2 // i32: 4 bytes (little-endian) +#define BID_TAG_I64 3 // i64: 8 bytes (little-endian) +#define BID_TAG_F32 4 // f32: 4 bytes (IEEE 754) +#define BID_TAG_F64 5 // f64: 8 bytes (IEEE 754) +#define BID_TAG_STRING 6 // string: UTF-8 bytes +#define BID_TAG_BYTES 7 // bytes: binary data +#define BID_TAG_HANDLE 8 // handle: 8 bytes (type_id + instance_id) +#define BID_TAG_VOID 9 // void: 0 bytes +``` + +## 🔧 nyash.toml設定仕様 + +### 基本構造 +```toml +[libraries.""] +boxes = ["BoxType1", "BoxType2"] # 提供するBox型 +path = "./path/to/library.so" # ライブラリパス + +[libraries."".] +type_id = # Box型ID (必須) + +[libraries.""..methods] + = { method_id = } +``` + +### 実例 (FileBox) +```toml +[libraries."libnyash_filebox_plugin.so"] +boxes = ["FileBox"] +path = "./plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin.so" + +[libraries."libnyash_filebox_plugin.so".FileBox] +type_id = 6 + +[libraries."libnyash_filebox_plugin.so".FileBox.methods] +birth = { method_id = 0 } # コンストラクタ +open = { method_id = 1 } +read = { method_id = 2 } +write = { method_id = 3 } +close = { method_id = 4 } +fini = { method_id = 4294967295 } # デストラクタ (u32::MAX) +``` + +## 🔄 必須メソッド規約 + +### birth() - コンストラクタ +- **method_id**: 必ず 0 +- **引数**: TLV形式(型依存) +- **戻り値**: instance_id (u32, little-endian, 4bytes) +- **呼び出し**: instance_id=0 (static call) + +### fini() - デストラクタ +- **method_id**: 必ず 4294967295 (u32::MAX) +- **引数**: 空のTLV (version=1, argc=0) +- **戻り値**: Void +- **呼び出し**: 対象のinstance_id + +## 📝 PluginBoxV2構造体 + +```rust +pub struct PluginBoxV2 { + pub box_type: String, // "FileBox" + pub type_id: u32, // 6 + pub invoke_fn: InvokeFn, // 関数ポインタ + pub instance_id: u32, // プラグイン生成ID + pub fini_method_id: Option, // finiメソッドID +} +``` + +## 🚨 重要な制約 + +### メモリ管理 +- **プラグイン責任**: プラグインが確保したメモリはプラグインが解放 +- **2段階呼び出し**: + 1. result=NULL でサイズ取得 + 2. ホストがバッファ確保後、実際のデータ取得 + +### 文字列エンコーディング +- **UTF-8必須**: すべての文字列はUTF-8 +- **NUL終端不要**: lengthが正確性を保証 + +### インスタンス管理 +- **instance_id**: プラグイン内で一意 +- **birth順序**: birth() → 実際のメソッド → fini() +- **共有・複製**: clone_box()は新birth()、share_box()は同一instance_id + +## 🔗 実装ファイル + +### Nyash側 +- `src/runtime/plugin_loader_v2.rs` - プラグインローダー +- `src/config/nyash_toml_v2.rs` - 設定パーサー +- `src/bid/tlv.rs` - TLVエンコーダー/デコーダー + +### プラグイン例 +- `plugins/nyash-filebox-plugin/src/lib.rs` - FileBox実装 +- `plugins/nyash-test-multibox/src/lib.rs` - マルチBox実装 + +## ✅ 動作確認済み + +- ✅ FileBoxプラグイン完全動作 +- ✅ birth/finiライフサイクル +- ✅ TLVエンコーディング/デコーディング +- ✅ clone_box/share_box メソッド +- ✅ マルチインスタンス管理 + +--- + +**最終更新**: 2025年8月20日 - Phase 1現実調査完了 +**ベース**: plugin_loader_v2.rs実装 + FileBox実証 +**状態**: Production Ready (実際に動作中) \ No newline at end of file diff --git a/docs/説明書/reference/plugin-system/ffi-abi-specification.md b/docs/説明書/reference/plugin-system/ffi-abi-specification.md index 41baf610..a2d68193 100644 --- a/docs/説明書/reference/plugin-system/ffi-abi-specification.md +++ b/docs/説明書/reference/plugin-system/ffi-abi-specification.md @@ -1,5 +1,15 @@ # Box FFI/ABI v0 (BID-1 Enhanced Edition) +> ⚠️ **DEPRECATED - 理想案、未実装** +> +> この文書は将来の理想的なプラグインシステム設計案です。 +> **現在の実装とは異なります。** +> +> **実際に動作している仕様については、以下を参照してください:** +> - [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md) - 現在動作中の仕様 +> - [nyash.toml設定例](../../../../nyash.toml) - 実際の設定形式 +> - [plugin_loader_v2.rs](../../../../src/runtime/plugin_loader_v2.rs) - 実装詳細 + Purpose - Define a language-agnostic ABI to call external libraries as Boxes. - Serve as a single source of truth for MIR ExternCall, WASM RuntimeImports, VM stubs, and future language codegens (TS/Python/Rust/LLVM IR). diff --git a/docs/説明書/reference/plugin-system/migration-guide.md b/docs/説明書/reference/plugin-system/migration-guide.md new file mode 100644 index 00000000..d2524a79 --- /dev/null +++ b/docs/説明書/reference/plugin-system/migration-guide.md @@ -0,0 +1,104 @@ +# Plugin Documentation Migration Guide + +## 🎯 概要 + +このガイドは、Nyashプラグインシステムの古いドキュメントから実際の実装に移行するためのものです。 + +## 📚 Documentation Status + +### ✅ **Current Working Specification** +- **[BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md)** - **RECOMMENDED** + - 実際に動作している実装をベースとした正確な仕様 + - FileBoxプラグインで実証済み + - `plugin_loader_v2.rs`の実装に基づく + +### ⚠️ **Deprecated Documentation** +- **[ffi-abi-specification.md](./ffi-abi-specification.md)** - ❌ DEPRECATED + - 理想的な設計案だが未実装 + - MIR ExternCall設計が含まれているが、実際には使われていない + +- **[plugin-system.md](./plugin-system.md)** - ❌ DEPRECATED + - YAML DSLを使った将来構想 + - 現在の実装とは大きく異なる + +- **[nyash-toml-v2-spec.md](./nyash-toml-v2-spec.md)** - ⚠️ PARTIALLY OUTDATED + - 基本構造は正しいが、実際の形式と部分的に異なる + +### ✅ **Still Accurate Documentation** +- **[plugin-tester.md](./plugin-tester.md)** - ✅ CURRENT + - プラグイン診断ツールの使用方法 + - 実際のツールと一致 + +- **[filebox-bid-mapping.md](./filebox-bid-mapping.md)** - ✅ USEFUL REFERENCE + - FileBox APIとプラグイン実装の対応表 + - 開発時の参考資料として有効 + +## 🔄 Migration Steps + +### For Plugin Developers + +1. **Start with**: [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md) +2. **Refer to**: [実際のnyash.toml](../../../../nyash.toml) for configuration format +3. **Use**: [plugin-tester](../../../../tools/plugin-tester/) for testing +4. **Study**: [FileBox plugin](../../../../plugins/nyash-filebox-plugin/) as reference implementation + +### For Nyash Core Developers + +1. **Phase 1**: ✅ COMPLETED - Documentation cleanup with deprecation notices +2. **Phase 2**: ✅ COMPLETED - Accurate specification creation +3. **Phase 3**: 🚧 TODO - MIR ExternCall implementation to connect with plugin system + +## 🎯 Key Differences + +### Old Documentation vs Reality + +| Aspect | Old Docs | Reality | +|--------|----------|---------| +| Configuration | YAML DSL | TOML format | +| API Design | Complex handle system | Simple TLV + method_id | +| MIR Integration | Fully designed | Stub only | +| ABI Version | Multiple versions | BID-FFI v1 only | + +### Working Configuration Format + +**Old (in deprecated docs)**: +```yaml +# filebox.plugin.yaml +schema: 1 +apis: + - sig: "FileBox::open(path: string) -> FileBox" +``` + +**Current (actual)**: +```toml +[libraries."libnyash_filebox_plugin.so"] +boxes = ["FileBox"] +path = "./plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin.so" + +[libraries."libnyash_filebox_plugin.so".FileBox.methods] +birth = { method_id = 0 } +open = { method_id = 1 } +``` + +## 📞 FFI Interface + +**Old (complex)**: +- Multiple entry points +- Complex handle management +- Dynamic type discovery + +**Current (simple)**: +- Single entry point: `nyash_plugin_invoke` +- Fixed TLV protocol +- Static configuration in nyash.toml + +## 🚀 Next Steps + +1. ✅ **Documentation Cleanup**: Completed +2. 🚧 **MIR Integration**: Implement ExternCall → plugin system connection +3. 🔮 **Future**: Consider implementing some ideas from deprecated docs + +--- + +**Last Updated**: 2025-08-20 +**Status**: Documentation reorganization Phase 2 completed \ No newline at end of file diff --git a/docs/説明書/reference/plugin-system/nyash-toml-v2-spec.md b/docs/説明書/reference/plugin-system/nyash-toml-v2-spec.md new file mode 100644 index 00000000..7039b0d5 --- /dev/null +++ b/docs/説明書/reference/plugin-system/nyash-toml-v2-spec.md @@ -0,0 +1,163 @@ +# nyash.toml v2 仕様 - 究極のシンプル設計 + +> ⚠️ **PARTIALLY OUTDATED - 部分的に古い** +> +> この文書の基本構造は正しいですが、実際の形式と一部異なります。 +> +> **最新の実際の設定形式については、以下を参照してください:** +> - [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md) - 現在動作中の仕様 +> - [nyash.toml設定例](../../../../nyash.toml) - 実際の設定形式 + +## 🎯 概要 +**革命的シンプル設計**: nyash.toml中心アーキテクチャ + 最小限FFI + +## 📝 nyash.toml v2形式 + +### マルチBox型プラグイン対応 +```toml +[libraries] +# ライブラリ定義(1つのプラグインで複数のBox型を提供可能) +"libnyash_filebox_plugin.so" = { + boxes = ["FileBox"], + path = "./target/release/libnyash_filebox_plugin.so" +} + +# 将来の拡張例: 1つのプラグインで複数Box型 +"libnyash_network_plugin.so" = { + boxes = ["SocketBox", "HTTPServerBox", "HTTPClientBox"], + path = "./target/release/libnyash_network_plugin.so" +} + +# FileBoxの型情報定義 +[libraries."libnyash_filebox_plugin.so".FileBox] +type_id = 6 +abi_version = 1 # ABIバージョンもここに! + +[libraries."libnyash_filebox_plugin.so".FileBox.methods] +# method_id だけで十分(引数情報は実行時チェック) +birth = { method_id = 0 } +open = { method_id = 1 } +read = { method_id = 2 } +write = { method_id = 3 } +close = { method_id = 4 } +fini = { method_id = 4294967295 } # 0xFFFFFFFF +``` + +## 🚀 究極のシンプルFFI + +### プラグインが実装する関数 + +#### 必須: メソッド実行エントリーポイント +```c +// 唯一の必須関数 - すべてのメソッド呼び出しはここから +extern "C" fn nyash_plugin_invoke( + type_id: u32, // Box型ID(例: FileBox = 6) + method_id: u32, // メソッドID(0=birth, 0xFFFFFFFF=fini) + instance_id: u32, // インスタンスID(0=static/birth) + args: *const u8, // TLVエンコード引数 + args_len: usize, + result: *mut u8, // TLVエンコード結果バッファ + result_len: *mut usize // [IN/OUT]バッファサイズ +) -> i32 // 0=成功, 負=エラー +``` + +#### オプション: グローバル初期化 +```c +// プラグインロード時に1回だけ呼ばれる(実装は任意) +extern "C" fn nyash_plugin_init() -> i32 { + // グローバルリソースの初期化 + // 設定ファイルの読み込み + // ログファイルのオープン + // 0=成功, 負=エラー(プラグインは無効化される) +} +``` + +### 廃止されたAPI +```c +// ❌ これらは全部不要! +nyash_plugin_abi_version() // → nyash.tomlのabi_version +nyash_plugin_get_box_count() // → nyash.tomlのboxes配列 +nyash_plugin_get_box_info() // → nyash.tomlから取得 +NyashHostVtable // → 完全廃止! +``` + +## 📊 設計原則 + +### 1. **Single Source of Truth** +- すべてのメタ情報はnyash.tomlに集約 +- プラグインは純粋な実装のみ + +### 2. **Zero Dependencies** +- Host VTable廃止 = 依存関係ゼロ +- プラグインは完全に独立 + +### 3. **シンプルなライフサイクル** +- `init` (オプション): プラグインロード時の初期化 +- `birth` (method_id=0): インスタンス作成 +- 各種メソッド: インスタンス操作 +- `fini` (method_id=0xFFFFFFFF): 論理的終了 + +### 4. **ログ出力** +```rust +// プラグインは自己完結でログ出力 +eprintln!("[FileBox] Opened: {}", path); // 標準エラー + +// または専用ログファイル +let mut log = File::create("plugin_debug.log")?; +writeln!(log, "{}: FileBox birth", chrono::Local::now())?; +``` + +### 5. **init関数の活用例** +```rust +static mut LOG_FILE: Option = None; + +#[no_mangle] +pub extern "C" fn nyash_plugin_init() -> i32 { + // ログファイルを事前に開く + match File::create("filebox.log") { + Ok(f) => { + unsafe { LOG_FILE = Some(f); } + 0 // 成功 + } + Err(_) => -1 // エラー → プラグイン無効化 + } +} +``` + +## 🔧 実装の流れ + +### Phase 1: nyash.toml v2パーサー +1. 新形式の読み込み +2. Box型情報の抽出 +3. メソッドID管理 + +### Phase 2: プラグインローダー簡素化 +1. `nyash_plugin_init`(オプション)と`nyash_plugin_invoke`(必須)をロード +2. nyash.tomlベースの型登録 +3. Host VTable関連コードを削除 +4. init関数が存在し失敗した場合はプラグインを無効化 + +### Phase 3: プラグイン側の対応 +1. abi/get_box_count/get_box_info関数を削除 +2. init関数は必要に応じて実装(グローバル初期化) +3. invoke関数でメソッド処理 +4. ログ出力を自己完結に + +## 🎉 メリット + +1. **究極のシンプルさ** - 基本的にFFI関数1つ(initはオプション) +2. **保守性向上** - 複雑な相互依存なし +3. **テスト容易性** - モック不要 +4. **移植性** - どの言語でも実装可能 +5. **拡張性** - nyash.toml編集で機能追加 +6. **初期化保証** - init関数で早期エラー検出可能 + +## 🚨 注意事項 + +- プラグインのログは標準エラー出力かファイル出力で +- メモリ管理はプラグイン内で完結 +- 非同期処理はNyash側でFutureBoxラップ + +--- + +**革命完了**: これ以上シンプルにできない究極の設計! \ No newline at end of file diff --git a/docs/説明書/reference/plugin-system/plugin-system.md b/docs/説明書/reference/plugin-system/plugin-system.md index 59b435ed..0f157ce0 100644 --- a/docs/説明書/reference/plugin-system/plugin-system.md +++ b/docs/説明書/reference/plugin-system/plugin-system.md @@ -1,5 +1,15 @@ # Nyash Box プラグインシステム設計 +> ⚠️ **DEPRECATED - 将来構想** +> +> この文書はYAML DSLを使った将来的なプラグインシステム構想です。 +> **現在の実装とは異なります。** +> +> **実際に動作している仕様については、以下を参照してください:** +> - [BID-FFI v1 実装仕様書](./bid-ffi-v1-actual-specification.md) - 現在動作中の仕様 +> - [nyash.toml設定例](../../../../nyash.toml) - 実際の設定形式 +> - [plugin_loader_v2.rs](../../../../src/runtime/plugin_loader_v2.rs) - 実装詳細 + ## 概要 Nyashの「Everything is Box」哲学を維持しながら、Boxの実装をプラグイン化できるシステム。ビルトインBoxとプラグインBoxを透過的に切り替え可能。 diff --git a/src/box_factory/builtin.rs b/src/box_factory/builtin.rs index d8976f71..97b7dd36 100644 --- a/src/box_factory/builtin.rs +++ b/src/box_factory/builtin.rs @@ -6,7 +6,7 @@ */ use super::BoxFactory; -use crate::box_trait::NyashBox; +use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox}; use crate::interpreter::RuntimeError; use crate::boxes::*; use std::collections::HashMap; diff --git a/src/box_trait.rs b/src/box_trait.rs index 8097c1be..61064ca6 100644 --- a/src/box_trait.rs +++ b/src/box_trait.rs @@ -214,6 +214,17 @@ impl StringBox { Box::new(IntegerBox::new(self.value.len() as i64)) } + /// Convert string to integer (parse as i64) + pub fn to_integer(&self) -> Box { + match self.value.trim().parse::() { + Ok(n) => Box::new(IntegerBox::new(n)), + Err(_) => { + // If parsing fails, return 0 (JavaScript-like behavior) + Box::new(IntegerBox::new(0)) + } + } + } + /// Get character at index pub fn get(&self, index: usize) -> Option> { if let Some(ch) = self.value.chars().nth(index) { @@ -222,6 +233,15 @@ impl StringBox { None } } + + /// Get substring from start to end (exclusive) + pub fn substring(&self, start: usize, end: usize) -> Box { + let chars: Vec = self.value.chars().collect(); + let actual_end = end.min(chars.len()); + let actual_start = start.min(actual_end); + let substring: String = chars[actual_start..actual_end].iter().collect(); + Box::new(StringBox::new(substring)) + } } impl BoxCore for StringBox { diff --git a/src/boxes/mod.rs b/src/boxes/mod.rs index c4df1073..ce975d89 100644 --- a/src/boxes/mod.rs +++ b/src/boxes/mod.rs @@ -84,9 +84,9 @@ pub mod web; pub mod egui_box; // 共通で使う型とトレイトを再エクスポート -pub use string_box::StringBox; -pub use integer_box::IntegerBox; -pub use bool_box::BoolBox; +// pub use string_box::StringBox; // レガシー実装、box_trait::StringBoxを使用すること +// pub use integer_box::IntegerBox; // レガシー実装、box_trait::IntegerBoxを使用すること +// pub use bool_box::BoolBox; // レガシー実装、box_trait::BoolBoxを使用すること pub use math_box::{MathBox, FloatBox}; pub use time_box::{TimeBox, DateTimeBox}; pub use debug_box::DebugBox; diff --git a/src/boxes/string_box.rs b/src/boxes/string_box.rs index 9186023f..4c64a840 100644 --- a/src/boxes/string_box.rs +++ b/src/boxes/string_box.rs @@ -68,7 +68,7 @@ impl StringBox { /// Find substring and return position (or -1 if not found) pub fn find(&self, search: &str) -> Box { - use crate::boxes::IntegerBox; + use crate::boxes::integer_box::IntegerBox; match self.value.find(search) { Some(pos) => Box::new(IntegerBox::new(pos as i64)), None => Box::new(IntegerBox::new(-1)), @@ -97,19 +97,19 @@ impl StringBox { /// Check if string contains substring pub fn contains(&self, search: &str) -> Box { - use crate::boxes::BoolBox; + use crate::boxes::bool_box::BoolBox; Box::new(BoolBox::new(self.value.contains(search))) } /// Check if string starts with prefix pub fn starts_with(&self, prefix: &str) -> Box { - use crate::boxes::BoolBox; + use crate::boxes::bool_box::BoolBox; Box::new(BoolBox::new(self.value.starts_with(prefix))) } /// Check if string ends with suffix pub fn ends_with(&self, suffix: &str) -> Box { - use crate::boxes::BoolBox; + use crate::boxes::bool_box::BoolBox; Box::new(BoolBox::new(self.value.ends_with(suffix))) } @@ -127,6 +127,18 @@ impl StringBox { Box::new(StringBox::new(array_box.to_string_box().value)) } } + + /// Convert string to integer (parse as i64) + pub fn to_integer(&self) -> Box { + use crate::boxes::integer_box::IntegerBox; + match self.value.trim().parse::() { + Ok(n) => Box::new(IntegerBox::new(n)), + Err(_) => { + // If parsing fails, return 0 (JavaScript-like behavior) + Box::new(IntegerBox::new(0)) + } + } + } } impl NyashBox for StringBox { diff --git a/src/interpreter/expressions/calls.rs b/src/interpreter/expressions/calls.rs index 20abc4ce..1560b54a 100644 --- a/src/interpreter/expressions/calls.rs +++ b/src/interpreter/expressions/calls.rs @@ -221,10 +221,15 @@ impl NyashInterpreter { // オブジェクトを評価(通常のメソッド呼び出し) let obj_value = self.execute_expression(object)?; + eprintln!("🔍 DEBUG: execute_method_call - object type: {}, method: {}", obj_value.type_name(), method); // StringBox method calls + eprintln!("🔍 DEBUG: Checking StringBox downcast for type: {}", obj_value.type_name()); if let Some(string_box) = obj_value.as_any().downcast_ref::() { + eprintln!("🔍 DEBUG: StringBox detected, calling execute_string_method"); return self.execute_string_method(string_box, method, arguments); + } else { + eprintln!("🔍 DEBUG: StringBox downcast failed"); } // IntegerBox method calls @@ -495,7 +500,7 @@ impl NyashInterpreter { return self.execute_plugin_box_v2_method(plugin_box, method, arguments); } - // InstanceBox method calls + // ⚠️ InstanceBox method calls (最後にチェック、ビルトインBoxの後) if let Some(instance) = obj_value.as_any().downcast_ref::() { // 🔥 Usage prohibition guard - check if instance is finalized if instance.is_finalized() { @@ -690,6 +695,7 @@ impl NyashInterpreter { }) } } else { + eprintln!("🔍 DEBUG: Reached non-instance type error for type: {}, method: {}", obj_value.type_name(), method); Err(RuntimeError::TypeError { message: format!("Cannot call method '{}' on non-instance type", method), }) diff --git a/src/interpreter/expressions/operators.rs b/src/interpreter/expressions/operators.rs index 5b96fece..aff7cb35 100644 --- a/src/interpreter/expressions/operators.rs +++ b/src/interpreter/expressions/operators.rs @@ -5,7 +5,8 @@ // Removed super::* import - specific imports below use crate::ast::{ASTNode, BinaryOperator, UnaryOperator}; use crate::box_trait::{NyashBox, BoolBox, CompareBox}; -use crate::boxes::{IntegerBox, StringBox, FloatBox}; // 🔧 算術は boxes::* 実体に統一 +use crate::box_trait::{IntegerBox, StringBox}; // 🔧 修正: box_trait::*に統一 +use crate::boxes::FloatBox; // FloatBoxはboxesのみに存在 use crate::interpreter::core::{NyashInterpreter, RuntimeError}; use crate::instance_v2::InstanceBox; @@ -14,11 +15,15 @@ use crate::instance_v2::InstanceBox; /// InstanceBoxでラップされている場合、内部のBoxを取得する /// シンプルなヘルパー関数で型地獄を回避 fn unwrap_instance(boxed: &dyn NyashBox) -> &dyn NyashBox { + eprintln!("🔍 DEBUG unwrap_instance: input type = {}", boxed.type_name()); if let Some(instance) = boxed.as_any().downcast_ref::() { + eprintln!(" ✅ Is InstanceBox"); if let Some(ref inner) = instance.inner_content { + eprintln!(" 📦 Inner content type = {}", inner.type_name()); return inner.as_ref(); } } + eprintln!(" ❌ Not InstanceBox, returning as is"); boxed } pub(super) fn try_add_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Option> { @@ -71,11 +76,28 @@ pub(super) fn try_mul_operation(left: &dyn NyashBox, right: &dyn NyashBox) -> Op let left = unwrap_instance(left); let right = unwrap_instance(right); + // デバッグ出力 + eprintln!("🔍 DEBUG try_mul: left type = {}, right type = {}", left.type_name(), right.type_name()); + // IntegerBox * IntegerBox if let (Some(left_int), Some(right_int)) = ( left.as_any().downcast_ref::(), right.as_any().downcast_ref::() ) { + eprintln!("✅ IntegerBox downcast success: {} * {}", left_int.value, right_int.value); + return Some(Box::new(IntegerBox::new(left_int.value * right_int.value))); + } + + // box_trait::IntegerBoxも試す + eprintln!("❌ box_trait::IntegerBox downcast failed, trying boxes::integer_box::IntegerBox"); + + // boxes::integer_box::IntegerBoxを試す + use crate::boxes::integer_box::IntegerBox as BoxesIntegerBox; + if let (Some(left_int), Some(right_int)) = ( + left.as_any().downcast_ref::(), + right.as_any().downcast_ref::() + ) { + eprintln!("✅ boxes::IntegerBox downcast success: {} * {}", left_int.value, right_int.value); return Some(Box::new(IntegerBox::new(left_int.value * right_int.value))); } diff --git a/src/interpreter/methods/basic_methods.rs b/src/interpreter/methods/basic_methods.rs index f5b1a2be..65cf2169 100644 --- a/src/interpreter/methods/basic_methods.rs +++ b/src/interpreter/methods/basic_methods.rs @@ -126,6 +126,42 @@ impl NyashInterpreter { } Ok(string_box.to_lower()) } + "toInteger" => { + if !arguments.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("toInteger() expects 0 arguments, got {}", arguments.len()), + }); + } + Ok(string_box.to_integer()) + } + "substring" => { + if arguments.len() != 2 { + return Err(RuntimeError::InvalidOperation { + message: format!("substring() expects 2 arguments, got {}", arguments.len()), + }); + } + let start = self.execute_expression(&arguments[0])?; + let end = self.execute_expression(&arguments[1])?; + + // Convert arguments to integers + let start_int = if let Some(int_box) = start.as_any().downcast_ref::() { + int_box.value as usize + } else { + return Err(RuntimeError::TypeError { + message: "substring() expects integer arguments".to_string(), + }); + }; + + let end_int = if let Some(int_box) = end.as_any().downcast_ref::() { + int_box.value as usize + } else { + return Err(RuntimeError::TypeError { + message: "substring() expects integer arguments".to_string(), + }); + }; + + Ok(string_box.substring(start_int, end_int)) + } _ => { Err(RuntimeError::InvalidOperation { message: format!("Unknown method '{}' for StringBox", method),