Skip to main content

azucar_macro/
lib.rs

1use proc_macro::TokenStream;
2use syn::parse::Parser;
3use syn::spanned::Spanned;
4use syn::visit_mut::VisitMut;
5use syn::{Expr, Ident, Token};
6
7struct InferVisit;
8struct RefOpsVisit;
9struct IndexVisit;
10
11fn visit_expr_macro<V: VisitMut>(v: &mut V, i: &mut syn::ExprMacro) {
12	if let Ok(mut mac) = syn::punctuated::Punctuated::<Expr, Token![,]>::parse_terminated
13		.parse2(i.mac.tokens.clone())
14	{
15		for e in &mut mac {
16			v.visit_expr_mut(e);
17		}
18		i.mac.tokens = quote::quote!(#mac);
19		return;
20	}
21
22	if let Ok(mut mac) = syn::punctuated::Punctuated::<Expr, Token![;]>::parse_terminated
23		.parse2(i.mac.tokens.clone())
24	{
25		for e in &mut mac {
26			v.visit_expr_mut(e);
27		}
28		i.mac.tokens = quote::quote!(#mac);
29		return;
30	}
31}
32
33impl VisitMut for InferVisit {
34	fn visit_expr_macro_mut(&mut self, i: &mut syn::ExprMacro) {
35		visit_expr_macro(self, i)
36	}
37
38	fn visit_angle_bracketed_generic_arguments_mut(
39		&mut self,
40		_: &mut syn::AngleBracketedGenericArguments,
41	) {
42	}
43
44	fn visit_generic_argument_mut(&mut self, _: &mut syn::GenericArgument) {}
45
46	fn visit_generic_param_mut(&mut self, _: &mut syn::GenericParam) {}
47
48	fn visit_generics_mut(&mut self, _: &mut syn::Generics) {}
49
50	fn visit_parenthesized_generic_arguments_mut(
51		&mut self,
52		_: &mut syn::ParenthesizedGenericArguments,
53	) {
54	}
55
56	fn visit_expr_mut(&mut self, i: &mut syn::Expr) {
57		match i {
58			Expr::Infer(syn::ExprInfer {
59				attrs,
60				underscore_token,
61			}) => {
62				let krate = syn::Stmt::Item(syn::Item::ExternCrate(syn::ItemExternCrate {
63					attrs: vec![],
64					vis: syn::Visibility::Inherited,
65					extern_token: Default::default(),
66					crate_token: Default::default(),
67					ident: Ident::new("azucar", proc_macro::Span::call_site().into()),
68					rename: None,
69					semi_token: Default::default(),
70				}));
71
72				let call = Expr::Call(syn::ExprCall {
73					attrs: core::mem::take(attrs),
74					func: Box::new(Expr::Path(syn::ExprPath {
75						attrs: vec![],
76						qself: None,
77						path: syn::Path {
78							leading_colon: Some(Default::default()),
79							segments: [
80								syn::PathSegment {
81									ident: Ident::new("azucar", underscore_token.span),
82									arguments: syn::PathArguments::None,
83								},
84								syn::PathSegment {
85									ident: Ident::new("Infer", underscore_token.span),
86									arguments: syn::PathArguments::None,
87								},
88								syn::PathSegment {
89									ident: Ident::new("infer", underscore_token.span),
90									arguments: syn::PathArguments::None,
91								},
92							]
93							.into_iter()
94							.collect(),
95						},
96					})),
97					paren_token: Default::default(),
98					args: syn::punctuated::Punctuated::new(),
99				});
100
101				*i = Expr::Block(syn::ExprBlock {
102					attrs: vec![],
103					label: None,
104					block: syn::Block {
105						brace_token: Default::default(),
106						stmts: vec![krate, syn::Stmt::Expr(call, None)],
107					},
108				})
109			},
110			_ => {
111				syn::visit_mut::visit_expr_mut(self, i);
112			},
113		}
114	}
115}
116impl VisitMut for RefOpsVisit {
117	fn visit_expr_macro_mut(&mut self, i: &mut syn::ExprMacro) {
118		visit_expr_macro(self, i)
119	}
120
121	fn visit_expr_unary_mut(&mut self, i: &mut syn::ExprUnary) {
122		syn::visit_mut::visit_expr_unary_mut(self, i);
123
124		match i.op {
125			syn::UnOp::Not(_) | syn::UnOp::Neg(_) => {
126				*i.expr = Expr::Reference(syn::ExprReference {
127					attrs: vec![],
128					and_token: Default::default(),
129					mutability: None,
130					expr: Box::new(core::mem::replace(&mut i.expr, Expr::PLACEHOLDER)),
131				});
132			},
133			_ => {},
134		};
135	}
136
137	fn visit_expr_binary_mut(&mut self, i: &mut syn::ExprBinary) {
138		syn::visit_mut::visit_expr_binary_mut(self, i);
139
140		match i.op {
141			syn::BinOp::Add(_)
142			| syn::BinOp::Sub(_)
143			| syn::BinOp::Mul(_)
144			| syn::BinOp::Div(_)
145			| syn::BinOp::Rem(_)
146			| syn::BinOp::And(_)
147			| syn::BinOp::Or(_)
148			| syn::BinOp::BitXor(_)
149			| syn::BinOp::BitAnd(_)
150			| syn::BinOp::BitOr(_)
151			| syn::BinOp::Shl(_)
152			| syn::BinOp::Shr(_) => {
153				for e in [&mut i.left, &mut i.right] {
154					**e = Expr::Reference(syn::ExprReference {
155						attrs: vec![],
156						and_token: Default::default(),
157						mutability: None,
158						expr: Box::new(core::mem::replace(e, Expr::PLACEHOLDER)),
159					});
160				}
161			},
162			_ => {},
163		}
164	}
165}
166
167impl VisitMut for IndexVisit {
168	fn visit_expr_macro_mut(&mut self, i: &mut syn::ExprMacro) {
169		visit_expr_macro(self, i);
170	}
171
172	fn visit_expr_mut(&mut self, i: &mut syn::Expr) {
173		syn::visit_mut::visit_expr_mut(self, i);
174
175		match i {
176			Expr::Index(e) => match &mut *e.index {
177				Expr::Unary(syn::ExprUnary { attrs, op, expr }) => match op {
178					syn::UnOp::Deref(_) => {
179						let index = core::mem::replace(&mut **expr, Expr::PLACEHOLDER);
180						let expr = core::mem::replace(&mut *e.expr, Expr::PLACEHOLDER);
181
182						let krate = syn::Stmt::Item(syn::Item::ExternCrate(syn::ItemExternCrate {
183							attrs: vec![],
184							vis: syn::Visibility::Inherited,
185							extern_token: Default::default(),
186							crate_token: Default::default(),
187							ident: Ident::new("azucar", proc_macro::Span::call_site().into()),
188							rename: None,
189							semi_token: Default::default(),
190						}));
191
192						let call = Expr::Call(syn::ExprCall {
193							attrs: core::mem::take(attrs),
194							func: Box::new(Expr::Path(syn::ExprPath {
195								attrs: vec![],
196								qself: None,
197								path: syn::Path {
198									leading_colon: Some(Default::default()),
199									segments: [
200										syn::PathSegment {
201											ident: Ident::new(
202												"azucar",
203												e.bracket_token.span.span(),
204											),
205											arguments: syn::PathArguments::None,
206										},
207										syn::PathSegment {
208											ident: Ident::new(
209												"IndexMove",
210												e.bracket_token.span.span(),
211											),
212											arguments: syn::PathArguments::None,
213										},
214										syn::PathSegment {
215											ident: Ident::new(
216												"index_move",
217												e.bracket_token.span.span(),
218											),
219											arguments: syn::PathArguments::None,
220										},
221									]
222									.into_iter()
223									.collect(),
224								},
225							})),
226							paren_token: Default::default(),
227							args: [expr, index].into_iter().collect(),
228						});
229
230						*i = Expr::Block(syn::ExprBlock {
231							attrs: vec![],
232							label: None,
233							block: syn::Block {
234								brace_token: Default::default(),
235								stmts: vec![krate, syn::Stmt::Expr(call, None)],
236							},
237						})
238					},
239					_ => {},
240				},
241				Expr::Reference(syn::ExprReference {
242					attrs,
243					and_token,
244					mutability,
245					expr,
246				}) => {
247					let index = core::mem::replace(&mut **expr, Expr::PLACEHOLDER);
248
249					let expr = Box::new(core::mem::replace(&mut *e.expr, Expr::PLACEHOLDER));
250					let is_mut = mutability.is_some();
251
252					let expr = Expr::Reference(syn::ExprReference {
253						attrs: core::mem::take(attrs),
254						and_token: *and_token,
255						mutability: *mutability,
256						expr,
257					});
258
259					let krate = syn::Stmt::Item(syn::Item::ExternCrate(syn::ItemExternCrate {
260						attrs: vec![],
261						vis: syn::Visibility::Inherited,
262						extern_token: Default::default(),
263						crate_token: Default::default(),
264						ident: Ident::new("azucar", proc_macro::Span::call_site().into()),
265						rename: None,
266						semi_token: Default::default(),
267					}));
268
269					let call = Expr::Call(syn::ExprCall {
270						attrs: core::mem::take(attrs),
271						func: Box::new(Expr::Path(syn::ExprPath {
272							attrs: vec![],
273							qself: None,
274							path: syn::Path {
275								leading_colon: Some(Default::default()),
276								segments: [
277									syn::PathSegment {
278										ident: Ident::new("azucar", e.bracket_token.span.span()),
279										arguments: syn::PathArguments::None,
280									},
281									syn::PathSegment {
282										ident: Ident::new(
283											if is_mut { "IndexMut" } else { "Index" },
284											e.bracket_token.span.span(),
285										),
286										arguments: syn::PathArguments::None,
287									},
288									syn::PathSegment {
289										ident: Ident::new(
290											if is_mut { "index_mut" } else { "index" },
291											e.bracket_token.span.span(),
292										),
293										arguments: syn::PathArguments::None,
294									},
295								]
296								.into_iter()
297								.collect(),
298							},
299						})),
300						paren_token: Default::default(),
301						args: [expr, index].into_iter().collect(),
302					});
303
304					*i = Expr::Block(syn::ExprBlock {
305						attrs: vec![],
306						label: None,
307						block: syn::Block {
308							brace_token: Default::default(),
309							stmts: vec![krate, syn::Stmt::Expr(call, None)],
310						},
311					})
312				},
313				_ => {},
314			},
315			_ => {},
316		}
317	}
318}
319
320#[proc_macro_attribute]
321pub fn ref_ops(_: TokenStream, item: TokenStream) -> TokenStream {
322	let mut item = syn::parse_macro_input!(item as syn::Item);
323
324	RefOpsVisit.visit_item_mut(&mut item);
325
326	quote::quote!(#item).into()
327}
328
329#[proc_macro_attribute]
330pub fn infer(_: TokenStream, item: TokenStream) -> TokenStream {
331	let mut item = syn::parse_macro_input!(item as syn::Item);
332
333	InferVisit.visit_item_mut(&mut item);
334
335	quote::quote!(#item).into()
336}
337
338#[proc_macro_attribute]
339pub fn index(_: TokenStream, item: TokenStream) -> TokenStream {
340	let mut item = syn::parse_macro_input!(item as syn::Item);
341
342	IndexVisit.visit_item_mut(&mut item);
343
344	quote::quote!(#item).into()
345}