├── .gitignore ├── Cargo.toml ├── Changelog.md ├── Readme.md ├── src └── lib.rs └── tests ├── test.rs └── test_if_chains ├── mod.rs ├── test_initial_if_in_final_for.rs ├── test_initial_if_in_non_final_for.rs ├── test_initial_if_let_in_final_for.rs └── test_initial_if_let_in_non_final_for.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "py-comp" 3 | version = "0.1.3" 4 | edition = '2018' 5 | authors = ["Reuven Podmazo "] 6 | license = "MIT" 7 | description = "A macro implementing a Python-like generator expression" 8 | repository = "https://github.com/reuvenpo/rust-py-comp/" 9 | readme = "Readme.md" 10 | keywords = ["generator", "comprehension", "comp", "list", "python"] 11 | categories = ["rust-patterns"] 12 | 13 | [dependencies] 14 | doc-comment = "0.3.0" 15 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 0.1.3 4 | 5 | * Fixed bug preventing usage of `IntoIterator`s which are not copy 6 | * Added clarification to Readme.md about `Copy` requirements of 7 | captured objects 8 | * Added support for `if let` clauses 9 | * Added support for consecutive `if` and `if let` clauses such as 10 | 11 | ```rust 12 | comp!(x; for x in 0..10; if x > 3; if x < 6) 13 | ``` 14 | 15 | ## Version 0.1.2 16 | 17 | * Readme.md examples are now tested 18 | * The trailing semicolon after the last expression in the macro is now optional 19 | * Added nested-structure example to documentation 20 | 21 | ## Version 0.1.1 22 | 23 | * Added metadata to Readme.md to show more info on crates.io 24 | 25 | ## Version 0.1.0 26 | 27 | * Initial release 28 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # py-comp - A Rust macro implementing a Python-like generator expression 2 | 3 | [![Latest Version]][crates.io] [![Documentation]][docs.rs] ![License] 4 | 5 | [crates.io]: https://crates.io/crates/py-comp 6 | [Latest Version]: https://img.shields.io/crates/v/py-comp.svg 7 | [Documentation]: https://docs.rs/py-comp/badge.svg 8 | [docs.rs]: https://docs.rs/py-comp 9 | [License]: https://img.shields.io/crates/l/py-comp.svg 10 | 11 | This macro implements a syntax that emulates Python's 12 | [`generator-expression`] syntax in a form more compatible with Rust's 13 | usual syntax. 14 | 15 | This means that there are a few small differences between the Python syntax 16 | and the syntax provided in this macro: 17 | 18 | * The pattern between the `for` and `in` tokens is a fully-fledged 19 | Rust pattern, which can be as simple as a simple token and as complex 20 | as struct destructuring. 21 | * The expression defining the iterator after the `in` token 22 | must evaluate to either an `Iterator` or an `impl IntoIterator`. 23 | * The conditional expression after the `if` token must evaluate to 24 | a boolean. 25 | * You may use an `if let` clause instead of the usual `if` clause wherever 26 | `if` clauses are allowed. Any names introduced in the `if let` clause 27 | are available in any following clause. 28 | * The expression in the beginning of the generator expression, 29 | the expression following the `in` token, and the expression following 30 | the `if` token, must all end with a semicolon (;). The only exception 31 | to this is the last expression following `in` or `if` in the macro, 32 | which may omit the trailing semicolon. 33 | 34 | The expression replaced by the `comp!()` macro invocation is a lazy 35 | iterator whose lifetime is bound by any references it needs to capture. 36 | This means that it can be `.collect()`ed into any container you like. 37 | 38 | Note though that, at least for now, all objects named in an `in` clause, 39 | (except for the first `in` clause) must be either `Copy` or introduced by 40 | the previous `for` or `if let` clauses. This is because the macro uses a 41 | `move` closure (`FnOnce`) for each level of nesting, which may need to be 42 | instantiated more than once without implicit cloning of the captured 43 | objects. 44 | Similarly, objects named in the "yield" expression (preceding the first 45 | `for` clause) must be `Copy` types if they were not introduced by the final 46 | `for` or `if let` clauses. This is because they may be used in multiple 47 | output items. 48 | 49 | Specifying which objects should be cloned and where may be added in the 50 | future, but will probably require a breaking change. 51 | 52 | This is a BNF description of the syntax used by this macro: 53 | 54 | ```bnf 55 | comprehension ::= expression ";" comp_for [comp_iter] [";"] 56 | comp_iter ::= ";" (comp_for | comp_if | comp_if_let) 57 | comp_for ::= "for" pattern "in" expression [comp_iter] 58 | comp_if ::= "if" expression [comp_iter] 59 | comp_if_let ::= "if" "let" pattern ("|" pattern)* "=" expression [comp_iter] 60 | ``` 61 | 62 | Just like in Python, you can nest as many `for`, `if`, and `if let` 63 | clauses as you like. 64 | 65 | ## Examples 66 | 67 | Simple generator expression with a conditional: 68 | 69 | ```rust 70 | use py_comp::comp; 71 | 72 | #[derive(Debug, PartialEq, Eq)] 73 | struct Foo(i32); 74 | 75 | let arr = &[Foo(11), Foo(12)]; 76 | 77 | // Notice the semicolons 78 | let comp_vector = comp!(item; for item in arr; if item.0 % 10 == 2) 79 | .collect::>(); 80 | 81 | assert_eq!(comp_vector, vec![&Foo(12)]) 82 | ``` 83 | 84 | Triple cartesian product with conditions and patterns: 85 | 86 | ```rust 87 | use py_comp::comp; 88 | 89 | #[derive(Debug, PartialEq, Eq)] 90 | struct Foo(i32); 91 | 92 | // These need to be references to arrays because of how the closures 93 | // that the macro expands to capture their environment. 94 | let x = &[(Foo(11), "foo"), (Foo(12), "bar")]; 95 | let y = &[Foo(21), Foo(22)]; 96 | let z = &[Foo(31), Foo(32)]; 97 | 98 | let xyz = comp!( 99 | (a, b, c); 100 | for (a, _text) in x; // You can use any function parameter pattern. 101 | if a.0 % 10 == 2; 102 | for b in y; // Obviously not every level requires a conditional. 103 | for c in z; 104 | if c.0 % 10 == 2; 105 | ) 106 | .collect::>(); 107 | 108 | // The result vector here is short for illustration purposes 109 | // but can be as long as long as you need it to be. 110 | assert_eq!(xyz, vec![(&Foo(12), &Foo(21), &Foo(32)), (&Foo(12), &Foo(22), &Foo(32))]) 111 | ``` 112 | 113 | Flatten a triple-nested structure + complex expression: 114 | 115 | ```rust 116 | use py_comp::comp; 117 | 118 | #[derive(Debug, PartialEq, Eq)] 119 | struct Foo(i32); 120 | 121 | let nested_3 = &[ 122 | [ 123 | [Foo(0), Foo(1), Foo(2)], 124 | [Foo(3), Foo(4), Foo(5)], 125 | [Foo(6), Foo(7), Foo(8)], 126 | ], 127 | [ 128 | [Foo(9), Foo(10), Foo(11)], 129 | [Foo(12), Foo(13), Foo(14)], 130 | [Foo(15), Foo(16), Foo(17)], 131 | ], 132 | [ 133 | [Foo(18), Foo(19), Foo(20)], 134 | [Foo(21), Foo(22), Foo(23)], 135 | [Foo(24), Foo(25), Foo(26)], 136 | ], 137 | ]; 138 | 139 | let nested_objects = comp!( 140 | { 141 | let inner = nested.0; 142 | Foo(inner + 1) 143 | }; 144 | for nested_2 in nested_3; 145 | for nested_1 in nested_2; 146 | for nested in nested_1; 147 | ) 148 | .collect::>(); 149 | 150 | let expected_values = (1..28).map(Foo).collect::>(); 151 | 152 | assert_eq!(expected_values, nested_objects); 153 | ``` 154 | 155 | [`generator-expression`]: https://docs.python.org/3/reference/expressions.html#generator-expressions 156 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This macro implements a syntax that emulates Python's 2 | //! [`generator-expression`] syntax in a form more compatible with Rust's 3 | //! usual syntax. 4 | //! 5 | //! This means that there are a few small differences between the Python syntax 6 | //! and the syntax provided in this macro: 7 | //! 8 | //! * The pattern between the `for` and `in` tokens is a fully-fledged 9 | //! Rust pattern, which can be as simple as a simple token and as complex 10 | //! as struct destructuring. 11 | //! * The expression defining the iterator after the `in` token 12 | //! must evaluate to either an `Iterator` or an `impl IntoIterator`. 13 | //! * The conditional expression after the `if` token must evaluate to 14 | //! a boolean. 15 | //! * You may use an `if let` clause instead of the usual `if` clause wherever 16 | //! `if` clauses are allowed. Any names introduced in the `if let` clause 17 | //! are available in any following clause. 18 | //! * The expression in the beginning of the generator expression, 19 | //! the expression following the `in` token, and the expression following 20 | //! the `if` token, must all end with a semicolon (;). The only exception 21 | //! to this is the last expression following `in` or `if` in the macro, 22 | //! which may omit the trailing semicolon. 23 | //! 24 | //! The expression replaced by the `comp!()` macro invocation is a lazy 25 | //! iterator whose lifetime is bound by any references it needs to capture. 26 | //! This means that it can be `.collect()`ed into any container you like. 27 | //! 28 | //! Note though that, at least for now, all objects named in an `in` clause, 29 | //! (except for the first `in` clause) must be either `Copy` or introduced by 30 | //! the previous `for` or `if let` clauses. This is because the macro uses a 31 | //! `move` closure (`FnOnce`) for each level of nesting, which may need to be 32 | //! instantiated more than once without implicit cloning of the captured 33 | //! objects. 34 | //! Similarly, objects named in the "yield" expression (preceding the first 35 | //! `for` clause) must be `Copy` types if they were not introduced by the final 36 | //! `for` or `if let` clauses. This is because they may be used in multiple 37 | //! output items. 38 | //! 39 | //! Specifying which objects should be cloned and where may be added in the 40 | //! future, but will probably require a breaking change. 41 | //! 42 | //! This is a BNF description of the syntax used by this macro: 43 | //! 44 | //! ```bnf 45 | //! comprehension ::= expression ";" comp_for [comp_iter] [";"] 46 | //! comp_iter ::= ";" (comp_for | comp_if | comp_if_let) 47 | //! comp_for ::= "for" pattern "in" expression [comp_iter] 48 | //! comp_if ::= "if" expression [comp_iter] 49 | //! comp_if_let ::= "if" "let" pattern ("|" pattern)* "=" expression [comp_iter] 50 | //! ``` 51 | //! 52 | //! Just like in Python, you can nest as many `for`, `if`, and `if let` 53 | //! clauses as you like. 54 | //! 55 | //! ## Examples 56 | //! 57 | //! Simple generator expression with a conditional: 58 | //! 59 | //! ```rust 60 | //! use py_comp::comp; 61 | //! 62 | //! #[derive(Debug, PartialEq, Eq)] 63 | //! struct Foo(i32); 64 | //! 65 | //! let arr = &[Foo(11), Foo(12)]; 66 | //! 67 | //! // Notice the semicolons 68 | //! let comp_vector = comp!(item; for item in arr; if item.0 % 10 == 2) 69 | //! .collect::>(); 70 | //! 71 | //! assert_eq!(comp_vector, vec![&Foo(12)]) 72 | //! ``` 73 | //! 74 | //! Triple cartesian product with conditions and patterns: 75 | //! 76 | //! ```rust 77 | //! use py_comp::comp; 78 | //! 79 | //! #[derive(Debug, PartialEq, Eq)] 80 | //! struct Foo(i32); 81 | //! 82 | //! // These need to be references to arrays because of how the closures 83 | //! // that the macro expands to capture their environment. 84 | //! let x = &[(Foo(11), "foo"), (Foo(12), "bar")]; 85 | //! let y = &[Foo(21), Foo(22)]; 86 | //! let z = &[Foo(31), Foo(32)]; 87 | //! 88 | //! let xyz = comp!( 89 | //! (a, b, c); 90 | //! for (a, _text) in x; // You can use any function parameter pattern. 91 | //! if a.0 % 10 == 2; 92 | //! for b in y; // Obviously not every level requires a conditional. 93 | //! for c in z; 94 | //! if c.0 % 10 == 2; 95 | //! ) 96 | //! .collect::>(); 97 | //! 98 | //! // The result vector here is short for illustration purposes 99 | //! // but can be as long as long as you need it to be. 100 | //! assert_eq!(xyz, vec![(&Foo(12), &Foo(21), &Foo(32)), (&Foo(12), &Foo(22), &Foo(32))]) 101 | //! ``` 102 | //! 103 | //! Flatten a triple-nested structure + complex expression: 104 | //! 105 | //! ```rust 106 | //! use py_comp::comp; 107 | //! 108 | //! #[derive(Debug, PartialEq, Eq)] 109 | //! struct Foo(i32); 110 | //! 111 | //! let nested_3 = &[ 112 | //! [ 113 | //! [Foo(0), Foo(1), Foo(2)], 114 | //! [Foo(3), Foo(4), Foo(5)], 115 | //! [Foo(6), Foo(7), Foo(8)], 116 | //! ], 117 | //! [ 118 | //! [Foo(9), Foo(10), Foo(11)], 119 | //! [Foo(12), Foo(13), Foo(14)], 120 | //! [Foo(15), Foo(16), Foo(17)], 121 | //! ], 122 | //! [ 123 | //! [Foo(18), Foo(19), Foo(20)], 124 | //! [Foo(21), Foo(22), Foo(23)], 125 | //! [Foo(24), Foo(25), Foo(26)], 126 | //! ], 127 | //! ]; 128 | //! 129 | //! let nested_objects = comp!( 130 | //! { 131 | //! let inner = nested.0; 132 | //! Foo(inner + 1) 133 | //! }; 134 | //! for nested_2 in nested_3; 135 | //! for nested_1 in nested_2; 136 | //! for nested in nested_1; 137 | //! ) 138 | //! .collect::>(); 139 | //! 140 | //! let expected_values = (1..28).map(Foo).collect::>(); 141 | //! 142 | //! assert_eq!(expected_values, nested_objects); 143 | //! ``` 144 | //! 145 | //! [`generator-expression`]: https://docs.python.org/3/reference/expressions.html#generator-expressions 146 | //! 147 | 148 | #![warn(clippy::all)] 149 | 150 | use doc_comment::doctest; 151 | 152 | doctest!("../Readme.md"); 153 | 154 | /// Check that the type of the expression passed here implements IntoIterator. 155 | #[doc(hidden)] 156 | #[inline(always)] 157 | pub fn __py_comp_assert_impl_into_iter(_: &T) {} 158 | 159 | /// A Python-like lazy generator-expression 160 | /// 161 | /// For details see [module level documentation][super] 162 | /// 163 | /// [super]: ../py_comp/index.html 164 | #[macro_export(local_inner_macros)] 165 | macro_rules! comp { 166 | // @parse_if if 167 | (@parse_if 168 | $item_expr: expr; 169 | if $condition: expr 170 | ) => { 171 | if $condition { 172 | Some($item_expr) 173 | } else { 174 | None 175 | } 176 | }; 177 | 178 | // @parse_if if-let 179 | (@parse_if 180 | $item_expr: expr; 181 | if let $( $if_let_pattern: pat )|+ = $if_let_expr: expr 182 | ) => { 183 | if let $( $if_let_pattern )|+ = $if_let_expr { 184 | Some($item_expr) 185 | } else { 186 | None 187 | } 188 | }; 189 | 190 | // @parse_if if for ... 191 | // This case returns to the main macro parsing. 192 | (@parse_if 193 | $item_expr: expr; 194 | if $condition: expr; 195 | for $($rest: tt)* 196 | ) => { 197 | if $condition { 198 | Some(comp!($item_expr; for $($rest)*)) 199 | } else { 200 | None 201 | } 202 | }; 203 | 204 | // @parse_if if-let for ... 205 | // This case returns to the main macro parsing. 206 | (@parse_if 207 | $item_expr: expr; 208 | if let $( $if_let_pattern: pat )|+ = $if_let_expr: expr; 209 | for $($rest: tt)* 210 | ) => { 211 | if let $( $if_let_pattern )|+ = $if_let_expr { 212 | Some(comp!($item_expr; for $($rest)*)) 213 | } else { 214 | None 215 | } 216 | }; 217 | 218 | // @parse_if if if ... 219 | (@parse_if 220 | $item_expr: expr; 221 | if $condition: expr; 222 | if $($rest: tt)* 223 | ) => { 224 | if $condition { 225 | comp!(@parse_if $item_expr; if $($rest)*) 226 | } else { 227 | None 228 | } 229 | }; 230 | 231 | // @parse_if if-let if ... 232 | (@parse_if 233 | $item_expr: expr; 234 | if let $( $if_let_pattern: pat )|+ = $if_let_expr: expr; 235 | if $($rest: tt)* 236 | ) => { 237 | if let $( $if_let_pattern )|+ = $if_let_expr { 238 | comp!(@parse_if $item_expr; if $($rest)*) 239 | } else { 240 | None 241 | } 242 | }; 243 | 244 | // for in 245 | ( 246 | $item_expr: expr; 247 | for $pattern: pat in $into_iterator: expr $(;)? 248 | ) => {{ 249 | let into_iterator = $into_iterator; 250 | $crate::__py_comp_assert_impl_into_iter(&into_iterator); 251 | into_iterator 252 | .into_iter() 253 | .map(move |$pattern| $item_expr) 254 | }}; 255 | 256 | // for in $( if $( if-let )* )+ 257 | ( 258 | $item_expr: expr; 259 | for $pattern: pat in $into_iterator: expr 260 | $( 261 | ; if $condition: expr 262 | $( ; if let $( $if_let_pattern: pat )|+ = $if_let_expr: expr )* 263 | )+ 264 | $(;)? 265 | ) => {{ 266 | let into_iterator = $into_iterator; 267 | $crate::__py_comp_assert_impl_into_iter(&into_iterator); 268 | into_iterator 269 | .into_iter() 270 | .filter_map(move |$pattern| 271 | comp!(@parse_if 272 | $item_expr 273 | $( 274 | ; if $condition 275 | $( ; if let $( $if_let_pattern )|+ = $if_let_expr )* 276 | )+ 277 | ) 278 | ) 279 | }}; 280 | 281 | // for in $( if-let $( if )* )+ 282 | ( 283 | $item_expr: expr; 284 | for $pattern: pat in $into_iterator: expr 285 | $( 286 | ; if let $( $if_let_pattern: pat )|+ = $if_let_expr: expr 287 | $( ; if $condition: expr )* 288 | )+ 289 | $(;)? 290 | ) => {{ 291 | let into_iterator = $into_iterator; 292 | $crate::__py_comp_assert_impl_into_iter(&into_iterator); 293 | into_iterator 294 | .into_iter() 295 | .filter_map(move |$pattern| 296 | comp!(@parse_if 297 | $item_expr 298 | $( 299 | ; if let $( $if_let_pattern )|+ = $if_let_expr 300 | $( ; if $condition )* 301 | )+ 302 | ) 303 | ) 304 | }}; 305 | 306 | // for in for ... 307 | ( 308 | $item_expr: expr; 309 | for $pattern: pat in $into_iterator: expr; 310 | for $($rest: tt)* 311 | ) => {{ 312 | let into_iterator = $into_iterator; 313 | $crate::__py_comp_assert_impl_into_iter(&into_iterator); 314 | into_iterator 315 | .into_iter() 316 | .flat_map(move |$pattern| 317 | comp!($item_expr; for $($rest)*) 318 | ) 319 | }}; 320 | 321 | // for in $( if $( if-let )* )+ for ... 322 | ( 323 | $item_expr: expr; 324 | for $pattern: pat in $into_iterator: expr; 325 | $( 326 | if $condition: expr; 327 | $( if let $( $if_let_pattern: pat )|+ = $if_let_expr: expr; )* 328 | )+ 329 | for $($rest: tt)* 330 | ) => {{ 331 | let into_iterator = $into_iterator; 332 | $crate::__py_comp_assert_impl_into_iter(&into_iterator); 333 | into_iterator 334 | .into_iter() 335 | .filter_map(move |$pattern| 336 | comp!(@parse_if 337 | $item_expr; 338 | $( 339 | if $condition; 340 | $( if let $( $if_let_pattern )|+ = $if_let_expr; )* 341 | )+ 342 | for $($rest)* 343 | ) 344 | ) 345 | .flatten() 346 | }}; 347 | 348 | // for in $( if-let $( if )* )+ for ... 349 | ( 350 | $item_expr: expr; 351 | for $pattern: pat in $into_iterator: expr; 352 | $( 353 | if let $( $if_let_pattern: pat )|+ = $if_let_expr: expr; 354 | $( if $condition: expr; )* 355 | )+ 356 | for $($rest: tt)* 357 | ) => {{ 358 | let into_iterator = $into_iterator; 359 | $crate::__py_comp_assert_impl_into_iter(&into_iterator); 360 | into_iterator 361 | .into_iter() 362 | .filter_map(move |$pattern| 363 | comp!(@parse_if 364 | $item_expr; 365 | $( 366 | if let $( $if_let_pattern )|+ = $if_let_expr; 367 | $( if $condition; )* 368 | )+ 369 | for $($rest)* 370 | ) 371 | ) 372 | .flatten() 373 | }}; 374 | } 375 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | //! The tests in this file are just simple use cases and general usability 2 | //! checks and examples. 3 | //! 4 | //! For more complex tests, drill down to the sub modules. 5 | 6 | use py_comp::comp; 7 | 8 | mod test_if_chains; 9 | 10 | /// This is a stand-in for any type that does not implement Copy or Clone. 11 | /// Using this type we can know that our implementation does not depend on 12 | /// the implicit semantics of these traits and works for all types. 13 | #[derive(Debug, PartialEq, Eq)] 14 | struct Foo(i32); 15 | 16 | /// An Iterator that is not Copy. 17 | #[derive(Debug)] 18 | struct UncopyableIterator {} 19 | 20 | impl Iterator for UncopyableIterator { 21 | type Item = (); 22 | 23 | fn next(&mut self) -> Option { 24 | None 25 | } 26 | } 27 | 28 | /// An Iterator that is not Copy of Iterators that are not Copy. 29 | #[derive(Debug)] 30 | struct UncopyableIteratorOfUncopyableIterators {} 31 | 32 | impl Iterator for UncopyableIteratorOfUncopyableIterators { 33 | type Item = UncopyableIterator; 34 | 35 | fn next(&mut self) -> Option { 36 | None 37 | } 38 | } 39 | 40 | #[test] 41 | fn basic_implementation_1_layer() { 42 | // This needs to be a reference to an array because of how the closures 43 | // capture their environment 44 | let x = &[Foo(1), Foo(2)]; 45 | 46 | let mut xyz1 = Vec::new(); 47 | for a in x { 48 | xyz1.push(a) 49 | } 50 | 51 | #[rustfmt::skip] 52 | #[allow(clippy::into_iter_on_array)] 53 | let xyz2 = 54 | x 55 | .into_iter() 56 | .map(move |a| a) 57 | .collect::>(); 58 | 59 | assert_eq!(xyz1, xyz2); 60 | } 61 | 62 | #[test] 63 | fn basic_implementation_with_if_condition_1_layer() { 64 | // This needs to be a reference to an array because of how the closures 65 | // capture their environment 66 | let x = &[Foo(1), Foo(2)]; 67 | 68 | let mut xyz1 = Vec::new(); 69 | for a in x { 70 | if a.0 % 10 == 2 { 71 | xyz1.push(a) 72 | } 73 | } 74 | 75 | #[rustfmt::skip] 76 | #[allow(clippy::into_iter_on_array)] 77 | let xyz2 = 78 | x 79 | .into_iter() 80 | .filter(|a| a.0 % 10 == 2) 81 | .map(move |a| a) 82 | .collect::>(); 83 | 84 | assert_eq!(xyz1, xyz2); 85 | } 86 | 87 | #[test] 88 | fn basic_implementation_cartesian_4_layers() { 89 | // These need to be references to arrays because of how the closures 90 | // capture their environment 91 | let w = &[Foo(1), Foo(2)]; 92 | let x = &[Foo(11), Foo(12)]; 93 | let y = &[Foo(21), Foo(22)]; 94 | let z = &[Foo(31), Foo(32)]; 95 | 96 | let mut xyz1 = Vec::new(); 97 | for a in w { 98 | for b in x { 99 | for c in y { 100 | for d in z { 101 | xyz1.push((a, b, c, d)) 102 | } 103 | } 104 | } 105 | } 106 | 107 | #[rustfmt::skip] 108 | #[allow(clippy::into_iter_on_array)] 109 | let xyz2 = 110 | w 111 | .into_iter() 112 | .flat_map(move |a| { 113 | x 114 | .into_iter() 115 | .flat_map(move |b| { 116 | y 117 | .into_iter() 118 | .flat_map(move |c| { 119 | z 120 | .into_iter() 121 | .map(move |d| { 122 | (a, b, c, d) 123 | }) 124 | }) 125 | }) 126 | }) 127 | .collect::>(); 128 | 129 | assert_eq!(xyz1, xyz2); 130 | } 131 | 132 | #[test] 133 | fn basic_implementation_cartesian_with_if_conditions_4_layers() { 134 | // These need to be references to arrays because of how the closures 135 | // capture their environment 136 | let w = &[Foo(1), Foo(2)]; 137 | let x = &[Foo(11), Foo(12)]; 138 | let y = &[Foo(21), Foo(22)]; 139 | let z = &[Foo(31), Foo(32)]; 140 | 141 | let mut xyz1 = Vec::new(); 142 | for a in w.iter() { 143 | if a.0 % 10 == 2 { 144 | for b in x.iter() { 145 | if b.0 % 10 == 2 { 146 | for c in y.iter() { 147 | if c.0 % 10 == 2 { 148 | for d in z.iter() { 149 | if d.0 % 10 == 2 { 150 | xyz1.push((a, b, c, d)) 151 | } 152 | } 153 | } 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | #[rustfmt::skip] 161 | #[allow(clippy::into_iter_on_array)] 162 | let xyz2 = 163 | w 164 | .into_iter() 165 | .filter(|a| a.0 % 10 == 2) 166 | .flat_map(move |a| { 167 | x 168 | .into_iter() 169 | .filter(|b| b.0 % 10 == 2) 170 | .flat_map(move |b| { 171 | y 172 | .into_iter() 173 | .filter(|c| c.0 % 10 == 2) 174 | .flat_map(move |c| { 175 | z 176 | .into_iter() 177 | .filter(|d| d.0 % 10 == 2) 178 | .map(move |d| { 179 | (a, b, c, d) 180 | }) 181 | }) 182 | }) 183 | }) 184 | .collect::>(); 185 | 186 | assert_eq!(xyz1, xyz2); 187 | } 188 | 189 | /// Check that various mixed forms of invocation *compile*. 190 | /// This is not meant to be exhaustive, (it can't be, this macro can 191 | /// accept infinitely long inputs) but it should be representative and cover 192 | /// all/most "parse paths" in the macro, to make sure they are not regressed. 193 | #[test] 194 | fn various_forms_of_usage() { 195 | let x = &[Foo(1), Foo(2)]; 196 | let y = &[[Foo(1), Foo(2)], [Foo(3), Foo(4)]]; 197 | let z = &[ 198 | [[Foo(1), Foo(2)], [Foo(3), Foo(4)]], 199 | [[Foo(1), Foo(2)], [Foo(3), Foo(4)]], 200 | ]; 201 | 202 | // importantly: 203 | // * trailing semicolon is optional. 204 | // * you can nest as many `for in` clauses as you want. 205 | // * you may use an `if` or `if let` clause after any `for in` clause. 206 | let _ = comp!(a; for a in x); 207 | let _ = comp!(a; for a in x;); 208 | 209 | let _ = comp!(a; for a in x; if *a == Foo(123)); 210 | let _ = comp!(a; for a in x; if *a == Foo(123);); 211 | 212 | let _ = comp!(a; for x in y; for a in x); 213 | let _ = comp!(a; for x in y; for a in x;); 214 | 215 | let _ = comp!(a; for x in y; if x[0] == Foo(123); for a in x); 216 | let _ = comp!(a; for x in y; if x[0] == Foo(123); for a in x;); 217 | 218 | let _ = comp!(a; for x in y; for a in x; if x[0] == Foo(123)); 219 | let _ = comp!(a; for x in y; for a in x; if x[0] == Foo(123);); 220 | 221 | let _ = comp!(a; for y in z; for x in y; for a in x); 222 | let _ = comp!(a; for y in z; for x in y; for a in x;); 223 | 224 | let _ = comp!(a; for x in y; for a in x; if let Foo(1) | Foo(2) = a); 225 | let _ = comp!(a; for x in y; for a in x; if let Foo(1) | Foo(2) = a;); 226 | 227 | let _ = comp!( 228 | (a, b); 229 | for a in x; if let Foo(1) | Foo(2) = a; 230 | for b in x; if let Foo(1) | Foo(2) = b 231 | ); 232 | let _ = comp!( 233 | (a, b); 234 | for a in x; if let Foo(1) | Foo(2) = a; 235 | for b in x; if let Foo(1) | Foo(2) = b; 236 | ); 237 | } 238 | 239 | #[test] 240 | fn comp_1_layer() { 241 | // This needs to be a reference to an array because of how the closures 242 | // capture their environment 243 | let x = &[Foo(1), Foo(2)]; 244 | 245 | let mut xyz1 = Vec::new(); 246 | for a in x { 247 | xyz1.push(a) 248 | } 249 | 250 | let xyz2 = comp!(a; for a in x).collect::>(); 251 | 252 | assert_eq!(xyz1, xyz2); 253 | } 254 | 255 | #[test] 256 | fn comp_with_if_condition_1_layer() { 257 | // This needs to be a reference to an array because of how the closures 258 | // capture their environment 259 | let x = &[Foo(1), Foo(2)]; 260 | 261 | let mut xyz1 = Vec::new(); 262 | for a in x { 263 | if a.0 % 10 == 2 { 264 | xyz1.push(a) 265 | } 266 | } 267 | 268 | let xyz2 = comp!( 269 | a; 270 | for a in x; 271 | if a.0 % 10 == 2; 272 | ) 273 | .collect::>(); 274 | 275 | assert_eq!(xyz1, xyz2); 276 | } 277 | 278 | #[test] 279 | fn comp_with_if_let_condition_1_layer() { 280 | // This needs to be a reference to an array because of how the closures 281 | // capture their environment 282 | let x = &[Foo(4), Foo(8)]; 283 | 284 | let mut xyz1 = Vec::new(); 285 | for a in x { 286 | if let Foo(_inner @ 1...6) = a { 287 | xyz1.push(a) 288 | } 289 | } 290 | 291 | let xyz2 = comp!( 292 | a; 293 | for a in x; 294 | if let Foo(_inner @ 1...6) = a; 295 | ) 296 | .collect::>(); 297 | 298 | assert_eq!(xyz1, xyz2); 299 | } 300 | 301 | #[test] 302 | fn comp_with_pattern_1_layer() { 303 | // This needs to be a reference to an array because of how the closures 304 | // capture their environment 305 | let x = &[(Foo(1), Foo(2)), (Foo(3), Foo(4))]; 306 | 307 | let mut xyz1 = Vec::new(); 308 | for (a, _b) in x { 309 | xyz1.push(a) 310 | } 311 | 312 | let xyz2 = comp!(a; for (a, _b) in x).collect::>(); 313 | 314 | assert_eq!(xyz1, xyz2); 315 | } 316 | 317 | #[test] 318 | fn comp_with_pattern_with_if_condition_1_layer() { 319 | // This needs to be a reference to an array because of how the closures 320 | // capture their environment 321 | let x = &[(Foo(1), Foo(2)), (Foo(3), Foo(4))]; 322 | 323 | let mut xyz1 = Vec::new(); 324 | for (a, _b) in x { 325 | if a.0 % 10 == 2 { 326 | xyz1.push(a) 327 | } 328 | } 329 | 330 | let xyz2 = comp!( 331 | a; 332 | for (a, _b) in x; 333 | if a.0 % 10 == 2; 334 | ) 335 | .collect::>(); 336 | 337 | assert_eq!(xyz1, xyz2); 338 | } 339 | 340 | #[test] 341 | fn comp_cartesian_4_layers() { 342 | // These need to be references to arrays because of how the closures 343 | // capture their environment 344 | let w = &[Foo(1), Foo(2)]; 345 | let x = &[Foo(11), Foo(12)]; 346 | let y = &[Foo(21), Foo(22)]; 347 | let z = &[Foo(31), Foo(32)]; 348 | 349 | let mut xyz1 = Vec::new(); 350 | for a in w { 351 | for b in x { 352 | for c in y { 353 | for d in z { 354 | xyz1.push((a, b, c, d)) 355 | } 356 | } 357 | } 358 | } 359 | 360 | let xyz2 = comp!( 361 | (a, b, c, d); 362 | for a in w; 363 | for b in x; 364 | for c in y; 365 | for d in z; 366 | ) 367 | .collect::>(); 368 | 369 | assert_eq!(xyz1, xyz2); 370 | } 371 | 372 | #[test] 373 | fn comp_cartesian_with_if_conditions_4_layers() { 374 | // These need to be references to arrays because of how the closures 375 | // capture their environment 376 | let w = &[Foo(1), Foo(2)]; 377 | let x = &[Foo(11), Foo(12)]; 378 | let y = &[Foo(21), Foo(22)]; 379 | let z = &[Foo(31), Foo(32)]; 380 | 381 | let mut xyz1 = Vec::new(); 382 | for a in w.iter() { 383 | if a.0 % 10 == 2 { 384 | for b in x.iter() { 385 | if b.0 % 10 == 2 { 386 | for c in y.iter() { 387 | if c.0 % 10 == 2 { 388 | for d in z.iter() { 389 | if d.0 % 10 == 2 { 390 | xyz1.push((a, b, c, d)) 391 | } 392 | } 393 | } 394 | } 395 | } 396 | } 397 | } 398 | } 399 | 400 | let xyz2 = comp!( 401 | (a, b, c, d); 402 | for a in w; 403 | if a.0 % 10 == 2; 404 | for b in x; 405 | if b.0 % 10 == 2; 406 | for c in y; 407 | if c.0 % 10 == 2; 408 | for d in z; 409 | if d.0 % 10 == 2; 410 | ) 411 | .collect::>(); 412 | 413 | assert_eq!(xyz1, xyz2); 414 | } 415 | 416 | #[test] 417 | fn comp_cartesian_with_if_and_if_let_conditions_4_layers() { 418 | // These need to be references to arrays because of how the closures 419 | // capture their environment 420 | let w = &[Foo(1), Foo(2)]; 421 | let x = &[Foo(14), Foo(18)]; 422 | let y = &[Foo(21), Foo(22)]; 423 | let z = &[Foo(34), Foo(38)]; 424 | 425 | let mut xyz1 = Vec::new(); 426 | for a in w.iter() { 427 | if a.0 % 10 == 2 { 428 | for b in x.iter() { 429 | if let Foo(_inner @ 11...16) = b { 430 | for c in y.iter() { 431 | if c.0 % 10 == 2 { 432 | for d in z.iter() { 433 | if let Foo(_inner @ 31...36) = d { 434 | xyz1.push((a, b, c, d)) 435 | } 436 | } 437 | } 438 | } 439 | } 440 | } 441 | } 442 | } 443 | 444 | let xyz2 = comp!( 445 | (a, b, c, d); 446 | for a in w; 447 | if a.0 % 10 == 2; 448 | for b in x; 449 | if let Foo(_inner @ 11...16) = b; 450 | for c in y; 451 | if c.0 % 10 == 2; 452 | for d in z; 453 | if let Foo(_inner @ 31...36) = d; 454 | ) 455 | .collect::>(); 456 | 457 | assert_eq!(xyz1, xyz2); 458 | } 459 | 460 | #[test] 461 | fn comp_cartesian_with_pattern_4_layers() { 462 | // These need to be references to arrays because of how the closures 463 | // capture their environment 464 | let w = &[(Foo(1), Foo(99)), (Foo(2), Foo(99))]; 465 | let x = &[(Foo(11), Foo(99)), (Foo(12), Foo(99))]; 466 | let y = &[(Foo(21), Foo(99)), (Foo(22), Foo(99))]; 467 | let z = &[(Foo(31), Foo(99)), (Foo(32), Foo(99))]; 468 | 469 | let mut xyz1 = Vec::new(); 470 | for (a, _a) in w { 471 | for (b, _b) in x { 472 | for (c, _c) in y { 473 | for (d, _d) in z { 474 | xyz1.push((a, b, c, d)) 475 | } 476 | } 477 | } 478 | } 479 | 480 | let xyz2 = comp!( 481 | (a, b, c, d); 482 | for (a, _a) in w; 483 | for (b, _b) in x; 484 | for (c, _c) in y; 485 | for (d, _d) in z; 486 | ) 487 | .collect::>(); 488 | 489 | assert_eq!(xyz1, xyz2); 490 | } 491 | 492 | #[test] 493 | fn comp_cartesian_with_pattern_with_if_conditions_4_layers() { 494 | // These need to be references to arrays because of how the closures 495 | // capture their environment 496 | let w = &[(Foo(1), Foo(99)), (Foo(2), Foo(99))]; 497 | let x = &[(Foo(11), Foo(99)), (Foo(12), Foo(99))]; 498 | let y = &[(Foo(21), Foo(99)), (Foo(22), Foo(99))]; 499 | let z = &[(Foo(31), Foo(99)), (Foo(32), Foo(99))]; 500 | 501 | let mut xyz1 = Vec::new(); 502 | for (a, _a) in w.iter() { 503 | if a.0 % 10 == 2 { 504 | for (b, _b) in x.iter() { 505 | if b.0 % 10 == 2 { 506 | for (c, _c) in y.iter() { 507 | if c.0 % 10 == 2 { 508 | for (d, _d) in z.iter() { 509 | if d.0 % 10 == 2 { 510 | xyz1.push((a, b, c, d)) 511 | } 512 | } 513 | } 514 | } 515 | } 516 | } 517 | } 518 | } 519 | 520 | let xyz2 = comp!( 521 | (a, b, c, d); 522 | for (a, _a) in w; 523 | if a.0 % 10 == 2; 524 | for (b, _b) in x; 525 | if b.0 % 10 == 2; 526 | for (c, _c) in y; 527 | if c.0 % 10 == 2; 528 | for (d, _d) in z; 529 | if d.0 % 10 == 2; 530 | ) 531 | .collect::>(); 532 | 533 | assert_eq!(xyz1, xyz2); 534 | } 535 | 536 | #[test] 537 | fn triple_nested_structure() { 538 | // This needs to be a reference to an array because of how the closures 539 | // capture their environment 540 | let nested_3 = &[ 541 | [ 542 | [Foo(0), Foo(1), Foo(2)], 543 | [Foo(3), Foo(4), Foo(5)], 544 | [Foo(6), Foo(7), Foo(8)], 545 | ], 546 | [ 547 | [Foo(9), Foo(10), Foo(11)], 548 | [Foo(12), Foo(13), Foo(14)], 549 | [Foo(15), Foo(16), Foo(17)], 550 | ], 551 | [ 552 | [Foo(18), Foo(19), Foo(20)], 553 | [Foo(21), Foo(22), Foo(23)], 554 | [Foo(24), Foo(25), Foo(26)], 555 | ], 556 | ]; 557 | 558 | let nested_objects = comp!( 559 | { 560 | let inner = nested.0; 561 | Foo(inner + 1) 562 | }; 563 | for nested_2 in nested_3; 564 | for nested_1 in nested_2; 565 | for nested in nested_1; 566 | ) 567 | .collect::>(); 568 | 569 | let expected_values = (1..28).map(Foo).collect::>(); 570 | 571 | assert_eq!(expected_values, nested_objects); 572 | } 573 | 574 | #[test] 575 | fn uncopyable_iterator() { 576 | let _ = comp!(x; for x in UncopyableIterator {}); 577 | } 578 | 579 | #[test] 580 | fn uncopyable_iterator_of_uncopyable_iterators() { 581 | let _ = comp!( 582 | item; 583 | for uncopyable_iterator in UncopyableIteratorOfUncopyableIterators {}; 584 | for item in uncopyable_iterator; 585 | ); 586 | } 587 | 588 | mod renamed_comp_import { 589 | use py_comp::comp as c; 590 | 591 | #[test] 592 | fn use_renamed_macro() { 593 | let _ = c!(y; for x in &[[0]]; for y in x); 594 | } 595 | } 596 | -------------------------------------------------------------------------------- /tests/test_if_chains/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains tests for various complex chains of `if` clauses 2 | //! in different contexts. The tests in the sub modules should be very similar 3 | //! to each other, and try to cover all paths the parser in the `comp` macro 4 | //! takes while parsing if-chains, to make sure all generated code is sane 5 | //! and correct. 6 | //! 7 | //! If you add a test to one of the sub modules, you probably want to add an 8 | //! equivalent one to the other sub modules. 9 | 10 | mod test_initial_if_in_final_for; 11 | mod test_initial_if_in_non_final_for; 12 | mod test_initial_if_let_in_final_for; 13 | mod test_initial_if_let_in_non_final_for; 14 | -------------------------------------------------------------------------------- /tests/test_if_chains/test_initial_if_in_final_for.rs: -------------------------------------------------------------------------------- 1 | //! Test the syntax works correctly for if-chains starting with an `if` 2 | //! clause in the final `for` clause. 3 | 4 | use py_comp::comp; 5 | 6 | #[test] 7 | fn for_if() { 8 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 9 | 10 | let items: Vec = comp!(*a; for (a, _) in iterable; if *a > 1).collect(); 11 | 12 | assert_eq!(items, vec![2, 3, 4, 5]); 13 | } 14 | 15 | #[test] 16 | fn for_if_if_let() { 17 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 18 | 19 | let items: Vec = comp!( 20 | *a; 21 | for (a, b) in iterable; 22 | if *a > 1; 23 | if let 13...14 = b 24 | ) 25 | .collect(); 26 | 27 | assert_eq!(items, vec![3, 4]); 28 | } 29 | 30 | #[test] 31 | fn for_if_if_let_if_let() { 32 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 33 | 34 | let items: Vec = comp!( 35 | *a; 36 | for (a, b) in iterable; 37 | if *a > 1; 38 | if let 13...15 = b; 39 | if let 14...14 = b 40 | ) 41 | .collect(); 42 | 43 | assert_eq!(items, vec![4]); 44 | } 45 | 46 | #[test] 47 | fn for_if_if_let_if_let_if() { 48 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 49 | 50 | let items: Vec = comp!( 51 | *a; 52 | for (a, b) in iterable; 53 | if *a > 1; 54 | if let 13...15 = b; 55 | if let 13...14 = b; 56 | if *b < 14 57 | ) 58 | .collect(); 59 | 60 | assert_eq!(items, vec![3]); 61 | } 62 | 63 | #[test] 64 | fn for_if_if() { 65 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 66 | 67 | let items: Vec = comp!(*a; for (a, b) in iterable; if *a > 1; if *b < 15).collect(); 68 | 69 | assert_eq!(items, vec![2, 3, 4]); 70 | } 71 | 72 | #[test] 73 | fn for_if_if_if_let() { 74 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 75 | 76 | let items: Vec = comp!( 77 | *a; for (a, b) in iterable; if *a > 1; if *b < 15; if let 2...3 = a 78 | ) 79 | .collect(); 80 | 81 | assert_eq!(items, vec![2, 3]); 82 | } 83 | 84 | #[test] 85 | fn for_if_if_if_let_if_let() { 86 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 87 | 88 | let items: Vec = comp!( 89 | *a; 90 | for (a, b) in iterable; 91 | if *a > 1; 92 | if *b < 15; 93 | if let 2...3 = a; 94 | if let 3...4 = a 95 | ) 96 | .collect(); 97 | 98 | assert_eq!(items, vec![3]); 99 | } 100 | -------------------------------------------------------------------------------- /tests/test_if_chains/test_initial_if_in_non_final_for.rs: -------------------------------------------------------------------------------- 1 | //! Test the syntax works correctly for if-chains starting with an `if` 2 | //! clause in a non final `for` clause. 3 | 4 | use py_comp::comp; 5 | 6 | #[test] 7 | fn for_if_for() { 8 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 9 | let iterable2 = &[(1, 11)]; 10 | 11 | let items: Vec<(i32, i32)> = comp!( 12 | (*a, *x); 13 | for (a, _) in iterable1; 14 | if *a > 1; 15 | for (_, x) in iterable2 16 | ) 17 | .collect(); 18 | 19 | assert_eq!(items, vec![(2, 11), (3, 11), (4, 11), (5, 11)]); 20 | } 21 | 22 | #[test] 23 | fn for_if_if_let_for() { 24 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 25 | let iterable2 = &[(1, 11)]; 26 | 27 | let items: Vec<(i32, i32)> = comp!( 28 | (*a, *x); 29 | for (a, b) in iterable1; 30 | if *a > 1; 31 | if let 13...14 = b; 32 | for (_, x) in iterable2 33 | ) 34 | .collect(); 35 | 36 | assert_eq!(items, vec![(3, 11), (4, 11)]); 37 | } 38 | 39 | #[test] 40 | fn for_if_if_let_if_let_for() { 41 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 42 | let iterable2 = &[(1, 11)]; 43 | 44 | let items: Vec<(i32, i32)> = comp!( 45 | (*a, *x); 46 | for (a, b) in iterable1; 47 | if *a > 1; 48 | if let 13...15 = b; 49 | if let 14...14 = b; 50 | for (_, x) in iterable2 51 | ) 52 | .collect(); 53 | 54 | assert_eq!(items, vec![(4, 11)]); 55 | } 56 | 57 | #[test] 58 | fn for_if_if_let_if_let_if_for() { 59 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 60 | let iterable2 = &[(1, 11)]; 61 | 62 | let items: Vec<(i32, i32)> = comp!( 63 | (*a, *x); 64 | for (a, b) in iterable1; 65 | if *a > 1; 66 | if let 13...15 = b; 67 | if let 13...14 = b; 68 | if *b < 14; 69 | for (_, x) in iterable2 70 | ) 71 | .collect(); 72 | 73 | assert_eq!(items, vec![(3, 11)]); 74 | } 75 | 76 | #[test] 77 | fn for_if_if_for() { 78 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 79 | let iterable2 = &[(1, 11)]; 80 | 81 | let items: Vec<(i32, i32)> = comp!( 82 | (*a, *x); 83 | for (a, b) in iterable1; 84 | if *a > 1; 85 | if *b < 15; 86 | for (_, x) in iterable2 87 | ) 88 | .collect(); 89 | 90 | assert_eq!(items, vec![(2, 11), (3, 11), (4, 11)]); 91 | } 92 | 93 | #[test] 94 | fn for_if_if_if_let_for() { 95 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 96 | let iterable2 = &[(1, 11)]; 97 | 98 | let items: Vec<(i32, i32)> = comp!( 99 | (*a, *x); 100 | for (a, b) in iterable1; 101 | if *a > 1; if *b < 15; 102 | if let 2...3 = a; 103 | for (_, x) in iterable2 104 | ) 105 | .collect(); 106 | 107 | assert_eq!(items, vec![(2, 11), (3, 11)]); 108 | } 109 | 110 | #[test] 111 | fn for_if_if_if_let_if_let_for() { 112 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 113 | let iterable2 = &[(1, 11)]; 114 | 115 | let items: Vec<(i32, i32)> = comp!( 116 | (*a, *x); 117 | for (a, b) in iterable1; 118 | if *a > 1; 119 | if *b < 15; 120 | if let 2...3 = a; 121 | if let 3...4 = a; 122 | for (_, x) in iterable2 123 | ) 124 | .collect(); 125 | 126 | assert_eq!(items, vec![(3, 11)]); 127 | } 128 | -------------------------------------------------------------------------------- /tests/test_if_chains/test_initial_if_let_in_final_for.rs: -------------------------------------------------------------------------------- 1 | //! Test the syntax works correctly for if-chains starting with an `if let` 2 | //! clause in the final `for` clause. 3 | 4 | use py_comp::comp; 5 | 6 | #[test] 7 | fn for_if_let() { 8 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 9 | 10 | let items: Vec = comp!(*a; for (a, _) in iterable; if let 2...5 = a).collect(); 11 | 12 | assert_eq!(items, vec![2, 3, 4, 5]); 13 | } 14 | 15 | #[test] 16 | fn for_if_let_if() { 17 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 18 | 19 | let items: Vec = comp!( 20 | *a; 21 | for (a, b) in iterable; 22 | if let 2...5 = a; 23 | if *b >= 13 && *b <= 14 24 | ) 25 | .collect(); 26 | 27 | assert_eq!(items, vec![3, 4]); 28 | } 29 | 30 | #[test] 31 | fn for_if_let_if_if() { 32 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 33 | 34 | let items: Vec = comp!( 35 | *a; 36 | for (a, b) in iterable; 37 | if let 2...5 = a; 38 | if *b >= 13 && *b <= 15; 39 | if *b >= 14 && *b <= 14 40 | ) 41 | .collect(); 42 | 43 | assert_eq!(items, vec![4]); 44 | } 45 | 46 | #[test] 47 | fn for_if_let_if_if_if_let() { 48 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 49 | 50 | let items: Vec = comp!( 51 | *a; 52 | for (a, b) in iterable; 53 | if let 2...5 = a; 54 | if *b >= 13 && *b <= 15; 55 | if *b >= 13 && *b <= 14; 56 | if let 11...13 = b 57 | ) 58 | .collect(); 59 | 60 | assert_eq!(items, vec![3]); 61 | } 62 | 63 | #[test] 64 | fn for_if_let_if_let() { 65 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 66 | 67 | let items: Vec = comp!( 68 | *a; for (a, b) in iterable; if let 2...5 = a; if let 11...14 = b 69 | ) 70 | .collect(); 71 | 72 | assert_eq!(items, vec![2, 3, 4]); 73 | } 74 | 75 | #[test] 76 | fn for_if_let_if_let_if() { 77 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 78 | 79 | let items: Vec = comp!( 80 | *a; 81 | for (a, b) in iterable; 82 | if let 2...5 = a; 83 | if let 11...14 = b; 84 | if *a >= 2 && *a <= 3 85 | ) 86 | .collect(); 87 | 88 | assert_eq!(items, vec![2, 3]); 89 | } 90 | 91 | #[test] 92 | fn for_if_let_if_let_if_if() { 93 | let iterable = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 94 | 95 | let items: Vec = comp!( 96 | *a; 97 | for (a, b) in iterable; 98 | if let 2...5 = a; 99 | if let 11...14 = b; 100 | if *a >=2 && *a <= 3; 101 | if *a >=3 && *a <= 4 102 | ) 103 | .collect(); 104 | 105 | assert_eq!(items, vec![3]); 106 | } 107 | -------------------------------------------------------------------------------- /tests/test_if_chains/test_initial_if_let_in_non_final_for.rs: -------------------------------------------------------------------------------- 1 | //! Test the syntax works correctly for if-chains starting with an `if let` 2 | //! clause in a non final `for` clause. 3 | 4 | use py_comp::comp; 5 | 6 | #[test] 7 | fn for_if_let_for() { 8 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 9 | let iterable2 = &[(1, 11)]; 10 | 11 | let items: Vec<(i32, i32)> = comp!( 12 | (*a, *x); 13 | for (a, _) in iterable1; 14 | if let 2...5 = a; 15 | for (_, x) in iterable2 16 | ) 17 | .collect(); 18 | 19 | assert_eq!(items, vec![(2, 11), (3, 11), (4, 11), (5, 11)]); 20 | } 21 | 22 | #[test] 23 | fn for_if_let_if_for() { 24 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 25 | let iterable2 = &[(1, 11)]; 26 | 27 | let items: Vec<(i32, i32)> = comp!( 28 | (*a, *x); 29 | for (a, b) in iterable1; 30 | if let 2...5 = a; 31 | if *b >= 13 && *b <= 14; 32 | for (_, x) in iterable2 33 | ) 34 | .collect(); 35 | 36 | assert_eq!(items, vec![(3, 11), (4, 11)]); 37 | } 38 | 39 | #[test] 40 | fn for_if_let_if_if_for() { 41 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 42 | let iterable2 = &[(1, 11)]; 43 | 44 | let items: Vec<(i32, i32)> = comp!( 45 | (*a, *x); 46 | for (a, b) in iterable1; 47 | if let 2...5 = a; 48 | if *b >= 13 && *b <= 15; 49 | if *b >= 14 && *b <= 14; 50 | for (_, x) in iterable2 51 | ) 52 | .collect(); 53 | 54 | assert_eq!(items, vec![(4, 11)]); 55 | } 56 | 57 | #[test] 58 | fn for_if_let_if_if_if_let_for() { 59 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 60 | let iterable2 = &[(1, 11)]; 61 | 62 | let items: Vec<(i32, i32)> = comp!( 63 | (*a, *x); 64 | for (a, b) in iterable1; 65 | if let 2...5 = a; 66 | if *b >= 13 && *b <= 15; 67 | if *b >= 13 && *b <= 14; 68 | if let 11...13 = b; 69 | for (_, x) in iterable2 70 | ) 71 | .collect(); 72 | 73 | assert_eq!(items, vec![(3, 11)]); 74 | } 75 | 76 | #[test] 77 | fn for_if_let_if_let_for() { 78 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 79 | let iterable2 = &[(1, 11)]; 80 | 81 | let items: Vec<(i32, i32)> = comp!( 82 | (*a, *x); 83 | for (a, b) in iterable1; 84 | if let 2...5 = a; 85 | if let 11...14 = b; 86 | for (_, x) in iterable2 87 | ) 88 | .collect(); 89 | 90 | assert_eq!(items, vec![(2, 11), (3, 11), (4, 11)]); 91 | } 92 | 93 | #[test] 94 | fn for_if_let_if_let_if_for() { 95 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 96 | let iterable2 = &[(1, 11)]; 97 | 98 | let items: Vec<(i32, i32)> = comp!( 99 | (*a, *x); 100 | for (a, b) in iterable1; 101 | if let 2...5 = a; 102 | if let 11...14 = b; 103 | if *a >= 2 && *a <= 3; 104 | for (_, x) in iterable2 105 | ) 106 | .collect(); 107 | 108 | assert_eq!(items, vec![(2, 11), (3, 11)]); 109 | } 110 | 111 | #[test] 112 | fn for_if_let_if_let_if_if_for() { 113 | let iterable1 = &[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]; 114 | let iterable2 = &[(1, 11)]; 115 | 116 | let items: Vec<(i32, i32)> = comp!( 117 | (*a, *x); 118 | for (a, b) in iterable1; 119 | if let 2...5 = a; 120 | if let 11...14 = b; 121 | if *a >=2 && *a <= 3; 122 | if *a >=3 && *a <= 4; 123 | for (_, x) in iterable2 124 | ) 125 | .collect(); 126 | 127 | assert_eq!(items, vec![(3, 11)]); 128 | } 129 | --------------------------------------------------------------------------------