Skip to content

Commit

Permalink
Implement object literal rest/spread (...)
Browse files Browse the repository at this point in the history
As discussed in #245, this syntax is not part of ES6/2015, but it is
a stage 2 proposal, meaning the committee expects it to be included
in the standard.  It's already seeing heavy use by e.g. the React
community.

This change causes `...expr` in an object literal to be parsed as a
unary-node element of an object-node, and modifies places where an
element of an object-node is expected to be an infix-node.  Note that
only weak typing assumptions are made already about object-node elems.

In addition, this change implements proper name extraction in
object literal destructuring.  The cases are:

1. `let {a} = x;`
2. `let {a: b} = x;` (should define `b`)
3. `let {a = b} = x;` (should define `a`)
4. `let {...a} = x;` (should define `a`)

This change fixes case (2), which was broken, and adds case (4).
  • Loading branch information
dgreensp committed Nov 11, 2015
1 parent 7fe7165 commit 2a904e0
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 34 deletions.
85 changes: 51 additions & 34 deletions js2-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -7445,22 +7445,23 @@ We do a depth-first traversal of NODE. For any functions we find,
we append the property name to QNAME, then call `js2-record-imenu-entry'."
(let (right)
(dolist (e (js2-object-node-elems node)) ; e is a `js2-object-prop-node'
(let ((left (js2-infix-node-left e))
;; Element positions are relative to the parent position.
(pos (+ pos (js2-node-pos e))))
(cond
;; foo: function() {...}
((js2-function-node-p (setq right (js2-infix-node-right e)))
(when (js2-prop-node-name left)
;; As a policy decision, we record the position of the property,
;; not the position of the `function' keyword, since the property
;; is effectively the name of the function.
(js2-record-imenu-entry right (append qname (list left)) pos)))
;; foo: {object-literal} -- add foo to qname, offset position, and recurse
((js2-object-node-p right)
(js2-record-object-literal right
(append qname (list (js2-infix-node-left e)))
(+ pos (js2-node-pos right)))))))))
(when (js2-infix-node-p e)
(let ((left (js2-infix-node-left e))
;; Element positions are relative to the parent position.
(pos (+ pos (js2-node-pos e))))
(cond
;; foo: function() {...}
((js2-function-node-p (setq right (js2-infix-node-right e)))
(when (js2-prop-node-name left)
;; As a policy decision, we record the position of the property,
;; not the position of the `function' keyword, since the property
;; is effectively the name of the function.
(js2-record-imenu-entry right (append qname (list left)) pos)))
;; foo: {object-literal} -- add foo to qname, offset position, and recurse
((js2-object-node-p right)
(js2-record-object-literal right
(append qname (list (js2-infix-node-left e)))
(+ pos (js2-node-pos right))))))))))

(defun js2-node-top-level-decl-p (node)
"Return t if NODE's name is defined in the top-level scope.
Expand Down Expand Up @@ -8068,13 +8069,20 @@ declared; probably to check them for errors."
(list node)))
((js2-object-node-p node)
(dolist (elem (js2-object-node-elems node))
;; 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
(js2-infix-node-left elem)
decl-type face ignore-not-in-block)
name-nodes)))
(let ((subexpr (cond
((and (js2-infix-node-p elem)
(= js2-ASSIGN (js2-infix-node-type elem)))
(js2-infix-node-left elem))
((and (js2-infix-node-p elem)
(= js2-COLON (js2-infix-node-type elem)))
(js2-infix-node-right elem))
((and (js2-unary-node-p elem)
(= js2-TRIPLEDOT (js2-unary-node-type elem)))
(js2-unary-node-operand elem)))))
(when subexpr
(push (js2-define-destruct-symbols
subexpr 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))
Expand Down Expand Up @@ -10700,16 +10708,19 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed."
`js2-method-node') as a string, or nil if it can't be
represented as a string (e.g., the key is computed by an
expression)."
(let ((key (js2-infix-node-left property-node)))
(when (js2-computed-prop-name-node-p key)
(setq key (js2-computed-prop-name-node-expr key)))
(cond
((js2-name-node-p key)
(js2-name-node-name key))
((js2-string-node-p key)
(js2-string-node-value key))
((js2-number-node-p key)
(js2-number-node-value key)))))
(cond
((js2-unary-node-p property-node) nil) ;; {...foo}
(t
(let ((key (js2-infix-node-left property-node)))
(when (js2-computed-prop-name-node-p key)
(setq key (js2-computed-prop-name-node-expr key)))
(cond
((js2-name-node-p key)
(js2-name-node-name key))
((js2-string-node-p key)
(js2-string-node-value key))
((js2-number-node-p key)
(js2-number-node-value key)))))))

(defun js2-parse-object-literal-elems (&optional class-p)
(let ((pos (js2-current-token-beg))
Expand Down Expand Up @@ -10744,7 +10755,13 @@ expression)."
(setq previous-token (js2-current-token)
tt (js2-get-prop-name-token))))
(cond
;; Found a property (of any sort)
;; Rest/spread (...expr)
((and (>= js2-language-version 200)
(not class-p) (not static) (not previous-token)
(= js2-TRIPLEDOT tt))
(setq after-comma nil
elem (js2-make-unary js2-TRIPLEDOT 'js2-parse-assign-expr)))
;; Found a key/value property (of any sort)
((member tt (list js2-NAME js2-STRING js2-NUMBER js2-LB))
(setq after-comma nil
elem (js2-parse-named-prop tt pos previous-token))
Expand Down
6 changes: 6 additions & 0 deletions tests/parser.el
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@ the test."
(js2-deftest-parse array-destructure-expr-default
"let [[x] = [3]] = y;")

(js2-deftest-parse spread-in-object-literal
"f({x, y, ...z});")

(js2-deftest-parse rest-in-object-literal
"const {x, y, ...z} = f();")

;;; Arrow functions

(js2-deftest-parse arrow-function-with-empty-args-and-no-curlies
Expand Down

0 comments on commit 2a904e0

Please sign in to comment.