Skip to main content

object/
environment.rs

1use crate::Object;
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6pub type Env = Rc<RefCell<Environment>>;
7
8#[derive(Debug, Default, Eq, Clone, PartialEq)]
9pub struct Environment {
10    /// Copy-on-write: a closure `snapshot` shares this map with the live frame,
11    /// and whichever side writes next (`set`) copies it first. Creating a
12    /// closure therefore costs one `Rc` clone per frame, not a copy of every
13    /// binding in scope.
14    store: Rc<HashMap<String, Rc<Object>>>,
15    outer: Option<Env>,
16}
17
18impl Environment {
19    pub fn new_enclosed_environment(outer: &Env) -> Self {
20        return Environment {
21            outer: Some(Rc::clone(outer)),
22            ..Default::default()
23        };
24    }
25
26    pub fn get(&self, name: &str) -> Option<Rc<Object>> {
27        match self.store.get(name) {
28            Some(obj) => Some(Rc::clone(obj)),
29            None => {
30                if let Some(outer) = &self.outer {
31                    return outer.borrow().get(name);
32                } else {
33                    return None;
34                }
35            }
36        }
37    }
38
39    pub fn set(&mut self, name: String, val: Rc<Object>) {
40        // `make_mut` only copies the map while a snapshot still shares it.
41        Rc::make_mut(&mut self.store).insert(name, val);
42    }
43
44    /// Capture the bindings visible at a declaration site.
45    ///
46    /// Values remain shared, and so are the frames' maps until either side
47    /// writes again; a later `let` with the same name therefore cannot rewrite
48    /// what an existing closure sees, without copying every binding up front.
49    pub fn snapshot(&self) -> Self {
50        return Self {
51            store: Rc::clone(&self.store),
52            outer: self
53                .outer
54                .as_ref()
55                .map(|outer| Rc::new(RefCell::new(outer.borrow().snapshot()))),
56        };
57    }
58
59    pub fn visible_names(&self) -> Vec<String> {
60        let mut names = self
61            .outer
62            .as_ref()
63            .map(|outer| outer.borrow().visible_names())
64            .unwrap_or_default();
65        names.extend(self.store.keys().cloned());
66        names
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn snapshot_shares_frames_until_either_side_writes() {
76        let global = Rc::new(RefCell::new(Environment::default()));
77        global
78            .borrow_mut()
79            .set("x".to_string(), Rc::new(Object::Integer(1)));
80        let mut frame = Environment::new_enclosed_environment(&global);
81        frame.set("y".to_string(), Rc::new(Object::Integer(2)));
82
83        let snapshot = frame.snapshot();
84        assert!(Rc::ptr_eq(&snapshot.store, &frame.store));
85        let snapshot_outer = snapshot.outer.as_ref().unwrap();
86        assert!(Rc::ptr_eq(&snapshot_outer.borrow().store, &global.borrow().store));
87
88        // A later re-`let` on the live side copies that frame only; the
89        // snapshot keeps reading the binding it captured.
90        global
91            .borrow_mut()
92            .set("x".to_string(), Rc::new(Object::Integer(10)));
93        frame.set("y".to_string(), Rc::new(Object::Integer(20)));
94        assert_eq!(snapshot.get("x"), Some(Rc::new(Object::Integer(1))));
95        assert_eq!(snapshot.get("y"), Some(Rc::new(Object::Integer(2))));
96        assert_eq!(frame.get("x"), Some(Rc::new(Object::Integer(10))));
97        assert_eq!(frame.get("y"), Some(Rc::new(Object::Integer(20))));
98        assert!(!Rc::ptr_eq(&snapshot.store, &frame.store));
99    }
100}