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},
};
#[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 {
#[inline]
#[must_use]
pub fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self {
Self {
op,
lhs: Box::new(lhs),
rhs: Box::new(rhs),
}
}
#[inline]
#[must_use]
pub const fn op(&self) -> AssignOp {
self.op
}
#[inline]
#[must_use]
pub const fn lhs(&self) -> &AssignTarget {
&self.lhs
}
#[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)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum AssignTarget {
Identifier(Identifier),
Access(PropertyAccess),
Pattern(Pattern),
}
impl AssignTarget {
#[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),
}
}
#[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),
}
}
}