#![feature(gen_blocks, try_blocks, closure_lifetime_binder, arbitrary_self_types, arbitrary_self_types_pointers)]

pub mod generators {
    /// a wrapper around gen blocks. it has several different forms and purposes
    /// the generator macro, in its simplist form, wraps a block and allows the use of the yield keyword within. 
    /// it transforms wrapped code into a coroutine that can stop at specific breakpoints and transfer data out, denoted by the yield keyword.
    /// after the yield keyword, you then provide a value for the generator to yield. each time the generator yields, 
    /// it pauses, and returns an Option of the value, where None represents the completion of the generator.
    /// when created, a generator only runs when you call .next(). this is because generators are represented by iterators. 
    /// 
    /// for example: 
    /// generator!({
    ///    yield 1;
    /// } -> i32)
    /// the arrow after the block is the type of the data that it yields.
    /// 
    /// the macro allows you to add the move keyword to the beginning of the generator to make the underlying gen block also move.
    /// 
    /// the macro also supports generator closures.
    /// they are made in the exact same way as regular closures, but the return type (now the yield type) must be specified.
    /// 
    /// for example:
    /// 
    /// generator!(|x, y| -> i32 {
    ///     for n in x..=y {
    ///         yield n;
    ///     }
    /// })
    /// 
    /// you can also create a generator closure and call it immediatly:
    /// 
    /// generator!(|slice: &[u8] = vec![1,2,3,4,5]| -> &[u8] {
    ///     for l in 0..slice.len() {
    ///         yield &slice[..l]
    ///     }
    /// })
    /// 
    /// due to the the nature of generators, all generator closures are either Fn or FnOnce.
    /// 
    /// the macro is able to specify the yield type of gen blocks by boxing the gen blocks and casting them to Box<dyn Iterator<Item = Output>>, where Output is the yield type.
    /// the macro also provides a way to quickly get a generator type. for example:
    /// 
    /// let a: generator!(i32) = (generator!(|x| -> i32 {
    ///     yield x
    /// }))(78);
    /// 
    /// finally, the macro provides a unique syntax for gen blocks that wrap a for loop, which allows generators to consume iterators and other generators while yielding data.
    /// Exampel:
    /// 
    /// generator!(line in sdtin().lines() => {
    ///     if let Ok(line) = line {
    ///         yield line;
    ///     } else {
    ///         continue;
    ///     };
    /// } -> String)
    #[macro_export] macro_rules! generator {
        ( $generator:block -> $output:ty) => {
            (Box::new(gen $generator) as Box<dyn Iterator<Item = $output>>)
        };
        ( move $generator:block -> $output:ty) => {
            (Box::new(gen move $generator) as Box<dyn Iterator<Item = $output>>)
        };
        ( |$($param:ident $(: $param_ty:ty)?),*| -> $output:ty $generator:block ) => {
            |$($param $(: $param_ty)?,)*| -> Box<dyn Iterator<Item = $output>> {Box::new(gen move $generator) as Box<dyn Iterator<Item = $output>>}
        };

        ( ($($var:ident $(: $var_ty:ty)? = $var_value:expr),*) $generator:block -> $output:ty ) => {
            (|$($var $(: $var_ty)?,)*| -> Box<dyn Iterator<Item = $output>> {Box::new(gen move $generator) as Box<dyn Iterator<Item = $output>>})($($var_value,)*)
        };

        ( $output_ty:ty ) => {
            Box<dyn Iterator<Item = $output_ty>>
        };

        ( $var:pat_param in $source:expr => $generator:block -> $output:ty ) => {
            generator!({
                for $var in $source {
                    $generator
                }
            } -> $output)
        }
    }
    pub fn with_type<T>(subject: T) -> T {
        subject
    }
}

