1use crate::ast::types::{Effect, Type};
7use crate::ast::{TypeDef, Variant};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone)]
17pub struct Environment {
18 words: HashMap<String, Effect>,
20
21 types: HashMap<String, TypeDef>,
23}
24
25impl Environment {
26 pub fn new() -> Self {
28 let mut env = Environment {
29 words: HashMap::new(),
30 types: HashMap::new(),
31 };
32
33 env.add_builtin_words();
35 env.add_builtin_types();
36
37 env
38 }
39
40 pub fn add_word(&mut self, name: String, effect: Effect) {
42 self.words.insert(name, effect);
43 }
44
45 pub fn lookup_word(&self, name: &str) -> Option<&Effect> {
47 self.words.get(name)
48 }
49
50 pub fn add_type(&mut self, typedef: TypeDef) {
52 self.types.insert(typedef.name.clone(), typedef);
53 }
54
55 pub fn lookup_type(&self, name: &str) -> Option<&TypeDef> {
57 self.types.get(name)
58 }
59
60 pub fn get_variants(&self, type_name: &str) -> Option<&[Variant]> {
62 self.types.get(type_name).map(|td| td.variants.as_slice())
63 }
64
65 fn add_builtin_words(&mut self) {
67 use crate::ast::types::StackType;
68
69 self.add_word(
71 "dup".to_string(),
72 Effect {
73 inputs: StackType::empty().push(Type::Var("A".to_string())),
74 outputs: StackType::empty()
75 .push(Type::Var("A".to_string()))
76 .push(Type::Var("A".to_string())),
77 },
78 );
79
80 self.add_word(
82 "drop".to_string(),
83 Effect {
84 inputs: StackType::empty().push(Type::Var("A".to_string())),
85 outputs: StackType::empty(),
86 },
87 );
88
89 self.add_word(
91 "swap".to_string(),
92 Effect {
93 inputs: StackType::empty()
94 .push(Type::Var("A".to_string()))
95 .push(Type::Var("B".to_string())),
96 outputs: StackType::empty()
97 .push(Type::Var("B".to_string()))
98 .push(Type::Var("A".to_string())),
99 },
100 );
101
102 self.add_word(
104 "over".to_string(),
105 Effect {
106 inputs: StackType::empty()
107 .push(Type::Var("A".to_string()))
108 .push(Type::Var("B".to_string())),
109 outputs: StackType::empty()
110 .push(Type::Var("A".to_string()))
111 .push(Type::Var("B".to_string()))
112 .push(Type::Var("A".to_string())),
113 },
114 );
115
116 self.add_word(
118 "rot".to_string(),
119 Effect {
120 inputs: StackType::empty()
121 .push(Type::Var("A".to_string()))
122 .push(Type::Var("B".to_string()))
123 .push(Type::Var("C".to_string())),
124 outputs: StackType::empty()
125 .push(Type::Var("B".to_string()))
126 .push(Type::Var("C".to_string()))
127 .push(Type::Var("A".to_string())),
128 },
129 );
130
131 self.add_word(
134 "+".to_string(),
135 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
136 );
137
138 self.add_word(
140 "-".to_string(),
141 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
142 );
143
144 self.add_word(
146 "*".to_string(),
147 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
148 );
149
150 self.add_word(
152 "/".to_string(),
153 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
154 );
155
156 self.add_word(
159 "=".to_string(),
160 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
161 );
162
163 self.add_word(
165 "<".to_string(),
166 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
167 );
168
169 self.add_word(
171 ">".to_string(),
172 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
173 );
174
175 self.add_word(
177 "<=".to_string(),
178 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
179 );
180
181 self.add_word(
183 ">=".to_string(),
184 Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
185 );
186
187 self.add_word(
189 "clone".to_string(),
190 Effect {
191 inputs: StackType::empty().push(Type::Var("A".to_string())),
192 outputs: StackType::empty()
193 .push(Type::Var("A".to_string()))
194 .push(Type::Var("A".to_string())),
195 },
196 );
197 }
198
199 fn add_builtin_types(&mut self) {
201 self.add_type(TypeDef {
203 name: "Option".to_string(),
204 type_params: vec!["T".to_string()],
205 variants: vec![
206 Variant {
207 name: "Some".to_string(),
208 fields: vec![Type::Var("T".to_string())],
209 },
210 Variant {
211 name: "None".to_string(),
212 fields: vec![],
213 },
214 ],
215 });
216
217 self.add_type(TypeDef {
219 name: "Result".to_string(),
220 type_params: vec!["T".to_string(), "E".to_string()],
221 variants: vec![
222 Variant {
223 name: "Ok".to_string(),
224 fields: vec![Type::Var("T".to_string())],
225 },
226 Variant {
227 name: "Err".to_string(),
228 fields: vec![Type::Var("E".to_string())],
229 },
230 ],
231 });
232
233 self.add_type(TypeDef {
235 name: "List".to_string(),
236 type_params: vec!["T".to_string()],
237 variants: vec![
238 Variant {
239 name: "Cons".to_string(),
240 fields: vec![
241 Type::Var("T".to_string()),
242 Type::Named {
243 name: "List".to_string(),
244 args: vec![Type::Var("T".to_string())],
245 },
246 ],
247 },
248 Variant {
249 name: "Nil".to_string(),
250 fields: vec![],
251 },
252 ],
253 });
254 }
255}
256
257impl Default for Environment {
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[test]
268 fn test_builtin_words() {
269 let env = Environment::new();
270
271 assert!(env.lookup_word("dup").is_some());
273 assert!(env.lookup_word("drop").is_some());
274 assert!(env.lookup_word("swap").is_some());
275
276 assert!(env.lookup_word("+").is_some());
278 assert!(env.lookup_word("*").is_some());
279
280 assert!(env.lookup_word("unknown").is_none());
282 }
283
284 #[test]
285 fn test_builtin_types() {
286 let env = Environment::new();
287
288 let option_def = env.lookup_type("Option");
290 assert!(option_def.is_some());
291 assert_eq!(option_def.unwrap().variants.len(), 2);
292
293 let result_def = env.lookup_type("Result");
295 assert!(result_def.is_some());
296 assert_eq!(result_def.unwrap().variants.len(), 2);
297
298 let list_def = env.lookup_type("List");
300 assert!(list_def.is_some());
301 assert_eq!(list_def.unwrap().variants.len(), 2);
302 }
303
304 #[test]
305 fn test_add_word() {
306 let mut env = Environment::new();
307
308 let square_effect = Effect::from_vecs(vec![Type::Int], vec![Type::Int]);
309 env.add_word("square".to_string(), square_effect.clone());
310
311 let looked_up = env.lookup_word("square");
312 assert!(looked_up.is_some());
313 assert_eq!(*looked_up.unwrap(), square_effect);
314 }
315}