Skip to main content

cemc/typechecker/
environment.rs

1/**
2Type checking environment for Cem
3
4Maintains symbol tables for words, types, and type variables during type checking.
5*/
6use crate::ast::types::{Effect, Type};
7use crate::ast::{TypeDef, Variant};
8use std::collections::HashMap;
9
10/// Type checking environment
11///
12/// Contains:
13/// - Word definitions (name -> effect signature)
14/// - Type definitions (name -> ADT definition)
15/// - Built-in primitives
16#[derive(Debug, Clone)]
17pub struct Environment {
18    /// Word definitions: name -> effect
19    words: HashMap<String, Effect>,
20
21    /// Type definitions: name -> TypeDef
22    types: HashMap<String, TypeDef>,
23}
24
25impl Environment {
26    /// Create a new environment with built-in primitives
27    pub fn new() -> Self {
28        let mut env = Environment {
29            words: HashMap::new(),
30            types: HashMap::new(),
31        };
32
33        // Add built-in stack operations
34        env.add_builtin_words();
35        env.add_builtin_types();
36
37        env
38    }
39
40    /// Add a word definition
41    pub fn add_word(&mut self, name: String, effect: Effect) {
42        self.words.insert(name, effect);
43    }
44
45    /// Look up a word's effect signature
46    pub fn lookup_word(&self, name: &str) -> Option<&Effect> {
47        self.words.get(name)
48    }
49
50    /// Add a type definition
51    pub fn add_type(&mut self, typedef: TypeDef) {
52        self.types.insert(typedef.name.clone(), typedef);
53    }
54
55    /// Look up a type definition
56    pub fn lookup_type(&self, name: &str) -> Option<&TypeDef> {
57        self.types.get(name)
58    }
59
60    /// Get all variants for a sum type (for exhaustiveness checking)
61    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    /// Add built-in word definitions
66    fn add_builtin_words(&mut self) {
67        use crate::ast::types::StackType;
68
69        // dup: ( A -- A A )
70        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        // drop: ( A -- )
81        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        // swap: ( A B -- B A )
90        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        // over: ( A B -- A B A )
103        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        // rot: ( A B C -- B C A )
117        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        // Arithmetic operations
132        // +: ( Int Int -- Int )
133        self.add_word(
134            "+".to_string(),
135            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
136        );
137
138        // -: ( Int Int -- Int )
139        self.add_word(
140            "-".to_string(),
141            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
142        );
143
144        // *: ( Int Int -- Int )
145        self.add_word(
146            "*".to_string(),
147            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
148        );
149
150        // /: ( Int Int -- Int )
151        self.add_word(
152            "/".to_string(),
153            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
154        );
155
156        // Comparison operations
157        // =: ( Int Int -- Bool )
158        self.add_word(
159            "=".to_string(),
160            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
161        );
162
163        // <: ( Int Int -- Bool )
164        self.add_word(
165            "<".to_string(),
166            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
167        );
168
169        // >: ( Int Int -- Bool )
170        self.add_word(
171            ">".to_string(),
172            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
173        );
174
175        // <=: ( Int Int -- Bool )
176        self.add_word(
177            "<=".to_string(),
178            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
179        );
180
181        // >=: ( Int Int -- Bool )
182        self.add_word(
183            ">=".to_string(),
184            Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
185        );
186
187        // clone: ( A -- A A ) for explicit cloning
188        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    /// Add built-in type definitions
200    fn add_builtin_types(&mut self) {
201        // Option<T>
202        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        // Result<T, E>
218        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        // List<T>
234        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        // Should have basic stack operations
272        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        // Should have arithmetic
277        assert!(env.lookup_word("+").is_some());
278        assert!(env.lookup_word("*").is_some());
279
280        // Unknown word
281        assert!(env.lookup_word("unknown").is_none());
282    }
283
284    #[test]
285    fn test_builtin_types() {
286        let env = Environment::new();
287
288        // Should have Option
289        let option_def = env.lookup_type("Option");
290        assert!(option_def.is_some());
291        assert_eq!(option_def.unwrap().variants.len(), 2);
292
293        // Should have Result
294        let result_def = env.lookup_type("Result");
295        assert!(result_def.is_some());
296        assert_eq!(result_def.unwrap().variants.len(), 2);
297
298        // Should have List
299        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}