Skip to main content

miden_assembly_syntax/
module.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::ops::Index;
3
4use miden_core::mast::MastNodeId;
5use midenc_hir_type::FunctionType;
6
7use crate::{
8    Path, Word,
9    ast::{self, AttributeSet, ConstantValue, Ident, ItemIndex, ProcedureName, SubmoduleDecl},
10};
11
12// MODULE INFO
13// ================================================================================================
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct ModuleInfo {
17    path: Arc<Path>,
18    /// When specified, multiple modules with the same name can be present, so long as they are
19    /// disambiguated by version.
20    version: Option<crate::Version>,
21    items: Vec<ItemInfo>,
22    submodules: Vec<SubmoduleDecl>,
23}
24
25impl ModuleInfo {
26    pub(crate) fn raw_items(&self) -> &[ItemInfo] {
27        &self.items
28    }
29
30    /// Returns a new [`ModuleInfo`] instantiated by library path and optional semantic version.
31    ///
32    /// The semantic version is optional, as currently the assembler allows assembling artifacts
33    /// without providing one.
34    pub fn new(path: Arc<Path>, version: Option<crate::Version>) -> Self {
35        Self {
36            version,
37            path,
38            items: Vec::new(),
39            submodules: Vec::new(),
40        }
41    }
42
43    /// Specify the version of this module
44    pub fn set_version(&mut self, version: crate::Version) {
45        self.version = Some(version);
46    }
47
48    /// Adds a procedure to the module.
49    pub fn add_procedure(
50        &mut self,
51        name: ProcedureName,
52        digest: Word,
53        signature: Option<Arc<FunctionType>>,
54        attributes: AttributeSet,
55    ) {
56        self.add_procedure_with_provenance(name, digest, signature, attributes, None, None, None);
57    }
58
59    /// Adds a procedure to the module with optional source provenance.
60    pub fn add_procedure_with_provenance(
61        &mut self,
62        name: ProcedureName,
63        digest: Word,
64        signature: Option<Arc<FunctionType>>,
65        attributes: AttributeSet,
66        source_root_id: Option<MastNodeId>,
67        source_debug_root_id: Option<u32>,
68        source_library_commitment: Option<Word>,
69    ) {
70        self.items.push(ItemInfo::Procedure(ProcedureInfo {
71            name,
72            digest,
73            signature,
74            attributes,
75            source_root_id,
76            source_debug_root_id,
77            source_library_commitment,
78        }));
79    }
80
81    /// Adds a constant to the module.
82    pub fn add_constant(&mut self, name: Ident, value: ConstantValue) {
83        self.items.push(ItemInfo::Constant(ConstantInfo { name, value }));
84    }
85
86    /// Adds a type declaration to the module.
87    pub fn add_type(&mut self, name: Ident, ty: ast::types::Type) {
88        self.items.push(ItemInfo::Type(TypeInfo { name, ty }));
89    }
90
91    /// Adds a submodule declaration to the module surface.
92    pub fn add_submodule(&mut self, submodule: SubmoduleDecl) {
93        self.submodules.push(submodule);
94    }
95
96    /// Returns the module's library path.
97    pub fn path(&self) -> &Path {
98        &self.path
99    }
100
101    /// Returns the number of procedures in the module.
102    pub fn num_procedures(&self) -> usize {
103        self.items.iter().filter(|item| matches!(item, ItemInfo::Procedure(_))).count()
104    }
105
106    /// Returns the [`ItemInfo`] of the item at the provided index, if any.
107    pub fn get_item_by_index(&self, index: ItemIndex) -> Option<&ItemInfo> {
108        self.items.get(index.as_usize())
109    }
110
111    /// Returns the [ItemIndex] of an item by its local name
112    pub fn get_item_index_by_name(&self, name: &str) -> Option<ItemIndex> {
113        self.items.iter().enumerate().find_map(|(idx, info)| {
114            if info.name().as_str() == name {
115                Some(ItemIndex::new(idx))
116            } else {
117                None
118            }
119        })
120    }
121
122    /// Returns the procedure info for the procedure with the provided name, if any.
123    pub fn get_procedure_by_name(&self, name: &str) -> Option<&ProcedureInfo> {
124        self.items.iter().find_map(|info| match info {
125            ItemInfo::Procedure(proc) if proc.name.as_str() == name => Some(proc),
126            _ => None,
127        })
128    }
129
130    /// Returns the digest of the procedure with the provided name, if any.
131    pub fn get_procedure_digest_by_name(&self, name: &str) -> Option<Word> {
132        self.get_procedure_by_name(name).map(|proc| proc.digest)
133    }
134
135    /// Returns an iterator over the items in the module with their corresponding item index in the
136    /// module.
137    pub fn items(&self) -> impl ExactSizeIterator<Item = (ItemIndex, &ItemInfo)> {
138        self.items.iter().enumerate().map(|(idx, item)| (ItemIndex::new(idx), item))
139    }
140
141    /// Returns the declared submodules in this module surface.
142    pub fn submodules(&self) -> &[SubmoduleDecl] {
143        &self.submodules
144    }
145
146    /// Returns an iterator over the procedure infos in the module with their corresponding
147    /// item index in the module.
148    pub fn procedures(&self) -> impl Iterator<Item = (ItemIndex, &ProcedureInfo)> {
149        self.items.iter().enumerate().filter_map(|(idx, item)| match item {
150            ItemInfo::Procedure(proc) => Some((ItemIndex::new(idx), proc)),
151            _ => None,
152        })
153    }
154
155    /// Returns an iterator over the MAST roots of procedures defined in this module.
156    pub fn procedure_digests(&self) -> impl Iterator<Item = Word> + '_ {
157        self.items.iter().filter_map(|item| match item {
158            ItemInfo::Procedure(proc) => Some(proc.digest),
159            _ => None,
160        })
161    }
162
163    /// Access the constants associated with this module
164    pub fn constants(&self) -> impl Iterator<Item = (ItemIndex, &ConstantInfo)> {
165        self.items.iter().enumerate().filter_map(|(idx, item)| match item {
166            ItemInfo::Constant(info) => Some((ItemIndex::new(idx), info)),
167            _ => None,
168        })
169    }
170
171    /// Access the type declarations associated with this module
172    pub fn types(&self) -> impl Iterator<Item = (ItemIndex, &TypeInfo)> {
173        self.items.iter().enumerate().filter_map(|(idx, item)| match item {
174            ItemInfo::Type(info) => Some((ItemIndex::new(idx), info)),
175            _ => None,
176        })
177    }
178}
179
180impl Index<ItemIndex> for ModuleInfo {
181    type Output = ItemInfo;
182
183    fn index(&self, index: ItemIndex) -> &Self::Output {
184        &self.items[index.as_usize()]
185    }
186}
187
188/// Stores information about an item
189#[derive(Debug, Clone, PartialEq, Eq)]
190pub enum ItemInfo {
191    Procedure(ProcedureInfo),
192    Constant(ConstantInfo),
193    Type(TypeInfo),
194}
195
196impl ItemInfo {
197    pub fn name(&self) -> &Ident {
198        match self {
199            Self::Procedure(info) => info.name.as_ref(),
200            Self::Constant(info) => &info.name,
201            Self::Type(info) => &info.name,
202        }
203    }
204
205    pub fn attributes(&self) -> Option<&AttributeSet> {
206        match self {
207            Self::Procedure(info) => Some(&info.attributes),
208            Self::Constant(_) | Self::Type(_) => None,
209        }
210    }
211
212    pub fn unwrap_procedure(&self) -> &ProcedureInfo {
213        match self {
214            Self::Procedure(info) => info,
215            Self::Constant(_) | Self::Type(_) => panic!("expected item to be a procedure"),
216        }
217    }
218}
219
220/// Stores the name and digest of a procedure.
221#[derive(Debug, Clone, PartialEq, Eq)]
222pub struct ProcedureInfo {
223    pub name: ProcedureName,
224    pub digest: Word,
225    pub signature: Option<Arc<FunctionType>>,
226    pub attributes: AttributeSet,
227    /// The exact procedure root in the source library, if known.
228    ///
229    /// This is needed when multiple exported procedures share the same digest but carry different
230    /// diagnostics metadata.
231    pub source_root_id: Option<MastNodeId>,
232    /// The commitment of the source library forest that `source_root_id` belongs to, if known.
233    pub source_library_commitment: Option<Word>,
234    /// The exact source/debug occurrence root in package-owned debug info, if known.
235    pub source_debug_root_id: Option<u32>,
236}
237
238impl ProcedureInfo {
239    pub fn source_root_id(&self) -> Option<MastNodeId> {
240        self.source_root_id
241    }
242
243    pub fn source_library_commitment(&self) -> Option<Word> {
244        self.source_library_commitment
245    }
246
247    pub fn source_debug_root_id(&self) -> Option<u32> {
248        self.source_debug_root_id
249    }
250}
251
252/// Stores the name and value of a constant
253#[derive(Debug, Clone, PartialEq, Eq)]
254pub struct ConstantInfo {
255    pub name: Ident,
256    pub value: ConstantValue,
257}
258
259/// Stores the name and concrete type of a type declaration
260#[derive(Debug, Clone, PartialEq, Eq)]
261pub struct TypeInfo {
262    pub name: Ident,
263    pub ty: ast::types::Type,
264}