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 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 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}