From 0555a8a399f512f460cd9e164dbe3f17da01ea9d Mon Sep 17 00:00:00 2001 From: Carl Lei Date: Fri, 16 Oct 2015 11:31:22 +0800 Subject: [PATCH] Support initializer in destructuring Also known as default arguments. --- NEWS.md | 2 ++ js2-mode.el | 43 +++++++++++++++++++++++++++++++------------ tests/parser.el | 20 ++++++++++++++++++++ 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/NEWS.md b/NEWS.md index aa04a4227..30500e044 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,8 @@ with its related functions. It already handles generator methods, and will in the future add support for async methods, so the old name will get more confusing. +* Support for default parameters in destructuring. It should work for both + objects and arrays, in both literals and function arguments. ## 20150909 diff --git a/js2-mode.el b/js2-mode.el index 865f28759..249f4ca8b 100644 --- a/js2-mode.el +++ b/js2-mode.el @@ -8003,16 +8003,18 @@ declared; probably to check them for errors." (list node))) ((js2-object-node-p node) (dolist (elem (js2-object-node-elems node)) - (when (js2-object-prop-node-p elem) + ;; js2-infix-node-p catches both object prop node and initialized + ;; binding element (which is directly an infix node). + (when (js2-infix-node-p elem) (push (js2-define-destruct-symbols - ;; In abbreviated destructuring {a, b}, right == left. - (js2-object-prop-node-right elem) + (js2-infix-node-left elem) decl-type face ignore-not-in-block) name-nodes))) (apply #'append (nreverse name-nodes))) ((js2-array-node-p node) (dolist (elem (js2-array-node-elems node)) (when elem + (if (js2-infix-node-p elem) (setq elem (js2-infix-node-left elem))) (push (js2-define-destruct-symbols elem decl-type face ignore-not-in-block) name-nodes))) @@ -9632,7 +9634,7 @@ If NODE is non-nil, it is the AST node associated with the symbol." (js2-node-add-children pn test-expr if-true if-false)) pn)) -(defun js2-make-binary (type left parser) +(defun js2-make-binary (type left parser &optional no-get) "Helper for constructing a binary-operator AST node. LEFT is the left-side-expression, already parsed, and the binary operator should have just been matched. @@ -9643,7 +9645,7 @@ FIXME: The latter option is unused?" (op-pos (- (js2-current-token-beg) pos)) (right (if (js2-node-p parser) parser - (js2-get-token) + (unless no-get (js2-get-token)) (funcall parser))) (pn (make-js2-infix-node :type type :pos pos @@ -10364,14 +10366,20 @@ array-literals, array comprehensions and regular expressions." (apply #'js2-node-add-children pn (js2-array-node-elems pn))) ;; destructuring binding (js2-is-in-destructuring - (push (if (or (= tt js2-LC) - (= tt js2-LB) - (= tt js2-NAME)) - ;; [a, b, c] | {a, b, c} | {a:x, b:y, c:z} | a - (js2-parse-destruct-primary-expr) - ;; invalid pattern + (push (cond + ((and (= tt js2-NAME) + (= js2-ASSIGN (js2-peek-token))) + ;; a=defaultValue + (js2-parse-initialized-binding (js2-parse-name js2-NAME))) + ((or (= tt js2-LC) + (= tt js2-LB) + (= tt js2-NAME)) + ;; [a, b, c] | {a, b, c} | {a:x, b:y, c:z} | a + (js2-parse-destruct-primary-expr)) + ;; invalid pattern + (t (js2-report-error "msg.bad.var") - (make-js2-error-node)) + (make-js2-error-node))) elems) (setq after-lb-or-comma nil after-comma nil)) @@ -10714,6 +10722,10 @@ When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted." (when (js2-name-node-p key) ; highlight function name properties (js2-record-face 'font-lock-function-name-face)) (js2-parse-method-prop pos key property-type)) + ;; binding element with initializer + ((and (= (js2-peek-token) js2-ASSIGN) + (>= js2-language-version 200)) + (js2-parse-initialized-binding key)) ;; regular prop (t (let ((beg (js2-current-token-beg)) @@ -10732,6 +10744,13 @@ When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted." 'record) expr))))) +(defun js2-parse-initialized-binding (name) + "Parse a `SingleNameBinding' with initializer. + +`name' is the `BindingIdentifier'." + (when (js2-match-token js2-ASSIGN) + (js2-make-binary js2-ASSIGN name 'js2-parse-assign-expr t))) + (defun js2-parse-prop-name (tt) (cond ;; Literal string keys: {'foo': 'bar'} diff --git a/tests/parser.el b/tests/parser.el index c65be2c3b..51b03eb56 100644 --- a/tests/parser.el +++ b/tests/parser.el @@ -178,6 +178,26 @@ the test." (js2-deftest-parse destruct-in-catch-clause "try {\n} catch ({a, b}) {\n a + b;\n}") +(js2-deftest-parse destruct-with-initializer-in-object + "var {a, b = 2, c} = {};") + +(js2-deftest-parse destruct-with-initializer-in-array + "var [a, b = 2, c] = [];") + +(js2-deftest-parse destruct-non-name-target-is-error + "var {1=1} = {};" :syntax-error "1" :errors-count 1) + +(js2-deftest-parse destruct-with-initializer-in-function-arguments + "function f({a, b = 1, c}, [d, e = 1, f]) {\n}") + +(js2-deftest-parse destruct-name-conflict-is-error-in-object + "\"use strict\";\nvar {a=1,a=2} = {};" :syntax-error "a" :errors-count 1) + +(js2-deftest destruct-name-conflict-is-warning-in-array "\"use strict\";\nvar [a=1,a=2] = [];" + (js2-mode) + (should (equal '("msg.var.redecl" "a") + (caar js2-parsed-warnings)))) + ;;; Object literals (js2-deftest-parse object-literal-shorthand -- 2.39.2