bebop_lang/lisp/
env.rs

1use crate::lisp::{builtin::init_builtins, Lval};
2use std::collections::HashMap;
3
4#[derive(Clone)]
5pub struct Lenv {
6    head: LinkedEnv,
7}
8
9type LinkedEnv = Option<Box<Env>>;
10pub type Lookup = HashMap<String, Lval>;
11
12#[derive(Clone, Debug)]
13pub struct Env {
14    lookup: Lookup,
15    parent: LinkedEnv,
16}
17
18impl Lenv {
19    pub fn new() -> Self {
20        Lenv { head: None }
21    }
22}
23
24impl Lenv {
25    pub fn push(&mut self, lookup: Lookup) {
26        let new_env = Box::new(Env {
27            lookup,
28            parent: self.head.take(),
29        });
30
31        self.head = Some(new_env);
32    }
33
34    pub fn pop(&mut self) -> Option<Lookup> {
35        self.head.take().map(|env| {
36            self.head = env.parent;
37            env.lookup
38        })
39    }
40
41    pub fn peek(&self) -> Option<&Lookup> {
42        self.head.as_ref().map(|env| &env.lookup)
43    }
44
45    pub fn peek_mut(&mut self) -> Option<&mut Lookup> {
46        self.head.as_mut().map(|env| &mut env.lookup)
47    }
48
49    pub fn iter(&self) -> Iter<'_> {
50        Iter {
51            next: self.head.as_deref(),
52        }
53    }
54
55    pub fn insert(&mut self, key: &str, lval: Lval) {
56        self.peek_mut()
57            .map(|node| node.insert(key.to_owned(), lval));
58    }
59
60    pub fn insert_last(&mut self, key: &str, lval: Lval) {
61        let mut i = self.head.as_mut();
62
63        while let Some(env) = i {
64            i = env.parent.as_mut();
65            if let None = i {
66                env.lookup.insert(key.to_owned(), lval.clone());
67            }
68        }
69    }
70
71    pub fn get(&self, key: &str) -> Option<Lval> {
72        let mut i = self.iter();
73
74        while let Some(env) = i.next() {
75            if let Some(v) = env.get(key) {
76                return Some(v.clone());
77            }
78        }
79
80        None
81    }
82}
83
84impl Drop for Lenv {
85    fn drop(&mut self) {
86        let mut cur_link = self.head.take();
87        while let Some(mut boxed_env) = cur_link {
88            cur_link = boxed_env.parent.take();
89        }
90    }
91}
92
93pub struct Iter<'a> {
94    next: Option<&'a Env>,
95}
96
97impl<'a> Iterator for Iter<'a> {
98    type Item = &'a Lookup;
99    fn next(&mut self) -> Option<Self::Item> {
100        self.next.map(|env| {
101            self.next = env.parent.as_deref();
102            &env.lookup
103        })
104    }
105}
106
107pub fn init_env() -> Lenv {
108    let mut env = Lenv::new();
109    env.push(Lookup::new());
110    init_builtins(&mut env);
111    env
112}
113
114#[cfg(test)]
115mod test {
116    use super::*;
117
118    #[test]
119    fn it_nests_properly() {
120        let mut env = Lenv::new();
121        env.push(Lookup::new());
122        env.insert("abc", Lval::Num(1_f64));
123        env.insert("def", Lval::Num(2_f64));
124
125        env.push(Lookup::new());
126        env.insert("abc", Lval::Num(3_f64));
127        env.insert("ghi", Lval::Num(4_f64));
128
129        assert_eq!(env.get("def").unwrap().to_owned(), Lval::Num(2_f64));
130        assert_eq!(env.get("abc").unwrap().to_owned(), Lval::Num(3_f64));
131        env.pop();
132
133        assert_eq!(env.get("abc").unwrap().to_owned(), Lval::Num(1_f64));
134        assert_eq!(env.get("def").unwrap().to_owned(), Lval::Num(2_f64));
135        assert_eq!(env.get("ghi"), None);
136    }
137
138    #[test]
139    fn it_inserts_last() {
140        let mut env = Lenv::new();
141        env.push(Lookup::new());
142        env.insert("abc", Lval::Num(1_f64));
143        env.insert_last("def", Lval::Num(2_f64));
144
145        env.push(Lookup::new());
146        env.insert("abc", Lval::Num(3_f64));
147        env.insert_last("jkl", Lval::Num(5_f64));
148
149        assert_eq!(env.get("def").unwrap().to_owned(), Lval::Num(2_f64));
150        assert_eq!(env.get("abc").unwrap().to_owned(), Lval::Num(3_f64));
151        assert_eq!(env.get("jkl").unwrap().to_owned(), Lval::Num(5_f64));
152
153        env.pop();
154
155        assert_eq!(env.get("jkl").unwrap().to_owned(), Lval::Num(5_f64));
156        assert_eq!(env.get("abc").unwrap().to_owned(), Lval::Num(1_f64));
157    }
158
159    #[test]
160    fn it_grabs_from_higher_environments() {
161        let mut env = Lenv::new();
162        env.push(Lookup::new()); // base
163        env.insert("a", Lval::Num(1_f64));
164        env.insert_last("b", Lval::Num(2_f64));
165
166        assert_eq!(env.get("a").unwrap().to_owned(), Lval::Num(1_f64));
167        assert_eq!(env.get("b").unwrap().to_owned(), Lval::Num(2_f64));
168
169        env.push(Lookup::new()); // 2nd
170        env.insert("f", Lval::Num(3_f64));
171
172        assert_eq!(env.get("a").unwrap().to_owned(), Lval::Num(1_f64));
173        assert_eq!(env.get("b").unwrap().to_owned(), Lval::Num(2_f64));
174        assert_eq!(env.get("f").unwrap().to_owned(), Lval::Num(3_f64));
175
176        env.push(Lookup::new()); // 3rd
177        env.insert("g", Lval::Num(4_f64));
178
179        assert_eq!(env.get("a").unwrap().to_owned(), Lval::Num(1_f64));
180        assert_eq!(env.get("b").unwrap().to_owned(), Lval::Num(2_f64));
181        assert_eq!(env.get("f").unwrap().to_owned(), Lval::Num(3_f64));
182        assert_eq!(env.get("g").unwrap().to_owned(), Lval::Num(4_f64));
183
184        env.pop();
185        assert_eq!(env.get("a").unwrap().to_owned(), Lval::Num(1_f64));
186        assert_eq!(env.get("b").unwrap().to_owned(), Lval::Num(2_f64));
187        assert_eq!(env.get("f").unwrap().to_owned(), Lval::Num(3_f64));
188
189        env.pop();
190        assert_eq!(env.get("a").unwrap().to_owned(), Lval::Num(1_f64));
191        assert_eq!(env.get("b").unwrap().to_owned(), Lval::Num(2_f64));
192    }
193}