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