mpl 0.3.0

One-rule TDPL/PEG parsing language with a static-codegen backend (FastParse) that beats pest, peg, nom, winnow, and chumsky on equal-work benchmarks.
Documentation
use mpl::output::Output;
use mpl::parser::Parser;
use mpl::rules::{RightRule, RightRuleKind, Rules};
use mpl::span::{Len, Start, StartAndLenSpan};
use mpl::symbols::{StrTerminal, U8SliceTerminal, Variable};
use mpl::trees::{AST, CST};
use std::collections::HashMap;

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
enum ParenthesesVariable {
    Open,
    Parentheses,
    Close,
}

impl Variable for ParenthesesVariable {}

type ParenthesesSpan = StartAndLenSpan<u32, u16>;
type ParenthesesStringAst = AST<ParenthesesVariable, ParenthesesSpan, String>;
type ParenthesesUnitAst = AST<ParenthesesVariable, ParenthesesSpan, ()>;
type ParenthesesStringResult = Result<ParenthesesStringAst, ParenthesesStringAst>;
type ParenthesesUnitResult = Result<ParenthesesUnitAst, ParenthesesUnitAst>;

impl<'i> Output<'i, str, ParenthesesVariable, ParenthesesSpan> for String {
    fn output_ast(
        _input: &'i str,
        cst: CST<ParenthesesVariable, ParenthesesSpan, Self>,
    ) -> ParenthesesStringAst {
        match cst.node.value {
            ParenthesesVariable::Open => AST::from_cst_and_output(cst, Some(String::from("open"))),
            ParenthesesVariable::Parentheses => {
                AST::from_cst_and_output(cst, Some(String::from("paren")))
            }
            ParenthesesVariable::Close => {
                AST::from_cst_and_output(cst, Some(String::from("close")))
            }
        }
    }
}

enum ParseResult {
    Ok,
    Err,
}

const INPUTS: [(&str, ParseResult); 8] = [
    // Ok
    ("", ParseResult::Ok),
    ("()", ParseResult::Ok),
    ("()(())", ParseResult::Ok),
    ("(()(()))", ParseResult::Ok),
    // Err
    ("(", ParseResult::Err),
    (")", ParseResult::Err),
    ("()())", ParseResult::Err),
    ("(()(())))", ParseResult::Err),
];

struct ParenthesesParser;

impl<'i, V, P, L, R, O> Parser<'i, str, StrTerminal<'i>, V, StartAndLenSpan<P, L>, P, R, O>
    for ParenthesesParser
where
    V: Variable,
    P: Start<str, L>,
    L: Len<str, P>,
    R: Rules<StrTerminal<'i>, V>,
    O: Output<'i, str, V, StartAndLenSpan<P, L>>,
{
}

impl<'i, P, L, O>
    Parser<
        'i,
        [u8],
        U8SliceTerminal<'i>,
        ParenthesesVariable,
        StartAndLenSpan<P, L>,
        P,
        HashMap<ParenthesesVariable, RightRule<U8SliceTerminal<'i>, ParenthesesVariable>>,
        O,
    > for ParenthesesParser
where
    P: Start<[u8], L>,
    L: Len<[u8], P>,
    O: Output<'i, [u8], ParenthesesVariable, StartAndLenSpan<P, L>>,
{
}

/// ```
/// Open = '(' Parentheses / ()
/// Parentheses = Open Close / f
/// Close = ")" Open / f
/// ```
#[test]
fn str_parentheses() {
    let mut rules = HashMap::new();

    rules.insert(
        ParenthesesVariable::Open,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::T(StrTerminal::Char('(')),
                RightRuleKind::V(ParenthesesVariable::Parentheses),
            ),
            RightRuleKind::Empty,
        ),
    );
    rules.insert(
        ParenthesesVariable::Parentheses,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::V(ParenthesesVariable::Open),
                RightRuleKind::V(ParenthesesVariable::Close),
            ),
            RightRuleKind::Failure,
        ),
    );
    rules.insert(
        ParenthesesVariable::Close,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::T(StrTerminal::Str(")")),
                RightRuleKind::V(ParenthesesVariable::Open),
            ),
            RightRuleKind::Failure,
        ),
    );

    let parser = ParenthesesParser;

    for input in INPUTS {
        let input_data = input.0;
        let parse_result = input.1;
        // all of the span
        let all_of_the_span =
            StartAndLenSpan::<u32, u16>::from_start_len(0, input_data.len() as u16);
        let result: ParenthesesStringResult = parser.parse(
            input_data,
            &rules,
            &ParenthesesVariable::Open,
            &all_of_the_span,
        );
        match parse_result {
            ParseResult::Ok => assert!(result.is_ok()),
            ParseResult::Err => assert!(result.is_err()),
        }
    }
}

