feat: add dependency

This commit is contained in:
2023-01-20 22:36:19 +08:00
parent 68e8d103b4
commit cf8e579f27
644 changed files with 150099 additions and 14 deletions

View File

@@ -0,0 +1,79 @@
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::Statement;
/// The `break` statement terminates the current loop, switch, or label statement and transfers
/// program control to the statement following the terminated statement.
///
/// The break statement includes an optional label that allows the program to break out of a
/// labeled statement. The break statement needs to be nested within the referenced label. The
/// labeled statement can be any block statement; it does not have to be preceded by a loop
/// statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Break {
label: Option<Sym>,
}
impl Break {
/// Creates a `Break` AST node.
#[must_use]
pub const fn new(label: Option<Sym>) -> Self {
Self { label }
}
/// Gets the label of the break statement, if any.
#[must_use]
pub const fn label(&self) -> Option<Sym> {
self.label
}
}
impl ToInternedString for Break {
fn to_interned_string(&self, interner: &Interner) -> String {
self.label.map_or_else(
|| "break".to_owned(),
|label| format!("break {}", interner.resolve_expect(label)),
)
}
}
impl From<Break> for Statement {
fn from(break_smt: Break) -> Self {
Self::Break(break_smt)
}
}
impl VisitWith for Break {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
if let Some(sym) = &self.label {
visitor.visit_sym(sym)
} else {
ControlFlow::Continue(())
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
if let Some(sym) = &mut self.label {
visitor.visit_sym_mut(sym)
} else {
ControlFlow::Continue(())
}
}
}

View File

@@ -0,0 +1,77 @@
use crate::statement::Statement;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
/// The `continue` statement terminates execution of the statements in the current iteration of
/// the current or labeled loop, and continues execution of the loop with the next iteration.
///
/// The continue statement can include an optional label that allows the program to jump to the
/// next iteration of a labeled loop statement instead of the current loop. In this case, the
/// continue statement needs to be nested within this labeled statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Continue {
label: Option<Sym>,
}
impl Continue {
/// Creates a `Continue` AST node.
#[must_use]
pub const fn new(label: Option<Sym>) -> Self {
Self { label }
}
/// Gets the label of this `Continue` statement.
#[must_use]
pub const fn label(&self) -> Option<Sym> {
self.label
}
}
impl ToInternedString for Continue {
fn to_interned_string(&self, interner: &Interner) -> String {
self.label.map_or_else(
|| "continue".to_owned(),
|label| format!("continue {}", interner.resolve_expect(label)),
)
}
}
impl From<Continue> for Statement {
fn from(cont: Continue) -> Self {
Self::Continue(cont)
}
}
impl VisitWith for Continue {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
if let Some(sym) = &self.label {
visitor.visit_sym(sym)
} else {
ControlFlow::Continue(())
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
if let Some(sym) = &mut self.label {
visitor.visit_sym_mut(sym)
} else {
ControlFlow::Continue(())
}
}
}

View File

@@ -0,0 +1,87 @@
use crate::{
expression::Expression,
statement::Statement,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
/// The `do...while` statement creates a loop that executes a specified statement until the
/// test condition evaluates to false.
///
/// The condition is evaluated after executing the statement, resulting in the specified
/// statement executing at least once.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct DoWhileLoop {
body: Box<Statement>,
condition: Expression,
}
impl DoWhileLoop {
/// Gets the body of the do-while loop.
#[inline]
#[must_use]
pub const fn body(&self) -> &Statement {
&self.body
}
/// Gets the condition of the do-while loop.
#[inline]
#[must_use]
pub const fn cond(&self) -> &Expression {
&self.condition
}
/// Creates a `DoWhileLoop` AST node.
#[inline]
#[must_use]
pub fn new(body: Statement, condition: Expression) -> Self {
Self {
body: body.into(),
condition,
}
}
}
impl ToIndentedString for DoWhileLoop {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
"do {} while ({})",
self.body().to_indented_string(interner, indentation),
self.cond().to_interned_string(interner)
)
}
}
impl From<DoWhileLoop> for Statement {
fn from(do_while: DoWhileLoop) -> Self {
Self::DoWhileLoop(do_while)
}
}
impl VisitWith for DoWhileLoop {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_statement(&self.body));
visitor.visit_expression(&self.condition)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_statement_mut(&mut self.body));
visitor.visit_expression_mut(&mut self.condition)
}
}

View File

