Skip to main content

aver/interpreter/
core.rs

1use super::*;
2
3impl Interpreter {
4    pub fn new() -> Self {
5        let mut global = HashMap::new();
6
7        console::register(&mut global);
8        http::register(&mut global);
9        http_server::register(&mut global);
10        disk::register(&mut global);
11        tcp::register(&mut global);
12        int::register(&mut global);
13        float::register(&mut global);
14        string::register(&mut global);
15        list::register(&mut global);
16        map::register(&mut global);
17        char::register(&mut global);
18        byte::register(&mut global);
19
20        // Result and Option namespaces — constructors for Ok/Err/Some/None
21        {
22            let mut members = HashMap::new();
23            members.insert(
24                "Ok".to_string(),
25                Value::Builtin("__ctor:Result.Ok".to_string()),
26            );
27            members.insert(
28                "Err".to_string(),
29                Value::Builtin("__ctor:Result.Err".to_string()),
30            );
31            for (name, builtin_name) in result::extra_members() {
32                members.insert(name.to_string(), Value::Builtin(builtin_name));
33            }
34            global.insert(
35                "Result".to_string(),
36                Value::Namespace {
37                    name: "Result".to_string(),
38                    members,
39                },
40            );
41        }
42        {
43            let mut members = HashMap::new();
44            members.insert(
45                "Some".to_string(),
46                Value::Builtin("__ctor:Option.Some".to_string()),
47            );
48            members.insert("None".to_string(), Value::None);
49            for (name, builtin_name) in option::extra_members() {
50                members.insert(name.to_string(), Value::Builtin(builtin_name));
51            }
52            global.insert(
53                "Option".to_string(),
54                Value::Namespace {
55                    name: "Option".to_string(),
56                    members,
57                },
58            );
59        }
60
61        let rc_global = global
62            .into_iter()
63            .map(|(k, v)| (k, Rc::new(v)))
64            .collect::<HashMap<_, _>>();
65
66        let mut record_schemas = HashMap::new();
67        record_schemas.insert(
68            "HttpResponse".to_string(),
69            vec![
70                "status".to_string(),
71                "body".to_string(),
72                "headers".to_string(),
73            ],
74        );
75        record_schemas.insert(
76            "HttpRequest".to_string(),
77            vec![
78                "method".to_string(),
79                "path".to_string(),
80                "body".to_string(),
81                "headers".to_string(),
82            ],
83        );
84        record_schemas.insert(
85            "Header".to_string(),
86            vec!["name".to_string(), "value".to_string()],
87        );
88        record_schemas.insert(
89            "Tcp.Connection".to_string(),
90            vec!["id".to_string(), "host".to_string(), "port".to_string()],
91        );
92
93        Interpreter {
94            env: vec![EnvFrame::Owned(rc_global)],
95            module_cache: HashMap::new(),
96            record_schemas,
97            call_stack: Vec::new(),
98            effect_aliases: HashMap::new(),
99            active_local_slots: None,
100            memo_fns: HashSet::new(),
101            memo_cache: HashMap::new(),
102            execution_mode: ExecutionMode::Normal,
103            recorded_effects: Vec::new(),
104            replay_effects: Vec::new(),
105            replay_pos: 0,
106            validate_replay_args: false,
107            recording_sink: None,
108        }
109    }
110
111    pub fn execution_mode(&self) -> ExecutionMode {
112        self.execution_mode
113    }
114
115    pub fn set_execution_mode_normal(&mut self) {
116        self.execution_mode = ExecutionMode::Normal;
117        self.recorded_effects.clear();
118        self.replay_effects.clear();
119        self.replay_pos = 0;
120        self.validate_replay_args = false;
121    }
122
123    pub fn start_recording(&mut self) {
124        self.execution_mode = ExecutionMode::Record;
125        self.recorded_effects.clear();
126        self.replay_effects.clear();
127        self.replay_pos = 0;
128        self.validate_replay_args = false;
129    }
130
131    pub fn start_replay(&mut self, effects: Vec<EffectRecord>, validate_args: bool) {
132        self.execution_mode = ExecutionMode::Replay;
133        self.replay_effects = effects;
134        self.replay_pos = 0;
135        self.validate_replay_args = validate_args;
136        self.recorded_effects.clear();
137    }
138
139    pub fn take_recorded_effects(&mut self) -> Vec<EffectRecord> {
140        std::mem::take(&mut self.recorded_effects)
141    }
142
143    pub fn replay_progress(&self) -> (usize, usize) {
144        (self.replay_pos, self.replay_effects.len())
145    }
146
147    pub fn ensure_replay_consumed(&self) -> Result<(), RuntimeError> {
148        if self.execution_mode == ExecutionMode::Replay
149            && self.replay_pos < self.replay_effects.len()
150        {
151            return Err(RuntimeError::ReplayUnconsumed {
152                remaining: self.replay_effects.len() - self.replay_pos,
153            });
154        }
155        Ok(())
156    }
157
158    pub fn configure_recording_sink(
159        &mut self,
160        path: std::path::PathBuf,
161        request_id: String,
162        timestamp: String,
163        program_file: String,
164        module_root: String,
165        entry_fn: String,
166        input: JsonValue,
167    ) {
168        self.recording_sink = Some(RecordingSink {
169            path,
170            request_id,
171            timestamp,
172            program_file,
173            module_root,
174            entry_fn,
175            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.recorded_effects.clone(),
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    /// Mark a set of function names as eligible for auto-memoization.
212    pub fn enable_memo(&mut self, fns: HashSet<String>) {
213        self.memo_fns = fns;
214    }
215
216    /// Register a named effect set alias.
217    pub fn register_effect_set(&mut self, name: String, effects: Vec<String>) {
218        self.effect_aliases.insert(name, effects);
219    }
220
221    /// Expand effect names one level: aliases → concrete effect names.
222    pub(super) fn expand_effects(&self, effects: &[String]) -> Vec<String> {
223        let mut result = Vec::new();
224        for e in effects {
225            if let Some(expanded) = self.effect_aliases.get(e) {
226                result.extend(expanded.iter().cloned());
227            } else {
228                result.push(e.clone());
229            }
230        }
231        result
232    }
233
234    // -------------------------------------------------------------------------
235    // Environment management
236    // -------------------------------------------------------------------------
237    pub(super) fn push_env(&mut self, frame: EnvFrame) {
238        self.env.push(frame);
239    }
240
241    pub(super) fn pop_env(&mut self) {
242        if self.env.len() > 1 {
243            self.env.pop();
244        }
245    }
246
247    pub(super) fn last_owned_scope_mut(
248        &mut self,
249    ) -> Result<&mut HashMap<String, Rc<Value>>, RuntimeError> {
250        let frame = self
251            .env
252            .last_mut()
253            .ok_or_else(|| RuntimeError::Error("No active scope".to_string()))?;
254        match frame {
255            EnvFrame::Owned(scope) => Ok(scope),
256            EnvFrame::Shared(_) | EnvFrame::Slots(_) => Err(RuntimeError::Error(
257                "Cannot define name in non-owned frame".to_string(),
258            )),
259        }
260    }
261
262    pub(super) fn lookup_rc(&self, name: &str) -> Result<&Rc<Value>, RuntimeError> {
263        for frame in self.env.iter().rev() {
264            let found = match frame {
265                EnvFrame::Owned(scope) => scope.get(name),
266                EnvFrame::Shared(scope) => scope.get(name),
267                // Slots frames are indexed by slot, not by name — skip in name-based lookup
268                EnvFrame::Slots(_) => None,
269            };
270            if let Some(v) = found {
271                return Ok(v);
272            }
273        }
274        Err(RuntimeError::Error(format!(
275            "Undefined variable: '{}'",
276            name
277        )))
278    }
279
280    pub(super) fn global_scope_clone(&self) -> Result<HashMap<String, Rc<Value>>, RuntimeError> {
281        let frame = self
282            .env
283            .first()
284            .ok_or_else(|| RuntimeError::Error("No global scope".to_string()))?;
285        match frame {
286            EnvFrame::Owned(scope) => Ok(scope.clone()),
287            EnvFrame::Shared(scope) => Ok((**scope).clone()),
288            EnvFrame::Slots(_) => Err(RuntimeError::Error(
289                "Invalid global scope frame: Slots".to_string(),
290            )),
291        }
292    }
293
294    pub fn lookup(&self, name: &str) -> Result<Value, RuntimeError> {
295        self.lookup_rc(name).map(|rc| (**rc).clone())
296    }
297
298    pub fn define(&mut self, name: String, val: Value) {
299        if let Ok(scope) = self.last_owned_scope_mut() {
300            scope.insert(name, Rc::new(val));
301        }
302    }
303
304    /// O(1) slot-based variable lookup for resolved function bodies.
305    pub(super) fn lookup_slot(&self, slot: u16) -> Result<Value, RuntimeError> {
306        let idx = self.env.len() - 1;
307        match &self.env[idx] {
308            EnvFrame::Slots(v) => Ok(v[slot as usize].as_ref().clone()),
309            _ => {
310                // Fallback — shouldn't happen if resolver is correct
311                Err(RuntimeError::Error(
312                    "Resolved lookup on non-Slots frame".to_string(),
313                ))
314            }
315        }
316    }
317
318    /// Define a value in the current Slots frame at the given slot index.
319    pub(super) fn define_slot(&mut self, slot: u16, val: Value) {
320        let idx = self.env.len() - 1;
321        if let EnvFrame::Slots(v) = &mut self.env[idx] {
322            v[slot as usize] = Rc::new(val);
323        }
324    }
325
326    pub fn define_module_path(&mut self, path: &str, val: Value) -> Result<(), RuntimeError> {
327        let parts: Vec<&str> = path.split('.').filter(|s| !s.is_empty()).collect();
328        if parts.is_empty() {
329            return Err(RuntimeError::Error("Empty module path".to_string()));
330        }
331        if parts.len() == 1 {
332            self.define(parts[0].to_string(), val);
333            return Ok(());
334        }
335
336        let scope = self.last_owned_scope_mut()?;
337        let head = parts[0];
338        let tail = &parts[1..];
339
340        if let Some(rc_existing) = scope.remove(head) {
341            let existing = Rc::try_unwrap(rc_existing).unwrap_or_else(|rc| (*rc).clone());
342            match existing {
343                Value::Namespace { name, mut members } => {
344                    Self::insert_namespace_path(&mut members, tail, val)?;
345                    scope.insert(
346                        head.to_string(),
347                        Rc::new(Value::Namespace { name, members }),
348                    );
349                    Ok(())
350                }
351                _ => Err(RuntimeError::Error(format!(
352                    "Cannot mount module '{}': '{}' is not a namespace",
353                    parts.join("."),
354                    head
355                ))),
356            }
357        } else {
358            let mut members = HashMap::new();
359            Self::insert_namespace_path(&mut members, tail, val)?;
360            scope.insert(
361                head.to_string(),
362                Rc::new(Value::Namespace {
363                    name: head.to_string(),
364                    members,
365                }),
366            );
367            Ok(())
368        }
369    }
370
371    pub(super) fn insert_namespace_path(
372        scope: &mut HashMap<String, Value>,
373        parts: &[&str],
374        val: Value,
375    ) -> Result<(), RuntimeError> {
376        if parts.len() == 1 {
377            scope.insert(parts[0].to_string(), val);
378            return Ok(());
379        }
380
381        let head = parts[0];
382        let tail = &parts[1..];
383
384        if let Some(existing) = scope.remove(head) {
385            match existing {
386                Value::Namespace { name, mut members } => {
387                    Self::insert_namespace_path(&mut members, tail, val)?;
388                    scope.insert(head.to_string(), Value::Namespace { name, members });
389                    Ok(())
390                }
391                _ => Err(RuntimeError::Error(format!(
392                    "Cannot mount module '{}': '{}' is not a namespace",
393                    parts.join("."),
394                    head
395                ))),
396            }
397        } else {
398            let mut members = HashMap::new();
399            Self::insert_namespace_path(&mut members, tail, val)?;
400            scope.insert(
401                head.to_string(),
402                Value::Namespace {
403                    name: head.to_string(),
404                    members,
405                },
406            );
407            Ok(())
408        }
409    }
410
411    pub(super) fn module_cache_key(path: &Path) -> String {
412        canonicalize_path(path).to_string_lossy().to_string()
413    }
414
415    pub(super) fn module_decl(items: &[TopLevel]) -> Option<&Module> {
416        items.iter().find_map(|item| {
417            if let TopLevel::Module(m) = item {
418                Some(m)
419            } else {
420                None
421            }
422        })
423    }
424
425    pub(super) fn exposed_set(items: &[TopLevel]) -> Option<HashSet<String>> {
426        Self::module_decl(items).and_then(|m| {
427            if m.exposes.is_empty() {
428                None
429            } else {
430                Some(m.exposes.iter().cloned().collect())
431            }
432        })
433    }
434
435    pub(super) fn cycle_display(loading: &[String], next: &str) -> String {
436        let mut chain = loading
437            .iter()
438            .map(|key| {
439                Path::new(key)
440                    .file_stem()
441                    .and_then(|s| s.to_str())
442                    .unwrap_or(key)
443                    .to_string()
444            })
445            .collect::<Vec<_>>();
446        chain.push(
447            Path::new(next)
448                .file_stem()
449                .and_then(|s| s.to_str())
450                .unwrap_or(next)
451                .to_string(),
452        );
453        chain.join(" -> ")
454    }
455
456    pub fn load_module(
457        &mut self,
458        name: &str,
459        base_dir: &str,
460        loading: &mut Vec<String>,
461        loading_set: &mut HashSet<String>,
462    ) -> Result<Value, RuntimeError> {
463        let path = find_module_file(name, base_dir).ok_or_else(|| {
464            RuntimeError::Error(format!("Module '{}' not found in '{}'", name, base_dir))
465        })?;
466        let cache_key = Self::module_cache_key(&path);
467
468        if let Some(cached) = self.module_cache.get(&cache_key) {
469            return Ok(cached.clone());
470        }
471
472        if loading_set.contains(&cache_key) {
473            return Err(RuntimeError::Error(format!(
474                "Circular import: {}",
475                Self::cycle_display(loading, &cache_key)
476            )));
477        }
478
479        loading.push(cache_key.clone());
480        loading_set.insert(cache_key.clone());
481        let result = (|| -> Result<Value, RuntimeError> {
482            let src = std::fs::read_to_string(&path).map_err(|e| {
483                RuntimeError::Error(format!("Cannot read '{}': {}", path.display(), e))
484            })?;
485            let mut items = parse_source(&src).map_err(|e| {
486                RuntimeError::Error(format!("Parse error in '{}': {}", path.display(), e))
487            })?;
488            crate::resolver::resolve_program(&mut items);
489
490            if let Some(module) = Self::module_decl(&items) {
491                let expected = name.rsplit('.').next().unwrap_or(name);
492                if module.name != expected {
493                    return Err(RuntimeError::Error(format!(
494                        "Module name mismatch: expected '{}' (from '{}'), found '{}' in '{}'",
495                        expected,
496                        name,
497                        module.name,
498                        path.display()
499                    )));
500                }
501            }
502
503            let mut sub = Interpreter::new();
504
505            if let Some(module) = Self::module_decl(&items) {
506                for dep_name in &module.depends {
507                    let dep_ns = self.load_module(dep_name, base_dir, loading, loading_set)?;
508                    sub.define_module_path(dep_name, dep_ns)?;
509                }
510            }
511
512            for item in &items {
513                if let TopLevel::EffectSet { name, effects } = item {
514                    sub.register_effect_set(name.clone(), effects.clone());
515                }
516            }
517            for item in &items {
518                if let TopLevel::TypeDef(td) = item {
519                    sub.register_type_def(td);
520                }
521            }
522            for item in &items {
523                if let TopLevel::FnDef(fd) = item {
524                    sub.exec_fn_def(fd)?;
525                }
526            }
527            let module_globals = Rc::new(sub.global_scope_clone()?);
528
529            let exposed = Self::exposed_set(&items);
530            let mut members = HashMap::new();
531            for item in &items {
532                match item {
533                    TopLevel::FnDef(fd) => {
534                        let include = match &exposed {
535                            Some(set) => set.contains(&fd.name),
536                            None => !fd.name.starts_with('_'),
537                        };
538                        if include {
539                            let mut val = sub.lookup(&fd.name).map_err(|_| {
540                                RuntimeError::Error(format!(
541                                    "Failed to export '{}.{}'",
542                                    name, fd.name
543                                ))
544                            })?;
545                            if let Value::Fn { home_globals, .. } = &mut val {
546                                *home_globals = Some(Rc::clone(&module_globals));
547                            }
548                            members.insert(fd.name.clone(), val);
549                        }
550                    }
551                    TopLevel::TypeDef(TypeDef::Sum {
552                        name: type_name, ..
553                    }) => {
554                        let include = match &exposed {
555                            Some(set) => set.contains(type_name),
556                            None => !type_name.starts_with('_'),
557                        };
558                        if include {
559                            let val = sub.lookup(type_name).map_err(|_| {
560                                RuntimeError::Error(format!(
561                                    "Failed to export '{}.{}'",
562                                    name, type_name
563                                ))
564                            })?;
565                            members.insert(type_name.clone(), val);
566                        }
567                    }
568                    _ => {}
569                }
570            }
571
572            Ok(Value::Namespace {
573                name: name.to_string(),
574                members,
575            })
576        })();
577        loading.pop();
578        loading_set.remove(&cache_key);
579
580        match result {
581            Ok(ns) => {
582                self.module_cache.insert(cache_key, ns.clone());
583                Ok(ns)
584            }
585            Err(e) => Err(e),
586        }
587    }
588}