From c2a99944cfa35c7f7b0cdaaf264c16d05d3d39ec Mon Sep 17 00:00:00 2001 From: Boris Date: Tue, 22 Dec 2020 06:12:33 +0100 Subject: [PATCH] feat(ecmascript/parser): Add tests for binding patterns (#1289) --- ecmascript/parser/src/parser/object.rs | 2 +- ecmascript/parser/src/parser/pat.rs | 214 ++++++++++++++++++ ecmascript/parser/src/parser/stmt.rs | 61 +++++ .../tests/binding-pattern/description.js | 96 ++++++++ 4 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 ecmascript/parser/tests/binding-pattern/description.js diff --git a/ecmascript/parser/src/parser/object.rs b/ecmascript/parser/src/parser/object.rs index 819ee743fe13..e5a22980cfd6 100644 --- a/ecmascript/parser/src/parser/object.rs +++ b/ecmascript/parser/src/parser/object.rs @@ -131,7 +131,7 @@ impl ParseObject> for Parser { // Parse as 'MethodDefinition' if eat!("...") { - // spread elemnent + // spread element let dot3_token = span!(start); let expr = self.include_in_expr(true).parse_assignment_expr()?; diff --git a/ecmascript/parser/src/parser/pat.rs b/ecmascript/parser/src/parser/pat.rs index a4e7188b2d40..59780d0160ae 100644 --- a/ecmascript/parser/src/parser/pat.rs +++ b/ecmascript/parser/src/parser/pat.rs @@ -426,6 +426,7 @@ impl<'a, I: Tokens> Parser { self.parse_formal_params() } } + /// #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PatType { @@ -435,6 +436,7 @@ pub enum PatType { AssignPat, AssignElement, } + impl PatType { pub fn element(self) -> Self { match self { @@ -798,10 +800,23 @@ mod tests { test_parser(s, Syntax::default(), |p| p.parse_array_binding_pat()) } + fn object_pat(s: &'static str) -> Pat { + test_parser(s, Syntax::default(), |p| p.parse_binding_pat_or_ident()) + } + fn ident(s: &str) -> Ident { Ident::new(s.into(), span) } + fn rest() -> Option { + Some(Pat::Rest(RestPat { + span, + dot3_token: span, + type_ann: None, + arg: Box::new(Pat::Ident(ident("tail"))), + })) + } + #[test] fn array_pat_simple() { assert_eq_ignore_span!( @@ -884,4 +899,203 @@ mod tests { }) ); } + + #[test] + fn array_binding_pattern_tail() { + assert_eq_ignore_span!( + array_pat("[...tail]"), + Pat::Array(ArrayPat { + span, + optional: false, + elems: vec![rest()], + type_ann: None + }) + ); + } + + #[test] + fn array_binding_pattern_assign() { + assert_eq_ignore_span!( + array_pat("[,a=1,]"), + Pat::Array(ArrayPat { + span, + optional: false, + elems: vec![ + None, + Some(Pat::Assign(AssignPat { + type_ann: None, + span, + left: Box::new(Pat::Ident(ident("a"))), + right: Box::new(Expr::Lit(Lit::Num(Number { span, value: 1.0 }))) + })) + ], + type_ann: None + }) + ); + } + + #[test] + fn array_binding_pattern_tail_with_elems() { + assert_eq_ignore_span!( + array_pat("[,,,...tail]"), + Pat::Array(ArrayPat { + span, + optional: false, + elems: vec![None, None, None, rest()], + type_ann: None + }) + ); + } + + #[test] + fn array_binding_pattern_tail_inside_tail() { + assert_eq_ignore_span!( + array_pat("[,,,...[...tail]]"), + Pat::Array(ArrayPat { + span, + optional: false, + elems: vec![ + None, + None, + None, + Some(Pat::Rest(RestPat { + span, + dot3_token: span, + type_ann: None, + arg: Box::new(Pat::Array(ArrayPat { + span, + optional: false, + elems: vec![rest()], + type_ann: None + })) + })) + ], + type_ann: None + }) + ); + } + + #[test] + fn object_binding_pattern_tail() { + assert_eq_ignore_span!( + object_pat("{...obj}"), + Pat::Object(ObjectPat { + span, + type_ann: None, + optional: false, + props: vec![ObjectPatProp::Rest(RestPat { + span, + dot3_token: span, + type_ann: None, + arg: Box::new(Pat::Ident(ident("obj"))) + })] + }) + ); + } + + #[test] + fn object_binding_pattern_with_prop() { + assert_eq_ignore_span!( + object_pat("{prop = 10 }"), + Pat::Object(ObjectPat { + span, + type_ann: None, + optional: false, + props: vec![ObjectPatProp::Assign(AssignPatProp { + span, + key: ident("prop"), + value: Some(Box::new(Expr::Lit(Lit::Num(Number { span, value: 10.0 })))) + })] + }) + ); + } + + #[test] + fn object_binding_pattern_with_prop_and_label() { + fn prop(key: PropName, assign_name: &str, expr: Expr) -> PropOrSpread { + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key, + value: Box::new(Expr::Assign(AssignExpr { + span, + op: AssignOp::Assign, + left: PatOrExpr::Pat(Box::new(Pat::Ident(ident(assign_name)))), + right: Box::new(expr), + })), + }))) + } + + assert_eq_ignore_span!( + object_pat( + "{obj = {$: num = 10, '': sym = '', \" \": quote = \" \", _: under = [...tail],}}" + ), + Pat::Object(ObjectPat { + span, + type_ann: None, + optional: false, + props: vec![ObjectPatProp::Assign(AssignPatProp { + span, + key: ident("obj"), + value: Some(Box::new(Expr::Object(ObjectLit { + span, + props: vec![ + prop( + PropName::Ident(ident("$")), + "num", + Expr::Lit(Lit::Num(Number { span, value: 10.0 })) + ), + prop( + PropName::Str(Str { + span, + has_escape: false, + value: "".into(), + kind: StrKind::Normal { + contains_quote: true + } + }), + "sym", + Expr::Lit(Lit::Str(Str { + span, + has_escape: false, + value: "".into(), + kind: StrKind::Normal { + contains_quote: true + } + })) + ), + prop( + PropName::Str(Str { + span, + has_escape: false, + value: " ".into(), + kind: StrKind::Normal { + contains_quote: true + } + }), + "quote", + Expr::Lit(Lit::Str(Str { + span, + has_escape: false, + value: " ".into(), + kind: StrKind::Normal { + contains_quote: true + } + })) + ), + prop( + PropName::Ident(ident("_")), + "under", + Expr::Array(ArrayLit { + span, + elems: vec![Some(ExprOrSpread { + spread: Some(span), + expr: Box::new(Expr::Ident(ident("tail"))) + })] + }) + ), + ] + }))) + })] + }) + ); + } } diff --git a/ecmascript/parser/src/parser/stmt.rs b/ecmascript/parser/src/parser/stmt.rs index cd7e8792c0e5..a42f463bf8d4 100644 --- a/ecmascript/parser/src/parser/stmt.rs +++ b/ecmascript/parser/src/parser/stmt.rs @@ -1713,4 +1713,65 @@ export default function waitUntil(callback, options = {}) { assert!(trailing.borrow().is_empty()); assert_eq!(leading.borrow().len(), 1); } + fn parse_for_head(str: &'static str) -> ForHead { + test_parser(str, Syntax::default(), |p| p.parse_for_head()) + } + + #[test] + fn for_array_binding_pattern() { + match parse_for_head("let [, , t] = simple_array; t < 10; t++") { + ForHead::For { init: Some(v), .. } => assert_eq_ignore_span!( + v, + VarDeclOrExpr::VarDecl(VarDecl { + span, + declare: false, + kind: VarDeclKind::Let, + decls: vec![VarDeclarator { + span, + name: Pat::Array(ArrayPat { + span, + type_ann: None, + optional: false, + elems: vec![None, None, Some(Pat::Ident(Ident::new("t".into(), span)))] + }), + init: Some(Box::new(Expr::Ident(Ident::new( + "simple_array".into(), + span + )))), + definite: false + }] + }) + ), + _ => assert!(false), + } + } + #[test] + fn for_object_binding_pattern() { + match parse_for_head("let {num} = obj; num < 11; num++") { + ForHead::For { init: Some(v), .. } => assert_eq_ignore_span!( + v, + VarDeclOrExpr::VarDecl(VarDecl { + span, + declare: false, + kind: VarDeclKind::Let, + decls: vec![VarDeclarator { + span, + name: Pat::Object(ObjectPat { + optional: false, + type_ann: None, + span, + props: vec![ObjectPatProp::Assign(AssignPatProp { + span, + key: Ident::new("num".into(), span), + value: None + })] + }), + init: Some(Box::new(Expr::Ident(Ident::new("obj".into(), span)))), + definite: false + }] + }) + ), + _ => assert!(false), + } + } } diff --git a/ecmascript/parser/tests/binding-pattern/description.js b/ecmascript/parser/tests/binding-pattern/description.js new file mode 100644 index 000000000000..b79502e08544 --- /dev/null +++ b/ecmascript/parser/tests/binding-pattern/description.js @@ -0,0 +1,96 @@ +// self - local self + +// ArrayBindingPattern -> [Elision? BindingRestElement -> BindingIdentifier] + +/* +* ArrayBindingPattern +* Elision = , +* BindingRestElement = ... SELF | ... identifier +* BindingElementList = ,? BindingElement | self ,? BindingElement +* BindingElement = identifier = _ | SELF = _ +* +* */ +let simple_array = [1, 2, 3, 4, 5, 6] +let multi_array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] +let multi_array_2 = [[[1, 2, 3, 4], [5, 6, 7, 8, 9]]] +let obj = { + num: 10, + null: null, + nested: {nested: {val: "val"}, array: simple_array}, + s_array: simple_array, + m_array: multi_array +} +// ------- BindingPattern = SELF +{ + let [...tail] = simple_array // [ 1, 2, 3, 4, 5, 6 ] + let [, ...tail_1] = simple_array // [ 2, 3, 4, 5, 6 ] + let [, , , ...tail_2] = simple_array // [ 4, 5, 6 ] + +// ArrayBindingPattern -> [Elision? BindingRestElement -> BindingPattern -> ArrayBindingPattern -> [Elision? BindingRestElement -> BindingIdentifier]] + let [...[...tail_3]] = multi_array // [[ 1, 2, 3 ],[ 4, 5, 6 ],[ 7, 8, 9 ]] + let [...[...tail_4]] = multi_array_2 // [ [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8, 9 ] ] ] + let [...[...[...tail_5]]] = multi_array_2 // [ [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8, 9 ] ] ] + let [, ...[, ...tail_6]] = multi_array // [ [ 7, 8, 9 ] ] + let [, ...[, ...tail_7]] = multi_array_2 // [] + let [, ...[, , ...tail_8]] = [1, 2, 3, [4, 5, 6, 7]] // [ [ 4, 5, 6, 7 ] ] + +// ArrayBindingPattern -> [Elision? BindingRestElement -> BindingPattern -> ObjectBindingPattern ... ] + +// ArrayBindingPattern -> [BindingElementList -> BindingElisionElement -> BindingElement -> SingleNameBinding] + let [a = 10] = simple_array // 1 + let [, ab = 10, , ,] = simple_array // 2 + let [, , , ba = ab] = simple_array // 4 + +// ArrayBindingPattern -> [BindingElementList -> BindingElisionElement -> BindingElement -> BindingPattern] +// ArrayBindingPattern -> [BindingElementList,Elision? BindingRestElement] + let [, v1, , ...v_rest] = simple_array // 2 [ 4, 5, 6 ] + + /* + * ObjectBindingPattern + * {} + * {BindingRestProperty} = ... id + * {BindingPropertyList} = BindingProperty | self,BindingProperty + * {BindingPropertyList,BindingRestProperty } + * BindingProperty = id = _ | PropertyName : id = _ | PropertyName : SELF = _ + * PropertyName = ((UnicodeIDStart | $ | _ ) (UnicodeIDContinue | $ | ) | "string" | 'string' | num | [ _ ] + * */ +// ObjectBindingPattern -> {} + let {} = obj // {} +// ObjectBindingPattern -> {BindingRestProperty} + let {...obj2} = obj // obj + var {...obj3} = obj // obj + +// ObjectBindingPattern -> {BindingPropertyList} + let {simple_prop = 10} = obj // 10 + let {obj_prop = {value: prop2 = 10}, prop3 = [1]} = obj // obj_prop = {value:10},prop2 = 10 + let {obj1 = {$: num = 10, '': sym = '', " ": quote = " ", _: under = [...tail],}} = obj // obj_prop = {value:10},prop2 = 10 +} +// ----------------- // + +// Spots +// PrimaryExpression -> (...SELF) | (Expression, ...SELF) +// LexicalDeclaration[ -> (let | const) SELF +// Iteration Statements -> for((var | let | const) SELF (in | of) ...) +// TryStatement -> try{...} catch(SELF){...} +class MyError extends Error { + constructor(name1, name2) { + super(); + this.name1 = name1; + this.name2 = name2; + } +} + +try { + let err = new MyError("name1", "name2") + err.code = "code" + throw err +} catch ({name1, name2, code, ...rest}) { + console.log(name1, name2, code, rest) +} + +for (let [, , t] = simple_array; t < 10; t++) { + // console.log(t) +} +for (let {num} = obj; num < 11; num++) { + // console.log(num) +} \ No newline at end of file