use crate::item::{Item, Symbol, SymbolDecl};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ScopeId(usize);
#[derive(Debug, Clone)]
pub struct Scope {
id: ScopeId,
symbols: HashMap<String, Item>,
captures: HashSet<Symbol>,
}
impl Scope {
pub fn new(id: ScopeId) -> Self {
Self {
id,
symbols: HashMap::new(),
captures: HashSet::new(),
}
}
pub fn id(&self) -> ScopeId {
self.id
}
pub fn captures(&self) -> &HashSet<Symbol> {
&self.captures
}
pub fn insert(&mut self, name: String, item: Item) {
self.symbols.insert(name, item);
}
pub fn resolve_item(&self, name: &str) -> Option<&Item> {
self.symbols.get(name)
}
pub fn add_capture(&mut self, symbol: Symbol) {
self.captures.insert(symbol);
}
}
#[derive(Debug, Clone)]
pub struct SymbolTable {
next_id: usize,
symbols: HashMap<ScopeId, Scope>,
active_scopes: Vec<Scope>,
}
impl Default for SymbolTable {
fn default() -> Self {
Self {
next_id: 1,
symbols: HashMap::new(),
active_scopes: vec![Scope::new(ScopeId(0))], }
}
}
impl SymbolTable {
pub fn new() -> Self {
Self::default()
}
pub fn is_global_scope(&self) -> bool {
self.active_scopes.len() == 1
}
pub fn next_id(&self) -> ScopeId {
ScopeId(self.next_id)
}
pub fn enter_scope(&mut self) {
let id = ScopeId(self.next_id);
self.next_id += 1;
let scope = Scope::new(id);
self.active_scopes.push(scope);
}
pub fn exit_scope(&mut self) {
let scope = self.active_scopes.pop().expect("no scope to exit");
if !scope.symbols.is_empty() {
self.symbols.insert(scope.id(), scope);
}
}
pub(crate) fn exit_scope_get(&mut self) -> &Scope {
let scope = self.active_scopes.pop().expect("no scope to exit");
self.symbols.entry(scope.id()).or_insert(scope)
}
pub fn active_scope(&self) -> &Scope {
self.active_scopes
.last()
.expect("no active scope")
}
pub fn active_scope_mut(&mut self) -> &mut Scope {
self.active_scopes
.last_mut()
.expect("no active scope")
}
pub fn insert(&mut self, name: String, item: Item) {
self.active_scope_mut().insert(name, item);
}
pub fn resolve_item(&self, name: &str) -> Option<&Item> {
self.active_scopes
.iter()
.rev()
.find_map(|scope| scope.resolve_item(name))
}
pub fn resolve_item_mark_capture(&mut self, name: &str) -> Option<Symbol> {
let (last, rest) = self.active_scopes.split_last_mut()?;
if let Some(item) = last.resolve_item(name) {
return Some(Symbol::User(item.id()));
}
rest.iter_mut().rev().find_map(|scope| {
scope.resolve_item(name).map(|item| {
last.add_capture(Symbol::User(item.id()));
Symbol::User(item.id())
})
})
}
pub fn resolve_symbol(&self, name: &str) -> Option<SymbolDecl> {
self.resolve_item(name)
.and_then(|item| match item {
Item::Symbol(decl) => Some(*decl),
_ => None,
})
}
}