1extern crate proc_macro;
2use proc_macro::{TokenStream, TokenTree};
3use quote::*;
4
5struct Opts {
6 sup: Option<String>,
7 idtype: Option<TokenStream>,
8}
9
10macro_rules! fail {
11 ($t:expr) => {
12 {
13 let failresult = quote!{
14 compile_error!($t);
15 };
16 return failresult.into();
17 }
18 }
19}
20
21macro_rules! consume_punct {
22 ($i:expr) => {
23 {
24 let x = $i.next();
25 match x {
26 Some(e) => {
27 match e {
28 TokenTree::Punct(_) => {},
29 TokenTree::Group(y) => {
30 eprintln!("punctuation expected at {:?}", y.span());
31 fail!("while parsing actor");
32 },
33 TokenTree::Ident(y) => {
34 eprintln!("punctuation expected at {:?}", y.span());
35 fail!("while parsing actor");
36 },
37 TokenTree::Literal(y) => {
38 eprintln!("punctuation expected at {:?}", y.span());
39 fail!("while parsing actor");
40 },
41 }
42 },
43 _ => fail!("something expected, found none")
44 }
45 }
46 }
47}
48
49macro_rules! parse_type {
50 ($i:expr) => {
51 {
52 let mut tokens : Vec<TokenTree> = Vec::new();
53 let mut depth = 0;
54 while let Some(e) = $i.next() {
55 match e {
56 TokenTree::Ident(x) => {
57 tokens.push(TokenTree::Ident(x))
58 },
59 TokenTree::Punct(x) => {
60 match x.as_char() {
61 ',' => if depth == 0 {
62 break;
63 },
64 '<' | '(' | '[' | '{' => { depth = depth +1 },
65 '>' | ')' | ']' | '}' => { depth = depth -1 },
66 _ => {} }
68 tokens.push(TokenTree::Punct(x))
69 },
70 TokenTree::Group(x) => tokens.push(TokenTree::Group(x)),
71 _ => {
72 eprintln!("your error is near {:?}", e);
73 fail!("unexpected token in actor state member type");
74 }
75 }
76 }
77 TokenStream::from_iter(tokens.into_iter())
78 }
79 }
80}
81
82macro_rules! get_opt {
83 ($si:expr) => {
84 {
85 let r = if let Some(t) = $si.next() {
86 Some(t.to_string())
87 } else {
88 fail!("expected type");
89 };
90 consume_punct!($si);
91 r
92 }
93 }
94}
95
96macro_rules! parse_options {
97 ($opts:expr,$i:expr) => {
98 {
99 consume_punct!($i);
100 if let Some(TokenTree::Group(block)) = $i.next() {
101 let mut si = block.stream().into_iter();
102 while let Some(e) = si.next() {
103 match e {
104 TokenTree::Ident(i) => {
105 consume_punct!(si);
106 match i.to_string().as_str() {
107 "sup" => {
108 $opts.sup = get_opt!(si);
109 },
110 "id" => {
111 $opts.idtype = Some(parse_type!(si));
112 },
113 _ => fail!("unexpected option")
114 }
115
116 },
117 _ => fail!("bad syntax")
118 }
119 }
120 } else {
121 fail!("expected block");
122 }
123 consume_punct!($i);
124 }
125 }
126}
127macro_rules! get_block {
131 ($i:expr) => {
132 {
133 consume_punct!($i);
134 match $i.next() {
135 Some(TokenTree::Group(block)) => {
136 block.stream().into_iter()
137 },
138 _ => fail!("expected block")
139 }
140 }
141 }
142}
143
144
145macro_rules! parse_state {
146 ($i:expr, $v: expr) => {
147 {
148 let mut ii = get_block!($i);
149 while let Some(e) = ii.next() {
150 match e {
151 TokenTree::Ident(name) => {
152 consume_punct!(ii); $v.push((name.to_string(), parse_type!(ii)));
154 },
155 TokenTree::Punct(_) => {}, _ => fail!("unexpected token in actor state definition")
157 }
158 }
159 }
160 }
161}
162
163macro_rules! parse_messages {
164 ($i:expr, $v:expr) => {
165 {
166 let mut ii = get_block!($i);
167 while let Some(e) = ii.next() {
168 match e {
169 TokenTree::Ident(name) => {
170 match ii.next() {
171 Some(TokenTree::Punct(_)) => {
172 $v.push((name.to_string(), None))
174 },
175 Some(TokenTree::Group(group)) => {
176 $v.push((name.to_string(), Some(<TokenTree>::from(group))))
177 },
178 Some(TokenTree::Ident(i)) => {
179 $v.push((name.to_string(), Some(<TokenTree>::from(i))))
180 }
181 _ => fail!("unexpected token after message identifier")
182 }
183 },
184 TokenTree::Punct(_) => {}, _ => fail!("unexpected token in messages definition")
186 }
187 }
188 }
189 }
190}
191
192#[proc_macro]
260pub fn actor(tokens: TokenStream) -> TokenStream {
261 let mut input = tokens.into_iter();
262 let basename = match input.next() {
263 Some(TokenTree::Ident(i)) => {
264 i.to_string()
265 },
266 x => {
267 eprintln!("w0t ? {:?}", x);
268 fail!("actor needs a name");
269 }
270 };
271
272 let actor_ident = format_ident!("{}Actor", basename);
273 let messages_ident = format_ident!("{}ActorMessages", basename);
274 let state_ident = format_ident!("{}ActorState", basename);
275 let channel_ident = format_ident!("{}ActorChannel", basename);
276
277 let preamble = quote!{
278 #[derive(Debug)]
279 pub struct #actor_ident {}
280 };
281
282 let mut opts = Opts{
283 sup: None,
284 idtype: None,
285 };
286
287 let mut messages_structs = quote!{};
288 let mut messages_block = quote!{};
289 let mut extra_state : Vec<(String,TokenStream)> = Vec::new();
290 let mut messages : Vec<(String, Option<TokenTree>)> = Vec::new();
291 let mut messages_match_block = quote!{};
292
293 while let Some(e) = input.next() {
294 match e {
295 TokenTree::Ident(i) => {
296 match i.to_string().as_str() {
297 "Options" => parse_options!(opts, input),
298 "Messages" => parse_messages!(input, messages),
299 "State" => parse_state!(input, extra_state),
300 _ => fail!("unexpected key in actor")
301 }
302 },
303 TokenTree::Punct(_) => {}, _ => {
305 eprintln!("unexpected {:?}", e);
306 fail!("while parsing actor");
307 }
308 }
309 }
310
311 let mut start_params = quote! {};
312 let mut state_opts_block = quote! {};
313 let mut state_init_opts_block = quote! {};
314 let mut init_if_state = quote! {};
315
316 if let Some(t) = opts.sup {
317 let mt = format_ident!("{}ActorMessages", t);
318 start_params = quote! { #start_params , sup_ch: mpsc::Sender<#mt> };
319 state_opts_block = quote! { #state_opts_block sup_ch: Option<mpsc::Sender<#mt>>, };
320 state_init_opts_block = quote! { #state_init_opts_block sup_ch: Some(sup_ch), };
321 };
322
323 if let Some(t) = opts.idtype {
324 #[allow(unused_assignments)]
325 let mut bt = quote! {};
326 bt = t.into();
327 start_params = quote! { #start_params , id: &#bt };
328 state_opts_block = quote! { #state_opts_block id: #bt, };
329 state_init_opts_block = quote! { #state_init_opts_block id: id.clone(), };
330 };
331
332 for (name, tree) in messages {
333 let n = format_ident!("{}", name);
334 let mut sn = format_ident!("{}{}", messages_ident, name);
335 let hn = format_ident!("handle_{}", name.to_lowercase());
336 #[allow(unused_assignments)]
337 let mut x = quote!{};
338
339 match tree {
340 Some(v) => {
341 match v {
342 TokenTree::Ident(i) => {
343 sn = format_ident!("{}", i.to_string());
344 },
345 TokenTree::Group(g) => {
346 x = g.stream().into(); messages_structs = quote! { #messages_structs
348 #[derive(Debug)]
349 pub struct #sn { #x }
350 };
351 },
352 _ => {
353 }
355 }
356 messages_block = quote! { #messages_block #n(#sn), };
357 messages_match_block = quote! { #messages_match_block
358 #messages_ident::#n(payload) => self.#hn(state, payload).await,
359 }
360 },
361 None => {
362 messages_block = quote! { #messages_block #n, };
363 messages_match_block = quote! { #messages_match_block
364 #messages_ident::#n => self.#hn(state).await,
365 }
366 }
367 }
368 }
369
370 if extra_state.len() > 0 {
371 init_if_state = quote! { self.init(&mut state); };
372 }
373 for (name, typ) in extra_state {
374 let n = format_ident!("{}", name);
375 #[allow(unused_assignments)]
376 let mut t = quote! {};
377 t = typ.into();
378 state_opts_block = quote! { #state_opts_block #n : Option<#t> , };
379 state_init_opts_block = quote! { #state_init_opts_block #n : None , };
380 }
381
382 quote!{
383 #preamble
384
385 pub type #channel_ident = mpsc::Sender<#messages_ident>;
386
387 #messages_structs
388
389 #[derive(Debug)]
390 pub enum #messages_ident {
391 #messages_block
392 }
393
394 #[derive(Debug)]
395 struct #state_ident {
396 rx: mpsc::Receiver<#messages_ident>,
397 tx: #channel_ident,
398 #state_opts_block
399 }
400
401 impl #actor_ident {
402 pub fn start(mut self #start_params) -> #channel_ident {
403 let (tx, rx) = mpsc::channel(100);
404 let mut state = #state_ident {
405 rx: rx,
406 tx: tx.clone(),
407 #state_init_opts_block
408 };
409 #init_if_state
410 tokio::spawn(async move {
411 while let Some(msg) = state.rx.recv().await {
412 self.handle(&mut state, msg).await;
413 }
414 });
415 tx
416 }
417
418 async fn handle(&self, state: &mut #state_ident, msg: #messages_ident) -> () {
419 match msg {
420 #messages_match_block
421 }
422 ()
423 }
424 }
425 }.into()
426}
427
428