diff options
author | Ben Bridle <ben@derelict.engineering> | 2025-03-18 11:50:19 +1300 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2025-03-18 11:50:29 +1300 |
commit | f8a694267d3981b0437c05fc248406116ea9ec06 (patch) | |
tree | e7c0426f4278481490e269be7ccb0a710c22ebae /src/types | |
parent | 87cdf5e88fd1d0aaddb91e216c47344effd63ed3 (diff) | |
download | assembler-f8a694267d3981b0437c05fc248406116ea9ec06.zip |
Large restructure
Files were moved to be better organised, error messages were changed to
be more general, and a Compiler type was added to the library.
Diffstat (limited to 'src/types')
-rw-r--r-- | src/types/context.rs | 8 | ||||
-rw-r--r-- | src/types/mod.rs | 5 | ||||
-rw-r--r-- | src/types/source_unit.rs | 119 |
3 files changed, 132 insertions, 0 deletions
diff --git a/src/types/context.rs b/src/types/context.rs new file mode 100644 index 0000000..c015c7e --- /dev/null +++ b/src/types/context.rs @@ -0,0 +1,8 @@ +use crate::*; + + +/// Source context for a token. +pub struct Context<'a> { + pub source_code: &'a str, + pub source: &'a SourceSpan, +} diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 0000000..df23058 --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1,5 @@ +mod context; +mod source_unit; + +pub use context::*; +pub use source_unit::*; 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, +} |