helix/dna/
compiler.rs

1use crate::dna::hel::binary::{HelixBinary, CompressionMethod};
2pub use crate::dna::mds::optimizer::{Optimizer, OptimizationLevel};
3use crate::dna::mds::serializer::BinarySerializer;
4use crate::dna::mds::bundle::Bundler;
5use crate::dna::mds::runtime::HelixVM;
6use crate::dna::atp::ast::HelixAst;
7use crate::dna::mds::codegen::{CodeGenerator, HelixIR};
8use crate::{parse, validate};
9use std::path::{Path, PathBuf};
10use std::fs;
11use std::time::Instant;
12
13#[derive(Clone)]
14pub struct Compiler {
15    optimization_level: OptimizationLevel,
16    enable_compression: bool,
17    enable_cache: bool,
18    verbose: bool,
19    cache_dir: Option<PathBuf>,
20}
21impl Compiler {
22    pub fn new(optimization_level: OptimizationLevel) -> Self {
23        Self {
24            optimization_level,
25            enable_compression: true,
26            enable_cache: true,
27            verbose: false,
28            cache_dir: None,
29        }
30    }
31    pub fn builder() -> CompilerBuilder {
32        CompilerBuilder::default()
33    }
34    pub fn compile_file<P: AsRef<Path>>(
35        &self,
36        input: P,
37    ) -> Result<HelixBinary, CompileError> {
38        let start = Instant::now();
39        let path = input.as_ref();
40        if self.verbose {
41            println!("Compiling: {}", path.display());
42        }
43        if self.enable_cache {
44            if let Some(cached) = self.check_cache(path)? {
45                if self.verbose {
46                    println!("  Using cached version");
47                }
48                return Ok(cached);
49            }
50        }
51        let source = fs::read_to_string(path)
52            .map_err(|e| CompileError::IoError(format!("Failed to read file: {}", e)))?;
53        let binary = self.compile_source(&source, Some(path))?;
54        if self.enable_cache {
55            self.cache_binary(path, &binary)?;
56        }
57        if self.verbose {
58            let elapsed = start.elapsed();
59            println!("  Compiled in {:?}", elapsed);
60        }
61        Ok(binary)
62    }
63    pub fn compile_source(
64        &self,
65        source: &str,
66        source_path: Option<&Path>,
67    ) -> Result<HelixBinary, CompileError> {
68        let ast = parse(source).map_err(|e| CompileError::ParseError(e.to_string()))?;
69        validate(&ast).map_err(|e| CompileError::ValidationError(e.to_string()))?;
70        let mut generator = CodeGenerator::new();
71        let ir = generator.generate(&ast);
72        let optimized_ir = self.optimize_ir(ir);
73        let binary = self.ir_to_binary(optimized_ir, source_path)?;
74        Ok(binary)
75    }
76    pub fn compile_bundle<P: AsRef<Path>>(
77        &self,
78        directory: P,
79    ) -> Result<HelixBinary, CompileError> {
80        let bundler = Bundler::new();
81        bundler.bundle_directory(directory, self.optimization_level)
82    }
83    pub fn decompile(&self, binary: &HelixBinary) -> Result<String, CompileError> {
84        let ast = self.binary_to_ast(binary)?;
85        Ok(crate::pretty_print(&ast))
86    }
87    fn optimize_ir(&self, mut ir: HelixIR) -> HelixIR {
88        let mut optimizer = Optimizer::new(self.optimization_level);
89        optimizer.optimize(&mut ir);
90        ir
91    }
92    fn ir_to_binary(
93        &self,
94        ir: HelixIR,
95        source_path: Option<&Path>,
96    ) -> Result<HelixBinary, CompileError> {
97        let serializer = BinarySerializer::new(self.enable_compression)
98            .with_compression_method(
99                if self.enable_compression {
100                    CompressionMethod::Lz4
101                } else {
102                    CompressionMethod::Lz4
103                },
104            );
105        let binary = serializer
106            .serialize(ir, source_path)
107            .map_err(|e| CompileError::SerializationError(e.to_string()))?;
108        Ok(binary)
109    }
110    fn binary_to_ast(&self, binary: &HelixBinary) -> Result<HelixAst, CompileError> {
111        let deserializer = BinarySerializer::new(false);
112        let ir = deserializer
113            .deserialize_to_ir(binary)
114            .map_err(|e| CompileError::DeserializationError(e.to_string()))?;
115        ir_to_ast(ir)
116    }
117    fn check_cache(
118        &self,
119        source_path: &Path,
120    ) -> Result<Option<HelixBinary>, CompileError> {
121        if let Some(cache_dir) = &self.cache_dir {
122            let cache_path = cache_path_for(cache_dir, source_path);
123            if cache_path.exists() {
124                let source_modified = fs::metadata(source_path)
125                    .and_then(|m| m.modified())
126                    .map_err(|e| CompileError::IoError(e.to_string()))?;
127                let cache_modified = fs::metadata(&cache_path)
128                    .and_then(|m| m.modified())
129                    .map_err(|e| CompileError::IoError(e.to_string()))?;
130                if cache_modified > source_modified {
131                    let serializer = BinarySerializer::new(false);
132                    return serializer
133                        .read_from_file(&cache_path)
134                        .map(Some)
135                        .map_err(|e| CompileError::CacheError(e.to_string()));
136                }
137            }
138        }
139        Ok(None)
140    }
141    fn cache_binary(
142        &self,
143        source_path: &Path,
144        binary: &HelixBinary,
145    ) -> Result<(), CompileError> {
146        if let Some(cache_dir) = &self.cache_dir {
147            let cache_path = cache_path_for(cache_dir, source_path);
148            if let Some(parent) = cache_path.parent() {
149                fs::create_dir_all(parent)
150                    .map_err(|e| CompileError::IoError(e.to_string()))?;
151            }
152            let serializer = BinarySerializer::new(self.enable_compression);
153            serializer
154                .write_to_file(binary, &cache_path)
155                .map_err(|e| CompileError::CacheError(e.to_string()))?;
156        }
157        Ok(())
158    }
159}
160#[derive(Default)]
161pub struct CompilerBuilder {
162    optimization_level: OptimizationLevel,
163    enable_compression: bool,
164    enable_cache: bool,
165    verbose: bool,
166    cache_dir: Option<PathBuf>,
167}
168impl CompilerBuilder {
169    pub fn optimization_level(mut self, level: OptimizationLevel) -> Self {
170        self.optimization_level = level;
171        self
172    }
173    pub fn compression(mut self, enable: bool) -> Self {
174        self.enable_compression = enable;
175        self
176    }
177    pub fn cache(mut self, enable: bool) -> Self {
178        self.enable_cache = enable;
179        self
180    }
181    pub fn cache_dir<P: Into<PathBuf>>(mut self, dir: P) -> Self {
182        self.cache_dir = Some(dir.into());
183        self
184    }
185    pub fn verbose(mut self, enable: bool) -> Self {
186        self.verbose = enable;
187        self
188    }
189    pub fn build(self) -> Compiler {
190        Compiler {
191            optimization_level: self.optimization_level,
192            enable_compression: self.enable_compression,
193            enable_cache: self.enable_cache,
194            verbose: self.verbose,
195            cache_dir: self.cache_dir,
196        }
197    }
198}
199#[derive(Debug)]
200pub enum CompileError {
201    IoError(String),
202    ParseError(String),
203    ValidationError(String),
204    OptimizationError(String),
205    SerializationError(String),
206    DeserializationError(String),
207    CacheError(String),
208}
209impl std::fmt::Display for CompileError {
210    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
211        match self {
212            Self::IoError(e) => write!(f, "I/O error: {}", e),
213            Self::ParseError(e) => write!(f, "Parse error: {}", e),
214            Self::ValidationError(e) => write!(f, "Validation error: {}", e),
215            Self::OptimizationError(e) => write!(f, "Optimization error: {}", e),
216            Self::SerializationError(e) => write!(f, "Serialization error: {}", e),
217            Self::DeserializationError(e) => write!(f, "Deserialization error: {}", e),
218            Self::CacheError(e) => write!(f, "Cache error: {}", e),
219        }
220    }
221}
222impl std::error::Error for CompileError {}
223fn ir_to_ast(ir: HelixIR) -> Result<HelixAst, CompileError> {
224    let mut declarations = Vec::new();
225    for (_id, agent) in ir.symbol_table.agents {
226        let name = ir
227            .string_pool
228            .get(agent.name_idx)
229            .unwrap_or(&format!("agent_{}", agent.id))
230            .clone();
231        declarations
232            .push(
233                crate::dna::atp::ast::Declaration::Agent(crate::dna::atp::ast::AgentDecl {
234                    name,
235                    properties: std::collections::HashMap::new(),
236                    capabilities: None,
237                    backstory: None,
238                    tools: None,
239                }),
240            );
241    }
242    Ok(HelixAst { declarations })
243}
244fn cache_path_for(cache_dir: &Path, source_path: &Path) -> PathBuf {
245    let mut hasher = std::collections::hash_map::DefaultHasher::new();
246    use std::hash::{Hash, Hasher};
247    source_path.hash(&mut hasher);
248    let hash = hasher.finish();
249    cache_dir.join(format!("{:x}.hlxb", hash))
250}
251#[cfg(test)]
252mod tests {
253    use super::*;
254    #[test]
255    fn test_compiler_builder() {
256        let compiler = Compiler::builder()
257            .optimization_level(OptimizationLevel::Two)
258            .compression(true)
259            .cache(false)
260            .verbose(true)
261            .build();
262        assert_eq!(compiler.optimization_level, OptimizationLevel::Two);
263        assert!(compiler.enable_compression);
264        assert!(! compiler.enable_cache);
265        assert!(compiler.verbose);
266    }
267    #[test]
268    fn test_compile_simple() {
269        let source = r#"
270            agent "test" {
271                model = "gpt-4"
272                temperature = 0.7
273            }
274        "#;
275        let compiler = Compiler::new(OptimizationLevel::Zero);
276        let result = compiler.compile_source(source, None);
277        assert!(result.is_ok());
278    }
279}