emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master ec7932d 047/110: Merge pull request #283 from XeCycle/mast


From: Dmitry Gutov
Subject: [elpa] master ec7932d 047/110: Merge pull request #283 from XeCycle/master-with-async
Date: Thu, 23 Jun 2016 01:12:56 +0000 (UTC)

branch: master
commit ec7932da4ca5aaff8b8ab26ad27b361fa18d5d54
Merge: f652d17 d0e4323
Author: Dmitry Gutov <address@hidden>
Commit: Dmitry Gutov <address@hidden>

    Merge pull request #283 from XeCycle/master-with-async
    
    Adapt old async to master & async methods
---
 NEWS.md         |    9 +--
 js2-mode.el     |  175 ++++++++++++++++++++++++++++++++++++++++---------------
 tests/parser.el |   61 +++++++++++++++++++
 3 files changed, 195 insertions(+), 50 deletions(-)

diff --git a/NEWS.md b/NEWS.md
index 33ba376..a916356 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -4,15 +4,16 @@
 
 * Object properties are highlighted using a different face:
   `js2-object-property`, which has no color by default.
-* `js2-getter-setter-node` is renamed to `js2-method-node`, together
-  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.
+* `js2-getter-setter-node` is renamed to `js2-method-node`, together with
+  its related functions.  It already handles generator methods, and we
+  added support for async methods (see below), 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.
 * New mode: `js2-jsx-mode`, deriving from `js2-mode`.  Supports indentation of
   JSXElement expressions wrapped within parentheses or as function arguments.
   Indentation is customizable via `sgml-attribute-offset`.
+* Experimental support for ES7 stage 3 async/await.
 
 ## 20150909
 
diff --git a/js2-mode.el b/js2-mode.el
index 1cf19b8..c7af462 100644
--- a/js2-mode.el
+++ b/js2-mode.el
@@ -617,7 +617,9 @@ which doesn't seem particularly useful, but Rhino permits 
it."
 (defvar js2-NO_SUBS_TEMPLATE 167) ; template literal without substitutions
 (defvar js2-TAGGED_TEMPLATE 168)  ; tagged template literal
 
-(defconst js2-num-tokens (1+ js2-TAGGED_TEMPLATE))
+(defvar js2-AWAIT 169)  ; await (pseudo keyword)
+
+(defconst js2-num-tokens (1+ js2-AWAIT))
 
 (defconst js2-debug-print-trees nil)
 
@@ -1363,6 +1365,9 @@ the correct number of ARGS must be provided."
 (js2-msg "msg.yield.parenthesized"
          "yield expression must be parenthesized.")
 
