1use std::cell::RefCell;
2use std::collections::{BTreeMap, HashSet};
3use std::future::Future;
4use std::path::{Path, PathBuf};
5use std::pin::Pin;
6use std::rc::Rc;
7
8use crate::value::{ModuleFunctionRegistry, VmClosure, VmEnv, VmError, VmValue};
9
10use super::{LoadedModule, ScopeSpan, Vm};
11
12impl Vm {
13 fn export_loaded_module(
14 &mut self,
15 module_path: &Path,
16 loaded: &LoadedModule,
17 selected_names: Option<&[String]>,
18 ) -> Result<(), VmError> {
19 let export_names: Vec<String> = if let Some(names) = selected_names {
20 names.to_vec()
21 } else if !loaded.public_names.is_empty() {
22 loaded.public_names.iter().cloned().collect()
23 } else {
24 loaded.functions.keys().cloned().collect()
25 };
26
27 let module_name = module_path.display().to_string();
28 for name in export_names {
29 let Some(closure) = loaded.functions.get(&name) else {
30 return Err(VmError::Runtime(format!(
31 "Import error: '{name}' is not defined in {module_name}"
32 )));
33 };
34 if let Some(VmValue::Closure(_)) = self.env.get(&name) {
35 return Err(VmError::Runtime(format!(
36 "Import collision: '{name}' is already defined when importing {module_name}. \
37 Use selective imports to disambiguate: import {{ {name} }} from \"...\""
38 )));
39 }
40 self.env
41 .define(&name, VmValue::Closure(Rc::clone(closure)), false)?;
42 }
43 Ok(())
44 }
45
46 pub(super) fn execute_import<'a>(
48 &'a mut self,
49 path: &'a str,
50 selected_names: Option<&'a [String]>,
51 ) -> Pin<Box<dyn Future<Output = Result<(), VmError>> + 'a>> {
52 Box::pin(async move {
53 let _import_span = ScopeSpan::new(crate::tracing::SpanKind::Import, path.to_string());
54
55 if let Some(module) = path.strip_prefix("std/") {
56 if let Some(source) = crate::stdlib_modules::get_stdlib_source(module) {
57 let synthetic = PathBuf::from(format!("<stdlib>/{module}.harn"));
58 if self.imported_paths.contains(&synthetic) {
59 return Ok(());
60 }
61 if let Some(loaded) = self.module_cache.get(&synthetic).cloned() {
62 return self.export_loaded_module(&synthetic, &loaded, selected_names);
63 }
64 self.imported_paths.push(synthetic.clone());
65
66 let mut lexer = harn_lexer::Lexer::new(source);
67 let tokens = lexer.tokenize().map_err(|e| {
68 VmError::Runtime(format!("stdlib lex error in std/{module}: {e}"))
69 })?;
70 let mut parser = harn_parser::Parser::new(tokens);
71 let program = parser.parse().map_err(|e| {
72 VmError::Runtime(format!("stdlib parse error in std/{module}: {e}"))
73 })?;
74
75 let loaded = self.import_declarations(&program, None).await?;
76 self.imported_paths.pop();
77 self.module_cache.insert(synthetic.clone(), loaded.clone());
78 self.export_loaded_module(&synthetic, &loaded, selected_names)?;
79 return Ok(());
80 }
81 return Err(VmError::Runtime(format!(
82 "Unknown stdlib module: std/{module}"
83 )));
84 }
85
86 let base = self
87 .source_dir
88 .clone()
89 .unwrap_or_else(|| PathBuf::from("."));
90 let mut file_path = base.join(path);
91
92 if !file_path.exists() && file_path.extension().is_none() {
93 file_path.set_extension("harn");
94 }
95
96 if !file_path.exists() {
97 let pkg_path = base.join(".harn/packages").join(path);
98 if pkg_path.exists() {
99 file_path = if pkg_path.is_dir() {
100 let lib = pkg_path.join("lib.harn");
101 if lib.exists() {
102 lib
103 } else {
104 pkg_path
105 }
106 } else {
107 pkg_path
108 };
109 } else {
110 let mut pkg_harn = pkg_path.clone();
111 pkg_harn.set_extension("harn");
112 if pkg_harn.exists() {
113 file_path = pkg_harn;
114 }
115 }
116 }
117
118 let canonical = file_path
119 .canonicalize()
120 .unwrap_or_else(|_| file_path.clone());
121 if self.imported_paths.contains(&canonical) {
122 return Ok(());
123 }
124 if let Some(loaded) = self.module_cache.get(&canonical).cloned() {
125 return self.export_loaded_module(&canonical, &loaded, selected_names);
126 }
127 self.imported_paths.push(canonical.clone());
128
129 let source = std::fs::read_to_string(&file_path).map_err(|e| {
130 VmError::Runtime(format!(
131 "Import error: cannot read '{}': {e}",
132 file_path.display()
133 ))
134 })?;
135
136 let mut lexer = harn_lexer::Lexer::new(&source);
137 let tokens = lexer
138 .tokenize()
139 .map_err(|e| VmError::Runtime(format!("Import lex error: {e}")))?;
140 let mut parser = harn_parser::Parser::new(tokens);
141 let program = parser
142 .parse()
143 .map_err(|e| VmError::Runtime(format!("Import parse error: {e}")))?;
144
145 let loaded = self.import_declarations(&program, Some(&file_path)).await?;
146 self.imported_paths.pop();
147 self.module_cache.insert(canonical.clone(), loaded.clone());
148 self.export_loaded_module(&canonical, &loaded, selected_names)?;
149
150 Ok(())
151 })
152 }
153
154 fn import_declarations<'a>(
156 &'a mut self,
157 program: &'a [harn_parser::SNode],
158 file_path: Option<&'a Path>,
159 ) -> Pin<Box<dyn Future<Output = Result<LoadedModule, VmError>> + 'a>> {
160 Box::pin(async move {
161 let caller_env = self.env.clone();
162 let old_source_dir = self.source_dir.clone();
163 self.env = VmEnv::new();
164 if let Some(fp) = file_path {
165 if let Some(parent) = fp.parent() {
166 self.source_dir = Some(parent.to_path_buf());
167 }
168 }
169
170 for node in program {
171 match &node.node {
172 harn_parser::Node::ImportDecl { path: sub_path } => {
173 self.execute_import(sub_path, None).await?;
174 }
175 harn_parser::Node::SelectiveImport {
176 names,
177 path: sub_path,
178 } => {
179 self.execute_import(sub_path, Some(names)).await?;
180 }
181 _ => {}
182 }
183 }
184
185 let module_state: crate::value::ModuleState = {
191 let mut init_env = self.env.clone();
192 let init_nodes: Vec<harn_parser::SNode> = program
193 .iter()
194 .filter(|sn| {
195 matches!(
196 &sn.node,
197 harn_parser::Node::VarBinding { .. }
198 | harn_parser::Node::LetBinding { .. }
199 )
200 })
201 .cloned()
202 .collect();
203 if !init_nodes.is_empty() {
204 let init_compiler = crate::Compiler::new();
205 let init_chunk = init_compiler
206 .compile(&init_nodes)
207 .map_err(|e| VmError::Runtime(format!("Import init compile error: {e}")))?;
208 let saved_env = std::mem::replace(&mut self.env, init_env);
211 let saved_frames = std::mem::take(&mut self.frames);
212 let saved_handlers = std::mem::take(&mut self.exception_handlers);
213 let saved_iterators = std::mem::take(&mut self.iterators);
214 let saved_deadlines = std::mem::take(&mut self.deadlines);
215 let init_result = self.run_chunk(&init_chunk).await;
216 init_env = std::mem::replace(&mut self.env, saved_env);
217 self.frames = saved_frames;
218 self.exception_handlers = saved_handlers;
219 self.iterators = saved_iterators;
220 self.deadlines = saved_deadlines;
221 init_result?;
222 }
223 Rc::new(RefCell::new(init_env))
224 };
225
226 let module_env = self.env.clone();
227 let registry: ModuleFunctionRegistry = Rc::new(RefCell::new(BTreeMap::new()));
228 let source_dir = file_path.and_then(|fp| fp.parent().map(|p| p.to_path_buf()));
229 let mut functions: BTreeMap<String, Rc<VmClosure>> = BTreeMap::new();
230 let mut public_names: HashSet<String> = HashSet::new();
231
232 for node in program {
233 let inner = match &node.node {
237 harn_parser::Node::AttributedDecl { inner, .. } => inner.as_ref(),
238 _ => node,
239 };
240 let harn_parser::Node::FnDecl {
241 name,
242 params,
243 body,
244 is_pub,
245 ..
246 } = &inner.node
247 else {
248 continue;
249 };
250
251 let mut compiler = crate::Compiler::new();
252 let module_source_file = file_path.map(|p| p.display().to_string());
253 let func_chunk = compiler
254 .compile_fn_body(params, body, module_source_file)
255 .map_err(|e| VmError::Runtime(format!("Import compile error: {e}")))?;
256 let closure = Rc::new(VmClosure {
257 func: func_chunk,
258 env: module_env.clone(),
259 source_dir: source_dir.clone(),
260 module_functions: Some(Rc::clone(®istry)),
261 module_state: Some(Rc::clone(&module_state)),
262 });
263 registry
264 .borrow_mut()
265 .insert(name.clone(), Rc::clone(&closure));
266 self.env
267 .define(name, VmValue::Closure(Rc::clone(&closure)), false)?;
268 module_state.borrow_mut().define(
275 name,
276 VmValue::Closure(Rc::clone(&closure)),
277 false,
278 )?;
279 functions.insert(name.clone(), Rc::clone(&closure));
280 if *is_pub {
281 public_names.insert(name.clone());
282 }
283 }
284
285 self.env = caller_env;
286 self.source_dir = old_source_dir;
287
288 Ok(LoadedModule {
289 functions,
290 public_names,
291 })
292 })
293 }
294
295 pub async fn load_module_exports(
298 &mut self,
299 path: &Path,
300 ) -> Result<BTreeMap<String, Rc<VmClosure>>, VmError> {
301 let path_str = path.to_string_lossy().into_owned();
302 self.execute_import(&path_str, None).await?;
303
304 let mut file_path = if path.is_absolute() {
305 path.to_path_buf()
306 } else {
307 self.source_dir
308 .clone()
309 .unwrap_or_else(|| PathBuf::from("."))
310 .join(path)
311 };
312 if !file_path.exists() && file_path.extension().is_none() {
313 file_path.set_extension("harn");
314 }
315
316 let canonical = file_path
317 .canonicalize()
318 .unwrap_or_else(|_| file_path.clone());
319 let loaded = self.module_cache.get(&canonical).cloned().ok_or_else(|| {
320 VmError::Runtime(format!(
321 "Import error: failed to cache loaded module '{}'",
322 canonical.display()
323 ))
324 })?;
325
326 let export_names: Vec<String> = if loaded.public_names.is_empty() {
327 loaded.functions.keys().cloned().collect()
328 } else {
329 loaded.public_names.iter().cloned().collect()
330 };
331
332 let mut exports = BTreeMap::new();
333 for name in export_names {
334 let Some(closure) = loaded.functions.get(&name) else {
335 return Err(VmError::Runtime(format!(
336 "Import error: exported function '{name}' is missing from {}",
337 canonical.display()
338 )));
339 };
340 exports.insert(name, Rc::clone(closure));
341 }
342
343 Ok(exports)
344 }
345}