diff --git a/src/box_factory/builtin.rs b/src/box_factory/builtin.rs new file mode 100644 index 00000000..ab74a1f1 --- /dev/null +++ b/src/box_factory/builtin.rs @@ -0,0 +1,307 @@ +/*! + * Builtin Box Factory + * + * Handles creation of all built-in Box types (StringBox, IntegerBox, etc.) + * Replaces the 600+ line match statement with clean factory pattern + */ + +use super::BoxFactory; +use crate::box_trait::NyashBox; +use crate::RuntimeError; +use crate::boxes::*; +use std::collections::HashMap; + +type BoxCreator = Box]) -> Result, RuntimeError> + Send + Sync>; + +/// Factory for all built-in Box types +pub struct BuiltinBoxFactory { + /// Map of Box type names to their creation functions + creators: HashMap, +} + +impl BuiltinBoxFactory { + /// Create a new factory with all built-in types registered + pub fn new() -> Self { + let mut factory = Self { + creators: HashMap::new(), + }; + + // Register all built-in Box types + factory.register_basic_types(); + factory.register_container_types(); + factory.register_utility_types(); + factory.register_io_types(); + #[cfg(not(target_arch = "wasm32"))] + factory.register_native_types(); + #[cfg(target_arch = "wasm32")] + factory.register_wasm_types(); + + factory + } + + /// Register basic data types + fn register_basic_types(&mut self) { + // StringBox + self.register("StringBox", |args| { + let value = match args.get(0) { + Some(arg) => arg.to_string_box().value, + None => String::new(), + }; + Ok(Box::new(StringBox::new(value))) + }); + + // IntegerBox + self.register("IntegerBox", |args| { + let value = match args.get(0) { + Some(arg) => { + // Try direct downcast first + if let Some(int_box) = arg.as_any().downcast_ref::() { + int_box.value + } else { + // Parse from string + arg.to_string_box().value.parse::() + .map_err(|_| RuntimeError::TypeError { + message: format!("Cannot convert '{}' to integer", arg.to_string_box().value), + })? + } + }, + None => 0, + }; + Ok(Box::new(IntegerBox::new(value))) + }); + + // BoolBox + self.register("BoolBox", |args| { + let value = match args.get(0) { + Some(arg) => { + if let Some(bool_box) = arg.as_any().downcast_ref::() { + bool_box.value + } else { + match arg.to_string_box().value.to_lowercase().as_str() { + "true" => true, + "false" => false, + _ => return Err(RuntimeError::TypeError { + message: format!("Cannot convert '{}' to boolean", arg.to_string_box().value), + }), + } + } + }, + None => false, + }; + Ok(Box::new(BoolBox::new(value))) + }); + + // FloatBox + self.register("FloatBox", |args| { + let value = match args.get(0) { + Some(arg) => { + if let Some(float_box) = arg.as_any().downcast_ref::() { + float_box.value + } else if let Some(int_box) = arg.as_any().downcast_ref::() { + int_box.value as f64 + } else { + arg.to_string_box().value.parse::() + .map_err(|_| RuntimeError::TypeError { + message: format!("Cannot convert '{}' to float", arg.to_string_box().value), + })? + } + }, + None => 0.0, + }; + Ok(Box::new(FloatBox::new(value))) + }); + + // NullBox + self.register("NullBox", |_args| { + Ok(Box::new(NullBox::new())) + }); + } + + /// Register container types + fn register_container_types(&mut self) { + // ArrayBox + self.register("ArrayBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("ArrayBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(ArrayBox::new())) + }); + + // MapBox + self.register("MapBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("MapBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(MapBox::new())) + }); + + // ResultBox + self.register("ResultBox", |args| { + if args.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("ResultBox constructor expects 1 argument, got {}", args.len()), + }); + } + let value = args[0].clone_box(); + Ok(Box::new(crate::boxes::result::NyashResultBox::new_ok(value))) + }); + } + + /// Register utility types + fn register_utility_types(&mut self) { + // MathBox + self.register("MathBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("MathBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(MathBox::new())) + }); + + // RandomBox + self.register("RandomBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("RandomBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(RandomBox::new())) + }); + + // TimeBox + self.register("TimeBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("TimeBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(TimeBox::new())) + }); + + // DebugBox + self.register("DebugBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("DebugBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(DebugBox::new())) + }); + } + + /// Register I/O types + fn register_io_types(&mut self) { + // ConsoleBox + self.register("ConsoleBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("ConsoleBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(ConsoleBox::new())) + }); + + // SoundBox + self.register("SoundBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("SoundBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(SoundBox::new())) + }); + } + + /// Register native-only types + #[cfg(not(target_arch = "wasm32"))] + fn register_native_types(&mut self) { + // DateTimeBox + self.register("DateTimeBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("DateTimeBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(DateTimeBox::now())) + }); + + // Additional native types can be registered here + #[cfg(all(feature = "gui", not(target_arch = "wasm32")))] + { + self.register("EguiBox", |args| { + if !args.is_empty() { + return Err(RuntimeError::InvalidOperation { + message: format!("EguiBox constructor expects 0 arguments, got {}", args.len()), + }); + } + Ok(Box::new(crate::boxes::EguiBox::new())) + }); + } + } + + /// Register WASM-specific types + #[cfg(target_arch = "wasm32")] + fn register_wasm_types(&mut self) { + // WebDisplayBox + self.register("WebDisplayBox", |args| { + if args.len() != 1 { + return Err(RuntimeError::InvalidOperation { + message: format!("WebDisplayBox constructor expects 1 argument (element_id), got {}", args.len()), + }); + } + + if let Some(id_str) = args[0].as_any().downcast_ref::() { + Ok(Box::new(crate::boxes::WebDisplayBox::new(id_str.value.clone()))) + } else { + Err(RuntimeError::TypeError { + message: "WebDisplayBox constructor requires string element_id argument".to_string(), + }) + } + }); + + // Additional WASM types can be registered here + } + + /// Register a Box creator function + fn register(&mut self, name: &str, creator: F) + where + F: Fn(&[Box]) -> Result, RuntimeError> + Send + Sync + 'static, + { + self.creators.insert(name.to_string(), Box::new(creator)); + } +} + +impl BoxFactory for BuiltinBoxFactory { + fn create_box( + &self, + name: &str, + args: &[Box], + ) -> Result, RuntimeError> { + if let Some(creator) = self.creators.get(name) { + creator(args) + } else { + Err(RuntimeError::InvalidOperation { + message: format!("Unknown built-in Box type: {}", name), + }) + } + } + + fn box_types(&self) -> Vec<&str> { + self.creators.keys().map(|s| s.as_str()).collect() + } +} + +/// Declarative macro for registering multiple Box types at once +#[macro_export] +macro_rules! register_builtins { + ($factory:expr, $($box_name:literal => $creator_fn:expr),* $(,)?) => { + $( + $factory.register($box_name, $creator_fn); + )* + }; +} \ No newline at end of file diff --git a/src/box_factory/mod.rs b/src/box_factory/mod.rs new file mode 100644 index 00000000..03ecbf4c --- /dev/null +++ b/src/box_factory/mod.rs @@ -0,0 +1,135 @@ +/*! + * Unified Box Factory Architecture + * + * Phase 9.78: 統合BoxFactoryアーキテクチャ + * すべてのBox生成(ビルトイン、ユーザー定義、プラグイン)を統一的に扱う + * + * Design principles: + * - "Everything is Box" 哲学の実装レベルでの体現 + * - birth/finiライフサイクルの明確な責務分離 + * - 保守性と拡張性の劇的向上 + */ + +use crate::box_trait::NyashBox; +use crate::RuntimeError; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +/// Unified interface for all Box creation +pub trait BoxFactory: Send + Sync { + /// Create a new Box instance with given arguments + fn create_box( + &self, + name: &str, + args: &[Box], + ) -> Result, RuntimeError>; + + /// Check if this factory is currently available + fn is_available(&self) -> bool { + true + } + + /// Get list of Box types this factory can create + fn box_types(&self) -> Vec<&str>; + + /// Check if this factory supports birth/fini lifecycle + fn supports_birth(&self) -> bool { + true + } +} + +/// Registry that manages all BoxFactory implementations +pub struct UnifiedBoxRegistry { + /// Ordered list of factories (priority: builtin > user > plugin) + pub factories: Vec>, + + /// Quick lookup cache for performance + type_cache: RwLock>, // maps type name to factory index +} + +impl UnifiedBoxRegistry { + /// Create a new empty registry + pub fn new() -> Self { + Self { + factories: Vec::new(), + type_cache: RwLock::new(HashMap::new()), + } + } + + /// Register a new factory + pub fn register(&mut self, factory: Arc) { + // Get all types this factory can create + let types = factory.box_types(); + let factory_index = self.factories.len(); + + // Update cache + let mut cache = self.type_cache.write().unwrap(); + for type_name in types { + // First registered factory wins (priority order) + cache.entry(type_name.to_string()) + .or_insert(factory_index); + } + + self.factories.push(factory); + } + + /// Create a Box using the unified interface + pub fn create_box( + &self, + name: &str, + args: &[Box], + ) -> Result, RuntimeError> { + // Check cache first + let cache = self.type_cache.read().unwrap(); + if let Some(&factory_index) = cache.get(name) { + if let Some(factory) = self.factories.get(factory_index) { + if factory.is_available() { + return factory.create_box(name, args); + } + } + } + drop(cache); + + // Fallback: linear search through all factories + for factory in &self.factories { + if factory.box_types().contains(&name) && factory.is_available() { + return factory.create_box(name, args); + } + } + + Err(RuntimeError::InvalidOperation { + message: format!("Unknown Box type: {}", name), + }) + } + + /// Get all available Box types + pub fn available_types(&self) -> Vec { + let mut types = Vec::new(); + for factory in &self.factories { + if factory.is_available() { + for type_name in factory.box_types() { + types.push(type_name.to_string()); + } + } + } + types.sort(); + types.dedup(); + types + } +} + +/// Re-export submodules +pub mod builtin; +pub mod user_defined; +pub mod plugin; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_registry_creation() { + let registry = UnifiedBoxRegistry::new(); + assert_eq!(registry.available_types().len(), 0); + } +} \ No newline at end of file diff --git a/src/box_factory/plugin.rs b/src/box_factory/plugin.rs new file mode 100644 index 00000000..b8b6cde1 --- /dev/null +++ b/src/box_factory/plugin.rs @@ -0,0 +1,57 @@ +/*! + * Plugin Box Factory + * + * Handles creation of plugin-based Box types through BID/FFI system + * Integrates with v2 plugin system (BoxFactoryRegistry) + */ + +use super::BoxFactory; +use crate::box_trait::NyashBox; +use crate::RuntimeError; +use crate::runtime::get_global_registry; + +/// Factory for plugin-based Box types +pub struct PluginBoxFactory { + // Uses the global BoxFactoryRegistry from v2 plugin system +} + +impl PluginBoxFactory { + pub fn new() -> Self { + Self {} + } +} + +impl BoxFactory for PluginBoxFactory { + fn create_box( + &self, + name: &str, + args: &[Box], + ) -> Result, RuntimeError> { + // Use the existing v2 plugin system + let registry = get_global_registry(); + + if let Some(_provider) = registry.get_provider(name) { + registry.create_box(name, args) + .map_err(|e| RuntimeError::InvalidOperation { + message: format!("Plugin Box creation failed: {}", e), + }) + } else { + Err(RuntimeError::InvalidOperation { + message: format!("No plugin provider for Box type: {}", name), + }) + } + } + + fn box_types(&self) -> Vec<&str> { + // TODO: Get list from BoxFactoryRegistry + // For now, return empty as registry doesn't expose this yet + vec![] + } + + fn is_available(&self) -> bool { + // Check if any plugins are loaded + let registry = get_global_registry(); + // TODO: Add method to check if registry has any providers + true + } +} \ No newline at end of file diff --git a/src/box_factory/user_defined.rs b/src/box_factory/user_defined.rs new file mode 100644 index 00000000..7a083615 --- /dev/null +++ b/src/box_factory/user_defined.rs @@ -0,0 +1,54 @@ +/*! + * User-Defined Box Factory + * + * Handles creation of user-defined Box types through InstanceBox + * Manages inheritance, fields, methods, and birth/fini lifecycle + */ + +use super::BoxFactory; +use crate::box_trait::NyashBox; +use crate::RuntimeError; + +/// Factory for user-defined Box types +pub struct UserDefinedBoxFactory { + // TODO: This will need access to the interpreter context + // to look up box declarations and execute constructors + // For now, this is a placeholder +} + +impl UserDefinedBoxFactory { + pub fn new() -> Self { + Self { + // TODO: Initialize with interpreter reference + } + } +} + +impl BoxFactory for UserDefinedBoxFactory { + fn create_box( + &self, + _name: &str, + _args: &[Box], + ) -> Result, RuntimeError> { + // TODO: Implementation will be moved from objects.rs + // This will: + // 1. Look up box declaration + // 2. Create InstanceBox with fields and methods + // 3. Execute birth constructor if present + // 4. Return the instance + + Err(RuntimeError::InvalidOperation { + message: "User-defined Box factory not yet implemented".to_string(), + }) + } + + fn box_types(&self) -> Vec<&str> { + // TODO: Return list of registered user-defined Box types + vec![] + } + + fn is_available(&self) -> bool { + // TODO: Check if interpreter context is available + false + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index da29e7df..b48b1fdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ use wasm_bindgen::prelude::*; pub mod box_trait; pub mod boxes; +pub mod box_factory; // 🏭 Unified Box Factory Architecture (Phase 9.78) pub mod stdlib; pub mod environment; pub mod tokenizer; diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 2da3335a..8a422731 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -5,6 +5,7 @@ pub mod plugin_config; pub mod box_registry; pub mod plugin_loader_v2; +pub mod unified_registry; // pub mod plugin_box; // legacy - 古いPluginBox // pub mod plugin_loader; // legacy - Host VTable使用 @@ -14,6 +15,7 @@ mod tests; 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 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 diff --git a/src/runtime/unified_registry.rs b/src/runtime/unified_registry.rs new file mode 100644 index 00000000..e44e5403 --- /dev/null +++ b/src/runtime/unified_registry.rs @@ -0,0 +1,49 @@ +/*! + * Global Unified Box Registry + * + * Manages the global instance of UnifiedBoxRegistry + * Integrates all Box creation sources (builtin, user-defined, plugin) + */ + +use crate::box_factory::{UnifiedBoxRegistry, builtin::BuiltinBoxFactory, plugin::PluginBoxFactory}; +use std::sync::{Arc, Mutex, OnceLock}; + +/// Global registry instance +static GLOBAL_REGISTRY: OnceLock>> = OnceLock::new(); + +/// Initialize the global unified registry +pub fn init_global_unified_registry() { + GLOBAL_REGISTRY.get_or_init(|| { + let mut registry = UnifiedBoxRegistry::new(); + + // Register built-in Box factory (highest priority) + registry.register(Arc::new(BuiltinBoxFactory::new())); + + // Register plugin Box factory (lowest priority) + registry.register(Arc::new(PluginBoxFactory::new())); + + // TODO: User-defined Box factory will be registered by interpreter + + Arc::new(Mutex::new(registry)) + }); +} + +/// Get the global unified registry +pub fn get_global_unified_registry() -> Arc> { + init_global_unified_registry(); + GLOBAL_REGISTRY.get().unwrap().clone() +} + +/// Register a user-defined Box factory (called by interpreter) +pub fn register_user_defined_factory(factory: Arc) { + let registry = get_global_unified_registry(); + let mut registry_lock = registry.lock().unwrap(); + + // Insert at position 1 (after builtin, before plugin) + // This maintains priority: builtin > user > plugin + if registry_lock.factories.len() >= 2 { + registry_lock.factories.insert(1, factory); + } else { + registry_lock.register(factory); + } +} \ No newline at end of file