use regex::Regex;
use seq_map::SeqMap;
use source_map_cache::SourceMap;
use source_map_cache::SourceMapWrapper;
use std::env::current_dir;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
use swamp_analyzer::Analyzer;
pub use swamp_analyzer::prelude::{Error, Program};
use swamp_dep_loader::{
DependencyParser, ParsedAstModule, parse_local_modules_and_get_order, parse_single_module,
swamp_registry_path,
};
use swamp_error_report::{ScriptResolveError, prelude::show_script_resolve_error};
use swamp_modules::modules::{ModuleRef, Modules};
use swamp_modules::symtbl::{SymbolTable, SymbolTableRef};
use swamp_pretty_print::{SourceMapDisplay, SymbolTableDisplay};
use swamp_program_analyzer::analyze_modules_in_order;
use swamp_semantic::ProgramState;
use time_dilation::ScopedTimer;
use tiny_ver::TinyVersion;
use tracing::{info, trace};
pub const COMPILER_VERSION: &str = "0.0.0";
pub const CORE_VERSION: &str = "core-0.0.0";
pub fn analyze_ast_module_skip_expression(
analyzer: &mut Analyzer,
parsed_ast_module: &ParsedAstModule,
) -> Result<(), Error> {
for definition in &parsed_ast_module.ast_module.definitions {
analyzer.analyze_definition(definition)?;
}
Ok(())
}
pub fn analyze_single_module(
state: &mut ProgramState,
default_symbol_table: SymbolTable,
modules: &Modules,
core_symbol_table: SymbolTableRef,
parsed_ast_module: &ParsedAstModule,
source_map: &SourceMap,
versioned_module_path: &[String],
) -> Result<SymbolTable, Error> {
let mut analyzer = Analyzer::new(
state,
modules,
core_symbol_table,
source_map,
versioned_module_path,
parsed_ast_module.file_id,
);
analyzer.shared.lookup_table = default_symbol_table;
analyze_ast_module_skip_expression(&mut analyzer, parsed_ast_module)?;
Ok(analyzer.shared.definition_table)
}
pub fn create_source_map(registry_path: &Path, local_path: &Path) -> io::Result<SourceMap> {
trace!(?registry_path, ?local_path, "mounting source map");
let mut mounts = SeqMap::new();
mounts
.insert("crate".to_string(), local_path.to_path_buf())
.unwrap();
mounts
.insert("registry".to_string(), registry_path.to_path_buf())
.unwrap();
SourceMap::new(&mounts)
}
pub fn create_registry_source_map(registry_path: &Path) -> io::Result<SourceMap> {
trace!(?registry_path, "mounting registry path source map");
let mut mounts = SeqMap::new();
mounts
.insert("registry".to_string(), registry_path.to_path_buf())
.unwrap();
SourceMap::new(&mounts)
}
#[derive(Debug)]
pub struct BootstrapResult {
pub program: Program,
pub core_module_path: Vec<String>,
}
pub fn bootstrap_modules(
source_map: &mut SourceMap,
) -> Result<BootstrapResult, ScriptResolveError> {
let compiler_version = TinyVersion::from_str(COMPILER_VERSION).unwrap();
trace!(%compiler_version, "booting up compiler");
let mut modules = Modules::new();
let mut core_module_with_intrinsics = swamp_core::create_module(&compiler_version);
let core_ast_module = parse_single_module(
source_map,
&core_module_with_intrinsics.symbol_table.module_path(),
)?;
let mut state = ProgramState::new();
let half_completed_core_symbol_table = core_module_with_intrinsics.symbol_table.clone();
let default_symbol_table_for_core_with_intrinsics = half_completed_core_symbol_table.clone();
let mut core_analyzed_definition_table = analyze_single_module(
&mut state,
default_symbol_table_for_core_with_intrinsics.clone(),
&modules,
half_completed_core_symbol_table.clone().into(),
&core_ast_module,
source_map,
&core_module_with_intrinsics.symbol_table.module_path(),
)?;
core_analyzed_definition_table
.extend_intrinsic_functions_from(&default_symbol_table_for_core_with_intrinsics)
.expect("core extend");
core_analyzed_definition_table
.extend_basic_from(&default_symbol_table_for_core_with_intrinsics)
.expect("extend basics from core");
core_module_with_intrinsics.symbol_table = core_analyzed_definition_table;
let core_module_ref = Rc::new(core_module_with_intrinsics);
modules.add(core_module_ref.clone());
let mut default_symbol_table_for_others = SymbolTable::new(&[]);
default_symbol_table_for_others
.extend_alias_from(&core_module_ref.symbol_table)
.unwrap();
default_symbol_table_for_others
.add_package_version(swamp_core::PACKAGE_NAME, compiler_version)
.expect("should work");
let source_map_lookup = SourceMapWrapper {
source_map,
current_dir: current_dir().unwrap(),
};
let pretty_printer = SourceMapDisplay {
source_map: &source_map_lookup,
};
let program = Program::new(state, modules, default_symbol_table_for_others);
let result = BootstrapResult {
program,
core_module_path: core_module_ref.symbol_table.module_path().clone(),
};
Ok(result)
}
pub fn compile_and_analyze_all_modules(
module_path: &[String],
resolved_program: &mut Program,
source_map: &mut SourceMap,
core_symbol_table: SymbolTableRef,
) -> Result<(), ScriptResolveError> {
let mut dependency_parser = DependencyParser::new();
let module_paths_in_order =
parse_local_modules_and_get_order(module_path, &mut dependency_parser, source_map)?;
analyze_modules_in_order(
&mut resolved_program.state,
&resolved_program.default_symbol_table,
&mut resolved_program.modules,
&core_symbol_table,
source_map,
&module_paths_in_order,
&dependency_parser,
)?;
Ok(())
}
pub fn remove_version_from_package_name_regex(package_name_with_version: &str) -> String {
let re = Regex::new(
r"-(?P<version>[0-9]+(?:\.[0-9]+)*(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)?(?:\+.*)?$",
)
.unwrap();
re.replace(package_name_with_version, "").to_string()
}
pub fn compile_analyze_and_link_without_version(
root_module_path: &[String],
resolved_program: &mut Program,
source_map: &mut SourceMap,
core_symbol_table: SymbolTableRef,
) -> Result<(), ScriptResolveError> {
let mangrove_render_result = compile_and_analyze(
root_module_path,
resolved_program,
source_map,
core_symbol_table,
);
match mangrove_render_result {
Ok(..) => {}
Err(err) => {
show_script_resolve_error(&err, source_map, Path::new(""));
Err(err)?;
}
}
let mangrove_render_module = resolved_program.modules.get(root_module_path).unwrap();
let first_part = remove_version_from_package_name_regex(&root_module_path[0]);
let mut without_version_path: Vec<String> = root_module_path.to_vec();
without_version_path[0] = first_part;
resolved_program
.modules
.link_module(&without_version_path, mangrove_render_module.clone());
Ok(())
}
pub fn compile_and_analyze(
root_module_path: &[String],
resolved_program: &mut Program,
source_map: &mut SourceMap,
core_symbol_table: SymbolTableRef,
) -> Result<(), ScriptResolveError> {
compile_and_analyze_all_modules(
root_module_path,
resolved_program,
source_map,
core_symbol_table,
)
}
pub fn current_path() -> PathBuf {
current_dir().unwrap()
}
pub fn bootstrap_and_compile(
source_map: &mut SourceMap,
root_path: &[String],
) -> Result<Program, ScriptResolveError> {
let bootstrap_timer = ScopedTimer::new("bootstrap");
let bootstrap_result = bootstrap_modules(source_map).inspect_err(|err| {
show_script_resolve_error(err, source_map, ¤t_path());
})?;
drop(bootstrap_timer);
let mut program = bootstrap_result.program;
let core_symbol_table = program
.modules
.get(&bootstrap_result.core_module_path)
.unwrap()
.symbol_table
.clone();
let compile_all_modules_timer = ScopedTimer::new("compile all modules");
compile_and_analyze_all_modules(
root_path,
&mut program,
source_map,
core_symbol_table.into(),
)
.inspect_err(|err| {
show_script_resolve_error(err, source_map, ¤t_path());
})?;
drop(compile_all_modules_timer);
Ok(program)
}
pub fn debug_all_modules(modules: &Modules, source_map: &SourceMap) {
for (_name, module) in modules.modules() {
debug_module(&module.symbol_table, source_map);
}
}
pub fn debug_module(symbol_table: &SymbolTable, source_map: &SourceMap) {
let source_map_lookup = SourceMapWrapper {
source_map,
current_dir: current_dir().unwrap(),
};
let pretty_printer = SourceMapDisplay {
source_map: &source_map_lookup,
};
let symbol_table_display = SymbolTableDisplay {
symbol_table: &symbol_table,
source_map_display: &pretty_printer,
};
info!(%symbol_table_display, "{:?}", symbol_table.module_path());
}
pub fn compile_string(script: &str) -> Result<(Program, ModuleRef, SourceMap), ScriptResolveError> {
let mut source_map = SourceMap::new(&SeqMap::default()).unwrap();
let file_id = 0xffff;
source_map
.add_mount("registry", &swamp_registry_path().unwrap())
.unwrap();
source_map.add_mount("crate", Path::new("/tmp/")).unwrap();
source_map.add_to_cache("crate", Path::new("test.swamp"), script, file_id);
let resolved_path_str = vec!["crate".to_string(), "test".to_string()];
let program = bootstrap_and_compile(&mut source_map, &resolved_path_str)?;
let main_module = program.modules.get(&resolved_path_str).unwrap().clone();
Ok((program, main_module, source_map))
}