use crate::ast::{declaration_surface, Declaration, Loc, Program};
use crate::module_interface::CognitiveInterface;
use crate::module_resolver::ModulePath;
pub struct LinkUnit {
pub path: ModulePath,
pub origin: String,
pub program: Program,
pub line_base: u32,
pub line_count: u32,
pub interface: CognitiveInterface,
}
#[derive(Debug, Clone)]
pub struct LinkedModule {
pub path: ModulePath,
pub origin: String,
pub line_base: u32,
pub line_count: u32,
pub content_hash: String,
pub interface_hash: String,
pub export_names: Vec<String>,
}
pub struct LinkedProgram {
pub program: Program,
pub modules: Vec<LinkedModule>,
}
impl LinkedProgram {
pub fn locate(&self, virtual_line: u32) -> (String, u32) {
for m in &self.modules {
let end = m.line_base + m.line_count;
if virtual_line >= m.line_base && virtual_line < end {
return (m.origin.clone(), virtual_line - m.line_base);
}
}
let fallback = self
.modules
.last()
.map(|m| m.origin.clone())
.unwrap_or_default();
(fallback, virtual_line)
}
}
pub fn link(units: Vec<LinkUnit>) -> LinkedProgram {
let total: usize = units.iter().map(|u| u.program.declarations.len()).sum();
let mut declarations: Vec<Declaration> = Vec::with_capacity(total);
let mut modules: Vec<LinkedModule> = Vec::with_capacity(units.len());
let last_index = units.len().saturating_sub(1);
for (i, unit) in units.into_iter().enumerate() {
let is_entry = i == last_index;
let mut export_names: Vec<String> = unit
.interface
.exports
.keys()
.cloned()
.collect();
export_names.sort_unstable();
for decl in unit.program.declarations {
if !is_entry && matches!(decl, Declaration::Run(_)) {
continue;
}
declarations.push(decl);
}
modules.push(LinkedModule {
path: unit.path,
origin: unit.origin,
line_base: unit.line_base,
line_count: unit.line_count,
content_hash: unit.interface.content_hash.clone(),
interface_hash: unit.interface.interface_hash.clone(),
export_names,
});
}
LinkedProgram {
program: Program {
declarations,
declaration_trivia: Vec::new(),
loc: Loc { line: 1, column: 1 },
},
modules,
}
}
pub fn linked_name_index(linked: &LinkedProgram) -> std::collections::BTreeMap<String, String> {
let mut index = std::collections::BTreeMap::new();
for m in &linked.modules {
for name in &m.export_names {
index
.entry(name.clone())
.or_insert_with(|| m.path.dotted());
}
}
index
}
pub fn surface_names(program: &Program) -> Vec<(String, String)> {
program
.declarations
.iter()
.filter_map(|d| declaration_surface(d).map(|(n, k, _)| (n, k)))
.collect()
}