cagra_parser/
lib.rs

1extern crate proc_macro;
2
3use self::proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6use syn::parse::{Parse, ParseStream, Result};
7use syn::{parse_macro_input, Token};
8
9struct Input {
10    ty: syn::Type,
11    block: syn::Block,
12}
13
14impl Parse for Input {
15    fn parse(input: ParseStream) -> Result<Self> {
16        let ty = input.parse()?;
17        let _: Token![,] = input.parse()?;
18        let block = input.parse()?;
19        Ok(Input { ty, block })
20    }
21}
22
23#[proc_macro]
24pub fn graph_impl(item: TokenStream) -> TokenStream {
25    let input = parse_macro_input!(item as Input);
26    let ty = &input.ty;
27    let lines = &input.block.stmts;
28    let stmts: Vec<_> = lines
29        .into_iter()
30        .map(|line| match line {
31            syn::Stmt::Local(local) => {
32                // lhs of `=`
33                if local.pats.len() != 1 {
34                    unreachable!("Unknown case ??");
35                }
36                let id = match &local.pats[0] {
37                    syn::Pat::Ident(id) => &id.ident,
38                    _ => unreachable!("Unsupported lhs pattern"),
39                };
40                let name = id.to_string();
41
42                // rhs of `=`
43                let (_eq, expr) = local.init.as_ref().unwrap();
44                let (dep, expr) = quote_expr(&expr, &name);
45                quote!{
46                    #(#dep)*
47                    let #id = #expr;
48                    g.set_name(#id, #name);
49                }
50            }
51            _ => unreachable!("cagra-parser supports 'let' statement only"),
52        }).collect();
53    let stream = quote!{
54        #[allow(unused_variables)]
55        fn graph_new() -> cagra::graph::Graph<#ty> {
56            let mut g = cagra::graph::Graph::new();
57            #( #stmts )*
58            g
59        }
60    };
61    stream.into()
62}
63
64fn quote_expr(expr: &syn::Expr, name: &str) -> (Vec<TokenStream2>, TokenStream2) {
65    match expr {
66        syn::Expr::Call(call) => {
67            let mut ts = Vec::new();
68            let mut args = Vec::new();
69            for (i, arg) in call.args.iter().enumerate() {
70                let name = format!("{}__arg{}", name, i);
71                let id = syn::Ident::new(&name, proc_macro2::Span::call_site());
72                let (mut dep, arg) = quote_expr(arg, &name);
73                ts.append(&mut dep);
74                ts.push(quote!{ let #id = #arg; });
75                args.push(quote!( #id ));
76            }
77            let f = &call.func;
78            let f = quote!( #f );
79            let id = syn::Ident::new(&name, proc_macro2::Span::call_site());
80            ts.push(quote!{ let #id = g.#f(#(#args),*); });
81            (ts, quote!{ #id })
82        }
83        syn::Expr::Binary(bin) => {
84            let name_lhs = format!("{}__lhs", name);
85            let name_rhs = format!("{}__rhs", name);
86            let (mut dep_lhs, lhs) = quote_expr(&bin.left, &name_lhs);
87            let (mut dep_rhs, rhs) = quote_expr(&bin.right, &name_rhs);
88            dep_lhs.append(&mut dep_rhs);
89            let id_lhs = syn::Ident::new(&name_lhs, proc_macro2::Span::call_site());
90            let id_rhs = syn::Ident::new(&name_rhs, proc_macro2::Span::call_site());
91            dep_lhs.push(quote!{ let #id_lhs = #lhs; });
92            dep_lhs.push(quote!{ let #id_rhs = #rhs; });
93
94            let (op_str, span) = match bin.op {
95                syn::BinOp::Add(op) => ("add", op.spans[0]),
96                syn::BinOp::Sub(op) => ("sub", op.spans[0]),
97                syn::BinOp::Mul(op) => ("mul", op.spans[0]),
98                syn::BinOp::Div(op) => ("div", op.spans[0]),
99                _ => unreachable!("Unsupported binary operator"),
100            };
101            let op = syn::Ident::new(op_str, span);
102            (dep_lhs, quote!{ g.#op(#id_lhs, #id_rhs) })
103        }
104        syn::Expr::Lit(lit) => {
105            let id = syn::Ident::new(name, proc_macro2::Span::call_site());
106            let dep = if name.find("__").is_none() {
107                quote!{ let #id = g.variable(#name, #lit).expect("Duplicated symbols"); }
108            } else {
109                quote!{ let #id = g.constant(#lit); }
110            };
111            (vec![dep], quote!( #id ))
112        }
113        _ => (Vec::new(), quote!( #expr )),
114    }
115}