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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! gentian is a proc macro that transforms generators to state machines.
//! Currently it supports loop statements, while statements, if statements, and the extended syntax for using `co_yield` and `co_return` and `return` in these statements.
//! # `gentian_attr` attribute of a function
//!  It has two kinds of parameters,
//!  * `state` represents the state currently used to maintain the automaton.
//!  * `ret_val` represents the default return value of the function, which is usually used for the result returned by calling again after the state machine ends.
//!
//! # `co_yield` or `co_return` statement
//!  This divides into three logical steps:
//!  * `co_yield` or `co_return` save the current state of the coroutine.
//!  * The resume point is defined immediately following the statement.
//!  * Same as rust `return` semantics, it returns from the function immediately.
//!
//! # `co_await` statement
//!  It's a syntax sugar for `co_yield` or `co_return` with `std::task::Poll`.
//!  ````ignore
//!  co_await(some_poll_func());
//!  ````
//!  is equivalent to
//!  ````ignore
//!  loop{
//!    let tmp=some_poll_func();
//!    if tmp.is_ready(){
//!        break;
//!    }
//!    co_yield(Poll::Pending);
//!  }
//!  ````
//!  This divides into two logical steps:
//!  * `co_await` save the current state of the coroutine.
//!  * The resume point is defined immediately following the statement and if and only if the waited poll function is ready.
//!
//! # `return` statement
//!  This type of statement divides into two logical steps:
//!  * `return` sets the coroutine state to indicate termination.
//!  * Same as rust `return` semantics, it returns from the function immediately.
//!  * When the function is called again, it returns the default return value (`ret_val`), or does nothing which means the function has no return value.
//!
//! # Example
//! The following code demonstrates the use of generators with and without a return value.
//! ````rust
//! use gentian::gentian;
//!
//! #[cfg(test)]
//! struct MyGenerator {
//!     my_state_1: usize,
//!     pub my_state_2: usize,
//!     pub num: u32,
//!     pub num1: u32,
//! }
//!
//! #[cfg(test)]
//! impl MyGenerator {
//!     pub fn new() -> MyGenerator {
//!         MyGenerator {
//!             my_state_1: 0,
//!             my_state_2: 0,
//!             num: 0,
//!             num1: 0,
//!         }
//!     }
//!
//!     #[gentian]
//!     #[gentian_attr(state=self.my_state_1)]
//!     pub fn test_simple(&mut self) {
//!         loop {
//!             println!("Hello, ");
//!             //co_yield;
//!             while self.num1 < 99 {
//!                 println!("Generator{}", self.num1);
//!                 self.num1 += 1;
//!                 co_yield;
//!             }
//!             return;
//!         }
//!     }
//!
//!     // state_name , return_default_value
//!     #[gentian]
//!     #[gentian_attr(state=self.my_state_2,ret_val=0u32)]
//!     pub fn get_odd(&mut self) -> u32 {
//!         loop {
//!             if self.num % 2 == 1 {
//!                 co_yield(self.num);
//!             }
//!             self.num += 1;
//!         }
//!     }
//! }
//!
//! #[test]
//! fn test_generator_proc_macro() {
//!     let mut gen = MyGenerator::new();
//!     gen.test_simple(); // print Hello,
//!     for _ in 0..200 {
//!         gen.test_simple(); // only print 99 times `Generator`
//!     }
//!     gen.test_simple(); // print nothing
//!     assert_eq!(gen.num1, 99);
//!     for i in (1u32..1000).step_by(2) {
//!         assert_eq!(gen.get_odd(), i);
//!     }
//! }
//!
//! ````
//!
mod attr;
mod control_flow_graph;
mod generate_state_machines;
mod stmt;
mod test;

use crate::attr::GentianAttr;
use generate_state_machines::Generator;
use proc_macro::TokenStream;
use syn::parse_macro_input;
use syn::ItemFn;

#[proc_macro_attribute]
pub fn gentian(_args: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemFn);
    let expanded = transform_to_state_machine(input);
    TokenStream::from(expanded)
}

fn transform_to_state_machine(mut input: syn::ItemFn) -> proc_macro2::TokenStream {
    let mut attrs: Option<GentianAttr> = None;
    if !input.attrs.is_empty() {
        if let Ok(a) = GentianAttr::try_from_attributes(&input.attrs) {
            attrs = a;
        }
    }
    let state_name: String;
    let ret_val: String;
    if let Some(attr) = &attrs {
        state_name = attr.get_state_name();
        ret_val = attr.get_ret_val();
    } else {
        state_name = "self.state".to_string();
        ret_val = String::new();
    }
    input.attrs.clear();
    if ret_val.is_empty() {
        println!(
            "[gentian] found function `{}` state name: {}.",
            input.sig.ident, state_name
        );
    } else {
        println!(
            "[gentian] found function `{}` state name: {}, function default return value is: {}.",
            input.sig.ident, state_name, ret_val
        );
    }
    let mut generator = Generator::new();
    let expanded = generator.gen_state_machines_tokenstream(input, &state_name, &ret_val);
    return expanded;
}