Phase 11-12: LLVM backend initial, semantics layer, plugin unification

Major changes:
- LLVM backend initial implementation (compiler.rs, llvm mode)
- Semantics layer integration in interpreter (operators.rs)
- Phase 12 plugin architecture revision (3-layer system)
- Builtin box removal preparation
- MIR instruction set documentation (26→Core-15 migration)
- Cross-backend testing infrastructure
- Await/nowait syntax support

New features:
- LLVM AOT compilation support (--backend llvm)
- Semantics layer for interpreter→VM flow
- Tri-backend smoke tests
- Plugin-only registry mode

Bug fixes:
- Interpreter plugin box arithmetic operations
- Branch test returns incorrect values

Documentation:
- Phase 12 README.md updated with new plugin architecture
- Removed obsolete NYIR proposals
- Added LLVM test programs documentation

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-09-01 23:44:34 +09:00
parent fff9749f47
commit 11506cee3b
196 changed files with 10955 additions and 380 deletions

58
.github/workflows/llvm-aot-smoke.yml vendored Normal file
View File

@ -0,0 +1,58 @@
name: LLVM AOT Smoke
on:
push:
paths:
- 'src/**'
- 'crates/**'
- 'apps/**'
- 'tools/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/llvm-aot-smoke.yml'
pull_request:
paths:
- 'src/**'
- 'crates/**'
- 'apps/**'
- 'tools/**'
- 'Cargo.toml'
- 'Cargo.lock'
jobs:
llvm-aot:
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
NYASH_CLI_VERBOSE: '1'
NYASH_DISABLE_PLUGINS: '1'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry and build
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Install LLVM 18 (llvm-config-18)
run: |
sudo apt-get update
sudo apt-get install -y curl ca-certificates lsb-release wget gnupg
curl -fsSL https://apt.llvm.org/llvm.sh -o llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 18
llvm-config-18 --version
- name: Run LLVM AOT smokes
run: bash tools/llvm_smoke.sh

View File

@ -0,0 +1,58 @@
name: LLVM VInvoke Smoke
on:
push:
paths:
- 'src/**'
- 'crates/**'
- 'apps/**'
- 'tools/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/llvm-vinvoke-smoke.yml'
pull_request:
paths:
- 'src/**'
- 'crates/**'
- 'apps/**'
- 'tools/**'
- 'Cargo.toml'
- 'Cargo.lock'
jobs:
llvm-vinvoke:
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
NYASH_CLI_VERBOSE: '1'
# Plugins enabled (do not set NYASH_DISABLE_PLUGINS)
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry and build
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Install LLVM 18 (llvm-config-18)
run: |
sudo apt-get update
sudo apt-get install -y curl ca-certificates lsb-release wget gnupg build-essential libzstd-dev
curl -fsSL https://apt.llvm.org/llvm.sh -o llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 18
llvm-config-18 --version
- name: Run LLVM VInvoke + Array smoke tests
run: |
NYASH_LLVM_VINVOKE_RET_SMOKE=1 NYASH_LLVM_ARRAY_RET_SMOKE=1 ./tools/llvm_smoke.sh release

39
AGENTS.md Normal file
View File

@ -0,0 +1,39 @@
# Repository Guidelines
## Project Structure & Module Organization
- `src/`: Nyash core (MIR, backends, runner modes). Key: `backend/`, `runner/`, `mir/`.
- `crates/nyrt/`: NyRT static runtime for AOT/LLVM (`libnyrt.a`).
- `plugins/`: Firstparty plugins (e.g., `nyash-array-plugin`).
- `apps/` and `examples/`: Small runnable samples and smokes.
- `tools/`: Helper scripts (build, smoke).
- `tests/`: Rust and Nyash tests; historical samples in `tests/archive/`.
- `nyash.toml`: Box type/plugin mapping used by runtime.
## Build, Test, and Development Commands
- Build (JIT/VM): `cargo build --release --features cranelift-jit`
- Build (LLVM AOT): `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm`
- Quick VM run: `./target/release/nyash --backend vm apps/APP/main.nyash`
- Emit + link (LLVM): `tools/build_llvm.sh apps/APP/main.nyash -o app`
- Smokes: `./tools/llvm_smoke.sh release` (use env toggles like `NYASH_LLVM_VINVOKE_RET_SMOKE=1`)
## Coding Style & Naming Conventions
- Rust style (rustfmt defaults): 4space indent, `snake_case` for functions/vars, `CamelCase` for types.
- Keep patches focused; align with existing modules and file layout.
- New public APIs: document minimal usage and expected ABI (if exposed to NyRT/plugins).
## Testing Guidelines
- Rust tests: `cargo test` (add targeted unit tests near code).
- Smoke scripts validate endtoend AOT/JIT (`tools/llvm_smoke.sh`).
- Test naming: prefer `*_test.rs` for Rust and descriptive `.nyash` files under `apps/` or `tests/`.
- For LLVM tests, ensure LLVM 18 is installed and set `LLVM_SYS_180_PREFIX`.
## Commit & Pull Request Guidelines
- Commits: concise imperative subject; scope the change (e.g., "llvm: fix argc handling in nyrt").
- PRs must include: description, rationale, reproduction (if bug), and run instructions.
- Link issues (`docs/issues/*.md`) and reference affected scripts (e.g., `tools/llvm_smoke.sh`).
- CI: ensure smokes pass; use env toggles in the workflow as needed.
## Security & Configuration Tips
- Do not commit secrets. Plugin paths and native libs are configured via `nyash.toml`.
- LLVM builds require system LLVM 18; install via apt.llvm.org in CI.
- Optional logs: enable `NYASH_CLI_VERBOSE=1` for detailed emit diagnostics.

View File

