Browse Source

Implemented function expressions (#382)

pull/383/head
Iban Eguia 4 years ago committed by GitHub
parent
commit
9c9c4638e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      boa/src/exec/mod.rs
  2. 84
      boa/src/exec/tests.rs
  3. 292
      boa/src/syntax/ast/node.rs
  4. 2
      boa/src/syntax/parser/expression/primary/function_expression.rs
  5. 2
      boa/src/syntax/parser/expression/primary/object_initializer/mod.rs
  6. 8
      boa/src/syntax/parser/expression/primary/object_initializer/tests.rs

45
boa/src/exec/mod.rs

@ -297,17 +297,42 @@ impl Executor for Interpreter {
val.set_field_slice("length", to_value(args.len()));
// Set the name and assign it in the current environment
if name.is_some() {
self.realm.environment.create_mutable_binding(
name.clone().expect("No name was supplied"),
false,
VariableScope::Function,
);
val.set_field_slice("name", to_value(name.clone()));
self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
self.realm.environment.initialize_binding(name, val.clone());
Ok(val)
}
// <https://tc39.es/ecma262/#sec-createdynamicfunction>
Node::FunctionExpr(ref name, ref args, ref expr) => {
// Todo: Function.prototype doesn't exist yet, so the prototype right now is the Object.prototype
// let proto = &self
// .realm
// .environment
// .get_global_object()
// .expect("Could not get the global object")
// .get_field_slice("Object")
// .get_field_slice("Prototype");
let func = FunctionObject::create_ordinary(
args.clone(), // TODO: args shouldn't need to be a reference it should be passed by value
self.realm.environment.get_current_environment().clone(),
FunctionBody::Ordinary(*expr.clone()),
ThisMode::NonLexical,
);
let mut new_func = Object::function();
new_func.set_call(func);
let val = to_value(new_func);
val.set_field_slice("length", to_value(args.len()));
self.realm.environment.initialize_binding(
name.as_ref().expect("Could not get name as reference"),
val.clone(),
)
if let Some(name) = name {
val.set_field_slice("name", to_value(name.clone()));
}
Ok(val)

84
boa/src/exec/tests.rs

@ -47,7 +47,7 @@ fn object_field_set() {
m['key'] = 22;
m['key']
"#;
assert_eq!(exec(scenario), String::from("22"));
assert_eq!(&exec(scenario), "22");
}
#[test]
@ -98,28 +98,28 @@ fn array_field_set() {
m[1] = 5;
m[1]
"#;
assert_eq!(exec(element_changes), String::from("5"));
assert_eq!(&exec(element_changes), "5");
let length_changes = r#"
let m = [1, 2, 3];
m[10] = 52;
m.length
"#;
assert_eq!(exec(length_changes), String::from("11"));
assert_eq!(&exec(length_changes), "11");
let negative_index_wont_affect_length = r#"
let m = [1, 2, 3];
m[-11] = 5;
m.length
"#;
assert_eq!(exec(negative_index_wont_affect_length), String::from("3"));
assert_eq!(&exec(negative_index_wont_affect_length), "3");
let non_num_key_wont_affect_length = r#"
let m = [1, 2, 3];
m["magic"] = 5;
m.length
"#;
assert_eq!(exec(non_num_key_wont_affect_length), String::from("3"));
assert_eq!(&exec(non_num_key_wont_affect_length), "3");
}
#[test]
@ -128,36 +128,36 @@ fn tilde_operator() {
let f = -1.2;
~f
"#;
assert_eq!(exec(float), String::from("0"));
assert_eq!(&exec(float), "0");
let numeric = r#"
let f = 1789;
~f
"#;
assert_eq!(exec(numeric), String::from("-1790"));
assert_eq!(&exec(numeric), "-1790");
// TODO: enable test after we have NaN
// let nan = r#"
// var m = NaN;
// ~m
// "#;
// assert_eq!(exec(nan), String::from("-1"));
// assert_eq!(&exec(nan), "-1");
let object = r#"
let m = {};
~m
"#;
assert_eq!(exec(object), String::from("-1"));
assert_eq!(&exec(object), "-1");
let boolean_true = r#"
~true
"#;
assert_eq!(exec(boolean_true), String::from("-2"));
assert_eq!(&exec(boolean_true), "-2");
let boolean_false = r#"
~false
"#;
assert_eq!(exec(boolean_false), String::from("-1"));
assert_eq!(&exec(boolean_false), "-1");
}
#[test]
@ -171,7 +171,7 @@ fn early_return() {
}
early_return()
"#;
assert_eq!(exec(early_return), String::from("true"));
assert_eq!(&exec(early_return), "true");
let early_return = r#"
function nested_fnct() {
@ -183,7 +183,7 @@ fn early_return() {
}
outer_fnct()
"#;
assert_eq!(exec(early_return), String::from("outer"));
assert_eq!(&exec(early_return), "outer");
}
#[test]
@ -204,7 +204,7 @@ fn short_circuit_evaluation() {
let _ = add_one(counter) || add_one(counter);
counter.value
"#;
assert_eq!(exec(short_circuit_eval), String::from("1"));
assert_eq!(&exec(short_circuit_eval), "1");
// the second operand must be evaluated if the first one resolve to `false`.
let short_circuit_eval = r#"
@ -216,7 +216,7 @@ fn short_circuit_evaluation() {
let _ = add_one(counter) || add_one(counter);
counter.value
"#;
assert_eq!(exec(short_circuit_eval), String::from("2"));
assert_eq!(&exec(short_circuit_eval), "2");
// AND operation
assert_eq!(exec("true && true"), String::from("true"));
@ -234,7 +234,7 @@ fn short_circuit_evaluation() {
let _ = add_one(counter) && add_one(counter);
counter.value
"#;
assert_eq!(exec(short_circuit_eval), String::from("2"));
assert_eq!(&exec(short_circuit_eval), "2");
// the second operand must NOT be evaluated if the first one resolve to `false`.
let short_circuit_eval = r#"
@ -246,7 +246,7 @@ fn short_circuit_evaluation() {
let _ = add_one(counter) && add_one(counter);
counter.value
"#;
assert_eq!(exec(short_circuit_eval), String::from("1"));
assert_eq!(&exec(short_circuit_eval), "1");
}
#[test]
@ -256,7 +256,7 @@ fn assign_operator_precedence() {
a = a + 1;
a
"#;
assert_eq!(exec(src), String::from("2"));
assert_eq!(&exec(src), "2");
}
#[test]
@ -269,7 +269,7 @@ fn do_while_loop() {
a
"#;
assert_eq!(exec(simple_one), String::from("10"));
assert_eq!(&exec(simple_one), "10");
let multiline_statement = r#"
pow = 0;
@ -280,7 +280,7 @@ fn do_while_loop() {
} while (pow < 8);
b
"#;
assert_eq!(exec(multiline_statement), String::from("256"));
assert_eq!(&exec(multiline_statement), "256");
let body_is_executed_at_least_once = r#"
a = 0;
@ -291,7 +291,7 @@ fn do_while_loop() {
while (false);
a
"#;
assert_eq!(exec(body_is_executed_at_least_once), String::from("1"));
assert_eq!(&exec(body_is_executed_at_least_once), "1");
}
#[test]
@ -300,7 +300,7 @@ fn do_while_post_inc() {
var i = 0;
do {} while(i++ < 10) i;
"#;
assert_eq!(exec(with_post_incrementors), String::from("11"));
assert_eq!(&exec(with_post_incrementors), "11");
}
#[test]
@ -313,7 +313,7 @@ fn test_for_loop() {
}
b
"#;
assert_eq!(exec(simple), String::from("hello"));
assert_eq!(&exec(simple), "hello");
let without_init_and_inc_step = r#"
let a = 0;
@ -325,7 +325,7 @@ fn test_for_loop() {
a
"#;
assert_eq!(exec(without_init_and_inc_step), String::from("45"));
assert_eq!(&exec(without_init_and_inc_step), "45");
let body_should_not_execute_on_false_condition = r#"
let a = 0
@ -345,7 +345,7 @@ fn test_for_loop() {
i
"#;
assert_eq!(exec(inner_scope), String::from("undefined"));
assert_eq!(&exec(inner_scope), "undefined");
}
#[test]
@ -355,40 +355,40 @@ fn unary_pre() {
++a;
a;
"#;
assert_eq!(exec(unary_inc), String::from("6"));
assert_eq!(&exec(unary_inc), "6");
let unary_dec = r#"
let a = 5;
--a;
a;
"#;
assert_eq!(exec(unary_dec), String::from("4"));
assert_eq!(&exec(unary_dec), "4");
let inc_obj_prop = r#"
const a = { b: 5 };
++a.b;
a['b'];
"#;
assert_eq!(exec(inc_obj_prop), String::from("6"));
assert_eq!(&exec(inc_obj_prop), "6");
let inc_obj_field = r#"
const a = { b: 5 };
++a['b'];
a.b;
"#;
assert_eq!(exec(inc_obj_field), String::from("6"));
assert_eq!(&exec(inc_obj_field), "6");
let execs_before_inc = r#"
let a = 5;
++a === 6;
"#;
assert_eq!(exec(execs_before_inc), String::from("true"));
assert_eq!(&exec(execs_before_inc), "true");
let execs_before_dec = r#"
let a = 5;
--a === 4;
"#;
assert_eq!(exec(execs_before_dec), String::from("true"));
assert_eq!(&exec(execs_before_dec), "true");
}
#[test]
@ -398,40 +398,40 @@ fn unary_post() {
a++;
a;
"#;
assert_eq!(exec(unary_inc), String::from("6"));
assert_eq!(&exec(unary_inc), "6");
let unary_dec = r#"
let a = 5;
a--;
a;
"#;
assert_eq!(exec(unary_dec), String::from("4"));
assert_eq!(&exec(unary_dec), "4");
let inc_obj_prop = r#"
const a = { b: 5 };
a.b++;
a['b'];
"#;
assert_eq!(exec(inc_obj_prop), String::from("6"));
assert_eq!(&exec(inc_obj_prop), "6");
let inc_obj_field = r#"
const a = { b: 5 };
a['b']++;
a.b;
"#;
assert_eq!(exec(inc_obj_field), String::from("6"));
assert_eq!(&exec(inc_obj_field), "6");
let execs_after_inc = r#"
let a = 5;
a++ === 5;
"#;
assert_eq!(exec(execs_after_inc), String::from("true"));
assert_eq!(&exec(execs_after_inc), "true");
let execs_after_dec = r#"
let a = 5;
a-- === 5;
"#;
assert_eq!(exec(execs_after_dec), String::from("true"));
assert_eq!(&exec(execs_after_dec), "true");
}
#[cfg(test)]
@ -444,7 +444,7 @@ mod in_operator {
var p = 'a';
p in o
"#;
assert_eq!(exec(p_in_o), String::from("true"));
assert_eq!(&exec(p_in_o), "true");
}
#[test]
@ -454,7 +454,7 @@ mod in_operator {
var p = 'toString';
p in o
"#;
assert_eq!(exec(p_in_o), String::from("true"));
assert_eq!(&exec(p_in_o), "true");
}
#[test]
@ -464,7 +464,7 @@ mod in_operator {
var p = 'b';
p in o
"#;
assert_eq!(exec(p_not_in_o), String::from("false"));
assert_eq!(&exec(p_not_in_o), "false");
}
#[test]
@ -476,7 +476,7 @@ mod in_operator {
var a = ['a'];
n in a
"#;
assert_eq!(exec(num_in_array), String::from("true"));
assert_eq!(&exec(num_in_array), "true");
}
#[test]
@ -490,7 +490,7 @@ mod in_operator {
o[sym] = 'hello';
sym in o
"#;
assert_eq!(exec(sym_in_object), String::from("true"));
assert_eq!(&exec(sym_in_object), "true");
}
#[test]

292
boa/src/syntax/ast/node.rs

@ -14,13 +14,15 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum Node {
/// An array is an ordered collection of data (either primitive or object depending upon the language).
/// 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.
/// 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]
@ -30,10 +32,12 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
ArrayDecl(Box<[Node]>),
/// An arrow function expression is a syntactically compact alternative to a regular function expression.
/// An arrow function expression is a syntactically compact alternative to a regular function
/// expression.
///
/// Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
/// Arrow functions cannot be used as constructors and will throw an error when used with new.
/// Arrow function expressions are ill suited as methods, and they cannot be used as
/// constructors. Arrow functions cannot be used as constructors and will throw an error when
/// used with new.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -43,7 +47,8 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
ArrowFunctionDecl(Box<[FormalParameter]>, Box<Node>),
/// An assignment operator assigns a value to its left operand based on the value of its right operand.
/// An assignment operator assigns a value to its left operand based on the value of its right
/// operand.
///
/// Assignment operator (`=`), assigns the value of its right operand to its left operand.
///
@ -63,12 +68,14 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Operators
BinOp(BinOp, Box<Node>, Box<Node>),
/// A `block` statement (or compound statement in other languages) is used to group zero or more statements.
/// A `block` statement (or compound statement in other languages) is used to group zero or
/// more statements.
///
/// The block statement is often called compound statement in other languages.
/// It allows you to use multiple statements where JavaScript expects only one statement.
/// Combining statements into blocks is a common practice in JavaScript. The opposite behavior is possible using an empty statement,
/// where you provide no statement, although one is required.
/// Combining statements into blocks is a common practice in JavaScript. The opposite behavior
/// is possible using an empty statement, where you provide no statement, although one is
/// required.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -78,11 +85,13 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
Block(Box<[Node]>),
/// The `break` statement terminates the current loop, switch, or label statement and transfers program control to the statement following the terminated statement.
/// The `break` statement terminates the current loop, switch, or label statement and transfers
/// program control to the statement following the terminated statement.
///
/// The break statement includes an optional label that allows the program to break out of a labeled statement.
/// The break statement needs to be nested within the referenced label. The labeled statement can be any block statement;
/// it does not have to be preceded by a loop statement.
/// The break statement includes an optional label that allows the program to break out of a
/// labeled statement. The break statement needs to be nested within the referenced label. The
/// labeled statement can be any block statement; it does not have to be preceded by a loop
/// statement.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -94,9 +103,11 @@ pub enum Node {
/// Calling the function actually performs the specified actions with the indicated parameters.
///
/// Defining a function does not execute it. Defining it simply names the function and specifies what to do when the function is called.
/// Functions must be in scope when they are called, but the function declaration can be hoisted
/// The scope of a function is the function in which it is declared (or the entire program, if it is declared at the top level).
/// Defining a function does not execute it. Defining it simply names the function and
/// specifies what to do when the function is called. Functions must be in scope when they are
/// called, but the function declaration can be hoisted. The scope of a function is the
/// function in which it is declared (or the entire program, if it is declared at the top
/// level).
///
/// More information:
/// - [ECMAScript reference][spec]
@ -106,11 +117,13 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions
Call(Box<Node>, Box<[Node]>),
/// The `conditional` (ternary) operator is the only JavaScript operator that takes three operands.
/// The `conditional` (ternary) operator is the only JavaScript operator that takes three
/// operands.
///
/// This operator is the only JavaScript operator that takes three operands: a condition followed by a question mark (`?`),
/// then an expression to execute `if` the condition is truthy followed by a colon (`:`), and finally the expression to execute if the condition is `falsy`.
/// This operator is frequently used as a shortcut for the `if` statement.
/// This operator is the only JavaScript operator that takes three operands: a condition
/// followed by a question mark (`?`), then an expression to execute `if` the condition is
/// truthy followed by a colon (`:`), and finally the expression to execute if the condition
/// is `false`. This operator is frequently used as a shortcut for the `if` statement.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -132,13 +145,15 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals
Const(Const),
/// The `const` statements are block-scoped, much like variables defined using the `let` keyword.
/// The `const` statements are block-scoped, much like variables defined using the `let`
/// keyword.
///
/// This declaration creates a constant whose scope can be either global or local to the block in which it is declared.
/// Global constants do not become properties of the window object, unlike var variables.
/// This declaration creates a constant whose scope can be either global or local to the block
/// in which it is declared. Global constants do not become properties of the window object,
/// unlike var variables.
///
/// An initializer for a constant is required. You must specify its value in the same statement in which it's declared.
/// (This makes sense, given that it can't be changed later.)
/// An initializer for a constant is required. You must specify its value in the same statement
/// in which it's declared. (This makes sense, given that it can't be changed later.)
///
/// More information:
/// - [ECMAScript reference][spec]
@ -150,11 +165,12 @@ pub enum Node {
/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
ConstDecl(Box<[(String, Node)]>),
/// The `continue` statement terminates execution of the statements in the current iteration of the current or labeled loop,
/// and continues execution of the loop with the next iteration.
/// The `continue` statement terminates execution of the statements in the current iteration of
/// the current or labeled loop, and continues execution of the loop with the next iteration.
///
/// The continue statement can include an optional label that allows the program to jump to the next iteration of a labeled
/// loop statement instead of the current loop. In this case, the continue statement needs to be nested within this labeled statement.
/// The continue statement can include an optional label that allows the program to jump to the
/// next iteration of a labeled loop statement instead of the current loop. In this case, the
/// continue statement needs to be nested within this labeled statement.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -164,9 +180,11 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
Continue(Option<String>),
/// The `do...while` statement creates a loop that executes a specified statement until the test condition evaluates to false.
/// The `do...while` statement creates a loop that executes a specified statement until the
/// test condition evaluates to false.
///
/// The condition is evaluated after executing the statement, resulting in the specified statement executing at least once.
/// The condition is evaluated after executing the statement, resulting in the specified
/// statement executing at least once.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -176,13 +194,16 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
DoWhileLoop(Box<Node>, Box<Node>),
/// The `function` declaration (function statement) defines a function with the specified parameters.
/// The `function` declaration (function statement) defines a function with the specified
/// parameters.
///
/// A function created with a function declaration is a `Function` object and has all the properties, methods and behavior of `Function`.
/// A function created with a function declaration is a `Function` object and has all the
/// properties, methods and behavior of `Function`.
///
/// A function can also be created using an expression (see function expression).
///
/// By default, functions return undefined. To return any other value, the function must have a return statement that specifies the value to return.
/// By default, functions return `undefined`. To return any other value, the function must have
/// a return statement that specifies the value to return.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -190,20 +211,40 @@ pub enum Node {
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
FunctionDecl(Option<String>, Box<[FormalParameter]>, Box<Node>),
FunctionDecl(String, Box<[FormalParameter]>, Box<Node>),
/// This property accessor provides access to an object's properties by using the [dot notation][mdn].
/// The `function` expression defines a function with the specified parameters.
///
/// A function created with a function expression is a `Function` object and has all the
/// properties, methods and behavior of `Function`.
///
/// A function can also be created using a declaration (see function expression).
///
/// By default, functions return `undefined`. To return any other value, the function must have
/// a return statement that specifies the value to return.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
FunctionExpr(Option<String>, Box<[FormalParameter]>, Box<Node>),
/// This property accessor provides access to an object's properties by using the
/// [dot notation][mdn].
///
/// In the object.property syntax, the property must be a valid JavaScript identifier.
/// (In the ECMAScript standard, the names of properties are technically "IdentifierNames", not "Identifiers",
/// so reserved words can be used but are not recommended).
/// (In the ECMAScript standard, the names of properties are technically "IdentifierNames", not
/// "Identifiers", so reserved words can be used but are not recommended).
///
/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup table).
/// The keys in this array are the names of the object's properties.
/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup
/// table). The keys in this array are the names of the object's properties.
///
/// It's typical when speaking of an object's properties to make a distinction between properties and methods. However,
/// the property/method distinction is little more than a convention. A method is simply a property that can be called (for example,
/// if it has a reference to a Function instance as its value).
/// It's typical when speaking of an object's properties to make a distinction between
/// properties and methods. However, the property/method distinction is little more than a
/// convention. A method is simply a property that can be called (for example, if it has a
/// reference to a Function instance as its value).
///
/// More information:
/// - [ECMAScript reference][spec]
@ -213,16 +254,20 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Dot_notation
GetConstField(Box<Node>, String),
/// This property accessor provides access to an object's properties by using the [bracket notation][mdn].
/// This property accessor provides access to an object's properties by using the
/// [bracket notation][mdn].
///
/// In the object[property_name] syntax, the property_name is just a string or [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a space).
/// In the object[property_name] syntax, the property_name is just a string or
/// [Symbol][symbol]. So, it can be any string, including '1foo', '!bar!', or even ' ' (a
/// space).
///
/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup table).
/// The keys in this array are the names of the object's properties.
/// One can think of an object as an associative array (a.k.a. map, dictionary, hash, lookup
/// table). The keys in this array are the names of the object's properties.
///
/// It's typical when speaking of an object's properties to make a distinction between properties and methods. However,
/// the property/method distinction is little more than a convention. A method is simply a property that can be called (for example,
/// if it has a reference to a Function instance as its value).
/// It's typical when speaking of an object's properties to make a distinction between
/// properties and methods. However, the property/method distinction is little more than a
/// convention. A method is simply a property that can be called (for example, if it has a
/// reference to a Function instance as its value).
///
/// More information:
/// - [ECMAScript reference][spec]
@ -251,7 +296,8 @@ pub enum Node {
Box<Node>,
),
/// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If the condition is [`falsy`][falsy], another statement can be executed.
/// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If
/// the condition is [`falsy`][falsy], another statement can be executed.
///
/// Multiple `if...else` statements can be nested to create an else if clause.
///
@ -268,13 +314,16 @@ pub enum Node {
/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
If(Box<Node>, Box<Node>, Option<Box<Node>>),
/// The `let` statement declares a block scope local variable, optionally initializing it to a value.
/// The `let` statement declares a block scope local variable, optionally initializing it to a
/// value.
///
///
/// `let` allows you to declare variables that are limited to a scope of a block statement, or expression on which
/// it is used, unlike the `var` keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
/// `let` allows you to declare variables that are limited to a scope of a block statement, or
/// expression on which it is used, unlike the `var` keyword, which defines a variable
/// globally, or locally to an entire function regardless of block scope.
///
/// Just like const the `let` does not create properties of the window object when declared globally (in the top-most scope).
/// Just like const the `let` does not create properties of the window object when declared
/// globally (in the top-most scope).
///
/// More information:
/// - [ECMAScript reference][spec]
@ -284,12 +333,15 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
LetDecl(Box<[(String, Option<Node>)]>),
/// An `identifier` is a sequence of characters in the code that identifies a variable, function, or property.
/// An `identifier` is a sequence of characters in the code that identifies a variable,
/// function, or property.
///
/// In JavaScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and digits (0-9), but may not start with a digit.
/// In JavaScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and
/// digits (0-9), but may not start with a digit.
///
/// An identifier differs from a string in that a string is data, while an identifier is part of the code. In JavaScript, there is no way
/// to convert identifiers to strings, but sometimes it is possible to parse strings into identifiers.
/// An identifier differs from a string in that a string is data, while an identifier is part
/// of the code. In JavaScript, there is no way to convert identifiers to strings, but
/// sometimes it is possible to parse strings into identifiers.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -299,7 +351,8 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier
Local(String),
/// The `new` operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function.
/// The `new` operator lets developers create an instance of a user-defined object type or of
/// one of the built-in object types that has a constructor function.
///
/// The new keyword does the following things:
/// - Creates a blank, plain JavaScript object;
@ -315,13 +368,16 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
New(Box<Node>),
/// 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 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.
/// 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.
/// 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]
@ -333,15 +389,17 @@ pub enum Node {
/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive
Object(Box<[PropertyDefinition]>),
/// The `return` statement ends function execution and specifies a value to be returned to the function caller.
/// The `return` statement ends function execution and specifies a value to be returned to the
/// function caller.
///
/// Syntax: `return [expression];`
///
/// `expression`:
/// > The expression whose value is to be returned. If omitted, `undefined` is returned instead.
/// > The expression whose value is to be returned. If omitted, `undefined` is returned
/// > nstead.
///
/// When a `return` statement is used in a function body, the execution of the function is stopped.
/// If specified, a given value is returned to the function caller.
/// When a `return` statement is used in a function body, the execution of the function is
/// stopped. If specified, a given value is returned to the function caller.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -351,13 +409,15 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return
Return(Option<Box<Node>>),
/// The `switch` statement evaluates an expression, matching the expression's value to a case clause,
/// and executes statements associated with that case, as well as statements in cases that follow the matching case.
/// The `switch` statement evaluates an expression, matching the expression's value to a case
/// clause, and executes statements associated with that case, as well as statements in cases
/// that follow the matching case.
///
/// A `switch` statement first evaluates its expression. It then looks for the first case clause whose expression evaluates
/// to the same value as the result of the input expression (using the strict comparison, `===`) and transfers control to that clause,
/// executing the associated statements. (If multiple cases match the provided value, the first case that matches is selected, even if
/// the cases are not equal to each other.)
/// A `switch` statement first evaluates its expression. It then looks for the first case
/// clause whose expression evaluates to the same value as the result of the input expression
/// (using the strict comparison, `===`) and transfers control to that clause, executing the
/// associated statements. (If multiple cases match the provided value, the first case that
/// matches is selected, even if the cases are not equal to each other.)
///
/// More information:
/// - [ECMAScript reference][spec]
@ -367,12 +427,15 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
Switch(Box<Node>, Box<[(Node, Box<[Node]>)]>, Option<Box<Node>>),
/// The `spread` operator allows an iterable such as an array expression or string to be expanded.
/// The `spread` operator allows an iterable such as an array expression or string to be
/// expanded.
///
/// Syntax: `...x`
///
/// It expands array expressions or strings in places where zero or more arguments (for function calls) or elements (for array literals)
/// are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.
/// It expands array expressions or strings in places where zero or more arguments (for
/// function calls) or elements (for array literals)
/// are expected, or an object expression to be expanded in places where zero or more key-value
/// pairs (for object literals) are expected.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -395,8 +458,8 @@ pub enum Node {
/// Syntax: `throw expression;`
///
/// Execution of the current function will stop (the statements after throw won't be executed),
/// and control will be passed to the first catch block in the call stack. If no catch block exists among
/// caller functions, the program will terminate.
/// and control will be passed to the first catch block in the call stack. If no catch block
/// exists among caller functions, the program will terminate.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -420,10 +483,12 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
TypeOf(Box<Node>),
/// The `try...catch` statement marks a block of statements to try and specifies a response should an exception be thrown.
/// The `try...catch` statement marks a block of statements to try and specifies a response
/// should an exception be thrown.
///
/// The `try` statement consists of a `try`-block, which contains one or more statements. `{}` must always be used,
/// even for single statements. At least one `catch`-block, or a `finally`-block, must be present.
/// The `try` statement consists of a `try`-block, which contains one or more statements. `{}`
/// must always be used, even for single statements. At least one `catch`-block, or a
/// `finally`-block, must be present.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -464,13 +529,15 @@ pub enum Node {
/// The `var` statement declares a variable, optionally initializing it to a value.
///
/// var declarations, wherever they occur, are processed before any code is executed. This is called hoisting, and is discussed further below.
/// var declarations, wherever they occur, are processed before any code is executed. This is
/// called hoisting, and is discussed further below.
///
/// The scope of a variable declared with var is its current execution context, which is either the enclosing function or,
/// for variables declared outside any function, global. If you re-declare a JavaScript variable, it will not lose its value.
/// The scope of a variable declared with var is its current execution context, which is either
/// the enclosing function or, for variables declared outside any function, global. If you
/// re-declare a JavaScript variable, it will not lose its value.
///
/// Assigning a value to an undeclared variable implicitly creates it as a global variable
/// (it becomes a property of the global object) when the assignment is executed.
/// Assigning a value to an undeclared variable implicitly creates it as a global variable (it
/// becomes a property of the global object) when the assignment is executed.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -480,7 +547,8 @@ pub enum Node {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
VarDecl(Box<[(String, Option<Node>)]>),
/// The `while` statement creates a loop that executes a specified statement as long as the test condition evaluates to `true`.
/// The `while` statement creates a loop that executes a specified statement as long as the
/// test condition evaluates to `true`.
///
/// The condition is evaluated before executing the statement.
///
@ -636,14 +704,24 @@ impl Node {
}
/// Creates a `FunctionDecl` AST node.
pub fn function_decl<ON, N, P, B>(name: ON, params: P, body: B) -> Self
pub fn function_decl<N, P, B>(name: N, params: P, body: B) -> Self
where
N: Into<String>,
P: Into<Box<[FormalParameter]>>,
B: Into<Box<Self>>,
{
Self::FunctionDecl(name.into(), params.into(), body.into())
}
/// Creates a `FunctionDecl` AST node.
pub fn function_expr<ON, N, P, B>(name: ON, params: P, body: B) -> Self
where
N: Into<String>,
ON: Into<Option<N>>,
P: Into<Box<[FormalParameter]>>,
B: Into<Box<Self>>,
{
Self::FunctionDecl(name.into().map(N::into), params.into(), body.into())
Self::FunctionExpr(name.into().map(N::into), params.into(), body.into())
}
/// Creates a `GetConstField` AST node.
@ -990,18 +1068,24 @@ impl Node {
f.write_str("]")
}
Self::FunctionDecl(ref name, ref _args, ref node) => {
write!(f, "function {} {{", name)?;
//join_nodes(f, args)?; TODO: port
f.write_str("} ")?;
node.display(f, indentation + 1)
}
Self::FunctionExpr(ref name, ref args, ref node) => {
write!(f, "function ")?;
if let Some(func_name) = name {
write!(f, "{}", func_name)?;
}
write!(f, "{{")?;
//join_nodes(f, args)?; TODO: port
join_nodes(f, args)?;
f.write_str("} ")?;
node.display(f, indentation + 1)
}
Self::ArrowFunctionDecl(ref _args, ref node) => {
Self::ArrowFunctionDecl(ref args, ref node) => {
write!(f, "(")?;
//join_nodes(f, args)?; TODO: port
join_nodes(f, args)?;
f.write_str(") => ")?;
node.display(f, indentation)
}
@ -1038,7 +1122,10 @@ impl Node {
}
/// Utility to join multiple Nodes into a single string.
fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[Node]) -> fmt::Result {
fn join_nodes<N>(f: &mut fmt::Formatter<'_>, nodes: &[N]) -> fmt::Result
where
N: fmt::Display,
{
let mut first = true;
for e in nodes {
if !first {
@ -1086,6 +1173,19 @@ impl FormalParameter {
}
}
impl fmt::Display for FormalParameter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_rest_param {
write!(f, "...")?;
}
write!(f, "{}", self.name)?;
if let Some(n) = self.init.as_ref() {
write!(f, " = {}", n)?;
}
Ok(())
}
}
/// A JavaScript property is a characteristic of an object, often describing attributes associated with a data structure.
///
/// A property has a name (a string) and a value (primitive, method, or object reference).

2
boa/src/syntax/parser/expression/primary/function_expression.rs

@ -55,6 +55,6 @@ impl TokenParser for FunctionExpression {
cursor.expect(Punctuator::CloseBlock, "function expression")?;
Ok(Node::function_decl::<_, &String, _, _>(name, params, body))
Ok(Node::function_expr::<_, &String, _, _>(name, params, body))
}
}

2
boa/src/syntax/parser/expression/primary/object_initializer/mod.rs

@ -242,7 +242,7 @@ impl TokenParser for MethodDefinition {
Ok(node::PropertyDefinition::MethodDefinition(
methodkind,
prop_name,
Node::function_decl::<_, String, _, _>(None, params, body),
Node::function_expr::<_, String, _, _>(None, params, body),
))
}
}

8
boa/src/syntax/parser/expression/primary/object_initializer/tests.rs

@ -32,7 +32,7 @@ fn check_object_short_function() {
PropertyDefinition::method_definition(
MethodDefinitionKind::Ordinary,
"b",
Node::function_decl::<_, String, _, _>(None, Vec::new(), Node::statement_list(vec![])),
Node::function_expr::<_, String, _, _>(None, Vec::new(), Node::statement_list(vec![])),
),
];
@ -57,7 +57,7 @@ fn check_object_short_function_arguments() {
PropertyDefinition::method_definition(
MethodDefinitionKind::Ordinary,
"b",
Node::FunctionDecl(
Node::FunctionExpr(
None,
Box::new([FormalParameter::new("test", None, false)]),
Box::new(Node::StatementList(Box::new([]))),
@ -85,7 +85,7 @@ fn check_object_getter() {
PropertyDefinition::method_definition(
MethodDefinitionKind::Get,
"b",
Node::FunctionDecl(
Node::FunctionExpr(
None,
Box::new([]),
Box::new(Node::statement_list(Vec::new())),
@ -113,7 +113,7 @@ fn check_object_setter() {
PropertyDefinition::method_definition(
MethodDefinitionKind::Set,
"b",
Node::function_decl::<_, String, _, _>(
Node::function_expr::<_, String, _, _>(
None,
vec![FormalParameter::new("test", None, false)],
Node::statement_list(Vec::new()),

Loading…
Cancel
Save