diff options
author | Ben Bridle <ben@derelict.engineering> | 2025-02-05 12:58:02 +1300 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2025-02-05 13:03:36 +1300 |
commit | 80da2af821385b2fc89091e9ac37a047349da4bd (patch) | |
tree | 2ba50368301e041f8d1b99145ab0a1fe28f91571 /src/source_unit.rs | |
parent | 8d11be64f6c1747e7c4049105a6dd4ea9ab0d27f (diff) | |
download | assembler-80da2af821385b2fc89091e9ac37a047349da4bd.zip |
Implement source unit compilation, symbol resolution, error reporting
This library can now carry out all stages of assembly from collecting
source fragments to resolving symbols to pruning unused libraries to
generating a single compiled source file.
Pretty-printing of state has also been implemented in this library.
The source tree hierarchy, symbol resolution errors, and file read
errors can all be printed in a tidy format.
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, +} |