3
\$\begingroup\$

I'm very new to Rust and as I've been going through the book I became very interested in its macros. To better understand them I tried to write something of a usable list comprehension like those in Haskell and Python. I've asked and had answered a question on SO in the process: Rust macro error: local ambiguity: multiple parsing options

Ultimately, I've ended up with what's below but I still find that I'm not at ease that my choices of fragment types is necessarily optimal (or even correct besides working in my examples).

If it's not clear from my test examples at the bottom of the code, the goal is to allow destructive iterators and let statements in generating the "list" and call the macro recursively to scope things in deeper and deeper loops.

What improvements could be made to make this more robust and correct?

macro_rules! comp {

    ($f: expr, $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)*) => {{
       comp![$f, $x<-$iterx; $(let $s= $v;)*, true]
    }};
    ($f: expr, $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)* $(| $y: ident <- $itery:expr; $(let $t: ident = $w:expr;)*)*) => {
        comp![$f, $x <- $iterx; $(let $s = $v;)* $(| $y <- $itery; $(let $t = $w;)*)*, true]
    };
    ($f: expr, $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)* $(| $y: ident <- $itery:expr; $(let $t: ident = $w:expr;)*)+, $cond: expr) => {{
        let mut myvec = Vec::new();
        let iter=$iterx;
        for $x in iter {
            $(let $s = $v;)*
            comp![$f, $(| $y <- $itery; $(let $t = $w;)*)+, $cond; myvec]
        }
        myvec
    }};

    ($f: expr, $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)*, $cond: expr) => {{
        let mut myvec = Vec::new();
        let iter=$iterx;
        for $x in iter {
            $(let $s = $v;)*
            if $cond {
                myvec.push($f);
            };
        }
        myvec
    }};
    ($f: expr, | $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)*, $cond: expr; $myvec: ident) => {{
        let iter=$iterx;
        for $x in iter {
            $(let $s = $v;)*
            if $cond {
                $myvec.push($f);
            };
        }
    }};
    ($f: expr, | $x: ident <- $iterx:expr; $(let $s: ident = $v:expr;)* $(| $y: ident <- $itery:expr; $(let $t: ident = $w:expr;)*)+; $cond: expr; $myvec: ident) => {{
        let iter=$iterx;
        for $x in iter {
            $(let $s = $v;)*
            comp![$f, $(| $y <- $itery; $(let $t = $w;)*)+, $cond; $myvec]
        }
    }};
}

