use arcstr::Substr;
use crate::{
symbols::name::SymName,
syntax::cst::{
kind::TreeKind, tree::NodeRef, view::{self, TypeVar, View}
},
util::data::either::Either,
};
pub trait ScopeWalk {
type R;
fn scope(
&mut self,
node: NodeRef,
inner: impl FnOnce(&mut Self) -> Option<Self::R>,
) -> Option<Self::R>;
fn def(&mut self, node: NodeRef, name: SymName) -> Option<Self::R>;
fn link(&mut self, node: NodeRef, pkg: Option<Substr>, name: SymName) -> Option<Self::R>;
fn ty_var(&mut self, node: TypeVar) -> Option<Self::R>;
fn link_qual(&mut self, qid: view::QualId, is_ty: bool) -> Option<Self::R> {
let name = qid.name()?;
let pkg = qid.pkg().map(|id| id.str());
use SymName::*;
self.link(
name.syntax(),
pkg,
if is_ty {
Type(name.str())
} else {
Value(name.str())
},
)
}
fn walk(&mut self, node: NodeRef) -> Option<Self::R> {
self.walk_impl(node)
}
fn walk_impl(&mut self, node: NodeRef) -> Option<Self::R> {
use SymName::*;
use view::*;
match node.open_kind()? {
TreeKind::Body => self.scoped_children(node),
TreeKind::StmtModule => StmtModule::cast(node)
.name()
.and_then(|n| self.def(node, Value(n.str())))
.or_else(|| self.scoped_children(node)),
TreeKind::ModuleParam => ModuleParam::cast(node)
.typed_name()
.and_then(|n| n.name())
.and_then(|n| self.def(node, SymName::Value(n.str())))
.or_else(|| self.walk_children(node)),
TreeKind::ModulePortParam => ModulePortParam::cast(node)
.name()
.and_then(|n| self.def(node, SymName::Value(n.str())))
.or_else(|| self.walk_children(node)),
TreeKind::StmtFunction => {
let fun = StmtFunction::cast(node);
fun.typed_name()
.and_then(|n| n.name())
.and_then(|n| self.def(node, Value(n.str())))
.or_else(|| {
self.scope(node, |slf| {
fun.param_list()
.and_then(|l| {
l.params().find_map(|p| {
p.typed_name()
.and_then(|n| n.name())
.and_then(|n| slf.def(p.syntax(), Value(n.str())))
.or_else(|| slf.walk_children(node))
})
})
.or_else(|| slf.walk_children(node))
})
})
}
TreeKind::StmtMethod => {
let fun = StmtMethod::cast(node);
fun.typed_name()
.and_then(|n| n.name())
.and_then(|n| self.def(node, Value(n.str())))
.or_else(|| {
self.scope(node, |slf| {
fun.param_list()
.and_then(|l| {
l.params().find_map(|p| {
p.typed_name()
.and_then(|n| n.name())
.and_then(|n| slf.def(p.syntax(), Value(n.str())))
.or_else(|| slf.walk_children(node))
})
})
.or_else(|| slf.walk_children(node))
})
})
}
TreeKind::VarInit => VarInit::cast(node)
.target()
.and_then(Either::left)
.and_then(|n| self.def(node, Value(n.str())))
.or_else(|| self.walk_children(node)),
TreeKind::DeclPattern => DeclPattern::cast(node)
.name()
.and_then(Either::right)
.and_then(|n| self.def(node, Value(n.str())))
.or_else(|| self.walk_children(node)),
TreeKind::StmtInterface => StmtInterface::cast(node)
.name()
.and_then(|n| self.def(node, Type(n.str())))
.or_else(|| self.scoped_children(node)),
TreeKind::StmtTypedef => StmtTypedef::cast(node)
.kind()
.and_then(|kind| match kind {
TypedefKind::Enum(tydef) => tydef
.name()
.and_then(|n| self.def(tydef.syntax(), SymName::Type(n.str()))),
TypedefKind::Struct(tydef) => tydef
.name()
.and_then(|n| self.def(tydef.syntax(), SymName::Type(n.str()))),
TypedefKind::Union(tydef) => tydef
.name()
.and_then(|n| self.def(tydef.syntax(), SymName::Type(n.str()))),
TypedefKind::Alias(tydef) => tydef
.name()
.and_then(|n| self.def(tydef.syntax(), SymName::Type(n.str()))),
})
.or_else(|| self.scoped_children(node)),
TreeKind::TypedefEnum => {
let tydef = TypedefEnum::cast(node);
tydef
.param_list()
.and_then(|n| self.walk(n.syntax()))
.or_else(|| tydef.derives().and_then(|n| self.walk(n.syntax())))
.or_else(|| tydef.variant_list().and_then(|n| self.walk(n.syntax())))
}
TreeKind::TypedefStruct => {
let tydef = TypedefStruct::cast(node);
tydef
.param_list()
.and_then(|n| self.walk(n.syntax()))
.or_else(|| tydef.derives().and_then(|n| self.walk(n.syntax())))
.or_else(|| tydef.field_list().and_then(|n| self.walk(n.syntax())))
}
TreeKind::TypedefUnion => {
let tydef = TypedefUnion::cast(node);
tydef
.param_list()
.and_then(|n| self.walk(n.syntax()))
.or_else(|| tydef.derives().and_then(|n| self.walk(n.syntax())))
.or_else(|| tydef.field_list().and_then(|n| self.walk(n.syntax())))
}
TreeKind::TypedefAlias => {
let tydef = TypedefAlias::cast(node);
tydef
.param_list()
.and_then(|n| self.walk(n.syntax()))
.or_else(|| tydef.derives().and_then(|n| self.walk(n.syntax())))
.or_else(|| tydef.definition().and_then(|n| self.walk(n.syntax())))
}
TreeKind::TypedefParam => TypedefParam::cast(node)
.name()
.and_then(|n| self.def(node, SymName::Type(n.str())))
.or_else(|| self.walk_children(node)),
TreeKind::StmtInstance => self.scoped_children(node),
TreeKind::StmtTypeclass => StmtTypeclass::cast(node)
.name()
.and_then(|n| self.def(node, SymName::Type(n.str())))
.or_else(|| self.scoped_children(node)),
TreeKind::ExprVar => ExprVar::cast(node)
.id()
.and_then(|n| self.link_qual(n, false))
.or_else(|| self.walk_children(node)),
TreeKind::TypeCons => TypeCons::cast(node)
.id()
.and_then(|n| self.link_qual(n, true))
.or_else(|| self.walk_children(node)),
TreeKind::TypeVar => self
.ty_var(TypeVar::cast(node))
.or_else(|| self.walk_children(node)),
TreeKind::QualId => QualId::cast(node)
.pkg()
.and_then(|n| self.link(n.syntax(), None, SymName::Pkg(n.str())))
.or_else(|| self.walk_children(node)),
TreeKind::ImportItem => ImportItem::cast(node)
.name()
.and_then(|n| self.link(n.syntax(), None, SymName::Pkg(n.str())))
.or_else(|| self.walk_children(node)),
TreeKind::ExportItem => ExportItem::cast(node).kind().and_then(|k| match k {
ExportItemKind::Def(ident) => self
.link(ident.syntax(), None, SymName::Value(ident.str()))
.or_else(|| self.link(ident.syntax(), None, SymName::Type(ident.str()))),
ExportItemKind::Package(ident) => {
self.link(ident.syntax(), None, SymName::Pkg(ident.str()))
}
ExportItemKind::Struct(ident) => {
self.link(ident.syntax(), None, SymName::Type(ident.str()))
}
}),
TreeKind::Error => None,
_ => self.walk_children(node),
}
}
fn walk_children(&mut self, node: NodeRef) -> Option<Self::R> {
node.children().find_map(|child| self.walk(child))
}
fn scoped_children(&mut self, node: NodeRef) -> Option<Self::R> {
self.scope(node, |slf| slf.walk_children(node))
}
}