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