freeze: macro platform complete; default ON with profiles; env consolidation; docs + smokes\n\n- Profiles: --profile {lite|dev|ci|strict} (dev-like default for macros)\n- Macro paths: prefer NYASH_MACRO_PATHS (legacy envs deprecated with warnings)\n- Selfhost pre-expand: auto mode, PyVM-only, add smokes (array/map)\n- Docs: user-macros updated; new macro-profiles guide; AGENTS freeze note; CURRENT_TASK freeze\n- Compat: non-breaking; legacy envs print deprecation notices\n

This commit is contained in:
Selfhosting Dev
2025-09-19 22:27:59 +09:00
parent 811e3eb3f8
commit da32455afc
192 changed files with 6454 additions and 2973 deletions

View File

@ -0,0 +1,108 @@
# Nyash 設計図(アーキテクチャ概要)
最終更新: 2025-08-21Phase 9.78b〜3 反映)
本書はNyashの実装設計を、バックエンド共通で理解できる単一ドキュメントとしてまとめたもの。言語コア、MIR、インタープリター/VM統合、ランタイム/プラグイン、ビルドと配布の観点を俯瞰する。
## レイヤー構成
- 構文/AST: `tokenizer`, `parser`, `ast`
- モデル層: `core::model`BoxDeclaration等の純粋データ
- ランタイム層: `runtime`UnifiedBoxRegistry, PluginLoader, NyashRuntime
- 実行戦略層: `interpreter`AST実行/ `mir`+`backend::vm`MIR実行/ 将来 `wasm`/`llvm`
- 付帯基盤: `box_factory`, `instance_v2`, `scope_tracker`, `boxes/*`, `stdlib`
## コア概念
- Everything is Box: すべての値はBoxビルトイン、ユーザー定義、プラグイン
- 統一コンストラクタ: `birth(args)`packはビルトイン継承内部用に透過化
- 明示デリゲーション: `box Child from Parent``from Parent.method()`
- 厳密変数宣言/スコープ安全: `local`, `outbox`、スコープ退出時の`fini`一元化
## モデル層core::model
- `BoxDeclaration``interpreter` から分離し `core::model` に移動
- name, fields, methods, constructors(birth/N), extends, implements, type_parameters
- 実行戦略非依存の純粋データ
## ランタイム層runtime
- `NyashRuntime`
- `box_registry: UnifiedBoxRegistry`(ビルトイン/ユーザー定義/プラグインを順序付き検索)
- `box_declarations: RwLock<HashMap<String, BoxDeclaration>>`
- BuilderでDI`with_factory`可能。Interpreter/VMから共有・注入できる
- `UnifiedBoxRegistry`
- `Arc<dyn BoxFactory>` の列で優先解決builtin > user > plugin
- `create_box(name, args)` の統一エントリ
- `BoxFactory`
- builtin: 全ビルトインBoxの生成
- user_defined: `BoxDeclaration`に基づき`InstanceBox`生成birthは実行戦略側で
- plugin: BID-FFI準拠のプラグインBox将来のExternCall/MIR接続
## 実行戦略Interpreter / VM
- InterpreterAST実行
- `SharedState` は段階的に分解し、宣言等を `NyashRuntime` に寄せ替え
- 生成は統一レジストリへ委譲、コンストラクタ実行は`birth/N`のASTを実行
- VM (MIR実行)
- `VM::with_runtime(runtime)` でDI、`NewBox``runtime.box_registry.create_box`
- `ScopeTracker`でスコープ退出時に`fini`InstanceBox/PluginBox
- birth/メソッドのMIR関数化Phase 2/3
- Builderが `new``NewBox` + `BoxCall("birth")` に展開
- Box宣言の `birth/N` と通常メソッド(`method/N`)を `"{Box}.{name}/{N}"` のMIR関数へ関数化
- VMの`BoxCall``InstanceBox` なら該当MIR関数へディスパッチme + 引数)
## MIR中間表現
- 目的: バックエンド共通の最適化/実行基盤VM/LLVM/WASM/JIT
- Builder
- AST→MIR lowering。`ASTNode::New``NewBox`(+ `BoxCall("birth")`)
- `ASTNode::BoxDeclaration``constructors` / `methods` をMIR関数化
- if/loop/try-catch/phi等の基本構造を提供
- VM
- Stackベースの簡易実装→順次強化中
- `call_function_by_name` による関数呼び出しフレームの最小実装
## インスタンス表現InstanceBox
- 統一フィールド`fields_ng: HashMap<String, NyashValue>`
- メソッドASTを保持ユーザー定義時
- `fini()`による簡易解放将来、リソースBoxは明示やRAII連携
## ライフサイクル統一fini
- Interpreter: スコープ復帰時に`InstanceBox.fini()`等を呼ぶ
- VM: `ScopeTracker`で関数入退出時に登録Boxを`fini`
## プラグインBID-FFI
- v2ローダ`runtime::plugin_loader_v2`)とテスター完備
- 目標: MIRの`ExternCall`→ローダに接続し、VM/LLVM/WASMで共通パス
## Runner/ビルド
- VMモード:
1) ASTパース
2) ランタイムにBox宣言収集 + UserDefinedBoxFactory登録
3) MIRコンパイル
4) VMを`with_runtime`で起動し実行
## 進行中フェーズと方針
- Phase 9.78b: Interpreter/VMのモデル・ランタイム共有完了
- Phase 2/3実質: birth/メソッドのMIR関数化とVMディスパッチ実装済・基本動作
- 次:
- BoxCall→Callへの段階的置換型決定済みのとき
- ExternCallの実装VM→プラグイン
- WASM/LLVMバックエンドへMIR関数の共有
## 参考ファイル
- `src/core/model.rs`BoxDeclaration
- `src/runtime/nyash_runtime.rs`NyashRuntime
- `src/box_factory/*`builtin/user_defined/plugin
- `src/mir/*`builder/instruction/function/etc.
- `src/backend/vm.rs`VM実行
- `src/interpreter/*`AST実行

View File

