diff --git a/docs/説明書/NYASH_DESIGN_OVERVIEW.md b/docs/説明書/NYASH_DESIGN_OVERVIEW.md new file mode 100644 index 00000000..b70e1bfc --- /dev/null +++ b/docs/説明書/NYASH_DESIGN_OVERVIEW.md @@ -0,0 +1,108 @@ +# Nyash 設計図(アーキテクチャ概要) + +最終更新: 2025-08-21(Phase 9.78b〜3 反映) + +本書はNyashの実装設計を、バックエンド共通で理解できる単一ドキュメントとしてまとめたもの。言語コア、MIR、インタープリター/VM統合、ランタイム/プラグイン、ビルドと配布の観点を俯瞰する。 + +## レイヤー構成 + +- 構文/AST: `tokenizer`, `parser`, `ast` +- モデル層: `core::model`(BoxDeclaration等の純粋データ) +- ランタイム層: `runtime`(UnifiedBoxRegistry, PluginLoader, NyashRuntime) +- 実行戦略層: `interpreter`(AST実行)/ `mir`+`backend::vm`(MIR実行)/ 将来 `wasm`/`llvm` +- 付帯基盤: `box_factory`, `instance_v2`, `scope_tracker`, `boxes/*`, `stdlib` + +## コア概念 + +- Everything is Box: すべての値はBox(ビルトイン、ユーザー定義、プラグイン) +- 統一コンストラクタ: `birth(args)`(packはビルトイン継承内部用に透過化) +- 明示デリゲーション: `box Child from Parent` と `from Parent.method()` +- 厳密変数宣言/スコープ安全: `local`, `outbox`、スコープ退出時の`fini`一元化 + +## モデル層(core::model) + +- `BoxDeclaration` を `interpreter` から分離し `core::model` に移動 + - name, fields, methods, constructors(birth/N), extends, implements, type_parameters + - 実行戦略非依存の純粋データ + +## ランタイム層(runtime) + +- `NyashRuntime` + - `box_registry: UnifiedBoxRegistry`(ビルトイン/ユーザー定義/プラグインを順序付き検索) + - `box_declarations: RwLock>` + - BuilderでDI(`with_factory`)可能。Interpreter/VMから共有・注入できる +- `UnifiedBoxRegistry` + - `Arc` の列で優先解決(builtin > user > plugin) + - `create_box(name, args)` の統一エントリ +- `BoxFactory` + - builtin: 全ビルトインBoxの生成 + - user_defined: `BoxDeclaration`に基づき`InstanceBox`生成(birthは実行戦略側で) + - plugin: BID-FFI準拠のプラグインBox(将来のExternCall/MIR接続) + +## 実行戦略(Interpreter / VM) + +- Interpreter(AST実行) + - `SharedState` は段階的に分解し、宣言等を `NyashRuntime` に寄せ替え + - 生成は統一レジストリへ委譲、コンストラクタ実行は`birth/N`のASTを実行 + +- VM (MIR実行) + - `VM::with_runtime(runtime)` でDI、`NewBox`は`runtime.box_registry.create_box`へ + - `ScopeTracker`でスコープ退出時に`fini`(InstanceBox/PluginBox) + - birth/メソッドのMIR関数化(Phase 2/3): + - Builderが `new` を `NewBox` + `BoxCall("birth")` に展開 + - Box宣言の `birth/N` と通常メソッド(`method/N`)を `"{Box}.{name}/{N}"` のMIR関数へ関数化 + - VMの`BoxCall`は `InstanceBox` なら該当MIR関数へディスパッチ(me + 引数) + +## MIR(中間表現) + +- 目的: バックエンド共通の最適化/実行基盤(VM/LLVM/WASM/JIT) +- Builder + - AST→MIR lowering。`ASTNode::New`→`NewBox`(+ `BoxCall("birth")`) + - `ASTNode::BoxDeclaration` の `constructors` / `methods` をMIR関数化 + - if/loop/try-catch/phi等の基本構造を提供 +- VM + - Stackベースの簡易実装→順次強化中 + - `call_function_by_name` による関数呼び出しフレームの最小実装 + +## インスタンス表現(InstanceBox) + +- 統一フィールド`fields_ng: HashMap` +- メソッドASTを保持(ユーザー定義時) +- `fini()`による簡易解放(将来、リソースBoxは明示やRAII連携) + +## ライフサイクル統一(fini) + +- Interpreter: スコープ復帰時に`InstanceBox.fini()`等を呼ぶ +- VM: `ScopeTracker`で関数入退出時に登録Boxを`fini` + +## プラグイン(BID-FFI) + +- v2ローダ(`runtime::plugin_loader_v2`)とテスター完備 +- 目標: MIRの`ExternCall`→ローダに接続し、VM/LLVM/WASMで共通パス + +## Runner/ビルド + +- VMモード: + 1) ASTパース + 2) ランタイムにBox宣言収集 + UserDefinedBoxFactory登録 + 3) MIRコンパイル + 4) VMを`with_runtime`で起動し実行 + +## 進行中フェーズと方針 + +- Phase 9.78b: Interpreter/VMのモデル・ランタイム共有(完了) +- Phase 2/3(実質): birth/メソッドのMIR関数化とVMディスパッチ(実装済・基本動作) +- 次: + - BoxCall→Callへの段階的置換(型決定済みのとき) + - ExternCallの実装(VM→プラグイン) + - WASM/LLVMバックエンドへMIR関数の共有 + +## 参考ファイル + +- `src/core/model.rs`(BoxDeclaration) +- `src/runtime/nyash_runtime.rs`(NyashRuntime) +- `src/box_factory/*`(builtin/user_defined/plugin) +- `src/mir/*`(builder/instruction/function/etc.) +- `src/backend/vm.rs`(VM実行) +- `src/interpreter/*`(AST実行) + diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 67b7f1af..fefa2f2f 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -8,6 +8,10 @@ use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, C use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; use std::collections::HashMap; use std::sync::Arc; +use crate::runtime::NyashRuntime; +use crate::scope_tracker::ScopeTracker; +// MirModule is already imported via crate::mir at top +use crate::instance_v2::InstanceBox; use super::vm_phi::LoopExecutor; // Phase 9.78a: Import necessary components for unified Box handling @@ -171,6 +175,12 @@ pub struct VM { object_fields: HashMap>, /// Loop executor for handling phi nodes and loop-specific logic loop_executor: LoopExecutor, + /// Shared runtime for box creation and declarations + runtime: NyashRuntime, + /// Scope tracker for calling fini on scope exit + scope_tracker: ScopeTracker, + /// Active MIR module during execution (for function calls) + module: Option, // Phase 9.78a: Add unified Box handling components // TODO: Re-enable when interpreter refactoring is complete // /// Box registry for creating all Box types @@ -196,6 +206,9 @@ impl VM { last_result: None, object_fields: HashMap::new(), loop_executor: LoopExecutor::new(), + runtime: NyashRuntime::new(), + scope_tracker: ScopeTracker::new(), + module: None, // TODO: Re-enable when interpreter refactoring is complete // box_registry: Arc::new(UnifiedBoxRegistry::new()), // #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] @@ -204,6 +217,23 @@ impl VM { // box_declarations: Arc::new(RwLock::new(HashMap::new())), } } + + /// Create a VM with an external runtime (dependency injection) + pub fn with_runtime(runtime: NyashRuntime) -> Self { + Self { + values: Vec::new(), + current_function: None, + current_block: None, + previous_block: None, + pc: 0, + last_result: None, + object_fields: HashMap::new(), + loop_executor: LoopExecutor::new(), + runtime, + scope_tracker: ScopeTracker::new(), + module: None, + } + } // TODO: Re-enable when interpreter refactoring is complete /* @@ -230,6 +260,8 @@ impl VM { /// Execute a MIR module pub fn execute_module(&mut self, module: &MirModule) -> Result, VMError> { + // Store module for nested calls + self.module = Some(module.clone()); // Find main function let main_function = module.get_function("main") .ok_or_else(|| VMError::InvalidInstruction("No main function found".to_string()))?; @@ -240,6 +272,43 @@ impl VM { // Convert result to NyashBox Ok(result.to_nyash_box()) } + + /// Call a MIR function by name with VMValue arguments + fn call_function_by_name(&mut self, func_name: &str, args: Vec) -> Result { + let module_ref = self.module.as_ref().ok_or_else(|| VMError::InvalidInstruction("No active module".to_string()))?; + let function_ref = module_ref.get_function(func_name) + .ok_or_else(|| VMError::InvalidInstruction(format!("Function '{}' not found", func_name)))?; + // Clone function to avoid borrowing conflicts during execution + let function = function_ref.clone(); + + // Save current frame + let saved_values = std::mem::take(&mut self.values); + let saved_current_function = self.current_function.clone(); + let saved_current_block = self.current_block; + let saved_previous_block = self.previous_block; + let saved_pc = self.pc; + let saved_last_result = self.last_result.clone(); + + // Bind parameters + for (i, param_id) in function.params.iter().enumerate() { + if let Some(arg) = args.get(i) { + self.set_value(*param_id, arg.clone()); + } + } + + // Execute the function + let result = self.execute_function(&function); + + // Restore frame + self.values = saved_values; + self.current_function = saved_current_function; + self.current_block = saved_current_block; + self.previous_block = saved_previous_block; + self.pc = saved_pc; + self.last_result = saved_last_result; + + result + } /// Execute a single function fn execute_function(&mut self, function: &MirFunction) -> Result { @@ -248,8 +317,8 @@ impl VM { // Initialize loop executor for this function self.loop_executor.initialize(); - // Phase 9.78a: Enter a new scope for this function - // self.scope_tracker.push_scope(); + // Enter a new scope for this function + self.scope_tracker.push_scope(); // Start at entry block let mut current_block = function.entry_block; @@ -284,8 +353,8 @@ impl VM { // Handle control flow if let Some(return_value) = should_return { - // Phase 9.78a: Exit scope before returning - // self.scope_tracker.pop_scope(); + // Exit scope before returning + self.scope_tracker.pop_scope(); return Ok(return_value); } else if let Some(target) = next_block { // Update previous block before jumping @@ -296,8 +365,8 @@ impl VM { } else { // Block ended without terminator - this shouldn't happen in well-formed MIR // but let's handle it gracefully by returning void - // Phase 9.78a: Exit scope before returning - // self.scope_tracker.pop_scope(); + // Exit scope before returning + self.scope_tracker.pop_scope(); return Ok(VMValue::Void); } } @@ -429,6 +498,31 @@ impl VM { _ => box_vm_value.to_nyash_box(), }; + // Fast path: birth() for user-defined boxes is lowered to a MIR function + if method == "birth" { + if let Some(instance) = box_nyash.as_any().downcast_ref::() { + let class_name = instance.class_name.clone(); + let func_name = format!("{}.birth/{}", class_name, args.len()); + + // Prepare VMValue args: me + evaluated arguments + let mut vm_args: Vec = Vec::new(); + vm_args.push(VMValue::from_nyash_box(box_nyash.clone_box())); + for arg_id in args { + let arg_vm_value = self.get_value(*arg_id)?; + vm_args.push(arg_vm_value); + } + + // Call the lowered function (ignore return) + let _ = self.call_function_by_name(&func_name, vm_args)?; + + // birth returns void; only set dst if specified (rare for birth) + if let Some(dst_id) = dst { + self.set_value(*dst_id, VMValue::Void); + } + return Ok(ControlFlow::Continue); + } + } + // Evaluate arguments let mut arg_values = Vec::new(); for arg_id in args { @@ -437,6 +531,24 @@ impl VM { } // Call the method - unified dispatch for all Box types + // If user-defined InstanceBox: dispatch to lowered MIR function `{Class}.{method}/{argc}` + if let Some(instance) = box_nyash.as_any().downcast_ref::() { + let class_name = instance.class_name.clone(); + let func_name = format!("{}.{}{}", class_name, method, format!("/{}", args.len())); + // Prepare VMValue args: me + evaluated arguments (use original VM args for value-level fidelity) + let mut vm_args: Vec = Vec::new(); + vm_args.push(VMValue::from_nyash_box(box_nyash.clone_box())); + for arg_id in args { + let arg_vm_value = self.get_value(*arg_id)?; + vm_args.push(arg_vm_value); + } + let call_result = self.call_function_by_name(&func_name, vm_args)?; + if let Some(dst_id) = dst { + self.set_value(*dst_id, call_result); + } + return Ok(ControlFlow::Continue); + } + let result = self.call_unified_method(box_nyash, method, arg_values)?; // Store result if destination is specified @@ -448,58 +560,29 @@ impl VM { }, MirInstruction::NewBox { dst, box_type, args } => { - // Phase 9.78a: Simplified Box creation (temporary until interpreter refactoring) - - // Evaluate arguments - let mut arg_values = Vec::new(); + // Evaluate arguments into NyashBox for unified factory + let mut nyash_args: Vec> = Vec::new(); for arg_id in args { let arg_value = self.get_value(*arg_id)?; - arg_values.push(arg_value); + nyash_args.push(arg_value.to_nyash_box()); } - - // Basic Box creation for common types - let result = match box_type.as_str() { - "StringBox" => { - // Get first argument as string, or empty string - let value = if let Some(arg) = arg_values.first() { - arg.to_string() - } else { - String::new() - }; - VMValue::String(value) - }, - "IntegerBox" => { - // Get first argument as integer, or 0 - let value = if let Some(arg) = arg_values.first() { - arg.as_integer().unwrap_or(0) - } else { - 0 - }; - VMValue::Integer(value) - }, - "BoolBox" => { - // Get first argument as bool, or false - let value = if let Some(arg) = arg_values.first() { - arg.as_bool().unwrap_or(false) - } else { - false - }; - VMValue::Bool(value) - }, - "ArrayBox" => { - // Create empty ArrayBox - let array_box = Box::new(crate::boxes::array::ArrayBox::new()); - VMValue::from_nyash_box(array_box) - }, - _ => { - // For unknown types, create a placeholder - // TODO: Implement proper user-defined Box creation after refactoring - VMValue::String(format!("{}[placeholder]", box_type)) - } + // Create via unified registry from runtime + let registry = self.runtime.box_registry.clone(); + let created = { + let guard = registry.lock().map_err(|_| VMError::InvalidInstruction("Registry lock poisoned".into()))?; + guard.create_box(box_type, &nyash_args) }; - - self.set_value(*dst, result); - Ok(ControlFlow::Continue) + match created { + Ok(b) => { + // Register for scope-based finalization (clone for registration) + let reg_arc = std::sync::Arc::from(b.clone_box()); + self.scope_tracker.register_box(reg_arc); + // Store value in VM + self.set_value(*dst, VMValue::from_nyash_box(b)); + Ok(ControlFlow::Continue) + } + Err(e) => Err(VMError::InvalidInstruction(format!("NewBox failed for {}: {}", box_type, e))) + } }, MirInstruction::TypeCheck { dst, value: _, expected_type: _ } => { diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 00000000..eb58a64c --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1,4 @@ +//! Core model types shared across interpreter and VM + +pub mod model; + diff --git a/src/core/model.rs b/src/core/model.rs new file mode 100644 index 00000000..66c81bde --- /dev/null +++ b/src/core/model.rs @@ -0,0 +1,29 @@ +//! Core model definitions for Nyash +//! +//! This module contains pure data models that are shared between +//! the interpreter and the VM. Keep these types free of execution +//! strategy details so they can be reused across backends. + +use std::collections::HashMap; + +use crate::ast::ASTNode; + +/// Declaration of a user-defined Box type (class) in Nyash +/// +/// Pure model data used by both the interpreter and VM layers. +#[derive(Debug, Clone)] +pub struct BoxDeclaration { + pub name: String, + pub fields: Vec, + pub methods: HashMap, + pub constructors: HashMap, + pub init_fields: Vec, + pub weak_fields: Vec, + pub is_interface: bool, + /// Supports multi-delegation: list of parent types + pub extends: Vec, + pub implements: Vec, + /// Generic type parameters + pub type_parameters: Vec, +} + diff --git a/src/interpreter/core.rs b/src/interpreter/core.rs index 6945ee82..7768b08b 100644 --- a/src/interpreter/core.rs +++ b/src/interpreter/core.rs @@ -10,6 +10,7 @@ use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox, Shared use crate::instance_v2::InstanceBox; use crate::parser::ParseError; use super::BuiltinStdlib; +use crate::runtime::{NyashRuntime, NyashRuntimeBuilder}; use std::sync::{Arc, Mutex, RwLock}; use std::collections::{HashMap, HashSet}; use thiserror::Error; @@ -222,6 +223,9 @@ pub struct NyashInterpreter { /// 📚 組み込み標準ライブラリ pub(super) stdlib: Option, + + /// 共有ランタイム(Boxレジストリ等) + pub(super) runtime: NyashRuntime, } impl NyashInterpreter { @@ -229,12 +233,15 @@ impl NyashInterpreter { pub fn new() -> Self { let shared = SharedState::new(); - // Register user-defined box factory with unified registry + // ランタイムを構築し、ユーザー定義Boxファクトリを注入(グローバル登録を避ける) use crate::box_factory::user_defined::UserDefinedBoxFactory; - use crate::runtime::register_user_defined_factory; - - let factory = UserDefinedBoxFactory::new(shared.clone()); - register_user_defined_factory(Arc::new(factory)); + let udf = Arc::new(UserDefinedBoxFactory::new(shared.clone())); + let runtime = NyashRuntimeBuilder::new().with_factory(udf).build(); + + // Step 5: SharedState分解の第一歩として、 + // box_declarationsの保管先をRuntimeに寄せる + let mut shared = shared; // 可変化 + shared.box_declarations = runtime.box_declarations.clone(); Self { shared, @@ -245,11 +252,21 @@ impl NyashInterpreter { evaluation_stack: Vec::new(), invalidated_ids: Arc::new(Mutex::new(HashSet::new())), stdlib: None, // 遅延初期化 + runtime, } } /// 共有状態から新しいインタープリターを作成(非同期実行用) pub fn with_shared(shared: SharedState) -> Self { + // 共有状態に紐づいたランタイムを構築 + use crate::box_factory::user_defined::UserDefinedBoxFactory; + let udf = Arc::new(UserDefinedBoxFactory::new(shared.clone())); + let runtime = NyashRuntimeBuilder::new().with_factory(udf).build(); + + // Step 5: Runtimeのbox_declarationsに寄せ替え + let mut shared = shared; // 可変化 + shared.box_declarations = runtime.box_declarations.clone(); + Self { shared, local_vars: HashMap::new(), @@ -259,6 +276,7 @@ impl NyashInterpreter { evaluation_stack: Vec::new(), invalidated_ids: Arc::new(Mutex::new(HashSet::new())), stdlib: None, // 遅延初期化 + runtime, } } diff --git a/src/interpreter/expressions/calls.rs b/src/interpreter/expressions/calls.rs index ccfed8b4..b27e8074 100644 --- a/src/interpreter/expressions/calls.rs +++ b/src/interpreter/expressions/calls.rs @@ -643,8 +643,7 @@ impl NyashInterpreter { for parent_name in &parent_names { if crate::box_trait::is_builtin_box(parent_name) { // ビルトインBoxメソッドを実行 - match parent_name.as_str() { - "StringBox" => { + if parent_name == "StringBox" { // ユーザー定義BoxがStringBoxを継承している場合 // __builtin_contentフィールドからStringBoxを取得 if let Some(builtin_value) = instance.get_field_ng("__builtin_content") { @@ -659,8 +658,7 @@ impl NyashInterpreter { // フィールドが見つからない場合は空のStringBoxを使用(互換性のため) let string_box = StringBox::new(""); return self.execute_string_method(&string_box, method, arguments); - }, - "IntegerBox" => { + } else if parent_name == "IntegerBox" { // __builtin_contentフィールドからIntegerBoxを取得 if let Some(builtin_value) = instance.get_field_ng("__builtin_content") { if let crate::value::NyashValue::Box(boxed) = builtin_value { @@ -673,15 +671,12 @@ impl NyashInterpreter { // フィールドが見つからない場合は0のIntegerBoxを使用 let integer_box = IntegerBox::new(0); return self.execute_integer_method(&integer_box, method, arguments); - }, - "MathBox" => { + } else if parent_name == "MathBox" { // MathBoxはステートレスなので、新しいインスタンスを作成 let math_box = MathBox::new(); return self.execute_math_method(&math_box, method, arguments); - }, - // 他のビルトインBoxも必要に応じて追加 - _ => {} } + // 他のビルトインBoxも必要に応じて追加 } } @@ -1072,4 +1067,4 @@ impl NyashInterpreter { Ok(Box::new(VoidBox::new())) } } -} \ No newline at end of file +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1d5b4a31..43eb98e5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -61,20 +61,8 @@ pub struct ConstructorContext { pub parent_class: Option, } -/// Box宣言を保持する構造体 -#[derive(Debug, Clone)] -pub struct BoxDeclaration { - pub name: String, - pub fields: Vec, - pub methods: HashMap, - pub constructors: HashMap, - pub init_fields: Vec, - pub weak_fields: Vec, // 🔗 weak修飾子が付いたフィールドのリスト - pub is_interface: bool, - pub extends: Vec, // 🚀 Multi-delegation: Changed from Option to Vec - pub implements: Vec, - pub type_parameters: Vec, // 🔥 ジェネリクス型パラメータ -} +// Re-export core model so existing interpreter modules keep working +pub use crate::core::model::BoxDeclaration; /// 🔥 Static Box定義を保持する構造体 #[derive(Debug, Clone)] @@ -112,4 +100,4 @@ pub struct FunctionDeclaration { pub use core::*; // Import and re-export stdlib for interpreter modules -pub use crate::stdlib::BuiltinStdlib; \ No newline at end of file +pub use crate::stdlib::BuiltinStdlib; diff --git a/src/interpreter/objects.rs b/src/interpreter/objects.rs index f4e5c058..fc256b46 100644 --- a/src/interpreter/objects.rs +++ b/src/interpreter/objects.rs @@ -1120,7 +1120,7 @@ impl NyashInterpreter { let parent_decl = { let box_decls = self.shared.box_declarations.read().unwrap(); box_decls.get(parent_name) - .ok_or(RuntimeError::UndefinedClass { name: parent_name.clone() })? + .ok_or(RuntimeError::UndefinedClass { name: parent_name.to_string() })? .clone() }; diff --git a/src/lib.rs b/src/lib.rs index e10cb87e..e51d0d78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ pub mod tokenizer; pub mod ast; // Using old ast.rs for now pub mod parser; // Using old parser.rs for now pub mod interpreter; +pub mod core; // Core models shared by backends pub mod instance_v2; // 🎯 Phase 9.78d: Simplified InstanceBox implementation pub mod channel_box; pub mod finalization; diff --git a/src/main.rs b/src/main.rs index 735f5b5d..7421085b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,10 +19,12 @@ pub mod ast; pub mod parser; pub mod interpreter; pub mod instance_v2; // 🎯 Phase 9.78d: Simplified InstanceBox implementation +pub mod core; // core::model (shared models) pub mod channel_box; pub mod finalization; pub mod exception_box; pub mod method_box; +pub mod scope_tracker; // VM scope lifecycle pub mod operator_traits; pub mod box_arithmetic; // 🚀 Moved from box_trait.rs for better organization pub mod value; // 🔥 NyashValue Revolutionary System diff --git a/src/mir/builder.rs b/src/mir/builder.rs index 72f3d582..b4500710 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -50,6 +50,84 @@ impl MirBuilder { pending_phis: Vec::new(), } } + + /// Lower a box method (e.g., birth) into a standalone MIR 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, + body: Vec, + ) -> 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); + } + let signature = FunctionSignature { + name: func_name, + params: param_types, + return_type: MirType::Void, + effects: EffectMask::READ.add(Effect::ReadHeap), // conservative + }; + let entry = self.block_gen.next(); + let mut 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); + + // 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 + let me_id = self.value_gen.next(); + f.params.push(me_id); + self.variable_map.insert("me".to_string(), me_id); + // user parameters + 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; + + Ok(()) + } /// Build a complete MIR module from AST pub fn build_module(&mut self, ast: ASTNode) -> Result { @@ -199,12 +277,32 @@ impl MirBuilder { self.build_local_statement(variables.clone(), initial_values.clone()) }, - ASTNode::BoxDeclaration { name, methods, is_static, fields, .. } => { + ASTNode::BoxDeclaration { name, methods, is_static, fields, constructors, .. } => { if is_static && name == "Main" { self.build_static_main_box(methods.clone()) } else { // Support user-defined boxes - handle as statement, return void self.build_box_declaration(name.clone(), methods.clone(), fields.clone())?; + + // Phase 2: Lower constructors (birth/N) into MIR functions + // Function name pattern: "{BoxName}.{constructor_key}" (e.g., "Person.birth/1") + for (ctor_key, ctor_ast) in constructors.clone() { + if let ASTNode::FunctionDeclaration { params, body, .. } = ctor_ast { + let func_name = format!("{}.{}", name, ctor_key); + self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?; + } + } + + // Phase 3: Lower instance methods into MIR functions + // Function name pattern: "{BoxName}.{method}/{N}" + for (method_name, method_ast) in methods.clone() { + if let ASTNode::FunctionDeclaration { params, body, is_static, .. } = method_ast { + if !is_static { + let func_name = format!("{}.{}{}", name, method_name, format!("/{}", params.len())); + self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?; + } + } + } // Return a void value since this is a statement let void_val = self.value_gen.next(); @@ -687,7 +785,17 @@ impl MirBuilder { self.emit_instruction(MirInstruction::NewBox { dst, box_type: class, + args: arg_values.clone(), + })?; + + // Immediately call birth(...) on the created instance to run constructor semantics. + // birth typically returns void; we don't capture the result here (dst: None) + self.emit_instruction(MirInstruction::BoxCall { + dst: None, + box_val: dst, + method: "birth".to_string(), args: arg_values, + effects: EffectMask::READ.add(Effect::ReadHeap), })?; Ok(dst) diff --git a/src/runner.rs b/src/runner.rs index 8d1eec7b..4797f03e 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -15,6 +15,12 @@ use nyash_rust::{ mir::{MirCompiler, MirPrinter, MirInstruction}, backend::VM, }; +use nyash_rust::runtime::NyashRuntime; +use nyash_rust::interpreter::SharedState; +use nyash_rust::box_factory::user_defined::UserDefinedBoxFactory; +use nyash_rust::core::model::BoxDeclaration as CoreBoxDecl; +use std::sync::{Arc, RwLock}; +use std::collections::HashMap; #[cfg(feature = "wasm-backend")] use nyash_rust::backend::{wasm::WasmBackend, aot::AotBackend}; @@ -314,6 +320,20 @@ impl NyashRunner { } }; + // Prepare runtime and collect Box declarations for VM user-defined types + let runtime = { + let rt = NyashRuntime::new(); + self.collect_box_declarations(&ast, &rt); + // Register UserDefinedBoxFactory backed by the same declarations + let mut shared = SharedState::new(); + shared.box_declarations = rt.box_declarations.clone(); + let udf = Arc::new(UserDefinedBoxFactory::new(shared)); + if let Ok(mut reg) = rt.box_registry.lock() { + reg.register(udf); + } + rt + }; + // Compile to MIR let mut mir_compiler = MirCompiler::new(); let compile_result = match mir_compiler.compile(ast) { @@ -324,8 +344,8 @@ impl NyashRunner { } }; - // Execute with VM - let mut vm = VM::new(); + // Execute with VM using prepared runtime + let mut vm = VM::with_runtime(runtime); match vm.execute_module(&compile_result.module) { Ok(result) => { println!("✅ VM execution completed successfully!"); @@ -338,6 +358,36 @@ impl NyashRunner { } } + /// Collect Box declarations from AST and register into runtime + fn collect_box_declarations(&self, ast: &ASTNode, runtime: &NyashRuntime) { + fn walk(node: &ASTNode, runtime: &NyashRuntime) { + match node { + ASTNode::Program { statements, .. } => { + for st in statements { walk(st, runtime); } + } + ASTNode::BoxDeclaration { name, fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, .. } => { + let decl = CoreBoxDecl { + name: name.clone(), + fields: fields.clone(), + methods: methods.clone(), + constructors: constructors.clone(), + init_fields: init_fields.clone(), + weak_fields: weak_fields.clone(), + is_interface: *is_interface, + extends: extends.clone(), + implements: implements.clone(), + type_parameters: type_parameters.clone(), + }; + if let Ok(mut map) = runtime.box_declarations.write() { + map.insert(name.clone(), decl); + } + } + _ => {} + } + } + walk(ast, runtime); + } + /// Execute WASM compilation mode #[cfg(feature = "wasm-backend")] fn execute_wasm_mode(&self, filename: &str) { diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 8a422731..1f578a0e 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -6,6 +6,7 @@ pub mod plugin_config; pub mod box_registry; pub mod plugin_loader_v2; pub mod unified_registry; +pub mod nyash_runtime; // pub mod plugin_box; // legacy - 古いPluginBox // pub mod plugin_loader; // legacy - Host VTable使用 @@ -16,6 +17,7 @@ pub use plugin_config::PluginConfig; pub use box_registry::{BoxFactoryRegistry, BoxProvider, get_global_registry}; pub use plugin_loader_v2::{PluginLoaderV2, get_global_loader_v2, init_global_loader_v2}; pub use unified_registry::{get_global_unified_registry, init_global_unified_registry, register_user_defined_factory}; +pub use nyash_runtime::{NyashRuntime, NyashRuntimeBuilder}; // pub use plugin_box::PluginBox; // legacy // Use unified plugin loader (formerly v2) -// pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy \ No newline at end of file +// pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy diff --git a/src/runtime/nyash_runtime.rs b/src/runtime/nyash_runtime.rs new file mode 100644 index 00000000..92cd73a9 --- /dev/null +++ b/src/runtime/nyash_runtime.rs @@ -0,0 +1,78 @@ +//! Minimal NyashRuntime skeleton shared by interpreter and VM +//! +//! Focused on dependency inversion: core models + runtime services, +//! while execution strategies live in interpreter/VM layers. + +use std::collections::HashMap; +use std::sync::{Arc, Mutex, RwLock}; + +use crate::core::model::BoxDeclaration; +use crate::box_factory::{UnifiedBoxRegistry, BoxFactory}; +use crate::box_factory::builtin::BuiltinBoxFactory; +#[cfg(feature = "plugins")] +use crate::box_factory::plugin::PluginBoxFactory; + +/// Core runtime container for executing Nyash programs +pub struct NyashRuntime { + /// Unified registry that can create any Box type + pub box_registry: Arc>, + /// User-defined box declarations collected from source + pub box_declarations: Arc>>, +} + +impl NyashRuntime { + /// Create a new runtime with defaults + pub fn new() -> Self { + Self { + box_registry: create_default_registry(), + box_declarations: Arc::new(RwLock::new(HashMap::new())), + } + } +} + +/// Builder for NyashRuntime allowing DI without globals (future-proof) +pub struct NyashRuntimeBuilder { + box_registry: Option>>, + box_declarations: Option>>>, +} + +impl NyashRuntimeBuilder { + pub fn new() -> Self { + Self { box_registry: None, box_declarations: None } + } + + /// Inject a BoxFactory implementation directly into a private registry + pub fn with_factory(mut self, factory: Arc) -> Self { + let registry = self.box_registry.take().unwrap_or_else(|| create_default_registry()); + if let Ok(mut reg) = registry.lock() { + reg.register(factory); + } + self.box_registry = Some(registry); + self + } + + pub fn with_box_declarations( + mut self, + decls: Arc>>, + ) -> Self { + self.box_declarations = Some(decls); + self + } + + pub fn build(self) -> NyashRuntime { + NyashRuntime { + box_registry: self.box_registry.unwrap_or_else(|| create_default_registry()), + box_declarations: self.box_declarations.unwrap_or_else(|| Arc::new(RwLock::new(HashMap::new()))), + } + } +} + +fn create_default_registry() -> Arc> { + let mut registry = UnifiedBoxRegistry::new(); + registry.register(Arc::new(BuiltinBoxFactory::new())); + #[cfg(feature = "plugins")] + { + registry.register(Arc::new(PluginBoxFactory::new())); + } + Arc::new(Mutex::new(registry)) +} diff --git a/src/scope_tracker.rs b/src/scope_tracker.rs index e962f4eb..05a5c60b 100644 --- a/src/scope_tracker.rs +++ b/src/scope_tracker.rs @@ -6,6 +6,9 @@ use std::sync::Arc; use crate::box_trait::NyashBox; +use crate::instance_v2::InstanceBox; +#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] +use crate::runtime::plugin_loader_v2::PluginBoxV2; /// Tracks Box instances created in different scopes for proper fini calls pub struct ScopeTracker { @@ -31,11 +34,18 @@ impl ScopeTracker { if let Some(scope) = self.scopes.pop() { // Call fini in reverse order of creation for arc_box in scope.into_iter().rev() { - // For now, fini handling is simplified - // In a full implementation, we would check if the Box has a fini method - // and call it appropriately - // TODO: Implement proper fini dispatch - let _ = arc_box; // Suppress unused warning + // InstanceBox: call fini() + if let Some(instance) = arc_box.as_any().downcast_ref::() { + let _ = instance.fini(); + continue; + } + // PluginBox: call plugin fini + #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] + if let Some(plugin) = arc_box.as_any().downcast_ref::() { + plugin.call_fini(); + continue; + } + // Builtin and others: no-op for now } } @@ -70,4 +80,4 @@ impl Default for ScopeTracker { fn default() -> Self { Self::new() } -} \ No newline at end of file +}