#![cfg(feature = "e2e")] //! VM E2E: Compile Nyash to MIR and execute via VM, with mock plugin factory use std::sync::Arc; use nyash_rust::backend::VM; use nyash_rust::box_factory::builtin::BuiltinGroups; use nyash_rust::box_factory::BoxFactory; use nyash_rust::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox}; use nyash_rust::interpreter::RuntimeError; use nyash_rust::mir::MirCompiler; use nyash_rust::parser::NyashParser; use nyash_rust::runtime::NyashRuntimeBuilder; // Minimal AdderBox to validate plugin factory path under VM #[derive(Debug, Clone)] struct AdderBox { base: BoxBase, sum: i64, } impl AdderBox { fn new(a: i64, b: i64) -> Self { Self { base: BoxBase::new(), sum: a + b, } } } impl BoxCore for AdderBox { fn box_id(&self) -> u64 { self.base.id } fn parent_type_id(&self) -> Option { None } fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "AdderBox(sum={})", self.sum) } fn as_any(&self) -> &dyn std::any::Any { self } fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self } } impl NyashBox for AdderBox { fn to_string_box(&self) -> StringBox { StringBox::new(self.sum.to_string()) } fn equals(&self, other: &dyn NyashBox) -> BoolBox { if let Some(a) = other.as_any().downcast_ref::() { BoolBox::new(self.sum == a.sum) } else { BoolBox::new(false) } } fn type_name(&self) -> &'static str { "AdderBox" } fn clone_box(&self) -> Box { Box::new(self.clone()) } fn share_box(&self) -> Box { Box::new(self.clone()) } } struct TestPluginFactory; impl TestPluginFactory { fn new() -> Self { Self } } impl BoxFactory for TestPluginFactory { fn create_box( &self, name: &str, args: &[Box], ) -> Result, RuntimeError> { match name { "AdderBox" => { if args.len() != 2 { return Err(RuntimeError::InvalidOperation { message: format!("AdderBox expects 2 args, got {}", args.len()), }); } let a = args[0].to_string_box().value.parse::().map_err(|_| { RuntimeError::TypeError { message: "AdderBox arg a must be int".into(), } })?; let b = args[1].to_string_box().value.parse::().map_err(|_| { RuntimeError::TypeError { message: "AdderBox arg b must be int".into(), } })?; Ok(Box::new(AdderBox::new(a, b))) } _ => Err(RuntimeError::InvalidOperation { message: format!("Unknown Box type: {}", name), }), } } fn box_types(&self) -> Vec<&str> { vec!["AdderBox"] } } #[test] fn vm_e2e_adder_box() { // Build runtime with builtin + user-defined + mock plugin factory let runtime = NyashRuntimeBuilder::new() .with_builtin_groups(BuiltinGroups::native_full()) .with_factory(Arc::new( nyash_rust::box_factory::user_defined::UserDefinedBoxFactory::new( nyash_rust::interpreter::SharedState::new(), ), )) .with_factory(Arc::new(TestPluginFactory::new())) .build(); // Nyash code: construct AdderBox and leave it as final expression let code = r#" a = new AdderBox(10, 32) a "#; // Parse → MIR let ast = NyashParser::parse_from_string(code).expect("parse ok"); let mut mir_compiler = MirCompiler::new(); let compile_result = mir_compiler.compile(ast).expect("mir ok"); // Execute via VM using the prepared runtime let mut vm = VM::with_runtime(runtime); let result = vm .execute_module(&compile_result.module) .expect("vm exec ok"); // The VM returns an Option> or a value; we print/debug and check string form // Here we rely on Display via to_string_box through Debug format // Try to format the result if available // For this implementation, result is a generic value; we check debug string contains 42 or to_string equivalent. let s = format!("{:?}", result); assert!( s.contains("42") || s.contains("AdderBox"), "unexpected VM result: {}", s ); }