diff options
Diffstat (limited to 'src/gather_libraries.rs')
-rw-r--r-- | src/gather_libraries.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/gather_libraries.rs b/src/gather_libraries.rs new file mode 100644 index 0000000..0b5d2a6 --- /dev/null +++ b/src/gather_libraries.rs @@ -0,0 +1,185 @@ +use crate::*; + +use vagabond::*; + + +/// Gather all library units from the given path. +pub fn gather_project_libraries(path: &Path, extension: &str) -> Vec<SourceUnit> { + match path.parent() { + Some(parent_path) => gather_source_units(parent_path, extension), + None => Vec::new(), + } +} + + +/// Gather all library units from the paths specified in an environment variable. +pub fn gather_environment_libraries(extension: &str) -> Vec<Vec<SourceUnit>> { + let mut environment_libraries = Vec::new(); + if let Ok(lib_var) = std::env::var("BEDROCK_LIBS") { + for path_str in lib_var.split(":") { + let lib_path = PathBuf::from(path_str); + let source_units = gather_source_units(&lib_path, extension); + if !source_units.is_empty() { + environment_libraries.push(source_units); + } + } + }; + return environment_libraries; +} + + +/// Gather all source units at or descended from the given entry. +fn gather_source_units(path: &Path, extension: &str) -> Vec<SourceUnit> { + let mut source_units = Vec::new(); + if let Ok(entry) = Entry::from_path(path) { + match entry.entry_type { + EntryType::File => { + if let Ok(source) = SourceUnit::from_path(entry.path, extension) { + source_units.push(source); + } + } + EntryType::Directory => { + if let Ok(entries) = traverse_directory(entry.path) { + for entry in entries { + if let Ok(source) = SourceUnit::from_path(entry.path, extension) { + source_units.push(source); + } + } + } + } + } + }; + return source_units; +} + + +pub struct SourceUnit { + pub main: SourceFile, + pub head: Option<SourceFile>, + pub tail: Option<SourceFile>, +} + + +impl SourceUnit { + /// Load from a source file and an associated head and tail file. + pub fn from_path<P: Into<PathBuf>>(path: P, extension: &str) -> Result<Self, ParseError> { + let main_path = canonicalize_path(path); + 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); + if is_not_main || is_head || is_tail { return Err(ParseError::InvalidExtension); } + + let symbols = parse_symbols_from_file(&main_path)?; + let head_path = main_path.with_extension(head_extension); + let tail_path = main_path.with_extension(tail_extension); + + let main = SourceFile { path: main_path, symbols }; + let head = match parse_symbols_from_file(&head_path) { + Ok(symbols) => Some(SourceFile { path: head_path, symbols }), + Err(_) => None, + }; + let tail = match parse_symbols_from_file(&tail_path) { + Ok(symbols) => Some(SourceFile { path: tail_path, symbols }), + Err(_) => None, + }; + Ok( SourceUnit { main, head, tail } ) + } + + /// Load from a string of source code. + pub fn from_source_code<P: Into<PathBuf>>(source_code: String, path: P) -> Self { + let path = canonicalize_path(path); + let symbols = parse_symbols_from_source(source_code, Some(&path)); + Self { + main: SourceFile { path, symbols }, + head: None, + tail: None, + } + } +} + + +/// Read and parse all symbols from a source file. +fn parse_symbols_from_file(path: &Path) -> Result<Symbols, ParseError> { + let source = read_source_from_file(path)?; + Ok(parse_symbols_from_source(source, Some(path))) +} + + +/// Parse all symbols from a source code string. +fn parse_symbols_from_source(source_code: String, path: Option<&Path>) -> Symbols { + use SyntacticTokenVariant as SynVar; + + let token_iter = SyntacticParser::from_source_code(&source_code, path); + let mut definitions = Vec::new(); + let mut references = Vec::new(); + + for token in token_iter { + match token.variant { + SynVar::LabelDefinition(name) => { + definitions.push(Symbol { name, source: token.source }); + }, + SynVar::MacroDefinition(name) => { + definitions.push(Symbol { name, source: token.source }); + } + SynVar::Symbol(name) => { + references.push(Symbol { name, source: token.source }); + }, + _ => (), + } + } + + Symbols { + definitions: Some(definitions), + references: Some(references), + source_code, + } +} + + +/// Attempt to read program source from a file. +pub fn read_source_from_file(path: &Path) -> Result<String, ParseError> { + match std::fs::read(&path) { + Ok(bytes) => match String::from_utf8(bytes) { + Ok(source) => Ok(source), + Err(_) => return Err(ParseError::InvalidUtf8), + } + Err(err) => return Err( match err.kind() { + std::io::ErrorKind::NotFound => ParseError::NotFound, + std::io::ErrorKind::PermissionDenied => ParseError::NotReadable, + std::io::ErrorKind::IsADirectory => ParseError::IsADirectory, + _ => ParseError::Unknown, + } ) + } +} + + +fn canonicalize_path<P: Into<PathBuf>>(path: P) -> PathBuf { + let pathbuf = path.into(); + match pathbuf.canonicalize() { + Ok(canonical) => canonical, + Err(_) => pathbuf, + } +} + + + +pub struct SourceFile { + pub path: PathBuf, + pub symbols: Symbols, +} + + +pub struct Symbols { + pub definitions: Option<Vec<Symbol>>, + pub references: Option<Vec<Symbol>>, + pub source_code: String, +} + + +pub struct Symbol { + pub name: String, + pub source: SourceSpan, +} |