miden_assembly_syntax/ast/
module.rs1use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_core::{
5 AdviceMap,
6 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8use miden_debug_types::{SourceFile, SourceSpan, Span, Spanned};
9use miden_utils_diagnostics::Report;
10
11use super::{
12 DocString, Export, Import, LocalNameResolver, ProcedureIndex, ProcedureName,
13 QualifiedProcedureName, ResolvedProcedure,
14};
15use crate::{
16 LibraryNamespace, LibraryPath,
17 ast::{AliasTarget, Ident},
18 parser::ModuleParser,
19 sema::SemanticAnalysisError,
20};
21
22#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
31#[repr(u8)]
32pub enum ModuleKind {
33 #[default]
40 Library = 0,
41 Executable = 1,
50 Kernel = 2,
61}
62
63impl ModuleKind {
64 pub fn is_executable(&self) -> bool {
65 matches!(self, Self::Executable)
66 }
67
68 pub fn is_kernel(&self) -> bool {
69 matches!(self, Self::Kernel)
70 }
71
72 pub fn is_library(&self) -> bool {
73 matches!(self, Self::Library)
74 }
75}
76
77impl fmt::Display for ModuleKind {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 match self {
80 Self::Library => f.write_str("library"),
81 Self::Executable => f.write_str("executable"),
82 Self::Kernel => f.write_str("kernel"),
83 }
84 }
85}
86
87impl Serializable for ModuleKind {
88 fn write_into<W: ByteWriter>(&self, target: &mut W) {
89 target.write_u8(*self as u8)
90 }
91}
92
93impl Deserializable for ModuleKind {
94 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
95 match source.read_u8()? {
96 0 => Ok(Self::Library),
97 1 => Ok(Self::Executable),
98 2 => Ok(Self::Kernel),
99 n => Err(DeserializationError::InvalidValue(format!("invalid module kind tag: {n}"))),
100 }
101 }
102}
103
104#[derive(Clone)]
112pub struct Module {
113 span: SourceSpan,
115 docs: Option<DocString>,
121 path: LibraryPath,
123 kind: ModuleKind,
125 pub(crate) imports: Vec<Import>,
127 pub(crate) procedures: Vec<Export>,
132 pub(crate) advice_map: AdviceMap,
134}
135
136impl Module {
138 pub const FILE_EXTENSION: &'static str = "masm";
140
141 pub const ROOT: &'static str = "mod";
143
144 pub const ROOT_FILENAME: &'static str = "mod.masm";
146}
147
148impl Module {
150 pub fn new(kind: ModuleKind, path: LibraryPath) -> Self {
153 Self {
154 span: Default::default(),
155 docs: None,
156 path,
157 kind,
158 imports: Default::default(),
159 procedures: Default::default(),
160 advice_map: Default::default(),
161 }
162 }
163
164 pub fn new_kernel() -> Self {
166 Self::new(ModuleKind::Kernel, LibraryNamespace::Kernel.into())
167 }
168
169 pub fn new_executable() -> Self {
171 Self::new(ModuleKind::Executable, LibraryNamespace::Exec.into())
172 }
173
174 pub fn with_span(mut self, span: SourceSpan) -> Self {
177 self.span = span;
178 self
179 }
180
181 pub fn set_path(&mut self, path: LibraryPath) {
183 self.path = path;
184 }
185
186 pub fn set_namespace(&mut self, ns: LibraryNamespace) {
188 self.path.set_namespace(ns);
189 }
190
191 pub fn set_docs(&mut self, docs: Option<Span<String>>) {
193 self.docs = docs.map(DocString::new);
194 }
195
196 pub fn set_span(&mut self, span: SourceSpan) {
198 self.span = span;
199 }
200
201 pub fn define_procedure(&mut self, export: Export) -> Result<(), SemanticAnalysisError> {
204 if self.is_kernel() && matches!(export, Export::Alias(_)) {
205 return Err(SemanticAnalysisError::ReexportFromKernel { span: export.span() });
206 }
207 if let Some(prev) = self.resolve(export.name()) {
208 let prev_span = prev.span();
209 Err(SemanticAnalysisError::SymbolConflict { span: export.span(), prev_span })
210 } else {
211 self.procedures.push(export);
212 Ok(())
213 }
214 }
215
216 pub fn define_import(&mut self, import: Import) -> Result<(), SemanticAnalysisError> {
219 if let Some(prev_import) = self.resolve_import(&import.name) {
220 let prev_span = prev_import.span;
221 return Err(SemanticAnalysisError::ImportConflict { span: import.span, prev_span });
222 }
223
224 if let Some(prev_defined) = self.procedures.iter().find(|e| e.name().eq(&import.name)) {
225 let prev_span = prev_defined.span();
226 return Err(SemanticAnalysisError::SymbolConflict { span: import.span, prev_span });
227 }
228
229 self.imports.push(import);
230
231 Ok(())
232 }
233}
234
235impl Module {
237 pub fn parse(
239 name: LibraryPath,
240 kind: ModuleKind,
241 source_file: Arc<SourceFile>,
242 ) -> Result<Box<Self>, Report> {
243 let mut parser = Self::parser(kind);
244 parser.parse(name, source_file)
245 }
246
247 pub fn parser(kind: ModuleKind) -> ModuleParser {
249 ModuleParser::new(kind)
250 }
251}
252
253impl Module {
255 pub fn name(&self) -> &str {
258 self.path.last()
259 }
260
261 pub fn path(&self) -> &LibraryPath {
263 &self.path
264 }
265
266 pub fn namespace(&self) -> &LibraryNamespace {
268 self.path.namespace()
269 }
270
271 pub fn is_in_namespace(&self, namespace: &LibraryNamespace) -> bool {
273 self.path.namespace() == namespace
274 }
275
276 pub fn docs(&self) -> Option<Span<&str>> {
279 self.docs.as_ref().map(|spanned| spanned.as_spanned_str())
280 }
281
282 pub fn kind(&self) -> ModuleKind {
286 self.kind
287 }
288
289 pub fn set_kind(&mut self, kind: ModuleKind) {
293 self.kind = kind;
294 }
295
296 #[inline(always)]
298 pub fn is_executable(&self) -> bool {
299 self.kind.is_executable()
300 }
301
302 #[inline(always)]
304 pub fn is_kernel(&self) -> bool {
305 self.kind.is_kernel() && self.path.is_kernel_path()
306 }
307
308 #[inline(always)]
310 pub fn is_in_kernel(&self) -> bool {
311 self.kind.is_kernel()
312 }
313
314 pub fn has_entrypoint(&self) -> bool {
317 self.index_of(|p| p.is_main()).is_some()
318 }
319
320 pub fn advice_map(&self) -> &AdviceMap {
322 &self.advice_map
323 }
324
325 pub fn procedures(&self) -> core::slice::Iter<'_, Export> {
330 self.procedures.iter()
331 }
332
333 pub fn procedures_mut(&mut self) -> core::slice::IterMut<'_, Export> {
335 self.procedures.iter_mut()
336 }
337
338 pub fn exported_procedures(
343 &self,
344 ) -> impl Iterator<Item = (ProcedureIndex, QualifiedProcedureName)> + '_ {
345 self.procedures.iter().enumerate().filter_map(|(proc_idx, p)| {
346 if !p.visibility().is_exported() {
348 return None;
349 }
350
351 let proc_idx = ProcedureIndex::new(proc_idx);
352 let fqn = QualifiedProcedureName::new(self.path().clone(), p.name().clone());
353
354 Some((proc_idx, fqn))
355 })
356 }
357
358 pub fn imports(&self) -> core::slice::Iter<'_, Import> {
362 self.imports.iter()
363 }
364
365 pub fn imports_mut(&mut self) -> core::slice::IterMut<'_, Import> {
367 self.imports.iter_mut()
368 }
369
370 pub fn dependencies(&self) -> impl Iterator<Item = &LibraryNamespace> {
377 self.import_paths().map(|import| import.namespace())
378 }
379
380 pub fn get(&self, index: ProcedureIndex) -> Option<&Export> {
385 self.procedures.get(index.as_usize())
386 }
387
388 pub fn index_of<F>(&self, predicate: F) -> Option<ProcedureIndex>
391 where
392 F: FnMut(&Export) -> bool,
393 {
394 self.procedures.iter().position(predicate).map(ProcedureIndex::new)
395 }
396
397 pub fn index_of_name(&self, name: &ProcedureName) -> Option<ProcedureIndex> {
402 self.index_of(|p| p.name() == name && p.visibility().is_exported())
403 }
404
405 pub fn resolve(&self, name: &ProcedureName) -> Option<ResolvedProcedure> {
407 let index =
408 self.procedures.iter().position(|p| p.name() == name).map(ProcedureIndex::new)?;
409 match &self.procedures[index.as_usize()] {
410 Export::Procedure(proc) => {
411 Some(ResolvedProcedure::Local(Span::new(proc.name().span(), index)))
412 },
413 Export::Alias(alias) => match alias.target() {
414 AliasTarget::MastRoot(digest) => Some(ResolvedProcedure::MastRoot(**digest)),
415 AliasTarget::ProcedurePath(path) | AliasTarget::AbsoluteProcedurePath(path) => {
416 Some(ResolvedProcedure::External(path.clone()))
417 },
418 },
419 }
420 }
421
422 pub fn resolver(&self) -> LocalNameResolver {
424 LocalNameResolver::from_iter(self.procedures.iter().enumerate().map(|(i, p)| match p {
425 Export::Procedure(p) => (
426 p.name().clone(),
427 ResolvedProcedure::Local(Span::new(p.name().span(), ProcedureIndex::new(i))),
428 ),
429 Export::Alias(p) => {
430 let target = match p.target() {
431 AliasTarget::MastRoot(digest) => ResolvedProcedure::MastRoot(**digest),
432 AliasTarget::ProcedurePath(path) | AliasTarget::AbsoluteProcedurePath(path) => {
433 ResolvedProcedure::External(path.clone())
434 },
435 };
436 (p.name().clone(), target)
437 },
438 }))
439 .with_imports(
440 self.imports
441 .iter()
442 .map(|import| (import.name.clone(), Span::new(import.span(), import.path.clone()))),
443 )
444 }
445
446 pub fn resolve_import(&self, module_name: &Ident) -> Option<&Import> {
448 self.imports.iter().find(|import| &import.name == module_name)
449 }
450
451 pub fn resolve_import_mut(&mut self, module_name: &Ident) -> Option<&mut Import> {
453 self.imports.iter_mut().find(|import| &import.name == module_name)
454 }
455
456 pub fn import_paths(&self) -> impl Iterator<Item = &LibraryPath> + '_ {
458 self.imports.iter().map(|import| &import.path)
459 }
460}
461
462impl core::ops::Index<ProcedureIndex> for Module {
463 type Output = Export;
464
465 #[inline]
466 fn index(&self, index: ProcedureIndex) -> &Self::Output {
467 &self.procedures[index.as_usize()]
468 }
469}
470
471impl core::ops::IndexMut<ProcedureIndex> for Module {
472 #[inline]
473 fn index_mut(&mut self, index: ProcedureIndex) -> &mut Self::Output {
474 &mut self.procedures[index.as_usize()]
475 }
476}
477
478impl Spanned for Module {
479 fn span(&self) -> SourceSpan {
480 self.span
481 }
482}
483
484impl Eq for Module {}
485
486impl PartialEq for Module {
487 fn eq(&self, other: &Self) -> bool {
488 self.kind == other.kind
489 && self.path == other.path
490 && self.docs == other.docs
491 && self.imports == other.imports
492 && self.procedures == other.procedures
493 }
494}
495
496impl fmt::Debug for Module {
498 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
499 f.debug_struct("Module")
500 .field("docs", &self.docs)
501 .field("path", &self.path)
502 .field("kind", &self.kind)
503 .field("imports", &self.imports)
504 .field("procedures", &self.procedures)
505 .finish()
506 }
507}
508
509impl fmt::Display for Module {
513 #[inline]
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 use crate::prettier::PrettyPrint;
520
521 self.pretty_print(f)
522 }
523}
524
525impl crate::prettier::PrettyPrint for Module {
527 fn render(&self) -> crate::prettier::Document {
528 use crate::prettier::*;
529
530 let mut doc = self
531 .docs
532 .as_ref()
533 .map(|docstring| docstring.render() + nl())
534 .unwrap_or(Document::Empty);
535
536 for (i, import) in self.imports.iter().enumerate() {
537 if i > 0 {
538 doc += nl();
539 }
540 doc += import.render();
541 }
542
543 if !self.imports.is_empty() {
544 doc += nl();
545 }
546
547 let mut export_index = 0;
548 for export in self.procedures.iter() {
549 if export.is_main() {
550 continue;
551 }
552 if export_index > 0 {
553 doc += nl();
554 }
555 doc += export.render();
556 export_index += 1;
557 }
558
559 if let Some(main) = self.procedures().find(|p| p.is_main()) {
560 if export_index > 0 {
561 doc += nl();
562 }
563 doc += main.render();
564 }
565
566 doc
567 }
568}