Browse Source

Added `BindingIdentifier` parsing. (#389)

pull/391/head
Iban Eguia 5 years ago committed by GitHub
parent
commit
143434f643
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      boa/src/exec/tests.rs
  2. 43
      boa/src/syntax/parser/expression/assignment/arrow_function.rs
  3. 4
      boa/src/syntax/parser/expression/assignment/mod.rs
  4. 19
      boa/src/syntax/parser/expression/primary/function_expression.rs
  5. 2
      boa/src/syntax/parser/expression/primary/mod.rs
  6. 65
      boa/src/syntax/parser/function/mod.rs
  7. 1
      boa/src/syntax/parser/statement/break_stm/mod.rs
  8. 1
      boa/src/syntax/parser/statement/continue_stm/mod.rs
  9. 17
      boa/src/syntax/parser/statement/declaration/hoistable.rs
  10. 101
      boa/src/syntax/parser/statement/declaration/lexical.rs
  11. 119
      boa/src/syntax/parser/statement/declaration/tests.rs
  12. 47
      boa/src/syntax/parser/statement/mod.rs
  13. 20
      boa/src/syntax/parser/statement/try_stm/mod.rs
  14. 33
      boa/src/syntax/parser/statement/variable.rs

28
boa/src/exec/tests.rs

@ -10,9 +10,7 @@ fn empty_let_decl_undefined() {
a == undefined; a == undefined;
"#; "#;
let pass = String::from("true"); assert_eq!(&exec(scenario), "true");
assert_eq!(exec(scenario), pass);
} }
#[test] #[test]
@ -23,9 +21,7 @@ fn semicolon_expression_stop() {
a a
"#; "#;
let pass = String::from("1"); assert_eq!(&exec(scenario), "1");
assert_eq!(exec(scenario), pass);
} }
#[test] #[test]
@ -35,9 +31,7 @@ fn empty_var_decl_undefined() {
b == undefined; b == undefined;
"#; "#;
let pass = String::from("true"); assert_eq!(&exec(scenario), "true");
assert_eq!(exec(scenario), pass);
} }
#[test] #[test]
@ -189,10 +183,10 @@ fn early_return() {
#[test] #[test]
fn short_circuit_evaluation() { fn short_circuit_evaluation() {
// OR operation // OR operation
assert_eq!(exec("true || true"), String::from("true")); assert_eq!(&exec("true || true"), "true");
assert_eq!(exec("true || false"), String::from("true")); assert_eq!(&exec("true || false"), "true");
assert_eq!(exec("false || true"), String::from("true")); assert_eq!(&exec("false || true"), "true");
assert_eq!(exec("false || false"), String::from("false")); assert_eq!(&exec("false || false"), "false");
// the second operand must NOT be evaluated if the first one resolve to `true`. // the second operand must NOT be evaluated if the first one resolve to `true`.
let short_circuit_eval = r#" let short_circuit_eval = r#"
@ -219,10 +213,10 @@ fn short_circuit_evaluation() {
assert_eq!(&exec(short_circuit_eval), "2"); assert_eq!(&exec(short_circuit_eval), "2");
// AND operation // AND operation
assert_eq!(exec("true && true"), String::from("true")); assert_eq!(&exec("true && true"), "true");
assert_eq!(exec("true && false"), String::from("false")); assert_eq!(&exec("true && false"), "false");
assert_eq!(exec("false && true"), String::from("false")); assert_eq!(&exec("false && true"), "false");
assert_eq!(exec("false && false"), String::from("false")); assert_eq!(&exec("false && false"), "false");
// the second operand must be evaluated if the first one resolve to `true`. // the second operand must be evaluated if the first one resolve to `true`.
let short_circuit_eval = r#" let short_circuit_eval = r#"

43
boa/src/syntax/parser/expression/assignment/arrow_function.rs

@ -16,6 +16,7 @@ use crate::syntax::{
}, },
parser::{ parser::{
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
}, },
}; };
@ -59,30 +60,30 @@ impl TokenParser for ArrowFunction {
type Output = Node; type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?; let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?;
let params = match &next_token.kind { let params = if let TokenKind::Punctuator(Punctuator::OpenParen) = &next_token.kind {
TokenKind::Punctuator(Punctuator::OpenParen) => { // CoverParenthesizedExpressionAndArrowParameterList
let params = cursor.expect(Punctuator::OpenParen, "arrow function")?;
FormalParameters::new(self.allow_yield, self.allow_await).parse(cursor)?; let params = FormalParameters::new(self.allow_yield, self.allow_await).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "arrow function")?; cursor.expect(Punctuator::CloseParen, "arrow function")?;
params params.into_boxed_slice()
} } else {
TokenKind::Identifier(param_name) => vec![FormalParameter { 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, init: None,
name: param_name.clone(), name: param,
is_rest_param: false, 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.peek_expect_no_lineterminator(0, "arrow function")?;
cursor.expect(Punctuator::Arrow, "arrow function")?; cursor.expect(Punctuator::Arrow, "arrow function")?;

4
boa/src/syntax/parser/expression/assignment/mod.rs

@ -13,7 +13,7 @@ mod exponentiation;
use self::{arrow_function::ArrowFunction, conditional::ConditionalExpression}; use self::{arrow_function::ArrowFunction, conditional::ConditionalExpression};
use crate::syntax::{ 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}, parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
}; };
pub(super) use exponentiation::ExponentiationExpression; pub(super) use exponentiation::ExponentiationExpression;
@ -72,6 +72,8 @@ impl TokenParser for AssignmentExpression {
match next_token.kind { match next_token.kind {
// a=>{} // a=>{}
TokenKind::Identifier(_) TokenKind::Identifier(_)
| TokenKind::Keyword(Keyword::Yield)
| TokenKind::Keyword(Keyword::Await)
if cursor if cursor
.peek_expect_no_lineterminator(1, "arrow function") .peek_expect_no_lineterminator(1, "arrow function")
.is_ok() => .is_ok() =>

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

@ -8,10 +8,11 @@
//! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression //! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
use crate::syntax::{ use crate::syntax::{
ast::{node::Node, punc::Punctuator, token::TokenKind}, ast::{node::Node, punc::Punctuator},
parser::{ parser::{
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
Cursor, ParseError, ParseResult, TokenParser, statement::BindingIdentifier,
Cursor, ParseResult, TokenParser,
}, },
}; };
@ -30,17 +31,7 @@ impl TokenParser for FunctionExpression {
type Output = Node; type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let name = if let TokenKind::Identifier(name) = let name = BindingIdentifier::new(false, false).try_parse(cursor);
&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");
}
cursor.expect(Punctuator::OpenParen, "function expression")?; cursor.expect(Punctuator::OpenParen, "function expression")?;
@ -55,6 +46,6 @@ impl TokenParser for FunctionExpression {
cursor.expect(Punctuator::CloseBlock, "function expression")?; cursor.expect(Punctuator::CloseBlock, "function expression")?;
Ok(Node::function_expr::<_, &String, _, _>(name, params, body)) Ok(Node::function_expr::<_, String, _, _>(name, params, body))
} }
} }

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

@ -81,7 +81,7 @@ impl TokenParser for PrimaryExpression {
// TODO: ADD TokenKind::UndefinedLiteral // TODO: ADD TokenKind::UndefinedLiteral
TokenKind::Identifier(ref i) if i == "undefined" => Ok(Node::Const(Const::Undefined)), TokenKind::Identifier(ref i) if i == "undefined" => Ok(Node::Const(Const::Undefined)),
TokenKind::NullLiteral => Ok(Node::Const(Const::Null)), 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::StringLiteral(s) => Ok(Node::const_node(s)),
TokenKind::NumericLiteral(NumericLiteral::Integer(num)) => Ok(Node::const_node(*num)), TokenKind::NumericLiteral(NumericLiteral::Integer(num)) => Ok(Node::const_node(*num)),
TokenKind::NumericLiteral(NumericLiteral::Rational(num)) => Ok(Node::const_node(*num)), TokenKind::NumericLiteral(NumericLiteral::Rational(num)) => Ok(Node::const_node(*num)),

65
boa/src/syntax/parser/function/mod.rs

@ -16,7 +16,11 @@ use crate::syntax::{
punc::Punctuator, punc::Punctuator,
token::TokenKind, token::TokenKind,
}, },
parser::{statement::StatementList, AllowAwait, AllowYield, Cursor, ParseError, TokenParser}, parser::{
expression::Initializer,
statement::{BindingIdentifier, StatementList},
AllowAwait, AllowYield, Cursor, ParseError, TokenParser,
},
}; };
/// Formal parameters parsing. /// Formal parameters parsing.
@ -100,14 +104,24 @@ impl TokenParser for FormalParameters {
/// ///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
/// [spec]: https://tc39.es/ecma262/#prod-FunctionRestParameter /// [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)] #[derive(Debug, Clone, Copy)]
struct FunctionRestParameter { struct BindingRestElement {
allow_yield: AllowYield, allow_yield: AllowYield,
allow_await: AllowAwait, allow_await: AllowAwait,
} }
impl FunctionRestParameter { impl BindingRestElement {
/// Creates a new `FunctionRestParameter` parser. /// Creates a new `BindingRestElement` parser.
fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where where
Y: Into<AllowYield>, Y: Into<AllowYield>,
@ -120,24 +134,17 @@ impl FunctionRestParameter {
} }
} }
impl TokenParser for FunctionRestParameter { impl TokenParser for BindingRestElement {
type Output = node::FormalParameter; type Output = node::FormalParameter;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let token = cursor.next().ok_or(ParseError::AbruptEnd)?; // FIXME: we are reading the spread operator before the rest element.
Ok(Self::Output::new( // cursor.expect(Punctuator::Spread, "rest parameter")?;
if let TokenKind::Identifier(name) = &token.kind {
name let param = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;
} else { // TODO: BindingPattern
return Err(ParseError::Expected(
vec![TokenKind::identifier("identifier")], Ok(Self::Output::new(param, None, true))
token.clone(),
"rest parameter",
));
},
None,
true,
))
} }
} }
@ -173,19 +180,13 @@ impl TokenParser for FormalParameter {
type Output = node::FormalParameter; type Output = node::FormalParameter;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let token = cursor.next().ok_or(ParseError::AbruptEnd)?; // TODO: BindingPattern
let name = if let TokenKind::Identifier(name) = &token.kind {
name let param = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;
} else {
return Err(ParseError::Expected( let init = Initializer::new(true, self.allow_yield, self.allow_await).try_parse(cursor);
vec![TokenKind::identifier("identifier")],
token.clone(), Ok(Self::Output::new(param, init.map(Box::new), false))
"formal parameter",
));
};
// TODO: Implement initializer.
Ok(Self::Output::new(name, None, false))
} }
} }

