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