@ -0,0 +1,118 @@
# Dynamic Plugin Flow (VM × Registry × PluginLoader v2)
最終更新: 2025-08-23
目的
- Nyash 実行時に、MIR→VM→Registry→Plugin の呼び出しがどう流れるかを図解・手順で把握する。
- TLVエンコード、ResultBoxの扱い、Handleのライフサイクル、nyash.tomlとの連携を1枚で理解する。
## ハイレベル流れ(シーケンス)
```
Nyash Source ──▶ MIR (Builder)
│ (BoxCall/NewBox/…)
VM Executor
│ (BoxCall dispatch)
├─ InstanceBox → Lowered MIR 関数呼び出し
├─ BuiltinBox → VM内ディスパッチ
└─ PluginBoxV2 → PluginLoader v2
│ (nyash.toml を参照)
Invoke (TLV)
Plugin (lib*.so)
│ (戻り値をTLVで返却)
Loader でデコード
│ (returns_result/Handle/型)
NyashBox (ResultBox/PluginBoxV2/基本型)
VM に復帰
```
## 主要構成要素
- MIR: `MirInstruction::{BoxCall, NewBox, …}` で外部呼び出し箇所を明示。
- VM: `src/backend/vm.rs`
- InstanceBoxは `{Class}.{method}/{argc}` のLowered関数へ呼び出し
- BuiltinはVM内の簡易ディスパッチ
- PluginBoxV2は Loader v2 へ委譲
- Registry/Runtime: `NyashRuntime` + `box_registry` + `plugin_loader_v2`
- `nyash.toml``libraries.*` を読み込み、Box名→ライブラリ名、type_id、method_id等を集約
## NewBox生成
1) MIRの `NewBox { box_type, args }`
2) VM: `runtime.box_registry``box_type` を問い合わせ
3) PluginBoxの場合、Loader v2が `birth(method_id=0)` を TLV で呼び出し
4) Pluginは `type_id` と新規 `instance_id` を返却 → Loader は `PluginBoxV2` を構築
5) VMは `ScopeTracker` に登録(スコープ終了で `fini` を呼ぶ)
## BoxCallメソッド呼び出し
- InstanceBox: Lowered関数 `{Class}.{method}/{argc}` を MIR/VM内で実行
- Builtin: VM内の `call_box_method` で対応StringBox.length 等)
- PluginBoxV2: Loader v2 の `invoke_instance_method` で TLV を組み立てて呼び出し
## TLVType-Length-Value
- ヘッダ: `u16 ver=1`, `u16 argc`
- 各引数: `u8 tag`, `u8 reserved`, `u16 size`, `payload`
- 主な tag:
- 2 = i32 (size=4)
- 6 = string, 7 = bytes
- 8 = Handle(BoxRef) → payload = `u32 type_id || u32 instance_id`
- 9 = void (size=0)
## 戻り値のマッピング(重要)
- `returns_result=false`
- tag=8 → PluginBoxV2Handle
- tag=2 → IntegerBox、tag=6/7 → StringBox、tag=9 → void
- `returns_result=true`ResultBoxで包む
- tag=8/2 → `Result.Ok(value)`
- tag=6/7 → `Result.Err(ErrorBox(message))`Netプラグインなどがエラー文字列を返却
- tag=9 → `Result.Ok(void)`
補足
- VM内で ResultBox の `isOk/getValue/getError` をディスパッチ済み
- `toString()` フォールバックにより任意の Box を安全に文字列化可能
## HandleBoxRefのライフサイクル
- Loaderは `(type_id, instance_id)``PluginBoxV2` としてラップ
- `share_box()` は同一インスタンス共有、`clone_box()` はプラグインの birth を呼ぶ(設計意図による)
- `fini``ScopeTracker` または Drop で保証(プラグインの `fini_method_id` を参照)
## 具体例HttpClientBox.get
1) Nyash: `r = cli.get(url)`
2) MIR: `BoxCall`returns_result=true
3) VM→Loader: TLVurl = tag=6
4) Loader→Plugin: `invoke(type_id=HttpClient, method_id=get)`
5) Plugin:
- 接続成功: `Handle(HttpResponse)` を返す → Loaderは `Result.Ok(PluginBoxV2)`
- 接続失敗: `String("connect failed …")` を返す → Loaderは `Result.Err(ErrorBox)`
6) Nyash: `if r.isOk() { resp = r.getValue() … } else { print(r.getError().toString()) }`
## nyash.toml 連携
- 例: `libraries."libnyash_net_plugin.so".HttpClientBox.methods.get = { method_id = 1, args=["url"], returns_result = true }`
- Loaderは `method_id``returns_result` を参照し、TLVと戻り値のラップ方針を決定
- 型宣言args/kindにより、引数のTLVタグ検証を実施不一致は InvalidArgs
## デバッグTips
- `NYASH_DEBUG_PLUGIN=1`: VM→Plugin の TLV ヘッダと先頭64バイトをプレビュー
- `NYASH_NET_LOG=1 NYASH_NET_LOG_FILE=net_plugin.log`: Netプラグイン内部ログ
- `--dump-mir --mir-verbose`: if/phi/return などのMIRを確認
- `--vm-stats --vm-stats-json`: 命令使用のJSONを取得hot pathの裏取りに
## 将来の整合・改善
- nyash.toml に ok側の戻り型例: `ok_returns = "HttpResponseBox"`)を追加 → Loader判定の厳密化
- Verifier強化: use-before-def across merge の検出phi誤用を早期に発見
- BoxCall fast-path の最適化hot path最優先
関連
- `docs/reference/plugin-system/net-plugin.md`
- `docs/reference/architecture/mir-to-vm-mapping.md`
- `docs/reference/architecture/mir-26-instruction-diet.md`
See also
- `docs/examples/http_result_patterns.md` - HTTPのResult挙動unreachable/404/500のE2E例
- `docs/VM_README.md` - VM統計とプラグイン周りの既知制約

View File

@ -0,0 +1,98 @@
# MIR 26-Instruction Diet (Agreed Final Set)
Goal
- Converge on a lean, proven instruction set guided by vm-stats and E2E.
- Preserve hot paths, demote meta, fold type ops, reserve room for growth.
Agreed Final Set (26)
1) Const
2) Copy
3) Load
4) Store
5) BinOp
6) UnaryOp
7) Compare
8) Jump
9) Branch
10) Phi
11) Return
12) Call
13) BoxCall
14) NewBox
15) ArrayGet
16) ArraySet
17) RefNew
18) RefGet
19) RefSet
20) Await
21) Print
22) ExternCall (keep minimal; prefer BoxCall)
23) TypeOp (unify TypeCheck/Cast)
24) WeakRef (unify WeakNew/WeakLoad)
25) Barrier (unify BarrierRead/BarrierWrite)
26) Reserve (future async/error instr)
Hot/Core (keep)
- Data: Const, Copy, Load, Store
- ALU: BinOp, UnaryOp, Compare
- Control: Jump, Branch, Phi, Return
- Calls: Call, BoxCall
- Objects: NewBox
- Arrays: ArrayGet, ArraySet
Likely Keep (usage-dependent)
- Refs: RefNew, RefGet, RefSet (seen in language features; keep unless stats prove cold)
- Async: Await (FutureNew/Set can be Box/APIs)
Meta (demote to build-mode)
- Debug, Nop, Safepoint
Type Ops (fold)
- TypeCheck, Cast → fold/verify-time or unify as a single TypeOp (optional).
External (unify)
- ExternCall → prefer BoxCall; keep ExternCall only where required.
Extended/Reserve
- Weak*: WeakNew, WeakLoad
- Barriers: BarrierRead, BarrierWrite
- 2 Reserve IDs for future async/error instrumentation
Mapping Notes
- HTTP E2E shows BoxCall/NewBox dominate (3342%), then Const/NewBox, with Branch/Jump/Phi only in error flows.
- FileBox path similarly heavy on BoxCall/NewBox/Const.
- Implication: invest into BoxCall fast path and const/alloc optimization.
Migration Strategy
1) Introduce an experimental feature gate that switches TypeCheck/Cast to TypeOp or folds them.
2) Demote Debug/Nop/Safepoint under non-release builds.
3) Keep ExternCall available, route new external APIs via BoxCall when possible.
4) Track Weak*/Barrier usage; unify under WeakRef/Barrier. Graduate dedicated ops only if vm-stats shows recurring use.
Mapping from Current → Final
- TypeCheck, Cast → TypeOp
- WeakNew, WeakLoad → WeakRef
- BarrierRead, BarrierWrite → Barrier
- (Keep) ExternCall, but prefer BoxCall where possibleExternCall は最小限に)
- (Keep) Debug/Nop/Safepoint as meta under non-release builds命令セット外のビルドモード制御に降格
Verification
- Add MIR verifier rule: use-before-def across merges is rejected (guards phi misuse).
- Add snapshot tests for classic if-merge returning phi.
Appendix: Rationale
- Smaller ISA simplifies VM fast paths and aids JIT/AOT later.
- Data shows hot paths concentrated on calls, const, alloc; control sparse and localized.
- Folding type ops reduces interpreter/VM dispatch; verification handles safety.
---
Feature Flags (Cargo)
- Deprecated flags (unified by default):
- `mir_typeop_poc`: no-op now. Builder always emits `TypeOp(Check/Cast)` instead of legacy `TypeCheck/Cast`
- `mir_refbarrier_unify_poc`: no-op now. Builder always emits `WeakRef/Barrier` instead of legacy `WeakNew/WeakLoad/BarrierRead/Write`
- Optimizer has a Pass 0 that normalizes any残存の旧命令 → 統合命令(安全ネット)。
Status (2025-08-23)
- Flags declared in `Cargo.toml` (off by default)
- PoC design doc: `docs/development/proposals/mir-typeop-weakref-barrier-poc.md`
- Next: land builder/VM mapping behind flags, add snapshot tests; no behavior change with flags off

