#![doc = include_str!("../README.md")]
mod compiler;
mod error;
#[cfg(test)]
mod test;
pub use compiler::Compiler;
pub use error::Error;
#[cfg(test)]
mod tests {
    use super::*;
    use crate::test::create_test_context;
    use autophagy::Fn;
    use melior::{
        ir::{Location, Module},
        pass::{self, PassManager},
        Context, ExecutionEngine,
    };
    fn compile<'c>(context: &'c Context, module: &Module<'c>, r#fn: &Fn) -> Result<(), Error> {
        Compiler::new(context, module).compile_fn(r#fn)?;
        Ok(())
    }
    #[allow(clippy::assign_op_pattern)]
    #[autophagy::quote]
    fn factorial(mut x: i32) -> i32 {
        let mut y = 1i32;
        while x > 0i32 {
            y = y * x;
            x = x - 1i32;
        }
        y
    }
    #[test]
    fn compile_factorial() {
        let context = create_test_context();
        let location = Location::unknown(&context);
        let mut module = Module::new(location);
        compile(&context, &module, &factorial_fn()).unwrap();
        assert!(module.as_operation().verify());
        let pass_manager = PassManager::new(&context);
        pass_manager.add_pass(pass::conversion::create_func_to_llvm());
        pass_manager
            .nested_under("func.func")
            .add_pass(pass::conversion::create_arith_to_llvm());
        pass_manager
            .nested_under("func.func")
            .add_pass(pass::conversion::create_index_to_llvm());
        pass_manager.add_pass(pass::conversion::create_scf_to_control_flow());
        pass_manager.add_pass(pass::conversion::create_control_flow_to_llvm());
        pass_manager.add_pass(pass::conversion::create_finalize_mem_ref_to_llvm());
        assert_eq!(pass_manager.run(&mut module), Ok(()));
        assert!(module.as_operation().verify());
        let engine = ExecutionEngine::new(&module, 2, &[], false);
        let mut argument = 5;
        let mut result = -1;
        assert_eq!(
            unsafe {
                engine.invoke_packed(
                    "factorial",
                    &mut [
                        &mut argument as *mut _ as *mut _,
                        &mut result as *mut _ as *mut _,
                    ],
                )
            },
            Ok(())
        );
        assert_eq!(argument, 5);
        assert_eq!(result, factorial(argument));
    }
    #[autophagy::quote]
    fn fibonacci(x: i32) -> i32 {
        if x <= 0i32 {
            0i32
        } else if x == 1i32 {
            1i32
        } else {
            fibonacci(x - 1i32) + fibonacci(x - 2i32)
        }
    }
    #[test]
    fn compile_fibonacci() {
        let context = create_test_context();
        let location = Location::unknown(&context);
        let mut module = Module::new(location);
        compile(&context, &module, &fibonacci_fn()).unwrap();
        assert!(module.as_operation().verify());
        let pass_manager = PassManager::new(&context);
        pass_manager.add_pass(pass::conversion::create_func_to_llvm());
        pass_manager
            .nested_under("func.func")
            .add_pass(pass::conversion::create_arith_to_llvm());
        pass_manager
            .nested_under("func.func")
            .add_pass(pass::conversion::create_index_to_llvm());
        pass_manager.add_pass(pass::conversion::create_scf_to_control_flow());
        pass_manager.add_pass(pass::conversion::create_control_flow_to_llvm());
        pass_manager.add_pass(pass::conversion::create_finalize_mem_ref_to_llvm());
        assert_eq!(pass_manager.run(&mut module), Ok(()));
        assert!(module.as_operation().verify());
        let engine = ExecutionEngine::new(&module, 2, &[], false);
        let mut argument = 5;
        let mut result = -1;
        assert_eq!(
            unsafe {
                engine.invoke_packed(
                    "fibonacci",
                    &mut [
                        &mut argument as *mut _ as *mut _,
                        &mut result as *mut _ as *mut _,
                    ],
                )
            },
            Ok(())
        );
        assert_eq!(argument, 5);
        assert_eq!(result, fibonacci(argument));
    }
}