openvm_instructions_derive/
lib.rs

1extern crate alloc;
2extern crate proc_macro;
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprLit, Fields, Lit, Meta};
7
8#[proc_macro_derive(LocalOpcode, attributes(opcode_offset))]
9pub fn local_opcode_derive(input: TokenStream) -> TokenStream {
10    let ast = parse_macro_input!(input as DeriveInput);
11    let name = &ast.ident;
12
13    let mut offset = None;
14    for attr in ast.attrs {
15        if let Meta::NameValue(meta) = attr.meta {
16            if meta.path.is_ident("opcode_offset") {
17                if let Expr::Lit(ExprLit {
18                    lit: Lit::Int(lit_int),
19                    ..
20                }) = meta.value
21                {
22                    offset = Some(lit_int.base10_parse::<usize>().unwrap());
23                }
24            }
25        }
26    }
27    let offset = offset.expect("opcode_offset attribute not found");
28
29    match &ast.data {
30        Data::Struct(inner) => {
31            let inner = match &inner.fields {
32                Fields::Unnamed(fields) => {
33                    if fields.unnamed.len() != 1 {
34                        panic!("Only one unnamed field is supported");
35                    }
36                    fields.unnamed.first().unwrap().clone()
37                }
38                _ => panic!("Only unnamed fields are supported"),
39            };
40            let inner_ty = inner.ty;
41
42            quote! {
43                impl LocalOpcode for #name {
44                    const CLASS_OFFSET: usize = #offset;
45
46                    fn from_usize(value: usize) -> Self {
47                        #name(<#inner_ty as LocalOpcode>::from_usize(value))
48                    }
49
50                    fn local_usize(&self) -> usize {
51                        self.0.local_usize()
52                    }
53                }
54            }.into()
55        },
56        Data::Enum(_) => {
57            quote! {
58                impl LocalOpcode for #name {
59                    const CLASS_OFFSET: usize = #offset;
60
61                    fn from_usize(value: usize) -> Self {
62                        Self::from_repr(value.try_into().unwrap())
63                            .unwrap_or_else(|| panic!("Failed to convert usize {} to opcode {}", value, stringify!(#name)))
64                    }
65
66                    fn local_usize(&self) -> usize {
67                        *self as usize
68                    }
69                }
70            }.into()
71        },
72        Data::Union(_) => unimplemented!("Unions are not supported")
73    }
74}