boa/syntax/parser/statement/iteration/
for_statement.rs

1//! For statement parsing.
2//!
3//! More information:
4//!  - [MDN documentation][mdn]
5//!  - [ECMAScript specification][spec]
6//!
7//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
8//! [spec]: https://tc39.es/ecma262/#sec-for-statement
9
10use crate::syntax::lexer::TokenKind;
11use crate::{
12    syntax::{
13        ast::{
14            node::{ForInLoop, ForLoop, ForOfLoop, Node},
15            Const, Keyword, Punctuator,
16        },
17        parser::{
18            expression::Expression,
19            statement::declaration::Declaration,
20            statement::{variable::VariableDeclarationList, Statement},
21            AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser,
22        },
23    },
24    BoaProfiler,
25};
26
27use std::io::Read;
28
29/// For statement parsing
30///
31/// More information:
32///  - [MDN documentation][mdn]
33///  - [ECMAScript specification][spec]
34///
35/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
36/// [spec]: https://tc39.es/ecma262/#sec-for-statement
37#[derive(Debug, Clone, Copy)]
38pub(in crate::syntax::parser::statement) struct ForStatement {
39    allow_yield: AllowYield,
40    allow_await: AllowAwait,
41    allow_return: AllowReturn,
42}
43
44impl ForStatement {
45    /// Creates a new `ForStatement` parser.
46    pub(in crate::syntax::parser::statement) fn new<Y, A, R>(
47        allow_yield: Y,
48        allow_await: A,
49        allow_return: R,
50    ) -> Self
51    where
52        Y: Into<AllowYield>,
53        A: Into<AllowAwait>,
54        R: Into<AllowReturn>,
55    {
56        Self {
57            allow_yield: allow_yield.into(),
58            allow_await: allow_await.into(),
59            allow_return: allow_return.into(),
60        }
61    }
62}
63
64impl<R> TokenParser<R> for ForStatement
65where
66    R: Read,
67{
68    type Output = Node;
69
70    fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
71        let _timer = BoaProfiler::global().start_event("ForStatement", "Parsing");
72        cursor.expect(Keyword::For, "for statement")?;
73        cursor.expect(Punctuator::OpenParen, "for statement")?;
74
75        let init = match cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.kind() {
76            TokenKind::Keyword(Keyword::Var) => {
77                let _ = cursor.next()?;
78                Some(
79                    VariableDeclarationList::new(false, self.allow_yield, self.allow_await)
80                        .parse(cursor)
81                        .map(Node::from)?,
82                )
83            }
84            TokenKind::Keyword(Keyword::Let) | TokenKind::Keyword(Keyword::Const) => {
85                Some(Declaration::new(self.allow_yield, self.allow_await, false).parse(cursor)?)
86            }
87            TokenKind::Punctuator(Punctuator::Semicolon) => None,
88            _ => Some(Expression::new(false, self.allow_yield, self.allow_await).parse(cursor)?),
89        };
90
91        match cursor.peek(0)? {
92            Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::In) && init.is_some() => {
93                let _ = cursor.next();
94                let expr =
95                    Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?;
96                cursor.expect(Punctuator::CloseParen, "for in statement")?;
97                let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
98                    .parse(cursor)?;
99                return Ok(ForInLoop::new(init.unwrap(), expr, body).into());
100            }
101            Some(tok) if tok.kind() == &TokenKind::Keyword(Keyword::Of) && init.is_some() => {
102                let _ = cursor.next();
103                let iterable =
104                    Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?;
105                cursor.expect(Punctuator::CloseParen, "for of statement")?;
106                let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)
107                    .parse(cursor)?;
108                return Ok(ForOfLoop::new(init.unwrap(), iterable, body).into());
109            }
110            _ => {}
111        }
112
113        cursor.expect(Punctuator::Semicolon, "for statement")?;
114
115        let cond = if cursor.next_if(Punctuator::Semicolon)?.is_some() {
116            Const::from(true).into()
117        } else {
118            let step = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?;
119            cursor.expect(Punctuator::Semicolon, "for statement")?;
120            step
121        };
122
123        let step = if cursor.next_if(Punctuator::CloseParen)?.is_some() {
124            None
125        } else {
126            let step = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?;
127            cursor.expect(
128                TokenKind::Punctuator(Punctuator::CloseParen),
129                "for statement",
130            )?;
131            Some(step)
132        };
133
134        let body =
135            Statement::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?;
136
137        // TODO: do not encapsulate the `for` in a block just to have an inner scope.
138        Ok(ForLoop::new(init, cond, step, body).into())
139    }
140}