use crate::ast::Node;
use crate::error::SyntaxResult;
use crate::session::Session;
use crate::session::SessionHashMap;
use crate::session::SessionVec;
use crate::source::SourceRange;
use hashbrown::hash_map::Entry;
use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
use std::hash::Hash;
pub type Identifier<'a> = SourceRange<'a>;
#[derive(Clone)]
pub struct Symbol<'a> {
scope: Scope<'a>,
ordinal_in_scope: usize,
declarator_pattern: Node<'a>,
}
impl<'a> PartialEq for Symbol<'a> {
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.scope.0, other.scope.0) && self.ordinal_in_scope == other.ordinal_in_scope
}
}
impl<'a> Eq for Symbol<'a> {}
impl<'a> Hash for Symbol<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::ptr::hash(self.scope.0, state);
self.ordinal_in_scope.hash(state);
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ScopeType {
Global,
Module,
Class,
NonArrowFunction,
ArrowFunction,
Block,
}
impl ScopeType {
pub fn is_closure(&self) -> bool {
match self {
ScopeType::Module => true,
ScopeType::NonArrowFunction => true,
ScopeType::ArrowFunction => true,
_ => false,
}
}
}
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ScopeFlag {
UsesThis,
UsesArguments,
}
struct ScopeData<'a> {
symbols: SessionHashMap<'a, Identifier<'a>, Symbol<'a>>,
symbol_declaration_order: SessionVec<'a, Identifier<'a>>,
parent: Option<Scope<'a>>,
children: SessionVec<'a, Scope<'a>>,
typ: ScopeType,
flags: u64,
}
#[derive(Clone, Copy)]
pub struct Scope<'a>(&'a RefCell<ScopeData<'a>>);
impl<'a> Scope<'a> {
fn get<'b>(self) -> Ref<'b, ScopeData<'a>> {
self.0.borrow()
}
fn get_mut<'b>(self) -> RefMut<'b, ScopeData<'a>> {
self.0.borrow_mut()
}
pub fn new(session: &'a Session, parent: Option<Scope<'a>>, typ: ScopeType) -> Scope<'a> {
let scope = Scope(session.mem.alloc(RefCell::new(ScopeData {
symbols: session.new_hashmap(),
symbol_declaration_order: session.new_vec(),
parent,
children: session.new_vec(),
typ,
flags: 0,
})));
if let Some(parent) = parent {
parent.get_mut().children.push(scope);
};
scope
}
pub fn parent(self) -> Option<Scope<'a>> {
self.get().parent
}
pub fn create_child_scope(self, session: &'a Session, typ: ScopeType) -> Scope<'a> {
Scope::new(session, Some(self), typ)
}
pub fn find_self_or_ancestor<F: Fn(ScopeType) -> bool>(self, pred: F) -> Option<Scope<'a>> {
let cur = self.get();
if pred(cur.typ) {
Some(self)
} else if let Some(parent) = cur.parent {
parent.find_self_or_ancestor(pred)
} else {
None
}
}
pub fn has_flag(&self, flag: ScopeFlag) -> bool {
(self.get().flags & (1 << (flag as u8))) != 0
}
pub fn set_flag(&mut self, flag: ScopeFlag) {
self.get_mut().flags |= 1 << (flag as u8);
}
pub fn add_symbol(
self,
identifier: Identifier<'a>,
declarator_pattern: Node<'a>,
) -> SyntaxResult<'a, ()> {
let ordinal_in_scope = self.get().symbol_declaration_order.len();
let mut as_mut = self.get_mut();
match as_mut.symbols.entry(identifier.clone()) {
Entry::Occupied(_) => {
}
Entry::Vacant(e) => {
e.insert(Symbol {
scope: self,
declarator_pattern,
ordinal_in_scope,
});
as_mut.symbol_declaration_order.push(identifier.clone());
}
};
Ok(())
}
pub fn add_block_symbol(
self,
identifier: Identifier<'a>,
declarator_pattern: Node<'a>,
) -> SyntaxResult<'a, ()> {
if self.get().typ != ScopeType::Global {
self.add_symbol(identifier, declarator_pattern)?;
};
Ok(())
}
pub fn get_symbol(self, identifier: Identifier<'a>) -> Option<Symbol<'a>> {
self.get().symbols.get(&identifier).cloned()
}
pub fn find_symbol(self, identifier: Identifier<'a>) -> Option<Symbol<'a>> {
match self.get().symbols.get(&identifier) {
Some(symbol) => Some(symbol.clone()),
None => match self.get().parent {
Some(parent) => parent.find_symbol(identifier),
None => None,
},
}
}
pub fn find_symbol_up_to_nearest_scope_of_type<'b>(
self,
identifier: Identifier<'a>,
scope_type: ScopeType,
) -> Option<Symbol<'a>> {
let mut scope = self;
loop {
let scope_data = scope.get();
if let Some(symbol) = scope_data.symbols.get(&identifier) {
return Some(symbol.clone());
};
if scope_data.typ == scope_type {
break;
};
if let Some(parent) = scope_data.parent {
scope = parent;
continue;
};
break;
}
None
}
pub fn symbol_count(self) -> usize {
self.get().symbols.len()
}
pub fn symbol_names<'b>(self) -> Ref<'b, SessionVec<'a, SourceRange<'a>>> {
Ref::map(self.get(), |scope| &scope.symbol_declaration_order)
}
pub fn children<'b>(self) -> Ref<'b, SessionVec<'a, Scope<'a>>> {
Ref::map(self.get(), |scope| &scope.children)
}
}