use crate::*; use log::{info, warn}; use vagabond::*; /// Compiles multiple source code files into one. pub struct Compiler { pub source_path: Option, pub resolver: Resolver, pub parse_symbols: ParseFn, pub push_code: PushFn, } impl Compiler { pub fn new(parse_symbols: ParseFn, push_code: PushFn) -> Self { let resolver = Resolver::new(); Self { source_path: None, resolver, parse_symbols, push_code } } pub fn root_from_string>(&mut self, source_code: String, path: P) { let source_unit = SourceUnit::from_string(source_code, &path, self.parse_symbols); self.source_path = Some(path.as_ref().to_path_buf()); self.resolver.include_source_unit(source_unit, None); } pub fn root_from_path>(&mut self, path: P) -> Result<(), FileError> { let source_unit = SourceUnit::from_path(&path, None, self.parse_symbols)?; self.source_path = Some(path.as_ref().to_path_buf()); self.resolver.include_source_unit(source_unit, None); return Ok(()); } /// Find library files descending from the parent directory. pub fn include_libs_from_parent(&mut self, ext: Option<&str>) { if let Some(path) = &self.source_path { if let Some(parent_path) = path.parent() { let parent_path = parent_path.to_owned(); self.include_libs_from_path(&parent_path, ext); } } } /// Find library files at or descending from a path. pub fn include_libs_from_path(&mut self, path: &Path, ext: Option<&str>) { let libraries = gather_from_path(path, ext, self.parse_symbols); self.resolver.add_library_source_units(libraries); self.resolver.resolve(); } /// Find library files from a PATH-style environment variable. pub fn include_libs_from_path_variable(&mut self, name: &str, ext: Option<&str>) { let libraries = gather_from_path_variable(name, ext, self.parse_symbols); self.resolver.add_library_source_units(libraries); self.resolver.resolve(); } pub fn error(&self) -> Option { self.resolver.error() } pub fn hierarchy(&self) -> SourceHierarchy { self.resolver.hierarchy() } pub fn symbols(&self) -> SourceSymbols { self.resolver.symbols() } pub fn unused(&self) -> UnusedSymbols { self.resolver.unused() } pub fn get_compiled_source(&mut self) -> Result { self.resolver.calculate_hierarchy(); self.resolver.get_merged_source_code(self.push_code) } } /// Gather all source units with a given extension using a PATH-style environment variable. pub fn gather_from_path_variable(variable: &str, extension: Option<&str>, parse: ParseFn) -> Vec { let mut source_units = Vec::new(); if let Ok(string) = std::env::var(variable) { for path in string.split(":").map(PathBuf::from) { info!("Found path {path:?} in environment variable {variable:?}"); source_units.extend(gather_from_path(&path, extension, parse)); } }; return source_units; } /// Gather source units with a given extension at or descending from a path. pub fn gather_from_path(path: &Path, extension: Option<&str>, parse: ParseFn) -> Vec { let mut source_units = Vec::new(); let check_optional_file = |file: &Option| -> bool { match file { Some(file) => match file.symbols { Some(_) => { info!("Found source file at {:?}", file.path); true } None => { warn!("Could not parse source file at {:?}", file.path); false } } None => true, } }; let mut gather_source_unit = |path: &Path| { if let Ok(unit) = SourceUnit::from_path(&path, extension, parse) { if unit.main.symbols.is_some() { info!("Found source file at {:?}", unit.main.path); let head_good = check_optional_file(&unit.head); let tail_good = check_optional_file(&unit.tail); if head_good && tail_good { source_units.push(unit); } } else { warn!("Could not parse source file at {path:?}"); check_optional_file(&unit.head); check_optional_file(&unit.tail); } } }; if let Ok(entry) = Entry::from_path(path) { if EntryType::File == entry.entry_type { gather_source_unit(&entry.path) } else if EntryType::Directory == entry.entry_type { info!("Traversing directory {path:?} for source files"); if let Ok(entries) = traverse_directory(entry.path) { for entry in entries { gather_source_unit(&entry.path) } } } }; return source_units; }