@ -286,6 +286,15 @@ app.setSize(800, 600)
2. **🔄 ドキュメント更新** - 古い/不足している場合は更新
3. **💻 ソース確認** - それでも解決しない場合のみソースコード参照
### 🎯 最重要ドキュメント2つの核心
#### 🔤 言語仕様
- **[構文早見表](docs/quick-reference/syntax-cheatsheet.md)** - 基本構文・よくある間違い
- **[完全リファレンス](docs/reference/language/LANGUAGE_REFERENCE_2025.md)** - 言語仕様詳細
#### 📦 主要BOXのAPI
- **[Box/プラグイン関連](docs/reference/boxes-system/)** - APIと設計
### ⚡ API確認の実践例
```bash
# ❌ 悪い例:いきなりソース読む

View File

@ -1,4 +1,218 @@
# CURRENT TASK (Phase 10.8: MIR Core-15 確定 → LLVM 準備)
# CURRENT TASK (Phase 11.7 kick-off: JIT Complete / Semantics Layer)
Phase 11.7 へ仕切り直し(会議合意)
- 単一意味論層: MIR→Semantics→{VM/Cranelift/LLVM/WASM} の設計に切替。VMは参照実装、実行/生成はCodegen側で一本化。
- フォールバック廃止: VM→JITの実行時フォールバックは行わない。JITは“コンパイル専用/AOT補助”に限定。
- 共通ABI: handle/i64/ptr 変換、to_bool/compare、タグ分類、invoke固定/可変、NyRTシム呼び出しを共通化。
- Cranelift: 軽量JIT/AOTパスを追加し、最小実装からMIR-15を段階的に緑化。LLVM AOT は併存。
Docs: docs/development/roadmap/phases/phase-11.7_jit_complete/{README.md, PLAN.md, CURRENT_TASK.md, MEETING_NOTES.md}
以降は下記の旧計画LLVM準備をアーカイブ参照。スモークやツールは必要箇所を段階で引継ぎ。
Update (2025-09-01 AM / JIT handoff follow-up)
- Cranelift 最小JITの下地は進捗良好LowerCore→CraneliftBuilder 経路)
- Compare/Branch/Jump、最小Phiblock params、StackSlotベースの Load/Store は実装済みbuilder.rs/core.rs
- ExternCall(env.console.log/println) の最小橋渡しConsoleBox.birth_h→by-id invokeを追加済み。
- 追加スモークjit-direct 用I/Oなし
- apps/tests/mir-store-load: x=1; y=2; x=x+y; return x → 3
- apps/tests/mir-phi-min: if(1<2){x=10}else{x=20}; return x 10
- apps/tests/mir-branch-multi: 入れ子条件分岐 1
- 実行例: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-store-load/main.nyash`
- jit-direct 分岐/PHI 根治進行中
- 現象: select/compare の実行時観測では cond=1/then=1/else=0 と正しいが最終結果が 0 に落ちるケースあり
- 統合JIT`--backend cranelift`は期待どおりLowerCore の意味論は正しくjit-direct のCFG/合流が疑わしい
- 主因仮説確度高: 関数共有の value_stack をブロック間で使い回し分岐/合流で返値取り違え
変更点犯人切り分けと根治のための構造改革ログ
- CraneliftBuilderjit-direct 経路
- 単一出口明示合流SSAへ切替途中段階完了段階へ移行中
- ret 合流ブロックを導入しBlockParam(i64) で戻り値を受け渡し
- すべての `emit_return` i64 正規化 `jump(ret, [val])`に統一return 命令は合流側だけ)。
- end_function 側で ret 合流ブロックに切替しBlockParam f64 変換必要時のうえ return
- Compare/Branch/Select まわり
- Compare 結果をローカルスロットへ保存Branch/Select 時に確実にロードスタック取りこぼし排除)。
- `br_if` 直前に cond b1 正規化i64b1)。
- デバッグ用 extern を登録必要時のみ
- `nyash.jit.dbg_i64(tag: i64, val: i64) -> i64`値観測用
- `nyash.jit.block_enter(idx: i64) -> void`ブロック入場ログ
- LowerCorereturn 値の堅牢化
- Return 値が known/param/slot 経路に乗らない場合同一ブロックの Const 定義をスキャンして materialize
- Fast-path読みやすさ単純化: then/else が定数 return の場合`select(cond, K_then, K_else)``emit_return` に縮約`NYASH_JIT_FASTPATH_SELECT=1` で強制)。
診断ログ必要時のみ ON
- `NYASH_JIT_TRACE_BLOCKS=1` ブロック入場ログ`[JIT-BLOCK] enter=<idx>`
- `NYASH_JIT_TRACE_BR=1` …… br_if cond 有無`[JIT-CLIF] br_if cond_present=...`
- `NYASH_JIT_TRACE_SEL=1` select cond/then/else tag=100/101/102
- `NYASH_JIT_TRACE_RET=1` return の値: emit_return 直前tag=201、ret 合流tag=200
再現実行コマンドjit-direct
- 分岐の最小: `apps/tests/mir-branch-ret/main.nyash`
- `NYASH_JIT_THRESHOLD=1 NYASH_JIT_DUMP=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- 診断ON: `NYASH_JIT_THRESHOLD=1 NYASH_JIT_DUMP=1 NYASH_JIT_TRACE_RET=1 NYASH_JIT_TRACE_BLOCKS=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- PHI最小: `apps/tests/mir-phi-min/main.nyash`
- 複合分岐: `apps/tests/mir-branch-multi/main.nyash`
期待と現状2025-09-01 PM 時点
- 統合JIT`--backend cranelift`: OK`mir-branch-ret`1)。
- jit-direct: 単一出口BlockParam 合流の配線で安定化を確認cond/then/else も正常)。tag=201/200 は一致
検証結果jit-direct / 2025-09-01 実行
- `apps/tests/mir-branch-ret`: Result=1, [JIT-DBG] 201=1 / 200=1
- `apps/tests/mir-phi-min`: Result=10, [JIT-DBG] 201=10 / 200=10
- `apps/tests/mir-branch-multi`: Result=1, [JIT-DBG] 201=1 / 200=1
- `apps/tests/mir-nested-branch`: Result=1, [JIT-DBG] 201=1 / 200=1
- `apps/tests/mir-phi-two`: Result=50
次のタスク仕上げ
1) LowerCore fastpath/select のガード整理`NYASH_JIT_FASTPATH_SELECT` の簡素化)。
2) b1 返り値 ABI を有効化する場合の経路確認feature `jit-b1-abi`)。
3) ドキュメント整備CraneliftBuilder 単一出口方針と TRACE 変数の最終化)。
Update (2025-09-01 PM2 / Interpreter parity blockers)
- 目的: Semantics 層での VM/JIT/Interpreter パリティ検証に向けInterpreter 側の既知不具合を記録引き継ぎ
- 再現ファイルユーザー提供
- examples/semantics_test_branch.nyash
- 期待: 100
- 実際(Interpreter): IntegerBox(4)変数IDが返る挙動
- 実際(VM): 100
- examples/simple_test.nyash
- 事象: PluginBoxV2 同士の加算でエラーIntegerBoxダウンキャストのみを試行して失敗
- /tmp/test_string_concat.nyash
- 事象: 文字列連結でエラーPluginBox String の連結が不可
- 現状の暫定対応実装済み
- Await(JIT): I::Await nyash.future.await_h に降下同期get
- Safepoint(JIT): I::Safepoint nyash.rt.checkpoint に降下global_hooks 経由で GC/scheduler 連携
- Barrier(JIT): Array.set/Map.set nyash.gc.barrier_write emit
- Interpreter: FutureBox を登録new FutureBox(42)
- Interpreter: 加算まわりのフォールバック強化
- 文字列likeの優先連結toUtf8/Result.Ok含む
- 数値文字列整数にパースできる場合は加算
- 未解決最優先
1) 返り値が変数IDになるexamples/semantics_test_branch.nyash
- 調査方針: execute_statement(Return)→execute_function_call の伝播経路Variable 解決/共有の箇所を追跡しBox 値ではなく内部ID/インデックスを返している箇所を特定修正
- 対応: Return 直前と関数エピローグでの実値/型ログ限定ログを差し込み最小修正
2) PluginBox 同士の演算の包括対応
- 暫定は toString数値/文字列へ正規化で回避恒久対応は Semantics/VM と同じ規約handle-first + 文字列like/数値likeに寄せる
3) 文字列連結の広範囲対応
- toString()/toUtf8/Result.Ok の内包を最優先で文字列正規化現状の強化で多くは通る追加ケースが出れば順次取り込み)。
- 次アクションInterpreter fix plan
- [ ] semantics_test_branch 再現Return 値の実体化ルート修正
- [ ] simple_test の演算パスを toString 正規化で網羅必要なら算術 toNumber
- [ ] test_string_concat の失敗パターン収集 try_box_to_string の対象拡張
- [ ] SemanticsVM/ClifAdapter パリティ小スモーク追加分岐/配列/extern/await
開発メモ / 注意点
- 分岐の取り違えはブロックまたぎの共有スタックが原因になりがち根治策として BlockParam 経由の合流単一出口 return を徹底
- デバッグログは 必要時のみ ON の方針で仕込む収束後に不要箇所は落とす本番は静かに)。
チェックリスト収束条件
- [ ] jit-direct: `mir-branch-ret` 1tag=201/200 とも 1
- [ ] jit-direct: `mir-phi-min` 10合流経路で BlockParam 値が正しい
- [ ] jit-direct: `mir-branch-multi` 1ネスト分岐でも合流が正しい
- [ ] 統合JIT--backend craneliftと一致
- [ ] ログ一時コードの整理必要なものだけ残す
- 既知の注意jit-direct 経路のみ
- 条件分岐系Branch/Jumpで戻り値が 0 になる事象を確認`--backend cranelift`統合経路では期待値どおり例: mir-branch-ret 1)。
- 影響範囲: jit-direct 実験フラグのみLowerCore/CraneliftBuilder IR 自体は生成されており統合経路では正しく実行される
- 次回対応: brif 直後のブロック制御/シール順の見直しentry/sealing)、条件値スタック消費タイミングの再点検
Update (2025-09-01 night / JIT-direct branch/PHI fix)
- Summary
- Fixed jit-direct returning 0 for branches by unifying returns to a single-exit ret block and improving MIR return type inference (including PHI-based inference).
- Stabilized PHI joins by materializing PHI results into locals and preferring local loads for Return.
- Corrected stack order for `br_if_with_args` (pop else args then args cond), matching LowerCore push order; added minimal fallback when predecessor mapping is unavailable.
- Added debug symbols and logs (gated by env) for ret path and CFG wiring.
- Code touched
- MIR: `src/mir/builder.rs` (return type inference incl. PHI inputs)
- Lowering core: `src/jit/lower/core.rs` (PHI materialize to local; arg wiring fallback; small traces)
- Builder (Cranelift): `src/jit/lower/builder.rs` (single-exit return; br_if/jump args order; debug symbol registrations)
- New smokes
- `apps/tests/mir-phi-two` merge two locals (x,y) then add
- `apps/tests/mir-nested-branch` nested branches returning constants
- Status (jit-direct)
- mir-branch-ret 1
- mir-phi-min 10
- mir-phi-two OK合流加算
- mir-nested-branch 1
- LLVM AOT snips (prebuilt binaries) still OK for VInvoke samples.
- How to run
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-phi-min/main.nyash`
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-phi-two/main.nyash`
- `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-nested-branch/main.nyash`
- Optional logs: `NYASH_JIT_DUMP=1` and/or `NYASH_JIT_TRACE_RET=1`
- Next (small boxes; avoid saturation)
1) Fast-path(select) 1-hop extension only:
- If then/else each jump to a block that immediately returns a Const Integer, lower to `select(cond, K_then, K_else) → return`.
- Keep scope narrow to avoid regressions.
2) Multi-PHI (limited):
- Add one smoke with two PHI slots; confirm arg counts/ordering under `phi_min`.
3) Logging remains env-gated; no default noise. No broad refactors until the above are green.
# (以下、旧タスク: Phase 10.8 記録)
Contributor note: 開発手順レイアウトPR要件はリポジトリルートの `AGENTS.md`Repository Guidelines参照ビルド/テストやCIスモークの環境変数も簡潔にまとまっています
## Handoff (Phase 11 next): 実行確認プランtests/ は対象外)
目的: apps 配下の簡単なアプリから順にVM AOT(LLVM) の順で確実に動作させるtests/ フォルダは除外
前提/共通
- LLVM: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix)` を付与してビルド
- AOTリンク: `tools/build_llvm.sh <app.nyash> -o app` を使用`target/aot_objects/*.o` 固定
- Verbose: トラブル時は `NYASH_CLI_VERBOSE=1`
- プラグインテスター: `tools/plugin-tester` を利用可`cargo run --release -- check --config ../../nyash.toml --library <lib>`
推奨テスト順簡単段階的
1) apps/ny-array-llvm-ret/main.nyashArray push/get 戻り値
- 期待: `Result: 3`
- VM: `./target/release/nyash --backend vm apps/ny-array-llvm-ret/main.nyash`
- AOT: `tools/build_llvm.sh apps/ny-array-llvm-ret/main.nyash -o app && ./app`
2) apps/ny-vinvoke-llvm-ret-size/main.nyashby-id size
- 期待: `Result: 1`
- VM/AOT 同上
3) apps/ny-vinvoke-llvm-ret/main.nyashby-id get 可変長経路
- 期待: `Result: 42`
- VM/AOT 同上
4) apps/ny-echo-lite/main.nyashreadLine print 最小エコー
- 期待: 出力に `Result:` 行が含まれるllvm_smoke の基準
- 実行例: `echo hello | ./target/release/nyash --backend vm apps/ny-echo-lite/main.nyash`
- AOT: `tools/build_llvm.sh apps/ny-echo-lite/main.nyash -o app_echo && echo hello | ./app_echo`
5) apps/ny-llvm-smoke/main.nyashArray get/set/print
- 期待: `Result: 3`
- 備考: 文字列連結は NyRT concat シムへフォールバック済みemit 検知が不安定な場合は再試行問題が残る場合はこの項を後回し可
6) apps/ny-echo/main.nyashオプション付きエコー
- 期待: VM/JIT/AOT の出力一致upper/lower/そのまま
7) apps/ny-map-llvm-smoke/main.nyashMap by-id 経路
- 期待: 行に `Map: v=42` `size=1`
- 備考: 連結シム適用済み必要なら `NYASH_LLVM_ALLOW_BY_NAME=1` で一時回避
トラブルシュート要点
- AOT emit: `NYASH_LLVM_OBJ_OUT=$PWD/target/aot_objects/<name>.o ./target/release/nyash --backend llvm ...`
- リンク: `NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT=... tools/build_llvm.sh ...`
- プラグイン: `nyash.toml` のパス解決拡張子はOSで異なる)。tester は拡張子補完に対応
Update (2025-08-31 AM / Phase 11.1 quick pass)
@ -21,6 +235,102 @@ Update (2025-08-31 PM / Phase 11.2 partial)
- スモーク
- `examples/llvm11_core_smoke.nyash` EXE 実行し `Result: 3` を確認
Update (2025-08-31 PM2 / Phase 11.2 lightweight LLVM)
- 方針拡張性優先 / コア最小化: Tier0
- ExternCall: 環境/I/Oのみenv.console/debug/runtime など)。print ExternCall(env.console.log) 本線
- BoxCall: データ構造/Box 操作Array/Map/String/Instance )。AOT/LLVM は最小の安全シムのみ直結
- コアに残す安全シムNyRT: Array(get/set/push/length), Instance(getField/setField)。Map はコアに足さない後述)。
- Map/JSON/Math 等は当面コア外必要時はプラグイン byid + 汎用シムでAOTを通す)。
- 実装反映
- MIR パス: `passes/method_id_inject` 追加NewBox/Copy 由来の型から BoxCall method_id 注入PluginInvoke は可能なら BoxCall(byid)へ書換)。
- LLVM Lowering:
- ExternCall: `env.console.log` `nyash.console.log`NyRT, `env.debug.trace` `nyash.debug.trace`
- ExternCall: `env.console.readLine` 追加 `nyash.console.readline`stdin 1行, CR/LF 除去, C文字列返却)。
- ArrayBox: `get/set/push/length` NyRT 安全シム`nyash_array_get_h/set_h/push_h/length_h`に直結
- Instance: `getField/setField` NyRT 安全シム`nyash.instance.get_field_h/set_field_h`に直結既存)。
- プラグイン byid: f64 戻りの選択`nyash_plugin_invoke3_f64`/ i64 戻り`..._i64`)。先頭2引数はタグ付けint/float/handle対応`..._tagged_i64`)。
- byname 薄フォールバックデバッグ用: `NYASH_LLVM_ALLOW_BY_NAME=1` 下で `nyash.plugin.invoke_by_name_i64` を使用
- NyRT(libnyrt.a): 上記 API を追加console.log/debug.trace/readline, array push/length, instance get/set_field, byname, tagged_i64)。
- オブジェクト出力: `NYASH_LLVM_OBJ_OUT=<path>` .o を明示出力`runner/modes/llvm.rs` 経由)。
- ツール更新: `tools/build_llvm.sh` / `tools/llvm_smoke.sh` `NYASH_LLVM_OBJ_OUT` を使用して .o を取得後NyRT とリンク
- サンプル: `apps/ny-llvm-smoke/main.nyash`Array get/set/print)、`apps/ny-echo-lite/main.nyash`readLineprint)。
- しないこと / 後回し
- Map のコア安全シム追加`nyash_map_*_h`は見送り必要ならプラグイン byid + 汎用シムでAOT実行
- ConsoleBox の高度機能は ExternCall 側で段階導入出力は既存 log入力は readline のみ)。
- 汎用可変長引数>2は後段タグ付けの拡張で対応予定
- 次にやること(短期)
- ny-echo を縮小AOT対応console.readLine + print のみで OK 版)。
- Mapプラグイン最小版で string-key の get/set/size を byid 汎用シム経由でAOT実行コアは増やさない
- CI/スモーク: `.o→EXE→実行``apps/ny-llvm-smoke` / `apps/ny-echo-lite` で追加。
- ガードレール: コア安全シムを Tier0 以外に増やさない簡易チェックgrep ベース)導入検討。
- How to Build / RunAOT/LLVM
- ビルド: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm`
- .o→EXE: `tools/build_llvm.sh <file.nyash> -o app_llvm``./app_llvm`
- .o のみ: `NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o ./target/release/nyash --backend llvm <file.nyash>`
- 推奨 @envmain.nyash 冒頭コメント):
- `// @env NYASH_CLI_VERBOSE=1`(詳細ログ)
- デバッグ時のみ `// @env NYASH_LLVM_ALLOW_BY_NAME=1`
Update (2025-08-31 PM3 / LLVM VInvoke triage)
- 直近のテスト結果(要約)
- VM backend正常
- MapBox.size(): Result: 1 ✅
- MapBox.get(1): Result: 42 ✅
- 可変長VInvoke: get(1,9,8,7,6): 期待どおりにTLV I64(tag=3)でエンコードされ、出力確認済 ✅
- LLVM backend要修正
- MapBox birth は成功instance_id=1
- set()/get() 呼び出し経路で戻り値が Result: 0期待は 42
- LLVM 実行時に VM 相当の PluginLoaderV2 Invoke 詳細ログが出ず、by-id/可変長の値流しに不整合がある可能性
- 実装・修正状況
- ランタイムNyRT
- by-id 固定長/可変長の argc を「レシーバ除外の実引数個数」に統一済み
- invoke 時の type_id を「レシーバ実体PluginBoxV2から取得した実 type_id」に変更呼び出し先揺れを排除
- LLVM Lowering
- <=4 引数: nyash_plugin_invoke3_tagged_i64f64→i64ビット化タグ付与
- >=5 引数: nyash.plugin.invoke_tagged_v_i64vals/tags の vector 経路)
- スモーク
- apps/ny-vinvoke-llvm-ret: 戻り値で 42 を検証する LLVM 用スモークを追加print/concatに依存しない
- tools/llvm_smoke.sh に VInvoke戻り値スモークをオプション追加NYASH_LLVM_VINVOKE_RET_SMOKE=1
- いま見えている課題LLVM
- by-id vector 経路vals/tags/argcのどこかで齟齬 → get が 0 を返す(キーが TLV 化されていない/タグずれなど)
- 固定長(<=4経路は未切り分け → まず size()/get(1) を LLVM 戻り値で確定size→1, get(1)→42
- .o 出力の一部環境不安定は Runner/Lowering にフォールバックを追加済write_to_memory_buffer
- 次アクション
1) LLVM 戻り値テストを段階確認
- MapBox.size() → Result: 1
- MapBox.get(1) → Result: 42
- これで固定長 by-id の健全性を確定後、可変長(>=5vector 経路へ絞り込み
2) NyRT デバッグNYASH_CLI_VERBOSE=1 時のみ)を最小追加
- nyash.plugin.invoke_tagged_v_i64: argc/method_id、vals[0..2], tags[0..2] を stderr に出力
- 実際のTLV化の前に観測し、ズレ箇所を確定
3) LLVM Loweringvector 経路)の配列構築を点検
- alloca([N x i64]) → inbounds GEP → store → i64* へ pointer_cast → 呼び出し
- GEP index[0,i]/型一致/メモリ幅を再確認
4) 必要なら一時的に「<=4 でも vector 経路を選択」する実験分岐を作り、経路差異を切り分け
5) tools/llvm_smoke.sh: target/aot_objects 固定・事前 emit → link のフローをデフォルト強化CI安定化
- ゴール(本フェーズ収束条件)
- LLVM backend で MapBox: size()/get(1)/get(1,9,8,7,6) が戻り値ベースで一致
- VInvoke可変長経路が by-id で安定
- print/concat のLoweringは後続必要最小に回す
- タスク実行MVP
- `nyash.toml``[env]` / `[tasks]` を記述し、CLIから `--run-task <name>` で実行可能。
- 例:
- `[tasks] build_llvm = "LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm"`
- 実行: `./target/release/nyash --run-task build_llvm`
- 備考: `{root}` 変数展開対応。OS別/依存/並列は未対応(将来拡張)。
残作業(合意順)
1) method_id 埋め込みと by-id 本線化
@ -41,6 +351,74 @@ Update (2025-08-31 PM / Phase 11.2 partial)
- ビルド検証: `LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --features llvm` 成功
- 既存のConst/Unary/BinOp/Compare/Branch/Jump/Return と併せ、Phase 11.1の目標範囲は到達
---
一時メモMIRインタプリタ 80/20 方針)
- ConsoleBox.readLine ローカルフォールバック標準入力1行読みを一時実装`--backend mir` の echo-lite 用)。
- 後で必ず削除し、プラグイン側メソッド/Extern 経由に置換すること。
- 追跡タスク: 「ConsoleBox メソッド群の正式実装PluginInvoke/ExternCallとローカルフォールバックの撤去」。
- 影響箇所: `src/backend/mir_interpreter.rs`BoxCall: ConsoleBox.readLine 分岐)、`apps/tests/ny-echo-lite` スモーク。
次アクション(この項に紐づく)
- ClifSem 最小LoweringConst/Return/Addを実装し、JITスケルトンの0終了スモークを追加。
- ConsoleBox フォールバックは温存のままecho-liteのみのため。Console 正式化のタイミングで除去。
---
# Handoff (Phase 11.7) — MIR Interpreter + Cranelift Minimal JIT
目的: LLVMが重くなったため仕切り直し。新しいMIR解釈層と軽量Cranelift JITの最小機能を整備し、段階拡張しやすい骨格を確立。
実装済み(要点)
- 共通ABI/ユーティリティ:
- `src/backend/abi_util.rs`to_bool/eq/tag/handle
- MIRインタプリタ--backend mir:
- 対応: Const/Unary/BinOp(String結合)/Compare/Load/Store/Copy/Branch/Jump/Return/Print/Debug/Barrier/Safepoint(no-op)
- NewBox/PluginInvoke/BoxCall/ExternCallの最小対応PluginBoxV2はプラグインホスト経由
- ConsoleBox.readLine: 一時フォールバックで標準入力1行読みCURRENT_TASKに削除タスク記載済
- Cranelift最小JIT--backend cranelift:
- 実JITjit.rs: Const(i64/f64/bool->0/void->0)/Add/Sub/Mul/Div/Mod、Compare(Eq/Ne/Lt/Le/Gt/Ge)、Load/StoreStackSlot、Copy、Return/Jump/Branch
- 箱化: `src/backend/cranelift/context.rs` に ClifContext/BlockMap/ValueEnv を用意JIT構築をカプセル化
- LowerCore→ClifBuilderIRBuilder実体: 録画→実IR生成Const/Add/Returnを暫定実装
- 起動切替: `NYASH_JIT_LOWERCORE=1` で LowerCore→ClifBuilder 実IR経路を実行
- スモーク:
- `apps/tests/mir-const-add/main.nyash`0終了
- `apps/tests/mir-branch-ret/main.nyash`条件分岐で1
- どちらも --backend cranelift / --backend mir で確認済
使い方(コマンド)
- Cranelift有効ビルド: `cargo build --features cranelift-jit`
- MIRインタプリタ: `./target/debug/nyash --backend mir apps/tests/mir-const-add/main.nyash`
- Cranelift最小JIT: `./target/debug/nyash --backend cranelift apps/tests/mir-branch-ret/main.nyash`
- LowerCore→ClifBuilder 実IR: `NYASH_JIT_LOWERCORE=1 ./target/debug/nyash --backend cranelift apps/tests/mir-const-add/main.nyash`
次のタスク(推奨順)
1) ClifBuilder 実IR生成の拡張LowerCore連携
- Compare/Branch/Jump の実IR
- 最小PhiBlock Params
- StackSlotベースのLoad/StoreValueEnvから完全移行
2) ExternCallenv.console.log最小対応JIT経路でもprintln相当へ
3) スモーク追加: Load/Store、Phi最小ケース、Compare/Branch複合ケース
4) Console/Extern 正式化完了後に ConsoleBox.readLine フォールバック削除(本ファイルにタスク済)
5) 警告/CFG整理: 使っていないfeature cfgやunusedを段階的に整理
既知の注意/制限80/20の割り切り
- BoolはI64の0/1として扱っており、B1専用ABIは未導入将来拡張
- String/Null/Void のConstは暫定的に0へ丸め必要箇所から段階的に正規化
- `jit-b1-abi` 等のunexpected cfg警告は今後整理対象。
関連ファイル
- 追加: `src/backend/abi_util.rs`, `src/backend/mir_interpreter.rs`, `src/runner/modes/mir_interpreter.rs`
- 追加: `apps/tests/mir-const-add/main.nyash`, `apps/tests/mir-branch-ret/main.nyash`
- Cranelift: `src/backend/cranelift/jit.rs`, `src/backend/cranelift/context.rs`, `src/backend/cranelift/builder.rs`
- Semantics: `src/jit/semantics/{mod.rs,clif.rs}`(スケルトン)
メモ/トグル
- LowerCore→ClifBuilder 実IR: `NYASH_JIT_LOWERCORE=1`
- カバレッジログ: `NYASH_JIT_DUMP=1`
Handoff Snapshot (2025-08-31 / Phase 11 kick-off)

View File

@ -156,7 +156,8 @@ egui_extras = { version = "0.29", features = ["image"], optional = true }
image = { version = "0.25", features = ["png", "ico"], optional = true }
# LLVM backend deps (optional)
inkwell = { version = "0.5.0", features = ["llvm18-0"], optional = true }
# Move to LLVM 18.1.x toolchains
inkwell = { version = "0.6.0", features = ["llvm18-1"], optional = true }
# Windows API
[target.'cfg(windows)'.dependencies]

View File

@ -128,6 +128,13 @@ cargo build --release --features cranelift-jit
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
```
### LLVM バックエンドの補足
- `NYASH_LLVM_OBJ_OUT`: `--backend llvm` 実行時に `.o` を出力するパス。
- 例: `NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o ./target/release/nyash --backend llvm apps/ny-llvm-smoke/main.nyash`
- `NYASH_LLVM_ALLOW_BY_NAME=1`: デバッグ専用の by-name フォールバックby-id が未提供の場合の暫定措置)。
- 開発時のみ有効化してください(本番では無効)。
### 5. **WebAssembly** (ブラウザ用)
```bash
cargo build --release --features wasm-backend
@ -139,6 +146,35 @@ cargo build --release --features wasm-backend
---
## 🧰 タスク実行 (nyash.toml)
`nyash.toml``[tasks]``[env]` で、ビルド/スモークなどのタスクを簡単に実行できますMVP
nyash.toml の末尾に追記):
```
[env]
RUST_BACKTRACE = "1"
[tasks]
build_llvm = "LLVM_SYS_180_PREFIX=$(llvm-config-18 --prefix) cargo build --release --features llvm"
smoke_obj_array = "NYASH_LLVM_OBJ_OUT={root}/nyash_llvm_temp.o ./target/release/nyash --backend llvm apps/ny-llvm-smoke/main.nyash"
```
実行:
```
./target/release/nyash --run-task build_llvm
./target/release/nyash --run-task smoke_obj_array
```
補足:
- `[env]` の値は実行前に環境へ適用されます。
- `{root}` は現在のプロジェクトルートに展開されます。
- 現状は最小機能OS別/依存/並列は未対応)。
---
## 📊 **パフォーマンスベンチマーク**
実世界ベンチマーク結果 (ny_bench.nyash)
@ -325,6 +361,8 @@ box GameObject {
- 📚 ドキュメントの改善
- 🎮 クールなサンプルプログラム
詳細は `AGENTS.md`Repository Guidelinesをご参照ください。プロジェクト構成、ビルド/テスト手順、PRの要件を簡潔にまとめています。
## 📄 **ライセンス**
MIT ライセンス - プロジェクトで自由に使用してください!

View File

@ -128,6 +128,14 @@ Quick smoke test (VM vs EXE):
tools/smoke_aot_vs_vm.sh examples/aot_min_string_len.nyash
```
### LLVM Backend Notes
- `NYASH_LLVM_OBJ_OUT`: Path to emit `.o` when running `--backend llvm`.
- Example: `NYASH_LLVM_OBJ_OUT=$PWD/nyash_llvm_temp.o ./target/release/nyash --backend llvm apps/ny-llvm-smoke/main.nyash`
- `NYASH_LLVM_ALLOW_BY_NAME=1`: Debug-only fallback for plugin calls by name when by-id isnt available.
- Emits calls to `nyash.plugin.invoke_by_name_i64` for development.
- Do not enable in production.
### 5. **WebAssembly** (Browser)
```bash
cargo build --release --features wasm-backend
@ -325,6 +333,8 @@ Join the revolution! We welcome:
- 📚 Documentation improvements
- 🎮 Cool example programs
See also: Contributor guide in `AGENTS.md` (Repository Guidelines) for project layout, build/test commands, and PR expectations.
## 📄 **License**
MIT License - Use freely in your projects!

BIN
app_array_ret_llvm Normal file

Binary file not shown.

BIN
app_echo_llvm Normal file

Binary file not shown.

BIN
app_link

Binary file not shown.

BIN
app_map_llvm Normal file

Binary file not shown.

BIN
app_vinvoke_llvm Normal file

Binary file not shown.

BIN
app_vinvoke_ret_llvm Normal file

Binary file not shown.

View File

@ -0,0 +1,632 @@
# AI先生たちのNyashメモリ管理に関する完全な会話記録2025-08-31
## Gemini先生との最初の会話
### 私からの相談内容
```
Nyashプログラミング言語のメモリ管理について相談です。
【Nyashのメモリ管理の特徴】
- Everything is Box哲学すべてがBoxオブジェクト
- Arc<Mutex>統一アーキテクチャ全Box型で統一
- 参照カウント式のシンプルなGC
- プラグインシステムとの統合
- ハンドルベースのFFI
【問題】
論文用にbinary-trees、n-body、mandelbrotなどの標準ベンチマークが推奨されていますが、これらは小規模でNyashの特殊なメモリ管理の良さを表現しきれません。
【質問】
1. Nyashのメモリ管理の特殊性を活かせる大規模アプリケーションは
2. Arc<Mutex>の並行性を活かしたデモは?
3. プラグイン間でのBox共有を示すアプリケーションは
4. 他言語では難しいがNyashなら簡単に実装できる例は
```
### Gemini先生の完全回答
Nyashプログラミング言語のメモリ管理に関するご相談、ありがとうございます。言語設計の観点から、そのユニークなアーキテクチャの強みを最大限に表現できるアプリケーションについて、以下の通り提案します。
Nyashの「Everything is Box」哲学と`Arc<Mutex>`統一アーキテクチャは、従来の数値計算ベンチマークでは評価しにくい、**動的で状態が複雑に変化する大規模な並行処理**において真価を発揮します。論文でその独自性と優位性を示すには、まさにその特性を要求するアプリケーションを提示することが効果的です。
---
### 1. Nyashのメモリ管理の特殊性を活かせる大規模アプリケーション
**提案:リアルタイム・コラボレーティブ・アプリケーション基盤**
Google Docsのような共同編集エディタ、Figmaのようなデザインツール、またはマルチプレイヤーオンラインゲームのサーバーサイドなど、複数の主体が単一の共有状態を同時に変更するアプリケーションの基盤です。
**Nyashの強みが活きる点**
* **安全な状態共有:** ドキュメントのデータ構造(ツリー、グラフ)、あるいはゲームワールドの状態全体を`Box`オブジェクトとして表現します。`Arc<Mutex>`アーキテクチャにより、多数のユーザー(クライアント)からの同時編集リクエストがデータ競合を起こすことなく、安全に処理されることが言語レベルで保証されます。
* **動的なオブジェクト管理:** ユーザーのカーソル、テキストオブジェクト、図形、NPC、アイテムなど、アプリケーション内のあらゆる要素を統一された`Box`として動的に生成・破棄できます。「Everything is Box」哲学により、異種のオブジェクトをコレクションで扱う際も型システムが煩雑になりません。
* **並行処理との親和性:** 各クライアントからの操作を個別のタスクスレッドで処理するアーキテクチャと非常に相性が良いです。Nyashでは、開発者はロックの管理を意識することなく、ビジネスロジックの記述に集中できます。
このアプリケーションは、小規模なデータ構造を並列で計算するベンチマークとは対照的に、**巨大で複雑な単一の状態**を多数のアクターが**並行かつ安全**に変更し続けるという、Nyashの設計思想に最も合致したシナリオです。
### 2. `Arc<Mutex>`の並行性を活かしたデモ
**提案並行Webクローラー**
複数のワーカースレッドが並行してWebページをダウンロードし、ページ内のリンクを解析して、共有された「未訪問URLリスト」に追加していくアプリケーションです。
**Nyashの強みが活きる点**
* **シンプルな共有状態:** 「未訪問URLのキュー」と「訪問済みURLのセット」という2つの中心的な共有状態を、それぞれ`Box`化するだけで済みます。
* **コードの簡潔さ:** 他の言語では、共有データへのアクセスごとに `mutex.lock().unwrap()` のような明示的なロック獲得・解放処理が必要ですが、Nyashの統一アーキテクチャはこれを言語機能として抽象化します。これにより、並行処理のロジックが非常にクリーンかつ直感的に記述でき、デッドロックなどの典型的なバグを減らせます。
このデモは、Nyashがいかにして並行プログラミングの複雑さを軽減し、開発者がロジックそのものに集中できるようにするかを明確に示します。
### 3. プラグイン間でのBox共有を示すアプリケーション
**提案プラグインベースのECS (Entity-Component-System) アーキテクチャ**
ゲーム開発で多用されるECSパターンをNyashで実装します。これは、データComponentとロジックSystemを完全に分離する設計です。
* **Entity:** `Box`化された一意のID。
* **Component:** `Box`化された純粋なデータ(例: `Position { x, y }`, `Velocity { dx, dy }`)。
* **System:** 特定のロジックを実装した**プラグイン**(例: `PhysicsSystem`, `RenderSystem`)。
**Nyashの強みが活きる点**
* **究極のデータとロジックの分離:** `PhysicsSystem`プラグインは`Position``Velocity`を持つ全エンティティの`Box`を走査して位置を更新し、`RenderSystem`プラグインは`Position``Sprite`を持つエンティティの`Box`を走査して描画します。このように、**複数の独立したプラグインが同じデータ(`Box`)を共有し、それぞれの関心事に沿って操作する**様子を鮮やかに示せます。
* **動的な機能拡張:** `AISystem``AnimationSystem`といった新しい振る舞いを、既存のコードベースに一切変更を加えることなく、プラグインとして動的にロードできます。これはNyashのプラグインシステムと`Box`共有アーキテクチャの強力な組み合わせを実証します。
### 4. 他言語では難しいがNyashなら簡単に実装できる例
**提案:アプリケーションのライブ・リローディング(ホットスワップ)**
長時間稼働するサーバーアプリケーション(例: Web APIサーバーを停止させることなく、その一部の機能を新しいバージョンに更新するデモです。
**Nyashの強みが活きる点**
* **統一オブジェクトモデルの威力:** Nyashでは、関数やクロージャさえもが`Box`オブジェクトとして扱えます。これにより、特定のAPIエンドポイントの処理ロジックを実装した関数オブジェクトを、実行時にアトミックに安全に差し替えることが原理的に容易になります。
* **プラグインとFFIの活用** 新しいバージョンのロジックを独立したプラグイン(.so/.dllファイルなどとしてコンパイルします。サーバーは稼働中にこのプラグインを動的にロードし、ハンドルベースFFIを介して新しい関数`Box`への参照を取得し、ルーティングテーブル内の古い参照と入れ替えます。
* **他言語との差別化:**
* RustやC++のような静的言語でこれを実現するには、非常に複雑な設計と手作業でのメモリ管理が必要です。
- PythonやRubyのような動的言語では可能ですが、GILグローバルインタプリタロックの存在により、Nyashのような真の並行性は得られません。
Nyashは、**静的言語の安全性とパフォーマンス**のポテンシャルを持ちながら、**動的言語の柔軟性**を兼ね備えている、というユニークなポジションをこのデモで示すことができます。
---
### まとめ
Nyashのメモリ管理の真価は、静的な計算性能ではなく、**「複雑で動的な状態を、安全かつ並行に、そしてモジュール的に扱う能力」**にあります。提案したアプリケーションは、この言語の核心的な価値を学術的にも実践的にもアピールするための強力な材料となるでしょう。
## ChatGPT5先生との会話
### 私からの相談内容
```
Nyashプログラミング言語のメモリ管理の強みを表現するアプリケーションについて相談です。
【Gemini先生の提案】
1. リアルタイム・コラボレーティブ基盤Google Docs風の共同編集
2. 並行Webクローラー
3. プラグインベースのECSEntity-Component-System
4. ライブ・リローディング(ホットスワップ)
【Nyashの特殊性】
- Arc<Mutex>統一アーキテクチャで全Boxが自動的にスレッドセーフ
- プラグイン間でのBox共有が容易
- 参照カウントベースのシンプルなGC
- Everything is Box哲学
【質問】
1. P2Pネットワークを活用したNyash独自のアプリケーションは
2. NyaMeshとの統合を前提とした分散アプリケーションの例は
3. Box共有の安全性を活かした「他言語では危険だがNyashなら安全」な例は
4. 論文で最もインパクトがあるデモは何でしょうか?
```
### ChatGPT5先生の完全回答
了解です。Nyashの「Arc<Mutex>統一」「Everything is Box」「参照カウントGC」「プラグイン間Box共有」を強みに据え、P2P/NyaMesh前提で"Nyashならでは"を狙う案をまとめました。
**設計キーモチーフ**
- Box-as-capability: Box自体を能力トークンとして共有・移譲・複製する設計。
- 内容アドレス化: 大きなBoxは内容ハッシュで識別し、P2Pで重複排除。
- 暗黙スレッドセーフ: 共有は常に `Arc<Mutex<Box<T>>>`。ゼロコピー志向、衝突はMutexで局所化。
- ホットスワップ既定路: 新旧プラグインが同じBoxを安全に参照継続。
**1. P2P独自アプリNyashらしさ最大化**
- BoxTorrent: 内容アドレス化BoxのP2P配布基盤
- 大容量データや中間生成物を「Box=DAGード」として配布。変換プラグインは入力Box→出力Boxを生成し、ハッシュで再利用。
- Nyash優位: ゼロコピー共有、ホットスワップで変換器の差し替え可、参照カウントで自然にキャッシュエビクション。
- Live Shared Heap: メッシュ越し共有ヒープ
- 論理的に単一のShared HeapにBoxを配置。所有権はメッシュ内で移譲lease、読み取りは複製、書き込みはロックバージョニング。
- Nyash優位: 全Boxスレッドセーフ前提で分散ロックを単純化。プラグインがヒープ上の同一Boxをそのまま扱える。
- P2P Live-ML Pipeline
- センサ→前処理→特徴→推論→ポストの各段をプラグイン化し、ピア間でBoxバッファをパス。途中段のホットアップグレードや分岐実験が容易。
- Nyash優位: 各段が同じBoxを共有しゼロコピー。落ちたピアの未解放は参照カウントで自然解放。
- Cooperative Rendering/Video Mixer
- 各ピアがレイヤBoxを提供し、合成ードがロック付きで読み取り、最終フレームBoxを配信。遅延抑制のため差分Boxタイルを伝播。
- Nyash優位: 共有フレームバッファを安全に多者で読む/書く。ホットスワップでフィルタ入替。
**2. NyaMesh統合前提の分散例**
- MeshRef/Lease機構
- NyaMeshが提供するID/ルーティング上で `BoxID` をグローバル化。lease数が0でGC。複製はK-レプリカ。ゴシップで参照数を伝播。
- 使い道: Live Shared Heap/BoxTorrentの基盤。ネット分断でもlease期限で回復。
- NyaMesh PubSub CRDT-Box
- CRDTレジスタ/マップ/ログをBox実装。書込みはMutex保護バージョン、伝播はNyaMesh PubSub。競合はCRDTで収束。
- 使い道: 協同編集、分散ECS、分散設定管理。
- 分散ECSEntity=Box
- Entity/ComponentをBoxとしてNyaMesh上で移動。負荷に応じてオーナーシップを移譲。システムはプラグインで増減。
- 使い道: MMO風シミュ、ロボット群制御、IoTツイン。
- Edge Cache/Compute
- 内容アドレスBoxをNyaMesh距離で配置最適化。近傍にレプリカ、遠方はリモート参照。ホットスワップで演算器を近傍に移設。
**3. 他言語だと危険だがNyashなら安全な共有**
- ゼロコピー共有バッファの多段パイプ
- 大容量 `Box<ByteBuf>` を複数プラグインが並列処理。ダングリング/データ競合はArc<Mutex>で回避。古いプラグイン残留参照も安全に自然解放。
- ライブ・ホットスワップ中の同一Box共有
- 新旧プラグインが同じBoxを参照し続け、切替中も落ちない。通常はABI/所有権で危険だが、Box境界で能力を渡すだけで成立。
- 共有メモリマップファイルの安全クローズ
- 多スレッド/多プラグインで参照する `Box<Mmap>`。最後の参照が落ちた瞬間のみクローズされ、早期クローズ・UAFを防止。
- マルチプロデューサ・マルチコンシューマなリングバッファ
- `Box<Ring<T>>` をMutexで保護し、try_lockバックプレッシャで輻輳制御。言語レベルunsafe不要。
- 強制隔離されない共同編集のポインタ共有
- プラグイン間で直接同じBoxドキュメントを参照CRDT。ロックバージョンで整合、コピー不要。通常は破滅的競合を招く領域。
注意: デッドロックは設計課題なので、型でロック順序を表す/細粒度ロック/try_lock再試行/単一スレッドオーナー+メッセージ駆動等で回避。
**4. 論文で最もインパクトがあるデモ**
- 分散ホットスワップ・パイプライン(エンドツーエンド無停止)
- 構成: NyaMesh上のパイプラインセンサ→前処理→推論→配信。各段はプラグイン。高負荷時に段を増殖/縮退、同時に新バージョンへホットスワップ。
- デモ見せ場: スループット維持、フレーム落ち/UAF/クラッシュゼロ、旧版プラグインが参照するBoxの自然解放、Boxの移譲/複製の可視化。
- 計測: p99レイテンシ低下、スワップ時間中断ゼロ、メモリリーク0、再処理なしのバイナリ差替成功率。
- 共有ヒープ越しのP2Pコラボ編集巨大データ
- 構成: 1GB級ドキュメント/モデルを「単一Box」として複数ユーザが同時編集。差分はタイルBoxとして伝搬、CRDT-Boxで収束。
- 見せ場: ゼロコピー、Boxの局所ロックで高スループット、ード離脱でも自然復旧。
- 分散ECSワールドのライブ移住
- 構成: 数百万EntityのECS。混雑エリアを別ピアへオーナー移譲しながら、プレイ継続・スナップフリー。
- 見せ場: Entity=Box移譲、参照が残る間は両ピアで安全アクセス、解放の自動同期。
- BoxTorrentの内容アドレスDAG再計算スキップ
- 構成: DAG変換パイプ。同一ハッシュ出力をグローバル再利用し、再計算ゼロに。
- 見せ場: キャッシュヒット率と電力削減、重複排除。
**評価指標(論文化しやすい軸)**
- 安全性: UAF/ダブルフリー/データ競合/クラッシュの発生率0。
- 可用性: ホットスワップ中断時間0、p99レイテンシ悪化<5%。
- 効率: ゼロコピー率再計算/再送削減RAMフットプリント参照カウントによる自然エビクション)。
- スケール: ピア数とスループットの線形性チョークポイント不在
- 回復性: ノード喪失下でのデータ保全率復旧所要時間
**リスクと対策**
- デッドロック: ロック順序規約/静的解析/try_lock指数バックオフ
- 参照サイクル: 明示的Weak参照世代別GC/epochでのリーク検出ダッシュボード
- 分散参照数の正確性: lease期限ゴシップ再確認チェックポイント
- 一貫性と遅延: CRDT採用書込みはバージョン化Boxで楽観制御
- セキュリティ: Box能力にスコープ/寿命/権限を付与capability化)。
**次の一手(提案)**
- 小さなPoC: BoxTorrentのミニ版内容アドレスBoxNyaMesh PubSub参照カウント連携を最短で実装
- 併走PoC: 分散ホットスワップパイプライン2段で無停止差替を実証
- 計測仕込み: 参照グラフ可視化p99レイテンシ/スループット/リーク監視
どの路線を優先しますか最短でインパクトを狙うなら分散ホットスワップパイプラインBoxTorrent+DAG再利用が論文映えします要望があれば選んだ案のアーキ図と最小APIBoxID/lease/replicate/transferの草案を書きます
## 深い理解を求めての再相談
### 私からの指摘
```
うーん Arc<Mutex> ってかいてるけど これは 本質じゃない気がする 
boxのスコープを抜けたら開放 finiで開放 weakの弱参照ということ 
りかいしてない 効き方が悪いにゃ 
ルートフォルダで docsなど読むようにして きいたほうがいいにゃ 
深く考えて もっかいきいてみて
あ、まって gc無しで 話して その動作が本質なんだから 深く考えておねがいにゃ
```
## Gemini先生への再相談GC無しの本質
### 私からの再相談内容
```
Nyashプログラミング言語のメモリ管理について、より深い理解に基づいて相談です。
【重要な訂正】
Arc<Mutex>は実装詳細であり、本質ではありませんでした。
【Nyashメモリ管理の真の本質GC無しの動作
1. スコープベースの決定論的解放
- Boxがスコープを抜けたら即座に解放
- 明示的なdrop不要、自動的にメモリ返却
2. finiシステム
- オブジェクトの論理的な終了を宣言
- 物理的な破棄とは独立(参照が残っていても論理的に死んでいる)
- 「死んだ」オブジェクトへのアクセスは安全にエラー
3. weak参照の自動nil化
- 強参照が0になったらすべてのweak参照が自動的にnilに
- ダングリングポインタが原理的に発生しない
- 「生きているか」をweakで安全にチェック可能
4. 決定論的な解放順序
- init宣言の逆順で解放カスケード解放
- 親が解放されたら子も確実に解放
- メモリリークが構造的に防げる
【質問GC無しの観点から
1. この決定論的ライフサイクル管理を最大限に活かせるアプリケーションは?
2. finiシステムの「論理的死」を活用した革新的な設計パターンは
3. weak自動nil化を前提とした分散システムの可能性は
4. 他言語のGCやRAIIでは実現困難だがNyashなら簡単な例は
```
### Gemini先生の深い理解に基づく回答
ご指摘ありがとうございますArc<Mutex>という実装詳細に囚われていました。Nyashの本質的な革新性は、その**決定論的ライフサイクル管理**にあることを理解しました。GC無しの観点から、この独自のメモリ管理セマンティクスがもたらす新しいプログラミングパラダイムについて再考します。
## Nyashメモリ管理の真の革新性
Nyashは単なる「安全なメモリ管理」を超えて、**オブジェクトの生死を言語の第一級概念**として扱う点で革命的です。これは従来の言語設計では見られない、全く新しいアプローチです。
### 1. 決定論的ライフサイクルを最大限に活かすアプリケーション
**提案:ステートフル・ストリーム処理エンジン**
リアルタイムデータ処理において、各処理段階ウィンドウ、集約、結合の状態管理は複雑です。Nyashの決定論的ライフサイクルは、この複雑性を根本的に解決します。
```nyash
box StreamWindow {
init { data, startTime, duration }
fini() {
// ウィンドウ終了時の集計結果を下流に送信
downstream.send(me.aggregate())
// これ以降、このウィンドウへの書き込みは自動的にエラー
}
}
```
**革新的な点:**
- **時間ベースの自動リソース管理**: ウィンドウのライフタイムがスコープと連動し、期限切れで自動的にfiniが呼ばれる
- **遅延到着データの安全な処理**: fini後のデータは自動的に次のウィンドウへダングリングアクセスなし
- **バックプレッシャーの自然な実装**: 下流が詰まればスコープが解放されず、自動的に上流が停止
### 2. finiシステムの「論理的死」を活用した革新的設計パターン
**提案:リソース再利用ステートマシン**
データベース接続、ネットワークソケット、大規模バッファなど、高コストなリソースを「論理的に死んでいるが物理的に生きている」状態で管理します。
```nyash
box ConnectionPool {
init { connections }
checkout() {
// 論理的に死んでいるfini済みが物理的に生きている接続を探す
local conn = me.connections.find_fini_but_alive()
if conn {
conn.resurrect() // 論理的に復活
return conn
}
return new Connection()
}
}
box Connection {
init { socket, inUse }
fini() {
me.inUse = false
// ソケットは閉じない!プールに返すだけ
// 論理的には死んでいるが、物理的なリソースは保持
}
resurrect() {
me.inUse = true
// 論理的な再生
}
}
```
**革新的な点:**
- **ゼロコスト・リソースプーリング**: GCのファイナライザと異なり、finiのタイミングが確定的
- **論理的状態と物理的状態の分離**: 「使用中/未使用」を言語レベルで表現
- **メモリセーフな再利用**: 論理的に死んだオブジェクトへのアクセスは必ずエラー
### 3. weak自動nil化を前提とした分散システム
**提案:自己修復型サービスメッシュ**
分散システムにおいて、ードの生死を監視することは本質的に困難です。Nyashのweak参照自動nil化は、この問題に対する根本的な解決策を提供します。
```nyash
box ServiceNode {
init { endpoint, healthChecker }
fini() {
// ノードの論理的な死(ヘルスチェック失敗、手動停止など)
print("Node ${me.endpoint} is going down")
}
}
box ServiceMesh {
init { nodes } // weak参照のマップ
route(request) {
// 死んだードは自動的にnilになっている
local aliveNodes = me.nodes.values().filter(n => n != nil)
if aliveNodes.isEmpty() {
error("No healthy nodes available")
}
// ロードバランシング(死んだノードは既に除外されている)
return aliveNodes.random().handle(request)
}
}
```
**革新的な点:**
- **ゼロレイテンシ障害検知**: ードがfiniした瞬間、全てのweak参照がnilに
- **分散GCの回避**: 参照カウントの分散管理が不要
- **スプリットブレイン耐性**: ネットワーク分断時も各パーティションで正しく動作
### 4. 他言語では実現困難だがNyashなら簡単な例
**提案:トランザクショナル・オブジェクトライフサイクル**
データベーストランザクションのように、オブジェクトの生成・変更・破棄を原子的に行い、失敗時は自動的にロールバックします。
```nyash
box Transaction {
init { objects, committed }
createObject(Type, args) {
local obj = new Type(args)
me.objects.push(obj)
return obj
}
commit() {
me.committed = true
// オブジェクトを永続化
}
fini() {
if not me.committed {
// コミットされていない場合、全オブジェクトを破棄
me.objects.forEach(obj => obj.fini())
}
}
}
// 使用例
{
local tx = new Transaction()
local user = tx.createObject(User, {name: "Alice"})
local order = tx.createObject(Order, {userId: user.id})
if paymentSuccessful {
tx.commit()
}
// スコープを抜ける際、コミットされていなければ自動ロールバック
}
```
**革新的な点:**
- **言語レベルのトランザクション**: スコープとライフサイクルの統合
- **自動的なリソースクリーンアップ**: 例外安全性が言語仕様で保証
- **メモリとビジネスロジックの融合**: 技術的制約をビジネス要件に活用
## 結論:新しいプログラミングパラダイム
Nyashのメモリ管理は、単なる技術的優位性を超えて、開発者に新しい思考の枠組みを提供します
1. **論理的死と物理的生存の分離** → 「生きたログ」としてのオブジェクト
2. **遺言パターン** → finiでの「死後の自己報告」によるデバッグと監視
3. **Weak自動nil化** → 「自己修復」する回復力のあるデータ構造
4. **決定的解放順序** → 「安全な解体」が保証された積層的設計
これらはすべて、GCの不確実性からプログラマを解放し、システムの振る舞いを時間軸に沿って正確に予測・設計することを可能にします。これはもはやメモリ管理ではなく、**「ライフサイクル指向プログラミング」**と呼ぶべき新しいパラダイムです。
## ChatGPT5先生への再相談GC無しの本質
### 私からの再相談内容
```
Nyashメモリ管理について、GC無しの本質的な動作に基づいて再度相談です。
【Gemini先生の深い洞察】
「ライフサイクル指向プログラミング」という新パラダイムの提案:
- 論理的死と物理的生存の分離
- finiでの「遺言パターン」
- weak自動nil化による自己修復
- 決定的解放順序による安全な解体
【改めて理解したNyashの本質】
1. 決定論的ライフサイクルGCの不確実性なし
2. finiシステム論理的終了と物理的破棄の分離
3. weak自動nil化ダングリング不可能
4. スコープベース解放init宣言の逆順
【質問】
1. この決定論的モデルがP2P/分散環境でもたらす革新は?
2. 「生死の決定論」を分散システムの基本原語にする可能性は?
3. NyaMeshでこの特性を最大限活かすアーキテクチャは
4. 論文で最もインパクトのある実証方法は?
```
### ChatGPT5先生の深い理解に基づく回答
なるほど、Arc<Mutex>は実装詳細で、本質は「決定論的ライフサイクル管理」だったのですね。これは確かに革命的です。GCの不確実性を完全に排除し、「生死の決定論」を言語の第一級概念にする。これをP2P/分散の文脈で考え直します。
## 「生死の決定論」を分散原語にする革新
従来の分散システムは「故障検知の不確実性」と戦ってきましたFLP不可能性、CAP定理など。Nyashの決定論的ライフサイクルは、この戦いに新しい武器を提供します。
### 1. 決定論的モデルがP2P/分散環境でもたらす革新
**LifeToken分散プロトコル**
ードが「LifeToken」を持ち、それをweakで共有。トークンのfiniが即座に全ピアに伝播。
```nyash
box PeerNode {
init { nodeId, lifeToken, peers } // peersはweak参照のマップ
fini() {
// 明示的な離脱通知不要!
// 全ピアのweak参照が自動的にnilに
}
broadcast(message) {
me.peers.forEach((id, peer) => {
if peer != nil { // 生きているピアのみ
peer.receive(message)
} else {
me.peers.remove(id) // 死んだピアを削除
}
})
}
}
```
**革新性:**
- **即座の故障伝播**: ハートビート不要、タイムアウト不要
- **スプリットブレイン自己解決**: 分断されたードのfiniで自動的に縮退
- **決定的なクラスタ再構成**: メンバーシップの変更が瞬時かつ確定的
### 2. 「生死の決定論」を分散システムの基本原語に
**Two-Phase Fini Protocol**
分散トランザクションのTwo-Phase Commitを、オブジェクトライフサイクルで再発明。
```nyash
box DistributedTransaction {
init { participants, coordinator }
// Phase 1: Prepare to die
prepareFini() {
local votes = me.participants.map(p => p.canFini())
return votes.all(v => v == true)
}
// Phase 2: Coordinated death
fini() {
if me.prepareFini() {
// 全参加者が同時に論理的に死ぬ
me.participants.forEach(p => p.fini())
me.coordinator.recordCommit(me.id)
} else {
// 中止 - 誰も死なない
me.coordinator.recordAbort(me.id)
}
}
}
```
**パラダイムシフト:**
- **生存ではなく死を調整**: 「いつ死ぬか」の合意形成
- **決定的な終了**: GCの非決定性なし、リソースリーク不可能
- **カスケード死の活用**: 親の死で子も確実に死ぬ
### 3. NyaMeshでこの特性を最大限活かすアーキテクチャ
**Lifecycle-Aware Mesh Routing**
ルーティングテーブル自体をweak参照で構築し、ードの生死に即座に適応。
```nyash
box MeshRouter {
init { routes } // BoxID -> weak<NextHop>のマップ
route(boxId, data) {
local nextHop = me.routes.get(boxId)
if nextHop == nil {
// ルートが死んでいる - 自動的に再探索
me.discoverRoute(boxId)
}
// Weak参照の連鎖でマルチホップ
local path = me.tracePath(boxId)
path.forEach(hop => {
if hop != nil {
hop.forward(data)
} else {
// 経路上のノードが死んだ - 迂回
me.reroute(boxId, data)
}
})
}
}
```
**アーキテクチャの特徴:**
- **自己修復ルーティング**: 故障ノードの迂回が自動
- **メモリ効率的**: 死んだルートは即座にGC決定論的
- **リアルタイム適応**: 変化への反応がマイクロ秒単位
### 4. 論文で最もインパクトのある実証
**「決定論的ライフサイクルによる分散システムの新基盤」**
**実験設定:**
1. **1000ードP2Pネットワーク**でカオスエンジニアリング
2. **ランダムにードをkill**1秒に10ード
3. **同時に新ノードを投入**1秒に10ード
**測定項目と期待される結果:**
| 指標 | 従来の分散システム | Nyash決定論システム |
|------|-------------------|---------------------|
| 故障検知時間 | 5-30秒タイムアウト依存 | 0秒即座 |
| メンバーシップ収束時間 | 分〜時間 | ミリ秒 |
| 誤検知率 | 1-5%(ネットワーク遅延) | 0%(決定論的) |
| メモリリーク | 徐々に蓄積 | 構造的に0 |
| メッセージ複雑度 | O(n²)ハートビート | O(1)ライフサイクル |
**デモシナリオ:**
```nyash
// 分散ビデオ変換パイプライン
box VideoProcessor {
init { inputQueue, outputQueue, workers }
processVideo(video) {
// 動的にワーカーを追加
loop(me.inputQueue.size() > threshold) {
local worker = new Worker()
me.workers.add(weak(worker))
}
// 処理分配(死んだワーカーは自動スキップ)
me.workers.forEach(w => {
if w != nil {
w.process(video.nextChunk())
}
})
}
fini() {
// カスケード終了 - 全ワーカーが確実に停止
print("Processed ${me.outputQueue.size()} videos")
}
}
```
**論文の主張:**
1. **理論的貢献**: 分散システムの故障モデルに「決定論的死」という新概念を導入
2. **実装的貢献**: weak参照とfiniシステムによる実用的な実現
3. **実証的貢献**: 従来手法を全指標で上回る性能
**インパクトを最大化する見せ方:**
- **ライブデモ**: ードをkillしてもサービスが止まらない様子をリアルタイム可視化
- **決定論の可視化**: 各ードの生死状態を色で表示、weak参照のnil化を矢印の消失で表現
- **比較実験**: 同じシナリオを従来手法(タイムアウトベース)で実行し、収束の遅さを対比
## 結論:分散システムの新たな基礎
Nyashの決定論的ライフサイクルは、単なるメモリ管理を超えて、**分散システムの新しい基礎理論**を提供します:
1. **生死の即座伝播**: ネットワーク遅延に依存しない故障検知
2. **論理と物理の分離**: 「死んだが動いている」ノードの活用
3. **カスケード終了**: 依存関係に沿った確実な停止
4. **自己修復アーキテクチャ**: weak nil化による自動的な迂回
これは「Eventually Consistent」から「**Immediately Deterministic**」へのパラダイムシフトです。
GCを捨て、「生死の決定論」を分散の原語にすることで、従来不可能だった**故障への即座対応**と**確実なリソース管理**を両立できます。これがNyashの真の革新性です。

View File

@ -0,0 +1,215 @@
# AI先生たちのNyash言語仕様に関する純粋な会話記録2025-08-31
実装詳細Rust等を一切含まない、純粋にNyash言語仕様のメモリ管理に基づいた相談と回答です。
## Gemini先生との会話
### 私からの相談内容
```
Nyashプログラミング言語のメモリ管理について相談です。
【重要】実装言語Rust等の話は一切含めないでください。純粋にNyash言語仕様として考えてください。
【Nyashメモリ管理の本質】
1. スコープベースの決定論的解放
- Boxがスコープを抜けたら即座に解放される
- 手動でのメモリ管理不要、自動的にメモリが返却される
2. finiシステム
- オブジェクトの論理的な終了を宣言できる
- 物理的な破棄とは独立(参照が残っていても論理的に死んでいる状態)
- 死んだオブジェクトへのアクセスは安全にエラーになる
3. weak参照の自動nil化
- 強参照が0になったら全てのweak参照が自動的にnilになる
- ダングリングポインタが原理的に発生しない
- オブジェクトが生きているかをweakで安全にチェック可能
4. 決定論的な解放順序
- init宣言の逆順で解放されるカスケード解放
- 親が解放されたら子も確実に解放される
- メモリリークが構造的に防げる
5. Everything is Box哲学
- 全ての値がBoxオブジェクトとして統一的に扱われる
- スレッドセーフがNyash言語レベルで保証されるsyncが用意されている
【質問】
1. この決定論的メモリ管理を最大限に活かせる革新的なアプリケーションは?
2. 他の言語GCありの言語、手動管理の言語では実現困難だがNyashなら簡単な例は
3. 特にfiniとweakの組み合わせが生む新しいプログラミングパターンは
4. P2P/分散環境でこの決定論性が生む可能性は?
```
### Gemini先生の回答
Nyash言語のメモリ管理に関するご質問、ありがとうございます。提示いただいた5つの本質は、非常に興味深く、強力なコンセプトですね。これらが組み合わさることで生まれる可能性について、言語仕様の観点から考察します。
### 1. この決定論的メモリ管理を最大限に活かせる革新的なアプリケーションは?
決定論的な性質は、リソースのライフサイクルが厳密に管理されるべき領域で絶大な効果を発揮します。
* **リアルタイム・インタラクティブシステム:**
* **アプリケーション:** ゲームエンジン、音楽制作ソフトDAW、VR/AR環境、物理シミュレーション。
* **理由:** GCによる予測不能な停止Stop-the-Worldが一切発生しないため、フレームレートの安定や低レイテンシが保証されます。シーン遷移やオブジェクトの動的な生成・破棄において、リソースメモリ、GPUバッファ、サウンドハンドル等がスコープを抜けた瞬間に即座に解放されるため、パフォーマンスが安定し、リソースリークも構造的に防げます。
* **高信頼性が求められる組込み/制御システム:**
* **アプリケーション:** ファクトリーオートメーション、ロボット制御、医療機器。
* **理由:** メモリ使用量が予測可能であり、システムの振る舞いが時間と共に安定します。`init`の逆順でのカスケード解放は、複雑に依存しあう制御モジュール群を安全にシャットダウンさせる上で極めて重要です。
* **サーバーレス/エッジコンピューティング環境:**
* **アプリケーション:** 高速に起動・終了するFaaSFunction as a Serviceのランタイム、リソース制約の厳しいエッジデバイス上のアプリケーション。
* **理由:** リクエスト単位でスコープを作成すれば、処理完了後に使用した全リソースが即時かつ完全に解放されることが保証されます。これにより、次のリクエストにクリーンな状態で素早く応答でき、テナント間のリソース分離も確実になります。
### 2. 他の言語では実現困難だがNyashなら簡単な例は
**例:循環参照を含む複雑なオブジェクトグラフの安全な解体**
GUIのコンポーネントツリーを考えます。`Window`があり、その中に`Panel`があり、`Panel`の中に`Button`があるとします。ここで、`Button`が親の`Window`を(何らかのコールバックのために)参照するという循環参照が存在するケースを考えます。
* **GCあり言語 (Java, C#, Python):**
* **困難な点:** `Window`を不要にしても、`Button`からの参照が残っているため、`Window`, `Panel`, `Button`のサイクル全体がGCに回収されず、メモリリークとなります。これを回避するには、手動で参照を断ち切る`button.window = null`)か、`WeakReference`のような仕組みを慎重に使う必要があります。
* **Nyashの場合:** `Button`から`Window`への参照を`weak`にするだけで問題が解決します。`Window`がスコープを抜けると、まず`Window`への強参照が消えます。これにより、`Button`が持つ`weak`参照は自動的に`nil`になります。その後、`init`の逆順で`Panel``Button`がカスケード式に解放されます。開発者は循環参照の存在を意識しつつも、解放ロジックを自前で書く必要がありません。
* **手動管理言語 (C, C++):**
* **困難な点:** `Window`のデストラクタで`Panel``delete`し、`Panel`のデストラクタで`Button``delete`する、という手動での連鎖的な解放が必要です。もし外部のどこかから`Button`へのポインタが残っていた場合、`Window``delete`した後にそのポインタにアクセスすると未定義動作(ダングリングポインタ)を引き起こします。
* **Nyashの場合:** `weak`参照の自動`nil`化により、ダングリングポインタが原理的に発生しません。外部のコードは`weak`参照が`nil`でないかを確認するだけで、オブジェクトがまだ生存しているかを安全にチェックできます。
### 3. 特に`fini`と`weak`の組み合わせが生む新しいプログラミングパターンは?
`fini`(論理的な死)と`weak`(存在確認)の組み合わせは、オブジェクトの状態管理に新しい次元をもたらします。
**パターン名グレースフル・デコミッションGraceful Decommissioning**
これは、オブジェクトが即座に消えるのではなく、「終了処理中」という中間状態を経て安全に消滅するためのパターンです。
1. **デコミッションの開始:** オブジェクト(例:ネットワーク接続マネージャ)を破棄したいとき、まず`fini`を呼び出します。これによりオブジェクトは「論理的に死んだ」状態になります。
2. **新規リクエストの拒否:** `fini`されたオブジェクトへのアクセスはエラーになるため、新しいリクエスト(例:新規接続の要求)は安全に弾かれます。
3. **進行中タスクの完了:** しかし、オブジェクトのメモリはまだ存在するため、内部で進行中だったタスク(例:データの送信処理)は完了させることができます。
4. **外部からの安全な監視:** 他のオブジェクトは、このマネージャへの`weak`参照を持ちます。
* `weak`参照をチェックし、`nil`でなければ「まだ物理的には存在している」とわかります。
* さらにそのオブジェクトが`fini`済みかを確認することで、「現在シャットダウン処理中である」と判断できます。
5. **完全な消滅:** 進行中のタスクがすべて完了し、オブジェクトが自身のスコープを抜けるなどして強参照がすべてなくなると、物理的に解放され、すべての`weak`参照が`nil`になります。
このパターンは、システムのコンポーネントを、システム全体を停止させることなく、安全かつ段階的に入れ替えるような動的なアーキテクチャで極めて有効です。
### 4. P2P/分散環境でこの決定論性が生む可能性は?
ローカルノードでの決定論性は、分散システム全体の予測可能性と信頼性を向上させる基盤となり得ます。
* **分散合意プロトコルの簡素化:**
* 分散システムでは、ノードのクラッシュやネットワーク分断によってリソース(分散ロックなど)が解放されなくなる問題が頻発します。これを防ぐために、リース(時間貸し)や複雑な監視機構が使われます。
* Nyashの決定論的な解放を応用し、「特定の処理スコープに対応する分散トランザクション」のような概念を導入できれば、トランザクションが成功または失敗してスコープを抜けた際に、関与したすべてのード上のリソースがローカルのルールに従って確実に解放されるというプロトコルを設計しやすくなります。これにより、タイムアウトに頼らない、より決定論的な分散リソース管理が期待できます。
* **予測可能なリソースフットプリント:**
* P2Pネットワークに参加する各ードは、他のードのためにリソース帯域、ストレージを提供します。
* Nyashで実装されたードは、処理あるチャンクの転送が完了すれば、それに関連するリソースが即座に解放されるため、ードのリソース使用量を非常に正確に予測・管理できます。これにより、ネットワーク全体の負荷をより公平かつ効率的に分散させる高度なスケジューリングが可能になるかもしれません。
* **決定論的リプレイとデバッグ:**
* 分散システムのデバッグは困難を極めます。
* 各ノードの動作が決定論的であれば、外部からの入力イベントのシーケンスさえ記録しておけば、システム全体の振る舞いを正確にリプレイできる可能性が高まります。`fini``weak`参照の状態変化も追跡することで、複雑な非同期イベントの連鎖によって引き起こされる問題を特定しやすくなります。
## ChatGPT5先生との会話
### 私からの相談内容
```
Nyashプログラミング言語のメモリ管理について相談です。
【重要】実装言語Rust等の話は一切含めないでください。純粋にNyash言語仕様として考えてください。
【Nyashメモリ管理の本質】
1. スコープベースの決定論的解放
- Boxがスコープを抜けたら即座に解放される
- 手動でのメモリ管理不要、自動的にメモリが返却される
2. finiシステム
- オブジェクトの論理的な終了を宣言できる
- 物理的な破棄とは独立(参照が残っていても論理的に死んでいる状態)
- 死んだオブジェクトへのアクセスは安全にエラーになる
3. weak参照の自動nil化
- 強参照が0になったら全てのweak参照が自動的にnilになる
- ダングリングポインタが原理的に発生しない
- オブジェクトが生きているかをweakで安全にチェック可能
4. 決定論的な解放順序
- init宣言の逆順で解放されるカスケード解放
- 親が解放されたら子も確実に解放される
- メモリリークが構造的に防げる
5. Everything is Box哲学
- 全ての値がBoxオブジェクトとして統一的に扱われる
- スレッドセーフがNyash言語レベルで保証されるsyncが用意されている
【特にGemini先生の提案】
- グレースフル・デコミッション: finiとweakの組み合わせパターン
- リアルタイムシステムでの決定論性活用
- 分散合意プロトコルの簡素化
【質問】
1. この決定論的メモリ管理がP2P/NyaMesh環境で生む革新的な可能性は
2. finiシステムを分散環境での「論理的な死」に拡張する方法は
3. weak自動nil化を活用した新しい分散アーキテクチャは
4. 他の分散システムでは困難だがNyashなら実現可能な例は
```
### ChatGPT5先生の回答
以下はNyash言語仕様のメモリモデル決定論的解放、fini、weak自動nil化、逆順解放、Everything is Boxを前提に、P2P/NyaMeshでの設計指針と新機軸の提案です。
**P2Pでの可能性**
- 強制リーク不可能なオーバレイ: ルート/セッション/会員情報をBox化し、強参照消滅で確実に消えるため、ゾンビ状態を構造的に排除。
- スコープ=リース: 参加・ルート・セッションを「スコープ付きリース」として扱い、終了時に即時解放されルーティング表が自動整合。
- 逆順カスケード停止: 親(ノード/セッション)→子(フロー/バッファ)の順で安全に停止波及し、部分破壊や順序逆転を抑止。
- メモリ主導フロー制御: 送信クレジット/バッファをBox化し、解放=明確なバックプレッシャーとして働くため、過負荷時の挙動が決定論的。
- 再現可能性/監査容易: ライフタイムが決定論的で、シミュレーションやフォレンジック再現が容易(どの時点で何が死ぬかが一意)。
- 心拍依存の縮減: weak自動nil化により「生存確認」は参照状態で判定でき、ポーリング/心拍の頻度を下げられる。
**分散finiの拡張**
- finiドメイン: オブジェクトに「ドメイン」を付与し、finiの到達範囲/ポリシーを規定(ローカル/ピア集合/トポロジ領域)。
- 終了等級: Local-finiード即時エラー化、Domain-fini多数決/しきい値で論理死、Epoch-fini世代切替、TTL-fini期限死
- トンブストーン/世代: 論理死を単調増加の世代番号と原因コードで表明し、重複/乱順配送に対して冪等に適用。
- リース=強参照、委譲=強参照移転: 誰が生存責任を持つかを言語レベルで明確化。リース喪失で全weakがnil。
- アクセス規則の二段階化: Local-finiでローカルは即エラー、Domain-fini観測で全域エラー。weakはどちらでもnilイベントを受領。
- 監視プリミティブ: watch(weak)で「死の通知」を一回だけ受け取り、再購読なしに後片付けを確定的に実行。
**weak自動nil化が導くアーキテクチャ**
- 能力キャッシュの自然失効: リモート能力へのweak参照を配布し、能力の実体が消えれば自動nilで整合が取れる。
- エフェメラル・チャネル: パブリッシャが強参照、サブスクライバはweakで監視。発行元が消えれば全購読が即時nil。
- メンバーシップ=weak集合: クラスタ/スワームをweak集合で表現し、非在化が自動反映。別途ガーベジ手続き不要。
- 招待/セッション鍵=招待Box: 参加者はweakで招待を監視、主がfiniすれば招待は無効化。期限・回収が自動。
- ルータの無保持化: 中継はフロー状態へのweakのみ保持。両端の強参照が切れれば経路状態は自動消滅。
- キャッシュの権威弱結合: エントリは権威Boxへのweak錨を持ち、権威が死ねばキャッシュ行は一斉nilで整合。
**Nyashならではの実現例**
- ゼロリークDHT/オーバレイ: ードBox配下にエントリ/セッションをぶら下げ、逆順解放で孤児状態を禁止。
- 優雅なデコミッション: ードBoxにfini→全能力が弱化→新規受付停止→強参照が尽きたら自動nil→完全撤収が決定論的。
- ハードリアルタイムRPC: GC待ちや不定期ファイナライズ無し。タイムボックスと解放タイミングが一致し、遅延上限が厳密。
- 分散ロック不要の一回性: Job Boxを単一強参照にし、完了finiで全レプリカのweakがnil→重複実行が構造的に不可能。
- セッション世代切替: Epoch Boxを作り替えるだけで旧世代のweakが一斉nil→古いビュー/鍵/ルートの掃除が瞬時。
- 秘密情報の即時抹消: Key Boxのfiniで全派生状態がカスケード解放→参照側はエラー/weak nilで後工程が確定的。
- タイマレス・リース: リソース/帯域/優先度をスコープ化し、保持=利用、終了=回収。切替や回収がアラーム不要で正確。
**設計パターン(提案)**
- ドメイン付きBox: `Box<T> @domain(policy)` でfiniの適用範囲/合意条件を明示。
- 世代ガード: `Gen<T>`で現在世代のみ強参照可能、旧世代は弱参照に自動降格。
- 観測API: `watch(weak)`で一度きりのnil通知、`on_fini`で論理死通知、両者は因果順序を保証。
- 依存グラフ解放: 親子関係をinit順に構築し、逆順finiで停止順序の安全性とデッドロック回避を担保。
必要なら、上記のいずれかを具体的なプロトコル設計(メンバーシップ、ストリーミング、鍵ローテーション、ジョブ実行)に落とし込みます。どれから掘りますか?
## まとめ
両先生とも、Nyashの決定論的メモリ管理が持つ革新的な可能性を、実装詳細に依存しない純粋な言語仕様の観点から分析してくれました。
### Gemini先生の主要な洞察
- **グレースフル・デコミッション**: finiとweakを組み合わせた安全な段階的終了パターン
- **リアルタイムシステム**: GCの不確実性がない決定論的な性能保証
- **循環参照の自然解決**: weakによる安全な参照グラフ解体
### ChatGPT5先生の主要な洞察
- **強制リーク不可能なオーバレイ**: 構造的にゾンビ状態を排除
- **分散finiの拡張**: ドメイン、終了等級、トンブストーンによる分散環境での論理死
- **weak自動nil化アーキテクチャ**: 能力キャッシュ、エフェメラル・チャネル、メンバーシップ管理
これらの提案は、Nyashが単なるメモリ管理を超えて、新しいプログラミングパラダイムを提供する可能性を示しています。

View File

@ -0,0 +1,239 @@
# AI先生たちのNyashアプリケーション提案2025-08-31
Nyashの特殊でシンプルなメモリ管理Arc<Mutex>統一アーキテクチャを最大限に表現するアプリケーションについて、Gemini先生とChatGPT5先生から革新的な提案をいただきました。
## Gemini先生の提案
### 相談内容
```
Nyashプログラミング言語のメモリ管理について相談です。
【Nyashのメモリ管理の特徴】
- Everything is Box哲学すべてがBoxオブジェクト
- Arc<Mutex>統一アーキテクチャ全Box型で統一
- 参照カウント式のシンプルなGC
- プラグインシステムとの統合
- ハンドルベースのFFI
【問題】
論文用にbinary-trees、n-body、mandelbrotなどの標準ベンチマークが推奨されていますが、これらは小規模でNyashの特殊なメモリ管理の良さを表現しきれません。
【質問】
1. Nyashのメモリ管理の特殊性を活かせる大規模アプリケーションは
2. Arc<Mutex>の並行性を活かしたデモは?
3. プラグイン間でのBox共有を示すアプリケーションは
4. 他言語では難しいがNyashなら簡単に実装できる例は
```
### Gemini先生の回答
Nyashプログラミング言語のメモリ管理に関するご相談、ありがとうございます。言語設計の観点から、そのユニークなアーキテクチャの強みを最大限に表現できるアプリケーションについて、以下の通り提案します。
Nyashの「Everything is Box」哲学と`Arc<Mutex>`統一アーキテクチャは、従来の数値計算ベンチマークでは評価しにくい、**動的で状態が複雑に変化する大規模な並行処理**において真価を発揮します。論文でその独自性と優位性を示すには、まさにその特性を要求するアプリケーションを提示することが効果的です。
---
### 1. Nyashのメモリ管理の特殊性を活かせる大規模アプリケーション
**提案:リアルタイム・コラボレーティブ・アプリケーション基盤**
Google Docsのような共同編集エディタ、Figmaのようなデザインツール、またはマルチプレイヤーオンラインゲームのサーバーサイドなど、複数の主体が単一の共有状態を同時に変更するアプリケーションの基盤です。
**Nyashの強みが活きる点**
* **安全な状態共有:** ドキュメントのデータ構造(ツリー、グラフ)、あるいはゲームワールドの状態全体を`Box`オブジェクトとして表現します。`Arc<Mutex>`アーキテクチャにより、多数のユーザー(クライアント)からの同時編集リクエストがデータ競合を起こすことなく、安全に処理されることが言語レベルで保証されます。
* **動的なオブジェクト管理:** ユーザーのカーソル、テキストオブジェクト、図形、NPC、アイテムなど、アプリケーション内のあらゆる要素を統一された`Box`として動的に生成・破棄できます。「Everything is Box」哲学により、異種のオブジェクトをコレクションで扱う際も型システムが煩雑になりません。
* **並行処理との親和性:** 各クライアントからの操作を個別のタスクスレッドで処理するアーキテクチャと非常に相性が良いです。Nyashでは、開発者はロックの管理を意識することなく、ビジネスロジックの記述に集中できます。
このアプリケーションは、小規模なデータ構造を並列で計算するベンチマークとは対照的に、**巨大で複雑な単一の状態**を多数のアクターが**並行かつ安全**に変更し続けるという、Nyashの設計思想に最も合致したシナリオです。
### 2. `Arc<Mutex>`の並行性を活かしたデモ
**提案並行Webクローラー**
複数のワーカースレッドが並行してWebページをダウンロードし、ページ内のリンクを解析して、共有された「未訪問URLリスト」に追加していくアプリケーションです。
**Nyashの強みが活きる点**
* **シンプルな共有状態:** 「未訪問URLのキュー」と「訪問済みURLのセット」という2つの中心的な共有状態を、それぞれ`Box`化するだけで済みます。
* **コードの簡潔さ:** 他の言語では、共有データへのアクセスごとに `mutex.lock().unwrap()` のような明示的なロック獲得・解放処理が必要ですが、Nyashの統一アーキテクチャはこれを言語機能として抽象化します。これにより、並行処理のロジックが非常にクリーンかつ直感的に記述でき、デッドロックなどの典型的なバグを減らせます。
このデモは、Nyashがいかにして並行プログラミングの複雑さを軽減し、開発者がロジックそのものに集中できるようにするかを明確に示します。
### 3. プラグイン間でのBox共有を示すアプリケーション
**提案プラグインベースのECS (Entity-Component-System) アーキテクチャ**
ゲーム開発で多用されるECSパターンをNyashで実装します。これは、データComponentとロジックSystemを完全に分離する設計です。
* **Entity:** `Box`化された一意のID。
* **Component:** `Box`化された純粋なデータ(例: `Position { x, y }`, `Velocity { dx, dy }`)。
* **System:** 特定のロジックを実装した**プラグイン**(例: `PhysicsSystem`, `RenderSystem`)。
**Nyashの強みが活きる点**
* **究極のデータとロジックの分離:** `PhysicsSystem`プラグインは`Position``Velocity`を持つ全エンティティの`Box`を走査して位置を更新し、`RenderSystem`プラグインは`Position``Sprite`を持つエンティティの`Box`を走査して描画します。このように、**複数の独立したプラグインが同じデータ(`Box`)を共有し、それぞれの関心事に沿って操作する**様子を鮮やかに示せます。
* **動的な機能拡張:** `AISystem``AnimationSystem`といった新しい振る舞いを、既存のコードベースに一切変更を加えることなく、プラグインとして動的にロードできます。これはNyashのプラグインシステムと`Box`共有アーキテクチャの強力な組み合わせを実証します。
### 4. 他言語では難しいがNyashなら簡単に実装できる例
**提案:アプリケーションのライブ・リローディング(ホットスワップ)**
長時間稼働するサーバーアプリケーション(例: Web APIサーバーを停止させることなく、その一部の機能を新しいバージョンに更新するデモです。
**Nyashの強みが活きる点**
* **統一オブジェクトモデルの威力:** Nyashでは、関数やクロージャさえもが`Box`オブジェクトとして扱えます。これにより、特定のAPIエンドポイントの処理ロジックを実装した関数オブジェクトを、実行時にアトミックに安全に差し替えることが原理的に容易になります。
* **プラグインとFFIの活用** 新しいバージョンのロジックを独立したプラグイン(.so/.dllファイルなどとしてコンパイルします。サーバーは稼働中にこのプラグインを動的にロードし、ハンドルベースFFIを介して新しい関数`Box`への参照を取得し、ルーティングテーブル内の古い参照と入れ替えます。
* **他言語との差別化:**
* RustやC++のような静的言語でこれを実現するには、非常に複雑な設計と手作業でのメモリ管理が必要です。
- PythonやRubyのような動的言語では可能ですが、GILグローバルインタプリタロックの存在により、Nyashのような真の並行性は得られません。
Nyashは、**静的言語の安全性とパフォーマンス**のポテンシャルを持ちながら、**動的言語の柔軟性**を兼ね備えている、というユニークなポジションをこのデモで示すことができます。
---
### まとめ
Nyashのメモリ管理の真価は、静的な計算性能ではなく、**「複雑で動的な状態を、安全かつ並行に、そしてモジュール的に扱う能力」**にあります。提案したアプリケーションは、この言語の核心的な価値を学術的にも実践的にもアピールするための強力な材料となるでしょう。
## ChatGPT5先生の提案
### 相談内容
```
Nyashプログラミング言語のメモリ管理の強みを表現するアプリケーションについて相談です。
【Gemini先生の提案】
1. リアルタイム・コラボレーティブ基盤Google Docs風の共同編集
2. 並行Webクローラー
3. プラグインベースのECSEntity-Component-System
4. ライブ・リローディング(ホットスワップ)
【Nyashの特殊性】
- Arc<Mutex>統一アーキテクチャで全Boxが自動的にスレッドセーフ
- プラグイン間でのBox共有が容易
- 参照カウントベースのシンプルなGC
- Everything is Box哲学
【質問】
1. P2Pネットワークを活用したNyash独自のアプリケーションは
2. NyaMeshとの統合を前提とした分散アプリケーションの例は
3. Box共有の安全性を活かした「他言語では危険だがNyashなら安全」な例は
4. 論文で最もインパクトがあるデモは何でしょうか?
```
### ChatGPT5先生の回答
了解です。Nyashの「Arc<Mutex>統一」「Everything is Box」「参照カウントGC」「プラグイン間Box共有」を強みに据え、P2P/NyaMesh前提で"Nyashならでは"を狙う案をまとめました。
**設計キーモチーフ**
- Box-as-capability: Box自体を能力トークンとして共有・移譲・複製する設計。
- 内容アドレス化: 大きなBoxは内容ハッシュで識別し、P2Pで重複排除。
- 暗黙スレッドセーフ: 共有は常に `Arc<Mutex<Box<T>>>`。ゼロコピー志向、衝突はMutexで局所化。
- ホットスワップ既定路: 新旧プラグインが同じBoxを安全に参照継続。
**1. P2P独自アプリNyashらしさ最大化**
- BoxTorrent: 内容アドレス化BoxのP2P配布基盤
- 大容量データや中間生成物を「Box=DAGード」として配布。変換プラグインは入力Box→出力Boxを生成し、ハッシュで再利用。
- Nyash優位: ゼロコピー共有、ホットスワップで変換器の差し替え可、参照カウントで自然にキャッシュエビクション。
- Live Shared Heap: メッシュ越し共有ヒープ
- 論理的に単一のShared HeapにBoxを配置。所有権はメッシュ内で移譲lease、読み取りは複製、書き込みはロックバージョニング。
- Nyash優位: 全Boxスレッドセーフ前提で分散ロックを単純化。プラグインがヒープ上の同一Boxをそのまま扱える。
- P2P Live-ML Pipeline
- センサ→前処理→特徴→推論→ポストの各段をプラグイン化し、ピア間でBoxバッファをパス。途中段のホットアップグレードや分岐実験が容易。
- Nyash優位: 各段が同じBoxを共有しゼロコピー。落ちたピアの未解放は参照カウントで自然解放。
- Cooperative Rendering/Video Mixer
- 各ピアがレイヤBoxを提供し、合成ードがロック付きで読み取り、最終フレームBoxを配信。遅延抑制のため差分Boxタイルを伝播。
- Nyash優位: 共有フレームバッファを安全に多者で読む/書く。ホットスワップでフィルタ入替。
**2. NyaMesh統合前提の分散例**
- MeshRef/Lease機構
- NyaMeshが提供するID/ルーティング上で `BoxID` をグローバル化。lease数が0でGC。複製はK-レプリカ。ゴシップで参照数を伝播。
- 使い道: Live Shared Heap/BoxTorrentの基盤。ネット分断でもlease期限で回復。
- NyaMesh PubSub CRDT-Box
- CRDTレジスタ/マップ/ログをBox実装。書込みはMutex保護バージョン、伝播はNyaMesh PubSub。競合はCRDTで収束。
- 使い道: 協同編集、分散ECS、分散設定管理。
- 分散ECSEntity=Box
- Entity/ComponentをBoxとしてNyaMesh上で移動。負荷に応じてオーナーシップを移譲。システムはプラグインで増減。
- 使い道: MMO風シミュ、ロボット群制御、IoTツイン。
- Edge Cache/Compute
- 内容アドレスBoxをNyaMesh距離で配置最適化。近傍にレプリカ、遠方はリモート参照。ホットスワップで演算器を近傍に移設。
**3. 他言語だと危険だがNyashなら安全な共有**
- ゼロコピー共有バッファの多段パイプ
- 大容量 `Box<ByteBuf>` を複数プラグインが並列処理。ダングリング/データ競合はArc<Mutex>で回避。古いプラグイン残留参照も安全に自然解放。
- ライブ・ホットスワップ中の同一Box共有
- 新旧プラグインが同じBoxを参照し続け、切替中も落ちない。通常はABI/所有権で危険だが、Box境界で能力を渡すだけで成立。
- 共有メモリマップファイルの安全クローズ
- 多スレッド/多プラグインで参照する `Box<Mmap>`。最後の参照が落ちた瞬間のみクローズされ、早期クローズ・UAFを防止。
- マルチプロデューサ・マルチコンシューマなリングバッファ
- `Box<Ring<T>>` をMutexで保護し、try_lockバックプレッシャで輻輳制御。言語レベルunsafe不要。
- 強制隔離されない共同編集のポインタ共有
- プラグイン間で直接同じBoxドキュメントを参照CRDT。ロックバージョンで整合、コピー不要。通常は破滅的競合を招く領域。
注意: デッドロックは設計課題なので、型でロック順序を表す/細粒度ロック/try_lock再試行/単一スレッドオーナー+メッセージ駆動等で回避。
**4. 論文で最もインパクトがあるデモ**
- 分散ホットスワップ・パイプライン(エンドツーエンド無停止)
- 構成: NyaMesh上のパイプラインセンサ→前処理→推論→配信。各段はプラグイン。高負荷時に段を増殖/縮退、同時に新バージョンへホットスワップ。
- デモ見せ場: スループット維持、フレーム落ち/UAF/クラッシュゼロ、旧版プラグインが参照するBoxの自然解放、Boxの移譲/複製の可視化。
- 計測: p99レイテンシ低下、スワップ時間中断ゼロ、メモリリーク0、再処理なしのバイナリ差替成功率。
- 共有ヒープ越しのP2Pコラボ編集巨大データ
- 構成: 1GB級ドキュメント/モデルを「単一Box」として複数ユーザが同時編集。差分はタイルBoxとして伝搬、CRDT-Boxで収束。
- 見せ場: ゼロコピー、Boxの局所ロックで高スループット、ード離脱でも自然復旧。
- 分散ECSワールドのライブ移住
- 構成: 数百万EntityのECS。混雑エリアを別ピアへオーナー移譲しながら、プレイ継続・スナップフリー。
- 見せ場: Entity=Box移譲、参照が残る間は両ピアで安全アクセス、解放の自動同期。
- BoxTorrentの内容アドレスDAG再計算スキップ
- 構成: DAG変換パイプ。同一ハッシュ出力をグローバル再利用し、再計算ゼロに。
- 見せ場: キャッシュヒット率と電力削減、重複排除。
**評価指標(論文化しやすい軸)**
- 安全性: UAF/ダブルフリー/データ競合/クラッシュの発生率0。
- 可用性: ホットスワップ中断時間0、p99レイテンシ悪化<5%。
- 効率: ゼロコピー率再計算/再送削減RAMフットプリント参照カウントによる自然エビクション)。
- スケール: ピア数とスループットの線形性チョークポイント不在
- 回復性: ノード喪失下でのデータ保全率復旧所要時間
**リスクと対策**
- デッドロック: ロック順序規約/静的解析/try_lock指数バックオフ
- 参照サイクル: 明示的Weak参照世代別GC/epochでのリーク検出ダッシュボード
- 分散参照数の正確性: lease期限ゴシップ再確認チェックポイント
- 一貫性と遅延: CRDT採用書込みはバージョン化Boxで楽観制御
- セキュリティ: Box能力にスコープ/寿命/権限を付与capability化)。
**次の一手(提案)**
- 小さなPoC: BoxTorrentのミニ版内容アドレスBoxNyaMesh PubSub参照カウント連携を最短で実装
- 併走PoC: 分散ホットスワップパイプライン2段で無停止差替を実証
- 計測仕込み: 参照グラフ可視化p99レイテンシ/スループット/リーク監視
どの路線を優先しますか最短でインパクトを狙うなら分散ホットスワップパイプラインBoxTorrent+DAG再利用が論文映えします要望があれば選んだ案のアーキ図と最小APIBoxID/lease/replicate/transferの草案を書きます
## 深い理解に基づく再提案GC無しの本質
### Gemini先生の再提案「ライフサイクル指向プログラミング」
Nyashのメモリ管理は単なる技術的優位性を超え開発者に新しい思考の枠組みを提供します
- **論理的死と物理的生存の分離 生きたログとしてのオブジェクト**
- **遺言パターン 死後の自己報告によるデバッグと監視**
- **Weak自動nil化 自己修復する回復力のあるデータ構造**
- **決定的解放順序 安全な解体が保証された積層的設計**
これらはすべてGCの不確実性からプログラマを解放しシステムの振る舞いを時間軸に沿って正確に予測設計することを可能にしますこれはもはやメモリ管理ではなく、**「ライフサイクル指向プログラミング」**と呼ぶべき新しいパラダイムです
### ChatGPT5先生の再提案「生死の決定論を分散原語に」
GCを捨て、「生死の決定論を分散の原語にすることで解体故障再構成が予測可能になり自己修復とゼロリークを両立できます
- **LifeTokenモデル**: 各ノードの`LifeToken``Weak`で保持クラッシュで全weakが自動nil化
- **Two-Phase Fini**: 依存DAG逆順でfini合意分散環境での決定的解体
- **Lifecycle Actors**: 親scopeにタスクを紐付け親finiで逆順に確定終了
- **自己修復DHT**: ルーティングテーブルを`Weak<NodeRef>`で構築nil化で即座に再配線
## まとめ
Gemini先生とChatGPT5先生の提案はNyashの本質的な強みを異なる角度から照らし出しています
- **Gemini先生**: ローカル環境での決定論的ライフサイクル管理の革新性
- **ChatGPT5先生**: 分散環境での生死の決定論による自己修復と安全性
重要なのはこれらが**GCという概念とは無関係**Nyashの基本的なメモリ管理セマンティクススコープベース解放finiシステムweak自動nil化決定的順序から自然に導かれることです
両者の提案を統合することでNyashのEverything is Box哲学と決定論的メモリ管理が単なる実装の詳細ではなく、**新しいプログラミングパラダイムの基盤**であることが証明されます

11
apps/DELETE_THESE.txt Normal file
View File

@ -0,0 +1,11 @@
以下のディレクトリはテスト用で、apps/tests/に移動済みです。
手動で削除してください:
rm -rf ny-echo-lite/
rm -rf ny-map-llvm-smoke/
rm -rf ny-vinvoke-smoke/
rm -rf ny-vinvoke-llvm-ret/
rm -rf ny-vinvoke-llvm-ret-size/
これらはすでに apps/tests/ にコピー済みで、
tools/llvm_smoke.sh も更新済みです。

View File

@ -0,0 +1,74 @@
# LLVM/AOT Test Programs
このファイルはLLVMバックエンドのテスト用プログラムについて説明します。
これらは主にCIスモークテストで使用されます。
**注意**: これらのテストプログラムは `apps/tests/` ディレクトリに移動されました。
## テストプログラム一覧
### 基本動作テスト
- **ny-llvm-smoke/** - ArrayBox基本操作push/get+ print
- 現在の問題: ArrayBoxプラグインの引数エンコーディング問題で "Invalid arguments"
- 状態: NYASH_LLVM_ARRAY_SMOKE=0でデフォルトスキップ
- **ny-array-llvm-ret/** - ArrayBox戻り値テストprintなし
- 目的: print依存を排除して安定性向上
- 期待値: Result: 3
- **ny-echo-lite/** - 最小echo実装
- 目的: 標準入力/出力の基本動作確認
- 状態: NYASH_LLVM_ECHO_SMOKE=0でデフォルトスキップ
### プラグイン呼び出しテスト
- **ny-map-llvm-smoke/** - MapBoxプラグイン基本テスト
- 目的: by-idプラグイン呼び出しの動作確認
- 期待値: "Map: v=42" および "size=1"
### 可変長引数テストVInvoke
- **ny-vinvoke-smoke/** - 5引数呼び出し文字列出力
- 目的: 可変長引数≥3のtagged vector経路テスト
- 期待値: "VInvokeRc: 42"
- **ny-vinvoke-llvm-ret/** - 5引数呼び出し戻り値
- 目的: 可変長引数の戻り値検証
- 期待値: Result: 42
- **ny-vinvoke-llvm-ret-size/** - 0引数呼び出しsize())✅
- 目的: 固定長引数≤2の経路テスト
- 期待値: Result: 1
## CIスモークテストでの使用
`tools/llvm_smoke.sh`で以下の環境変数により制御:
```bash
# 基本テスト問題があるためデフォルトOFF
NYASH_LLVM_ARRAY_SMOKE=1 # ny-llvm-smoke
NYASH_LLVM_ARRAY_RET_SMOKE=1 # ny-array-llvm-ret
NYASH_LLVM_ECHO_SMOKE=1 # ny-echo-lite
# プラグインテスト(安定)
NYASH_LLVM_MAP_SMOKE=1 # ny-map-llvm-smoke
NYASH_LLVM_VINVOKE_SMOKE=1 # ny-vinvoke-smoke
NYASH_LLVM_VINVOKE_RET_SMOKE=1 # ny-vinvoke-llvm-ret + ny-vinvoke-llvm-ret-size
```
## 既知の問題
1. **ArrayBoxプラグイン**: set/getで "Invalid arguments" エラー
- 原因: プラグイン側の引数デコード問題
- 対策: 戻り値型テストで回避
2. **print文字列連結**: `print("Result: " + v)` での型エラー
- 原因: binop型不一致
- 対策: toString()を使用するか、戻り値型テストで回避
## 実アプリケーションとの違い
これらのテストプログラムは:
- 最小限の機能に絞った単体テスト
- CI自動実行を前提とした設計
- エラー時の切り分けが容易
実際のアプリケーションchip8_nyash、kilo_nyash等とは目的が異なります。

View File

@ -2,6 +2,8 @@
このディレクトリには、Nyashの実力を示す実用的なアプリケーションが含まれています。
開発・貢献に関する全体ガイドはリポジトリルートの`AGENTS.md`Repository Guidelinesを参照してください。プロジェクト構成、ビルド/テスト、PR要件の要点を簡潔にまとめています。
## 🚀 実装済みアプリケーション
### 🎮 ゲーム・エミュレータ
@ -177,6 +179,80 @@ box TreeNode {
- [ ] Lispインタープリター言語実装
- [ ] 静的サイトジェネレータ(実用性)
## 🚀 Nyashメモリ管理の真価を示す革新的アプリケーション
### AI先生たちの提案2025-08-31
Gemini先生とChatGPT5先生から、Nyashの決定論的メモリ管理スコープベース解放、finiシステム、weak自動nil化がもたらす新しいプログラミングパラダイムについて革新的な提案を受けました。
### 🌟 最優先実装候補
#### 1. **分散ホットスワップ・パイプライン**
**概要**: NyaMesh上でセンサ→前処理→推論→配信の各段をプラグイン化し、無停止で更新可能なMLパイプライン
**Nyashならではの特徴**:
- 🔄 **無停止プラグイン更新**: finiシステムにより論理的に終了しても物理的に参照可能
- 🧹 **決定的メモリ管理**: スコープ解放と逆順カスケードで予測可能な解放
-**性能維持**: p99レイテンシ悪化<5%、スループット維持
**他言語では困難な理由**:
- Rust/C++: 手動メモリ管理で複雑ホットスワップ時にUAFリスク
- Python/Ruby: GILにより真の並行性が得られない
#### 2. **BoxTorrent - 内容アドレス化P2P配布基盤**
**概要**: 大容量データや中間生成物をBox=DAGード」として配布し、変換プラグインで処理
**Nyashならではの特徴**:
- 📦 **ゼロコピー共有**: Arc<Mutex>で安全にBoxを共有
- 🔍 **内容ハッシュ重複排除**: 同一内容のBoxを自動的に再利用
- 🗑️ **自然なキャッシュ管理**: 参照カウントで不要データを自動削除
#### 3. **Live Shared Heap - メッシュ越し共有ヒープ**
**概要**: 論理的に単一のShared HeapにBoxを配置し、P2Pネットワーク上で共有
**Nyashならではの特徴**:
- 🌐 **分散ロックの単純化**: 全Boxがスレッドセーフ前提
- 🔌 **プラグイン透過性**: ヒープ上の同一Boxをそのまま扱える
- 🔧 **ノード障害耐性**: 参照カウントで自然復旧
### 📊 実装による測定可能な優位性
| 指標 | 期待される優位性 |
|------|-----------------|
| **安全性** | UAF/データ競合/クラッシュ発生率 0% |
| **可用性** | ホットスワップ中断時間 0秒 |
| **効率性** | ゼロコピー率 90%以上 |
| **拡張性** | ピア数に対して線形スケール |
| **回復性** | ノード喪失下での自動復旧 |
### 🎯 実装ロードマップNyash特化
#### Phase 1: ミニマムPoC1週間
- [ ] **BoxTorrentミニ版**: 内容アドレスBox + 参照カウント連携
- [ ] **測定基盤**: 参照グラフ可視化、メモリリーク監視
#### Phase 2: 分散デモ2週間
- [ ] **2段パイプライン**: センサ→処理のホットスワップ実証
- [ ] **性能計測**: p99レイテンシ、スループット監視
#### Phase 3: 論文向け完全版3週間
- [ ] **完全なMLパイプライン**: 4段以上の処理段
- [ ] **大規模ベンチマーク**: 100ード規模での性能実証
### 💡 Nyashだからこそ可能な革新
**「他言語では危険だがNyashなら安全」な例**:
1. **ゼロコピー共有バッファの多段パイプ**: 大容量Box<ByteBuf>を複数プラグインが並列処理
2. **共有メモリマップファイルの安全クローズ**: 最後の参照が落ちた瞬間のみクローズ
3. **マルチプロデューサ・マルチコンシューマなリングバッファ**: 言語レベルでunsafe不要
これらの実装により、Nyashの「Everything is Box」哲学とArc<Mutex>統一アーキテクチャが、単なる理論ではなく実用的な価値を持つことを証明します。
### 🔮 将来候補
- [ ] レイトレーサーCPU集約的
- [ ] Lispインタープリター言語実装
- [ ] 静的サイトジェネレータ(実用性)
## 🤝 貢献方法
新しいアプリケーションのアイデアや改善提案は大歓迎です!

View File

@ -28,8 +28,11 @@ static box Main {
parseArgs(args) {
local options = new MapBox()
loop(arg in args) {
// foreach は未対応のため、index で明示ループ
local i = 0
local n = args.length()
loop(i < n) {
local arg = args.get(i)
if arg == "--upper" {
options.set("upper", true)
} else if arg == "--lower" {
@ -39,6 +42,7 @@ static box Main {
} else if arg == "--help" || arg == "-h" {
options.set("help", true)
}
i = i + 1
}
return options
@ -47,8 +51,7 @@ static box Main {
processInput() {
loop(true) {
local input = me.console.readLine()
if input == null { break } // EOF
if input == null { return 0 } // EOF → 関数終了に変更break未対応MIR対策
local output = me.transformText(input)
me.console.log(output)
}

View File

@ -0,0 +1,76 @@
# Nyashメモリ管理ベンチマーク設計書
## 概要
イベントディスパッチャと揮発性リスナーを使用して、Nyashの決定論的メモリ管理の特性を測定・可視化するベンチマーク。
## 選定理由
- **最小実装**: GUIや並行処理なしで実装可能
- **全特性網羅**: スコープ解放、fini、weak自動nil化、カスケード順序を一度に示せる
- **測定可能**: 明確な数値指標で他言語と比較可能
## アーキテクチャ
### コンポーネント
1. **Dispatcher**: イベント発行者
- リスナーをweak参照の配列で保持
- イベント発行時に死んだリスナーを自動スキップ
2. **Listener**: イベント受信者
- 子リソースBoxを保持してツリー構造を形成
- finiで子→自身の順で解放カスケード
3. **Scope**: ライフタイム境界
- スコープ内でリスナー生成
- スコープ終了で一括解放
## 測定指標
### 主要メトリクス
1. **メモリピーク**: 最大メモリ使用量
2. **解放レイテンシ**: スコープ終了→解放完了の時間
3. **ディスパッチ効率**: イベント/秒
4. **カスケード順序確定性**: 解放順序の一致率100%が理想)
5. **残留メモリ**: 解放後の残存メモリNyashは0が理想
6. **ジッタ**: 各時間の分散(低いほど決定論的)
### 他言語との比較ポイント
- **GC言語Go/Java/JS**: 解放タイミングの非決定性
- **RAII系Rust/C++**: weak掃除ロジックの複雑さ
- **手動管理C**: メモリリーク・二重解放リスク
## 実装計画
### CLI仕様
```bash
nyash-mem-bench dispatch --listeners N --events M --fanout F --depth D
```
### パラメータ
- `N`: リスナー数
- `M`: イベント数
- `F`: 各ノードの子数(分岐率)
- `D`: ツリーの深さ
### 出力形式
```
construct_ms: 123.45
dispatch_ns_p50: 789
dispatch_ns_p99: 1234
fini_ms_avg: 45.67
fini_ms_max: 89.01
mem_peak_mb: 256.78
mem_after_fini_kb: 0
order_ok: true
weak_prune_count: 5000
```
## 実装ステップ
1. **Phase 1**: 基本構造Dispatcher、Listener、Scope
2. **Phase 2**: 測定機能(時間、メモリ)
3. **Phase 3**: 解放順序ログ
4. **Phase 4**: 統計出力・可視化
## 期待される成果
- Nyashの決定論的メモリ管理の優位性を数値で証明
- 論文用の説得力のあるベンチマーク結果
- 他言語実装者にとってのリファレンス実装

View File

@ -0,0 +1,231 @@
// Nyashメモリ管理ベンチマーク
// イベントディスパッチャ+揮発性リスナーで決定論的メモリ管理を実証
static box Main {
init { console, stats }
main() {
me.console = new ConsoleBox()
me.stats = new StatsBox()
// コマンドライン引数の解析
local args = new MapBox()
args.set("listeners", 1000) // デフォルト値
args.set("events", 10000)
args.set("fanout", 3)
args.set("depth", 4)
me.console.log("=== Nyash Memory Management Benchmark ===")
me.console.log("Listeners: " + args.get("listeners"))
me.console.log("Events: " + args.get("events"))
me.console.log("Fanout: " + args.get("fanout"))
me.console.log("Depth: " + args.get("depth"))
// ベンチマーク実行
me.runBenchmark(
args.get("listeners"),
args.get("events"),
args.get("fanout"),
args.get("depth")
)
return 0
}
runBenchmark(listenerCount, eventCount, fanout, depth) {
local dispatcher = new EventDispatcher()
// 構築フェーズの計測
me.stats.startTimer("construct")
{
// スコープ内でリスナー生成
local listeners = new ArrayBox()
local i = 0
loop(i < listenerCount) {
local listener = new Listener("L" + i, fanout, depth)
listeners.push(listener)
dispatcher.addListener(listener)
i = i + 1
}
me.stats.stopTimer("construct")
me.stats.recordMemory("peak_before_dispatch")
// ディスパッチフェーズの計測
me.stats.startTimer("dispatch")
local j = 0
loop(j < eventCount) {
dispatcher.dispatch("event_" + j)
j = j + 1
}
me.stats.stopTimer("dispatch")
// スコープ終了による解放フェーズの計測
me.stats.startTimer("fini")
} // ここでlistenersスコープが終了し、カスケード解放が発生
me.stats.stopTimer("fini")
me.stats.recordMemory("after_fini")
// 結果出力
me.printResults(listenerCount, eventCount)
}
printResults(listenerCount, eventCount) {
me.console.log("\n=== Results ===")
me.console.log("construct_ms: " + me.stats.getTimer("construct"))
local dispatchTime = me.stats.getTimer("dispatch")
local perEvent = dispatchTime / eventCount
me.console.log("dispatch_ns_avg: " + (perEvent * 1000000))
me.console.log("fini_ms: " + me.stats.getTimer("fini"))
me.console.log("mem_peak_mb: " + me.stats.getMemory("peak_before_dispatch"))
me.console.log("mem_after_fini_kb: " + me.stats.getMemory("after_fini"))
me.console.log("order_ok: " + OrderLogger.isOrderCorrect())
me.console.log("weak_prune_count: " + EventDispatcher.getPruneCount())
}
}
// イベントディスパッチャweak参照でリスナー管理
box EventDispatcher {
init { listeners, pruneCount }
static pruneCountGlobal = 0
constructor() {
me.listeners = new ArrayBox()
me.pruneCount = 0
}
addListener(listener) {
// weak参照として保持
me.listeners.push(weak(listener))
}
dispatch(event) {
local activeListeners = new ArrayBox()
local i = 0
local len = me.listeners.length()
loop(i < len) {
local weakListener = me.listeners.get(i)
// weak参照が生きているかチェック
if weakListener != nil {
weakListener.onEvent(event)
activeListeners.push(weakListener)
} else {
me.pruneCount = me.pruneCount + 1
EventDispatcher.pruneCountGlobal = EventDispatcher.pruneCountGlobal + 1
}
i = i + 1
}
// 生きているリスナーのみを保持
me.listeners = activeListeners
}
static getPruneCount() {
return EventDispatcher.pruneCountGlobal
}
}
// リスナー(子リソースを持つツリー構造)
box Listener {
init { id, children, eventCount }
constructor(id, fanout, depth) {
me.id = id
me.children = new ArrayBox()
me.eventCount = 0
// 子ノードを再帰的に生成
if depth > 0 {
local i = 0
loop(i < fanout) {
local child = new Listener(id + "." + i, fanout, depth - 1)
me.children.push(child)
i = i + 1
}
}
}
onEvent(event) {
me.eventCount = me.eventCount + 1
}
fini() {
// 子→親の順で解放(カスケード)
local i = me.children.length() - 1
loop(i >= 0) {
local child = me.children.get(i)
if child != nil {
child.fini()
}
i = i - 1
}
// 解放順序をログ
OrderLogger.log(me.id)
}
}
// 統計収集Box
box StatsBox {
init { timers, memories }
constructor() {
me.timers = new MapBox()
me.memories = new MapBox()
}
startTimer(name) {
// 実際の実装では高精度タイマーを使用
me.timers.set(name + "_start", TimeBox.now())
}
stopTimer(name) {
local start = me.timers.get(name + "_start")
local elapsed = TimeBox.now() - start
me.timers.set(name, elapsed)
}
getTimer(name) {
return me.timers.get(name)
}
recordMemory(name) {
// 実際の実装ではシステムメモリ情報を取得
me.memories.set(name, 0) // プレースホルダー
}
getMemory(name) {
return me.memories.get(name)
}
}
// 解放順序ログ(シングルトン)
static box OrderLogger {
init { log, expectedOrder }
static instance = nil
static log(id) {
if OrderLogger.instance == nil {
OrderLogger.instance = new OrderLogger()
}
OrderLogger.instance.addLog(id)
}
constructor() {
me.log = new ArrayBox()
me.expectedOrder = new ArrayBox()
}
addLog(id) {
me.log.push(id)
}
static isOrderCorrect() {
// 実際の実装では期待される順序と比較
return true
}
}

31
apps/tests/README.md Normal file
View File

@ -0,0 +1,31 @@
# Nyash Test Programs
このディレクトリには、CI/CDやデバッグ用のテストプログラムが含まれています。
実用的なアプリケーションは親ディレクトリ(`apps/`)にあります。
## テストプログラム一覧
### LLVMバックエンドテスト
- **ny-llvm-smoke/** - ArrayBox基本操作テスト
- **ny-array-llvm-ret/** - ArrayBox戻り値テスト
- **ny-echo-lite/** - 最小echo実装I/Oテスト
- **ny-map-llvm-smoke/** - MapBoxプラグインテスト
- **ny-vinvoke-smoke/** - 可変長引数5引数テスト
- **ny-vinvoke-llvm-ret/** - 可変長引数戻り値テスト
- **ny-vinvoke-llvm-ret-size/** - 固定長引数size())テスト
## 実行方法
これらのテストは主に `tools/llvm_smoke.sh` から実行されます:
```bash
# 環境変数でテストを有効化
NYASH_LLVM_MAP_SMOKE=1 ./tools/llvm_smoke.sh
NYASH_LLVM_VINVOKE_RET_SMOKE=1 ./tools/llvm_smoke.sh
```
## 注意事項
- これらは最小限の機能テストであり、実用的なアプリケーションではありません
- CIでの自動テストを前提に設計されています
- エラー時の切り分けが容易になるよう、各テストは単一の機能に焦点を当てています

View File

@ -0,0 +1,11 @@
# Async/Await minimal smoke
static box Main {
main() {
// Create a resolved future using nowait sugar
nowait fut = 42
// Await and return the value
local v = await fut
return v
}
}

View File

@ -0,0 +1,9 @@
static box Main {
main() {
local a = new StringBox("12")
local b = new StringBox("30")
local r = a + b
print(r)
return 0
}
}

View File

@ -0,0 +1,7 @@
static box Main {
main() {
local s = new StringBox("12")
print("x = " + s)
return 0
}
}

View File

@ -0,0 +1,16 @@
// gc-sync-stress: allocate many small boxes to pass through checkpoints
static box Main {
main() {
local n = 5000
local arr = new ArrayBox()
local i = 0
loop(i < n) {
arr.push(new IntegerBox(i))
i = i + 1
}
print(arr.length())
return 0
}
}

View File

@ -0,0 +1,19 @@
// mir-branch-multi - composite compare + nested branch
static box Main {
main() {
local a, b
a = 5
b = 3
if a > b {
if a == 5 {
return 1
} else {
return 2
}
} else {
return 0
}
}
}

View File

@ -0,0 +1,12 @@
// mir-branch-ret - simple conditional branch returning 1 or 0
static box Main {
main() {
if 1 < 2 {
return 1
} else {
return 0
}
}
}

View File

@ -0,0 +1,13 @@
// mir-const-add - minimal MIR-only smoke
static box Main {
main() {
// basic const/add/return
local a = 1
local b = 2
local c = a + b
// not printed to avoid I/O; ensure interpreter path works
return 0
}
}

View File

@ -0,0 +1,20 @@
// mir-nested-branch - nested branches with returns
static box Main {
main() {
if 1 < 2 {
if 3 < 4 {
return 1
} else {
return 2
}
} else {
if 5 < 6 {
return 3
} else {
return 4
}
}
}
}

View File

@ -0,0 +1,15 @@
// mir-phi-min - minimal PHI merge across then/else
static box Main {
main() {
local x
if 1 < 2 {
x = 10
} else {
x = 20
}
// Use x after merge to force PHI
return x // expect 10
}
}

View File

@ -0,0 +1,18 @@
// mir-phi-two - branch assigns two locals and merges
static box Main {
main() {
local x
local y
if 1 < 2 {
x = 10
y = 20
} else {
x = 30
y = 40
}
// Expect x=10,y=20 → x+y=30
return x + y
}
}

View File

@ -0,0 +1,12 @@
// mir-store-load - exercise minimal StackSlot-based Load/Store
static box Main {
main() {
local x, y
x = 1
y = 2
x = x + y
return x // expect 3
}
}

View File

@ -0,0 +1,12 @@
// ny-array-llvm-ret - LLVM向け: ArrayBoxのpush/get 戻り値を検証
// 期待: ランタイム標準出力に "Result: 3" が出るny_mainの戻り値
static box Main {
main() {
local a = new ArrayBox()
a.push(3)
local v = a.get(0)
return v
}
}

View File

@ -0,0 +1,16 @@
// ny-echo-lite - minimal echo for LLVM/AOT smoke
// Reads one line from stdin and prints it back. EOF -> exit(0)
static box Main {
init { console }
main() {
me.console = new ConsoleBox()
local line = me.console.readLine()
if line != null {
print(line)
}
return 0
}
}

View File

@ -0,0 +1,16 @@
// ny-llvm-smoke - 最小LLVM/AOTスモーク
// 目的: ArrayBox.get/set と ExternCall(console.log) の実行確認
static box Main {
main() {
// ArrayBox を生成し、push で 3 を追加してから取得
local a = new ArrayBox()
a.push(3)
local v = a.get(0)
// 代表出力ExternCallに正規化される print を使用)
print("Result: " + v)
return 0
}
}

View File

@ -0,0 +1,361 @@
# Nyash Configuration File v2
# マルチBox型プラグイン対応
[libraries]
# ライブラリ定義1つのプラグインで複数のBox型を提供可能
[libraries."libnyash_filebox_plugin"]
boxes = ["FileBox"]
path = "./plugins/nyash-filebox-plugin/target/release/libnyash_filebox_plugin"
[libraries."libnyash_counter_plugin"]
boxes = ["CounterBox"]
path = "./plugins/nyash-counter-plugin/target/release/libnyash_counter_plugin"
[libraries."libnyash_net_plugin"]
boxes = ["HttpServerBox", "HttpClientBox", "HttpResponseBox", "HttpRequestBox", "SocketServerBox", "SocketClientBox", "SocketConnBox"]
path = "./plugins/nyash-net-plugin/target/release/libnyash_net_plugin"
# FileBoxの型情報定義
[libraries."libnyash_filebox_plugin".FileBox]
type_id = 6
[libraries."libnyash_filebox_plugin".FileBox.methods]
birth = { method_id = 0 }
open = { method_id = 1, args = ["path", "mode"] }
read = { method_id = 2 }
write = { method_id = 3, args = ["data"] }
close = { method_id = 4 }
fini = { method_id = 4294967295 }
copyFrom = { method_id = 7, args = [ { kind = "box", category = "plugin" } ] }
cloneSelf = { method_id = 8 }
[libraries."libnyash_counter_plugin".CounterBox]
type_id = 7
singleton = true
[libraries."libnyash_counter_plugin".CounterBox.methods]
birth = { method_id = 0 }
inc = { method_id = 1 }
get = { method_id = 2 }
fini = { method_id = 4294967295 }
# HttpServerBox
[libraries."libnyash_net_plugin".HttpServerBox]
type_id = 20
[libraries."libnyash_net_plugin".HttpServerBox.methods]
birth = { method_id = 0 }
start = { method_id = 1, args = ["port"], returns_result = true }
stop = { method_id = 2, returns_result = true }
accept = { method_id = 3, returns_result = true }
fini = { method_id = 4294967295 }
# HttpClientBox
[libraries."libnyash_net_plugin".HttpClientBox]
type_id = 23
[libraries."libnyash_net_plugin".HttpClientBox.methods]
birth = { method_id = 0 }
get = { method_id = 1, args = ["url"], returns_result = true }
post = { method_id = 2, args = ["url", "body"], returns_result = true }
fini = { method_id = 4294967295 }
# HttpResponseBox
[libraries."libnyash_net_plugin".HttpResponseBox]
type_id = 22
[libraries."libnyash_net_plugin".HttpResponseBox.methods]
birth = { method_id = 0 }
setStatus = { method_id = 1, args = ["status"] }
setHeader = { method_id = 2, args = ["key", "value"] }
write = { method_id = 3, args = ["body"] }
readBody = { method_id = 4 }
getStatus = { method_id = 5 }
getHeader = { method_id = 6, args = ["key"] }
fini = { method_id = 4294967295 }
# HttpRequestBox
[libraries."libnyash_net_plugin".HttpRequestBox]
type_id = 21
[libraries."libnyash_net_plugin".HttpRequestBox.methods]
birth = { method_id = 0 }
path = { method_id = 1 }
readBody = { method_id = 2 }
respond = { method_id = 3, args = [{ kind = "box", category = "plugin" }] }
fini = { method_id = 4294967295 }
# SocketServerBox
[libraries."libnyash_net_plugin".SocketServerBox]
type_id = 30
[libraries."libnyash_net_plugin".SocketServerBox.methods]
birth = { method_id = 0 }
bind = { method_id = 1, args = ["port"] }
accept = { method_id = 2 }
fini = { method_id = 4294967295 }
# SocketClientBox
[libraries."libnyash_net_plugin".SocketClientBox]
type_id = 32
[libraries."libnyash_net_plugin".SocketClientBox.methods]
birth = { method_id = 0 }
connect = { method_id = 1, args = ["host", "port"] }
send = { method_id = 2, args = ["data"] }
receive = { method_id = 3 }
close = { method_id = 4 }
fini = { method_id = 4294967295 }
# SocketConnBox
[libraries."libnyash_net_plugin".SocketConnBox]
type_id = 31
[libraries."libnyash_net_plugin".SocketConnBox.methods]
birth = { method_id = 0 }
send = { method_id = 1, args = ["data"] }
recv = { method_id = 2 }
close = { method_id = 3 }
fini = { method_id = 4294967295 }
[plugin_paths]
# プラグインの検索パス(デフォルト)
search_paths = [
"./target/release",
"./target/debug",
"./plugins/*/target/release",
"./plugins/*/target/debug",
"/usr/local/lib/nyash/plugins",
"~/.nyash/plugins"
]
# 中央タイプIDレジストリ: 各プラグインの nyash_box.toml と一致させる
[box_types]
FileBox = 6
ConsoleBox = 5
ArrayBox = 10
MapBox = 11
IntegerBox = 12
StringBox = 13
CounterBox = 7
HttpServerBox = 20
HttpRequestBox = 21
HttpResponseBox = 22
HttpClientBox = 23
SocketServerBox = 30
SocketConnBox = 31
SocketClientBox = 32
MathBox = 50
TimeBox = 51
RegexBox = 52
EncodingBox = 53
TOMLBox = 54
PathBox = 55
PyRuntimeBox= 40
PyObjectBox = 41
PythonParserBox = 60
PythonCompilerBox = 61
# 新スタイルのプラグインルート(併用可・[libraries]は後方互換)
[plugins]
"libnyash_filebox_plugin" = "./plugins/nyash-filebox-plugin"
"libnyash_console_plugin" = "./plugins/nyash-console-plugin"
"libnyash_string_plugin" = "./plugins/nyash-string-plugin"
"libnyash_map_plugin" = "./plugins/nyash-map-plugin"
"libnyash_array_plugin" = "./plugins/nyash-array-plugin"
"libnyash_python_plugin" = "./plugins/nyash-python-plugin"
"libnyash_integer_plugin" = "./plugins/nyash-integer-plugin"
"libnyash_counter_plugin" = "./plugins/nyash-counter-plugin"
"libnyash_net_plugin" = "./plugins/nyash-net-plugin"
"libnyash_math_plugin" = "./plugins/nyash-math-plugin"
"libnyash_python_parser_plugin" = "./plugins/nyash-python-parser-plugin"
"libnyash_python_compiler_plugin" = "./plugins/nyash-python-compiler-plugin"
"libnyash_regex_plugin" = "./plugins/nyash-regex-plugin"
"libnyash_encoding_plugin" = "./plugins/nyash-encoding-plugin"
"libnyash_toml_plugin" = "./plugins/nyash-toml-plugin"
"libnyash_path_plugin" = "./plugins/nyash-path-plugin"
[libraries."libnyash_array_plugin"]
boxes = ["ArrayBox"]
path = "./plugins/nyash-array-plugin/target/release/libnyash_array_plugin"
[libraries."libnyash_array_plugin".ArrayBox]
type_id = 10
[libraries."libnyash_array_plugin".ArrayBox.methods]
birth = { method_id = 0 }
length = { method_id = 1 }
get = { method_id = 2, args = ["index"] }
push = { method_id = 3, args = ["value"] }
set = { method_id = 4, args = ["index", "value"] }
fini = { method_id = 4294967295 }
[libraries."libnyash_map_plugin"]
boxes = ["MapBox"]
path = "./plugins/nyash-map-plugin/target/release/libnyash_map_plugin"
[libraries."libnyash_map_plugin".MapBox]
type_id = 11
[libraries."libnyash_map_plugin".MapBox.methods]
birth = { method_id = 0 }
size = { method_id = 1 }
get = { method_id = 2, args = ["key"] }
has = { method_id = 3, args = ["key"] }
set = { method_id = 4, args = ["key", "value"] }
fini = { method_id = 4294967295 }
# IntegerBox plugin (basic numeric box)
[libraries."libnyash_integer_plugin"]
boxes = ["IntegerBox"]
path = "./plugins/nyash-integer-plugin/target/release/libnyash_integer_plugin"
[libraries."libnyash_integer_plugin".IntegerBox]
type_id = 12
[libraries."libnyash_integer_plugin".IntegerBox.methods]
birth = { method_id = 0 }
get = { method_id = 1 }
set = { method_id = 2, args = ["value"] }
fini = { method_id = 4294967295 }
# StringBox plugin (read-only methods first)
[libraries."libnyash_string_plugin"]
boxes = ["StringBox"]
path = "./plugins/nyash-string-plugin/target/release/libnyash_string_plugin"
[libraries."libnyash_string_plugin".StringBox]
type_id = 13
[libraries."libnyash_string_plugin".StringBox.methods]
birth = { method_id = 0 }
length = { method_id = 1 }
is_empty = { method_id = 2 }
charCodeAt = { method_id = 3, args = ["index"] }
concat = { method_id = 4, args = ["other"] }
fromUtf8 = { method_id = 5, args = ["data"] }
fini = { method_id = 4294967295 }
# Python plugin (Phase 10.5 Embedding & FFI, initial scaffold)
[libraries."libnyash_python_plugin"]
boxes = ["PyRuntimeBox", "PyObjectBox"]
path = "./plugins/nyash-python-plugin/target/release/libnyash_python_plugin"
[libraries."libnyash_python_plugin".PyRuntimeBox]
type_id = 40
[libraries."libnyash_python_plugin".PyRuntimeBox.methods]
birth = { method_id = 0 }
eval = { method_id = 1, args = ["code"] }
import = { method_id = 2, args = ["name"] }
fini = { method_id = 4294967295 }
evalR = { method_id = 11, args = ["code"], returns_result = true }
importR= { method_id = 12, args = ["name"], returns_result = true }
[libraries."libnyash_python_plugin".PyObjectBox]
type_id = 41
[libraries."libnyash_python_plugin".PyObjectBox.methods]
birth = { method_id = 0 }
getattr = { method_id = 1, args = ["name"] }
call = { method_id = 2, args = ["args"] }
callKw = { method_id = 5 }
str = { method_id = 3 }
fini = { method_id = 4294967295 }
getattrR= { method_id = 11, args = ["name"], returns_result = true }
callR = { method_id = 12, args = ["args"], returns_result = true }
callKwR = { method_id = 15, returns_result = true }
[libraries."libnyash_console_plugin"]
boxes = ["ConsoleBox"]
path = "./plugins/nyash-console-plugin/target/release/libnyash_console_plugin"
[libraries."libnyash_console_plugin".ConsoleBox]
type_id = 5
[libraries."libnyash_console_plugin".ConsoleBox.methods]
birth = { method_id = 0 }
log = { method_id = 1, args = ["text"] }
println = { method_id = 2, args = ["text"] }
fini = { method_id = 4294967295 }
[libraries."libnyash_math_plugin"]
boxes = ["MathBox", "TimeBox"]
path = "./plugins/nyash-math-plugin/target/release/libnyash_math_plugin"
[libraries."libnyash_math_plugin".MathBox]
type_id = 50
[libraries."libnyash_math_plugin".MathBox.methods]
birth = { method_id = 0 }
sqrt = { method_id = 1, args = ["x"] }
sin = { method_id = 2, args = ["x"] }
cos = { method_id = 3, args = ["x"] }
round = { method_id = 4, args = ["x"] }
fini = { method_id = 4294967295 }
[libraries."libnyash_math_plugin".TimeBox]
type_id = 51
[libraries."libnyash_math_plugin".TimeBox.methods]
birth = { method_id = 0 }
now = { method_id = 1 }
fini = { method_id = 4294967295 }
[libraries."libnyash_regex_plugin"]
boxes = ["RegexBox"]
path = "./plugins/nyash-regex-plugin/target/release/libnyash_regex_plugin"
[libraries."libnyash_regex_plugin".RegexBox]
type_id = 52
[libraries."libnyash_regex_plugin".RegexBox.methods]
birth = { method_id = 0, args = ["pattern?"] }
compile = { method_id = 1, args = ["pattern"] }
isMatch = { method_id = 2, args = ["text"], returns_result = true }
find = { method_id = 3, args = ["text"], returns_result = true }
replaceAll = { method_id = 4, args = ["text", "repl"], returns_result = true }
split = { method_id = 5, args = ["text", "limit"], returns_result = true }
fini = { method_id = 4294967295 }
[libraries."libnyash_encoding_plugin"]
boxes = ["EncodingBox"]
path = "./plugins/nyash-encoding-plugin/target/release/libnyash_encoding_plugin"
[libraries."libnyash_encoding_plugin".EncodingBox]
type_id = 53
[libraries."libnyash_encoding_plugin".EncodingBox.methods]
birth = { method_id = 0 }
toUtf8Bytes = { method_id = 1, args = ["s"], returns_result = true }
fromUtf8Bytes = { method_id = 2, args = ["bytes"], returns_result = true }
base64Encode = { method_id = 3, args = ["data"], returns_result = true }
base64Decode = { method_id = 4, args = ["text"], returns_result = true }
hexEncode = { method_id = 5, args = ["data"], returns_result = true }
hexDecode = { method_id = 6, args = ["text"], returns_result = true }
fini = { method_id = 4294967295 }
[libraries."libnyash_toml_plugin"]
boxes = ["TOMLBox"]
path = "./plugins/nyash-toml-plugin/target/release/libnyash_toml_plugin"
[libraries."libnyash_toml_plugin".TOMLBox]
type_id = 54
[libraries."libnyash_toml_plugin".TOMLBox.methods]
birth = { method_id = 0 }
parse = { method_id = 1, args = ["text"], returns_result = true }
get = { method_id = 2, args = ["path"], returns_result = true }
toJson = { method_id = 3, returns_result = true }
fini = { method_id = 4294967295 }
[libraries."libnyash_path_plugin"]
boxes = ["PathBox"]
path = "./plugins/nyash-path-plugin/target/release/libnyash_path_plugin"
[libraries."libnyash_path_plugin".PathBox]
type_id = 55
[libraries."libnyash_path_plugin".PathBox.methods]
birth = { method_id = 0 }
join = { method_id = 1, args = ["base", "rest"], returns_result = true }
dirname = { method_id = 2, args = ["path"], returns_result = true }
basename = { method_id = 3, args = ["path"], returns_result = true }
extname = { method_id = 4, args = ["path"], returns_result = true }
isAbs = { method_id = 5, args = ["path"], returns_result = true }
normalize = { method_id = 6, args = ["path"], returns_result = true }
fini = { method_id = 4294967295 }

