bnf 0.6.0

A library for parsing Backus–Naur form context-free grammars
Documentation
use super::Format;

#[non_exhaustive]
pub struct BNF;

impl Format for BNF {
    fn nonterminal_delimiter() -> Option<(char, char)> {
        Some(('<', '>'))
    }
    fn production_separator() -> &'static str {
        "::="
    }
    fn alternative_separator() -> char {
        '|'
    }
    fn production_start_char() -> Option<char> {
        Some('<')
    }
}

#[cfg(test)]
mod tests {
    use super::BNF;
    use crate::parsers::*;

    #[test]
    fn nonterminal_match() {
        let input = "<nonterminal-pattern>";
        let expected = Term::Nonterminal("nonterminal-pattern".to_string());

        let (_, actual) = nonterminal::<BNF>(input).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn expression_match() {
        let input = r#"<nonterminal-pattern> "terminal-pattern""#;
        let expected = Expression::from_parts(vec![
            Term::Nonterminal("nonterminal-pattern".to_string()),
            Term::Terminal("terminal-pattern".to_string()),
        ]);

        let (_, actual) = expression::<BNF>(input).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn production_match() {
        let input = r#"<nonterminal-pattern> ::= <nonterminal-pattern> "terminal-pattern" | "terminal-pattern";\r\n"#;
        let expected = Production::from_parts(
            Term::Nonterminal("nonterminal-pattern".to_string()),
            vec![
                Expression::from_parts(vec![
                    Term::Nonterminal("nonterminal-pattern".to_string()),
                    Term::Terminal("terminal-pattern".to_string()),
                ]),
                Expression::from_parts(vec![Term::Terminal("terminal-pattern".to_string())]),
            ],
        );

        let (_, actual) = production::<BNF>(input).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn grammar_match() {
        let input = r#"<nonterminal-pattern> ::= <nonterminal-pattern> "terminal-pattern" | "terminal-pattern";\r\n"#;
        let expected = Grammar::from_parts(vec![Production::from_parts(
            Term::Nonterminal("nonterminal-pattern".to_string()),
            vec![
                Expression::from_parts(vec![
                    Term::Nonterminal("nonterminal-pattern".to_string()),
                    Term::Terminal("terminal-pattern".to_string()),
                ]),
                Expression::from_parts(vec![Term::Terminal("terminal-pattern".to_string())]),
            ],
        )]);

        let (_, actual) = grammar::<BNF>(input).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn production_with_comment_suffix() {
        let input = "<a> ::= 'x' ; only a comment\n";
        let expected = Production::from_parts(
            Term::Nonterminal("a".to_string()),
            vec![Expression::from_parts(vec![Term::Terminal(
                "x".to_string(),
            )])],
        );

        let (_, actual) = production::<BNF>(input).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn grammar_with_comment_only_line() {
        let input = "<a> ::= 'x'\n; comment\n<b> ::= 'y'\n";
        let expected = Grammar::from_parts(vec![
            Production::from_parts(
                Term::Nonterminal("a".to_string()),
                vec![Expression::from_parts(vec![Term::Terminal(
                    "x".to_string(),
                )])],
            ),
            Production::from_parts(
                Term::Nonterminal("b".to_string()),
                vec![Expression::from_parts(vec![Term::Terminal(
                    "y".to_string(),
                )])],
            ),
        ]);

        let (_, actual) = grammar::<BNF>(input).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn grammar_with_comment_to_eof() {
        let input = "<a> ::= 'x'\n<b> ::= 'y' ; last line comment";
        let expected = Grammar::from_parts(vec![
            Production::from_parts(
                Term::Nonterminal("a".to_string()),
                vec![Expression::from_parts(vec![Term::Terminal(
                    "x".to_string(),
                )])],
            ),
            Production::from_parts(
                Term::Nonterminal("b".to_string()),
                vec![Expression::from_parts(vec![Term::Terminal(
                    "y".to_string(),
                )])],
            ),
        ]);

        let (_, actual) = grammar::<BNF>(input).unwrap();
        assert_eq!(expected, actual);
    }
}