/// Same grammar as `str_parentheses`, parsed via Squirrel (seed-growing
/// left-recursion-aware packrat). On a non-left-recursive grammar this
/// must produce identical results to `parse` and `packrat_parse`.
#[test]
fn str_parentheses_squirrel_matches_parse() {
    let mut rules = HashMap::new();
    rules.insert(
        ParenthesesVariable::Open,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::T(StrTerminal::Char('(')),
                RightRuleKind::V(ParenthesesVariable::Parentheses),
            ),
            RightRuleKind::Empty,
        ),
    );
    rules.insert(
        ParenthesesVariable::Parentheses,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::V(ParenthesesVariable::Open),
                RightRuleKind::V(ParenthesesVariable::Close),
            ),
            RightRuleKind::Failure,
        ),
    );
    rules.insert(
        ParenthesesVariable::Close,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::T(StrTerminal::Str(")")),
                RightRuleKind::V(ParenthesesVariable::Open),
            ),
            RightRuleKind::Failure,
        ),
    );

    let parser = ParenthesesParser;

    for input in INPUTS {
        let input_data = input.0;
        let parse_result = input.1;
        let all_of_the_span =
            StartAndLenSpan::<u32, u16>::from_start_len(0, input_data.len() as u16);
        let plain: ParenthesesStringResult = parser.parse(
            input_data,
            &rules,
            &ParenthesesVariable::Open,
            &all_of_the_span,
        );
        let squirrel: ParenthesesStringResult = parser.squirrel_parse(
            input_data,
            &rules,
            &ParenthesesVariable::Open,
            &all_of_the_span,
        );

        match parse_result {
            ParseResult::Ok => assert!(squirrel.is_ok()),
            ParseResult::Err => assert!(squirrel.is_err()),
        }
        assert_eq!(plain.is_ok(), squirrel.is_ok(), "input = {:?}", input_data);
    }
}

/// Same grammar as `str_parentheses`, parsed via the Packrat-memoized entry
/// point. Both code paths must agree on every input.
#[test]
fn str_parentheses_packrat_matches_parse() {
    let mut rules = HashMap::new();

    rules.insert(
        ParenthesesVariable::Open,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::T(StrTerminal::Char('(')),
                RightRuleKind::V(ParenthesesVariable::Parentheses),
            ),
            RightRuleKind::Empty,
        ),
    );
    rules.insert(
        ParenthesesVariable::Parentheses,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::V(ParenthesesVariable::Open),
                RightRuleKind::V(ParenthesesVariable::Close),
            ),
            RightRuleKind::Failure,
        ),
    );
    rules.insert(
        ParenthesesVariable::Close,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::T(StrTerminal::Str(")")),
                RightRuleKind::V(ParenthesesVariable::Open),
            ),
            RightRuleKind::Failure,
        ),
    );

    let parser = ParenthesesParser;

    for input in INPUTS {
        let input_data = input.0;
        let parse_result = input.1;
        let all_of_the_span =
            StartAndLenSpan::<u32, u16>::from_start_len(0, input_data.len() as u16);

        let plain: ParenthesesStringResult = parser.parse(
            input_data,
            &rules,
            &ParenthesesVariable::Open,
            &all_of_the_span,
        );
        let packrat: ParenthesesStringResult = parser.packrat_parse(
            input_data,
            &rules,
            &ParenthesesVariable::Open,
            &all_of_the_span,
        );

        match parse_result {
            ParseResult::Ok => assert!(packrat.is_ok()),
            ParseResult::Err => assert!(packrat.is_err()),
        }
        assert_eq!(plain, packrat, "input = {:?}", input_data);
    }
}

/// ```
/// Open = '(' Parentheses / ()
/// Parentheses = Open Close / f
/// Close = ")" Open / f
/// ```
#[test]
fn u8_slice_parentheses() {
    let mut rules = HashMap::new();

    rules.insert(
        ParenthesesVariable::Open,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::T(U8SliceTerminal::Char('(')),
                RightRuleKind::V(ParenthesesVariable::Parentheses),
            ),
            RightRuleKind::Empty,
        ),
    );
    rules.insert(
        ParenthesesVariable::Parentheses,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::V(ParenthesesVariable::Open),
                RightRuleKind::V(ParenthesesVariable::Close),
            ),
            RightRuleKind::Failure,
        ),
    );
    rules.insert(
        ParenthesesVariable::Close,
        RightRule::from_right_rule_kind(
            (
                RightRuleKind::T(U8SliceTerminal::Str(")")),
                RightRuleKind::V(ParenthesesVariable::Open),
            ),
            RightRuleKind::Failure,
        ),
    );

    let parser = ParenthesesParser;

    for input in INPUTS {
        let input_data = input.0.as_bytes();
        let parse_result = input.1;
        // all of the span
        let all_of_the_span =
            StartAndLenSpan::<u32, u16>::from_start_len(0, input_data.len() as u16);
        let result: ParenthesesUnitResult = parser.parse(
            input_data,
            &rules,
            &ParenthesesVariable::Open,
            &all_of_the_span,
        );
        match parse_result {
            ParseResult::Ok => assert!(result.is_ok()),
            ParseResult::Err => assert!(result.is_err()),
        }
    }
}