boa/syntax/parser/expression/
mod.rs

1//! Expression 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/Operators
8//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-expressions
9
10mod assignment;
11mod left_hand_side;
12mod primary;
13#[cfg(test)]
14mod tests;
15mod unary;
16mod update;
17
18pub(in crate::syntax::parser) mod await_expr;
19
20use self::assignment::ExponentiationExpression;
21pub(super) use self::{assignment::AssignmentExpression, primary::Initializer};
22use super::{AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser};
23
24use crate::{
25    profiler::BoaProfiler,
26    syntax::{
27        ast::op::LogOp,
28        ast::{
29            node::{BinOp, Node},
30            Keyword, Punctuator,
31        },
32        lexer::{InputElement, TokenKind},
33        parser::ParseError,
34    },
35};
36use std::io::Read;
37
38// For use in the expression! macro to allow for both Punctuator and Keyword parameters.
39// Always returns false.
40impl PartialEq<Keyword> for Punctuator {
41    fn eq(&self, _other: &Keyword) -> bool {
42        false
43    }
44}
45
46// For use in the expression! macro to allow for both Punctuator and Keyword parameters.
47// Always returns false.
48impl PartialEq<Punctuator> for Keyword {
49    fn eq(&self, _other: &Punctuator) -> bool {
50        false
51    }
52}
53
54/// Generates an expression parser for a number of expressions whose production rules are of the following pattern.
55/// <TargetExpression>[allowed_identifiers]
56///     => <InnerExpression>[?allowed_identifiers]
57///     => <TargetExpression>[?allowed_identifiers] <op1> <InnerExpression>[?allowed_identifiers]
58///     => <TargetExpression>[?allowed_identifiers] <op2> <InnerExpression>[?allowed_identifiers]
59///     ...
60///
61/// This macro has 2 mandatory identifiers:
62///  - The `$name` identifier is the name of the TargetExpression struct that the parser will be implemented for.
63///  - The `$lower` identifier is the name of the InnerExpression struct according to the pattern above.
64///
65/// A list of punctuators (operands between the <TargetExpression> and <InnerExpression>) are passed as the third parameter.
66///
67/// The fifth parameter is an Option<InputElement> which sets the goal symbol to set before parsing (or None to leave it as is).
68macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $low_param:ident ),*], $goal:expr ) => {
69    impl<R> TokenParser<R> for $name
70    where
71        R: Read
72    {
73        type Output = Node;
74
75        fn parse(self, cursor: &mut Cursor<R>)-> ParseResult {
76            let _timer = BoaProfiler::global().start_event(stringify!($name), "Parsing");
77
78            if $goal.is_some() {
79                cursor.set_goal($goal.unwrap());
80            }
81
82            let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor)?;
83            while let Some(tok) = cursor.peek(0)? {
84                match *tok.kind() {
85                    TokenKind::Punctuator(op) if $( op == $op )||* => {
86                        let _ = cursor.next().expect("token disappeared");
87                        lhs = BinOp::new(
88                            op.as_binop().expect("Could not get binary operation."),
89                            lhs,
90                            $lower::new($( self.$low_param ),*).parse(cursor)?
91                        ).into();
92                    }
93                    TokenKind::Keyword(op) if $( op == $op )||* => {
94                        let _ = cursor.next().expect("token disappeared");
95                        lhs = BinOp::new(
96                            op.as_binop().expect("Could not get binary operation."),
97                            lhs,
98                            $lower::new($( self.$low_param ),*).parse(cursor)?
99                        ).into();
100                    }
101                    _ => break
102                }
103            }
104
105            Ok(lhs)
106        }
107    }
108} }
109
110/// Expression parsing.
111///
112/// More information:
113///  - [MDN documentation][mdn]
114///  - [ECMAScript specification][spec]
115///
116/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators
117/// [spec]: https://tc39.es/ecma262/#prod-Expression
118#[derive(Debug, Clone, Copy)]
119pub(super) struct Expression {
120    allow_in: AllowIn,
121    allow_yield: AllowYield,
122    allow_await: AllowAwait,
123}
124
125impl Expression {
126    /// Creates a new `Expression` parser.
127    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
128    where
129        I: Into<AllowIn>,
130        Y: Into<AllowYield>,
131        A: Into<AllowAwait>,
132    {
133        Self {
134            allow_in: allow_in.into(),
135            allow_yield: allow_yield.into(),
136            allow_await: allow_await.into(),
137        }
138    }
139}
140
141expression!(
142    Expression,
143    AssignmentExpression,
144    [Punctuator::Comma],
145    [allow_in, allow_yield, allow_await],
146    None::<InputElement>
147);
148
149/// Parses a logical expression expression.
150///
151/// More information:
152///  - [MDN documentation][mdn]
153///  - [ECMAScript specification][spec]
154///
155/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators
156/// [spec]: https://tc39.es/ecma262/#prod-ShortCircuitExpression
157#[derive(Debug, Clone, Copy)]
158struct ShortCircuitExpression {
159    allow_in: AllowIn,
160    allow_yield: AllowYield,
161    allow_await: AllowAwait,
162    previous: PreviousExpr,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq)]
166enum PreviousExpr {
167    None,
168    Logical,
169    Coalesce,
170}
171
172impl ShortCircuitExpression {
173    /// Creates a new `ShortCircuitExpression` parser.
174    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
175    where
176        I: Into<AllowIn>,
177        Y: Into<AllowYield>,
178        A: Into<AllowAwait>,
179    {
180        Self {
181            allow_in: allow_in.into(),
182            allow_yield: allow_yield.into(),
183            allow_await: allow_await.into(),
184            previous: PreviousExpr::None,
185        }
186    }
187
188    fn with_previous<I, Y, A>(
189        allow_in: I,
190        allow_yield: Y,
191        allow_await: A,
192        previous: PreviousExpr,
193    ) -> Self
194    where
195        I: Into<AllowIn>,
196        Y: Into<AllowYield>,
197        A: Into<AllowAwait>,
198    {
199        Self {
200            allow_in: allow_in.into(),
201            allow_yield: allow_yield.into(),
202            allow_await: allow_await.into(),
203            previous,
204        }
205    }
206}
207
208impl<R> TokenParser<R> for ShortCircuitExpression
209where
210    R: Read,
211{
212    type Output = Node;
213
214    fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
215        let _timer = BoaProfiler::global().start_event("ShortCircuitExpression", "Parsing");
216
217        let mut current_node =
218            BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)
219                .parse(cursor)?;
220        let mut previous = self.previous;
221
222        while let Some(tok) = cursor.peek(0)? {
223            match tok.kind() {
224                TokenKind::Punctuator(Punctuator::BoolAnd) => {
225                    if previous == PreviousExpr::Coalesce {
226                        return Err(ParseError::expected(
227                            [TokenKind::Punctuator(Punctuator::Coalesce)],
228                            tok.clone(),
229                            "logical expression (cannot use '??' without parentheses within '||' or '&&')",
230                        ));
231                    }
232                    let _ = cursor.next()?.expect("'&&' expected");
233                    previous = PreviousExpr::Logical;
234                    let rhs =
235                        BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)
236                            .parse(cursor)?;
237
238                    current_node = BinOp::new(LogOp::And, current_node, rhs).into();
239                }
240                TokenKind::Punctuator(Punctuator::BoolOr) => {
241                    if previous == PreviousExpr::Coalesce {
242                        return Err(ParseError::expected(
243                            [TokenKind::Punctuator(Punctuator::Coalesce)],
244                            tok.clone(),
245                            "logical expression (cannot use '??' without parentheses within '||' or '&&')",
246                        ));
247                    }
248                    let _ = cursor.next()?.expect("'||' expected");
249                    previous = PreviousExpr::Logical;
250                    let rhs = ShortCircuitExpression::with_previous(
251                        self.allow_in,
252                        self.allow_yield,
253                        self.allow_await,
254                        PreviousExpr::Logical,
255                    )
256                    .parse(cursor)?;
257                    current_node = BinOp::new(LogOp::Or, current_node, rhs).into();
258                }
259                TokenKind::Punctuator(Punctuator::Coalesce) => {
260                    if previous == PreviousExpr::Logical {
261                        return Err(ParseError::expected(
262                            [
263                                TokenKind::Punctuator(Punctuator::BoolAnd),
264                                TokenKind::Punctuator(Punctuator::BoolOr),
265                            ],
266                            tok.clone(),
267                            "cannot use '??' unparenthesized within '||' or '&&'",
268                        ));
269                    }
270                    let _ = cursor.next()?.expect("'??' expected");
271                    previous = PreviousExpr::Coalesce;
272                    let rhs =
273                        BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)
274                            .parse(cursor)?;
275                    current_node = BinOp::new(LogOp::Coalesce, current_node, rhs).into();
276                }
277                _ => break,
278            }
279        }
280        Ok(current_node)
281    }
282}
283
284/// Parses a bitwise `OR` expression.
285///
286/// More information:
287///  - [MDN documentation][mdn]
288///  - [ECMAScript specification][spec]
289///
290/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR
291/// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression
292#[derive(Debug, Clone, Copy)]
293struct BitwiseORExpression {
294    allow_in: AllowIn,
295    allow_yield: AllowYield,
296    allow_await: AllowAwait,
297}
298
299impl BitwiseORExpression {
300    /// Creates a new `BitwiseORExpression` parser.
301    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
302    where
303        I: Into<AllowIn>,
304        Y: Into<AllowYield>,
305        A: Into<AllowAwait>,
306    {
307        Self {
308            allow_in: allow_in.into(),
309            allow_yield: allow_yield.into(),
310            allow_await: allow_await.into(),
311        }
312    }
313}
314
315expression!(
316    BitwiseORExpression,
317    BitwiseXORExpression,
318    [Punctuator::Or],
319    [allow_in, allow_yield, allow_await],
320    None::<InputElement>
321);
322
323/// Parses a bitwise `XOR` expression.
324///
325/// More information:
326///  - [MDN documentation][mdn]
327///  - [ECMAScript specification][spec]
328///
329/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR
330/// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression
331#[derive(Debug, Clone, Copy)]
332struct BitwiseXORExpression {
333    allow_in: AllowIn,
334    allow_yield: AllowYield,
335    allow_await: AllowAwait,
336}
337
338impl BitwiseXORExpression {
339    /// Creates a new `BitwiseXORExpression` parser.
340    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
341    where
342        I: Into<AllowIn>,
343        Y: Into<AllowYield>,
344        A: Into<AllowAwait>,
345    {
346        Self {
347            allow_in: allow_in.into(),
348            allow_yield: allow_yield.into(),
349            allow_await: allow_await.into(),
350        }
351    }
352}
353
354expression!(
355    BitwiseXORExpression,
356    BitwiseANDExpression,
357    [Punctuator::Xor],
358    [allow_in, allow_yield, allow_await],
359    None::<InputElement>
360);
361
362/// Parses a bitwise `AND` expression.
363///
364/// More information:
365///  - [MDN documentation][mdn]
366///  - [ECMAScript specification][spec]
367///
368/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND
369/// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression
370#[derive(Debug, Clone, Copy)]
371struct BitwiseANDExpression {
372    allow_in: AllowIn,
373    allow_yield: AllowYield,
374    allow_await: AllowAwait,
375}
376
377impl BitwiseANDExpression {
378    /// Creates a new `BitwiseANDExpression` parser.
379    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
380    where
381        I: Into<AllowIn>,
382        Y: Into<AllowYield>,
383        A: Into<AllowAwait>,
384    {
385        Self {
386            allow_in: allow_in.into(),
387            allow_yield: allow_yield.into(),
388            allow_await: allow_await.into(),
389        }
390    }
391}
392
393expression!(
394    BitwiseANDExpression,
395    EqualityExpression,
396    [Punctuator::And],
397    [allow_in, allow_yield, allow_await],
398    None::<InputElement>
399);
400
401/// Parses an equality expression.
402///
403/// More information:
404///  - [MDN documentation][mdn]
405///  - [ECMAScript specification][spec]
406///
407/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality_operators
408/// [spec]: https://tc39.es/ecma262/#sec-equality-operators
409#[derive(Debug, Clone, Copy)]
410struct EqualityExpression {
411    allow_in: AllowIn,
412    allow_yield: AllowYield,
413    allow_await: AllowAwait,
414}
415
416impl EqualityExpression {
417    /// Creates a new `EqualityExpression` parser.
418    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
419    where
420        I: Into<AllowIn>,
421        Y: Into<AllowYield>,
422        A: Into<AllowAwait>,
423    {
424        Self {
425            allow_in: allow_in.into(),
426            allow_yield: allow_yield.into(),
427            allow_await: allow_await.into(),
428        }
429    }
430}
431
432expression!(
433    EqualityExpression,
434    RelationalExpression,
435    [
436        Punctuator::Eq,
437        Punctuator::NotEq,
438        Punctuator::StrictEq,
439        Punctuator::StrictNotEq
440    ],
441    [allow_in, allow_yield, allow_await],
442    None::<InputElement>
443);
444
445/// Parses a relational expression.
446///
447/// More information:
448///  - [MDN documentation][mdn]
449///  - [ECMAScript specification][spec]
450///
451/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Relational_operators
452/// [spec]: https://tc39.es/ecma262/#sec-relational-operators
453#[derive(Debug, Clone, Copy)]
454struct RelationalExpression {
455    allow_in: AllowIn,
456    allow_yield: AllowYield,
457    allow_await: AllowAwait,
458}
459
460impl RelationalExpression {
461    /// Creates a new `RelationalExpression` parser.
462    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
463    where
464        I: Into<AllowIn>,
465        Y: Into<AllowYield>,
466        A: Into<AllowAwait>,
467    {
468        Self {
469            allow_in: allow_in.into(),
470            allow_yield: allow_yield.into(),
471            allow_await: allow_await.into(),
472        }
473    }
474}
475
476impl<R> TokenParser<R> for RelationalExpression
477where
478    R: Read,
479{
480    type Output = Node;
481
482    fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
483        let _timer = BoaProfiler::global().start_event("Relation Expression", "Parsing");
484
485        if None::<InputElement>.is_some() {
486            cursor.set_goal(None::<InputElement>.unwrap());
487        }
488
489        let mut lhs = ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor)?;
490        while let Some(tok) = cursor.peek(0)? {
491            match *tok.kind() {
492                TokenKind::Punctuator(op)
493                    if op == Punctuator::LessThan
494                        || op == Punctuator::GreaterThan
495                        || op == Punctuator::LessThanOrEq
496                        || op == Punctuator::GreaterThanOrEq =>
497                {
498                    let _ = cursor.next().expect("token disappeared");
499                    lhs = BinOp::new(
500                        op.as_binop().expect("Could not get binary operation."),
501                        lhs,
502                        ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor)?,
503                    )
504                    .into();
505                }
506                TokenKind::Keyword(op)
507                    if op == Keyword::InstanceOf
508                        || (op == Keyword::In && self.allow_in == AllowIn(true)) =>
509                {
510                    let _ = cursor.next().expect("token disappeared");
511                    lhs = BinOp::new(
512                        op.as_binop().expect("Could not get binary operation."),
513                        lhs,
514                        ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor)?,
515                    )
516                    .into();
517                }
518                _ => break,
519            }
520        }
521
522        Ok(lhs)
523    }
524}
525
526/// Parses a bitwise shift expression.
527///
528/// More information:
529///  - [MDN documentation][mdn]
530///  - [ECMAScript specification][spec]
531///
532/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_shift_operators
533/// [spec]: https://tc39.es/ecma262/#sec-bitwise-shift-operators
534#[derive(Debug, Clone, Copy)]
535struct ShiftExpression {
536    allow_yield: AllowYield,
537    allow_await: AllowAwait,
538}
539
540impl ShiftExpression {
541    /// Creates a new `ShiftExpression` parser.
542    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
543    where
544        Y: Into<AllowYield>,
545        A: Into<AllowAwait>,
546    {
547        Self {
548            allow_yield: allow_yield.into(),
549            allow_await: allow_await.into(),
550        }
551    }
552}
553
554expression!(
555    ShiftExpression,
556    AdditiveExpression,
557    [
558        Punctuator::LeftSh,
559        Punctuator::RightSh,
560        Punctuator::URightSh
561    ],
562    [allow_yield, allow_await],
563    None::<InputElement>
564);
565
566/// Parses an additive expression.
567///
568/// This can be either an addition or a subtraction.
569///
570/// More information:
571///  - [MDN documentation][mdn]
572///  - [ECMAScript specification][spec]
573///
574/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators
575/// [spec]: https://tc39.es/ecma262/#sec-additive-operators
576#[derive(Debug, Clone, Copy)]
577struct AdditiveExpression {
578    allow_yield: AllowYield,
579    allow_await: AllowAwait,
580}
581
582impl AdditiveExpression {
583    /// Creates a new `AdditiveExpression` parser.
584    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
585    where
586        Y: Into<AllowYield>,
587        A: Into<AllowAwait>,
588    {
589        Self {
590            allow_yield: allow_yield.into(),
591            allow_await: allow_await.into(),
592        }
593    }
594}
595
596expression!(
597    AdditiveExpression,
598    MultiplicativeExpression,
599    [Punctuator::Add, Punctuator::Sub],
600    [allow_yield, allow_await],
601    None::<InputElement>
602);
603
604/// Parses a multiplicative expression.
605///
606/// This can be either a multiplication, division or a modulo (remainder) expression.
607///
608/// More information:
609///  - [MDN documentation][mdn]
610///  - [ECMAScript specification][spec]
611///
612/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division
613/// [spec]: https://tc39.es/ecma262/#sec-multiplicative-operators
614#[derive(Debug, Clone, Copy)]
615struct MultiplicativeExpression {
616    allow_yield: AllowYield,
617    allow_await: AllowAwait,
618}
619
620impl MultiplicativeExpression {
621    /// Creates a new `MultiplicativeExpression` parser.
622    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
623    where
624        Y: Into<AllowYield>,
625        A: Into<AllowAwait>,
626    {
627        Self {
628            allow_yield: allow_yield.into(),
629            allow_await: allow_await.into(),
630        }
631    }
632}
633
634expression!(
635    MultiplicativeExpression,
636    ExponentiationExpression,
637    [Punctuator::Mul, Punctuator::Div, Punctuator::Mod],
638    [allow_yield, allow_await],
639    Some(InputElement::Div)
640);