View File

@ -0,0 +1,19 @@
// ny-map-llvm-smoke - MapBox by-id plugin path smoke
// 目的: MapBox(size/get/set) を by-id 経由で実行し代表出力を確認
static box Main {
main() {
// Map を生成し、1->42 を設定
local m = new MapBox()
m.set(1, 42)
// 取得とサイズ
local v = m.get(1)
local s = m.size()
// 代表出力連結は1回ずつに限定してLLVM簡易対応
print("Map: v=" + v)
print("size=" + s)
return 0
}
}

View File

@ -0,0 +1,13 @@
// ny-vinvoke-llvm-ret-size - LLVM向け: by-id(size)の戻り値を検証
// 期待: ランタイム標準出力に "Result: 1" が出るny_mainの戻り値
static box Main {
main() {
local m = new MapBox()
m.set(1, 42)
// size() は固定長、by-id経路の戻り値確認
local s = m.size()
return s
}
}

View File

@ -0,0 +1,13 @@
// ny-vinvoke-llvm-ret - LLVM向け: 可変長(tagged vector) by-id シムの結果を戻り値で検証
// 期待: ランタイム標準出力に "Result: 42" が出るny_mainの戻り値
static box Main {
main() {
local m = new MapBox()
m.set(1, 42)
// 可変長経路を通すため5引数
local v = m.get(1, 9, 8, 7, 6)
return v
}
}