@@ -0,0 +1,98 @@
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
expression::Expression,
statement::{iteration::IterableLoopInitializer, Statement},
};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
/// A `for...in` loop statement, as defined by the [spec].
///
/// [`for...in`][forin] statements loop over all enumerable string properties of an object, including
/// inherited properties.
///
/// [forin]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForInLoop {
initializer: IterableLoopInitializer,
target: Expression,
body: Box<Statement>,
}
impl ForInLoop {
/// Creates a new `ForInLoop`.
#[inline]
#[must_use]
pub fn new(initializer: IterableLoopInitializer, target: Expression, body: Statement) -> Self {
Self {
initializer,
target,
body: body.into(),
}
}
/// Gets the initializer of the for...in loop.
#[inline]
#[must_use]
pub const fn initializer(&self) -> &IterableLoopInitializer {
&self.initializer
}
/// Gets the target object of the for...in loop.
#[inline]
#[must_use]
pub const fn target(&self) -> &Expression {
&self.target
}
/// Gets the body of the for...in loop.
#[inline]
#[must_use]
pub const fn body(&self) -> &Statement {
&self.body
}
}
impl ToIndentedString for ForInLoop {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = format!(
"for ({} in {}) ",
self.initializer.to_interned_string(interner),
self.target.to_interned_string(interner)
);
buf.push_str(&self.body().to_indented_string(interner, indentation));
buf
}
}
impl From<ForInLoop> for Statement {
#[inline]
fn from(for_in: ForInLoop) -> Self {
Self::ForInLoop(for_in)
}
}
impl VisitWith for ForInLoop {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_iterable_loop_initializer(&self.initializer));
try_break!(visitor.visit_expression(&self.target));
visitor.visit_statement(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_iterable_loop_initializer_mut(&mut self.initializer));
try_break!(visitor.visit_expression_mut(&mut self.target));
visitor.visit_statement_mut(&mut self.body)
}
}

View File

