diff options
Diffstat (limited to 'src/types/source_unit.rs')
-rw-r--r-- | src/types/source_unit.rs | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/src/types/source_unit.rs b/src/types/source_unit.rs new file mode 100644 index 0000000..2fbbc61 --- /dev/null +++ b/src/types/source_unit.rs @@ -0,0 +1,119 @@ +use crate::*; + + +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: Option<&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(); + // Attempt to extract an extension from main path if no extension was provided. + let extension = extension.or_else(|| main_path.extension().and_then(|ext| ext.to_str())); + + let head_extension = extension.map(|ext| format!("head.{ext}")); + let tail_extension = extension.map(|ext| format!("tail.{ext}")); + let is_head = head_extension.as_ref().map_or(false, |ext| main_path_str.ends_with(ext.as_str())); + let is_tail = tail_extension.as_ref().map_or(false, |ext| main_path_str.ends_with(ext.as_str())); + let is_main = extension.map_or(true, |ext| main_path_str.ends_with(ext)); + // Head and tail files will be picked up later along with the main file. + if !is_main || is_head || is_tail { return Err(FileError::InvalidExtension); } + + let parse_file = |path: PathBuf| { + if let Ok(source_code) = read_file(&path) { + let symbols = parse(&source_code, Some(&path)); + return Some(SourceFile { symbols, source_code, path: path }); + } + return None; + }; + let head = head_extension.map_or(None, |ext| parse_file(main_path.with_extension(&ext))); + let tail = tail_extension.map_or(None, |ext| parse_file(main_path.with_extension(&ext))); + let source_code = read_file(path.as_ref())?; + let symbols = parse(&source_code, Some(path.as_ref())); + let main = SourceFile { path: main_path, source_code, symbols }; + 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)); + let main = SourceFile { path, source_code, symbols }; + Self { main, 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() + + } +} + +impl PartialEq for SourceUnit { + fn eq(&self, other: &SourceUnit) -> bool { + if let Ok(this_path) = self.main.path.canonicalize() { + if let Ok(other_path) = other.main.path.canonicalize() { + return this_path == other_path; + } + } + return false; + } +} + + +pub struct SourceFile { + pub path: PathBuf, + pub source_code: String, + pub symbols: Option<Vec<Symbol>>, +} + + +pub struct Symbol { + pub name: String, + pub namespace: Vec<String>, + pub source: SourceSpan, + pub role: SymbolRole, +} + +impl Symbol { + /// True if this symbol is a valid definition for a reference symbol + pub fn defines(&self, reference: &Symbol) -> bool { + self.name == reference.name && + self.namespace.len() <= reference.namespace.len() && + std::iter::zip(&self.namespace, &reference.namespace).all(|(a, b)| a == b) + } +} + +impl PartialEq for Symbol { + fn eq(&self, other: &Symbol) -> bool { + self.name == other.name && self.namespace == other.namespace + } +} + +impl std::fmt::Debug for Symbol { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + for name in &self.namespace { + write!(f, "{name}::")? + } + write!(f, "{}", self.name) + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum SymbolRole { + Definition(DefinitionType), + Reference, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum DefinitionType { + MustPrecedeReference, + CanFollowReference, +} |