+(js2-msg "msg.bad.await"
+         "await must be in async functions.")
+
 ;; NativeGlobal
 (js2-msg "msg.cant.call.indirect"
           "Function '%s' must be called directly, and not by way of a "
@@ -3257,6 +3262,7 @@ a `js2-label-node' or the innermost enclosing loop.")
                                                           params rest-p
                                                           body
                                                           generator-type
+                                                          async
                                                           lp rp)))
   "AST node for a function declaration.
 The `params' field is a Lisp list of nodes.  Each node is either a simple
@@ -3273,6 +3279,7 @@ The `params' field is a Lisp list of nodes.  Each node is 
either a simple
   ignore-dynamic   ; ignore value of the dynamic-scope flag (interpreter only)
   needs-activation ; t if we need an activation object for this frame
   generator-type   ; STAR, LEGACY, COMPREHENSION or nil
+  async            ; t if the function is defined as `async function`
   member-expr)     ; nonstandard Ecma extension from Rhino
 
 (put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node)
@@ -3294,8 +3301,10 @@ The `params' field is a Lisp list of nodes.  Each node 
is either a simple
          (rest-p (js2-function-node-rest-p n))
          (body (js2-function-node-body n))
          (expr (not (eq (js2-function-node-form n) 'FUNCTION_STATEMENT))))
-    (unless (or method arrow)
-      (insert pad "function")
+    (unless method
+      (insert pad)
+      (when (js2-function-node-async n) (insert "async "))
+      (unless arrow (insert "function"))
       (when (eq (js2-function-node-generator-type n) 'STAR)
         (insert "*")))
     (when name
@@ -3466,6 +3475,7 @@ The type field inherited from `js2-node' holds the 
operator."
                (cons js2-TYPEOF "typeof")
                (cons js2-INSTANCEOF "instanceof")
                (cons js2-DELPROP "delete")
+               (cons js2-AWAIT "await")
                (cons js2-COMMA ",")
                (cons js2-COLON ":")
                (cons js2-OR "||")
@@ -3546,7 +3556,7 @@ The type field holds the actual assignment operator.")
                                                        len operand)))
   "AST node type for unary operator nodes.
 The type field can be NOT, BITNOT, POS, NEG, INC, DEC,
-TYPEOF, DELPROP or TRIPLEDOT.  For INC or DEC, a 'postfix node
+TYPEOF, DELPROP, TRIPLEDOT or AWAIT.  For INC or DEC, a 'postfix node
 property is added if the operator follows the operand."
   operand)  ; a `js2-node' expression
 
@@ -3566,7 +3576,8 @@ property is added if the operator follows the operand."
     (unless postfix
       (insert op))
     (if (or (= tt js2-TYPEOF)
-            (= tt js2-DELPROP))
+            (= tt js2-DELPROP)
+            (= tt js2-AWAIT))
         (insert " "))
     (js2-print-ast (js2-unary-node-operand n) 0)
     (when postfix
@@ -3951,13 +3962,12 @@ both fields have the same value.")
 (cl-defstruct (js2-method-node
                (:include js2-infix-node)
                (:constructor nil)
-               (:constructor make-js2-method-node (&key type ; GET, SET, or 
FUNCTION
-                                                        (pos js2-ts-cursor)
+               (:constructor make-js2-method-node (&key (pos js2-ts-cursor)
                                                         len left right)))
   "AST node for a method in an object literal or a class body.
 The `left' field is the `js2-name-node' naming the method.
 The `right' field is always an anonymous `js2-function-node' with a node
-property `METHOD_TYPE' set to js2-GET, js2-SET, or js2-FUNCTION. ")
+property `METHOD_TYPE' set to 'GET or 'SET. ")
 
 (put 'cl-struct-js2-method-node 'js2-visitor 'js2-visit-infix-node)
 (put 'cl-struct-js2-method-node 'js2-printer 'js2-print-method)
@@ -3965,10 +3975,14 @@ property `METHOD_TYPE' set to js2-GET, js2-SET, or 
js2-FUNCTION. ")
 (defun js2-print-method (n i)
   (let* ((pad (js2-make-pad i))
          (left (js2-method-node-left n))
-         (right (js2-method-node-right n)))
+         (right (js2-method-node-right n))
+         (type (js2-node-get-prop right 'METHOD_TYPE)))
     (insert pad)
-    (if (/= (js2-node-type n) js2-FUNCTION)
-        (insert (if (= (js2-node-type n) js2-GET) "get " "set ")))
+    (when type
+      (insert (cdr (assoc type '((GET . "get ")
+                                 (SET . "set ")
+                                 (ASYNC . "async ")
+                                 (FUNCTION . ""))))))
     (when (and (js2-function-node-p right)
                (eq 'STAR (js2-function-node-generator-type right)))
       (insert "*"))
@@ -5134,6 +5148,8 @@ You should use `js2-print-tree' instead of this function."
           (or (js2-node-has-side-effects expr)
               (when (js2-string-node-p expr)
                 (member (js2-string-node-value expr) '("use strict" "use 
asm"))))))
+       ((= tt js2-AWAIT)
+        (js2-node-has-side-effects (js2-unary-node-operand node)))
        ((= tt js2-COMMA)
         (js2-node-has-side-effects (js2-infix-node-right node)))
        ((or (= tt js2-AND)
@@ -7709,12 +7725,43 @@ Returns nil and consumes nothing if MATCH is not the 
next token."
 (defun js2-match-contextual-kwd (name)
   "Consume and return t if next token is `js2-NAME', and its
 string is NAME.  Returns nil and keeps current token otherwise."
-  (if (or (/= (js2-get-token) js2-NAME)
-          (not (string= (js2-current-token-string) name)))
-      (progn
-        (js2-unget-token)
-        nil)
+  (if (js2-contextual-kwd-p (progn (js2-get-token)
+                                   (js2-current-token))
+                            name)
+      (progn (js2-record-face 'font-lock-keyword-face) t)
+    (js2-unget-token)
+    nil))
+
+(defun js2-contextual-kwd-p (token name)
+  "Return t if TOKEN is `js2-NAME', and its string is NAME."
+  (and (= (js2-token-type token) js2-NAME)
+       (string= (js2-token-string token) name)))
+
+(defun js2-match-async-function ()
+  (when (and (js2-contextual-kwd-p (js2-current-token) "async")
+             (= (js2-peek-token) js2-FUNCTION))
     (js2-record-face 'font-lock-keyword-face)
+    (js2-get-token)
+    t))
+
+(defun js2-match-async-arrow-function ()
+  (when (and (js2-contextual-kwd-p (js2-current-token) "async")
+             (/= (js2-peek-token) js2-FUNCTION))
+    (js2-record-face 'font-lock-keyword-face)
+    (js2-get-token)
+    t))
+
+(defun js2-match-await ()
+  (when (and (= tt js2-NAME)
+             (js2-contextual-kwd-p (js2-current-token) "await"))
+    (js2-record-face 'font-lock-keyword-face)
+    (let ((beg (js2-current-token-beg))
+          (end (js2-current-token-end)))
+      (js2-get-token)
+      (unless (and (js2-inside-function)
+                   (js2-function-node-async js2-current-script-or-fn))
+        (js2-report-error "msg.bad.await" nil
+                          beg (- end beg))))
     t))
 
 (defun js2-get-prop-name-token ()
@@ -8151,7 +8198,7 @@ Last token scanned is the close-curly for the function 
body."
                                   (js2-name-node-name name) pos end)
         (js2-add-strict-warning "msg.anon.no.return.value" nil pos end)))))
 
-(defun js2-parse-function-stmt ()
+(defun js2-parse-function-stmt (&optional async-p)
   (let ((pos (js2-current-token-beg))
         (star-p (js2-match-token js2-MUL)))
     (js2-must-match-name "msg.unnamed.function.stmt")
@@ -8159,28 +8206,31 @@ Last token scanned is the close-curly for the function 
body."
           pn member-expr)
       (cond
        ((js2-match-token js2-LP)
-        (js2-parse-function 'FUNCTION_STATEMENT pos star-p name))
+        (js2-parse-function 'FUNCTION_STATEMENT pos star-p async-p name))
        (js2-allow-member-expr-as-function-name
         (setq member-expr (js2-parse-member-expr-tail nil name))
         (js2-parse-highlight-member-expr-fn-name member-expr)
         (js2-must-match js2-LP "msg.no.paren.parms")
-        (setf pn (js2-parse-function 'FUNCTION_STATEMENT pos star-p)
+        (setf pn (js2-parse-function 'FUNCTION_STATEMENT pos star-p async-p)
               (js2-function-node-member-expr pn) member-expr)
         pn)
        (t
         (js2-report-error "msg.no.paren.parms")
         (make-js2-error-node))))))
 
-(defun js2-parse-function-expr ()
+(defun js2-parse-async-function-stmt ()
+  (js2-parse-function-stmt t))
+
+(defun js2-parse-function-expr (&optional async-p)
   (let ((pos (js2-current-token-beg))
         (star-p (js2-match-token js2-MUL))
         name)
     (when (js2-match-token js2-NAME)
       (setq name (js2-create-name-node t)))
     (js2-must-match js2-LP "msg.no.paren.parms")
-    (js2-parse-function 'FUNCTION_EXPRESSION pos star-p name)))
+    (js2-parse-function 'FUNCTION_EXPRESSION pos star-p async-p name)))
 
-(defun js2-parse-function-internal (function-type pos star-p &optional name)
+(defun js2-parse-function-internal (function-type pos star-p &optional async-p 
name)
   (let (fn-node lp)
     (if (= (js2-current-token-type) js2-LP) ; eventually matched LP?
         (setq lp (js2-current-token-beg)))
@@ -8188,7 +8238,8 @@ Last token scanned is the close-curly for the function 
body."
                                           :name name
                                           :form function-type
                                           :lp (if lp (- lp pos))
-                                          :generator-type (and star-p 'STAR)))
+                                          :generator-type (and star-p 'STAR)
+                                          :async async-p))
     (when name
       (js2-set-face (js2-node-pos name) (js2-node-end name)
                     'font-lock-function-name-face 'record)
@@ -8243,7 +8294,7 @@ Last token scanned is the close-curly for the function 
body."
     (setf (js2-scope-parent-scope fn-node) js2-current-scope)
     fn-node))
 
-(defun js2-parse-function (function-type pos star-p &optional name)
+(defun js2-parse-function (function-type pos star-p &optional async-p name)
   "Function parser.  FUNCTION-TYPE is a symbol, POS is the
 beginning of the first token (function keyword, unless it's an
 arrow function), NAME is js2-name-node."
@@ -8259,7 +8310,7 @@ arrow function), NAME is js2-name-node."
       (setq ts-state (make-js2-ts-state))
       (setq continue (catch 'reparse
                        (setq fn-node (js2-parse-function-internal
-                                      function-type pos star-p name))
+                                      function-type pos star-p async-p name))
                        ;; Don't continue.
                        nil))
       (when continue
@@ -8369,9 +8420,12 @@ node are given relative start positions and correct 
lengths."
 (defun js2-statement-helper ()
   (let* ((tt (js2-get-token))
          (first-tt tt)
+         (async-stmt (js2-match-async-function))
          (parser (if (= tt js2-ERROR)
                      #'js2-parse-semi
-                   (aref js2-parsers tt)))
+                   (if async-stmt
+                       #'js2-parse-async-function-stmt
+                     (aref js2-parsers tt))))
          pn)
     ;; If the statement is set, then it's been told its label by now.
     (and js2-labeled-stmt
@@ -8380,7 +8434,8 @@ node are given relative start positions and correct 
lengths."
     (setq pn (funcall parser))
     ;; Don't do auto semi insertion for certain statement types.
     (unless (or (memq first-tt js2-no-semi-insertion)
-                (js2-labeled-stmt-node-p pn))
+                (js2-labeled-stmt-node-p pn)
+                async-stmt)
       (js2-auto-insert-semicolon pn))
     pn))
 
@@ -9583,9 +9638,20 @@ If NODE is non-nil, it is the AST node associated with 
the symbol."
   (let ((tt (js2-get-token))
         (pos (js2-current-token-beg))
         pn left right op-pos
-        ts-state recorded-identifiers parsed-errors)
+        ts-state recorded-identifiers parsed-errors
+        async-p)
     (if (= tt js2-YIELD)
         (js2-parse-return-or-yield tt t)
+      ;; TODO(mooz): Bit confusing.
+      ;; If we meet `async` token and it's not part of `async
+      ;; function`, then this `async` is for a succeeding async arrow
+      ;; function.
+      ;; Since arrow function parsing doesn't rely on neither
+      ;; `js2-parse-function-stmt' nor `js2-parse-function-expr' that
+      ;; interpret `async` token, we trash `async` and just remember
+      ;; we met `async` keyword to `async-p'.
+      (when (js2-match-async-arrow-function)
+        (setq async-p t))
       ;; Save the tokenizer state in case we find an arrow function
       ;; and have to rewind.
       (setq ts-state (make-js2-ts-state)
@@ -9621,7 +9687,7 @@ If NODE is non-nil, it is the AST node associated with 
the symbol."
         (js2-ts-seek ts-state)
         (setq js2-recorded-identifiers recorded-identifiers
               js2-parsed-errors parsed-errors)
-        (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) 
nil)))
+        (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) 
nil async-p)))
        (t
         (js2-unget-token)))
       pn)))
@@ -9810,6 +9876,18 @@ to parse the operand (for prefix operators)."
     (js2-node-add-children pn expr)
     pn))
 
+(defun js2-make-await ()
+  "Make an await node."
+  (let* ((pos (js2-current-token-beg))
+         (expr (js2-parse-unary-expr))
+         (end (js2-node-end expr))
+         pn)
+    (setq pn (make-js2-await-node :pos pos
+                                  :len (- end pos)
+                                  :operand expr))
+    (js2-node-add-children pn expr)
+    pn))
+
 (defconst js2-incrementable-node-types
   (list js2-NAME js2-GETPROP js2-GETELEM js2-GET_REF js2-CALL)
   "Node types that can be the operand of a ++ or -- operator.")
@@ -9851,6 +9929,8 @@ to parse the operand (for prefix operators)."
      ((= tt js2-DELPROP)
       (js2-get-token)
       (js2-make-unary js2-DELPROP 'js2-parse-unary-expr))
+     ((js2-match-await)
+      (js2-make-unary js2-AWAIT 'js2-parse-unary-expr))
      ((= tt js2-ERROR)
       (js2-get-token)
       (make-js2-error-node))  ; try to continue
@@ -10240,6 +10320,8 @@ array-literals, array comprehensions and regular 
expressions."
       (js2-parse-class-expr))
      ((= tt js2-FUNCTION)
       (js2-parse-function-expr))
+     ((js2-match-async-function)
+      (js2-parse-function-expr t))
      ((= tt js2-LB)
       (js2-parse-array-comp-or-literal))
      ((= tt js2-LC)
@@ -10649,12 +10731,11 @@ expression)."
                  (= js2-MUL tt))
         (setq previous-token (js2-current-token)
               tt (js2-get-prop-name-token)))
-      ;; Handle 'get' or 'set' keywords
+      ;; Handle getter, setter and async methods
       (let ((prop (js2-current-token-string)))
         (when (and (>= js2-language-version 200)
                    (= js2-NAME tt)
-                   (or (string= prop "get")
-                       (string= prop "set"))
+                   (member prop '("get" "set" "async"))
                    (member (js2-peek-token)
                            (list js2-NAME js2-STRING js2-NUMBER js2-LB)))
           (setq previous-token (js2-current-token)
@@ -10707,8 +10788,11 @@ expression)."
                            ;; But make an exception for getter / setter pairs.
                            (not (and (js2-method-node-p elem)
                                      (js2-method-node-p previous-elem)
-                                     (/= (js2-method-node-type elem)
-                                         (js2-method-node-type 
previous-elem))))))
+                                     (let ((type (js2-node-get-prop 
(js2-method-node-right elem) 'METHOD_TYPE))
+                                           (previous-type (js2-node-get-prop 
(js2-method-node-right previous-elem) 'METHOD_TYPE)))
+                                       (and (member type '(GET SET))
+                                            (member previous-type '(GET SET))
+                                            (not (eq type previous-type))))))))
                     elems))
           (js2-report-error "msg.dup.obj.lit.prop.strict"
                             elem-key-string
@@ -10728,11 +10812,10 @@ When `js2-is-in-destructuring' is t, forms like {a, 
b, c} will be permitted."
                              (if (= (js2-token-type previous-token) js2-MUL)
                                  "*"
                                (js2-token-string previous-token)))))
-    (when (or (string= prop "get")
-              (string= prop "set"))
+    (when (member prop '("get" "set" "async"))
       (js2-set-face (js2-token-beg previous-token)
                     (js2-token-end previous-token)
-                    'font-lock-keyword-face 'record))  ; get/set
+                    'font-lock-keyword-face 'record))  ; get/set/async
     (cond
      ;; method definition: {f() {...}}
      ((and (= (js2-peek-token) js2-LP)
@@ -10837,7 +10920,8 @@ string or expression."
   "Parse method property in an object literal or a class body.
 JavaScript syntax is:
 
-  { foo(...) {...}, get foo() {...}, set foo(x) {...}, *foo(...) {...} }
+  { foo(...) {...}, get foo() {...}, set foo(x) {...}, *foo(...) {...},
+    async foo(...) {...} }
 
 and expression closure style is also supported
 
@@ -10846,12 +10930,12 @@ and expression closure style is also supported
 POS is the start position of the `get' or `set' keyword.
 PROP is the `js2-name-node' representing the property name.
 TYPE-STRING is a string `get', `set', `*', or nil, indicating a found keyword."
-  (let ((type (cond
-               ((string= "get" type-string) js2-GET)
-               ((string= "set" type-string) js2-SET)
-               (t js2-FUNCTION)))
-        result end
-        (fn (js2-parse-function-expr)))
+  (let* ((type (or (cdr (assoc type-string '(("get" . GET)
+                                             ("set" . SET)
+                                             ("async" . ASYNC))))
+                   'FUNCTION))
+         result end
+         (fn (js2-parse-function-expr (eq type 'ASYNC))))
     ;; it has to be an anonymous function, as we already parsed the name
     (if (/= (js2-node-type fn) js2-FUNCTION)
         (js2-report-error "msg.bad.prop")
@@ -10861,8 +10945,7 @@ TYPE-STRING is a string `get', `set', `*', or nil, 
indicating a found keyword."
     (when (string= type-string "*")
       (setf (js2-function-node-generator-type fn) 'STAR))
     (setq end (js2-node-end fn)
-          result (make-js2-method-node :type type
-                                       :pos pos
+          result (make-js2-method-node :pos pos
                                        :len (- end pos)
                                        :left prop
                                        :right fn))
diff --git a/tests/parser.el b/tests/parser.el
index e97000d..7c4daa8 100644
--- a/tests/parser.el
+++ b/tests/parser.el
@@ -435,6 +435,67 @@ the test."
 (js2-deftest-parse parse-generator-comp-with-yield-inside-function-is-ok
   "(for (x of []) function*() {  yield x;\n});")
 
+;;; Async
+
+(js2-deftest-parse async-function-statement
+  "async function foo() {\n}")
+
+(js2-deftest-parse async-function-statement-inside-block
+  "if (true) {\n  async function foo() {\n  }\n}")
+
+(js2-deftest-parse async-function-expression-statements-are-verboten
+  "async function() {}" :syntax-error "(")
+
+(js2-deftest-parse async-named-function-expression
+  "a = async function b() {};")
+
+(js2-deftest-parse async-arrow-function-expression
+  "a = async (b) => {  b;\n};")
+
+(js2-deftest-parse async-method-in-object-literal
+  "({async f() {}});")
+
+(js2-deftest-parse async-method-in-class-body
+  "class C {\n  async foo() {}\n}")
+
+(js2-deftest-parse static-async-method-in-class-body
+  "class C {\n  static async foo() {}\n}")
+
+(js2-deftest-parse async-method-allow-await
+  "({async f() {  await x;\n}});")
+
+;;; Await
+
+(js2-deftest-parse await-is-ok "async function foo() {\n  await bar();\n}")
+
+(js2-deftest-parse await-inside-assignment-is-ok
+                   "async function foo() {\n  var result = await bar();\n}")
+
+(js2-deftest-parse await-inside-array-is-ok
+                   "async function foo() {\n  var results = [await bar(), 
await baz()];\n}")
+
+(js2-deftest-parse await-inside-non-async-function-is-not-ok
+                   "function foo() {\n  await bar();\n}"
+                   :syntax-error "await")
+
+(js2-deftest-parse await-inside-non-async-arrow-function-is-not-ok
+                   "a = () => {  await bar();\n}"
+                   :syntax-error "await")
+
+;;; 'async' and 'await' are contextual keywords
+
+(js2-deftest-parse async-can-be-var-name
+  "var async = 3;")
+
+(js2-deftest-parse async-can-be-function-name
+  "function async() {\n}")
+
+(js2-deftest-parse await-can-be-var-name
+  "var await = 3;")
+
+(js2-deftest-parse await-can-be-function-name
+  "function await() {\n}")
+
 ;;; Numbers
 
 (js2-deftest-parse decimal-starting-with-zero "081;" :reference "81;")



reply via email to

[Prev in Thread] Current Thread [Next in Thread]