miden_assembly_syntax/ast/procedure/
resolver.rs

1use alloc::{collections::BTreeMap, vec::Vec};
2
3use miden_debug_types::{SourceSpan, Span, Spanned};
4
5use super::{ProcedureIndex, ProcedureName, QualifiedProcedureName};
6use crate::{LibraryPath, Word, ast::Ident};
7
8// RESOLVED PROCEDURE
9// ================================================================================================
10
11/// Represents the result of resolving a [ProcedureName] in the context of a module.
12#[derive(Debug, Clone)]
13pub enum ResolvedProcedure {
14    /// The name was resolved to a procedure definition in the same module at the given index
15    Local(Span<ProcedureIndex>),
16    /// The name was resolved to a procedure exported from another module
17    External(QualifiedProcedureName),
18    /// The name was resolved to a procedure with a known MAST root
19    MastRoot(Word),
20}
21
22impl Spanned for ResolvedProcedure {
23    fn span(&self) -> SourceSpan {
24        match self {
25            ResolvedProcedure::Local(p) => p.span(),
26            ResolvedProcedure::External(p) => p.span(),
27            ResolvedProcedure::MastRoot(_) => SourceSpan::default(),
28        }
29    }
30}
31
32// LOCAL NAME RESOLVER
33// ================================================================================================
34
35/// A lookup table for procedure names in the context of some module
36pub struct LocalNameResolver {
37    imports: BTreeMap<Ident, Span<LibraryPath>>,
38    resolved: BTreeMap<ProcedureName, ProcedureIndex>,
39    resolutions: Vec<ResolvedProcedure>,
40}
41
42impl LocalNameResolver {
43    /// Try to resolve `name` as a procedure
44    pub fn resolve(&self, name: &ProcedureName) -> Option<ResolvedProcedure> {
45        self.resolved
46            .get(name)
47            .copied()
48            .map(|index| self.resolutions[index.as_usize()].clone())
49    }
50
51    /// Try to resolve `name` to an imported module, returning the [LibraryPath] of that module.
52    pub fn resolve_import(&self, name: &Ident) -> Option<Span<&LibraryPath>> {
53        self.imports.get(name).map(|spanned| spanned.as_ref())
54    }
55
56    /// Get the name of the procedure at `index`
57    ///
58    /// This is guaranteed to resolve if `index` is valid, and will panic if not.
59    pub fn get_name(&self, index: ProcedureIndex) -> &ProcedureName {
60        self.resolved
61            .iter()
62            .find_map(|(k, v)| if v == &index { Some(k) } else { None })
63            .expect("invalid procedure index")
64    }
65
66    /// Extend the set of imports this resolver knows about.
67    pub fn with_imports<I>(mut self, imports: I) -> Self
68    where
69        I: IntoIterator<Item = (Ident, Span<LibraryPath>)>,
70    {
71        self.imports.extend(imports);
72        self
73    }
74}
75
76impl FromIterator<(ProcedureName, ResolvedProcedure)> for LocalNameResolver {
77    /// Construct a [LocalNameResolver] from an iterator of resolved names.
78    fn from_iter<T>(iter: T) -> Self
79    where
80        T: IntoIterator<Item = (ProcedureName, ResolvedProcedure)>,
81    {
82        let mut resolver = Self {
83            imports: Default::default(),
84            resolved: Default::default(),
85            resolutions: Default::default(),
86        };
87        for (name, resolution) in iter {
88            let index = ProcedureIndex::new(resolver.resolutions.len());
89            resolver.resolutions.push(resolution);
90            resolver.resolved.insert(name, index);
91        }
92        resolver
93    }
94}