use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use crate::{model::{DataRef, Graph, NodeRef}, runtime::{Error, Variable}};
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SymbolTable {
pub scopes: Vec<Scope>,
}
impl SymbolTable {
#[inline(always)]
pub fn push(&mut self) {
self.scopes.push(Scope::default());
}
#[inline(always)]
pub fn clear(&mut self) {
self.scopes.clear();
}
#[inline(always)]
pub fn pop(&mut self) -> bool {
self.scopes.pop().is_some()
}
#[inline(always)]
pub fn can_declare(&self, name: impl AsRef<str>) -> bool {
self.scopes.is_empty() || !self.scopes.last().unwrap().has(name)
}
#[inline]
pub fn insert(&mut self, name: impl ToString, var: Variable) {
if self.scopes.is_empty() { self.push(); }
self.scopes.last_mut().unwrap().insert(name, var);
}
pub fn drop_var(&mut self, name: impl AsRef<str>) -> Option<Variable> {
let name = name.as_ref();
for scope in self.scopes.iter_mut().rev() {
if let Some(var) = scope.remove(name) {
return Some(var);
}
}
None
}
pub fn get(&self, name: impl AsRef<str>) -> Option<&Variable> {
let name = name.as_ref();
for scope in self.scopes.iter().rev() {
if let Some(var) = scope.get(name) {
return Some(var);
}
}
None
}
pub fn set(&mut self, name: impl AsRef<str>, var: &Variable, graph: &mut Graph, context: Option<NodeRef>) -> Result<bool, Error> {
let name = name.as_ref();
for scope in self.scopes.iter_mut().rev() {
if scope.set(name, var, graph, context.clone())? {
return Ok(true);
}
}
Ok(false)
}
#[inline]
pub fn gc_node(&mut self, node: &NodeRef) {
for scope in &mut self.scopes {
scope.gc_node(node);
}
}
#[inline]
pub fn gc_data(&mut self, data: &DataRef) {
for scope in &mut self.scopes {
scope.gc_data(data);
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Scope {
variables: FxHashMap<String, Variable>,
}
impl Scope {
#[inline(always)]
pub fn has(&self, name: impl AsRef<str>) -> bool {
self.variables.contains_key(name.as_ref())
}
#[inline(always)]
pub fn insert(&mut self, name: impl ToString, var: Variable) {
self.variables.insert(name.to_string(), var);
}
#[inline(always)]
pub fn remove(&mut self, name: impl AsRef<str>) -> Option<Variable> {
self.variables.remove(name.as_ref())
}
#[inline(always)]
pub fn get(&self, name: impl AsRef<str>) -> Option<&Variable> {
self.variables.get(name.as_ref())
}
#[inline(always)]
pub fn get_mut(&mut self, name: impl AsRef<str>) -> Option<&mut Variable> {
self.variables.get_mut(name.as_ref())
}
#[inline]
pub fn set(&mut self, name: impl AsRef<str>, var: &Variable, graph: &mut Graph, context: Option<NodeRef>) -> Result<bool, Error> {
if let Some(svar) = self.get_mut(name) {
svar.set(var, graph, context)?;
Ok(true)
} else {
Ok(false)
}
}
pub fn gc_node(&mut self, node: &NodeRef) {
let mut to_remove = Vec::new();
for (name, var) in &self.variables {
if let Some(nref) = var.try_obj() {
if &nref == node {
to_remove.push(name.clone());
}
}
}
for name in to_remove {
self.variables.remove(&name);
}
}
pub fn gc_data(&mut self, data: &DataRef) {
let mut to_remove = Vec::new();
for (name, var) in &self.variables {
if var.is_data_ref(data) {
to_remove.push(name.clone());
}
}
for name in to_remove {
self.variables.remove(&name);
}
}
}