swamp_compile/
lib.rs

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