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()); 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()); 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()); 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}