@@ -0,0 +1,263 @@
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
declaration::{LexicalDeclaration, VarDeclaration},
statement::Statement,
Expression,
};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
/// The `for` statement creates a loop that consists of three optional expressions.
///
/// A [`for`][mdn] loop repeats until a specified condition evaluates to `false`.
/// The JavaScript for loop is similar to the Java and C for loop.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForLoop {
#[cfg_attr(feature = "serde", serde(flatten))]
inner: Box<InnerForLoop>,
}
impl ForLoop {
/// Creates a new for loop AST node.
#[inline]
#[must_use]
pub fn new(
init: Option<ForLoopInitializer>,
condition: Option<Expression>,
final_expr: Option<Expression>,
body: Statement,
) -> Self {
Self {
inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),
}
}
/// Gets the initialization node.
#[inline]
#[must_use]
pub const fn init(&self) -> Option<&ForLoopInitializer> {
self.inner.init()
}
/// Gets the loop condition node.
#[inline]
#[must_use]
pub const fn condition(&self) -> Option<&Expression> {
self.inner.condition()
}
/// Gets the final expression node.
#[inline]
#[must_use]
pub const fn final_expr(&self) -> Option<&Expression> {
self.inner.final_expr()
}
/// Gets the body of the for loop.
#[inline]
#[must_use]
pub const fn body(&self) -> &Statement {
self.inner.body()
}
}
impl ToIndentedString for ForLoop {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = String::from("for (");
if let Some(init) = self.init() {
buf.push_str(&init.to_interned_string(interner));
}
buf.push_str("; ");
if let Some(condition) = self.condition() {
buf.push_str(&condition.to_interned_string(interner));
}
buf.push_str("; ");
if let Some(final_expr) = self.final_expr() {
buf.push_str(&final_expr.to_interned_string(interner));
}
buf.push_str(&format!(
") {}",
self.inner.body().to_indented_string(interner, indentation)
));
buf
}
}
impl From<ForLoop> for Statement {
#[inline]
fn from(for_loop: ForLoop) -> Self {
Self::ForLoop(for_loop)
}
}
impl VisitWith for ForLoop {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
if let Some(fli) = &self.inner.init {
try_break!(visitor.visit_for_loop_initializer(fli));
}
if let Some(expr) = &self.inner.condition {
try_break!(visitor.visit_expression(expr));
}
if let Some(expr) = &self.inner.final_expr {
try_break!(visitor.visit_expression(expr));
}
visitor.visit_statement(&self.inner.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
if let Some(fli) = &mut self.inner.init {
try_break!(visitor.visit_for_loop_initializer_mut(fli));
}
if let Some(expr) = &mut self.inner.condition {
try_break!(visitor.visit_expression_mut(expr));
}
if let Some(expr) = &mut self.inner.final_expr {
try_break!(visitor.visit_expression_mut(expr));
}
visitor.visit_statement_mut(&mut self.inner.body)
}
}
/// Inner structure to avoid multiple indirections in the heap.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
struct InnerForLoop {
init: Option<ForLoopInitializer>,
condition: Option<Expression>,
final_expr: Option<Expression>,
body: Statement,
}
impl InnerForLoop {
/// Creates a new inner for loop.
#[inline]
const fn new(
init: Option<ForLoopInitializer>,
condition: Option<Expression>,
final_expr: Option<Expression>,
body: Statement,
) -> Self {
Self {
init,
condition,
final_expr,
body,
}
}
/// Gets the initialization node.
#[inline]
const fn init(&self) -> Option<&ForLoopInitializer> {
self.init.as_ref()
}
/// Gets the loop condition node.
#[inline]
const fn condition(&self) -> Option<&Expression> {
self.condition.as_ref()
}
/// Gets the final expression node.
#[inline]
const fn final_expr(&self) -> Option<&Expression> {
self.final_expr.as_ref()
}
/// Gets the body of the for loop.
#[inline]
const fn body(&self) -> &Statement {
&self.body
}
}
/// A [`ForLoop`] initializer, as defined by the [spec].
///
/// A `ForLoop` initializer differs a lot from an
/// [`IterableLoopInitializer`][super::IterableLoopInitializer], since it can contain any arbitrary
/// expression instead of only accessors and patterns. Additionally, it can also contain many variable
/// declarations instead of only one.
///
/// [spec]: https://tc39.es/ecma262/#prod-ForStatement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ForLoopInitializer {
/// An expression initializer.
Expression(Expression),
/// A var declaration initializer.
Var(VarDeclaration),
/// A lexical declaration initializer.
Lexical(LexicalDeclaration),
}
impl ToInternedString for ForLoopInitializer {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
Self::Var(var) => var.to_interned_string(interner),
Self::Lexical(lex) => lex.to_interned_string(interner),
Self::Expression(expr) => expr.to_interned_string(interner),
}
}
}
impl From<Expression> for ForLoopInitializer {
#[inline]
fn from(expr: Expression) -> Self {
Self::Expression(expr)
}
}
impl From<LexicalDeclaration> for ForLoopInitializer {
#[inline]
fn from(list: LexicalDeclaration) -> Self {
Self::Lexical(list)
}
}
impl From<VarDeclaration> for ForLoopInitializer {
#[inline]
fn from(list: VarDeclaration) -> Self {
Self::Var(list)
}
}
impl VisitWith for ForLoopInitializer {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::Expression(expr) => visitor.visit_expression(expr),
Self::Var(vd) => visitor.visit_var_declaration(vd),
Self::Lexical(ld) => visitor.visit_lexical_declaration(ld),
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::Expression(expr) => visitor.visit_expression_mut(expr),
Self::Var(vd) => visitor.visit_var_declaration_mut(vd),
Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld),
}
}
}

View File

@@ -0,0 +1,115 @@
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
expression::Expression,
statement::{iteration::IterableLoopInitializer, Statement},
};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
/// A `for...of` loop statement, as defined by the [spec].
///
/// [`for..of`][forof] statements loop over a sequence of values obtained from an iterable object (Array,
/// String, Map, generators).
///
/// This type combines `for..of` and [`for await...of`][forawait] statements in a single structure,
/// since `for await...of` is essentially the same statement but with async iterable objects
/// as the source of iteration.
///
/// [forof]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
/// [forawait]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForOfLoop {
init: IterableLoopInitializer,
iterable: Expression,
body: Box<Statement>,
r#await: bool,
}
impl ForOfLoop {
/// Creates a new "for of" loop AST node.
#[inline]
#[must_use]
pub fn new(
init: IterableLoopInitializer,
iterable: Expression,
body: Statement,
r#await: bool,
) -> Self {
Self {
init,
iterable,
body: body.into(),
r#await,
}
}
/// Gets the initializer of the for...of loop.
#[inline]
#[must_use]
pub const fn initializer(&self) -> &IterableLoopInitializer {
&self.init
}
/// Gets the iterable expression of the for...of loop.
#[inline]
#[must_use]
pub const fn iterable(&self) -> &Expression {
&self.iterable
}
/// Gets the body to execute in the for...of loop.
#[inline]
#[must_use]
pub const fn body(&self) -> &Statement {
&self.body
}
/// Returns true if this "for...of" loop is an "for await...of" loop.
#[inline]
#[must_use]
pub const fn r#await(&self) -> bool {
self.r#await
}
}
impl ToIndentedString for ForOfLoop {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
"for ({} of {}) {}",
self.init.to_interned_string(interner),
self.iterable.to_interned_string(interner),
self.body().to_indented_string(interner, indentation)
)
}
}
impl From<ForOfLoop> for Statement {
#[inline]
fn from(for_of: ForOfLoop) -> Self {
Self::ForOfLoop(for_of)
}
}
impl VisitWith for ForOfLoop {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_iterable_loop_initializer(&self.init));
try_break!(visitor.visit_expression(&self.iterable));
visitor.visit_statement(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_iterable_loop_initializer_mut(&mut self.init));
try_break!(visitor.visit_expression_mut(&mut self.iterable));
visitor.visit_statement_mut(&mut self.body)
}
}

