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:
Moe Charm
2025-09-03 01:37:38 +09:00
parent d52779dc10
commit 59b8cb3ace
14 changed files with 747 additions and 660 deletions

View File

@ -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:

View File

@ -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 をブロック間で使い回し分岐/合流で返値取り違え
変更点犯人切り分けと根治のための構造改革ログ 変更点犯人切り分けと根治のための構造改革ログ
- CraneliftBuilderjit-direct 経路 - CraneliftBuilderjit-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`ブロック入場ログ
- LowerCorereturn 値の堅牢化 - LowerCorereturn 値の堅牢化
- 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) 文字列連結の広範囲対応

View File

@ -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
到達M1M3 要約)
- 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-inCI 環境用意後に拡張)
- `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_*`

View File

@ -17,3 +17,17 @@ Backends Strategy:
- JIT 既定は Craneliftfeature: `cranelift-jit`。AOT は必要に応じ `cranelift-object` を併用。 - JIT 既定は Craneliftfeature: `cranelift-jit`。AOT は必要に応じ `cranelift-object` を併用。
This folder contains the living plan (PLAN.md) and the rolling snapshot of the current task focus (CURRENT_TASK.md). Semantics 層の導入により、Nyash スクリプトVMJITexeの動作を一致させる。 This folder contains the living plan (PLAN.md) and the rolling snapshot of the current task focus (CURRENT_TASK.md). Semantics 層の導入により、Nyash スクリプトVMJITexeの動作を一致させる。
## 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` は不要になりました(存在しても無視)。

View File

@ -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("+"));
}
}

View File

@ -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;

View File

@ -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()))
} }

View File

@ -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+returnbranchless。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(())

View File

@ -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);
}
}
}

View File

@ -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 &params {
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 &params {
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>,

View 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 &params { 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 &params { 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 &params { 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 &params { 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
View 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
View 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),
}
}

View File

@ -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");
}
}