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 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 Rc::make_mut(&mut self.store).insert(name, val);
42 }
43
44 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 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}