View File

@@ -0,0 +1,94 @@
//! Iteration nodes
mod r#break;
mod r#continue;
mod do_while_loop;
mod for_in_loop;
mod for_loop;
mod for_of_loop;
mod while_loop;
use crate::{
declaration::Binding,
expression::{access::PropertyAccess, Identifier},
pattern::Pattern,
};
use core::ops::ControlFlow;
pub use self::{
do_while_loop::DoWhileLoop,
for_in_loop::ForInLoop,
for_loop::{ForLoop, ForLoopInitializer},
for_of_loop::ForOfLoop,
r#break::Break,
r#continue::Continue,
while_loop::WhileLoop,
};
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, ToInternedString};
/// A `for-in`, `for-of` and `for-await-of` loop initializer.
///
/// The [spec] specifies only single bindings for the listed types of loops, which makes us
/// unable to use plain `LexicalDeclaration`s or `VarStatement`s as initializers, since those
/// can have more than one binding.
///
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum IterableLoopInitializer {
/// An already declared variable.
Identifier(Identifier),
/// A property access.
Access(PropertyAccess),
/// A new var declaration.
Var(Binding),
/// A new let declaration.
Let(Binding),
/// A new const declaration.
Const(Binding),
/// A pattern with already declared variables.
Pattern(Pattern),
}
impl ToInternedString for IterableLoopInitializer {
fn to_interned_string(&self, interner: &Interner) -> String {
let (binding, pre) = match self {
Self::Identifier(ident) => return ident.to_interned_string(interner),
Self::Pattern(pattern) => return pattern.to_interned_string(interner),
Self::Access(access) => return access.to_interned_string(interner),
Self::Var(binding) => (binding, "var"),
Self::Let(binding) => (binding, "let"),
Self::Const(binding) => (binding, "const"),
};
format!("{pre} {}", binding.to_interned_string(interner))
}
}
impl VisitWith for IterableLoopInitializer {
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::Var(b) | Self::Let(b) | Self::Const(b) => visitor.visit_binding(b),
Self::Pattern(p) => visitor.visit_pattern(p),
}
}
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::Var(b) | Self::Let(b) | Self::Const(b) => visitor.visit_binding_mut(b),
Self::Pattern(p) => visitor.visit_pattern_mut(p),
}
}
}

View File

@@ -0,0 +1,88 @@
use crate::{
expression::Expression,
statement::Statement,
try_break,
visitor::{VisitWith, Visitor, VisitorMut},
};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
/// The `while` statement creates a loop that executes a specified statement as long as the
/// test condition evaluates to `true`.
///
/// The condition is evaluated before executing the statement.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct WhileLoop {
condition: Expression,
body: Box<Statement>,
}
impl WhileLoop {
/// Creates a `WhileLoop` AST node.
#[inline]
#[must_use]
pub fn new(condition: Expression, body: Statement) -> Self {
Self {
condition,
body: body.into(),
}
}
/// Gets the condition of the while loop.
#[inline]
#[must_use]
pub const fn condition(&self) -> &Expression {
&self.condition
}
/// Gets the body of the while loop.
#[inline]
#[must_use]
pub const fn body(&self) -> &Statement {
&self.body
}
}
impl ToIndentedString for WhileLoop {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
format!(
"while ({}) {}",
self.condition().to_interned_string(interner),
self.body().to_indented_string(interner, indentation)
)
}
}
impl From<WhileLoop> for Statement {
#[inline]
fn from(while_loop: WhileLoop) -> Self {
Self::WhileLoop(while_loop)
}
}
impl VisitWith for WhileLoop {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_expression(&self.condition));
visitor.visit_statement(&self.body)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_expression_mut(&mut self.condition));
visitor.visit_statement_mut(&mut self.body)
}
}