From f8a694267d3981b0437c05fc248406116ea9ec06 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Tue, 18 Mar 2025 11:50:19 +1300 Subject: 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. --- src/types/context.rs | 8 ++++ src/types/mod.rs | 5 ++ src/types/source_unit.rs | 119 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/types/context.rs create mode 100644 src/types/mod.rs create mode 100644 src/types/source_unit.rs (limited to 'src/types') 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, + pub tail: Option, +} + +impl SourceUnit { + /// Load source from a main file and an associated head and tail file. + pub fn from_path>(path: P, extension: Option<&str>, parse: ParseFn) -> Result { + 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>(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 { + 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>, +} + + +pub struct Symbol { + pub name: String, + pub namespace: Vec, + 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, +} -- cgit v1.2.3-70-g09d2