View File

@ -0,0 +1,17 @@
// ny-vinvoke-smoke - 可変長(tagged vector) by-id シムのスモーク
// 手順: set(1,42) は固定長シム(<=2引数
// その後 get(1,9,8,7,6) と5引数で呼び、可変長シム経路を通す追加引数は無視される想定
static box Main {
main() {
local m = new MapBox()
m.set(1, 42)
// 可変長シムを通すため5引数で呼ぶ先頭以外はダミー
local v = m.get(1, 9, 8, 7, 6)
print("VInvokeRc: " + v.toString())
return 0
}
}

View File

@ -0,0 +1,21 @@
// semantics-unified: check + for plugin strings and integers
static box Main {
main() {
// plugin StringBox concat (both sides)
local a = new StringBox("12")
local b = new StringBox("30")
print(a + b) // expect 1230
// mixed concat (host string + plugin string)
print("x = " + a) // expect x = 12
// plugin IntegerBox add
local i = new IntegerBox(10)
local j = new IntegerBox(5)
print(i + j) // expect 15
return 0
}
}

32
build_aot_only.ps1 Normal file
View File

@ -0,0 +1,32 @@
# PowerShell script for building Nyash with LLVM AOT support (no libffi)
Write-Host "Setting up environment for AOT-only LLVM build..." -ForegroundColor Green
# Set environment variables
$env:LLVM_SYS_180_PREFIX = "C:\LLVM-18"
$env:LLVM_SYS_180_FFI_WORKAROUND = "1"
$env:LLVM_SYS_NO_LIBFFI = "1" # This is the key - disable libffi
$env:PATH = "C:\LLVM-18\bin;" + $env:PATH
Write-Host "Environment variables set:" -ForegroundColor Yellow
Write-Host " LLVM_SYS_180_PREFIX = $env:LLVM_SYS_180_PREFIX"
Write-Host " LLVM_SYS_NO_LIBFFI = $env:LLVM_SYS_NO_LIBFFI (libffi disabled for AOT)"
# Clean build directory
Write-Host "`nCleaning previous build..." -ForegroundColor Yellow
cargo clean
# Build with LLVM feature
Write-Host "`nBuilding Nyash with LLVM AOT support..." -ForegroundColor Green
cargo build --bin nyash --release --features llvm
# Check output
Write-Host "`nChecking build output..." -ForegroundColor Yellow
if (Test-Path "target\release\nyash.exe") {
Write-Host "SUCCESS: nyash.exe created!" -ForegroundColor Green
Get-Item "target\release\nyash.exe" | Format-List Name, Length, LastWriteTime
} else {
Write-Host "ERROR: nyash.exe not found" -ForegroundColor Red
Write-Host "Listing exe files in target\release:"
Get-ChildItem "target\release\*.exe" | Format-Table Name, Length
}

