oxc_syntax/
operator.rs

1//! ECMAScript operators.
2//!
3//! Not all operators are punctuation - some, such as `delete`, are keywords.
4
5use oxc_allocator::CloneIn;
6use oxc_ast_macros::ast;
7use oxc_estree::ESTree;
8use oxc_span::ContentEq;
9
10use crate::precedence::{GetPrecedence, Precedence};
11
12/// Operators that may be used in assignment epxressions.
13///
14/// ## References
15/// - [13.15 Assignment Operators](https://tc39.es/ecma262/#sec-assignment-operators)
16#[ast]
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[generate_derive(CloneIn, ContentEq, ESTree)]
19pub enum AssignmentOperator {
20    /// `=`
21    #[estree(rename = "=")]
22    Assign = 0,
23    /// `+=`
24    #[estree(rename = "+=")]
25    Addition = 1,
26    /// `-=`
27    #[estree(rename = "-=")]
28    Subtraction = 2,
29    /// `*=`
30    #[estree(rename = "*=")]
31    Multiplication = 3,
32    /// `/=`
33    #[estree(rename = "/=")]
34    Division = 4,
35    /// `%=`
36    #[estree(rename = "%=")]
37    Remainder = 5,
38    /// `**=`
39    #[estree(rename = "**=")]
40    Exponential = 6,
41    /// `<<=`
42    #[estree(rename = "<<=")]
43    ShiftLeft = 7,
44    /// `>>=`
45    #[estree(rename = ">>=")]
46    ShiftRight = 8,
47    /// `>>>=`
48    #[estree(rename = ">>>=")]
49    ShiftRightZeroFill = 9,
50    /// `|=`
51    #[estree(rename = "|=")]
52    BitwiseOR = 10,
53    /// `^=`
54    #[estree(rename = "^=")]
55    BitwiseXOR = 11,
56    /// `&=`
57    #[estree(rename = "&=")]
58    BitwiseAnd = 12,
59    /// `||=`
60    #[estree(rename = "||=")]
61    LogicalOr = 13,
62    /// `&&=`
63    #[estree(rename = "&&=")]
64    LogicalAnd = 14,
65    /// `??=`
66    #[estree(rename = "??=")]
67    LogicalNullish = 15,
68}
69
70impl AssignmentOperator {
71    /// Returns `true` for `=`.
72    pub fn is_assign(self) -> bool {
73        self == Self::Assign
74    }
75
76    /// Returns `true` for '||=`, `&&=`, and `??=`.
77    pub fn is_logical(self) -> bool {
78        matches!(self, Self::LogicalOr | Self::LogicalAnd | Self::LogicalNullish)
79    }
80
81    /// Returns `true` for `+=`, `-=`, `*=`, `/=`, `%=`, and `**=`.
82    #[rustfmt::skip]
83    pub fn is_arithmetic(self) -> bool {
84        matches!(
85            self,
86            Self::Addition | Self::Subtraction | Self::Multiplication
87            | Self::Division | Self::Remainder | Self::Exponential
88        )
89    }
90
91    /// Returns `true` for `|=`, `^=`, `&=`, `<<=`, `>>=`, and `>>>=`.
92    #[rustfmt::skip]
93    pub fn is_bitwise(self) -> bool {
94        matches!(
95            self,
96            Self::ShiftLeft | Self::ShiftRight | Self::ShiftRightZeroFill
97            | Self::BitwiseOR | Self::BitwiseXOR | Self::BitwiseAnd
98        )
99    }
100
101    /// Get [`LogicalOperator`] corresponding to this [`AssignmentOperator`].
102    pub fn to_logical_operator(self) -> Option<LogicalOperator> {
103        match self {
104            Self::LogicalOr => Some(LogicalOperator::Or),
105            Self::LogicalAnd => Some(LogicalOperator::And),
106            Self::LogicalNullish => Some(LogicalOperator::Coalesce),
107            _ => None,
108        }
109    }
110
111    /// Get [`BinaryOperator`] corresponding to this [`AssignmentOperator`].
112    pub fn to_binary_operator(self) -> Option<BinaryOperator> {
113        match self {
114            Self::Addition => Some(BinaryOperator::Addition),
115            Self::Subtraction => Some(BinaryOperator::Subtraction),
116            Self::Multiplication => Some(BinaryOperator::Multiplication),
117            Self::Division => Some(BinaryOperator::Division),
118            Self::Remainder => Some(BinaryOperator::Remainder),
119            Self::Exponential => Some(BinaryOperator::Exponential),
120            Self::ShiftLeft => Some(BinaryOperator::ShiftLeft),
121            Self::ShiftRight => Some(BinaryOperator::ShiftRight),
122            Self::ShiftRightZeroFill => Some(BinaryOperator::ShiftRightZeroFill),
123            Self::BitwiseOR => Some(BinaryOperator::BitwiseOR),
124            Self::BitwiseXOR => Some(BinaryOperator::BitwiseXOR),
125            Self::BitwiseAnd => Some(BinaryOperator::BitwiseAnd),
126            _ => None,
127        }
128    }
129
130    /// Get the string representation of this operator.
131    ///
132    /// This is the same as how the operator appears in source code.
133    pub fn as_str(&self) -> &'static str {
134        match self {
135            Self::Assign => "=",
136            Self::Addition => "+=",
137            Self::Subtraction => "-=",
138            Self::Multiplication => "*=",
139            Self::Division => "/=",
140            Self::Remainder => "%=",
141            Self::Exponential => "**=",
142            Self::ShiftLeft => "<<=",
143            Self::ShiftRight => ">>=",
144            Self::ShiftRightZeroFill => ">>>=",
145            Self::BitwiseOR => "|=",
146            Self::BitwiseXOR => "^=",
147            Self::BitwiseAnd => "&=",
148            Self::LogicalOr => "||=",
149            Self::LogicalAnd => "&&=",
150            Self::LogicalNullish => "??=",
151        }
152    }
153}
154
155/// Operators used in binary expressions. Does not include logical binary
156/// operators, which are in [`LogicalOperator`].
157///
158/// ## References
159/// - [12.10 Binary Logical Operators](https://tc39.es/ecma262/#sec-binary-logical-operators)
160#[ast]
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
162#[generate_derive(CloneIn, ContentEq, ESTree)]
163pub enum BinaryOperator {
164    /// `==`
165    #[estree(rename = "==")]
166    Equality = 0,
167    /// `!=`
168    #[estree(rename = "!=")]
169    Inequality = 1,
170    /// `===`
171    #[estree(rename = "===")]
172    StrictEquality = 2,
173    /// `!==`
174    #[estree(rename = "!==")]
175    StrictInequality = 3,
176    /// `<`
177    #[estree(rename = "<")]
178    LessThan = 4,
179    /// `<=`
180    #[estree(rename = "<=")]
181    LessEqualThan = 5,
182    /// `>`
183    #[estree(rename = ">")]
184    GreaterThan = 6,
185    /// `>=`
186    #[estree(rename = ">=")]
187    GreaterEqualThan = 7,
188    /// `+`
189    #[estree(rename = "+")]
190    Addition = 8,
191    /// `-`
192    #[estree(rename = "-")]
193    Subtraction = 9,
194    /// `*`
195    #[estree(rename = "*")]
196    Multiplication = 10,
197    /// `/`
198    #[estree(rename = "/")]
199    Division = 11,
200    /// `%`
201    #[estree(rename = "%")]
202    Remainder = 12,
203    /// `**`
204    #[estree(rename = "**")]
205    Exponential = 13,
206    /// `<<`
207    #[estree(rename = "<<")]
208    ShiftLeft = 14,
209    /// `>>`
210    #[estree(rename = ">>")]
211    ShiftRight = 15,
212    /// `>>>`
213    #[estree(rename = ">>>")]
214    ShiftRightZeroFill = 16,
215    /// `|`
216    #[estree(rename = "|")]
217    BitwiseOR = 17,
218    /// `^`
219    #[estree(rename = "^")]
220    BitwiseXOR = 18,
221    /// `&`
222    #[estree(rename = "&")]
223    BitwiseAnd = 19,
224    /// `in`
225    #[estree(rename = "in")]
226    In = 20,
227    /// `instanceof`
228    #[estree(rename = "instanceof")]
229    Instanceof = 21,
230}
231
232impl BinaryOperator {
233    /// Returns `true` for inequality or inequality operarors
234    #[rustfmt::skip]
235    pub fn is_equality(self) -> bool {
236        matches!(self, Self::Equality | Self::Inequality | Self::StrictEquality | Self::StrictInequality)
237    }
238
239    /// Returns `true` for logical comparison operators
240    #[rustfmt::skip]
241    pub fn is_compare(self) -> bool {
242        matches!(self, Self::LessThan | Self::LessEqualThan | Self::GreaterThan | Self::GreaterEqualThan)
243    }
244
245    /// Returns `true` for arithmetic operators
246    #[rustfmt::skip]
247    pub fn is_arithmetic(self) -> bool {
248        matches!(
249            self,
250            Self::Addition | Self::Subtraction | Self::Multiplication
251            | Self::Division | Self::Remainder | Self::Exponential
252        )
253    }
254
255    /// Returns `true` for multiplication (`*`), division (`/`), and remainder
256    /// (`%`) operators
257    pub fn is_multiplicative(self) -> bool {
258        matches!(self, Self::Multiplication | Self::Division | Self::Remainder)
259    }
260
261    /// Returns `true` for object relation operators
262    pub fn is_relational(self) -> bool {
263        matches!(self, Self::In | Self::Instanceof)
264    }
265
266    /// Returns `true` if this is an [`In`](BinaryOperator::In) operator.
267    pub fn is_in(self) -> bool {
268        self == Self::In
269    }
270
271    /// Returns `true` if this is an [`In`](BinaryOperator::Instanceof) operator.
272    pub fn is_instance_of(self) -> bool {
273        self == Self::Instanceof
274    }
275
276    /// Returns `true` for any bitwise operator
277    #[rustfmt::skip]
278    pub fn is_bitwise(self) -> bool {
279        self.is_bitshift() || matches!(self, Self::BitwiseOR | Self::BitwiseXOR | Self::BitwiseAnd)
280    }
281
282    /// Returns `true` for any bitshift operator
283    pub fn is_bitshift(self) -> bool {
284        matches!(self, Self::ShiftLeft | Self::ShiftRight | Self::ShiftRightZeroFill)
285    }
286
287    /// Returns `true` for any numeric or string binary operator
288    pub fn is_numeric_or_string_binary_operator(self) -> bool {
289        self.is_arithmetic() || self.is_bitwise()
290    }
291
292    /// Returns `true` if this operator is a keyword instead of punctuation.
293    pub fn is_keyword(self) -> bool {
294        matches!(self, Self::In | Self::Instanceof)
295    }
296
297    /// Try to get the operator that performs the inverse comparison operation.
298    /// [`None`] if this is not a comparison operator.
299    pub fn compare_inverse_operator(self) -> Option<Self> {
300        match self {
301            Self::LessThan => Some(Self::GreaterThan),
302            Self::LessEqualThan => Some(Self::GreaterEqualThan),
303            Self::GreaterThan => Some(Self::LessThan),
304            Self::GreaterEqualThan => Some(Self::LessEqualThan),
305            _ => None,
306        }
307    }
308
309    /// Try to get the operator that performs the inverse equality operation.
310    /// [`None`] if this is not an equality operator.
311    pub fn equality_inverse_operator(self) -> Option<Self> {
312        match self {
313            Self::Equality => Some(Self::Inequality),
314            Self::Inequality => Some(Self::Equality),
315            Self::StrictEquality => Some(Self::StrictInequality),
316            Self::StrictInequality => Some(Self::StrictEquality),
317            _ => None,
318        }
319    }
320
321    /// Get [`AssignmentOperator`] corresponding to this [`BinaryOperator`].
322    pub fn to_assignment_operator(self) -> Option<AssignmentOperator> {
323        match self {
324            Self::Addition => Some(AssignmentOperator::Addition),
325            Self::Subtraction => Some(AssignmentOperator::Subtraction),
326            Self::Multiplication => Some(AssignmentOperator::Multiplication),
327            Self::Division => Some(AssignmentOperator::Division),
328            Self::Remainder => Some(AssignmentOperator::Remainder),
329            Self::Exponential => Some(AssignmentOperator::Exponential),
330            Self::ShiftLeft => Some(AssignmentOperator::ShiftLeft),
331            Self::ShiftRight => Some(AssignmentOperator::ShiftRight),
332            Self::ShiftRightZeroFill => Some(AssignmentOperator::ShiftRightZeroFill),
333            Self::BitwiseOR => Some(AssignmentOperator::BitwiseOR),
334            Self::BitwiseXOR => Some(AssignmentOperator::BitwiseXOR),
335            Self::BitwiseAnd => Some(AssignmentOperator::BitwiseAnd),
336            _ => None,
337        }
338    }
339
340    /// The string representation of this operator as it appears in source code.
341    pub fn as_str(&self) -> &'static str {
342        match self {
343            Self::Equality => "==",
344            Self::Inequality => "!=",
345            Self::StrictEquality => "===",
346            Self::StrictInequality => "!==",
347            Self::LessThan => "<",
348            Self::LessEqualThan => "<=",
349            Self::GreaterThan => ">",
350            Self::GreaterEqualThan => ">=",
351            Self::Addition => "+",
352            Self::Subtraction => "-",
353            Self::Multiplication => "*",
354            Self::Division => "/",
355            Self::Remainder => "%",
356            Self::Exponential => "**",
357            Self::ShiftLeft => "<<",
358            Self::ShiftRight => ">>",
359            Self::ShiftRightZeroFill => ">>>",
360            Self::BitwiseOR => "|",
361            Self::BitwiseXOR => "^",
362            Self::BitwiseAnd => "&",
363            Self::In => "in",
364            Self::Instanceof => "instanceof",
365        }
366    }
367
368    /// Get the operator that has a lower precedence than this operator by a
369    /// single level. Use [`BinaryOperator::precedence`] to get the operator
370    /// with a higher precedence.
371    pub fn lower_precedence(&self) -> Precedence {
372        match self {
373            Self::BitwiseOR => Precedence::LogicalAnd,
374            Self::BitwiseXOR => Precedence::BitwiseOr,
375            Self::BitwiseAnd => Precedence::BitwiseXor,
376            Self::Equality | Self::Inequality | Self::StrictEquality | Self::StrictInequality => {
377                Precedence::BitwiseAnd
378            }
379            Self::LessThan
380            | Self::LessEqualThan
381            | Self::GreaterThan
382            | Self::GreaterEqualThan
383            | Self::Instanceof
384            | Self::In => Precedence::Equals,
385            Self::ShiftLeft | Self::ShiftRight | Self::ShiftRightZeroFill => Precedence::Compare,
386            Self::Addition | Self::Subtraction => Precedence::Shift,
387            Self::Multiplication | Self::Remainder | Self::Division => Precedence::Add,
388            Self::Exponential => Precedence::Multiply,
389        }
390    }
391}
392
393impl GetPrecedence for BinaryOperator {
394    fn precedence(&self) -> Precedence {
395        match self {
396            Self::BitwiseOR => Precedence::BitwiseOr,
397            Self::BitwiseXOR => Precedence::BitwiseXor,
398            Self::BitwiseAnd => Precedence::BitwiseAnd,
399            Self::Equality | Self::Inequality | Self::StrictEquality | Self::StrictInequality => {
400                Precedence::Equals
401            }
402            Self::LessThan
403            | Self::LessEqualThan
404            | Self::GreaterThan
405            | Self::GreaterEqualThan
406            | Self::Instanceof
407            | Self::In => Precedence::Compare,
408            Self::ShiftLeft | Self::ShiftRight | Self::ShiftRightZeroFill => Precedence::Shift,
409            Self::Subtraction | Self::Addition => Precedence::Add,
410            Self::Multiplication | Self::Remainder | Self::Division => Precedence::Multiply,
411            Self::Exponential => Precedence::Exponentiation,
412        }
413    }
414}
415
416/// Logical binary operators
417#[ast]
418#[derive(Debug, Clone, Copy, PartialEq, Eq)]
419#[generate_derive(CloneIn, ContentEq, ESTree)]
420pub enum LogicalOperator {
421    /// `||`
422    #[estree(rename = "||")]
423    Or = 0,
424    /// `&&`
425    #[estree(rename = "&&")]
426    And = 1,
427    /// `??`
428    #[estree(rename = "??")]
429    Coalesce = 2,
430}
431
432impl LogicalOperator {
433    /// Is `||`
434    #[inline]
435    pub fn is_or(self) -> bool {
436        self == Self::Or
437    }
438
439    /// Is `&&`
440    #[inline]
441    pub fn is_and(self) -> bool {
442        self == Self::And
443    }
444
445    /// Is `??`
446    #[inline]
447    pub fn is_coalesce(self) -> bool {
448        self == Self::Coalesce
449    }
450
451    /// Get the string representation of this operator as it appears in source code.
452    pub fn as_str(&self) -> &'static str {
453        match self {
454            Self::Or => "||",
455            Self::And => "&&",
456            Self::Coalesce => "??",
457        }
458    }
459
460    /// Get the operator that has a lower precedence than this operator by a
461    /// single level. Use [`BinaryOperator::precedence`] to get the operator
462    /// with a higher precedence.
463    pub fn lower_precedence(&self) -> Precedence {
464        match self {
465            Self::Or => Precedence::NullishCoalescing,
466            Self::And => Precedence::LogicalOr,
467            Self::Coalesce => Precedence::Conditional,
468        }
469    }
470
471    /// Get [`AssignmentOperator`] corresponding to this [`LogicalOperator`].
472    pub fn to_assignment_operator(self) -> AssignmentOperator {
473        match self {
474            Self::Or => AssignmentOperator::LogicalOr,
475            Self::And => AssignmentOperator::LogicalAnd,
476            Self::Coalesce => AssignmentOperator::LogicalNullish,
477        }
478    }
479}
480
481impl GetPrecedence for LogicalOperator {
482    fn precedence(&self) -> Precedence {
483        match self {
484            Self::Or => Precedence::LogicalOr,
485            Self::And => Precedence::LogicalAnd,
486            Self::Coalesce => Precedence::NullishCoalescing,
487        }
488    }
489}
490
491/// Operators used in unary operators.
492///
493/// Does not include self-modifying operators, which are in [`UpdateOperator`].
494///
495/// ## References
496/// - [12.5 Unary Operators](https://tc39.es/ecma262/#sec-unary-operators)
497#[ast]
498#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
499#[generate_derive(CloneIn, ContentEq, ESTree)]
500pub enum UnaryOperator {
501    /// `+`
502    #[estree(rename = "+")]
503    UnaryPlus = 0,
504    /// `-`
505    #[estree(rename = "-")]
506    UnaryNegation = 1,
507    /// `!`
508    #[estree(rename = "!")]
509    LogicalNot = 2,
510    /// `~`
511    #[estree(rename = "~")]
512    BitwiseNot = 3,
513    /// `typeof`
514    #[estree(rename = "typeof")]
515    Typeof = 4,
516    /// `void`
517    #[estree(rename = "void")]
518    Void = 5,
519    /// `delete`
520    #[estree(rename = "delete")]
521    Delete = 6,
522}
523
524impl UnaryOperator {
525    /// Returns `true` if this operator is a unary arithmetic operator.
526    pub fn is_arithmetic(self) -> bool {
527        matches!(self, Self::UnaryPlus | Self::UnaryNegation)
528    }
529
530    /// Returns `true` if this operator is a [`LogicalNot`].
531    ///
532    /// [`LogicalNot`]: UnaryOperator::LogicalNot
533    pub fn is_not(self) -> bool {
534        self == Self::LogicalNot
535    }
536
537    /// Returns `true` if this operator is a bitwise operator.
538    pub fn is_bitwise(self) -> bool {
539        self == Self::BitwiseNot
540    }
541
542    /// Returns `true` if this is the [`void`](UnaryOperator::Typeof) operator.
543    pub fn is_typeof(self) -> bool {
544        self == Self::Typeof
545    }
546
547    /// Returns `true` if this is the [`void`](UnaryOperator::Void) operator.
548    pub fn is_void(self) -> bool {
549        self == Self::Void
550    }
551
552    /// Returns `true` if this is the [`delete`](UnaryOperator::Delete) operator.
553    pub fn is_delete(self) -> bool {
554        self == Self::Delete
555    }
556
557    /// Returns `true` if this operator is a keyword instead of punctuation.
558    pub fn is_keyword(self) -> bool {
559        matches!(self, Self::Typeof | Self::Void | Self::Delete)
560    }
561
562    /// Get the string representation of this operator as it appears in source code.
563    pub fn as_str(&self) -> &'static str {
564        match self {
565            Self::UnaryPlus => "+",
566            Self::UnaryNegation => "-",
567            Self::LogicalNot => "!",
568            Self::BitwiseNot => "~",
569            Self::Typeof => "typeof",
570            Self::Void => "void",
571            Self::Delete => "delete",
572        }
573    }
574}
575
576/// Unary update operators.
577#[ast]
578#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
579#[generate_derive(CloneIn, ContentEq, ESTree)]
580pub enum UpdateOperator {
581    /// `++`
582    #[estree(rename = "++")]
583    Increment = 0,
584    /// `--`
585    #[estree(rename = "--")]
586    Decrement = 1,
587}
588
589impl UpdateOperator {
590    /// Get the string representation of this operator as it appears in source code.
591    pub fn as_str(&self) -> &'static str {
592        match self {
593            Self::Increment => "++",
594            Self::Decrement => "--",
595        }
596    }
597}