use crate::{
common::{ Identifier, },
ctx::{ GlobalKey, GlobalItem, },
};
use super::{
Analyzer,
support_structures::{ Alias, AliasData, },
};
pub fn resolve_aliases (analyzer: &mut Analyzer, aliases: &mut Vec<Alias>) {
while let Some(alias) = aliases.pop() {
resolve_alias(analyzer, aliases, alias);
}
}
fn try_get_alias (aliases: &mut Vec<Alias>, in_module: GlobalKey, find_import: bool, identifier: &Identifier) -> Option<Alias> {
for (index, alias) in aliases.iter().enumerate() {
if alias.destination_module == in_module
&& (( find_import && matches!(alias.data, AliasData::Import { .. }))
|| (!find_import && matches!(alias.data, AliasData::Export { .. })))
&& &alias.new_name == identifier {
return Some(aliases.remove(index))
}
}
None
}
fn resolve_alias (analyzer: &mut Analyzer, aliases: &mut Vec<Alias>, alias: Alias) -> Option<GlobalKey> {
match alias.data {
AliasData::Import { absolute, relative_to, chain } => {
let dest_mod =
analyzer.context.items
.get(alias.destination_module)
.expect("Internal error, alias has invalid destination module key")
.ref_module()
.expect("Internal error, alias destination key does not resolve to a module");
if let Some(existing_key) = dest_mod.local_bindings.get_entry(&alias.new_name) {
let existing_origin =
dest_mod.local_bindings
.get_bind_location(existing_key)
.expect("Internal error, local item has no binding source location");
analyzer.error(alias.origin, format!(
"Module import `{}` shadows an existing item, defined at [{}]",
alias.new_name,
existing_origin,
))
}
let mut base_name = Identifier::default();
let mut resolved_key = relative_to;
for ident in chain.iter() {
let base = analyzer.context.items.get(resolved_key).expect("Internal error, invalid lowered key during import alias resolution");
if let GlobalItem::Module(module) = base {
base_name.set(&module.canonical_name);
resolved_key = if !absolute && resolved_key == relative_to {
if let Some(local) = module.local_bindings.get_entry(ident) {
local
} else if let Some(alias) = try_get_alias(aliases, resolved_key, true, ident) {
resolve_alias(analyzer, aliases, alias)?
} else if let Some(core) = analyzer.context.core_ns.get_entry(ident) {
core
} else {
analyzer.error(alias.origin, format!("Module `{}` does not have access to an item named `{}`", base_name, ident));
return None
}
} else if let Some(exported_key) = module.export_bindings.get_entry(ident) {
exported_key
} else if let Some(alias) = try_get_alias(aliases, resolved_key, false, ident) {
resolve_alias(analyzer, aliases, alias)?
} else {
analyzer.error(alias.origin, format!("Module `{}` does not export an item named `{}`", base_name, ident));
return None
};
} else {
analyzer.error(alias.origin, format!("{} is not a Module and has no exports", ident));
return None
}
}
let dest_mod_mut = unsafe { analyzer.context.items.get_unchecked_mut(alias.destination_module).mut_module_unchecked() };
dest_mod_mut.local_bindings.set_entry_bound(alias.new_name, resolved_key, alias.origin);
Some(resolved_key)
},
AliasData::Export { base } => {
let dest_mod =
analyzer.context.items
.get(alias.destination_module)
.expect("Internal error, alias has invalid destination module key")
.ref_module()
.expect("Internal error, alias destination key does not resolve to a module");
if let Some(existing_key) = dest_mod.export_bindings.get_entry(&alias.new_name) {
let existing_origin =
dest_mod.export_bindings
.get_bind_location(existing_key)
.expect("Internal error, exported item has no binding source location");
analyzer.error(alias.origin, format!(
"Module export `{}` shadows an existing export, defined at [{}]",
alias.new_name,
existing_origin,
))
}
let base_key = if let Some(local_key) = dest_mod.local_bindings.get_entry(&base) {
local_key
} else if let Some(alias) = try_get_alias(aliases, alias.destination_module, true, &base) {
resolve_alias(analyzer, aliases, alias)?
} else if let Some(core_key) = analyzer.context.core_ns.get_entry(&base) {
core_key
} else {
analyzer.error(alias.origin, format!("Cannot export undefined identifier `{}`", base));
return None
};
let dest_mod_mut = unsafe { analyzer.context.items.get_unchecked_mut(alias.destination_module).mut_module_unchecked() };
dest_mod_mut.export_bindings.set_entry_bound(alias.new_name, base_key, alias.origin);
Some(base_key)
},
}
}