Browse Source

Modularize try statement parsing (#390)

* Fix catch parsing - move the cursor to next token

* Refactor catch and finally parsing into separate modules

* Refactor catchparam parsing into separate module and add more tests

* Refactoring - use ? instead of match
pull/397/head
abhi 5 years ago committed by GitHub
parent
commit
2851eb02a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 99
      boa/src/syntax/parser/statement/try_stm/catch.rs
  2. 47
      boa/src/syntax/parser/statement/try_stm/finally.rs
  3. 46
      boa/src/syntax/parser/statement/try_stm/mod.rs
  4. 124
      boa/src/syntax/parser/statement/try_stm/tests.rs

99
boa/src/syntax/parser/statement/try_stm/catch.rs

@ -0,0 +1,99 @@
use crate::syntax::{
ast::{keyword::Keyword, node::Node, punc::Punctuator},
parser::{
statement::{block::Block, BindingIdentifier},
AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
/// Catch parsing
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript specification][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
/// [spec]: https://tc39.es/ecma262/#prod-Catch
#[derive(Debug, Clone, Copy)]
pub(super) struct Catch {
allow_yield: AllowYield,
allow_await: AllowAwait,
allow_return: AllowReturn,
}
impl Catch {
/// Creates a new `Catch` block parser.
pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
R: Into<AllowReturn>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
allow_return: allow_return.into(),
}
}
}
impl TokenParser for Catch {
type Output = (Option<Node>, Option<Node>);
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
cursor.expect(Keyword::Catch, "try statement")?;
let catch_param = if cursor.next_if(Punctuator::OpenParen).is_some() {
let catch_param =
CatchParameter::new(self.allow_yield, self.allow_await).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "catch in try statement")?;
Some(catch_param)
} else {
None
};
// Catch block
Ok((
Some(Block::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?),
catch_param,
))
}
}
/// CatchParameter parsing
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript specification][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
/// [spec]: https://tc39.es/ecma262/#prod-CatchParameter
#[derive(Debug, Clone, Copy)]
pub(super) struct CatchParameter {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl CatchParameter {
/// Creates a new `CatchParameter` 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 CatchParameter {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
// TODO: should accept BindingPattern
BindingIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor)
.map(Node::local)
}
}

47
boa/src/syntax/parser/statement/try_stm/finally.rs

@ -0,0 +1,47 @@
use crate::syntax::{
ast::{keyword::Keyword, node::Node},
parser::{
statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult,
TokenParser,
},
};
/// Finally parsing
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript specification][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
/// [spec]: https://tc39.es/ecma262/#prod-Finally
#[derive(Debug, Clone, Copy)]
pub(super) struct Finally {
allow_yield: AllowYield,
allow_await: AllowAwait,
allow_return: AllowReturn,
}
impl Finally {
/// Creates a new `Finally` block parser.
pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
R: Into<AllowReturn>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
allow_return: allow_return.into(),
}
}
}
impl TokenParser for Finally {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
cursor.expect(Keyword::Finally, "try statement")?;
Ok(Block::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?)
}
}

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

@ -1,13 +1,15 @@
mod catch;
mod finally;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use self::catch::Catch;
use self::finally::Finally;
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, token::TokenKind},
parser::{ parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
statement::BindingIdentifier, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError,
ParseResult, TokenParser,
},
}; };
/// Try...catch statement parsing /// Try...catch statement parsing
@ -66,33 +68,23 @@ impl TokenParser for TryStatement {
)); ));
} }
// CATCH
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::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?
cursor.expect(Punctuator::OpenParen, "catch in try statement")?;
// TODO: CatchParameter - BindingPattern
let catch_param = BindingIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor)
.map(Node::local)?;
cursor.expect(Punctuator::CloseParen, "catch in try statement")?;
// Catch block
(
Some(
Block::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor)?,
),
Some(catch_param),
)
} else { } else {
(None, None) (None, None)
}; };
// FINALLY let next_token = cursor.peek(0);
let finally_block = if cursor.next_if(Keyword::Finally).is_some() { let finally_block = match next_token {
Some(Block::new(self.allow_yield, self.allow_await, self.allow_return).parse(cursor)?) Some(token) => match token.kind {
} else { TokenKind::Keyword(Keyword::Finally) => Some(
None Finally::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor)?,
),
_ => None,
},
None => None,
}; };
Ok(Node::try_node::<_, _, _, _, Node, Node, Node>( Ok(Node::try_node::<_, _, _, _, Node, Node, Node>(

124
boa/src/syntax/parser/statement/try_stm/tests.rs

@ -1 +1,125 @@
use crate::syntax::{
ast::node::Node,
parser::tests::{check_invalid, check_parser},
};
#[test]
fn check_inline_with_empty_try_catch() {
check_parser(
"try { } catch(e) {}",
vec![Node::try_node::<_, _, _, _, Node, Node, Node>(
Node::block(vec![]),
Node::block(vec![]),
Node::local("e"),
None,
)],
);
}
#[test]
fn check_inline_with_var_decl_inside_try() {
check_parser(
"try { var x = 1; } catch(e) {}",
vec![Node::try_node::<_, _, _, _, Node, Node, Node>(
Node::block(vec![Node::var_decl(vec![(
String::from("x"),
Some(Node::const_node(1)),
)])]),
Node::block(vec![]),
Node::local("e"),
None,
)],
);
}
#[test]
fn check_inline_with_var_decl_inside_catch() {
check_parser(
"try { var x = 1; } catch(e) { var x = 1; }",
vec![Node::try_node::<_, _, _, _, Node, Node, Node>(
Node::block(vec![Node::var_decl(vec![(
String::from("x"),
Some(Node::const_node(1)),
)])]),
Node::block(vec![Node::var_decl(vec![(
String::from("x"),
Some(Node::const_node(1)),
)])]),
Node::local("e"),
None,
)],
);
}
#[test]
fn check_inline_with_empty_try_catch_finally() {
check_parser(
"try {} catch(e) {} finally {}",
vec![Node::try_node::<_, _, _, _, Node, Node, Node>(
Node::block(vec![]),
Node::block(vec![]),
Node::local("e"),
Node::block(vec![]),
)],
);
}
#[test]
fn check_inline_with_empty_try_finally() {
check_parser(
"try {} finally {}",
vec![Node::try_node::<_, _, _, _, Node, Node, Node>(
Node::block(vec![]),
None,
None,
Node::block(vec![]),
)],
);
}
#[test]
fn check_inline_with_empty_try_var_decl_in_finally() {
check_parser(
"try {} finally { var x = 1; }",
vec![Node::try_node::<_, _, _, _, Node, Node, Node>(
Node::block(vec![]),
None,
None,
Node::block(vec![Node::var_decl(vec![(
String::from("x"),
Some(Node::const_node(1)),
)])]),
)],
);
}
#[test]
fn check_inline_empty_try_paramless_catch() {
check_parser(
"try {} catch { var x = 1; }",
vec![Node::try_node::<_, _, _, _, Node, Node, Node>(
Node::block(vec![]),
Node::block(vec![Node::var_decl(vec![(
String::from("x"),
Some(Node::const_node(1)),
)])]),
None,
None,
)],
);
}
#[test]
fn check_inline_invalid_catch() {
check_invalid("try {} catch");
}
#[test]
fn check_inline_invalid_catch_without_closing_paren() {
check_invalid("try {} catch(e {}");
}
#[test]
fn check_inline_invalid_catch_parameter() {
check_invalid("try {} catch(1) {}");
}

Loading…
Cancel
Save