1
boa/src/syntax/parser/statement/break_stm/mod.rs

@ -61,6 +61,7 @@ impl TokenParser for BreakStatement {
} }
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; let tok = cursor.next().ok_or(ParseError::AbruptEnd)?;
// TODO: LabelIdentifier
let node = if let TokenKind::Identifier(name) = &tok.kind { let node = if let TokenKind::Identifier(name) = &tok.kind {
Node::break_node(name) Node::break_node(name)
} else { } else {

1
boa/src/syntax/parser/statement/continue_stm/mod.rs

@ -61,6 +61,7 @@ impl TokenParser for ContinueStatement {
} }
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; let tok = cursor.next().ok_or(ParseError::AbruptEnd)?;
// TODO: LabelIdentifier
let node = if let TokenKind::Identifier(name) = &tok.kind { let node = if let TokenKind::Identifier(name) = &tok.kind {
Node::continue_node(name) Node::continue_node(name)
} else { } else {

17
boa/src/syntax/parser/statement/declaration/hoistable.rs

@ -6,10 +6,10 @@
//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration //! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration
use crate::syntax::{ use crate::syntax::{
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, ast::{keyword::Keyword, node::Node, punc::Punctuator},
parser::{ parser::{
function::FormalParameters, function::FunctionBody, AllowAwait, AllowDefault, AllowYield, function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
Cursor, ParseError, ParseResult, TokenParser, AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,
}, },
}; };
@ -89,16 +89,7 @@ impl TokenParser for FunctionDeclaration {
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
cursor.expect(Keyword::Function, "function declaration")?; cursor.expect(Keyword::Function, "function declaration")?;
let token = cursor.next().ok_or(ParseError::AbruptEnd)?; let name = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;
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",
));
};
cursor.expect(Punctuator::OpenParen, "function declaration")?; cursor.expect(Punctuator::OpenParen, "function declaration")?;

101
boa/src/syntax/parser/statement/declaration/lexical.rs

@ -10,8 +10,8 @@
use crate::syntax::{ use crate::syntax::{
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind},
parser::{ parser::{
expression::Initializer, AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield,
TokenParser, Cursor, ParseError, ParseResult, TokenParser,
}, },
}; };
@ -69,7 +69,10 @@ impl TokenParser for LexicalDeclaration {
/// It will return an error if a `const` declaration is being parsed and there is no /// It will return an error if a `const` declaration is being parsed and there is no
/// initializer. /// initializer.
/// ///
/// More information: <https://tc39.es/ecma262/#prod-BindingList>. /// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-BindingList
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct BindingList { struct BindingList {
allow_in: AllowIn, allow_in: AllowIn,
@ -105,44 +108,22 @@ impl TokenParser for BindingList {
let mut const_decls = Vec::new(); let mut const_decls = Vec::new();
loop { loop {
let token = cursor.next().ok_or(ParseError::AbruptEnd)?; let lexical_binding =
let name = if let TokenKind::Identifier(ref name) = token.kind { LexicalBinding::new(self.allow_in, self.allow_yield, self.allow_await)
name.clone() .parse(cursor)?;
} else {
return Err(ParseError::Expected( if self.is_const {
vec![TokenKind::identifier("identifier")], if let (ident, Some(init)) = lexical_binding {
token.clone(), const_decls.push((ident, init));
if self.is_const { } else {
"const declaration" return Err(ParseError::Expected(
} else { vec![TokenKind::Punctuator(Punctuator::Assign)],
"let declaration" cursor.next().ok_or(ParseError::AbruptEnd)?.clone(),
}, "const declaration",
)); ));
};
match cursor.peek(0) {
Some(token) if token.kind == TokenKind::Punctuator(Punctuator::Assign) => {
let init = Some(
Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor)?,
);
if self.is_const {
const_decls.push((name, init.unwrap()));
} else {
let_decls.push((name, init));
};
}
_ => {
if self.is_const {
return Err(ParseError::Expected(
vec![TokenKind::Punctuator(Punctuator::Assign)],
cursor.next().ok_or(ParseError::AbruptEnd)?.clone(),
"const declaration",
));
} else {
let_decls.push((name, None));
}
} }
} else {
let_decls.push(lexical_binding);
} }
match cursor.peek_semicolon(false) { match cursor.peek_semicolon(false) {
@ -170,3 +151,43 @@ impl TokenParser for BindingList {
} }
} }
} }
/// Lexical binding parsing.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-LexicalBinding
struct LexicalBinding {
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl LexicalBinding {
/// Creates a new `BindingList` parser.
fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self
where
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl TokenParser for LexicalBinding {
type Output = (String, Option<Node>);
fn parse(self, cursor: &mut Cursor<'_>) -> Result<(String, Option<Node>), ParseError> {
let ident = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;
let initializer =
Initializer::new(self.allow_in, self.allow_yield, self.allow_await).try_parse(cursor);
Ok((ident, initializer))
}
}

119
boa/src/syntax/parser/statement/declaration/tests.rs

@ -5,7 +5,7 @@ use crate::syntax::{
/// Checks `var` declaration parsing. /// Checks `var` declaration parsing.
#[test] #[test]
fn check_var_declaration() { fn var_declaration() {
check_parser( check_parser(
"var a = 5;", "var a = 5;",
vec![Node::var_decl(vec![( vec![Node::var_decl(vec![(
@ -15,9 +15,29 @@ fn check_var_declaration() {
); );
} }
/// Checks `var` declaration parsing with reserved words.
#[test]
fn var_declaration_keywords() {
check_parser(
"var yield = 5;",
vec![Node::var_decl(vec![(
String::from("yield"),
Some(Node::const_node(5)),
)])],
);
check_parser(
"var await = 5;",
vec![Node::var_decl(vec![(
String::from("await"),
Some(Node::const_node(5)),
)])],
);
}
/// Checks `var` declaration parsing with no spaces. /// Checks `var` declaration parsing with no spaces.
#[test] #[test]
fn check_var_declaration_no_spaces() { fn var_declaration_no_spaces() {
check_parser( check_parser(
"var a=5;", "var a=5;",
vec![Node::var_decl(vec![( vec![Node::var_decl(vec![(
@ -29,7 +49,7 @@ fn check_var_declaration_no_spaces() {
/// Checks empty `var` declaration parsing. /// Checks empty `var` declaration parsing.
#[test] #[test]
fn check_empty_var_declaration() { fn empty_var_declaration() {
check_parser( check_parser(
"var a;", "var a;",
vec![Node::var_decl(vec![(String::from("a"), None)])], vec![Node::var_decl(vec![(String::from("a"), None)])],
@ -38,7 +58,7 @@ fn check_empty_var_declaration() {
/// Checks multiple `var` declarations. /// Checks multiple `var` declarations.
#[test] #[test]
fn check_multiple_var_declaration() { fn multiple_var_declaration() {
check_parser( check_parser(
"var a = 5, b, c = 6;", "var a = 5, b, c = 6;",
vec![Node::var_decl(vec![ vec![Node::var_decl(vec![
@ -51,7 +71,7 @@ fn check_multiple_var_declaration() {
/// Checks `let` declaration parsing. /// Checks `let` declaration parsing.
#[test] #[test]
fn check_let_declaration() { fn let_declaration() {
check_parser( check_parser(
"let a = 5;", "let a = 5;",
vec![Node::let_decl(vec![( vec![Node::let_decl(vec![(
@ -61,9 +81,29 @@ fn check_let_declaration() {
); );
} }
/// Checks `let` declaration parsing with reserved words.
#[test]
fn let_declaration_keywords() {
check_parser(
"let yield = 5;",
vec![Node::let_decl(vec![(
String::from("yield"),
Some(Node::const_node(5)),
)])],
);
check_parser(
"let await = 5;",
vec![Node::let_decl(vec![(
String::from("await"),
Some(Node::const_node(5)),
)])],
);
}
/// Checks `let` declaration parsing with no spaces. /// Checks `let` declaration parsing with no spaces.
#[test] #[test]
fn check_let_declaration_no_spaces() { fn let_declaration_no_spaces() {
check_parser( check_parser(
"let a=5;", "let a=5;",
vec![Node::let_decl(vec![( vec![Node::let_decl(vec![(
@ -75,7 +115,7 @@ fn check_let_declaration_no_spaces() {
/// Checks empty `let` declaration parsing. /// Checks empty `let` declaration parsing.
#[test] #[test]
fn check_empty_let_declaration() { fn empty_let_declaration() {
check_parser( check_parser(
"let a;", "let a;",
vec![Node::let_decl(vec![(String::from("a"), None)])], vec![Node::let_decl(vec![(String::from("a"), None)])],
@ -84,7 +124,7 @@ fn check_empty_let_declaration() {
/// Checks multiple `let` declarations. /// Checks multiple `let` declarations.
#[test] #[test]
fn check_multiple_let_declaration() { fn multiple_let_declaration() {
check_parser( check_parser(
"let a = 5, b, c = 6;", "let a = 5, b, c = 6;",
vec![Node::let_decl(vec![ vec![Node::let_decl(vec![
@ -97,7 +137,7 @@ fn check_multiple_let_declaration() {
/// Checks `const` declaration parsing. /// Checks `const` declaration parsing.
#[test] #[test]
fn check_const_declaration() { fn const_declaration() {
check_parser( check_parser(
"const a = 5;", "const a = 5;",
vec![Node::const_decl(vec![( vec![Node::const_decl(vec![(
@ -107,9 +147,29 @@ fn check_const_declaration() {
); );
} }
/// Checks `const` declaration parsing with reserved words.
#[test]
fn const_declaration_keywords() {
check_parser(
"const yield = 5;",
vec![Node::const_decl(vec![(
String::from("yield"),
Node::const_node(5),
)])],
);
check_parser(
"const await = 5;",
vec![Node::const_decl(vec![(
String::from("await"),
Node::const_node(5),
)])],
);
}
/// Checks `const` declaration parsing with no spaces. /// Checks `const` declaration parsing with no spaces.
#[test] #[test]
fn check_const_declaration_no_spaces() { fn const_declaration_no_spaces() {
check_parser( check_parser(
"const a=5;", "const a=5;",
vec![Node::const_decl(vec![( vec![Node::const_decl(vec![(
@ -121,13 +181,13 @@ fn check_const_declaration_no_spaces() {
/// Checks empty `const` declaration parsing. /// Checks empty `const` declaration parsing.
#[test] #[test]
fn check_empty_const_declaration() { fn empty_const_declaration() {
check_invalid("const a;"); check_invalid("const a;");
} }
/// Checks multiple `const` declarations. /// Checks multiple `const` declarations.
#[test] #[test]
fn check_multiple_const_declaration() { fn multiple_const_declaration() {
check_parser( check_parser(
"const a = 5, c = 6;", "const a = 5, c = 6;",
vec![Node::const_decl(vec![ vec![Node::const_decl(vec![
@ -136,3 +196,38 @@ fn check_multiple_const_declaration() {
])], ])],
); );
} }
/// Function declaration parsing.
#[test]
fn function_declaration() {
check_parser(
"function hello() {}",
vec![Node::function_decl(
"hello",
vec![],
Node::statement_list(vec![]),
)],
);
}
/// Function declaration parsing with keywords.
#[test]
fn function_declaration_keywords() {
check_parser(
"function yield() {}",
vec![Node::function_decl(
"yield",
vec![],
Node::statement_list(vec![]),
)],
);
check_parser(
"function await() {}",
vec![Node::function_decl(
"await",
vec![],
Node::statement_list(vec![]),
)],
);
}

47
boa/src/syntax/parser/statement/mod.rs

@ -316,3 +316,50 @@ impl TokenParser for ExpressionStatement {
Ok(expr) Ok(expr)
} }
} }
/// Binding identifier parsing.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-BindingIdentifier
#[derive(Debug, Clone, Copy)]
pub(super) struct BindingIdentifier {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl BindingIdentifier {
/// Creates a new `BindingIdentifier` parser.
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl TokenParser for BindingIdentifier {
type Output = String;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<String, ParseError> {
// TODO: strict mode.
let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?;
match next_token.kind {
TokenKind::Identifier(ref s) => Ok(s.clone()),
TokenKind::Keyword(k @ Keyword::Yield) if !self.allow_yield.0 => Ok(k.to_string()),
TokenKind::Keyword(k @ Keyword::Await) if !self.allow_await.0 => Ok(k.to_string()),
_ => Err(ParseError::Expected(
vec![TokenKind::identifier("identifier")],
next_token.clone(),
"binding identifier",
)),
}
}
}

20
boa/src/syntax/parser/statement/try_stm/mod.rs

@ -4,7 +4,10 @@ mod tests;
use super::block::Block; use super::block::Block;
use crate::syntax::{ use crate::syntax::{
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind},
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, parser::{
statement::BindingIdentifier, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError,
ParseResult, TokenParser,
},
}; };
/// Try...catch statement parsing /// Try...catch statement parsing
@ -67,17 +70,10 @@ impl TokenParser for TryStatement {
let (catch, param) = if next_token.kind == TokenKind::Keyword(Keyword::Catch) { let (catch, param) = if next_token.kind == TokenKind::Keyword(Keyword::Catch) {
// Catch binding // Catch binding
cursor.expect(Punctuator::OpenParen, "catch in try statement")?; cursor.expect(Punctuator::OpenParen, "catch in try statement")?;
// TODO: should accept BindingPattern // TODO: CatchParameter - BindingPattern
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; let catch_param = BindingIdentifier::new(self.allow_yield, self.allow_await)
let catch_param = if let TokenKind::Identifier(s) = &tok.kind { .parse(cursor)
Node::local(s) .map(Node::local)?;
} else {
return Err(ParseError::Expected(
vec![TokenKind::identifier("identifier")],
tok.clone(),
"catch in try statement",
));
};
cursor.expect(Punctuator::CloseParen, "catch in try statement")?; cursor.expect(Punctuator::CloseParen, "catch in try statement")?;
// Catch block // Catch block

33
boa/src/syntax/parser/statement/variable.rs

@ -2,8 +2,8 @@
use crate::syntax::{ use crate::syntax::{
ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind}, ast::{keyword::Keyword, node::Node, punc::Punctuator, token::TokenKind},
parser::{ parser::{
expression::Initializer, AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield,
TokenParser, Cursor, ParseError, ParseResult, TokenParser,
}, },
}; };
@ -154,26 +154,13 @@ impl TokenParser for VariableDeclaration {
type Output = (String, Option<Node>); type Output = (String, Option<Node>);
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> { fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; // TODO: BindingPattern
let name = if let TokenKind::Identifier(name) = &tok.kind {
name.clone() let name = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?;
} else {
return Err(ParseError::Expected( let ident =
vec![TokenKind::identifier("identifier")], Initializer::new(self.allow_in, self.allow_yield, self.allow_await).try_parse(cursor);
tok.clone(),
"variable declaration", Ok((name, ident))
));
};
match cursor.peek(0) {
Some(tk) if tk.kind == TokenKind::Punctuator(Punctuator::Assign) => Ok((
name,
Some(
Initializer::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor)?,
),
)),
_ => Ok((name, None)),
}
} }
} }

Loading…
Cancel
Save