Skip to main content

compiler/
symbol_table.rs

1use std::collections::HashMap;
2use std::rc::Rc;
3
4#[derive(Clone, Debug, Eq, PartialEq)]
5pub enum SymbolScope {
6    LOCAL,
7    Global,
8    Builtin,
9    Free,
10    Function,
11}
12
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct Symbol {
15    pub name: String,
16    pub scope: SymbolScope,
17    pub index: usize,
18}
19
20#[derive(Clone, Debug, Eq, PartialEq)]
21pub struct SymbolTable {
22    pub outer: Option<Rc<SymbolTable>>,
23    symbols: HashMap<String, Rc<Symbol>>,
24    pub free_symbols: Vec<Rc<Symbol>>,
25    pub num_definitions: usize,
26}
27
28impl SymbolTable {
29    pub fn new() -> SymbolTable {
30        SymbolTable {
31            symbols: HashMap::new(),
32            free_symbols: vec![],
33            num_definitions: 0,
34            outer: None,
35        }
36    }
37
38    pub fn new_enclosed_symbol_table(outer: SymbolTable) -> SymbolTable {
39        SymbolTable {
40            symbols: HashMap::new(),
41            free_symbols: vec![],
42            num_definitions: 0,
43            outer: Some(Rc::new(outer)),
44        }
45    }
46
47    pub fn define(&mut self, name: String) -> Rc<Symbol> {
48        let mut scope = SymbolScope::LOCAL;
49        if self.outer.is_none() {
50            scope = SymbolScope::Global;
51        }
52
53        let symbol = Rc::new(Symbol {
54            name: name.clone(),
55            index: self.num_definitions,
56            scope,
57        });
58
59        self.num_definitions += 1;
60        self.symbols.insert(name.clone(), Rc::clone(&symbol));
61        return symbol;
62    }
63
64    // Resolve a name in the current scope, capturing free variables from outers when needed.
65    pub fn resolve(&mut self, name: String) -> Option<Rc<Symbol>> {
66        if let Some(sym) = self.symbols.get(&name) {
67            return Some(sym.clone());
68        }
69
70        // If not found locally, try outer scopes.
71        if let Some(outer) = &self.outer {
72            // We can't mutate outer here, so use a read-only helper to locate the original symbol.
73            if let Some(original) = outer.resolve_readonly(&name) {
74                return match original.scope {
75                    // Globals and builtins are accessed directly.
76                    SymbolScope::Global | SymbolScope::Builtin => Some(original),
77                    // Locals (from outer scope) or already-free symbols should be captured as a new free symbol here.
78                    SymbolScope::LOCAL | SymbolScope::Free | SymbolScope::Function => {
79                        Some(self.define_free(original))
80                    }
81                };
82            }
83        }
84
85        None
86    }
87
88    // Read-only resolver used internally to search outer scopes without mutating them.
89    fn resolve_readonly(&self, name: &str) -> Option<Rc<Symbol>> {
90        if let Some(sym) = self.symbols.get(name) {
91            return Some(sym.clone());
92        }
93        if let Some(outer) = &self.outer {
94            return outer.resolve_readonly(name);
95        }
96        None
97    }
98
99    pub fn define_builtin(&mut self, index: usize, name: String) -> Rc<Symbol> {
100        let symbol = Rc::new(Symbol {
101            name: name.clone(),
102            index,
103            scope: SymbolScope::Builtin,
104        });
105        self.symbols.insert(name.clone(), Rc::clone(&symbol));
106        return symbol;
107    }
108
109    pub fn define_function_name(&mut self, name: String) -> Rc<Symbol> {
110        let symbol = Rc::new(Symbol {
111            name: name.clone(),
112            index: 0,
113            scope: SymbolScope::Function,
114        });
115        self.symbols.insert(name.clone(), Rc::clone(&symbol));
116        return symbol;
117    }
118
119    pub fn define_free(&mut self, original: Rc<Symbol>) -> Rc<Symbol> {
120        self.free_symbols.push(Rc::clone(&original));
121        let symbol = Rc::new(Symbol {
122            name: original.name.clone(),
123            index: self.free_symbols.len() - 1,
124            scope: SymbolScope::Free,
125        });
126        self.symbols
127            .insert(original.name.clone(), Rc::clone(&symbol));
128        return symbol;
129    }
130}