summaryrefslogtreecommitdiff
path: root/src/gather_libraries.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/gather_libraries.rs')
-rw-r--r--src/gather_libraries.rs185
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,
+}