miden_assembly_syntax/ast/procedure/
procedure.rs1use alloc::{collections::BTreeSet, string::String};
2use core::fmt;
3
4use miden_debug_types::{SourceSpan, Span, Spanned};
5
6use super::ProcedureName;
7use crate::ast::{Attribute, AttributeSet, Block, DocString, FunctionType, Invoke, Visibility};
8
9#[derive(Clone)]
14pub struct Procedure {
15 span: SourceSpan,
17 docs: Option<DocString>,
19 attrs: AttributeSet,
21 name: ProcedureName,
23 visibility: Visibility,
25 syscall: bool,
27 ty: Option<FunctionType>,
29 num_locals: u16,
31 body: Block,
33 pub(crate) invoked: BTreeSet<Invoke>,
35}
36
37impl Procedure {
39 pub fn new(
42 span: SourceSpan,
43 visibility: Visibility,
44 name: ProcedureName,
45 num_locals: u16,
46 body: Block,
47 ) -> Self {
48 Self {
49 span,
50 docs: None,
51 attrs: Default::default(),
52 name,
53 visibility,
54 syscall: false,
55 ty: None,
56 num_locals,
57 invoked: Default::default(),
58 body,
59 }
60 }
61
62 pub fn new_syscall(
64 span: SourceSpan,
65 name: ProcedureName,
66 num_locals: u16,
67 body: Block,
68 ) -> Self {
69 Self {
70 span,
71 docs: None,
72 attrs: Default::default(),
73 name,
74 visibility: Visibility::Public,
75 syscall: true,
76 ty: None,
77 num_locals,
78 invoked: Default::default(),
79 body,
80 }
81 }
82
83 pub fn with_signature(mut self, ty: FunctionType) -> Self {
85 self.ty = Some(ty);
86 self
87 }
88
89 pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
91 self.docs = docs.map(DocString::new);
92 self
93 }
94
95 pub fn with_attributes<I>(mut self, attrs: I) -> Self
97 where
98 I: IntoIterator<Item = Attribute>,
99 {
100 self.attrs.extend(attrs);
101 self
102 }
103
104 pub fn set_visibility(&mut self, visibility: Visibility) {
106 self.visibility = visibility;
107 }
108
109 pub fn set_syscall(&mut self, yes: bool) {
111 self.syscall = yes;
112 }
113
114 pub fn set_signature(&mut self, signature: FunctionType) {
116 self.ty = Some(signature);
117 }
118
119 pub fn set_num_locals(&mut self, num_locals: u16) {
121 self.num_locals = num_locals;
122 }
123}
124
125impl Procedure {
127 pub fn name(&self) -> &ProcedureName {
129 &self.name
130 }
131
132 pub fn visibility(&self) -> Visibility {
134 self.visibility
135 }
136
137 pub fn is_syscall(&self) -> bool {
139 self.syscall
140 }
141
142 pub fn signature(&self) -> Option<&FunctionType> {
144 self.ty.as_ref()
145 }
146
147 pub fn signature_mut(&mut self) -> Option<&mut FunctionType> {
149 self.ty.as_mut()
150 }
151
152 pub fn num_locals(&self) -> u16 {
154 self.num_locals
155 }
156
157 pub fn is_entrypoint(&self) -> bool {
160 self.name.is_main()
161 }
162
163 pub fn docs(&self) -> Option<Span<&str>> {
165 self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
166 }
167
168 #[inline]
170 pub fn attributes(&self) -> &AttributeSet {
171 &self.attrs
172 }
173
174 #[inline]
176 pub fn attributes_mut(&mut self) -> &mut AttributeSet {
177 &mut self.attrs
178 }
179
180 #[inline]
182 pub fn has_attribute(&self, name: impl AsRef<str>) -> bool {
183 self.attrs.has(name)
184 }
185
186 #[inline]
188 pub fn get_attribute(&self, name: impl AsRef<str>) -> Option<&Attribute> {
189 self.attrs.get(name)
190 }
191
192 pub fn body(&self) -> &Block {
194 &self.body
195 }
196
197 pub fn body_mut(&mut self) -> &mut Block {
199 &mut self.body
200 }
201
202 pub fn iter(&self) -> core::slice::Iter<'_, crate::ast::Op> {
204 self.body.iter()
205 }
206
207 pub fn invoked<'a, 'b: 'a>(&'b self) -> impl Iterator<Item = &'a Invoke> + 'a {
210 if self.invoked.is_empty() {
211 InvokedIter::Empty
212 } else {
213 InvokedIter::NonEmpty(self.invoked.iter())
214 }
215 }
216
217 pub fn extend_invoked<I>(&mut self, iter: I)
222 where
223 I: IntoIterator<Item = Invoke>,
224 {
225 self.invoked.extend(iter);
226 }
227}
228
229#[doc(hidden)]
230pub(crate) enum InvokedIter<'a, I: Iterator<Item = &'a Invoke> + 'a> {
231 Empty,
232 NonEmpty(I),
233}
234
235impl<'a, I> Iterator for InvokedIter<'a, I>
236where
237 I: Iterator<Item = &'a Invoke> + 'a,
238{
239 type Item = <I as Iterator>::Item;
240
241 fn next(&mut self) -> Option<Self::Item> {
242 match self {
243 Self::Empty => None,
244 Self::NonEmpty(iter) => {
245 let result = iter.next();
246 if result.is_none() {
247 *self = Self::Empty;
248 }
249 result
250 },
251 }
252 }
253}
254
255impl Spanned for Procedure {
256 fn span(&self) -> SourceSpan {
257 self.span
258 }
259}
260
261impl crate::prettier::PrettyPrint for Procedure {
262 fn render(&self) -> crate::prettier::Document {
263 use crate::prettier::*;
264
265 let mut doc = self
266 .docs
267 .as_ref()
268 .map(|docstring| docstring.render())
269 .unwrap_or(Document::Empty);
270
271 if !self.attrs.is_empty() {
272 doc += self
273 .attrs
274 .iter()
275 .map(|attr| attr.render())
276 .reduce(|acc, attr| acc + nl() + attr)
277 .unwrap_or(Document::Empty);
278 }
279
280 if self.is_entrypoint() {
281 doc += const_text("begin");
282 } else {
283 if self.num_locals > 0 {
284 doc += text(format!("@locals(\"{}\")", &self.num_locals)) + nl();
285 }
286 match self.signature() {
287 Some(sig) if sig.cc != crate::ast::types::CallConv::Fast => {
288 doc += text(format!("@callconv(\"{}\")", &sig.cc)) + nl();
289 },
290 _ => (),
291 }
292 if self.visibility.is_public() {
293 doc += display(self.visibility) + const_text(" ");
294 }
295 doc += const_text("proc") + const_text(" ") + display(&self.name);
296 if let Some(sig) = self.signature() {
297 doc += sig.render();
298 }
299 }
300
301 doc + self.body.render() + const_text("end") + nl()
302 }
303}
304
305impl fmt::Debug for Procedure {
306 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
307 f.debug_struct("Procedure")
308 .field("docs", &self.docs)
309 .field("attrs", &self.attrs)
310 .field("name", &self.name)
311 .field("visibility", &self.visibility)
312 .field("syscall", &self.syscall)
313 .field("num_locals", &self.num_locals)
314 .field("ty", &self.ty)
315 .field("body", &self.body)
316 .field("invoked", &self.invoked)
317 .finish()
318 }
319}
320
321impl Eq for Procedure {}
322
323impl PartialEq for Procedure {
324 fn eq(&self, other: &Self) -> bool {
325 self.name == other.name
326 && self.visibility == other.visibility
327 && self.syscall == other.syscall
328 && self.num_locals == other.num_locals
329 && self.ty == other.ty
330 && self.body == other.body
331 && self.attrs == other.attrs
332 && self.docs == other.docs
333 }
334}