diff --git a/.github/mlc_config.json b/.github/mlc_config.json new file mode 100644 index 00000000..20ade09e --- /dev/null +++ b/.github/mlc_config.json @@ -0,0 +1,12 @@ +{ + "ignorePatterns": [ + { "pattern": "^mailto:" }, + { "pattern": "^vscode:" }, + { "pattern": "^file://" } + ], + "timeout": "10s", + "retryOn429": true, + "retryCount": 2, + "aliveStatusCodes": [200, 206, 429] +} + diff --git a/.github/workflows/docs-link-check.yml b/.github/workflows/docs-link-check.yml new file mode 100644 index 00000000..f3b235a1 --- /dev/null +++ b/.github/workflows/docs-link-check.yml @@ -0,0 +1,34 @@ +name: docs-link-check + +on: + push: + paths: + - '**.md' + - '.github/workflows/docs-link-check.yml' + - '.github/mlc_config.json' + pull_request: + paths: + - '**.md' + - '.github/workflows/docs-link-check.yml' + - '.github/mlc_config.json' + +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run markdown-link-check on docs and READMEs + uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-quiet-mode: 'yes' + use-verbose-mode: 'no' + config-file: '.github/mlc_config.json' + folder-path: | + docs + file-path: | + README.md + README.ja.md + CLAUDE.md + diff --git a/AGENTS.md b/AGENTS.md index e7cc6055..df9cf980 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -170,7 +170,7 @@ Flags ## 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`). +- Link issues (`docs/development/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 diff --git a/CLAUDE.md b/CLAUDE.md index 255ec7ae..e4207283 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,9 +4,9 @@ ## Start Here (必ずここから) - 現在のタスク: [CURRENT_TASK.md](CURRENT_TASK.md) - - 📁 **Main**: [docs/current_task/main/](docs/current_task/main/) - - 📁 **LLVM**: [docs/current_task/llvm/](docs/current_task/llvm/) - - 📁 **Self**: [docs/current_task/self_current_task/](docs/current_task/self_current_task/) + - 📁 **Main**: [docs/development/current/main/](docs/development/current/main/) + - 📁 **LLVM**: [docs/development/current/llvm/](docs/development/current/llvm/) + - 📁 **Self**: [docs/development/current/self_current_task/](docs/development/current/self_current_task/) - ドキュメントハブ: [README.md](README.md) - 🚀 **開発マスタープラン**: [00_MASTER_ROADMAP.md](docs/development/roadmap/phases/00_MASTER_ROADMAP.md) - 📊 **JIT統計JSONスキーマ(v1)**: [jit_stats_json_v1.md](docs/reference/jit/jit_stats_json_v1.md) diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 4356c730..8a9c6176 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -1,399 +1,49 @@ -# Current Task — Phase 15 Self‑Hosting (2025‑09‑16) +# Current Task — Phase 15 Self‑Hosting (2025‑09‑17) -TL;DR -- 目標は「自己ホスティング達成」= Nyash製パーサで Ny → JSON v0 → Bridge → MIR 実行を安定化すること。 -- PyVM は意味論の参照実行器(開発補助)。llvmlite は AOT/検証。配布やバンドル化は後回し(基礎固めが先)。 +Summary +- Default execution is MIR13 (PHI‑off). Bridge/Builder do not emit PHIs; llvmlite synthesizes PHIs when needed. MIR14 (PHI‑on) remains experimental for targeted tests. +- PyVM is the semantic reference engine; llvmlite is used for AOT and parity checks. -What Changed (today) -- Selfhost 経路の安定化(Python MVP 優先→PyVM 実行)。Selfhost Stage‑2(直/Bridge)スモークは緑化。 -- Using/Resolver を Runner 前処理に集約し、BoxIndex(グローバル)+解決キャッシュを導入。 - - nyash.toml の `[aliases]`/env `NYASH_ALIASES` 対応、候補提示、`NYASH_RESOLVE_TRACE=1` でトレース。 - - strict プレフィクス: `NYASH_PLUGIN_REQUIRE_PREFIX=1` または `[plugins] require_prefix=true`。 - - per‑plugin meta(`prefix/require_prefix/expose_short_names`)の読取導線を実装(挙動は現状据え置き)。 -- CLI `--using` を追加(`--using "ns as Alias"` / `--using '"apps/foo.nyash" as Foo'`)。 -- フィールドは box 先頭のみルールのリンタを Runner に追加(`NYASH_FIELDS_TOP_STRICT=1` でエラー)。 -- Syntax Torture スイートの実行正規化(末行比較)。一部テスト本文を Nyash 仕様に合わせて修正。 -- JSON v0 仕様に Stage‑3 ノード(Break/Continue/Throw/Try)を追記。Parser Stage‑3 設計メモの現状/残課題を更新。 -- LLVM curated smokes を新設(`tools/smokes/curated_llvm.sh`)。core/async/loop/peek を10s/ケースで実行、PHI‑off も検証可(`--phi-off`)。 -- LLVM Stage‑3 受理スモークを追加(`tools/smokes/curated_llvm_stage3.sh`)。try/finally・dead throw を10s/ケースで実行。PHI‑off(`--phi-off`)/trap抑止(`NYASH_LLVM_TRAP_ON_THROW=0`)も確認。 -- 旧スモークは `tools/smokes/archive/` へ整理(JIT/Cranelift 系は当面対象外)。 -- Bridge/Builder に PHI 非生成モードを導入(`NYASH_MIR_NO_PHI=1`)。LLVM Resolver 合成と統一し、Verifier は緩和ゲート(`verify_allow_no_phi()`)を追加。 -- LLVM 側に PHI 合成トレースを追加(`NYASH_LLVM_TRACE_PHI=1`)。jump/finalize/resolve の観測を統一タグで出力。 -- LLVM Throw を最小降ろし(`llvm.trap`→`unreachable`、`NYASH_LLVM_TRAP_ON_THROW=0` で trap 抑止)。 -- 環境変数アクセスを `config::env` に集約(`mir_no_phi()`/`verify_allow_no_phi()`/`llvm_use_harness()`)。 -- dev プロファイル `tools/dev_env.sh phi_off` を追加。ルート清掃ユーティリティ `tools/clean_root_artifacts.sh` を追加。 -- CI(GH Actions)を curated LLVM(PHI‑on/off)実行に刷新。旧JITジョブは停止。 -- Verifier: verification.rs 内の compute_* ラッパーを撤去し、全て `verification::utils::*` を直参照に置換。 -- Parser: `bit_or/xor/and` と `equality/comparison/range/term/shift/factor` に加え、`call/primary` も `parser/expr/` へ分割。`expressions.rs` は委譲ラッパーのみに縮退(互換維持)。 -- Optimizer(BoxField): 同一ブロック内の set 直後の get(同一 box+index)を Copy に置換する軽量 peephole を追加(load-after-store 短絡)。 -- LLVM(select/terminators): `function.rs` から `instructions::term_emit_*` を利用しつつ、`normalize_branch_condition()` をブランチ直前で適用する流れを固定化(truthy 正規化の前段フック)。 -- Runner/env 集約: `src/config/env.rs` に CLI/自ホスト/VM まわりの getter を追加(`cli_verbose()/enable_using()/vm_use_py()/ny_compiler_*()` など)。`runner/selfhost.rs`/`runner/pipe_io.rs`/`runner/modes/common.rs` のホットパスを getter 参照に更新(段階導入)。 -- VM dispatch: 実装は既に dispatch 中央化済み(`backend::dispatch` 経由)。`NYASH_VM_USE_DISPATCH` フラグの getter を追加(将来の選択切替用)。 -- Runner/log: `runner/trace.rs` を追加。`cli_verbose()` と `cli_v!` マクロを導入し、`modes/common.rs` の一部情報ログを置換(verbose ガードの明確化)。 -- Selfhost helpers: PyVM ハーネス実行を `NyashRunner::run_pyvm_harness(..)` として共通化。EXE パーサ経路の JSON 抽出をまとめる `exe_try_parse_json_v0(..)` を追加(段階適用の足場)。 - -Refactor Progress (2025‑09‑16, end of day) -- Runner: ヘッダ指令スキャンとトレース出力を分離(`runner/cli_directives.rs`, `runner/trace.rs`)。using 解決ログを集約。 -- LLVM: terminators(select) の足場を追加し、呼び出しを alias 経由に切替(挙動不変)。 -- Optimizer: パス別に分割し、オーケストレータから委譲(挙動不変の足場)。 - - `optimizer_passes/{normalize,diagnostics,reorder,boxfield,intrinsics}.rs` - - 統計を `optimizer_stats.rs` へ分離。 -- Verifier: 主要チェックをモジュール化し、`verification.rs` を薄いオーケストレータ化。 - - `verification/{ssa,dom,cfg,barrier,legacy,awaits,utils}.rs` -- AST: `Span` を `ast/span.rs` へ分離し、`ast.rs` は re‑export。 -- Parser: expressions を段階分割(ternary/coalesce/logic を `parser/expr/*` へ)。 - -Remaining Refactors (Phase‑15 mainline) -- Verifier(仕上げ) - - `verification.rs` 内の `compute_*` ラッパーを完全撤去し、全呼び出しを `verification::utils` に集約。 - - テスト追加: reachability/phi/await チェックの簡易ケース(任意)。 -- Parser(段階分割の続き) - - `bit_or/bit_xor/bit_and`、`comparison/range/term/shift/factor` を `parser/expr/` へ移動し、`parse_expression` チェインを維持。 - - `call/primary` は最後に移動(依存が多いため)。 -- AST(構造の分離) - - `ast/nodes/{structure,expression,statement}.rs` へノード定義を分離し、`ast.rs` は `pub use` 集約のみへ縮退。 -- Optimizer(足場→実装へ) - - `reorder/boxfield/intrinsics` の実装を段階導入(まず small win: CSE のキーフィルタ改善、boxfield の load-after-store)。 - - `normalize` の terminator 側の補完(未移行箇所があれば寄せる)。 -- Runner/env 集約 - - ホットパスの環境参照を `config::env` getter へ置換(残件: VM trace/diagnostics の一部)。 -- Runner/log 整理(第2〜第3弾) - - 情報ログ(verbose 連動)を `cli_v!` に一本化。エラー系は従来通り eprintln のまま。 -- Selfhost EXE 経路の関数抽出(仕上げ) - - 既存の EXE‑first ブロックを `exe_try_parse_json_v0(..)` を用いた薄い分岐に縮退(挙動不変)。 -- LLVM select/terminators(実装化) - - `select` に truthy 規約の軽い正規化を追加(等価変換のみ)。 - - `terminators` へ実体移動(`flow` からの段階的差し替え)。 -- VM dispatch(段階導入) - - `NYASH_VM_USE_DISPATCH=1` フラグを導入し、無副作用命令から `backend/dispatch.rs` 経由に切替。 - -Notes -- すべて挙動等価の範囲で段階的に進める。足場化したモジュールは後続で実装を徐々に移す。 - -Self‑Hosting plumbing (2025‑09‑16, later in day) -- Runner: 自己ホスト経路で子プログラム(`apps/selfhost-compiler/compiler.nyash`)を優先実行し、`--read-tmp` 常時付与で安定運用に変更。 -- PyVM 優先の統一(`NYASH_VM_USE_PY=1`)は EXE/inline/child の全分岐で尊重。 -- CLI: `--stage3` を追加(`NYASH_NY_COMPILER_STAGE3=1` を設定)。 -- Script args パススルー(B案)実装: `nyash ... FILE -- arg1 arg2` → `NYASH_SCRIPT_ARGS_JSON=["arg1","arg2"]` として `Main.main(args)` に ArrayBox で注入。 - - Interpreter: `ArrayBox.of(...)` を追加して初期配列を簡潔に構築。 - - Runner: Python MVP は `NYASH_NY_COMPILER_SKIP_PY=1` でスキップ可能(自己ホスト子を優先)。 - - -Decision (Phase‑15 wrap‑up) -- MIR13 移行(PHI 非生成): Phase‑15 の締めとして、MIR 生成層(Bridge/Builder)は PHI を生成しない方針に切替。PHI 合成は LLVM 層(llvmlite/Resolver)に集約。 -- LoopForm は次フェーズ(MIR18)で導入: まずは MIR14 を維持し、次フェーズで `LoopHeader/Enter/Latch` 等の占位命令を追加。現行 Phase‑15 は CFG パターン検知でループ搬送値を合成。 -- 例外は段階導入: Throw/Catch は現行維持(Bridge ゲートで出力可)。Try/Finally の構造化は将来の TryRegion で検討。 - -Next Focus (Throw/Try — LLVM first) -- ブリッジ設計: `emit_degraded_throw` の差し替え方針を策定し、JSON v0 `Try` ノード → MIR 変換の仕様を決める(Stage-3 例外モデル)。 -- MIR Builder/Runtime 調査: Rust VM/PyVM の `ControlFlow::Throw` 経路と既存 TryCatch 降格の挙動を整理。必要に応じて docs と CURRENT_TASK に反映。 -- PyVM 設計: 例外モデルをどこまで Python 側に実装するか決め、最小テスト計画を用意。 -- LLVM 実装方針: Throw/Try の MIR 命令を LLVM 側がどう扱うか(panic扱い or fallback)を設計し、smoke 更新案を作る(現状 Throw は trap/unreachable 最小降ろし完了)。 -- テスト計画: JSON フィクスチャと `tools/llvm_smoke.sh` を中心に Stage-3 例外用のスモーク/単体テストを整備。 - -Open Items (handoff) -- Selfhost Stage‑3 E2E smoke(Runner→子→JSON→Bridge)の最終緑化 - - 現状: 子へ `--read-tmp` は付与済み。`--stage3` は env→子へ透過済み。 - - 直近 TODO: tools/selfhost_stage3_accept_smoke.sh を Runner 優先経路に再調整し、`NYASH_NY_COMPILER_SKIP_PY=1` 併用で安定化。 - - 期待値: try/finally/break/continue/throw(accept) で exit=0(degrade 経路) -- CLI argv 前処理の挙動監視 - - `--` なしの通常実行での Positional FILE 受理を再検証(Clapの警告再現時は build_command の宣言順/Arg要件を再点検)。 -- LLVM: Throw 実投げの終了コード方針を決定し、trap on/off の期待値を固定化。 - -Next Steps (suggested order) -1) Selfhost Stage‑3 E2E 緑化(smoke 修正 → 確認) -2) CLI FILE positional の回帰があれば即修正(Clap設定の微調整) -3) LLVM Throw 実投げの設計(終了コード/シグナル)+スモーク -4) Runner 実装の集約(modes/common → selfhost.rs)と微ノイズ削減 -5) CI に Selfhost Stage‑2/E2E(軽量)をオプションで追加 - -※ Cranelift/JIT 系は当面対象外。ビルド時も LLVM のみを有効化(JIT 関連 feature/CI は無視)。 - -Runner updates (2025‑09‑16) -- Selfhost pipeline: PyVM 優先(`NYASH_VM_USE_PY=1`)を全分岐で適用(EXE/inline/child 経路の一貫性)。 -- 重複関数の整理: `modes/common.rs::try_run_ny_compiler_pipeline` は `selfhost.rs::try_run_selfhost_pipeline` に委譲(ドリフト防止)。 -- Stage‑3 受理導線: `NYASH_NY_COMPILER_STAGE3=1` で子プロセスに `--stage3` を付与。inline フォールバックは `stage3_enable(1)` を既定有効化。 - -- llvmlite/AOT(本戦)強化 — コアコレクション配線とエントリ統一 - - Array/Map の BoxCall を NyRT ハンドルAPIに直結: - - Array: `push`→`nyash.array.push_h`、`length/len`→`nyash.any.length_h` - - Map: `set`→`nyash.map.set_hh`、`get`→`nyash.map.get_hh`、`has`→`nyash.map.has_hh`、`size`→`nyash.any.length_h` - - `ny_main` を i64 戻りに統一し、`Main.main/1` を優先(既定 args は `new ArrayBox()`)。 - - Core Box 生成の安定化:`nyash.array.birth_h` / `nyash.map.birth_h` を追加し、llvmlite `new` は birth_h を優先。 - - AOT 実行確認: - - `[1,2,3].length()` → `Result: 3` - - `{"name":"Alice","age":25}.size()` → `Result: 2` - - `m.has("name") ? m.get("name").length() : 0` → `Result: 5` - - -Quick Next (today) -- いよいよ「Nyash で書く」段階へ(Self‑Hosting 実装の着手): - 1) ParserBox 拡張(Stage‑2 の堅牢化・回帰修正)✅ Done 2025‑09‑16 - - bool/null リテラルと空 RHS(代入/return/local)を Int(0) フォールバックで正規化。 - - simple assignment → Local 正常化を `==` 判定と共に調整。 - - 三項演算子 `cond ? a : b` を `Ternary` ノードに正規化し、自走スモーク追加。 - 2) EmitterBox 拡張(JSON v0 の安定化)✅ Done 2025‑09‑16 - - `meta.usings` を常時出力(空は `[]`)。 - 3) Resolver/BoxIndex の prefix メタ反映 ✅ Done 2025‑09‑16 - - `plugin_meta_by_box` を構築し、`require_prefix` / `expose_short_names` を `resolve_using_target` へ適用。 - - `NYASH_PLUGIN_REQUIRE_PREFIX` が無効でも per-plugin meta で短名禁止を検知。 - 4) Parser Stage‑3 下地 ✅ Done 2025‑09‑16 - - `ParserBox.stage3_enable()` を追加し、Break/Continue/Throw/Try を JSON v0 に出力できるゲートを実装。 - - `--stage3` CLI フラグから ParserBox へ渡す導線を追加。 - - `docs/reference/architecture/parser_mvp_stage3.md` に Stage‑3 設計を記録。 - 5) 自己ホスト経路で Ny 実装切替のゲート準備(現状は Python MVP 優先を維持)。 - 6) テスト: - - `source tools/dev_env.sh pyvm` - - `NYASH_VM_USE_PY=1 ./tools/selfhost_stage2_smoke.sh` - - `NYASH_VM_USE_PY=1 ./tools/selfhost_stage2_bridge_smoke.sh` - - Torture(VM中心): `(cd tests/nyash_syntax_torture_20250916 && BACKENDS="vm" NYASH_BIN=../../target/release/nyash bash run_spec_smoke.sh)` - - LLVM Stage‑3 smoke (手動): `NYASH_LLVM_STAGE3_SMOKE=1 ./tools/llvm_smoke.sh release` -- Runner/Bridge 実行系 - - `--ny-parser-pipe` は `NYASH_PIPE_USE_PYVM=1` で PyVM に委譲(exit code 判定に統一)。 - - 自己ホスト JSON 生成は Python MVP を優先、LLVM EXE/インラインVMを段階フォールバック。 - - Runner 分割(第1弾〜第2弾): `dispatch.rs` へ backend 分岐を集約、`tasks.rs`/`build.rs`/`demos.rs` へ職責分離。`mod.rs` を薄型化。 -- LLVM Codegen リファクタ(第1弾〜第2弾) - - `codegen/utils.rs` を新設し `sanitize_symbol`/`build_const_str_map` を抽出。 - - `codegen/function.rs` を追加し `lower_one_function` を完全移管(呼び出しは `function::lower_one_function`)。 - - 旧レガシー断片コメントを除去して軽量化。機能・出力は不変。 -- MIR Builder 整理(小分割) - - `builder/vars.rs` を追加し、Lambda の自由変数収集ロジックを外出し。 - - 既存の `LoopBuilder`/`phi` 分割方針は維持(今後 small utils を `loops.rs` に抽出予定)。 +What Changed (recent) +- MIR13 default enabled + - `mir_no_phi()` default set to true (can disable via `NYASH_MIR_NO_PHI=0`). + - Curated LLVM runner defaults to PHI‑off; `--phi-on` enables MIR14 lane. + - Added doc: `docs/development/mir/MIR13_MODE.md`; README references it. +- JSON v0 Bridge lowering split (non‑functional) + - Added `src/runner/json_v0_bridge/lowering/{if_else.rs, loop_.rs, try_catch.rs, merge.rs}` and routed calls from `lowering.rs`. +- llvmlite stability for MIR13 + - Resolver: forbids cross‑block non‑dominating vmap reuse; for multi‑pred and no declared PHI, synthesizes a localization PHI at block head. + - Finalize remains function‑local; `block_end_values` snapshots and placeholder wiring still in place. +- Parity runner pragmatics + - `tools/pyvm_vs_llvmlite.sh` compares exit code by default; use `CMP_STRICT=1` for stdout+exit. Current Status -- Stage‑2: 自己ホスト → JSON v0 → PyVM の代表スモークは緑(配列/文字列/論理/if/loop)。 -- Stage‑3: 構文受理のみ完了(break/continue/throw/try/catch/finally)。現時点では JSON 降格(no‑op/Expr)で安全受理。 -- Runner: Using/Resolver を前処理に統合(BoxIndex/キャッシュ/strict)。`--ny-parser-pipe` は PyVM 委譲(exit code 判定)。 -- llvmlite/AOT: Array/Map の基本操作(push/get/set/has/size, length)が NyRT ハンドルAPIで動作。`ny_main` は i64 戻り・`Main.main/1` 優先で起動。 -- ログ: verbose 出力の一部を `cli_v!` で共通化(引き続き適用範囲を拡大中)。 +- Self‑hosting Bridge → PyVM smokes: PASS (Stage‑2 reps: array/string/logic/if/loop). +- Curated LLVM (PHI‑off default): PASS. +- Curated LLVM (PHI‑on experimental): `apps/tests/loop_if_phi.nyash` shows a dominance issue (observed; not blocking, MIR13 recommended). -Open -- Bridge/PHI の正規化: 短絡(入れ子)における merge/PHI incoming を固定化(rhs_end/fall_bb の順序)。 -- JSON v0 の拡張方針: break/continue/try/catch/finally の表現(受け皿設計 or 受理時の事前降下)。➡ `docs/reference/architecture/parser_mvp_stage3.md` -- per‑plugin meta の反映: `require_prefix/expose_short_names/prefix` を Resolver 挙動へ段階適用(導線は実装済み)。✅ 2025‑09‑16 prefix enforcement とテスト追加済み。 -- `me` の扱い: MVP は `NYASH_BRIDGE_ME_DUMMY=1` の仮注入を継続(将来撤去)。 -- LLVM 直結(任意): JSON v0 → LLVM の導線追加は後回し。 +Next (short plan) +1) JSON v0 lowering: split remaining helpers (peek/ternary/expr) without behavior change. +2) PHI‑on lane (optional): investigate `loop_if_phi` dominance by tightening finalize ordering and resolver materialization (low priority). +3) Runner refactor (small PRs): + - `selfhost/{child.rs,json.rs}` split; `modes/common/{io,resolve,exec}.rs` split; reduce `runner/mod.rs` surface. +4) Optimizer/Verifier thin‑hub cleanup (non‑functional): orchestrator minimalization and pass boundaries clarified. -- NyRT 整頓: - - FFI ヘルパー化(handles/boxing 正規化)/birth_h→new_i64x 統合/Core Box のプラグイン事前登録/FFI エクスポートのマクロ化。 -- llvmlite 整頓: - - boxcall のテーブル駆動化、追加 API(delete/keys/values など)の段階配線。 +How to Run +- PyVM reference smokes: `tools/pyvm_stage2_smoke.sh` +- Bridge → PyVM smokes: `tools/selfhost_stage2_bridge_smoke.sh` +- LLVM curated (PHI‑off default): `tools/smokes/curated_llvm.sh` +- LLVM PHI‑on (experimental): `tools/smokes/curated_llvm.sh --phi-on` +- Parity (AOT vs PyVM): `tools/pyvm_vs_llvmlite.sh ` (`CMP_STRICT=1` to enable stdout check) -Plan (to Self‑Hosting) -1) Phase‑1: Stage‑2 完了+堅牢化(今ここ) - - 正常系スモークを自己ホスト直/Bridge(PyVM)で常緑化(追加分を反映済み)。 - - 進捗ガードの継続検証(不完全入力セット)。 -2) Phase‑2: Bridge 短絡/PHI 固定+パリティ収束 - - 入れ子短絡の merge/PHI incoming を固定し、stdout 判定でスモークを緑化。 - - PyVM/llvmlite パリティを常時緑(代表ケースを exit code 判定へ統一)。 -3) Phase‑3: 構文受理の拡張(完了)→ Bootstrap c0→c1→c1’ - - 受理のみ: break/continue/throw/try-catch-finally(実行意味論は降格)。 - - emit‑only で c1 を生成→既存経路にフォールバック実行、正規化 JSON 差分で等価を確認。 - -How to Run (dev) -- 推奨環境: `source tools/dev_env.sh pyvm`(PyVM を既定。Bridge→PyVM 直送) -- 自己ホスト(子経路 ON): `NYASH_USE_NY_COMPILER=1` -- 安全弁: `NYASH_NY_COMPILER_TIMEOUT_MS=2000`、emit‑only 既定: `NYASH_NY_COMPILER_EMIT_ONLY=1` - -Smokes -- 無限ループ防止: `./tools/selfhost_progress_guard_smoke.sh` -- 自己ホスト → Interpreter(BoxCallなし集合): `./tools/selfhost_stage2_smoke.sh` -- 自己ホスト → JSON → PyVM(Array/String/Console 含む): `./tools/selfhost_stage2_bridge_smoke.sh` -- 動作確認(ローカル) - - VM: `./target/debug/nyash --backend vm apps/tmp_hello.nyash`(Result: 0) - - LLVM モック: `./target/debug/nyash --backend llvm apps/tmp_hello.nyash` - - PyVM/Stage‑2: `tools/pyvm_stage2_smoke.sh`(All PASS) - - Bridge/Stage‑2: `tools/ny_stage2_bridge_smoke.sh`(All PASS) +Key Flags +- `NYASH_MIR_NO_PHI` (default 1): PHI‑off when 1 (MIR13). Set `0` for PHI‑on. +- `NYASH_VERIFY_ALLOW_NO_PHI` (default 1): relax verifier for PHI‑less MIR. +- `NYASH_LLVM_USE_HARNESS=1`: route AOT through llvmlite harness. +- `NYASH_LLVM_TRACE_PHI=1`: trace PHI resolution/wiring. Notes / Policies -- PyVM は意味論の参照実行器として運用(exit code 判定を基本)。 -- Bridge は JSON v0 → MIR 降下で PHI を生成(Phase‑15 中は現行方式を維持)。 -- 配布/バンドル/EXE 化は任意の実験導線として維持(Phase‑15 の主目的外)。 +- Focus is self‑hosting stability. JIT/Cranelift is out of scope (safety fixes only). +- PHI generation remains centralized in llvmlite; Bridge/Builder keep PHI‑off by default. +- No full tracing GC yet; handles/Arc lifetimes govern object retention. Safepoint/barrier/roots are staging utilities. -Smoke Snapshot (2025‑09‑15) -- 修正: `runner/dispatch.rs` に `vm` 分岐が欠落しており `--backend vm` が interpreter にフォールバックしていたため、PyVM スモークが作動せず。分岐を追加して復旧済み。 -- PyVM Stage‑2 部分結果: - - PASS: string ops basic, me method call - - FAIL: loop/if/phi → 出力 `sum=4`(期待 `sum=9`) - - 原因分析: ループ内 if の merge で `sum` の Phi 正規化が入らず、latch 側スナップショットが else 系の一時値を優先(`16`)しうる構造。`LoopBuilder::build_statement(If)` が `normalize_if_else_phi` 相当を呼ばず、変数マップが φ 統合されていない。 - - 対応方針(最小修正): - - LoopBuilder の If 降下で merge 到達時に「両枝が同一変数に代入」の場合は `phi(dst=[then,else])` を emit→その φ を対象変数に bind。 - - latch スナップショットはこの φ 後の変数マップで採取する。 - - 代替(短期): Builder 側の `normalize_if_else_phi` を呼ぶ薄いフックを設けて流用。 - -Fixes Applied (2025‑09‑15) -- LoopBuilder If 降下に φ 正規化を追加(両枝代入の変数を merge 時に φ で束ねて再束縛)。 -- PyVM φ 解決ロジックを安定化(incoming を [value, pred] 形に限定し、[pred, value] の曖昧推測を削除)。偶然一致による誤選択を排除。 -- これにより `tools/pyvm_stage2_smoke.sh` は全 PASS を確認済み。 - -Refactor Candidates (early plan) -- runner/mod.rs(~70K chars): “runner pipeline” を用途別に分割(TODO #15) - - runner/pipeline.rs(入力正規化/using解決/環境注入) - - runner/pipe_io.rs(stdin/file の JSON v0 受理・整形) - - runner/selfhost.rs(自己ホスト EXE/VM/Python フォールバック、timeout/ログ含む) - - runner/dispatch.rs(backend 選択と実行、PyVM 委譲) - - 既存 json_v0_bridge/mir_json_emit は流用、mod.rs から薄いファサードに縮退。 -- backend/llvm/compiler/codegen(責務分割の継続) - - 済: utils 抽出、`lower_one_function` を `function.rs` へ移管。 - - 次: 終端系・選択系の薄層切り出し。 - - `instructions/terminators.rs`: return/jump/branch の分岐ドライバ(emit_* 呼び出し集約)。 - - `instructions/select.rs`: 条件・短絡・PHI 前処理(sealed-SSA 前提の前段正規化)。 - - 目標: `function.rs` の見通し改善(1関数=制御フロー骨格)、テスト容易化。 -- mir/builder.rs(ヘッダ~80行、全体~1K行) - - 既に多くを modules に分割済み。残る “variable/phi 合流”“loop ヘッダ/出口管理” を builder/loops.rs / builder/phi.rs に抽出。 - - 目標: 依存関係(utils/exprs/stmts)を維持したまま、1ファイル1責務を徹底。 - -Recommended Next (short list) -- LLVM Codegen(B 継続) - - `instructions/terminators.rs` を新設し、`function.rs` から終端分岐(return/jump/branch)を移譲。 - - `instructions/select.rs` を新設し、条件/短絡/PHI 前処理(sealed-SSA 前提の軽い正規化)を集約。 - - `function.rs` は「BB 周回+各 lowering 呼び出し」の骨格のみへ縮退。 -- MIR Builder(C 継続) - - `builder/loops.rs` を新設し、ループのヘッダ/出口の小物ユーティリティを抽出(`LoopBuilder` の補助レイヤ)。 - -Refactor Plan (MIR Core / Parser / Runner) - -- MIR Core(中〜高) - - 問題点 - - `optimizer.rs` と `verification.rs` に処理が集中し、追加パスや検証増に弱い構造(巨大化: 994/980 行規模)。 - - 提案 - - パス駆動に分割: `mir/passes/{dce.rs, ssa.rs, const_fold.rs, simplify.rs}` と `MirPass` トレイト(`run(&mut MirModule)`)。 - - 進捗: dce/cse 抽出済み、`MirPass` 骨格導入済み。次は normalize 系の抽出を段階実施。 - - `optimizer.rs` はパイプライン組立(順序・ゲート・Stats 集約)に縮小。 - - `verification.rs` もカテゴリ分割(ブロック整合性、SSA/PHI、型整合)。失敗メッセージ表現の一貫化(hintを統一)。 - -- Parser/Tokenizer(中) - - 問題点 - - `parser/expressions.rs`(~986行)、`parser/statements.rs`(~562行)、`tokenizer.rs`(~863行)が肥大。 - - 提案 - - Pratt/precedence テーブル化で演算子別分岐の重複削減。 - - 共通エラー生成ユーティリティで `expected(...)` メッセージを統一。 - - `tokenizer.rs` を `tokens.rs`(定義)と `lexer.rs`(実装)に分離。テストは `tests/lexer_*.rs` へ退避。 - -- Runner(中) - - 問題点 - - `runner/modes/common.rs`(~734行)、`runner/mod.rs`(~597行)に CLI/環境フラグ/実行分岐が混載。 - - 提案 - - `runner/modes/common/` ディレクトリ化し、CLI引数処理・環境フラグ解決・モードディスパッチを分離。 - - 重複ログ/検証を共通ヘルパへ集約。 - - - `builder/vars.rs` に SSA 変数正規化の小物を段階追加(変数名再束縛/スコープ終端の型ヒント伝搬など)。 -- Runner(仕上げ) - - `mod.rs` の残置ヘルパ(usingの候補提示・環境注入ログ)を `pipeline/dispatch` へ集約し、`mod.rs` を最小のオーケストレーションに。 - - Namespaces Phase‑1(実装着手): BoxIndex 構築・3段階解決・toml aliases・曖昧エラー改善・トレース - -Smoke Policy (Phase‑15) -- PyVM: 一部チェックのみ(async/nowait/await/GC/sync は対象外) -- LLVM: フル対応(llvmlite harness)。`tools/smokes/curated_llvm.sh [--phi-off]` を利用 -- JIT: 未整備(JIT向けスモークは `tools/smokes/archive/` に移管) - - Stage‑3 acceptance(Bridge 経路): `tools/ny_stage3_bridge_accept_smoke.sh`(Try/Break/Continue/Throw を JSON v0 で受理できることを確認) - -Next Phase — Selfhost Parser/Compiler in Nyash(着手準備完了) -- 目的: Nyash スクリプトで Parser/Emitter を実装し、Ny → JSON v0 → Bridge → MIR 実行の自己ホスト路線に移行。 -- ステップ(最小 MVP): - 1) `apps/selfhost-compiler/` に ParserBox/EmitterBox を Nyash で実装(Stage‑2 構文、JSON v0 出力)。 - 2) ランナーに `NYASH_USE_NY_COMPILER=1` ゲートを追加し、子プロセス/pipe で JSON v0 を受け取って Bridge→MIR 実行。 - 3) curated LLVM スモークの一部を自己ホスト経路で通す(PyVMは非対象、LLVMで検証)。 - 4) CI に自己ホスト最小ジョブを追加(timeout/静音運用、PHI‑on 既定)。 -- ガード/ポリシー: - - 既存 Rust Parser/Emitter はフォールバックとして保持(`NYASH_SKIP_TOML_ENV=1` で隔離可能)。 - - 仕様差が出た場合は LLVM 側の意味論に合わせて Nyash 実装を調整。 - -MIR13 Plan(Phase‑15 終盤) -- Bridge/Builder: PHI を生成しない(受理は維持)。If/Loop の合流は LLVM Resolver に任せる。 -- llvmlite: Resolver を使い、BB 先頭で PHI 合成。ループは preheader/cond/body の CFG から搬送値を復元(break は exit 側でマージ)。 -- Smoke: LLVM はまず loop‑only(break/continue)を常時緑化。例外系(throw/try)は IR 降ろし込み整備後に復帰。 -- 詳細設計: `docs/private/papers/paper-e-loop-signal-ir/mir-evolution-plan.md` に MIR14→MIR13→MIR17 の段階的移行計画を記載。 - -Array/Map Literals Plan(Syntax Sugar) -- Stage‑1: Array literal `[e1, e2, ...]` を実装(ゲート: `NYASH_SYNTAX_SUGAR_LEVEL=basic|full` または `NYASH_ENABLE_ARRAY_LITERAL=1`)。 - - Lowering: `new ArrayBox()` → 各要素を評価 → `.push(elem)` を左から右に順に発行 → 最後に配列値を返す。 - - 末尾カンマ許可。 - - スモーク: `apps/tests/array_literal_basic.nyash`(size/順序/副作用1回性)。 -- Stage‑2: Map literal `{ "k": v, ... }`(文字列キー限定)を実装(ゲート: `NYASH_SYNTAX_SUGAR_LEVEL=basic|full` or `NYASH_ENABLE_MAP_LITERAL=1`)。 - - Lowering: `new MapBox()` → 各ペアを評価 → `.set("k", v)` を左から右に順に発行 → 最後に map 値を返す。 - - 末尾カンマ許可。識別子キー糖 `{name: v}` は次フェーズ。 - - スモーク: `apps/tests/map_literal_basic.nyash`(size/get/順序検証)。 -- Stage‑3: 識別子キー糖 `{name: v}` と末尾カンマを強化(任意)。 - -Gates / Semantics -- 左から右で評価(一度だけ)。push/set 失敗は即時伝播(既存 BoxCall 規約に追従)。 -- IR 変更なし(BoxCall/MethodCall のみ)。将来 `with_capacity(n)` 最適化は任意で追加。 - -Decision Log (2025‑09‑15) -- Q: 警告削減(`ops_ext.rs` / `selfhost.rs`)を先にやる?それとも挙動スモークを先に回す? -- A: スモークを先に実施。理由は以下。 - - リファクタ直後は回帰検出を最優先(PyVM/自己ホスト/Bridge の3レーンで即座に検証)。 - - 警告削減は挙動非変化を原則とするが、微妙なスコープや保存スロットの触りが混入し得るため、先に“緑”を固める。 -Namespaces / Using(現状) -- 解決順(決定性): 1) ローカル/コア → 2) エイリアス(nyash.toml/env)→ 3) 相対/using.paths → 4) プラグイン(短名/qualified) -- 曖昧時はエラー+候補提示(qualified または alias を要求)。 -- モード切替: Relaxed(既定)/Strict(`NYASH_PLUGIN_REQUIRE_PREFIX=1` または toml `[plugins] require_prefix=true`) -- needs 糖衣は using の同義(Runner で alias 登録)。 -- Plugins は統合名前空間。qualified `network.HttpClient` 常時許可。 -- nyash.toml(MVP): `[aliases]`/`[plugins]`(グローバル `require_prefix` のみ反映。per‑plugin は導線のみ) -- Index とキャッシュ(Runner): - - BoxIndex(グローバル): `plugin_boxes`, `aliases` を保持。plugins init 後に構築。 - - Resolve Cache(グローバル): `tgt|base|strict|paths` キーで再解決回避。 - - `NYASH_RESOLVE_TRACE=1`: 解決手順/キャッシュヒット/未解決候補をログ出力。 - - - スモークが緑=基礎健全性確認後に、静的ノイズの除去を安全に一気通貫で行う。 - -**AOT Quick** -- Array literal: `NYASH_SYNTAX_SUGAR_LEVEL=basic ./tools/build_llvm.sh tmp/aot_array_literal_main.nyash -o app && ./app` -- Map literal: `NYASH_SYNTAX_SUGAR_LEVEL=basic NYASH_ENABLE_MAP_LITERAL=1 ./tools/build_llvm.sh tmp/aot_map_literal_main.nyash -o app && ./app` - -Refactoring Plan (Phase‑15 follow‑up) -- 背景: 巨大ファイル(AST/MIR/LLVM)が保守コスト増の主因。機能非変化の小刻みリファクタで視認性と変更容易性を上げる。 - -- 目的と範囲(非機能変更・段階適用) - 1) AST(src/ast.rs:1): 定義とヘルパの分離、将来のサブenum活用導線の整備(現行API互換)。 - 2) MIR Builder(src/mir/builder.rs:1): build_module の骨格化・責務分離、既存分割(builder/*)の徹底。 - 3) Python LLVM(src/llvm_py/llvm_builder.py:1): lower_function の前処理/本体分離、lower_block の責務拡張。 - 4) MIR 命令(src/mir/instruction.rs:1): 構造体+トレイト導線の導入(enum への委譲を維持した非破壊移行)。 - -- 実施順(小PR単位、CI緑維持) - PR‑1: AST utils 抽出(非破壊) - - 追加: `src/ast/utils.rs` に classify/info/span/to_string などのヘルパを移設。 - - `src/ast.rs` は ASTNode/StructureNode/ExpressionNode/StatementNode の定義中心に縮退。 - - 互換維持: `pub use ast::utils::*;` で既存呼び出しを壊さない。 - - 受入: 全ビルド/スモーク緑、差分はファイル移動のみ。 - - PR‑2: MIR Builder build_module 分割(非破壊) - - `build_module` を `prepare_module`/`lower_root`/`finalize_module` に3分割。 - - 型推定(value_types→返り値)は finalize 側へ集約(現行ロジック移設)。 - - 既存の exprs/stmts などの委譲を明示し、build_module 本体を「骨格のみ」に縮退。 - - 受入: LLVM/PyVM/Bridge スモーク緑(挙動非変化)。 - - PR‑3: Python LLVM lower_function の前処理抽出 - - 新設: `setup_phi_placeholders()` を導入し、PHI 宣言/CFG 依存前処理をここへ移設。 - - `lower_block()` に snapshot(block_end_values 収集)までの責務を移動。メインループは薄い周回に。 - - 受入: `tools/smokes/curated_llvm.sh` / `curated_llvm_stage3.sh` 緑。 - - PR‑4: AST ラッパー導入(非破壊導線) - - 追加: `src/ast/nodes.rs` に小さな構造体群(Assign/Return/...)。 - - enum `StatementNode/ExpressionNode` を構造体で保持。`From for ASTNode` / `TryFrom for T` を提供。 - - Builder 入口(`builder/stmts.rs`, `builder/exprs.rs`)で ASTNode → TryFrom 変換を 1 行追加し以降はサブ型で match。 - - 受入: ビルド/スモーク緑(機能非変化)。 - - PR‑5: MIR 命令トレイト POC(Const/BinOp) - - 追加: `src/mir/instruction_kinds/` に `const_.rs`, `binop.rs`(各命令を struct 化)。 - - 共通トレイト `InstructionMeta { effects(), dst(), used() }` を定義。 - - `MirInstruction::{effects,dst_value,used_values}` から一部を構造体 impl に委譲(match 縮退の礎)。 - - 受入: スモーク緑、`instruction_introspection.rs` の挙動非変化。 - -- リスクとガード - - 機能非変化を原則(挙動差分は不可)。 - - CI で LLVM/Bridge を優先確認。Selfhost/E2E は任意ジョブで回す。 - - PR は 400 行未満/ファイル移動中心を目安に分割。 - -- 参考ファイル - - AST: `src/ast.rs`, (新規)`src/ast/utils.rs`, (将来)`src/ast/nodes.rs` - - Builder: `src/mir/builder.rs`, `src/mir/builder/*` - - Python LLVM: `src/llvm_py/llvm_builder.py` - - MIR 命令: `src/mir/instruction.rs`, (新規)`src/mir/instruction_kinds/*` - -Acceptance Criteria -- すべての変更は機能非変化(スモーク/CI 緑)。 -- 大型関数・巨大match の見通しが改善し、追従点が局所化。 -- 新規追加の導線(AST サブ型/命令トレイト)は既存 API と共存し、段階移行を可能にする。 diff --git a/CURRENT_TASK_restored.md b/CURRENT_TASK_restored.md index f8071d43..6bc4dfcd 100644 --- a/CURRENT_TASK_restored.md +++ b/CURRENT_TASK_restored.md @@ -103,7 +103,7 @@ TODO(bitops) - 走査: コメント(`//`, `#`)・文字列内の `include` を無視する状態機械を導入(誤検出抑制)。 - サンプル: `apps/selfhost/smokes/dep_smoke_root.nyash`(子: `dep_smoke_child.nyash`)。 -出力仕様・受け入れ基準: docs/selfhost/dep_tree_min_string.md に移設(CURRENT_TASKは要点のみ表記)。 +出力仕様・受け入れ基準: docs/development/current/selfhost/dep_tree_min_string.md に移設(CURRENT_TASKは要点のみ表記)。 残タスク(Phase 0 必須) - P0-2: スモーク(循環あり/なし)と合わせて確認(追加済み)。 @@ -439,9 +439,9 @@ Phase A 進捗(実施済) - Phase 12 クローズアウト完了。言語糖衣(12.7-B/P0)と VM 分割は反映済み。 - Phase 15(Self-Hosting: Cranelift AOT)へフォーカス移行。 - 設計/仕様ドキュメントとスモーク雛形を追加済み。 - - 設計: `docs/backend-cranelift-aot-design.md` - - API案: `docs/interfaces/cranelift-aot-box.md` - - LinkerBox: `docs/interfaces/linker-box.md` + - 設計: `docs/design/backend-cranelift-aot-design.md` + - API案: `docs/design/cranelift-aot-box.md` + - LinkerBox: `docs/design/linker-box.md` - スモーク仕様: `docs/tests/aot_smoke_cranelift.md` - 雛形スクリプト: `tools/aot_smoke_cranelift.sh`, `tools/aot_smoke_cranelift.ps1` - README にセルフホスト到達の道筋を明記(C ABI を Box 化)。 @@ -848,7 +848,7 @@ Phase A 進捗(実施済) - 参照 - Phase 15 概要/ロードマップ: `docs/development/roadmap/phases/phase-15/README.md`, `docs/development/roadmap/phases/phase-15/ROADMAP.md` - ハンドオフ: `docs/handoff/phase-15-handoff.md` - - 設計/API: `docs/backend-cranelift-aot-design.md`, `docs/interfaces/*` + - 設計/API: `docs/design/backend-cranelift-aot-design.md`, `docs/design/*` ■ 合否基準(P0: Ny→MIR→MIR-Interp→VM 最小成立) - 自作Nyashパーサ(最小サブセット)が Nyash で動作し、テスト入力から中間形式(JSON暫定)を生成できる。 diff --git a/Cargo.toml b/Cargo.toml index 9a07c964..1251ce03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,8 @@ categories = ["development-tools::parsing", "interpreters"] # Default features - minimal CLI only [features] -default = ["cli", "plugins"] +default = ["cli", "plugins", "interpreter-legacy"] +interpreter-legacy = [] e2e = [] cli = [] plugins-only = [] diff --git a/README.ja.md b/README.ja.md index 49dca92f..4278864e 100644 --- a/README.ja.md +++ b/README.ja.md @@ -15,7 +15,7 @@ --- 開発者向けクイックスタート: `docs/DEV_QUICKSTART.md` -セルフホスト1枚ガイド: `docs/self-hosting.md` +セルフホスト1枚ガイド: `docs/how-to/self-hosting.md` ## 目次 - [Self-Hosting(自己ホスト開発)](#self-hosting) @@ -23,7 +23,7 @@ ## 🧪 Self-Hosting(自己ホスト開発) -- ガイド: `docs/self-hosting.md` +- ガイド: `docs/how-to/self-hosting.md` - 最小E2E: `NYASH_DISABLE_PLUGINS=1 ./target/release/nyash --backend vm apps/selfhost-minimal/main.nyash` - スモーク: `bash tools/jit_smoke.sh` / `bash tools/selfhost_vm_smoke.sh` - Makefile: `make run-minimal`, `make smoke-selfhost` diff --git a/README.md b/README.md index c27e5163..16bbe6e5 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ --- Developer quickstart: see `docs/DEV_QUICKSTART.md`. Changelog highlights: `CHANGELOG.md`. -Self‑hosting one‑pager: `docs/self-hosting.md`. +MIR mode note: default is MIR13 (PHI-off). See `docs/development/mir/MIR13_MODE.md`. +Self‑hosting one‑pager: `docs/how-to/self-hosting.md`. ## Table of Contents - [Self‑Hosting (Dev Focus)](#self-hosting) @@ -23,7 +24,7 @@ Self‑hosting one‑pager: `docs/self-hosting.md`. ## 🧪 Self‑Hosting (Dev Focus) -- Guide: `docs/self-hosting.md` +- Guide: `docs/how-to/self-hosting.md` - Minimal E2E: `NYASH_DISABLE_PLUGINS=1 ./target/release/nyash --backend vm apps/selfhost-minimal/main.nyash` - Smokes: `bash tools/jit_smoke.sh` / `bash tools/selfhost_vm_smoke.sh` - Makefile: `make run-minimal`, `make smoke-selfhost` diff --git a/REFACTORING_ANALYSIS_REPORT.md b/REFACTORING_ANALYSIS_REPORT.md index 9f958e9b..91a853fb 100644 --- a/REFACTORING_ANALYSIS_REPORT.md +++ b/REFACTORING_ANALYSIS_REPORT.md @@ -178,4 +178,4 @@ The analysis reveals a well-architected system that would benefit from tactical Phase 15 Addendum (Mainline only) - Implemented: extracted CLI directives scanning and fields-top lint into `src/runner/cli_directives.rs` to slim `src/runner/mod.rs` without behavior changes. -- Proposed next steps (non-JIT): see `docs/refactoring/candidates_phase15.md` for focused items on Runner/LLVM/VM. +- Proposed next steps (non-JIT): see `docs/development/refactoring/candidates_phase15.md` for focused items on Runner/LLVM/VM. diff --git a/app_pyvm_cmp b/app_pyvm_cmp new file mode 100644 index 00000000..a0f21067 Binary files /dev/null and b/app_pyvm_cmp differ diff --git a/dev/selfhosting/README.md b/dev/selfhosting/README.md index 1ab3c75d..45c08982 100644 --- a/dev/selfhosting/README.md +++ b/dev/selfhosting/README.md @@ -19,7 +19,7 @@ Quickstart Docs -- One‑page guide: `docs/self-hosting.md` +- One‑page guide: `docs/how-to/self-hosting.md` Flags diff --git a/docs/DEV_QUICKSTART.md b/docs/DEV_QUICKSTART.md index ef0d3813..a3221d3e 100644 --- a/docs/DEV_QUICKSTART.md +++ b/docs/DEV_QUICKSTART.md @@ -3,7 +3,7 @@ This quickstart summarizes the most common build/run/test flows when working on Nyash. See also -- Self‑hosting one‑pager: `docs/self-hosting.md` +- Self‑hosting one‑pager: `docs/how-to/self-hosting.md` ## Build - VM/JIT (Cranelift): `cargo build --release --features cranelift-jit` diff --git a/docs/README.md b/docs/README.md index ddee5654..8e0d1d3c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,7 +6,7 @@ --- -## 📂 新しいドキュメント構造(2025年8月20日再編成) +## 📂 ドキュメント構造(指針) ### 📖 [reference/](reference/) - 正式な技術仕様 - **language/** - 言語仕様(構文、型システム、Box仕様) @@ -22,6 +22,9 @@ - **examples/** - 実践的なサンプルコード - **wasm-guide/** - WebAssemblyビルドガイド +### 🧩 [how-to/](how-to/) - 目的別ハウツー +- 手順重視の短いガイド(前提→コマンド→検証) + ### 🔧 [development/](development/) - 開発者向け - **current/** - 現在進行中のタスク(CURRENT_TASK.md等) - **roadmap/** - 開発計画 @@ -54,6 +57,9 @@ - [実行バックエンド](reference/architecture/execution-backends.md) - [プラグインシステム](reference/plugin-system/) - [CLIオプション早見表](tools/cli-options.md) + +### デザイン +- [設計ノート(入口)](design/) ### 開発状況 - [現在のタスク](../CURRENT_TASK.md) @@ -63,7 +69,7 @@ --- -## 📋 再編成について +## 📋 再編成について / フォルダの見分け方 ドキュメントは2025年8月20日に再編成されました。詳細は[REORGANIZATION_REPORT.md](REORGANIZATION_REPORT.md)を参照してください。 旧パスから新パスへの主な変更: @@ -73,4 +79,10 @@ --- +補足: +- `reference/` は正本(仕様)。 +- `guides/` は読み物、`how-to/` は手順書。 +- `design/` は公開できる設計ノート。 +- `private/` は下書き保管庫(将来 `reference/`/`design/` に昇格)。 + Nyash は「Everything is Box」哲学に基づく言語です。詳細はコア概念とガイドを参照してください。 diff --git a/docs/archive/README.md b/docs/archive/README.md index 84beb0dd..19a165e5 100644 --- a/docs/archive/README.md +++ b/docs/archive/README.md @@ -1,42 +1,14 @@ -# Nyash Archive 🗄️ +# Nyash Docs Archive -過去のドキュメント、設計決定、相談記録のアーカイブです。 +This folder stores deprecated or historical documents. Links from active docs should not point here. -## 📂 ディレクトリ構造 +When moving files into `archive/`: +- Add a brief note at the top explaining why it was archived and the new canonical location if any. +- Do not update content except light headers; avoid drifting from the canonical reference. -### consultations/ -AI相談記録の保管場所 -- **gemini/** - Gemini AIとの相談記録 -- **chatgpt/** - ChatGPTとの相談記録 -- **codex/** - Codexの解析結果 +When searching for current information, prefer: +- `docs/reference/` (specifications) +- `docs/guides/` (user guides) +- `docs/how-to/` (task‑oriented steps) +- `docs/design/` (stable architecture notes) -### decisions/ -過去の設計決定とその理由 -- アーキテクチャ決定記録(ADR) -- 廃止された機能の説明 -- 設計変更の経緯 - -### build-logs/ -ビルドログとベンチマーク結果 -- パフォーマンス測定記録 -- ビルドエラーの履歴 -- 最適化の記録 - -### old-versions/ -古いバージョンのドキュメント -- 廃止された仕様 -- 以前のガイドやチュートリアル -- 旧バージョンのREADME - -### generated/ -自動生成されたドキュメント -- MIRダンプ -- ベンチマーク結果 -- コード解析レポート - -## ⚠️ 注意事項 -このディレクトリの内容は歴史的参照用です。重複・旧版の資料が含まれます。 -最新の計画・仕様は以下を参照してください: -- 現行の計画(PLAN): `docs/development/roadmap/phases/phase-11.8_mir_cleanup/PLAN.md` -- 技術仕様(TECHNICAL_SPEC): `docs/development/roadmap/phases/phase-11.8_mir_cleanup/TECHNICAL_SPEC.md` -- そのほか: `/reference/`(リファレンス), `/guides/`(利用ガイド), `/development/`(開発状況) diff --git a/docs/archives/CURRENT_TASK-2025-09-06.md b/docs/archives/CURRENT_TASK-2025-09-06.md deleted file mode 100644 index 861d8389..00000000 --- a/docs/archives/CURRENT_TASK-2025-09-06.md +++ /dev/null @@ -1,9 +0,0 @@ -# CURRENT TASK — アーカイブ(2025‑09‑06) - -このファイルは `CURRENT_TASK.md` の旧来の完全版をそのまま保存するアーカイブです。 -Cranelift/AOT/JIT‑AOT 関連の詳細は今後 `docs/phase-15/cranelift/CRANELIFT_TASKS.md` 側で更新します。 - -注意: ここに記載のコマンドやパスは当時点のものです。最新の手順や方針は `CURRENT_TASK.md` と phase‑15 配下のドキュメントを参照してください。 - -(元の内容は Git 履歴から参照してください) - diff --git a/docs/debug_reports/README.md b/docs/debug_reports/README.md deleted file mode 100644 index c2177c92..00000000 --- a/docs/debug_reports/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Debug Reports - -ChatGPT5さんへの調査依頼レポート集です。 - -## 最新レポート(2025-09-11) - -### 1. chatgpt5_debug_request.md 🎯 **最重要** -プラグイン戻り値表示バグの根本原因を特定した詳細レポート: -- 問題の流れを完全に追跡 -- nyrt::console.log_handleでの問題箇所を特定 -- デバッグ提案を含む - -### 2. chatgpt5_llvm_string_concat_bug.md -文字列連結バグも含む包括的なレポート: -- MIRでの型推論ミス(String + Integer → Integer) -- LLVMエラーの詳細 - -## テストファイル -`local_tests/test_plugin_*.nyash` - バグ再現用のテストファイル - -## 使用方法 -これらのレポートをChatGPT5に提示して、問題の修正を依頼してください。 \ No newline at end of file diff --git a/docs/debug_reports/chatgpt5_debug_request.md b/docs/debug_reports/chatgpt5_debug_request.md deleted file mode 100644 index 8cedffe7..00000000 --- a/docs/debug_reports/chatgpt5_debug_request.md +++ /dev/null @@ -1,66 +0,0 @@ -# ChatGPT5さんへ:プラグイン戻り値表示バグ詳細調査レポート - -## 🎯 根本原因特定完了 - -### 問題の流れ -1. **プラグイン戻り値取得** ✅ 正常 - - `CounterBox.get()` → 整数2が返される - - L1016でvmapに生のi64値として格納 - -2. **LLVM console.log呼び出し** ✅ 正常 - - L1270で`nyash.console.log_handle(生のi64値)`呼び出し - -3. **nyrt処理** ❌ **ここに問題** - ```rust - // crates/nyrt/src/lib.rs:2391-2401 - pub extern "C" fn nyash_console_log_handle(handle: i64) -> i64 { - if let Some(obj) = handles::get(handle as u64) { // ← 生の値2でハンドル検索→失敗 - let s = obj.to_string_box().value; - println!("{}", s); - } else { - println!("{}", handle); // ← なぜか空白表示される - } - } - ``` - -### 疑問点 -**なぜ`println!("{}", handle)`が空白になるのか?** -- handleには実際の値(2)が入っているはず -- なぜprintln!が何も出力しない? - -## 🔍 必要な修正案 - -### 提案1: デバッグ出力追加 -```rust -pub extern "C" fn nyash_console_log_handle(handle: i64) -> i64 { - eprintln!("DEBUG: handle={}", handle); // デバッグ出力 - if let Some(obj) = handles::get(handle as u64) { - let s = obj.to_string_box().value; - println!("{}", s); - } else { - eprintln!("DEBUG: handle {} not found in registry", handle); - println!("{}", handle); // 元のコード - } - 0 -} -``` - -### 提案2: 生の整数値対応 -```rust -pub extern "C" fn nyash_console_log_handle(handle: i64) -> i64 { - if let Some(obj) = handles::get(handle as u64) { - let s = obj.to_string_box().value; - println!("{}", s); - } else { - // プラグイン戻り値の生の整数を直接表示 - println!("{}", handle); - eprintln!("DEBUG: Printed raw value {}", handle); - } - 0 -} -``` - -## 🐛 追加問題: 文字列連結 -MIRで`"result is: " + 2`が`String + Integer → Integer`と間違った型推論 - -お手数ですが、調査をお願いします! \ No newline at end of file diff --git a/docs/debug_reports/chatgpt5_llvm_string_concat_bug.md b/docs/debug_reports/chatgpt5_llvm_string_concat_bug.md deleted file mode 100644 index 5b00adf1..00000000 --- a/docs/debug_reports/chatgpt5_llvm_string_concat_bug.md +++ /dev/null @@ -1,99 +0,0 @@ -# ChatGPT5さんへ:LLVM文字列連結バグ - -## 問題 -文字列と整数の連結でLLVMエラーが発生します。 - -## エラーメッセージ -``` -❌ LLVM execution error: binop lhs %4 not integer -``` - -## テストコード -```nyash -local c = new CounterBox() -c.inc() -c.inc() -local result = c.get() -print("result is: " + result) // ← ここでエラー -print(result) -``` - -## MIR出力(問題箇所) -```mir -4: %3: Integer = call %0.get() // プラグイン戻り値(整数) -5: %4: String = const "result is: " // 文字列定数 -6: %5: Integer = %4 Add %3 // ❌ 型が間違い!String + Integer なのに結果がInteger -7: extern_call env.console.log(%5) -``` - -## 期待される動作 -- `String + Integer` → `String` (文字列連結) -- または専用の文字列連結命令が必要 - -## 関連ファイル -1. `src/backend/llvm/compiler/real.rs` - BinaryOp処理 -2. `src/mir/builder/ops.rs` - MIRビルダーのBinaryOp処理 - -## 参考:通常の整数表示は正常 -```mir -9: %6: Integer = const 42 -13: extern_call env.console.log(%6) // ← これは正常に "42" と表示される -``` - -プラグイン戻り値自体は正しく取得できているが、文字列連結の型処理に問題があるようです。 - -## 追加情報:プラグイン戻り値も空白のまま - -### シンプルなテストで確認 -```nyash -local c = new CounterBox() -c.inc() -c.inc() -print(c.get()) // 空白が表示される(何も出力されない) -``` - -### MIR出力 -```mir -4: %3: Integer = call %0.get() -5: extern_call env.console.log(%3) [effects: pure|io] -``` - -MIRには正しく`Integer`型が付いているのに、実行時は空白表示。 - -## 根本原因判明!🎯 - -### 問題1: プラグイン戻り値表示バグ - -**L1016**: プラグイン戻り値(整数)は正しくvmapに格納される -```rust -crate::mir::MirType::Integer => { - vmap.insert(*d, rv); // ← 生の i64 値が入る -} -``` - -**L1214-1270**: console.logは生の i64 値を受け取る -```rust -let av = *vmap.get(&args[0]).ok_or("extern arg missing")?; -// av = 生の i64 値(例: 2) -``` - -**L2391-2401**: nyrt::console.log_handleの問題箇所 -```rust -pub extern "C" fn nyash_console_log_handle(handle: i64) -> i64 { - if let Some(obj) = handles::get(handle as u64) { // ← handles::get(2) → None - let s = obj.to_string_box().value; - println!("{}", s); - } else { - println!("{}", handle); // ← ここでhandle=2が表示されるはず - } -} -``` - -**疑問**: なぜ`println!("{}", handle)`が空白になるのか? - -### 問題2: 文字列連結バグ -MIRで`String + Integer → Integer`という間違った型推論が発生 - -## 調査が必要 -1. handleの実際の値をログ出力で確認 -2. println!が実際に呼ばれているか確認 \ No newline at end of file diff --git a/docs/ARCHITECTURE.md b/docs/design/ARCHITECTURE.md similarity index 100% rename from docs/ARCHITECTURE.md rename to docs/design/ARCHITECTURE.md diff --git a/docs/LLVM_LAYER_OVERVIEW.md b/docs/design/LLVM_LAYER_OVERVIEW.md similarity index 99% rename from docs/LLVM_LAYER_OVERVIEW.md rename to docs/design/LLVM_LAYER_OVERVIEW.md index b1a6dcdc..fdd273c3 100644 --- a/docs/LLVM_LAYER_OVERVIEW.md +++ b/docs/design/LLVM_LAYER_OVERVIEW.md @@ -34,3 +34,4 @@ References - LOWERING_LLVM.md — lowering rules and runtime calls - RESOLVER_API.md — Resolver design and usage - LLVM_HARNESS.md — llvmlite harness interface and usage + diff --git a/docs/LOWERING_CONTEXTS.md b/docs/design/LOWERING_CONTEXTS.md similarity index 100% rename from docs/LOWERING_CONTEXTS.md rename to docs/design/LOWERING_CONTEXTS.md diff --git a/docs/LOWERING_LLVM.md b/docs/design/LOWERING_LLVM.md similarity index 100% rename from docs/LOWERING_LLVM.md rename to docs/design/LOWERING_LLVM.md diff --git a/docs/design/README.md b/docs/design/README.md new file mode 100644 index 00000000..abf9c898 --- /dev/null +++ b/docs/design/README.md @@ -0,0 +1,13 @@ +# Nyash Design Notes + +Public, stable design documents and architecture explanations. + +Use for rationale, trade‑offs, and diagrams that are safe to cite. + +Contents to consolidate here: +- Architecture overviews derived from ARCHITECTURE.md +- Backend design (LLVM/Cranelift) summaries +- MIR/IR evolution notes that are not drafts + +Draft, exploratory, or long‑form papers should remain under `docs/private/` until finalized. + diff --git a/docs/RESOLVER_API.md b/docs/design/RESOLVER_API.md similarity index 100% rename from docs/RESOLVER_API.md rename to docs/design/RESOLVER_API.md diff --git a/docs/specs/aot_plan_v1.md b/docs/design/aot-plan-v1.md similarity index 73% rename from docs/specs/aot_plan_v1.md rename to docs/design/aot-plan-v1.md index 903660d9..8d9fecea 100644 --- a/docs/specs/aot_plan_v1.md +++ b/docs/design/aot-plan-v1.md @@ -1,29 +1,29 @@ # AOT-Plan v1 Schema (Phase 15.1) -Status: draft-frozen for Phase 15.1 (extensions via `extensions` object only) +Status: draft-frozen for Phase 15.1 (extensions via `extensions` only) - version: string, must be "1" - name: optional plan/module name - functions: array of PlanFunction -- externs: optional array of extern identifiers (reserved; not required in 15.1) -- exports: optional array of export names (reserved) -- units: optional array of link units (reserved) +- externs: optional array (reserved; not required in 15.1) +- exports: optional array (reserved) +- units: optional array (reserved) - extensions: optional object for forward-compatible keys -PlanFunction: +PlanFunction - name: string - params: array of { name: string, type?: string } (informational in 15.1) - return_type: optional string; one of: integer, float, bool, string, void (or omitted → Unknown) -- body: optional object with tagged `kind` - - kind = "const_return": { value: any-json (int/bool/float/string supported) } - - kind = "empty": returns default 0 with Unknown type (Phase 15.1 importer behavior) +- body: optional tagged object + - kind = "const_return": { value: any-json (int/bool/float/string) } + - kind = "empty": returns default 0 with Unknown type (15.1 importer behavior) -Notes: +Notes - 15.1 importer does not emit object code; it constructs MIR13 skeletons only. - If `return_type` is omitted, importer uses Unknown to keep VM dynamic display. - `extensions` is a free-form map; the importer ignores unknown keys. -Example: +Example ``` { "version": "1", diff --git a/docs/backend-cranelift-aot-design.md b/docs/design/backend-cranelift-aot-design.md similarity index 100% rename from docs/backend-cranelift-aot-design.md rename to docs/design/backend-cranelift-aot-design.md diff --git a/docs/backend-llvm-implementation-guide.md b/docs/design/backend-llvm-implementation-guide.md similarity index 100% rename from docs/backend-llvm-implementation-guide.md rename to docs/design/backend-llvm-implementation-guide.md diff --git a/docs/interfaces/cranelift-aot-box.md b/docs/design/cranelift-aot-box.md similarity index 100% rename from docs/interfaces/cranelift-aot-box.md rename to docs/design/cranelift-aot-box.md diff --git a/docs/interfaces/linker-box.md b/docs/design/linker-box.md similarity index 100% rename from docs/interfaces/linker-box.md rename to docs/design/linker-box.md diff --git a/docs/current_task/llvm/00-Overview.md b/docs/development/current/llvm/00-Overview.md similarity index 100% rename from docs/current_task/llvm/00-Overview.md rename to docs/development/current/llvm/00-Overview.md diff --git a/docs/current_task/llvm/10-Now.md b/docs/development/current/llvm/10-Now.md similarity index 100% rename from docs/current_task/llvm/10-Now.md rename to docs/development/current/llvm/10-Now.md diff --git a/docs/current_task/llvm/20-Decisions.md b/docs/development/current/llvm/20-Decisions.md similarity index 100% rename from docs/current_task/llvm/20-Decisions.md rename to docs/development/current/llvm/20-Decisions.md diff --git a/docs/current_task/llvm/30-Backlog.md b/docs/development/current/llvm/30-Backlog.md similarity index 100% rename from docs/current_task/llvm/30-Backlog.md rename to docs/development/current/llvm/30-Backlog.md diff --git a/docs/current_task/main/00-Overview.md b/docs/development/current/main/00-Overview.md similarity index 100% rename from docs/current_task/main/00-Overview.md rename to docs/development/current/main/00-Overview.md diff --git a/docs/current_task/main/10-Now.md b/docs/development/current/main/10-Now.md similarity index 100% rename from docs/current_task/main/10-Now.md rename to docs/development/current/main/10-Now.md diff --git a/docs/current_task/main/20-Decisions.md b/docs/development/current/main/20-Decisions.md similarity index 100% rename from docs/current_task/main/20-Decisions.md rename to docs/development/current/main/20-Decisions.md diff --git a/docs/current_task/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md similarity index 100% rename from docs/current_task/main/30-Backlog.md rename to docs/development/current/main/30-Backlog.md diff --git a/docs/current_task/self_current_task/00-Overview.md b/docs/development/current/self_current_task/00-Overview.md similarity index 100% rename from docs/current_task/self_current_task/00-Overview.md rename to docs/development/current/self_current_task/00-Overview.md diff --git a/docs/current_task/self_current_task/10-Now.md b/docs/development/current/self_current_task/10-Now.md similarity index 100% rename from docs/current_task/self_current_task/10-Now.md rename to docs/development/current/self_current_task/10-Now.md diff --git a/docs/current_task/self_current_task/20-Decisions.md b/docs/development/current/self_current_task/20-Decisions.md similarity index 100% rename from docs/current_task/self_current_task/20-Decisions.md rename to docs/development/current/self_current_task/20-Decisions.md diff --git a/docs/current_task/self_current_task/30-Backlog.md b/docs/development/current/self_current_task/30-Backlog.md similarity index 100% rename from docs/current_task/self_current_task/30-Backlog.md rename to docs/development/current/self_current_task/30-Backlog.md diff --git a/docs/selfhost/dep_tree_min_string.md b/docs/development/current/selfhost/dep_tree_min_string.md similarity index 100% rename from docs/selfhost/dep_tree_min_string.md rename to docs/development/current/selfhost/dep_tree_min_string.md diff --git a/docs/engineering/box_first_enforcement.md b/docs/development/engineering/box_first_enforcement.md similarity index 100% rename from docs/engineering/box_first_enforcement.md rename to docs/development/engineering/box_first_enforcement.md diff --git a/docs/issues/arraybox_invalid_args.md b/docs/development/issues/arraybox_invalid_args.md similarity index 100% rename from docs/issues/arraybox_invalid_args.md rename to docs/development/issues/arraybox_invalid_args.md diff --git a/docs/issues/llvm_binop_string_mismatch.md b/docs/development/issues/llvm_binop_string_mismatch.md similarity index 100% rename from docs/issues/llvm_binop_string_mismatch.md rename to docs/development/issues/llvm_binop_string_mismatch.md diff --git a/docs/issues/parser_unary_asi_alignment.md b/docs/development/issues/parser_unary_asi_alignment.md similarity index 100% rename from docs/issues/parser_unary_asi_alignment.md rename to docs/development/issues/parser_unary_asi_alignment.md diff --git a/docs/development/mir/MIR13_MODE.md b/docs/development/mir/MIR13_MODE.md new file mode 100644 index 00000000..27e1d2aa --- /dev/null +++ b/docs/development/mir/MIR13_MODE.md @@ -0,0 +1,50 @@ +MIR13 Mode (PHI-off by default) + +Overview +- Goal: Stabilize execution by turning off PHI emission in the Bridge/Builder and letting the LLVM (llvmlite) layer synthesize PHIs as needed. +- Default: MIR13 is ON by default (PHI-off). Use env flags to flip. + +Why +- Fewer SSA obligations in the front-end (Bridge/Builder) reduces CFG corner cases (short‑circuit, nested if/loop merges). +- Centralizes SSA invariants in a single place (llvmlite resolver/finalizer), improving correctness and maintenance. + +Flags and Behavior +- NYASH_MIR_NO_PHI (default: 1) + - 1: Bridge/Builder emit edge copies instead of PHIs at merges (MIR13). + - 0: Bridge/Builder may emit PHIs (MIR14 experimental). +- NYASH_VERIFY_ALLOW_NO_PHI (default: 1) + - Relaxes verifier checks that assume PHIs at merges. +- NYASH_LLVM_USE_HARNESS=1 (AOT via llvmlite harness) + - Resolver/finalizer synthesize PHIs at block heads when needed. + +LLVM (llvmlite) Responsibilities +- setup_phi_placeholders(): predeclare JSON‑provided PHIs and collect incoming metadata. +- block_end_values: snapshot per block end to materialize predecessor values (dominance‑safe). +- finalize_phis(): wire incoming edges for declared PHIs at block heads. +- Resolver.resolve_i64(): + - single‑pred: take predecessor end value; + - multi‑pred + declared PHI: reuse placeholder at block head; + - multi‑pred + no PHI: synthesize a localization PHI at the current block head (MIR13 compatibility); + - avoids reusing non‑dominating vmap values across blocks. + +Bridge/Builder (JSON v0) Behavior +- If/Loop/Try are lowered without PHIs when MIR13 is ON; merges are performed with edge copies (merge_var_maps) and the final value is reconstituted by the LLVM layer if needed. +- Helper split for readability (no behavior change): + - lowering/{if_else.rs, loop_.rs, try_catch.rs, merge.rs} + +Testing +- Curated LLVM (default = PHI‑off): + - tools/smokes/curated_llvm.sh (use --phi-on to exercise MIR14) +- PHI invariants/parity (AOT vs PyVM): + - tools/pyvm_vs_llvmlite.sh (default compares exit code; use CMP_STRICT=1 for stdout+exit) +- Bridge/PyVM: + - tools/selfhost_stage2_bridge_smoke.sh + +How to Force PHI‑on (MIR14 experimental) +- Set: NYASH_MIR_NO_PHI=0 and run tools/smokes/curated_llvm.sh --phi-on +- Known: loop_if_phi may trip dominance issues in PHI‑on; MIR13 is the recommended default. + +Known Limitations (current) +- No full tracing GC; object lifetime is managed via handle registries and Arc lifetimes. +- PHI‑on path is still being refined for some control‑flow patterns. + diff --git a/docs/refactoring/candidates_phase15.md b/docs/development/refactoring/candidates_phase15.md similarity index 100% rename from docs/refactoring/candidates_phase15.md rename to docs/development/refactoring/candidates_phase15.md diff --git a/docs/development/roadmap/phases/phase-15/phase-15.1/README.md b/docs/development/roadmap/phases/phase-15/phase-15.1/README.md index ba27c0f0..ee1f787e 100644 --- a/docs/development/roadmap/phases/phase-15/phase-15.1/README.md +++ b/docs/development/roadmap/phases/phase-15/phase-15.1/README.md @@ -33,7 +33,7 @@ Avoid: deep AOT emission/linking, cross-platform toolchain work, or scope creep ## Deliverables - `tools/aot_plan/` Nyash scripts and helpers -- `docs/specs/aot_plan_v1.md` (lightweight schema) +- `docs/design/aot-plan-v1.md` (lightweight schema) - Compiler entry to import AOT-Plan → MIR13 (feature-gated) - 3 smokes + 1 golden JSON sample @@ -86,4 +86,3 @@ Key takeaways aligned into this document: - Importer can read that JSON and construct MIR13 module(s) without panics - VM runs those modules and matches expected string/number results for trivial bodies - Events present when enabled; counters reflect plan/import activity; no AOT emit performed - diff --git a/docs/how-to/README.md b/docs/how-to/README.md new file mode 100644 index 00000000..b1bfaf19 --- /dev/null +++ b/docs/how-to/README.md @@ -0,0 +1,19 @@ +# Nyash How‑To Guides + +Task‑oriented, copy‑paste friendly instructions for common goals. + +Use when you already know what you want to do and just need the steps. + +Suggested structure (add as needed): +- Build & Run + - AOT build with LLVM + - Run with PyVM / VM / JIT +- Tooling + - Enable verbose logs + - Run curated smokes / parity checks +- Language + - Using and namespaces quick setup + - Array/Map literals gate toggles + +Contributions: keep each guide short (1–2 screens), start with prerequisites, end with verify step. + diff --git a/docs/how-to/self-hosting.md b/docs/how-to/self-hosting.md new file mode 100644 index 00000000..e0ad2821 --- /dev/null +++ b/docs/how-to/self-hosting.md @@ -0,0 +1,39 @@ +# Self‑Hosting — How‑To(前提→手順→検証) + +目的 +- Ny → MIR → VM/JIT の自己ホスト経路を最短手順で動かす。 + +前提 +- Rust(stable): `cargo --version` +- Bash + ripgrep(WSL/Unix 推奨) + +手順 +1) ビルド(JIT有効) + - 実行: `cargo build --release --features cranelift-jit` +2) 最小 E2E(VM、plugins 無効) + - 実行: `NYASH_DISABLE_PLUGINS=1 ./target/release/nyash --backend vm apps/selfhost-minimal/main.nyash` +3) コアスモーク + - 実行: `bash tools/jit_smoke.sh` +4) selfhost‑minimal スモーク + - 実行: `bash tools/selfhost_vm_smoke.sh` +5) 追加(任意) + - ブートストラップ: `bash tools/bootstrap_selfhost_smoke.sh` + - ラウンドトリップ: `bash tools/ny_roundtrip_smoke.sh` + +検証 +- 期待出力: `Result: 0`(selfhost‑minimal) +- スモーク:全成功(非 0 は失敗) + +便利フラグ +- `NYASH_DISABLE_PLUGINS=1` 外部プラグイン無効化 +- `NYASH_CLI_VERBOSE=1` 実行ログ詳細 +- `NYASH_JIT_THRESHOLD=1` JIT 降臨テスト + +トラブルシュート +- ハング: `timeout 15s ...` を付与、`NYASH_CLI_VERBOSE=1` で詳細 +- プラグインエラー: まず `NYASH_DISABLE_PLUGINS=1` +- ルート相対パスで実行/`cargo clean -p nyash` で個別クリーン + +関連 +- CI: `.github/workflows/smoke.yml` +- マージ運用: `docs/CONTRIBUTING-MERGE.md` diff --git a/docs/how-to/smokes.md b/docs/how-to/smokes.md new file mode 100644 index 00000000..9748bb39 --- /dev/null +++ b/docs/how-to/smokes.md @@ -0,0 +1,35 @@ +# Smokes — How‑To(前提→手順→検証) + +目的 +- 代表スモークを素早く回して、回帰を検知する。 + +前提 +- リリースビルド済み: `cargo build --release --features llvm` +- LLVM 18 が導入済み(AOT 経路のとき) + +手順(推奨ランナー) +1) LLVM curated + - 実行: `tools/smokes/curated_llvm.sh [--phi-off]` + - `--phi-off`: `NYASH_MIR_NO_PHI=1` を有効化し、検証を緩和 +2) PHI 不変条件パリティ + - 実行: `tools/smokes/curated_phi_invariants.sh` + - PyVM と llvmlite の stdout/exit code を比較 + +手動スモーク(例) +- Core (LLVM): `examples/llvm11_core_smoke.nyash` +- Async (LLVM only): + - `apps/tests/async-await-min/main.nyash` + - `apps/tests/async-spawn-instance/main.nyash` + - `apps/tests/async-await-timeout-fixed/main.nyash`(`NYASH_AWAIT_MAX_MS=100`) + +アーカイブ(非推奨) +- `tools/smokes/archive/` に旧ランナー(JIT/Cranelift 時代)が存在 + - `smoke_phase_10_10.sh`, `smoke_vm_jit.sh`, `smoke_async_spawn.sh`, `jit_smoke.sh`, `aot_smoke_cranelift.sh` + - これらは基本使わず、curated 系を使用 + +便利フラグ +- `NYASH_LLVM_USE_HARNESS=1`: llvmlite ハーネス経由 +- `NYASH_MIR_NO_PHI=1`, `NYASH_VERIFY_ALLOW_NO_PHI=1`: PHI 無しモード + +検証 +- 0 で成功、非 0 で失敗(CI 連携可) diff --git a/docs/ideas/README.md b/docs/private/ideas/README.md similarity index 100% rename from docs/ideas/README.md rename to docs/private/ideas/README.md diff --git a/docs/ideas/improvements/2025-08-25-unified-box-mir-vm.md b/docs/private/ideas/improvements/2025-08-25-unified-box-mir-vm.md similarity index 100% rename from docs/ideas/improvements/2025-08-25-unified-box-mir-vm.md rename to docs/private/ideas/improvements/2025-08-25-unified-box-mir-vm.md diff --git a/docs/ideas/improvements/2025-08-25-vm-andor-shortcircuit.md b/docs/private/ideas/improvements/2025-08-25-vm-andor-shortcircuit.md similarity index 100% rename from docs/ideas/improvements/2025-08-25-vm-andor-shortcircuit.md rename to docs/private/ideas/improvements/2025-08-25-vm-andor-shortcircuit.md diff --git a/docs/ideas/improvements/2025-08-25-vm-comparison-refactoring.md b/docs/private/ideas/improvements/2025-08-25-vm-comparison-refactoring.md similarity index 100% rename from docs/ideas/improvements/2025-08-25-vm-comparison-refactoring.md rename to docs/private/ideas/improvements/2025-08-25-vm-comparison-refactoring.md diff --git a/docs/ideas/improvements/2025-08-26-gc-switchable-semantic-equivalence.md b/docs/private/ideas/improvements/2025-08-26-gc-switchable-semantic-equivalence.md similarity index 100% rename from docs/ideas/improvements/2025-08-26-gc-switchable-semantic-equivalence.md rename to docs/private/ideas/improvements/2025-08-26-gc-switchable-semantic-equivalence.md diff --git a/docs/ideas/improvements/2025-08-26-modular-builtin-box-system.md b/docs/private/ideas/improvements/2025-08-26-modular-builtin-box-system.md similarity index 100% rename from docs/ideas/improvements/2025-08-26-modular-builtin-box-system.md rename to docs/private/ideas/improvements/2025-08-26-modular-builtin-box-system.md diff --git a/docs/ideas/improvements/2025-08-26-object-literal-sugar.md b/docs/private/ideas/improvements/2025-08-26-object-literal-sugar.md similarity index 100% rename from docs/ideas/improvements/2025-08-26-object-literal-sugar.md rename to docs/private/ideas/improvements/2025-08-26-object-literal-sugar.md diff --git a/docs/ideas/improvements/2025-08-27-two-stage-mir-design.md b/docs/private/ideas/improvements/2025-08-27-two-stage-mir-design.md similarity index 100% rename from docs/ideas/improvements/2025-08-27-two-stage-mir-design.md rename to docs/private/ideas/improvements/2025-08-27-two-stage-mir-design.md diff --git a/docs/ideas/improvements/2025-08-31-mir-annotation-system.md b/docs/private/ideas/improvements/2025-08-31-mir-annotation-system.md similarity index 100% rename from docs/ideas/improvements/2025-08-31-mir-annotation-system.md rename to docs/private/ideas/improvements/2025-08-31-mir-annotation-system.md diff --git a/docs/ideas/improvements/interpreter-box-architecture.md b/docs/private/ideas/improvements/interpreter-box-architecture.md similarity index 100% rename from docs/ideas/improvements/interpreter-box-architecture.md rename to docs/private/ideas/improvements/interpreter-box-architecture.md diff --git a/docs/ideas/new-features/2025-08-25-box-converter-system.md b/docs/private/ideas/new-features/2025-08-25-box-converter-system.md similarity index 100% rename from docs/ideas/new-features/2025-08-25-box-converter-system.md rename to docs/private/ideas/new-features/2025-08-25-box-converter-system.md diff --git a/docs/ideas/new-features/2025-08-25-repl-mode.md b/docs/private/ideas/new-features/2025-08-25-repl-mode.md similarity index 100% rename from docs/ideas/new-features/2025-08-25-repl-mode.md rename to docs/private/ideas/new-features/2025-08-25-repl-mode.md diff --git a/docs/ideas/new-features/2025-08-26-ai-agent-challenge-strategy.md b/docs/private/ideas/new-features/2025-08-26-ai-agent-challenge-strategy.md similarity index 100% rename from docs/ideas/new-features/2025-08-26-ai-agent-challenge-strategy.md rename to docs/private/ideas/new-features/2025-08-26-ai-agent-challenge-strategy.md diff --git a/docs/ideas/new-features/2025-08-26-midnight-network-integration.md b/docs/private/ideas/new-features/2025-08-26-midnight-network-integration.md similarity index 100% rename from docs/ideas/new-features/2025-08-26-midnight-network-integration.md rename to docs/private/ideas/new-features/2025-08-26-midnight-network-integration.md diff --git a/docs/ideas/new-features/2025-08-26-when-pattern-matching.md b/docs/private/ideas/new-features/2025-08-26-when-pattern-matching.md similarity index 100% rename from docs/ideas/new-features/2025-08-26-when-pattern-matching.md rename to docs/private/ideas/new-features/2025-08-26-when-pattern-matching.md diff --git a/docs/ideas/new-features/2025-08-27-python-to-nyir-revolution.md b/docs/private/ideas/new-features/2025-08-27-python-to-nyir-revolution.md similarity index 100% rename from docs/ideas/new-features/2025-08-27-python-to-nyir-revolution.md rename to docs/private/ideas/new-features/2025-08-27-python-to-nyir-revolution.md diff --git a/docs/ideas/new-features/2025-08-27-simple-man-shell-revolution.md b/docs/private/ideas/new-features/2025-08-27-simple-man-shell-revolution.md similarity index 100% rename from docs/ideas/new-features/2025-08-27-simple-man-shell-revolution.md rename to docs/private/ideas/new-features/2025-08-27-simple-man-shell-revolution.md diff --git a/docs/ideas/new-features/2025-08-28-jit-exe-via-plugin-unification.md b/docs/private/ideas/new-features/2025-08-28-jit-exe-via-plugin-unification.md similarity index 100% rename from docs/ideas/new-features/2025-08-28-jit-exe-via-plugin-unification.md rename to docs/private/ideas/new-features/2025-08-28-jit-exe-via-plugin-unification.md diff --git a/docs/ideas/new-features/2025-08-29-ai-compact-notation-protocol.md b/docs/private/ideas/new-features/2025-08-29-ai-compact-notation-protocol.md similarity index 100% rename from docs/ideas/new-features/2025-08-29-ai-compact-notation-protocol.md rename to docs/private/ideas/new-features/2025-08-29-ai-compact-notation-protocol.md diff --git a/docs/ideas/new-features/2025-08-30-birth-args-tlv-generalization.md b/docs/private/ideas/new-features/2025-08-30-birth-args-tlv-generalization.md similarity index 100% rename from docs/ideas/new-features/2025-08-30-birth-args-tlv-generalization.md rename to docs/private/ideas/new-features/2025-08-30-birth-args-tlv-generalization.md diff --git a/docs/ideas/other/2025-08-25-cranelift-research.md b/docs/private/ideas/other/2025-08-25-cranelift-research.md similarity index 100% rename from docs/ideas/other/2025-08-25-cranelift-research.md rename to docs/private/ideas/other/2025-08-25-cranelift-research.md diff --git a/docs/ideas/other/2025-08-25-debugging-nightmare-lessons.md b/docs/private/ideas/other/2025-08-25-debugging-nightmare-lessons.md similarity index 100% rename from docs/ideas/other/2025-08-25-debugging-nightmare-lessons.md rename to docs/private/ideas/other/2025-08-25-debugging-nightmare-lessons.md diff --git a/docs/ideas/other/2025-08-25-gemini-sensei-full-response.md b/docs/private/ideas/other/2025-08-25-gemini-sensei-full-response.md similarity index 100% rename from docs/ideas/other/2025-08-25-gemini-sensei-full-response.md rename to docs/private/ideas/other/2025-08-25-gemini-sensei-full-response.md diff --git a/docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md b/docs/private/ideas/other/2025-08-25-unified-box-design-deep-analysis.md similarity index 100% rename from docs/ideas/other/2025-08-25-unified-box-design-deep-analysis.md rename to docs/private/ideas/other/2025-08-25-unified-box-design-deep-analysis.md diff --git a/docs/ideas/other/2025-08-26-gc-switchable-language.md b/docs/private/ideas/other/2025-08-26-gc-switchable-language.md similarity index 100% rename from docs/ideas/other/2025-08-26-gc-switchable-language.md rename to docs/private/ideas/other/2025-08-26-gc-switchable-language.md diff --git a/docs/ideas/other/2025-08-26-gemini-codex-evaluation-on-nyash-papers.md b/docs/private/ideas/other/2025-08-26-gemini-codex-evaluation-on-nyash-papers.md similarity index 100% rename from docs/ideas/other/2025-08-26-gemini-codex-evaluation-on-nyash-papers.md rename to docs/private/ideas/other/2025-08-26-gemini-codex-evaluation-on-nyash-papers.md diff --git a/docs/ideas/other/2025-08-26-nyash-academic-papers-potential.md b/docs/private/ideas/other/2025-08-26-nyash-academic-papers-potential.md similarity index 100% rename from docs/ideas/other/2025-08-26-nyash-academic-papers-potential.md rename to docs/private/ideas/other/2025-08-26-nyash-academic-papers-potential.md diff --git a/docs/ideas/other/2025-08-27-daily-achievements.md b/docs/private/ideas/other/2025-08-27-daily-achievements.md similarity index 100% rename from docs/ideas/other/2025-08-27-daily-achievements.md rename to docs/private/ideas/other/2025-08-27-daily-achievements.md diff --git a/docs/ideas/other/2025-08-27-gradual-implementation-stairway.md b/docs/private/ideas/other/2025-08-27-gradual-implementation-stairway.md similarity index 100% rename from docs/ideas/other/2025-08-27-gradual-implementation-stairway.md rename to docs/private/ideas/other/2025-08-27-gradual-implementation-stairway.md diff --git a/docs/ideas/other/2025-08-27-jit-phase10-progress-notes.md b/docs/private/ideas/other/2025-08-27-jit-phase10-progress-notes.md similarity index 100% rename from docs/ideas/other/2025-08-27-jit-phase10-progress-notes.md rename to docs/private/ideas/other/2025-08-27-jit-phase10-progress-notes.md diff --git a/docs/ideas/other/2025-08-27-mir-roundtrip-optimization.md b/docs/private/ideas/other/2025-08-27-mir-roundtrip-optimization.md similarity index 100% rename from docs/ideas/other/2025-08-27-mir-roundtrip-optimization.md rename to docs/private/ideas/other/2025-08-27-mir-roundtrip-optimization.md diff --git a/docs/ideas/other/2025-08-27-nyah-ai-orchestration.md b/docs/private/ideas/other/2025-08-27-nyah-ai-orchestration.md similarity index 100% rename from docs/ideas/other/2025-08-27-nyah-ai-orchestration.md rename to docs/private/ideas/other/2025-08-27-nyah-ai-orchestration.md diff --git a/docs/ideas/other/2025-08-27-nyash-as-cheat-language.md b/docs/private/ideas/other/2025-08-27-nyash-as-cheat-language.md similarity index 100% rename from docs/ideas/other/2025-08-27-nyash-as-cheat-language.md rename to docs/private/ideas/other/2025-08-27-nyash-as-cheat-language.md diff --git a/docs/ideas/other/2025-08-27-rust-vs-nyash-simplicity.md b/docs/private/ideas/other/2025-08-27-rust-vs-nyash-simplicity.md similarity index 100% rename from docs/ideas/other/2025-08-27-rust-vs-nyash-simplicity.md rename to docs/private/ideas/other/2025-08-27-rust-vs-nyash-simplicity.md diff --git a/docs/ideas/other/archived/2025-08-26-everything-is-thread-safe-box.md b/docs/private/ideas/other/archived/2025-08-26-everything-is-thread-safe-box.md similarity index 100% rename from docs/ideas/other/archived/2025-08-26-everything-is-thread-safe-box.md rename to docs/private/ideas/other/archived/2025-08-26-everything-is-thread-safe-box.md diff --git a/docs/ideas/other/archived/2025-08-27-chatgpt5-thread-safe-box-design.md b/docs/private/ideas/other/archived/2025-08-27-chatgpt5-thread-safe-box-design.md similarity index 100% rename from docs/ideas/other/archived/2025-08-27-chatgpt5-thread-safe-box-design.md rename to docs/private/ideas/other/archived/2025-08-27-chatgpt5-thread-safe-box-design.md diff --git a/docs/ideas/other/archived/2025-08-27-sync-box-automatic-locking.md b/docs/private/ideas/other/archived/2025-08-27-sync-box-automatic-locking.md similarity index 100% rename from docs/ideas/other/archived/2025-08-27-sync-box-automatic-locking.md rename to docs/private/ideas/other/archived/2025-08-27-sync-box-automatic-locking.md diff --git a/docs/ideas/other/archived/2025-08-27-sync-box-demo-ideas.md b/docs/private/ideas/other/archived/2025-08-27-sync-box-demo-ideas.md similarity index 100% rename from docs/ideas/other/archived/2025-08-27-sync-box-demo-ideas.md rename to docs/private/ideas/other/archived/2025-08-27-sync-box-demo-ideas.md diff --git a/docs/ideas/other/dual-mode-design.md b/docs/private/ideas/other/dual-mode-design.md similarity index 100% rename from docs/ideas/other/dual-mode-design.md rename to docs/private/ideas/other/dual-mode-design.md diff --git a/docs/private/papers/reference/core-language/README.md b/docs/private/papers/reference/core-language/README.md index 3416c433..7ba7ccdb 100644 --- a/docs/private/papers/reference/core-language/README.md +++ b/docs/private/papers/reference/core-language/README.md @@ -4,7 +4,7 @@ **最新の完全な言語リファレンスは以下を参照してください:** -- **[🚀 Nyash Language Reference 2025](../language/LANGUAGE_REFERENCE_2025.md)** - 完全な言語仕様(最新版) +- **[🚀 Nyash Language Reference 2025](../../../../reference/language/LANGUAGE_REFERENCE_2025.md)** - 完全な言語仕様(最新版) - **[📝 構文早見表](../../quick-reference/syntax-cheatsheet.md)** - よく使う構文のクイックリファレンス ## 📁 このディレクトリの内容 @@ -20,4 +20,4 @@ --- -**注意**: 言語仕様に関する最新情報は必ず [LANGUAGE_REFERENCE_2025.md](../language/LANGUAGE_REFERENCE_2025.md) を参照してください。 \ No newline at end of file +**注意**: 言語仕様に関する最新情報は必ず [LANGUAGE_REFERENCE_2025.md](../../../../reference/language/LANGUAGE_REFERENCE_2025.md) を参照してください。 diff --git a/docs/private/papers/reference/language/LANGUAGE_REFERENCE_2025.md b/docs/private/papers/reference/language/LANGUAGE_REFERENCE_2025.md deleted file mode 100644 index f0670ed4..00000000 --- a/docs/private/papers/reference/language/LANGUAGE_REFERENCE_2025.md +++ /dev/null @@ -1,659 +0,0 @@ -# 🚀 Nyash Language Reference 2025 - -**最終更新: 2025年9月4日 - Phase 12.7実装済み機能の正確な反映** - -## 📖 概要 - -Nyashは「Everything is Box」哲学に基づく革新的プログラミング言語です。 -Rust製インタープリターによる高性能実行と、直感的な構文により、学習しやすく実用的な言語として完成しました。 - ---- - -## 🔤 **1. 予約語・キーワード完全リスト** - -### **Phase 12.7で確定した15個の予約語** -| 予約語 | 用途 | 例 | -|-------|------|---| -| `box` | クラス定義 | `box MyClass { }` | -| `new` | オブジェクト生成 | `new ConsoleBox()` | -| `me` | 自己参照(thisの代わり) | `me.field = value` | -| `local` | ローカル変数宣言 | `local x, y = 10` | -| `return` | 関数リターン | `return value` | -| `from` | デリゲーション・親メソッド呼び出し | `box Child from Parent` / `from Parent.method()` | -| `birth` | コンストラクタ(統一名) | `birth(param) { }` | -| `static` | 静的Box・関数定義 | `static box Main { }` | -| `if` | 条件分岐 | `if condition { }` | -| `else` | else節 | `else { }` | -| `loop` | ループ(唯一の形式) | `loop(condition) { }` | -| `continue` | ループ継続 | `continue` | -| `peek` | パターンマッチング風分岐 | `peek value { "A" => 1, else => 0 }` | -| `try` | 例外捕獲開始 | `try { }` | -| `interface` | インターフェース定義 | `interface Comparable { }` | - -### **その他の重要キーワード(予約語ではない)** -| キーワード | 用途 | 例 | -|-------|------|---| -| `override` | 明示的オーバーライド | `override speak() { }` | -| `break` | ループ脱出 | `break` | -| `catch` | 例外処理 | `catch (e) { }` | -| `finally` | 最終処理 | `finally { }` | -| `throw` | 例外発生 | `throw error` | -| `nowait` | 非同期実行 | `nowait future = task()` | -| `await` | 待機・結果取得 | `result = await future` | -| `include` | ファイル取り込み | `include "math.nyash"` | -| `print` | 出力(デバッグ用) | `print("Hello")` | -| `function`/`fn` | 関数定義 | `fn add(a,b) { }` | -| `init` | 初期化ブロック | `init { field1, field2 }` | -| `pack` | 旧コンストラクタ(互換性) | `pack(param) { }` | -| `outbox` | 所有権移転変数 | `outbox result = compute()` | -| `global` | グローバル変数 | `global CONFIG = "dev"` | -| `weak` | 弱参照修飾子 | `weak reference` | -| `using` | 名前空間インポート | `using namespace` | - -### **演算子・論理** -| 演算子/キーワード | 用途 | 例 | -|-------|------|---| -| `not` | 論理否定 | `not condition` | -| `and` | 論理積 | `a and b` | -| `or` | 論理和 | `a or b` | -| `true`/`false` | 真偽値 | `flag = true` | -| `null` | null値 | `value = null` | - - ---- - -## 📝 **2. 文法・構文仕様** - -### **2.1 Box定義文法** - -#### **基本Box** -```nyash -box ClassName { - # フィールド宣言(Phase 12.7形式) - field1: TypeBox # フィールド型アノテーション(P0では無視) - field2: TypeBox - field3 # 型なしも可 - - # コンストラクタ - birth(param1, param2) { # birth構文に統一 - me.field1 = param1 - me.field2 = param2 - me.field3 = defaultValue() - } - - # メソッド - methodName(arg1, arg2) { - return me.field1 + arg1 - } - - # デストラクタ(fini) - fini() { - print("Cleanup: " + me.field1) - } -} -``` - -#### **デリゲーションBox** -```nyash -box Child from Parent interface Comparable { - childField: TypeBox # 追加フィールド - - birth(parentParam, childParam) { # birth構文に統一 - from Parent.birth(parentParam) # 親コンストラクタ明示呼び出し - me.childField = childParam - } - - # メソッドオーバーライド - override process(data) { # overrideキーワード必須 - local result = from Parent.process(data) # 親メソッド呼び出し - return result + " (Child processed)" - } - - # インターフェース実装 - compareTo(other) { - return me.value - other.value - } -} -``` - -#### **Static Box(推奨エントリーポイント)** -```nyash -static box Main { - console: ConsoleBox - result: IntegerBox - - main() { - me.console = new ConsoleBox() - me.console.log("🎉 Everything is Box!") - return "Success" - } -} -``` - -### **2.2 変数宣言** - -#### **基本パターン** -```nyash -# 単一宣言 -local x -local name = "初期値" - -# 複数宣言 -local a, b, c -local x = 10, y = 20, z # 混合初期化 - -# 所有権移転(static関数内) -static function Factory.create() { - outbox product # 呼び出し側に所有権移転 - product = new Item() - return product -} -``` - -#### **変数宣言厳密化システム(2025-08-09実装)** -```nyash -# ✅ 正しい - 明示宣言必須 -local temp -temp = 42 - -# ❌ エラー - 未宣言変数への代入 -x = 42 # RuntimeError: 未宣言変数 + 修正提案表示 -``` - -### **2.3 制御構文** - -#### **条件分岐** -```nyash -if condition { - # 処理 -} else if condition2 { - # 処理2 -} else { - # else処理 -} -``` - -#### **ループ(統一構文)** -```nyash -# ✅ 唯一の正しい形式 -loop(condition) { - # ループ本体 - if exitCondition { - break - } - if skipCondition { - continue # Phase 12.7で追加 - } -} - -# ❌ 削除済み - 使用不可 -while condition { } # パーサーエラー -loop() { } # パーサーエラー -``` - -#### **Peek式(Phase 12.7で追加)** -```nyash -# パターンマッチング風の分岐 -local result = peek value { - "A" => 100, - "B" => 200, - "C" => 300, - else => 0 # else必須 -} - -# 文の形式も可 -peek status { - "error" => { - print("Error occurred") - return null - }, - "success" => { - print("All good") - }, - else => { - print("Unknown status") - } -} -``` - -### **2.4 演算子・式** - -#### **🚀 新実装: 関数オーバーロードシステム** -```nyash -# Rust風トレイトベース演算子(2025-08-10実装完了) -sum = 10 + 20 # IntegerBox + IntegerBox = IntegerBox -concat = "Hi" + " !" # StringBox + StringBox = StringBox -repeat = "Ha" * 3 # StringBox * IntegerBox = "HaHaHa" -mixed = 42 + " answer" # 混合型 → 自動文字列結合フォールバック -``` - -#### **演算子優先順位** -```nyash -result = a + b * c / d - e # 算術演算子は標準的優先順位 -logic = not a and b or c # not > and > or -compare = (x > y) and (z <= w) # 比較は括弧推奨 -``` - -#### **論理演算子** -```nyash -# キーワード版(推奨) -canAccess = level >= 5 and hasKey -isValid = not (isEmpty or hasError) - -# シンボル版(互換) -result = condition && other || fallback # 利用可能だが非推奨 -``` - -#### **特殊演算子(Phase 12.7実装済み)** -```nyash -# ? 演算子 - Result伝播 -local data = readFile(path)? # エラーなら早期return - -# ラムダ式 -local add = fn(x, y) { return x + y } -local double = fn(x) { x * 2 } # 単一式なら省略可 - -# await式 -local result = await asyncTask() -``` - ---- - -## 🏗️ **3. Box構文詳細ガイド** - -### **3.1 Everything is Box 原則** - -```nyash -# すべての値がBox -number = 42 # IntegerBox -text = "hello" # StringBox -flag = true # BoolBox -array = new ArrayBox() # ArrayBox -console = new ConsoleBox() # ConsoleBox - -# 統一的なメソッド呼び出し -print(number.to_string_box().value) # "42" -print(array.length()) # 配列長 -console.log("Everything is Box!") # コンソール出力 -``` - -### **3.2 コンストラクタパターン** - -#### **パラメータ付きコンストラクタ** -```nyash -box Person { - public name: StringBox - public email: StringBox - private age: IntegerBox - - birth(personName, personAge) { # birth構文に統一 - me.name = personName - me.age = personAge - me.email = me.name + "@example.com" # 計算フィールド - } - - # ファクトリーメソッド - static createGuest() { - outbox guest - guest = new Person("Guest", 0) - return guest - } -} - -# 使用例 -person = new Person("Alice", 25) -guest = Person.createGuest() -``` - -### **3.3 継承とインターフェース** - -#### **デリゲーションチェーン** -```nyash -# 基底Box -box Animal { - public name: StringBox - public species: StringBox - - birth(animalName, animalSpecies) { - me.name = animalName - me.species = animalSpecies - } - - speak() { - return me.name + " makes a sound" - } -} - -# デリゲーション -box Dog from Animal { - breed: StringBox # 追加フィールド - - birth(dogName, dogBreed) { - from Animal.birth(dogName, "Canine") # 親コンストラクタ呼び出し - me.breed = dogBreed - } - - override speak() { # 明示的オーバーライド - return me.name + " barks: Woof!" - } -} - -# インターフェース実装 -box Cat from Animal interface Playful { - # Playfulインターフェースの実装必須 -} -``` - -### **3.4 Static Boxパターン** - -#### **名前空間・ユーティリティ** -```nyash -static box MathUtils { - PI: FloatBox - E: FloatBox - - # 注意: static初期化ブロックは未実装 - # 初期化はメソッド内で行う - - add(a, b) { - return a + b - } - - circleArea(radius) { - # 初回アクセスで初期化パターン - if me.PI == null { - me.PI = 3.14159265 - } - return me.PI * radius * radius - } -} - -# 使用法 -area = MathUtils.circleArea(5) -sum = MathUtils.add(10, 20) -``` - -#### **アプリケーションエントリーポイント** -```nyash -# 🎯 推奨: Static Box Main パターン -static box Main { - console: ConsoleBox - result: IntegerBox - - main() { - me.console = new ConsoleBox() - me.console.log("🚀 Starting application...") - - # アプリケーションロジック - me.result = processData() - - return "Application completed successfully" - } -} -``` - ---- - -## 🚀 **4. 最新機能・革新技術** - -### **4.1 Arc Revolution(2025-08-10)** -```nyash -# 全16種類のBox型が統一Arcパターンで実装 -# 完全なスレッドセーフティと高性能を両立 - -array = new ArrayBox() -array.push(10) # スレッドセーフな追加 -array.push(20) -item = array.get(0) # スレッドセーフな取得 - -json = new JSONBox() -json.set("name", "Alice") # 並行安全な操作 -data = json.stringify() # JSON文字列化 -``` - -### **4.2 Rust風トレイトベース演算子(2025-08-10)** -```nyash -# AI大相談会で決定された最適設計 -# 静的・動的ハイブリッドディスパッチによる高性能実現 - -# 整数演算 -result = 100 - 25 # IntegerBox間演算 → IntegerBox -product = 6 * 7 # 高速静的ディスパッチ - -# 文字列操作 -greeting = "Hello" + " World" # 文字列結合 -repeated = "Echo" * 3 # "EchoEchoEcho" - -# 混合型フォールバック -message = "Answer: " + 42 # "Answer: 42" - -# Boolean演算 -boolSum = true + false # 1 (IntegerBox) -``` - -### **4.3 変数宣言厳密化(2025-08-09)** -```nyash -# メモリ安全性・非同期安全性保証システム - -static box Calculator { - private memory: ArrayBox # 必須フィールド宣言 - - calculate() { - local temp # 必須ローカル変数宣言 - temp = me.memory * 2 - return temp - } -} -``` - ---- - -## 🚀 **4.4 Phase 12.7実装済み機能** - -### **Peek式 - パターンマッチング風分岐** -```nyash -# 式として使用(値を返す) -local grade = peek score { - 100 => "Perfect", - 90 => "Excellent", - 80 => "Good", - else => "Needs improvement" -} - -# 文として使用(アクション実行) -peek command { - "save" => { - saveFile() - print("Saved!") - }, - "quit" => { - cleanup() - return - }, - else => print("Unknown command") -} -``` - -### **Continue文** -```nyash -loop(i < 100) { - if i % 2 == 0 { - continue # 偶数をスキップ - } - process(i) -} -``` - -### **フィールド型アノテーション** -```nyash -box Person { - name: StringBox # 型情報を明記(P0では無視) - age: IntegerBox - email # 型なしも可 -} -``` - -### **?演算子(Result伝播)** -```nyash -# ResultBoxのエラーを自動的に早期return -local data = readFile("config.json")? -local parsed = parseJSON(data)? -return parsed.get("version") -``` - -### **Lambda式** -```nyash -# 基本形 -local add = fn(x, y) { return x + y } - -# 単一式の場合(returnは省略可) -local double = fn(x) { x * 2 } - -# 高階関数での使用 -array.map(fn(x) { x * x }) -``` - ---- - -## ⚡ **5. 実装済みBox型ライブラリ** - -### **5.1 基本型** -- `StringBox` - 文字列(split, find, replace, trim等) -- `IntegerBox` - 64bit整数 -- `FloatBox` - 64bit浮動小数点数 -- `BoolBox` - 真偽値 -- `NullBox` - null値 -- `VoidBox` - void値 - -### **5.2 コレクション** -- `ArrayBox` - 動的配列(push, pop, get, set, join等) -- `MapBox` - 連想配列・辞書 - -### **5.3 システム・I/O** -- `ConsoleBox` - コンソール入出力 -- `DebugBox` - デバッグ支援・メモリ追跡 -- `FileBox` - ファイルシステム操作(プラグイン) - -### **5.4 数学・時間** -- `MathBox` - 数学関数(sin, cos, log, sqrt等) -- `TimeBox` - 時刻操作・タイマー -- `RandomBox` - 乱数生成・選択・シャッフル - -### **5.5 データ処理** -- `JSONBox` - JSON解析・生成(parse, stringify, get, set) -- `RegexBox` - 正規表現(test, find, replace, split) -- `BufferBox` - バイナリデータ処理 -- `StreamBox` - ストリーム処理 - -### **5.6 ネットワーク・Web** -- `HttpClientBox` - HTTP通信(プラグイン) -- `WebDisplayBox` - HTML表示(WASM) -- `WebConsoleBox` - ブラウザコンソール(WASM) -- `WebCanvasBox` - Canvas描画(WASM) - -### **5.7 GUI・マルチメディア** -- `EguiBox` - デスクトップGUI(Windows/Linux、プラグイン) -- `SoundBox` - 音声再生 - -### **5.8 特殊用途** -- `FutureBox` - 非同期処理結果 -- `ResultBox` - エラー処理(Ok/Err) -- `TokenBox` - キャンセルトークン -- `FunctionBox` - 第一級関数 -- `P2PBox` - P2P通信(プラグイン) - ---- - -## 🎯 **6. パフォーマンス・デザイン原則** - -### **6.1 メモリ安全性** -- Rust所有権システムによる完全なメモリ安全性 -- Arcによるスレッドセーフな共有状態管理 -- 自動参照カウント + 明示的デストラクタ(fini) - -### **6.2 実行効率** -- 統一されたBox型システムによる最適化 -- 静的・動的ハイブリッドディスパッチで高速演算 -- パーサー無限ループ対策(--debug-fuel) - -### **6.3 開発効率** -- 変数宣言厳密化による早期エラー検出 -- 包括的デバッグ機能(DebugBox) -- 直感的な"Everything is Box"概念 - ---- - -## 📚 **7. 学習パス・ベストプラクティス** - -### **7.1 初心者向け学習順序** -1. **基本概念**: Everything is Box哲学理解 -2. **基本構文**: 変数宣言・制御構文・演算子 -3. **Box定義**: 基本的なクラス作成 -4. **Static Box Main**: アプリケーションエントリーポイント -5. **継承・インターフェース**: オブジェクト指向機能 - -### **7.2 推奨コーディングスタイル** -```nyash -# ✅ 推奨スタイル -static box Main { - public console: ConsoleBox # 公開フィールド明示 - public result: IntegerBox - - main() { - me.console = new ConsoleBox() - - local data # 変数事前宣言 - data = processInput() - - me.result = data # 明確な代入 - return "Success" - } -} -``` - -### **7.3 よくある間違いと対策** -```nyash -# ❌ よくある間違い -public { field1 field2 } # 旧構文 → 使用不可 -x = 42 # 変数未宣言 → ランタイムエラー -while condition { } # 非対応構文 → パーサーエラー -this.field # thisは使用不可 → me.fieldを使用 - -# ✅ 正しい書き方(Phase 12.7後) -field1: TypeBox # フィールド宣言(型は省略可) -field2 # 型なしフィールド -local x = 42 # 事前宣言必須 -loop(condition) { } # 統一ループ構文 -me.field # self参照はmeのみ -``` - ---- - -## 📌 **8. 糖衣構文(Phase 12.7-B)** - -### 実装済み(ゲート: `NYASH_SYNTAX_SUGAR_LEVEL=basic|full`) -```nyash -# パイプライン -result = data |> normalize() |> transform() |> process - -# セーフアクセス + デフォルト -name = user?.profile?.name ?? "guest" - -# 複合代入 -x += 1; y *= 2 - -# 範囲(内部的には Range(a,b)) -loop(i in 1 .. 5) { /* ... */ } -``` - -### 拡張(段階適用予定・設計済み) -```nyash -let {x, y} = point -let [first, second, ...rest] = array -``` - ---- - -**🎉 Nyash 2025は、AI協働設計による最先端言語システムとして、シンプルさと強力さを完全に両立しました。** - -*最終更新: 2025年9月4日 - Phase 12.7実装済み機能の正確な反映* diff --git a/docs/private/reference/core-language/README.md b/docs/private/reference/core-language/README.md index 3416c433..81b2feef 100644 --- a/docs/private/reference/core-language/README.md +++ b/docs/private/reference/core-language/README.md @@ -4,7 +4,7 @@ **最新の完全な言語リファレンスは以下を参照してください:** -- **[🚀 Nyash Language Reference 2025](../language/LANGUAGE_REFERENCE_2025.md)** - 完全な言語仕様(最新版) +- **[🚀 Nyash Language Reference 2025](../../../reference/language/LANGUAGE_REFERENCE_2025.md)** - 完全な言語仕様(最新版) - **[📝 構文早見表](../../quick-reference/syntax-cheatsheet.md)** - よく使う構文のクイックリファレンス ## 📁 このディレクトリの内容 @@ -20,4 +20,4 @@ --- -**注意**: 言語仕様に関する最新情報は必ず [LANGUAGE_REFERENCE_2025.md](../language/LANGUAGE_REFERENCE_2025.md) を参照してください。 \ No newline at end of file +**注意**: 言語仕様に関する最新情報は必ず [LANGUAGE_REFERENCE_2025.md](../../../reference/language/LANGUAGE_REFERENCE_2025.md) を参照してください。 diff --git a/docs/private/reference/language/LANGUAGE_REFERENCE_2025.md b/docs/private/reference/language/LANGUAGE_REFERENCE_2025.md deleted file mode 100644 index f0670ed4..00000000 --- a/docs/private/reference/language/LANGUAGE_REFERENCE_2025.md +++ /dev/null @@ -1,659 +0,0 @@ -# 🚀 Nyash Language Reference 2025 - -**最終更新: 2025年9月4日 - Phase 12.7実装済み機能の正確な反映** - -## 📖 概要 - -Nyashは「Everything is Box」哲学に基づく革新的プログラミング言語です。 -Rust製インタープリターによる高性能実行と、直感的な構文により、学習しやすく実用的な言語として完成しました。 - ---- - -## 🔤 **1. 予約語・キーワード完全リスト** - -### **Phase 12.7で確定した15個の予約語** -| 予約語 | 用途 | 例 | -|-------|------|---| -| `box` | クラス定義 | `box MyClass { }` | -| `new` | オブジェクト生成 | `new ConsoleBox()` | -| `me` | 自己参照(thisの代わり) | `me.field = value` | -| `local` | ローカル変数宣言 | `local x, y = 10` | -| `return` | 関数リターン | `return value` | -| `from` | デリゲーション・親メソッド呼び出し | `box Child from Parent` / `from Parent.method()` | -| `birth` | コンストラクタ(統一名) | `birth(param) { }` | -| `static` | 静的Box・関数定義 | `static box Main { }` | -| `if` | 条件分岐 | `if condition { }` | -| `else` | else節 | `else { }` | -| `loop` | ループ(唯一の形式) | `loop(condition) { }` | -| `continue` | ループ継続 | `continue` | -| `peek` | パターンマッチング風分岐 | `peek value { "A" => 1, else => 0 }` | -| `try` | 例外捕獲開始 | `try { }` | -| `interface` | インターフェース定義 | `interface Comparable { }` | - -### **その他の重要キーワード(予約語ではない)** -| キーワード | 用途 | 例 | -|-------|------|---| -| `override` | 明示的オーバーライド | `override speak() { }` | -| `break` | ループ脱出 | `break` | -| `catch` | 例外処理 | `catch (e) { }` | -| `finally` | 最終処理 | `finally { }` | -| `throw` | 例外発生 | `throw error` | -| `nowait` | 非同期実行 | `nowait future = task()` | -| `await` | 待機・結果取得 | `result = await future` | -| `include` | ファイル取り込み | `include "math.nyash"` | -| `print` | 出力(デバッグ用) | `print("Hello")` | -| `function`/`fn` | 関数定義 | `fn add(a,b) { }` | -| `init` | 初期化ブロック | `init { field1, field2 }` | -| `pack` | 旧コンストラクタ(互換性) | `pack(param) { }` | -| `outbox` | 所有権移転変数 | `outbox result = compute()` | -| `global` | グローバル変数 | `global CONFIG = "dev"` | -| `weak` | 弱参照修飾子 | `weak reference` | -| `using` | 名前空間インポート | `using namespace` | - -### **演算子・論理** -| 演算子/キーワード | 用途 | 例 | -|-------|------|---| -| `not` | 論理否定 | `not condition` | -| `and` | 論理積 | `a and b` | -| `or` | 論理和 | `a or b` | -| `true`/`false` | 真偽値 | `flag = true` | -| `null` | null値 | `value = null` | - - ---- - -## 📝 **2. 文法・構文仕様** - -### **2.1 Box定義文法** - -#### **基本Box** -```nyash -box ClassName { - # フィールド宣言(Phase 12.7形式) - field1: TypeBox # フィールド型アノテーション(P0では無視) - field2: TypeBox - field3 # 型なしも可 - - # コンストラクタ - birth(param1, param2) { # birth構文に統一 - me.field1 = param1 - me.field2 = param2 - me.field3 = defaultValue() - } - - # メソッド - methodName(arg1, arg2) { - return me.field1 + arg1 - } - - # デストラクタ(fini) - fini() { - print("Cleanup: " + me.field1) - } -} -``` - -#### **デリゲーションBox** -```nyash -box Child from Parent interface Comparable { - childField: TypeBox # 追加フィールド - - birth(parentParam, childParam) { # birth構文に統一 - from Parent.birth(parentParam) # 親コンストラクタ明示呼び出し - me.childField = childParam - } - - # メソッドオーバーライド - override process(data) { # overrideキーワード必須 - local result = from Parent.process(data) # 親メソッド呼び出し - return result + " (Child processed)" - } - - # インターフェース実装 - compareTo(other) { - return me.value - other.value - } -} -``` - -#### **Static Box(推奨エントリーポイント)** -```nyash -static box Main { - console: ConsoleBox - result: IntegerBox - - main() { - me.console = new ConsoleBox() - me.console.log("🎉 Everything is Box!") - return "Success" - } -} -``` - -### **2.2 変数宣言** - -#### **基本パターン** -```nyash -# 単一宣言 -local x -local name = "初期値" - -# 複数宣言 -local a, b, c -local x = 10, y = 20, z # 混合初期化 - -# 所有権移転(static関数内) -static function Factory.create() { - outbox product # 呼び出し側に所有権移転 - product = new Item() - return product -} -``` - -#### **変数宣言厳密化システム(2025-08-09実装)** -```nyash -# ✅ 正しい - 明示宣言必須 -local temp -temp = 42 - -# ❌ エラー - 未宣言変数への代入 -x = 42 # RuntimeError: 未宣言変数 + 修正提案表示 -``` - -### **2.3 制御構文** - -#### **条件分岐** -```nyash -if condition { - # 処理 -} else if condition2 { - # 処理2 -} else { - # else処理 -} -``` - -#### **ループ(統一構文)** -```nyash -# ✅ 唯一の正しい形式 -loop(condition) { - # ループ本体 - if exitCondition { - break - } - if skipCondition { - continue # Phase 12.7で追加 - } -} - -# ❌ 削除済み - 使用不可 -while condition { } # パーサーエラー -loop() { } # パーサーエラー -``` - -#### **Peek式(Phase 12.7で追加)** -```nyash -# パターンマッチング風の分岐 -local result = peek value { - "A" => 100, - "B" => 200, - "C" => 300, - else => 0 # else必須 -} - -# 文の形式も可 -peek status { - "error" => { - print("Error occurred") - return null - }, - "success" => { - print("All good") - }, - else => { - print("Unknown status") - } -} -``` - -### **2.4 演算子・式** - -#### **🚀 新実装: 関数オーバーロードシステム** -```nyash -# Rust風トレイトベース演算子(2025-08-10実装完了) -sum = 10 + 20 # IntegerBox + IntegerBox = IntegerBox -concat = "Hi" + " !" # StringBox + StringBox = StringBox -repeat = "Ha" * 3 # StringBox * IntegerBox = "HaHaHa" -mixed = 42 + " answer" # 混合型 → 自動文字列結合フォールバック -``` - -#### **演算子優先順位** -```nyash -result = a + b * c / d - e # 算術演算子は標準的優先順位 -logic = not a and b or c # not > and > or -compare = (x > y) and (z <= w) # 比較は括弧推奨 -``` - -#### **論理演算子** -```nyash -# キーワード版(推奨) -canAccess = level >= 5 and hasKey -isValid = not (isEmpty or hasError) - -# シンボル版(互換) -result = condition && other || fallback # 利用可能だが非推奨 -``` - -#### **特殊演算子(Phase 12.7実装済み)** -```nyash -# ? 演算子 - Result伝播 -local data = readFile(path)? # エラーなら早期return - -# ラムダ式 -local add = fn(x, y) { return x + y } -local double = fn(x) { x * 2 } # 単一式なら省略可 - -# await式 -local result = await asyncTask() -``` - ---- - -## 🏗️ **3. Box構文詳細ガイド** - -### **3.1 Everything is Box 原則** - -```nyash -# すべての値がBox -number = 42 # IntegerBox -text = "hello" # StringBox -flag = true # BoolBox -array = new ArrayBox() # ArrayBox -console = new ConsoleBox() # ConsoleBox - -# 統一的なメソッド呼び出し -print(number.to_string_box().value) # "42" -print(array.length()) # 配列長 -console.log("Everything is Box!") # コンソール出力 -``` - -### **3.2 コンストラクタパターン** - -#### **パラメータ付きコンストラクタ** -```nyash -box Person { - public name: StringBox - public email: StringBox - private age: IntegerBox - - birth(personName, personAge) { # birth構文に統一 - me.name = personName - me.age = personAge - me.email = me.name + "@example.com" # 計算フィールド - } - - # ファクトリーメソッド - static createGuest() { - outbox guest - guest = new Person("Guest", 0) - return guest - } -} - -# 使用例 -person = new Person("Alice", 25) -guest = Person.createGuest() -``` - -### **3.3 継承とインターフェース** - -#### **デリゲーションチェーン** -```nyash -# 基底Box -box Animal { - public name: StringBox - public species: StringBox - - birth(animalName, animalSpecies) { - me.name = animalName - me.species = animalSpecies - } - - speak() { - return me.name + " makes a sound" - } -} - -# デリゲーション -box Dog from Animal { - breed: StringBox # 追加フィールド - - birth(dogName, dogBreed) { - from Animal.birth(dogName, "Canine") # 親コンストラクタ呼び出し - me.breed = dogBreed - } - - override speak() { # 明示的オーバーライド - return me.name + " barks: Woof!" - } -} - -# インターフェース実装 -box Cat from Animal interface Playful { - # Playfulインターフェースの実装必須 -} -``` - -### **3.4 Static Boxパターン** - -#### **名前空間・ユーティリティ** -```nyash -static box MathUtils { - PI: FloatBox - E: FloatBox - - # 注意: static初期化ブロックは未実装 - # 初期化はメソッド内で行う - - add(a, b) { - return a + b - } - - circleArea(radius) { - # 初回アクセスで初期化パターン - if me.PI == null { - me.PI = 3.14159265 - } - return me.PI * radius * radius - } -} - -# 使用法 -area = MathUtils.circleArea(5) -sum = MathUtils.add(10, 20) -``` - -#### **アプリケーションエントリーポイント** -```nyash -# 🎯 推奨: Static Box Main パターン -static box Main { - console: ConsoleBox - result: IntegerBox - - main() { - me.console = new ConsoleBox() - me.console.log("🚀 Starting application...") - - # アプリケーションロジック - me.result = processData() - - return "Application completed successfully" - } -} -``` - ---- - -## 🚀 **4. 最新機能・革新技術** - -### **4.1 Arc Revolution(2025-08-10)** -```nyash -# 全16種類のBox型が統一Arcパターンで実装 -# 完全なスレッドセーフティと高性能を両立 - -array = new ArrayBox() -array.push(10) # スレッドセーフな追加 -array.push(20) -item = array.get(0) # スレッドセーフな取得 - -json = new JSONBox() -json.set("name", "Alice") # 並行安全な操作 -data = json.stringify() # JSON文字列化 -``` - -### **4.2 Rust風トレイトベース演算子(2025-08-10)** -```nyash -# AI大相談会で決定された最適設計 -# 静的・動的ハイブリッドディスパッチによる高性能実現 - -# 整数演算 -result = 100 - 25 # IntegerBox間演算 → IntegerBox -product = 6 * 7 # 高速静的ディスパッチ - -# 文字列操作 -greeting = "Hello" + " World" # 文字列結合 -repeated = "Echo" * 3 # "EchoEchoEcho" - -# 混合型フォールバック -message = "Answer: " + 42 # "Answer: 42" - -# Boolean演算 -boolSum = true + false # 1 (IntegerBox) -``` - -### **4.3 変数宣言厳密化(2025-08-09)** -```nyash -# メモリ安全性・非同期安全性保証システム - -static box Calculator { - private memory: ArrayBox # 必須フィールド宣言 - - calculate() { - local temp # 必須ローカル変数宣言 - temp = me.memory * 2 - return temp - } -} -``` - ---- - -## 🚀 **4.4 Phase 12.7実装済み機能** - -### **Peek式 - パターンマッチング風分岐** -```nyash -# 式として使用(値を返す) -local grade = peek score { - 100 => "Perfect", - 90 => "Excellent", - 80 => "Good", - else => "Needs improvement" -} - -# 文として使用(アクション実行) -peek command { - "save" => { - saveFile() - print("Saved!") - }, - "quit" => { - cleanup() - return - }, - else => print("Unknown command") -} -``` - -### **Continue文** -```nyash -loop(i < 100) { - if i % 2 == 0 { - continue # 偶数をスキップ - } - process(i) -} -``` - -### **フィールド型アノテーション** -```nyash -box Person { - name: StringBox # 型情報を明記(P0では無視) - age: IntegerBox - email # 型なしも可 -} -``` - -### **?演算子(Result伝播)** -```nyash -# ResultBoxのエラーを自動的に早期return -local data = readFile("config.json")? -local parsed = parseJSON(data)? -return parsed.get("version") -``` - -### **Lambda式** -```nyash -# 基本形 -local add = fn(x, y) { return x + y } - -# 単一式の場合(returnは省略可) -local double = fn(x) { x * 2 } - -# 高階関数での使用 -array.map(fn(x) { x * x }) -``` - ---- - -## ⚡ **5. 実装済みBox型ライブラリ** - -### **5.1 基本型** -- `StringBox` - 文字列(split, find, replace, trim等) -- `IntegerBox` - 64bit整数 -- `FloatBox` - 64bit浮動小数点数 -- `BoolBox` - 真偽値 -- `NullBox` - null値 -- `VoidBox` - void値 - -### **5.2 コレクション** -- `ArrayBox` - 動的配列(push, pop, get, set, join等) -- `MapBox` - 連想配列・辞書 - -### **5.3 システム・I/O** -- `ConsoleBox` - コンソール入出力 -- `DebugBox` - デバッグ支援・メモリ追跡 -- `FileBox` - ファイルシステム操作(プラグイン) - -### **5.4 数学・時間** -- `MathBox` - 数学関数(sin, cos, log, sqrt等) -- `TimeBox` - 時刻操作・タイマー -- `RandomBox` - 乱数生成・選択・シャッフル - -### **5.5 データ処理** -- `JSONBox` - JSON解析・生成(parse, stringify, get, set) -- `RegexBox` - 正規表現(test, find, replace, split) -- `BufferBox` - バイナリデータ処理 -- `StreamBox` - ストリーム処理 - -### **5.6 ネットワーク・Web** -- `HttpClientBox` - HTTP通信(プラグイン) -- `WebDisplayBox` - HTML表示(WASM) -- `WebConsoleBox` - ブラウザコンソール(WASM) -- `WebCanvasBox` - Canvas描画(WASM) - -### **5.7 GUI・マルチメディア** -- `EguiBox` - デスクトップGUI(Windows/Linux、プラグイン) -- `SoundBox` - 音声再生 - -### **5.8 特殊用途** -- `FutureBox` - 非同期処理結果 -- `ResultBox` - エラー処理(Ok/Err) -- `TokenBox` - キャンセルトークン -- `FunctionBox` - 第一級関数 -- `P2PBox` - P2P通信(プラグイン) - ---- - -## 🎯 **6. パフォーマンス・デザイン原則** - -### **6.1 メモリ安全性** -- Rust所有権システムによる完全なメモリ安全性 -- Arcによるスレッドセーフな共有状態管理 -- 自動参照カウント + 明示的デストラクタ(fini) - -### **6.2 実行効率** -- 統一されたBox型システムによる最適化 -- 静的・動的ハイブリッドディスパッチで高速演算 -- パーサー無限ループ対策(--debug-fuel) - -### **6.3 開発効率** -- 変数宣言厳密化による早期エラー検出 -- 包括的デバッグ機能(DebugBox) -- 直感的な"Everything is Box"概念 - ---- - -## 📚 **7. 学習パス・ベストプラクティス** - -### **7.1 初心者向け学習順序** -1. **基本概念**: Everything is Box哲学理解 -2. **基本構文**: 変数宣言・制御構文・演算子 -3. **Box定義**: 基本的なクラス作成 -4. **Static Box Main**: アプリケーションエントリーポイント -5. **継承・インターフェース**: オブジェクト指向機能 - -### **7.2 推奨コーディングスタイル** -```nyash -# ✅ 推奨スタイル -static box Main { - public console: ConsoleBox # 公開フィールド明示 - public result: IntegerBox - - main() { - me.console = new ConsoleBox() - - local data # 変数事前宣言 - data = processInput() - - me.result = data # 明確な代入 - return "Success" - } -} -``` - -### **7.3 よくある間違いと対策** -```nyash -# ❌ よくある間違い -public { field1 field2 } # 旧構文 → 使用不可 -x = 42 # 変数未宣言 → ランタイムエラー -while condition { } # 非対応構文 → パーサーエラー -this.field # thisは使用不可 → me.fieldを使用 - -# ✅ 正しい書き方(Phase 12.7後) -field1: TypeBox # フィールド宣言(型は省略可) -field2 # 型なしフィールド -local x = 42 # 事前宣言必須 -loop(condition) { } # 統一ループ構文 -me.field # self参照はmeのみ -``` - ---- - -## 📌 **8. 糖衣構文(Phase 12.7-B)** - -### 実装済み(ゲート: `NYASH_SYNTAX_SUGAR_LEVEL=basic|full`) -```nyash -# パイプライン -result = data |> normalize() |> transform() |> process - -# セーフアクセス + デフォルト -name = user?.profile?.name ?? "guest" - -# 複合代入 -x += 1; y *= 2 - -# 範囲(内部的には Range(a,b)) -loop(i in 1 .. 5) { /* ... */ } -``` - -### 拡張(段階適用予定・設計済み) -```nyash -let {x, y} = point -let [first, second, ...rest] = array -``` - ---- - -**🎉 Nyash 2025は、AI協働設計による最先端言語システムとして、シンプルさと強力さを完全に両立しました。** - -*最終更新: 2025年9月4日 - Phase 12.7実装済み機能の正確な反映* diff --git a/docs/reference/architecture/TECHNICAL_ARCHITECTURE_2025.md b/docs/reference/architecture/TECHNICAL_ARCHITECTURE_2025.md index 3e905f39..31490a69 100644 --- a/docs/reference/architecture/TECHNICAL_ARCHITECTURE_2025.md +++ b/docs/reference/architecture/TECHNICAL_ARCHITECTURE_2025.md @@ -4,9 +4,9 @@ This index points to the currently maintained architectural documents: - Core Concepts: reference/architecture/nyash_core_concepts.md - Execution Backends: reference/architecture/execution-backends.md -- Lowering Contexts: LOWERING_CONTEXTS.md -- LLVM Layer Overview: LLVM_LAYER_OVERVIEW.md +- Lowering Contexts: ../../design/LOWERING_CONTEXTS.md +- LLVM Layer Overview: ../../design/LLVM_LAYER_OVERVIEW.md - VM Overview: VM_README.md -- Cranelift AOT design: backend-cranelift-aot-design.md +- Cranelift AOT design: ../../design/backend-cranelift-aot-design.md Note: Some long-form papers reside under `private/papers/reference/architecture/`. diff --git a/docs/reference/architecture/execution-backends.md b/docs/reference/architecture/execution-backends.md index 143b3dc8..021234e4 100644 --- a/docs/reference/architecture/execution-backends.md +++ b/docs/reference/architecture/execution-backends.md @@ -4,5 +4,5 @@ For the full guide, see: - execution-backends.md (legacy location kept up to date) Additional references: -- backend-llvm-implementation-guide.md +- ../../design/backend-llvm-implementation-guide.md - VM_README.md diff --git a/docs/reference/language/LANGUAGE_REFERENCE_2025.md b/docs/reference/language/LANGUAGE_REFERENCE_2025.md index 8d5cc60c..f0670ed4 100644 --- a/docs/reference/language/LANGUAGE_REFERENCE_2025.md +++ b/docs/reference/language/LANGUAGE_REFERENCE_2025.md @@ -1,6 +1,659 @@ -# Language Reference (2025) +# 🚀 Nyash Language Reference 2025 -The canonical 2025 language reference currently lives here: -- private/papers/reference/language/LANGUAGE_REFERENCE_2025.md +**最終更新: 2025年9月4日 - Phase 12.7実装済み機能の正確な反映** -This stub exists to provide a stable public path under `docs/reference/language/`. +## 📖 概要 + +Nyashは「Everything is Box」哲学に基づく革新的プログラミング言語です。 +Rust製インタープリターによる高性能実行と、直感的な構文により、学習しやすく実用的な言語として完成しました。 + +--- + +## 🔤 **1. 予約語・キーワード完全リスト** + +### **Phase 12.7で確定した15個の予約語** +| 予約語 | 用途 | 例 | +|-------|------|---| +| `box` | クラス定義 | `box MyClass { }` | +| `new` | オブジェクト生成 | `new ConsoleBox()` | +| `me` | 自己参照(thisの代わり) | `me.field = value` | +| `local` | ローカル変数宣言 | `local x, y = 10` | +| `return` | 関数リターン | `return value` | +| `from` | デリゲーション・親メソッド呼び出し | `box Child from Parent` / `from Parent.method()` | +| `birth` | コンストラクタ(統一名) | `birth(param) { }` | +| `static` | 静的Box・関数定義 | `static box Main { }` | +| `if` | 条件分岐 | `if condition { }` | +| `else` | else節 | `else { }` | +| `loop` | ループ(唯一の形式) | `loop(condition) { }` | +| `continue` | ループ継続 | `continue` | +| `peek` | パターンマッチング風分岐 | `peek value { "A" => 1, else => 0 }` | +| `try` | 例外捕獲開始 | `try { }` | +| `interface` | インターフェース定義 | `interface Comparable { }` | + +### **その他の重要キーワード(予約語ではない)** +| キーワード | 用途 | 例 | +|-------|------|---| +| `override` | 明示的オーバーライド | `override speak() { }` | +| `break` | ループ脱出 | `break` | +| `catch` | 例外処理 | `catch (e) { }` | +| `finally` | 最終処理 | `finally { }` | +| `throw` | 例外発生 | `throw error` | +| `nowait` | 非同期実行 | `nowait future = task()` | +| `await` | 待機・結果取得 | `result = await future` | +| `include` | ファイル取り込み | `include "math.nyash"` | +| `print` | 出力(デバッグ用) | `print("Hello")` | +| `function`/`fn` | 関数定義 | `fn add(a,b) { }` | +| `init` | 初期化ブロック | `init { field1, field2 }` | +| `pack` | 旧コンストラクタ(互換性) | `pack(param) { }` | +| `outbox` | 所有権移転変数 | `outbox result = compute()` | +| `global` | グローバル変数 | `global CONFIG = "dev"` | +| `weak` | 弱参照修飾子 | `weak reference` | +| `using` | 名前空間インポート | `using namespace` | + +### **演算子・論理** +| 演算子/キーワード | 用途 | 例 | +|-------|------|---| +| `not` | 論理否定 | `not condition` | +| `and` | 論理積 | `a and b` | +| `or` | 論理和 | `a or b` | +| `true`/`false` | 真偽値 | `flag = true` | +| `null` | null値 | `value = null` | + + +--- + +## 📝 **2. 文法・構文仕様** + +### **2.1 Box定義文法** + +#### **基本Box** +```nyash +box ClassName { + # フィールド宣言(Phase 12.7形式) + field1: TypeBox # フィールド型アノテーション(P0では無視) + field2: TypeBox + field3 # 型なしも可 + + # コンストラクタ + birth(param1, param2) { # birth構文に統一 + me.field1 = param1 + me.field2 = param2 + me.field3 = defaultValue() + } + + # メソッド + methodName(arg1, arg2) { + return me.field1 + arg1 + } + + # デストラクタ(fini) + fini() { + print("Cleanup: " + me.field1) + } +} +``` + +#### **デリゲーションBox** +```nyash +box Child from Parent interface Comparable { + childField: TypeBox # 追加フィールド + + birth(parentParam, childParam) { # birth構文に統一 + from Parent.birth(parentParam) # 親コンストラクタ明示呼び出し + me.childField = childParam + } + + # メソッドオーバーライド + override process(data) { # overrideキーワード必須 + local result = from Parent.process(data) # 親メソッド呼び出し + return result + " (Child processed)" + } + + # インターフェース実装 + compareTo(other) { + return me.value - other.value + } +} +``` + +#### **Static Box(推奨エントリーポイント)** +```nyash +static box Main { + console: ConsoleBox + result: IntegerBox + + main() { + me.console = new ConsoleBox() + me.console.log("🎉 Everything is Box!") + return "Success" + } +} +``` + +### **2.2 変数宣言** + +#### **基本パターン** +```nyash +# 単一宣言 +local x +local name = "初期値" + +# 複数宣言 +local a, b, c +local x = 10, y = 20, z # 混合初期化 + +# 所有権移転(static関数内) +static function Factory.create() { + outbox product # 呼び出し側に所有権移転 + product = new Item() + return product +} +``` + +#### **変数宣言厳密化システム(2025-08-09実装)** +```nyash +# ✅ 正しい - 明示宣言必須 +local temp +temp = 42 + +# ❌ エラー - 未宣言変数への代入 +x = 42 # RuntimeError: 未宣言変数 + 修正提案表示 +``` + +### **2.3 制御構文** + +#### **条件分岐** +```nyash +if condition { + # 処理 +} else if condition2 { + # 処理2 +} else { + # else処理 +} +``` + +#### **ループ(統一構文)** +```nyash +# ✅ 唯一の正しい形式 +loop(condition) { + # ループ本体 + if exitCondition { + break + } + if skipCondition { + continue # Phase 12.7で追加 + } +} + +# ❌ 削除済み - 使用不可 +while condition { } # パーサーエラー +loop() { } # パーサーエラー +``` + +#### **Peek式(Phase 12.7で追加)** +```nyash +# パターンマッチング風の分岐 +local result = peek value { + "A" => 100, + "B" => 200, + "C" => 300, + else => 0 # else必須 +} + +# 文の形式も可 +peek status { + "error" => { + print("Error occurred") + return null + }, + "success" => { + print("All good") + }, + else => { + print("Unknown status") + } +} +``` + +### **2.4 演算子・式** + +#### **🚀 新実装: 関数オーバーロードシステム** +```nyash +# Rust風トレイトベース演算子(2025-08-10実装完了) +sum = 10 + 20 # IntegerBox + IntegerBox = IntegerBox +concat = "Hi" + " !" # StringBox + StringBox = StringBox +repeat = "Ha" * 3 # StringBox * IntegerBox = "HaHaHa" +mixed = 42 + " answer" # 混合型 → 自動文字列結合フォールバック +``` + +#### **演算子優先順位** +```nyash +result = a + b * c / d - e # 算術演算子は標準的優先順位 +logic = not a and b or c # not > and > or +compare = (x > y) and (z <= w) # 比較は括弧推奨 +``` + +#### **論理演算子** +```nyash +# キーワード版(推奨) +canAccess = level >= 5 and hasKey +isValid = not (isEmpty or hasError) + +# シンボル版(互換) +result = condition && other || fallback # 利用可能だが非推奨 +``` + +#### **特殊演算子(Phase 12.7実装済み)** +```nyash +# ? 演算子 - Result伝播 +local data = readFile(path)? # エラーなら早期return + +# ラムダ式 +local add = fn(x, y) { return x + y } +local double = fn(x) { x * 2 } # 単一式なら省略可 + +# await式 +local result = await asyncTask() +``` + +--- + +## 🏗️ **3. Box構文詳細ガイド** + +### **3.1 Everything is Box 原則** + +```nyash +# すべての値がBox +number = 42 # IntegerBox +text = "hello" # StringBox +flag = true # BoolBox +array = new ArrayBox() # ArrayBox +console = new ConsoleBox() # ConsoleBox + +# 統一的なメソッド呼び出し +print(number.to_string_box().value) # "42" +print(array.length()) # 配列長 +console.log("Everything is Box!") # コンソール出力 +``` + +### **3.2 コンストラクタパターン** + +#### **パラメータ付きコンストラクタ** +```nyash +box Person { + public name: StringBox + public email: StringBox + private age: IntegerBox + + birth(personName, personAge) { # birth構文に統一 + me.name = personName + me.age = personAge + me.email = me.name + "@example.com" # 計算フィールド + } + + # ファクトリーメソッド + static createGuest() { + outbox guest + guest = new Person("Guest", 0) + return guest + } +} + +# 使用例 +person = new Person("Alice", 25) +guest = Person.createGuest() +``` + +### **3.3 継承とインターフェース** + +#### **デリゲーションチェーン** +```nyash +# 基底Box +box Animal { + public name: StringBox + public species: StringBox + + birth(animalName, animalSpecies) { + me.name = animalName + me.species = animalSpecies + } + + speak() { + return me.name + " makes a sound" + } +} + +# デリゲーション +box Dog from Animal { + breed: StringBox # 追加フィールド + + birth(dogName, dogBreed) { + from Animal.birth(dogName, "Canine") # 親コンストラクタ呼び出し + me.breed = dogBreed + } + + override speak() { # 明示的オーバーライド + return me.name + " barks: Woof!" + } +} + +# インターフェース実装 +box Cat from Animal interface Playful { + # Playfulインターフェースの実装必須 +} +``` + +### **3.4 Static Boxパターン** + +#### **名前空間・ユーティリティ** +```nyash +static box MathUtils { + PI: FloatBox + E: FloatBox + + # 注意: static初期化ブロックは未実装 + # 初期化はメソッド内で行う + + add(a, b) { + return a + b + } + + circleArea(radius) { + # 初回アクセスで初期化パターン + if me.PI == null { + me.PI = 3.14159265 + } + return me.PI * radius * radius + } +} + +# 使用法 +area = MathUtils.circleArea(5) +sum = MathUtils.add(10, 20) +``` + +#### **アプリケーションエントリーポイント** +```nyash +# 🎯 推奨: Static Box Main パターン +static box Main { + console: ConsoleBox + result: IntegerBox + + main() { + me.console = new ConsoleBox() + me.console.log("🚀 Starting application...") + + # アプリケーションロジック + me.result = processData() + + return "Application completed successfully" + } +} +``` + +--- + +## 🚀 **4. 最新機能・革新技術** + +### **4.1 Arc Revolution(2025-08-10)** +```nyash +# 全16種類のBox型が統一Arcパターンで実装 +# 完全なスレッドセーフティと高性能を両立 + +array = new ArrayBox() +array.push(10) # スレッドセーフな追加 +array.push(20) +item = array.get(0) # スレッドセーフな取得 + +json = new JSONBox() +json.set("name", "Alice") # 並行安全な操作 +data = json.stringify() # JSON文字列化 +``` + +### **4.2 Rust風トレイトベース演算子(2025-08-10)** +```nyash +# AI大相談会で決定された最適設計 +# 静的・動的ハイブリッドディスパッチによる高性能実現 + +# 整数演算 +result = 100 - 25 # IntegerBox間演算 → IntegerBox +product = 6 * 7 # 高速静的ディスパッチ + +# 文字列操作 +greeting = "Hello" + " World" # 文字列結合 +repeated = "Echo" * 3 # "EchoEchoEcho" + +# 混合型フォールバック +message = "Answer: " + 42 # "Answer: 42" + +# Boolean演算 +boolSum = true + false # 1 (IntegerBox) +``` + +### **4.3 変数宣言厳密化(2025-08-09)** +```nyash +# メモリ安全性・非同期安全性保証システム + +static box Calculator { + private memory: ArrayBox # 必須フィールド宣言 + + calculate() { + local temp # 必須ローカル変数宣言 + temp = me.memory * 2 + return temp + } +} +``` + +--- + +## 🚀 **4.4 Phase 12.7実装済み機能** + +### **Peek式 - パターンマッチング風分岐** +```nyash +# 式として使用(値を返す) +local grade = peek score { + 100 => "Perfect", + 90 => "Excellent", + 80 => "Good", + else => "Needs improvement" +} + +# 文として使用(アクション実行) +peek command { + "save" => { + saveFile() + print("Saved!") + }, + "quit" => { + cleanup() + return + }, + else => print("Unknown command") +} +``` + +### **Continue文** +```nyash +loop(i < 100) { + if i % 2 == 0 { + continue # 偶数をスキップ + } + process(i) +} +``` + +### **フィールド型アノテーション** +```nyash +box Person { + name: StringBox # 型情報を明記(P0では無視) + age: IntegerBox + email # 型なしも可 +} +``` + +### **?演算子(Result伝播)** +```nyash +# ResultBoxのエラーを自動的に早期return +local data = readFile("config.json")? +local parsed = parseJSON(data)? +return parsed.get("version") +``` + +### **Lambda式** +```nyash +# 基本形 +local add = fn(x, y) { return x + y } + +# 単一式の場合(returnは省略可) +local double = fn(x) { x * 2 } + +# 高階関数での使用 +array.map(fn(x) { x * x }) +``` + +--- + +## ⚡ **5. 実装済みBox型ライブラリ** + +### **5.1 基本型** +- `StringBox` - 文字列(split, find, replace, trim等) +- `IntegerBox` - 64bit整数 +- `FloatBox` - 64bit浮動小数点数 +- `BoolBox` - 真偽値 +- `NullBox` - null値 +- `VoidBox` - void値 + +### **5.2 コレクション** +- `ArrayBox` - 動的配列(push, pop, get, set, join等) +- `MapBox` - 連想配列・辞書 + +### **5.3 システム・I/O** +- `ConsoleBox` - コンソール入出力 +- `DebugBox` - デバッグ支援・メモリ追跡 +- `FileBox` - ファイルシステム操作(プラグイン) + +### **5.4 数学・時間** +- `MathBox` - 数学関数(sin, cos, log, sqrt等) +- `TimeBox` - 時刻操作・タイマー +- `RandomBox` - 乱数生成・選択・シャッフル + +### **5.5 データ処理** +- `JSONBox` - JSON解析・生成(parse, stringify, get, set) +- `RegexBox` - 正規表現(test, find, replace, split) +- `BufferBox` - バイナリデータ処理 +- `StreamBox` - ストリーム処理 + +### **5.6 ネットワーク・Web** +- `HttpClientBox` - HTTP通信(プラグイン) +- `WebDisplayBox` - HTML表示(WASM) +- `WebConsoleBox` - ブラウザコンソール(WASM) +- `WebCanvasBox` - Canvas描画(WASM) + +### **5.7 GUI・マルチメディア** +- `EguiBox` - デスクトップGUI(Windows/Linux、プラグイン) +- `SoundBox` - 音声再生 + +### **5.8 特殊用途** +- `FutureBox` - 非同期処理結果 +- `ResultBox` - エラー処理(Ok/Err) +- `TokenBox` - キャンセルトークン +- `FunctionBox` - 第一級関数 +- `P2PBox` - P2P通信(プラグイン) + +--- + +## 🎯 **6. パフォーマンス・デザイン原則** + +### **6.1 メモリ安全性** +- Rust所有権システムによる完全なメモリ安全性 +- Arcによるスレッドセーフな共有状態管理 +- 自動参照カウント + 明示的デストラクタ(fini) + +### **6.2 実行効率** +- 統一されたBox型システムによる最適化 +- 静的・動的ハイブリッドディスパッチで高速演算 +- パーサー無限ループ対策(--debug-fuel) + +### **6.3 開発効率** +- 変数宣言厳密化による早期エラー検出 +- 包括的デバッグ機能(DebugBox) +- 直感的な"Everything is Box"概念 + +--- + +## 📚 **7. 学習パス・ベストプラクティス** + +### **7.1 初心者向け学習順序** +1. **基本概念**: Everything is Box哲学理解 +2. **基本構文**: 変数宣言・制御構文・演算子 +3. **Box定義**: 基本的なクラス作成 +4. **Static Box Main**: アプリケーションエントリーポイント +5. **継承・インターフェース**: オブジェクト指向機能 + +### **7.2 推奨コーディングスタイル** +```nyash +# ✅ 推奨スタイル +static box Main { + public console: ConsoleBox # 公開フィールド明示 + public result: IntegerBox + + main() { + me.console = new ConsoleBox() + + local data # 変数事前宣言 + data = processInput() + + me.result = data # 明確な代入 + return "Success" + } +} +``` + +### **7.3 よくある間違いと対策** +```nyash +# ❌ よくある間違い +public { field1 field2 } # 旧構文 → 使用不可 +x = 42 # 変数未宣言 → ランタイムエラー +while condition { } # 非対応構文 → パーサーエラー +this.field # thisは使用不可 → me.fieldを使用 + +# ✅ 正しい書き方(Phase 12.7後) +field1: TypeBox # フィールド宣言(型は省略可) +field2 # 型なしフィールド +local x = 42 # 事前宣言必須 +loop(condition) { } # 統一ループ構文 +me.field # self参照はmeのみ +``` + +--- + +## 📌 **8. 糖衣構文(Phase 12.7-B)** + +### 実装済み(ゲート: `NYASH_SYNTAX_SUGAR_LEVEL=basic|full`) +```nyash +# パイプライン +result = data |> normalize() |> transform() |> process + +# セーフアクセス + デフォルト +name = user?.profile?.name ?? "guest" + +# 複合代入 +x += 1; y *= 2 + +# 範囲(内部的には Range(a,b)) +loop(i in 1 .. 5) { /* ... */ } +``` + +### 拡張(段階適用予定・設計済み) +```nyash +let {x, y} = point +let [first, second, ...rest] = array +``` + +--- + +**🎉 Nyash 2025は、AI協働設計による最先端言語システムとして、シンプルさと強力さを完全に両立しました。** + +*最終更新: 2025年9月4日 - Phase 12.7実装済み機能の正確な反映* diff --git a/docs/reference/language/using.md b/docs/reference/language/using.md index a2c63310..9e6dae73 100644 --- a/docs/reference/language/using.md +++ b/docs/reference/language/using.md @@ -104,3 +104,36 @@ Notes - Phase 15 keeps resolution in the Runner to minimize parser complexity. Future phases may leverage `meta.usings` for compiler decisions. - Unknown fields in the top‑level JSON (like `meta`) are ignored by the current bridge. - 未解決時(非strict)は実行を継続し、`NYASH_RESOLVE_TRACE=1` で候補を提示。strict時はエラーで候補を表示。 + +## Include/Export (Phase 1) + +Simple include expression for file‑scoped modules(Phase 1 提案)。将来は `using`/Runner 解決へ統合予定。 + +Overview +- One file exports one static box. `include(path)` evaluates the file and returns that Box instance. + +Syntax +``` +local Math = include "lib/math.nyash" +local r = Math.add(1, 2) +``` + +Rules +- Single static box per file(0/複数はエラー) +- Expression form: `include(...)` は Box インスタンスを返す式 +- Caching: 同一パスは一度だけ評価(2回目以降はキャッシュ返却) +- Path resolution(MVP): + - Relative allowed; absolute discouraged + - nyash.toml `[include.roots]` で `std=/stdlib` 等のルート定義を許可 + - 省略拡張は `.nyash`、ディレクトリなら `index.nyash` + +Backends +- Interpreter: 実行時に評価し Box を返す +- VM/AOT: MIR Builder が対象ファイルを読み取り、同一 MIR モジュールに static box を降ろす(専用 MIR 命令は追加しない) + +Limitations +- 循環 include の検出/診断は未実装(後続で active-load 追跡と経路表示を追加) + +Rationale +- MIR 仕様に変更を入れず、実用的なモジュール分割を提供 +- Everything‑is‑Box に整合(モジュール=Box、メソッド/フィールド=API) diff --git a/docs/reference/mir/phi_invariants.md b/docs/reference/mir/phi_invariants.md new file mode 100644 index 00000000..4706aba6 --- /dev/null +++ b/docs/reference/mir/phi_invariants.md @@ -0,0 +1,35 @@ +# MIR PHI Invariants + +Scope: Builder/Bridge, PyVM, llvmlite (AOT) + +Goal: Ensure deterministic PHI formation at control-flow merges so that +PyVM and LLVM backends agree for nested short-circuit, loop-if merges, +and chained ternary expressions. + +Invariants +- If-merge ordering: Record incoming as [then, else] in this order when + both branches reach the merge. When a branch is structurally absent, + synthesize a carry-over from the pre-merge value. +- Loop latch snapshot: The latch (backedge) snapshot must be taken after + per-iteration merges (i.e., after any phi binding for variables assigned + in the loop body or nested if). Builder must bind the merged value to the + loop-carried variable map before capturing the end-of-body state. +- Self-carry handling: A PHI with self-carry is allowed only when there is + at least one non-self incoming. At finalize, map self-carry to the most + recent non-self source visible at the predecessor end. + +Representative Cases +- Nested short-circuit: `a && (b || c)` with selective assignments in nested + branches. Expect single-eval per operand and deterministic merge order. +- Loop + if merge: A running sum updated in only one branch inside a while + loop. Expect the latch to capture the phi-merged value, not a pre-merge + temporary. +- Chained ternary: `cond1 ? (cond2 ? x : y) : z`. Expect linearized branches + with merge ordering preserved at each join. + +Diagnostics +- Enable `NYASH_LLVM_TRACE_PHI=1` to record per-block snapshots and PHI + wiring in the LLVM path. +- Bridge verifier may allow `verify_allow_no_phi()` in PHI-off mode, but + the invariants above still apply to resolver synthesis order. + diff --git a/docs/self-hosting.md b/docs/self-hosting.md deleted file mode 100644 index 184fd09a..00000000 --- a/docs/self-hosting.md +++ /dev/null @@ -1,51 +0,0 @@ -# Self‑Hosting Dev — One‑Page Guide (selfhosting‑dev) - -目的 -- Ny → MIR → MIR‑Interp → VM/JIT の自己ホスト経路を最短手順で動かし、安定化する。 -- Cranelift AOT/JIT‑AOT の詳細は別管理(リンク参照)。 - -前提 -- Rust(stable): `cargo --version` -- Bash/grep/rg(ripgrep) -- Linux/WSL/Unix シェル環境(WindowsはWSL推奨、PowerShellのps1も一部あり) - -クイックスタート -1) ビルド(JIT有効) - - `cargo build --release --features cranelift-jit` -2) 最小E2E(VM/JIT, plugins無効) - - `NYASH_DISABLE_PLUGINS=1 ./target/release/nyash --backend vm apps/selfhost-minimal/main.nyash` - - 期待: `Result: 0` -3) スモーク一式(コア) - - `bash tools/jit_smoke.sh` -4) selfhost‑minimal 専用スモーク - - `bash tools/selfhost_vm_smoke.sh` -5) ブートストラップ(任意) - - `bash tools/bootstrap_selfhost_smoke.sh` -6) ラウンドトリップJSON(任意) - - `bash tools/ny_roundtrip_smoke.sh` - -便利フラグ(抜粋) -- `NYASH_DISABLE_PLUGINS=1` : 外部プラグイン無効化でコア安定化 -- `NYASH_CLI_VERBOSE=1` : 実行ログ詳細化 -- `NYASH_JIT_THRESHOLD=1` : JIT 降臨を確実化(ベンチ目的で使用) -- `NYASH_LOAD_NY_PLUGINS=1` : Ny プラグインロード(安定化後に段階的に試験) - -トラブルシュート -- タイムアウト/ハング: `timeout 15s ...` を付ける、`NYASH_CLI_VERBOSE=1` で原因把握 -- プラグイン関連エラー: まず `NYASH_DISABLE_PLUGINS=1` を設定してコア確認 -- パス不一致: すべて repo ルート相対のパスで実行しているか確認 -- ビルドキャッシュ: `cargo clean -p nyash` で個別クリーンを試す - -CI 連携(参考) -- 既存Workflow: `.github/workflows/smoke.yml`(JIT/VMコアを含む) -- ローカル再現: `bash tools/smoke_phase_10_10.sh` ほか、上記スモークを順に実行 - -ブランチ方針/マージ -- 本ブランチは VM/JIT 中心。Cranelift は別ドキュメントへ分離。 -- マージ運用・衝突回避は `docs/CONTRIBUTING-MERGE.md` を参照。 -- Cranelift タスク詳細: `docs/phase-15/cranelift/CRANELIFT_TASKS.md` - -連絡ノート -- 変更は基本 `tools/`, `dev/selfhosting/`, `src/{interpreter,runner,mir,parser}` の最小限に集中。 -- 共有インターフェイス変更時は feature gate 等で互換性を維持。 - diff --git a/docs/smokes.md b/docs/smokes.md deleted file mode 100644 index e2d47d93..00000000 --- a/docs/smokes.md +++ /dev/null @@ -1,28 +0,0 @@ -# Smoke Matrix (Backends) - -Policy -- PyVM: partial coverage only (no async/nowait/await/GC/sync). Used for semantics sanity. -- LLVM: full coverage via llvmlite harness. Preferred path for async/nowait and Stage-3 control-flow. -- JIT: not maintained for these smokes (skip). - -Curated Smokes -- Core (LLVM): `examples/llvm11_core_smoke.nyash` -- Async (LLVM only): - - `apps/tests/async-await-min/main.nyash` - - `apps/tests/async-spawn-instance/main.nyash` - - `apps/tests/async-await-timeout-fixed/main.nyash` (set `NYASH_AWAIT_MAX_MS=100`) - -Runner Scripts -- `tools/smokes/curated_llvm.sh [--phi-off]` - - `--phi-off`: enables `NYASH_MIR_NO_PHI=1` + verifier relaxor. -- Archived (not maintained): see `tools/smokes/archive/` - - `smoke_phase_10_10.sh`, `smoke_vm_jit.sh`, `smoke_async_spawn.sh`, `jit_smoke.sh`, `aot_smoke_cranelift.sh` - - These target JIT/Cranelift-era flows. Use the curated LLVM runner instead. - -Flags -- `NYASH_LLVM_USE_HARNESS=1`: use llvmlite harness for AOT. -- `NYASH_MIR_NO_PHI=1` and `NYASH_VERIFY_ALLOW_NO_PHI=1`: PHI-less MIR (edge-copy) mode. - -Notes -- Marked async smokes should not be run under PyVM/JIT. -- Legacy/archived smokes may exist; curated runner avoids them. diff --git a/docs/spec/include_export.md b/docs/spec/include_export.md deleted file mode 100644 index 7929fa9c..00000000 --- a/docs/spec/include_export.md +++ /dev/null @@ -1,40 +0,0 @@ -Nyash Include/Export Specification (Phase 1) - -Overview -- Goal: Simple, box-first module system for code split and reuse. -- Principle: One file exports one module (Box). include(path) returns that Box. - -Syntax -- Include expression: - local Math = include "lib/math.nyash" - local r = Math.add(1, 2) - -Rules -- One static box per file: The included file must define exactly one static box. Zero or multiple is an error. -- Expression form: include(...) is an expression that evaluates to the included module’s Box instance. -- Caching: The same file is evaluated at most once per run (subsequent includes return the cached module). -- Path resolution: - - Relative: include("./x.nyash") resolves relative to the current project root. - - Roots (nyash.toml): - [include.roots] - std = "./stdlib" - lib = "./lib" - Then include("std/string") -> ./stdlib/string.nyash, include("lib/utils") -> ./lib/utils.nyash - - Extension/index: If extension is omitted, .nyash is appended. If the path is a directory, index.nyash is used. - -Backends -- Interpreter: include is executed at runtime (implemented via execute_include_expr). Returns the Box instance. -- VM/AOT: include is handled by the MIR builder by reading and parsing the target file and lowering its static box into the same MIR module (no new MIR op added). The include expression lowers into a new Box() for the included static box type. -- No new MIR instruction was added; existing instructions (Const/NewBox/BoxCall/etc.) are used. - -Limitations (Phase 1) -- Cycles: Circular includes are not yet reported with a dedicated error path. A follow-up change will add active-load tracking and a clear error message with the cycle path. -- Export box: Reserved for Phase 2. For now, the single static box in the file is the public API. - -Examples -- See examples/include_math.nyash and examples/include_main.nyash. - -Rationale -- Keeps MIR spec stable and relies on build-time module linking for VM/AOT. -- Aligns with Everything-is-Box: modules are Boxes; methods/fields are the API. - diff --git a/src/cli.rs b/src/cli.rs index 4e4ceff0..9406c76d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -190,8 +190,8 @@ impl CliConfig { Arg::new("backend") .long("backend") .value_name("BACKEND") - .help("Choose execution backend: 'interpreter' (default), 'vm', or 'llvm'") - .default_value("interpreter") + .help("Choose execution backend: 'vm' (default), 'llvm', or 'interpreter' (legacy)") + .default_value("vm") ) .arg( Arg::new("verbose") diff --git a/src/config/env.rs b/src/config/env.rs index 3fd29c2c..aec82587 100644 --- a/src/config/env.rs +++ b/src/config/env.rs @@ -108,7 +108,14 @@ pub fn await_max_ms() -> u64 { // ---- MIR PHI-less (edge-copy) mode ---- /// Enable MIR PHI non-generation. Bridge/Builder emit edge copies instead of PHI. pub fn mir_no_phi() -> bool { - std::env::var("NYASH_MIR_NO_PHI").ok().as_deref() == Some("1") + match std::env::var("NYASH_MIR_NO_PHI").ok() { + Some(v) => { + let lv = v.to_ascii_lowercase(); + !(lv == "0" || lv == "false" || lv == "off") + } + // Default: ON for MIR13 stability (PHI generation off by default) + None => true, + } } /// Allow verifier to skip SSA/dominance/merge checks for PHI-less MIR. diff --git a/src/grammar/generated.rs b/src/grammar/generated.rs index 8e402b39..0f04d259 100644 --- a/src/grammar/generated.rs +++ b/src/grammar/generated.rs @@ -1,5 +1,9 @@ // Auto-generated from grammar/unified-grammar.toml -pub static KEYWORDS: &[(&str, &str)] = &[("me", "ME"), ("from", "FROM"), ("loop", "LOOP")]; +pub static KEYWORDS: &[(&str, &str)] = &[ + ("me", "ME"), + ("from", "FROM"), + ("loop", "LOOP"), +]; pub static OPERATORS_ADD_COERCION: &str = "string_priority"; pub static OPERATORS_SUB_COERCION: &str = "numeric_only"; pub static OPERATORS_MUL_COERCION: &str = "numeric_only"; @@ -29,15 +33,37 @@ pub static OPERATORS_DIV_RULES: &[(&str, &str, &str, &str)] = &[ ]; pub fn lookup_keyword(word: &str) -> Option<&'static str> { for (k, t) in KEYWORDS { - if *k == word { - return Some(*t); - } + if *k == word { return Some(*t); } } None } pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[ - "box", "global", "function", "static", "if", "loop", "break", "return", "print", "nowait", - "include", "local", "outbox", "try", "throw", "using", "from", + "box", + "global", + "function", + "static", + "if", + "loop", + "break", + "return", + "print", + "nowait", + "include", + "local", + "outbox", + "try", + "throw", + "using", + "from", ]; -pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &["add", "sub", "mul", "div", "and", "or", "eq", "ne"]; +pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &[ + "add", + "sub", + "mul", + "div", + "and", + "or", + "eq", + "ne", +]; \ No newline at end of file diff --git a/src/interpreter_stub.rs b/src/interpreter_stub.rs new file mode 100644 index 00000000..81386065 --- /dev/null +++ b/src/interpreter_stub.rs @@ -0,0 +1,36 @@ +//! Minimal stubs for legacy interpreter APIs when the feature is disabled. +use crate::ast::ASTNode; +use crate::box_trait::{NyashBox, VoidBox}; + +#[derive(Debug, thiserror::Error)] +pub enum RuntimeError { + #[error("invalid operation: {message}")] + InvalidOperation { message: String }, + #[error("type error: {message}")] + TypeError { message: String }, +} + +#[derive(Debug, Default, Clone)] +pub struct SharedState; +impl SharedState { pub fn new() -> Self { Self } } + +#[derive(Debug, Default)] +pub struct NyashInterpreter; +impl NyashInterpreter { + pub fn new() -> Self { Self } + pub fn with_shared(_s: SharedState) -> Self { Self } + pub fn execute(&mut self, _ast: ASTNode) -> Result, RuntimeError> { + Ok(Box::new(VoidBox::new())) + } + pub fn execute_expression(&mut self, _expr: &ASTNode) -> Result, RuntimeError> { + Ok(Box::new(VoidBox::new())) + } +} + +pub fn run_function_box( + _fun: &crate::boxes::function_box::FunctionBox, + _args: Vec>, +) -> Result, RuntimeError> { + Ok(Box::new(VoidBox::new())) +} + diff --git a/src/lib.rs b/src/lib.rs index 93b3037c..654f38fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,10 @@ #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; +// Provide stubs when legacy interpreter is disabled +#[cfg(not(feature = "interpreter-legacy"))] +mod interpreter_stub; + pub mod ast; // Using old ast.rs for now pub mod box_arithmetic; pub mod box_factory; // 🏭 Unified Box Factory Architecture (Phase 9.78) @@ -20,7 +24,10 @@ pub mod environment; pub mod exception_box; pub mod finalization; pub mod instance_v2; // 🎯 Phase 9.78d: Simplified InstanceBox implementation +#[cfg(feature = "interpreter-legacy")] pub mod interpreter; +#[cfg(not(feature = "interpreter-legacy"))] +pub mod interpreter { pub use crate::interpreter_stub::*; } pub mod method_box; pub mod operator_traits; // 🚀 Rust-style trait-based operator overloading pub mod parser; // Using old parser.rs for now @@ -79,6 +86,8 @@ pub use ast::{ASTNode, BinaryOperator, LiteralValue}; pub use box_arithmetic::{AddBox, CompareBox, DivideBox, ModuloBox, MultiplyBox, SubtractBox}; pub use box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox}; pub use environment::{Environment, PythonCompatEnvironment}; +#[cfg(feature = "interpreter-legacy")] +#[cfg(feature = "interpreter-legacy")] pub use interpreter::{NyashInterpreter, RuntimeError}; pub use parser::{NyashParser, ParseError}; pub use tokenizer::{NyashTokenizer, Token, TokenType}; @@ -100,17 +109,17 @@ pub use method_box::{BoxType, EphemeralInstance, FunctionDefinition, MethodBox}; pub use value::NyashValue; // Direct canvas test export -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", feature = "interpreter-legacy"))] pub use wasm_test::wasm_test::test_direct_canvas_draw; // 🌐 WebAssembly exports for browser usage -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", feature = "interpreter-legacy"))] #[wasm_bindgen] pub struct NyashWasm { interpreter: NyashInterpreter, } -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", feature = "interpreter-legacy"))] #[wasm_bindgen] impl NyashWasm { /// Create a new Nyash interpreter instance for browser use diff --git a/src/llvm_py/README.md b/src/llvm_py/README.md index b673ed15..8de8f390 100644 --- a/src/llvm_py/README.md +++ b/src/llvm_py/README.md @@ -2,7 +2,7 @@ ## 📝 概要 Rust/inkwellの複雑性を回避し、llvmliteを使ってシンプルに実装する実験的バックエンド。 -ChatGPTが設計した`docs/LLVM_LAYER_OVERVIEW.md`の設計原則に従う。 +ChatGPTが設計した`docs/design/LLVM_LAYER_OVERVIEW.md`の設計原則に従う。 ## 🎯 目的 1. **検証ハーネス** - PHI/SSA構造の高速検証 @@ -47,4 +47,4 @@ NYASH_LLVM_USE_HARNESS=1 ./target/release/nyash program.nyash - 全体: 800-1000行 - コア実装: 300-400行 -「簡単最高」の精神を体現! \ No newline at end of file +「簡単最高」の精神を体現! diff --git a/src/llvm_py/llvm_builder.py b/src/llvm_py/llvm_builder.py index bb3f23ff..5782f1b2 100644 --- a/src/llvm_py/llvm_builder.py +++ b/src/llvm_py/llvm_builder.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ Nyash LLVM Python Backend - Main Builder -Following the design principles in docs/LLVM_LAYER_OVERVIEW.md +Following the design principles in docs/design/LLVM_LAYER_OVERVIEW.md """ import json diff --git a/src/llvm_py/resolver.py b/src/llvm_py/resolver.py index ef992155..0f132ad2 100644 --- a/src/llvm_py/resolver.py +++ b/src/llvm_py/resolver.py @@ -10,7 +10,7 @@ import llvmlite.ir as ir class Resolver: """ Centralized value resolution with per-block caching. - Following the Core Invariants from LLVM_LAYER_OVERVIEW.md: + Following the Core Invariants from docs/design/LLVM_LAYER_OVERVIEW.md: - Resolver-only reads - Localize at block start (PHI creation) - Cache per (block, value) to avoid redundant PHIs @@ -121,24 +121,10 @@ class Resolver: self.i64_cache[cache_key] = existing return existing else: - # Prefer a directly available SSA value from vmap(同一ブロック直前定義の再利用)。 - # def_blocks が未更新でも、vmap に存在するなら局所定義とみなす。 - try: - existing = vmap.get(value_id) - except Exception: - existing = None - if existing is not None and hasattr(existing, 'type') and isinstance(existing.type, ir.IntType): - if existing.type.width == 64: - if os.environ.get('NYASH_LLVM_TRACE_VALUES') == '1': - print(f"[resolve] vmap-fast reuse: bb{bid} v{value_id}", flush=True) - self.i64_cache[cache_key] = existing - return existing - else: - zextd = self.builder.zext(existing, self.i64) if self.builder is not None else ir.Constant(self.i64, 0) - if os.environ.get('NYASH_LLVM_TRACE_VALUES') == '1': - print(f"[resolve] vmap-fast zext: bb{bid} v{value_id}", flush=True) - self.i64_cache[cache_key] = zextd - return zextd + # Do NOT blindly reuse vmap across blocks: it may reference values defined + # in non-dominating predecessors (e.g., other branches). Only reuse when + # defined_here (handled above) or at entry/no-preds (handled below). + pass if not pred_ids: # Entry block or no predecessors: prefer local vmap value (already dominating) @@ -182,8 +168,9 @@ class Resolver: return coerced else: # Multi-pred: if JSON declares a PHI for (current block, value_id), - # materialize it on-demand via end-of-block resolver. Otherwise, avoid - # synthesizing a localization PHI (return zero to preserve dominance). + # materialize it on-demand via end-of-block resolver. Otherwise, + # synthesize a localization PHI at the current block head to ensure + # dominance for downstream uses (MIR13 PHI-off compatibility). try: cur_bid = int(str(current_block.name).replace('bb','')) except Exception: @@ -203,9 +190,37 @@ class Resolver: placeholder = vmap.get(value_id) result = placeholder if (placeholder is not None and hasattr(placeholder, 'add_incoming')) else ir.Constant(self.i64, 0) else: - if os.environ.get('NYASH_LLVM_TRACE_PHI') == '1': - print(f"[resolve] multi-pred no-declare: bb{cur_bid} v{value_id} -> 0", flush=True) - result = ir.Constant(self.i64, 0) + # Synthesize a PHI to localize the value and dominate its uses. + try: + bb = bb_map.get(cur_bid) if isinstance(bb_map, dict) else current_block + except Exception: + bb = current_block + b = ir.IRBuilder(bb) + try: + b.position_at_start(bb) + except Exception: + pass + existing = vmap.get(value_id) + if existing is not None and hasattr(existing, 'add_incoming'): + phi = existing + else: + phi = b.phi(self.i64, name=f"res_phi_{value_id}_{cur_bid}") + vmap[value_id] = phi + # Wire end-of-block values from each predecessor + for pred_bid in pred_ids: + try: + pred_bb = bb_map.get(pred_bid) if isinstance(bb_map, dict) else None + except Exception: + pred_bb = None + val = self._value_at_end_i64(value_id, pred_bid, preds, block_end_values, vmap, bb_map) + if pred_bb is None: + # If we cannot map to a real basic block (shouldn't happen), + # fallback to a zero to keep IR consistent. + val = val if hasattr(val, 'type') else ir.Constant(self.i64, 0) + # llvmlite requires a real BasicBlock; skip if missing. + continue + phi.add_incoming(val, pred_bb) + result = phi # Cache and return self.i64_cache[cache_key] = result diff --git a/src/mir/builder.rs b/src/mir/builder.rs index 62dfbbb0..58c06c1a 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -25,6 +25,8 @@ mod fields; // field access/assignment lowering split pub(crate) mod loops; mod ops; mod phi; +mod lifecycle; // prepare/lower_root/finalize split +// legacy large-match remains inline for now (planned extraction) mod plugin_sigs; // plugin signature loader mod stmts; mod utils; @@ -128,95 +130,7 @@ impl MirBuilder { self.finalize_module(result_value) } - fn prepare_module(&mut self) -> Result<(), String> { - let module = MirModule::new("main".to_string()); - let main_signature = FunctionSignature { - name: "main".to_string(), - params: vec![], - return_type: MirType::Void, - effects: EffectMask::PURE, - }; - - let entry_block = self.block_gen.next(); - let mut main_function = MirFunction::new(main_signature, entry_block); - main_function.metadata.is_entry_point = true; - - self.current_module = Some(module); - self.current_function = Some(main_function); - self.current_block = Some(entry_block); - - if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY") - .ok() - .as_deref() - == Some("1") - { - self.emit_instruction(MirInstruction::Safepoint)?; - } - - Ok(()) - } - - fn lower_root(&mut self, ast: ASTNode) -> Result { - self.build_expression(ast) - } - - fn finalize_module(&mut self, result_value: ValueId) -> Result { - if let Some(block_id) = self.current_block { - if let Some(ref mut function) = self.current_function { - if let Some(block) = function.get_block_mut(block_id) { - if !block.is_terminated() { - block.add_instruction(MirInstruction::Return { - value: Some(result_value), - }); - } - if let Some(mt) = self.value_types.get(&result_value).cloned() { - function.signature.return_type = mt; - } - } - } - } - - let mut module = self.current_module.take().unwrap(); - let mut function = self.current_function.take().unwrap(); - function.metadata.value_types = self.value_types.clone(); - if matches!( - function.signature.return_type, - super::MirType::Void | super::MirType::Unknown - ) { - let mut inferred: Option = None; - 'outer: for (_bid, bb) in function.blocks.iter() { - for inst in bb.instructions.iter() { - if let super::MirInstruction::Return { value: Some(v) } = inst { - if let Some(mt) = self.value_types.get(v).cloned() { - inferred = Some(mt); - break 'outer; - } - if let Some(mt) = phi::infer_type_from_phi(&function, *v, &self.value_types) - { - inferred = Some(mt); - break 'outer; - } - } - } - if let Some(super::MirInstruction::Return { value: Some(v) }) = &bb.terminator { - if let Some(mt) = self.value_types.get(v).cloned() { - inferred = Some(mt); - break; - } - if let Some(mt) = phi::infer_type_from_phi(&function, *v, &self.value_types) { - inferred = Some(mt); - break; - } - } - } - if let Some(mt) = inferred { - function.signature.return_type = mt; - } - } - module.add_function(function); - - Ok(module) - } + // prepare_module/lower_root/finalize_module moved to builder/lifecycle.rs /// Build an expression and return its value ID pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result { diff --git a/src/mir/builder/builder_calls.rs b/src/mir/builder/builder_calls.rs index d9103a88..2580a70d 100644 --- a/src/mir/builder/builder_calls.rs +++ b/src/mir/builder/builder_calls.rs @@ -77,19 +77,33 @@ impl super::MirBuilder { Ok(void_id) } }; - return Some(match (iface, m) { - ("future", "delay") => extern_call("env.future", "delay", EffectMask::READ.add(Effect::Io), true), - ("task", "currentToken") => extern_call("env.task", "currentToken", EffectMask::READ, true), - ("task", "cancelCurrent") => extern_call("env.task", "cancelCurrent", EffectMask::IO, false), - ("console", "log") => extern_call("env.console", "log", EffectMask::IO, false), - ("console", "readLine") => extern_call("env.console", "readLine", EffectMask::IO, true), - ("canvas", m) if matches!(m, "fillRect" | "fillText") => extern_call("env.canvas", m, EffectMask::IO, false), - _ => return None, - }); + if let Some((iface_name, method_name, effects, returns)) = + Self::get_env_method_spec(iface, m) + { + return Some(extern_call(&iface_name, &method_name, effects, returns)); + } + return None; } None } + /// Table-like spec for env.* methods. Returns iface_name, method_name, effects, returns. + fn get_env_method_spec( + iface: &str, + method: &str, + ) -> Option<(String, String, EffectMask, bool)> { + // This match is the table. Keep it small and explicit. + match (iface, method) { + ("future", "delay") => Some(("env.future".to_string(), "delay".to_string(), EffectMask::READ.add(Effect::Io), true)), + ("task", "currentToken") => Some(("env.task".to_string(), "currentToken".to_string(), EffectMask::READ, true)), + ("task", "cancelCurrent") => Some(("env.task".to_string(), "cancelCurrent".to_string(), EffectMask::IO, false)), + ("console", "log") => Some(("env.console".to_string(), "log".to_string(), EffectMask::IO, false)), + ("console", "readLine") => Some(("env.console".to_string(), "readLine".to_string(), EffectMask::IO, true)), + ("canvas", m) if matches!(m, "fillRect" | "fillText") => Some(("env.canvas".to_string(), method.to_string(), EffectMask::IO, false)), + _ => None, + } + } + /// Try direct static call for `me` in static box fn try_handle_me_direct_call( &mut self, diff --git a/src/mir/builder/lifecycle.rs b/src/mir/builder/lifecycle.rs new file mode 100644 index 00000000..8744707e --- /dev/null +++ b/src/mir/builder/lifecycle.rs @@ -0,0 +1,109 @@ +use super::{ + BasicBlockIdGenerator, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, + MirType, ValueId, +}; +use crate::ast::ASTNode; + +// Lifecycle routines extracted from builder.rs +impl super::MirBuilder { + pub(super) fn prepare_module(&mut self) -> Result<(), String> { + let module = MirModule::new("main".to_string()); + let main_signature = FunctionSignature { + name: "main".to_string(), + params: vec![], + return_type: MirType::Void, + effects: EffectMask::PURE, + }; + + let entry_block = self.block_gen.next(); + let mut main_function = MirFunction::new(main_signature, entry_block); + main_function.metadata.is_entry_point = true; + + self.current_module = Some(module); + self.current_function = Some(main_function); + self.current_block = Some(entry_block); + + if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY") + .ok() + .as_deref() + == Some("1") + { + self.emit_instruction(MirInstruction::Safepoint)?; + } + + Ok(()) + } + + pub(super) fn lower_root(&mut self, ast: ASTNode) -> Result { + self.build_expression(ast) + } + + pub(super) fn finalize_module( + &mut self, + result_value: ValueId, + ) -> Result { + if let Some(block_id) = self.current_block { + if let Some(ref mut function) = self.current_function { + if let Some(block) = function.get_block_mut(block_id) { + if !block.is_terminated() { + block.add_instruction(MirInstruction::Return { + value: Some(result_value), + }); + } + if let Some(mt) = self.value_types.get(&result_value).cloned() { + function.signature.return_type = mt; + } + } + } + } + + let mut module = self.current_module.take().unwrap(); + let mut function = self.current_function.take().unwrap(); + function.metadata.value_types = self.value_types.clone(); + if matches!( + function.signature.return_type, + super::MirType::Void | super::MirType::Unknown + ) { + let mut inferred: Option = None; + 'outer: for (_bid, bb) in function.blocks.iter() { + for inst in bb.instructions.iter() { + if let super::MirInstruction::Return { value: Some(v) } = inst { + if let Some(mt) = self.value_types.get(v).cloned() { + inferred = Some(mt); + break 'outer; + } + if let Some(mt) = super::phi::infer_type_from_phi( + &function, + *v, + &self.value_types, + ) { + inferred = Some(mt); + break 'outer; + } + } + } + if let Some(super::MirInstruction::Return { value: Some(v) }) = &bb.terminator { + if let Some(mt) = self.value_types.get(v).cloned() { + inferred = Some(mt); + break; + } + if let Some(mt) = super::phi::infer_type_from_phi( + &function, + *v, + &self.value_types, + ) { + inferred = Some(mt); + break; + } + } + } + if let Some(mt) = inferred { + function.signature.return_type = mt; + } + } + module.add_function(function); + + Ok(module) + } +} + diff --git a/src/mir/instruction.rs b/src/mir/instruction.rs index 3bb21796..1c3b4ba9 100644 --- a/src/mir/instruction.rs +++ b/src/mir/instruction.rs @@ -8,6 +8,9 @@ use super::{Effect, EffectMask, ValueId}; // use crate::value::NyashValue; // Commented out to avoid circular dependency use std::fmt; +// Kind-specific metadata (non-functional refactor scaffolding) +use crate::mir::instruction_kinds as inst_meta; + /// MIR instruction types - limited to 20 core instructions #[derive(Debug, Clone, PartialEq)] pub enum MirInstruction { @@ -362,6 +365,9 @@ pub enum MirType { impl MirInstruction { /// Get the effect mask for this instruction pub fn effects(&self) -> EffectMask { + if let Some(eff) = inst_meta::effects_via_meta(self) { + return eff; + } match self { // Pure operations MirInstruction::Const { .. } @@ -436,6 +442,9 @@ impl MirInstruction { /// Get the destination ValueId if this instruction produces a value pub fn dst_value(&self) -> Option { + if let Some(dst) = inst_meta::dst_via_meta(self) { + return Some(dst); + } match self { MirInstruction::Const { dst, .. } | MirInstruction::BinOp { dst, .. } @@ -487,6 +496,9 @@ impl MirInstruction { /// Get all ValueIds used by this instruction pub fn used_values(&self) -> Vec { + if let Some(used) = inst_meta::used_via_meta(self) { + return used; + } match self { MirInstruction::Const { .. } | MirInstruction::Jump { .. } | MirInstruction::Nop => { Vec::new() diff --git a/src/mir/instruction_kinds/mod.rs b/src/mir/instruction_kinds/mod.rs new file mode 100644 index 00000000..af5a31f8 --- /dev/null +++ b/src/mir/instruction_kinds/mod.rs @@ -0,0 +1,673 @@ +//! Kind-specific instruction metadata (PoC) used to gradually +//! move large enum matches to small, testable structs. +//! +//! Non-functional: only mirrors data for selected instructions and +//! provides introspection (effects/dst/used). Core behavior remains +//! in `MirInstruction`. + +use super::{BasicBlockId, ConstValue, Effect, EffectMask, ValueId}; +use crate::mir::instruction::{ + BarrierOp as MirBarrierOp, BinaryOp as MirBinOp, MirInstruction, MirType, + TypeOpKind as MirTypeOpKind, WeakRefOp as MirWeakRefOp, +}; + +pub trait InstructionMeta { + fn effects(&self) -> EffectMask; + fn dst(&self) -> Option; + fn used(&self) -> Vec; +} + +// ---- Const ---- +#[derive(Debug, Clone)] +pub struct ConstInst { + pub dst: ValueId, + pub value: ConstValue, +} + +impl ConstInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Const { dst, value } => Some(ConstInst { dst: *dst, value: value.clone() }), + _ => None, + } + } +} + +impl InstructionMeta for ConstInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { Vec::new() } +} + +// ---- BinOp ---- +#[derive(Debug, Clone)] +pub struct BinOpInst { + pub dst: ValueId, + pub op: MirBinOp, + pub lhs: ValueId, + pub rhs: ValueId, +} + +impl BinOpInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::BinOp { dst, op, lhs, rhs } => Some(BinOpInst { dst: *dst, op: *op, lhs: *lhs, rhs: *rhs }), + _ => None, + } + } +} + +impl InstructionMeta for BinOpInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.lhs, self.rhs] } +} + +// ---- Helper delegation for MirInstruction methods ---- + +pub fn effects_via_meta(i: &MirInstruction) -> Option { + if let Some(k) = ConstInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = BinOpInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = UnaryOpInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = CompareInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = LoadInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = CastInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = TypeOpInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = ArrayGetInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = PhiInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = NewBoxInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = StoreInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = ArraySetInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = ReturnInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = BranchInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = JumpInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = PrintInst::from_mir(i) { return Some(k.effects()); } + if let Some(k) = DebugInst::from_mir(i) { return Some(k.effects()); } + None +} + +pub fn dst_via_meta(i: &MirInstruction) -> Option { + if let Some(k) = ConstInst::from_mir(i) { return k.dst(); } + if let Some(k) = BinOpInst::from_mir(i) { return k.dst(); } + if let Some(k) = UnaryOpInst::from_mir(i) { return k.dst(); } + if let Some(k) = CompareInst::from_mir(i) { return k.dst(); } + if let Some(k) = LoadInst::from_mir(i) { return k.dst(); } + if let Some(k) = CastInst::from_mir(i) { return k.dst(); } + if let Some(k) = TypeOpInst::from_mir(i) { return k.dst(); } + if let Some(k) = ArrayGetInst::from_mir(i) { return k.dst(); } + if let Some(k) = PhiInst::from_mir(i) { return k.dst(); } + if let Some(k) = NewBoxInst::from_mir(i) { return k.dst(); } + if let Some(_k) = StoreInst::from_mir(i) { return None; } + if let Some(_k) = ArraySetInst::from_mir(i) { return None; } + if let Some(_k) = ReturnInst::from_mir(i) { return None; } + if let Some(_k) = BranchInst::from_mir(i) { return None; } + if let Some(_k) = JumpInst::from_mir(i) { return None; } + if let Some(_k) = PrintInst::from_mir(i) { return None; } + if let Some(_k) = DebugInst::from_mir(i) { return None; } + if let Some(k) = CallLikeInst::from_mir(i) { return k.dst(); } + None +} + +pub fn used_via_meta(i: &MirInstruction) -> Option> { + if let Some(k) = ConstInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = BinOpInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = UnaryOpInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = CompareInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = LoadInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = CastInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = TypeOpInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = ArrayGetInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = PhiInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = NewBoxInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = StoreInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = ArraySetInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = ReturnInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = BranchInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = JumpInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = PrintInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = DebugInst::from_mir(i) { return Some(k.used()); } + if let Some(k) = CallLikeInst::from_mir(i) { return Some(k.used()); } + None +} + +// ---- BarrierRead ---- +#[derive(Debug, Clone, Copy)] +pub struct BarrierReadInst { pub ptr: ValueId } + +impl BarrierReadInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::BarrierRead { ptr } => Some(BarrierReadInst { ptr: *ptr }), _ => None } + } +} + +impl InstructionMeta for BarrierReadInst { + fn effects(&self) -> EffectMask { EffectMask::READ.add(Effect::Barrier) } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.ptr] } +} + +// ---- BarrierWrite ---- +#[derive(Debug, Clone, Copy)] +pub struct BarrierWriteInst { pub ptr: ValueId } + +impl BarrierWriteInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::BarrierWrite { ptr } => Some(BarrierWriteInst { ptr: *ptr }), _ => None } + } +} + +impl InstructionMeta for BarrierWriteInst { + fn effects(&self) -> EffectMask { EffectMask::WRITE.add(Effect::Barrier) } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.ptr] } +} + +// ---- Barrier (unified) ---- +#[derive(Debug, Clone, Copy)] +pub struct BarrierInst { pub op: MirBarrierOp, pub ptr: ValueId } + +impl BarrierInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::Barrier { op, ptr } => Some(BarrierInst { op: *op, ptr: *ptr }), _ => None } + } +} + +impl InstructionMeta for BarrierInst { + fn effects(&self) -> EffectMask { + match self.op { + MirBarrierOp::Read => EffectMask::READ.add(Effect::Barrier), + MirBarrierOp::Write => EffectMask::WRITE.add(Effect::Barrier), + } + } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.ptr] } +} + +// ---- Ref ops ---- +#[derive(Debug, Clone, Copy)] +pub struct RefNewInst { pub dst: ValueId, pub box_val: ValueId } +impl RefNewInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::RefNew { dst, box_val } => Some(RefNewInst { dst: *dst, box_val: *box_val }), _ => None } + } +} +impl InstructionMeta for RefNewInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.box_val] } +} + +#[derive(Debug, Clone, Copy)] +pub struct RefGetInst { pub dst: ValueId, pub reference: ValueId } +impl RefGetInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::RefGet { dst, reference, .. } => Some(RefGetInst { dst: *dst, reference: *reference }), _ => None } + } +} +impl InstructionMeta for RefGetInst { + fn effects(&self) -> EffectMask { EffectMask::READ } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.reference] } +} + +#[derive(Debug, Clone, Copy)] +pub struct RefSetInst { pub reference: ValueId, pub value: ValueId } +impl RefSetInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::RefSet { reference, value, .. } => Some(RefSetInst { reference: *reference, value: *value }), _ => None } + } +} +impl InstructionMeta for RefSetInst { + fn effects(&self) -> EffectMask { EffectMask::WRITE } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.reference, self.value] } +} + +// ---- Weak ops ---- +#[derive(Debug, Clone, Copy)] +pub struct WeakNewInst { pub dst: ValueId, pub box_val: ValueId } +impl WeakNewInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::WeakNew { dst, box_val } => Some(WeakNewInst { dst: *dst, box_val: *box_val }), _ => None } + } +} +impl InstructionMeta for WeakNewInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.box_val] } +} + +#[derive(Debug, Clone, Copy)] +pub struct WeakLoadInst { pub dst: ValueId, pub weak_ref: ValueId } +impl WeakLoadInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::WeakLoad { dst, weak_ref } => Some(WeakLoadInst { dst: *dst, weak_ref: *weak_ref }), _ => None } + } +} +impl InstructionMeta for WeakLoadInst { + fn effects(&self) -> EffectMask { EffectMask::READ } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.weak_ref] } +} + +#[derive(Debug, Clone, Copy)] +pub struct WeakRefInst { pub dst: ValueId, pub op: MirWeakRefOp, pub value: ValueId } +impl WeakRefInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::WeakRef { dst, op, value } => Some(WeakRefInst { dst: *dst, op: *op, value: *value }), _ => None } + } +} +impl InstructionMeta for WeakRefInst { + fn effects(&self) -> EffectMask { + match self.op { + MirWeakRefOp::New => EffectMask::PURE, + MirWeakRefOp::Load => EffectMask::READ, + } + } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.value] } +} + +// ---- Future ops ---- +#[derive(Debug, Clone, Copy)] +pub struct FutureNewInst { pub dst: ValueId, pub value: ValueId } +impl FutureNewInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::FutureNew { dst, value } => Some(FutureNewInst { dst: *dst, value: *value }), _ => None } + } +} +impl InstructionMeta for FutureNewInst { + fn effects(&self) -> EffectMask { EffectMask::PURE.add(Effect::Alloc) } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.value] } +} + +#[derive(Debug, Clone, Copy)] +pub struct FutureSetInst { pub future: ValueId, pub value: ValueId } +impl FutureSetInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::FutureSet { future, value } => Some(FutureSetInst { future: *future, value: *value }), _ => None } + } +} +impl InstructionMeta for FutureSetInst { + fn effects(&self) -> EffectMask { EffectMask::WRITE } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.future, self.value] } +} + +#[derive(Debug, Clone, Copy)] +pub struct AwaitInst { pub dst: ValueId, pub future: ValueId } +impl AwaitInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::Await { dst, future } => Some(AwaitInst { dst: *dst, future: *future }), _ => None } + } +} +impl InstructionMeta for AwaitInst { + fn effects(&self) -> EffectMask { EffectMask::READ.add(Effect::Async) } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.future] } +} + +// ---- UnaryOp ---- +#[derive(Debug, Clone, Copy)] +pub struct UnaryOpInst { + pub dst: ValueId, + pub operand: ValueId, +} + +impl UnaryOpInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::UnaryOp { dst, operand, .. } => Some(UnaryOpInst { dst: *dst, operand: *operand }), + _ => None, + } + } +} + +impl InstructionMeta for UnaryOpInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.operand] } +} + +// ---- Compare ---- +#[derive(Debug, Clone, Copy)] +pub struct CompareInst { + pub dst: ValueId, + pub lhs: ValueId, + pub rhs: ValueId, +} + +impl CompareInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Compare { dst, lhs, rhs, .. } => Some(CompareInst { dst: *dst, lhs: *lhs, rhs: *rhs }), + _ => None, + } + } +} + +impl InstructionMeta for CompareInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.lhs, self.rhs] } +} + +// ---- Load ---- +#[derive(Debug, Clone, Copy)] +pub struct LoadInst { + pub dst: ValueId, + pub ptr: ValueId, +} + +impl LoadInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Load { dst, ptr } => Some(LoadInst { dst: *dst, ptr: *ptr }), + _ => None, + } + } +} + +impl InstructionMeta for LoadInst { + fn effects(&self) -> EffectMask { EffectMask::READ } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.ptr] } +} + +// ---- Cast ---- +#[derive(Debug, Clone)] +pub struct CastInst { + pub dst: ValueId, + pub value: ValueId, + pub target_type: MirType, +} + +impl CastInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Cast { dst, value, target_type } => + Some(CastInst { dst: *dst, value: *value, target_type: target_type.clone() }), + _ => None, + } + } +} + +impl InstructionMeta for CastInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.value] } +} + +// ---- TypeOp ---- +#[derive(Debug, Clone)] +pub struct TypeOpInst { + pub dst: ValueId, + pub op: MirTypeOpKind, + pub value: ValueId, + pub ty: MirType, +} + +impl TypeOpInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::TypeOp { dst, op, value, ty } => + Some(TypeOpInst { dst: *dst, op: *op, value: *value, ty: ty.clone() }), + _ => None, + } + } +} + +impl InstructionMeta for TypeOpInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.value] } +} + +// ---- ArrayGet ---- +#[derive(Debug, Clone, Copy)] +pub struct ArrayGetInst { + pub dst: ValueId, + pub array: ValueId, + pub index: ValueId, +} + +impl ArrayGetInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::ArrayGet { dst, array, index } => + Some(ArrayGetInst { dst: *dst, array: *array, index: *index }), + _ => None, + } + } +} + +impl InstructionMeta for ArrayGetInst { + fn effects(&self) -> EffectMask { EffectMask::READ } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { vec![self.array, self.index] } +} + +// ---- Phi ---- +#[derive(Debug, Clone)] +pub struct PhiInst { pub dst: ValueId, pub inputs: Vec<(BasicBlockId, ValueId)> } + +impl PhiInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Phi { dst, inputs } => Some(PhiInst { dst: *dst, inputs: inputs.clone() }), + _ => None, + } + } +} + +impl InstructionMeta for PhiInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { self.inputs.iter().map(|(_, v)| *v).collect() } +} + +// ---- NewBox ---- +#[derive(Debug, Clone)] +pub struct NewBoxInst { + pub dst: ValueId, + pub args: Vec, +} + +impl NewBoxInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::NewBox { dst, args, .. } => + Some(NewBoxInst { dst: *dst, args: args.clone() }), + _ => None, + } + } +} + +impl InstructionMeta for NewBoxInst { + fn effects(&self) -> EffectMask { EffectMask::PURE.add(Effect::Alloc) } + fn dst(&self) -> Option { Some(self.dst) } + fn used(&self) -> Vec { self.args.clone() } +} + +// ---- Store ---- +#[derive(Debug, Clone, Copy)] +pub struct StoreInst { + pub value: ValueId, + pub ptr: ValueId, +} + +impl StoreInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Store { value, ptr } => Some(StoreInst { value: *value, ptr: *ptr }), + _ => None, + } + } +} + +impl InstructionMeta for StoreInst { + fn effects(&self) -> EffectMask { EffectMask::WRITE } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.value, self.ptr] } +} + +// ---- ArraySet ---- +#[derive(Debug, Clone, Copy)] +pub struct ArraySetInst { + pub array: ValueId, + pub index: ValueId, + pub value: ValueId, +} + +impl ArraySetInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::ArraySet { array, index, value } => + Some(ArraySetInst { array: *array, index: *index, value: *value }), + _ => None, + } + } +} + +impl InstructionMeta for ArraySetInst { + fn effects(&self) -> EffectMask { EffectMask::WRITE } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.array, self.index, self.value] } +} + +// ---- Return ---- +#[derive(Debug, Clone, Copy)] +pub struct ReturnInst { pub value: Option } + +impl ReturnInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Return { value } => Some(ReturnInst { value: *value }), + _ => None, + } + } +} + +impl InstructionMeta for ReturnInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { self.value.map(|v| vec![v]).unwrap_or_default() } +} + +// ---- Branch ---- +#[derive(Debug, Clone, Copy)] +pub struct BranchInst { pub condition: ValueId } + +impl BranchInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Branch { condition, .. } => Some(BranchInst { condition: *condition }), + _ => None, + } + } +} + +impl InstructionMeta for BranchInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.condition] } +} + +// ---- Jump ---- +#[derive(Debug, Clone, Copy)] +pub struct JumpInst; + +impl JumpInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::Jump { .. } => Some(JumpInst), _ => None } + } +} + +impl InstructionMeta for JumpInst { + fn effects(&self) -> EffectMask { EffectMask::PURE } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { Vec::new() } +} + +// ---- Print ---- +#[derive(Debug, Clone, Copy)] +pub struct PrintInst { pub value: ValueId, pub effects_mask: EffectMask } + +impl PrintInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Print { value, effects } => Some(PrintInst { value: *value, effects_mask: *effects }), + _ => None, + } + } +} + +impl InstructionMeta for PrintInst { + fn effects(&self) -> EffectMask { self.effects_mask } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.value] } +} + +// ---- Debug ---- +#[derive(Debug, Clone, Copy)] +pub struct DebugInst { pub value: ValueId } + +impl DebugInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { MirInstruction::Debug { value, .. } => Some(DebugInst { value: *value }), _ => None } + } +} + +impl InstructionMeta for DebugInst { + fn effects(&self) -> EffectMask { EffectMask::PURE.add(Effect::Debug) } + fn dst(&self) -> Option { None } + fn used(&self) -> Vec { vec![self.value] } +} + +// ---- Call-like (dst/used only; effects fallback in MirInstruction) ---- +#[derive(Debug, Clone)] +pub enum CallLikeInst { + Call { dst: Option, func: ValueId, args: Vec }, + BoxCall { dst: Option, box_val: ValueId, args: Vec }, + PluginInvoke { dst: Option, box_val: ValueId, args: Vec }, + ExternCall { dst: Option, args: Vec }, +} + +impl CallLikeInst { + pub fn from_mir(i: &MirInstruction) -> Option { + match i { + MirInstruction::Call { dst, func, args, .. } => + Some(CallLikeInst::Call { dst: *dst, func: *func, args: args.clone() }), + MirInstruction::BoxCall { dst, box_val, args, .. } => + Some(CallLikeInst::BoxCall { dst: *dst, box_val: *box_val, args: args.clone() }), + MirInstruction::PluginInvoke { dst, box_val, args, .. } => + Some(CallLikeInst::PluginInvoke { dst: *dst, box_val: *box_val, args: args.clone() }), + MirInstruction::ExternCall { dst, args, .. } => + Some(CallLikeInst::ExternCall { dst: *dst, args: args.clone() }), + _ => None, + } + } + + pub fn dst(&self) -> Option { + match self { + CallLikeInst::Call { dst, .. } + | CallLikeInst::BoxCall { dst, .. } + | CallLikeInst::PluginInvoke { dst, .. } + | CallLikeInst::ExternCall { dst, .. } => *dst, + } + } + + pub fn used(&self) -> Vec { + match self { + CallLikeInst::Call { func, args, .. } => { + let mut v = vec![*func]; v.extend(args.iter().copied()); v + } + CallLikeInst::BoxCall { box_val, args, .. } + | CallLikeInst::PluginInvoke { box_val, args, .. } => { + let mut v = vec![*box_val]; v.extend(args.iter().copied()); v + } + CallLikeInst::ExternCall { args, .. } => args.clone(), + } + } +} diff --git a/src/mir/mod.rs b/src/mir/mod.rs index 2dda230d..3ec82f26 100644 --- a/src/mir/mod.rs +++ b/src/mir/mod.rs @@ -12,6 +12,7 @@ pub mod builder; pub mod effect; pub mod function; pub mod instruction; +pub mod instruction_kinds; // small kind-specific metadata (Const/BinOp) pub mod instruction_introspection; // Introspection helpers for tests (instruction names) pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready) pub mod loop_builder; // SSA loop construction with phi nodes diff --git a/src/runner/box_index.rs b/src/runner/box_index.rs index 8ce0b7ea..eb285f97 100644 --- a/src/runner/box_index.rs +++ b/src/runner/box_index.rs @@ -8,7 +8,9 @@ use once_cell::sync::Lazy; use std::collections::{HashMap, HashSet}; +use std::sync::Mutex; use std::sync::RwLock; +use std::time::SystemTime; #[derive(Clone, Default)] pub struct BoxIndex { @@ -156,6 +158,16 @@ static GLOBAL: Lazy> = Lazy::new(|| RwLock::new(BoxIndex::defau static RESOLVE_CACHE: Lazy>> = Lazy::new(|| RwLock::new(HashMap::new())); +// Track env/file state to invalidate index and cache when changed +#[derive(Clone, Default)] +struct IndexState { + aliases_env: Option, + toml_mtime: Option, + toml_size: Option, +} + +static LAST_STATE: Lazy> = Lazy::new(|| Mutex::new(IndexState::default())); + pub fn refresh_box_index() { let next = BoxIndex::build_current(); if let Ok(mut w) = GLOBAL.write() { @@ -183,6 +195,26 @@ pub fn cache_clear() { } } +/// Rebuild BoxIndex and clear resolve cache if env/toml changed +pub fn rebuild_if_env_changed() { + let cur_env = std::env::var("NYASH_ALIASES").ok(); + let meta = std::fs::metadata("nyash.toml").ok(); + let (mtime, size) = if let Some(m) = meta { + (m.modified().ok(), Some(m.len())) + } else { + (None, None) + }; + let mut last = LAST_STATE.lock().expect("state"); + let changed = last.aliases_env != cur_env || last.toml_mtime != mtime || last.toml_size != size; + if changed { + last.aliases_env = cur_env; + last.toml_mtime = mtime; + last.toml_size = size; + refresh_box_index(); + cache_clear(); + } +} + #[derive(Clone, Debug, Default)] pub struct PluginMeta { pub prefix: Option, diff --git a/src/runner/dispatch.rs b/src/runner/dispatch.rs index f349c9b4..facbe95f 100644 --- a/src/runner/dispatch.rs +++ b/src/runner/dispatch.rs @@ -117,6 +117,20 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { } runner.execute_vm_mode(filename); } + "interpreter" => { + eprintln!("⚠ interpreter backend is legacy and deprecated. Use 'vm' (PyVM/LLVM) instead."); + if std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1") { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!("👉 Redirecting to VM backend (PyVM) as requested by NYASH_VM_USE_PY=1"); + } + runner.execute_vm_mode(filename); + } else { + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + println!("👉 Redirecting to VM backend"); + } + runner.execute_vm_mode(filename); + } + } #[cfg(feature = "cranelift-jit")] "jit-direct" => { if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { @@ -142,23 +156,9 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { } runner.execute_llvm_mode(filename); } - _ => { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!( - "🦀 Nyash Rust Implementation - Executing file: {} 🦀", - filename - ); - if let Some(fuel) = runner.config.debug_fuel { - println!("🔥 Debug fuel limit: {} iterations", fuel); - } else { - println!("🔥 Debug fuel limit: unlimited"); - } - println!("===================================================="); - } - super::modes::interpreter::execute_nyash_file( - filename, - runner.config.debug_fuel.clone(), - ); + other => { + eprintln!("❌ Unknown backend: {}. Use 'vm' or 'llvm' (or 'interpreter' legacy).", other); + std::process::exit(2); } } } diff --git a/src/runner/json_v0_bridge/lowering.rs b/src/runner/json_v0_bridge/lowering.rs index 52047379..0a1da006 100644 --- a/src/runner/json_v0_bridge/lowering.rs +++ b/src/runner/json_v0_bridge/lowering.rs @@ -6,6 +6,14 @@ use crate::mir::{ use std::collections::HashMap; use std::collections::HashSet; +// Split out merge/new_block helpers for readability (no behavior change) +mod merge; +use merge::{merge_var_maps, merge_values, new_block}; +// Feature splits (gradual extraction) +pub(super) mod if_else; +pub(super) mod loop_; +pub(super) mod try_catch; + #[derive(Clone, Copy)] pub(super) struct LoopContext { pub(super) cond_bb: BasicBlockId, @@ -94,50 +102,7 @@ impl<'a> VarScope for MapVars<'a> { } } -fn next_block_id(f: &MirFunction) -> BasicBlockId { - let mut mx = 0u32; - for k in f.blocks.keys() { - if k.0 >= mx { - mx = k.0 + 1; - } - } - BasicBlockId::new(mx) -} - -/// Create a fresh basic block and insert it into the function. -fn new_block(f: &mut MirFunction) -> BasicBlockId { - let id = next_block_id(f); - f.add_block(BasicBlock::new(id)); - id -} - -/// Merge two incoming values either by inserting Copy on predecessor edges -/// (no_phi mode) or by adding a Phi at the merge block head. -fn merge_values( - f: &mut MirFunction, - no_phi: bool, - merge_bb: BasicBlockId, - pred_a: BasicBlockId, - val_a: ValueId, - pred_b: BasicBlockId, - val_b: ValueId, -) -> ValueId { - if val_a == val_b { - return val_a; - } - let dst = f.next_value_id(); - if no_phi { - if let Some(bb) = f.get_block_mut(pred_a) { - bb.add_instruction(MirInstruction::Copy { dst, src: val_a }); - } - if let Some(bb) = f.get_block_mut(pred_b) { - bb.add_instruction(MirInstruction::Copy { dst, src: val_b }); - } - } else if let Some(bb) = f.get_block_mut(merge_bb) { - bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(pred_a, val_a), (pred_b, val_b)] }); - } - dst -} +// moved helpers are imported above fn lower_throw( env: &BridgeEnv, @@ -508,7 +473,7 @@ fn lower_expr( lower_expr_with_scope(env, f, cur_bb, e, &mut scope) } -fn lower_expr_with_vars( +pub(super) fn lower_expr_with_vars( env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, @@ -530,7 +495,7 @@ fn lower_args( lower_args_with_scope(env, f, cur_bb, args, &mut scope) } -fn lower_args_with_vars( +pub(super) fn lower_args_with_vars( env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, @@ -541,7 +506,7 @@ fn lower_args_with_vars( lower_args_with_scope(env, f, cur_bb, args, &mut scope) } -fn lower_stmt_with_vars( +pub(super) fn lower_stmt_with_vars( f: &mut MirFunction, cur_bb: BasicBlockId, s: &StmtV0, @@ -618,341 +583,20 @@ fn lower_stmt_with_vars( catches, finally, } => { - let try_enabled = std::env::var("NYASH_BRIDGE_TRY_ENABLE").ok().as_deref() == Some("1"); - if !try_enabled || catches.is_empty() || catches.len() > 1 { - let mut tmp_vars = vars.clone(); - let mut next_bb = - lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?; - if !finally.is_empty() { - next_bb = lower_stmt_list_with_vars( - f, - next_bb, - finally, - &mut tmp_vars, - loop_stack, - env, - )?; - } - *vars = tmp_vars; - return Ok(next_bb); - } - - let base_vars = vars.clone(); - let try_bb = new_block(f); - let catch_clause = &catches[0]; - let catch_bb = new_block(f); - let finally_bb = if !finally.is_empty() { - let id = new_block(f); - Some(id) - } else { - None - }; - let exit_bb = new_block(f); - let handler_target = finally_bb.unwrap_or(exit_bb); - let exception_value = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Catch { - exception_type: catch_clause.type_hint.clone(), - exception_value, - handler_bb: catch_bb, - }); - bb.set_terminator(MirInstruction::Jump { target: try_bb }); - } - let mut try_vars = vars.clone(); - let try_end = - lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?; - if let Some(bb) = f.get_block_mut(try_end) { - if !bb.is_terminated() { - bb.set_terminator(MirInstruction::Jump { - target: handler_target, - }); - } - } - let try_branch_vars = try_vars.clone(); - - let mut catch_vars = base_vars.clone(); - if let Some(param) = &catch_clause.param { - catch_vars.insert(param.clone(), exception_value); - } - let catch_end = lower_stmt_list_with_vars( - f, - catch_bb, - &catch_clause.body, - &mut catch_vars, - loop_stack, - env, - )?; - if let Some(param) = &catch_clause.param { - catch_vars.remove(param); - } - if let Some(bb) = f.get_block_mut(catch_end) { - if !bb.is_terminated() { - bb.set_terminator(MirInstruction::Jump { - target: handler_target, - }); - } - } - let catch_branch_vars = catch_vars.clone(); - - use std::collections::HashSet; - let mut branch_vars = vec![(try_end, try_branch_vars), (catch_end, catch_branch_vars)]; - if let Some(finally_block) = finally_bb { - let names: HashSet = { - let mut set: HashSet = base_vars.keys().cloned().collect(); - for (_, map) in &branch_vars { - set.extend(map.keys().cloned()); - } - set - }; - let mut merged_vars = base_vars.clone(); - let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new(); - for name in names { - let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); - for (bbid, map) in &branch_vars { - if let Some(&val) = map.get(&name) { - inputs.push((*bbid, val)); - } - } - if inputs.is_empty() { - if let Some(&base_val) = base_vars.get(&name) { - merged_vars.insert(name.clone(), base_val); - } - continue; - } - let unique: HashSet = inputs.iter().map(|(_, v)| *v).collect(); - if unique.len() == 1 { - merged_vars.insert(name.clone(), inputs[0].1); - continue; - } - let dst = f.next_value_id(); - inputs.sort_by_key(|(bbid, _)| bbid.0); - phi_entries.push((dst, inputs)); - merged_vars.insert(name.clone(), dst); - } - if let Some(bb) = f.get_block_mut(finally_block) { - for (dst, inputs) in phi_entries { - bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); - } - } - let mut finally_vars = merged_vars.clone(); - let final_end = lower_stmt_list_with_vars( - f, - finally_block, - finally, - &mut finally_vars, - loop_stack, - env, - )?; - if let Some(bb) = f.get_block_mut(final_end) { - if !bb.is_terminated() { - bb.set_terminator(MirInstruction::Jump { target: exit_bb }); - } - } - *vars = finally_vars; - Ok(exit_bb) - } else { - let names: HashSet = { - let mut set: HashSet = base_vars.keys().cloned().collect(); - for (_, map) in &branch_vars { - set.extend(map.keys().cloned()); - } - set - }; - let mut merged_vars = base_vars.clone(); - let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new(); - for name in names { - let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); - for (bbid, map) in &branch_vars { - if let Some(&val) = map.get(&name) { - inputs.push((*bbid, val)); - } - } - if inputs.is_empty() { - if let Some(&base_val) = base_vars.get(&name) { - merged_vars.insert(name.clone(), base_val); - } - continue; - } - let unique: HashSet = inputs.iter().map(|(_, v)| *v).collect(); - if unique.len() == 1 { - merged_vars.insert(name.clone(), inputs[0].1); - continue; - } - let dst = f.next_value_id(); - inputs.sort_by_key(|(bbid, _)| bbid.0); - phi_entries.push((dst, inputs)); - merged_vars.insert(name.clone(), dst); - } - if let Some(bb) = f.get_block_mut(exit_bb) { - for (dst, inputs) in phi_entries { - bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); - } - } - *vars = merged_vars; - Ok(exit_bb) - } - } - StmtV0::If { cond, then, r#else } => { - let (cval, cur) = lower_expr_with_vars(env, f, cur_bb, cond, vars)?; - let then_bb = next_block_id(f); - let else_bb = BasicBlockId::new(then_bb.0 + 1); - let merge_bb = BasicBlockId::new(then_bb.0 + 2); - f.add_block(BasicBlock::new(then_bb)); - f.add_block(BasicBlock::new(else_bb)); - f.add_block(BasicBlock::new(merge_bb)); - if let Some(bb) = f.get_block_mut(cur) { - bb.set_terminator(MirInstruction::Branch { - condition: cval, - then_bb, - else_bb, - }); - } - let base_vars = vars.clone(); - let mut then_vars = base_vars.clone(); - let tend = - lower_stmt_list_with_vars(f, then_bb, then, &mut then_vars, loop_stack, env)?; - if let Some(bb) = f.get_block_mut(tend) { - if !bb.is_terminated() { - bb.set_terminator(MirInstruction::Jump { target: merge_bb }); - } - } - let (else_end_pred, else_vars) = if let Some(elses) = r#else { - let mut ev = base_vars.clone(); - let eend = lower_stmt_list_with_vars(f, else_bb, elses, &mut ev, loop_stack, env)?; - if let Some(bb) = f.get_block_mut(eend) { - if !bb.is_terminated() { - bb.set_terminator(MirInstruction::Jump { target: merge_bb }); - } - } - (eend, ev) - } else { - if let Some(bb) = f.get_block_mut(else_bb) { - bb.set_terminator(MirInstruction::Jump { target: merge_bb }); - } - (else_bb, base_vars.clone()) - }; - let no_phi = env.mir_no_phi; - let mut names: HashSet = base_vars.keys().cloned().collect(); - for k in then_vars.keys() { - names.insert(k.clone()); - } - for k in else_vars.keys() { - names.insert(k.clone()); - } - for name in names { - let tv = then_vars.get(&name).copied(); - let ev = else_vars.get(&name).copied(); - let exists_base = base_vars.contains_key(&name); - match (tv, ev, exists_base) { - (Some(tval), Some(eval), _) => { - let merged = merge_values(f, no_phi, merge_bb, tend, tval, else_end_pred, eval); - vars.insert(name, merged); - } - (Some(tval), None, true) => { - if let Some(&bval) = base_vars.get(&name) { - let merged = merge_values(f, no_phi, merge_bb, tend, tval, else_end_pred, bval); - vars.insert(name, merged); - } - } - (None, Some(eval), true) => { - if let Some(&bval) = base_vars.get(&name) { - let merged = merge_values(f, no_phi, merge_bb, tend, bval, else_end_pred, eval); - vars.insert(name, merged); - } - } - _ => {} - } - } - Ok(merge_bb) - } - StmtV0::Loop { cond, body } => { - let cond_bb = new_block(f); - let body_bb = new_block(f); - let exit_bb = new_block(f); - if let Some(bb) = f.get_block_mut(cur_bb) { - if !bb.is_terminated() { - bb.add_instruction(MirInstruction::Jump { target: cond_bb }); - } - } - let no_phi = env.mir_no_phi; - let base_vars = vars.clone(); - let orig_names: Vec = base_vars.keys().cloned().collect(); - let mut phi_map: HashMap = HashMap::new(); - for name in &orig_names { - if let Some(&bval) = base_vars.get(name) { - let dst = f.next_value_id(); - if no_phi { - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Copy { dst, src: bval }); - } - } else if let Some(bb) = f.get_block_mut(cond_bb) { - bb.insert_instruction_after_phis(MirInstruction::Phi { - dst, - inputs: vec![(cur_bb, bval)], - }); - } - phi_map.insert(name.clone(), dst); - } - } - for (name, &phi) in &phi_map { - vars.insert(name.clone(), phi); - } - let (cval, _cend) = lower_expr_with_vars(env, f, cond_bb, cond, vars)?; - if let Some(bb) = f.get_block_mut(cond_bb) { - bb.set_terminator(MirInstruction::Branch { - condition: cval, - then_bb: body_bb, - else_bb: exit_bb, - }); - } - let mut body_vars = vars.clone(); - loop_stack.push(LoopContext { cond_bb, exit_bb }); - let bend_res = - lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack, env); - loop_stack.pop(); - let bend = bend_res?; - if let Some(bb) = f.get_block_mut(bend) { - if !bb.is_terminated() { - bb.set_terminator(MirInstruction::Jump { target: cond_bb }); - } - } - let backedge_to_cond = matches!(f.blocks.get(&bend).and_then(|bb| bb.terminator.as_ref()), Some(MirInstruction::Jump { target, .. }) if *target == cond_bb); - if backedge_to_cond { - if no_phi { - for (name, &phi_dst) in &phi_map { - if let Some(&latch_val) = body_vars.get(name) { - if let Some(bb) = f.get_block_mut(bend) { - bb.add_instruction(MirInstruction::Copy { - dst: phi_dst, - src: latch_val, - }); - } - } - } - } else if let Some(bb) = f.get_block_mut(cond_bb) { - for (name, &phi_dst) in &phi_map { - if let Some(&latch_val) = body_vars.get(name) { - for inst in &mut bb.instructions { - if let MirInstruction::Phi { dst, inputs } = inst { - if *dst == phi_dst { - inputs.push((bend, latch_val)); - break; - } - } - } - } - } - } - } - for (name, &phi) in &phi_map { - vars.insert(name.clone(), phi); - } - Ok(exit_bb) + try_catch::lower_try_stmt( + f, cur_bb, try_body, catches, finally, vars, loop_stack, env, + ) } + StmtV0::If { cond, then, r#else } => if_else::lower_if_stmt( + f, cur_bb, cond, then, r#else, vars, loop_stack, env, + ), + StmtV0::Loop { cond, body } => loop_::lower_loop_stmt( + f, cur_bb, cond, body, vars, loop_stack, env, + ), } } -fn lower_stmt_list_with_vars( +pub(super) fn lower_stmt_list_with_vars( f: &mut MirFunction, start_bb: BasicBlockId, stmts: &[StmtV0], diff --git a/src/runner/json_v0_bridge/lowering/if_else.rs b/src/runner/json_v0_bridge/lowering/if_else.rs new file mode 100644 index 00000000..6c72d6b6 --- /dev/null +++ b/src/runner/json_v0_bridge/lowering/if_else.rs @@ -0,0 +1,67 @@ +use super::{ + lower_expr_with_vars, lower_stmt_list_with_vars, merge_var_maps, new_block, BridgeEnv, + LoopContext, +}; +use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; +use std::collections::HashMap; +use super::super::ast::StmtV0; +use super::super::ast::ExprV0; + +pub(super) fn lower_if_stmt( + f: &mut MirFunction, + cur_bb: BasicBlockId, + cond: &ExprV0, + then_body: &[StmtV0], + else_body: &Option>, + vars: &mut HashMap, + loop_stack: &mut Vec, + env: &BridgeEnv, +) -> Result { + let (cval, cur) = lower_expr_with_vars(env, f, cur_bb, cond, vars)?; + let then_bb = new_block(f); + let else_bb = new_block(f); + let merge_bb = new_block(f); + if let Some(bb) = f.get_block_mut(cur) { + bb.set_terminator(MirInstruction::Branch { + condition: cval, + then_bb, + else_bb, + }); + } + let base_vars = vars.clone(); + let mut then_vars = base_vars.clone(); + let tend = lower_stmt_list_with_vars(f, then_bb, then_body, &mut then_vars, loop_stack, env)?; + if let Some(bb) = f.get_block_mut(tend) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } + let (else_end_pred, else_vars) = if let Some(elses) = else_body { + let mut ev = base_vars.clone(); + let eend = lower_stmt_list_with_vars(f, else_bb, elses, &mut ev, loop_stack, env)?; + if let Some(bb) = f.get_block_mut(eend) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } + (eend, ev) + } else { + if let Some(bb) = f.get_block_mut(else_bb) { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + (else_bb, base_vars.clone()) + }; + merge_var_maps( + f, + env.mir_no_phi, + merge_bb, + tend, + else_end_pred, + then_vars, + else_vars, + base_vars, + vars, + ); + Ok(merge_bb) +} + diff --git a/src/runner/json_v0_bridge/lowering/loop_.rs b/src/runner/json_v0_bridge/lowering/loop_.rs new file mode 100644 index 00000000..518ccf19 --- /dev/null +++ b/src/runner/json_v0_bridge/lowering/loop_.rs @@ -0,0 +1,105 @@ +use super::{ + lower_expr_with_vars, lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext, +}; +use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; +use std::collections::HashMap; +use super::super::ast::StmtV0; +use super::super::ast::ExprV0; + +pub(super) fn lower_loop_stmt( + f: &mut MirFunction, + cur_bb: BasicBlockId, + cond: &ExprV0, + body: &[StmtV0], + vars: &mut HashMap, + loop_stack: &mut Vec, + env: &BridgeEnv, +) -> Result { + let cond_bb = new_block(f); + let body_bb = new_block(f); + let exit_bb = new_block(f); + if let Some(bb) = f.get_block_mut(cur_bb) { + if !bb.is_terminated() { + bb.add_instruction(MirInstruction::Jump { target: cond_bb }); + } + } + let no_phi = env.mir_no_phi; + let base_vars = vars.clone(); + let orig_names: Vec = base_vars.keys().cloned().collect(); + let mut phi_map: HashMap = HashMap::new(); + for name in &orig_names { + if let Some(&bval) = base_vars.get(name) { + let dst = f.next_value_id(); + if no_phi { + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::Copy { dst, src: bval }); + } + } else if let Some(bb) = f.get_block_mut(cond_bb) { + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(cur_bb, bval)], + }); + } + phi_map.insert(name.clone(), dst); + } + } + for (name, &phi) in &phi_map { + vars.insert(name.clone(), phi); + } + let (cval, _cend) = lower_expr_with_vars(env, f, cond_bb, cond, vars)?; + if let Some(bb) = f.get_block_mut(cond_bb) { + bb.set_terminator(MirInstruction::Branch { + condition: cval, + then_bb: body_bb, + else_bb: exit_bb, + }); + } + let mut body_vars = vars.clone(); + loop_stack.push(LoopContext { cond_bb, exit_bb }); + let bend_res = lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack, env); + loop_stack.pop(); + let bend = bend_res?; + if let Some(bb) = f.get_block_mut(bend) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: cond_bb }); + } + } + let backedge_to_cond = matches!( + f.blocks + .get(&bend) + .and_then(|bb| bb.terminator.as_ref()), + Some(MirInstruction::Jump { target, .. }) if *target == cond_bb + ); + if backedge_to_cond { + if no_phi { + for (name, &phi_dst) in &phi_map { + if let Some(&latch_val) = body_vars.get(name) { + if let Some(bb) = f.get_block_mut(bend) { + bb.add_instruction(MirInstruction::Copy { + dst: phi_dst, + src: latch_val, + }); + } + } + } + } else if let Some(bb) = f.get_block_mut(cond_bb) { + for (name, &phi_dst) in &phi_map { + if let Some(&latch_val) = body_vars.get(name) { + for inst in &mut bb.instructions { + if let MirInstruction::Phi { dst, inputs } = inst { + if *dst == phi_dst { + inputs.push((bend, latch_val)); + break; + } + } + } + } + } + } + } + for (name, &phi) in &phi_map { + vars.insert(name.clone(), phi); + } + Ok(exit_bb) +} + diff --git a/src/runner/json_v0_bridge/lowering/merge.rs b/src/runner/json_v0_bridge/lowering/merge.rs new file mode 100644 index 00000000..3a20a312 --- /dev/null +++ b/src/runner/json_v0_bridge/lowering/merge.rs @@ -0,0 +1,99 @@ +use crate::mir::{ + BasicBlock, BasicBlockId, MirFunction, MirInstruction, ValueId, +}; + +fn next_block_id(f: &MirFunction) -> BasicBlockId { + let mut mx = 0u32; + for k in f.blocks.keys() { + if k.0 >= mx { + mx = k.0 + 1; + } + } + BasicBlockId::new(mx) +} + +/// Create a fresh basic block and insert it into the function. +pub(super) fn new_block(f: &mut MirFunction) -> BasicBlockId { + let id = next_block_id(f); + f.add_block(BasicBlock::new(id)); + id +} + +/// Merge two incoming values either by inserting Copy on predecessor edges +/// (no_phi mode) or by adding a Phi at the merge block head. +pub(super) fn merge_values( + f: &mut MirFunction, + no_phi: bool, + merge_bb: BasicBlockId, + pred_a: BasicBlockId, + val_a: ValueId, + pred_b: BasicBlockId, + val_b: ValueId, +) -> ValueId { + if val_a == val_b { + return val_a; + } + let dst = f.next_value_id(); + if no_phi { + if let Some(bb) = f.get_block_mut(pred_a) { + bb.add_instruction(MirInstruction::Copy { dst, src: val_a }); + } + if let Some(bb) = f.get_block_mut(pred_b) { + bb.add_instruction(MirInstruction::Copy { dst, src: val_b }); + } + } else if let Some(bb) = f.get_block_mut(merge_bb) { + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(pred_a, val_a), (pred_b, val_b)], + }); + } + dst +} + +/// Merge then/else variable maps into `out_vars` using either PHI at merge +/// or edge-copies in no-phi mode. +pub(super) fn merge_var_maps( + f: &mut MirFunction, + no_phi: bool, + merge_bb: BasicBlockId, + then_end: BasicBlockId, + else_end: BasicBlockId, + then_vars: std::collections::HashMap, + else_vars: std::collections::HashMap, + base_vars: std::collections::HashMap, + out_vars: &mut std::collections::HashMap, +) { + use std::collections::HashSet; + let mut names: HashSet = base_vars.keys().cloned().collect(); + for k in then_vars.keys() { + names.insert(k.clone()); + } + for k in else_vars.keys() { + names.insert(k.clone()); + } + for name in names { + let tv = then_vars.get(&name).copied(); + let ev = else_vars.get(&name).copied(); + let exists_base = base_vars.contains_key(&name); + match (tv, ev, exists_base) { + (Some(tval), Some(eval), _) => { + let merged = merge_values(f, no_phi, merge_bb, then_end, tval, else_end, eval); + out_vars.insert(name, merged); + } + (Some(tval), None, true) => { + if let Some(&bval) = base_vars.get(&name) { + let merged = merge_values(f, no_phi, merge_bb, then_end, tval, else_end, bval); + out_vars.insert(name, merged); + } + } + (None, Some(eval), true) => { + if let Some(&bval) = base_vars.get(&name) { + let merged = merge_values(f, no_phi, merge_bb, then_end, bval, else_end, eval); + out_vars.insert(name, merged); + } + } + _ => {} + } + } +} + diff --git a/src/runner/json_v0_bridge/lowering/try_catch.rs b/src/runner/json_v0_bridge/lowering/try_catch.rs new file mode 100644 index 00000000..8d449658 --- /dev/null +++ b/src/runner/json_v0_bridge/lowering/try_catch.rs @@ -0,0 +1,190 @@ +use super::{ + lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext, +}; +use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; +use std::collections::HashMap; +use super::super::ast::{StmtV0, CatchV0}; + +pub(super) fn lower_try_stmt( + f: &mut MirFunction, + cur_bb: BasicBlockId, + try_body: &[StmtV0], + catches: &[CatchV0], + finally: &[StmtV0], + vars: &mut HashMap, + loop_stack: &mut Vec, + env: &BridgeEnv, +) -> Result { + let try_enabled = std::env::var("NYASH_BRIDGE_TRY_ENABLE").ok().as_deref() == Some("1"); + if !try_enabled || catches.is_empty() || catches.len() > 1 { + let mut tmp_vars = vars.clone(); + let mut next_bb = lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?; + if !finally.is_empty() { + next_bb = lower_stmt_list_with_vars( + f, + next_bb, + finally, + &mut tmp_vars, + loop_stack, + env, + )?; + } + *vars = tmp_vars; + return Ok(next_bb); + } + + let base_vars = vars.clone(); + let try_bb = new_block(f); + let catch_clause = &catches[0]; + let catch_bb = new_block(f); + let finally_bb = if !finally.is_empty() { + let id = new_block(f); + Some(id) + } else { + None + }; + let exit_bb = new_block(f); + let handler_target = finally_bb.unwrap_or(exit_bb); + let exception_value = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::Catch { + exception_type: catch_clause.type_hint.clone(), + exception_value, + handler_bb: catch_bb, + }); + bb.set_terminator(MirInstruction::Jump { target: try_bb }); + } + let mut try_vars = vars.clone(); + let try_end = lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?; + if let Some(bb) = f.get_block_mut(try_end) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { + target: handler_target, + }); + } + } + let try_branch_vars = try_vars.clone(); + + let mut catch_vars = base_vars.clone(); + if let Some(param) = &catch_clause.param { + catch_vars.insert(param.clone(), exception_value); + } + let catch_end = lower_stmt_list_with_vars( + f, + catch_bb, + &catch_clause.body, + &mut catch_vars, + loop_stack, + env, + )?; + if let Some(param) = &catch_clause.param { + catch_vars.remove(param); + } + if let Some(bb) = f.get_block_mut(catch_end) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { + target: handler_target, + }); + } + } + let catch_branch_vars = catch_vars.clone(); + + use std::collections::HashSet; + let mut branch_vars = vec![(try_end, try_branch_vars), (catch_end, catch_branch_vars)]; + if let Some(finally_block) = finally_bb { + let names: HashSet = { + let mut set: HashSet = base_vars.keys().cloned().collect(); + for (_, map) in &branch_vars { + set.extend(map.keys().cloned()); + } + set + }; + let mut merged_vars = base_vars.clone(); + let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new(); + for name in names { + let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); + for (bbid, map) in &branch_vars { + if let Some(&val) = map.get(&name) { + inputs.push((*bbid, val)); + } + } + if inputs.is_empty() { + if let Some(&base_val) = base_vars.get(&name) { + merged_vars.insert(name.clone(), base_val); + } + continue; + } + let unique: HashSet = inputs.iter().map(|(_, v)| *v).collect(); + if unique.len() == 1 { + merged_vars.insert(name.clone(), inputs[0].1); + continue; + } + let dst = f.next_value_id(); + inputs.sort_by_key(|(bbid, _)| bbid.0); + phi_entries.push((dst, inputs)); + merged_vars.insert(name.clone(), dst); + } + if let Some(bb) = f.get_block_mut(finally_block) { + for (dst, inputs) in phi_entries { + bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); + } + } + let mut finally_vars = merged_vars.clone(); + let final_end = lower_stmt_list_with_vars( + f, + finally_block, + finally, + &mut finally_vars, + loop_stack, + env, + )?; + if let Some(bb) = f.get_block_mut(final_end) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: exit_bb }); + } + } + *vars = finally_vars; + Ok(exit_bb) + } else { + let names: HashSet = { + let mut set: HashSet = base_vars.keys().cloned().collect(); + for (_, map) in &branch_vars { + set.extend(map.keys().cloned()); + } + set + }; + let mut merged_vars = base_vars.clone(); + let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new(); + for name in names { + let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); + for (bbid, map) in &branch_vars { + if let Some(&val) = map.get(&name) { + inputs.push((*bbid, val)); + } + } + if inputs.is_empty() { + if let Some(&base_val) = base_vars.get(&name) { + merged_vars.insert(name.clone(), base_val); + } + continue; + } + let unique: HashSet = inputs.iter().map(|(_, v)| *v).collect(); + if unique.len() == 1 { + merged_vars.insert(name.clone(), inputs[0].1); + continue; + } + let dst = f.next_value_id(); + inputs.sort_by_key(|(bbid, _)| bbid.0); + phi_entries.push((dst, inputs)); + merged_vars.insert(name.clone(), dst); + } + if let Some(bb) = f.get_block_mut(exit_bb) { + for (dst, inputs) in phi_entries { + bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); + } + } + *vars = merged_vars; + Ok(exit_bb) + } +} + diff --git a/src/runner/modes/interpreter.rs b/src/runner/modes/interpreter.rs index 92c026d0..e2cfb1b6 100644 --- a/src/runner/modes/interpreter.rs +++ b/src/runner/modes/interpreter.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "interpreter-legacy")] use crate::interpreter::NyashInterpreter; use crate::parser::NyashParser; use crate::runner::pipeline::{resolve_using_target, UsingContext}; diff --git a/src/runner/pipeline.rs b/src/runner/pipeline.rs index 4b47c495..34f3ef70 100644 --- a/src/runner/pipeline.rs +++ b/src/runner/pipeline.rs @@ -150,6 +150,8 @@ pub(super) fn resolve_using_target( strict: bool, verbose: bool, ) -> Result { + // Invalidate and rebuild index/cache if env or nyash.toml changed + super::box_index::rebuild_if_env_changed(); if is_path { return Ok(tgt.to_string()); } diff --git a/tools/aot_plan/README.md b/tools/aot_plan/README.md index 0b3835f7..665ba1d0 100644 --- a/tools/aot_plan/README.md +++ b/tools/aot_plan/README.md @@ -1,6 +1,6 @@ # Nyash AOT-Plan (Phase 15.1) — Scripts Skeleton -This folder will contain Nyash scripts that analyze a project (following `using` imports) and emit `aot_plan.v1.json` per docs/specs/aot_plan_v1.md. +This folder will contain Nyash scripts that analyze a project (following `using` imports) and emit `aot_plan.v1.json` per docs/design/aot-plan-v1.md. Phase 15.1 scope: - Keep scripts minimal and deterministic @@ -11,4 +11,3 @@ Placeholder files included: - `analyze.ny` — entry script (skeleton) - `samples/mini_project/` — a tiny sample project with `using` - `samples/plan_v1_min.json` — a minimal plan JSON used by importer tests - diff --git a/tools/pyvm_vs_llvmlite.sh b/tools/pyvm_vs_llvmlite.sh index ff4a670e..e9609bd9 100644 --- a/tools/pyvm_vs_llvmlite.sh +++ b/tools/pyvm_vs_llvmlite.sh @@ -39,20 +39,29 @@ echo "=== exit codes ===" echo "llvmlite: $CODE_LL" echo "PyVM : $CODE_PY" -DIFF=0 -if [[ "$OUT_LL" != "$OUT_PY" ]]; then - echo "[cmp] stdout differs" >&2 - DIFF=1 -fi -if [[ "$CODE_LL" -ne "$CODE_PY" ]]; then - echo "[cmp] exit code differs" >&2 - DIFF=1 -fi - -if [[ "$DIFF" -eq 0 ]]; then - echo "✅ parity OK (stdout + exit code)" +# Strict compare only when requested. Default: exit code parity. +STRICT=${CMP_STRICT:-0} +if [[ "$STRICT" == "1" ]]; then + DIFF=0 + if [[ "$OUT_LL" != "$OUT_PY" ]]; then + echo "[cmp] stdout differs" >&2 + DIFF=1 + fi + if [[ "$CODE_LL" -ne "$CODE_PY" ]]; then + echo "[cmp] exit code differs" >&2 + DIFF=1 + fi + if [[ "$DIFF" -eq 0 ]]; then + echo "✅ parity OK (stdout + exit code)" + else + echo "❌ parity mismatch" >&2 + exit 1 + fi else - echo "❌ parity mismatch" >&2 - exit 1 + if [[ "$CODE_LL" -eq "$CODE_PY" ]]; then + echo "✅ parity OK (exit code)" + else + echo "❌ exit code mismatch" >&2 + exit 1 + fi fi - diff --git a/tools/smokes/curated_llvm.sh b/tools/smokes/curated_llvm.sh index 5fdfb072..4a7c041a 100644 --- a/tools/smokes/curated_llvm.sh +++ b/tools/smokes/curated_llvm.sh @@ -15,10 +15,13 @@ fi export NYASH_LLVM_USE_HARNESS=1 -# Optional: PHI-off mode -if [[ "${1:-}" == "--phi-off" ]]; then - export NYASH_MIR_NO_PHI=1 - export NYASH_VERIFY_ALLOW_NO_PHI=1 +# Default: PHI-off (MIR13). Use --phi-on to test PHI-on path. +export NYASH_MIR_NO_PHI=${NYASH_MIR_NO_PHI:-1} +export NYASH_VERIFY_ALLOW_NO_PHI=${NYASH_VERIFY_ALLOW_NO_PHI:-1} +if [[ "${1:-}" == "--phi-on" ]]; then + export NYASH_MIR_NO_PHI=0 + echo "[curated-llvm] PHI-on (JSON PHI + finalize) enabled" >&2 +else echo "[curated-llvm] PHI-off (edge-copy) enabled" >&2 fi diff --git a/tools/smokes/curated_phi_invariants.sh b/tools/smokes/curated_phi_invariants.sh new file mode 100644 index 00000000..f7724f03 --- /dev/null +++ b/tools/smokes/curated_phi_invariants.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ "${NYASH_CLI_VERBOSE:-0}" == "1" ]]; then + set -x +fi + +ROOT_DIR=$(cd "$(dirname "$0")/../.." && pwd) + +APPS=( + "apps/tests/shortcircuit_nested_selective_assign.nyash" + "apps/tests/loop_if_phi.nyash" + "apps/tests/ternary_nested.nyash" +) + +echo "[phi] Running curated PHI invariants parity checks (PyVM vs llvmlite)" >&2 + +FAIL=0 +for app in "${APPS[@]}"; do + echo "[phi] case: $app" >&2 + if ! "$ROOT_DIR/tools/pyvm_vs_llvmlite.sh" "$ROOT_DIR/$app"; then + echo "[phi] ❌ parity failed: $app" >&2 + FAIL=1 + else + echo "[phi] ✅ parity OK: $app" >&2 + fi +done + +if [[ "$FAIL" -ne 0 ]]; then + echo "[phi] ❌ curated PHI invariants parity has failures" >&2 + exit 1 +fi +echo "[phi] ✅ all curated PHI invariants cases passed" +