19
build_clean_aot.bat Normal file
View File

@ -0,0 +1,19 @@
@echo off
chcp 437 >nul
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
set "LLVM_SYS_180_FFI_WORKAROUND=1"
set "LLVM_SYS_180_NO_LIBFFI=1"
set "PATH=C:\LLVM-18\bin;%PATH%"
echo Cleaning and building Nyash with LLVM AOT (no libffi)...
cargo clean
cargo build --bin nyash --release --features llvm
echo.
echo Checking output...
if exist target\release\nyash.exe (
echo SUCCESS: nyash.exe created
dir target\release\nyash.exe
) else (
echo ERROR: nyash.exe not found
)

17
build_llvm_clean.bat Normal file
View File

@ -0,0 +1,17 @@
@echo off
echo Cleaning environment and building with LLVM...
REM Remove old environment variables
set LLVM_CONFIG_PATH=
set LLVM_SYS_180_NO_LIBFFI=
set LLVM_SYS_180_FFI_WORKAROUND=
REM Set new environment variables
set LLVM_SYS_180_PREFIX=C:\LLVM-18
set PATH=C:\LLVM-18\bin;%PATH%
set LLVM_SYS_NO_LIBFFI=1
set LLVM_SYS_180_STRICT_VERSIONING=0
set RUST_LOG=llvm_sys=trace
echo Building with verbose output...
cargo +stable-x86_64-pc-windows-msvc build --release --features llvm -vv

10
build_llvm_simple.bat Normal file
View File

@ -0,0 +1,10 @@
@echo off
set LLVM_SYS_180_PREFIX=C:\LLVM-18
set LLVM_SYS_NO_LIBFFI=1
set LLVM_SYS_180_STRICT_VERSIONING=0
set PATH=C:\LLVM-18\bin;%PATH%
echo LLVM_SYS_180_PREFIX=%LLVM_SYS_180_PREFIX%
echo Building with 24 threads...
cargo build --release --features llvm -j24

34
build_llvm_windows.bat Normal file
View File

@ -0,0 +1,34 @@
@echo off
setlocal enabledelayedexpansion
echo [build_llvm_windows] Starting LLVM build...
REM Set LLVM path
set "LLVM_SYS_180_PREFIX=C:\Program Files\LLVM"
set "PATH=C:\Program Files\LLVM\bin;%PATH%"
REM Check LLVM
where clang.exe >nul 2>&1
if errorlevel 1 (
echo [build_llvm_windows] ERROR: clang.exe not found
exit /b 1
)
echo [build_llvm_windows] LLVM found, building nyash...
REM Build nyash with LLVM
cargo build --release --features llvm
if errorlevel 1 (
echo [build_llvm_windows] ERROR: cargo build failed
exit /b 1
)
echo [build_llvm_windows] Build successful!
REM Build ny-echo-lite
echo [build_llvm_windows] Building ny-echo-lite...
powershell -ExecutionPolicy Bypass -File tools\build_llvm.ps1 apps\tests\ny-echo-lite\main.nyash -Out app_echo.exe
echo [build_llvm_windows] Done!
endlocal

26
build_llvm_wsl.sh Normal file
View File

@ -0,0 +1,26 @@
#!/bin/bash
set -e
echo "Building Nyash with LLVM for Windows from WSL..."
# Windows側のLLVMを使う
export LLVM_SYS_180_PREFIX="/mnt/c/Program Files/LLVM"
# 追加の環境変数Qt6ビルドで使っていたかもしれない技
export LLVM_SYS_180_FFI_WORKAROUND=1
export CC=x86_64-w64-mingw32-gcc
export CXX=x86_64-w64-mingw32-g++
export AR=x86_64-w64-mingw32-ar
# MinGWターゲットで試すQt6と同じ方法
echo "Trying MinGW target..."
cargo build --target x86_64-pc-windows-gnu --release --features llvm
# 成功したら実行ファイルの場所を表示
if [ $? -eq 0 ]; then
echo "Build successful!"
echo "Binary at: target/x86_64-pc-windows-gnu/release/nyash.exe"
else
echo "MinGW build failed, trying MSVC target with cargo-xwin..."
cargo xwin build --target x86_64-pc-windows-msvc --release --features llvm
fi

23
build_llvm_wsl_msvc.sh Normal file
View File

@ -0,0 +1,23 @@
#!/bin/bash
# Build Windows exe with LLVM from WSL using cross compilation
echo "Setting up Windows cross-compilation with LLVM..."
# Set environment variables for WSL cross-compilation
export LLVM_SYS_180_PREFIX="C:\\LLVM-18"
export LLVM_SYS_180_FFI_WORKAROUND="1"
export LLVM_SYS_NO_LIBFFI="1" # This is the key!
# Use cargo-xwin for cross compilation
echo "Building nyash.exe for Windows with LLVM support..."
cargo xwin build --target x86_64-pc-windows-msvc --release --features llvm -j32
# Check if successful
if [ -f "target/x86_64-pc-windows-msvc/release/nyash.exe" ]; then
echo "Build successful!"
ls -la target/x86_64-pc-windows-msvc/release/nyash.exe
else
echo "Build failed - nyash.exe not found"
echo "Checking what was built:"
ls -la target/x86_64-pc-windows-msvc/release/ 2>/dev/null || echo "Target directory not found"
fi

20
build_nyash_exe.bat Normal file
View File

@ -0,0 +1,20 @@
@echo off
chcp 437 >nul
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
set "LLVM_SYS_180_FFI_WORKAROUND=1"
set "PATH=C:\LLVM-18\bin;%PATH%"
echo Building Nyash executable with LLVM support without libffi...
set "LLVM_SYS_NO_LIBFFI=1"
cargo build --bin nyash --release --features llvm
echo.
echo Checking output...
if exist target\release\nyash.exe (
echo SUCCESS: nyash.exe created
dir target\release\nyash.exe
) else (
echo ERROR: nyash.exe not found
echo Listing exe files:
dir target\release\*.exe
)

11
build_ps.ps1 Normal file
View File

@ -0,0 +1,11 @@
# PowerShell build script
$env:LLVM_SYS_180_PREFIX = "C:\LLVM-18"
$env:LLVM_SYS_180_FFI_WORKAROUND = "1"
$env:LLVM_SYS_NO_LIBFFI = "1"
Write-Host "Environment variables set:"
Write-Host "LLVM_SYS_180_PREFIX = $env:LLVM_SYS_180_PREFIX"
Write-Host "LLVM_SYS_NO_LIBFFI = $env:LLVM_SYS_NO_LIBFFI"
Write-Host "`nBuilding..."
cargo build --bin nyash --release --features llvm

4
build_simple.ps1 Normal file
View File

@ -0,0 +1,4 @@
$env:LLVM_SYS_180_PREFIX = "C:\LLVM-18"
$env:LLVM_SYS_180_FFI_WORKAROUND = "1"
$env:LLVM_SYS_NO_LIBFFI = "1"
cargo build --release --features llvm

52
build_win_aot.bat Normal file
View File

@ -0,0 +1,52 @@
@echo off
setlocal ENABLEDELAYEDEXPANSION
chcp 65001 >nul
echo [Nyash AOT Build - libffi disabled]
echo ===================================
REM Set LLVM environment variables
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
set "LLVM_SYS_NO_LIBFFI=1"
set "LLVM_SYS_180_FFI_WORKAROUND=1"
echo LLVM_SYS_180_PREFIX=%LLVM_SYS_180_PREFIX%
echo LLVM_SYS_NO_LIBFFI=%LLVM_SYS_NO_LIBFFI%
REM Verify LLVM installation
if not exist "%LLVM_SYS_180_PREFIX%\include\llvm-c\Core.h" (
echo ERROR: Core.h not found at %LLVM_SYS_180_PREFIX%\include\llvm-c\Core.h
exit /b 1
)
if not exist "%LLVM_SYS_180_PREFIX%\lib\cmake\llvm\LLVMConfig.cmake" (
echo ERROR: LLVMConfig.cmake not found at %LLVM_SYS_180_PREFIX%\lib\cmake\llvm\LLVMConfig.cmake
exit /b 1
)
echo LLVM installation verified successfully!
REM Add LLVM to PATH
set "PATH=%LLVM_SYS_180_PREFIX%\bin;%PATH%"
REM Clean and build
echo.
echo Cleaning previous build...
cargo clean
echo.
echo Building Nyash with LLVM AOT support (no libffi)...
cargo build --bin nyash --release --features llvm
REM Check result
echo.
if exist "target\release\nyash.exe" (
echo SUCCESS: nyash.exe built successfully!
echo.
dir target\release\nyash.exe
) else (
echo ERROR: Build failed - nyash.exe not found
exit /b 1
)
endlocal

131
build_win_env_and_build.bat Normal file
View File

@ -0,0 +1,131 @@
@echo off
setlocal ENABLEDELAYEDEXPANSION
chcp 65001 >nul
rem =============================
rem User config (編集ポイント)
rem =============================
rem LLVM のプレフィックス(ヘッダ/Core.h と CMake/LLVMConfig.cmake がある場所)
rem 例) 自前CMake: C:\LLVM-18
rem 例) vcpkg: C:\vcpkg\installed\x64-windows
set "LLVM_PREFIX=C:\LLVM-18"
rem AOTのみなら 0libffi無効 / JITも使うなら 1libffi有効
set "USE_LIBFFI=0"
rem libffi のプレフィックスUSE_LIBFFI=1 のとき使用; vcpkg推奨
set "LIBFFI_PREFIX=C:\vcpkg\installed\x64-windows"
rem Cargo 子プロセスへ PATH を強制注入(有効化=1
set "FORCE_CARGO_PATH=1"
rem =============================
rem VS 開発環境MSVC x64を有効化
rem =============================
if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" (
call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" -arch=x64
) else if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat" (
call "%ProgramFiles(x86)%\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat" -arch=x64
)
rem =============================
rem 前提チェック
rem =============================
if not exist "%LLVM_PREFIX%\include\llvm-c\Core.h" (
echo [ERROR] Core.h not found: "%LLVM_PREFIX%\include\llvm-c\Core.h"
echo LLVM_PREFIX をヘッダがある場所に直してください。
exit /b 2
)
if not exist "%LLVM_PREFIX%\lib\cmake\llvm\LLVMConfig.cmake" (
echo [ERROR] LLVMConfig.cmake not found: "%LLVM_PREFIX%\lib\cmake\llvm\LLVMConfig.cmake"
echo LLVM_PREFIX を CMake 定義がある場所に直してください。
exit /b 2
)
if not exist "%LLVM_PREFIX%\lib\LLVMCore.lib" (
echo [WARN] LLVMCore.lib が見つかりません: "%LLVM_PREFIX%\lib\LLVMCore.lib"
)
if not exist "%LLVM_PREFIX%\lib\LLVMSupport.lib" (
echo [WARN] LLVMSupport.lib が見つかりません: "%LLVM_PREFIX%\lib\LLVMSupport.lib"
)
rem =============================
rem 衝突しやすい環境変数を掃除
rem =============================
set LLVM_CONFIG_PATH=
set LLVM_SYS_180_NO_LIBFFI=
set LLVM_SYS_180_FFI_WORKAROUND=
rem =============================
rem このシェル限定の環境を設定
rem =============================
set "LLVM_SYS_180_PREFIX=%LLVM_PREFIX%"
set "LLVM_SYS_180_INCLUDE_DIR=%LLVM_PREFIX%\include"
set "LLVM_SYS_180_LIB_DIR=%LLVM_PREFIX%\lib"
set "LLVM_SYS_180_STRICT_VERSIONING=0"
set "PATH=%LLVM_PREFIX%\bin;%PATH%"
if "%USE_LIBFFI%"=="1" (
set "LLVM_SYS_NO_LIBFFI="
if exist "%LIBFFI_PREFIX%\lib\ffi.lib" (
set "LIB=%LIBFFI_PREFIX%\lib;%LIB%"
set "PATH=%LIBFFI_PREFIX%\bin;%PATH%"
) else (
echo [WARN] libffi not found at "%LIBFFI_PREFIX%\lib\ffi.lib" JIT想定なら vcpkg で libffi を追加してください)
)
) else (
rem AOT-only
set "LLVM_SYS_NO_LIBFFI=1"
)
rem =============================
rem 現在の設定を表示
rem =============================
echo [ENV] LLVM_SYS_180_PREFIX=%LLVM_SYS_180_PREFIX%
echo [ENV] LLVM_SYS_180_INCLUDE_DIR=%LLVM_SYS_180_INCLUDE_DIR%
echo [ENV] LLVM_SYS_180_LIB_DIR=%LLVM_SYS_180_LIB_DIR%
echo [ENV] LLVM_SYS_NO_LIBFFI=%LLVM_SYS_NO_LIBFFI%
echo [ENV] LLVM_SYS_180_STRICT_VERSIONING=%LLVM_SYS_180_STRICT_VERSIONING%
where cl
where link
where clang
rem =============================
rem Cargo 子プロセスへ PATH を強制注入(必要なら)
rem =============================
if "%FORCE_CARGO_PATH%"=="1" (
if not exist ".cargo" mkdir ".cargo"
> ".cargo\config.toml" (
echo [env]
echo LLVM_SYS_180_PREFIX = "%LLVM_SYS_180_PREFIX:\=\\%"
echo LLVM_SYS_180_INCLUDE_DIR = "%LLVM_SYS_180_INCLUDE_DIR:\=\\%"
echo LLVM_SYS_180_LIB_DIR = "%LLVM_SYS_180_LIB_DIR:\=\\%"
if "%USE_LIBFFI%"=="1" (echo LLVM_SYS_NO_LIBFFI = "") else (echo LLVM_SYS_NO_LIBFFI = "1")
echo LLVM_SYS_180_STRICT_VERSIONING = "0"
echo PATH = { value = "%LLVM_SYS_180_PREFIX:\=\\%\bin;{PATH}", force = true }
)
echo [INFO] Wrote .cargo\config.toml
)
rem =============================
rem Rust toolchain を MSVC に固定&ビルド
rem =============================
echo [INFO] Using MSVC toolchain...
rustup default stable-x86_64-pc-windows-msvc
set "RUST_LOG=llvm_sys=trace"
echo [INFO] Cleaning...
cargo clean
echo [INFO] Building nyash (release, feature=llvm)...
cargo +stable-x86_64-pc-windows-msvc build --release --features llvm -vv -j24
if errorlevel 1 (
echo [ERROR] cargo build failed. 上の末尾link.exe の行/ffi.libの有無を確認してください。
exit /b 1
)
echo [OK] Build complete.
echo [HINT] AOT→EXE: powershell -ExecutionPolicy Bypass -File tools\build_llvm.ps1 apps\tests\ny-echo-lite\main.nyash -Out app_echo.exe
endlocal
exit /b 0

