From a5ff3ecafed15fb3debf6cea097f723a3f9d4122 Mon Sep 17 00:00:00 2001 From: Moe Charm Date: Sun, 17 Aug 2025 19:54:57 +0900 Subject: [PATCH] feat(phase-9.75g-0): Implement BID-FFI Day 3 - Box type integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement BID Box Bridge interface for Nyash Box <-> BID Handle conversion - Add StringBox BID bridge implementation with handle/TLV support - Add IntegerBox BID bridge implementation with handle/TLV support - Implement BoxRegistry for managing Box instances and handles - Add comprehensive tests for StringBox/IntegerBox BID round-trip - Extract helper functions for string/integer value extraction Everything is Box philosophy shines through unified BID integration! 🎉 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- ...hatgpt_final_box_theory_review_2025-08-17.md | 179 +++++++++++++ .../phase_9_75g_0_everything_is_box_aligned.md | 248 ++++++++++++++++++ .../phase_9_75g_0_final_corrected_design.md | 231 ++++++++++++++++ local_tests/bid_async_design_review.txt | 91 +++++++ local_tests/nyash_box_theory_final_review.txt | 125 +++++++++ src/bid/bridge.rs | 202 ++++++++++++++ src/bid/mod.rs | 2 + src/boxes/integer_box.rs | 20 ++ src/boxes/string_box.rs | 20 ++ 9 files changed, 1118 insertions(+) create mode 100644 docs/予定/native-plan/issues/chatgpt_final_box_theory_review_2025-08-17.md create mode 100644 docs/予定/native-plan/issues/phase_9_75g_0_everything_is_box_aligned.md create mode 100644 docs/予定/native-plan/issues/phase_9_75g_0_final_corrected_design.md create mode 100644 local_tests/bid_async_design_review.txt create mode 100644 local_tests/nyash_box_theory_final_review.txt create mode 100644 src/bid/bridge.rs diff --git a/docs/予定/native-plan/issues/chatgpt_final_box_theory_review_2025-08-17.md b/docs/予定/native-plan/issues/chatgpt_final_box_theory_review_2025-08-17.md new file mode 100644 index 00000000..ceaeab74 --- /dev/null +++ b/docs/予定/native-plan/issues/chatgpt_final_box_theory_review_2025-08-17.md @@ -0,0 +1,179 @@ +# ChatGPT先生による箱理論設計 最終レビュー結果 (2025-08-17) + +## 🎯 総合評価 + +### ✅ **Executive Summary(ChatGPT判定)** +> **方向性は正しい**: primitives-by-value + box-by-handle は適切で、Everything is Box哲学を維持している。 +> **1週間Phase 1は現実的**(スコープを限定すれば) + +## 🔧 重要な修正提案 + +### 1. **Handle設計の改善** 🚨 +```rust +// ❌ 現在の設計 +Handle(String) // "StringBox:123" - 文字列解析コスト高い + +// ✅ ChatGPT推奨 +Handle { + type_id: u32, // StringBox=1, FileBox=6等 + instance_id: u32 // インスタンス識別子 +} +// または単一u64として: type_id << 32 | instance_id +``` + +**理由**: 文字列解析は遅く、エラーの原因。バイナリ形式が効率的。 + +### 2. **メタデータAPI追加** 💡 +```c +// プラグインに追加すべき関数 +u32 nyash_plugin_abi(void); // ABI版本(1)を返す +i32 nyash_plugin_init(const NyashHostVtable*, NyashPluginInfo*); +void nyash_plugin_shutdown(void); +``` + +**理由**: バージョン管理、ホスト連携、型・メソッド登録が必要。 + +### 3. **TLV統一フォーマット** 📦 +```c +// BID-1 TLV仕様(ChatGPT提案) +struct BidTLV { + u16 version; // 1 + u16 argc; // 引数数 + // TLVs: u8 tag, u8 reserved, u16 size, payload +} + +// タグ定義 +1=Bool(1), 2=I32(4), 3=I64(8), 4=F32(4), 5=F64(8), +6=String(utf8), 7=Bytes, 8=Handle(8 bytes) +// 予約: 20=Result, 21=Option, 22=Array (Phase 2) +``` + +**理由**: メソッドごとの個別エンコードを避け、統一フォーマットで効率化。 + +### 4. **メモリ管理の明確化** 🛠️ +```c +// ChatGPT推奨: 2回呼び出しパターン +// 1回目: result_ptr=null でサイズ取得 +// 2回目: ホストがallocateして再呼び出し +i32 nyash_plugin_invoke(..., result_ptr, result_len); +``` + +**理由**: 所有権が明確、メモリリーク回避。 + +## 📊 各質問への回答 + +### 1. 箱理論の技術的妥当性 ✅ +- **適切性**: 全Boxをハンドル統一は妥当 +- **統一扱い**: 既存/プラグインを同一レジストリで管理可 +- **ハンドル表現**: バイナリ形式(type_id, instance_id)に変更推奨 + +### 2. 最低設計のメリット・デメリット ✅ +- **メリット**: 実装最短、既存Box再利用最大化、API安定 +- **デメリット**: Array/Map未対応で複合データが冗長(TLVで緩和可) +- **戦略**: Phase 1基本 → Phase 2拡張は正解 + +### 3. 既存資産活用の是非 ✅ +- **FutureBox再利用**: 正解、二重実装回避 +- **統合アプローチ**: 適切、メソッドIDはメタデータで合意 +- **純粋性トレードオフ**: 実用性を優先が現実的 + +### 4. 実装現実性 ✅ +- **1週間**: 現実的(スコープ限定時) +- **統合難易度**: 中レベル、FutureBoxのwake統合がポイント +- **Linux x86-64限定**: 妥当 + +### 5. 将来拡張性 ✅ +- **gRPC/REST**: invoke+TLVをRPCカプセル化で対応可 +- **Transport抽象化**: Phase 2でTransportBox導入 +- **P2P**: 同じinvokeメッセージで転送可能 + +## 🔧 具体的な実装修正案 + +### BidType修正版 +```rust +#[derive(Clone, Debug, PartialEq)] +pub enum BidType { + // プリミティブ(値渡し) + Bool, I32, I64, F32, F64, String, Bytes, + + // Box参照(ハンドル) + Handle { type_id: u32, instance_id: u32 }, + + // メタ型 + Void, + + // Phase 2予約(TLVタグ予約済み) + Option(Box), // tag=21 + Result(Box, Box), // tag=20 + Array(Box), // tag=22 +} +``` + +### C ABI修正版 +```c +// メタデータ構造体 +typedef struct { + u32 type_id; + const char* type_name; + u32 method_count; + // メソッドテーブル... +} NyashPluginInfo; + +// ホスト機能 +typedef struct { + void* (*alloc)(size_t size); + void (*free)(void* ptr); + void (*wake)(u32 future_id); // FutureBox起床 + void (*log)(const char* msg); +} NyashHostVtable; + +// プラグインAPI +u32 nyash_plugin_abi(void); +i32 nyash_plugin_init(const NyashHostVtable* host, NyashPluginInfo* info); +i32 nyash_plugin_invoke(u32 type_id, u32 method_id, u32 instance_id, + const u8* args, size_t args_len, + u8* result, size_t* result_len); +void nyash_plugin_shutdown(void); +``` + +## ⚠️ リスク対策 + +### ChatGPT指摘のリスク +1. **ハンドル再利用**: generation追加で回避 +2. **スレッド前提**: シングルスレッド前提を明記 +3. **メソッドID衝突**: ビルド時固定で回避 +4. **エラー伝播**: トランスポート/ドメインエラー分離 +5. **文字列エンコード**: UTF-8必須、内部NUL禁止 + +## 📋 Phase 1実装チェックリスト(ChatGPT提案) + +- [ ] BID-1 TLV仕様とエラーコード定義 +- [ ] ホストレジストリ + Handle{type_id,instance_id} +- [ ] プラグインinit/abi/shutdown追加 +- [ ] 既存StringBox/IntegerBox/FutureBoxブリッジ +- [ ] FileBoxプラグイン(open/read/close) +- [ ] FutureBox用wake経路 +- [ ] 適合性テスト(プリミティブ、ハンドル、エラー) + +## 🚀 結論 + +ChatGPT先生の判定: +> **箱理論設計は技術的に妥当!** ただし具体的な実装詳細で重要な改善提案あり。 + +### 主要な価値 +1. **Everything is Box哲学の技術的実現**を評価 +2. **具体的で実装可能な修正案**を提示 +3. **1週間実装の現実性**を確認 +4. **将来拡張への明確な道筋**を提示 + +### 推奨アクション +1. Handle設計をバイナリ形式に変更 +2. メタデータAPIを追加 +3. TLV統一フォーマット導入 +4. Phase 1スコープでの実装開始 + +--- + +**レビュー日**: 2025-08-17 +**レビュワー**: ChatGPT-5 +**結論**: 方向性正しい、実装詳細要修正、1週間実装可能 \ No newline at end of file diff --git a/docs/予定/native-plan/issues/phase_9_75g_0_everything_is_box_aligned.md b/docs/予定/native-plan/issues/phase_9_75g_0_everything_is_box_aligned.md new file mode 100644 index 00000000..abc661a0 --- /dev/null +++ b/docs/予定/native-plan/issues/phase_9_75g_0_everything_is_box_aligned.md @@ -0,0 +1,248 @@ +# Phase 9.75g-0 修正版: Everything is Box哲学準拠のシンプル設計 + +## 🎯 基本方針:Nyash哲学ファースト + +**重要な気づき**: ChatGPT設計は一般的だが、**既存のFutureBox等と二重実装**になってしまう。 +Nyashの「Everything is Box」哲学を貫き、既存資産を活用する。 + +## 🌟 Nyash哲学に準拠した設計 + +### 1. 型システム:プリミティブ + Handle + +```rust +// src/bid/types.rs - Everything is Box哲学準拠 + +#[derive(Clone, Debug, PartialEq)] +pub enum BidType { + // === プリミティブ型(FFI境界で直接渡せる) === + Bool, // Nyashのbool literal + I32, // 32ビット整数 + I64, // Nyashの標準整数 + F32, // 32ビット浮動小数点 + F64, // Nyashの標準浮動小数点 + String, // UTF-8文字列 (ptr: usize, len: usize) + Bytes, // バイナリデータ (ptr: usize, len: usize) + + // === Everything is Box: すべてのBoxは統一Handle === + Handle(String), // "StringBox:123", "FileBox:456", "FutureBox:789" + + // === メタ型(FFI用) === + Void, // 戻り値なし + + // Phase 2以降で追加(定義だけ先に) + Option(Box), // Option + Result(Box, Box), // Result + Array(Box), // Array + + // === Everything is Box哲学の拡張 === + // Array, Map, Future等はすべてHandle("ArrayBox:id")として扱う +} + +// Nyashの既存Boxとの対応表 +/* +Handle("StringBox:123") → StringBox インスタンス +Handle("IntegerBox:456") → IntegerBox インスタンス +Handle("FutureBox:789") → FutureBox インスタンス(非同期) +Handle("FileBox:101") → FileBox インスタンス +Handle("ArrayBox:102") → ArrayBox インスタンス +Handle("P2PBox:103") → P2PBox インスタンス +*/ +``` + +### 2. シンプルなBoxヘッダー(Nyash統一仕様) + +```rust +// 既存のNyash Boxヘッダーと統一 +#[repr(C, align(8))] +pub struct BoxHeader { + magic: u32, // "NYBX" (0x5859424E) + version: u16, // 1 + _pad: u16, // アライメント用 + type_id: u32, // BoxTypeId(StringBox=1, FileBox=2等) + instance_id: u32, // インスタンス識別子 + ref_count: u32, // 非atomic(Nyashはシングルスレッド中心) + flags: u32, // 将来の拡張用 +} + +// Nyashの既存Box型との統合 +pub const NYASH_BOX_TYPES: &[(u32, &str)] = &[ + (1, "StringBox"), + (2, "IntegerBox"), + (3, "BoolBox"), + (4, "ArrayBox"), + (5, "MapBox"), + (6, "FileBox"), // プラグインで提供 + (7, "FutureBox"), // 既存の非同期Box + (8, "P2PBox"), // 既存のP2P Box + // 新しいプラグインBoxも同じ仕組みで追加 +]; +``` + +### 3. 単一エントリーポイント(Everything is Box対応) + +```rust +// Nyashの全Boxを統一的に扱える設計 +#[no_mangle] +extern "C" fn nyash_plugin_invoke( + box_type_id: u32, // どのBox型?(StringBox=1, FileBox=6等) + method_id: u32, // どのメソッド?(open=1, read=2等) + instance_id: u32, // どのインスタンス?(Handle解析用) + args_ptr: *const u8, // 引数データ + args_len: usize, // 引数サイズ + result_ptr: *mut u8, // 結果置き場 + result_len: *mut usize, // 結果サイズ +) -> i32 { // 0=成功, 非0=エラー + // Everything is Box哲学:すべて同じ仕組みで処理 +} + +// 既存のNyash Boxとの統合例 +fn handle_stringbox_call(method_id: u32, instance_id: u32, args: &[u8]) -> Result, BidError> { + match method_id { + 1 => { /* length() */ }, + 2 => { /* substring() */ }, + 3 => { /* append() */ }, + _ => Err(BidError::MethodNotFound), + } +} + +fn handle_filebox_call(method_id: u32, instance_id: u32, args: &[u8]) -> Result, BidError> { + match method_id { + 1 => { /* open() */ }, + 2 => { /* read() */ }, + 3 => { /* write() */ }, + 4 => { /* close() */ }, + _ => Err(BidError::MethodNotFound), + } +} +``` + +### 4. 既存Nyash Boxとの統合戦略 + +```rust +// src/bid/integration.rs - 既存Boxとの橋渡し + +pub struct NyashBoxRegistry { + // 既存のStringBox、ArrayBox等のインスタンス管理 + instances: HashMap>>, + next_id: AtomicU32, +} + +impl NyashBoxRegistry { + pub fn register_box(&self, box_instance: Arc>) -> u32 { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.instances.insert(id, box_instance); + id + } + + pub fn call_method(&self, type_id: u32, instance_id: u32, method_id: u32, args: &[u8]) + -> Result, BidError> + { + match type_id { + 1 => self.call_stringbox_method(instance_id, method_id, args), + 6 => self.call_filebox_method(instance_id, method_id, args), + 7 => self.call_futurebox_method(instance_id, method_id, args), // 既存FutureBox活用! + _ => Err(BidError::UnknownBoxType(type_id)), + } + } +} +``` + +## 📋 修正された実装計画 + +### Day 1: Nyash統合基盤 +- [ ] `src/bid/types.rs` - Everything is Box準拠型定義 +- [ ] `src/bid/registry.rs` - 既存Box統合レジストリ +- [ ] `src/bid/header.rs` - 統一Boxヘッダー +- [ ] テスト: 既存StringBoxとの統合 + +### Day 2: プラグインローダー +- [ ] `src/bid/loader.rs` - dlopen/dlsym +- [ ] 最小プラグイン(MathBox拡張) +- [ ] テスト: 既存BoxとプラグインBoxの共存 + +### Day 3: Handle型統合 +- [ ] Handle("FileBox:123")の解決機構 +- [ ] プリミティブ⇔Handle変換 +- [ ] テスト: 全Box型の統一的操作 + +### Day 4: FileBox実装 +- [ ] FileBoxプラグインの完全実装 +- [ ] 既存のNyashコードとの互換性 +- [ ] テスト: FileBox e2e動作 + +### Day 5: エラー処理とOption/Result +- [ ] 統一エラーシステム +- [ ] Option/Result型の最小実装 +- [ ] テスト: エラーケース網羅 + +### Day 6-7: 統合テスト・ドキュメント +- [ ] 既存インタープリターとの統合 +- [ ] 使用例とドキュメント +- [ ] Linux x86-64 CI設定 + +## 🌟 この設計の哲学的利点 + +### 1. Everything is Box哲学の完全準拠 +```nyash +// Nyashコード側:変わらない! +local file = new FileBox("test.txt", "r") // プラグイン提供 +local future = new FutureBox() // 既存Box +local array = new ArrayBox() // 既存Box + +// すべて同じHandle("BoxType:id")として扱われる +``` + +### 2. 既存資産の完全活用 +- ❌ 新しいBidFuture実装 → ✅ 既存FutureBox活用 +- ❌ 新しい型システム → ✅ 既存Nyash型との統合 +- ❌ 二重実装 → ✅ 単一の統一システム + +### 3. スレッド最小設計 +```rust +// Nyashの現実に合わせた設計 +ref_count: u32, // 非atomic(シングルスレッド中心) + +// Phase 2以降でatomic対応を検討 +#[cfg(feature = "atomic")] +ref_count: AtomicU32, +``` + +### 4. エラー対策の強化 +```rust +// 統一エラー処理でプラグインの安定性向上 +pub enum BidError { + UnknownBoxType(u32), + InstanceNotFound(u32), + MethodNotFound(u32), + InvalidArguments(String), + PluginError(String), // プラグイン側エラーを安全に伝播 +} +``` + +## ✅ 成功基準(Everything is Box準拠) + +### 必須 +- [ ] 既存StringBox、ArrayBoxとプラグインFileBoxが同じ仕組みで動作 +- [ ] Handle("FileBox:123")でのBox操作 +- [ ] 既存FutureBoxの活用(新実装なし) +- [ ] すべてのBoxが統一的にアクセス可能 + +### 理想 +- [ ] 新しいプラグインBoxも既存Boxと見分けがつかない +- [ ] Nyashコード側は変更不要 +- [ ] Everything is Box哲学の技術的実現 + +## 📝 まとめ + +**ChatGPT先生の一般論は正しいが、Nyashの独特な哲学には合わない。** + +**Nyash Way**: Everything is Box → すべてHandle + 既存Box活用 +**一般的Way**: 型システム分離 → 新しい実装追加 + +**結論**: Nyashの哲学を貫いて、既存の資産を最大活用する設計で進む! + +--- + +**修正日**: 2025-08-17 +**修正理由**: Everything is Box哲学の完全準拠、既存資産活用 +**キーワード**: Simple, Nyash-native, No-duplication \ No newline at end of file diff --git a/docs/予定/native-plan/issues/phase_9_75g_0_final_corrected_design.md b/docs/予定/native-plan/issues/phase_9_75g_0_final_corrected_design.md new file mode 100644 index 00000000..f41bf839 --- /dev/null +++ b/docs/予定/native-plan/issues/phase_9_75g_0_final_corrected_design.md @@ -0,0 +1,231 @@ +# Phase 9.75g-0 最終修正版: ChatGPT先生の知恵を反映した型設計 + +## 🎯 ChatGPT先生の明確な判断 + +> **結論**: Future/StreamはBidType(値型)に含めないでください。非同期性は「実行モデル」であって「値の表現」ではありません。 + +## 🛠️ 修正された型システム設計 + +### 1. 値型(BidType)- 純粋な値のみ + +```rust +// src/bid/types.rs - ChatGPT先生推奨の清潔な設計 + +#[derive(Clone, Debug, PartialEq)] +pub enum BidType { + // === 基本型(Phase 1で実装) === + Bool, + I32, + I64, + F32, + F64, + String, // (ptr: usize, len: usize) + Bytes, // (ptr: usize, len: usize) + + // === 複合型(Phase 2で実装) === + Array(Box), // 配列 + List(Box), // 可変長リスト + Map(Box, Box), // キーバリューマップ + Tuple(Vec), // タプル + Record(Vec<(String, BidType)>), // 名前付きフィールド + Variant(Vec<(String, Option)>), // 列挙型 + + // === 特殊型(Phase 2で実装) === + Option(Box), // null許容 + Result(Box, Box), // エラー型 + Handle(String), // 不透明ハンドル(同期リソース用) + Void, // 戻り値なし + + // === 拡張用(定義だけ) === + Opaque(String), // 不透明型 + + // ❌ 削除: Future/Streamは値型ではない! + // Future(Box), // 削除 + // Stream(Box), // 削除 +} +``` + +### 2. 実行モデル(MethodShape)- 新設計 + +```rust +// メソッドの実行形状を表現(ChatGPT推奨) +#[derive(Clone, Debug, PartialEq)] +pub enum MethodShape { + Sync, // 通常の同期呼び出し + Async, // Futureを返す(ハンドル経由) + Streaming, // Streamを返す(ハンドル経由) +} + +// メソッドシグネチャ(形状と値型を分離) +#[derive(Clone, Debug)] +pub struct MethodSig { + pub name: String, + pub shape: MethodShape, // 実行モデル + pub params: Vec, // 引数の値型 + pub returns: BidType, // 戻り値の値型(Future抜き) + pub effects: Vec, +} + +// BID定義でメソッド記述 +#[derive(Clone, Debug)] +pub struct Method { + pub sig: MethodSig, + pub doc: Option, +} +``` + +### 3. 非同期ハンドル(FFI境界用) + +```rust +// ChatGPT推奨のハンドル方式 +use std::ffi::c_void; + +// FFI境界での非同期ハンドル(不透明ポインタ) +#[repr(transparent)] +pub struct BidFutureHandle(*mut c_void); + +#[repr(transparent)] +pub struct BidStreamHandle(*mut c_void); + +// Rust側の安全ラッパー +pub struct BidFuture { + handle: BidFutureHandle, + return_type: BidType, +} + +pub struct BidStream { + handle: BidStreamHandle, + item_type: BidType, +} + +// 将来のRust async/await統合 +impl std::future::Future for BidFuture { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // FFI経由でpolling or callback設定 + unimplemented!("Phase 3で実装") + } +} + +impl futures_core::Stream for BidStream { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unimplemented!("Phase 3で実装") + } +} +``` + +### 4. Connection trait(形状別実装) + +```rust +// ChatGPT推奨の分離アプローチ +pub trait Connection: Send + Sync { + // 同期呼び出し(Phase 1で実装) + fn invoke(&self, sig: &MethodSig, args: &[BidValue]) -> Result; + + // 非同期呼び出し(Phase 3で実装) + fn invoke_future(&self, sig: &MethodSig, args: &[BidValue]) -> Result { + Err(BidError::Unsupported("async not supported yet".to_string())) + } + + // ストリーミング(Phase 3で実装) + fn invoke_stream(&self, sig: &MethodSig, args: &[BidValue]) -> Result { + Err(BidError::Unsupported("streaming not supported yet".to_string())) + } +} +``` + +### 5. FFI境界の非同期API(Phase 3で実装) + +```c +// ChatGPT推奨のC ABI設計(Phase 3で実装予定) + +// Future操作 +extern "C" fn bid_future_poll( + handle: *mut c_void, + out_value: *mut BidValue, + out_is_ready: *mut bool +) -> BidStatus; + +extern "C" fn bid_future_set_callback( + handle: *mut c_void, + callback: extern "C" fn(*mut c_void, BidValue, BidStatus), + user_data: *mut c_void +) -> BidStatus; + +extern "C" fn bid_future_cancel(handle: *mut c_void) -> BidStatus; +extern "C" fn bid_future_free(handle: *mut c_void); + +// Stream操作 +extern "C" fn bid_stream_poll_next( + handle: *mut c_void, + out_item: *mut BidValue, + out_has_item: *mut bool, + out_is_closed: *mut bool +) -> BidStatus; + +extern "C" fn bid_stream_set_callback( + handle: *mut c_void, + callback: extern "C" fn(*mut c_void, BidValue, bool, BidStatus), + user_data: *mut c_void +) -> BidStatus; + +extern "C" fn bid_stream_close(handle: *mut c_void) -> BidStatus; +extern "C" fn bid_stream_free(handle: *mut c_void); +``` + +## 📋 修正された実装スケジュール + +### Phase 1(1週間)- 同期のみ +```rust +// 実装するもの +- BidType基本型(Bool, I32, I64, F32, F64, String) +- MethodShape::Syncのみ +- DynamicLibraryコネクター +- Connection::invoke()のみ + +// 実装しないもの +- 非同期型(Future/Stream) → 定義から削除済み +- MethodShape::Async/Streaming → unsupportedエラー +``` + +### Phase 2(2週間後)- 複合型 +```rust +// 追加実装 +- Array, List, Map, Option, Result型 +- エラー処理の充実 +- 複数プラグイン同時ロード +``` + +### Phase 3(1ヶ月後)- 非同期 +```rust +// ハンドル方式で非同期追加 +- BidFuture/BidStream実装 +- FFI境界非同期API +- Rust async/await統合 +- WasmComponent対応 +``` + +## 🌟 ChatGPT先生の知恵のまとめ + +1. **型と実行モデルの分離** - 値型は純粋に、実行形状は別定義 +2. **FFI境界の現実性** - ハンドル+API関数群で非同期表現 +3. **WASM整合性** - Component Modelの流儀に準拠 +4. **段階的実装** - unsupportedエラーでpanic回避 +5. **将来拡張性** - Transport差異を抽象化で吸収 + +## ✅ この設計の利点 + +- **シンプル**: 型システムが明確(値型のみ) +- **拡張可能**: 実行モデルを後から追加可能 +- **FFI現実的**: C ABIで実際に渡せる形 +- **標準準拠**: WASM Component Modelと整合 +- **実装しやすい**: 同期から始めて段階的に + +--- + +**修正日**: 2025-08-17 +**修正理由**: ChatGPT先生のアドバイス適用 +**重要な変更**: Future/Stream削除、MethodShape導入 \ No newline at end of file diff --git a/local_tests/bid_async_design_review.txt b/local_tests/bid_async_design_review.txt new file mode 100644 index 00000000..7a6ebb2d --- /dev/null +++ b/local_tests/bid_async_design_review.txt @@ -0,0 +1,91 @@ +Nyash BID-FFI実装の型定義ファースト戦略について、特に非同期型の設計に関してレビューをお願いします。 + +【現在の設計】 +phase_9_75g_0_revised_type_first_approach.mdより抜粋: + +```rust +pub enum BidType { + // 基本型 + Bool, I32, I64, F32, F64, String, Bytes, + + // 複合型 + Array(Box), Map(Box, Box), + Option(Box), Result(Box, Box), + + // === 問題のある部分 === + Future(Box), // 非同期結果 + Stream(Box), // ストリーム +} +``` + +【懸念点】 +1. **Future/StreamはBidTypeに含めるべきか?** + - FFI境界でFutureそのものは渡せない + - C ABIは基本的に同期的 + - 非同期は実装の詳細であって値の型ではない? + +2. **現在のConnection trait設計** +```rust +trait Connection { + fn invoke(&self, ...) -> Result; + fn invoke_async(&self, ...) -> Result; + fn stream(&self, ...) -> Result; +} +``` + +【代替案の検討】 + +案A: メソッドのシェイプで表現 +```rust +pub enum MethodShape { + Sync, // 通常の同期呼び出し + Async, // Futureを返す + Stream, // Streamを返す +} + +pub struct Method { + pub shape: MethodShape, // ここで同期/非同期を区別 + pub returns: BidType, // 実際の値の型(Future抜き) +} +``` + +案B: ハンドル型として扱う +```rust +pub enum BidType { + // Future/Stream削除 + Handle(String), // "FutureHandle", "StreamHandle"等 +} +``` + +案C: 型階層を分ける +```rust +pub enum BidType { /* 値型のみ */ } +pub enum BidAsyncType { + Future(BidType), + Stream(BidType), +} +``` + +【質問】 + +1. **型システム設計** + - Future/StreamをBidTypeに含めるのは適切か? + - FFI境界での非同期処理の正しい表現方法は? + - 値型と実行モデルを混在させることの是非は? + +2. **実装戦略** + - 型定義ファースト戦略(全型を最初に定義)は妥当か? + - unimplemented!()での段階的実装は適切か? + - ビルドエラー回避とAPI安定性のトレードオフは? + +3. **FFI境界での非同期** + - C ABI経由での非同期処理の標準的な方法は? + - ポーリング vs コールバック vs ハンドル方式? + - WASM Component Modelではどう扱われている? + +4. **将来の拡張性** + - gRPC/RESTでの非同期は別の話? + - TransportTypeごとに非同期モデルが異なる場合の抽象化は? + - Rust async/awaitとの統合方法は? + +実装者(Rust中級者)が混乱しない、シンプルで拡張可能な設計を教えてください。特に「Future/StreamはBidTypeに含めるべきか」の判断をお願いします。 \ No newline at end of file diff --git a/local_tests/nyash_box_theory_final_review.txt b/local_tests/nyash_box_theory_final_review.txt new file mode 100644 index 00000000..37f0e05b --- /dev/null +++ b/local_tests/nyash_box_theory_final_review.txt @@ -0,0 +1,125 @@ +Nyash「箱理論」に基づくBID-FFI設計の最終レビューをお願いします。 + +【Nyashの箱理論(Everything is Box)】 +Nyashプログラミング言語は「Everything is Box」哲学を採用しており、すべてのデータと機能がBoxとして統一されています。 + +既存のBox型: +- StringBox, IntegerBox, BoolBox(基本型) +- ArrayBox, MapBox(コレクション) +- FutureBox(非同期処理) +- P2PBox(ネットワーク) +- FileBox(将来プラグインで提供予定) + +【設計の変遷】 +1. 最初の設計: 野心的すぎて複雑(gRPC/REST/P2P等全部) +2. ChatGPT提案: 一般的で正しいが、既存FutureBoxと二重実装になる +3. 最終設計: 箱理論準拠の最低設計 + +【最終設計案】 + +```rust +// 1. 型システム:プリミティブ + Handle統一 +pub enum BidType { + // プリミティブ(FFI境界で直接渡せる) + Bool, I32, I64, F32, F64, String, Bytes, + + // Everything is Box: すべてのBoxは統一Handle + Handle(String), // "StringBox:123", "FileBox:456", "FutureBox:789" + + // メタ型 + Void, + + // Phase 2以降(定義だけ) + Option(Box), + Result(Box, Box), + Array(Box), +} + +// 2. 既存Boxとの対応 +/* +Handle("StringBox:123") → 既存StringBox インスタンス +Handle("FileBox:456") → プラグインFileBox +Handle("FutureBox:789") → 既存FutureBox(非同期) +Handle("P2PBox:101") → 既存P2PBox +*/ + +// 3. 統一エントリーポイント +extern "C" fn nyash_plugin_invoke( + box_type_id: u32, // StringBox=1, FileBox=6等 + method_id: u32, // open=1, read=2等 + instance_id: u32, // Handle解析用 + args_ptr: *const u8, + args_len: usize, + result_ptr: *mut u8, + result_len: *mut usize, +) -> i32 + +// 4. Boxヘッダー統一 +#[repr(C, align(8))] +pub struct BoxHeader { + magic: u32, // "NYBX" + version: u16, + type_id: u32, // BoxTypeId + instance_id: u32, // インスタンス識別子 + ref_count: u32, // 非atomic(シングルスレッド中心) + flags: u32, // 拡張用 +} + +// 5. 既存Box統合 +pub struct NyashBoxRegistry { + instances: HashMap>>, +} + +impl NyashBoxRegistry { + fn call_method(&self, type_id: u32, instance_id: u32, method_id: u32, args: &[u8]) + -> Result, BidError> { + match type_id { + 1 => self.call_stringbox_method(...), // 既存StringBox + 6 => self.call_filebox_method(...), // プラグインFileBox + 7 => self.call_futurebox_method(...), // 既存FutureBox活用 + } + } +} +``` + +【設計原則】 +1. **箱理論準拠**: すべてのBoxが同じ仕組みで扱える +2. **既存資産活用**: FutureBox等の既存実装を最大活用 +3. **最低設計**: 動くものを最速で、複雑な機能は後回し +4. **二重実装回避**: 新しいBidFutureではなく既存FutureBox使用 +5. **シングルスレッド中心**: Nyashの現実に合わせた設計 + +【具体的な利点】 +- Nyashコード側は変更不要(local file = new FileBox("test.txt")) +- 既存StringBox, ArrayBox, FutureBoxと新しいプラグインFileBoxが同じ仕組み +- Handle("BoxType:id")による統一的なBox参照 +- 非同期はFutureBoxで、新実装不要 + +【質問】 + +1. **箱理論の技術的妥当性** + - すべてをHandle統一する設計は適切か? + - 既存BoxとプラグインBoxの統一的な扱いは可能か? + - FFI境界でのBox参照としてHandle("Type:id")は適切か? + +2. **最低設計のメリット・デメリット** + - プリミティブ + Handle のみの設計は十分か? + - 複雑な型(Array, Map等)を後回しにする判断は? + - Phase 1で基本動作、Phase 2で拡張の戦略は妥当か? + +3. **既存資産活用の是非** + - 新しいBidFutureを作らずFutureBox活用は正解か? + - 既存のStringBox等との統合アプローチは適切か? + - 二重実装回避 vs 設計の純粋性のトレードオフは? + +4. **実装現実性** + - 1週間でのPhase 1実装は現実的か? + - 既存インタープリターとの統合難易度は? + - Linux x86-64限定での最初の実装は妥当か? + +5. **将来拡張性** + - この基盤で将来のgRPC/REST対応は可能か? + - Transport抽象化への拡張パスは明確か? + - P2P(NyaMesh)統合への道筋は適切か? + +「箱理論」という独特な哲学を持つNyashにとって、一般的なFFI設計とは異なる特殊なアプローチが必要だと考えています。この「最低設計から始めて段階的拡張」の戦略についての専門的見解をお願いします。 \ No newline at end of file diff --git a/src/bid/bridge.rs b/src/bid/bridge.rs new file mode 100644 index 00000000..8374baa2 --- /dev/null +++ b/src/bid/bridge.rs @@ -0,0 +1,202 @@ +use crate::box_trait::NyashBox; +use super::{BidHandle, BidType, BidError}; +use std::sync::Arc; +use std::collections::HashMap; + +/// BID-FFI Bridge for Nyash Box types +/// Provides conversion between Nyash runtime values and BID handles +pub trait BidBridge { + /// Convert a Nyash Box to a BID handle + fn to_bid_handle(&self, registry: &mut BoxRegistry) -> Result; + + /// Get the BID type representation + fn bid_type(&self) -> BidType; +} + +/// Registry for managing Box instances and their handles +pub struct BoxRegistry { + /// Maps handle to Arc + handle_to_box: HashMap>, + + /// Next instance ID for each type + next_instance_id: HashMap, + + /// Reverse lookup: Arc pointer to handle + box_to_handle: HashMap, +} + +impl BoxRegistry { + pub fn new() -> Self { + Self { + handle_to_box: HashMap::new(), + next_instance_id: HashMap::new(), + box_to_handle: HashMap::new(), + } + } + + /// Register a Box and get its handle + pub fn register_box(&mut self, type_id: u32, boxed: Arc) -> BidHandle { + // Check if already registered by comparing Arc pointers + // We use the address of the Arc allocation itself as the key + let arc_addr = &*boxed as *const dyn NyashBox as *const () as usize; + if let Some(&handle) = self.box_to_handle.get(&arc_addr) { + return handle; + } + + // Generate new instance ID + let instance_id = self.next_instance_id.entry(type_id).or_insert(1); + let handle = BidHandle::new(type_id, *instance_id); + *instance_id += 1; + + // Register bidirectionally + self.handle_to_box.insert(handle, boxed.clone()); + self.box_to_handle.insert(arc_addr, handle); + + handle + } + + /// Retrieve a Box by its handle + pub fn get_box(&self, handle: BidHandle) -> Option> { + self.handle_to_box.get(&handle).cloned() + } + + /// Remove a Box from the registry + pub fn unregister(&mut self, handle: BidHandle) -> Option> { + if let Some(boxed) = self.handle_to_box.remove(&handle) { + let arc_addr = &*boxed as *const dyn NyashBox as *const () as usize; + self.box_to_handle.remove(&arc_addr); + Some(boxed) + } else { + None + } + } +} + +/// Convert Nyash Box to BID handle +pub fn box_to_bid_handle( + arc_box: &Arc, + registry: &mut BoxRegistry, +) -> Result<(BidType, BidHandle), BidError> { + // Downcast to specific box types + if let Some(_string_box) = arc_box.as_any().downcast_ref::() { + let handle = registry.register_box( + crate::bid::types::BoxTypeId::StringBox as u32, + arc_box.clone() + ); + Ok((BidType::Handle { type_id: 1, instance_id: handle.instance_id }, handle)) + } else if let Some(_integer_box) = arc_box.as_any().downcast_ref::() { + let handle = registry.register_box( + crate::bid::types::BoxTypeId::IntegerBox as u32, + arc_box.clone() + ); + Ok((BidType::Handle { type_id: 2, instance_id: handle.instance_id }, handle)) + } else { + Err(BidError::InvalidType) + } +} + +/// Convert BID handle back to Nyash Box +pub fn bid_handle_to_box( + handle: BidHandle, + registry: &BoxRegistry, +) -> Result, BidError> { + registry.get_box(handle) + .ok_or(BidError::InvalidHandle) +} + +/// Extract string value from a Box for TLV encoding +pub fn extract_string_value(arc_box: &Arc) -> Result { + if let Some(string_box) = arc_box.as_any().downcast_ref::() { + Ok(string_box.value.clone()) + } else { + Err(BidError::InvalidType) + } +} + +/// Extract integer value from a Box for TLV encoding +pub fn extract_integer_value(arc_box: &Arc) -> Result { + if let Some(integer_box) = arc_box.as_any().downcast_ref::() { + Ok(integer_box.value) + } else { + Err(BidError::InvalidType) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_box_registry() { + let mut registry = BoxRegistry::new(); + + // Create a mock box + let string_box = crate::boxes::string_box::StringBox::new("Hello"); + let arc_box: Arc = Arc::new(string_box); + + // Register it + let handle = registry.register_box(1, arc_box.clone()); + assert_eq!(handle.type_id, 1); + assert_eq!(handle.instance_id, 1); + + // Retrieve it + let retrieved = registry.get_box(handle).unwrap(); + assert_eq!(Arc::as_ptr(&retrieved), Arc::as_ptr(&arc_box)); + + // Register same box again should return same handle + let handle2 = registry.register_box(1, arc_box.clone()); + assert_eq!(handle, handle2); + } + + #[test] + fn test_string_box_bid_conversion() { + let mut registry = BoxRegistry::new(); + + // Create StringBox + let string_box = crate::boxes::string_box::StringBox::new("Test String"); + let arc_box: Arc = Arc::new(string_box); + + // Convert to BID handle + let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap(); + assert_eq!(handle.type_id, 1); // StringBox type ID + match bid_type { + BidType::Handle { type_id, .. } => assert_eq!(type_id, 1), + _ => panic!("Expected Handle type"), + } + + // Extract string value + let value = extract_string_value(&arc_box).unwrap(); + assert_eq!(value, "Test String"); + + // Round-trip test + let retrieved = bid_handle_to_box(handle, ®istry).unwrap(); + let retrieved_value = extract_string_value(&retrieved).unwrap(); + assert_eq!(retrieved_value, "Test String"); + } + + #[test] + fn test_integer_box_bid_conversion() { + let mut registry = BoxRegistry::new(); + + // Create IntegerBox + let integer_box = crate::boxes::integer_box::IntegerBox::new(42); + let arc_box: Arc = Arc::new(integer_box); + + // Convert to BID handle + let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap(); + assert_eq!(handle.type_id, 2); // IntegerBox type ID + match bid_type { + BidType::Handle { type_id, .. } => assert_eq!(type_id, 2), + _ => panic!("Expected Handle type"), + } + + // Extract integer value + let value = extract_integer_value(&arc_box).unwrap(); + assert_eq!(value, 42); + + // Round-trip test + let retrieved = bid_handle_to_box(handle, ®istry).unwrap(); + let retrieved_value = extract_integer_value(&retrieved).unwrap(); + assert_eq!(retrieved_value, 42); + } +} \ No newline at end of file diff --git a/src/bid/mod.rs b/src/bid/mod.rs index 076a4740..87bdec6e 100644 --- a/src/bid/mod.rs +++ b/src/bid/mod.rs @@ -6,12 +6,14 @@ pub mod tlv; pub mod error; pub mod metadata; pub mod plugin_api; +pub mod bridge; pub use types::*; pub use tlv::*; pub use error::*; pub use metadata::*; pub use plugin_api::*; +pub use bridge::*; /// BID-1 version constant pub const BID_VERSION: u16 = 1; diff --git a/src/boxes/integer_box.rs b/src/boxes/integer_box.rs index 65e9e966..13fd2fb4 100644 --- a/src/boxes/integer_box.rs +++ b/src/boxes/integer_box.rs @@ -38,6 +38,7 @@ */ use crate::box_trait::{NyashBox, BoxCore, BoxBase}; +use crate::bid::{BidBridge, BidHandle, BidType, BidError, BoxRegistry}; use std::any::Any; use std::fmt::Display; @@ -116,4 +117,23 @@ impl Display for IntegerBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } +} + +impl BidBridge for IntegerBox { + fn to_bid_handle(&self, registry: &mut BoxRegistry) -> Result { + use std::sync::Arc; + let arc_box: Arc = Arc::new(self.clone()); + let handle = registry.register_box( + crate::bid::types::BoxTypeId::IntegerBox as u32, + arc_box + ); + Ok(handle) + } + + fn bid_type(&self) -> BidType { + BidType::Handle { + type_id: crate::bid::types::BoxTypeId::IntegerBox as u32, + instance_id: 0 // Will be filled by registry + } + } } \ No newline at end of file diff --git a/src/boxes/string_box.rs b/src/boxes/string_box.rs index 9186023f..4de46668 100644 --- a/src/boxes/string_box.rs +++ b/src/boxes/string_box.rs @@ -28,6 +28,7 @@ * ``` */ use crate::box_trait::{NyashBox, BoxCore, BoxBase}; +use crate::bid::{BidBridge, BidHandle, BidType, BidError, BoxRegistry}; use std::any::Any; use std::fmt::Display; @@ -184,4 +185,23 @@ impl Display for StringBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.fmt_box(f) } +} + +impl BidBridge for StringBox { + fn to_bid_handle(&self, registry: &mut BoxRegistry) -> Result { + use std::sync::Arc; + let arc_box: Arc = Arc::new(self.clone()); + let handle = registry.register_box( + crate::bid::types::BoxTypeId::StringBox as u32, + arc_box + ); + Ok(handle) + } + + fn bid_type(&self) -> BidType { + BidType::Handle { + type_id: crate::bid::types::BoxTypeId::StringBox as u32, + instance_id: 0 // Will be filled by registry + } + } } \ No newline at end of file