//! 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::box_factory::builtin::BuiltinBoxFactory; #[cfg(feature = "plugins")] use crate::box_factory::plugin::PluginBoxFactory; use crate::box_factory::{BoxFactory, UnifiedBoxRegistry}; use crate::core::model::BoxDeclaration; /// 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>>, /// GC hooks (switchable runtime). Default is no-op. pub gc: Arc, /// Optional scheduler (single-thread by default is fine) pub scheduler: Option>, } 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())), gc: Arc::new(crate::runtime::gc::NullGc), scheduler: Some(Arc::new( crate::runtime::scheduler::SingleThreadScheduler::new(), )), } } } /// Builder for NyashRuntime allowing DI without globals (future-proof) pub struct NyashRuntimeBuilder { box_registry: Option>>, box_declarations: Option>>>, gc: Option>, scheduler: Option>, } impl NyashRuntimeBuilder { pub fn new() -> Self { Self { box_registry: None, box_declarations: None, gc: None, scheduler: 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 { let registry = self .box_registry .unwrap_or_else(|| create_default_registry()); NyashRuntime { box_registry: registry, box_declarations: self .box_declarations .unwrap_or_else(|| Arc::new(RwLock::new(HashMap::new()))), gc: self .gc .unwrap_or_else(|| Arc::new(crate::runtime::gc::NullGc)), scheduler: Some(self.scheduler.unwrap_or_else(|| { Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new()) })), } } } fn create_default_registry() -> Arc> { let mut registry = UnifiedBoxRegistry::new(); // Default: enable builtins unless explicitly building with feature "plugins-only" #[cfg(not(feature = "plugins-only"))] { registry.register(Arc::new(BuiltinBoxFactory::new())); } #[cfg(feature = "plugins")] { registry.register(Arc::new(PluginBoxFactory::new())); } Arc::new(Mutex::new(registry)) } impl NyashRuntimeBuilder { /// Inject custom GC hooks (switchable runtime). Default is no-op. pub fn with_gc_hooks(mut self, gc: Arc) -> Self { self.gc = Some(gc); self } /// Convenience: use CountingGc for development metrics pub fn with_counting_gc(mut self) -> Self { let mode = crate::runtime::gc_mode::GcMode::from_env(); if mode == crate::runtime::gc_mode::GcMode::Off { // Respect GC_MODE=off: keep NullGc self.gc = Some(Arc::new(crate::runtime::gc::NullGc)); return self; } let gc = Arc::new(crate::runtime::gc::CountingGc::new_with_mode(mode)); self.gc = Some(gc); self } /// Inject a custom scheduler implementation pub fn with_scheduler(mut self, sched: Arc) -> Self { self.scheduler = Some(sched); self } /// Convenience: use SingleThreadScheduler pub fn with_single_thread_scheduler(mut self) -> Self { self.scheduler = Some(Arc::new( crate::runtime::scheduler::SingleThreadScheduler::new(), )); self } }