utterance 0.1.2

A parser library for creating readable, natural-language-inspired domain-specific languages.
Documentation
use std::ptr::fn_addr_eq;
use std::rc::Rc;

use crate::lexer::Lexer;
use crate::parser::expectation::{Expectation, ExpectationError, Statement, StatementKind};
use crate::parser::tree::factory::ParseTreeFactory;
use crate::parser::tree::{Child, ParseContext};
use crate::parser::{CustomParseError, ParseError};
use crate::syntax::{Highlight, HighlightKind, Highlights};

pub fn noop_fn<C>(_: &mut C, _: &Statement) -> Result<(), CustomParseError>
where
    C: ParseContext,
{
    Ok(())
}

pub type TreeFn<T> = fn(&mut T, &Statement) -> Result<(), CustomParseError>;

#[derive(Debug, Clone)]
pub struct ParseTree<C>
where
    C: ParseContext,
{
    tree_factory: Rc<ParseTreeFactory<C>>,

    statement_expectation: StatementKind,
    function: TreeFn<C>,

    children: Vec<Child<C>>,
}

impl<C> ParseTree<C>
where
    C: ParseContext,
{
    pub fn new(
        tree_factory: Rc<ParseTreeFactory<C>>,
        statement_expectation: StatementKind,
        children: Vec<ParseTree<C>>,
        function: TreeFn<C>,
    ) -> Self {
        let children = children.into_iter().map(Child::Contains).collect();
        Self::new_from_children(tree_factory, statement_expectation, children, function)
    }

    pub fn tree_factory(&self) -> Rc<ParseTreeFactory<C>> {
        self.tree_factory.clone()
    }

    pub(crate) fn new_from_children(
        tree_factory: Rc<ParseTreeFactory<C>>,
        statement_expectation: StatementKind,
        children: Vec<Child<C>>,
        function: TreeFn<C>,
    ) -> Self {
        Self {
            tree_factory,
            statement_expectation,
            children,
            function,
        }
    }

    fn check<'a, const P: usize>(
        &self,
        lexer: &'a mut Lexer<P>,
    ) -> Result<Expectation<'a>, ExpectationError> {
        self.statement_expectation.expect(lexer)
    }

    fn do_function(
        &self,
        context: &mut C,
        statement: &Statement,
        depth: usize,
    ) -> Result<(), ParseError> {
        (self.function)(context, statement)
            .map_err(|err| ParseError::custom(depth, err.to_string()))
    }

    pub(crate) fn try_match<const H: usize, const P: usize>(
        &self,
        lexer: &mut Lexer<P>,
        highlights: &mut Highlights<H>,
        context: &mut C,
        depth: usize,
    ) -> Result<(), ParseError> {
        let expectation = self
            .check(lexer)
            .map_err(|err| err.into_parse_error(depth))?;

        if let Err(err) = self.do_function(context, expectation.statement(), depth) {
            for span in expectation.span() {
                highlights.push(Highlight::new(HighlightKind::Error, span));
            }

            return Err(err);
        }

        let highlight_kind = expectation.statement().highlight_kind();
        for span in expectation.span() {
            highlights.push(Highlight::new(highlight_kind, span));
        }

        Ok(())
    }

    pub fn expectation(&self) -> &StatementKind {
        &self.statement_expectation
    }

    pub fn function(&self) -> &TreeFn<C> {
        &self.function
    }

    pub(crate) fn is_end(&self) -> bool {
        self.children.is_empty()
    }

    pub fn push_child(&mut self, child: ParseTree<C>) {
        self.children.push(Child::Contains(child));
    }

    pub fn push_child_ref(&mut self, child: Rc<ParseTree<C>>) {
        self.children.push(Child::ByRef(child));
    }

    pub fn push_child_to_leaves(&mut self, child: ParseTree<C>) {
        if self.is_end() {
            self.push_child(child);
            return;
        }

        for c in self.children_mut() {
            c.push_child_to_leaves(child.clone());
        }
    }

    pub fn push_child_ref_to_leaves(&mut self, child: Rc<ParseTree<C>>) {
        if self.is_end() {
            self.push_child_ref(child);

            return;
        }

        for c in self.children_mut() {
            c.push_child_ref_to_leaves(child.clone());
        }
    }

    pub fn push_child_to_leaves_mut(&mut self, child: ParseTree<C>) {
        if self.children_mut().next().is_none() {
            self.push_child(child);

            return;
        }

        for c in self.children_mut() {
            c.push_child_to_leaves_mut(child.clone());
        }
    }

    pub fn push_child_ref_to_leaves_mut(&mut self, child: Rc<ParseTree<C>>) {
        if self.children_mut().next().is_none() {
            self.push_child_ref(child);

            return;
        }

        for c in self.children_mut() {
            c.push_child_ref_to_leaves_mut(child.clone());
        }
    }

    pub(crate) fn set_children<I>(&mut self, children: I)
    where
        I: IntoIterator<Item = Child<C>>,
    {
        self.children = children.into_iter().collect();
    }

    pub(crate) fn children(&self) -> impl Iterator<Item = &ParseTree<C>> {
        self.children.iter().map(|child| child.as_tree())
    }

    pub(crate) fn children_mut(&mut self) -> impl Iterator<Item = &mut ParseTree<C>> {
        self.children
            .iter_mut()
            .filter_map(|child| child.as_tree_mut())
    }

    fn same_kind(&self, other: &Self) -> bool {
        self.statement_expectation == other.statement_expectation
            && fn_addr_eq(self.function, other.function)
    }

    pub fn merge(&mut self, other: Self) {
        if !self.same_kind(&other) {
            *self = ParseTree::noop(
                self.tree_factory.clone(),
                vec![
                    std::mem::replace(self, ParseTree::noop(self.tree_factory.clone(), Vec::new())),
                    other,
                ],
            );
            return;
        }

        for other_child in other.children {
            let other_tree = match other_child.into_owned() {
                Ok(tree) => tree,
                Err(child) => {
                    self.children.push(child);
                    continue;
                }
            };

            if let Some(self_tree) = self.children.iter_mut().find_map(|child| {
                child
                    .as_tree_mut()
                    .filter(|tree| tree.same_kind(&other_tree))
            }) {
                self_tree.merge(other_tree);
            } else {
                self.children.push(Child::Contains(other_tree));
            }
        }
    }

    pub fn root(tree_factory: Rc<ParseTreeFactory<C>>, children: Vec<ParseTree<C>>) -> Self {
        let mut noop = Self::noop(tree_factory.clone(), children);
        noop.push_child_ref(tree_factory.comments());
        noop.push_child_ref(tree_factory.line_terminator());

        noop
    }

    pub fn noop(tree_factory: Rc<ParseTreeFactory<C>>, children: Vec<ParseTree<C>>) -> Self {
        ParseTree::new(tree_factory, StatementKind::Noop, children, noop_fn)
    }

    pub fn add_end_of_line_fn(&mut self) {
        if self.is_end() {
            self.push_child_ref(self.tree_factory.statement_terminator());
            return;
        }

        for child in self.children_mut() {
            child.add_end_of_line_fn();
        }
    }

    pub fn into_line(mut self) -> Self {
        self.add_end_of_line_fn();

        Self::new(
            self.tree_factory.clone(),
            StatementKind::CapitalizeWord,
            vec![self],
            noop_fn,
        )
    }
}

#[macro_export]
macro_rules! expect_words {
    ($factory:expr; $($word:expr),+; $children:expr; $callback:expr $(,)?) => {{
        expect_words!(@build $factory, $callback, $children; $($word),+)
    }};

    (@build $factory:expr, $callback:expr, $children:expr; $last:expr) => {
        ParseTree::new(
            $factory,
            StatementKind::ExactWord($last),
            $children,
            $callback,
        )
    };

    (@build $factory:expr, $callback:expr, $children:expr; $head:expr, $($tail:expr),+) => {
        ParseTree::new(
            $factory.clone(),
            StatementKind::ExactWord($head),
            vec![
                expect_words!(@build $factory, $callback, $children; $($tail),+)
            ],
            noop_fn,
        )
    };
}

#[macro_export]
macro_rules! merge_trees {
    ($tree:expr $(,)?) => {
        $tree
    };

    ($first:expr, $($rest:expr),+ $(,)?) => {{
        let mut tree = $first;
        $(
            tree.merge($rest);
        )+
        tree
    }};
}

pub use expect_words;
pub use merge_trees;