use bitflags::bitflags;
use rustc_hash::FxHashMap;
use slotmap::{KeyData, SlotMap, new_key_type};
use swc_core::atoms::Atom;
new_key_type! {
pub struct ScopeInfoId;
pub struct VariableInfoId;
pub struct TagInfoId;
}
impl VariableInfoId {
pub fn tombstone() -> Self {
Self::from(KeyData::from_ffi(u64::MAX))
}
pub fn undefined() -> Self {
Self::from(KeyData::from_ffi(u64::MAX - 1))
}
}
#[derive(Debug, Default)]
pub struct VariableInfoDB {
map: SlotMap<VariableInfoId, VariableInfo>,
}
impl VariableInfoDB {
fn new() -> Self {
Self {
map: SlotMap::with_key(),
}
}
}
#[derive(Debug, Default)]
pub struct TagInfoDB {
pub map: SlotMap<TagInfoId, TagInfo>,
}
impl TagInfoDB {
fn new() -> Self {
Self {
map: SlotMap::with_key(),
}
}
}
#[derive(Debug)]
pub struct ScopeInfoDB {
map: SlotMap<ScopeInfoId, ScopeInfo>,
variable_info_db: VariableInfoDB,
tag_info_db: TagInfoDB,
}
impl Default for ScopeInfoDB {
fn default() -> Self {
Self::new()
}
}
impl ScopeInfoDB {
pub fn new() -> Self {
Self {
map: SlotMap::with_key(),
variable_info_db: VariableInfoDB::new(),
tag_info_db: TagInfoDB::new(),
}
}
fn _create(&mut self, parent: Option<ScopeInfoId>) -> ScopeInfoId {
let is_strict = match parent {
Some(parent) => self.expect_get_scope(parent).is_strict,
None => false,
};
let info = ScopeInfo {
is_strict,
parent,
map: Default::default(),
};
self.map.insert(info)
}
pub fn create(&mut self) -> ScopeInfoId {
self._create(None)
}
pub fn create_child(&mut self, parent: ScopeInfoId) -> ScopeInfoId {
self._create(Some(parent))
}
pub fn expect_get_scope(&self, id: ScopeInfoId) -> &ScopeInfo {
self
.map
.get(id)
.unwrap_or_else(|| panic!("{id:#?} should exist"))
}
pub fn expect_get_mut_scope(&mut self, id: ScopeInfoId) -> &mut ScopeInfo {
self
.map
.get_mut(id)
.unwrap_or_else(|| panic!("{id:#?} should exist"))
}
pub fn expect_get_variable(&self, id: VariableInfoId) -> &VariableInfo {
self
.variable_info_db
.map
.get(id)
.unwrap_or_else(|| panic!("{id:#?} should exist"))
}
pub fn expect_get_tag_info(&self, id: TagInfoId) -> &TagInfo {
self
.tag_info_db
.map
.get(id)
.unwrap_or_else(|| panic!("{id:#?} should exist"))
}
pub fn expect_get_mut_tag_info(&mut self, id: TagInfoId) -> &mut TagInfo {
self
.tag_info_db
.map
.get_mut(id)
.unwrap_or_else(|| panic!("{id:#?} should exist"))
}
pub fn get(&mut self, id: ScopeInfoId, key: &Atom) -> Option<VariableInfoId> {
let definitions = self.expect_get_scope(id);
if let Some(&top_value) = definitions.map.get(key) {
if top_value == VariableInfoId::tombstone() || top_value == VariableInfoId::undefined() {
None
} else {
Some(top_value)
}
} else if let Some(parent) = definitions.parent {
let mut current = Some(parent);
while let Some(current_id) = current {
let scope = self.expect_get_scope(current_id);
if let Some(&value) = scope.map.get(key) {
if value == VariableInfoId::tombstone() || value == VariableInfoId::undefined() {
return None;
} else {
return Some(value);
}
}
current = scope.parent;
}
let definitions = self.expect_get_mut_scope(id);
definitions
.map
.insert(key.clone(), VariableInfoId::tombstone());
None
} else {
None
}
}
pub fn set(&mut self, id: ScopeInfoId, key: Atom, variable_info_id: VariableInfoId) {
let scope = self.expect_get_mut_scope(id);
scope.map.insert(key, variable_info_id);
}
pub fn delete(&mut self, id: ScopeInfoId, key: &Atom) {
let scope = self.expect_get_mut_scope(id);
if scope.parent.is_some() {
scope.map.insert(key.clone(), VariableInfoId::tombstone());
} else {
scope.map.remove(key);
}
}
}
#[derive(Debug)]
pub struct TagInfo {
pub tag: &'static str,
pub data: Option<Box<dyn anymap::CloneAny>>,
pub next: Option<TagInfoId>,
}
impl TagInfo {
pub fn create(
definitions_db: &mut ScopeInfoDB,
tag: &'static str,
data: Option<Box<dyn anymap::CloneAny>>,
next: Option<TagInfoId>,
) -> TagInfoId {
let tag_info = TagInfo { tag, data, next };
definitions_db.tag_info_db.map.insert(tag_info)
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VariableInfoFlags: u8 {
const EVALUATED = 0b000;
const FREE = 0b001;
const NORMAL = 0b010;
const TAGGED = 0b100;
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct VariableInfo {
id: VariableInfoId,
pub declared_scope: ScopeInfoId,
pub name: Option<Atom>,
pub flags: VariableInfoFlags,
pub tag_info: Option<TagInfoId>,
}
impl VariableInfo {
pub fn create(
definitions_db: &mut ScopeInfoDB,
declared_scope: ScopeInfoId,
name: Option<Atom>,
flags: VariableInfoFlags,
tag_info: Option<TagInfoId>,
) -> VariableInfoId {
definitions_db
.variable_info_db
.map
.insert_with_key(|id| VariableInfo {
id,
declared_scope,
name,
flags,
tag_info,
})
}
pub fn id(&self) -> VariableInfoId {
self.id
}
pub fn is_free(&self) -> bool {
self.flags.contains(VariableInfoFlags::FREE)
}
pub fn is_tagged(&self) -> bool {
self.flags.contains(VariableInfoFlags::TAGGED)
}
}
#[derive(Debug)]
pub struct ScopeInfo {
parent: Option<ScopeInfoId>,
map: FxHashMap<Atom, VariableInfoId>,
pub is_strict: bool,
}
impl ScopeInfo {
pub fn variables(&self) -> impl Iterator<Item = (&str, &VariableInfoId)> {
self
.map
.iter()
.filter(|&(_, &info_id)| info_id != VariableInfoId::tombstone())
.map(|(name, info_id)| (name.as_str(), info_id))
}
}