View File

@ -0,0 +1,218 @@
# MIR → VM Mapping (Draft)
最終更新: 2025-08-23
目的: 生成されたMIR命令がVMでどう実行されるかを1枚で把握し、欠落や暫定実装を洗い出す。26命令ダイエット検討の足場にする。
記法: 状態 = Implemented / Partial / No-op / TODO。
## コア命令
- Const: Implemented
- 定数を `VMValue` に格納。
- BinOp: Partial
- Integer: Add/Sub/Mul/Div 実装Divは0割チェック。他の算術/論理/ビット演算は TODO。
- String: `+` 連結のみ対応。その他は TypeError。
- UnaryOp: Partial
- `Neg`(int), `Not`(bool) のみ。`BitNot` は TODO。
- Compare: Partial
- Integer/ String の Eq/Ne/Lt/Le/Gt/Ge 対応。Bool は Eq/Ne のみ。
- Void は値を持たないため、比較は Eq/Ne のみ定義。
- `Void == Void` は true、`Void != Void` は false
- `Void == X` は false、`Void != X` は true順序比較は TypeError
- Load / Store: Implemented
- 現状はVM内の値スロット操作簡易
- Copy: Implemented
- 値コピー+クラス名/内部参照印の伝播。
## 制御フロー
- Branch / Jump / Return: Implemented
- Branchの条件は `Bool` を期待。以下の動的変換を許容:
- `Integer` → 非ゼロは true
- `BoxRef(BoolBox)` → 中身のbool
- `BoxRef(VoidBox)` → falsenullish false
- Phi: Implemented
- `LoopExecutor` による選択実装前BB情報を利用
## 呼び出し/Box関連
- Call: Implemented
- 関数名を `Const String` として解決しVM内ディスパッチ。
- BoxCall: Partial
- InstanceBox: `{Class}.{method}/{argc}` へ降格呼び出しMIR関数
- PluginBoxV2: cfg(feature="plugins")下でLoader経由invoke引数: NyashBox配列
- Builtinの簡易ディスパッチ: `StringBox.length/substr/concat`, `IntegerBox.toString/abs` 等の最小対応。
- birth 特例: user-definedの `birth` はMIR関数へ直呼。
- NewBox: Implemented
- `runtime.box_registry` から生成。`scope_tracker` に登録。クラス名マップ更新。
- TypeCheck: No-op (常にtrue)
- TODO: 正式な型チェックに置換。
- Cast: No-op (コピー)
- TODO: 正式な型変換に置換。
## 配列
- ArrayGet: ArrayBox.get(index) を呼び出し、戻り値を格納VM対応済み
- ArraySet: ArrayBox.set(index, value) を呼び出しVM対応済み
## デバッグ/出力
- Debug: No-op性能優先
- Print: Implemented`to_string()`して標準出力)
- Nop: No-op
## 例外/安全ポイント
- Throw: Partial
- 例外値を表示してVMErrorで中断。ハンドラ探索なし。
- Catch: No-op
- 例外値スロットを `Void` セットのみ。制御遷移の実装は未対応。
- Safepoint: No-op
## 参照/弱参照/バリア
- RefNew / RefGet / RefSet: Partial
- `object_fields` に簡易格納。`object_class``box_declarations` を用いた可視性public/private簡易検査あり。
- WeakNew / WeakLoad: No-op相当通常コピー/取得と同値)
- TODO: 実際の弱参照生存判定を導入。
- BarrierRead / BarrierWrite: No-op
- 効果注釈のみ(将来の最適化/並行実行基盤に備えた形)。
## 非同期
- FutureNew / FutureSet / Await: Implemented
- `boxes::future::FutureBox` を利用し、同期ブロッキングで結果取得。
## 外部呼び出し
- ExternCall: Implemented
- `runtime::get_global_loader_v2().extern_call(iface, method, args)` にルーティング。Some/Noneで戻り値void扱いも考慮。
---
## Result / Err / HandleBoxRef 取り扱い(重要)
目的: プラグイン呼び出しBoxCall→PluginLoaderV2およびVM内の戻り値で、ResultとBoxRefHandleを正しく扱うための合意事項。
- HTTP系Netプラグインの約束
- unreachable接続不可/タイムアウト等): `Result.Err(ErrorBox)` にマップする。
- HTTP 404/500 等のステータス異常: `Result.Ok(Response)` として返す(アプリ層で扱う)。
- FileBox等のVoid戻り
- `close()` のような副作用のみのメソッドは `Ok(Void)` を返す。VMではVoidの実体は持たない。
- HandleBoxRef戻り値
- プラグインは TLV tag=8Handle`type_id:u32, instance_id:u32` を返す。
- Loader は返り値の `type_id` に対応する正しい `fini_method_id` を設定し、`PluginBoxV2` を生成してVMへ返す。
- 注意: 生成元のBoxと返り値のBoxの型が異なるケースがあるため、「呼び出し元のfini値を流用しない」。必ず返り値 `type_id` を基に解決する。
- Resultの整合
- VMは `Result<T, ErrorBox>` をネイティブ表現し、`match` 等で分岐可能。
- `Ok(Void)``match Ok(_)` と等価に扱えるVoidは値を持たない
参考: TLV/Loader仕様は `docs/reference/plugin-system/ffi-abi-specification.md``plugin-tester.md` を参照。
---
## 既知の課題(抜粋)
- BinOp/UnaryOp/Compare の型拡張浮動小数・Bool/Box等
- ArrayGet/ArraySet の実装。
- TypeCheck/Cast の正規化(型表現と整合)。
- 例外ハンドリングThrow/Catchの制御フロー接続
- WeakRef/Barrier の実体化(必要性評価の上、命令ダイエット候補)。
- PluginBoxV2 のVM側統合強化引数/戻り値のTLV全型対応、Handle戻り値→BoxRef化
Verifier検証に関する追加事項方針
- use-before-def across merge の強化: merge後にphiが未使用/未定義を選択するパスを禁止。
- if-merge の戻り: retはphiのdstを返す実装済み
- TypeOpTypeCheck/Castと命令の整合: Verifierで型注釈に基づく不一致を検出。
## VM統計計測
- `--vm-stats` / `--vm-stats-json` で命令ごとの使用回数と時間(ms)を出力。
- ホット命令抽出によりダイエット候補を定量化。
---
## 実測結果サマリー(初回プローブ)
出所: `local_tests/vm_stats_hello.json`, `local_tests/vm_stats_loop.json`, `simple_math.nyash`
- ループ系139命令 / 0.158ms)トップ:
- Const: 25, BoxCall: 23, NewBox: 23, BinOp: 11, Branch: 11, Compare: 11, Jump: 11, Phi: 11, Safepoint: 11
- 所見: ループloweringで Branch/Jump/Phi/Safepoint が並び、Box初期化とBoxCallが多い。
- Hello系6命令: 期待どおりの最小構成Const/Print/Return中心
- simple_math18命令: BinOpの使用を確認整数加減乗除
補足:
- Safepoint はMIR上で挿入されるが、VMではNo-op計測には現れる
- NewBox/BoxCall が上位に入るため、命令セットから外すのは不可(コア扱い)。
- Compare/Branch/Jump/Phi は制御フローのコア。26命令の中核として維持が妥当。
## 実測統計2025-08-23
出所: vm-stats正常系HTTP異常系HTTPFileBox
- 正常系HTTP40命令
- BoxCall: 1742.5%/ Const: 1230%/ NewBox: 922.5%/ Return: 1 / Safepoint: 1
- 異常系HTTP21命令 = 正常の52.5%
- BoxCall: 733.3%/ Const: 628.6%/ NewBox: 314.3%
- Branch: 1 / Jump: 1 / Phi: 1エラーハンドリング特有/ Return: 1 / Safepoint: 1
- FileBox44命令
- BoxCall: 1738.6%/ Const: 1329.5%/ NewBox: 1227.3%/ Return: 1 / Safepoint: 1
設計含意:
- BoxCallが常に最頻出33〜42%)。呼び出しコスト最適化が最優先。
- Const/NewBoxが次点。定数・生成の最適化定数畳み込み軽量生成・シェアリングが効果的。
- 異常系は早期収束で命令半減。if/phi修正は実戦で有効Branch/Jump/Phiが顕在化
補足HTTP 404/500の比較
- 404"Not Found"、500"Internal Server Error"ともに同一プロファイル合計55命令
- 内訳例: BoxCall: 23 / Const: 16 / NewBox: 12 / BinOp: 2 / Return: 1 / Safepoint: 1
- ステータス差はアプリ層で完結し、VM命令の分布には有意差なし設計の意図どおり
## 26命令ダイエット検討のたたき台
方針: 「命令の意味は保ちつつ集約」。代表案:
- 維持: Const / Copy / Load / Store / BinOp / UnaryOp / Compare / Jump / Branch / Phi / Return / Call / BoxCall / NewBox / ArrayGet / ArraySet
- 参照: RefNew / RefGet / RefSetWeak/Barrierは拡張枠へ
- 非同期: AwaitFutureNew/SetはBox APIへ寄せる案も可
- I/O: Print は開発モード限定 or ExternCall統合ExternCall自体はBoxCallへ統合方針
- 調整: TypeCheck/Cast はVerifier/型系に寄せる(命令から外す or 1命令に集約
- Debug/Nop/Safepoint: メタ扱い(命令数からは外す)
次ステップ:
- サンプル/テストをVMで実行し、`vm-stats`結果から実使用命令セットを抽出。
- 上記案に対し互換影響を洗い出し、段階移行(エイリアス→削除)を設計。
---
## 26命令ダイエットの指針実測反映
- 維持(ホット・コア): BoxCall / NewBox / Const / BinOp / Compare / Branch / Jump / Phi / Return / Copy / Load / Store / Call
- 実装方針: ExternCallは原則BoxCallへ集約必要なら限定的に残す
- メタ降格: Debug/Nop/Safepointビルドモードで制御
- 型系: TypeCheck/Castは折りたたみ or 検証時に処理1命令に集約も可
- 参照/弱参照/バリア: 需要ベースで拡張枠へvm-statsに登場しない限りコア外
提案(ドラフト):
- コア候補(例): Const, Copy, Load, Store, BinOp, UnaryOp, Compare, Jump, Branch, Phi, Return, Call, BoxCall, NewBox, ArrayGet, ArraySet, RefNew, RefGet, RefSet, Await, Print, ExternCall(集約可), TypeOp(=TypeCheck/Cast), 予備2将来枠
- 予備はWeak*/Barrierや将来の非同期拡張等に割当実測で常用化したら昇格
---
## E2E更新VM経由の実働確認
成功ケースVM:
- FileBox.open/write/read: 引数2個のTLVエンコードString, Stringで成功HELLO往復
- FileBox.copyFrom(handle): Handle引数tag=8, size=8, type_id+instance_idで成功
- HttpClientBox.get + HttpServerBox: 基本GETの往復ResultBox経由でResponse取得
- HttpClientBox.post + headers: Status/ヘッダー/ボディをVMで往復確認
- HttpClientBox.get unreachable: 接続失敗時はResult.Err(ErrorBox)ローダーがstring/bytesをErrにマップ
- HTTP 404/500: Result.Ok(Response)ステータスはResponse上に保持
デバッグ小技:
- `NYASH_DEBUG_PLUGIN=1` で VM→Plugin 呼び出しTLVの ver/argc/先頭バイトをダンプ
- Netプラグインの内部ログ: `NYASH_NET_LOG=1 NYASH_NET_LOG_FILE=net_plugin.log`
## 型・Null/Void・比較の扱い更新
- NullはVM内部でVoidに折りたたみ`Const Null → VMValue::Void`)。
- VoidとNullは同一視されない等価比較は `Void == Void` のみtrue
- Compareの対応
- 整数/文字列: Eq/Ne/Lt/Le/Gt/Ge実装済
- 真偽値: Eq/Ne のみ
- Void: Eq/Ne のみVoid==Voidはtrue、それ以外はfalse
- 浮動小数点: Eq/Ne/Lt/Le/Gt/Ge新規
- 整数と浮動小数点の混在: 双方をf64比較で対応新規
## TypeOpPoC
- 目的: TypeCheck/Castの統合。
- Check: 最小意味論を実装Integer/Float/Bool/String/Void/Box名に対し一致判定
- Cast: 当面コピー等価(将来の変換方針に備える)。
- me 参照
- メソッド/コンストラクタlowering時は `%0` にマップ(パラメータ)。
- それ以外の文脈ではフォールバックとして `Const "__me__"` を一度だけ発行して変数マップに保持し、以降の `me` は同一ValueIdを参照RefGet/RefSetの整合性を保証

