swc_ecma_parser/parser/
expr.rs

1use either::Either;
2use swc_common::{BytePos, Span, Spanned};
3use swc_ecma_lexer::{
4    common::parser::{
5        class_and_fn::parse_fn_expr,
6        expr::{
7            parse_array_lit, parse_await_expr, parse_lit, parse_member_expr_or_new_expr,
8            parse_paren_expr_or_arrow_fn, parse_primary_expr_rest, parse_this_expr,
9            try_parse_async_start, try_parse_regexp,
10        },
11        object::parse_object_expr,
12        token_and_span::TokenAndSpan,
13        typescript::parse_ts_type_assertion,
14    },
15    error::SyntaxError,
16};
17
18use super::*;
19
20mod ops;
21#[cfg(test)]
22mod tests;
23
24use crate::parser::Parser;
25
26impl<I: Tokens> Parser<I> {
27    pub fn parse_expr(&mut self) -> PResult<Box<Expr>> {
28        ParserTrait::parse_expr(self)
29    }
30
31    #[allow(dead_code)]
32    fn parse_member_expr(&mut self) -> PResult<Box<Expr>> {
33        parse_member_expr_or_new_expr(self, false)
34    }
35
36    pub(super) fn parse_unary_expr(&mut self) -> PResult<Box<Expr>> {
37        trace_cur!(self, parse_unary_expr);
38
39        let token_and_span = self.input().get_cur();
40        let start = token_and_span.span().lo;
41        let cur = *token_and_span.token();
42
43        if cur == Token::Lt && self.input().syntax().typescript() && !self.input().syntax().jsx() {
44            self.bump(); // consume `<`
45            return if self.input_mut().eat(&Token::Const) {
46                self.expect(&Token::Gt)?;
47                let expr = self.parse_unary_expr()?;
48                Ok(TsConstAssertion {
49                    span: self.span(start),
50                    expr,
51                }
52                .into())
53            } else {
54                parse_ts_type_assertion(self, start)
55                    .map(Expr::from)
56                    .map(Box::new)
57            };
58        } else if cur == Token::Lt
59            && self.input().syntax().jsx()
60            && self.input_mut().peek().is_some_and(|peek| {
61                (*peek).is_word() || peek == &Token::Gt || peek.should_rescan_into_gt_in_jsx()
62            })
63        {
64            fn into_expr(e: Either<JSXFragment, JSXElement>) -> Box<Expr> {
65                match e {
66                    Either::Left(l) => l.into(),
67                    Either::Right(r) => r.into(),
68                }
69            }
70            return self.parse_jsx_element(true).map(into_expr);
71        } else if matches!(cur, Token::PlusPlus | Token::MinusMinus) {
72            // Parse update expression
73            let op = if cur == Token::PlusPlus {
74                op!("++")
75            } else {
76                op!("--")
77            };
78            self.bump();
79
80            let arg = self.parse_unary_expr()?;
81            let span = Span::new_with_checked(start, arg.span_hi());
82            self.check_assign_target(&arg, false);
83
84            return Ok(UpdateExpr {
85                span,
86                prefix: true,
87                op,
88                arg,
89            }
90            .into());
91        } else if cur == Token::Delete
92            || cur == Token::Void
93            || cur == Token::TypeOf
94            || cur == Token::Plus
95            || cur == Token::Minus
96            || cur == Token::Tilde
97            || cur == Token::Bang
98        {
99            // Parse unary expression
100            let op = if cur == Token::Delete {
101                op!("delete")
102            } else if cur == Token::Void {
103                op!("void")
104            } else if cur == Token::TypeOf {
105                op!("typeof")
106            } else if cur == Token::Plus {
107                op!(unary, "+")
108            } else if cur == Token::Minus {
109                op!(unary, "-")
110            } else if cur == Token::Tilde {
111                op!("~")
112            } else {
113                debug_assert!(cur == Token::Bang);
114                op!("!")
115            };
116            self.bump();
117            let arg_start = self.cur_pos() - BytePos(1);
118            let arg = match self.parse_unary_expr() {
119                Ok(expr) => expr,
120                Err(err) => {
121                    self.emit_error(err);
122                    Invalid {
123                        span: Span::new_with_checked(arg_start, arg_start),
124                    }
125                    .into()
126                }
127            };
128
129            if op == op!("delete") {
130                if let Expr::Ident(ref i) = *arg {
131                    self.emit_strict_mode_err(i.span, SyntaxError::TS1102)
132                }
133            }
134
135            return Ok(UnaryExpr {
136                span: Span::new_with_checked(start, arg.span_hi()),
137                op,
138                arg,
139            }
140            .into());
141        } else if cur == Token::Await {
142            return parse_await_expr(self, None);
143        }
144
145        // UpdateExpression
146        let expr = self.parse_lhs_expr()?;
147        if let Expr::Arrow { .. } = *expr {
148            return Ok(expr);
149        }
150
151        // Line terminator isn't allowed here.
152        if self.input_mut().had_line_break_before_cur() {
153            return Ok(expr);
154        }
155
156        let cur = self.input().cur();
157        if cur == &Token::PlusPlus || cur == &Token::MinusMinus {
158            let op = if cur == &Token::PlusPlus {
159                op!("++")
160            } else {
161                op!("--")
162            };
163
164            self.check_assign_target(&expr, false);
165            self.bump();
166
167            return Ok(UpdateExpr {
168                span: self.span(expr.span_lo()),
169                prefix: false,
170                op,
171                arg: expr,
172            }
173            .into());
174        }
175        Ok(expr)
176    }
177
178    pub(super) fn parse_primary_expr(&mut self) -> PResult<Box<Expr>> {
179        trace_cur!(self, parse_primary_expr);
180        let start = self.input().cur_pos();
181        let can_be_arrow = self
182            .state
183            .potential_arrow_start
184            .map(|s| s == start)
185            .unwrap_or(false);
186        let tok = self.input.cur();
187        match *tok {
188            Token::This => return parse_this_expr(self, start),
189            Token::Async => {
190                if let Some(res) = try_parse_async_start(self, can_be_arrow) {
191                    return res;
192                }
193            }
194            Token::LBracket => {
195                return self.do_outside_of_context(Context::WillExpectColonForCond, parse_array_lit)
196            }
197            Token::LBrace => {
198                return parse_object_expr(self).map(Box::new);
199            }
200            // Handle FunctionExpression and GeneratorExpression
201            Token::Function => {
202                return parse_fn_expr(self);
203            }
204            // Literals
205            Token::Null | Token::True | Token::False | Token::Num | Token::BigInt | Token::Str => {
206                return parse_lit(self).map(|lit| lit.into());
207            }
208            // Regexp
209            Token::Slash | Token::DivEq => {
210                if let Some(res) = try_parse_regexp(self, start) {
211                    return Ok(res);
212                }
213            }
214            Token::LParen => return parse_paren_expr_or_arrow_fn(self, can_be_arrow, None),
215            Token::NoSubstitutionTemplateLiteral => {
216                return Ok(self.parse_no_substitution_template_literal(false)?.into())
217            }
218            Token::TemplateHead => {
219                // parse template literal
220                return Ok(self
221                    .do_outside_of_context(Context::WillExpectColonForCond, |p| p.parse_tpl(false))?
222                    .into());
223            }
224            _ => {}
225        }
226
227        parse_primary_expr_rest(self, start, can_be_arrow)
228    }
229}