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