lelwel 0.10.4

Resilient LL(1) parser generator
Documentation
use crate::frontend::lexer::Token;
use crate::{Cst, CstChildren, Node, NodeRef, Rule, Span};

pub trait AstNode {
    fn cast(cst: &Cst<'_>, syntax: NodeRef) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> NodeRef;

    fn span(&self, cst: &Cst<'_>) -> Span {
        cst.span(self.syntax())
    }
}

macro_rules! ast_node {
    ($node_name:ident) => {
        #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone, Ord, PartialOrd)]
        pub struct $node_name {
            syntax: NodeRef,
        }
        impl AstNode for $node_name {
            fn cast(cst: &Cst<'_>, syntax: NodeRef) -> Option<Self> {
                match cst.get(syntax) {
                    Node::Rule(Rule::$node_name, _) => Some(Self { syntax }),
                    _ => None,
                }
            }
            fn syntax(&self) -> NodeRef {
                self.syntax
            }
        }
    };
    ($node_name:ident, ($($node_names:ident),+)) => {
        #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone, Ord, PartialOrd)]
        pub enum $node_name {
            $($node_names($node_names),)*
        }
        impl AstNode for $node_name {
            fn cast(cst: &Cst<'_>, syntax: NodeRef) -> Option<Self> {
                $(
                if let Some(node) = $node_names::cast(cst, syntax) {
                    return Some(Self::$node_names(node));
                }
                )*
                return None;
            }
            fn syntax(&self) -> NodeRef {
                match self {
                    $(Self::$node_names(node) => node.syntax,)*
                }
            }
        }
    }
}

ast_node!(File);
ast_node!(
    Decl,
    (
        TokenDecl, RuleDecl, StartDecl, RightDecl, SkipDecl, PartDecl
    )
);
ast_node!(TokenDecl);
ast_node!(RuleDecl);
ast_node!(StartDecl);
ast_node!(RightDecl);
ast_node!(SkipDecl);
ast_node!(PartDecl);
ast_node!(
    Regex,
    (
        OrderedChoice,
        Alternation,
        Concat,
        Paren,
        Optional,
        Star,
        Plus,
        Name,
        Symbol,
        Predicate,
        Action,
        Assertion,
        NodeRename,
        NodeElision,
        NodeMarker,
        NodeCreation,
        Commit,
        Return
    )
);
ast_node!(OrderedChoice);
ast_node!(Alternation);
ast_node!(Concat);
ast_node!(Paren);
ast_node!(Optional);
ast_node!(Star);
ast_node!(Plus);
ast_node!(Name);
ast_node!(Symbol);
ast_node!(Predicate);
ast_node!(Action);
ast_node!(Assertion);
ast_node!(NodeRename);
ast_node!(NodeElision);
ast_node!(NodeMarker);
ast_node!(NodeCreation);
ast_node!(Commit);
ast_node!(Return);

impl Cst<'_> {
    fn child_node<T: AstNode>(&self, syntax: NodeRef) -> Option<T> {
        self.children(syntax).find_map(|c| T::cast(self, c))
    }
    fn child_node_iter<T: AstNode>(
        &self,
        syntax: NodeRef,
    ) -> std::iter::FilterMap<CstChildren<'_>, impl FnMut(NodeRef) -> Option<T> + '_> {
        self.children(syntax).filter_map(|c| T::cast(self, c))
    }
    fn child_token(&self, syntax: NodeRef, token: Token) -> Option<(&str, Span)> {
        self.children(syntax)
            .find_map(|c| self.match_token(c, token))
    }
}

