mirror of https://github.com/boa-dev/boa.git
Browse Source
* 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 matchpull/397/head
abhi
5 years ago
committed by
GitHub
4 changed files with 289 additions and 27 deletions
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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)?) |
||||||
|
} |
||||||
|
} |
@ -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…
Reference in new issue