boa/syntax/parser/statement/declaration/
lexical.rs1use crate::{
11 syntax::{
12 ast::{
13 node::{
14 declaration::{Declaration, DeclarationList},
15 Node,
16 },
17 Keyword, Punctuator,
18 },
19 lexer::TokenKind,
20 parser::{
21 cursor::{Cursor, SemicolonResult},
22 expression::Initializer,
23 statement::{ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern},
24 AllowAwait, AllowIn, AllowYield, ParseError, ParseResult, TokenParser,
25 },
26 },
27 BoaProfiler,
28};
29
30use std::io::Read;
31
32#[derive(Debug, Clone, Copy)]
39pub(super) struct LexicalDeclaration {
40 allow_in: AllowIn,
41 allow_yield: AllowYield,
42 allow_await: AllowAwait,
43 const_init_required: bool,
44}
45
46impl LexicalDeclaration {
47 pub(super) fn new<I, Y, A>(
49 allow_in: I,
50 allow_yield: Y,
51 allow_await: A,
52 const_init_required: bool,
53 ) -> Self
54 where
55 I: Into<AllowIn>,
56 Y: Into<AllowYield>,
57 A: Into<AllowAwait>,
58 {
59 Self {
60 allow_in: allow_in.into(),
61 allow_yield: allow_yield.into(),
62 allow_await: allow_await.into(),
63 const_init_required,
64 }
65 }
66}
67
68impl<R> TokenParser<R> for LexicalDeclaration
69where
70 R: Read,
71{
72 type Output = Node;
73
74 fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
75 let _timer = BoaProfiler::global().start_event("LexicalDeclaration", "Parsing");
76 let tok = cursor.next()?.ok_or(ParseError::AbruptEnd)?;
77
78 match tok.kind() {
79 TokenKind::Keyword(Keyword::Const) => BindingList::new(
80 self.allow_in,
81 self.allow_yield,
82 self.allow_await,
83 true,
84 self.const_init_required,
85 )
86 .parse(cursor),
87 TokenKind::Keyword(Keyword::Let) => BindingList::new(
88 self.allow_in,
89 self.allow_yield,
90 self.allow_await,
91 false,
92 self.const_init_required,
93 )
94 .parse(cursor),
95 _ => unreachable!("unknown token found: {:?}", tok),
96 }
97 }
98}
99
100#[derive(Debug, Clone, Copy)]
110struct BindingList {
111 allow_in: AllowIn,
112 allow_yield: AllowYield,
113 allow_await: AllowAwait,
114 is_const: bool,
115 const_init_required: bool,
116}
117
118impl BindingList {
119 fn new<I, Y, A>(
121 allow_in: I,
122 allow_yield: Y,
123 allow_await: A,
124 is_const: bool,
125 const_init_required: bool,
126 ) -> Self
127 where
128 I: Into<AllowIn>,
129 Y: Into<AllowYield>,
130 A: Into<AllowAwait>,
131 {
132 Self {
133 allow_in: allow_in.into(),
134 allow_yield: allow_yield.into(),
135 allow_await: allow_await.into(),
136 is_const,
137 const_init_required,
138 }
139 }
140}
141
142impl<R> TokenParser<R> for BindingList
143where
144 R: Read,
145{
146 type Output = Node;
147
148 fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
149 let _timer = BoaProfiler::global().start_event("BindingList", "Parsing");
150
151 let mut let_decls = Vec::new();
154 let mut const_decls = Vec::new();
155
156 loop {
157 let decl = LexicalBinding::new(self.allow_in, self.allow_yield, self.allow_await)
158 .parse(cursor)?;
159
160 if self.is_const {
161 if self.const_init_required {
162 let init_is_some = match &decl {
163 Declaration::Identifier { init, .. } if init.is_some() => true,
164 Declaration::Pattern(p) if p.init().is_some() => true,
165 _ => false,
166 };
167
168 if init_is_some {
169 const_decls.push(decl);
170 } else {
171 return Err(ParseError::expected(
172 vec![TokenKind::Punctuator(Punctuator::Assign)],
173 cursor.next()?.ok_or(ParseError::AbruptEnd)?,
174 "const declaration",
175 ));
176 }
177 } else {
178 const_decls.push(decl)
179 }
180 } else {
181 let_decls.push(decl);
182 }
183
184 match cursor.peek_semicolon()? {
185 SemicolonResult::Found(_) => break,
186 SemicolonResult::NotFound(tk)
187 if tk.kind() == &TokenKind::Keyword(Keyword::Of)
188 || tk.kind() == &TokenKind::Keyword(Keyword::In) =>
189 {
190 break
191 }
192 SemicolonResult::NotFound(tk)
193 if tk.kind() == &TokenKind::Punctuator(Punctuator::Comma) =>
194 {
195 let _ = cursor.next()?;
197 }
198 _ => {
199 return Err(ParseError::expected(
200 vec![
201 TokenKind::Punctuator(Punctuator::Semicolon),
202 TokenKind::LineTerminator,
203 ],
204 cursor.next()?.ok_or(ParseError::AbruptEnd)?,
205 "lexical declaration binding list",
206 ))
207 }
208 }
209 }
210
211 if self.is_const {
212 Ok(DeclarationList::Const(const_decls.into()).into())
213 } else {
214 Ok(DeclarationList::Let(let_decls.into()).into())
215 }
216 }
217}
218
219struct LexicalBinding {
226 allow_in: AllowIn,
227 allow_yield: AllowYield,
228 allow_await: AllowAwait,
229}
230
231impl LexicalBinding {
232 fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
234 where
235 I: Into<AllowIn>,
236 Y: Into<AllowYield>,
237 A: Into<AllowAwait>,
238 {
239 Self {
240 allow_in: allow_in.into(),
241 allow_yield: allow_yield.into(),
242 allow_await: allow_await.into(),
243 }
244 }
245}
246
247impl<R> TokenParser<R> for LexicalBinding
248where
249 R: Read,
250{
251 type Output = Declaration;
252
253 fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
254 let _timer = BoaProfiler::global().start_event("LexicalBinding", "Parsing");
255
256 let peek_token = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?;
257
258 match peek_token.kind() {
259 TokenKind::Punctuator(Punctuator::OpenBlock) => {
260 let bindings =
261 ObjectBindingPattern::new(self.allow_in, self.allow_yield, self.allow_await)
262 .parse(cursor)?;
263
264 let init = if let Some(t) = cursor.peek(0)? {
265 if *t.kind() == TokenKind::Punctuator(Punctuator::Assign) {
266 Some(
267 Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
268 .parse(cursor)?,
269 )
270 } else {
271 None
272 }
273 } else {
274 None
275 };
276
277 Ok(Declaration::new_with_object_pattern(bindings, init))
278 }
279 TokenKind::Punctuator(Punctuator::OpenBracket) => {
280 let bindings =
281 ArrayBindingPattern::new(self.allow_in, self.allow_yield, self.allow_await)
282 .parse(cursor)?;
283
284 let init = if let Some(t) = cursor.peek(0)? {
285 if *t.kind() == TokenKind::Punctuator(Punctuator::Assign) {
286 Some(
287 Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
288 .parse(cursor)?,
289 )
290 } else {
291 None
292 }
293 } else {
294 None
295 };
296
297 Ok(Declaration::new_with_array_pattern(bindings, init))
298 }
299
300 _ => {
301 let ident =
302 BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;
303
304 let init = if let Some(t) = cursor.peek(0)? {
305 if *t.kind() == TokenKind::Punctuator(Punctuator::Assign) {
306 Some(
307 Initializer::new(true, self.allow_yield, self.allow_await)
308 .parse(cursor)?,
309 )
310 } else {
311 None
312 }
313 } else {
314 None
315 };
316
317 Ok(Declaration::new_with_identifier(ident, init))
318 }
319 }
320 }
321}