├── .gitignore ├── Cargo.toml ├── .travis.yml ├── LICENSE ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "comp" 3 | version = "0.2.1" 4 | authors = ["goandylok"] 5 | license = "MIT/Apache-2.0" 6 | 7 | description = "Pure-macro Do notation and List-comprehension for Option, Result and Iterator." 8 | repository = "https://github.com/goandylok/comp-rs" 9 | homepage = "https://github.com/goandylok/comp-rs" 10 | documentation = "https://docs.rs/comp/" 11 | 12 | keywords = ["macro", "hado", "do", "mdo", "notation"] 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | sudo: required 4 | 5 | matrix: 6 | include: 7 | - rust: stable 8 | - rust: beta 9 | - rust: nightly 10 | 11 | branches: 12 | only: 13 | - master 14 | 15 | script: 16 | - | 17 | cargo test --verbose 18 | 19 | before_install: 20 | - sudo apt-get update 21 | 22 | addons: 23 | apt: 24 | packages: 25 | - libcurl4-openssl-dev 26 | - libelf-dev 27 | - libdw-dev 28 | - cmake 29 | - gcc 30 | - binutils-dev 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 goandylok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `comp-rs` 2 | 3 | [![Build Status](https://travis-ci.org/goandylok/comp-rs.svg?branch=master)](https://travis-ci.org/goandylok/comp-rs) 4 | [![crates.io](https://img.shields.io/crates/v/comp.svg)](https://crates.io/crates/comp) 5 | [![docs.rs](https://docs.rs/comp/badge.svg)](https://docs.rs/comp) 6 | 7 | Pure-macro Do notation and List-comprehension for Option, Result and Iterator. 8 | 9 | It provides syntax extensions to easily combind wrapper type (`Option`, `Result` and `Iterator`), which seems like 10 | `for-comprehension` in scala or `Do notation` in haskell. 11 | 12 | ### [**Documentation**](https://docs.rs/comp/) 13 | 14 | ## Usage 15 | 16 | First, add the following to your `Cargo.toml`: 17 | 18 | ```toml 19 | [dependencies] 20 | comp = "*" 21 | ``` 22 | 23 | Next, add this to your crate root: 24 | 25 | ```rust 26 | #[macro_use] 27 | extern crate comp; 28 | ``` 29 | 30 | ## Example 31 | 32 | `comp-rs` delivers three macros : *`option!`*, *`result!`* and *`iter!`*, 33 | transforming the `arrow(<-)` statements into FP bind (`flat_map`). 34 | 35 | ### Iterator 36 | 37 | ```rust 38 | #[macro_use] 39 | extern crate comp; 40 | 41 | let iter = iter! { 42 | let x <- 0..2u8; 43 | let y <- vec!['a', 'b']; 44 | (x, y) 45 | }; 46 | 47 | for x in iter { 48 | println!("{:?}", x); 49 | } 50 | 51 | // Print (0, 'a') (0, 'b') (1, 'a') (1, 'b') 52 | ``` 53 | 54 | It can be written like generator expressions in python: 55 | 56 | ```python 57 | # Python 58 | values = [6, 2, 9, 4, -1, 33, 87, 23] 59 | result = (x*x for x in values if x < 10) 60 | ``` 61 | 62 | or in scala 63 | 64 | ```scala 65 | // Scala 66 | val values = Array(6, 2, 9, 4, -1, 33, 87, 23) 67 | val result = for(x <- values if x < 10) yield x*x 68 | ``` 69 | 70 | in Rust 71 | 72 | ```rust 73 | // comp-rs 74 | // The result is of type FlatMap and also lazy, like Python's generator expressions. 75 | let values = vec![6, 2, 9, 4, -1, 33, 87, 23]; 76 | let result = iter!{let x <- values; if x < 10; x*x}; 77 | ``` 78 | 79 | 80 | ### Option 81 | ```rust 82 | #[macro_use] 83 | extern crate comp; 84 | 85 | let option = option! { 86 | let a <- Some(1); 87 | let b <- Some(2); 88 | a + b 89 | }; 90 | 91 | assert_eq!(option, Some(3)); 92 | ``` 93 | 94 | ### Result 95 | 96 | Unlike `Iterator` and `Option`, rust provides __*Question Mark*__ syntax to combine `Result`s. 97 | 98 | Let's see how `comp-rs` makes it more explicit and expressive. 99 | 100 | #### Native way 101 | 102 | ```rust 103 | #[macro_use] 104 | extern crate comp; 105 | 106 | use std::fs::File; 107 | use std::io; 108 | use std::io::prelude::*; 109 | 110 | // try!() macro must be wrap into a function 111 | fn content() -> io::Result { 112 | let mut f = try!(File::open("foo.txt")); 113 | let mut s = String::new(); 114 | try!(f.read_to_string(&mut s)); 115 | Ok(s) 116 | } 117 | ``` 118 | 119 | #### Question mark 120 | 121 | ```rust 122 | #[macro_use] 123 | extern crate comp; 124 | 125 | use std::fs::File; 126 | use std::io; 127 | use std::io::prelude::*; 128 | 129 | // '?' mark must be wrap into a function 130 | fn content() -> io::Result { 131 | let mut f = File::open("foo.txt")?; 132 | let mut s = String::new(); 133 | f.read_to_string(&mut s)?; 134 | Ok(s) 135 | } 136 | ``` 137 | 138 | #### `result!` way 139 | 140 | ```rust 141 | #[macro_use] 142 | extern crate comp; 143 | 144 | use std::fs::File; 145 | use std::io; 146 | use std::io::prelude::*; 147 | 148 | let content: io::Result = result! { 149 | let mut f <- File::open("foo.txt"); 150 | let mut s = String::new(); 151 | let _ <- f.read_to_string(&mut s); 152 | s 153 | }; 154 | ``` 155 | 156 | ## Contribution 157 | 158 | All kinds of contribution are welcome. 159 | 160 | - **Issue** Feel free to open an issue when you find typos, bugs, or have any question. 161 | - **Pull requests**. Better implementation, more tests, more documents and typo fixes are all welcome. 162 | 163 | ## License 164 | 165 | Licensed under MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 166 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | 3 | //! Pure-macro Do notation and List-comprehension for Option, Result and Iterator. 4 | //! 5 | //! It provides syntax extensions to easily combind wrapper type (`Option`, `Result` and `Iterator`), 6 | //! which seems like `for-comprehension` in scala or `Do notation` in haskell. 7 | //! 8 | //! # Usage 9 | //! 10 | //! First, add the following to your `Cargo.toml`: 11 | //! 12 | //! ```toml 13 | //! [dependencies] 14 | //! comp = "*" 15 | //! ``` 16 | //! 17 | //! Next, add this to your crate root: 18 | //! 19 | //! ``` 20 | //! #[macro_use] 21 | //! extern crate comp; 22 | //! # fn main() {} 23 | //! ``` 24 | //! 25 | //! # Example 26 | //! 27 | //! `comp-rs` delivers three macros : *`option!`*, *`result!`* and *`iter!`*, 28 | //! transforming the `arrow(<-)` statements into FP binding( *`flat_map()`* ). 29 | //! 30 | //! ## Iterator 31 | //! 32 | //! ``` 33 | //! #[macro_use] 34 | //! extern crate comp; 35 | //! 36 | //! # fn main() { 37 | //! let iter = iter! { 38 | //! let x <- 0..2u8; 39 | //! let y <- vec!['a', 'b']; 40 | //! (x, y) 41 | //! }; 42 | //! 43 | //! for x in iter { 44 | //! println!("{:?}", x); 45 | //! } 46 | //! 47 | //! // Print (0, 'a') (0, 'b') (1, 'a') (1, 'b') 48 | //! # } 49 | //! ``` 50 | //! 51 | //! ## Option 52 | //! ``` 53 | //! #[macro_use] 54 | //! extern crate comp; 55 | //! 56 | //! # fn main() { 57 | //! let option = option! { 58 | //! let a <- Some(1); 59 | //! let b <- Some(2); 60 | //! a + b 61 | //! }; 62 | //! 63 | //! assert_eq!(option, Some(3)); 64 | //! # } 65 | //! ``` 66 | //! 67 | //! ## Result 68 | //! 69 | //! Unlike `Iterator` and `Option`, rust provides __*Question Mark*__ syntax to combine `Result`s. 70 | //! 71 | //! Let's see how `comp-rs` makes it more explicit and expressive. 72 | //! 73 | //! **Native way** 74 | //! 75 | //! ```no_run 76 | //! use std::fs::File; 77 | //! use std::io; 78 | //! use std::io::prelude::*; 79 | //! 80 | //! # fn main() { 81 | //! // try!() macro must be wrap into a function 82 | //! fn content() -> io::Result { 83 | //! let mut f = try!(File::open("foo.txt")); 84 | //! let mut s = String::new(); 85 | //! try!(f.read_to_string(&mut s)); 86 | //! Ok(s) 87 | //! } 88 | //! # } 89 | //! ``` 90 | //! 91 | //! **Question mark** 92 | //! 93 | //! ```no_run 94 | //! use std::fs::File; 95 | //! use std::io; 96 | //! use std::io::prelude::*; 97 | //! 98 | //! # fn main() { 99 | //! // '?' mark must be wrap into a function 100 | //! fn content() -> io::Result { 101 | //! let mut f = File::open("foo.txt")?; 102 | //! let mut s = String::new(); 103 | //! f.read_to_string(&mut s)?; 104 | //! Ok(s) 105 | //! } 106 | //! # } 107 | //! ``` 108 | //! 109 | //! **`result!` way** 110 | //! 111 | //! ```no_run 112 | //! # #[macro_use] 113 | //! # extern crate comp; 114 | //! # 115 | //! use std::fs::File; 116 | //! use std::io; 117 | //! use std::io::prelude::*; 118 | //! 119 | //! # fn main() { 120 | //! let content: io::Result = result! { 121 | //! let mut f <- File::open("foo.txt"); 122 | //! let mut s = String::new(); 123 | //! let _ <- f.read_to_string(&mut s); 124 | //! s 125 | //! }; 126 | //! # } 127 | //! ``` 128 | //! 129 | //! # Syntax 130 | //! 131 | //! All three macros return wrapped type(`Option`, `Result` and 132 | //! `Iterator`), and yield the last expression. 133 | //! 134 | //! Syntax: `(sentence)* ; expression` 135 | //! 136 | //! sentence can be: 137 | 138 | //! * `let pattern <- expression;`: bind expression to pattern. 139 | //! 140 | //! * `if filter_expression;`: filter by condition, and jump over when not satisfied. 141 | //! 142 | //! * `statement;`: let assignment, value assignment, etc. 143 | //! 144 | //! * `{...}`: block and unsafe block. 145 | //! 146 | //! # Syntax Detail 147 | //! 148 | //! ## 1. Basic arrow(<-) syntax 149 | //! 150 | //! ## Rules 151 | //! 152 | //! ```rust,ignore 153 | //! Macro Expand 154 | //! | 155 | //! option! {} | Some(()) 156 | //! | 157 | //! option! { x; } | { x; Some(()) } 158 | //! | 159 | //! option! { x } | Some(x) 160 | //! | 161 | //! 162 | //! Macro 163 | //! ------------------------------------ 164 | //! Expand 165 | //! 166 | //! option! { let x <- Some(1); } 167 | //! ------------------------------------ 168 | //! Some(1).and_then(move |x| option!{}) 169 | //! 170 | //! option! { let x <- Some(1); x } 171 | //! ------------------------------------ 172 | //! Some(1).and_then(move |x| option!{ x }) 173 | //! 174 | //! option! { let mut x <- Some(1); x } 175 | //! ------------------------------------ 176 | //! Some(1).and_then(move |mut x| option!{ x }) 177 | //! ``` 178 | //! 179 | //! ## Example 180 | //! 181 | //! ``` 182 | //! # #[macro_use] 183 | //! # extern crate comp; 184 | //! # 185 | //! # fn main() { 186 | //! let option = option! { 187 | //! let a <- Some(1); 188 | //! let b <- Some(2); 189 | //! a + b 190 | //! }; 191 | //! 192 | //! // code above is expanded roughly into this 193 | //! 194 | //! let option = { 195 | //! Some(1).and_then(move |a| { 196 | //! Some(2).and_then(move |b| { 197 | //! Some(a + b) 198 | //! }) 199 | //! }) 200 | //! }; 201 | //! # } 202 | //! ``` 203 | //! 204 | //! ``` 205 | //! # #[macro_use] 206 | //! # extern crate comp; 207 | //! # 208 | //! # fn main() { 209 | //! let iter = iter! { 210 | //! let x <- 0..2; 211 | //! let y <- vec!['a', 'b']; 212 | //! (x, y) 213 | //! }; 214 | //! 215 | //! // code above is expanded roughly into this 216 | //! 217 | //! let iter = { 218 | //! (0..2).into_iter().flat_map(move |x| { 219 | //! (vec!['a', 'b']).into_iter().flat_map(move |y| { 220 | //! ::std::iter::once((x, y)) 221 | //! }) 222 | //! }) 223 | //! }; 224 | //! # } 225 | //! ``` 226 | //! 227 | //! ## 2. Yield 228 | //! 229 | //! The last expression of the block will be yielded, similar to functions in rust. 230 | //! 231 | //! ``` 232 | //! # #[macro_use] 233 | //! # extern crate comp; 234 | //! # 235 | //! # fn main() { 236 | //! let iter = iter! { 237 | //! let x <- 0..2; 238 | //! let y <- vec!['a', 'b']; 239 | //! 240 | //! (x, y) // <------- Yield 241 | //! }; 242 | //! # } 243 | //! ``` 244 | //! 245 | //! The block yields `()` while the last line is __*arrow statement*__ or statement 246 | //! with __*semicolon*__. 247 | //! 248 | //! ``` 249 | //! # #[macro_use] 250 | //! # extern crate comp; 251 | //! # 252 | //! # fn main() { 253 | //! let option: Option<()> = option! { 254 | //! let a <- Some(1); 255 | //! let b <- Some(2); 256 | //! }; 257 | //! 258 | //! let option: Option<()> = option! { 259 | //! let a <- Some(1); 260 | //! let b <- Some(2); 261 | //! a + b; 262 | //! }; 263 | //! # } 264 | //! ``` 265 | //! 266 | //! ## 3. Pattern 267 | //! 268 | //! In `comp-rs`, pattern is supported as it should be. 269 | //! 270 | //! ## Tuple 271 | //! 272 | //! ``` 273 | //! # #[macro_use] 274 | //! # extern crate comp; 275 | //! # 276 | //! # fn main() { 277 | //! let option = option! { 278 | //! let (x, y) <- Some((1, 2)); 279 | //! (y, x) 280 | //! }; 281 | //! 282 | //! assert_eq!(option, Some((2, 1))); 283 | //! # } 284 | //! ``` 285 | //! 286 | //! ## Struct 287 | //! 288 | //! ``` 289 | //! # #[macro_use] 290 | //! # extern crate comp; 291 | //! # 292 | //! # fn main() { 293 | //! struct Struct { x: usize }; 294 | //! 295 | //! let option = option! { 296 | //! let Struct { x } <- Some(Struct { x: 1 }); 297 | //! x 298 | //! }; 299 | //! 300 | //! assert_eq!(option, Some(1)); 301 | //! # } 302 | //! ``` 303 | //! 304 | //! ## Ignore 305 | //! 306 | //! ``` 307 | //! # #[macro_use] 308 | //! # extern crate comp; 309 | //! # 310 | //! # fn main() { 311 | //! let option = option! { 312 | //! let _ <- Some(1); 313 | //! }; 314 | //! # } 315 | //! ``` 316 | //! 317 | //! ## 4. If-Guard 318 | //! 319 | //! If-Guard is specific for `iter!` which translates condition into `filter()`. 320 | //! 321 | //! It wraps the following code into a block and call `filter()` on it. 322 | //! 323 | //! ``` 324 | //! # #[macro_use] 325 | //! # extern crate comp; 326 | //! # 327 | //! # fn main() { 328 | //! let iter = iter! { 329 | //! let x <- 0..4; 330 | //! let y <- 2..6; 331 | //! 332 | //! if x == y; 333 | //! 334 | //! // won't reach here if condition isn't satisfied 335 | //! (x, y) 336 | //! }; 337 | //! 338 | //! let expected = vec![(2, 2), (3, 3)]; 339 | //! assert_eq!(expected, iter.collect::>()); 340 | //! # } 341 | //! ``` 342 | //! 343 | //! ## 5. Statement & Block 344 | //! 345 | //! Statements and blocks are also supported. 346 | //! 347 | //! ``` 348 | //! # #[macro_use] 349 | //! # extern crate comp; 350 | //! # 351 | //! # fn main() { 352 | //! // statement 353 | //! let iter = iter! { 354 | //! let start = 5; 355 | //! let end; 356 | //! end = start * 3; 357 | //! 358 | //! // 5, 6, ..., 13, 14 359 | //! let x <- start..end; 360 | //! x 361 | //! }; 362 | //! let expected = 5..15; 363 | //! assert!(iter.eq(expected.into_iter())); 364 | //! # } 365 | //! ``` 366 | //! ``` 367 | //! # #[macro_use] 368 | //! # extern crate comp; 369 | //! # 370 | //! # fn main() { 371 | //! let iter = iter! { 372 | //! let mut a <- 0..5; 373 | //! 374 | //! // block 375 | //! { 376 | //! fn double(x: u8) -> u8 { x * 2} 377 | //! let tmp = double(a); 378 | //! a = tmp; 379 | //! }; 380 | //! 381 | //! // unsafe block 382 | //! let count = unsafe { 383 | //! static mut CALL_COUNT: u8 = 0; 384 | //! CALL_COUNT += 1; 385 | //! CALL_COUNT 386 | //! }; 387 | //! 388 | //! (a, count) 389 | //! }; 390 | //! let expected = vec![(0, 1), (2, 2), (4, 3), (6, 4), (8, 5)]; 391 | //! assert!(iter.eq(expected.into_iter())); 392 | //! # } 393 | //! ``` 394 | //! 395 | //! # Array 396 | //! 397 | //! `Array` in rust behaves differently from other collections. It only iterates its 398 | //! content by reference. 399 | //! So `iter!` always binds *references* in `arrow(<-)` syntax, then you need to 400 | //! *deref* the bound value. 401 | //! And since one can't move any value out of an `array`, array should be placed 402 | //! outside the macro to satisfy lifetime. 403 | //! 404 | //! ``` 405 | //! # #[macro_use] 406 | //! # extern crate comp; 407 | //! # 408 | //! # fn main() { 409 | //! let array = [0, 1, 2, 3]; 410 | //! let iter = iter! { 411 | //! let x <- array; 412 | //! let y <- *x..4; 413 | //! (*x, y) 414 | //! }; 415 | //! let expected = vec![(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), 416 | //! (2, 3), (3, 3)]; 417 | //! assert_eq!(expected, iter.collect::>()); 418 | //! # } 419 | //! ``` 420 | //! 421 | //! # Contribution 422 | //! 423 | //! All kinds of contribution are welcome. 424 | //! 425 | //! - **Issue.** Feel free to open an issue when you find typos, bugs, or have any question. 426 | //! - **Pull requests**. Better implementation, more tests, more documents and typo fixes are all welcome. 427 | //! 428 | //! # License 429 | //! 430 | //! Licensed under MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 431 | 432 | /// syntax extension specific for Option 433 | /// 434 | /// See the module-level documentation for more details. 435 | #[macro_export] 436 | macro_rules! option { 437 | (@as_pat $p: pat) => ($p); 438 | 439 | () => { 440 | Some(()) 441 | }; 442 | 443 | ( 444 | let mut $p: tt <- $e: expr ; $( $t: tt )* 445 | ) => ( 446 | $e.and_then(move | option! (@as_pat mut $p) | { option! { $( $t )* } } ) 447 | ); 448 | 449 | ( 450 | let mut $p: ident : $ty: tt <- $e: expr ; $( $t: tt )* 451 | ) => ( 452 | $e.and_then(move | mut $p : $ty | { option! { $( $t )* } } ) 453 | ); 454 | 455 | ( 456 | let $p: tt <- $e: expr ; $( $t: tt )* 457 | ) => ( 458 | $e.and_then(move | option! (@as_pat $p) | { option! { $( $t )* } } ) 459 | ); 460 | 461 | ( 462 | let $p: tt ( $( $para: tt )* ) <- $e: expr ; $( $t: tt )* 463 | ) => ( 464 | $e.and_then(move | option! (@as_pat $p ( $( $para )* ) ) | { option! { $( $t )* } } ) 465 | ); 466 | 467 | ( 468 | let $p: tt { $( $para: tt )* } <- $e: expr ; $( $t: tt )* 469 | ) => ( 470 | $e.and_then(move | option! (@as_pat $p { $( $para )* } ) | { option! { $( $t )* } } ) 471 | ); 472 | 473 | ( 474 | let $p: ident : $ty: tt <- $e: expr ; $( $t: tt )* 475 | ) => ( 476 | $e.and_then(move | $p : $ty | { option! { $( $t )* } } ) 477 | ); 478 | 479 | ( 480 | $stmt: stmt ; $( $t: tt )* 481 | ) => ( 482 | { $stmt ; option! { $( $t )* } } 483 | ); 484 | 485 | ( 486 | $e: expr ; $( $t: tt )* 487 | ) => ( 488 | { $e ; option! { $( $t )* } } 489 | ); 490 | 491 | ( 492 | $e: expr 493 | ) => ( 494 | Some($e) 495 | ); 496 | 497 | ( 498 | $b: block ; $( $t: tt )* 499 | ) => ( 500 | $b ; option! { $( $t )* } 501 | ); 502 | } 503 | 504 | /// syntax extension specific for Result 505 | /// 506 | /// See the module-level documentation for more details. 507 | #[macro_export] 508 | macro_rules! result { 509 | (@as_pat $p: pat) => ($p); 510 | 511 | () => { 512 | Ok(()) 513 | }; 514 | 515 | ( 516 | let mut $p: tt <- $e: expr ; $( $t: tt )* 517 | ) => ( 518 | $e.and_then(move | result! (@as_pat mut $p) | { result! { $( $t )* } } ) 519 | ); 520 | 521 | ( 522 | let mut $p: ident : $ty: tt <- $e: expr ; $( $t: tt )* 523 | ) => ( 524 | $e.and_then(move | mut $p : $ty | { result! { $( $t )* } } ) 525 | ); 526 | 527 | ( 528 | let $p: tt <- $e: expr ; $( $t: tt )* 529 | ) => ( 530 | $e.and_then(move | result! (@as_pat $p) | { result! { $( $t )* } } ) 531 | ); 532 | 533 | ( 534 | let $p: tt ( $( $para: tt )* ) <- $e: expr ; $( $t: tt )* 535 | ) => ( 536 | $e.and_then(move | result! (@as_pat $p ( $( $para )* ) ) | { result! { $( $t )* } } ) 537 | ); 538 | 539 | ( 540 | let $p: tt { $( $para: tt )* } <- $e: expr ; $( $t: tt )* 541 | ) => ( 542 | $e.and_then(move | result! (@as_pat $p { $( $para )* } ) | { result! { $( $t )* } } ) 543 | ); 544 | 545 | ( 546 | let $p: ident : $ty: tt <- $e: expr ; $( $t: tt )* 547 | ) => ( 548 | $e.and_then(move | $p : $ty | { result! { $( $t )* } } ) 549 | ); 550 | 551 | ( 552 | $stmt: stmt ; $( $t: tt )* 553 | ) => ( 554 | { $stmt ; result! { $( $t )* } } 555 | ); 556 | 557 | ( 558 | $e: expr ; $( $t: tt )* 559 | ) => ( 560 | { $e ; result! { $( $t )* } } 561 | ); 562 | 563 | ( 564 | $e: expr 565 | ) => ( 566 | Ok($e) 567 | ); 568 | 569 | ( 570 | $b: block ; $( $t: tt )* 571 | ) => ( 572 | $b ; result! { $( $t )* } 573 | ); 574 | } 575 | 576 | /// syntax extension specific for Iterator 577 | /// 578 | /// See the module-level documentation for more details. 579 | #[macro_export] 580 | macro_rules! iter { 581 | (@as_pat $p: pat) => ($p); 582 | 583 | () => { 584 | Some(()) 585 | }; 586 | 587 | ( 588 | let mut $p: tt <- $e: expr ; $( $t: tt )* 589 | ) => ( 590 | $e.into_iter().flat_map(move | iter! (@as_pat mut $p) | { iter! { $( $t )* } } ) 591 | ); 592 | 593 | ( 594 | let mut $p: ident : $ty: tt <- $e: expr ; $( $t: tt )* 595 | ) => ( 596 | $e.into_iter().flat_map(move | mut $p : $ty | { iter! { $( $t )* } } ) 597 | ); 598 | 599 | ( 600 | let $p: tt <- $e: expr ; $( $t: tt )* 601 | ) => ( 602 | $e.into_iter().flat_map(move | iter! (@as_pat $p) | { iter! { $( $t )* } } ) 603 | ); 604 | 605 | ( 606 | let $p: tt ( $( $para: tt )* ) <- $e: expr ; $( $t: tt )* 607 | ) => ( 608 | $e.into_iter().flat_map(move | iter! (@as_pat $p ( $( $para )* ) ) | { iter! { $( $t )* } } ) 609 | ); 610 | 611 | ( 612 | let $p: tt { $( $para: tt )* } <- $e: expr; $( $t: tt )* 613 | ) => ( 614 | $e.into_iter().flat_map(move | iter! (@as_pat $p { $( $para )* } ) | { iter! { $( $t )* } } ) 615 | ); 616 | 617 | ( 618 | let $p: ident : $ty: tt <- $e: expr ; $( $t: tt )* 619 | ) => ( 620 | $e.into_iter().flat_map(move | $p : $ty | { iter! { $( $t )* } } ) 621 | ); 622 | 623 | ( 624 | if $e: expr ; $( $t: tt )* 625 | ) => ( 626 | ( iter! { $( $t )* } ).into_iter().filter(move |_| $e) 627 | ); 628 | 629 | ( 630 | $stmt: stmt ; $( $t: tt )* 631 | ) => ( 632 | { $stmt ; iter! { $( $t )* } } 633 | ); 634 | 635 | ( 636 | $e: expr ; $( $t: tt )* 637 | ) => ( 638 | { $e ; iter! { $( $t )* } } 639 | ); 640 | 641 | ( 642 | $e: expr 643 | ) => ( 644 | Some($e) 645 | ); 646 | 647 | ( 648 | $b: block ; $( $t: tt )* 649 | ) => ( 650 | $b ; iter! { $( $t )* } 651 | ); 652 | } 653 | 654 | #[cfg(test)] 655 | mod tests { 656 | #![allow(unused_variables)] 657 | #![allow(dead_code)] 658 | 659 | fn ok(t: T) -> Result { 660 | Ok(t) 661 | } 662 | 663 | #[test] 664 | fn test_basic() { 665 | let option = option! { 666 | let a <- Some(1); 667 | let b <- Some(2); 668 | }; 669 | assert_eq!(option, Some(())); 670 | 671 | let option = option! { 672 | let a <- Some(1); 673 | let b <- Some('a'); 674 | (a, b) 675 | }; 676 | assert_eq!(option, Some((1, 'a'))); 677 | 678 | let option = option! { 679 | let a <- Some(1); 680 | let b <- None::<()>; 681 | (a, b) 682 | }; 683 | assert_eq!(option, None); 684 | 685 | let option = option! { 686 | let a <- None::<()>; 687 | let b <- Some(2); 688 | (a, b) 689 | }; 690 | assert_eq!(option, None); 691 | 692 | let result = result! { 693 | let a <- ok(1); 694 | let b <- ok(2); 695 | }; 696 | assert_eq!(result, Ok(())); 697 | 698 | let result = result! { 699 | let a <- ok(1); 700 | let b <- ok('a'); 701 | (a, b) 702 | }; 703 | assert_eq!(result, Ok((1, 'a'))); 704 | 705 | let result = result! { 706 | let a <- Err::<(), _>(1); 707 | let b <- Ok('a'); 708 | (a, b) 709 | }; 710 | assert_eq!(result, Err(1)); 711 | 712 | let result = result! { 713 | let a <- Ok('a'); 714 | let b <- Err::<(), _>(2); 715 | (a, b) 716 | }; 717 | assert_eq!(result, Err(2)); 718 | 719 | let iter = iter! { 720 | let x <- vec![0, 1, 2, 3]; 721 | let y <- x..4; 722 | (x, y) 723 | }; 724 | let expected = vec![(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), 725 | (2, 3), (3, 3)]; 726 | assert!(iter.eq(expected.into_iter())); 727 | } 728 | 729 | #[test] 730 | fn test_array() { 731 | let array = [0, 1, 2, 3]; 732 | let iter = iter! { 733 | let x <- array; 734 | let y <- *x..4; 735 | (*x, y) 736 | }; 737 | let expected = vec![(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), 738 | (2, 3), (3, 3)]; 739 | assert!(iter.eq(expected.into_iter())); 740 | } 741 | 742 | #[test] 743 | fn test_guard() { 744 | let iter = iter! { 745 | let x <- 0..4; 746 | let y <- x..4; 747 | if x * 2 == y; 748 | (x, y) 749 | }; 750 | let expected = vec![(0, 0), (1, 2)]; 751 | assert!(iter.eq(expected.into_iter())); 752 | } 753 | 754 | #[test] 755 | fn test_pattern() { 756 | struct TupleStruct0(); 757 | struct TupleStruct(usize); 758 | struct TupleStruct2(usize, usize); 759 | struct Struct { 760 | x: usize, 761 | }; 762 | struct Struct2 { 763 | x: usize, 764 | y: usize, 765 | }; 766 | 767 | let option = option! { 768 | let (x, y, z) <- Some((1, 2, 3)); 769 | (x, y, z) 770 | }; 771 | assert_eq!(option, Some((1, 2, 3))); 772 | 773 | let option = option! { 774 | let (x, (y, z)) <- Some((1, (2, 3))); 775 | (x, y, z) 776 | }; 777 | assert_eq!(option, Some((1, 2, 3))); 778 | 779 | let option = option! { 780 | let TupleStruct0() <- Some(TupleStruct0()); 781 | }; 782 | assert_eq!(option, Some(())); 783 | 784 | let option = option! { 785 | let TupleStruct0() <- Some(TupleStruct0()); 786 | }; 787 | assert_eq!(option, Some(())); 788 | 789 | let option = option! { 790 | let TupleStruct(x) <- Some(TupleStruct(9)); 791 | x 792 | }; 793 | assert_eq!(option, Some(9)); 794 | 795 | let option = option! { 796 | let TupleStruct2(x, y) <- Some(TupleStruct2(9, 10)); 797 | (x, y) 798 | }; 799 | assert_eq!(option, Some((9, 10))); 800 | 801 | let option = option! { 802 | let Struct { x } <- Some(Struct { x: 8 }); 803 | x 804 | }; 805 | assert_eq!(option, Some(8)); 806 | 807 | let option = option! { 808 | let Struct2 { x, y } <- Some(Struct2 { x: 9, y: 10 }); 809 | (x, y) 810 | }; 811 | assert_eq!(option, Some((9, 10))); 812 | } 813 | 814 | #[test] 815 | fn test_ignore() { 816 | struct TupleStruct(usize); 817 | struct TupleStruct2(usize, usize); 818 | struct Struct { 819 | x: usize, 820 | }; 821 | struct Struct2 { 822 | x: usize, 823 | y: usize, 824 | }; 825 | 826 | let option = option! { 827 | let _ <- Some(0); 828 | }; 829 | assert_eq!(option, Some(())); 830 | 831 | let iter = iter! { 832 | let _ <- 0..10; 833 | 1 834 | }; 835 | assert_eq!(iter.sum::(), 10); 836 | 837 | let option = option! { 838 | let (_, y, _) <- Some((1, 2, 3)); 839 | y 840 | }; 841 | assert_eq!(option, Some(2)); 842 | 843 | let option = option! { 844 | let (_, (y, _)) <- Some((1, (2, 3))); 845 | y 846 | }; 847 | assert_eq!(option, Some(2)); 848 | 849 | let option = option! { 850 | let TupleStruct(_) <- Some(TupleStruct(9)); 851 | }; 852 | assert_eq!(option, Some(())); 853 | 854 | let option = option! { 855 | let TupleStruct2(x, _) <- Some(TupleStruct2(9, 10)); 856 | x 857 | }; 858 | assert_eq!(option, Some(9)); 859 | 860 | let option = option! { 861 | let Struct { x: _ } <- Some(Struct { x: 8 }); 862 | }; 863 | assert_eq!(option, Some(())); 864 | 865 | let option = option! { 866 | let Struct2 { x, y: _ } <- Some(Struct2 { x: 9, y: 10 }); 867 | x 868 | }; 869 | assert_eq!(option, Some(9)); 870 | } 871 | 872 | #[test] 873 | fn test_if_expression() { 874 | let iter = iter! { 875 | let x <- 0..5; 876 | let y <- if x % 2 == 0 { Some(x + 1) } else { None }; 877 | y 878 | }; 879 | let expected = vec![1, 3, 5]; 880 | assert!(iter.eq(expected.into_iter())); 881 | 882 | let iter = iter! { 883 | let x <- 0..5; 884 | if x < 2 { 0 } else { 1 } 885 | }; 886 | let expected = vec![0, 0, 1, 1, 1]; 887 | assert!(iter.eq(expected.into_iter())); 888 | } 889 | 890 | #[test] 891 | fn test_statement() { 892 | let iter = iter! { 893 | let start = 5; 894 | let end; 895 | end = start * 3; 896 | 897 | // 5, 6, ..., 13, 14 898 | let x <- start..end; 899 | x 900 | }; 901 | let expected = 5..15; 902 | assert!(iter.eq(expected.into_iter())); 903 | } 904 | 905 | #[test] 906 | fn test_block() { 907 | let iter = iter! { 908 | let mut a <- 0..5; 909 | 910 | { 911 | fn double(x: u8) -> u8 { x * 2} 912 | let tmp = double(a); 913 | a = tmp; 914 | }; 915 | 916 | let count = unsafe { 917 | static mut CALL_COUNT: u8 = 0; 918 | CALL_COUNT += 1; 919 | 920 | CALL_COUNT 921 | }; 922 | 923 | (a, count) 924 | }; 925 | let expected = vec![(0, 1), (2, 2), (4, 3), (6, 4), (8, 5)]; 926 | assert!(iter.eq(expected.into_iter())); 927 | } 928 | 929 | #[test] 930 | fn test_mut() { 931 | struct TupleStruct2(usize, usize); 932 | 933 | let option = option! { 934 | let mut a <- Some(2); 935 | a = a + 10; 936 | 937 | let (mut b,) <- Some((3,)); 938 | b = b + 10; 939 | 940 | (a, b) 941 | }; 942 | assert_eq!(option, Some((12, 13))); 943 | 944 | let result = result! { 945 | let mut a <- ok(2); 946 | a = a + 10; 947 | 948 | let TupleStruct2(mut b, _) <- ok(TupleStruct2(3, 4)); 949 | b = b + 10; 950 | 951 | (a, b) 952 | }; 953 | assert_eq!(result, ok((12, 13))); 954 | 955 | let iter = iter! { 956 | let mut a <- 2..3; 957 | a = a + 10; 958 | a 959 | }; 960 | let expected = vec![12]; 961 | assert!(iter.eq(expected.into_iter())); 962 | } 963 | 964 | #[test] 965 | fn test_comments() { 966 | option! { 967 | // single line comments 968 | let a <- Some(1); 969 | 970 | /* 971 | * block comments 972 | */ 973 | let b <- Some(2); 974 | }; 975 | } 976 | 977 | #[test] 978 | fn test_nested() { 979 | let iter = iter! { 980 | let a <- 0..2; 981 | 982 | option! { 983 | let b <- Some(a); 984 | (b,) 985 | } 986 | }; 987 | let expected = vec![Some((0,)), Some((1,))]; 988 | assert!(iter.eq(expected.into_iter())); 989 | } 990 | } 991 | --------------------------------------------------------------------------------