use super::*;
use std::marker::PhantomData;
macro_rules! define_cst_token {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident {}
) => {
$(#[$meta])*
$vis struct $name {
pub(super) token: SyntaxToken,
}
impl std::ops::Deref for $name {
type Target = SyntaxToken;
fn deref(&self) -> &Self::Target {
&self.token
}
}
};
(
$(#[$meta:meta])*
$vis:vis struct $name:ident {
kind: $kind:expr
}
) => {
define_cst_token! {
$(#[$meta])*
$vis struct $name {}
}
impl CstToken for $name {
fn can_cast(kind: SyntaxKind) -> bool {
kind == $kind
}
fn token(&self) -> &SyntaxToken {
&self.token
}
fn cast(token: SyntaxToken) -> Option<Self> {
Self::can_cast(token.kind()).then(|| Self { token })
}
}
};
}
macro_rules! define_cst_token {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident {}
) => {
$(#[$meta])*
$vis struct $name {
pub(super) token: SyntaxToken,
}
impl std::ops::Deref for $name {
type Target = SyntaxToken;
fn deref(&self) -> &Self::Target {
&self.token
}
}
};
(
$(#[$meta:meta])*
$vis:vis struct $name:ident {
kind: $kind:expr
}
) => {
define_cst_token! {
$(#[$meta])*
$vis struct $name {}
}
impl CstToken for $name {
fn can_cast(kind: SyntaxKind) -> bool {
kind == $kind
}
fn token(&self) -> &SyntaxToken {
&self.token
}
fn cast(token: SyntaxToken) -> Option<Self> {
Self::can_cast(token.kind()).then(|| Self { token })
}
}
};
}
macro_rules! define_cst_node {
(@token_method $(#[$tok_meta:meta])* $vis:vis $TokMethod:ident $Tok:ident $KindTok:expr) => {
$vis fn $TokMethod(&self) -> Option<$Tok> {
crate::cst::utils::token(&self.syntax, $KindTok)
}
};
(
$(#[$meta:meta])*
$vis:vis struct $Name:ident {
$( child: [ $( $Child:ident => $ReturnNode:ty ),* ], )?
$( tokens: [ $( $(#[$tok_meta:meta])* $TokMethod:ident $Tok:ident => $KindTok:expr ),* ], )?
$( nogen tokens : [
$(
$NoGenTokMethod:ident $NoGenTok:ident => $NoGenKindTok:expr
),*
],)?
$( forward: [ $( $ForwardMethod:ident $ForwardKind:expr => $ForwardMapNode:ty ),* ],)?
}
) => {
$(#[$meta])*
$vis struct $Name {
pub(super) syntax: SyntaxNode,
}
$(
$(
crate::cst::utils::define_cst_token! {
$(#[$tok_meta])*
$vis struct $Tok {
kind: $KindTok
}
}
)*
)?
impl $Name {
$(
$(
$vis fn $Child(&self) -> Option<$ReturnNode> {
crate::cst::utils::child(&self.syntax)
}
)*
)?
$(
$(
$vis fn $ForwardMethod(&self) -> Option<$ForwardMapNode> {
crate::cst::utils::forward_through(&self.syntax, $ForwardKind)
}
)*
)?
$(
$(
define_cst_node!(@token_method $(#[$tok_meta])* $vis $TokMethod $Tok $KindTok);
)*
)?
$(
$(
define_cst_node!(@token_method $vis $NoGenTokMethod $NoGenTok $NoGenKindTok);
)*
)?
}
};
(
$(#[$meta:meta])*
$vis:vis struct $Name:ident {
kind: $KIND:expr,
$( child: [ $( $Child:ident => $ReturnNode:ty ),* ], )?
$(
tokens: [
$(
$(#[$tok_meta:meta])* $TokMethod:ident $Tok:ident => $KindTok:expr
),*
],
)?
$(nogen tokens : [
$(
$NoGenTokMethod:ident $NoGenTok:ident => $NoGenKindTok:expr
),*
],)?
$( forward: [ $( $ForwardMethod:ident $ForwardKind:expr => $ForwardMapNode:ty ),* ],)?
}
) => {
define_cst_node! {
$(#[$meta])*
$vis struct $Name {
$( child: [ $( $Child => $ReturnNode ),* ], )?
$( tokens: [ $( $(#[$tok_meta])* $TokMethod $Tok => $KindTok ),* ], )?
$( nogen tokens: [ $( $NoGenTokMethod $NoGenTok => $NoGenKindTok ),* ], )?
$( forward: [ $( $ForwardMethod $ForwardKind => $ForwardMapNode ),* ],)?
}
}
impl CstNode for $Name {
fn can_cast(kind: SyntaxKind) -> bool {
kind == $KIND
}
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
Self::can_cast(syntax.kind()).then(|| Self { syntax })
}
}
};
}
pub(super) use {define_cst_node, define_cst_token};
pub(super) fn child<N: CstNode>(parent: &SyntaxNode) -> Option<N> {
children(parent).next()
}
pub(super) fn children<N: CstNode>(parent: &'_ SyntaxNode) -> AstChildren<'_, N> {
AstChildren::<N>::new(parent)
}
pub(super) fn token<T: CstToken>(parent: &SyntaxNode, kind: SyntaxKind) -> Option<T> {
let tok = token_with(parent, |it| it == kind)?;
T::cast(tok.clone())
}
pub(super) fn token_with(
parent: &SyntaxNode,
is: impl Fn(SyntaxKind) -> bool,
) -> Option<&SyntaxToken> {
parent
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|it| is(it.kind()))
}
pub(super) fn forward_through<T: CstNode>(parent: &SyntaxNode, kind: SyntaxKind) -> Option<T> {
parent
.children()
.find(|i| i.kind() == kind)
.and_then(|n| child(n))
}
#[derive(Debug, Clone)]
pub(super) struct AstChildren<'a, N> {
inner: SyntaxNodeChildren<'a>,
ph: PhantomData<N>,
}
impl<'a, N> AstChildren<'a, N> {
fn new(parent: &'a SyntaxNode) -> Self {
AstChildren {
inner: parent.children(),
ph: PhantomData,
}
}
}
impl<N: CstNode> Iterator for AstChildren<'_, N> {
type Item = N;
fn next(&mut self) -> Option<N> {
self.inner.find_map(|n| N::cast(n.clone()))
}
}