swamp_script_compile/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/script
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use regex::Regex;
6use seq_map::SeqMap;
7use std::env::current_dir;
8use std::io;
9use std::path::Path;
10use std::path::PathBuf;
11use std::rc::Rc;
12use std::str::FromStr;
13use swamp_script_analyzer::Analyzer;
14use swamp_script_analyzer::prelude::{Error, Program};
15use swamp_script_dep_loader::{
16    DependencyParser, ParsedAstModule, parse_local_modules_and_get_order, parse_single_module,
17    swamp_registry_path,
18};
19use swamp_script_error_report::{ScriptResolveError, prelude::show_script_resolve_error};
20use swamp_script_eval_loader::analyze_modules_in_order;
21use swamp_script_modules::modules::{Module, Modules};
22use swamp_script_modules::symtbl::{SymbolTable, SymbolTableRef};
23use swamp_script_pretty_print::{SourceMapDisplay, SymbolTableDisplay};
24use swamp_script_semantic::ProgramState;
25use swamp_script_source_map::SourceMap;
26use swamp_script_source_map_lookup::SourceMapWrapper;
27use tiny_ver::TinyVersion;
28use tracing::{debug, info, trace};
29
30const COMPILER_VERSION: &str = "0.0.0";
31
32/// # Errors
33///
34pub fn analyze_ast_module_skip_expression(
35    analyzer: &mut Analyzer,
36    parsed_ast_module: &ParsedAstModule,
37) -> Result<(), Error> {
38    for definition in &parsed_ast_module.ast_module.definitions {
39        analyzer.analyze_definition(definition)?;
40    }
41    Ok(())
42}
43
44pub fn analyze_single_module(
45    state: &mut ProgramState,
46    default_symbol_table: SymbolTable,
47    modules: &Modules,
48    core_symbol_table: SymbolTableRef,
49    parsed_ast_module: &ParsedAstModule,
50    source_map: &SourceMap,
51    versioned_module_path: &[String],
52) -> Result<SymbolTable, Error> {
53    let mut analyzer = Analyzer::new(
54        state,
55        modules,
56        core_symbol_table,
57        source_map,
58        versioned_module_path,
59        parsed_ast_module.file_id,
60    );
61
62    analyzer.shared.lookup_table = default_symbol_table;
63
64    //    trace!(lookup_table=?analyzer.shared.lookup_table, "analyzer lookup_table");
65
66    analyze_ast_module_skip_expression(&mut analyzer, parsed_ast_module)?;
67
68    Ok(analyzer.shared.definition_table)
69}
70
71pub fn create_source_map(registry_path: &Path, local_path: &Path) -> io::Result<SourceMap> {
72    trace!(?registry_path, ?local_path, "mounting source map");
73
74    let mut mounts = SeqMap::new();
75    mounts
76        .insert("crate".to_string(), local_path.to_path_buf())
77        .unwrap();
78
79    mounts
80        .insert("registry".to_string(), registry_path.to_path_buf())
81        .unwrap();
82
83    SourceMap::new(&mounts)
84}
85
86pub fn create_registry_source_map(registry_path: &Path) -> io::Result<SourceMap> {
87    trace!(?registry_path, "mounting registry path source map");
88
89    let mut mounts = SeqMap::new();
90    mounts
91        .insert("registry".to_string(), registry_path.to_path_buf())
92        .unwrap();
93
94    SourceMap::new(&mounts)
95}
96
97#[derive(Debug)]
98pub struct BootstrapResult {
99    pub program: Program,
100    pub core_module_path: Vec<String>,
101}
102
103/// Bootstraps the core and ffi modules and creates a default symbol table
104///
105/// # Errors
106///
107/// # Panics
108/// In theory it can panic, but should be safe.
109pub fn bootstrap_modules(
110    mut source_map: &mut SourceMap,
111) -> Result<BootstrapResult, ScriptResolveError> {
112    let compiler_version = TinyVersion::from_str(COMPILER_VERSION).unwrap();
113    trace!(%compiler_version, "booting up compiler");
114
115    let mut modules = Modules::new();
116
117    let mut core_module = swamp_script_core::create_module(&compiler_version);
118
119    let mut default_symbol_table = SymbolTable::new(&[]);
120
121    // Prelude for the core module
122    // Expose the basic primitive types, like `Int`, `String`, `Float`, `Bool`
123    // so they can be references without a `use core::{Int, String, Float, Bool}` statement.
124    default_symbol_table
125        .extend_basic_from(&core_module.symbol_table)
126        .unwrap();
127
128    let core_ast_module = parse_single_module(source_map, &core_module.symbol_table.module_path())?;
129
130    let mut state = ProgramState::new();
131
132    let core_symbol_table = core_module.symbol_table.clone();
133
134    let mut core_analyzed_symbol_table = analyze_single_module(
135        &mut state,
136        core_symbol_table.clone(),
137        &modules,
138        core_symbol_table.clone().into(),
139        &core_ast_module,
140        source_map,
141        &core_module.symbol_table.module_path(),
142    )?;
143
144    debug!("analyzed core module");
145
146    core_analyzed_symbol_table
147        .extend_intrinsic_functions_from(&core_symbol_table)
148        .unwrap();
149
150    /*
151    let source_map_lookup = SourceMapWrapper { source_map };
152    let pretty_printer = SourceMapDisplay {
153        source_map: &source_map_lookup,
154    };
155
156    let symbol_table_display = SymbolTableDisplay {
157        symbol_table: &core_analyzed_symbol_table,
158        source_map_display: &pretty_printer,
159    };
160
161    info!(%symbol_table_display, "core symbol table");
162
163     */
164
165    core_module.symbol_table = core_analyzed_symbol_table;
166
167    // core module is done, so add it read only to the modules
168    let core_module_ref = Rc::new(core_module);
169    modules.add(core_module_ref.clone());
170
171    // Add `core` module without the version number, so they can be referenced from code
172    default_symbol_table
173        .add_package_version(swamp_script_core::PACKAGE_NAME, compiler_version)
174        .expect("should work");
175
176    let program = Program::new(state, modules, default_symbol_table);
177
178    let result = BootstrapResult {
179        program,
180        core_module_path: core_module_ref.symbol_table.module_path().clone(),
181    };
182    Ok(result)
183}
184
185pub fn compile_and_analyze_all_modules(
186    module_path: &[String],
187    resolved_program: &mut Program,
188    source_map: &mut SourceMap,
189    core_symbol_table: SymbolTableRef,
190) -> Result<(), ScriptResolveError> {
191    let mut dependency_parser = DependencyParser::new();
192
193    let module_paths_in_order = parse_local_modules_and_get_order(
194        module_path.to_vec(),
195        &mut dependency_parser,
196        source_map,
197    )?;
198
199    analyze_modules_in_order(
200        &mut resolved_program.state,
201        &resolved_program.default_symbol_table,
202        &mut resolved_program.modules,
203        core_symbol_table,
204        source_map,
205        &module_paths_in_order,
206        &dependency_parser,
207    )?;
208
209    Ok(())
210}
211
212pub fn remove_version_from_package_name_regex(package_name_with_version: &str) -> String {
213    let re = Regex::new(
214        r"-(?P<version>[0-9]+(?:\.[0-9]+)*(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)?(?:\+.*)?$",
215    )
216    .unwrap();
217    re.replace(package_name_with_version, "").to_string()
218}
219
220/// # Errors
221///
222/// # Panics
223///
224pub fn compile_analyze_and_link_without_version(
225    root_module_path: &[String],
226    resolved_program: &mut Program,
227    source_map: &mut SourceMap,
228    core_symbol_table: SymbolTableRef,
229) -> Result<(), ScriptResolveError> {
230    let mangrove_render_result = compile_and_analyze(
231        root_module_path,
232        resolved_program,
233        source_map,
234        core_symbol_table,
235    );
236
237    match mangrove_render_result {
238        Ok(..) => {}
239        Err(err) => {
240            show_script_resolve_error(&err, source_map, &Path::new(""));
241            Err(err)?;
242        }
243    }
244    let mangrove_render_module = resolved_program.modules.get(root_module_path).unwrap();
245
246    let first_part = remove_version_from_package_name_regex(&root_module_path[0]);
247    let mut without_version_path: Vec<String> = root_module_path.to_vec();
248    without_version_path[0] = first_part;
249
250    resolved_program
251        .modules
252        .link_module(&without_version_path, mangrove_render_module.clone());
253
254    Ok(())
255}
256
257/// # Errors
258///
259pub fn compile_and_analyze(
260    root_module_path: &[String],
261    resolved_program: &mut Program,
262    source_map: &mut SourceMap,
263    core_symbol_table: SymbolTableRef,
264) -> Result<(), ScriptResolveError> {
265    compile_and_analyze_all_modules(
266        root_module_path,
267        resolved_program,
268        source_map,
269        core_symbol_table,
270    )
271}
272
273pub fn current_path() -> PathBuf {
274    current_dir().unwrap()
275}
276
277/// # Errors
278///
279/// # Panics
280///
281pub fn bootstrap_and_compile(
282    source_map: &mut SourceMap,
283    //root_dependencies: SeqMap<String, TinyVersion>,
284    root_path: &[String],
285) -> Result<Program, ScriptResolveError> {
286    let bootstrap_result = bootstrap_modules(source_map).inspect_err(|err| {
287        show_script_resolve_error(err, source_map, &current_path());
288    })?;
289
290    let mut program = bootstrap_result.program;
291
292    let core_symbol_table = program
293        .modules
294        .get(&bootstrap_result.core_module_path)
295        .unwrap()
296        .symbol_table
297        .clone();
298
299    /*
300    for (package_name, tiny_version) in root_dependencies {
301        program
302            .default_symbol_table
303            .add_package_version(&package_name, tiny_version)
304            .unwrap()
305    }
306
307     */
308
309    compile_and_analyze_all_modules(
310        root_path,
311        &mut program,
312        source_map,
313        core_symbol_table.into(),
314    )
315    .inspect_err(|err| {
316        show_script_resolve_error(err, source_map, &current_path());
317    })?;
318
319    // debug_all_modules(&program.modules, source_map);
320
321    Ok(program)
322}
323
324pub fn debug_all_modules(modules: &Modules, source_map: &SourceMap) {
325    for (_name, module) in modules.modules() {
326        debug_module(&module.symbol_table, source_map)
327    }
328}
329pub fn debug_module(symbol_table: &SymbolTable, source_map: &SourceMap) {
330    let source_map_lookup = SourceMapWrapper { source_map };
331    let pretty_printer = SourceMapDisplay {
332        source_map: &source_map_lookup,
333    };
334
335    let symbol_table_display = SymbolTableDisplay {
336        symbol_table: &symbol_table,
337        source_map_display: &pretty_printer,
338    };
339
340    info!(%symbol_table_display, "{:?}", symbol_table.module_path());
341}