1use {
2 proc_macro::TokenStream,
3 proc_macro2::Span,
4 quote::quote,
5 syn::{
6 parse_macro_input,
7 Data::{Enum, Struct, Union},
8 DeriveInput,
9 Fields::{self, Named, Unnamed},
10 FieldsNamed, FieldsUnnamed, Ident, Index, Variant,
11 },
12};
13
14fn of_named_fields(n: &Ident, named_fields: &FieldsNamed) -> proc_macro2::TokenStream {
15 let initialize = named_fields.named.iter().map(|f| {
16 let name = f.ident.as_ref().unwrap();
17 quote! {
18 #name: ::cvlr::nondet::nondet(),
19 }
20 });
21
22 quote! {
23 #n {
24 #( #initialize )*
25 }
26 }
27}
28
29fn of_unnamed_fields(n: &Ident, unnamed: &FieldsUnnamed) -> proc_macro2::TokenStream {
30 let initialize = unnamed.unnamed.iter().map(|_| {
31 quote! { ::cvlr::nondet::nondet(), }
32 });
33
34 quote! {
35 #n (
36 #( #initialize )*
37 )
38 }
39}
40
41fn of_enum_variant(variant: &Variant, enum_name: &Ident) -> proc_macro2::TokenStream {
42 let variant_name = &variant.ident;
43 match &variant.fields {
44 Fields::Unit => quote! {
45 #enum_name::#variant_name
46 },
47 Fields::Unnamed(unnamed) => {
48 let initialize = unnamed.unnamed.iter().map(|_| {
49 quote! { ::cvlr::nondet::nondet(), }
50 });
51 quote! {
52 #enum_name::#variant_name(
53 #( #initialize )*
54 )
55 }
56 }
57 Fields::Named(named) => {
58 let initialize = named.named.iter().map(|f| {
59 let field_name = f.ident.as_ref().unwrap();
60 quote! {
61 #field_name: ::cvlr::nondet::nondet(),
62 }
63 });
64 quote! {
65 #enum_name::#variant_name {
66 #( #initialize )*
67 }
68 }
69 }
70 }
71}
72
73#[proc_macro_derive(Nondet)]
101pub fn derive_nondet(item: TokenStream) -> TokenStream {
102 let input = parse_macro_input!(item as DeriveInput);
103 let name = input.ident;
104 match input.data {
105 Enum(data_enum) => {
106 let variants = &data_enum.variants;
107 let variant_count = variants.len();
108
109 if variant_count == 0 {
110 return quote! {
111 compile_error!("Enum must have at least one variant");
112 }
113 .into();
114 }
115
116 let mut match_arms = Vec::new();
117 for (index, variant) in variants.iter().enumerate() {
118 let variant_expr = of_enum_variant(variant, &name);
119 if index == variant_count - 1 {
120 match_arms.push(quote! {
122 _ => #variant_expr,
123 });
124 } else {
125 let index_lit = index as u64;
126 match_arms.push(quote! {
127 #index_lit => #variant_expr,
128 });
129 }
130 }
131
132 quote! {
133 impl ::cvlr::nondet::Nondet for #name {
134 fn nondet() -> #name {
135 match ::cvlr::nondet::nondet::<u64>() {
136 #( #match_arms )*
137 }
138 }
139 }
140 }
141 .into()
142 }
143
144 Union(_) => {
145 todo!("Union not supported yet")
146 }
147
148 Struct(ds) => match ds.fields {
149 Fields::Unit => quote! {
150 impl ::cvlr::nondet::Nondet for #name {
151 fn nondet() -> #name {
152 #name
153 }
154 }
155 }
156 .into(),
157
158 Named(named) => {
159 let init = of_named_fields(&name, &named);
160 quote! {
161 impl ::cvlr::nondet::Nondet for #name {
162 fn nondet() -> #name {
163 #init
164 }
165 }
166 }
167 .into()
168 }
169
170 Unnamed(fields) => {
171 let init = of_unnamed_fields(&name, &fields);
172 quote! {
173 impl ::cvlr::nondet::Nondet for #name {
174 fn nondet() -> #name {
175 #init
176 }
177 }
178 }
179 .into()
180 }
181 },
182 }
183}
184
185#[proc_macro_derive(CvlrLog)]
229pub fn derive_cvlr_log(item: TokenStream) -> TokenStream {
230 let input = parse_macro_input!(item as DeriveInput);
231 let name = input.ident;
232
233 match input.data {
234 Enum(data_enum) => {
235 let variants = &data_enum.variants;
236 let match_arms: Vec<_> = variants.iter().map(|variant| {
237 let variant_name = &variant.ident;
238 let variant_name_str = variant_name.to_string();
239 match &variant.fields {
240 Fields::Unit => {
241 quote! {
242 #name::#variant_name => {
243 logger.log_str(tag, #variant_name_str);
244 }
245 }
246 }
247 Fields::Unnamed(unnamed) => {
248 let field_bindings: Vec<_> = unnamed.unnamed.iter().enumerate().map(|(index, _f)| {
249 syn::Ident::new(&format!("field{}", index), Span::call_site())
250 }).collect();
251 let field_logs: Vec<_> = unnamed.unnamed.iter().enumerate().map(|(index, _f)| {
252 let field_binding = &field_bindings[index];
253 let field_index_str = index.to_string();
254 quote! {
255 ::cvlr::log::cvlr_log_with(#field_index_str, &#field_binding, logger);
256 }
257 }).collect();
258 quote! {
259 #name::#variant_name(#(ref #field_bindings),*) => {
260 logger.log_scope_start(tag);
261 logger.log_str(tag, #variant_name_str);
262 #( #field_logs )*
263 logger.log_scope_end(tag);
264 }
265 }
266 }
267 Fields::Named(named) => {
268 let field_logs: Vec<_> = named.named.iter().map(|f| {
269 let field_name = f.ident.as_ref().unwrap();
270 let field_name_str = field_name.to_string();
271 quote! {
272 ::cvlr::log::cvlr_log_with(#field_name_str, &#field_name, logger);
273 }
274 }).collect();
275 let field_names: Vec<_> = named.named.iter().map(|f| {
276 f.ident.as_ref().unwrap()
277 }).collect();
278
279 quote! {
280 #name::#variant_name { #(ref #field_names),* } => {
281 logger.log_scope_start(tag);
282 logger.log_str(tag, #variant_name_str);
283 #( #field_logs )*
284 logger.log_scope_end(tag);
285 }
286 }
287 }
288 }
289 }).collect();
290
291 quote! {
292 impl ::cvlr::log::CvlrLog for #name {
293 #[inline(always)]
294 fn log(&self, tag: &str, logger: &mut ::cvlr::log::CvlrLogger) {
295 match self {
296 #( #match_arms )*
297 }
298 }
299 }
300 }
301 .into()
302 }
303
304 Union(_) => quote! {
305 compile_error!("CvlrLog derive is only supported for structs");
306 }
307 .into(),
308
309 Struct(ds) => {
310 match ds.fields {
311 Fields::Unit => quote! {
312 impl ::cvlr::log::CvlrLog for #name {
313 #[inline(always)]
314 fn log(&self, tag: &str, logger: &mut ::cvlr::log::CvlrLogger) {
315 logger.log_scope_start(tag);
316 logger.log_scope_end(tag);
317 }
318 }
319 }
320 .into(),
321
322 Fields::Unnamed(unnamed) => {
323 let field_logs: Vec<_> = unnamed.unnamed.iter().enumerate().map(|(index, _f)| {
324 let field_index = Index::from(index);
325 let field_index_str = index.to_string();
326 quote! {
327 ::cvlr::log::cvlr_log_with(#field_index_str, &self.#field_index, logger);
328 }
329 }).collect();
330
331 quote! {
332 impl ::cvlr::log::CvlrLog for #name {
333 #[inline(always)]
334 fn log(&self, tag: &str, logger: &mut ::cvlr::log::CvlrLogger) {
335 logger.log_scope_start(tag);
336 #( #field_logs )*
337 logger.log_scope_end(tag);
338 }
339 }
340 }
341 .into()
342 }
343
344 Fields::Named(named) => {
345 let field_logs: Vec<_> = named.named.iter().map(|f| {
346 let field_name = f.ident.as_ref().unwrap();
347 let field_name_str = field_name.to_string();
348 quote! {
349 ::cvlr::log::cvlr_log_with(#field_name_str, &self.#field_name, logger);
350 }
351 }).collect();
352
353 quote! {
354 impl ::cvlr::log::CvlrLog for #name {
355 #[inline(always)]
356 fn log(&self, tag: &str, logger: &mut ::cvlr::log::CvlrLogger) {
357 logger.log_scope_start(tag);
358 #( #field_logs )*
359 logger.log_scope_end(tag);
360 }
361 }
362 }
363 .into()
364 }
365 }
366 }
367 }
368}