marser 0.1.1

Parser combinator toolkit with matcher-level backtracking and rich error reporting.
Documentation
use std::fmt::Display;
use std::marker::PhantomData;

use crate::{
    context::ParserContext,
    error::{MatcherRunError, error_handler::ErrorHandler},
    input::{Input, InputStream},
    matcher::{DirectMatchRunner, Matcher, NoMemoizeBacktrackingRunner, runner::MatchRunner},
    parser::{ParserCombinator, internal::ParserImpl},
};

use super::match_result::{MatchResultMultiple, MatchResultOptional, MatchResultSingle};

/// Parser that runs `matcher` and, on success, calls `constructor` with the filled capture buckets.
///
/// `MRes` is a triple `(single, multiple, optional)` of match-result pieces; the macro-generated
/// grammar usually matches this shape. See the [`super`] module for bind helpers and for using
/// `capture!` to build this type.
pub struct Capture<MRes, Match, F> {
    pub(super) matcher: Match,
    pub(super) constructor: F,
    pub(super) _phantom: PhantomData<MRes>,
}

impl<MRes, Match, F> Clone for Capture<MRes, Match, F>
where
    Match: Clone,
    F: Clone,
{
    fn clone(&self) -> Self {
        Self {
            matcher: self.matcher.clone(),
            constructor: self.constructor.clone(),
            _phantom: PhantomData,
        }
    }
}

impl<MRes, Match, F> std::fmt::Debug for Capture<MRes, Match, F>
where
    Match: std::fmt::Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Capture")
            .field("matcher", &self.matcher)
            .finish()
    }
}

impl<MResSingle, MResMultiple, MResOptional, Match, F> ParserCombinator
    for Capture<(MResSingle, MResMultiple, MResOptional), Match, F>
where
    Match: crate::matcher::MatcherCombinator,
{
}

impl<Out, MResSingle, MResMultiple, MResOptional, Match, F>
    Capture<(MResSingle, MResMultiple, MResOptional), Match, F>
where
    MResSingle: MatchResultSingle,
    MResMultiple: MatchResultMultiple,
    MResOptional: MatchResultOptional,
    F: Fn(MResSingle::Output, MResMultiple, MResOptional) -> Out,
{
    /// Builds a capture parser: `grammar_factory` receives empty property slots and must return
    /// the matcher; `constructor` maps filled results to `Out`.
    pub fn new<
        'a,
        'ctx: 'a,
        GF: FnOnce(MResSingle::Properties, MResMultiple::Properties, MResOptional::Properties) -> Match,
    >(
        grammar_factory: GF,
        constructor: F,
    ) -> Self {
        let properties_single = MResSingle::new_properties();
        let properties_multiple = MResMultiple::new_properties();
        let properties_optional = MResOptional::new_properties();
        Self {
            matcher: grammar_factory(properties_single, properties_multiple, properties_optional),
            constructor,
            _phantom: PhantomData,
        }
    }
}

impl<'src, Inp: Input<'src>, Out, MResSingle, MResMultiple, MResOptional, Match, F>
    ParserImpl<'src, Inp> for Capture<(MResSingle, MResMultiple, MResOptional), Match, F>
where
    MResSingle: MatchResultSingle,
    MResMultiple: MatchResultMultiple,
    MResOptional: MatchResultOptional,
    Match: Matcher<'src, Inp, (MResSingle, MResMultiple, MResOptional)>,
    Inp: Input<'src>,
    F: Fn(MResSingle::Output, MResMultiple, MResOptional) -> Out + Clone,
{
    type Output = Out;
    const CAN_FAIL: bool = Match::CAN_FAIL;

    #[inline]
    fn parse(
        &self,
        context: &mut ParserContext<'src>,
        error_handler: &mut impl ErrorHandler,
        input: &mut InputStream<'src, Inp>,
    ) -> Result<Option<Self::Output>, MatcherRunError> {
        if Match::CAN_MATCH_DIRECTLY && !error_handler.is_real() && !context.is_in_error_recovery {
            let mut runner = DirectMatchRunner::new(context);
            match runner.run_match(&self.matcher, error_handler, input) {
                Ok(true) => {
                    let (res_single, res_multiple, res_optional) = runner.get_match_result();
                    let result =
                        (self.constructor)(res_single.to_output(), res_multiple, res_optional);
                    Ok(Some(result))
                }
                Ok(false) => Ok(None),
                Err(e) => Err(e),
            }
        } else {
            let mut runner = NoMemoizeBacktrackingRunner::new(context);
            match runner.run_match(&self.matcher, error_handler, input) {
                Ok(true) => {
                    let (res_single, res_multiple, res_optional) = runner.get_match_result();
                    let result =
                        (self.constructor)(res_single.to_output(), res_multiple, res_optional);
                    Ok(Some(result))
                }
                Ok(false) => Ok(None),
                Err(e) => Err(e),
            }
        }
    }

    #[inline]
    fn maybe_label(&self) -> Option<Box<dyn Display>> {
        self.matcher.maybe_label()
    }
}