20
build_win_llvm.bat Normal file
View File

@ -0,0 +1,20 @@
@echo off
echo Building Nyash with LLVM for Windows...
REM Set environment variables
set LLVM_SYS_180_PREFIX=C:\LLVM-18
set LLVM_SYS_180_FFI_WORKAROUND=1
set LLVM_SYS_NO_LIBFFI=1
echo Environment variables:
echo LLVM_SYS_180_PREFIX = %LLVM_SYS_180_PREFIX%
echo LLVM_SYS_180_FFI_WORKAROUND = %LLVM_SYS_180_FFI_WORKAROUND%
echo LLVM_SYS_NO_LIBFFI = %LLVM_SYS_NO_LIBFFI%
echo.
echo Building...
cargo build --release --features llvm
echo.
echo Done!
pause

16
build_windows_llvm.bat Normal file
View File

@ -0,0 +1,16 @@
@echo off
setlocal
REM Set LLVM path
set "LLVM_SYS_180_PREFIX=C:\Program Files\LLVM"
REM Build nyash with LLVM feature
echo Building nyash with LLVM backend...
cargo build --release --features llvm
REM Build ny-echo-lite to exe
echo Building ny-echo-lite...
powershell -ExecutionPolicy Bypass -File tools\build_llvm.ps1 apps\tests\ny-echo-lite\main.nyash -Out app_echo.exe
echo Done!
endlocal

View File

@ -0,0 +1,9 @@
@echo off
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
set "LLVM_SYS_180_FFI_WORKAROUND=1"
set "PATH=C:\LLVM-18\bin;%PATH%"
echo Building Nyash with LLVM support...
cargo build --release --features llvm
echo Done!

View File

@ -0,0 +1,20 @@
@echo off
echo Using existing LLVM at C:\LLVM-18
REM Set environment variables
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
set "LLVM_SYS_180_NO_LIBFFI=1"
set "LLVM_SYS_180_FFI_WORKAROUND=1"
set "PATH=C:\LLVM-18\bin;%PATH%"
echo.
echo Building Nyash without libffi (AOT only)...
cargo build --bin nyash --release --features llvm --no-default-features --features cli,plugins
echo.
if exist target\release\nyash.exe (
echo SUCCESS: nyash.exe created!
dir target\release\nyash.exe
) else (
echo Build failed. Trying alternative approach...
)

13
build_with_setx.bat Normal file
View File

@ -0,0 +1,13 @@
@echo off
echo Setting system environment variables for LLVM...
REM Set system-wide environment variables
setx LLVM_SYS_180_PREFIX "C:\LLVM-18"
setx LLVM_SYS_NO_LIBFFI "1"
setx LLVM_SYS_180_FFI_WORKAROUND "1"
echo.
echo Environment variables set. Please open a NEW command prompt and run:
echo cargo build --bin nyash --release --features llvm
echo.
pause

13
check_build.bat Normal file
View File

@ -0,0 +1,13 @@
@echo off
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
set "LLVM_SYS_180_FFI_WORKAROUND=1"
echo Checking build status...
dir target\release\*.exe 2>nul
if errorlevel 1 (
echo No exe files in target\release
dir target\x86_64-pc-windows-msvc\release\*.exe 2>nul
if errorlevel 1 (
echo No exe files in Windows target either
)
)

37
check_llvm_env.bat Normal file
View File

@ -0,0 +1,37 @@
@echo off
echo === LLVM Environment Check ===
echo.
echo LLVM_SYS_180_PREFIX = %LLVM_SYS_180_PREFIX%
echo.
echo Checking if LLVM files exist...
if exist "C:\LLVM-18\include\llvm-c\Core.h" (
echo [OK] Core.h found at C:\LLVM-18\include\llvm-c\Core.h
) else (
echo [ERROR] Core.h NOT FOUND
)
if exist "C:\LLVM-18\lib\cmake\llvm\LLVMConfig.cmake" (
echo [OK] LLVMConfig.cmake found at C:\LLVM-18\lib\cmake\llvm\LLVMConfig.cmake
) else (
echo [ERROR] LLVMConfig.cmake NOT FOUND
)
echo.
echo Setting environment variables...
set "LLVM_SYS_180_PREFIX=C:\LLVM-18"
set "LLVM_SYS_180_FFI_WORKAROUND=1"
set "LLVM_SYS_NO_LIBFFI=1"
echo.
echo After setting:
echo LLVM_SYS_180_PREFIX = %LLVM_SYS_180_PREFIX%
echo LLVM_SYS_180_FFI_WORKAROUND = %LLVM_SYS_180_FFI_WORKAROUND%
echo LLVM_SYS_NO_LIBFFI = %LLVM_SYS_NO_LIBFFI%
echo.
echo Testing cargo environment...
cargo --version
rustc --version
pause

View File

@ -99,11 +99,13 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
// Resolve receiver instance from handle first; fallback to legacy VM args (param index)
let mut instance_id: u32 = 0;
let mut real_type_id: u32 = 0;
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
if a0 > 0 {
if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
instance_id = p.instance_id();
real_type_id = p.inner.type_id;
invoke = Some(p.inner.invoke_fn);
}
}
@ -122,7 +124,8 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
if invoke.is_none() { return 0; }
// Build TLV args from a1/a2 if present. Prefer handles/StringBox/IntegerBox via runtime host.
use nyash_rust::{jit::rt::handles, backend::vm::VMValue};
let nargs = argc.saturating_sub(1).max(0) as usize;
// argc from LLVM lowering is explicit arg count (excludes receiver)
let nargs = argc.max(0) as usize;
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
// Encode legacy VM arg at position into provided buffer (avoid capturing &mut buf)
let mut encode_from_legacy_into = |dst: &mut Vec<u8>, arg_pos: usize| {
@ -335,7 +338,8 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
if invoke.is_none() { return 0.0; }
// Build TLV args from a1/a2 with String/Integer support
use nyash_rust::{jit::rt::handles, backend::vm::VMValue};
let nargs = argc.saturating_sub(1).max(0) as usize;
// argc from LLVM lowering is explicit arg count (excludes receiver)
let nargs = argc.max(0) as usize;
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
let mut encode_from_legacy = |arg_pos: usize| {
nyash_rust::jit::rt::with_legacy_vm_args(|args| {
@ -576,6 +580,205 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
0
}
// General by-name invoke: (recv_handle, method_cstr, argc, a1, a2) -> i64
// Export name: nyash.plugin.invoke_by_name_i64
#[export_name = "nyash.plugin.invoke_by_name_i64"]
pub extern "C" fn nyash_plugin_invoke_by_name_i64(
recv_handle: i64,
method: *const i8,
argc: i64,
a1: i64,
a2: i64,
) -> i64 {
if method.is_null() { return 0; }
let mname = unsafe { std::ffi::CStr::from_ptr(method) };
let Ok(method_str) = mname.to_str() else { return 0 };
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
let mut instance_id: u32 = 0;
let mut type_id: u32 = 0;
let mut box_type: Option<String> = None;
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
if recv_handle > 0 {
if let Some(obj) = nyash_rust::jit::rt::handles::get(recv_handle as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
instance_id = p.instance_id(); type_id = p.inner.type_id; box_type = Some(p.box_type.clone());
invoke = Some(p.inner.invoke_fn);
}
}
}
if invoke.is_none() { return 0; }
let box_type = box_type.unwrap_or_default();
// Resolve method_id via PluginHost by name
let mh = if let Ok(host) = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host().read() {
host.resolve_method(&box_type, method_str)
} else { return 0 };
let method_id = match mh { Ok(h) => h.method_id, Err(_) => return 0 } as u32;
// Build TLV args from a1/a2 (no legacy in LLVM path)
// argc is the number of explicit arguments (receiver excluded)
let nargs = argc.max(0) as usize;
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
nyrt_encode_arg_or_legacy(&mut buf, a1, 1);
if nargs >= 2 { nyrt_encode_arg_or_legacy(&mut buf, a2, 2); }
// Execute
let mut out = vec![0u8; 512]; let mut out_len: usize = out.len();
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
if rc != 0 { return 0; }
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
match tag {
3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } }
1 => { return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
8 => { if payload.len()==8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type=u32::from_le_bytes(t); let r_inst=u32::from_le_bytes(i); let pb=nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2(box_type.clone(), r_type, r_inst, invoke.unwrap()); let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox>=std::sync::Arc::new(pb); let h=nyash_rust::jit::rt::handles::to_handle(arc); return h as i64; } }
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } }
_ => {}
}
}
0
}
// Tagged by-id invoke (supports f64/int/handle for first two args)
// tag: 3=I64, 5=F64(bits), 8=Handle
#[export_name = "nyash_plugin_invoke3_tagged_i64"]
pub extern "C" fn nyash_plugin_invoke3_tagged_i64(
type_id: i64,
method_id: i64,
argc: i64,
a0: i64,
a1: i64,
tag1: i64,
a2: i64,
tag2: i64,
a3: i64,
tag3: i64,
a4: i64,
tag4: i64,
) -> i64 {
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
// Resolve receiver invoke and actual plugin type_id
let mut instance_id: u32 = 0;
let mut real_type_id: u32 = type_id as u32;
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
if a0 > 0 {
if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
instance_id = p.instance_id();
real_type_id = p.inner.type_id;
invoke = Some(p.inner.invoke_fn);
}
}
}
if invoke.is_none() { return 0; }
// Build TLV from tags
// argc is the number of explicit arguments (receiver excluded)
let nargs = argc.max(0) as usize;
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
let mut enc = |val: i64, tag: i64| {
match tag {
3 => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, val),
5 => { let bits = val as u64; let f = f64::from_bits(bits); nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, f); },
8 => {
if val > 0 {
if let Some(obj) = nyash_rust::jit::rt::handles::get(val as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id());
} else {
let s = obj.to_string_box().value;
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s);
}
}
} else {
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, 0);
}
}
_ => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, val),
}
};
if nargs >= 1 { enc(a1, tag1); }
if nargs >= 2 { enc(a2, tag2); }
if nargs >= 3 { enc(a3, tag3); }
if nargs >= 4 { enc(a4, tag4); }
// Invoke
let mut out = vec![0u8; 512]; let mut out_len: usize = out.len();
let rc = unsafe { invoke.unwrap()(real_type_id, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
if rc != 0 { return 0; }
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
match tag {
3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } }
1 => { return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
8 => { if payload.len()==8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type=u32::from_le_bytes(t); let r_inst=u32::from_le_bytes(i); let pb=nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2("PluginBox".into(), r_type, r_inst, invoke.unwrap()); let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox>=std::sync::Arc::new(pb); let h=nyash_rust::jit::rt::handles::to_handle(arc); return h as i64; } }
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } }
_ => {}
}
}
0
}
// Variable-length tagged invoke by-id
// Exported as: nyash.plugin.invoke_tagged_v_i64(i64 type_id, i64 method_id, i64 argc, i64 recv_h, i64* vals, i64* tags) -> i64
#[export_name = "nyash.plugin.invoke_tagged_v_i64"]
pub extern "C" fn nyash_plugin_invoke_tagged_v_i64(
type_id: i64,
method_id: i64,
argc: i64,
recv_h: i64,
vals: *const i64,
tags: *const i64,
) -> i64 {
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
if recv_h <= 0 { return 0; }
// Resolve receiver invoke
let mut instance_id: u32 = 0;
let mut real_type_id: u32 = 0;
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
if let Some(obj) = nyash_rust::jit::rt::handles::get(recv_h as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
instance_id = p.instance_id();
real_type_id = p.inner.type_id;
invoke = Some(p.inner.invoke_fn);
}
}
if invoke.is_none() { return 0; }
let nargs = argc.saturating_sub(1).max(0) as usize;
let (vals, tags) = if nargs > 0 && !vals.is_null() && !tags.is_null() {
unsafe {
(std::slice::from_raw_parts(vals, nargs), std::slice::from_raw_parts(tags, nargs))
}
} else { (&[][..], &[][..]) };
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
for i in 0..nargs {
match tags[i] {
3 => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, vals[i]),
5 => { let f = f64::from_bits(vals[i] as u64); nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, f); },
8 => {
if let Some(obj) = nyash_rust::jit::rt::handles::get(vals[i] as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id());
} else {
let s = obj.to_string_box().value;
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s);
}
} else {
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, 0);
}
}
_ => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, vals[i]),
}
}
let mut out = vec![0u8; 1024]; let mut out_len: usize = out.len();
let rc = unsafe { invoke.unwrap()(real_type_id, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
if rc != 0 { return 0; }
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
match tag {
3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } }
1 => { return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
8 => { if payload.len()==8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type=u32::from_le_bytes(t); let r_inst=u32::from_le_bytes(i); let pb=nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2("PluginBox".into(), r_type, r_inst, invoke.unwrap()); let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox>=std::sync::Arc::new(pb); let h=nyash_rust::jit::rt::handles::to_handle(arc); return h as i64; } }
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } }
_ => {}
}
}
0
}
// ---- Handle-based birth shims for AOT/JIT object linkage ----
// These resolve symbols like "nyash.string.birth_h" referenced by ObjectModule.
@ -729,6 +932,35 @@ pub extern "C" fn nyash_string_new(ptr: *const u8, len: i32) -> *mut i8 {
raw as *mut i8
}
// ---- Unified semantics shims (handle-based) ----
// Exported as: nyash.semantics.add_hh(i64 lhs_handle, i64 rhs_handle) -> i64 (NyashBox handle)
#[export_name = "nyash.semantics.add_hh"]
pub extern "C" fn nyash_semantics_add_hh_export(lhs_h: i64, rhs_h: i64) -> i64 {
use nyash_rust::{box_trait::{StringBox, IntegerBox}, runtime::semantics};
use nyash_rust::jit::rt::handles;
if lhs_h <= 0 || rhs_h <= 0 { return 0; }
let lhs = if let Some(obj) = handles::get(lhs_h as u64) { obj } else { return 0 };
let rhs = if let Some(obj) = handles::get(rhs_h as u64) { obj } else { return 0 };
let ls_opt = semantics::coerce_to_string(lhs.as_ref());
let rs_opt = semantics::coerce_to_string(rhs.as_ref());
if ls_opt.is_some() || rs_opt.is_some() {
let ls = ls_opt.unwrap_or_else(|| lhs.to_string_box().value);
let rs = rs_opt.unwrap_or_else(|| rhs.to_string_box().value);
let s = format!("{}{}", ls, rs);
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::new(StringBox::new(s));
return handles::to_handle(arc) as i64;
}
if let (Some(li), Some(ri)) = (semantics::coerce_to_i64(lhs.as_ref()), semantics::coerce_to_i64(rhs.as_ref())) {
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::new(IntegerBox::new(li + ri));
return handles::to_handle(arc) as i64;
}
// Fallback: stringify both and concat to preserve total order
let ls = lhs.to_string_box().value;
let rs = rhs.to_string_box().value;
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::new(StringBox::new(format!("{}{}", ls, rs)));
handles::to_handle(arc) as i64
}
// ---- Array helpers for LLVM lowering (handle-based) ----
// Exported as: nyash_array_get_h(i64 handle, i64 idx) -> i64
#[no_mangle]
@ -744,6 +976,177 @@ pub extern "C" fn nyash_array_get_h(handle: i64, idx: i64) -> i64 {
0
}
// ---- ExternCall helpers for LLVM lowering ----
// Exported as: nyash.console.log(i8* cstr) -> i64
#[export_name = "nyash.console.log"]
pub extern "C" fn nyash_console_log_export(ptr: *const i8) -> i64 {
if ptr.is_null() { return 0; }
unsafe {
let c = std::ffi::CStr::from_ptr(ptr);
if let Ok(s) = c.to_str() {
println!("{}", s);
}
}
0
}
// Exported as: nyash.console.warn(i8* cstr) -> i64
#[export_name = "nyash.console.warn"]
pub extern "C" fn nyash_console_warn_export(ptr: *const i8) -> i64 {
if ptr.is_null() { return 0; }
unsafe {
let c = std::ffi::CStr::from_ptr(ptr);
if let Ok(s) = c.to_str() {
eprintln!("[warn] {}", s);
}
}
0
}
// Exported as: nyash.console.error(i8* cstr) -> i64
#[export_name = "nyash.console.error"]
pub extern "C" fn nyash_console_error_export(ptr: *const i8) -> i64 {
if ptr.is_null() { return 0; }
unsafe {
let c = std::ffi::CStr::from_ptr(ptr);
if let Ok(s) = c.to_str() {
eprintln!("[error] {}", s);
}
}
0
}
// Exported as: nyash.debug.trace(i8* cstr) -> i64
#[export_name = "nyash.debug.trace"]
pub extern "C" fn nyash_debug_trace_export(ptr: *const i8) -> i64 {
if ptr.is_null() { return 0; }
unsafe {
let c = std::ffi::CStr::from_ptr(ptr);
if let Ok(s) = c.to_str() {
eprintln!("[trace] {}", s);
}
}
0
}
// Exported as: nyash.console.readline() -> i8*
#[export_name = "nyash.console.readline"]
pub extern "C" fn nyash_console_readline_export() -> *mut i8 {
use std::io::{self, Read};
// Read a line from stdin; normalize to UTF-8 and strip trailing CR/LF
let mut input = String::new();
// Use read_to_end if stdin is not a TTY? Simpler: read_line through BufRead
// For simplicity, read from stdin into buffer until newline or EOF
let mut buf = String::new();
let mut handle = io::stdin();
// On failure or EOF, return empty string
match io::stdin().read_line(&mut buf) {
Ok(_n) => { input = buf; },
Err(_) => { input.clear(); }
}
while input.ends_with('\n') || input.ends_with('\r') {
input.pop();
}
// Allocate C string (null-terminated)
let mut bytes = input.into_bytes();
bytes.push(0);
let boxed = bytes.into_boxed_slice();
let raw = Box::into_raw(boxed) as *mut u8;
raw as *mut i8
}
// ---- String concat helpers for LLVM lowering ----
// Exported as: nyash.string.concat_ss(i8* a, i8* b) -> i8*
#[export_name = "nyash.string.concat_ss"]
pub extern "C" fn nyash_string_concat_ss(a: *const i8, b: *const i8) -> *mut i8 {
let mut s = String::new();
unsafe {
if !a.is_null() {
if let Ok(sa) = std::ffi::CStr::from_ptr(a).to_str() { s.push_str(sa); }
}
if !b.is_null() {
if let Ok(sb) = std::ffi::CStr::from_ptr(b).to_str() { s.push_str(sb); }
}
}
let mut bytes = s.into_bytes();
bytes.push(0);
let boxed = bytes.into_boxed_slice();
let raw = Box::into_raw(boxed) as *mut u8;
raw as *mut i8
}
// Exported as: nyash.string.concat_si(i8* a, i64 b) -> i8*
#[export_name = "nyash.string.concat_si"]
pub extern "C" fn nyash_string_concat_si(a: *const i8, b: i64) -> *mut i8 {
let mut s = String::new();
unsafe {
if !a.is_null() {
if let Ok(sa) = std::ffi::CStr::from_ptr(a).to_str() { s.push_str(sa); }
}
}
s.push_str(&b.to_string());
let mut bytes = s.into_bytes();
bytes.push(0);
let boxed = bytes.into_boxed_slice();
let raw = Box::into_raw(boxed) as *mut u8;
raw as *mut i8
}
// Exported as: nyash.string.concat_is(i64 a, i8* b) -> i8*
#[export_name = "nyash.string.concat_is"]
pub extern "C" fn nyash_string_concat_is(a: i64, b: *const i8) -> *mut i8 {
let mut s = a.to_string();
unsafe {
if !b.is_null() {
if let Ok(sb) = std::ffi::CStr::from_ptr(b).to_str() { s.push_str(sb); }
}
}
let mut bytes = s.into_bytes();
bytes.push(0);
let boxed = bytes.into_boxed_slice();
let raw = Box::into_raw(boxed) as *mut u8;
raw as *mut i8
}
// ---- Instance field helpers for LLVM lowering (handle-based) ----
// Exported as: nyash.instance.get_field_h(i64 handle, i8* name) -> i64
#[export_name = "nyash.instance.get_field_h"]
pub extern "C" fn nyash_instance_get_field_h(handle: i64, name: *const i8) -> i64 {
if handle <= 0 || name.is_null() { return 0; }
let name = unsafe { std::ffi::CStr::from_ptr(name) };
let Ok(field) = name.to_str() else { return 0 };
if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) {
if let Some(inst) = obj.as_any().downcast_ref::<nyash_rust::instance_v2::InstanceBox>() {
if let Some(shared) = inst.get_field(field) {
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::from(shared);
let h = nyash_rust::jit::rt::handles::to_handle(arc);
return h as i64;
}
}
}
0
}
// Exported as: nyash.instance.set_field_h(i64 handle, i8* name, i64 val_h) -> i64
#[export_name = "nyash.instance.set_field_h"]
pub extern "C" fn nyash_instance_set_field_h(handle: i64, name: *const i8, val_h: i64) -> i64 {
if handle <= 0 || name.is_null() { return 0; }
let name = unsafe { std::ffi::CStr::from_ptr(name) };
let Ok(field) = name.to_str() else { return 0 };
if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) {
if let Some(inst) = obj.as_any().downcast_ref::<nyash_rust::instance_v2::InstanceBox>() {
if val_h > 0 {
if let Some(val) = nyash_rust::jit::rt::handles::get(val_h as u64) {
let shared: nyash_rust::box_trait::SharedNyashBox = std::sync::Arc::clone(&val);
let _ = inst.set_field(field, shared);
return 0;
}
}
}
}
0
}
// Exported as: nyash_array_set_h(i64 handle, i64 idx, i64 val) -> i64
#[no_mangle]
pub extern "C" fn nyash_array_set_h(handle: i64, idx: i64, val: i64) -> i64 {
@ -751,13 +1154,52 @@ pub extern "C" fn nyash_array_set_h(handle: i64, idx: i64, val: i64) -> i64 {
if handle <= 0 || idx < 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(arr) = obj.as_any().downcast_ref::<nyash_rust::boxes::array::ArrayBox>() {
let i = idx as usize;
let len = arr.len();
if i < len {
let _ = arr.set(Box::new(IntegerBox::new(idx)), Box::new(IntegerBox::new(val)));
} else if i == len {
let _ = arr.push(Box::new(IntegerBox::new(val)));
} else {
// Do nothing for gaps (keep behavior conservative)
}
return 0;
}
}
0
}
// Exported as: nyash_array_push_h(i64 handle, i64 val) -> i64 (returns new length)
#[no_mangle]
pub extern "C" fn nyash_array_push_h(handle: i64, val: i64) -> i64 {
use nyash_rust::{jit::rt::handles, box_trait::{IntegerBox, NyashBox}};
if handle <= 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(arr) = obj.as_any().downcast_ref::<nyash_rust::boxes::array::ArrayBox>() {
// If val is handle, try to use it; otherwise treat as integer
let vbox: Box<dyn NyashBox> = if val > 0 {
if let Some(o) = handles::get(val as u64) { o.clone_box() } else { Box::new(IntegerBox::new(val)) }
} else { Box::new(IntegerBox::new(val)) };
let _ = arr.push(vbox);
return arr.len() as i64;
}
}
0
}
// Exported as: nyash_array_length_h(i64 handle) -> i64
#[no_mangle]
pub extern "C" fn nyash_array_length_h(handle: i64) -> i64 {
use nyash_rust::jit::rt::handles;
if handle <= 0 { return 0; }
if let Some(obj) = handles::get(handle as u64) {
if let Some(arr) = obj.as_any().downcast_ref::<nyash_rust::boxes::array::ArrayBox>() {
return arr.len() as i64;
}
}
0
}
// Convert a VM argument (param index or existing handle) into a runtime handle
// Exported as: nyash.handle.of
#[export_name = "nyash.handle.of"]

17
create_dummy_ffi.bat Normal file
View File

@ -0,0 +1,17 @@
@echo off
echo Creating dummy ffi.lib to bypass linker error...
cd C:\LLVM-18\lib
REM Create empty C file
echo // Dummy ffi functions > ffi_dummy.c
echo void ffi_call() {} >> ffi_dummy.c
echo void ffi_prep_cif() {} >> ffi_dummy.c
echo void ffi_prep_closure_loc() {} >> ffi_dummy.c
REM Compile to object file
cl /c ffi_dummy.c
REM Create library
lib /OUT:ffi.lib ffi_dummy.obj
echo Done! Created C:\LLVM-18\lib\ffi.lib

17
create_dummy_ffi_ps.ps1 Normal file
View File

@ -0,0 +1,17 @@
# Create dummy ffi.lib
$dummyContent = @"
// Dummy FFI functions for AOT-only build
void ffi_call() {}
void ffi_prep_cif() {}
void ffi_prep_closure_loc() {}
void ffi_type_void() {}
"@
# Write C file
$dummyContent | Out-File -FilePath "C:\LLVM-18\lib\ffi_dummy.c" -Encoding UTF8
Write-Host "Created dummy C file at C:\LLVM-18\lib\ffi_dummy.c"
Write-Host "Now you need to compile it with Visual Studio Developer Command Prompt:"
Write-Host " cd C:\LLVM-18\lib"
Write-Host " cl /c ffi_dummy.c"
Write-Host " lib /OUT:ffi.lib ffi_dummy.obj"

View File

@ -1,10 +1,11 @@
# MIR 35→26命令削減: 詳細分析・移行戦略
# MIR 35→26→15命令削減: 詳細分析・移行戦略
*実装ベース完全マッピング - 2025年8月17日版*
**注意**: このドキュメントは35→26命令への移行計画を記録しています。現在はさらに15命令に削減済みです。
## 🔍 **現在実装35命令 vs ChatGPT5仕様26命令の完全マッピング**
## 🔍 **過去の実装35命令 vs 26命令仕様の完全マッピング(歴史的記録)**
### **維持する命令 (既存実装 → 26命令仕様)**
### **維持する命令 (35命令実装 → 26命令仕様への移行計画)**
| 現在実装 | 26命令仕様 | 効果 | 変更 |
|----------|------------|------|------|

View File

@ -15,7 +15,7 @@ Purpose: Claude×Copilot×ChatGPT協調開発の総合ロードマップ
| Phase | 状態 | 概要 | 詳細リンク |
|-------|------|------|------------|
| 8.4 | ✅完了 | AST→MIR Lowering完全実装 | [phase_8_4_ast_mir_lowering.md](phase-8/phase_8_4_ast_mir_lowering.md) |
| 8.5 | ✅完了 | MIRダイエット35→26命令 | [phase_8_5_mir_35_to_26_reduction.md](phase-8/phase_8_5_mir_35_to_26_reduction.md) |
| 8.5 | ✅完了 | MIRダイエット35→26→15命令) | [phase_8_5_mir_35_to_26_reduction.md](phase-8/phase_8_5_mir_35_to_26_reduction.md) |
| 8.6 | 🔄進行中 | VM性能改善0.9倍→2倍以上 | [phase_8_6_vm_performance_improvement.md](phase-8/phase_8_6_vm_performance_improvement.md) |
| 9 | 📅予定 | JIT実装 | [phase-9/](phase-9/) |
| 9.75g-0 | ✅完了 | BID-FFI Plugin System | [Phase-9.75g-0-BID-FFI-Developer-Guide.md](phase-9/Phase-9.75g-0-BID-FFI-Developer-Guide.md) |
@ -114,7 +114,7 @@ nyash bid gen --target llvm bid.yaml # AOT用declare生成LLVM実装時
- **🌟 NEW: GC切り替え可能ランタイム世界初の柔軟なメモリ管理**
**Start Gate着手前の必須完了**:
- ✅ MIRダイエット26命令)整合完了
- ✅ MIRダイエット15命令)整合完了
- ✅ VM統計: `--vm-stats` でホット関数抽出可能
- 🔄 Proof of Concept: MIR→CLIFの最小Lower
- ❓ BoxCall/Array/MapのJIT最適化
@ -185,7 +185,7 @@ nyash bid gen --target llvm bid.yaml # AOT用declare生成LLVM実装時
### 達成済み
- 🏆 RefNew/RefGet/RefSet WASM完全動作
- 🏆 26命令MIR階層化完了Phase 8.5
- 🏆 MIR命令削減完了35→26→15命令、Phase 8.5
- 🏆 Phase 9.75g-0 BID-FFI Plugin System完全完了
- 🏆 警告削減100%達成Phase 9.75j

View File

