1use regex::Regex;
6use seq_map::SeqMap;
7use source_map_cache::SourceMap;
8use source_map_cache::SourceMapWrapper;
9use std::env::current_dir;
10use std::io;
11use std::path::Path;
12use std::path::PathBuf;
13use std::str::FromStr;
14pub use swamp_analyzer::prelude::Program;
15use swamp_analyzer::Analyzer;
16use swamp_core::text::core_text;
17use swamp_dep_loader::{parse_local_modules_and_get_order, parse_single_module_from_text, swamp_registry_path, DependencyParser, ParsedAstModule, RunMode};
18use swamp_error_report::analyze::show_analyzer_error;
19use swamp_error_report::prelude::Kind;
20use swamp_error_report::{prelude::show_script_resolve_error, ScriptResolveError};
21use swamp_modules::modules::{ModuleRef, Modules};
22use swamp_modules::prelude::Module;
23use swamp_modules::symtbl::{SymbolTable, SymbolTableRef};
24use swamp_pretty_print::{ImplsDisplay, SourceMapDisplay, SymbolTableDisplay};
25use swamp_program_analyzer::analyze_modules_in_order;
26use swamp_semantic::err::Error;
27use swamp_semantic::{formal_module_name, AssociatedImpls, ProgramState};
28use swamp_std::std_text;
29use swamp_types::prelude::print_types;
30use time_dilation::ScopedTimer;
31use tiny_ver::TinyVersion;
32use tracing::{info, trace};
33
34pub const COMPILER_VERSION: &str = "0.0.0";
35pub const CORE_VERSION: &str = "core-0.0.0";
36
37pub fn analyze_ast_module_skip_expression(
40 analyzer: &mut Analyzer,
41 parsed_ast_module: &ParsedAstModule,
42) {
43 for definition in &parsed_ast_module.ast_module.definitions {
44 analyzer.analyze_definition(definition);
45 }
46}
47
48#[must_use]
49pub fn analyze_single_module(
50 state: &mut ProgramState,
51 default_symbol_table: SymbolTable,
52 modules: &Modules,
53 core_symbol_table: SymbolTableRef,
54 parsed_ast_module: &ParsedAstModule,
55 source_map: &SourceMap,
56 versioned_module_path: &[String],
57) -> SymbolTable {
58 let mut analyzer = Analyzer::new(
59 state,
60 modules,
61 core_symbol_table,
62 source_map,
63 versioned_module_path,
64 parsed_ast_module.file_id,
65 );
66
67 analyzer.shared.lookup_table = default_symbol_table;
68
69 analyze_ast_module_skip_expression(&mut analyzer, parsed_ast_module);
72
73 analyzer.shared.definition_table
74}
75
76pub fn create_source_map(registry_path: &Path, local_path: &Path) -> io::Result<SourceMap> {
77 trace!(?registry_path, ?local_path, "mounting source map");
78
79 let mut mounts = SeqMap::new();
80 mounts
81 .insert("crate".to_string(), local_path.to_path_buf())
82 .unwrap();
83
84 mounts
85 .insert("registry".to_string(), registry_path.to_path_buf())
86 .unwrap();
87
88 SourceMap::new(&mounts)
89}
90
91pub fn create_registry_source_map(registry_path: &Path) -> io::Result<SourceMap> {
92 trace!(?registry_path, "mounting registry path source map");
93
94 let mut mounts = SeqMap::new();
95 mounts
96 .insert("registry".to_string(), registry_path.to_path_buf())
97 .unwrap();
98
99 SourceMap::new(&mounts)
100}
101
102#[derive(Debug)]
103pub struct BootstrapResult {
104 pub program: Program,
105 pub core_module_path: Vec<String>,
106}
107
108pub fn bootstrap_modules(
115 source_map: &mut SourceMap,
116) -> Result<BootstrapResult, ScriptResolveError> {
117 let compiler_version = TinyVersion::from_str(COMPILER_VERSION).unwrap();
118 trace!(%compiler_version, "booting up compiler");
119 let mut state = ProgramState::new();
120
121 let mut modules = Modules::new();
122
123 let core_module_with_intrinsics =
124 swamp_core::create_module(&compiler_version, &mut state.types);
125
126 let core_path = core_module_with_intrinsics.symbol_table.module_path();
127 let core_parsed_ast_module =
128 parse_single_module_from_text(source_map, &core_path, &core_text())?;
129
130 let half_completed_core_symbol_table = core_module_with_intrinsics.symbol_table.clone();
131 let default_symbol_table_for_core_with_intrinsics = half_completed_core_symbol_table.clone();
132
133 let mut analyzed_core_symbol_table = analyze_single_module(
134 &mut state,
135 default_symbol_table_for_core_with_intrinsics.clone(),
136 &modules,
137 half_completed_core_symbol_table.clone().into(),
138 &core_parsed_ast_module,
139 source_map,
140 &core_module_with_intrinsics.symbol_table.module_path(),
141 );
142
143 analyzed_core_symbol_table
145 .extend_basic_from(&core_module_with_intrinsics.symbol_table)
146 .expect("couldn't extend core");
147 let mut default_module = swamp_core::create_module_with_name(&[]);
148 default_module
149 .symbol_table
150 .extend_alias_from(&analyzed_core_symbol_table)
151 .expect("extend basic alias and functions from core");
152
153 let core_module = Module::new(analyzed_core_symbol_table, vec![], None);
154 modules.add(ModuleRef::from(core_module));
155
156 let std_path = &["std".to_string()];
159 let std_module_with_intrinsics = swamp_core::create_module_with_name(std_path);
160 let std_ast_module = parse_single_module_from_text(source_map, std_path, &std_text())?;
161 let analyzed_std_symbol_table = analyze_single_module(
162 &mut state,
163 default_symbol_table_for_core_with_intrinsics,
164 &modules,
165 half_completed_core_symbol_table.into(),
166 &std_ast_module,
167 source_map,
168 &std_module_with_intrinsics.symbol_table.module_path(),
169 );
170 default_module
171 .symbol_table
172 .extend_basic_from(&analyzed_std_symbol_table)
173 .expect("extend basics from core");
174
175 let analyzed_std_module = Module::new(analyzed_std_symbol_table, vec![], None);
176
177 modules.add(ModuleRef::from(analyzed_std_module));
178
179 let bootstrap_program = Program::new(state, modules, default_module.symbol_table);
180
181 let result = BootstrapResult {
182 program: bootstrap_program,
183 core_module_path: core_path,
184 };
185
186 Ok(result)
187}
188
189pub fn compile_and_analyze_all_modules(
190 module_path: &[String],
191 resolved_program: &mut Program,
192 source_map: &mut SourceMap,
193 core_symbol_table: SymbolTableRef,
194) -> Result<(), ScriptResolveError> {
195 let mut dependency_parser = DependencyParser::new();
196
197 let module_paths_in_order =
198 parse_local_modules_and_get_order(module_path, &mut dependency_parser, source_map)?;
199
200 analyze_modules_in_order(
201 &mut resolved_program.state,
202 &resolved_program.default_symbol_table,
203 &mut resolved_program.modules,
204 &core_symbol_table,
205 source_map,
206 &module_paths_in_order,
207 &dependency_parser,
208 )?;
209
210 Ok(())
211}
212
213#[must_use]
214pub fn remove_version_from_package_name_regex(package_name_with_version: &str) -> String {
215 let re = Regex::new(
216 r"-(?P<version>[0-9]+(?:\.[0-9]+)*(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)?(?:\+.*)?$",
217 )
218 .unwrap();
219 re.replace(package_name_with_version, "").to_string()
220}
221
222#[must_use]
223pub fn current_path() -> PathBuf {
224 current_dir().unwrap()
225}
226
227pub struct CompileOptions {
228 pub show_semantic: bool,
229 pub show_modules: bool,
230 pub show_errors: bool,
231 pub show_warnings: bool,
232 pub show_hints: bool,
233 pub show_information: bool,
234 pub show_types: bool,
235}
236
237pub fn bootstrap_and_compile(
242 source_map: &mut SourceMap,
243 root_path: &[String],
244 options: &CompileOptions,
245) -> Result<Program, ScriptResolveError> {
246 let bootstrap_timer = ScopedTimer::new("bootstrap");
247 let bootstrap_result = bootstrap_modules(source_map).inspect_err(|err| {
248 show_script_resolve_error(err, source_map, ¤t_path());
249 })?;
250 drop(bootstrap_timer);
251 let mut program = bootstrap_result.program;
252
253 let core_symbol_table = program
254 .modules
255 .get(&bootstrap_result.core_module_path)
256 .unwrap()
257 .symbol_table
258 .clone();
259
260 let compile_all_modules_timer = ScopedTimer::new("compile all modules");
261 compile_and_analyze_all_modules(
262 root_path,
263 &mut program,
264 source_map,
265 core_symbol_table.into(),
266 )
267 .inspect_err(|err| {
268 show_script_resolve_error(err, source_map, ¤t_path());
269 })?;
270
271 drop(compile_all_modules_timer);
272
273 if options.show_modules {
274 debug_all_modules(&program.modules, source_map);
275 }
276
277 if options.show_semantic {
278 debug_all_impl_functions(&program.state.associated_impls, source_map);
279 }
280
281 if options.show_types {
282 let mut str = String::new();
283 print_types(&mut str, &program.state.types).expect("should work");
284 eprintln!("{str}");
285 }
287
288 let source_map_wrapper = SourceMapWrapper {
289 source_map,
290 current_dir: current_dir().unwrap(),
291 };
292
293 if options.show_errors {
294 show_errors(&program.state.errors, &source_map_wrapper);
295 }
296
297 if options.show_hints {
298 show_hints(&program.state.hints, &source_map_wrapper);
299 }
300
301 if options.show_information {
302 show_information(&program.state.infos, &source_map_wrapper);
303 }
304
305 Ok(program)
306}
307
308fn show_errors(errors: &[Error], source_map_wrapper: &SourceMapWrapper) {
309 for err in errors.iter().take(4) {
310 show_analyzer_error(
311 err,
312 Kind::Error,
313 source_map_wrapper.source_map,
314 &source_map_wrapper.current_dir,
315 );
316 }
317}
318
319fn show_hints(hints: &[Error], source_map_wrapper: &SourceMapWrapper) {
320 for err in hints {
321 show_analyzer_error(
322 err,
323 Kind::Warning,
324 source_map_wrapper.source_map,
325 &source_map_wrapper.current_dir,
326 );
327 }
328}
329
330fn show_information(infos: &[Error], source_map_wrapper: &SourceMapWrapper) {
331 for err in infos {
332 show_analyzer_error(
333 err,
334 Kind::Help,
335 source_map_wrapper.source_map,
336 &source_map_wrapper.current_dir,
337 );
338 }
339}
340
341pub fn debug_all_modules(modules: &Modules, source_map: &SourceMap) {
342 for (_name, module) in modules.modules() {
343 debug_module(&module.symbol_table, source_map);
344 }
345}
346pub fn debug_module(symbol_table: &SymbolTable, source_map: &SourceMap) {
347 let source_map_lookup = SourceMapWrapper {
348 source_map,
349 current_dir: current_dir().unwrap(),
350 };
351 let pretty_printer = SourceMapDisplay {
352 source_map: &source_map_lookup,
353 };
354
355 let symbol_table_display = SymbolTableDisplay {
356 symbol_table,
357 source_map_display: &pretty_printer,
358 };
359
360 info!(
361 "module: {}{}",
362 formal_module_name(&symbol_table.module_path()),
363 symbol_table_display
364 );
365}
366
367fn debug_all_impl_functions(all_impls: &AssociatedImpls, source_map: &mut SourceMap) {
368 let source_map_lookup = SourceMapWrapper {
369 source_map,
370 current_dir: current_dir().unwrap(),
371 };
372 let pretty_printer = SourceMapDisplay {
373 source_map: &source_map_lookup,
374 };
375
376 let symbol_table_display = ImplsDisplay {
377 all_impls,
378 source_map: &pretty_printer,
379 };
380
381 info!("impls: {}", symbol_table_display);
382}
383
384#[must_use]
385pub fn compile_string(script: &str, run_mode: &RunMode) -> (Program, ModuleRef, SourceMap) {
386 let mut source_map = SourceMap::new(&SeqMap::default()).unwrap();
387 let file_id = 0xffff;
388
389 if let Some(swamp_home) = swamp_registry_path(run_mode) {
390 source_map.add_mount("registry", &swamp_home).unwrap();
391 }
392
393 source_map.add_mount("crate", Path::new("/tmp/")).unwrap();
394 source_map.add_to_cache("crate", Path::new("test.swamp"), script, file_id);
395
396 let resolved_path_str = vec!["crate".to_string(), "test".to_string()];
397 let compile_options = CompileOptions {
398 show_semantic: false,
399 show_modules: false,
400 show_errors: true,
401 show_warnings: true,
402 show_hints: false,
403 show_information: false,
404 show_types: false,
405 };
406 let program =
407 bootstrap_and_compile(&mut source_map, &resolved_path_str, &compile_options).unwrap();
408 let main_module = program.modules.get(&resolved_path_str).unwrap().clone();
409
410 (program, main_module, source_map)
411}