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