pub trait Named: AstNode {
    fn name<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)>;
}
impl File {
    #[allow(clippy::type_complexity, clippy::filter_map_bool_then)]
    pub fn token_decls<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<
        std::iter::Flatten<
            std::iter::FilterMap<
                CstChildren<'a>,
                impl FnMut(NodeRef) -> Option<CstChildren<'a>> + use<'a>,
            >,
        >,
        impl FnMut(NodeRef) -> Option<TokenDecl> + 'a + use<'a>,
    > {
        cst.children(self.syntax)
            .filter_map(|c| cst.match_rule(c, Rule::TokenList).then(|| cst.children(c)))
            .flatten()
            .filter_map(|c| TokenDecl::cast(cst, c))
    }
    pub fn rule_decls<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<CstChildren<'a>, impl FnMut(NodeRef) -> Option<RuleDecl> + 'a + use<'a>>
    {
        cst.child_node_iter(self.syntax)
    }
    pub fn start_decls<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<
        CstChildren<'a>,
        impl FnMut(NodeRef) -> Option<StartDecl> + 'a + use<'a>,
    > {
        cst.child_node_iter(self.syntax)
    }
    pub fn right_decls<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<
        CstChildren<'a>,
        impl FnMut(NodeRef) -> Option<RightDecl> + 'a + use<'a>,
    > {
        cst.child_node_iter(self.syntax)
    }
    pub fn skip_decls<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<CstChildren<'a>, impl FnMut(NodeRef) -> Option<SkipDecl> + 'a + use<'a>>
    {
        cst.child_node_iter(self.syntax)
    }
    pub fn part_decls<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<CstChildren<'a>, impl FnMut(NodeRef) -> Option<PartDecl> + 'a + use<'a>>
    {
        cst.child_node_iter(self.syntax)
    }
}
impl Named for TokenDecl {
    fn name<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Id)
    }
}
impl TokenDecl {
    pub fn symbol<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Str)
    }
}
impl Named for RuleDecl {
    fn name<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Id)
    }
}
impl RuleDecl {
    pub fn regex(&self, cst: &Cst<'_>) -> Option<Regex> {
        cst.child_node(self.syntax)
    }
    pub fn elision<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Hat)
    }
    pub fn is_elided(&self, cst: &Cst<'_>) -> bool {
        self.elision(cst).is_some()
    }
}
impl StartDecl {
    pub fn rule_name<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Id)
    }
}
impl RightDecl {
    pub fn token_names<'a, F: FnMut((&'a str, Span))>(&self, cst: &'a Cst<'_>, f: F) {
        cst.children(self.syntax)
            .filter_map(|c| {
                cst.match_token(c, Token::Id)
                    .or_else(|| cst.match_token(c, Token::Str))
            })
            .for_each(f);
    }
}
impl SkipDecl {
    pub fn token_names<'a, F: FnMut((&'a str, Span))>(&self, cst: &'a Cst<'_>, f: F) {
        cst.children(self.syntax)
            .filter_map(|c| {
                cst.match_token(c, Token::Id)
                    .or_else(|| cst.match_token(c, Token::Str))
            })
            .for_each(f);
    }
}
impl PartDecl {
    pub fn rule_names<'a, F: FnMut((&'a str, Span))>(&self, cst: &'a Cst<'_>, f: F) {
        cst.children(self.syntax)
            .filter_map(|c| cst.match_token(c, Token::Id))
            .for_each(f);
    }
}
impl OrderedChoice {
    pub fn operands<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<CstChildren<'a>, impl FnMut(NodeRef) -> Option<Regex> + 'a + use<'a>>
    {
        cst.child_node_iter(self.syntax)
    }
}
impl Alternation {
    pub fn operands<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<CstChildren<'a>, impl FnMut(NodeRef) -> Option<Regex> + 'a + use<'a>>
    {
        cst.child_node_iter(self.syntax)
    }
}
impl Concat {
    pub fn operands<'a>(
        &self,
        cst: &'a Cst<'_>,
    ) -> std::iter::FilterMap<CstChildren<'a>, impl FnMut(NodeRef) -> Option<Regex> + 'a + use<'a>>
    {
        cst.child_node_iter(self.syntax)
    }
}
impl Paren {
    pub fn inner(&self, cst: &Cst<'_>) -> Option<Regex> {
        cst.child_node(self.syntax)
    }
}
impl Optional {
    pub fn operand(&self, cst: &Cst<'_>) -> Option<Regex> {
        cst.child_node(self.syntax)
    }
}
impl Star {
    pub fn operand(&self, cst: &Cst<'_>) -> Option<Regex> {
        cst.child_node(self.syntax)
    }
}
impl Plus {
    pub fn operand(&self, cst: &Cst<'_>) -> Option<Regex> {
        cst.child_node(self.syntax)
    }
}
impl Name {
    pub fn value<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Id)
    }
}
impl Symbol {
    pub fn value<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Str)
    }
}
impl Predicate {
    pub fn value<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Predicate)
    }
    pub fn is_true(&self, cst: &Cst<'_>) -> bool {
        self.value(cst)
            .is_some_and(|(val, _)| matches!(&val[1..], "t"))
    }
}
impl Action {
    pub fn value<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Action)
    }
}
impl Assertion {
    pub fn value<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::Assertion)
    }
}
impl NodeRename {
    pub fn value<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::NodeRename)
    }
}
impl NodeMarker {
    pub fn value<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::NodeMarker)
    }
    pub fn number<'a>(&self, cst: &'a Cst<'_>) -> &'a str {
        self.value(cst)
            .and_then(|(s, _)| s.split_once('<'))
            .map(|(_, r)| r)
            .unwrap()
    }
}
impl NodeCreation {
    pub fn value<'a>(&self, cst: &'a Cst<'_>) -> Option<(&'a str, Span)> {
        cst.child_token(self.syntax, Token::NodeCreation)
    }
    pub fn number<'a>(&self, cst: &'a Cst<'_>) -> Option<&'a str> {
        self.value(cst)
            .and_then(|(s, _)| s.split_once('>'))
            .and_then(|(l, _)| (!l.is_empty()).then_some(l))
    }
    pub fn node_name<'a>(&self, cst: &'a Cst<'_>) -> Option<&'a str> {
        self.value(cst)
            .and_then(|(s, _)| s.split_once('>'))
            .and_then(|(_, r)| (!r.is_empty()).then_some(r))
    }
    pub fn whole_rule(&self, cst: &Cst<'_>) -> bool {
        self.number(cst).is_none()
    }
}