openvm_instructions_derive/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
extern crate alloc;
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprLit, Fields, Lit, Meta};

#[proc_macro_derive(UsizeOpcode, attributes(opcode_offset))]
pub fn usize_opcode_derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let name = &ast.ident;

    let mut offset = None;
    for attr in ast.attrs {
        if let Meta::NameValue(meta) = attr.meta {
            if meta.path.is_ident("opcode_offset") {
                if let Expr::Lit(ExprLit {
                    lit: Lit::Int(lit_int),
                    ..
                }) = meta.value
                {
                    offset = Some(lit_int.base10_parse::<usize>().unwrap());
                }
            }
        }
    }
    let offset = offset.expect("opcode_offset attribute not found");

    match &ast.data {
        Data::Struct(inner) => {
            let inner = match &inner.fields {
                Fields::Unnamed(fields) => {
                    if fields.unnamed.len() != 1 {
                        panic!("Only one unnamed field is supported");
                    }
                    fields.unnamed.first().unwrap().clone()
                }
                _ => panic!("Only unnamed fields are supported"),
            };
            let inner_ty = inner.ty;

            quote! {
                impl UsizeOpcode for #name {
                    fn default_offset() -> usize {
                        #offset
                    }

                    fn from_usize(value: usize) -> Self {
                        #name(<#inner_ty as UsizeOpcode>::from_usize(value))
                    }

                    fn as_usize(&self) -> usize {
                        self.0.as_usize()
                    }
                }
            }.into()
        },
        Data::Enum(_) => {
            quote! {
                impl UsizeOpcode for #name {
                    fn default_offset() -> usize {
                        #offset
                    }

                    fn from_usize(value: usize) -> Self {
                        Self::from_repr(value.try_into().unwrap())
                            .unwrap_or_else(|| panic!("Failed to convert usize {} to opcode {}", value, stringify!(#name)))
                    }

                    fn as_usize(&self) -> usize {
                        *self as usize
                    }
                }
            }.into()
        },
        Data::Union(_) => unimplemented!("Unions are not supported")
    }
}