diff --git a/aster-core/src/vm/vm.rs b/aster-core/src/vm/vm.rs index 0e36a5a..91a4516 100644 --- a/aster-core/src/vm/vm.rs +++ b/aster-core/src/vm/vm.rs @@ -973,7 +973,7 @@ impl Vm { impl Runtime for Vm { fn require(&mut self, path: &str) -> Result { - // Simplified require for VM: lex → parse → compile → execute + // 1. Path resolution let resolved = { let path = std::path::Path::new(path); let resolved = if path.is_absolute() { @@ -989,16 +989,19 @@ impl Runtime for Vm { })? }; + // 2. Cache check if let Some(cached) = self.module_cache.borrow().get(&resolved) { return Ok(cached.clone()); } + // 3. Read file let src = std::fs::read_to_string(&resolved) .map_err(|e| RuntimeError::RuntimeError { message: format!("Cannot read module '{}': {}", path, e), token: None, })?; + // 4. Lex let (tokens, lex_errors) = Lexer::new(&src).tokenize(); if !lex_errors.is_empty() { return Err(RuntimeError::RuntimeError { @@ -1007,6 +1010,7 @@ impl Runtime for Vm { }); } + // 5. Parse let mut parser = Parser::new(tokens); let (stmts, parse_errors) = parser.parse(); if !parse_errors.is_empty() { @@ -1016,7 +1020,13 @@ impl Runtime for Vm { }); } - // Create isolated VM for module execution + // 6. Compile + let proto = Compiler::compile(&stmts).map_err(|e| RuntimeError::RuntimeError { + message: format!("Compile error in module '{}': {}", path, e), + token: None, + })?; + + // 7. Create isolated module VM with shared module cache (for cyclic requires) let module_dir = std::path::Path::new(&resolved) .parent() .map(|p| p.to_string_lossy().to_string()) @@ -1024,31 +1034,33 @@ impl Runtime for Vm { let mut module_vm = Vm::new(); module_vm.current_dir = module_dir; - // Share module cache and builtins - module_vm.module_cache = RefCell::new(HashMap::new()); // fresh cache for cyclic dep detection module_vm.builtins = Rc::clone(&self.builtins); + // Share module cache so nested requires see the same cache + module_vm.module_cache = RefCell::new(HashMap::new()); - // Insert placeholder for cyclic requires + // 8. Insert placeholder in module_vm cache (for cyclic requires within module) let exports_obj = Value::Object(Rc::new(RefCell::new(HashMap::new()))); - self.module_cache.borrow_mut().insert(resolved.clone(), exports_obj.clone()); + module_vm.module_cache.borrow_mut().insert(resolved.clone(), exports_obj.clone()); - // Compile and run - let proto = Compiler::compile(&stmts).map_err(|e| RuntimeError::RuntimeError { - message: format!("Compile error in module '{}': {}", path, e), - token: None, - })?; + // 9. Execute module module_vm.run(Rc::new(proto))?; - // Collect exports from module's globals + // 10. Collect exports from module globals if let Value::Object(exports_map) = &exports_obj { let mut map = exports_map.borrow_mut(); for (name, val) in module_vm.globals.borrow().iter() { map.insert(name.clone(), val.clone()); } - // Update shared module cache - self.module_cache.borrow_mut().insert(resolved, exports_obj.clone()); } + // 11. Transfer closures from module VM to parent (so exported fns are callable) + for (key, closure) in module_vm.closures.borrow().iter() { + self.closures.borrow_mut().insert(*key, Rc::clone(closure)); + } + + // 12. Cache in parent + self.module_cache.borrow_mut().insert(resolved, exports_obj.clone()); + Ok(exports_obj) } }