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_with_intrinsics = swamp_script_core::create_module(&compiler_version);
118
119    let core_ast_module = parse_single_module(
120        source_map,
121        &core_module_with_intrinsics.symbol_table.module_path(),
122    )?;
123
124    let mut state = ProgramState::new();
125
126    let half_completed_core_symbol_table = core_module_with_intrinsics.symbol_table.clone();
127    let default_symbol_table_for_core_with_intrinsics = half_completed_core_symbol_table.clone();
128
129    let mut core_analyzed_definition_table = analyze_single_module(
130        &mut state,
131        default_symbol_table_for_core_with_intrinsics.clone(),
132        &modules,
133        half_completed_core_symbol_table.clone().into(),
134        &core_ast_module,
135        source_map,
136        &core_module_with_intrinsics.symbol_table.module_path(),
137    )?;
138    // Overwrite the default lookup table to the definition table
139    core_analyzed_definition_table
140        .extend_intrinsic_functions_from(&default_symbol_table_for_core_with_intrinsics);
141    core_analyzed_definition_table
142        .extend_basic_from(&default_symbol_table_for_core_with_intrinsics);
143
144    let source_map_lookup = SourceMapWrapper { source_map };
145    let pretty_printer = SourceMapDisplay {
146        source_map: &source_map_lookup,
147    };
148
149    let display_core_analyzed_definition_table = SymbolTableDisplay {
150        symbol_table: &core_analyzed_definition_table,
151        source_map_display: &pretty_printer,
152    };
153
154    info!(%display_core_analyzed_definition_table, "core analyzed symbol table");
155
156    core_module_with_intrinsics.symbol_table = core_analyzed_definition_table;
157
158    // core module is done, so add it read only to the modules
159    let core_module_ref = Rc::new(core_module_with_intrinsics);
160    modules.add(core_module_ref.clone());
161
162    let mut default_symbol_table_for_others = SymbolTable::new(&[]);
163
164    // Prelude for the core module
165    // Expose the basic primitive types, like `Int`, `String`, `Float`, `Bool`
166    // so they can be references without a `use core::{Int, String, Float, Bool}` statement.
167    default_symbol_table_for_others
168        .extend_alias_from(&core_module_ref.symbol_table)
169        .unwrap();
170
171    // Add `core` module without the version number, so they can be referenced from code
172    default_symbol_table_for_others
173        .add_package_version(swamp_script_core::PACKAGE_NAME, compiler_version)
174        .expect("should work");
175
176    let source_map_lookup = SourceMapWrapper { source_map };
177    let pretty_printer = SourceMapDisplay {
178        source_map: &source_map_lookup,
179    };
180
181    let symbol_table_display = SymbolTableDisplay {
182        symbol_table: &default_symbol_table_for_others,
183        source_map_display: &pretty_printer,
184    };
185
186    info!(%symbol_table_display, "default_symbol_table");
187
188    let program = Program::new(state, modules, default_symbol_table_for_others);
189
190    let result = BootstrapResult {
191        program,
192        core_module_path: core_module_ref.symbol_table.module_path().clone(),
193    };
194    Ok(result)
195}
196
197pub fn compile_and_analyze_all_modules(
198    module_path: &[String],
199    resolved_program: &mut Program,
200    source_map: &mut SourceMap,
201    core_symbol_table: SymbolTableRef,
202) -> Result<(), ScriptResolveError> {
203    let mut dependency_parser = DependencyParser::new();
204
205    let module_paths_in_order = parse_local_modules_and_get_order(
206        module_path.to_vec(),
207        &mut dependency_parser,
208        source_map,
209    )?;
210
211    analyze_modules_in_order(
212        &mut resolved_program.state,
213        &resolved_program.default_symbol_table,
214        &mut resolved_program.modules,
215        core_symbol_table,
216        source_map,
217        &module_paths_in_order,
218        &dependency_parser,
219    )?;
220
221    Ok(())
222}
223
224pub fn remove_version_from_package_name_regex(package_name_with_version: &str) -> String {
225    let re = Regex::new(
226        r"-(?P<version>[0-9]+(?:\.[0-9]+)*(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)?(?:\+.*)?$",
227    )
228    .unwrap();
229    re.replace(package_name_with_version, "").to_string()
230}
231
232/// # Errors
233///
234/// # Panics
235///
236pub fn compile_analyze_and_link_without_version(
237    root_module_path: &[String],
238    resolved_program: &mut Program,
239    source_map: &mut SourceMap,
240    core_symbol_table: SymbolTableRef,
241) -> Result<(), ScriptResolveError> {
242    let mangrove_render_result = compile_and_analyze(
243        root_module_path,
244        resolved_program,
245        source_map,
246        core_symbol_table,
247    );
248
249    match mangrove_render_result {
250        Ok(..) => {}
251        Err(err) => {
252            show_script_resolve_error(&err, source_map, &Path::new(""));
253            Err(err)?;
254        }
255    }
256    let mangrove_render_module = resolved_program.modules.get(root_module_path).unwrap();
257
258    let first_part = remove_version_from_package_name_regex(&root_module_path[0]);
259    let mut without_version_path: Vec<String> = root_module_path.to_vec();
260    without_version_path[0] = first_part;
261
262    resolved_program
263        .modules
264        .link_module(&without_version_path, mangrove_render_module.clone());
265
266    Ok(())
267}
268
269/// # Errors
270///
271pub fn compile_and_analyze(
272    root_module_path: &[String],
273    resolved_program: &mut Program,
274    source_map: &mut SourceMap,
275    core_symbol_table: SymbolTableRef,
276) -> Result<(), ScriptResolveError> {
277    compile_and_analyze_all_modules(
278        root_module_path,
279        resolved_program,
280        source_map,
281        core_symbol_table,
282    )
283}
284
285pub fn current_path() -> PathBuf {
286    current_dir().unwrap()
287}
288
289/// # Errors
290///
291/// # Panics
292///
293pub fn bootstrap_and_compile(
294    source_map: &mut SourceMap,
295    //root_dependencies: SeqMap<String, TinyVersion>,
296    root_path: &[String],
297) -> Result<Program, ScriptResolveError> {
298    let bootstrap_result = bootstrap_modules(source_map).inspect_err(|err| {
299        show_script_resolve_error(err, source_map, &current_path());
300    })?;
301
302    let mut program = bootstrap_result.program;
303
304    let core_symbol_table = program
305        .modules
306        .get(&bootstrap_result.core_module_path)
307        .unwrap()
308        .symbol_table
309        .clone();
310
311    /*
312    for (package_name, tiny_version) in root_dependencies {
313        program
314            .default_symbol_table
315            .add_package_version(&package_name, tiny_version)
316            .unwrap()
317    }
318
319     */
320
321    compile_and_analyze_all_modules(
322        root_path,
323        &mut program,
324        source_map,
325        core_symbol_table.into(),
326    )
327    .inspect_err(|err| {
328        show_script_resolve_error(err, source_map, &current_path());
329    })?;
330
331    // debug_all_modules(&program.modules, source_map);
332
333    Ok(program)
334}
335
336pub fn debug_all_modules(modules: &Modules, source_map: &SourceMap) {
337    for (_name, module) in modules.modules() {
338        debug_module(&module.symbol_table, source_map)
339    }
340}
341pub fn debug_module(symbol_table: &SymbolTable, source_map: &SourceMap) {
342    let source_map_lookup = SourceMapWrapper { source_map };
343    let pretty_printer = SourceMapDisplay {
344        source_map: &source_map_lookup,
345    };
346
347    let symbol_table_display = SymbolTableDisplay {
348        symbol_table: &symbol_table,
349        source_map_display: &pretty_printer,
350    };
351
352    info!(%symbol_table_display, "{:?}", symbol_table.module_path());
353}