[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master d78046d 098/271: Include external libraries.
From: |
Jackson Ray Hamilton |
Subject: |
[elpa] master d78046d 098/271: Include external libraries. |
Date: |
Thu, 05 Feb 2015 18:30:10 +0000 |
branch: master
commit d78046db35d38d0bae904db5eacb0df3fd1fd1dc
Author: Jackson Ray Hamilton <address@hidden>
Commit: Jackson Ray Hamilton <address@hidden>
Include external libraries.
---
README.md | 12 +-
lib/escope.js | 1117 ++++++++++++++++
lib/esprima.js | 3756 +++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/estraverse.js | 838 ++++++++++++
package.json | 4 -
scopifier.js | 4 +-
6 files changed, 5723 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index 7adc0ea..4ad0eb2 100644
--- a/README.md
+++ b/README.md
@@ -40,11 +40,19 @@ can also be integrated into your workflow via [flycheck][].
Requires Emacs 24+ and [Node.js 0.10+][node].
- Clone this repository.
-- Run `make` in it.
+
+```bash
+cd ~/.emacs.d/
+git clone https://github.com/jacksonrayhamilton/context-coloring.git
+```
+
- Add it to your [load path][].
-- Add the following to your `~/.emacs`:
+- Add a mode hook for `context-coloring-mode`.
+
+In your `~/.emacs` file:
```lisp
+(add-to-list 'load-path "~/.emacs.d/context-coloring")
(require 'context-coloring)
(add-hook 'js-mode-hook 'context-coloring-mode)
```
diff --git a/lib/escope.js b/lib/escope.js
new file mode 100644
index 0000000..b42783c
--- /dev/null
+++ b/lib/escope.js
@@ -0,0 +1,1117 @@
+/*
+ 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/lib/esprima.js b/lib/esprima.js
new file mode 100644
index 0000000..593021f
--- /dev/null
+++ b/lib/esprima.js
@@ -0,0 +1,3756 @@
+/*
+ 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/lib/estraverse.js b/lib/estraverse.js
new file mode 100644
index 0000000..4009cb0
--- /dev/null
+++ b/lib/estraverse.js
@@ -0,0 +1,838 @@
+/*
+ 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/package.json b/package.json
index bc1db33..2070d44 100644
--- a/package.json
+++ b/package.json
@@ -19,10 +19,6 @@
"url": "https://github.com/jacksonrayhamilton/context-coloring/issues"
},
"homepage": "https://github.com/jacksonrayhamilton/context-coloring",
- "dependencies": {
- "escope": "^1.0.1",
- "esprima": "^1.2.2"
- },
"devDependencies": {
"matcha": "^0.6.0",
"mocha": "^2.0.1"
diff --git a/scopifier.js b/scopifier.js
index 167e899..8145501 100644
--- a/scopifier.js
+++ b/scopifier.js
@@ -1,7 +1,7 @@
'use strict';
-var escope = require('escope'),
- esprima = require('esprima'),
+var escope = require('./lib/escope'),
+ esprima = require('./lib/esprima'),
normal = 0,
bold = 1,
- [elpa] master ab083f4 102/271: Simplify package.json., (continued)
- [elpa] master ab083f4 102/271: Simplify package.json., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 1c47db9 107/271: Fix link., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 0960e18 106/271: Spelling., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 79513b2 109/271: Remove styling from faces., Jackson Ray Hamilton, 2015/02/05
- [elpa] master c6ea2eb 116/271: Add degree sign., Jackson Ray Hamilton, 2015/02/05
- [elpa] master d430804 108/271: Add tty faces., Jackson Ray Hamilton, 2015/02/05
- [elpa] master d339677 113/271: Strip BOM., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 9693c56 125/271: Move fixture into its own file., Jackson Ray Hamilton, 2015/02/05
- [elpa] master e58833f 120/271: Refactor and document., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 3031e54 119/271: Extend range; fix modulo bug., Jackson Ray Hamilton, 2015/02/05
- [elpa] master d78046d 098/271: Include external libraries.,
Jackson Ray Hamilton <=
- [elpa] master f7f4fe8 126/271: Improve elisp tests., Jackson Ray Hamilton, 2015/02/05
- [elpa] master fe2bd04 128/271: Multiple vars., Jackson Ray Hamilton, 2015/02/05
- [elpa] master b51c36d 118/271: New experimental color scheme., Jackson Ray Hamilton, 2015/02/05
- [elpa] master c595539 111/271: Shift and darken colors for light color scheme., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 146c096 114/271: Merge branch 'develop' into feature/color-schemer, Jackson Ray Hamilton, 2015/02/05
- [elpa] master aa5cca3 129/271: Multiple vars. Rename., Jackson Ray Hamilton, 2015/02/05
- [elpa] master fae85f9 117/271: Add code samples., Jackson Ray Hamilton, 2015/02/05
- [elpa] master b190309 133/271: Update documentation to be language-agnostic., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 6f34a6a 134/271: Update screenshot., Jackson Ray Hamilton, 2015/02/05
- [elpa] master 5a7fc32 138/271: Documentation., Jackson Ray Hamilton, 2015/02/05