use std::{borrow::Cow, hash::BuildHasherDefault};
use indexmap::IndexMap;
use rustc_hash::FxHasher;
use thiserror::Error;
use crate::v0::table::{NodeId, RegionId};
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
#[derive(Debug, Clone, Default)]
pub struct SymbolTable<'a> {
symbols: FxIndexMap<&'a str, BindingIndex>,
bindings: FxIndexMap<NodeId, Binding>,
scopes: FxIndexMap<RegionId, Scope>,
}
impl<'a> SymbolTable<'a> {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn enter(&mut self, region: RegionId) {
self.scopes.insert(
region,
Scope {
binding_stack: self.bindings.len(),
},
);
}
pub fn exit(&mut self) {
let (_, scope) = self.scopes.pop().unwrap();
for _ in scope.binding_stack..self.bindings.len() {
let (_, binding) = self.bindings.pop().unwrap();
if let Some(shadows) = binding.shadows {
self.symbols[binding.symbol_index] = shadows;
} else {
let last = self.symbols.pop();
debug_assert_eq!(last.unwrap().1, self.bindings.len());
}
}
}
pub fn insert(&mut self, name: &'a str, node: NodeId) -> Result<(), DuplicateSymbolError<'_>> {
let scope_depth = self.scopes.len() as u16 - 1;
let (symbol_index, shadowed) = self.symbols.insert_full(name, self.bindings.len());
if let Some(shadowed) = shadowed {
let (shadowed_node, shadowed_binding) = self.bindings.get_index(shadowed).unwrap();
if shadowed_binding.scope_depth == scope_depth {
self.symbols.insert(name, shadowed);
return Err(DuplicateSymbolError(name.into(), node, *shadowed_node));
}
}
self.bindings.insert(
node,
Binding {
scope_depth,
shadows: shadowed,
symbol_index,
},
);
Ok(())
}
#[must_use]
pub fn is_visible(&self, node: NodeId) -> bool {
let Some(binding) = self.bindings.get(&node) else {
return false;
};
self.symbols[binding.symbol_index] == binding.symbol_index
}
pub fn resolve(&self, name: &'a str) -> Result<NodeId, UnknownSymbolError<'_>> {
let index = *self
.symbols
.get(name)
.ok_or(UnknownSymbolError(name.into()))?;
let (node, _) = self.bindings.get_index(index).unwrap();
Ok(*node)
}
#[must_use]
pub fn region_to_depth(&self, region: RegionId) -> Option<ScopeDepth> {
Some(self.scopes.get_index_of(®ion)? as _)
}
#[must_use]
pub fn depth_to_region(&self, depth: ScopeDepth) -> Option<RegionId> {
let (region, _) = self.scopes.get_index(depth as _)?;
Some(*region)
}
pub fn clear(&mut self) {
self.symbols.clear();
self.bindings.clear();
self.scopes.clear();
}
}
#[derive(Debug, Clone, Copy)]
struct Binding {
scope_depth: ScopeDepth,
shadows: Option<BindingIndex>,
symbol_index: SymbolIndex,
}
#[derive(Debug, Clone, Copy)]
struct Scope {
binding_stack: usize,
}
type BindingIndex = usize;
type SymbolIndex = usize;
pub type ScopeDepth = u16;
#[derive(Debug, Clone, Error)]
#[error("symbol name `{0}` not found in this scope")]
pub struct UnknownSymbolError<'a>(pub Cow<'a, str>);
#[derive(Debug, Clone, Error)]
#[error("symbol `{0}` is already defined in this scope")]
pub struct DuplicateSymbolError<'a>(pub Cow<'a, str>, pub NodeId, pub NodeId);