parol 4.5.0

LL(k) and LALR(1) parser generator for Rust
Documentation
use crate::config::{CommonGeneratorConfig, UserTraitGeneratorConfig};
use crate::generators::UserTraitGenerator;
use crate::generators::user_trait_ir::UserTraitGenerationIR;
use anyhow::{Result, anyhow};

pub(crate) trait UserTraitLanguageBackend<C>
where
    C: CommonGeneratorConfig + UserTraitGeneratorConfig,
{
    fn generate_user_trait_source(
        &self,
        user_trait_ir: &mut UserTraitGenerationIR<'_, C>,
    ) -> Result<String>;
}

pub(crate) struct RustUserTraitBackend;

impl<C> UserTraitLanguageBackend<C> for RustUserTraitBackend
where
    C: CommonGeneratorConfig + UserTraitGeneratorConfig,
{
    fn generate_user_trait_source(
        &self,
        user_trait_ir: &mut UserTraitGenerationIR<'_, C>,
    ) -> Result<String> {
        let user_trait_generator = UserTraitGenerator::new(user_trait_ir.grammar_config);
        user_trait_generator.generate_user_trait_source(
            user_trait_ir.config,
            user_trait_ir.grammar_type,
            user_trait_ir.type_info,
        )
    }
}

pub(crate) struct CSharpUserTraitBackend;

impl<C> UserTraitLanguageBackend<C> for CSharpUserTraitBackend
where
    C: CommonGeneratorConfig + UserTraitGeneratorConfig,
{
    fn generate_user_trait_source(
        &self,
        user_trait_ir: &mut UserTraitGenerationIR<'_, C>,
    ) -> Result<String> {
        let user_trait_generator =
            crate::generators::cs_user_trait_generator::CSUserTraitGenerator::new(
                user_trait_ir.grammar_config,
            );
        user_trait_generator.generate_user_trait_source(
            user_trait_ir.config,
            user_trait_ir.grammar_type,
            user_trait_ir.type_info,
        )
    }
}

pub(crate) fn generate_user_trait_source_for_language<C>(
    backend: &impl UserTraitLanguageBackend<C>,
    user_trait_ir: &mut UserTraitGenerationIR<'_, C>,
) -> Result<String>
where
    C: CommonGeneratorConfig + UserTraitGeneratorConfig,
{
    if user_trait_ir.grammar_config.cfg.pr.is_empty() {
        return Err(anyhow!("Grammar contains no productions"));
    }
    backend.generate_user_trait_source(user_trait_ir)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::InnerAttributes;
    use crate::config::{CommonGeneratorConfig, UserTraitGeneratorConfig};
    use crate::generators::{GrammarTypeInfo, UserTraitGenerator};
    use crate::utils::obtain_grammar_config;
    use std::path::PathBuf;

    const RUST_USER_TRAIT_OUTPUT_CHECKSUM: u64 = 9804298626913191341;
    const CSHARP_USER_TRAIT_OUTPUT_CHECKSUM: u64 = 15779923004694027088;

    #[derive(Debug)]
    struct TestConfig;

    impl CommonGeneratorConfig for TestConfig {
        fn user_type_name(&self) -> &str {
            "BackendTest"
        }

        fn module_name(&self) -> &str {
            "backend_test"
        }

        fn minimize_boxed_types(&self) -> bool {
            false
        }

        fn range(&self) -> bool {
            false
        }

        fn node_kind_enums(&self) -> bool {
            false
        }
    }

    impl UserTraitGeneratorConfig for TestConfig {
        fn inner_attributes(&self) -> &[InnerAttributes] {
            &[]
        }
    }

    fn test_grammar_path() -> PathBuf {
        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/arg_tests/generate.par")
    }

    fn stable_checksum(input: &str) -> u64 {
        input
            .as_bytes()
            .iter()
            .fold(14695981039346656037u64, |hash, b| {
                hash.wrapping_mul(1099511628211).wrapping_add(u64::from(*b))
            })
    }

    #[test]
    fn rust_user_trait_backend_matches_direct_generation() {
        let grammar_config = obtain_grammar_config(test_grammar_path(), false).unwrap();
        let config = TestConfig;

        let mut type_info_backend = GrammarTypeInfo::try_new(config.user_type_name()).unwrap();
        let mut user_trait_ir = UserTraitGenerationIR::new(
            &grammar_config,
            &config,
            grammar_config.grammar_type,
            &mut type_info_backend,
        );
        let via_backend =
            generate_user_trait_source_for_language(&RustUserTraitBackend, &mut user_trait_ir)
                .unwrap();

        let mut type_info_direct = GrammarTypeInfo::try_new(config.user_type_name()).unwrap();
        let direct = UserTraitGenerator::new(&grammar_config)
            .generate_user_trait_source(&config, grammar_config.grammar_type, &mut type_info_direct)
            .unwrap();

        assert_eq!(direct, via_backend);
    }

    #[test]
    fn csharp_user_trait_backend_matches_direct_generation() {
        let grammar_config = obtain_grammar_config(test_grammar_path(), false).unwrap();
        let config = TestConfig;

        let mut type_info_backend = GrammarTypeInfo::try_new(config.user_type_name()).unwrap();
        let mut user_trait_ir = UserTraitGenerationIR::new(
            &grammar_config,
            &config,
            grammar_config.grammar_type,
            &mut type_info_backend,
        );
        let via_backend =
            generate_user_trait_source_for_language(&CSharpUserTraitBackend, &mut user_trait_ir)
                .unwrap();

        let mut type_info_direct = GrammarTypeInfo::try_new(config.user_type_name()).unwrap();
        let direct =
            crate::generators::cs_user_trait_generator::CSUserTraitGenerator::new(&grammar_config)
                .generate_user_trait_source(
                    &config,
                    grammar_config.grammar_type,
                    &mut type_info_direct,
                )
                .unwrap();

        assert_eq!(direct, via_backend);
    }

    #[test]
    fn rust_user_trait_output_checksum_stable() {
        let grammar_config = obtain_grammar_config(test_grammar_path(), false).unwrap();
        let config = TestConfig;
        let mut type_info = GrammarTypeInfo::try_new(config.user_type_name()).unwrap();

        let direct = UserTraitGenerator::new(&grammar_config)
            .generate_user_trait_source(&config, grammar_config.grammar_type, &mut type_info)
            .unwrap();

        let checksum = stable_checksum(&direct);
        assert_eq!(
            RUST_USER_TRAIT_OUTPUT_CHECKSUM, checksum,
            "Rust user-trait output checksum changed: {checksum}"
        );
    }

    #[test]
    fn csharp_user_trait_output_checksum_stable() {
        let grammar_config = obtain_grammar_config(test_grammar_path(), false).unwrap();
        let config = TestConfig;
        let mut type_info = GrammarTypeInfo::try_new(config.user_type_name()).unwrap();

        let direct =
            crate::generators::cs_user_trait_generator::CSUserTraitGenerator::new(&grammar_config)
                .generate_user_trait_source(&config, grammar_config.grammar_type, &mut type_info)
                .unwrap();

        let checksum = stable_checksum(&direct);
        assert_eq!(
            CSHARP_USER_TRAIT_OUTPUT_CHECKSUM, checksum,
            "C# user-trait output checksum changed: {checksum}"
        );
    }
}