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