yerevan/
yer_macro.rs

1//! Module containing the [`crate::yer`] macro.
2
3/// `yerevan.rs` macros for fancy-shmancy syntax for computation expressions.
4///
5/// Example:
6/// ```rust
7/// use yerevan::yer;
8///
9/// // Some simple user-defined structs for compuation expressions
10/// struct SimpleBinder {}
11/// impl SimpleBinder {
12///     pub fn bind<T, U>(val: Option<T>, f: &dyn Fn(T) -> Option<U>) -> Option<U> {
13///         match val {
14///             Some(v) => f(v),
15///             None => SimpleBinder::zero(),
16///         }
17///     }
18///     pub fn ret<T>(val: T) -> Option<T> {
19///         Some(val)
20///     }
21///     pub fn zero<T>() -> Option<T> {
22///         None
23///     }
24/// }
25///
26/// struct Incrementer {}
27/// impl Incrementer {
28///     pub fn bind(val: i32, f: &dyn Fn(i32) -> i32) -> i32 {
29///         f(val + 1)
30///     }
31///     pub fn ret(val: i32) -> i32 {
32///         val
33///     }
34/// }
35///
36/// pub fn showcase(wrapped1: Option<i32>, wrapped2: Option<&str>) -> bool {
37///     let from_macro = yer!(
38///         SimpleBinder =>
39///         let! unwrapped1 = wrapped1;
40///         let! unwrapped2 = wrapped2;
41///         let one = 1;
42///         Incrementer =>
43///         let! res = one + unwrapped1 + (unwrapped2.len() as i32);
44///         ret res
45///     );
46///
47///     let by_hand =
48///         SimpleBinder::bind(
49///             wrapped1, &|unwrapped1| {
50///                 SimpleBinder::bind(
51///                     wrapped2, &|unwrapped2| {
52///                         let one = 1;
53///                         SimpleBinder::ret(
54///                             Incrementer::bind(
55///                                 one + unwrapped1 + (unwrapped2.len() as i32), &|res| {
56///                                     Incrementer::ret(res)
57///                                 }
58///                             )
59///                         )
60///                     }
61///                 )
62///             }
63///         );
64///
65///     from_macro == by_hand // true
66/// }
67/// ```
68
69#[allow(unused_macros)]
70#[macro_export]
71macro_rules! yer {
72    // let!
73    (
74        $struct_name:ident =>
75        let! $var_name:ident$(: $var_type:ty)? = $expression:expr;
76        $($tail:tt)*
77    ) => {
78        $struct_name::bind($expression, &|$var_name $(: $var_type)?| {
79            yer!($struct_name => $($tail)*)
80        })
81    };
82
83    // do!
84    (
85        $struct_name:ident =>
86        do! $expression:expr;
87        $($tail:tt)*
88    ) => {
89        $struct_name::bind($expression, &|_| {
90            yer!($struct_name => $($tail)*)
91        })
92    };
93
94    // let
95    (
96        $struct_name:ident =>
97        let $var_name:ident $(: $var_type:ty)? = $expression:expr;
98        $($tail:tt)*
99    ) => {
100        {
101            let $var_name $(: $var_type)? = $expression;
102            (yer!($struct_name => $($tail)*))
103        }
104    };
105
106    // do
107    (
108        $struct_name:ident =>
109        do $expression:expr;
110        $($tail:tt)*
111    ) => {
112        {
113            $expression;
114            yer!($struct_name => $($tail)*)
115        }
116    };
117
118    // ret with generics
119    ( $struct_name:ident => ret <$($gtype:ty),+> $expression:expr ) => {
120        $struct_name::ret::<$($gtype),+>($expression)
121    };
122    // ret
123    ( $struct_name:ident => ret $expression:expr ) => {
124        $struct_name::ret($expression)
125    };
126
127    // ret!
128    ( $struct_name:ident => ret! $expression:expr ) => {
129        $struct_name::ret_from($expression)
130    };
131    // ret! with generics
132    ( $struct_name:ident => ret! <$($gtype:ty),+> $expression:expr ) => {
133        $struct_name::ret_from::<$($gtype),+>($expression)
134    };
135
136    // yield! as return (last yield)
137    (
138        $struct_name:ident =>
139        yield! $expression:expr;
140    ) => {
141        $struct_name::ret_yield_from($expression)
142    };
143
144    // combined yield!
145    (
146        $struct_name:ident =>
147        yield! $expression:expr;
148        $($tail:tt)*
149    ) => {
150        $struct_name::combine(
151            yer!($struct_name => $($tail)*),
152            $struct_name::ret_yield_from($expression)
153        )
154    };
155
156    // yield as return (last yield)
157    (
158        $struct_name:ident =>
159        yield $expression:expr;
160    ) => {
161        $struct_name::ret_yield($expression)
162    };
163
164    // combined yield
165    (
166        $struct_name:ident =>
167        yield $expression:expr;
168        $($tail:tt)*
169    ) => {
170        $struct_name::combine(
171            yer!($struct_name => $($tail)*),
172            $struct_name::ret_yield($expression)
173        )
174    };
175
176    // running CE with run method
177    ( run $struct_name:ident => $($tail:tt)* ) => {
178        $struct_name::run({yer!($struct_name => $($tail)*)})
179    };
180    ( $struct_name:ident >> $($tail:tt)* ) => {
181        $struct_name::run({yer!($struct_name => $($tail)*)})
182    };
183
184    // changing the CE-functions provider type
185    ( $previous_struct_name:ident => $struct_name:ident => $($tail:tt)* ) => {
186        $previous_struct_name::ret(yer!($struct_name => $($tail)*))
187    };
188    ( $previous_struct_name:ident => $struct_name:ident! => $($tail:tt)* ) => {
189        $previous_struct_name::ret_from(yer!($struct_name => $($tail)*))
190    };
191
192    // If-branching
193    (
194        $struct_name:ident =>
195        if ($if_expr:expr) {
196            $($if_block:tt)*
197        } else zero;
198        $($tail:tt)*
199    ) => {
200        {
201            $struct_name::combine(
202                yer!($struct_name => $($tail)*),
203                if $if_expr {
204                    yer!(
205                        $struct_name =>
206                        $($if_block)*
207                    )
208                }
209                else { $struct_name::zero() }
210            )
211        }
212    };
213
214    (
215        $struct_name:ident =>
216        if ($if_expr:expr) {
217            $($if_block:tt)*
218        }
219        $( else if ($else_if_expr:expr) {
220            $($else_if_block:tt)*
221        })*
222        else {
223            $($else_block:tt)*
224        }
225        $($tail:tt)*
226    ) => {
227        {
228            $struct_name::combine(
229                yer!($struct_name => $($tail)*),
230                if $if_expr {
231                    yer!(
232                        $struct_name =>
233                        $($if_block)*
234                    )
235                }
236                $(else if $else_if_expr {
237                    yer!($struct_name => $($else_if_block)*)
238                })*
239                else {
240                    yer!($struct_name => $($else_block)*)
241                }
242            )
243        }
244    };
245
246    // exit-point
247    ( $struct_name:ident => ) => { };
248}