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;
10use midenc_hir_type::FunctionType;
11
12use super::{
13 Constant, DocString, EnumType, Export, Import, LocalNameResolver, ProcedureIndex,
14 ProcedureName, QualifiedProcedureName, ResolvedProcedure, TypeAlias, TypeDecl, Variant,
15};
16use crate::{
17 LibraryNamespace, LibraryPath,
18 ast::{AliasTarget, Ident},
19 parser::ModuleParser,
20 sema::SemanticAnalysisError,
21};
22
23#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
32#[repr(u8)]
33pub enum ModuleKind {
34 #[default]
41 Library = 0,
42 Executable = 1,
51 Kernel = 2,
62}
63
64impl ModuleKind {
65 pub fn is_executable(&self) -> bool {
66 matches!(self, Self::Executable)
67 }
68
69 pub fn is_kernel(&self) -> bool {
70 matches!(self, Self::Kernel)
71 }
72
73 pub fn is_library(&self) -> bool {
74 matches!(self, Self::Library)
75 }
76}
77
78impl fmt::Display for ModuleKind {
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 match self {
81 Self::Library => f.write_str("library"),
82 Self::Executable => f.write_str("executable"),
83 Self::Kernel => f.write_str("kernel"),
84 }
85 }
86}
87
88impl Serializable for ModuleKind {
89 fn write_into<W: ByteWriter>(&self, target: &mut W) {
90 target.write_u8(*self as u8)
91 }
92}
93
94impl Deserializable for ModuleKind {
95 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
96 match source.read_u8()? {
97 0 => Ok(Self::Library),
98 1 => Ok(Self::Executable),
99 2 => Ok(Self::Kernel),
100 n => Err(DeserializationError::InvalidValue(format!("invalid module kind tag: {n}"))),
101 }
102 }
103}
104
105#[derive(Clone)]
113pub struct Module {
114 span: SourceSpan,
116 docs: Option<DocString>,
122 path: LibraryPath,
124 kind: ModuleKind,
126 pub(crate) constants: Vec<Constant>,
128 pub(crate) types: Vec<TypeDecl>,
130 pub(crate) imports: Vec<Import>,
132 pub(crate) procedures: Vec<Export>,
137 pub(crate) advice_map: AdviceMap,
139}
140
141impl Module {
143 pub const FILE_EXTENSION: &'static str = "masm";
145
146 pub const ROOT: &'static str = "mod";
148
149 pub const ROOT_FILENAME: &'static str = "mod.masm";
151}
152
153impl Module {
155 pub fn new(kind: ModuleKind, path: LibraryPath) -> Self {
158 Self {
159 span: Default::default(),
160 docs: None,
161 path,
162 kind,
163 constants: Default::default(),
164 types: Default::default(),
165 imports: Default::default(),
166 procedures: Default::default(),
167 advice_map: Default::default(),
168 }
169 }
170
171 pub fn new_kernel() -> Self {
173 Self::new(ModuleKind::Kernel, LibraryNamespace::Kernel.into())
174 }
175
176 pub fn new_executable() -> Self {
178 Self::new(ModuleKind::Executable, LibraryNamespace::Exec.into())
179 }
180
181 pub fn with_span(mut self, span: SourceSpan) -> Self {
184 self.span = span;
185 self
186 }
187
188 pub fn set_path(&mut self, path: LibraryPath) {
190 self.path = path;
191 }
192
193 pub fn set_namespace(&mut self, ns: LibraryNamespace) {
195 self.path.set_namespace(ns);
196 }
197
198 pub fn set_docs(&mut self, docs: Option<Span<String>>) {
200 self.docs = docs.map(DocString::new);
201 }
202
203 pub fn set_span(&mut self, span: SourceSpan) {
205 self.span = span;
206 }
207
208 pub fn define_constant(&mut self, constant: Constant) -> Result<(), SemanticAnalysisError> {
210 for c in self.constants.iter() {
211 if c.name == constant.name {
212 return Err(SemanticAnalysisError::SymbolConflict {
213 span: constant.span,
214 prev_span: c.span,
215 });
216 }
217 }
218 self.constants.push(constant);
219 Ok(())
220 }
221
222 pub fn define_type(&mut self, ty: TypeAlias) -> Result<(), SemanticAnalysisError> {
224 for t in self.types.iter() {
225 if t.name() == &ty.name {
226 return Err(SemanticAnalysisError::SymbolConflict {
227 span: ty.span(),
228 prev_span: t.span(),
229 });
230 }
231 }
232 self.types.push(ty.into());
233 Ok(())
234 }
235
236 pub fn define_enum(&mut self, ty: EnumType) -> Result<(), SemanticAnalysisError> {
246 if !ty.ty().is_integer() {
247 return Err(SemanticAnalysisError::InvalidEnumRepr { span: ty.span() });
248 }
249
250 if let Some(prev) = self.types.iter().find(|t| t.name() == ty.name()) {
251 return Err(SemanticAnalysisError::SymbolConflict {
252 span: ty.span(),
253 prev_span: prev.span(),
254 });
255 }
256
257 for variant in ty.variants() {
258 let Variant { span, docs, name, discriminant } = variant;
259 self.define_constant(Constant {
260 span: *span,
261 docs: docs.clone(),
262 name: name.clone(),
263 value: discriminant.clone(),
264 })?;
265 }
266
267 self.types.push(ty.into());
268
269 Ok(())
270 }
271
272 pub fn define_procedure(&mut self, export: Export) -> Result<(), SemanticAnalysisError> {
275 if self.is_kernel() && matches!(export, Export::Alias(_)) {
276 return Err(SemanticAnalysisError::ReexportFromKernel { span: export.span() });
277 }
278 if let Some(prev) = self.resolve(export.name()) {
279 let prev_span = prev.span();
280 Err(SemanticAnalysisError::SymbolConflict { span: export.span(), prev_span })
281 } else {
282 self.procedures.push(export);
283 Ok(())
284 }
285 }
286
287 pub fn define_import(&mut self, import: Import) -> Result<(), SemanticAnalysisError> {
290 if let Some(prev_import) = self.resolve_import(&import.name) {
291 let prev_span = prev_import.span;
292 return Err(SemanticAnalysisError::ImportConflict { span: import.span, prev_span });
293 }
294
295 if let Some(prev_defined) = self.procedures.iter().find(|e| e.name().eq(&import.name)) {
296 let prev_span = prev_defined.span();
297 return Err(SemanticAnalysisError::SymbolConflict { span: import.span, prev_span });
298 }
299
300 self.imports.push(import);
301
302 Ok(())
303 }
304}
305
306impl Module {
308 pub fn parse(
310 name: LibraryPath,
311 kind: ModuleKind,
312 source_file: Arc<SourceFile>,
313 ) -> Result<Box<Self>, Report> {
314 let mut parser = Self::parser(kind);
315 parser.parse(name, source_file)
316 }
317
318 pub fn parser(kind: ModuleKind) -> ModuleParser {
320 ModuleParser::new(kind)
321 }
322}
323
324impl Module {
326 pub fn name(&self) -> &str {
329 self.path.last()
330 }
331
332 pub fn path(&self) -> &LibraryPath {
334 &self.path
335 }
336
337 pub fn namespace(&self) -> &LibraryNamespace {
339 self.path.namespace()
340 }
341
342 pub fn is_in_namespace(&self, namespace: &LibraryNamespace) -> bool {
344 self.path.namespace() == namespace
345 }
346
347 pub fn docs(&self) -> Option<Span<&str>> {
350 self.docs.as_ref().map(|spanned| spanned.as_spanned_str())
351 }
352
353 pub fn kind(&self) -> ModuleKind {
357 self.kind
358 }
359
360 pub fn set_kind(&mut self, kind: ModuleKind) {
364 self.kind = kind;
365 }
366
367 #[inline(always)]
369 pub fn is_executable(&self) -> bool {
370 self.kind.is_executable()
371 }
372
373 #[inline(always)]
375 pub fn is_kernel(&self) -> bool {
376 self.kind.is_kernel() && self.path.is_kernel_path()
377 }
378
379 #[inline(always)]
381 pub fn is_in_kernel(&self) -> bool {
382 self.kind.is_kernel()
383 }
384
385 pub fn has_entrypoint(&self) -> bool {
388 self.index_of(|p| p.is_main()).is_some()
389 }
390
391 pub fn advice_map(&self) -> &AdviceMap {
393 &self.advice_map
394 }
395
396 pub fn constants(&self) -> core::slice::Iter<'_, Constant> {
398 self.constants.iter()
399 }
400
401 pub fn constants_mut(&mut self) -> core::slice::IterMut<'_, Constant> {
403 self.constants.iter_mut()
404 }
405
406 pub fn types(&self) -> core::slice::Iter<'_, TypeDecl> {
408 self.types.iter()
409 }
410
411 pub fn types_mut(&mut self) -> core::slice::IterMut<'_, TypeDecl> {
413 self.types.iter_mut()
414 }
415
416 pub fn procedures(&self) -> core::slice::Iter<'_, Export> {
421 self.procedures.iter()
422 }
423
424 pub fn procedures_mut(&mut self) -> core::slice::IterMut<'_, Export> {
426 self.procedures.iter_mut()
427 }
428
429 pub fn exported_procedures(
434 &self,
435 ) -> impl Iterator<Item = (ProcedureIndex, QualifiedProcedureName)> + '_ {
436 self.procedures.iter().enumerate().filter_map(|(proc_idx, p)| {
437 if !p.visibility().is_exported() {
439 return None;
440 }
441
442 let proc_idx = ProcedureIndex::new(proc_idx);
443 let fqn = QualifiedProcedureName::new(self.path().clone(), p.name().clone());
444
445 Some((proc_idx, fqn))
446 })
447 }
448
449 pub fn procedure_signature(&self, id: ProcedureIndex) -> Option<&FunctionType> {
451 self.procedures[id.as_usize()].signature()
452 }
453
454 pub fn imports(&self) -> core::slice::Iter<'_, Import> {
458 self.imports.iter()
459 }
460
461 pub fn imports_mut(&mut self) -> core::slice::IterMut<'_, Import> {
463 self.imports.iter_mut()
464 }
465
466 pub fn dependencies(&self) -> impl Iterator<Item = &LibraryNamespace> {
473 self.import_paths().map(|import| import.namespace())
474 }
475
476 pub fn get(&self, index: ProcedureIndex) -> Option<&Export> {
481 self.procedures.get(index.as_usize())
482 }
483
484 pub fn index_of<F>(&self, predicate: F) -> Option<ProcedureIndex>
487 where
488 F: FnMut(&Export) -> bool,
489 {
490 self.procedures.iter().position(predicate).map(ProcedureIndex::new)
491 }
492
493 pub fn index_of_name(&self, name: &ProcedureName) -> Option<ProcedureIndex> {
498 self.index_of(|p| p.name() == name && p.visibility().is_exported())
499 }
500
501 pub fn resolve(&self, name: &ProcedureName) -> Option<ResolvedProcedure> {
503 let index =
504 self.procedures.iter().position(|p| p.name() == name).map(ProcedureIndex::new)?;
505 match &self.procedures[index.as_usize()] {
506 Export::Procedure(proc) => {
507 Some(ResolvedProcedure::Local(Span::new(proc.name().span(), index)))
508 },
509 Export::Alias(alias) => match alias.target() {
510 AliasTarget::MastRoot(digest) => Some(ResolvedProcedure::MastRoot(**digest)),
511 AliasTarget::ProcedurePath(path) | AliasTarget::AbsoluteProcedurePath(path) => {
512 Some(ResolvedProcedure::External(path.clone()))
513 },
514 },
515 }
516 }
517
518 pub fn resolver(&self) -> LocalNameResolver {
520 LocalNameResolver::from_iter(self.procedures.iter().enumerate().map(|(i, p)| match p {
521 Export::Procedure(p) => (
522 p.name().clone(),
523 ResolvedProcedure::Local(Span::new(p.name().span(), ProcedureIndex::new(i))),
524 ),
525 Export::Alias(p) => {
526 let target = match p.target() {
527 AliasTarget::MastRoot(digest) => ResolvedProcedure::MastRoot(**digest),
528 AliasTarget::ProcedurePath(path) | AliasTarget::AbsoluteProcedurePath(path) => {
529 ResolvedProcedure::External(path.clone())
530 },
531 };
532 (p.name().clone(), target)
533 },
534 }))
535 .with_imports(
536 self.imports
537 .iter()
538 .map(|import| (import.name.clone(), Span::new(import.span(), import.path.clone()))),
539 )
540 }
541
542 pub fn resolve_import(&self, module_name: &Ident) -> Option<&Import> {
544 self.imports.iter().find(|import| &import.name == module_name)
545 }
546
547 pub fn resolve_import_mut(&mut self, module_name: &Ident) -> Option<&mut Import> {
549 self.imports.iter_mut().find(|import| &import.name == module_name)
550 }
551
552 pub fn import_paths(&self) -> impl Iterator<Item = &LibraryPath> + '_ {
554 self.imports.iter().map(|import| &import.path)
555 }
556}
557
558impl core::ops::Index<ProcedureIndex> for Module {
559 type Output = Export;
560
561 #[inline]
562 fn index(&self, index: ProcedureIndex) -> &Self::Output {
563 &self.procedures[index.as_usize()]
564 }
565}
566
567impl core::ops::IndexMut<ProcedureIndex> for Module {
568 #[inline]
569 fn index_mut(&mut self, index: ProcedureIndex) -> &mut Self::Output {
570 &mut self.procedures[index.as_usize()]
571 }
572}
573
574impl Spanned for Module {
575 fn span(&self) -> SourceSpan {
576 self.span
577 }
578}
579
580impl Eq for Module {}
581
582impl PartialEq for Module {
583 fn eq(&self, other: &Self) -> bool {
584 self.kind == other.kind
585 && self.path == other.path
586 && self.docs == other.docs
587 && self.constants == other.constants
588 && self.types == other.types
589 && self.imports == other.imports
590 && self.procedures == other.procedures
591 }
592}
593
594impl fmt::Debug for Module {
596 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
597 f.debug_struct("Module")
598 .field("docs", &self.docs)
599 .field("path", &self.path)
600 .field("kind", &self.kind)
601 .field("constants", &self.constants)
602 .field("types", &self.types)
603 .field("imports", &self.imports)
604 .field("procedures", &self.procedures)
605 .finish()
606 }
607}
608
609impl fmt::Display for Module {
613 #[inline]
618 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
619 use crate::prettier::PrettyPrint;
620
621 self.pretty_print(f)
622 }
623}
624
625impl crate::prettier::PrettyPrint for Module {
627 fn render(&self) -> crate::prettier::Document {
628 use crate::prettier::*;
629
630 let mut doc = self
631 .docs
632 .as_ref()
633 .map(|docstring| docstring.render() + nl())
634 .unwrap_or(Document::Empty);
635
636 for (i, import) in self.imports.iter().enumerate() {
637 if i > 0 {
638 doc += nl();
639 }
640 doc += import.render();
641 }
642
643 if !self.imports.is_empty() {
644 doc += nl();
645 }
646
647 for (i, constant) in self.constants.iter().enumerate() {
648 if i > 0 {
649 doc += nl();
650 }
651 doc += constant.render();
652 }
653
654 if !self.constants.is_empty() {
655 doc += nl();
656 }
657
658 let mut export_index = 0;
659 for export in self.procedures.iter() {
660 if export.is_main() {
661 continue;
662 }
663 if export_index > 0 {
664 doc += nl();
665 }
666 doc += export.render();
667 export_index += 1;
668 }
669
670 if let Some(main) = self.procedures().find(|p| p.is_main()) {
671 if export_index > 0 {
672 doc += nl();
673 }
674 doc += main.render();
675 }
676
677 doc
678 }
679}