Phase 11.9: 統一文法アーキテクチャ作業中 - MIR builder分割とJIT lower整理
- MIR builder: stmts/exprs/typeingモジュール分割による整理 - JIT lower: core/ops_ext.rs追加(外部呼び出し系の分離) - 統一文法エンジン基盤構築中(grammar/unified-grammar.toml対応) - ChatGPT5協調作業: M1完了、M2作業中 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
38
.github/workflows/smoke.yml
vendored
38
.github/workflows/smoke.yml
vendored
@ -47,6 +47,44 @@ jobs:
|
|||||||
- name: Run smoke script
|
- name: Run smoke script
|
||||||
run: bash tools/smoke_phase_10_10.sh
|
run: bash tools/smoke_phase_10_10.sh
|
||||||
|
|
||||||
|
jit-direct-smoke:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
# Disable external plugins to keep CI deterministic
|
||||||
|
NYASH_DISABLE_PLUGINS: '1'
|
||||||
|
# Ensure lowering runs
|
||||||
|
NYASH_JIT_THRESHOLD: '1'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust (stable)
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Cache cargo registry and build
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-
|
||||||
|
|
||||||
|
- name: Build (release, cranelift-jit)
|
||||||
|
run: cargo build --release --features cranelift-jit
|
||||||
|
|
||||||
|
- name: JIT-direct smoke: mir-branch-ret
|
||||||
|
run: timeout 15s ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash
|
||||||
|
|
||||||
|
- name: JIT-direct smoke: mir-phi-min
|
||||||
|
run: timeout 15s ./target/release/nyash --jit-direct apps/tests/mir-phi-min/main.nyash
|
||||||
|
|
||||||
|
- name: JIT-direct smoke: mir-branch-multi
|
||||||
|
run: timeout 15s ./target/release/nyash --jit-direct apps/tests/mir-branch-multi/main.nyash
|
||||||
|
|
||||||
smoke-compile-events:
|
smoke-compile-events:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
|
|||||||
@ -111,10 +111,7 @@ Update (2025-09-01 AM / JIT handoff follow-up)
|
|||||||
- apps/tests/mir-phi-min: if(1<2){x=10}else{x=20}; return x → 10
|
- apps/tests/mir-phi-min: if(1<2){x=10}else{x=20}; return x → 10
|
||||||
- apps/tests/mir-branch-multi: 入れ子条件分岐 → 1
|
- apps/tests/mir-branch-multi: 入れ子条件分岐 → 1
|
||||||
- 実行例: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-store-load/main.nyash`
|
- 実行例: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-store-load/main.nyash`
|
||||||
- jit-direct 分岐/PHI 根治(進行中)
|
- jit-direct 分岐/PHI 根治: 単一出口+BlockParam 合流で安定(fast-path は常時有効)。
|
||||||
- 現象: select/compare の実行時観測では cond=1/then=1/else=0 と正しいが、最終結果が 0 に落ちるケースあり。
|
|
||||||
- 統合JIT(`--backend cranelift`)は期待どおり→LowerCore の意味論は正しく、jit-direct のCFG/合流が疑わしい。
|
|
||||||
- 主因(仮説→確度高): 関数共有の value_stack をブロック間で使い回し→分岐/合流で返値取り違え。
|
|
||||||
|
|
||||||
変更点(犯人切り分けと根治のための構造改革+ログ)
|
変更点(犯人切り分けと根治のための構造改革+ログ)
|
||||||
- CraneliftBuilder(jit-direct 経路)
|
- CraneliftBuilder(jit-direct 経路)
|
||||||
@ -130,7 +127,7 @@ Update (2025-09-01 AM / JIT handoff follow-up)
|
|||||||
- `nyash.jit.block_enter(idx: i64) -> void`(ブロック入場ログ)
|
- `nyash.jit.block_enter(idx: i64) -> void`(ブロック入場ログ)
|
||||||
- LowerCore(return 値の堅牢化)
|
- LowerCore(return 値の堅牢化)
|
||||||
- Return 値が known/param/slot 経路に乗らない場合、同一ブロックの Const 定義をスキャンして materialize。
|
- Return 値が known/param/slot 経路に乗らない場合、同一ブロックの Const 定義をスキャンして materialize。
|
||||||
- Fast-path(読みやすさ&単純化): then/else が定数 return の場合、`select(cond, K_then, K_else)`→`emit_return` に縮約(`NYASH_JIT_FASTPATH_SELECT=1` で強制)。
|
- Fast-path(読みやすさ&単純化): then/else が定数 return の場合、`select(cond, K_then, K_else)`→`emit_return` に縮約(常時有効)。
|
||||||
|
|
||||||
診断ログ(必要時のみ ON)
|
診断ログ(必要時のみ ON)
|
||||||
- `NYASH_JIT_TRACE_BLOCKS=1` … ブロック入場ログ(`[JIT-BLOCK] enter=<idx>`)
|
- `NYASH_JIT_TRACE_BLOCKS=1` … ブロック入場ログ(`[JIT-BLOCK] enter=<idx>`)
|
||||||
@ -170,6 +167,11 @@ Update (2025-09-02 / JIT seal・PHI安定化 + builder分割 進捗)
|
|||||||
- 診断ログ整備(NYASH_JIT_DUMP/TRACE_*)。
|
- 診断ログ整備(NYASH_JIT_DUMP/TRACE_*)。
|
||||||
- スモーク(debug/release): mir-branch-ret=1, mir-phi-min=10, mir-branch-multi=1。
|
- スモーク(debug/release): mir-branch-ret=1, mir-phi-min=10, mir-branch-multi=1。
|
||||||
|
|
||||||
|
- 追加(2025-09-02 PM / fast-path 簡素化)
|
||||||
|
- Branch fast-path を常時有効化(両後続が i64 定数 return の場合に限り `select+return` で縮約)。
|
||||||
|
- 環境変数 `NYASH_JIT_FASTPATH_SELECT` は不要になりました(存在しても無視)。
|
||||||
|
- jit-direct の3本スモーク(debug/release)で回帰なしを確認。
|
||||||
|
|
||||||
- リファクタリング(builder 1,000行目安に向けて段階実施)
|
- リファクタリング(builder 1,000行目安に向けて段階実施)
|
||||||
- 分離済み:
|
- 分離済み:
|
||||||
- `src/jit/lower/builder/noop.rs`(NoopBuilder)
|
- `src/jit/lower/builder/noop.rs`(NoopBuilder)
|
||||||
@ -178,6 +180,21 @@ Update (2025-09-02 / JIT seal・PHI安定化 + builder分割 進捗)
|
|||||||
- `src/jit/lower/builder/tls.rs`(clif_tls と TLS 呼び出しヘルパ)
|
- `src/jit/lower/builder/tls.rs`(clif_tls と TLS 呼び出しヘルパ)
|
||||||
- 動作維持: pub use で既存パス互換。jit-direct スモーク通過。
|
- 動作維持: pub use で既存パス互換。jit-direct スモーク通過。
|
||||||
|
|
||||||
|
- 追加(2025-09-02 PM / MIR builder 分割の第一段)
|
||||||
|
- Calls 抽出: `src/mir/builder/builder_calls.rs` 新設。
|
||||||
|
- 移動: `build_function_call` / `build_method_call` / `build_from_expression` / `lower_method_as_function` / `lower_static_method_as_function` + 補助 `parse_type_name_to_mir` / `extract_string_literal`。
|
||||||
|
- 依存を明示(`use crate::mir::{TypeOpKind, slot_registry};`)。
|
||||||
|
- Stmts ラッパ導入: `src/mir/builder/stmts.rs` 新設。
|
||||||
|
- 現状は `build_*` 系をラッパで委譲(`*_legacy`)し、動作回帰チェックを優先。
|
||||||
|
- ビルド/スモーク: release + jit-direct 3本(branch-ret/phi-min/branch-multi)緑維持。
|
||||||
|
|
||||||
|
- 次のステップ(builder 分割 続き)
|
||||||
|
1) Stmts 本体移設: `builder.rs` の `build_block/if/loop/try/return/local/nowait/await/me/print/throw` 実装(`*_legacy`)を `builder/stmts.rs` へ完全移動し、`builder.rs` から削除。
|
||||||
|
2) Ops 抽出: `build_binary_op/unary_op` + `convert_binary_operator/convert_unary_operator` を `builder/ops.rs` へ。
|
||||||
|
3) Utils 抽出: `resolve_include_path_builder` / `builder_debug_*` / `infer_type_from_phi` などを `builder/utils.rs` へ。
|
||||||
|
4) 残存 `*_legacy` の削除と最終ビルド+jit-direct 3本スモークで回帰確認。
|
||||||
|
5) 目標: `src/mir/builder.rs` を < 1,000 行に縮小(薄いハブ化)。
|
||||||
|
|
||||||
- 残タスク(次手)
|
- 残タスク(次手)
|
||||||
- [ ] CraneliftBuilder 本体を `builder/cranelift.rs` に分離(大枠)。
|
- [ ] CraneliftBuilder 本体を `builder/cranelift.rs` に分離(大枠)。
|
||||||
- [ ] `builder.rs` を薄い “ハブ” 化(trait/enum/API公開 + pub use)。
|
- [ ] `builder.rs` を薄い “ハブ” 化(trait/enum/API公開 + pub use)。
|
||||||
@ -188,6 +205,8 @@ Update (2025-09-02 / JIT seal・PHI安定化 + builder分割 進捗)
|
|||||||
- Build: `cargo build --release --features cranelift-jit`
|
- Build: `cargo build --release --features cranelift-jit`
|
||||||
- jit-direct: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
|
- jit-direct: `NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
|
||||||
- 診断: `NYASH_JIT_DUMP=1 NYASH_JIT_TRACE_BLOCKS=1 NYASH_JIT_TRACE_RET=1` 併用可。
|
- 診断: `NYASH_JIT_DUMP=1 NYASH_JIT_TRACE_BLOCKS=1 NYASH_JIT_TRACE_RET=1` 併用可。
|
||||||
|
- 単一出口方針: jit-direct は関数終端で単一の ret ブロックに合流し、PHI(min) は BlockParam で表現。
|
||||||
|
- TRACE 変数一覧(JIT): `NYASH_JIT_DUMP`, `NYASH_JIT_TRACE_BLOCKS`, `NYASH_JIT_TRACE_BR`, `NYASH_JIT_TRACE_SEL`, `NYASH_JIT_TRACE_RET`
|
||||||
|
|
||||||
Update (2025-09-01 PM2 / Interpreter parity blockers)
|
Update (2025-09-01 PM2 / Interpreter parity blockers)
|
||||||
|
|
||||||
@ -214,8 +233,8 @@ Update (2025-09-01 PM2 / Interpreter parity blockers)
|
|||||||
|
|
||||||
- 未解決(最優先)
|
- 未解決(最優先)
|
||||||
1) 返り値が変数IDになる(examples/semantics_test_branch.nyash)
|
1) 返り値が変数IDになる(examples/semantics_test_branch.nyash)
|
||||||
- 調査方針: execute_statement(Return)→execute_function_call の伝播経路、Variable 解決/共有の箇所を追跡し、Box 値ではなく内部ID/インデックスを返している箇所を特定・修正。
|
- 現状: 再現せず(CLI実行で Result: 100 を確認)。
|
||||||
- 対応: Return 直前と関数エピローグでの実値/型ログ(限定ログ)を差し込み、最小修正。
|
- 対応: Return 直前と関数エピローグに限定トレース追加(`NYASH_INT_RET_TRACE=1`)。再発時に型/値を即観測可。
|
||||||
2) PluginBox 同士の演算の包括対応
|
2) PluginBox 同士の演算の包括対応
|
||||||
- 暫定は toString→数値/文字列へ正規化で回避。恒久対応は Semantics/VM と同じ規約(handle-first + 文字列like/数値like)に寄せる。
|
- 暫定は toString→数値/文字列へ正規化で回避。恒久対応は Semantics/VM と同じ規約(handle-first + 文字列like/数値like)に寄せる。
|
||||||
3) 文字列連結の広範囲対応
|
3) 文字列連結の広範囲対応
|
||||||
|
|||||||
@ -739,3 +739,47 @@ JIT分割 進捗(継続観点)
|
|||||||
- [ ] Extern/PluginInvoke/BoxCall 周辺の肥大化した分岐を `core/ops_ext.rs` に整理
|
- [ ] Extern/PluginInvoke/BoxCall 周辺の肥大化した分岐を `core/ops_ext.rs` に整理
|
||||||
- [ ] `analysis`/`cfg` の補助関数(succ_phi_inputs など)の関数化
|
- [ ] `analysis`/`cfg` の補助関数(succ_phi_inputs など)の関数化
|
||||||
- [ ] 分割ごとに jit-direct スモークの緑維持(debug / release+feature)
|
- [ ] 分割ごとに jit-direct スモークの緑維持(debug / release+feature)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🆕 Update (Phase 11.9) — M3→M4 引き継ぎメモ(2025-09-02)
|
||||||
|
|
||||||
|
到達(M1–M3 要約)
|
||||||
|
- M1: 予約語レジストリ(build.rs→generated.rs)、Tokenizer 後段に `engine.is_keyword_str()`(`NYASH_GRAMMAR_DIFF=1` 差分ログ)
|
||||||
|
- M2: `operators.{add,sub,mul,div}` を TOML→生成(未記載は既定補完)。VM/Interp/JIT に `NYASH_GRAMMAR_DIFF=1` ログ、Add 強制は `NYASH_GRAMMAR_ENFORCE_ADD=1`
|
||||||
|
- M3: 構文規則スキャフォールド(`syntax.statements.allow` / `syntax.expressions.allow_binops`)を生成し、Parser に非侵襲統合(差分ログのみ)
|
||||||
|
- JIT: `ops_ext.rs` 新設。`I::BoxCall` は ops_ext に完全委譲(core 旧分岐は削除/到達不能化)。jit-direct スモーク(branch-ret/phi-min/branch-multi)緑維持
|
||||||
|
|
||||||
|
提案(M4: スナップショット/差分検出・CI 整備)
|
||||||
|
- 目的: 新旧ルートの整合をスナップショット+マトリクスで機械的に検証し、回帰を防止
|
||||||
|
- スコープ:
|
||||||
|
- Parser→MIR のスナップショットテスト(代表ケースから開始)
|
||||||
|
- 実行マトリクス `{vm,jit,aot} × {gc on,off}` の最小スモークを追加。出力/trace のハッシュ一致で検証
|
||||||
|
- ログ: `NYASH_GRAMMAR_DIFF=1` をCIで有効化(初期は Allow-failure 的に集計・監視、収束後に強化)
|
||||||
|
|
||||||
|
実施順(小粒→段階導入)
|
||||||
|
1) スナップショット追加
|
||||||
|
- `tests/snapshots/parser_mir/` に代表ケース(if/loop/return、二項演算 add/sub/mul/div、簡単なメソッド呼び)
|
||||||
|
- `assert_snapshot!(print_mir(func))` 形式 or 既存プリンタ出力の文字列一致
|
||||||
|
2) マトリクス・スモーク
|
||||||
|
- `tools/smoke_matrix.sh` を追加(VM/JIT/AOT × GC on/off)。既存 `apps/tests/*` を利用
|
||||||
|
- 出力ハッシュ(`sha256sum`)と軽量 trace(行フィルタ後の hash)で一致確認
|
||||||
|
- JIT は `--features cranelift-jit`、AOT は LLVM18 前提(`LLVM_SYS_180_PREFIX`)
|
||||||
|
3) CI 連携
|
||||||
|
- Workflow にマトリクスを追加。最初は JIT/VM のみ → AOT は opt-in(CI 環境用意後に拡張)
|
||||||
|
- `NYASH_GRAMMAR_DIFF=1` を付与し差分ログを保存(失敗条件には含めない)。収束後に閾値/厳格化を検討
|
||||||
|
4) ドキュメント
|
||||||
|
- `docs/guides/testing/unified-grammar-ci.md`(テスト方針/実行方法/FAQ)を追加
|
||||||
|
|
||||||
|
注意/メモ
|
||||||
|
- AOT は LLVM18 が前提(CIでは apt.llvm.org を想定)。Windows 環境は別途 runner で段階導入
|
||||||
|
- リポ全体 `cargo test` は未整備テストで赤があり得るため、当面は対象テスト/スモークに集中
|
||||||
|
- 差分ログは冗長になり得るため、CIではフィルタ(例: INFO/WARN のみ、件数集計)を併用
|
||||||
|
|
||||||
|
実行/確認コマンド(ローカル)
|
||||||
|
- ビルド(JIT/VM): `cargo build --features cranelift-jit`
|
||||||
|
- jit 直実行(例): `NYASH_JIT_THRESHOLD=1 ./target/debug/nyash --jit-direct apps/tests/mir-branch-ret/main.nyash`
|
||||||
|
- テスト(本領域):
|
||||||
|
- `cargo test -q --test grammar_add_rules`
|
||||||
|
- `cargo test -q --test grammar_other_ops`
|
||||||
|
- 追加予定: `tests/snapshots/parser_mir_*`
|
||||||
|
|||||||
@ -17,3 +17,17 @@ Backends Strategy:
|
|||||||
- JIT 既定は Cranelift(feature: `cranelift-jit`)。AOT は必要に応じ `cranelift-object` を併用。
|
- JIT 既定は Cranelift(feature: `cranelift-jit`)。AOT は必要に応じ `cranelift-object` を併用。
|
||||||
|
|
||||||
This folder contains the living plan (PLAN.md) and the rolling snapshot of the current task focus (CURRENT_TASK.md). Semantics 層の導入により、Nyash スクリプト/VM/JIT(exe)の動作を一致させる。
|
This folder contains the living plan (PLAN.md) and the rolling snapshot of the current task focus (CURRENT_TASK.md). Semantics 層の導入により、Nyash スクリプト/VM/JIT(exe)の動作を一致させる。
|
||||||
|
|
||||||
|
## JIT Single-Exit Policy and TRACE
|
||||||
|
|
||||||
|
- Single-Exit: JIT は関数終端で単一の ret ブロックに合流する方針。分岐合流は BlockParam(最小PHI)で表現し、`end_function` で最終 seal を行う。
|
||||||
|
- Branch Fast-Path: then/else がともに i64 定数を即時 return する場合、`select(cond, K_then, K_else)` → `return` に縮約(常時有効)。
|
||||||
|
- TRACE 環境変数(必要時のみON):
|
||||||
|
- `NYASH_JIT_DUMP=1` …… Lower の要約/CFGライトダンプを表示
|
||||||
|
- `NYASH_JIT_TRACE_BLOCKS=1` … ブロック入場ログ(`[JIT-BLOCK] enter=<idx>`)
|
||||||
|
- `NYASH_JIT_TRACE_BR=1` …… br_if の cond 有無ログ
|
||||||
|
- `NYASH_JIT_TRACE_SEL=1` … select の cond/then/else 値(tag=100/101/102)
|
||||||
|
- `NYASH_JIT_TRACE_RET=1` … return 値ログ(tag=201=直前, 200=合流)
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- 旧フラグ `NYASH_JIT_FASTPATH_SELECT` は不要になりました(存在しても無視)。
|
||||||
|
|||||||
130
src/ast.rs
130
src/ast.rs
@ -881,132 +881,4 @@ impl ASTNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Tests =====
|
// Tests moved to integration tests to keep this file lean
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::box_trait::{StringBox, IntegerBox, BoolBox};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ast_node_creation() {
|
|
||||||
// Program node
|
|
||||||
let program = ASTNode::Program {
|
|
||||||
statements: vec![],
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
|
||||||
assert_eq!(program.node_type(), "Program");
|
|
||||||
assert!(program.info().contains("Program(0 statements)"));
|
|
||||||
|
|
||||||
// Variable node
|
|
||||||
let variable = ASTNode::Variable {
|
|
||||||
name: "test_var".to_string(),
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
|
||||||
assert_eq!(variable.node_type(), "Variable");
|
|
||||||
assert!(variable.is_expression());
|
|
||||||
assert!(!variable.is_statement());
|
|
||||||
|
|
||||||
// Assignment node
|
|
||||||
let assignment = ASTNode::Assignment {
|
|
||||||
target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }),
|
|
||||||
value: Box::new(ASTNode::Literal {
|
|
||||||
value: LiteralValue::Integer(42),
|
|
||||||
span: Span::unknown(),
|
|
||||||
}),
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
|
||||||
assert_eq!(assignment.node_type(), "Assignment");
|
|
||||||
assert!(!assignment.is_expression());
|
|
||||||
assert!(assignment.is_statement());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_binary_operator() {
|
|
||||||
let add_op = BinaryOperator::Add;
|
|
||||||
assert_eq!(format!("{}", add_op), "+");
|
|
||||||
|
|
||||||
let equals_op = BinaryOperator::Equal;
|
|
||||||
assert_eq!(format!("{}", equals_op), "==");
|
|
||||||
|
|
||||||
let less_equals_op = BinaryOperator::LessEqual;
|
|
||||||
assert_eq!(format!("{}", less_equals_op), "<=");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_complex_ast() {
|
|
||||||
// box TestBox { value }のAST
|
|
||||||
let mut methods = HashMap::new();
|
|
||||||
methods.insert("getValue".to_string(), ASTNode::FunctionDeclaration {
|
|
||||||
name: "getValue".to_string(),
|
|
||||||
params: vec![],
|
|
||||||
body: vec![
|
|
||||||
ASTNode::Return {
|
|
||||||
value: Some(Box::new(ASTNode::FieldAccess {
|
|
||||||
object: Box::new(ASTNode::This { span: Span::unknown() }),
|
|
||||||
field: "value".to_string(),
|
|
||||||
span: Span::unknown(),
|
|
||||||
})),
|
|
||||||
span: Span::unknown(),
|
|
||||||
}
|
|
||||||
],
|
|
||||||
is_static: false, // 通常のメソッド
|
|
||||||
is_override: false,
|
|
||||||
span: Span::unknown(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let box_decl = ASTNode::BoxDeclaration {
|
|
||||||
name: "TestBox".to_string(),
|
|
||||||
fields: vec!["value".to_string()],
|
|
||||||
public_fields: vec![],
|
|
||||||
private_fields: vec![],
|
|
||||||
methods,
|
|
||||||
constructors: HashMap::new(),
|
|
||||||
init_fields: vec![],
|
|
||||||
weak_fields: vec![], // 🔗 No weak fields in test
|
|
||||||
is_interface: false,
|
|
||||||
extends: vec![], // 🚀 Multi-delegation: Changed from None to vec![]
|
|
||||||
implements: vec![],
|
|
||||||
type_parameters: vec![], // No generics in test
|
|
||||||
is_static: false,
|
|
||||||
static_init: None,
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(box_decl.node_type(), "BoxDeclaration");
|
|
||||||
assert!(box_decl.info().contains("TestBox"));
|
|
||||||
assert!(box_decl.info().contains("1 fields"));
|
|
||||||
assert!(box_decl.info().contains("1 methods"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_method_call() {
|
|
||||||
// obj.getValue()のAST
|
|
||||||
let method_call = ASTNode::MethodCall {
|
|
||||||
object: Box::new(ASTNode::Variable { name: "obj".to_string(), span: Span::unknown() }),
|
|
||||||
method: "getValue".to_string(),
|
|
||||||
arguments: vec![],
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(method_call.node_type(), "MethodCall");
|
|
||||||
assert!(method_call.is_expression());
|
|
||||||
assert!(method_call.info().contains("getValue"));
|
|
||||||
assert!(method_call.info().contains("0 args"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_binary_operation() {
|
|
||||||
// x + y のAST
|
|
||||||
let binary_op = ASTNode::BinaryOp {
|
|
||||||
operator: BinaryOperator::Add,
|
|
||||||
left: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }),
|
|
||||||
right: Box::new(ASTNode::Variable { name: "y".to_string(), span: Span::unknown() }),
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(binary_op.node_type(), "BinaryOp");
|
|
||||||
assert!(binary_op.is_expression());
|
|
||||||
assert!(binary_op.info().contains("+"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -82,6 +82,11 @@ impl NyashInterpreter {
|
|||||||
|
|
||||||
// return文チェック
|
// return文チェック
|
||||||
if let super::ControlFlow::Return(return_val) = &self.control_flow {
|
if let super::ControlFlow::Return(return_val) = &self.control_flow {
|
||||||
|
if std::env::var("NYASH_INT_RET_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
let ty = return_val.type_name();
|
||||||
|
let sv = return_val.to_string_box().value;
|
||||||
|
eprintln!("[INT-RET] epilogue capture: type={} value={}", ty, sv);
|
||||||
|
}
|
||||||
result = return_val.clone_box();
|
result = return_val.clone_box();
|
||||||
self.control_flow = super::ControlFlow::None;
|
self.control_flow = super::ControlFlow::None;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -72,6 +72,12 @@ impl NyashInterpreter {
|
|||||||
} else {
|
} else {
|
||||||
Box::new(VoidBox::new())
|
Box::new(VoidBox::new())
|
||||||
};
|
};
|
||||||
|
// Optional diagnostic: trace return value (type + string view)
|
||||||
|
if std::env::var("NYASH_INT_RET_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
let ty = return_value.type_name();
|
||||||
|
let sv = return_value.to_string_box().value;
|
||||||
|
eprintln!("[INT-RET] return set: type={} value={}", ty, sv);
|
||||||
|
}
|
||||||
self.control_flow = super::ControlFlow::Return(return_value);
|
self.control_flow = super::ControlFlow::Return(return_value);
|
||||||
Ok(Box::new(VoidBox::new()))
|
Ok(Box::new(VoidBox::new()))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,128 +163,10 @@ impl LowerCore {
|
|||||||
// Branch/Jump need block mapping: pass indices
|
// Branch/Jump need block mapping: pass indices
|
||||||
match term {
|
match term {
|
||||||
crate::mir::MirInstruction::Branch { condition, then_bb, else_bb } => {
|
crate::mir::MirInstruction::Branch { condition, then_bb, else_bb } => {
|
||||||
// Fast-path (opt-in): if both successors immediately return known i64 constants,
|
self.lower_branch_terminator(builder, func, &bb_ids, *bb_id, condition, then_bb, else_bb, &succ_phi_order, enable_phi_min);
|
||||||
// lower as select+return(branchless)。NYASH_JIT_FASTPATH_SELECT=1 のときだけ有効。
|
|
||||||
let mut fastpath_done = false;
|
|
||||||
let fastpath_on = std::env::var("NYASH_JIT_FASTPATH_SELECT").ok().as_deref() == Some("1");
|
|
||||||
let succ_returns_const = |succ: &crate::mir::BasicBlock| -> Option<i64> {
|
|
||||||
// Pattern: [optional Nops] Const(Integer k) ; Return(Some that const))
|
|
||||||
use crate::mir::MirInstruction as I;
|
|
||||||
// find last Const and matching Return
|
|
||||||
if let Some(I::Return { value: Some(v) }) = &succ.terminator {
|
|
||||||
// Find definition of v in succ.instructions as a Const Integer
|
|
||||||
for ins in succ.instructions.iter() {
|
|
||||||
if let I::Const { dst, value } = ins {
|
|
||||||
if dst == v {
|
|
||||||
if let crate::mir::ConstValue::Integer(k) = value { return Some(*k); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
};
|
|
||||||
if fastpath_on { if let (Some(bb_then), Some(bb_else)) = (func.blocks.get(then_bb), func.blocks.get(else_bb)) {
|
|
||||||
if let (Some(k_then), Some(k_else)) = (succ_returns_const(bb_then), succ_returns_const(bb_else)) {
|
|
||||||
// cond, then, else on stack → select → return
|
|
||||||
self.push_value_if_known_or_param(builder, condition);
|
|
||||||
builder.emit_const_i64(k_then);
|
|
||||||
builder.emit_const_i64(k_else);
|
|
||||||
builder.emit_select_i64();
|
|
||||||
builder.emit_return();
|
|
||||||
fastpath_done = true;
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
if fastpath_done { continue; }
|
|
||||||
|
|
||||||
// Otherwise, emit CFG branch
|
|
||||||
// Try to place condition on stack (param/const path); builder will adapt
|
|
||||||
self.push_value_if_known_or_param(builder, condition);
|
|
||||||
// Map BasicBlockId -> index
|
|
||||||
let then_index = bb_ids.iter().position(|x| x == then_bb).unwrap_or(0);
|
|
||||||
let else_index = bb_ids.iter().position(|x| x == else_bb).unwrap_or(0);
|
|
||||||
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
|
|
||||||
eprintln!("[LowerCore] br_if: cur_bb={} then_idx={} else_idx={}", bb_id.0, then_index, else_index);
|
|
||||||
}
|
|
||||||
if enable_phi_min {
|
|
||||||
// For multi-PHI, push args in successor's phi order
|
|
||||||
let mut then_n = 0usize; let mut else_n = 0usize;
|
|
||||||
if let Some(order) = succ_phi_order.get(then_bb) {
|
|
||||||
let mut cnt = 0usize;
|
|
||||||
for dst in order.iter() {
|
|
||||||
// find input from current block
|
|
||||||
if let Some(bb_succ) = func.blocks.get(then_bb) {
|
|
||||||
// locate the specific phi to read its inputs
|
|
||||||
for ins in bb_succ.instructions.iter() {
|
|
||||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
|
||||||
if d2 == dst {
|
|
||||||
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
|
||||||
self.push_value_if_known_or_param(builder, val);
|
|
||||||
cnt += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cnt > 0 { builder.ensure_block_params_i64(then_index, cnt); }
|
|
||||||
then_n = cnt;
|
|
||||||
}
|
|
||||||
if let Some(order) = succ_phi_order.get(else_bb) {
|
|
||||||
let mut cnt = 0usize;
|
|
||||||
for dst in order.iter() {
|
|
||||||
if let Some(bb_succ) = func.blocks.get(else_bb) {
|
|
||||||
for ins in bb_succ.instructions.iter() {
|
|
||||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
|
||||||
if d2 == dst {
|
|
||||||
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
|
||||||
self.push_value_if_known_or_param(builder, val);
|
|
||||||
cnt += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cnt > 0 { builder.ensure_block_params_i64(else_index, cnt); }
|
|
||||||
else_n = cnt;
|
|
||||||
}
|
|
||||||
builder.br_if_with_args(then_index, else_index, then_n, else_n);
|
|
||||||
} else {
|
|
||||||
builder.br_if_top_is_true(then_index, else_index);
|
|
||||||
}
|
|
||||||
// Sealing is deferred to end_function to avoid premature seals during CFG construction
|
|
||||||
}
|
}
|
||||||
crate::mir::MirInstruction::Jump { target } => {
|
crate::mir::MirInstruction::Jump { target } => {
|
||||||
let target_index = bb_ids.iter().position(|x| x == target).unwrap_or(0);
|
self.lower_jump_terminator(builder, func, &bb_ids, *bb_id, target, &succ_phi_order, enable_phi_min);
|
||||||
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
|
|
||||||
eprintln!("[LowerCore] jump: cur_bb={} target_idx={}", bb_id.0, target_index);
|
|
||||||
}
|
|
||||||
if enable_phi_min {
|
|
||||||
let mut n = 0usize;
|
|
||||||
if let Some(order) = succ_phi_order.get(target) {
|
|
||||||
let mut cnt = 0usize;
|
|
||||||
if let Some(bb_succ) = func.blocks.get(target) {
|
|
||||||
for dst in order.iter() {
|
|
||||||
for ins in bb_succ.instructions.iter() {
|
|
||||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
|
||||||
if d2 == dst {
|
|
||||||
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
|
||||||
self.push_value_if_known_or_param(builder, val);
|
|
||||||
cnt += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cnt > 0 { builder.ensure_block_params_i64(target_index, cnt); }
|
|
||||||
n = cnt;
|
|
||||||
}
|
|
||||||
builder.jump_with_args(target_index, n);
|
|
||||||
} else {
|
|
||||||
builder.jump_to(target_index);
|
|
||||||
}
|
|
||||||
// Sealing is deferred to end_function to avoid premature seals during CFG construction
|
|
||||||
}
|
}
|
||||||
_ => { /* other terminators handled via generic emission below */ }
|
_ => { /* other terminators handled via generic emission below */ }
|
||||||
}
|
}
|
||||||
@ -581,7 +463,8 @@ impl LowerCore {
|
|||||||
// Clean path: delegate to ops_ext and return
|
// Clean path: delegate to ops_ext and return
|
||||||
let _ = self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?;
|
let _ = self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
if super::core_hostcall::lower_boxcall_simple_reads(b, &self.param_index, &self.known_i64, array, method.as_str(), args, dst.clone()) {
|
}
|
||||||
|
/* legacy BoxCall branch removed (now handled in ops_ext)
|
||||||
// handled in helper (read-only simple methods)
|
// handled in helper (read-only simple methods)
|
||||||
} else if matches!(method.as_str(), "sin" | "cos" | "abs" | "min" | "max") {
|
} else if matches!(method.as_str(), "sin" | "cos" | "abs" | "min" | "max") {
|
||||||
super::core_hostcall::lower_math_call(
|
super::core_hostcall::lower_math_call(
|
||||||
@ -1011,8 +894,7 @@ impl LowerCore {
|
|||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
*/
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction};
|
use crate::mir::{BasicBlockId, MirFunction, MirInstruction};
|
||||||
use super::super::builder::IRBuilder;
|
use super::super::builder::IRBuilder;
|
||||||
use super::LowerCore;
|
use super::LowerCore;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
impl LowerCore {
|
impl LowerCore {
|
||||||
pub(crate) fn build_phi_succords(
|
pub(crate) fn build_phi_succords(
|
||||||
@ -76,3 +75,139 @@ impl LowerCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LowerCore {
|
||||||
|
/// Lower a Branch terminator, including fast-path select+return and PHI(min) argument wiring.
|
||||||
|
pub(crate) fn lower_branch_terminator(
|
||||||
|
&mut self,
|
||||||
|
builder: &mut dyn IRBuilder,
|
||||||
|
func: &MirFunction,
|
||||||
|
bb_ids: &Vec<BasicBlockId>,
|
||||||
|
bb_id: BasicBlockId,
|
||||||
|
condition: &crate::mir::ValueId,
|
||||||
|
then_bb: &BasicBlockId,
|
||||||
|
else_bb: &BasicBlockId,
|
||||||
|
succ_phi_order: &HashMap<BasicBlockId, Vec<crate::mir::ValueId>>,
|
||||||
|
enable_phi_min: bool,
|
||||||
|
) {
|
||||||
|
// Fast-path: if both successors immediately return known i64 constants, lower as select+return
|
||||||
|
let mut fastpath_done = false;
|
||||||
|
let succ_returns_const = |succ: &crate::mir::BasicBlock| -> Option<i64> {
|
||||||
|
use crate::mir::MirInstruction as I;
|
||||||
|
if let Some(I::Return { value: Some(v) }) = &succ.terminator {
|
||||||
|
for ins in succ.instructions.iter() {
|
||||||
|
if let I::Const { dst, value } = ins {
|
||||||
|
if dst == v {
|
||||||
|
if let crate::mir::ConstValue::Integer(k) = value { return Some(*k); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let (Some(bb_then), Some(bb_else)) = (func.blocks.get(then_bb), func.blocks.get(else_bb)) {
|
||||||
|
if let (Some(k_then), Some(k_else)) = (succ_returns_const(bb_then), succ_returns_const(bb_else)) {
|
||||||
|
self.push_value_if_known_or_param(builder, condition);
|
||||||
|
builder.emit_const_i64(k_then);
|
||||||
|
builder.emit_const_i64(k_else);
|
||||||
|
builder.emit_select_i64();
|
||||||
|
builder.emit_return();
|
||||||
|
fastpath_done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fastpath_done { return; }
|
||||||
|
|
||||||
|
// Otherwise, emit CFG branch with optional PHI(min) argument wiring
|
||||||
|
self.push_value_if_known_or_param(builder, condition);
|
||||||
|
let then_index = bb_ids.iter().position(|x| x == then_bb).unwrap_or(0);
|
||||||
|
let else_index = bb_ids.iter().position(|x| x == else_bb).unwrap_or(0);
|
||||||
|
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[LowerCore] br_if: cur_bb={} then_idx={} else_idx={}", bb_id.0, then_index, else_index);
|
||||||
|
}
|
||||||
|
if enable_phi_min {
|
||||||
|
let mut then_n = 0usize; let mut else_n = 0usize;
|
||||||
|
if let Some(order) = succ_phi_order.get(then_bb) {
|
||||||
|
let mut cnt = 0usize;
|
||||||
|
if let Some(bb_succ) = func.blocks.get(then_bb) {
|
||||||
|
for dst in order.iter() {
|
||||||
|
for ins in bb_succ.instructions.iter() {
|
||||||
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||||
|
if d2 == dst {
|
||||||
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == &bb_id) {
|
||||||
|
self.push_value_if_known_or_param(builder, val);
|
||||||
|
cnt += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cnt > 0 { builder.ensure_block_params_i64(then_index, cnt); }
|
||||||
|
then_n = cnt;
|
||||||
|
}
|
||||||
|
if let Some(order) = succ_phi_order.get(else_bb) {
|
||||||
|
let mut cnt = 0usize;
|
||||||
|
if let Some(bb_succ) = func.blocks.get(else_bb) {
|
||||||
|
for dst in order.iter() {
|
||||||
|
for ins in bb_succ.instructions.iter() {
|
||||||
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||||
|
if d2 == dst {
|
||||||
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == &bb_id) {
|
||||||
|
self.push_value_if_known_or_param(builder, val);
|
||||||
|
cnt += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cnt > 0 { builder.ensure_block_params_i64(else_index, cnt); }
|
||||||
|
else_n = cnt;
|
||||||
|
}
|
||||||
|
builder.br_if_with_args(then_index, else_index, then_n, else_n);
|
||||||
|
} else {
|
||||||
|
builder.br_if_top_is_true(then_index, else_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lower a Jump terminator with optional PHI(min) argument wiring.
|
||||||
|
pub(crate) fn lower_jump_terminator(
|
||||||
|
&mut self,
|
||||||
|
builder: &mut dyn IRBuilder,
|
||||||
|
func: &MirFunction,
|
||||||
|
bb_ids: &Vec<BasicBlockId>,
|
||||||
|
bb_id: BasicBlockId,
|
||||||
|
target: &BasicBlockId,
|
||||||
|
succ_phi_order: &HashMap<BasicBlockId, Vec<crate::mir::ValueId>>,
|
||||||
|
enable_phi_min: bool,
|
||||||
|
) {
|
||||||
|
let target_index = bb_ids.iter().position(|x| x == target).unwrap_or(0);
|
||||||
|
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[LowerCore] jump: cur_bb={} target_idx={}", bb_id.0, target_index);
|
||||||
|
}
|
||||||
|
if enable_phi_min {
|
||||||
|
let mut n = 0usize;
|
||||||
|
if let Some(order) = succ_phi_order.get(target) {
|
||||||
|
let mut cnt = 0usize;
|
||||||
|
if let Some(bb_succ) = func.blocks.get(target) {
|
||||||
|
for dst in order.iter() {
|
||||||
|
for ins in bb_succ.instructions.iter() {
|
||||||
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||||
|
if d2 == dst {
|
||||||
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == &bb_id) {
|
||||||
|
self.push_value_if_known_or_param(builder, val);
|
||||||
|
cnt += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cnt > 0 { builder.ensure_block_params_i64(target_index, cnt); }
|
||||||
|
n = cnt;
|
||||||
|
}
|
||||||
|
builder.jump_with_args(target_index, n);
|
||||||
|
} else {
|
||||||
|
builder.jump_to(target_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@ use crate::ast::{ASTNode, LiteralValue, BinaryOperator};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
mod builder_calls;
|
||||||
|
mod stmts;
|
||||||
|
|
||||||
fn resolve_include_path_builder(filename: &str) -> String {
|
fn resolve_include_path_builder(filename: &str) -> String {
|
||||||
// If relative path provided, keep as is
|
// If relative path provided, keep as is
|
||||||
@ -205,97 +207,7 @@ impl MirBuilder {
|
|||||||
self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Write, ptr })
|
self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Write, ptr })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lower a box method (e.g., birth) into a standalone MIR function
|
// moved to builder_calls.rs: lower_method_as_function
|
||||||
/// func_name: Fully-qualified name like "Person.birth/1"
|
|
||||||
/// box_name: Owning box type name (used for 'me' param type)
|
|
||||||
fn lower_method_as_function(
|
|
||||||
&mut self,
|
|
||||||
func_name: String,
|
|
||||||
box_name: String,
|
|
||||||
params: Vec<String>,
|
|
||||||
body: Vec<ASTNode>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
// Prepare function signature: (me: Box(box_name), args: Unknown...)-> Void
|
|
||||||
let mut param_types = Vec::new();
|
|
||||||
param_types.push(MirType::Box(box_name.clone())); // me
|
|
||||||
for _ in ¶ms {
|
|
||||||
param_types.push(MirType::Unknown);
|
|
||||||
}
|
|
||||||
// Lightweight return type inference: if there is an explicit `return <expr>`
|
|
||||||
// in the top-level body, mark return type as Unknown; otherwise Void.
|
|
||||||
let mut returns_value = false;
|
|
||||||
for st in &body {
|
|
||||||
if let ASTNode::Return { value: Some(_), .. } = st { returns_value = true; break; }
|
|
||||||
}
|
|
||||||
let ret_ty = if returns_value { MirType::Unknown } else { MirType::Void };
|
|
||||||
|
|
||||||
let signature = FunctionSignature {
|
|
||||||
name: func_name,
|
|
||||||
params: param_types,
|
|
||||||
return_type: ret_ty,
|
|
||||||
effects: EffectMask::READ.add(Effect::ReadHeap), // conservative
|
|
||||||
};
|
|
||||||
let entry = self.block_gen.next();
|
|
||||||
let function = MirFunction::new(signature, entry);
|
|
||||||
|
|
||||||
// Save current builder state
|
|
||||||
let saved_function = self.current_function.take();
|
|
||||||
let saved_block = self.current_block.take();
|
|
||||||
let saved_var_map = std::mem::take(&mut self.variable_map);
|
|
||||||
let saved_value_gen = self.value_gen.clone();
|
|
||||||
// Reset value id generator so that params start from %0, %1, ...
|
|
||||||
self.value_gen.reset();
|
|
||||||
|
|
||||||
// Switch context to new function
|
|
||||||
self.current_function = Some(function);
|
|
||||||
self.current_block = Some(entry);
|
|
||||||
self.ensure_block_exists(entry)?;
|
|
||||||
|
|
||||||
// Create parameter value ids and bind variable names
|
|
||||||
if let Some(ref mut f) = self.current_function {
|
|
||||||
// 'me' parameter will be %0
|
|
||||||
let me_id = self.value_gen.next();
|
|
||||||
f.params.push(me_id);
|
|
||||||
self.variable_map.insert("me".to_string(), me_id);
|
|
||||||
// Record origin: 'me' belongs to this box type (enables weak field wiring)
|
|
||||||
self.value_origin_newbox.insert(me_id, box_name.clone());
|
|
||||||
// user parameters continue as %1..N
|
|
||||||
for p in ¶ms {
|
|
||||||
let pid = self.value_gen.next();
|
|
||||||
f.params.push(pid);
|
|
||||||
self.variable_map.insert(p.clone(), pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lower body as a Program block
|
|
||||||
let program_ast = ASTNode::Program { statements: body, span: crate::ast::Span::unknown() };
|
|
||||||
let _last = self.build_expression(program_ast)?;
|
|
||||||
|
|
||||||
// Ensure function is properly terminated
|
|
||||||
if let Some(ref mut f) = self.current_function {
|
|
||||||
if let Some(block) = f.get_block(self.current_block.unwrap()) {
|
|
||||||
if !block.is_terminated() {
|
|
||||||
let void_val = self.value_gen.next();
|
|
||||||
self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void })?;
|
|
||||||
self.emit_instruction(MirInstruction::Return { value: Some(void_val) })?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take the function out and add to module
|
|
||||||
let finalized_function = self.current_function.take().unwrap();
|
|
||||||
if let Some(ref mut module) = self.current_module {
|
|
||||||
module.add_function(finalized_function);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore builder state
|
|
||||||
self.current_function = saved_function;
|
|
||||||
self.current_block = saved_block;
|
|
||||||
self.variable_map = saved_var_map;
|
|
||||||
self.value_gen = saved_value_gen;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build a complete MIR module from AST
|
/// Build a complete MIR module from AST
|
||||||
pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
|
pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
|
||||||
@ -704,7 +616,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build function call
|
/// Build function call
|
||||||
fn build_function_call(&mut self, name: String, args: Vec<ASTNode>) -> Result<ValueId, String> {
|
fn build_function_call_legacy(&mut self, name: String, args: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
// Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type")
|
// Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type")
|
||||||
if (name == "isType" || name == "asType") && args.len() == 2 {
|
if (name == "isType" || name == "asType") && args.len() == 2 {
|
||||||
if let Some(type_name) = Self::extract_string_literal(&args[1]) {
|
if let Some(type_name) = Self::extract_string_literal(&args[1]) {
|
||||||
@ -807,7 +719,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build print statement - ExternCall to env.console.log (Box哲学準拠)
|
/// Build print statement - ExternCall to env.console.log (Box哲学準拠)
|
||||||
fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
fn build_print_statement_legacy(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
builder_debug_log("enter build_print_statement");
|
builder_debug_log("enter build_print_statement");
|
||||||
// 根治: print(isType(...)) / print(asType(...)) / print(obj.is(...)) / print(obj.as(...)) は必ずTypeOpを先に生成してからprintする
|
// 根治: print(isType(...)) / print(asType(...)) / print(obj.is(...)) / print(obj.as(...)) は必ずTypeOpを先に生成してからprintする
|
||||||
match &expression {
|
match &expression {
|
||||||
@ -877,7 +789,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build a block of statements
|
/// Build a block of statements
|
||||||
fn build_block(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
|
fn build_block_legacy(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
let mut last_value = None;
|
let mut last_value = None;
|
||||||
|
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
@ -896,7 +808,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build if statement with conditional branches
|
/// Build if statement with conditional branches
|
||||||
fn build_if_statement(&mut self, condition: ASTNode, then_branch: ASTNode, else_branch: Option<ASTNode>) -> Result<ValueId, String> {
|
fn build_if_statement_legacy(&mut self, condition: ASTNode, then_branch: ASTNode, else_branch: Option<ASTNode>) -> Result<ValueId, String> {
|
||||||
let condition_val = self.build_expression(condition)?;
|
let condition_val = self.build_expression(condition)?;
|
||||||
|
|
||||||
// Create basic blocks for then/else/merge
|
// Create basic blocks for then/else/merge
|
||||||
@ -1029,14 +941,14 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build a loop statement: loop(condition) { body }
|
/// Build a loop statement: loop(condition) { body }
|
||||||
fn build_loop_statement(&mut self, condition: ASTNode, body: Vec<ASTNode>) -> Result<ValueId, String> {
|
fn build_loop_statement_legacy(&mut self, condition: ASTNode, body: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
// Use the specialized LoopBuilder for proper SSA loop construction
|
// Use the specialized LoopBuilder for proper SSA loop construction
|
||||||
let mut loop_builder = super::loop_builder::LoopBuilder::new(self);
|
let mut loop_builder = super::loop_builder::LoopBuilder::new(self);
|
||||||
loop_builder.build_loop(condition, body)
|
loop_builder.build_loop(condition, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a try/catch statement
|
/// Build a try/catch statement
|
||||||
fn build_try_catch_statement(&mut self, try_body: Vec<ASTNode>, catch_clauses: Vec<crate::ast::CatchClause>, finally_body: Option<Vec<ASTNode>>) -> Result<ValueId, String> {
|
fn build_try_catch_statement_legacy(&mut self, try_body: Vec<ASTNode>, catch_clauses: Vec<crate::ast::CatchClause>, finally_body: Option<Vec<ASTNode>>) -> Result<ValueId, String> {
|
||||||
if std::env::var("NYASH_BUILDER_DISABLE_TRYCATCH").ok().as_deref() == Some("1") {
|
if std::env::var("NYASH_BUILDER_DISABLE_TRYCATCH").ok().as_deref() == Some("1") {
|
||||||
// Compatibility fallback: build try body only; ignore handlers/finally
|
// Compatibility fallback: build try body only; ignore handlers/finally
|
||||||
let try_ast = ASTNode::Program { statements: try_body, span: crate::ast::Span::unknown() };
|
let try_ast = ASTNode::Program { statements: try_body, span: crate::ast::Span::unknown() };
|
||||||
@ -1133,7 +1045,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build a throw statement
|
/// Build a throw statement
|
||||||
fn build_throw_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
fn build_throw_statement_legacy(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
if std::env::var("NYASH_BUILDER_DISABLE_THROW").ok().as_deref() == Some("1") {
|
if std::env::var("NYASH_BUILDER_DISABLE_THROW").ok().as_deref() == Some("1") {
|
||||||
// Fallback: route to debug trace and return the value
|
// Fallback: route to debug trace and return the value
|
||||||
let v = self.build_expression(expression)?;
|
let v = self.build_expression(expression)?;
|
||||||
@ -1160,7 +1072,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build local variable declarations with optional initial values
|
/// Build local variable declarations with optional initial values
|
||||||
fn build_local_statement(&mut self, variables: Vec<String>, initial_values: Vec<Option<Box<ASTNode>>>) -> Result<ValueId, String> {
|
fn build_local_statement_legacy(&mut self, variables: Vec<String>, initial_values: Vec<Option<Box<ASTNode>>>) -> Result<ValueId, String> {
|
||||||
let mut last_value = None;
|
let mut last_value = None;
|
||||||
|
|
||||||
// Process each variable declaration
|
// Process each variable declaration
|
||||||
@ -1188,7 +1100,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build return statement
|
/// Build return statement
|
||||||
fn build_return_statement(&mut self, value: Option<Box<ASTNode>>) -> Result<ValueId, String> {
|
fn build_return_statement_legacy(&mut self, value: Option<Box<ASTNode>>) -> Result<ValueId, String> {
|
||||||
let return_value = if let Some(expr) = value {
|
let return_value = if let Some(expr) = value {
|
||||||
self.build_expression(*expr)?
|
self.build_expression(*expr)?
|
||||||
} else {
|
} else {
|
||||||
@ -1463,7 +1375,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build nowait statement: nowait variable = expression
|
/// Build nowait statement: nowait variable = expression
|
||||||
fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result<ValueId, String> {
|
fn build_nowait_statement_legacy(&mut self, variable: String, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
// If expression is a method call, prefer true async via env.future.spawn_instance
|
// If expression is a method call, prefer true async via env.future.spawn_instance
|
||||||
if let ASTNode::MethodCall { object, method, arguments, .. } = expression.clone() {
|
if let ASTNode::MethodCall { object, method, arguments, .. } = expression.clone() {
|
||||||
let recv_val = self.build_expression(*object)?;
|
let recv_val = self.build_expression(*object)?;
|
||||||
@ -1493,7 +1405,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build await expression: await expression
|
/// Build await expression: await expression
|
||||||
fn build_await_expression(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
fn build_await_expression_legacy(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
// Evaluate the expression (should be a Future)
|
// Evaluate the expression (should be a Future)
|
||||||
let future_value = self.build_expression(expression)?;
|
let future_value = self.build_expression(expression)?;
|
||||||
|
|
||||||
@ -1515,7 +1427,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build me expression: me
|
/// Build me expression: me
|
||||||
fn build_me_expression(&mut self) -> Result<ValueId, String> {
|
fn build_me_expression_legacy(&mut self) -> Result<ValueId, String> {
|
||||||
// If lowering a method/birth function, "me" should be a parameter
|
// If lowering a method/birth function, "me" should be a parameter
|
||||||
if let Some(id) = self.variable_map.get("me").cloned() {
|
if let Some(id) = self.variable_map.get("me").cloned() {
|
||||||
return Ok(id);
|
return Ok(id);
|
||||||
@ -1531,7 +1443,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build method call: object.method(arguments)
|
/// Build method call: object.method(arguments)
|
||||||
fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
fn build_method_call_legacy(&mut self, object: ASTNode, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
// Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type")
|
// Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type")
|
||||||
if (method == "is" || method == "as") && arguments.len() == 1 {
|
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||||
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||||
@ -1827,7 +1739,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Map a user-facing type name to MIR type
|
/// Map a user-facing type name to MIR type
|
||||||
fn parse_type_name_to_mir(name: &str) -> super::MirType {
|
fn parse_type_name_to_mir_legacy(name: &str) -> super::MirType {
|
||||||
match name {
|
match name {
|
||||||
"Integer" | "Int" | "I64" => super::MirType::Integer,
|
"Integer" | "Int" | "I64" => super::MirType::Integer,
|
||||||
"Float" | "F64" => super::MirType::Float,
|
"Float" | "F64" => super::MirType::Float,
|
||||||
@ -1840,7 +1752,7 @@ impl MirBuilder {
|
|||||||
|
|
||||||
/// Extract string literal from AST node if possible
|
/// Extract string literal from AST node if possible
|
||||||
/// Supports: Literal("Type") and new StringBox("Type")
|
/// Supports: Literal("Type") and new StringBox("Type")
|
||||||
fn extract_string_literal(node: &ASTNode) -> Option<String> {
|
fn extract_string_literal_legacy(node: &ASTNode) -> Option<String> {
|
||||||
let mut cur = node;
|
let mut cur = node;
|
||||||
loop {
|
loop {
|
||||||
match cur {
|
match cur {
|
||||||
@ -1855,7 +1767,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build from expression: from Parent.method(arguments)
|
/// Build from expression: from Parent.method(arguments)
|
||||||
fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
fn build_from_expression_legacy(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
// Build argument expressions
|
// Build argument expressions
|
||||||
let mut arg_values = Vec::new();
|
let mut arg_values = Vec::new();
|
||||||
for arg in arguments {
|
for arg in arguments {
|
||||||
@ -1886,7 +1798,7 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Lower a static method body into a standalone MIR function (no `me` parameter)
|
/// Lower a static method body into a standalone MIR function (no `me` parameter)
|
||||||
fn lower_static_method_as_function(
|
fn lower_static_method_as_function_legacy(
|
||||||
&mut self,
|
&mut self,
|
||||||
func_name: String,
|
func_name: String,
|
||||||
params: Vec<String>,
|
params: Vec<String>,
|
||||||
|
|||||||
262
src/mir/builder/builder_calls.rs
Normal file
262
src/mir/builder/builder_calls.rs
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
// Extracted call-related builders from builder.rs to keep files lean
|
||||||
|
use super::{
|
||||||
|
MirInstruction, FunctionSignature, EffectMask, Effect, MirType, ValueId,
|
||||||
|
};
|
||||||
|
use crate::mir::{TypeOpKind, slot_registry};
|
||||||
|
use crate::ast::{ASTNode, LiteralValue};
|
||||||
|
|
||||||
|
impl super::MirBuilder {
|
||||||
|
// Build function call: name(args)
|
||||||
|
pub(super) fn build_function_call(&mut self, name: String, args: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
|
// Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type")
|
||||||
|
if (name == "isType" || name == "asType") && args.len() == 2 {
|
||||||
|
if let Some(type_name) = Self::extract_string_literal(&args[1]) {
|
||||||
|
let val = self.build_expression(args[0].clone())?;
|
||||||
|
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Keep original args for special handling (math.*)
|
||||||
|
let raw_args = args.clone();
|
||||||
|
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
|
||||||
|
// Special-case: math.* as function-style (sin/cos/abs/min/max)
|
||||||
|
let is_math_func = matches!(name.as_str(), "sin" | "cos" | "abs" | "min" | "max");
|
||||||
|
if is_math_func {
|
||||||
|
// Build numeric args directly for math.* to preserve f64 typing
|
||||||
|
let mut math_args: Vec<ValueId> = Vec::new();
|
||||||
|
for a in raw_args.into_iter() {
|
||||||
|
match a {
|
||||||
|
ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => {
|
||||||
|
let v = self.build_expression(arguments[0].clone())?;
|
||||||
|
math_args.push(v);
|
||||||
|
}
|
||||||
|
ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => {
|
||||||
|
let iv = self.build_expression(arguments[0].clone())?;
|
||||||
|
let fv = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::TypeOp { dst: fv, op: TypeOpKind::Cast, value: iv, ty: MirType::Float })?;
|
||||||
|
math_args.push(fv);
|
||||||
|
}
|
||||||
|
ASTNode::Literal { value: LiteralValue::Float(_), .. } => {
|
||||||
|
let v = self.build_expression(a)?; math_args.push(v);
|
||||||
|
}
|
||||||
|
other => { let v = self.build_expression(other)?; math_args.push(v); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// new MathBox()
|
||||||
|
let math_recv = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::NewBox { dst: math_recv, box_type: "MathBox".to_string(), args: vec![] })?;
|
||||||
|
self.value_origin_newbox.insert(math_recv, "MathBox".to_string());
|
||||||
|
// birth()
|
||||||
|
let birt_mid = slot_registry::resolve_slot_by_type_name("MathBox", "birth");
|
||||||
|
self.emit_box_or_plugin_call(
|
||||||
|
None,
|
||||||
|
math_recv,
|
||||||
|
"birth".to_string(),
|
||||||
|
birt_mid,
|
||||||
|
vec![],
|
||||||
|
EffectMask::READ,
|
||||||
|
)?;
|
||||||
|
// call method
|
||||||
|
self.emit_box_or_plugin_call(
|
||||||
|
Some(dst),
|
||||||
|
math_recv,
|
||||||
|
name,
|
||||||
|
None,
|
||||||
|
math_args,
|
||||||
|
EffectMask::READ,
|
||||||
|
)?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: call via fully-qualified function name string
|
||||||
|
let mut arg_values = Vec::new();
|
||||||
|
for a in args { arg_values.push(self.build_expression(a)?); }
|
||||||
|
let fun_val = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const { dst: fun_val, value: super::ConstValue::String(name) })?;
|
||||||
|
self.emit_instruction(MirInstruction::Call { dst: Some(dst), func: fun_val, args: arg_values, effects: EffectMask::READ.add(Effect::ReadHeap) })?;
|
||||||
|
Ok(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build method call: object.method(arguments)
|
||||||
|
pub(super) fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
|
// Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type")
|
||||||
|
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||||
|
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||||
|
let object_value = self.build_expression(object.clone())?;
|
||||||
|
let mir_ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ExternCall: env.X.* pattern via field access (e.g., env.future.delay)
|
||||||
|
if let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object.clone() {
|
||||||
|
if let ASTNode::Variable { name: env_name, .. } = *env_obj {
|
||||||
|
if env_name == "env" {
|
||||||
|
let mut arg_values = Vec::new();
|
||||||
|
for arg in &arguments { arg_values.push(self.build_expression(arg.clone())?); }
|
||||||
|
match (env_field.as_str(), method.as_str()) {
|
||||||
|
("future", "delay") => {
|
||||||
|
let result_id = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::ExternCall {
|
||||||
|
dst: Some(result_id), iface_name: "env.future".to_string(), method_name: "delay".to_string(), args: arg_values,
|
||||||
|
effects: EffectMask::READ.add(Effect::Io),
|
||||||
|
})?;
|
||||||
|
return Ok(result_id);
|
||||||
|
}
|
||||||
|
("task", "currentToken") => {
|
||||||
|
let result_id = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::ExternCall {
|
||||||
|
dst: Some(result_id), iface_name: "env.task".to_string(), method_name: "currentToken".to_string(), args: arg_values,
|
||||||
|
effects: EffectMask::READ,
|
||||||
|
})?;
|
||||||
|
return Ok(result_id);
|
||||||
|
}
|
||||||
|
("task", "cancelCurrent") => {
|
||||||
|
self.emit_instruction(MirInstruction::ExternCall {
|
||||||
|
dst: None, iface_name: "env.task".to_string(), method_name: "cancelCurrent".to_string(), args: arg_values,
|
||||||
|
effects: EffectMask::IO,
|
||||||
|
})?;
|
||||||
|
let void_id = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?;
|
||||||
|
return Ok(void_id);
|
||||||
|
}
|
||||||
|
("console", "log") => {
|
||||||
|
self.emit_instruction(MirInstruction::ExternCall {
|
||||||
|
dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: arg_values, effects: EffectMask::IO,
|
||||||
|
})?;
|
||||||
|
let void_id = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?;
|
||||||
|
return Ok(void_id);
|
||||||
|
}
|
||||||
|
("console", "readLine") => {
|
||||||
|
let result_id = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::ExternCall {
|
||||||
|
dst: Some(result_id), iface_name: "env.console".to_string(), method_name: "readLine".to_string(), args: arg_values,
|
||||||
|
effects: EffectMask::IO,
|
||||||
|
})?;
|
||||||
|
return Ok(result_id);
|
||||||
|
}
|
||||||
|
("canvas", m @ ("fillRect" | "fillText")) => {
|
||||||
|
self.emit_instruction(MirInstruction::ExternCall {
|
||||||
|
dst: None, iface_name: "env.canvas".to_string(), method_name: m.to_string(), args: arg_values, effects: EffectMask::IO,
|
||||||
|
})?;
|
||||||
|
let void_id = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?;
|
||||||
|
return Ok(void_id);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If object is `me` within a static box, lower to direct Call: BoxName.method/N
|
||||||
|
if let ASTNode::Me { .. } = object {
|
||||||
|
if let Some(cls_name) = self.current_static_box.clone() {
|
||||||
|
let mut arg_values: Vec<ValueId> = Vec::new();
|
||||||
|
for a in &arguments { arg_values.push(self.build_expression(a.clone())?); }
|
||||||
|
let result_id = self.value_gen.next();
|
||||||
|
let fun_name = format!("{}.{}{}", cls_name, method, format!("/{}", arg_values.len()));
|
||||||
|
let fun_val = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const { dst: fun_val, value: super::ConstValue::String(fun_name) })?;
|
||||||
|
self.emit_instruction(MirInstruction::Call { dst: Some(result_id), func: fun_val, args: arg_values, effects: EffectMask::READ.add(Effect::ReadHeap) })?;
|
||||||
|
return Ok(result_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Build the object expression
|
||||||
|
let object_value = self.build_expression(object.clone())?;
|
||||||
|
// Secondary interception for is/as
|
||||||
|
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||||
|
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||||
|
let mir_ty = Self::parse_type_name_to_mir(&type_name);
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||||
|
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback: generic plugin invoke
|
||||||
|
let mut arg_values: Vec<ValueId> = Vec::new();
|
||||||
|
for a in &arguments { arg_values.push(self.build_expression(a.clone())?); }
|
||||||
|
let result_id = self.value_gen.next();
|
||||||
|
self.emit_box_or_plugin_call(Some(result_id), object_value, method, None, arg_values, EffectMask::READ.add(Effect::ReadHeap))?;
|
||||||
|
Ok(result_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map a user-facing type name to MIR type
|
||||||
|
pub(super) fn parse_type_name_to_mir(name: &str) -> super::MirType {
|
||||||
|
match name {
|
||||||
|
"Integer" | "Int" | "I64" => super::MirType::Integer,
|
||||||
|
"Float" | "F64" => super::MirType::Float,
|
||||||
|
"Bool" | "Boolean" => super::MirType::Bool,
|
||||||
|
"String" => super::MirType::String,
|
||||||
|
"Void" | "Unit" => super::MirType::Void,
|
||||||
|
other => super::MirType::Box(other.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract string literal from AST node if possible
|
||||||
|
pub(super) fn extract_string_literal(node: &ASTNode) -> Option<String> {
|
||||||
|
let mut cur = node;
|
||||||
|
loop {
|
||||||
|
match cur {
|
||||||
|
ASTNode::Literal { value: LiteralValue::String(s), .. } => return Some(s.clone()),
|
||||||
|
ASTNode::New { class, arguments, .. } if class == "StringBox" && arguments.len() == 1 => {
|
||||||
|
cur = &arguments[0];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build from expression: from Parent.method(arguments)
|
||||||
|
pub(super) fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
|
let mut arg_values = Vec::new();
|
||||||
|
for arg in arguments { arg_values.push(self.build_expression(arg)?); }
|
||||||
|
let parent_value = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const { dst: parent_value, value: super::ConstValue::String(parent) })?;
|
||||||
|
let result_id = self.value_gen.next();
|
||||||
|
self.emit_box_or_plugin_call(Some(result_id), parent_value, method, None, arg_values, EffectMask::READ.add(Effect::ReadHeap))?;
|
||||||
|
Ok(result_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower a box method into a standalone MIR function (with `me` parameter)
|
||||||
|
pub(super) fn lower_method_as_function(&mut self, func_name: String, box_name: String, params: Vec<String>, body: Vec<ASTNode>) -> Result<(), String> {
|
||||||
|
let mut param_types = Vec::new(); param_types.push(MirType::Box(box_name.clone())); for _ in ¶ms { param_types.push(MirType::Unknown); }
|
||||||
|
let mut returns_value = false; for st in &body { if let ASTNode::Return { value: Some(_), .. } = st { returns_value = true; break; } }
|
||||||
|
let ret_ty = if returns_value { MirType::Unknown } else { MirType::Void };
|
||||||
|
let signature = FunctionSignature { name: func_name, params: param_types, return_type: ret_ty, effects: EffectMask::READ.add(Effect::ReadHeap) };
|
||||||
|
let entry = self.block_gen.next(); let function = super::MirFunction::new(signature, entry);
|
||||||
|
let saved_function = self.current_function.take(); let saved_block = self.current_block.take();
|
||||||
|
let saved_var_map = std::mem::take(&mut self.variable_map); let saved_value_gen = self.value_gen.clone();
|
||||||
|
self.value_gen.reset(); self.current_function = Some(function); self.current_block = Some(entry); self.ensure_block_exists(entry)?;
|
||||||
|
if let Some(ref mut f) = self.current_function { let me_id = self.value_gen.next(); f.params.push(me_id); self.variable_map.insert("me".to_string(), me_id); self.value_origin_newbox.insert(me_id, box_name.clone()); for p in ¶ms { let pid = self.value_gen.next(); f.params.push(pid); self.variable_map.insert(p.clone(), pid); } }
|
||||||
|
let program_ast = ASTNode::Program { statements: body, span: crate::ast::Span::unknown() }; let _last = self.build_expression(program_ast)?;
|
||||||
|
if let Some(ref mut f) = self.current_function { if let Some(block) = f.get_block(self.current_block.unwrap()) { if !block.is_terminated() { let void_val = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: void_val, value: super::ConstValue::Void })?; self.emit_instruction(MirInstruction::Return { value: Some(void_val) })?; } } }
|
||||||
|
let finalized_function = self.current_function.take().unwrap(); if let Some(ref mut module) = self.current_module { module.add_function(finalized_function); }
|
||||||
|
self.current_function = saved_function; self.current_block = saved_block; self.variable_map = saved_var_map; self.value_gen = saved_value_gen; Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower a static method body into a standalone MIR function (no `me` parameter)
|
||||||
|
pub(super) fn lower_static_method_as_function(&mut self, func_name: String, params: Vec<String>, body: Vec<ASTNode>) -> Result<(), String> {
|
||||||
|
let mut param_types = Vec::new(); for _ in ¶ms { param_types.push(MirType::Unknown); }
|
||||||
|
let mut returns_value = false; for st in &body { if let ASTNode::Return { value: Some(_), .. } = st { returns_value = true; break; } }
|
||||||
|
let ret_ty = if returns_value { MirType::Unknown } else { MirType::Void };
|
||||||
|
let signature = FunctionSignature { name: func_name, params: param_types, return_type: ret_ty, effects: EffectMask::READ.add(Effect::ReadHeap) };
|
||||||
|
let entry = self.block_gen.next(); let function = super::MirFunction::new(signature, entry);
|
||||||
|
let saved_function = self.current_function.take(); let saved_block = self.current_block.take(); let saved_var_map = std::mem::take(&mut self.variable_map); let saved_value_gen = self.value_gen.clone(); self.value_gen.reset();
|
||||||
|
self.current_function = Some(function); self.current_block = Some(entry); self.ensure_block_exists(entry)?;
|
||||||
|
if let Some(ref mut f) = self.current_function { for p in ¶ms { let pid = self.value_gen.next(); f.params.push(pid); self.variable_map.insert(p.clone(), pid); } }
|
||||||
|
let program_ast = ASTNode::Program { statements: body, span: crate::ast::Span::unknown() }; let _last = self.build_expression(program_ast)?;
|
||||||
|
if let Some(ref mut f) = self.current_function { if let Some(block) = f.get_block(self.current_block.unwrap()) { if !block.is_terminated() { let void_val = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: void_val, value: super::ConstValue::Void })?; self.emit_instruction(MirInstruction::Return { value: Some(void_val) })?; } } }
|
||||||
|
let finalized = self.current_function.take().unwrap(); if let Some(ref mut module) = self.current_module { module.add_function(finalized); }
|
||||||
|
self.current_function = saved_function; self.current_block = saved_block; self.variable_map = saved_var_map; self.value_gen = saved_value_gen; Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/mir/builder/stmts.rs
Normal file
39
src/mir/builder/stmts.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use super::{MirInstruction, EffectMask, Effect, ConstValue, ValueId};
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
|
||||||
|
impl super::MirBuilder {
|
||||||
|
pub(super) fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
|
self.build_print_statement_legacy(expression)
|
||||||
|
}
|
||||||
|
pub(super) fn build_block(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
|
self.build_block_legacy(statements)
|
||||||
|
}
|
||||||
|
pub(super) fn build_if_statement(&mut self, condition: ASTNode, then_branch: ASTNode, else_branch: Option<ASTNode>) -> Result<ValueId, String> {
|
||||||
|
self.build_if_statement_legacy(condition, then_branch, else_branch)
|
||||||
|
}
|
||||||
|
pub(super) fn build_loop_statement(&mut self, condition: ASTNode, body: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
|
self.build_loop_statement_legacy(condition, body)
|
||||||
|
}
|
||||||
|
pub(super) fn build_try_catch_statement(&mut self, try_body: Vec<ASTNode>, catch_clauses: Vec<crate::ast::CatchClause>, finally_body: Option<Vec<ASTNode>>) -> Result<ValueId, String> {
|
||||||
|
self.build_try_catch_statement_legacy(try_body, catch_clauses, finally_body)
|
||||||
|
}
|
||||||
|
pub(super) fn build_throw_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
|
self.build_throw_statement_legacy(expression)
|
||||||
|
}
|
||||||
|
pub(super) fn build_local_statement(&mut self, variables: Vec<String>, initial_values: Vec<Option<Box<ASTNode>>>) -> Result<ValueId, String> {
|
||||||
|
self.build_local_statement_legacy(variables, initial_values)
|
||||||
|
}
|
||||||
|
pub(super) fn build_return_statement(&mut self, value: Option<Box<ASTNode>>) -> Result<ValueId, String> {
|
||||||
|
self.build_return_statement_legacy(value)
|
||||||
|
}
|
||||||
|
pub(super) fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
|
self.build_nowait_statement_legacy(variable, expression)
|
||||||
|
}
|
||||||
|
pub(super) fn build_await_expression(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
|
self.build_await_expression_legacy(expression)
|
||||||
|
}
|
||||||
|
pub(super) fn build_me_expression(&mut self) -> Result<ValueId, String> {
|
||||||
|
self.build_me_expression_legacy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
136
src/runner/demos.rs
Normal file
136
src/runner/demos.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
//! Runner demo helpers (moved out of mod.rs to reduce file size)
|
||||||
|
use nyash_rust::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox, AddBox, NyashBox, BoxCore};
|
||||||
|
use nyash_rust::tokenizer::NyashTokenizer;
|
||||||
|
use nyash_rust::ast::ASTNode;
|
||||||
|
use nyash_rust::parser::NyashParser;
|
||||||
|
use nyash_rust::interpreter::NyashInterpreter;
|
||||||
|
|
||||||
|
pub(super) fn demo_basic_boxes() {
|
||||||
|
println!("\n📦 1. Basic Box Creation:");
|
||||||
|
let string_box = StringBox::new("Hello, Nyash!".to_string());
|
||||||
|
let int_box = IntegerBox::new(42);
|
||||||
|
let bool_box = BoolBox::new(true);
|
||||||
|
let void_box = VoidBox::new();
|
||||||
|
println!(" StringBox: {}", string_box.to_string_box().value);
|
||||||
|
println!(" IntegerBox: {}", int_box.to_string_box().value);
|
||||||
|
println!(" BoolBox: {}", bool_box.to_string_box().value);
|
||||||
|
println!(" VoidBox: {}", void_box.to_string_box().value);
|
||||||
|
println!(
|
||||||
|
" Box IDs: String={}, Integer={}, Bool={}, Void={}",
|
||||||
|
string_box.box_id(),
|
||||||
|
int_box.box_id(),
|
||||||
|
bool_box.box_id(),
|
||||||
|
void_box.box_id()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn demo_box_operations() {
|
||||||
|
println!("\n🔄 2. Box Operations:");
|
||||||
|
let left = IntegerBox::new(10);
|
||||||
|
let right = IntegerBox::new(32);
|
||||||
|
let add_box = AddBox::new(Box::new(left), Box::new(right));
|
||||||
|
println!(" 10 + 32 = {}", add_box.to_string_box().value);
|
||||||
|
let str1 = StringBox::new("Hello, ".to_string());
|
||||||
|
let str2 = StringBox::new("World!".to_string());
|
||||||
|
let concat_box = AddBox::new(Box::new(str1), Box::new(str2));
|
||||||
|
println!(" \"Hello, \" + \"World!\" = {}", concat_box.to_string_box().value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn demo_box_collections() {
|
||||||
|
println!("\n📚 3. Box Collections:");
|
||||||
|
println!(" Box collections functionality placeholder");
|
||||||
|
println!(" (ArrayBox and other collection types will be demonstrated here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn demo_environment_system() {
|
||||||
|
println!("\n🌍 4. Environment & Scope Management:");
|
||||||
|
println!(" Environment demo placeholder - full testing done in interpreter");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn demo_tokenizer_system() {
|
||||||
|
println!("\n🔤 5. Tokenizer System:");
|
||||||
|
let test_code = "x = 42 + y";
|
||||||
|
println!(" Input: {}", test_code);
|
||||||
|
let mut tokenizer = NyashTokenizer::new(test_code);
|
||||||
|
match tokenizer.tokenize() {
|
||||||
|
Ok(tokens) => {
|
||||||
|
println!(" Tokenized {} tokens successfully", tokens.len());
|
||||||
|
},
|
||||||
|
Err(e) => println!(" Tokenization error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn demo_parser_system() {
|
||||||
|
println!("\n🌳 6. Parser & AST System:");
|
||||||
|
println!(" 📝 Simple Box Declaration Test:");
|
||||||
|
let simple_code = r#"
|
||||||
|
box TestBox {
|
||||||
|
value
|
||||||
|
|
||||||
|
getValue() {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
match NyashParser::parse_from_string(simple_code) {
|
||||||
|
Ok(ast) => {
|
||||||
|
println!(" Input: {}", simple_code.trim());
|
||||||
|
println!(" AST: {}", ast);
|
||||||
|
if let ASTNode::Program { statements, .. } = &ast {
|
||||||
|
println!(" Program has {} statements", statements.len());
|
||||||
|
for (i, stmt) in statements.iter().enumerate() {
|
||||||
|
println!(" [{}] {}", i, stmt.info());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => println!(" Parser error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn demo_interpreter_system() {
|
||||||
|
println!("\n🎭 7. Interpreter System:");
|
||||||
|
// Simple execution test
|
||||||
|
let simple_code = r#"
|
||||||
|
local x
|
||||||
|
x = 42
|
||||||
|
return x
|
||||||
|
"#;
|
||||||
|
println!(" 📝 Simple Variable Test:");
|
||||||
|
println!(" Code: {}", simple_code.trim());
|
||||||
|
match NyashParser::parse_from_string(simple_code) {
|
||||||
|
Ok(ast) => {
|
||||||
|
let mut interpreter = NyashInterpreter::new();
|
||||||
|
match interpreter.execute(ast) {
|
||||||
|
Ok(result) => {
|
||||||
|
println!(" ✅ Result: {}", result.to_string_box().value);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!(" ❌ Execution error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => println!(" ❌ Parse error: {}", e),
|
||||||
|
}
|
||||||
|
// Expression evaluation test
|
||||||
|
let expr_code = r#"
|
||||||
|
local result
|
||||||
|
result = 10 + 32
|
||||||
|
return result
|
||||||
|
"#;
|
||||||
|
println!("\n ⚡ Expression Evaluation Test:");
|
||||||
|
println!(" Code: {}", expr_code.trim());
|
||||||
|
match NyashParser::parse_from_string(expr_code) {
|
||||||
|
Ok(ast) => {
|
||||||
|
let mut interpreter = NyashInterpreter::new();
|
||||||
|
match interpreter.execute(ast) {
|
||||||
|
Ok(result) => {
|
||||||
|
println!(" ✅ Result: {}", result.to_string_box().value);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!(" ❌ Execution error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => println!(" ❌ Parse error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,6 +28,7 @@ use nyash_rust::backend::{wasm::WasmBackend, aot::AotBackend};
|
|||||||
use nyash_rust::backend::{llvm_compile_and_execute};
|
use nyash_rust::backend::{llvm_compile_and_execute};
|
||||||
use std::{fs, process};
|
use std::{fs, process};
|
||||||
mod modes;
|
mod modes;
|
||||||
|
mod demos;
|
||||||
|
|
||||||
// v2 plugin system imports
|
// v2 plugin system imports
|
||||||
use nyash_rust::runtime;
|
use nyash_rust::runtime;
|
||||||
@ -230,28 +231,13 @@ impl NyashRunner {
|
|||||||
fn execute_demo_mode(&self) {
|
fn execute_demo_mode(&self) {
|
||||||
println!("🦀 Nyash Rust Implementation - Everything is Box! 🦀");
|
println!("🦀 Nyash Rust Implementation - Everything is Box! 🦀");
|
||||||
println!("====================================================");
|
println!("====================================================");
|
||||||
|
demos::demo_basic_boxes();
|
||||||
// Demonstrate basic Box creation and operations
|
demos::demo_box_operations();
|
||||||
demo_basic_boxes();
|
demos::demo_box_collections();
|
||||||
|
demos::demo_environment_system();
|
||||||
// Demonstrate Box operations
|
demos::demo_tokenizer_system();
|
||||||
demo_box_operations();
|
demos::demo_parser_system();
|
||||||
|
demos::demo_interpreter_system();
|
||||||
// Demonstrate Box collections
|
|
||||||
demo_box_collections();
|
|
||||||
|
|
||||||
// Demonstrate Environment & Scope management
|
|
||||||
demo_environment_system();
|
|
||||||
|
|
||||||
// Demonstrate Tokenizer system
|
|
||||||
demo_tokenizer_system();
|
|
||||||
|
|
||||||
// Demonstrate Parser system
|
|
||||||
demo_parser_system();
|
|
||||||
|
|
||||||
// Demonstrate Interpreter system
|
|
||||||
demo_interpreter_system();
|
|
||||||
|
|
||||||
println!("\n🎉 All Box operations completed successfully!");
|
println!("\n🎉 All Box operations completed successfully!");
|
||||||
println!("Memory safety guaranteed by Rust's borrow checker! 🛡️");
|
println!("Memory safety guaranteed by Rust's borrow checker! 🛡️");
|
||||||
}
|
}
|
||||||
@ -886,277 +872,14 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Demo functions (moved from main.rs)
|
// Demo functions (moved from main.rs)
|
||||||
fn demo_basic_boxes() {
|
// moved to demos.rs
|
||||||
println!("\n📦 1. Basic Box Creation:");
|
|
||||||
|
|
||||||
// Create different types of boxes
|
|
||||||
let string_box = StringBox::new("Hello, Nyash!".to_string());
|
|
||||||
let int_box = IntegerBox::new(42);
|
|
||||||
let bool_box = BoolBox::new(true);
|
|
||||||
let void_box = VoidBox::new();
|
|
||||||
|
|
||||||
println!(" StringBox: {}", string_box.to_string_box().value);
|
|
||||||
println!(" IntegerBox: {}", int_box.to_string_box().value);
|
|
||||||
println!(" BoolBox: {}", bool_box.to_string_box().value);
|
|
||||||
println!(" VoidBox: {}", void_box.to_string_box().value);
|
|
||||||
|
|
||||||
// Show unique IDs
|
|
||||||
println!(" Box IDs: String={}, Integer={}, Bool={}, Void={}",
|
|
||||||
string_box.box_id(), int_box.box_id(), bool_box.box_id(), void_box.box_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn demo_box_operations() {
|
// moved to demos.rs
|
||||||
println!("\n🔄 2. Box Operations:");
|
|
||||||
|
|
||||||
// Addition between boxes
|
|
||||||
let left = IntegerBox::new(10);
|
|
||||||
let right = IntegerBox::new(32);
|
|
||||||
let add_box = AddBox::new(Box::new(left), Box::new(right));
|
|
||||||
|
|
||||||
println!(" 10 + 32 = {}", add_box.to_string_box().value);
|
|
||||||
|
|
||||||
// String concatenation
|
|
||||||
let str1 = StringBox::new("Hello, ".to_string());
|
|
||||||
let str2 = StringBox::new("World!".to_string());
|
|
||||||
let concat_box = AddBox::new(Box::new(str1), Box::new(str2));
|
|
||||||
|
|
||||||
println!(" \"Hello, \" + \"World!\" = {}", concat_box.to_string_box().value);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn demo_box_collections() {
|
// moved to demos.rs
|
||||||
println!("\n📚 3. Box Collections:");
|
|
||||||
|
|
||||||
// This would be expanded when ArrayBox is implemented
|
|
||||||
println!(" Box collections functionality placeholder");
|
|
||||||
println!(" (ArrayBox and other collection types will be demonstrated here)");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn demo_environment_system() {
|
// moved to demos.rs
|
||||||
println!("\n🌍 4. Environment & Scope Management:");
|
|
||||||
println!(" Environment demo placeholder - full testing done in interpreter");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn demo_tokenizer_system() {
|
// moved to demos.rs
|
||||||
println!("\n🔤 5. Tokenizer System:");
|
|
||||||
|
|
||||||
// Test code to tokenize
|
|
||||||
let test_code = "x = 42 + y";
|
|
||||||
println!(" Input: {}", test_code);
|
|
||||||
|
|
||||||
// Tokenize the code
|
|
||||||
let mut tokenizer = NyashTokenizer::new(test_code);
|
|
||||||
|
|
||||||
match tokenizer.tokenize() {
|
|
||||||
Ok(tokens) => {
|
|
||||||
println!(" Tokenized {} tokens successfully", tokens.len());
|
|
||||||
},
|
|
||||||
Err(e) => println!(" Tokenization error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn demo_parser_system() {
|
// moved to demos.rs
|
||||||
println!("\n🌳 6. Parser & AST System:");
|
|
||||||
|
|
||||||
// Test simple box declaration
|
|
||||||
println!(" 📝 Simple Box Declaration Test:");
|
|
||||||
let simple_code = r#"
|
|
||||||
box TestBox {
|
|
||||||
value
|
|
||||||
|
|
||||||
getValue() {
|
|
||||||
return this.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
match NyashParser::parse_from_string(simple_code) {
|
|
||||||
Ok(ast) => {
|
|
||||||
println!(" Input: {}", simple_code.trim());
|
|
||||||
println!(" AST: {}", ast);
|
|
||||||
|
|
||||||
if let ASTNode::Program { statements, .. } = &ast {
|
|
||||||
println!(" Program has {} statements", statements.len());
|
|
||||||
for (i, stmt) in statements.iter().enumerate() {
|
|
||||||
println!(" [{}] {}", i, stmt.info());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => println!(" Error: {}", e),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test assignment and method call
|
|
||||||
println!("\n 🚀 Assignment & Method Call Test:");
|
|
||||||
let assignment_code = r#"
|
|
||||||
obj = new TestBox()
|
|
||||||
obj.value = "test123"
|
|
||||||
print("Direct field: " + obj.value)
|
|
||||||
print("Method call: " + obj.getValue())
|
|
||||||
"#;
|
|
||||||
|
|
||||||
match NyashParser::parse_from_string(assignment_code) {
|
|
||||||
Ok(ast) => {
|
|
||||||
println!(" Successfully parsed assignment & method call code");
|
|
||||||
|
|
||||||
if let ASTNode::Program { statements, .. } = &ast {
|
|
||||||
println!(" Parsed {} statements:", statements.len());
|
|
||||||
for (i, stmt) in statements.iter().enumerate() {
|
|
||||||
println!(" [{}] {} ({})", i, stmt.info(), stmt.node_type());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => println!(" Error: {}", e),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test expression parsing
|
|
||||||
println!("\n ⚡ Expression Parsing Test:");
|
|
||||||
let expr_code = r#"
|
|
||||||
result = x + y * z
|
|
||||||
condition = a == b && c < d
|
|
||||||
"#;
|
|
||||||
|
|
||||||
match NyashParser::parse_from_string(expr_code) {
|
|
||||||
Ok(ast) => {
|
|
||||||
println!(" Successfully parsed complex expressions");
|
|
||||||
|
|
||||||
if let ASTNode::Program { statements, .. } = &ast {
|
|
||||||
for (i, stmt) in statements.iter().enumerate() {
|
|
||||||
if let ASTNode::Assignment { target, value, .. } = stmt {
|
|
||||||
println!(" Assignment [{}]: {} = {}", i, target.info(), value.info());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => println!(" Error: {}", e),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test control structures
|
|
||||||
println!("\n 🔄 Control Structure Test:");
|
|
||||||
let control_code = r#"
|
|
||||||
if condition {
|
|
||||||
print("True branch")
|
|
||||||
} else {
|
|
||||||
print("False branch")
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
print("Loop body")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
match NyashParser::parse_from_string(control_code) {
|
|
||||||
Ok(ast) => {
|
|
||||||
println!(" Successfully parsed control structures");
|
|
||||||
|
|
||||||
if let ASTNode::Program { statements, .. } = &ast {
|
|
||||||
for (i, stmt) in statements.iter().enumerate() {
|
|
||||||
println!(" [{}] {} ({})", i, stmt.info(), stmt.node_type());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => println!(" Error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn demo_interpreter_system() {
|
|
||||||
println!("\n🎭 7. Interpreter System:");
|
|
||||||
|
|
||||||
// Simple execution test
|
|
||||||
let simple_code = r#"
|
|
||||||
local x
|
|
||||||
x = 42
|
|
||||||
return x
|
|
||||||
"#;
|
|
||||||
|
|
||||||
println!(" 📝 Simple Variable Test:");
|
|
||||||
println!(" Code: {}", simple_code.trim());
|
|
||||||
|
|
||||||
match NyashParser::parse_from_string(simple_code) {
|
|
||||||
Ok(ast) => {
|
|
||||||
let mut interpreter = NyashInterpreter::new();
|
|
||||||
match interpreter.execute(ast) {
|
|
||||||
Ok(result) => {
|
|
||||||
println!(" ✅ Result: {}", result.to_string_box().value);
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
println!(" ❌ Execution error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => println!(" ❌ Parse error: {}", e),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expression evaluation test
|
|
||||||
let expr_code = r#"
|
|
||||||
local result
|
|
||||||
result = 10 + 32
|
|
||||||
return result
|
|
||||||
"#;
|
|
||||||
|
|
||||||
println!("\n ⚡ Expression Evaluation Test:");
|
|
||||||
println!(" Code: {}", expr_code.trim());
|
|
||||||
|
|
||||||
match NyashParser::parse_from_string(expr_code) {
|
|
||||||
Ok(ast) => {
|
|
||||||
let mut interpreter = NyashInterpreter::new();
|
|
||||||
match interpreter.execute(ast) {
|
|
||||||
Ok(result) => {
|
|
||||||
println!(" ✅ Result: {}", result.to_string_box().value);
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
println!(" ❌ Execution error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => println!(" ❌ Parse error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_runner_creation() {
|
|
||||||
let config = CliConfig {
|
|
||||||
file: None,
|
|
||||||
debug_fuel: Some(100000),
|
|
||||||
dump_ast: false,
|
|
||||||
dump_mir: false,
|
|
||||||
verify_mir: false,
|
|
||||||
mir_verbose: false,
|
|
||||||
mir_verbose_effects: false,
|
|
||||||
no_optimize: false,
|
|
||||||
backend: "interpreter".to_string(),
|
|
||||||
compile_wasm: false,
|
|
||||||
compile_native: false,
|
|
||||||
output_file: None,
|
|
||||||
benchmark: false,
|
|
||||||
iterations: 10,
|
|
||||||
vm_stats: false,
|
|
||||||
vm_stats_json: false,
|
|
||||||
// JIT defaults for test
|
|
||||||
jit_exec: false,
|
|
||||||
jit_stats: false,
|
|
||||||
jit_stats_json: false,
|
|
||||||
jit_dump: false,
|
|
||||||
jit_events: false,
|
|
||||||
jit_events_compile: false,
|
|
||||||
jit_events_runtime: false,
|
|
||||||
jit_events_path: None,
|
|
||||||
jit_threshold: None,
|
|
||||||
jit_phi_min: false,
|
|
||||||
jit_hostcall: false,
|
|
||||||
jit_handle_debug: false,
|
|
||||||
jit_native_f64: false,
|
|
||||||
jit_native_bool: false,
|
|
||||||
emit_cfg: None,
|
|
||||||
jit_only: false,
|
|
||||||
jit_direct: false,
|
|
||||||
cli_verbose: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let runner = NyashRunner::new(config);
|
|
||||||
assert_eq!(runner.config.backend, "interpreter");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user