use crate::semantic::symbol_table::{ResolvedImport, SymbolId, SymbolTable};
pub fn resolve_imports(symbol_table: &mut SymbolTable) {
let scope_count = symbol_table.scope_count();
for scope_id in 0..scope_count {
let imports = symbol_table.get_scope_imports(scope_id);
for import in imports {
if let Some(resolved) = resolve_import_path(symbol_table, scope_id, &import.path) {
let resolved_import = ResolvedImport {
raw_path: import.path.clone(),
resolved_path: resolved,
is_namespace: import.is_namespace,
is_recursive: import.is_recursive,
is_public: import.is_public,
};
symbol_table.add_resolved_import(scope_id, resolved_import);
} else {
let resolved_import = ResolvedImport {
raw_path: import.path.clone(),
resolved_path: import.path.clone(),
is_namespace: import.is_namespace,
is_recursive: import.is_recursive,
is_public: import.is_public,
};
symbol_table.add_resolved_import(scope_id, resolved_import);
}
}
}
}
fn resolve_import_path(
symbol_table: &SymbolTable,
scope_id: usize,
import_path: &str,
) -> Option<String> {
let path_without_wildcard = import_path.trim_end_matches("::**").trim_end_matches("::*");
let (first_segment, rest) = match path_without_wildcard.find("::") {
Some(pos) => (&path_without_wildcard[..pos], &path_without_wildcard[pos..]),
None => (path_without_wildcard, ""),
};
if let Some(resolved_first) = resolve_from_scope_chain(symbol_table, scope_id, first_segment) {
let suffix = if import_path.ends_with("::**") {
"::**"
} else if import_path.ends_with("::*") {
"::*"
} else {
""
};
return Some(format!("{}{}{}", resolved_first, rest, suffix));
}
None
}
fn resolve_from_scope_chain(
symbol_table: &SymbolTable,
scope_id: usize,
name: &str,
) -> Option<String> {
let mut current = scope_id;
loop {
if let Some(symbol) = symbol_table.get_symbol_in_scope(current, name) {
return Some(symbol.qualified_name().to_string());
}
if let Some(symbol_id) = symbol_table
.get_export_map(current)
.and_then(|m| m.get(name))
{
if let Some(symbol) = symbol_table.get_symbol(*symbol_id) {
return Some(symbol.qualified_name().to_string());
}
}
current = symbol_table.get_scope_parent(current)?;
}
}
pub fn build_export_maps(symbol_table: &mut SymbolTable) {
let scope_count = symbol_table.scope_count();
symbol_table.clear_export_maps();
for scope_id in 0..scope_count {
let children = symbol_table.get_scope_children_symbols(scope_id);
for (name, symbol_id) in children {
symbol_table.add_to_export_map(scope_id, name, symbol_id);
}
}
let mut changed = true;
let mut iterations = 0;
const MAX_ITERATIONS: usize = 100;
while changed && iterations < MAX_ITERATIONS {
changed = false;
iterations += 1;
for scope_id in 0..scope_count {
let resolved_imports: Vec<ResolvedImport> =
symbol_table.get_resolved_imports(scope_id).to_vec();
for import in &resolved_imports {
if !import.is_public || !import.is_namespace {
continue;
}
let namespace = import
.resolved_path
.trim_end_matches("::**")
.trim_end_matches("::*");
let ns_symbol = symbol_table.find_by_qualified_name(namespace).or_else(|| {
symbol_table
.get_export_map(scope_id)
.and_then(|m| m.get(namespace))
.and_then(|id| symbol_table.get_symbol(*id))
});
if let Some(ns_symbol) = ns_symbol {
let ns_def_scope = ns_symbol.scope_id();
let ns_qname = ns_symbol.qualified_name();
if let Some(ns_body_scope) =
symbol_table.find_namespace_body_scope(ns_qname, ns_def_scope)
{
let ns_exports: Vec<(String, SymbolId)> = if import.is_recursive {
collect_namespace_exports(symbol_table, ns_qname, true)
} else {
symbol_table
.get_export_map(ns_body_scope)
.map(|m| m.iter().map(|(k, v)| (k.clone(), *v)).collect())
.unwrap_or_default()
};
for (name, symbol_id) in ns_exports {
if name.starts_with("import::") {
continue;
}
let existing = symbol_table
.get_export_map(scope_id)
.and_then(|m| m.get(&name));
if existing.is_none() {
symbol_table.add_to_export_map(scope_id, name, symbol_id);
changed = true;
}
}
}
}
}
}
}
if iterations >= MAX_ITERATIONS {
tracing::warn!("Export map building hit iteration limit - possible circular imports");
}
for scope_id in 0..scope_count {
let resolved_imports: Vec<ResolvedImport> =
symbol_table.get_resolved_imports(scope_id).to_vec();
for import in &resolved_imports {
if import.is_public && import.is_namespace {
continue;
}
if import.is_namespace {
let namespace = import
.resolved_path
.trim_end_matches("::**")
.trim_end_matches("::*");
let resolved_namespace = symbol_table
.find_by_qualified_name(namespace)
.map(|s| s.qualified_name().to_string())
.or_else(|| {
symbol_table
.get_export_map(scope_id)
.and_then(|m| m.get(namespace))
.and_then(|id| symbol_table.get_symbol(*id))
.map(|s| s.qualified_name().to_string())
});
if let Some(ns_qname) = resolved_namespace {
let exports_to_add =
collect_namespace_exports(symbol_table, &ns_qname, import.is_recursive);
for (name, symbol_id) in exports_to_add {
symbol_table.add_to_export_map(scope_id, name, symbol_id);
}
}
} else {
let import_path = &import.resolved_path;
let symbol_id = symbol_table
.find_id_by_qualified_name(import_path)
.or_else(|| {
if let Some(colon_pos) = import_path.rfind("::") {
let namespace = &import_path[..colon_pos];
let member = &import_path[colon_pos + 2..];
if let Some(ns_symbol) = symbol_table.find_by_qualified_name(namespace)
{
let ns_def_scope = ns_symbol.scope_id();
if let Some(ns_body) =
symbol_table.find_namespace_body_scope(namespace, ns_def_scope)
{
if let Some(export_map) = symbol_table.get_export_map(ns_body) {
return export_map.get(member).copied();
}
}
}
}
None
});
if let Some(symbol_id) = symbol_id {
if let Some(name) = import_path.rsplit("::").next() {
symbol_table.add_to_export_map(scope_id, name.to_string(), symbol_id);
}
}
}
}
}
}
fn collect_namespace_exports(
symbol_table: &SymbolTable,
namespace: &str,
is_recursive: bool,
) -> Vec<(String, SymbolId)> {
let mut exports = Vec::new();
let namespace_symbol = match symbol_table.find_by_qualified_name(namespace) {
Some(s) => s,
None => return exports,
};
let def_scope = namespace_symbol.scope_id();
if !is_recursive {
if let Some(body_scope) = symbol_table.find_namespace_body_scope(namespace, def_scope) {
if let Some(export_map) = symbol_table.get_export_map(body_scope) {
for (name, symbol_id) in export_map.iter() {
if !name.starts_with("import::") {
exports.push((name.clone(), *symbol_id));
}
}
} else {
exports.extend(symbol_table.get_scope_children_symbols(body_scope));
}
} else {
if let Some(scope) = symbol_table.scopes().get(def_scope) {
for &child_scope_id in &scope.children {
let children = symbol_table.get_scope_children_symbols(child_scope_id);
exports.extend(children);
}
exports.extend(symbol_table.get_scope_children_symbols(def_scope));
}
}
} else {
let prefix = format!("{}::", namespace);
for (id, symbol) in symbol_table.iter_symbols_with_ids() {
let qname = symbol.qualified_name();
if qname.starts_with(&prefix) {
if let Some(name) = qname.rsplit("::").next() {
exports.push((name.to_string(), id));
}
}
}
}
exports
}
#[cfg(test)]
mod tests {
}