boa_parser/parser/statement/block/
mod.rs

1//! Block 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/block
8//! [spec]: https://tc39.es/ecma262/#sec-block
9
10#[cfg(test)]
11mod tests;
12
13use crate::{
14    Error,
15    lexer::TokenKind,
16    parser::{
17        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,
18        statement::StatementList,
19    },
20    source::ReadChar,
21};
22use boa_ast::{
23    Punctuator, Spanned,
24    operations::{lexically_declared_names_legacy, var_declared_names},
25    statement,
26};
27use boa_interner::Interner;
28use rustc_hash::FxHashMap;
29
30/// The possible `TokenKind` which indicate the end of a block statement.
31const BLOCK_BREAK_TOKENS: [TokenKind; 1] = [TokenKind::Punctuator(Punctuator::CloseBlock)];
32
33/// A `BlockStatement` is equivalent to a `Block`.
34///
35/// More information:
36///  - [ECMAScript specification][spec]
37///
38/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement
39pub(super) type BlockStatement = Block;
40
41/// Variable declaration list parsing.
42///
43/// More information:
44///  - [MDN documentation][mdn]
45///  - [ECMAScript specification][spec]
46///
47/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
48/// [spec]: https://tc39.es/ecma262/#prod-Block
49#[derive(Debug, Clone, Copy)]
50pub(super) struct Block {
51    allow_yield: AllowYield,
52    allow_await: AllowAwait,
53    allow_return: AllowReturn,
54}
55
56impl Block {
57    /// Creates a new `Block` parser.
58    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self
59    where
60        Y: Into<AllowYield>,
61        A: Into<AllowAwait>,
62        R: Into<AllowReturn>,
63    {
64        Self {
65            allow_yield: allow_yield.into(),
66            allow_await: allow_await.into(),
67            allow_return: allow_return.into(),
68        }
69    }
70}
71
72impl<R> TokenParser<R> for Block
73where
74    R: ReadChar,
75{
76    type Output = statement::Block;
77
78    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
79        cursor.expect(Punctuator::OpenBlock, "block", interner)?;
80        if let Some(tk) = cursor.peek(0, interner)?
81            && tk.kind() == &TokenKind::Punctuator(Punctuator::CloseBlock)
82        {
83            cursor.advance(interner);
84            return Ok(statement::Block::from((vec![], cursor.linear_pos())));
85        }
86        let position = cursor.peek(0, interner).or_abrupt()?.span().start();
87        let (statement_list, _end) = StatementList::new(
88            self.allow_yield,
89            self.allow_await,
90            self.allow_return,
91            &BLOCK_BREAK_TOKENS,
92            false,
93            false,
94        )
95        .parse(cursor, interner)
96        .map(|(statement_list, end)| (statement::Block::from(statement_list), end))?;
97        cursor.expect(Punctuator::CloseBlock, "block", interner)?;
98
99        // It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate
100        // entries, unless the source text matched by this production is not strict mode code and the
101        // duplicate entries are only bound by FunctionDeclarations.
102        let mut lexical_names = FxHashMap::default();
103        for (name, is_fn) in lexically_declared_names_legacy(&statement_list) {
104            if let Some(is_fn_previous) = lexical_names.insert(name, is_fn) {
105                match (cursor.strict(), is_fn, is_fn_previous) {
106                    (false, true, true) => {}
107                    _ => {
108                        return Err(Error::general(
109                            "lexical name declared multiple times",
110                            position,
111                        ));
112                    }
113                }
114            }
115        }
116
117        // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList also
118        // occurs in the VarDeclaredNames of StatementList.
119        for name in var_declared_names(&statement_list) {
120            if lexical_names.contains_key(&name) {
121                return Err(Error::general(
122                    "lexical name declared in var names",
123                    position,
124                ));
125            }
126        }
127
128        Ok(statement_list)
129    }
130}