1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{
4 Data, DeriveInput, Expr, ExprLit, GenericArgument, Ident, Lit, LitStr, PathArguments, Type,
5 Variant,
6};
7
8#[proc_macro_derive(OpcodeHelper)]
9pub fn derive_opcode_helper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10 let ast = syn::parse_macro_input!(input as DeriveInput);
11 let variants = match &ast.data {
12 Data::Enum(v) => Some(&v.variants),
13 _ => None,
14 }
15 .unwrap();
16
17 let name = &ast.ident;
18 let i = 0..variants.len() as u8;
19
20 let initr = variants.iter().map(|v| read_variant(name, v));
21 let initw = variants
22 .iter()
23 .enumerate()
24 .map(|(i, v)| write_variant(name, v, i as u8));
25 let vname = variants.iter().map(|v| &v.ident);
26 let vname2 = vname.clone();
27 let vname_str = variants
28 .iter()
29 .map(|v| LitStr::new(&v.ident.to_string(), v.ident.span()));
30 let vname_str2 = vname_str.clone();
31 let vdesc = variants.iter().map(|v| {
32 let mut acc = String::new();
33 for attr in &v.attrs {
34 if let Ok(nv) = attr.meta.require_name_value() {
35 if nv.path.is_ident("doc") {
36 match &nv.value {
37 Expr::Lit(ExprLit {
38 lit: Lit::Str(lit), ..
39 }) => {
40 let lstr = lit.value();
41 let to_acc = lstr.trim();
42 if !to_acc.is_empty() {
43 acc.push_str(to_acc);
44 acc.push('\n');
45 }
46 }
47 _ => {}
48 }
49 }
50 }
51 }
52 acc.trim().to_string()
53 });
54 let vdefault_init = variants.iter().map(|v| {
55 let vname = &v.ident;
56 let finit = v.fields.iter().map(|f| {
57 let fname = f.ident.as_ref().unwrap();
58 quote! {
59 #fname: Default::default()
60 }
61 });
62 quote! {
63 #name::#vname { #( #finit,)* }
64 }
65 });
66
67 proc_macro::TokenStream::from(quote! {
68 impl #name {
69 pub fn read(r: &mut impl std::io::Read) -> crate::Result<#name> {
71
72 use byteorder::ReadBytesExt;
73 use crate::types::*;
74 use crate::read::{read_vari, read_varu};
75
76 let op = r.read_u8()?;
77 match op {
78 #( #i => #initr, )*
79 other => Err(crate::Error::MalformedBytecode(format!("Unknown opcode {}", op))),
80 }
81 }
82
83 pub fn write(&self, w: &mut impl std::io::Write) -> crate::Result<()> {
85
86 use byteorder::WriteBytesExt;
87 use crate::types::*;
88 use crate::write::write_var;
89
90 match self {
91 #( #initw )*
92 }
93
94 Ok(())
95 }
96
97 pub fn name(&self) -> &'static str {
99 match self {
100 #( #name::#vname { .. } => #vname_str, )*
101 }
102 }
103
104 pub fn description(&self) -> &'static str {
106 match self {
107 #( #name::#vname2 { .. } => #vdesc, )*
108 }
109 }
110
111 pub fn from_name(name: &str) -> Option<Self> {
113 match name {
114 #( #vname_str2 => Some(#vdefault_init), )*
115 _ => None
116 }
117 }
118 }
119 })
120}
121
122fn ident(ty: &Type) -> String {
124 match ty {
125 Type::Path(path) => {
126 let seg = &path.path.segments[0];
127 match &seg.arguments {
128 PathArguments::None => seg.ident.to_string(),
129 PathArguments::AngleBracketed(a) => {
130 let a = match &a.args[0] {
131 GenericArgument::Type(ty) => ident(ty),
132 _ => unreachable!(),
133 };
134 format!("{}<{}>", seg.ident, a)
135 }
136 _ => unreachable!(),
137 }
138 }
139 other => unreachable!("unknown type {:?}", other),
140 }
141}
142
143fn read_variant(enum_name: &Ident, v: &Variant) -> TokenStream {
144 let rvi32 = quote!(read_vari(r)?);
145 let rvu32 = quote!(read_varu(r)?);
146 let reg = quote!(Reg(#rvi32 as u32));
147
148 let vname = &v.ident;
149 let fname = v.fields.iter().map(|f| &f.ident);
150 let fvalue = v.fields.iter().map(|f| match ident(&f.ty).as_str() {
151 "usize" => quote!(#rvi32 as usize),
152 "i32" => quote! {
153 #rvi32 as JumpOffset
154 },
155 "JumpOffset" => quote! {
156 #rvi32 as JumpOffset
157 },
158 "Vec<JumpOffset>" => quote! {
159 {
160 let n = #rvu32 as usize;
161 let mut offsets = Vec::with_capacity(n);
162 for _ in 0..n {
163 offsets.push(#rvu32 as JumpOffset);
164 }
165 offsets
166 }
167 },
168 "Reg" => reg.clone(),
169 "Vec<Reg>" => quote! {
170 {
171 let n = r.read_u8()? as usize;
172 let mut regs = Vec::with_capacity(n);
173 for _ in 0..n {
174 regs.push(#reg);
175 }
176 regs
177 }
178 },
179 "RefInt" => quote! {
180 RefInt::read(r)?
181 },
182 "RefFloat" => quote! {
183 RefFloat::read(r)?
184 },
185 "RefBytes" => quote! {
186 RefBytes(#rvi32 as usize)
187 },
188 "RefString" => quote! {
189 RefString::read(r)?
190 },
191 "RefType" => quote! {
192 RefType::read(r)?
193 },
194 "ValBool" => quote! {
195 ValBool(#rvi32 == 1)
196 },
197 "RefFun" => quote! {
198 RefFun::read(r)?
199 },
200 "RefField" => quote! {
201 RefField::read(r)?
202 },
203 "RefGlobal" => quote! {
204 RefGlobal::read(r)?
205 },
206 "RefEnumConstruct" => quote! {
207 RefEnumConstruct(#rvi32 as usize)
208 },
209 _ => TokenStream::default(),
210 });
211 quote! {
212 Ok(#enum_name::#vname {
213 #( #fname: #fvalue, )*
214 })
215 }
216}
217
218fn write_variant(enum_name: &Ident, v: &Variant, i: u8) -> TokenStream {
219 let vname = &v.ident;
220 let fname = v.fields.iter().map(|f| &f.ident);
221 let fwrite = v.fields.iter().map(|f| {
222 let fname = f.ident.as_ref().unwrap();
223 match ident(&f.ty).as_str() {
224 "usize" => quote!(write_var(w, #fname as i32)?;),
225 "i32" => quote! {
226 write_var(w, #fname)?;
227 },
228 "JumpOffset" => quote! {
229 write_var(w, *#fname as i32)?;
230 },
231 "Vec<JumpOffset>" => quote! {
232 {
233 write_var(w, #fname.len() as i32)?;
234 for r__ in #fname {
235 write_var(w, *r__ as i32)?;
236 }
237 }
238 },
239 "Reg" => quote! {
240 write_var(w, #fname.0 as i32)?;
241 },
242 "Vec<Reg>" => quote! {
243 {
244 w.write_u8(#fname.len() as u8)?;
245 for r__ in #fname {
246 write_var(w, r__.0 as i32)?;
247 }
248 }
249 },
250 "RefInt" | "RefFloat" | "RefString" | "RefType" | "RefFun" | "RefField"
251 | "RefGlobal" => quote! {
252 #fname.write(w)?;
253 },
254 "RefBytes" => quote! {
255 write_var(w, #fname.0 as i32)?;
256 },
257 "ValBool" => quote! {
258 write_var(w, if #fname.0 { 1 } else { 0 })?;
259 },
260 "RefEnumConstruct" => quote! {
261 write_var(w, #fname.0 as i32)?;
262 },
263 _ => TokenStream::default(),
264 }
265 });
266 quote! {
267 #enum_name::#vname { #( #fname, )* } => {
268 w.write_u8(#i)?;
269 #( #fwrite )*
270 }
271 }
272}