pub mod prelude;
use seq_map::SeqMap;
use std::collections::HashSet;
use std::path::PathBuf;
use std::{env, io};
use swamp_script_ast::prelude::*;
use swamp_script_ast::Function;
use swamp_script_parser::{AstParser, ParseError};
use swamp_script_source_map::{FileId, SourceMap};
use tracing::{debug, info, trace};
pub struct ParseRoot {
pub base_path: PathBuf,
}
#[derive(Debug)]
pub enum ParseRootError {
IoError(std::io::Error),
ParseError(ParseError),
}
impl From<std::io::Error> for ParseRootError {
fn from(err: std::io::Error) -> Self {
Self::IoError(err)
}
}
impl From<ParseError> for ParseRootError {
fn from(value: ParseError) -> Self {
Self::ParseError(value)
}
}
#[derive(Debug)]
pub struct ParseModule {
pub ast_module: swamp_script_ast::Module,
pub file_id: FileId,
}
impl ParseModule {
pub fn declare_external_function(
&mut self,
parameters: Vec<Parameter>,
return_type: Option<Type>,
) {
let fake_identifier = Node::default();
let signature = FunctionDeclaration {
name: fake_identifier.clone(),
params: parameters,
self_parameter: None,
return_type,
};
let external_signature = Function::External(signature);
self.ast_module.definitions.insert(
0, Definition::FunctionDef(external_signature),
);
}
}
#[derive(Debug)]
pub struct RelativePath(pub String);
impl ParseRoot {
pub fn new(base_path: PathBuf) -> Self {
Self { base_path }
}
pub fn parse(&self, contents: String, file_id: FileId) -> Result<ParseModule, ParseRootError> {
let parser = AstParser {};
let ast_program = parser.parse_module(&*contents)?;
Ok(ParseModule {
ast_module: ast_program,
file_id,
})
}
}
#[derive(Clone)]
#[allow(unused)]
pub struct ModuleInfo {
path: Vec<String>,
imports: Vec<Vec<String>>,
parsed: bool,
analyzed: bool,
}
pub struct DependencyParser {
pub import_scanned_modules: SeqMap<Vec<String>, ModuleInfo>,
already_parsed_modules: SeqMap<Vec<String>, ParseModule>,
pub already_resolved_modules: HashSet<Vec<String>>,
}
impl Default for DependencyParser {
fn default() -> Self {
Self::new()
}
}
impl DependencyParser {
pub fn new() -> Self {
Self {
import_scanned_modules: SeqMap::new(),
already_parsed_modules: SeqMap::new(),
already_resolved_modules: HashSet::new(),
}
}
pub fn add_resolved_module(&mut self, module_path: Vec<String>) {
self.already_resolved_modules.insert(module_path);
}
pub fn add_ast_module(&mut self, module_path: Vec<String>, parsed_module: ParseModule) {
debug!(
"Adding ast module parsed outside of graph resolver {:?}",
module_path
);
self.already_parsed_modules
.insert(module_path, parsed_module)
.expect("can not add parsed module")
}
}
#[derive(Debug)]
pub enum DependencyError {
CircularDependency(Vec<String>),
ParseRootError(ParseRootError),
IoError(io::Error),
}
impl From<ParseRootError> for DependencyError {
fn from(err: ParseRootError) -> Self {
Self::ParseRootError(err)
}
}
impl From<io::Error> for DependencyError {
fn from(value: io::Error) -> Self {
Self::IoError(value)
}
}
fn get_all_import_paths(parsed_module: &ParseModule) -> Vec<Vec<String>> {
let mut imports = vec![];
for def in parsed_module.ast_module.definitions() {
match def {
Definition::Use(import) => imports.push(import.assigned_path.clone()),
_ => continue,
}
}
imports
}
impl DependencyParser {
pub fn parse_all_dependant_modules(
&mut self,
parse_root: ParseRoot,
module_path: &[String],
source_map: &mut SourceMap,
) -> Result<(), DependencyError> {
let mut to_parse = vec![module_path.to_vec()];
while let Some(path) = to_parse.pop() {
let module_path_vec = &path.clone();
if self.import_scanned_modules.contains_key(module_path_vec) {
continue;
}
let parsed_module_to_scan =
if let Some(parsed_module) = self.already_parsed_modules.get(module_path_vec) {
parsed_module
} else {
if self.already_resolved_modules.contains(module_path_vec) {
info!("a module that already has been resolved {path:?}");
continue;
} else {
info!("a module we haven't seen before: {path:?}");
let (file_id, script) =
source_map.read_file_relative(module_path_vec.join("/").as_ref())?;
let parse_module = parse_root.parse(script, file_id)?;
self.already_parsed_modules
.insert(path.clone(), parse_module)
.expect("TODO: panic message");
self.already_parsed_modules
.get(&path.clone())
.expect("we just inserted it")
}
};
let imports = get_all_import_paths(parsed_module_to_scan);
let filtered_imports: Vec<Vec<String>> = imports
.into_iter()
.filter(|import| !self.already_resolved_modules.contains(import))
.collect();
for import in &filtered_imports {
info!("..found use: {import:?}");
}
self.import_scanned_modules
.insert(
path.clone(),
ModuleInfo {
path: path.clone(),
imports: filtered_imports.clone(),
parsed: false,
analyzed: false,
},
)
.expect("TODO: panic message");
to_parse.extend(filtered_imports.clone());
}
Ok(())
}
pub fn get_parsed_module(&self, path: &[String]) -> Option<&ParseModule> {
self.already_parsed_modules.get(&path.to_vec())
}
pub fn get_parsed_module_mut(&mut self, path: &[String]) -> Option<&mut ParseModule> {
self.already_parsed_modules.get_mut(&path.to_vec())
}
pub(crate) fn get_analysis_order(&self) -> Result<Vec<Vec<String>>, DependencyError> {
let mut order = Vec::new();
let mut visited = HashSet::new();
let mut temp_visited = HashSet::new();
fn visit(
graph: &DependencyParser,
path: &[String],
visited: &mut HashSet<Vec<String>>,
temp_visited: &mut HashSet<Vec<String>>,
order: &mut Vec<Vec<String>>,
) -> Result<(), DependencyError> {
if temp_visited.contains(path) {
return Err(DependencyError::CircularDependency(Vec::from(path)));
}
if visited.contains(path) {
return Ok(());
}
temp_visited.insert(Vec::from(path));
if let Some(module) = graph.import_scanned_modules.get(&path.to_vec()) {
for import in &module.imports {
visit(graph, import, visited, temp_visited, order)?;
}
}
order.push(Vec::from(path));
visited.insert(Vec::from(path));
temp_visited.remove(path);
Ok(())
}
for path in self.import_scanned_modules.keys() {
if !visited.contains(path) {
visit(self, path, &mut visited, &mut temp_visited, &mut order)?;
}
}
Ok(order)
}
}
fn get_current_dir() -> Result<PathBuf, std::io::Error> {
let path = env::current_dir()?;
Ok(path)
}
#[derive(Debug)]
pub enum DepLoaderError {
DependencyError(DependencyError),
}
impl From<DependencyError> for DepLoaderError {
fn from(e: DependencyError) -> Self {
Self::DependencyError(e)
}
}
pub fn parse_dependant_modules_and_resolve(
base_path: PathBuf,
module_path: Vec<String>,
dependency_parser: &mut DependencyParser,
source_map: &mut SourceMap,
) -> Result<Vec<Vec<String>>, DepLoaderError> {
debug!(current_directory=?get_current_dir().expect("failed to get current directory"), "current directory");
let parse_root = ParseRoot::new(base_path);
dependency_parser.parse_all_dependant_modules(parse_root, &module_path, source_map)?;
let module_paths_in_order = dependency_parser.get_analysis_order()?;
Ok(module_paths_in_order)
}
pub fn create_parsed_modules(
script: &str,
source_map: &mut SourceMap,
root_path: PathBuf,
) -> Result<DependencyParser, ParseError> {
let parser = AstParser {};
let file_id = source_map.add_manual_no_id(&*root_path, script);
let ast_module_result = parser.parse_module(script);
if let Err(some) = ast_module_result {
return Err(some);
}
let ast_module = ast_module_result.unwrap();
trace!("ast_module:\n{:?}", ast_module);
let parse_module = ParseModule {
ast_module,
file_id,
};
let mut graph = DependencyParser::new();
let root = vec!["test".to_string()];
graph.add_ast_module(root, parse_module);
debug!("root path is {root_path:?}");
Ok(graph)
}