1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use nom::{
    error::context,
    sequence::{delimited, preceded, terminated, tuple},
};

use crate::parser::expression::{parse_expression, Expression};
use crate::parser::literals::sp;
use crate::parser::literals::{parse_variable, Variable};
use crate::parser::statement::{parse_block, Block};
use crate::parser::tokens::{end, ldo, lfor, lin};
use crate::parser::{Res, Span};

#[derive(Debug, PartialEq, Clone)]
pub struct For<'a> {
    pub iter_item: Variable<'a>,
    pub iterator: Expression<'a>,
    pub block: Block<'a>,
}

pub fn parse_for(input: Span) -> Res<For> {
    tuple((
        parse_iter_item,
        parse_iterator,
        terminated(parse_block, preceded(sp, end)),
    ))(input)
    .map(|(next_input, res)| {
        (
            next_input,
            For {
                iter_item: res.0,
                iterator: res.1,
                block: res.2,
            },
        )
    })
}

fn parse_iter_item<'a>(input: Span<'a>) -> Res<Variable<'a>> {
    context(
        "ForIterItem",
        preceded(preceded(sp, lfor), preceded(sp, parse_variable)),
    )(input)
}

fn parse_iterator(input: Span) -> Res<Expression> {
    context(
        "ForIterator",
        delimited(
            preceded(sp, lin),
            preceded(sp, parse_expression),
            preceded(sp, ldo),
        ),
    )(input)
}

#[cfg(test)]
mod tests {

    use super::*;
    use crate::literals::Token;
    use crate::parser::expression::{ExprOrVarname, Expression, PrefixExpr};
    use crate::parser::literals::Literal;
    use crate::parser::statement::{Block, LAssignment, Statement};

    #[test]
    fn test_parse_iter_item() {
        let string = "for item in iterator do";
        let (_, res) = parse_iter_item(Span::new(string)).unwrap();
        assert_eq!(res, Token::new("item", Span::new("item")));
    }

    #[test]
    fn test_parse_iterator() {
        let string = " in iterator do";
        let (_, res) = parse_iterator(Span::new(string)).unwrap();
        assert_eq!(
            res,
            Expression::PrefixExpr(Box::new(PrefixExpr {
                prefix: ExprOrVarname::Varname(Token::new("iterator", Span::new("iterator"))),
                suffix_chain: vec![]
            }))
        );
    }

    #[test]
    fn test_parse_for_loop() {
        let string = "\nfor x in y do \n let y = 3 \nend";
        let (_, res) = parse_for(Span::new(string)).unwrap();

        assert_eq!(
            res,
            For {
                iter_item: Token::new("x", Span::new("x")),
                iterator: Expression::PrefixExpr(Box::new(PrefixExpr {
                    prefix: ExprOrVarname::Varname(Token::new("y", Span::new("y"))),
                    suffix_chain: vec![]
                })),
                block: Block {
                    statements: vec![Statement::LAssignment(LAssignment {
                        variable: Token::new("y", Span::new("y")),
                        expression: Expression::Literal(Literal::Int(Token::new(
                            3,
                            Span::new("3")
                        )))
                    })],
                    return_stmt: None
                }
            }
        )
    }
}