mirror of https://github.com/boa-dev/boa.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
814 lines
29 KiB
814 lines
29 KiB
//! A pattern binding or assignment node. |
|
//! |
|
//! A [`Pattern`] Corresponds to the [`BindingPattern`][spec1] and the [`AssignmentPattern`][spec2] |
|
//! nodes, each of which is used in different situations and have slightly different grammars. |
|
//! For example, a variable declaration combined with a destructuring expression is a `BindingPattern`: |
|
//! |
|
//! ```Javascript |
|
//! const obj = { a: 1, b: 2 }; |
|
//! const { a, b } = obj; // BindingPattern |
|
//! ``` |
|
//! |
|
//! On the other hand, a simple destructuring expression with already declared variables is called |
|
//! an `AssignmentPattern`: |
|
//! |
|
//! ```Javascript |
|
//! let a = 1; |
|
//! let b = 3; |
|
//! [a, b] = [b, a]; // AssignmentPattern |
|
//! ``` |
|
//! |
|
//! [spec1]: https://tc39.es/ecma262/#prod-BindingPattern |
|
//! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern |
|
//! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment |
|
|
|
use crate::{ |
|
expression::{access::PropertyAccess, Identifier}, |
|
property::PropertyName, |
|
try_break, |
|
visitor::{VisitWith, Visitor, VisitorMut}, |
|
Expression, |
|
}; |
|
use boa_interner::{Interner, ToInternedString}; |
|
use core::ops::ControlFlow; |
|
|
|
/// An object or array pattern binding or assignment. |
|
/// |
|
/// 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 enum Pattern { |
|
/// An object pattern (`let {a, b, c} = object`). |
|
Object(ObjectPattern), |
|
/// An array pattern (`[a, b, c] = array`). |
|
Array(ArrayPattern), |
|
} |
|
|
|
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 From<Vec<ObjectPatternElement>> for Pattern { |
|
fn from(elements: Vec<ObjectPatternElement>) -> Self { |
|
ObjectPattern::new(elements.into()).into() |
|
} |
|
} |
|
impl From<Vec<ArrayPatternElement>> for Pattern { |
|
fn from(elements: Vec<ArrayPatternElement>) -> Self { |
|
ArrayPattern::new(elements.into()).into() |
|
} |
|
} |
|
|
|
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), |
|
} |
|
} |
|
} |
|
|
|
/// An object binding or assignment pattern. |
|
/// |
|
/// Corresponds to the [`ObjectBindingPattern`][spec1] and the [`ObjectAssignmentPattern`][spec2] |
|
/// Parse Nodes. |
|
/// |
|
/// For more information on what is a valid binding in an object pattern, see [`ObjectPatternElement`]. |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern |
|
/// [spec2]: https://tc39.es/ecma262/#prod-ObjectAssignmentPattern |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] |
|
#[derive(Clone, Debug, PartialEq)] |
|
pub struct ObjectPattern(Box<[ObjectPatternElement]>); |
|
|
|
impl From<Vec<ObjectPatternElement>> for ObjectPattern { |
|
fn from(elements: Vec<ObjectPatternElement>) -> Self { |
|
Self(elements.into()) |
|
} |
|
} |
|
|
|
impl ToInternedString for ObjectPattern { |
|
fn to_interned_string(&self, interner: &Interner) -> String { |
|
let mut buf = "{".to_owned(); |
|
for (i, binding) in self.0.iter().enumerate() { |
|
let binding = binding.to_interned_string(interner); |
|
let str = if i == self.0.len() - 1 { |
|
format!("{binding} ") |
|
} else { |
|
format!("{binding},") |
|
}; |
|
|
|
buf.push_str(&str); |
|
} |
|
if self.0.is_empty() { |
|
buf.push(' '); |
|
} |
|
buf.push('}'); |
|
buf |
|
} |
|
} |
|
|
|
impl ObjectPattern { |
|
/// Creates a new object binding pattern. |
|
#[inline] |
|
#[must_use] |
|
pub fn new(bindings: Box<[ObjectPatternElement]>) -> Self { |
|
Self(bindings) |
|
} |
|
|
|
/// Gets the bindings for the object binding pattern. |
|
#[inline] |
|
#[must_use] |
|
pub const fn bindings(&self) -> &[ObjectPatternElement] { |
|
&self.0 |
|
} |
|
|
|
/// Returns true if the object binding pattern has a rest element. |
|
#[inline] |
|
#[must_use] |
|
pub const fn has_rest(&self) -> bool { |
|
matches!( |
|
self.0.last(), |
|
Some(ObjectPatternElement::RestProperty { .. }) |
|
) |
|
} |
|
} |
|
|
|
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.0 { |
|
try_break!(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.0 { |
|
try_break!(visitor.visit_object_pattern_element_mut(elem)); |
|
} |
|
ControlFlow::Continue(()) |
|
} |
|
} |
|
|
|
/// An array binding or assignment pattern. |
|
/// |
|
/// Corresponds to the [`ArrayBindingPattern`][spec1] and the [`ArrayAssignmentPattern`][spec2] |
|
/// Parse Nodes. |
|
/// |
|
/// For more information on what is a valid binding in an array pattern, see [`ArrayPatternElement`]. |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern |
|
/// [spec2]: https://tc39.es/ecma262/#prod-ArrayAssignmentPattern |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] |
|
#[derive(Clone, Debug, PartialEq)] |
|
pub struct ArrayPattern(Box<[ArrayPatternElement]>); |
|
|
|
impl From<Vec<ArrayPatternElement>> for ArrayPattern { |
|
fn from(elements: Vec<ArrayPatternElement>) -> Self { |
|
Self(elements.into()) |
|
} |
|
} |
|
|
|
impl ToInternedString for ArrayPattern { |
|
fn to_interned_string(&self, interner: &Interner) -> String { |
|
let mut buf = "[".to_owned(); |
|
for (i, binding) in self.0.iter().enumerate() { |
|
if i == self.0.len() - 1 { |
|
match binding { |
|
ArrayPatternElement::Elision => { |
|
buf.push_str(&format!("{}, ", binding.to_interned_string(interner))); |
|
} |
|
_ => buf.push_str(&format!("{} ", binding.to_interned_string(interner))), |
|
} |
|
} else { |
|
buf.push_str(&format!("{},", binding.to_interned_string(interner))); |
|
} |
|
} |
|
buf.push(']'); |
|
buf |
|
} |
|
} |
|
|
|
impl ArrayPattern { |
|
/// Creates a new array binding pattern. |
|
#[inline] |
|
#[must_use] |
|
pub fn new(bindings: Box<[ArrayPatternElement]>) -> Self { |
|
Self(bindings) |
|
} |
|
|
|
/// Gets the bindings for the array binding pattern. |
|
#[inline] |
|
#[must_use] |
|
pub const fn bindings(&self) -> &[ArrayPatternElement] { |
|
&self.0 |
|
} |
|
} |
|
|
|
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.0 { |
|
try_break!(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.0 { |
|
try_break!(visitor.visit_array_pattern_element_mut(elem)); |
|
} |
|
ControlFlow::Continue(()) |
|
} |
|
} |
|
|
|
/// The different types of bindings that an [`ObjectPattern`] may contain. |
|
/// |
|
/// Corresponds to the [`BindingProperty`][spec1] and the [`AssignmentProperty`][spec2] nodes. |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty |
|
/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentProperty |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] |
|
#[derive(Clone, Debug, PartialEq)] |
|
pub enum ObjectPatternElement { |
|
/// SingleName represents one of the following properties: |
|
/// |
|
/// - `SingleName` with an identifier and an optional default initializer. |
|
/// - `BindingProperty` with an property name and a `SingleNameBinding` as the `BindingElement`. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] |
|
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec2] |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding |
|
/// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty |
|
SingleName { |
|
/// The identifier name of the property to be destructured. |
|
name: PropertyName, |
|
/// The variable name where the property value will be stored. |
|
ident: Identifier, |
|
/// An optional default value for the variable, in case the property doesn't exist. |
|
default_init: Option<Expression>, |
|
}, |
|
|
|
/// RestProperty represents a `BindingRestProperty` with an identifier. |
|
/// |
|
/// It also includes a list of the property keys that should be excluded from the rest, |
|
/// because they where already assigned. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1] |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty |
|
RestProperty { |
|
/// The variable name where the unassigned properties will be stored. |
|
ident: Identifier, |
|
/// A list of the excluded property keys that were already destructured. |
|
excluded_keys: Vec<Identifier>, |
|
}, |
|
|
|
/// AssignmentGetField represents an AssignmentProperty with an expression field member expression AssignmentElement. |
|
/// |
|
/// Note: According to the spec this is not part of an ObjectBindingPattern. |
|
/// This is only used when a object literal is used to cover an AssignmentPattern. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty |
|
AssignmentPropertyAccess { |
|
/// The identifier name of the property to be destructured. |
|
name: PropertyName, |
|
/// The property access where the property value will be destructured. |
|
access: PropertyAccess, |
|
/// An optional default value for the variable, in case the property doesn't exist. |
|
default_init: Option<Expression>, |
|
}, |
|
|
|
/// AssignmentRestProperty represents a rest property with a DestructuringAssignmentTarget. |
|
/// |
|
/// Note: According to the spec this is not part of an ObjectBindingPattern. |
|
/// This is only used when a object literal is used to cover an AssignmentPattern. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentRestProperty |
|
AssignmentRestPropertyAccess { |
|
/// The property access where the unassigned properties will be stored. |
|
access: PropertyAccess, |
|
/// A list of the excluded property keys that were already destructured. |
|
excluded_keys: Vec<Identifier>, |
|
}, |
|
|
|
/// Pattern represents a property with a `Pattern` as the element. |
|
/// |
|
/// Additionally to the identifier of the new property and the nested pattern, |
|
/// this may also include an optional default initializer. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec1] |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty |
|
Pattern { |
|
/// The identifier name of the property to be destructured. |
|
name: PropertyName, |
|
/// The pattern where the property value will be destructured. |
|
pattern: Pattern, |
|
/// An optional default value for the variable, in case the property doesn't exist. |
|
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), |
|
interner.resolve_expect(ident.sym()) |
|
) |
|
} |
|
PropertyName::Computed(node) => { |
|
format!( |
|
" [{}] : {}", |
|
node.to_interned_string(interner), |
|
interner.resolve_expect(ident.sym()) |
|
) |
|
} |
|
}; |
|
if let Some(ref init) = default_init { |
|
buf.push_str(&format!(" = {}", init.to_interned_string(interner))); |
|
} |
|
buf |
|
} |
|
Self::RestProperty { |
|
ident, |
|
excluded_keys: _, |
|
} => { |
|
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), |
|
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 { |
|
buf.push_str(&format!(" = {}", 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), |
|
pattern.to_interned_string(interner), |
|
) |
|
} |
|
PropertyName::Computed(node) => { |
|
format!( |
|
" [{}] : {}", |
|
node.to_interned_string(interner), |
|
pattern.to_interned_string(interner), |
|
) |
|
} |
|
}; |
|
if let Some(ref init) = default_init { |
|
buf.push_str(&format!(" = {}", 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, |
|
} => { |
|
try_break!(visitor.visit_property_name(name)); |
|
try_break!(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, |
|
} => { |
|
try_break!(visitor.visit_property_name(name)); |
|
try_break!(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, |
|
} => { |
|
try_break!(visitor.visit_property_name(name)); |
|
try_break!(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, |
|
} => { |
|
try_break!(visitor.visit_property_name_mut(name)); |
|
try_break!(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, |
|
} => { |
|
try_break!(visitor.visit_property_name_mut(name)); |
|
try_break!(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, |
|
} => { |
|
try_break!(visitor.visit_property_name_mut(name)); |
|
try_break!(visitor.visit_pattern_mut(pattern)); |
|
if let Some(expr) = default_init { |
|
visitor.visit_expression_mut(expr) |
|
} else { |
|
ControlFlow::Continue(()) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
/// The different types of bindings that an array binding pattern may contain. |
|
/// |
|
/// Corresponds to the [`BindingElement`][spec1] and the [`AssignmentElement`][spec2] nodes. |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-BindingElement |
|
/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentElement |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] |
|
#[derive(Clone, Debug, PartialEq)] |
|
pub enum ArrayPatternElement { |
|
/// Elision represents the elision of an item in the array binding pattern. |
|
/// |
|
/// An `Elision` may occur at multiple points in the pattern and may be multiple elisions. |
|
/// This variant strictly represents one elision. If there are multiple, this should be used multiple times. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference: 13.2.4 Array Initializer - Elision][spec1] |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-Elision |
|
Elision, |
|
|
|
/// SingleName represents a `SingleName` with an identifier and an optional default initializer. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding |
|
SingleName { |
|
/// The variable name where the index element will be stored. |
|
ident: Identifier, |
|
/// An optional default value for the variable, in case the index element doesn't exist. |
|
default_init: Option<Expression>, |
|
}, |
|
|
|
/// PropertyAccess represents a binding with a property accessor. |
|
/// |
|
/// Note: According to the spec this is not part of an ArrayBindingPattern. |
|
/// This is only used when a array literal is used as the left-hand-side of an assignment expression. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression |
|
PropertyAccess { |
|
/// The property access where the index element will be stored. |
|
access: PropertyAccess, |
|
/// An optional default value for the variable, in case the index element doesn't exist. |
|
default_init: Option<Expression>, |
|
}, |
|
|
|
/// Pattern represents a `Pattern` in an `Element` of an array pattern. |
|
/// |
|
/// The pattern and the optional default initializer are both stored in the DeclarationPattern. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1] |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-BindingElement |
|
Pattern { |
|
/// The pattern where the index element will be stored. |
|
pattern: Pattern, |
|
/// An optional default value for the pattern, in case the index element doesn't exist. |
|
default_init: Option<Expression>, |
|
}, |
|
|
|
/// SingleNameRest represents a `BindingIdentifier` in a `BindingRestElement` of an array pattern. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement |
|
SingleNameRest { |
|
/// The variable where the unassigned index elements will be stored. |
|
ident: Identifier, |
|
}, |
|
|
|
/// PropertyAccess represents a rest (spread operator) with a property accessor. |
|
/// |
|
/// Note: According to the spec this is not part of an ArrayBindingPattern. |
|
/// This is only used when a array literal is used as the left-hand-side of an assignment expression. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference][spec] |
|
/// |
|
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression |
|
PropertyAccessRest { |
|
/// The property access where the unassigned index elements will be stored. |
|
access: PropertyAccess, |
|
}, |
|
|
|
/// PatternRest represents a `Pattern` in a `RestElement` of an array pattern. |
|
/// |
|
/// More information: |
|
/// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] |
|
/// |
|
/// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement |
|
PatternRest { |
|
/// The pattern where the unassigned index elements will be stored. |
|
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(ref init) = default_init { |
|
buf.push_str(&format!(" = {}", 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 { |
|
buf.push_str(&format!(" = {}", 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 { |
|
buf.push_str(&format!(" = {}", 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, |
|
} => { |
|
try_break!(visitor.visit_identifier(ident)); |
|
if let Some(expr) = default_init { |
|
visitor.visit_expression(expr) |
|
} else { |
|
ControlFlow::Continue(()) |
|
} |
|
} |
|
Self::PropertyAccess { |
|
access, |
|
default_init, |
|
} => { |
|
try_break!(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, |
|
} => { |
|
try_break!(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 => { |
|
// special case to be handled by user |
|
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, |
|
} => { |
|
try_break!(visitor.visit_identifier_mut(ident)); |
|
if let Some(expr) = default_init { |
|
visitor.visit_expression_mut(expr) |
|
} else { |
|
ControlFlow::Continue(()) |
|
} |
|
} |
|
Self::PropertyAccess { |
|
access, |
|
default_init, |
|
} => { |
|
try_break!(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, |
|
} => { |
|
try_break!(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 => { |
|
// special case to be handled by user |
|
ControlFlow::Continue(()) |
|
} |
|
} |
|
} |
|
}
|
|
|