Skip to content

Commit

Permalink
Added BindingIdentifier parsing. (#389)
Browse files Browse the repository at this point in the history
  • Loading branch information
Razican authored May 10, 2020
1 parent bdad99c commit 143434f
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 186 deletions.
28 changes: 11 additions & 17 deletions boa/src/exec/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ fn empty_let_decl_undefined() {
a == undefined;
"#;

let pass = String::from("true");

assert_eq!(exec(scenario), pass);
assert_eq!(&exec(scenario), "true");
}

#[test]
Expand All @@ -23,9 +21,7 @@ fn semicolon_expression_stop() {
a
"#;

let pass = String::from("1");

assert_eq!(exec(scenario), pass);
assert_eq!(&exec(scenario), "1");
}

#[test]
Expand All @@ -35,9 +31,7 @@ fn empty_var_decl_undefined() {
b == undefined;
"#;

let pass = String::from("true");

assert_eq!(exec(scenario), pass);
assert_eq!(&exec(scenario), "true");
}

#[test]
Expand Down Expand Up @@ -189,10 +183,10 @@ fn early_return() {
#[test]
fn short_circuit_evaluation() {
// OR operation
assert_eq!(exec("true || true"), String::from("true"));
assert_eq!(exec("true || false"), String::from("true"));
assert_eq!(exec("false || true"), String::from("true"));
assert_eq!(exec("false || false"), String::from("false"));
assert_eq!(&exec("true || true"), "true");
assert_eq!(&exec("true || false"), "true");
assert_eq!(&exec("false || true"), "true");
assert_eq!(&exec("false || false"), "false");

// the second operand must NOT be evaluated if the first one resolve to `true`.
let short_circuit_eval = r#"
Expand All @@ -219,10 +213,10 @@ fn short_circuit_evaluation() {
assert_eq!(&exec(short_circuit_eval), "2");

// AND operation
assert_eq!(exec("true && true"), String::from("true"));
assert_eq!(exec("true && false"), String::from("false"));
assert_eq!(exec("false && true"), String::from("false"));
assert_eq!(exec("false && false"), String::from("false"));
assert_eq!(&exec("true && true"), "true");
assert_eq!(&exec("true && false"), "false");
assert_eq!(&exec("false && true"), "false");
assert_eq!(&exec("false && false"), "false");

// the second operand must be evaluated if the first one resolve to `true`.
let short_circuit_eval = r#"
Expand Down
43 changes: 22 additions & 21 deletions boa/src/syntax/parser/expression/assignment/arrow_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::syntax::{
},
parser::{
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
Expand Down Expand Up @@ -59,30 +60,30 @@ impl TokenParser for ArrowFunction {
type Output = Node;

fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?;
let params = match &next_token.kind {
TokenKind::Punctuator(Punctuator::OpenParen) => {
let params =
FormalParameters::new(self.allow_yield, self.allow_await).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "arrow function")?;
params
}
TokenKind::Identifier(param_name) => vec![FormalParameter {
let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?;
let params = if let TokenKind::Punctuator(Punctuator::OpenParen) = &next_token.kind {
// CoverParenthesizedExpressionAndArrowParameterList
cursor.expect(Punctuator::OpenParen, "arrow function")?;
let params = FormalParameters::new(self.allow_yield, self.allow_await).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "arrow function")?;
params.into_boxed_slice()
} else {
let param = BindingIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor)
.map_err(|e| match e {
ParseError::Expected(mut exp, tok, _) => {
exp.push(Punctuator::OpenParen.into());
ParseError::Expected(exp, tok, "arrow function")
}
e => e,
})?;
Box::new([FormalParameter {
init: None,
name: param_name.clone(),
name: param,
is_rest_param: false,
}],
_ => {
return Err(ParseError::Expected(
vec![
TokenKind::Punctuator(Punctuator::OpenParen),
TokenKind::identifier("identifier"),
],
next_token.clone(),
"arrow function",
))
}
}])
};

cursor.peek_expect_no_lineterminator(0, "arrow function")?;

cursor.expect(Punctuator::Arrow, "arrow function")?;
Expand Down
4 changes: 3 additions & 1 deletion boa/src/syntax/parser/expression/assignment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod exponentiation;

use self::{arrow_function::ArrowFunction, conditional::ConditionalExpression};
use crate::syntax::{
ast::{node::Node, punc::Punctuator, token::TokenKind},
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind},
parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
};
pub(super) use exponentiation::ExponentiationExpression;
Expand Down Expand Up @@ -72,6 +72,8 @@ impl TokenParser for AssignmentExpression {
match next_token.kind {
// a=>{}
TokenKind::Identifier(_)
| TokenKind::Keyword(Keyword::Yield)
| TokenKind::Keyword(Keyword::Await)
if cursor
.peek_expect_no_lineterminator(1, "arrow function")
.is_ok() =>
Expand Down
19 changes: 5 additions & 14 deletions boa/src/syntax/parser/expression/primary/function_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
//! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
use crate::syntax::{
ast::{node::Node, punc::Punctuator, token::TokenKind},
ast::{node::Node, punc::Punctuator},
parser::{
function::{FormalParameters, FunctionBody},
Cursor, ParseError, ParseResult, TokenParser,
statement::BindingIdentifier,
Cursor, ParseResult, TokenParser,
},
};

Expand All @@ -30,17 +31,7 @@ impl TokenParser for FunctionExpression {
type Output = Node;

fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let name = if let TokenKind::Identifier(name) =
&cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind
{
Some(name)
} else {
None
};
if name.is_some() {
// We move the cursor forward.
let _ = cursor.next().expect("nex token disappeared");
}
let name = BindingIdentifier::new(false, false).try_parse(cursor);

cursor.expect(Punctuator::OpenParen, "function expression")?;

Expand All @@ -55,6 +46,6 @@ impl TokenParser for FunctionExpression {

cursor.expect(Punctuator::CloseBlock, "function expression")?;

Ok(Node::function_expr::<_, &String, _, _>(name, params, body))
Ok(Node::function_expr::<_, String, _, _>(name, params, body))
}
}
2 changes: 1 addition & 1 deletion boa/src/syntax/parser/expression/primary/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl TokenParser for PrimaryExpression {
// TODO: ADD TokenKind::UndefinedLiteral
TokenKind::Identifier(ref i) if i == "undefined" => Ok(Node::Const(Const::Undefined)),
TokenKind::NullLiteral => Ok(Node::Const(Const::Null)),
TokenKind::Identifier(ident) => Ok(Node::local(ident)),
TokenKind::Identifier(ident) => Ok(Node::local(ident)), // TODO: IdentifierReference
TokenKind::StringLiteral(s) => Ok(Node::const_node(s)),
TokenKind::NumericLiteral(NumericLiteral::Integer(num)) => Ok(Node::const_node(*num)),
TokenKind::NumericLiteral(NumericLiteral::Rational(num)) => Ok(Node::const_node(*num)),
Expand Down
65 changes: 33 additions & 32 deletions boa/src/syntax/parser/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ use crate::syntax::{
punc::Punctuator,
token::TokenKind,
},
parser::{statement::StatementList, AllowAwait, AllowYield, Cursor, ParseError, TokenParser},
parser::{
expression::Initializer,
statement::{BindingIdentifier, StatementList},
AllowAwait, AllowYield, Cursor, ParseError, TokenParser,
},
};

/// Formal parameters parsing.
Expand Down Expand Up @@ -100,14 +104,24 @@ impl TokenParser for FormalParameters {
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
/// [spec]: https://tc39.es/ecma262/#prod-FunctionRestParameter
type FunctionRestParameter = BindingRestElement;

/// Rest parameter parsing.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript specification][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
/// [spec]: https://tc39.es/ecma262/#prod-BindingRestElement
#[derive(Debug, Clone, Copy)]
struct FunctionRestParameter {
struct BindingRestElement {
allow_yield: AllowYield,
allow_await: AllowAwait,
}

impl FunctionRestParameter {
/// Creates a new `FunctionRestParameter` parser.
impl BindingRestElement {
/// Creates a new `BindingRestElement` parser.
fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
Expand All @@ -120,24 +134,17 @@ impl FunctionRestParameter {
}
}

impl TokenParser for FunctionRestParameter {
impl TokenParser for BindingRestElement {
type Output = node::FormalParameter;

fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let token = cursor.next().ok_or(ParseError::AbruptEnd)?;
Ok(Self::Output::new(
if let TokenKind::Identifier(name) = &token.kind {
name
} else {
return Err(ParseError::Expected(
vec![TokenKind::identifier("identifier")],
token.clone(),
"rest parameter",
));
},
None,
true,
))
// FIXME: we are reading the spread operator before the rest element.
// cursor.expect(Punctuator::Spread, "rest parameter")?;

let param = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;
// TODO: BindingPattern

Ok(Self::Output::new(param, None, true))
}
}

Expand Down Expand Up @@ -173,19 +180,13 @@ impl TokenParser for FormalParameter {
type Output = node::FormalParameter;

fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let token = cursor.next().ok_or(ParseError::AbruptEnd)?;
let name = if let TokenKind::Identifier(name) = &token.kind {
name
} else {
return Err(ParseError::Expected(
vec![TokenKind::identifier("identifier")],
token.clone(),
"formal parameter",
));
};

// TODO: Implement initializer.
Ok(Self::Output::new(name, None, false))
// TODO: BindingPattern

let param = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;

let init = Initializer::new(true, self.allow_yield, self.allow_await).try_parse(cursor);

Ok(Self::Output::new(param, init.map(Box::new), false))
}
}

Expand Down
1 change: 1 addition & 0 deletions boa/src/syntax/parser/statement/break_stm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl TokenParser for BreakStatement {
}

let tok = cursor.next().ok_or(ParseError::AbruptEnd)?;
// TODO: LabelIdentifier
let node = if let TokenKind::Identifier(name) = &tok.kind {
Node::break_node(name)
} else {
Expand Down
1 change: 1 addition & 0 deletions boa/src/syntax/parser/statement/continue_stm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl TokenParser for ContinueStatement {
}

let tok = cursor.next().ok_or(ParseError::AbruptEnd)?;
// TODO: LabelIdentifier
let node = if let TokenKind::Identifier(name) = &tok.kind {
Node::continue_node(name)
} else {
Expand Down
17 changes: 4 additions & 13 deletions boa/src/syntax/parser/statement/declaration/hoistable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration
use crate::syntax::{
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind},
ast::{keyword::Keyword, node::Node, punc::Punctuator},
parser::{
function::FormalParameters, function::FunctionBody, AllowAwait, AllowDefault, AllowYield,
Cursor, ParseError, ParseResult, TokenParser,
function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
},
};

Expand Down Expand Up @@ -89,16 +89,7 @@ impl TokenParser for FunctionDeclaration {
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
cursor.expect(Keyword::Function, "function declaration")?;

let token = cursor.next().ok_or(ParseError::AbruptEnd)?;
let name = if let TokenKind::Identifier(name) = &token.kind {
name.clone()
} else {
return Err(ParseError::Expected(
vec![TokenKind::identifier("function name")],
token.clone(),
"function declaration",
));
};
let name = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;

cursor.expect(Punctuator::OpenParen, "function declaration")?;

Expand Down
Loading

0 comments on commit 143434f

Please sign in to comment.