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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
extern crate proc_macro;
use self::proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parse_macro_input, Token};
struct Input {
ty: syn::Type,
block: syn::Block,
}
impl Parse for Input {
fn parse(input: ParseStream) -> Result<Self> {
let ty = input.parse()?;
let _: Token![,] = input.parse()?;
let block = input.parse()?;
Ok(Input { ty, block })
}
}
#[proc_macro]
pub fn graph_impl(item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as Input);
let ty = &input.ty;
let lines = &input.block.stmts;
let stmts: Vec<_> = lines
.into_iter()
.map(|line| match line {
syn::Stmt::Local(local) => {
if local.pats.len() != 1 {
unreachable!("Unknown case ??");
}
let id = match &local.pats[0] {
syn::Pat::Ident(id) => &id.ident,
_ => unreachable!("Unsupported lhs pattern"),
};
let name = id.to_string();
let (_eq, expr) = local.init.as_ref().unwrap();
let (dep, expr) = quote_expr(&expr, &name);
quote!{
#(#dep)*
let #id = #expr;
g.set_name(#id, #name);
}
}
_ => unreachable!("cagra-parser supports 'let' statement only"),
}).collect();
let stream = quote!{
#[allow(unused_variables)]
fn graph_new() -> cagra::graph::Graph<#ty> {
let mut g = cagra::graph::Graph::new();
#( #stmts )*
g
}
};
stream.into()
}
fn quote_expr(expr: &syn::Expr, name: &str) -> (Vec<TokenStream2>, TokenStream2) {
match expr {
syn::Expr::Call(call) => {
let mut ts = Vec::new();
let mut args = Vec::new();
for (i, arg) in call.args.iter().enumerate() {
let name = format!("{}__arg{}", name, i);
let id = syn::Ident::new(&name, proc_macro2::Span::call_site());
let (mut dep, arg) = quote_expr(arg, &name);
ts.append(&mut dep);
ts.push(quote!{ let #id = #arg; });
args.push(quote!( #id ));
}
let f = &call.func;
let f = quote!( #f );
let id = syn::Ident::new(&name, proc_macro2::Span::call_site());
ts.push(quote!{ let #id = g.#f(#(#args),*); });
(ts, quote!{ #id })
}
syn::Expr::Binary(bin) => {
let name_lhs = format!("{}__lhs", name);
let name_rhs = format!("{}__rhs", name);
let (mut dep_lhs, lhs) = quote_expr(&bin.left, &name_lhs);
let (mut dep_rhs, rhs) = quote_expr(&bin.right, &name_rhs);
dep_lhs.append(&mut dep_rhs);
let id_lhs = syn::Ident::new(&name_lhs, proc_macro2::Span::call_site());
let id_rhs = syn::Ident::new(&name_rhs, proc_macro2::Span::call_site());
dep_lhs.push(quote!{ let #id_lhs = #lhs; });
dep_lhs.push(quote!{ let #id_rhs = #rhs; });
let (op_str, span) = match bin.op {
syn::BinOp::Add(op) => ("add", op.spans[0]),
syn::BinOp::Sub(op) => ("sub", op.spans[0]),
syn::BinOp::Mul(op) => ("mul", op.spans[0]),
syn::BinOp::Div(op) => ("div", op.spans[0]),
_ => unreachable!("Unsupported binary operator"),
};
let op = syn::Ident::new(op_str, span);
(dep_lhs, quote!{ g.#op(#id_lhs, #id_rhs) })
}
syn::Expr::Lit(lit) => {
let id = syn::Ident::new(name, proc_macro2::Span::call_site());
let dep = if name.find("__").is_none() {
quote!{ let #id = g.variable(#name, #lit).expect("Duplicated symbols"); }
} else {
quote!{ let #id = g.constant(#lit); }
};
(vec![dep], quote!( #id ))
}
_ => (Vec::new(), quote!( #expr )),
}
}