Rust Monad Macro
A Rust Chain Macro

        
          
          //! Chain macro in Rust
        
        
          
          //!
        
        
          
          //! A typical usage is to avoid nested match expressions.
        
        
          
          //!
        
        
          
          //! Supports:
        
        
          
          //! - Pattern matching
        
        
          
          //! - Or expressions (A | B => ...)
        
        
          
          //! - Guarded statements (x if <cond> => ...)
        
        
          
          //! - Implicit else (requires all arms to return same type)
        
        
          
          

        
        
          
          #[feature(macro_rules)];
        
        
          
          

        
        
          
          // The chain macro is syntactically similar to a 'match' statement.
        
        
          
          // Each arm pattern matches against the result of the previous arm.
        
        
          
          // If any pattern match fails, the expression after the 'else' keyword is executed.
        
        
          
          // In the case the 'else' part is omitted, all arms must return same type (usually Result).
        
        
          
          //
        
        
          
          // #Syntax (v4)
        
        
          
          // ```
        
        
          
          // let <name>: <type> = chain!(
        
        
          
          //     do <start> {
        
        
          
          //         <pattern0> => <step1>,
        
        
          
          //         <pattern1> => <step2>,
        
        
          
          //         <pattern2> => <step3>,
        
        
          
          //         ... => <result>
        
        
          
          //     } else <err>
        
        
          
          // );
        
        
          
          // ```
        
        
          
          //
        
        
          
          // No colon is allowed after the last arm.
        
        
          
          // The macro is calling itself recursively. 
        
        
          
          macro_rules! chain(
        
        
          
              // Base case (2 arms).
        
        
          
              (
        
        
          
                  do $start: expr {
        
        
          
                      $($a: pat)|+ $(if $c: expr)* => $b: expr
        
        
          
                  } else $err: expr
        
        
          
              ) => {
        
        
          
                  match $start {
        
        
          
                      $($a)|+ $(if $c)* => $b,
        
        
          
                      _ => $err	
        
        
          
                  }
        
        
          
              };
        
        
          
          

        
        
          
              // Case for more than 2 arms.
        
        
          
              (
        
        
          
                  do $start: expr {
        
        
          
                      $($a_head: pat)|+ $(if $c_head: expr)* => 
        
        
          
                          $b_head: expr $(, $($a: pat)|+ $(if $c: expr)*  => $b: expr)+
        
        
          
                  } else $err: expr
        
        
          
              ) => {
        
        
          
                  match $start {
        
        
          
                      $($a_head)|+ $(if $c_head)* => chain!(
        
        
          
                          do $b_head {
        
        
          
                              $($($a)|+ $(if $c)* => $b),+
        
        
          
                          } else $err 
        
        
          
                      ),
        
        
          
                      _ => $err
        
        
          
                  }
        
        
          
              };
        
        
          
          

        
        
          
              // Base case implicit else
        
        
          
              (do $start: expr {
        
        
          
                  $($a: pat)|+ $(if $c: expr)* => $b: expr
        
        
          
              }) => {
        
        
          
                  match $start {
        
        
          
                      $($a)|+ $(if $c)* => $b,
        
        
          
                      x => x
        
        
          
                  }
        
        
          
              };
        
        
          
          

        
        
          
              // Multi-case implicit else
        
        
          
              (do $start: expr {
        
        
          
                  $($a_head: pat)|+ $(if $c_head: expr)* =>
        
        
          
                      $b_head: expr $(, $($a: pat)|+ $(if $c: expr)* => $b: expr)+
        
        
          
              }) => {
        
        
          
                  match $start {
        
        
          
                      $($a_head)|+ $(if $c_head)* => chain!(
        
        
          
                          do $b_head {
        
        
          
                              $($($a)|+ $(if $c)* => $b),+
        
        
          
                          }
        
        
          
                      ),
        
        
          
                      x => x
        
        
          
                  }
        
        
          
              }
        
        
          
          )
        
        
          
          

        
        
          
          #[allow(dead_code)]
        
        
          
          fn main() {
        
        
          
              use std::path::Path;
        
        
          
              use std::io::fs::File;
        
        
          
              use std::io::BufferedReader;
        
        
          
              use std::vec_ng::Vec;
        
        
          
          

        
        
          
              // Creates a list of numbers from 1 to n where n is the first line in 'n.txt'.
        
        
          
              let numbers: Vec<uint> = chain!(
        
        
          
                  do File::open(&Path::new("n.txt")) {
        
        
          
                      Ok(file) => BufferedReader::new(file).lines().map(|line| line.unwrap()).next(),
        
        
          
                      Some(first_line) => from_str::<uint>(first_line.trim()),
        
        
          
                      Some(n) => range(0, n).map(|x| x + 1).collect()
        
        
          
                  } else Vec::new()
        
        
          
              );
        
        
          
          

        
        
          
              for i in numbers.iter() {
        
        
          
                  println!("{}", i);
        
        
          
              }
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_1() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do Some(5) {
        
        
          
                      Some(x) => Some(x + 1)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(6));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_2_pre() {
        
        
          
              let x: Option<int> = match Some(5) {
        
        
          
                  Some(4) | Some(5) => Some(5),
        
        
          
                  _ => None
        
        
          
              };
        
        
          
              assert_eq!(x, Some(5));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_2() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do Some(5) {
        
        
          
                      Some(4) | Some(5) => Some(4)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(4));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_3() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do Some(5) {
        
        
          
                      Some(4) | Some(5) => Some(4),
        
        
          
                      Some(4) | Some(5) => Some(6)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(6));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_4() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do Some(5) {
        
        
          
                      Some(x) if x == 5 => Some(x+1)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(6));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_5() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do Some(5) {
        
        
          
                      Some(x) if x == 5 => Some(x+1),
        
        
          
                      Some(x) if x == 6 => Some(x+1)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(7));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_6() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do Some(5) {
        
        
          
                      Some(x) if x == 5 => Some(x+1),
        
        
          
                      Some(6) | Some(7) => Some(7)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(7));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_7_pre() {
        
        
          
              let x: Option<int> = match Some(5) {
        
        
          
                  Some(0..10) => Some(6),
        
        
          
                  _ => None,
        
        
          
              };
        
        
          
              assert_eq!(x, Some(6))
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_7() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do Some(5) {
        
        
          
                      Some(0..10) => Some(6)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(6));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_8() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do 5 {
        
        
          
                      4 | 5 => Some(5)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(5));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_9() {
        
        
          
              let x: Option<int> = chain!(
        
        
          
                  do 5 {
        
        
          
                      x @ 0..10 => Some(x)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(x, Some(5));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_10() {
        
        
          
              let x = Some(1);
        
        
          
              let y = Some(2);
        
        
          
              let z = chain!(
        
        
          
                  do x {
        
        
          
                      Some(x) => y,
        
        
          
                      Some(y) => Some(x+y)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(z, Some(3));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_implicit_else() {
        
        
          
              let res = chain!(
        
        
          
                  do Ok(1) {
        
        
          
                      Ok(x) => Ok(2),
        
        
          
                      Ok(y) => Err('z'),
        
        
          
                      Ok(z) => Ok(x+y+z)
        
        
          
                  }
        
        
          
              );
        
        
          
              assert_eq!(res, Err('z'));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_block() {
        
        
          
              let x = Some(1);
        
        
          
              let y = Some(2);
        
        
          
              let z = chain!(
        
        
          
                  do x {
        
        
          
                      Some(x) => {
        
        
          
                          println!("in a block!");
        
        
          
                          y
        
        
          
                      },
        
        
          
                      Some(y) => Some(x+y)
        
        
          
                  }
        
        
          
              );
        
        
          
              assert_eq!(z, Some(3));
        
        
          
          }
        
        
          
          

        
        
          
          #[test]
        
        
          
          pub fn test_inner_chain() {
        
        
          
              let x = Some(1);
        
        
          
              let y = Some(2);
        
        
          
              let z = chain!(
        
        
          
                  do x {
        
        
          
                      Some(x) => chain!(
        
        
          
                          do x {
        
        
          
                              1 => {
        
        
          
                                  println!("in a block!");
        
        
          
                                  y
        
        
          
                              }
        
        
          
                          } else None
        
        
          
                      ),
        
        
          
                      Some(y) => Some(x+y)
        
        
          
                  } else None
        
        
          
              );
        
        
          
              assert_eq!(z, Some(3));
        
        
          
          }
        
  