murray/
lib.rs

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                            _ => {} // neutral punct : 
67                        } 
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}
127// Group { delimiter: Brace, stream: TokenStream [Ident { ident: "sup", span: #0 bytes(697..700) }, Punct { ch: ':', spacing: Alone, span: #0 bytes(700..701) }, Ident { ident: "None", span: #0 bytes(702..706) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(706..707) }, Ident { ident: "id", span: #0 bytes(716..718) }, Punct { ch: ':', spacing: Alone, span: #0 bytes(718..719) }, Ident { ident: "u32", span: #0 bytes(720..723) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(723..724) }], span: #0 bytes(687..730) }
128//Some(Group { delimiter: Brace, stream: TokenStream [Ident { ident: "A", span: #0 bytes(756..757) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(757..758) }, Ident { ident: "B", span: #0 bytes(76 7..768) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "x", span: #0 bytes(783..784) }, Punct { ch: ':', spacing: Alone, span: #0 bytes(784..785) }, Punct { ch: '&', spacing: Alone , span: #0 bytes(786..787) }, Ident { ident: "str", span: #0 bytes(787..790) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(790..791) }], span: #0 bytes(769..801) }, Punct { ch: ',', spacing: Al one, span: #0 bytes(801..802) }], span: #0 bytes(746..808) }) unexpected Punct { ch: ',', spacing: Alone, span: #0 bytes(808..809) }
129
130macro_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); // :
153                        $v.push((name.to_string(), parse_type!(ii)));
154                    },
155                    TokenTree::Punct(_) => {}, // ignore , 
156                    _ => 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                                // bare variant
173                                $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(_) => {}, // ignore trailing ,
185                    _ => fail!("unexpected token in messages definition")
186                }
187            }
188        }
189    }
190}
191
192/// Define an actor and it's related structures and types.
193///
194/// # Usage
195///
196/// 
197///```
198///actor! {
199///    Foo,
200///    Messages: {
201///        Msg1,
202///        Msg2,
203///        Msg3 { ch: mpsc::Receiver<String> }
204///    }
205///}
206///
207///actor! {
208///    Bar,
209///    Options: {
210///        sup: Foo,
211///        id: String,
212///    },
213///    Messages: {
214///        A,
215///        B {
216///            x: bool,
217///        },
218///    },
219///    State: {
220///        foo: TypeC,
221///    }
222///}
223///
224///impl FooActor {
225///    async fn handle_msg1(&self, state: &mut FooActorState)  {
226///	...
227///    }
228///    async fn handle_msg2(&self, state: &mut FooActorState)  {
229///	...
230///    }
231///    async fn handle_msg3(&self, state: &mut FooActorState, msg: FooActorMessagesMsg3)  {
232///	...
233///    }
234///}
235///
236///impl BarActor {
237///    async fn handle_a(&self, state: &mut BarActorState)  {
238///	...
239///    }
240///    async fn handle_b(&self, state: &mut BarActorState, msg: BarActorMessagesB)  {
241///	...
242///    }
243///
244///    fn init(&self, state: &mut BarActorState) {
245///      ...
246///    }
247///}
248///
249///let sup = FooActor{}.start();
250///let id = String::from("abar");
251///let abar = BarActor{}.start(sup, &id);
252///
253///abar.send(BarActorMessages::B(true));
254///
255///```
256///
257/// Check the README for more details
258///
259#[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(_) => {}, // ignore commas at this level
304            _ => {
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(); // coerse into token_macro2
347                            messages_structs = quote! { #messages_structs 
348                                #[derive(Debug)]
349                                pub struct #sn { #x } 
350                            };
351                        },
352                        _ => {
353                            // unreachable
354                        }
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