//! Statement list node. use super::Declaration; use crate::{ statement::Statement, try_break, visitor::{VisitWith, Visitor, VisitorMut}, }; use boa_interner::{Interner, ToIndentedString}; use core::ops::ControlFlow; use std::cmp::Ordering; /// An item inside a [`StatementList`] Parse Node, as defined by the [spec]. /// /// Items in a `StatementList` can be either [`Declaration`]s (functions, classes, let/const declarations) /// or [`Statement`]s (if, while, var statement). /// /// [spec]: https://tc39.es/ecma262/#prod-StatementListItem #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq)] pub enum StatementListItem { /// See [`Statement`]. Statement(Statement), /// See [`Declaration`]. Declaration(Declaration), } impl StatementListItem { /// Returns a node ordering based on the hoistability of each statement. #[must_use] pub const fn hoistable_order(a: &Self, b: &Self) -> Ordering { match (a, b) { ( _, Self::Declaration( Declaration::Function(_) | Declaration::Generator(_) | Declaration::AsyncFunction(_) | Declaration::AsyncGenerator(_), ), ) => Ordering::Greater, ( Self::Declaration( Declaration::Function(_) | Declaration::Generator(_) | Declaration::AsyncFunction(_) | Declaration::AsyncGenerator(_), ), _, ) => Ordering::Less, (_, _) => Ordering::Equal, } } } impl ToIndentedString for StatementListItem { /// Creates a string of the value of the node with the given indentation. For example, an /// indent level of 2 would produce this: /// /// ```js /// function hello() { /// console.log("hello"); /// } /// hello(); /// a = 2; /// ``` fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = " ".repeat(indentation); match self { Self::Statement(stmt) => { buf.push_str(&stmt.to_no_indent_string(interner, indentation)); } Self::Declaration(decl) => { buf.push_str(&decl.to_indented_string(interner, indentation)); } } buf } } impl From for StatementListItem { #[inline] fn from(stmt: Statement) -> Self { Self::Statement(stmt) } } impl From for StatementListItem { #[inline] fn from(decl: Declaration) -> Self { Self::Declaration(decl) } } impl VisitWith for StatementListItem { fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow where V: Visitor<'a>, { match self { Self::Statement(statement) => visitor.visit_statement(statement), Self::Declaration(declaration) => visitor.visit_declaration(declaration), } } fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow where V: VisitorMut<'a>, { match self { Self::Statement(statement) => visitor.visit_statement_mut(statement), Self::Declaration(declaration) => visitor.visit_declaration_mut(declaration), } } } /// List of statements. /// /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-StatementList #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct StatementList { statements: Box<[StatementListItem]>, strict: bool, } impl StatementList { /// Creates a new `StatementList` AST node. #[must_use] pub fn new(statements: S, strict: bool) -> Self where S: Into>, { Self { statements: statements.into(), strict, } } /// Gets the list of statements. #[inline] #[must_use] pub const fn statements(&self) -> &[StatementListItem] { &self.statements } /// Get the strict mode. #[inline] #[must_use] pub const fn strict(&self) -> bool { self.strict } } impl From> for StatementList { #[inline] fn from(stm: Box<[StatementListItem]>) -> Self { Self { statements: stm, strict: false, } } } impl From> for StatementList { #[inline] fn from(stm: Vec) -> Self { Self { statements: stm.into(), strict: false, } } } impl ToIndentedString for StatementList { fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = String::new(); // Print statements for item in self.statements.iter() { // We rely on the node to add the correct indent. buf.push_str(&item.to_indented_string(interner, indentation)); buf.push('\n'); } buf } } impl VisitWith for StatementList { fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow where V: Visitor<'a>, { for statement in self.statements.iter() { try_break!(visitor.visit_statement_list_item(statement)); } ControlFlow::Continue(()) } fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow where V: VisitorMut<'a>, { for statement in self.statements.iter_mut() { try_break!(visitor.visit_statement_list_item_mut(statement)); } ControlFlow::Continue(()) } } #[cfg(feature = "arbitrary")] impl<'a> arbitrary::Arbitrary<'a> for StatementList { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self { statements: u.arbitrary()?, strict: false, // disable strictness; this is *not* in source data }) } }