skullbrain-parser 0.1.0

Parses skullbrain tokens into a concrete syntax tree (CST).
Documentation
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) => {
        /// Returns the token `$Tok`, if present.
        $vis fn $TokMethod(&self) -> Option<$Tok> {
            crate::cst::utils::token(&self.syntax, $KindTok)
        }
    };

    // Main macro without kind
    (
        $(#[$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,
        }

        // Generate token structs (only when $Tok is specified)
        $(
            $(
                crate::cst::utils::define_cst_token! {
                    $(#[$tok_meta])*
                    $vis struct $Tok {
                        kind: $KindTok
                    }
                }
            )*
        )?

        impl $Name {
            $(
                $(
                    /// Returns the child `$Child` node, if present.
                    $vis fn $Child(&self) -> Option<$ReturnNode> {
                        crate::cst::utils::child(&self.syntax)
                    }
                )*
            )?

            $(
                $(
                    /// Returns the child `$ForwardMapNode` node, if present.
                    $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);
                )*
            )?
        }
    };

    // Main macro with kind
    (
        $(#[$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};

/// Utility functions for extracting children and tokens
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()))
}

/// Forward to the first child node of a specific wrapper type
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))
}

/// Iterator over typed AST children
#[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()))
    }
}