1extern crate proc_macro;
2use heck::ToUpperCamelCase;
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{
6 Expr, Ident, LitInt, Token, bracketed, parenthesized, parse::Parse, parse_macro_input,
7 spanned::Spanned,
8};
9
10#[derive(Clone)]
11enum StackItem {
12 Name(Ident),
13 NameCounted(Ident, Expr),
14 Unused(Expr),
16}
17
18#[derive(Clone)]
19struct StackEffect {
20 pops: Vec<StackItem>,
21 pushes: Vec<StackItem>,
22}
23
24#[derive(Clone)]
25struct Opcode {
26 name: Ident,
27 number: LitInt,
28 stack_effect: Option<StackEffect>,
29}
30
31struct Exception {
32 stack_effect: StackEffect,
33}
34
35impl Parse for Opcode {
36 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
37 let name: Ident = input.parse()?;
40 input.parse::<Token![=]>()?;
41 let number: LitInt = input.parse()?;
42
43 let inner_stack_effect;
44
45 parenthesized!(inner_stack_effect in input);
46
47 let mut stack_effect = StackEffect {
48 pops: vec![],
49 pushes: vec![],
50 };
51
52 if inner_stack_effect.parse::<Token![/]>().is_ok() {
53 return Ok(Opcode {
55 name,
56 number,
57 stack_effect: None,
58 });
59 }
60
61 while inner_stack_effect.peek(Ident) {
63 let name: Ident = inner_stack_effect.parse()?;
64
65 stack_effect.pops.push(
66 if name == "unused" {
68 if inner_stack_effect.peek(syn::token::Bracket) {
69 let inner_bracket;
70 bracketed!(inner_bracket in inner_stack_effect);
71 let size: Expr = inner_bracket.parse()?;
72 StackItem::Unused(size)
73 } else {
74 StackItem::Unused(Expr::Lit(syn::ExprLit {
75 attrs: vec![],
76 lit: syn::Lit::Int(LitInt::new(
77 "1",
78 proc_macro::Span::call_site().into(),
79 )),
80 }))
81 }
82 } else {
83 if inner_stack_effect.peek(syn::token::Bracket) {
84 let inner_bracket;
85 bracketed!(inner_bracket in inner_stack_effect);
86 let size: Expr = inner_bracket.parse()?;
87 StackItem::NameCounted(name, size)
88 } else {
89 StackItem::Name(name)
90 }
91 },
92 );
93
94 if inner_stack_effect.parse::<Token![,]>().is_err() {
95 break;
96 }
97 }
98
99 inner_stack_effect.parse::<Token![-]>()?;
100 inner_stack_effect.parse::<Token![-]>()?;
101
102 while inner_stack_effect.peek(Ident) {
103 let name: Ident = inner_stack_effect.parse()?;
104
105 stack_effect.pushes.push(
106 if name == "unused" {
108 if inner_stack_effect.peek(syn::token::Bracket) {
109 let inner_bracket;
110 bracketed!(inner_bracket in inner_stack_effect);
111 let size: Expr = inner_bracket.parse()?;
112 StackItem::Unused(size)
113 } else {
114 StackItem::Unused(Expr::Lit(syn::ExprLit {
115 attrs: vec![],
116 lit: syn::Lit::Int(LitInt::new(
117 "1",
118 proc_macro::Span::call_site().into(),
119 )),
120 }))
121 }
122 } else {
123 if inner_stack_effect.peek(syn::token::Bracket) {
124 let inner_bracket;
125 bracketed!(inner_bracket in inner_stack_effect);
126 let size: Expr = inner_bracket.parse()?;
127 StackItem::NameCounted(name, size)
128 } else {
129 StackItem::Name(name)
130 }
131 },
132 );
133
134 if inner_stack_effect.parse::<Token![,]>().is_err() {
135 break;
136 }
137 }
138
139 Ok(Opcode {
140 name,
141 number,
142 stack_effect: Some(stack_effect),
143 })
144 }
145}
146
147impl Parse for Exception {
148 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
149 input.parse::<Token![*]>()?;
153 syn::custom_keyword!(EXCEPTION);
154 input.parse::<EXCEPTION>()?;
155
156 let inner_stack_effect;
157
158 parenthesized!(inner_stack_effect in input);
159
160 let mut stack_effect = StackEffect {
161 pops: vec![],
162 pushes: vec![],
163 };
164
165 while inner_stack_effect.peek(Ident) {
167 let name: Ident = inner_stack_effect.parse()?;
168
169 stack_effect.pops.push(
170 if name == "unused" {
172 if inner_stack_effect.peek(syn::token::Bracket) {
173 let inner_bracket;
174 bracketed!(inner_bracket in inner_stack_effect);
175 let size: Expr = inner_bracket.parse()?;
176 StackItem::Unused(size)
177 } else {
178 StackItem::Unused(Expr::Lit(syn::ExprLit {
179 attrs: vec![],
180 lit: syn::Lit::Int(LitInt::new(
181 "1",
182 proc_macro::Span::call_site().into(),
183 )),
184 }))
185 }
186 } else {
187 if inner_stack_effect.peek(syn::token::Bracket) {
188 let inner_bracket;
189 bracketed!(inner_bracket in inner_stack_effect);
190 let size: Expr = inner_bracket.parse()?;
191 StackItem::NameCounted(name, size)
192 } else {
193 StackItem::Name(name)
194 }
195 },
196 );
197
198 if inner_stack_effect.parse::<Token![,]>().is_err() {
199 break;
200 }
201 }
202
203 inner_stack_effect.parse::<Token![-]>()?;
204 inner_stack_effect.parse::<Token![-]>()?;
205
206 while inner_stack_effect.peek(Ident) {
207 let name: Ident = inner_stack_effect.parse()?;
208
209 stack_effect.pushes.push(
210 if name == "unused" {
212 if inner_stack_effect.peek(syn::token::Bracket) {
213 let inner_bracket;
214 bracketed!(inner_bracket in inner_stack_effect);
215 let size: Expr = inner_bracket.parse()?;
216 StackItem::Unused(size)
217 } else {
218 StackItem::Unused(Expr::Lit(syn::ExprLit {
219 attrs: vec![],
220 lit: syn::Lit::Int(LitInt::new(
221 "1",
222 proc_macro::Span::call_site().into(),
223 )),
224 }))
225 }
226 } else {
227 if inner_stack_effect.peek(syn::token::Bracket) {
228 let inner_bracket;
229 bracketed!(inner_bracket in inner_stack_effect);
230 let size: Expr = inner_bracket.parse()?;
231 StackItem::NameCounted(name, size)
232 } else {
233 StackItem::Name(name)
234 }
235 },
236 );
237
238 if inner_stack_effect.parse::<Token![,]>().is_err() {
239 break;
240 }
241 }
242
243 Ok(Exception { stack_effect })
244 }
245}
246
247struct Opcodes {
248 opcodes: Vec<Opcode>,
249 exception: Option<Exception>,
250}
251
252impl Parse for Opcodes {
253 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
254 let mut opcodes = vec![];
255 let mut exception = None;
256
257 loop {
258 if input.peek(Token![*]) {
259 exception = Some(Exception::parse(input)?);
260 } else {
261 opcodes.push(Opcode::parse(input)?);
262 }
263
264 if input.parse::<Token![,]>().is_err() || input.is_empty() {
265 break;
266 }
267 }
268
269 Ok(Opcodes { opcodes, exception })
270 }
271}
272
273fn sum_items(items: &[StackItem]) -> Expr {
274 if items.is_empty() {
275 Expr::Lit(syn::ExprLit {
277 attrs: vec![],
278 lit: syn::Lit::Int(LitInt::new("0", proc_macro::Span::call_site().into())),
279 })
280 } else {
281 items
282 .iter()
283 .map(|p| match p {
284 StackItem::Name(_) => Expr::Lit(syn::ExprLit {
285 attrs: vec![],
286 lit: syn::Lit::Int(LitInt::new("1", proc_macro::Span::call_site().into())),
287 }),
288 StackItem::NameCounted(_, size) => size.clone(),
289 StackItem::Unused(size) => size.clone(),
290 })
291 .reduce(|left, right| {
292 syn::Expr::Binary(syn::ExprBinary {
293 attrs: vec![],
294 left: Box::new(left),
295 op: syn::BinOp::Add(syn::token::Plus {
296 spans: [proc_macro::Span::call_site().into()],
297 }),
298 right: Box::new(right),
299 })
300 })
301 .expect("Something is wrong with the format")
302 }
303}
304
305fn collect_stack_effect<'a, T>(
306 stack_items: T,
307 index_offset: Option<proc_macro2::TokenStream>,
308) -> (Vec<proc_macro2::TokenStream>, proc_macro2::TokenStream)
309where
310 T: DoubleEndedIterator<Item = &'a StackItem>,
311{
312 let mut index = if let Some(ref index_offset) = index_offset {
313 index_offset.clone()
314 } else {
315 quote! { 0 }
316 };
317
318 let mut prev_index = index.clone();
319
320 let mut fields = vec![];
321
322 for item in stack_items.rev() {
323 prev_index = index.clone();
324 let count = match item {
325 StackItem::Name(name) => {
326 let name = name.to_string();
327
328 let new_index = if index_offset.is_none() {
329 quote! { (#index) - 1 }
330 } else {
331 index.clone()
332 };
333
334 fields.push(quote! { StackItem { name: #name, count: 1, index: #new_index } });
335 quote! { 1 }
336 }
337 StackItem::NameCounted(name, count) => {
338 let name = name.to_string();
339
340 let new_index = if index_offset.is_none() {
341 quote! { (#index) - (#count) }
342 } else {
343 index.clone()
344 };
345
346 fields.push(
347 quote! { StackItem { name: #name, count: (#count) as u32, index: #new_index } },
348 );
349 quote! { #count }
350 }
351 StackItem::Unused(count) => quote! { #count },
352 };
353
354 if index_offset.is_none() {
355 index = quote! { (#index) - (#count) };
356 } else {
357 index = quote! { (#index) + (#count) };
358 }
359 }
360
361 if index_offset.is_none() {
362 fields.reverse();
363 }
364
365 (fields, index)
366}
367
368#[proc_macro]
369pub fn define_opcodes(input: TokenStream) -> TokenStream {
370 let Opcodes { opcodes, exception } = parse_macro_input!(input as Opcodes);
371
372 let opcodes_with_stack: Vec<_> = opcodes
373 .iter()
374 .filter(|o| o.stack_effect.is_some())
375 .collect();
376
377 let names: Vec<_> = opcodes.iter().map(|o| &o.name).collect();
378 let camel_names: Vec<Ident> = names
379 .iter()
380 .map(|ident| {
381 let camel = ident.to_string().to_upper_camel_case();
382 Ident::new(&camel, ident.span())
383 })
384 .collect();
385
386 let names_with_stack: Vec<_> = opcodes_with_stack.iter().map(|o| &o.name).collect();
387
388 let numbers: Vec<_> = opcodes.iter().map(|o| &o.number).collect();
389
390 let pops: Vec<_> = opcodes_with_stack
391 .iter()
392 .map(|o| sum_items(&o.stack_effect.as_ref().unwrap().pops))
393 .collect();
394
395 let pushes: Vec<_> = opcodes_with_stack
396 .iter()
397 .map(|o| sum_items(&o.stack_effect.as_ref().unwrap().pushes))
398 .collect();
399
400 let mut expanded = quote! {
401 #[allow(non_camel_case_types)]
402 #[allow(clippy::upper_case_acronyms)]
403 #[derive(Debug, Clone, PartialEq, Eq)]
404 pub enum Opcode {
405 #( #names ),*,
406 INVALID_OPCODE(u8),
407 }
408
409 impl From<u8> for Opcode {
410 fn from(value: u8) -> Self {
411 match value {
412 #( #numbers => Opcode::#names, )*
413 _ => Opcode::INVALID_OPCODE(value),
414 }
415 }
416 }
417
418 impl From<Opcode> for u8 {
419 fn from(value: Opcode) -> Self {
420 match value {
421 #( Opcode::#names => #numbers , )*
422 Opcode::INVALID_OPCODE(value) => value,
423 }
424 }
425 }
426
427 impl From<(Opcode, u8)> for Instruction {
428 fn from(value: (Opcode, u8)) -> Self {
429 match value.0 {
430 #(
431 Opcode::#names => Instruction::#camel_names(value.1),
432 )*
433 Opcode::INVALID_OPCODE(opcode) => {
434 if !cfg!(test) {
435 Instruction::InvalidOpcode((opcode, value.1))
436 } else {
437 panic!("Testing environment should not come across invalid opcodes")
438 }
439 },
440 }
441 }
442 }
443
444 impl Opcode {
445 pub fn from_instruction(instruction: &Instruction) -> Self {
446 match instruction {
447 #(
448 Instruction::#camel_names(_) => Opcode::#names ,
449 )*
450 Instruction::InvalidOpcode((opcode, _)) => Opcode::INVALID_OPCODE(*opcode),
451 }
452 }
453 }
454
455 impl StackEffectTrait for Opcode {
456 fn stack_effect(&self, oparg: u32, jump: bool, calculate_max: bool) -> StackEffect {
457 match &self {
458 #(
459 Opcode::#names_with_stack => StackEffect { pops: #pops, pushes: #pushes },
460 )*
461 Opcode::INVALID_OPCODE(_) => StackEffect { pops: 0, pushes: 0 },
462
463 _ => unimplemented!("stack_effect not implemented for {:?}", self),
464 }
465 }
466 }
467 };
468
469 let mut input_sirs = vec![];
470 let mut output_sirs = vec![];
471 let mut stack_deltas = vec![];
472
473 for (opcode, name) in opcodes.iter().zip(names) {
474 let mut input_constructor_fields = vec![];
475 let mut output_constructor_fields = vec![];
476 let mut stack_delta = quote! { 0 };
477
478 if let Some(stack_effect) = &opcode.stack_effect {
479 let input_offset;
480 (input_constructor_fields, input_offset) =
481 collect_stack_effect(stack_effect.pops.iter(), None);
482
483 (output_constructor_fields, _) =
484 collect_stack_effect(stack_effect.pushes.iter().rev(), Some(input_offset));
485
486 let pushes = sum_items(&stack_effect.pushes);
487 let pops = sum_items(&stack_effect.pops);
488
489 stack_delta = quote! { (#pushes) as isize - (#pops) as isize};
490 }
491
492 input_sirs.push(quote! { Opcode::#name => vec![
493 #(
494 #input_constructor_fields
495 ),*
496 ] });
497
498 output_sirs.push(quote! { Opcode::#name => vec![
499 #(
500 #output_constructor_fields
501 ),*
502 ] });
503
504 stack_deltas.push(quote! { Opcode::#name => #stack_delta });
505 }
506
507 let sir_exception = if let Some(exception) = exception {
508 let (input_fields, input_offset) =
509 collect_stack_effect(exception.stack_effect.pops.iter(), None);
510
511 let (output_fields, _) = collect_stack_effect(
512 exception.stack_effect.pushes.iter().rev(),
513 Some(input_offset),
514 );
515
516 let pushes = sum_items(&exception.stack_effect.pushes);
517 let pops = sum_items(&exception.stack_effect.pops);
518
519 let stack_delta = quote! { (#pushes) as isize - (#pops) as isize};
520
521 quote! {
522 #[derive(PartialEq, Debug, Clone)]
523 pub struct SIRException {
524 pub lasti: bool,
525 pub stack_depth: usize,
526 pub input: Vec<StackItem>,
527 pub output: Vec<StackItem>,
528 pub net_stack_delta: isize
529 }
530
531 impl SIRException {
532 pub fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
533 let input = vec![
534 #(
535 #input_fields
536 ),*
537 ];
538
539 let output = vec![
540 #(
541 #output_fields
542 ),*
543 ];
544
545 let net_stack_delta = #stack_delta;
546
547 Self {
548 lasti,
549 stack_depth,
550 input,
551 output,
552 net_stack_delta,
553 }
554 }
555 }
556
557 impl GenericSIRException for SIRException {
558 type Opcode = Opcode;
559
560 fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
561 SIRException::new(lasti, stack_depth, jump)
562 }
563
564 fn get_outputs(&self) -> &[StackItem] {
565 &self.output
566 }
567
568 fn get_inputs(&self) -> &[StackItem] {
569 &self.input
570 }
571
572 fn get_net_stack_delta(&self) -> isize {
573 self.net_stack_delta
574 }
575
576 fn get_stack_depth(&self) -> usize {
577 self.stack_depth
578 }
579 }
580
581 impl std::fmt::Display for ExceptionCall<SIRNode> {
582 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
583 let mut inputs = self
584 .stack_inputs
585 .iter()
586 .map(|input| format!("{}", input))
587 .collect::<Vec<_>>();
588
589 inputs.push(format!("{}", self.exception.lasti));
590
591 write!(f, "EXCEPTION({})", inputs.join(", "))
592 }
593 }
594 }
595 } else {
596 quote! {#[derive(PartialEq, Debug, Clone)]
597 pub struct SIRException {
600 }
601
602 impl std::fmt::Display for ExceptionCall<SIRNode> {
603 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604 unreachable!()
605 }
606 }
607
608 impl GenericSIRException for SIRException {
609 type Opcode = Opcode;
610
611 fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
612 SIRException {}
613 }
614
615 fn get_outputs(&self) -> &[StackItem] {
616 &[]
617 }
618
619 fn get_inputs(&self) -> &[StackItem] {
620 &[]
621 }
622
623 fn get_net_stack_delta(&self) -> isize {
624 0
625 }
626
627 fn get_stack_depth(&self) -> usize {
628 0
629 }
630 }
631 }
632 };
633
634 let sir = quote! {
635 pub mod sir {
636 use super::{Opcode};
637 use crate::sir::{SIR, StackItem, SIRStatement, ExceptionCall, Call, SIRExpression, AuxVar};
638 use crate::traits::{GenericSIRNode, SIROwned, GenericSIRException};
639
640
641 #[derive(PartialEq, Debug, Clone)]
642 pub struct SIRNode {
643 pub opcode: Opcode,
644 pub oparg: u32,
645 pub input: Vec<StackItem>,
646 pub output: Vec<StackItem>,
647 pub net_stack_delta: isize
648 }
649
650 impl SIRNode {
651 pub fn new(opcode: Opcode, oparg: u32, jump: bool) -> Self {
652 let calculate_max = false;
654 let oparg = oparg as isize;
655
656 let input = match opcode {
657 #(
658 #input_sirs
659 ),*,
660 Opcode::INVALID_OPCODE(_) => vec![],
661 };
662
663 let output = match opcode {
664 #(
665 #output_sirs
666 ),*,
667 Opcode::INVALID_OPCODE(_) => vec![],
668 };
669
670 let net_stack_delta = match opcode {
671 #(
672 #stack_deltas
673 ),*,
674 Opcode::INVALID_OPCODE(_) => 0,
675 };
676
677 Self {
678 opcode,
679 oparg: oparg as u32,
680 input,
681 output,
682 net_stack_delta
683 }
684 }
685 }
686
687 #sir_exception
688
689 impl GenericSIRNode for SIRNode {
690 type Opcode = Opcode;
691 type SIRException = SIRException;
692
693 fn new(opcode: Self::Opcode, oparg: u32, jump: bool) -> Self {
694 SIRNode::new(opcode, oparg, jump)
695 }
696
697 fn get_outputs(&self) -> &[StackItem] {
698 &self.output
699 }
700
701 fn get_inputs(&self) -> &[StackItem] {
702 &self.input
703 }
704
705 fn get_net_stack_delta(&self) -> isize {
706 self.net_stack_delta
707 }
708 }
709
710 impl SIROwned<SIRNode> for SIR<SIRNode> {
711 fn new(statements: Vec<SIRStatement<SIRNode>>) -> Self {
712 SIR(statements)
713 }
714 }
715
716 impl std::fmt::Display for SIR<SIRNode> {
717 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718 for statement in &self.0 {
719 match statement {
720 SIRStatement::Assignment(aux_var, call) => {
721 writeln!(f, "{} = {}", aux_var.name, call)?;
722 }
723 SIRStatement::TupleAssignment(aux_vars, call) => {
724 let vars = aux_vars.iter().map(|v| v.name.clone()).collect::<Vec<_>>().join(", ");
725 writeln!(f, "({}) = {}", vars, call)?;
726 }
727 SIRStatement::DisregardCall(call) => {
728 writeln!(f, "{}", call)?;
729 }
730 }
731 }
732 Ok(())
733 }
734 }
735
736 impl std::fmt::Display for Call<SIRNode> {
737 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
738 let mut inputs = self
739 .stack_inputs
740 .iter()
741 .map(|input| format!("{}", input))
742 .collect::<Vec<_>>();
743
744 inputs.push(format!("{}", self.node.oparg));
745
746 write!(f, "{:#?}({})", self.node.opcode, inputs.join(", "))
747 }
748 }
749
750 impl std::fmt::Display for SIRExpression<SIRNode> {
751 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
752 match self {
753 SIRExpression::Call(call) => write!(f, "{}", call),
754 SIRExpression::Exception(exception_call) => write!(f, "{}", exception_call),
755 SIRExpression::AuxVar(aux_var) => write!(f, "{}", aux_var.name.clone()),
756 SIRExpression::PhiNode(phi) => write!(f, "phi({})", phi.iter().map(|v| &v.name).cloned().collect::<Vec<_>>().join(", ")),
757 SIRExpression::GeneratorStart => write!(f, "GenStart()"),
758 }
759 }
760 }
761 }
762 };
763
764 expanded.extend(sir);
765
766 expanded.into()
767}
768
769#[cfg(test)]
770mod tests {
771 use proc_macro2::Span;
772 use syn::Ident;
773
774 use crate::StackItem;
775
776 #[test]
777 fn test_stack_effect() {
778 let inputs = [
779 StackItem::Name(Ident::new("first", Span::call_site())),
780 StackItem::Name(Ident::new("second", Span::call_site())),
781 ];
782
783 let outputs = [
784 StackItem::Name(Ident::new("out", Span::call_site())),
785 StackItem::Name(Ident::new("out2", Span::call_site())),
786 ];
787
788 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
789
790 let (output_fields, _) =
791 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
792
793 for input_field in input_fields {
794 println!("{}", input_field);
795 }
796
797 assert_eq!(
798 format!("{}", output_fields[0]),
799 "StackItem { name : \"out\" , count : 1 , index : ((0) - (1)) - (1) }"
800 );
801
802 assert_eq!(
803 format!("{}", output_fields[1]),
804 "StackItem { name : \"out2\" , count : 1 , index : (((0) - (1)) - (1)) + (1) }"
805 );
806
807 for output_field in output_fields {
808 println!("{}", output_field);
809 }
810 }
811
812 #[test]
813 fn test_stack_effect2() {
814 let inputs = [
815 StackItem::NameCounted(
816 Ident::new("first", Span::call_site()),
817 syn::Expr::Lit(syn::ExprLit {
818 attrs: vec![],
819 lit: syn::Lit::Int(syn::LitInt::new("5", Span::call_site().into())),
820 }),
821 ),
822 StackItem::Name(Ident::new("second", Span::call_site())),
823 ];
824
825 let outputs = [
826 StackItem::NameCounted(
827 Ident::new("out", Span::call_site()),
828 syn::Expr::Lit(syn::ExprLit {
829 attrs: vec![],
830 lit: syn::Lit::Int(syn::LitInt::new("5", Span::call_site().into())),
831 }),
832 ),
833 StackItem::Name(Ident::new("out2", Span::call_site())),
834 ];
835
836 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
837
838 let (output_fields, _) =
839 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
840
841 for input_field in input_fields {
842 println!("{}", input_field);
843 }
844
845 assert_eq!(
846 format!("{}", output_fields[0]),
847 "StackItem { name : \"out\" , count : (5) as u32 , index : ((0) - (1)) - (5) }"
848 );
849
850 assert_eq!(
851 format!("{}", output_fields[1]),
852 "StackItem { name : \"out2\" , count : 1 , index : (((0) - (1)) - (5)) + (5) }"
853 );
854
855 for output_field in output_fields {
856 println!("{}", output_field);
857 }
858 }
859
860 #[test]
861 fn test_stack_effect_copy() {
862 let inputs = [
864 StackItem::Name(Ident::new("bottom", Span::call_site())),
865 StackItem::Unused(syn::Expr::Lit(syn::ExprLit {
866 attrs: vec![],
867 lit: syn::Lit::Int(syn::LitInt::new("2", Span::call_site().into())),
868 })),
869 ];
870
871 let outputs = [
872 StackItem::Name(Ident::new("bottom", Span::call_site())),
873 StackItem::Unused(syn::Expr::Lit(syn::ExprLit {
874 attrs: vec![],
875 lit: syn::Lit::Int(syn::LitInt::new("2", Span::call_site().into())),
876 })),
877 StackItem::Name(Ident::new("top", Span::call_site())),
878 ];
879
880 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
881
882 let (output_fields, _) =
883 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
884
885 for input_field in input_fields {
886 println!("{}", input_field);
887 }
888
889 assert_eq!(
890 format!("{}", output_fields[0]),
891 "StackItem { name : \"bottom\" , count : 1 , index : ((0) - (2)) - (1) }"
892 );
893
894 assert_eq!(
895 format!("{}", output_fields[1]),
896 "StackItem { name : \"top\" , count : 1 , index : ((((0) - (2)) - (1)) + (1)) + (2) }"
897 );
898
899 for output_field in output_fields {
900 println!("{}", output_field);
901 }
902 }
903}