mod error;
mod symbol_table;
use alloc::sync::Arc;
use miden_debug_types::{SourceManager, SourceSpan, Span, Spanned};
use self::symbol_table::LocalSymbolTable;
pub use self::{
error::SymbolResolutionError,
symbol_table::{LocalSymbol, SymbolTable},
};
use super::{GlobalItemIndex, ModuleIndex};
use crate::{
Path, Word,
ast::{AliasTarget, Ident, ItemIndex},
};
#[derive(Debug, Clone)]
pub enum SymbolResolution {
Local(Span<ItemIndex>),
External(Span<Arc<Path>>),
MastRoot(Span<Word>),
Exact {
gid: GlobalItemIndex,
path: Span<Arc<Path>>,
},
Module { id: ModuleIndex, path: Span<Arc<Path>> },
}
impl SymbolResolution {
pub fn into_global_id(&self) -> Option<GlobalItemIndex> {
match self {
Self::Exact { gid, .. } => Some(*gid),
Self::Local(_) | Self::External(_) | Self::MastRoot(_) | Self::Module { .. } => None,
}
}
}
impl Spanned for SymbolResolution {
fn span(&self) -> SourceSpan {
match self {
Self::Local(p) => p.span(),
Self::External(p) => p.span(),
Self::MastRoot(p) => p.span(),
Self::Exact { path, .. } => path.span(),
Self::Module { path, .. } => path.span(),
}
}
}
pub struct LocalSymbolResolver {
symbols: LocalSymbolTable,
}
impl LocalSymbolResolver {
pub fn new<S>(symbols: S, source_manager: Arc<dyn SourceManager>) -> Self
where
S: SymbolTable,
{
Self {
symbols: LocalSymbolTable::new(symbols, source_manager),
}
}
#[inline]
pub fn expand<F>(
get_import: F,
path: Span<&Path>,
source_manager: &dyn SourceManager,
) -> Result<SymbolResolution, SymbolResolutionError>
where
F: Fn(&str) -> Option<AliasTarget>,
{
LocalSymbolTable::expand(get_import, path, source_manager)
}
#[inline]
pub fn source_manager(&self) -> Arc<dyn SourceManager> {
self.symbols.source_manager_arc()
}
#[inline]
pub fn resolve(&self, name: Span<&str>) -> Result<SymbolResolution, SymbolResolutionError> {
self.symbols.get(name)
}
pub fn resolve_path(
&self,
path: Span<&Path>,
) -> Result<SymbolResolution, SymbolResolutionError> {
if path.is_absolute() {
return Ok(SymbolResolution::External(path.map(|p| p.into())));
}
log::debug!(target: "local-symbol-resolver", "resolving path '{path}'");
let (ns, subpath) = path.split_first().expect("invalid item path");
log::debug!(target: "local-symbol-resolver", "resolving symbol '{ns}'");
match self.resolve(Span::new(path.span(), ns))? {
SymbolResolution::External(target) => {
log::debug!(target: "local-symbol-resolver", "resolved '{ns}' to import of '{target}'");
if subpath.is_empty() {
log::debug!(target: "local-symbol-resolver", "resolved '{path}' '{target}'");
Ok(SymbolResolution::External(target))
} else {
let resolved = target.join(subpath).into();
log::debug!(target: "local-symbol-resolver", "resolved '{path}' '{resolved}'");
Ok(SymbolResolution::External(Span::new(target.span(), resolved)))
}
},
SymbolResolution::Local(item) => {
log::debug!(target: "local-symbol-resolver", "resolved '{ns}' to local item '{item}'");
if subpath.is_empty() {
return Ok(SymbolResolution::Local(item));
}
log::error!(target: "local-symbol-resolver", "cannot resolve '{subpath}' relative to non-module item");
Err(SymbolResolutionError::invalid_sub_path(
path.span(),
item.span(),
self.symbols.source_manager(),
))
},
SymbolResolution::MastRoot(digest) => {
log::debug!(target: "local-symbol-resolver", "resolved '{ns}' to procedure root '{digest}'");
if subpath.is_empty() {
return Ok(SymbolResolution::MastRoot(digest));
}
log::error!(target: "local-symbol-resolver", "cannot resolve '{subpath}' relative to procedure");
Err(SymbolResolutionError::invalid_sub_path(
path.span(),
digest.span(),
self.symbols.source_manager(),
))
},
SymbolResolution::Module { id, path, .. } => {
if subpath.is_empty() {
Ok(SymbolResolution::Module { id, path })
} else {
Ok(SymbolResolution::External(path.map(|p| p.join(subpath).into())))
}
},
SymbolResolution::Exact { .. } => unreachable!(),
}
}
pub fn get_item_name(&self, index: ItemIndex) -> Ident {
match &self.symbols[index] {
LocalSymbol::Item { name, .. } => name.clone(),
LocalSymbol::Import { name, .. } => Ident::from_raw_parts(name.clone()),
}
}
}