Skip to main content

miden_assembly_syntax/ast/item/resolver/
mod.rs

1mod error;
2mod symbol_table;
3
4use alloc::sync::Arc;
5
6use miden_debug_types::{SourceManager, SourceSpan, Span, Spanned};
7
8use self::symbol_table::LocalSymbolTable;
9pub use self::{
10    error::SymbolResolutionError,
11    symbol_table::{LocalSymbol, SymbolTable},
12};
13use super::{GlobalItemIndex, ModuleIndex};
14use crate::{
15    Path, Word,
16    ast::{AliasTarget, Ident, ItemIndex},
17};
18
19/// Represents the result of resolving a symbol
20#[derive(Debug, Clone)]
21pub enum SymbolResolution {
22    /// The name was resolved to a definition in the same module at the given index
23    Local(Span<ItemIndex>),
24    /// The name was resolved to a path referring to an item exported from another module
25    External(Span<Arc<Path>>),
26    /// The name was resolved to a procedure MAST root
27    MastRoot(Span<Word>),
28    /// The name was resolved to a known definition in with the given global index and absolute path
29    Exact {
30        gid: GlobalItemIndex,
31        path: Span<Arc<Path>>,
32    },
33    /// The name was resolved to a known module
34    Module { id: ModuleIndex, path: Span<Arc<Path>> },
35}
36
37impl SymbolResolution {
38    pub fn into_global_id(&self) -> Option<GlobalItemIndex> {
39        match self {
40            Self::Exact { gid, .. } => Some(*gid),
41            Self::Local(_) | Self::External(_) | Self::MastRoot(_) | Self::Module { .. } => None,
42        }
43    }
44}
45
46impl Spanned for SymbolResolution {
47    fn span(&self) -> SourceSpan {
48        match self {
49            Self::Local(p) => p.span(),
50            Self::External(p) => p.span(),
51            Self::MastRoot(p) => p.span(),
52            Self::Exact { path, .. } => path.span(),
53            Self::Module { path, .. } => path.span(),
54        }
55    }
56}
57
58// LOCAL SYMBOL RESOLVER
59// ================================================================================================
60
61/// A resolver for symbol references in the context of some module.
62///
63/// This resolver does not attempt to resolve external references, aside from expanding any uses
64/// of imports in an external path.
65///
66/// This is used as a low-level symbol resolution primitive in the linker as well.
67pub struct LocalSymbolResolver {
68    source_manager: Arc<dyn SourceManager>,
69    symbols: LocalSymbolTable,
70}
71
72impl LocalSymbolResolver {
73    /// Create a new resolver using the provided [SymbolTable] and [SourceManager].
74    pub fn new<S>(
75        symbols: S,
76        source_manager: Arc<dyn SourceManager>,
77    ) -> Result<Self, SymbolResolutionError>
78    where
79        S: SymbolTable,
80    {
81        let symbols = LocalSymbolTable::new(symbols, source_manager.clone())?;
82        Ok(Self { source_manager, symbols })
83    }
84
85    /// Expand `path` using `get_import` to resolve a raw symbol name to an import in the current
86    /// symbol resolution context.
87    ///
88    /// Uses the provided [SourceManager] to emit errors that are discovered during expansion.
89    #[inline]
90    pub fn expand<F>(
91        get_import: F,
92        path: Span<&Path>,
93        source_manager: &dyn SourceManager,
94    ) -> Result<SymbolResolution, SymbolResolutionError>
95    where
96        F: Fn(&str) -> Option<AliasTarget>,
97    {
98        LocalSymbolTable::expand(get_import, path, source_manager)
99    }
100
101    #[inline]
102    pub fn source_manager(&self) -> Arc<dyn SourceManager> {
103        self.source_manager.clone()
104    }
105
106    /// Try to resolve `name` to an item, either local or external
107    ///
108    /// See `SymbolTable::get` for details.
109    #[inline]
110    pub fn resolve(&self, name: Span<&str>) -> Result<SymbolResolution, SymbolResolutionError> {
111        self.symbols.get(name)
112    }
113
114    /// Try to resolve `path` to an item, either local or external
115    pub fn resolve_path(
116        &self,
117        path: Span<&Path>,
118    ) -> Result<SymbolResolution, SymbolResolutionError> {
119        if path.is_absolute() {
120            return Ok(SymbolResolution::External(path.map(Into::into)));
121        }
122        log::debug!(target: "local-symbol-resolver", "resolving path '{path}'");
123        let (ns, subpath) = path.split_first().expect("invalid item path");
124        log::debug!(target: "local-symbol-resolver", "resolving symbol '{ns}'");
125        match self.resolve(Span::new(path.span(), ns))? {
126            SymbolResolution::External(target) => {
127                log::debug!(target: "local-symbol-resolver", "resolved '{ns}' to import of '{target}'");
128                if subpath.is_empty() {
129                    log::debug!(target: "local-symbol-resolver", "resolved '{path}' '{target}'");
130                    Ok(SymbolResolution::External(target))
131                } else {
132                    let resolved = target.join(subpath).into();
133                    log::debug!(target: "local-symbol-resolver", "resolved '{path}' '{resolved}'");
134                    Ok(SymbolResolution::External(Span::new(target.span(), resolved)))
135                }
136            },
137            SymbolResolution::Local(item) => {
138                log::debug!(target: "local-symbol-resolver", "resolved '{ns}' to local item '{item}'");
139                if subpath.is_empty() {
140                    return Ok(SymbolResolution::Local(item));
141                }
142
143                // This is an invalid subpath reference
144                log::error!(target: "local-symbol-resolver", "cannot resolve '{subpath}' relative to non-module item");
145                Err(SymbolResolutionError::invalid_sub_path(
146                    path.span(),
147                    item.span(),
148                    &*self.source_manager,
149                ))
150            },
151            SymbolResolution::MastRoot(digest) => {
152                log::debug!(target: "local-symbol-resolver", "resolved '{ns}' to procedure root '{digest}'");
153                if subpath.is_empty() {
154                    return Ok(SymbolResolution::MastRoot(digest));
155                }
156
157                // This is an invalid subpath reference
158                log::error!(target: "local-symbol-resolver", "cannot resolve '{subpath}' relative to procedure");
159                Err(SymbolResolutionError::invalid_sub_path(
160                    path.span(),
161                    digest.span(),
162                    &*self.source_manager,
163                ))
164            },
165            SymbolResolution::Module { id, path, .. } => {
166                if subpath.is_empty() {
167                    Ok(SymbolResolution::Module { id, path })
168                } else {
169                    Ok(SymbolResolution::External(path.map(|p| p.join(subpath).into())))
170                }
171            },
172            SymbolResolution::Exact { .. } => unreachable!(),
173        }
174    }
175
176    /// Get the name of the item at `index`
177    ///
178    /// This is guaranteed to resolve if `index` is valid, and will panic if not.
179    pub fn get_item_name(&self, index: ItemIndex) -> Ident {
180        match &self.symbols[index] {
181            LocalSymbol::Item { name, .. } => name.clone(),
182            LocalSymbol::Import { name, .. } => Ident::from_raw_parts(name.clone()),
183        }
184    }
185}