diff --git a/docs/README.md b/docs/README.md index 5fa26351..11186f2f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -51,6 +51,7 @@ - [アーキテクチャ概要](reference/architecture/TECHNICAL_ARCHITECTURE_2025.md) - [実行バックエンド](reference/architecture/execution-backends.md) - [プラグインシステム](reference/plugin-system/) + - [CLIオプション早見表](tools/cli-options.md) ### 開発状況 - [現在のタスク](development/current/CURRENT_TASK.md) diff --git a/docs/VM_README.md b/docs/VM_README.md index 78e6c415..c648b19c 100644 --- a/docs/VM_README.md +++ b/docs/VM_README.md @@ -13,3 +13,25 @@ 今後のタスク: - VM側のfrom Parent.method対応(Builder/VM両対応) - TLVの型拡張(Float/配列/BoxRef戻り値など) + +## 🧮 VM実行統計(NYASH_VM_STATS / JSON) + +VMは命令カウントと実行時間を出力できます。 + +使い方(CLIフラグ): +```bash +# 人間向け表示 +nyash --backend vm --vm-stats program.nyash + +# JSON出力 +nyash --backend vm --vm-stats --vm-stats-json program.nyash +``` + +環境変数(直接指定): +```bash +NYASH_VM_STATS=1 ./target/debug/nyash --backend vm program.nyash +NYASH_VM_STATS=1 NYASH_VM_STATS_JSON=1 ./target/debug/nyash --backend vm program.nyash +# 代替: NYASH_VM_STATS_FORMAT=json +``` + +出力は `total`(総命令数), `elapsed_ms`(経過時間), `counts`(命令種別→回数), `top20`(上位20種)を含みます。 diff --git a/docs/guides/getting-started.md b/docs/guides/getting-started.md index 61df6ac6..6de4d38e 100644 --- a/docs/guides/getting-started.md +++ b/docs/guides/getting-started.md @@ -238,6 +238,20 @@ print("(3 * 4) + (20 / 4) = " + complex1) calc.showHistory() ``` +## 📊 VM性能計測(オプション) + +VMバックエンドの命令統計を有効化すると、性能分析に役立つ集計が得られます。 + +```bash +# 人間向け表示 +nyash --backend vm --vm-stats hello.nyash + +# JSON出力(ツール連携向け) +nyash --backend vm --vm-stats --vm-stats-json hello.nyash +``` + +環境変数での制御も可能です(`NYASH_VM_STATS`, `NYASH_VM_STATS_JSON`)。 + ## ⚡ 並行処理の実践 ```nyash @@ -544,4 +558,4 @@ loop(i < 1000) { --- *Getting Started Guide v1.0* -*Everything is Box - Start Simple, Think Big* \ No newline at end of file +*Everything is Box - Start Simple, Think Big* diff --git a/docs/reference/architecture/execution-backends.md b/docs/reference/architecture/execution-backends.md index 8b131d4a..c4b6a8e9 100644 --- a/docs/reference/architecture/execution-backends.md +++ b/docs/reference/architecture/execution-backends.md @@ -126,6 +126,40 @@ bb0: - **基本ブロック**: 制御フロー最適化 - **効果追跡**: 副作用の管理 - **型安全**: 実行時型チェック + - **対応状況**: 命令ごとの実装は「MIR → VM Mapping」を参照(欠落・暫定箇所の把握に) + - docs/reference/architecture/mir-to-vm-mapping.md + +### 🧮 VM実行統計(命令カウント・時間計測) +VMバックエンドは命令ごとの実行回数と総実行時間(ms)を出力できます。 + +有効化方法(CLI推奨): +```bash +# 人間向け表示 +nyash --backend vm --vm-stats program.nyash + +# JSON出力(機械可読) +nyash --backend vm --vm-stats --vm-stats-json program.nyash +``` + +環境変数でも制御可能: +```bash +NYASH_VM_STATS=1 ./target/release/nyash --backend vm program.nyash +NYASH_VM_STATS=1 NYASH_VM_STATS_JSON=1 ./target/release/nyash --backend vm program.nyash +# もしくは NYASH_VM_STATS_FORMAT=json でも可 +``` + +JSON出力例: +```json +{ + "total": 1234, + "elapsed_ms": 5.123, + "counts": { "Const": 200, "BinOp": 300, "Return": 100 }, + "top20": [ { "op": "BinOp", "count": 300 } ], + "timestamp_ms": 1724371200000 +} +``` + +ベンチマークと併用して、ホット命令の抽出・命令セット最適化に活用できます。 ## 🌐 WASM実行(Web対応) @@ -336,4 +370,4 @@ nyash --compile-wasm app.nyash -o public/app.wat **💡 Tip**: 開発中は**インタープリター**、テスト時は**VM**、配布時は**WASM**という使い分けが効果的です! 最終更新: 2025-08-14 -作成者: Nyash Development Team \ No newline at end of file +作成者: Nyash Development Team diff --git a/docs/reference/architecture/mir-to-vm-mapping.md b/docs/reference/architecture/mir-to-vm-mapping.md new file mode 100644 index 00000000..607a11ac --- /dev/null +++ b/docs/reference/architecture/mir-to-vm-mapping.md @@ -0,0 +1,117 @@ +# MIR → VM Mapping (Draft) + +最終更新: 2025-08-23 + +目的: 生成されたMIR命令がVMでどう実行されるかを1枚で把握し、欠落や暫定実装を洗い出す。26命令ダイエット検討の足場にする。 + +記法: 状態 = Implemented / Partial / No-op / TODO。 + +## コア命令 +- Const: Implemented + - 定数を `VMValue` に格納。 +- BinOp: Partial + - Integer: Add/Sub/Mul/Div 実装(Divは0割チェック)。他の算術/論理/ビット演算は TODO。 + - String: `+` 連結のみ対応。その他は TypeError。 +- UnaryOp: Partial + - `Neg`(int), `Not`(bool) のみ。`BitNot` は TODO。 +- Compare: Partial + - Integer/ String の Eq/Ne/Lt/Le/Gt/Ge 対応。その他型は TypeError。 +- Load / Store: Implemented + - 現状はVM内の値スロット操作(簡易)。 +- Copy: Implemented + - 値コピー+クラス名/内部参照印の伝播。 + +## 制御フロー +- Branch / Jump / Return: Implemented +- Phi: Implemented + - `LoopExecutor` による選択実装(前BB情報を利用)。 + +## 呼び出し/Box関連 +- Call: Implemented + - 関数名を `Const String` として解決しVM内ディスパッチ。 +- BoxCall: Partial + - InstanceBox: `{Class}.{method}/{argc}` へ降格呼び出し(MIR関数)。 + - PluginBoxV2: cfg(feature="plugins")下でLoader経由invoke(引数: NyashBox配列)。 + - Builtinの簡易ディスパッチ: `StringBox.length/substr/concat`, `IntegerBox.toString/abs` 等の最小対応。 + - birth 特例: user-definedの `birth` はMIR関数へ直呼。 +- NewBox: Implemented + - `runtime.box_registry` から生成。`scope_tracker` に登録。クラス名マップ更新。 +- TypeCheck: No-op (常にtrue) + - TODO: 正式な型チェックに置換。 +- Cast: No-op (コピー) + - TODO: 正式な型変換に置換。 + +## 配列 +- ArrayGet: TODO(一時的に0を返す) +- ArraySet: TODO(現在はno-op) + +## デバッグ/出力 +- Debug: No-op(性能優先) +- Print: Implemented(`to_string()`して標準出力) +- Nop: No-op + +## 例外/安全ポイント +- Throw: Partial + - 例外値を表示してVMErrorで中断。ハンドラ探索なし。 +- Catch: No-op + - 例外値スロットを `Void` セットのみ。制御遷移の実装は未対応。 +- Safepoint: No-op + +## 参照/弱参照/バリア +- RefNew / RefGet / RefSet: Partial + - `object_fields` に簡易格納。`object_class` と `box_declarations` を用いた可視性(public/private)簡易検査あり。 +- WeakNew / WeakLoad: No-op相当(通常コピー/取得と同値) + - TODO: 実際の弱参照生存判定を導入。 +- BarrierRead / BarrierWrite: No-op + - 効果注釈のみ(将来の最適化/並行実行基盤に備えた形)。 + +## 非同期 +- FutureNew / FutureSet / Await: Implemented + - `boxes::future::FutureBox` を利用し、同期ブロッキングで結果取得。 + +## 外部呼び出し +- ExternCall: Implemented + - `runtime::get_global_loader_v2().extern_call(iface, method, args)` にルーティング。Some/Noneで戻り値void扱いも考慮。 + +--- + +## 既知の課題(抜粋) +- BinOp/UnaryOp/Compare の型拡張(浮動小数・Bool/Box等)。 +- ArrayGet/ArraySet の実装。 +- TypeCheck/Cast の正規化(型表現と整合)。 +- 例外ハンドリング(Throw/Catchの制御フロー接続)。 +- WeakRef/Barrier の実体化(必要性評価の上、命令ダイエット候補)。 +- PluginBoxV2 のVM側統合強化(引数/戻り値のTLV全型対応、Handle戻り値→BoxRef化)。 + +## VM統計(計測) +- `--vm-stats` / `--vm-stats-json` で命令ごとの使用回数と時間(ms)を出力。 +- ホット命令抽出によりダイエット候補を定量化。 + +--- + +## 実測結果サマリー(初回プローブ) +出所: `local_tests/vm_stats_hello.json`, `local_tests/vm_stats_loop.json`, `simple_math.nyash` + +- ループ系(139命令 / 0.158ms)トップ: + - Const: 25, BoxCall: 23, NewBox: 23, BinOp: 11, Branch: 11, Compare: 11, Jump: 11, Phi: 11, Safepoint: 11 + - 所見: ループloweringで Branch/Jump/Phi/Safepoint が並び、Box初期化とBoxCallが多い。 +- Hello系(6命令): 期待どおりの最小構成(Const/Print/Return中心)。 +- simple_math(18命令): BinOpの使用を確認(整数加減乗除)。 + +補足: +- Safepoint はMIR上で挿入されるが、VMではNo-op(計測には現れる)。 +- NewBox/BoxCall が上位に入るため、命令セットから外すのは不可(コア扱い)。 +- Compare/Branch/Jump/Phi は制御フローのコア。26命令の中核として維持が妥当。 + +## 26命令ダイエット(検討のたたき台) +方針: 「命令の意味は保ちつつ集約」。代表案: +- 維持: Const / Copy / Load / Store / BinOp / UnaryOp / Compare / Jump / Branch / Phi / Return / Call / BoxCall / NewBox / ArrayGet / ArraySet +- 参照: RefNew / RefGet / RefSet(Weak/Barrierは拡張枠へ) +- 非同期: Await(FutureNew/SetはBox APIへ寄せる案も可) +- I/O: Print は開発モード限定 or ExternCall統合(ExternCall自体はBoxCallへ統合方針) +- 調整: TypeCheck/Cast はVerifier/型系に寄せる(命令から外す or 1命令に集約) +- Debug/Nop/Safepoint: メタ扱い(命令数からは外す) + +次ステップ: +- サンプル/テストをVMで実行し、`vm-stats`結果から実使用命令セットを抽出。 +- 上記案に対し互換影響を洗い出し、段階移行(エイリアス→削除)を設計。 diff --git a/docs/tools/cli-options.md b/docs/tools/cli-options.md new file mode 100644 index 00000000..5fc773e1 --- /dev/null +++ b/docs/tools/cli-options.md @@ -0,0 +1,46 @@ +# Nyash CLI Options Quick Reference + +最終更新: 2025-08-23 + +## 基本 +- `file`: 実行するNyashファイル(位置引数) +- `--backend {interpreter|vm|llvm}`: 実行バックエンド選択(既定: interpreter) +- `--debug-fuel {N|unlimited}`: パーサーのデバッグ燃料(無限ループ対策) + +## MIR関連 +- `--dump-mir`: MIRを出力(実行はしない) +- `--verify`: MIR検証を実施 +- `--mir-verbose`: 詳細MIR出力(統計など) + +## VM関連 +- `--vm-stats`: VM命令統計を有効化(`NYASH_VM_STATS=1`) +- `--vm-stats-json`: VM統計をJSONで出力(`NYASH_VM_STATS_JSON=1`) + +## WASM/AOT +- `--compile-wasm`: WATを出力 +- `--compile-native` / `--aot`: AOT実行ファイル出力(要wasm-backend) +- `--output, -o FILE`: 出力先を指定 + +## ベンチマーク +- `--benchmark`: バックエンド比較ベンチを実行 +- `--iterations N`: ベンチ実行回数(既定: 10) + +## 使用例 +```bash +# インタープリターで実行 +nyash program.nyash + +# VMで実行 + 統計をJSON出力 +nyash --backend vm --vm-stats --vm-stats-json program.nyash + +# MIRを出力 +nyash --dump-mir --mir-verbose program.nyash + +# ベンチマーク +nyash --benchmark --iterations 100 +``` + +詳細は `docs/reference/architecture/execution-backends.md` も参照してください。 + +## 参考: `nyash --help` スナップショット +- docs/tools/nyash-help.md diff --git a/docs/tools/nyash-help.md b/docs/tools/nyash-help.md new file mode 100644 index 00000000..90b75fc0 --- /dev/null +++ b/docs/tools/nyash-help.md @@ -0,0 +1,33 @@ +# `nyash --help` Snapshot + +Captured: 2025-08-23 +Source: Built-in clap help from the `nyash` binary + +``` +🦀 Nyash Programming Language - Everything is Box in Rust! 🦀 + +Usage: nyash [OPTIONS] [FILE] + +Arguments: + [FILE] Nyash file to execute + +Options: + --debug-fuel Set parser debug fuel limit (default: 100000, 'unlimited' for no limit) [default: 100000] + --dump-mir Dump MIR (Mid-level Intermediate Representation) instead of executing + --verify Verify MIR integrity and exit + --mir-verbose Show verbose MIR output with statistics + --backend Choose execution backend: 'interpreter' (default), 'vm', or 'llvm' [default: interpreter] + --compile-wasm Compile to WebAssembly (WAT format) instead of executing + --compile-native Compile to native AOT executable using wasmtime precompilation + --aot Short form of --compile-native + -o, --output Output file (for WASM compilation or AOT executable) + --benchmark Run performance benchmarks across all backends + --iterations Number of iterations for benchmarks (default: 10) [default: 10] + --vm-stats Enable VM instruction statistics (equivalent to NYASH_VM_STATS=1) + --vm-stats-json Output VM statistics in JSON format + -h, --help Print help + -V, --version Print version +``` + +関連: CLIオプション早見表は `docs/tools/cli-options.md` + diff --git a/local_tests/test_return_42.nyash b/local_tests/test_return_42.nyash index 40710365..86f1b49d 100644 --- a/local_tests/test_return_42.nyash +++ b/local_tests/test_return_42.nyash @@ -1,5 +1,2 @@ -static box Main { - main() { - return 42 - } -} \ No newline at end of file +// Simple return test +return 42 diff --git a/src/ast.rs b/src/ast.rs index 8b66253b..8126e35b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -958,6 +958,8 @@ mod tests { let box_decl = ASTNode::BoxDeclaration { name: "TestBox".to_string(), fields: vec!["value".to_string()], + public_fields: vec![], + private_fields: vec![], methods, constructors: HashMap::new(), init_fields: vec![], diff --git a/test.txt b/test.txt index e69de29b..4ea2f518 100644 --- a/test.txt +++ b/test.txt @@ -0,0 +1 @@ +Hello from Nyash! \ No newline at end of file diff --git a/tests/e2e_plugin_filebox.rs b/tests/e2e_plugin_filebox.rs index 500b3a98..a51d2af5 100644 --- a/tests/e2e_plugin_filebox.rs +++ b/tests/e2e_plugin_filebox.rs @@ -121,6 +121,57 @@ f.close() assert_eq!(result.to_string_box().value, "void"); } +#[test] +fn e2e_vm_plugin_filebox_open_rw() { + if !try_init_plugins() { return; } + + // Open, write, read via VM backend + let code = r#" +local f, data +f = new FileBox() +f.open("./test_write.txt", "rw") +f.write("HELLO") +data = f.read() +data +"#; + + let ast = NyashParser::parse_from_string(code).expect("parse failed"); + let runtime = NyashRuntime::new(); + let mut compiler = nyash_rust::mir::MirCompiler::new(); + let compile_result = compiler.compile(ast).expect("mir compile failed"); + let mut vm = VM::with_runtime(runtime); + let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); + assert_eq!(result.to_string_box().value, "HELLO"); +} + +#[test] +fn e2e_vm_plugin_filebox_copy_from_handle() { + if !try_init_plugins() { return; } + + let p1 = "./test_out_src.txt"; + let p2 = "./test_out_dst.txt"; + + let code = format!(r#" +local a, b, data +a = new FileBox() +b = new FileBox() +a.open("{}", "w") +b.open("{}", "rw") +a.write("HELLO") +b.copyFrom(a) +data = b.read() +data +"#, p1, p2); + + let ast = NyashParser::parse_from_string(&code).expect("parse failed"); + let runtime = NyashRuntime::new(); + let mut compiler = nyash_rust::mir::MirCompiler::new(); + let compile_result = compiler.compile(ast).expect("mir compile failed"); + let mut vm = VM::with_runtime(runtime); + let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); + assert_eq!(result.to_string_box().value, "HELLO"); +} + #[test] fn e2e_interpreter_plugin_filebox_copy_from_handle() { if !try_init_plugins() { return; } diff --git a/tests/e2e_plugin_net.rs b/tests/e2e_plugin_net.rs index a3ebf0f2..a2f1d914 100644 --- a/tests/e2e_plugin_net.rs +++ b/tests/e2e_plugin_net.rs @@ -4,6 +4,8 @@ use nyash_rust::parser::NyashParser; use nyash_rust::runtime::plugin_loader_v2::{init_global_loader_v2, get_global_loader_v2}; use nyash_rust::runtime::box_registry::get_global_registry; use nyash_rust::runtime::PluginConfig; +use nyash_rust::runtime::NyashRuntime; +use nyash_rust::backend::VM; fn try_init_plugins() -> bool { if !std::path::Path::new("nyash.toml").exists() { return false; } @@ -49,6 +51,39 @@ body assert_eq!(result.to_string_box().value, "OK"); } +#[test] +fn e2e_vm_http_get_basic() { + std::env::set_var("NYASH_NET_LOG", "1"); + std::env::set_var("NYASH_NET_LOG_FILE", "net_plugin3.log"); + if !try_init_plugins() { return; } + + let code = r#" +local srv, cli, r, resp, req, body +srv = new HttpServerBox() +srv.start(8085) + +cli = new HttpClientBox() +r = cli.get("http://localhost:8085/hello") + +req = srv.accept().get_value() +resp = new HttpResponseBox() +resp.write("OK") +req.respond(resp) + +resp = r.get_value() +body = resp.readBody() +body +"#; + + let ast = NyashParser::parse_from_string(code).expect("parse failed"); + let runtime = NyashRuntime::new(); + let mut compiler = nyash_rust::mir::MirCompiler::new(); + let compile_result = compiler.compile(ast).expect("mir compile failed"); + let mut vm = VM::with_runtime(runtime); + let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); + assert_eq!(result.to_string_box().value, "OK"); +} + #[test] fn e2e_http_server_restart() { std::env::set_var("NYASH_NET_LOG", "1");