diff options
Diffstat (limited to 'src/source_unit.rs')
-rw-r--r-- | src/source_unit.rs | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/source_unit.rs b/src/source_unit.rs new file mode 100644 index 0000000..3e674be --- /dev/null +++ b/src/source_unit.rs @@ -0,0 +1,121 @@ +use crate::*; + +use vagabond::*; + + +type ParseFn = fn(&str, Option<&Path>) -> Vec<Symbol>; + + +/// Gather all source units from a PATH-style environment variable. +pub fn gather_from_path_variable(variable: &str, extension: &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) { + source_units.extend(gather_from_path(&path, extension, parse)); + } + }; + return source_units; +} + +/// Gather source units at or descending from a path. +pub fn gather_from_path(path: &Path, extension: &str, parse: ParseFn) -> Vec<SourceUnit> { + let mut source_units = Vec::new(); + if let Ok(entry) = Entry::from_path(path) { + if EntryType::File == entry.entry_type { + if let Ok(unit) = SourceUnit::from_path(&entry.path, extension, parse) { + source_units.push(unit); + } + } else if EntryType::Directory == entry.entry_type { + if let Ok(entries) = traverse_directory(entry.path) { + for entry in entries { + if let Ok(unit) = SourceUnit::from_path(&entry.path, extension, parse) { + source_units.push(unit); + } + } + } + } + }; + return source_units; +} + + +pub struct SourceUnit { + pub main: SourceFile, + pub head: Option<SourceFile>, + pub tail: Option<SourceFile>, +} + +impl SourceUnit { + /// Load source from a main file and an associated head and tail file. + pub fn from_path<P: AsRef<Path>>(path: P, extension: &str, parse: ParseFn) -> Result<Self, FileError> { + let main_path = { path.as_ref().canonicalize().unwrap_or_else(|_| path.as_ref().to_path_buf()) }; + let main_path_str = main_path.as_os_str().to_string_lossy().to_string(); + let head_extension = format!("head.{extension}"); + let tail_extension = format!("tail.{extension}"); + let is_head = main_path_str.ends_with(&head_extension); + let is_tail = main_path_str.ends_with(&tail_extension); + let is_not_main = !main_path_str.ends_with(extension); + // Head and tail files will be picked up later along with the main file. + if is_not_main || is_head || is_tail { return Err(FileError::InvalidExtension); } + + let source_code = read_file(path.as_ref())?; + let symbols = parse(&source_code, Some(path.as_ref())); + let head_path = main_path.with_extension(head_extension); + let tail_path = main_path.with_extension(tail_extension); + + macro_rules! parse_file { + ($path:expr) => { + read_file(&$path).ok().map(|source_code| { + let symbols = parse(&source_code, Some(&$path)); + let path = $path; + SourceFile { symbols, source_code, path } + }) + }; + } + let main = SourceFile { path: main_path, source_code, symbols }; + let head = parse_file!(head_path); + let tail = parse_file!(tail_path); + Ok( SourceUnit { main, head, tail } ) + } + + /// Load from a string of source code. + pub fn from_string<P: AsRef<Path>>(source_code: String, path: P, parse: ParseFn) -> Self { + let path = { path.as_ref().canonicalize().unwrap_or_else(|_| path.as_ref().to_path_buf()) }; + let symbols = parse(&source_code, Some(&path)); + Self { main: SourceFile { path, source_code, symbols }, head: None, tail: None } + } + + pub fn name(&self) -> Option<String> { + self.main.path.file_name().map(|s| s.to_string_lossy().to_string()) + } + + pub fn path(&self) -> String { + self.main.path.as_os_str().to_string_lossy().to_string() + + } +} + + +pub struct SourceFile { + pub path: PathBuf, + pub source_code: String, + pub symbols: Vec<Symbol>, +} + +pub struct Symbol { + pub name: String, + pub source: SourceSpan, + pub role: SymbolRole, +} + +#[derive(PartialEq)] +pub enum SymbolRole { + Definition(DefinitionType), + Reference, +} + +#[derive(PartialEq)] +pub enum DefinitionType { + MustPrecedeReference, + CanFollowReference, +} |