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}