1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
//! Assignment expression nodes, as defined by the [spec].
//!
//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right
//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple
//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=`
//! only allow ["simple"][simple] left hand side expressions as an assignment target.
//!
//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression
//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype

mod op;

use core::ops::ControlFlow;
pub use op::*;

use boa_interner::{Interner, Sym, ToInternedString};

use crate::{
    expression::{access::PropertyAccess, identifier::Identifier, Expression},
    pattern::Pattern,
    try_break,
    visitor::{VisitWith, Visitor, VisitorMut},
};

/// An assignment operator expression.
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Assign {
    op: AssignOp,
    lhs: Box<AssignTarget>,
    rhs: Box<Expression>,
}

impl Assign {
    /// Creates an `Assign` AST Expression.
    #[inline]
    #[must_use]
    pub fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self {
        Self {
            op,
            lhs: Box::new(lhs),
            rhs: Box::new(rhs),
        }
    }

    /// Gets the operator of the assignment operation.
    #[inline]
    #[must_use]
    pub const fn op(&self) -> AssignOp {
        self.op
    }

    /// Gets the left hand side of the assignment operation.
    #[inline]
    #[must_use]
    pub const fn lhs(&self) -> &AssignTarget {
        &self.lhs
    }

    /// Gets the right hand side of the assignment operation.
    #[inline]
    #[must_use]
    pub const fn rhs(&self) -> &Expression {
        &self.rhs
    }
}

impl ToInternedString for Assign {
    #[inline]
    fn to_interned_string(&self, interner: &Interner) -> String {
        format!(
            "{} {} {}",
            self.lhs.to_interned_string(interner),
            self.op,
            self.rhs.to_interned_string(interner)
        )
    }
}

impl From<Assign> for Expression {
    #[inline]
    fn from(op: Assign) -> Self {
        Self::Assign(op)
    }
}

impl VisitWith for Assign {
    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
    where
        V: Visitor<'a>,
    {
        try_break!(visitor.visit_assign_target(&self.lhs));
        visitor.visit_expression(&self.rhs)
    }

    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
    where
        V: VisitorMut<'a>,
    {
        try_break!(visitor.visit_assign_target_mut(&mut self.lhs));
        visitor.visit_expression_mut(&mut self.rhs)
    }
}

/// The valid left-hand-side expressions of an assignment operator. Also called
/// [`LeftHandSideExpression`][spec] in the spec.
///
/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum AssignTarget {
    /// A simple identifier, such as `a`.
    Identifier(Identifier),
    /// A property access, such as `a.prop`.
    Access(PropertyAccess),
    /// A pattern assignment, such as `{a, b, ...c}`.
    Pattern(Pattern),
}

impl AssignTarget {
    /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].
    /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.
    #[must_use]
    pub fn from_expression(expression: &Expression, strict: bool) -> Option<Self> {
        match expression {
            Expression::ObjectLiteral(object) => {
                let pattern = object.to_pattern(strict)?;
                Some(Self::Pattern(pattern.into()))
            }
            Expression::ArrayLiteral(array) => {
                let pattern = array.to_pattern(strict)?;
                Some(Self::Pattern(pattern.into()))
            }
            e => Self::from_expression_simple(e, strict),
        }
    }

    /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].
    /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.
    ///
    /// The `AssignmentTargetType` of the expression must be `simple`.
    #[must_use]
    pub fn from_expression_simple(expression: &Expression, strict: bool) -> Option<Self> {
        match expression {
            Expression::Identifier(id)
                if strict && (id.sym() == Sym::EVAL || id.sym() == Sym::ARGUMENTS) =>
            {
                None
            }
            Expression::Identifier(id) => Some(Self::Identifier(*id)),
            Expression::PropertyAccess(access) => Some(Self::Access(access.clone())),
            Expression::Parenthesized(p) => Self::from_expression_simple(p.expression(), strict),
            _ => None,
        }
    }
}

impl ToInternedString for AssignTarget {
    #[inline]
    fn to_interned_string(&self, interner: &Interner) -> String {
        match self {
            Self::Identifier(id) => id.to_interned_string(interner),
            Self::Access(access) => access.to_interned_string(interner),
            Self::Pattern(pattern) => pattern.to_interned_string(interner),
        }
    }
}

impl From<Identifier> for AssignTarget {
    #[inline]
    fn from(target: Identifier) -> Self {
        Self::Identifier(target)
    }
}

impl VisitWith for AssignTarget {
    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
    where
        V: Visitor<'a>,
    {
        match self {
            Self::Identifier(id) => visitor.visit_identifier(id),
            Self::Access(pa) => visitor.visit_property_access(pa),
            Self::Pattern(pat) => visitor.visit_pattern(pat),
        }
    }

    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
    where
        V: VisitorMut<'a>,
    {
        match self {
            Self::Identifier(id) => visitor.visit_identifier_mut(id),
            Self::Access(pa) => visitor.visit_property_access_mut(pa),
            Self::Pattern(pat) => visitor.visit_pattern_mut(pat),
        }
    }
}