1use super::*;
2
3impl Default for Interpreter {
4 fn default() -> Self {
5 Self::new()
6 }
7}
8
9impl Interpreter {
10 pub fn new() -> Self {
11 let mut arena = Arena::new();
12 let mut global: HashMap<String, NanValue> = HashMap::new();
13
14 args::register_nv(&mut global, &mut arena);
16 console::register_nv(&mut global, &mut arena);
17 http::register_nv(&mut global, &mut arena);
18 http_server::register_nv(&mut global, &mut arena);
19 disk::register_nv(&mut global, &mut arena);
20 env::register_nv(&mut global, &mut arena);
21 random::register_nv(&mut global, &mut arena);
22 tcp::register_nv(&mut global, &mut arena);
23 #[cfg(feature = "terminal")]
24 terminal::register_nv(&mut global, &mut arena);
25 time::register_nv(&mut global, &mut arena);
26 bool::register_nv(&mut global, &mut arena);
27 int::register_nv(&mut global, &mut arena);
28 float::register_nv(&mut global, &mut arena);
29 string::register_nv(&mut global, &mut arena);
30 list::register_nv(&mut global, &mut arena);
31 map::register_nv(&mut global, &mut arena);
32 vector::register_nv(&mut global, &mut arena);
33 char::register_nv(&mut global, &mut arena);
34 byte::register_nv(&mut global, &mut arena);
35
36 {
38 use std::sync::Arc as Rc;
39 let mut members: Vec<(Rc<str>, NanValue)> = Vec::new();
40 let ok_idx = arena.push_builtin("__ctor:Result.Ok");
41 members.push((Rc::from("Ok"), NanValue::new_builtin(ok_idx)));
42 let err_idx = arena.push_builtin("__ctor:Result.Err");
43 members.push((Rc::from("Err"), NanValue::new_builtin(err_idx)));
44 for (name, builtin_name) in result::extra_members() {
45 let idx = arena.push_builtin(&builtin_name);
46 members.push((Rc::from(name), NanValue::new_builtin(idx)));
47 }
48 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
49 name: Rc::from("Result"),
50 members,
51 });
52 global.insert("Result".to_string(), NanValue::new_namespace(ns_idx));
53 }
54 {
56 use std::sync::Arc as Rc;
57 let mut members: Vec<(Rc<str>, NanValue)> = Vec::new();
58 let some_idx = arena.push_builtin("__ctor:Option.Some");
59 members.push((Rc::from("Some"), NanValue::new_builtin(some_idx)));
60 members.push((Rc::from("None"), NanValue::NONE));
61 for (name, builtin_name) in option::extra_members() {
62 let idx = arena.push_builtin(&builtin_name);
63 members.push((Rc::from(name), NanValue::new_builtin(idx)));
64 }
65 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
66 name: Rc::from("Option"),
67 members,
68 });
69 global.insert("Option".to_string(), NanValue::new_namespace(ns_idx));
70 }
71
72 let mut record_schemas = HashMap::new();
73 record_schemas.insert(
74 "HttpResponse".to_string(),
75 vec![
76 "status".to_string(),
77 "body".to_string(),
78 "headers".to_string(),
79 ],
80 );
81 record_schemas.insert(
82 "HttpRequest".to_string(),
83 vec![
84 "method".to_string(),
85 "path".to_string(),
86 "body".to_string(),
87 "headers".to_string(),
88 ],
89 );
90 record_schemas.insert(
91 "Header".to_string(),
92 vec!["name".to_string(), "value".to_string()],
93 );
94 record_schemas.insert(
95 "Tcp.Connection".to_string(),
96 vec!["id".to_string(), "host".to_string(), "port".to_string()],
97 );
98 #[cfg(feature = "terminal")]
99 record_schemas.insert(
100 "Terminal.Size".to_string(),
101 vec!["width".to_string(), "height".to_string()],
102 );
103
104 Interpreter {
105 env: vec![EnvFrame::Owned(global)],
106 env_base: 1,
107 arena,
108 module_cache: HashMap::new(),
109 record_schemas,
110 call_stack: Vec::new(),
111 active_local_slots: None,
112 memo_fns: HashSet::new(),
113 memo_cache: HashMap::new(),
114 replay_state: EffectReplayState::default(),
115 recording_sink: None,
116 verify_match_coverage: None,
117 runtime_policy: None,
118 cli_args: Vec::new(),
119 last_call_line: 0,
120 }
121 }
122
123 pub fn execution_mode(&self) -> ExecutionMode {
124 match self.replay_state.mode() {
125 EffectReplayMode::Normal => ExecutionMode::Normal,
126 EffectReplayMode::Record => ExecutionMode::Record,
127 EffectReplayMode::Replay => ExecutionMode::Replay,
128 }
129 }
130
131 pub fn set_execution_mode_normal(&mut self) {
132 self.replay_state.set_normal();
133 }
134
135 pub fn start_recording(&mut self) {
136 self.replay_state.start_recording();
137 }
138
139 pub fn start_replay(&mut self, effects: Vec<EffectRecord>, validate_args: bool) {
140 self.replay_state.start_replay(effects, validate_args);
141 }
142
143 pub fn take_recorded_effects(&mut self) -> Vec<EffectRecord> {
144 self.replay_state.take_recorded_effects()
145 }
146
147 pub fn replay_progress(&self) -> (usize, usize) {
148 self.replay_state.replay_progress()
149 }
150
151 pub fn args_diff_count(&self) -> usize {
152 self.replay_state.args_diff_count()
153 }
154
155 pub fn ensure_replay_consumed(&self) -> Result<(), RuntimeError> {
156 self.replay_state
157 .ensure_replay_consumed()
158 .map_err(|err| match err {
159 crate::replay::ReplayFailure::Unconsumed { remaining } => {
160 RuntimeError::ReplayUnconsumed { remaining }
161 }
162 other => RuntimeError::Error(format!("invalid replay state: {:?}", other)),
163 })
164 }
165
166 pub fn configure_recording_sink(&mut self, cfg: RecordingConfig) {
167 self.recording_sink = Some(RecordingSink {
168 path: cfg.path,
169 request_id: cfg.request_id,
170 timestamp: cfg.timestamp,
171 program_file: cfg.program_file,
172 module_root: cfg.module_root,
173 entry_fn: cfg.entry_fn,
174 input: cfg.input,
175 });
176 }
177
178 pub fn recording_sink_path(&self) -> Option<std::path::PathBuf> {
179 self.recording_sink.as_ref().map(|s| s.path.clone())
180 }
181
182 pub fn persist_recording_snapshot(&self, output: RecordedOutcome) -> Result<(), RuntimeError> {
183 let Some(sink) = &self.recording_sink else {
184 return Ok(());
185 };
186
187 let recording = SessionRecording {
188 schema_version: 1,
189 request_id: sink.request_id.clone(),
190 timestamp: sink.timestamp.clone(),
191 program_file: sink.program_file.clone(),
192 module_root: sink.module_root.clone(),
193 entry_fn: sink.entry_fn.clone(),
194 input: sink.input.clone(),
195 effects: self.replay_state.recorded_effects().to_vec(),
196 output,
197 };
198
199 let json = session_recording_to_string_pretty(&recording);
200 std::fs::write(&sink.path, json).map_err(|e| {
201 RuntimeError::Error(format!(
202 "Cannot write recording '{}': {}",
203 sink.path.display(),
204 e
205 ))
206 })?;
207 Ok(())
208 }
209
210 pub fn enable_memo(&mut self, fns: HashSet<String>) {
212 self.memo_fns = fns;
213 }
214
215 pub fn set_runtime_policy(&mut self, config: crate::config::ProjectConfig) {
218 self.runtime_policy = Some(config);
219 }
220
221 pub fn set_cli_args(&mut self, args: Vec<String>) {
223 self.cli_args = args;
224 }
225
226 pub(super) fn check_runtime_policy(
229 &self,
230 name: &str,
231 args: &[Value],
232 ) -> Result<(), RuntimeError> {
233 if self.execution_mode() == ExecutionMode::Replay {
234 return Ok(());
235 }
236 let Some(policy) = &self.runtime_policy else {
237 return Ok(());
238 };
239
240 if name.starts_with("Http.") {
241 if let Some(Value::Str(url)) = args.first() {
242 policy
243 .check_http_host(name, url)
244 .map_err(RuntimeError::Error)?;
245 }
246 } else if name.starts_with("Disk.") {
247 if let Some(Value::Str(path)) = args.first() {
248 policy
249 .check_disk_path(name, path)
250 .map_err(RuntimeError::Error)?;
251 }
252 } else if name.starts_with("Env.")
253 && let Some(Value::Str(key)) = args.first()
254 {
255 policy
256 .check_env_key(name, key)
257 .map_err(RuntimeError::Error)?;
258 }
259
260 Ok(())
261 }
262
263 pub fn start_verify_match_coverage(&mut self, fn_name: &str) {
264 let Ok(fn_val) = self.lookup(fn_name) else {
265 self.verify_match_coverage = None;
266 return;
267 };
268 let Value::Fn(function) = fn_val else {
269 self.verify_match_coverage = None;
270 return;
271 };
272
273 let mut expected = std::collections::BTreeMap::new();
274 Self::collect_match_sites_from_fn_body(function.body.as_ref(), &mut expected);
275 if expected.is_empty() {
276 self.verify_match_coverage = None;
277 return;
278 }
279
280 self.verify_match_coverage = Some(VerifyMatchCoverageTracker {
281 target_fn: fn_name.to_string(),
282 expected_arms: expected,
283 visited_arms: HashMap::new(),
284 });
285 }
286
287 pub fn finish_verify_match_coverage(&mut self) -> Vec<VerifyMatchCoverageMiss> {
288 let Some(tracker) = self.verify_match_coverage.take() else {
289 return vec![];
290 };
291
292 let mut misses = Vec::new();
293 for ((line, arm_count), expected_total) in tracker.expected_arms {
294 let visited = tracker.visited_arms.get(&(line, arm_count));
295 let mut missing = Vec::new();
296 for arm_idx in 0..expected_total {
297 let covered = visited.is_some_and(|set| set.contains(&arm_idx));
298 if !covered {
299 missing.push(arm_idx);
300 }
301 }
302 if !missing.is_empty() {
303 misses.push(VerifyMatchCoverageMiss {
304 line,
305 total_arms: expected_total,
306 missing_arms: missing,
307 });
308 }
309 }
310 misses
311 }
312
313 pub(super) fn note_verify_match_arm(&mut self, line: usize, arm_count: usize, arm_idx: usize) {
314 let Some(tracker) = self.verify_match_coverage.as_mut() else {
315 return;
316 };
317 let Some(frame) = self.call_stack.last() else {
318 return;
319 };
320 if frame.name.as_str() != tracker.target_fn {
321 return;
322 }
323 let key = (line, arm_count);
324 if !tracker.expected_arms.contains_key(&key) {
325 return;
326 }
327 tracker.visited_arms.entry(key).or_default().insert(arm_idx);
328 }
329
330 fn collect_match_sites_from_fn_body(
331 body: &FnBody,
332 out: &mut std::collections::BTreeMap<MatchSiteKey, usize>,
333 ) {
334 for stmt in body.stmts() {
335 Self::collect_match_sites_from_stmt(stmt, out);
336 }
337 }
338
339 fn collect_match_sites_from_stmt(
340 stmt: &Stmt,
341 out: &mut std::collections::BTreeMap<MatchSiteKey, usize>,
342 ) {
343 match stmt {
344 Stmt::Binding(_, _, spanned_expr) | Stmt::Expr(spanned_expr) => {
345 Self::collect_match_sites_from_spanned(spanned_expr, out);
346 }
347 }
348 }
349
350 fn collect_match_sites_from_spanned(
351 spanned: &Spanned<Expr>,
352 out: &mut std::collections::BTreeMap<MatchSiteKey, usize>,
353 ) {
354 let line = spanned.line;
355 match &spanned.node {
356 Expr::Match { subject, arms } => {
357 out.insert((line, arms.len()), arms.len());
358 Self::collect_match_sites_from_spanned(subject, out);
359 for arm in arms {
360 Self::collect_match_sites_from_spanned(&arm.body, out);
361 }
362 }
363 Expr::FnCall(fn_expr, args) => {
364 Self::collect_match_sites_from_spanned(fn_expr, out);
365 for arg in args {
366 Self::collect_match_sites_from_spanned(arg, out);
367 }
368 }
369 Expr::BinOp(_, left, right) => {
370 Self::collect_match_sites_from_spanned(left, out);
371 Self::collect_match_sites_from_spanned(right, out);
372 }
373 Expr::Attr(obj, _) => {
374 Self::collect_match_sites_from_spanned(obj, out);
375 }
376 Expr::ErrorProp(inner) => {
377 Self::collect_match_sites_from_spanned(inner, out);
378 }
379 Expr::Constructor(_, maybe_arg) => {
380 if let Some(arg) = maybe_arg {
381 Self::collect_match_sites_from_spanned(arg, out);
382 }
383 }
384 Expr::InterpolatedStr(parts) => {
385 for part in parts {
386 if let StrPart::Parsed(expr) = part {
387 Self::collect_match_sites_from_spanned(expr, out);
388 }
389 }
390 }
391 Expr::List(items) | Expr::Tuple(items) | Expr::IndependentProduct(items, _) => {
392 for item in items {
393 Self::collect_match_sites_from_spanned(item, out);
394 }
395 }
396 Expr::MapLiteral(entries) => {
397 for (key, value) in entries {
398 Self::collect_match_sites_from_spanned(key, out);
399 Self::collect_match_sites_from_spanned(value, out);
400 }
401 }
402 Expr::RecordCreate { fields, .. } => {
403 for (_, expr) in fields {
404 Self::collect_match_sites_from_spanned(expr, out);
405 }
406 }
407 Expr::RecordUpdate { base, updates, .. } => {
408 Self::collect_match_sites_from_spanned(base, out);
409 for (_, expr) in updates {
410 Self::collect_match_sites_from_spanned(expr, out);
411 }
412 }
413 Expr::TailCall(boxed) => {
414 for arg in &boxed.1 {
415 Self::collect_match_sites_from_spanned(arg, out);
416 }
417 }
418 Expr::Literal(_) | Expr::Ident(_) | Expr::Resolved(_) => {}
419 }
420 }
421
422 pub(super) fn push_env(&mut self, frame: EnvFrame) {
426 self.env.push(frame);
427 }
428
429 pub(super) fn pop_env(&mut self) {
430 if self.env.len() > 1 {
431 self.env.pop();
432 }
433 }
434
435 pub(super) fn last_owned_scope_mut(
436 &mut self,
437 ) -> Result<&mut HashMap<String, NanValue>, RuntimeError> {
438 let frame = self
439 .env
440 .last_mut()
441 .ok_or_else(|| RuntimeError::Error("No active scope".to_string()))?;
442 match frame {
443 EnvFrame::Owned(scope) => Ok(scope),
444 EnvFrame::Shared(_) | EnvFrame::Slots(_) => Err(RuntimeError::Error(
445 "Cannot define name in non-owned frame".to_string(),
446 )),
447 }
448 }
449
450 pub(super) fn lookup_nv(&self, name: &str) -> Result<NanValue, RuntimeError> {
452 let env = &self.env;
453 let mut i = env.len();
454 let base = self.env_base;
455 while i > base {
456 i -= 1;
457 match &env[i] {
458 EnvFrame::Owned(scope) => {
459 if let Some(v) = scope.get(name) {
460 return Ok(*v);
461 }
462 }
463 EnvFrame::Shared(scope) => {
464 if let Some(v) = scope.get(name) {
465 return Ok(*v);
466 }
467 }
468 EnvFrame::Slots(_) => {}
469 }
470 }
471 match &env[0] {
472 EnvFrame::Owned(scope) => scope.get(name).copied(),
473 EnvFrame::Shared(scope) => scope.get(name).copied(),
474 EnvFrame::Slots(_) => None,
475 }
476 .ok_or_else(|| RuntimeError::Error(format!("Undefined variable: '{}'", name)))
477 }
478
479 pub(super) fn lookup_path_nv(&self, path: &str) -> Result<NanValue, RuntimeError> {
480 let mut parts = path.split('.').filter(|part| !part.is_empty());
481 let Some(first) = parts.next() else {
482 return Err(RuntimeError::Error("Empty path lookup".to_string()));
483 };
484
485 let mut current = self.lookup_nv(first)?;
486 for part in parts {
487 if !current.is_namespace() {
488 return Err(RuntimeError::Error(format!(
489 "Cannot resolve '{}': '{}' is not a namespace",
490 path, part
491 )));
492 }
493 let (_, members) = self.arena.get_namespace(current.symbol_index());
494 let Some((_, next)) = members.iter().find(|(name, _)| name.as_ref() == part) else {
495 return Err(RuntimeError::Error(format!(
496 "Undefined variable: '{}'",
497 path
498 )));
499 };
500 current = *next;
501 }
502
503 Ok(current)
504 }
505
506 pub(super) fn global_scope_clone(&self) -> Result<HashMap<String, NanValue>, RuntimeError> {
507 let frame = self
508 .env
509 .first()
510 .ok_or_else(|| RuntimeError::Error("No global scope".to_string()))?;
511 match frame {
512 EnvFrame::Owned(scope) => Ok(scope.clone()),
513 EnvFrame::Shared(scope) => Ok((**scope).clone()),
514 EnvFrame::Slots(_) => Err(RuntimeError::Error(
515 "Invalid global scope frame: Slots".to_string(),
516 )),
517 }
518 }
519
520 pub fn lookup(&self, name: &str) -> Result<Value, RuntimeError> {
522 let nv = self.lookup_nv(name)?;
523 Ok(nv.to_value(&self.arena))
524 }
525
526 pub fn define(&mut self, name: String, val: Value) {
528 let nv = NanValue::from_value(&val, &mut self.arena);
529 if let Ok(scope) = self.last_owned_scope_mut() {
530 scope.insert(name, nv);
531 }
532 }
533
534 pub(super) fn define_nv(&mut self, name: String, nv: NanValue) {
536 if let Ok(scope) = self.last_owned_scope_mut() {
537 scope.insert(name, nv);
538 }
539 }
540
541 fn alias_exposed_type_namespaces(&mut self, module_val: &Value) {
542 let Value::Namespace { members, .. } = module_val else {
543 return;
544 };
545 for (name, member) in members {
546 if matches!(member, Value::Namespace { .. }) {
547 self.define(name.clone(), member.clone());
548 }
549 }
550 }
551
552 pub(super) fn lookup_slot(&self, slot: u16) -> Result<NanValue, RuntimeError> {
554 let idx = self.env.len() - 1;
555 match &self.env[idx] {
556 EnvFrame::Slots(v) => Ok(v[slot as usize]),
557 _ => Err(RuntimeError::Error(
558 "Resolved lookup on non-Slots frame".to_string(),
559 )),
560 }
561 }
562
563 pub(super) fn define_slot(&mut self, slot: u16, val: NanValue) {
565 let idx = self.env.len() - 1;
566 if let EnvFrame::Slots(v) = &mut self.env[idx] {
567 v[slot as usize] = val;
568 }
569 }
570
571 pub fn define_module_path(&mut self, path: &str, val: Value) -> Result<(), RuntimeError> {
572 let alias_source = val.clone();
573 let parts: Vec<&str> = path.split('.').filter(|s| !s.is_empty()).collect();
574 if parts.is_empty() {
575 return Err(RuntimeError::Error("Empty module path".to_string()));
576 }
577 if parts.len() == 1 {
578 self.define(parts[0].to_string(), val);
579 self.alias_exposed_type_namespaces(&alias_source);
580 return Ok(());
581 }
582
583 let head = parts[0];
584 let tail = &parts[1..];
585
586 let result = {
587 let scope = self.last_owned_scope_mut()?;
588 if let Some(existing_nv) = scope.remove(head) {
589 let existing = existing_nv.to_value(&self.arena);
590 match existing {
591 Value::Namespace { name, mut members } => {
592 Self::insert_namespace_path(&mut members, tail, val)?;
593 let ns = Value::Namespace { name, members };
594 let nv = NanValue::from_value(&ns, &mut self.arena);
595 let scope = self.last_owned_scope_mut()?;
597 scope.insert(head.to_string(), nv);
598 Ok(())
599 }
600 _ => {
601 let scope2 = self.last_owned_scope_mut()?;
603 scope2.insert(head.to_string(), existing_nv);
604 Err(RuntimeError::Error(format!(
605 "Cannot mount module '{}': '{}' is not a namespace",
606 parts.join("."),
607 head
608 )))
609 }
610 }
611 } else {
612 let mut members = HashMap::new();
613 Self::insert_namespace_path(&mut members, tail, val)?;
614 let ns = Value::Namespace {
615 name: head.to_string(),
616 members,
617 };
618 let nv = NanValue::from_value(&ns, &mut self.arena);
619 let scope = self.last_owned_scope_mut()?;
620 scope.insert(head.to_string(), nv);
621 Ok(())
622 }
623 };
624
625 if result.is_ok() {
626 self.alias_exposed_type_namespaces(&alias_source);
627 }
628 result
629 }
630
631 pub(super) fn insert_namespace_path(
632 scope: &mut HashMap<String, Value>,
633 parts: &[&str],
634 val: Value,
635 ) -> Result<(), RuntimeError> {
636 if parts.len() == 1 {
637 scope.insert(parts[0].to_string(), val);
638 return Ok(());
639 }
640
641 let head = parts[0];
642 let tail = &parts[1..];
643
644 if let Some(existing) = scope.remove(head) {
645 match existing {
646 Value::Namespace { name, mut members } => {
647 Self::insert_namespace_path(&mut members, tail, val)?;
648 scope.insert(head.to_string(), Value::Namespace { name, members });
649 Ok(())
650 }
651 _ => Err(RuntimeError::Error(format!(
652 "Cannot mount module '{}': '{}' is not a namespace",
653 parts.join("."),
654 head
655 ))),
656 }
657 } else {
658 let mut members = HashMap::new();
659 Self::insert_namespace_path(&mut members, tail, val)?;
660 scope.insert(
661 head.to_string(),
662 Value::Namespace {
663 name: head.to_string(),
664 members,
665 },
666 );
667 Ok(())
668 }
669 }
670
671 pub(super) fn module_cache_key(path: &Path) -> String {
672 canonicalize_path(path).to_string_lossy().to_string()
673 }
674
675 pub(super) fn module_decl(items: &[TopLevel]) -> Option<&Module> {
676 items.iter().find_map(|item| {
677 if let TopLevel::Module(m) = item {
678 Some(m)
679 } else {
680 None
681 }
682 })
683 }
684
685 pub(super) fn exposed_set(items: &[TopLevel]) -> Option<HashSet<String>> {
686 Self::module_decl(items).and_then(|m| {
687 if m.exposes.is_empty() {
688 None
689 } else {
690 Some(m.exposes.iter().cloned().collect())
691 }
692 })
693 }
694
695 pub(super) fn cycle_display(loading: &[String], next: &str) -> String {
696 let mut chain = loading
697 .iter()
698 .map(|key| {
699 Path::new(key)
700 .file_stem()
701 .and_then(|s| s.to_str())
702 .unwrap_or(key)
703 .to_string()
704 })
705 .collect::<Vec<_>>();
706 chain.push(
707 Path::new(next)
708 .file_stem()
709 .and_then(|s| s.to_str())
710 .unwrap_or(next)
711 .to_string(),
712 );
713 chain.join(" -> ")
714 }
715
716 pub fn load_module(
717 &mut self,
718 name: &str,
719 base_dir: &str,
720 loading: &mut Vec<String>,
721 loading_set: &mut HashSet<String>,
722 ) -> Result<Value, RuntimeError> {
723 let path = find_module_file(name, base_dir).ok_or_else(|| {
724 RuntimeError::Error(format!("Module '{}' not found in '{}'", name, base_dir))
725 })?;
726 let cache_key = Self::module_cache_key(&path);
727
728 if let Some(cached) = self.module_cache.get(&cache_key) {
729 return Ok(cached.clone());
730 }
731
732 if loading_set.contains(&cache_key) {
733 return Err(RuntimeError::Error(format!(
734 "Circular import: {}",
735 Self::cycle_display(loading, &cache_key)
736 )));
737 }
738
739 loading.push(cache_key.clone());
740 loading_set.insert(cache_key.clone());
741 let result = (|| -> Result<Value, RuntimeError> {
742 let src = std::fs::read_to_string(&path).map_err(|e| {
743 RuntimeError::Error(format!("Cannot read '{}': {}", path.display(), e))
744 })?;
745 let mut items = parse_source(&src).map_err(|e| {
746 RuntimeError::Error(format!("Parse error in '{}': {}", path.display(), e))
747 })?;
748 require_module_declaration(&items, &path.to_string_lossy())
749 .map_err(RuntimeError::Error)?;
750 crate::resolver::resolve_program(&mut items);
751
752 if let Some(module) = Self::module_decl(&items) {
753 let expected = name.rsplit('.').next().unwrap_or(name);
754 if module.name != expected {
755 return Err(RuntimeError::Error(format!(
756 "Module name mismatch: expected '{}' (from '{}'), found '{}' in '{}'",
757 expected,
758 name,
759 module.name,
760 path.display()
761 )));
762 }
763 }
764
765 let mut sub = Interpreter::new();
766
767 if let Some(module) = Self::module_decl(&items) {
768 for dep_name in &module.depends {
769 let dep_ns = self.load_module(dep_name, base_dir, loading, loading_set)?;
770 sub.define_module_path(dep_name, dep_ns)?;
771 }
772 }
773
774 for item in &items {
775 if let TopLevel::TypeDef(td) = item {
776 sub.register_type_def(td);
777 }
778 }
779 for item in &items {
780 if let TopLevel::FnDef(fd) = item {
781 sub.exec_fn_def(fd)?;
782 }
783 }
784 let sub_globals_nv = sub.global_scope_clone()?;
787 let mut reencoded_globals: HashMap<String, NanValue> =
788 HashMap::with_capacity(sub_globals_nv.len());
789 for (k, nv) in &sub_globals_nv {
790 let old_val = nv.to_value(&sub.arena);
791 let new_nv = NanValue::from_value(&old_val, &mut self.arena);
792 reencoded_globals.insert(k.clone(), new_nv);
793 }
794 let module_globals = Rc::new(reencoded_globals);
795
796 let exposed = Self::exposed_set(&items);
797 let opaque_set: HashSet<String> = Self::module_decl(&items)
798 .map(|m| m.exposes_opaque.iter().cloned().collect())
799 .unwrap_or_default();
800 let mut members = HashMap::new();
801 for item in &items {
802 match item {
803 TopLevel::FnDef(fd) => {
804 let include = match &exposed {
805 Some(set) => set.contains(&fd.name),
806 None => !fd.name.starts_with('_'),
807 };
808 if include {
809 let mut val = sub.lookup(&fd.name).map_err(|_| {
810 RuntimeError::Error(format!(
811 "Failed to export '{}.{}'",
812 name, fd.name
813 ))
814 })?;
815 if let Value::Fn(function) = &mut val {
816 Rc::make_mut(function).home_globals =
817 Some(Rc::clone(&module_globals));
818 }
819 members.insert(fd.name.clone(), val);
820 }
821 }
822 TopLevel::TypeDef(TypeDef::Sum {
823 name: type_name, ..
824 }) => {
825 if opaque_set.contains(type_name) {
827 continue;
828 }
829 let include = match &exposed {
830 Some(set) => set.contains(type_name),
831 None => !type_name.starts_with('_'),
832 };
833 if include {
834 let val = sub.lookup(type_name).map_err(|_| {
835 RuntimeError::Error(format!(
836 "Failed to export '{}.{}'",
837 name, type_name
838 ))
839 })?;
840 members.insert(type_name.clone(), val);
841 }
842 }
843 _ => {}
844 }
845 }
846
847 Ok(Value::Namespace {
848 name: name.to_string(),
849 members,
850 })
851 })();
852 loading.pop();
853 loading_set.remove(&cache_key);
854
855 match result {
856 Ok(ns) => {
857 self.module_cache.insert(cache_key, ns.clone());
858 Ok(ns)
859 }
860 Err(e) => Err(e),
861 }
862 }
863}