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    pub fn visible_names(&self) -> Vec<String> {
65        let mut names = self
66            .outer
67            .as_ref()
68            .map(|outer| outer.visible_names())
69            .unwrap_or_default();
70        names.extend(self.symbols.keys().cloned());
71        names
72    }
73
74    /// Names defined in the outermost (global) scope, paired with their slot
75    /// index. Sorted by name so reports stay deterministic.
76    pub fn global_symbols(&self) -> Vec<(String, usize)> {
77        let mut table = self;
78        while let Some(outer) = table.outer.as_deref() {
79            table = outer;
80        }
81        let mut globals: Vec<(String, usize)> = table
82            .symbols
83            .values()
84            .filter(|symbol| symbol.scope == SymbolScope::Global)
85            .map(|symbol| (symbol.name.clone(), symbol.index))
86            .collect();
87        globals.sort();
88        globals
89    }
90
91    // Resolve a name in the current scope, capturing free variables from outers when needed.
92    pub fn resolve(&mut self, name: String) -> Option<Rc<Symbol>> {
93        if let Some(sym) = self.symbols.get(&name) {
94            return Some(sym.clone());
95        }
96
97        // Resolve through every intermediate function scope. Each scope must
98        // create its own free symbol so closures capture from the immediately
99        // enclosing frame rather than reading a grandparent's local slot.
100        let outer = self.outer.take()?;
101        let mut outer_table = outer.as_ref().clone();
102        let original = outer_table.resolve(name);
103        self.outer = Some(Rc::new(outer_table));
104        let original = original?;
105        match original.scope {
106            SymbolScope::Global | SymbolScope::Builtin => Some(original),
107            SymbolScope::LOCAL | SymbolScope::Free | SymbolScope::Function => {
108                Some(self.define_free(original))
109            }
110        }
111    }
112
113    pub fn define_builtin(&mut self, index: usize, name: String) -> Rc<Symbol> {
114        let symbol = Rc::new(Symbol {
115            name: name.clone(),
116            index,
117            scope: SymbolScope::Builtin,
118        });
119        self.symbols.insert(name.clone(), Rc::clone(&symbol));
120        return symbol;
121    }
122
123    pub fn define_function_name(&mut self, name: String) -> Rc<Symbol> {
124        let symbol = Rc::new(Symbol {
125            name: name.clone(),
126            index: 0,
127            scope: SymbolScope::Function,
128        });
129        self.symbols.insert(name.clone(), Rc::clone(&symbol));
130        return symbol;
131    }
132
133    pub fn define_free(&mut self, original: Rc<Symbol>) -> Rc<Symbol> {
134        self.free_symbols.push(Rc::clone(&original));
135        let symbol = Rc::new(Symbol {
136            name: original.name.clone(),
137            index: self.free_symbols.len() - 1,
138            scope: SymbolScope::Free,
139        });
140        self.symbols
141            .insert(original.name.clone(), Rc::clone(&symbol));
142        return symbol;
143    }
144}