/// A macro for chaining together statements that act on an initial expression.
/// # Usage
/// Cascading allows you to run multiple statements on a struct generated by an expression without having
/// to repeatedly type it out. This can be very convinient for modifying multiple properties simultaneously,
/// or running a long series of individual methods on a struct.
///
/// When writing a cascade, the first line specifies the expression that will be operated upon.
/// All following lines modify the struct created by the expression in some way.
/// The most common operator is the `..` member operator, which is borrowed from Dart's syntax.
/// It's a convinient shorthand for accessing a member or method of the struct. For example:
/// ```
/// use cascade::cascade;
///
/// let old_vector = vec!(1, 2, 3);
/// let new_vector = cascade! {
///   old_vector;
///   ..push(4);
///   ..push(5);
///   ..push(6);
/// };
/// ```
///
/// Remember, you can't move the struct, because it gets returned at the end of the cascade. In other words,
/// you can't run a method on a struct with the `..` operator if the method takes `self` or
/// `mut self`. But `&self` or `&mut self` works fine.
///
/// You can also put statements without a `..` in a cascade macro:
/// ```
/// use cascade::cascade;
/// use std::collections::HashMap;
///
/// let hash_map = cascade! {
///   HashMap::new();
///   ..insert("foo", "bar");
///   println!("Look! You can put statements in a cascade!");
///   for i in 0..3 {
///     println!("You can put loops in here too! Make sure to put a semicolon at the end!");
///   };
/// };
/// ```
///
/// If you need to reference the expression inside a cascade, you can name it using `let`:
/// ```
/// use cascade::cascade;
///
/// let vector = cascade! {
///   let v = Vec::new();
///   ..push(1);
///   println!("The vector now has {} element(s).", v.len());
///   ..push(2);
/// };
/// ```
/// This will print `The vector now has 1 element(s).`
///
/// Once again, trying to move this value will throw an error.
///
/// Finally, you can also use the member operator (`..`) to set or change members of the cascaded struct:
/// ```
/// use cascade::cascade;
/// struct A {
///   pub b: u32,
///   pub c: u32
/// }
///
/// let a = cascade! {
///   A { b: 0, c: 0 };
///   ..b = 5;
///   ..c += 1;
/// };
/// ```
///
/// More examples of the cascade macro can be found in the examples folder on the Github repository.
#[macro_export]
macro_rules! cascade {
    (let _ : $t:ty = $e: expr; $($tail: tt)*) => {
        cascade!(let __tmp: $t = $e; $($tail)*)
    };
    (let $i:ident : $t:ty = $e: expr; $($tail: tt)*) => {
        {
            let mut $i: $t = $e;
            cascade!(@line $i, $($tail)*)
        }
    };
    (let $i:ident = $e: expr; $($tail: tt)*) => {
      {
        let mut $i = $e;
        cascade!(@line $i, $($tail)*)
      }
    };
    ($e: expr; $($tail: tt)*) => {
        cascade!(let __tmp = $e; $($tail)*)
    };
    (@line $i: ident, .. $v: ident = $e: expr; $($tail: tt)*) => {
        {
            $i.$v = $e;
            $crate::cascade!(@line $i, $($tail)*)
        }
    };
    (@line $i:ident, .. $v:ident += $e:expr; $($tail:tt)*) => {
        {
            $i.$v += $e;
            $crate::cascade!(@line $i, $($tail)*)
        }
    };
    (@line $i:ident, .. $v:ident -= $e:expr; $($tail:tt)*) => {
        {
            $i.$v -= $e;
            $crate::cascade!(@line $i, $($tail)*)
        }
    };
    (@line $i:ident, .. $v:ident *= $e:expr; $($tail:tt)*) => {
        {
            $i.$v *= $e;
            $crate::cascade!(@line $i, $($tail)*)
        }
    };
    (@line $i:ident, .. $($q: ident ($($e: expr),*)).+; $($tail: tt)*) => {
        {
            $i.$($q($($e),*)).+;
            $crate::cascade!(@line $i, $($tail)*)
        }
    };
    (@line $i:ident, .. $($q: ident ($($e: expr),*)).+?; $($tail: tt)*) => {
        {
            $i.$($q($($e),*)).+?;
            $crate::cascade!(@line $i, $($tail)*)
        }
    };
    (@line $i:ident, { $($t:tt)* }; $($tail: tt)*) => {
        {
            { $crate::cascade!(@line $i, $($t)*); }
            $crate::cascade!(@line $i, $($tail)*)
        }
    };
    (@line $i:ident, $s: stmt; $($tail: tt)*) => {
        {
            $s
            cascade!(@line $i, $($tail)*)
        }
    };
    (@line $i:ident, { $($t:tt)* }) => {
        { $crate::cascade!(@line $i, $($t)*) }
    };
    (@line $i:ident, .. $($q: ident ($($e: expr),*)).+) => {
        $i.$($q($($e),*)).+
    };
    (@line $i:ident, $e:expr) => {
        $e
    };
    (@line $i:ident,) => {
        $i
    };
    () => {}
}
