rslint_parser/ast/
expr_ext.rs

1//! Extensions for things which are not easily generated in ast expr nodes
2
3use crate::{ast::*, numbers::*, util::*, SyntaxText, TextRange, TextSize, TokenSet, T};
4use SyntaxKind::*;
5
6impl BracketExpr {
7    pub fn object(&self) -> Option<Expr> {
8        support::children(self.syntax()).next()
9    }
10
11    pub fn prop(&self) -> Option<Expr> {
12        support::children(self.syntax()).nth(1)
13    }
14}
15
16impl CondExpr {
17    pub fn test(&self) -> Option<Expr> {
18        support::children(self.syntax()).next()
19    }
20
21    pub fn cons(&self) -> Option<Expr> {
22        support::children(self.syntax()).nth(1)
23    }
24
25    pub fn alt(&self) -> Option<Expr> {
26        support::children(self.syntax()).nth(2)
27    }
28}
29
30#[derive(Debug, Clone, PartialEq, Eq, Hash)]
31pub enum PropName {
32    Computed(ComputedPropertyName),
33    Literal(Literal),
34    Ident(Name),
35}
36
37impl AstNode for PropName {
38    fn can_cast(kind: SyntaxKind) -> bool {
39        matches!(kind, NAME | LITERAL | COMPUTED_PROPERTY_NAME)
40    }
41
42    fn cast(syntax: SyntaxNode) -> Option<Self> {
43        if !Self::can_cast(syntax.kind()) {
44            None
45        } else {
46            Some(match syntax.kind() {
47                LITERAL => PropName::Literal(Literal::cast(syntax).unwrap()),
48                NAME => PropName::Ident(Name::cast(syntax).unwrap()),
49                COMPUTED_PROPERTY_NAME => {
50                    PropName::Computed(ComputedPropertyName::cast(syntax).unwrap())
51                }
52                _ => unreachable!(),
53            })
54        }
55    }
56
57    fn syntax(&self) -> &SyntaxNode {
58        match self {
59            PropName::Ident(s) => s.syntax(),
60            PropName::Literal(s) => s.syntax(),
61            PropName::Computed(s) => s.syntax(),
62        }
63    }
64}
65
66impl PropName {
67    pub fn as_string(&self) -> Option<std::string::String> {
68        Some(self.syntax().text().to_string())
69    }
70}
71
72impl LiteralProp {
73    pub fn key(&self) -> Option<PropName> {
74        if PropName::can_cast(
75            support::children::<PropName>(self.syntax())
76                .next()?
77                .syntax()
78                .kind(),
79        ) {
80            PropName::cast(
81                support::children::<PropName>(self.syntax())
82                    .next()
83                    .unwrap()
84                    .syntax()
85                    .to_owned(),
86            )
87        } else {
88            None
89        }
90    }
91
92    pub fn value(&self) -> Option<Expr> {
93        self.syntax().children().nth(1)?.try_to()
94    }
95}
96
97/// A binary operation applied to two expressions
98#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
99pub enum BinOp {
100    /// `<`
101    LessThan,
102    /// `>`
103    GreaterThan,
104    /// `<=`
105    LessThanOrEqual,
106    /// `>=`
107    GreaterThanOrEqual,
108    /// `==`
109    Equality,
110    /// `===`
111    StrictEquality,
112    /// `!=`
113    Inequality,
114    /// `!==`
115    StrictInequality,
116    /// `+`
117    Plus,
118    /// `-`
119    Minus,
120    /// `*`
121    Times,
122    /// `/`
123    Divide,
124    /// `%`
125    Remainder,
126    /// `**`
127    Exponent,
128    /// `<<`
129    LeftShift,
130    /// `>>`
131    RightShift,
132    /// `>>>`
133    UnsignedRightShift,
134    /// `&`
135    BitwiseAnd,
136    /// `|`
137    BitwiseOr,
138    /// `^`
139    BitwiseXor,
140    /// `??`
141    NullishCoalescing,
142    /// `||`
143    LogicalOr,
144    /// `&&`
145    LogicalAnd,
146    /// `in`
147    In,
148    /// `instanceof`
149    Instanceof,
150}
151
152impl BinExpr {
153    pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
154        self.syntax()
155            .children_with_tokens()
156            .filter_map(|x| x.into_token())
157            .find_map(|t| {
158                let op = match t.kind() {
159                    T![<] => BinOp::LessThan,
160                    T![>] => BinOp::GreaterThan,
161                    T![<=] => BinOp::LessThanOrEqual,
162                    T![>=] => BinOp::GreaterThanOrEqual,
163                    T![==] => BinOp::Equality,
164                    T![===] => BinOp::StrictEquality,
165                    T![!=] => BinOp::Inequality,
166                    T![!==] => BinOp::StrictInequality,
167                    T![+] => BinOp::Plus,
168                    T![-] => BinOp::Minus,
169                    T![*] => BinOp::Times,
170                    T![/] => BinOp::Divide,
171                    T![%] => BinOp::Remainder,
172                    T![**] => BinOp::Exponent,
173                    T![<<] => BinOp::LeftShift,
174                    T![>>] => BinOp::RightShift,
175                    T![>>>] => BinOp::UnsignedRightShift,
176                    T![&] => BinOp::BitwiseAnd,
177                    T![|] => BinOp::BitwiseOr,
178                    T![^] => BinOp::BitwiseXor,
179                    T![??] => BinOp::NullishCoalescing,
180                    T![||] => BinOp::LogicalOr,
181                    T![&&] => BinOp::LogicalAnd,
182                    T![in] => BinOp::In,
183                    T![instanceof] => BinOp::Instanceof,
184                    _ => return None,
185                };
186                Some((t, op))
187            })
188    }
189
190    pub fn op(&self) -> Option<BinOp> {
191        self.op_details().map(|t| t.1)
192    }
193
194    pub fn op_token(&self) -> Option<SyntaxToken> {
195        self.op_details().map(|t| t.0)
196    }
197
198    pub fn lhs(&self) -> Option<Expr> {
199        support::children(self.syntax()).next()
200    }
201
202    pub fn rhs(&self) -> Option<Expr> {
203        support::children(self.syntax()).nth(1)
204    }
205
206    /// Whether this binary expr is a `||` or `&&` expression.
207    pub fn conditional(&self) -> bool {
208        token_set![T![||], T![&&]].contains(self.op_token().map(|x| x.kind()).unwrap_or(T![&]))
209    }
210
211    /// Whether this is a comparison operation, such as `>`, `<`, `==`, `!=`, `===`, etc.
212    pub fn comparison(&self) -> bool {
213        const SET: TokenSet = token_set![
214            T![>],
215            T![<],
216            T![>=],
217            T![<=],
218            T![==],
219            T![===],
220            T![!=],
221            T![!==]
222        ];
223        SET.contains(self.op_token().map(|x| x.kind()).unwrap_or(T![&]))
224    }
225}
226
227#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
228pub enum UnaryOp {
229    /// `++`
230    Increment,
231    /// `--`
232    Decrement,
233    /// `delete`
234    Delete,
235    /// `void`
236    Void,
237    /// `typeof`
238    Typeof,
239    /// `+`
240    Plus,
241    /// `-`
242    Minus,
243    /// `~`
244    BitwiseNot,
245    /// `!`
246    LogicalNot,
247    /// `await`
248    Await,
249}
250
251impl UnaryExpr {
252    pub fn op_details(&self) -> Option<(SyntaxToken, UnaryOp)> {
253        self.syntax()
254            .children_with_tokens()
255            .filter_map(|x| x.into_token())
256            .find_map(|t| {
257                let op = match t.kind() {
258                    T![++] => UnaryOp::Increment,
259                    T![--] => UnaryOp::Decrement,
260                    T![delete] => UnaryOp::Delete,
261                    T![void] => UnaryOp::Void,
262                    T![typeof] => UnaryOp::Typeof,
263                    T![+] => UnaryOp::Plus,
264                    T![-] => UnaryOp::Minus,
265                    T![~] => UnaryOp::BitwiseNot,
266                    T![!] => UnaryOp::LogicalNot,
267                    T![await] => UnaryOp::Await,
268                    _ => return None,
269                };
270                Some((t, op))
271            })
272    }
273
274    pub fn op(&self) -> Option<UnaryOp> {
275        self.op_details().map(|t| t.1)
276    }
277
278    pub fn op_token(&self) -> Option<SyntaxToken> {
279        self.op_details().map(|t| t.0)
280    }
281
282    /// Whether this is an update expression.
283    pub fn is_update(&self) -> bool {
284        self.op().map_or(false, |op| {
285            op == UnaryOp::Increment || op == UnaryOp::Decrement
286        })
287    }
288
289    /// Whether this is an update expression and it is a prefix update expression
290    pub fn is_prefix(&self) -> Option<bool> {
291        if !self.is_update() {
292            return None;
293        }
294
295        Some(self.op_token()?.text_range().start() > self.expr()?.syntax().text_range().end())
296    }
297}
298
299impl KeyValuePattern {
300    pub fn value(&self) -> Option<Pattern> {
301        // This is to easily handle both `NAME NAME` and `: NAME`
302        if self.syntax().children().count() == 2 {
303            Pattern::cast(self.syntax().last_child().unwrap())
304        } else {
305            self.colon_token()?
306                .next_sibling_or_token()?
307                .into_node()?
308                .try_to::<Pattern>()
309        }
310    }
311}
312
313#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
314pub enum AssignOp {
315    Assign,
316    AddAssign,
317    SubtractAssign,
318    TimesAssign,
319    RemainderAssign,
320    ExponentAssign,
321    LeftShiftAssign,
322    RightShiftAssign,
323    UnsignedRightShiftAssign,
324    BitwiseAndAssign,
325    BitwiseOrAssign,
326    BitwiseXorAssign,
327    LogicalAndAssign,
328    LogicalOrAssign,
329    NullishCoalescingAssign,
330}
331
332impl AssignExpr {
333    pub fn op_details(&self) -> Option<(SyntaxToken, AssignOp)> {
334        self.syntax()
335            .children_with_tokens()
336            .filter_map(|x| x.into_token())
337            .find_map(|t| {
338                let op = match t.kind() {
339                    T![=] => AssignOp::Assign,
340                    T![+=] => AssignOp::AddAssign,
341                    T![-=] => AssignOp::SubtractAssign,
342                    T![*=] => AssignOp::TimesAssign,
343                    T![%=] => AssignOp::RemainderAssign,
344                    T![**=] => AssignOp::ExponentAssign,
345                    T![>>=] => AssignOp::LeftShiftAssign,
346                    T![<<=] => AssignOp::RightShiftAssign,
347                    T![>>>=] => AssignOp::UnsignedRightShiftAssign,
348                    T![&=] => AssignOp::BitwiseAndAssign,
349                    T![|=] => AssignOp::BitwiseOrAssign,
350                    T![^=] => AssignOp::BitwiseXorAssign,
351                    T![&&=] => AssignOp::LogicalAndAssign,
352                    T![||=] => AssignOp::LogicalOrAssign,
353                    T![??=] => AssignOp::NullishCoalescingAssign,
354                    _ => return None,
355                };
356                Some((t, op))
357            })
358    }
359
360    pub fn op(&self) -> Option<AssignOp> {
361        self.op_details().map(|t| t.1)
362    }
363
364    pub fn op_token(&self) -> Option<SyntaxToken> {
365        self.op_details().map(|t| t.0)
366    }
367
368    pub fn lhs(&self) -> Option<PatternOrExpr> {
369        self.syntax.children().next().and_then(|n| n.try_to())
370    }
371
372    pub fn rhs(&self) -> Option<Expr> {
373        self.syntax.children().nth(1).and_then(|n| n.try_to())
374    }
375}
376
377impl ArrayExpr {
378    pub fn has_trailing_comma(&self) -> bool {
379        if let Some(last) = self.elements().last().map(|it| it.syntax().to_owned()) {
380            if let Some(tok) = last
381                .next_sibling_or_token()
382                .map(|it| it.into_token())
383                .flatten()
384            {
385                return tok.kind() == T![,];
386            }
387        }
388        false
389    }
390
391    /// A list of all sparse elements as a vector of the comma tokens
392    pub fn sparse_elements(&self) -> Vec<SyntaxToken> {
393        let node = self.syntax();
394        let commas = node
395            .children_with_tokens()
396            .filter_map(|x| x.into_token().filter(|tok| tok.kind() == COMMA));
397        commas
398            .filter(|comma| {
399                let mut siblings = comma
400                    .siblings_with_tokens(crate::Direction::Prev)
401                    .skip(1)
402                    .skip_while(|item| {
403                        item.as_token()
404                            .filter(|tok| tok.kind().is_trivia())
405                            .is_some()
406                    });
407
408                siblings
409                    .next()
410                    .and_then(|x| x.into_node()?.try_to::<ExprOrSpread>())
411                    .is_none()
412            })
413            .collect()
414    }
415}
416
417#[derive(Debug, Clone, PartialEq, Eq, Hash)]
418pub enum ExprOrSpread {
419    Expr(Expr),
420    Spread(SpreadElement),
421}
422
423impl AstNode for ExprOrSpread {
424    fn can_cast(kind: SyntaxKind) -> bool {
425        match kind {
426            SPREAD_ELEMENT => true,
427            _ => Expr::can_cast(kind),
428        }
429    }
430
431    fn cast(syntax: SyntaxNode) -> Option<Self> {
432        if !Self::can_cast(syntax.kind()) {
433            None
434        } else {
435            Some(if syntax.kind() == SPREAD_ELEMENT {
436                ExprOrSpread::Spread(SpreadElement::cast(syntax).unwrap())
437            } else {
438                ExprOrSpread::Expr(Expr::cast(syntax).unwrap())
439            })
440        }
441    }
442
443    fn syntax(&self) -> &SyntaxNode {
444        match self {
445            ExprOrSpread::Expr(it) => it.syntax(),
446            ExprOrSpread::Spread(it) => it.syntax(),
447        }
448    }
449}
450
451impl ExprOrSpread {
452    pub fn is_spread(&self) -> bool {
453        matches!(self, ExprOrSpread::Spread(_))
454    }
455
456    pub fn is_expr(&self) -> bool {
457        matches!(self, ExprOrSpread::Expr(_))
458    }
459}
460
461impl ObjectExpr {
462    pub fn has_trailing_comma(&self) -> bool {
463        if let Some(last) = self.props().last().map(|it| it.syntax().to_owned()) {
464            if let Some(tok) = last
465                .next_sibling_or_token()
466                .map(|it| it.into_token())
467                .flatten()
468            {
469                return tok.kind() == T![,];
470            }
471        }
472        false
473    }
474}
475
476#[derive(Debug, Clone, PartialEq, PartialOrd)]
477pub enum LiteralKind {
478    Number(f64),
479    BigInt(BigInt),
480    String,
481    Null,
482    Bool(bool),
483    Regex,
484}
485
486impl Literal {
487    pub fn token(&self) -> SyntaxToken {
488        self.syntax()
489            .children_with_tokens()
490            .find(|e| !e.kind().is_trivia())
491            .and_then(|e| e.into_token())
492            .unwrap()
493    }
494
495    pub fn kind(&self) -> LiteralKind {
496        match self.token().kind() {
497            T![null] => LiteralKind::Null,
498            NUMBER => match parse_js_num(self.to_string()).unwrap() {
499                JsNum::BigInt(bigint) => LiteralKind::BigInt(bigint),
500                JsNum::Float(float) => LiteralKind::Number(float),
501            },
502            STRING => LiteralKind::String,
503            TRUE_KW => LiteralKind::Bool(true),
504            FALSE_KW => LiteralKind::Bool(false),
505            REGEX => LiteralKind::Regex,
506            _ => unreachable!(),
507        }
508    }
509
510    pub fn as_number(&self) -> Option<f64> {
511        if let LiteralKind::Number(num) = self.kind() {
512            Some(num)
513        } else {
514            None
515        }
516    }
517
518    pub fn is_number(&self) -> bool {
519        matches!(self.kind(), LiteralKind::Number(_))
520    }
521
522    pub fn is_string(&self) -> bool {
523        self.kind() == LiteralKind::String
524    }
525
526    pub fn is_null(&self) -> bool {
527        self.kind() == LiteralKind::Null
528    }
529
530    pub fn is_bool(&self) -> bool {
531        matches!(self.kind(), LiteralKind::Bool(_))
532    }
533
534    pub fn is_regex(&self) -> bool {
535        self.kind() == LiteralKind::Regex
536    }
537
538    /// Get the inner text of a string not including the quotes
539    pub fn inner_string_text(&self) -> Option<SyntaxText> {
540        if !self.is_string() {
541            return None;
542        }
543
544        let start = self.syntax().text_range().start() + TextSize::from(1);
545        let end_char = self
546            .syntax()
547            .text()
548            .char_at(self.syntax().text().len() - TextSize::from(1))
549            .unwrap();
550        let end = if end_char == '"' || end_char == '\'' {
551            self.syntax().text_range().end() - TextSize::from(1)
552        } else {
553            self.syntax().text_range().end()
554        };
555
556        let offset = self.syntax().text_range().start();
557
558        Some(
559            self.syntax()
560                .text()
561                .slice(TextRange::new(start - offset, end - offset)),
562        )
563    }
564}
565
566#[derive(Debug, Clone, PartialEq, Eq, Hash)]
567pub enum ExprOrBlock {
568    Expr(Expr),
569    Block(BlockStmt),
570}
571
572impl AstNode for ExprOrBlock {
573    fn can_cast(kind: SyntaxKind) -> bool {
574        if kind == BLOCK_STMT {
575            true
576        } else {
577            Expr::can_cast(kind)
578        }
579    }
580
581    fn cast(syntax: SyntaxNode) -> Option<Self> {
582        if syntax.kind() == BLOCK_STMT {
583            Some(ExprOrBlock::Block(BlockStmt::cast(syntax).unwrap()))
584        } else {
585            Some(ExprOrBlock::Expr(Expr::cast(syntax)?))
586        }
587    }
588
589    fn syntax(&self) -> &SyntaxNode {
590        match self {
591            ExprOrBlock::Expr(it) => it.syntax(),
592            ExprOrBlock::Block(it) => it.syntax(),
593        }
594    }
595}
596
597impl ArrowExpr {
598    pub fn body(&self) -> Option<ExprOrBlock> {
599        ExprOrBlock::cast(self.syntax().children().last()?)
600    }
601}
602
603#[derive(Debug, Clone, PartialEq, Eq, Hash)]
604pub enum PatternOrExpr {
605    Pattern(Pattern),
606    Expr(Expr),
607}
608
609impl AstNode for PatternOrExpr {
610    fn can_cast(kind: SyntaxKind) -> bool {
611        Expr::can_cast(kind) || Pattern::can_cast(kind)
612    }
613
614    fn cast(syntax: SyntaxNode) -> Option<Self> {
615        Some(if Pattern::can_cast(syntax.kind()) {
616            PatternOrExpr::Pattern(Pattern::cast(syntax).unwrap())
617        } else {
618            PatternOrExpr::Expr(Expr::cast(syntax).unwrap())
619        })
620    }
621
622    fn syntax(&self) -> &SyntaxNode {
623        match self {
624            PatternOrExpr::Pattern(it) => it.syntax(),
625            PatternOrExpr::Expr(it) => it.syntax(),
626        }
627    }
628}
629
630impl Template {
631    /// The string chunks of the template. aka:
632    /// `foo ${bar} foo` breaks down into:
633    /// `QUASIS ELEMENT{EXPR} QUASIS`
634    pub fn quasis(&self) -> impl Iterator<Item = SyntaxToken> {
635        self.syntax()
636            .children_with_tokens()
637            .filter_map(NodeOrToken::into_token)
638            .filter(|t| t.kind() == TEMPLATE_CHUNK)
639    }
640
641    pub fn template_range(&self) -> Option<TextRange> {
642        let start = self
643            .syntax()
644            .children_with_tokens()
645            .filter_map(|x| x.into_token())
646            .find(|tok| tok.kind() == BACKTICK)?;
647        Some(TextRange::new(
648            start.text_range().start(),
649            self.syntax().text_range().end(),
650        ))
651    }
652}
653
654impl ObjectProp {
655    pub fn key(&self) -> Option<std::string::String> {
656        Some(self.key_element()?.to_string())
657    }
658
659    pub fn key_element(&self) -> Option<SyntaxElement> {
660        Some(
661            match self {
662                ObjectProp::IdentProp(idt) => idt.syntax().clone(),
663                ObjectProp::LiteralProp(litprop) => prop_name_syntax(litprop.key()?)?,
664                ObjectProp::Getter(getter) => prop_name_syntax(getter.key()?)?,
665                ObjectProp::Setter(setter) => prop_name_syntax(setter.key()?)?,
666                ObjectProp::Method(method) => prop_name_syntax(method.name()?)?,
667                ObjectProp::InitializedProp(init) => init.key()?.syntax().clone(),
668                ObjectProp::SpreadProp(_) => return None,
669            }
670            .into(),
671        )
672    }
673}
674
675fn prop_name_syntax(name: PropName) -> Option<SyntaxNode> {
676    Some(match name {
677        PropName::Ident(idt) => idt.syntax().clone(),
678        PropName::Literal(lit) => lit.syntax().clone(),
679        PropName::Computed(_) => return None,
680    })
681}
682
683impl Expr {
684    /// Whether this is an optional chain expression.
685    pub fn opt_chain(&self) -> bool {
686        match self {
687            Expr::DotExpr(dotexpr) => dotexpr.opt_chain_token(),
688            Expr::CallExpr(callexpr) => callexpr.opt_chain_token(),
689            Expr::BracketExpr(bracketexpr) => bracketexpr.opt_chain_token(),
690            _ => return false,
691        }
692        .is_some()
693    }
694}
695
696impl DotExpr {
697    pub fn opt_chain_token(&self) -> Option<SyntaxToken> {
698        self.syntax()
699            .children_with_tokens()
700            .filter_map(|child| child.into_token())
701            .find(|tok| tok.kind() == QUESTIONDOT)
702    }
703}
704
705impl CallExpr {
706    pub fn opt_chain_token(&self) -> Option<SyntaxToken> {
707        self.syntax()
708            .children_with_tokens()
709            .filter_map(|child| child.into_token())
710            .find(|tok| tok.kind() == QUESTIONDOT)
711    }
712}
713
714impl BracketExpr {
715    pub fn opt_chain_token(&self) -> Option<SyntaxToken> {
716        self.syntax()
717            .children_with_tokens()
718            .filter_map(|child| child.into_token())
719            .find(|tok| tok.kind() == QUESTIONDOT)
720    }
721}
722
723/// A simple macro for making assign, binop, or unary operators
724#[macro_export]
725macro_rules! op {
726    (<) => {
727        $crate::ast::BinOp::LessThan
728    };
729    (>) => {
730        $crate::ast::BinOp::GreaterThan
731    };
732    (<=) => {
733        $crate::ast::BinOp::LessThanOrEqual
734    };
735    (>=) => {
736        $crate::ast::BinOp::GreaterThanOrEqual
737    };
738    (==) => {
739        $crate::ast::BinOp::Equality
740    };
741    (===) => {
742        $crate::ast::BinOp::StrictEquality
743    };
744    (!=) => {
745        $crate::ast::BinOp::Inequality
746    };
747    (!==) => {
748        $crate::ast::BinOp::StrictInequality
749    };
750    (+) => {
751        $crate::ast::BinOp::Plus
752    };
753    (-) => {
754        $crate::ast::BinOp::Minus
755    };
756    (*) => {
757        $crate::ast::BinOp::Times
758    };
759    (/) => {
760        $crate::ast::BinOp::Divide
761    };
762    (%) => {
763        $crate::ast::BinOp::Remainder
764    };
765    (**) => {
766        $crate::ast::BinOp::Exponent
767    };
768    (<<) => {
769        $crate::ast::BinOp::LeftShift
770    };
771    (>>) => {
772        $crate::ast::BinOp::RightShift
773    };
774    (>>>) => {
775        $crate::ast::BinOp::UnsignedRightShift
776    };
777    (&) => {
778        $crate::ast::BinOp::BitwiseAnd
779    };
780    (|) => {
781        $crate::ast::BinOp::BitwiseOr
782    };
783    (^) => {
784        $crate::ast::BinOp::BitwiseXor
785    };
786    (??) => {
787        $crate::ast::BinOp::NullishCoalescing
788    };
789    (||) => {
790        $crate::ast::BinOp::LogicalOr
791    };
792    (&&) => {
793        $crate::ast::BinOp::LogicalAnd
794    };
795    (in) => {
796        $crate::ast::BinOp::In
797    };
798    (instanceof) => {
799        $crate::ast::BinOp::Instanceof
800    };
801
802    (=) => {
803        $crate::ast::AssignOp::Assign
804    };
805    (+=) => {
806        $crate::ast::AssignOp::AddAssign
807    };
808    (-=) => {
809        $crate::ast::AssignOp::SubtractAssign
810    };
811    (*=) => {
812        $crate::ast::AssignOp::TimesAssign
813    };
814    (%=) => {
815        $crate::ast::AssignOp::RemainderAssign
816    };
817    (**=) => {
818        $crate::ast::AssignOp::ExponentAssign
819    };
820    (>>=) => {
821        $crate::ast::AssignOp::LeftShiftAssign
822    };
823    (<<=) => {
824        $crate::ast::AssignOp::RightShiftAssign
825    };
826    (>>>=) => {
827        $crate::ast::AssignOp::UnsignedRightShiftAssign
828    };
829    (&=) => {
830        $crate::ast::AssignOp::BitwiseAndAssign
831    };
832    (|=) => {
833        $crate::ast::AssignOp::BitwiseOrAssign
834    };
835    (^=) => {
836        $crate::ast::AssignOp::BitwiseXorAssign
837    };
838    (&&=) => {
839        $crate::ast::AssignOp::LogicalAndAssign
840    };
841    (||=) => {
842        $crate::ast::AssignOp::LogicalOrAssign
843    };
844    (??=) => {
845        $crate::ast::AssignOp::NullishCoalescingAssign
846    };
847
848    (++) => {
849        $crate::ast::UnaryOp::Increment
850    };
851    (--) => {
852        $crate::ast::UnaryOp::Decrement
853    };
854    (delete) => {
855        $crate::ast::UnaryOp::Delete
856    };
857    (void) => {
858        $crate::ast::UnaryOp::Void
859    };
860    (typeof) => {
861        $crate::ast::UnaryOp::Typeof
862    };
863    (+) => {
864        $crate::ast::UnaryOp::Plus
865    };
866    (-) => {
867        $crate::ast::UnaryOp::Minus
868    };
869    (~) => {
870        $crate::ast::UnaryOp::BitwiseNot
871    };
872    (!) => {
873        $crate::ast::UnaryOp::LogicalNot
874    };
875    (await) => {
876        $crate::ast::UnaryOp::Await
877    };
878}