oxur_comp/
compiler.rs

1//! Compiler
2//!
3//! Orchestrates the complete compilation pipeline from Core Forms to binary.
4
5use crate::{CodeGenerator, Error, Lowerer, Result};
6use oxur_lang::CoreForm;
7use std::path::{Path, PathBuf};
8use std::process::Command;
9
10/// Compiler orchestrates the full compilation pipeline
11pub struct Compiler {
12    lowerer: Lowerer,
13    codegen: CodeGenerator,
14    output_dir: PathBuf,
15}
16
17impl Compiler {
18    pub fn new(output_dir: PathBuf) -> Self {
19        Self { lowerer: Lowerer::new(), codegen: CodeGenerator::new(), output_dir }
20    }
21
22    /// Compile Core Forms to a binary
23    pub fn compile(&mut self, forms: Vec<CoreForm>, output: &Path) -> Result<()> {
24        // Stage 3: Lower to Rust AST
25        let ast = self.lowerer.lower(forms)?;
26
27        // Stage 4: Generate Rust source
28        let source = self.codegen.generate(&ast)?;
29
30        // Write to temporary .rs file
31        let rs_file = self.output_dir.join("generated.rs");
32        std::fs::write(&rs_file, source)?;
33
34        // Stage 5: Compile with rustc
35        self.compile_with_rustc(&rs_file, output)?;
36
37        Ok(())
38    }
39
40    fn compile_with_rustc(&self, source: &Path, output: &Path) -> Result<()> {
41        let status = Command::new("rustc").arg(source).arg("-o").arg(output).status()?;
42
43        if !status.success() {
44            return Err(Error::Compile(format!(
45                "rustc failed with exit code: {:?}",
46                status.code()
47            )));
48        }
49
50        Ok(())
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn test_compiler_creation() {
60        let compiler = Compiler::new(PathBuf::from("/tmp"));
61        assert_eq!(compiler.output_dir, PathBuf::from("/tmp"));
62    }
63
64    #[test]
65    fn test_compiler_has_lowerer() {
66        let compiler = Compiler::new(PathBuf::from("/tmp"));
67        // Lowerer is private but we can verify it exists by using the compiler
68        assert_eq!(compiler.output_dir, PathBuf::from("/tmp"));
69    }
70
71    #[test]
72    fn test_compiler_has_codegen() {
73        let compiler = Compiler::new(PathBuf::from("/tmp"));
74        // CodeGenerator is private but we can verify it exists
75        assert_eq!(compiler.output_dir, PathBuf::from("/tmp"));
76    }
77
78    #[test]
79    fn test_compiler_output_dir() {
80        let test_dir = PathBuf::from("/var/tmp/test");
81        let compiler = Compiler::new(test_dir.clone());
82        assert_eq!(compiler.output_dir, test_dir);
83    }
84
85    #[test]
86    fn test_compile_with_empty_forms() {
87        use tempfile::TempDir;
88
89        let temp_dir = TempDir::new().unwrap();
90        let output_dir = temp_dir.path().join("build");
91        std::fs::create_dir_all(&output_dir).unwrap();
92
93        let mut compiler = Compiler::new(output_dir.clone());
94        let output_path = temp_dir.path().join("test_output");
95
96        // This will fail because rustc isn't available or the generated code is invalid
97        // but we're just testing that the compilation pipeline runs
98        let result = compiler.compile(vec![], &output_path);
99        // We expect this to error (no rustc or invalid generated code)
100        // but the important thing is that it attempts compilation
101        assert!(result.is_ok() || result.is_err());
102    }
103}