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
112
113
114
115
116
117
118
119
120
121
122
use crate::parser::expression::parse_expression;
use crate::parser::expression::Expression;
use crate::parser::literals::sp;
use crate::parser::tokens::{end, ldo, lwhile};
use crate::parser::Res;
use crate::Span;

use crate::parser::statement::{parse_block, Block};

use nom::{
    error::context,
    sequence::{delimited, preceded, terminated, tuple},
};

#[derive(Debug, PartialEq, Clone)]
pub struct While<'a> {
    pub cond: Expression<'a>,
    pub block: Block<'a>,
}

pub fn parse_while(input: Span) -> Res<While> {
    tuple((while_condition, terminated(parse_block, preceded(sp, end))))(input).map(
        |(next_input, res)| {
            (
                next_input,
                While {
                    cond: res.0,
                    block: res.1,
                },
            )
        },
    )
}

fn while_condition(input: Span) -> Res<Expression> {
    context(
        "WhileCond",
        delimited(
            preceded(sp, lwhile),
            preceded(sp, parse_expression),
            preceded(sp, ldo),
        ),
    )(input)
}

#[cfg(test)]
mod tests {

    use super::*;

    use crate::literals::Token;
    use crate::parser::expression::binary::BinaryOp;
    use crate::parser::expression::{ExprOrVarname, Expression, PrefixExpr};
    use crate::parser::literals::Literal;
    use crate::parser::statement::declaration::assignment::LAssignment;
    use crate::parser::statement::Statement;
    use crate::parser::tokens::Operator;

    #[test]
    fn parse_while_condition() {
        let string = " while x < 3 do";
        let (_, res) = while_condition(Span::new(string)).unwrap();

        assert_eq!(
            res,
            Expression::BinaryOp(Box::new(BinaryOp {
                op: Operator::Lt,
                left: Expression::PrefixExpr(Box::new(PrefixExpr {
                    prefix: ExprOrVarname::Varname(Token::new("x", Span::new("x"))),
                    suffix_chain: vec![],
                })),
                right: Expression::Literal(Literal::Int(Token::new(3, Span::new("3")))),
            }))
        )
    }

    #[test]
    fn parse_while_statement() {
        let string = "while x < 3 do \nlet z = x + 3\n let y = 3 \nend";
        let (_, res) = parse_while(Span::new(string)).unwrap();

        assert_eq!(
            res,
            While {
                cond: Expression::BinaryOp(Box::new(BinaryOp {
                    op: Operator::Lt,
                    left: Expression::PrefixExpr(Box::new(PrefixExpr {
                        prefix: ExprOrVarname::Varname(Token::new("x", Span::new("x"))),
                        suffix_chain: vec![],
                    })),
                    right: Expression::Literal(Literal::Int(Token::new(3, Span::new("3")))),
                })),
                block: Block {
                    statements: vec![
                        Statement::LAssignment(LAssignment {
                            variable: Token::new("z", Span::new("z")),
                            expression: Expression::BinaryOp(Box::new(BinaryOp {
                                left: Expression::PrefixExpr(Box::new(PrefixExpr {
                                    prefix: ExprOrVarname::Varname(Token::new("x", Span::new("x"))),
                                    suffix_chain: vec![]
                                })),
                                op: Operator::Add,
                                right: Expression::Literal(Literal::Int(Token::new(
                                    3,
                                    Span::new("3")
                                )))
                            }))
                        }),
                        Statement::LAssignment(LAssignment {
                            variable: Token::new("y", Span::new("y")),
                            expression: Expression::Literal(Literal::Int(Token::new(
                                3,
                                Span::new("3")
                            )))
                        })
                    ],
                    return_stmt: None
                }
            }
        )
    }
}