Skip to main content

boa_ast/expression/operator/
conditional.rs

1use crate::{
2    Span, Spanned,
3    expression::Expression,
4    visitor::{VisitWith, Visitor, VisitorMut},
5};
6use boa_interner::{Interner, ToInternedString};
7use core::ops::ControlFlow;
8
9/// The `conditional` (ternary) operation is the only ECMAScript operation that takes three
10/// operands.
11///
12/// This operation takes three operands: a condition followed by a question mark (`?`),
13/// then an expression to execute `if` the condition is truthy followed by a colon (`:`),
14/// and finally the expression to execute if the condition is `false`.
15/// This operator is frequently used as a shortcut for the `if` statement.
16///
17/// More information:
18///  - [ECMAScript reference][spec]
19///  - [MDN documentation][mdn]
20///
21/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression
22/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[derive(Clone, Debug, PartialEq)]
26pub struct Conditional {
27    condition: Box<Expression>,
28    if_true: Box<Expression>,
29    if_false: Box<Expression>,
30}
31
32impl Conditional {
33    /// Gets the condition of the `Conditional` expression.
34    #[inline]
35    #[must_use]
36    pub const fn condition(&self) -> &Expression {
37        &self.condition
38    }
39
40    /// Gets the expression returned if `condition` is truthy.
41    #[inline]
42    #[must_use]
43    pub const fn if_true(&self) -> &Expression {
44        &self.if_true
45    }
46
47    /// Gets the expression returned if `condition` is falsy.
48    #[inline]
49    #[must_use]
50    pub const fn if_false(&self) -> &Expression {
51        &self.if_false
52    }
53
54    /// Creates a `Conditional` AST Expression.
55    #[inline]
56    #[must_use]
57    pub fn new(condition: Expression, if_true: Expression, if_false: Expression) -> Self {
58        Self {
59            condition: Box::new(condition),
60            if_true: Box::new(if_true),
61            if_false: Box::new(if_false),
62        }
63    }
64}
65
66impl Spanned for Conditional {
67    #[inline]
68    fn span(&self) -> Span {
69        Span::new(self.condition.span().start(), self.if_false.span().end())
70    }
71}
72
73impl ToInternedString for Conditional {
74    #[inline]
75    fn to_interned_string(&self, interner: &Interner) -> String {
76        format!(
77            "{} ? {} : {}",
78            self.condition().to_interned_string(interner),
79            self.if_true().to_interned_string(interner),
80            self.if_false().to_interned_string(interner)
81        )
82    }
83}
84
85impl From<Conditional> for Expression {
86    #[inline]
87    fn from(cond_op: Conditional) -> Self {
88        Self::Conditional(cond_op)
89    }
90}
91
92impl VisitWith for Conditional {
93    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
94    where
95        V: Visitor<'a>,
96    {
97        visitor.visit_expression(&self.condition)?;
98        visitor.visit_expression(&self.if_true)?;
99        visitor.visit_expression(&self.if_false)
100    }
101
102    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
103    where
104        V: VisitorMut<'a>,
105    {
106        visitor.visit_expression_mut(&mut self.condition)?;
107        visitor.visit_expression_mut(&mut self.if_true)?;
108        visitor.visit_expression_mut(&mut self.if_false)
109    }
110}