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}