use std::{collections::HashMap, sync::Arc};
use arcstr::Substr;
use tracing::trace;
use crate::{
symbols::{name::SymName, scopes::ScopeWalk},
syntax::cst::{
aside::Aside,
tree::{NodeHandle, NodeRef},
view::{TypeVar, View},
},
};
use super::{PkgHandle, Project, querry::Queryable, syntax::Syntax};
pub struct Bindings {
map: Aside<Option<(PkgHandle, NodeHandle)>>,
}
impl Queryable for Bindings {
type Input = PkgHandle;
type Ctx = ();
type Backref = ();
fn compute(pkg: Self::Input, _ctx: &Self::Ctx, proj: &Project) -> Arc<Self> {
trace!("computing bindings for {}", proj.get_pkg(pkg));
Arc::new(Self::compute(proj, pkg))
}
fn invalidate(&self, _input: &Self::Input, _ctx: &Self::Ctx, _proj: &Project) {}
fn invalidate_backref(&self, _backref: Self::Backref, _proj: &Project) {}
}
impl Bindings {
fn compute(proj: &Project, pkg: PkgHandle) -> Self {
let syntax = proj.syntax.get(proj, pkg);
let mut state = BindState {
aside: Aside::new(&syntax.cst),
proj,
syntax: &syntax,
globals: syntax.compute_globals(proj),
locals: HashMap::new(),
depth: 0,
};
state.walk_impl(syntax.cst.root());
Bindings { map: state.aside }
}
pub fn get(&self, node: NodeHandle) -> Option<(PkgHandle, NodeHandle)> {
*self.map.get(node)
}
pub fn iter(&self) -> impl Iterator<Item = (NodeHandle, (PkgHandle, NodeHandle))> {
self.map.iter().filter_map(|(n, d)| Some((n, (*d)?)))
}
}
struct BindState<'ctx> {
aside: Aside<Option<(PkgHandle, NodeHandle)>>,
proj: &'ctx Project,
syntax: &'ctx Syntax,
globals: HashMap<SymName, (PkgHandle, NodeHandle)>,
locals: HashMap<SymName, NodeHandle>,
depth: usize,
}
impl BindState<'_> {
fn resolve_id(&self, pkg: Option<Substr>, name: SymName) -> Option<(PkgHandle, NodeHandle)> {
if let Some(pkg) = pkg {
let pkg = self.globals.get(&SymName::Pkg(pkg)).map(|(p, _)| *p)?;
let exports = self.proj.exports.get(self.proj, pkg);
exports.defs.get(&name).copied()
} else {
if let Some(def) = self.locals.get(&name) {
return Some((self.syntax.ctx.pkg, *def));
}
self.globals.get(&name).copied()
}
}
}
impl ScopeWalk for BindState<'_> {
type R = ();
fn scope(
&mut self,
_node: NodeRef,
inner: impl FnOnce(&mut Self) -> Option<Self::R>,
) -> Option<Self::R> {
let prev = self.locals.clone();
self.depth += 1;
(inner(self));
self.locals = prev;
self.depth -= 1;
None
}
fn def(&mut self, node: NodeRef, name: SymName) -> Option<Self::R> {
if self.depth > 1 {
self.locals.insert(name, node.handle());
}
None
}
fn link(&mut self, node: NodeRef, pkg: Option<Substr>, name: SymName) -> Option<Self::R> {
if let Some(def) = self.resolve_id(pkg, name) {
self.aside.set(node.handle(), Some(def))
}
None
}
fn ty_var(&mut self, node: TypeVar) -> Option<Self::R> {
let name = node.name()?;
if let Some(def) = self.locals.get(&SymName::Type(name.str())) {
self.aside
.set(name.syntax().handle(), Some((self.syntax.ctx.pkg, *def)));
} else {
self.locals
.insert(SymName::Type(name.str()), node.syntax().handle());
}
None
}
}