[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 3d034f9 262/271: Merge branch 'develop'
From: |
Jackson Ray Hamilton |
Subject: |
[elpa] master 3d034f9 262/271: Merge branch 'develop' |
Date: |
Thu, 05 Feb 2015 18:31:50 +0000 |
branch: master
commit 3d034f96a470e90316f1c01a5205fe56f2622542
Merge: bbebea1 43bb3c1
Author: Jackson Ray Hamilton <address@hidden>
Commit: Jackson Ray Hamilton <address@hidden>
Merge branch 'develop'
---
.elpaignore | 5 +-
.gitignore | 1 +
.travis.yml | 4 +
Makefile | 13 +-
README.md | 10 +-
context-coloring-themes.el | 122 -
context-coloring.el | 104 +-
languages/javascript/.jshintrc | 24 -
languages/javascript/binaries/scopifier | 37 -
languages/javascript/libraries/escope.js | 1117 ---
languages/javascript/libraries/esprima.js | 3756 --------
languages/javascript/libraries/estraverse.js | 838 --
languages/javascript/scopifier.js | 129 -
libraries/.dir-locals.el | 2 -
libraries/ert-async.el | 89 -
libraries/js2-mode.el |12331 --------------------------
scripts/download-dependencies.el | 40 +
17 files changed, 157 insertions(+), 18465 deletions(-)
diff --git a/.elpaignore b/.elpaignore
index 5b2e6d9..c860abc 100644
--- a/.elpaignore
+++ b/.elpaignore
@@ -1,13 +1,10 @@
.elpaignore
.gitignore
-.jshintrc
.travis.yml
Makefile
README.md
benchmark
-libraries/.dir-locals.el
-libraries/ert-async.el
-libraries/js2-mode.el
scopifier.png
screenshot.png
+scripts
test
diff --git a/.gitignore b/.gitignore
index d9c77ef..f090318 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.elc
/benchmark/logs/
+/libraries/
diff --git a/.travis.yml b/.travis.yml
index a3b3615..2dcc8a6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,6 +2,9 @@
language: emacs-lisp
+node_js:
+ - "0.10"
+
env:
matrix:
- EMACS=emacs24
@@ -12,6 +15,7 @@ install:
sudo apt-get update -qq &&
sudo apt-get install -qq emacs24 emacs24-el;
fi
+ - npm install -g scopifier
script:
make test EMACS=${EMACS}
diff --git a/Makefile b/Makefile
index 1df5088..2d37cd6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,9 @@
EMACS = emacs
+DEPENDENCIES = libraries/ert-async.el libraries/js2-mode.el
all: clean compile test
-bench:
+bench: ${DEPENDENCIES}
${EMACS} -Q \
-L . \
-L libraries \
@@ -10,16 +11,20 @@ bench:
-l benchmark/context-coloring-benchmark \
-f context-coloring-benchmark-run
-compile:
+compile: ${DEPENDENCIES}
${EMACS} -Q -batch \
-L . \
-L libraries \
-f batch-byte-compile *.el libraries/*.el
clean:
- rm -f *.elc libraries/*.elc
+ rm -f *.elc libraries/*.elc ${DEPENDENCIES}
-test:
+${DEPENDENCIES}:
+ ${EMACS} -Q -batch \
+ -l scripts/download-dependencies.el
+
+test: ${DEPENDENCIES}
${EMACS} -Q -batch \
-L . \
-L libraries \
diff --git a/README.md b/README.md
index c31f895..16b671a 100644
--- a/README.md
+++ b/README.md
@@ -38,8 +38,12 @@ code*.
Requires Emacs 24+.
-JavaScript language support requires either [js2-mode][] or
-[Node.js 0.10+][node], respectively.
+JavaScript language support requires either [js2-mode][], or
+[Node.js 0.10+][node] and the [scopifier][] executable.
+
+```bash
+npm install -g scopifier
+```
### ELPA
@@ -67,7 +71,7 @@ make compile
```lisp
(add-to-list 'load-path "~/.emacs.d/context-coloring")
(require 'context-coloring)
-(add-hook 'js-mode-hook 'context-coloring-mode)
+(add-hook 'js2-mode-hook 'context-coloring-mode)
```
## Customizing
diff --git a/context-coloring-themes.el b/context-coloring-themes.el
deleted file mode 100644
index a964169..0000000
--- a/context-coloring-themes.el
+++ /dev/null
@@ -1,122 +0,0 @@
-;;; context-coloring-themes.el --- Color schemes for Context Coloring. -*-
lexical-binding: t; -*-
-
-;; Copyright (C) 2014-2015 Free Software Foundation, Inc.
-
-;; This file is part of GNU Emacs.
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; An assortment of color schemes for Context Coloring, many of which are based
-;; on existing color themes and custom themes.
-
-;; To use, simply call `context-coloring-load-theme':
-
-;; (require 'context-coloring)
-;; (context-coloring-load-theme 'zenburn)
-
-;;; Code:
-
-(defvar context-coloring-theme-hash-table (make-hash-table :test 'eq)
- "Mapping of theme names to theme properties.")
-
-(defun context-coloring-define-theme (theme &rest properties)
- "Define a theme named THEME for coloring scope levels.
-PROPERTIES is a property list specifiying the following details:
-
-`:colors': List of colors that this theme uses."
- (puthash
- theme
- (lambda ()
- (apply 'context-coloring-set-colors (plist-get properties :colors)))
- context-coloring-theme-hash-table))
-
-(defun context-coloring-load-theme (theme)
- "Apply THEME's colors and other properties for context
-coloring."
- (let ((function (gethash theme context-coloring-theme-hash-table)))
- (when (null function)
- (error (format "No such theme `%s'" theme)))
- (funcall function)))
-
-(context-coloring-define-theme
- 'monokai
- :colors '("#F8F8F2"
- "#66D9EF"
- "#A1EFE4"
- "#A6E22E"
- "#E6DB74"
- "#FD971F"
- "#F92672"
- "#FD5FF0"
- "#AE81FF"))
-
-(context-coloring-define-theme
- 'solarized
- :colors '("#839496"
- "#268bd2"
- "#2aa198"
- "#859900"
- "#b58900"
- "#cb4b16"
- "#dc322f"
- "#d33682"
- "#6c71c4"
- "#69B7F0"
- "#69CABF"
- "#B4C342"
- "#DEB542"
- "#F2804F"
- "#FF6E64"
- "#F771AC"
- "#9EA0E5"))
-
-(context-coloring-define-theme
- 'tango
- :colors '("#2e3436"
- "#346604"
- "#204a87"
- "#5c3566"
- "#a40000"
- "#b35000"
- "#c4a000"
- "#8ae234"
- "#8cc4ff"
- "#ad7fa8"
- "#ef2929"
- "#fcaf3e"
- "#fce94f"))
-
-(context-coloring-define-theme
- 'zenburn
- :colors '("#DCDCCC"
- "#93E0E3"
- "#BFEBBF"
- "#F0DFAF"
- "#DFAF8F"
- "#CC9393"
- "#DC8CC3"
- "#94BFF3"
- "#9FC59F"
- "#D0BF8F"
- "#DCA3A3"))
-
-(provide 'context-coloring-themes)
-
-;; Local Variables:
-;; eval: (when (fboundp 'rainbow-mode) (rainbow-mode 1))
-;; End:
-
-;;; context-coloring-themes.el ends here
diff --git a/context-coloring.el b/context-coloring.el
index 8e9c49e..9607512 100644
--- a/context-coloring.el
+++ b/context-coloring.el
@@ -44,11 +44,15 @@
;; To use, add the following to your ~/.emacs:
;; (require 'context-coloring)
-;; (add-hook 'js-mode-hook 'context-coloring-mode)
+;; (add-hook 'js2-mode-hook 'context-coloring-mode)
+
+;; js-mode or js3-mode support requires Node.js 0.10+ and the scopifier
+;; executable.
+
+;; $ npm install -g scopifier
;;; Code:
-(require 'context-coloring-themes)
(require 'js2-mode)
@@ -398,10 +402,8 @@ level data returned via stdout."
(context-coloring-define-dispatch
'javascript-node
:modes '(js-mode js3-mode)
- :executable "node"
- :command (expand-file-name
- "./languages/javascript/binaries/scopifier"
- context-coloring-path))
+ :executable "scopifier"
+ :command "scopifier")
(context-coloring-define-dispatch
'javascript-js2
@@ -433,9 +435,6 @@ elisp tracks, and asynchronously for shell command tracks."
(if (and executable
(null (executable-find executable)))
(message "Executable \"%s\" not found" executable)
- (if (and executable
- (eq system-type 'windows-nt))
- (setq command (concat executable " " command)))
(context-coloring-scopify-shell-command command callback)))))))
@@ -470,6 +469,93 @@ would be redundant."
(context-coloring-colorize)))
+;;; Themes
+
+(defvar context-coloring-theme-hash-table (make-hash-table :test 'eq)
+ "Mapping of theme names to theme properties.")
+
+(defun context-coloring-define-theme (theme &rest properties)
+ "Define a theme named THEME for coloring scope levels.
+PROPERTIES is a property list specifiying the following details:
+
+`:colors': List of colors that this theme uses."
+ (puthash
+ theme
+ (lambda ()
+ (apply 'context-coloring-set-colors (plist-get properties :colors)))
+ context-coloring-theme-hash-table))
+
+(defun context-coloring-load-theme (theme)
+ "Apply THEME's colors and other properties for context
+coloring."
+ (let ((function (gethash theme context-coloring-theme-hash-table)))
+ (when (null function)
+ (error (format "No such theme `%s'" theme)))
+ (funcall function)))
+
+(context-coloring-define-theme
+ 'monokai
+ :colors '("#F8F8F2"
+ "#66D9EF"
+ "#A1EFE4"
+ "#A6E22E"
+ "#E6DB74"
+ "#FD971F"
+ "#F92672"
+ "#FD5FF0"
+ "#AE81FF"))
+
+(context-coloring-define-theme
+ 'solarized
+ :colors '("#839496"
+ "#268bd2"
+ "#2aa198"
+ "#859900"
+ "#b58900"
+ "#cb4b16"
+ "#dc322f"
+ "#d33682"
+ "#6c71c4"
+ "#69B7F0"
+ "#69CABF"
+ "#B4C342"
+ "#DEB542"
+ "#F2804F"
+ "#FF6E64"
+ "#F771AC"
+ "#9EA0E5"))
+
+(context-coloring-define-theme
+ 'tango
+ :colors '("#2e3436"
+ "#346604"
+ "#204a87"
+ "#5c3566"
+ "#a40000"
+ "#b35000"
+ "#c4a000"
+ "#8ae234"
+ "#8cc4ff"
+ "#ad7fa8"
+ "#ef2929"
+ "#fcaf3e"
+ "#fce94f"))
+
+(context-coloring-define-theme
+ 'zenburn
+ :colors '("#DCDCCC"
+ "#93E0E3"
+ "#BFEBBF"
+ "#F0DFAF"
+ "#DFAF8F"
+ "#CC9393"
+ "#DC8CC3"
+ "#94BFF3"
+ "#9FC59F"
+ "#D0BF8F"
+ "#DCA3A3"))
+
+
;;; Minor mode
;;;###autoload
diff --git a/languages/javascript/.jshintrc b/languages/javascript/.jshintrc
deleted file mode 100644
index 82211c2..0000000
--- a/languages/javascript/.jshintrc
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "bitwise": true,
- "camelcase": true,
- "curly": true,
- "devel": true,
- "eqeqeq": true,
- "forin": true,
- "freeze": true,
- "funcscope": true,
- "immed": true,
- "indent": 4,
- "latedef": true,
- "newcap": true,
- "noarg": true,
- "node": true,
- "noempty": true,
- "nonbsp": true,
- "nonew": true,
- "plusplus": true,
- "quotmark": "single",
- "strict": true,
- "undef": true,
- "unused": "strict"
-}
diff --git a/languages/javascript/binaries/scopifier
b/languages/javascript/binaries/scopifier
deleted file mode 100755
index 0c0a149..0000000
--- a/languages/javascript/binaries/scopifier
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env node
-
-// Copyright (C) 2014-2015 Free Software Foundation, Inc.
-
-// This file is part of GNU Emacs.
-
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-'use strict';
-
-var scopifier = require('../scopifier'),
- whole = '';
-
-process.stdin.setEncoding('utf8');
-
-process.stdin.on('readable', function () {
- var chunk = process.stdin.read();
- if (chunk !== null) {
- whole += chunk;
- }
-});
-
-process.stdin.on('end', function () {
- whole = whole.replace(/\r\n/g, '\n'); // Windows
- console.log(JSON.stringify(scopifier(whole)));
-});
diff --git a/languages/javascript/libraries/escope.js
b/languages/javascript/libraries/escope.js
deleted file mode 100644
index b42783c..0000000
--- a/languages/javascript/libraries/escope.js
+++ /dev/null
@@ -1,1117 +0,0 @@
-/*
- Copyright (C) 2012-2013 Yusuke Suzuki <address@hidden>
- Copyright (C) 2013 Alex Seville <address@hidden>
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-/**
- * Escope (<a href="http://github.com/Constellation/escope">escope</a>) is an
<a
- *
href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript</a>
- * scope analyzer extracted from the <a
- * href="http://github.com/Constellation/esmangle">esmangle project</a/>.
- * <p>
- * <em>escope</em> finds lexical scopes in a source program, i.e. areas of that
- * program where different occurrences of the same identifier refer to the same
- * variable. With each scope the contained variables are collected, and each
- * identifier reference in code is linked to its corresponding variable (if
- * possible).
- * <p>
- * <em>escope</em> works on a syntax tree of the parsed source code which has
- * to adhere to the <a
- * href="https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API">
- * Mozilla Parser API</a>. E.g. <a href="http://esprima.org">esprima</a> is a
parser
- * that produces such syntax trees.
- * <p>
- * The main interface is the address@hidden analyze} function.
- * @module
- */
-
-/*jslint bitwise:true */
-/*global exports:true, define:true, require:true*/
-(function (factory, global) {
- 'use strict';
-
- function namespace(str, obj) {
- var i, iz, names, name;
- names = str.split('.');
- for (i = 0, iz = names.length; i < iz; ++i) {
- name = names[i];
- if (obj.hasOwnProperty(name)) {
- obj = obj[name];
- } else {
- obj = (obj[name] = {});
- }
- }
- return obj;
- }
-
- // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
- // and plain browser loading,
- if (typeof define === 'function' && define.amd) {
- define('escope', ['exports', 'estraverse'], function (exports,
estraverse) {
- factory(exports, global, estraverse);
- });
- } else if (typeof exports !== 'undefined') {
- factory(exports, global, require('./estraverse'));
- } else {
- factory(namespace('escope', global), global, global.estraverse);
- }
-}(function (exports, global, estraverse) {
- 'use strict';
-
- var Syntax,
- Map,
- currentScope,
- globalScope,
- scopes,
- options;
-
- Syntax = estraverse.Syntax;
-
- if (typeof global.Map !== 'undefined') {
- // ES6 Map
- Map = global.Map;
- } else {
- Map = function Map() {
- this.__data = {};
- };
-
- Map.prototype.get = function MapGet(key) {
- key = '$' + key;
- if (this.__data.hasOwnProperty(key)) {
- return this.__data[key];
- }
- return undefined;
- };
-
- Map.prototype.has = function MapHas(key) {
- key = '$' + key;
- return this.__data.hasOwnProperty(key);
- };
-
- Map.prototype.set = function MapSet(key, val) {
- key = '$' + key;
- this.__data[key] = val;
- };
-
- Map.prototype['delete'] = function MapDelete(key) {
- key = '$' + key;
- return delete this.__data[key];
- };
- }
-
- function assert(cond, text) {
- if (!cond) {
- throw new Error(text);
- }
- }
-
- function defaultOptions() {
- return {
- optimistic: false,
- directive: false
- };
- }
-
- function updateDeeply(target, override) {
- var key, val;
-
- function isHashObject(target) {
- return typeof target === 'object' && target instanceof Object &&
!(target instanceof RegExp);
- }
-
- for (key in override) {
- if (override.hasOwnProperty(key)) {
- val = override[key];
- if (isHashObject(val)) {
- if (isHashObject(target[key])) {
- updateDeeply(target[key], val);
- } else {
- target[key] = updateDeeply({}, val);
- }
- } else {
- target[key] = val;
- }
- }
- }
- return target;
- }
-
- /**
- * A Reference represents a single occurrence of an identifier in code.
- * @class Reference
- */
- function Reference(ident, scope, flag, writeExpr, maybeImplicitGlobal) {
- /**
- * Identifier syntax node.
- * @member {esprima#Identifier} Reference#identifier
- */
- this.identifier = ident;
- /**
- * Reference to the enclosing Scope.
- * @member {Scope} Reference#from
- */
- this.from = scope;
- /**
- * Whether the reference comes from a dynamic scope (such as 'eval',
- * 'with', etc.), and may be trapped by dynamic scopes.
- * @member {boolean} Reference#tainted
- */
- this.tainted = false;
- /**
- * The variable this reference is resolved with.
- * @member {Variable} Reference#resolved
- */
- this.resolved = null;
- /**
- * The read-write mode of the reference. (Value is one of
address@hidden
- * Reference.READ}, address@hidden Reference.RW}, address@hidden
Reference.WRITE}).
- * @member {number} Reference#flag
- * @private
- */
- this.flag = flag;
- if (this.isWrite()) {
- /**
- * If reference is writeable, this is the tree being written to it.
- * @member {esprima#Node} Reference#writeExpr
- */
- this.writeExpr = writeExpr;
- }
- /**
- * Whether the Reference might refer to a global variable.
- * @member {boolean} Reference#__maybeImplicitGlobal
- * @private
- */
- this.__maybeImplicitGlobal = maybeImplicitGlobal;
- }
-
- /**
- * @constant Reference.READ
- * @private
- */
- Reference.READ = 0x1;
- /**
- * @constant Reference.WRITE
- * @private
- */
- Reference.WRITE = 0x2;
- /**
- * @constant Reference.RW
- * @private
- */
- Reference.RW = 0x3;
-
- /**
- * Whether the reference is static.
- * @method Reference#isStatic
- * @return {boolean}
- */
- Reference.prototype.isStatic = function isStatic() {
- return !this.tainted && this.resolved &&
this.resolved.scope.isStatic();
- };
-
- /**
- * Whether the reference is writeable.
- * @method Reference#isWrite
- * @return {boolean}
- */
- Reference.prototype.isWrite = function isWrite() {
- return this.flag & Reference.WRITE;
- };
-
- /**
- * Whether the reference is readable.
- * @method Reference#isRead
- * @return {boolean}
- */
- Reference.prototype.isRead = function isRead() {
- return this.flag & Reference.READ;
- };
-
- /**
- * Whether the reference is read-only.
- * @method Reference#isReadOnly
- * @return {boolean}
- */
- Reference.prototype.isReadOnly = function isReadOnly() {
- return this.flag === Reference.READ;
- };
-
- /**
- * Whether the reference is write-only.
- * @method Reference#isWriteOnly
- * @return {boolean}
- */
- Reference.prototype.isWriteOnly = function isWriteOnly() {
- return this.flag === Reference.WRITE;
- };
-
- /**
- * Whether the reference is read-write.
- * @method Reference#isReadWrite
- * @return {boolean}
- */
- Reference.prototype.isReadWrite = function isReadWrite() {
- return this.flag === Reference.RW;
- };
-
- /**
- * A Variable represents a locally scoped identifier. These include
arguments to
- * functions.
- * @class Variable
- */
- function Variable(name, scope) {
- /**
- * The variable name, as given in the source code.
- * @member {String} Variable#name
- */
- this.name = name;
- /**
- * List of defining occurrences of this variable (like in 'var ...'
- * statements or as parameter), as AST nodes.
- * @member {esprima.Identifier[]} Variable#identifiers
- */
- this.identifiers = [];
- /**
- * List of address@hidden Reference|references} of this variable
(excluding parameter entries)
- * in its defining scope and all nested scopes. For defining
- * occurrences only see address@hidden Variable#defs}.
- * @member {Reference[]} Variable#references
- */
- this.references = [];
-
- /**
- * List of defining occurrences of this variable (like in 'var ...'
- * statements or as parameter), as custom objects.
- * @typedef {Object} DefEntry
- * @property {String} DefEntry.type - the type of the occurrence (e.g.
- * "Parameter", "Variable", ...)
- * @property {esprima.Identifier} DefEntry.name - the identifier AST
node of the occurrence
- * @property {esprima.Node} DefEntry.node - the enclosing node of the
- * identifier
- * @property {esprima.Node} [DefEntry.parent] - the enclosing statement
- * node of the identifier
- * @member {DefEntry[]} Variable#defs
- */
- this.defs = [];
-
- this.tainted = false;
- /**
- * Whether this is a stack variable.
- * @member {boolean} Variable#stack
- */
- this.stack = true;
- /**
- * Reference to the enclosing Scope.
- * @member {Scope} Variable#scope
- */
- this.scope = scope;
- }
-
- Variable.CatchClause = 'CatchClause';
- Variable.Parameter = 'Parameter';
- Variable.FunctionName = 'FunctionName';
- Variable.Variable = 'Variable';
- Variable.ImplicitGlobalVariable = 'ImplicitGlobalVariable';
-
- function isStrictScope(scope, block) {
- var body, i, iz, stmt, expr;
-
- // When upper scope is exists and strict, inner scope is also strict.
- if (scope.upper && scope.upper.isStrict) {
- return true;
- }
-
- if (scope.type === 'function') {
- body = block.body;
- } else if (scope.type === 'global') {
- body = block;
- } else {
- return false;
- }
-
- if (options.directive) {
- for (i = 0, iz = body.body.length; i < iz; ++i) {
- stmt = body.body[i];
- if (stmt.type !== 'DirectiveStatement') {
- break;
- }
- if (stmt.raw === '"use strict"' || stmt.raw === '\'use
strict\'') {
- return true;
- }
- }
- } else {
- for (i = 0, iz = body.body.length; i < iz; ++i) {
- stmt = body.body[i];
- if (stmt.type !== Syntax.ExpressionStatement) {
- break;
- }
- expr = stmt.expression;
- if (expr.type !== Syntax.Literal || typeof expr.value !==
'string') {
- break;
- }
- if (expr.raw != null) {
- if (expr.raw === '"use strict"' || expr.raw === '\'use
strict\'') {
- return true;
- }
- } else {
- if (expr.value === 'use strict') {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * @class Scope
- */
- function Scope(block, opt) {
- var variable, body;
-
- /**
- * One of 'catch', 'with', 'function' or 'global'.
- * @member {String} Scope#type
- */
- this.type =
- (block.type === Syntax.CatchClause) ? 'catch' :
- (block.type === Syntax.WithStatement) ? 'with' :
- (block.type === Syntax.Program) ? 'global' : 'function';
- /**
- * The scoped address@hidden Variable}s of this scope, as <code>{
Variable.name
- * : Variable }</code>.
- * @member {Map} Scope#set
- */
- this.set = new Map();
- /**
- * The tainted variables of this scope, as <code>{ Variable.name :
- * boolean }</code>.
- * @member {Map} Scope#taints */
- this.taints = new Map();
- /**
- * Generally, through the lexical scoping of JS you can always know
- * which variable an identifier in the source code refers to. There are
- * a few exceptions to this rule. With 'global' and 'with' scopes you
- * can only decide at runtime which variable a reference refers to.
- * Moreover, if 'eval()' is used in a scope, it might introduce new
- * bindings in this or its prarent scopes.
- * All those scopes are considered 'dynamic'.
- * @member {boolean} Scope#dynamic
- */
- this.dynamic = this.type === 'global' || this.type === 'with';
- /**
- * A reference to the scope-defining syntax node.
- * @member {esprima.Node} Scope#block
- */
- this.block = block;
- /**
- * The address@hidden Reference|references} that are not resolved with
this scope.
- * @member {Reference[]} Scope#through
- */
- this.through = [];
- /**
- * The scoped address@hidden Variable}s of this scope. In the case of a
- * 'function' scope this includes the automatic argument
<em>arguments</em> as
- * its first element, as well as all further formal arguments.
- * @member {Variable[]} Scope#variables
- */
- this.variables = [];
- /**
- * Any variable address@hidden Reference|reference} found in this
scope. This
- * includes occurrences of local variables as well as variables from
- * parent scopes (including the global scope). For local variables
- * this also includes defining occurrences (like in a 'var' statement).
- * In a 'function' scope this does not include the occurrences of the
- * formal parameter in the parameter list.
- * @member {Reference[]} Scope#references
- */
- this.references = [];
- /**
- * List of address@hidden Reference}s that are left to be resolved
(i.e. which
- * need to be linked to the variable they refer to). Used internally to
- * resolve bindings during scope analysis. On a finalized scope
- * analysis, all sopes have <em>left</em> value <strong>null</strong>.
- * @member {Reference[]} Scope#left
- */
- this.left = [];
- /**
- * For 'global' and 'function' scopes, this is a self-reference. For
- * other scope types this is the <em>variableScope</em> value of the
- * parent scope.
- * @member {Scope} Scope#variableScope
- */
- this.variableScope =
- (this.type === 'global' || this.type === 'function') ? this :
currentScope.variableScope;
- /**
- * Whether this scope is created by a FunctionExpression.
- * @member {boolean} Scope#functionExpressionScope
- */
- this.functionExpressionScope = false;
- /**
- * Whether this is a scope that contains an 'eval()' invocation.
- * @member {boolean} Scope#directCallToEvalScope
- */
- this.directCallToEvalScope = false;
- /**
- * @member {boolean} Scope#thisFound
- */
- this.thisFound = false;
- body = this.type === 'function' ? block.body : block;
- if (opt.naming) {
- this.__define(block.id, {
- type: Variable.FunctionName,
- name: block.id,
- node: block
- });
- this.functionExpressionScope = true;
- } else {
- if (this.type === 'function') {
- variable = new Variable('arguments', this);
- this.taints.set('arguments', true);
- this.set.set('arguments', variable);
- this.variables.push(variable);
- }
-
- if (block.type === Syntax.FunctionExpression && block.id) {
- new Scope(block, { naming: true });
- }
- }
-
- /**
- * Reference to the parent address@hidden Scope|scope}.
- * @member {Scope} Scope#upper
- */
- this.upper = currentScope;
- /**
- * Whether 'use strict' is in effect in this scope.
- * @member {boolean} Scope#isStrict
- */
- this.isStrict = isStrictScope(this, block);
-
- /**
- * List of nested address@hidden Scope}s.
- * @member {Scope[]} Scope#childScopes
- */
- this.childScopes = [];
- if (currentScope) {
- currentScope.childScopes.push(this);
- }
-
-
- // RAII
- currentScope = this;
- if (this.type === 'global') {
- globalScope = this;
- globalScope.implicit = {
- set: new Map(),
- variables: []
- };
- }
- scopes.push(this);
- }
-
- Scope.prototype.__close = function __close() {
- var i, iz, ref, current, node, implicit;
-
- // Because if this is global environment, upper is null
- if (!this.dynamic || options.optimistic) {
- // static resolve
- for (i = 0, iz = this.left.length; i < iz; ++i) {
- ref = this.left[i];
- if (!this.__resolve(ref)) {
- this.__delegateToUpperScope(ref);
- }
- }
- } else {
- // this is "global" / "with" / "function with eval" environment
- if (this.type === 'with') {
- for (i = 0, iz = this.left.length; i < iz; ++i) {
- ref = this.left[i];
- ref.tainted = true;
- this.__delegateToUpperScope(ref);
- }
- } else {
- for (i = 0, iz = this.left.length; i < iz; ++i) {
- // notify all names are through to global
- ref = this.left[i];
- current = this;
- do {
- current.through.push(ref);
- current = current.upper;
- } while (current);
- }
- }
- }
-
- if (this.type === 'global') {
- implicit = [];
- for (i = 0, iz = this.left.length; i < iz; ++i) {
- ref = this.left[i];
- if (ref.__maybeImplicitGlobal &&
!this.set.has(ref.identifier.name)) {
- implicit.push(ref.__maybeImplicitGlobal);
- }
- }
-
- // create an implicit global variable from assignment expression
- for (i = 0, iz = implicit.length; i < iz; ++i) {
- node = implicit[i];
- this.__defineImplicit(node.left, {
- type: Variable.ImplicitGlobalVariable,
- name: node.left,
- node: node
- });
- }
- }
-
- this.left = null;
- currentScope = this.upper;
- };
-
- Scope.prototype.__resolve = function __resolve(ref) {
- var variable, name;
- name = ref.identifier.name;
- if (this.set.has(name)) {
- variable = this.set.get(name);
- variable.references.push(ref);
- variable.stack = variable.stack && ref.from.variableScope ===
this.variableScope;
- if (ref.tainted) {
- variable.tainted = true;
- this.taints.set(variable.name, true);
- }
- ref.resolved = variable;
- return true;
- }
- return false;
- };
-
- Scope.prototype.__delegateToUpperScope = function
__delegateToUpperScope(ref) {
- if (this.upper) {
- this.upper.left.push(ref);
- }
- this.through.push(ref);
- };
-
- Scope.prototype.__defineImplicit = function __defineImplicit(node, info) {
- var name, variable;
- if (node && node.type === Syntax.Identifier) {
- name = node.name;
- if (!this.implicit.set.has(name)) {
- variable = new Variable(name, this);
- variable.identifiers.push(node);
- variable.defs.push(info);
- this.implicit.set.set(name, variable);
- this.implicit.variables.push(variable);
- } else {
- variable = this.implicit.set.get(name);
- variable.identifiers.push(node);
- variable.defs.push(info);
- }
- }
- };
-
- Scope.prototype.__define = function __define(node, info) {
- var name, variable;
- if (node && node.type === Syntax.Identifier) {
- name = node.name;
- if (!this.set.has(name)) {
- variable = new Variable(name, this);
- variable.identifiers.push(node);
- variable.defs.push(info);
- this.set.set(name, variable);
- this.variables.push(variable);
- } else {
- variable = this.set.get(name);
- variable.identifiers.push(node);
- variable.defs.push(info);
- }
- }
- };
-
- Scope.prototype.__referencing = function __referencing(node, assign,
writeExpr, maybeImplicitGlobal) {
- var ref;
- // because Array element may be null
- if (node && node.type === Syntax.Identifier) {
- ref = new Reference(node, this, assign || Reference.READ,
writeExpr, maybeImplicitGlobal);
- this.references.push(ref);
- this.left.push(ref);
- }
- };
-
- Scope.prototype.__detectEval = function __detectEval() {
- var current;
- current = this;
- this.directCallToEvalScope = true;
- do {
- current.dynamic = true;
- current = current.upper;
- } while (current);
- };
-
- Scope.prototype.__detectThis = function __detectThis() {
- this.thisFound = true;
- };
-
- Scope.prototype.__isClosed = function isClosed() {
- return this.left === null;
- };
-
- // API Scope#resolve(name)
- // returns resolved reference
- Scope.prototype.resolve = function resolve(ident) {
- var ref, i, iz;
- assert(this.__isClosed(), 'scope should be closed');
- assert(ident.type === Syntax.Identifier, 'target should be
identifier');
- for (i = 0, iz = this.references.length; i < iz; ++i) {
- ref = this.references[i];
- if (ref.identifier === ident) {
- return ref;
- }
- }
- return null;
- };
-
- // API Scope#isStatic
- // returns this scope is static
- Scope.prototype.isStatic = function isStatic() {
- return !this.dynamic;
- };
-
- // API Scope#isArgumentsMaterialized
- // return this scope has materialized arguments
- Scope.prototype.isArgumentsMaterialized = function
isArgumentsMaterialized() {
- // TODO(Constellation)
- // We can more aggressive on this condition like this.
- //
- // function t() {
- // // arguments of t is always hidden.
- // function arguments() {
- // }
- // }
- var variable;
-
- // This is not function scope
- if (this.type !== 'function') {
- return true;
- }
-
- if (!this.isStatic()) {
- return true;
- }
-
- variable = this.set.get('arguments');
- assert(variable, 'always have arguments variable');
- return variable.tainted || variable.references.length !== 0;
- };
-
- // API Scope#isThisMaterialized
- // return this scope has materialized `this` reference
- Scope.prototype.isThisMaterialized = function isThisMaterialized() {
- // This is not function scope
- if (this.type !== 'function') {
- return true;
- }
- if (!this.isStatic()) {
- return true;
- }
- return this.thisFound;
- };
-
- Scope.mangledName = '__$escope$__';
-
- Scope.prototype.attach = function attach() {
- if (!this.functionExpressionScope) {
- this.block[Scope.mangledName] = this;
- }
- };
-
- Scope.prototype.detach = function detach() {
- if (!this.functionExpressionScope) {
- delete this.block[Scope.mangledName];
- }
- };
-
- Scope.prototype.isUsedName = function (name) {
- if (this.set.has(name)) {
- return true;
- }
- for (var i = 0, iz = this.through.length; i < iz; ++i) {
- if (this.through[i].identifier.name === name) {
- return true;
- }
- }
- return false;
- };
-
- /**
- * @class ScopeManager
- */
- function ScopeManager(scopes) {
- this.scopes = scopes;
- this.attached = false;
- }
-
- // Returns appropliate scope for this node
- ScopeManager.prototype.__get = function __get(node) {
- var i, iz, scope;
- if (this.attached) {
- return node[Scope.mangledName] || null;
- }
- if (Scope.isScopeRequired(node)) {
- for (i = 0, iz = this.scopes.length; i < iz; ++i) {
- scope = this.scopes[i];
- if (!scope.functionExpressionScope) {
- if (scope.block === node) {
- return scope;
- }
- }
- }
- }
- return null;
- };
-
- ScopeManager.prototype.acquire = function acquire(node) {
- return this.__get(node);
- };
-
- ScopeManager.prototype.release = function release(node) {
- var scope = this.__get(node);
- if (scope) {
- scope = scope.upper;
- while (scope) {
- if (!scope.functionExpressionScope) {
- return scope;
- }
- scope = scope.upper;
- }
- }
- return null;
- };
-
- ScopeManager.prototype.attach = function attach() {
- var i, iz;
- for (i = 0, iz = this.scopes.length; i < iz; ++i) {
- this.scopes[i].attach();
- }
- this.attached = true;
- };
-
- ScopeManager.prototype.detach = function detach() {
- var i, iz;
- for (i = 0, iz = this.scopes.length; i < iz; ++i) {
- this.scopes[i].detach();
- }
- this.attached = false;
- };
-
- Scope.isScopeRequired = function isScopeRequired(node) {
- return Scope.isVariableScopeRequired(node) || node.type ===
Syntax.WithStatement || node.type === Syntax.CatchClause;
- };
-
- Scope.isVariableScopeRequired = function isVariableScopeRequired(node) {
- return node.type === Syntax.Program || node.type ===
Syntax.FunctionExpression || node.type === Syntax.FunctionDeclaration;
- };
-
- /**
- * Main interface function. Takes an Esprima syntax tree and returns the
- * analyzed scopes.
- * @function analyze
- * @param {esprima.Tree} tree
- * @param {Object} providedOptions - Options that tailor the scope analysis
- * @param {boolean} [providedOptions.optimistic=false] - the optimistic
flag
- * @param {boolean} [providedOptions.directive=false]- the directive flag
- * @param {boolean} [providedOptions.ignoreEval=false]- whether to check
'eval()' calls
- * @return {ScopeManager}
- */
- function analyze(tree, providedOptions) {
- var resultScopes;
-
- options = updateDeeply(defaultOptions(), providedOptions);
- resultScopes = scopes = [];
- currentScope = null;
- globalScope = null;
-
- // attach scope and collect / resolve names
- estraverse.traverse(tree, {
- enter: function enter(node) {
- var i, iz, decl;
- if (Scope.isScopeRequired(node)) {
- new Scope(node, {});
- }
-
- switch (node.type) {
- case Syntax.AssignmentExpression:
- if (node.operator === '=') {
- currentScope.__referencing(node.left, Reference.WRITE,
node.right, (!currentScope.isStrict && node.left.name != null) && node);
- } else {
- currentScope.__referencing(node.left, Reference.RW,
node.right);
- }
- currentScope.__referencing(node.right);
- break;
-
- case Syntax.ArrayExpression:
- for (i = 0, iz = node.elements.length; i < iz; ++i) {
- currentScope.__referencing(node.elements[i]);
- }
- break;
-
- case Syntax.BlockStatement:
- break;
-
- case Syntax.BinaryExpression:
- currentScope.__referencing(node.left);
- currentScope.__referencing(node.right);
- break;
-
- case Syntax.BreakStatement:
- break;
-
- case Syntax.CallExpression:
- currentScope.__referencing(node.callee);
- for (i = 0, iz = node['arguments'].length; i < iz; ++i) {
- currentScope.__referencing(node['arguments'][i]);
- }
-
- // check this is direct call to eval
- if (!options.ignoreEval && node.callee.type ===
Syntax.Identifier && node.callee.name === 'eval') {
- currentScope.variableScope.__detectEval();
- }
- break;
-
- case Syntax.CatchClause:
- currentScope.__define(node.param, {
- type: Variable.CatchClause,
- name: node.param,
- node: node
- });
- break;
-
- case Syntax.ConditionalExpression:
- currentScope.__referencing(node.test);
- currentScope.__referencing(node.consequent);
- currentScope.__referencing(node.alternate);
- break;
-
- case Syntax.ContinueStatement:
- break;
-
- case Syntax.DirectiveStatement:
- break;
-
- case Syntax.DoWhileStatement:
- currentScope.__referencing(node.test);
- break;
-
- case Syntax.DebuggerStatement:
- break;
-
- case Syntax.EmptyStatement:
- break;
-
- case Syntax.ExpressionStatement:
- currentScope.__referencing(node.expression);
- break;
-
- case Syntax.ForStatement:
- currentScope.__referencing(node.init);
- currentScope.__referencing(node.test);
- currentScope.__referencing(node.update);
- break;
-
- case Syntax.ForInStatement:
- if (node.left.type === Syntax.VariableDeclaration) {
-
currentScope.__referencing(node.left.declarations[0].id, Reference.WRITE, null,
false);
- } else {
- currentScope.__referencing(node.left, Reference.WRITE,
null, (!currentScope.isStrict && node.left.name != null) && node);
- }
- currentScope.__referencing(node.right);
- break;
-
- case Syntax.FunctionDeclaration:
- // FunctionDeclaration name is defined in upper scope
- currentScope.upper.__define(node.id, {
- type: Variable.FunctionName,
- name: node.id,
- node: node
- });
- for (i = 0, iz = node.params.length; i < iz; ++i) {
- currentScope.__define(node.params[i], {
- type: Variable.Parameter,
- name: node.params[i],
- node: node,
- index: i
- });
- }
- break;
-
- case Syntax.FunctionExpression:
- // id is defined in upper scope
- for (i = 0, iz = node.params.length; i < iz; ++i) {
- currentScope.__define(node.params[i], {
- type: Variable.Parameter,
- name: node.params[i],
- node: node,
- index: i
- });
- }
- break;
-
- case Syntax.Identifier:
- break;
-
- case Syntax.IfStatement:
- currentScope.__referencing(node.test);
- break;
-
- case Syntax.Literal:
- break;
-
- case Syntax.LabeledStatement:
- break;
-
- case Syntax.LogicalExpression:
- currentScope.__referencing(node.left);
- currentScope.__referencing(node.right);
- break;
-
- case Syntax.MemberExpression:
- currentScope.__referencing(node.object);
- if (node.computed) {
- currentScope.__referencing(node.property);
- }
- break;
-
- case Syntax.NewExpression:
- currentScope.__referencing(node.callee);
- for (i = 0, iz = node['arguments'].length; i < iz; ++i) {
- currentScope.__referencing(node['arguments'][i]);
- }
- break;
-
- case Syntax.ObjectExpression:
- break;
-
- case Syntax.Program:
- break;
-
- case Syntax.Property:
- currentScope.__referencing(node.value);
- break;
-
- case Syntax.ReturnStatement:
- currentScope.__referencing(node.argument);
- break;
-
- case Syntax.SequenceExpression:
- for (i = 0, iz = node.expressions.length; i < iz; ++i) {
- currentScope.__referencing(node.expressions[i]);
- }
- break;
-
- case Syntax.SwitchStatement:
- currentScope.__referencing(node.discriminant);
- break;
-
- case Syntax.SwitchCase:
- currentScope.__referencing(node.test);
- break;
-
- case Syntax.ThisExpression:
- currentScope.variableScope.__detectThis();
- break;
-
- case Syntax.ThrowStatement:
- currentScope.__referencing(node.argument);
- break;
-
- case Syntax.TryStatement:
- break;
-
- case Syntax.UnaryExpression:
- currentScope.__referencing(node.argument);
- break;
-
- case Syntax.UpdateExpression:
- currentScope.__referencing(node.argument, Reference.RW,
null);
- break;
-
- case Syntax.VariableDeclaration:
- for (i = 0, iz = node.declarations.length; i < iz; ++i) {
- decl = node.declarations[i];
- currentScope.variableScope.__define(decl.id, {
- type: Variable.Variable,
- name: decl.id,
- node: decl,
- index: i,
- parent: node
- });
- if (decl.init) {
- // initializer is found
- currentScope.__referencing(decl.id,
Reference.WRITE, decl.init, false);
- currentScope.__referencing(decl.init);
- }
- }
- break;
-
- case Syntax.VariableDeclarator:
- break;
-
- case Syntax.WhileStatement:
- currentScope.__referencing(node.test);
- break;
-
- case Syntax.WithStatement:
- // WithStatement object is referenced at upper scope
- currentScope.upper.__referencing(node.object);
- break;
- }
- },
-
- leave: function leave(node) {
- while (currentScope && node === currentScope.block) {
- currentScope.__close();
- }
- }
- });
-
- assert(currentScope === null);
- globalScope = null;
- scopes = null;
- options = null;
-
- return new ScopeManager(resultScopes);
- }
-
- /** @name module:escope.version */
- exports.version = '1.0.1';
- /** @name module:escope.Reference */
- exports.Reference = Reference;
- /** @name module:escope.Variable */
- exports.Variable = Variable;
- /** @name module:escope.Scope */
- exports.Scope = Scope;
- /** @name module:escope.ScopeManager */
- exports.ScopeManager = ScopeManager;
- /** @name module:escope.analyze */
- exports.analyze = analyze;
-}, this));
-/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/languages/javascript/libraries/esprima.js
b/languages/javascript/libraries/esprima.js
deleted file mode 100644
index 593021f..0000000
--- a/languages/javascript/libraries/esprima.js
+++ /dev/null
@@ -1,3756 +0,0 @@
-/*
- Copyright (C) 2013 Ariya Hidayat <address@hidden>
- Copyright (C) 2013 Thaddee Tyl <address@hidden>
- Copyright (C) 2013 Mathias Bynens <address@hidden>
- Copyright (C) 2012 Ariya Hidayat <address@hidden>
- Copyright (C) 2012 Mathias Bynens <address@hidden>
- Copyright (C) 2012 Joost-Wim Boekesteijn <address@hidden>
- Copyright (C) 2012 Kris Kowal <address@hidden>
- Copyright (C) 2012 Yusuke Suzuki <address@hidden>
- Copyright (C) 2012 Arpad Borsos <address@hidden>
- Copyright (C) 2011 Ariya Hidayat <address@hidden>
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-/*jslint bitwise:true plusplus:true */
-/*global esprima:true, define:true, exports:true, window: true,
-throwErrorTolerant: true,
-throwError: true, generateStatement: true, peek: true,
-parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
-parseFunctionDeclaration: true, parseFunctionExpression: true,
-parseFunctionSourceElements: true, parseVariableIdentifier: true,
-parseLeftHandSideExpression: true,
-parseUnaryExpression: true,
-parseStatement: true, parseSourceElement: true */
-
-(function (root, factory) {
- 'use strict';
-
- // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
- // Rhino, and plain browser loading.
-
- /* istanbul ignore next */
- if (typeof define === 'function' && define.amd) {
- define(['exports'], factory);
- } else if (typeof exports !== 'undefined') {
- factory(exports);
- } else {
- factory((root.esprima = {}));
- }
-}(this, function (exports) {
- 'use strict';
-
- var Token,
- TokenName,
- FnExprTokens,
- Syntax,
- PropertyKind,
- Messages,
- Regex,
- SyntaxTreeDelegate,
- source,
- strict,
- index,
- lineNumber,
- lineStart,
- length,
- delegate,
- lookahead,
- state,
- extra;
-
- Token = {
- BooleanLiteral: 1,
- EOF: 2,
- Identifier: 3,
- Keyword: 4,
- NullLiteral: 5,
- NumericLiteral: 6,
- Punctuator: 7,
- StringLiteral: 8,
- RegularExpression: 9
- };
-
- TokenName = {};
- TokenName[Token.BooleanLiteral] = 'Boolean';
- TokenName[Token.EOF] = '<end>';
- TokenName[Token.Identifier] = 'Identifier';
- TokenName[Token.Keyword] = 'Keyword';
- TokenName[Token.NullLiteral] = 'Null';
- TokenName[Token.NumericLiteral] = 'Numeric';
- TokenName[Token.Punctuator] = 'Punctuator';
- TokenName[Token.StringLiteral] = 'String';
- TokenName[Token.RegularExpression] = 'RegularExpression';
-
- // A function following one of those tokens is an expression.
- FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new',
- 'return', 'case', 'delete', 'throw', 'void',
- // assignment operators
- '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=',
- '&=', '|=', '^=', ',',
- // binary/unary operators
- '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>',
'&',
- '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==',
'>=',
- '<=', '<', '>', '!=', '!=='];
-
- Syntax = {
- AssignmentExpression: 'AssignmentExpression',
- ArrayExpression: 'ArrayExpression',
- BlockStatement: 'BlockStatement',
- BinaryExpression: 'BinaryExpression',
- BreakStatement: 'BreakStatement',
- CallExpression: 'CallExpression',
- CatchClause: 'CatchClause',
- ConditionalExpression: 'ConditionalExpression',
- ContinueStatement: 'ContinueStatement',
- DoWhileStatement: 'DoWhileStatement',
- DebuggerStatement: 'DebuggerStatement',
- EmptyStatement: 'EmptyStatement',
- ExpressionStatement: 'ExpressionStatement',
- ForStatement: 'ForStatement',
- ForInStatement: 'ForInStatement',
- FunctionDeclaration: 'FunctionDeclaration',
- FunctionExpression: 'FunctionExpression',
- Identifier: 'Identifier',
- IfStatement: 'IfStatement',
- Literal: 'Literal',
- LabeledStatement: 'LabeledStatement',
- LogicalExpression: 'LogicalExpression',
- MemberExpression: 'MemberExpression',
- NewExpression: 'NewExpression',
- ObjectExpression: 'ObjectExpression',
- Program: 'Program',
- Property: 'Property',
- ReturnStatement: 'ReturnStatement',
- SequenceExpression: 'SequenceExpression',
- SwitchStatement: 'SwitchStatement',
- SwitchCase: 'SwitchCase',
- ThisExpression: 'ThisExpression',
- ThrowStatement: 'ThrowStatement',
- TryStatement: 'TryStatement',
- UnaryExpression: 'UnaryExpression',
- UpdateExpression: 'UpdateExpression',
- VariableDeclaration: 'VariableDeclaration',
- VariableDeclarator: 'VariableDeclarator',
- WhileStatement: 'WhileStatement',
- WithStatement: 'WithStatement'
- };
-
- PropertyKind = {
- Data: 1,
- Get: 2,
- Set: 4
- };
-
- // Error messages should be identical to V8.
- Messages = {
- UnexpectedToken: 'Unexpected token %0',
- UnexpectedNumber: 'Unexpected number',
- UnexpectedString: 'Unexpected string',
- UnexpectedIdentifier: 'Unexpected identifier',
- UnexpectedReserved: 'Unexpected reserved word',
- UnexpectedEOS: 'Unexpected end of input',
- NewlineAfterThrow: 'Illegal newline after throw',
- InvalidRegExp: 'Invalid regular expression',
- UnterminatedRegExp: 'Invalid regular expression: missing /',
- InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
- InvalidLHSInForIn: 'Invalid left-hand side in for-in',
- MultipleDefaultsInSwitch: 'More than one default clause in switch
statement',
- NoCatchOrFinally: 'Missing catch or finally after try',
- UnknownLabel: 'Undefined label \'%0\'',
- Redeclaration: '%0 \'%1\' has already been declared',
- IllegalContinue: 'Illegal continue statement',
- IllegalBreak: 'Illegal break statement',
- IllegalReturn: 'Illegal return statement',
- StrictModeWith: 'Strict mode code may not include a with statement',
- StrictCatchVariable: 'Catch variable may not be eval or arguments in
strict mode',
- StrictVarName: 'Variable name may not be eval or arguments in strict
mode',
- StrictParamName: 'Parameter name eval or arguments is not allowed in
strict mode',
- StrictParamDupe: 'Strict mode function may not have duplicate
parameter names',
- StrictFunctionName: 'Function name may not be eval or arguments in
strict mode',
- StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
- StrictDelete: 'Delete of an unqualified identifier in strict mode.',
- StrictDuplicateProperty: 'Duplicate data property in object literal
not allowed in strict mode',
- AccessorDataProperty: 'Object literal may not have data and accessor
property with the same name',
- AccessorGetSet: 'Object literal may not have multiple get/set
accessors with the same name',
- StrictLHSAssignment: 'Assignment to eval or arguments is not allowed
in strict mode',
- StrictLHSPostfix: 'Postfix increment/decrement may not have eval or
arguments operand in strict mode',
- StrictLHSPrefix: 'Prefix increment/decrement may not have eval or
arguments operand in strict mode',
- StrictReservedWord: 'Use of future reserved word in strict mode'
- };
-
- // See also tools/generate-unicode-regex.py.
- Regex = {
- NonAsciiIdentifierStart: new
RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-
[...]
- NonAsciiIdentifierPart: new
RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u08
[...]
- };
-
- // Ensure the condition is true, otherwise throw an error.
- // This is only to have a better contract semantic, i.e. another safety net
- // to catch a logic error. The condition shall be fulfilled in normal case.
- // Do NOT use this to enforce a certain condition on any user input.
-
- function assert(condition, message) {
- /* istanbul ignore if */
- if (!condition) {
- throw new Error('ASSERT: ' + message);
- }
- }
-
- function isDecimalDigit(ch) {
- return (ch >= 48 && ch <= 57); // 0..9
- }
-
- function isHexDigit(ch) {
- return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
- }
-
- function isOctalDigit(ch) {
- return '01234567'.indexOf(ch) >= 0;
- }
-
-
- // 7.2 White Space
-
- function isWhiteSpace(ch) {
- return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch ===
0x0C) || (ch === 0xA0) ||
- (ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003,
0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000,
0xFEFF].indexOf(ch) >= 0);
- }
-
- // 7.3 Line Terminators
-
- function isLineTerminator(ch) {
- return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch ===
0x2029);
- }
-
- // 7.6 Identifier Names and Identifiers
-
- function isIdentifierStart(ch) {
- return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _
(underscore)
- (ch >= 0x41 && ch <= 0x5A) || // A..Z
- (ch >= 0x61 && ch <= 0x7A) || // a..z
- (ch === 0x5C) || // \ (backslash)
- ((ch >= 0x80) &&
Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch)));
- }
-
- function isIdentifierPart(ch) {
- return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _
(underscore)
- (ch >= 0x41 && ch <= 0x5A) || // A..Z
- (ch >= 0x61 && ch <= 0x7A) || // a..z
- (ch >= 0x30 && ch <= 0x39) || // 0..9
- (ch === 0x5C) || // \ (backslash)
- ((ch >= 0x80) &&
Regex.NonAsciiIdentifierPart.test(String.fromCharCode(ch)));
- }
-
- // 7.6.1.2 Future Reserved Words
-
- function isFutureReservedWord(id) {
- switch (id) {
- case 'class':
- case 'enum':
- case 'export':
- case 'extends':
- case 'import':
- case 'super':
- return true;
- default:
- return false;
- }
- }
-
- function isStrictModeReservedWord(id) {
- switch (id) {
- case 'implements':
- case 'interface':
- case 'package':
- case 'private':
- case 'protected':
- case 'public':
- case 'static':
- case 'yield':
- case 'let':
- return true;
- default:
- return false;
- }
- }
-
- function isRestrictedWord(id) {
- return id === 'eval' || id === 'arguments';
- }
-
- // 7.6.1.1 Keywords
-
- function isKeyword(id) {
- if (strict && isStrictModeReservedWord(id)) {
- return true;
- }
-
- // 'const' is specialized as Keyword in V8.
- // 'yield' and 'let' are for compatiblity with SpiderMonkey and
ES.next.
- // Some others are from future reserved words.
-
- switch (id.length) {
- case 2:
- return (id === 'if') || (id === 'in') || (id === 'do');
- case 3:
- return (id === 'var') || (id === 'for') || (id === 'new') ||
- (id === 'try') || (id === 'let');
- case 4:
- return (id === 'this') || (id === 'else') || (id === 'case') ||
- (id === 'void') || (id === 'with') || (id === 'enum');
- case 5:
- return (id === 'while') || (id === 'break') || (id === 'catch') ||
- (id === 'throw') || (id === 'const') || (id === 'yield') ||
- (id === 'class') || (id === 'super');
- case 6:
- return (id === 'return') || (id === 'typeof') || (id === 'delete')
||
- (id === 'switch') || (id === 'export') || (id === 'import');
- case 7:
- return (id === 'default') || (id === 'finally') || (id ===
'extends');
- case 8:
- return (id === 'function') || (id === 'continue') || (id ===
'debugger');
- case 10:
- return (id === 'instanceof');
- default:
- return false;
- }
- }
-
- // 7.4 Comments
-
- function addComment(type, value, start, end, loc) {
- var comment, attacher;
-
- assert(typeof start === 'number', 'Comment must have valid position');
-
- // Because the way the actual token is scanned, often the comments
- // (if any) are skipped twice during the lexical analysis.
- // Thus, we need to skip adding a comment if the comment array already
- // handled it.
- if (state.lastCommentStart >= start) {
- return;
- }
- state.lastCommentStart = start;
-
- comment = {
- type: type,
- value: value
- };
- if (extra.range) {
- comment.range = [start, end];
- }
- if (extra.loc) {
- comment.loc = loc;
- }
- extra.comments.push(comment);
- if (extra.attachComment) {
- extra.leadingComments.push(comment);
- extra.trailingComments.push(comment);
- }
- }
-
- function skipSingleLineComment(offset) {
- var start, loc, ch, comment;
-
- start = index - offset;
- loc = {
- start: {
- line: lineNumber,
- column: index - lineStart - offset
- }
- };
-
- while (index < length) {
- ch = source.charCodeAt(index);
- ++index;
- if (isLineTerminator(ch)) {
- if (extra.comments) {
- comment = source.slice(start + offset, index - 1);
- loc.end = {
- line: lineNumber,
- column: index - lineStart - 1
- };
- addComment('Line', comment, start, index - 1, loc);
- }
- if (ch === 13 && source.charCodeAt(index) === 10) {
- ++index;
- }
- ++lineNumber;
- lineStart = index;
- return;
- }
- }
-
- if (extra.comments) {
- comment = source.slice(start + offset, index);
- loc.end = {
- line: lineNumber,
- column: index - lineStart
- };
- addComment('Line', comment, start, index, loc);
- }
- }
-
- function skipMultiLineComment() {
- var start, loc, ch, comment;
-
- if (extra.comments) {
- start = index - 2;
- loc = {
- start: {
- line: lineNumber,
- column: index - lineStart - 2
- }
- };
- }
-
- while (index < length) {
- ch = source.charCodeAt(index);
- if (isLineTerminator(ch)) {
- if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) {
- ++index;
- }
- ++lineNumber;
- ++index;
- lineStart = index;
- if (index >= length) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- } else if (ch === 0x2A) {
- // Block comment ends with '*/'.
- if (source.charCodeAt(index + 1) === 0x2F) {
- ++index;
- ++index;
- if (extra.comments) {
- comment = source.slice(start + 2, index - 2);
- loc.end = {
- line: lineNumber,
- column: index - lineStart
- };
- addComment('Block', comment, start, index, loc);
- }
- return;
- }
- ++index;
- } else {
- ++index;
- }
- }
-
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- function skipComment() {
- var ch, start;
-
- start = (index === 0);
- while (index < length) {
- ch = source.charCodeAt(index);
-
- if (isWhiteSpace(ch)) {
- ++index;
- } else if (isLineTerminator(ch)) {
- ++index;
- if (ch === 0x0D && source.charCodeAt(index) === 0x0A) {
- ++index;
- }
- ++lineNumber;
- lineStart = index;
- start = true;
- } else if (ch === 0x2F) { // U+002F is '/'
- ch = source.charCodeAt(index + 1);
- if (ch === 0x2F) {
- ++index;
- ++index;
- skipSingleLineComment(2);
- start = true;
- } else if (ch === 0x2A) { // U+002A is '*'
- ++index;
- ++index;
- skipMultiLineComment();
- } else {
- break;
- }
- } else if (start && ch === 0x2D) { // U+002D is '-'
- // U+003E is '>'
- if ((source.charCodeAt(index + 1) === 0x2D) &&
(source.charCodeAt(index + 2) === 0x3E)) {
- // '-->' is a single-line comment
- index += 3;
- skipSingleLineComment(3);
- } else {
- break;
- }
- } else if (ch === 0x3C) { // U+003C is '<'
- if (source.slice(index + 1, index + 4) === '!--') {
- ++index; // `<`
- ++index; // `!`
- ++index; // `-`
- ++index; // `-`
- skipSingleLineComment(4);
- } else {
- break;
- }
- } else {
- break;
- }
- }
- }
-
- function scanHexEscape(prefix) {
- var i, len, ch, code = 0;
-
- len = (prefix === 'u') ? 4 : 2;
- for (i = 0; i < len; ++i) {
- if (index < length && isHexDigit(source[index])) {
- ch = source[index++];
- code = code * 16 +
'0123456789abcdef'.indexOf(ch.toLowerCase());
- } else {
- return '';
- }
- }
- return String.fromCharCode(code);
- }
-
- function getEscapedIdentifier() {
- var ch, id;
-
- ch = source.charCodeAt(index++);
- id = String.fromCharCode(ch);
-
- // '\u' (U+005C, U+0075) denotes an escaped character.
- if (ch === 0x5C) {
- if (source.charCodeAt(index) !== 0x75) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- ++index;
- ch = scanHexEscape('u');
- if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- id = ch;
- }
-
- while (index < length) {
- ch = source.charCodeAt(index);
- if (!isIdentifierPart(ch)) {
- break;
- }
- ++index;
- id += String.fromCharCode(ch);
-
- // '\u' (U+005C, U+0075) denotes an escaped character.
- if (ch === 0x5C) {
- id = id.substr(0, id.length - 1);
- if (source.charCodeAt(index) !== 0x75) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- ++index;
- ch = scanHexEscape('u');
- if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0)))
{
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- id += ch;
- }
- }
-
- return id;
- }
-
- function getIdentifier() {
- var start, ch;
-
- start = index++;
- while (index < length) {
- ch = source.charCodeAt(index);
- if (ch === 0x5C) {
- // Blackslash (U+005C) marks Unicode escape sequence.
- index = start;
- return getEscapedIdentifier();
- }
- if (isIdentifierPart(ch)) {
- ++index;
- } else {
- break;
- }
- }
-
- return source.slice(start, index);
- }
-
- function scanIdentifier() {
- var start, id, type;
-
- start = index;
-
- // Backslash (U+005C) starts an escaped character.
- id = (source.charCodeAt(index) === 0x5C) ? getEscapedIdentifier() :
getIdentifier();
-
- // There is no keyword or literal with only one character.
- // Thus, it must be an identifier.
- if (id.length === 1) {
- type = Token.Identifier;
- } else if (isKeyword(id)) {
- type = Token.Keyword;
- } else if (id === 'null') {
- type = Token.NullLiteral;
- } else if (id === 'true' || id === 'false') {
- type = Token.BooleanLiteral;
- } else {
- type = Token.Identifier;
- }
-
- return {
- type: type,
- value: id,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
-
- // 7.7 Punctuators
-
- function scanPunctuator() {
- var start = index,
- code = source.charCodeAt(index),
- code2,
- ch1 = source[index],
- ch2,
- ch3,
- ch4;
-
- switch (code) {
-
- // Check for most common single-character punctuators.
- case 0x2E: // . dot
- case 0x28: // ( open bracket
- case 0x29: // ) close bracket
- case 0x3B: // ; semicolon
- case 0x2C: // , comma
- case 0x7B: // { open curly brace
- case 0x7D: // } close curly brace
- case 0x5B: // [
- case 0x5D: // ]
- case 0x3A: // :
- case 0x3F: // ?
- case 0x7E: // ~
- ++index;
- if (extra.tokenize) {
- if (code === 0x28) {
- extra.openParenToken = extra.tokens.length;
- } else if (code === 0x7B) {
- extra.openCurlyToken = extra.tokens.length;
- }
- }
- return {
- type: Token.Punctuator,
- value: String.fromCharCode(code),
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
-
- default:
- code2 = source.charCodeAt(index + 1);
-
- // '=' (U+003D) marks an assignment or comparison operator.
- if (code2 === 0x3D) {
- switch (code) {
- case 0x2B: // +
- case 0x2D: // -
- case 0x2F: // /
- case 0x3C: // <
- case 0x3E: // >
- case 0x5E: // ^
- case 0x7C: // |
- case 0x25: // %
- case 0x26: // &
- case 0x2A: // *
- index += 2;
- return {
- type: Token.Punctuator,
- value: String.fromCharCode(code) +
String.fromCharCode(code2),
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
-
- case 0x21: // !
- case 0x3D: // =
- index += 2;
-
- // !== and ===
- if (source.charCodeAt(index) === 0x3D) {
- ++index;
- }
- return {
- type: Token.Punctuator,
- value: source.slice(start, index),
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
- }
- }
-
- // 4-character punctuator: >>>=
-
- ch4 = source.substr(index, 4);
-
- if (ch4 === '>>>=') {
- index += 4;
- return {
- type: Token.Punctuator,
- value: ch4,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- // 3-character punctuators: === !== >>> <<= >>=
-
- ch3 = ch4.substr(0, 3);
-
- if (ch3 === '>>>' || ch3 === '<<=' || ch3 === '>>=') {
- index += 3;
- return {
- type: Token.Punctuator,
- value: ch3,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- // Other 2-character punctuators: ++ -- << >> && ||
- ch2 = ch3.substr(0, 2);
-
- if ((ch1 === ch2[1] && ('+-<>&|'.indexOf(ch1) >= 0)) || ch2 === '=>') {
- index += 2;
- return {
- type: Token.Punctuator,
- value: ch2,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- // 1-character punctuators: < > = ! + - * % & | ^ /
- if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
- ++index;
- return {
- type: Token.Punctuator,
- value: ch1,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- // 7.8.3 Numeric Literals
-
- function scanHexLiteral(start) {
- var number = '';
-
- while (index < length) {
- if (!isHexDigit(source[index])) {
- break;
- }
- number += source[index++];
- }
-
- if (number.length === 0) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- if (isIdentifierStart(source.charCodeAt(index))) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- return {
- type: Token.NumericLiteral,
- value: parseInt('0x' + number, 16),
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- function scanOctalLiteral(start) {
- var number = '0' + source[index++];
- while (index < length) {
- if (!isOctalDigit(source[index])) {
- break;
- }
- number += source[index++];
- }
-
- if (isIdentifierStart(source.charCodeAt(index)) ||
isDecimalDigit(source.charCodeAt(index))) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- return {
- type: Token.NumericLiteral,
- value: parseInt(number, 8),
- octal: true,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- function scanNumericLiteral() {
- var number, start, ch;
-
- ch = source[index];
- assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
- 'Numeric literal must start with a decimal digit or a decimal
point');
-
- start = index;
- number = '';
- if (ch !== '.') {
- number = source[index++];
- ch = source[index];
-
- // Hex number starts with '0x'.
- // Octal number starts with '0'.
- if (number === '0') {
- if (ch === 'x' || ch === 'X') {
- ++index;
- return scanHexLiteral(start);
- }
- if (isOctalDigit(ch)) {
- return scanOctalLiteral(start);
- }
-
- // decimal number starts with '0' such as '09' is illegal.
- if (ch && isDecimalDigit(ch.charCodeAt(0))) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- }
-
- while (isDecimalDigit(source.charCodeAt(index))) {
- number += source[index++];
- }
- ch = source[index];
- }
-
- if (ch === '.') {
- number += source[index++];
- while (isDecimalDigit(source.charCodeAt(index))) {
- number += source[index++];
- }
- ch = source[index];
- }
-
- if (ch === 'e' || ch === 'E') {
- number += source[index++];
-
- ch = source[index];
- if (ch === '+' || ch === '-') {
- number += source[index++];
- }
- if (isDecimalDigit(source.charCodeAt(index))) {
- while (isDecimalDigit(source.charCodeAt(index))) {
- number += source[index++];
- }
- } else {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- }
-
- if (isIdentifierStart(source.charCodeAt(index))) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- return {
- type: Token.NumericLiteral,
- value: parseFloat(number),
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- // 7.8.4 String Literals
-
- function scanStringLiteral() {
- var str = '', quote, start, ch, code, unescaped, restore, octal =
false, startLineNumber, startLineStart;
- startLineNumber = lineNumber;
- startLineStart = lineStart;
-
- quote = source[index];
- assert((quote === '\'' || quote === '"'),
- 'String literal must starts with a quote');
-
- start = index;
- ++index;
-
- while (index < length) {
- ch = source[index++];
-
- if (ch === quote) {
- quote = '';
- break;
- } else if (ch === '\\') {
- ch = source[index++];
- if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
- switch (ch) {
- case 'u':
- case 'x':
- restore = index;
- unescaped = scanHexEscape(ch);
- if (unescaped) {
- str += unescaped;
- } else {
- index = restore;
- str += ch;
- }
- break;
- case 'n':
- str += '\n';
- break;
- case 'r':
- str += '\r';
- break;
- case 't':
- str += '\t';
- break;
- case 'b':
- str += '\b';
- break;
- case 'f':
- str += '\f';
- break;
- case 'v':
- str += '\x0B';
- break;
-
- default:
- if (isOctalDigit(ch)) {
- code = '01234567'.indexOf(ch);
-
- // \0 is not octal escape sequence
- if (code !== 0) {
- octal = true;
- }
-
- if (index < length && isOctalDigit(source[index]))
{
- octal = true;
- code = code * 8 +
'01234567'.indexOf(source[index++]);
-
- // 3 digits are only allowed when string starts
- // with 0, 1, 2, 3
- if ('0123'.indexOf(ch) >= 0 &&
- index < length &&
- isOctalDigit(source[index])) {
- code = code * 8 +
'01234567'.indexOf(source[index++]);
- }
- }
- str += String.fromCharCode(code);
- } else {
- str += ch;
- }
- break;
- }
- } else {
- ++lineNumber;
- if (ch === '\r' && source[index] === '\n') {
- ++index;
- }
- lineStart = index;
- }
- } else if (isLineTerminator(ch.charCodeAt(0))) {
- break;
- } else {
- str += ch;
- }
- }
-
- if (quote !== '') {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- return {
- type: Token.StringLiteral,
- value: str,
- octal: octal,
- startLineNumber: startLineNumber,
- startLineStart: startLineStart,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- function testRegExp(pattern, flags) {
- var value;
- try {
- value = new RegExp(pattern, flags);
- } catch (e) {
- throwError({}, Messages.InvalidRegExp);
- }
- return value;
- }
-
- function scanRegExpBody() {
- var ch, str, classMarker, terminated, body;
-
- ch = source[index];
- assert(ch === '/', 'Regular expression literal must start with a
slash');
- str = source[index++];
-
- classMarker = false;
- terminated = false;
- while (index < length) {
- ch = source[index++];
- str += ch;
- if (ch === '\\') {
- ch = source[index++];
- // ECMA-262 7.8.5
- if (isLineTerminator(ch.charCodeAt(0))) {
- throwError({}, Messages.UnterminatedRegExp);
- }
- str += ch;
- } else if (isLineTerminator(ch.charCodeAt(0))) {
- throwError({}, Messages.UnterminatedRegExp);
- } else if (classMarker) {
- if (ch === ']') {
- classMarker = false;
- }
- } else {
- if (ch === '/') {
- terminated = true;
- break;
- } else if (ch === '[') {
- classMarker = true;
- }
- }
- }
-
- if (!terminated) {
- throwError({}, Messages.UnterminatedRegExp);
- }
-
- // Exclude leading and trailing slash.
- body = str.substr(1, str.length - 2);
- return {
- value: body,
- literal: str
- };
- }
-
- function scanRegExpFlags() {
- var ch, str, flags, restore;
-
- str = '';
- flags = '';
- while (index < length) {
- ch = source[index];
- if (!isIdentifierPart(ch.charCodeAt(0))) {
- break;
- }
-
- ++index;
- if (ch === '\\' && index < length) {
- ch = source[index];
- if (ch === 'u') {
- ++index;
- restore = index;
- ch = scanHexEscape('u');
- if (ch) {
- flags += ch;
- for (str += '\\u'; restore < index; ++restore) {
- str += source[restore];
- }
- } else {
- index = restore;
- flags += 'u';
- str += '\\u';
- }
- throwErrorTolerant({}, Messages.UnexpectedToken,
'ILLEGAL');
- } else {
- str += '\\';
- throwErrorTolerant({}, Messages.UnexpectedToken,
'ILLEGAL');
- }
- } else {
- flags += ch;
- str += ch;
- }
- }
-
- return {
- value: flags,
- literal: str
- };
- }
-
- function scanRegExp() {
- var start, body, flags, pattern, value;
-
- lookahead = null;
- skipComment();
- start = index;
-
- body = scanRegExpBody();
- flags = scanRegExpFlags();
- value = testRegExp(body.value, flags.value);
-
- if (extra.tokenize) {
- return {
- type: Token.RegularExpression,
- value: value,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: start,
- end: index
- };
- }
-
- return {
- literal: body.literal + flags.literal,
- value: value,
- start: start,
- end: index
- };
- }
-
- function collectRegex() {
- var pos, loc, regex, token;
-
- skipComment();
-
- pos = index;
- loc = {
- start: {
- line: lineNumber,
- column: index - lineStart
- }
- };
-
- regex = scanRegExp();
- loc.end = {
- line: lineNumber,
- column: index - lineStart
- };
-
- /* istanbul ignore next */
- if (!extra.tokenize) {
- // Pop the previous token, which is likely '/' or '/='
- if (extra.tokens.length > 0) {
- token = extra.tokens[extra.tokens.length - 1];
- if (token.range[0] === pos && token.type === 'Punctuator') {
- if (token.value === '/' || token.value === '/=') {
- extra.tokens.pop();
- }
- }
- }
-
- extra.tokens.push({
- type: 'RegularExpression',
- value: regex.literal,
- range: [pos, index],
- loc: loc
- });
- }
-
- return regex;
- }
-
- function isIdentifierName(token) {
- return token.type === Token.Identifier ||
- token.type === Token.Keyword ||
- token.type === Token.BooleanLiteral ||
- token.type === Token.NullLiteral;
- }
-
- function advanceSlash() {
- var prevToken,
- checkToken;
- // Using the following algorithm:
- // https://github.com/mozilla/sweet.js/wiki/design
- prevToken = extra.tokens[extra.tokens.length - 1];
- if (!prevToken) {
- // Nothing before that: it cannot be a division.
- return collectRegex();
- }
- if (prevToken.type === 'Punctuator') {
- if (prevToken.value === ']') {
- return scanPunctuator();
- }
- if (prevToken.value === ')') {
- checkToken = extra.tokens[extra.openParenToken - 1];
- if (checkToken &&
- checkToken.type === 'Keyword' &&
- (checkToken.value === 'if' ||
- checkToken.value === 'while' ||
- checkToken.value === 'for' ||
- checkToken.value === 'with')) {
- return collectRegex();
- }
- return scanPunctuator();
- }
- if (prevToken.value === '}') {
- // Dividing a function by anything makes little sense,
- // but we have to check for that.
- if (extra.tokens[extra.openCurlyToken - 3] &&
- extra.tokens[extra.openCurlyToken - 3].type ===
'Keyword') {
- // Anonymous function.
- checkToken = extra.tokens[extra.openCurlyToken - 4];
- if (!checkToken) {
- return scanPunctuator();
- }
- } else if (extra.tokens[extra.openCurlyToken - 4] &&
- extra.tokens[extra.openCurlyToken - 4].type ===
'Keyword') {
- // Named function.
- checkToken = extra.tokens[extra.openCurlyToken - 5];
- if (!checkToken) {
- return collectRegex();
- }
- } else {
- return scanPunctuator();
- }
- // checkToken determines whether the function is
- // a declaration or an expression.
- if (FnExprTokens.indexOf(checkToken.value) >= 0) {
- // It is an expression.
- return scanPunctuator();
- }
- // It is a declaration.
- return collectRegex();
- }
- return collectRegex();
- }
- if (prevToken.type === 'Keyword') {
- return collectRegex();
- }
- return scanPunctuator();
- }
-
- function advance() {
- var ch;
-
- skipComment();
-
- if (index >= length) {
- return {
- type: Token.EOF,
- lineNumber: lineNumber,
- lineStart: lineStart,
- start: index,
- end: index
- };
- }
-
- ch = source.charCodeAt(index);
-
- if (isIdentifierStart(ch)) {
- return scanIdentifier();
- }
-
- // Very common: ( and ) and ;
- if (ch === 0x28 || ch === 0x29 || ch === 0x3B) {
- return scanPunctuator();
- }
-
- // String literal starts with single quote (U+0027) or double quote
(U+0022).
- if (ch === 0x27 || ch === 0x22) {
- return scanStringLiteral();
- }
-
-
- // Dot (.) U+002E can also start a floating-point number, hence the
need
- // to check the next character.
- if (ch === 0x2E) {
- if (isDecimalDigit(source.charCodeAt(index + 1))) {
- return scanNumericLiteral();
- }
- return scanPunctuator();
- }
-
- if (isDecimalDigit(ch)) {
- return scanNumericLiteral();
- }
-
- // Slash (/) U+002F can also start a regex.
- if (extra.tokenize && ch === 0x2F) {
- return advanceSlash();
- }
-
- return scanPunctuator();
- }
-
- function collectToken() {
- var loc, token, range, value;
-
- skipComment();
- loc = {
- start: {
- line: lineNumber,
- column: index - lineStart
- }
- };
-
- token = advance();
- loc.end = {
- line: lineNumber,
- column: index - lineStart
- };
-
- if (token.type !== Token.EOF) {
- value = source.slice(token.start, token.end);
- extra.tokens.push({
- type: TokenName[token.type],
- value: value,
- range: [token.start, token.end],
- loc: loc
- });
- }
-
- return token;
- }
-
- function lex() {
- var token;
-
- token = lookahead;
- index = token.end;
- lineNumber = token.lineNumber;
- lineStart = token.lineStart;
-
- lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() :
advance();
-
- index = token.end;
- lineNumber = token.lineNumber;
- lineStart = token.lineStart;
-
- return token;
- }
-
- function peek() {
- var pos, line, start;
-
- pos = index;
- line = lineNumber;
- start = lineStart;
- lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() :
advance();
- index = pos;
- lineNumber = line;
- lineStart = start;
- }
-
- function Position(line, column) {
- this.line = line;
- this.column = column;
- }
-
- function SourceLocation(startLine, startColumn, line, column) {
- this.start = new Position(startLine, startColumn);
- this.end = new Position(line, column);
- }
-
- SyntaxTreeDelegate = {
-
- name: 'SyntaxTree',
-
- processComment: function (node) {
- var lastChild, trailingComments;
-
- if (node.type === Syntax.Program) {
- if (node.body.length > 0) {
- return;
- }
- }
-
- if (extra.trailingComments.length > 0) {
- if (extra.trailingComments[0].range[0] >= node.range[1]) {
- trailingComments = extra.trailingComments;
- extra.trailingComments = [];
- } else {
- extra.trailingComments.length = 0;
- }
- } else {
- if (extra.bottomRightStack.length > 0 &&
- extra.bottomRightStack[extra.bottomRightStack.length -
1].trailingComments &&
- extra.bottomRightStack[extra.bottomRightStack.length -
1].trailingComments[0].range[0] >= node.range[1]) {
- trailingComments =
extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
- delete
extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
- }
- }
-
- // Eating the stack.
- while (extra.bottomRightStack.length > 0 &&
extra.bottomRightStack[extra.bottomRightStack.length - 1].range[0] >=
node.range[0]) {
- lastChild = extra.bottomRightStack.pop();
- }
-
- if (lastChild) {
- if (lastChild.leadingComments &&
lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <=
node.range[0]) {
- node.leadingComments = lastChild.leadingComments;
- delete lastChild.leadingComments;
- }
- } else if (extra.leadingComments.length > 0 &&
extra.leadingComments[extra.leadingComments.length - 1].range[1] <=
node.range[0]) {
- node.leadingComments = extra.leadingComments;
- extra.leadingComments = [];
- }
-
-
- if (trailingComments) {
- node.trailingComments = trailingComments;
- }
-
- extra.bottomRightStack.push(node);
- },
-
- markEnd: function (node, startToken) {
- if (extra.range) {
- node.range = [startToken.start, index];
- }
- if (extra.loc) {
- node.loc = new SourceLocation(
- startToken.startLineNumber === undefined ?
startToken.lineNumber : startToken.startLineNumber,
- startToken.start - (startToken.startLineStart ===
undefined ? startToken.lineStart : startToken.startLineStart),
- lineNumber,
- index - lineStart
- );
- this.postProcess(node);
- }
-
- if (extra.attachComment) {
- this.processComment(node);
- }
- return node;
- },
-
- postProcess: function (node) {
- if (extra.source) {
- node.loc.source = extra.source;
- }
- return node;
- },
-
- createArrayExpression: function (elements) {
- return {
- type: Syntax.ArrayExpression,
- elements: elements
- };
- },
-
- createAssignmentExpression: function (operator, left, right) {
- return {
- type: Syntax.AssignmentExpression,
- operator: operator,
- left: left,
- right: right
- };
- },
-
- createBinaryExpression: function (operator, left, right) {
- var type = (operator === '||' || operator === '&&') ?
Syntax.LogicalExpression :
- Syntax.BinaryExpression;
- return {
- type: type,
- operator: operator,
- left: left,
- right: right
- };
- },
-
- createBlockStatement: function (body) {
- return {
- type: Syntax.BlockStatement,
- body: body
- };
- },
-
- createBreakStatement: function (label) {
- return {
- type: Syntax.BreakStatement,
- label: label
- };
- },
-
- createCallExpression: function (callee, args) {
- return {
- type: Syntax.CallExpression,
- callee: callee,
- 'arguments': args
- };
- },
-
- createCatchClause: function (param, body) {
- return {
- type: Syntax.CatchClause,
- param: param,
- body: body
- };
- },
-
- createConditionalExpression: function (test, consequent, alternate) {
- return {
- type: Syntax.ConditionalExpression,
- test: test,
- consequent: consequent,
- alternate: alternate
- };
- },
-
- createContinueStatement: function (label) {
- return {
- type: Syntax.ContinueStatement,
- label: label
- };
- },
-
- createDebuggerStatement: function () {
- return {
- type: Syntax.DebuggerStatement
- };
- },
-
- createDoWhileStatement: function (body, test) {
- return {
- type: Syntax.DoWhileStatement,
- body: body,
- test: test
- };
- },
-
- createEmptyStatement: function () {
- return {
- type: Syntax.EmptyStatement
- };
- },
-
- createExpressionStatement: function (expression) {
- return {
- type: Syntax.ExpressionStatement,
- expression: expression
- };
- },
-
- createForStatement: function (init, test, update, body) {
- return {
- type: Syntax.ForStatement,
- init: init,
- test: test,
- update: update,
- body: body
- };
- },
-
- createForInStatement: function (left, right, body) {
- return {
- type: Syntax.ForInStatement,
- left: left,
- right: right,
- body: body,
- each: false
- };
- },
-
- createFunctionDeclaration: function (id, params, defaults, body) {
- return {
- type: Syntax.FunctionDeclaration,
- id: id,
- params: params,
- defaults: defaults,
- body: body,
- rest: null,
- generator: false,
- expression: false
- };
- },
-
- createFunctionExpression: function (id, params, defaults, body) {
- return {
- type: Syntax.FunctionExpression,
- id: id,
- params: params,
- defaults: defaults,
- body: body,
- rest: null,
- generator: false,
- expression: false
- };
- },
-
- createIdentifier: function (name) {
- return {
- type: Syntax.Identifier,
- name: name
- };
- },
-
- createIfStatement: function (test, consequent, alternate) {
- return {
- type: Syntax.IfStatement,
- test: test,
- consequent: consequent,
- alternate: alternate
- };
- },
-
- createLabeledStatement: function (label, body) {
- return {
- type: Syntax.LabeledStatement,
- label: label,
- body: body
- };
- },
-
- createLiteral: function (token) {
- return {
- type: Syntax.Literal,
- value: token.value,
- raw: source.slice(token.start, token.end)
- };
- },
-
- createMemberExpression: function (accessor, object, property) {
- return {
- type: Syntax.MemberExpression,
- computed: accessor === '[',
- object: object,
- property: property
- };
- },
-
- createNewExpression: function (callee, args) {
- return {
- type: Syntax.NewExpression,
- callee: callee,
- 'arguments': args
- };
- },
-
- createObjectExpression: function (properties) {
- return {
- type: Syntax.ObjectExpression,
- properties: properties
- };
- },
-
- createPostfixExpression: function (operator, argument) {
- return {
- type: Syntax.UpdateExpression,
- operator: operator,
- argument: argument,
- prefix: false
- };
- },
-
- createProgram: function (body) {
- return {
- type: Syntax.Program,
- body: body
- };
- },
-
- createProperty: function (kind, key, value) {
- return {
- type: Syntax.Property,
- key: key,
- value: value,
- kind: kind
- };
- },
-
- createReturnStatement: function (argument) {
- return {
- type: Syntax.ReturnStatement,
- argument: argument
- };
- },
-
- createSequenceExpression: function (expressions) {
- return {
- type: Syntax.SequenceExpression,
- expressions: expressions
- };
- },
-
- createSwitchCase: function (test, consequent) {
- return {
- type: Syntax.SwitchCase,
- test: test,
- consequent: consequent
- };
- },
-
- createSwitchStatement: function (discriminant, cases) {
- return {
- type: Syntax.SwitchStatement,
- discriminant: discriminant,
- cases: cases
- };
- },
-
- createThisExpression: function () {
- return {
- type: Syntax.ThisExpression
- };
- },
-
- createThrowStatement: function (argument) {
- return {
- type: Syntax.ThrowStatement,
- argument: argument
- };
- },
-
- createTryStatement: function (block, guardedHandlers, handlers,
finalizer) {
- return {
- type: Syntax.TryStatement,
- block: block,
- guardedHandlers: guardedHandlers,
- handlers: handlers,
- finalizer: finalizer
- };
- },
-
- createUnaryExpression: function (operator, argument) {
- if (operator === '++' || operator === '--') {
- return {
- type: Syntax.UpdateExpression,
- operator: operator,
- argument: argument,
- prefix: true
- };
- }
- return {
- type: Syntax.UnaryExpression,
- operator: operator,
- argument: argument,
- prefix: true
- };
- },
-
- createVariableDeclaration: function (declarations, kind) {
- return {
- type: Syntax.VariableDeclaration,
- declarations: declarations,
- kind: kind
- };
- },
-
- createVariableDeclarator: function (id, init) {
- return {
- type: Syntax.VariableDeclarator,
- id: id,
- init: init
- };
- },
-
- createWhileStatement: function (test, body) {
- return {
- type: Syntax.WhileStatement,
- test: test,
- body: body
- };
- },
-
- createWithStatement: function (object, body) {
- return {
- type: Syntax.WithStatement,
- object: object,
- body: body
- };
- }
- };
-
- // Return true if there is a line terminator before the next token.
-
- function peekLineTerminator() {
- var pos, line, start, found;
-
- pos = index;
- line = lineNumber;
- start = lineStart;
- skipComment();
- found = lineNumber !== line;
- index = pos;
- lineNumber = line;
- lineStart = start;
-
- return found;
- }
-
- // Throw an exception
-
- function throwError(token, messageFormat) {
- var error,
- args = Array.prototype.slice.call(arguments, 2),
- msg = messageFormat.replace(
- /%(\d)/g,
- function (whole, index) {
- assert(index < args.length, 'Message reference must be in
range');
- return args[index];
- }
- );
-
- if (typeof token.lineNumber === 'number') {
- error = new Error('Line ' + token.lineNumber + ': ' + msg);
- error.index = token.start;
- error.lineNumber = token.lineNumber;
- error.column = token.start - lineStart + 1;
- } else {
- error = new Error('Line ' + lineNumber + ': ' + msg);
- error.index = index;
- error.lineNumber = lineNumber;
- error.column = index - lineStart + 1;
- }
-
- error.description = msg;
- throw error;
- }
-
- function throwErrorTolerant() {
- try {
- throwError.apply(null, arguments);
- } catch (e) {
- if (extra.errors) {
- extra.errors.push(e);
- } else {
- throw e;
- }
- }
- }
-
-
- // Throw an exception because of the token.
-
- function throwUnexpected(token) {
- if (token.type === Token.EOF) {
- throwError(token, Messages.UnexpectedEOS);
- }
-
- if (token.type === Token.NumericLiteral) {
- throwError(token, Messages.UnexpectedNumber);
- }
-
- if (token.type === Token.StringLiteral) {
- throwError(token, Messages.UnexpectedString);
- }
-
- if (token.type === Token.Identifier) {
- throwError(token, Messages.UnexpectedIdentifier);
- }
-
- if (token.type === Token.Keyword) {
- if (isFutureReservedWord(token.value)) {
- throwError(token, Messages.UnexpectedReserved);
- } else if (strict && isStrictModeReservedWord(token.value)) {
- throwErrorTolerant(token, Messages.StrictReservedWord);
- return;
- }
- throwError(token, Messages.UnexpectedToken, token.value);
- }
-
- // BooleanLiteral, NullLiteral, or Punctuator.
- throwError(token, Messages.UnexpectedToken, token.value);
- }
-
- // Expect the next token to match the specified punctuator.
- // If not, an exception will be thrown.
-
- function expect(value) {
- var token = lex();
- if (token.type !== Token.Punctuator || token.value !== value) {
- throwUnexpected(token);
- }
- }
-
- // Expect the next token to match the specified keyword.
- // If not, an exception will be thrown.
-
- function expectKeyword(keyword) {
- var token = lex();
- if (token.type !== Token.Keyword || token.value !== keyword) {
- throwUnexpected(token);
- }
- }
-
- // Return true if the next token matches the specified punctuator.
-
- function match(value) {
- return lookahead.type === Token.Punctuator && lookahead.value ===
value;
- }
-
- // Return true if the next token matches the specified keyword
-
- function matchKeyword(keyword) {
- return lookahead.type === Token.Keyword && lookahead.value === keyword;
- }
-
- // Return true if the next token is an assignment operator
-
- function matchAssign() {
- var op;
-
- if (lookahead.type !== Token.Punctuator) {
- return false;
- }
- op = lookahead.value;
- return op === '=' ||
- op === '*=' ||
- op === '/=' ||
- op === '%=' ||
- op === '+=' ||
- op === '-=' ||
- op === '<<=' ||
- op === '>>=' ||
- op === '>>>=' ||
- op === '&=' ||
- op === '^=' ||
- op === '|=';
- }
-
- function consumeSemicolon() {
- var line;
-
- // Catch the very common case first: immediately a semicolon (U+003B).
- if (source.charCodeAt(index) === 0x3B || match(';')) {
- lex();
- return;
- }
-
- line = lineNumber;
- skipComment();
- if (lineNumber !== line) {
- return;
- }
-
- if (lookahead.type !== Token.EOF && !match('}')) {
- throwUnexpected(lookahead);
- }
- }
-
- // Return true if provided expression is LeftHandSideExpression
-
- function isLeftHandSide(expr) {
- return expr.type === Syntax.Identifier || expr.type ===
Syntax.MemberExpression;
- }
-
- // 11.1.4 Array Initialiser
-
- function parseArrayInitialiser() {
- var elements = [], startToken;
-
- startToken = lookahead;
- expect('[');
-
- while (!match(']')) {
- if (match(',')) {
- lex();
- elements.push(null);
- } else {
- elements.push(parseAssignmentExpression());
-
- if (!match(']')) {
- expect(',');
- }
- }
- }
-
- lex();
-
- return delegate.markEnd(delegate.createArrayExpression(elements),
startToken);
- }
-
- // 11.1.5 Object Initialiser
-
- function parsePropertyFunction(param, first) {
- var previousStrict, body, startToken;
-
- previousStrict = strict;
- startToken = lookahead;
- body = parseFunctionSourceElements();
- if (first && strict && isRestrictedWord(param[0].name)) {
- throwErrorTolerant(first, Messages.StrictParamName);
- }
- strict = previousStrict;
- return delegate.markEnd(delegate.createFunctionExpression(null, param,
[], body), startToken);
- }
-
- function parseObjectPropertyKey() {
- var token, startToken;
-
- startToken = lookahead;
- token = lex();
-
- // Note: This function is called only from parseObjectProperty(), where
- // EOF and Punctuator tokens are already filtered out.
-
- if (token.type === Token.StringLiteral || token.type ===
Token.NumericLiteral) {
- if (strict && token.octal) {
- throwErrorTolerant(token, Messages.StrictOctalLiteral);
- }
- return delegate.markEnd(delegate.createLiteral(token), startToken);
- }
-
- return delegate.markEnd(delegate.createIdentifier(token.value),
startToken);
- }
-
- function parseObjectProperty() {
- var token, key, id, value, param, startToken;
-
- token = lookahead;
- startToken = lookahead;
-
- if (token.type === Token.Identifier) {
-
- id = parseObjectPropertyKey();
-
- // Property Assignment: Getter and Setter.
-
- if (token.value === 'get' && !match(':')) {
- key = parseObjectPropertyKey();
- expect('(');
- expect(')');
- value = parsePropertyFunction([]);
- return delegate.markEnd(delegate.createProperty('get', key,
value), startToken);
- }
- if (token.value === 'set' && !match(':')) {
- key = parseObjectPropertyKey();
- expect('(');
- token = lookahead;
- if (token.type !== Token.Identifier) {
- expect(')');
- throwErrorTolerant(token, Messages.UnexpectedToken,
token.value);
- value = parsePropertyFunction([]);
- } else {
- param = [ parseVariableIdentifier() ];
- expect(')');
- value = parsePropertyFunction(param, token);
- }
- return delegate.markEnd(delegate.createProperty('set', key,
value), startToken);
- }
- expect(':');
- value = parseAssignmentExpression();
- return delegate.markEnd(delegate.createProperty('init', id,
value), startToken);
- }
- if (token.type === Token.EOF || token.type === Token.Punctuator) {
- throwUnexpected(token);
- } else {
- key = parseObjectPropertyKey();
- expect(':');
- value = parseAssignmentExpression();
- return delegate.markEnd(delegate.createProperty('init', key,
value), startToken);
- }
- }
-
- function parseObjectInitialiser() {
- var properties = [], property, name, key, kind, map = {}, toString =
String, startToken;
-
- startToken = lookahead;
-
- expect('{');
-
- while (!match('}')) {
- property = parseObjectProperty();
-
- if (property.key.type === Syntax.Identifier) {
- name = property.key.name;
- } else {
- name = toString(property.key.value);
- }
- kind = (property.kind === 'init') ? PropertyKind.Data :
(property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;
-
- key = '$' + name;
- if (Object.prototype.hasOwnProperty.call(map, key)) {
- if (map[key] === PropertyKind.Data) {
- if (strict && kind === PropertyKind.Data) {
- throwErrorTolerant({},
Messages.StrictDuplicateProperty);
- } else if (kind !== PropertyKind.Data) {
- throwErrorTolerant({}, Messages.AccessorDataProperty);
- }
- } else {
- if (kind === PropertyKind.Data) {
- throwErrorTolerant({}, Messages.AccessorDataProperty);
- } else if (map[key] & kind) {
- throwErrorTolerant({}, Messages.AccessorGetSet);
- }
- }
- map[key] |= kind;
- } else {
- map[key] = kind;
- }
-
- properties.push(property);
-
- if (!match('}')) {
- expect(',');
- }
- }
-
- expect('}');
-
- return delegate.markEnd(delegate.createObjectExpression(properties),
startToken);
- }
-
- // 11.1.6 The Grouping Operator
-
- function parseGroupExpression() {
- var expr;
-
- expect('(');
-
- expr = parseExpression();
-
- expect(')');
-
- return expr;
- }
-
-
- // 11.1 Primary Expressions
-
- function parsePrimaryExpression() {
- var type, token, expr, startToken;
-
- if (match('(')) {
- return parseGroupExpression();
- }
-
- if (match('[')) {
- return parseArrayInitialiser();
- }
-
- if (match('{')) {
- return parseObjectInitialiser();
- }
-
- type = lookahead.type;
- startToken = lookahead;
-
- if (type === Token.Identifier) {
- expr = delegate.createIdentifier(lex().value);
- } else if (type === Token.StringLiteral || type ===
Token.NumericLiteral) {
- if (strict && lookahead.octal) {
- throwErrorTolerant(lookahead, Messages.StrictOctalLiteral);
- }
- expr = delegate.createLiteral(lex());
- } else if (type === Token.Keyword) {
- if (matchKeyword('function')) {
- return parseFunctionExpression();
- }
- if (matchKeyword('this')) {
- lex();
- expr = delegate.createThisExpression();
- } else {
- throwUnexpected(lex());
- }
- } else if (type === Token.BooleanLiteral) {
- token = lex();
- token.value = (token.value === 'true');
- expr = delegate.createLiteral(token);
- } else if (type === Token.NullLiteral) {
- token = lex();
- token.value = null;
- expr = delegate.createLiteral(token);
- } else if (match('/') || match('/=')) {
- if (typeof extra.tokens !== 'undefined') {
- expr = delegate.createLiteral(collectRegex());
- } else {
- expr = delegate.createLiteral(scanRegExp());
- }
- peek();
- } else {
- throwUnexpected(lex());
- }
-
- return delegate.markEnd(expr, startToken);
- }
-
- // 11.2 Left-Hand-Side Expressions
-
- function parseArguments() {
- var args = [];
-
- expect('(');
-
- if (!match(')')) {
- while (index < length) {
- args.push(parseAssignmentExpression());
- if (match(')')) {
- break;
- }
- expect(',');
- }
- }
-
- expect(')');
-
- return args;
- }
-
- function parseNonComputedProperty() {
- var token, startToken;
-
- startToken = lookahead;
- token = lex();
-
- if (!isIdentifierName(token)) {
- throwUnexpected(token);
- }
-
- return delegate.markEnd(delegate.createIdentifier(token.value),
startToken);
- }
-
- function parseNonComputedMember() {
- expect('.');
-
- return parseNonComputedProperty();
- }
-
- function parseComputedMember() {
- var expr;
-
- expect('[');
-
- expr = parseExpression();
-
- expect(']');
-
- return expr;
- }
-
- function parseNewExpression() {
- var callee, args, startToken;
-
- startToken = lookahead;
- expectKeyword('new');
- callee = parseLeftHandSideExpression();
- args = match('(') ? parseArguments() : [];
-
- return delegate.markEnd(delegate.createNewExpression(callee, args),
startToken);
- }
-
- function parseLeftHandSideExpressionAllowCall() {
- var previousAllowIn, expr, args, property, startToken;
-
- startToken = lookahead;
-
- previousAllowIn = state.allowIn;
- state.allowIn = true;
- expr = matchKeyword('new') ? parseNewExpression() :
parsePrimaryExpression();
- state.allowIn = previousAllowIn;
-
- for (;;) {
- if (match('.')) {
- property = parseNonComputedMember();
- expr = delegate.createMemberExpression('.', expr, property);
- } else if (match('(')) {
- args = parseArguments();
- expr = delegate.createCallExpression(expr, args);
- } else if (match('[')) {
- property = parseComputedMember();
- expr = delegate.createMemberExpression('[', expr, property);
- } else {
- break;
- }
- delegate.markEnd(expr, startToken);
- }
-
- return expr;
- }
-
- function parseLeftHandSideExpression() {
- var previousAllowIn, expr, property, startToken;
-
- startToken = lookahead;
-
- previousAllowIn = state.allowIn;
- expr = matchKeyword('new') ? parseNewExpression() :
parsePrimaryExpression();
- state.allowIn = previousAllowIn;
-
- while (match('.') || match('[')) {
- if (match('[')) {
- property = parseComputedMember();
- expr = delegate.createMemberExpression('[', expr, property);
- } else {
- property = parseNonComputedMember();
- expr = delegate.createMemberExpression('.', expr, property);
- }
- delegate.markEnd(expr, startToken);
- }
-
- return expr;
- }
-
- // 11.3 Postfix Expressions
-
- function parsePostfixExpression() {
- var expr, token, startToken = lookahead;
-
- expr = parseLeftHandSideExpressionAllowCall();
-
- if (lookahead.type === Token.Punctuator) {
- if ((match('++') || match('--')) && !peekLineTerminator()) {
- // 11.3.1, 11.3.2
- if (strict && expr.type === Syntax.Identifier &&
isRestrictedWord(expr.name)) {
- throwErrorTolerant({}, Messages.StrictLHSPostfix);
- }
-
- if (!isLeftHandSide(expr)) {
- throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
- }
-
- token = lex();
- expr =
delegate.markEnd(delegate.createPostfixExpression(token.value, expr),
startToken);
- }
- }
-
- return expr;
- }
-
- // 11.4 Unary Operators
-
- function parseUnaryExpression() {
- var token, expr, startToken;
-
- if (lookahead.type !== Token.Punctuator && lookahead.type !==
Token.Keyword) {
- expr = parsePostfixExpression();
- } else if (match('++') || match('--')) {
- startToken = lookahead;
- token = lex();
- expr = parseUnaryExpression();
- // 11.4.4, 11.4.5
- if (strict && expr.type === Syntax.Identifier &&
isRestrictedWord(expr.name)) {
- throwErrorTolerant({}, Messages.StrictLHSPrefix);
- }
-
- if (!isLeftHandSide(expr)) {
- throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
- }
-
- expr = delegate.createUnaryExpression(token.value, expr);
- expr = delegate.markEnd(expr, startToken);
- } else if (match('+') || match('-') || match('~') || match('!')) {
- startToken = lookahead;
- token = lex();
- expr = parseUnaryExpression();
- expr = delegate.createUnaryExpression(token.value, expr);
- expr = delegate.markEnd(expr, startToken);
- } else if (matchKeyword('delete') || matchKeyword('void') ||
matchKeyword('typeof')) {
- startToken = lookahead;
- token = lex();
- expr = parseUnaryExpression();
- expr = delegate.createUnaryExpression(token.value, expr);
- expr = delegate.markEnd(expr, startToken);
- if (strict && expr.operator === 'delete' && expr.argument.type ===
Syntax.Identifier) {
- throwErrorTolerant({}, Messages.StrictDelete);
- }
- } else {
- expr = parsePostfixExpression();
- }
-
- return expr;
- }
-
- function binaryPrecedence(token, allowIn) {
- var prec = 0;
-
- if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
- return 0;
- }
-
- switch (token.value) {
- case '||':
- prec = 1;
- break;
-
- case '&&':
- prec = 2;
- break;
-
- case '|':
- prec = 3;
- break;
-
- case '^':
- prec = 4;
- break;
-
- case '&':
- prec = 5;
- break;
-
- case '==':
- case '!=':
- case '===':
- case '!==':
- prec = 6;
- break;
-
- case '<':
- case '>':
- case '<=':
- case '>=':
- case 'instanceof':
- prec = 7;
- break;
-
- case 'in':
- prec = allowIn ? 7 : 0;
- break;
-
- case '<<':
- case '>>':
- case '>>>':
- prec = 8;
- break;
-
- case '+':
- case '-':
- prec = 9;
- break;
-
- case '*':
- case '/':
- case '%':
- prec = 11;
- break;
-
- default:
- break;
- }
-
- return prec;
- }
-
- // 11.5 Multiplicative Operators
- // 11.6 Additive Operators
- // 11.7 Bitwise Shift Operators
- // 11.8 Relational Operators
- // 11.9 Equality Operators
- // 11.10 Binary Bitwise Operators
- // 11.11 Binary Logical Operators
-
- function parseBinaryExpression() {
- var marker, markers, expr, token, prec, stack, right, operator, left,
i;
-
- marker = lookahead;
- left = parseUnaryExpression();
-
- token = lookahead;
- prec = binaryPrecedence(token, state.allowIn);
- if (prec === 0) {
- return left;
- }
- token.prec = prec;
- lex();
-
- markers = [marker, lookahead];
- right = parseUnaryExpression();
-
- stack = [left, token, right];
-
- while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) {
-
- // Reduce: make a binary expression from the three topmost entries.
- while ((stack.length > 2) && (prec <= stack[stack.length -
2].prec)) {
- right = stack.pop();
- operator = stack.pop().value;
- left = stack.pop();
- expr = delegate.createBinaryExpression(operator, left, right);
- markers.pop();
- marker = markers[markers.length - 1];
- delegate.markEnd(expr, marker);
- stack.push(expr);
- }
-
- // Shift.
- token = lex();
- token.prec = prec;
- stack.push(token);
- markers.push(lookahead);
- expr = parseUnaryExpression();
- stack.push(expr);
- }
-
- // Final reduce to clean-up the stack.
- i = stack.length - 1;
- expr = stack[i];
- markers.pop();
- while (i > 1) {
- expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i
- 2], expr);
- i -= 2;
- marker = markers.pop();
- delegate.markEnd(expr, marker);
- }
-
- return expr;
- }
-
-
- // 11.12 Conditional Operator
-
- function parseConditionalExpression() {
- var expr, previousAllowIn, consequent, alternate, startToken;
-
- startToken = lookahead;
-
- expr = parseBinaryExpression();
-
- if (match('?')) {
- lex();
- previousAllowIn = state.allowIn;
- state.allowIn = true;
- consequent = parseAssignmentExpression();
- state.allowIn = previousAllowIn;
- expect(':');
- alternate = parseAssignmentExpression();
-
- expr = delegate.createConditionalExpression(expr, consequent,
alternate);
- delegate.markEnd(expr, startToken);
- }
-
- return expr;
- }
-
- // 11.13 Assignment Operators
-
- function parseAssignmentExpression() {
- var token, left, right, node, startToken;
-
- token = lookahead;
- startToken = lookahead;
-
- node = left = parseConditionalExpression();
-
- if (matchAssign()) {
- // LeftHandSideExpression
- if (!isLeftHandSide(left)) {
- throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
- }
-
- // 11.13.1
- if (strict && left.type === Syntax.Identifier &&
isRestrictedWord(left.name)) {
- throwErrorTolerant(token, Messages.StrictLHSAssignment);
- }
-
- token = lex();
- right = parseAssignmentExpression();
- node =
delegate.markEnd(delegate.createAssignmentExpression(token.value, left, right),
startToken);
- }
-
- return node;
- }
-
- // 11.14 Comma Operator
-
- function parseExpression() {
- var expr, startToken = lookahead;
-
- expr = parseAssignmentExpression();
-
- if (match(',')) {
- expr = delegate.createSequenceExpression([ expr ]);
-
- while (index < length) {
- if (!match(',')) {
- break;
- }
- lex();
- expr.expressions.push(parseAssignmentExpression());
- }
-
- delegate.markEnd(expr, startToken);
- }
-
- return expr;
- }
-
- // 12.1 Block
-
- function parseStatementList() {
- var list = [],
- statement;
-
- while (index < length) {
- if (match('}')) {
- break;
- }
- statement = parseSourceElement();
- if (typeof statement === 'undefined') {
- break;
- }
- list.push(statement);
- }
-
- return list;
- }
-
- function parseBlock() {
- var block, startToken;
-
- startToken = lookahead;
- expect('{');
-
- block = parseStatementList();
-
- expect('}');
-
- return delegate.markEnd(delegate.createBlockStatement(block),
startToken);
- }
-
- // 12.2 Variable Statement
-
- function parseVariableIdentifier() {
- var token, startToken;
-
- startToken = lookahead;
- token = lex();
-
- if (token.type !== Token.Identifier) {
- throwUnexpected(token);
- }
-
- return delegate.markEnd(delegate.createIdentifier(token.value),
startToken);
- }
-
- function parseVariableDeclaration(kind) {
- var init = null, id, startToken;
-
- startToken = lookahead;
- id = parseVariableIdentifier();
-
- // 12.2.1
- if (strict && isRestrictedWord(id.name)) {
- throwErrorTolerant({}, Messages.StrictVarName);
- }
-
- if (kind === 'const') {
- expect('=');
- init = parseAssignmentExpression();
- } else if (match('=')) {
- lex();
- init = parseAssignmentExpression();
- }
-
- return delegate.markEnd(delegate.createVariableDeclarator(id, init),
startToken);
- }
-
- function parseVariableDeclarationList(kind) {
- var list = [];
-
- do {
- list.push(parseVariableDeclaration(kind));
- if (!match(',')) {
- break;
- }
- lex();
- } while (index < length);
-
- return list;
- }
-
- function parseVariableStatement() {
- var declarations;
-
- expectKeyword('var');
-
- declarations = parseVariableDeclarationList();
-
- consumeSemicolon();
-
- return delegate.createVariableDeclaration(declarations, 'var');
- }
-
- // kind may be `const` or `let`
- // Both are experimental and not in the specification yet.
- // see http://wiki.ecmascript.org/doku.php?id=harmony:const
- // and http://wiki.ecmascript.org/doku.php?id=harmony:let
- function parseConstLetDeclaration(kind) {
- var declarations, startToken;
-
- startToken = lookahead;
-
- expectKeyword(kind);
-
- declarations = parseVariableDeclarationList(kind);
-
- consumeSemicolon();
-
- return
delegate.markEnd(delegate.createVariableDeclaration(declarations, kind),
startToken);
- }
-
- // 12.3 Empty Statement
-
- function parseEmptyStatement() {
- expect(';');
- return delegate.createEmptyStatement();
- }
-
- // 12.4 Expression Statement
-
- function parseExpressionStatement() {
- var expr = parseExpression();
- consumeSemicolon();
- return delegate.createExpressionStatement(expr);
- }
-
- // 12.5 If statement
-
- function parseIfStatement() {
- var test, consequent, alternate;
-
- expectKeyword('if');
-
- expect('(');
-
- test = parseExpression();
-
- expect(')');
-
- consequent = parseStatement();
-
- if (matchKeyword('else')) {
- lex();
- alternate = parseStatement();
- } else {
- alternate = null;
- }
-
- return delegate.createIfStatement(test, consequent, alternate);
- }
-
- // 12.6 Iteration Statements
-
- function parseDoWhileStatement() {
- var body, test, oldInIteration;
-
- expectKeyword('do');
-
- oldInIteration = state.inIteration;
- state.inIteration = true;
-
- body = parseStatement();
-
- state.inIteration = oldInIteration;
-
- expectKeyword('while');
-
- expect('(');
-
- test = parseExpression();
-
- expect(')');
-
- if (match(';')) {
- lex();
- }
-
- return delegate.createDoWhileStatement(body, test);
- }
-
- function parseWhileStatement() {
- var test, body, oldInIteration;
-
- expectKeyword('while');
-
- expect('(');
-
- test = parseExpression();
-
- expect(')');
-
- oldInIteration = state.inIteration;
- state.inIteration = true;
-
- body = parseStatement();
-
- state.inIteration = oldInIteration;
-
- return delegate.createWhileStatement(test, body);
- }
-
- function parseForVariableDeclaration() {
- var token, declarations, startToken;
-
- startToken = lookahead;
- token = lex();
- declarations = parseVariableDeclarationList();
-
- return
delegate.markEnd(delegate.createVariableDeclaration(declarations, token.value),
startToken);
- }
-
- function parseForStatement() {
- var init, test, update, left, right, body, oldInIteration;
-
- init = test = update = null;
-
- expectKeyword('for');
-
- expect('(');
-
- if (match(';')) {
- lex();
- } else {
- if (matchKeyword('var') || matchKeyword('let')) {
- state.allowIn = false;
- init = parseForVariableDeclaration();
- state.allowIn = true;
-
- if (init.declarations.length === 1 && matchKeyword('in')) {
- lex();
- left = init;
- right = parseExpression();
- init = null;
- }
- } else {
- state.allowIn = false;
- init = parseExpression();
- state.allowIn = true;
-
- if (matchKeyword('in')) {
- // LeftHandSideExpression
- if (!isLeftHandSide(init)) {
- throwErrorTolerant({}, Messages.InvalidLHSInForIn);
- }
-
- lex();
- left = init;
- right = parseExpression();
- init = null;
- }
- }
-
- if (typeof left === 'undefined') {
- expect(';');
- }
- }
-
- if (typeof left === 'undefined') {
-
- if (!match(';')) {
- test = parseExpression();
- }
- expect(';');
-
- if (!match(')')) {
- update = parseExpression();
- }
- }
-
- expect(')');
-
- oldInIteration = state.inIteration;
- state.inIteration = true;
-
- body = parseStatement();
-
- state.inIteration = oldInIteration;
-
- return (typeof left === 'undefined') ?
- delegate.createForStatement(init, test, update, body) :
- delegate.createForInStatement(left, right, body);
- }
-
- // 12.7 The continue statement
-
- function parseContinueStatement() {
- var label = null, key;
-
- expectKeyword('continue');
-
- // Optimize the most common form: 'continue;'.
- if (source.charCodeAt(index) === 0x3B) {
- lex();
-
- if (!state.inIteration) {
- throwError({}, Messages.IllegalContinue);
- }
-
- return delegate.createContinueStatement(null);
- }
-
- if (peekLineTerminator()) {
- if (!state.inIteration) {
- throwError({}, Messages.IllegalContinue);
- }
-
- return delegate.createContinueStatement(null);
- }
-
- if (lookahead.type === Token.Identifier) {
- label = parseVariableIdentifier();
-
- key = '$' + label.name;
- if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
- throwError({}, Messages.UnknownLabel, label.name);
- }
- }
-
- consumeSemicolon();
-
- if (label === null && !state.inIteration) {
- throwError({}, Messages.IllegalContinue);
- }
-
- return delegate.createContinueStatement(label);
- }
-
- // 12.8 The break statement
-
- function parseBreakStatement() {
- var label = null, key;
-
- expectKeyword('break');
-
- // Catch the very common case first: immediately a semicolon (U+003B).
- if (source.charCodeAt(index) === 0x3B) {
- lex();
-
- if (!(state.inIteration || state.inSwitch)) {
- throwError({}, Messages.IllegalBreak);
- }
-
- return delegate.createBreakStatement(null);
- }
-
- if (peekLineTerminator()) {
- if (!(state.inIteration || state.inSwitch)) {
- throwError({}, Messages.IllegalBreak);
- }
-
- return delegate.createBreakStatement(null);
- }
-
- if (lookahead.type === Token.Identifier) {
- label = parseVariableIdentifier();
-
- key = '$' + label.name;
- if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
- throwError({}, Messages.UnknownLabel, label.name);
- }
- }
-
- consumeSemicolon();
-
- if (label === null && !(state.inIteration || state.inSwitch)) {
- throwError({}, Messages.IllegalBreak);
- }
-
- return delegate.createBreakStatement(label);
- }
-
- // 12.9 The return statement
-
- function parseReturnStatement() {
- var argument = null;
-
- expectKeyword('return');
-
- if (!state.inFunctionBody) {
- throwErrorTolerant({}, Messages.IllegalReturn);
- }
-
- // 'return' followed by a space and an identifier is very common.
- if (source.charCodeAt(index) === 0x20) {
- if (isIdentifierStart(source.charCodeAt(index + 1))) {
- argument = parseExpression();
- consumeSemicolon();
- return delegate.createReturnStatement(argument);
- }
- }
-
- if (peekLineTerminator()) {
- return delegate.createReturnStatement(null);
- }
-
- if (!match(';')) {
- if (!match('}') && lookahead.type !== Token.EOF) {
- argument = parseExpression();
- }
- }
-
- consumeSemicolon();
-
- return delegate.createReturnStatement(argument);
- }
-
- // 12.10 The with statement
-
- function parseWithStatement() {
- var object, body;
-
- if (strict) {
- // TODO(ikarienator): Should we update the test cases instead?
- skipComment();
- throwErrorTolerant({}, Messages.StrictModeWith);
- }
-
- expectKeyword('with');
-
- expect('(');
-
- object = parseExpression();
-
- expect(')');
-
- body = parseStatement();
-
- return delegate.createWithStatement(object, body);
- }
-
- // 12.10 The swith statement
-
- function parseSwitchCase() {
- var test, consequent = [], statement, startToken;
-
- startToken = lookahead;
- if (matchKeyword('default')) {
- lex();
- test = null;
- } else {
- expectKeyword('case');
- test = parseExpression();
- }
- expect(':');
-
- while (index < length) {
- if (match('}') || matchKeyword('default') || matchKeyword('case'))
{
- break;
- }
- statement = parseStatement();
- consequent.push(statement);
- }
-
- return delegate.markEnd(delegate.createSwitchCase(test, consequent),
startToken);
- }
-
- function parseSwitchStatement() {
- var discriminant, cases, clause, oldInSwitch, defaultFound;
-
- expectKeyword('switch');
-
- expect('(');
-
- discriminant = parseExpression();
-
- expect(')');
-
- expect('{');
-
- cases = [];
-
- if (match('}')) {
- lex();
- return delegate.createSwitchStatement(discriminant, cases);
- }
-
- oldInSwitch = state.inSwitch;
- state.inSwitch = true;
- defaultFound = false;
-
- while (index < length) {
- if (match('}')) {
- break;
- }
- clause = parseSwitchCase();
- if (clause.test === null) {
- if (defaultFound) {
- throwError({}, Messages.MultipleDefaultsInSwitch);
- }
- defaultFound = true;
- }
- cases.push(clause);
- }
-
- state.inSwitch = oldInSwitch;
-
- expect('}');
-
- return delegate.createSwitchStatement(discriminant, cases);
- }
-
- // 12.13 The throw statement
-
- function parseThrowStatement() {
- var argument;
-
- expectKeyword('throw');
-
- if (peekLineTerminator()) {
- throwError({}, Messages.NewlineAfterThrow);
- }
-
- argument = parseExpression();
-
- consumeSemicolon();
-
- return delegate.createThrowStatement(argument);
- }
-
- // 12.14 The try statement
-
- function parseCatchClause() {
- var param, body, startToken;
-
- startToken = lookahead;
- expectKeyword('catch');
-
- expect('(');
- if (match(')')) {
- throwUnexpected(lookahead);
- }
-
- param = parseVariableIdentifier();
- // 12.14.1
- if (strict && isRestrictedWord(param.name)) {
- throwErrorTolerant({}, Messages.StrictCatchVariable);
- }
-
- expect(')');
- body = parseBlock();
- return delegate.markEnd(delegate.createCatchClause(param, body),
startToken);
- }
-
- function parseTryStatement() {
- var block, handlers = [], finalizer = null;
-
- expectKeyword('try');
-
- block = parseBlock();
-
- if (matchKeyword('catch')) {
- handlers.push(parseCatchClause());
- }
-
- if (matchKeyword('finally')) {
- lex();
- finalizer = parseBlock();
- }
-
- if (handlers.length === 0 && !finalizer) {
- throwError({}, Messages.NoCatchOrFinally);
- }
-
- return delegate.createTryStatement(block, [], handlers, finalizer);
- }
-
- // 12.15 The debugger statement
-
- function parseDebuggerStatement() {
- expectKeyword('debugger');
-
- consumeSemicolon();
-
- return delegate.createDebuggerStatement();
- }
-
- // 12 Statements
-
- function parseStatement() {
- var type = lookahead.type,
- expr,
- labeledBody,
- key,
- startToken;
-
- if (type === Token.EOF) {
- throwUnexpected(lookahead);
- }
-
- if (type === Token.Punctuator && lookahead.value === '{') {
- return parseBlock();
- }
-
- startToken = lookahead;
-
- if (type === Token.Punctuator) {
- switch (lookahead.value) {
- case ';':
- return delegate.markEnd(parseEmptyStatement(), startToken);
- case '(':
- return delegate.markEnd(parseExpressionStatement(),
startToken);
- default:
- break;
- }
- }
-
- if (type === Token.Keyword) {
- switch (lookahead.value) {
- case 'break':
- return delegate.markEnd(parseBreakStatement(), startToken);
- case 'continue':
- return delegate.markEnd(parseContinueStatement(), startToken);
- case 'debugger':
- return delegate.markEnd(parseDebuggerStatement(), startToken);
- case 'do':
- return delegate.markEnd(parseDoWhileStatement(), startToken);
- case 'for':
- return delegate.markEnd(parseForStatement(), startToken);
- case 'function':
- return delegate.markEnd(parseFunctionDeclaration(),
startToken);
- case 'if':
- return delegate.markEnd(parseIfStatement(), startToken);
- case 'return':
- return delegate.markEnd(parseReturnStatement(), startToken);
- case 'switch':
- return delegate.markEnd(parseSwitchStatement(), startToken);
- case 'throw':
- return delegate.markEnd(parseThrowStatement(), startToken);
- case 'try':
- return delegate.markEnd(parseTryStatement(), startToken);
- case 'var':
- return delegate.markEnd(parseVariableStatement(), startToken);
- case 'while':
- return delegate.markEnd(parseWhileStatement(), startToken);
- case 'with':
- return delegate.markEnd(parseWithStatement(), startToken);
- default:
- break;
- }
- }
-
- expr = parseExpression();
-
- // 12.12 Labelled Statements
- if ((expr.type === Syntax.Identifier) && match(':')) {
- lex();
-
- key = '$' + expr.name;
- if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
- throwError({}, Messages.Redeclaration, 'Label', expr.name);
- }
-
- state.labelSet[key] = true;
- labeledBody = parseStatement();
- delete state.labelSet[key];
- return delegate.markEnd(delegate.createLabeledStatement(expr,
labeledBody), startToken);
- }
-
- consumeSemicolon();
-
- return delegate.markEnd(delegate.createExpressionStatement(expr),
startToken);
- }
-
- // 13 Function Definition
-
- function parseFunctionSourceElements() {
- var sourceElement, sourceElements = [], token, directive,
firstRestricted,
- oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody,
startToken;
-
- startToken = lookahead;
- expect('{');
-
- while (index < length) {
- if (lookahead.type !== Token.StringLiteral) {
- break;
- }
- token = lookahead;
-
- sourceElement = parseSourceElement();
- sourceElements.push(sourceElement);
- if (sourceElement.expression.type !== Syntax.Literal) {
- // this is not directive
- break;
- }
- directive = source.slice(token.start + 1, token.end - 1);
- if (directive === 'use strict') {
- strict = true;
- if (firstRestricted) {
- throwErrorTolerant(firstRestricted,
Messages.StrictOctalLiteral);
- }
- } else {
- if (!firstRestricted && token.octal) {
- firstRestricted = token;
- }
- }
- }
-
- oldLabelSet = state.labelSet;
- oldInIteration = state.inIteration;
- oldInSwitch = state.inSwitch;
- oldInFunctionBody = state.inFunctionBody;
-
- state.labelSet = {};
- state.inIteration = false;
- state.inSwitch = false;
- state.inFunctionBody = true;
-
- while (index < length) {
- if (match('}')) {
- break;
- }
- sourceElement = parseSourceElement();
- if (typeof sourceElement === 'undefined') {
- break;
- }
- sourceElements.push(sourceElement);
- }
-
- expect('}');
-
- state.labelSet = oldLabelSet;
- state.inIteration = oldInIteration;
- state.inSwitch = oldInSwitch;
- state.inFunctionBody = oldInFunctionBody;
-
- return delegate.markEnd(delegate.createBlockStatement(sourceElements),
startToken);
- }
-
- function parseParams(firstRestricted) {
- var param, params = [], token, stricted, paramSet, key, message;
- expect('(');
-
- if (!match(')')) {
- paramSet = {};
- while (index < length) {
- token = lookahead;
- param = parseVariableIdentifier();
- key = '$' + token.value;
- if (strict) {
- if (isRestrictedWord(token.value)) {
- stricted = token;
- message = Messages.StrictParamName;
- }
- if (Object.prototype.hasOwnProperty.call(paramSet, key)) {
- stricted = token;
- message = Messages.StrictParamDupe;
- }
- } else if (!firstRestricted) {
- if (isRestrictedWord(token.value)) {
- firstRestricted = token;
- message = Messages.StrictParamName;
- } else if (isStrictModeReservedWord(token.value)) {
- firstRestricted = token;
- message = Messages.StrictReservedWord;
- } else if (Object.prototype.hasOwnProperty.call(paramSet,
key)) {
- firstRestricted = token;
- message = Messages.StrictParamDupe;
- }
- }
- params.push(param);
- paramSet[key] = true;
- if (match(')')) {
- break;
- }
- expect(',');
- }
- }
-
- expect(')');
-
- return {
- params: params,
- stricted: stricted,
- firstRestricted: firstRestricted,
- message: message
- };
- }
-
- function parseFunctionDeclaration() {
- var id, params = [], body, token, stricted, tmp, firstRestricted,
message, previousStrict, startToken;
-
- startToken = lookahead;
-
- expectKeyword('function');
- token = lookahead;
- id = parseVariableIdentifier();
- if (strict) {
- if (isRestrictedWord(token.value)) {
- throwErrorTolerant(token, Messages.StrictFunctionName);
- }
- } else {
- if (isRestrictedWord(token.value)) {
- firstRestricted = token;
- message = Messages.StrictFunctionName;
- } else if (isStrictModeReservedWord(token.value)) {
- firstRestricted = token;
- message = Messages.StrictReservedWord;
- }
- }
-
- tmp = parseParams(firstRestricted);
- params = tmp.params;
- stricted = tmp.stricted;
- firstRestricted = tmp.firstRestricted;
- if (tmp.message) {
- message = tmp.message;
- }
-
- previousStrict = strict;
- body = parseFunctionSourceElements();
- if (strict && firstRestricted) {
- throwError(firstRestricted, message);
- }
- if (strict && stricted) {
- throwErrorTolerant(stricted, message);
- }
- strict = previousStrict;
-
- return delegate.markEnd(delegate.createFunctionDeclaration(id, params,
[], body), startToken);
- }
-
- function parseFunctionExpression() {
- var token, id = null, stricted, firstRestricted, message, tmp, params
= [], body, previousStrict, startToken;
-
- startToken = lookahead;
- expectKeyword('function');
-
- if (!match('(')) {
- token = lookahead;
- id = parseVariableIdentifier();
- if (strict) {
- if (isRestrictedWord(token.value)) {
- throwErrorTolerant(token, Messages.StrictFunctionName);
- }
- } else {
- if (isRestrictedWord(token.value)) {
- firstRestricted = token;
- message = Messages.StrictFunctionName;
- } else if (isStrictModeReservedWord(token.value)) {
- firstRestricted = token;
- message = Messages.StrictReservedWord;
- }
- }
- }
-
- tmp = parseParams(firstRestricted);
- params = tmp.params;
- stricted = tmp.stricted;
- firstRestricted = tmp.firstRestricted;
- if (tmp.message) {
- message = tmp.message;
- }
-
- previousStrict = strict;
- body = parseFunctionSourceElements();
- if (strict && firstRestricted) {
- throwError(firstRestricted, message);
- }
- if (strict && stricted) {
- throwErrorTolerant(stricted, message);
- }
- strict = previousStrict;
-
- return delegate.markEnd(delegate.createFunctionExpression(id, params,
[], body), startToken);
- }
-
- // 14 Program
-
- function parseSourceElement() {
- if (lookahead.type === Token.Keyword) {
- switch (lookahead.value) {
- case 'const':
- case 'let':
- return parseConstLetDeclaration(lookahead.value);
- case 'function':
- return parseFunctionDeclaration();
- default:
- return parseStatement();
- }
- }
-
- if (lookahead.type !== Token.EOF) {
- return parseStatement();
- }
- }
-
- function parseSourceElements() {
- var sourceElement, sourceElements = [], token, directive,
firstRestricted;
-
- while (index < length) {
- token = lookahead;
- if (token.type !== Token.StringLiteral) {
- break;
- }
-
- sourceElement = parseSourceElement();
- sourceElements.push(sourceElement);
- if (sourceElement.expression.type !== Syntax.Literal) {
- // this is not directive
- break;
- }
- directive = source.slice(token.start + 1, token.end - 1);
- if (directive === 'use strict') {
- strict = true;
- if (firstRestricted) {
- throwErrorTolerant(firstRestricted,
Messages.StrictOctalLiteral);
- }
- } else {
- if (!firstRestricted && token.octal) {
- firstRestricted = token;
- }
- }
- }
-
- while (index < length) {
- sourceElement = parseSourceElement();
- /* istanbul ignore if */
- if (typeof sourceElement === 'undefined') {
- break;
- }
- sourceElements.push(sourceElement);
- }
- return sourceElements;
- }
-
- function parseProgram() {
- var body, startToken;
-
- skipComment();
- peek();
- startToken = lookahead;
- strict = false;
-
- body = parseSourceElements();
- return delegate.markEnd(delegate.createProgram(body), startToken);
- }
-
- function filterTokenLocation() {
- var i, entry, token, tokens = [];
-
- for (i = 0; i < extra.tokens.length; ++i) {
- entry = extra.tokens[i];
- token = {
- type: entry.type,
- value: entry.value
- };
- if (extra.range) {
- token.range = entry.range;
- }
- if (extra.loc) {
- token.loc = entry.loc;
- }
- tokens.push(token);
- }
-
- extra.tokens = tokens;
- }
-
- function tokenize(code, options) {
- var toString,
- token,
- tokens;
-
- toString = String;
- if (typeof code !== 'string' && !(code instanceof String)) {
- code = toString(code);
- }
-
- delegate = SyntaxTreeDelegate;
- source = code;
- index = 0;
- lineNumber = (source.length > 0) ? 1 : 0;
- lineStart = 0;
- length = source.length;
- lookahead = null;
- state = {
- allowIn: true,
- labelSet: {},
- inFunctionBody: false,
- inIteration: false,
- inSwitch: false,
- lastCommentStart: -1
- };
-
- extra = {};
-
- // Options matching.
- options = options || {};
-
- // Of course we collect tokens here.
- options.tokens = true;
- extra.tokens = [];
- extra.tokenize = true;
- // The following two fields are necessary to compute the Regex tokens.
- extra.openParenToken = -1;
- extra.openCurlyToken = -1;
-
- extra.range = (typeof options.range === 'boolean') && options.range;
- extra.loc = (typeof options.loc === 'boolean') && options.loc;
-
- if (typeof options.comment === 'boolean' && options.comment) {
- extra.comments = [];
- }
- if (typeof options.tolerant === 'boolean' && options.tolerant) {
- extra.errors = [];
- }
-
- try {
- peek();
- if (lookahead.type === Token.EOF) {
- return extra.tokens;
- }
-
- token = lex();
- while (lookahead.type !== Token.EOF) {
- try {
- token = lex();
- } catch (lexError) {
- token = lookahead;
- if (extra.errors) {
- extra.errors.push(lexError);
- // We have to break on the first error
- // to avoid infinite loops.
- break;
- } else {
- throw lexError;
- }
- }
- }
-
- filterTokenLocation();
- tokens = extra.tokens;
- if (typeof extra.comments !== 'undefined') {
- tokens.comments = extra.comments;
- }
- if (typeof extra.errors !== 'undefined') {
- tokens.errors = extra.errors;
- }
- } catch (e) {
- throw e;
- } finally {
- extra = {};
- }
- return tokens;
- }
-
- function parse(code, options) {
- var program, toString;
-
- toString = String;
- if (typeof code !== 'string' && !(code instanceof String)) {
- code = toString(code);
- }
-
- delegate = SyntaxTreeDelegate;
- source = code;
- index = 0;
- lineNumber = (source.length > 0) ? 1 : 0;
- lineStart = 0;
- length = source.length;
- lookahead = null;
- state = {
- allowIn: true,
- labelSet: {},
- inFunctionBody: false,
- inIteration: false,
- inSwitch: false,
- lastCommentStart: -1
- };
-
- extra = {};
- if (typeof options !== 'undefined') {
- extra.range = (typeof options.range === 'boolean') &&
options.range;
- extra.loc = (typeof options.loc === 'boolean') && options.loc;
- extra.attachComment = (typeof options.attachComment === 'boolean')
&& options.attachComment;
-
- if (extra.loc && options.source !== null && options.source !==
undefined) {
- extra.source = toString(options.source);
- }
-
- if (typeof options.tokens === 'boolean' && options.tokens) {
- extra.tokens = [];
- }
- if (typeof options.comment === 'boolean' && options.comment) {
- extra.comments = [];
- }
- if (typeof options.tolerant === 'boolean' && options.tolerant) {
- extra.errors = [];
- }
- if (extra.attachComment) {
- extra.range = true;
- extra.comments = [];
- extra.bottomRightStack = [];
- extra.trailingComments = [];
- extra.leadingComments = [];
- }
- }
-
- try {
- program = parseProgram();
- if (typeof extra.comments !== 'undefined') {
- program.comments = extra.comments;
- }
- if (typeof extra.tokens !== 'undefined') {
- filterTokenLocation();
- program.tokens = extra.tokens;
- }
- if (typeof extra.errors !== 'undefined') {
- program.errors = extra.errors;
- }
- } catch (e) {
- throw e;
- } finally {
- extra = {};
- }
-
- return program;
- }
-
- // Sync with *.json manifests.
- exports.version = '1.2.2';
-
- exports.tokenize = tokenize;
-
- exports.parse = parse;
-
- // Deep copy.
- /* istanbul ignore next */
- exports.Syntax = (function () {
- var name, types = {};
-
- if (typeof Object.create === 'function') {
- types = Object.create(null);
- }
-
- for (name in Syntax) {
- if (Syntax.hasOwnProperty(name)) {
- types[name] = Syntax[name];
- }
- }
-
- if (typeof Object.freeze === 'function') {
- Object.freeze(types);
- }
-
- return types;
- }());
-
-}));
-/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/languages/javascript/libraries/estraverse.js
b/languages/javascript/libraries/estraverse.js
deleted file mode 100644
index 4009cb0..0000000
--- a/languages/javascript/libraries/estraverse.js
+++ /dev/null
@@ -1,838 +0,0 @@
-/*
- Copyright (C) 2012-2013 Yusuke Suzuki <address@hidden>
- Copyright (C) 2012 Ariya Hidayat <address@hidden>
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-/*jslint vars:false, bitwise:true*/
-/*jshint indent:4*/
-/*global exports:true, define:true*/
-(function (root, factory) {
- 'use strict';
-
- // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
- // and plain browser loading,
- if (typeof define === 'function' && define.amd) {
- define(['exports'], factory);
- } else if (typeof exports !== 'undefined') {
- factory(exports);
- } else {
- factory((root.estraverse = {}));
- }
-}(this, function (exports) {
- 'use strict';
-
- var Syntax,
- isArray,
- VisitorOption,
- VisitorKeys,
- objectCreate,
- objectKeys,
- BREAK,
- SKIP,
- REMOVE;
-
- function ignoreJSHintError() { }
-
- isArray = Array.isArray;
- if (!isArray) {
- isArray = function isArray(array) {
- return Object.prototype.toString.call(array) === '[object Array]';
- };
- }
-
- function deepCopy(obj) {
- var ret = {}, key, val;
- for (key in obj) {
- if (obj.hasOwnProperty(key)) {
- val = obj[key];
- if (typeof val === 'object' && val !== null) {
- ret[key] = deepCopy(val);
- } else {
- ret[key] = val;
- }
- }
- }
- return ret;
- }
-
- function shallowCopy(obj) {
- var ret = {}, key;
- for (key in obj) {
- if (obj.hasOwnProperty(key)) {
- ret[key] = obj[key];
- }
- }
- return ret;
- }
- ignoreJSHintError(shallowCopy);
-
- // based on LLVM libc++ upper_bound / lower_bound
- // MIT License
-
- function upperBound(array, func) {
- var diff, len, i, current;
-
- len = array.length;
- i = 0;
-
- while (len) {
- diff = len >>> 1;
- current = i + diff;
- if (func(array[current])) {
- len = diff;
- } else {
- i = current + 1;
- len -= diff + 1;
- }
- }
- return i;
- }
-
- function lowerBound(array, func) {
- var diff, len, i, current;
-
- len = array.length;
- i = 0;
-
- while (len) {
- diff = len >>> 1;
- current = i + diff;
- if (func(array[current])) {
- i = current + 1;
- len -= diff + 1;
- } else {
- len = diff;
- }
- }
- return i;
- }
- ignoreJSHintError(lowerBound);
-
- objectCreate = Object.create || (function () {
- function F() { }
-
- return function (o) {
- F.prototype = o;
- return new F();
- };
- })();
-
- objectKeys = Object.keys || function (o) {
- var keys = [], key;
- for (key in o) {
- keys.push(key);
- }
- return keys;
- };
-
- function extend(to, from) {
- objectKeys(from).forEach(function (key) {
- to[key] = from[key];
- });
- return to;
- }
-
- Syntax = {
- AssignmentExpression: 'AssignmentExpression',
- ArrayExpression: 'ArrayExpression',
- ArrayPattern: 'ArrayPattern',
- ArrowFunctionExpression: 'ArrowFunctionExpression',
- BlockStatement: 'BlockStatement',
- BinaryExpression: 'BinaryExpression',
- BreakStatement: 'BreakStatement',
- CallExpression: 'CallExpression',
- CatchClause: 'CatchClause',
- ClassBody: 'ClassBody',
- ClassDeclaration: 'ClassDeclaration',
- ClassExpression: 'ClassExpression',
- ComprehensionBlock: 'ComprehensionBlock', // CAUTION: It's deferred
to ES7.
- ComprehensionExpression: 'ComprehensionExpression', // CAUTION: It's
deferred to ES7.
- ConditionalExpression: 'ConditionalExpression',
- ContinueStatement: 'ContinueStatement',
- DebuggerStatement: 'DebuggerStatement',
- DirectiveStatement: 'DirectiveStatement',
- DoWhileStatement: 'DoWhileStatement',
- EmptyStatement: 'EmptyStatement',
- ExportBatchSpecifier: 'ExportBatchSpecifier',
- ExportDeclaration: 'ExportDeclaration',
- ExportSpecifier: 'ExportSpecifier',
- ExpressionStatement: 'ExpressionStatement',
- ForStatement: 'ForStatement',
- ForInStatement: 'ForInStatement',
- ForOfStatement: 'ForOfStatement',
- FunctionDeclaration: 'FunctionDeclaration',
- FunctionExpression: 'FunctionExpression',
- GeneratorExpression: 'GeneratorExpression', // CAUTION: It's deferred
to ES7.
- Identifier: 'Identifier',
- IfStatement: 'IfStatement',
- ImportDeclaration: 'ImportDeclaration',
- ImportDefaultSpecifier: 'ImportDefaultSpecifier',
- ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
- ImportSpecifier: 'ImportSpecifier',
- Literal: 'Literal',
- LabeledStatement: 'LabeledStatement',
- LogicalExpression: 'LogicalExpression',
- MemberExpression: 'MemberExpression',
- MethodDefinition: 'MethodDefinition',
- ModuleSpecifier: 'ModuleSpecifier',
- NewExpression: 'NewExpression',
- ObjectExpression: 'ObjectExpression',
- ObjectPattern: 'ObjectPattern',
- Program: 'Program',
- Property: 'Property',
- ReturnStatement: 'ReturnStatement',
- SequenceExpression: 'SequenceExpression',
- SpreadElement: 'SpreadElement',
- SwitchStatement: 'SwitchStatement',
- SwitchCase: 'SwitchCase',
- TaggedTemplateExpression: 'TaggedTemplateExpression',
- TemplateElement: 'TemplateElement',
- TemplateLiteral: 'TemplateLiteral',
- ThisExpression: 'ThisExpression',
- ThrowStatement: 'ThrowStatement',
- TryStatement: 'TryStatement',
- UnaryExpression: 'UnaryExpression',
- UpdateExpression: 'UpdateExpression',
- VariableDeclaration: 'VariableDeclaration',
- VariableDeclarator: 'VariableDeclarator',
- WhileStatement: 'WhileStatement',
- WithStatement: 'WithStatement',
- YieldExpression: 'YieldExpression'
- };
-
- VisitorKeys = {
- AssignmentExpression: ['left', 'right'],
- ArrayExpression: ['elements'],
- ArrayPattern: ['elements'],
- ArrowFunctionExpression: ['params', 'defaults', 'rest', 'body'],
- BlockStatement: ['body'],
- BinaryExpression: ['left', 'right'],
- BreakStatement: ['label'],
- CallExpression: ['callee', 'arguments'],
- CatchClause: ['param', 'body'],
- ClassBody: ['body'],
- ClassDeclaration: ['id', 'body', 'superClass'],
- ClassExpression: ['id', 'body', 'superClass'],
- ComprehensionBlock: ['left', 'right'], // CAUTION: It's deferred to
ES7.
- ComprehensionExpression: ['blocks', 'filter', 'body'], // CAUTION:
It's deferred to ES7.
- ConditionalExpression: ['test', 'consequent', 'alternate'],
- ContinueStatement: ['label'],
- DebuggerStatement: [],
- DirectiveStatement: [],
- DoWhileStatement: ['body', 'test'],
- EmptyStatement: [],
- ExportBatchSpecifier: [],
- ExportDeclaration: ['declaration', 'specifiers', 'source'],
- ExportSpecifier: ['id', 'name'],
- ExpressionStatement: ['expression'],
- ForStatement: ['init', 'test', 'update', 'body'],
- ForInStatement: ['left', 'right', 'body'],
- ForOfStatement: ['left', 'right', 'body'],
- FunctionDeclaration: ['id', 'params', 'defaults', 'rest', 'body'],
- FunctionExpression: ['id', 'params', 'defaults', 'rest', 'body'],
- GeneratorExpression: ['blocks', 'filter', 'body'], // CAUTION: It's
deferred to ES7.
- Identifier: [],
- IfStatement: ['test', 'consequent', 'alternate'],
- ImportDeclaration: ['specifiers', 'source'],
- ImportDefaultSpecifier: ['id'],
- ImportNamespaceSpecifier: ['id'],
- ImportSpecifier: ['id', 'name'],
- Literal: [],
- LabeledStatement: ['label', 'body'],
- LogicalExpression: ['left', 'right'],
- MemberExpression: ['object', 'property'],
- MethodDefinition: ['key', 'value'],
- ModuleSpecifier: [],
- NewExpression: ['callee', 'arguments'],
- ObjectExpression: ['properties'],
- ObjectPattern: ['properties'],
- Program: ['body'],
- Property: ['key', 'value'],
- ReturnStatement: ['argument'],
- SequenceExpression: ['expressions'],
- SpreadElement: ['argument'],
- SwitchStatement: ['discriminant', 'cases'],
- SwitchCase: ['test', 'consequent'],
- TaggedTemplateExpression: ['tag', 'quasi'],
- TemplateElement: [],
- TemplateLiteral: ['quasis', 'expressions'],
- ThisExpression: [],
- ThrowStatement: ['argument'],
- TryStatement: ['block', 'handlers', 'handler', 'guardedHandlers',
'finalizer'],
- UnaryExpression: ['argument'],
- UpdateExpression: ['argument'],
- VariableDeclaration: ['declarations'],
- VariableDeclarator: ['id', 'init'],
- WhileStatement: ['test', 'body'],
- WithStatement: ['object', 'body'],
- YieldExpression: ['argument']
- };
-
- // unique id
- BREAK = {};
- SKIP = {};
- REMOVE = {};
-
- VisitorOption = {
- Break: BREAK,
- Skip: SKIP,
- Remove: REMOVE
- };
-
- function Reference(parent, key) {
- this.parent = parent;
- this.key = key;
- }
-
- Reference.prototype.replace = function replace(node) {
- this.parent[this.key] = node;
- };
-
- Reference.prototype.remove = function remove() {
- if (isArray(this.parent)) {
- this.parent.splice(this.key, 1);
- return true;
- } else {
- this.replace(null);
- return false;
- }
- };
-
- function Element(node, path, wrap, ref) {
- this.node = node;
- this.path = path;
- this.wrap = wrap;
- this.ref = ref;
- }
-
- function Controller() { }
-
- // API:
- // return property path array from root to current node
- Controller.prototype.path = function path() {
- var i, iz, j, jz, result, element;
-
- function addToPath(result, path) {
- if (isArray(path)) {
- for (j = 0, jz = path.length; j < jz; ++j) {
- result.push(path[j]);
- }
- } else {
- result.push(path);
- }
- }
-
- // root node
- if (!this.__current.path) {
- return null;
- }
-
- // first node is sentinel, second node is root element
- result = [];
- for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {
- element = this.__leavelist[i];
- addToPath(result, element.path);
- }
- addToPath(result, this.__current.path);
- return result;
- };
-
- // API:
- // return type of current node
- Controller.prototype.type = function () {
- var node = this.current();
- return node.type || this.__current.wrap;
- };
-
- // API:
- // return array of parent elements
- Controller.prototype.parents = function parents() {
- var i, iz, result;
-
- // first node is sentinel
- result = [];
- for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {
- result.push(this.__leavelist[i].node);
- }
-
- return result;
- };
-
- // API:
- // return current node
- Controller.prototype.current = function current() {
- return this.__current.node;
- };
-
- Controller.prototype.__execute = function __execute(callback, element) {
- var previous, result;
-
- result = undefined;
-
- previous = this.__current;
- this.__current = element;
- this.__state = null;
- if (callback) {
- result = callback.call(this, element.node,
this.__leavelist[this.__leavelist.length - 1].node);
- }
- this.__current = previous;
-
- return result;
- };
-
- // API:
- // notify control skip / break
- Controller.prototype.notify = function notify(flag) {
- this.__state = flag;
- };
-
- // API:
- // skip child nodes of current node
- Controller.prototype.skip = function () {
- this.notify(SKIP);
- };
-
- // API:
- // break traversals
- Controller.prototype['break'] = function () {
- this.notify(BREAK);
- };
-
- // API:
- // remove node
- Controller.prototype.remove = function () {
- this.notify(REMOVE);
- };
-
- Controller.prototype.__initialize = function(root, visitor) {
- this.visitor = visitor;
- this.root = root;
- this.__worklist = [];
- this.__leavelist = [];
- this.__current = null;
- this.__state = null;
- this.__fallback = visitor.fallback === 'iteration';
- this.__keys = VisitorKeys;
- if (visitor.keys) {
- this.__keys = extend(objectCreate(this.__keys), visitor.keys);
- }
- };
-
- function isNode(node) {
- if (node == null) {
- return false;
- }
- return typeof node === 'object' && typeof node.type === 'string';
- }
-
- function isProperty(nodeType, key) {
- return (nodeType === Syntax.ObjectExpression || nodeType ===
Syntax.ObjectPattern) && 'properties' === key;
- }
-
- Controller.prototype.traverse = function traverse(root, visitor) {
- var worklist,
- leavelist,
- element,
- node,
- nodeType,
- ret,
- key,
- current,
- current2,
- candidates,
- candidate,
- sentinel;
-
- this.__initialize(root, visitor);
-
- sentinel = {};
-
- // reference
- worklist = this.__worklist;
- leavelist = this.__leavelist;
-
- // initialize
- worklist.push(new Element(root, null, null, null));
- leavelist.push(new Element(null, null, null, null));
-
- while (worklist.length) {
- element = worklist.pop();
-
- if (element === sentinel) {
- element = leavelist.pop();
-
- ret = this.__execute(visitor.leave, element);
-
- if (this.__state === BREAK || ret === BREAK) {
- return;
- }
- continue;
- }
-
- if (element.node) {
-
- ret = this.__execute(visitor.enter, element);
-
- if (this.__state === BREAK || ret === BREAK) {
- return;
- }
-
- worklist.push(sentinel);
- leavelist.push(element);
-
- if (this.__state === SKIP || ret === SKIP) {
- continue;
- }
-
- node = element.node;
- nodeType = element.wrap || node.type;
- candidates = this.__keys[nodeType];
- if (!candidates) {
- if (this.__fallback) {
- candidates = objectKeys(node);
- } else {
- throw new Error('Unknown node type ' + nodeType + '.');
- }
- }
-
- current = candidates.length;
- while ((current -= 1) >= 0) {
- key = candidates[current];
- candidate = node[key];
- if (!candidate) {
- continue;
- }
-
- if (isArray(candidate)) {
- current2 = candidate.length;
- while ((current2 -= 1) >= 0) {
- if (!candidate[current2]) {
- continue;
- }
- if (isProperty(nodeType, candidates[current])) {
- element = new Element(candidate[current2],
[key, current2], 'Property', null);
- } else if (isNode(candidate[current2])) {
- element = new Element(candidate[current2],
[key, current2], null, null);
- } else {
- continue;
- }
- worklist.push(element);
- }
- } else if (isNode(candidate)) {
- worklist.push(new Element(candidate, key, null, null));
- }
- }
- }
- }
- };
-
- Controller.prototype.replace = function replace(root, visitor) {
- function removeElem(element) {
- var i,
- key,
- nextElem,
- parent;
-
- if (element.ref.remove()) {
- // When the reference is an element of an array.
- key = element.ref.key;
- parent = element.ref.parent;
-
- // If removed from array, then decrease following items' keys.
- i = worklist.length;
- while (i--) {
- nextElem = worklist[i];
- if (nextElem.ref && nextElem.ref.parent === parent) {
- if (nextElem.ref.key < key) {
- break;
- }
- --nextElem.ref.key;
- }
- }
- }
- }
-
- var worklist,
- leavelist,
- node,
- nodeType,
- target,
- element,
- current,
- current2,
- candidates,
- candidate,
- sentinel,
- outer,
- key;
-
- this.__initialize(root, visitor);
-
- sentinel = {};
-
- // reference
- worklist = this.__worklist;
- leavelist = this.__leavelist;
-
- // initialize
- outer = {
- root: root
- };
- element = new Element(root, null, null, new Reference(outer, 'root'));
- worklist.push(element);
- leavelist.push(element);
-
- while (worklist.length) {
- element = worklist.pop();
-
- if (element === sentinel) {
- element = leavelist.pop();
-
- target = this.__execute(visitor.leave, element);
-
- // node may be replaced with null,
- // so distinguish between undefined and null in this place
- if (target !== undefined && target !== BREAK && target !==
SKIP && target !== REMOVE) {
- // replace
- element.ref.replace(target);
- }
-
- if (this.__state === REMOVE || target === REMOVE) {
- removeElem(element);
- }
-
- if (this.__state === BREAK || target === BREAK) {
- return outer.root;
- }
- continue;
- }
-
- target = this.__execute(visitor.enter, element);
-
- // node may be replaced with null,
- // so distinguish between undefined and null in this place
- if (target !== undefined && target !== BREAK && target !== SKIP &&
target !== REMOVE) {
- // replace
- element.ref.replace(target);
- element.node = target;
- }
-
- if (this.__state === REMOVE || target === REMOVE) {
- removeElem(element);
- element.node = null;
- }
-
- if (this.__state === BREAK || target === BREAK) {
- return outer.root;
- }
-
- // node may be null
- node = element.node;
- if (!node) {
- continue;
- }
-
- worklist.push(sentinel);
- leavelist.push(element);
-
- if (this.__state === SKIP || target === SKIP) {
- continue;
- }
-
- nodeType = element.wrap || node.type;
- candidates = this.__keys[nodeType];
- if (!candidates) {
- if (this.__fallback) {
- candidates = objectKeys(node);
- } else {
- throw new Error('Unknown node type ' + nodeType + '.');
- }
- }
-
- current = candidates.length;
- while ((current -= 1) >= 0) {
- key = candidates[current];
- candidate = node[key];
- if (!candidate) {
- continue;
- }
-
- if (isArray(candidate)) {
- current2 = candidate.length;
- while ((current2 -= 1) >= 0) {
- if (!candidate[current2]) {
- continue;
- }
- if (isProperty(nodeType, candidates[current])) {
- element = new Element(candidate[current2], [key,
current2], 'Property', new Reference(candidate, current2));
- } else if (isNode(candidate[current2])) {
- element = new Element(candidate[current2], [key,
current2], null, new Reference(candidate, current2));
- } else {
- continue;
- }
- worklist.push(element);
- }
- } else if (isNode(candidate)) {
- worklist.push(new Element(candidate, key, null, new
Reference(node, key)));
- }
- }
- }
-
- return outer.root;
- };
-
- function traverse(root, visitor) {
- var controller = new Controller();
- return controller.traverse(root, visitor);
- }
-
- function replace(root, visitor) {
- var controller = new Controller();
- return controller.replace(root, visitor);
- }
-
- function extendCommentRange(comment, tokens) {
- var target;
-
- target = upperBound(tokens, function search(token) {
- return token.range[0] > comment.range[0];
- });
-
- comment.extendedRange = [comment.range[0], comment.range[1]];
-
- if (target !== tokens.length) {
- comment.extendedRange[1] = tokens[target].range[0];
- }
-
- target -= 1;
- if (target >= 0) {
- comment.extendedRange[0] = tokens[target].range[1];
- }
-
- return comment;
- }
-
- function attachComments(tree, providedComments, tokens) {
- // At first, we should calculate extended comment ranges.
- var comments = [], comment, len, i, cursor;
-
- if (!tree.range) {
- throw new Error('attachComments needs range information');
- }
-
- // tokens array is empty, we attach comments to tree as
'leadingComments'
- if (!tokens.length) {
- if (providedComments.length) {
- for (i = 0, len = providedComments.length; i < len; i += 1) {
- comment = deepCopy(providedComments[i]);
- comment.extendedRange = [0, tree.range[0]];
- comments.push(comment);
- }
- tree.leadingComments = comments;
- }
- return tree;
- }
-
- for (i = 0, len = providedComments.length; i < len; i += 1) {
- comments.push(extendCommentRange(deepCopy(providedComments[i]),
tokens));
- }
-
- // This is based on John Freeman's implementation.
- cursor = 0;
- traverse(tree, {
- enter: function (node) {
- var comment;
-
- while (cursor < comments.length) {
- comment = comments[cursor];
- if (comment.extendedRange[1] > node.range[0]) {
- break;
- }
-
- if (comment.extendedRange[1] === node.range[0]) {
- if (!node.leadingComments) {
- node.leadingComments = [];
- }
- node.leadingComments.push(comment);
- comments.splice(cursor, 1);
- } else {
- cursor += 1;
- }
- }
-
- // already out of owned node
- if (cursor === comments.length) {
- return VisitorOption.Break;
- }
-
- if (comments[cursor].extendedRange[0] > node.range[1]) {
- return VisitorOption.Skip;
- }
- }
- });
-
- cursor = 0;
- traverse(tree, {
- leave: function (node) {
- var comment;
-
- while (cursor < comments.length) {
- comment = comments[cursor];
- if (node.range[1] < comment.extendedRange[0]) {
- break;
- }
-
- if (node.range[1] === comment.extendedRange[0]) {
- if (!node.trailingComments) {
- node.trailingComments = [];
- }
- node.trailingComments.push(comment);
- comments.splice(cursor, 1);
- } else {
- cursor += 1;
- }
- }
-
- // already out of owned node
- if (cursor === comments.length) {
- return VisitorOption.Break;
- }
-
- if (comments[cursor].extendedRange[0] > node.range[1]) {
- return VisitorOption.Skip;
- }
- }
- });
-
- return tree;
- }
-
- exports.version = '1.8.0';
- exports.Syntax = Syntax;
- exports.traverse = traverse;
- exports.replace = replace;
- exports.attachComments = attachComments;
- exports.VisitorKeys = VisitorKeys;
- exports.VisitorOption = VisitorOption;
- exports.Controller = Controller;
-}));
-/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/languages/javascript/scopifier.js
b/languages/javascript/scopifier.js
deleted file mode 100644
index fd3549a..0000000
--- a/languages/javascript/scopifier.js
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (C) 2014-2015 Free Software Foundation, Inc.
-
-// This file is part of GNU Emacs.
-
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-'use strict';
-
-var escope = require('./libraries/escope'),
- esprima = require('./libraries/esprima');
-
-// Given code, returns an array of tokens for context-coloring.
-function scopifier(code) {
-
- var analyzedScopes,
- ast,
- definition,
- definitionsCount,
- definitionsIndex,
- i,
- isDefined,
- j,
- k,
- pointer,
- range,
- reference,
- scope,
- scopes,
- tokens,
- variable;
-
- // Strip BOM.
- code = code.replace(/^\ufeff/g, '');
-
- // Gracefully handle parse errors by doing nothing.
- try {
- ast = esprima.parse(code, {
- range: true
- });
- analyzedScopes = escope.analyze(ast).scopes;
- } catch (error) {
- process.exit(1);
- }
-
- scopes = [];
- tokens = [];
- for (i = 0; i < analyzedScopes.length; i += 1) {
- scope = analyzedScopes[i];
- // Having its level set implies it was already annotated.
- if (scope.level === undefined) {
- if (scope.upper) {
- if (scope.upper.functionExpressionScope) {
- // Pretend function expression scope doesn't exist.
- scope.level = scope.upper.level;
- scope.variables =
scope.upper.variables.concat(scope.variables);
- } else {
- scope.level = scope.upper.level + 1;
- }
- } else {
- // Base case.
- scope.level = 0;
- }
- // We've only given the scope a level for posterity's sake. We're
- // done now.
- if (!scope.functionExpressionScope) {
- range = scope.block.range;
- scopes.push(
- range[0] + 1,
- range[1] + 1,
- scope.level
- );
- definitionsIndex = tokens.length;
- definitionsCount = 0;
- for (j = 0; j < scope.variables.length; j += 1) {
- variable = scope.variables[j];
- definitionsCount += variable.defs.length;
- for (k = 0; k < variable.defs.length; k += 1) {
- definition = variable.defs[k];
- range = definition.name.range;
- tokens.push(
- range[0] + 1,
- range[1] + 1,
- scope.level
- );
- }
- }
- for (j = 0; j < scope.references.length; j += 1) {
- reference = scope.references[j];
- range = reference.identifier.range;
- isDefined = false;
- // Determine if a definition already exists for the range.
- // (escope detects variables twice if they are declared and
- // initialized simultaneously; this filters them.)
- for (k = 0; k < definitionsCount; k += 1) {
- pointer = definitionsIndex + (k * 3);
- if (tokens[pointer] === range[0] + 1 &&
- tokens[pointer + 1] === range[1] + 1) {
- isDefined = true;
- break;
- }
- }
- if (!isDefined) {
- tokens.push(
- // Handle global references too.
- range[0] + 1,
- range[1] + 1,
- reference.resolved ?
reference.resolved.scope.level : 0
- );
- }
- }
- }
- }
- }
-
- return scopes.concat(tokens);
-}
-
-module.exports = scopifier;
diff --git a/libraries/.dir-locals.el b/libraries/.dir-locals.el
deleted file mode 100644
index d0b7cd5..0000000
--- a/libraries/.dir-locals.el
+++ /dev/null
@@ -1,2 +0,0 @@
-((nil . ((sentence-end-double-space . t)
- (buffer-read-only . t))))
diff --git a/libraries/ert-async.el b/libraries/ert-async.el
deleted file mode 100644
index bcff3b0..0000000
--- a/libraries/ert-async.el
+++ /dev/null
@@ -1,89 +0,0 @@
-;;; ert-async.el --- Async support for ERT -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2014 Johan Andersson
-
-;; Author: Johan Andersson <address@hidden>
-;; Maintainer: Johan Andersson <address@hidden>
-;; Version: 0.1.1
-;; Keywords: test
-;; URL: http://github.com/rejeep/ert-async.el
-
-;; This file is NOT part of GNU Emacs.
-
-;;; License:
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
-
-;;; Commentary:
-
-;;; Code:
-
-(require 'ert)
-
-(defvar ert-async-timeout 10
- "Number of seconds to wait for callbacks before failing.")
-
-(defun ert-async-activate-font-lock-keywords ()
- "Activate font-lock keywords for `ert-deftest-async'."
- (font-lock-add-keywords
- nil
- '(("(\\(\\<ert-deftest\\(?:-async\\)?\\)\\>\\s *\\(\\(?:\\sw\\|\\s_\\)+\\)?"
- (1 font-lock-keyword-face nil t)
- (2 font-lock-function-name-face nil t)))))
-
-(defmacro ert-deftest-async (name callbacks &rest body)
- "Like `ert-deftest' but with support for async.
-
-NAME is the name of the test, which is the first argument to
-`ert-deftest'.
-
-CALLBACKS is a list of callback functions that all must be called
-before `ert-async-timeout'. If all callback functions have not
-been called before the timeout, the test fails.
-
-The callback functions should be called without any argument. If
-a callback function is called with a string as argument, the test
-will fail with that error string.
-
-BODY is the actual test."
- (declare (indent 2))
- (let ((varlist
- (cons
- 'callbacked
- (mapcar
- (lambda (callback)
- (list
- callback
- `(lambda (&optional error-message)
- (if error-message
- (ert-fail (format "Callback %s invoked with argument: %s"
',callback error-message))
- (if (member ',callback callbacked)
- (ert-fail (format "Callback %s called multiple times"
',callback))
- (push ',callback callbacked))))))
- callbacks))))
- `(ert-deftest ,name ()
- (let* ,varlist
- (with-timeout
- (ert-async-timeout
- (ert-fail (format "Timeout of %ds exceeded" ert-async-timeout)))
- ,@body
- (while (not (equal (sort (mapcar 'symbol-name callbacked) 'string<)
- (sort (mapcar 'symbol-name ',callbacks)
'string<)))
- (accept-process-output nil 0.05)))))))
-
-(provide 'ert-async)
-
-;;; ert-async.el ends here
diff --git a/libraries/js2-mode.el b/libraries/js2-mode.el
deleted file mode 100644
index c332cfc..0000000
--- a/libraries/js2-mode.el
+++ /dev/null
@@ -1,12331 +0,0 @@
-;;; js2-mode.el --- Improved JavaScript editing mode
-
-;; Copyright (C) 2009, 2011-2014 Free Software Foundation, Inc.
-
-;; Author: Steve Yegge <address@hidden>
-;; mooz <address@hidden>
-;; Dmitry Gutov <address@hidden>
-;; URL: https://github.com/mooz/js2-mode/
-;; http://code.google.com/p/js2-mode/
-;; Version: 20141118
-;; Keywords: languages, javascript
-;; Package-Requires: ((emacs "24.1") (cl-lib "0.5"))
-
-;; This file is part of GNU Emacs.
-
-;; GNU Emacs is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; GNU Emacs is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This JavaScript editing mode supports:
-
-;; - strict recognition of the Ecma-262 language standard
-;; - support for most Rhino and SpiderMonkey extensions from 1.5 and up
-;; - parsing support for ECMAScript for XML (E4X, ECMA-357)
-;; - accurate syntax highlighting using a recursive-descent parser
-;; - on-the-fly reporting of syntax errors and strict-mode warnings
-;; - undeclared-variable warnings using a configurable externs framework
-;; - "bouncing" line indentation to choose among alternate indentation points
-;; - smart line-wrapping within comments and strings
-;; - code folding:
-;; - show some or all function bodies as {...}
-;; - show some or all block comments as /*...*/
-;; - context-sensitive menu bar and popup menus
-;; - code browsing using the `imenu' package
-;; - many customization options
-
-;; Installation:
-;;
-;; To install it as your major mode for JavaScript editing:
-
-;; (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
-
-;; Alternatively, to install it as a minor mode just for JavaScript linting,
-;; you must add it to the appropriate major-mode hook. Normally this would be:
-
-;; (add-hook 'js-mode-hook 'js2-minor-mode)
-
-;; You may also want to hook it in for shell scripts running via node.js:
-
-;; (add-to-list 'interpreter-mode-alist '("node" . js2-mode))
-
-;; To customize how it works:
-;; M-x customize-group RET js2-mode RET
-
-;; Notes:
-
-;; This mode includes a port of Mozilla Rhino's scanner, parser and
-;; symbol table. Ideally it should stay in sync with Rhino, keeping
-;; `js2-mode' current as the EcmaScript language standard evolves.
-
-;; Unlike cc-engine based language modes, js2-mode's line-indentation is not
-;; customizable. It is a surprising amount of work to support customizable
-;; indentation. The current compromise is that the tab key lets you cycle
among
-;; various likely indentation points, similar to the behavior of python-mode.
-
-;; This mode does not yet work with "multi-mode" modes such as `mmm-mode'
-;; and `mumamo', although it could be made to do so with some effort.
-;; This means that `js2-mode' is currently only useful for editing JavaScript
-;; 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 has outdated information and is mostly
-;; unmaintained.
-
-;;; Code:
-
-(require 'cl-lib)
-(require 'imenu)
-(require 'cc-cmds) ; for `c-fill-paragraph'
-
-(eval-and-compile
- (require 'cc-mode) ; (only) for `c-populate-syntax-table'
- (require 'cc-engine)) ; for `c-paragraph-start' et. al.
-
-(defvar electric-layout-rules)
-
-;;; Externs (variables presumed to be defined by the host system)
-
-(defvar js2-ecma-262-externs
- (mapcar 'symbol-name
- '(Array Boolean Date Error EvalError Function Infinity JSON
- Math NaN Number Object RangeError ReferenceError RegExp
- String SyntaxError TypeError URIError
- decodeURI decodeURIComponent encodeURI
- encodeURIComponent escape eval isFinite isNaN
- parseFloat parseInt undefined unescape))
-"Ecma-262 externs. Included in `js2-externs' by default.")
-
-(defvar js2-browser-externs
- (mapcar 'symbol-name
- '(;; DOM level 1
- Attr CDATASection CharacterData Comment DOMException
- DOMImplementation Document DocumentFragment
- DocumentType Element Entity EntityReference
- ExceptionCode NamedNodeMap Node NodeList Notation
- ProcessingInstruction Text
-
- ;; DOM level 2
- HTMLAnchorElement HTMLAppletElement HTMLAreaElement
- HTMLBRElement HTMLBaseElement HTMLBaseFontElement
- HTMLBodyElement HTMLButtonElement HTMLCollection
- HTMLDListElement HTMLDirectoryElement HTMLDivElement
- HTMLDocument HTMLElement HTMLFieldSetElement
- HTMLFontElement HTMLFormElement HTMLFrameElement
- HTMLFrameSetElement HTMLHRElement HTMLHeadElement
- HTMLHeadingElement HTMLHtmlElement HTMLIFrameElement
- HTMLImageElement HTMLInputElement HTMLIsIndexElement
- HTMLLIElement HTMLLabelElement HTMLLegendElement
- HTMLLinkElement HTMLMapElement HTMLMenuElement
- HTMLMetaElement HTMLModElement HTMLOListElement
- HTMLObjectElement HTMLOptGroupElement
- HTMLOptionElement HTMLOptionsCollection
- HTMLParagraphElement HTMLParamElement HTMLPreElement
- HTMLQuoteElement HTMLScriptElement HTMLSelectElement
- HTMLStyleElement HTMLTableCaptionElement
- HTMLTableCellElement HTMLTableColElement
- HTMLTableElement HTMLTableRowElement
- HTMLTableSectionElement HTMLTextAreaElement
- HTMLTitleElement HTMLUListElement
-
- ;; DOM level 3
- DOMConfiguration DOMError DOMException
- DOMImplementationList DOMImplementationSource
- DOMLocator DOMStringList NameList TypeInfo
- UserDataHandler
-
- ;; Window
- window alert confirm document java navigator prompt screen
- self top
-
- ;; W3C CSS
- CSSCharsetRule CSSFontFace CSSFontFaceRule
- CSSImportRule CSSMediaRule CSSPageRule
- CSSPrimitiveValue CSSProperties CSSRule CSSRuleList
- CSSStyleDeclaration CSSStyleRule CSSStyleSheet
- CSSValue CSSValueList Counter DOMImplementationCSS
- DocumentCSS DocumentStyle ElementCSSInlineStyle
- LinkStyle MediaList RGBColor Rect StyleSheet
- StyleSheetList ViewCSS
-
- ;; W3C Event
- EventListener EventTarget Event DocumentEvent UIEvent
- MouseEvent MutationEvent KeyboardEvent
-
- ;; W3C Range
- DocumentRange Range RangeException
-
- ;; W3C XML
- XPathResult XMLHttpRequest
-
- ;; console object. Provided by at least Chrome and Firefox.
- console))
- "Browser externs.
-You can cause these to be included or excluded with the custom
-variable `js2-include-browser-externs'.")
-
-(defvar js2-rhino-externs
- (mapcar 'symbol-name
- '(Packages importClass importPackage com org java
- ;; Global object (shell) externs.
- defineClass deserialize doctest gc help load
- loadClass print quit readFile readUrl runCommand seal
- serialize spawn sync toint32 version))
- "Mozilla Rhino externs.
-Set `js2-include-rhino-externs' to t to include them.")
-
-(defvar js2-node-externs
- (mapcar 'symbol-name
- '(__dirname __filename Buffer clearInterval clearTimeout require
- console exports global module process setInterval setTimeout))
- "Node.js externs.
-Set `js2-include-node-externs' to t to include them.")
-
-(defvar js2-typed-array-externs
- (mapcar 'symbol-name
- '(ArrayBuffer Uint8ClampedArray DataView
- Int8Array Uint8Array Int16Array Uint16Array Int32Array Uint32Array
- Float32Array Float64Array))
- "Khronos typed array externs. Available in most modern browsers and
-in node.js >= 0.6. If `js2-include-node-externs' or
`js2-include-browser-externs'
-are enabled, these will also be included.")
-
-(defvar js2-harmony-externs
- (mapcar 'symbol-name
- '(Map Promise Proxy Reflect Set Symbol WeakMap WeakSet))
- "ES6 externs. If `js2-include-browser-externs' is enabled and
-`js2-language-version' is sufficiently high, these will be included.")
-
-;;; Variables
-
-(defun js2-mark-safe-local (name pred)
- "Make the variable NAME buffer-local and mark it as safe file-local
-variable with predicate PRED."
- (make-variable-buffer-local name)
- (put name 'safe-local-variable pred))
-
-(defcustom js2-highlight-level 2
- "Amount of syntax highlighting to perform.
-0 or a negative value means none.
-1 adds basic syntax highlighting.
-2 adds highlighting of some Ecma built-in properties.
-3 adds highlighting of many Ecma built-in functions."
- :group 'js2-mode
- :type '(choice (const :tag "None" 0)
- (const :tag "Basic" 1)
- (const :tag "Include Properties" 2)
- (const :tag "Include Functions" 3)))
-
-(defvar js2-mode-dev-mode-p nil
- "Non-nil if running in development mode. Normally nil.")
-
-(defgroup js2-mode nil
- "An improved JavaScript mode."
- :group 'languages)
-
-(defcustom js2-basic-offset (if (and (boundp 'c-basic-offset)
- (numberp c-basic-offset))
- c-basic-offset
- 4)
- "Number of spaces to indent nested statements.
-Similar to `c-basic-offset'."
- :group 'js2-mode
- :type 'integer)
-(js2-mark-safe-local 'js2-basic-offset 'integerp)
-
-(defcustom js2-bounce-indent-p nil
- "Non-nil to have indent-line function choose among alternatives.
-If nil, the indent-line function will indent to a predetermined column
-based on heuristic guessing. If non-nil, then if the current line is
-already indented to that predetermined column, indenting will choose
-another likely column and indent to that spot. Repeated invocation of
-the indent-line function will cycle among the computed alternatives.
-See the function `js2-bounce-indent' for details. When it is non-nil,
-js2-mode also binds `js2-bounce-indent-backwards' to Shift-Tab."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-pretty-multiline-declarations t
- "Non-nil to line up multiline declarations vertically:
-
- var a = 10,
- b = 20,
- c = 30;
-
-If the value is not `all', and the first assigned value in
-declaration is a function/array/object literal spanning several
-lines, it won't be indented additionally:
-
- var o = { var bar = 2,
- foo: 3 vs. o = {
- }, foo: 3
- bar = 2; };"
- :group 'js2-mode
- :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."
- :type 'number
- :group 'js2-mode)
-(make-variable-buffer-local 'js2-idle-timer-delay)
-
-(defcustom js2-dynamic-idle-timer-adjust 0
- "Positive to adjust `js2-idle-timer-delay' based on file size.
-The idea is that for short files, parsing is faster so we can be
-more responsive to user edits without interfering with editing.
-The buffer length in characters (typically bytes) is divided by
-this value and used to multiply `js2-idle-timer-delay' for the
-buffer. For example, a 21k file and 10k adjust yields 21k/10k
-== 2, so js2-idle-timer-delay is multiplied by 2.
-If `js2-dynamic-idle-timer-adjust' is 0 or negative,
-`js2-idle-timer-delay' is not dependent on the file size."
- :type 'number
- :group 'js2-mode)
-
-(defcustom js2-concat-multiline-strings t
- "When non-nil, `js2-line-break' in mid-string will make it a
-string concatenation. When `eol', the '+' will be inserted at the
-end of the line, otherwise, at the beginning of the next line."
- :type '(choice (const t) (const eol) (const nil))
- :group 'js2-mode)
-
-(defcustom js2-mode-show-parse-errors t
- "True to highlight parse errors."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-mode-show-strict-warnings t
- "Non-nil to emit Ecma strict-mode warnings.
-Some of the warnings can be individually disabled by other flags,
-even if this flag is non-nil."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-strict-trailing-comma-warning t
- "Non-nil to warn about trailing commas in array literals.
-Ecma-262-5.1 allows them, but older versions of IE raise an error."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-strict-missing-semi-warning t
- "Non-nil to warn about semicolon auto-insertion after statement.
-Technically this is legal per Ecma-262, but some style guides disallow
-depending on it."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-missing-semi-one-line-override nil
- "Non-nil to permit missing semicolons in one-line functions.
-In one-liner functions such as `function identity(x) {return x}'
-people often omit the semicolon for a cleaner look. If you are
-such a person, you can suppress the missing-semicolon warning
-by setting this variable to t."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-strict-inconsistent-return-warning t
- "Non-nil to warn about mixing returns with value-returns.
-It's perfectly legal to have a `return' and a `return foo' in the
-same function, but it's often an indicator of a bug, and it also
-interferes with type inference (in systems that support it.)"
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-strict-cond-assign-warning t
- "Non-nil to warn about expressions like if (a = b).
-This often should have been '==' instead of '='. If the warning
-is enabled, you can suppress it on a per-expression basis by
-parenthesizing the expression, e.g. if ((a = b)) ..."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-strict-var-redeclaration-warning t
- "Non-nil to warn about redeclaring variables in a script or function."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-strict-var-hides-function-arg-warning t
- "Non-nil to warn about a var decl hiding a function argument."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-skip-preprocessor-directives nil
- "Non-nil to treat lines beginning with # as comments.
-Useful for viewing Mozilla JavaScript source code."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-language-version 200
- "Configures what JavaScript language version to recognize.
-Currently versions 150, 160, 170, 180 and 200 are supported,
-corresponding to JavaScript 1.5, 1.6, 1.7, 1.8 and 2.0 (Harmony),
-respectively. In a nutshell, 1.6 adds E4X support, 1.7 adds let,
-yield, and Array comprehensions, and 1.8 adds function closures."
- :type 'integer
- :group 'js2-mode)
-
-(defcustom js2-instanceof-has-side-effects nil
- "If non-nil, treats the instanceof operator as having side effects.
-This is useful for xulrunner apps."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-move-point-on-right-click t
- "Non-nil to move insertion point when you right-click.
-This makes right-click context menu behavior a bit more intuitive,
-since menu operations generally apply to the point. The exception
-is if there is a region selection, in which case the point does -not-
-move, so cut/copy/paste can work properly.
-
-Note that IntelliJ moves the point, and Eclipse leaves it alone,
-so this behavior is customizable."
- :group 'js2-mode
- :type 'boolean)
-
-(defcustom js2-allow-rhino-new-expr-initializer t
- "Non-nil to support a Rhino's experimental syntactic construct.
-
-Rhino supports the ability to follow a `new' expression with an object
-literal, which is used to set additional properties on the new object
-after calling its constructor. Syntax:
-
- new <expr> [ ( arglist ) ] [initializer]
-
-Hence, this expression:
-
- new Object {a: 1, b: 2}
-
-results in an Object with properties a=1 and b=2. This syntax is
-apparently not configurable in Rhino - it's currently always enabled,
-as of Rhino version 1.7R2."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-allow-member-expr-as-function-name nil
- "Non-nil to support experimental Rhino syntax for function names.
-
-Rhino supports an experimental syntax configured via the Rhino Context
-setting `allowMemberExprAsFunctionName'. The experimental syntax is:
-
- function <member-expr> ( [ arg-list ] ) { <body> }
-
-Where member-expr is a non-parenthesized 'member expression', which
-is anything at the grammar level of a new-expression or lower, meaning
-any expression that does not involve infix or unary operators.
-
-When <member-expr> is not a simple identifier, then it is syntactic
-sugar for assigning the anonymous function to the <member-expr>. Hence,
-this code:
-
- function a.b().c[2] (x, y) { ... }
-
-is rewritten as:
-
- a.b().c[2] = function(x, y) {...}
-
-which doesn't seem particularly useful, but Rhino permits it."
- :type 'boolean
- :group 'js2-mode)
-
-;; scanner variables
-
-(defmacro js2-deflocal (name value &optional comment)
- "Define a buffer-local variable NAME with VALUE and COMMENT."
- (declare (debug defvar) (doc-string 3))
- `(progn
- (defvar ,name ,value ,comment)
- (make-variable-buffer-local ',name)))
-
-(defvar js2-EOF_CHAR -1
- "Represents end of stream. Distinct from js2-EOF token type.")
-
-;; I originally used symbols to represent tokens, but Rhino uses
-;; ints and then sets various flag bits in them, so ints it is.
-;; The upshot is that we need a `js2-' prefix in front of each name.
-(defvar js2-ERROR -1)
-(defvar js2-EOF 0)
-(defvar js2-EOL 1)
-(defvar js2-ENTERWITH 2) ; begin interpreter bytecodes
-(defvar js2-LEAVEWITH 3)
-(defvar js2-RETURN 4)
-(defvar js2-GOTO 5)
-(defvar js2-IFEQ 6)
-(defvar js2-IFNE 7)
-(defvar js2-SETNAME 8)
-(defvar js2-BITOR 9)
-(defvar js2-BITXOR 10)
-(defvar js2-BITAND 11)
-(defvar js2-EQ 12)
-(defvar js2-NE 13)
-(defvar js2-LT 14)
-(defvar js2-LE 15)
-(defvar js2-GT 16)
-(defvar js2-GE 17)
-(defvar js2-LSH 18)
-(defvar js2-RSH 19)
-(defvar js2-URSH 20)
-(defvar js2-ADD 21) ; infix plus
-(defvar js2-SUB 22) ; infix minus
-(defvar js2-MUL 23)
-(defvar js2-DIV 24)
-(defvar js2-MOD 25)
-(defvar js2-NOT 26)
-(defvar js2-BITNOT 27)
-(defvar js2-POS 28) ; unary plus
-(defvar js2-NEG 29) ; unary minus
-(defvar js2-NEW 30)
-(defvar js2-DELPROP 31)
-(defvar js2-TYPEOF 32)
-(defvar js2-GETPROP 33)
-(defvar js2-GETPROPNOWARN 34)
-(defvar js2-SETPROP 35)
-(defvar js2-GETELEM 36)
-(defvar js2-SETELEM 37)
-(defvar js2-CALL 38)
-(defvar js2-NAME 39) ; an identifier
-(defvar js2-NUMBER 40)
-(defvar js2-STRING 41)
-(defvar js2-NULL 42)
-(defvar js2-THIS 43)
-(defvar js2-FALSE 44)
-(defvar js2-TRUE 45)
-(defvar js2-SHEQ 46) ; shallow equality (===)
-(defvar js2-SHNE 47) ; shallow inequality (!==)
-(defvar js2-REGEXP 48)
-(defvar js2-BINDNAME 49)
-(defvar js2-THROW 50)
-(defvar js2-RETHROW 51) ; rethrow caught exception: catch (e if ) uses
it
-(defvar js2-IN 52)
-(defvar js2-INSTANCEOF 53)
-(defvar js2-LOCAL_LOAD 54)
-(defvar js2-GETVAR 55)
-(defvar js2-SETVAR 56)
-(defvar js2-CATCH_SCOPE 57)
-(defvar js2-ENUM_INIT_KEYS 58) ; FIXME: what are these?
-(defvar js2-ENUM_INIT_VALUES 59)
-(defvar js2-ENUM_INIT_ARRAY 60)
-(defvar js2-ENUM_NEXT 61)
-(defvar js2-ENUM_ID 62)
-(defvar js2-THISFN 63)
-(defvar js2-RETURN_RESULT 64) ; to return previously stored return result
-(defvar js2-ARRAYLIT 65) ; array literal
-(defvar js2-OBJECTLIT 66) ; object literal
-(defvar js2-GET_REF 67) ; *reference
-(defvar js2-SET_REF 68) ; *reference = something
-(defvar js2-DEL_REF 69) ; delete reference
-(defvar js2-REF_CALL 70) ; f(args) = something or f(args)++
-(defvar js2-REF_SPECIAL 71) ; reference for special properties like __proto
-(defvar js2-YIELD 72) ; JS 1.7 yield pseudo keyword
-
-;; XML support
-(defvar js2-DEFAULTNAMESPACE 73)
-(defvar js2-ESCXMLATTR 74)
-(defvar js2-ESCXMLTEXT 75)
-(defvar js2-REF_MEMBER 76) ; Reference for address@hidden, x..y etc.
-(defvar js2-REF_NS_MEMBER 77) ; Reference for x.ns::y, x..ns::y etc.
-(defvar js2-REF_NAME 78) ; Reference for @y, @[y] etc.
-(defvar js2-REF_NS_NAME 79) ; Reference for ns::y, @ns::address@hidden etc.
-
-(defvar js2-first-bytecode js2-ENTERWITH)
-(defvar js2-last-bytecode js2-REF_NS_NAME)
-
-(defvar js2-TRY 80)
-(defvar js2-SEMI 81) ; semicolon
-(defvar js2-LB 82) ; left and right brackets
-(defvar js2-RB 83)
-(defvar js2-LC 84) ; left and right curly-braces
-(defvar js2-RC 85)
-(defvar js2-LP 86) ; left and right parens
-(defvar js2-RP 87)
-(defvar js2-COMMA 88) ; comma operator
-
-(defvar js2-ASSIGN 89) ; simple assignment (=)
-(defvar js2-ASSIGN_BITOR 90) ; |=
-(defvar js2-ASSIGN_BITXOR 91) ; ^=
-(defvar js2-ASSIGN_BITAND 92) ; &=
-(defvar js2-ASSIGN_LSH 93) ; <<=
-(defvar js2-ASSIGN_RSH 94) ; >>=
-(defvar js2-ASSIGN_URSH 95) ; >>>=
-(defvar js2-ASSIGN_ADD 96) ; +=
-(defvar js2-ASSIGN_SUB 97) ; -=
-(defvar js2-ASSIGN_MUL 98) ; *=
-(defvar js2-ASSIGN_DIV 99) ; /=
-(defvar js2-ASSIGN_MOD 100) ; %=
-
-(defvar js2-first-assign js2-ASSIGN)
-(defvar js2-last-assign js2-ASSIGN_MOD)
-
-(defvar js2-HOOK 101) ; conditional (?:)
-(defvar js2-COLON 102)
-(defvar js2-OR 103) ; logical or (||)
-(defvar js2-AND 104) ; logical and (&&)
-(defvar js2-INC 105) ; increment/decrement (++ --)
-(defvar js2-DEC 106)
-(defvar js2-DOT 107) ; member operator (.)
-(defvar js2-FUNCTION 108) ; function keyword
-(defvar js2-EXPORT 109) ; export keyword
-(defvar js2-IMPORT 110) ; import keyword
-(defvar js2-IF 111) ; if keyword
-(defvar js2-ELSE 112) ; else keyword
-(defvar js2-SWITCH 113) ; switch keyword
-(defvar js2-CASE 114) ; case keyword
-(defvar js2-DEFAULT 115) ; default keyword
-(defvar js2-WHILE 116) ; while keyword
-(defvar js2-DO 117) ; do keyword
-(defvar js2-FOR 118) ; for keyword
-(defvar js2-BREAK 119) ; break keyword
-(defvar js2-CONTINUE 120) ; continue keyword
-(defvar js2-VAR 121) ; var keyword
-(defvar js2-WITH 122) ; with keyword
-(defvar js2-CATCH 123) ; catch keyword
-(defvar js2-FINALLY 124) ; finally keyword
-(defvar js2-VOID 125) ; void keyword
-(defvar js2-RESERVED 126) ; reserved keywords
-
-(defvar js2-EMPTY 127)
-
-;; Types used for the parse tree - never returned by scanner.
-
-(defvar js2-BLOCK 128) ; statement block
-(defvar js2-LABEL 129) ; label
-(defvar js2-TARGET 130)
-(defvar js2-LOOP 131)
-(defvar js2-EXPR_VOID 132) ; expression statement in functions
-(defvar js2-EXPR_RESULT 133) ; expression statement in scripts
-(defvar js2-JSR 134)
-(defvar js2-SCRIPT 135) ; top-level node for entire script
-(defvar js2-TYPEOFNAME 136) ; for typeof(simple-name)
-(defvar js2-USE_STACK 137)
-(defvar js2-SETPROP_OP 138) ; x.y op= something
-(defvar js2-SETELEM_OP 139) ; x[y] op= something
-(defvar js2-LOCAL_BLOCK 140)
-(defvar js2-SET_REF_OP 141) ; *reference op= something
-
-;; For XML support:
-(defvar js2-DOTDOT 142) ; member operator (..)
-(defvar js2-COLONCOLON 143) ; namespace::name
-(defvar js2-XML 144) ; XML type
-(defvar js2-DOTQUERY 145) ; .() -- e.g., x.emps.emp.(name == "terry")
-(defvar js2-XMLATTR 146) ; @
-(defvar js2-XMLEND 147)
-
-;; Optimizer-only tokens
-(defvar js2-TO_OBJECT 148)
-(defvar js2-TO_DOUBLE 149)
-
-(defvar js2-GET 150) ; JS 1.5 get pseudo keyword
-(defvar js2-SET 151) ; JS 1.5 set pseudo keyword
-(defvar js2-LET 152) ; JS 1.7 let pseudo keyword
-(defvar js2-CONST 153)
-(defvar js2-SETCONST 154)
-(defvar js2-SETCONSTVAR 155)
-(defvar js2-ARRAYCOMP 156)
-(defvar js2-LETEXPR 157)
-(defvar js2-WITHEXPR 158)
-(defvar js2-DEBUGGER 159)
-
-(defvar js2-COMMENT 160)
-(defvar js2-TRIPLEDOT 161) ; for rest parameter
-(defvar js2-ARROW 162) ; function arrow (=>)
-(defvar js2-CLASS 163)
-(defvar js2-EXTENDS 164)
-(defvar js2-STATIC 165)
-(defvar js2-SUPER 166)
-(defvar js2-TEMPLATE_HEAD 167) ; part of template literal before
substitution
-(defvar js2-NO_SUBS_TEMPLATE 168) ; template literal without substitutions
-(defvar js2-TAGGED_TEMPLATE 169) ; tagged template literal
-
-(defconst js2-num-tokens (1+ js2-TAGGED_TEMPLATE))
-
-(defconst js2-debug-print-trees nil)
-
-;; Rhino accepts any string or stream as input. Emacs character
-;; processing works best in buffers, so we'll assume the input is a
-;; buffer. JavaScript strings can be copied into temp buffers before
-;; scanning them.
-
-;; Buffer-local variables yield much cleaner code than using `defstruct'.
-;; They're the Emacs equivalent of instance variables, more or less.
-
-(js2-deflocal js2-ts-dirty-line nil
- "Token stream buffer-local variable.
-Indicates stuff other than whitespace since start of line.")
-
-(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.")
-
-(js2-deflocal js2-ts-cursor 1 ; emacs buffers are 1-indexed
- "Token stream buffer-local variable.
-Current scan position.")
-
-;; FIXME: Unused.
-(js2-deflocal js2-ts-is-xml-attribute nil
- "Token stream buffer-local variable.")
-
-(js2-deflocal js2-ts-xml-is-tag-content nil
- "Token stream buffer-local variable.")
-
-(js2-deflocal js2-ts-xml-open-tags-count 0
- "Token stream buffer-local variable.")
-
-(js2-deflocal js2-ts-string-buffer nil
- "Token stream buffer-local variable.
-List of chars built up while scanning various tokens.")
-
-(cl-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)
-
-;; 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)
-
-(cl-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
-
-(js2-deflocal js2-parsed-errors nil
- "List of errors produced during scanning/parsing.")
-
-(js2-deflocal js2-parsed-warnings nil
- "List of warnings produced during scanning/parsing.")
-
-(js2-deflocal js2-recover-from-parse-errors t
- "Non-nil to continue parsing after a syntax error.
-
-In recovery mode, the AST will be built in full, and any error
-nodes will be flagged with appropriate error information. If
-this flag is nil, a syntax error will result in an error being
-signaled.
-
-The variable is automatically buffer-local, because different
-modes that use the parser will need different settings.")
-
-(js2-deflocal js2-parse-hook nil
- "List of callbacks for receiving parsing progress.")
-
-(defvar js2-parse-finished-hook nil
- "List of callbacks to notify when parsing finishes.
-Not called if parsing was interrupted.")
-
-(js2-deflocal js2-is-eval-code nil
- "True if we're evaluating code in a string.
-If non-nil, the tokenizer will record the token text, and the AST nodes
-will record their source text. Off by default for IDE modes, since the
-text is available in the buffer.")
-
-(defvar js2-parse-ide-mode t
- "Non-nil if the parser is being used for `js2-mode'.
-If non-nil, the parser will set text properties for fontification
-and the syntax table. The value should be nil when using the
-parser as a frontend to an interpreter or byte compiler.")
-
-;;; Parser instance variables (buffer-local vars for js2-parse)
-
-(defconst js2-ti-after-eol (lsh 1 16)
- "Flag: first token of the source line.")
-
-;; Inline Rhino's CompilerEnvirons vars as buffer-locals.
-
-(js2-deflocal js2-compiler-generate-debug-info t)
-(js2-deflocal js2-compiler-use-dynamic-scope nil)
-(js2-deflocal js2-compiler-reserved-keywords-as-identifier nil)
-(js2-deflocal js2-compiler-xml-available t)
-(js2-deflocal js2-compiler-optimization-level 0)
-(js2-deflocal js2-compiler-generating-source t)
-(js2-deflocal js2-compiler-strict-mode nil)
-(js2-deflocal js2-compiler-report-warning-as-error nil)
-(js2-deflocal js2-compiler-generate-observer-count nil)
-(js2-deflocal js2-compiler-activation-names nil)
-
-;; SKIP: sourceURI
-
-;; There's a compileFunction method in Context.java - may need it.
-(js2-deflocal js2-called-by-compile-function nil
- "True if `js2-parse' was called by `js2-compile-function'.
-Will only be used when we finish implementing the interpreter.")
-
-;; SKIP: ts (we just call `js2-init-scanner' and use its vars)
-
-;; SKIP: node factory - we're going to just call functions directly,
-;; and eventually go to a unified AST format.
-
-(js2-deflocal js2-nesting-of-function 0)
-
-(js2-deflocal js2-recorded-identifiers nil
- "Tracks identifiers found during parsing.")
-
-(js2-deflocal js2-is-in-destructuring nil
- "True while parsing destructuring expression.")
-
-(defcustom js2-global-externs nil
- "A list of any extern names you'd like to consider always declared.
-This list is global and is used by all `js2-mode' files.
-You can create buffer-local externs list using `js2-additional-externs'.
-
-There is also a buffer-local variable `js2-default-externs',
-which is initialized by default to include the Ecma-262 externs
-and the standard browser externs. The three lists are all
-checked during highlighting."
- :type 'list
- :group 'js2-mode)
-
-(js2-deflocal js2-default-externs nil
- "Default external declarations.
-
-These are currently only used for highlighting undeclared variables,
-which only worries about top-level (unqualified) references.
-As js2-mode's processing improves, we will flesh out this list.
-
-The initial value is set to `js2-ecma-262-externs', unless some
-of the `js2-include-?-externs' variables are set to t, in which
-case the browser, Rhino and/or Node.js externs are also included.
-
-See `js2-additional-externs' for more information.")
-
-(defcustom js2-include-browser-externs t
- "Non-nil to include browser externs in the master externs list.
-If you work on JavaScript files that are not intended for browsers,
-such as Mozilla Rhino server-side JavaScript, set this to nil.
-See `js2-additional-externs' for more information about externs."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-include-rhino-externs nil
- "Non-nil to include Mozilla Rhino externs in the master externs list.
-See `js2-additional-externs' for more information about externs."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-include-node-externs nil
- "Non-nil to include Node.js externs in the master externs list.
-See `js2-additional-externs' for more information about externs."
- :type 'boolean
- :group 'js2-mode)
-
-(js2-deflocal js2-additional-externs nil
- "A buffer-local list of additional external declarations.
-It is used to decide whether variables are considered undeclared
-for purposes of highlighting.
-
-Each entry is a Lisp string. The string should be the fully qualified
-name of an external entity. All externs should be added to this list,
-so that as js2-mode's processing improves it can take advantage of them.
-
-You may want to declare your externs in three ways.
-First, you can add externs that are valid for all your JavaScript files.
-You should probably do this by adding them to `js2-global-externs', which
-is a global list used for all js2-mode files.
-
-Next, you can add a function to `js2-init-hook' that adds additional
-externs appropriate for the specific file, perhaps based on its path.
-These should go in `js2-additional-externs', which is buffer-local.
-
-Third, you can use JSLint's global declaration, as long as
-`js2-include-jslint-globals' is non-nil, which see.
-
-Finally, you can add a function to `js2-post-parse-callbacks',
-which is called after parsing completes, and `js2-mode-ast' is bound to
-the root of the parse tree. At this stage you can set up an AST
-node visitor using `js2-visit-ast' and examine the parse tree
-for specific import patterns that may imply the existence of
-other externs, possibly tied to your build system. These should also
-be added to `js2-additional-externs'.
-
-Your post-parse callback may of course also use the simpler and
-faster (but perhaps less robust) approach of simply scanning the
-buffer text for your imports, using regular expressions.")
-
-;; SKIP: decompiler
-;; SKIP: encoded-source
-
-;;; The following variables are per-function and should be saved/restored
-;;; during function parsing...
-
-(js2-deflocal js2-current-script-or-fn nil)
-(js2-deflocal js2-current-scope nil)
-(js2-deflocal js2-nesting-of-with 0)
-(js2-deflocal js2-label-set nil
- "An alist mapping label names to nodes.")
-
-(js2-deflocal js2-loop-set nil)
-(js2-deflocal js2-loop-and-switch-set nil)
-(js2-deflocal js2-has-return-value nil)
-(js2-deflocal js2-end-flags 0)
-
-;;; ...end of per function variables
-
-;; 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.
-;;
-;; END_UNREACHED is reserved for code paths that are assumed to always be
-;; able to execute (example: throw, continue)
-;;
-;; END_DROPS_OFF indicates if the statement can transfer control to the
-;; next one. Statement such as return dont. A compound statement may have
-;; some branch that drops off control to the next statement.
-;;
-;; END_RETURNS indicates that the statement can return (without arguments)
-;; END_RETURNS_VALUE indicates that the statement can return a value.
-;;
-;; A compound statement such as
-;; if (condition) {
-;; return value;
-;; }
-;; Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
-
-(defconst js2-end-unreached #x0)
-(defconst js2-end-drops-off #x1)
-(defconst js2-end-returns #x2)
-(defconst js2-end-returns-value #x4)
-
-;; Rhino awkwardly passes a statementLabel parameter to the
-;; statementHelper() function, the main statement parser, which
-;; is then used by quite a few of the sub-parsers. We just make
-;; it a buffer-local variable and make sure it's cleaned up properly.
-(js2-deflocal js2-labeled-stmt nil) ; type `js2-labeled-stmt-node'
-
-;; Similarly, Rhino passes an inForInit boolean through about half
-;; the expression parsers. We use a dynamically-scoped variable,
-;; which makes it easier to funcall the parsers individually without
-;; worrying about whether they take the parameter or not.
-(js2-deflocal js2-in-for-init nil)
-(js2-deflocal js2-temp-name-counter 0)
-(js2-deflocal js2-parse-stmt-count 0)
-
-(defsubst js2-get-next-temp-name ()
- (format "$%d" (cl-incf js2-temp-name-counter)))
-
-(defvar js2-parse-interruptable-p t
- "Set this to nil to force parse to continue until finished.
-This will mostly be useful for interpreters.")
-
-(defvar js2-statements-per-pause 50
- "Pause after this many statements to check for user input.
-If user input is pending, stop the parse and discard the tree.
-This makes for a smoother user experience for large files.
-You may have to wait a second or two before the highlighting
-and error-reporting appear, but you can always type ahead if
-you wish. This appears to be more or less how Eclipse, IntelliJ
-and other editors work.")
-
-(js2-deflocal js2-record-comments t
- "Instructs the scanner to record comments in `js2-scanned-comments'.")
-
-(js2-deflocal js2-scanned-comments nil
- "List of all comments from the current parse.")
-
-(defcustom js2-mode-indent-inhibit-undo nil
- "Non-nil to disable collection of Undo information when indenting lines.
-Some users have requested this behavior. It's nil by default because
-other Emacs modes don't work this way."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-mode-indent-ignore-first-tab nil
- "If non-nil, ignore first TAB keypress if we look indented properly.
-It's fairly common for users to navigate to an already-indented line
-and press TAB for reassurance that it's been indented. For this class
-of users, we want the first TAB press on a line to be ignored if the
-line is already indented to one of the precomputed alternatives.
-
-This behavior is only partly implemented. If you TAB-indent a line,
-navigate to another line, and then navigate back, it fails to clear
-the last-indented variable, so it thinks you've already hit TAB once,
-and performs the indent. A full solution would involve getting on the
-point-motion hooks for the entire buffer. If we come across another
-use cases that requires watching point motion, I'll consider doing it.
-
-If you set this variable to nil, then the TAB key will always change
-the indentation of the current line, if more than one alternative
-indentation spot exists."
- :type 'boolean
- :group 'js2-mode)
-
-(defvar js2-indent-hook nil
- "A hook for user-defined indentation rules.
-
-Functions on this hook should expect two arguments: (LIST INDEX)
-The LIST argument is the list of computed indentation points for
-the current line. INDEX is the list index of the indentation point
-that `js2-bounce-indent' plans to use. If INDEX is nil, then the
-indent function is not going to change the current line indentation.
-
-If a hook function on this list returns a non-nil value, then
-`js2-bounce-indent' assumes the hook function has performed its own
-indentation, and will do nothing. If all hook functions on the list
-return nil, then `js2-bounce-indent' will use its computed indentation
-and reindent the line.
-
-When hook functions on this hook list are called, the variable
-`js2-mode-ast' may or may not be set, depending on whether the
-parse tree is available. If the variable is nil, you can pass a
-callback to `js2-mode-wait-for-parse', and your callback will be
-called after the new parse tree is built. This can take some time
-in large files.")
-
-(defface js2-warning
- `((((class color) (background light))
- (:underline "orange"))
- (((class color) (background dark))
- (:underline "orange"))
- (t (:underline t)))
- "Face for JavaScript warnings."
- :group 'js2-mode)
-
-(defface js2-error
- `((((class color) (background light))
- (:foreground "red"))
- (((class color) (background dark))
- (:foreground "red"))
- (t (:foreground "red")))
- "Face for JavaScript errors."
- :group 'js2-mode)
-
-(defface js2-jsdoc-tag
- '((t :foreground "SlateGray"))
- "Face used to highlight @whatever tags in jsdoc comments."
- :group 'js2-mode)
-
-(defface js2-jsdoc-type
- '((t :foreground "SteelBlue"))
- "Face used to highlight {FooBar} types in jsdoc comments."
- :group 'js2-mode)
-
-(defface js2-jsdoc-value
- '((t :foreground "PeachPuff3"))
- "Face used to highlight tag values in jsdoc comments."
- :group 'js2-mode)
-
-(defface js2-function-param
- '((t :foreground "SeaGreen"))
- "Face used to highlight function parameters in javascript."
- :group 'js2-mode)
-
-(defface js2-function-call
- '((t :inherit default))
- "Face used to highlight function name in calls."
- :group 'js2-mode)
-
-(defface js2-instance-member
- '((t :foreground "DarkOrchid"))
- "Face used to highlight instance variables in javascript.
-Not currently used."
- :group 'js2-mode)
-
-(defface js2-private-member
- '((t :foreground "PeachPuff3"))
- "Face used to highlight calls to private methods in javascript.
-Not currently used."
- :group 'js2-mode)
-
-(defface js2-private-function-call
- '((t :foreground "goldenrod"))
- "Face used to highlight calls to private functions in javascript.
-Not currently used."
- :group 'js2-mode)
-
-(defface js2-jsdoc-html-tag-name
- '((((class color) (min-colors 88) (background light))
- (:foreground "rosybrown"))
- (((class color) (min-colors 8) (background dark))
- (:foreground "yellow"))
- (((class color) (min-colors 8) (background light))
- (:foreground "magenta")))
- "Face used to highlight jsdoc html tag names"
- :group 'js2-mode)
-
-(defface js2-jsdoc-html-tag-delimiter
- '((((class color) (min-colors 88) (background light))
- (:foreground "dark khaki"))
- (((class color) (min-colors 8) (background dark))
- (:foreground "green"))
- (((class color) (min-colors 8) (background light))
- (:foreground "green")))
- "Face used to highlight brackets in jsdoc html tags."
- :group 'js2-mode)
-
-(defface js2-external-variable
- '((t :foreground "orange"))
- "Face used to highlight undeclared variable identifiers.")
-
-(defcustom js2-init-hook nil
- "List of functions to be called after `js2-mode' or
-`js2-minor-mode' has initialized all variables, before parsing
-the buffer for the first time."
- :type 'hook
- :group 'js2-mode
- :version "20130608")
-
-(defcustom js2-post-parse-callbacks nil
- "List of callback functions invoked after parsing finishes.
-Currently, the main use for this function is to add synthetic
-declarations to `js2-recorded-identifiers', which see."
- :type 'hook
- :group 'js2-mode)
-
-(defcustom js2-build-imenu-callbacks nil
- "List of functions called during Imenu index generation.
-It's a good place to add additional entries to it, using
-`js2-record-imenu-entry'."
- :type 'hook
- :group 'js2-mode)
-
-(defcustom js2-highlight-external-variables t
- "Non-nil to highlight undeclared variable identifiers.
-An undeclared variable is any variable not declared with var or let
-in the current scope or any lexically enclosing scope. If you use
-such a variable, then you are either expecting it to originate from
-another file, or you've got a potential bug."
- :type 'boolean
- :group 'js2-mode)
-
-(defcustom js2-include-jslint-globals t
- "Non-nil to include the identifiers from JSLint global
-declaration (see http://www.jslint.com/lint.html#global) in the
-buffer-local externs list. See `js2-additional-externs' for more
-information."
- :type 'boolean
- :group 'js2-mode)
-
-(defvar js2-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map [mouse-1] #'js2-mode-show-node)
- (define-key map (kbd "M-j") #'js2-line-break)
- (define-key map (kbd "C-c C-e") #'js2-mode-hide-element)
- (define-key map (kbd "C-c C-s") #'js2-mode-show-element)
- (define-key map (kbd "C-c C-a") #'js2-mode-show-all)
- (define-key map (kbd "C-c C-f") #'js2-mode-toggle-hide-functions)
- (define-key map (kbd "C-c C-t") #'js2-mode-toggle-hide-comments)
- (define-key map (kbd "C-c C-o") #'js2-mode-toggle-element)
- (define-key map (kbd "C-c C-w") #'js2-mode-toggle-warnings-and-errors)
- (define-key map [down-mouse-3] #'js2-down-mouse-3)
- (when js2-bounce-indent-p
- (define-key map (kbd "<backtab>") #'js2-indent-bounce-backwards))
-
- (define-key map [menu-bar javascript]
- (cons "JavaScript" (make-sparse-keymap "JavaScript")))
-
- (define-key map [menu-bar javascript customize-js2-mode]
- '(menu-item "Customize js2-mode" js2-mode-customize
- :help "Customize the behavior of this mode"))
-
- (define-key map [menu-bar javascript js2-force-refresh]
- '(menu-item "Force buffer refresh" js2-mode-reset
- :help "Re-parse the buffer from scratch"))
-
- (define-key map [menu-bar javascript separator-2]
- '("--"))
-
- (define-key map [menu-bar javascript next-error]
- '(menu-item "Next warning or error" next-error
- :enabled (and js2-mode-ast
- (or (js2-ast-root-errors js2-mode-ast)
- (js2-ast-root-warnings js2-mode-ast)))
- :help "Move to next warning or error"))
-
- (define-key map [menu-bar javascript display-errors]
- '(menu-item "Show errors and warnings"
js2-mode-display-warnings-and-errors
- :visible (not js2-mode-show-parse-errors)
- :help "Turn on display of warnings and errors"))
-
- (define-key map [menu-bar javascript hide-errors]
- '(menu-item "Hide errors and warnings" js2-mode-hide-warnings-and-errors
- :visible js2-mode-show-parse-errors
- :help "Turn off display of warnings and errors"))
-
- (define-key map [menu-bar javascript separator-1]
- '("--"))
-
- (define-key map [menu-bar javascript js2-toggle-function]
- '(menu-item "Show/collapse element" js2-mode-toggle-element
- :help "Hide or show function body or comment"))
-
- (define-key map [menu-bar javascript show-comments]
- '(menu-item "Show block comments" js2-mode-toggle-hide-comments
- :visible js2-mode-comments-hidden
- :help "Expand all hidden block comments"))
-
- (define-key map [menu-bar javascript hide-comments]
- '(menu-item "Hide block comments" js2-mode-toggle-hide-comments
- :visible (not js2-mode-comments-hidden)
- :help "Show block comments as /*...*/"))
-
- (define-key map [menu-bar javascript show-all-functions]
- '(menu-item "Show function bodies" js2-mode-toggle-hide-functions
- :visible js2-mode-functions-hidden
- :help "Expand all hidden function bodies"))
-
- (define-key map [menu-bar javascript hide-all-functions]
- '(menu-item "Hide function bodies" js2-mode-toggle-hide-functions
- :visible (not js2-mode-functions-hidden)
- :help "Show {...} for all top-level function bodies"))
-
- map)
- "Keymap used in `js2-mode' buffers.")
-
-(defconst js2-mode-identifier-re "[[:alpha:]_$][[:alnum:]_$]*")
-
-(defvar js2-mode-//-comment-re "^\\(\\s-*\\)//.+"
- "Matches a //-comment line. Must be first non-whitespace on line.
-First match-group is the leading whitespace.")
-
-(defvar js2-mode-hook nil)
-
-(js2-deflocal js2-mode-ast nil "Private variable.")
-(js2-deflocal js2-mode-parse-timer nil "Private variable.")
-(js2-deflocal js2-mode-buffer-dirty-p nil "Private variable.")
-(js2-deflocal js2-mode-parsing nil "Private variable.")
-(js2-deflocal js2-mode-node-overlay nil)
-
-(defvar js2-mode-show-overlay js2-mode-dev-mode-p
- "Debug: Non-nil to highlight AST nodes on mouse-down.")
-
-(js2-deflocal js2-mode-fontifications nil "Private variable")
-(js2-deflocal js2-mode-deferred-properties nil "Private variable")
-(js2-deflocal js2-imenu-recorder nil "Private variable")
-(js2-deflocal js2-imenu-function-map nil "Private variable")
-
-(defvar js2-paragraph-start
- "\\(@[[:alpha:]]+\\>\\|$\\)")
-
-;; Note that we also set a 'c-in-sws text property in html comments,
-;; so that `c-forward-sws' and `c-backward-sws' work properly.
-(defvar js2-syntactic-ws-start
- "\\s \\|/[*/]\\|[\n\r]\\|\\\\[\n\r]\\|\\s!\\|<!--\\|^\\s-*-->")
-
-(defvar js2-syntactic-ws-end
- "\\s \\|[\n\r/]\\|\\s!")
-
-(defvar js2-syntactic-eol
- (concat "\\s *\\(/\\*[^*\n\r]*"
- "\\(\\*+[^*\n\r/][^*\n\r]*\\)*"
- "\\*+/\\s *\\)*"
- "\\(//\\|/\\*[^*\n\r]*"
- "\\(\\*+[^*\n\r/][^*\n\r]*\\)*$"
- "\\|\\\\$\\|$\\)")
- "Copied from `java-mode'. Needed for some cc-engine functions.")
-
-(defvar js2-comment-prefix-regexp
- "//+\\|\\**")
-
-(defvar js2-comment-start-skip
- "\\(//+\\|/\\*+\\)\\s *")
-
-(defvar js2-mode-verbose-parse-p js2-mode-dev-mode-p
- "Non-nil to emit status messages during parsing.")
-
-(defvar js2-mode-functions-hidden nil "Private variable.")
-(defvar js2-mode-comments-hidden nil "Private variable.")
-
-(defvar js2-mode-syntax-table
- (let ((table (make-syntax-table)))
- (c-populate-syntax-table table)
- table)
- "Syntax table used in `js2-mode' buffers.")
-
-(defvar js2-mode-abbrev-table nil
- "Abbrev table in use in `js2-mode' buffers.")
-(define-abbrev-table 'js2-mode-abbrev-table ())
-
-(defvar js2-mode-pending-parse-callbacks nil
- "List of functions waiting to be notified that parse is finished.")
-
-(defvar js2-mode-last-indented-line -1)
-
-;;; Localizable error and warning messages
-
-;; Messages are copied from Rhino's Messages.properties.
-;; Many of the Java-specific messages have been elided.
-;; Add any js2-specific ones at the end, so we can keep
-;; this file synced with changes to Rhino's.
-
-(defvar js2-message-table
- (make-hash-table :test 'equal :size 250)
- "Contains localized messages for `js2-mode'.")
-
-;; TODO(stevey): construct this table at compile-time.
-(defmacro js2-msg (key &rest strings)
- `(puthash ,key (concat ,@strings)
- js2-message-table))
-
-(defun js2-get-msg (msg-key)
- "Look up a localized message.
-MSG-KEY is a list of (MSG ARGS). If the message takes parameters,
-the correct number of ARGS must be provided."
- (let* ((key (if (listp msg-key) (car msg-key) msg-key))
- (args (if (listp msg-key) (cdr msg-key)))
- (msg (gethash key js2-message-table)))
- (if msg
- (apply #'format msg args)
- key))) ; default to showing the key
-
-(js2-msg "msg.dup.parms"
- "Duplicate parameter name '%s'.")
-
-(js2-msg "msg.too.big.jump"
- "Program too complex: jump offset too big.")
-
-(js2-msg "msg.too.big.index"
- "Program too complex: internal index exceeds 64K limit.")
-
-(js2-msg "msg.while.compiling.fn"
- "Encountered code generation error while compiling function '%s': %s")
-
-(js2-msg "msg.while.compiling.script"
- "Encountered code generation error while compiling script: %s")
-
-;; Context
-(js2-msg "msg.ctor.not.found"
- "Constructor for '%s' not found.")
-
-(js2-msg "msg.not.ctor"
- "'%s' is not a constructor.")
-
-;; FunctionObject
-(js2-msg "msg.varargs.ctor"
- "Method or constructor '%s' must be static "
- "with the signature (Context cx, Object[] args, "
- "Function ctorObj, boolean inNewExpr) "
- "to define a variable arguments constructor.")
-
-(js2-msg "msg.varargs.fun"
- "Method '%s' must be static with the signature "
- "(Context cx, Scriptable thisObj, Object[] args, Function funObj) "
- "to define a variable arguments function.")
-
-(js2-msg "msg.incompat.call"
- "Method '%s' called on incompatible object.")
-
-(js2-msg "msg.bad.parms"
- "Unsupported parameter type '%s' in method '%s'.")
-
-(js2-msg "msg.bad.method.return"
- "Unsupported return type '%s' in method '%s'.")
-
-(js2-msg "msg.bad.ctor.return"
- "Construction of objects of type '%s' is not supported.")
-
-(js2-msg "msg.no.overload"
- "Method '%s' occurs multiple times in class '%s'.")
-
-(js2-msg "msg.method.not.found"
- "Method '%s' not found in '%s'.")
-
-;; IRFactory
-
-(js2-msg "msg.bad.for.in.lhs"
- "Invalid left-hand side of for..in loop.")
-
-(js2-msg "msg.mult.index"
- "Only one variable allowed in for..in loop.")
-
-(js2-msg "msg.bad.for.in.destruct"
- "Left hand side of for..in loop must be an array of "
- "length 2 to accept key/value pair.")
-
-(js2-msg "msg.cant.convert"
- "Can't convert to type '%s'.")
-
-(js2-msg "msg.bad.assign.left"
- "Invalid assignment left-hand side.")
-
-(js2-msg "msg.bad.decr"
- "Invalid decerement operand.")
-
-(js2-msg "msg.bad.incr"
- "Invalid increment operand.")
-
-(js2-msg "msg.bad.yield"
- "yield must be in a function.")
-
-(js2-msg "msg.yield.parenthesized"
- "yield expression must be parenthesized.")
-
-;; NativeGlobal
-(js2-msg "msg.cant.call.indirect"
- "Function '%s' must be called directly, and not by way of a "
- "function of another name.")
-
-(js2-msg "msg.eval.nonstring"
- "Calling eval() with anything other than a primitive "
- "string value will simply return the value. "
- "Is this what you intended?")
-
-(js2-msg "msg.eval.nonstring.strict"
- "Calling eval() with anything other than a primitive "
- "string value is not allowed in strict mode.")
-
-(js2-msg "msg.bad.destruct.op"
- "Invalid destructuring assignment operator")
-
-;; NativeCall
-(js2-msg "msg.only.from.new"
- "'%s' may only be invoked from a `new' expression.")
-
-(js2-msg "msg.deprec.ctor"
- "The '%s' constructor is deprecated.")
-
-;; NativeFunction
-(js2-msg "msg.no.function.ref.found"
- "no source found to decompile function reference %s")
-
-(js2-msg "msg.arg.isnt.array"
- "second argument to Function.prototype.apply must be an array")
-
-;; NativeGlobal
-(js2-msg "msg.bad.esc.mask"
- "invalid string escape mask")
-
-;; NativeRegExp
-(js2-msg "msg.bad.quant"
- "Invalid quantifier %s")
-
-(js2-msg "msg.overlarge.backref"
- "Overly large back reference %s")
-
-(js2-msg "msg.overlarge.min"
- "Overly large minimum %s")
-
-(js2-msg "msg.overlarge.max"
- "Overly large maximum %s")
-
-(js2-msg "msg.zero.quant"
- "Zero quantifier %s")
-
-(js2-msg "msg.max.lt.min"
- "Maximum %s less than minimum")
-
-(js2-msg "msg.unterm.quant"
- "Unterminated quantifier %s")
-
-(js2-msg "msg.unterm.paren"
- "Unterminated parenthetical %s")
-
-(js2-msg "msg.unterm.class"
- "Unterminated character class %s")
-
-(js2-msg "msg.bad.range"
- "Invalid range in character class.")
-
-(js2-msg "msg.trail.backslash"
- "Trailing \\ in regular expression.")
-
-(js2-msg "msg.re.unmatched.right.paren"
- "unmatched ) in regular expression.")
-
-(js2-msg "msg.no.regexp"
- "Regular expressions are not available.")
-
-(js2-msg "msg.bad.backref"
- "back-reference exceeds number of capturing parentheses.")
-
-(js2-msg "msg.bad.regexp.compile"
- "Only one argument may be specified if the first "
- "argument to RegExp.prototype.compile is a RegExp object.")
-
-;; Parser
-(js2-msg "msg.got.syntax.errors"
- "Compilation produced %s syntax errors.")
-
-(js2-msg "msg.var.redecl"
- "TypeError: redeclaration of var %s.")
-
-(js2-msg "msg.const.redecl"
- "TypeError: redeclaration of const %s.")
-
-(js2-msg "msg.let.redecl"
- "TypeError: redeclaration of variable %s.")
-
-(js2-msg "msg.parm.redecl"
- "TypeError: redeclaration of formal parameter %s.")
-
-(js2-msg "msg.fn.redecl"
- "TypeError: redeclaration of function %s.")
-
-(js2-msg "msg.let.decl.not.in.block"
- "SyntaxError: let declaration not directly within block")
-
-(js2-msg "msg.mod.import.decl.at.top.level"
- "SyntaxError: import declarations may only appear at the top level")
-
-(js2-msg "msg.mod.as.after.reserved.word"
- "SyntaxError: missing keyword 'as' after reserved word %s")
-
-(js2-msg "msg.mod.rc.after.import.spec.list"
- "SyntaxError: missing '}' after module specifier list")
-
-(js2-msg "msg.mod.from.after.import.spec.set"
- "SyntaxError: missing keyword 'from' after import specifier set")
-
-(js2-msg "msg.mod.declaration.after.import"
- "SyntaxError: missing declaration after 'import' keyword")
-
-(js2-msg "msg.mod.spec.after.from"
- "SyntaxError: missing module specifier after 'from' keyword")
-
-(js2-msg "msg.mod.export.decl.at.top.level"
- "SyntaxError: export declarations may only appear at top level")
-
-(js2-msg "msg.mod.rc.after.export.spec.list"
- "SyntaxError: missing '}' after export specifier list")
-
-;; NodeTransformer
-(js2-msg "msg.dup.label"
- "duplicated label")
-
-(js2-msg "msg.undef.label"
- "undefined label")
-
-(js2-msg "msg.bad.break"
- "unlabelled break must be inside loop or switch")
-
-(js2-msg "msg.continue.outside"
- "continue must be inside loop")
-
-(js2-msg "msg.continue.nonloop"
- "continue can only use labels of iteration statements")
-
-(js2-msg "msg.bad.throw.eol"
- "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.no.parm"
- "missing formal parameter")
-
-(js2-msg "msg.no.paren.after.parms"
- "missing ) after formal parameters")
-
-(js2-msg "msg.no.default.after.default.param" ; added by js2-mode
- "parameter without default follows parameter with default")
-
-(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")
-
-(js2-msg "msg.no.brace.after.body"
- "missing } after function body")
-
-(js2-msg "msg.no.paren.cond"
- "missing ( before condition")
-
-(js2-msg "msg.no.paren.after.cond"
- "missing ) after condition")
-
-(js2-msg "msg.no.semi.stmt"
- "missing ; before statement")
-
-(js2-msg "msg.missing.semi"
- "missing ; after statement")
-
-(js2-msg "msg.no.name.after.dot"
- "missing name after . operator")
-
-(js2-msg "msg.no.name.after.coloncolon"
- "missing name after :: operator")
-
-(js2-msg "msg.no.name.after.dotdot"
- "missing name after .. operator")
-
-(js2-msg "msg.no.name.after.xmlAttr"
- "missing name after .@")
-
-(js2-msg "msg.no.bracket.index"
- "missing ] in index expression")
-
-(js2-msg "msg.no.paren.switch"
- "missing ( before switch expression")
-
-(js2-msg "msg.no.paren.after.switch"
- "missing ) after switch expression")
-
-(js2-msg "msg.no.brace.switch"
- "missing '{' before switch body")
-
-(js2-msg "msg.bad.switch"
- "invalid switch statement")
-
-(js2-msg "msg.no.colon.case"
- "missing : after case expression")
-
-(js2-msg "msg.double.switch.default"
- "double default label in the switch statement")
-
-(js2-msg "msg.no.while.do"
- "missing while after do-loop body")
-
-(js2-msg "msg.no.paren.for"
- "missing ( after for")
-
-(js2-msg "msg.no.semi.for"
- "missing ; after for-loop initializer")
-
-(js2-msg "msg.no.semi.for.cond"
- "missing ; after for-loop condition")
-
-(js2-msg "msg.in.after.for.name"
- "missing in or of after for")
-
-(js2-msg "msg.no.paren.for.ctrl"
- "missing ) after for-loop control")
-
-(js2-msg "msg.no.paren.with"
- "missing ( before with-statement object")
-
-(js2-msg "msg.no.paren.after.with"
- "missing ) after with-statement object")
-
-(js2-msg "msg.no.paren.after.let"
- "missing ( after let")
-
-(js2-msg "msg.no.paren.let"
- "missing ) after variable list")
-
-(js2-msg "msg.no.curly.let"
- "missing } after let statement")
-
-(js2-msg "msg.bad.return"
- "invalid return")
-
-(js2-msg "msg.no.brace.block"
- "missing } in compound statement")
-
-(js2-msg "msg.bad.label"
- "invalid label")
-
-(js2-msg "msg.bad.var"
- "missing variable name")
-
-(js2-msg "msg.bad.var.init"
- "invalid variable initialization")
-
-(js2-msg "msg.no.colon.cond"
- "missing : in conditional expression")
-
-(js2-msg "msg.no.paren.arg"
- "missing ) after argument list")
-
-(js2-msg "msg.no.bracket.arg"
- "missing ] after element list")
-
-(js2-msg "msg.bad.prop"
- "invalid property id")
-
-(js2-msg "msg.no.colon.prop"
- "missing : after property id")
-
-(js2-msg "msg.no.brace.prop"
- "missing } after property list")
-
-(js2-msg "msg.no.paren"
- "missing ) in parenthetical")
-
-(js2-msg "msg.reserved.id"
- "'%s' is a reserved identifier")
-
-(js2-msg "msg.no.paren.catch"
- "missing ( before catch-block condition")
-
-(js2-msg "msg.bad.catchcond"
- "invalid catch block condition")
-
-(js2-msg "msg.catch.unreachable"
- "any catch clauses following an unqualified catch are unreachable")
-
-(js2-msg "msg.no.brace.try"
- "missing '{' before try block")
-
-(js2-msg "msg.no.brace.catchblock"
- "missing '{' before catch-block body")
-
-(js2-msg "msg.try.no.catchfinally"
- "'try' without 'catch' or 'finally'")
-
-(js2-msg "msg.no.return.value"
- "function %s does not always return a value")
-
-(js2-msg "msg.anon.no.return.value"
- "anonymous function does not always return a value")
-
-(js2-msg "msg.return.inconsistent"
- "return statement is inconsistent with previous usage")
-
-(js2-msg "msg.generator.returns"
- "TypeError: legacy generator function '%s' returns a value")
-
-(js2-msg "msg.anon.generator.returns"
- "TypeError: anonymous legacy generator function returns a value")
-
-(js2-msg "msg.syntax"
- "syntax error")
-
-(js2-msg "msg.unexpected.eof"
- "Unexpected end of file")
-
-(js2-msg "msg.XML.bad.form"
- "illegally formed XML syntax")
-
-(js2-msg "msg.XML.not.available"
- "XML runtime not available")
-
-(js2-msg "msg.too.deep.parser.recursion"
- "Too deep recursion while parsing")
-
-(js2-msg "msg.no.side.effects"
- "Code has no side effects")
-
-(js2-msg "msg.extra.trailing.comma"
- "Trailing comma is not supported in some browsers")
-
-(js2-msg "msg.array.trailing.comma"
- "Trailing comma yields different behavior across browsers")
-
-(js2-msg "msg.equal.as.assign"
- (concat "Test for equality (==) mistyped as assignment (=)?"
- " (parenthesize to suppress warning)"))
-
-(js2-msg "msg.var.hides.arg"
- "Variable %s hides argument")
-
-(js2-msg "msg.destruct.assign.no.init"
- "Missing = in destructuring declaration")
-
-;; ScriptRuntime
-(js2-msg "msg.no.properties"
- "%s has no properties.")
-
-(js2-msg "msg.invalid.iterator"
- "Invalid iterator value")
-
-(js2-msg "msg.iterator.primitive"
- "__iterator__ returned a primitive value")
-
-(js2-msg "msg.assn.create.strict"
- "Assignment to undeclared variable %s")
-
-(js2-msg "msg.undeclared.variable" ; added by js2-mode
- "Undeclared variable or function '%s'")
-
-(js2-msg "msg.ref.undefined.prop"
- "Reference to undefined property '%s'")
-
-(js2-msg "msg.prop.not.found"
- "Property %s not found.")
-
-(js2-msg "msg.invalid.type"
- "Invalid JavaScript value of type %s")
-
-(js2-msg "msg.primitive.expected"
- "Primitive type expected (had %s instead)")
-
-(js2-msg "msg.namespace.expected"
- "Namespace object expected to left of :: (found %s instead)")
-
-(js2-msg "msg.null.to.object"
- "Cannot convert null to an object.")
-
-(js2-msg "msg.undef.to.object"
- "Cannot convert undefined to an object.")
-
-(js2-msg "msg.cyclic.value"
- "Cyclic %s value not allowed.")
-
-(js2-msg "msg.is.not.defined"
- "'%s' is not defined.")
-
-(js2-msg "msg.undef.prop.read"
- "Cannot read property '%s' from %s")
-
-(js2-msg "msg.undef.prop.write"
- "Cannot set property '%s' of %s to '%s'")
-
-(js2-msg "msg.undef.prop.delete"
- "Cannot delete property '%s' of %s")
-
-(js2-msg "msg.undef.method.call"
- "Cannot call method '%s' of %s")
-
-(js2-msg "msg.undef.with"
- "Cannot apply 'with' to %s")
-
-(js2-msg "msg.isnt.function"
- "%s is not a function, it is %s.")
-
-(js2-msg "msg.isnt.function.in"
- "Cannot call property %s in object %s. "
- "It is not a function, it is '%s'.")
-
-(js2-msg "msg.function.not.found"
- "Cannot find function %s.")
-
-(js2-msg "msg.function.not.found.in"
- "Cannot find function %s in object %s.")
-
-(js2-msg "msg.isnt.xml.object"
- "%s is not an xml object.")
-
-(js2-msg "msg.no.ref.to.get"
- "%s is not a reference to read reference value.")
-
-(js2-msg "msg.no.ref.to.set"
- "%s is not a reference to set reference value to %s.")
-
-(js2-msg "msg.no.ref.from.function"
- "Function %s can not be used as the left-hand "
- "side of assignment or as an operand of ++ or -- operator.")
-
-(js2-msg "msg.bad.default.value"
- "Object's getDefaultValue() method returned an object.")
-
-(js2-msg "msg.instanceof.not.object"
- "Can't use instanceof on a non-object.")
-
-(js2-msg "msg.instanceof.bad.prototype"
- "'prototype' property of %s is not an object.")
-
-(js2-msg "msg.bad.radix"
- "illegal radix %s.")
-
-;; ScriptableObject
-(js2-msg "msg.default.value"
- "Cannot find default value for object.")
-
-(js2-msg "msg.zero.arg.ctor"
- "Cannot load class '%s' which has no zero-parameter constructor.")
-
-(js2-msg "msg.ctor.multiple.parms"
- "Can't define constructor or class %s since more than "
- "one constructor has multiple parameters.")
-
-(js2-msg "msg.extend.scriptable"
- "%s must extend ScriptableObject in order to define property %s.")
-
-(js2-msg "msg.bad.getter.parms"
- "In order to define a property, getter %s must have zero "
- "parameters or a single ScriptableObject parameter.")
-
-(js2-msg "msg.obj.getter.parms"
- "Expected static or delegated getter %s to take "
- "a ScriptableObject parameter.")
-
-(js2-msg "msg.getter.static"
- "Getter and setter must both be static or neither be static.")
-
-(js2-msg "msg.setter.return"
- "Setter must have void return type: %s")
-
-(js2-msg "msg.setter2.parms"
- "Two-parameter setter must take a ScriptableObject as "
- "its first parameter.")
-
-(js2-msg "msg.setter1.parms"
- "Expected single parameter setter for %s")
-
-(js2-msg "msg.setter2.expected"
- "Expected static or delegated setter %s to take two parameters.")
-
-(js2-msg "msg.setter.parms"
- "Expected either one or two parameters for setter.")
-
-(js2-msg "msg.setter.bad.type"
- "Unsupported parameter type '%s' in setter '%s'.")
-
-(js2-msg "msg.add.sealed"
- "Cannot add a property to a sealed object: %s.")
-
-(js2-msg "msg.remove.sealed"
- "Cannot remove a property from a sealed object: %s.")
-
-(js2-msg "msg.modify.sealed"
- "Cannot modify a property of a sealed object: %s.")
-
-(js2-msg "msg.modify.readonly"
- "Cannot modify readonly property: %s.")
-
-;; TokenStream
-(js2-msg "msg.missing.exponent"
- "missing exponent")
-
-(js2-msg "msg.caught.nfe"
- "number format error")
-
-(js2-msg "msg.unterminated.string.lit"
- "unterminated string literal")
-
-(js2-msg "msg.unterminated.comment"
- "unterminated comment")
-
-(js2-msg "msg.unterminated.re.lit"
- "unterminated regular expression literal")
-
-(js2-msg "msg.invalid.re.flag"
- "invalid flag after regular expression")
-
-(js2-msg "msg.no.re.input.for"
- "no input for %s")
-
-(js2-msg "msg.illegal.character"
- "illegal character")
-
-(js2-msg "msg.invalid.escape"
- "invalid Unicode escape sequence")
-
-(js2-msg "msg.bad.namespace"
- "not a valid default namespace statement. "
- "Syntax is: default xml namespace = EXPRESSION;")
-
-;; TokensStream warnings
-(js2-msg "msg.bad.octal.literal"
- "illegal octal literal digit %s; "
- "interpreting it as a decimal digit")
-
-(js2-msg "msg.missing.hex.digits"
- "missing hexadecimal digits after '0x'")
-
-(js2-msg "msg.missing.binary.digits"
- "missing binary digits after '0b'")
-
-(js2-msg "msg.missing.octal.digits"
- "missing octal digits after '0o'")
-
-(js2-msg "msg.script.is.not.constructor"
- "Script objects are not constructors.")
-
-;; Arrays
-(js2-msg "msg.arraylength.bad"
- "Inappropriate array length.")
-
-;; Arrays
-(js2-msg "msg.arraylength.too.big"
- "Array length %s exceeds supported capacity limit.")
-
-;; URI
-(js2-msg "msg.bad.uri"
- "Malformed URI sequence.")
-
-;; Number
-(js2-msg "msg.bad.precision"
- "Precision %s out of range.")
-
-;; NativeGenerator
-(js2-msg "msg.send.newborn"
- "Attempt to send value to newborn generator")
-
-(js2-msg "msg.already.exec.gen"
- "Already executing generator")
-
-(js2-msg "msg.StopIteration.invalid"
- "StopIteration may not be changed to an arbitrary object.")
-
-;; Interpreter
-(js2-msg "msg.yield.closing"
- "Yield from closing generator")
-
-;; Classes
-(js2-msg "msg.unnamed.class.stmt" ; added by js2-mode
- "class statement requires a name")
-
-(js2-msg "msg.class.unexpected.comma" ; added by js2-mode
- "unexpected ',' between class properties")
-
-(js2-msg "msg.unexpected.static" ; added by js2-mode
- "unexpected 'static'")
-
-(js2-msg "msg.missing.extends" ; added by js2-mode
- "name is required after extends")
-
-(js2-msg "msg.no.brace.class" ; added by js2-mode
- "missing '{' before class body")
-
-(js2-msg "msg.missing.computed.rb" ; added by js2-mode
- "missing ']' after computed property expression")
-
-;;; Tokens Buffer
-
-(defconst js2-ti-max-lookahead 2)
-(defconst js2-ti-ntokens (1+ js2-ti-max-lookahead))
-
-(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)
- "Remove all items satisfying PREDICATE in LIST."
- (cl-loop for item in list
- if (not (funcall predicate item))
- collect item))
-
-(defun js2-position (element list)
- "Find 0-indexed position of ELEMENT in LIST comparing with `eq'.
-Returns nil if element is not found in the list."
- (let ((count 0)
- found)
- (while (and list (not found))
- (if (eq element (car list))
- (setq found t)
- (setq count (1+ count)
- list (cdr list))))
- (if found count)))
-
-(defun js2-find-if (predicate list)
- "Find first item satisfying PREDICATE in LIST."
- (let (result)
- (while (and list (not result))
- (if (funcall predicate (car list))
- (setq result (car list)))
- (setq list (cdr list)))
- result))
-
-(defmacro js2-time (form)
- "Evaluate FORM, discard result, and return elapsed time in sec."
- (declare (debug t))
- (let ((beg (make-symbol "--js2-time-beg--"))
- (delta (make-symbol "--js2-time-end--")))
- `(let ((,beg (current-time))
- ,delta)
- ,form
- (/ (truncate (* (- (float-time (current-time))
- (float-time ,beg))
- 10000))
- 10000.0))))
-
-(defsubst js2-same-line (pos)
- "Return t if POS is on the same line as current point."
- (and (>= pos (point-at-bol))
- (<= pos (point-at-eol))))
-
-(defun js2-code-bug ()
- "Signal an error when we encounter an unexpected code path."
- (error "failed assertion"))
-
-(defsubst js2-record-text-property (beg end prop value)
- "Record a text property to set when parsing finishes."
- (push (list beg end prop value) js2-mode-deferred-properties))
-
-;; I'd like to associate errors with nodes, but for now the
-;; 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-current-token-beg))
- (or len (js2-current-token-len)))
- js2-parsed-errors))
-
-(defun js2-report-error (msg &optional msg-arg pos len)
- "Signal a syntax error or record a parse error."
- (if js2-recover-from-parse-errors
- (js2-record-parse-error msg msg-arg pos len)
- (signal 'js2-syntax-error
- (list msg
- js2-ts-lineno
- (save-excursion
- (goto-char js2-ts-cursor)
- (current-column))
- js2-ts-hit-eof))))
-
-(defun js2-report-warning (msg &optional msg-arg pos len face)
- (if js2-compiler-report-warning-as-error
- (js2-report-error msg msg-arg pos len)
- (push (list (list msg msg-arg)
- (or pos (js2-current-token-beg))
- (or len (js2-current-token-len))
- face)
- js2-parsed-warnings)))
-
-(defun js2-add-strict-warning (msg-id &optional msg-arg beg end)
- (if js2-compiler-strict-mode
- (js2-report-warning msg-id msg-arg beg
- (and beg end (- end beg)))))
-
-(put 'js2-syntax-error 'error-conditions
- '(error syntax-error js2-syntax-error))
-(put 'js2-syntax-error 'error-message "Syntax error")
-
-(put 'js2-parse-error 'error-conditions
- '(error parse-error js2-parse-error))
-(put 'js2-parse-error 'error-message "Parse error")
-
-(defmacro js2-clear-flag (flags flag)
- `(setq ,flags (logand ,flags (lognot ,flag))))
-
-(defmacro js2-set-flag (flags flag)
- "Logical-or FLAG into FLAGS."
- `(setq ,flags (logior ,flags ,flag)))
-
-(defsubst js2-flag-set-p (flags flag)
- (/= 0 (logand flags flag)))
-
-(defsubst js2-flag-not-set-p (flags flag)
- (zerop (logand flags flag)))
-
-(defmacro js2-with-underscore-as-word-syntax (&rest body)
- "Evaluate BODY with the _ character set to be word-syntax."
- (declare (indent 0) (debug t))
- (let ((old-syntax (make-symbol "old-syntax")))
- `(let ((,old-syntax (string (char-syntax ?_))))
- (unwind-protect
- (progn
- (modify-syntax-entry ?_ "w" js2-mode-syntax-table)
- ,@body)
- (modify-syntax-entry ?_ ,old-syntax js2-mode-syntax-table)))))
-
-;;; AST struct and function definitions
-
-;; flags for ast node property 'member-type (used for e4x operators)
-(defvar js2-property-flag #x1 "Property access: element is valid name.")
-(defvar js2-attribute-flag #x2 "address@hidden or address@hidden")
-(defvar js2-descendants-flag #x4 "x..y or address@hidden")
-
-(defsubst js2-relpos (pos anchor)
- "Convert POS to be relative to ANCHOR.
-If POS is nil, returns nil."
- (and pos (- pos anchor)))
-
-(defun js2-make-pad (indent)
- (if (zerop indent)
- ""
- (make-string (* indent js2-basic-offset) ? )))
-
-(defun js2-visit-ast (node callback)
- "Visit every node in ast NODE with visitor CALLBACK.
-
-CALLBACK is a function that takes two arguments: (NODE END-P). It is
-called twice: once to visit the node, and again after all the node's
-children have been processed. The END-P argument is nil on the first
-call and non-nil on the second call. The return value of the callback
-affects the traversal: if non-nil, the children of NODE are processed.
-If the callback returns nil, or if the node has no children, then the
-callback is called immediately with a non-nil END-P argument.
-
-The node traversal is approximately lexical-order, although there
-are currently no guarantees around this."
- (when node
- (let ((vfunc (get (aref node 0) 'js2-visitor)))
- ;; visit the node
- (when (funcall callback node nil)
- ;; visit the kids
- (cond
- ((eq vfunc 'js2-visit-none)
- nil) ; don't even bother calling it
- ;; Each AST node type has to define a `js2-visitor' function
- ;; that takes a node and a callback, and calls `js2-visit-ast'
- ;; on each child of the node.
- (vfunc
- (funcall vfunc node callback))
- (t
- (error "%s does not define a visitor-traversal function"
- (aref node 0)))))
- ;; call the end-visit
- (funcall callback node t))))
-
-(cl-defstruct (js2-node
- (:constructor nil)) ; abstract
- "Base AST node type."
- (type -1) ; token type
- (pos -1) ; start position of this AST node in parsed input
- (len 1) ; num characters spanned by the node
- props ; optional node property list (an alist)
- parent) ; link to parent node; null for root
-
-(defsubst js2-node-get-prop (node prop &optional default)
- (or (cadr (assoc prop (js2-node-props node))) default))
-
-(defsubst js2-node-set-prop (node prop value)
- (setf (js2-node-props node)
- (cons (list prop value) (js2-node-props node))))
-
-(defun js2-fixup-starts (n nodes)
- "Adjust the start positions of NODES to be relative to N.
-Any node in the list may be nil, for convenience."
- (dolist (node nodes)
- (when node
- (setf (js2-node-pos node) (- (js2-node-pos node)
- (js2-node-pos n))))))
-
-(defun js2-node-add-children (parent &rest nodes)
- "Set parent node of NODES to PARENT, and return PARENT.
-Does nothing if we're not recording parent links.
-If any given node in NODES is nil, doesn't record that link."
- (js2-fixup-starts parent nodes)
- (dolist (node nodes)
- (and node
- (setf (js2-node-parent node) parent))))
-
-;; Non-recursive since it's called a frightening number of times.
-(defun js2-node-abs-pos (n)
- (let ((pos (js2-node-pos n)))
- (while (setq n (js2-node-parent n))
- (setq pos (+ pos (js2-node-pos n))))
- pos))
-
-(defsubst js2-node-abs-end (n)
- "Return absolute buffer position of end of N."
- (+ (js2-node-abs-pos n) (js2-node-len n)))
-
-;; It's important to make sure block nodes have a Lisp list for the
-;; child nodes, to limit printing recursion depth in an AST that
-;; otherwise consists of defstruct vectors. Emacs will crash printing
-;; a sufficiently large vector tree.
-
-(cl-defstruct (js2-block-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-block-node (&key (type js2-BLOCK)
- (pos
(js2-current-token-beg))
- len
- props
- kids)))
- "A block of statements."
- kids) ; a Lisp list of the child statement nodes
-
-(put 'cl-struct-js2-block-node 'js2-visitor 'js2-visit-block)
-(put 'cl-struct-js2-block-node 'js2-printer 'js2-print-block)
-
-(defun js2-visit-block (ast callback)
- "Visit the `js2-block-node' children of AST."
- (dolist (kid (js2-block-node-kids ast))
- (js2-visit-ast kid callback)))
-
-(defun js2-print-block (n i)
- (let ((pad (js2-make-pad i)))
- (insert pad "{\n")
- (dolist (kid (js2-block-node-kids n))
- (js2-print-ast kid (1+ i)))
- (insert pad "}")))
-
-(cl-defstruct (js2-scope
- (:include js2-block-node)
- (:constructor nil)
- (:constructor make-js2-scope (&key (type js2-BLOCK)
- (pos (js2-current-token-beg))
- len
- kids)))
- ;; The symbol-table is a LinkedHashMap<String,Symbol> in Rhino.
- ;; I don't have one of those handy, so I'll use an alist for now.
- ;; It's as fast as an emacs hashtable for up to about 50 elements,
- ;; and is much lighter-weight to construct (both CPU and mem).
- ;; The keys are interned strings (symbols) for faster lookup.
- ;; Should switch to hybrid alist/hashtable eventually.
- symbol-table ; an alist of (symbol . js2-symbol)
- parent-scope ; a `js2-scope'
- top) ; top-level `js2-scope' (script/function)
-
-(put 'cl-struct-js2-scope 'js2-visitor 'js2-visit-block)
-(put 'cl-struct-js2-scope 'js2-printer 'js2-print-none)
-
-(defun js2-node-get-enclosing-scope (node)
- "Return the innermost `js2-scope' node surrounding NODE.
-Returns nil if there is no enclosing scope node."
- (let ((parent (js2-node-parent node)))
- (while (not (js2-scope-p parent))
- (setq parent (js2-node-parent parent)))
- parent))
-
-(defun js2-get-defining-scope (scope name)
- "Search up scope chain from SCOPE looking for NAME, a string or symbol.
-Returns `js2-scope' in which NAME is defined, or nil if not found."
- (let ((sym (if (symbolp name)
- name
- (intern name)))
- table
- result
- (continue t))
- (while (and scope continue)
- (if (or
- (and (setq table (js2-scope-symbol-table scope))
- (assq sym table))
- (and (eq sym 'arguments)
- (js2-function-node-p scope)))
- (setq continue nil
- result scope)
- (setq scope (js2-scope-parent-scope scope))))
- result))
-
-(defun js2-scope-get-symbol (scope name)
- "Return symbol table entry for NAME in SCOPE.
-NAME can be a string or symbol. Returns a `js2-symbol' or nil if not found."
- (and (js2-scope-symbol-table scope)
- (cdr (assq (if (symbolp name)
- name
- (intern name))
- (js2-scope-symbol-table scope)))))
-
-(defun js2-scope-put-symbol (scope name symbol)
- "Enter SYMBOL into symbol-table for SCOPE under NAME.
-NAME can be a Lisp symbol or string. SYMBOL is a `js2-symbol'."
- (let* ((table (js2-scope-symbol-table scope))
- (sym (if (symbolp name) name (intern name)))
- (entry (assq sym table)))
- (if entry
- (setcdr entry symbol)
- (push (cons sym symbol)
- (js2-scope-symbol-table scope)))))
-
-(cl-defstruct (js2-symbol
- (:constructor nil)
- (:constructor make-js2-symbol (decl-type name &optional
ast-node)))
- "A symbol table entry."
- ;; One of js2-FUNCTION, js2-LP (for parameters), js2-VAR,
- ;; js2-LET, or js2-CONST
- decl-type
- name ; string
- ast-node) ; a `js2-node'
-
-(cl-defstruct (js2-error-node
- (:include js2-node)
- (:constructor nil) ; silence emacs21 byte-compiler
- (:constructor make-js2-error-node (&key (type js2-ERROR)
- (pos
(js2-current-token-beg))
- len)))
- "AST node representing a parse error.")
-
-(put 'cl-struct-js2-error-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-error-node 'js2-printer 'js2-print-none)
-
-(cl-defstruct (js2-script-node
- (:include js2-scope)
- (:constructor nil)
- (:constructor make-js2-script-node (&key (type js2-SCRIPT)
- (pos
(js2-current-token-beg))
- len
- ;; FIXME: What are
those?
- var-decls
- fun-decls)))
- functions ; Lisp list of nested functions
- regexps ; Lisp list of (string . flags)
- symbols ; alist (every symbol gets unique index)
- (param-count 0)
- var-names ; vector of string names
- consts ; bool-vector matching var-decls
- (temp-number 0)) ; for generating temp variables
-
-(put 'cl-struct-js2-script-node 'js2-visitor 'js2-visit-block)
-(put 'cl-struct-js2-script-node 'js2-printer 'js2-print-script)
-
-(defun js2-print-script (node indent)
- (dolist (kid (js2-block-node-kids node))
- (js2-print-ast kid indent)))
-
-(cl-defstruct (js2-ast-root
- (:include js2-script-node)
- (:constructor nil)
- (:constructor make-js2-ast-root (&key (type js2-SCRIPT)
- (pos
(js2-current-token-beg))
- len
- buffer)))
- "The root node of a js2 AST."
- buffer ; the source buffer from which the code was parsed
- comments ; a Lisp list of comments, ordered by start position
- errors ; a Lisp list of errors found during parsing
- warnings ; a Lisp list of warnings found during parsing
- node-count) ; number of nodes in the tree, including the root
-
-(put 'cl-struct-js2-ast-root 'js2-visitor 'js2-visit-ast-root)
-(put 'cl-struct-js2-ast-root 'js2-printer 'js2-print-script)
-
-(defun js2-visit-ast-root (ast callback)
- (dolist (kid (js2-ast-root-kids ast))
- (js2-visit-ast kid callback))
- (dolist (comment (js2-ast-root-comments ast))
- (js2-visit-ast comment callback)))
-
-(cl-defstruct (js2-comment-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-comment-node (&key (type js2-COMMENT)
- (pos
(js2-current-token-beg))
- len
- format)))
- format) ; 'line, 'block, 'jsdoc or 'html
-
-(put 'cl-struct-js2-comment-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-comment-node 'js2-printer 'js2-print-comment)
-
-(defun js2-print-comment (n i)
- ;; We really ought to link end-of-line comments to their nodes.
- ;; Or maybe we could add a new comment type, 'endline.
- (insert (js2-make-pad i)
- (js2-node-string n)))
-
-(cl-defstruct (js2-expr-stmt-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-expr-stmt-node (&key (type js2-EXPR_VOID)
- (pos js2-ts-cursor)
- len
- expr)))
- "An expression statement."
- expr)
-
-(defsubst js2-expr-stmt-node-set-has-result (node)
- "Change NODE type to `js2-EXPR_RESULT'. Used for code generation."
- (setf (js2-node-type node) js2-EXPR_RESULT))
-
-(put 'cl-struct-js2-expr-stmt-node 'js2-visitor 'js2-visit-expr-stmt-node)
-(put 'cl-struct-js2-expr-stmt-node 'js2-printer 'js2-print-expr-stmt-node)
-
-(defun js2-visit-expr-stmt-node (n v)
- (js2-visit-ast (js2-expr-stmt-node-expr n) v))
-
-(defun js2-print-expr-stmt-node (n indent)
- (js2-print-ast (js2-expr-stmt-node-expr n) indent)
- (insert ";\n"))
-
-(cl-defstruct (js2-loop-node
- (:include js2-scope)
- (:constructor nil))
- "Abstract supertype of loop nodes."
- body ; a `js2-block-node'
- lp ; position of left-paren, nil if omitted
- rp) ; position of right-paren, nil if omitted
-
-(cl-defstruct (js2-do-node
- (:include js2-loop-node)
- (:constructor nil)
- (:constructor make-js2-do-node (&key (type js2-DO)
- (pos
(js2-current-token-beg))
- len
- body
- condition
- while-pos
- lp
- rp)))
- "AST node for do-loop."
- condition ; while (expression)
- while-pos) ; buffer position of 'while' keyword
-
-(put 'cl-struct-js2-do-node 'js2-visitor 'js2-visit-do-node)
-(put 'cl-struct-js2-do-node 'js2-printer 'js2-print-do-node)
-
-(defun js2-visit-do-node (n v)
- (js2-visit-ast (js2-do-node-body n) v)
- (js2-visit-ast (js2-do-node-condition n) v))
-
-(defun js2-print-do-node (n i)
- (let ((pad (js2-make-pad i)))
- (insert pad "do {\n")
- (dolist (kid (js2-block-node-kids (js2-do-node-body n)))
- (js2-print-ast kid (1+ i)))
- (insert pad "} while (")
- (js2-print-ast (js2-do-node-condition n) 0)
- (insert ");\n")))
-
-(cl-defstruct (js2-export-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-export-node (&key (type js2-EXPORT)
- (pos)
(js2-current-token-beg)
- len
- exports-list
- from-clause
- declaration
- default)))
- "AST node for an export statement. There are many things that can be
exported,
-so many of its properties will be nil.
-"
- exports-list ; lisp list of js2-export-binding-node to export
- from-clause ; js2-from-clause-node for re-exporting symbols from another
module
- declaration ; js2-var-decl-node (var, let, const) or js2-class-node
- default) ; js2-function-node or js2-assign-node
-
-(put 'cl-struct-js2-export-node 'js2-visitor 'js2-visit-export-node)
-(put 'cl-struct-js2-export-node 'js2-printer 'js2-print-export-node)
-
-(defun js2-visit-export-node (n v)
- (let ((exports-list (js2-export-node-exports-list n))
- (from (js2-export-node-from-clause n))
- (declaration (js2-export-node-declaration n))
- (default (js2-export-node-default n)))
- (when exports-list
- (dolist (export exports-list)
- (js2-visit-ast export v)))
- (when from
- (js2-visit-ast from v))
- (when declaration
- (js2-visit-ast declaration v))
- (when default
- (js2-visit-ast default v))))
-
-(defun js2-print-export-node (n i)
- (let ((pad (js2-make-pad i))
- (exports-list (js2-export-node-exports-list n))
- (from (js2-export-node-from-clause n))
- (declaration (js2-export-node-declaration n))
- (default (js2-export-node-default n)))
- (insert pad "export ")
- (cond
- (default
- (insert "default ")
- (js2-print-ast default i))
- (declaration
- (js2-print-ast declaration i))
- ((and exports-list from)
- (js2-print-named-imports exports-list)
- (insert " ")
- (js2-print-from-clause from))
- (from
- (insert "* ")
- (js2-print-from-clause from))
- (exports-list
- (js2-print-named-imports exports-list)))
- (insert ";\n")))
-
-(cl-defstruct (js2-while-node
- (:include js2-loop-node)
- (:constructor nil)
- (:constructor make-js2-while-node (&key (type js2-WHILE)
- (pos
(js2-current-token-beg))
- len body
- condition lp
- rp)))
- "AST node for while-loop."
- condition) ; while-condition
-
-(put 'cl-struct-js2-while-node 'js2-visitor 'js2-visit-while-node)
-(put 'cl-struct-js2-while-node 'js2-printer 'js2-print-while-node)
-
-(defun js2-visit-while-node (n v)
- (js2-visit-ast (js2-while-node-condition n) v)
- (js2-visit-ast (js2-while-node-body n) v))
-
-(defun js2-print-while-node (n i)
- (let ((pad (js2-make-pad i)))
- (insert pad "while (")
- (js2-print-ast (js2-while-node-condition n) 0)
- (insert ") {\n")
- (js2-print-body (js2-while-node-body n) (1+ i))
- (insert pad "}\n")))
-
-(cl-defstruct (js2-for-node
- (:include js2-loop-node)
- (:constructor nil)
- (:constructor make-js2-for-node (&key (type js2-FOR)
- (pos js2-ts-cursor)
- len body init
- condition
- update lp rp)))
- "AST node for a C-style for-loop."
- init ; initialization expression
- condition ; loop condition
- update) ; update clause
-
-(put 'cl-struct-js2-for-node 'js2-visitor 'js2-visit-for-node)
-(put 'cl-struct-js2-for-node 'js2-printer 'js2-print-for-node)
-
-(defun js2-visit-for-node (n v)
- (js2-visit-ast (js2-for-node-init n) v)
- (js2-visit-ast (js2-for-node-condition n) v)
- (js2-visit-ast (js2-for-node-update n) v)
- (js2-visit-ast (js2-for-node-body n) v))
-
-(defun js2-print-for-node (n i)
- (let ((pad (js2-make-pad i)))
- (insert pad "for (")
- (js2-print-ast (js2-for-node-init n) 0)
- (insert "; ")
- (js2-print-ast (js2-for-node-condition n) 0)
- (insert "; ")
- (js2-print-ast (js2-for-node-update n) 0)
- (insert ") {\n")
- (js2-print-body (js2-for-node-body n) (1+ i))
- (insert pad "}\n")))
-
-(cl-defstruct (js2-for-in-node
- (:include js2-loop-node)
- (:constructor nil)
- (:constructor make-js2-for-in-node (&key (type js2-FOR)
- (pos js2-ts-cursor)
- len body
- iterator
- object
- in-pos
- each-pos
- foreach-p forof-p
- lp rp)))
- "AST node for a for..in loop."
- iterator ; [var] foo in ...
- object ; object over which we're iterating
- in-pos ; buffer position of 'in' keyword
- each-pos ; buffer position of 'each' keyword, if foreach-p
- foreach-p ; t if it's a for-each loop
- forof-p) ; t if it's a for-of loop
-
-(put 'cl-struct-js2-for-in-node 'js2-visitor 'js2-visit-for-in-node)
-(put 'cl-struct-js2-for-in-node 'js2-printer 'js2-print-for-in-node)
-
-(defun js2-visit-for-in-node (n v)
- (js2-visit-ast (js2-for-in-node-iterator n) v)
- (js2-visit-ast (js2-for-in-node-object n) v)
- (js2-visit-ast (js2-for-in-node-body n) v))
-
-(defun js2-print-for-in-node (n i)
- (let ((pad (js2-make-pad i))
- (foreach (js2-for-in-node-foreach-p n))
- (forof (js2-for-in-node-forof-p n)))
- (insert pad "for ")
- (if foreach
- (insert "each "))
- (insert "(")
- (js2-print-ast (js2-for-in-node-iterator n) 0)
- (insert (if forof " of " " in "))
- (js2-print-ast (js2-for-in-node-object n) 0)
- (insert ") {\n")
- (js2-print-body (js2-for-in-node-body n) (1+ i))
- (insert pad "}\n")))
-
-(cl-defstruct (js2-return-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-return-node (&key (type js2-RETURN)
- (pos js2-ts-cursor)
- len
- retval)))
- "AST node for a return statement."
- retval) ; expression to return, or 'undefined
-
-(put 'cl-struct-js2-return-node 'js2-visitor 'js2-visit-return-node)
-(put 'cl-struct-js2-return-node 'js2-printer 'js2-print-return-node)
-
-(defun js2-visit-return-node (n v)
- (js2-visit-ast (js2-return-node-retval n) v))
-
-(defun js2-print-return-node (n i)
- (insert (js2-make-pad i) "return")
- (when (js2-return-node-retval n)
- (insert " ")
- (js2-print-ast (js2-return-node-retval n) 0))
- (insert ";\n"))
-
-(cl-defstruct (js2-if-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-if-node (&key (type js2-IF)
- (pos js2-ts-cursor)
- len condition
- then-part
- else-pos
- else-part lp
- rp)))
- "AST node for an if-statement."
- condition ; expression
- then-part ; statement or block
- else-pos ; optional buffer position of 'else' keyword
- else-part ; optional statement or block
- lp ; position of left-paren, nil if omitted
- rp) ; position of right-paren, nil if omitted
-
-(put 'cl-struct-js2-if-node 'js2-visitor 'js2-visit-if-node)
-(put 'cl-struct-js2-if-node 'js2-printer 'js2-print-if-node)
-
-(defun js2-visit-if-node (n v)
- (js2-visit-ast (js2-if-node-condition n) v)
- (js2-visit-ast (js2-if-node-then-part n) v)
- (js2-visit-ast (js2-if-node-else-part n) v))
-
-(defun js2-print-if-node (n i)
- (let ((pad (js2-make-pad i))
- (then-part (js2-if-node-then-part n))
- (else-part (js2-if-node-else-part n)))
- (insert pad "if (")
- (js2-print-ast (js2-if-node-condition n) 0)
- (insert ") {\n")
- (js2-print-body then-part (1+ i))
- (insert pad "}")
- (cond
- ((not else-part)
- (insert "\n"))
- ((js2-if-node-p else-part)
- (insert " else ")
- (js2-print-body else-part i))
- (t
- (insert " else {\n")
- (js2-print-body else-part (1+ i))
- (insert pad "}\n")))))
-
-(cl-defstruct (js2-export-binding-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-export-binding-node (&key (type -1)
- pos
- len
- local-name
- extern-name)))
- "AST node for an external symbol binding.
-It contains a local-name node which is the name of the value in the
-current scope, and extern-name which is the name of the value in the
-imported or exported scope. By default these are the same, but if the
-name is aliased as in {foo as bar}, it would have an extern-name node
-containing 'foo' and a local-name node containing 'bar'."
- local-name ; js2-name-node with the variable name in this scope
- extern-name) ; js2-name-node with the value name in the exporting module
-
-(put 'cl-struct-js2-export-binding-node 'js2-printer 'js2-print-extern-binding)
-(put 'cl-struct-js2-export-binding-node 'js2-visitor 'js2-visit-extern-binding)
-
-(defun js2-visit-extern-binding (n v)
- "Visit an extern binding node. First visit the local-name, and, if
-different, visit the extern-name."
- (let ((local-name (js2-export-binding-node-local-name n))
- (extern-name (js2-export-binding-node-extern-name n)))
- (when local-name
- (js2-visit-ast local-name v))
- (when (not (equal local-name extern-name))
- (js2-visit-ast extern-name v))))
-
-(defun js2-print-extern-binding (n i)
- "Print a representation of a single extern binding. E.g. 'foo' or
-'foo as bar'."
- (let ((local-name (js2-export-binding-node-local-name n))
- (extern-name (js2-export-binding-node-extern-name n)))
- (insert (js2-name-node-name extern-name))
- (when (not (equal local-name extern-name))
- (insert " as ")
- (insert (js2-name-node-name local-name)))))
-
-
-(cl-defstruct (js2-import-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-import-node (&key (type js2-IMPORT)
- (pos
(js2-current-token-beg))
- len
- import
- from
- module-id)))
- "AST node for an import statement. It follows the form
-
-import ModuleSpecifier;
-import ImportClause FromClause;"
- import ; js2-import-clause-node specifying which names are to imported.
- from ; js2-from-clause-node indicating the module from which to import.
- module-id) ; module-id of the import. E.g. 'src/mylib'.
-
-(put 'cl-struct-js2-import-node 'js2-printer 'js2-print-import)
-(put 'cl-struct-js2-import-node 'js2-visitor 'js2-visit-import)
-
-(defun js2-visit-import (n v)
- (let ((import-clause (js2-import-node-import n))
- (from-clause (js2-import-node-from n)))
- (when import-clause
- (js2-visit-ast import-clause v))
- (when from-clause
- (js2-visit-ast from-clause v))))
-
-(defun js2-print-import (n i)
- "Prints a representation of the import node"
- (let ((pad (js2-make-pad i))
- (import-clause (js2-import-node-import n))
- (from-clause (js2-import-node-from n))
- (module-id (js2-import-node-module-id n)))
- (insert pad "import ")
- (if import-clause
- (progn
- (js2-print-import-clause import-clause)
- (insert " ")
- (js2-print-from-clause from-clause))
- (insert "'")
- (insert module-id)
- (insert "'"))
- (insert ";\n")))
-
-(cl-defstruct (js2-import-clause-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-import-clause-node (&key (type -1)
- pos
- len
- namespace-import
- named-imports
-
default-binding)))
- "AST node corresponding to the import clause of an import statement. This is
-the portion of the import that bindings names from the external context to the
-local context."
- namespace-import ; js2-namespace-import-node. E.g. '* as lib'
- named-imports ; lisp list of js2-export-binding-node for all named
imports.
- default-binding) ; js2-export-binding-node for the default import binding
-
-(put 'cl-struct-js2-import-clause-node 'js2-visitor 'js2-visit-import-clause)
-(put 'cl-struct-js2-import-clause-node 'js2-printer 'js2-print-import-clause)
-
-(defun js2-visit-import-clause (n v)
- (let ((ns-import (js2-import-clause-node-namespace-import n))
- (named-imports (js2-import-clause-node-named-imports n))
- (default (js2-import-clause-node-default-binding n)))
- (when ns-import
- (js2-visit-ast ns-import v))
- (when named-imports
- (dolist (import named-imports)
- (js2-visit-ast import v)))
- (when default
- (js2-visit-ast default v))))
-
-(defun js2-print-import-clause (n)
- (let ((ns-import (js2-import-clause-node-namespace-import n))
- (named-imports (js2-import-clause-node-named-imports n))
- (default (js2-import-clause-node-default-binding n)))
- (cond
- ((and default ns-import)
- (js2-print-ast default)
- (insert ", ")
- (js2-print-namespace-import ns-import))
- ((and default named-imports)
- (js2-print-ast default)
- (insert ", ")
- (js2-print-named-imports named-imports))
- (default
- (js2-print-ast default))
- (ns-import
- (js2-print-namespace-import ns-import))
- (named-imports
- (js2-print-named-imports named-imports)))))
-
-(defun js2-print-namespace-import (node)
- (insert "* as ")
- (insert (js2-name-node-name (js2-namespace-import-node-name node))))
-
-(defun js2-print-named-imports (imports)
- (insert "{")
- (let ((len (length imports))
- (n 0))
- (while (< n len)
- (js2-print-extern-binding (nth n imports) 0)
- (unless (= n (- len 1))
- (insert ", "))
- (setq n (+ n 1))))
- (insert "}"))
-
-(cl-defstruct (js2-namespace-import-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-namespace-import-node (&key (type -1)
- pos
- len
- name)))
- "AST node for a complete namespace import.
-E.g. the '* as lib' expression in:
-
-import * as lib from 'src/lib'
-
-It contains a single name node referring to the bound name."
- name) ; js2-name-node of the bound name.
-
-(defun js2-visit-namespace-import (n v)
- (js2-visit-ast (js2-namespace-import-node-name n) v))
-
-(put 'cl-struct-js2-namespace-import-node 'js2-visitor
'js2-visit-namespace-import)
-(put 'cl-struct-js2-namespace-import-node 'js2-printer
'js2-print-namespace-import)
-
-(cl-defstruct (js2-from-clause-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-from-clause-node (&key (type js2-NAME)
- pos
- len
- module-id
- metadata-p)))
- "AST node for the from clause in an import or export statement.
-E.g. from 'my/module'. It can refere to either an external module, or to the
-modules metadata itself."
- module-id ; string containing the module specifier.
- metadata-p) ; true if this clause refers to the module's metadata
-
-(put 'cl-struct-js2-from-clause-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-from-clause-node 'js2-printer 'js2-print-from-clause)
-
-(defun js2-print-from-clause (n)
- (insert "from ")
- (if (js2-from-clause-node-metadata-p n)
- (insert "this module")
- (insert "'")
- (insert (js2-from-clause-node-module-id n))
- (insert "'")))
-
-(cl-defstruct (js2-try-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-try-node (&key (type js2-TRY)
- (pos js2-ts-cursor)
- len
- try-block
- catch-clauses
- finally-block)))
- "AST node for a try-statement."
- try-block
- catch-clauses ; a Lisp list of `js2-catch-node'
- finally-block) ; a `js2-finally-node'
-
-(put 'cl-struct-js2-try-node 'js2-visitor 'js2-visit-try-node)
-(put 'cl-struct-js2-try-node 'js2-printer 'js2-print-try-node)
-
-(defun js2-visit-try-node (n v)
- (js2-visit-ast (js2-try-node-try-block n) v)
- (dolist (clause (js2-try-node-catch-clauses n))
- (js2-visit-ast clause v))
- (js2-visit-ast (js2-try-node-finally-block n) v))
-
-(defun js2-print-try-node (n i)
- (let ((pad (js2-make-pad i))
- (catches (js2-try-node-catch-clauses n))
- (finally (js2-try-node-finally-block n)))
- (insert pad "try {\n")
- (js2-print-body (js2-try-node-try-block n) (1+ i))
- (insert pad "}")
- (when catches
- (dolist (catch catches)
- (js2-print-ast catch i)))
- (if finally
- (js2-print-ast finally i)
- (insert "\n"))))
-
-(cl-defstruct (js2-catch-node
- (:include js2-scope)
- (:constructor nil)
- (:constructor make-js2-catch-node (&key (type js2-CATCH)
- (pos js2-ts-cursor)
- len
- param
- guard-kwd
- guard-expr
- lp rp)))
- "AST node for a catch clause."
- param ; destructuring form or simple name node
- guard-kwd ; relative buffer position of "if" in "catch (x if ...)"
- guard-expr ; catch condition, a `js2-node'
- lp ; buffer position of left-paren, nil if omitted
- rp) ; buffer position of right-paren, nil if omitted
-
-(put 'cl-struct-js2-catch-node 'js2-visitor 'js2-visit-catch-node)
-(put 'cl-struct-js2-catch-node 'js2-printer 'js2-print-catch-node)
-
-(defun js2-visit-catch-node (n v)
- (js2-visit-ast (js2-catch-node-param n) v)
- (when (js2-catch-node-guard-kwd n)
- (js2-visit-ast (js2-catch-node-guard-expr n) v))
- (js2-visit-block n v))
-
-(defun js2-print-catch-node (n i)
- (let ((pad (js2-make-pad i))
- (guard-kwd (js2-catch-node-guard-kwd n))
- (guard-expr (js2-catch-node-guard-expr n)))
- (insert " catch (")
- (js2-print-ast (js2-catch-node-param n) 0)
- (when guard-kwd
- (insert " if ")
- (js2-print-ast guard-expr 0))
- (insert ") {\n")
- (js2-print-body n (1+ i))
- (insert pad "}")))
-
-(cl-defstruct (js2-finally-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-finally-node (&key (type js2-FINALLY)
- (pos js2-ts-cursor)
- len body)))
- "AST node for a finally clause."
- body) ; a `js2-node', often but not always a block node
-
-(put 'cl-struct-js2-finally-node 'js2-visitor 'js2-visit-finally-node)
-(put 'cl-struct-js2-finally-node 'js2-printer 'js2-print-finally-node)
-
-(defun js2-visit-finally-node (n v)
- (js2-visit-ast (js2-finally-node-body n) v))
-
-(defun js2-print-finally-node (n i)
- (let ((pad (js2-make-pad i)))
- (insert " finally {\n")
- (js2-print-body (js2-finally-node-body n) (1+ i))
- (insert pad "}\n")))
-
-(cl-defstruct (js2-switch-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-switch-node (&key (type js2-SWITCH)
- (pos js2-ts-cursor)
- len
- discriminant
- cases lp
- rp)))
- "AST node for a switch statement."
- discriminant ; a `js2-node' (switch expression)
- cases ; a Lisp list of `js2-case-node'
- lp ; position of open-paren for discriminant, nil if omitted
- rp) ; position of close-paren for discriminant, nil if omitted
-
-(put 'cl-struct-js2-switch-node 'js2-visitor 'js2-visit-switch-node)
-(put 'cl-struct-js2-switch-node 'js2-printer 'js2-print-switch-node)
-
-(defun js2-visit-switch-node (n v)
- (js2-visit-ast (js2-switch-node-discriminant n) v)
- (dolist (c (js2-switch-node-cases n))
- (js2-visit-ast c v)))
-
-(defun js2-print-switch-node (n i)
- (let ((pad (js2-make-pad i))
- (cases (js2-switch-node-cases n)))
- (insert pad "switch (")
- (js2-print-ast (js2-switch-node-discriminant n) 0)
- (insert ") {\n")
- (dolist (case cases)
- (js2-print-ast case i))
- (insert pad "}\n")))
-
-(cl-defstruct (js2-case-node
- (:include js2-block-node)
- (:constructor nil)
- (:constructor make-js2-case-node (&key (type js2-CASE)
- (pos js2-ts-cursor)
- len kids expr)))
- "AST node for a case clause of a switch statement."
- expr) ; the case expression (nil for default)
-
-(put 'cl-struct-js2-case-node 'js2-visitor 'js2-visit-case-node)
-(put 'cl-struct-js2-case-node 'js2-printer 'js2-print-case-node)
-
-(defun js2-visit-case-node (n v)
- (js2-visit-ast (js2-case-node-expr n) v)
- (js2-visit-block n v))
-
-(defun js2-print-case-node (n i)
- (let ((pad (js2-make-pad i))
- (expr (js2-case-node-expr n)))
- (insert pad)
- (if (null expr)
- (insert "default:\n")
- (insert "case ")
- (js2-print-ast expr 0)
- (insert ":\n"))
- (dolist (kid (js2-case-node-kids n))
- (js2-print-ast kid (1+ i)))))
-
-(cl-defstruct (js2-throw-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-throw-node (&key (type js2-THROW)
- (pos js2-ts-cursor)
- len expr)))
- "AST node for a throw statement."
- expr) ; the expression to throw
-
-(put 'cl-struct-js2-throw-node 'js2-visitor 'js2-visit-throw-node)
-(put 'cl-struct-js2-throw-node 'js2-printer 'js2-print-throw-node)
-
-(defun js2-visit-throw-node (n v)
- (js2-visit-ast (js2-throw-node-expr n) v))
-
-(defun js2-print-throw-node (n i)
- (insert (js2-make-pad i) "throw ")
- (js2-print-ast (js2-throw-node-expr n) 0)
- (insert ";\n"))
-
-(cl-defstruct (js2-with-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-with-node (&key (type js2-WITH)
- (pos js2-ts-cursor)
- len object
- body lp rp)))
- "AST node for a with-statement."
- object
- body
- lp ; buffer position of left-paren around object, nil if omitted
- rp) ; buffer position of right-paren around object, nil if omitted
-
-(put 'cl-struct-js2-with-node 'js2-visitor 'js2-visit-with-node)
-(put 'cl-struct-js2-with-node 'js2-printer 'js2-print-with-node)
-
-(defun js2-visit-with-node (n v)
- (js2-visit-ast (js2-with-node-object n) v)
- (js2-visit-ast (js2-with-node-body n) v))
-
-(defun js2-print-with-node (n i)
- (let ((pad (js2-make-pad i)))
- (insert pad "with (")
- (js2-print-ast (js2-with-node-object n) 0)
- (insert ") {\n")
- (js2-print-body (js2-with-node-body n) (1+ i))
- (insert pad "}\n")))
-
-(cl-defstruct (js2-label-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-label-node (&key (type js2-LABEL)
- (pos js2-ts-cursor)
- len name)))
- "AST node for a statement label or case label."
- name ; a string
- loop) ; for validating and code-generating continue-to-label
-
-(put 'cl-struct-js2-label-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-label-node 'js2-printer 'js2-print-label)
-
-(defun js2-print-label (n i)
- (insert (js2-make-pad i)
- (js2-label-node-name n)
- ":\n"))
-
-(cl-defstruct (js2-labeled-stmt-node
- (:include js2-node)
- (:constructor nil)
- ;; type needs to be in `js2-side-effecting-tokens' to avoid
spurious
- ;; no-side-effects warnings, hence js2-EXPR_RESULT.
- (:constructor make-js2-labeled-stmt-node (&key (type
js2-EXPR_RESULT)
- (pos
js2-ts-cursor)
- len labels
stmt)))
- "AST node for a statement with one or more labels.
-Multiple labels for a statement are collapsed into the labels field."
- labels ; Lisp list of `js2-label-node'
- stmt) ; the statement these labels are for
-
-(put 'cl-struct-js2-labeled-stmt-node 'js2-visitor 'js2-visit-labeled-stmt)
-(put 'cl-struct-js2-labeled-stmt-node 'js2-printer 'js2-print-labeled-stmt)
-
-(defun js2-get-label-by-name (lbl-stmt name)
- "Return a `js2-label-node' by NAME from LBL-STMT's labels list.
-Returns nil if no such label is in the list."
- (let ((label-list (js2-labeled-stmt-node-labels lbl-stmt))
- result)
- (while (and label-list (not result))
- (if (string= (js2-label-node-name (car label-list)) name)
- (setq result (car label-list))
- (setq label-list (cdr label-list))))
- result))
-
-(defun js2-visit-labeled-stmt (n v)
- (dolist (label (js2-labeled-stmt-node-labels n))
- (js2-visit-ast label v))
- (js2-visit-ast (js2-labeled-stmt-node-stmt n) v))
-
-(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) i))
-
-(defun js2-labeled-stmt-node-contains (node label)
- "Return t if NODE contains LABEL in its label set.
-NODE is a `js2-labels-node'. LABEL is an identifier."
- (cl-loop for nl in (js2-labeled-stmt-node-labels node)
- if (string= label (js2-label-node-name nl))
- return t
- finally return nil))
-
-(defsubst js2-labeled-stmt-node-add-label (node label)
- "Add a `js2-label-node' to the label set for this statement."
- (setf (js2-labeled-stmt-node-labels node)
- (nconc (js2-labeled-stmt-node-labels node) (list label))))
-
-(cl-defstruct (js2-jump-node
- (:include js2-node)
- (:constructor nil))
- "Abstract supertype of break and continue nodes."
- label ; `js2-name-node' for location of label identifier, if present
- target) ; target js2-labels-node or loop/switch statement
-
-(defun js2-visit-jump-node (n v)
- ;; We don't visit the target, since it's a back-link.
- (js2-visit-ast (js2-jump-node-label n) v))
-
-(cl-defstruct (js2-break-node
- (:include js2-jump-node)
- (:constructor nil)
- (:constructor make-js2-break-node (&key (type js2-BREAK)
- (pos js2-ts-cursor)
- len label target)))
- "AST node for a break statement.
-The label field is a `js2-name-node', possibly nil, for the named label
-if provided. E.g. in 'break foo', it represents 'foo'. The target field
-is the target of the break - a label node or enclosing loop/switch statement.")
-
-(put 'cl-struct-js2-break-node 'js2-visitor 'js2-visit-jump-node)
-(put 'cl-struct-js2-break-node 'js2-printer 'js2-print-break-node)
-
-(defun js2-print-break-node (n i)
- (insert (js2-make-pad i) "break")
- (when (js2-break-node-label n)
- (insert " ")
- (js2-print-ast (js2-break-node-label n) 0))
- (insert ";\n"))
-
-(cl-defstruct (js2-continue-node
- (:include js2-jump-node)
- (:constructor nil)
- (:constructor make-js2-continue-node (&key (type js2-CONTINUE)
- (pos js2-ts-cursor)
- len label target)))
- "AST node for a continue statement.
-The label field is the user-supplied enclosing label name, a `js2-name-node'.
-It is nil if continue specifies no label. The target field is the jump target:
-a `js2-label-node' or the innermost enclosing loop.")
-
-(put 'cl-struct-js2-continue-node 'js2-visitor 'js2-visit-jump-node)
-(put 'cl-struct-js2-continue-node 'js2-printer 'js2-print-continue-node)
-
-(defun js2-print-continue-node (n i)
- (insert (js2-make-pad i) "continue")
- (when (js2-continue-node-label n)
- (insert " ")
- (js2-print-ast (js2-continue-node-label n) 0))
- (insert ";\n"))
-
-(cl-defstruct (js2-function-node
- (:include js2-script-node)
- (:constructor nil)
- (:constructor make-js2-function-node (&key (type js2-FUNCTION)
- (pos js2-ts-cursor)
- len
- (ftype 'FUNCTION)
- (form
'FUNCTION_STATEMENT)
- (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|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
- body ; a `js2-block-node' or expression node (1.8 only)
- lp ; position of arg-list open-paren, or nil if omitted
- 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
- generator-type ; STAR, LEGACY, COMPREHENSION or nil
- member-expr) ; nonstandard Ecma extension from Rhino
-
-(put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node)
-(put 'cl-struct-js2-function-node 'js2-printer 'js2-print-function-node)
-
-(defun js2-visit-function-node (n v)
- (js2-visit-ast (js2-function-node-name n) v)
- (dolist (p (js2-function-node-params n))
- (js2-visit-ast p v))
- (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 (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 "(")
- (cl-loop with len = (length params)
- for param in params
- for count from 1
- do
- (when (and rest-p (= count len))
- (insert "..."))
- (js2-print-ast param 0)
- (when (< count len)
- (insert ", ")))
- (insert ") ")
- (when arrow
- (insert "=> "))
- (insert "{")
- ;; TODO: fix this to be smarter about indenting, etc.
- (unless expr
- (insert "\n"))
- (if (js2-block-node-p body)
- (js2-print-body body (1+ i))
- (js2-print-ast body 0))
- (insert pad "}")
- (unless expr
- (insert "\n"))))
-
-(defun js2-function-name (node)
- "Return function name for NODE, a `js2-function-node', or nil if anonymous."
- (and (js2-function-node-name node)
- (js2-name-node-name (js2-function-node-name node))))
-
-;; Having this be an expression node makes it more flexible.
-;; There are IDE contexts, such as indentation in a for-loop initializer,
-;; that work better if you assume it's an expression. Whenever we have
-;; a standalone var/const declaration, we just wrap with an expr stmt.
-;; Eclipse apparently screwed this up and now has two versions, expr and stmt.
-(cl-defstruct (js2-var-decl-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-var-decl-node (&key (type js2-VAR)
- (pos
(js2-current-token-beg))
- len kids
- decl-type)))
- "AST node for a variable declaration list (VAR, CONST or LET).
-The node bounds differ depending on the declaration type. For VAR or
-CONST declarations, the bounds include the var/const keyword. For LET
-declarations, the node begins at the position of the first child."
- kids ; a Lisp list of `js2-var-init-node' structs.
- decl-type) ; js2-VAR, js2-CONST or js2-LET
-
-(put 'cl-struct-js2-var-decl-node 'js2-visitor 'js2-visit-var-decl)
-(put 'cl-struct-js2-var-decl-node 'js2-printer 'js2-print-var-decl)
-
-(defun js2-visit-var-decl (n v)
- (dolist (kid (js2-var-decl-node-kids n))
- (js2-visit-ast kid v)))
-
-(defun js2-print-var-decl (n i)
- (let ((pad (js2-make-pad i))
- (tt (js2-var-decl-node-decl-type n)))
- (insert pad)
- (insert (cond
- ((= tt js2-VAR) "var ")
- ((= tt js2-LET) "let ")
- ((= tt js2-CONST) "const ")
- (t
- (error "malformed var-decl node"))))
- (cl-loop with kids = (js2-var-decl-node-kids n)
- with len = (length kids)
- for kid in kids
- for count from 1
- do
- (js2-print-ast kid 0)
- (if (< count len)
- (insert ", ")))))
-
-(cl-defstruct (js2-var-init-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-var-init-node (&key (type js2-VAR)
- (pos js2-ts-cursor)
- len target
- initializer)))
- "AST node for a variable declaration.
-The type field will be js2-CONST for a const decl."
- target ; `js2-name-node', `js2-object-node', or `js2-array-node'
- initializer) ; initializer expression, a `js2-node'
-
-(put 'cl-struct-js2-var-init-node 'js2-visitor 'js2-visit-var-init-node)
-(put 'cl-struct-js2-var-init-node 'js2-printer 'js2-print-var-init-node)
-
-(defun js2-visit-var-init-node (n v)
- (js2-visit-ast (js2-var-init-node-target n) v)
- (js2-visit-ast (js2-var-init-node-initializer n) v))
-
-(defun js2-print-var-init-node (n i)
- (let ((pad (js2-make-pad i))
- (name (js2-var-init-node-target n))
- (init (js2-var-init-node-initializer n)))
- (insert pad)
- (js2-print-ast name 0)
- (when init
- (insert " = ")
- (js2-print-ast init 0))))
-
-(cl-defstruct (js2-cond-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-cond-node (&key (type js2-HOOK)
- (pos js2-ts-cursor)
- len
- test-expr
- true-expr
- false-expr
- q-pos c-pos)))
- "AST node for the ternary operator"
- test-expr
- true-expr
- false-expr
- q-pos ; buffer position of ?
- c-pos) ; buffer position of :
-
-(put 'cl-struct-js2-cond-node 'js2-visitor 'js2-visit-cond-node)
-(put 'cl-struct-js2-cond-node 'js2-printer 'js2-print-cond-node)
-
-(defun js2-visit-cond-node (n v)
- (js2-visit-ast (js2-cond-node-test-expr n) v)
- (js2-visit-ast (js2-cond-node-true-expr n) v)
- (js2-visit-ast (js2-cond-node-false-expr n) v))
-
-(defun js2-print-cond-node (n i)
- (let ((pad (js2-make-pad i)))
- (insert pad)
- (js2-print-ast (js2-cond-node-test-expr n) 0)
- (insert " ? ")
- (js2-print-ast (js2-cond-node-true-expr n) 0)
- (insert " : ")
- (js2-print-ast (js2-cond-node-false-expr n) 0)))
-
-(cl-defstruct (js2-infix-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-infix-node (&key type
- (pos js2-ts-cursor)
- len op-pos
- left right)))
- "Represents infix expressions.
-Includes assignment ops like `|=', and the comma operator.
-The type field inherited from `js2-node' holds the operator."
- op-pos ; buffer position where operator begins
- left ; any `js2-node'
- right) ; any `js2-node'
-
-(put 'cl-struct-js2-infix-node 'js2-visitor 'js2-visit-infix-node)
-(put 'cl-struct-js2-infix-node 'js2-printer 'js2-print-infix-node)
-
-(defun js2-visit-infix-node (n v)
- (js2-visit-ast (js2-infix-node-left n) v)
- (js2-visit-ast (js2-infix-node-right n) v))
-
-(defconst js2-operator-tokens
- (let ((table (make-hash-table :test 'eq))
- (tokens
- (list (cons js2-IN "in")
- (cons js2-TYPEOF "typeof")
- (cons js2-INSTANCEOF "instanceof")
- (cons js2-DELPROP "delete")
- (cons js2-COMMA ",")
- (cons js2-COLON ":")
- (cons js2-OR "||")
- (cons js2-AND "&&")
- (cons js2-INC "++")
- (cons js2-DEC "--")
- (cons js2-BITOR "|")
- (cons js2-BITXOR "^")
- (cons js2-BITAND "&")
- (cons js2-EQ "==")
- (cons js2-NE "!=")
- (cons js2-LT "<")
- (cons js2-LE "<=")
- (cons js2-GT ">")
- (cons js2-GE ">=")
- (cons js2-LSH "<<")
- (cons js2-RSH ">>")
- (cons js2-URSH ">>>")
- (cons js2-ADD "+") ; infix plus
- (cons js2-SUB "-") ; infix minus
- (cons js2-MUL "*")
- (cons js2-DIV "/")
- (cons js2-MOD "%")
- (cons js2-NOT "!")
- (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 "=")
- (cons js2-ASSIGN_BITOR "|=")
- (cons js2-ASSIGN_BITXOR "^=")
- (cons js2-ASSIGN_BITAND "&=")
- (cons js2-ASSIGN_LSH "<<=")
- (cons js2-ASSIGN_RSH ">>=")
- (cons js2-ASSIGN_URSH ">>>=")
- (cons js2-ASSIGN_ADD "+=")
- (cons js2-ASSIGN_SUB "-=")
- (cons js2-ASSIGN_MUL "*=")
- (cons js2-ASSIGN_DIV "/=")
- (cons js2-ASSIGN_MOD "%="))))
- (cl-loop for (k . v) in tokens do
- (puthash k v table))
- table))
-
-(defun js2-print-infix-node (n i)
- (let* ((tt (js2-node-type n))
- (op (gethash tt js2-operator-tokens)))
- (unless op
- (error "unrecognized infix operator %s" (js2-node-type n)))
- (insert (js2-make-pad i))
- (js2-print-ast (js2-infix-node-left n) 0)
- (unless (= tt js2-COMMA)
- (insert " "))
- (insert op)
- (insert " ")
- (js2-print-ast (js2-infix-node-right n) 0)))
-
-(cl-defstruct (js2-assign-node
- (:include js2-infix-node)
- (:constructor nil)
- (:constructor make-js2-assign-node (&key type
- (pos js2-ts-cursor)
- len op-pos
- left right)))
- "Represents any assignment.
-The type field holds the actual assignment operator.")
-
-(put 'cl-struct-js2-assign-node 'js2-visitor 'js2-visit-infix-node)
-(put 'cl-struct-js2-assign-node 'js2-printer 'js2-print-infix-node)
-
-(cl-defstruct (js2-unary-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-unary-node (&key type ; required
- (pos js2-ts-cursor)
- len operand)))
- "AST node type for unary operator nodes.
-The type field can be NOT, BITNOT, POS, NEG, INC, DEC,
-TYPEOF, DELPROP or TRIPLEDOT. For INC or DEC, a 'postfix node
-property is added if the operator follows the operand."
- operand) ; a `js2-node' expression
-
-(put 'cl-struct-js2-unary-node 'js2-visitor 'js2-visit-unary-node)
-(put 'cl-struct-js2-unary-node 'js2-printer 'js2-print-unary-node)
-
-(defun js2-visit-unary-node (n v)
- (js2-visit-ast (js2-unary-node-operand n) v))
-
-(defun js2-print-unary-node (n i)
- (let* ((tt (js2-node-type n))
- (op (gethash tt js2-operator-tokens))
- (postfix (js2-node-get-prop n 'postfix)))
- (unless op
- (error "unrecognized unary operator %s" tt))
- (insert (js2-make-pad i))
- (unless postfix
- (insert op))
- (if (or (= tt js2-TYPEOF)
- (= tt js2-DELPROP))
- (insert " "))
- (js2-print-ast (js2-unary-node-operand n) 0)
- (when postfix
- (insert op))))
-
-(cl-defstruct (js2-let-node
- (:include js2-scope)
- (:constructor nil)
- (:constructor make-js2-let-node (&key (type js2-LETEXPR)
- (pos
(js2-current-token-beg))
- len vars body
- lp rp)))
- "AST node for a let expression or a let statement.
-Note that a let declaration such as let x=6, y=7 is a `js2-var-decl-node'."
- vars ; a `js2-var-decl-node'
- body ; a `js2-node' representing the expression or body block
- lp
- rp)
-
-(put 'cl-struct-js2-let-node 'js2-visitor 'js2-visit-let-node)
-(put 'cl-struct-js2-let-node 'js2-printer 'js2-print-let-node)
-
-(defun js2-visit-let-node (n v)
- (js2-visit-ast (js2-let-node-vars n) v)
- (js2-visit-ast (js2-let-node-body n) v))
-
-(defun js2-print-let-node (n i)
- (insert (js2-make-pad i) "let (")
- (let ((p (point)))
- (js2-print-ast (js2-let-node-vars n) 0)
- (delete-region p (+ p 4)))
- (insert ") ")
- (js2-print-ast (js2-let-node-body n) i))
-
-(cl-defstruct (js2-keyword-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-keyword-node (&key type
- (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'.
-The node type is set to js2-NULL, js2-THIS, etc.")
-
-(put 'cl-struct-js2-keyword-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-keyword-node 'js2-printer 'js2-print-keyword-node)
-
-(defun js2-print-keyword-node (n i)
- (insert (js2-make-pad i)
- (let ((tt (js2-node-type n)))
- (cond
- ((= tt js2-THIS) "this")
- ((= tt js2-SUPER) "super")
- ((= tt js2-NULL) "null")
- ((= tt js2-TRUE) "true")
- ((= tt js2-FALSE) "false")
- ((= tt js2-DEBUGGER) "debugger")
- (t (error "Invalid keyword literal type: %d" tt))))))
-
-(defsubst js2-this-or-super-node-p (node)
- "Return t if NODE is a `js2-literal-node' of type js2-THIS or js2-SUPER."
- (let ((type (js2-node-type node)))
- (or (eq type js2-THIS) (eq type js2-SUPER))))
-
-(cl-defstruct (js2-new-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-new-node (&key (type js2-NEW)
- (pos
(js2-current-token-beg))
- len target
- args initializer
- lp rp)))
- "AST node for new-expression such as new Foo()."
- target ; an identifier or reference
- args ; a Lisp list of argument nodes
- lp ; position of left-paren, nil if omitted
- rp ; position of right-paren, nil if omitted
- initializer) ; experimental Rhino syntax: optional `js2-object-node'
-
-(put 'cl-struct-js2-new-node 'js2-visitor 'js2-visit-new-node)
-(put 'cl-struct-js2-new-node 'js2-printer 'js2-print-new-node)
-
-(defun js2-visit-new-node (n v)
- (js2-visit-ast (js2-new-node-target n) v)
- (dolist (arg (js2-new-node-args n))
- (js2-visit-ast arg v))
- (js2-visit-ast (js2-new-node-initializer n) v))
-
-(defun js2-print-new-node (n i)
- (insert (js2-make-pad i) "new ")
- (js2-print-ast (js2-new-node-target n))
- (insert "(")
- (js2-print-list (js2-new-node-args n))
- (insert ")")
- (when (js2-new-node-initializer n)
- (insert " ")
- (js2-print-ast (js2-new-node-initializer n))))
-
-(cl-defstruct (js2-name-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-name-node (&key (type js2-NAME)
- (pos
(js2-current-token-beg))
- (len (- js2-ts-cursor
-
(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)
-
-(put 'cl-struct-js2-name-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-name-node 'js2-printer 'js2-print-name-node)
-
-(defun js2-print-name-node (n i)
- (insert (js2-make-pad i)
- (js2-name-node-name n)))
-
-(defsubst js2-name-node-length (node)
- "Return identifier length of NODE, a `js2-name-node'.
-Returns 0 if NODE is nil or its identifier field is nil."
- (if node
- (length (js2-name-node-name node))
- 0))
-
-(cl-defstruct (js2-number-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-number-node (&key (type js2-NUMBER)
- (pos
(js2-current-token-beg))
- (len (- js2-ts-cursor
-
(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
-
-(put 'cl-struct-js2-number-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-number-node 'js2-printer 'js2-print-number-node)
-
-(defun js2-print-number-node (n i)
- (insert (js2-make-pad i)
- (number-to-string (js2-number-node-num-value n))))
-
-(cl-defstruct (js2-regexp-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-regexp-node (&key (type js2-REGEXP)
- (pos
(js2-current-token-beg))
- (len (- js2-ts-cursor
-
(js2-current-token-beg)))
- value flags)))
- "AST node for a regular expression literal."
- value ; the regexp string, without // delimiters
- flags) ; a string of flags, e.g. `mi'.
-
-(put 'cl-struct-js2-regexp-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-regexp-node 'js2-printer 'js2-print-regexp)
-
-(defun js2-print-regexp (n i)
- (insert (js2-make-pad i)
- "/"
- (js2-regexp-node-value n)
- "/")
- (if (js2-regexp-node-flags n)
- (insert (js2-regexp-node-flags n))))
-
-(cl-defstruct (js2-string-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-string-node (&key (type js2-STRING)
- (pos
(js2-current-token-beg))
- (len (- js2-ts-cursor
-
(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."
- value) ; the characters of the string, including the quotes
-
-(put 'cl-struct-js2-string-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-string-node 'js2-printer 'js2-print-string-node)
-
-(defun js2-print-string-node (n i)
- (insert (js2-make-pad i)
- (js2-node-string n)))
-
-(cl-defstruct (js2-template-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-template-node (&key (type
js2-TEMPLATE_HEAD)
- beg len kids)))
- "Template literal."
- kids) ; `js2-string-node' is used for string segments, other nodes
- ; for substitutions inside.
-
-(put 'cl-struct-js2-template-node 'js2-visitor 'js2-visit-template)
-(put 'cl-struct-js2-template-node 'js2-printer 'js2-print-template)
-
-(defun js2-visit-template (n callback)
- (dolist (kid (js2-template-node-kids n))
- (js2-visit-ast kid callback)))
-
-(defun js2-print-template (n i)
- (insert (js2-make-pad i))
- (dolist (kid (js2-template-node-kids n))
- (if (js2-string-node-p kid)
- (insert (js2-node-string kid))
- (js2-print-ast kid))))
-
-(cl-defstruct (js2-tagged-template-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-tagged-template-node (&key (type
js2-TAGGED_TEMPLATE)
- beg len tag
template)))
- "Tagged template literal."
- tag ; `js2-node' with the tag expression.
- template) ; `js2-template-node' with the template.
-
-(put 'cl-struct-js2-tagged-template-node 'js2-visitor
'js2-visit-tagged-template)
-(put 'cl-struct-js2-tagged-template-node 'js2-printer
'js2-print-tagged-template)
-
-(defun js2-visit-tagged-template (n callback)
- (js2-visit-ast (js2-tagged-template-node-tag n) callback)
- (js2-visit-ast (js2-tagged-template-node-template n) callback))
-
-(defun js2-print-tagged-template (n i)
- (insert (js2-make-pad i))
- (js2-print-ast (js2-tagged-template-node-tag n))
- (js2-print-ast (js2-tagged-template-node-template n)))
-
-(cl-defstruct (js2-array-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-array-node (&key (type js2-ARRAYLIT)
- (pos js2-ts-cursor)
- len elems)))
- "AST node for an array literal."
- elems) ; list of expressions. [foo,,bar] yields a nil middle element.
-
-(put 'cl-struct-js2-array-node 'js2-visitor 'js2-visit-array-node)
-(put 'cl-struct-js2-array-node 'js2-printer 'js2-print-array-node)
-
-(defun js2-visit-array-node (n v)
- (dolist (e (js2-array-node-elems n))
- (js2-visit-ast e v))) ; Can be nil; e.g. [a, ,b].
-
-(defun js2-print-array-node (n i)
- (insert (js2-make-pad i) "[")
- (let ((elems (js2-array-node-elems n)))
- (js2-print-list elems)
- (when (and elems (null (car (last elems))))
- (insert ",")))
- (insert "]"))
-
-(cl-defstruct (js2-class-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-class-node (&key (type js2-CLASS)
- (pos js2-ts-cursor)
- (form 'CLASS_STATEMENT)
- (name "")
- extends len elems)))
- "AST node for an class expression.
-`elems' is a list of `js2-object-prop-node', and `extends' is an
-optional `js2-expr-node'"
- form ; CLASS_{STATEMENT|EXPRESSION}
- name ; class name (a `js2-node-name', or nil if anonymous)
- extends ; class heritage (a `js2-expr-node', or nil if none)
- elems)
-
-(put 'cl-struct-js2-class-node 'js2-visitor 'js2-visit-class-node)
-(put 'cl-struct-js2-class-node 'js2-printer 'js2-print-class-node)
-
-(defun js2-visit-class-node (n v)
- (js2-visit-ast (js2-class-node-name n) v)
- (js2-visit-ast (js2-class-node-extends n) v)
- (dolist (e (js2-class-node-elems n))
- (js2-visit-ast e v)))
-
-(defun js2-print-class-node (n i)
- (let* ((pad (js2-make-pad i))
- (name (js2-class-node-name n))
- (extends (js2-class-node-extends n))
- (elems (js2-class-node-elems n)))
- (insert pad "class")
- (when name
- (insert " ")
- (js2-print-ast name 0))
- (when extends
- (insert " extends ")
- (js2-print-ast extends))
- (insert " {")
- (dolist (elem elems)
- (insert "\n")
- (if (js2-node-get-prop elem 'STATIC)
- (progn (insert (js2-make-pad (1+ i)) "static ")
- (js2-print-ast elem 0)) ;; TODO(sdh): indentation isn't quite
right
- (js2-print-ast elem (1+ i))))
- (insert "\n" pad "}")))
-
-(cl-defstruct (js2-object-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-object-node (&key (type js2-OBJECTLIT)
- (pos js2-ts-cursor)
- len
- elems)))
- "AST node for an object literal expression.
-`elems' is a list of `js2-object-prop-node'."
- elems)
-
-(put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
-(put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node)
-
-(defun js2-visit-object-node (n v)
- (dolist (e (js2-object-node-elems n))
- (js2-visit-ast e v)))
-
-(defun js2-print-object-node (n i)
- (insert (js2-make-pad i) "{")
- (js2-print-list (js2-object-node-elems n))
- (insert "}"))
-
-(cl-defstruct (js2-object-prop-node
- (:include js2-infix-node)
- (:constructor nil)
- (:constructor make-js2-object-prop-node (&key (type js2-COLON)
- (pos
js2-ts-cursor)
- len left
- right op-pos)))
- "AST node for an object literal prop:value entry.
-The `left' field is the property: a name node, string node or number node.
-The `right' field is a `js2-node' representing the initializer value.
-If the property is abbreviated, the node's `SHORTHAND' property is non-nil
-and both fields have the same value.")
-
-(put 'cl-struct-js2-object-prop-node 'js2-visitor 'js2-visit-infix-node)
-(put 'cl-struct-js2-object-prop-node 'js2-printer 'js2-print-object-prop-node)
-
-(defun js2-print-object-prop-node (n i)
- (let* ((left (js2-object-prop-node-left n))
- (computed (not (or (js2-string-node-p left)
- (js2-number-node-p left)
- (js2-name-node-p left)))))
- (insert (js2-make-pad i))
- (if computed
- (insert "["))
- (js2-print-ast left 0)
- (if computed
- (insert "]"))
- (if (not (js2-node-get-prop n 'SHORTHAND))
- (progn
- (insert ": ")
- (js2-print-ast (js2-object-prop-node-right n) 0)))))
-
-(cl-defstruct (js2-getter-setter-node
- (:include js2-infix-node)
- (:constructor nil)
- (:constructor make-js2-getter-setter-node (&key type ; GET,
SET, or FUNCTION
- (pos
js2-ts-cursor)
- len left
right)))
- "AST node for a getter/setter property in an object literal.
-The `left' field is the `js2-name-node' naming the getter/setter prop.
-The `right' field is always an anonymous `js2-function-node' with a node
-property `GETTER_SETTER' set to js2-GET, js2-SET, or js2-FUNCTION. ")
-
-(put 'cl-struct-js2-getter-setter-node 'js2-visitor 'js2-visit-infix-node)
-(put 'cl-struct-js2-getter-setter-node 'js2-printer 'js2-print-getter-setter)
-
-(defun js2-print-getter-setter (n i)
- (let ((pad (js2-make-pad i))
- (left (js2-getter-setter-node-left n))
- (right (js2-getter-setter-node-right n)))
- (insert pad)
- (if (/= (js2-node-type n) js2-FUNCTION)
- (insert (if (= (js2-node-type n) js2-GET) "get " "set ")))
- (js2-print-ast left 0)
- (js2-print-ast right 0)))
-
-(cl-defstruct (js2-prop-get-node
- (:include js2-infix-node)
- (:constructor nil)
- (:constructor make-js2-prop-get-node (&key (type js2-GETPROP)
- (pos js2-ts-cursor)
- len left right)))
- "AST node for a dotted property reference, e.g. foo.bar or foo().bar")
-
-(put 'cl-struct-js2-prop-get-node 'js2-visitor 'js2-visit-prop-get-node)
-(put 'cl-struct-js2-prop-get-node 'js2-printer 'js2-print-prop-get-node)
-
-(defun js2-visit-prop-get-node (n v)
- (js2-visit-ast (js2-prop-get-node-left n) v)
- (js2-visit-ast (js2-prop-get-node-right n) v))
-
-(defun js2-print-prop-get-node (n i)
- (insert (js2-make-pad i))
- (js2-print-ast (js2-prop-get-node-left n) 0)
- (insert ".")
- (js2-print-ast (js2-prop-get-node-right n) 0))
-
-(cl-defstruct (js2-elem-get-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-elem-get-node (&key (type js2-GETELEM)
- (pos js2-ts-cursor)
- len target element
- lb rb)))
- "AST node for an array index expression such as foo[bar]."
- target ; a `js2-node' - the expression preceding the "."
- element ; a `js2-node' - the expression in brackets
- lb ; position of left-bracket, nil if omitted
- rb) ; position of right-bracket, nil if omitted
-
-(put 'cl-struct-js2-elem-get-node 'js2-visitor 'js2-visit-elem-get-node)
-(put 'cl-struct-js2-elem-get-node 'js2-printer 'js2-print-elem-get-node)
-
-(defun js2-visit-elem-get-node (n v)
- (js2-visit-ast (js2-elem-get-node-target n) v)
- (js2-visit-ast (js2-elem-get-node-element n) v))
-
-(defun js2-print-elem-get-node (n i)
- (insert (js2-make-pad i))
- (js2-print-ast (js2-elem-get-node-target n) 0)
- (insert "[")
- (js2-print-ast (js2-elem-get-node-element n) 0)
- (insert "]"))
-
-(cl-defstruct (js2-call-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-call-node (&key (type js2-CALL)
- (pos js2-ts-cursor)
- len target args
- lp rp)))
- "AST node for a JavaScript function call."
- target ; a `js2-node' evaluating to the function to call
- args ; a Lisp list of `js2-node' arguments
- lp ; position of open-paren, or nil if missing
- rp) ; position of close-paren, or nil if missing
-
-(put 'cl-struct-js2-call-node 'js2-visitor 'js2-visit-call-node)
-(put 'cl-struct-js2-call-node 'js2-printer 'js2-print-call-node)
-
-(defun js2-visit-call-node (n v)
- (js2-visit-ast (js2-call-node-target n) v)
- (dolist (arg (js2-call-node-args n))
- (js2-visit-ast arg v)))
-
-(defun js2-print-call-node (n i)
- (insert (js2-make-pad i))
- (js2-print-ast (js2-call-node-target n) 0)
- (insert "(")
- (js2-print-list (js2-call-node-args n))
- (insert ")"))
-
-(cl-defstruct (js2-yield-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-yield-node (&key (type js2-YIELD)
- (pos js2-ts-cursor)
- len value star-p)))
- "AST node for yield statement or expression."
- star-p ; whether it's yield*
- value) ; optional: value to be yielded
-
-(put 'cl-struct-js2-yield-node 'js2-visitor 'js2-visit-yield-node)
-(put 'cl-struct-js2-yield-node 'js2-printer 'js2-print-yield-node)
-
-(defun js2-visit-yield-node (n v)
- (js2-visit-ast (js2-yield-node-value n) v))
-
-(defun js2-print-yield-node (n i)
- (insert (js2-make-pad i))
- (insert "yield")
- (when (js2-yield-node-star-p n)
- (insert "*"))
- (when (js2-yield-node-value n)
- (insert " ")
- (js2-print-ast (js2-yield-node-value n) 0)))
-
-(cl-defstruct (js2-paren-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-paren-node (&key (type js2-LP)
- (pos js2-ts-cursor)
- len expr)))
- "AST node for a parenthesized expression.
-In particular, used when the parens are syntactically optional,
-as opposed to required parens such as those enclosing an if-conditional."
- expr) ; `js2-node'
-
-(put 'cl-struct-js2-paren-node 'js2-visitor 'js2-visit-paren-node)
-(put 'cl-struct-js2-paren-node 'js2-printer 'js2-print-paren-node)
-
-(defun js2-visit-paren-node (n v)
- (js2-visit-ast (js2-paren-node-expr n) v))
-
-(defun js2-print-paren-node (n i)
- (insert (js2-make-pad i))
- (insert "(")
- (js2-print-ast (js2-paren-node-expr n) 0)
- (insert ")"))
-
-(cl-defstruct (js2-comp-node
- (:include js2-scope)
- (:constructor nil)
- (:constructor make-js2-comp-node (&key (type js2-ARRAYCOMP)
- (pos js2-ts-cursor)
- len result
- loops filters
- form)))
- "AST node for an Array comprehension such as [[x,y] for (x in foo) for (y in
bar)]."
- result ; result expression (just after left-bracket)
- loops ; a Lisp list of `js2-comp-loop-node'
- filters ; a Lisp list of guard/filter expressions
- form ; ARRAY, LEGACY_ARRAY or STAR_GENERATOR
- ; SpiderMonkey also supports "legacy generator expressions", but we
dont.
- )
-
-(put 'cl-struct-js2-comp-node 'js2-visitor 'js2-visit-comp-node)
-(put 'cl-struct-js2-comp-node 'js2-printer 'js2-print-comp-node)
-
-(defun js2-visit-comp-node (n v)
- (js2-visit-ast (js2-comp-node-result n) v)
- (dolist (l (js2-comp-node-loops n))
- (js2-visit-ast l v))
- (dolist (f (js2-comp-node-filters n))
- (js2-visit-ast f v)))
-
-(defun js2-print-comp-node (n i)
- (let ((pad (js2-make-pad i))
- (result (js2-comp-node-result n))
- (loops (js2-comp-node-loops n))
- (filters (js2-comp-node-filters n))
- (legacy-p (eq (js2-comp-node-form n) 'LEGACY_ARRAY))
- (gen-p (eq (js2-comp-node-form n) 'STAR_GENERATOR)))
- (insert pad (if gen-p "(" "["))
- (when legacy-p
- (js2-print-ast result 0))
- (dolist (l loops)
- (when legacy-p
- (insert " "))
- (js2-print-ast l 0)
- (unless legacy-p
- (insert " ")))
- (dolist (f filters)
- (when legacy-p
- (insert " "))
- (insert "if (")
- (js2-print-ast f 0)
- (insert ")")
- (unless legacy-p
- (insert " ")))
- (unless legacy-p
- (js2-print-ast result 0))
- (insert (if gen-p ")" "]"))))
-
-(cl-defstruct (js2-comp-loop-node
- (:include js2-for-in-node)
- (:constructor nil)
- (:constructor make-js2-comp-loop-node (&key (type js2-FOR)
- (pos js2-ts-cursor)
- len iterator
- object in-pos
- foreach-p
- each-pos
- forof-p
- lp rp)))
- "AST subtree for each 'for (foo in bar)' loop in an array comprehension.")
-
-(put 'cl-struct-js2-comp-loop-node 'js2-visitor 'js2-visit-comp-loop)
-(put 'cl-struct-js2-comp-loop-node 'js2-printer 'js2-print-comp-loop)
-
-(defun js2-visit-comp-loop (n v)
- (js2-visit-ast (js2-comp-loop-node-iterator n) v)
- (js2-visit-ast (js2-comp-loop-node-object n) v))
-
-(defun js2-print-comp-loop (n _i)
- (insert "for ")
- (when (js2-comp-loop-node-foreach-p n) (insert "each "))
- (insert "(")
- (js2-print-ast (js2-comp-loop-node-iterator n) 0)
- (insert (if (js2-comp-loop-node-forof-p n)
- " of " " in "))
- (js2-print-ast (js2-comp-loop-node-object n) 0)
- (insert ")"))
-
-(cl-defstruct (js2-empty-expr-node
- (:include js2-node)
- (:constructor nil)
- (:constructor make-js2-empty-expr-node (&key (type js2-EMPTY)
- (pos
(js2-current-token-beg))
- len)))
- "AST node for an empty expression.")
-
-(put 'cl-struct-js2-empty-expr-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-empty-expr-node 'js2-printer 'js2-print-none)
-
-(cl-defstruct (js2-xml-node
- (:include js2-block-node)
- (:constructor nil)
- (:constructor make-js2-xml-node (&key (type js2-XML)
- (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
-a `js2-xml-js-expr-node'. Equivalent to Rhino's XmlLiteral node.")
-
-(put 'cl-struct-js2-xml-node 'js2-visitor 'js2-visit-block)
-(put 'cl-struct-js2-xml-node 'js2-printer 'js2-print-xml-node)
-
-(defun js2-print-xml-node (n i)
- (dolist (kid (js2-xml-node-kids n))
- (js2-print-ast kid i)))
-
-(cl-defstruct (js2-xml-js-expr-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-xml-js-expr-node (&key (type js2-XML)
- (pos
js2-ts-cursor)
- len expr)))
- "AST node for an embedded JavaScript {expression} in an E4X literal.
-The start and end fields correspond to the curly-braces."
- expr) ; a `js2-expr-node' of some sort
-
-(put 'cl-struct-js2-xml-js-expr-node 'js2-visitor 'js2-visit-xml-js-expr)
-(put 'cl-struct-js2-xml-js-expr-node 'js2-printer 'js2-print-xml-js-expr)
-
-(defun js2-visit-xml-js-expr (n v)
- (js2-visit-ast (js2-xml-js-expr-node-expr n) v))
-
-(defun js2-print-xml-js-expr (n i)
- (insert (js2-make-pad i))
- (insert "{")
- (js2-print-ast (js2-xml-js-expr-node-expr n) 0)
- (insert "}"))
-
-(cl-defstruct (js2-xml-dot-query-node
- (:include js2-infix-node)
- (:constructor nil)
- (:constructor make-js2-xml-dot-query-node (&key (type
js2-DOTQUERY)
- (pos
js2-ts-cursor)
- op-pos len left
- right rp)))
- "AST node for an E4X foo.(bar) filter expression.
-Note that the left-paren is automatically the character immediately
-following the dot (.) in the operator. No whitespace is permitted
-between the dot and the lp by the scanner."
- rp)
-
-(put 'cl-struct-js2-xml-dot-query-node 'js2-visitor 'js2-visit-infix-node)
-(put 'cl-struct-js2-xml-dot-query-node 'js2-printer 'js2-print-xml-dot-query)
-
-(defun js2-print-xml-dot-query (n i)
- (insert (js2-make-pad i))
- (js2-print-ast (js2-xml-dot-query-node-left n) 0)
- (insert ".(")
- (js2-print-ast (js2-xml-dot-query-node-right n) 0)
- (insert ")"))
-
-(cl-defstruct (js2-xml-ref-node
- (:include js2-node)
- (:constructor nil)) ; abstract
- "Base type for E4X XML attribute-access or property-get expressions.
-Such expressions can take a variety of forms. The general syntax has
-three parts:
-
- - (optional) an @ (specifying an attribute access)
- - (optional) a namespace (a `js2-name-node') and double-colon
- - (required) either a `js2-name-node' or a bracketed [expression]
-
-The property-name expressions (examples: ns::name, @name) are
-represented as `js2-xml-prop-ref' nodes. The bracketed-expression
-versions (examples: ns::[name], @[name]) become `js2-xml-elem-ref' nodes.
-
-This node type (or more specifically, its subclasses) will sometimes
-be the right-hand child of a `js2-prop-get-node' or a
-`js2-infix-node' of type `js2-DOTDOT', the .. xml-descendants operator.
-The `js2-xml-ref-node' may also be a standalone primary expression with
-no explicit target, which is valid in certain expression contexts such as
-
- company..employee.(@id < 100)
-
-in this case, the @id is a `js2-xml-ref' that is part of an infix '<'
-expression whose parent is a `js2-xml-dot-query-node'."
- namespace
- at-pos
- colon-pos)
-
-(defsubst js2-xml-ref-node-attr-access-p (node)
- "Return non-nil if this expression began with an @-token."
- (and (numberp (js2-xml-ref-node-at-pos node))
- (cl-plusp (js2-xml-ref-node-at-pos node))))
-
-(cl-defstruct (js2-xml-prop-ref-node
- (:include js2-xml-ref-node)
- (:constructor nil)
- (:constructor make-js2-xml-prop-ref-node (&key (type
js2-REF_NAME)
- (pos
(js2-current-token-beg))
- len propname
- namespace at-pos
- colon-pos)))
- "AST node for an E4X XML [expr] property-ref expression.
-The JavaScript syntax is an optional @, an optional ns::, and a name.
-
- [ '@' ] [ name '::' ] name
-
-Examples include name, ns::name, ns::*, *::name, *::*, @attr, @ns::attr,
address@hidden::*, @*::attr, @*::*, and @*.
-
-The node starts at the @ token, if present. Otherwise it starts at the
-namespace name. The node bounds extend through the closing right-bracket,
-or if it is missing due to a syntax error, through the end of the index
-expression."
- propname)
-
-(put 'cl-struct-js2-xml-prop-ref-node 'js2-visitor
'js2-visit-xml-prop-ref-node)
-(put 'cl-struct-js2-xml-prop-ref-node 'js2-printer
'js2-print-xml-prop-ref-node)
-
-(defun js2-visit-xml-prop-ref-node (n v)
- (js2-visit-ast (js2-xml-prop-ref-node-namespace n) v)
- (js2-visit-ast (js2-xml-prop-ref-node-propname n) v))
-
-(defun js2-print-xml-prop-ref-node (n i)
- (insert (js2-make-pad i))
- (if (js2-xml-ref-node-attr-access-p n)
- (insert "@"))
- (when (js2-xml-prop-ref-node-namespace n)
- (js2-print-ast (js2-xml-prop-ref-node-namespace n) 0)
- (insert "::"))
- (if (js2-xml-prop-ref-node-propname n)
- (js2-print-ast (js2-xml-prop-ref-node-propname n) 0)))
-
-(cl-defstruct (js2-xml-elem-ref-node
- (:include js2-xml-ref-node)
- (:constructor nil)
- (:constructor make-js2-xml-elem-ref-node (&key (type
js2-REF_MEMBER)
- (pos
(js2-current-token-beg))
- len expr lb rb
- namespace at-pos
- colon-pos)))
- "AST node for an E4X XML [expr] member-ref expression.
-Syntax:
-
- [ '@' ] [ name '::' ] '[' expr ']'
-
-Examples include ns::[expr], @ns::[expr], @[expr], *::[expr] and @*::[expr].
-
-Note that the form [expr] (i.e. no namespace or attribute-qualifier)
-is not a legal E4X XML element-ref expression, since it's already used
-for standard JavaScript element-get array indexing. Hence, a
-`js2-xml-elem-ref-node' always has either the attribute-qualifier, a
-non-nil namespace node, or both.
-
-The node starts at the @ token, if present. Otherwise it starts
-at the namespace name. The node bounds extend through the closing
-right-bracket, or if it is missing due to a syntax error, through the
-end of the index expression."
- expr ; the bracketed index expression
- lb
- rb)
-
-(put 'cl-struct-js2-xml-elem-ref-node 'js2-visitor
'js2-visit-xml-elem-ref-node)
-(put 'cl-struct-js2-xml-elem-ref-node 'js2-printer
'js2-print-xml-elem-ref-node)
-
-(defun js2-visit-xml-elem-ref-node (n v)
- (js2-visit-ast (js2-xml-elem-ref-node-namespace n) v)
- (js2-visit-ast (js2-xml-elem-ref-node-expr n) v))
-
-(defun js2-print-xml-elem-ref-node (n i)
- (insert (js2-make-pad i))
- (if (js2-xml-ref-node-attr-access-p n)
- (insert "@"))
- (when (js2-xml-elem-ref-node-namespace n)
- (js2-print-ast (js2-xml-elem-ref-node-namespace n) 0)
- (insert "::"))
- (insert "[")
- (if (js2-xml-elem-ref-node-expr n)
- (js2-print-ast (js2-xml-elem-ref-node-expr n) 0))
- (insert "]"))
-
-;;; Placeholder nodes for when we try parsing the XML literals structurally.
-
-(cl-defstruct (js2-xml-start-tag-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-xml-start-tag-node (&key (type js2-XML)
- (pos
js2-ts-cursor)
- len name attrs
kids
- empty-p)))
- "AST node for an XML start-tag. Not currently used.
-The `kids' field is a Lisp list of child content nodes."
- name ; a `js2-xml-name-node'
- attrs ; a Lisp list of `js2-xml-attr-node'
- empty-p) ; t if this is an empty element such as <foo bar="baz"/>
-
-(put 'cl-struct-js2-xml-start-tag-node 'js2-visitor 'js2-visit-xml-start-tag)
-(put 'cl-struct-js2-xml-start-tag-node 'js2-printer 'js2-print-xml-start-tag)
-
-(defun js2-visit-xml-start-tag (n v)
- (js2-visit-ast (js2-xml-start-tag-node-name n) v)
- (dolist (attr (js2-xml-start-tag-node-attrs n))
- (js2-visit-ast attr v))
- (js2-visit-block n v))
-
-(defun js2-print-xml-start-tag (n i)
- (insert (js2-make-pad i) "<")
- (js2-print-ast (js2-xml-start-tag-node-name n) 0)
- (when (js2-xml-start-tag-node-attrs n)
- (insert " ")
- (js2-print-list (js2-xml-start-tag-node-attrs n) " "))
- (insert ">"))
-
-;; I -think- I'm going to make the parent node the corresponding start-tag,
-;; and add the end-tag to the kids list of the parent as well.
-(cl-defstruct (js2-xml-end-tag-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-xml-end-tag-node (&key (type js2-XML)
- (pos
js2-ts-cursor)
- len name)))
- "AST node for an XML end-tag. Not currently used."
- name) ; a `js2-xml-name-node'
-
-(put 'cl-struct-js2-xml-end-tag-node 'js2-visitor 'js2-visit-xml-end-tag)
-(put 'cl-struct-js2-xml-end-tag-node 'js2-printer 'js2-print-xml-end-tag)
-
-(defun js2-visit-xml-end-tag (n v)
- (js2-visit-ast (js2-xml-end-tag-node-name n) v))
-
-(defun js2-print-xml-end-tag (n i)
- (insert (js2-make-pad i))
- (insert "</")
- (js2-print-ast (js2-xml-end-tag-node-name n) 0)
- (insert ">"))
-
-(cl-defstruct (js2-xml-name-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-xml-name-node (&key (type js2-XML)
- (pos js2-ts-cursor)
- len namespace kids)))
- "AST node for an E4X XML name. Not currently used.
-Any XML name can be qualified with a namespace, hence the namespace field.
-Further, any E4X name can be comprised of arbitrary JavaScript {} expressions.
-The kids field is a list of `js2-name-node' and `js2-xml-js-expr-node'.
-For a simple name, the kids list has exactly one node, a `js2-name-node'."
- namespace) ; a `js2-string-node'
-
-(put 'cl-struct-js2-xml-name-node 'js2-visitor 'js2-visit-xml-name-node)
-(put 'cl-struct-js2-xml-name-node 'js2-printer 'js2-print-xml-name-node)
-
-(defun js2-visit-xml-name-node (n v)
- (js2-visit-ast (js2-xml-name-node-namespace n) v))
-
-(defun js2-print-xml-name-node (n i)
- (insert (js2-make-pad i))
- (when (js2-xml-name-node-namespace n)
- (js2-print-ast (js2-xml-name-node-namespace n) 0)
- (insert "::"))
- (dolist (kid (js2-xml-name-node-kids n))
- (js2-print-ast kid 0)))
-
-(cl-defstruct (js2-xml-pi-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-xml-pi-node (&key (type js2-XML)
- (pos js2-ts-cursor)
- len name attrs)))
- "AST node for an E4X XML processing instruction. Not currently used."
- name ; a `js2-xml-name-node'
- attrs) ; a list of `js2-xml-attr-node'
-
-(put 'cl-struct-js2-xml-pi-node 'js2-visitor 'js2-visit-xml-pi-node)
-(put 'cl-struct-js2-xml-pi-node 'js2-printer 'js2-print-xml-pi-node)
-
-(defun js2-visit-xml-pi-node (n v)
- (js2-visit-ast (js2-xml-pi-node-name n) v)
- (dolist (attr (js2-xml-pi-node-attrs n))
- (js2-visit-ast attr v)))
-
-(defun js2-print-xml-pi-node (n i)
- (insert (js2-make-pad i) "<?")
- (js2-print-ast (js2-xml-pi-node-name n))
- (when (js2-xml-pi-node-attrs n)
- (insert " ")
- (js2-print-list (js2-xml-pi-node-attrs n)))
- (insert "?>"))
-
-(cl-defstruct (js2-xml-cdata-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-xml-cdata-node (&key (type js2-XML)
- (pos js2-ts-cursor)
- len content)))
- "AST node for a CDATA escape section. Not currently used."
- content) ; a `js2-string-node' with node-property 'quote-type 'cdata
-
-(put 'cl-struct-js2-xml-cdata-node 'js2-visitor 'js2-visit-xml-cdata-node)
-(put 'cl-struct-js2-xml-cdata-node 'js2-printer 'js2-print-xml-cdata-node)
-
-(defun js2-visit-xml-cdata-node (n v)
- (js2-visit-ast (js2-xml-cdata-node-content n) v))
-
-(defun js2-print-xml-cdata-node (n i)
- (insert (js2-make-pad i))
- (js2-print-ast (js2-xml-cdata-node-content n)))
-
-(cl-defstruct (js2-xml-attr-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-attr-node (&key (type js2-XML)
- (pos js2-ts-cursor)
- len name value
- eq-pos quote-type)))
- "AST node representing a foo='bar' XML attribute value. Not yet used."
- name ; a `js2-xml-name-node'
- value ; a `js2-xml-name-node'
- eq-pos ; buffer position of "=" sign
- quote-type) ; 'single or 'double
-
-(put 'cl-struct-js2-xml-attr-node 'js2-visitor 'js2-visit-xml-attr-node)
-(put 'cl-struct-js2-xml-attr-node 'js2-printer 'js2-print-xml-attr-node)
-
-(defun js2-visit-xml-attr-node (n v)
- (js2-visit-ast (js2-xml-attr-node-name n) v)
- (js2-visit-ast (js2-xml-attr-node-value n) v))
-
-(defun js2-print-xml-attr-node (n i)
- (let ((quote (if (eq (js2-xml-attr-node-quote-type n) 'single)
- "'"
- "\"")))
- (insert (js2-make-pad i))
- (js2-print-ast (js2-xml-attr-node-name n) 0)
- (insert "=" quote)
- (js2-print-ast (js2-xml-attr-node-value n) 0)
- (insert quote)))
-
-(cl-defstruct (js2-xml-text-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-text-node (&key (type js2-XML)
- (pos js2-ts-cursor)
- len content)))
- "AST node for an E4X XML text node. Not currently used."
- content) ; a Lisp list of `js2-string-node' and `js2-xml-js-expr-node'
-
-(put 'cl-struct-js2-xml-text-node 'js2-visitor 'js2-visit-xml-text-node)
-(put 'cl-struct-js2-xml-text-node 'js2-printer 'js2-print-xml-text-node)
-
-(defun js2-visit-xml-text-node (n v)
- (js2-visit-ast (js2-xml-text-node-content n) v))
-
-(defun js2-print-xml-text-node (n i)
- (insert (js2-make-pad i))
- (dolist (kid (js2-xml-text-node-content n))
- (js2-print-ast kid)))
-
-(cl-defstruct (js2-xml-comment-node
- (:include js2-xml-node)
- (:constructor nil)
- (:constructor make-js2-xml-comment-node (&key (type js2-XML)
- (pos
js2-ts-cursor)
- len)))
- "AST node for E4X XML comment. Not currently used.")
-
-(put 'cl-struct-js2-xml-comment-node 'js2-visitor 'js2-visit-none)
-(put 'cl-struct-js2-xml-comment-node 'js2-printer 'js2-print-xml-comment)
-
-(defun js2-print-xml-comment (n i)
- (insert (js2-make-pad i)
- (js2-node-string n)))
-
-;;; Node utilities
-
-(defsubst js2-node-line (n)
- "Fetch the source line number at the start of node N.
-This is O(n) in the length of the source buffer; use prudently."
- (1+ (count-lines (point-min) (js2-node-abs-pos n))))
-
-(defsubst js2-block-node-kid (n i)
- "Return child I of node N, or nil if there aren't that many."
- (nth i (js2-block-node-kids n)))
-
-(defsubst js2-block-node-first (n)
- "Return first child of block node N, or nil if there is none."
- (cl-first (js2-block-node-kids n)))
-
-(defun js2-node-root (n)
- "Return the root of the AST containing N.
-If N has no parent pointer, returns N."
- (let ((parent (js2-node-parent n)))
- (if parent
- (js2-node-root parent)
- n)))
-
-(defsubst js2-node-short-name (n)
- "Return the short name of node N as a string, e.g. `js2-if-node'."
- (substring (symbol-name (aref n 0))
- (length "cl-struct-")))
-
-(defun js2-node-child-list (node)
- "Return the child list for NODE, a Lisp list of nodes.
-Works for block nodes, array nodes, obj literals, funarg lists,
-var decls and try nodes (for catch clauses). Note that you should call
-`js2-block-node-kids' on the function body for the body statements.
-Returns nil for zero-length child lists or unsupported nodes."
- (cond
- ((js2-function-node-p node)
- (js2-function-node-params node))
- ((js2-block-node-p node)
- (js2-block-node-kids node))
- ((js2-try-node-p node)
- (js2-try-node-catch-clauses node))
- ((js2-array-node-p node)
- (js2-array-node-elems node))
- ((js2-object-node-p node)
- (js2-object-node-elems node))
- ((js2-call-node-p node)
- (js2-call-node-args node))
- ((js2-new-node-p node)
- (js2-new-node-args node))
- ((js2-var-decl-node-p node)
- (js2-var-decl-node-kids node))
- (t
- nil)))
-
-(defun js2-node-set-child-list (node kids)
- "Set the child list for NODE to KIDS."
- (cond
- ((js2-function-node-p node)
- (setf (js2-function-node-params node) kids))
- ((js2-block-node-p node)
- (setf (js2-block-node-kids node) kids))
- ((js2-try-node-p node)
- (setf (js2-try-node-catch-clauses node) kids))
- ((js2-array-node-p node)
- (setf (js2-array-node-elems node) kids))
- ((js2-object-node-p node)
- (setf (js2-object-node-elems node) kids))
- ((js2-call-node-p node)
- (setf (js2-call-node-args node) kids))
- ((js2-new-node-p node)
- (setf (js2-new-node-args node) kids))
- ((js2-var-decl-node-p node)
- (setf (js2-var-decl-node-kids node) kids))
- (t
- (error "Unsupported node type: %s" (js2-node-short-name node))))
- kids)
-
-;; All because Common Lisp doesn't support multiple inheritance for defstructs.
-(defconst js2-paren-expr-nodes
- '(cl-struct-js2-comp-loop-node
- cl-struct-js2-comp-node
- cl-struct-js2-call-node
- cl-struct-js2-catch-node
- cl-struct-js2-do-node
- cl-struct-js2-elem-get-node
- cl-struct-js2-for-in-node
- cl-struct-js2-for-node
- cl-struct-js2-function-node
- cl-struct-js2-if-node
- cl-struct-js2-let-node
- cl-struct-js2-new-node
- cl-struct-js2-paren-node
- cl-struct-js2-switch-node
- cl-struct-js2-while-node
- cl-struct-js2-with-node
- cl-struct-js2-xml-dot-query-node)
- "Node types that can have a parenthesized child expression.
-In particular, nodes that respond to `js2-node-lp' and `js2-node-rp'.")
-
-(defsubst js2-paren-expr-node-p (node)
- "Return t for nodes that typically have a parenthesized child expression.
-Useful for computing the indentation anchors for arg-lists and conditions.
-Note that it may return a false positive, for instance when NODE is
-a `js2-new-node' and there are no arguments or parentheses."
- (memq (aref node 0) js2-paren-expr-nodes))
-
-;; Fake polymorphism... yech.
-(defun js2-node-lp (node)
- "Return relative left-paren position for NODE, if applicable.
-For `js2-elem-get-node' structs, returns left-bracket position.
-Note that the position may be nil in the case of a parse error."
- (cond
- ((js2-elem-get-node-p node)
- (js2-elem-get-node-lb node))
- ((js2-loop-node-p node)
- (js2-loop-node-lp node))
- ((js2-function-node-p node)
- (js2-function-node-lp node))
- ((js2-if-node-p node)
- (js2-if-node-lp node))
- ((js2-new-node-p node)
- (js2-new-node-lp node))
- ((js2-call-node-p node)
- (js2-call-node-lp node))
- ((js2-paren-node-p node)
- 0)
- ((js2-switch-node-p node)
- (js2-switch-node-lp node))
- ((js2-catch-node-p node)
- (js2-catch-node-lp node))
- ((js2-let-node-p node)
- (js2-let-node-lp node))
- ((js2-comp-node-p node)
- 0)
- ((js2-with-node-p node)
- (js2-with-node-lp node))
- ((js2-xml-dot-query-node-p node)
- (1+ (js2-infix-node-op-pos node)))
- (t
- (error "Unsupported node type: %s" (js2-node-short-name node)))))
-
-;; Fake polymorphism... blech.
-(defun js2-node-rp (node)
- "Return relative right-paren position for NODE, if applicable.
-For `js2-elem-get-node' structs, returns right-bracket position.
-Note that the position may be nil in the case of a parse error."
- (cond
- ((js2-elem-get-node-p node)
- (js2-elem-get-node-rb node))
- ((js2-loop-node-p node)
- (js2-loop-node-rp node))
- ((js2-function-node-p node)
- (js2-function-node-rp node))
- ((js2-if-node-p node)
- (js2-if-node-rp node))
- ((js2-new-node-p node)
- (js2-new-node-rp node))
- ((js2-call-node-p node)
- (js2-call-node-rp node))
- ((js2-paren-node-p node)
- (1- (js2-node-len node)))
- ((js2-switch-node-p node)
- (js2-switch-node-rp node))
- ((js2-catch-node-p node)
- (js2-catch-node-rp node))
- ((js2-let-node-p node)
- (js2-let-node-rp node))
- ((js2-comp-node-p node)
- (1- (js2-node-len node)))
- ((js2-with-node-p node)
- (js2-with-node-rp node))
- ((js2-xml-dot-query-node-p node)
- (1+ (js2-xml-dot-query-node-rp node)))
- (t
- (error "Unsupported node type: %s" (js2-node-short-name node)))))
-
-(defsubst js2-node-first-child (node)
- "Return the first element of `js2-node-child-list' for NODE."
- (car (js2-node-child-list node)))
-
-(defsubst js2-node-last-child (node)
- "Return the last element of `js2-node-last-child' for NODE."
- (car (last (js2-node-child-list node))))
-
-(defun js2-node-prev-sibling (node)
- "Return the previous statement in parent.
-Works for parents supported by `js2-node-child-list'.
-Returns nil if NODE is not in the parent, or PARENT is
-not a supported node, or if NODE is the first child."
- (let* ((p (js2-node-parent node))
- (kids (js2-node-child-list p))
- (sib (car kids)))
- (while (and kids
- (not (eq node (cadr kids))))
- (setq kids (cdr kids)
- sib (car kids)))
- sib))
-
-(defun js2-node-next-sibling (node)
- "Return the next statement in parent block.
-Returns nil if NODE is not in the block, or PARENT is not
-a block node, or if NODE is the last statement."
- (let* ((p (js2-node-parent node))
- (kids (js2-node-child-list p)))
- (while (and kids
- (not (eq node (car kids))))
- (setq kids (cdr kids)))
- (cadr kids)))
-
-(defun js2-node-find-child-before (pos parent &optional after)
- "Find the last child that starts before POS in parent.
-If AFTER is non-nil, returns first child starting after POS.
-POS is an absolute buffer position. PARENT is any node
-supported by `js2-node-child-list'.
-Returns nil if no applicable child is found."
- (let ((kids (if (js2-function-node-p parent)
- (js2-block-node-kids (js2-function-node-body parent))
- (js2-node-child-list parent)))
- (beg (js2-node-abs-pos (if (js2-function-node-p parent)
- (js2-function-node-body parent)
- parent)))
- kid result fn
- (continue t))
- (setq fn (if after '>= '<))
- (while (and kids continue)
- (setq kid (car kids))
- (if (funcall fn (+ beg (js2-node-pos kid)) pos)
- (setq result kid
- continue (not after))
- (setq continue after))
- (setq kids (cdr kids)))
- result))
-
-(defun js2-node-find-child-after (pos parent)
- "Find first child that starts after POS in parent.
-POS is an absolute buffer position. PARENT is any node
-supported by `js2-node-child-list'.
-Returns nil if no applicable child is found."
- (js2-node-find-child-before pos parent 'after))
-
-(defun js2-node-replace-child (pos parent new-node)
- "Replace node at index POS in PARENT with NEW-NODE.
-Only works for parents supported by `js2-node-child-list'."
- (let ((kids (js2-node-child-list parent))
- (i 0))
- (while (< i pos)
- (setq kids (cdr kids)
- i (1+ i)))
- (setcar kids new-node)
- (js2-node-add-children parent new-node)))
-
-(defun js2-node-buffer (n)
- "Return the buffer associated with AST N.
-Returns nil if the buffer is not set as a property on the root
-node, or if parent links were not recorded during parsing."
- (let ((root (js2-node-root n)))
- (and root
- (js2-ast-root-p root)
- (js2-ast-root-buffer root))))
-
-(defun js2-block-node-push (n kid)
- "Push js2-node KID onto the end of js2-block-node N's child list.
-KID is always added to the -end- of the kids list.
-Function also calls `js2-node-add-children' to add the parent link."
- (let ((kids (js2-node-child-list n)))
- (if kids
- (setcdr kids (nconc (cdr kids) (list kid)))
- (js2-node-set-child-list n (list kid)))
- (js2-node-add-children n kid)))
-
-(defun js2-node-string (node)
- (with-current-buffer (or (js2-node-buffer node)
- (error "No buffer available for node %s" node))
- (let ((pos (js2-node-abs-pos node)))
- (buffer-substring-no-properties pos (+ pos (js2-node-len node))))))
-
-;; Container for storing the node we're looking for in a traversal.
-(js2-deflocal js2-discovered-node nil)
-
-;; Keep track of absolute node position during traversals.
-(js2-deflocal js2-visitor-offset nil)
-
-(js2-deflocal js2-node-search-point nil)
-
-(when js2-mode-dev-mode-p
- (defun js2-find-node-at-point ()
- (interactive)
- (let ((node (js2-node-at-point)))
- (message "%s" (or node "No node found at point"))))
- (defun js2-node-name-at-point ()
- (interactive)
- (let ((node (js2-node-at-point)))
- (message "%s" (if node
- (js2-node-short-name node)
- "No node found at point.")))))
-
-(defun js2-node-at-point (&optional pos skip-comments)
- "Return AST node at POS, a buffer position, defaulting to current point.
-The `js2-mode-ast' variable must be set to the current parse tree.
-Signals an error if the AST (`js2-mode-ast') is nil.
-Always returns a node - if it can't find one, it returns the root.
-If SKIP-COMMENTS is non-nil, comment nodes are ignored."
- (let ((ast js2-mode-ast)
- result)
- (unless ast
- (error "No JavaScript AST available"))
- ;; Look through comments first, since they may be inside nodes that
- ;; would otherwise report a match.
- (setq pos (or pos (point))
- result (if (> pos (js2-node-abs-end ast))
- ast
- (if (not skip-comments)
- (js2-comment-at-point pos))))
- (unless result
- (setq js2-discovered-node nil
- js2-visitor-offset 0
- js2-node-search-point pos)
- (unwind-protect
- (catch 'js2-visit-done
- (js2-visit-ast ast #'js2-node-at-point-visitor))
- (setq js2-visitor-offset nil
- js2-node-search-point nil))
- (setq result js2-discovered-node))
- ;; may have found a comment beyond end of last child node,
- ;; since visiting the ast-root looks at the comment-list last.
- (if (and skip-comments
- (js2-comment-node-p result))
- (setq result nil))
- (or result js2-mode-ast)))
-
-(defun js2-node-at-point-visitor (node end-p)
- (let ((rel-pos (js2-node-pos node))
- abs-pos
- abs-end
- (point js2-node-search-point))
- (cond
- (end-p
- ;; this evaluates to a non-nil return value, even if it's zero
- (cl-decf js2-visitor-offset rel-pos))
- ;; we already looked for comments before visiting, and don't want them now
- ((js2-comment-node-p node)
- nil)
- (t
- (setq abs-pos (cl-incf js2-visitor-offset rel-pos)
- ;; we only want to use the node if the point is before
- ;; the last character position in the node, so we decrement
- ;; the absolute end by 1.
- abs-end (+ abs-pos (js2-node-len node) -1))
- (cond
- ;; If this node starts after search-point, stop the search.
- ((> abs-pos point)
- (throw 'js2-visit-done nil))
- ;; If this node ends before the search-point, don't check kids.
- ((> point abs-end)
- nil)
- (t
- ;; Otherwise point is within this node, possibly in a child.
- (setq js2-discovered-node node)
- t)))))) ; keep processing kids to look for more specific match
-
-(defsubst js2-block-comment-p (node)
- "Return non-nil if NODE is a comment node of format `jsdoc' or `block'."
- (and (js2-comment-node-p node)
- (memq (js2-comment-node-format node) '(jsdoc block))))
-
-;; TODO: put the comments in a vector and binary-search them instead
-(defun js2-comment-at-point (&optional pos)
- "Look through scanned comment nodes for one containing POS.
-POS is a buffer position that defaults to current point.
-Function returns nil if POS was not in any comment node."
- (let ((ast js2-mode-ast)
- (x (or pos (point)))
- beg end)
- (unless ast
- (error "No JavaScript AST available"))
- (catch 'done
- ;; Comments are stored in lexical order.
- (dolist (comment (js2-ast-root-comments ast) nil)
- (setq beg (js2-node-abs-pos comment)
- end (+ beg (js2-node-len comment)))
- (if (and (>= x beg)
- (<= x end))
- (throw 'done comment))))))
-
-(defun js2-mode-find-parent-fn (node)
- "Find function enclosing NODE.
-Returns nil if NODE is not inside a function."
- (setq node (js2-node-parent node))
- (while (and node (not (js2-function-node-p node)))
- (setq node (js2-node-parent node)))
- (and (js2-function-node-p node) node))
-
-(defun js2-mode-find-enclosing-fn (node)
- "Find function or root enclosing NODE."
- (if (js2-ast-root-p node)
- node
- (setq node (js2-node-parent node))
- (while (not (or (js2-ast-root-p node)
- (js2-function-node-p node)))
- (setq node (js2-node-parent node)))
- node))
-
- (defun js2-mode-find-enclosing-node (beg end)
- "Find node fully enclosing BEG and END."
- (let ((node (js2-node-at-point beg))
- pos
- (continue t))
- (while continue
- (if (or (js2-ast-root-p node)
- (and
- (<= (setq pos (js2-node-abs-pos node)) beg)
- (>= (+ pos (js2-node-len node)) end)))
- (setq continue nil)
- (setq node (js2-node-parent node))))
- node))
-
-(defun js2-node-parent-script-or-fn (node)
- "Find script or function immediately enclosing NODE.
-If NODE is the ast-root, returns nil."
- (if (js2-ast-root-p node)
- nil
- (setq node (js2-node-parent node))
- (while (and node (not (or (js2-function-node-p node)
- (js2-script-node-p node))))
- (setq node (js2-node-parent node)))
- node))
-
-(defun js2-node-is-descendant (node ancestor)
- "Return t if NODE is a descendant of ANCESTOR."
- (while (and node
- (not (eq node ancestor)))
- (setq node (js2-node-parent node)))
- node)
-
-;;; visitor infrastructure
-
-(defun js2-visit-none (_node _callback)
- "Visitor for AST node that have no node children."
- nil)
-
-(defun js2-print-none (_node _indent)
- "Visitor for AST node with no printed representation.")
-
-(defun js2-print-body (node indent)
- "Print a statement, or a block without braces."
- (if (js2-block-node-p node)
- (dolist (kid (js2-block-node-kids node))
- (js2-print-ast kid indent))
- (js2-print-ast node indent)))
-
-(defun js2-print-list (args &optional delimiter)
- (cl-loop with len = (length args)
- for arg in args
- for count from 1
- do
- (when arg (js2-print-ast arg 0))
- (if (< count len)
- (insert (or delimiter ", ")))))
-
-(defun js2-print-tree (ast)
- "Prints an AST to the current buffer.
-Makes `js2-ast-parent-nodes' available to the printer functions."
- (let ((max-lisp-eval-depth (max max-lisp-eval-depth 1500)))
- (js2-print-ast ast)))
-
-(defun js2-print-ast (node &optional indent)
- "Helper function for printing AST nodes.
-Requires `js2-ast-parent-nodes' to be non-nil.
-You should use `js2-print-tree' instead of this function."
- (let ((printer (get (aref node 0) 'js2-printer))
- (i (or indent 0)))
- ;; TODO: wedge comments in here somewhere
- (if printer
- (funcall printer node i))))
-
-(defconst js2-side-effecting-tokens
- (let ((tokens (make-bool-vector js2-num-tokens nil)))
- (dolist (tt (list js2-ASSIGN
- js2-ASSIGN_ADD
- js2-ASSIGN_BITAND
- js2-ASSIGN_BITOR
- js2-ASSIGN_BITXOR
- js2-ASSIGN_DIV
- js2-ASSIGN_LSH
- js2-ASSIGN_MOD
- js2-ASSIGN_MUL
- js2-ASSIGN_RSH
- js2-ASSIGN_SUB
- js2-ASSIGN_URSH
- js2-BLOCK
- js2-BREAK
- js2-CALL
- js2-CATCH
- js2-CATCH_SCOPE
- js2-CLASS
- js2-CONST
- js2-CONTINUE
- js2-DEBUGGER
- js2-DEC
- js2-DELPROP
- js2-DEL_REF
- js2-DO
- js2-ELSE
- js2-EMPTY
- js2-ENTERWITH
- js2-EXPORT
- js2-EXPR_RESULT
- js2-FINALLY
- js2-FOR
- js2-FUNCTION
- js2-GOTO
- js2-IF
- js2-IFEQ
- js2-IFNE
- js2-IMPORT
- js2-INC
- js2-JSR
- js2-LABEL
- js2-LEAVEWITH
- js2-LET
- js2-LETEXPR
- js2-LOCAL_BLOCK
- js2-LOOP
- js2-NEW
- js2-REF_CALL
- js2-RETHROW
- js2-RETURN
- js2-RETURN_RESULT
- js2-SEMI
- js2-SETELEM
- js2-SETELEM_OP
- js2-SETNAME
- js2-SETPROP
- js2-SETPROP_OP
- js2-SETVAR
- js2-SET_REF
- js2-SET_REF_OP
- js2-SWITCH
- js2-TARGET
- js2-THROW
- js2-TRY
- js2-VAR
- js2-WHILE
- js2-WITH
- js2-WITHEXPR
- js2-YIELD))
- (aset tokens tt t))
- (if js2-instanceof-has-side-effects
- (aset tokens js2-INSTANCEOF t))
- tokens))
-
-(defun js2-node-has-side-effects (node)
- "Return t if NODE has side effects."
- (when node ; makes it easier to handle malformed expressions
- (let ((tt (js2-node-type node)))
- (cond
- ;; This doubtless needs some work, since EXPR_VOID is used
- ;; in several ways in Rhino and I may not have caught them all.
- ;; I'll wait for people to notice incorrect warnings.
- ((and (= tt js2-EXPR_VOID)
- (js2-expr-stmt-node-p node)) ; but not if EXPR_RESULT
- (let ((expr (js2-expr-stmt-node-expr node)))
- (or (js2-node-has-side-effects expr)
- (when (js2-string-node-p expr)
- (member (js2-string-node-value expr) '("use strict" "use
asm"))))))
- ((= tt js2-COMMA)
- (js2-node-has-side-effects (js2-infix-node-right node)))
- ((or (= tt js2-AND)
- (= tt js2-OR))
- (or (js2-node-has-side-effects (js2-infix-node-right node))
- (js2-node-has-side-effects (js2-infix-node-left node))))
- ((= tt js2-HOOK)
- (and (js2-node-has-side-effects (js2-cond-node-true-expr node))
- (js2-node-has-side-effects (js2-cond-node-false-expr node))))
- ((js2-paren-node-p node)
- (js2-node-has-side-effects (js2-paren-node-expr node)))
- ((= tt js2-ERROR) ; avoid cascaded error messages
- nil)
- (t
- (aref js2-side-effecting-tokens tt))))))
-
-(defconst js2-stmt-node-types
- (list js2-BLOCK
- js2-BREAK
- js2-CONTINUE
- js2-DEFAULT ; e4x "default xml namespace" statement
- js2-DO
- js2-EXPORT
- js2-EXPR_RESULT
- js2-EXPR_VOID
- js2-FOR
- js2-IF
- js2-IMPORT
- js2-RETURN
- js2-SWITCH
- js2-THROW
- js2-TRY
- js2-WHILE
- js2-WITH)
- "Node types that only appear in statement contexts.
-The list does not include nodes that always appear as the child
-of another specific statement type, such as switch-cases,
-catch and finally blocks, and else-clauses. The list also excludes
-nodes like yield, let and var, which may appear in either expression
-or statement context, and in the latter context always have a
-`js2-expr-stmt-node' parent. Finally, the list does not include
-functions or scripts, which are treated separately from statements
-by the JavaScript parser and runtime.")
-
-(defun js2-stmt-node-p (node)
- "Heuristic for figuring out if NODE is a statement.
-Some node types can appear in either an expression context or a
-statement context, e.g. let-nodes, yield-nodes, and var-decl nodes.
-For these node types in a statement context, the parent will be a
-`js2-expr-stmt-node'.
-Functions aren't included in the check."
- (memq (js2-node-type node) js2-stmt-node-types))
-
-(defun js2-mode-find-first-stmt (node)
- "Search upward starting from NODE looking for a statement.
-For purposes of this function, a `js2-function-node' counts."
- (while (not (or (js2-stmt-node-p node)
- (js2-function-node-p node)))
- (setq node (js2-node-parent node)))
- node)
-
-(defun js2-node-parent-stmt (node)
- "Return the node's first ancestor that is a statement.
-Returns nil if NODE is a `js2-ast-root'. Note that any expression
-appearing in a statement context will have a parent that is a
-`js2-expr-stmt-node' that will be returned by this function."
- (let ((parent (js2-node-parent node)))
- (if (or (null parent)
- (js2-stmt-node-p parent)
- (and (js2-function-node-p parent)
- (not (eq (js2-function-node-form parent)
- 'FUNCTION_EXPRESSION))))
- parent
- (js2-node-parent-stmt parent))))
-
-;; In the Mozilla Rhino sources, Roshan James writes:
-;; Does consistent-return analysis on the function body when strict mode is
-;; enabled.
-;;
-;; function (x) { return (x+1) }
-;;
-;; is ok, but
-;;
-;; function (x) { if (x < 0) return (x+1); }
-;;
-;; is not because the function can potentially return a value when the
-;; condition is satisfied and if not, the function does not explicitly
-;; return a value.
-;;
-;; This extends to checking mismatches such as "return" and "return <value>"
-;; used in the same function. Warnings are not emitted if inconsistent
-;; returns exist in code that can be statically shown to be unreachable.
-;; Ex.
-;; function (x) { while (true) { ... if (..) { return value } ... } }
-;;
-;; emits no warning. However if the loop had a break statement, then a
-;; warning would be emitted.
-;;
-;; The consistency analysis looks at control structures such as loops, ifs,
-;; switch, try-catch-finally blocks, examines the reachable code paths and
-;; warns the user about an inconsistent set of termination possibilities.
-;;
-;; 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.
-;;
-;; END_UNREACHED is reserved for code paths that are assumed to always be
-;; able to execute (example: throw, continue)
-;;
-;; END_DROPS_OFF indicates if the statement can transfer control to the
-;; next one. Statement such as return dont. A compound statement may have
-;; some branch that drops off control to the next statement.
-;;
-;; END_RETURNS indicates that the statement can return with no value.
-;; END_RETURNS_VALUE indicates that the statement can return a value.
-;;
-;; A compound statement such as
-;; if (condition) {
-;; return value;
-;; }
-;; Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
-
-(defconst js2-END_UNREACHED 0)
-(defconst js2-END_DROPS_OFF 1)
-(defconst js2-END_RETURNS 2)
-(defconst js2-END_RETURNS_VALUE 4)
-(defconst js2-END_YIELDS 8)
-
-(defun js2-has-consistent-return-usage (node)
- "Check that every return usage in a function body is consistent.
-Returns t if the function satisfies strict mode requirement."
- (let ((n (js2-end-check node)))
- ;; either it doesn't return a value in any branch...
- (or (js2-flag-not-set-p n js2-END_RETURNS_VALUE)
- ;; or it returns a value (or is unreached) at every branch
- (js2-flag-not-set-p n (logior js2-END_DROPS_OFF
- js2-END_RETURNS
- js2-END_YIELDS)))))
-
-(defun js2-end-check-if (node)
- "Ensure that return usage in then/else blocks is consistent.
-If there is no else block, then the return statement can fall through.
-Returns logical OR of END_* flags"
- (let ((th (js2-if-node-then-part node))
- (el (js2-if-node-else-part node)))
- (if (null th)
- js2-END_UNREACHED
- (logior (js2-end-check th) (if el
- (js2-end-check el)
- js2-END_DROPS_OFF)))))
-
-(defun js2-end-check-switch (node)
- "Consistency of return statements is checked between the case statements.
-If there is no default, then the switch can fall through. If there is a
-default, we check to see if all code paths in the default return or if
-there is a code path that can fall through.
-Returns logical OR of END_* flags."
- (let ((rv js2-END_UNREACHED)
- default-case)
- ;; examine the cases
- (catch 'break
- (dolist (c (js2-switch-node-cases node))
- (if (js2-case-node-expr c)
- (js2-set-flag rv (js2-end-check-block c))
- (setq default-case c)
- (throw 'break nil))))
- ;; we don't care how the cases drop into each other
- (js2-clear-flag rv js2-END_DROPS_OFF)
- ;; examine the default
- (js2-set-flag rv (if default-case
- (js2-end-check default-case)
- js2-END_DROPS_OFF))
- rv))
-
-(defun js2-end-check-try (node)
- "If the block has a finally, return consistency is checked in the
-finally block. If all code paths in the finally return, then the
-returns in the try-catch blocks don't matter. If there is a code path
-that does not return or if there is no finally block, the returns
-of the try and catch blocks are checked for mismatch.
-Returns logical OR of END_* flags."
- (let ((finally (js2-try-node-finally-block node))
- rv)
- ;; check the finally if it exists
- (setq rv (if finally
- (js2-end-check (js2-finally-node-body finally))
- js2-END_DROPS_OFF))
- ;; If the finally block always returns, then none of the returns
- ;; in the try or catch blocks matter.
- (when (js2-flag-set-p rv js2-END_DROPS_OFF)
- (js2-clear-flag rv js2-END_DROPS_OFF)
- ;; examine the try block
- (js2-set-flag rv (js2-end-check (js2-try-node-try-block node)))
- ;; check each catch block
- (dolist (cb (js2-try-node-catch-clauses node))
- (js2-set-flag rv (js2-end-check cb))))
- rv))
-
-(defun js2-end-check-loop (node)
- "Return statement in the loop body must be consistent.
-The default assumption for any kind of a loop is that it will eventually
-terminate. The only exception is a loop with a constant true condition.
-Code that follows such a loop is examined only if one can determine
-statically that there is a break out of the loop.
-
- for(... ; ... ; ...) {}
- for(... in ... ) {}
- while(...) { }
- do { } while(...)
-
-Returns logical OR of END_* flags."
- (let ((rv (js2-end-check (js2-loop-node-body node)))
- (condition (cond
- ((js2-while-node-p node)
- (js2-while-node-condition node))
- ((js2-do-node-p node)
- (js2-do-node-condition node))
- ((js2-for-node-p node)
- (js2-for-node-condition node)))))
-
- ;; check to see if the loop condition is always true
- (if (and condition
- (eq (js2-always-defined-boolean-p condition) 'ALWAYS_TRUE))
- (js2-clear-flag rv js2-END_DROPS_OFF))
-
- ;; look for effect of breaks
- (js2-set-flag rv (js2-node-get-prop node
- 'CONTROL_BLOCK_PROP
- js2-END_UNREACHED))
- rv))
-
-(defun js2-end-check-block (node)
- "A general block of code is examined statement by statement.
-If any statement (even a compound one) returns in all branches, then
-subsequent statements are not examined.
-Returns logical OR of END_* flags."
- (let* ((rv js2-END_DROPS_OFF)
- (kids (js2-block-node-kids node))
- (n (car kids)))
- ;; Check each statment. If the statement can continue onto the next
- ;; one (i.e. END_DROPS_OFF is set), then check the next statement.
- (while (and n (js2-flag-set-p rv js2-END_DROPS_OFF))
- (js2-clear-flag rv js2-END_DROPS_OFF)
- (js2-set-flag rv (js2-end-check n))
- (setq kids (cdr kids)
- n (car kids)))
- rv))
-
-(defun js2-end-check-label (node)
- "A labeled statement implies that there may be a break to the label.
-The function processes the labeled statement and then checks the
-CONTROL_BLOCK_PROP property to see if there is ever a break to the
-particular label.
-Returns logical OR of END_* flags."
- (let ((rv (js2-end-check (js2-labeled-stmt-node-stmt node))))
- (logior rv (js2-node-get-prop node
- 'CONTROL_BLOCK_PROP
- js2-END_UNREACHED))))
-
-(defun js2-end-check-break (node)
- "When a break is encountered annotate the statement being broken
-out of by setting its CONTROL_BLOCK_PROP property.
-Returns logical OR of END_* flags."
- (and (js2-break-node-target node)
- (js2-node-set-prop (js2-break-node-target node)
- 'CONTROL_BLOCK_PROP
- js2-END_DROPS_OFF))
- js2-END_UNREACHED)
-
-(defun js2-end-check (node)
- "Examine the body of a function, doing a basic reachability analysis.
-Returns a combination of flags END_* flags that indicate
-how the function execution can terminate. These constitute only the
-pessimistic set of termination conditions. It is possible that at
-runtime certain code paths will never be actually taken. Hence this
-analysis will flag errors in cases where there may not be errors.
-Returns logical OR of END_* flags"
- (let (kid)
- (cond
- ((js2-break-node-p node)
- (js2-end-check-break node))
- ((js2-expr-stmt-node-p node)
- (if (setq kid (js2-expr-stmt-node-expr node))
- (js2-end-check kid)
- js2-END_DROPS_OFF))
- ((or (js2-continue-node-p node)
- (js2-throw-node-p node))
- js2-END_UNREACHED)
- ((js2-return-node-p node)
- (if (setq kid (js2-return-node-retval node))
- js2-END_RETURNS_VALUE
- js2-END_RETURNS))
- ((js2-loop-node-p node)
- (js2-end-check-loop node))
- ((js2-switch-node-p node)
- (js2-end-check-switch node))
- ((js2-labeled-stmt-node-p node)
- (js2-end-check-label node))
- ((js2-if-node-p node)
- (js2-end-check-if node))
- ((js2-try-node-p node)
- (js2-end-check-try node))
- ((js2-block-node-p node)
- (if (null (js2-block-node-kids node))
- js2-END_DROPS_OFF
- (js2-end-check-block node)))
- ((js2-yield-node-p node)
- js2-END_YIELDS)
- (t
- js2-END_DROPS_OFF))))
-
-(defun js2-always-defined-boolean-p (node)
- "Check if NODE always evaluates to true or false in boolean context.
-Returns 'ALWAYS_TRUE, 'ALWAYS_FALSE, or nil if it's neither always true
-nor always false."
- (let ((tt (js2-node-type node))
- num)
- (cond
- ((or (= tt js2-FALSE) (= tt js2-NULL))
- 'ALWAYS_FALSE)
- ((= tt js2-TRUE)
- 'ALWAYS_TRUE)
- ((= tt js2-NUMBER)
- (setq num (js2-number-node-num-value node))
- (if (and (not (eq num 0.0e+NaN))
- (not (zerop num)))
- 'ALWAYS_TRUE
- 'ALWAYS_FALSE))
- (t
- nil))))
-
-;;; Scanner -- a port of Mozilla Rhino's lexer.
-;; Corresponds to Rhino files Token.java and TokenStream.java.
-
-(defvar js2-tokens nil
- "List of all defined token names.") ; initialized in `js2-token-names'
-
-(defconst js2-token-names
- (let* ((names (make-vector js2-num-tokens -1))
- (case-fold-search nil) ; only match js2-UPPER_CASE
- (syms (apropos-internal "^js2-\\(?:[[:upper:]_]+\\)")))
- (cl-loop for sym in syms
- for i from 0
- do
- (unless (or (memq sym '(js2-EOF_CHAR js2-ERROR))
- (not (boundp sym)))
- (aset names (symbol-value sym) ; code, e.g. 152
- (downcase
- (substring (symbol-name sym) 4))) ; name, e.g. "let"
- (push sym js2-tokens)))
- names)
- "Vector mapping int values to token string names, sans `js2-' prefix.")
-
-(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))
- (if (symbolp tok)
- (setq code (symbol-value tok)))
- (if (eq code -1)
- "ERROR"
- (if (and (numberp code)
- (not (cl-minusp code))
- (< code js2-num-tokens))
- (aref js2-token-names code)
- (error "Invalid token: %s" code)))))
-
-(defsubst js2-tt-sym (tok)
- "Return symbol for TOK given its code, e.g. 'js2-LP for code 86."
- (intern (js2-tt-name tok)))
-
-(defconst js2-token-codes
- (let ((table (make-hash-table :test 'eq :size 256)))
- (cl-loop for name across js2-token-names
- for sym = (intern (concat "js2-" (upcase name)))
- do
- (puthash sym (symbol-value sym) table))
- ;; clean up a few that are "wrong" in Rhino's token codes
- (puthash 'js2-DELETE js2-DELPROP table)
- table)
- "Hashtable mapping token type symbols to their bytecodes.")
-
-(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)
- (setf (js2-token-end (js2-current-token)) js2-ts-cursor)
- (js2-report-error msg nil
- (or beg (js2-current-token-beg))
- (or len (js2-current-token-len)))
- (unless no-throw
- (throw 'return js2-ERROR)))
-
-(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.
-(defsubst js2-add-to-string (c)
- (push c js2-ts-string-buffer))
-
-;; Note that when we "read" the end-of-file, we advance js2-ts-cursor
-;; to (1+ (point-max)), which lets the scanner treat end-of-file like
-;; any other character: when it's not part of the current token, we
-;; unget it, allowing it to be read again by the following call.
-(defsubst js2-unget-char ()
- (cl-decf js2-ts-cursor))
-
-;; Rhino distinguishes \r and \n line endings. We don't need to
-;; because we only scan from Emacs buffers, which always use \n.
-(defun js2-get-char ()
- "Read and return the next character from the input buffer.
-Increments `js2-ts-lineno' if the return value is a newline char.
-Updates `js2-ts-cursor' to the point after the returned char.
-Returns `js2-EOF_CHAR' if we hit the end of the buffer.
-Also updates `js2-ts-hit-eof' and `js2-ts-line-start' as needed."
- (let (c)
- ;; check for end of buffer
- (if (>= js2-ts-cursor (point-max))
- (setq js2-ts-hit-eof t
- js2-ts-cursor (1+ js2-ts-cursor)
- c js2-EOF_CHAR) ; return value
- ;; otherwise read next char
- (setq c (char-before (cl-incf js2-ts-cursor)))
- ;; if we read a newline, update counters
- (if (= c ?\n)
- (setq js2-ts-line-start js2-ts-cursor
- js2-ts-lineno (1+ js2-ts-lineno)))
- ;; TODO: skip over format characters
- c)))
-
-(defun js2-read-unicode-escape ()
- "Read a \\uNNNN sequence from the input.
-Assumes the ?\ and ?u have already been read.
-Returns the unicode character, or nil if it wasn't a valid character.
-Doesn't change the values of any scanner variables."
- ;; I really wish I knew a better way to do this, but I can't
- ;; find the Emacs function that takes a 16-bit int and converts
- ;; it to a Unicode/utf-8 character. So I basically eval it with (read).
- ;; Have to first check that it's 4 hex characters or it may stop
- ;; the read early.
- (ignore-errors
- (let ((s (buffer-substring-no-properties js2-ts-cursor
- (+ 4 js2-ts-cursor))))
- (if (string-match "[0-9a-fA-F]\\{4\\}" s)
- (read (concat "?\\u" s))))))
-
-(defun js2-match-char (test)
- "Consume and return next character if it matches TEST, a character.
-Returns nil and consumes nothing if TEST is not the next character."
- (let ((c (js2-get-char)))
- (if (eq c test)
- t
- (js2-unget-char)
- nil)))
-
-(defun js2-peek-char ()
- (prog1
- (js2-get-char)
- (js2-unget-char)))
-
-(defun js2-identifier-start-p (c)
- "Is C a valid start to an ES5 Identifier?
-See http://es5.github.io/#x7.6"
- (or
- (memq c '(?$ ?_))
- (memq (get-char-code-property c 'general-category)
- ;; Letters
- '(Lu Ll Lt Lm Lo Nl))))
-
-(defun js2-identifier-part-p (c)
- "Is C a valid part of an ES5 Identifier?
-See http://es5.github.io/#x7.6"
- (or
- (memq c '(?$ ?_ ?\u200c ?\u200d))
- (memq (get-char-code-property c 'general-category)
- '(;; Letters
- Lu Ll Lt Lm Lo Nl
- ;; Combining Marks
- Mn Mc
- ;; Digits
- Nd
- ;; Connector Punctuation
- Pc))))
-
-(defun js2-alpha-p (c)
- (cond ((and (<= ?A c) (<= c ?Z)) t)
- ((and (<= ?a c) (<= c ?z)) t)
- (t nil)))
-
-(defsubst js2-digit-p (c)
- (and (<= ?0 c) (<= c ?9)))
-
-(defun js2-js-space-p (c)
- (if (<= c 127)
- (memq c '(#x20 #x9 #xB #xC #xD))
- (or
- (eq c #xA0)
- ;; TODO: change this nil to check for Unicode space character
- nil)))
-
-(defconst js2-eol-chars (list js2-EOF_CHAR ?\n ?\r))
-
-(defun js2-skip-line ()
- "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)
- "Create token stream for BUF starting on LINE.
-BUF defaults to `current-buffer' and LINE defaults to 1.
-
-A buffer can only have one scanner active at a time, which yields
-dramatically simpler code than using a defstruct. If you need to
-have simultaneous scanners in a buffer, copy the regions to scan
-into temp buffers."
- (with-current-buffer (or buf (current-buffer))
- (setq js2-ts-dirty-line 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
- js2-ts-string-buffer nil)))
-
-;; This function uses the cached op, string and number fields in
-;; TokenStream; if getToken has been called since the passed token
-;; was scanned, the op or string printed may be incorrect.
-(defun js2-token-to-string (token)
- ;; Not sure where this function is used in Rhino. Not tested.
- (if (not js2-debug-print-trees)
- ""
- (let ((name (js2-tt-name token)))
- (cond
- ((memq token '(js2-STRING js2-REGEXP js2-NAME
- js2-TEMPLATE_HEAD js2-NO_SUBS_TEMPLATE))
- (concat name " `" (js2-current-token-string) "'"))
- ((eq token js2-NUMBER)
- (format "NUMBER %g" (js2-token-number (js2-current-token))))
- (t
- name)))))
-
-(defconst js2-keywords
- '(break
- case catch class const continue
- debugger default delete do
- else extends export
- false finally for function
- if in instanceof import
- let
- new null
- return
- static super switch
- this throw true try typeof
- var void
- while with
- yield))
-
-;; Token names aren't exactly the same as the keywords, unfortunately.
-;; E.g. delete is js2-DELPROP.
-(defconst js2-kwd-tokens
- (let ((table (make-vector js2-num-tokens nil))
- (tokens
- (list js2-BREAK
- js2-CASE js2-CATCH js2-CLASS js2-CONST js2-CONTINUE
- js2-DEBUGGER js2-DEFAULT js2-DELPROP js2-DO
- js2-ELSE js2-EXPORT
- js2-ELSE js2-EXTENDS js2-EXPORT
- js2-FALSE js2-FINALLY js2-FOR js2-FUNCTION
- js2-IF js2-IN js2-INSTANCEOF js2-IMPORT
- js2-LET
- js2-NEW js2-NULL
- js2-RETURN
- js2-STATIC js2-SUPER js2-SWITCH
- js2-THIS js2-THROW js2-TRUE js2-TRY js2-TYPEOF
- js2-VAR
- js2-WHILE js2-WITH
- js2-YIELD)))
- (dolist (i tokens)
- (aset table i 'font-lock-keyword-face))
- (aset table js2-STRING 'font-lock-string-face)
- (aset table js2-REGEXP 'font-lock-string-face)
- (aset table js2-NO_SUBS_TEMPLATE 'font-lock-string-face)
- (aset table js2-TEMPLATE_HEAD 'font-lock-string-face)
- (aset table js2-COMMENT 'font-lock-comment-face)
- (aset table js2-THIS 'font-lock-builtin-face)
- (aset table js2-SUPER 'font-lock-builtin-face)
- (aset table js2-VOID 'font-lock-constant-face)
- (aset table js2-NULL 'font-lock-constant-face)
- (aset table js2-TRUE 'font-lock-constant-face)
- (aset table js2-FALSE 'font-lock-constant-face)
- (aset table js2-NOT 'font-lock-negation-char-face)
- table)
- "Vector whose values are non-nil for tokens that are keywords.
-The values are default faces to use for highlighting the keywords.")
-
-;; FIXME: Support strict mode-only future reserved words, after we know
-;; which parts scopes are in strict mode, and which are not.
-(defconst js2-reserved-words '(class enum export extends import super)
- "Future reserved keywords in ECMAScript 5.1.")
-
-(defconst js2-keyword-names
- (let ((table (make-hash-table :test 'equal)))
- (cl-loop for k in js2-keywords
- do (puthash
- (symbol-name k) ; instanceof
- (intern (concat "js2-"
- (upcase (symbol-name k)))) ; js2-INSTANCEOF
- table))
- table)
- "JavaScript keywords by name, mapped to their symbols.")
-
-(defconst js2-reserved-word-names
- (let ((table (make-hash-table :test 'equal)))
- (cl-loop for k in js2-reserved-words
- do
- (puthash (symbol-name k) 'js2-RESERVED table))
- table)
- "JavaScript reserved words by name, mapped to 'js2-RESERVED.")
-
-(defun js2-collect-string (buf)
- "Convert BUF, a list of chars, to a string.
-Reverses BUF before converting."
- (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.
-Returns a symbol such as 'js2-BREAK, or nil if not keyword/reserved."
- (or (gethash s js2-keyword-names)
- (gethash s js2-reserved-word-names)))
-
-(defsubst js2-ts-set-char-token-bounds (token)
- "Used when next token is one character."
- (setf (js2-token-beg token) (1- js2-ts-cursor)
- (js2-token-end token) js2-ts-cursor))
-
-(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.
-If C is a hexadecimal digit, return ACCUMULATOR * 16 plus
-corresponding number. Otherwise return -1."
- (catch 'return
- (catch 'check
- ;; Use 0..9 < A..Z < a..z
- (cond
- ((<= c ?9)
- (cl-decf c ?0)
- (if (<= 0 c)
- (throw 'check nil)))
- ((<= c ?F)
- (when (<= ?A c)
- (cl-decf c (- ?A 10))
- (throw 'check nil)))
- ((<= c ?f)
- (when (<= ?a c)
- (cl-decf c (- ?a 10))
- (throw 'check nil))))
- (throw 'return -1))
- (logior c (lsh accumulator 4))))
-
-(defun js2-get-token (&optional modifier)
- "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 modifier)
- (cl-decf js2-ti-lookahead)
- (setq js2-ti-tokens-cursor (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens))
- (let ((tt (js2-current-token-type)))
- (cl-assert (not (= tt js2-EOL)))
- tt)))
-
-(defun js2-unget-token ()
- (cl-assert (< js2-ti-lookahead js2-ti-max-lookahead))
- (cl-incf js2-ti-lookahead)
- (setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens)))
-
-(defun js2-get-token-internal (modifier)
- (let* ((token (js2-get-token-internal-1 modifier)) ; 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 modifier) ; 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
- ((cl-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))
-
-(defsubst js2-string-to-number (str base)
- ;; TODO: Maybe port ScriptRuntime.stringToNumber.
- (condition-case nil
- (string-to-number str base)
- (overflow-error -1)))
-
-(defun js2-get-token-internal-1 (modifier)
- "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 (identifier-start
- is-unicode-escape-start c c1
- contains-escape escape-val str result base
- quote-char val look-for-slash continue tt
- (token (js2-new-token 0)))
- (setq
- tt
- (catch 'return
- (when (eq modifier 'TEMPLATE_TAIL)
- (setf (js2-token-beg token) (1- js2-ts-cursor))
- (throw 'return (js2-get-string-or-template-token ?` token)))
- (while t
- ;; Eat whitespace, possibly sensitive to newlines.
- (setq continue t)
- (while continue
- (setq c (js2-get-char))
- (cond
- ((eq c js2-EOF_CHAR)
- (js2-unget-char)
- (js2-ts-set-char-token-bounds token)
- (throw 'return js2-EOF))
- ((eq c ?\n)
- (js2-ts-set-char-token-bounds token)
- (setq js2-ts-dirty-line nil)
- (throw 'return js2-EOL))
- ((not (js2-js-space-p c))
- (if (/= c ?-) ; in case end of HTML comment
- (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 token)
- (when (eq c ?@)
- (throw 'return js2-XMLATTR))
- ;; identifier/keyword/instanceof?
- ;; watch out for starting with a <backslash>
- (cond
- ((eq c ?\\)
- (setq c (js2-get-char))
- (if (eq c ?u)
- (setq identifier-start t
- is-unicode-escape-start t
- js2-ts-string-buffer nil)
- (setq identifier-start nil)
- (js2-unget-char)
- (setq c ?\\)))
- (t
- (when (setq identifier-start (js2-identifier-start-p c))
- (setq js2-ts-string-buffer nil)
- (js2-add-to-string c))))
- (when identifier-start
- (setq contains-escape is-unicode-escape-start)
- (catch 'break
- (while t
- (if is-unicode-escape-start
- ;; strictly speaking we should probably push-back
- ;; all the bad characters if the <backslash>uXXXX
- ;; sequence is malformed. But since there isn't a
- ;; correct context(is there?) for a bad Unicode
- ;; escape sequence in an identifier, we can report
- ;; an error here.
- (progn
- (setq escape-val 0)
- (dotimes (_ 4)
- (setq c (js2-get-char)
- escape-val (js2-x-digit-to-int c escape-val))
- ;; Next check takes care of c < 0 and bad escape
- (if (cl-minusp escape-val)
- (throw 'break nil)))
- (if (cl-minusp escape-val)
- (js2-report-scan-error "msg.invalid.escape" t))
- (js2-add-to-string escape-val)
- (setq is-unicode-escape-start nil))
- (setq c (js2-get-char))
- (cond
- ((eq c ?\\)
- (setq c (js2-get-char))
- (if (eq c ?u)
- (setq is-unicode-escape-start t
- contains-escape t)
- (js2-report-scan-error "msg.illegal.character" t)))
- (t
- (if (or (eq c js2-EOF_CHAR)
- (not (js2-identifier-part-p c)))
- (throw 'break nil))
- (js2-add-to-string c))))))
- (js2-unget-char)
- (setf str (js2-collect-string js2-ts-string-buffer)
- (js2-token-end token) js2-ts-cursor)
- ;; FIXME: Invalid in ES5 and ES6, see
- ;; https://bugzilla.mozilla.org/show_bug.cgi?id=694360
- ;; Probably should just drop this conditional.
- (unless contains-escape
- ;; OPT we shouldn't have to make a string (object!) to
- ;; check if it's a keyword.
- ;; Return the corresponding token if it's a keyword
- (when (and (not (eq modifier 'KEYWORD_IS_NAME))
- (setq result (js2-string-to-keyword str)))
- (if (and (< js2-language-version 170)
- (memq result '(js2-LET js2-YIELD)))
- ;; LET and YIELD are tokens only in 1.7 and later
- (setq result 'js2-NAME))
- (when (eq result 'js2-RESERVED)
- (setf (js2-token-string token) str))
- (throw 'return (js2-tt-code result))))
- ;; If we want to intern these as Rhino does, just use (intern 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)
- (and (eq c ?.) (js2-digit-p (js2-peek-char))))
- (setq js2-ts-string-buffer nil
- base 10)
- (when (eq c ?0)
- (setq c (js2-get-char))
- (cond
- ((or (eq c ?x) (eq c ?X))
- (setq base 16)
- (setq c (js2-get-char)))
- ((and (or (eq c ?b) (eq c ?B))
- (>= js2-language-version 200))
- (setq base 2)
- (setq c (js2-get-char)))
- ((and (or (eq c ?o) (eq c ?O))
- (>= js2-language-version 200))
- (setq base 8)
- (setq c (js2-get-char)))
- ((js2-digit-p c)
- (setq base 'maybe-8))
- (t
- (js2-add-to-string ?0))))
- (cond
- ((eq base 16)
- (if (> 0 (js2-x-digit-to-int c 0))
- (js2-report-scan-error "msg.missing.hex.digits")
- (while (<= 0 (js2-x-digit-to-int c 0))
- (js2-add-to-string c)
- (setq c (js2-get-char)))))
- ((eq base 2)
- (if (not (memq c '(?0 ?1)))
- (js2-report-scan-error "msg.missing.binary.digits")
- (while (memq c '(?0 ?1))
- (js2-add-to-string c)
- (setq c (js2-get-char)))))
- ((eq base 8)
- (if (or (> ?0 c) (< ?7 c))
- (js2-report-scan-error "msg.missing.octal.digits")
- (while (and (<= ?0 c) (>= ?7 c))
- (js2-add-to-string c)
- (setq c (js2-get-char)))))
- (t
- (while (and (<= ?0 c) (<= c ?9))
- ;; We permit 08 and 09 as decimal numbers, which
- ;; makes our behavior a superset of the ECMA
- ;; numeric grammar. We might not always be so
- ;; permissive, so we warn about it.
- (when (and (eq base 'maybe-8) (>= c ?8))
- (js2-report-warning "msg.bad.octal.literal"
- (if (eq c ?8) "8" "9"))
- (setq base 10))
- (js2-add-to-string c)
- (setq c (js2-get-char)))
- (when (eq base 'maybe-8)
- (setq base 8))))
- (when (and (eq base 10) (memq c '(?. ?e ?E)))
- (when (eq c ?.)
- (cl-loop do
- (js2-add-to-string c)
- (setq c (js2-get-char))
- while (js2-digit-p c)))
- (when (memq c '(?e ?E))
- (js2-add-to-string c)
- (setq c (js2-get-char))
- (when (memq c '(?+ ?-))
- (js2-add-to-string c)
- (setq c (js2-get-char)))
- (unless (js2-digit-p c)
- (js2-report-scan-error "msg.missing.exponent" t))
- (cl-loop do
- (js2-add-to-string c)
- (setq c (js2-get-char))
- while (js2-digit-p c))))
- (js2-unget-char)
- (let ((str (js2-set-string-from-buffer token)))
- (setf (js2-token-number token)
- (js2-string-to-number str base)))
- (throw 'return js2-NUMBER))
- ;; is it a string?
- (when (or (memq c '(?\" ?\'))
- (and (>= js2-language-version 200)
- (= c ?`)))
- (throw 'return
- (js2-get-string-or-template-token c token)))
- (js2-ts-return token
- (cl-case c
- (?\;
- (throw 'return js2-SEMI))
- (?\[
- (throw 'return js2-LB))
- (?\]
- (throw 'return js2-RB))
- (?{
- (throw 'return js2-LC))
- (?}
- (throw 'return js2-RC))
- (?\(
- (throw 'return js2-LP))
- (?\)
- (throw 'return js2-RP))
- (?,
- (throw 'return js2-COMMA))
- (??
- (throw 'return js2-HOOK))
- (?:
- (if (js2-match-char ?:)
- js2-COLONCOLON
- (throw 'return js2-COLON)))
- (?.
- (if (js2-match-char ?.)
- (if (js2-match-char ?.)
- js2-TRIPLEDOT js2-DOTDOT)
- (if (js2-match-char ?\()
- js2-DOTQUERY
- (throw 'return js2-DOT))))
- (?|
- (if (js2-match-char ?|)
- (throw 'return js2-OR)
- (if (js2-match-char ?=)
- js2-ASSIGN_BITOR
- (throw 'return js2-BITOR))))
- (?^
- (if (js2-match-char ?=)
- js2-ASSIGN_BITOR
- (throw 'return js2-BITXOR)))
- (?&
- (if (js2-match-char ?&)
- (throw 'return js2-AND)
- (if (js2-match-char ?=)
- js2-ASSIGN_BITAND
- (throw 'return js2-BITAND))))
- (?=
- (if (js2-match-char ?=)
- (if (js2-match-char ?=)
- js2-SHEQ
- (throw 'return js2-EQ))
- (if (js2-match-char ?>)
- (js2-ts-return token js2-ARROW)
- (throw 'return js2-ASSIGN))))
- (?!
- (if (js2-match-char ?=)
- (if (js2-match-char ?=)
- js2-SHNE
- js2-NE)
- (throw 'return js2-NOT)))
- (?<
- ;; NB:treat HTML begin-comment as comment-till-eol
- (when (js2-match-char ?!)
- (when (js2-match-char ?-)
- (when (js2-match-char ?-)
- (js2-skip-line)
- (setf (js2-token-comment-type
(js2-current-token)) 'html)
- (throw 'return js2-COMMENT)))
- (js2-unget-char))
- (if (js2-match-char ?<)
- (if (js2-match-char ?=)
- js2-ASSIGN_LSH
- js2-LSH)
- (if (js2-match-char ?=)
- js2-LE
- (throw 'return js2-LT))))
- (?>
- (if (js2-match-char ?>)
- (if (js2-match-char ?>)
- (if (js2-match-char ?=)
- js2-ASSIGN_URSH
- js2-URSH)
- (if (js2-match-char ?=)
- js2-ASSIGN_RSH
- js2-RSH))
- (if (js2-match-char ?=)
- js2-GE
- (throw 'return js2-GT))))
- (?*
- (if (js2-match-char ?=)
- js2-ASSIGN_MUL
- (throw 'return js2-MUL)))
- (?/
- ;; is it a // comment?
- (when (js2-match-char ?/)
- (setf (js2-token-beg token) (- js2-ts-cursor 2))
- (js2-skip-line)
- (setf (js2-token-comment-type token) 'line)
- ;; include newline so highlighting goes to end of
window
- (cl-incf (js2-token-end token))
- (throw 'return js2-COMMENT))
- ;; is it a /* comment?
- (when (js2-match-char ?*)
- (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)
- 'jsdoc)
- 'block))
- (while t
- (setq c (js2-get-char))
- (cond
- ((eq c js2-EOF_CHAR)
- (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 token js2-COMMENT)))
- (t
- (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)
- (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
- (throw 'return js2-MOD)))
- (?~
- (throw 'return js2-BITNOT))
- (?+
- (if (js2-match-char ?=)
- js2-ASSIGN_ADD
- (if (js2-match-char ?+)
- js2-INC
- (throw 'return js2-ADD))))
- (?-
- (cond
- ((js2-match-char ?=)
- (setq c js2-ASSIGN_SUB))
- ((js2-match-char ?-)
- (unless js2-ts-dirty-line
- ;; treat HTML end-comment after possible
whitespace
- ;; after line start as comment-until-eol
- (when (js2-match-char ?>)
- (js2-skip-line)
- (setf (js2-token-comment-type
(js2-current-token)) 'html)
- (throw 'return js2-COMMENT)))
- (setq c js2-DEC))
- (t
- (setq c js2-SUB)))
- (setq js2-ts-dirty-line t)
- c)
- (otherwise
- (js2-report-scan-error
"msg.illegal.character")))))))
- (setf (js2-token-type token) tt)
- token))
-
-(defun js2-get-string-or-template-token (quote-char token)
- ;; We attempt to accumulate a string the fast way, by
- ;; building it directly out of the reader. But if there
- ;; are any escaped characters in the string, we revert to
- ;; building it out of a string buffer.
- (let ((c (js2-get-char))
- js2-ts-string-buffer
- nc)
- (catch 'break
- (while (/= c quote-char)
- (catch 'continue
- (when (eq c js2-EOF_CHAR)
- (js2-unget-char)
- (js2-report-error "msg.unterminated.string.lit")
- (throw 'break nil))
- (when (and (eq c ?\n) (not (eq quote-char ?`)))
- (js2-unget-char)
- (js2-report-error "msg.unterminated.string.lit")
- (throw 'break nil))
- (when (eq c ?\\)
- ;; We've hit an escaped character
- (setq c (js2-get-char))
- (cl-case c
- (?b (setq c ?\b))
- (?f (setq c ?\f))
- (?n (setq c ?\n))
- (?r (setq c ?\r))
- (?t (setq c ?\t))
- (?v (setq c ?\v))
- (?u
- (setq c1 (js2-read-unicode-escape))
- (if js2-parse-ide-mode
- (if c1
- (progn
- ;; just copy the string in IDE-mode
- (js2-add-to-string ?\\)
- (js2-add-to-string ?u)
- (dotimes (_ 3)
- (js2-add-to-string (js2-get-char)))
- (setq c (js2-get-char))) ; added at end of loop
- ;; flag it as an invalid escape
- (js2-report-warning "msg.invalid.escape"
- nil (- js2-ts-cursor 2) 6))
- ;; Get 4 hex digits; if the u escape is not
- ;; followed by 4 hex digits, use 'u' + the
- ;; literal character sequence that follows.
- (js2-add-to-string ?u)
- (setq escape-val 0)
- (dotimes (_ 4)
- (setq c (js2-get-char)
- escape-val (js2-x-digit-to-int c escape-val))
- (if (cl-minusp escape-val)
- (throw 'continue nil))
- (js2-add-to-string c))
- ;; prepare for replace of stored 'u' sequence by escape value
- (setq js2-ts-string-buffer (nthcdr 5 js2-ts-string-buffer)
- c escape-val)))
- (?x
- ;; Get 2 hex digits, defaulting to 'x'+literal
- ;; sequence, as above.
- (setq c (js2-get-char)
- escape-val (js2-x-digit-to-int c 0))
- (if (cl-minusp escape-val)
- (progn
- (js2-add-to-string ?x)
- (throw 'continue nil))
- (setq c1 c
- c (js2-get-char)
- escape-val (js2-x-digit-to-int c escape-val))
- (if (cl-minusp escape-val)
- (progn
- (js2-add-to-string ?x)
- (js2-add-to-string c1)
- (throw 'continue nil))
- ;; got 2 hex digits
- (setq c escape-val))))
- (?\n
- ;; Remove line terminator after escape to follow
- ;; SpiderMonkey and C/C++
- (setq c (js2-get-char))
- (throw 'continue nil))
- (t
- (when (and (<= ?0 c) (< c ?8))
- (setq val (- c ?0)
- c (js2-get-char))
- (when (and (<= ?0 c) (< c ?8))
- (setq val (- (+ (* 8 val) c) ?0)
- c (js2-get-char))
- (when (and (<= ?0 c)
- (< c ?8)
- (< val #o37))
- ;; c is 3rd char of octal sequence only
- ;; if the resulting val <= 0377
- (setq val (- (+ (* 8 val) c) ?0)
- c (js2-get-char))))
- (js2-unget-char)
- (setq c val)))))
- (when (and (eq quote-char ?`) (eq c ?$))
- (when (eq (setq nc (js2-get-char)) ?\{)
- (throw 'break nil))
- (js2-unget-char))
- (js2-add-to-string c)
- (setq c (js2-get-char)))))
- (js2-set-string-from-buffer token)
- (if (not (eq quote-char ?`))
- js2-STRING
- (if (and (eq c ?$) (eq nc ?\{))
- js2-TEMPLATE_HEAD
- js2-NO_SUBS_TEMPLATE))))
-
-(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)
- (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-tt js2-DIV))
- (error "failed assertion")))
- (while (and (not err)
- (or (/= (setq c (js2-get-char)) ?/)
- in-class))
- (cond
- ((or (= c ?\n)
- (= c js2-EOF_CHAR))
- (setf (js2-token-end token) (1- js2-ts-cursor)
- err t
- (js2-token-string token) (js2-collect-string
js2-ts-string-buffer))
- (js2-report-error "msg.unterminated.re.lit"))
- (t (cond
- ((= c ?\\)
- (js2-add-to-string c)
- (setq c (js2-get-char)))
- ((= c ?\[)
- (setq in-class t))
- ((= c ?\])
- (setq in-class nil)))
- (js2-add-to-string c))))
- (unless err
- (while continue
- (cond
- ((js2-match-char ?g)
- (push ?g flags))
- ((js2-match-char ?i)
- (push ?i flags))
- ((js2-match-char ?m)
- (push ?m flags))
- ((and (js2-match-char ?u)
- (>= js2-language-version 200))
- (push ?u flags))
- ((and (js2-match-char ?y)
- (>= js2-language-version 200))
- (push ?y flags))
- (t
- (setq continue nil))))
- (if (js2-alpha-p (js2-peek-char))
- (js2-report-scan-error "msg.invalid.re.flag" t
- js2-ts-cursor 1))
- (js2-set-string-from-buffer token))
- (js2-collect-string flags)))
-
-(defun js2-get-first-xml-token ()
- (setq js2-ts-xml-open-tags-count 0
- js2-ts-is-xml-attribute nil
- js2-ts-xml-is-tag-content nil)
- (js2-unget-char)
- (js2-get-next-xml-token))
-
-(defun js2-xml-discard-string (token)
- "Throw away the string in progress and flag an XML parse error."
- (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
- (let ((token (js2-new-token 0))
- c result)
- (setq result
- (catch 'return
- (while t
- (setq c (js2-get-char))
- (cond
- ((= c js2-EOF_CHAR)
- (throw 'return js2-ERROR))
- (js2-ts-xml-is-tag-content
- (cl-case c
- (?>
- (js2-add-to-string c)
- (setq js2-ts-xml-is-tag-content nil
- js2-ts-is-xml-attribute nil))
- (?/
- (js2-add-to-string c)
- (when (eq ?> (js2-peek-char))
- (setq c (js2-get-char))
- (js2-add-to-string c)
- (setq js2-ts-xml-is-tag-content nil)
- (cl-decf js2-ts-xml-open-tags-count)))
- (?{
- (js2-unget-char)
- (js2-set-string-from-buffer token)
- (throw 'return js2-XML))
- ((?\' ?\")
- (js2-add-to-string c)
- (unless (js2-read-quoted-string c token)
- (throw 'return js2-ERROR)))
- (?=
- (js2-add-to-string c)
- (setq js2-ts-is-xml-attribute t))
- ((? ?\t ?\r ?\n)
- (js2-add-to-string c))
- (t
- (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))
- (js2-set-string-from-buffer token)
- (throw 'return js2-XMLEND)))
- (t
- ;; else not tag content
- (cl-case c
- (?<
- (js2-add-to-string c)
- (setq c (js2-peek-char))
- (cl-case c
- (?!
- (setq c (js2-get-char)) ;; skip !
- (js2-add-to-string c)
- (setq c (js2-peek-char))
- (cl-case c
- (?-
- (setq c (js2-get-char)) ;; skip -
- (js2-add-to-string c)
- (if (eq c ?-)
- (progn
- (js2-add-to-string c)
- (unless (js2-read-xml-comment token)
- (throw 'return js2-ERROR)))
- (js2-xml-discard-string token)
- (throw 'return js2-ERROR)))
- (?\[
- (setq c (js2-get-char)) ;; skip [
- (js2-add-to-string c)
- (if (and (= (js2-get-char) ?C)
- (= (js2-get-char) ?D)
- (= (js2-get-char) ?A)
- (= (js2-get-char) ?T)
- (= (js2-get-char) ?A)
- (= (js2-get-char) ?\[))
- (progn
- (js2-add-to-string ?C)
- (js2-add-to-string ?D)
- (js2-add-to-string ?A)
- (js2-add-to-string ?T)
- (js2-add-to-string ?A)
- (js2-add-to-string ?\[)
- (unless (js2-read-cdata token)
- (throw 'return js2-ERROR)))
- (js2-xml-discard-string token)
- (throw 'return js2-ERROR)))
- (t
- (unless (js2-read-entity token)
- (throw 'return js2-ERROR))))
- ;; Allow bare CDATA section, e.g.:
- ;; let xml = <![CDATA[ foo bar baz ]]>;
- (when (zerop js2-ts-xml-open-tags-count)
- (throw 'return js2-XMLEND)))
- (??
- (setq c (js2-get-char)) ;; skip ?
- (js2-add-to-string c)
- (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 token)
- (throw 'return js2-ERROR))
- (setq js2-ts-xml-is-tag-content t)
- (cl-decf js2-ts-xml-open-tags-count))
- (t
- ;; start tag
- (setq js2-ts-xml-is-tag-content t)
- (cl-incf js2-ts-xml-open-tags-count))))
- (?{
- (js2-unget-char)
- (js2-set-string-from-buffer token)
- (throw 'return js2-XML))
- (t
- (js2-add-to-string c))))))))
- (setf (js2-token-end token) js2-ts-cursor)
- (setf (js2-token-type token) result)
- result))
-
-(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 token) ;; throw away string in progress
- nil)))
-
-(defun js2-read-xml-comment (token)
- (let ((c (js2-get-char)))
- (catch 'return
- (while (/= c js2-EOF_CHAR)
- (catch 'continue
- (js2-add-to-string c)
- (when (and (eq c ?-) (eq ?- (js2-peek-char)))
- (setq c (js2-get-char))
- (js2-add-to-string c)
- (if (eq (js2-peek-char) ?>)
- (progn
- (setq c (js2-get-char)) ;; skip >
- (js2-add-to-string c)
- (throw 'return t))
- (throw 'continue nil)))
- (setq c (js2-get-char))))
- (js2-xml-discard-string token)
- nil)))
-
-(defun js2-read-cdata (token)
- (let ((c (js2-get-char)))
- (catch 'return
- (while (/= c js2-EOF_CHAR)
- (catch 'continue
- (js2-add-to-string c)
- (when (and (eq c ?\]) (eq (js2-peek-char) ?\]))
- (setq c (js2-get-char))
- (js2-add-to-string c)
- (if (eq (js2-peek-char) ?>)
- (progn
- (setq c (js2-get-char)) ;; Skip >
- (js2-add-to-string c)
- (throw 'return t))
- (throw 'continue nil)))
- (setq c (js2-get-char))))
- (js2-xml-discard-string token)
- nil)))
-
-(defun js2-read-entity (token)
- (let ((decl-tags 1)
- c)
- (catch 'return
- (while (/= js2-EOF_CHAR (setq c (js2-get-char)))
- (js2-add-to-string c)
- (cl-case c
- (?<
- (cl-incf decl-tags))
- (?>
- (cl-decf decl-tags)
- (if (zerop decl-tags)
- (throw 'return t)))))
- (js2-xml-discard-string token)
- nil)))
-
-(defun js2-read-PI (token)
- "Scan an XML processing instruction."
- (let (c)
- (catch 'return
- (while (/= js2-EOF_CHAR (setq c (js2-get-char)))
- (js2-add-to-string c)
- (when (and (eq c ??) (eq (js2-peek-char) ?>))
- (setq c (js2-get-char)) ;; Skip >
- (js2-add-to-string c)
- (throw 'return t)))
- (js2-xml-discard-string token)
- nil)))
-
-;;; Highlighting
-
-(defun js2-set-face (beg end face &optional record)
- "Fontify a region. If RECORD is non-nil, record for later."
- (when (cl-plusp js2-highlight-level)
- (setq beg (min (point-max) beg)
- beg (max (point-min) beg)
- end (min (point-max) end)
- end (max (point-min) end))
- (if record
- (push (list beg end face) js2-mode-fontifications)
- (put-text-property beg end 'font-lock-face face))))
-
-(defsubst js2-clear-face (beg end)
- (remove-text-properties beg end '(font-lock-face nil
- help-echo nil
- point-entered nil
- c-in-sws nil)))
-
-(defconst js2-ecma-global-props
- (concat "^"
- (regexp-opt
- '("Infinity" "NaN" "undefined" "arguments") t)
- "$")
- "Value properties of the Ecma-262 Global Object.
-Shown at or above `js2-highlight-level' 2.")
-
-;; might want to add the name "arguments" to this list?
-(defconst js2-ecma-object-props
- (concat "^"
- (regexp-opt
- '("prototype" "__proto__" "__parent__") t)
- "$")
- "Value properties of the Ecma-262 Object constructor.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-global-funcs
- (concat
- "^"
- (regexp-opt
- '("decodeURI" "decodeURIComponent" "encodeURI" "encodeURIComponent"
- "eval" "isFinite" "isNaN" "parseFloat" "parseInt") t)
- "$")
- "Function properties of the Ecma-262 Global object.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-number-props
- (concat "^"
- (regexp-opt '("MAX_VALUE" "MIN_VALUE" "NaN"
- "NEGATIVE_INFINITY"
- "POSITIVE_INFINITY") t)
- "$")
- "Properties of the Ecma-262 Number constructor.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-date-props "^\\(parse\\|UTC\\)$"
- "Properties of the Ecma-262 Date constructor.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-math-props
- (concat "^"
- (regexp-opt
- '("E" "LN10" "LN2" "LOG2E" "LOG10E" "PI" "SQRT1_2" "SQRT2")
- t)
- "$")
- "Properties of the Ecma-262 Math object.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-math-funcs
- (concat "^"
- (regexp-opt
- '("abs" "acos" "asin" "atan" "atan2" "ceil" "cos" "exp" "floor"
- "log" "max" "min" "pow" "random" "round" "sin" "sqrt" "tan") t)
- "$")
- "Function properties of the Ecma-262 Math object.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-function-props
- (concat
- "^"
- (regexp-opt
- '(;; properties of the Object prototype object
- "hasOwnProperty" "isPrototypeOf" "propertyIsEnumerable"
- "toLocaleString" "toString" "valueOf"
- ;; properties of the Function prototype object
- "apply" "call"
- ;; properties of the Array prototype object
- "concat" "join" "pop" "push" "reverse" "shift" "slice" "sort"
- "splice" "unshift"
- ;; properties of the String prototype object
- "charAt" "charCodeAt" "fromCharCode" "indexOf" "lastIndexOf"
- "localeCompare" "match" "replace" "search" "split" "substring"
- "toLocaleLowerCase" "toLocaleUpperCase" "toLowerCase"
- "toUpperCase"
- ;; properties of the Number prototype object
- "toExponential" "toFixed" "toPrecision"
- ;; properties of the Date prototype object
- "getDate" "getDay" "getFullYear" "getHours" "getMilliseconds"
- "getMinutes" "getMonth" "getSeconds" "getTime"
- "getTimezoneOffset" "getUTCDate" "getUTCDay" "getUTCFullYear"
- "getUTCHours" "getUTCMilliseconds" "getUTCMinutes" "getUTCMonth"
- "getUTCSeconds" "setDate" "setFullYear" "setHours"
- "setMilliseconds" "setMinutes" "setMonth" "setSeconds" "setTime"
- "setUTCDate" "setUTCFullYear" "setUTCHours" "setUTCMilliseconds"
- "setUTCMinutes" "setUTCMonth" "setUTCSeconds" "toDateString"
- "toLocaleDateString" "toLocaleString" "toLocaleTimeString"
- "toTimeString" "toUTCString"
- ;; properties of the RegExp prototype object
- "exec" "test"
- ;; properties of the JSON prototype object
- "parse" "stringify"
- ;; SpiderMonkey/Rhino extensions, versions 1.5+
- "toSource" "__defineGetter__" "__defineSetter__"
- "__lookupGetter__" "__lookupSetter__" "__noSuchMethod__"
- "every" "filter" "forEach" "lastIndexOf" "map" "some")
- t)
- "$")
- "Built-in functions defined by Ecma-262 and SpiderMonkey extensions.
-Shown at or above `js2-highlight-level' 3.")
-
-(defun js2-parse-highlight-prop-get (parent target prop call-p)
- (let ((target-name (and target
- (js2-name-node-p target)
- (js2-name-node-name target)))
- (prop-name (if prop (js2-name-node-name prop)))
- (level2 (>= js2-highlight-level 2))
- (level3 (>= js2-highlight-level 3)))
- (when level2
- (let ((face
- (if call-p
- (cond
- ((and target prop)
- (cond
- ((and level3 (string-match js2-ecma-function-props
prop-name))
- 'font-lock-builtin-face)
- ((and target-name prop)
- (cond
- ((string= target-name "Date")
- (if (string-match js2-ecma-date-props prop-name)
- 'font-lock-builtin-face))
- ((string= target-name "Math")
- (if (string-match js2-ecma-math-funcs prop-name)
- 'font-lock-builtin-face))))))
- (prop
- (if (string-match js2-ecma-global-funcs prop-name)
- 'font-lock-builtin-face)))
- (cond
- ((and target prop)
- (cond
- ((string= target-name "Number")
- (if (string-match js2-ecma-number-props prop-name)
- 'font-lock-constant-face))
- ((string= target-name "Math")
- (if (string-match js2-ecma-math-props prop-name)
- 'font-lock-constant-face))))
- (prop
- (if (string-match js2-ecma-object-props prop-name)
- 'font-lock-constant-face))))))
- (when face
- (let ((pos (+ (js2-node-pos parent) ; absolute
- (js2-node-pos prop)))) ; relative
- (js2-set-face pos
- (+ pos (js2-node-len prop))
- face 'record)))))))
-
-(defun js2-parse-highlight-member-expr-node (node)
- "Perform syntax highlighting of EcmaScript built-in properties.
-The variable `js2-highlight-level' governs this highighting."
- (let (face target prop name pos end parent call-p callee)
- (cond
- ;; case 1: simple name, e.g. foo
- ((js2-name-node-p node)
- (setq name (js2-name-node-name node))
- ;; possible for name to be nil in rare cases - saw it when
- ;; running js2-mode on an elisp buffer. Might as well try to
- ;; make it so js2-mode never barfs.
- (when name
- (setq face (if (string-match js2-ecma-global-props name)
- 'font-lock-constant-face))
- (when face
- (setq pos (js2-node-pos node)
- end (+ pos (js2-node-len node)))
- (js2-set-face pos end face 'record))))
- ;; case 2: property access or function call
- ((or (js2-prop-get-node-p node)
- ;; highlight function call if expr is a prop-get node
- ;; or a plain name (i.e. unqualified function call)
- (and (setq call-p (js2-call-node-p node))
- (setq callee (js2-call-node-target node)) ; separate setq!
- (or (js2-prop-get-node-p callee)
- (js2-name-node-p callee))))
- (setq parent node
- node (if call-p callee node))
- (if (and call-p (js2-name-node-p callee))
- (setq prop callee)
- (setq target (js2-prop-get-node-left node)
- prop (js2-prop-get-node-right node)))
- (cond
- ((js2-name-node-p prop)
- ;; case 2(a&c): simple or complex target, simple name, e.g. x[y].bar
- (js2-parse-highlight-prop-get parent target prop call-p))
- ((js2-name-node-p target)
- ;; case 2b: simple target, complex name, e.g. foo.x[y]
- (js2-parse-highlight-prop-get parent target nil call-p)))))))
-
-(defun js2-parse-highlight-member-expr-fn-name (expr)
- "Highlight the `baz' in function foo.bar.baz(args) {...}.
-This is experimental Rhino syntax. EXPR is the foo.bar.baz member expr.
-We currently only handle the case where the last component is a prop-get
-of a simple name. Called before EXPR has a parent node."
- (let (pos
- (name (and (js2-prop-get-node-p expr)
- (js2-prop-get-node-right expr))))
- (when (js2-name-node-p name)
- (js2-set-face (setq pos (+ (js2-node-pos expr) ; parent is absolute
- (js2-node-pos name)))
- (+ pos (js2-node-len name))
- 'font-lock-function-name-face
- 'record))))
-
-;; source: http://jsdoc.sourceforge.net/
-;; Note - this syntax is for Google's enhanced jsdoc parser that
-;; allows type specifications, and needs work before entering the wild.
-
-(defconst js2-jsdoc-param-tag-regexp
- (concat "^\\s-*\\*+\\s-*\\(@"
- "\\(?:param\\|argument\\)"
- "\\)"
- "\\s-*\\({[^}]+}\\)?" ; optional type
- "\\s-*\\[?\\([[:alnum:]_$\.]+\\)?\\]?" ; name
- "\\>")
- "Matches jsdoc tags with optional type and optional param name.")
-
-(defconst js2-jsdoc-typed-tag-regexp
- (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
- (regexp-opt
- '("enum"
- "extends"
- "field"
- "id"
- "implements"
- "lends"
- "mods"
- "requires"
- "return"
- "returns"
- "throw"
- "throws"))
- "\\)\\)\\s-*\\({[^}]+}\\)?")
- "Matches jsdoc tags with optional type.")
-
-(defconst js2-jsdoc-arg-tag-regexp
- (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
- (regexp-opt
- '("alias"
- "augments"
- "borrows"
- "bug"
- "base"
- "config"
- "default"
- "define"
- "exception"
- "function"
- "member"
- "memberOf"
- "name"
- "namespace"
- "property"
- "since"
- "suppress"
- "this"
- "throws"
- "type"
- "version"))
- "\\)\\)\\s-+\\([^ \t]+\\)")
- "Matches jsdoc tags with a single argument.")
-
-(defconst js2-jsdoc-empty-tag-regexp
- (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
- (regexp-opt
- '("addon"
- "author"
- "class"
- "const"
- "constant"
- "constructor"
- "constructs"
- "deprecated"
- "desc"
- "description"
- "event"
- "example"
- "exec"
- "export"
- "fileoverview"
- "final"
- "function"
- "hidden"
- "ignore"
- "implicitCast"
- "inheritDoc"
- "inner"
- "interface"
- "license"
- "noalias"
- "noshadow"
- "notypecheck"
- "override"
- "owner"
- "preserve"
- "preserveTry"
- "private"
- "protected"
- "public"
- "static"
- "supported"
- ))
- "\\)\\)\\s-*")
- "Matches empty jsdoc tags.")
-
-(defconst js2-jsdoc-link-tag-regexp
- "{\\(@\\(?:link\\|code\\)\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?}"
- "Matches a jsdoc link or code tag.")
-
-(defconst js2-jsdoc-see-tag-regexp
- "^\\s-*\\*+\\s-*\\(@see\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?"
- "Matches a jsdoc @see tag.")
-
-(defconst js2-jsdoc-html-tag-regexp
- "\\(</?\\)\\([[:alpha:]]+\\)\\s-*\\(/?>\\)"
- "Matches a simple (no attributes) html start- or end-tag.")
-
-(defun js2-jsdoc-highlight-helper ()
- (js2-set-face (match-beginning 1)
- (match-end 1)
- 'js2-jsdoc-tag)
- (if (match-beginning 2)
- (if (save-excursion
- (goto-char (match-beginning 2))
- (= (char-after) ?{))
- (js2-set-face (1+ (match-beginning 2))
- (1- (match-end 2))
- 'js2-jsdoc-type)
- (js2-set-face (match-beginning 2)
- (match-end 2)
- 'js2-jsdoc-value)))
- (if (match-beginning 3)
- (js2-set-face (match-beginning 3)
- (match-end 3)
- 'js2-jsdoc-value)))
-
-(defun js2-highlight-jsdoc (ast)
- "Highlight doc comment tags."
- (let ((comments (js2-ast-root-comments ast))
- beg end)
- (save-excursion
- (dolist (node comments)
- (when (eq (js2-comment-node-format node) 'jsdoc)
- (setq beg (js2-node-abs-pos node)
- end (+ beg (js2-node-len node)))
- (save-restriction
- (narrow-to-region beg end)
- (dolist (re (list js2-jsdoc-param-tag-regexp
- js2-jsdoc-typed-tag-regexp
- js2-jsdoc-arg-tag-regexp
- js2-jsdoc-link-tag-regexp
- js2-jsdoc-see-tag-regexp
- js2-jsdoc-empty-tag-regexp))
- (goto-char beg)
- (while (re-search-forward re nil t)
- (js2-jsdoc-highlight-helper)))
- ;; simple highlighting for html tags
- (goto-char beg)
- (while (re-search-forward js2-jsdoc-html-tag-regexp nil t)
- (js2-set-face (match-beginning 1)
- (match-end 1)
- 'js2-jsdoc-html-tag-delimiter)
- (js2-set-face (match-beginning 2)
- (match-end 2)
- 'js2-jsdoc-html-tag-name)
- (js2-set-face (match-beginning 3)
- (match-end 3)
- 'js2-jsdoc-html-tag-delimiter))))))))
-
-(defun js2-highlight-assign-targets (_node left right)
- "Highlight function properties and external variables."
- (let (leftpos name)
- ;; highlight vars and props assigned function values
- (when (or (js2-function-node-p right)
- (js2-class-node-p right))
- (cond
- ;; var foo = function() {...}
- ((js2-name-node-p left)
- (setq name left))
- ;; foo.bar.baz = function() {...}
- ((and (js2-prop-get-node-p left)
- (js2-name-node-p (js2-prop-get-node-right left)))
- (setq name (js2-prop-get-node-right left))))
- (when name
- (js2-set-face (setq leftpos (js2-node-abs-pos name))
- (+ leftpos (js2-node-len name))
- 'font-lock-function-name-face
- 'record)))))
-
-(defun js2-record-name-node (node)
- "Saves NODE to `js2-recorded-identifiers' to check for undeclared variables
-later. NODE must be a name node."
- (let ((leftpos (js2-node-abs-pos node)))
- (push (list node js2-current-scope
- leftpos
- (+ leftpos (js2-node-len node)))
- js2-recorded-identifiers)))
-
-(defun js2-highlight-undeclared-vars ()
- "After entire parse is finished, look for undeclared variable references.
-We have to wait until entire buffer is parsed, since JavaScript permits var
-decls to occur after they're used.
-
-If any undeclared var name is in `js2-externs' or `js2-additional-externs',
-it is considered declared."
- (let (name)
- (dolist (entry js2-recorded-identifiers)
- (cl-destructuring-bind (name-node scope pos end) entry
- (setq name (js2-name-node-name name-node))
- (unless (or (member name js2-global-externs)
- (member name js2-default-externs)
- (member name js2-additional-externs)
- (js2-get-defining-scope scope name))
- (js2-report-warning "msg.undeclared.variable" name pos (- end pos)
- 'js2-external-variable))))
- (setq js2-recorded-identifiers nil)))
-
-(defun js2-set-default-externs ()
- "Set the value of `js2-default-externs' based on the various
-`js2-include-?-externs' variables."
- (setq js2-default-externs
- (append js2-ecma-262-externs
- (if js2-include-browser-externs js2-browser-externs)
- (if (and js2-include-browser-externs
- (>= js2-language-version 200)) js2-harmony-externs)
- (if js2-include-rhino-externs js2-rhino-externs)
- (if js2-include-node-externs js2-node-externs)
- (if (or js2-include-browser-externs js2-include-node-externs)
- js2-typed-array-externs))))
-
-(defun js2-apply-jslint-globals ()
- (setq js2-additional-externs
- (nconc (js2-get-jslint-globals)
- js2-additional-externs)))
-
-(defun js2-get-jslint-globals ()
- (cl-loop for node in (js2-ast-root-comments js2-mode-ast)
- when (and (eq 'block (js2-comment-node-format node))
- (save-excursion
- (goto-char (js2-node-abs-pos node))
- (looking-at "/\\*global ")))
- append (js2-get-jslint-globals-in
- (match-end 0)
- (js2-node-abs-end node))))
-
-(defun js2-get-jslint-globals-in (beg end)
- (let (res)
- (save-excursion
- (goto-char beg)
- (while (re-search-forward js2-mode-identifier-re end t)
- (let ((match (match-string 0)))
- (unless (member match '("true" "false"))
- (push match res)))))
- (nreverse res)))
-
-;;; IMenu support
-
-;; We currently only support imenu, but eventually should support speedbar and
-;; possibly other browsing mechanisms.
-
-;; The basic strategy is to identify function assignment targets of the form
-;; `foo.bar.baz', convert them to (list fn foo bar baz <position>), and push
the
-;; list into `js2-imenu-recorder'. The lists are merged into a trie-like tree
-;; for imenu after parsing is finished.
-
-;; A `foo.bar.baz' assignment target may be expressed in many ways in
-;; JavaScript, and the general problem is undecidable. However, several forms
-;; are readily recognizable at parse-time; the forms we attempt to recognize
-;; include:
-
-;; function foo() -- function declaration
-;; foo = function() -- function expression assigned to variable
-;; foo.bar.baz = function() -- function expr assigned to nested property-get
-;; foo = {bar: function()} -- fun prop in object literal assigned to var
-;; foo = {bar: {baz: function()}} -- inside nested object literal
-;; foo.bar = {baz: function()}} -- obj lit assigned to nested prop get
-;; a.b = {c: {d: function()}} -- nested obj lit assigned to nested prop get
-;; foo = {get bar() {...}} -- getter/setter in obj literal
-;; function foo() {function bar() {...}} -- nested function
-;; foo['a'] = function() -- fun expr assigned to deterministic element-get
-
-;; This list boils down to a few forms that can be combined recursively.
-;; Top-level named function declarations include both the left-hand (name)
-;; and the right-hand (function value) expressions needed to produce an imenu
-;; entry. The other "right-hand" forms we need to look for are:
-;; - functions declared as props/getters/setters in object literals
-;; - nested named function declarations
-;; The "left-hand" expressions that functions can be assigned to include:
-;; - local/global variables
-;; - nested property-get expressions like a.b.c.d
-;; - element gets like foo[10] or foo['bar'] where the index
-;; expression can be trivially converted to a property name. They
-;; effectively then become property gets.
-
-;; All the different definition types are canonicalized into the form
-;; foo.bar.baz = position-of-function-keyword
-
-;; We need to build a trie-like structure for imenu. As an example,
-;; consider the following JavaScript code:
-
-;; a = function() {...} // function at position 5
-;; b = function() {...} // function at position 25
-;; foo = function() {...} // function at position 100
-;; foo.bar = function() {...} // function at position 200
-;; foo.bar.baz = function() {...} // function at position 300
-;; foo.bar.zab = function() {...} // function at position 400
-
-;; During parsing we accumulate an entry for each definition in
-;; the variable `js2-imenu-recorder', like so:
-
-;; '((fn a 5)
-;; (fn b 25)
-;; (fn foo 100)
-;; (fn foo bar 200)
-;; (fn foo bar baz 300)
-;; (fn foo bar zab 400))
-
-;; Where 'fn' is the respective function node.
-;; After parsing these entries are merged into this alist-trie:
-
-;; '((a . 1)
-;; (b . 2)
-;; (foo (<definition> . 3)
-;; (bar (<definition> . 6)
-;; (baz . 100)
-;; (zab . 200))))
-
-;; Note the wacky need for a <definition> name. The token can be anything
-;; that isn't a valid JavaScript identifier, because you might make foo
-;; a function and then start setting properties on it that are also functions.
-
-(defun js2-prop-node-name (node)
- "Return the name of a node that may be a property-get/property-name.
-If NODE is not a valid name-node, string-node or integral number-node,
-returns nil. Otherwise returns the string name/value of the node."
- (cond
- ((js2-name-node-p node)
- (js2-name-node-name node))
- ((js2-string-node-p node)
- (js2-string-node-value node))
- ((and (js2-number-node-p node)
- (string-match "^[0-9]+$" (js2-number-node-value node)))
- (js2-number-node-value node))
- ((eq (js2-node-type node) js2-THIS)
- "this")
- ((eq (js2-node-type node) js2-SUPER)
- "super")))
-
-(defun js2-node-qname-component (node)
- "Return the name of this node, if it contributes to a qname.
-Returns nil if the node doesn't contribute."
- (copy-sequence
- (or (js2-prop-node-name node)
- (if (and (js2-function-node-p node)
- (js2-function-node-name node))
- (js2-name-node-name (js2-function-node-name node))))))
-
-(defun js2-record-imenu-entry (fn-node qname pos)
- "Add an entry to `js2-imenu-recorder'.
-FN-NODE should be the current item's function node.
-
-Associate FN-NODE with its QNAME for later lookup.
-This is used in postprocessing the chain list. For each chain, we find
-the parent function, look up its qname, then prepend a copy of it to the
chain."
- (push (cons fn-node (append qname (list pos))) js2-imenu-recorder)
- (unless js2-imenu-function-map
- (setq js2-imenu-function-map (make-hash-table :test 'eq)))
- (puthash fn-node qname js2-imenu-function-map))
-
-(defun js2-record-imenu-functions (node &optional var)
- "Record function definitions for imenu.
-NODE is a function node or an object literal.
-VAR, if non-nil, is the expression that NODE is being assigned to.
-When passed arguments of wrong type, does nothing."
- (when js2-parse-ide-mode
- (let ((fun-p (js2-function-node-p node))
- qname fname-node)
- (cond
- ;; non-anonymous function declaration?
- ((and fun-p
- (not var)
- (setq fname-node (js2-function-node-name node)))
- (js2-record-imenu-entry node (list fname-node) (js2-node-pos node)))
- ;; for remaining forms, compute left-side tree branch first
- ((and var (setq qname (js2-compute-nested-prop-get var)))
- (cond
- ;; foo.bar.baz = function
- (fun-p
- (js2-record-imenu-entry node qname (js2-node-pos node)))
- ;; foo.bar.baz = object-literal
- ;; look for nested functions: {a: {b: function() {...} }}
- ((js2-object-node-p node)
- ;; Node position here is still absolute, since the parser
- ;; passes the assignment target and value expressions
- ;; to us before they are added as children of the assignment node.
- (js2-record-object-literal node qname (js2-node-pos node)))))))))
-
-(defun js2-compute-nested-prop-get (node)
- "If NODE is of form foo.bar, foo['bar'], or any nested combination, return
-component nodes as a list. Otherwise return nil. Element-gets are treated
-as property-gets if the index expression is a string, or a positive integer."
- (let (left right head)
- (cond
- ((or (js2-name-node-p node)
- (js2-this-or-super-node-p node))
- (list node))
- ;; foo.bar.baz is parenthesized as (foo.bar).baz => right operand is a
leaf
- ((js2-prop-get-node-p node) ; foo.bar
- (setq left (js2-prop-get-node-left node)
- right (js2-prop-get-node-right node))
- (if (setq head (js2-compute-nested-prop-get left))
- (nconc head (list right))))
- ((js2-elem-get-node-p node) ; foo['bar'] or foo[101]
- (setq left (js2-elem-get-node-target node)
- right (js2-elem-get-node-element node))
- (if (or (js2-string-node-p right) ; ['bar']
- (and (js2-number-node-p right) ; [10]
- (string-match "^[0-9]+$"
- (js2-number-node-value right))))
- (if (setq head (js2-compute-nested-prop-get left))
- (nconc head (list right))))))))
-
-(defun js2-record-object-literal (node qname pos)
- "Recursively process an object literal looking for functions.
-NODE is an object literal that is the right-hand child of an assignment
-expression. QNAME is a list of nodes representing the assignment target,
-e.g. for foo.bar.baz = {...}, QNAME is (foo-node bar-node baz-node).
-POS is the absolute position of the node.
-We do a depth-first traversal of NODE. For any functions we find,
-we append the property name to QNAME, then call `js2-record-imenu-entry'."
- (let (right)
- (dolist (e (js2-object-node-elems node)) ; e is a `js2-object-prop-node'
- (let ((left (js2-infix-node-left e))
- ;; Element positions are relative to the parent position.
- (pos (+ pos (js2-node-pos e))))
- (cond
- ;; foo: function() {...}
- ((js2-function-node-p (setq right (js2-infix-node-right e)))
- (when (js2-prop-node-name left)
- ;; As a policy decision, we record the position of the property,
- ;; not the position of the `function' keyword, since the property
- ;; is effectively the name of the function.
- (js2-record-imenu-entry right (append qname (list left)) pos)))
- ;; foo: {object-literal} -- add foo to qname, offset position, and
recurse
- ((js2-object-node-p right)
- (js2-record-object-literal right
- (append qname (list (js2-infix-node-left
e)))
- (+ pos (js2-node-pos right)))))))))
-
-(defun js2-node-top-level-decl-p (node)
- "Return t if NODE's name is defined in the top-level scope.
-Also returns t if NODE's name is not defined in any scope, since it implies
-that it's an external variable, which must also be in the top-level scope."
- (let* ((name (js2-prop-node-name node))
- (this-scope (js2-node-get-enclosing-scope node))
- defining-scope)
- (cond
- ((js2-this-or-super-node-p node)
- nil)
- ((null this-scope)
- t)
- ((setq defining-scope (js2-get-defining-scope this-scope name))
- (js2-ast-root-p defining-scope))
- (t t))))
-
-(defun js2-wrapper-function-p (node)
- "Return t if NODE is a function expression that's immediately invoked.
-NODE must be `js2-function-node'."
- (let ((parent (js2-node-parent node)))
- (or
- ;; function(){...}();
- (and (js2-call-node-p parent)
- (eq node (js2-call-node-target parent)))
- (and (js2-paren-node-p parent)
- ;; (function(){...})();
- (or (js2-call-node-p (setq parent (js2-node-parent parent)))
- ;; (function(){...}).call(this);
- (and (js2-prop-get-node-p parent)
- (member (js2-name-node-name (js2-prop-get-node-right
parent))
- '("call" "apply"))
- (js2-call-node-p (js2-node-parent parent))))))))
-
-(defun js2-browse-postprocess-chains ()
- "Modify function-declaration name chains after parsing finishes.
-Some of the information is only available after the parse tree is complete.
-For instance, processing a nested scope requires a parent function node."
- (let (result fn parent-qname p elem)
- (dolist (entry js2-imenu-recorder)
- ;; function node goes first
- (cl-destructuring-bind (current-fn &rest (&whole chain head &rest)) entry
- ;; Examine head's defining scope:
- ;; Pre-processed chain, or top-level/external, keep as-is.
- (if (or (stringp head) (js2-node-top-level-decl-p head))
- (push chain result)
- (when (js2-this-or-super-node-p head)
- (setq chain (cdr chain))) ; discard this-node
- (when (setq fn (js2-node-parent-script-or-fn current-fn))
- (setq parent-qname (gethash fn js2-imenu-function-map 'not-found))
- (when (eq parent-qname 'not-found)
- ;; anonymous function expressions are not recorded
- ;; during the parse, so we need to handle this case here
- (setq parent-qname
- (if (js2-wrapper-function-p fn)
- (let ((grandparent (js2-node-parent-script-or-fn fn)))
- (if (js2-ast-root-p grandparent)
- nil
- (gethash grandparent js2-imenu-function-map
'skip)))
- 'skip))
- (puthash fn parent-qname js2-imenu-function-map))
- (if (eq parent-qname 'skip)
- ;; We don't show it, let's record that fact.
- (remhash current-fn js2-imenu-function-map)
- ;; Prepend parent fn qname to this chain.
- (let ((qname (append parent-qname chain)))
- (puthash current-fn (butlast qname) js2-imenu-function-map)
- (push qname result)))))))
- ;; Collect chains obtained by third-party code.
- (let (js2-imenu-recorder)
- (run-hooks 'js2-build-imenu-callbacks)
- (dolist (entry js2-imenu-recorder)
- (push (cdr entry) result)))
- ;; Finally replace each node in each chain with its name.
- (dolist (chain result)
- (setq p chain)
- (while p
- (if (js2-node-p (setq elem (car p)))
- (setcar p (js2-node-qname-component elem)))
- (setq p (cdr p))))
- result))
-
-;; Merge name chains into a trie-like tree structure of nested lists.
-;; To simplify construction of the trie, we first build it out using the rule
-;; that the trie consists of lists of pairs. Each pair is a 2-element array:
-;; [key, num-or-list]. The second element can be a number; if so, this key
-;; is a leaf-node with only one value. (I.e. there is only one declaration
-;; associated with the key at this level.) Otherwise the second element is
-;; a list of pairs, with the rule applied recursively. This symmetry permits
-;; a simple recursive formulation.
-;;
-;; js2-mode is building the data structure for imenu. The imenu documentation
-;; claims that it's the structure above, but in practice it wants the children
-;; at the same list level as the key for that level, which is how I've drawn
-;; the "Expected final result" above. We'll postprocess the trie to remove the
-;; list wrapper around the children at each level.
-;;
-;; A completed nested imenu-alist entry looks like this:
-;; '(("foo"
-;; ("<definition>" . 7)
-;; ("bar"
-;; ("a" . 40)
-;; ("b" . 60))))
-;;
-;; In particular, the documentation for `imenu--index-alist' says that
-;; a nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
-;; The sub-alist entries immediately follow INDEX-NAME, the head of the list.
-
-(defun js2-treeify (lst)
- "Convert (a b c d) to (a ((b ((c d)))))."
- (if (null (cddr lst)) ; list length <= 2
- lst
- (list (car lst) (list (js2-treeify (cdr lst))))))
-
-(defun js2-build-alist-trie (chains trie)
- "Merge declaration name chains into a trie-like alist structure for imenu.
-CHAINS is the qname chain list produced during parsing. TRIE is a
-list of elements built up so far."
- (let (head tail pos branch kids)
- (dolist (chain chains)
- (setq head (car chain)
- tail (cdr chain)
- pos (if (numberp (car tail)) (car tail))
- branch (js2-find-if (lambda (n)
- (string= (car n) head))
- trie)
- kids (cl-second branch))
- (cond
- ;; case 1: this key isn't in the trie yet
- ((null branch)
- (if trie
- (setcdr (last trie) (list (js2-treeify chain)))
- (setq trie (list (js2-treeify chain)))))
- ;; case 2: key is present with a single number entry: replace w/ list
- ;; ("a1" 10) + ("a1" 20) => ("a1" (("<definition>" 10)
- ;; ("<definition>" 20)))
- ((numberp kids)
- (setcar (cdr branch)
- (list (list "<definition-1>" kids)
- (if pos
- (list "<definition-2>" pos)
- (js2-treeify tail)))))
- ;; case 3: key is there (with kids), and we're a number entry
- (pos
- (setcdr (last kids)
- (list
- (list (format "<definition-%d>"
- (1+ (cl-loop for kid in kids
- count (eq ?< (aref (car kid) 0)))))
- pos))))
- ;; case 4: key is there with kids, need to merge in our chain
- (t
- (js2-build-alist-trie (list tail) kids))))
- trie))
-
-(defun js2-flatten-trie (trie)
- "Convert TRIE to imenu-format.
-Recurses through nodes, and for each one whose second element is a list,
-appends the list's flattened elements to the current element. Also
-changes the tails into conses. For instance, this pre-flattened trie
-
-'(a ((b 20)
- (c ((d 30)
- (e 40)))))
-
-becomes
-
-'(a (b . 20)
- (c (d . 30)
- (e . 40)))
-
-Note that the root of the trie has no key, just a list of chains.
-This is also true for the value of any key with multiple children,
-e.g. key 'c' in the example above."
- (cond
- ((listp (car trie))
- (mapcar #'js2-flatten-trie trie))
- (t
- (if (numberp (cl-second trie))
- (cons (car trie) (cl-second trie))
- ;; else pop list and append its kids
- (apply #'append (list (car trie)) (js2-flatten-trie (cdr trie)))))))
-
-(defun js2-build-imenu-index ()
- "Turn `js2-imenu-recorder' into an imenu data structure."
- (when (eq js2-imenu-recorder 'empty)
- (setq js2-imenu-recorder nil))
- (let* ((chains (js2-browse-postprocess-chains))
- (result (js2-build-alist-trie chains nil)))
- (js2-flatten-trie result)))
-
-(defun js2-test-print-chains (chains)
- "Print a list of qname chains.
-Each element of CHAINS is a list of the form (NODE [NODE *] pos);
-i.e. one or more nodes, and an integer position as the list tail."
- (mapconcat (lambda (chain)
- (concat "("
- (mapconcat (lambda (elem)
- (if (js2-node-p elem)
- (or (js2-node-qname-component elem)
- "nil")
- (number-to-string elem)))
- chain
- " ")
- ")"))
- chains
- "\n"))
-
-;;; Parser
-
-(defconst js2-version "1.8.5"
- "Version of JavaScript supported.")
-
-(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.
-Use with caution! Assumes `js2-node-pos' is -absolute-, which
-is only true until the node is added to its parent; i.e., while parsing."
- (+ (js2-node-pos n)
- (js2-node-len n)))
-
-(defun js2-record-comment (token)
- "Record a comment in `js2-scanned-comments'."
- (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)))))
-
-(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 nil)))
- (js2-unget-token)
- tt)))
-
-(defalias 'js2-next-token 'js2-get-token)
-
-(defun js2-match-token (match &optional dont-unget)
- "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-get-token) match)
- (ignore (unless dont-unget (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 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-get-prop-name-token ()
- (js2-get-token (and (>= js2-language-version 170) 'KEYWORD_IS_NAME)))
-
-(defun js2-match-prop-name ()
- "Consume token and return t if next token is a valid property name.
-If `js2-language-version' is >= 180, a keyword or reserved word
-is considered valid name as well."
- (if (eq js2-NAME (js2-get-prop-name-token))
- t
- (js2-unget-token)
- nil))
-
-(defun js2-must-match-prop-name (msg-id &optional pos len)
- (if (js2-match-prop-name)
- t
- (js2-report-error msg-id nil pos len)
- nil))
-
-(defun js2-peek-token-or-eol ()
- "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-get-token))
- (follows-eol (js2-token-follows-eol-p (js2-current-token))))
- (js2-unget-token)
- (if follows-eol
- js2-EOL
- tt)))
-
-(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.
-Returns t on match, nil if no match."
- (if (js2-match-token token t)
- t
- (js2-report-error msg-id nil pos len)
- (js2-unget-token)
- nil))
-
-(defun js2-must-match-name (msg-id)
- (if (js2-match-token js2-NAME t)
- t
- (if (eq (js2-current-token-type) js2-RESERVED)
- (js2-report-error "msg.reserved.id" (js2-current-token-string))
- (js2-report-error msg-id)
- (js2-unget-token))
- nil))
-
-(defsubst js2-inside-function ()
- (cl-plusp js2-nesting-of-function))
-
-(defun js2-set-requires-activation ()
- (if (js2-function-node-p js2-current-script-or-fn)
- (setf (js2-function-node-needs-activation js2-current-script-or-fn) t)))
-
-(defun js2-check-activation-name (name _token)
- (when (js2-inside-function)
- ;; skip language-version 1.2 check from Rhino
- (if (or (string= "arguments" name)
- (and js2-compiler-activation-names ; only used in codegen
- (gethash name js2-compiler-activation-names)))
- (js2-set-requires-activation))))
-
-(defun js2-set-is-generator ()
- (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
- (js2-report-error "msg.XML.not.available")))
-
-(defun js2-push-scope (scope)
- "Push SCOPE, a `js2-scope', onto the lexical scope chain."
- (cl-assert (js2-scope-p scope))
- (cl-assert (null (js2-scope-parent-scope scope)))
- (cl-assert (not (eq js2-current-scope scope)))
- (setf (js2-scope-parent-scope scope) js2-current-scope
- js2-current-scope scope))
-
-(defsubst js2-pop-scope ()
- (setq js2-current-scope
- (js2-scope-parent-scope js2-current-scope)))
-
-(defun js2-enter-loop (loop-node)
- (push loop-node js2-loop-set)
- (push loop-node js2-loop-and-switch-set)
- (js2-push-scope loop-node)
- ;; Tell the current labeled statement (if any) its statement,
- ;; and set the jump target of the first label to the loop.
- ;; These are used in `js2-parse-continue' to verify that the
- ;; continue target is an actual labeled loop. (And for codegen.)
- (when js2-labeled-stmt
- (setf (js2-labeled-stmt-node-stmt js2-labeled-stmt) loop-node
- (js2-label-node-loop (car (js2-labeled-stmt-node-labels
- js2-labeled-stmt))) loop-node)))
-
-(defun js2-exit-loop ()
- (pop js2-loop-set)
- (pop js2-loop-and-switch-set)
- (js2-pop-scope))
-
-(defsubst js2-enter-switch (switch-node)
- (push switch-node js2-loop-and-switch-set))
-
-(defsubst js2-exit-switch ()
- (pop js2-loop-and-switch-set))
-
-(defun js2-parse (&optional buf cb)
- "Tell the js2 parser to parse a region of JavaScript.
-
-BUF is a buffer or buffer name containing the code to parse.
-Call `narrow-to-region' first to parse only part of the buffer.
-
-The returned AST root node is given some additional properties:
- `node-count' - total number of nodes in the AST
- `buffer' - BUF. The buffer it refers to may change or be killed,
- so the value is not necessarily reliable.
-
-An optional callback CB can be specified to report parsing
-progress. If `(functionp CB)' returns t, it will be called with
-the current line number once before parsing begins, then again
-each time the lexer reaches a new line number.
-
-CB can also be a list of the form `(symbol cb ...)' to specify
-multiple callbacks with different criteria. Each symbol is a
-criterion keyword, and the following element is the callback to
-call
-
- :line - called whenever the line number changes
- :token - called for each new token consumed
-
-The list of criteria could be extended to include entering or
-leaving a statement, an expression, or a function definition."
- (if (and cb (not (functionp cb)))
- (error "criteria callbacks not yet implemented"))
- (let ((inhibit-point-motion-hooks t)
- (js2-compiler-xml-available (>= js2-language-version 160))
- ;; This is a recursive-descent parser, so give it a big stack.
- (max-lisp-eval-depth (max max-lisp-eval-depth 3000))
- (max-specpdl-size (max max-specpdl-size 3000))
- (case-fold-search nil)
- ast)
- (with-current-buffer (or buf (current-buffer))
- (setq js2-scanned-comments nil
- js2-parsed-errors nil
- js2-parsed-warnings nil
- js2-imenu-recorder nil
- js2-imenu-function-map nil
- js2-label-set nil)
- (js2-init-scanner)
- (setq ast (js2-do-parse))
- (unless js2-ts-hit-eof
- (js2-report-error "msg.got.syntax.errors" (length js2-parsed-errors)))
- (setf (js2-ast-root-errors ast) js2-parsed-errors
- (js2-ast-root-warnings ast) js2-parsed-warnings)
- ;; if we didn't find any declarations, put a dummy in this list so we
- ;; don't end up re-parsing the buffer in `js2-mode-create-imenu-index'
- (unless js2-imenu-recorder
- (setq js2-imenu-recorder 'empty))
- (run-hooks 'js2-parse-finished-hook)
- ast)))
-
-;; Corresponds to Rhino's Parser.parse() method.
-(defun js2-do-parse ()
- "Parse current buffer starting from current point.
-Scanner should be initialized."
- (let ((pos js2-ts-cursor)
- (end js2-ts-cursor) ; in case file is empty
- root n tt)
- ;; initialize buffer-local parsing vars
- (setf root (make-js2-ast-root :buffer (buffer-name) :pos pos)
- js2-current-script-or-fn root
- js2-current-scope root
- js2-nesting-of-function 0
- js2-labeled-stmt nil
- js2-recorded-identifiers nil) ; for js2-highlight
- (while (/= (setq tt (js2-get-token)) js2-EOF)
- (if (= tt js2-FUNCTION)
- (progn
- (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))
- (js2-block-node-push root n))
- ;; add comments to root in lexical order
- (when js2-scanned-comments
- ;; if we find a comment beyond end of normal kids, use its end
- (setq end (max end (js2-node-end (cl-first js2-scanned-comments))))
- (dolist (comment js2-scanned-comments)
- (push comment (js2-ast-root-comments root))
- (js2-node-add-children root comment)))
- (setf (js2-node-len root) (- end pos))
- (setq js2-mode-ast root) ; Make sure this is available for callbacks.
- ;; Give extensions a chance to muck with things before highlighting starts.
- (let ((js2-additional-externs js2-additional-externs))
- (save-excursion
- (run-hooks 'js2-post-parse-callbacks))
- (js2-highlight-undeclared-vars))
- root))
-
-(defun js2-parse-function-closure-body (fn-node)
- "Parse a JavaScript 1.8 function closure body."
- (let ((js2-nesting-of-function (1+ js2-nesting-of-function)))
- (if js2-ts-hit-eof
- (js2-report-error "msg.no.brace.body" nil
- (js2-node-pos fn-node)
- (- js2-ts-cursor (js2-node-pos fn-node)))
- (js2-node-add-children fn-node
- (setf (js2-function-node-body fn-node)
- (js2-parse-expr t))))))
-
-(defun js2-parse-function-body (fn-node)
- (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-current-token-beg)) ; LC position
- (pn (make-js2-block-node)) ; starts at LC position
- tt
- end)
- (cl-incf js2-nesting-of-function)
- (unwind-protect
- (while (not (or (= (setq tt (js2-peek-token)) js2-ERROR)
- (= tt js2-EOF)
- (= tt js2-RC)))
- (js2-block-node-push pn (if (/= tt js2-FUNCTION)
- (js2-parse-statement)
- (js2-get-token)
- (js2-parse-function-stmt))))
- (cl-decf js2-nesting-of-function))
- (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-current-token-end)))
- (setf (js2-node-pos pn) pos
- (js2-node-len pn) (- end pos))
- (setf (js2-function-node-body fn-node) pn)
- (js2-node-add-children fn-node pn)
- pn))
-
-(defun js2-define-destruct-symbols (node decl-type face &optional
ignore-not-in-block)
- "Declare and fontify destructuring parameters inside NODE.
-NODE is either `js2-array-node', `js2-object-node', or `js2-name-node'."
- (cond
- ((js2-name-node-p node)
- (let (leftpos)
- (js2-define-symbol decl-type (js2-name-node-name node)
- node ignore-not-in-block)
- (when face
- (js2-set-face (setq leftpos (js2-node-abs-pos node))
- (+ leftpos (js2-node-len node))
- face 'record))))
- ((js2-object-node-p node)
- (dolist (elem (js2-object-node-elems node))
- (js2-define-destruct-symbols
- ;; In abbreviated destructuring {a, b}, right == left.
- (js2-object-prop-node-right elem)
- decl-type face ignore-not-in-block)))
- ((js2-array-node-p node)
- (dolist (elem (js2-array-node-elems node))
- (when elem
- (js2-define-destruct-symbols elem decl-type face
ignore-not-in-block))))
- (t (js2-report-error "msg.no.parm" nil (js2-node-abs-pos node)
- (js2-node-len node)))))
-
-(defun js2-parse-function-params (function-type fn-node pos)
- (if (js2-match-token js2-RP)
- (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))
- (cl-loop for tt = (js2-peek-token)
- do
- (cond
- ;; destructuring param
- ((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))
- (js2-define-destruct-symbols param
- js2-LP
- 'js2-function-param)
- (push param params))
- ;; 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
- (setq rest-param-at (length params)))
- (js2-must-match-name "msg.no.parm")
- (js2-record-face 'js2-function-param)
- (setq param (js2-create-name-node))
- (js2-define-symbol js2-LP (js2-current-token-string) param)
- ;; default parameter value
- (when (or (and default-found
- (not rest-param-at)
- (js2-must-match js2-ASSIGN
-
"msg.no.default.after.default.param"
- (js2-node-pos param)
- (js2-node-len param)))
- (and (>= js2-language-version 200)
- (js2-match-token js2-ASSIGN)))
- (cl-assert (not paren-free-arrow))
- (let* ((pos (js2-node-pos param))
- (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)))
- (setq param (make-js2-assign-node
- :type tt :pos pos :len len :op-pos op-pos
- :left left :right right)
- default-found t)
- (js2-node-add-children param left right)))
- (push param params)))
- (when (and rest-param-at (> (length params) (1+ rest-param-at)))
- (js2-report-error "msg.param.after.rest" nil
- (js2-node-pos param) (js2-node-len param)))
- while
- (js2-match-token js2-COMMA))
- (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-node-add-children fn-node p)
- (push p (js2-function-node-params fn-node))))))
-
-(defun js2-check-inconsistent-return-warning (fn-node name)
- "Possibly show inconsistent-return warning.
-Last token scanned is the close-curly for the function body."
- (when (and js2-mode-show-strict-warnings
- js2-strict-inconsistent-return-warning
- (not (js2-has-consistent-return-usage
- (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-current-token-end))
- (max (js2-node-abs-pos (js2-function-node-body fn-node))
- (point-at-bol))))
- (end (js2-current-token-end)))
- (if (cl-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-stmt ()
- (let ((pos (js2-current-token-beg))
- (star-p (js2-match-token js2-MUL)))
- (js2-must-match-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))
- :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 (and (eq function-type 'FUNCTION_STATEMENT)
- (cl-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) (cl-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.
- ;; 2. Functions defined under the with statement also immune to
- ;; this setup, in which case dynamic scope is ignored in favor
- ;; of the with object.
- (setf (js2-function-node-ignore-dynamic fn-node) t))
- ;; dynamically bind all the per-function variables
- (let ((js2-current-script-or-fn fn-node)
- (js2-current-scope fn-node)
- (js2-nesting-of-with 0)
- (js2-end-flags 0)
- js2-label-set
- js2-loop-set
- js2-loop-and-switch-set)
- (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))
- (js2-check-inconsistent-return-warning fn-node 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))
- (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
- ;; at the function boundary when checking for redeclarations.
- (setf (js2-scope-parent-scope fn-node) js2-current-scope)
- fn-node))
-
-(defun js2-parse-statements (&optional parent)
- "Parse a statement list. Last token consumed must be js2-LC.
-
-PARENT can be a `js2-block-node', in which case the statements are
-appended to PARENT. Otherwise a new `js2-block-node' is created
-and returned.
-
-This function does not match the closing js2-RC: the caller
-matches the RC so it can provide a suitable error message if not
-matched. This means it's up to the caller to set the length of
-the node to include the closing RC. The node start pos is set to
-the absolute buffer start position, and the caller should fix it
-up to be relative to the parent node. All children of this block
-node are given relative start positions and correct lengths."
- (let ((pn (or parent (make-js2-block-node)))
- tt)
- (while (and (> (setq tt (js2-peek-token)) js2-EOF)
- (/= tt js2-RC))
- (js2-block-node-push pn (js2-parse-statement)))
- pn))
-
-(defun js2-parse-statement ()
- (let (pn beg end)
- ;; coarse-grained user-interrupt check - needs work
- (and js2-parse-interruptable-p
- (zerop (% (cl-incf js2-parse-stmt-count)
- js2-statements-per-pause))
- (input-pending-p)
- (throw 'interrupted t))
- (setq pn (js2-statement-helper))
- ;; no-side-effects warning check
- (unless (js2-node-has-side-effects pn)
- (setq end (js2-node-end pn))
- (save-excursion
- (goto-char end)
- (setq beg (max (js2-node-pos pn) (point-at-bol))))
- (js2-add-strict-warning "msg.no.side.effects" nil beg end))
- pn))
-
-;; These correspond to the switch cases in Parser.statementHelper
-(defconst js2-parsers
- (let ((parsers (make-vector js2-num-tokens
- #'js2-parse-expr-stmt)))
- (aset parsers js2-BREAK #'js2-parse-break)
- (aset parsers js2-CLASS #'js2-parse-class-stmt)
- (aset parsers js2-CONST #'js2-parse-const-var)
- (aset parsers js2-CONTINUE #'js2-parse-continue)
- (aset parsers js2-DEBUGGER #'js2-parse-debugger)
- (aset parsers js2-DEFAULT #'js2-parse-default-xml-namespace)
- (aset parsers js2-DO #'js2-parse-do)
- (aset parsers js2-EXPORT #'js2-parse-export)
- (aset parsers js2-FOR #'js2-parse-for)
- (aset parsers js2-FUNCTION #'js2-parse-function-stmt)
- (aset parsers js2-IF #'js2-parse-if)
- (aset parsers js2-IMPORT #'js2-parse-import)
- (aset parsers js2-LC #'js2-parse-block)
- (aset parsers js2-LET #'js2-parse-let-stmt)
- (aset parsers js2-NAME #'js2-parse-name-or-label)
- (aset parsers js2-RETURN #'js2-parse-ret-yield)
- (aset parsers js2-SEMI #'js2-parse-semi)
- (aset parsers js2-SWITCH #'js2-parse-switch)
- (aset parsers js2-THROW #'js2-parse-throw)
- (aset parsers js2-TRY #'js2-parse-try)
- (aset parsers js2-VAR #'js2-parse-const-var)
- (aset parsers js2-WHILE #'js2-parse-while)
- (aset parsers js2-WITH #'js2-parse-with)
- (aset parsers js2-YIELD #'js2-parse-ret-yield)
- parsers)
- "A vector mapping token types to parser functions.")
-
-(defun js2-parse-warn-missing-semi (beg end)
- (and js2-mode-show-strict-warnings
- js2-strict-missing-semi-warning
- (js2-add-strict-warning
- "msg.missing.semi" nil
- ;; back up to beginning of statement or line
- (max beg (save-excursion
- (goto-char end)
- (point-at-bol)))
- end)))
-
-(defconst js2-no-semi-insertion
- (list js2-IF
- js2-SWITCH
- js2-WHILE
- js2-DO
- js2-FOR
- js2-TRY
- js2-WITH
- js2-LC
- js2-ERROR
- js2-SEMI
- js2-CLASS
- js2-FUNCTION)
- "List of tokens that don't do automatic semicolon insertion.")
-
-(defconst js2-autoinsert-semi-and-warn
- (list js2-ERROR js2-EOF js2-RC))
-
-(defun js2-statement-helper ()
- (let* ((tt (js2-get-token))
- (first-tt tt)
- (parser (if (= tt js2-ERROR)
- #'js2-parse-semi
- (aref js2-parsers tt)))
- pn)
- ;; If the statement is set, then it's been told its label by now.
- (and js2-labeled-stmt
- (js2-labeled-stmt-node-stmt js2-labeled-stmt)
- (setq js2-labeled-stmt nil))
- (setq pn (funcall parser))
- ;; Don't do auto semi insertion for certain statement types.
- (unless (or (memq first-tt js2-no-semi-insertion)
- (js2-labeled-stmt-node-p pn))
- (js2-auto-insert-semicolon pn))
- pn))
-
-(defun js2-auto-insert-semicolon (pn)
- (let* ((tt (js2-get-token))
- (pos (js2-node-pos pn)))
- (cond
- ((= tt js2-SEMI)
- ;; extend the node bounds to include the semicolon.
- (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 (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-unget-token) ; Not ';', do not consume.
- ))))
-
-(defun js2-parse-condition ()
- "Parse a parenthesized boolean expression, e.g. in an if- or while-stmt.
-The parens are discarded and the expression node is returned.
-The `pos' field of the return value is set to an absolute position
-that must be fixed up by the caller.
-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-current-token-beg)))
- (setq pn (js2-parse-expr))
- (if (js2-must-match js2-RP "msg.no.paren.after.cond")
- (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))
- (js2-add-strict-warning "msg.equal.as.assign" nil
- (js2-node-pos pn)
- (+ (js2-node-pos pn)
- (js2-node-len pn))))
- (list pn lp rp)))
-
-(defun js2-parse-if ()
- "Parser for if-statement. Last matched token must be js2-IF."
- (let ((pos (js2-current-token-beg))
- cond if-true if-false else-pos end pn)
- (setq cond (js2-parse-condition)
- if-true (js2-parse-statement)
- if-false (if (js2-match-token js2-ELSE)
- (progn
- (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
- :len (- end pos)
- :condition (car cond)
- :then-part if-true
- :else-part if-false
- :else-pos else-pos
- :lp (js2-relpos (cl-second cond) pos)
- :rp (js2-relpos (cl-third cond) pos)))
- (js2-node-add-children pn (car cond) if-true if-false)
- pn))
-
-(defun js2-parse-import ()
- "Parse import statement. The current token must be js2-IMPORT."
- (unless (js2-ast-root-p js2-current-scope)
- (js2-report-error "msg.mod.import.decl.at.top.level"))
- (let ((beg (js2-current-token-beg)))
- (cond ((js2-match-token js2-STRING)
- (make-js2-import-node
- :pos beg
- :len (- (js2-current-token-end) beg)
- :module-id (js2-current-token-string)))
- (t
- (let* ((import-clause (js2-parse-import-clause))
- (from-clause (and import-clause (js2-parse-from-clause)))
- (module-id (when from-clause (js2-from-clause-node-module-id
from-clause)))
- (node (make-js2-import-node
- :pos beg
- :len (- (js2-current-token-end) beg)
- :import import-clause
- :from from-clause
- :module-id module-id)))
- (when import-clause
- (js2-node-add-children node import-clause))
- (when from-clause
- (js2-node-add-children node from-clause))
- node)))))
-
-(defun js2-parse-import-clause ()
- "Parse the bindings in an import statement.
-This can take many forms:
-
-ImportedDefaultBinding -> 'foo'
-NameSpaceImport -> '* as lib'
-NamedImports -> '{foo as bar, bang}'
-ImportedDefaultBinding , NameSpaceImport -> 'foo, * as lib'
-ImportedDefaultBinding , NamedImports -> 'foo, {bar, baz as bif}'
-
-Try to match namespace imports and named imports first because nothing can
-come after them. If it is an imported default binding, then it could have named
-imports or a namespace import that follows it.
-"
- (let* ((beg (js2-current-token-beg))
- (clause (make-js2-import-clause-node
- :pos beg))
- (children (list)))
- (cond
- ((js2-match-token js2-MUL)
- (let ((ns-import (js2-parse-namespace-import)))
- (when ns-import
- (let ((name-node (js2-namespace-import-node-name ns-import)))
- (js2-define-symbol
- js2-LET (js2-name-node-name name-node) name-node t)))
- (setf (js2-import-clause-node-namespace-import clause) ns-import)
- (push ns-import children)))
- ((js2-match-token js2-LC)
- (let ((imports (js2-parse-export-bindings t)))
- (setf (js2-import-clause-node-named-imports clause) imports)
- (dolist (import imports)
- (push import children)
- (let ((name-node (js2-export-binding-node-local-name import)))
- (when name-node
- (js2-define-symbol
- js2-LET (js2-name-node-name name-node) name-node t))))))
- ((= (js2-peek-token) js2-NAME)
- (let ((binding (js2-maybe-parse-export-binding)))
- (let ((node-name (js2-export-binding-node-local-name binding)))
- (js2-define-symbol js2-LET (js2-name-node-name node-name) node-name
t))
- (setf (js2-import-clause-node-default-binding clause) binding)
- (push binding children))
- (when (js2-match-token js2-COMMA)
- (cond
- ((js2-match-token js2-MUL)
- (let ((ns-import (js2-parse-namespace-import)))
- (let ((name-node (js2-namespace-import-node-name ns-import)))
- (js2-define-symbol
- js2-LET (js2-name-node-name name-node) name-node t))
- (setf (js2-import-clause-node-namespace-import clause) ns-import)
- (push ns-import children)))
- ((js2-match-token js2-LC)
- (let ((imports (js2-parse-export-bindings t)))
- (setf (js2-import-clause-node-named-imports clause) imports)
- (dolist (import imports)
- (push import children)
- (let ((name-node (js2-export-binding-node-local-name import)))
- (when name-node
- (js2-define-symbol
- js2-LET (js2-name-node-name name-node) name-node t))))))
- (t (js2-report-error "msg.syntax")))))
- (t (js2-report-error "msg.mod.declaration.after.import")))
- (setf (js2-node-len clause) (- (js2-current-token-end) beg))
- (apply #'js2-node-add-children clause children)
- clause))
-
-(defun js2-parse-namespace-import ()
- "Parse a namespace import expression such as '* as bar'.
-The current token must be js2-MUL."
- (let ((beg (js2-current-token-beg)))
- (when (js2-must-match js2-NAME "msg.syntax")
- (if (equal "as" (js2-current-token-string))
- (when (js2-must-match-prop-name "msg.syntax")
- (let ((node (make-js2-namespace-import-node
- :pos beg
- :len (- (js2-current-token-end) beg)
- :name (make-js2-name-node
- :pos (js2-current-token-beg)
- :len (js2-current-token-end)
- :name (js2-current-token-string)))))
- (js2-node-add-children node (js2-namespace-import-node-name
node))
- node))
- (js2-unget-token)
- (js2-report-error "msg.syntax")))))
-
-
-(defun js2-parse-from-clause ()
- "Parse the from clause in an import or export statement. E.g. from 'src/lib'"
- (when (js2-must-match-name "msg.mod.from.after.import.spec.set")
- (let ((beg (js2-current-token-beg)))
- (if (equal "from" (js2-current-token-string))
- (cond
- ((js2-match-token js2-STRING)
- (make-js2-from-clause-node
- :pos beg
- :len (- (js2-current-token-end) beg)
- :module-id (js2-current-token-string)
- :metadata-p nil))
- ((js2-match-token js2-THIS)
- (when (js2-must-match-name "msg.mod.spec.after.from")
- (if (equal "module" (js2-current-token-string))
- (make-js2-from-clause-node
- :pos beg
- :len (- (js2-current-token-end) beg)
- :module-id "this"
- :metadata-p t)
- (js2-unget-token)
- (js2-unget-token)
- (js2-report-error "msg.mod.spec.after.from")
- nil)))
- (t (js2-report-error "msg.mod.spec.after.from") nil))
- (js2-unget-token)
- (js2-report-error "msg.mod.from.after.import.spec.set")
- nil))))
-
-(defun js2-parse-export-bindings (&optional import-p)
- "Parse a list of export binding expressions such as {}, {foo, bar}, and
-{foo as bar, baz as bang}. The current token must be
-js2-LC. Return a lisp list of js2-export-binding-node"
- (let ((bindings (list)))
- (while
- (let ((binding (js2-maybe-parse-export-binding)))
- (when binding
- (push binding bindings))
- (js2-match-token js2-COMMA)))
- (when (js2-must-match js2-RC (if import-p
- "msg.mod.rc.after.import.spec.list"
- "msg.mod.rc.after.export.spec.list"))
- (reverse bindings))))
-
-(defun js2-maybe-parse-export-binding ()
- "Attempt to parse a binding expression found inside an import/export
statement.
-This can take the form of either as single js2-NAME token as in 'foo' or as in
a
-rebinding expression 'bar as foo'. If it matches, it will return an instance of
-js2-export-binding-node and consume all the tokens. If it does not match, it
-consumes no tokens."
- (let ((extern-name (when (js2-match-prop-name) (js2-current-token-string)))
- (beg (js2-current-token-beg))
- (extern-name-len (js2-current-token-len))
- (is-reserved-name (or (= (js2-current-token-type) js2-RESERVED)
- (aref js2-kwd-tokens (js2-current-token-type)))))
- (if extern-name
- (let ((as (and (js2-match-token js2-NAME) (js2-current-token-string))))
- (if (and as (equal "as" (js2-current-token-string)))
- (let ((name
- (or
- (and (js2-match-token js2-DEFAULT) "default")
- (and (js2-match-token js2-NAME)
(js2-current-token-string)))))
- (if name
- (let ((node (make-js2-export-binding-node
- :pos beg
- :len (- (js2-current-token-end) beg)
- :local-name (make-js2-name-node
- :name name
- :pos (js2-current-token-beg)
- :len (js2-current-token-len))
- :extern-name (make-js2-name-node
- :name extern-name
- :pos beg
- :len extern-name-len))))
- (js2-node-add-children
- node
- (js2-export-binding-node-local-name node)
- (js2-export-binding-node-extern-name node))
- node)
- (js2-unget-token)
- nil))
- (when as (js2-unget-token))
- (let* ((name-node (make-js2-name-node
- :name (js2-current-token-string)
- :pos (js2-current-token-beg)
- :len (js2-current-token-len)))
- (node (make-js2-export-binding-node
- :pos (js2-current-token-beg)
- :len (js2-current-token-len)
- :local-name name-node
- :extern-name name-node)))
- (when is-reserved-name
- (js2-report-error "msg.mod.as.after.reserved.word"
extern-name))
- (js2-node-add-children node name-node)
- node)))
- nil)))
-
-(defun js2-parse-switch ()
- "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)
- (if (js2-must-match js2-LP "msg.no.paren.switch")
- (setq lp (js2-current-token-beg)))
- (setq discriminant (js2-parse-expr)
- pn (make-js2-switch-node :discriminant discriminant
- :pos pos
- :lp (js2-relpos lp pos)))
- (js2-node-add-children pn discriminant)
- (js2-enter-switch pn)
- (unwind-protect
- (progn
- (if (js2-must-match js2-RP "msg.no.paren.after.switch")
- (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-current-token-beg))
- (cond
- ((= tt js2-RC)
- (setf (js2-node-len pn) (- (js2-current-token-end) pos))
- (throw 'break nil)) ; done
- ((= tt js2-CASE)
- (setq case-expr (js2-parse-expr))
- (js2-must-match js2-COLON "msg.no.colon.case"))
- ((= tt js2-DEFAULT)
- (if has-default
- (js2-report-error "msg.double.switch.default"))
- (setq has-default t
- case-expr nil)
- (js2-must-match js2-COLON "msg.no.colon.case"))
- (t
- (js2-report-error "msg.bad.switch")
- (throw 'break nil)))
- (setq case-node (make-js2-case-node :pos 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)
- (/= tt js2-CASE)
- (/= tt js2-DEFAULT)
- (/= tt js2-EOF))
- (setf stmt (js2-parse-statement)
- (js2-node-len case-node) (- (js2-node-end stmt)
case-pos))
- (js2-block-node-push case-node stmt))
- (push case-node cases)))
- ;; add cases last, as pushing reverses the order to be correct
- (dolist (kid cases)
- (js2-node-add-children pn kid)
- (push kid (js2-switch-node-cases pn)))
- pn) ; return value
- (js2-exit-switch))))
-
-(defun js2-parse-while ()
- "Parser for while-statement. Last matched token must be js2-WHILE."
- (let ((pos (js2-current-token-beg))
- (pn (make-js2-while-node))
- cond body)
- (js2-enter-loop pn)
- (unwind-protect
- (progn
- (setf cond (js2-parse-condition)
- (js2-while-node-condition pn) (car cond)
- body (js2-parse-statement)
- (js2-while-node-body pn) body
- (js2-node-len pn) (- (js2-node-end body) pos)
- (js2-while-node-lp pn) (js2-relpos (cl-second cond) pos)
- (js2-while-node-rp pn) (js2-relpos (cl-third cond) pos))
- (js2-node-add-children pn body (car cond)))
- (js2-exit-loop))
- pn))
-
-(defun js2-parse-do ()
- "Parser for do-statement. Last matched token must be js2-DO."
- (let ((pos (js2-current-token-beg))
- (pn (make-js2-do-node))
- cond body end)
- (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-current-token-beg) pos)
- cond (js2-parse-condition)
- (js2-do-node-condition pn) (car cond)
- (js2-do-node-body pn) body
- end js2-ts-cursor
- (js2-do-node-lp pn) (js2-relpos (cl-second cond) pos)
- (js2-do-node-rp pn) (js2-relpos (cl-third cond) pos))
- (js2-node-add-children pn (car cond) body))
- (js2-exit-loop))
- ;; Always auto-insert semicolon to follow SpiderMonkey:
- ;; It is required by ECMAScript but is ignored by the rest of
- ;; world; see bug 238945
- (if (js2-match-token js2-SEMI)
- (setq end js2-ts-cursor))
- (setf (js2-node-len pn) (- end pos))
- pn))
-
-(defun js2-parse-export ()
- "Parse an export statement.
-The Last matched token must be js2-EXPORT. Currently, the 'default' and 'expr'
-expressions should only be either hoistable expressions (function or generator)
-or assignment expressions, but there is no checking to enforce that and so it
-will parse without error a small subset of
-invalid export statements."
- (unless (js2-ast-root-p js2-current-scope)
- (js2-report-error "msg.mod.export.decl.at.top.level"))
- (let ((beg (js2-current-token-beg))
- (children (list))
- exports-list from-clause declaration default)
- (cond
- ((js2-match-token js2-MUL)
- (setq from-clause (js2-parse-from-clause))
- (when from-clause
- (push from-clause children)))
- ((js2-match-token js2-LC)
- (setq exports-list (js2-parse-export-bindings))
- (when exports-list
- (dolist (export exports-list)
- (push export children)))
- (when (js2-match-token js2-NAME)
- (if (equal "from" (js2-current-token-string))
- (progn
- (js2-unget-token)
- (setq from-clause (js2-parse-from-clause)))
- (js2-unget-token))))
- ((js2-match-token js2-DEFAULT)
- (setq default (js2-parse-expr)))
- ((or (js2-match-token js2-VAR) (js2-match-token js2-CONST)
(js2-match-token js2-LET))
- (setq declaration (js2-parse-variables (js2-current-token-type)
(js2-current-token-beg))))
- (t
- (setq declaration (js2-parse-expr))))
- (when from-clause
- (push from-clause children))
- (when declaration
- (push declaration children))
- (when default
- (push default children))
- (let ((node (make-js2-export-node
- :pos beg
- :len (- (js2-current-token-end) beg)
- :exports-list exports-list
- :from-clause from-clause
- :declaration declaration
- :default default)))
- (apply #'js2-node-add-children node children)
- node)))
-
-(defun js2-parse-for ()
- "Parse a for, for-in or for each-in statement.
-Last matched token must be js2-FOR."
- (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)
- ;; See if this is a for each () instead of just a for ()
- (when (js2-match-token js2-NAME)
- (if (string= "each" (js2-current-token-string))
- (progn
- (setq is-for-each t
- 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-current-token-beg) for-pos)))
- (setq tt (js2-get-token))
- ;; 'for' makes local scope
- (js2-push-scope (make-js2-scope))
- (unwind-protect
- ;; parse init clause
- (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))
- (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-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)))
- (js2-parse-expr))) ; object over which we're iterating
- ;; else ordinary for loop - parse cond and incr
- (js2-must-match js2-SEMI "msg.no.semi.for")
- (setq cond (if (= (js2-peek-token) js2-SEMI)
- (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-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-current-token-beg) for-pos)))
- (if (not is-for-in-or-of)
- (setq pn (make-js2-for-node :init init
- :condition cond
- :update incr
- :lp lp
- :rp rp))
- ;; cond could be null if 'in obj' got eaten by the init node.
- (if (js2-infix-node-p init)
- ;; it was (foo in bar) instead of (var foo in bar)
- (setq cond (js2-infix-node-right init)
- init (js2-infix-node-left init))
- (if (and (js2-var-decl-node-p init)
- (> (length (js2-var-decl-node-kids init)) 1))
- (js2-report-error "msg.mult.index")))
- (setq pn (make-js2-for-in-node :iterator init
- :object cond
- :in-pos in-pos
- :foreach-p is-for-each
- :each-pos each-pos
- :forof-p is-for-of
- :lp lp
- :rp rp)))
- (unwind-protect
- (progn
- (js2-enter-loop pn)
- ;; We have to parse the body -after- creating the loop node,
- ;; so that the loop node appears in the js2-loop-set, allowing
- ;; break/continue statements to find the enclosing loop.
- (setf body (js2-parse-statement)
- (js2-loop-node-body pn) body
- (js2-node-pos pn) for-pos
- (js2-node-len pn) (- (js2-node-end body) for-pos))
- (js2-node-add-children pn init cond incr body))
- ;; finally
- (js2-exit-loop))
- (js2-pop-scope))
- pn))
-
-(defun js2-parse-try ()
- "Parse a try statement. Last matched token must be js2-TRY."
- (let ((try-pos (js2-current-token-beg))
- try-end
- try-block
- catch-blocks
- finally-block
- saw-default-catch
- peek)
- (if (/= (js2-peek-token) js2-LC)
- (js2-report-error "msg.no.brace.try"))
- (setq try-block (js2-parse-statement)
- try-end (js2-node-end try-block)
- peek (js2-peek-token))
- (cond
- ((= peek js2-CATCH)
- (while (js2-match-token js2-CATCH)
- (let* ((catch-pos (js2-current-token-beg))
- (catch-node (make-js2-catch-node :pos catch-pos))
- param
- guard-kwd
- catch-cond
- lp rp)
- (if saw-default-catch
- (js2-report-error "msg.catch.unreachable"))
- (if (js2-must-match js2-LP "msg.no.paren.catch")
- (setq lp (- (js2-current-token-beg) catch-pos)))
- (js2-push-scope catch-node)
- (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-name "msg.bad.catchcond")
- (setq param (js2-create-name-node))
- (js2-define-symbol js2-LET (js2-current-token-string) param))))
- ;; Catch condition.
- (if (js2-match-token js2-IF)
- (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-current-token-beg) catch-pos)))
- (js2-must-match js2-LC "msg.no.brace.catchblock")
- (js2-parse-statements catch-node)
- (if (js2-must-match js2-RC "msg.no.brace.after.body")
- (setq try-end (js2-current-token-end)))
- (js2-pop-scope)
- (setf (js2-node-len catch-node) (- try-end catch-pos)
- (js2-catch-node-param catch-node) param
- (js2-catch-node-guard-expr catch-node) catch-cond
- (js2-catch-node-guard-kwd catch-node) guard-kwd
- (js2-catch-node-lp catch-node) lp
- (js2-catch-node-rp catch-node) rp)
- (js2-node-add-children catch-node param catch-cond)
- (push catch-node catch-blocks))))
- ((/= peek js2-FINALLY)
- (js2-must-match js2-FINALLY "msg.try.no.catchfinally"
- (js2-node-pos try-block)
- (- (setq try-end (js2-node-end try-block))
- (js2-node-pos try-block)))))
- (when (js2-match-token js2-FINALLY)
- (let ((finally-pos (js2-current-token-beg))
- (block (js2-parse-statement)))
- (setq try-end (js2-node-end block)
- finally-block (make-js2-finally-node :pos finally-pos
- :len (- try-end finally-pos)
- :body block))
- (js2-node-add-children finally-block block)))
- (let ((pn (make-js2-try-node :pos try-pos
- :len (- try-end try-pos)
- :try-block try-block
- :finally-block finally-block)))
- (js2-node-add-children pn try-block finally-block)
- ;; Push them onto the try-node, which reverses and corrects their order.
- (dolist (cb catch-blocks)
- (js2-node-add-children pn cb)
- (push cb (js2-try-node-catch-clauses pn)))
- pn)))
-
-(defun js2-parse-throw ()
- "Parser for throw-statement. Last matched token must be js2-THROW."
- (let ((pos (js2-current-token-beg))
- expr pn)
- (if (= (js2-peek-token-or-eol) js2-EOL)
- ;; ECMAScript does not allow new lines before throw expression,
- ;; see bug 256617
- (js2-report-error "msg.bad.throw.eol"))
- (setq expr (js2-parse-expr)
- pn (make-js2-throw-node :pos pos
- :len (- (js2-node-end expr) pos)
- :expr expr))
- (js2-node-add-children pn expr)
- pn))
-
-(defun js2-match-jump-label-name (label-name)
- "If break/continue specified a label, return that label's labeled stmt.
-Returns the corresponding `js2-labeled-stmt-node', or if LABEL-NAME
-does not match an existing label, reports an error and returns nil."
- (let ((bundle (cdr (assoc label-name js2-label-set))))
- (if (null bundle)
- (js2-report-error "msg.undef.label"))
- bundle))
-
-(defun js2-parse-break ()
- "Parser for break-statement. Last matched token must be js2-BREAK."
- (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)
- (when (eq (js2-peek-token-or-eol) js2-NAME)
- (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-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
- (if (null js2-loop-and-switch-set)
- (unless break-label
- (js2-report-error "msg.bad.break" nil pos (length "break")))
- (setq break-target (car js2-loop-and-switch-set))))
- (setq pn (make-js2-break-node :pos pos
- :len (- end pos)
- :label break-label
- :target break-target))
- (js2-node-add-children pn break-label) ; but not break-target
- pn))
-
-(defun js2-parse-continue ()
- "Parser for continue-statement. Last matched token must be js2-CONTINUE."
- (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)
- (when (= (js2-peek-token-or-eol) js2-NAME)
- (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-current-token-string))))
- (cond
- ((null labels) ; no current label to go to
- (if (null js2-loop-set) ; no loop to continue to
- (js2-report-error "msg.continue.outside" nil pos
- (length "continue"))
- (setq target (car js2-loop-set)))) ; innermost enclosing loop
- (t
- (if (js2-loop-node-p (js2-labeled-stmt-node-stmt labels))
- (setq target (js2-labeled-stmt-node-stmt labels))
- (js2-report-error "msg.continue.nonloop" nil pos (- end pos)))))
- (setq pn (make-js2-continue-node :pos pos
- :len (- end pos)
- :label label
- :target target))
- (js2-node-add-children pn label) ; but not target - it's not our child
- pn))
-
-(defun js2-parse-with ()
- "Parser for with-statement. Last matched token must be js2-WITH."
- (let ((pos (js2-current-token-beg))
- obj body pn lp rp)
- (if (js2-must-match js2-LP "msg.no.paren.with")
- (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-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
- :len (- (js2-node-end body) pos)
- :object obj
- :body body
- :lp (js2-relpos lp pos)
- :rp (js2-relpos rp pos)))
- (js2-node-add-children pn obj body)
- pn))
-
-(defun js2-parse-const-var ()
- "Parser for var- or const-statement.
-Last matched token must be js2-CONST or js2-VAR."
- (let ((tt (js2-current-token-type))
- (pos (js2-current-token-beg))
- expr pn)
- (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))
- (js2-node-add-children pn expr)
- pn))
-
-(defun js2-wrap-with-expr-stmt (pos expr &optional add-child)
- (let ((pn (make-js2-expr-stmt-node :pos pos
- :len (js2-node-len expr)
- :type (if (js2-inside-function)
- js2-EXPR_VOID
- js2-EXPR_RESULT)
- :expr expr)))
- (if add-child
- (js2-node-add-children pn expr))
- pn))
-
-(defun js2-parse-let-stmt ()
- "Parser for let-statement. Last matched token must be js2-LET."
- (let ((pos (js2-current-token-beg))
- expr pn)
- (if (= (js2-peek-token) js2-LP)
- ;; let expression in statement context
- (setq expr (js2-parse-let pos 'statement)
- pn (js2-wrap-with-expr-stmt pos expr t))
- ;; else we're looking at a statement like let x=6, y=7;
- (setf expr (js2-parse-variables js2-LET pos)
- pn (js2-wrap-with-expr-stmt pos expr t)
- (js2-node-type pn) js2-EXPR_RESULT))
- pn))
-
-(defun js2-parse-ret-yield ()
- (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))
-
-(defsubst js2-now-all-set (before after mask)
- "Return whether or not the bits in the mask have changed to all set.
-BEFORE is bits before change, AFTER is bits after change, and MASK is
-the mask for bits. Returns t if all the bits in the mask are set in AFTER
-but not BEFORE."
- (and (/= (logand before mask) mask)
- (= (logand after mask) mask)))
-
-(defun js2-parse-return-or-yield (tt expr-context)
- (let* ((pos (js2-current-token-beg))
- (end (js2-current-token-end))
- (before js2-end-flags)
- (inside-function (js2-inside-function))
- (gen-type (and inside-function (js2-function-node-generator-type
- js2-current-script-or-fn)))
- e ret name yield-star-p)
- (unless inside-function
- (js2-report-error (if (eq tt js2-RETURN)
- "msg.bad.return"
- "msg.bad.yield")))
- (when (and inside-function
- (eq gen-type 'STAR)
- (js2-match-token js2-MUL))
- (setq yield-star-p t))
- ;; 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)
- end (js2-node-end e)))
- (cond
- ((eq tt js2-RETURN)
- (js2-set-flag js2-end-flags (if (null e)
- js2-end-returns
- js2-end-returns-value))
- (setq ret (make-js2-return-node :pos pos
- :len (- end pos)
- :retval e))
- (js2-node-add-children ret e)
- ;; See if we need a strict mode warning.
- ;; TODO: The analysis done by `js2-has-consistent-return-usage' is
- ;; more thorough and accurate than this before/after flag check.
- ;; E.g. if there's a finally-block that always returns, we shouldn't
- ;; show a warning generated by inconsistent returns in the catch blocks.
- ;; Basically `js2-has-consistent-return-usage' needs to keep more state,
- ;; so we know which returns/yields to highlight, and we should get rid of
- ;; all the checking in `js2-parse-return-or-yield'.
- (if (and js2-strict-inconsistent-return-warning
- (js2-now-all-set before js2-end-flags
- (logior js2-end-returns
js2-end-returns-value)))
- (js2-add-strict-warning "msg.return.inconsistent" nil pos end)))
- ((eq gen-type 'COMPREHENSION)
- ;; FIXME: We should probably switch to saving and using lastYieldOffset,
- ;; like SpiderMonkey does.
- (js2-report-error "msg.syntax" nil pos 5))
- (t
- (setq ret (make-js2-yield-node :pos pos
- :len (- end pos)
- :value e
- :star-p yield-star-p))
- (js2-node-add-children ret e)
- (unless expr-context
- (setq e ret
- ret (js2-wrap-with-expr-stmt pos e t))
- (js2-set-requires-activation)
- (js2-set-is-generator))))
- ;; see if we are mixing yields and value returns.
- (when (and inside-function
- (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))
- (js2-report-error "msg.generator.returns" name pos (- end pos))))
- ret))
-
-(defun js2-parse-debugger ()
- (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-current-token-beg))
- (pn (make-js2-scope)))
- (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-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.
-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-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-current-token-beg))
- end len expr unary)
- (js2-must-have-xml)
- (js2-set-requires-activation)
- (setq len (- js2-ts-cursor pos))
- (unless (and (js2-match-token js2-NAME)
- (string= (js2-current-token-string) "xml"))
- (js2-report-error "msg.bad.namespace" nil pos len))
- (unless (and (js2-match-token js2-NAME)
- (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))
- (setq expr (js2-parse-expr)
- end (js2-node-end expr)
- unary (make-js2-unary-node :type js2-DEFAULTNAMESPACE
- :pos pos
- :len (- end pos)
- :operand expr))
- (js2-node-add-children unary expr)
- (make-js2-expr-stmt-node :pos pos
- :len (- end pos)
- :expr unary)))
-
-(defun js2-record-label (label bundle)
- ;; current token should be colon that `js2-parse-primary-expr' left untouched
- (js2-get-token)
- (let ((name (js2-label-node-name label))
- labeled-stmt
- dup)
- (when (setq labeled-stmt (cdr (assoc name js2-label-set)))
- ;; flag both labels if possible when used in editing mode
- (if (and js2-parse-ide-mode
- (setq dup (js2-get-label-by-name labeled-stmt name)))
- (js2-report-error "msg.dup.label" nil
- (js2-node-abs-pos dup) (js2-node-len dup)))
- (js2-report-error "msg.dup.label" nil
- (js2-node-pos label) (js2-node-len label)))
- (js2-labeled-stmt-node-add-label bundle label)
- (js2-node-add-children bundle label)
- ;; Add one reference to the bundle per label in `js2-label-set'
- (push (cons name bundle) js2-label-set)))
-
-(defun js2-parse-name-or-label ()
- "Parser for identifier or label. Last token matched must be js2-NAME.
-Called when we found a name in a statement context. If it's a label, we gather
-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-current-token-beg))
- expr stmt bundle
- (continue t))
- ;; set check for label and call down to `js2-parse-primary-expr'
- (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-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))
- (setq js2-label-set (remove label js2-label-set))))
- (setf (js2-labeled-stmt-node-stmt bundle) stmt
- (js2-node-len bundle) (- (js2-node-end stmt) pos))
- (js2-node-add-children bundle stmt)
- bundle)))
-
-(defun js2-maybe-parse-label ()
- (cl-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-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.
-Could be a 'var', 'const' or 'let' expression, possibly in a for-loop
initializer.
-
-DECL-TYPE is a token value: either VAR, CONST, or LET depending on context.
-For 'var' or 'const', the keyword should be the token last scanned.
-
-POS is the position where the node should start. It's sometimes the
-var/const/let keyword, and other times the beginning of the first token
-in the first variable declaration.
-
-Returns the parsed `js2-var-decl-node' expression node."
- (let* ((result (make-js2-var-decl-node :decl-type decl-type
- :pos pos))
- destructuring kid-pos tt init name end nbeg nend vi
- (continue t))
- ;; Example:
- ;; var foo = {a: 1, b: 2}, bar = [3, 4];
- ;; var {b: s2, a: s1} = foo, x = 6, y, [s3, s4] = bar;
- ;; var {a, b} = baz;
- (while continue
- (setq destructuring nil
- name nil
- 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-name "msg.bad.var")
- (setq name (js2-create-name-node)
- nbeg (js2-current-token-beg)
- nend (js2-current-token-end)
- end nend)
- (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))
- (js2-record-imenu-functions init name))
- (when name
- (js2-set-face nbeg nend (if (js2-function-node-p init)
- 'font-lock-function-name-face
- 'font-lock-variable-name-face)
- 'record))
- (setq vi (make-js2-var-init-node :pos kid-pos
- :len (- end kid-pos)
- :type decl-type))
- (if destructuring
- (progn
- (if (and (null init) (not js2-in-for-init))
- (js2-report-error "msg.destruct.assign.no.init"))
- (js2-define-destruct-symbols destructuring
- decl-type
- 'font-lock-variable-name-face)
- (setf (js2-var-init-node-target vi) destructuring))
- (setf (js2-var-init-node-target vi) name))
- (setf (js2-var-init-node-initializer vi) init)
- (js2-node-add-children vi name destructuring init)
- (js2-block-node-push result vi)
- (unless (js2-match-token js2-COMMA)
- (setq continue nil)))
- (setf (js2-node-len result) (- end pos))
- result))
-
-(defun js2-parse-let (pos &optional stmt-p)
- "Parse a let expression or statement.
-A let-expression is of the form `let (vars) expr'.
-A let-statment is of the form `let (vars) {statements}'.
-The third form of let is a variable declaration list, handled
-by `js2-parse-variables'."
- (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-current-token-beg) pos)))
- (js2-push-scope pn)
- (unwind-protect
- (progn
- (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-current-token-beg) pos)))
- (if (and stmt-p (js2-match-token js2-LC))
- ;; let statement
- (progn
- (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-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
- (setf body (js2-parse-expr)
- (js2-node-len pn) (- (js2-node-end body) pos)
- (js2-let-node-body pn) body))
- (setf (js2-let-node-vars pn) vars)
- (js2-node-add-children pn vars body))
- (js2-pop-scope))
- pn))
-
-(defun js2-define-new-symbol (decl-type name node &optional scope)
- (js2-scope-put-symbol (or scope js2-current-scope)
- name
- (make-js2-symbol decl-type name node)))
-
-(defun js2-define-symbol (decl-type name &optional node ignore-not-in-block)
- "Define a symbol in the current scope.
-If NODE is non-nil, it is the AST node associated with the symbol."
- (let* ((defining-scope (js2-get-defining-scope js2-current-scope name))
- (symbol (if defining-scope
- (js2-scope-get-symbol defining-scope name)))
- (sdt (if symbol (js2-symbol-decl-type symbol) -1))
- (pos (if node (js2-node-abs-pos node)))
- (len (if node (js2-node-len node))))
- (cond
- ((and symbol ; already defined
- (or (= sdt js2-CONST) ; old version is const
- (= decl-type js2-CONST) ; new version is const
- ;; two let-bound vars in this block have same name
- (and (= sdt js2-LET)
- (eq defining-scope js2-current-scope))))
- (js2-report-error
- (cond
- ((= sdt js2-CONST) "msg.const.redecl")
- ((= sdt js2-LET) "msg.let.redecl")
- ((= sdt js2-VAR) "msg.var.redecl")
- ((= sdt js2-FUNCTION) "msg.function.redecl")
- (t "msg.parm.redecl"))
- name pos len))
- ((= decl-type js2-LET)
- (if (and (not ignore-not-in-block)
- (or (= (js2-node-type js2-current-scope) js2-IF)
- (js2-loop-node-p js2-current-scope)))
- (js2-report-error "msg.let.decl.not.in.block")
- (js2-define-new-symbol decl-type name node)))
- ((or (= decl-type js2-VAR)
- (= decl-type js2-CONST)
- (= decl-type js2-FUNCTION))
- (if symbol
- (if (and js2-strict-var-redeclaration-warning (= sdt js2-VAR))
- (js2-add-strict-warning "msg.var.redecl" name)
- (if (and js2-strict-var-hides-function-arg-warning (= sdt js2-LP))
- (js2-add-strict-warning "msg.var.hides.arg" name)))
- (js2-define-new-symbol decl-type name node
- js2-current-script-or-fn)))
- ((= decl-type js2-LP)
- (if symbol
- ;; must be duplicate parameter. Second parameter hides the
- ;; first, so go ahead and add the second pararameter
- (js2-report-warning "msg.dup.parms" name))
- (js2-define-new-symbol decl-type name node))
- (t (js2-code-bug)))))
-
-(defun js2-parse-paren-expr-or-generator-comp ()
- (let ((px-pos (js2-current-token-beg)))
- (if (and (>= js2-language-version 200)
- (js2-match-token js2-FOR))
- (js2-parse-generator-comp px-pos)
- (let* ((js2-in-for-init nil)
- (expr (js2-parse-expr))
- (pn (make-js2-paren-node :pos px-pos
- :expr expr
- :len (- (js2-current-token-end)
- px-pos))))
- (js2-node-add-children pn (js2-paren-node-expr pn))
- (js2-must-match js2-RP "msg.no.paren")
- pn))))
-
-(defun js2-parse-expr (&optional oneshot)
- (let* ((pn (js2-parse-assign-expr))
- (pos (js2-node-pos pn))
- left
- right
- op-pos)
- (while (and (not oneshot)
- (js2-match-token js2-COMMA))
- (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)
- left pn
- pn (make-js2-infix-node :type js2-COMMA
- :pos pos
- :len (- js2-ts-cursor pos)
- :op-pos op-pos
- :left left
- :right right))
- (js2-node-add-children pn left right))
- pn))
-
-(defun js2-parse-assign-expr ()
- (let ((tt (js2-get-token))
- (pos (js2-current-token-beg))
- pn left right op-pos
- ts-state recorded-identifiers parsed-errors)
- (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
- parsed-errors js2-parsed-errors)
- ;; not yield - parse assignment expression
- (setq pn (js2-parse-cond-expr)
- tt (js2-get-token))
- (cond
- ((and (<= js2-first-assign tt)
- (<= tt js2-last-assign))
- ;; tt express assignment (=, |=, ^=, ..., %=)
- (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)
- :op-pos op-pos
- :left left
- :right right))
- (when js2-parse-ide-mode
- (js2-highlight-assign-targets pn left right)
- (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
- js2-parsed-errors parsed-errors)
- (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-current-token-beg))
- (pn (js2-parse-or-expr))
- test-expr
- if-true
- if-false
- q-pos
- c-pos)
- (when (js2-match-token js2-HOOK)
- (setq q-pos (- (js2-current-token-beg) pos)
- if-true (let (js2-in-for-init) (js2-parse-assign-expr)))
- (js2-must-match js2-COLON "msg.no.colon.cond")
- (setq c-pos (- (js2-current-token-beg) pos)
- if-false (js2-parse-assign-expr)
- test-expr pn
- pn (make-js2-cond-node :pos pos
- :len (- (js2-node-end if-false) pos)
- :test-expr test-expr
- :true-expr if-true
- :false-expr if-false
- :q-pos q-pos
- :c-pos c-pos))
- (js2-node-add-children pn test-expr if-true if-false))
- pn))
-
-(defun js2-make-binary (type left parser)
- "Helper for constructing a binary-operator AST node.
-LEFT is the left-side-expression, already parsed, and the
-binary operator should have just been matched.
-PARSER is a function to call to parse the right operand,
-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-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
- :len (- (js2-node-end right) pos)
- :op-pos op-pos
- :left left
- :right right)))
- (js2-node-add-children pn left right)
- pn))
-
-(defun js2-parse-or-expr ()
- (let ((pn (js2-parse-and-expr)))
- (when (js2-match-token js2-OR)
- (setq pn (js2-make-binary js2-OR
- pn
- 'js2-parse-or-expr)))
- pn))
-
-(defun js2-parse-and-expr ()
- (let ((pn (js2-parse-bit-or-expr)))
- (when (js2-match-token js2-AND)
- (setq pn (js2-make-binary js2-AND
- pn
- 'js2-parse-and-expr)))
- pn))
-
-(defun js2-parse-bit-or-expr ()
- (let ((pn (js2-parse-bit-xor-expr)))
- (while (js2-match-token js2-BITOR)
- (setq pn (js2-make-binary js2-BITOR
- pn
- 'js2-parse-bit-xor-expr)))
- pn))
-
-(defun js2-parse-bit-xor-expr ()
- (let ((pn (js2-parse-bit-and-expr)))
- (while (js2-match-token js2-BITXOR)
- (setq pn (js2-make-binary js2-BITXOR
- pn
- 'js2-parse-bit-and-expr)))
- pn))
-
-(defun js2-parse-bit-and-expr ()
- (let ((pn (js2-parse-eq-expr)))
- (while (js2-match-token js2-BITAND)
- (setq pn (js2-make-binary js2-BITAND
- pn
- 'js2-parse-eq-expr)))
- pn))
-
-(defconst js2-parse-eq-ops
- (list js2-EQ js2-NE js2-SHEQ js2-SHNE))
-
-(defun js2-parse-eq-expr ()
- (let ((pn (js2-parse-rel-expr))
- tt)
- (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
- (list js2-IN js2-INSTANCEOF js2-LE js2-LT js2-GE js2-GT))
-
-(defun js2-parse-rel-expr ()
- (let ((pn (js2-parse-shift-expr))
- (continue t)
- tt)
- (while continue
- (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)
- (setq pn (js2-make-binary tt pn 'js2-parse-shift-expr)))
- (t
- (js2-unget-token)
- (setq continue nil))))
- pn))
-
-(defconst js2-parse-shift-ops
- (list js2-LSH js2-URSH js2-RSH))
-
-(defun js2-parse-shift-expr ()
- (let ((pn (js2-parse-add-expr))
- tt
- (continue t))
- (while continue
- (setq tt (js2-get-token))
- (if (memq tt js2-parse-shift-ops)
- (setq pn (js2-make-binary tt pn 'js2-parse-add-expr))
- (js2-unget-token)
- (setq continue nil)))
- pn))
-
-(defun js2-parse-add-expr ()
- (let ((pn (js2-parse-mul-expr))
- tt
- (continue t))
- (while continue
- (setq tt (js2-get-token))
- (if (or (= tt js2-ADD) (= tt js2-SUB))
- (setq pn (js2-make-binary tt pn 'js2-parse-mul-expr))
- (js2-unget-token)
- (setq continue nil)))
- pn))
-
-(defconst js2-parse-mul-ops
- (list js2-MUL js2-DIV js2-MOD))
-
-(defun js2-parse-mul-expr ()
- (let ((pn (js2-parse-unary-expr))
- tt
- (continue t))
- (while continue
- (setq tt (js2-get-token))
- (if (memq tt js2-parse-mul-ops)
- (setq pn (js2-make-binary tt pn 'js2-parse-unary-expr))
- (js2-unget-token)
- (setq continue nil)))
- pn))
-
-(defun js2-make-unary (type parser &rest args)
- "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-current-token-beg))
- (postfix (js2-node-p parser))
- (expr (if postfix
- parser
- (apply parser args)))
- end
- pn)
- (if postfix ; e.g. i++
- (setq pos (js2-node-pos expr)
- end (js2-current-token-end))
- (setq end (js2-node-end expr)))
- (setq pn (make-js2-unary-node :type type
- :pos pos
- :len (- end pos)
- :operand expr))
- (js2-node-add-children pn expr)
- pn))
-
-(defconst js2-incrementable-node-types
- (list js2-NAME js2-GETPROP js2-GETELEM js2-GET_REF js2-CALL)
- "Node types that can be the operand of a ++ or -- operator.")
-
-(defun js2-check-bad-inc-dec (tt beg end unary)
- (unless (memq (js2-node-type (js2-unary-node-operand unary))
- js2-incrementable-node-types)
- (js2-report-error (if (= tt js2-INC)
- "msg.bad.incr"
- "msg.bad.decr")
- nil beg (- end beg))))
-
-(defun js2-parse-unary-expr ()
- (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-get-token)
- (js2-make-unary tt 'js2-parse-unary-expr))
- ((= tt js2-ADD)
- (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-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-get-token)
- (prog1
- (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-get-token)
- (js2-make-unary js2-DELPROP 'js2-parse-unary-expr))
- ((= tt js2-ERROR)
- (js2-get-token)
- (make-js2-error-node)) ; try to continue
- ((and (= tt js2-LT)
- js2-compiler-xml-available)
- ;; XML stream encountered in expression.
- (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-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-current-token-beg)
(js2-current-token-end) pn))
- pn))))
-
-(defun js2-parse-xml-initializer ()
- "Parse an E4X XML initializer.
-I'm parsing it the way Rhino parses it, but without the tree-rewriting.
-Then I'll postprocess the result, depending on whether we're in IDE
-mode or codegen mode, and generate the appropriate rewritten AST.
-IDE mode uses a rich AST that models the XML structure. Codegen mode
-just concatenates everything and makes a new XML or XMLList out of it."
- (let ((tt (js2-get-first-xml-token))
- pn-xml pn expr kids expr-pos
- (continue t)
- (first-token t))
- (when (not (or (= tt js2-XML) (= tt js2-XMLEND)))
- (js2-report-error "msg.syntax"))
- (setq pn-xml (make-js2-xml-node))
- (while continue
- (if first-token
- (setq first-token nil)
- (setq tt (js2-get-next-xml-token)))
- (cond
- ;; js2-XML means we found a {expr} in the XML stream.
- ;; The token string is the XML up to the left-curly.
- ((= tt js2-XML)
- (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
- expr (if (eq (js2-peek-token) js2-RC)
- (make-js2-empty-expr-node :pos expr-pos)
- (js2-parse-expr)))
- (js2-must-match js2-RC "msg.syntax")
- (setq pn (make-js2-xml-js-expr-node :pos (js2-node-pos expr)
- :len (js2-node-len expr)
- :expr expr))
- (js2-node-add-children pn expr)
- (push pn kids))
- ;; a js2-XMLEND token means we hit the final close-tag.
- ((= tt js2-XMLEND)
- (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))
- (setf (js2-node-len pn-xml) (- js2-ts-cursor
- (js2-node-pos pn-xml))
- continue nil))
- (t
- (js2-report-error "msg.syntax")
- (setq continue nil))))
- pn-xml))
-
-
-(defun js2-parse-argument-list ()
- "Parse an argument list and return it as a Lisp list of nodes.
-Returns the list in reverse order. Consumes the right-paren token."
- (let (result)
- (unless (js2-match-token js2-RP)
- (cl-loop do
- (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-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-get-token)
- (setq pos (js2-current-token-beg)
- beg pos
- target (js2-parse-member-expr)
- end (js2-node-end target)
- pn (make-js2-new-node :pos pos
- :target target
- :len (- end pos)))
- (js2-highlight-function-call (js2-current-token))
- (js2-node-add-children pn 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-current-token-beg)
- args (nreverse (js2-parse-argument-list))
- (js2-new-node-args pn) args
- 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))
- (when (and js2-allow-rhino-new-expr-initializer
- (js2-match-token js2-LC))
- (setf init (js2-parse-object-literal)
- end (js2-node-end init)
- (js2-new-node-initializer pn) init)
- (js2-node-add-children pn init))
- (setf (js2-node-len pn) (- end beg))) ; end outer if
- (js2-parse-member-expr-tail allow-call-syntax pn)))
-
-(defun js2-parse-member-expr-tail (allow-call-syntax pn)
- "Parse a chain of property/array accesses or function calls.
-Includes parsing for E4X operators like `..' and `.@'.
-If ALLOW-CALL-SYNTAX is nil, stops when we encounter a left-paren.
-Returns an expression tree that includes PN, the parent node."
- (let (tt
- (continue t))
- (while continue
- (setq tt (js2-get-token))
- (cond
- ((or (= tt js2-DOT) (= tt js2-DOTDOT))
- (setq pn (js2-parse-property-access tt pn)))
- ((= tt js2-DOTQUERY)
- (setq pn (js2-parse-dot-query pn)))
- ((= tt js2-LB)
- (setq pn (js2-parse-element-get pn)))
- ((= tt js2-LP)
- (js2-unget-token)
- (if allow-call-syntax
- (setq pn (js2-parse-function-call pn))
- (setq continue nil)))
- ((= tt js2-TEMPLATE_HEAD)
- (setq pn (js2-parse-tagged-template pn (js2-parse-template-literal))))
- ((= tt js2-NO_SUBS_TEMPLATE)
- (setq pn (js2-parse-tagged-template pn (make-js2-string-node :type
tt))))
- (t
- (js2-unget-token)
- (setq continue nil))))
- (if (>= js2-highlight-level 2)
- (js2-parse-highlight-member-expr-node pn))
- pn))
-
-(defun js2-parse-tagged-template (tag-node tpl-node)
- "Parse tagged template expression."
- (let* ((beg (js2-node-pos tag-node))
- (pn (make-js2-tagged-template-node :beg beg
- :len (- (js2-current-token-end)
beg)
- :tag tag-node
- :template tpl-node)))
- (js2-node-add-children pn tag-node tpl-node)
- pn))
-
-(defun js2-parse-dot-query (pn)
- "Parse a dot-query expression, e.g. foo.bar.(@name == 2)
-Last token parsed must be `js2-DOTQUERY'."
- (let ((pos (js2-node-pos pn))
- op-pos expr end)
- (js2-must-have-xml)
- (js2-set-requires-activation)
- (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
- :pos pos
- :op-pos op-pos
- :right expr))
- (js2-node-add-children 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-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-current-token-beg))
- (pos (js2-node-pos pn))
- rb expr)
- (setq expr (js2-parse-expr))
- (if (js2-must-match js2-RB "msg.no.bracket.index")
- (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-current-token-end) pos)))
- (js2-node-add-children pn
- (js2-elem-get-node-target pn)
- (js2-elem-get-node-element pn))
- pn))
-
-(defun js2-highlight-function-call (token)
- (when (eq (js2-token-type token) js2-NAME)
- (js2-record-face 'js2-function-call token)))
-
-(defun js2-parse-function-call (pn)
- (js2-highlight-function-call (js2-current-token))
- (js2-get-token)
- (let (args
- (pos (js2-node-pos pn)))
- (setq pn (make-js2-call-node :pos pos
- :target pn
- :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-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))
- pn))
-
-(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-current-token-beg))
- (dot-len (if (= tt js2-DOTDOT) 2 1))
- name
- ref ; right side of . or .. operator
- result)
- (when (= tt js2-DOTDOT)
- (js2-must-have-xml)
- (setq member-type-flags js2-descendants-flag))
- (if (not js2-compiler-xml-available)
- (progn
- (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-current-token-beg)
- :right name
- :len (js2-current-token-len)))
- (js2-node-add-children result pn name)
- result)
- ;; otherwise look for XML operators
- (setf result (if (= tt js2-DOT)
- (make-js2-prop-get-node)
- (make-js2-infix-node :type js2-DOTDOT))
- (js2-node-pos result) (js2-node-pos pn)
- (js2-infix-node-op-pos result) dot-pos
- (js2-infix-node-left result) pn ; do this after setting position
- tt (js2-get-prop-name-token))
- (cond
- ;; handles: name, ns::name, ns::*, ns::[expr]
- ((= tt js2-NAME)
- (setq ref (js2-parse-property-name -1 nil member-type-flags)))
- ;; handles: *, *::name, *::*, *::[expr]
- ((= tt js2-MUL)
- (setq ref (js2-parse-property-name nil "*" member-type-flags)))
- ;; handles: '@attr', '@ns::attr', '@ns::*', '@ns::[expr]', etc.
- ((= tt js2-XMLATTR)
- (setq result (js2-parse-attribute-access)))
- (t
- (js2-report-error "msg.no.name.after.dot" nil dot-pos dot-len)))
- (if ref
- (setf (js2-node-len result) (- (js2-node-end ref)
- (js2-node-pos result))
- (js2-infix-node-right result) ref))
- (if (js2-infix-node-p result)
- (js2-node-add-children result
- (js2-infix-node-left result)
- (js2-infix-node-right result)))
- result)))
-
-(defun js2-parse-attribute-access ()
- "Parse an E4X XML attribute expression.
-This includes expressions of the forms:
-
- @attr @ns::attr @ns::*
- @* @*::attr @*::*
- @[expr] @*::[expr] @ns::[expr]
-
-Called if we peeked an '@' token."
- (let ((tt (js2-get-prop-name-token))
- (at-pos (js2-current-token-beg)))
- (cond
- ;; handles: @name, @ns::name, @ns::*, @ns::[expr]
- ((= tt js2-NAME)
- (js2-parse-property-name at-pos nil 0))
- ;; handles: @*, @*::name, @*::*, @*::[expr]
- ((= tt js2-MUL)
- (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-parse-property-name (js2-current-token-beg) "" 0)))))
-
-(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.
-S is the name or string that was matched: an identifier, 'throw' or '*'.
-MEMBER-TYPE-FLAGS is a bit set tracking whether we're a '.' or '..' child.
-
-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-current-token-beg)))
- colon-pos
- (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-current-token-beg)
- tt (js2-get-prop-name-token))
- (cond
- ;; handles name::name
- ((= tt js2-NAME)
- (setq name (js2-create-name-node)))
- ;; handles name::*
- ((= tt js2-MUL)
- (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)))
- (t
- (js2-report-error "msg.no.name.after.coloncolon"))))
- (if (and (null ns) (zerop member-type-flags))
- name
- (prog1
- (setq pn
- (make-js2-xml-prop-ref-node :pos pos
- :len (- (js2-node-end name) pos)
- :at-pos at-pos
- :colon-pos colon-pos
- :propname name))
- (js2-node-add-children pn name))))))
-
-(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-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-current-token-beg)
- end (js2-current-token-end)))
- (prog1
- (setq pn
- (make-js2-xml-elem-ref-node :pos pos
- :len (- end pos)
- :namespace namespace
- :colon-pos colon-pos
- :at-pos at-pos
- :expr expr
- :lb (js2-relpos lb pos)
- :rb (js2-relpos rb pos)))
- (js2-node-add-children pn namespace expr))))
-
-(defun js2-parse-destruct-primary-expr ()
- (let ((js2-is-in-destructuring t))
- (js2-parse-primary-expr)))
-
-(defun js2-parse-primary-expr ()
- "Parse a literal (leaf) expression of some sort.
-Includes complex literals such as functions, object-literals,
-array-literals, array comprehensions and regular expressions."
- (let (pn ; parent node (usually return value)
- tt)
- (setq tt (js2-current-token-type))
- (cond
- ((= tt js2-CLASS)
- (js2-parse-class-expr))
- ((= tt js2-FUNCTION)
- (js2-parse-function-expr))
- ((= tt js2-LB)
- (js2-parse-array-comp-or-literal))
- ((= tt js2-LC)
- (js2-parse-object-literal))
- ((= tt js2-LET)
- (js2-parse-let (js2-current-token-beg)))
- ((= tt js2-LP)
- (js2-parse-paren-expr-or-generator-comp))
- ((= tt js2-XMLATTR)
- (js2-must-have-xml)
- (js2-parse-attribute-access))
- ((= tt js2-NAME)
- (js2-parse-name tt))
- ((= tt js2-NUMBER)
- (make-js2-number-node))
- ((or (= tt js2-STRING) (= tt js2-NO_SUBS_TEMPLATE))
- (make-js2-string-node :type tt))
- ((= tt js2-TEMPLATE_HEAD)
- (js2-parse-template-literal))
- ((or (= tt js2-DIV) (= tt js2-ASSIGN_DIV))
- ;; Got / or /= which in this context means a regexp literal
- (let ((px-pos (js2-current-token-beg))
- (flags (js2-read-regexp tt))
- (end (js2-current-token-end)))
- (prog1
- (make-js2-regexp-node :pos px-pos
- :len (- end px-pos)
- :value (js2-current-token-string)
- :flags flags)
- (js2-set-face px-pos end 'font-lock-string-face 'record)
- (js2-record-text-property px-pos end 'syntax-table '(2)))))
- ((or (= tt js2-NULL)
- (= tt js2-THIS)
- (= tt js2-SUPER)
- (= 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))
- ((= tt js2-ERROR)
- ;; the scanner or one of its subroutines reported the error.
- (make-js2-error-node))
- ((= tt js2-EOF)
- (let* ((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 (1- js2-ts-cursor)))
- (t
- (js2-report-error "msg.syntax")
- (make-js2-error-node)))))
-
-(defun js2-parse-template-literal ()
- (let ((beg (js2-current-token-beg))
- (kids (list (make-js2-string-node :type js2-TEMPLATE_HEAD)))
- (tt js2-TEMPLATE_HEAD))
- (while (eq tt js2-TEMPLATE_HEAD)
- (push (js2-parse-expr) kids)
- (js2-must-match js2-RC "msg.syntax")
- (setq tt (js2-get-token 'TEMPLATE_TAIL))
- (push (make-js2-string-node :type tt) kids))
- (setq kids (nreverse kids))
- (let ((tpl (make-js2-template-node :beg beg
- :len (- (js2-current-token-end) beg)
- :kids kids)))
- (apply #'js2-node-add-children tpl kids)
- tpl)))
-
-(defun js2-parse-name (_tt)
- (let ((name (js2-current-token-string))
- 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
- msg nil
- ;; back up from comma to beginning of line or array/objlit
- (max (if elems
- (js2-node-pos (car elems))
- pos)
- (save-excursion
- (goto-char comma-pos)
- (back-to-indentation)
- (point)))
- comma-pos))
-
-(defun js2-parse-array-comp-or-literal ()
- (let ((pos (js2-current-token-beg)))
- (if (and (>= js2-language-version 200)
- (js2-match-token js2-FOR))
- (js2-parse-array-comp pos)
- (js2-parse-array-literal pos))))
-
-(defun js2-parse-array-literal (pos)
- (let ((after-lb-or-comma t)
- after-comma tt elems pn
- (continue t))
- (unless js2-is-in-destructuring
- (js2-push-scope (make-js2-scope))) ; for the legacy array comp
- (while continue
- (setq tt (js2-get-token))
- (cond
- ;; comma
- ((= tt js2-COMMA)
- (setq after-comma (js2-current-token-end))
- (if (not after-lb-or-comma)
- (setq after-lb-or-comma t)
- (push nil elems)))
- ;; end of array
- ((or (= tt js2-RB)
- (= tt js2-EOF)) ; prevent infinite loop
- (if (= tt js2-EOF)
- (js2-report-error "msg.no.bracket.arg" nil pos))
- (when (and after-comma (< js2-language-version 170))
- (js2-parse-warn-trailing-comma "msg.array.trailing.comma"
- pos (remove nil elems) after-comma))
- (setq continue nil
- pn (make-js2-array-node :pos pos
- :len (- js2-ts-cursor pos)
- :elems (nreverse elems)))
- (apply #'js2-node-add-children pn (js2-array-node-elems pn)))
- ;; destructuring binding
- (js2-is-in-destructuring
- (push (if (or (= tt js2-LC)
- (= tt js2-LB)
- (= tt js2-NAME))
- ;; [a, b, c] | {a, b, c} | {a:x, b:y, c:z} | a
- (js2-parse-destruct-primary-expr)
- ;; invalid pattern
- (js2-report-error "msg.bad.var")
- (make-js2-error-node))
- elems)
- (setq after-lb-or-comma nil
- after-comma nil))
- ;; array comp
- ((and (>= js2-language-version 170)
- (= tt js2-FOR) ; check for array comprehension
- (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-legacy-array-comp (car elems) pos)))
- ;; another element
- (t
- (unless after-lb-or-comma
- (js2-report-error "msg.no.bracket.arg"))
- (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
- (js2-pop-scope))
- pn))
-
-(defun js2-parse-legacy-array-comp (expr pos)
- "Parse a legacy array comprehension (JavaScript 1.7).
-EXPR is the first expression after the opening left-bracket.
-POS is the beginning of the LB token preceding EXPR.
-We should have just parsed the 'for' keyword before calling this function."
- (let ((current-scope js2-current-scope)
- loops first filter result)
- (unwind-protect
- (progn
- (while (js2-match-token js2-FOR)
- (let ((loop (make-js2-comp-loop-node)))
- (js2-push-scope loop)
- (push loop loops)
- (js2-parse-comp-loop loop)))
- ;; First loop takes expr scope's parent.
- (setf (js2-scope-parent-scope (setq first (car (last loops))))
- (js2-scope-parent-scope current-scope))
- ;; Set expr scope's parent to the last loop.
- (setf (js2-scope-parent-scope current-scope) (car loops))
- (if (/= (js2-get-token) js2-IF)
- (js2-unget-token)
- (setq filter (js2-parse-condition))))
- (dotimes (_ (1- (length loops)))
- (js2-pop-scope)))
- (js2-must-match js2-RB "msg.no.bracket.arg" pos)
- (setq result (make-js2-comp-node :pos pos
- :len (- js2-ts-cursor pos)
- :result expr
- :loops (nreverse loops)
- :filters (and filter (list (car filter)))
- :form 'LEGACY_ARRAY))
- (apply #'js2-node-add-children result expr (car filter)
- (js2-comp-node-loops result))
- result))
-
-(defun js2-parse-array-comp (pos)
- "Parse an ES6 array comprehension.
-POS is the beginning of the LB token.
-We should have just parsed the 'for' keyword before calling this function."
- (let ((pn (js2-parse-comprehension pos 'ARRAY)))
- (js2-must-match js2-RB "msg.no.bracket.arg" pos)
- pn))
-
-(defun js2-parse-generator-comp (pos)
- (let* ((js2-nesting-of-function (1+ js2-nesting-of-function))
- (js2-current-script-or-fn
- (make-js2-function-node :generator-type 'COMPREHENSION))
- (pn (js2-parse-comprehension pos 'STAR_GENERATOR)))
- (js2-must-match js2-RP "msg.no.paren" pos)
- pn))
-
-(defun js2-parse-comprehension (pos form)
- (let (loops filters expr result)
- (unwind-protect
- (progn
- (js2-unget-token)
- (while (js2-match-token js2-FOR)
- (let ((loop (make-js2-comp-loop-node)))
- (js2-push-scope loop)
- (push loop loops)
- (js2-parse-comp-loop loop)))
- (while (js2-match-token js2-IF)
- (push (car (js2-parse-condition)) filters))
- (setq expr (js2-parse-assign-expr)))
- (dolist (_ loops)
- (js2-pop-scope)))
- (setq result (make-js2-comp-node :pos pos
- :len (- js2-ts-cursor pos)
- :result expr
- :loops (nreverse loops)
- :filters (nreverse filters)
- :form form))
- (apply #'js2-node-add-children result (js2-comp-node-loops result))
- (apply #'js2-node-add-children result expr (js2-comp-node-filters result))
- result))
-
-(defun js2-parse-comp-loop (pn &optional only-of-p)
- "Parse a 'for [each] (foo [in|of] bar)' expression in an Array comprehension.
-The current token should be the initial FOR.
-If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed."
- (let ((pos (js2-comp-loop-node-pos pn))
- tt iter obj foreach-p forof-p in-pos each-pos lp rp)
- (when (and (not only-of-p) (js2-match-token js2-NAME))
- (if (string= (js2-current-token-string) "each")
- (progn
- (setq foreach-p t
- 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-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))
- ((js2-match-token js2-NAME)
- (setq iter (js2-create-name-node)))
- (t
- (js2-report-error "msg.bad.var")))
- ;; Define as a let since we want the scope of the variable to
- ;; be restricted to the array comprehension
- (if (js2-name-node-p iter)
- (js2-define-symbol js2-LET (js2-name-node-name iter) pn t))
- (if (or (and (not only-of-p) (js2-match-token js2-IN))
- (and (>= js2-language-version 200)
- (js2-match-contextual-kwd "of")
- (setq forof-p t)))
- (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-current-token-beg) pos)))
- (setf (js2-node-pos pn) pos
- (js2-node-len pn) (- js2-ts-cursor pos)
- (js2-comp-loop-node-iterator pn) iter
- (js2-comp-loop-node-object pn) obj
- (js2-comp-loop-node-in-pos pn) in-pos
- (js2-comp-loop-node-each-pos pn) each-pos
- (js2-comp-loop-node-foreach-p pn) foreach-p
- (js2-comp-loop-node-forof-p pn) forof-p
- (js2-comp-loop-node-lp pn) lp
- (js2-comp-loop-node-rp pn) rp)
- (js2-node-add-children pn iter obj)
- pn))
-
-(defun js2-parse-class-stmt ()
- (let ((pos (js2-current-token-beg)))
- (js2-must-match-name "msg.unnamed.class.stmt")
- (js2-parse-class pos 'CLASS_STATEMENT (js2-create-name-node t))))
-
-(defun js2-parse-class-expr ()
- (let ((pos (js2-current-token-beg))
- name)
- (when (js2-match-token js2-NAME)
- (setq name (js2-create-name-node t)))
- (js2-parse-class pos 'CLASS_EXPRESSION name)))
-
-(defun js2-parse-class (pos form name)
- ;; class X [extends ...] {
- (let (pn elems extends)
- (when name
- (js2-set-face (js2-node-pos name) (js2-node-end name)
- 'font-lock-function-name-face 'record))
- (if (js2-match-token js2-EXTENDS)
- (if (= (js2-peek-token) js2-LC)
- (js2-report-error "msg.missing.extends")
- ;; TODO(sdh): this should be left-hand-side-expr, not assign-expr
- (setq extends (js2-parse-assign-expr))
- (if (not extends)
- (js2-report-error "msg.bad.extends"))))
- (js2-must-match js2-LC "msg.no.brace.class")
- (setq elems (js2-parse-object-literal-elems t)
- pn (make-js2-class-node :pos pos
- :len (- js2-ts-cursor pos)
- :form form
- :name name
- :extends extends
- :elems elems))
- (apply #'js2-node-add-children pn (js2-class-node-elems pn))
- pn))
-
-(defun js2-parse-object-literal ()
- (let* ((pos (js2-current-token-beg))
- (elems (js2-parse-object-literal-elems))
- (result (make-js2-object-node :pos pos
- :len (- js2-ts-cursor pos)
- :elems elems)))
- (apply #'js2-node-add-children result (js2-object-node-elems result))
- result))
-
-(defun js2-parse-object-literal-elems (&optional class-p)
- (let ((pos (js2-current-token-beg))
- (static nil)
- (continue t)
- tt elems elem after-comma)
- (while continue
- (setq static (and class-p (js2-match-token js2-STATIC))
- tt (js2-get-prop-name-token)
- elem nil)
- (cond
- ;; {foo: ...}, {'foo': ...}, {foo, bar, ...},
- ;; {get foo() {...}}, {set foo(x) {...}}, or {foo(x) {...}}
- ;; TODO(sdh): support *foo() {...}
- ((or (= js2-NAME tt)
- (= tt js2-STRING))
- (setq after-comma nil
- elem (js2-parse-named-prop tt))
- (if (and (null elem)
- (not js2-recover-from-parse-errors))
- (setq continue nil)))
- ;; {[Symbol.iterator]: ...}
- ((and (= tt js2-LB)
- (>= js2-language-version 200))
- (let ((expr (js2-parse-expr)))
- (js2-must-match js2-RB "msg.missing.computed.rb")
- (setq after-comma nil
- elem (js2-parse-plain-property expr))))
- ;; {12: x} or {10.7: x}
- ((= tt js2-NUMBER)
- (setq after-comma nil
- elem (js2-parse-plain-property (make-js2-number-node))))
- ;; Break out of loop, and handle trailing commas.
- ((or (= tt js2-RC)
- (= tt js2-EOF))
- (js2-unget-token)
- (setq continue nil)
- (if after-comma
- (js2-parse-warn-trailing-comma "msg.extra.trailing.comma"
- pos elems after-comma)))
- (t
- (js2-report-error "msg.bad.prop")
- (unless js2-recover-from-parse-errors
- (setq continue nil)))) ; end switch
- ;; Handle static for classes' codegen.
- (if static
- (if elem (js2-node-set-prop elem 'STATIC t)
- (js2-report-error "msg.unexpected.static")))
- ;; Handle commas, depending on class-p.
- (let ((comma (js2-match-token js2-COMMA)))
- (if class-p
- (if comma
- (js2-report-error "msg.class.unexpected.comma"))
- (if comma
- (setq after-comma (js2-current-token-end))
- (setq continue nil))))
- ;; Append any parsed element.
- (if elem (push elem elems))) ; end loop
- (js2-must-match js2-RC "msg.no.brace.prop")
- (nreverse elems)))
-
-(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."
- (let ((string-prop (and (= tt js2-STRING)
- (make-js2-string-node)))
- expr
- (ppos (js2-current-token-beg))
- (pend (js2-current-token-end))
- (name (js2-create-name-node))
- (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-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
- (js2-parse-getter-setter-prop ppos name prop))
- ;; method definition: {f() {...}}
- ((and (= (js2-peek-token) js2-LP)
- (>= js2-language-version 200))
- (js2-record-face 'font-lock-function-name-face) ; name
- (js2-parse-getter-setter-prop ppos name ""))
- ;; regular prop
- (t
- (prog1
- (setq expr (js2-parse-plain-property (or string-prop name)))
- (when (and (not string-prop)
- (not js2-is-in-destructuring)
- js2-highlight-external-variables
- (js2-node-get-prop expr 'SHORTHAND))
- (js2-record-name-node name))
- (js2-set-face ppos pend
- (if (js2-function-node-p
- (js2-object-prop-node-right expr))
- 'font-lock-function-name-face
- 'font-lock-variable-name-face)
- 'record))))))
-
-(defun js2-parse-plain-property (prop)
- "Parse a non-getter/setter property in an object literal.
-PROP is the node representing the property: a number, name or string."
- (let* ((tt (js2-get-token))
- (pos (js2-node-pos prop))
- colon expr result)
- (cond
- ;; Abbreviated property, as in {foo, bar}
- ((and (>= js2-language-version 200)
- (or (= tt js2-COMMA)
- (= tt js2-RC))
- (not (js2-number-node-p prop)))
- (js2-unget-token)
- (setq result (make-js2-object-prop-node
- :pos pos
- :left prop
- :right prop
- :op-pos (js2-current-token-len)))
- (js2-node-add-children result prop)
- (js2-node-set-prop result 'SHORTHAND t)
- result)
- ;; Normal property
- (t
- (if (= tt js2-COLON)
- (setq colon (- (js2-current-token-beg) pos)
- expr (js2-parse-assign-expr))
- (js2-report-error "msg.no.colon.prop")
- (setq expr (make-js2-error-node)))
- (setq result (make-js2-object-prop-node
- :pos pos
- ;; don't include last consumed token in length
- :len (- (+ (js2-node-pos expr)
- (js2-node-len expr))
- pos)
- :left prop
- :right expr
- :op-pos colon))
- (js2-node-add-children result prop expr)
- result))))
-
-(defun js2-parse-getter-setter-prop (pos prop type-string)
- "Parse getter or setter property in an object literal.
-JavaScript syntax is:
-
- { get foo() {...}, set foo(x) {...} }
-
-and expression closure style is also supported
-
- { get foo() x, set foo(x) _x = x }
-
-POS is the start position of the `get' or `set' keyword.
-PROP is the `js2-name-node' representing the property name.
-GET-P is non-nil if the keyword was `get'."
- (let ((type (cond
- ((string= "get" type-string) js2-GET)
- ((string= "set" type-string) js2-SET)
- (t js2-FUNCTION)))
- result end
- (fn (js2-parse-function-expr)))
- ;; 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")
- (if (cl-plusp (length (js2-function-name fn)))
- (js2-report-error "msg.bad.prop")))
- (js2-node-set-prop fn 'GETTER_SETTER type) ; for codegen
- (setq end (js2-node-end fn)
- result (make-js2-getter-setter-node :type type
- :pos pos
- :len (- end pos)
- :left prop
- :right fn))
- (js2-node-add-children result prop fn)
- result))
-
-(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)))
- (if check-activation-p
- (js2-check-activation-name s (or token js2-NAME)))
- name))
-
-;;; Indentation support
-
-;; This indenter is based on Karl Landström's "javascript.el" indenter.
-;; Karl cleverly deduces that the desired indentation level is often a
-;; function of paren/bracket/brace nesting depth, which can be determined
-;; quickly via the built-in `parse-partial-sexp' function. His indenter
-;; then does some equally clever checks to see if we're in the context of a
-;; substatement of a possibly braceless statement keyword such as if, while,
-;; or finally. This approach yields pretty good results.
-
-;; The indenter is often "wrong", however, and needs to be overridden.
-;; The right long-term solution is probably to emulate (or integrate
-;; with) cc-engine, but it's a nontrivial amount of coding. Even when a
-;; parse tree from `js2-parse' is present, which is not true at the
-;; moment the user is typing, computing indentation is still thousands
-;; of lines of code to handle every possible syntactic edge case.
-
-;; In the meantime, the compromise solution is that we offer a "bounce
-;; indenter", configured with `js2-bounce-indent-p', which cycles the
-;; current line indent among various likely guess points. This approach
-;; is far from perfect, but should at least make it slightly easier to
-;; move the line towards its desired indentation when manually
-;; overriding Karl's heuristic nesting guesser.
-
-;; I've made miscellaneous tweaks to Karl's code to handle some Ecma
-;; extensions such as `let' and Array comprehensions. Major kudos to
-;; Karl for coming up with the initial approach, which packs a lot of
-;; punch for so little code.
-
-(defconst js2-possibly-braceless-keywords-re
- (concat "else[ \t]+if\\|for[ \t]+each\\|"
- (regexp-opt '("catch" "do" "else" "finally" "for" "if"
- "try" "while" "with" "let")))
- "Regular expression matching keywords that are optionally
-followed by an opening brace.")
-
-(defconst js2-indent-operator-re
- (concat "[-+*/%<>&^|?:.]\\([^-+*/]\\|$\\)\\|!?=\\|"
- (regexp-opt '("in" "instanceof") 'words))
- "Regular expression matching operators that affect indentation
-of continued expressions.")
-
-(defconst js2-declaration-keyword-re
- (regexp-opt '("var" "let" "const") 'words)
- "Regular expression matching variable declaration keywords.")
-
-(defun js2-re-search-forward-inner (regexp &optional bound count)
- "Auxiliary function for `js2-re-search-forward'."
- (let (parse saved-point)
- (while (> count 0)
- (re-search-forward regexp bound)
- (setq parse (if saved-point
- (parse-partial-sexp saved-point (point))
- (syntax-ppss (point))))
- (cond ((nth 3 parse)
- (re-search-forward
- (concat "\\(\\=\\|[^\\]\\|^\\)" (string (nth 3 parse)))
- (save-excursion (end-of-line) (point)) t))
- ((nth 7 parse)
- (forward-line))
- ((or (nth 4 parse)
- (and (eq (char-before) ?\/) (eq (char-after) ?\*)))
- (re-search-forward "\\*/"))
- (t
- (setq count (1- count))))
- (setq saved-point (point))))
- (point))
-
-(defun js2-re-search-forward (regexp &optional bound noerror count)
- "Search forward but ignore strings and comments.
-Invokes `re-search-forward' but treats the buffer as if strings
-and comments have been removed."
- (let ((saved-point (point)))
- (condition-case err
- (cond ((null count)
- (js2-re-search-forward-inner regexp bound 1))
- ((< count 0)
- (js2-re-search-backward-inner regexp bound (- count)))
- ((> count 0)
- (js2-re-search-forward-inner regexp bound count)))
- (search-failed
- (goto-char saved-point)
- (unless noerror
- (error (error-message-string err)))))))
-
-(defun js2-re-search-backward-inner (regexp &optional bound count)
- "Auxiliary function for `js2-re-search-backward'."
- (let (parse)
- (while (> count 0)
- (re-search-backward regexp bound)
- (setq parse (syntax-ppss (point)))
- (cond ((nth 3 parse)
- (re-search-backward
- (concat "\\([^\\]\\|^\\)" (string (nth 3 parse)))
- (line-beginning-position) t))
- ((nth 7 parse)
- (goto-char (nth 8 parse)))
- ((or (nth 4 parse)
- (and (eq (char-before) ?/) (eq (char-after) ?*)))
- (re-search-backward "/\\*"))
- (t
- (setq count (1- count))))))
- (point))
-
-(defun js2-re-search-backward (regexp &optional bound noerror count)
- "Search backward but ignore strings and comments.
-Invokes `re-search-backward' but treats the buffer as if strings
-and comments have been removed."
- (let ((saved-point (point)))
- (condition-case err
- (cond ((null count)
- (js2-re-search-backward-inner regexp bound 1))
- ((< count 0)
- (js2-re-search-forward-inner regexp bound (- count)))
- ((> count 0)
- (js2-re-search-backward-inner regexp bound count)))
- (search-failed
- (goto-char saved-point)
- (unless noerror
- (error (error-message-string err)))))))
-
-(defun js2-looking-at-operator-p ()
- "Return non-nil if text after point is a non-comma operator."
- (and (looking-at js2-indent-operator-re)
- (or (not (looking-at ":"))
- (save-excursion
- (and (js2-re-search-backward "[?:{]\\|\\<case\\>" nil t)
- (looking-at "?"))))))
-
-(defun js2-continued-expression-p ()
- "Return non-nil if the current line continues an expression."
- (save-excursion
- (back-to-indentation)
- (or (js2-looking-at-operator-p)
- (when (catch 'found
- (while (and (re-search-backward "\n" nil t)
- (let ((state (syntax-ppss)))
- (when (nth 4 state)
- (goto-char (nth 8 state))) ;; skip comments
- (skip-chars-backward " \t")
- (if (bolp)
- t
- (throw 'found t))))))
- (backward-char)
- (when (js2-looking-at-operator-p)
- (backward-char)
- (not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]")))))))
-
-(defun js2-end-of-do-while-loop-p ()
- "Return non-nil if word after point is `while' of a do-while
-statement, else returns nil. A braceless do-while statement
-spanning several lines requires that the start of the loop is
-indented to the same column as the current line."
- (interactive)
- (save-excursion
- (when (looking-at "\\s-*\\<while\\>")
- (if (save-excursion
- (skip-chars-backward "[ \t\n]*}")
- (looking-at "[ \t\n]*}"))
- (save-excursion
- (backward-list) (backward-word 1) (looking-at "\\<do\\>"))
- (js2-re-search-backward "\\<do\\>" (point-at-bol) t)
- (or (looking-at "\\<do\\>")
- (let ((saved-indent (current-indentation)))
- (while (and (js2-re-search-backward "^[ \t]*\\<" nil t)
- (/= (current-indentation) saved-indent)))
- (and (looking-at "[ \t]*\\<do\\>")
- (not (js2-re-search-forward
- "\\<while\\>" (point-at-eol) t))
- (= (current-indentation) saved-indent))))))))
-
-(defun js2-multiline-decl-indentation ()
- "Return the declaration indentation column if the current line belongs
-to a multiline declaration statement. See
`js2-pretty-multiline-declarations'."
- (let (forward-sexp-function ; use Lisp version
- at-opening-bracket)
- (save-excursion
- (back-to-indentation)
- (when (not (looking-at js2-declaration-keyword-re))
- (when (looking-at js2-indent-operator-re)
- (goto-char (match-end 0))) ; continued expressions are ok
- (while (and (not at-opening-bracket)
- (not (bobp))
- (let ((pos (point)))
- (save-excursion
- (js2-backward-sws)
- (or (eq (char-before) ?,)
- (and (not (eq (char-before) ?\;))
- (prog2 (skip-syntax-backward ".")
- (looking-at js2-indent-operator-re)
- (js2-backward-sws))
- (not (eq (char-before) ?\;)))
- (js2-same-line pos)))))
- (condition-case _
- (backward-sexp)
- (scan-error (setq at-opening-bracket t))))
- (when (looking-at js2-declaration-keyword-re)
- (goto-char (match-end 0))
- (1+ (current-column)))))))
-
-(defun js2-ctrl-statement-indentation ()
- "Return the proper indentation of current line if it is a control statement.
-Returns an indentation if this line starts the body of a control
-statement without braces, else returns nil."
- (let (forward-sexp-function)
- (save-excursion
- (back-to-indentation)
- (when (and (not (js2-same-line (point-min)))
- (not (looking-at "{"))
- (js2-re-search-backward "[[:graph:]]" nil t)
- (not (looking-at "[{([]"))
- (progn
- (forward-char)
- (when (= (char-before) ?\))
- ;; scan-sexps sometimes throws an error
- (ignore-errors (backward-sexp))
- (skip-chars-backward " \t" (point-at-bol)))
- (let ((pt (point)))
- (back-to-indentation)
- (when (looking-at "}[ \t]*")
- (goto-char (match-end 0)))
- (and (looking-at js2-possibly-braceless-keywords-re)
- (= (match-end 0) pt)
- (not (js2-end-of-do-while-loop-p))))))
- (+ (current-indentation) js2-basic-offset)))))
-
-(defun js2-indent-in-array-comp (parse-status)
- "Return non-nil if we think we're in an array comprehension.
-In particular, return the buffer position of the first `for' kwd."
- (let ((bracket (nth 1 parse-status))
- (end (point)))
- (when bracket
- (save-excursion
- (goto-char bracket)
- (when (looking-at "\\[")
- (forward-char 1)
- (js2-forward-sws)
- (if (looking-at "[[{]")
- (let (forward-sexp-function) ; use Lisp version
- (forward-sexp) ; skip destructuring form
- (js2-forward-sws)
- (if (and (/= (char-after) ?,) ; regular array
- (looking-at "for"))
- (match-beginning 0)))
- ;; to skip arbitrary expressions we need the parser,
- ;; so we'll just guess at it.
- (if (and (> end (point)) ; not empty literal
- (re-search-forward "[^,]]* \\(for\\) " end t)
- ;; not inside comment or string literal
- (let ((state (parse-partial-sexp bracket (point))))
- (not (or (nth 3 state) (nth 4 state)))))
- (match-beginning 1))))))))
-
-(defun js2-array-comp-indentation (parse-status for-kwd)
- (if (js2-same-line for-kwd)
- ;; first continuation line
- (save-excursion
- (goto-char (nth 1 parse-status))
- (forward-char 1)
- (skip-chars-forward " \t")
- (current-column))
- (save-excursion
- (goto-char for-kwd)
- (current-column))))
-
-(defun js2-proper-indentation (parse-status)
- "Return the proper indentation for the current line."
- (save-excursion
- (back-to-indentation)
- (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
- (>= js2-language-version 170)
- (not (js2-same-line bracket))
- (setq beg (js2-indent-in-array-comp parse-status))
- (>= (point) (save-excursion
- (goto-char beg)
- (point-at-bol)))) ; at or after first loop?
- (js2-array-comp-indentation parse-status beg))
-
- (ctrl-stmt-indent)
-
- ((and declaration-indent continued-expr-p)
- (+ declaration-indent js2-basic-offset))
-
- (declaration-indent)
-
- (bracket
- (goto-char bracket)
- (cond
- ((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)")
- (when (save-excursion (skip-chars-backward " \t)")
- (looking-at ")"))
- (backward-list))
- (back-to-indentation)
- (and (eq js2-pretty-multiline-declarations 'all)
- (looking-at js2-declaration-keyword-re)
- (goto-char (1+ (match-end 0))))
- (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)
- (skip-chars-forward " \t"))
- (current-column))))
-
- (continued-expr-p js2-basic-offset)
-
- (t 0)))))
-
-(defun js2-lineup-comment (parse-status)
- "Indent a multi-line block comment continuation line."
- (let* ((beg (nth 8 parse-status))
- (first-line (js2-same-line beg))
- (offset (save-excursion
- (goto-char beg)
- (if (looking-at "/\\*")
- (+ 1 (current-column))
- 0))))
- (unless first-line
- (indent-line-to offset))))
-
-(defun js2-backward-sws ()
- "Move backward through whitespace and comments."
- (interactive)
- (while (forward-comment -1)))
-
-(defun js2-forward-sws ()
- "Move forward through whitespace and comments."
- (interactive)
- (while (forward-comment 1)))
-
-(defun js2-current-indent (&optional pos)
- "Return column of indentation on current line.
-If POS is non-nil, go to that point and return indentation for that line."
- (save-excursion
- (if pos
- (goto-char pos))
- (back-to-indentation)
- (current-column)))
-
-(defun js2-arglist-close ()
- "Return non-nil if we're on a line beginning with a close-paren/brace."
- (save-excursion
- (goto-char (point-at-bol))
- (js2-forward-sws)
- (looking-at "[])}]")))
-
-(defun js2-indent-looks-like-label-p ()
- (goto-char (point-at-bol))
- (js2-forward-sws)
- (looking-at (concat js2-mode-identifier-re ":")))
-
-(defun js2-indent-in-objlit-p (parse-status)
- "Return non-nil if this looks like an object-literal entry."
- (let ((start (nth 1 parse-status)))
- (and
- start
- (save-excursion
- (and (zerop (forward-line -1))
- (not (< (point) start)) ; crossed a {} boundary
- (js2-indent-looks-like-label-p)))
- (save-excursion
- (js2-indent-looks-like-label-p)))))
-
-;; If prev line looks like foobar({ then we're passing an object
-;; literal to a function call, and people pretty much always want to
-;; de-dent back to the previous line, so move the 'basic-offset'
-;; position to the front.
-(defun js2-indent-objlit-arg-p (parse-status)
- (save-excursion
- (back-to-indentation)
- (js2-backward-sws)
- (and (eq (1- (point)) (nth 1 parse-status))
- (eq (char-before) ?{)
- (progn
- (forward-char -1)
- (skip-chars-backward " \t")
- (eq (char-before) ?\()))))
-
-(defun js2-indent-case-block-p ()
- (save-excursion
- (back-to-indentation)
- (js2-backward-sws)
- (goto-char (point-at-bol))
- (skip-chars-forward " \t")
- (looking-at "case\\s-.+:")))
-
-(defun js2-bounce-indent (normal-col parse-status &optional backwards)
- "Cycle among alternate computed indentation positions.
-PARSE-STATUS is the result of `parse-partial-sexp' from the beginning
-of the buffer to the current point. NORMAL-COL is the indentation
-column computed by the heuristic guesser based on current paren,
-bracket, brace and statement nesting. If BACKWARDS, cycle positions
-in reverse."
- (let ((cur-indent (js2-current-indent))
- (old-buffer-undo-list buffer-undo-list)
- ;; Emacs 21 only has `count-lines', not `line-number-at-pos'
- (current-line (save-excursion
- (forward-line 0) ; move to bol
- (1+ (count-lines (point-min) (point)))))
- positions pos main-pos anchor arglist-cont same-indent
- basic-offset computed-pos)
- ;; temporarily don't record undo info, if user requested this
- (when js2-mode-indent-inhibit-undo
- (setq buffer-undo-list t))
- (unwind-protect
- (progn
- ;; First likely point: indent from beginning of previous code line
- (push (setq basic-offset
- (+ (save-excursion
- (back-to-indentation)
- (js2-backward-sws)
- (back-to-indentation)
- (current-column))
- js2-basic-offset))
- positions)
-
- ;; (First + epsilon) likely point: indent 2x from beginning of
- ;; previous code line. Google does it this way.
- (push (setq basic-offset
- (+ (save-excursion
- (back-to-indentation)
- (js2-backward-sws)
- (back-to-indentation)
- (current-column))
- (* 2 js2-basic-offset)))
- positions)
-
- ;; Second likely point: indent from assign-expr RHS. This
- ;; is just a crude guess based on finding " = " on the previous
- ;; line containing actual code.
- (setq pos (save-excursion
- (forward-line -1)
- (goto-char (point-at-bol))
- (when (re-search-forward "\\s-+\\(=\\)\\s-+"
- (point-at-eol) t)
- (goto-char (match-end 1))
- (skip-chars-forward " \t\r\n")
- (current-column))))
- (when pos
- (cl-incf pos js2-basic-offset)
- (push pos positions))
-
- ;; Third likely point: same indent as previous line of code.
- ;; Make it the first likely point if we're not on an
- ;; arglist-close line and previous line ends in a comma, or
- ;; both this line and prev line look like object-literal
- ;; elements.
- (setq pos (save-excursion
- (goto-char (point-at-bol))
- (js2-backward-sws)
- (back-to-indentation)
- (prog1
- (current-column)
- ;; while we're here, look for trailing comma
- (if (save-excursion
- (goto-char (point-at-eol))
- (js2-backward-sws)
- (eq (char-before) ?,))
- (setq arglist-cont (1- (point)))))))
- (when pos
- (if (and (or arglist-cont
- (js2-indent-in-objlit-p parse-status))
- (not (js2-arglist-close)))
- (setq same-indent pos))
- (push pos positions))
-
- ;; Fourth likely point: first preceding code with less indentation.
- ;; than the immediately preceding code line.
- (setq pos (save-excursion
- (back-to-indentation)
- (js2-backward-sws)
- (back-to-indentation)
- (setq anchor (current-column))
- (while (and (zerop (forward-line -1))
- (>= (progn
- (back-to-indentation)
- (current-column))
- anchor)))
- (setq pos (current-column))))
- (push pos positions)
-
- ;; nesting-heuristic position, main by default
- (push (setq main-pos normal-col) positions)
-
- ;; delete duplicates and sort positions list
- (setq positions (sort (delete-dups positions) '<))
-
- ;; comma-list continuation lines: prev line indent takes precedence
- (if same-indent
- (setq main-pos same-indent))
-
- ;; common special cases where we want to indent in from previous line
- (if (or (js2-indent-case-block-p)
- (js2-indent-objlit-arg-p parse-status))
- (setq main-pos basic-offset))
-
- ;; if bouncing backwards, reverse positions list
- (if backwards
- (setq positions (reverse positions)))
-
- ;; record whether we're already sitting on one of the alternatives
- (setq pos (member cur-indent positions))
-
- (cond
- ;; case 0: we're one one of the alternatives and this is the
- ;; first time they've pressed TAB on this line (best-guess).
- ((and js2-mode-indent-ignore-first-tab
- pos
- ;; first time pressing TAB on this line?
- (not (eq js2-mode-last-indented-line current-line)))
- ;; do nothing
- (setq computed-pos nil))
- ;; case 1: only one computed position => use it
- ((null (cdr positions))
- (setq computed-pos 0))
- ;; case 2: not on any of the computed spots => use main spot
- ((not pos)
- (setq computed-pos (js2-position main-pos positions)))
- ;; case 3: on last position: cycle to first position
- ((null (cdr pos))
- (setq computed-pos 0))
- ;; case 4: on intermediate position: cycle to next position
- (t
- (setq computed-pos (js2-position (cl-second pos) positions))))
-
- ;; see if any hooks want to indent; otherwise we do it
- (cl-loop with result = nil
- for hook in js2-indent-hook
- while (null result)
- do
- (setq result (funcall hook positions computed-pos))
- finally do
- (unless (or result (null computed-pos))
- (indent-line-to (nth computed-pos positions)))))
-
- ;; finally
- (if js2-mode-indent-inhibit-undo
- (setq buffer-undo-list old-buffer-undo-list))
- ;; see commentary for `js2-mode-last-indented-line'
- (setq js2-mode-last-indented-line current-line))))
-
-(defun js2-indent-bounce-backwards ()
- "Calls `js2-indent-line'. When `js2-bounce-indent-p',
-cycles between the computed indentation positions in reverse order."
- (interactive)
- (js2-indent-line t))
-
-(defun js2-1-line-comment-continuation-p ()
- "Return t if we're in a 1-line comment continuation.
-If so, we don't ever want to use bounce-indent."
- (save-excursion
- (and (progn
- (forward-line 0)
- (looking-at "\\s-*//"))
- (progn
- (forward-line -1)
- (forward-line 0)
- (when (looking-at "\\s-*$")
- (js2-backward-sws)
- (forward-line 0))
- (looking-at "\\s-*//")))))
-
-(defun js2-indent-line (&optional bounce-backwards)
- "Indent the current line as JavaScript source text."
- (interactive)
- (let (parse-status offset indent-col
- ;; Don't whine about errors/warnings when we're indenting.
- ;; This has to be set before calling parse-partial-sexp below.
- (inhibit-point-motion-hooks t))
- (setq parse-status (save-excursion
- (syntax-ppss (point-at-bol)))
- offset (- (point) (save-excursion
- (back-to-indentation)
- (point))))
- (js2-with-underscore-as-word-syntax
- (if (nth 4 parse-status)
- (js2-lineup-comment parse-status)
- (setq indent-col (js2-proper-indentation parse-status))
- ;; See comments below about `js2-mode-last-indented-line'.
- (cond
- ;; bounce-indenting is disabled during electric-key indent.
- ;; It doesn't work well on first line of buffer.
- ((and js2-bounce-indent-p
- (not (js2-same-line (point-min)))
- (not (js2-1-line-comment-continuation-p)))
- (js2-bounce-indent indent-col parse-status bounce-backwards))
- ;; just indent to the guesser's likely spot
- (t (indent-line-to indent-col))))
- (when (cl-plusp offset)
- (forward-char offset)))))
-
-(defun js2-indent-region (start end)
- "Indent the region, but don't use bounce indenting."
- (let ((js2-bounce-indent-p nil)
- (indent-region-function nil)
- (after-change-functions (remq 'js2-mode-edit
- after-change-functions)))
- (indent-region start end nil) ; nil for byte-compiler
- (js2-mode-edit start end (- end start))))
-
-(defvar js2-minor-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map (kbd "C-c C-`") #'js2-next-error)
- (define-key map [mouse-1] #'js2-mode-show-node)
- map)
- "Keymap used when `js2-minor-mode' is active.")
-
-;;;###autoload
-(define-minor-mode js2-minor-mode
- "Minor mode for running js2 as a background linter.
-This allows you to use a different major mode for JavaScript editing,
-such as `js-mode', while retaining the asynchronous error/warning
-highlighting features of `js2-mode'."
- :group 'js2-mode
- :lighter " js-lint"
- (if js2-minor-mode
- (js2-minor-mode-enter)
- (js2-minor-mode-exit)))
-
-(defun js2-minor-mode-enter ()
- "Initialization for `js2-minor-mode'."
- (set (make-local-variable 'max-lisp-eval-depth)
- (max max-lisp-eval-depth 3000))
- (setq next-error-function #'js2-next-error)
- (js2-set-default-externs)
- ;; Experiment: make reparse-delay longer for longer files.
- (if (cl-plusp js2-dynamic-idle-timer-adjust)
- (setq js2-idle-timer-delay
- (* js2-idle-timer-delay
- (/ (point-max) js2-dynamic-idle-timer-adjust))))
- (setq js2-mode-buffer-dirty-p t
- js2-mode-parsing nil)
- (set (make-local-variable 'js2-highlight-level) 0) ; no syntax highlighting
- (add-hook 'after-change-functions #'js2-minor-mode-edit nil t)
- (add-hook 'change-major-mode-hook #'js2-minor-mode-exit nil t)
- (when js2-include-jslint-globals
- (add-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals nil t))
- (run-hooks 'js2-init-hook)
- (js2-reparse))
-
-(defun js2-minor-mode-exit ()
- "Turn off `js2-minor-mode'."
- (setq next-error-function nil)
- (remove-hook 'after-change-functions #'js2-mode-edit t)
- (remove-hook 'change-major-mode-hook #'js2-minor-mode-exit t)
- (when js2-mode-node-overlay
- (delete-overlay js2-mode-node-overlay)
- (setq js2-mode-node-overlay nil))
- (js2-remove-overlays)
- (remove-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals t)
- (setq js2-mode-ast nil))
-
-(defvar js2-source-buffer nil "Linked source buffer for diagnostics view")
-(make-variable-buffer-local 'js2-source-buffer)
-
-(cl-defun js2-display-error-list ()
- "Display a navigable buffer listing parse errors/warnings."
- (interactive)
- (unless (js2-have-errors-p)
- (message "No errors")
- (cl-return-from js2-display-error-list))
- (cl-labels ((annotate-list
- (lst type)
- "Add diagnostic TYPE and line number to errs list"
- (mapcar (lambda (err)
- (list err type (line-number-at-pos (nth 1 err))))
- lst)))
- (let* ((srcbuf (current-buffer))
- (errbuf (get-buffer-create "*js-lint*"))
- (errors (annotate-list
- (when js2-mode-ast (js2-ast-root-errors js2-mode-ast))
- 'js2-error)) ; must be a valid face name
- (warnings (annotate-list
- (when js2-mode-ast (js2-ast-root-warnings js2-mode-ast))
- 'js2-warning)) ; must be a valid face name
- (all-errs (sort (append errors warnings)
- (lambda (e1 e2) (< (cl-cadar e1) (cl-cadar e2))))))
- (with-current-buffer errbuf
- (let ((inhibit-read-only t))
- (erase-buffer)
- (dolist (err all-errs)
- (cl-destructuring-bind ((msg-key beg _end &rest) type line) err
- (insert-text-button
- (format "line %d: %s" line (js2-get-msg msg-key))
- 'face type
- 'follow-link "\C-m"
- 'action 'js2-error-buffer-jump
- 'js2-msg (js2-get-msg msg-key)
- 'js2-pos beg)
- (insert "\n"))))
- (js2-error-buffer-mode)
- (setq js2-source-buffer srcbuf)
- (pop-to-buffer errbuf)
- (goto-char (point-min))
- (unless (eobp)
- (js2-error-buffer-view))))))
-
-(defvar js2-error-buffer-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map "n" #'js2-error-buffer-next)
- (define-key map "p" #'js2-error-buffer-prev)
- (define-key map (kbd "RET") #'js2-error-buffer-jump)
- (define-key map "o" #'js2-error-buffer-view)
- (define-key map "q" #'js2-error-buffer-quit)
- map)
- "Keymap used for js2 diagnostics buffers.")
-
-(defun js2-error-buffer-mode ()
- "Major mode for js2 diagnostics buffers.
-Selecting an error will jump it to the corresponding source-buffer error.
-\\{js2-error-buffer-mode-map}"
- (interactive)
- (setq major-mode 'js2-error-buffer-mode
- mode-name "JS Lint Diagnostics")
- (use-local-map js2-error-buffer-mode-map)
- (setq truncate-lines t)
- (set-buffer-modified-p nil)
- (setq buffer-read-only t)
- (run-hooks 'js2-error-buffer-mode-hook))
-
-(defun js2-error-buffer-next ()
- "Move to next error and view it."
- (interactive)
- (when (zerop (forward-line 1))
- (js2-error-buffer-view)))
-
-(defun js2-error-buffer-prev ()
- "Move to previous error and view it."
- (interactive)
- (when (zerop (forward-line -1))
- (js2-error-buffer-view)))
-
-(defun js2-error-buffer-quit ()
- "Kill the current buffer."
- (interactive)
- (kill-buffer))
-
-(defun js2-error-buffer-jump (&rest ignored)
- "Jump cursor to current error in source buffer."
- (interactive)
- (when (js2-error-buffer-view)
- (pop-to-buffer js2-source-buffer)))
-
-(defun js2-error-buffer-view ()
- "Scroll source buffer to show error at current line."
- (interactive)
- (cond
- ((not (eq major-mode 'js2-error-buffer-mode))
- (message "Not in a js2 errors buffer"))
- ((not (buffer-live-p js2-source-buffer))
- (message "Source buffer has been killed"))
- ((not (wholenump (get-text-property (point) 'js2-pos)))
- (message "There does not seem to be an error here"))
- (t
- (let ((pos (get-text-property (point) 'js2-pos))
- (msg (get-text-property (point) 'js2-msg)))
- (save-selected-window
- (pop-to-buffer js2-source-buffer)
- (goto-char pos)
- (message msg))))))
-
-;;;###autoload
-(define-derived-mode js2-mode prog-mode "Javascript-IDE"
- ;; FIXME: Should derive from js-mode.
- "Major mode for editing JavaScript code."
- ;; Used by comment-region; don't change it.
- (set (make-local-variable 'comment-start) "//")
- (set (make-local-variable 'comment-end) "")
- (set (make-local-variable 'comment-start-skip) js2-comment-start-skip)
- (set (make-local-variable 'max-lisp-eval-depth)
- (max max-lisp-eval-depth 3000))
- (set (make-local-variable 'indent-line-function) #'js2-indent-line)
- (set (make-local-variable 'indent-region-function) #'js2-indent-region)
- (set (make-local-variable 'fill-paragraph-function) #'c-fill-paragraph)
- (set (make-local-variable 'comment-line-break-function) #'js2-line-break)
- (set (make-local-variable 'beginning-of-defun-function)
#'js2-beginning-of-defun)
- (set (make-local-variable 'end-of-defun-function) #'js2-end-of-defun)
- ;; We un-confuse `parse-partial-sexp' by setting syntax-table properties
- ;; for characters inside regexp literals.
- (set (make-local-variable 'parse-sexp-lookup-properties) t)
- ;; this is necessary to make `show-paren-function' work properly
- (set (make-local-variable 'parse-sexp-ignore-comments) t)
- ;; needed for M-x rgrep, among other things
- (put 'js2-mode 'find-tag-default-function #'js2-mode-find-tag)
-
- (set (make-local-variable 'electric-indent-chars)
- (append "{}()[]:;,*." electric-indent-chars))
- (set (make-local-variable 'electric-layout-rules)
- '((?\; . after) (?\{ . after) (?\} . before)))
-
- ;; some variables needed by cc-engine for paragraph-fill, etc.
- (setq c-comment-prefix-regexp js2-comment-prefix-regexp
- c-comment-start-regexp "/[*/]\\|\\s|"
- c-line-comment-starter "//"
- c-paragraph-start js2-paragraph-start
- c-paragraph-separate "$"
- c-syntactic-ws-start js2-syntactic-ws-start
- c-syntactic-ws-end js2-syntactic-ws-end
- c-syntactic-eol js2-syntactic-eol)
-
- (let ((c-buffer-is-cc-mode t))
- ;; Copied from `js-mode'. Also see Bug#6071.
- (make-local-variable 'paragraph-start)
- (make-local-variable 'paragraph-separate)
- (make-local-variable 'paragraph-ignore-fill-prefix)
- (make-local-variable 'adaptive-fill-mode)
- (make-local-variable 'adaptive-fill-regexp)
- (c-setup-paragraph-variables))
-
- (setq font-lock-defaults '(nil t))
-
- ;; Experiment: make reparse-delay longer for longer files.
- (when (cl-plusp js2-dynamic-idle-timer-adjust)
- (setq js2-idle-timer-delay
- (* js2-idle-timer-delay
- (/ (point-max) js2-dynamic-idle-timer-adjust))))
-
- (add-hook 'change-major-mode-hook #'js2-mode-exit nil t)
- (add-hook 'after-change-functions #'js2-mode-edit nil t)
- (setq imenu-create-index-function #'js2-mode-create-imenu-index)
- (setq next-error-function #'js2-next-error)
- (imenu-add-to-menubar (concat "IM-" mode-name))
- (add-to-invisibility-spec '(js2-outline . t))
- (set (make-local-variable 'line-move-ignore-invisible) t)
- (set (make-local-variable 'forward-sexp-function) #'js2-mode-forward-sexp)
-
- (setq js2-mode-functions-hidden nil
- js2-mode-comments-hidden nil
- js2-mode-buffer-dirty-p t
- js2-mode-parsing nil)
-
- (js2-set-default-externs)
-
- (when js2-include-jslint-globals
- (add-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals nil t))
-
- (run-hooks 'js2-init-hook)
-
- (js2-reparse))
-
-(defun js2-mode-exit ()
- "Exit `js2-mode' and clean up."
- (interactive)
- (when js2-mode-node-overlay
- (delete-overlay js2-mode-node-overlay)
- (setq js2-mode-node-overlay nil))
- (js2-remove-overlays)
- (setq js2-mode-ast nil)
- (remove-hook 'change-major-mode-hook #'js2-mode-exit t)
- (remove-from-invisibility-spec '(js2-outline . t))
- (js2-mode-show-all)
- (with-silent-modifications
- (js2-clear-face (point-min) (point-max))))
-
-(defun js2-mode-reset-timer ()
- "Cancel any existing parse timer and schedule a new one."
- (if js2-mode-parse-timer
- (cancel-timer js2-mode-parse-timer))
- (setq js2-mode-parsing nil)
- (let ((timer (timer-create)))
- (setq js2-mode-parse-timer timer)
- (timer-set-function timer 'js2-mode-idle-reparse (list (current-buffer)))
- (timer-set-idle-time timer js2-idle-timer-delay)
- ;; http://debbugs.gnu.org/cgi/bugreport.cgi?bug=12326
- (timer-activate-when-idle timer nil)))
-
-(defun js2-mode-idle-reparse (buffer)
- "Run `js2-reparse' if BUFFER is the current buffer, or schedule
-it to be reparsed when the buffer is selected."
- (cond ((eq buffer (current-buffer))
- (js2-reparse))
- ((buffer-live-p buffer)
- ;; reparse when the buffer is selected again
- (with-current-buffer buffer
- (add-hook 'window-configuration-change-hook
- #'js2-mode-idle-reparse-inner
- nil t)))))
-
-(defun js2-mode-idle-reparse-inner ()
- (remove-hook 'window-configuration-change-hook
- #'js2-mode-idle-reparse-inner
- t)
- (js2-reparse))
-
-(defun js2-mode-edit (_beg _end _len)
- "Schedule a new parse after buffer is edited.
-Buffer edit spans from BEG to END and is of length LEN."
- (setq js2-mode-buffer-dirty-p t)
- (js2-mode-hide-overlay)
- (js2-mode-reset-timer))
-
-(defun js2-minor-mode-edit (_beg _end _len)
- "Callback for buffer edits in `js2-mode'.
-Schedules a new parse after buffer is edited.
-Buffer edit spans from BEG to END and is of length LEN."
- (setq js2-mode-buffer-dirty-p t)
- (js2-mode-hide-overlay)
- (js2-mode-reset-timer))
-
-(defun js2-reparse (&optional force)
- "Re-parse current buffer after user finishes some data entry.
-If we get any user input while parsing, including cursor motion,
-we discard the parse and reschedule it. If FORCE is nil, then the
-buffer will only rebuild its `js2-mode-ast' if the buffer is dirty."
- (let (time
- interrupted-p
- (js2-compiler-strict-mode js2-mode-show-strict-warnings))
- (unless js2-mode-parsing
- (setq js2-mode-parsing t)
- (unwind-protect
- (when (or js2-mode-buffer-dirty-p force)
- (js2-remove-overlays)
- (setq js2-mode-buffer-dirty-p nil
- js2-mode-fontifications nil
- js2-mode-deferred-properties nil)
- (if js2-mode-verbose-parse-p
- (message "parsing..."))
- (setq time
- (js2-time
- (setq interrupted-p
- (catch 'interrupted
- (js2-parse)
- (with-silent-modifications
- ;; if parsing is interrupted, comments and regex
- ;; literals stay ignored by `parse-partial-sexp'
- (remove-text-properties (point-min) (point-max)
- '(syntax-table))
- (js2-mode-apply-deferred-properties)
- (js2-mode-remove-suppressed-warnings)
- (js2-mode-show-warnings)
- (js2-mode-show-errors)
- (if (>= js2-highlight-level 1)
- (js2-highlight-jsdoc js2-mode-ast)))
- nil))))
- (if interrupted-p
- (progn
- ;; unfinished parse => try again
- (setq js2-mode-buffer-dirty-p t)
- (js2-mode-reset-timer))
- (if js2-mode-verbose-parse-p
- (message "Parse time: %s" time))))
- (setq js2-mode-parsing nil)
- (unless interrupted-p
- (setq js2-mode-parse-timer nil))))))
-
-(defun js2-mode-show-node (event)
- "Debugging aid: highlight selected AST node on mouse click."
- (interactive "e")
- (mouse-set-point event)
- (setq deactivate-mark t)
- (when js2-mode-show-overlay
- (let ((node (js2-node-at-point))
- beg end)
- (if (null node)
- (message "No node found at location %s" (point))
- (setq beg (js2-node-abs-pos node)
- end (+ beg (js2-node-len node)))
- (if js2-mode-node-overlay
- (move-overlay js2-mode-node-overlay beg end)
- (setq js2-mode-node-overlay (make-overlay beg end))
- (overlay-put js2-mode-node-overlay 'font-lock-face 'highlight))
- (with-silent-modifications
- (put-text-property beg end 'point-left #'js2-mode-hide-overlay))
- (message "%s, parent: %s"
- (js2-node-short-name node)
- (if (js2-node-parent node)
- (js2-node-short-name (js2-node-parent node))
- "nil"))))))
-
-(defun js2-mode-hide-overlay (&optional _p1 p2)
- "Remove the debugging overlay when the point moves.
-P1 and P2 are the old and new values of point, respectively."
- (when js2-mode-node-overlay
- (let ((beg (overlay-start js2-mode-node-overlay))
- (end (overlay-end js2-mode-node-overlay)))
- ;; Sometimes we're called spuriously.
- (unless (and p2
- (>= p2 beg)
- (<= p2 end))
- (with-silent-modifications
- (remove-text-properties beg end '(point-left nil)))
- (delete-overlay js2-mode-node-overlay)
- (setq js2-mode-node-overlay nil)))))
-
-(defun js2-mode-reset ()
- "Debugging helper: reset everything."
- (interactive)
- (js2-mode-exit)
- (js2-mode))
-
-(defun js2-mode-show-warn-or-err (e face)
- "Highlight a warning or error E with FACE.
-E is a list of ((MSG-KEY MSG-ARG) BEG LEN OVERRIDE-FACE).
-The last element is optional. When present, use instead of FACE."
- (let* ((key (cl-first e))
- (beg (cl-second e))
- (end (+ beg (cl-third e)))
- ;; Don't inadvertently go out of bounds.
- (beg (max (point-min) (min beg (point-max))))
- (end (max (point-min) (min end (point-max))))
- (ovl (make-overlay beg end)))
- (overlay-put ovl 'font-lock-face (or (cl-fourth e) face))
- (overlay-put ovl 'js2-error t)
- (put-text-property beg end 'help-echo (js2-get-msg key))
- (put-text-property beg end 'point-entered #'js2-echo-error)))
-
-(defun js2-remove-overlays ()
- "Remove overlays from buffer that have a `js2-error' property."
- (let ((beg (point-min))
- (end (point-max)))
- (save-excursion
- (dolist (o (overlays-in beg end))
- (when (overlay-get o 'js2-error)
- (delete-overlay o))))))
-
-(defun js2-error-at-point (&optional pos)
- "Return non-nil if there's an error overlay at POS.
-Defaults to point."
- (cl-loop with pos = (or pos (point))
- for o in (overlays-at pos)
- thereis (overlay-get o 'js2-error)))
-
-(defun js2-mode-apply-deferred-properties ()
- "Apply fontifications and other text properties recorded during parsing."
- (when (cl-plusp js2-highlight-level)
- ;; We defer clearing faces as long as possible to eliminate flashing.
- (js2-clear-face (point-min) (point-max))
- ;; Have to reverse the recorded fontifications list so that errors
- ;; and warnings overwrite the normal fontifications.
- (dolist (f (nreverse js2-mode-fontifications))
- (put-text-property (cl-first f) (cl-second f) 'font-lock-face (cl-third
f)))
- (setq js2-mode-fontifications nil))
- (dolist (p js2-mode-deferred-properties)
- (apply #'put-text-property p))
- (setq js2-mode-deferred-properties nil))
-
-(defun js2-mode-show-errors ()
- "Highlight syntax errors."
- (when js2-mode-show-parse-errors
- (dolist (e (js2-ast-root-errors js2-mode-ast))
- (js2-mode-show-warn-or-err e 'js2-error))))
-
-(defun js2-mode-remove-suppressed-warnings ()
- "Take suppressed warnings out of the AST warnings list.
-This ensures that the counts and `next-error' are correct."
- (setf (js2-ast-root-warnings js2-mode-ast)
- (js2-delete-if
- (lambda (e)
- (let ((key (caar e)))
- (or
- (and (not js2-strict-trailing-comma-warning)
- (string-match "trailing\\.comma" key))
- (and (not js2-strict-cond-assign-warning)
- (string= key "msg.equal.as.assign"))
- (and js2-missing-semi-one-line-override
- (string= key "msg.missing.semi")
- (let* ((beg (cl-second e))
- (node (js2-node-at-point beg))
- (fn (js2-mode-find-parent-fn node))
- (body (and fn (js2-function-node-body fn)))
- (lc (and body (js2-node-abs-pos body)))
- (rc (and lc (+ lc (js2-node-len body)))))
- (and fn
- (or (null body)
- (save-excursion
- (goto-char beg)
- (and (js2-same-line lc)
- (js2-same-line rc))))))))))
- (js2-ast-root-warnings js2-mode-ast))))
-
-(defun js2-mode-show-warnings ()
- "Highlight strict-mode warnings."
- (when js2-mode-show-strict-warnings
- (dolist (e (js2-ast-root-warnings js2-mode-ast))
- (js2-mode-show-warn-or-err e 'js2-warning))))
-
-(defun js2-echo-error (_old-point new-point)
- "Called by point-motion hooks."
- (let ((msg (get-text-property new-point 'help-echo)))
- (when (and (stringp msg)
- (not (active-minibuffer-window))
- (not (current-message)))
- (message msg))))
-
-(defalias 'js2-echo-help #'js2-echo-error)
-
-(defun js2-line-break (&optional _soft)
- "Break line at point and indent, continuing comment if within one.
-If inside a string, and `js2-concat-multiline-strings' is not
-nil, turn it into concatenation."
- (interactive)
- (let ((parse-status (syntax-ppss)))
- (cond
- ;; Check if we're inside a string.
- ((nth 3 parse-status)
- (if js2-concat-multiline-strings
- (js2-mode-split-string parse-status)
- (insert "\n")))
- ;; Check if inside a block comment.
- ((nth 4 parse-status)
- (js2-mode-extend-comment (nth 8 parse-status)))
- (t
- (newline-and-indent)))))
-
-(defun js2-mode-split-string (parse-status)
- "Turn a newline in mid-string into a string concatenation.
-PARSE-STATUS is as documented in `parse-partial-sexp'."
- (let* ((quote-char (nth 3 parse-status))
- (at-eol (eq js2-concat-multiline-strings 'eol)))
- (insert quote-char)
- (insert (if at-eol " +\n" "\n"))
- (unless at-eol
- (insert "+ "))
- (js2-indent-line)
- (insert quote-char)
- (when (eolp)
- (insert quote-char)
- (backward-char 1))))
-
-(defun js2-mode-extend-comment (start-pos)
- "Indent the line and, when inside a comment block, add comment prefix."
- (let (star single col first-line needs-close)
- (save-excursion
- (back-to-indentation)
- (when (< (point) start-pos)
- (goto-char start-pos))
- (cond
- ((looking-at "\\*[^/]")
- (setq star t
- col (current-column)))
- ((looking-at "/\\*")
- (setq star t
- first-line t
- col (1+ (current-column))))
- ((looking-at "//")
- (setq single t
- col (current-column)))))
- ;; Heuristic for whether we need to close the comment:
- ;; if we've got a parse error here, assume it's an unterminated
- ;; comment.
- (setq needs-close
- (or
- (eq (get-text-property (1- (point)) 'point-entered)
- 'js2-echo-error)
- ;; The heuristic above doesn't work well when we're
- ;; creating a comment and there's another one downstream,
- ;; as our parser thinks this one ends at the end of the
- ;; next one. (You can have a /* inside a js block comment.)
- ;; So just close it if the next non-ws char isn't a *.
- (and first-line
- (eolp)
- (save-excursion
- (skip-chars-forward " \t\r\n")
- (not (eq (char-after) ?*))))))
- (delete-horizontal-space)
- (insert "\n")
- (cond
- (star
- (indent-to col)
- (insert "* ")
- (if (and first-line needs-close)
- (save-excursion
- (insert "\n")
- (indent-to col)
- (insert "*/"))))
- ((and single
- (save-excursion
- (and (zerop (forward-line 1))
- (looking-at "\\s-*//"))))
- (indent-to col)
- (insert "// ")))
- ;; Don't need to extend the comment after all.
- (js2-indent-line)))
-
-(defun js2-beginning-of-line ()
- "Toggle point between bol and first non-whitespace char in line.
-Also moves past comment delimiters when inside comments."
- (interactive)
- (let (node)
- (cond
- ((bolp)
- (back-to-indentation))
- ((looking-at "//")
- (skip-chars-forward "/ \t"))
- ((and (eq (char-after) ?*)
- (setq node (js2-comment-at-point))
- (memq (js2-comment-node-format node) '(jsdoc block))
- (save-excursion
- (skip-chars-backward " \t")
- (bolp)))
- (skip-chars-forward "\* \t"))
- (t
- (goto-char (point-at-bol))))))
-
-(defun js2-end-of-line ()
- "Toggle point between eol and last non-whitespace char in line."
- (interactive)
- (if (eolp)
- (skip-chars-backward " \t")
- (goto-char (point-at-eol))))
-
-(defun js2-mode-wait-for-parse (callback)
- "Invoke CALLBACK when parsing is finished.
-If parsing is already finished, calls CALLBACK immediately."
- (if (not js2-mode-buffer-dirty-p)
- (funcall callback)
- (push callback js2-mode-pending-parse-callbacks)
- (add-hook 'js2-parse-finished-hook #'js2-mode-parse-finished)))
-
-(defun js2-mode-parse-finished ()
- "Invoke callbacks in `js2-mode-pending-parse-callbacks'."
- ;; We can't let errors propagate up, since it prevents the
- ;; `js2-parse' method from completing normally and returning
- ;; the ast, which makes things mysteriously not work right.
- (unwind-protect
- (dolist (cb js2-mode-pending-parse-callbacks)
- (condition-case err
- (funcall cb)
- (error (message "%s" err))))
- (setq js2-mode-pending-parse-callbacks nil)))
-
-(defun js2-mode-flag-region (from to flag)
- "Hide or show text from FROM to TO, according to FLAG.
-If FLAG is nil then text is shown, while if FLAG is t the text is hidden.
-Returns the created overlay if FLAG is non-nil."
- (remove-overlays from to 'invisible 'js2-outline)
- (when flag
- (let ((o (make-overlay from to)))
- (overlay-put o 'invisible 'js2-outline)
- (overlay-put o 'isearch-open-invisible
- 'js2-isearch-open-invisible)
- o)))
-
-;; Function to be set as an outline-isearch-open-invisible' property
-;; to the overlay that makes the outline invisible (see
-;; `js2-mode-flag-region').
-(defun js2-isearch-open-invisible (_overlay)
- ;; We rely on the fact that isearch places point on the matched text.
- (js2-mode-show-element))
-
-(defun js2-mode-invisible-overlay-bounds (&optional pos)
- "Return cons cell of bounds of folding overlay at POS.
-Returns nil if not found."
- (let ((overlays (overlays-at (or pos (point))))
- o)
- (while (and overlays
- (not o))
- (if (overlay-get (car overlays) 'invisible)
- (setq o (car overlays))
- (setq overlays (cdr overlays))))
- (if o
- (cons (overlay-start o) (overlay-end o)))))
-
-(defun js2-mode-function-at-point (&optional pos)
- "Return the innermost function node enclosing current point.
-Returns nil if point is not in a function."
- (let ((node (js2-node-at-point pos)))
- (while (and node (not (js2-function-node-p node)))
- (setq node (js2-node-parent node)))
- (if (js2-function-node-p node)
- node)))
-
-(defun js2-mode-toggle-element ()
- "Hide or show the foldable element at the point."
- (interactive)
- (let (comment fn pos)
- (save-excursion
- (cond
- ;; /* ... */ comment?
- ((js2-block-comment-p (setq comment (js2-comment-at-point)))
- (if (js2-mode-invisible-overlay-bounds
- (setq pos (+ 3 (js2-node-abs-pos comment))))
- (progn
- (goto-char pos)
- (js2-mode-show-element))
- (js2-mode-hide-element)))
- ;; //-comment?
- ((save-excursion
- (back-to-indentation)
- (looking-at js2-mode-//-comment-re))
- (js2-mode-toggle-//-comment))
- ;; function?
- ((setq fn (js2-mode-function-at-point))
- (setq pos (and (js2-function-node-body fn)
- (js2-node-abs-pos (js2-function-node-body fn))))
- (goto-char (1+ pos))
- (if (js2-mode-invisible-overlay-bounds)
- (js2-mode-show-element)
- (js2-mode-hide-element)))
- (t
- (message "Nothing at point to hide or show"))))))
-
-(defun js2-mode-hide-element ()
- "Fold/hide contents of a block, showing ellipses.
-Show the hidden text with \\[js2-mode-show-element]."
- (interactive)
- (if js2-mode-buffer-dirty-p
- (js2-mode-wait-for-parse #'js2-mode-hide-element))
- (let (node body beg end)
- (cond
- ((js2-mode-invisible-overlay-bounds)
- (message "already hidden"))
- (t
- (setq node (js2-node-at-point))
- (cond
- ((js2-block-comment-p node)
- (js2-mode-hide-comment node))
- (t
- (while (and node (not (js2-function-node-p node)))
- (setq node (js2-node-parent node)))
- (if (and node
- (setq body (js2-function-node-body node)))
- (progn
- (setq beg (js2-node-abs-pos body)
- end (+ beg (js2-node-len body)))
- (js2-mode-flag-region (1+ beg) (1- end) 'hide))
- (message "No collapsable element found at point"))))))))
-
-(defun js2-mode-show-element ()
- "Show the hidden element at current point."
- (interactive)
- (let ((bounds (js2-mode-invisible-overlay-bounds)))
- (if bounds
- (js2-mode-flag-region (car bounds) (cdr bounds) nil)
- (message "Nothing to un-hide"))))
-
-(defun js2-mode-show-all ()
- "Show all of the text in the buffer."
- (interactive)
- (js2-mode-flag-region (point-min) (point-max) nil))
-
-(defun js2-mode-toggle-hide-functions ()
- (interactive)
- (if js2-mode-functions-hidden
- (js2-mode-show-functions)
- (js2-mode-hide-functions)))
-
-(defun js2-mode-hide-functions ()
- "Hides all non-nested function bodies in the buffer.
-Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element]
-to open an individual entry."
- (interactive)
- (if js2-mode-buffer-dirty-p
- (js2-mode-wait-for-parse #'js2-mode-hide-functions))
- (if (null js2-mode-ast)
- (message "Oops - parsing failed")
- (setq js2-mode-functions-hidden t)
- (js2-visit-ast js2-mode-ast #'js2-mode-function-hider)))
-
-(defun js2-mode-function-hider (n endp)
- (when (not endp)
- (let ((tt (js2-node-type n))
- body beg end)
- (cond
- ((and (= tt js2-FUNCTION)
- (setq body (js2-function-node-body n)))
- (setq beg (js2-node-abs-pos body)
- end (+ beg (js2-node-len body)))
- (js2-mode-flag-region (1+ beg) (1- end) 'hide)
- nil) ; don't process children of function
- (t
- t))))) ; keep processing other AST nodes
-
-(defun js2-mode-show-functions ()
- "Un-hide any folded function bodies in the buffer."
- (interactive)
- (setq js2-mode-functions-hidden nil)
- (save-excursion
- (goto-char (point-min))
- (while (/= (goto-char (next-overlay-change (point)))
- (point-max))
- (dolist (o (overlays-at (point)))
- (when (and (overlay-get o 'invisible)
- (not (overlay-get o 'comment)))
- (js2-mode-flag-region (overlay-start o) (overlay-end o) nil))))))
-
-(defun js2-mode-hide-comment (n)
- (let* ((head (if (eq (js2-comment-node-format n) 'jsdoc)
- 3 ; /**
- 2)) ; /*
- (beg (+ (js2-node-abs-pos n) head))
- (end (- (+ beg (js2-node-len n)) head 2))
- (o (js2-mode-flag-region beg end 'hide)))
- (overlay-put o 'comment t)))
-
-(defun js2-mode-toggle-hide-comments ()
- "Folds all block comments in the buffer.
-Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element]
-to open an individual entry."
- (interactive)
- (if js2-mode-comments-hidden
- (js2-mode-show-comments)
- (js2-mode-hide-comments)))
-
-(defun js2-mode-hide-comments ()
- (interactive)
- (if js2-mode-buffer-dirty-p
- (js2-mode-wait-for-parse #'js2-mode-hide-comments))
- (if (null js2-mode-ast)
- (message "Oops - parsing failed")
- (setq js2-mode-comments-hidden t)
- (dolist (n (js2-ast-root-comments js2-mode-ast))
- (when (js2-block-comment-p n)
- (js2-mode-hide-comment n)))
- (js2-mode-hide-//-comments)))
-
-(defun js2-mode-extend-//-comment (direction)
- "Find start or end of a block of similar //-comment lines.
-DIRECTION is -1 to look back, 1 to look forward.
-INDENT is the indentation level to match.
-Returns the end-of-line position of the furthest adjacent
-//-comment line with the same indentation as the current line.
-If there is no such matching line, returns current end of line."
- (let ((pos (point-at-eol))
- (indent (current-indentation)))
- (save-excursion
- (while (and (zerop (forward-line direction))
- (looking-at js2-mode-//-comment-re)
- (eq indent (length (match-string 1))))
- (setq pos (point-at-eol)))
- pos)))
-
-(defun js2-mode-hide-//-comments ()
- "Fold adjacent 1-line comments, showing only snippet of first one."
- (let (beg end)
- (save-excursion
- (goto-char (point-min))
- (while (re-search-forward js2-mode-//-comment-re nil t)
- (setq beg (point)
- end (js2-mode-extend-//-comment 1))
- (unless (eq beg end)
- (overlay-put (js2-mode-flag-region beg end 'hide)
- 'comment t))
- (goto-char end)
- (forward-char 1)))))
-
-(defun js2-mode-toggle-//-comment ()
- "Fold or un-fold any multi-line //-comment at point.
-Caller should have determined that this line starts with a //-comment."
- (let* ((beg (point-at-eol))
- (end beg))
- (save-excursion
- (goto-char end)
- (if (js2-mode-invisible-overlay-bounds)
- (js2-mode-show-element)
- ;; else hide the comment
- (setq beg (js2-mode-extend-//-comment -1)
- end (js2-mode-extend-//-comment 1))
- (unless (eq beg end)
- (overlay-put (js2-mode-flag-region beg end 'hide)
- 'comment t))))))
-
-(defun js2-mode-show-comments ()
- "Un-hide any hidden comments, leaving other hidden elements alone."
- (interactive)
- (setq js2-mode-comments-hidden nil)
- (save-excursion
- (goto-char (point-min))
- (while (/= (goto-char (next-overlay-change (point)))
- (point-max))
- (dolist (o (overlays-at (point)))
- (when (overlay-get o 'comment)
- (js2-mode-flag-region (overlay-start o) (overlay-end o) nil))))))
-
-(defun js2-mode-display-warnings-and-errors ()
- "Turn on display of warnings and errors."
- (interactive)
- (setq js2-mode-show-parse-errors t
- js2-mode-show-strict-warnings t)
- (js2-reparse 'force))
-
-(defun js2-mode-hide-warnings-and-errors ()
- "Turn off display of warnings and errors."
- (interactive)
- (setq js2-mode-show-parse-errors nil
- js2-mode-show-strict-warnings nil)
- (js2-reparse 'force))
-
-(defun js2-mode-toggle-warnings-and-errors ()
- "Toggle the display of warnings and errors.
-Some users don't like having warnings/errors reported while they type."
- (interactive)
- (setq js2-mode-show-parse-errors (not js2-mode-show-parse-errors)
- js2-mode-show-strict-warnings (not js2-mode-show-strict-warnings))
- (if (called-interactively-p 'any)
- (message "warnings and errors %s"
- (if js2-mode-show-parse-errors
- "enabled"
- "disabled")))
- (js2-reparse 'force))
-
-(defun js2-mode-customize ()
- (interactive)
- (customize-group 'js2-mode))
-
-(defun js2-mode-forward-sexp (&optional arg)
- "Move forward across one statement or balanced expression.
-With ARG, do it that many times. Negative arg -N means
-move backward across N balanced expressions."
- (interactive "p")
- (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 (cl-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.
- (cl-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."
- (cond
- ((or (js2-array-node-p node)
- (js2-object-node-p node)
- (js2-comp-node-p node)
- (memq (aref node 0) '(cl-struct-js2-block-node cl-struct-js2-scope)))
- (cons abs-pos (+ abs-pos (js2-node-len node) -1)))
- ((js2-paren-expr-node-p node)
- (let ((lp (js2-node-lp node))
- (rp (js2-node-rp node)))
- (cons (when lp (+ abs-pos lp))
- (when rp (+ abs-pos rp)))))))
-
-(defun js2-node-closest-child (parent point limit &optional before)
- (let* ((parent-pos (js2-node-abs-pos parent))
- (rpoint (- point parent-pos))
- (rlimit (- limit parent-pos))
- (min (min rpoint rlimit))
- (max (max rpoint rlimit))
- found)
- (catch 'done
- (js2-visit-ast
- parent
- (lambda (node _end-p)
- (if (eq node parent)
- t
- (let ((pos (js2-node-pos node)) ;; Both relative values.
- (end (+ (js2-node-pos node) (js2-node-len node))))
- (when (and (>= pos min) (<= end max)
- (if before (< pos rpoint) (> end rpoint)))
- (setq found node))
- (when (> end rpoint)
- (throw 'done nil)))
- nil))))
- found))
-
-(defun js2-errors ()
- "Return a list of errors found."
- (and js2-mode-ast
- (js2-ast-root-errors js2-mode-ast)))
-
-(defun js2-warnings ()
- "Return a list of warnings found."
- (and js2-mode-ast
- (js2-ast-root-warnings js2-mode-ast)))
-
-(defun js2-have-errors-p ()
- "Return non-nil if any parse errors or warnings were found."
- (or (js2-errors) (js2-warnings)))
-
-(defun js2-errors-and-warnings ()
- "Return a copy of the concatenated errors and warnings lists.
-They are appended: first the errors, then the warnings.
-Entries are of the form (MSG BEG END)."
- (when js2-mode-ast
- (append (js2-ast-root-errors js2-mode-ast)
- (copy-sequence (js2-ast-root-warnings js2-mode-ast)))))
-
-(defun js2-next-error (&optional arg reset)
- "Move to next parse error.
-Typically invoked via \\[next-error].
-ARG is the number of errors, forward or backward, to move.
-RESET means start over from the beginning."
- (interactive "p")
- (if (not (or (js2-errors) (js2-warnings)))
- (message "No errors")
- (when reset
- (goto-char (point-min)))
- (let* ((errs (js2-errors-and-warnings))
- (continue t)
- (start (point))
- (count (or arg 1))
- (backward (cl-minusp count))
- (sorter (if backward '> '<))
- (stopper (if backward '< '>))
- (count (abs count))
- all-errs err)
- ;; Sort by start position.
- (setq errs (sort errs (lambda (e1 e2)
- (funcall sorter (cl-second e1) (cl-second e2))))
- all-errs errs)
- ;; Find nth error with pos > start.
- (while (and errs continue)
- (when (funcall stopper (cl-cadar errs) start)
- (setq err (car errs))
- (if (zerop (cl-decf count))
- (setq continue nil)))
- (setq errs (cdr errs)))
- ;; Clear for `js2-echo-error'.
- (message nil)
- (if err
- (goto-char (cl-second err))
- ;; Wrap around to first error.
- (goto-char (cl-second (car all-errs)))
- ;; If we were already on it, echo msg again.
- (if (= (point) start)
- (js2-echo-error (point) (point)))))))
-
-(defun js2-down-mouse-3 ()
- "Make right-click move the point to the click location.
-This makes right-click context menu operations a bit more intuitive.
-The point will not move if the region is active, however, to avoid
-destroying the region selection."
- (interactive)
- (when (and js2-move-point-on-right-click
- (not mark-active))
- (let ((e last-input-event))
- (ignore-errors
- (goto-char (cl-cadadr e))))))
-
-(defun js2-mode-create-imenu-index ()
- "Return an alist for `imenu--index-alist'."
- ;; This is built up in `js2-parse-record-imenu' during parsing.
- (when js2-mode-ast
- ;; if we have an ast but no recorder, they're requesting a rescan
- (unless js2-imenu-recorder
- (js2-reparse 'force))
- (prog1
- (js2-build-imenu-index)
- (setq js2-imenu-recorder nil
- js2-imenu-function-map nil))))
-
-(defun js2-mode-find-tag ()
- "Replacement for `find-tag-default'.
-`find-tag-default' returns a ridiculous answer inside comments."
- (let (beg end)
- (js2-with-underscore-as-word-syntax
- (save-excursion
- (if (and (not (looking-at "[[:alnum:]_$]"))
- (looking-back "[[:alnum:]_$]"))
- (setq beg (progn (forward-word -1) (point))
- end (progn (forward-word 1) (point)))
- (setq beg (progn (forward-word 1) (point))
- end (progn (forward-word -1) (point))))
- (replace-regexp-in-string
- "[\"']" ""
- (buffer-substring-no-properties beg end))))))
-
-(defun js2-mode-forward-sibling ()
- "Move to the end of the sibling following point in parent.
-Returns non-nil if successful, or nil if there was no following sibling."
- (let* ((node (js2-node-at-point))
- (parent (js2-mode-find-enclosing-fn node))
- sib)
- (when (setq sib (js2-node-find-child-after (point) parent))
- (goto-char (+ (js2-node-abs-pos sib)
- (js2-node-len sib))))))
-
-(defun js2-mode-backward-sibling ()
- "Move to the beginning of the sibling node preceding point in parent.
-Parent is defined as the enclosing script or function."
- (let* ((node (js2-node-at-point))
- (parent (js2-mode-find-enclosing-fn node))
- sib)
- (when (setq sib (js2-node-find-child-before (point) parent))
- (goto-char (js2-node-abs-pos sib)))))
-
-(defun js2-beginning-of-defun (&optional arg)
- "Go to line on which current function starts, and return t on success.
-If we're not in a function or already at the beginning of one, go
-to beginning of previous script-level element.
-With ARG N, do that N times. If N is negative, move forward."
- (setq arg (or arg 1))
- (if (cl-plusp arg)
- (let ((parent (js2-node-parent-script-or-fn (js2-node-at-point))))
- (when (cond
- ((js2-function-node-p parent)
- (goto-char (js2-node-abs-pos parent)))
- (t
- (js2-mode-backward-sibling)))
- (if (> arg 1)
- (js2-beginning-of-defun (1- arg))
- t)))
- (when (js2-end-of-defun)
- (js2-beginning-of-defun (if (>= arg -1) 1 (1+ arg))))))
-
-(defun js2-end-of-defun ()
- "Go to the char after the last position of the current function
-or script-level element."
- (let* ((node (js2-node-at-point))
- (parent (or (and (js2-function-node-p node) node)
- (js2-node-parent-script-or-fn node)))
- script)
- (unless (js2-function-node-p parent)
- ;; Use current script-level node, or, if none, the next one.
- (setq script (or parent node)
- parent (js2-node-find-child-before (point) script))
- (when (or (null parent)
- (>= (point) (+ (js2-node-abs-pos parent)
- (js2-node-len parent))))
- (setq parent (js2-node-find-child-after (point) script))))
- (when parent
- (goto-char (+ (js2-node-abs-pos parent)
- (js2-node-len parent))))))
-
-(defun js2-mark-defun (&optional allow-extend)
- "Put mark at end of this function, point at beginning.
-The function marked is the one that contains point.
-
-Interactively, if this command is repeated,
-or (in Transient Mark mode) if the mark is active,
-it marks the next defun after the ones already marked."
- (interactive "p")
- (let (extended)
- (when (and allow-extend
- (or (and (eq last-command this-command) (mark t))
- (and transient-mark-mode mark-active)))
- (let ((sib (save-excursion
- (goto-char (mark))
- (if (js2-mode-forward-sibling)
- (point)))))
- (if sib
- (progn
- (set-mark sib)
- (setq extended t))
- ;; no more siblings - try extending to enclosing node
- (goto-char (mark t)))))
- (when (not extended)
- (let ((node (js2-node-at-point (point) t)) ; skip comments
- ast fn stmt parent beg end)
- (when (js2-ast-root-p node)
- (setq ast node
- node (or (js2-node-find-child-after (point) node)
- (js2-node-find-child-before (point) node))))
- ;; only mark whole buffer if we can't find any children
- (if (null node)
- (setq node ast))
- (if (js2-function-node-p node)
- (setq parent node)
- (setq fn (js2-mode-find-enclosing-fn node)
- stmt (if (or (null fn)
- (js2-ast-root-p fn))
- (js2-mode-find-first-stmt node))
- parent (or stmt fn)))
- (setq beg (js2-node-abs-pos parent)
- end (+ beg (js2-node-len parent)))
- (push-mark beg)
- (goto-char end)
- (exchange-point-and-mark)))))
-
-(defun js2-narrow-to-defun ()
- "Narrow to the function enclosing point."
- (interactive)
- (let* ((node (js2-node-at-point (point) t)) ; skip comments
- (fn (if (js2-script-node-p node)
- node
- (js2-mode-find-enclosing-fn node)))
- (beg (js2-node-abs-pos fn)))
- (unless (js2-ast-root-p fn)
- (narrow-to-region beg (+ beg (js2-node-len fn))))))
-
-(provide 'js2-mode)
-
-;;; js2-mode.el ends here
diff --git a/scripts/download-dependencies.el b/scripts/download-dependencies.el
new file mode 100644
index 0000000..37f243b
--- /dev/null
+++ b/scripts/download-dependencies.el
@@ -0,0 +1,40 @@
+;; -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+(defconst directory (file-name-directory (or load-file-name buffer-file-name)))
+
+(defun resolve-path (path)
+ (expand-file-name path directory))
+
+(defun strip-headers ()
+ (goto-char 1)
+ (kill-paragraph 1) ; The headers are 1 paragraph. I hope.
+ (kill-line) ; A line separates the headers from the file's content.
+ )
+
+(let ((files
'("https://raw.githubusercontent.com/mooz/js2-mode/master/js2-mode.el"
+
"https://raw.githubusercontent.com/rejeep/ert-async.el/master/ert-async.el")))
+ (make-directory (resolve-path "../libraries") t)
+ (dolist (file files)
+ (let* ((basename (file-name-nondirectory file))
+ (destination (resolve-path (concat "../libraries/" basename))))
+ (when (null (file-exists-p destination))
+ (with-current-buffer (url-retrieve-synchronously file)
+ (strip-headers)
+ (write-file destination))))))
- [elpa] master 450aa85 236/271: Use macros to clean up tests., (continued)
- [elpa] master 450aa85 236/271: Use macros to clean up tests., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 555820b 247/271: Merge branch 'master' into develop, Jackson Ray Hamilton, 2015/02/05
- [elpa] master 43bb3c1 261/271: Remove unnecessary file from .elpaignore., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 195fbe7 196/271: Improve assertion message., Jackson Ray Hamilton, 2015/02/05
- [elpa] master d82f357 199/271: Merge branch 'feature/name-nodes' into develop, Jackson Ray Hamilton, 2015/02/05
- [elpa] master 310aca4 228/271: Use `save-excursion' before fontifying., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 5cc5224 249/271: Update sentence spacing., Jackson Ray Hamilton, 2015/02/05
- [elpa] master de8eacd 204/271: Update benchmarks in readme., Jackson Ray Hamilton, 2015/02/05
- [elpa] master f15c193 189/271: Escape., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 31ee972 197/271: Improve property lookup test., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 3d034f9 262/271: Merge branch 'develop',
Jackson Ray Hamilton <=
- [elpa] master 6d24750 206/271: Update docs., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 6059dca 179/271: Add Travis CI., Jackson Ray Hamilton, 2015/02/05
- [elpa] master f2ace00 224/271: Add faces dynamically. Stop looping around at the last level., Jackson Ray Hamilton, 2015/02/05
- [elpa] master d44b426 208/271: Merge branch 'master' into develop, Jackson Ray Hamilton, 2015/02/05
- [elpa] master 3cff176 210/271: Add failing key-value pair test., Jackson Ray Hamilton, 2015/02/05
- [elpa] master e09d9ad 227/271: Add tests for comments and strings., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 21cc6c6 200/271: Save benchmark logs to an untracked directory., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 30a24f9 265/271: Add 'packages/context-coloring/' from commit 'f2b5d796d4dffc71b10503a8ae119bfa3274978e', Jackson Ray Hamilton, 2015/02/05
- [elpa] master 03d111c 235/271: Cleanup., Jackson Ray Hamilton, 2015/02/05
- [elpa] master eba74ae 192/271: Improve assertion functions., Jackson Ray Hamilton, 2015/02/05