miden_assembly_syntax/ast/procedure/
procedure.rs1use alloc::{collections::BTreeSet, string::String};
2use core::fmt;
3
4use miden_debug_types::{SourceSpan, Span, Spanned};
5use midenc_hir_type::FunctionType;
6
7use super::ProcedureName;
8use crate::ast::{Attribute, AttributeSet, Block, DocString, Invoke};
9
10#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
15#[repr(u8)]
16pub enum Visibility {
17 Public = 0,
19 Syscall = 1,
21 #[default]
23 Private = 2,
24}
25
26impl fmt::Display for Visibility {
27 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28 if self.is_exported() {
29 f.write_str("export")
30 } else {
31 f.write_str("proc")
32 }
33 }
34}
35
36impl Visibility {
37 pub fn is_exported(&self) -> bool {
39 matches!(self, Self::Public | Self::Syscall)
40 }
41
42 pub fn is_syscall(&self) -> bool {
44 matches!(self, Self::Syscall)
45 }
46}
47
48#[derive(Clone)]
53pub struct Procedure {
54 span: SourceSpan,
56 docs: Option<DocString>,
58 attrs: AttributeSet,
60 name: ProcedureName,
62 visibility: Visibility,
64 ty: Option<FunctionType>,
66 num_locals: u16,
68 body: Block,
70 pub(super) invoked: BTreeSet<Invoke>,
72}
73
74impl Procedure {
76 pub fn new(
79 span: SourceSpan,
80 visibility: Visibility,
81 name: ProcedureName,
82 num_locals: u16,
83 body: Block,
84 ) -> Self {
85 Self {
86 span,
87 docs: None,
88 attrs: Default::default(),
89 name,
90 visibility,
91 ty: None,
92 num_locals,
93 invoked: Default::default(),
94 body,
95 }
96 }
97
98 pub fn with_signature(mut self, ty: FunctionType) -> Self {
100 self.ty = Some(ty);
101 self
102 }
103
104 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
106 self.docs = docs.map(DocString::new);
107 self
108 }
109
110 pub fn with_attributes<I>(mut self, attrs: I) -> Self
112 where
113 I: IntoIterator<Item = Attribute>,
114 {
115 self.attrs.extend(attrs);
116 self
117 }
118
119 pub fn set_visibility(&mut self, visibility: Visibility) {
121 self.visibility = visibility;
122 }
123
124 pub fn set_signature(&mut self, signature: FunctionType) {
126 self.ty = Some(signature);
127 }
128}
129
130impl Procedure {
132 pub fn name(&self) -> &ProcedureName {
134 &self.name
135 }
136
137 pub fn visibility(&self) -> Visibility {
139 self.visibility
140 }
141
142 pub fn signature(&self) -> Option<&FunctionType> {
144 self.ty.as_ref()
145 }
146
147 pub fn num_locals(&self) -> u16 {
149 self.num_locals
150 }
151
152 pub fn is_entrypoint(&self) -> bool {
155 self.name.is_main()
156 }
157
158 pub fn docs(&self) -> Option<Span<&str>> {
160 self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
161 }
162
163 #[inline]
165 pub fn attributes(&self) -> &AttributeSet {
166 &self.attrs
167 }
168
169 #[inline]
171 pub fn attributes_mut(&mut self) -> &mut AttributeSet {
172 &mut self.attrs
173 }
174
175 #[inline]
177 pub fn has_attribute(&self, name: impl AsRef<str>) -> bool {
178 self.attrs.has(name)
179 }
180
181 #[inline]
183 pub fn get_attribute(&self, name: impl AsRef<str>) -> Option<&Attribute> {
184 self.attrs.get(name)
185 }
186
187 pub fn body(&self) -> &Block {
189 &self.body
190 }
191
192 pub fn body_mut(&mut self) -> &mut Block {
194 &mut self.body
195 }
196
197 pub fn iter(&self) -> core::slice::Iter<'_, crate::ast::Op> {
199 self.body.iter()
200 }
201
202 pub fn invoked<'a, 'b: 'a>(&'b self) -> impl Iterator<Item = &'a Invoke> + 'a {
205 if self.invoked.is_empty() {
206 InvokedIter::Empty
207 } else {
208 InvokedIter::NonEmpty(self.invoked.iter())
209 }
210 }
211
212 pub fn extend_invoked<I>(&mut self, iter: I)
217 where
218 I: IntoIterator<Item = Invoke>,
219 {
220 self.invoked.extend(iter);
221 }
222}
223
224#[doc(hidden)]
225pub(crate) enum InvokedIter<'a, I: Iterator<Item = &'a Invoke> + 'a> {
226 Empty,
227 NonEmpty(I),
228}
229
230impl<'a, I> Iterator for InvokedIter<'a, I>
231where
232 I: Iterator<Item = &'a Invoke> + 'a,
233{
234 type Item = <I as Iterator>::Item;
235
236 fn next(&mut self) -> Option<Self::Item> {
237 match self {
238 Self::Empty => None,
239 Self::NonEmpty(iter) => {
240 let result = iter.next();
241 if result.is_none() {
242 *self = Self::Empty;
243 }
244 result
245 },
246 }
247 }
248}
249
250impl Spanned for Procedure {
251 fn span(&self) -> SourceSpan {
252 self.span
253 }
254}
255
256impl crate::prettier::PrettyPrint for Procedure {
257 fn render(&self) -> crate::prettier::Document {
258 use crate::prettier::*;
259
260 let mut doc = self
261 .docs
262 .as_ref()
263 .map(|docstring| docstring.render())
264 .unwrap_or(Document::Empty);
265
266 if !self.attrs.is_empty() {
267 doc += self
268 .attrs
269 .iter()
270 .map(|attr| attr.render())
271 .reduce(|acc, attr| acc + nl() + attr)
272 .unwrap_or(Document::Empty);
273 }
274
275 if self.is_entrypoint() {
276 doc += const_text("begin");
277 } else {
278 doc += display(self.visibility) + const_text(".") + display(&self.name);
279 if self.num_locals > 0 {
280 doc += const_text(".") + display(self.num_locals);
281 }
282 }
283
284 doc + self.body.render() + const_text("end") + nl()
285 }
286}
287
288impl fmt::Debug for Procedure {
289 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290 f.debug_struct("Procedure")
291 .field("docs", &self.docs)
292 .field("attrs", &self.attrs)
293 .field("name", &self.name)
294 .field("visibility", &self.visibility)
295 .field("num_locals", &self.num_locals)
296 .field("ty", &self.ty)
297 .field("body", &self.body)
298 .field("invoked", &self.invoked)
299 .finish()
300 }
301}
302
303impl Eq for Procedure {}
304
305impl PartialEq for Procedure {
306 fn eq(&self, other: &Self) -> bool {
307 self.name == other.name
308 && self.visibility == other.visibility
309 && self.num_locals == other.num_locals
310 && self.ty == other.ty
311 && self.body == other.body
312 && self.attrs == other.attrs
313 && self.docs == other.docs
314 }
315}