1use 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 smallvec::SmallVec;
11
12use super::{
13 Constant, DocString, EnumType, Export, FunctionType, Import, LocalNameResolver, ProcedureIndex,
14 ProcedureName, QualifiedProcedureName, ResolvedProcedure, TypeAlias, TypeDecl, Variant,
15};
16use crate::{
17 LibraryNamespace, LibraryPath,
18 ast::{self, AliasTarget, Ident, types},
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 let repr = ty.ty().clone();
247
248 if !repr.is_integer() {
249 return Err(SemanticAnalysisError::InvalidEnumRepr { span: ty.span() });
250 }
251
252 let (alias, variants) = ty.into_parts();
253
254 if let Some(prev) = self.types.iter().find(|t| t.name() == &alias.name) {
255 return Err(SemanticAnalysisError::SymbolConflict {
256 span: alias.span(),
257 prev_span: prev.span(),
258 });
259 }
260
261 let mut values = SmallVec::<[Span<u64>; 8]>::new_const();
262
263 for variant in variants {
264 let value = match &variant.discriminant {
266 ast::ConstantExpr::Int(value) => (*value).map(|v| v.as_int()),
267 expr => {
268 return Err(SemanticAnalysisError::InvalidEnumDiscriminant {
269 span: expr.span(),
270 repr,
271 });
272 },
273 };
274 if let Some(prev) = values.iter().find(|v| *v == &value) {
275 return Err(SemanticAnalysisError::EnumDiscriminantConflict {
276 span: value.span(),
277 prev: prev.span(),
278 });
279 } else {
280 values.push(value);
281 }
282
283 variant.assert_instance_of(&repr)?;
285
286 let Variant { span, docs, name, discriminant } = variant;
287
288 self.define_constant(Constant { span, docs, name, value: discriminant })?;
289 }
290
291 self.types.push(alias.into());
292
293 Ok(())
294 }
295
296 pub fn define_procedure(&mut self, export: Export) -> Result<(), SemanticAnalysisError> {
299 if self.is_kernel() && matches!(export, Export::Alias(_)) {
300 return Err(SemanticAnalysisError::ReexportFromKernel { span: export.span() });
301 }
302 if let Some(prev) = self.resolve(export.name()) {
303 let prev_span = prev.span();
304 Err(SemanticAnalysisError::SymbolConflict { span: export.span(), prev_span })
305 } else {
306 self.procedures.push(export);
307 Ok(())
308 }
309 }
310
311 pub fn define_import(&mut self, import: Import) -> Result<(), SemanticAnalysisError> {
314 if let Some(prev_import) = self.resolve_import(&import.name) {
315 let prev_span = prev_import.span;
316 return Err(SemanticAnalysisError::ImportConflict { span: import.span, prev_span });
317 }
318
319 if let Some(prev_defined) = self.procedures.iter().find(|e| e.name().eq(&import.name)) {
320 let prev_span = prev_defined.span();
321 return Err(SemanticAnalysisError::SymbolConflict { span: import.span, prev_span });
322 }
323
324 self.imports.push(import);
325
326 Ok(())
327 }
328}
329
330impl Module {
332 pub fn parse(
334 name: LibraryPath,
335 kind: ModuleKind,
336 source_file: Arc<SourceFile>,
337 ) -> Result<Box<Self>, Report> {
338 let mut parser = Self::parser(kind);
339 parser.parse(name, source_file)
340 }
341
342 pub fn parser(kind: ModuleKind) -> ModuleParser {
344 ModuleParser::new(kind)
345 }
346}
347
348impl Module {
350 pub fn name(&self) -> &str {
353 self.path.last()
354 }
355
356 pub fn path(&self) -> &LibraryPath {
358 &self.path
359 }
360
361 pub fn namespace(&self) -> &LibraryNamespace {
363 self.path.namespace()
364 }
365
366 pub fn is_in_namespace(&self, namespace: &LibraryNamespace) -> bool {
368 self.path.namespace() == namespace
369 }
370
371 pub fn docs(&self) -> Option<Span<&str>> {
374 self.docs.as_ref().map(|spanned| spanned.as_spanned_str())
375 }
376
377 pub fn kind(&self) -> ModuleKind {
381 self.kind
382 }
383
384 pub fn set_kind(&mut self, kind: ModuleKind) {
388 self.kind = kind;
389 }
390
391 #[inline(always)]
393 pub fn is_executable(&self) -> bool {
394 self.kind.is_executable()
395 }
396
397 #[inline(always)]
399 pub fn is_kernel(&self) -> bool {
400 self.kind.is_kernel() && self.path.is_kernel_path()
401 }
402
403 #[inline(always)]
405 pub fn is_in_kernel(&self) -> bool {
406 self.kind.is_kernel()
407 }
408
409 pub fn has_entrypoint(&self) -> bool {
412 self.index_of(|p| p.is_main()).is_some()
413 }
414
415 pub fn advice_map(&self) -> &AdviceMap {
417 &self.advice_map
418 }
419
420 pub fn constants(&self) -> core::slice::Iter<'_, Constant> {
422 self.constants.iter()
423 }
424
425 pub fn constants_mut(&mut self) -> core::slice::IterMut<'_, Constant> {
427 self.constants.iter_mut()
428 }
429
430 pub fn types(&self) -> core::slice::Iter<'_, TypeDecl> {
432 self.types.iter()
433 }
434
435 pub fn types_mut(&mut self) -> core::slice::IterMut<'_, TypeDecl> {
437 self.types.iter_mut()
438 }
439
440 pub fn procedures(&self) -> core::slice::Iter<'_, Export> {
445 self.procedures.iter()
446 }
447
448 pub fn procedures_mut(&mut self) -> core::slice::IterMut<'_, Export> {
450 self.procedures.iter_mut()
451 }
452
453 pub fn exported_procedures(
458 &self,
459 ) -> impl Iterator<Item = (ProcedureIndex, QualifiedProcedureName)> + '_ {
460 self.procedures.iter().enumerate().filter_map(|(proc_idx, p)| {
461 if !p.visibility().is_exported() {
463 return None;
464 }
465
466 let proc_idx = ProcedureIndex::new(proc_idx);
467 let fqn = QualifiedProcedureName::new(self.path().clone(), p.name().clone());
468
469 Some((proc_idx, fqn))
470 })
471 }
472
473 pub fn procedure_signature(&self, id: ProcedureIndex) -> Option<&FunctionType> {
475 self.procedures[id.as_usize()].signature()
476 }
477
478 pub fn imports(&self) -> core::slice::Iter<'_, Import> {
482 self.imports.iter()
483 }
484
485 pub fn imports_mut(&mut self) -> core::slice::IterMut<'_, Import> {
487 self.imports.iter_mut()
488 }
489
490 pub fn dependencies(&self) -> impl Iterator<Item = &LibraryNamespace> {
497 self.import_paths().map(|import| import.namespace())
498 }
499
500 pub fn get(&self, index: ProcedureIndex) -> Option<&Export> {
505 self.procedures.get(index.as_usize())
506 }
507
508 pub fn index_of<F>(&self, predicate: F) -> Option<ProcedureIndex>
511 where
512 F: FnMut(&Export) -> bool,
513 {
514 self.procedures.iter().position(predicate).map(ProcedureIndex::new)
515 }
516
517 pub fn index_of_name(&self, name: &ProcedureName) -> Option<ProcedureIndex> {
522 self.index_of(|p| p.name() == name && p.visibility().is_exported())
523 }
524
525 pub fn resolve(&self, name: &ProcedureName) -> Option<ResolvedProcedure> {
527 let index =
528 self.procedures.iter().position(|p| p.name() == name).map(ProcedureIndex::new)?;
529 match &self.procedures[index.as_usize()] {
530 Export::Procedure(proc) => {
531 Some(ResolvedProcedure::Local(Span::new(proc.name().span(), index)))
532 },
533 Export::Alias(alias) => match alias.target() {
534 AliasTarget::MastRoot(digest) => Some(ResolvedProcedure::MastRoot(**digest)),
535 AliasTarget::ProcedurePath(path) | AliasTarget::AbsoluteProcedurePath(path) => {
536 Some(ResolvedProcedure::External(path.clone()))
537 },
538 },
539 }
540 }
541
542 pub fn resolver(&self) -> LocalNameResolver {
544 LocalNameResolver::from_iter(self.procedures.iter().enumerate().map(|(i, p)| match p {
545 Export::Procedure(p) => (
546 p.name().clone(),
547 ResolvedProcedure::Local(Span::new(p.name().span(), ProcedureIndex::new(i))),
548 ),
549 Export::Alias(p) => {
550 let target = match p.target() {
551 AliasTarget::MastRoot(digest) => ResolvedProcedure::MastRoot(**digest),
552 AliasTarget::ProcedurePath(path) | AliasTarget::AbsoluteProcedurePath(path) => {
553 ResolvedProcedure::External(path.clone())
554 },
555 };
556 (p.name().clone(), target)
557 },
558 }))
559 .with_imports(
560 self.imports
561 .iter()
562 .map(|import| (import.name.clone(), Span::new(import.span(), import.path.clone()))),
563 )
564 }
565
566 pub fn resolve_import(&self, module_name: &Ident) -> Option<&Import> {
568 self.imports.iter().find(|import| &import.name == module_name)
569 }
570
571 pub fn resolve_import_mut(&mut self, module_name: &Ident) -> Option<&mut Import> {
573 self.imports.iter_mut().find(|import| &import.name == module_name)
574 }
575
576 pub fn import_paths(&self) -> impl Iterator<Item = &LibraryPath> + '_ {
578 self.imports.iter().map(|import| &import.path)
579 }
580
581 pub fn resolve_type(&self, ty: &ast::TypeExpr) -> Option<types::Type> {
583 match ty {
584 ast::TypeExpr::Ref(name) => match self.types.iter().find(|t| t.name() == name)? {
585 ast::TypeDecl::Alias(alias) => self.resolve_type(&alias.ty),
586 ast::TypeDecl::Enum(enum_ty) => Some(enum_ty.ty().clone()),
587 },
588 ast::TypeExpr::Primitive(t) => Some(t.inner().clone()),
589 ast::TypeExpr::Array(t) => {
590 let elem = self.resolve_type(&t.elem)?;
591 Some(types::Type::Array(Arc::new(types::ArrayType::new(elem, t.arity))))
592 },
593 ast::TypeExpr::Ptr(ty) => {
594 let pointee = self.resolve_type(&ty.pointee)?;
595 Some(types::Type::Ptr(Arc::new(types::PointerType::new(pointee))))
596 },
597 ast::TypeExpr::Struct(t) => {
598 let mut fields = Vec::with_capacity(t.fields.len());
599 for field in t.fields.iter() {
600 let field_ty = self.resolve_type(&field.ty)?;
601 fields.push(field_ty);
602 }
603 Some(types::Type::Struct(Arc::new(types::StructType::new(fields))))
604 },
605 }
606 }
607}
608
609impl core::ops::Index<ProcedureIndex> for Module {
610 type Output = Export;
611
612 #[inline]
613 fn index(&self, index: ProcedureIndex) -> &Self::Output {
614 &self.procedures[index.as_usize()]
615 }
616}
617
618impl core::ops::IndexMut<ProcedureIndex> for Module {
619 #[inline]
620 fn index_mut(&mut self, index: ProcedureIndex) -> &mut Self::Output {
621 &mut self.procedures[index.as_usize()]
622 }
623}
624
625impl Spanned for Module {
626 fn span(&self) -> SourceSpan {
627 self.span
628 }
629}
630
631impl Eq for Module {}
632
633impl PartialEq for Module {
634 fn eq(&self, other: &Self) -> bool {
635 self.kind == other.kind
636 && self.path == other.path
637 && self.docs == other.docs
638 && self.constants == other.constants
639 && self.types == other.types
640 && self.imports == other.imports
641 && self.procedures == other.procedures
642 }
643}
644
645impl fmt::Debug for Module {
647 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
648 f.debug_struct("Module")
649 .field("docs", &self.docs)
650 .field("path", &self.path)
651 .field("kind", &self.kind)
652 .field("constants", &self.constants)
653 .field("types", &self.types)
654 .field("imports", &self.imports)
655 .field("procedures", &self.procedures)
656 .finish()
657 }
658}
659
660impl fmt::Display for Module {
664 #[inline]
669 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670 use crate::prettier::PrettyPrint;
671
672 self.pretty_print(f)
673 }
674}
675
676impl crate::prettier::PrettyPrint for Module {
678 fn render(&self) -> crate::prettier::Document {
679 use crate::prettier::*;
680
681 let mut doc = self
682 .docs
683 .as_ref()
684 .map(|docstring| docstring.render() + nl())
685 .unwrap_or(Document::Empty);
686
687 for (i, import) in self.imports.iter().enumerate() {
688 if i > 0 {
689 doc += nl();
690 }
691 doc += import.render();
692 }
693
694 if !self.imports.is_empty() {
695 doc += nl();
696 }
697
698 for (i, constant) in self.constants.iter().enumerate() {
699 if i > 0 {
700 doc += nl();
701 }
702 doc += constant.render();
703 }
704
705 if !self.constants.is_empty() {
706 doc += nl();
707 }
708
709 let mut export_index = 0;
710 for export in self.procedures.iter() {
711 if export.is_main() {
712 continue;
713 }
714 if export_index > 0 {
715 doc += nl();
716 }
717 doc += export.render();
718 export_index += 1;
719 }
720
721 if let Some(main) = self.procedures().find(|p| p.is_main()) {
722 if export_index > 0 {
723 doc += nl();
724 }
725 doc += main.render();
726 }
727
728 doc
729 }
730}