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 Default for SymbolTable {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl SymbolTable {
35    pub fn new() -> SymbolTable {
36        SymbolTable {
37            symbols: HashMap::new(),
38            free_symbols: vec![],
39            num_definitions: 0,
40            outer: None,
41        }
42    }
43
44    pub fn new_enclosed_symbol_table(outer: SymbolTable) -> SymbolTable {
45        SymbolTable {
46            symbols: HashMap::new(),
47            free_symbols: vec![],
48            num_definitions: 0,
49            outer: Some(Rc::new(outer)),
50        }
51    }
52
53    pub fn define(&mut self, name: String) -> Rc<Symbol> {
54        let mut scope = SymbolScope::LOCAL;
55        if self.outer.is_none() {
56            scope = SymbolScope::Global;
57        }
58
59        let symbol = Rc::new(Symbol {
60            name: name.clone(),
61            index: self.num_definitions,
62            scope,
63        });
64
65        self.num_definitions += 1;
66        self.symbols.insert(name.clone(), Rc::clone(&symbol));
67        return symbol;
68    }
69
70    pub fn visible_names(&self) -> Vec<String> {
71        let mut names = self
72            .outer
73            .as_ref()
74            .map(|outer| outer.visible_names())
75            .unwrap_or_default();
76        names.extend(self.symbols.keys().cloned());
77        names
78    }
79
80    /// Names defined in the outermost (global) scope, paired with their slot
81    /// index. Sorted by name so reports stay deterministic.
82    pub fn global_symbols(&self) -> Vec<(String, usize)> {
83        let mut table = self;
84        while let Some(outer) = table.outer.as_deref() {
85            table = outer;
86        }
87        let mut globals: Vec<(String, usize)> = table
88            .symbols
89            .values()
90            .filter(|symbol| symbol.scope == SymbolScope::Global)
91            .map(|symbol| (symbol.name.clone(), symbol.index))
92            .collect();
93        globals.sort();
94        globals
95    }
96
97    // Resolve a name in the current scope, capturing free variables from outers when needed.
98    pub fn resolve(&mut self, name: String) -> Option<Rc<Symbol>> {
99        if let Some(sym) = self.symbols.get(&name) {
100            return Some(sym.clone());
101        }
102
103        // Resolve through every intermediate function scope. Each scope must
104        // create its own free symbol so closures capture from the immediately
105        // enclosing frame rather than reading a grandparent's local slot.
106        let outer = self.outer.take()?;
107        let mut outer_table = outer.as_ref().clone();
108        let original = outer_table.resolve(name);
109        self.outer = Some(Rc::new(outer_table));
110        let original = original?;
111        match original.scope {
112            SymbolScope::Global | SymbolScope::Builtin => Some(original),
113            SymbolScope::LOCAL | SymbolScope::Free | SymbolScope::Function => {
114                Some(self.define_free(original))
115            }
116        }
117    }
118
119    pub fn define_builtin(&mut self, index: usize, name: String) -> Rc<Symbol> {
120        let symbol = Rc::new(Symbol {
121            name: name.clone(),
122            index,
123            scope: SymbolScope::Builtin,
124        });
125        self.symbols.insert(name.clone(), Rc::clone(&symbol));
126        return symbol;
127    }
128
129    pub fn define_function_name(&mut self, name: String) -> Rc<Symbol> {
130        let symbol = Rc::new(Symbol {
131            name: name.clone(),
132            index: 0,
133            scope: SymbolScope::Function,
134        });
135        self.symbols.insert(name.clone(), Rc::clone(&symbol));
136        return symbol;
137    }
138
139    pub fn define_free(&mut self, original: Rc<Symbol>) -> Rc<Symbol> {
140        self.free_symbols.push(Rc::clone(&original));
141        let symbol = Rc::new(Symbol {
142            name: original.name.clone(),
143            index: self.free_symbols.len() - 1,
144            scope: SymbolScope::Free,
145        });
146        self.symbols
147            .insert(original.name.clone(), Rc::clone(&symbol));
148        return symbol;
149    }
150}