use crate::syntax::ast::node::{
field::{GetConstField, GetField},
join_nodes,
object::PropertyName,
statement_list::StatementList,
ContainsSymbol, Identifier, Node,
};
use boa_interner::{Interner, Sym, ToInternedString};
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
pub mod arrow_function_decl;
pub mod async_function_decl;
pub mod async_function_expr;
pub mod async_generator_decl;
pub mod async_generator_expr;
pub mod class_decl;
pub mod function_decl;
pub mod function_expr;
pub mod generator_decl;
pub mod generator_expr;
pub use self::{
arrow_function_decl::ArrowFunctionDecl, async_function_decl::AsyncFunctionDecl,
async_function_expr::AsyncFunctionExpr, async_generator_decl::AsyncGeneratorDecl,
async_generator_expr::AsyncGeneratorExpr, function_decl::FunctionDecl,
function_expr::FunctionExpr,
};
#[cfg(test)]
mod tests;
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum DeclarationList {
Const(Box<[Declaration]>),
Let(Box<[Declaration]>),
Var(Box<[Declaration]>),
}
impl AsRef<[Declaration]> for DeclarationList {
fn as_ref(&self) -> &[Declaration] {
use DeclarationList::{Const, Let, Var};
match self {
Var(list) | Const(list) | Let(list) => list,
}
}
}
impl ToInternedString for DeclarationList {
fn to_interned_string(&self, interner: &Interner) -> String {
if self.as_ref().is_empty() {
String::new()
} else {
use DeclarationList::{Const, Let, Var};
format!(
"{} {}",
match &self {
Let(_) => "let",
Const(_) => "const",
Var(_) => "var",
},
join_nodes(interner, self.as_ref())
)
}
}
}
impl From<DeclarationList> for Node {
fn from(list: DeclarationList) -> Self {
use DeclarationList::{Const, Let, Var};
match &list {
Let(_) => Self::LetDeclList(list),
Const(_) => Self::ConstDeclList(list),
Var(_) => Self::VarDeclList(list),
}
}
}
impl From<Declaration> for Box<[Declaration]> {
fn from(d: Declaration) -> Self {
Box::new([d])
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Declaration {
Identifier {
ident: Identifier,
init: Option<Node>,
},
Pattern(DeclarationPattern),
}
impl ToInternedString for Declaration {
fn to_interned_string(&self, interner: &Interner) -> String {
match &self {
Self::Identifier { ident, init } => {
let mut buf = ident.to_interned_string(interner);
if let Some(ref init) = &init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
Self::Pattern(pattern) => pattern.to_interned_string(interner),
}
}
}
impl Declaration {
#[inline]
pub(in crate::syntax) fn new_with_identifier<N, I>(ident: N, init: I) -> Self
where
N: Into<Identifier>,
I: Into<Option<Node>>,
{
Self::Identifier {
ident: ident.into(),
init: init.into(),
}
}
#[inline]
pub(in crate::syntax) fn new_with_object_pattern<I>(
bindings: Vec<BindingPatternTypeObject>,
init: I,
) -> Self
where
I: Into<Option<Node>>,
{
Self::Pattern(DeclarationPattern::Object(DeclarationPatternObject::new(
bindings,
init.into(),
)))
}
#[inline]
pub(in crate::syntax) fn new_with_array_pattern<I>(
bindings: Vec<BindingPatternTypeArray>,
init: I,
) -> Self
where
I: Into<Option<Node>>,
{
Self::Pattern(DeclarationPattern::Array(DeclarationPatternArray::new(
bindings,
init.into(),
)))
}
#[inline]
pub(crate) fn init(&self) -> Option<&Node> {
match &self {
Self::Identifier { init, .. } => init.as_ref(),
Self::Pattern(pattern) => pattern.init(),
}
}
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Self::Identifier { init, .. } => {
if let Some(node) = init {
if node.contains(symbol) {
return true;
}
}
}
Self::Pattern(pattern) => {
if pattern.contains(symbol) {
return true;
}
}
}
false
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum DeclarationPattern {
Object(DeclarationPatternObject),
Array(DeclarationPatternArray),
}
impl ToInternedString for DeclarationPattern {
fn to_interned_string(&self, interner: &Interner) -> String {
match &self {
DeclarationPattern::Object(o) => o.to_interned_string(interner),
DeclarationPattern::Array(a) => a.to_interned_string(interner),
}
}
}
impl DeclarationPattern {
#[inline]
pub fn idents(&self) -> Vec<Sym> {
match &self {
DeclarationPattern::Object(pattern) => pattern.idents(),
DeclarationPattern::Array(pattern) => pattern.idents(),
}
}
#[inline]
pub fn init(&self) -> Option<&Node> {
match &self {
DeclarationPattern::Object(pattern) => pattern.init(),
DeclarationPattern::Array(pattern) => pattern.init(),
}
}
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
DeclarationPattern::Object(pattern) => {
if let Some(init) = pattern.init() {
if init.contains_arguments() {
return true;
}
}
for binding in pattern.bindings() {
match binding {
BindingPatternTypeObject::SingleName {
property_name,
default_init,
..
} => {
if let PropertyName::Computed(node) = property_name {
if node.contains_arguments() {
return true;
}
}
if let Some(init) = default_init {
if init.contains_arguments() {
return true;
}
}
}
BindingPatternTypeObject::AssignmentRestProperty {
get_const_field,
..
} => {
if get_const_field.obj().contains_arguments() {
return true;
}
}
BindingPatternTypeObject::BindingPattern {
ident,
pattern,
default_init,
} => {
if let PropertyName::Computed(node) = ident {
if node.contains_arguments() {
return true;
}
}
if pattern.contains_arguments() {
return true;
}
if let Some(init) = default_init {
if init.contains_arguments() {
return true;
}
}
}
_ => {}
}
}
}
DeclarationPattern::Array(pattern) => {
if let Some(init) = pattern.init() {
if init.contains_arguments() {
return true;
}
}
for binding in pattern.bindings() {
match binding {
BindingPatternTypeArray::SingleName {
default_init: Some(init),
..
} => {
if init.contains_arguments() {
return true;
}
}
BindingPatternTypeArray::GetField { get_field }
| BindingPatternTypeArray::GetFieldRest { get_field } => {
if get_field.obj().contains_arguments() {
return true;
}
if get_field.field().contains_arguments() {
return true;
}
}
BindingPatternTypeArray::GetConstField { get_const_field }
| BindingPatternTypeArray::GetConstFieldRest { get_const_field } => {
if get_const_field.obj().contains_arguments() {
return true;
}
}
BindingPatternTypeArray::BindingPattern { pattern }
| BindingPatternTypeArray::BindingPatternRest { pattern } => {
if pattern.contains_arguments() {
return true;
}
}
_ => {}
}
}
}
}
false
}
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
DeclarationPattern::Object(object) => {
if let Some(node) = object.init() {
if node.contains(symbol) {
return true;
}
}
for binding in &object.bindings {
match binding {
BindingPatternTypeObject::SingleName {
default_init: Some(node),
..
} => {
if node.contains(symbol) {
return true;
}
}
BindingPatternTypeObject::AssignmentRestProperty {
get_const_field,
..
} => {
if get_const_field.obj().contains(symbol) {
return true;
}
}
BindingPatternTypeObject::BindingPattern {
pattern,
default_init,
..
} => {
if let Some(node) = default_init {
if node.contains(symbol) {
return true;
}
}
if pattern.contains(symbol) {
return true;
}
}
_ => {}
}
}
}
DeclarationPattern::Array(array) => {
if let Some(node) = array.init() {
if node.contains(symbol) {
return true;
}
}
for binding in array.bindings() {
match binding {
BindingPatternTypeArray::SingleName {
default_init: Some(node),
..
} => {
if node.contains(symbol) {
return true;
}
}
BindingPatternTypeArray::GetField { get_field }
| BindingPatternTypeArray::GetFieldRest { get_field } => {
if get_field.obj().contains(symbol)
|| get_field.field().contains(symbol)
{
return true;
}
}
BindingPatternTypeArray::GetConstField { get_const_field }
| BindingPatternTypeArray::GetConstFieldRest { get_const_field } => {
if get_const_field.obj().contains(symbol) {
return true;
}
}
BindingPatternTypeArray::BindingPattern { pattern }
| BindingPatternTypeArray::BindingPatternRest { pattern } => {
if pattern.contains(symbol) {
return true;
}
}
_ => {}
}
}
}
}
false
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct DeclarationPatternObject {
bindings: Vec<BindingPatternTypeObject>,
pub(crate) init: Option<Node>,
}
impl ToInternedString for DeclarationPatternObject {
fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = "{".to_owned();
for (i, binding) in self.bindings.iter().enumerate() {
let binding = binding.to_interned_string(interner);
let str = if i == self.bindings.len() - 1 {
format!("{binding} ")
} else {
format!("{binding},")
};
buf.push_str(&str);
}
buf.push('}');
if let Some(ref init) = self.init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
}
impl DeclarationPatternObject {
#[inline]
pub(in crate::syntax) fn new(
bindings: Vec<BindingPatternTypeObject>,
init: Option<Node>,
) -> Self {
Self { bindings, init }
}
#[inline]
pub(crate) fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
#[inline]
pub(crate) fn bindings(&self) -> &Vec<BindingPatternTypeObject> {
&self.bindings
}
#[inline]
pub(crate) fn has_rest(&self) -> bool {
matches!(
self.bindings.last(),
Some(BindingPatternTypeObject::RestProperty { .. })
)
}
#[inline]
pub(crate) fn idents(&self) -> Vec<Sym> {
let mut idents = Vec::new();
for binding in &self.bindings {
use BindingPatternTypeObject::{
AssignmentRestProperty, BindingPattern, Empty, RestProperty, SingleName,
};
match binding {
Empty | AssignmentRestProperty { .. } => {}
SingleName {
ident,
property_name: _,
default_init: _,
} => {
idents.push(*ident);
}
RestProperty {
ident: property_name,
excluded_keys: _,
} => {
idents.push(*property_name);
}
BindingPatternTypeObject::AssignmentGetConstField { property_name, .. }
| BindingPatternTypeObject::AssignmentGetField { property_name, .. } => {
if let Some(name) = property_name.literal() {
idents.push(name);
}
}
BindingPattern {
ident: _,
pattern,
default_init: _,
} => {
for ident in pattern.idents() {
idents.push(ident);
}
}
}
}
idents
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct DeclarationPatternArray {
bindings: Vec<BindingPatternTypeArray>,
pub(crate) init: Option<Node>,
}
impl ToInternedString for DeclarationPatternArray {
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 {
BindingPatternTypeArray::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(']');
if let Some(ref init) = self.init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
}
impl DeclarationPatternArray {
#[inline]
pub(in crate::syntax) fn new(
bindings: Vec<BindingPatternTypeArray>,
init: Option<Node>,
) -> Self {
Self { bindings, init }
}
#[inline]
pub(crate) fn init(&self) -> Option<&Node> {
self.init.as_ref()
}
#[inline]
pub(crate) fn bindings(&self) -> &Vec<BindingPatternTypeArray> {
&self.bindings
}
#[inline]
pub(crate) fn idents(&self) -> Vec<Sym> {
let mut idents = Vec::new();
for binding in &self.bindings {
use BindingPatternTypeArray::{
BindingPattern, BindingPatternRest, Elision, Empty, GetConstField,
GetConstFieldRest, GetField, GetFieldRest, SingleName, SingleNameRest,
};
match binding {
Empty
| Elision
| GetField { .. }
| GetConstField { .. }
| GetFieldRest { .. }
| GetConstFieldRest { .. } => {}
SingleName {
ident,
default_init: _,
} => {
idents.push(*ident);
}
BindingPattern { pattern } | BindingPatternRest { pattern } => {
let mut i = pattern.idents();
idents.append(&mut i);
}
SingleNameRest { ident } => idents.push(*ident),
}
}
idents
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum BindingPatternTypeObject {
Empty,
SingleName {
ident: Sym,
property_name: PropertyName,
default_init: Option<Node>,
},
RestProperty { ident: Sym, excluded_keys: Vec<Sym> },
AssignmentRestProperty {
get_const_field: GetConstField,
excluded_keys: Vec<Sym>,
},
AssignmentGetConstField {
property_name: PropertyName,
get_const_field: GetConstField,
default_init: Option<Node>,
},
AssignmentGetField {
property_name: PropertyName,
get_field: GetField,
default_init: Option<Node>,
},
BindingPattern {
ident: PropertyName,
pattern: DeclarationPattern,
default_init: Option<Node>,
},
}
impl ToInternedString for BindingPatternTypeObject {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
Self::Empty => String::new(),
Self::SingleName {
ident,
property_name,
default_init,
} => {
let mut buf = match property_name {
PropertyName::Literal(name) if *name == *ident => {
format!(" {}", interner.resolve_expect(*ident))
}
PropertyName::Literal(name) => {
format!(
" {} : {}",
interner.resolve_expect(*name),
interner.resolve_expect(*ident)
)
}
PropertyName::Computed(node) => {
format!(
" [{}] : {}",
node.to_interned_string(interner),
interner.resolve_expect(*ident)
)
}
};
if let Some(ref init) = default_init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
Self::RestProperty {
ident: property_name,
excluded_keys: _,
} => {
format!(" ... {}", interner.resolve_expect(*property_name))
}
Self::AssignmentRestProperty {
get_const_field, ..
} => {
format!(" ... {}", get_const_field.to_interned_string(interner))
}
Self::AssignmentGetConstField {
property_name,
get_const_field,
default_init,
} => {
let mut buf = match property_name {
PropertyName::Literal(name) => {
format!(
" {} : {}",
interner.resolve_expect(*name),
get_const_field.to_interned_string(interner)
)
}
PropertyName::Computed(node) => {
format!(
" [{}] : {}",
node.to_interned_string(interner),
get_const_field.to_interned_string(interner)
)
}
};
if let Some(init) = &default_init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
Self::AssignmentGetField {
property_name,
get_field,
default_init,
} => {
let mut buf = match property_name {
PropertyName::Literal(name) => {
format!(
" {} : {}",
interner.resolve_expect(*name),
get_field.to_interned_string(interner)
)
}
PropertyName::Computed(node) => {
format!(
" [{}] : {}",
node.to_interned_string(interner),
get_field.to_interned_string(interner)
)
}
};
if let Some(init) = &default_init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
Self::BindingPattern {
ident: property_name,
pattern,
default_init,
} => {
let mut buf = match property_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
}
}
}
}
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum BindingPatternTypeArray {
Empty,
Elision,
SingleName {
ident: Sym,
default_init: Option<Node>,
},
GetField { get_field: GetField },
GetConstField { get_const_field: GetConstField },
BindingPattern { pattern: DeclarationPattern },
SingleNameRest { ident: Sym },
GetFieldRest { get_field: GetField },
GetConstFieldRest { get_const_field: GetConstField },
BindingPatternRest { pattern: DeclarationPattern },
}
impl ToInternedString for BindingPatternTypeArray {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
Self::Empty => String::new(),
Self::Elision => " ".to_owned(),
Self::SingleName {
ident,
default_init,
} => {
let mut buf = format!(" {}", interner.resolve_expect(*ident));
if let Some(ref init) = default_init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
Self::GetField { get_field } => {
format!(" {}", get_field.to_interned_string(interner))
}
Self::GetConstField { get_const_field } => {
format!(" {}", get_const_field.to_interned_string(interner))
}
Self::BindingPattern { pattern } => {
format!(" {}", pattern.to_interned_string(interner))
}
Self::SingleNameRest { ident } => {
format!(" ... {}", interner.resolve_expect(*ident))
}
Self::GetFieldRest { get_field } => {
format!(" ... {}", get_field.to_interned_string(interner))
}
Self::GetConstFieldRest { get_const_field } => {
format!(" ... {}", get_const_field.to_interned_string(interner))
}
Self::BindingPatternRest { pattern } => {
format!(" ... {}", pattern.to_interned_string(interner))
}
}
}
}
pub(in crate::syntax::ast::node) fn block_to_string(
body: &StatementList,
interner: &Interner,
indentation: usize,
) -> String {
if body.items().is_empty() {
"{}".to_owned()
} else {
format!(
"{{\n{}{}}}",
body.to_indented_string(interner, indentation + 1),
" ".repeat(indentation)
)
}
}