use crate::{
Expression, Span, Spanned,
expression::{Identifier, access::PropertyAccess},
property::PropertyName,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToInternedString};
use core::{fmt::Write as _, ops::ControlFlow};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum Pattern {
Object(ObjectPattern),
Array(ArrayPattern),
}
impl Spanned for Pattern {
#[inline]
fn span(&self) -> Span {
match self {
Pattern::Object(object_pattern) => object_pattern.span(),
Pattern::Array(array_pattern) => array_pattern.span(),
}
}
}
impl From<ObjectPattern> for Pattern {
fn from(obj: ObjectPattern) -> Self {
Self::Object(obj)
}
}
impl From<ArrayPattern> for Pattern {
fn from(obj: ArrayPattern) -> Self {
Self::Array(obj)
}
}
impl ToInternedString for Pattern {
fn to_interned_string(&self, interner: &Interner) -> String {
match &self {
Self::Object(o) => o.to_interned_string(interner),
Self::Array(a) => a.to_interned_string(interner),
}
}
}
impl VisitWith for Pattern {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::Object(op) => visitor.visit_object_pattern(op),
Self::Array(ap) => visitor.visit_array_pattern(ap),
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::Object(op) => visitor.visit_object_pattern_mut(op),
Self::Array(ap) => visitor.visit_array_pattern_mut(ap),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectPattern {
elements: Box<[ObjectPatternElement]>,
span: Span,
}
impl ToInternedString for ObjectPattern {
fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = "{".to_owned();
for (i, binding) in self.elements.iter().enumerate() {
let binding = binding.to_interned_string(interner);
let str = if i == self.elements.len() - 1 {
format!("{binding} ")
} else {
format!("{binding},")
};
buf.push_str(&str);
}
if self.elements.is_empty() {
buf.push(' ');
}
buf.push('}');
buf
}
}
impl ObjectPattern {
#[inline]
#[must_use]
pub const fn new(elements: Box<[ObjectPatternElement]>, span: Span) -> Self {
Self { elements, span }
}
#[inline]
#[must_use]
pub const fn bindings(&self) -> &[ObjectPatternElement] {
&self.elements
}
#[inline]
#[must_use]
pub const fn has_rest(&self) -> bool {
matches!(
self.elements.last(),
Some(ObjectPatternElement::RestProperty { .. })
)
}
}
impl Spanned for ObjectPattern {
#[inline]
fn span(&self) -> Span {
self.span
}
}
impl VisitWith for ObjectPattern {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
for elem in &*self.elements {
visitor.visit_object_pattern_element(elem)?;
}
ControlFlow::Continue(())
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
for elem in &mut *self.elements {
visitor.visit_object_pattern_element_mut(elem)?;
}
ControlFlow::Continue(())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ArrayPattern {
bindings: Box<[ArrayPatternElement]>,
span: Span,
}
impl ToInternedString for ArrayPattern {
fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = "[".to_owned();
for (i, binding) in self.bindings.iter().enumerate() {
if i == self.bindings.len() - 1 {
match binding {
ArrayPatternElement::Elision => {
let _ = write!(buf, "{}, ", binding.to_interned_string(interner));
}
_ => {
let _ = write!(buf, "{} ", binding.to_interned_string(interner));
}
}
} else {
let _ = write!(buf, "{},", binding.to_interned_string(interner));
}
}
buf.push(']');
buf
}
}
impl ArrayPattern {
#[inline]
#[must_use]
pub fn new(bindings: Box<[ArrayPatternElement]>, span: Span) -> Self {
Self { bindings, span }
}
#[inline]
#[must_use]
pub const fn bindings(&self) -> &[ArrayPatternElement] {
&self.bindings
}
}
impl Spanned for ArrayPattern {
#[inline]
fn span(&self) -> Span {
self.span
}
}
impl VisitWith for ArrayPattern {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
for elem in &*self.bindings {
visitor.visit_array_pattern_element(elem)?;
}
ControlFlow::Continue(())
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
for elem in &mut *self.bindings {
visitor.visit_array_pattern_element_mut(elem)?;
}
ControlFlow::Continue(())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ObjectPatternElement {
SingleName {
name: PropertyName,
ident: Identifier,
default_init: Option<Expression>,
},
RestProperty {
ident: Identifier,
},
AssignmentPropertyAccess {
name: PropertyName,
access: PropertyAccess,
default_init: Option<Expression>,
},
AssignmentRestPropertyAccess {
access: PropertyAccess,
},
Pattern {
name: PropertyName,
pattern: Pattern,
default_init: Option<Expression>,
},
}
impl ToInternedString for ObjectPatternElement {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
Self::SingleName {
ident,
name,
default_init,
} => {
let mut buf = match name {
PropertyName::Literal(name) if name == ident => {
format!(" {}", interner.resolve_expect(ident.sym()))
}
PropertyName::Literal(name) => {
format!(
" {} : {}",
interner.resolve_expect(name.sym()),
interner.resolve_expect(ident.sym())
)
}
PropertyName::Computed(node) => {
format!(
" [{}] : {}",
node.to_interned_string(interner),
interner.resolve_expect(ident.sym())
)
}
};
if let Some(init) = default_init {
let _ = write!(buf, " = {}", init.to_interned_string(interner));
}
buf
}
Self::RestProperty { ident } => {
format!(" ... {}", interner.resolve_expect(ident.sym()))
}
Self::AssignmentRestPropertyAccess { access } => {
format!(" ... {}", access.to_interned_string(interner))
}
Self::AssignmentPropertyAccess {
name,
access,
default_init,
} => {
let mut buf = match name {
PropertyName::Literal(name) => {
format!(
" {} : {}",
interner.resolve_expect(name.sym()),
access.to_interned_string(interner)
)
}
PropertyName::Computed(node) => {
format!(
" [{}] : {}",
node.to_interned_string(interner),
access.to_interned_string(interner)
)
}
};
if let Some(init) = &default_init {
let _ = write!(buf, " = {}", init.to_interned_string(interner));
}
buf
}
Self::Pattern {
name,
pattern,
default_init,
} => {
let mut buf = match name {
PropertyName::Literal(name) => {
format!(
" {} : {}",
interner.resolve_expect(name.sym()),
pattern.to_interned_string(interner),
)
}
PropertyName::Computed(node) => {
format!(
" [{}] : {}",
node.to_interned_string(interner),
pattern.to_interned_string(interner),
)
}
};
if let Some(init) = default_init {
let _ = write!(buf, " = {}", init.to_interned_string(interner));
}
buf
}
}
}
}
impl VisitWith for ObjectPatternElement {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::SingleName {
name,
ident,
default_init,
} => {
visitor.visit_property_name(name)?;
visitor.visit_identifier(ident)?;
if let Some(expr) = default_init {
visitor.visit_expression(expr)
} else {
ControlFlow::Continue(())
}
}
Self::RestProperty { ident, .. } => visitor.visit_identifier(ident),
Self::AssignmentPropertyAccess {
name,
access,
default_init,
} => {
visitor.visit_property_name(name)?;
visitor.visit_property_access(access)?;
if let Some(expr) = default_init {
visitor.visit_expression(expr)
} else {
ControlFlow::Continue(())
}
}
Self::AssignmentRestPropertyAccess { access, .. } => {
visitor.visit_property_access(access)
}
Self::Pattern {
name,
pattern,
default_init,
} => {
visitor.visit_property_name(name)?;
visitor.visit_pattern(pattern)?;
if let Some(expr) = default_init {
visitor.visit_expression(expr)
} else {
ControlFlow::Continue(())
}
}
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::SingleName {
name,
ident,
default_init,
} => {
visitor.visit_property_name_mut(name)?;
visitor.visit_identifier_mut(ident)?;
if let Some(expr) = default_init {
visitor.visit_expression_mut(expr)
} else {
ControlFlow::Continue(())
}
}
Self::RestProperty { ident, .. } => visitor.visit_identifier_mut(ident),
Self::AssignmentPropertyAccess {
name,
access,
default_init,
} => {
visitor.visit_property_name_mut(name)?;
visitor.visit_property_access_mut(access)?;
if let Some(expr) = default_init {
visitor.visit_expression_mut(expr)
} else {
ControlFlow::Continue(())
}
}
Self::AssignmentRestPropertyAccess { access, .. } => {
visitor.visit_property_access_mut(access)
}
Self::Pattern {
name,
pattern,
default_init,
} => {
visitor.visit_property_name_mut(name)?;
visitor.visit_pattern_mut(pattern)?;
if let Some(expr) = default_init {
visitor.visit_expression_mut(expr)
} else {
ControlFlow::Continue(())
}
}
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ArrayPatternElement {
Elision,
SingleName {
ident: Identifier,
default_init: Option<Expression>,
},
PropertyAccess {
access: PropertyAccess,
default_init: Option<Expression>,
},
Pattern {
pattern: Pattern,
default_init: Option<Expression>,
},
SingleNameRest {
ident: Identifier,
},
PropertyAccessRest {
access: PropertyAccess,
},
PatternRest {
pattern: Pattern,
},
}
impl ToInternedString for ArrayPatternElement {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
Self::Elision => " ".to_owned(),
Self::SingleName {
ident,
default_init,
} => {
let mut buf = format!(" {}", interner.resolve_expect(ident.sym()));
if let Some(init) = default_init {
let _ = write!(buf, " = {}", init.to_interned_string(interner));
}
buf
}
Self::PropertyAccess {
access,
default_init,
} => {
let mut buf = format!(" {}", access.to_interned_string(interner));
if let Some(init) = default_init {
let _ = write!(buf, " = {}", init.to_interned_string(interner));
}
buf
}
Self::Pattern {
pattern,
default_init,
} => {
let mut buf = format!(" {}", pattern.to_interned_string(interner));
if let Some(init) = default_init {
let _ = write!(buf, " = {}", init.to_interned_string(interner));
}
buf
}
Self::SingleNameRest { ident } => {
format!(" ... {}", interner.resolve_expect(ident.sym()))
}
Self::PropertyAccessRest { access } => {
format!(" ... {}", access.to_interned_string(interner))
}
Self::PatternRest { pattern } => {
format!(" ... {}", pattern.to_interned_string(interner))
}
}
}
}
impl VisitWith for ArrayPatternElement {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::SingleName {
ident,
default_init,
} => {
visitor.visit_identifier(ident)?;
if let Some(expr) = default_init {
visitor.visit_expression(expr)
} else {
ControlFlow::Continue(())
}
}
Self::PropertyAccess {
access,
default_init,
} => {
visitor.visit_property_access(access)?;
if let Some(expr) = default_init {
visitor.visit_expression(expr)
} else {
ControlFlow::Continue(())
}
}
Self::PropertyAccessRest { access } => visitor.visit_property_access(access),
Self::Pattern {
pattern,
default_init,
} => {
visitor.visit_pattern(pattern)?;
if let Some(expr) = default_init {
visitor.visit_expression(expr)
} else {
ControlFlow::Continue(())
}
}
Self::SingleNameRest { ident } => visitor.visit_identifier(ident),
Self::PatternRest { pattern } => visitor.visit_pattern(pattern),
Self::Elision => {
ControlFlow::Continue(())
}
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::SingleName {
ident,
default_init,
} => {
visitor.visit_identifier_mut(ident)?;
if let Some(expr) = default_init {
visitor.visit_expression_mut(expr)
} else {
ControlFlow::Continue(())
}
}
Self::PropertyAccess {
access,
default_init,
} => {
visitor.visit_property_access_mut(access)?;
if let Some(expr) = default_init {
visitor.visit_expression_mut(expr)
} else {
ControlFlow::Continue(())
}
}
Self::PropertyAccessRest { access } => visitor.visit_property_access_mut(access),
Self::Pattern {
pattern,
default_init,
} => {
visitor.visit_pattern_mut(pattern)?;
if let Some(expr) = default_init {
visitor.visit_expression_mut(expr)
} else {
ControlFlow::Continue(())
}
}
Self::SingleNameRest { ident } => visitor.visit_identifier_mut(ident),
Self::PatternRest { pattern } => visitor.visit_pattern_mut(pattern),
Self::Elision => {
ControlFlow::Continue(())
}
}
}
}