use crate::*; use std::mem::take; /// Resolve symbol references across source units. pub struct SymbolResolver { pub definitions: Vec, pub unresolved: Vec, /// Contains the ID of the owner of the original definition. pub redefinitions: Vec<(TrackedSymbol, usize)>, pub source_units: Vec, pub root_unit_ids: Vec, pub unused_library_units: Vec, } impl SymbolResolver { /// Construct a resolver from a root source unit. pub fn from_source_unit(source_unit: SourceUnit) -> Self { let mut new = Self { definitions: Vec::new(), unresolved: Vec::new(), redefinitions: Vec::new(), source_units: Vec::new(), root_unit_ids: Vec::new(), unused_library_units: Vec::new(), }; new.add_source_unit(source_unit, None); return new; } pub fn add_library_units(&mut self, mut source_units: Vec) { self.unused_library_units.append(&mut source_units); } pub fn resolve(&mut self) { // Repeatedly test if any unused source unit resolves an unresolved symbol, // breaking the loop when no new resolutions are found. 'outer: loop { for (i, source_unit) in self.unused_library_units.iter().enumerate() { if let Some(id) = self.resolves_reference(&source_unit) { let source_unit = self.unused_library_units.remove(i); self.add_source_unit(source_unit, Some(id)); continue 'outer; } } break; } } /// Add a source unit to the resolver and link it to a parent unit. pub fn add_source_unit(&mut self, mut source_unit: SourceUnit, parent_id: Option) { let source_id = self.source_units.len(); // Add all main symbols. if let Some(definitions) = take(&mut source_unit.main.symbols.definitions) { self.add_definitions(definitions, source_id, SourceRole::Main); } if let Some(references) = take(&mut source_unit.main.symbols.references) { self.add_references(references, source_id, SourceRole::Main); } // Add all head symbols. if let Some(head) = &mut source_unit.head { if let Some(references) = take(&mut head.symbols.references) { self.add_references(references, source_id, SourceRole::Head); } if let Some(definitions) = take(&mut head.symbols.definitions) { self.add_definitions(definitions, source_id, SourceRole::Head); } } // Add all tail symbols. if let Some(tail) = &mut source_unit.tail { if let Some(references) = take(&mut tail.symbols.references) { self.add_references(references, source_id, SourceRole::Tail); } if let Some(definitions) = take(&mut tail.symbols.definitions) { self.add_definitions(definitions, source_id, SourceRole::Tail); } } if let Some(parent_id) = parent_id { if let Some(parent_unit) = self.source_units.get_mut(parent_id) { parent_unit.child_ids.push(source_id); } } else { self.root_unit_ids.push(source_id); } let source_unit = HeirarchicalSourceUnit { source_unit, child_ids: Vec::new() }; self.source_units.push(source_unit); } fn add_references(&mut self, references: Vec, source_id: usize, source_role: SourceRole) { for symbol in references { let reference = TrackedSymbol { symbol, source_id, source_role }; if !self.definitions.contains(&reference) { self.unresolved.push(reference); } } } fn add_definitions(&mut self, definitions: Vec, source_id: usize, source_role: SourceRole) { for symbol in definitions { let predicate = |d: &&TrackedSymbol| { &d.symbol.name == &symbol.name }; if let Some(def) = self.definitions.iter().find(predicate) { let definition = TrackedSymbol { symbol, source_id, source_role }; let redefinition = (definition, def.source_id); self.redefinitions.push(redefinition); } else { self.unresolved.retain(|s| s.symbol.name != symbol.name); let definition = TrackedSymbol { symbol, source_id, source_role }; self.definitions.push(definition); } } } /// Returns the ID of the owner of a symbol resolved by this unit. pub fn resolves_reference(&self, source_unit: &SourceUnit) -> Option { if let Some(definitions) = &source_unit.main.symbols.definitions { if let Some(id) = self.source_id_of_unresolved(&definitions) { return Some(id); } } if let Some(head) = &source_unit.head { if let Some(definitions) = &head.symbols.definitions { if let Some(id) = self.source_id_of_unresolved(&definitions) { return Some(id); } } } if let Some(tail) = &source_unit.tail { if let Some(definitions) = &tail.symbols.definitions { if let Some(id) = self.source_id_of_unresolved(&definitions) { return Some(id); } } } return None; } /// Returns the ID of the owner of a reference to one of these symbols. fn source_id_of_unresolved(&self, symbols: &[Symbol]) -> Option { for symbol in symbols { let opt = self.unresolved.iter().find(|s| s.symbol.name == symbol.name); if let Some(unresolved) = opt { return Some(unresolved.source_id); } } return None; } pub fn get_source_code_for_tracked_symbol(&self, symbol: &TrackedSymbol) -> &str { let source_unit = &self.source_units[symbol.source_id].source_unit; match symbol.source_role { SourceRole::Main => source_unit.main.symbols.source_code.as_str(), SourceRole::Head => match &source_unit.head { Some(head) => head.symbols.source_code.as_str(), None => unreachable!("Failed to find source for token"), } SourceRole::Tail => match &source_unit.tail { Some(tail) => tail.symbols.source_code.as_str(), None => unreachable!("Failed to find source for token"), } } } /// Create a source file by concatenating all source units. pub fn get_merged_source_code(&self) -> String { // The first source unit is guaranteed to be the root unit, so we can // just push source files in their current order. let mut source_code = String::new(); // Push head source code. for source_unit in self.source_units.iter().rev() { if let Some(head) = &source_unit.source_unit.head { push_source_code_to_string(&mut source_code, head); } } // Push main source code. for source_unit in self.source_units.iter() { push_source_code_to_string(&mut source_code, &source_unit.source_unit.main); } // Push tail source code. for source_unit in self.source_units.iter().rev() { if let Some(tail) = &source_unit.source_unit.tail { push_source_code_to_string(&mut source_code, tail); } } return source_code; } } fn push_source_code_to_string(string: &mut String, source_file: &SourceFile) { // Ensure that sections are separated by two newlines. if !string.is_empty() { if !string.ends_with('\n') { string.push('\n'); } if !string.ends_with("\n\n") { string.push('\n'); } } // Write a path comment to the string. let path_str = source_file.path.as_os_str().to_string_lossy(); let path_comment = format!("(: {path_str} )\n"); string.push_str(&path_comment); string.push_str(&source_file.symbols.source_code); } pub struct HeirarchicalSourceUnit { pub source_unit: SourceUnit, pub child_ids: Vec, } pub struct TrackedSymbol { pub symbol: Symbol, pub source_id: usize, pub source_role: SourceRole, } #[derive(Clone, Copy)] pub enum SourceRole { Main, Head, Tail, } impl PartialEq for TrackedSymbol { fn eq(&self, other: &TrackedSymbol) -> bool { self.symbol.name.eq(&other.symbol.name) } }