desugars operator overloading
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! a lil macro crate.
//!
//! provides a handy macro for converting `a + b` to `a.add(b)` for when you cant easily overload the `Add` trait.
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, spanned::Spanned, Expr, ExprBinary, ExprUnary};

fn walk(e: Expr) -> proc_macro2::TokenStream {
    match e {
        Expr::Binary(ExprBinary {
            left, op, right, ..
        }) => {
            let left = walk(*left);
            let right = walk(*right);
            use syn::BinOp::*;
            match op {
                Add(_) => quote!((#left).add(#right)),
                Sub(_) => quote!((#left).sub(#right)),
                Mul(_) => quote!((#left).mul(#right)),
                Div(_) => quote!((#left).div(#right)),
                Rem(_) => quote!((#left).rem(#right)),
                And(_) => quote!((#left).and(#right)),
                Or(_) => quote!((#left).or(#right)),
                BitXor(_) => quote!((#left).bitxor(#right)),
                BitAnd(_) => quote!((#left).bitand(#right)),
                BitOr(_) => quote!((#left).bitor(#right)),
                Shl(_) => quote!((#left).shl(#right)),
                Shr(_) => quote!((#left).shr(#right)),
                Eq(_) => quote!((#left).eq(#right)),
                Lt(_) => quote!((#left).lt(#right)),
                Le(_) => quote!((#left).le(#right)),
                Ne(_) => quote!((#left).ne(#right)),
                Ge(_) => quote!((#left).ge(#right)),
                Gt(_) => quote!((#left).gt(#right)),
                // don't support assigning ops
                e => syn::Error::new(e.span(), format!("{}", quote!(op #e not supported)))
                    .to_compile_error(),
            }
        }
        Expr::Unary(ExprUnary { op, expr, .. }) => {
            let x = walk(*expr);
            match op {
                syn::UnOp::Deref(_) => quote!((#x).deref()),
                syn::UnOp::Not(_) => quote!((#x).not()),
                syn::UnOp::Neg(_) => quote!((#x).neg()),
                e => syn::Error::new(
                    e.span(),
                    "it would appear a new operation has been added! please tell me.",
                )
                .to_compile_error(),
            }
        }
        e => quote!(#e),
    }
}

/// Lower math to method calls.
/// ```
/// # use std::ops::*;
/// let [a, b, c] = [5i32, 6, 7];
/// assert_eq!(lower::math! { a * *&b + -c }, a * *&b + -c);
/// // expands to
/// // a.mul((&b).deref()).add(c.neg())
/// ```
#[proc_macro]
pub fn math(input: TokenStream) -> TokenStream {
    walk(parse_macro_input!(input as Expr)).into()
}