use crate::*;
use log::{info, warn};
use vagabond::*;
/// Compiles multiple source code files into one.
pub struct Compiler {
pub source_path: Option<PathBuf>,
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<P: AsRef<Path>>(&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<P: AsRef<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<ResolverError> {
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<String, MergeError> {
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<SourceUnit> {
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<SourceUnit> {
let mut source_units = Vec::new();
let check_optional_file = |file: &Option<SourceFile>| -> 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;
}