1use joule_common::{Diagnostic, SourceMap, Symbol};
33use std::collections::{HashMap, HashSet};
34use std::path::Path;
35
36#[derive(Debug, Clone, Default)]
38pub struct CompileOptions {
39 pub energy_check: bool,
41 pub opt_level: u8,
43 pub file_to_module: HashMap<u32, Symbol>,
45}
46
47pub struct CompileOutput {
49 pub hir: joule_hir::Hir,
51 pub mir: joule_mir::MirContext,
53 pub conflicting_types: HashSet<Symbol>,
55 pub source_map: SourceMap,
57}
58
59pub fn compile_source(
63 source: &str,
64 filename: &str,
65 options: &CompileOptions,
66) -> Result<CompileOutput, Vec<Diagnostic>> {
67 let mut source_map = SourceMap::new();
68 let file_id = source_map.add_file(filename.to_string(), source.to_string());
69
70 let tokens = {
72 let lexer = joule_lexer::Lexer::new(source, file_id);
73 lexer.tokenize()
74 };
75
76 let ast = {
78 let parser = joule_parser::Parser::new(tokens);
79 parser.parse_file()?
80 };
81
82 let mut typeck = joule_typeck::TypeChecker::new();
84 typeck.set_energy_budget_checking(options.energy_check);
85 if !options.file_to_module.is_empty() {
86 typeck.set_file_to_module(options.file_to_module.clone());
87 }
88
89 let hir = typeck.check_file(&ast)?;
90 let conflicting_types = typeck.conflicting_types().clone();
91
92 let mir = joule_mir::build::build_mir(&hir);
94
95 let mut borrowck = joule_borrowck::BorrowChecker::new();
97 borrowck.check_context(&mir)?;
98
99 Ok(CompileOutput {
100 hir,
101 mir,
102 conflicting_types,
103 source_map,
104 })
105}
106
107pub fn compile_to_c(
111 source: &str,
112 filename: &str,
113 options: &CompileOptions,
114) -> Result<String, Vec<Diagnostic>> {
115 let output = compile_source(source, filename, options)?;
116 joule_codegen_c::compile_to_c_with_modules(
117 &output.mir,
118 &options.file_to_module,
119 &output.conflicting_types,
120 )
121 .map_err(|e| vec![Diagnostic::error(format!("C code generation failed: {e}"))])
122}
123
124pub fn parse_source(
129 source: &str,
130 filename: &str,
131) -> Result<(joule_ast::File, SourceMap), Vec<Diagnostic>> {
132 let mut source_map = SourceMap::new();
133 let file_id = source_map.add_file(filename.to_string(), source.to_string());
134
135 let tokens = {
136 let lexer = joule_lexer::Lexer::new(source, file_id);
137 lexer.tokenize()
138 };
139
140 let ast = {
141 let parser = joule_parser::Parser::new(tokens);
142 parser.parse_file()?
143 };
144
145 Ok((ast, source_map))
146}
147
148pub fn type_check_source(
153 source: &str,
154 filename: &str,
155 options: &CompileOptions,
156) -> Result<(joule_hir::Hir, SourceMap), Vec<Diagnostic>> {
157 let mut source_map = SourceMap::new();
158 let file_id = source_map.add_file(filename.to_string(), source.to_string());
159
160 let tokens = {
161 let lexer = joule_lexer::Lexer::new(source, file_id);
162 lexer.tokenize()
163 };
164
165 let ast = {
166 let parser = joule_parser::Parser::new(tokens);
167 parser.parse_file()?
168 };
169
170 let mut typeck = joule_typeck::TypeChecker::new();
171 typeck.set_energy_budget_checking(options.energy_check);
172 if !options.file_to_module.is_empty() {
173 typeck.set_file_to_module(options.file_to_module.clone());
174 }
175
176 let hir = typeck.check_file(&ast)?;
177 Ok((hir, source_map))
178}
179
180pub fn compile_multi_file(
185 sources: &[(&str, &str)], options: &CompileOptions,
187) -> Result<CompileOutput, Vec<Diagnostic>> {
188 let mut source_map = SourceMap::new();
189 let mut all_items: Vec<joule_ast::Item> = Vec::new();
190 let mut file_to_module = options.file_to_module.clone();
191
192 for (filename, source) in sources {
193 let file_id = source_map.add_file(filename.to_string(), source.to_string());
194
195 if let Some(stem) = Path::new(filename).file_stem().and_then(|s| s.to_str()) {
197 file_to_module.insert(file_id, Symbol::intern(stem));
198 }
199
200 let tokens = {
201 let lexer = joule_lexer::Lexer::new(source, file_id);
202 lexer.tokenize()
203 };
204
205 let ast = {
206 let parser = joule_parser::Parser::new(tokens);
207 parser.parse_file()?
208 };
209
210 for item in ast.items {
212 if !matches!(item, joule_ast::Item::Use(_)) {
213 all_items.push(item);
214 }
215 }
216 }
217
218 let merged_ast = joule_ast::File { items: all_items };
219
220 let mut typeck = joule_typeck::TypeChecker::new();
222 typeck.set_energy_budget_checking(options.energy_check);
223 typeck.set_file_to_module(file_to_module.clone());
224
225 let hir = typeck.check_file(&merged_ast)?;
226 let conflicting_types = typeck.conflicting_types().clone();
227
228 let mir = joule_mir::build::build_mir(&hir);
230 let mut borrowck = joule_borrowck::BorrowChecker::new();
231 borrowck.check_context(&mir)?;
232
233 Ok(CompileOutput {
234 hir,
235 mir,
236 conflicting_types,
237 source_map,
238 })
239}