use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Symbol {
pub name: String,
pub scope: SymbolScope,
pub index: usize,
pub depth: usize,
}
impl Symbol {
pub fn new(name: &str, scope: SymbolScope, index: usize, depth: usize) -> Self {
Self {
name: name.to_string(),
scope,
index,
depth,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SymbolScope {
Global,
Local,
BuiltinFn,
BuiltinVar,
Free,
Function,
}
impl fmt::Display for SymbolScope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SymbolScope::Global => write!(f, "GLOBAL"),
SymbolScope::Local => write!(f, "LOCAL"),
SymbolScope::BuiltinFn => write!(f, "BUILTINFN"),
SymbolScope::BuiltinVar => write!(f, "BUILTINVAR"),
SymbolScope::Free => write!(f, "FREE"),
SymbolScope::Function => write!(f, "FUNCTION"),
}
}
}
#[derive(Default, Clone, Debug, Eq, PartialEq)]
pub struct SymbolTable {
store: HashMap<String, Vec<Rc<Symbol>>>,
num_definitions: usize,
pub outer: Option<Box<SymbolTable>>,
pub free_symbols: Vec<Rc<Symbol>>,
}
impl SymbolTable {
pub fn new_enclosed(outer: SymbolTable) -> SymbolTable {
SymbolTable {
store: HashMap::new(),
num_definitions: 0,
outer: Some(Box::new(outer)),
free_symbols: Vec::new(),
}
}
pub fn get_num_definitions(&self) -> usize {
self.num_definitions
}
pub fn define(&mut self, name: &str, depth: usize) -> Rc<Symbol> {
let symbol = Rc::new(Symbol::new(
name,
if self.outer.is_none() {
SymbolScope::Global
} else {
SymbolScope::Local
},
self.num_definitions,
depth, ));
self.store
.entry(name.to_string())
.or_default()
.push(Rc::clone(&symbol));
self.num_definitions += 1;
symbol
}
pub fn define_function_name(&mut self, name: &str) -> Rc<Symbol> {
let symbol = Rc::new(Symbol::new(name, SymbolScope::Function, 0, 0));
self.store
.insert(name.to_string(), vec![Rc::clone(&symbol)]);
symbol
}
pub fn resolve(&mut self, name: &str, depth: usize) -> Option<Rc<Symbol>> {
if let Some(symbols) = self.store.get(name) {
for symbol in symbols.iter().rev() {
if symbol.depth <= depth {
return Some(Rc::clone(symbol));
}
}
} else if let Some(outer) = &mut self.outer {
if let Some(obj) = outer.resolve(name, depth) {
if matches!(
obj.scope,
SymbolScope::Global | SymbolScope::BuiltinFn | SymbolScope::BuiltinVar
) {
return Some(obj);
} else {
return Some(self.define_free(obj));
}
}
}
None
}
pub fn define_builtin_fn(&mut self, index: usize, name: &str) -> Rc<Symbol> {
let symbol = Rc::new(Symbol::new(name, SymbolScope::BuiltinFn, index, 0));
self.store
.insert(name.to_string(), vec![Rc::clone(&symbol)]);
symbol
}
pub fn define_builtin_var(&mut self, index: usize, name: &str) -> Rc<Symbol> {
let symbol = Rc::new(Symbol::new(name, SymbolScope::BuiltinVar, index, 0));
self.store
.insert(name.to_string(), vec![Rc::clone(&symbol)]);
symbol
}
fn define_free(&mut self, original: Rc<Symbol>) -> Rc<Symbol> {
self.free_symbols.push(original.clone());
let len = self.free_symbols.len();
let symbol = Rc::new(Symbol::new(
&original.name,
SymbolScope::Free,
len - 1,
original.depth,
));
self.store.insert(symbol.name.clone(), vec![symbol.clone()]);
symbol
}
}