artlr_syntax 0.3.0

Another Rust tool for syntax analysis
Documentation
#[cfg(test)]
mod test {
    mod from_grammar {
        use crate::grammar::context_free_grammar::ContextFreeGrammar;
        use crate::grammar::context_free_grammar_production::ContextFreeGrammarProduction;
        use crate::grammar::first_follow_symbols::FirstFollowSymbols;
        use crate::parser::recursive_descent_parser_transitions::RecursiveDescentParserTransitions;

        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
        enum TerminalTokenTypeTest {
            Eof,
            Eos,
            Eq,
            Id,
        }

        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
        enum SyntaxTokenTest {
            Epsilon,
            Expression,
            Module,
            Sentence,
            Terminal(TerminalTokenTypeTest),
        }

        fn equals<T: PartialEq>(
            first_production: &ContextFreeGrammarProduction<T>,
            second_production: &ContextFreeGrammarProduction<T>,
        ) -> bool {
            if first_production.input.ne(&second_production.input) {
                return false;
            }

            if first_production.output.len() != second_production.output.len() {
                return false;
            }

            for i in 0..first_production.output.len() {
                if first_production.output.get(i).unwrap() != second_production.output.get(i).unwrap() {
                    return false;
                }
            }

            true
        }

        #[test]
        fn it_adds_first_symbols_productions() -> () {
            let grammar_productions: Vec<ContextFreeGrammarProduction<SyntaxTokenTest>> = vec![
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Module, vec![
                    SyntaxTokenTest::Expression,
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eof),
                ]),
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Expression, vec![
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Id),
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eos),
                ]),
            ];

            let grammar: ContextFreeGrammar<SyntaxTokenTest> = ContextFreeGrammar::new(
                SyntaxTokenTest::Epsilon,
                SyntaxTokenTest::Module,
                grammar_productions,
            );

            let first_follow_symbols: FirstFollowSymbols<SyntaxTokenTest> =
                FirstFollowSymbols::from(&grammar);

            let recursive_descent_parser_transitions: RecursiveDescentParserTransitions<SyntaxTokenTest>
                = RecursiveDescentParserTransitions::from(&grammar, &first_follow_symbols);

            let module_id_productions =
                recursive_descent_parser_transitions
                    .get_productions(
                        &SyntaxTokenTest::Module,
                        &SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Id)
                    ).unwrap();

            let first_module_id_production = module_id_productions.get(0).unwrap();
            let expected_first_module_id_production = ContextFreeGrammarProduction::new(SyntaxTokenTest::Module, vec![
                SyntaxTokenTest::Expression,
                SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eof),
            ]);

            assert_eq!(module_id_productions.len(), 1);
            assert!(equals(first_module_id_production, &expected_first_module_id_production));
        }

        #[test]
        fn it_adds_follow_symbols_productions() -> () {
            let grammar_productions: Vec<ContextFreeGrammarProduction<SyntaxTokenTest>> = vec![
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Module, vec![
                    SyntaxTokenTest::Sentence,
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eof),
                ]),
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Sentence, vec![
                    SyntaxTokenTest::Epsilon,
                ]),
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Sentence, vec![
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Id),
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eq),
                    SyntaxTokenTest::Expression,
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eos),
                ]),
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Expression, vec![
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Id),
                ]),
            ];

            let grammar: ContextFreeGrammar<SyntaxTokenTest> = ContextFreeGrammar::new(
                SyntaxTokenTest::Epsilon,
                SyntaxTokenTest::Module,
                grammar_productions,
            );

            let first_follow_symbols: FirstFollowSymbols<SyntaxTokenTest> =
                FirstFollowSymbols::from(&grammar);

            let recursive_descent_parser_transitions: RecursiveDescentParserTransitions<SyntaxTokenTest>
                = RecursiveDescentParserTransitions::from(&grammar, &first_follow_symbols);

            let module_eof_productions =
                recursive_descent_parser_transitions
                    .get_productions(
                        &SyntaxTokenTest::Module,
                        &SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Id)
                    ).unwrap();

            let first_module_eof_production = module_eof_productions.get(0).unwrap();
            let expected_first_module_id_production = ContextFreeGrammarProduction::new(SyntaxTokenTest::Module, vec![
                SyntaxTokenTest::Sentence,
                SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eof),
            ]);

            assert_eq!(module_eof_productions.len(), 1);
            assert!(equals(first_module_eof_production, &expected_first_module_id_production));
        }

        #[test]
        fn it_adds_once_on_first_follow_conflict() -> () {
            let grammar_productions: Vec<ContextFreeGrammarProduction<SyntaxTokenTest>> = vec![
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Module, vec![
                    SyntaxTokenTest::Sentence,
                    SyntaxTokenTest::Sentence,
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eof),
                ]),
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Sentence, vec![
                    SyntaxTokenTest::Epsilon,
                ]),
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Sentence, vec![
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Id),
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eq),
                    SyntaxTokenTest::Expression,
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eos),
                ]),
                ContextFreeGrammarProduction::new(SyntaxTokenTest::Expression, vec![
                    SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Id),
                ]),
            ];

            let grammar: ContextFreeGrammar<SyntaxTokenTest> = ContextFreeGrammar::new(
                SyntaxTokenTest::Epsilon,
                SyntaxTokenTest::Module,
                grammar_productions,
            );

            let first_follow_symbols: FirstFollowSymbols<SyntaxTokenTest> =
                FirstFollowSymbols::from(&grammar);

            let recursive_descent_parser_transitions: RecursiveDescentParserTransitions<SyntaxTokenTest>
                = RecursiveDescentParserTransitions::from(&grammar, &first_follow_symbols);

            let module_id_productions =
                recursive_descent_parser_transitions
                    .get_productions(
                        &SyntaxTokenTest::Module,
                        &SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Id)
                    ).unwrap();

            let first_module_id_production = module_id_productions.get(0).unwrap();
            let expected_first_module_id_production = ContextFreeGrammarProduction::new(SyntaxTokenTest::Module, vec![
                SyntaxTokenTest::Sentence,
                SyntaxTokenTest::Sentence,
                SyntaxTokenTest::Terminal(TerminalTokenTypeTest::Eof),
            ]);

            assert_eq!(module_id_productions.len(), 1);
            assert!(equals(first_module_id_production, &expected_first_module_id_production));
        }

        #[test]
        fn it_adds_on_non_terminal_symbol_with_first_set_is_epsilon() {
            #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
            enum DummySyntaxTokenTest {
                Empty,
                Eof,
                Epsilon,
                S,
            }

            let grammar_productions: Vec<ContextFreeGrammarProduction<DummySyntaxTokenTest>> = vec![
                ContextFreeGrammarProduction::new(DummySyntaxTokenTest::S, vec![
                    DummySyntaxTokenTest::Empty,
                    DummySyntaxTokenTest::Eof,
                ]),
                ContextFreeGrammarProduction::new(DummySyntaxTokenTest::Empty, vec![
                    DummySyntaxTokenTest::Epsilon,
                ]),
            ];

            let grammar: ContextFreeGrammar<DummySyntaxTokenTest> = ContextFreeGrammar::new(
                DummySyntaxTokenTest::Epsilon,
                DummySyntaxTokenTest::S,
                grammar_productions,
            );

            let first_follow_symbols: FirstFollowSymbols<DummySyntaxTokenTest> =
                FirstFollowSymbols::from(&grammar);

            let recursive_descent_parser_transitions: RecursiveDescentParserTransitions<DummySyntaxTokenTest>
                = RecursiveDescentParserTransitions::from(&grammar, &first_follow_symbols);

            let empty_eof_productions
                = recursive_descent_parser_transitions
                    .get_productions(
                        &DummySyntaxTokenTest::Empty,
                        &DummySyntaxTokenTest::Eof,
                    )
                    .unwrap();

            let first_empty_eof_production = empty_eof_productions.get(0).unwrap();
            let expected_first_empty_eof_production = ContextFreeGrammarProduction::new(
                DummySyntaxTokenTest::Empty,
                vec![
                    DummySyntaxTokenTest::Epsilon,
                ]
            );

            assert_eq!(empty_eof_productions.len(), 1);
            assert!(
                equals(
                    first_empty_eof_production,
                    &expected_first_empty_eof_production,
                )
            );
        }
    }
}