pub mod syntax;
pub mod compiler;
pub mod support;
pub mod gen;

pub use support::Codec;
pub use support::Deserialize;
pub use support::ParseError;

#[cfg(test)]
mod tests {
    #[test]
    fn can_access_preserves_core() {
        use preserves::value::*;
        assert_eq!(format!("{:?}", UnwrappedIOValue::from(3 + 4)), "7");
    }

    #[test]
    fn simple_rendering() {
        use crate::*;
        use crate::syntax::block::*;

        let code = semiblock![
            seq!["f", parens!["a", "b", "c"]],
            seq!["f", parens!["a", "b", "c"]],
            seq!["f", parens!["a", "b", "c"]],
            seq!["f", parens!["a", "b", "c"]],
            seq!["f", parens!["a", "b", "c"]],
            seq!["f", parens!["a", "b", "c"]],
            seq!["g", parens![]],
            parens![]
        ];
        println!("{}", Formatter::to_string(&code));
    }

    #[test]
    fn metaschema_parsing() -> Result<(), std::io::Error> {
        use preserves::value::{BinarySource, IOBinarySource, Reader};
        use crate::support::Parse;
        use crate::support::Unparse;
        use crate::gen::schema::*;

        let mut f = std::fs::File::open("../../../schema/schema.bin")?;
        let mut src = IOBinarySource::new(&mut f);
        let mut reader = src.packed_iovalues();
        let schema = reader.demand_next(false)?;
        let language = crate::gen::Language::default();
        let parsed = Schema::parse(&language, &schema).expect("successful parse");
        assert_eq!(schema, parsed.unparse(&language));
        Ok(())
    }
}

#[macro_export]
macro_rules! define_language {
    ($fname:ident () : $lang:ident < $default_value:ty > { $($field:ident : $($type:ident)::+ ,)* }) => {
        pub struct $lang<N: $crate::support::preserves::value::NestedValue> {
            $(pub $field: std::sync::Arc<$($type)::*<N>>),*
        }

        $(impl<'a, N: $crate::support::preserves::value::NestedValue> From<&'a $lang<N>> for &'a $($type)::*<N> {
            fn from(v: &'a $lang<N>) -> Self {
                &v.$field
            }
        })*

        impl<N: $crate::support::preserves::value::NestedValue> $crate::support::NestedValueCodec
            for $lang<N> {}

        mod $fname {
            use super::*;
            lazy_static::lazy_static! {
                pub static ref GLOBAL_LANG: std::sync::Arc<$lang<$default_value>> =
                    std::sync::Arc::new($lang {
                        $($field: std::sync::Arc::new($($type)::*::default())),*
                    });
            }
        }

        impl $lang<$default_value> {
            pub fn arc() -> &'static std::sync::Arc<$lang<$default_value>> {
                &*$fname::GLOBAL_LANG
            }
        }

        pub fn $fname() -> &'static $lang<$default_value> {
            &*$fname::GLOBAL_LANG
        }
    };
}
