use arcstr::Substr;
use crate::{
syntax::{cst::kind::TreeKind, lexer::token::{Kw, Sym, TokenKind}, parser::token::Tt},
util::data::either::Either,
};
use super::tree::NodeRef;
pub trait View<'cst>: Sized {
fn try_cast(node: NodeRef<'cst>) -> Option<Self>;
fn syntax(&self) -> NodeRef<'cst>;
fn cast(node: NodeRef<'cst>) -> Self {
Self::try_cast(node).expect("cast called on mismatching node")
}
fn tt(&self, tt: impl Into<Tt>) -> Option<NodeRef<'cst>> {
self.syntax().children().find_tt(tt)
}
fn tts(&self, tt: impl Into<Tt>) -> impl Iterator<Item = NodeRef<'cst>> {
let tt = tt.into();
self.syntax().children().filter(move |n| n.is_tt(tt))
}
}
macro_rules! simple_accessor {
($name:ident) => {
#[derive(Debug, Clone, Copy)]
pub struct $name<'cst>(NodeRef<'cst>);
impl<'cst> View<'cst> for $name<'cst> {
fn try_cast(node: NodeRef<'cst>) -> Option<Self> {
if !node.is_open(TreeKind::$name) {
return None;
}
Some(Self(node))
}
fn syntax(&self) -> NodeRef<'cst> {
self.0
}
}
};
}
simple_accessor!(Package);
impl<'cst> Package<'cst> {
pub fn name(&self) -> Option<Ident<'cst>> {
self.0
.children()
.find_kind(TreeKind::StartClause)
.and_then(|n| n.children().find_map(Ident::try_cast))
}
pub fn end_name(&self) -> Option<Ident<'cst>> {
self.0
.children()
.find_kind(TreeKind::EndClause)
.and_then(|n| n.children().find_map(Ident::try_cast))
}
pub fn body(&self) -> Option<Body<'cst>> {
self.0.children().find_map(Body::try_cast)
}
}
simple_accessor!(Body);
impl<'cst> Body<'cst> {
pub fn stmts(&self) -> impl Iterator<Item = Stmt<'cst>> {
self.0.children().filter_map(Stmt::try_cast)
}
}
simple_accessor!(Stmt);
impl<'cst> Stmt<'cst> {
pub fn kind(&self) -> Option<StmtKind<'cst>> {
let stmt = self.0.children().find_kind_where(TreeKind::is_stmt)?;
match stmt.open_kind()? {
TreeKind::StmtFunction => StmtFunction::try_cast(stmt).map(StmtKind::Function),
TreeKind::StmtImport => StmtImport::try_cast(stmt).map(StmtKind::Import),
TreeKind::StmtExport => StmtExport::try_cast(stmt).map(StmtKind::Export),
TreeKind::StmtExprOrBind => StmtExprOrBind::try_cast(stmt).map(StmtKind::ExprOrBind),
TreeKind::StmtInterface => StmtInterface::try_cast(stmt).map(StmtKind::Interface),
TreeKind::StmtModule => StmtModule::try_cast(stmt).map(StmtKind::Module),
TreeKind::StmtTypedef => StmtTypedef::try_cast(stmt).map(StmtKind::Typedef),
TreeKind::StmtInstance => StmtInstance::try_cast(stmt).map(StmtKind::Instance),
TreeKind::StmtTypeclass => StmtTypeclass::try_cast(stmt).map(StmtKind::Typeclass),
_ => None,
}
}
}
#[derive(Debug)]
pub enum StmtKind<'cst> {
Function(StmtFunction<'cst>),
Method(StmtMethod<'cst>),
Import(StmtImport<'cst>),
Export(StmtExport<'cst>),
ExprOrBind(StmtExprOrBind<'cst>),
Interface(StmtInterface<'cst>),
Module(StmtModule<'cst>),
Typedef(StmtTypedef<'cst>),
Instance(StmtInstance<'cst>),
Typeclass(StmtTypeclass<'cst>),
}
simple_accessor!(StmtTypeclass);
impl<'cst> StmtTypeclass<'cst> {
pub fn name(&self) -> Option<Ident<'cst>> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(StmtInstance);
simple_accessor!(StmtTypedef);
impl StmtTypedef<'_> {
pub fn kind(&self) -> Option<TypedefKind> {
self.0.children().find_map(|node| match node.open_kind()? {
TreeKind::TypedefEnum => TypedefEnum::try_cast(node).map(TypedefKind::Enum),
TreeKind::TypedefStruct => TypedefStruct::try_cast(node).map(TypedefKind::Struct),
TreeKind::TypedefUnion => TypedefUnion::try_cast(node).map(TypedefKind::Union),
TreeKind::TypedefAlias => TypedefAlias::try_cast(node).map(TypedefKind::Alias),
_ => None,
})
}
}
pub enum TypedefKind<'cst> {
Enum(TypedefEnum<'cst>),
Struct(TypedefStruct<'cst>),
Union(TypedefUnion<'cst>),
Alias(TypedefAlias<'cst>),
}
simple_accessor!(TypedefEnum);
impl<'cst> TypedefEnum<'cst> {
pub fn name(&self) -> Option<Ident> {
self.0.children().find_map(Ident::try_cast)
}
pub fn param_list(&self) -> Option<TypedefParamList<'cst>> {
self.0.children().find_map(TypedefParamList::try_cast)
}
pub fn derives(&self) -> Option<Derives<'cst>> {
self.0.children().find_map(Derives::try_cast)
}
pub fn variant_list(&self) -> Option<EnumVariantList<'cst>> {
self.0.children().find_map(EnumVariantList::try_cast)
}
}
simple_accessor!(TypedefStruct);
impl<'cst> TypedefStruct<'cst> {
pub fn name(&self) -> Option<Ident> {
self.0.children().find_map(Ident::try_cast)
}
pub fn param_list(&self) -> Option<TypedefParamList<'cst>> {
self.0.children().find_map(TypedefParamList::try_cast)
}
pub fn derives(&self) -> Option<Derives<'cst>> {
self.0.children().find_map(Derives::try_cast)
}
pub fn field_list(&self) -> Option<TypedefFieldList<'cst>> {
self.0.children().find_map(TypedefFieldList::try_cast)
}
}
simple_accessor!(TypedefUnion);
impl<'cst> TypedefUnion<'cst> {
pub fn name(&self) -> Option<Ident> {
self.0.children().find_map(Ident::try_cast)
}
pub fn param_list(&self) -> Option<TypedefParamList<'cst>> {
self.0.children().find_map(TypedefParamList::try_cast)
}
pub fn derives(&self) -> Option<Derives<'cst>> {
self.0.children().find_map(Derives::try_cast)
}
pub fn field_list(&self) -> Option<TypedefFieldList<'cst>> {
self.0.children().find_map(TypedefFieldList::try_cast)
}
}
simple_accessor!(TypedefAlias);
impl<'cst> TypedefAlias<'cst> {
pub fn name(&self) -> Option<Ident> {
self.0.children().find_map(Ident::try_cast)
}
pub fn param_list(&self) -> Option<TypedefParamList<'cst>> {
self.0.children().find_map(TypedefParamList::try_cast)
}
pub fn derives(&self) -> Option<Derives<'cst>> {
self.0.children().find_map(Derives::try_cast)
}
pub fn definition(&self) -> Option<TypeExpr<'cst>> {
self.0.children().find_map(TypeExpr::try_cast)
}
}
simple_accessor!(TypedefFieldList);
simple_accessor!(EnumVariantList);
simple_accessor!(StmtModule);
impl StmtModule<'_> {
pub fn name(&self) -> Option<Ident> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(ModuleParam);
impl<'cst> ModuleParam<'cst> {
pub fn typed_name(&self) -> Option<TypedName<'cst>> {
self.0.children().find_map(TypedName::try_cast)
}
}
simple_accessor!(ModulePortParam);
impl<'cst> ModulePortParam<'cst> {
pub fn name(&self) -> Option<Ident<'cst>> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(StmtInterface);
impl StmtInterface<'_> {
pub fn name(&self) -> Option<Ident> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(StmtExprOrBind);
impl StmtExprOrBind<'_> {
pub fn bind(&self) -> Option<ExprOrBind> {
self.0.children().find_map(ExprOrBind::try_cast)
}
}
#[derive(Debug)]
pub struct ExprOrBind<'cst>(NodeRef<'cst>);
impl<'cst> View<'cst> for ExprOrBind<'cst> {
fn try_cast(node: NodeRef<'cst>) -> Option<Self> {
use TreeKind::*;
match node.open_kind()? {
BindAssign | BindDecl | BindMatch => Some(Self(node)),
kind if kind.is_expr() => Some(Self(node)),
_ => None,
}
}
fn syntax(&self) -> NodeRef<'cst> {
self.0
}
}
impl<'cst> ExprOrBind<'cst> {
pub fn expr_or_bind(&self) -> Option<Either<Expr<'cst>, BindKind<'cst>>> {
match self.0.open_kind()? {
TreeKind::BindDecl => BindDecl::try_cast(self.0)
.map(BindKind::Decl)
.map(Either::Right),
kind if kind.is_expr() => Expr::try_cast(self.0).map(Either::Left),
_ => None,
}
}
}
pub enum BindKind<'cst> {
Decl(BindDecl<'cst>),
}
simple_accessor!(StmtExport);
impl StmtExport<'_> {
pub fn items(&self) -> impl Iterator<Item = ExportItem> {
self.0.children().filter_map(ExportItem::try_cast)
}
}
simple_accessor!(ExportItem);
impl<'cst> ExportItem<'cst> {
pub fn kind(&self) -> Option<ExportItemKind<'cst>> {
let id = self.0.children().find_map(Ident::try_cast)?;
if self.0.children().find_tt(Sym::ColonColon).is_some() {
Some(ExportItemKind::Package(id))
} else if self.0.children().find_tt(Sym::Lparen).is_some() {
Some(ExportItemKind::Struct(id))
} else {
Some(ExportItemKind::Def(id))
}
}
}
pub enum ExportItemKind<'cst> {
Def(Ident<'cst>),
Package(Ident<'cst>),
Struct(Ident<'cst>),
}
simple_accessor!(BindDecl);
impl<'cst> BindDecl<'cst> {
pub fn ty(&self) -> Option<Either<(), TypeExpr<'cst>>> {
self.0
.children()
.find_map(TypeExpr::try_cast)
.map(Either::Right)
.or_else(|| self.0.children().find_tt(Kw::Let).map(|_| Either::Left(())))
}
pub fn init_list(&self) -> Option<VarInitList<'cst>> {
self.0.children().find_map(VarInitList::try_cast)
}
}
simple_accessor!(StmtImport);
impl StmtImport<'_> {
pub fn items(&self) -> impl Iterator<Item = ImportItem> {
self.0.children().filter_map(ImportItem::try_cast)
}
}
simple_accessor!(ImportItem);
impl<'cst> ImportItem<'cst> {
pub fn name(&self) -> Option<Ident<'cst>> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(VarInitList);
impl<'cst> VarInitList<'cst> {
pub fn inits(&self) -> impl Iterator<Item = VarInit<'cst>> {
self.0.children().filter_map(VarInit::try_cast)
}
}
simple_accessor!(VarInit);
impl<'cst> VarInit<'cst> {
pub fn target(&self) -> Option<Either<Ident, DeclPatternList>> {
self.0
.children()
.find_map(Ident::try_cast)
.map(Either::Left)
.or_else(|| {
self.0
.children()
.find_map(DeclPatternList::try_cast)
.map(Either::Right)
})
}
pub fn init(&self) -> Option<Expr<'cst>> {
self.0.children().find_map(Expr::try_cast)
}
}
simple_accessor!(DeclPatternList);
impl DeclPatternList<'_> {
pub fn elems(&self) -> impl Iterator<Item = DeclPattern> {
self.0.children().filter_map(DeclPattern::try_cast)
}
}
simple_accessor!(DeclPattern);
impl DeclPattern<'_> {
pub fn name(&self) -> Option<Either<(), Ident>> {
self.0
.children()
.find_map(Ident::try_cast)
.map(Either::Right)
.or_else(|| {
self.0
.children()
.find_tt(Sym::DotStar)
.map(|_| Either::Left(()))
})
}
}
simple_accessor!(StmtFunction);
impl<'cst> StmtFunction<'cst> {
pub fn typed_name(&self) -> Option<TypedFunName<'cst>> {
self.0.children().find_map(TypedFunName::try_cast)
}
pub fn param_list(&self) -> Option<FunctionParamList<'cst>> {
self.0.children().find_map(FunctionParamList::try_cast)
}
pub fn body(&self) -> Option<Either<Expr<'cst>, Body<'cst>>> {
self.0
.children()
.find_map(|n| Expr::try_cast(n).map(Either::Left))
.or_else(|| {
self.0
.children()
.find_map(Body::try_cast)
.map(Either::Right)
})
}
}
simple_accessor!(StmtMethod);
impl<'cst> StmtMethod<'cst> {
pub fn typed_name(&self) -> Option<TypedFunName<'cst>> {
self.0.children().find_map(TypedFunName::try_cast)
}
pub fn param_list(&self) -> Option<FunctionParamList<'cst>> {
self.0.children().find_map(FunctionParamList::try_cast)
}
}
simple_accessor!(TypedFunName);
impl<'cst> TypedFunName<'cst> {
pub fn name(&self) -> Option<Ident<'cst>> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(FunctionParamList);
impl<'cst> FunctionParamList<'cst> {
pub fn params(&self) -> impl Iterator<Item = FunctionParam<'cst>> {
self.0.children().filter_map(FunctionParam::try_cast)
}
}
simple_accessor!(FunctionParam);
impl<'cst> FunctionParam<'cst> {
pub fn typed_name(&self) -> Option<TypedName<'cst>> {
self.0.children().find_map(TypedName::try_cast)
}
}
#[derive(Debug)]
pub struct Expr<'cst>(NodeRef<'cst>);
impl<'cst> View<'cst> for Expr<'cst> {
fn try_cast(node: NodeRef<'cst>) -> Option<Self> {
if !node.is_open_where(TreeKind::is_expr) {
return None;
}
Some(Self(node))
}
fn syntax(&self) -> NodeRef<'cst> {
self.0
}
}
impl<'cst> Expr<'cst> {
pub fn kind(&self) -> Option<ExprKind<'cst>> {
match self.0.open_kind()? {
TreeKind::ExprVar => ExprVar::try_cast(self.0).map(ExprKind::Var),
TreeKind::ExprBinary => ExprBinary::try_cast(self.0).map(ExprKind::Binary),
TreeKind::ExprPrefixOp => ExprPrefixOp::try_cast(self.0).map(ExprKind::PrefixOp),
TreeKind::ExprStructInit => ExprStructInit::try_cast(self.0).map(ExprKind::StructInit),
_ => None,
}
}
}
pub enum ExprKind<'cst> {
Var(ExprVar<'cst>),
Binary(ExprBinary<'cst>),
PrefixOp(ExprPrefixOp<'cst>),
StructInit(ExprStructInit<'cst>),
}
simple_accessor!(ExprBinary);
impl<'cst> ExprBinary<'cst> {
pub fn op(&self) -> Option<NodeRef<'cst>> {
self.0.children().find(|n| {
n.token()
.map(|t| matches!(t.kind, TokenKind::Sym(_)))
.unwrap_or(false)
})
}
}
simple_accessor!(ExprPrefixOp);
impl<'cst> ExprPrefixOp<'cst> {
pub fn op(&self) -> Option<NodeRef<'cst>> {
self.0.children().find(|n| {
n.token()
.map(|t| matches!(t.kind, TokenKind::Sym(_)))
.unwrap_or(false)
})
}
}
simple_accessor!(ExprStructInit);
simple_accessor!(ExprVar);
impl<'cst> ExprVar<'cst> {
pub fn id(&self) -> Option<QualId<'cst>> {
self.0.children().find_map(QualId::try_cast)
}
}
pub struct TypeExpr<'cst>(NodeRef<'cst>);
impl<'cst> View<'cst> for TypeExpr<'cst> {
fn try_cast(node: NodeRef<'cst>) -> Option<Self> {
if !node.is_open_where(TreeKind::is_type) {
return None;
}
Some(Self(node))
}
fn syntax(&self) -> NodeRef<'cst> {
self.0
}
}
impl<'cst> TypeExpr<'cst> {
pub fn kind(&self) -> Option<TypeKind<'cst>> {
match self.0.open_kind()? {
TreeKind::TypeCons => TypeCons::try_cast(self.0).map(TypeKind::Cons),
TreeKind::TypeVar => TypeVar::try_cast(self.0).map(TypeKind::Var),
_ => None,
}
}
}
pub enum TypeKind<'cst> {
Cons(TypeCons<'cst>),
Var(TypeVar<'cst>),
}
simple_accessor!(TypeCons);
impl<'cst> TypeCons<'cst> {
pub fn id(&self) -> Option<QualId<'cst>> {
self.0.children().find_map(QualId::try_cast)
}
}
simple_accessor!(TypeVar);
impl<'cst> TypeVar<'cst> {
pub fn name(&self) -> Option<Ident<'cst>> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(TypeArgList);
simple_accessor!(QualId);
impl<'cst> QualId<'cst> {
pub fn pkg(&self) -> Option<Ident<'cst>> {
self.0
.children()
.find_kind(TreeKind::PkgId)
.and_then(|n| n.children().find_map(Ident::try_cast))
}
pub fn colon_colon(&self) -> Option<NodeRef> {
self.0
.children()
.find_kind(TreeKind::PkgId)
.and_then(|n| n.children().find_tt(Sym::ColonColon))
}
pub fn name(&self) -> Option<Ident<'cst>> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(TypedName);
impl<'cst> TypedName<'cst> {
pub fn ty(&self) -> Option<Either<TypeExpr<'cst>, FunctionProto<'cst>>> {
self.0
.children()
.find_map(FunctionProto::try_cast)
.map(Either::Right)
.or_else(|| {
self.0
.children()
.find_map(TypeExpr::try_cast)
.map(Either::Left)
})
}
pub fn name(&self) -> Option<Ident<'cst>> {
self.0
.children()
.find_map(FunctionProto::try_cast)
.and_then(|f| f.name())
.or_else(|| self.0.children().find_map(Ident::try_cast))
}
}
simple_accessor!(FunctionProto);
impl<'cst> FunctionProto<'cst> {
pub fn name(&self) -> Option<Ident<'cst>> {
self.0.children().find_map(Ident::try_cast)
}
pub fn param_list(&self) -> Option<FunctionParamList<'cst>> {
self.0.children().find_map(FunctionParamList::try_cast)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Ident<'cst>(NodeRef<'cst>);
impl<'cst> View<'cst> for Ident<'cst> {
fn try_cast(node: NodeRef<'cst>) -> Option<Self> {
if !node.is_tt(Tt::Ident) {
return None;
}
Some(Self(node))
}
fn syntax(&self) -> NodeRef<'cst> {
self.0
}
}
impl Ident<'_> {
pub fn str(&self) -> Substr {
self.0
.token()
.and_then(|t| t.kind.ident())
.expect("Ident accessor created on non-ident node")
.clone()
}
}
simple_accessor!(AttributeList);
simple_accessor!(Attribute);
simple_accessor!(AttributeName);
simple_accessor!(AttributeValue);
simple_accessor!(AttributeValueList);
simple_accessor!(SoftKw);
simple_accessor!(TypedefParamList);
simple_accessor!(TypedefParam);
impl<'cst> TypedefParam<'cst> {
pub fn name(&self) -> Option<Ident<'cst>> {
self.0.children().find_map(Ident::try_cast)
}
}
simple_accessor!(Derives);