Skip to main content

aver/interpreter/
core.rs

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