;; Dmitry Gutov <dgutov@yandex.ru>
;; URL: https://github.com/mooz/js2-mode/
;; http://code.google.com/p/js2-mode/
- ;; Version: 20130619
+ ;; Version: 20131106
;; Keywords: languages, javascript
;; Package-Requires: ((emacs "24.1"))
;; files, and not for editing JavaScript within <script> tags or templates.
;; The project page on GitHub is used for development and issue tracking.
- ;; The original homepage at Google Code is mentioned here for posterity, it has
- ;; outdated information and is mostly unmaintained.
+ ;; The original homepage at Google Code has outdated information and is mostly
+ ;; unmaintained.
;;; Code:
:type 'symbol)
(js2-mark-safe-local 'js2-pretty-multiline-declarations 'symbolp)
+ (defcustom js2-indent-switch-body nil
+ "When nil, case labels are indented on the same level as the
+ containing switch statement. Otherwise, all lines inside
+ switch statement body are indented one additional level."
+ :type 'boolean
+ :group 'js2-mode)
+ (js2-mark-safe-local 'js2-indent-case-same-as-switch 'booleanp)
+
(defcustom js2-idle-timer-delay 0.2
"Delay in secs before re-parsing after user makes changes.
Multiplied by `js2-dynamic-idle-timer-adjust', which see."
(defvar ,name ,value ,comment)
(make-variable-buffer-local ',name)))
- ;; We record the start and end position of each token.
- (js2-deflocal js2-token-beg 1)
- (js2-deflocal js2-token-end -1)
-
(defvar js2-EOF_CHAR -1
"Represents end of stream. Distinct from js2-EOF token type.")
(defvar js2-DEBUGGER 159)
(defvar js2-COMMENT 160)
- (defvar js2-ENUM 161) ; for "enum" reserved word
- (defvar js2-TRIPLEDOT 162) ; for rest parameter
+ (defvar js2-ENUM 161) ; for "enum" reserved word
+ (defvar js2-TRIPLEDOT 162) ; for rest parameter
+ (defvar js2-ARROW 163) ; function arrow (=>)
- (defconst js2-num-tokens (1+ js2-TRIPLEDOT))
+ (defconst js2-num-tokens (1+ js2-ARROW))
(defconst js2-debug-print-trees nil)
"Token stream buffer-local variable.
Indicates stuff other than whitespace since start of line.")
- (js2-deflocal js2-ts-regexp-flags nil
- "Token stream buffer-local variable.")
-
- (js2-deflocal js2-ts-string ""
- "Token stream buffer-local variable.
- Last string scanned.")
-
- (js2-deflocal js2-ts-number nil
- "Token stream buffer-local variable.
- Last literal number scanned.")
-
(js2-deflocal js2-ts-hit-eof nil
"Token stream buffer-local variable.")
+ ;; FIXME: Unused.
(js2-deflocal js2-ts-line-start 0
"Token stream buffer-local variable.")
(js2-deflocal js2-ts-lineno 1
"Token stream buffer-local variable.")
+ ;; FIXME: Unused.
(js2-deflocal js2-ts-line-end-char -1
"Token stream buffer-local variable.")
"Token stream buffer-local variable.
Current scan position.")
+ ;; FIXME: Unused.
(js2-deflocal js2-ts-is-xml-attribute nil
"Token stream buffer-local variable.")
"Token stream buffer-local variable.
List of chars built up while scanning various tokens.")
- (js2-deflocal js2-ts-comment-type nil
- "Token stream buffer-local variable.")
+ (defstruct (js2-token
+ (:constructor nil)
+ (:constructor make-js2-token (beg)))
+ "Value returned from the token stream."
+ (type js2-EOF)
+ (beg 1)
+ (end -1)
+ (string "")
+ number
+ regexp-flags
+ comment-type
+ follows-eol-p)
+
+ (defstruct (js2-ts-state
+ (:constructor make-js2-ts-state (&key (lineno js2-ts-lineno)
+ (cursor js2-ts-cursor)
+ (tokens (copy-sequence js2-ti-tokens))
+ (tokens-cursor js2-ti-tokens-cursor)
+ (lookahead js2-ti-lookahead))))
+ lineno
+ cursor
+ tokens
+ tokens-cursor
+ lookahead)
;;; Parser variables
;;; Parser instance variables (buffer-local vars for js2-parse)
- (defconst js2-clear-ti-mask #xFFFF
- "Mask to clear token information bits.")
-
(defconst js2-ti-after-eol (lsh 1 16)
"Flag: first token of the source line.")
- (defconst js2-ti-check-label (lsh 1 17)
- "Flag: indicates to check for label.")
-
;; Inline Rhino's CompilerEnvirons vars as buffer-locals.
(js2-deflocal js2-compiler-generate-debug-info t)
;; SKIP: ts (we just call `js2-init-scanner' and use its vars)
- (js2-deflocal js2-current-flagged-token js2-EOF)
- (js2-deflocal js2-current-token js2-EOF)
-
;; SKIP: node factory - we're going to just call functions directly,
;; and eventually go to a unified AST format.
;;; ...end of per function variables
- ;; Without 2-token lookahead, labels are a problem.
- ;; These vars store the token info of the last matched name,
- ;; iff it wasn't the last matched token. Only valid in some contexts.
- (defvar js2-prev-name-token-start nil)
- (defvar js2-prev-name-token-string nil)
-
- (defsubst js2-save-name-token-data (pos name)
- (setq js2-prev-name-token-start pos
- js2-prev-name-token-string name))
-
;; These flags enumerate the possible ways a statement/function can
;; terminate. These flags are used by endCheck() and by the Parser to
;; detect inconsistent return usage.
(defconst js2-end-drops-off #x1)
(defconst js2-end-returns #x2)
(defconst js2-end-returns-value #x4)
- (defconst js2-end-yields #x8)
;; Rhino awkwardly passes a statementLabel parameter to the
;; statementHelper() function, the main statement parser, which
"Line terminator is not allowed between the throw "
"keyword and throw expression.")
+ (js2-msg "msg.unnamed.function.stmt" ; added by js2-mode
+ "function statement requires a name")
+
(js2-msg "msg.no.paren.parms"
"missing ( before function parameters.")
(js2-msg "msg.param.after.rest" ; added by js2-mode
"parameter after rest parameter")
+ (js2-msg "msg.bad.arrow.args" ; added by js2-mode
+ "invalid arrow-function arguments (parentheses around the arrow-function may help)")
+
(js2-msg "msg.no.brace.body"
"missing '{' before function body")
"return statement is inconsistent with previous usage")
(js2-msg "msg.generator.returns"
- "TypeError: generator function '%s' returns a value")
+ "TypeError: legacy generator function '%s' returns a value")
(js2-msg "msg.anon.generator.returns"
- "TypeError: anonymous generator function returns a value")
+ "TypeError: anonymous legacy generator function returns a value")
(js2-msg "msg.syntax"
"syntax error")
(js2-msg "msg.yield.closing"
"Yield from closing generator")
+ ;;; Tokens Buffer
+
+ (defconst js2-ti-max-lookahead 2)
+ (defconst js2-ti-ntokens (1+ js2-ti-max-lookahead))
+
+ ;; Have to call `js2-init-scanner' to initialize the values.
+ (js2-deflocal js2-ti-tokens nil)
+ (js2-deflocal js2-ti-tokens-cursor nil)
+ (js2-deflocal js2-ti-lookahead nil)
+
+ (defun js2-new-token (offset)
+ (let ((token (make-js2-token (+ offset js2-ts-cursor))))
+ (setq js2-ti-tokens-cursor (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens))
+ (aset js2-ti-tokens js2-ti-tokens-cursor token)
+ token))
+
+ (defsubst js2-current-token ()
+ (aref js2-ti-tokens js2-ti-tokens-cursor))
+
+ (defsubst js2-current-token-string ()
+ (js2-token-string (js2-current-token)))
+
+ (defsubst js2-current-token-type ()
+ (js2-token-type (js2-current-token)))
+
+ (defsubst js2-current-token-beg ()
+ (js2-token-beg (js2-current-token)))
+
+ (defsubst js2-current-token-end ()
+ (js2-token-end (js2-current-token)))
+
+ (defun js2-current-token-len ()
+ (let ((token (js2-current-token)))
+ (- (js2-token-end token)
+ (js2-token-beg token))))
+
+ (defun js2-ts-seek (state)
+ (setq js2-ts-lineno (js2-ts-state-lineno state)
+ js2-ts-cursor (js2-ts-state-cursor state)
+ js2-ti-tokens (js2-ts-state-tokens state)
+ js2-ti-tokens-cursor (js2-ts-state-tokens-cursor state)
+ js2-ti-lookahead (js2-ts-state-lookahead state)))
+
;;; Utilities
(defun js2-delete-if (predicate list)
;; easiest thing to do is get the context info from the last token.
(defun js2-record-parse-error (msg &optional arg pos len)
(push (list (list msg arg)
- (or pos js2-token-beg)
- (or len (- js2-token-end js2-token-beg)))
+ (or pos (js2-current-token-beg))
+ (or len (js2-current-token-len)))
js2-parsed-errors))
(defun js2-report-error (msg &optional msg-arg pos len)
(if js2-compiler-report-warning-as-error
(js2-report-error msg msg-arg pos len)
(push (list (list msg msg-arg)
- (or pos js2-token-beg)
- (or len (- js2-token-end js2-token-beg))
+ (or pos (js2-current-token-beg))
+ (or len (js2-current-token-len))
face)
js2-parsed-warnings)))
(:include js2-node)
(:constructor nil)
(:constructor make-js2-block-node (&key (type js2-BLOCK)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len
props
kids)))
(:include js2-block-node)
(:constructor nil)
(:constructor make-js2-scope (&key (type js2-BLOCK)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len
kids)))
;; The symbol-table is a LinkedHashMap<String,Symbol> in Rhino.
(:include js2-node)
(:constructor nil) ; silence emacs21 byte-compiler
(:constructor make-js2-error-node (&key (type js2-ERROR)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len)))
"AST node representing a parse error.")
(:include js2-scope)
(:constructor nil)
(:constructor make-js2-script-node (&key (type js2-SCRIPT)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len
;; FIXME: What are those?
var-decls
(:include js2-script-node)
(:constructor nil)
(:constructor make-js2-ast-root (&key (type js2-SCRIPT)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len
buffer)))
"The root node of a js2 AST."
(:include js2-node)
(:constructor nil)
(:constructor make-js2-comment-node (&key (type js2-COMMENT)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len
- (format js2-ts-comment-type))))
+ format)))
format) ; 'line, 'block, 'jsdoc or 'html
(put 'cl-struct-js2-comment-node 'js2-visitor 'js2-visit-none)
(:include js2-loop-node)
(:constructor nil)
(:constructor make-js2-do-node (&key (type js2-DO)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len
body
condition
(:include js2-loop-node)
(:constructor nil)
(:constructor make-js2-while-node (&key (type js2-WHILE)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len body
condition lp
rp)))
(defun js2-print-labeled-stmt (n i)
(dolist (label (js2-labeled-stmt-node-labels n))
(js2-print-ast label i))
- (js2-print-ast (js2-labeled-stmt-node-stmt n) (1+ i)))
+ (js2-print-ast (js2-labeled-stmt-node-stmt n) i))
(defun js2-labeled-stmt-node-contains (node label)
"Return t if NODE contains LABEL in its label set.
(name "")
params rest-p
body
+ generator-type
lp rp)))
"AST node for a function declaration.
The `params' field is a Lisp list of nodes. Each node is either a simple
`js2-name-node', or if it's a destructuring-assignment parameter, a
`js2-array-node' or `js2-object-node'."
ftype ; FUNCTION, GETTER or SETTER
- form ; FUNCTION_{STATEMENT|EXPRESSION|EXPRESSION_STATEMENT}
+ form ; FUNCTION_{STATEMENT|EXPRESSION|ARROW}
name ; function name (a `js2-name-node', or nil if anonymous)
params ; a Lisp list of destructuring forms or simple name nodes
rest-p ; if t, the last parameter is rest parameter
rp ; position of arg-list close-paren, or nil if omitted
ignore-dynamic ; ignore value of the dynamic-scope flag (interpreter only)
needs-activation ; t if we need an activation object for this frame
- is-generator ; t if this function contains a yield
+ generator-type ; STAR, LEGACY or nil
member-expr) ; nonstandard Ecma extension from Rhino
(put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node)
(js2-visit-ast (js2-function-node-body n) v))
(defun js2-print-function-node (n i)
- (let ((pad (js2-make-pad i))
- (getter (js2-node-get-prop n 'GETTER_SETTER))
- (name (js2-function-node-name n))
- (params (js2-function-node-params n))
- (rest-p (js2-function-node-rest-p n))
- (body (js2-function-node-body n))
- (expr (eq (js2-function-node-form n) 'FUNCTION_EXPRESSION)))
- (unless getter
- (insert pad "function"))
+ (let* ((pad (js2-make-pad i))
+ (getter (js2-node-get-prop n 'GETTER_SETTER))
+ (name (or (js2-function-node-name n)
+ (js2-function-node-member-expr n)))
+ (params (js2-function-node-params n))
+ (arrow (eq (js2-function-node-form n) 'FUNCTION_ARROW))
+ (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 getter arrow)
+ (insert pad "function")
+ (when (eq (js2-function-node-generator-type n) 'STAR)
+ (insert "*")))
(when name
- (insert " ")
- (js2-print-ast name 0))
+ (insert " ")
+ (js2-print-ast name 0))
(insert "(")
(loop with len = (length params)
for param in params
(js2-print-ast param 0)
(when (< count len)
(insert ", ")))
- (insert ") {")
+ (insert ") ")
+ (when arrow
+ (insert "=> "))
+ (insert "{")
+ ;; TODO: fix this to be smarter about indenting, etc.
(unless expr
(insert "\n"))
- ;; TODO: fix this to be smarter about indenting, etc.
- (js2-print-body body (1+ i))
+ (if (js2-block-node-p body)
+ (js2-print-body body (1+ i))
+ (js2-print-ast body 0))
(insert pad "}")
(unless expr
(insert "\n"))))
(:include js2-node)
(:constructor nil)
(:constructor make-js2-var-decl-node (&key (type js2-VAR)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len kids
decl-type)))
"AST node for a variable declaration list (VAR, CONST or LET).
(cons js2-BITNOT "~")
(cons js2-POS "+") ; unary plus
(cons js2-NEG "-") ; unary minus
+ (cons js2-TRIPLEDOT "...")
(cons js2-SHEQ "===") ; shallow equality
(cons js2-SHNE "!==") ; shallow inequality
(cons js2-ASSIGN "=")
len operand)))
"AST node type for unary operator nodes.
The type field can be NOT, BITNOT, POS, NEG, INC, DEC,
- TYPEOF, or DELPROP. For INC or DEC, a 'postfix node
+ TYPEOF, DELPROP or TRIPLEDOT. For INC or DEC, a 'postfix node
property is added if the operator follows the operand."
operand) ; a `js2-node' expression
(:include js2-scope)
(:constructor nil)
(:constructor make-js2-let-node (&key (type js2-LETEXPR)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len vars body
lp rp)))
"AST node for a let expression or a let statement.
(:include js2-node)
(:constructor nil)
(:constructor make-js2-keyword-node (&key type
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
(len (- js2-ts-cursor pos)))))
"AST node representing a literal keyword such as `null'.
Used for `null', `this', `true', `false' and `debugger'.
(:include js2-node)
(:constructor nil)
(:constructor make-js2-new-node (&key (type js2-NEW)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len target
args initializer
lp rp)))
(:include js2-node)
(:constructor nil)
(:constructor make-js2-name-node (&key (type js2-NAME)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
(len (- js2-ts-cursor
- js2-token-beg))
- (name js2-ts-string))))
+ (js2-current-token-beg)))
+ (name (js2-current-token-string)))))
"AST node for a JavaScript identifier"
name ; a string
scope) ; a `js2-scope' (optional, used for codegen)
(:include js2-node)
(:constructor nil)
(:constructor make-js2-number-node (&key (type js2-NUMBER)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
(len (- js2-ts-cursor
- js2-token-beg))
- (value js2-ts-string)
- (num-value js2-ts-number))))
+ (js2-current-token-beg)))
+ (value (js2-current-token-string))
+ (num-value (js2-token-number
+ (js2-current-token))))))
"AST node for a number literal."
value ; the original string, e.g. "6.02e23"
num-value) ; the parsed number value
(:include js2-node)
(:constructor nil)
(:constructor make-js2-regexp-node (&key (type js2-REGEXP)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
(len (- js2-ts-cursor
- js2-token-beg))
+ (js2-current-token-beg)))
value flags)))
"AST node for a regular expression literal."
value ; the regexp string, without // delimiters
(:include js2-node)
(:constructor nil)
(:constructor make-js2-string-node (&key (type js2-STRING)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
(len (- js2-ts-cursor
- js2-token-beg))
- (value js2-ts-string))))
+ (js2-current-token-beg)))
+ (value (js2-current-token-string)))))
"String literal.
Escape characters are not evaluated; e.g. \n is 2 chars in value field.
You can tell the quote type by looking at the first character."
(:include js2-node)
(:constructor nil)
(:constructor make-js2-empty-expr-node (&key (type js2-EMPTY)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len)))
"AST node for an empty expression.")
(:include js2-block-node)
(:constructor nil)
(:constructor make-js2-xml-node (&key (type js2-XML)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len kids)))
"AST node for initial parse of E4X literals.
The kids field is a list of XML fragments, each a `js2-string-node' or
(:include js2-xml-ref-node)
(:constructor nil)
(:constructor make-js2-xml-prop-ref-node (&key (type js2-REF_NAME)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len propname
namespace at-pos
colon-pos)))
(:include js2-xml-ref-node)
(:constructor nil)
(:constructor make-js2-xml-elem-ref-node (&key (type js2-REF_MEMBER)
- (pos js2-token-beg)
+ (pos (js2-current-token-beg))
len expr lb rb
namespace at-pos
colon-pos)))
names)
"Vector mapping int values to token string names, sans `js2-' prefix.")
- (defun js2-token-name (tok)
+ (defun js2-tt-name (tok)
"Return a string name for TOK, a token symbol or code.
Signals an error if it's not a recognized token."
(let ((code tok))
(aref js2-token-names code)
(error "Invalid token: %s" code)))))
- (defsubst js2-token-sym (tok)
+ (defsubst js2-tt-sym (tok)
"Return symbol for TOK given its code, e.g. 'js2-LP for code 86."
- (intern (js2-token-name tok)))
+ (intern (js2-tt-name tok)))
(defconst js2-token-codes
(let ((table (make-hash-table :test 'eq :size 256)))
;; clean up a few that are "wrong" in Rhino's token codes
(puthash 'js2-DELETE js2-DELPROP table)
table)
- "Hashtable mapping token symbols to their bytecodes.")
+ "Hashtable mapping token type symbols to their bytecodes.")
- (defsubst js2-token-code (sym)
+ (defsubst js2-tt-code (sym)
"Return code for token symbol SYM, e.g. 86 for 'js2-LP."
(or (gethash sym js2-token-codes)
(error "Invalid token symbol: %s " sym))) ; signal code bug
(defun js2-report-scan-error (msg &optional no-throw beg len)
- (setq js2-token-end js2-ts-cursor)
+ (setf (js2-token-end (js2-current-token)) js2-ts-cursor)
(js2-report-error msg nil
- (or beg js2-token-beg)
- (or len (- js2-token-end js2-token-beg)))
+ (or beg (js2-current-token-beg))
+ (or len (js2-current-token-len)))
(unless no-throw
(throw 'return js2-ERROR)))
- (defun js2-get-string-from-buffer ()
- "Reverse the char accumulator and return it as a string."
- (setq js2-token-end js2-ts-cursor)
- (if js2-ts-string-buffer
- (apply #'string (nreverse js2-ts-string-buffer))
- ""))
+ (defun js2-set-string-from-buffer (token)
+ "Set `string' and `end' slots for TOKEN, return the string."
+ (setf (js2-token-end token) js2-ts-cursor
+ (js2-token-string token) (js2-collect-string js2-ts-string-buffer)))
;; TODO: could potentially avoid a lot of consing by allocating a
;; char buffer the way Rhino does.
"Skip to end of line."
(while (not (memq (js2-get-char) js2-eol-chars)))
(js2-unget-char)
+ (setf (js2-token-end (js2-current-token)) js2-ts-cursor)
(setq js2-token-end js2-ts-cursor))
(defun js2-init-scanner (&optional buf line)
into temp buffers."
(with-current-buffer (or buf (current-buffer))
(setq js2-ts-dirty-line nil
- js2-ts-regexp-flags nil
- js2-ts-string ""
- js2-ts-number nil
js2-ts-hit-eof nil
js2-ts-line-start 0
js2-ts-lineno (or line 1)
js2-ts-line-end-char -1
js2-ts-cursor (point-min)
+ js2-ti-tokens (make-vector js2-ti-ntokens nil)
+ js2-ti-tokens-cursor 0
+ js2-ti-lookahead 0
js2-ts-is-xml-attribute nil
js2-ts-xml-is-tag-content nil
js2-ts-xml-open-tags-count 0
;; Not sure where this function is used in Rhino. Not tested.
(if (not js2-debug-print-trees)
""
- (let ((name (js2-token-name token)))
+ (let ((name (js2-tt-name token)))
(cond
((memq token (list js2-STRING js2-REGEXP js2-NAME))
- (concat name " `" js2-ts-string "'"))
+ (concat name " `" (js2-current-token-string) "'"))
((eq token js2-NUMBER)
- (format "NUMBER %g" js2-ts-number))
+ (format "NUMBER %g" (js2-token-number (js2-current-token))))
(t
name)))))
(defun js2-collect-string (buf)
"Convert BUF, a list of chars, to a string.
Reverses BUF before converting."
- (cond
- ((stringp buf)
- buf)
- ((null buf) ; for emacs21 compat
- "")
- (t
- (if buf
- (apply #'string (nreverse buf))
- ""))))
+ (if buf
+ (apply #'string (nreverse buf))
+ ""))
(defun js2-string-to-keyword (s)
"Return token for S, a string, if S is a keyword or reserved word.
(or (gethash s js2-keyword-names)
(gethash s js2-reserved-word-names)))
- (defsubst js2-ts-set-char-token-bounds ()
+ (defsubst js2-ts-set-char-token-bounds (token)
"Used when next token is one character."
- (setq js2-token-beg (1- js2-ts-cursor)
- js2-token-end js2-ts-cursor))
+ (setf (js2-token-beg token) (1- js2-ts-cursor)
+ (js2-token-end token) js2-ts-cursor))
- (defsubst js2-ts-return (token)
- "Return an N-character TOKEN from `js2-get-token'.
- Updates `js2-token-end' accordingly."
- (setq js2-token-end js2-ts-cursor)
- (throw 'return token))
+ (defsubst js2-ts-return (token type)
+ "Update the `end' and `type' slots of TOKEN,
+ then throw `return' with value TYPE."
+ (setf (js2-token-end token) js2-ts-cursor
+ (js2-token-type token) type)
+ (throw 'return type))
(defun js2-x-digit-to-int (c accumulator)
"Build up a hex number.
(logior c (lsh accumulator 4))))
(defun js2-get-token ()
- "Return next JavaScript token, an int such as js2-RETURN."
+ "If `js2-ti-lookahead' is zero, call scanner to get new token.
+ Otherwise, move `js2-ti-tokens-cursor' and return the type of
+ next saved token.
+
+ This function will not return a newline (js2-EOL) - instead, it
+ gobbles newlines until it finds a non-newline token. Call
+ `js2-peek-token-or-eol' when you care about newlines.
+
+ This function will also not return a js2-COMMENT. Instead, it
+ records comments found in `js2-scanned-comments'. If the token
+ returned by this function immediately follows a jsdoc comment,
+ the token is flagged as such."
+ (if (zerop js2-ti-lookahead)
+ (js2-get-token-internal)
+ (decf js2-ti-lookahead)
+ (setq js2-ti-tokens-cursor (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens))
+ (let ((tt (js2-current-token-type)))
+ (assert (not (= tt js2-EOL)))
+ tt)))
+
+ (defun js2-unget-token ()
+ (assert (< js2-ti-lookahead js2-ti-max-lookahead))
+ (incf js2-ti-lookahead)
+ (setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens)))
+
+ (defun js2-get-token-internal ()
+ (let* ((token (js2-get-token-internal-1)) ; call scanner
+ (tt (js2-token-type token))
+ saw-eol
+ face)
+ ;; process comments
+ (while (or (= tt js2-EOL) (= tt js2-COMMENT))
+ (if (= tt js2-EOL)
+ (setq saw-eol t)
+ (setq saw-eol nil)
+ (when js2-record-comments
+ (js2-record-comment token)))
+ (setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens))
+ (setq token (js2-get-token-internal-1) ; call scanner again
+ tt (js2-token-type token)))
+
+ (when saw-eol
+ (setf (js2-token-follows-eol-p token) t))
+
+ ;; perform lexical fontification as soon as token is scanned
+ (when js2-parse-ide-mode
+ (cond
+ ((minusp tt)
+ (js2-record-face 'js2-error token))
+ ((setq face (aref js2-kwd-tokens tt))
+ (js2-record-face face token))
+ ((and (= tt js2-NAME)
+ (equal (js2-token-string token) "undefined"))
+ (js2-record-face 'font-lock-constant-face token))))
+ tt))
+
+ (defun js2-get-token-internal-1 ()
+ "Return next JavaScript token type, an int such as js2-RETURN.
+ During operation, creates an instance of `js2-token' struct, sets
+ its relevant fields and puts it into `js2-ti-tokens'."
(let (c c1 identifier-start is-unicode-escape-start
contains-escape escape-val str result base
- is-integer quote-char val look-for-slash continue)
+ is-integer quote-char val look-for-slash continue tt
+ (token (js2-new-token 0)))
+ (setq
+ tt
(catch 'return
(while t
;; Eat whitespace, possibly sensitive to newlines.
(setq c (js2-get-char))
(cond
((eq c js2-EOF_CHAR)
- (js2-ts-set-char-token-bounds)
+ (js2-unget-char)
+ (js2-ts-set-char-token-bounds token)
(throw 'return js2-EOF))
((eq c ?\n)
- (js2-ts-set-char-token-bounds)
+ (js2-ts-set-char-token-bounds token)
(setq js2-ts-dirty-line nil)
(throw 'return js2-EOL))
((not (js2-js-space-p c))
(setq js2-ts-dirty-line t))
(setq continue nil))))
;; Assume the token will be 1 char - fixed up below.
- (js2-ts-set-char-token-bounds)
+ (js2-ts-set-char-token-bounds token)
(when (eq c ?@)
(throw 'return js2-XMLATTR))
;; identifier/keyword/instanceof?
(throw 'break nil))
(js2-add-to-string c))))))
(js2-unget-char)
- (setq str (js2-get-string-from-buffer))
+ (setf str (js2-collect-string js2-ts-string-buffer)
+ (js2-token-end token) js2-ts-cursor)
(unless contains-escape
;; OPT we shouldn't have to make a string (object!) to
;; check if it's a keyword.
;; LET and YIELD are tokens only in 1.7 and later
(setq result 'js2-NAME))
(if (not (eq result 'js2-RESERVED))
- (throw 'return (js2-token-code result)))
+ (throw 'return (js2-tt-code result)))
(js2-report-warning "msg.reserved.keyword" str)))
;; If we want to intern these as Rhino does, just use (intern str)
- (setq js2-ts-string str)
+ (setf (js2-token-string token) str)
(throw 'return js2-NAME)) ; end identifier/kwd check
;; is it a number?
(when (or (js2-digit-p c)
(setq c (js2-get-char))
while (js2-digit-p c))))
(js2-unget-char)
- (setq js2-ts-string (js2-get-string-from-buffer)
- js2-ts-number
- (if (and (eq base 10) (not is-integer))
- (string-to-number js2-ts-string)
- ;; TODO: call runtime number-parser. Some of it is in
- ;; js2-util.el, but I need to port ScriptRuntime.stringToNumber.
- (string-to-number js2-ts-string)))
+ (let ((str (js2-set-string-from-buffer token)))
+ (setf (js2-token-number token)
+ (if (and (eq base 10) (not is-integer))
+ (string-to-number str)
+ ;; TODO: call runtime number-parser. Some of it is in
+ ;; js2-util.el, but I need to port ScriptRuntime.stringToNumber.
+ (string-to-number str))))
(throw 'return js2-NUMBER))
;; is it a string?
(when (memq c '(?\" ?\'))
(catch 'continue
(when (or (eq c ?\n) (eq c js2-EOF_CHAR))
(js2-unget-char)
- (setq js2-token-end js2-ts-cursor)
+ (setf (js2-token-end token) js2-ts-cursor)
(js2-report-error "msg.unterminated.string.lit")
(throw 'return js2-STRING))
(when (eq c ?\\)
(setq c val)))))
(js2-add-to-string c)
(setq c (js2-get-char)))))
- (setq js2-ts-string (js2-get-string-from-buffer))
+ (js2-set-string-from-buffer token)
(throw 'return js2-STRING))
- (js2-ts-return
+ (js2-ts-return token
(case c
(?\;
(throw 'return js2-SEMI))
(if (js2-match-char ?=)
js2-SHEQ
(throw 'return js2-EQ))
- (throw 'return js2-ASSIGN)))
+ (if (js2-match-char ?>)
+ (js2-ts-return token js2-ARROW)
+ (throw 'return js2-ASSIGN))))
(?!
(if (js2-match-char ?=)
(if (js2-match-char ?=)
(when (js2-match-char ?-)
(when (js2-match-char ?-)
(js2-skip-line)
- (setq js2-ts-comment-type 'html)
+ (setf (js2-token-comment-type (js2-current-token)) 'html)
(throw 'return js2-COMMENT)))
(js2-unget-char))
(if (js2-match-char ?<)
(?/
;; is it a // comment?
(when (js2-match-char ?/)
- (setq js2-token-beg (- js2-ts-cursor 2))
+ (setf (js2-token-beg token) (- js2-ts-cursor 2))
(js2-skip-line)
- (setq js2-ts-comment-type 'line)
+ (setf (js2-token-comment-type token) 'line)
;; include newline so highlighting goes to end of window
- (incf js2-token-end)
+ (incf (js2-token-end token))
(throw 'return js2-COMMENT))
;; is it a /* comment?
(when (js2-match-char ?*)
- (setq look-for-slash nil
- js2-token-beg (- js2-ts-cursor 2)
- js2-ts-comment-type
+ (setf look-for-slash nil
+ (js2-token-beg token) (- js2-ts-cursor 2)
+ (js2-token-comment-type token)
(if (js2-match-char ?*)
(progn
(setq look-for-slash t)
(setq c (js2-get-char))
(cond
((eq c js2-EOF_CHAR)
- (setq js2-token-end (1- js2-ts-cursor))
+ (setf (js2-token-end token) (1- js2-ts-cursor))
(js2-report-error "msg.unterminated.comment")
(throw 'return js2-COMMENT))
((eq c ?*)
(setq look-for-slash t))
((eq c ?/)
(if look-for-slash
- (js2-ts-return js2-COMMENT)))
+ (js2-ts-return token js2-COMMENT)))
(t
- (setq look-for-slash nil
- js2-token-end js2-ts-cursor)))))
+ (setf look-for-slash nil
+ (js2-token-end token) js2-ts-cursor)))))
(if (js2-match-char ?=)
js2-ASSIGN_DIV
(throw 'return js2-DIV)))
(?#
- (when js2-skip-preprocessor-directives
- (js2-skip-line)
- (setq js2-ts-comment-type 'preprocessor
- js2-token-end js2-ts-cursor)
- (throw 'return js2-COMMENT))
- (throw 'return js2-ERROR))
+ (when js2-skip-preprocessor-directives
+ (js2-skip-line)
+ (setf (js2-token-comment-type token) 'preprocessor
+ (js2-token-end token) js2-ts-cursor)
+ (throw 'return js2-COMMENT))
+ (throw 'return js2-ERROR))
(?%
(if (js2-match-char ?=)
js2-ASSIGN_MOD
;; after line start as comment-until-eol
(when (js2-match-char ?>)
(js2-skip-line)
- (setq js2-ts-comment-type 'html)
+ (setf (js2-token-comment-type (js2-current-token)) 'html)
(throw 'return js2-COMMENT)))
(setq c js2-DEC))
(t
(setq js2-ts-dirty-line t)
c)
(otherwise
- (js2-report-scan-error "msg.illegal.character"))))))))
+ (js2-report-scan-error "msg.illegal.character")))))))
+ (setf (js2-token-type token) tt)
+ token))
- (defun js2-read-regexp (start-token)
+ (defun js2-read-regexp (start-tt)
"Called by parser when it gets / or /= in literal context."
(let (c err
in-class ; inside a '[' .. ']' character-class
flags
- (continue t))
- (setq js2-token-beg js2-ts-cursor
- js2-ts-string-buffer nil
- js2-ts-regexp-flags nil)
- (if (eq start-token js2-ASSIGN_DIV)
+ (continue t)
+ (token (js2-new-token 0)))
+ (setq js2-ts-string-buffer nil)
+ (if (eq start-tt js2-ASSIGN_DIV)
;; mis-scanned /=
(js2-add-to-string ?=)
- (if (not (eq start-token js2-DIV))
+ (if (not (eq start-tt js2-DIV))
(error "failed assertion")))
(while (and (not err)
(or (/= (setq c (js2-get-char)) ?/)
(cond
((or (= c ?\n)
(= c js2-EOF_CHAR))
- (setq js2-token-end (1- js2-ts-cursor)
+ (setf (js2-token-end token) (1- js2-ts-cursor)
err t
- js2-ts-string (js2-collect-string js2-ts-string-buffer))
+ (js2-token-string token) (js2-collect-string js2-ts-string-buffer))
(js2-report-error "msg.unterminated.re.lit"))
(t (cond
((= c ?\\)
(if (js2-alpha-p (js2-peek-char))
(js2-report-scan-error "msg.invalid.re.flag" t
js2-ts-cursor 1))
- (setq js2-ts-string (js2-collect-string js2-ts-string-buffer)
- js2-ts-regexp-flags (js2-collect-string flags)
- js2-token-end js2-ts-cursor)
+ (js2-set-string-from-buffer token)
;; tell `parse-partial-sexp' to ignore this range of chars
- (js2-record-text-property js2-token-beg js2-token-end 'syntax-class '(2)))))
+ (js2-record-text-property (js2-current-token-beg)
+ (js2-current-token-end) 'syntax-class '(2)))
+ (js2-collect-string flags)))
(defun js2-get-first-xml-token ()
(setq js2-ts-xml-open-tags-count 0
(js2-unget-char)
(js2-get-next-xml-token))
- (defun js2-xml-discard-string ()
+ (defun js2-xml-discard-string (token)
"Throw away the string in progress and flag an XML parse error."
- (setq js2-ts-string-buffer nil
- js2-ts-string nil)
+ (setf js2-ts-string-buffer nil
+ (js2-token-string token) nil)
(js2-report-scan-error "msg.XML.bad.form" t))
(defun js2-get-next-xml-token ()
- (setq js2-ts-string-buffer nil ; for recording the XML
- js2-token-beg js2-ts-cursor)
- (let (c result)
+ (setq js2-ts-string-buffer nil) ; for recording the XML
+ (let ((token (js2-new-token 0))
+ c result)
(setq result
(catch 'return
(while t
(decf js2-ts-xml-open-tags-count)))
(?{
(js2-unget-char)
- (setq js2-ts-string (js2-get-string-from-buffer))
+ (js2-set-string-from-buffer token)
(throw 'return js2-XML))
((?\' ?\")
(js2-add-to-string c)
- (unless (js2-read-quoted-string c)
+ (unless (js2-read-quoted-string c token)
(throw 'return js2-ERROR)))
(?=
(js2-add-to-string c)
(setq js2-ts-is-xml-attribute nil)))
(when (and (not js2-ts-xml-is-tag-content)
(zerop js2-ts-xml-open-tags-count))
- (setq js2-ts-string (js2-get-string-from-buffer))
+ (js2-set-string-from-buffer token)
(throw 'return js2-XMLEND)))
(t
;; else not tag content
(if (eq c ?-)
(progn
(js2-add-to-string c)
- (unless (js2-read-xml-comment)
+ (unless (js2-read-xml-comment token)
(throw 'return js2-ERROR)))
- (js2-xml-discard-string)
+ (js2-xml-discard-string token)
(throw 'return js2-ERROR)))
(?\[
(setq c (js2-get-char)) ;; skip [
(js2-add-to-string ?T)
(js2-add-to-string ?A)
(js2-add-to-string ?\[)
- (unless (js2-read-cdata)
+ (unless (js2-read-cdata token)
(throw 'return js2-ERROR)))
- (js2-xml-discard-string)
+ (js2-xml-discard-string token)
(throw 'return js2-ERROR)))
(t
- (unless (js2-read-entity)
+ (unless (js2-read-entity token)
(throw 'return js2-ERROR))))
;; Allow bare CDATA section, e.g.:
;; let xml = <![CDATA[ foo bar baz ]]>;
(??
(setq c (js2-get-char)) ;; skip ?
(js2-add-to-string c)
- (unless (js2-read-PI)
+ (unless (js2-read-PI token)
(throw 'return js2-ERROR)))
(?/
;; end tag
(setq c (js2-get-char)) ;; skip /
(js2-add-to-string c)
(when (zerop js2-ts-xml-open-tags-count)
- (js2-xml-discard-string)
+ (js2-xml-discard-string token)
(throw 'return js2-ERROR))
(setq js2-ts-xml-is-tag-content t)
(decf js2-ts-xml-open-tags-count))
(incf js2-ts-xml-open-tags-count))))
(?{
(js2-unget-char)
- (setq js2-ts-string (js2-get-string-from-buffer))
+ (js2-set-string-from-buffer token)
(throw 'return js2-XML))
(t
(js2-add-to-string c))))))))
- (setq js2-token-end js2-ts-cursor)
+ (setf (js2-token-end token) js2-ts-cursor)
+ (setf (js2-token-type token) result)
result))
- (defun js2-read-quoted-string (quote)
+ (defun js2-read-quoted-string (quote token)
(let (c)
(catch 'return
(while (/= (setq c (js2-get-char)) js2-EOF_CHAR)
(js2-add-to-string c)
(if (eq c quote)
(throw 'return t)))
- (js2-xml-discard-string) ;; throw away string in progress
+ (js2-xml-discard-string token) ;; throw away string in progress
nil)))
- (defun js2-read-xml-comment ()
+ (defun js2-read-xml-comment (token)
(let ((c (js2-get-char)))
(catch 'return
(while (/= c js2-EOF_CHAR)
(throw 'return t))
(throw 'continue nil)))
(setq c (js2-get-char))))
- (js2-xml-discard-string)
+ (js2-xml-discard-string token)
nil)))
- (defun js2-read-cdata ()
+ (defun js2-read-cdata (token)
(let ((c (js2-get-char)))
(catch 'return
(while (/= c js2-EOF_CHAR)
(throw 'return t))
(throw 'continue nil)))
(setq c (js2-get-char))))
- (js2-xml-discard-string)
+ (js2-xml-discard-string token)
nil)))
- (defun js2-read-entity ()
+ (defun js2-read-entity (token)
(let ((decl-tags 1)
c)
(catch 'return
(decf decl-tags)
(if (zerop decl-tags)
(throw 'return t)))))
- (js2-xml-discard-string)
+ (js2-xml-discard-string token)
nil)))
- (defun js2-read-PI ()
+ (defun js2-read-PI (token)
"Scan an XML processing instruction."
(let (c)
(catch 'return
(setq c (js2-get-char)) ;; Skip >
(js2-add-to-string c)
(throw 'return t)))
- (js2-xml-discard-string)
+ (js2-xml-discard-string token)
nil)))
;;; Highlighting
(defconst js2-version "1.8.5"
"Version of JavaScript supported.")
- (defmacro js2-record-face (face)
- "Record a style run of FACE for the current token."
- `(js2-set-face js2-token-beg js2-token-end ,face 'record))
+ (defun js2-record-face (face &optional token)
+ "Record a style run of FACE for TOKEN or the current token."
+ (unless token (setq token (js2-current-token)))
+ (js2-set-face (js2-token-beg token) (js2-token-end token) face 'record))
(defsubst js2-node-end (n)
"Computes the absolute end of node N.
(+ (js2-node-pos n)
(js2-node-len n)))
- (defun js2-record-comment ()
+ (defun js2-record-comment (token)
"Record a comment in `js2-scanned-comments'."
- (push (make-js2-comment-node :len (- js2-token-end js2-token-beg)
- :format js2-ts-comment-type)
- js2-scanned-comments)
- (when js2-parse-ide-mode
- (js2-record-face (if (eq js2-ts-comment-type 'jsdoc)
- 'font-lock-doc-face
- 'font-lock-comment-face))
- (when (memq js2-ts-comment-type '(html preprocessor))
- ;; Tell cc-engine the bounds of the comment.
- (js2-record-text-property js2-token-beg (1- js2-token-end) 'c-in-sws t))))
-
- ;; This function is called depressingly often, so it should be fast.
- ;; Most of the time it's looking at the same token it peeked before.
- (defun js2-peek-token ()
- "Return the next token without consuming it.
- If previous token was consumed, calls scanner to get new token.
- If previous token was -not- consumed, returns it (idempotent).
+ (let ((ct (js2-token-comment-type token))
+ (beg (js2-token-beg token))
+ (end (js2-token-end token)))
+ (push (make-js2-comment-node :len (- end beg)
+ :format ct)
+ js2-scanned-comments)
+ (when js2-parse-ide-mode
+ (js2-record-face (if (eq ct 'jsdoc)
+ 'font-lock-doc-face
+ 'font-lock-comment-face)
+ token)
+ (when (memq ct '(html preprocessor))
+ ;; Tell cc-engine the bounds of the comment.
+ (js2-record-text-property beg (1- end) 'c-in-sws t)))))
- This function will not return a newline (js2-EOL) - instead, it
- gobbles newlines until it finds a non-newline token, and flags
- that token as appearing just after a newline.
-
- This function will also not return a js2-COMMENT. Instead, it
- records comments found in `js2-scanned-comments'. If the token
- returned by this function immediately follows a jsdoc comment,
- the token is flagged as such.
-
- Note that this function always returned the un-flagged token!
- The flags, if any, are saved in `js2-current-flagged-token'."
- (if (/= js2-current-flagged-token js2-EOF) ; last token not consumed
- js2-current-token ; most common case - return already-peeked token
- (let ((tt (js2-get-token)) ; call scanner
- saw-eol
- face)
- ;; process comments and whitespace
- (while (or (= tt js2-EOL)
- (= tt js2-COMMENT))
- (if (= tt js2-EOL)
- (setq saw-eol t)
- (setq saw-eol nil)
- (if js2-record-comments
- (js2-record-comment)))
- (setq tt (js2-get-token))) ; call scanner
- (setq js2-current-token tt
- js2-current-flagged-token (if saw-eol
- (logior tt js2-ti-after-eol)
- tt))
- ;; perform lexical fontification as soon as token is scanned
- (when js2-parse-ide-mode
- (cond
- ((minusp tt)
- (js2-record-face 'js2-error))
- ((setq face (aref js2-kwd-tokens tt))
- (js2-record-face face))
- ((and (= tt js2-NAME)
- (equal js2-ts-string "undefined"))
- (js2-record-face 'font-lock-constant-face))))
- tt))) ; return unflagged token
-
- (defun js2-peek-flagged-token ()
- "Return the current token along with any flags set for it."
- (js2-peek-token)
- js2-current-flagged-token)
-
- (defsubst js2-consume-token ()
- (setq js2-current-flagged-token js2-EOF))
-
- (defun js2-next-token ()
- (prog1
- (js2-peek-token)
- (js2-consume-token)))
+ (defun js2-peek-token ()
+ "Return the next token type without consuming it.
+ If `js2-ti-lookahead' is positive, return the type of next token
+ from `js2-ti-tokens'. Otherwise, call `js2-get-token'."
+ (if (not (zerop js2-ti-lookahead))
+ (js2-token-type
+ (aref js2-ti-tokens (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens)))
+ (let ((tt (js2-get-token-internal)))
+ (js2-unget-token)
+ tt)))
- (defun js2-next-flagged-token ()
- (js2-peek-token)
- (prog1 js2-current-flagged-token
- (js2-consume-token)))
+ (defalias 'js2-next-token 'js2-get-token)
(defun js2-match-token (match)
- "Consume and return t if next token matches MATCH, a bytecode.
+ "Get next token and return t if it matches MATCH, a bytecode.
Returns nil and consumes nothing if MATCH is not the next token."
- (if (/= (js2-peek-token) match)
- nil
- (js2-consume-token)
+ (if (/= (js2-get-token) match)
+ (ignore (js2-unget-token))
t))
(defun js2-match-contextual-kwd (name)
"Consume and return t if next token is `js2-NAME', and its
- string is NAME. Returns nil and does nothing otherwise."
- (if (or (/= (js2-peek-token) js2-NAME)
- (not (string= js2-ts-string name)))
- nil
- (js2-consume-token)
+ 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)
(js2-record-face 'font-lock-keyword-face)
t))
(defun js2-valid-prop-name-token (tt)
(or (= tt js2-NAME)
- (when (and js2-allow-keywords-as-property-names
- (plusp tt)
- (aref js2-kwd-tokens tt))
- (js2-save-name-token-data js2-token-beg (js2-token-name tt))
- t)))
+ (and js2-allow-keywords-as-property-names
+ (plusp tt)
+ (aref js2-kwd-tokens tt))))
(defun js2-match-prop-name ()
"Consume token and return t if next token is a valid property name.
It's valid if it's a js2-NAME, or `js2-allow-keywords-as-property-names'
is non-nil and it's a keyword token."
- (if (js2-valid-prop-name-token (js2-peek-token))
- (progn
- (js2-consume-token)
- t)
+ (if (js2-valid-prop-name-token (js2-get-token))
+ t
+ (js2-unget-token)
nil))
(defun js2-must-match-prop-name (msg-id &optional pos len)
nil))
(defun js2-peek-token-or-eol ()
- "Return js2-EOL if the current token immediately follows a newline.
- Else returns the current token. Used in situations where we don't
+ "Return js2-EOL if the next token immediately follows a newline.
+ Else returns the next token. Used in situations where we don't
consider certain token types valid if they are preceded by a newline.
One example is the postfix ++ or -- operator, which has to be on the
same line as its operand."
- (let ((tt (js2-peek-token)))
- ;; Check for last peeked token flags
- (if (js2-flag-set-p js2-current-flagged-token js2-ti-after-eol)
+ (let ((tt (js2-get-token))
+ (follows-eol (js2-token-follows-eol-p (js2-current-token))))
+ (js2-unget-token)
+ (if follows-eol
js2-EOL
tt)))
- (defun js2-set-check-for-label ()
- (assert (= (logand js2-current-flagged-token js2-clear-ti-mask) js2-NAME))
- (js2-set-flag js2-current-flagged-token js2-ti-check-label))
-
(defun js2-must-match (token msg-id &optional pos len)
"Match next token to token code TOKEN, or record a syntax error.
MSG-ID is the error message to report if the match fails.
(js2-set-requires-activation))))
(defun js2-set-is-generator ()
- (if (js2-function-node-p js2-current-script-or-fn)
- (setf (js2-function-node-is-generator js2-current-script-or-fn) t)))
+ (let ((fn-node js2-current-script-or-fn))
+ (when (and (js2-function-node-p fn-node)
+ (not (js2-function-node-generator-type fn-node)))
+ (setf (js2-function-node-generator-type js2-current-script-or-fn) 'LEGACY))))
(defun js2-must-have-xml ()
(unless js2-compiler-xml-available
(setf root (make-js2-ast-root :buffer (buffer-name) :pos pos)
js2-current-script-or-fn root
js2-current-scope root
- js2-current-flagged-token js2-EOF
js2-nesting-of-function 0
js2-labeled-stmt nil
js2-recorded-identifiers nil) ; for js2-highlight
- (while (/= (setq tt (js2-peek-token)) js2-EOF)
+ (while (/= (setq tt (js2-get-token)) js2-EOF)
(if (= tt js2-FUNCTION)
(progn
- (js2-consume-token)
- (setq n (js2-parse-function (if js2-called-by-compile-function
- 'FUNCTION_EXPRESSION
- 'FUNCTION_STATEMENT))))
+ (setq n (if js2-called-by-compile-function
+ (js2-parse-function-expr)
+ (js2-parse-function-stmt))))
;; not a function - parse a statement
+ (js2-unget-token)
(setq n (js2-parse-statement)))
;; add function or statement to script
(setq end (js2-node-end n))
root))
(defun js2-function-parser ()
- (js2-consume-token)
- (js2-parse-function 'FUNCTION_EXPRESSION_STATEMENT))
+ (js2-get-token)
+ (js2-parse-function-stmt))
(defun js2-parse-function-closure-body (fn-node)
"Parse a JavaScript 1.8 function closure body."
(js2-must-match js2-LC "msg.no.brace.body"
(js2-node-pos fn-node)
(- js2-ts-cursor (js2-node-pos fn-node)))
- (let ((pos js2-token-beg) ; LC position
+ (let ((pos (js2-current-token-beg)) ; LC position
(pn (make-js2-block-node)) ; starts at LC position
tt
end)
(= tt js2-RC)))
(js2-block-node-push pn (if (/= tt js2-FUNCTION)
(js2-parse-statement)
- (js2-consume-token)
- (js2-parse-function 'FUNCTION_STATEMENT))))
+ (js2-get-token)
+ (js2-parse-function-stmt))))
(decf js2-nesting-of-function))
- (setq end js2-token-end) ; assume no curly and leave at current token
+ (setq end (js2-current-token-end)) ; assume no curly and leave at current token
(if (js2-must-match js2-RC "msg.no.brace.after.body" pos)
- (setq end js2-token-end))
+ (setq end (js2-current-token-end)))
(setf (js2-node-pos pn) pos
(js2-node-len pn) (- end pos))
(setf (js2-function-node-body fn-node) pn)
(t (js2-report-error "msg.no.parm" nil (js2-node-abs-pos node)
(js2-node-len node)))))
- (defun js2-parse-function-params (fn-node pos)
+ (defun js2-parse-function-params (function-type fn-node pos)
(if (js2-match-token js2-RP)
- (setf (js2-function-node-rp fn-node) (- js2-token-beg pos))
- (let (params param default-found rest-param-at)
+ (setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos))
+ (let ((paren-free-arrow (and (eq function-type 'FUNCTION_ARROW)
+ (eq (js2-current-token-type) js2-NAME)))
+ params param default-found rest-param-at)
+ (when paren-free-arrow
+ (js2-unget-token))
(loop for tt = (js2-peek-token)
do
(cond
;; destructuring param
- ((or (= tt js2-LB) (= tt js2-LC))
+ ((and (not paren-free-arrow)
+ (or (= tt js2-LB) (= tt js2-LC)))
+ (js2-get-token)
(when default-found
(js2-report-error "msg.no.default.after.default.param"))
(setq param (js2-parse-destruct-primary-expr))
;; variable name
(t
(when (and (>= js2-language-version 200)
+ (not paren-free-arrow)
(js2-match-token js2-TRIPLEDOT)
(not rest-param-at))
;; to report errors if there are more parameters
(js2-must-match js2-NAME "msg.no.parm")
(js2-record-face 'js2-function-param)
(setq param (js2-create-name-node))
- (js2-define-symbol js2-LP js2-ts-string param)
+ (js2-define-symbol js2-LP (js2-current-token-string) param)
;; default parameter value
(when (or (and default-found
(not rest-param-at)
(js2-node-len param)))
(and (>= js2-language-version 200)
(js2-match-token js2-ASSIGN)))
+ (assert (not paren-free-arrow))
(let* ((pos (js2-node-pos param))
- (tt js2-current-token)
- (op-pos (- js2-token-beg pos))
+ (tt (js2-current-token-type))
+ (op-pos (- (js2-current-token-beg) pos))
(left param)
(right (js2-parse-assign-expr))
(len (- (js2-node-end right) pos)))
(js2-node-pos param) (js2-node-len param)))
while
(js2-match-token js2-COMMA))
- (when (js2-must-match js2-RP "msg.no.paren.after.parms")
- (setf (js2-function-node-rp fn-node) (- js2-token-beg pos)))
+ (when (and (not paren-free-arrow)
+ (js2-must-match js2-RP "msg.no.paren.after.parms"))
+ (setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos)))
(when rest-param-at
(setf (js2-function-node-rest-p fn-node) t))
(dolist (p params)
(js2-function-node-body fn-node))))
;; Have it extend from close-curly to bol or beginning of block.
(let ((pos (save-excursion
- (goto-char js2-token-end)
+ (goto-char (js2-current-token-end))
(max (js2-node-abs-pos (js2-function-node-body fn-node))
(point-at-bol))))
- (end js2-token-end))
+ (end (js2-current-token-end)))
(if (plusp (js2-name-node-length name))
(js2-add-strict-warning "msg.no.return.value"
(js2-name-node-name name) pos end)
(js2-add-strict-warning "msg.anon.no.return.value" nil pos end)))))
- (defun js2-parse-function (function-type)
- "Function parser. FUNCTION-TYPE is a symbol."
- (let ((pos js2-token-beg) ; start of 'function' keyword
- name name-beg name-end fn-node lp
- (synthetic-type function-type)
- member-expr-node)
- ;; parse function name, expression, or non-name (anonymous)
- (cond
- ;; function foo(...)
- ((js2-match-token js2-NAME)
- (setq name (js2-create-name-node t)
- name-beg js2-token-beg
- name-end js2-token-end)
- (unless (js2-match-token js2-LP)
- (when js2-allow-member-expr-as-function-name
- ;; function foo.bar(...)
- (setq member-expr-node name
- name nil
- member-expr-node (js2-parse-member-expr-tail
- nil member-expr-node)))
- (js2-must-match js2-LP "msg.no.paren.parms")))
- ((js2-match-token js2-LP)
- nil) ; anonymous function: leave name as null
- (t
- ;; function random-member-expr(...)
- (when js2-allow-member-expr-as-function-name
- ;; Note that memberExpr can not start with '(' like
- ;; in function (1+2).toString(), because 'function (' already
- ;; processed as anonymous function
- (setq member-expr-node (js2-parse-member-expr)))
- (js2-must-match js2-LP "msg.no.paren.parms")))
- (if (= js2-current-token js2-LP) ; eventually matched LP?
- (setq lp js2-token-beg))
- (if member-expr-node
- (progn
- (setq synthetic-type 'FUNCTION_EXPRESSION)
- (js2-parse-highlight-member-expr-fn-name member-expr-node))
- (if name
- (js2-set-face name-beg name-end
- 'font-lock-function-name-face 'record)))
- (if (and (not (eq synthetic-type 'FUNCTION_EXPRESSION))
- (plusp (js2-name-node-length name)))
- ;; Function statements define a symbol in the enclosing scope
- (js2-define-symbol js2-FUNCTION (js2-name-node-name name) fn-node))
+ (defun js2-parse-function-stmt ()
+ (let ((pos (js2-current-token-beg))
+ (star-p (js2-match-token js2-MUL)))
+ (js2-must-match js2-NAME "msg.unnamed.function.stmt")
+ (let ((name (js2-create-name-node t))
+ pn member-expr)
+ (cond
+ ((js2-match-token js2-LP)
+ (js2-parse-function 'FUNCTION_STATEMENT pos star-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)
+ (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 ()
+ (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)))
+
+ (defun js2-parse-function (function-type pos star-p &optional 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."
+ (let (fn-node lp)
+ (if (= (js2-current-token-type) js2-LP) ; eventually matched LP?
+ (setq lp (js2-current-token-beg)))
(setf fn-node (make-js2-function-node :pos pos
:name name
:form function-type
- :lp (if lp (- lp pos))))
+ :lp (if lp (- lp pos))
+ :generator-type (and star-p 'STAR)))
+ (when name
+ (js2-set-face (js2-node-pos name) (js2-node-end name)
+ 'font-lock-function-name-face 'record)
+ (when (plusp (js2-name-node-length name))
+ ;; Function statements define a symbol in the enclosing scope
+ (js2-define-symbol js2-FUNCTION (js2-name-node-name name) fn-node)))
(if (or (js2-inside-function) (plusp js2-nesting-of-with))
;; 1. Nested functions are not affected by the dynamic scope flag
;; as dynamic scope is already a parent of their scope.
js2-label-set
js2-loop-set
js2-loop-and-switch-set)
- (js2-parse-function-params fn-node pos)
+ (js2-parse-function-params function-type fn-node pos)
+ (when (eq function-type 'FUNCTION_ARROW)
+ (js2-must-match js2-ARROW "msg.bad.arrow.args"))
(if (and (>= js2-language-version 180)
(/= (js2-peek-token) js2-LC))
(js2-parse-function-closure-body fn-node)
(js2-parse-function-body fn-node))
- (if name
- (js2-node-add-children fn-node name))
(js2-check-inconsistent-return-warning fn-node name)
- ;; Function expressions define a name only in the body of the
- ;; function, and only if not hidden by a parameter name
- (if (and name
- (eq synthetic-type 'FUNCTION_EXPRESSION)
- (null (js2-scope-get-symbol js2-current-scope
- (js2-name-node-name name))))
+
+ (when name
+ (js2-node-add-children fn-node name)
+ ;; Function expressions define a name only in the body of the
+ ;; function, and only if not hidden by a parameter name
+ (when (and (eq function-type 'FUNCTION_EXPRESSION)
+ (null (js2-scope-get-symbol js2-current-scope
+ (js2-name-node-name name))))
(js2-define-symbol js2-FUNCTION
(js2-name-node-name name)
fn-node))
- (if (and name
- (not (eq function-type 'FUNCTION_EXPRESSION)))
- (js2-record-imenu-functions fn-node)))
- (setf (js2-node-len fn-node) (- js2-ts-cursor pos)
- (js2-function-node-member-expr fn-node) member-expr-node) ; may be nil
+ (when (eq function-type 'FUNCTION_STATEMENT)
+ (js2-record-imenu-functions fn-node))))
+
+ (setf (js2-node-len fn-node) (- js2-ts-cursor pos))
;; Rhino doesn't do this, but we need it for finding undeclared vars.
;; We wait until after parsing the function to set its parent scope,
;; since `js2-define-symbol' needs the defining-scope check to stop
node are given relative start positions and correct lengths."
(let ((pn (or parent (make-js2-block-node)))
tt)
- (setf (js2-node-pos pn) js2-token-beg)
+ (setf (js2-node-pos pn) (js2-current-token-beg))
(while (and (> (setq tt (js2-peek-token)) js2-EOF)
(/= tt js2-RC))
(js2-block-node-push pn (js2-parse-statement)))
(list js2-ERROR js2-EOF js2-RC))
(defun js2-statement-helper ()
- (let* ((tt (js2-peek-token))
+ (let* ((tt (js2-get-token))
(first-tt tt)
(parser (if (= tt js2-ERROR)
#'js2-parse-semi
pn))
(defun js2-auto-insert-semicolon (pn)
- (let* ((tt-flagged (js2-peek-flagged-token))
- (tt (logand tt-flagged js2-clear-ti-mask))
+ (let* ((tt (js2-get-token))
(pos (js2-node-pos pn)))
(cond
((= tt js2-SEMI)
- ;; Consume ';' as a part of expression
- (js2-consume-token)
;; extend the node bounds to include the semicolon.
- (setf (js2-node-len pn) (- js2-token-end pos)))
+ (setf (js2-node-len pn) (- (js2-current-token-end) pos)))
((memq tt js2-autoinsert-semi-and-warn)
+ (js2-unget-token) ; Not ';', do not consume.
;; Autoinsert ;
(js2-parse-warn-missing-semi pos (js2-node-end pn)))
(t
- (if (js2-flag-not-set-p tt-flagged js2-ti-after-eol)
+ (if (not (js2-token-follows-eol-p (js2-current-token)))
;; Report error if no EOL or autoinsert ';' otherwise
(js2-report-error "msg.no.semi.stmt")
- (js2-parse-warn-missing-semi pos (js2-node-end pn)))))))
+ (js2-parse-warn-missing-semi pos (js2-node-end pn)))
+ (js2-unget-token) ; Not ';', do not consume.
+ ))))
(defun js2-parse-condition ()
"Parse a parenthesized boolean expression, e.g. in an if- or while-stmt.
Return value is a list (EXPR LP RP), with absolute paren positions."
(let (pn lp rp)
(if (js2-must-match js2-LP "msg.no.paren.cond")
- (setq lp js2-token-beg))
+ (setq lp (js2-current-token-beg)))
(setq pn (js2-parse-expr))
(if (js2-must-match js2-RP "msg.no.paren.after.cond")
- (setq rp js2-token-beg))
+ (setq rp (js2-current-token-beg)))
;; Report strict warning on code like "if (a = 7) ..."
(if (and js2-strict-cond-assign-warning
(js2-assign-node-p pn))
(defun js2-parse-if ()
"Parser for if-statement. Last matched token must be js2-IF."
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
cond if-true if-false else-pos end pn)
- (js2-consume-token)
(setq cond (js2-parse-condition)
if-true (js2-parse-statement)
if-false (if (js2-match-token js2-ELSE)
(progn
- (setq else-pos (- js2-token-beg pos))
+ (setq else-pos (- (js2-current-token-beg) pos))
(js2-parse-statement)))
end (js2-node-end (or if-false if-true))
pn (make-js2-if-node :pos pos
pn))
(defun js2-parse-switch ()
- "Parser for if-statement. Last matched token must be js2-SWITCH."
- (let ((pos js2-token-beg)
+ "Parser for switch-statement. Last matched token must be js2-SWITCH."
+ (let ((pos (js2-current-token-beg))
tt pn discriminant has-default case-expr case-node
case-pos cases stmt lp)
- (js2-consume-token)
(if (js2-must-match js2-LP "msg.no.paren.switch")
- (setq lp js2-token-beg))
+ (setq lp (js2-current-token-beg)))
(setq discriminant (js2-parse-expr)
pn (make-js2-switch-node :discriminant discriminant
:pos pos
(unwind-protect
(progn
(if (js2-must-match js2-RP "msg.no.paren.after.switch")
- (setf (js2-switch-node-rp pn) (- js2-token-beg pos)))
+ (setf (js2-switch-node-rp pn) (- (js2-current-token-beg) pos)))
(js2-must-match js2-LC "msg.no.brace.switch")
(catch 'break
(while t
(setq tt (js2-next-token)
- case-pos js2-token-beg)
+ case-pos (js2-current-token-beg))
(cond
((= tt js2-RC)
- (setf (js2-node-len pn) (- js2-token-end pos))
+ (setf (js2-node-len pn) (- (js2-current-token-end) pos))
(throw 'break nil)) ; done
((= tt js2-CASE)
(setq case-expr (js2-parse-expr))
(js2-report-error "msg.bad.switch")
(throw 'break nil)))
(setq case-node (make-js2-case-node :pos case-pos
- :len (- js2-token-end case-pos)
+ :len (- (js2-current-token-end) case-pos)
:expr case-expr))
(js2-node-add-children case-node case-expr)
(while (and (/= (setq tt (js2-peek-token)) js2-RC)
(defun js2-parse-while ()
"Parser for while-statement. Last matched token must be js2-WHILE."
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
(pn (make-js2-while-node))
cond body)
- (js2-consume-token)
(js2-enter-loop pn)
(unwind-protect
(progn
(defun js2-parse-do ()
"Parser for do-statement. Last matched token must be js2-DO."
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
(pn (make-js2-do-node))
cond body end)
- (js2-consume-token)
(js2-enter-loop pn)
(unwind-protect
(progn
(setq body (js2-parse-statement))
(js2-must-match js2-WHILE "msg.no.while.do")
- (setf (js2-do-node-while-pos pn) (- js2-token-beg pos)
+ (setf (js2-do-node-while-pos pn) (- (js2-current-token-beg) pos)
cond (js2-parse-condition)
(js2-do-node-condition pn) (car cond)
(js2-do-node-body pn) body
(defun js2-parse-for ()
"Parser for for-statement. Last matched token must be js2-FOR.
Parses for, for-in, and for each-in statements."
- (let ((for-pos js2-token-beg)
+ (let ((for-pos (js2-current-token-beg))
pn is-for-each is-for-in-or-of is-for-of
in-pos each-pos tmp-pos
init ; Node init is also foo in 'foo in object'
cond ; Node cond is also object in 'foo in object'
incr ; 3rd section of for-loop initializer
body tt lp rp)
- (js2-consume-token)
;; See if this is a for each () instead of just a for ()
(when (js2-match-token js2-NAME)
- (if (string= "each" js2-ts-string)
+ (if (string= "each" (js2-current-token-string))
(progn
(setq is-for-each t
- each-pos (- js2-token-beg for-pos)) ; relative
+ each-pos (- (js2-current-token-beg) for-pos)) ; relative
(js2-record-face 'font-lock-keyword-face))
(js2-report-error "msg.no.paren.for")))
(if (js2-must-match js2-LP "msg.no.paren.for")
- (setq lp (- js2-token-beg for-pos)))
- (setq tt (js2-peek-token))
+ (setq lp (- (js2-current-token-beg) for-pos)))
+ (setq tt (js2-get-token))
;; 'for' makes local scope
(js2-push-scope (make-js2-scope))
(unwind-protect
(let ((js2-in-for-init t)) ; set as dynamic variable
(cond
((= tt js2-SEMI)
+ (js2-unget-token)
(setq init (make-js2-empty-expr-node)))
((or (= tt js2-VAR) (= tt js2-LET))
- (js2-consume-token)
- (setq init (js2-parse-variables tt js2-token-beg)))
+ (setq init (js2-parse-variables tt (js2-current-token-beg))))
(t
+ (js2-unget-token)
(setq init (js2-parse-expr)))))
(if (or (js2-match-token js2-IN)
(and (>= js2-language-version 200)
(js2-match-contextual-kwd "of")
(setq is-for-of t)))
(setq is-for-in-or-of t
- in-pos (- js2-token-beg for-pos)
+ in-pos (- (js2-current-token-beg) for-pos)
;; scope of iteration target object is not the scope we've created above.
;; stash current scope temporary.
cond (let ((js2-current-scope (js2-scope-parent-scope js2-current-scope)))
(make-js2-empty-expr-node) ; no loop condition
(js2-parse-expr)))
(js2-must-match js2-SEMI "msg.no.semi.for.cond")
- (setq tmp-pos js2-token-end
+ (setq tmp-pos (js2-current-token-end)
incr (if (= (js2-peek-token) js2-RP)
(make-js2-empty-expr-node :pos tmp-pos)
(js2-parse-expr))))
(if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
- (setq rp (- js2-token-beg for-pos)))
+ (setq rp (- (js2-current-token-beg) for-pos)))
(if (not is-for-in-or-of)
(setq pn (make-js2-for-node :init init
:condition cond
(defun js2-parse-try ()
"Parser for try-statement. Last matched token must be js2-TRY."
- (let ((try-pos js2-token-beg)
+ (let ((try-pos (js2-current-token-beg))
try-end
try-block
catch-blocks
block
lp
rp)
- (js2-consume-token)
(if (/= (js2-peek-token) js2-LC)
(js2-report-error "msg.no.brace.try"))
(setq try-block (js2-parse-statement)
(cond
((= peek js2-CATCH)
(while (js2-match-token js2-CATCH)
- (setq catch-pos js2-token-beg
+ (setq catch-pos (js2-current-token-beg)
guard-kwd nil
catch-cond nil
lp nil
(if saw-default-catch
(js2-report-error "msg.catch.unreachable"))
(if (js2-must-match js2-LP "msg.no.paren.catch")
- (setq lp (- js2-token-beg catch-pos)))
+ (setq lp (- (js2-current-token-beg) catch-pos)))
(js2-push-scope (make-js2-scope))
(let ((tt (js2-peek-token)))
(cond
;; destructuring pattern
;; catch ({ message, file }) { ... }
((or (= tt js2-LB) (= tt js2-LC))
+ (js2-get-token)
(setq param (js2-parse-destruct-primary-expr))
(js2-define-destruct-symbols param js2-LET nil))
;; simple name
(t
(js2-must-match js2-NAME "msg.bad.catchcond")
(setq param (js2-create-name-node))
- (js2-define-symbol js2-LET js2-ts-string param))))
+ (js2-define-symbol js2-LET (js2-current-token-string) param))))
;; pattern guard
(if (js2-match-token js2-IF)
- (setq guard-kwd (- js2-token-beg catch-pos)
+ (setq guard-kwd (- (js2-current-token-beg) catch-pos)
catch-cond (js2-parse-expr))
(setq saw-default-catch t))
(if (js2-must-match js2-RP "msg.bad.catchcond")
- (setq rp (- js2-token-beg catch-pos)))
+ (setq rp (- (js2-current-token-beg) catch-pos)))
(js2-must-match js2-LC "msg.no.brace.catchblock")
(setq block (js2-parse-statements)
try-end (js2-node-end block)
:rp rp))
(js2-pop-scope)
(if (js2-must-match js2-RC "msg.no.brace.after.body")
- (setq try-end js2-token-beg))
+ (setq try-end (js2-current-token-beg)))
(setf (js2-node-len block) (- try-end (js2-node-pos block))
(js2-node-len catch-node) (- try-end catch-pos))
(js2-node-add-children catch-node param catch-cond block)
(- (setq try-end (js2-node-end try-block))
(js2-node-pos try-block)))))
(when (js2-match-token js2-FINALLY)
- (setq finally-pos js2-token-beg
+ (setq finally-pos (js2-current-token-beg)
block (js2-parse-statement)
try-end (js2-node-end block)
finally-block (make-js2-finally-node :pos finally-pos
(defun js2-parse-throw ()
"Parser for throw-statement. Last matched token must be js2-THROW."
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
expr pn)
- (js2-consume-token)
(if (= (js2-peek-token-or-eol) js2-EOL)
;; ECMAScript does not allow new lines before throw expression,
;; see bug 256617
(defun js2-parse-break ()
"Parser for break-statement. Last matched token must be js2-BREAK."
- (let ((pos js2-token-beg)
- (end js2-token-end)
+ (let ((pos (js2-current-token-beg))
+ (end (js2-current-token-end))
break-target ; statement to break from
break-label ; in "break foo", name-node representing the foo
labels ; matching labeled statement to break to
pn)
- (js2-consume-token) ; `break'
(when (eq (js2-peek-token-or-eol) js2-NAME)
- (js2-consume-token)
+ (js2-get-token)
(setq break-label (js2-create-name-node)
end (js2-node-end break-label)
;; matchJumpLabelName only matches if there is one
- labels (js2-match-jump-label-name js2-ts-string)
+ labels (js2-match-jump-label-name (js2-current-token-string))
break-target (if labels (car (js2-labeled-stmt-node-labels labels)))))
(unless (or break-target break-label)
;; no break target specified - try for innermost enclosing loop/switch
(defun js2-parse-continue ()
"Parser for continue-statement. Last matched token must be js2-CONTINUE."
- (let ((pos js2-token-beg)
- (end js2-token-end)
+ (let ((pos (js2-current-token-beg))
+ (end (js2-current-token-end))
label ; optional user-specified label, a `js2-name-node'
labels ; current matching labeled stmt, if any
target ; the `js2-loop-node' target of this continue stmt
pn)
- (js2-consume-token) ; `continue'
(when (= (js2-peek-token-or-eol) js2-NAME)
- (js2-consume-token)
+ (js2-get-token)
(setq label (js2-create-name-node)
end (js2-node-end label)
;; matchJumpLabelName only matches if there is one
- labels (js2-match-jump-label-name js2-ts-string)))
+ labels (js2-match-jump-label-name (js2-current-token-string))))
(cond
((null labels) ; no current label to go to
(if (null js2-loop-set) ; no loop to continue to
(defun js2-parse-with ()
"Parser for with-statement. Last matched token must be js2-WITH."
- (js2-consume-token)
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
obj body pn lp rp)
(if (js2-must-match js2-LP "msg.no.paren.with")
- (setq lp js2-token-beg))
+ (setq lp (js2-current-token-beg)))
(setq obj (js2-parse-expr))
(if (js2-must-match js2-RP "msg.no.paren.after.with")
- (setq rp js2-token-beg))
+ (setq rp (js2-current-token-beg)))
(let ((js2-nesting-of-with (1+ js2-nesting-of-with)))
(setq body (js2-parse-statement)))
(setq pn (make-js2-with-node :pos pos
(defun js2-parse-const-var ()
"Parser for var- or const-statement.
Last matched token must be js2-CONST or js2-VAR."
- (let ((tt (js2-peek-token))
- (pos js2-token-beg)
+ (let ((tt (js2-current-token-type))
+ (pos (js2-current-token-beg))
expr pn)
- (js2-consume-token)
- (setq expr (js2-parse-variables tt js2-token-beg)
+ (setq expr (js2-parse-variables tt (js2-current-token-beg))
pn (make-js2-expr-stmt-node :pos pos
:len (- (js2-node-end expr) pos)
:expr expr))
(defun js2-parse-let-stmt ()
"Parser for let-statement. Last matched token must be js2-LET."
- (js2-consume-token)
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
expr pn)
(if (= (js2-peek-token) js2-LP)
;; let expression in statement context
pn))
(defun js2-parse-ret-yield ()
- (js2-parse-return-or-yield (js2-peek-token) nil))
+ (js2-parse-return-or-yield (js2-current-token-type) nil))
(defconst js2-parse-return-stmt-enders
(list js2-SEMI js2-RC js2-EOF js2-EOL js2-ERROR js2-RB js2-RP js2-YIELD))
(= (logand after mask) mask)))
(defun js2-parse-return-or-yield (tt expr-context)
- (let ((pos js2-token-beg)
- (end js2-token-end)
+ (let ((pos (js2-current-token-beg))
+ (end (js2-current-token-end))
(before js2-end-flags)
(inside-function (js2-inside-function))
e ret name)
(js2-report-error (if (eq tt js2-RETURN)
"msg.bad.return"
"msg.bad.yield")))
- (js2-consume-token)
;; This is ugly, but we don't want to require a semicolon.
(unless (memq (js2-peek-token-or-eol) js2-parse-return-stmt-enders)
(setq e (js2-parse-expr)
(t
(unless (js2-inside-function)
(js2-report-error "msg.bad.yield"))
- (js2-set-flag js2-end-flags js2-end-yields)
(setq ret (make-js2-yield-node :pos pos
:len (- end pos)
:value e))
(js2-set-is-generator))))
;; see if we are mixing yields and value returns.
(when (and inside-function
- (js2-now-all-set before js2-end-flags
- (logior js2-end-yields js2-end-returns-value)))
+ (js2-flag-set-p js2-end-flags js2-end-returns-value)
+ (eq (js2-function-node-generator-type js2-current-script-or-fn)
+ 'LEGACY))
(setq name (js2-function-name js2-current-script-or-fn))
(if (zerop (length name))
(js2-report-error "msg.anon.generator.returns" nil pos (- end pos))
ret))
(defun js2-parse-debugger ()
- (js2-consume-token)
(make-js2-keyword-node :type js2-DEBUGGER))
(defun js2-parse-block ()
"Parser for a curly-delimited statement block.
Last token matched must be `js2-LC'."
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
(pn (make-js2-scope)))
- (js2-consume-token)
(js2-push-scope pn)
(unwind-protect
(progn
(js2-parse-statements pn)
(js2-must-match js2-RC "msg.no.brace.block")
- (setf (js2-node-len pn) (- js2-token-end pos)))
+ (setf (js2-node-len pn) (- (js2-current-token-end) pos)))
(js2-pop-scope))
pn))
;; For `js2-ERROR' too, to have a node for error recovery to work on.
(defun js2-parse-semi ()
"Parse a statement or handle an error.
- Last matched token is `js2-SEMI' or `js2-ERROR'."
- (let ((tt (js2-peek-token)) pos len)
- (js2-consume-token)
+ Current token type is `js2-SEMI' or `js2-ERROR'."
+ (let ((tt (js2-current-token-type)) pos len)
(if (eq tt js2-SEMI)
(make-js2-empty-expr-node :len 1)
- (setq pos js2-token-beg
- len (- js2-token-beg pos))
+ (setq pos (js2-current-token-beg)
+ len (- (js2-current-token-end) pos))
(js2-report-error "msg.syntax" nil pos len)
(make-js2-error-node :pos pos :len len))))
(defun js2-parse-default-xml-namespace ()
"Parse a `default xml namespace = <expr>' e4x statement."
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
end len expr unary)
- (js2-consume-token)
(js2-must-have-xml)
(js2-set-requires-activation)
(setq len (- js2-ts-cursor pos))
(unless (and (js2-match-token js2-NAME)
- (string= js2-ts-string "xml"))
+ (string= (js2-current-token-string) "xml"))
(js2-report-error "msg.bad.namespace" nil pos len))
(unless (and (js2-match-token js2-NAME)
- (string= js2-ts-string "namespace"))
+ (string= (js2-current-token-string) "namespace"))
(js2-report-error "msg.bad.namespace" nil pos len))
(unless (js2-match-token js2-ASSIGN)
(js2-report-error "msg.bad.namespace" nil pos len))
(defun js2-record-label (label bundle)
;; current token should be colon that `js2-parse-primary-expr' left untouched
- (js2-consume-token)
+ (js2-get-token)
(let ((name (js2-label-node-name label))
labeled-stmt
dup)
up any following labels and the next non-label statement into a
`js2-labeled-stmt-node' bundle and return that. Otherwise we parse an
expression and return it wrapped in a `js2-expr-stmt-node'."
- (let ((pos js2-token-beg)
- expr stmt pn bundle
+ (let ((pos (js2-current-token-beg))
+ expr stmt bundle
(continue t))
;; set check for label and call down to `js2-parse-primary-expr'
- (js2-set-check-for-label)
- (setq expr (js2-parse-expr))
- (if (/= (js2-node-type expr) js2-LABEL)
- ;; Parsed non-label expression - wrap with expression stmt.
- (setq pn (js2-wrap-with-expr-stmt pos expr t)) ;FIXME: `pn' is unused!
+ (setq expr (js2-maybe-parse-label))
+ (if (null expr)
+ ;; Parse the non-label expression and wrap with expression stmt.
+ (js2-wrap-with-expr-stmt pos (js2-parse-expr) t)
;; else parsed a label
(setq bundle (make-js2-labeled-stmt-node :pos pos))
(js2-record-label expr bundle)
;; look for more labels
- (while (and continue (= (js2-peek-token) js2-NAME))
- (js2-set-check-for-label)
- (setq expr (js2-parse-expr))
- (if (/= (js2-node-type expr) js2-LABEL)
- (progn
- (setq stmt (js2-wrap-with-expr-stmt (js2-node-pos expr) expr t)
- continue nil)
- (js2-auto-insert-semicolon stmt))
- (js2-record-label expr bundle)))
+ (while (and continue (= (js2-get-token) js2-NAME))
+ (if (setq expr (js2-maybe-parse-label))
+ (js2-record-label expr bundle)
+ (setq expr (js2-parse-expr)
+ stmt (js2-wrap-with-expr-stmt (js2-node-pos expr) expr t)
+ continue nil)
+ (js2-auto-insert-semicolon stmt)))
;; no more labels; now parse the labeled statement
(unwind-protect
(unless stmt
(let ((js2-labeled-stmt bundle)) ; bind dynamically
+ (js2-unget-token)
(setq stmt (js2-statement-helper))))
;; remove the labels for this statement from the global set
(dolist (label (js2-labeled-stmt-node-labels bundle))
(js2-node-add-children bundle stmt)
bundle)))
+ (defun js2-maybe-parse-label ()
+ (assert (= (js2-current-token-type) js2-NAME))
+ (let (label-pos
+ (next-tt (js2-get-token))
+ (label-end (js2-current-token-end)))
+ ;; Do not consume colon, it is used as unwind indicator
+ ;; to return to statementHelper.
+ (js2-unget-token)
+ (if (= next-tt js2-COLON)
+ (prog2
+ (setq label-pos (js2-current-token-beg))
+ (make-js2-label-node :pos label-pos
+ :len (- label-end label-pos)
+ :name (js2-current-token-string))
+ (js2-set-face label-pos
+ label-end
+ 'font-lock-variable-name-face 'record))
+ ;; Backtrack from the name token, too.
+ (js2-unget-token)
+ nil)))
+
(defun js2-parse-expr-stmt ()
"Default parser in statement context, if no recognized statement found."
- (js2-wrap-with-expr-stmt js2-token-beg (js2-parse-expr) t))
+ (js2-wrap-with-expr-stmt (js2-current-token-beg)
+ (progn
+ (js2-unget-token)
+ (js2-parse-expr)) t))
(defun js2-parse-variables (decl-type pos)
"Parse a comma-separated list of variable declarations.
(while continue
(setq destructuring nil
name nil
- tt (js2-peek-token)
- kid-pos js2-token-beg
- end js2-token-end
+ tt (js2-get-token)
+ kid-pos (js2-current-token-beg)
+ end (js2-current-token-end)
init nil)
(if (or (= tt js2-LB) (= tt js2-LC))
;; Destructuring assignment, e.g., var [a, b] = ...
(setq destructuring (js2-parse-destruct-primary-expr)
end (js2-node-end destructuring))
;; Simple variable name
+ (js2-unget-token)
(when (js2-must-match js2-NAME "msg.bad.var")
(setq name (js2-create-name-node)
- nbeg js2-token-beg
- nend js2-token-end
+ nbeg (js2-current-token-beg)
+ nend (js2-current-token-end)
end nend)
- (js2-define-symbol decl-type js2-ts-string name js2-in-for-init)))
+ (js2-define-symbol decl-type (js2-current-token-string) name js2-in-for-init)))
(when (js2-match-token js2-ASSIGN)
(setq init (js2-parse-assign-expr)
end (js2-node-end init))
(let ((pn (make-js2-let-node :pos pos))
beg vars body)
(if (js2-must-match js2-LP "msg.no.paren.after.let")
- (setf (js2-let-node-lp pn) (- js2-token-beg pos)))
+ (setf (js2-let-node-lp pn) (- (js2-current-token-beg) pos)))
(js2-push-scope pn)
(unwind-protect
(progn
- (setq vars (js2-parse-variables js2-LET js2-token-beg))
+ (setq vars (js2-parse-variables js2-LET (js2-current-token-beg)))
(if (js2-must-match js2-RP "msg.no.paren.let")
- (setf (js2-let-node-rp pn) (- js2-token-beg pos)))
- (if (and stmt-p (eq (js2-peek-token) js2-LC))
+ (setf (js2-let-node-rp pn) (- (js2-current-token-beg) pos)))
+ (if (and stmt-p (eq (js2-get-token) js2-LC))
;; let statement
(progn
- (js2-consume-token)
- (setf beg js2-token-beg ; position stmt at LC
+ (setf beg (js2-current-token-beg) ; position stmt at LC
body (js2-parse-statements))
(js2-must-match js2-RC "msg.no.curly.let")
- (setf (js2-node-len body) (- js2-token-end beg)
- (js2-node-len pn) (- js2-token-end pos)
+ (setf (js2-node-len body) (- (js2-current-token-end) beg)
+ (js2-node-len pn) (- (js2-current-token-end) pos)
(js2-let-node-body pn) body
(js2-node-type pn) js2-LET))
;; let expression
+ (js2-unget-token)
(setf body (js2-parse-expr)
(js2-node-len pn) (- (js2-node-end body) pos)
(js2-let-node-body pn) body))
op-pos)
(while (and (not oneshot)
(js2-match-token js2-COMMA))
- (setq op-pos (- js2-token-beg pos)) ; relative
+ (setq op-pos (- (js2-current-token-beg) pos)) ; relative
(if (= (js2-peek-token) js2-YIELD)
(js2-report-error "msg.yield.parenthesized"))
(setq right (js2-parse-assign-expr)
pn))
(defun js2-parse-assign-expr ()
- (let ((tt (js2-peek-token))
- (pos js2-token-beg)
- pn left right op-pos)
+ (let ((tt (js2-get-token))
+ (pos (js2-current-token-beg))
+ pn left right op-pos
+ ts-state recorded-identifiers)
(if (= tt js2-YIELD)
(js2-parse-return-or-yield tt t)
+ ;; Save the tokenizer state in case we find an arrow function
+ ;; and have to rewind.
+ (setq ts-state (make-js2-ts-state)
+ recorded-identifiers js2-recorded-identifiers)
;; not yield - parse assignment expression
(setq pn (js2-parse-cond-expr)
- tt (js2-peek-token))
- (when (and (<= js2-first-assign tt)
- (<= tt js2-last-assign))
+ tt (js2-get-token))
+ (cond
+ ((and (<= js2-first-assign tt)
+ (<= tt js2-last-assign))
;; tt express assignment (=, |=, ^=, ..., %=)
- (js2-consume-token)
- (setq op-pos (- js2-token-beg pos) ; relative
- left pn
- right (js2-parse-assign-expr)
+ (setq op-pos (- (js2-current-token-beg) pos) ; relative
+ left pn)
+ (setq right (js2-parse-assign-expr)
pn (make-js2-assign-node :type tt
:pos pos
:len (- (js2-node-end right) pos)
(js2-record-imenu-functions right left))
;; do this last so ide checks above can use absolute positions
(js2-node-add-children pn left right))
+ ((and (= tt js2-ARROW)
+ (>= js2-language-version 200))
+ (js2-ts-seek ts-state)
+ (setq js2-recorded-identifiers recorded-identifiers)
+ (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) nil)))
+ (t
+ (js2-unget-token)))
pn)))
(defun js2-parse-cond-expr ()
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
(pn (js2-parse-or-expr))
test-expr
if-true
q-pos
c-pos)
(when (js2-match-token js2-HOOK)
- (setq q-pos (- js2-token-beg pos)
+ (setq q-pos (- (js2-current-token-beg) pos)
if-true (js2-parse-assign-expr))
(js2-must-match js2-COLON "msg.no.colon.cond")
- (setq c-pos (- js2-token-beg pos)
+ (setq c-pos (- (js2-current-token-beg) pos)
if-false (js2-parse-assign-expr)
test-expr pn
pn (make-js2-cond-node :pos pos
LEFT is the left-side-expression, already parsed, and the
binary operator should have just been matched.
PARSER is a function to call to parse the right operand,
- or a `js2-node' struct if it has already been parsed."
+ or a `js2-node' struct if it has already been parsed.
+ FIXME: The latter option is unused?"
(let* ((pos (js2-node-pos left))
- (op-pos (- js2-token-beg pos))
+ (op-pos (- (js2-current-token-beg) pos))
(right (if (js2-node-p parser)
parser
+ (js2-get-token)
(funcall parser)))
(pn (make-js2-infix-node :type type
:pos pos
(defun js2-parse-eq-expr ()
(let ((pn (js2-parse-rel-expr))
tt)
- (while (memq (setq tt (js2-peek-token)) js2-parse-eq-ops)
- (js2-consume-token)
+ (while (memq (setq tt (js2-get-token)) js2-parse-eq-ops)
(setq pn (js2-make-binary tt
pn
'js2-parse-rel-expr)))
+ (js2-unget-token)
pn))
(defconst js2-parse-rel-ops
(continue t)
tt)
(while continue
- (setq tt (js2-peek-token))
+ (setq tt (js2-get-token))
(cond
((and js2-in-for-init (= tt js2-IN))
+ (js2-unget-token)
(setq continue nil))
((memq tt js2-parse-rel-ops)
- (js2-consume-token)
(setq pn (js2-make-binary tt pn 'js2-parse-shift-expr)))
(t
+ (js2-unget-token)
(setq continue nil))))
pn))
tt
(continue t))
(while continue
- (setq tt (js2-peek-token))
+ (setq tt (js2-get-token))
(if (memq tt js2-parse-shift-ops)
- (progn
- (js2-consume-token)
- (setq pn (js2-make-binary tt pn 'js2-parse-add-expr)))
+ (setq pn (js2-make-binary tt pn 'js2-parse-add-expr))
+ (js2-unget-token)
(setq continue nil)))
pn))
tt
(continue t))
(while continue
- (setq tt (js2-peek-token))
+ (setq tt (js2-get-token))
(if (or (= tt js2-ADD) (= tt js2-SUB))
- (progn
- (js2-consume-token)
- (setq pn (js2-make-binary tt pn 'js2-parse-mul-expr)))
+ (setq pn (js2-make-binary tt pn 'js2-parse-mul-expr))
+ (js2-unget-token)
(setq continue nil)))
pn))
tt
(continue t))
(while continue
- (setq tt (js2-peek-token))
+ (setq tt (js2-get-token))
(if (memq tt js2-parse-mul-ops)
- (progn
- (js2-consume-token)
- (setq pn (js2-make-binary tt pn 'js2-parse-unary-expr)))
+ (setq pn (js2-make-binary tt pn 'js2-parse-unary-expr))
+ (js2-unget-token)
(setq continue nil)))
pn))
"Make a unary node of type TYPE.
PARSER is either a node (for postfix operators) or a function to call
to parse the operand (for prefix operators)."
- (let* ((pos js2-token-beg)
+ (let* ((pos (js2-current-token-beg))
(postfix (js2-node-p parser))
(expr (if postfix
parser
pn)
(if postfix ; e.g. i++
(setq pos (js2-node-pos expr)
- end js2-token-end)
+ end (js2-current-token-end))
(setq end (js2-node-end expr)))
(setq pn (make-js2-unary-node :type type
:pos pos
nil beg (- end beg))))
(defun js2-parse-unary-expr ()
- (let ((tt (js2-peek-token))
+ (let ((tt (js2-current-token-type))
pn expr beg end)
(cond
((or (= tt js2-VOID)
(= tt js2-NOT)
(= tt js2-BITNOT)
(= tt js2-TYPEOF))
- (js2-consume-token)
+ (js2-get-token)
(js2-make-unary tt 'js2-parse-unary-expr))
((= tt js2-ADD)
- (js2-consume-token)
+ (js2-get-token)
;; Convert to special POS token in decompiler and parse tree
(js2-make-unary js2-POS 'js2-parse-unary-expr))
((= tt js2-SUB)
- (js2-consume-token)
+ (js2-get-token)
;; Convert to special NEG token in decompiler and parse tree
(js2-make-unary js2-NEG 'js2-parse-unary-expr))
((or (= tt js2-INC)
(= tt js2-DEC))
- (js2-consume-token)
+ (js2-get-token)
(prog1
- (setq beg js2-token-beg
- end js2-token-end
+ (setq beg (js2-current-token-beg)
+ end (js2-current-token-end)
expr (js2-make-unary tt 'js2-parse-member-expr t))
(js2-check-bad-inc-dec tt beg end expr)))
((= tt js2-DELPROP)
- (js2-consume-token)
+ (js2-get-token)
(js2-make-unary js2-DELPROP 'js2-parse-unary-expr))
((= tt js2-ERROR)
- (js2-consume-token)
+ (js2-get-token)
(make-js2-error-node)) ; try to continue
((and (= tt js2-LT)
js2-compiler-xml-available)
;; XML stream encountered in expression.
- (js2-consume-token)
(js2-parse-member-expr-tail t (js2-parse-xml-initializer)))
(t
(setq pn (js2-parse-member-expr t)
;; Don't look across a newline boundary for a postfix incop.
tt (js2-peek-token-or-eol))
(when (or (= tt js2-INC) (= tt js2-DEC))
- (js2-consume-token)
+ (js2-get-token)
(setf expr pn
pn (js2-make-unary tt expr))
(js2-node-set-prop pn 'postfix t)
- (js2-check-bad-inc-dec tt js2-token-beg js2-token-end pn))
+ (js2-check-bad-inc-dec tt (js2-current-token-beg) (js2-current-token-end) pn))
pn))))
(defun js2-parse-xml-initializer ()
(setq tt (js2-get-next-xml-token)))
(cond
;; js2-XML means we found a {expr} in the XML stream.
- ;; The js2-ts-string is the XML up to the left-curly.
+ ;; The token string is the XML up to the left-curly.
((= tt js2-XML)
- (push (make-js2-string-node :pos js2-token-beg
- :len (- js2-ts-cursor js2-token-beg))
+ (push (make-js2-string-node :pos (js2-current-token-beg)
+ :len (- js2-ts-cursor (js2-current-token-beg)))
kids)
(js2-must-match js2-LC "msg.syntax")
(setq expr-pos js2-ts-cursor
(push pn kids))
;; a js2-XMLEND token means we hit the final close-tag.
((= tt js2-XMLEND)
- (push (make-js2-string-node :pos js2-token-beg
- :len (- js2-ts-cursor js2-token-beg))
+ (push (make-js2-string-node :pos (js2-current-token-beg)
+ :len (- js2-ts-cursor (js2-current-token-beg)))
kids)
(dolist (kid (nreverse kids))
(js2-block-node-push pn-xml kid))
(let (result)
(unless (js2-match-token js2-RP)
(loop do
- (if (= (js2-peek-token) js2-YIELD)
- (js2-report-error "msg.yield.parenthesized"))
- (push (js2-parse-assign-expr) result)
+ (let ((tt (js2-get-token)))
+ (if (= tt js2-YIELD)
+ (js2-report-error "msg.yield.parenthesized"))
+ (if (and (= tt js2-TRIPLEDOT)
+ (>= js2-language-version 200))
+ (push (js2-make-unary tt 'js2-parse-assign-expr) result)
+ (js2-unget-token)
+ (push (js2-parse-assign-expr) result)))
while
(js2-match-token js2-COMMA))
(js2-must-match js2-RP "msg.no.paren.arg")
result)))
(defun js2-parse-member-expr (&optional allow-call-syntax)
- (let ((tt (js2-peek-token))
+ (let ((tt (js2-current-token-type))
pn pos target args beg end init)
(if (/= tt js2-NEW)
(setq pn (js2-parse-primary-expr))
;; parse a 'new' expression
- (js2-consume-token)
- (setq pos js2-token-beg
+ (js2-get-token)
+ (setq pos (js2-current-token-beg)
beg pos
target (js2-parse-member-expr)
end (js2-node-end target)
(when (js2-match-token js2-LP)
;; Add the arguments to pn, if any are supplied.
(setf beg pos ; start of "new" keyword
- pos js2-token-beg
+ pos (js2-current-token-beg)
args (nreverse (js2-parse-argument-list))
(js2-new-node-args pn) args
- end js2-token-end
+ end (js2-current-token-end)
(js2-new-node-lp pn) (- pos beg)
(js2-new-node-rp pn) (- end 1 beg))
(apply #'js2-node-add-children pn args))
(let (tt
(continue t))
(while continue
- (setq tt (js2-peek-token))
+ (setq tt (js2-get-token))
(cond
((or (= tt js2-DOT) (= tt js2-DOTDOT))
(setq pn (js2-parse-property-access tt pn)))
((= tt js2-LP)
(if allow-call-syntax
(setq pn (js2-parse-function-call pn))
+ (js2-unget-token)
(setq continue nil)))
(t
+ (js2-unget-token)
(setq continue nil))))
(if (>= js2-highlight-level 2)
(js2-parse-highlight-member-expr-node pn))
Last token parsed must be `js2-DOTQUERY'."
(let ((pos (js2-node-pos pn))
op-pos expr end)
- (js2-consume-token)
(js2-must-have-xml)
(js2-set-requires-activation)
- (setq op-pos js2-token-beg
+ (setq op-pos (js2-current-token-beg)
expr (js2-parse-expr)
end (js2-node-end expr)
pn (make-js2-xml-dot-query-node :left pn
(js2-xml-dot-query-node-left pn)
(js2-xml-dot-query-node-right pn))
(if (js2-must-match js2-RP "msg.no.paren")
- (setf (js2-xml-dot-query-node-rp pn) js2-token-beg
- end js2-token-end))
+ (setf (js2-xml-dot-query-node-rp pn) (js2-current-token-beg)
+ end (js2-current-token-end)))
(setf (js2-node-len pn) (- end pos))
pn))
(defun js2-parse-element-get (pn)
"Parse an element-get expression, e.g. foo[bar].
Last token parsed must be `js2-RB'."
- (let ((lb js2-token-beg)
+ (let ((lb (js2-current-token-beg))
(pos (js2-node-pos pn))
rb expr)
- (js2-consume-token)
(setq expr (js2-parse-expr))
(if (js2-must-match js2-RB "msg.no.bracket.index")
- (setq rb js2-token-beg))
+ (setq rb (js2-current-token-beg)))
(setq pn (make-js2-elem-get-node :target pn
:pos pos
:element expr
:lb (js2-relpos lb pos)
:rb (js2-relpos rb pos)
- :len (- js2-token-end pos)))
+ :len (- (js2-current-token-end) pos)))
(js2-node-add-children pn
(js2-elem-get-node-target pn)
(js2-elem-get-node-element pn))
(defun js2-parse-function-call (pn)
(let (args
(pos (js2-node-pos pn)))
- (js2-consume-token)
(setq pn (make-js2-call-node :pos pos
:target pn
- :lp (- js2-token-beg pos)))
+ :lp (- (js2-current-token-beg) pos)))
(js2-node-add-children pn (js2-call-node-target pn))
;; Add the arguments to pn, if any are supplied.
(setf args (nreverse (js2-parse-argument-list))
- (js2-call-node-rp pn) (- js2-token-beg pos)
+ (js2-call-node-rp pn) (- (js2-current-token-beg) pos)
(js2-call-node-args pn) args)
(apply #'js2-node-add-children pn args)
(setf (js2-node-len pn) (- js2-ts-cursor pos))
(defun js2-parse-property-access (tt pn)
"Parse a property access, XML descendants access, or XML attr access."
(let ((member-type-flags 0)
- (dot-pos js2-token-beg)
+ (dot-pos (js2-current-token-beg))
(dot-len (if (= tt js2-DOTDOT) 2 1))
name
ref ; right side of . or .. operator
result)
- (js2-consume-token)
(when (= tt js2-DOTDOT)
(js2-must-have-xml)
(setq member-type-flags js2-descendants-flag))
(js2-must-match-prop-name "msg.no.name.after.dot")
(setq name (js2-create-name-node t js2-GETPROP)
result (make-js2-prop-get-node :left pn
- :pos js2-token-beg
+ :pos (js2-current-token-beg)
:right name
- :len (- js2-token-end
- js2-token-beg)))
+ :len (js2-current-token-len)))
(js2-node-add-children result pn name)
result)
;; otherwise look for XML operators
(cond
;; needed for generator.throw()
((= tt js2-THROW)
- (js2-save-name-token-data js2-token-beg "throw")
- (setq ref (js2-parse-property-name nil js2-ts-string member-type-flags)))
+ (setq ref (js2-parse-property-name nil nil member-type-flags)))
;; handles: name, ns::name, ns::*, ns::[expr]
((js2-valid-prop-name-token tt)
- (setq ref (js2-parse-property-name -1 js2-ts-string member-type-flags)))
+ (setq ref (js2-parse-property-name -1 nil member-type-flags)))
;; handles: *, *::name, *::*, *::[expr]
((= tt js2-MUL)
- (js2-save-name-token-data js2-token-beg "*")
(setq ref (js2-parse-property-name nil "*" member-type-flags)))
;; handles: '@attr', '@ns::attr', '@ns::*', '@ns::[expr]', etc.
((= tt js2-XMLATTR)
Called if we peeked an '@' token."
(let ((tt (js2-next-token))
- (at-pos js2-token-beg))
+ (at-pos (js2-current-token-beg)))
(cond
;; handles: @name, @ns::name, @ns::*, @ns::[expr]
((js2-valid-prop-name-token tt)
- (js2-parse-property-name at-pos js2-ts-string 0))
+ (js2-parse-property-name at-pos nil 0))
;; handles: @*, @*::name, @*::*, @*::[expr]
((= tt js2-MUL)
- (js2-save-name-token-data js2-token-beg "*")
- (js2-parse-property-name js2-token-beg "*" 0))
+ (js2-parse-property-name (js2-current-token-beg) "*" 0))
;; handles @[expr]
((= tt js2-LB)
(js2-parse-xml-elem-ref at-pos))
(t
(js2-report-error "msg.no.name.after.xmlAttr")
;; Avoid cascaded errors that happen if we make an error node here.
- (js2-save-name-token-data js2-token-beg "")
- (js2-parse-property-name js2-token-beg "" 0)))))
+ (js2-parse-property-name (js2-current-token-beg) "" 0)))))
- (defun js2-parse-property-name (at-pos _s member-type-flags)
+ (defun js2-parse-property-name (at-pos s member-type-flags)
"Check if :: follows name in which case it becomes qualified name.
AT-POS is a natural number if we just read an '@' token, else nil.
Returns a `js2-xml-ref-node' if it's an attribute access, a child of a '..'
operator, or the name is followed by ::. For a plain name, returns a
`js2-name-node'. Returns a `js2-error-node' for malformed XML expressions."
- (let ((pos (or at-pos js2-token-beg))
+ (let ((pos (or at-pos (js2-current-token-beg)))
colon-pos
- (name (js2-create-name-node t js2-current-token))
+ (name (js2-create-name-node t (js2-current-token-type) s))
ns tt pn)
(catch 'return
(when (js2-match-token js2-COLONCOLON)
(setq ns name
- colon-pos js2-token-beg
+ colon-pos (js2-current-token-beg)
tt (js2-next-token))
(cond
;; handles name::name
(setq name (js2-create-name-node)))
;; handles name::*
((= tt js2-MUL)
- (js2-save-name-token-data js2-token-beg "*")
- (setq name (js2-create-name-node)))
+ (setq name (js2-create-name-node nil nil "*")))
;; handles name::[expr]
((= tt js2-LB)
(throw 'return (js2-parse-xml-elem-ref at-pos ns colon-pos)))
(defun js2-parse-xml-elem-ref (at-pos &optional namespace colon-pos)
"Parse the [expr] portion of an xml element reference.
For instance, @[expr], @*::[expr], or ns::[expr]."
- (let* ((lb js2-token-beg)
+ (let* ((lb (js2-current-token-beg))
(pos (or at-pos lb))
rb
(expr (js2-parse-expr))
(end (js2-node-end expr))
pn)
(if (js2-must-match js2-RB "msg.no.bracket.index")
- (setq rb js2-token-beg
- end js2-token-end))
+ (setq rb (js2-current-token-beg)
+ end (js2-current-token-end)))
(prog1
(setq pn
(make-js2-xml-elem-ref-node :pos pos
"Parse a literal (leaf) expression of some sort.
Includes complex literals such as functions, object-literals,
array-literals, array comprehensions and regular expressions."
- (let ((tt-flagged (js2-next-flagged-token))
- pn ; parent node (usually return value)
+ (let (pn ; parent node (usually return value)
tt
px-pos ; paren-expr pos
len
flags ; regexp flags
expr)
- (setq tt js2-current-token)
+ (setq tt (js2-current-token-type))
(cond
((= tt js2-FUNCTION)
- (js2-parse-function 'FUNCTION_EXPRESSION))
+ (js2-parse-function-expr))
((= tt js2-LB)
(js2-parse-array-literal))
((= tt js2-LC)
(js2-parse-object-literal))
((= tt js2-LET)
- (js2-parse-let js2-token-beg))
+ (js2-parse-let (js2-current-token-beg)))
((= tt js2-LP)
- (setq px-pos js2-token-beg
+ (setq px-pos (js2-current-token-beg)
expr (js2-parse-expr))
(js2-must-match js2-RP "msg.no.paren")
(setq pn (make-js2-paren-node :pos px-pos
:expr expr
- :len (- js2-token-end px-pos)))
+ :len (- (js2-current-token-end) px-pos)))
(js2-node-add-children pn (js2-paren-node-expr pn))
pn)
((= tt js2-XMLATTR)
(js2-must-have-xml)
(js2-parse-attribute-access))
((= tt js2-NAME)
- (js2-parse-name tt-flagged tt))
+ (js2-parse-name tt))
((= tt js2-NUMBER)
(make-js2-number-node))
((= tt js2-STRING)
(js2-record-face 'font-lock-string-face)))
((or (= tt js2-DIV) (= tt js2-ASSIGN_DIV))
;; Got / or /= which in this context means a regexp literal
- (setq px-pos js2-token-beg)
- (js2-read-regexp tt)
- (setq flags js2-ts-regexp-flags
- js2-ts-regexp-flags nil)
+ (setq px-pos (js2-current-token-beg))
+ (setq flags (js2-read-regexp tt))
(prog1
(make-js2-regexp-node :pos px-pos
:len (- js2-ts-cursor px-pos)
- :value js2-ts-string
+ :value (js2-current-token-string)
:flags flags)
(js2-set-face px-pos js2-ts-cursor 'font-lock-string-face 'record)
(js2-record-text-property px-pos js2-ts-cursor 'syntax-table '(2))))
(= tt js2-FALSE)
(= tt js2-TRUE))
(make-js2-keyword-node :type tt))
+ ((= tt js2-RP)
+ ;; Not valid expression syntax, but this is valid in an arrow
+ ;; function with no params: () => body.
+ (if (eq (js2-peek-token) js2-ARROW)
+ (progn
+ (js2-unget-token) ; Put back the right paren.
+ ;; Return whatever, it will hopefully be rewinded and
+ ;; reparsed when we reach the =>.
+ (make-js2-keyword-node :type js2-NULL))
+ (js2-report-error "msg.syntax")
+ (make-js2-error-node)))
+ ((= tt js2-TRIPLEDOT)
+ ;; Likewise, only valid in an arrow function with a rest param.
+ (if (and (js2-match-token js2-NAME)
+ (js2-match-token js2-RP)
+ (eq (js2-peek-token) js2-ARROW))
+ (progn
+ (js2-unget-token) ; Put back the right paren.
+ ;; See the previous case.
+ (make-js2-keyword-node :type js2-NULL))
+ (js2-report-error "msg.syntax")
+ (make-js2-error-node)))
((= tt js2-RESERVED)
(js2-report-error "msg.reserved.id")
(make-js2-name-node))
(setq px-pos (point-at-bol)
len (- js2-ts-cursor px-pos))
(js2-report-error "msg.unexpected.eof" nil px-pos len)
- (make-js2-error-node :pos px-pos :len len))
+ (make-js2-error-node :pos (1- js2-ts-cursor)))
(t
(js2-report-error "msg.syntax")
(make-js2-error-node)))))
- (defun js2-parse-name (tt-flagged _tt)
- (let ((name js2-ts-string)
- (name-pos js2-token-beg)
+ (defun js2-parse-name (_tt)
+ (let ((name (js2-current-token-string))
node)
- (if (and (js2-flag-set-p tt-flagged js2-ti-check-label)
- (= (js2-peek-token) js2-COLON))
- (prog1
- ;; Do not consume colon, it is used as unwind indicator
- ;; to return to statementHelper.
- (make-js2-label-node :pos name-pos
- :len (- js2-token-end name-pos)
- :name name)
- (js2-set-face name-pos
- js2-token-end
- 'font-lock-variable-name-face 'record))
- ;; Otherwise not a label, just a name. Unfortunately peeking
- ;; the next token to check for a colon has biffed js2-token-beg
- ;; and js2-token-end. We store the name's bounds in buffer vars
- ;; and `js2-create-name-node' uses them.
- (js2-save-name-token-data name-pos name)
- (setq node (if js2-compiler-xml-available
- (js2-parse-property-name nil name 0)
- (js2-create-name-node 'check-activation)))
- (if js2-highlight-external-variables
- (js2-record-name-node node))
- node)))
+ (setq node (if js2-compiler-xml-available
+ (js2-parse-property-name nil name 0)
+ (js2-create-name-node 'check-activation nil name)))
+ (if js2-highlight-external-variables
+ (js2-record-name-node node))
+ node))
(defun js2-parse-warn-trailing-comma (msg pos elems comma-pos)
(js2-add-strict-warning
comma-pos))
(defun js2-parse-array-literal ()
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
(after-lb-or-comma t)
after-comma tt elems pn
(continue t))
(unless js2-is-in-destructuring
(js2-push-scope (make-js2-scope))) ; for array comp
(while continue
- (setq tt (js2-peek-token))
+ (setq tt (js2-get-token))
(cond
;; comma
((= tt js2-COMMA)
- (js2-consume-token)
- (setq after-comma js2-token-end)
+ (setq after-comma (js2-current-token-end))
(if (not after-lb-or-comma)
(setq after-lb-or-comma t)
(push nil elems)))
((or (= tt js2-RB)
(= tt js2-EOF)) ; prevent infinite loop
(if (= tt js2-EOF)
- (js2-report-error "msg.no.bracket.arg" nil pos)
- (js2-consume-token))
+ (js2-report-error "msg.no.bracket.arg" nil pos))
(setq continue nil
pn (make-js2-array-node :pos pos
:len (- js2-ts-cursor pos)
;; [a, b, c] | {a, b, c} | {a:x, b:y, c:z} | a
(js2-parse-destruct-primary-expr)
;; invalid pattern
- (js2-consume-token)
(js2-report-error "msg.bad.var")
(make-js2-error-node))
elems)
(not after-lb-or-comma) ; "for" can't follow a comma
elems ; must have at least 1 element
(not (cdr elems))) ; but no 2nd element
+ (js2-unget-token)
(setf continue nil
pn (js2-parse-array-comprehension (car elems) pos)))
-
;; another element
(t
(unless after-lb-or-comma
(js2-report-error "msg.no.bracket.arg"))
- (push (js2-parse-assign-expr) elems)
+ (if (and (= tt js2-TRIPLEDOT)
+ (>= js2-language-version 200))
+ ;; spread operator
+ (push (js2-make-unary tt 'js2-parse-assign-expr)
+ elems)
+ (js2-unget-token)
+ (push (js2-parse-assign-expr) elems))
(setq after-lb-or-comma nil
after-comma nil))))
(unless js2-is-in-destructuring
POS is the beginning of the LB token preceding EXPR.
We should have just parsed the 'for' keyword before calling this function."
(let (loops loop first filter if-pos result)
- (while (= (js2-peek-token) js2-FOR)
+ (while (= (js2-get-token) js2-FOR)
(let ((prev (car loops))) ; rearrange scope chain
(push (setq loop (js2-parse-array-comp-loop)) loops)
(if prev ; each loop is parent scope to the next one
; first loop takes expr scope's parent
(setf (js2-scope-parent-scope (setq first loop))
(js2-scope-parent-scope js2-current-scope)))))
+ (js2-unget-token)
;; set expr scope's parent to the last loop
(setf (js2-scope-parent-scope js2-current-scope) (car loops))
- (when (= (js2-peek-token) js2-IF)
- (js2-consume-token)
- (setq if-pos (- js2-token-beg pos) ; relative
+ (if (/= (js2-get-token) js2-IF)
+ (js2-unget-token)
+ (setq if-pos (- (js2-current-token-beg) pos) ; relative
filter (js2-parse-condition)))
(js2-must-match js2-RB "msg.no.bracket.arg" pos)
(setq result (make-js2-array-comp-node :pos pos
(defun js2-parse-array-comp-loop ()
"Parse a 'for [each] (foo [in|of] bar)' expression in an Array comprehension.
Last token peeked should be the initial FOR."
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
(pn (make-js2-array-comp-loop-node))
tt iter obj foreach-p forof-p in-pos each-pos lp rp)
- (assert (= (js2-next-token) js2-FOR)) ; consumes token
+ (assert (= (js2-current-token-type) js2-FOR))
(js2-push-scope pn)
(unwind-protect
(progn
(when (js2-match-token js2-NAME)
- (if (string= js2-ts-string "each")
+ (if (string= (js2-current-token-string) "each")
(progn
(setq foreach-p t
- each-pos (- js2-token-beg pos)) ; relative
+ each-pos (- (js2-current-token-beg) pos)) ; relative
(js2-record-face 'font-lock-keyword-face))
(js2-report-error "msg.no.paren.for")))
(if (js2-must-match js2-LP "msg.no.paren.for")
- (setq lp (- js2-token-beg pos)))
+ (setq lp (- (js2-current-token-beg) pos)))
(setq tt (js2-peek-token))
(cond
((or (= tt js2-LB)
(= tt js2-LC))
+ (js2-get-token)
(setq iter (js2-parse-destruct-primary-expr))
(js2-define-destruct-symbols iter js2-LET
'font-lock-variable-name-face t))
(and (>= js2-language-version 200)
(js2-match-contextual-kwd "of")
(setq forof-p t)))
- (setq in-pos (- js2-token-beg pos))
+ (setq in-pos (- (js2-current-token-beg) pos))
(js2-report-error "msg.in.after.for.name"))
(setq obj (js2-parse-expr))
(if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
- (setq rp (- js2-token-beg pos)))
+ (setq rp (- (js2-current-token-beg) pos)))
(setf (js2-node-pos pn) pos
(js2-node-len pn) (- js2-ts-cursor pos)
(js2-array-comp-loop-node-iterator pn) iter
pn))
(defun js2-parse-object-literal ()
- (let ((pos js2-token-beg)
+ (let ((pos (js2-current-token-beg))
tt elems result after-comma
(continue t))
(while continue
- (setq tt (js2-peek-token))
+ (setq tt (js2-get-token))
(cond
;; {foo: ...}, {'foo': ...}, {foo, bar, ...},
;; {get foo() {...}}, or {set foo(x) {...}}
(push result elems)))
;; {12: x} or {10.7: x}
((= tt js2-NUMBER)
- (js2-consume-token)
(setq after-comma nil)
(push (js2-parse-plain-property (make-js2-number-node)) elems))
;; trailing comma
((= tt js2-RC)
+ (js2-unget-token)
(setq continue nil)
(if after-comma
(js2-parse-warn-trailing-comma "msg.extra.trailing.comma"
(unless js2-recover-from-parse-errors
(setq continue nil)))) ; end switch
(if (js2-match-token js2-COMMA)
- (setq after-comma js2-token-end)
+ (setq after-comma (js2-current-token-end))
(setq continue nil))) ; end loop
(js2-must-match js2-RC "msg.no.brace.prop")
(setq result (make-js2-object-node :pos pos
(defun js2-parse-named-prop (tt)
"Parse a name, string, or getter/setter object property.
When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted."
- (js2-consume-token)
(let ((string-prop (and (= tt js2-STRING)
(make-js2-string-node)))
expr
- (ppos js2-token-beg)
- (pend js2-token-end)
+ (ppos (js2-current-token-beg))
+ (pend (js2-current-token-end))
(name (js2-create-name-node))
- (prop js2-ts-string))
+ (prop (js2-current-token-string)))
(cond
;; getter/setter prop
((and (= tt js2-NAME)
(= (js2-peek-token) js2-NAME)
(or (string= prop "get")
(string= prop "set")))
- (js2-consume-token)
+ (js2-get-token)
(js2-set-face ppos pend 'font-lock-keyword-face 'record) ; get/set
(js2-record-face 'font-lock-function-name-face) ; for peeked name
(setq name (js2-create-name-node)) ; discard get/set & use peeked name
PROP is the node representing the property: a number, name or string."
(js2-must-match js2-COLON "msg.no.colon.prop")
(let* ((pos (js2-node-pos prop))
- (colon (- js2-token-beg pos))
+ (colon (- (js2-current-token-beg) pos))
(expr (js2-parse-assign-expr))
(result (make-js2-object-prop-node
:pos pos
GET-P is non-nil if the keyword was `get'."
(let ((type (if get-p js2-GET js2-SET))
result end
- (fn (js2-parse-function 'FUNCTION_EXPRESSION)))
+ (fn (js2-parse-function-expr)))
;; 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")
(js2-node-add-children result prop fn)
result))
- (defun js2-create-name-node (&optional check-activation-p token)
- "Create a name node using the token info from last scanned name.
- In some cases we need to either synthesize a name node, or we lost
- the name token information by peeking. If the TOKEN parameter is
- not `js2-NAME', then we use the token info saved in instance vars."
- (let ((beg js2-token-beg)
- (s js2-ts-string)
- name)
- (when (/= js2-current-token js2-NAME)
- (setq beg (or js2-prev-name-token-start js2-ts-cursor)
- s js2-prev-name-token-string
- js2-prev-name-token-start nil
- js2-prev-name-token-string nil))
+ (defun js2-create-name-node (&optional check-activation-p token string)
+ "Create a name node using the current token and, optionally, STRING.
+ And, if CHECK-ACTIVATION-P is non-nil, use the value of TOKEN."
+ (let* ((beg (js2-current-token-beg))
+ (tt (js2-current-token-type))
+ (s (or string
+ (if (= js2-NAME tt)
+ (js2-current-token-string)
+ (js2-tt-name tt))))
+ name)
(setq name (make-js2-name-node :pos beg
:name s
:len (length s)))
"Return the proper indentation for the current line."
(save-excursion
(back-to-indentation)
- (let ((ctrl-stmt-indent (js2-ctrl-statement-indentation))
- (same-indent-p (looking-at "[]})]\\|\\<case\\>\\|\\<default\\>"))
- (continued-expr-p (js2-continued-expression-p))
- (declaration-indent (and js2-pretty-multiline-declarations
- (js2-multiline-decl-indentation)))
- (bracket (nth 1 parse-status))
- beg)
+ (let* ((ctrl-stmt-indent (js2-ctrl-statement-indentation))
+ (at-closing-bracket (looking-at "[]})]"))
+ (same-indent-p (or at-closing-bracket
+ (looking-at "\\<case\\>[^:]")
+ (and (looking-at "\\<default:")
+ (save-excursion
+ (js2-backward-sws)
+ (not (memq (char-before) '(?, ?{)))))))
+ (continued-expr-p (js2-continued-expression-p))
+ (declaration-indent (and js2-pretty-multiline-declarations
+ (js2-multiline-decl-indentation)))
+ (bracket (nth 1 parse-status))
+ beg indent)
(cond
;; indent array comprehension continuation lines specially
((and bracket
(and (eq js2-pretty-multiline-declarations 'all)
(looking-at js2-declaration-keyword-re)
(goto-char (1+ (match-end 0))))
- (cond (same-indent-p
- (current-column))
- (continued-expr-p
- (+ (current-column) (* 2 js2-basic-offset)))
- (t
- (+ (current-column) js2-basic-offset))))
+ (setq indent
+ (cond (same-indent-p
+ (current-column))
+ (continued-expr-p
+ (+ (current-column) (* 2 js2-basic-offset)))
+ (t
+ (+ (current-column) js2-basic-offset))))
+ (if (and js2-indent-switch-body
+ (not at-closing-bracket)
+ (looking-at "\\_<switch\\_>"))
+ (+ indent js2-basic-offset)
+ indent))
(t
(unless same-indent-p
(forward-char)
(setq arg (or arg 1))
(save-restriction
(widen) ;; `blink-matching-open' calls `narrow-to-region'
- (js2-reparse))
- (let (forward-sexp-function
- node (start (point)) pos lp rp child)
- (cond
- ;; backward-sexp
- ;; could probably make this better for some cases:
- ;; - if in statement block (e.g. function body), go to parent
- ;; - infix exprs like (foo in bar) - maybe go to beginning
- ;; of infix expr if in the right-side expression?
- ((and arg (minusp arg))
- (dotimes (_ (- arg))
- (js2-backward-sws)
- (forward-char -1) ; Enter the node we backed up to.
- (when (setq node (js2-node-at-point (point) t))
- (setq pos (js2-node-abs-pos node))
- (let ((parens (js2-mode-forward-sexp-parens node pos)))
- (setq lp (car parens)
- rp (cdr parens)))
- (when (and lp (> start lp))
- (if (and rp (<= start rp))
- ;; Between parens, check if there's a child node we can jump.
- (when (setq child (js2-node-closest-child node (point) lp t))
- (setq pos (js2-node-abs-pos child)))
- ;; Before both parens.
- (setq pos lp)))
- (let ((state (parse-partial-sexp start pos)))
- (goto-char (if (not (zerop (car state)))
- ;; Stumble at the unbalanced paren if < 0, or
- ;; jump a bit further if > 0.
- (scan-sexps start -1)
- pos))))
- (unless pos (goto-char (point-min)))))
- (t
- ;; forward-sexp
- (dotimes (_ arg)
- (js2-forward-sws)
- (when (setq node (js2-node-at-point (point) t))
- (setq pos (js2-node-abs-pos node))
- (let ((parens (js2-mode-forward-sexp-parens node pos)))
- (setq lp (car parens)
- rp (cdr parens)))
- (or
- (when (and rp (<= start rp))
- (if (> start lp)
- (when (setq child (js2-node-closest-child node (point) rp))
- (setq pos (js2-node-abs-end child)))
- (setq pos (1+ rp))))
- ;; No parens or child nodes, looks for the end of the curren node.
- (incf pos (js2-node-len
- (if (js2-expr-stmt-node-p (js2-node-parent node))
- ;; Stop after the semicolon.
- (js2-node-parent node)
- node))))
- (let ((state (save-excursion (parse-partial-sexp start pos))))
- (goto-char (if (not (zerop (car state)))
- (scan-sexps start 1)
- pos))))
- (unless pos (goto-char (point-max))))))))
+ (js2-reparse)
+ (let (forward-sexp-function
+ node (start (point)) pos lp rp child)
+ (cond
+ ;; backward-sexp
+ ;; could probably make this better for some cases:
+ ;; - if in statement block (e.g. function body), go to parent
+ ;; - infix exprs like (foo in bar) - maybe go to beginning
+ ;; of infix expr if in the right-side expression?
+ ((and arg (minusp arg))
+ (dotimes (_ (- arg))
+ (js2-backward-sws)
+ (forward-char -1) ; Enter the node we backed up to.
+ (when (setq node (js2-node-at-point (point) t))
+ (setq pos (js2-node-abs-pos node))
+ (let ((parens (js2-mode-forward-sexp-parens node pos)))
+ (setq lp (car parens)
+ rp (cdr parens)))
+ (when (and lp (> start lp))
+ (if (and rp (<= start rp))
+ ;; Between parens, check if there's a child node we can jump.
+ (when (setq child (js2-node-closest-child node (point) lp t))
+ (setq pos (js2-node-abs-pos child)))
+ ;; Before both parens.
+ (setq pos lp)))
+ (let ((state (parse-partial-sexp start pos)))
+ (goto-char (if (not (zerop (car state)))
+ ;; Stumble at the unbalanced paren if < 0, or
+ ;; jump a bit further if > 0.
+ (scan-sexps start -1)
+ pos))))
+ (unless pos (goto-char (point-min)))))
+ (t
+ ;; forward-sexp
+ (dotimes (_ arg)
+ (js2-forward-sws)
+ (when (setq node (js2-node-at-point (point) t))
+ (setq pos (js2-node-abs-pos node))
+ (let ((parens (js2-mode-forward-sexp-parens node pos)))
+ (setq lp (car parens)
+ rp (cdr parens)))
+ (or
+ (when (and rp (<= start rp))
+ (if (> start lp)
+ (when (setq child (js2-node-closest-child node (point) rp))
+ (setq pos (js2-node-abs-end child)))
+ (setq pos (1+ rp))))
+ ;; No parens or child nodes, looks for the end of the curren node.
+ (incf pos (js2-node-len
+ (if (js2-expr-stmt-node-p (js2-node-parent node))
+ ;; Stop after the semicolon.
+ (js2-node-parent node)
+ node))))
+ (let ((state (save-excursion (parse-partial-sexp start pos))))
+ (goto-char (if (not (zerop (car state)))
+ (scan-sexps start 1)
+ pos))))
+ (unless pos (goto-char (point-max)))))))))
(defun js2-mode-forward-sexp-parens (node abs-pos)
"Return a cons cell with positions of main parens in NODE."