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}