Skip to main content

boa_ast/expression/operator/binary/
op.rs

1//! This module implements various structure for logic handling.
2
3use std::fmt::{Display, Formatter, Result};
4
5/// This represents a binary operation between two values.
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum BinaryOp {
10    /// Numeric operation.
11    ///
12    /// see: [`NumOp`](enum.NumOp.html)
13    Arithmetic(ArithmeticOp),
14
15    /// Bitwise operation.
16    ///
17    /// see: [`BitOp`](enum.BitOp.html).
18    Bitwise(BitwiseOp),
19
20    /// Comparative operation.
21    ///
22    /// see: [`CompOp`](enum.CompOp.html).
23    Relational(RelationalOp),
24
25    /// Logical operation.
26    ///
27    /// see: [`LogOp`](enum.LogOp.html).
28    Logical(LogicalOp),
29
30    /// Comma operation.
31    Comma,
32}
33
34impl From<ArithmeticOp> for BinaryOp {
35    #[inline]
36    fn from(op: ArithmeticOp) -> Self {
37        Self::Arithmetic(op)
38    }
39}
40
41impl From<BitwiseOp> for BinaryOp {
42    #[inline]
43    fn from(op: BitwiseOp) -> Self {
44        Self::Bitwise(op)
45    }
46}
47
48impl From<RelationalOp> for BinaryOp {
49    #[inline]
50    fn from(op: RelationalOp) -> Self {
51        Self::Relational(op)
52    }
53}
54
55impl From<LogicalOp> for BinaryOp {
56    #[inline]
57    fn from(op: LogicalOp) -> Self {
58        Self::Logical(op)
59    }
60}
61
62impl BinaryOp {
63    /// Retrieves the operation as a static string.
64    const fn as_str(self) -> &'static str {
65        match self {
66            Self::Arithmetic(ref op) => op.as_str(),
67            Self::Bitwise(ref op) => op.as_str(),
68            Self::Relational(ref op) => op.as_str(),
69            Self::Logical(ref op) => op.as_str(),
70            Self::Comma => ",",
71        }
72    }
73}
74
75impl Display for BinaryOp {
76    #[inline]
77    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
78        write!(f, "{}", self.as_str())
79    }
80}
81
82/// Arithmetic operators take numerical values (either literals or variables)
83/// as their operands and return a single numerical value.
84///
85/// More information:
86///  - [MDN documentation][mdn]
87///
88/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92pub enum ArithmeticOp {
93    /// The addition operator produces the sum of numeric operands or string concatenation.
94    ///
95    /// Syntax: `x + y`
96    ///
97    /// More information:
98    ///  - [ECMAScript reference][spec].
99    ///  - [MDN documentation][mdn]
100    ///
101    /// [spec]: https://tc39.es/ecma262/#sec-addition-operator-plus
102    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Addition
103    Add,
104
105    /// The subtraction operator subtracts the two operands, producing their difference.
106    ///
107    /// Syntax: `x - y`
108    ///
109    /// More information:
110    ///  - [ECMAScript reference][spec].
111    ///  - [MDN documentation][mdn]
112    ///
113    /// [spec]: https://tc39.es/ecma262/#sec-subtraction-operator-minus
114    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Subtraction
115    Sub,
116
117    /// The division operator produces the quotient of its operands where the left operand
118    /// is the dividend and the right operand is the divisor.
119    ///
120    /// Syntax: `x / y`
121    ///
122    /// More information:
123    ///  - [ECMAScript reference][spec]
124    ///  - [MDN documentation][mdn]
125    ///
126    /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator
127    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division
128    Div,
129
130    /// The multiplication operator produces the product of the operands.
131    ///
132    /// Syntax: `x * y`
133    ///
134    /// More information:
135    ///  - [ECMAScript reference][spec]
136    ///  - [MDN documentation][mdn]
137    ///
138    /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeExpression
139    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Multiplication
140    Mul,
141
142    /// The exponentiation operator returns the result of raising the first operand to
143    /// the power of the second operand.
144    ///
145    /// Syntax: `x ** y`
146    ///
147    /// The exponentiation operator is right-associative. a ** b ** c is equal to a ** (b ** c).
148    ///
149    /// More information:
150    ///  - [ECMAScript reference][spec]
151    ///  - [MDN documentation][mdn]
152    ///
153    /// [spec]: https://tc39.es/ecma262/#sec-exp-operator
154    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation
155    Exp,
156
157    /// The remainder operator returns the remainder left over when one operand is divided by a second operand.
158    ///
159    /// Syntax: `x % y`
160    ///
161    /// The remainder operator always takes the sign of the dividend.
162    ///
163    /// More information:
164    ///  - [ECMAScript reference][spec]
165    ///  - [MDN documentation][mdn]
166    ///
167    /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator
168    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder
169    Mod,
170}
171
172impl ArithmeticOp {
173    /// Retrieves the operation as a static string.
174    const fn as_str(self) -> &'static str {
175        match self {
176            Self::Add => "+",
177            Self::Sub => "-",
178            Self::Div => "/",
179            Self::Mul => "*",
180            Self::Exp => "**",
181            Self::Mod => "%",
182        }
183    }
184}
185
186impl Display for ArithmeticOp {
187    #[inline]
188    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
189        write!(f, "{}", self.as_str())
190    }
191}
192
193/// A bitwise operator is an operator used to perform bitwise operations
194/// on bit patterns or binary numerals that involve the manipulation of individual bits.
195///
196/// More information:
197///  - [MDN documentation][mdn]
198///
199/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise
200#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
201#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
203pub enum BitwiseOp {
204    /// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1.
205    ///
206    /// Syntax: `x & y`
207    ///
208    /// More information:
209    ///  - [ECMAScript reference][spec]
210    ///  - [MDN documentation][mdn]
211    ///
212    /// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression
213    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND
214    And,
215
216    /// Performs the OR operation on each pair of bits. a OR b yields 1 if either a or b is 1.
217    ///
218    /// Syntax: `x | y`
219    ///
220    /// More information:
221    ///  - [ECMAScript reference][spec]
222    ///  - [MDN documentation][mdn]
223    ///
224    /// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression
225    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR
226    Or,
227
228    /// Performs the XOR operation on each pair of bits. a XOR b yields 1 if a and b are different.
229    ///
230    /// Syntax: `x ^ y`
231    ///
232    /// More information:
233    ///  - [ECMAScript reference][spec]
234    ///  - [MDN documentation][mdn]
235    ///
236    /// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression
237    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR
238    Xor,
239
240    /// This operator shifts the first operand the specified number of bits to the left.
241    ///
242    /// Syntax: `x << y`
243    ///
244    /// Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right.
245    ///
246    /// More information:
247    ///  - [ECMAScript reference][spec]
248    ///  - [MDN documentation][mdn]
249    ///
250    /// [spec]: https://tc39.es/ecma262/#sec-left-shift-operator
251    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Left_shift
252    Shl,
253
254    /// This operator shifts the first operand the specified number of bits to the right.
255    ///
256    /// Syntax: `x >> y`
257    ///
258    /// Excess bits shifted off to the right are discarded. Copies of the leftmost bit
259    /// are shifted in from the left. Since the new leftmost bit has the same value as
260    /// the previous leftmost bit, the sign bit (the leftmost bit) does not change.
261    /// Hence the name "sign-propagating".
262    ///
263    /// More information:
264    ///  - [ECMAScript reference][spec]
265    ///  - [MDN documentation][mdn]
266    ///
267    /// [spec]: https://tc39.es/ecma262/#sec-signed-right-shift-operator
268    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift
269    Shr,
270
271    /// This operator shifts the first operand the specified number of bits to the right.
272    ///
273    /// Syntax: `x >>> y`
274    ///
275    /// Excess bits shifted off to the right are discarded. Zero bits are shifted in
276    /// from the left. The sign bit becomes 0, so the result is always non-negative.
277    /// Unlike the other bitwise operators, zero-fill right shift returns an unsigned 32-bit integer.
278    ///
279    /// More information:
280    ///  - [ECMAScript reference][spec]
281    ///  - [MDN documentation][mdn]
282    ///
283    /// [spec]: https://tc39.es/ecma262/#sec-unsigned-right-shift-operator
284    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Unsigned_right_shift
285    UShr,
286}
287
288impl BitwiseOp {
289    /// Retrieves the operation as a static string.
290    const fn as_str(self) -> &'static str {
291        match self {
292            Self::And => "&",
293            Self::Or => "|",
294            Self::Xor => "^",
295            Self::Shl => "<<",
296            Self::Shr => ">>",
297            Self::UShr => ">>>",
298        }
299    }
300}
301
302impl Display for BitwiseOp {
303    #[inline]
304    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
305        write!(f, "{}", self.as_str())
306    }
307}
308
309/// A relational operator compares its operands and returns a logical value based on whether the relation is true.
310///
311/// The operands can be numerical, string, logical, or object values. Strings are compared based on standard
312/// lexicographical ordering, using Unicode values. In most cases, if the two operands are not of the same type,
313/// JavaScript attempts to convert them to an appropriate type for the comparison. This behavior generally results in
314/// comparing the operands numerically. The sole exceptions to type conversion within comparisons involve the `===` and `!==`
315/// operators, which perform strict equality and inequality comparisons. These operators do not attempt to convert the operands
316/// to compatible types before checking equality.
317///
318/// More information:
319///  - [ECMAScript reference][spec]
320///  - [MDN documentation][mdn]
321///
322/// [spec]: tc39.es/ecma262/#sec-testing-and-comparison-operations
323/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Comparison
324#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
325#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
326#[derive(Clone, Copy, Debug, PartialEq, Eq)]
327pub enum RelationalOp {
328    /// The equality operator converts the operands if they are not of the same type, then applies
329    /// strict comparison.
330    ///
331    /// Syntax: `y == y`
332    ///
333    /// If both operands are objects, then JavaScript compares internal references which are equal
334    /// when operands refer to the same object in memory.
335    ///
336    /// More information:
337    ///  - [ECMAScript reference][spec]
338    ///  - [MDN documentation][mdn]
339    ///
340    /// [spec]: https://tc39.es/ecma262/#sec-abstract-equality-comparison
341    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality
342    Equal,
343
344    /// The inequality operator returns `true` if the operands are not equal.
345    ///
346    /// Syntax: `x != y`
347    ///
348    /// If the two operands are not of the same type, JavaScript attempts to convert the operands
349    /// to an appropriate type for the comparison. If both operands are objects, then JavaScript
350    /// compares internal references which are not equal when operands refer to different objects
351    /// in memory.
352    ///
353    /// More information:
354    ///  - [ECMAScript reference][spec]
355    ///  - [MDN documentation][mdn]
356    ///
357    /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression
358    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Inequality
359    NotEqual,
360
361    /// The identity operator returns `true` if the operands are strictly equal **with no type
362    /// conversion**.
363    ///
364    /// Syntax: `x === y`
365    ///
366    /// Returns `true` if the operands are equal and of the same type.
367    ///
368    /// More information:
369    ///  - [ECMAScript reference][spec]
370    ///  - [MDN documentation][mdn]
371    ///
372    /// [spec]: https://tc39.es/ecma262/#sec-strict-equality-comparison
373    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity
374    StrictEqual,
375
376    /// The non-identity operator returns `true` if the operands **are not equal and/or not of the
377    /// same type**.
378    ///
379    /// Syntax: `x !== y`
380    ///
381    /// Returns `true` if the operands are of the same type but not equal, or are of different type.
382    ///
383    /// More information:
384    ///  - [ECMAScript reference][spec]
385    ///  - [MDN documentation][mdn]
386    ///
387    /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression
388    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Nonidentity>
389    StrictNotEqual,
390
391    /// The greater than operator returns `true` if the left operand is greater than the right
392    /// operand.
393    ///
394    /// Syntax: `x > y`
395    ///
396    /// Returns `true` if the left operand is greater than the right operand.
397    ///
398    /// More information:
399    ///  - [ECMAScript reference][spec]
400    ///  - [MDN documentation][mdn]
401    ///
402    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
403    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator
404    GreaterThan,
405
406    /// The greater than or equal operator returns `true` if the left operand is greater than or
407    /// equal to the right operand.
408    ///
409    /// Syntax: `x >= y`
410    ///
411    /// Returns `true` if the left operand is greater than the right operand.
412    ///
413    /// More information:
414    ///  - [ECMAScript reference][spec]
415    ///  - [MDN documentation][mdn]
416    ///
417    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
418    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator
419    GreaterThanOrEqual,
420
421    /// The less than operator returns `true` if the left operand is less than the right operand.
422    ///
423    /// Syntax: `x < y`
424    ///
425    /// Returns `true` if the left operand is less than the right operand.
426    ///
427    /// More information:
428    ///  - [ECMAScript reference][spec]
429    ///  - [MDN documentation][mdn]
430    ///
431    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
432    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_operator
433    LessThan,
434
435    /// The less than or equal operator returns `true` if the left operand is less than or equal to
436    /// the right operand.
437    ///
438    /// Syntax: `x <= y`
439    ///
440    /// Returns `true` if the left operand is less than or equal to the right operand.
441    ///
442    /// More information:
443    ///  - [ECMAScript reference][spec]
444    ///  - [MDN documentation][mdn]
445    ///
446    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
447    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_or_equal_operator
448    LessThanOrEqual,
449
450    /// The `in` operator returns `true` if the specified property is in the specified object or
451    /// its prototype chain.
452    ///
453    /// Syntax: `prop in object`
454    ///
455    /// Returns `true` the specified property is in the specified object or its prototype chain.
456    ///
457    /// More information:
458    ///  - [ECMAScript reference][spec]
459    ///  - [MDN documentation][mdn]
460    ///
461    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
462    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in
463    In,
464
465    /// The `instanceof` operator returns `true` if the specified object is an instance of the
466    /// right hand side object.
467    ///
468    /// Syntax: `obj instanceof Object`
469    ///
470    /// Returns `true` the `prototype` property of the right hand side constructor appears anywhere
471    /// in the prototype chain of the object.
472    ///
473    /// More information:
474    ///  - [ECMAScript reference][spec]
475    ///  - [MDN documentation][mdn]
476    ///
477    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression
478    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
479    InstanceOf,
480}
481
482impl RelationalOp {
483    /// Retrieves the operation as a static string.
484    const fn as_str(self) -> &'static str {
485        match self {
486            Self::Equal => "==",
487            Self::NotEqual => "!=",
488            Self::StrictEqual => "===",
489            Self::StrictNotEqual => "!==",
490            Self::GreaterThan => ">",
491            Self::GreaterThanOrEqual => ">=",
492            Self::LessThan => "<",
493            Self::LessThanOrEqual => "<=",
494            Self::In => "in",
495            Self::InstanceOf => "instanceof",
496        }
497    }
498}
499
500impl Display for RelationalOp {
501    #[inline]
502    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
503        write!(f, "{}", self.as_str())
504    }
505}
506
507/// Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value.
508///
509/// However, the `&&` and `||` operators actually return the value of one of the specified operands,
510/// so if these operators are used with non-Boolean values, they may return a non-Boolean value.
511///
512/// More information:
513///  - [ECMAScript reference][spec]
514///  - [MDN documentation][mdn]
515///
516/// [spec]: https://tc39.es/ecma262/#sec-binary-logical-operators
517/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Logical
518#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
519#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
520#[derive(Clone, Copy, Debug, PartialEq, Eq)]
521pub enum LogicalOp {
522    /// The logical AND operator returns the value of the first operand if it can be coerced into `false`;
523    /// otherwise, it returns the second operand.
524    ///
525    /// Syntax: `x && y`
526    ///
527    /// More information:
528    ///  - [ECMAScript reference][spec]
529    ///  - [MDN documentation][mdn]
530    ///
531    /// [spec]: https://tc39.es/ecma262/#prod-LogicalANDExpression
532    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_AND
533    And,
534
535    /// The logical OR operator returns the value the first operand if it can be coerced into `true`;
536    /// otherwise, it returns the second operand.
537    ///
538    /// Syntax: `x || y`
539    ///
540    /// More information:
541    ///  - [ECMAScript reference][spec]
542    ///  - [MDN documentation][mdn]
543    ///
544    /// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression
545    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR
546    Or,
547
548    /// The nullish coalescing operator is a logical operator that returns the second operand
549    /// when its first operand is null or undefined, and otherwise returns its first operand.
550    ///
551    /// Syntax: `x ?? y`
552    ///
553    /// More information:
554    ///  - [ECMAScript reference][spec]
555    ///  - [MDN documentation][mdn]
556    ///
557    /// [spec]: https://tc39.es/ecma262/#prod-CoalesceExpression
558    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
559    Coalesce,
560}
561
562impl LogicalOp {
563    /// Retrieves the operation as a static string.
564    const fn as_str(self) -> &'static str {
565        match self {
566            Self::And => "&&",
567            Self::Or => "||",
568            Self::Coalesce => "??",
569        }
570    }
571}
572
573impl Display for LogicalOp {
574    #[inline]
575    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
576        write!(f, "{}", self.as_str())
577    }
578}