#[cfg(test)]
mod tests {
    #[test]
    fn test() {
        assert_eq!(
            comp![(i,j), i <- 1..3; | j <- 1..5; ],
            vec![
                (1, 1),
                (1, 2),
                (1, 3),
                (1, 4),
                (2, 1),
                (2, 2),
                (2, 3),
                (2, 4)
            ]
        );
        assert_eq!(
            comp![ (i,j), i <- 1..4; let k = i*i;| j <- 1..k; ],
            vec![
                (2, 1),
                (2, 2),
                (2, 3),
                (3, 1),
                (3, 2),
                (3, 3),
                (3, 4),
                (3, 5),
                (3, 6),
                (3, 7),
                (3, 8)
            ]
        );
        assert_eq!(
            comp![ comp![ (i,j) , i <- 1..3; ] , j <- 1..6;],
            vec![
                [(1, 1), (2, 1)],
                [(1, 2), (2, 2)],
                [(1, 3), (2, 3)],
                [(1, 4), (2, 4)],
                [(1, 5), (2, 5)]
            ]
        );
        assert_eq!(
            comp![(i,j), i <- 1..3; | j <- 1..5;, j>3],
            vec![(1, 4), (2, 4)]
        );
        assert_eq!(
            comp![ (i,j), i <- 1..4; let k = i*i;| j <- 1..k;, j>3],
            vec![(3, 4), (3, 5), (3, 6), (3, 7), (3, 8)]
        );
        assert_eq!(
            comp![ comp![ (i,j) , i <- 1..3; ] , j <- 1..6;, j>3],
            vec![[(1, 4), (2, 4)], [(1, 5), (2, 5)]]
        );
        assert_eq!(
            comp![ comp![ (i,j, iv*ivv, k) , i <- vec![0,2,4,5].into_iter(); let iv=i*i*i; let ivv=iv*3;] , j <- vec![0,2,4,5].into_iter(); | k <-9..17; let kv=j*k;, kv*kv>400],
            vec![
                [
                    (0, 2, 0, 11),
                    (2, 2, 192, 11),
                    (4, 2, 12288, 11),
                    (5, 2, 46875, 11)
                ],
                [
                    (0, 2, 0, 12),
                    (2, 2, 192, 12),
                    (4, 2, 12288, 12),
                    (5, 2, 46875, 12)
                ],
                [
                    (0, 2, 0, 13),
                    (2, 2, 192, 13),
                    (4, 2, 12288, 13),
                    (5, 2, 46875, 13)
                ],
                [
                    (0, 2, 0, 14),
                    (2, 2, 192, 14),
                    (4, 2, 12288, 14),
                    (5, 2, 46875, 14)
                ],
                [
                    (0, 2, 0, 15),
                    (2, 2, 192, 15),
                    (4, 2, 12288, 15),
                    (5, 2, 46875, 15)
                ],
                [
                    (0, 2, 0, 16),
                    (2, 2, 192, 16),
                    (4, 2, 12288, 16),
                    (5, 2, 46875, 16)
                ],
                [
                    (0, 4, 0, 9),
                    (2, 4, 192, 9),
                    (4, 4, 12288, 9),
                    (5, 4, 46875, 9)
                ],
                [
                    (0, 4, 0, 10),
                    (2, 4, 192, 10),
                    (4, 4, 12288, 10),
                    (5, 4, 46875, 10)
                ],
                [
                    (0, 4, 0, 11),
                    (2, 4, 192, 11),
                    (4, 4, 12288, 11),
                    (5, 4, 46875, 11)
                ],
                [
                    (0, 4, 0, 12),
                    (2, 4, 192, 12),
                    (4, 4, 12288, 12),
                    (5, 4, 46875, 12)
                ],
                [
                    (0, 4, 0, 13),
                    (2, 4, 192, 13),
                    (4, 4, 12288, 13),
                    (5, 4, 46875, 13)
                ],
                [
                    (0, 4, 0, 14),
                    (2, 4, 192, 14),
                    (4, 4, 12288, 14),
                    (5, 4, 46875, 14)
                ],
                [
                    (0, 4, 0, 15),
                    (2, 4, 192, 15),
                    (4, 4, 12288, 15),
                    (5, 4, 46875, 15)
                ],
                [
                    (0, 4, 0, 16),
                    (2, 4, 192, 16),
                    (4, 4, 12288, 16),
                    (5, 4, 46875, 16)
                ],
                [
                    (0, 5, 0, 9),
                    (2, 5, 192, 9),
                    (4, 5, 12288, 9),
                    (5, 5, 46875, 9)
                ],
                [
                    (0, 5, 0, 10),
                    (2, 5, 192, 10),
                    (4, 5, 12288, 10),
                    (5, 5, 46875, 10)
                ],
                [
                    (0, 5, 0, 11),
                    (2, 5, 192, 11),
                    (4, 5, 12288, 11),
                    (5, 5, 46875, 11)
                ],
                [
                    (0, 5, 0, 12),
                    (2, 5, 192, 12),
                    (4, 5, 12288, 12),
                    (5, 5, 46875, 12)
                ],
                [
                    (0, 5, 0, 13),
                    (2, 5, 192, 13),
                    (4, 5, 12288, 13),
                    (5, 5, 46875, 13)
                ],
                [
                    (0, 5, 0, 14),
                    (2, 5, 192, 14),
                    (4, 5, 12288, 14),
                    (5, 5, 46875, 14)
                ],
                [
                    (0, 5, 0, 15),
                    (2, 5, 192, 15),
                    (4, 5, 12288, 15),
                    (5, 5, 46875, 15)
                ],
                [
                    (0, 5, 0, 16),
                    (2, 5, 192, 16),
                    (4, 5, 12288, 16),
                    (5, 5, 46875, 16)
                ]
            ]
        );
    }
}
\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.