@ -0,0 +1,41 @@
# CURRENT TASK Phase 11.7JIT Complete / Semantics Layer
Focus Window: Semantics導入jit-direct安定化の確定報告 → GC/Sync/非同期のJIT降下着手
合意事項(要約)
- LLVM AOT は一旦クローズ。Windows 依存と重量を回避し、Cranelift に集中。
- 単一意味論層Semanticsを導入し、Nyashスクリプト/VM/JIT(exe)を同一動作に揃える。
- VM は参照実装。JIT は実行/生成を担い、VM→JITのランタイムフォールバックは行わない。
現状ステータス2025-09-01
- jit-direct 分岐/PHI 合流単一出口BlockParam合流で安定化を確認。
- テスト: `mir-branch-ret`, `mir-phi-min`, `mir-branch-multi`, `mir-nested-branch`, `mir-phi-two` で VM/JIT 一致tag=201/200 一致)。
- Semantics 層:`src/semantics/{mod.rs, eval.rs}` にトレイトとPoCインタプリタの骨組みを追加済未配線
- C ABINyRT`crates/nyrt``libnyrt.a` に必要シンボル実装済console/array/string/plugin_invoke/checkpoint/gc_barrier 等)。
- VM 側Safepoint/書込バリア/簡易スケジューラSingleThread連携は稼働。
- JIT 側Safepoint/バリア/await はまだスタブまたは未emit要降下
直近タスク(このフェーズでやること)
1) Semantics 実用化配線VM/JITの動作一致の“芯”
- `SemanticsVM`VM実行での実装`SemanticsClif`LowerCore+IRBuilder委譲を用意。
- `semantics::MirInterpreter` で両者を同一MIRへ適用し、差分検出の土台を作る。
2) JIT へ GC/Sync/非同期の降下
- Safepoint: `I::Safepoint``nyash.rt.checkpoint` emit。`nyrt` 側で `gc.safepoint()``scheduler.poll()` に橋渡し。
- Write Barrier: Array/Map の set/push 等をlowerする箇所で `nyash.gc.barrier_write` を emitCountingGc で検証)。
- Await: PoC として FutureBox の同期 get にlower動作一致優先
3) パリティ検証
- `NYASH_GC_COUNTING=1` で VM/JIT ともに safepoint/barrier カウントが増えることを確認。
- 既存 smokes分岐/PHI/配列/外部呼び出し)で一致を継続監視。
実行メモ
- BuildJIT: `cargo build --release --features cranelift-jit`
- jit-direct: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct <app>`
- 追跡: `NYASH_JIT_TRACE_RET/SEL/BLOCKS=1`、GC: `NYASH_GC_COUNTING=1`(必要時)
備考
- LLVM AOT のドキュメント/ツールは維持するが、Windows 前提の依存導入は行わない。Cranelift で“がっちり作る”。

View File

@ -0,0 +1,46 @@
# Design Notes Phase 11.7 JIT Complete (Meeting Summary)
Date: 2025-09-01
Key Decisions
- Single Semantics Source: Introduce a MIR semantics layer (trait) as the single source of truth. All backends (VM/Cranelift/LLVM/WASM) implement this interface.
- No Runtime Fallback: Remove VM→JIT fallback complexity. VM becomes the reference executor; codegen backends handle execution/AOT. JIT is compile-only/AOTassist when needed.
- HandleFirst ABI: Unify handle/i64/ptr conversions, tag classification, concat/extern/boxcall via shared helpers; call into NyRT shims from backends.
- GC Hooks: Insert sync barriers and async safepoints as MIRlevel hooks that each backend lowers appropriately.
- Backends Roadmap: LLVM AOT は一旦アーカイブ。Cranelift を主線JIT/軽量AOTとし、WASM は同一セマンティクスで後段。Windows DX を軽く保つ。
Architecture Sketch
- MIR → Semantics<E>
- VmSem: executes values (reference)
- ClifSem: builds Cranelift IR (JIT/AOT)
- LlvmSem: builds LLVM IR (AOT)
- WasmSem: builds Wasm (future)
- Shared ABI utilities: handle↔i64/ptr, to_bool, compare, tags, invoke variants, NyRT shims.
Implementation Plan (Progressive)
- Phase 1: Skeleton + minimal lowering (Const/Return/Add) → echo-lite returns 0 via Cranelift JIT skeleton.
- Phase 2: Control (Jump/Branch/Phi), Load/Store, Compare, String concat via NyRT, Box/Extern by-id (fixed/vector).
- Phase 3: GC barriers/safepoints; parity with VMCountingGc での観測を含む)。
- Phase 4: Stability, logs, strict/legacy guards; optional AOT via cranelift-object + link scripts.
Notes from Review
- Using Semantics trait enables zerocost abstractions with static dispatch.
- Add optional debug hooks (location/value) and optimization hints (likely_branch, pure_function_hint) later.
- Testing: MockSemantics for unit tests; parity tests VM vs CLIF.
Action Items
- Land Semantics trait + minimal MirInterpreter (skeleton added).
- Implement ClifSem minimal lowering; wire Runner `--backend cranelift`.
- Centralize ABI helpers; migrate existing scattered logic to shared module.
- Emit `nyash.rt.checkpoint` and `nyash.gc.barrier_write` from LowerCore at appropriate sites; wire Await (PoC: blocking get).
- Add build_cl scripts for AOT when ready.
Links
- PLAN.md milestones and coverage
- CURRENT_TASK.md immediate focus

View File

@ -0,0 +1,81 @@
# Phase 11.7 JIT Complete Plan (Cranelift)
Goal
- Ship a complete JIT backend (Cranelift) for MIR Core15 with Semantics layer as the single source of truth, GC sync/async support, and full Box/Extern integration. Keep DX simple and crossplatform.
Milestones
1) Bootstrap (Infra + Skeleton)
- Add backend module: `src/backend/cranelift/{mod.rs,context.rs,lower.rs,jit.rs,object.rs}`
- Context: host ISA, FunctionBuilder, Module (JIT/Object) setup helpers
- Runner: `--backend cranelift` execution path; feature flag reuse (`cranelift-jit`)
- Minimal ops: Const i64/f64/bool/null/string → CLIF values; Return; straightline add/sub
- Smoke: `apps/tests/ny-echo-lite` returns 0 via JIT
2) Core MIR15 Lowering (Parity with VM)
- Control: Jump/Branch/Phi, Load/Store (alloca on entry; i1↔i64 widen/narrow)
- Unary/Binary/Compare: int/float/ptr eq/ne; logical and/or via to_bool
- TypeOp (when needed) + pointer/int casts (handle→i64/i8* via ABI helpers)
- Strings: concat via NyRT shims (`nyash.string.concat_*`)
- BoxCall (byid): fixed/tagged args (<=4) + vector path; handlefirst returns (i64/ptr)
- ExternCall: `env.console.*`, `env.debug.trace`, `console.readLine` via NyRT shims
- Smokes: array/map/vinvoke(size/ret), echo; compare outputs with VM
3) GC Cooperation
- Sync barriers: insert read/write barriers at Load/Store & NewBox as per VM semantics
- Async safepoints: at call sites/loop backedges; NyRT entry glue to yield if required
- Tests: targeted barrier smoke (array/map mutations) + perf sanity (no excessive barriers)
4) Parity + Stability
- UnsupportedLegacyInstruction: maintain strict mode; allow env override for debug
- Error reporting: source op prettyprint, MIR value ids in error messages
- Logging: `NYASH_CLI_VERBOSE=1` shows JIT compile stages + object sizes (optional)
- Doc/Doctor script: `tools/doctor.ps1`/`.sh` for quick env checks (optional nicetohave)
5) AOT (Optional within 11.7 if time)
- `cranelift-object` emit: `.o` for main; link with NyRT → exe (`tools/build_cl.*`)
- Windows: clang/lld link; Linux: cc link; parity with LLVMs scripts
Instruction Coverage (MIR Core15)
- Const, UnaryOp, BinOp, Compare, TypeOp
- Load, Store, Phi, Jump, Branch, Return
- Call, NewBox, BoxCall, ExternCall
ABI & Shims
- Handlefirst: receiver/values normalized to i64; ptr/int casts via helpers
- NyRT shims used: `nyash.console.*`, `nyash.debug.trace`, `nyash.console.readline`,
`nyash_string_new`, `nyash.string.concat_*`, `nyash_array_*_h`, `nyash.instance.*_h`,
plugin invoke (byid tagged, vector variants)
Semantics Integration (new in 11.7)
- Add `Semantics` trait as unified MIR semantics API.
- Provide `SemanticsVM` (exec) and `SemanticsClif` (lower) so the same MIR walks yield identical behavior across VM and JIT.
- Use `semantics::MirInterpreter` for parity tests; prefer zerocost abstractions with static dispatch.
Status Notes (20250901)
- LLVM AOT: closed for now due to Windows dependency weight; Cranelift is the mainline.
- VM: safepoint/barrier/scheduler wired and observable (CountingGc).
- JIT: `nyash.rt.checkpoint`/`nyash.gc.barrier_write` symbols are available via NyRT; LowerCore needs to emit safepoints and barriers; Await lowering pending.
Deliverables
- Code: Cranelift backend files + runner integration
- Tools: build/run scripts for JIT/AOT; updated smokes to exercise CL route
- Docs: this plan + CURRENT_TASK.md; brief README how to run JIT
Risks & Mitigations
- Pointer/int mismatch → normalize via i64/ptr helpers in one place; add asserts
- Barrier placement overhead → start with conservative placement; measure; trim if safe
- Windows toolchain variance → Cranelift avoids external LLVM; keep MSVC only
Timeline (indicative)
- Week 1: Milestone 1 + 2 (most ops) → basic smokes green
- Week 2: GC barriers + safepoints, full parity sweep; docs/tools polish
- Optional: AOT via clobject emit & link scripts

View File

@ -0,0 +1,19 @@
# Phase 11.7 JIT Complete (MIR-15 + Semantics + Sync/Async GC)
Scope: Implement a productionready JIT backend for Nyash that fully supports the MIR Core15 instruction set, synchronous/async GC cooperation, and delivers a simple, reproducible developer experience across platforms (WSL/Linux/Windows).
Why now:
- LLVM AOT は一度実装を進めたが、Windows 依存が重くサイズも大きい。Cranelift に回帰して、純Rustで“Just Works”のDXを取り戻し、高速な反復開発を実現するAOT/LLVMは資料として保持
Outcomes (Definition of Done):
- All MIR15 ops compile and execute via JIT with behavioral parity to VM.
- BoxCall/ExternCall are handled through NyRT shims (handlefirst ABI) safely.
- Sync GC barriers in place (read/write), async safepoints wired at call/loop edges.
- Smokes: echo/array/map/vinvoke/extern pass; parity checks vs VM/JIT (logs included).
- 1command setup and run on WSL + Windows Dev PowerShell; no external LLVM needed.
Backends Strategy:
- LLVM AOT はアーカイブ(参照は可)。主線は CraneliftJIT/軽量AOT
- JIT 既定は Craneliftfeature: `cranelift-jit`。AOT は必要に応じ `cranelift-object` を併用。
This folder contains the living plan (PLAN.md) and the rolling snapshot of the current task focus (CURRENT_TASK.md). Semantics 層の導入により、Nyash スクリプトVMJITexeの動作を一致させる。

View File

@ -1,115 +1,107 @@
# Phase 12: Nyashスクリプトプラグインシステム革命
# Phase 12: Nyashコード共有エコシステム - Everything is Box の実現
## 🚀 概要
## 🎯 重要な変更 (2025-09-01)
Nyashスクリプト自体でプラグインを作成できる革命的発見ビルド不要で、既存のネイティブプラグインを組み合わせて新機能を作成可能。
Phase 12の議論とビルトインBox廃止により、プラグインシステムが進化
## 💡 発見の経緯
include/export仕様の検討中に、以下の重要な気づきが
**新しい3層プラグインシステムが確立されました**
```nyash
# custom_math_plugin.ny
export box CustomMathPlugin {
# Nyashスクリプトプラグインユーザー定義Box
box DataProcessor {
init {
_math = new MathBox() # 既存プラグイン
_cache = new MapBox() # 結果キャッシュ
me.file = new FileBox() # C ABIプラグイン使
me.math = new MathBox() # C ABIプラグイン使用
me.cache = new MapBox() # これもC ABIプラグインビルトイン廃止
}
// カスタム拡張
cached_sin(x) {
local key = x.toString()
if me._cache.has(key) {
return me._cache.get(key)
}
local result = me._math.sin(x)
me._cache.set(key, result)
process(data) {
local result = me.math.sin(data)
me.file.write("log.txt", result.toString())
return result
}
}
# 使用例
local processor = new DataProcessor()
processor.process(3.14) # すべてプラグインで動作!
```
これにより、Rust/C++のビルドなしでプラグイン開発が可能に!
## 📝 なぜ誤解が生まれたのか
## 🎯 統一Box ABI設計
「プラグイン」という言葉から、特別な仕組みが必要だと考えてしまいましたが、Nyashの「Everything is Box」哲学により、ユーザー定義Boxこそが最高のプラグインシステムでした。
### 基本インターフェース
詳細な分析:[なぜ天才AIたちは間違えたのか](./WHY-AIS-FAILED.md)
```rust
// Rust側の統一インターフェース
trait BoxInterface {
fn invoke(&self, method_id: u32, args: NyashValue) -> NyashValue;
fn get_methods(&self) -> Vec<MethodInfo>;
fn init(&mut self, ctx: Context);
fn drop(&mut self);
}
```
## 🚀 Phase 12の真の価値コード共有エコシステム
### Nyashスクリプトプラグインの要件
### 本当に必要なもの
1. **export/import構文**
```nyash
export box MyPlugin {
// 必須:初期化
init { ... }
// 推奨FFI互換インターフェース
invoke(method_id, args) {
// method_idに基づいてディスパッチ
# math_utils.ny
export box MathUtils {
factorial(n) { ... }
fibonacci(n) { ... }
}
// オプション:メソッド情報
get_methods() {
return [
{ name: "method1", id: 1 },
{ name: "method2", id: 2 }
]
}
}
# main.ny
import { MathUtils } from "math_utils.ny"
local utils = new MathUtils()
```
## 📊 エコシステムへの影響
2. **パッケージマネージャー**
```bash
nyash install awesome-math-utils
nyash publish my-cool-box
```
### 開発の民主化
- **参入障壁の劇的低下**: Rust/C++環境不要
- **即座の開発**: ビルド待ち時間ゼロ
- **コミュニティ拡大**: より多くの開発者が参加可能
3. **ドキュメント生成**
```nyash
# @doc 素晴らしい数学ユーティリティ
# @param n 計算したい数値
# @return 階乗の結果
export box MathUtils { ... }
```
### 新しい開発パターン
1. **プラグインの合成**: 複数のネイティブプラグインを組み合わせ
2. **ラピッドプロトタイピング**: アイデアを即座に実装
3. **ホットリロード**: 実行中の更新が可能
## 📊 新しい3層プラグインシステム
## 🛣️ 実装ロードマップ
```
NyashエコシステムビルトインBox廃止後
├── Nyashスクリプトプラグインユーザー定義Box← .nyashファイル
├── C ABIプラグイン既存のまま使用← シンプル・高速・安定
└── Nyash ABIプラグイン必要時のみ← 言語間相互運用・将来拡張
└── MIR命令は増やさないBoxCallにabi_hint追加のみ
```
### Phase 12.1: 基盤構築
- [ ] Box ABI仕様の最終決定
- [ ] export box構文のパーサー実装
- [ ] 基本的なPluginRegistry実装
### プラグイン選択の指針
- **C ABIで済むなら、C ABIを使う**(シンプルイズベスト)
- Nyash ABIは以下の場合のみ
- 他言語Python/Go等からの呼び出し
- 複雑な型の相互運用が必要
- 将来の拡張性を重視する場合
### Phase 12.2: 統一インターフェース
- [ ] FFIプラグインのBoxInterface対応
- [ ] NyashスクリプトのBoxInterface実装
- [ ] 相互運用テスト
## 🛣️ 実装ロードマップ(修正版)
### Phase 12.3: 動的機能
- [ ] 動的ロード/アンロード機能
- [ ] ホットリロード対応
- [ ] プラグイン間依存関係管理
### Phase 12.1: export/import構文2週間
- [ ] exportキーワードのパーサー実装
- [ ] importステートメントの実装
- [ ] モジュール解決システム
- 📄 **[詳細仕様書](./export-import-spec.md)**
### Phase 12.4: セキュリティと最適化
- [ ] サンドボックス実装
- [ ] ケイパビリティベース権限
- [ ] パフォーマンス最適化
### Phase 12.2: パッケージ管理3週間
- [ ] nyash.tomlのdependencies対応
- [ ] 中央リポジトリ設計
- [ ] CLIツールinstall/publish
- 📄 **[パッケージマネージャー設計書](./package-manager-design.md)**
## 📚 関連ドキュメント
- [Gemini先生の分析](./gemini-analysis-script-plugins.md)
- [Codex先生の技術提案](./codex-technical-proposal.md)
- [統合分析まとめ](./synthesis-script-plugin-revolution.md)
## 🎯 次のアクション
1. Box ABI仕様書の作成
2. export box構文の実装開始
3. 既存FFIプラグイン1つを統一インターフェースに移行
### Phase 12.3: 開発者体験向上(継続的)
- [ ] ドキュメント生成ツール
- [ ] VSCode拡張補完・定義ジャンプ
- [ ] サンプルパッケージ作成
---
*Everything is Box - そしてプラグインもBoxになる*
### 🗄️ 議論の過程
AIたちがなぜ複雑な解決策を提案したのか、その議論の過程は `archive/` ディレクトリに保存されています。良い教訓として残しておきます。

View File

@ -0,0 +1,112 @@
# なぜ天才AIたちは間違えたのか - Phase 12の教訓
## 🤔 根本的な誤解
### AIたちの誤解
```
「スクリプトプラグイン」という特別な仕組みが必要
→ JIT/AOTから呼び出せない問題がある
→ 複雑な解決策が必要
```
### 実際の真実
```
Nyashスクリプト = 普通のユーザー定義Box
→ すでに動いている
→ 何も問題ない
```
## 💡 なぜこんな誤解が生まれたのか
### 1. 「プラグイン」という言葉の罠
- AI思考「プラグイン = 外部DLL/SO = 特別な仕組み」
- 実際:「プラグイン = 再利用可能なBox = 普通のユーザー定義Box」
### 2. 実装レイヤーの混同
```
AIの頭の中
┌─────────────────────┐
│ JIT/AOT (最適化層) │ ← ここに固執
├─────────────────────┤
│ C ABI プラグイン │
├─────────────────────┤
│ スクリプトプラグイン │ ← 新しい層を作ろうとした
└─────────────────────┘
実際のNyash
┌─────────────────────┐
│ Nyash VM/インタープリター │
├─────────────────────┤
│ すべてがBox統一層
│ - ビルトインBox │
│ - C ABIプラグイン │
│ - ユーザー定義Box │ ← これだけで十分!
└─────────────────────┘
```
### 3. 過度な最適化思考
- AI「JIT/AOTで高速化しなければ
- 実際:「インタープリター言語として十分高速」
### 4. Everything is Boxの哲学を忘れた
```nyash
# これが既に「スクリプトプラグイン」!
box DataProcessor {
init {
me.file = new FileBox() # C ABIプラグイン
me.math = new MathBox() # C ABIプラグイン
me.cache = new MapBox() # ビルトインBox
}
process(data) {
# 全部普通に動く!
local result = me.math.sin(data)
me.file.write("log.txt", result.toString())
return result
}
}
```
## 🎓 教訓
### 1. シンプルな解決策を見逃すな
- 複雑な問題に見えても、既存の仕組みで解決できることが多い
- 「Everything is Box」は強力な統一原理
### 2. 言葉に惑わされるな
- 「プラグイン」→「特別な仕組み」という連想を避ける
- 本質を見る:「再利用可能なコード」
### 3. ユーザー視点を忘れるな
```nyash
# ユーザーから見れば、これで十分!
local processor = new DataProcessor()
local result = processor.process(3.14)
```
### 4. AIの弱点
- 技術用語から過度に複雑な解釈をしがち
- 既存の解決策より新しい仕組みを作りたがる
- レイヤーを増やしたがる傾向
## 📝 Phase 12の本当の価値
### 実は必要なのは
1. **export/import構文**ファイル間でBoxを共有
2. **パッケージマネージャー**Boxの配布・インストール
3. **ドキュメント生成**Boxの使い方を共有
### 必要ないもの
- 特別な「スクリプトプラグイン」層
- JIT/AOT統合
- 複雑なトランスパイル
## 🚀 結論
**Nyashは最初から正しかった。Everything is Boxだから、すべてが統一的に動く。**
AIたちは「プラグイン」という言葉に惑わされて、存在しない問題を解決しようとしていた。
---
*時に、最も賢い解決策は「何もしないこと」である。*

View File

@ -0,0 +1,87 @@
# Nyash ABI戦略議論まとめ (2025-09-01)
## 📋 概要
Phase 12のプラグインシステム実装において、C ABIとNyash ABIの選択について、AI先生方と深い技術議論を行いました。
## 🗂️ ドキュメント一覧
1. **[gemini-abi-analysis.md](gemini-abi-analysis.md)**
- Gemini先生の長期エコシステム視点
- ABI安定性の重要性
- 段階的進化戦略C ABI → SDK → WASM
2. **[codex-abi-implementation.md](codex-abi-implementation.md)**
- Codex先生の実装最適化視点
- C呼出規約×Nyash値表現の提案
- VM/JIT最適化の具体策
3. **[deep-analysis-synthesis.md](deep-analysis-synthesis.md)**
- なぜ正解が難しいのかの深い分析
- 時間軸のジレンマ
- 「正解がない」ことが答え
## 🎯 結論BoxCall拡張による統合案
### 最終的な実装方針
```rust
// MIRレベルBoxCallをちょっと拡張
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
abi_hint: Option<AbiType>, // ← これだけ追加
}
```
### 利点
1. **MIR命令数は15個のまま**(美しさ維持)
2. **既存コードは変更不要**(後方互換)
3. **プラグインごとにABI選択可能**(段階的移行)
4. **Everything is Box哲学の体現**
### 実装計画
```yaml
Week 1: 基盤整備
- PluginABI enum定義
- nyash.tomlのabi field追加
- NyashValue構造体作成
Week 2: VM統合
- プラグインローダー拡張
- VM実行時のABI分岐
- pack/unpack実装
Week 3: 検証
- 比較ベンチマーク
- ドキュメント作成
- 方向性判断
```
## 🔑 重要な洞察
1. **両ABI共存が現実的**
- C ABI既存資産・高速・安定
- Nyash ABI型安全・拡張性・将来性
2. **適応的戦略の採用**
- 3ヶ月ごとに測定・評価
- データに基づく進化
3. **箱理論による差し替え可能性**
- 実装を箱に切り出す
- いつでも戻せる安心感
## 📊 次のステップ
1. このREADMEを起点に実装開始
2. 最小限のプロトタイプ作成
3. 性能・開発体験の比較データ収集
4. Phase 12本実装への反映
---
*「間に挟むだけ」が最も難しい設計判断だった。しかし、BoxCall拡張という自然な解決策にたどり着いた。*

View File

@ -0,0 +1,124 @@
# Codex先生のNyash ABI実装戦略 (2025-09-01)
## 質問内容
Nyashプラグインシステムの ABI戦略について技術的相談です。
【背景】
Phase 12でNyashスクリプトプラグインシステムを実装中です。現在、プラグインインターフェースの設計で重要な判断が必要です。
【現状】
- 既存: C ABIシンプル、高速、実績あり
- 提案: Nyash ABI3×u64構造体、型情報付き、拡張性高い
【トレードオフ】
C ABI:
- 利点: ゼロオーバーヘッド、既存資産活用、シンプル
- 欠点: 型情報なし、拡張性限定、エラー処理が原始的
Nyash ABI:
- 利点: 型安全、メタデータ活用、将来性async等
- 欠点: pack/unpackのオーバーヘッド、新規学習コスト
【具体的な選択肢】
1. Nyash ABIのみ統一性重視
2. C ABI + Nyash ABI両方実用性重視
3. その他の革新的アプローチ?
【質問】
1. どちらの戦略が長期的に見て賢明でしょうか?
2. 両方サポートする場合の複雑性は管理可能でしょうか?
3. 他の言語での成功例・失敗例から学べることは?
4. VM/JIT最適化の観点から、どちらが有利でしょうか
実装の実践的観点から、C ABIとNyash ABIの選択について深く分析してください。特にVM/JIT最適化や実装の複雑性を考慮して。
## Codex先生の回答
**結論**
- 長期的には「C呼出規約 × Nyash値表現3×u64」を中核に、C ABIとNyash ABIの両方をサポートする二層戦略が最も堅実。
- まずは二者併存で運用し、JIT/VMでの最適化余地と実用性を確保しながら、将来的にNyash ABIを第一級に育てるのが現実的。
**推奨戦略**
- 両対応2を採用。ただし"呼出規約はC、値表現はNyash"を共通土台にする。
- 共通の値ABI: `#[repr(C)] struct NyValue { u64 tag; u64 payload; u64 aux; }`
- C ABI関数でもNyValueを受け渡しできるため、型安全・メタデータ拡張の道を確保しつつ、Cの安定性・高速性を得る。
- プラグイン初期化は「バージョン付き関数テーブルvtable機能フラグ」の交渉型にする。
- 例: `NyPluginInitV1(NyRtFnsV1* rt, NyPluginV1* out)``NyPluginV1 { u32 abi_mask; u32 size; NyFnDesc* fns; ... }`
- `abi_mask``NY_ABI_C``NY_ABI_NYV1` の両方可否を示す。将来の `NY_ABI_NYV2` も追加可能。
- エラーモデルは二段階:
- 速経路: `NyStatus`enum, small intを戻す。失敗時は`NyValue`でエラーオブジェクトをout-param返却。
- 詳細情報は`NyError`ヘルパ(`NyValue`として表現に格納。FFI境界でpanic禁止・例外横断なし。
- メモリ/所有権はハンドル/関数テーブル経由で統一NyRTのalloc/free、string/slice作成API。内部構造体への直接依存は不可。
**複雑性と管理**
- 追加コストは主にローダとアダプタ(トランポリン)実装+テスト行列の増加。ただし次の工夫で管理可能:
- 機能交渉を一本化init関数とマニフェスト/nyash.tomlで表明。ロード時に一度だけ決定。
- 呼出アダプタをテンプレート化JIT時に生成・キャッシュ型配列→シグネチャ→thunkアドレス
- 型表(`NyFnDesc`に引数/戻りの型ID配列、純関数/readonly/aliasing属性を持ち、最適化と検証を兼用。
- 見積りローダvtableアダプタの骨格は数百行〜千行弱で収まり、以降は関数種別追加が足し算。
**他言語の事例からの学び**
- 成功例
- Node N-API: バージョン化された関数テーブルでABIを安定化。実装側の進化と互換の両立に有効。
- Lua C API: C呼出規約独自値表現で長期安定。ただしstackベースは学習コストあり。
- 反省点
- CPython C API: 内部構造体露出で進化の足枷に。HPyはハンドル化で再設計。
- JNI: 汎用だが冗長・低速傾向。最適化しづらい。
- 教訓
- 内部レイアウト非公開(ハンドル/関数経由・バージョン化vtable・拡張フラグが長期安定の鍵。
- 値ABIは固定サイズ・レジスタ渡し最適化可能な形が吉3×u64は合理的
**VM/JIT最適化の観点**
- 呼出オーバーヘッド
- 3×u64はSysV/Win64いずれもレジスタ渡しが効きやすく、C ABIと同等のコールコストに収束。
- 本質的ボトルネックは間接呼出ミス予測と型検査。JITで型が既知ならチェックを埋め込み/削減可能。
- 直呼 vs トランポリン
- 署名が一致する場合は直接呼出JITに外部シンボルとして登録
- 型昇降/借用が必要な時のみthunkを噛ませる。JITでモモーフィック化・デッドパス除去。
- 最適化ヒントCranelift/LLVM
- 関数属性: `nounwind`, `readonly`/`readnone`, `nocapture`, `nonnull`, `cold`(エラーパス)
- 戻り値はout-paramにしてtailcall最適化`musttail`適用可否は要検証)。
- シンボルはAOT時`dso_local`、JITは`CallConv::SystemV/WindowsFast`で3×i64に整合。
- ホットループ対策
- スカラー跨ぎは避け、スライス/バルクAPIを定義例: vector add。boundary跨ぎ回数を削減。
- 将来は静的リンクモードfirst-party pluginsでLTO許可→AOTでインライン・DCE可。
**Nyash ABIのみ vs 併存の比較**
- Nyash ABIのみ1
- 一貫性と将来性は高いが、既存C資産/ツール連携コストと立上げ摩擦が大きい。
- 併存2, 推奨)
- 早期エコシステム拡大と最適化の両取り。複雑性はvtableアダプタの設計で抑え込み可能。
- その他3
- N-API風ハンドル専一は安定するが、オーバーヘッド/冗長性が増える。Nyashのパフォーマンス志向には過剰。
**実装メモ(具体)**
- 値ABI
- `tag`: 上位ビットに種別・フラグown/borrow、small/heap、nullable等
- `payload`: ポインタor即値、`aux`: len/stride/arena_id等
- `NySlice`: `tag=SLICE``payload=ptr`, `aux=len`。borrow時は生存期間制約、必要なら`nyrt_clone_slice`で所有権取得。
- 初期化/交渉
- `NyPluginInitV1(NyRtFnsV1*, NyPluginV1*) -> NyStatus`
- `NyRtFnsV1`: alloc/free、string/slice/future、log、error造作API。
- `NyPluginV1`: `abi_mask`, `size`, `features`, `fn_count`, `NyFnDesc*`.
- エラー/非同期
- `NyStatus` + `NyValue err_out`。非同期は `NyFuture*`を返却、`NyRtFnsV1``poll/wake`を橋渡し。
- VMのスケジューラとWakerのブリッジはvtableで隠蔽ABI非依存
- 安定性
- すべての公開構造体に`size`を先頭フィールドに持たせ将来拡張。未理解フィールドは無視。
- ABIバージョンは数値機能フラグで条件分岐ビルド時にも`#[cfg(feature)]`で絞る)。
**質問への回答**
- 1) 長期的に賢明か: 併存共通値ABIが最善。Cの互換性とNyashの型安全/拡張性を両立。
- 2) 複雑性は管理可能か: はい。vtableの一本化、JIT生成thunk、機能交渉で抑制できる。
- 3) 学び: 内部構造体を外部に晒さない・ハンドル経由・バージョン化テーブル・豊富な属性メタデータ。CPythonの反省とN-APIの成功を取り込む。
- 4) VM/JIT最適化: 3×u64のNyash値はレジスタ渡しでC ABIと同等に速く、型既知箇所はJITで直呼/チェック省略可能。最適化の自由度はNyash ABIの方が高い。
**次のアクション**
- 値ABIと関数テーブル仕様を`crates/nyrt/include/nyrt.h`に最小ドラフト化(`NyValue`, `NyStatus`, `NyRtFnsV1`, `NyPluginV1`, `NyFnDesc`)。
- ローダにinit/交渉実装(`nyash.toml`のabi種別・型情報読み込み→関数登録
- JIT/VMに外部関数署名登録とthunk生成を追加Cranelift/LLVM兼用
- バルク演算プラグインのスモークを作成C ABI版とNyash ABI版を比較ベンチ
- ドキュメント化(`docs/plugins/abi.md`)とサンプル(`plugins/``apps/`)追加。
必要なら、ドラフトの`NyValue``NyPluginInitV1`の最小Cヘッダ案もすぐ出します。

View File

@ -0,0 +1,188 @@
# Codex先生のBoxCall拡張分析 (2025-09-01)
## 質問内容
Nyash ABI実装の具体的アプローチについて技術相談です。
【背景】
前回の議論で、C ABIとNyash ABIの両方をサポートする方向性が見えました。実装方法について新しいアイデアがあります。
【BoxCall拡張による統合案】
MIR命令を増やさず、既存のBoxCallを自然に拡張する案です
```rust
// 現在のBoxCall
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
}
// 提案:最小限の拡張
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
abi_hint: Option<AbiType>, // 追加
}
```
【実装の流れ】
1. MIRビルダープラグインBoxならABIヒント付与
2. VM実行時receiver.type_idから適切なABI選択
3. プラグインローダーnyash.tomlでABI指定可能
【この案の利点】
- MIR命令数は15個維持Everything is Box哲学
- 既存コード無変更(後方互換)
- プラグインごとに段階的移行可能
- JITでABI別最適化も可能
【質問】
1. このBoxCall拡張案は設計として健全でしょうか
2. abi_hintは本当に必要VM実行時の型判定で十分
3. 将来的な拡張WASM等にも対応できる設計か
4. パフォーマンス影響をどう評価すべきか?
実装の観点から、MIR層でのabi_hint追加とVM層でのABI判定について深く分析してください。特に既存実装への影響を最小化する方法を考慮して。
## Codex先生の回答
### コードベース分析
まず、現在のMIR/VM実装を確認
```rust
// 現在のMirInstruction::BoxCall定義
BoxCall {
dst: Option<ValueId>,
box_val: ValueId,
method: String,
method_id: Option<u16>, // Universal method用
args: Vec<ValueId>,
effects: EffectMask,
}
```
プラグインローダーはすでにnyash.tomlから型情報を読み込んでおり、VM実行時に利用可能。
### 核心的な洞察
**abi_hintは実は不要かもしれない**
理由:
1. VM実行時にreceiver.type_idから型情報を取得可能
2. プラグインローダーが既にABI情報を保持している
3. MIR層を変更せずに済む15命令維持
### 実装提案
#### Option A: 最小限実装MIR無変更
```rust
// VM層での実装
fn execute_boxcall(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
let receiver = self.get_value(box_val)?;
let type_id = receiver.get_type_id();
// プラグインローダーから型情報取得
if let Some(plugin_info) = self.runtime.get_plugin_info(type_id) {
match plugin_info.abi.as_str() {
"c" => self.call_c_abi_plugin(plugin_info, method, args),
"nyash" => self.call_nyash_abi_plugin(plugin_info, method, args),
_ => self.call_c_abi_plugin(plugin_info, method, args), // fallback
}
} else {
// ビルトインBox処理既存のまま
self.execute_builtin_method(receiver, method, args)
}
}
```
#### Option B: 将来の拡張性を考慮
```rust
// MIRに汎用メタデータ追加ABIに限定しない
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
metadata: Option<u32>, // 汎用的な最適化ヒント
}
```
### JIT/AOT最適化の観点
```rust
// JIT時の特化
match type_info.abi {
Some("c") => {
// 直接call命令生成
builder.emit_direct_call(symbol, args);
}
Some("nyash") => {
// NyashValue pack/unpackを含めてインライン化
let packed = builder.emit_pack_nyash_values(args);
builder.emit_call_with_abi(symbol, packed);
}
_ => {
// 汎用パスVM呼び出し
builder.emit_vm_boxcall(receiver, method, args);
}
}
```
### パフォーマンス評価方法
```rust
// ベンチマーク設計
fn benchmark_abi_comparison() {
// 1. 高頻度呼び出しmath operations
measure_time("C ABI", || {
for _ in 0..1_000_000 {
math_add_c_abi(1.0, 2.0);
}
});
measure_time("Nyash ABI", || {
for _ in 0..1_000_000 {
math_add_nyash_abi(pack(1.0), pack(2.0));
}
});
// 2. 複雑な型string operations
// 3. プラグイン初期化コスト
}
```
### 結論と推奨事項
1. **abi_hintは不要** - VM実行時の型判定で十分
2. **MIR層は無変更** - 15命令の美しさ維持
3. **将来拡張は型システム経由** - WASMもプラグイン型として追加
4. **段階的実装**
- Week 1: nyash.toml拡張 + VM分岐
- Week 2: ベンチマーク + 最適化
- Week 3: JIT特化必要なら
### 実装の具体的ステップ
```yaml
Step 1: nyash.toml拡張
- LibraryDefinition に abi: Option<String> 追加
- デフォルトは "c"
Step 2: プラグインローダー
- PluginInfo 構造体に abi フィールド追加
- ロード時に保存
Step 3: VM execute_boxcall
- 型判定でABI分岐
- call_c_abi / call_nyash_abi メソッド追加
Step 4: テスト
- 同一機能の C/Nyash 両実装
- 性能比較測定
```
この最小限アプローチなら、既存コードへの影響を最小化しつつ、将来の拡張性も確保できます。

View File

@ -0,0 +1,164 @@
# Nyash ABI戦略の深い分析 - なぜ正解が難しいのか (2025-09-01)
## 🤔 両先生の回答から見えた根本的な難しさ
### 1. 時間軸のジレンマ
**Gemini先生の視点**(長期エコシステム):
- 10年後も動くプラグインを作りたい
- 破壊的変更は絶対避けたい
- 開発者の信頼が最重要
**Codex先生の視点**(現実的最適化):
- 今すぐ高速に動かしたい
- JIT/VMの最適化余地を残したい
- 実装の複雑性を抑えたい
**この2つは本質的に矛盾する**
### 2. 抽象化レベルの選択
```
高レベル抽象化Gemini案
SDK層
C ABI安定境界
vs
低レベル統合Codex案
共通値表現3×u64
C呼出規約で統一
```
どちらが正解? **状況による!**
### 3. 隠れた複雑性の罠
**表面的には単純に見える選択**
- C ABI only → シンプル!
- Nyash ABI only → 統一的!
- 両方サポート → 柔軟!
**実際の複雑さ**
- C ABI only → 型情報なし、拡張困難
- Nyash ABI only → 既存資産切り捨て
- 両方サポート → **複雑性が2倍...ではなく4倍**
### 4. なぜ複雑性が爆発するのか
```
組み合わせ爆発:
- 2つのABI ×
- 3つのバックエンドInterpreter/VM/JIT ×
- N個のプラグイン型 ×
- M個の最適化レベル
= 指数関数的複雑性
```
## 🎯 深い洞察:本当の問題は何か
### 技術的正解 vs ビジネス的正解
**技術的に美しい解**
- Nyash ABI一本化
- 型安全、拡張可能、統一的
**ビジネス的に賢い解**
- C ABI + 段階的移行
- 既存資産活用、リスク分散
**どちらも「正解」であり「不正解」**
### 隠れた第3の選択肢
両先生が暗黙的に示唆している:
```
時期による使い分け:
Phase 1: C ABIで素早くエコシステム立ち上げ
Phase 2: SDK層で開発体験向上
Phase 3: Nyash ABIで技術的優位性確立
Phase 4: 統合または選択的廃止
```
## 💡 究極の洞察:「正解がない」ことが答え
### なぜ正解が難しいのか
1. **未来は予測不可能**
- NyashがRustを超えるか
- WASMが世界標準になるか
- 新しいABI標準が生まれるか
2. **トレードオフは価値観次第**
- 速度 vs 安全性
- シンプル vs 機能性
- 互換性 vs 革新性
3. **成功の定義が人による**
- 多くのプラグイン?
- 高速な実行?
- 美しいコード?
## 🚀 実践的な答え:適応的戦略
### 推奨アプローチ
```rust
// 初期実装:両対応だが内部統一
enum PluginABI {
C(CPlugin), // 既存資産
Nyash(NyashPlugin), // 新規開発
}
// 共通インターフェース
trait Plugin {
fn invoke(&self, args: &[Value]) -> Result<Value>;
}
// 将来の拡張ポイント
impl PluginABI {
fn optimize_for_jit(&self) -> Option<DirectCall> {
// JIT時に最適化可能なら直接呼び出しに変換
}
}
```
### 段階的進化の道筋
1. **観察期間**6ヶ月
- 両ABI並行運用
- 使用パターン分析
- パフォーマンス測定
2. **最適化期間**次の6ヶ月
- 頻出パターンの高速化
- SDK層の洗練
- ドキュメント充実
3. **判断期間**1年後
- データに基づく選択
- 片方を非推奨に?
- それとも永続的共存?
## 🌟 結論:「正解がない」を受け入れる勇気
**Nyashの哲学**
- Everything is Box → Everything has Trade-offs
- 完璧より進捗80/20ルール
- 箱理論で差し替え可能に
**最終提案**
1. 両方実装するが、**内部アーキテクチャは統一**
2. **測定可能な成功指標**を先に定義
3. **3ヶ月ごとに振り返り**、方向修正
正解がないからこそ、**適応し続けることが正解**。
---
*「間に挟むだけ」のABI層が、実は最も難しい設計判断の一つだった。*

View File

@ -0,0 +1,115 @@
# BoxCall拡張によるABI戦略 - 最終実装決定 (2025-09-01)
## 🎯 両先生の回答から得た重要な洞察
### Gemini先生の誤解が示す重要な点
- 先生は「BoxCall = Box<dyn Trait>を渡す」と誤解
- しかし、この誤解が**abi_stable**の重要性を教えてくれた
- 将来的にRustトレイトオブジェクトを扱う際の指針に
### Codex先生の実践的分析
1. **abi_hintは実は不要かもしれない**
- VM実行時にtype_idから判定可能
- プラグインローダーが型情報を保持
- MIR層を汚さない
2. **既存実装への影響最小化**
- TypeMetaにABI情報を含める
- nyash.tomlから読み込み済み
- VM側で判定するだけ
## 🚀 最終実装方針
### Option A: 最小限実装(推奨)
```rust
// MIR層変更なし
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
// abi_hint不要
}
// VM層型判定でABI選択
fn execute_boxcall(...) {
let type_id = receiver.get_type_id();
// プラグインローダーから型情報取得
if let Some(plugin_info) = get_plugin_info(type_id) {
match plugin_info.abi {
"c" => call_c_abi(...),
"nyash" => call_nyash_abi(...),
_ => fallback_to_c_abi(...),
}
}
}
```
### Option B: 明示的ヒント(将来の拡張性)
```rust
// MIR層最小限の拡張
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value],
metadata: Option<u32>, // 汎用メタデータABIヒント含む
}
```
## 📊 比較表
| 観点 | Option A最小限 | Option Bヒント付き |
|------|------------------|---------------------|
| MIR変更 | なし ✅ | 最小限 |
| 実装工数 | 1週間 | 2週間 |
| JIT最適化 | VM時判定 | ビルド時判定可能 |
| 将来拡張 | 型情報経由 | メタデータ活用 |
## 🎯 結論Option Aで始める
### 理由
1. **80/20ルール**: まず動くものを作る
2. **MIR無変更**: 15命令の美しさ維持
3. **段階的進化**: 必要になったらOption Bへ
### 実装ステップ1週間
```yaml
Day 1-2: nyash.toml拡張
- abi: "c" | "nyash" field追加
- プラグインローダー対応
Day 3-4: VM実行時分岐
- execute_boxcall内でABI判定
- NyashValue pack/unpack実装
Day 5-6: テスト・ベンチマーク
- 同一機能の2種類プラグイン
- 性能比較測定
Day 7: 判断
- データに基づく方向性決定
- Option B移行の必要性評価
```
## 💡 深い洞察
**「abi_hintは不要」という発見が示すもの**
- 型システムが既に十分な情報を持っている
- MIRは純粋な意図の表現で良い
- 実行時の賢い判断に任せる
これこそ「Everything is Box」の真髄 - Boxが自分の実装方法を知っている
## 🚀 次のアクション
1. nyash.tomlパーサーに`abi` field追加
2. プラグインローダーでABI情報保持
3. VM execute_boxcallで分岐実装
4. 最小限のテストプラグイン作成
---
*最小限から始めて、データで判断する。これがNyashの道。*

View File

@ -0,0 +1,179 @@
# Gemini先生のNyash ABI戦略分析 (2025-09-01)
## 質問内容
Nyashプラグインシステムの ABI戦略について技術的相談です。
【背景】
Phase 12でNyashスクリプトプラグインシステムを実装中です。現在、プラグインインターフェースの設計で重要な判断が必要です。
【現状】
- 既存: C ABIシンプル、高速、実績あり
- 提案: Nyash ABI3×u64構造体、型情報付き、拡張性高い
【トレードオフ】
C ABI:
- 利点: ゼロオーバーヘッド、既存資産活用、シンプル
- 欠点: 型情報なし、拡張性限定、エラー処理が原始的
Nyash ABI:
- 利点: 型安全、メタデータ活用、将来性async等
- 欠点: pack/unpackのオーバーヘッド、新規学習コスト
【具体的な選択肢】
1. Nyash ABIのみ統一性重視
2. C ABI + Nyash ABI両方実用性重視
3. その他の革新的アプローチ?
【質問】
1. どちらの戦略が長期的に見て賢明でしょうか?
2. 両方サポートする場合の複雑性は管理可能でしょうか?
3. 他の言語での成功例・失敗例から学べることは?
4. VM/JIT最適化の観点から、どちらが有利でしょうか
## Gemini先生の回答
### NyashプラグインシステムにおけるABI戦略の技術的考察
Nyashプラグインシステムの長期的なエコシステム発展を見据えたABIApplication Binary Interface戦略について、深く技術的に考察します。
### 1. ABI安定性の重要性エコシステム成功の礎
プラグインシステムの成功は、**ABIの鉄壁の安定性**にかかっています。ホストアプリケーションNyashとプラグインは、別々にコンパイル・配布されるバイナリです。両者が正しく協調動作できるのは、関数呼び出し規約、データ型表現、メモリアロケーションといった低レベルな規約、すなわちABIが厳密に一致しているからです。
もしNyashのバージョンアップでABIに破壊的変更が生じれば、既存の全プラグインは動作しなくなり、再コンパイルが必要になります。これはエコシステムにとって致命的です。開発者は追従を諦め、ユーザーはプラグインが使えなくなることを恐れてNyashのアップデートをためらうでしょう。
したがって、ABI戦略の最優先目標は**「前方互換性新しいNyashが古いプラグインを動かせると、可能な限りの後方互換性古いNyashが新しいプラグインの機能を限定的にでも利用できる」**を保証することです。
### 2. 現状の分析と潜在的リスク
現在のNyashのコードベースを調査し、プラグイン関連の実装を特定する必要があります。`Cargo.toml`やソースコードから、おそらくRustのダイナミックライブラリ`.so`, `.dll`, `.dylib`)を動的に読み込む形式が採用されていると推測されます。
ここで最も警戒すべきは、**Rust固有のABIに依存することのリスク**です。Rustコンパイラは、パフォーマンス最適化のために、構造体のメモリレイアウトや関数呼び出し規約をバージョン間で予告なく変更します。したがって、`extern "Rust"`(デフォルト)の関数をプラグイン境界として公開するのは、エコシステムの観点からは**絶対に避けなければなりません**。
素晴らしい検索結果から、Nyashはすでに `extern "C"` を広範囲に利用していることが明確にわかります。これは非常に良い出発点です。`plugin_loader_v2.rs``bid/plugin_api.rs` などを見ると、プラグインの初期化(`nyash_plugin_init`)や関数呼び出し(`invoke_fn`のシグネチャがC ABIで定義されています。
これは、ABI安定性に対する基本的な理解がプロジェクトに既に存在することを示しています。しかし、長期的なエコシステムを築くには、これをさらに推し進め、より洗練された戦略を立てる必要があります。
### 3. 長期的な発展のためのABI戦略提案
現在の `extern "C"` ベースのアプローチを基礎としつつ、以下の戦略を段階的に導入することを提案します。
#### 戦略1C ABIを「公式な安定境界」として厳格に定義・文書化する
これは最も重要です。`extern "C"` を使っているというだけでは不十分で、それを**公式な規約**として確立する必要があります。
**具体的なアクション:**
1. **ABI仕様書の作成:**
- `nyash-abi.h`のようなCヘッダーファイル形式で、プラグインが実装すべき関数シグネチャと、Nyashが提供するホスト関数コールバックをすべて定義します。
- `invoke_fn` で使われている `*const u8`, `*mut u8` のようなバイトポインタが、具体的にどのようなデータを指すのか(シリアライズ形式、レイアウトなど)を厳密に文書化します。
- `NyashHostVtable` のような構造体は、フィールドの順序と型が未来永劫変わらないことを保証しなければなりません。新しいホスト関数を追加する場合は、末尾にのみ追加します。
- エラー報告の規約(戻り値の `i32` が何を意味するのか)を定義します。
2. **FFIForeign Function Interface層の明確化:**
- Nyash内部のRustコードと、C ABI境界を明確に分離します。`plugin_loader_v2.rs` はこの役割を担っているようですが、この層は「ABI変換」にのみ責任を持つべきです。内部的なリファクタリングがABIに影響を与えないようにします。
**利点:**
- **言語非依存:** C ABIはあらゆる主要言語C++, Zig, Go, Python, etc.)から利用可能で、プラグイン開発者の裾野を広げます。
- **究極の安定性:** C ABIは数十年にわたり安定しています。
**欠点:**
- **表現力の低さ:** Rustの`Result``Option`、ライフタイムといった豊かな型情報を直接渡せず、`i32`のエラーコードやポインタのNULLチェックといったプリミティブな方法に頼らざるを得ません。
- **手作業と`unsafe`:** データのシリアライズ・デシリアライズやポインタ操作が必須となり、`unsafe`コードが増え、バグの温床になりやすいです。
#### 戦略2高レベルな「Rust向けラッパーSDK」を提供する
C ABIの欠点を補うため、プラグイン開発者が直接C ABIを触らなくても済むように、公式のRust製SDKクレートを提供します。
**具体的なアクション:**
1. **`nyash-plugin-sdk` クレートの作成:**
- このSDKは、低レベルなC ABIのやり取りをすべてカプセル化します。
- プラグイン開発者は、`Result<MyData, MyError>` のような自然なRustの型を扱うだけで、SDKが自動的にC ABI準拠のデータバイト列やエラーコードとの相互変換を行います。
- 定型的なコードを自動生成するマクロ(例: `#[nyash_plugin]`)を提供し、開発体験を向上させます。
**SDK利用者のコードイメージ:**
```rust
// プラグイン開発者が書くコード
use nyash_plugin_sdk::{plugin, NyashResult, NyashError};
struct MyPlugin;
#[plugin]
impl Plugin for MyPlugin {
fn handle_request(&self, input: &str) -> NyashResult<String> {
if input == "hello" {
Ok("world".to_string())
} else {
Err(NyashError::new("Invalid input"))
}
}
}
```
**SDK内部の実装イメージ:**
```rust
// #[plugin] マクロが生成するコード
#[no_mangle]
pub unsafe extern "C" fn nyash_plugin_invoke(
/* C ABI引数 */
) -> i32 {
// 1. C ABIのバイト列から &str へデシリアライズ
// 2. MyPlugin::handle_request を呼び出し
// 3. 結果の Result<String, NyashError> をC ABIのエラーコードとバイト列にシリアライズ
// 4. 結果を返す
// ... この部分はSDKが隠蔽する ...
}
```
**利点:**
- **高い開発者体験:** Rust開発者は、安全で慣用的なRustコードを書くことに集中できます。
- **安全性の向上:** `unsafe`コードをSDKに集約させることで、プラグイン開発者がメモリ安全性を破壊するリスクを大幅に低減できます。
#### 戦略3データ交換フォーマットの標準化
`invoke_fn` でやり取りされる `*const u8` の中身を標準化します。これにより、堅牢性と前方互換性が向上します。
**候補:**
- **Protocol Buffers / FlatBuffers:**
- **利点:** スキーマ定義が明確で、前方・後方互換性を考慮した設計が容易。多言語対応のコード生成ツールが豊富。パフォーマンスも高い。
- **欠点:** ビルドプロセスに一手間加わる。
- **JSON / MessagePack:**
- **利点:** 動的で柔軟。デバッグが容易。
- **欠点:** スキーマがないため、実行時エラーになりやすい。シリアライズ・デシリアライズのオーバーヘッドが比較的多きい。
**推奨:**
長期的なエコシステムの安定性を考えると、スキーマベースの **Protocol Buffers** または **FlatBuffers** が最適です。
#### 戦略4WebAssembly (Wasm) の採用検討
究極の目標として、プラグインをWebAssemblyモジュールとして実行する方式を検討する価値があります。
**利点:**
- **サンドボックス化:** WasmランタイムWasmtimeなど上でプラグインを実行することで、Nyash本体から完全に隔離された安全な環境を提供できます。悪意のあるプラグインやバグのあるプラグインがホストをクラッシュさせるのを防ぎます。
- **真の言語非依存:** C ABIよりもさらに多くの言語コンパイルターゲットにWASIを持つものがプラグイン開発に利用可能になります。
- **WASIとコンポーネントモデル:** WASIWebAssembly System InterfaceやWasm Component Modelの標準化が進むことで、ファイルアクセスやネットワークなどのホスト機能へのアクセスが標準化され、より洗練されたABIを構築できます。
**欠点:**
- **パフォーマンスオーバーヘッド:** C ABIのダイナミックライブラリ呼び出しに比べ、Wasmランタイムを介在させることによるオーバーヘッドが存在します。
- **実装の複雑さ:** Wasmランタイムの組み込みと、ホスト・ゲスト間のデータ共有特に複雑なデータ構造の実装は複雑になります。
### まとめと提言
Nyashは `extern "C"` の採用により、ABI安定化への正しい一歩を踏み出しています。長期的なエコシステムの繁栄のため、以下のロードマップを提案します。
1. **短期(すぐ着手すべき):**
- **C ABIの公式化:** `nyash-abi.h`相当の仕様を定義し、文書化する。
- **データフォーマットの選定:** `invoke_fn` で交換されるデータのシリアライズ形式としてProtocol Buffers等を導入する。
2. **中期(エコシステムの成長期):**
- **Rust向けSDKの提供:** `nyash-plugin-sdk` クレートを開発し、Rust開発者の体験を向上させる。
- **他言語向けSDKの提供:** 需要に応じて、PythonやGo向けのラッパーライブラリを提供する。
3. **長期(未来のビジョン):**
- **WebAssemblyへの移行:** パフォーマンス要件とセキュリティ要件を天秤にかけ、プラグイン実行環境をWasmに移行することを検討する。これにより、究極の安全性と移植性を獲得できる。
この戦略により、Nyashは安定性、開発者体験、安全性を段階的に向上させ、活発で持続可能なプラグインエコシステムを構築できると確信します。

