use std::cell::RefCell; use std::collections::{HashMap, HashSet, VecDeque}; use std::fmt::Debug; use std::rc::Rc; use mappable_rc::Mrc; use crate::ast::Rule; use crate::parse::{self, FileEntry}; use crate::utils::{Cache, mrc_derive, to_mrc_slice}; use super::name_resolver::NameResolver; use super::module_error::ModuleError; use super::prefix::prefix_expr; use super::loaded::Loaded; type ParseResult = Result>; #[derive(Debug, Clone)] pub struct Module { pub rules: Vec, pub exports: Vec, pub references: Vec> } pub type RuleCollectionResult = Result, ModuleError>; pub fn rule_collector( load_mod: F, prelude: Vec ) -> Cache<'static, Mrc<[String]>, RuleCollectionResult> where F: FnMut(Mrc<[String]>) -> Result, ELoad: Clone + Debug { let load_mod_rc = RefCell::new(load_mod); // Map paths to a namespace with name list (folder) or module with source text (file) let loaded = Rc::new(Cache::new(move |path: Mrc<[String]>, _| -> ParseResult { (load_mod_rc.borrow_mut())(path).map_err(ModuleError::Load) })); // Map names to the longest prefix that points to a valid module // At least one segment must be in the prefix, and the prefix must not be the whole name let modname = Rc::new(Cache::new({ let loaded = Rc::clone(&loaded); move |symbol: Mrc<[String]>, _| -> Result, Vec>> { let mut errv: Vec> = Vec::new(); let reg_err = |e, errv: &mut Vec>| { errv.push(e); if symbol.len() == errv.len() { Err(errv.clone()) } else { Ok(()) } }; loop { let path = mrc_derive(&symbol, |s| &s[..s.len() - errv.len() - 1]); match loaded.try_find(&path) { Ok(imports) => match imports.as_ref() { Loaded::Module(_) => break Ok(path), _ => reg_err(ModuleError::None, &mut errv)? }, Err(err) => reg_err(err, &mut errv)? } } } })); // Preliminarily parse a file, substitution rules and imports are valid let preparsed = Rc::new(Cache::new({ let loaded = Rc::clone(&loaded); let prelude2 = prelude.clone(); move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { let loaded = loaded.try_find(&path)?; if let Loaded::Module(source) = loaded.as_ref() { Ok(parse::parse(&prelude2, source.as_str())?) } else {Err(ModuleError::None)} } })); // Collect all toplevel names exported from a given file let exports = Rc::new(Cache::new({ let loaded = Rc::clone(&loaded); let preparsed = Rc::clone(&preparsed); move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { let loaded = loaded.try_find(&path)?; if let Loaded::Namespace(names) = loaded.as_ref() { return Ok(names.clone()); } let preparsed = preparsed.try_find(&path)?; Ok(parse::exported_names(&preparsed) .into_iter() .map(|n| n[0].clone()) .collect()) } })); // Collect all toplevel names imported by a given file let imports = Rc::new(Cache::new({ let preparsed = Rc::clone(&preparsed); let exports = Rc::clone(&exports); move |path: Mrc<[String]>, _| -> ParseResult>, ELoad> { let entv = preparsed.try_find(&path)?; let import_entries = parse::imports(entv.iter()); let mut imported_symbols: HashMap> = HashMap::new(); for imp in import_entries { let export = exports.try_find(&imp.path)?; if let Some(ref name) = imp.name { if export.contains(name) { imported_symbols.insert(name.clone(), Mrc::clone(&imp.path)); } } else { for exp in export.as_ref() { imported_symbols.insert(exp.clone(), Mrc::clone(&imp.path)); } } } Ok(imported_symbols) } })); // Final parse, operators are correctly separated let parsed = Rc::new(Cache::new({ let preparsed = Rc::clone(&preparsed); let imports = Rc::clone(&imports); let loaded = Rc::clone(&loaded); move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { let imported_ops: Vec = imports.try_find(&path)? .keys() .chain(prelude.iter()) .filter(|s| parse::is_op(s)) .cloned() .collect(); // let parser = file_parser(&prelude, &imported_ops); let pre = preparsed.try_find(&path)?; if let Loaded::Module(source) = loaded.try_find(&path)?.as_ref() { Ok(parse::reparse(&imported_ops, source.as_str(), &pre)?) } else { Err(ModuleError::None) } } })); let name_resolver_rc = RefCell::new(NameResolver::new({ let modname = Rc::clone(&modname); move |path| { Some(modname.try_find(&path).ok()?.as_ref().clone()) } }, { let imports = Rc::clone(&imports); move |path| { imports.try_find(&path).map(|f| f.as_ref().clone()) } })); // Turn parsed files into a bag of rules and a list of toplevel export names let resolved = Rc::new(Cache::new({ let parsed = Rc::clone(&parsed); let exports = Rc::clone(&exports); let imports = Rc::clone(&imports); let modname = Rc::clone(&modname); move |path: Mrc<[String]>, _| -> ParseResult { let mut name_resolver = name_resolver_rc.borrow_mut(); let module = Module { rules: parsed.try_find(&path)? .iter() .filter_map(|ent| { if let FileEntry::Rule(Rule{source, prio, target}, _) = ent { Some(Rule { source: source.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(), target: target.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(), prio: *prio, }) } else { None } }) .map(|rule| Ok(super::Rule { source: to_mrc_slice(rule.source.iter() .map(|ex| name_resolver.process_expression(ex)) .collect::, _>>()?), target: to_mrc_slice(rule.target.iter() .map(|ex| name_resolver.process_expression(ex)) .collect::, _>>()?), ..rule })) .collect::, ELoad>>()?, exports: exports.try_find(&path)?.as_ref().clone(), references: imports.try_find(&path)? .values() .filter_map(|imps| { modname.try_find(imps).ok().map(|r| r.as_ref().clone()) }) .collect() }; Ok(module) } })); Cache::new({ let resolved = Rc::clone(&resolved); move |path: Mrc<[String]>, _| -> ParseResult, ELoad> { // Breadth-first search let mut processed: HashSet> = HashSet::new(); let mut rules: Vec = Vec::new(); let mut pending: VecDeque> = VecDeque::new(); pending.push_back(path); while let Some(el) = pending.pop_front() { let resolved = resolved.try_find(&el)?; processed.insert(el.clone()); pending.extend( resolved.references.iter() .filter(|&v| !processed.contains(v)) .cloned() ); rules.extend( resolved.rules.iter().cloned() ) }; Ok(rules) } }) }