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) {
128 assert!(
129 num_locals == 0 || !self.is_entrypoint(),
130 "program entrypoint cannot have locals"
131 );
132 self.num_locals = num_locals;
133 }
134}
135
136impl Procedure {
138 pub fn name(&self) -> &ProcedureName {
140 &self.name
141 }
142
143 pub fn visibility(&self) -> Visibility {
145 self.visibility
146 }
147
148 pub fn is_syscall(&self) -> bool {
150 self.syscall
151 }
152
153 pub fn signature(&self) -> Option<&FunctionType> {
155 self.ty.as_ref()
156 }
157
158 pub fn signature_mut(&mut self) -> Option<&mut FunctionType> {
160 self.ty.as_mut()
161 }
162
163 pub fn num_locals(&self) -> u16 {
165 self.num_locals
166 }
167
168 pub fn is_entrypoint(&self) -> bool {
171 self.name.is_main()
172 }
173
174 pub fn docs(&self) -> Option<Span<&str>> {
176 self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
177 }
178
179 #[inline]
181 pub fn attributes(&self) -> &AttributeSet {
182 &self.attrs
183 }
184
185 #[inline]
187 pub fn attributes_mut(&mut self) -> &mut AttributeSet {
188 &mut self.attrs
189 }
190
191 #[inline]
193 pub fn has_attribute(&self, name: impl AsRef<str>) -> bool {
194 self.attrs.has(name)
195 }
196
197 #[inline]
199 pub fn get_attribute(&self, name: impl AsRef<str>) -> Option<&Attribute> {
200 self.attrs.get(name)
201 }
202
203 pub fn body(&self) -> &Block {
205 &self.body
206 }
207
208 pub fn body_mut(&mut self) -> &mut Block {
210 &mut self.body
211 }
212
213 pub fn iter(&self) -> core::slice::Iter<'_, crate::ast::Op> {
215 self.body.iter()
216 }
217
218 pub fn invoked<'a, 'b: 'a>(&'b self) -> impl Iterator<Item = &'a Invoke> + 'a {
221 if self.invoked.is_empty() {
222 InvokedIter::Empty
223 } else {
224 InvokedIter::NonEmpty(self.invoked.iter())
225 }
226 }
227
228 pub fn extend_invoked<I>(&mut self, iter: I)
233 where
234 I: IntoIterator<Item = Invoke>,
235 {
236 self.invoked.extend(iter);
237 }
238}
239
240#[doc(hidden)]
241pub(crate) enum InvokedIter<'a, I: Iterator<Item = &'a Invoke> + 'a> {
242 Empty,
243 NonEmpty(I),
244}
245
246impl<'a, I> Iterator for InvokedIter<'a, I>
247where
248 I: Iterator<Item = &'a Invoke> + 'a,
249{
250 type Item = <I as Iterator>::Item;
251
252 fn next(&mut self) -> Option<Self::Item> {
253 match self {
254 Self::Empty => None,
255 Self::NonEmpty(iter) => {
256 let result = iter.next();
257 if result.is_none() {
258 *self = Self::Empty;
259 }
260 result
261 },
262 }
263 }
264}
265
266impl Spanned for Procedure {
267 fn span(&self) -> SourceSpan {
268 self.span
269 }
270}
271
272impl crate::prettier::PrettyPrint for Procedure {
273 fn render(&self) -> crate::prettier::Document {
274 use crate::prettier::*;
275
276 let mut doc = self.docs.as_ref().map(PrettyPrint::render).unwrap_or(Document::Empty);
277
278 if !self.attrs.is_empty() {
279 doc += self
280 .attrs
281 .iter()
282 .map(PrettyPrint::render)
283 .reduce(|acc, attr| acc + nl() + attr)
284 .unwrap_or(Document::Empty);
285 }
286
287 if self.is_entrypoint() {
288 doc += const_text("begin");
289 } else {
290 if self.num_locals > 0 {
291 doc += text(format!("@locals(\"{}\")", self.num_locals)) + nl();
292 }
293 match self.signature() {
294 Some(sig) if sig.cc != crate::ast::types::CallConv::Fast => {
295 doc += text(format!("@callconv(\"{}\")", sig.cc)) + nl();
296 },
297 _ => (),
298 }
299 if self.visibility.is_public() {
300 doc += display(self.visibility) + const_text(" ");
301 }
302 doc += const_text("proc") + const_text(" ") + display(self.name.as_ident());
303 if let Some(sig) = self.signature() {
304 doc += sig.render();
305 }
306 }
307
308 doc + self.body.render() + const_text("end") + nl()
309 }
310}
311
312impl fmt::Debug for Procedure {
313 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314 f.debug_struct("Procedure")
315 .field("docs", &self.docs)
316 .field("attrs", &self.attrs)
317 .field("name", &self.name)
318 .field("visibility", &self.visibility)
319 .field("syscall", &self.syscall)
320 .field("num_locals", &self.num_locals)
321 .field("ty", &self.ty)
322 .field("body", &self.body)
323 .field("invoked", &self.invoked)
324 .finish()
325 }
326}
327
328impl Eq for Procedure {}
329
330impl PartialEq for Procedure {
331 fn eq(&self, other: &Self) -> bool {
332 self.name == other.name
333 && self.visibility == other.visibility
334 && self.syscall == other.syscall
335 && self.num_locals == other.num_locals
336 && self.ty == other.ty
337 && self.body == other.body
338 && self.attrs == other.attrs
339 && self.docs == other.docs
340 }
341}