diff --git a/Cargo.lock b/Cargo.lock
index 00c71dbf3c2..7e98a81c4d8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -55,10 +55,23 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+[[package]]
+name = "boa_ast"
+version = "0.16.0"
+dependencies = [
+ "bitflags",
+ "boa_interner",
+ "boa_macros",
+ "num-bigint",
+ "rustc-hash",
+ "serde",
+]
+
[[package]]
name = "boa_cli"
version = "0.16.0"
dependencies = [
+ "boa_ast",
"boa_engine",
"boa_interner",
"clap 4.0.18",
@@ -76,6 +89,7 @@ name = "boa_engine"
version = "0.16.0"
dependencies = [
"bitflags",
+ "boa_ast",
"boa_gc",
"boa_interner",
"boa_macros",
@@ -117,6 +131,7 @@ dependencies = [
name = "boa_examples"
version = "0.16.0"
dependencies = [
+ "boa_ast",
"boa_engine",
"boa_gc",
"boa_interner",
diff --git a/Cargo.toml b/Cargo.toml
index cffa4bbe1ea..bfd347d7842 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,6 +2,7 @@
members = [
"boa_cli",
"boa_engine",
+ "boa_ast",
"boa_gc",
"boa_interner",
"boa_profiler",
@@ -28,6 +29,7 @@ boa_gc = { version = "0.16.0", path = "boa_gc" }
boa_profiler = { version = "0.16.0", path = "boa_profiler" }
boa_unicode = { version = "0.16.0", path = "boa_unicode" }
boa_macros = { version = "0.16.0", path = "boa_macros" }
+boa_ast = { version = "0.16.0", path = "boa_ast" }
[workspace.metadata.workspaces]
allow_branch = "main"
diff --git a/boa_ast/Cargo.toml b/boa_ast/Cargo.toml
new file mode 100644
index 00000000000..b248436cf1d
--- /dev/null
+++ b/boa_ast/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "boa_ast"
+description = "Abstract Syntax Tree definition for the Boa JavaScript engine."
+keywords = ["javascript", "js", "syntax", "ast"]
+categories = ["parser-implementations", "compilers"]
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+
+[features]
+serde = ["boa_interner/serde", "dep:serde"]
+
+[dependencies]
+boa_interner.workspace = true
+boa_macros.workspace = true
+rustc-hash = "1.1.0"
+serde = { version = "1.0.147", features = ["derive"], optional = true }
+bitflags = "1.3.2"
+num-bigint = "0.4.3"
diff --git a/boa_engine/src/syntax/ast/declaration/mod.rs b/boa_ast/src/declaration/mod.rs
similarity index 96%
rename from boa_engine/src/syntax/ast/declaration/mod.rs
rename to boa_ast/src/declaration/mod.rs
index eca685b8159..a1c03cd1d7d 100644
--- a/boa_engine/src/syntax/ast/declaration/mod.rs
+++ b/boa_ast/src/declaration/mod.rs
@@ -21,17 +21,16 @@ use super::{
};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
-use tap::Tap;
mod variable;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
pub use variable::*;
/// The `Declaration` Parse Node.
///
/// See the [module level documentation][self] for more information.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Declaration {
/// See [`Function`]
@@ -163,7 +162,11 @@ impl ToIndentedString for Declaration {
Declaration::AsyncFunction(af) => af.to_indented_string(interner, indentation),
Declaration::AsyncGenerator(ag) => ag.to_indented_string(interner, indentation),
Declaration::Class(c) => c.to_indented_string(interner, indentation),
- Declaration::Lexical(l) => l.to_interned_string(interner).tap_mut(|s| s.push(';')),
+ Declaration::Lexical(l) => {
+ let mut s = l.to_interned_string(interner);
+ s.push(';');
+ s
+ }
}
}
}
diff --git a/boa_engine/src/syntax/ast/declaration/variable.rs b/boa_ast/src/declaration/variable.rs
similarity index 88%
rename from boa_engine/src/syntax/ast/declaration/variable.rs
rename to boa_ast/src/declaration/variable.rs
index c2ea06b86e4..2a2536bf4de 100644
--- a/boa_engine/src/syntax/ast/declaration/variable.rs
+++ b/boa_ast/src/declaration/variable.rs
@@ -3,14 +3,14 @@
use core::ops::ControlFlow;
use std::convert::TryFrom;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
expression::{Expression, Identifier},
join_nodes,
pattern::Pattern,
ContainsSymbol, Statement,
};
-use crate::try_break;
use boa_interner::{Interner, ToInternedString};
use super::Declaration;
@@ -44,7 +44,7 @@ use super::Declaration;
/// [var]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
/// [varstmt]: https://tc39.es/ecma262/#prod-VariableStatement
/// [hoisting]: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct VarDeclaration(pub VariableList);
@@ -91,7 +91,7 @@ impl VisitWith for VarDeclaration {
/// the variable declaration.
///
/// [lexical declaration]: https://tc39.es/ecma262/#sec-let-and-const-declarations
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum LexicalDeclaration {
/// A [const]
variable creates a constant whose scope can be either global or local
@@ -118,6 +118,7 @@ pub enum LexicalDeclaration {
impl LexicalDeclaration {
/// Gets the inner variable list of the `LexicalDeclaration`
+ #[must_use]
pub fn variable_list(&self) -> &VariableList {
match self {
LexicalDeclaration::Const(list) | LexicalDeclaration::Let(list) => list,
@@ -185,7 +186,7 @@ impl VisitWith for LexicalDeclaration {
}
/// List of variables in a variable declaration.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct VariableList {
list: Box<[Variable]>,
@@ -193,6 +194,7 @@ pub struct VariableList {
impl VariableList {
/// Creates a variable list if the provided list of [`Variable`] is not empty.
+ #[must_use]
pub fn new(list: Box<[Variable]>) -> Option {
if list.is_empty() {
return None;
@@ -274,7 +276,7 @@ impl TryFrom> for VariableList {
/// [spec1]: https://tc39.es/ecma262/#prod-LexicalBinding
/// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration
/// [spec3]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Variable {
binding: Binding,
@@ -295,7 +297,8 @@ impl ToInternedString for Variable {
impl Variable {
/// Creates a new variable declaration from a `BindingIdentifier`.
#[inline]
- pub(in crate::syntax) fn from_identifier(ident: Identifier, init: Option) -> Self {
+ #[must_use]
+ pub fn from_identifier(ident: Identifier, init: Option) -> Self {
Self {
binding: Binding::Identifier(ident),
init,
@@ -304,23 +307,32 @@ impl Variable {
/// Creates a new variable declaration from a `Pattern`.
#[inline]
- pub(in crate::syntax) fn from_pattern(pattern: Pattern, init: Option) -> Self {
+ #[must_use]
+ pub fn from_pattern(pattern: Pattern, init: Option) -> Self {
Self {
binding: Binding::Pattern(pattern),
init,
}
}
/// Gets the variable declaration binding.
- pub(crate) fn binding(&self) -> &Binding {
+ #[must_use]
+ pub fn binding(&self) -> &Binding {
&self.binding
}
/// Gets the initialization expression for the variable declaration, if any.
#[inline]
- pub(crate) fn init(&self) -> Option<&Expression> {
+ #[must_use]
+ pub fn init(&self) -> Option<&Expression> {
self.init.as_ref()
}
+ /// Gets the list of declared identifiers.
+ #[must_use]
+ pub fn idents(&self) -> Vec {
+ self.binding.idents()
+ }
+
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
if let Some(ref node) = self.init {
@@ -338,7 +350,8 @@ impl Variable {
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
#[inline]
- pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
+ #[must_use]
+ pub fn contains(&self, symbol: ContainsSymbol) -> bool {
if let Some(ref node) = self.init {
if node.contains(symbol) {
return true;
@@ -346,11 +359,6 @@ impl Variable {
}
self.binding.contains(symbol)
}
-
- /// Gets the list of declared identifiers.
- pub(crate) fn idents(&self) -> Vec {
- self.binding.idents()
- }
}
impl VisitWith for Variable {
@@ -383,7 +391,7 @@ impl VisitWith for Variable {
/// - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Binding {
/// A single identifier binding.
@@ -458,47 +466,3 @@ impl VisitWith for Binding {
}
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt_binding_pattern() {
- crate::syntax::ast::test_formatting(
- r#"
- var { } = {
- o: "1",
- };
- var { o_v1 } = {
- o_v1: "1",
- };
- var { o_v2 = "1" } = {
- o_v2: "2",
- };
- var { a : o_v3 = "1" } = {
- a: "2",
- };
- var { ... o_rest_v1 } = {
- a: "2",
- };
- var { o_v4, o_v5, o_v6 = "1", a : o_v7 = "1", ... o_rest_v2 } = {
- o_v4: "1",
- o_v5: "1",
- };
- var [] = [];
- var [ , ] = [];
- var [ a_v1 ] = [1, 2, 3];
- var [ a_v2, a_v3 ] = [1, 2, 3];
- var [ a_v2, , a_v3 ] = [1, 2, 3];
- var [ ... a_rest_v1 ] = [1, 2, 3];
- var [ a_v4, , ... a_rest_v2 ] = [1, 2, 3];
- var [ { a_v5 } ] = [{
- a_v5: 1,
- }, {
- a_v5: 2,
- }, {
- a_v5: 3,
- }];
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/access.rs b/boa_ast/src/expression/access.rs
similarity index 94%
rename from boa_engine/src/syntax/ast/expression/access.rs
rename to boa_ast/src/expression/access.rs
index 2c4e2b3257f..520d20c377e 100644
--- a/boa_engine/src/syntax/ast/expression/access.rs
+++ b/boa_ast/src/expression/access.rs
@@ -14,16 +14,16 @@
//! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors
//! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, ContainsSymbol};
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
/// A property access field.
///
/// See the [module level documentation][self] for more information.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyAccessField {
/// A constant property field, such as `x.prop`.
@@ -87,7 +87,7 @@ impl VisitWith for PropertyAccessField {
/// A property access expression.
///
/// See the [module level documentation][self] for more information.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyAccess {
/// A simple property access (`x.prop`).
@@ -161,7 +161,7 @@ impl VisitWith for PropertyAccess {
}
/// A simple property access, where the target object is an [`Expression`].
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct SimplePropertyAccess {
target: Box,
@@ -171,12 +171,14 @@ pub struct SimplePropertyAccess {
impl SimplePropertyAccess {
/// Gets the target object of the property access.
#[inline]
+ #[must_use]
pub fn target(&self) -> &Expression {
&self.target
}
/// Gets the accessed field of the target object.
#[inline]
+ #[must_use]
pub fn field(&self) -> &PropertyAccessField {
&self.field
}
@@ -252,7 +254,7 @@ impl VisitWith for SimplePropertyAccess {
///
/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct PrivatePropertyAccess {
target: Box,
@@ -262,6 +264,7 @@ pub struct PrivatePropertyAccess {
impl PrivatePropertyAccess {
/// Creates a `GetPrivateField` AST Expression.
#[inline]
+ #[must_use]
pub fn new(value: Expression, field: Sym) -> Self {
Self {
target: value.into(),
@@ -271,12 +274,14 @@ impl PrivatePropertyAccess {
/// Gets the original object from where to get the field from.
#[inline]
+ #[must_use]
pub fn target(&self) -> &Expression {
&self.target
}
/// Gets the name of the field to retrieve.
#[inline]
+ #[must_use]
pub fn field(&self) -> Sym {
self.field
}
@@ -335,19 +340,22 @@ impl VisitWith for PrivatePropertyAccess {
///
/// [spec]: https://tc39.es/ecma262/#prod-SuperProperty
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct SuperPropertyAccess {
field: PropertyAccessField,
}
impl SuperPropertyAccess {
- pub(in crate::syntax) fn new(field: PropertyAccessField) -> Self {
+ /// Creates a new property access field node.
+ #[must_use]
+ pub fn new(field: PropertyAccessField) -> Self {
Self { field }
}
/// Gets the name of the field to retrieve.
#[inline]
+ #[must_use]
pub fn field(&self) -> &PropertyAccessField {
&self.field
}
diff --git a/boa_engine/src/syntax/ast/expression/await.rs b/boa_ast/src/expression/await.rs
similarity index 76%
rename from boa_engine/src/syntax/ast/expression/await.rs
rename to boa_ast/src/expression/await.rs
index 00154c3b78c..604f99f6c9f 100644
--- a/boa_engine/src/syntax/ast/expression/await.rs
+++ b/boa_ast/src/expression/await.rs
@@ -1,10 +1,10 @@
//! Await expression Expression.
-use crate::syntax::ast::ContainsSymbol;
+use crate::ContainsSymbol;
use core::ops::ControlFlow;
use super::Expression;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
/// An await expression is used within an async function to pause execution and wait for a
@@ -16,7 +16,7 @@ use boa_interner::{Interner, ToIndentedString, ToInternedString};
///
/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Await {
target: Box,
@@ -25,7 +25,8 @@ pub struct Await {
impl Await {
/// Return the target expression that should be awaited.
#[inline]
- pub(crate) fn target(&self) -> &Expression {
+ #[must_use]
+ pub fn target(&self) -> &Expression {
&self.target
}
@@ -79,18 +80,3 @@ impl VisitWith for Await {
visitor.visit_expression_mut(&mut self.target)
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- // TODO: `let a = await fn()` is invalid syntax as of writing. It should be tested here once implemented.
- crate::syntax::ast::test_formatting(
- r#"
- async function f() {
- await function_call();
- }
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/call.rs b/boa_ast/src/expression/call.rs
similarity index 89%
rename from boa_engine/src/syntax/ast/expression/call.rs
rename to boa_ast/src/expression/call.rs
index f99bc9956bb..22626631553 100644
--- a/boa_engine/src/syntax/ast/expression/call.rs
+++ b/boa_ast/src/expression/call.rs
@@ -1,6 +1,6 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{join_nodes, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{join_nodes, ContainsSymbol};
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;
@@ -20,7 +20,7 @@ use super::Expression;
///
/// [spec]: https://tc39.es/ecma262/#prod-CallExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Call {
function: Box,
@@ -30,6 +30,7 @@ pub struct Call {
impl Call {
/// Creates a new `Call` AST Expression.
#[inline]
+ #[must_use]
pub fn new(function: Expression, args: Box<[Expression]>) -> Self {
Self {
function: function.into(),
@@ -39,12 +40,14 @@ impl Call {
/// Gets the target function of this call expression.
#[inline]
+ #[must_use]
pub fn function(&self) -> &Expression {
&self.function
}
/// Retrieves the arguments passed to the function.
#[inline]
+ #[must_use]
pub fn args(&self) -> &[Expression] {
&self.args
}
@@ -110,7 +113,7 @@ impl VisitWith for Call {
///
/// [spec]: https://tc39.es/ecma262/#prod-SuperCall
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct SuperCall {
args: Box<[Expression]>,
@@ -118,7 +121,7 @@ pub struct SuperCall {
impl SuperCall {
/// Creates a new `SuperCall` AST node.
- pub(crate) fn new(args: A) -> Self
+ pub fn new(args: A) -> Self
where
A: Into>,
{
@@ -126,7 +129,8 @@ impl SuperCall {
}
/// Retrieves the arguments of the super call.
- pub(crate) fn args(&self) -> &[Expression] {
+ #[must_use]
+ pub fn arguments(&self) -> &[Expression] {
&self.args
}
@@ -176,17 +180,3 @@ impl VisitWith for SuperCall {
ControlFlow::Continue(())
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- call_1(1, 2, 3);
- call_2("argument here");
- call_3();
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/identifier.rs b/boa_ast/src/expression/identifier.rs
similarity index 76%
rename from boa_engine/src/syntax/ast/expression/identifier.rs
rename to boa_ast/src/expression/identifier.rs
index c9b753bed3e..11320d06bcd 100644
--- a/boa_engine/src/syntax/ast/expression/identifier.rs
+++ b/boa_ast/src/expression/identifier.rs
@@ -1,15 +1,27 @@
//! Local identifier Expression.
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
- string::ToStringEscaped,
- syntax::{ast::Position, parser::ParseError},
+ visitor::{VisitWith, Visitor, VisitorMut},
+ ToStringEscaped,
};
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
use super::Expression;
+/// List of reserved keywords exclusive to strict mode.
+pub const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [
+ Sym::IMPLEMENTS,
+ Sym::INTERFACE,
+ Sym::LET,
+ Sym::PACKAGE,
+ Sym::PRIVATE,
+ Sym::PROTECTED,
+ Sym::PUBLIC,
+ Sym::STATIC,
+ Sym::YIELD,
+];
+
/// An `identifier` is a sequence of characters in the code that identifies a variable,
/// function, or property.
///
@@ -27,7 +39,7 @@ use super::Expression;
/// [spec]: https://tc39.es/ecma262/#prod-Identifier
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier
#[cfg_attr(
- feature = "deser",
+ feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
@@ -54,33 +66,17 @@ impl PartialEq for Sym {
impl Identifier {
/// Creates a new identifier AST Expression.
#[inline]
+ #[must_use]
pub fn new(ident: Sym) -> Self {
Self { ident }
}
/// Retrieves the identifier's string symbol in the interner.
#[inline]
+ #[must_use]
pub fn sym(self) -> Sym {
self.ident
}
-
- /// Returns an error if `arguments` or `eval` are used as identifier in strict mode.
- pub(crate) fn check_strict_arguments_or_eval(
- self,
- position: Position,
- ) -> Result<(), ParseError> {
- match self.ident {
- Sym::ARGUMENTS => Err(ParseError::general(
- "unexpected identifier 'arguments' in strict mode",
- position,
- )),
- Sym::EVAL => Err(ParseError::general(
- "unexpected identifier 'eval' in strict mode",
- position,
- )),
- _ => Ok(()),
- }
- }
}
impl ToInternedString for Identifier {
diff --git a/boa_ast/src/expression/literal/array.rs b/boa_ast/src/expression/literal/array.rs
new file mode 100644
index 00000000000..8fc0875f750
--- /dev/null
+++ b/boa_ast/src/expression/literal/array.rs
@@ -0,0 +1,236 @@
+//! Array declaration Expression.
+
+use crate::expression::operator::assign::AssignTarget;
+use crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern};
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, ContainsSymbol};
+use boa_interner::{Interner, Sym, ToInternedString};
+use core::ops::ControlFlow;
+
+/// An array is an ordered collection of data (either primitive or object depending upon the
+/// language).
+///
+/// Arrays are used to store multiple values in a single variable.
+/// This is compared to a variable that can store only one value.
+///
+/// Each item in an array has a number attached to it, called a numeric index, that allows you
+/// to access it. In JavaScript, arrays start at index zero and can be manipulated with various
+/// methods.
+///
+/// More information:
+/// - [ECMAScript reference][spec]
+/// - [MDN documentation][mdn]
+///
+/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral
+/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug, PartialEq)]
+pub struct ArrayLiteral {
+ arr: Box<[Option]>,
+ has_trailing_comma_spread: bool,
+}
+
+impl ArrayLiteral {
+ /// Creates a new array literal.
+ pub fn new(array: A, has_trailing_comma_spread: bool) -> Self
+ where
+ A: Into]>>,
+ {
+ Self {
+ arr: array.into(),
+ has_trailing_comma_spread,
+ }
+ }
+
+ /// Indicates if a spread operator in the array literal has a trailing comma.
+ /// This is a syntax error in some cases.
+ #[must_use]
+ pub fn has_trailing_comma_spread(&self) -> bool {
+ self.has_trailing_comma_spread
+ }
+
+ /// Converts this `ArrayLiteral` into an [`ArrayPattern`].
+ #[must_use]
+ pub fn to_pattern(&self, strict: bool) -> Option {
+ if self.has_trailing_comma_spread() {
+ return None;
+ }
+
+ let mut bindings = Vec::new();
+ for (i, expr) in self.arr.iter().enumerate() {
+ let expr = if let Some(expr) = expr {
+ expr
+ } else {
+ bindings.push(ArrayPatternElement::Elision);
+ continue;
+ };
+ match expr {
+ Expression::Identifier(ident) => {
+ if strict && *ident == Sym::ARGUMENTS {
+ return None;
+ }
+
+ bindings.push(ArrayPatternElement::SingleName {
+ ident: *ident,
+ default_init: None,
+ });
+ }
+ Expression::Spread(spread) => {
+ match spread.target() {
+ Expression::Identifier(ident) => {
+ bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident });
+ }
+ Expression::PropertyAccess(access) => {
+ bindings.push(ArrayPatternElement::PropertyAccessRest {
+ access: access.clone(),
+ });
+ }
+ Expression::ArrayLiteral(array) => {
+ let pattern = array.to_pattern(strict)?.into();
+ bindings.push(ArrayPatternElement::PatternRest { pattern });
+ }
+ Expression::ObjectLiteral(object) => {
+ let pattern = object.to_pattern(strict)?.into();
+ bindings.push(ArrayPatternElement::PatternRest { pattern });
+ }
+ _ => return None,
+ }
+ if i + 1 != self.arr.len() {
+ return None;
+ }
+ }
+ Expression::Assign(assign) => match assign.lhs() {
+ AssignTarget::Identifier(ident) => {
+ bindings.push(ArrayPatternElement::SingleName {
+ ident: *ident,
+ default_init: Some(assign.rhs().clone()),
+ });
+ }
+ AssignTarget::Access(access) => {
+ bindings.push(ArrayPatternElement::PropertyAccess {
+ access: access.clone(),
+ });
+ }
+ AssignTarget::Pattern(pattern) => match pattern {
+ Pattern::Object(pattern) => {
+ bindings.push(ArrayPatternElement::Pattern {
+ pattern: Pattern::Object(pattern.clone()),
+ default_init: Some(assign.rhs().clone()),
+ });
+ }
+ Pattern::Array(pattern) => {
+ bindings.push(ArrayPatternElement::Pattern {
+ pattern: Pattern::Array(pattern.clone()),
+ default_init: Some(assign.rhs().clone()),
+ });
+ }
+ },
+ },
+ Expression::ArrayLiteral(array) => {
+ let pattern = array.to_pattern(strict)?.into();
+ bindings.push(ArrayPatternElement::Pattern {
+ pattern,
+ default_init: None,
+ });
+ }
+ Expression::ObjectLiteral(object) => {
+ let pattern = object.to_pattern(strict)?.into();
+ bindings.push(ArrayPatternElement::Pattern {
+ pattern,
+ default_init: None,
+ });
+ }
+ Expression::PropertyAccess(access) => {
+ bindings.push(ArrayPatternElement::PropertyAccess {
+ access: access.clone(),
+ });
+ }
+ _ => return None,
+ }
+ }
+ Some(ArrayPattern::new(bindings.into()))
+ }
+
+ #[inline]
+ pub(crate) fn contains_arguments(&self) -> bool {
+ self.arr
+ .iter()
+ .flatten()
+ .any(Expression::contains_arguments)
+ }
+
+ #[inline]
+ pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
+ self.arr.iter().flatten().any(|expr| expr.contains(symbol))
+ }
+}
+
+impl AsRef<[Option]> for ArrayLiteral {
+ #[inline]
+ fn as_ref(&self) -> &[Option] {
+ &self.arr
+ }
+}
+
+impl From for ArrayLiteral
+where
+ T: Into]>>,
+{
+ #[inline]
+ fn from(decl: T) -> Self {
+ Self {
+ arr: decl.into(),
+ has_trailing_comma_spread: false,
+ }
+ }
+}
+
+impl ToInternedString for ArrayLiteral {
+ #[inline]
+ fn to_interned_string(&self, interner: &Interner) -> String {
+ let mut buf = String::from("[");
+ let mut first = true;
+ for e in &*self.arr {
+ if first {
+ first = false;
+ } else {
+ buf.push_str(", ");
+ }
+ if let Some(e) = e {
+ buf.push_str(&e.to_interned_string(interner));
+ }
+ }
+ buf.push(']');
+ buf
+ }
+}
+
+impl From for Expression {
+ #[inline]
+ fn from(arr: ArrayLiteral) -> Self {
+ Self::ArrayLiteral(arr)
+ }
+}
+
+impl VisitWith for ArrayLiteral {
+ fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
+ where
+ V: Visitor<'a>,
+ {
+ for expr in self.arr.iter().flatten() {
+ try_break!(visitor.visit_expression(expr));
+ }
+ ControlFlow::Continue(())
+ }
+
+ fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow
+ where
+ V: VisitorMut<'a>,
+ {
+ for expr in self.arr.iter_mut().flatten() {
+ try_break!(visitor.visit_expression_mut(expr));
+ }
+ ControlFlow::Continue(())
+ }
+}
diff --git a/boa_engine/src/syntax/ast/expression/literal/mod.rs b/boa_ast/src/expression/literal/mod.rs
similarity index 98%
rename from boa_engine/src/syntax/ast/expression/literal/mod.rs
rename to boa_ast/src/expression/literal/mod.rs
index dac95a4e7f1..bc8692d7fba 100644
--- a/boa_engine/src/syntax/ast/expression/literal/mod.rs
+++ b/boa_ast/src/expression/literal/mod.rs
@@ -16,7 +16,7 @@ use core::ops::ControlFlow;
pub use object::ObjectLiteral;
pub use template::{TemplateElement, TemplateLiteral};
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString};
use num_bigint::BigInt;
@@ -32,7 +32,7 @@ use super::Expression;
///
/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
/// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks.
diff --git a/boa_ast/src/expression/literal/object.rs b/boa_ast/src/expression/literal/object.rs
new file mode 100644
index 00000000000..1458dfdc33a
--- /dev/null
+++ b/boa_ast/src/expression/literal/object.rs
@@ -0,0 +1,338 @@
+//! Object Expression.
+
+use crate::{
+ block_to_string,
+ expression::{operator::assign::AssignTarget, Expression, RESERVED_IDENTIFIERS_STRICT},
+ join_nodes,
+ pattern::{ObjectPattern, ObjectPatternElement},
+ property::{MethodDefinition, PropertyDefinition, PropertyName},
+ try_break,
+ visitor::{VisitWith, Visitor, VisitorMut},
+ ContainsSymbol,
+};
+use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
+use core::ops::ControlFlow;
+
+/// Objects in JavaScript may be defined as an unordered collection of related data, of
+/// primitive or reference types, in the form of “key: value” pairs.
+///
+/// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal
+/// notation.
+///
+/// An object initializer is an expression that describes the initialization of an
+/// [`Object`][object]. Objects consist of properties, which are used to describe an object.
+/// Values of object properties can either contain [`primitive`][primitive] data types or other
+/// objects.
+///
+/// More information:
+/// - [ECMAScript reference][spec]
+/// - [MDN documentation][mdn]
+///
+/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral
+/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
+/// [object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
+/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(transparent))]
+#[derive(Clone, Debug, PartialEq)]
+pub struct ObjectLiteral {
+ properties: Box<[PropertyDefinition]>,
+}
+
+impl ObjectLiteral {
+ /// Gets the object literal properties
+ #[inline]
+ #[must_use]
+ pub fn properties(&self) -> &[PropertyDefinition] {
+ &self.properties
+ }
+
+ /// Converts the object literal into an [`ObjectPattern`].
+ #[must_use]
+ pub fn to_pattern(&self, strict: bool) -> Option {
+ let mut bindings = Vec::new();
+ let mut excluded_keys = Vec::new();
+ for (i, property) in self.properties.iter().enumerate() {
+ match property {
+ PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => {
+ return None
+ }
+ PropertyDefinition::IdentifierReference(ident) => {
+ if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) {
+ return None;
+ }
+
+ excluded_keys.push(*ident);
+ bindings.push(ObjectPatternElement::SingleName {
+ ident: *ident,
+ name: PropertyName::Literal(ident.sym()),
+ default_init: None,
+ });
+ }
+ PropertyDefinition::Property(name, expr) => match (name, expr) {
+ (PropertyName::Literal(name), Expression::Identifier(ident))
+ if *name == *ident =>
+ {
+ if strict && *name == Sym::EVAL {
+ return None;
+ }
+ if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) {
+ return None;
+ }
+
+ excluded_keys.push(*ident);
+ bindings.push(ObjectPatternElement::SingleName {
+ ident: *ident,
+ name: PropertyName::Literal(*name),
+ default_init: None,
+ });
+ }
+ (PropertyName::Literal(name), Expression::Identifier(ident)) => {
+ bindings.push(ObjectPatternElement::SingleName {
+ ident: *ident,
+ name: PropertyName::Literal(*name),
+ default_init: None,
+ });
+ }
+ (PropertyName::Literal(name), Expression::ObjectLiteral(object)) => {
+ let pattern = object.to_pattern(strict)?.into();
+ bindings.push(ObjectPatternElement::Pattern {
+ name: PropertyName::Literal(*name),
+ pattern,
+ default_init: None,
+ });
+ }
+ (PropertyName::Literal(name), Expression::ArrayLiteral(array)) => {
+ let pattern = array.to_pattern(strict)?.into();
+ bindings.push(ObjectPatternElement::Pattern {
+ name: PropertyName::Literal(*name),
+ pattern,
+ default_init: None,
+ });
+ }
+ (_, Expression::Assign(assign)) => match assign.lhs() {
+ AssignTarget::Identifier(ident) => {
+ if let Some(name) = name.literal() {
+ if name == *ident {
+ if strict && name == Sym::EVAL {
+ return None;
+ }
+ if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) {
+ return None;
+ }
+ excluded_keys.push(*ident);
+ bindings.push(ObjectPatternElement::SingleName {
+ ident: *ident,
+ name: PropertyName::Literal(name),
+ default_init: Some(assign.rhs().clone()),
+ });
+ } else {
+ bindings.push(ObjectPatternElement::SingleName {
+ ident: *ident,
+ name: PropertyName::Literal(name),
+ default_init: Some(assign.rhs().clone()),
+ });
+ }
+ } else {
+ return None;
+ }
+ }
+ AssignTarget::Pattern(pattern) => {
+ bindings.push(ObjectPatternElement::Pattern {
+ name: name.clone(),
+ pattern: pattern.clone(),
+ default_init: Some(assign.rhs().clone()),
+ });
+ }
+ AssignTarget::Access(access) => {
+ bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
+ name: name.clone(),
+ access: access.clone(),
+ default_init: Some(assign.rhs().clone()),
+ });
+ }
+ },
+ (_, Expression::PropertyAccess(access)) => {
+ bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
+ name: name.clone(),
+ access: access.clone(),
+ default_init: None,
+ });
+ }
+ (PropertyName::Computed(name), Expression::Identifier(ident)) => {
+ bindings.push(ObjectPatternElement::SingleName {
+ ident: *ident,
+ name: PropertyName::Computed(name.clone()),
+ default_init: None,
+ });
+ }
+ _ => return None,
+ },
+ PropertyDefinition::SpreadObject(spread) => {
+ match spread {
+ Expression::Identifier(ident) => {
+ bindings.push(ObjectPatternElement::RestProperty {
+ ident: *ident,
+ excluded_keys: excluded_keys.clone(),
+ });
+ }
+ Expression::PropertyAccess(access) => {
+ bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess {
+ access: access.clone(),
+ excluded_keys: excluded_keys.clone(),
+ });
+ }
+ _ => return None,
+ }
+ if i + 1 != self.properties.len() {
+ return None;
+ }
+ }
+ PropertyDefinition::MethodDefinition(_, _) => return None,
+ PropertyDefinition::CoverInitializedName(ident, expr) => {
+ if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) {
+ return None;
+ }
+
+ bindings.push(ObjectPatternElement::SingleName {
+ ident: *ident,
+ name: PropertyName::Literal(ident.sym()),
+ default_init: Some(expr.clone()),
+ });
+ }
+ }
+ }
+
+ Some(ObjectPattern::new(bindings.into()))
+ }
+
+ #[inline]
+ pub(crate) fn contains_arguments(&self) -> bool {
+ self.properties
+ .iter()
+ .any(PropertyDefinition::contains_arguments)
+ }
+
+ #[inline]
+ pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
+ self.properties.iter().any(|prop| prop.contains(symbol))
+ }
+}
+
+impl ToIndentedString for ObjectLiteral {
+ fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
+ let mut buf = "{\n".to_owned();
+ let indentation = " ".repeat(indent_n + 1);
+ for property in self.properties().iter() {
+ buf.push_str(&match property {
+ PropertyDefinition::IdentifierReference(ident) => {
+ format!("{indentation}{},\n", interner.resolve_expect(ident.sym()))
+ }
+ PropertyDefinition::Property(key, value) => {
+ format!(
+ "{indentation}{}: {},\n",
+ key.to_interned_string(interner),
+ value.to_no_indent_string(interner, indent_n + 1)
+ )
+ }
+ PropertyDefinition::SpreadObject(key) => {
+ format!("{indentation}...{},\n", key.to_interned_string(interner))
+ }
+ PropertyDefinition::MethodDefinition(key, method) => {
+ format!(
+ "{indentation}{}{}({}) {},\n",
+ match &method {
+ MethodDefinition::Get(_) => "get ",
+ MethodDefinition::Set(_) => "set ",
+ _ => "",
+ },
+ key.to_interned_string(interner),
+ match &method {
+ MethodDefinition::Get(expression)
+ | MethodDefinition::Set(expression)
+ | MethodDefinition::Ordinary(expression) => {
+ join_nodes(interner, expression.parameters().as_ref())
+ }
+ MethodDefinition::Generator(expression) => {
+ join_nodes(interner, expression.parameters().as_ref())
+ }
+ MethodDefinition::AsyncGenerator(expression) => {
+ join_nodes(interner, expression.parameters().as_ref())
+ }
+ MethodDefinition::Async(expression) => {
+ join_nodes(interner, expression.parameters().as_ref())
+ }
+ },
+ match &method {
+ MethodDefinition::Get(expression)
+ | MethodDefinition::Set(expression)
+ | MethodDefinition::Ordinary(expression) => {
+ block_to_string(expression.body(), interner, indent_n + 1)
+ }
+ MethodDefinition::Generator(expression) => {
+ block_to_string(expression.body(), interner, indent_n + 1)
+ }
+ MethodDefinition::AsyncGenerator(expression) => {
+ block_to_string(expression.body(), interner, indent_n + 1)
+ }
+ MethodDefinition::Async(expression) => {
+ block_to_string(expression.body(), interner, indent_n + 1)
+ }
+ },
+ )
+ }
+ PropertyDefinition::CoverInitializedName(ident, expr) => {
+ format!(
+ "{indentation}{} = {},\n",
+ interner.resolve_expect(ident.sym()),
+ expr.to_no_indent_string(interner, indent_n + 1)
+ )
+ }
+ });
+ }
+ buf.push_str(&format!("{}}}", " ".repeat(indent_n)));
+
+ buf
+ }
+}
+
+impl From for ObjectLiteral
+where
+ T: Into>,
+{
+ #[inline]
+ fn from(props: T) -> Self {
+ Self {
+ properties: props.into(),
+ }
+ }
+}
+
+impl From for Expression {
+ #[inline]
+ fn from(obj: ObjectLiteral) -> Self {
+ Self::ObjectLiteral(obj)
+ }
+}
+
+impl VisitWith for ObjectLiteral {
+ fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
+ where
+ V: Visitor<'a>,
+ {
+ for pd in self.properties.iter() {
+ try_break!(visitor.visit_property_definition(pd));
+ }
+ ControlFlow::Continue(())
+ }
+
+ fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow
+ where
+ V: VisitorMut<'a>,
+ {
+ for pd in self.properties.iter_mut() {
+ try_break!(visitor.visit_property_definition_mut(pd));
+ }
+ ControlFlow::Continue(())
+ }
+}
diff --git a/boa_engine/src/syntax/ast/expression/literal/template.rs b/boa_ast/src/expression/literal/template.rs
similarity index 79%
rename from boa_engine/src/syntax/ast/expression/literal/template.rs
rename to boa_ast/src/expression/literal/template.rs
index cd1dc7c7ceb..a0994daacd3 100644
--- a/boa_engine/src/syntax/ast/expression/literal/template.rs
+++ b/boa_ast/src/expression/literal/template.rs
@@ -5,11 +5,11 @@ use std::borrow::Cow;
use boa_interner::{Interner, Sym, ToInternedString};
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
- string::ToStringEscaped,
- syntax::ast::{expression::Expression, ContainsSymbol},
+ expression::Expression,
try_break,
+ visitor::{VisitWith, Visitor, VisitorMut},
+ ContainsSymbol, ToStringEscaped,
};
/// Template literals are string literals allowing embedded expressions.
@@ -20,7 +20,7 @@ use crate::{
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
/// [spec]: https://tc39.es/ecma262/#sec-template-literals
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct TemplateLiteral {
elements: Box<[TemplateElement]>,
@@ -39,7 +39,7 @@ impl From for Expression {
/// node as the equivalent of the components found in a template literal.
///
/// [spec]: https://tc39.es/ecma262/#sec-template-literals
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum TemplateElement {
/// A simple string.
@@ -51,11 +51,14 @@ pub enum TemplateElement {
impl TemplateLiteral {
/// Creates a new `TemplateLiteral` from a list of [`TemplateElement`]s.
#[inline]
+ #[must_use]
pub fn new(elements: Box<[TemplateElement]>) -> Self {
Self { elements }
}
- pub(crate) fn elements(&self) -> &[TemplateElement] {
+ /// Gets the element list of this `TemplateLiteral`.
+ #[must_use]
+ pub fn elements(&self) -> &[TemplateElement] {
&self.elements
}
@@ -142,35 +145,3 @@ impl VisitWith for TemplateElement {
}
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::exec;
-
- #[test]
- fn template_literal() {
- let scenario = r#"
- let a = 10;
- `result: ${a} and ${a+10}`;
- "#;
-
- assert_eq!(&exec(scenario), "\"result: 10 and 20\"");
- }
-
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- function tag(t, ...args) {
- let a = [];
- a = a.concat([t[0], t[1], t[2]]);
- a = a.concat([t.raw[0], t.raw[1], t.raw[2]]);
- a = a.concat([args[0], args[1]]);
- return a;
- }
- let a = 10;
- tag`result: ${a} \x26 ${a + 10}`;
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/mod.rs b/boa_ast/src/expression/mod.rs
similarity index 97%
rename from boa_engine/src/syntax/ast/expression/mod.rs
rename to boa_ast/src/expression/mod.rs
index 75d74068a71..c229d0d9dd1 100644
--- a/boa_engine/src/syntax/ast/expression/mod.rs
+++ b/boa_ast/src/expression/mod.rs
@@ -33,9 +33,9 @@ mod spread;
mod tagged_template;
mod r#yield;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
pub use call::{Call, SuperCall};
-pub use identifier::Identifier;
+pub use identifier::{Identifier, RESERVED_IDENTIFIERS_STRICT};
pub use new::New;
pub use optional::{Optional, OptionalOperation, OptionalOperationKind};
pub use r#await::Await;
@@ -50,7 +50,7 @@ pub mod operator;
/// The `Expression` Parse Node.
///
/// See the [module level documentation][self] for more information.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
/// The JavaScript `this` keyword refers to the object it belongs to.
@@ -199,7 +199,8 @@ impl Expression {
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
// TODO: replace with a visitor
#[inline]
- pub(crate) fn contains_arguments(&self) -> bool {
+ #[must_use]
+ pub fn contains_arguments(&self) -> bool {
match self {
Expression::Identifier(ident) => *ident == Sym::ARGUMENTS,
Expression::Function(_)
@@ -239,7 +240,8 @@ impl Expression {
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
// TODO: replace with a visitor
- pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
+ #[must_use]
+ pub fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
Expression::This => symbol == ContainsSymbol::This,
Expression::Identifier(_)
diff --git a/boa_engine/src/syntax/ast/expression/new.rs b/boa_ast/src/expression/new.rs
similarity index 82%
rename from boa_engine/src/syntax/ast/expression/new.rs
rename to boa_ast/src/expression/new.rs
index 9b8c0a70880..90fb74f8332 100644
--- a/boa_engine/src/syntax/ast/expression/new.rs
+++ b/boa_ast/src/expression/new.rs
@@ -1,5 +1,5 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Call, ContainsSymbol};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Call, ContainsSymbol};
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;
@@ -20,7 +20,7 @@ use super::Expression;
///
/// [spec]: https://tc39.es/ecma262/#prod-NewExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct New {
call: Call,
@@ -29,18 +29,21 @@ pub struct New {
impl New {
/// Gets the constructor of the new expression.
#[inline]
+ #[must_use]
pub fn constructor(&self) -> &Expression {
self.call.function()
}
/// Retrieves the arguments passed to the constructor.
#[inline]
- pub fn args(&self) -> &[Expression] {
+ #[must_use]
+ pub fn arguments(&self) -> &[Expression] {
self.call.args()
}
/// Returns the inner call expression.
- pub(crate) fn call(&self) -> &Call {
+ #[must_use]
+ pub fn call(&self) -> &Call {
&self.call
}
@@ -91,16 +94,3 @@ impl VisitWith for New {
visitor.visit_call_mut(&mut self.call)
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- function MyClass() {}
- let inst = new MyClass();
- "#,
- );
- }
-}
diff --git a/boa_ast/src/expression/operator/assign/mod.rs b/boa_ast/src/expression/operator/assign/mod.rs
new file mode 100644
index 00000000000..c11e7ba0e57
--- /dev/null
+++ b/boa_ast/src/expression/operator/assign/mod.rs
@@ -0,0 +1,207 @@
+//! Assignment expression nodes, as defined by the [spec].
+//!
+//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right
+//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple
+//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=`
+//! only allow ["simple"][simple] left hand side expressions as an assignment target.
+//!
+//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
+//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
+//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression
+//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype
+
+mod op;
+
+use core::ops::ControlFlow;
+pub use op::*;
+
+use boa_interner::{Interner, Sym, ToInternedString};
+
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
+ expression::{access::PropertyAccess, identifier::Identifier, Expression},
+ pattern::Pattern,
+ ContainsSymbol,
+};
+
+/// An assignment operator expression.
+///
+/// See the [module level documentation][self] for more information.
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug, PartialEq)]
+pub struct Assign {
+ op: AssignOp,
+ lhs: Box,
+ rhs: Box,
+}
+
+impl Assign {
+ /// Creates an `Assign` AST Expression.
+ #[must_use]
+ pub fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self {
+ Self {
+ op,
+ lhs: Box::new(lhs),
+ rhs: Box::new(rhs),
+ }
+ }
+
+ /// Gets the operator of the assignment operation.
+ #[inline]
+ #[must_use]
+ pub fn op(&self) -> AssignOp {
+ self.op
+ }
+
+ /// Gets the left hand side of the assignment operation.
+ #[inline]
+ #[must_use]
+ pub fn lhs(&self) -> &AssignTarget {
+ &self.lhs
+ }
+
+ /// Gets the right hand side of the assignment operation.
+ #[inline]
+ #[must_use]
+ pub fn rhs(&self) -> &Expression {
+ &self.rhs
+ }
+
+ #[inline]
+ pub(crate) fn contains_arguments(&self) -> bool {
+ (match &*self.lhs {
+ AssignTarget::Identifier(ident) => *ident == Sym::ARGUMENTS,
+ AssignTarget::Access(access) => access.contains_arguments(),
+ AssignTarget::Pattern(pattern) => pattern.contains_arguments(),
+ } || self.rhs.contains_arguments())
+ }
+
+ #[inline]
+ pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
+ (match &*self.lhs {
+ AssignTarget::Identifier(_) => false,
+ AssignTarget::Access(access) => access.contains(symbol),
+ AssignTarget::Pattern(pattern) => pattern.contains(symbol),
+ } || self.rhs.contains(symbol))
+ }
+}
+
+impl ToInternedString for Assign {
+ #[inline]
+ fn to_interned_string(&self, interner: &Interner) -> String {
+ format!(
+ "{} {} {}",
+ self.lhs.to_interned_string(interner),
+ self.op,
+ self.rhs.to_interned_string(interner)
+ )
+ }
+}
+
+impl From for Expression {
+ #[inline]
+ fn from(op: Assign) -> Self {
+ Self::Assign(op)
+ }
+}
+
+impl VisitWith for Assign {
+ fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
+ where
+ V: Visitor<'a>,
+ {
+ try_break!(visitor.visit_assign_target(&self.lhs));
+ visitor.visit_expression(&self.rhs)
+ }
+
+ fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow
+ where
+ V: VisitorMut<'a>,
+ {
+ try_break!(visitor.visit_assign_target_mut(&mut self.lhs));
+ visitor.visit_expression_mut(&mut self.rhs)
+ }
+}
+
+/// The valid left-hand-side expressions of an assignment operator. Also called
+/// [`LeftHandSideExpression`][spec] in the spec.
+///
+/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug, PartialEq)]
+pub enum AssignTarget {
+ /// A simple identifier, such as `a`.
+ Identifier(Identifier),
+ /// A property access, such as `a.prop`.
+ Access(PropertyAccess),
+ /// A pattern assignment, such as `{a, b, ...c}`.
+ Pattern(Pattern),
+}
+
+impl AssignTarget {
+ /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].
+ /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.
+ #[must_use]
+ pub fn from_expression(
+ expression: &Expression,
+ strict: bool,
+ destructure: bool,
+ ) -> Option {
+ match expression {
+ Expression::Identifier(id) => Some(Self::Identifier(*id)),
+ Expression::PropertyAccess(access) => Some(Self::Access(access.clone())),
+ Expression::ObjectLiteral(object) if destructure => {
+ let pattern = object.to_pattern(strict)?;
+ Some(Self::Pattern(pattern.into()))
+ }
+ Expression::ArrayLiteral(array) if destructure => {
+ let pattern = array.to_pattern(strict)?;
+ Some(Self::Pattern(pattern.into()))
+ }
+ _ => None,
+ }
+ }
+}
+
+impl ToInternedString for AssignTarget {
+ #[inline]
+ fn to_interned_string(&self, interner: &Interner) -> String {
+ match self {
+ AssignTarget::Identifier(id) => id.to_interned_string(interner),
+ AssignTarget::Access(access) => access.to_interned_string(interner),
+ AssignTarget::Pattern(pattern) => pattern.to_interned_string(interner),
+ }
+ }
+}
+
+impl From for AssignTarget {
+ #[inline]
+ fn from(target: Identifier) -> Self {
+ Self::Identifier(target)
+ }
+}
+
+impl VisitWith for AssignTarget {
+ fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
+ where
+ V: Visitor<'a>,
+ {
+ match self {
+ AssignTarget::Identifier(id) => visitor.visit_identifier(id),
+ AssignTarget::Access(pa) => visitor.visit_property_access(pa),
+ AssignTarget::Pattern(pat) => visitor.visit_pattern(pat),
+ }
+ }
+
+ fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow
+ where
+ V: VisitorMut<'a>,
+ {
+ match self {
+ AssignTarget::Identifier(id) => visitor.visit_identifier_mut(id),
+ AssignTarget::Access(pa) => visitor.visit_property_access_mut(pa),
+ AssignTarget::Pattern(pat) => visitor.visit_pattern_mut(pat),
+ }
+ }
+}
diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/op.rs b/boa_ast/src/expression/operator/assign/op.rs
similarity index 99%
rename from boa_engine/src/syntax/ast/expression/operator/assign/op.rs
rename to boa_ast/src/expression/operator/assign/op.rs
index 5900b2745d2..8a815a022d0 100644
--- a/boa_engine/src/syntax/ast/expression/operator/assign/op.rs
+++ b/boa_ast/src/expression/operator/assign/op.rs
@@ -11,7 +11,7 @@
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AssignOp {
/// The assignment operator assigns the value of the right operand to the left operand.
diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs b/boa_ast/src/expression/operator/binary/mod.rs
similarity index 91%
rename from boa_engine/src/syntax/ast/expression/operator/binary/mod.rs
rename to boa_ast/src/expression/operator/binary/mod.rs
index d46ea962d41..15daa000e16 100644
--- a/boa_engine/src/syntax/ast/expression/operator/binary/mod.rs
+++ b/boa_ast/src/expression/operator/binary/mod.rs
@@ -21,14 +21,14 @@ pub use op::*;
use boa_interner::{Interner, ToInternedString};
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, ContainsSymbol};
/// Binary operations require two operands, one before the operator and one after the operator.
///
/// See the [module level documentation][self] for more information.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Binary {
op: BinaryOp,
@@ -38,7 +38,8 @@ pub struct Binary {
impl Binary {
/// Creates a `BinOp` AST Expression.
- pub(in crate::syntax) fn new(op: BinaryOp, lhs: Expression, rhs: Expression) -> Self {
+ #[must_use]
+ pub fn new(op: BinaryOp, lhs: Expression, rhs: Expression) -> Self {
Self {
op,
lhs: Box::new(lhs),
@@ -48,18 +49,21 @@ impl Binary {
/// Gets the binary operation of the Expression.
#[inline]
+ #[must_use]
pub fn op(&self) -> BinaryOp {
self.op
}
/// Gets the left hand side of the binary operation.
#[inline]
+ #[must_use]
pub fn lhs(&self) -> &Expression {
&self.lhs
}
/// Gets the right hand side of the binary operation.
#[inline]
+ #[must_use]
pub fn rhs(&self) -> &Expression {
&self.rhs
}
diff --git a/boa_engine/src/syntax/ast/expression/operator/binary/op.rs b/boa_ast/src/expression/operator/binary/op.rs
similarity index 98%
rename from boa_engine/src/syntax/ast/expression/operator/binary/op.rs
rename to boa_ast/src/expression/operator/binary/op.rs
index cb6739b5f3d..6430bb00ba5 100644
--- a/boa_engine/src/syntax/ast/expression/operator/binary/op.rs
+++ b/boa_ast/src/expression/operator/binary/op.rs
@@ -3,7 +3,7 @@
use std::fmt::{Display, Formatter, Result};
/// This represents a binary operation between two values.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinaryOp {
/// Numeric operation.
@@ -85,7 +85,7 @@ impl Display for BinaryOp {
/// - [MDN documentation][mdn]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ArithmeticOp {
/// The addition operator produces the sum of numeric operands or string concatenation.
@@ -195,7 +195,7 @@ impl Display for ArithmeticOp {
/// - [MDN documentation][mdn]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BitwiseOp {
/// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1.
@@ -318,7 +318,7 @@ impl Display for BitwiseOp {
///
/// [spec]: tc39.es/ecma262/#sec-testing-and-comparison-operations
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Comparison
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RelationalOp {
/// The equality operator converts the operands if they are not of the same type, then applies
@@ -511,7 +511,7 @@ impl Display for RelationalOp {
///
/// [spec]: https://tc39.es/ecma262/#sec-binary-logical-operators
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Logical
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LogicalOp {
/// The logical AND operator returns the value of the first operand if it can be coerced into `false`;
diff --git a/boa_engine/src/syntax/ast/expression/operator/conditional.rs b/boa_ast/src/expression/operator/conditional.rs
similarity index 93%
rename from boa_engine/src/syntax/ast/expression/operator/conditional.rs
rename to boa_ast/src/expression/operator/conditional.rs
index fc0f19eddec..12983acbf3c 100644
--- a/boa_engine/src/syntax/ast/expression/operator/conditional.rs
+++ b/boa_ast/src/expression/operator/conditional.rs
@@ -1,6 +1,6 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, ContainsSymbol};
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;
@@ -18,7 +18,7 @@ use core::ops::ControlFlow;
///
/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Conditional {
condition: Box,
@@ -29,24 +29,28 @@ pub struct Conditional {
impl Conditional {
/// Gets the condition of the `Conditional` expression.
#[inline]
+ #[must_use]
pub fn condition(&self) -> &Expression {
&self.condition
}
/// Gets the expression returned if `condition` is truthy.
#[inline]
+ #[must_use]
pub fn if_true(&self) -> &Expression {
&self.if_true
}
/// Gets the expression returned if `condition` is falsy.
#[inline]
+ #[must_use]
pub fn if_false(&self) -> &Expression {
&self.if_false
}
/// Creates a `Conditional` AST Expression.
#[inline]
+ #[must_use]
pub fn new(condition: Expression, if_true: Expression, if_false: Expression) -> Self {
Self {
condition: Box::new(condition),
diff --git a/boa_engine/src/syntax/ast/expression/operator/mod.rs b/boa_ast/src/expression/operator/mod.rs
similarity index 96%
rename from boa_engine/src/syntax/ast/expression/operator/mod.rs
rename to boa_ast/src/expression/operator/mod.rs
index 4623c8ce99b..2591935c1ce 100644
--- a/boa_engine/src/syntax/ast/expression/operator/mod.rs
+++ b/boa_ast/src/expression/operator/mod.rs
@@ -18,6 +18,3 @@ pub mod binary;
pub mod unary;
pub use self::{assign::Assign, binary::Binary, conditional::Conditional, unary::Unary};
-
-#[cfg(test)]
-mod tests;
diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs b/boa_ast/src/expression/operator/unary/mod.rs
similarity index 90%
rename from boa_engine/src/syntax/ast/expression/operator/unary/mod.rs
rename to boa_ast/src/expression/operator/unary/mod.rs
index baa262f940f..0f29998195d 100644
--- a/boa_engine/src/syntax/ast/expression/operator/unary/mod.rs
+++ b/boa_ast/src/expression/operator/unary/mod.rs
@@ -18,8 +18,8 @@ pub use op::*;
use boa_interner::{Interner, ToInternedString};
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, ContainsSymbol};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, ContainsSymbol};
/// A unary expression is an operation with only one operand.
///
@@ -29,7 +29,7 @@ use crate::syntax::ast::{expression::Expression, ContainsSymbol};
///
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Unary {
op: UnaryOp,
@@ -38,7 +38,8 @@ pub struct Unary {
impl Unary {
/// Creates a new `UnaryOp` AST Expression.
- pub(in crate::syntax) fn new(op: UnaryOp, target: Expression) -> Self {
+ #[must_use]
+ pub fn new(op: UnaryOp, target: Expression) -> Self {
Self {
op,
target: Box::new(target),
@@ -47,12 +48,14 @@ impl Unary {
/// Gets the unary operation of the Expression.
#[inline]
+ #[must_use]
pub fn op(&self) -> UnaryOp {
self.op
}
/// Gets the target of this unary operator.
#[inline]
+ #[must_use]
pub fn target(&self) -> &Expression {
self.target.as_ref()
}
diff --git a/boa_engine/src/syntax/ast/expression/operator/unary/op.rs b/boa_ast/src/expression/operator/unary/op.rs
similarity index 99%
rename from boa_engine/src/syntax/ast/expression/operator/unary/op.rs
rename to boa_ast/src/expression/operator/unary/op.rs
index a14a634d550..63527dce239 100644
--- a/boa_engine/src/syntax/ast/expression/operator/unary/op.rs
+++ b/boa_ast/src/expression/operator/unary/op.rs
@@ -10,7 +10,7 @@
///
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UnaryOp {
/// The increment operator increments (adds one to) its operand and returns a value.
diff --git a/boa_engine/src/syntax/ast/expression/optional.rs b/boa_ast/src/expression/optional.rs
similarity index 95%
rename from boa_engine/src/syntax/ast/expression/optional.rs
rename to boa_ast/src/expression/optional.rs
index 4919578ac76..42f668bee55 100644
--- a/boa_engine/src/syntax/ast/expression/optional.rs
+++ b/boa_ast/src/expression/optional.rs
@@ -1,14 +1,14 @@
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{join_nodes, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{join_nodes, ContainsSymbol};
use super::{access::PropertyAccessField, Expression};
/// List of valid operations in an [`Optional`] chain.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum OptionalOperationKind {
/// A property access (`a?.prop`).
@@ -91,7 +91,7 @@ impl VisitWith for OptionalOperationKind {
/// (`?.item`) will force the expression to return `undefined` if the target is `undefined` or `null`.
/// In contrast, a non-shorted operation (`.prop`) will try to access the property, even if the target
/// is `undefined` or `null`.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct OptionalOperation {
kind: OptionalOperationKind,
@@ -101,11 +101,13 @@ pub struct OptionalOperation {
impl OptionalOperation {
/// Creates a new `OptionalOperation`.
#[inline]
+ #[must_use]
pub fn new(kind: OptionalOperationKind, shorted: bool) -> Self {
Self { kind, shorted }
}
/// Gets the kind of operation.
#[inline]
+ #[must_use]
pub fn kind(&self) -> &OptionalOperationKind {
&self.kind
}
@@ -113,6 +115,7 @@ impl OptionalOperation {
/// Returns `true` if the operation short-circuits the [`Optional`] chain when the target is
/// `undefined` or `null`.
#[inline]
+ #[must_use]
pub fn shorted(&self) -> bool {
self.shorted
}
@@ -200,7 +203,7 @@ impl VisitWith for OptionalOperation {
///
/// [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Optional {
target: Box,
@@ -234,6 +237,7 @@ impl VisitWith for Optional {
impl Optional {
/// Creates a new `Optional` expression.
#[inline]
+ #[must_use]
pub fn new(target: Expression, chain: Box<[OptionalOperation]>) -> Self {
Self {
target: Box::new(target),
@@ -243,12 +247,14 @@ impl Optional {
/// Gets the target of this `Optional` expression.
#[inline]
+ #[must_use]
pub fn target(&self) -> &Expression {
self.target.as_ref()
}
/// Gets the chain of accesses and calls that will be applied to the target at runtime.
#[inline]
+ #[must_use]
pub fn chain(&self) -> &[OptionalOperation] {
self.chain.as_ref()
}
diff --git a/boa_engine/src/syntax/ast/expression/spread.rs b/boa_ast/src/expression/spread.rs
similarity index 66%
rename from boa_engine/src/syntax/ast/expression/spread.rs
rename to boa_ast/src/expression/spread.rs
index 5dc32d36ba3..68805d08a00 100644
--- a/boa_engine/src/syntax/ast/expression/spread.rs
+++ b/boa_ast/src/expression/spread.rs
@@ -1,8 +1,8 @@
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::ContainsSymbol;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::ContainsSymbol;
use super::Expression;
@@ -22,8 +22,8 @@ use super::Expression;
///
/// [spec]: https://tc39.es/ecma262/#prod-SpreadElement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "deser", serde(transparent))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Clone, Debug, PartialEq)]
pub struct Spread {
target: Box,
@@ -32,12 +32,14 @@ pub struct Spread {
impl Spread {
/// Gets the target expression to be expanded by the spread operator.
#[inline]
+ #[must_use]
pub fn target(&self) -> &Expression {
&self.target
}
/// Creates a `Spread` AST Expression.
#[inline]
+ #[must_use]
pub fn new(target: Expression) -> Self {
Self {
target: Box::new(target),
@@ -84,54 +86,3 @@ impl VisitWith for Spread {
visitor.visit_expression_mut(&mut self.target)
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::exec;
-
- #[test]
- fn spread_with_new() {
- let scenario = r#"
- function F(m) {
- this.m = m;
- }
- function f(...args) {
- return new F(...args);
- }
- let a = f('message');
- a.m;
- "#;
- assert_eq!(&exec(scenario), r#""message""#);
- }
-
- #[test]
- fn spread_with_call() {
- let scenario = r#"
- function f(m) {
- return m;
- }
- function g(...args) {
- return f(...args);
- }
- let a = g('message');
- a;
- "#;
- assert_eq!(&exec(scenario), r#""message""#);
- }
-
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- function f(m) {
- return m;
- }
- function g(...args) {
- return f(...args);
- }
- let a = g("message");
- a;
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/tagged_template.rs b/boa_ast/src/expression/tagged_template.rs
similarity index 79%
rename from boa_engine/src/syntax/ast/expression/tagged_template.rs
rename to boa_ast/src/expression/tagged_template.rs
index 78b1f8f45cd..0557d32e76c 100644
--- a/boa_engine/src/syntax/ast/expression/tagged_template.rs
+++ b/boa_ast/src/expression/tagged_template.rs
@@ -1,9 +1,9 @@
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::ContainsSymbol;
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::ContainsSymbol;
use super::Expression;
@@ -14,7 +14,7 @@ use super::Expression;
///
/// [moz]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
/// [spec]: https://tc39.es/ecma262/#sec-tagged-templates
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct TaggedTemplate {
tag: Box,
@@ -27,6 +27,7 @@ impl TaggedTemplate {
/// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and
/// the expressions.
#[inline]
+ #[must_use]
pub fn new(
tag: Expression,
raws: Box<[Sym]>,
@@ -41,23 +42,31 @@ impl TaggedTemplate {
}
}
+ /// Gets the tag function of the template.
#[inline]
- pub(crate) fn tag(&self) -> &Expression {
+ #[must_use]
+ pub fn tag(&self) -> &Expression {
&self.tag
}
+ /// Gets the inner raw strings of the template.
#[inline]
- pub(crate) fn raws(&self) -> &[Sym] {
+ #[must_use]
+ pub fn raws(&self) -> &[Sym] {
&self.raws
}
+ /// Gets the cooked strings of the template.
#[inline]
- pub(crate) fn cookeds(&self) -> &[Option] {
+ #[must_use]
+ pub fn cookeds(&self) -> &[Option] {
&self.cookeds
}
+ /// Gets the interpolated expressions of the template.
#[inline]
- pub(crate) fn exprs(&self) -> &[Expression] {
+ #[must_use]
+ pub fn exprs(&self) -> &[Expression] {
&self.exprs
}
@@ -131,26 +140,3 @@ impl VisitWith for TaggedTemplate {
ControlFlow::Continue(())
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn tagged_template() {
- let scenario = r#"
- function tag(t, ...args) {
- let a = []
- a = a.concat([t[0], t[1], t[2]]);
- a = a.concat([t.raw[0], t.raw[1], t.raw[2]]);
- a = a.concat([args[0], args[1]]);
- return a
- }
- let a = 10;
- tag`result: ${a} \x26 ${a+10}`;
- "#;
-
- assert_eq!(
- &crate::exec(scenario),
- r#"[ "result: ", " & ", "", "result: ", " \x26 ", "", 10, 20 ]"#
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/yield.rs b/boa_ast/src/expression/yield.rs
similarity index 93%
rename from boa_engine/src/syntax/ast/expression/yield.rs
rename to boa_ast/src/expression/yield.rs
index 6284f316fc4..01e67d2ed3b 100644
--- a/boa_engine/src/syntax/ast/expression/yield.rs
+++ b/boa_ast/src/expression/yield.rs
@@ -1,8 +1,8 @@
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::ContainsSymbol;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::ContainsSymbol;
use super::Expression;
@@ -14,7 +14,7 @@ use super::Expression;
///
/// [spec]: https://tc39.es/ecma262/#prod-YieldExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Yield {
target: Option>,
@@ -30,12 +30,14 @@ impl Yield {
/// Returns `true` if this `Yield` statement delegates to another generator or iterable object.
#[inline]
+ #[must_use]
pub fn delegate(&self) -> bool {
self.delegate
}
/// Creates a `Yield` AST Expression.
#[inline]
+ #[must_use]
pub fn new(expr: Option, delegate: bool) -> Self {
Self {
target: expr.map(Box::new),
diff --git a/boa_engine/src/syntax/ast/function/arrow_function.rs b/boa_ast/src/function/arrow_function.rs
similarity index 81%
rename from boa_engine/src/syntax/ast/function/arrow_function.rs
rename to boa_ast/src/function/arrow_function.rs
index ad2003d7eb3..c7cb7e88e74 100644
--- a/boa_engine/src/syntax/ast/function/arrow_function.rs
+++ b/boa_ast/src/function/arrow_function.rs
@@ -1,9 +1,9 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
expression::{Expression, Identifier},
join_nodes, ContainsSymbol, StatementList,
};
-use crate::try_break;
use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow;
@@ -18,7 +18,7 @@ use super::FormalParameterList;
///
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct ArrowFunction {
name: Option,
@@ -29,11 +29,8 @@ pub struct ArrowFunction {
impl ArrowFunction {
/// Creates a new `ArrowFunctionDecl` AST Expression.
#[inline]
- pub(in crate::syntax) fn new(
- name: Option,
- params: FormalParameterList,
- body: StatementList,
- ) -> Self {
+ #[must_use]
+ pub fn new(name: Option, params: FormalParameterList, body: StatementList) -> Self {
Self {
name,
parameters: params,
@@ -43,6 +40,7 @@ impl ArrowFunction {
/// Gets the name of the function declaration.
#[inline]
+ #[must_use]
pub fn name(&self) -> Option {
self.name
}
@@ -55,13 +53,15 @@ impl ArrowFunction {
/// Gets the list of parameters of the arrow function.
#[inline]
- pub(crate) fn parameters(&self) -> &FormalParameterList {
+ #[must_use]
+ pub fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the arrow function.
#[inline]
- pub(crate) fn body(&self) -> &StatementList {
+ #[must_use]
+ pub fn body(&self) -> &StatementList {
&self.body
}
@@ -88,7 +88,7 @@ impl ArrowFunction {
impl ToIndentedString for ArrowFunction {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
- let mut buf = format!("({}", join_nodes(interner, &self.parameters.parameters));
+ let mut buf = format!("({}", join_nodes(interner, self.parameters.as_ref()));
if self.body().statements().is_empty() {
buf.push_str(") => {}");
} else {
@@ -131,19 +131,3 @@ impl VisitWith for ArrowFunction {
visitor.visit_statement_list_mut(&mut self.body)
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- let arrow_func = (a, b) => {
- console.log("in multi statement arrow");
- console.log(b);
- };
- let arrow_func_2 = (a, b) => {};
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/function/async_function.rs b/boa_ast/src/function/async_function.rs
similarity index 82%
rename from boa_engine/src/syntax/ast/function/async_function.rs
rename to boa_ast/src/function/async_function.rs
index 867c8fb8daa..556a02c016e 100644
--- a/boa_engine/src/syntax/ast/function/async_function.rs
+++ b/boa_ast/src/function/async_function.rs
@@ -1,11 +1,11 @@
//! Async Function Expression.
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
expression::{Expression, Identifier},
join_nodes, Declaration, StatementList,
};
-use crate::try_break;
use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow;
@@ -19,7 +19,7 @@ use super::FormalParameterList;
///
/// [spec]: https://tc39.es/ecma262/#sec-async-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncFunction {
name: Option,
@@ -30,7 +30,8 @@ pub struct AsyncFunction {
impl AsyncFunction {
/// Creates a new function expression
#[inline]
- pub(in crate::syntax) fn new(
+ #[must_use]
+ pub fn new(
name: Option,
parameters: FormalParameterList,
body: StatementList,
@@ -44,18 +45,21 @@ impl AsyncFunction {
/// Gets the name of the function declaration.
#[inline]
+ #[must_use]
pub fn name(&self) -> Option {
self.name
}
/// Gets the list of parameters of the function declaration.
#[inline]
+ #[must_use]
pub fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the function declaration.
#[inline]
+ #[must_use]
pub fn body(&self) -> &StatementList {
&self.body
}
@@ -69,7 +73,7 @@ impl ToIndentedString for AsyncFunction {
}
buf.push_str(&format!(
"({}",
- join_nodes(interner, &self.parameters.parameters)
+ join_nodes(interner, self.parameters.as_ref())
));
if self.body().statements().is_empty() {
buf.push_str(") {}");
@@ -121,22 +125,3 @@ impl VisitWith for AsyncFunction {
visitor.visit_statement_list_mut(&mut self.body)
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- async function async_func(a, b) {
- console.log(a);
- }
- async function async_func_2(a, b) {}
- pass_async_func(async function(a, b) {
- console.log("in async callback", a);
- });
- pass_async_func(async function(a, b) {});
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/function/async_generator.rs b/boa_ast/src/function/async_generator.rs
similarity index 92%
rename from boa_engine/src/syntax/ast/function/async_generator.rs
rename to boa_ast/src/function/async_generator.rs
index 93db559e5bf..fe85194aad2 100644
--- a/boa_engine/src/syntax/ast/function/async_generator.rs
+++ b/boa_ast/src/function/async_generator.rs
@@ -1,11 +1,11 @@
//! Async Generator Expression
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
block_to_string,
expression::{Expression, Identifier},
join_nodes, Declaration, StatementList,
};
-use crate::try_break;
use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow;
@@ -18,7 +18,7 @@ use super::FormalParameterList;
///
/// [spec]: https://tc39.es/ecma262/#sec-async-generator-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncGenerator {
name: Option,
@@ -29,7 +29,8 @@ pub struct AsyncGenerator {
impl AsyncGenerator {
/// Creates a new async generator expression
#[inline]
- pub(in crate::syntax) fn new(
+ #[must_use]
+ pub fn new(
name: Option,
parameters: FormalParameterList,
body: StatementList,
@@ -43,18 +44,21 @@ impl AsyncGenerator {
/// Gets the name of the async generator expression
#[inline]
+ #[must_use]
pub fn name(&self) -> Option {
self.name
}
/// Gets the list of parameters of the async generator expression
#[inline]
+ #[must_use]
pub fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the async generator expression
#[inline]
+ #[must_use]
pub fn body(&self) -> &StatementList {
&self.body
}
@@ -68,7 +72,7 @@ impl ToIndentedString for AsyncGenerator {
}
buf.push_str(&format!(
"({}) {}",
- join_nodes(interner, &self.parameters.parameters),
+ join_nodes(interner, self.parameters.as_ref()),
block_to_string(&self.body, interner, indentation)
));
diff --git a/boa_engine/src/syntax/ast/function/class.rs b/boa_ast/src/function/class.rs
similarity index 83%
rename from boa_engine/src/syntax/ast/function/class.rs
rename to boa_ast/src/function/class.rs
index 302313f8033..ba2586a2bd1 100644
--- a/boa_engine/src/syntax/ast/function/class.rs
+++ b/boa_ast/src/function/class.rs
@@ -1,17 +1,14 @@
use core::ops::ControlFlow;
use std::borrow::Cow;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
- string::ToStringEscaped,
- syntax::ast::{
- block_to_string,
- expression::{Expression, Identifier},
- join_nodes,
- property::{MethodDefinition, PropertyName},
- ContainsSymbol, Declaration, StatementList, StatementListItem,
- },
+ block_to_string,
+ expression::{Expression, Identifier},
+ join_nodes,
+ property::{MethodDefinition, PropertyName},
try_break,
+ visitor::{VisitWith, Visitor, VisitorMut},
+ ContainsSymbol, Declaration, StatementList, StatementListItem, ToStringEscaped,
};
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
@@ -24,7 +21,7 @@ use super::Function;
///
/// [spec]: https://tc39.es/ecma262/#sec-class-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Class {
name: Option,
@@ -36,7 +33,8 @@ pub struct Class {
impl Class {
/// Creates a new class declaration.
#[inline]
- pub(in crate::syntax) fn new(
+ #[must_use]
+ pub fn new(
name: Option,
super_ref: Option,
constructor: Option,
@@ -52,25 +50,29 @@ impl Class {
/// Returns the name of the class.
#[inline]
- pub(crate) fn name(&self) -> Option {
+ #[must_use]
+ pub fn name(&self) -> Option {
self.name
}
/// Returns the super class ref of the class.
#[inline]
- pub(crate) fn super_ref(&self) -> Option<&Expression> {
+ #[must_use]
+ pub fn super_ref(&self) -> Option<&Expression> {
self.super_ref.as_ref()
}
/// Returns the constructor of the class.
#[inline]
- pub(crate) fn constructor(&self) -> Option<&Function> {
+ #[must_use]
+ pub fn constructor(&self) -> Option<&Function> {
self.constructor.as_ref()
}
/// Gets the list of all fields defined on the class.
#[inline]
- pub(crate) fn elements(&self) -> &[ClassElement] {
+ #[must_use]
+ pub fn elements(&self) -> &[ClassElement] {
&self.elements
}
@@ -126,7 +128,7 @@ impl ToIndentedString for Class {
if let Some(expr) = &self.constructor {
buf.push_str(&format!(
"{indentation}constructor({}) {}\n",
- join_nodes(interner, &expr.parameters().parameters),
+ join_nodes(interner, expr.parameters().as_ref()),
block_to_string(expr.body(), interner, indent_n + 1)
));
}
@@ -145,16 +147,16 @@ impl ToIndentedString for Class {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Generator(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Async(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
},
match &method {
@@ -188,16 +190,16 @@ impl ToIndentedString for Class {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Generator(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Async(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
},
match &method {
@@ -258,16 +260,16 @@ impl ToIndentedString for Class {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Generator(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Async(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
},
match &method {
@@ -301,16 +303,16 @@ impl ToIndentedString for Class {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
| MethodDefinition::Ordinary(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Generator(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::AsyncGenerator(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
MethodDefinition::Async(expr) => {
- join_nodes(interner, &expr.parameters().parameters)
+ join_nodes(interner, expr.parameters().as_ref())
}
},
match &method {
@@ -423,7 +425,7 @@ impl VisitWith for Class {
/// An element that can be within a [`Class`], as defined by the [spec].
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassElement
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum ClassElement {
/// A method definition, including `get` and `set` accessors.
@@ -564,113 +566,3 @@ impl VisitWith for ClassElement {
}
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::syntax::ast::test_formatting;
-
- #[test]
- fn class_declaration_empty() {
- test_formatting(
- r#"
- class A {}
- "#,
- );
- }
-
- #[test]
- fn class_declaration_empty_extends() {
- test_formatting(
- r#"
- class A extends Object {}
- "#,
- );
- }
-
- #[test]
- fn class_declaration_constructor() {
- test_formatting(
- r#"
- class A {
- constructor(a, b, c) {
- this.value = a + b + c;
- }
- }
- "#,
- );
- }
-
- #[test]
- fn class_declaration_elements() {
- test_formatting(
- r#"
- class A {
- a;
- b = 1;
- c() {}
- d(a, b, c) {
- return a + b + c;
- }
- set e(value) {}
- get e() {}
- set(a, b) {}
- get(a, b) {}
- }
- "#,
- );
- }
-
- #[test]
- fn class_declaration_elements_private() {
- test_formatting(
- r#"
- class A {
- #a;
- #b = 1;
- #c() {}
- #d(a, b, c) {
- return a + b + c;
- }
- set #e(value) {}
- get #e() {}
- }
- "#,
- );
- }
-
- #[test]
- fn class_declaration_elements_static() {
- test_formatting(
- r#"
- class A {
- static a;
- static b = 1;
- static c() {}
- static d(a, b, c) {
- return a + b + c;
- }
- static set e(value) {}
- static get e() {}
- }
- "#,
- );
- }
-
- #[test]
- fn class_declaration_elements_private_static() {
- test_formatting(
- r#"
- class A {
- static #a;
- static #b = 1;
- static #c() {}
- static #d(a, b, c) {
- return a + b + c;
- }
- static set #e(value) {}
- static get #e() {}
- }
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/function/generator.rs b/boa_ast/src/function/generator.rs
similarity index 92%
rename from boa_engine/src/syntax/ast/function/generator.rs
rename to boa_ast/src/function/generator.rs
index 89f240d0092..bae097453d5 100644
--- a/boa_engine/src/syntax/ast/function/generator.rs
+++ b/boa_ast/src/function/generator.rs
@@ -1,12 +1,12 @@
-use crate::syntax::ast::{
+use crate::{
block_to_string,
expression::{Expression, Identifier},
join_nodes, Declaration, StatementList,
};
use core::ops::ControlFlow;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, ToIndentedString};
use super::FormalParameterList;
@@ -20,7 +20,7 @@ use super::FormalParameterList;
///
/// [spec]: https://tc39.es/ecma262/#sec-generator-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Generator {
name: Option,
@@ -31,7 +31,8 @@ pub struct Generator {
impl Generator {
/// Creates a new generator expression
#[inline]
- pub(in crate::syntax) fn new(
+ #[must_use]
+ pub fn new(
name: Option,
parameters: FormalParameterList,
body: StatementList,
@@ -45,18 +46,21 @@ impl Generator {
/// Gets the name of the generator declaration.
#[inline]
+ #[must_use]
pub fn name(&self) -> Option {
self.name
}
/// Gets the list of parameters of the generator declaration.
#[inline]
+ #[must_use]
pub fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the generator declaration.
#[inline]
+ #[must_use]
pub fn body(&self) -> &StatementList {
&self.body
}
@@ -70,7 +74,7 @@ impl ToIndentedString for Generator {
}
buf.push_str(&format!(
"({}) {}",
- join_nodes(interner, &self.parameters.parameters),
+ join_nodes(interner, self.parameters.as_ref()),
block_to_string(&self.body, interner, indentation)
));
diff --git a/boa_engine/src/syntax/ast/function/mod.rs b/boa_ast/src/function/mod.rs
similarity index 65%
rename from boa_engine/src/syntax/ast/function/mod.rs
rename to boa_ast/src/function/mod.rs
index f9e6c2f90c0..34d0eede730 100644
--- a/boa_engine/src/syntax/ast/function/mod.rs
+++ b/boa_ast/src/function/mod.rs
@@ -33,17 +33,15 @@ pub use async_generator::AsyncGenerator;
pub use class::{Class, ClassElement};
use core::ops::ControlFlow;
pub use generator::Generator;
-pub use parameters::{FormalParameter, FormalParameterList};
+pub use parameters::{FormalParameter, FormalParameterList, FormalParameterListFlags};
-pub(crate) use parameters::FormalParameterListFlags;
-
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{block_to_string, join_nodes, StatementList};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{block_to_string, join_nodes, StatementList};
use boa_interner::{Interner, ToIndentedString};
use super::expression::{Expression, Identifier};
-use super::{ContainsSymbol, Declaration};
+use super::Declaration;
/// A function definition, as defined by the [spec].
///
@@ -55,7 +53,7 @@ use super::{ContainsSymbol, Declaration};
///
/// [spec]: https://tc39.es/ecma262/#sec-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Function {
name: Option,
@@ -66,7 +64,8 @@ pub struct Function {
impl Function {
/// Creates a new function expression
#[inline]
- pub(in crate::syntax) fn new(
+ #[must_use]
+ pub fn new(
name: Option,
parameters: FormalParameterList,
body: StatementList,
@@ -80,18 +79,21 @@ impl Function {
/// Gets the name of the function declaration.
#[inline]
+ #[must_use]
pub fn name(&self) -> Option {
self.name
}
/// Gets the list of parameters of the function declaration.
#[inline]
+ #[must_use]
pub fn parameters(&self) -> &FormalParameterList {
&self.parameters
}
/// Gets the body of the function declaration.
#[inline]
+ #[must_use]
pub fn body(&self) -> &StatementList {
&self.body
}
@@ -105,7 +107,7 @@ impl ToIndentedString for Function {
}
buf.push_str(&format!(
"({}) {}",
- join_nodes(interner, &self.parameters.parameters),
+ join_nodes(interner, self.parameters.as_ref()),
block_to_string(&self.body, interner, indentation)
));
@@ -127,47 +129,6 @@ impl From for Declaration {
}
}
-/// Helper function to check if a function contains a super call or super property access.
-pub(crate) fn function_contains_super(
- body: &StatementList,
- parameters: &FormalParameterList,
-) -> bool {
- for param in parameters.parameters.iter() {
- if param.variable().contains(ContainsSymbol::SuperCall)
- || param.variable().contains(ContainsSymbol::SuperProperty)
- {
- return true;
- }
- }
- for node in body.statements() {
- if node.contains(ContainsSymbol::SuperCall) || node.contains(ContainsSymbol::SuperProperty)
- {
- return true;
- }
- }
- false
-}
-
-/// Returns `true` if the function parameters or body contain a direct `super` call.
-///
-/// More information:
-/// - [ECMAScript specification][spec]
-///
-/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper
-pub(crate) fn has_direct_super(body: &StatementList, parameters: &FormalParameterList) -> bool {
- for param in parameters.parameters.iter() {
- if param.variable().contains(ContainsSymbol::SuperCall) {
- return true;
- }
- }
- for node in body.statements() {
- if node.contains(ContainsSymbol::SuperCall) {
- return true;
- }
- }
- false
-}
-
impl VisitWith for Function {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
where
@@ -191,34 +152,3 @@ impl VisitWith for Function {
visitor.visit_statement_list_mut(&mut self.body)
}
}
-
-#[cfg(test)]
-mod tests {
-
- #[test]
- fn duplicate_function_name() {
- let scenario = r#"
- function f () {}
- function f () {return 12;}
- f()
- "#;
-
- assert_eq!(&crate::exec(scenario), "12");
- }
-
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- function func(a, b) {
- console.log(a);
- }
- function func_2(a, b) {}
- pass_func(function(a, b) {
- console.log("in callback", a);
- });
- pass_func(function(a, b) {});
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/function/parameters.rs b/boa_ast/src/function/parameters.rs
similarity index 72%
rename from boa_engine/src/syntax/ast/function/parameters.rs
rename to boa_ast/src/function/parameters.rs
index 9386ec5bec8..512c785c2eb 100644
--- a/boa_engine/src/syntax/ast/function/parameters.rs
+++ b/boa_ast/src/function/parameters.rs
@@ -1,14 +1,11 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::{
- ast::{
- declaration::{Binding, Variable},
- expression::{Expression, Identifier},
- pattern::Pattern,
- ContainsSymbol, Position,
- },
- parser::ParseError,
+use crate::{
+ declaration::{Binding, Variable},
+ expression::{Expression, Identifier},
+ pattern::Pattern,
+ try_break,
+ visitor::{VisitWith, Visitor, VisitorMut},
+ ContainsSymbol,
};
-use crate::try_break;
use bitflags::bitflags;
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
@@ -17,30 +14,18 @@ use rustc_hash::FxHashSet;
/// A list of `FormalParameter`s that describes the parameters of a function, as defined by the [spec].
///
/// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FormalParameterList {
- pub(crate) parameters: Box<[FormalParameter]>,
- pub(crate) flags: FormalParameterListFlags,
- pub(crate) length: u32,
+ parameters: Box<[FormalParameter]>,
+ flags: FormalParameterListFlags,
+ length: u32,
}
impl FormalParameterList {
- /// Creates a new formal parameter list.
- pub(crate) fn new(
- parameters: Box<[FormalParameter]>,
- flags: FormalParameterListFlags,
- length: u32,
- ) -> Self {
- Self {
- parameters,
- flags,
- length,
- }
- }
-
/// Creates a new empty formal parameter list.
- pub(crate) fn empty() -> Self {
+ #[must_use]
+ pub fn new() -> Self {
Self {
parameters: Box::new([]),
flags: FormalParameterListFlags::default(),
@@ -48,61 +33,101 @@ impl FormalParameterList {
}
}
+ /// Creates a `FormalParameterList` from a list of [`FormalParameter`]s.
+ #[must_use]
+ pub fn from_parameters(parameters: Vec) -> Self {
+ let mut flags = FormalParameterListFlags::default();
+ let mut length = 0;
+ let mut names = FxHashSet::default();
+
+ for parameter in ¶meters {
+ let parameter_names = parameter.names();
+
+ for name in parameter_names {
+ if name == Sym::ARGUMENTS {
+ flags |= FormalParameterListFlags::HAS_ARGUMENTS;
+ }
+ if names.contains(&name) {
+ flags |= FormalParameterListFlags::HAS_DUPLICATES;
+ } else {
+ names.insert(name);
+ }
+ }
+
+ if parameter.is_rest_param() {
+ flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
+ }
+ if parameter.init().is_some() {
+ flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
+ }
+ if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier()
+ {
+ flags.remove(FormalParameterListFlags::IS_SIMPLE);
+ }
+ if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
+ || parameter.is_rest_param()
+ || parameter.init().is_some())
+ {
+ length += 1;
+ }
+ }
+
+ Self {
+ parameters: parameters.into(),
+ flags,
+ length,
+ }
+ }
+
/// Returns the length of the parameter list.
/// Note that this is not equal to the length of the parameters slice.
- pub(crate) fn length(&self) -> u32 {
+ #[must_use]
+ pub fn length(&self) -> u32 {
self.length
}
+ /// Returns the parameter list flags.
+ #[must_use]
+ pub fn flags(&self) -> FormalParameterListFlags {
+ self.flags
+ }
+
/// Indicates if the parameter list is simple.
- pub(crate) fn is_simple(&self) -> bool {
+ #[must_use]
+ pub fn is_simple(&self) -> bool {
self.flags.contains(FormalParameterListFlags::IS_SIMPLE)
}
/// Indicates if the parameter list has duplicate parameters.
- pub(crate) fn has_duplicates(&self) -> bool {
+ #[must_use]
+ pub fn has_duplicates(&self) -> bool {
self.flags
.contains(FormalParameterListFlags::HAS_DUPLICATES)
}
/// Indicates if the parameter list has a rest parameter.
- pub(crate) fn has_rest_parameter(&self) -> bool {
+ #[must_use]
+ pub fn has_rest_parameter(&self) -> bool {
self.flags
.contains(FormalParameterListFlags::HAS_REST_PARAMETER)
}
/// Indicates if the parameter list has expressions in it's parameters.
- pub(crate) fn has_expressions(&self) -> bool {
+ #[must_use]
+ pub fn has_expressions(&self) -> bool {
self.flags
.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
}
/// Indicates if the parameter list has parameters named 'arguments'.
- pub(crate) fn has_arguments(&self) -> bool {
+ #[must_use]
+ pub fn has_arguments(&self) -> bool {
self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS)
}
- /// Helper to check if any parameter names are declared in the given list.
- pub(crate) fn name_in_lexically_declared_names(
- &self,
- names: &[Identifier],
- position: Position,
- ) -> Result<(), ParseError> {
- for parameter in self.parameters.iter() {
- for name in ¶meter.names() {
- if names.contains(name) {
- return Err(ParseError::General {
- message: "formal parameter declared in lexically declared names",
- position,
- });
- }
- }
- }
- Ok(())
- }
-
/// Check if the any of the parameters contains a yield expression.
- pub(crate) fn contains_yield_expression(&self) -> bool {
+ #[must_use]
+ pub fn contains_yield_expression(&self) -> bool {
for parameter in self.parameters.iter() {
if parameter
.variable()
@@ -115,7 +140,8 @@ impl FormalParameterList {
}
/// Check if the any of the parameters contains a await expression.
- pub(crate) fn contains_await_expression(&self) -> bool {
+ #[must_use]
+ pub fn contains_await_expression(&self) -> bool {
for parameter in self.parameters.iter() {
if parameter
.variable()
@@ -142,75 +168,19 @@ impl FormalParameterList {
impl From> for FormalParameterList {
fn from(parameters: Vec) -> Self {
- let mut flags = FormalParameterListFlags::default();
- let mut length = 0;
- let mut names = FxHashSet::default();
-
- for parameter in ¶meters {
- let parameter_names = parameter.names();
-
- for name in parameter_names {
- if name == Sym::ARGUMENTS {
- flags |= FormalParameterListFlags::HAS_ARGUMENTS;
- }
- if names.contains(&name) {
- flags |= FormalParameterListFlags::HAS_DUPLICATES;
- } else {
- names.insert(name);
- }
- }
-
- if parameter.is_rest_param() {
- flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
- }
- if parameter.init().is_some() {
- flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
- }
- if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier()
- {
- flags.remove(FormalParameterListFlags::IS_SIMPLE);
- }
- if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
- || parameter.is_rest_param()
- || parameter.init().is_some())
- {
- length += 1;
- }
- }
-
- Self {
- parameters: parameters.into_boxed_slice(),
- flags,
- length,
- }
+ Self::from_parameters(parameters)
}
}
impl From for FormalParameterList {
fn from(parameter: FormalParameter) -> Self {
- let mut flags = FormalParameterListFlags::default();
- if parameter.is_rest_param() {
- flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
- }
- if parameter.init().is_some() {
- flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
- }
- if parameter.names().contains(&Sym::ARGUMENTS.into()) {
- flags |= FormalParameterListFlags::HAS_ARGUMENTS;
- }
- if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier() {
- flags.remove(FormalParameterListFlags::IS_SIMPLE);
- }
- let length = if parameter.is_rest_param() || parameter.init().is_some() {
- 0
- } else {
- 1
- };
- Self {
- parameters: Box::new([parameter]),
- flags,
- length,
- }
+ Self::from_parameters(vec![parameter])
+ }
+}
+
+impl AsRef<[FormalParameter]> for FormalParameterList {
+ fn as_ref(&self) -> &[FormalParameter] {
+ &self.parameters
}
}
@@ -240,12 +210,17 @@ impl VisitWith for FormalParameterList {
bitflags! {
/// Flags for a [`FormalParameterList`].
#[allow(clippy::unsafe_derive_deserialize)]
- #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
- pub(crate) struct FormalParameterListFlags: u8 {
+ #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+ pub struct FormalParameterListFlags: u8 {
+ /// Has only identifier parameters with no initialization expressions.
const IS_SIMPLE = 0b0000_0001;
+ /// Has any duplicate parameters.
const HAS_DUPLICATES = 0b0000_0010;
+ /// Has a rest parameter.
const HAS_REST_PARAMETER = 0b0000_0100;
+ /// Has any initialization expression.
const HAS_EXPRESSIONS = 0b0000_1000;
+ /// Has an argument with the name `arguments`.
const HAS_ARGUMENTS = 0b0001_0000;
}
}
@@ -271,7 +246,7 @@ impl Default for FormalParameterListFlags {
///
/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct FormalParameter {
variable: Variable,
@@ -280,7 +255,7 @@ pub struct FormalParameter {
impl FormalParameter {
/// Creates a new formal parameter.
- pub(in crate::syntax) fn new(variable: D, is_rest_param: bool) -> Self
+ pub fn new(variable: D, is_rest_param: bool) -> Self
where
D: Into,
{
@@ -291,6 +266,7 @@ impl FormalParameter {
}
/// Gets the name of the formal parameter.
+ #[must_use]
pub fn names(&self) -> Vec {
match self.variable.binding() {
Binding::Identifier(ident) => vec![*ident],
@@ -303,21 +279,25 @@ impl FormalParameter {
}
/// Gets the variable of the formal parameter
+ #[must_use]
pub fn variable(&self) -> &Variable {
&self.variable
}
/// Gets the initialization node of the formal parameter, if any.
+ #[must_use]
pub fn init(&self) -> Option<&Expression> {
self.variable.init()
}
/// Returns `true` if the parameter is a rest parameter.
+ #[must_use]
pub fn is_rest_param(&self) -> bool {
self.is_rest_param
}
/// Returns `true` if the parameter is a simple [`Identifier`].
+ #[must_use]
pub fn is_identifier(&self) -> bool {
matches!(&self.variable.binding(), Binding::Identifier(_))
}
diff --git a/boa_engine/src/syntax/ast/keyword.rs b/boa_ast/src/keyword.rs
similarity index 99%
rename from boa_engine/src/syntax/ast/keyword.rs
rename to boa_ast/src/keyword.rs
index 9c605e3d6f6..a59527929b6 100644
--- a/boa_engine/src/syntax/ast/keyword.rs
+++ b/boa_ast/src/keyword.rs
@@ -9,17 +9,15 @@
//! [spec]: https://tc39.es/ecma262/#sec-keywords-and-reserved-words
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
-use crate::{
- string::utf16,
- syntax::ast::expression::operator::binary::{BinaryOp, RelationalOp},
-};
+use crate::expression::operator::binary::{BinaryOp, RelationalOp};
use boa_interner::{Interner, Sym};
+use boa_macros::utf16;
use std::{convert::TryFrom, error, fmt, str::FromStr};
/// List of keywords recognized by the JavaScript grammar.
///
/// See the [module-level documentation][self] for more details.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Keyword {
/// The `await` keyword.
@@ -479,6 +477,7 @@ pub enum Keyword {
impl Keyword {
/// Gets the keyword as a binary operation, if this keyword is the `in` or the `instanceof`
/// keywords.
+ #[must_use]
pub fn as_binary_op(self) -> Option {
match self {
Self::In => Some(BinaryOp::Relational(RelationalOp::In)),
@@ -488,6 +487,7 @@ impl Keyword {
}
/// Gets the keyword as a tuple of strings.
+ #[must_use]
pub fn as_str(self) -> (&'static str, &'static [u16]) {
match self {
Self::Await => ("await", utf16!("await")),
diff --git a/boa_engine/src/syntax/ast/mod.rs b/boa_ast/src/lib.rs
similarity index 50%
rename from boa_engine/src/syntax/ast/mod.rs
rename to boa_ast/src/lib.rs
index 6f4d3e87f40..2978d55c1b5 100644
--- a/boa_engine/src/syntax/ast/mod.rs
+++ b/boa_ast/src/lib.rs
@@ -1,6 +1,6 @@
//! The Javascript Abstract Syntax Tree.
//!
-//! This module contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript spec.
+//! This crate contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript spec.
//! Some `Parse Node`s are not represented by Boa's AST, because a lot of grammar productions are
//! only used to throw [**Early Errors**][early], and don't influence the evaluation of the AST itself.
//!
@@ -11,7 +11,49 @@
//! [grammar]: https://tc39.es/ecma262/#sec-syntactic-grammar
//! [early]: https://tc39.es/ecma262/#sec-static-semantic-rules
-#![deny(missing_docs)]
+#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
+#![warn(
+ clippy::perf,
+ clippy::single_match_else,
+ clippy::dbg_macro,
+ clippy::doc_markdown,
+ clippy::wildcard_imports,
+ clippy::struct_excessive_bools,
+ clippy::doc_markdown,
+ clippy::semicolon_if_nothing_returned,
+ clippy::pedantic
+)]
+#![deny(
+ clippy::all,
+ clippy::cast_lossless,
+ clippy::redundant_closure_for_method_calls,
+ clippy::unnested_or_patterns,
+ clippy::trivially_copy_pass_by_ref,
+ clippy::needless_pass_by_value,
+ clippy::match_wildcard_for_single_variants,
+ clippy::map_unwrap_or,
+ unused_qualifications,
+ unused_import_braces,
+ unused_lifetimes,
+ unreachable_pub,
+ trivial_numeric_casts,
+ rustdoc::broken_intra_doc_links,
+ missing_debug_implementations,
+ missing_copy_implementations,
+ deprecated_in_future,
+ meta_variable_misuse,
+ non_ascii_idents,
+ rust_2018_compatibility,
+ rust_2018_idioms,
+ future_incompatible,
+ nonstandard_style,
+ missing_docs
+)]
+#![allow(
+ clippy::module_name_repetitions,
+ clippy::too_many_lines,
+ rustdoc::missing_doc_code_examples
+)]
mod position;
mod punctuator;
@@ -41,16 +83,26 @@ pub use self::{
/// Represents all the possible symbols searched for by the [`Contains`][contains] operation.
///
/// [contains]: https://tc39.es/ecma262/#sec-syntax-directed-operations-contains
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub(crate) enum ContainsSymbol {
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum ContainsSymbol {
+ /// A super property access (`super.prop`).
SuperProperty,
+ /// A super constructor call (`super(args)`).
SuperCall,
+ /// A yield expression (`yield 5`).
YieldExpression,
+ /// An await expression (`await 4`).
AwaitExpression,
+ /// The new target expression (`new.target`).
NewTarget,
+ /// The body of a class definition.
ClassBody,
+ /// The super class of a class definition.
ClassHeritage,
+ /// A this expression (`this`).
This,
+ /// A method definition.
MethodDefinition,
}
@@ -88,43 +140,20 @@ fn block_to_string(body: &StatementList, interner: &Interner, indentation: usize
}
}
-/// This parses the given source code, and then makes sure that
-/// the resulting `StatementList` is formatted in the same manner
-/// as the source code. This is expected to have a preceding
-/// newline.
-///
-/// This is a utility function for tests. It was made in case people
-/// are using different indents in their source files. This fixes
-/// any strings which may have been changed in a different indent
-/// level.
-#[cfg(test)]
-fn test_formatting(source: &'static str) {
- use crate::{syntax::Parser, Context};
-
- // Remove preceding newline.
- let source = &source[1..];
-
- // Find out how much the code is indented
- let first_line = &source[..source.find('\n').unwrap()];
- let trimmed_first_line = first_line.trim();
- let characters_to_remove = first_line.len() - trimmed_first_line.len();
+/// Utility trait that adds a `UTF-16` escaped representation to every [`[u16]`][slice].
+trait ToStringEscaped {
+ /// Decodes `self` as an `UTF-16` encoded string, escaping any unpaired surrogates by its
+ /// codepoint value.
+ fn to_string_escaped(&self) -> String;
+}
- let scenario = source
- .lines()
- .map(|l| &l[characters_to_remove..]) // Remove preceding whitespace from each line
- .collect::>()
- .join("\n");
- let mut context = Context::default();
- let result = Parser::new(scenario.as_bytes())
- .parse_all(&mut context)
- .expect("parsing failed")
- .to_interned_string(context.interner());
- if scenario != result {
- eprint!("========= Expected:\n{scenario}");
- eprint!("========= Got:\n{result}");
- // Might be helpful to find differing whitespace
- eprintln!("========= Expected: {scenario:?}");
- eprintln!("========= Got: {result:?}");
- panic!("parsing test did not give the correct result (see above)");
+impl ToStringEscaped for [u16] {
+ fn to_string_escaped(&self) -> String {
+ char::decode_utf16(self.iter().copied())
+ .map(|r| match r {
+ Ok(c) => String::from(c),
+ Err(e) => format!("\\u{:04X}", e.unpaired_surrogate()),
+ })
+ .collect()
}
}
diff --git a/boa_engine/src/syntax/ast/pattern.rs b/boa_ast/src/pattern.rs
similarity index 97%
rename from boa_engine/src/syntax/ast/pattern.rs
rename to boa_ast/src/pattern.rs
index 080c8697f3e..852ea1a0bf4 100644
--- a/boa_engine/src/syntax/ast/pattern.rs
+++ b/boa_ast/src/pattern.rs
@@ -22,8 +22,8 @@
//! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern
//! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
@@ -36,7 +36,7 @@ use super::{
/// An object or array pattern binding or assignment.
///
/// See the [module level documentation][self] for more information.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Pattern {
/// An object pattern (`let {a, b, c} = object`).
@@ -82,6 +82,7 @@ impl Pattern {
///
/// A single pattern may have 0 to n identifiers.
#[inline]
+ #[must_use]
pub fn idents(&self) -> Vec {
match &self {
Pattern::Object(pattern) => pattern.idents(),
@@ -149,7 +150,7 @@ impl VisitWith for Pattern {
///
/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern
/// [spec2]: https://tc39.es/ecma262/#prod-ObjectAssignmentPattern
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectPattern(Box<[ObjectPatternElement]>);
@@ -183,19 +184,32 @@ impl ToInternedString for ObjectPattern {
impl ObjectPattern {
/// Creates a new object binding pattern.
#[inline]
- pub(in crate::syntax) fn new(bindings: Box<[ObjectPatternElement]>) -> Self {
+ #[must_use]
+ pub fn new(bindings: Box<[ObjectPatternElement]>) -> Self {
Self(bindings)
}
/// Gets the bindings for the object binding pattern.
#[inline]
- pub(crate) fn bindings(&self) -> &[ObjectPatternElement] {
+ #[must_use]
+ pub fn bindings(&self) -> &[ObjectPatternElement] {
&self.0
}
- // Returns true if the object binding pattern has a rest element.
+ /// Gets the list of identifiers declared by the object binding pattern.
+ #[inline]
+ #[must_use]
+ pub fn idents(&self) -> Vec {
+ self.0
+ .iter()
+ .flat_map(ObjectPatternElement::idents)
+ .collect()
+ }
+
+ /// Returns true if the object binding pattern has a rest element.
#[inline]
- pub(crate) fn has_rest(&self) -> bool {
+ #[must_use]
+ pub fn has_rest(&self) -> bool {
matches!(
self.0.last(),
Some(ObjectPatternElement::RestProperty { .. })
@@ -211,15 +225,6 @@ impl ObjectPattern {
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.0.iter().any(|e| e.contains(symbol))
}
-
- /// Gets the list of identifiers declared by the object binding pattern.
- #[inline]
- pub(crate) fn idents(&self) -> Vec {
- self.0
- .iter()
- .flat_map(ObjectPatternElement::idents)
- .collect()
- }
}
impl VisitWith for ObjectPattern {
@@ -253,7 +258,7 @@ impl VisitWith for ObjectPattern {
///
/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern
/// [spec2]: https://tc39.es/ecma262/#prod-ArrayAssignmentPattern
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct ArrayPattern(Box<[ArrayPatternElement]>);
@@ -286,25 +291,18 @@ impl ToInternedString for ArrayPattern {
impl ArrayPattern {
/// Creates a new array binding pattern.
#[inline]
- pub(in crate::syntax) fn new(bindings: Box<[ArrayPatternElement]>) -> Self {
+ #[must_use]
+ pub fn new(bindings: Box<[ArrayPatternElement]>) -> Self {
Self(bindings)
}
/// Gets the bindings for the array binding pattern.
#[inline]
- pub(crate) fn bindings(&self) -> &[ArrayPatternElement] {
+ #[must_use]
+ pub fn bindings(&self) -> &[ArrayPatternElement] {
&self.0
}
- #[inline]
- pub(crate) fn contains_arguments(&self) -> bool {
- self.0.iter().any(ArrayPatternElement::contains_arguments)
- }
-
- pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
- self.0.iter().any(|e| e.contains(symbol))
- }
-
/// Gets the list of identifiers declared by the array binding pattern.
#[inline]
pub(crate) fn idents(&self) -> Vec {
@@ -313,6 +311,15 @@ impl ArrayPattern {
.flat_map(ArrayPatternElement::idents)
.collect()
}
+
+ #[inline]
+ pub(crate) fn contains_arguments(&self) -> bool {
+ self.0.iter().any(ArrayPatternElement::contains_arguments)
+ }
+
+ pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
+ self.0.iter().any(|e| e.contains(symbol))
+ }
}
impl VisitWith for ArrayPattern {
@@ -343,7 +350,7 @@ impl VisitWith for ArrayPattern {
///
/// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty
/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentProperty
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum ObjectPatternElement {
/// SingleName represents one of the following properties:
@@ -752,7 +759,7 @@ impl VisitWith for ObjectPatternElement {
///
/// [spec1]: https://tc39.es/ecma262/#prod-BindingElement
/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentElement
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum ArrayPatternElement {
/// Elision represents the elision of an item in the array binding pattern.
diff --git a/boa_engine/src/syntax/ast/position.rs b/boa_ast/src/position.rs
similarity index 95%
rename from boa_engine/src/syntax/ast/position.rs
rename to boa_ast/src/position.rs
index c133b132add..dafba2fb30c 100644
--- a/boa_engine/src/syntax/ast/position.rs
+++ b/boa_ast/src/position.rs
@@ -6,7 +6,7 @@ use std::{cmp::Ordering, fmt, num::NonZeroU32};
///
/// ## Similar Implementations
/// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216)
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Position {
/// Line number.
@@ -19,6 +19,7 @@ impl Position {
/// Creates a new `Position`.
#[inline]
#[track_caller]
+ #[must_use]
pub fn new(line_number: u32, column_number: u32) -> Self {
Self {
line_number: NonZeroU32::new(line_number).expect("line number cannot be 0"),
@@ -28,12 +29,14 @@ impl Position {
/// Gets the line number of the position.
#[inline]
+ #[must_use]
pub fn line_number(self) -> u32 {
self.line_number.get()
}
/// Gets the column number of the position.
#[inline]
+ #[must_use]
pub fn column_number(self) -> u32 {
self.column_number.get()
}
@@ -51,7 +54,7 @@ impl fmt::Display for Position {
///
/// Note that spans are of the form [start, end) i.e. that the start position is inclusive
/// and the end position is exclusive.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
start: Position,
@@ -60,8 +63,13 @@ pub struct Span {
impl Span {
/// Creates a new `Span`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the start position is bigger than the end position.
#[inline]
#[track_caller]
+ #[must_use]
pub fn new(start: Position, end: Position) -> Self {
assert!(start <= end, "a span cannot start after its end");
@@ -70,12 +78,14 @@ impl Span {
/// Gets the starting position of the span.
#[inline]
+ #[must_use]
pub fn start(self) -> Position {
self.start
}
/// Gets the final position of the span.
#[inline]
+ #[must_use]
pub fn end(self) -> Position {
self.end
}
@@ -121,8 +131,9 @@ impl fmt::Display for Span {
}
#[cfg(test)]
-#[allow(clippy::similar_names)]
mod tests {
+ #![allow(clippy::similar_names)]
+ #![allow(unused_must_use)]
use super::{Position, Span};
/// Checks that we cannot create a position with 0 as the column.
diff --git a/boa_engine/src/syntax/ast/property.rs b/boa_ast/src/property.rs
similarity index 95%
rename from boa_engine/src/syntax/ast/property.rs
rename to boa_ast/src/property.rs
index 6a465dbb638..72abc19ffcc 100644
--- a/boa_engine/src/syntax/ast/property.rs
+++ b/boa_ast/src/property.rs
@@ -1,7 +1,7 @@
//! Property definition related types, used in object literals and class definitions.
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
@@ -24,7 +24,7 @@ use super::{
/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript
// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyDefinition {
/// Puts a variable into an object.
@@ -173,7 +173,7 @@ impl VisitWith for PropertyDefinition {
///
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum MethodDefinition {
/// The `get` syntax binds an object property to a function that will be called when that property is looked up.
@@ -249,8 +249,9 @@ pub enum MethodDefinition {
}
impl MethodDefinition {
- /// Return the body of the method.
- pub(crate) fn body(&self) -> &StatementList {
+ /// Gets the body of the method.
+ #[must_use]
+ pub fn body(&self) -> &StatementList {
match self {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
@@ -261,8 +262,9 @@ impl MethodDefinition {
}
}
- /// Return the parameters of the method.
- pub(crate) fn parameters(&self) -> &FormalParameterList {
+ /// Gets the parameters of the method.
+ #[must_use]
+ pub fn parameters(&self) -> &FormalParameterList {
match self {
MethodDefinition::Get(expr)
| MethodDefinition::Set(expr)
@@ -310,7 +312,7 @@ impl VisitWith for MethodDefinition {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-PropertyName
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyName {
/// A `Literal` property name can be either an identifier, a string or a numeric literal.
@@ -332,7 +334,8 @@ pub enum PropertyName {
impl PropertyName {
/// Returns the literal property name if it exists.
- pub(crate) fn literal(&self) -> Option {
+ #[must_use]
+ pub fn literal(&self) -> Option {
if let Self::Literal(sym) = self {
Some(*sym)
} else {
@@ -341,7 +344,8 @@ impl PropertyName {
}
/// Returns the expression if the property name is computed.
- pub(crate) fn computed(&self) -> Option<&Expression> {
+ #[must_use]
+ pub fn computed(&self) -> Option<&Expression> {
if let Self::Computed(expr) = self {
Some(expr)
} else {
@@ -350,7 +354,8 @@ impl PropertyName {
}
/// Returns either the literal property name or the computed const string property name.
- pub(in crate::syntax) fn prop_name(&self) -> Option {
+ #[must_use]
+ pub fn prop_name(&self) -> Option {
match self {
PropertyName::Literal(sym)
| PropertyName::Computed(Expression::Literal(Literal::String(sym))) => Some(*sym),
@@ -424,9 +429,11 @@ impl VisitWith for PropertyName {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
-pub(crate) enum ClassElementName {
+pub enum ClassElementName {
+ /// A public property.
PropertyName(PropertyName),
+ /// A private property.
PrivateIdentifier(Sym),
}
diff --git a/boa_engine/src/syntax/ast/punctuator.rs b/boa_ast/src/punctuator.rs
similarity index 98%
rename from boa_engine/src/syntax/ast/punctuator.rs
rename to boa_ast/src/punctuator.rs
index 0f678d99482..3ab065dc6b8 100644
--- a/boa_engine/src/syntax/ast/punctuator.rs
+++ b/boa_ast/src/punctuator.rs
@@ -5,7 +5,7 @@
//!
//! [spec]: https://tc39.es/ecma262/#prod-Punctuator
-use crate::syntax::ast::expression::operator::{
+use crate::expression::operator::{
assign::AssignOp,
binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp},
};
@@ -20,7 +20,7 @@ use std::{
/// - [ECMAScript Reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-Punctuator
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Punctuator {
/// `+`
@@ -143,6 +143,7 @@ impl Punctuator {
/// Attempts to convert a punctuator (`+`, `=`...) to an Assign Operator
///
/// If there is no match, `None` will be returned.
+ #[must_use]
pub const fn as_assign_op(self) -> Option {
match self {
Self::Assign => Some(AssignOp::Assign),
@@ -168,6 +169,7 @@ impl Punctuator {
/// Attempts to convert a punctuator (`+`, `=`...) to a Binary Operator
///
/// If there is no match, `None` will be returned.
+ #[must_use]
pub const fn as_binary_op(self) -> Option {
match self {
Self::Add => Some(BinaryOp::Arithmetic(ArithmeticOp::Add)),
@@ -198,6 +200,7 @@ impl Punctuator {
}
/// Retrieves the punctuator as a static string.
+ #[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Add => "+",
diff --git a/boa_engine/src/syntax/ast/statement/block.rs b/boa_ast/src/statement/block.rs
similarity index 71%
rename from boa_engine/src/syntax/ast/statement/block.rs
rename to boa_ast/src/statement/block.rs
index c6a8d61ec4e..2e79cec6595 100644
--- a/boa_engine/src/syntax/ast/statement/block.rs
+++ b/boa_ast/src/statement/block.rs
@@ -1,8 +1,8 @@
//! Block AST node.
use super::Statement;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Identifier, ContainsSymbol, StatementList};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Identifier, ContainsSymbol, StatementList};
use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow;
@@ -21,23 +21,25 @@ use core::ops::ControlFlow;
///
/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Block {
- #[cfg_attr(feature = "deser", serde(flatten))]
+ #[cfg_attr(feature = "serde", serde(flatten))]
statements: StatementList,
}
impl Block {
/// Gets the list of statements and declarations in this block.
#[inline]
- pub(crate) fn statement_list(&self) -> &StatementList {
+ #[must_use]
+ pub fn statement_list(&self) -> &StatementList {
&self.statements
}
/// Get the lexically declared names of the block.
#[inline]
- pub(crate) fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> {
+ #[must_use]
+ pub fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> {
self.statements.lexically_declared_names()
}
@@ -97,29 +99,3 @@ impl VisitWith for Block {
visitor.visit_statement_list_mut(&mut self.statements)
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- {
- let a = function_call();
- console.log("hello");
- }
- another_statement();
- "#,
- );
- // TODO: Once block labels are implemtned, this should be tested:
- // super::super::test_formatting(
- // r#"
- // block_name: {
- // let a = function_call();
- // console.log("hello");
- // }
- // another_statement();
- // "#,
- // );
- }
-}
diff --git a/boa_engine/src/syntax/ast/statement/if.rs b/boa_ast/src/statement/if.rs
similarity index 89%
rename from boa_engine/src/syntax/ast/statement/if.rs
rename to boa_ast/src/statement/if.rs
index adca66a8552..25b6cf2c641 100644
--- a/boa_engine/src/syntax/ast/statement/if.rs
+++ b/boa_ast/src/statement/if.rs
@@ -1,8 +1,8 @@
//! If statement
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, statement::Statement, ContainsSymbol};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
@@ -22,7 +22,7 @@ use core::ops::ControlFlow;
/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/truthy
/// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/falsy
/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct If {
condition: Expression,
@@ -33,12 +33,14 @@ pub struct If {
impl If {
/// Gets the condition of the if statement.
#[inline]
+ #[must_use]
pub fn cond(&self) -> &Expression {
&self.condition
}
/// Gets the body to execute if the condition is true.
#[inline]
+ #[must_use]
pub fn body(&self) -> &Statement {
&self.body
}
@@ -50,6 +52,7 @@ impl If {
}
/// Creates an `If` AST node.
+ #[must_use]
pub fn new(condition: Expression, body: Statement, else_node: Option) -> Self {
Self {
condition,
@@ -123,20 +126,3 @@ impl VisitWith for If {
ControlFlow::Continue(())
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- let a = true ? 5 : 6;
- if (false) {
- a = 10;
- } else {
- a = 20;
- }
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/statement/iteration/break.rs b/boa_ast/src/statement/iteration/break.rs
similarity index 71%
rename from boa_engine/src/syntax/ast/statement/iteration/break.rs
rename to boa_ast/src/statement/iteration/break.rs
index b05c47aeb13..af97bf503de 100644
--- a/boa_engine/src/syntax/ast/statement/iteration/break.rs
+++ b/boa_ast/src/statement/iteration/break.rs
@@ -1,8 +1,8 @@
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::Statement;
+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.
@@ -18,7 +18,7 @@ use crate::syntax::ast::Statement;
///
/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Break {
label: Option,
@@ -26,11 +26,13 @@ pub struct Break {
impl Break {
/// Creates a `Break` AST node.
+ #[must_use]
pub fn new(label: Option) -> Self {
Self { label }
}
/// Gets the label of the break statement, if any.
+ #[must_use]
pub fn label(&self) -> Option {
self.label
}
@@ -80,37 +82,3 @@ impl VisitWith for Break {
}
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- // Blocks do not store their label, so we cannot test with
- // the outer block having a label.
- //
- // TODO: Once block labels are implemented, this test should
- // include them:
- //
- // ```
- // outer: {
- // while (true) {
- // break outer;
- // }
- // skipped_call();
- // }
- // ```
- crate::syntax::ast::test_formatting(
- r#"
- {
- while (true) {
- break outer;
- }
- skipped_call();
- }
- while (true) {
- break;
- }
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/statement/iteration/continue.rs b/boa_ast/src/statement/iteration/continue.rs
similarity index 92%
rename from boa_engine/src/syntax/ast/statement/iteration/continue.rs
rename to boa_ast/src/statement/iteration/continue.rs
index 7c7f1f1f177..5b461365221 100644
--- a/boa_engine/src/syntax/ast/statement/iteration/continue.rs
+++ b/boa_ast/src/statement/iteration/continue.rs
@@ -1,5 +1,5 @@
-use crate::syntax::ast::statement::Statement;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::statement::Statement;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString};
use core::ops::ControlFlow;
@@ -16,7 +16,7 @@ use core::ops::ControlFlow;
///
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Continue {
label: Option,
@@ -24,11 +24,13 @@ pub struct Continue {
impl Continue {
/// Creates a `Continue` AST node.
+ #[must_use]
pub fn new(label: Option) -> Self {
Self { label }
}
/// Gets the label of this `Continue` statement.
+ #[must_use]
pub fn label(&self) -> Option {
self.label
}
diff --git a/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs b/boa_ast/src/statement/iteration/do_while_loop.rs
similarity index 91%
rename from boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs
rename to boa_ast/src/statement/iteration/do_while_loop.rs
index 5ebd5de0b16..0be9eb6ad2c 100644
--- a/boa_engine/src/syntax/ast/statement/iteration/do_while_loop.rs
+++ b/boa_ast/src/statement/iteration/do_while_loop.rs
@@ -1,6 +1,6 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, statement::Statement, ContainsSymbol};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
@@ -16,7 +16,7 @@ use core::ops::ControlFlow;
///
/// [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 = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct DoWhileLoop {
body: Box,
@@ -26,17 +26,20 @@ pub struct DoWhileLoop {
impl DoWhileLoop {
/// Gets the body of the do-while loop.
#[inline]
+ #[must_use]
pub fn body(&self) -> &Statement {
&self.body
}
/// Gets the condition of the do-while loop.
#[inline]
+ #[must_use]
pub 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(),
diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs b/boa_ast/src/statement/iteration/for_in_loop.rs
similarity index 94%
rename from boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs
rename to boa_ast/src/statement/iteration/for_in_loop.rs
index 0b04974dae1..341dd10ded6 100644
--- a/boa_engine/src/syntax/ast/statement/iteration/for_in_loop.rs
+++ b/boa_ast/src/statement/iteration/for_in_loop.rs
@@ -1,10 +1,10 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
expression::Expression,
statement::{iteration::IterableLoopInitializer, Statement},
ContainsSymbol,
};
-use crate::try_break;
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
@@ -15,7 +15,7 @@ use core::ops::ControlFlow;
///
/// [forin]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForInLoop {
initializer: IterableLoopInitializer,
@@ -26,6 +26,7 @@ pub struct ForInLoop {
impl ForInLoop {
/// Creates a new `ForInLoop`.
#[inline]
+ #[must_use]
pub fn new(initializer: IterableLoopInitializer, target: Expression, body: Statement) -> Self {
Self {
initializer,
@@ -36,18 +37,21 @@ impl ForInLoop {
/// Gets the initializer of the for...in loop.
#[inline]
+ #[must_use]
pub fn initializer(&self) -> &IterableLoopInitializer {
&self.initializer
}
/// Gets the target object of the for...in loop.
#[inline]
+ #[must_use]
pub fn target(&self) -> &Expression {
&self.target
}
/// Gets the body of the for...in loop.
#[inline]
+ #[must_use]
pub fn body(&self) -> &Statement {
&self.body
}
diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs b/boa_ast/src/statement/iteration/for_loop.rs
similarity index 95%
rename from boa_engine/src/syntax/ast/statement/iteration/for_loop.rs
rename to boa_ast/src/statement/iteration/for_loop.rs
index c697e2c7e39..e12753ff197 100644
--- a/boa_engine/src/syntax/ast/statement/iteration/for_loop.rs
+++ b/boa_ast/src/statement/iteration/for_loop.rs
@@ -1,11 +1,11 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
declaration::{LexicalDeclaration, VarDeclaration, Variable},
expression::Identifier,
statement::Statement,
ContainsSymbol, Expression,
};
-use crate::try_break;
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
@@ -19,17 +19,18 @@ use core::ops::ControlFlow;
///
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForLoop {
- #[cfg_attr(feature = "deser", serde(flatten))]
+ #[cfg_attr(feature = "serde", serde(flatten))]
inner: Box,
}
impl ForLoop {
/// Creates a new for loop AST node.
#[inline]
- pub(in crate::syntax) fn new(
+ #[must_use]
+ pub fn new(
init: Option,
condition: Option,
final_expr: Option,
@@ -42,24 +43,28 @@ impl ForLoop {
/// Gets the initialization node.
#[inline]
+ #[must_use]
pub fn init(&self) -> Option<&ForLoopInitializer> {
self.inner.init()
}
/// Gets the loop condition node.
#[inline]
+ #[must_use]
pub fn condition(&self) -> Option<&Expression> {
self.inner.condition()
}
/// Gets the final expression node.
#[inline]
+ #[must_use]
pub fn final_expr(&self) -> Option<&Expression> {
self.inner.final_expr()
}
/// Gets the body of the for loop.
#[inline]
+ #[must_use]
pub fn body(&self) -> &Statement {
self.inner.body()
}
@@ -148,7 +153,7 @@ impl VisitWith for ForLoop {
}
/// Inner structure to avoid multiple indirections in the heap.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
struct InnerForLoop {
init: Option,
@@ -207,7 +212,7 @@ impl InnerForLoop {
/// declarations instead of only one.
///
/// [spec]: https://tc39.es/ecma262/#prod-ForStatement
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum ForLoopInitializer {
/// An expression initializer.
@@ -225,7 +230,8 @@ impl ForLoopInitializer {
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames
- pub(crate) fn bound_names(&self) -> Vec {
+ #[must_use]
+ pub fn bound_names(&self) -> Vec {
match self {
ForLoopInitializer::Lexical(lex) => lex
.variable_list()
diff --git a/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs b/boa_ast/src/statement/iteration/for_of_loop.rs
similarity index 93%
rename from boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs
rename to boa_ast/src/statement/iteration/for_of_loop.rs
index cfcd1079c34..538cad9df74 100644
--- a/boa_engine/src/syntax/ast/statement/iteration/for_of_loop.rs
+++ b/boa_ast/src/statement/iteration/for_of_loop.rs
@@ -1,10 +1,10 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
expression::Expression,
statement::{iteration::IterableLoopInitializer, Statement},
ContainsSymbol,
};
-use crate::try_break;
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
@@ -20,7 +20,7 @@ use core::ops::ControlFlow;
/// [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 = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForOfLoop {
init: IterableLoopInitializer,
@@ -32,6 +32,7 @@ pub struct ForOfLoop {
impl ForOfLoop {
/// Creates a new "for of" loop AST node.
#[inline]
+ #[must_use]
pub fn new(
init: IterableLoopInitializer,
iterable: Expression,
@@ -48,25 +49,29 @@ impl ForOfLoop {
/// Gets the initializer of the for...of loop.
#[inline]
+ #[must_use]
pub fn init(&self) -> &IterableLoopInitializer {
&self.init
}
/// Gets the iterable expression of the for...of loop.
#[inline]
+ #[must_use]
pub fn iterable(&self) -> &Expression {
&self.iterable
}
/// Gets the body to execute in the for...of loop.
#[inline]
+ #[must_use]
pub fn body(&self) -> &Statement {
&self.body
}
/// Returns true if this "for...of" loop is an "for await...of" loop.
#[inline]
- pub(crate) fn r#await(&self) -> bool {
+ #[must_use]
+ pub fn r#await(&self) -> bool {
self.r#await
}
diff --git a/boa_engine/src/syntax/ast/statement/iteration/mod.rs b/boa_ast/src/statement/iteration/mod.rs
similarity index 94%
rename from boa_engine/src/syntax/ast/statement/iteration/mod.rs
rename to boa_ast/src/statement/iteration/mod.rs
index ff1ecd30370..5ce33c77081 100644
--- a/boa_engine/src/syntax/ast/statement/iteration/mod.rs
+++ b/boa_ast/src/statement/iteration/mod.rs
@@ -8,7 +8,7 @@ mod for_loop;
mod for_of_loop;
mod while_loop;
-use crate::syntax::ast::{
+use crate::{
declaration::Binding,
expression::{access::PropertyAccess, Identifier},
pattern::Pattern,
@@ -24,14 +24,11 @@ pub use self::{
r#continue::Continue,
while_loop::WhileLoop,
};
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, Sym, ToInternedString};
use super::ContainsSymbol;
-#[cfg(test)]
-mod tests;
-
/// 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
@@ -39,7 +36,7 @@ mod tests;
/// can have more than one binding.
///
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum IterableLoopInitializer {
/// An already declared variable.
@@ -65,7 +62,8 @@ impl IterableLoopInitializer {
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames
- pub(crate) fn bound_names(&self) -> Vec {
+ #[must_use]
+ pub fn bound_names(&self) -> Vec {
match self {
Self::Let(binding) | Self::Const(binding) => binding.idents(),
_ => Vec::new(),
diff --git a/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs b/boa_ast/src/statement/iteration/while_loop.rs
similarity index 91%
rename from boa_engine/src/syntax/ast/statement/iteration/while_loop.rs
rename to boa_ast/src/statement/iteration/while_loop.rs
index 6b2eeb396c8..dc010fb2708 100644
--- a/boa_engine/src/syntax/ast/statement/iteration/while_loop.rs
+++ b/boa_ast/src/statement/iteration/while_loop.rs
@@ -1,6 +1,6 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, statement::Statement, ContainsSymbol};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
@@ -15,7 +15,7 @@ use core::ops::ControlFlow;
///
/// [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 = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct WhileLoop {
condition: Expression,
@@ -25,6 +25,7 @@ pub struct WhileLoop {
impl WhileLoop {
/// Creates a `WhileLoop` AST node.
#[inline]
+ #[must_use]
pub fn new(condition: Expression, body: Statement) -> Self {
Self {
condition,
@@ -34,12 +35,14 @@ impl WhileLoop {
/// Gets the condition of the while loop.
#[inline]
+ #[must_use]
pub fn condition(&self) -> &Expression {
&self.condition
}
/// Gets the body of the while loop.
#[inline]
+ #[must_use]
pub fn body(&self) -> &Statement {
&self.body
}
diff --git a/boa_engine/src/syntax/ast/statement/labelled.rs b/boa_ast/src/statement/labelled.rs
similarity index 94%
rename from boa_engine/src/syntax/ast/statement/labelled.rs
rename to boa_ast/src/statement/labelled.rs
index 477fdd0af0a..25aefff37a8 100644
--- a/boa_engine/src/syntax/ast/statement/labelled.rs
+++ b/boa_ast/src/statement/labelled.rs
@@ -1,7 +1,7 @@
use super::Statement;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{function::Function, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{function::Function, ContainsSymbol};
use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
@@ -14,7 +14,7 @@ use core::ops::ControlFlow;
///
/// [spec]: https://tc39.es/ecma262/#prod-LabelledItem
/// [label-fn]: https://tc39.es/ecma262/#sec-labelled-function-declarations
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum LabelledItem {
/// A labelled [`Function`].
@@ -94,7 +94,7 @@ impl VisitWith for LabelledItem {
/// See [`LabelledItem`] for more information.
///
/// [spec]: https://tc39.es/ecma262/#sec-labelled-statements
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Labelled {
item: Box,
@@ -103,6 +103,7 @@ pub struct Labelled {
impl Labelled {
/// Creates a new `Labelled` statement.
+ #[must_use]
pub fn new(item: LabelledItem, label: Sym) -> Self {
Self {
item: Box::new(item),
@@ -111,11 +112,13 @@ impl Labelled {
}
/// Gets the labelled item.
+ #[must_use]
pub fn item(&self) -> &LabelledItem {
&self.item
}
/// Gets the label name.
+ #[must_use]
pub fn label(&self) -> Sym {
self.label
}
diff --git a/boa_engine/src/syntax/ast/statement/mod.rs b/boa_ast/src/statement/mod.rs
similarity index 95%
rename from boa_engine/src/syntax/ast/statement/mod.rs
rename to boa_ast/src/statement/mod.rs
index 831d3956514..c3d9c56299a 100644
--- a/boa_engine/src/syntax/ast/statement/mod.rs
+++ b/boa_ast/src/statement/mod.rs
@@ -24,16 +24,15 @@ pub use self::{
labelled::{Labelled, LabelledItem},
r#if::If,
r#return::Return,
- r#try::{Catch, Finally, Try},
+ r#try::{Catch, ErrorHandler, Finally, Try},
switch::{Case, Switch},
throw::Throw,
};
use core::ops::ControlFlow;
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use rustc_hash::FxHashSet;
-use tap::Tap;
use super::{
declaration::{Binding, VarDeclaration},
@@ -44,7 +43,7 @@ use super::{
/// The `Statement` Parse Node.
///
/// See the [module level documentation][self] for more information.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Statement {
/// See [`Block`].
@@ -115,7 +114,7 @@ impl Statement {
/// This will not prefix the value with any indentation. If you want to prefix this with proper
/// indents, use [`to_indented_string()`](Self::to_indented_string).
pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String {
- match self {
+ let mut s = match self {
Self::Block(block) => return block.to_indented_string(interner, indentation),
Self::Var(var) => var.to_interned_string(interner),
Self::Empty => return ";".to_owned(),
@@ -135,11 +134,13 @@ impl Statement {
Self::Labelled(labelled) => return labelled.to_interned_string(interner),
Self::Throw(throw) => throw.to_interned_string(interner),
Self::Try(try_catch) => return try_catch.to_indented_string(interner, indentation),
- }
- .tap_mut(|s| s.push(';'))
+ };
+ s.push(';');
+ s
}
- pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) {
+ /// Gets the var declared names of this `Statement`.
+ pub fn var_declared_names(&self, vars: &mut FxHashSet) {
match self {
Self::Var(VarDeclaration(list)) => {
for decl in list.as_ref() {
@@ -212,7 +213,7 @@ impl Statement {
}
}
if let Some(finally) = try_statement.finally() {
- for node in finally.statement_list().statements() {
+ for node in finally.block().statement_list().statements() {
node.var_declared_names(vars);
}
}
@@ -281,8 +282,14 @@ impl Statement {
}
}
+ /// `IsLabelledFunction` static operation, as defined by the [spec].
+ ///
+ /// Returns `true` if this `Statement` is a labelled function.
+ ///
+ /// [spec]: https://tc39.es/ecma262/#sec-islabelledfunction
#[inline]
- pub(crate) fn is_labelled_function(&self) -> bool {
+ #[must_use]
+ pub fn is_labelled_function(&self) -> bool {
match self {
Self::Labelled(stmt) => match stmt.item() {
LabelledItem::Function(_) => true,
diff --git a/boa_engine/src/syntax/ast/statement/return.rs b/boa_ast/src/statement/return.rs
similarity index 80%
rename from boa_engine/src/syntax/ast/statement/return.rs
rename to boa_ast/src/statement/return.rs
index 4370f64aec7..7640dea294f 100644
--- a/boa_engine/src/syntax/ast/statement/return.rs
+++ b/boa_ast/src/statement/return.rs
@@ -1,5 +1,5 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, statement::Statement, ContainsSymbol};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, statement::Statement, ContainsSymbol};
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;
@@ -20,7 +20,7 @@ use core::ops::ControlFlow;
///
/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Return {
target: Option,
@@ -28,11 +28,13 @@ pub struct Return {
impl Return {
/// Gets the target expression value of this `Return` statement.
+ #[must_use]
pub fn target(&self) -> Option<&Expression> {
self.target.as_ref()
}
/// Creates a `Return` AST node.
+ #[must_use]
pub fn new(expression: Option) -> Self {
Self { target: expression }
}
@@ -85,23 +87,3 @@ impl VisitWith for Return {
}
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- function say_hello(msg) {
- if (msg === "") {
- return 0;
- }
- console.log("hello " + msg);
- return;
- }
- say_hello("");
- say_hello("world");
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/statement/switch/mod.rs b/boa_ast/src/statement/switch.rs
similarity index 94%
rename from boa_engine/src/syntax/ast/statement/switch/mod.rs
rename to boa_ast/src/statement/switch.rs
index 44d85c44d58..de3eeb4f5be 100644
--- a/boa_engine/src/syntax/ast/statement/switch/mod.rs
+++ b/boa_ast/src/statement/switch.rs
@@ -1,16 +1,13 @@
//! Switch node.
//!
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, statement::Statement, StatementList};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Expression, statement::Statement, StatementList};
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
use super::ContainsSymbol;
-#[cfg(test)]
-mod tests;
-
/// A case clause inside a [`Switch`] statement, as defined by the [spec].
///
/// Even though every [`Case`] body is a [`StatementList`], it doesn't create a new lexical
@@ -19,7 +16,7 @@ mod tests;
///
/// [spec]: https://tc39.es/ecma262/#prod-CaseClause
/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Case {
condition: Expression,
@@ -29,18 +26,21 @@ pub struct Case {
impl Case {
/// Creates a `Case` AST node.
#[inline]
+ #[must_use]
pub fn new(condition: Expression, body: StatementList) -> Self {
Self { condition, body }
}
/// Gets the condition of the case.
#[inline]
+ #[must_use]
pub fn condition(&self) -> &Expression {
&self.condition
}
/// Gets the statement listin the body of the case.
#[inline]
+ #[must_use]
pub fn body(&self) -> &StatementList {
&self.body
}
@@ -95,7 +95,7 @@ impl VisitWith for Case {
///
/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Switch {
val: Expression,
@@ -106,6 +106,7 @@ pub struct Switch {
impl Switch {
/// Creates a `Switch` AST node.
#[inline]
+ #[must_use]
pub fn new(val: Expression, cases: Box<[Case]>, default: Option) -> Self {
Self {
val,
@@ -116,18 +117,21 @@ impl Switch {
/// Gets the value to switch.
#[inline]
+ #[must_use]
pub fn val(&self) -> &Expression {
&self.val
}
/// Gets the list of cases for the switch statement.
#[inline]
+ #[must_use]
pub fn cases(&self) -> &[Case] {
&self.cases
}
/// Gets the default statement list, if any.
#[inline]
+ #[must_use]
pub fn default(&self) -> Option<&StatementList> {
self.default.as_ref()
}
diff --git a/boa_engine/src/syntax/ast/statement/throw.rs b/boa_ast/src/statement/throw.rs
similarity index 81%
rename from boa_engine/src/syntax/ast/statement/throw.rs
rename to boa_ast/src/statement/throw.rs
index c1747c6f510..f3d6dac42c0 100644
--- a/boa_engine/src/syntax/ast/statement/throw.rs
+++ b/boa_ast/src/statement/throw.rs
@@ -1,5 +1,5 @@
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{statement::Statement, ContainsSymbol, Expression};
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{statement::Statement, ContainsSymbol, Expression};
use boa_interner::{Interner, ToInternedString};
use core::ops::ControlFlow;
@@ -17,7 +17,7 @@ use core::ops::ControlFlow;
///
/// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Throw {
target: Expression,
@@ -25,11 +25,13 @@ pub struct Throw {
impl Throw {
/// Gets the target expression of this `Throw` statement.
+ #[must_use]
pub fn target(&self) -> &Expression {
&self.target
}
/// Creates a `Throw` AST node.
+ #[must_use]
pub fn new(target: Expression) -> Self {
Self { target }
}
@@ -72,19 +74,3 @@ impl VisitWith for Throw {
visitor.visit_expression_mut(&mut self.target)
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- try {
- throw "hello";
- } catch(e) {
- console.log(e);
- }
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/statement/try/mod.rs b/boa_ast/src/statement/try.rs
similarity index 74%
rename from boa_engine/src/syntax/ast/statement/try/mod.rs
rename to boa_ast/src/statement/try.rs
index 0237162f858..bed249076bf 100644
--- a/boa_engine/src/syntax/ast/statement/try/mod.rs
+++ b/boa_ast/src/statement/try.rs
@@ -1,20 +1,17 @@
//! Error handling statements
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
+use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{
declaration::Binding,
statement::{Block, Statement},
StatementListItem,
};
-use crate::try_break;
use boa_interner::{Interner, ToIndentedString, ToInternedString};
use core::ops::ControlFlow;
use super::ContainsSymbol;
-#[cfg(test)]
-mod tests;
-
/// The `try...catch` statement marks a block of statements to try and specifies a response
/// should an exception be thrown.
///
@@ -28,64 +25,72 @@ mod tests;
///
/// [spec]: https://tc39.es/ecma262/#prod-TryStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Try {
block: Block,
- catch: Option,
- finally: Option,
+ handler: ErrorHandler,
+}
+
+/// The type of error handler in a [`Try`] statement.
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug, PartialEq)]
+pub enum ErrorHandler {
+ /// A [`Catch`] error handler.
+ Catch(Catch),
+ /// A [`Finally`] error handler.
+ Finally(Finally),
+ /// A [`Catch`] and [`Finally`] error handler.
+ Full(Catch, Finally),
}
impl Try {
/// Creates a new `Try` AST node.
#[inline]
- pub(in crate::syntax) fn new(
- block: Block,
- catch: Option,
- finally: Option,
- ) -> Self {
- assert!(
- catch.is_some() || finally.is_some(),
- "one of catch or finally must be pressent"
- );
-
- Self {
- block,
- catch,
- finally,
- }
+ #[must_use]
+ pub fn new(block: Block, handler: ErrorHandler) -> Self {
+ Self { block, handler }
}
/// Gets the `try` block.
#[inline]
+ #[must_use]
pub fn block(&self) -> &Block {
&self.block
}
/// Gets the `catch` block, if any.
#[inline]
+ #[must_use]
pub fn catch(&self) -> Option<&Catch> {
- self.catch.as_ref()
+ match &self.handler {
+ ErrorHandler::Catch(c) | ErrorHandler::Full(c, _) => Some(c),
+ ErrorHandler::Finally(_) => None,
+ }
}
/// Gets the `finally` block, if any.
#[inline]
- pub fn finally(&self) -> Option<&Block> {
- self.finally.as_ref().map(Finally::block)
+ #[must_use]
+ pub fn finally(&self) -> Option<&Finally> {
+ match &self.handler {
+ ErrorHandler::Finally(f) | ErrorHandler::Full(_, f) => Some(f),
+ ErrorHandler::Catch(_) => None,
+ }
}
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
self.block.contains_arguments()
- || matches!(self.catch, Some(ref catch) if catch.contains_arguments())
- || matches!(self.finally, Some(ref finally) if finally.contains_arguments())
+ || matches!(self.catch(), Some(catch) if catch.contains_arguments())
+ || matches!(self.finally(), Some(finally) if finally.contains_arguments())
}
#[inline]
pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
self.block.contains(symbol)
- || matches!(self.catch, Some(ref catch) if catch.contains(symbol))
- || matches!(self.finally, Some(ref finally) if finally.contains(symbol))
+ || matches!(self.catch(), Some(catch) if catch.contains(symbol))
+ || matches!(self.finally(), Some(finally) if finally.contains(symbol))
}
}
@@ -97,11 +102,11 @@ impl ToIndentedString for Try {
self.block.to_indented_string(interner, indentation)
);
- if let Some(ref catch) = self.catch {
+ if let Some(catch) = self.catch() {
buf.push_str(&catch.to_indented_string(interner, indentation));
}
- if let Some(ref finally) = self.finally {
+ if let Some(finally) = self.finally() {
buf.push_str(&finally.to_indented_string(interner, indentation));
}
buf
@@ -121,10 +126,10 @@ impl VisitWith for Try {
V: Visitor<'a>,
{
try_break!(visitor.visit_block(&self.block));
- if let Some(catch) = &self.catch {
+ if let Some(catch) = &self.catch() {
try_break!(visitor.visit_catch(catch));
}
- if let Some(finally) = &self.finally {
+ if let Some(finally) = &self.finally() {
try_break!(visitor.visit_finally(finally));
}
ControlFlow::Continue(())
@@ -135,18 +140,20 @@ impl VisitWith for Try {
V: VisitorMut<'a>,
{
try_break!(visitor.visit_block_mut(&mut self.block));
- if let Some(catch) = &mut self.catch {
- try_break!(visitor.visit_catch_mut(catch));
- }
- if let Some(finally) = &mut self.finally {
- try_break!(visitor.visit_finally_mut(finally));
+ match &mut self.handler {
+ ErrorHandler::Catch(c) => try_break!(visitor.visit_catch_mut(c)),
+ ErrorHandler::Finally(f) => try_break!(visitor.visit_finally_mut(f)),
+ ErrorHandler::Full(c, f) => {
+ try_break!(visitor.visit_catch_mut(c));
+ try_break!(visitor.visit_finally_mut(f));
+ }
}
ControlFlow::Continue(())
}
}
/// Catch block.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Catch {
parameter: Option,
@@ -156,18 +163,21 @@ pub struct Catch {
impl Catch {
/// Creates a new catch block.
#[inline]
- pub(in crate::syntax) fn new(parameter: Option, block: Block) -> Self {
+ #[must_use]
+ pub fn new(parameter: Option, block: Block) -> Self {
Self { parameter, block }
}
/// Gets the parameter of the catch block.
#[inline]
+ #[must_use]
pub fn parameter(&self) -> Option<&Binding> {
self.parameter.as_ref()
}
/// Retrieves the catch execution block.
#[inline]
+ #[must_use]
pub fn block(&self) -> &Block {
&self.block
}
@@ -229,7 +239,7 @@ impl VisitWith for Catch {
}
/// Finally block.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Finally {
block: Block,
@@ -238,6 +248,7 @@ pub struct Finally {
impl Finally {
/// Gets the finally block.
#[inline]
+ #[must_use]
pub fn block(&self) -> &Block {
&self.block
}
diff --git a/boa_engine/src/syntax/ast/statement_list/mod.rs b/boa_ast/src/statement_list.rs
similarity index 88%
rename from boa_engine/src/syntax/ast/statement_list/mod.rs
rename to boa_ast/src/statement_list.rs
index c43bc3bd8c9..7f141974d9a 100644
--- a/boa_engine/src/syntax/ast/statement_list/mod.rs
+++ b/boa_ast/src/statement_list.rs
@@ -1,24 +1,21 @@
//! Statement list node.
use super::{declaration::Binding, Declaration};
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Identifier, statement::Statement, ContainsSymbol};
use crate::try_break;
+use crate::visitor::{VisitWith, Visitor, VisitorMut};
+use crate::{expression::Identifier, statement::Statement, ContainsSymbol};
use boa_interner::{Interner, ToIndentedString};
use core::ops::ControlFlow;
use rustc_hash::FxHashSet;
use std::cmp::Ordering;
-#[cfg(test)]
-mod tests;
-
/// 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 = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum StatementListItem {
/// See [`Statement`].
@@ -29,7 +26,8 @@ pub enum StatementListItem {
impl StatementListItem {
/// Returns a node ordering based on the hoistability of each statement.
- pub(crate) fn hoistable_order(a: &Self, b: &Self) -> Ordering {
+ #[must_use]
+ pub fn hoistable_order(a: &Self, b: &Self) -> Ordering {
match (a, b) {
(
Self::Declaration(Declaration::Function(_)),
@@ -42,8 +40,9 @@ impl StatementListItem {
}
}
+ /// Gets the var declared names of this `StatementListItem`.
#[inline]
- pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) {
+ pub fn var_declared_names(&self, vars: &mut FxHashSet) {
match self {
StatementListItem::Statement(stmt) => stmt.var_declared_names(vars),
StatementListItem::Declaration(_) => {}
@@ -57,7 +56,8 @@ impl StatementListItem {
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
#[inline]
- pub(crate) fn contains_arguments(&self) -> bool {
+ #[must_use]
+ pub fn contains_arguments(&self) -> bool {
match self {
StatementListItem::Statement(stmt) => stmt.contains_arguments(),
StatementListItem::Declaration(decl) => decl.contains_arguments(),
@@ -71,7 +71,8 @@ impl StatementListItem {
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
#[inline]
- pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
+ #[must_use]
+ pub fn contains(&self, symbol: ContainsSymbol) -> bool {
match self {
StatementListItem::Statement(stmt) => stmt.contains(symbol),
StatementListItem::Declaration(decl) => decl.contains(symbol),
@@ -150,7 +151,7 @@ impl VisitWith for StatementListItem {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-StatementList
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct StatementList {
statements: Box<[StatementListItem]>,
@@ -160,12 +161,14 @@ pub struct StatementList {
impl StatementList {
/// Gets the list of statements.
#[inline]
+ #[must_use]
pub fn statements(&self) -> &[StatementListItem] {
&self.statements
}
/// Get the strict mode.
#[inline]
+ #[must_use]
pub fn strict(&self) -> bool {
self.strict
}
@@ -176,14 +179,15 @@ impl StatementList {
self.strict = strict;
}
+ /// Returns the var declared names of a `StatementList`.
#[inline]
- pub(crate) fn var_declared_names(&self, vars: &mut FxHashSet) {
+ pub fn var_declared_names(&self, vars: &mut FxHashSet) {
for stmt in &*self.statements {
stmt.var_declared_names(vars);
}
}
- /// Return the lexically declared names of a `StatementList`.
+ /// Returns the lexically declared names of a `StatementList`.
///
/// The returned list may contain duplicates.
///
@@ -193,7 +197,8 @@ impl StatementList {
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames
- pub(crate) fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> {
+ #[must_use]
+ pub fn lexically_declared_names(&self) -> Vec<(Identifier, bool)> {
let mut names = Vec::new();
for node in self.statements() {
@@ -216,7 +221,8 @@ impl StatementList {
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevellexicallydeclarednames
- pub(crate) fn lexically_declared_names_top_level(&self) -> Vec {
+ #[must_use]
+ pub fn lexically_declared_names_top_level(&self) -> Vec {
let mut names = Vec::new();
for node in self.statements() {
@@ -254,7 +260,7 @@ impl StatementList {
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
#[inline]
- pub(crate) fn contains_arguments(&self) -> bool {
+ pub fn contains_arguments(&self) -> bool {
self.statements
.iter()
.any(StatementListItem::contains_arguments)
@@ -267,7 +273,8 @@ impl StatementList {
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains
#[inline]
- pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
+ #[must_use]
+ pub fn contains(&self, symbol: ContainsSymbol) -> bool {
self.statements.iter().any(|stmt| stmt.contains(symbol))
}
}
diff --git a/boa_engine/src/syntax/ast/visitor.rs b/boa_ast/src/visitor.rs
similarity index 94%
rename from boa_engine/src/syntax/ast/visitor.rs
rename to boa_ast/src/visitor.rs
index ea38a122ee7..dd6e8be2be5 100644
--- a/boa_engine/src/syntax/ast/visitor.rs
+++ b/boa_ast/src/visitor.rs
@@ -14,38 +14,38 @@ macro_rules! try_break {
};
}
-use crate::syntax::ast::declaration::{
+use crate::declaration::{
Binding, Declaration, LexicalDeclaration, VarDeclaration, Variable, VariableList,
};
-use crate::syntax::ast::expression::access::{
+use crate::expression::access::{
PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SimplePropertyAccess,
SuperPropertyAccess,
};
-use crate::syntax::ast::expression::literal::{
+use crate::expression::literal::{
ArrayLiteral, Literal, ObjectLiteral, TemplateElement, TemplateLiteral,
};
-use crate::syntax::ast::expression::operator::assign::{Assign, AssignTarget};
-use crate::syntax::ast::expression::operator::{Binary, Conditional, Unary};
-use crate::syntax::ast::expression::{
+use crate::expression::operator::assign::{Assign, AssignTarget};
+use crate::expression::operator::{Binary, Conditional, Unary};
+use crate::expression::{
Await, Call, Expression, Identifier, New, Optional, OptionalOperation, OptionalOperationKind,
Spread, SuperCall, TaggedTemplate, Yield,
};
-use crate::syntax::ast::function::{
+use crate::function::{
ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameter,
FormalParameterList, Function, Generator,
};
-use crate::syntax::ast::pattern::{
+use crate::pattern::{
ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern,
};
-use crate::syntax::ast::property::{MethodDefinition, PropertyDefinition, PropertyName};
-use crate::syntax::ast::statement::iteration::{
+use crate::property::{MethodDefinition, PropertyDefinition, PropertyName};
+use crate::statement::iteration::{
Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForLoopInitializer, ForOfLoop,
IterableLoopInitializer, WhileLoop,
};
-use crate::syntax::ast::statement::{
+use crate::statement::{
Block, Case, Catch, Finally, If, Labelled, LabelledItem, Return, Statement, Switch, Throw, Try,
};
-use crate::syntax::ast::{StatementList, StatementListItem};
+use crate::{StatementList, StatementListItem};
use boa_interner::Sym;
/// Creates the default visit function implementation for a particular type
@@ -77,7 +77,6 @@ macro_rules! define_visit_mut {
///
/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s
/// visitor pattern.
-#[allow(unused_variables)]
pub trait Visitor<'ast>: Sized {
/// Type which will be propagated from the visitor if completing early.
type BreakTy;
@@ -162,7 +161,6 @@ pub trait Visitor<'ast>: Sized {
///
/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s
/// visitor pattern.
-#[allow(unused_variables)]
pub trait VisitorMut<'ast>: Sized {
/// Type which will be propagated from the visitor if completing early.
type BreakTy;
diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml
index 5ad745d93eb..3d9963d70e8 100644
--- a/boa_cli/Cargo.toml
+++ b/boa_cli/Cargo.toml
@@ -13,6 +13,7 @@ rust-version.workspace = true
[dependencies]
boa_engine = { workspace = true, features = ["deser", "console"] }
+boa_ast = { workspace = true, features = ["serde"]}
boa_interner.workspace = true
rustyline = "10.0.0"
rustyline-derive = "0.7.0"
diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs
index 88694009e0e..b330225aa4f 100644
--- a/boa_cli/src/main.rs
+++ b/boa_cli/src/main.rs
@@ -59,7 +59,8 @@
rustdoc::missing_doc_code_examples
)]
-use boa_engine::{syntax::ast::StatementList, Context};
+use boa_ast::StatementList;
+use boa_engine::Context;
use clap::{Parser, ValueEnum, ValueHint};
use colored::{Color, Colorize};
use rustyline::{config::Config, error::ReadlineError, EditMode, Editor};
diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml
index b10e9e90982..b5f443da7e0 100644
--- a/boa_engine/Cargo.toml
+++ b/boa_engine/Cargo.toml
@@ -13,7 +13,7 @@ rust-version.workspace = true
[features]
profiler = ["boa_profiler/profiler"]
-deser = ["boa_interner/serde"]
+deser = ["boa_interner/serde", "boa_ast/serde"]
intl = [
"dep:icu_locale_canonicalizer",
"dep:icu_locid",
@@ -33,6 +33,7 @@ boa_interner.workspace = true
boa_gc.workspace = true
boa_profiler.workspace = true
boa_macros.workspace = true
+boa_ast.workspace = true
gc = "0.4.1"
serde = { version = "1.0.147", features = ["derive", "rc"] }
serde_json = "1.0.87"
diff --git a/boa_engine/src/builtins/function/arguments.rs b/boa_engine/src/builtins/function/arguments.rs
index 798364c1612..4532eaaaa80 100644
--- a/boa_engine/src/builtins/function/arguments.rs
+++ b/boa_engine/src/builtins/function/arguments.rs
@@ -3,9 +3,9 @@ use crate::{
object::{JsObject, ObjectData},
property::PropertyDescriptor,
symbol::{self, WellKnownSymbols},
- syntax::ast::function::FormalParameterList,
Context, JsValue,
};
+use boa_ast::function::FormalParameterList;
use boa_gc::{Finalize, Gc, Trace};
use rustc_hash::FxHashMap;
@@ -199,7 +199,7 @@ impl Arguments {
let mut bindings = FxHashMap::default();
let mut property_index = 0;
- 'outer: for formal in formals.parameters.iter() {
+ 'outer: for formal in formals.as_ref() {
for name in formal.names() {
if property_index >= len {
break 'outer;
diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs
index 3741d63b3d7..0236c85e775 100644
--- a/boa_engine/src/builtins/function/mod.rs
+++ b/boa_engine/src/builtins/function/mod.rs
@@ -26,10 +26,11 @@ use crate::{
property::{Attribute, PropertyDescriptor, PropertyKey},
string::utf16,
symbol::WellKnownSymbols,
- syntax::{ast::function::FormalParameterList, ast::StatementList, Parser},
+ syntax::Parser,
value::IntegerOrInfinity,
Context, JsResult, JsString, JsValue,
};
+use boa_ast::{function::FormalParameterList, StatementList};
use boa_gc::{self, custom_trace, Finalize, Gc, Trace};
use boa_interner::Sym;
use boa_profiler::Profiler;
@@ -493,7 +494,7 @@ impl BuiltInFunctionObject {
let prototype = get_prototype_from_constructor(new_target, default, context)?;
if let Some((body_arg, args)) = args.split_last() {
let parameters = if args.is_empty() {
- FormalParameterList::empty()
+ FormalParameterList::default()
} else {
let mut parameters = Vec::with_capacity(args.len());
for arg in args {
@@ -554,7 +555,7 @@ impl BuiltInFunctionObject {
// Early Error: If BindingIdentifier is present and the source text matched by BindingIdentifier is strict mode code,
// it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments".
if body.strict() {
- for parameter in parameters.parameters.iter() {
+ for parameter in parameters.as_ref() {
for name in parameter.names() {
if name == Sym::ARGUMENTS || name == Sym::EVAL {
return Err(JsNativeError::syntax()
@@ -588,7 +589,7 @@ impl BuiltInFunctionObject {
// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
{
let lexically_declared_names = body.lexically_declared_names();
- for param in parameters.parameters.as_ref() {
+ for param in parameters.as_ref() {
for param_name in param.names() {
if lexically_declared_names
.iter()
@@ -627,7 +628,7 @@ impl BuiltInFunctionObject {
.name(Sym::ANONYMOUS)
.generator(true)
.compile(
- &FormalParameterList::empty(),
+ &FormalParameterList::default(),
&StatementList::default(),
context,
)?;
@@ -640,7 +641,7 @@ impl BuiltInFunctionObject {
Ok(function_object)
} else {
let code = FunctionCompiler::new().name(Sym::ANONYMOUS).compile(
- &FormalParameterList::empty(),
+ &FormalParameterList::default(),
&StatementList::default(),
context,
)?;
diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs
index cacf9d02bb2..08440efdb25 100644
--- a/boa_engine/src/bytecompiler/function.rs
+++ b/boa_engine/src/bytecompiler/function.rs
@@ -1,10 +1,10 @@
use crate::{
builtins::function::ThisMode,
bytecompiler::ByteCompiler,
- syntax::ast::{declaration::Binding, function::FormalParameterList, StatementList},
vm::{BindingOpcode, CodeBlock, Opcode},
Context, JsResult,
};
+use boa_ast::{declaration::Binding, function::FormalParameterList, StatementList};
use boa_gc::Gc;
use boa_interner::Sym;
use rustc_hash::FxHashMap;
@@ -117,7 +117,7 @@ impl FunctionCompiler {
);
}
- for parameter in parameters.parameters.iter() {
+ for parameter in parameters.as_ref() {
if parameter.is_rest_param() {
compiler.emit_opcode(Opcode::RestParameterInit);
}
diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs
index 8fdec12531c..180040dc1c9 100644
--- a/boa_engine/src/bytecompiler/mod.rs
+++ b/boa_engine/src/bytecompiler/mod.rs
@@ -2,33 +2,33 @@ mod function;
use crate::{
environments::{BindingLocator, CompileTimeEnvironment},
- syntax::ast::{
- declaration::{Binding, LexicalDeclaration, VarDeclaration},
- expression::{
- access::{PropertyAccess, PropertyAccessField},
- literal::{self, TemplateElement},
- operator::{
- assign::{AssignOp, AssignTarget},
- binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp},
- unary::UnaryOp,
- },
- Call, Identifier, New, Optional, OptionalOperationKind,
- },
- function::{
- ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameterList,
- Function, Generator,
- },
- pattern::{ArrayPatternElement, ObjectPatternElement, Pattern},
- property::{MethodDefinition, PropertyDefinition, PropertyName},
- statement::{
- iteration::{ForLoopInitializer, IterableLoopInitializer},
- Block, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, LabelledItem, WhileLoop,
- },
- Declaration, Expression, Statement, StatementList, StatementListItem,
- },
vm::{BindingOpcode, CodeBlock, Opcode},
Context, JsBigInt, JsNativeError, JsResult, JsString, JsValue,
};
+use boa_ast::{
+ declaration::{Binding, LexicalDeclaration, VarDeclaration},
+ expression::{
+ access::{PropertyAccess, PropertyAccessField},
+ literal::{self, TemplateElement},
+ operator::{
+ assign::{AssignOp, AssignTarget},
+ binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp},
+ unary::UnaryOp,
+ },
+ Call, Identifier, New, Optional, OptionalOperationKind,
+ },
+ function::{
+ ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, FormalParameterList,
+ Function, Generator,
+ },
+ pattern::{ArrayPatternElement, ObjectPatternElement, Pattern},
+ property::{MethodDefinition, PropertyDefinition, PropertyName},
+ statement::{
+ iteration::{ForLoopInitializer, IterableLoopInitializer},
+ Block, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, LabelledItem, WhileLoop,
+ },
+ Declaration, Expression, Statement, StatementList, StatementListItem,
+};
use boa_gc::Gc;
use boa_interner::{Interner, Sym};
use rustc_hash::FxHashMap;
@@ -1483,13 +1483,13 @@ impl<'b> ByteCompiler<'b> {
Expression::Class(class) => self.class(class, true)?,
Expression::SuperCall(super_call) => {
let contains_spread = super_call
- .args()
+ .arguments()
.iter()
.any(|arg| matches!(arg, Expression::Spread(_)));
if contains_spread {
self.emit_opcode(Opcode::PushNewArray);
- for arg in super_call.args() {
+ for arg in super_call.arguments() {
self.compile_expr(arg, true)?;
if let Expression::Spread(_) = arg {
self.emit_opcode(Opcode::InitIterator);
@@ -1499,7 +1499,7 @@ impl<'b> ByteCompiler<'b> {
}
}
} else {
- for arg in super_call.args() {
+ for arg in super_call.arguments() {
self.compile_expr(arg, true)?;
}
}
@@ -1507,7 +1507,7 @@ impl<'b> ByteCompiler<'b> {
if contains_spread {
self.emit_opcode(Opcode::SuperCallSpread);
} else {
- self.emit(Opcode::SuperCall, &[super_call.args().len() as u32]);
+ self.emit(Opcode::SuperCall, &[super_call.arguments().len() as u32]);
}
if !use_expr {
@@ -2589,9 +2589,9 @@ impl<'b> ByteCompiler<'b> {
let push_env =
self.emit_opcode_with_two_operands(Opcode::PushDeclarativeEnvironment);
- self.create_decls(finally.statement_list(), configurable_globals);
+ self.create_decls(finally.block().statement_list(), configurable_globals);
self.compile_statement_list(
- finally.statement_list(),
+ finally.block().statement_list(),
false,
configurable_globals,
)?;
@@ -3207,7 +3207,7 @@ impl<'b> ByteCompiler<'b> {
.context
.initialize_mutable_binding(Sym::ARGUMENTS.into(), false),
);
- for parameter in expr.parameters().parameters.iter() {
+ for parameter in expr.parameters().as_ref() {
if parameter.is_rest_param() {
compiler.emit_opcode(Opcode::RestParameterInit);
}
diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs
index 0a4d592cf26..f2c97fb751d 100644
--- a/boa_engine/src/context/mod.rs
+++ b/boa_engine/src/context/mod.rs
@@ -20,11 +20,12 @@ use crate::{
object::{FunctionBuilder, GlobalPropertyMap, JsObject, ObjectData},
property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm,
- syntax::{ast::StatementList, parser::ParseError, Parser},
+ syntax::{parser::ParseError, Parser},
vm::{CallFrame, CodeBlock, FinallyReturn, GeneratorResumeKind, Vm},
JsResult, JsString, JsValue,
};
+use boa_ast::StatementList;
use boa_gc::Gc;
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs
index 1b30759b2bf..79e30e1acc8 100644
--- a/boa_engine/src/environments/compile.rs
+++ b/boa_engine/src/environments/compile.rs
@@ -1,7 +1,7 @@
use crate::{
- environments::runtime::BindingLocator, property::PropertyDescriptor,
- syntax::ast::expression::Identifier, Context, JsString, JsValue,
+ environments::runtime::BindingLocator, property::PropertyDescriptor, Context, JsString, JsValue,
};
+use boa_ast::expression::Identifier;
use boa_gc::{Cell, Finalize, Gc, Trace};
use rustc_hash::FxHashMap;
diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs
index b1a60202549..7d2f9569417 100644
--- a/boa_engine/src/environments/runtime.rs
+++ b/boa_engine/src/environments/runtime.rs
@@ -1,11 +1,11 @@
use std::cell::Cell;
use crate::{
- environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject,
- syntax::ast::expression::Identifier, Context, JsValue,
+ environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, JsValue,
};
use boa_gc::{Cell as GcCell, Finalize, Gc, Trace};
+use boa_ast::expression::Identifier;
use rustc_hash::FxHashSet;
/// A declarative environment holds binding values at runtime.
diff --git a/boa_engine/src/syntax/ast/expression/literal/array.rs b/boa_engine/src/syntax/ast/expression/literal/array.rs
deleted file mode 100644
index b072b72a40c..00000000000
--- a/boa_engine/src/syntax/ast/expression/literal/array.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-//! Array declaration Expression.
-
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{expression::Expression, ContainsSymbol};
-use crate::try_break;
-use boa_interner::{Interner, ToInternedString};
-use core::ops::ControlFlow;
-
-/// An array is an ordered collection of data (either primitive or object depending upon the
-/// language).
-///
-/// Arrays are used to store multiple values in a single variable.
-/// This is compared to a variable that can store only one value.
-///
-/// Each item in an array has a number attached to it, called a numeric index, that allows you
-/// to access it. In JavaScript, arrays start at index zero and can be manipulated with various
-/// methods.
-///
-/// More information:
-/// - [ECMAScript reference][spec]
-/// - [MDN documentation][mdn]
-///
-/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral
-/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone, Debug, PartialEq)]
-pub struct ArrayLiteral {
- arr: Box<[Option]>,
- has_trailing_comma_spread: bool,
-}
-
-impl ArrayLiteral {
- /// Crate a new array literal.
- pub(crate) fn new(array: A, has_trailing_comma_spread: bool) -> Self
- where
- A: Into]>>,
- {
- Self {
- arr: array.into(),
- has_trailing_comma_spread,
- }
- }
-
- /// Indicates if a spread operator in the array literal has a trailing comma.
- /// This is a syntax error in some cases.
- pub(crate) fn has_trailing_comma_spread(&self) -> bool {
- self.has_trailing_comma_spread
- }
-
- #[inline]
- pub(crate) fn contains_arguments(&self) -> bool {
- self.arr
- .iter()
- .flatten()
- .any(Expression::contains_arguments)
- }
-
- #[inline]
- pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
- self.arr.iter().flatten().any(|expr| expr.contains(symbol))
- }
-}
-
-impl AsRef<[Option]> for ArrayLiteral {
- #[inline]
- fn as_ref(&self) -> &[Option] {
- &self.arr
- }
-}
-
-impl From for ArrayLiteral
-where
- T: Into]>>,
-{
- #[inline]
- fn from(decl: T) -> Self {
- Self {
- arr: decl.into(),
- has_trailing_comma_spread: false,
- }
- }
-}
-
-impl ToInternedString for ArrayLiteral {
- #[inline]
- fn to_interned_string(&self, interner: &Interner) -> String {
- let mut buf = String::from("[");
- let mut first = true;
- for e in &*self.arr {
- if first {
- first = false;
- } else {
- buf.push_str(", ");
- }
- if let Some(e) = e {
- buf.push_str(&e.to_interned_string(interner));
- }
- }
- buf.push(']');
- buf
- }
-}
-
-impl From for Expression {
- #[inline]
- fn from(arr: ArrayLiteral) -> Self {
- Self::ArrayLiteral(arr)
- }
-}
-
-impl VisitWith for ArrayLiteral {
- fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
- where
- V: Visitor<'a>,
- {
- for expr in self.arr.iter().flatten() {
- try_break!(visitor.visit_expression(expr));
- }
- ControlFlow::Continue(())
- }
-
- fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow
- where
- V: VisitorMut<'a>,
- {
- for expr in self.arr.iter_mut().flatten() {
- try_break!(visitor.visit_expression_mut(expr));
- }
- ControlFlow::Continue(())
- }
-}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- let a = [1, 2, 3, "words", "more words"];
- let b = [];
- "#,
- );
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs b/boa_engine/src/syntax/ast/expression/literal/object/mod.rs
deleted file mode 100644
index e3e410feacf..00000000000
--- a/boa_engine/src/syntax/ast/expression/literal/object/mod.rs
+++ /dev/null
@@ -1,180 +0,0 @@
-//! Object Expression.
-
-#[cfg(test)]
-mod tests;
-
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::ast::{
- block_to_string,
- expression::Expression,
- join_nodes,
- property::{MethodDefinition, PropertyDefinition},
- ContainsSymbol,
-};
-use crate::try_break;
-use boa_interner::{Interner, ToIndentedString, ToInternedString};
-use core::ops::ControlFlow;
-
-/// Objects in JavaScript may be defined as an unordered collection of related data, of
-/// primitive or reference types, in the form of “key: value” pairs.
-///
-/// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal
-/// notation.
-///
-/// An object initializer is an expression that describes the initialization of an
-/// [`Object`][object]. Objects consist of properties, which are used to describe an object.
-/// Values of object properties can either contain [`primitive`][primitive] data types or other
-/// objects.
-///
-/// More information:
-/// - [ECMAScript reference][spec]
-/// - [MDN documentation][mdn]
-///
-/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral
-/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
-/// [object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
-/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "deser", serde(transparent))]
-#[derive(Clone, Debug, PartialEq)]
-pub struct ObjectLiteral {
- properties: Box<[PropertyDefinition]>,
-}
-
-impl ObjectLiteral {
- /// Gets the object literal properties
- #[inline]
- pub fn properties(&self) -> &[PropertyDefinition] {
- &self.properties
- }
-
- #[inline]
- pub(crate) fn contains_arguments(&self) -> bool {
- self.properties
- .iter()
- .any(PropertyDefinition::contains_arguments)
- }
-
- #[inline]
- pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
- self.properties.iter().any(|prop| prop.contains(symbol))
- }
-}
-
-impl ToIndentedString for ObjectLiteral {
- fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {
- let mut buf = "{\n".to_owned();
- let indentation = " ".repeat(indent_n + 1);
- for property in self.properties().iter() {
- buf.push_str(&match property {
- PropertyDefinition::IdentifierReference(ident) => {
- format!("{indentation}{},\n", interner.resolve_expect(ident.sym()))
- }
- PropertyDefinition::Property(key, value) => {
- format!(
- "{indentation}{}: {},\n",
- key.to_interned_string(interner),
- value.to_no_indent_string(interner, indent_n + 1)
- )
- }
- PropertyDefinition::SpreadObject(key) => {
- format!("{indentation}...{},\n", key.to_interned_string(interner))
- }
- PropertyDefinition::MethodDefinition(key, method) => {
- format!(
- "{indentation}{}{}({}) {},\n",
- match &method {
- MethodDefinition::Get(_) => "get ",
- MethodDefinition::Set(_) => "set ",
- _ => "",
- },
- key.to_interned_string(interner),
- match &method {
- MethodDefinition::Get(expression)
- | MethodDefinition::Set(expression)
- | MethodDefinition::Ordinary(expression) => {
- join_nodes(interner, &expression.parameters().parameters)
- }
- MethodDefinition::Generator(expression) => {
- join_nodes(interner, &expression.parameters().parameters)
- }
- MethodDefinition::AsyncGenerator(expression) => {
- join_nodes(interner, &expression.parameters().parameters)
- }
- MethodDefinition::Async(expression) => {
- join_nodes(interner, &expression.parameters().parameters)
- }
- },
- match &method {
- MethodDefinition::Get(expression)
- | MethodDefinition::Set(expression)
- | MethodDefinition::Ordinary(expression) => {
- block_to_string(expression.body(), interner, indent_n + 1)
- }
- MethodDefinition::Generator(expression) => {
- block_to_string(expression.body(), interner, indent_n + 1)
- }
- MethodDefinition::AsyncGenerator(expression) => {
- block_to_string(expression.body(), interner, indent_n + 1)
- }
- MethodDefinition::Async(expression) => {
- block_to_string(expression.body(), interner, indent_n + 1)
- }
- },
- )
- }
- PropertyDefinition::CoverInitializedName(ident, expr) => {
- format!(
- "{indentation}{} = {},\n",
- interner.resolve_expect(ident.sym()),
- expr.to_no_indent_string(interner, indent_n + 1)
- )
- }
- });
- }
- buf.push_str(&format!("{}}}", " ".repeat(indent_n)));
-
- buf
- }
-}
-
-impl From for ObjectLiteral
-where
- T: Into>,
-{
- #[inline]
- fn from(props: T) -> Self {
- Self {
- properties: props.into(),
- }
- }
-}
-
-impl From for Expression {
- #[inline]
- fn from(obj: ObjectLiteral) -> Self {
- Self::ObjectLiteral(obj)
- }
-}
-
-impl VisitWith for ObjectLiteral {
- fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
- where
- V: Visitor<'a>,
- {
- for pd in self.properties.iter() {
- try_break!(visitor.visit_property_definition(pd));
- }
- ControlFlow::Continue(())
- }
-
- fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow
- where
- V: VisitorMut<'a>,
- {
- for pd in self.properties.iter_mut() {
- try_break!(visitor.visit_property_definition_mut(pd));
- }
- ControlFlow::Continue(())
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/literal/object/tests.rs b/boa_engine/src/syntax/ast/expression/literal/object/tests.rs
deleted file mode 100644
index 3f078f5645f..00000000000
--- a/boa_engine/src/syntax/ast/expression/literal/object/tests.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-use crate::exec;
-
-#[test]
-fn spread_shallow_clone() {
- let scenario = r#"
- var a = { x: {} };
- var aClone = { ...a };
-
- a.x === aClone.x
- "#;
- assert_eq!(&exec(scenario), "true");
-}
-
-#[test]
-fn spread_merge() {
- let scenario = r#"
- var a = { x: 1, y: 2 };
- var b = { x: -1, z: -3, ...a };
-
- (b.x === 1) && (b.y === 2) && (b.z === -3)
- "#;
- assert_eq!(&exec(scenario), "true");
-}
-
-#[test]
-fn spread_overriding_properties() {
- let scenario = r#"
- var a = { x: 0, y: 0 };
- var aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
-
- (aWithOverrides.x === 1) && (aWithOverrides.y === 2)
- "#;
- assert_eq!(&exec(scenario), "true");
-}
-
-#[test]
-fn spread_getters_in_initializer() {
- let scenario = r#"
- var a = { x: 42 };
- var aWithXGetter = { ...a, get x() { throw new Error('not thrown yet') } };
- "#;
- assert_eq!(&exec(scenario), "undefined");
-}
-
-#[test]
-fn spread_getters_in_object() {
- let scenario = r#"
- var a = { x: 42 };
- var aWithXGetter = { ...a, ... { get x() { throw new Error('not thrown yet') } } };
- "#;
- assert_eq!(&exec(scenario), "\"Error\": \"not thrown yet\"");
-}
-
-#[test]
-fn spread_setters() {
- let scenario = r#"
- var z = { set x(nexX) { throw new Error() }, ... { x: 1 } };
- "#;
- assert_eq!(&exec(scenario), "undefined");
-}
-
-#[test]
-fn spread_null_and_undefined_ignored() {
- let scenario = r#"
- var a = { ...null, ...undefined };
- var count = 0;
-
- for (key in a) { count++; }
-
- count === 0
- "#;
-
- assert_eq!(&exec(scenario), "true");
-}
-
-#[test]
-fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- let other = {
- c: 10,
- };
- let inst = {
- val: 5,
- b: "hello world",
- nested: {
- a: 5,
- b: 6,
- },
- ...other,
- say_hi: function() {
- console.log("hello!");
- },
- get a() {
- return this.val + 1;
- },
- set a(new_value) {
- this.val = new_value;
- },
- say_hello(msg) {
- console.log("hello " + msg);
- },
- };
- inst.a = 20;
- inst.a;
- inst.say_hello("humans");
- "#,
- );
-}
diff --git a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs b/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs
deleted file mode 100644
index bc6d339245a..00000000000
--- a/boa_engine/src/syntax/ast/expression/operator/assign/mod.rs
+++ /dev/null
@@ -1,475 +0,0 @@
-//! Assignment expression nodes, as defined by the [spec].
-//!
-//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right
-//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple
-//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=`
-//! only allow ["simple"][simple] left hand side expressions as an assignment target.
-//!
-//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
-//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
-//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression
-//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype
-
-mod op;
-
-use core::ops::ControlFlow;
-pub use op::*;
-
-use boa_interner::{Interner, Sym, ToInternedString};
-
-use crate::syntax::ast::visitor::{VisitWith, Visitor, VisitorMut};
-use crate::syntax::{
- ast::{
- expression::{
- access::PropertyAccess,
- identifier::Identifier,
- literal::{ArrayLiteral, ObjectLiteral},
- Expression,
- },
- pattern::{
- ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern,
- },
- property::{PropertyDefinition, PropertyName},
- ContainsSymbol,
- },
- parser::RESERVED_IDENTIFIERS_STRICT,
-};
-use crate::try_break;
-
-/// An assignment operator expression.
-///
-/// See the [module level documentation][self] for more information.
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone, Debug, PartialEq)]
-pub struct Assign {
- op: AssignOp,
- lhs: Box,
- rhs: Box,
-}
-
-impl Assign {
- /// Creates an `Assign` AST Expression.
- pub(in crate::syntax) fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self {
- Self {
- op,
- lhs: Box::new(lhs),
- rhs: Box::new(rhs),
- }
- }
-
- /// Gets the operator of the assignment operation.
- #[inline]
- pub fn op(&self) -> AssignOp {
- self.op
- }
-
- /// Gets the left hand side of the assignment operation.
- #[inline]
- pub fn lhs(&self) -> &AssignTarget {
- &self.lhs
- }
-
- /// Gets the right hand side of the assignment operation.
- #[inline]
- pub fn rhs(&self) -> &Expression {
- &self.rhs
- }
-
- #[inline]
- pub(crate) fn contains_arguments(&self) -> bool {
- (match &*self.lhs {
- AssignTarget::Identifier(ident) => *ident == Sym::ARGUMENTS,
- AssignTarget::Access(access) => access.contains_arguments(),
- AssignTarget::Pattern(pattern) => pattern.contains_arguments(),
- } || self.rhs.contains_arguments())
- }
-
- #[inline]
- pub(crate) fn contains(&self, symbol: ContainsSymbol) -> bool {
- (match &*self.lhs {
- AssignTarget::Identifier(_) => false,
- AssignTarget::Access(access) => access.contains(symbol),
- AssignTarget::Pattern(pattern) => pattern.contains(symbol),
- } || self.rhs.contains(symbol))
- }
-}
-
-impl ToInternedString for Assign {
- #[inline]
- fn to_interned_string(&self, interner: &Interner) -> String {
- format!(
- "{} {} {}",
- self.lhs.to_interned_string(interner),
- self.op,
- self.rhs.to_interned_string(interner)
- )
- }
-}
-
-impl From for Expression {
- #[inline]
- fn from(op: Assign) -> Self {
- Self::Assign(op)
- }
-}
-
-impl VisitWith for Assign {
- fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
- where
- V: Visitor<'a>,
- {
- try_break!(visitor.visit_assign_target(&self.lhs));
- visitor.visit_expression(&self.rhs)
- }
-
- fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow
- where
- V: VisitorMut<'a>,
- {
- try_break!(visitor.visit_assign_target_mut(&mut self.lhs));
- visitor.visit_expression_mut(&mut self.rhs)
- }
-}
-
-/// The valid left-hand-side expressions of an assignment operator. Also called
-/// [`LeftHandSideExpression`][spec] in the spec.
-///
-/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression
-#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone, Debug, PartialEq)]
-pub enum AssignTarget {
- /// A simple identifier, such as `a`.
- Identifier(Identifier),
- /// A property access, such as `a.prop`.
- Access(PropertyAccess),
- /// A pattern assignment, such as `{a, b, ...c}`.
- Pattern(Pattern),
-}
-
-impl AssignTarget {
- /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].
- /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.
- pub(crate) fn from_expression(
- expression: &Expression,
- strict: bool,
- destructure: bool,
- ) -> Option {
- match expression {
- Expression::Identifier(id) => Some(Self::Identifier(*id)),
- Expression::PropertyAccess(access) => Some(Self::Access(access.clone())),
- Expression::ObjectLiteral(object) if destructure => {
- let pattern = object_decl_to_declaration_pattern(object, strict)?;
- Some(Self::Pattern(pattern.into()))
- }
- Expression::ArrayLiteral(array) if destructure => {
- let pattern = array_decl_to_declaration_pattern(array, strict)?;
- Some(Self::Pattern(pattern.into()))
- }
- _ => None,
- }
- }
-}
-
-impl ToInternedString for AssignTarget {
- #[inline]
- fn to_interned_string(&self, interner: &Interner) -> String {
- match self {
- AssignTarget::Identifier(id) => id.to_interned_string(interner),
- AssignTarget::Access(access) => access.to_interned_string(interner),
- AssignTarget::Pattern(pattern) => pattern.to_interned_string(interner),
- }
- }
-}
-
-impl From for AssignTarget {
- #[inline]
- fn from(target: Identifier) -> Self {
- Self::Identifier(target)
- }
-}
-
-/// Converts an object literal into an object declaration pattern.
-pub(crate) fn object_decl_to_declaration_pattern(
- object: &ObjectLiteral,
- strict: bool,
-) -> Option {
- let mut bindings = Vec::new();
- let mut excluded_keys = Vec::new();
- for (i, property) in object.properties().iter().enumerate() {
- match property {
- PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => {
- return None
- }
- PropertyDefinition::IdentifierReference(ident) => {
- if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) {
- return None;
- }
-
- excluded_keys.push(*ident);
- bindings.push(ObjectPatternElement::SingleName {
- ident: *ident,
- name: PropertyName::Literal(ident.sym()),
- default_init: None,
- });
- }
- PropertyDefinition::Property(name, expr) => match (name, expr) {
- (PropertyName::Literal(name), Expression::Identifier(ident)) if *name == *ident => {
- if strict && *name == Sym::EVAL {
- return None;
- }
- if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) {
- return None;
- }
-
- excluded_keys.push(*ident);
- bindings.push(ObjectPatternElement::SingleName {
- ident: *ident,
- name: PropertyName::Literal(*name),
- default_init: None,
- });
- }
- (PropertyName::Literal(name), Expression::Identifier(ident)) => {
- bindings.push(ObjectPatternElement::SingleName {
- ident: *ident,
- name: PropertyName::Literal(*name),
- default_init: None,
- });
- }
- (PropertyName::Literal(name), Expression::ObjectLiteral(object)) => {
- let pattern = object_decl_to_declaration_pattern(object, strict)?.into();
- bindings.push(ObjectPatternElement::Pattern {
- name: PropertyName::Literal(*name),
- pattern,
- default_init: None,
- });
- }
- (PropertyName::Literal(name), Expression::ArrayLiteral(array)) => {
- let pattern = array_decl_to_declaration_pattern(array, strict)?.into();
- bindings.push(ObjectPatternElement::Pattern {
- name: PropertyName::Literal(*name),
- pattern,
- default_init: None,
- });
- }
- (_, Expression::Assign(assign)) => match assign.lhs() {
- AssignTarget::Identifier(ident) => {
- if let Some(name) = name.literal() {
- if name == *ident {
- if strict && name == Sym::EVAL {
- return None;
- }
- if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name) {
- return None;
- }
- excluded_keys.push(*ident);
- bindings.push(ObjectPatternElement::SingleName {
- ident: *ident,
- name: PropertyName::Literal(name),
- default_init: Some(assign.rhs().clone()),
- });
- } else {
- bindings.push(ObjectPatternElement::SingleName {
- ident: *ident,
- name: PropertyName::Literal(name),
- default_init: Some(assign.rhs().clone()),
- });
- }
- } else {
- return None;
- }
- }
- AssignTarget::Pattern(pattern) => {
- bindings.push(ObjectPatternElement::Pattern {
- name: name.clone(),
- pattern: pattern.clone(),
- default_init: Some(assign.rhs().clone()),
- });
- }
- AssignTarget::Access(access) => {
- bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
- name: name.clone(),
- access: access.clone(),
- default_init: Some(assign.rhs().clone()),
- });
- }
- },
- (_, Expression::PropertyAccess(access)) => {
- bindings.push(ObjectPatternElement::AssignmentPropertyAccess {
- name: name.clone(),
- access: access.clone(),
- default_init: None,
- });
- }
- (PropertyName::Computed(name), Expression::Identifier(ident)) => {
- bindings.push(ObjectPatternElement::SingleName {
- ident: *ident,
- name: PropertyName::Computed(name.clone()),
- default_init: None,
- });
- }
- _ => return None,
- },
- PropertyDefinition::SpreadObject(spread) => {
- match spread {
- Expression::Identifier(ident) => {
- bindings.push(ObjectPatternElement::RestProperty {
- ident: *ident,
- excluded_keys: excluded_keys.clone(),
- });
- }
- Expression::PropertyAccess(access) => {
- bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess {
- access: access.clone(),
- excluded_keys: excluded_keys.clone(),
- });
- }
- _ => return None,
- }
- if i + 1 != object.properties().len() {
- return None;
- }
- }
- PropertyDefinition::MethodDefinition(_, _) => return None,
- PropertyDefinition::CoverInitializedName(ident, expr) => {
- if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) {
- return None;
- }
-
- bindings.push(ObjectPatternElement::SingleName {
- ident: *ident,
- name: PropertyName::Literal(ident.sym()),
- default_init: Some(expr.clone()),
- });
- }
- }
- }
- Some(ObjectPattern::new(bindings.into()))
-}
-
-/// Converts an array declaration into an array declaration pattern.
-pub(crate) fn array_decl_to_declaration_pattern(
- array: &ArrayLiteral,
- strict: bool,
-) -> Option {
- if array.has_trailing_comma_spread() {
- return None;
- }
-
- let mut bindings = Vec::new();
- for (i, expr) in array.as_ref().iter().enumerate() {
- let expr = if let Some(expr) = expr {
- expr
- } else {
- bindings.push(ArrayPatternElement::Elision);
- continue;
- };
- match expr {
- Expression::Identifier(ident) => {
- if strict && *ident == Sym::ARGUMENTS {
- return None;
- }
-
- bindings.push(ArrayPatternElement::SingleName {
- ident: *ident,
- default_init: None,
- });
- }
- Expression::Spread(spread) => {
- match spread.target() {
- Expression::Identifier(ident) => {
- bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident });
- }
- Expression::PropertyAccess(access) => {
- bindings.push(ArrayPatternElement::PropertyAccessRest {
- access: access.clone(),
- });
- }
- Expression::ArrayLiteral(array) => {
- let pattern = array_decl_to_declaration_pattern(array, strict)?.into();
- bindings.push(ArrayPatternElement::PatternRest { pattern });
- }
- Expression::ObjectLiteral(object) => {
- let pattern = object_decl_to_declaration_pattern(object, strict)?.into();
- bindings.push(ArrayPatternElement::PatternRest { pattern });
- }
- _ => return None,
- }
- if i + 1 != array.as_ref().len() {
- return None;
- }
- }
- Expression::Assign(assign) => match assign.lhs() {
- AssignTarget::Identifier(ident) => {
- bindings.push(ArrayPatternElement::SingleName {
- ident: *ident,
- default_init: Some(assign.rhs().clone()),
- });
- }
- AssignTarget::Access(access) => {
- bindings.push(ArrayPatternElement::PropertyAccess {
- access: access.clone(),
- });
- }
- AssignTarget::Pattern(pattern) => match pattern {
- Pattern::Object(pattern) => {
- bindings.push(ArrayPatternElement::Pattern {
- pattern: Pattern::Object(pattern.clone()),
- default_init: Some(assign.rhs().clone()),
- });
- }
- Pattern::Array(pattern) => {
- bindings.push(ArrayPatternElement::Pattern {
- pattern: Pattern::Array(pattern.clone()),
- default_init: Some(assign.rhs().clone()),
- });
- }
- },
- },
- Expression::ArrayLiteral(array) => {
- let pattern = array_decl_to_declaration_pattern(array, strict)?.into();
- bindings.push(ArrayPatternElement::Pattern {
- pattern,
- default_init: None,
- });
- }
- Expression::ObjectLiteral(object) => {
- let pattern = object_decl_to_declaration_pattern(object, strict)?.into();
- bindings.push(ArrayPatternElement::Pattern {
- pattern,
- default_init: None,
- });
- }
- Expression::PropertyAccess(access) => {
- bindings.push(ArrayPatternElement::PropertyAccess {
- access: access.clone(),
- });
- }
- _ => return None,
- }
- }
- Some(ArrayPattern::new(bindings.into()))
-}
-
-impl VisitWith for AssignTarget {
- fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow
- where
- V: Visitor<'a>,
- {
- match self {
- AssignTarget::Identifier(id) => visitor.visit_identifier(id),
- AssignTarget::Access(pa) => visitor.visit_property_access(pa),
- AssignTarget::Pattern(pat) => visitor.visit_pattern(pat),
- }
- }
-
- fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow
- where
- V: VisitorMut<'a>,
- {
- match self {
- AssignTarget::Identifier(id) => visitor.visit_identifier_mut(id),
- AssignTarget::Access(pa) => visitor.visit_property_access_mut(pa),
- AssignTarget::Pattern(pat) => visitor.visit_pattern_mut(pat),
- }
- }
-}
diff --git a/boa_engine/src/syntax/ast/expression/operator/tests.rs b/boa_engine/src/syntax/ast/expression/operator/tests.rs
deleted file mode 100644
index 071fc2eb9a4..00000000000
--- a/boa_engine/src/syntax/ast/expression/operator/tests.rs
+++ /dev/null
@@ -1,140 +0,0 @@
-use crate::exec;
-
-#[test]
-fn assignmentoperator_lhs_not_defined() {
- let scenario = r#"
- try {
- a += 5
- } catch (err) {
- err.toString()
- }
- "#;
-
- assert_eq!(&exec(scenario), "\"ReferenceError: a is not defined\"");
-}
-
-#[test]
-fn assignmentoperator_rhs_throws_error() {
- let scenario = r#"
- try {
- let a;
- a += b
- } catch (err) {
- err.toString()
- }
- "#;
-
- assert_eq!(&exec(scenario), "\"ReferenceError: b is not defined\"");
-}
-
-#[test]
-fn instanceofoperator_rhs_not_object() {
- let scenario = r#"
- try {
- let s = new String();
- s instanceof 1
- } catch (err) {
- err.toString()
- }
- "#;
-
- assert_eq!(
- &exec(scenario),
- "\"TypeError: right-hand side of 'instanceof' should be an object, got number\""
- );
-}
-
-#[test]
-fn instanceofoperator_rhs_not_callable() {
- let scenario = r#"
- try {
- let s = new String();
- s instanceof {}
- } catch (err) {
- err.toString()
- }
- "#;
-
- assert_eq!(
- &exec(scenario),
- "\"TypeError: right-hand side of 'instanceof' is not callable\""
- );
-}
-
-#[test]
-fn logical_nullish_assignment() {
- let scenario = r#"
- let a = undefined;
- a ??= 10;
- a;
- "#;
-
- assert_eq!(&exec(scenario), "10");
-
- let scenario = r#"
- let a = 20;
- a ??= 10;
- a;
- "#;
-
- assert_eq!(&exec(scenario), "20");
-}
-
-#[test]
-fn logical_assignment() {
- let scenario = r#"
- let a = false;
- a &&= 10;
- a;
- "#;
-
- assert_eq!(&exec(scenario), "false");
-
- let scenario = r#"
- let a = 20;
- a &&= 10;
- a;
- "#;
-
- assert_eq!(&exec(scenario), "10");
-
- let scenario = r#"
- let a = null;
- a ||= 10;
- a;
- "#;
-
- assert_eq!(&exec(scenario), "10");
- let scenario = r#"
- let a = 20;
- a ||= 10;
- a;
- "#;
-
- assert_eq!(&exec(scenario), "20");
-}
-
-#[test]
-fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- let a = 20;
- a += 10;
- a -= 10;
- a *= 10;
- a **= 10;
- a /= 10;
- a %= 10;
- a &= 10;
- a |= 10;
- a ^= 10;
- a <<= 10;
- a >>= 10;
- a >>>= 10;
- a &&= 10;
- a ||= 10;
- a ??= 10;
- a;
- "#,
- );
-}
diff --git a/boa_engine/src/syntax/ast/statement/iteration/tests.rs b/boa_engine/src/syntax/ast/statement/iteration/tests.rs
deleted file mode 100644
index 98859f6a9b8..00000000000
--- a/boa_engine/src/syntax/ast/statement/iteration/tests.rs
+++ /dev/null
@@ -1,558 +0,0 @@
-use crate::{check_output, exec, syntax::ast::test_formatting, TestAction};
-
-#[test]
-fn while_loop_late_break() {
- // Ordering with statement before the break.
- let scenario = r#"
- let a = 1;
- while (a < 5) {
- a++;
- if (a == 3) {
- break;
- }
- }
- a;
- "#;
-
- assert_eq!(&exec(scenario), "3");
-}
-
-#[test]
-fn while_loop_early_break() {
- // Ordering with statements after the break.
- let scenario = r#"
- let a = 1;
- while (a < 5) {
- if (a == 3) {
- break;
- }
- a++;
- }
- a;
- "#;
-
- assert_eq!(&exec(scenario), "3");
-}
-
-#[test]
-fn for_loop_break() {
- let scenario = r#"
- let a = 1;
- for (; a < 5; a++) {
- if (a == 3) {
- break;
- }
- }
- a;
- "#;
-
- assert_eq!(&exec(scenario), "3");
-}
-
-#[test]
-fn for_loop_return() {
- let scenario = r#"
- function foo() {
- for (let a = 1; a < 5; a++) {
- if (a == 3) {
- return a;
- }
- }
- }
-
- foo();
- "#;
-
- assert_eq!(&exec(scenario), "3");
-}
-
-#[test]
-fn do_loop_late_break() {
- // Ordering with statement before the break.
- let scenario = r#"
- let a = 1;
- do {
- a++;
- if (a == 3) {
- break;
- }
- } while (a < 5);
- a;
- "#;
-
- assert_eq!(&exec(scenario), "3");
-}
-
-#[test]
-fn do_loop_early_break() {
- // Ordering with statements after the break.
- let scenario = r#"
- let a = 1;
- do {
- if (a == 3) {
- break;
- }
- a++;
- } while (a < 5);
- a;
- "#;
-
- assert_eq!(&exec(scenario), "3");
-}
-
-#[test]
-fn break_out_of_inner_loop() {
- let scenario = r#"
- var a = 0, b = 0;
- for (let i = 0; i < 2; i++) {
- a++;
- for (let j = 0; j < 10; j++) {
- b++;
- if (j == 3)
- break;
- }
- a++;
- }
- [a, b]
- "#;
- assert_eq!(&exec(scenario), "[ 4, 8 ]");
-}
-
-#[test]
-fn continue_inner_loop() {
- let scenario = r#"
- var a = 0, b = 0;
- for (let i = 0; i < 2; i++) {
- a++;
- for (let j = 0; j < 10; j++) {
- if (j < 3)
- continue;
- b++;
- }
- a++;
- }
- [a, b]
- "#;
- assert_eq!(&exec(scenario), "[ 4, 14 ]");
-}
-
-#[test]
-fn for_loop_continue_out_of_switch() {
- let scenario = r#"
- var a = 0, b = 0, c = 0;
- for (let i = 0; i < 3; i++) {
- a++;
- switch (i) {
- case 0:
- continue;
- c++;
- case 1:
- continue;
- case 5:
- c++;
- }
- b++;
- }
- [a, b, c]
- "#;
- assert_eq!(&exec(scenario), "[ 3, 1, 0 ]");
-}
-
-#[test]
-fn while_loop_continue() {
- let scenario = r#"
- var i = 0, a = 0, b = 0;
- while (i < 3) {
- i++;
- if (i < 2) {
- a++;
- continue;
- }
- b++;
- }
- [a, b]
- "#;
- assert_eq!(&exec(scenario), "[ 1, 2 ]");
-}
-
-#[test]
-fn do_while_loop_continue() {
- let scenario = r#"
- var i = 0, a = 0, b = 0;
- do {
- i++;
- if (i < 2) {
- a++;
- continue;
- }
- b++;
- } while (i < 3)
- [a, b]
- "#;
- assert_eq!(&exec(scenario), "[ 1, 2 ]");
-}
-
-#[test]
-fn for_of_loop_declaration() {
- let scenario = r#"
- var result = 0;
- for (i of [1, 2, 3]) {
- result = i;
- }
- "#;
- check_output(&[
- TestAction::Execute(scenario),
- TestAction::TestEq("result", "3"),
- TestAction::TestEq("i", "3"),
- ]);
-}
-
-#[test]
-fn for_of_loop_var() {
- let scenario = r#"
- var result = 0;
- for (var i of [1, 2, 3]) {
- result = i;
- }
- "#;
- check_output(&[
- TestAction::Execute(scenario),
- TestAction::TestEq("result", "3"),
- TestAction::TestEq("i", "3"),
- ]);
-}
-
-#[test]
-fn for_of_loop_let() {
- let scenario = r#"
- var result = 0;
- for (let i of [1, 2, 3]) {
- result = i;
- }
- "#;
- check_output(&[
- TestAction::Execute(scenario),
- TestAction::TestEq("result", "3"),
- TestAction::TestEq(
- r#"
- try {
- i
- } catch(e) {
- e.toString()
- }
- "#,
- "\"ReferenceError: i is not defined\"",
- ),
- ]);
-}
-
-#[test]
-fn for_of_loop_const() {
- let scenario = r#"
- var result = 0;
- for (let i of [1, 2, 3]) {
- result = i;
- }
- "#;
- check_output(&[
- TestAction::Execute(scenario),
- TestAction::TestEq("result", "3"),
- TestAction::TestEq(
- r#"
- try {
- i
- } catch(e) {
- e.toString()
- }
- "#,
- "\"ReferenceError: i is not defined\"",
- ),
- ]);
-}
-
-#[test]
-fn for_of_loop_break() {
- let scenario = r#"
- var result = 0;
- for (var i of [1, 2, 3]) {
- if (i > 1)
- break;
- result = i
- }
- "#;
- check_output(&[
- TestAction::Execute(scenario),
- TestAction::TestEq("result", "1"),
- TestAction::TestEq("i", "2"),
- ]);
-}
-
-#[test]
-fn for_of_loop_continue() {
- let scenario = r#"
- var result = 0;
- for (var i of [1, 2, 3]) {
- if (i == 3)
- continue;
- result = i
- }
- "#;
- check_output(&[
- TestAction::Execute(scenario),
- TestAction::TestEq("result", "2"),
- TestAction::TestEq("i", "3"),
- ]);
-}
-
-#[test]
-fn for_of_loop_return() {
- let scenario = r#"
- function foo() {
- for (i of [1, 2, 3]) {
- if (i > 1)
- return i;
- }
- }
- "#;
- check_output(&[
- TestAction::Execute(scenario),
- TestAction::TestEq("foo()", "2"),
- ]);
-}
-
-#[test]
-fn for_loop_break_label() {
- let scenario = r#"
- var str = "";
-
- outer: for (let i = 0; i < 5; i++) {
- inner: for (let b = 0; b < 5; b++) {
- if (b === 2) {
- break outer;
- }
- str = str + b;
- }
- str = str + i;
- }
- str
- "#;
- assert_eq!(&exec(scenario), "\"01\"");
-}
-
-#[test]
-fn for_loop_continue_label() {
- let scenario = r#"
- var count = 0;
- label: for (let x = 0; x < 10;) {
- while (true) {
- x++;
- count++;
- continue label;
- }
- }
- count
- "#;
- assert_eq!(&exec(scenario), "10");
-}
-
-#[test]
-fn for_in_declaration() {
- let init = r#"
- let result = [];
- let obj = { a: "a", b: 2};
- var i;
- for (i in obj) {
- result = result.concat([i]);
- }
- "#;
- check_output(&[
- TestAction::Execute(init),
- TestAction::TestEq(
- "result.length === 2 && result.includes('a') && result.includes('b')",
- "true",
- ),
- ]);
-}
-
-#[test]
-fn for_in_var_object() {
- let init = r#"
- let result = [];
- let obj = { a: "a", b: 2};
- for (var i in obj) {
- result = result.concat([i]);
- }
- "#;
- check_output(&[
- TestAction::Execute(init),
- TestAction::TestEq(
- "result.length === 2 && result.includes('a') && result.includes('b')",
- "true",
- ),
- ]);
-}
-
-#[test]
-fn for_in_var_array() {
- let init = r#"
- let result = [];
- let arr = ["a", "b"];
- for (var i in arr) {
- result = result.concat([i]);
- }
- "#;
- check_output(&[
- TestAction::Execute(init),
- TestAction::TestEq(
- "result.length === 2 && result.includes('0') && result.includes('1')",
- "true",
- ),
- ]);
-}
-
-#[test]
-fn for_in_let_object() {
- let init = r#"
- let result = [];
- let obj = { a: "a", b: 2};
- for (let i in obj) {
- result = result.concat([i]);
- }
- "#;
- check_output(&[
- TestAction::Execute(init),
- TestAction::TestEq(
- "result.length === 2 && result.includes('a') && result.includes('b')",
- "true",
- ),
- ]);
-}
-
-#[test]
-fn for_in_const_array() {
- let init = r#"
- let result = [];
- let arr = ["a", "b"];
- for (const i in arr) {
- result = result.concat([i]);
- }
- "#;
- check_output(&[
- TestAction::Execute(init),
- TestAction::TestEq(
- "result.length === 2 && result.includes('0') && result.includes('1')",
- "true",
- ),
- ]);
-}
-
-#[test]
-fn for_in_break_label() {
- let scenario = r#"
- var str = "";
-
- outer: for (let i in [1, 2]) {
- inner: for (let b in [2, 3, 4]) {
- if (b === "1") {
- break outer;
- }
- str = str + b;
- }
- str = str + i;
- }
- str
- "#;
- assert_eq!(&exec(scenario), "\"0\"");
-}
-
-#[test]
-fn for_in_continue_label() {
- let scenario = r#"
- var str = "";
-
- outer: for (let i in [1, 2]) {
- inner: for (let b in [2, 3, 4]) {
- if (b === "1") {
- continue outer;
- }
- str = str + b;
- }
- str = str + i;
- }
- str
- "#;
- assert_eq!(&exec(scenario), "\"00\"");
-}
-
-#[test]
-fn fmt() {
- // Labeled and unlabeled for in loops
- test_formatting(
- r#"
- var str = "";
- outer: for (let i in [1, 2]) {
- for (let b in [2, 3, 4]) {
- if (b === "1") {
- continue outer;
- }
- str = str + b;
- }
- str = str + i;
- }
- str;
- "#,
- );
- // Labeled and unlabeled for loops
- test_formatting(
- r#"
- var str = "";
- outer: for (let i = 0; i < 10; ++i) {
- for (let j = 3; j < 6; ++j) {
- if (j === "1") {
- continue outer;
- }
- str = str + j;
- }
- str = str + i;
- }
- str;
- "#,
- );
- // Labeled and unlabeled for of loops
- test_formatting(
- r#"
- for (i of [1, 2, 3]) {
- if (false) {
- break;
- }
- }
- label: for (i of [1, 2, 3]) {
- if (false) {
- break label;
- }
- }
- "#,
- );
- // Labeled and unlabeled do while loops
- test_formatting(
- r#"
- do {
- break;
- } while (true);
- label: do {
- break label;
- } while (true);
- "#,
- );
- // Labeled and unlabeled while loops
- test_formatting(
- r#"
- while (true) {
- break;
- }
- label: while (true) {
- break label;
- }
- "#,
- );
-}
diff --git a/boa_engine/src/syntax/ast/statement/switch/tests.rs b/boa_engine/src/syntax/ast/statement/switch/tests.rs
deleted file mode 100644
index b9182b831d7..00000000000
--- a/boa_engine/src/syntax/ast/statement/switch/tests.rs
+++ /dev/null
@@ -1,243 +0,0 @@
-use crate::exec;
-
-#[test]
-fn single_case_switch() {
- let scenario = r#"
- let a = 10;
- switch (a) {
- case 10:
- a = 20;
- break;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "20");
-}
-
-#[test]
-fn no_cases_switch() {
- let scenario = r#"
- let a = 10;
- switch (a) {
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "10");
-}
-
-#[test]
-fn no_true_case_switch() {
- let scenario = r#"
- let a = 10;
- switch (a) {
- case 5:
- a = 15;
- break;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "10");
-}
-
-#[test]
-fn two_case_switch() {
- let scenario = r#"
- let a = 10;
- switch (a) {
- case 5:
- a = 15;
- break;
- case 10:
- a = 20;
- break;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "20");
-}
-
-#[test]
-fn two_case_no_break_switch() {
- let scenario = r#"
- let a = 10;
- let b = 10;
-
- switch (a) {
- case 10:
- a = 150;
- case 20:
- b = 150;
- break;
- }
-
- a + b;
- "#;
- assert_eq!(&exec(scenario), "300");
-}
-
-#[test]
-fn three_case_partial_fallthrough() {
- let scenario = r#"
- let a = 10;
- let b = 10;
-
- switch (a) {
- case 10:
- a = 150;
- case 20:
- b = 150;
- break;
- case 15:
- b = 1000;
- break;
- }
-
- a + b;
- "#;
- assert_eq!(&exec(scenario), "300");
-}
-
-#[test]
-fn default_taken_switch() {
- let scenario = r#"
- let a = 10;
-
- switch (a) {
- case 5:
- a = 150;
- break;
- default:
- a = 70;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "70");
-}
-
-#[test]
-fn default_not_taken_switch() {
- let scenario = r#"
- let a = 5;
-
- switch (a) {
- case 5:
- a = 150;
- break;
- default:
- a = 70;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "150");
-}
-
-#[test]
-fn string_switch() {
- let scenario = r#"
- let a = "hello";
-
- switch (a) {
- case "hello":
- a = "world";
- break;
- default:
- a = "hi";
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "\"world\"");
-}
-
-#[test]
-fn bigger_switch_example() {
- let expected = [
- "\"Mon\"",
- "\"Tue\"",
- "\"Wed\"",
- "\"Thurs\"",
- "\"Fri\"",
- "\"Sat\"",
- "\"Sun\"",
- ];
-
- for (i, val) in expected.iter().enumerate() {
- let scenario = format!(
- r#"
- let a = {i};
- let b = "unknown";
-
- switch (a) {{
- case 0:
- b = "Mon";
- break;
- case 1:
- b = "Tue";
- break;
- case 2:
- b = "Wed";
- break;
- case 3:
- b = "Thurs";
- break;
- case 4:
- b = "Fri";
- break;
- case 5:
- b = "Sat";
- break;
- case 6:
- b = "Sun";
- break;
- }}
-
- b;
-
- "#,
- );
-
- assert_eq!(&exec(&scenario), val);
- }
-}
-
-#[test]
-fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- let a = 3;
- let b = "unknown";
- switch (a) {
- case 0:
- b = "Mon";
- break;
- case 1:
- b = "Tue";
- break;
- case 2:
- b = "Wed";
- break;
- case 3:
- b = "Thurs";
- break;
- case 4:
- b = "Fri";
- break;
- case 5:
- b = "Sat";
- break;
- case 6:
- b = "Sun";
- break;
- default:
- b = "Unknown";
- }
- b;
- "#,
- );
-}
diff --git a/boa_engine/src/syntax/ast/statement/try/tests.rs b/boa_engine/src/syntax/ast/statement/try/tests.rs
deleted file mode 100644
index 4cd55ebe8f8..00000000000
--- a/boa_engine/src/syntax/ast/statement/try/tests.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-use crate::exec;
-
-#[test]
-fn simple_try() {
- let scenario = r#"
- let a = 10;
- try {
- a = 20;
- } catch {
- a = 30;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "20");
-}
-
-#[test]
-fn finally() {
- let scenario = r#"
- let a = 10;
- try {
- a = 20;
- } finally {
- a = 30;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "30");
-}
-
-#[test]
-fn catch_finally() {
- let scenario = r#"
- let a = 10;
- try {
- a = 20;
- } catch {
- a = 40;
- } finally {
- a = 30;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "30");
-}
-
-#[test]
-fn catch() {
- let scenario = r#"
- let a = 10;
- try {
- throw "error";
- } catch {
- a = 20;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "20");
-}
-
-#[test]
-fn catch_binding() {
- let scenario = r#"
- let a = 10;
- try {
- throw 20;
- } catch(err) {
- a = err;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "20");
-}
-
-#[test]
-fn catch_binding_pattern_object() {
- let scenario = r#"
- let a = 10;
- try {
- throw {
- n: 30,
- };
- } catch ({ n }) {
- a = n;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "30");
-}
-
-#[test]
-fn catch_binding_pattern_array() {
- let scenario = r#"
- let a = 10;
- try {
- throw [20, 30];
- } catch ([, n]) {
- a = n;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "30");
-}
-
-#[test]
-fn catch_binding_finally() {
- let scenario = r#"
- let a = 10;
- try {
- throw 20;
- } catch(err) {
- a = err;
- } finally {
- a = 30;
- }
-
- a;
- "#;
- assert_eq!(&exec(scenario), "30");
-}
-
-#[test]
-fn fmt() {
- crate::syntax::ast::test_formatting(
- r#"
- try {
- throw "hello";
- } catch(e) {
- console.log(e);
- } finally {
- console.log("things");
- }
- try {
- throw "hello";
- } catch {
- console.log("something went wrong");
- }
- "#,
- );
-}
diff --git a/boa_engine/src/syntax/ast/statement_list/tests.rs b/boa_engine/src/syntax/ast/statement_list/tests.rs
deleted file mode 100644
index 71df252646b..00000000000
--- a/boa_engine/src/syntax/ast/statement_list/tests.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-use crate::exec;
-
-#[test]
-fn strict_mode_global() {
- let scenario = r#"
- 'use strict';
- let throws = false;
- try {
- delete Boolean.prototype;
- } catch (e) {
- throws = true;
- }
- throws
- "#;
-
- assert_eq!(&exec(scenario), "true");
-}
-
-#[test]
-fn strict_mode_function() {
- let scenario = r#"
- let throws = false;
- function t() {
- 'use strict';
- try {
- delete Boolean.prototype;
- } catch (e) {
- throws = true;
- }
- }
- t()
- throws
- "#;
-
- assert_eq!(&exec(scenario), "true");
-}
-
-#[test]
-fn strict_mode_function_after() {
- let scenario = r#"
- function t() {
- 'use strict';
- }
- t()
- let throws = false;
- try {
- delete Boolean.prototype;
- } catch (e) {
- throws = true;
- }
- throws
- "#;
-
- assert_eq!(&exec(scenario), "false");
-}
-
-#[test]
-fn strict_mode_global_active_in_function() {
- let scenario = r#"
- 'use strict'
- let throws = false;
- function a(){
- try {
- delete Boolean.prototype;
- } catch (e) {
- throws = true;
- }
- }
- a();
- throws
- "#;
-
- assert_eq!(&exec(scenario), "true");
-}
-
-#[test]
-fn strict_mode_function_in_function() {
- let scenario = r#"
- let throws = false;
- function a(){
- try {
- delete Boolean.prototype;
- } catch (e) {
- throws = true;
- }
- }
- function b(){
- 'use strict';
- a();
- }
- b();
- throws
- "#;
-
- assert_eq!(&exec(scenario), "false");
-}
-
-#[test]
-fn strict_mode_function_return() {
- let scenario = r#"
- let throws = false;
- function a() {
- 'use strict';
-
- return function () {
- try {
- delete Boolean.prototype;
- } catch (e) {
- throws = true;
- }
- }
- }
- a()();
- throws
- "#;
-
- assert_eq!(&exec(scenario), "true");
-}
diff --git a/boa_engine/src/syntax/lexer/comment.rs b/boa_engine/src/syntax/lexer/comment.rs
index a34bce9fbca..bc0a1d2f673 100644
--- a/boa_engine/src/syntax/lexer/comment.rs
+++ b/boa_engine/src/syntax/lexer/comment.rs
@@ -1,10 +1,8 @@
//! This module implements lexing for comments used in the JavaScript programing language.
use super::{Cursor, Error, Tokenizer};
-use crate::syntax::{
- ast::{Position, Span},
- lexer::{Token, TokenKind},
-};
+use crate::syntax::lexer::{Token, TokenKind};
+use boa_ast::{Position, Span};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/lexer/cursor.rs b/boa_engine/src/syntax/lexer/cursor.rs
index e59d0e8e15a..79d4fe24549 100644
--- a/boa_engine/src/syntax/lexer/cursor.rs
+++ b/boa_engine/src/syntax/lexer/cursor.rs
@@ -1,5 +1,5 @@
//! Module implementing the lexer cursor. This is used for managing the input byte stream.
-use crate::syntax::ast::Position;
+use boa_ast::Position;
use boa_profiler::Profiler;
use std::io::{self, Bytes, Error, ErrorKind, Read};
diff --git a/boa_engine/src/syntax/lexer/error.rs b/boa_engine/src/syntax/lexer/error.rs
index d6082cde45b..d24044aa9a2 100644
--- a/boa_engine/src/syntax/lexer/error.rs
+++ b/boa_engine/src/syntax/lexer/error.rs
@@ -5,7 +5,7 @@
//!
//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard
-use super::Position;
+use boa_ast::Position;
use std::{error::Error as StdError, fmt, io};
#[derive(Debug)]
diff --git a/boa_engine/src/syntax/lexer/identifier.rs b/boa_engine/src/syntax/lexer/identifier.rs
index e44d697e3e8..2520281039d 100644
--- a/boa_engine/src/syntax/lexer/identifier.rs
+++ b/boa_engine/src/syntax/lexer/identifier.rs
@@ -1,10 +1,8 @@
//! This module implements lexing for identifiers (foo, myvar, etc.) used in the JavaScript programing language.
use super::{Cursor, Error, Tokenizer};
-use crate::syntax::{
- ast::{Keyword, Position, Span},
- lexer::{StringLiteral, Token, TokenKind},
-};
+use crate::syntax::lexer::{StringLiteral, Token, TokenKind};
+use boa_ast::{Keyword, Position, Span};
use boa_interner::Interner;
use boa_profiler::Profiler;
use boa_unicode::UnicodeProperties;
diff --git a/boa_engine/src/syntax/lexer/mod.rs b/boa_engine/src/syntax/lexer/mod.rs
index f122239b429..de064198064 100644
--- a/boa_engine/src/syntax/lexer/mod.rs
+++ b/boa_engine/src/syntax/lexer/mod.rs
@@ -42,7 +42,7 @@ use self::{
string::StringLiteral,
template::TemplateLiteral,
};
-use crate::syntax::ast::{Position, Punctuator, Span};
+use boa_ast::{Position, Punctuator, Span};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/lexer/number.rs b/boa_engine/src/syntax/lexer/number.rs
index 0ad3b4e28cd..978bdba3646 100644
--- a/boa_engine/src/syntax/lexer/number.rs
+++ b/boa_engine/src/syntax/lexer/number.rs
@@ -1,12 +1,10 @@
//! This module implements lexing for number literals (123, 787) used in the JavaScript programing language.
use crate::{
- syntax::{
- ast::{Position, Span},
- lexer::{token::Numeric, Cursor, Error, Token, TokenKind, Tokenizer},
- },
+ syntax::lexer::{token::Numeric, Cursor, Error, Token, TokenKind, Tokenizer},
JsBigInt,
};
+use boa_ast::{Position, Span};
use boa_interner::Interner;
use boa_profiler::Profiler;
use num_bigint::BigInt;
diff --git a/boa_engine/src/syntax/lexer/operator.rs b/boa_engine/src/syntax/lexer/operator.rs
index f53d85bfb7f..122dfd0dbdd 100644
--- a/boa_engine/src/syntax/lexer/operator.rs
+++ b/boa_engine/src/syntax/lexer/operator.rs
@@ -1,9 +1,7 @@
//! This module implements lexing for operators (+, - etc.) used in the JavaScript programing language.
-use crate::syntax::{
- ast::{Position, Punctuator, Span},
- lexer::{Cursor, Error, Token, TokenKind, Tokenizer},
-};
+use crate::syntax::lexer::{Cursor, Error, Token, TokenKind, Tokenizer};
+use boa_ast::{Position, Punctuator, Span};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/lexer/private_identifier.rs b/boa_engine/src/syntax/lexer/private_identifier.rs
index 867461c8baf..0f3185658e1 100644
--- a/boa_engine/src/syntax/lexer/private_identifier.rs
+++ b/boa_engine/src/syntax/lexer/private_identifier.rs
@@ -1,10 +1,8 @@
//! This module implements lexing for private identifiers (#foo, #myvar, etc.) used in the JavaScript programing language.
use super::{identifier::Identifier, Cursor, Error, Tokenizer};
-use crate::syntax::{
- ast::{Position, Span},
- lexer::{Token, TokenKind},
-};
+use crate::syntax::lexer::{Token, TokenKind};
+use boa_ast::{Position, Span};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/lexer/regex.rs b/boa_engine/src/syntax/lexer/regex.rs
index 0e2e9e00189..f72faafc3fa 100644
--- a/boa_engine/src/syntax/lexer/regex.rs
+++ b/boa_engine/src/syntax/lexer/regex.rs
@@ -1,11 +1,9 @@
//! This module implements lexing for regex literals used in the JavaScript programing language.
use super::{Cursor, Error, Span, Tokenizer};
-use crate::syntax::{
- ast::Position,
- lexer::{Token, TokenKind},
-};
+use crate::syntax::lexer::{Token, TokenKind};
use bitflags::bitflags;
+use boa_ast::Position;
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::{
diff --git a/boa_engine/src/syntax/lexer/spread.rs b/boa_engine/src/syntax/lexer/spread.rs
index fac7c2d963b..fe626ab40a6 100644
--- a/boa_engine/src/syntax/lexer/spread.rs
+++ b/boa_engine/src/syntax/lexer/spread.rs
@@ -1,10 +1,8 @@
//! This module implements lexing for spread (...) literals used in the JavaScript programing language.
use super::{Cursor, Error, Tokenizer};
-use crate::syntax::{
- ast::{Position, Punctuator, Span},
- lexer::Token,
-};
+use crate::syntax::lexer::Token;
+use boa_ast::{Position, Punctuator, Span};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/lexer/string.rs b/boa_engine/src/syntax/lexer/string.rs
index ea5e58eec5b..32a6ba8e517 100644
--- a/boa_engine/src/syntax/lexer/string.rs
+++ b/boa_engine/src/syntax/lexer/string.rs
@@ -1,10 +1,8 @@
//! This module implements lexing for string literals used in the JavaScript programing language.
use super::{Cursor, Error, Tokenizer};
-use crate::syntax::{
- ast::{Position, Span},
- lexer::{Token, TokenKind},
-};
+use crate::syntax::lexer::{Token, TokenKind};
+use boa_ast::{Position, Span};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::{
diff --git a/boa_engine/src/syntax/lexer/template.rs b/boa_engine/src/syntax/lexer/template.rs
index 68d9df180f3..32e7ec64698 100644
--- a/boa_engine/src/syntax/lexer/template.rs
+++ b/boa_engine/src/syntax/lexer/template.rs
@@ -1,21 +1,16 @@
//! This module implements lexing for template literals used in the JavaScript programing language.
use super::{Cursor, Error, Tokenizer};
-use crate::{
- syntax::lexer::string::{StringLiteral, UTF16CodeUnitsBuffer},
- syntax::{
- ast::{Position, Span},
- lexer::{Token, TokenKind},
- },
+use crate::syntax::lexer::{
+ string::{StringLiteral, UTF16CodeUnitsBuffer},
+ Token, TokenKind,
};
+use boa_ast::{Position, Span};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::{self, ErrorKind, Read};
-#[cfg(feature = "deser")]
-use serde::{Deserialize, Serialize};
-
-#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TemplateString {
/// The template string of template literal with argument `raw` true.
diff --git a/boa_engine/src/syntax/lexer/tests.rs b/boa_engine/src/syntax/lexer/tests.rs
index 12bc197e46d..ac0d3d51d13 100644
--- a/boa_engine/src/syntax/lexer/tests.rs
+++ b/boa_engine/src/syntax/lexer/tests.rs
@@ -1,16 +1,12 @@
//! Tests for the lexer.
#![allow(clippy::indexing_slicing)]
+use boa_ast::Keyword;
use boa_interner::Sym;
// use super::regex::RegExpFlags;
-use super::token::Numeric;
-use super::*;
-use super::{Error, Position};
-use crate::{
- string::utf16,
- syntax::{ast::Keyword, lexer::template::TemplateString},
-};
+use super::{token::Numeric, Error, Position, *};
+use crate::{string::utf16, syntax::lexer::template::TemplateString};
use std::str;
fn span(start: (u32, u32), end: (u32, u32)) -> Span {
diff --git a/boa_engine/src/syntax/lexer/token.rs b/boa_engine/src/syntax/lexer/token.rs
index ae836db3f57..6094fb38be7 100644
--- a/boa_engine/src/syntax/lexer/token.rs
+++ b/boa_engine/src/syntax/lexer/token.rs
@@ -5,14 +5,10 @@
//!
//! [spec]: https://tc39.es/ecma262/#sec-tokens
-use crate::syntax::{
- ast::{Keyword, Punctuator, Span},
- lexer::template::TemplateString,
-};
+use crate::syntax::lexer::template::TemplateString;
+use boa_ast::{Keyword, Punctuator, Span};
use boa_interner::{Interner, Sym};
use num_bigint::BigInt;
-#[cfg(feature = "deser")]
-use serde::{Deserialize, Serialize};
/// This represents the smallest individual words, phrases, or characters that JavaScript can understand.
///
@@ -20,7 +16,7 @@ use serde::{Deserialize, Serialize};
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-tokens
-#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
/// The token kind, which contains the actual data of the token.
@@ -55,7 +51,7 @@ impl Token {
}
/// Represents the type different types of numeric literals.
-#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, PartialEq, Debug)]
pub enum Numeric {
/// A floating point number
@@ -90,7 +86,7 @@ impl From for Numeric {
}
/// Represents the type of Token and the data it has inside.
-#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, PartialEq, Debug)]
pub enum TokenKind {
/// A boolean literal, which is either `true` or `false`.
diff --git a/boa_engine/src/syntax/mod.rs b/boa_engine/src/syntax/mod.rs
index fca8f72b283..7ced1e404cf 100644
--- a/boa_engine/src/syntax/mod.rs
+++ b/boa_engine/src/syntax/mod.rs
@@ -1,7 +1,6 @@
-//! Syntactical analysis, such as Abstract Syntax Tree (AST), Parsing and Lexing
+//! Syntactical analysis, such as Parsing and Lexing.
// syntax module has a lot of acronyms
-pub mod ast;
pub mod lexer;
pub mod parser;
diff --git a/boa_engine/src/syntax/parser/cursor/buffered_lexer/mod.rs b/boa_engine/src/syntax/parser/cursor/buffered_lexer/mod.rs
index 88a7fd89250..df34a0b960a 100644
--- a/boa_engine/src/syntax/parser/cursor/buffered_lexer/mod.rs
+++ b/boa_engine/src/syntax/parser/cursor/buffered_lexer/mod.rs
@@ -1,8 +1,8 @@
use crate::syntax::{
- ast::Position,
lexer::{InputElement, Lexer, Token, TokenKind},
parser::error::ParseError,
};
+use boa_ast::Position;
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/cursor/mod.rs b/boa_engine/src/syntax/parser/cursor/mod.rs
index 85bfb67e836..1eada2e5e60 100644
--- a/boa_engine/src/syntax/parser/cursor/mod.rs
+++ b/boa_engine/src/syntax/parser/cursor/mod.rs
@@ -2,10 +2,8 @@
mod buffered_lexer;
use super::{statement::PrivateElement, ParseError};
-use crate::syntax::{
- ast::{Position, Punctuator},
- lexer::{InputElement, Lexer, Token, TokenKind},
-};
+use crate::syntax::lexer::{InputElement, Lexer, Token, TokenKind};
+use boa_ast::{Position, Punctuator};
use boa_interner::{Interner, Sym};
use buffered_lexer::BufferedLexer;
use rustc_hash::FxHashMap;
diff --git a/boa_engine/src/syntax/parser/error.rs b/boa_engine/src/syntax/parser/error.rs
index 4f375dcdd12..f0ccb28bdc0 100644
--- a/boa_engine/src/syntax/parser/error.rs
+++ b/boa_engine/src/syntax/parser/error.rs
@@ -1,9 +1,8 @@
//! Error and result implementation for the parser.
-use crate::syntax::{
- ast::{Position, Span},
- lexer::Error as LexError,
-};
+use crate::syntax::lexer::Error as LexError;
+
+use boa_ast::{Position, Span};
use std::fmt;
/// Result of a parsing operation.
diff --git a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs b/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs
index 145b8628bd7..7ec4bd99c6c 100644
--- a/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs
+++ b/boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs
@@ -9,23 +9,23 @@
use super::AssignmentExpression;
use crate::syntax::{
- ast::{
- self,
- declaration::Variable,
- expression::Identifier,
- function::{FormalParameter, FormalParameterList, FormalParameterListFlags},
- statement::Return,
- Expression, Punctuator, StatementList,
- },
lexer::{Error as LexError, TokenKind},
parser::{
error::{ErrorContext, ParseError, ParseResult},
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody},
- AllowAwait, AllowIn, AllowYield, Cursor, TokenParser,
+ name_in_lexically_declared_names, AllowAwait, AllowIn, AllowYield, Cursor, TokenParser,
},
};
-use boa_interner::{Interner, Sym};
+use boa_ast::{
+ self as ast,
+ declaration::Variable,
+ expression::Identifier,
+ function::{FormalParameter, FormalParameterList},
+ statement::Return,
+ Expression, Punctuator, StatementList,
+};
+use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
@@ -78,40 +78,33 @@ where
let _timer = Profiler::global().start_event("ArrowFunction", "Parsing");
let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
- let (params, params_start_position) =
- if let TokenKind::Punctuator(Punctuator::OpenParen) = &next_token.kind() {
- // CoverParenthesizedExpressionAndArrowParameterList
- let params_start_position = cursor
- .expect(Punctuator::OpenParen, "arrow function", interner)?
- .span()
- .end();
-
- let params = FormalParameters::new(self.allow_yield, self.allow_await)
- .parse(cursor, interner)?;
- cursor.expect(Punctuator::CloseParen, "arrow function", interner)?;
- (params, params_start_position)
- } else {
- let params_start_position = next_token.span().start();
- let param = BindingIdentifier::new(self.allow_yield, self.allow_await)
- .parse(cursor, interner)
- .context("arrow function")?;
- let has_arguments = param == Sym::ARGUMENTS;
- let mut flags = FormalParameterListFlags::IS_SIMPLE;
- if has_arguments {
- flags |= FormalParameterListFlags::HAS_ARGUMENTS;
- }
- (
- FormalParameterList::new(
- Box::new([FormalParameter::new(
- Variable::from_identifier(param, None),
- false,
- )]),
- flags,
- 1,
- ),
- params_start_position,
- )
- };
+ let (params, params_start_position) = if let TokenKind::Punctuator(Punctuator::OpenParen) =
+ &next_token.kind()
+ {
+ // CoverParenthesizedExpressionAndArrowParameterList
+ let params_start_position = cursor
+ .expect(Punctuator::OpenParen, "arrow function", interner)?
+ .span()
+ .end();
+
+ let params = FormalParameters::new(self.allow_yield, self.allow_await)
+ .parse(cursor, interner)?;
+ cursor.expect(Punctuator::CloseParen, "arrow function", interner)?;
+ (params, params_start_position)
+ } else {
+ let params_start_position = next_token.span().start();
+ let param = BindingIdentifier::new(self.allow_yield, self.allow_await)
+ .parse(cursor, interner)
+ .context("arrow function")?;
+ (
+ FormalParameterList::try_from(FormalParameter::new(
+ Variable::from_identifier(param, None),
+ false,
+ ))
+ .expect("a single binding identifier without init is always a valid param list"),
+ params_start_position,
+ )
+ };
cursor.peek_expect_no_lineterminator(0, "arrow function", interner)?;
@@ -144,7 +137,8 @@ where
// It is a Syntax Error if any element of the BoundNames of ArrowParameters
// also occurs in the LexicallyDeclaredNames of ConciseBody.
// https://tc39.es/ecma262/#sec-arrow-function-definitions-static-semantics-early-errors
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
params_start_position,
)?;
diff --git a/boa_engine/src/syntax/parser/expression/assignment/conditional.rs b/boa_engine/src/syntax/parser/expression/assignment/conditional.rs
index 064b79b2833..9370ad4d352 100644
--- a/boa_engine/src/syntax/parser/expression/assignment/conditional.rs
+++ b/boa_engine/src/syntax/parser/expression/assignment/conditional.rs
@@ -8,16 +8,16 @@
//! [spec]: https://tc39.es/ecma262/#sec-conditional-operator
use crate::syntax::{
- ast::{
- expression::{operator::Conditional, Identifier},
- Expression, Punctuator,
- },
lexer::TokenKind,
parser::{
expression::{AssignmentExpression, ShortCircuitExpression},
AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ expression::{operator::Conditional, Identifier},
+ Expression, Punctuator,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs b/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs
index 62384234285..50a2ab5664d 100644
--- a/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs
+++ b/boa_engine/src/syntax/parser/expression/assignment/exponentiation.rs
@@ -9,19 +9,19 @@
use super::ParseError;
use crate::syntax::{
- ast::{
- expression::{
- operator::{binary::ArithmeticOp, Binary},
- Identifier,
- },
- Expression, Keyword, Punctuator,
- },
lexer::TokenKind,
parser::{
expression::{unary::UnaryExpression, update::UpdateExpression},
AllowAwait, AllowYield, Cursor, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ expression::{
+ operator::{binary::ArithmeticOp, Binary},
+ Identifier,
+ },
+ Expression, Keyword, Punctuator,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/assignment/mod.rs b/boa_engine/src/syntax/parser/expression/assignment/mod.rs
index c9f968cd6fb..f7c8e02c05a 100644
--- a/boa_engine/src/syntax/parser/expression/assignment/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/assignment/mod.rs
@@ -13,14 +13,6 @@ mod exponentiation;
mod r#yield;
use crate::syntax::{
- ast::{
- self,
- expression::{
- operator::assign::{Assign, AssignOp, AssignTarget},
- Identifier,
- },
- Expression, Keyword, Punctuator,
- },
lexer::{Error as LexError, InputElement, TokenKind},
parser::{
expression::assignment::{
@@ -28,15 +20,26 @@ use crate::syntax::{
conditional::ConditionalExpression,
r#yield::YieldExpression,
},
- AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
+ name_in_lexically_declared_names, AllowAwait, AllowIn, AllowYield, Cursor, ParseError,
+ ParseResult, TokenParser,
},
};
+use boa_ast::{
+ self as ast,
+ expression::{
+ operator::assign::{Assign, AssignOp, AssignTarget},
+ Identifier,
+ },
+ Expression, Keyword, Punctuator,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
pub(super) use exponentiation::ExponentiationExpression;
+use super::check_strict_arguments_or_eval;
+
/// Assignment expression parsing.
///
/// This can be one of the following:
@@ -188,7 +191,8 @@ where
// It is a Syntax Error if any element of the BoundNames of ArrowParameters
// also occurs in the LexicallyDeclaredNames of ConciseBody.
// https://tc39.es/ecma262/#sec-arrow-function-definitions-static-semantics-early-errors
- parameters.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶meters,
&body.lexically_declared_names_top_level(),
position,
)?;
@@ -202,7 +206,7 @@ where
TokenKind::Punctuator(Punctuator::Assign) => {
if cursor.strict_mode() {
if let Expression::Identifier(ident) = lhs {
- ident.check_strict_arguments_or_eval(position)?;
+ check_strict_arguments_or_eval(ident, position)?;
}
}
@@ -227,7 +231,7 @@ where
TokenKind::Punctuator(p) if p.as_assign_op().is_some() => {
if cursor.strict_mode() {
if let Expression::Identifier(ident) = lhs {
- ident.check_strict_arguments_or_eval(position)?;
+ check_strict_arguments_or_eval(ident, position)?;
}
}
diff --git a/boa_engine/src/syntax/parser/expression/assignment/yield.rs b/boa_engine/src/syntax/parser/expression/assignment/yield.rs
index 0cb033756c6..189b314c4a7 100644
--- a/boa_engine/src/syntax/parser/expression/assignment/yield.rs
+++ b/boa_engine/src/syntax/parser/expression/assignment/yield.rs
@@ -9,10 +9,10 @@
use super::AssignmentExpression;
use crate::syntax::{
- ast::{expression::Yield, Expression, Keyword, Punctuator},
lexer::TokenKind,
parser::{AllowAwait, AllowIn, Cursor, ParseError, ParseResult, TokenParser},
};
+use boa_ast::{expression::Yield, Expression, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/await_expr.rs b/boa_engine/src/syntax/parser/expression/await_expr.rs
index 2042685b31c..01a83775b74 100644
--- a/boa_engine/src/syntax/parser/expression/await_expr.rs
+++ b/boa_engine/src/syntax/parser/expression/await_expr.rs
@@ -9,10 +9,10 @@
use super::unary::UnaryExpression;
use crate::syntax::{
- ast::{expression::Await, Keyword},
lexer::TokenKind,
parser::{AllowYield, Cursor, ParseResult, TokenParser},
};
+use boa_ast::{expression::Await, Keyword};
use boa_interner::Interner;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/identifiers.rs b/boa_engine/src/syntax/parser/expression/identifiers.rs
index 2e284dbd3b2..c108575af1a 100644
--- a/boa_engine/src/syntax/parser/expression/identifiers.rs
+++ b/boa_engine/src/syntax/parser/expression/identifiers.rs
@@ -6,10 +6,10 @@
//! [spec]: https://tc39.es/ecma262/#sec-identifiers
use crate::syntax::{
- ast::{expression::Identifier, Keyword},
lexer::{Error as LexError, TokenKind},
parser::{cursor::Cursor, AllowAwait, AllowYield, ParseError, ParseResult, TokenParser},
};
+use boa_ast::{expression::Identifier, Keyword};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/arguments.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/arguments.rs
index b6547c92c5c..c98a3a4a7ea 100644
--- a/boa_engine/src/syntax/parser/expression/left_hand_side/arguments.rs
+++ b/boa_engine/src/syntax/parser/expression/left_hand_side/arguments.rs
@@ -8,13 +8,13 @@
//! [spec]: https://tc39.es/ecma262/#prod-Arguments
use crate::syntax::{
- ast::{expression::Spread, Expression, Punctuator},
lexer::{InputElement, TokenKind},
parser::{
expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult,
TokenParser,
},
};
+use boa_ast::{expression::Spread, Expression, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs
index 45287378078..c082a3650e4 100644
--- a/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs
+++ b/boa_engine/src/syntax/parser/expression/left_hand_side/call.rs
@@ -9,20 +9,20 @@
use super::arguments::Arguments;
use crate::syntax::{
- ast::{
- self,
- expression::{
- access::{PrivatePropertyAccess, SimplePropertyAccess},
- Call,
- },
- Punctuator,
- },
lexer::TokenKind,
parser::{
expression::{left_hand_side::template::TaggedTemplateLiteral, Expression},
AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ self as ast,
+ expression::{
+ access::{PrivatePropertyAccess, SimplePropertyAccess},
+ Call,
+ },
+ Punctuator,
+};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs
index 89ad3c7f072..19329614016 100644
--- a/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs
+++ b/boa_engine/src/syntax/parser/expression/left_hand_side/member.rs
@@ -7,17 +7,6 @@
use super::arguments::Arguments;
use crate::syntax::{
- ast::{
- self,
- expression::{
- access::{
- PrivatePropertyAccess, PropertyAccessField, SimplePropertyAccess,
- SuperPropertyAccess,
- },
- Call, Identifier, New,
- },
- Keyword, Punctuator,
- },
lexer::{InputElement, TokenKind},
parser::{
expression::{
@@ -26,6 +15,16 @@ use crate::syntax::{
AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ self as ast,
+ expression::{
+ access::{
+ PrivatePropertyAccess, PropertyAccessField, SimplePropertyAccess, SuperPropertyAccess,
+ },
+ Call, Identifier, New,
+ },
+ Keyword, Punctuator,
+};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/mod.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/mod.rs
index 5d6389b2eac..8045fc082f3 100644
--- a/boa_engine/src/syntax/parser/expression/left_hand_side/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/left_hand_side/mod.rs
@@ -17,10 +17,6 @@ mod optional;
mod template;
use crate::syntax::{
- ast::{
- expression::{Identifier, SuperCall},
- Expression, Keyword, Punctuator,
- },
lexer::{InputElement, TokenKind},
parser::{
expression::left_hand_side::{
@@ -30,6 +26,10 @@ use crate::syntax::{
AllowAwait, AllowYield, Cursor, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ expression::{Identifier, SuperCall},
+ Expression, Keyword, Punctuator,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/optional/mod.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/optional/mod.rs
index ce78197969f..b985c92a95d 100644
--- a/boa_engine/src/syntax/parser/expression/left_hand_side/optional/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/left_hand_side/optional/mod.rs
@@ -7,13 +7,6 @@ use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use crate::syntax::{
- ast::{
- self,
- expression::{
- access::PropertyAccessField, Optional, OptionalOperation, OptionalOperationKind,
- },
- Punctuator,
- },
lexer::{Token, TokenKind},
parser::{
cursor::Cursor, expression::Expression, AllowAwait, AllowYield, ParseError, ParseResult,
@@ -22,6 +15,11 @@ use crate::syntax::{
};
use super::arguments::Arguments;
+use boa_ast::{
+ self as ast,
+ expression::{access::PropertyAccessField, Optional, OptionalOperation, OptionalOperationKind},
+ Punctuator,
+};
/// Parses an optional expression.
///
diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/optional/tests.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/optional/tests.rs
index b7a59888080..bd044368f91 100644
--- a/boa_engine/src/syntax/parser/expression/left_hand_side/optional/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/left_hand_side/optional/tests.rs
@@ -1,15 +1,13 @@
use boa_interner::Interner;
use boa_macros::utf16;
-use crate::syntax::{
- ast::{
- expression::{
- access::PropertyAccessField, literal::Literal, Identifier, Optional, OptionalOperation,
- OptionalOperationKind,
- },
- Expression, Statement,
+use crate::syntax::parser::tests::{check_invalid, check_parser};
+use boa_ast::{
+ expression::{
+ access::PropertyAccessField, literal::Literal, Identifier, Optional, OptionalOperation,
+ OptionalOperationKind,
},
- parser::tests::{check_invalid, check_parser},
+ Expression, Statement,
};
#[test]
diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/template.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/template.rs
index d8b35183536..16c15e4e496 100644
--- a/boa_engine/src/syntax/parser/expression/left_hand_side/template.rs
+++ b/boa_engine/src/syntax/parser/expression/left_hand_side/template.rs
@@ -1,11 +1,11 @@
use crate::syntax::{
- ast::{self, expression::TaggedTemplate, Position, Punctuator},
lexer::TokenKind,
parser::{
cursor::Cursor, expression::Expression, AllowAwait, AllowYield, ParseError, ParseResult,
TokenParser,
},
};
+use boa_ast::{self as ast, expression::TaggedTemplate, Position, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs b/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs
index 50df92d5be6..17ba27aecc4 100644
--- a/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/left_hand_side/tests.rs
@@ -1,9 +1,7 @@
-use crate::syntax::{
- ast::{
- expression::{access::SimplePropertyAccess, Call, Identifier},
- Expression, Statement,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ expression::{access::SimplePropertyAccess, Call, Identifier},
+ Expression, Statement,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/expression/mod.rs b/boa_engine/src/syntax/parser/expression/mod.rs
index fbd3475d88a..b4b601ef6d5 100644
--- a/boa_engine/src/syntax/parser/expression/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/mod.rs
@@ -20,29 +20,29 @@ pub(in crate::syntax::parser) mod await_expr;
mod tests;
use crate::syntax::{
- ast::{
- self,
- expression::{
- operator::{
- binary::{BinaryOp, LogicalOp},
- Binary,
- },
- Identifier,
- },
- Keyword, Punctuator,
- },
lexer::{InputElement, TokenKind},
parser::{
expression::assignment::ExponentiationExpression, AllowAwait, AllowIn, AllowYield, Cursor,
ParseError, ParseResult, TokenParser,
},
};
-use boa_interner::Interner;
+
+use boa_ast::{
+ self as ast,
+ expression::{
+ operator::{
+ binary::{BinaryOp, LogicalOp},
+ Binary,
+ },
+ Identifier,
+ },
+ Keyword, Position, Punctuator,
+};
+use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
pub(super) use self::{assignment::AssignmentExpression, primary::Initializer};
-pub(in crate::syntax) use identifiers::RESERVED_IDENTIFIERS_STRICT;
pub(in crate::syntax::parser) use {
identifiers::{BindingIdentifier, LabelIdentifier},
left_hand_side::LeftHandSideExpression,
@@ -51,22 +51,6 @@ pub(in crate::syntax::parser) use {
},
};
-// For use in the expression! macro to allow for both Punctuator and Keyword parameters.
-// Always returns false.
-impl PartialEq for Punctuator {
- fn eq(&self, _other: &Keyword) -> bool {
- false
- }
-}
-
-// For use in the expression! macro to allow for both Punctuator and Keyword parameters.
-// Always returns false.
-impl PartialEq for Keyword {
- fn eq(&self, _other: &Punctuator) -> bool {
- false
- }
-}
-
/// Generates an expression parser for a number of expressions whose production rules are of the following pattern.
///
/// ```text
@@ -84,48 +68,42 @@ impl PartialEq for Keyword {
/// A list of punctuators (operands between the and ) are passed as the third parameter.
///
/// The fifth parameter is an Option which sets the goal symbol to set before parsing (or None to leave it as is).
-macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $low_param:ident ),*], $goal:expr ) => {
- impl TokenParser for $name
- where
- R: Read
- {
- type Output = ast::Expression;
-
- fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner)-> ParseResult {
- let _timer = Profiler::global().start_event(stringify!($name), "Parsing");
-
- if $goal.is_some() {
- cursor.set_goal($goal.unwrap());
- }
+macro_rules! expression {
+ ($name:ident, $lower:ident, [$( $op:path ),*], [$( $low_param:ident ),*], $goal:expr ) => {
+ impl TokenParser for $name
+ where
+ R: Read
+ {
+ type Output = ast::Expression;
+
+ fn parse(mut self, cursor: &mut Cursor, interner: &mut Interner)-> ParseResult {
+ let _timer = Profiler::global().start_event(stringify!($name), "Parsing");
+
+ if $goal.is_some() {
+ cursor.set_goal($goal.unwrap());
+ }
- let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor, interner)?;
- self.name = None;
- while let Some(tok) = cursor.peek(0, interner)? {
- match *tok.kind() {
- TokenKind::Punctuator(op) if $( op == $op )||* => {
- let _next = cursor.next(interner).expect("token disappeared");
- lhs = Binary::new(
- op.as_binary_op().expect("Could not get binary operation."),
- lhs,
- $lower::new($( self.$low_param ),*).parse(cursor, interner)?
- ).into();
- }
- TokenKind::Keyword((op, false)) if $( op == $op )||* => {
- let _next = cursor.next(interner).expect("token disappeared");
- lhs = Binary::new(
- op.as_binary_op().expect("Could not get binary operation."),
- lhs,
- $lower::new($( self.$low_param ),*).parse(cursor, interner)?
- ).into();
+ let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor, interner)?;
+ self.name = None;
+ while let Some(tok) = cursor.peek(0, interner)? {
+ match *tok.kind() {
+ TokenKind::Punctuator(op) if $( op == $op )||* => {
+ let _next = cursor.next(interner).expect("token disappeared");
+ lhs = Binary::new(
+ op.as_binary_op().expect("Could not get binary operation."),
+ lhs,
+ $lower::new($( self.$low_param ),*).parse(cursor, interner)?
+ ).into();
+ }
+ _ => break
}
- _ => break
}
- }
- Ok(lhs)
+ Ok(lhs)
+ }
}
- }
-} }
+ };
+}
/// Expression parsing.
///
@@ -762,3 +740,18 @@ expression!(
[name, allow_yield, allow_await],
Some(InputElement::Div)
);
+
+/// Returns an error if `arguments` or `eval` are used as identifier in strict mode.
+fn check_strict_arguments_or_eval(ident: Identifier, position: Position) -> Result<(), ParseError> {
+ match ident.sym() {
+ Sym::ARGUMENTS => Err(ParseError::general(
+ "unexpected identifier 'arguments' in strict mode",
+ position,
+ )),
+ Sym::EVAL => Err(ParseError::general(
+ "unexpected identifier 'eval' in strict mode",
+ position,
+ )),
+ _ => Ok(()),
+ }
+}
diff --git a/boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs b/boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs
index 5d1050b4d21..e5fe76d9749 100644
--- a/boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs
@@ -11,16 +11,16 @@
mod tests;
use crate::syntax::{
- ast::{
- expression::{literal, Spread},
- Punctuator,
- },
lexer::TokenKind,
parser::{
expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult,
TokenParser,
},
};
+use boa_ast::{
+ expression::{literal, Spread},
+ Punctuator,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/primary/array_initializer/tests.rs b/boa_engine/src/syntax/parser/expression/primary/array_initializer/tests.rs
index d61d0627477..f7dc087b298 100644
--- a/boa_engine/src/syntax/parser/expression/primary/array_initializer/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/array_initializer/tests.rs
@@ -1,11 +1,9 @@
// ! Tests for array initializer parsing.
-use crate::syntax::{
- ast::{
- expression::literal::{ArrayLiteral, Literal},
- Expression, Statement,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ expression::literal::{ArrayLiteral, Literal},
+ Expression, Statement,
};
use boa_interner::{Interner, Sym};
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs
index 2e535436f05..80743857730 100644
--- a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs
@@ -2,17 +2,15 @@
mod tests;
use crate::syntax::{
- ast::{
- expression::Identifier, function::function_contains_super, function::AsyncFunction,
- Keyword, Position, Punctuator,
- },
lexer::{Error as LexError, TokenKind},
parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody},
- AllowYield, Cursor, ParseError, ParseResult, TokenParser,
+ function_contains_super, name_in_lexically_declared_names, AllowYield, Cursor, ParseError,
+ ParseResult, TokenParser,
},
};
+use boa_ast::{expression::Identifier, function::AsyncFunction, Keyword, Position, Punctuator};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
@@ -126,7 +124,8 @@ where
// It is a Syntax Error if any element of the BoundNames of FormalParameters
// also occurs in the LexicallyDeclaredNames of FunctionBody.
// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
params_start_position,
)?;
diff --git a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs
index 069d803ea86..cd11c496d01 100644
--- a/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/async_function_expression/tests.rs
@@ -1,12 +1,10 @@
-use crate::syntax::{
- ast::{
- declaration::{Declaration, LexicalDeclaration, Variable},
- expression::literal::Literal,
- function::{AsyncFunction, FormalParameterList},
- statement::Return,
- Statement, StatementListItem,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ declaration::{Declaration, LexicalDeclaration, Variable},
+ expression::literal::Literal,
+ function::{AsyncFunction, FormalParameterList},
+ statement::Return,
+ Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs
index 9f61be7eddb..369c37cedf7 100644
--- a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs
@@ -11,17 +11,15 @@
mod tests;
use crate::syntax::{
- ast::{
- expression::Identifier, function::function_contains_super, function::AsyncGenerator,
- Keyword, Position, Punctuator,
- },
lexer::{Error as LexError, TokenKind},
parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody},
- Cursor, ParseError, ParseResult, TokenParser,
+ function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult,
+ TokenParser,
},
};
+use boa_ast::{expression::Identifier, function::AsyncGenerator, Keyword, Position, Punctuator};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
@@ -158,7 +156,8 @@ where
// It is a Syntax Error if any element of the BoundNames of FormalParameters
// also occurs in the LexicallyDeclaredNames of FunctionBody.
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
params_start_position,
)?;
diff --git a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs
index 57c3a17de21..e275bee8970 100644
--- a/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/async_generator_expression/tests.rs
@@ -1,14 +1,12 @@
use std::convert::TryInto;
-use crate::syntax::{
- ast::{
- declaration::{LexicalDeclaration, Variable},
- expression::literal::Literal,
- function::{AsyncGenerator, FormalParameterList},
- statement::Return,
- Declaration, Statement, StatementListItem,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ declaration::{LexicalDeclaration, Variable},
+ expression::literal::Literal,
+ function::{AsyncGenerator, FormalParameterList},
+ statement::Return,
+ Declaration, Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs
index fba2483f95d..dc41a142b5c 100644
--- a/boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs
@@ -1,11 +1,11 @@
use crate::syntax::{
- ast::{expression::Identifier, function::Class, Keyword},
lexer::TokenKind,
parser::{
expression::BindingIdentifier, statement::ClassTail, AllowAwait, AllowYield, Cursor,
ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{expression::Identifier, function::Class, Keyword};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs
index 03dcef6082e..65b04594099 100644
--- a/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs
@@ -11,18 +11,15 @@
mod tests;
use crate::syntax::{
- ast::{
- expression::Identifier,
- function::{function_contains_super, Function},
- Keyword, Position, Punctuator,
- },
lexer::{Error as LexError, TokenKind},
parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody},
- Cursor, ParseError, ParseResult, TokenParser,
+ function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult,
+ TokenParser,
},
};
+use boa_ast::{expression::Identifier, function::Function, Keyword, Position, Punctuator};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
@@ -121,7 +118,8 @@ where
// It is a Syntax Error if any element of the BoundNames of FormalParameters
// also occurs in the LexicallyDeclaredNames of FunctionBody.
// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
params_start_position,
)?;
diff --git a/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs
index 1f273b75e48..cf63d1735ce 100644
--- a/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/function_expression/tests.rs
@@ -1,12 +1,10 @@
-use crate::syntax::{
- ast::{
- declaration::{LexicalDeclaration, Variable},
- expression::literal::Literal,
- function::{FormalParameterList, Function},
- statement::Return,
- Declaration, Statement, StatementListItem,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ declaration::{LexicalDeclaration, Variable},
+ expression::literal::Literal,
+ function::{FormalParameterList, Function},
+ statement::Return,
+ Declaration, Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs b/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs
index 24ae0cb4c24..85c3306d722 100644
--- a/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs
@@ -11,18 +11,15 @@
mod tests;
use crate::syntax::{
- ast::{
- expression::Identifier,
- function::{function_contains_super, Generator},
- Position, Punctuator,
- },
lexer::{Error as LexError, TokenKind},
parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody},
- Cursor, ParseError, ParseResult, TokenParser,
+ function_contains_super, name_in_lexically_declared_names, Cursor, ParseError, ParseResult,
+ TokenParser,
},
};
+use boa_ast::{expression::Identifier, function::Generator, Position, Punctuator};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
@@ -123,7 +120,8 @@ where
// It is a Syntax Error if any element of the BoundNames of FormalParameters
// also occurs in the LexicallyDeclaredNames of FunctionBody.
// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
params_start_position,
)?;
diff --git a/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs b/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs
index 2fb521e60ef..819dafcb3a4 100644
--- a/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/generator_expression/tests.rs
@@ -1,11 +1,9 @@
-use crate::syntax::{
- ast::{
- declaration::{LexicalDeclaration, Variable},
- expression::{literal::Literal, Yield},
- function::{FormalParameterList, Generator},
- Declaration, Expression, Statement, StatementListItem,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ declaration::{LexicalDeclaration, Variable},
+ expression::{literal::Literal, Yield},
+ function::{FormalParameterList, Generator},
+ Declaration, Expression, Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/expression/primary/mod.rs b/boa_engine/src/syntax/parser/expression/primary/mod.rs
index b160d25c742..6c61b73acec 100644
--- a/boa_engine/src/syntax/parser/expression/primary/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/mod.rs
@@ -27,24 +27,6 @@ use self::{
object_initializer::ObjectLiteral,
};
use crate::syntax::{
- ast::{
- self,
- declaration::Variable,
- expression::{
- literal::Literal,
- operator::{
- assign::{
- array_decl_to_declaration_pattern, object_decl_to_declaration_pattern,
- AssignTarget,
- },
- binary::BinaryOp,
- },
- Call, Identifier, New,
- },
- function::{FormalParameter, FormalParameterList},
- pattern::{ArrayPatternElement, ObjectPatternElement, Pattern},
- Keyword, Punctuator, Span,
- },
lexer::{token::Numeric, InputElement, Token, TokenKind},
parser::{
expression::{
@@ -55,6 +37,18 @@ use crate::syntax::{
AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ self as ast,
+ declaration::Variable,
+ expression::{
+ literal::Literal,
+ operator::{assign::AssignTarget, binary::BinaryOp},
+ Call, Identifier, New,
+ },
+ function::{FormalParameter, FormalParameterList},
+ pattern::{ArrayPatternElement, ObjectPatternElement, Pattern},
+ Keyword, Punctuator, Span,
+};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
@@ -549,34 +543,30 @@ fn expression_to_formal_parameters(
}
},
ast::Expression::ObjectLiteral(object) => {
- let decl = object_decl_to_declaration_pattern(object, strict);
-
- if let Some(pattern) = decl {
- parameters.push(FormalParameter::new(
- Variable::from_pattern(pattern.into(), None),
- false,
- ));
- } else {
- return Err(ParseError::general(
+ let pattern = object.to_pattern(strict).ok_or_else(|| {
+ ParseError::general(
"invalid object binding pattern in formal parameter list",
span.start(),
- ));
- }
+ )
+ })?;
+
+ parameters.push(FormalParameter::new(
+ Variable::from_pattern(pattern.into(), None),
+ false,
+ ));
}
ast::Expression::ArrayLiteral(array) => {
- let decl = array_decl_to_declaration_pattern(array, strict);
-
- if let Some(pattern) = decl {
- parameters.push(FormalParameter::new(
- Variable::from_pattern(pattern.into(), None),
- false,
- ));
- } else {
- return Err(ParseError::general(
+ let pattern = array.to_pattern(strict).ok_or_else(|| {
+ ParseError::general(
"invalid array binding pattern in formal parameter list",
span.start(),
- ));
- }
+ )
+ })?;
+
+ parameters.push(FormalParameter::new(
+ Variable::from_pattern(pattern.into(), None),
+ false,
+ ));
}
_ => {
return Err(ParseError::unexpected(
diff --git a/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs b/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs
index 26d2f7414ba..03e75fd4b6b 100644
--- a/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs
@@ -11,22 +11,22 @@
mod tests;
use crate::syntax::{
- ast::{
- expression::{
- literal::{self, Literal},
- Identifier,
- },
- function::{function_contains_super, has_direct_super},
- function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator},
- property::{self, MethodDefinition},
- Expression, Keyword, Punctuator,
- },
lexer::{token::Numeric, Error as LexError, TokenKind},
parser::{
expression::{identifiers::IdentifierReference, AssignmentExpression},
function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters},
- AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
+ function_contains_super, has_direct_super, name_in_lexically_declared_names, AllowAwait,
+ AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
+ },
+};
+use boa_ast::{
+ expression::{
+ literal::{self, Literal},
+ Identifier,
},
+ function::{AsyncFunction, AsyncGenerator, FormalParameterList, Function, Generator},
+ property::{self, MethodDefinition},
+ Expression, Keyword, Punctuator,
};
use boa_interner::{Interner, Sym};
use boa_macros::utf16;
@@ -445,7 +445,7 @@ where
// It is a Syntax Error if any element of the BoundNames of FormalParameters also occurs in the LexicallyDeclaredNames of FunctionBody.
let lexically_declared_names = body.lexically_declared_names();
- for parameter in params.parameters.iter() {
+ for parameter in params.as_ref() {
for name in ¶meter.names() {
if lexically_declared_names.contains(&(*name, false)) {
return Err(ParseError::general(
@@ -717,7 +717,8 @@ where
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also
// occurs in the LexicallyDeclaredNames of GeneratorBody.
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
body_start,
)?;
@@ -831,7 +832,8 @@ where
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also
// occurs in the LexicallyDeclaredNames of GeneratorBody.
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
body_start,
)?;
@@ -916,7 +918,8 @@ where
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also
// occurs in the LexicallyDeclaredNames of GeneratorBody.
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
body_start,
)?;
diff --git a/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs b/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs
index 898f43aeb72..37b0c4738a0 100644
--- a/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/object_initializer/tests.rs
@@ -1,18 +1,16 @@
-use crate::syntax::{
- ast::{
- declaration::{LexicalDeclaration, Variable},
- expression::{
- literal::{Literal, ObjectLiteral},
- Identifier,
- },
- function::{
- AsyncFunction, AsyncGenerator, FormalParameter, FormalParameterList,
- FormalParameterListFlags, Function,
- },
- property::{MethodDefinition, PropertyDefinition, PropertyName},
- Declaration, StatementList,
+use crate::syntax::parser::tests::{check_invalid, check_parser};
+use boa_ast::{
+ declaration::{LexicalDeclaration, Variable},
+ expression::{
+ literal::{Literal, ObjectLiteral},
+ Identifier,
},
- parser::tests::{check_invalid, check_parser},
+ function::{
+ AsyncFunction, AsyncGenerator, FormalParameter, FormalParameterList,
+ FormalParameterListFlags, Function,
+ },
+ property::{MethodDefinition, PropertyDefinition, PropertyName},
+ Declaration, StatementList,
};
use boa_interner::Interner;
use boa_macros::utf16;
@@ -96,6 +94,17 @@ fn check_object_short_function() {
fn check_object_short_function_arguments() {
let mut interner = Interner::default();
+ let parameters = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(
+ interner.get_or_intern_static("test", utf16!("test")).into(),
+ None,
+ ),
+ false,
+ ));
+
+ assert_eq!(parameters.flags(), FormalParameterListFlags::default());
+ assert_eq!(parameters.length(), 1);
+
let object_properties = vec![
PropertyDefinition::Property(
interner.get_or_intern_static("a", utf16!("a")).into(),
@@ -103,21 +112,7 @@ fn check_object_short_function_arguments() {
),
PropertyDefinition::MethodDefinition(
interner.get_or_intern_static("b", utf16!("b")).into(),
- MethodDefinition::Ordinary(Function::new(
- None,
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("test", utf16!("test")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
- StatementList::default(),
- )),
+ MethodDefinition::Ordinary(Function::new(None, parameters, StatementList::default())),
),
];
@@ -182,6 +177,17 @@ fn check_object_getter() {
fn check_object_setter() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(
+ interner.get_or_intern_static("test", utf16!("test")).into(),
+ None,
+ ),
+ false,
+ ));
+
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
+
let object_properties = vec![
PropertyDefinition::Property(
interner.get_or_intern_static("a", utf16!("a")).into(),
@@ -189,21 +195,7 @@ fn check_object_setter() {
),
PropertyDefinition::MethodDefinition(
interner.get_or_intern_static("b", utf16!("b")).into(),
- MethodDefinition::Set(Function::new(
- None,
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("test", utf16!("test")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
- StatementList::default(),
- )),
+ MethodDefinition::Set(Function::new(None, params, StatementList::default())),
),
];
diff --git a/boa_engine/src/syntax/parser/expression/primary/template/mod.rs b/boa_engine/src/syntax/parser/expression/primary/template/mod.rs
index 807faad5cd2..187b088017d 100644
--- a/boa_engine/src/syntax/parser/expression/primary/template/mod.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/template/mod.rs
@@ -8,16 +8,16 @@
//! [spec]: https://tc39.es/ecma262/#sec-template-literals
use crate::syntax::{
- ast::{
- expression::literal::{self, TemplateElement},
- Position, Punctuator,
- },
lexer::TokenKind,
parser::{
expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult,
TokenParser,
},
};
+use boa_ast::{
+ expression::literal::{self, TemplateElement},
+ Position, Punctuator,
+};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/primary/tests.rs b/boa_engine/src/syntax/parser/expression/primary/tests.rs
index c793eb79ae1..0097f032a06 100644
--- a/boa_engine/src/syntax/parser/expression/primary/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/primary/tests.rs
@@ -1,7 +1,5 @@
-use crate::syntax::{
- ast::{expression::literal::Literal, Expression, Statement},
- parser::tests::check_parser,
-};
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{expression::literal::Literal, Expression, Statement};
use boa_interner::{Interner, Sym};
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/expression/tests.rs b/boa_engine/src/syntax/parser/expression/tests.rs
index 3356624ef06..21066e8459c 100644
--- a/boa_engine/src/syntax/parser/expression/tests.rs
+++ b/boa_engine/src/syntax/parser/expression/tests.rs
@@ -1,18 +1,16 @@
-use crate::syntax::{
- ast::{
- declaration::{LexicalDeclaration, Variable},
- expression::{
- literal::Literal,
- operator::{
- assign::AssignOp,
- binary::{ArithmeticOp, BitwiseOp, LogicalOp, RelationalOp},
- Assign, Binary,
- },
- Call, Identifier, New,
+use crate::syntax::parser::tests::{check_invalid, check_parser};
+use boa_ast::{
+ declaration::{LexicalDeclaration, Variable},
+ expression::{
+ literal::Literal,
+ operator::{
+ assign::AssignOp,
+ binary::{ArithmeticOp, BitwiseOp, LogicalOp, RelationalOp},
+ Assign, Binary,
},
- Declaration, Expression, Statement,
+ Call, Identifier, New,
},
- parser::tests::{check_invalid, check_parser},
+ Declaration, Expression, Statement,
};
use boa_interner::{Interner, Sym};
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/expression/unary.rs b/boa_engine/src/syntax/parser/expression/unary.rs
index 241c15d7646..9593ccbb642 100644
--- a/boa_engine/src/syntax/parser/expression/unary.rs
+++ b/boa_engine/src/syntax/parser/expression/unary.rs
@@ -8,20 +8,20 @@
//! [spec]: https://tc39.es/ecma262/#sec-unary-operators
use crate::syntax::{
- ast::{
- expression::{
- access::PropertyAccess,
- operator::{unary::UnaryOp, Unary},
- Identifier,
- },
- Expression, Keyword, Punctuator,
- },
lexer::{Error as LexError, TokenKind},
parser::{
expression::{await_expr::AwaitExpression, update::UpdateExpression},
AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ expression::{
+ access::PropertyAccess,
+ operator::{unary::UnaryOp, Unary},
+ Identifier,
+ },
+ Expression, Keyword, Punctuator,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/expression/update.rs b/boa_engine/src/syntax/parser/expression/update.rs
index bd0af16cd84..386b50b7660 100644
--- a/boa_engine/src/syntax/parser/expression/update.rs
+++ b/boa_engine/src/syntax/parser/expression/update.rs
@@ -5,21 +5,21 @@
//!
//! [spec]: https://tc39.es/ecma262/#sec-update-expressions
-use super::left_hand_side::LeftHandSideExpression;
+use super::{check_strict_arguments_or_eval, left_hand_side::LeftHandSideExpression};
use crate::syntax::{
- ast::{
- expression::{
- operator::{unary::UnaryOp, Unary},
- Identifier,
- },
- Expression, Punctuator,
- },
lexer::{Error as LexError, TokenKind},
parser::{
expression::unary::UnaryExpression, AllowAwait, AllowYield, Cursor, ParseError,
ParseResult, TokenParser,
},
};
+use boa_ast::{
+ expression::{
+ operator::{unary::UnaryOp, Unary},
+ Identifier,
+ },
+ Expression, Punctuator,
+};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
@@ -75,7 +75,7 @@ where
if cursor.strict_mode() {
if let Expression::Identifier(ident) = target {
- ident.check_strict_arguments_or_eval(position)?;
+ check_strict_arguments_or_eval(ident, position)?;
}
}
@@ -91,7 +91,7 @@ where
if cursor.strict_mode() {
if let Expression::Identifier(ident) = target {
- ident.check_strict_arguments_or_eval(position)?;
+ check_strict_arguments_or_eval(ident, position)?;
}
}
diff --git a/boa_engine/src/syntax/parser/function/mod.rs b/boa_engine/src/syntax/parser/function/mod.rs
index cf4f57b0f9c..5476d769717 100644
--- a/boa_engine/src/syntax/parser/function/mod.rs
+++ b/boa_engine/src/syntax/parser/function/mod.rs
@@ -11,12 +11,6 @@
mod tests;
use crate::syntax::{
- ast::{
- self,
- declaration::Variable,
- function::{FormalParameterList, FormalParameterListFlags},
- Punctuator,
- },
lexer::{Error as LexError, InputElement, TokenKind},
parser::{
expression::{BindingIdentifier, Initializer},
@@ -24,10 +18,15 @@ use crate::syntax::{
AllowAwait, AllowYield, Cursor, ParseError, TokenParser,
},
};
+use boa_ast::{
+ self as ast,
+ declaration::Variable,
+ function::{FormalParameterList, FormalParameterListFlags},
+ Punctuator,
+};
use boa_interner::{Interner, Sym};
use boa_macros::utf16;
use boa_profiler::Profiler;
-use rustc_hash::FxHashSet;
use std::io::Read;
use super::ParseResult;
@@ -70,43 +69,25 @@ where
let _timer = Profiler::global().start_event("FormalParameters", "Parsing");
cursor.set_goal(InputElement::RegExp);
- let mut flags = FormalParameterListFlags::default();
let mut params = Vec::new();
- let mut length = 0;
let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
if next_token.kind() == &TokenKind::Punctuator(Punctuator::CloseParen) {
- return Ok(FormalParameterList::new(
- params.into_boxed_slice(),
- flags,
- length,
- ));
+ return Ok(FormalParameterList::default());
}
let start_position = next_token.span().start();
- let mut parameter_names = FxHashSet::default();
-
loop {
let mut rest_param = false;
let next_param = match cursor.peek(0, interner)? {
Some(tok) if tok.kind() == &TokenKind::Punctuator(Punctuator::Spread) => {
rest_param = true;
- flags |= FormalParameterListFlags::HAS_REST_PARAMETER;
FunctionRestParameter::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?
}
- _ => {
- let param = FormalParameter::new(self.allow_yield, self.allow_await)
- .parse(cursor, interner)?;
- if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS)
- || param.is_rest_param()
- || param.init().is_some())
- {
- length += 1;
- }
- param
- }
+ _ => FormalParameter::new(self.allow_yield, self.allow_await)
+ .parse(cursor, interner)?,
};
if next_param.is_rest_param() && next_param.init().is_some() {
@@ -116,25 +97,6 @@ where
)));
}
- if next_param.init().is_some() {
- flags |= FormalParameterListFlags::HAS_EXPRESSIONS;
- }
- if next_param.names().contains(&Sym::ARGUMENTS.into()) {
- flags |= FormalParameterListFlags::HAS_ARGUMENTS;
- }
-
- if next_param.is_rest_param()
- || next_param.init().is_some()
- || !next_param.is_identifier()
- {
- flags.remove(FormalParameterListFlags::IS_SIMPLE);
- }
- for param_name in next_param.names() {
- if parameter_names.contains(¶m_name) {
- flags |= FormalParameterListFlags::HAS_DUPLICATES;
- }
- parameter_names.insert(Box::from(param_name));
- }
params.push(next_param);
if cursor
@@ -166,21 +128,21 @@ where
}
}
+ let params = FormalParameterList::from_parameters(params);
+
// Early Error: It is a Syntax Error if IsSimpleParameterList of FormalParameterList is false
// and BoundNames of FormalParameterList contains any duplicate elements.
- if !flags.contains(FormalParameterListFlags::IS_SIMPLE)
- && flags.contains(FormalParameterListFlags::HAS_DUPLICATES)
+ if !params.flags().contains(FormalParameterListFlags::IS_SIMPLE)
+ && params
+ .flags()
+ .contains(FormalParameterListFlags::HAS_DUPLICATES)
{
return Err(ParseError::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
start_position,
)));
}
- Ok(FormalParameterList::new(
- params.into_boxed_slice(),
- flags,
- length,
- ))
+ Ok(params)
}
}
diff --git a/boa_engine/src/syntax/parser/function/tests.rs b/boa_engine/src/syntax/parser/function/tests.rs
index 867abe974c1..c4b184356d4 100644
--- a/boa_engine/src/syntax/parser/function/tests.rs
+++ b/boa_engine/src/syntax/parser/function/tests.rs
@@ -1,45 +1,38 @@
use crate::{
string::utf16,
- syntax::{
- ast::{
- declaration::{LexicalDeclaration, Variable},
- expression::{
- operator::{binary::ArithmeticOp, Binary},
- Identifier,
- },
- function::{
- ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags,
- Function,
- },
- statement::Return,
- Declaration, Expression, Statement, StatementList, StatementListItem,
- },
- parser::tests::check_parser,
- Parser,
- },
+ syntax::{parser::tests::check_parser, Parser},
Context,
};
+use boa_ast::{
+ declaration::{LexicalDeclaration, Variable},
+ expression::{
+ operator::{binary::ArithmeticOp, Binary},
+ Identifier,
+ },
+ function::{
+ ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function,
+ },
+ statement::Return,
+ Declaration, Expression, Statement, StatementList, StatementListItem,
+};
use boa_interner::Interner;
/// Checks basic function declaration parsing.
#[test]
fn check_basic() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
+
check_parser(
"function foo(a) { return a; }",
vec![Declaration::Function(Function::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(),
@@ -56,31 +49,26 @@ fn check_basic() {
#[test]
fn check_duplicates_strict_off() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ ]);
+ assert_eq!(
+ params.flags(),
+ FormalParameterListFlags::default().union(FormalParameterListFlags::HAS_DUPLICATES)
+ );
+ assert_eq!(params.length(), 2);
check_parser(
"function foo(a, a) { return a; }",
vec![Declaration::Function(Function::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default()
- .union(FormalParameterListFlags::HAS_DUPLICATES),
- length: 2,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(),
@@ -107,21 +95,18 @@ fn check_duplicates_strict_on() {
#[test]
fn check_basic_semicolon_insertion() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
+
check_parser(
"function foo(a) { return a }",
vec![Declaration::Function(Function::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::from(interner.get_or_intern_static("a", utf16!("a"))).into(),
@@ -138,21 +123,17 @@ fn check_basic_semicolon_insertion() {
#[test]
fn check_empty_return() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
check_parser(
"function foo(a) { return; }",
vec![Declaration::Function(Function::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(None),
))]
@@ -167,21 +148,17 @@ fn check_empty_return() {
#[test]
fn check_empty_return_semicolon_insertion() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
check_parser(
"function foo(a) { return }",
vec![Declaration::Function(Function::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(None),
))]
@@ -196,31 +173,26 @@ fn check_empty_return_semicolon_insertion() {
#[test]
fn check_rest_operator() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ true,
+ ),
+ ]);
+ assert_eq!(
+ params.flags(),
+ FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER)
+ );
+ assert_eq!(params.length(), 1);
check_parser(
"function foo(a, ...b) {}",
vec![Declaration::Function(Function::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- true,
- ),
- ]),
- flags: FormalParameterListFlags::empty()
- .union(FormalParameterListFlags::HAS_REST_PARAMETER),
- length: 1,
- },
+ params,
StatementList::default(),
))
.into()],
@@ -232,22 +204,20 @@ fn check_rest_operator() {
#[test]
fn check_arrow_only_rest() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ true,
+ ));
+ assert_eq!(
+ params.flags(),
+ FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER)
+ );
+ assert_eq!(params.length(), 0);
check_parser(
"(...a) => {}",
vec![Statement::Expression(Expression::from(ArrowFunction::new(
None,
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- true,
- )]),
- flags: FormalParameterListFlags::empty()
- .union(FormalParameterListFlags::HAS_REST_PARAMETER),
- length: 0,
- },
+ params,
StatementList::default(),
)))
.into()],
@@ -259,38 +229,30 @@ fn check_arrow_only_rest() {
#[test]
fn check_arrow_rest() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("c", utf16!("c")).into(), None),
+ true,
+ ),
+ ]);
+ assert_eq!(
+ params.flags(),
+ FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER)
+ );
+ assert_eq!(params.length(), 2);
check_parser(
"(a, b, ...c) => {}",
vec![Statement::Expression(Expression::from(ArrowFunction::new(
None,
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("c", utf16!("c")).into(),
- None,
- ),
- true,
- ),
- ]),
- flags: FormalParameterListFlags::empty()
- .union(FormalParameterListFlags::HAS_REST_PARAMETER),
- length: 2,
- },
+ params,
StatementList::default(),
)))
.into()],
@@ -302,30 +264,23 @@ fn check_arrow_rest() {
#[test]
fn check_arrow() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ ]);
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 2);
check_parser(
"(a, b) => { return a + b; }",
vec![Statement::Expression(Expression::from(ArrowFunction::new(
None,
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default(),
- length: 2,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Binary::new(
@@ -347,30 +302,21 @@ fn check_arrow() {
#[test]
fn check_arrow_semicolon_insertion() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ ]);
check_parser(
"(a, b) => { return a + b }",
vec![Statement::Expression(Expression::from(ArrowFunction::new(
None,
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default(),
- length: 2,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Binary::new(
@@ -392,30 +338,21 @@ fn check_arrow_semicolon_insertion() {
#[test]
fn check_arrow_epty_return() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ ]);
check_parser(
"(a, b) => { return; }",
vec![Statement::Expression(Expression::from(ArrowFunction::new(
None,
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default(),
- length: 2,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(None),
))]
@@ -430,30 +367,21 @@ fn check_arrow_epty_return() {
#[test]
fn check_arrow_empty_return_semicolon_insertion() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ ]);
check_parser(
"(a, b) => { return }",
vec![Statement::Expression(Expression::from(ArrowFunction::new(
None,
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default(),
- length: 2,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(None),
))]
@@ -467,6 +395,12 @@ fn check_arrow_empty_return_semicolon_insertion() {
#[test]
fn check_arrow_assignment() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
check_parser(
"let foo = (a) => { return a };",
vec![Declaration::Lexical(LexicalDeclaration::Let(
@@ -475,17 +409,7 @@ fn check_arrow_assignment() {
Some(
ArrowFunction::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
@@ -508,6 +432,12 @@ fn check_arrow_assignment() {
#[test]
fn check_arrow_assignment_nobrackets() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
check_parser(
"let foo = (a) => a;",
vec![Declaration::Lexical(LexicalDeclaration::Let(
@@ -516,17 +446,7 @@ fn check_arrow_assignment_nobrackets() {
Some(
ArrowFunction::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
@@ -549,6 +469,12 @@ fn check_arrow_assignment_nobrackets() {
#[test]
fn check_arrow_assignment_noparenthesis() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
check_parser(
"let foo = a => { return a };",
vec![Declaration::Lexical(LexicalDeclaration::Let(
@@ -557,17 +483,7 @@ fn check_arrow_assignment_noparenthesis() {
Some(
ArrowFunction::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
@@ -590,6 +506,12 @@ fn check_arrow_assignment_noparenthesis() {
#[test]
fn check_arrow_assignment_noparenthesis_nobrackets() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 1);
check_parser(
"let foo = a => a;",
vec![Declaration::Lexical(LexicalDeclaration::Let(
@@ -598,17 +520,7 @@ fn check_arrow_assignment_noparenthesis_nobrackets() {
Some(
ArrowFunction::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- )]),
- flags: FormalParameterListFlags::default(),
- length: 1,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
@@ -631,6 +543,18 @@ fn check_arrow_assignment_noparenthesis_nobrackets() {
#[test]
fn check_arrow_assignment_2arg() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ ]);
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 2);
check_parser(
"let foo = (a, b) => { return a };",
vec![Declaration::Lexical(LexicalDeclaration::Let(
@@ -639,26 +563,7 @@ fn check_arrow_assignment_2arg() {
Some(
ArrowFunction::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default(),
- length: 2,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
@@ -681,6 +586,18 @@ fn check_arrow_assignment_2arg() {
#[test]
fn check_arrow_assignment_2arg_nobrackets() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ ]);
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 2);
check_parser(
"let foo = (a, b) => a;",
vec![Declaration::Lexical(LexicalDeclaration::Let(
@@ -689,26 +606,7 @@ fn check_arrow_assignment_2arg_nobrackets() {
Some(
ArrowFunction::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default(),
- length: 2,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
@@ -731,6 +629,22 @@ fn check_arrow_assignment_2arg_nobrackets() {
#[test]
fn check_arrow_assignment_3arg() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("c", utf16!("c")).into(), None),
+ false,
+ ),
+ ]);
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 3);
check_parser(
"let foo = (a, b, c) => { return a };",
vec![Declaration::Lexical(LexicalDeclaration::Let(
@@ -739,33 +653,7 @@ fn check_arrow_assignment_3arg() {
Some(
ArrowFunction::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("c", utf16!("c")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default(),
- length: 3,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
@@ -788,6 +676,22 @@ fn check_arrow_assignment_3arg() {
#[test]
fn check_arrow_assignment_3arg_nobrackets() {
let mut interner = Interner::default();
+ let params = FormalParameterList::from(vec![
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("a", utf16!("a")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("b", utf16!("b")).into(), None),
+ false,
+ ),
+ FormalParameter::new(
+ Variable::from_identifier(interner.get_or_intern_static("c", utf16!("c")).into(), None),
+ false,
+ ),
+ ]);
+ assert_eq!(params.flags(), FormalParameterListFlags::default());
+ assert_eq!(params.length(), 3);
check_parser(
"let foo = (a, b, c) => a;",
vec![Declaration::Lexical(LexicalDeclaration::Let(
@@ -796,33 +700,7 @@ fn check_arrow_assignment_3arg_nobrackets() {
Some(
ArrowFunction::new(
Some(interner.get_or_intern_static("foo", utf16!("foo")).into()),
- FormalParameterList {
- parameters: Box::new([
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("a", utf16!("a")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("b", utf16!("b")).into(),
- None,
- ),
- false,
- ),
- FormalParameter::new(
- Variable::from_identifier(
- interner.get_or_intern_static("c", utf16!("c")).into(),
- None,
- ),
- false,
- ),
- ]),
- flags: FormalParameterListFlags::default(),
- length: 3,
- },
+ params,
vec![StatementListItem::Statement(Statement::Return(
Return::new(Some(
Identifier::new(interner.get_or_intern_static("a", utf16!("a")))
diff --git a/boa_engine/src/syntax/parser/mod.rs b/boa_engine/src/syntax/parser/mod.rs
index 1d1bd90559d..b78aaa5a6e6 100644
--- a/boa_engine/src/syntax/parser/mod.rs
+++ b/boa_engine/src/syntax/parser/mod.rs
@@ -27,9 +27,8 @@ use rustc_hash::{FxHashMap, FxHashSet};
use std::io::Read;
pub use self::error::{ParseError, ParseResult};
-pub(in crate::syntax) use expression::RESERVED_IDENTIFIERS_STRICT;
-use super::ast::{
+use boa_ast::{
expression::Identifier, function::FormalParameterList, ContainsSymbol, Position, StatementList,
};
@@ -426,3 +425,60 @@ where
Ok(body)
}
}
+
+// Checks if a function contains a super call or super property access.
+fn function_contains_super(body: &StatementList, parameters: &FormalParameterList) -> bool {
+ for param in parameters.as_ref() {
+ if param.variable().contains(ContainsSymbol::SuperCall)
+ || param.variable().contains(ContainsSymbol::SuperProperty)
+ {
+ return true;
+ }
+ }
+ for node in body.statements() {
+ if node.contains(ContainsSymbol::SuperCall) || node.contains(ContainsSymbol::SuperProperty)
+ {
+ return true;
+ }
+ }
+ false
+}
+
+/// Returns `true` if the function parameters or body contain a direct `super` call.
+///
+/// More information:
+/// - [ECMAScript specification][spec]
+///
+/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper
+pub fn has_direct_super(body: &StatementList, parameters: &FormalParameterList) -> bool {
+ for param in parameters.as_ref() {
+ if param.variable().contains(ContainsSymbol::SuperCall) {
+ return true;
+ }
+ }
+ for node in body.statements() {
+ if node.contains(ContainsSymbol::SuperCall) {
+ return true;
+ }
+ }
+ false
+}
+
+/// Helper to check if any parameter names are declared in the given list.
+fn name_in_lexically_declared_names(
+ parameter_list: &FormalParameterList,
+ names: &[Identifier],
+ position: Position,
+) -> Result<(), ParseError> {
+ for parameter in parameter_list.as_ref() {
+ for name in ¶meter.names() {
+ if names.contains(name) {
+ return Err(ParseError::General {
+ message: "formal parameter declared in lexically declared names",
+ position,
+ });
+ }
+ }
+ }
+ Ok(())
+}
diff --git a/boa_engine/src/syntax/parser/statement/block/mod.rs b/boa_engine/src/syntax/parser/statement/block/mod.rs
index 73030c642c7..4e62c2a2acf 100644
--- a/boa_engine/src/syntax/parser/statement/block/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/block/mod.rs
@@ -12,10 +12,10 @@ mod tests;
use super::StatementList;
use crate::syntax::{
- ast::{expression::Identifier, statement, Punctuator},
lexer::TokenKind,
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
};
+use boa_ast::{expression::Identifier, statement, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use rustc_hash::{FxHashMap, FxHashSet};
diff --git a/boa_engine/src/syntax/parser/statement/block/tests.rs b/boa_engine/src/syntax/parser/statement/block/tests.rs
index e38fa4f90e7..2be9fb324b6 100644
--- a/boa_engine/src/syntax/parser/statement/block/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/block/tests.rs
@@ -2,19 +2,17 @@
use std::convert::TryInto;
-use crate::syntax::{
- ast::{
- declaration::{VarDeclaration, Variable},
- expression::{
- literal::Literal,
- operator::{assign::AssignOp, unary::UnaryOp, Assign, Unary},
- Call, Identifier,
- },
- function::{FormalParameterList, Function},
- statement::{Block, Return},
- Declaration, Expression, Statement, StatementListItem,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ declaration::{VarDeclaration, Variable},
+ expression::{
+ literal::Literal,
+ operator::{assign::AssignOp, unary::UnaryOp, Assign, Unary},
+ Call, Identifier,
},
- parser::tests::check_parser,
+ function::{FormalParameterList, Function},
+ statement::{Block, Return},
+ Declaration, Expression, Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/break_stm/mod.rs b/boa_engine/src/syntax/parser/statement/break_stm/mod.rs
index 6c766ef0ed1..bc523fb2159 100644
--- a/boa_engine/src/syntax/parser/statement/break_stm/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/break_stm/mod.rs
@@ -11,7 +11,6 @@
mod tests;
use crate::syntax::{
- ast::{statement::Break, Keyword, Punctuator},
lexer::TokenKind,
parser::{
cursor::{Cursor, SemicolonResult},
@@ -19,6 +18,7 @@ use crate::syntax::{
AllowAwait, AllowYield, ParseResult, TokenParser,
},
};
+use boa_ast::{statement::Break, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/break_stm/tests.rs b/boa_engine/src/syntax/parser/statement/break_stm/tests.rs
index 824648a82b1..38d6f82fd43 100644
--- a/boa_engine/src/syntax/parser/statement/break_stm/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/break_stm/tests.rs
@@ -1,10 +1,8 @@
-use crate::syntax::{
- ast::{
- expression::literal::Literal,
- statement::{Block, Break, WhileLoop},
- Statement, StatementListItem,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ expression::literal::Literal,
+ statement::{Block, Break, WhileLoop},
+ Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs b/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs
index fc336499540..c81d0a59b5c 100644
--- a/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/continue_stm/mod.rs
@@ -11,7 +11,6 @@
mod tests;
use crate::syntax::{
- ast::{statement::Continue, Keyword, Punctuator},
lexer::TokenKind,
parser::{
cursor::{Cursor, SemicolonResult},
@@ -19,6 +18,7 @@ use crate::syntax::{
AllowAwait, AllowYield, ParseResult, TokenParser,
},
};
+use boa_ast::{statement::Continue, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs b/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs
index 7f83ae3e079..600646a0826 100644
--- a/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/continue_stm/tests.rs
@@ -1,10 +1,8 @@
-use crate::syntax::{
- ast::{
- expression::literal::Literal,
- statement::{Block, Continue, WhileLoop},
- Statement, StatementListItem,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ expression::literal::Literal,
+ statement::{Block, Continue, WhileLoop},
+ Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs
index 9286aa0229c..0d7abe6b6dd 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs
@@ -1,14 +1,11 @@
#[cfg(test)]
mod tests;
-use crate::syntax::{
- ast::function::AsyncFunction,
- ast::Keyword,
- parser::{
- statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
- AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
- },
+use crate::syntax::parser::{
+ statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
+ AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
};
+use boa_ast::{function::AsyncFunction, Keyword};
use boa_interner::Interner;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs
index 3a30a2ea49d..6b5f8382b05 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs
@@ -1,9 +1,7 @@
-use crate::syntax::{
- ast::{
- function::{AsyncFunction, FormalParameterList},
- Declaration, StatementList,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ function::{AsyncFunction, FormalParameterList},
+ Declaration, StatementList,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs
index 5dd8ef292da..2f5b0b2f4e1 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs
@@ -6,13 +6,11 @@
#[cfg(test)]
mod tests;
-use crate::syntax::{
- ast::{function::AsyncGenerator, Keyword, Punctuator},
- parser::{
- statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
- AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
- },
+use crate::syntax::parser::{
+ statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
+ AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
};
+use boa_ast::{function::AsyncGenerator, Keyword, Punctuator};
use boa_interner::Interner;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs
index 7a0fd51c16b..15ad31e4bfb 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs
@@ -1,9 +1,7 @@
-use crate::syntax::{
- ast::{
- function::{AsyncGenerator, FormalParameterList},
- Declaration, StatementList,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ function::{AsyncGenerator, FormalParameterList},
+ Declaration, StatementList,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs
index 15d95617e22..d679ebf94b5 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs
@@ -2,15 +2,6 @@
mod tests;
use crate::syntax::{
- ast::{
- self,
- expression::Identifier,
- function::{
- self, function_contains_super, has_direct_super, Class, FormalParameterList, Function,
- },
- property::{ClassElementName, MethodDefinition},
- ContainsSymbol, Declaration, Expression, Keyword, Punctuator,
- },
lexer::{Error as LexError, TokenKind},
parser::{
expression::{
@@ -18,10 +9,18 @@ use crate::syntax::{
GeneratorMethod, LeftHandSideExpression, PropertyName,
},
function::{FormalParameters, FunctionBody, UniqueFormalParameters, FUNCTION_BREAK_TOKENS},
+ function_contains_super, has_direct_super,
statement::StatementList,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ self as ast,
+ expression::Identifier,
+ function::{self, Class, FormalParameterList, Function},
+ property::{ClassElementName, MethodDefinition},
+ ContainsSymbol, Declaration, Expression, Keyword, Punctuator,
+};
use boa_interner::{Interner, Sym};
use rustc_hash::{FxHashMap, FxHashSet};
use std::io::Read;
@@ -919,7 +918,7 @@ where
let method = MethodDefinition::Get(Function::new(
None,
- FormalParameterList::empty(),
+ FormalParameterList::default(),
body,
));
if r#static {
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/tests.rs
index 7c394688ec4..af1b9f95da5 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/tests.rs
@@ -1,11 +1,9 @@
-use crate::syntax::{
- ast::{
- expression::literal::Literal,
- function::{Class, ClassElement, FormalParameterList, Function},
- property::{MethodDefinition, PropertyName},
- Declaration, StatementList,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ expression::literal::Literal,
+ function::{Class, ClassElement, FormalParameterList, Function},
+ property::{MethodDefinition, PropertyName},
+ Declaration, StatementList,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs
index 7c237367ef0..ec4d11010e7 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs
@@ -1,13 +1,11 @@
#[cfg(test)]
mod tests;
-use crate::syntax::{
- ast::{function::Function, Keyword},
- parser::{
- statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
- AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
- },
+use crate::syntax::parser::{
+ statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
+ AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
};
+use boa_ast::{function::Function, Keyword};
use boa_interner::Interner;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs
index d8552dca968..6000d0375a1 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs
@@ -1,9 +1,7 @@
-use crate::syntax::{
- ast::{
- function::{FormalParameterList, Function},
- Declaration, StatementList,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ function::{FormalParameterList, Function},
+ Declaration, StatementList,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs
index d991cc0cca0..8ebdbb14ce6 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs
@@ -1,14 +1,11 @@
#[cfg(test)]
mod tests;
-use crate::syntax::{
- ast::function::Generator,
- ast::{Keyword, Punctuator},
- parser::{
- statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
- AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
- },
+use crate::syntax::parser::{
+ statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
+ AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
};
+use boa_ast::{function::Generator, Keyword, Punctuator};
use boa_interner::Interner;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs
index 64f2b5bb14c..0d568a724e4 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/generator_decl/tests.rs
@@ -1,9 +1,7 @@
-use crate::syntax::{
- ast::{
- function::{FormalParameterList, Generator},
- Declaration, StatementList,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ function::{FormalParameterList, Generator},
+ Declaration, StatementList,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs
index c7ada60691a..732c00c3382 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs
@@ -20,19 +20,19 @@ use self::{
class_decl::ClassDeclaration, generator_decl::GeneratorDeclaration,
};
use crate::syntax::{
- ast::{
- expression::Identifier,
- function::{function_contains_super, FormalParameterList},
- Declaration, Keyword, Position, Punctuator, StatementList,
- },
lexer::TokenKind,
parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody},
+ function_contains_super, name_in_lexically_declared_names,
statement::LexError,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ expression::Identifier, function::FormalParameterList, Declaration, Keyword, Position,
+ Punctuator, StatementList,
+};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
@@ -210,7 +210,8 @@ fn parse_callable_declaration(
// It is a Syntax Error if any element of the BoundNames of FormalParameters
// also occurs in the LexicallyDeclaredNames of FunctionBody.
// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
- params.name_in_lexically_declared_names(
+ name_in_lexically_declared_names(
+ ¶ms,
&body.lexically_declared_names_top_level(),
params_start_position,
)?;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/lexical.rs b/boa_engine/src/syntax/parser/statement/declaration/lexical.rs
index 994cf2b94f1..0f2bfca73fe 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/lexical.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/lexical.rs
@@ -8,7 +8,6 @@
//! [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
use crate::syntax::{
- ast::{self, declaration::Variable, pattern::Pattern, Keyword, Punctuator},
lexer::{Error as LexError, TokenKind},
parser::{
cursor::{Cursor, SemicolonResult},
@@ -17,6 +16,7 @@ use crate::syntax::{
AllowAwait, AllowIn, AllowYield, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{self as ast, declaration::Variable, pattern::Pattern, Keyword, Punctuator};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::{convert::TryInto, io::Read};
diff --git a/boa_engine/src/syntax/parser/statement/declaration/mod.rs b/boa_engine/src/syntax/parser/statement/declaration/mod.rs
index d5f3918db08..2aa8052b163 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/mod.rs
@@ -19,10 +19,10 @@ use hoistable::HoistableDeclaration;
pub(in crate::syntax::parser) use lexical::LexicalDeclaration;
use crate::syntax::{
- ast::{self, Keyword},
lexer::TokenKind,
parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
};
+use boa_ast::{self as ast, Keyword};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/declaration/tests.rs b/boa_engine/src/syntax/parser/statement/declaration/tests.rs
index ef6fb26081c..db6d9172201 100644
--- a/boa_engine/src/syntax/parser/statement/declaration/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/declaration/tests.rs
@@ -1,12 +1,10 @@
use std::convert::TryInto;
-use crate::syntax::{
- ast::{
- declaration::{LexicalDeclaration, VarDeclaration, Variable},
- expression::literal::Literal,
- Declaration, Statement,
- },
- parser::tests::{check_invalid, check_parser},
+use crate::syntax::parser::tests::{check_invalid, check_parser};
+use boa_ast::{
+ declaration::{LexicalDeclaration, VarDeclaration, Variable},
+ expression::literal::Literal,
+ Declaration, Statement,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/expression/mod.rs b/boa_engine/src/syntax/parser/statement/expression/mod.rs
index 57f7ee33435..6d9db6ec792 100644
--- a/boa_engine/src/syntax/parser/statement/expression/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/expression/mod.rs
@@ -1,11 +1,11 @@
use crate::syntax::{
- ast::{Keyword, Punctuator, Statement},
lexer::TokenKind,
parser::{
expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult,
TokenParser,
},
};
+use boa_ast::{Keyword, Punctuator, Statement};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/if_stm/mod.rs b/boa_engine/src/syntax/parser/statement/if_stm/mod.rs
index 79a56b92149..1c06bf2a682 100644
--- a/boa_engine/src/syntax/parser/statement/if_stm/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/if_stm/mod.rs
@@ -2,16 +2,16 @@
mod tests;
use crate::syntax::{
- ast::{
- statement::{Block, If},
- Declaration, Keyword, Punctuator, StatementListItem,
- },
lexer::TokenKind,
parser::{
expression::Expression, statement::declaration::FunctionDeclaration, AllowAwait,
AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ statement::{Block, If},
+ Declaration, Keyword, Punctuator, StatementListItem,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/if_stm/tests.rs b/boa_engine/src/syntax/parser/statement/if_stm/tests.rs
index a4a694d7cfa..405f229e0fa 100644
--- a/boa_engine/src/syntax/parser/statement/if_stm/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/if_stm/tests.rs
@@ -1,10 +1,8 @@
-use crate::syntax::{
- ast::{
- expression::literal::Literal,
- statement::{Block, If},
- Statement,
- },
- parser::tests::check_parser,
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{
+ expression::literal::Literal,
+ statement::{Block, If},
+ Statement,
};
use boa_interner::Interner;
diff --git a/boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs b/boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs
index 655a17b0f01..fd157959672 100644
--- a/boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs
+++ b/boa_engine/src/syntax/parser/statement/iteration/do_while_statement.rs
@@ -8,13 +8,13 @@
//! [spec]: https://tc39.es/ecma262/#sec-do-while-statement
use crate::syntax::{
- ast::{statement::DoWhileLoop, Keyword, Punctuator},
lexer::TokenKind,
parser::{
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor,
ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{statement::DoWhileLoop, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs
index e08232a3a21..146739f747e 100644
--- a/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs
+++ b/boa_engine/src/syntax/parser/statement/iteration/for_statement.rs
@@ -8,17 +8,6 @@
//! [spec]: https://tc39.es/ecma262/#sec-for-statement
use crate::syntax::{
- ast::{
- self,
- expression::operator::assign::{
- array_decl_to_declaration_pattern, object_decl_to_declaration_pattern,
- },
- statement::{
- iteration::{ForLoopInitializer, IterableLoopInitializer},
- ForInLoop, ForLoop, ForOfLoop,
- },
- Keyword, Position, Punctuator,
- },
lexer::{Error as LexError, TokenKind},
parser::{
expression::Expression,
@@ -27,6 +16,14 @@ use crate::syntax::{
AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ self as ast,
+ statement::{
+ iteration::{ForLoopInitializer, IterableLoopInitializer},
+ ForInLoop, ForLoop, ForOfLoop,
+ },
+ Keyword, Position, Punctuator,
+};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use rustc_hash::FxHashSet;
@@ -357,26 +354,20 @@ fn initializer_to_iterable_loop_initializer(
)))
}
ast::Expression::Identifier(ident) => Ok(IterableLoopInitializer::Identifier(ident)),
- ast::Expression::ArrayLiteral(array) => {
- array_decl_to_declaration_pattern(&array, strict)
- .ok_or(ParseError::General {
- message: "
- invalid array destructuring pattern in iterable loop initializer
- ",
- position,
- })
- .map(|arr| IterableLoopInitializer::Pattern(arr.into()))
- }
- ast::Expression::ObjectLiteral(object) => {
- object_decl_to_declaration_pattern(&object, strict)
- .ok_or(ParseError::General {
- message: "
- invalid object destructuring pattern in iterable loop initializer
- ",
- position,
- })
- .map(|obj| IterableLoopInitializer::Pattern(obj.into()))
- }
+ ast::Expression::ArrayLiteral(array) => array
+ .to_pattern(strict)
+ .ok_or(ParseError::General {
+ message: "invalid array destructuring pattern in iterable loop initializer",
+ position,
+ })
+ .map(|arr| IterableLoopInitializer::Pattern(arr.into())),
+ ast::Expression::ObjectLiteral(object) => object
+ .to_pattern(strict)
+ .ok_or(ParseError::General {
+ message: "invalid object destructuring pattern in iterable loop initializer",
+ position,
+ })
+ .map(|obj| IterableLoopInitializer::Pattern(obj.into())),
ast::Expression::PropertyAccess(access) => Ok(IterableLoopInitializer::Access(access)),
_ => Err(ParseError::lex(LexError::Syntax(
"invalid variable for iterable loop".into(),
diff --git a/boa_engine/src/syntax/parser/statement/iteration/tests.rs b/boa_engine/src/syntax/parser/statement/iteration/tests.rs
index 8ecc62df31f..2db171ccd1f 100644
--- a/boa_engine/src/syntax/parser/statement/iteration/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/iteration/tests.rs
@@ -1,18 +1,14 @@
-use crate::syntax::{
- ast::{
- declaration::{VarDeclaration, Variable},
- expression::{
- access::SimplePropertyAccess,
- literal::Literal,
- operator::{
- assign::AssignOp, binary::RelationalOp, unary::UnaryOp, Assign, Binary, Unary,
- },
- Call, Identifier,
- },
- statement::{Block, Break, DoWhileLoop, WhileLoop},
- Expression, Statement, StatementListItem,
+use crate::syntax::parser::tests::{check_invalid, check_parser};
+use boa_ast::{
+ declaration::{VarDeclaration, Variable},
+ expression::{
+ access::SimplePropertyAccess,
+ literal::Literal,
+ operator::{assign::AssignOp, binary::RelationalOp, unary::UnaryOp, Assign, Binary, Unary},
+ Call, Identifier,
},
- parser::tests::{check_invalid, check_parser},
+ statement::{Block, Break, DoWhileLoop, WhileLoop},
+ Expression, Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs b/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs
index eff204947bd..47d113b0278 100644
--- a/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs
+++ b/boa_engine/src/syntax/parser/statement/iteration/while_statement.rs
@@ -1,10 +1,8 @@
-use crate::syntax::{
- ast::{statement::WhileLoop, Keyword, Punctuator},
- parser::{
- expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor,
- ParseError, ParseResult, TokenParser,
- },
+use crate::syntax::parser::{
+ expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor,
+ ParseError, ParseResult, TokenParser,
};
+use boa_ast::{statement::WhileLoop, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs b/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs
index b6993b1c267..b266d742759 100644
--- a/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs
@@ -1,5 +1,4 @@
use crate::syntax::{
- ast::{self, Keyword, Punctuator},
lexer::TokenKind,
parser::{
cursor::Cursor,
@@ -9,6 +8,7 @@ use crate::syntax::{
AllowYield, ParseResult, TokenParser,
},
};
+use boa_ast::{self as ast, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/mod.rs b/boa_engine/src/syntax/parser/statement/mod.rs
index 846116b625b..5822e4de161 100644
--- a/boa_engine/src/syntax/parser/statement/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/mod.rs
@@ -41,14 +41,14 @@ use super::{
TokenParser,
};
use crate::syntax::{
- ast::{
- self,
- pattern::{ArrayPattern, ArrayPatternElement, ObjectPatternElement},
- Keyword, Punctuator,
- },
lexer::{Error as LexError, InputElement, Token, TokenKind},
parser::expression::{BindingIdentifier, Initializer},
};
+use boa_ast::{
+ self as ast,
+ pattern::{ArrayPattern, ArrayPatternElement, ObjectPatternElement},
+ Keyword, Punctuator,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/return_stm/mod.rs b/boa_engine/src/syntax/parser/statement/return_stm/mod.rs
index ea94ff190f5..b986db7a63f 100644
--- a/boa_engine/src/syntax/parser/statement/return_stm/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/return_stm/mod.rs
@@ -1,5 +1,4 @@
use crate::syntax::{
- ast::{statement::Return, Keyword, Punctuator},
lexer::TokenKind,
parser::{
cursor::{Cursor, SemicolonResult},
@@ -7,6 +6,7 @@ use crate::syntax::{
AllowAwait, AllowYield, ParseResult, TokenParser,
},
};
+use boa_ast::{statement::Return, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/switch/mod.rs b/boa_engine/src/syntax/parser/statement/switch/mod.rs
index 9094e104c89..05f9950d418 100644
--- a/boa_engine/src/syntax/parser/statement/switch/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/switch/mod.rs
@@ -2,13 +2,15 @@
mod tests;
use crate::syntax::{
- ast::{self, expression::Identifier, statement, statement::Switch, Keyword, Punctuator},
lexer::TokenKind,
parser::{
expression::Expression, statement::StatementList, AllowAwait, AllowReturn, AllowYield,
Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ self as ast, expression::Identifier, statement, statement::Switch, Keyword, Punctuator,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use rustc_hash::{FxHashMap, FxHashSet};
diff --git a/boa_engine/src/syntax/parser/statement/switch/tests.rs b/boa_engine/src/syntax/parser/statement/switch/tests.rs
index 7306769b6d1..06eb58a0d62 100644
--- a/boa_engine/src/syntax/parser/statement/switch/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/switch/tests.rs
@@ -1,11 +1,9 @@
-use crate::syntax::{
- ast::{
- declaration::{LexicalDeclaration, Variable},
- expression::{access::SimplePropertyAccess, literal::Literal, Call, Identifier},
- statement::{Break, Case, Switch},
- Declaration, Expression, Statement,
- },
- parser::tests::{check_invalid, check_parser},
+use crate::syntax::parser::tests::{check_invalid, check_parser};
+use boa_ast::{
+ declaration::{LexicalDeclaration, Variable},
+ expression::{access::SimplePropertyAccess, literal::Literal, Call, Identifier},
+ statement::{Break, Case, Switch},
+ Declaration, Expression, Statement,
};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/throw/mod.rs b/boa_engine/src/syntax/parser/statement/throw/mod.rs
index c53e8bfcfd0..78f17fbdbab 100644
--- a/boa_engine/src/syntax/parser/statement/throw/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/throw/mod.rs
@@ -2,10 +2,10 @@
mod tests;
use crate::syntax::{
- ast::{statement::Throw, Keyword, Punctuator},
lexer::TokenKind,
parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
};
+use boa_ast::{statement::Throw, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/throw/tests.rs b/boa_engine/src/syntax/parser/statement/throw/tests.rs
index 8c869cb5b4d..960a94a191b 100644
--- a/boa_engine/src/syntax/parser/statement/throw/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/throw/tests.rs
@@ -1,7 +1,5 @@
-use crate::syntax::{
- ast::{expression::literal::Literal, statement::Throw, Statement},
- parser::tests::check_parser,
-};
+use crate::syntax::parser::tests::check_parser;
+use boa_ast::{expression::literal::Literal, statement::Throw, Statement};
use boa_interner::Interner;
use boa_macros::utf16;
diff --git a/boa_engine/src/syntax/parser/statement/try_stm/catch.rs b/boa_engine/src/syntax/parser/statement/try_stm/catch.rs
index 3721f7bbcdb..0cbc2906dc6 100644
--- a/boa_engine/src/syntax/parser/statement/try_stm/catch.rs
+++ b/boa_engine/src/syntax/parser/statement/try_stm/catch.rs
@@ -1,11 +1,11 @@
use crate::syntax::{
- ast::{declaration::Binding, statement, Keyword, Punctuator},
lexer::TokenKind,
parser::{
statement::{block::Block, ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern},
AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{declaration::Binding, statement, Keyword, Punctuator};
use boa_interner::Interner;
use boa_profiler::Profiler;
use rustc_hash::FxHashSet;
diff --git a/boa_engine/src/syntax/parser/statement/try_stm/finally.rs b/boa_engine/src/syntax/parser/statement/try_stm/finally.rs
index 3a6262e0c78..9e670a7ba43 100644
--- a/boa_engine/src/syntax/parser/statement/try_stm/finally.rs
+++ b/boa_engine/src/syntax/parser/statement/try_stm/finally.rs
@@ -1,10 +1,7 @@
-use crate::syntax::{
- ast::{statement, Keyword},
- parser::{
- statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult,
- TokenParser,
- },
+use crate::syntax::parser::{
+ statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult, TokenParser,
};
+use boa_ast::{statement, Keyword};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
diff --git a/boa_engine/src/syntax/parser/statement/try_stm/mod.rs b/boa_engine/src/syntax/parser/statement/try_stm/mod.rs
index 73aa05a93da..989a738e39e 100644
--- a/boa_engine/src/syntax/parser/statement/try_stm/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/try_stm/mod.rs
@@ -7,10 +7,13 @@ mod tests;
use self::{catch::Catch, finally::Finally};
use super::block::Block;
use crate::syntax::{
- ast::{statement::Try, Keyword},
lexer::TokenKind,
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
};
+use boa_ast::{
+ statement::{ErrorHandler, Try},
+ Keyword,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::io::Read;
@@ -89,7 +92,7 @@ where
};
let next_token = cursor.peek(0, interner)?;
- let finally_block = if let Some(token) = next_token {
+ let finally = if let Some(token) = next_token {
match token.kind() {
TokenKind::Keyword((Keyword::Finally, true)) => {
return Err(ParseError::general(
@@ -107,6 +110,13 @@ where
None
};
- Ok(Try::new(try_clause, catch, finally_block))
+ let handler = match (catch, finally) {
+ (Some(catch), None) => ErrorHandler::Catch(catch),
+ (None, Some(finally)) => ErrorHandler::Finally(finally),
+ (Some(catch), Some(finally)) => ErrorHandler::Full(catch, finally),
+ (None, None) => unreachable!(),
+ };
+
+ Ok(Try::new(try_clause, handler))
}
}
diff --git a/boa_engine/src/syntax/parser/statement/try_stm/tests.rs b/boa_engine/src/syntax/parser/statement/try_stm/tests.rs
index c789ffb126b..ccebb3fef29 100644
--- a/boa_engine/src/syntax/parser/statement/try_stm/tests.rs
+++ b/boa_engine/src/syntax/parser/statement/try_stm/tests.rs
@@ -1,15 +1,13 @@
use std::convert::TryInto;
-use crate::syntax::{
- ast::{
- declaration::{VarDeclaration, Variable},
- expression::{literal::Literal, Identifier},
- pattern::{ArrayPatternElement, ObjectPatternElement, Pattern},
- property::PropertyName,
- statement::{Block, Catch, Finally, Try},
- Statement, StatementListItem,
- },
- parser::tests::{check_invalid, check_parser},
+use crate::syntax::parser::tests::{check_invalid, check_parser};
+use boa_ast::{
+ declaration::{VarDeclaration, Variable},
+ expression::{literal::Literal, Identifier},
+ pattern::{ArrayPatternElement, ObjectPatternElement, Pattern},
+ property::PropertyName,
+ statement::{Block, Catch, ErrorHandler, Finally, Try},
+ Statement, StatementListItem,
};
use boa_interner::Interner;
use boa_macros::utf16;
@@ -21,11 +19,10 @@ fn check_inline_with_empty_try_catch() {
"try { } catch(e) {}",
vec![Statement::Try(Try::new(
Block::default(),
- Some(Catch::new(
+ ErrorHandler::Catch(Catch::new(
Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()),
Block::default(),
)),
- None,
))
.into()],
interner,
@@ -48,11 +45,10 @@ fn check_inline_with_var_decl_inside_try() {
))
.into()]
.into(),
- Some(Catch::new(
+ ErrorHandler::Catch(Catch::new(
Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()),
Block::default(),
)),
- None,
))
.into()],
interner,
@@ -75,7 +71,7 @@ fn check_inline_with_var_decl_inside_catch() {
))
.into()]
.into(),
- Some(Catch::new(
+ ErrorHandler::Catch(Catch::new(
Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()),
vec![Statement::Var(VarDeclaration(
vec![Variable::from_identifier(
@@ -88,7 +84,6 @@ fn check_inline_with_var_decl_inside_catch() {
.into()]
.into(),
)),
- None,
))
.into()],
interner,
@@ -102,11 +97,13 @@ fn check_inline_with_empty_try_catch_finally() {
"try {} catch(e) {} finally {}",
vec![Statement::Try(Try::new(
Block::default(),
- Some(Catch::new(
- Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()),
- Block::default(),
- )),
- Some(Finally::from(Block::default())),
+ ErrorHandler::Full(
+ Catch::new(
+ Some(Identifier::from(interner.get_or_intern_static("e", utf16!("e"))).into()),
+ Block::default(),
+ ),
+ Finally::from(Block::default()),
+ ),
))
.into()],
interner,
@@ -119,8 +116,7 @@ fn check_inline_with_empty_try_finally() {
"try {} finally {}",
vec![Statement::Try(Try::new(
Block::default(),
- None,
- Some(Finally::from(Block::default())),
+ ErrorHandler::Finally(Finally::from(Block::default())),
))
.into()],
Interner::default(),
@@ -134,8 +130,7 @@ fn check_inline_with_empty_try_var_decl_in_finally() {
"try {} finally { var x = 1; }",
vec![Statement::Try(Try::new(
Block::default(),
- None,
- Some(Finally::from(Block::from(vec![
+ ErrorHandler::Finally(Finally::from(Block::from(vec![
StatementListItem::Statement(Statement::Var(VarDeclaration(
vec![Variable::from_identifier(
interner.get_or_intern_static("x", utf16!("x")).into(),
@@ -158,7 +153,7 @@ fn check_inline_empty_try_paramless_catch() {
"try {} catch { var x = 1; }",
vec![Statement::Try(Try::new(
Block::default(),
- Some(Catch::new(
+ ErrorHandler::Catch(Catch::new(
None,
vec![Statement::Var(VarDeclaration(
vec![Variable::from_identifier(
@@ -171,7 +166,6 @@ fn check_inline_empty_try_paramless_catch() {
.into()]
.into(),
)),
- None,
))
.into()],
interner,
@@ -186,7 +180,7 @@ fn check_inline_with_binding_pattern_object() {
"try {} catch ({ a, b: c }) {}",
vec![Statement::Try(Try::new(
Block::default(),
- Some(Catch::new(
+ ErrorHandler::Catch(Catch::new(
Some(
Pattern::from(vec![
ObjectPatternElement::SingleName {
@@ -206,7 +200,6 @@ fn check_inline_with_binding_pattern_object() {
),
Block::default(),
)),
- None,
))
.into()],
interner,
@@ -220,7 +213,7 @@ fn check_inline_with_binding_pattern_array() {
"try {} catch ([a, b]) {}",
vec![Statement::Try(Try::new(
Block::from(vec![]),
- Some(Catch::new(
+ ErrorHandler::Catch(Catch::new(
Some(
Pattern::from(vec![
ArrayPatternElement::SingleName {
@@ -236,7 +229,6 @@ fn check_inline_with_binding_pattern_array() {
),
Block::default(),
)),
- None,
))
.into()],
interner,
@@ -250,7 +242,7 @@ fn check_catch_with_var_redeclaration() {
"try {} catch(e) { var e = 'oh' }",
vec![Statement::Try(Try::new(
Block::from(vec![]),
- Some(Catch::new(
+ ErrorHandler::Catch(Catch::new(
Some(Identifier::new(interner.get_or_intern_static("e", utf16!("e"))).into()),
vec![Statement::Var(VarDeclaration(
vec![Variable::from_identifier(
@@ -265,7 +257,6 @@ fn check_catch_with_var_redeclaration() {
.into()]
.into(),
)),
- None,
))
.into()],
interner,
diff --git a/boa_engine/src/syntax/parser/statement/variable/mod.rs b/boa_engine/src/syntax/parser/statement/variable/mod.rs
index 728975e5e53..a243b106d85 100644
--- a/boa_engine/src/syntax/parser/statement/variable/mod.rs
+++ b/boa_engine/src/syntax/parser/statement/variable/mod.rs
@@ -1,20 +1,18 @@
//! Variable statement parsing.
use crate::syntax::{
- ast::{
- declaration::{VarDeclaration, Variable},
- Keyword, Punctuator, Statement,
- },
lexer::TokenKind,
parser::{
- cursor::Cursor, expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn,
- AllowYield, ParseError, TokenParser,
- },
- parser::{
- statement::{ArrayBindingPattern, ObjectBindingPattern},
- ParseResult,
+ cursor::Cursor,
+ expression::Initializer,
+ statement::{ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern},
+ AllowAwait, AllowIn, AllowYield, ParseError, ParseResult, TokenParser,
},
};
+use boa_ast::{
+ declaration::{VarDeclaration, Variable},
+ Keyword, Punctuator, Statement,
+};
use boa_interner::Interner;
use boa_profiler::Profiler;
use std::{convert::TryInto, io::Read};
diff --git a/boa_engine/src/syntax/parser/tests/format/declaration.rs b/boa_engine/src/syntax/parser/tests/format/declaration.rs
new file mode 100644
index 00000000000..cf951f4dc4e
--- /dev/null
+++ b/boa_engine/src/syntax/parser/tests/format/declaration.rs
@@ -0,0 +1,42 @@
+use crate::syntax::parser::tests::format::test_formatting;
+
+#[test]
+fn binding_pattern() {
+ test_formatting(
+ r#"
+ var { } = {
+ o: "1",
+ };
+ var { o_v1 } = {
+ o_v1: "1",
+ };
+ var { o_v2 = "1" } = {
+ o_v2: "2",
+ };
+ var { a : o_v3 = "1" } = {
+ a: "2",
+ };
+ var { ... o_rest_v1 } = {
+ a: "2",
+ };
+ var { o_v4, o_v5, o_v6 = "1", a : o_v7 = "1", ... o_rest_v2 } = {
+ o_v4: "1",
+ o_v5: "1",
+ };
+ var [] = [];
+ var [ , ] = [];
+ var [ a_v1 ] = [1, 2, 3];
+ var [ a_v2, a_v3 ] = [1, 2, 3];
+ var [ a_v2, , a_v3 ] = [1, 2, 3];
+ var [ ... a_rest_v1 ] = [1, 2, 3];
+ var [ a_v4, , ... a_rest_v2 ] = [1, 2, 3];
+ var [ { a_v5 } ] = [{
+ a_v5: 1,
+ }, {
+ a_v5: 2,
+ }, {
+ a_v5: 3,
+ }];
+ "#,
+ );
+}
diff --git a/boa_engine/src/syntax/parser/tests/format/expression.rs b/boa_engine/src/syntax/parser/tests/format/expression.rs
new file mode 100644
index 00000000000..da20dec2e99
--- /dev/null
+++ b/boa_engine/src/syntax/parser/tests/format/expression.rs
@@ -0,0 +1,137 @@
+use crate::syntax::parser::tests::format::test_formatting;
+
+#[test]
+fn new() {
+ test_formatting(
+ r#"
+ function MyClass() {}
+ let inst = new MyClass();
+ "#,
+ );
+}
+
+#[test]
+fn call() {
+ test_formatting(
+ r#"
+ call_1(1, 2, 3);
+ call_2("argument here");
+ call_3();
+ "#,
+ );
+}
+
+#[test]
+fn assign() {
+ test_formatting(
+ r#"
+ let a = 20;
+ a += 10;
+ a -= 10;
+ a *= 10;
+ a **= 10;
+ a /= 10;
+ a %= 10;
+ a &= 10;
+ a |= 10;
+ a ^= 10;
+ a <<= 10;
+ a >>= 10;
+ a >>>= 10;
+ a &&= 10;
+ a ||= 10;
+ a ??= 10;
+ a;
+ "#,
+ );
+}
+
+#[test]
+fn spread() {
+ test_formatting(
+ r#"
+ function f(m) {
+ return m;
+ }
+ function g(...args) {
+ return f(...args);
+ }
+ let a = g("message");
+ a;
+ "#,
+ );
+}
+
+#[test]
+fn r#await() {
+ // TODO: `let a = await fn()` is invalid syntax as of writing. It should be tested here once implemented.
+ test_formatting(
+ r#"
+ async function f() {
+ await function_call();
+ }
+ "#,
+ );
+}
+
+#[test]
+fn array() {
+ test_formatting(
+ r#"
+ let a = [1, 2, 3, "words", "more words"];
+ let b = [];
+ "#,
+ );
+}
+
+#[test]
+fn template() {
+ test_formatting(
+ r#"
+ function tag(t, ...args) {
+ let a = [];
+ a = a.concat([t[0], t[1], t[2]]);
+ a = a.concat([t.raw[0], t.raw[1], t.raw[2]]);
+ a = a.concat([args[0], args[1]]);
+ return a;
+ }
+ let a = 10;
+ tag`result: ${a} \x26 ${a + 10}`;
+ "#,
+ );
+}
+
+#[test]
+fn object() {
+ test_formatting(
+ r#"
+ let other = {
+ c: 10,
+ };
+ let inst = {
+ val: 5,
+ b: "hello world",
+ nested: {
+ a: 5,
+ b: 6,
+ },
+ ...other,
+ say_hi: function() {
+ console.log("hello!");
+ },
+ get a() {
+ return this.val + 1;
+ },
+ set a(new_value) {
+ this.val = new_value;
+ },
+ say_hello(msg) {
+ console.log("hello " + msg);
+ },
+ };
+ inst.a = 20;
+ inst.a;
+ inst.say_hello("humans");
+ "#,
+ );
+}
diff --git a/boa_engine/src/syntax/parser/tests/format/function/class.rs b/boa_engine/src/syntax/parser/tests/format/function/class.rs
new file mode 100644
index 00000000000..4563105a316
--- /dev/null
+++ b/boa_engine/src/syntax/parser/tests/format/function/class.rs
@@ -0,0 +1,106 @@
+use crate::syntax::parser::tests::format::test_formatting;
+
+#[test]
+fn class_declaration_empty() {
+ test_formatting(
+ r#"
+ class A {}
+ "#,
+ );
+}
+
+#[test]
+fn class_declaration_empty_extends() {
+ test_formatting(
+ r#"
+ class A extends Object {}
+ "#,
+ );
+}
+
+#[test]
+fn class_declaration_constructor() {
+ test_formatting(
+ r#"
+ class A {
+ constructor(a, b, c) {
+ this.value = a + b + c;
+ }
+ }
+ "#,
+ );
+}
+
+#[test]
+fn class_declaration_elements() {
+ test_formatting(
+ r#"
+ class A {
+ a;
+ b = 1;
+ c() {}
+ d(a, b, c) {
+ return a + b + c;
+ }
+ set e(value) {}
+ get e() {}
+ set(a, b) {}
+ get(a, b) {}
+ }
+ "#,
+ );
+}
+
+#[test]
+fn class_declaration_elements_private() {
+ test_formatting(
+ r#"
+ class A {
+ #a;
+ #b = 1;
+ #c() {}
+ #d(a, b, c) {
+ return a + b + c;
+ }
+ set #e(value) {}
+ get #e() {}
+ }
+ "#,
+ );
+}
+
+#[test]
+fn class_declaration_elements_static() {
+ test_formatting(
+ r#"
+ class A {
+ static a;
+ static b = 1;
+ static c() {}
+ static d(a, b, c) {
+ return a + b + c;
+ }
+ static set e(value) {}
+ static get e() {}
+ }
+ "#,
+ );
+}
+
+#[test]
+fn class_declaration_elements_private_static() {
+ test_formatting(
+ r#"
+ class A {
+ static #a;
+ static #b = 1;
+ static #c() {}
+ static #d(a, b, c) {
+ return a + b + c;
+ }
+ static set #e(value) {}
+ static get #e() {}
+ }
+ "#,
+ );
+}
diff --git a/boa_engine/src/syntax/parser/tests/format/function/mod.rs b/boa_engine/src/syntax/parser/tests/format/function/mod.rs
new file mode 100644
index 00000000000..4c8417a862e
--- /dev/null
+++ b/boa_engine/src/syntax/parser/tests/format/function/mod.rs
@@ -0,0 +1,48 @@
+use crate::syntax::parser::tests::format::test_formatting;
+
+mod class;
+
+#[test]
+fn function() {
+ test_formatting(
+ r#"
+ function func(a, b) {
+ console.log(a);
+ }
+ function func_2(a, b) {}
+ pass_func(function(a, b) {
+ console.log("in callback", a);
+ });
+ pass_func(function(a, b) {});
+ "#,
+ );
+}
+
+#[test]
+fn arrow() {
+ test_formatting(
+ r#"
+ let arrow_func = (a, b) => {
+ console.log("in multi statement arrow");
+ console.log(b);
+ };
+ let arrow_func_2 = (a, b) => {};
+ "#,
+ );
+}
+
+#[test]
+fn r#async() {
+ test_formatting(
+ r#"
+ async function async_func(a, b) {
+ console.log(a);
+ }
+ async function async_func_2(a, b) {}
+ pass_async_func(async function(a, b) {
+ console.log("in async callback", a);
+ });
+ pass_async_func(async function(a, b) {});
+ "#,
+ );
+}
diff --git a/boa_engine/src/syntax/parser/tests/format/mod.rs b/boa_engine/src/syntax/parser/tests/format/mod.rs
new file mode 100644
index 00000000000..7df0dc9e159
--- /dev/null
+++ b/boa_engine/src/syntax/parser/tests/format/mod.rs
@@ -0,0 +1,47 @@
+mod declaration;
+mod expression;
+mod function;
+mod statement;
+
+/// This parses the given source code, and then makes sure that
+/// the resulting `StatementList` is formatted in the same manner
+/// as the source code. This is expected to have a preceding
+/// newline.
+///
+/// This is a utility function for tests. It was made in case people
+/// are using different indents in their source files. This fixes
+/// any strings which may have been changed in a different indent
+/// level.
+#[cfg(test)]
+fn test_formatting(source: &'static str) {
+ // Remove preceding newline.
+
+ use boa_interner::ToInternedString;
+
+ use crate::{syntax::Parser, Context};
+ let source = &source[1..];
+
+ // Find out how much the code is indented
+ let first_line = &source[..source.find('\n').unwrap()];
+ let trimmed_first_line = first_line.trim();
+ let characters_to_remove = first_line.len() - trimmed_first_line.len();
+
+ let scenario = source
+ .lines()
+ .map(|l| &l[characters_to_remove..]) // Remove preceding whitespace from each line
+ .collect::>()
+ .join("\n");
+ let mut context = Context::default();
+ let result = Parser::new(scenario.as_bytes())
+ .parse_all(&mut context)
+ .expect("parsing failed")
+ .to_interned_string(context.interner());
+ if scenario != result {
+ eprint!("========= Expected:\n{scenario}");
+ eprint!("========= Got:\n{result}");
+ // Might be helpful to find differing whitespace
+ eprintln!("========= Expected: {scenario:?}");
+ eprintln!("========= Got: {result:?}");
+ panic!("parsing test did not give the correct result (see above)");
+ }
+}
diff --git a/boa_engine/src/syntax/parser/tests/format/statement.rs b/boa_engine/src/syntax/parser/tests/format/statement.rs
new file mode 100644
index 00000000000..ca6794db51d
--- /dev/null
+++ b/boa_engine/src/syntax/parser/tests/format/statement.rs
@@ -0,0 +1,124 @@
+use crate::syntax::parser::tests::format::test_formatting;
+
+#[test]
+fn block() {
+ test_formatting(
+ r#"
+ {
+ let a = function_call();
+ console.log("hello");
+ }
+ another_statement();
+ "#,
+ );
+ // TODO: Once block labels are implemtned, this should be tested:
+ // super::super::test_formatting(
+ // r#"
+ // block_name: {
+ // let a = function_call();
+ // console.log("hello");
+ // }
+ // another_statement();
+ // "#,
+ // );
+}
+
+#[test]
+fn r#if() {
+ test_formatting(
+ r#"
+ let a = true ? 5 : 6;
+ if (false) {
+ a = 10;
+ } else {
+ a = 20;
+ }
+ "#,
+ );
+}
+
+#[test]
+fn r#return() {
+ test_formatting(
+ r#"
+ function say_hello(msg) {
+ if (msg === "") {
+ return 0;
+ }
+ console.log("hello " + msg);
+ return;
+ }
+ say_hello("");
+ say_hello("world");
+ "#,
+ );
+}
+
+#[test]
+fn throw() {
+ test_formatting(
+ r#"
+ try {
+ throw "hello";
+ } catch(e) {
+ console.log(e);
+ }
+ "#,
+ );
+}
+
+#[test]
+fn r#try() {
+ test_formatting(
+ r#"
+ try {
+ throw "hello";
+ } catch(e) {
+ console.log(e);
+ } finally {
+ console.log("things");
+ }
+ try {
+ throw "hello";
+ } catch {
+ console.log("something went wrong");
+ }
+ "#,
+ );
+}
+
+#[test]
+fn switch() {
+ test_formatting(
+ r#"
+ let a = 3;
+ let b = "unknown";
+ switch (a) {
+ case 0:
+ b = "Mon";
+ break;
+ case 1:
+ b = "Tue";
+ break;
+ case 2:
+ b = "Wed";
+ break;
+ case 3:
+ b = "Thurs";
+ break;
+ case 4:
+ b = "Fri";
+ break;
+ case 5:
+ b = "Sat";
+ break;
+ case 6:
+ b = "Sun";
+ break;
+ default:
+ b = "Unknown";
+ }
+ b;
+ "#,
+ );
+}
diff --git a/boa_engine/src/syntax/parser/tests.rs b/boa_engine/src/syntax/parser/tests/mod.rs
similarity index 92%
rename from boa_engine/src/syntax/parser/tests.rs
rename to boa_engine/src/syntax/parser/tests/mod.rs
index 4d6aa24b654..12b9c35518d 100644
--- a/boa_engine/src/syntax/parser/tests.rs
+++ b/boa_engine/src/syntax/parser/tests/mod.rs
@@ -1,32 +1,30 @@
//! Tests for the parser.
+mod format;
+
use std::convert::TryInto;
use super::Parser;
-use crate::{
- context::ContextBuilder,
- string::utf16,
- syntax::ast::{
- declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable},
- expression::{
- access::SimplePropertyAccess,
- literal::{Literal, ObjectLiteral},
- operator::{
- assign::AssignOp,
- binary::{ArithmeticOp, BinaryOp, LogicalOp, RelationalOp},
- unary::UnaryOp,
- Assign, Binary, Unary,
- },
- Call, Identifier, New,
- },
- function::{
- ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function,
+use crate::{context::ContextBuilder, string::utf16, Context};
+use boa_ast::{
+ declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable},
+ expression::{
+ access::SimplePropertyAccess,
+ literal::{Literal, ObjectLiteral},
+ operator::{
+ assign::AssignOp,
+ binary::{ArithmeticOp, BinaryOp, LogicalOp, RelationalOp},
+ unary::UnaryOp,
+ Assign, Binary, Unary,
},
- property::PropertyDefinition,
- statement::{If, Return},
- Expression, Statement, StatementList, StatementListItem,
+ Call, Identifier, New,
+ },
+ function::{
+ ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, Function,
},
- Context,
+ property::PropertyDefinition,
+ statement::{If, Return},
+ Expression, Statement, StatementList, StatementListItem,
};
use boa_interner::Interner;
@@ -485,19 +483,17 @@ fn spread_in_arrow_function() {
let mut interner = Interner::default();
let b = interner.get_or_intern_static("b", utf16!("b"));
+ let params = FormalParameterList::from(FormalParameter::new(
+ Variable::from_identifier(b.into(), None),
+ true,
+ ));
+ assert_eq!(params.flags(), FormalParameterListFlags::HAS_REST_PARAMETER);
+ assert_eq!(params.length(), 0);
check_parser(
s,
vec![Statement::Expression(Expression::from(ArrowFunction::new(
None,
- FormalParameterList {
- parameters: Box::new([FormalParameter::new(
- Variable::from_identifier(b.into(), None),
- true,
- )]),
- flags: FormalParameterListFlags::empty()
- .union(FormalParameterListFlags::HAS_REST_PARAMETER),
- length: 0,
- },
+ params,
vec![Statement::Expression(Expression::from(Identifier::from(b))).into()].into(),
)))
.into()],
diff --git a/boa_engine/src/tests.rs b/boa_engine/src/tests.rs
index a0bc3295bf5..4a0cda6adac 100644
--- a/boa_engine/src/tests.rs
+++ b/boa_engine/src/tests.rs
@@ -1615,6 +1615,123 @@ fn test_strict_mode_dup_func_parameters() {
)]);
}
+#[test]
+fn strict_mode_global() {
+ let scenario = r#"
+ 'use strict';
+ let throws = false;
+ try {
+ delete Boolean.prototype;
+ } catch (e) {
+ throws = true;
+ }
+ throws
+ "#;
+
+ check_output(&[TestAction::TestEq(scenario, "true")]);
+}
+
+#[test]
+fn strict_mode_function() {
+ let scenario = r#"
+ let throws = false;
+ function t() {
+ 'use strict';
+ try {
+ delete Boolean.prototype;
+ } catch (e) {
+ throws = true;
+ }
+ }
+ t()
+ throws
+ "#;
+
+ check_output(&[TestAction::TestEq(scenario, "true")]);
+}
+
+#[test]
+fn strict_mode_function_after() {
+ let scenario = r#"
+ function t() {
+ 'use strict';
+ }
+ t()
+ let throws = false;
+ try {
+ delete Boolean.prototype;
+ } catch (e) {
+ throws = true;
+ }
+ throws
+ "#;
+
+ check_output(&[TestAction::TestEq(scenario, "false")]);
+}
+
+#[test]
+fn strict_mode_global_active_in_function() {
+ let scenario = r#"
+ 'use strict'
+ let throws = false;
+ function a(){
+ try {
+ delete Boolean.prototype;
+ } catch (e) {
+ throws = true;
+ }
+ }
+ a();
+ throws
+ "#;
+
+ check_output(&[TestAction::TestEq(scenario, "true")]);
+}
+
+#[test]
+fn strict_mode_function_in_function() {
+ let scenario = r#"
+ let throws = false;
+ function a(){
+ try {
+ delete Boolean.prototype;
+ } catch (e) {
+ throws = true;
+ }
+ }
+ function b(){
+ 'use strict';
+ a();
+ }
+ b();
+ throws
+ "#;
+
+ check_output(&[TestAction::TestEq(scenario, "false")]);
+}
+
+#[test]
+fn strict_mode_function_return() {
+ let scenario = r#"
+ let throws = false;
+ function a() {
+ 'use strict';
+
+ return function () {
+ try {
+ delete Boolean.prototype;
+ } catch (e) {
+ throws = true;
+ }
+ }
+ }
+ a()();
+ throws
+ "#;
+
+ check_output(&[TestAction::TestEq(scenario, "true")]);
+}
+
#[test]
fn test_empty_statement() {
let src = r#"
@@ -1643,3 +1760,1077 @@ fn test_labelled_block() {
"#;
assert_eq!(&exec(src), "true");
}
+
+#[test]
+fn simple_try() {
+ let scenario = r#"
+ let a = 10;
+ try {
+ a = 20;
+ } catch {
+ a = 30;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "20");
+}
+
+#[test]
+fn finally() {
+ let scenario = r#"
+ let a = 10;
+ try {
+ a = 20;
+ } finally {
+ a = 30;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "30");
+}
+
+#[test]
+fn catch_finally() {
+ let scenario = r#"
+ let a = 10;
+ try {
+ a = 20;
+ } catch {
+ a = 40;
+ } finally {
+ a = 30;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "30");
+}
+
+#[test]
+fn catch() {
+ let scenario = r#"
+ let a = 10;
+ try {
+ throw "error";
+ } catch {
+ a = 20;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "20");
+}
+
+#[test]
+fn catch_binding() {
+ let scenario = r#"
+ let a = 10;
+ try {
+ throw 20;
+ } catch(err) {
+ a = err;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "20");
+}
+
+#[test]
+fn catch_binding_pattern_object() {
+ let scenario = r#"
+ let a = 10;
+ try {
+ throw {
+ n: 30,
+ };
+ } catch ({ n }) {
+ a = n;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "30");
+}
+
+#[test]
+fn catch_binding_pattern_array() {
+ let scenario = r#"
+ let a = 10;
+ try {
+ throw [20, 30];
+ } catch ([, n]) {
+ a = n;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "30");
+}
+
+#[test]
+fn catch_binding_finally() {
+ let scenario = r#"
+ let a = 10;
+ try {
+ throw 20;
+ } catch(err) {
+ a = err;
+ } finally {
+ a = 30;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "30");
+}
+
+#[test]
+fn single_case_switch() {
+ let scenario = r#"
+ let a = 10;
+ switch (a) {
+ case 10:
+ a = 20;
+ break;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "20");
+}
+
+#[test]
+fn no_cases_switch() {
+ let scenario = r#"
+ let a = 10;
+ switch (a) {
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "10");
+}
+
+#[test]
+fn no_true_case_switch() {
+ let scenario = r#"
+ let a = 10;
+ switch (a) {
+ case 5:
+ a = 15;
+ break;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "10");
+}
+
+#[test]
+fn two_case_switch() {
+ let scenario = r#"
+ let a = 10;
+ switch (a) {
+ case 5:
+ a = 15;
+ break;
+ case 10:
+ a = 20;
+ break;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "20");
+}
+
+#[test]
+fn two_case_no_break_switch() {
+ let scenario = r#"
+ let a = 10;
+ let b = 10;
+
+ switch (a) {
+ case 10:
+ a = 150;
+ case 20:
+ b = 150;
+ break;
+ }
+
+ a + b;
+ "#;
+ assert_eq!(&exec(scenario), "300");
+}
+
+#[test]
+fn three_case_partial_fallthrough() {
+ let scenario = r#"
+ let a = 10;
+ let b = 10;
+
+ switch (a) {
+ case 10:
+ a = 150;
+ case 20:
+ b = 150;
+ break;
+ case 15:
+ b = 1000;
+ break;
+ }
+
+ a + b;
+ "#;
+ assert_eq!(&exec(scenario), "300");
+}
+
+#[test]
+fn default_taken_switch() {
+ let scenario = r#"
+ let a = 10;
+
+ switch (a) {
+ case 5:
+ a = 150;
+ break;
+ default:
+ a = 70;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "70");
+}
+
+#[test]
+fn default_not_taken_switch() {
+ let scenario = r#"
+ let a = 5;
+
+ switch (a) {
+ case 5:
+ a = 150;
+ break;
+ default:
+ a = 70;
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "150");
+}
+
+#[test]
+fn string_switch() {
+ let scenario = r#"
+ let a = "hello";
+
+ switch (a) {
+ case "hello":
+ a = "world";
+ break;
+ default:
+ a = "hi";
+ }
+
+ a;
+ "#;
+ assert_eq!(&exec(scenario), "\"world\"");
+}
+
+#[test]
+fn bigger_switch_example() {
+ let expected = [
+ "\"Mon\"",
+ "\"Tue\"",
+ "\"Wed\"",
+ "\"Thurs\"",
+ "\"Fri\"",
+ "\"Sat\"",
+ "\"Sun\"",
+ ];
+
+ for (i, val) in expected.iter().enumerate() {
+ let scenario = format!(
+ r#"
+ let a = {i};
+ let b = "unknown";
+
+ switch (a) {{
+ case 0:
+ b = "Mon";
+ break;
+ case 1:
+ b = "Tue";
+ break;
+ case 2:
+ b = "Wed";
+ break;
+ case 3:
+ b = "Thurs";
+ break;
+ case 4:
+ b = "Fri";
+ break;
+ case 5:
+ b = "Sat";
+ break;
+ case 6:
+ b = "Sun";
+ break;
+ }}
+
+ b;
+
+ "#,
+ );
+
+ assert_eq!(&exec(&scenario), val);
+ }
+}
+
+#[test]
+fn while_loop_late_break() {
+ // Ordering with statement before the break.
+ let scenario = r#"
+ let a = 1;
+ while (a < 5) {
+ a++;
+ if (a == 3) {
+ break;
+ }
+ }
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "3");
+}
+
+#[test]
+fn while_loop_early_break() {
+ // Ordering with statements after the break.
+ let scenario = r#"
+ let a = 1;
+ while (a < 5) {
+ if (a == 3) {
+ break;
+ }
+ a++;
+ }
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "3");
+}
+
+#[test]
+fn for_loop_break() {
+ let scenario = r#"
+ let a = 1;
+ for (; a < 5; a++) {
+ if (a == 3) {
+ break;
+ }
+ }
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "3");
+}
+
+#[test]
+fn for_loop_return() {
+ let scenario = r#"
+ function foo() {
+ for (let a = 1; a < 5; a++) {
+ if (a == 3) {
+ return a;
+ }
+ }
+ }
+
+ foo();
+ "#;
+
+ assert_eq!(&exec(scenario), "3");
+}
+
+#[test]
+fn do_loop_late_break() {
+ // Ordering with statement before the break.
+ let scenario = r#"
+ let a = 1;
+ do {
+ a++;
+ if (a == 3) {
+ break;
+ }
+ } while (a < 5);
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "3");
+}
+
+#[test]
+fn do_loop_early_break() {
+ // Ordering with statements after the break.
+ let scenario = r#"
+ let a = 1;
+ do {
+ if (a == 3) {
+ break;
+ }
+ a++;
+ } while (a < 5);
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "3");
+}
+
+#[test]
+fn break_out_of_inner_loop() {
+ let scenario = r#"
+ var a = 0, b = 0;
+ for (let i = 0; i < 2; i++) {
+ a++;
+ for (let j = 0; j < 10; j++) {
+ b++;
+ if (j == 3)
+ break;
+ }
+ a++;
+ }
+ [a, b]
+ "#;
+ assert_eq!(&exec(scenario), "[ 4, 8 ]");
+}
+
+#[test]
+fn continue_inner_loop() {
+ let scenario = r#"
+ var a = 0, b = 0;
+ for (let i = 0; i < 2; i++) {
+ a++;
+ for (let j = 0; j < 10; j++) {
+ if (j < 3)
+ continue;
+ b++;
+ }
+ a++;
+ }
+ [a, b]
+ "#;
+ assert_eq!(&exec(scenario), "[ 4, 14 ]");
+}
+
+#[test]
+fn for_loop_continue_out_of_switch() {
+ let scenario = r#"
+ var a = 0, b = 0, c = 0;
+ for (let i = 0; i < 3; i++) {
+ a++;
+ switch (i) {
+ case 0:
+ continue;
+ c++;
+ case 1:
+ continue;
+ case 5:
+ c++;
+ }
+ b++;
+ }
+ [a, b, c]
+ "#;
+ assert_eq!(&exec(scenario), "[ 3, 1, 0 ]");
+}
+
+#[test]
+fn while_loop_continue() {
+ let scenario = r#"
+ var i = 0, a = 0, b = 0;
+ while (i < 3) {
+ i++;
+ if (i < 2) {
+ a++;
+ continue;
+ }
+ b++;
+ }
+ [a, b]
+ "#;
+ assert_eq!(&exec(scenario), "[ 1, 2 ]");
+}
+
+#[test]
+fn do_while_loop_continue() {
+ let scenario = r#"
+ var i = 0, a = 0, b = 0;
+ do {
+ i++;
+ if (i < 2) {
+ a++;
+ continue;
+ }
+ b++;
+ } while (i < 3)
+ [a, b]
+ "#;
+ assert_eq!(&exec(scenario), "[ 1, 2 ]");
+}
+
+#[test]
+fn for_of_loop_declaration() {
+ let scenario = r#"
+ var result = 0;
+ for (i of [1, 2, 3]) {
+ result = i;
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(scenario),
+ TestAction::TestEq("result", "3"),
+ TestAction::TestEq("i", "3"),
+ ]);
+}
+
+#[test]
+fn for_of_loop_var() {
+ let scenario = r#"
+ var result = 0;
+ for (var i of [1, 2, 3]) {
+ result = i;
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(scenario),
+ TestAction::TestEq("result", "3"),
+ TestAction::TestEq("i", "3"),
+ ]);
+}
+
+#[test]
+fn for_of_loop_let() {
+ let scenario = r#"
+ var result = 0;
+ for (let i of [1, 2, 3]) {
+ result = i;
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(scenario),
+ TestAction::TestEq("result", "3"),
+ TestAction::TestEq(
+ r#"
+ try {
+ i
+ } catch(e) {
+ e.toString()
+ }
+ "#,
+ "\"ReferenceError: i is not defined\"",
+ ),
+ ]);
+}
+
+#[test]
+fn for_of_loop_const() {
+ let scenario = r#"
+ var result = 0;
+ for (let i of [1, 2, 3]) {
+ result = i;
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(scenario),
+ TestAction::TestEq("result", "3"),
+ TestAction::TestEq(
+ r#"
+ try {
+ i
+ } catch(e) {
+ e.toString()
+ }
+ "#,
+ "\"ReferenceError: i is not defined\"",
+ ),
+ ]);
+}
+
+#[test]
+fn for_of_loop_break() {
+ let scenario = r#"
+ var result = 0;
+ for (var i of [1, 2, 3]) {
+ if (i > 1)
+ break;
+ result = i
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(scenario),
+ TestAction::TestEq("result", "1"),
+ TestAction::TestEq("i", "2"),
+ ]);
+}
+
+#[test]
+fn for_of_loop_continue() {
+ let scenario = r#"
+ var result = 0;
+ for (var i of [1, 2, 3]) {
+ if (i == 3)
+ continue;
+ result = i
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(scenario),
+ TestAction::TestEq("result", "2"),
+ TestAction::TestEq("i", "3"),
+ ]);
+}
+
+#[test]
+fn for_of_loop_return() {
+ let scenario = r#"
+ function foo() {
+ for (i of [1, 2, 3]) {
+ if (i > 1)
+ return i;
+ }
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(scenario),
+ TestAction::TestEq("foo()", "2"),
+ ]);
+}
+
+#[test]
+fn for_loop_break_label() {
+ let scenario = r#"
+ var str = "";
+
+ outer: for (let i = 0; i < 5; i++) {
+ inner: for (let b = 0; b < 5; b++) {
+ if (b === 2) {
+ break outer;
+ }
+ str = str + b;
+ }
+ str = str + i;
+ }
+ str
+ "#;
+ assert_eq!(&exec(scenario), "\"01\"");
+}
+
+#[test]
+fn for_loop_continue_label() {
+ let scenario = r#"
+ var count = 0;
+ label: for (let x = 0; x < 10;) {
+ while (true) {
+ x++;
+ count++;
+ continue label;
+ }
+ }
+ count
+ "#;
+ assert_eq!(&exec(scenario), "10");
+}
+
+#[test]
+fn for_in_declaration() {
+ let init = r#"
+ let result = [];
+ let obj = { a: "a", b: 2};
+ var i;
+ for (i in obj) {
+ result = result.concat([i]);
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(init),
+ TestAction::TestEq(
+ "result.length === 2 && result.includes('a') && result.includes('b')",
+ "true",
+ ),
+ ]);
+}
+
+#[test]
+fn for_in_var_object() {
+ let init = r#"
+ let result = [];
+ let obj = { a: "a", b: 2};
+ for (var i in obj) {
+ result = result.concat([i]);
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(init),
+ TestAction::TestEq(
+ "result.length === 2 && result.includes('a') && result.includes('b')",
+ "true",
+ ),
+ ]);
+}
+
+#[test]
+fn for_in_var_array() {
+ let init = r#"
+ let result = [];
+ let arr = ["a", "b"];
+ for (var i in arr) {
+ result = result.concat([i]);
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(init),
+ TestAction::TestEq(
+ "result.length === 2 && result.includes('0') && result.includes('1')",
+ "true",
+ ),
+ ]);
+}
+
+#[test]
+fn for_in_let_object() {
+ let init = r#"
+ let result = [];
+ let obj = { a: "a", b: 2};
+ for (let i in obj) {
+ result = result.concat([i]);
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(init),
+ TestAction::TestEq(
+ "result.length === 2 && result.includes('a') && result.includes('b')",
+ "true",
+ ),
+ ]);
+}
+
+#[test]
+fn for_in_const_array() {
+ let init = r#"
+ let result = [];
+ let arr = ["a", "b"];
+ for (const i in arr) {
+ result = result.concat([i]);
+ }
+ "#;
+ check_output(&[
+ TestAction::Execute(init),
+ TestAction::TestEq(
+ "result.length === 2 && result.includes('0') && result.includes('1')",
+ "true",
+ ),
+ ]);
+}
+
+#[test]
+fn for_in_break_label() {
+ let scenario = r#"
+ var str = "";
+
+ outer: for (let i in [1, 2]) {
+ inner: for (let b in [2, 3, 4]) {
+ if (b === "1") {
+ break outer;
+ }
+ str = str + b;
+ }
+ str = str + i;
+ }
+ str
+ "#;
+ assert_eq!(&exec(scenario), "\"0\"");
+}
+
+#[test]
+fn for_in_continue_label() {
+ let scenario = r#"
+ var str = "";
+
+ outer: for (let i in [1, 2]) {
+ inner: for (let b in [2, 3, 4]) {
+ if (b === "1") {
+ continue outer;
+ }
+ str = str + b;
+ }
+ str = str + i;
+ }
+ str
+ "#;
+ assert_eq!(&exec(scenario), "\"00\"");
+}
+
+#[test]
+fn tagged_template() {
+ let scenario = r#"
+ function tag(t, ...args) {
+ let a = []
+ a = a.concat([t[0], t[1], t[2]]);
+ a = a.concat([t.raw[0], t.raw[1], t.raw[2]]);
+ a = a.concat([args[0], args[1]]);
+ return a
+ }
+ let a = 10;
+ tag`result: ${a} \x26 ${a+10}`;
+ "#;
+
+ assert_eq!(
+ &exec(scenario),
+ r#"[ "result: ", " & ", "", "result: ", " \x26 ", "", 10, 20 ]"#
+ );
+}
+
+#[test]
+fn spread_shallow_clone() {
+ let scenario = r#"
+ var a = { x: {} };
+ var aClone = { ...a };
+
+ a.x === aClone.x
+ "#;
+ assert_eq!(&exec(scenario), "true");
+}
+
+#[test]
+fn spread_merge() {
+ let scenario = r#"
+ var a = { x: 1, y: 2 };
+ var b = { x: -1, z: -3, ...a };
+
+ (b.x === 1) && (b.y === 2) && (b.z === -3)
+ "#;
+ assert_eq!(&exec(scenario), "true");
+}
+
+#[test]
+fn spread_overriding_properties() {
+ let scenario = r#"
+ var a = { x: 0, y: 0 };
+ var aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
+
+ (aWithOverrides.x === 1) && (aWithOverrides.y === 2)
+ "#;
+ assert_eq!(&exec(scenario), "true");
+}
+
+#[test]
+fn spread_getters_in_initializer() {
+ let scenario = r#"
+ var a = { x: 42 };
+ var aWithXGetter = { ...a, get x() { throw new Error('not thrown yet') } };
+ "#;
+ assert_eq!(&exec(scenario), "undefined");
+}
+
+#[test]
+fn spread_getters_in_object() {
+ let scenario = r#"
+ var a = { x: 42 };
+ var aWithXGetter = { ...a, ... { get x() { throw new Error('not thrown yet') } } };
+ "#;
+ assert_eq!(&exec(scenario), "\"Error\": \"not thrown yet\"");
+}
+
+#[test]
+fn spread_setters() {
+ let scenario = r#"
+ var z = { set x(nexX) { throw new Error() }, ... { x: 1 } };
+ "#;
+ assert_eq!(&exec(scenario), "undefined");
+}
+
+#[test]
+fn spread_null_and_undefined_ignored() {
+ let scenario = r#"
+ var a = { ...null, ...undefined };
+ var count = 0;
+
+ for (key in a) { count++; }
+
+ count === 0
+ "#;
+
+ assert_eq!(&exec(scenario), "true");
+}
+
+#[test]
+fn template_literal() {
+ let scenario = r#"
+ let a = 10;
+ `result: ${a} and ${a+10}`;
+ "#;
+
+ assert_eq!(&exec(scenario), "\"result: 10 and 20\"");
+}
+
+#[test]
+fn assignmentoperator_lhs_not_defined() {
+ let scenario = r#"
+ try {
+ a += 5
+ } catch (err) {
+ err.toString()
+ }
+ "#;
+
+ assert_eq!(&exec(scenario), "\"ReferenceError: a is not defined\"");
+}
+
+#[test]
+fn assignmentoperator_rhs_throws_error() {
+ let scenario = r#"
+ try {
+ let a;
+ a += b
+ } catch (err) {
+ err.toString()
+ }
+ "#;
+
+ assert_eq!(&exec(scenario), "\"ReferenceError: b is not defined\"");
+}
+
+#[test]
+fn instanceofoperator_rhs_not_object() {
+ let scenario = r#"
+ try {
+ let s = new String();
+ s instanceof 1
+ } catch (err) {
+ err.toString()
+ }
+ "#;
+
+ assert_eq!(
+ &exec(scenario),
+ "\"TypeError: right-hand side of 'instanceof' should be an object, got number\""
+ );
+}
+
+#[test]
+fn instanceofoperator_rhs_not_callable() {
+ let scenario = r#"
+ try {
+ let s = new String();
+ s instanceof {}
+ } catch (err) {
+ err.toString()
+ }
+ "#;
+
+ assert_eq!(
+ &exec(scenario),
+ "\"TypeError: right-hand side of 'instanceof' is not callable\""
+ );
+}
+
+#[test]
+fn logical_nullish_assignment() {
+ let scenario = r#"
+ let a = undefined;
+ a ??= 10;
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "10");
+
+ let scenario = r#"
+ let a = 20;
+ a ??= 10;
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "20");
+}
+
+#[test]
+fn logical_assignment() {
+ let scenario = r#"
+ let a = false;
+ a &&= 10;
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "false");
+
+ let scenario = r#"
+ let a = 20;
+ a &&= 10;
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "10");
+
+ let scenario = r#"
+ let a = null;
+ a ||= 10;
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "10");
+ let scenario = r#"
+ let a = 20;
+ a ||= 10;
+ a;
+ "#;
+
+ assert_eq!(&exec(scenario), "20");
+}
+
+#[test]
+fn duplicate_function_name() {
+ let scenario = r#"
+ function f () {}
+ function f () {return 12;}
+ f()
+ "#;
+
+ assert_eq!(&exec(scenario), "12");
+}
+
+#[test]
+fn spread_with_new() {
+ let scenario = r#"
+ function F(m) {
+ this.m = m;
+ }
+ function f(...args) {
+ return new F(...args);
+ }
+ let a = f('message');
+ a.m;
+ "#;
+ assert_eq!(&exec(scenario), r#""message""#);
+}
+
+#[test]
+fn spread_with_call() {
+ let scenario = r#"
+ function f(m) {
+ return m;
+ }
+ function g(...args) {
+ return f(...args);
+ }
+ let a = g('message');
+ a;
+ "#;
+ assert_eq!(&exec(scenario), r#""message""#);
+}
diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs
index e411decf850..e83e085bbff 100644
--- a/boa_engine/src/vm/code_block.rs
+++ b/boa_engine/src/vm/code_block.rs
@@ -19,11 +19,11 @@ use crate::{
internal_methods::get_prototype_from_constructor, JsObject, ObjectData, PrivateElement,
},
property::PropertyDescriptor,
- syntax::ast::{expression::Identifier, function::FormalParameterList},
vm::call_frame::GeneratorResumeKind,
vm::{call_frame::FinallyReturn, CallFrame, Opcode},
Context, JsResult, JsString, JsValue,
};
+use boa_ast::{expression::Identifier, function::FormalParameterList};
use boa_gc::{Cell, Finalize, Gc, Trace};
use boa_interner::{Interner, Sym, ToInternedString};
use boa_profiler::Profiler;
@@ -775,11 +775,11 @@ impl JsObject {
let arg_count = args.len();
// Push function arguments to the stack.
- let args = if code.params.parameters.len() > args.len() {
+ let args = if code.params.as_ref().len() > args.len() {
let mut v = args.to_vec();
v.extend(vec![
JsValue::Undefined;
- code.params.parameters.len() - args.len()
+ code.params.as_ref().len() - args.len()
]);
v
} else {
@@ -790,7 +790,7 @@ impl JsObject {
context.vm.push(arg);
}
- let param_count = code.params.parameters.len();
+ let param_count = code.params.as_ref().len();
let has_expressions = code.params.has_expressions();
context.vm.push_frame(CallFrame {
@@ -898,11 +898,11 @@ impl JsObject {
let arg_count = args.len();
// Push function arguments to the stack.
- let args = if code.params.parameters.len() > args.len() {
+ let args = if code.params.as_ref().len() > args.len() {
let mut v = args.to_vec();
v.extend(vec![
JsValue::Undefined;
- code.params.parameters.len() - args.len()
+ code.params.as_ref().len() - args.len()
]);
v
} else {
@@ -913,7 +913,7 @@ impl JsObject {
context.vm.push(arg);
}
- let param_count = code.params.parameters.len();
+ let param_count = code.params.as_ref().len();
let has_expressions = code.params.has_expressions();
context.vm.push_frame(CallFrame {
@@ -1015,11 +1015,11 @@ impl JsObject {
let arg_count = args.len();
// Push function arguments to the stack.
- let mut args = if code.params.parameters.len() > args.len() {
+ let mut args = if code.params.as_ref().len() > args.len() {
let mut v = args.to_vec();
v.extend(vec![
JsValue::Undefined;
- code.params.parameters.len() - args.len()
+ code.params.as_ref().len() - args.len()
]);
v
} else {
@@ -1027,7 +1027,7 @@ impl JsObject {
};
args.reverse();
- let param_count = code.params.parameters.len();
+ let param_count = code.params.as_ref().len();
let call_frame = CallFrame {
code,
@@ -1150,11 +1150,11 @@ impl JsObject {
let arg_count = args.len();
// Push function arguments to the stack.
- let mut args = if code.params.parameters.len() > args.len() {
+ let mut args = if code.params.as_ref().len() > args.len() {
let mut v = args.to_vec();
v.extend(vec![
JsValue::Undefined;
- code.params.parameters.len() - args.len()
+ code.params.as_ref().len() - args.len()
]);
v
} else {
@@ -1162,7 +1162,7 @@ impl JsObject {
};
args.reverse();
- let param_count = code.params.parameters.len();
+ let param_count = code.params.as_ref().len();
let call_frame = CallFrame {
code,
@@ -1368,7 +1368,7 @@ impl JsObject {
let mut is_simple_parameter_list = true;
let mut has_parameter_expressions = false;
- for param in code.params.parameters.iter() {
+ for param in code.params.as_ref().iter() {
has_parameter_expressions = has_parameter_expressions || param.init().is_some();
arguments_in_parameter_names = arguments_in_parameter_names
|| param.names().contains(&Sym::ARGUMENTS.into());
@@ -1401,11 +1401,11 @@ impl JsObject {
let arg_count = args.len();
// Push function arguments to the stack.
- let args = if code.params.parameters.len() > args.len() {
+ let args = if code.params.as_ref().len() > args.len() {
let mut v = args.to_vec();
v.extend(vec![
JsValue::Undefined;
- code.params.parameters.len() - args.len()
+ code.params.as_ref().len() - args.len()
]);
v
} else {
@@ -1416,7 +1416,7 @@ impl JsObject {
context.vm.push(arg);
}
- let param_count = code.params.parameters.len();
+ let param_count = code.params.as_ref().len();
context.vm.push_frame(CallFrame {
code,
diff --git a/boa_examples/Cargo.toml b/boa_examples/Cargo.toml
index daf754f80bb..931c89374e4 100644
--- a/boa_examples/Cargo.toml
+++ b/boa_examples/Cargo.toml
@@ -13,6 +13,7 @@ rust-version.workspace = true
[dependencies]
boa_engine = { workspace = true, features = ["console"] }
+boa_ast.workspace = true
boa_interner.workspace = true
boa_gc.workspace = true
gc = "0.4.1"
diff --git a/boa_examples/src/bin/commuter_visitor.rs b/boa_examples/src/bin/commuter_visitor.rs
index eee08b50e4c..c53a1be0fa5 100644
--- a/boa_examples/src/bin/commuter_visitor.rs
+++ b/boa_examples/src/bin/commuter_visitor.rs
@@ -2,17 +2,18 @@
// are used to swap the operands of commutable arithmetic operations. For an example which simply
// inspects the AST without modifying it, see symbol_visitor.rs.
-use boa_engine::syntax::ast::expression::operator::binary::{ArithmeticOp, BinaryOp};
-use boa_engine::syntax::ast::expression::operator::Binary;
-use boa_engine::syntax::ast::visitor::{VisitWith, VisitorMut};
-use boa_engine::syntax::ast::Expression;
-use boa_engine::syntax::Parser;
-use boa_engine::Context;
+use boa_ast::{
+ expression::operator::{
+ binary::{ArithmeticOp, BinaryOp},
+ Binary,
+ },
+ visitor::{VisitWith, VisitorMut},
+ Expression,
+};
+use boa_engine::{syntax::Parser, Context};
use boa_interner::ToInternedString;
use core::ops::ControlFlow;
-use std::convert::Infallible;
-use std::fs::File;
-use std::io::BufReader;
+use std::{convert::Infallible, fs::File, io::BufReader};
/// Visitor which, when applied to a binary expression, will swap the operands. Use in other
/// circumstances is undefined.
@@ -44,21 +45,18 @@ impl<'ast> VisitorMut<'ast> for CommutorVisitor {
type BreakTy = Infallible;
fn visit_binary_mut(&mut self, node: &'ast mut Binary) -> ControlFlow {
- match node.op() {
- BinaryOp::Arithmetic(op) => {
- match op {
- ArithmeticOp::Add | ArithmeticOp::Mul => {
- // set up the exchanger and swap lhs and rhs
- let mut exchanger = OpExchanger::default();
- assert!(matches!(
- exchanger.visit_binary_mut(node),
- ControlFlow::Break(_)
- ));
- }
- _ => {}
+ if let BinaryOp::Arithmetic(op) = node.op() {
+ match op {
+ ArithmeticOp::Add | ArithmeticOp::Mul => {
+ // set up the exchanger and swap lhs and rhs
+ let mut exchanger = OpExchanger::default();
+ assert!(matches!(
+ exchanger.visit_binary_mut(node),
+ ControlFlow::Break(_)
+ ));
}
+ _ => {}
}
- _ => {}
}
// traverse further in; there may nested binary operations
node.visit_with_mut(self)
diff --git a/boa_examples/src/bin/symbol_visitor.rs b/boa_examples/src/bin/symbol_visitor.rs
index d1cd169846d..8c5c898ff21 100644
--- a/boa_examples/src/bin/symbol_visitor.rs
+++ b/boa_examples/src/bin/symbol_visitor.rs
@@ -2,15 +2,11 @@
// AST, namely: finding all the `Sym`s present in a script. See commuter_visitor.rs for an example
// which mutates the AST.
-use boa_engine::syntax::ast::visitor::Visitor;
-use boa_engine::syntax::Parser;
-use boa_engine::Context;
+use boa_ast::visitor::Visitor;
+use boa_engine::{syntax::Parser, Context};
use boa_interner::Sym;
use core::ops::ControlFlow;
-use std::collections::HashSet;
-use std::convert::Infallible;
-use std::fs::File;
-use std::io::BufReader;
+use std::{collections::HashSet, convert::Infallible, fs::File, io::BufReader};
#[derive(Debug, Clone, Default)]
struct SymbolVisitor {