View File

@ -0,0 +1,235 @@
# 🤖 Nyash MIR (Mid-level Intermediate Representation) - 統合リファレンス
*26命令削減実装中・ChatGPT5仕様準拠 - 2025年8月17日版*
## 🚨 **重要: MIR命令削減プロジェクト進行中**
**現状**: 35命令実装175%膨張)→ **目標**: 26命令ChatGPT5仕様
**Gemini評価**: 削減戦略「極めて健全」「断行推奨」
## 🎯 **MIR概要**
Nyash MIRは、Everything is Box哲学を基盤とした中間表現です。現在35命令が実装され、インタープリター・VM・WASM・AOTの全バックエンドで統一された実行を実現します。
### **🌟 主要特徴**
- **Everything is Box**: 全データがBoxオブジェクトとして統一表現
- **Effect System**: pure/mut/io/control効果による最適化基盤
- **所有権管理**: 強参照森ownership forest+ weak参照システム
- **非同期対応**: Future/Bus操作の言語レベル統合
- **FFI/ABI統合**: ExternCall命令による外部API統一呼び出し
## 🏗️ **命令分類 - 35命令全体系**
### **Tier-0: コア演算 (8命令)**
基本的な計算・制御フロー命令
| 命令 | 形式 | 効果 | 説明 |
|------|------|------|------|
| `Const` | `%dst = const value` | pure | 定数値生成 |
| `BinOp` | `%dst = %lhs op %rhs` | pure | 二項演算(+,-,*,/等) |
| `UnaryOp` | `%dst = op %operand` | pure | 単項演算not, neg等 |
| `Compare` | `%dst = %lhs cmp %rhs` | pure | 比較演算(==, !=, < |
| `Branch` | `br %cond -> %then, %else` | control | 条件分岐 |
| `Jump` | `jmp %target` | control | 無条件ジャンプ |
| `Return` | `ret %value?` | control | 関数戻り |
| `Phi` | `%dst = phi [%val1:%bb1, %val2:%bb2]` | pure | SSA φ関数 |
### **Tier-1: メモリ・関数操作 (8命令)**
メモリアクセス関数呼び出し型操作
| 命令 | 形式 | 効果 | 説明 |
|------|------|------|------|
| `Load` | `%dst = load %ptr` | pure | メモリ読み取り |
| `Store` | `store %value -> %ptr` | mut | メモリ書き込み |
| `Call` | `%dst = call %func(%args...)` | context | 関数呼び出し |
| `BoxCall` | `%dst = %box.method(%args...)` | context | Boxメソッド呼び出し |
| `NewBox` | `%dst = new_box "Type"(%args...)` | mut | Box生成 |
| `TypeCheck` | `%dst = type_check %box "Type"` | pure | 型チェック |
| `Cast` | `%dst = cast %value as Type` | pure | 型変換 |
| `Copy` | `%dst = copy %src` | pure | 値コピー |
### **Tier-2: 配列・デバッグ・制御 (7命令)**
配列操作デバッグ例外処理
| 命令 | 形式 | 効果 | 説明 |
|------|------|------|------|
| `ArrayGet` | `%dst = %array[%index]` | pure | 配列要素取得 |
| `ArraySet` | `%array[%index] = %value` | mut | 配列要素設定 |
| `Debug` | `debug %value "message"` | io | デバッグ出力 |
| `Print` | `print %value` | io | コンソール出力 |
| `Nop` | `nop` | pure | 無操作 |
| `Throw` | `throw %exception` | control | 例外発生 |
| `Catch` | `catch %type -> %handler` | control | 例外捕捉 |
### **Tier-3: 参照・非同期・外部API (12命令)**
所有権管理非同期処理外部連携
| 命令 | 形式 | 効果 | 説明 |
|------|------|------|------|
| `Safepoint` | `safepoint` | io | セーフポイント |
| `RefNew` | `%dst = ref_new %box` | pure | 参照生成 |
| `RefGet` | `%dst = ref_get %ref.field` | pure | 参照経由読み取り |
| `RefSet` | `ref_set %ref.field = %value` | mut | 参照経由書き込み |
| `WeakNew` | `%dst = weak_new %box` | pure | weak参照生成 |
| `WeakLoad` | `%dst = weak_load %weak_ref` | pure | weak参照読み取り |
| `BarrierRead` | `barrier_read %ptr` | io | メモリバリア読み |
| `BarrierWrite` | `barrier_write %ptr` | io | メモリバリア書き |
| `FutureNew` | `%dst = future_new %value` | mut | Future生成 |
| `FutureSet` | `future_set %future = %value` | mut | Future値設定 |
| `Await` | `%dst = await %future` | io | Future待機 |
| `ExternCall` | `%dst = extern_call iface.method(%args...)` | context | 外部API呼び出し |
## 🎭 **Effect System - 4種類の効果**
### **効果分類と最適化ルール**
```rust
pub enum Effect {
Pure, // 再順序化可能、共通部分式除去可能
Mut, // 同一リソースで順序保持必要
Io, // 全順序保持必要(副作用あり)
Control, // 制御フロー変更
}
```
### **効果別命令分類**
#### **Pure命令 (15命令)**
```
Const, BinOp, UnaryOp, Compare, Phi, Load, TypeCheck, Cast, Copy,
ArrayGet, Nop, RefNew, RefGet, WeakNew, WeakLoad
```
#### **Mut命令 (7命令)**
```
Store, NewBox, ArraySet, RefSet, FutureNew, FutureSet
```
#### **Io命令 (6命令)**
```
Debug, Print, Safepoint, BarrierRead, BarrierWrite, Await
```
#### **Control命令 (4命令)**
```
Branch, Jump, Return, Throw, Catch
```
#### **Context依存命令 (3命令)**
```
Call, BoxCall, ExternCall
```
*効果は呼び出し先に依存*
## 🔧 **重要なMIR実装詳細**
### **ExternCall命令 - FFI/ABI統合**
```rust
ExternCall {
dst: Option<ValueId>,
iface_name: String, // "env.console", "nyash.math"等
method_name: String, // "log", "sqrt"等
args: Vec<ValueId>,
effects: EffectMask, // BID仕様から決定
}
```
**用途**: ブラウザーAPIネイティブライブラリプラグインの統一呼び出し
### **所有権管理システム**
#### **強参照森Ownership Forest**
- 各Boxは最大1つの強参照を持つin-degree 1
- 強参照による循環は禁止DAG構造保証
- `NewBox`, `RefSet`で所有権移転
#### **weak参照システム**
- 所有権を持たない軽量参照
- `WeakNew`で生成`WeakLoad`で安全アクセス
- 参照先削除時は自動的にnull化
### **非同期処理 - Future操作**
```mir
%future = FutureNew %initial_value // Future生成
FutureSet %future = %result // 結果設定
%value = Await %future // 結果取得(ブロッキング)
```
## 🚀 **バックエンド別対応状況**
### **実装済みバックエンド**
| バックエンド | 対応命令数 | 主要用途 | 特徴 |
|-------------|-----------|----------|------|
| **Interpreter** | 35/35 | デバッグ開発 | 全命令完全対応 |
| **VM** | 35/35 | 高速実行 | レジスタベース |
| **WASM** | 30/35 | Web配布 | ExternCallimport対応 |
| **AOT準備** | 計画中 | ネイティブ | LLVM IR生成予定 |
### **バックエンド固有の最適化**
#### **VM バックエンド**
- レジスタベース実行
- 局所最適化ローカルBus elision
- 直接スレッド化
#### **WASM バックエンド**
- メモリ線形化文字列は (ptr,len)
- ExternCall import宣言自動生成
- ホスト側JavaScript連携
## 📊 **MIR最適化パス**
### **Pure命令最適化**
- **共通部分式除去 (CSE)**: 同一pure計算の除去
- **不変コード移動 (LICM)**: ループ外移動
- **定数畳み込み**: コンパイル時計算
### **Effect-aware最適化**
- **Mut順序保持**: 同一リソースアクセス順序維持
- **Io順序保持**: 全Io命令の順序保証
- **Bus elision**: ローカル通信の直接アクセス化
## 🧪 **テスト・検証**
### **MIR検証項目**
- [ ] **所有権森検証**: strong in-degree 1
- [ ] **強循環禁止**: 強参照のDAG構造保証
- [ ] **weak参照安全性**: 失効時null化
- [ ] **効果注釈正確性**: 各命令の効果分類
- [ ] **型安全性**: Box型システム整合性
### **バックエンド互換性テスト**
```bash
# 全バックエンドMIR一致テスト
./target/release/nyash --dump-mir program.nyash > interpreter.mir
./target/release/nyash --backend vm --dump-mir program.nyash > vm.mir
./target/release/nyash --backend wasm --dump-mir program.nyash > wasm.mir
diff interpreter.mir vm.mir && diff vm.mir wasm.mir
```
## 🔮 **将来計画**
### **Phase 10: AOT/JIT対応**
- LLVM IR生成バックエンド
- ExternCall ネイティブ関数呼び出し
- 高度な最適化パス統合
### **Phase 11: MIR v2設計**
- 命令数最適化35 25命令目標
- BID統合Box Interface Definition
- リソース所有権拡張own<T>, borrow<T>
## 📚 **関連ドキュメント**
- **FFI/ABI仕様**: `docs/説明書/reference/box-design/ffi-abi-specification.md`
- **実装詳細**: `src/mir/instruction.rs`
- **Phase計画**: `docs/予定/native-plan/copilot_issues.txt`
---
**最終更新**: 2025年8月17日
**実装ベース**: 35命令完全対応
**次期計画**: BID統合プラグインシステムPhase 9.75f-BID

View File

@ -0,0 +1,45 @@
# Plugin Migration Guide v2 Enhancement Summary
## What was accomplished
This task involved enhancing the existing `docs/plugin-migration-request.md` with comprehensive implementation guidance based on the issue requirements.
## Key improvements made:
### 1. **Comprehensive nyash.toml explanation**
- Added detailed explanation of the `from`/`to` type conversion system
- Explained TLV (Type-Length-Value) encoding with specific tag mappings
- Provided clear examples using FileBox as reference
### 2. **Detailed implementation examples**
- Added complete Rust code examples for TLV parsing
- Showed real HttpClientBox plugin implementation patterns
- Included proper error handling and memory management examples
### 3. **Structured migration priorities**
- **Phase 1**: Network boxes (HttpClientBox, SocketBox) - highest priority
- **Phase 2**: GUI boxes (EguiBox, Canvas) - platform dependent
- **Phase 3**: Special purpose boxes (TimerBox, QRBox) - independent
### 4. **Testing and validation guidelines**
- Complete testing workflow with plugin-tester
- Real Nyash code examples for validation
- Troubleshooting guidance for common mistakes
### 5. **Reference implementation guidance**
- FileBox plugin as the gold standard example
- Specific file paths for all reference materials
- Success tips and common pitfalls
## Document statistics:
- **Length**: 368 lines (vs ~200 lines originally)
- **Code examples**: 16 code blocks with real implementation patterns
- **Comprehensive coverage**: TLV, nyash.toml, FFI, testing, and reference materials
## Validation:
- All key sections verified to be present
- Code examples cover the full implementation pipeline
- References to FileBox plugin success story maintained
- HttpClientBox positioned as Phase 1 priority target
The guide now serves as a complete reference for migrating any builtin Box to a plugin, with FileBox as the proven template and HttpClientBox as the next target.

View File

@ -0,0 +1,249 @@
# 📦 Nyash ビルトインBox → プラグイン化移行ガイド v2
## 🎯 概要
NyashのビルトインBoxをプラグイン化し、コアを軽量化します。
FileBoxプラグインの成功例を詳しく解説しながら、移行方法を説明します。
## 🔑 重要な概念nyash.tomlの型定義システム
### 型変換の仕組み
nyash.tomlでは、Nyash側とプラグイン側の型変換を明示的に定義します
```toml
# FileBoxの例
[plugins.FileBox.methods]
# writeメソッドNyashのstringをプラグインではbytesとして扱う
write = { args = [{ from = "string", to = "bytes" }] }
# openメソッド2つのstring引数型変換なし
open = { args = [
{ name = "path", from = "string", to = "string" },
{ name = "mode", from = "string", to = "string" }
] }
```
### from/toの意味
- **from**: Nyash側の型ユーザーが渡す型
- **to**: プラグイン側で受け取る型TLVエンコーディング
### TLVタグとの対応
プラグインはTLVType-Length-Value形式でデータを受け取ります
- `to = "i32"` → TLV tag=232ビット整数
- `to = "string"` → TLV tag=6UTF-8文字列
- `to = "bytes"` → TLV tag=7バイト配列
## 📋 移行対象Box一覧優先順位順
### 🌐 Phase 1: ネットワーク系(最優先・最も簡単)
既にスタブ実装があり、reqwest依存を追加するだけで完成します。
#### HttpClientBox
```toml
[plugins.HttpClientBox.methods]
# シンプルなGETリクエスト
get = {
args = [{ from = "string", to = "string" }], # URL
returns = "string" # レスポンスボディ
}
# POSTリクエストボディ付き
post = {
args = [
{ from = "string", to = "string" }, # URL
{ from = "string", to = "bytes" } # ボディ(バイナリ対応)
],
returns = "string"
}
# 詳細なリクエスト(ヘッダー等を含む)
request = {
args = [
{ from = "string", to = "string" }, # メソッドGET/POST等
{ from = "string", to = "string" }, # URL
{ from = "map", to = "map" } # オプションheaders, timeout等
],
returns = "map" # { status: i32, body: string, headers: map }
}
```
### 🖼️ Phase 2: GUI系プラットフォーム依存
EguiBoxは既にfeature分離されているので参考になります。
### 🎵 Phase 3: 特殊用途系(独立性高い)
TimerBox、QRBox等は単機能で実装しやすいです。
## 🔧 実装ガイドFileBoxを例に
### 1. プラグイン側での型受け取り例
```rust
// nyash.toml: write = { args = [{ from = "string", to = "bytes" }] }
METHOD_WRITE => {
// TLVでbytesとして受け取る
let data = tlv_parse_bytes(args)?; // Vec<u8>として取得
// ファイルに書き込み
match file.write(&data) {
Ok(n) => {
file.flush()?; // 重要:フラッシュを忘れずに!
// 書き込んだバイト数を返すTLV i32
write_tlv_i32(n as i32, result, result_len)
}
Err(_) => NYB_E_PLUGIN_ERROR
}
}
```
### 2. 複数引数の解析例
```rust
// nyash.toml: open = { args = [{ from = "string", to = "string" }, { from = "string", to = "string" }] }
METHOD_OPEN => {
// 2つのstring引数を解析
let (path, mode) = tlv_parse_two_strings(args)?;
// ファイルを開く
let file = match mode.as_str() {
"r" => File::open(&path)?,
"w" => File::create(&path)?,
"a" => OpenOptions::new().append(true).open(&path)?,
_ => return NYB_E_INVALID_ARGS
};
// 成功時はVoidを返す
write_tlv_void(result, result_len)
}
```
### 3. 引数なしメソッドの例
```rust
// nyash.toml: read = { args = [] }
METHOD_READ => {
// 引数なし - ファイル全体を読む
file.seek(SeekFrom::Start(0))?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
// bytesとして返す
write_tlv_bytes(&buf, result, result_len)
}
```
## 📝 HttpClientBox実装の具体例
```rust
// HttpClientBoxプラグインの実装イメージ
use reqwest::blocking::Client;
METHOD_GET => {
// URLを解析
let url = tlv_parse_string(args)?;
// HTTPリクエスト実行
let client = Client::new();
let response = client.get(&url).send()?;
let body = response.text()?;
// 文字列として返す
write_tlv_string(&body, result, result_len)
}
METHOD_POST => {
// URL と ボディを解析
let (url, body_bytes) = tlv_parse_string_and_bytes(args)?;
// POSTリクエスト
let client = Client::new();
let response = client.post(&url)
.body(body_bytes)
.send()?;
let body = response.text()?;
write_tlv_string(&body, result, result_len)
}
```
## 💡 実装のコツとよくある間違い
### ✅ 正しいnyash.toml
```toml
# 引数の型変換を明示
write = { args = [{ from = "string", to = "bytes" }] }
# 戻り値の型も指定可能
exists = { args = [], returns = "bool" }
```
### ❌ よくある間違い
```toml
# 間違い:型情報がない
write = { args = ["string"] } # ❌ from/toが必要
# 間違い:不要なフィールド
get = { args = [{ type = "string" }] } # ❌ typeではなくfrom/to
```
### メモリ管理の注意点
1. 文字列は必ずCString/CStr経由で変換
2. プラグイン側でallocしたメモリはプラグイン側でfree
3. ホスト側のVtableを使ってログ出力
### エラーハンドリング
```rust
// パニックをFFI境界で止める
let result = std::panic::catch_unwind(|| {
// 実際の処理
});
match result {
Ok(val) => val,
Err(_) => NYB_E_PLUGIN_ERROR
}
```
## 🧪 テスト方法
### 1. プラグインビルド
```bash
cd plugins/nyash-http-plugin
cargo build --release
```
### 2. plugin-testerで診断
```bash
cd ../../tools/plugin-tester
./target/release/plugin-tester ../../plugins/nyash-http-plugin/target/release/libnyash_http_plugin.so
# 期待される出力:
# Plugin Information:
# Box Type: HttpClientBox (ID: 20)
# Methods: 5
# - birth [ID: 0] (constructor)
# - get, post, put, delete
# - fini [ID: 4294967295] (destructor)
```
### 3. Nyashで実行
```nyash
// test_http.nyash
local http = new HttpClientBox()
local response = http.get("https://api.example.com/data")
print(response)
```
## 📚 参考資料
- **FileBoxプラグイン完全実装**: `plugins/nyash-filebox-plugin/src/lib.rs`
- **TLVエンコーディング仕様**: `docs/説明書/reference/plugin-system/ffi-abi-specification.md`
- **nyash.toml設定例**: プロジェクトルートの`nyash.toml`
## 🎯 成功の秘訣
1. **FileBoxを完全に理解してから始める** - コピペベースで改造
2. **nyash.tomlの型定義を正確に** - from/toを明示
3. **TLVの理解** - tag=6(string), tag=7(bytes)の違い
4. **plugin-testerで早期検証** - 問題を早期発見
---
質問があれば、FileBoxの実装を参考にしてください。
すべての答えがそこにあります!

View File

@ -0,0 +1,369 @@
# 📦 Nyash ビルトインBox → プラグイン化移行ガイド v2
## 🎯 概要
NyashのビルトインBoxをプラグイン化し、コアを軽量化します。
FileBoxプラグインの成功例を詳しく解説しながら、移行方法を説明します。
## 🔑 重要な概念nyash.tomlの型定義システム
### 型変換の仕組み
nyash.tomlでは、Nyash側とプラグイン側の型変換を明示的に定義します
```toml
# FileBoxの例
[plugins.FileBox.methods]
# writeメソッドNyashのstringをプラグインではbytesとして扱う
write = { args = [{ from = "string", to = "bytes" }] }
# openメソッド2つのstring引数型変換なし
open = { args = [
{ name = "path", from = "string", to = "string" },
{ name = "mode", from = "string", to = "string" }
] }
```
### from/toの意味
- **from**: Nyash側の型ユーザーが渡す型
- **to**: プラグイン側で受け取る型TLVエンコーディング
### TLVタグとの対応
プラグインはTLVType-Length-Value形式でデータを受け取ります
- `to = "i32"` → TLV tag=232ビット整数
- `to = "string"` → TLV tag=6UTF-8文字列
- `to = "bytes"` → TLV tag=7バイト配列
## 📋 移行対象Box一覧優先順位順
### 🌐 Phase 1: ネットワーク系(最優先・最も簡単)
既にスタブ実装があり、reqwest依存を追加するだけで完成します。
#### HttpClientBox
```toml
[plugins.HttpClientBox.methods]
# シンプルなGETリクエスト
get = {
args = [{ from = "string", to = "string" }], # URL
returns = "string" # レスポンスボディ
}
# POSTリクエストボディ付き
post = {
args = [
{ from = "string", to = "string" }, # URL
{ from = "string", to = "bytes" } # ボディ(バイナリ対応)
],
returns = "string"
}
# 詳細なリクエスト(ヘッダー等を含む)
request = {
args = [
{ from = "string", to = "string" }, # メソッドGET/POST等
{ from = "string", to = "string" }, # URL
{ from = "map", to = "map" } # オプションheaders, timeout等
],
returns = "map" # { status: i32, body: string, headers: map }
}
```
### 🖼️ Phase 2: GUI系プラットフォーム依存
EguiBoxは既にfeature分離されているので参考になります。
### 🎵 Phase 3: 特殊用途系(独立性高い)
TimerBox、QRBox等は単機能で実装しやすいです。
## 🔧 実装ガイドライン
### 1. 参考にするファイル
- **成功例**: `plugins/nyash-filebox-plugin/` - 動作確認済みのFileBoxプラグイン
- **設定例**: `nyash.toml` - 型情報定義の書き方
- **テスト**: `tools/plugin-tester/` - プラグイン診断ツール
### 2. 各プラグインの構成
```
plugins/nyash-xxx-plugin/
├── Cargo.toml # 依存関係(例: reqwest for HTTP
├── src/
│ └── lib.rs # FFI実装
├── nyash.toml # 型情報定義
└── README.md # 使用方法
```
### 3. nyash.toml記述例HttpClientBoxの場合
```toml
[plugins.HttpClientBox.methods]
# GETリクエスト
get = {
args = [{ name = "url", from = "string", to = "string" }],
returns = "string"
}
# POSTリクエスト
post = {
args = [
{ name = "url", from = "string", to = "string" },
{ name = "body", from = "string", to = "string" }
],
returns = "string"
}
# ヘッダー付きリクエスト
request = {
args = [
{ name = "method", from = "string", to = "string" },
{ name = "url", from = "string", to = "string" },
{ name = "options", from = "map", to = "map" }
],
returns = "map" # { status, body, headers }
}
# DELETE リクエスト
delete = {
args = [{ name = "url", from = "string", to = "string" }],
returns = "string"
}
# PUT リクエスト
put = {
args = [
{ name = "url", from = "string", to = "string" },
{ name = "body", from = "string", to = "string" }
],
returns = "string"
}
```
### 4. テスト方法
```bash
# ビルド
cd plugins/nyash-xxx-plugin
cargo build --release
# plugin-testerで診断
cd ../../tools/plugin-tester
./target/release/plugin-tester ../../plugins/nyash-xxx-plugin/target/release/libnyash_xxx_plugin.so
# Nyashで実行テスト
./target/release/nyash test_xxx.nyash
```
## 📝 特記事項
### HttpBox系
- 現在スタブ実装なので移行しやすい
- reqwest依存を復活させる
- 非同期処理の考慮が必要
### EguiBox
- 既にfeature分離されているので参考になる
- メインスレッド制約に注意
### AudioBox/SoundBox
- プラットフォーム依存性が高い
- Web/Desktop両対応を検討
### 依存関係の管理
- 各プラグインは独立したCargo.tomlを持つ
- ビルド時間短縮のため最小限の依存にする
## 💡 実装の重要ポイント
### FFI境界での注意事項
1. **メモリ管理**:
- Rustの所有権とCのメモリ管理の違いに注意
- 文字列は必ずCString/CStr経由で変換
2. **エラーハンドリング**:
- パニックをFFI境界で止めるcatch_unwind使用
- エラーコードで通信0=成功, 負値=エラー)
3. **型変換パターン** (FileBoxプラグインより):
```rust
// Nyash文字列 → Rust文字列
let path = get_string_arg(&args[0], 0)?;
// Rust文字列 → Nyash文字列
encode_string_result(&contents, result, result_len)
```
### 参考ファイルの具体的パス
- **FileBoxプラグイン実装**: `plugins/nyash-filebox-plugin/src/lib.rs`
- **FFI仕様書**: `docs/説明書/reference/plugin-system/ffi-abi-specification.md`
- **プラグインシステム説明**: `docs/説明書/reference/plugin-system/plugin-system.md`
- **BID-FFI型変換** (参考): `src/bid-converter-copilot/tlv.rs`
## 🔧 実装ガイドFileBoxを例に
### 1. プラグイン側での型受け取り例
```rust
// nyash.toml: write = { args = [{ from = "string", to = "bytes" }] }
METHOD_WRITE => {
// TLVでbytesとして受け取る
let data = tlv_parse_bytes(args)?; // Vec<u8>として取得
// ファイルに書き込み
match file.write(&data) {
Ok(n) => {
file.flush()?; // 重要:フラッシュを忘れずに!
// 書き込んだバイト数を返すTLV i32
write_tlv_i32(n as i32, result, result_len)
}
Err(_) => NYB_E_PLUGIN_ERROR
}
}
```
### 2. 複数引数の解析例
```rust
// nyash.toml: open = { args = [{ from = "string", to = "string" }, { from = "string", to = "string" }] }
METHOD_OPEN => {
// 2つのstring引数を解析
let (path, mode) = tlv_parse_two_strings(args)?;
// ファイルを開く
let file = match mode.as_str() {
"r" => File::open(&path)?,
"w" => File::create(&path)?,
"a" => OpenOptions::new().append(true).open(&path)?,
_ => return NYB_E_INVALID_ARGS
};
// 成功時はVoidを返す
write_tlv_void(result, result_len)
}
```
### 3. 引数なしメソッドの例
```rust
// nyash.toml: read = { args = [] }
METHOD_READ => {
// 引数なし - ファイル全体を読む
file.seek(SeekFrom::Start(0))?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
// bytesとして返す
write_tlv_bytes(&buf, result, result_len)
}
```
## 📝 HttpClientBox実装の具体例
```rust
// HttpClientBoxプラグインの実装イメージ
use reqwest::blocking::Client;
METHOD_GET => {
// URLを解析
let url = tlv_parse_string(args)?;
// HTTPリクエスト実行
let client = Client::new();
let response = client.get(&url).send()?;
let body = response.text()?;
// 文字列として返す
write_tlv_string(&body, result, result_len)
}
METHOD_POST => {
// URL と ボディを解析
let (url, body_bytes) = tlv_parse_string_and_bytes(args)?;
// POSTリクエスト
let client = Client::new();
let response = client.post(&url)
.body(body_bytes)
.send()?;
let body = response.text()?;
write_tlv_string(&body, result, result_len)
}
```
## 💡 実装のコツとよくある間違い
### ✅ 正しいnyash.toml
```toml
# 引数の型変換を明示
write = { args = [{ from = "string", to = "bytes" }] }
# 戻り値の型も指定可能
exists = { args = [], returns = "bool" }
```
### ❌ よくある間違い
```toml
# 間違い:型情報がない
write = { args = ["string"] } # ❌ from/toが必要
# 間違い:不要なフィールド
get = { args = [{ type = "string" }] } # ❌ typeではなくfrom/to
```
### メモリ管理の注意点
1. 文字列は必ずCString/CStr経由で変換
2. プラグイン側でallocしたメモリはプラグイン側でfree
3. ホスト側のVtableを使ってログ出力
### エラーハンドリング
```rust
// パニックをFFI境界で止める
let result = std::panic::catch_unwind(|| {
// 実際の処理
});
match result {
Ok(val) => val,
Err(_) => NYB_E_PLUGIN_ERROR
}
```
## 🧪 テスト方法
### 1. プラグインビルド
```bash
cd plugins/nyash-http-plugin
cargo build --release
```
### 2. plugin-testerで診断
```bash
cd ../../tools/plugin-tester
./target/release/plugin-tester ../../plugins/nyash-http-plugin/target/release/libnyash_http_plugin.so
# 期待される出力:
# Plugin Information:
# Box Type: HttpClientBox (ID: 20)
# Methods: 5
# - birth [ID: 0] (constructor)
# - get, post, put, delete
# - fini [ID: 4294967295] (destructor)
```
### 3. Nyashで実行
```nyash
// test_http.nyash
local http = new HttpClientBox()
local response = http.get("https://api.example.com/data")
print(response)
```
## 📚 参考資料
- **FileBoxプラグイン完全実装**: `plugins/nyash-filebox-plugin/src/lib.rs`
- **TLVエンコーディング仕様**: `docs/説明書/reference/plugin-system/ffi-abi-specification.md`
- **nyash.toml設定例**: プロジェクトルートの`nyash.toml`
## 🎯 成功の秘訣
1. **FileBoxを完全に理解してから始める** - コピペベースで改造
2. **nyash.tomlの型定義を正確に** - from/toを明示
3. **TLVの理解** - tag=6(string), tag=7(bytes)の違い
4. **plugin-testerで早期検証** - 問題を早期発見
---
質問があれば、FileBoxの実装を参考にしてください。
すべての答えがそこにあります!