iter_python/macros.rs
1pub use ::join_lazy_fmt::lazy_format;
2
3/// (shortname: `i!`) — Write the most pervasive iterator adapters
4/// ([filter]ing and [map]ping) as [Python generator expressions].
5///
6/// # Examples
7///
8/// ### Squaring even numbers
9///
10/// ```rust
11/// use ::iter_python::prelude::*;
12///
13/// let mut all_evens_squared = i!(
14/// x * x
15/// for x in (0 ..)
16/// if x % 2 == 0
17/// );
18/// assert_eq!(all_evens_squared.next(), Some(0));
19/// assert_eq!(all_evens_squared.next(), Some(4));
20/// assert_eq!(all_evens_squared.next(), Some(16));
21/// ```
22///
23/// ### `filter`ing is optional, such as in Python:
24///
25/// ```rust
26/// use ::iter_python::prelude::*;
27///
28/// let mut numbers_binary = i!(format!("{:02b}", x) for x in 1 ..= 3);
29///
30/// assert_eq!(numbers_binary.next(), Some("01".into()));
31/// assert_eq!(numbers_binary.next(), Some("10".into()));
32/// assert_eq!(numbers_binary.next(), Some("11".into()));
33/// assert_eq!(numbers_binary.next(), None);
34/// ```
35///
36/// ### You may also `filter` with `if let`:
37///
38/// ```rust
39/// use ::iter_python::prelude::*;
40///
41/// let strings = ["42", "0", "zero", "27"];
42///
43/// let parsed_as_i32s = i!(s.parse::<i32>() for &s in &strings);
44///
45/// let total: i32 = Iterator::sum(i!(
46/// x
47/// for res in parsed_as_i32s
48/// if let Ok(x) = res
49/// ));
50///
51/// assert_eq!(total, 42 + 0 + 27);
52/// ```
53///
54/// ```rust
55/// use ::iter_python::prelude::*;
56///
57/// enum Fruit { Banana, Peach, RottenApple }
58/// use Fruit::*;
59///
60/// impl Fruit {
61/// fn is_fresh (&self) -> bool
62/// {
63/// if let RottenApple = self {
64/// false
65/// } else {
66/// true
67/// }
68/// }
69/// }
70///
71/// static BASKET: &[Fruit] = &[Banana, RottenApple, Peach, Banana];
72///
73/// let no_rotten_apple = i!(
74/// fruit
75/// for fruit in BASKET
76/// if let Banana | Peach = fruit
77/// );
78///
79/// assert!({no_rotten_apple}.all(Fruit::is_fresh));
80/// ```
81///
82/// ### You can also nest / combine iterators
83///
84/// ```rust
85/// use ::iter_python::prelude::*;
86///
87/// assert_eq!(
88/// i!((i, j) for i in 0 .. 3 for j in 0 .. 2).vec(),
89/// vec![
90/// (0, 0),
91/// (0, 1),
92/// (1, 0),
93/// (1, 1),
94/// (2, 0),
95/// (2, 1),
96/// ],
97/// );
98/// ```
99///
100/// #### With the same `if` guards as with the single case:
101///
102/// ```rust
103/// use ::iter_python::prelude::*;
104///
105/// assert_eq!(
106/// i!(
107/// (i, j)
108/// for i in 0 .. 4
109/// if i != 2
110/// for j in 0 .. i
111/// if j != 1
112/// ).vec(),
113/// vec![
114/// (1, 0),
115/// (3, 0),
116/// (3, 2),
117/// ]
118/// )
119/// ```
120/// [filter]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.filter
121/// [map]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.map
122/// [Python generator expressions]: https://www.python.org/dev/peps/pep-0289
123/// [`if let`]: https://doc.rust-lang.org/book/if-let.html
124#[macro_export]
125macro_rules! iter {
126 (
127 $($input:tt)*
128 ) => ($crate::__munch_iter_args! {
129 munching[
130 $($input)*
131 ]
132 current_elem[]
133 to_endpoint[]
134 });
135}
136#[doc(inline)]
137pub use iter;
138
139/// Split the input using `for` and `if`.
140#[doc(hidden)] /** Not part of the public API */ #[macro_export]
141macro_rules! __munch_iter_args {
142 // New `for` *nested* (no `if` in between)
143 (
144 munching[
145 for $($rest:tt)*
146 ]
147 current_elem[
148 for $($acc_for:tt)*
149 ]
150 to_endpoint[
151 $($out:tt)*
152 ]
153 ) => ($crate::__munch_iter_args! {
154 munching[
155 $($rest)*
156 ]
157 current_elem[ for ]
158 to_endpoint[
159 $($out)*
160 [for $($acc_for)*]
161 [] // encountered `for` after `for` => no `if` guard
162 ]
163 });
164
165 // New `for`
166 (
167 munching[
168 for $($rest:tt)*
169 ]
170 current_elem $current_elem:tt
171 to_endpoint[
172 $($out:tt)*
173 ]
174 ) => ($crate::__munch_iter_args! {
175 munching[
176 $($rest)*
177 ]
178 current_elem[ for ]
179 to_endpoint[
180 $($out)*
181 $current_elem
182 ]
183 });
184
185 // New `if`
186 (
187 munching[
188 if $($rest:tt)*
189 ]
190 current_elem $current_elem:tt
191 to_endpoint[
192 $($out:tt)*
193 ]
194 ) => ($crate::__munch_iter_args! {
195 munching[
196 $($rest)*
197 ]
198 current_elem[ if ]
199 to_endpoint[
200 $($out)*
201 $current_elem
202 ]
203 });
204
205 // Neither `for` nor `if`: just accumulate that `:tt`
206 (
207 munching[
208 $current_tt:tt $($rest:tt)*
209 ]
210 current_elem[
211 $($current_elem:tt)*
212 ]
213 to_endpoint $to_endpoint:tt
214 ) => ($crate::__munch_iter_args! {
215 munching[
216 $($rest)*
217 ]
218 current_elem[
219 $($current_elem)* $current_tt
220 ]
221 to_endpoint $to_endpoint
222 });
223
224 // END: nothing left to munch
225 (
226 munching[]
227 current_elem $current_elem:tt
228 to_endpoint[
229 $($out:tt)*
230 ]
231 ) => ($crate::__endpoint! {
232 $($out)*
233 $current_elem
234 })
235}
236
237#[doc(hidden)] /** Not part of the public API */ #[macro_export]
238macro_rules! __endpoint {
239 // Non-last `for`-&-`if`
240 (
241 $mapped_expr:tt
242 [for $var:pat in $iterable:expr]
243 [if $($filter:tt)*]
244 $($rest:tt)+
245 ) => (
246 $crate::__::core::iter::Iterator::flat_map(
247 $crate::__::core::iter::IntoIterator::into_iter(
248 $iterable
249 ),
250 move |$var| {
251 $crate::__::core::iter::Iterator::flatten(
252 $crate::__::core::iter::IntoIterator::into_iter(
253 if $($filter)* {
254 $crate::__::core::option::Option::Some(
255 $crate::__endpoint!(
256 $mapped_expr
257 $($rest)*
258 )
259 )
260 } else {
261 $crate::__::core::option::Option::None
262 }
263 )
264 )
265 },
266 )
267 );
268
269 // Non-last `for` (no `if`!)
270 (
271 $mapped_expr:tt
272 [for $var:pat in $iterable:expr]
273 [/* no filter */]
274 $($rest:tt)+
275 ) => (
276 $crate::__::core::iter::Iterator::flat_map(
277 $crate::__::core::iter::IntoIterator::into_iter(
278 $iterable
279 ),
280 move |$var| $crate::__endpoint!($mapped_expr $($rest)*),
281 )
282 );
283
284 // Last `for`-&-`if`
285 (
286 [$mapped_expr:expr]
287 [for $var:pat in $iterable:expr]
288 [if $($filter:tt)*]
289 /* no rest */
290 ) => (
291 $crate::__::core::iter::Iterator::filter_map(
292 $crate::__::core::iter::IntoIterator::into_iter(
293 $iterable
294 ),
295 move |$var| if $($filter)* {
296 $crate::__::core::option::Option::Some($mapped_expr)
297 } else {
298 $crate::__::core::option::Option::None
299 },
300 )
301 );
302
303 // Last `for` (no `if`!)
304 (
305 [$mapped_expr:expr]
306 [for $var:pat in $iterable:expr]
307 $([/* no filter */])?
308 /* no rest */
309 ) => (
310 $crate::__::core::iter::Iterator::map(
311 $crate::__::core::iter::IntoIterator::into_iter(
312 $iterable
313 ),
314 move |$var| $mapped_expr,
315 )
316 );
317}
318
319/// (shortname: `v!`) — [Python "list" comprehensions]: same as [`i!`],
320/// but [`collect`]ed into a [`Vec`] instead.
321///
322/// # `v!` or `vec!`?
323///
324/// **[`v!`] fallbacks to [`::std::vec!`] functionality**,
325/// thus allowing maximum compatiblity!
326///
327/// ### Example
328/// ```rust
329/// use ::iter_python::macros::vec;
330///
331/// let v1 = vec![i for i in 1 ..= 4];
332/// let v2 = vec![1, 2, 3, 4];
333/// assert_eq!(v1, v2);
334/// ```
335///
336/// It has not been named `vec` to prevent lints against ambiguous blob imports.
337///
338/// [`i!`]: `iter`
339/// [`collect`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.collect
340/// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
341/// [Python "list" comprehensions]: https://www.python.org/dev/peps/pep-0202
342/// [`v!`]: `vec`
343/// [`::std::vec!`]: https://doc.rust-lang.org/std/macro.vec.html
344#[cfg(feature = "std")]
345#[cfg_attr(feature = "better-docs",
346 doc(cfg(feature = "std")),
347)]
348#[macro_export]
349macro_rules! v {
350 // std syntax compat.
351 (
352 $($e:expr),* $(,)?
353 ) => (
354 $crate::__::std::vec! { $($e),* }
355 );
356
357 // std syntax compat.
358 (
359 $e:expr ; $count:expr
360 ) => (
361 $crate::__::std::vec! { $e; $count }
362 );
363
364 (
365 $($otherwise:tt)*
366 ) => (
367 <
368 $crate::__::std::vec::Vec<_>
369 as
370 $crate::__::core::iter::FromIterator<_>
371 >::from_iter(
372 $crate::macros::iter!( $($otherwise)* )
373 )
374 );
375}
376
377#[cfg(feature = "std")]
378#[cfg_attr(feature = "better-docs",
379 doc(cfg(feature = "std")),
380)]
381#[doc(inline)]
382pub use crate::v as vec;