summaryrefslogtreecommitdiff
path: root/src/types
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2025-03-18 11:50:19 +1300
committerBen Bridle <ben@derelict.engineering>2025-03-18 11:50:29 +1300
commitf8a694267d3981b0437c05fc248406116ea9ec06 (patch)
treee7c0426f4278481490e269be7ccb0a710c22ebae /src/types
parent87cdf5e88fd1d0aaddb91e216c47344effd63ed3 (diff)
downloadassembler-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.rs8
-rw-r--r--src/types/mod.rs5
-rw-r--r--src/types/source_unit.rs119
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,
+}