View File

@ -1,8 +1,17 @@
# 🚨 重大な設計問題スクリプトプラグインとMIR/EXEの非互換性
# 🚨 AI議論の誤解存在しない問題を解決しようとした記録
## 問題の本質
## 誤解の本質
**MIR/JIT/AOTEXEは、C ABIの関数しか呼び出せない。**
**この文書は、AIたちが「スクリプトプラグイン」という存在しない問題を作り出し、複雑な解決策を提案した記録です。**
### 実際の事実
- Nyashスクリプト = 普通のユーザー定義Box
- ユーザー定義Boxは既に完璧に動作している
- 特別な仕組みは一切不要
## なぜこんな誤解をしたのか
**「プラグイン」という言葉に引っ張られて、C ABIの外部DLLのような特別な仕組みが必要だと思い込んでしまった。**
```
現実のフロー:

View File

@ -0,0 +1,48 @@
# Phase 12 Archive - AIたちの誤解の記録
## 🚨 重要:このフォルダの内容について
このフォルダには、Phase 12の議論で生まれた**誤解に基づいたドキュメント**が保存されています。
### なぜ誤解が生まれたのか
AIたちは「プラグイン」という言葉から、以下のような複雑な仕組みが必要だと考えました
- トランスパイルNyash→C
- 埋め込みVM
- 特別なABI変換
- JIT/AOT統合
### 実際の真実
**Nyashスクリプト = 普通のユーザー定義Box**
```nyash
# これで十分!特別な仕組みは不要
box MyPlugin {
init {
me.file = new FileBox() # C ABIプラグイン使える
}
process(data) {
return me.file.read(data)
}
}
```
### 教訓として
これらのドキュメントは、以下の教訓を示すために残しています:
1. シンプルな解決策を見逃してはいけない
2. 技術用語に惑わされてはいけない
3. Everything is Boxの哲学を忘れてはいけない
## 📁 アーカイブ内容
- `CRITICAL-ISSUE.md` - 存在しない問題を解決しようとした記録
- `01_roadmap_final.md` - 不要なトランスパイル実装計画
- `02_spec_embedded_vm.md` - 不要な埋め込みVM仕様
- `03_spec_box_arguments.md` - 不要なBox受け渡し仕様
- その他、AI会議での誤解に基づく提案
---
*「時に、最も賢い解決策は何もしないことである」*

View File

@ -0,0 +1,230 @@
# Phase 12: プラグインシステムの進化 - 既存C ABIとの共存戦略
## 🚀 概要
**重要**: 既存のC ABIプラグインはそのまま使い続けられますその上で、以下の2つの新機能を追加
1. **Nyash ABIサポート** - より型安全で拡張性の高い新しいABIオプション
2. **Nyashスクリプトプラグイン** - ビルド不要でプラグイン作成可能
### なぜ既存C ABIを残すのか
- **実績と安定性**: 現在動いているFileBox、NetBox等はそのまま
- **ゼロオーバーヘッド**: 高頻度呼び出しには最速のC ABI
- **段階的移行**: 必要に応じて選択的にNyash ABIへ移行可能
## 💡 発見の経緯
include/export仕様の検討中に、以下の重要な気づきが
```nyash
# custom_math_plugin.ny
export box CustomMathPlugin {
init {
_math = new MathBox() # 既存プラグイン活用
_cache = new MapBox() # 結果キャッシュ
}
// カスタム拡張
cached_sin(x) {
local key = x.toString()
if me._cache.has(key) {
return me._cache.get(key)
}
local result = me._math.sin(x)
me._cache.set(key, result)
return result
}
}
```
これにより、Rust/C++のビルドなしでプラグイン開発が可能に!
## 🎯 ABI共存戦略 - 適材適所の選択
### 三層構造のプラグインエコシステム
```
1. C ABIプラグイン既存・継続
- FileBox, NetBox, MathBox等
- 高速・安定・実績あり
- 変更なし!そのまま使える
2. Nyash ABIプラグイン新規追加
- 型安全・拡張性重視の新規開発向け
- async/await対応などの将来機能
3. Nyashスクリプトプラグイン革命的
- ビルド不要・即座に開発
- 上記1,2を組み合わせて利用可能
```
### 使い分けの指針
| 用途 | 推奨ABI | 理由 |
|------|---------|------|
| 数値計算(高頻度) | C ABI | ゼロオーバーヘッド |
| ファイルI/O | C ABI | 既存実装が安定 |
| 複雑な型操作 | Nyash ABI | 型安全性重視 |
| プロトタイプ | Nyashスクリプト | 即座に試せる |
### 設定例nyash.toml v2.1
```toml
# nyash.toml v2.1
[plugin.math]
path = "plugins/math.so"
abi = "c" # 高速・安定(デフォルト)
[plugin.advanced_math]
path = "plugins/advanced_math.so"
abi = "nyash" # 型安全・拡張性
```
### BoxCall拡張による実装
**重要な発見**MIR層の変更は不要VM実行時の型判定で十分。
```rust
// MIR層変更なし15命令維持
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
}
// VM層賢い判定
fn execute_boxcall(...) {
let type_info = get_plugin_info(receiver.type_id);
match type_info.abi {
"c" => call_c_abi(...), // 既存プラグイン
"nyash" => call_nyash_abi(...), // 新プラグイン
}
}
```
### Nyash ABI仕様
```c
// 3×u64構造体による型安全な値渡し
typedef struct NyashValue {
uint64_t type_id; // 型識別子
uint64_t box_handle; // Boxハンドル
uint64_t metadata; // 拡張用async flag等
} NyashValue;
// 統一関数シグネチャ
typedef NyashValue (*NyashFunc)(
uint32_t argc,
NyashValue* args,
void* context
);
```
### 基本インターフェース(内部)
```rust
// Rust側の統一インターフェース
trait BoxInterface {
fn invoke(&self, method_id: u32, args: NyashValue) -> NyashValue;
fn get_methods(&self) -> Vec<MethodInfo>;
fn init(&mut self, ctx: Context);
fn drop(&mut self);
}
```
### Nyashスクリプトプラグインの要件
```nyash
export box MyPlugin {
// 必須:初期化
init { ... }
// 推奨FFI互換インターフェース
invoke(method_id, args) {
// method_idに基づいてディスパッチ
}
// オプション:メソッド情報
get_methods() {
return [
{ name: "method1", id: 1 },
{ name: "method2", id: 2 }
]
}
}
```
## 📊 エコシステムへの影響
### 開発の民主化
- **参入障壁の劇的低下**: Rust/C++環境不要
- **即座の開発**: ビルド待ち時間ゼロ
- **コミュニティ拡大**: より多くの開発者が参加可能
### 新しい開発パターン
1. **プラグインの合成**: 複数のネイティブプラグインを組み合わせ
2. **ラピッドプロトタイピング**: アイデアを即座に実装
3. **ホットリロード**: 実行中の更新が可能
## 🛣️ 実装ロードマップ2025-09-01更新
**重要**: 既存のC ABIプラグインは一切変更不要追加機能として実装します。
### Phase 12.1: ABI共存基盤1週間
- [ ] nyash.toml v2.1仕様abi fieldサポート、デフォルト="c"
- [ ] プラグインローダーのABI判定実装後方互換性保証
- [ ] VM execute_boxcallのABI分岐追加C ABIは現状維持
- [ ] 同一機能のC ABI / Nyash ABI比較ベンチマーク
### Phase 12.2: Nyash ABI基盤2週間
- [ ] NyashValue構造体定義crates/nyrt/include/nyash_abi.h
- [ ] pack/unpack関数実装
- [ ] 既存プラグイン1つをNyash ABI移行実証実験
- [ ] JIT最適化型既知時の特化コード生成
### Phase 12.3: スクリプトプラグイン対応3週間
- [ ] export box構文のパーサー実装
- [ ] BoxInterface trait実装
- [ ] NyashスクリプトのVM内実行環境
- [ ] 相互運用テストC/Nyash/Script混在
### Phase 12.4: 動的機能とエコシステム(継続的)
- [ ] ホットリロード対応
- [ ] プラグイン間依存関係管理
- [ ] プラグインマーケットプレイス構想
- [ ] セキュリティサンドボックス
### 実装優先順位(短期)
1. **Week 1**: nyash.tomlのabi field + VM分岐動作確認
2. **Week 2**: 性能測定 + 方向性判断
3. **Week 3**: 本格実装 or 方針転換
## 📚 関連ドキュメント
### 初期設計
- [Gemini先生の分析](./gemini-analysis-script-plugins.md)
- [Codex先生の技術提案](./codex-technical-proposal.md)
- [統合分析まとめ](./synthesis-script-plugin-revolution.md)
### ABI戦略議論2025-09-01
- [議論まとめ](./abi-strategy-discussion/README.md)
- [Gemini先生のABI分析](./abi-strategy-discussion/gemini-abi-analysis.md)
- [Codex先生のABI実装戦略](./abi-strategy-discussion/codex-abi-implementation.md)
- [Codex先生のBoxCall拡張分析](./abi-strategy-discussion/codex-boxcall-extension-analysis.md)
- [深い分析と結論](./abi-strategy-discussion/deep-analysis-synthesis.md)
- [最終実装決定](./abi-strategy-discussion/final-implementation-decision.md)
## 🎯 次のアクション(優先順位順)
1. nyash.toml v2.1パーサーに`abi` field追加Day 1-2
2. VM execute_boxcallでABI判定分岐実装Day 3-4
3. SimpleMathプラグインで両ABI比較テストDay 5-6
4. ベンチマーク結果に基づく方向性判断Day 7
### 将来的なアクション
- Nyash ABI仕様書nyash_abi.h作成
- export box構文の実装
- プラグインSDK#[nyash_plugin]マクロ)開発
---
*Everything is Box - そしてプラグインもBoxになる*

Some files were not shown because too many files have changed in this diff Show More