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
69
70
71
extern crate proc_macro;
use proc_macro::TokenStream;

mod component;

use syn::{DeriveInput, Expr};

#[proc_macro_derive(Component, attributes(component))]
pub fn component(input: TokenStream) -> TokenStream {
    let input: DeriveInput = syn::parse(input).unwrap();

    let gen = component::impl_component(&input);

    gen.into()
}

/// Evaluate a numeric expression
fn eval_expr(expr: Expr) -> u64 {
    use syn::{BinOp, ExprBinary, ExprLit, ExprUnary, Lit, UnOp};
    match expr {
        Expr::Binary(ExprBinary {
            left, op, right, ..
        }) => {
            let l = eval_expr(*left);
            let r = eval_expr(*right);
            match op {
                BinOp::Add(_) => l + r,
                BinOp::Sub(_) => l - r,
                BinOp::Mul(_) => l * r,
                BinOp::Div(_) => l / r,
                BinOp::Rem(_) => l % r,
                BinOp::BitXor(_) => l ^ r,
                BinOp::BitAnd(_) => l & r,
                BinOp::BitOr(_) => l | r,
                BinOp::Shl(_) => l << r,
                BinOp::Shr(_) => l >> r,
                op => panic!("Unsuported operator type: {:?}", op),
            }
        }
        Expr::Unary(ExprUnary { op, expr, .. }) => {
            let a = eval_expr(*expr);
            match op {
                UnOp::Not(_) => !a,
                op => panic!("Unsuported operator type: {:?}", op),
            }
        }
        Expr::Lit(ExprLit {
            lit: Lit::Int(i), ..
        }) => i.base10_parse::<u64>().expect("Invalid integer literal"),
        _ => panic!("Unsuported expression type"),
    }
}

/// A simple macro constructing U# types given an expression.
///
/// For example: `U![3*3]` becomes `U9`.
#[allow(non_snake_case)]
#[proc_macro]
pub fn U(input: TokenStream) -> TokenStream {
    use proc_macro2::Span;
    use quote::quote;
    use syn::Ident;
    let expr: Expr = syn::parse(input).expect("Expected an expression");

    let num = eval_expr(expr);
    let ident = Ident::new(&format!("U{}", num), Span::call_site());
    (quote! {
        #ident
    })
    .into()
}