pax_global_header00006660000000000000000000000064126703661770014531gustar00rootroot0000000000000052 comment=6f6a4e99653908e859c7c10d04d9518bf4844ede estraverse-4.2.0/000077500000000000000000000000001267036617700137175ustar00rootroot00000000000000estraverse-4.2.0/.babelrc000066400000000000000000000000351267036617700153100ustar00rootroot00000000000000{ "presets": ["es2015"] }estraverse-4.2.0/.gitignore000066400000000000000000000001721267036617700157070ustar00rootroot00000000000000# Emacs *~ \#*\# # Node modules node_modules/ # Cover .coverage_data/ cover_html/ coverage/ npm-debug.log .vimrc.local estraverse-4.2.0/.jshintrc000066400000000000000000000003621267036617700155450ustar00rootroot00000000000000{ "curly": true, "eqeqeq": true, "immed": true, "eqnull": true, "latedef": true, "noarg": true, "noempty": true, "quotmark": "single", "undef": true, "unused": true, "strict": true, "trailing": true, "node": true } estraverse-4.2.0/.npmignore000066400000000000000000000002351267036617700157160ustar00rootroot00000000000000npm-debug.log .DS_Store .vimrc.local t.js .travis.yml .npmignore /tmp/ /.git/ /node_modules/ /tools/ /test/ /build/ /cover_html/ /coverage/ /.coverage_data/ estraverse-4.2.0/.travis.yml000066400000000000000000000002261267036617700160300ustar00rootroot00000000000000language: node_js node_js: - "0.10" - "0.11" - "0.12" - "iojs" - 4 - 5 matrix: allow_failures: - node_js: "0.11" estraverse-4.2.0/LICENSE.BSD000066400000000000000000000023171267036617700153360ustar00rootroot00000000000000Redistribution 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 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. estraverse-4.2.0/README.md000066400000000000000000000114011267036617700151730ustar00rootroot00000000000000### Estraverse [![Build Status](https://secure.travis-ci.org/estools/estraverse.png)](http://travis-ci.org/estools/estraverse) Estraverse ([estraverse](http://github.com/estools/estraverse)) is [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm) traversal functions from [esmangle project](http://github.com/estools/esmangle). ### Documentation You can find usage docs at [wiki page](https://github.com/estools/estraverse/wiki/Usage). ### Example Usage The following code will output all variables declared at the root of a file. ```javascript estraverse.traverse(ast, { enter: function (node, parent) { if (node.type == 'FunctionExpression' || node.type == 'FunctionDeclaration') return estraverse.VisitorOption.Skip; }, leave: function (node, parent) { if (node.type == 'VariableDeclarator') console.log(node.id.name); } }); ``` We can use `this.skip`, `this.remove` and `this.break` functions instead of using Skip, Remove and Break. ```javascript estraverse.traverse(ast, { enter: function (node) { this.break(); } }); ``` And estraverse provides `estraverse.replace` function. When returning node from `enter`/`leave`, current node is replaced with it. ```javascript result = estraverse.replace(tree, { enter: function (node) { // Replace it with replaced. if (node.type === 'Literal') return replaced; } }); ``` By passing `visitor.keys` mapping, we can extend estraverse traversing functionality. ```javascript // This tree contains a user-defined `TestExpression` node. var tree = { type: 'TestExpression', // This 'argument' is the property containing the other **node**. argument: { type: 'Literal', value: 20 }, // This 'extended' is the property not containing the other **node**. extended: true }; estraverse.traverse(tree, { enter: function (node) { }, // Extending the existing traversing rules. keys: { // TargetNodeName: [ 'keys', 'containing', 'the', 'other', '**node**' ] TestExpression: ['argument'] } }); ``` By passing `visitor.fallback` option, we can control the behavior when encountering unknown nodes. ```javascript // This tree contains a user-defined `TestExpression` node. var tree = { type: 'TestExpression', // This 'argument' is the property containing the other **node**. argument: { type: 'Literal', value: 20 }, // This 'extended' is the property not containing the other **node**. extended: true }; estraverse.traverse(tree, { enter: function (node) { }, // Iterating the child **nodes** of unknown nodes. fallback: 'iteration' }); ``` When `visitor.fallback` is a function, we can determine which keys to visit on each node. ```javascript // This tree contains a user-defined `TestExpression` node. var tree = { type: 'TestExpression', // This 'argument' is the property containing the other **node**. argument: { type: 'Literal', value: 20 }, // This 'extended' is the property not containing the other **node**. extended: true }; estraverse.traverse(tree, { enter: function (node) { }, // Skip the `argument` property of each node fallback: function(node) { return Object.keys(node).filter(function(key) { return key !== 'argument'; }); } }); ``` ### License Copyright (C) 2012-2016 [Yusuke Suzuki](http://github.com/Constellation) (twitter: [@Constellation](http://twitter.com/Constellation)) and other contributors. 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 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. estraverse-4.2.0/estraverse.js000066400000000000000000000657721267036617700164610ustar00rootroot00000000000000/* Copyright (C) 2012-2013 Yusuke Suzuki Copyright (C) 2012 Ariya Hidayat 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 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*/ (function clone(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) { var keys = objectKeys(from), key, i, len; for (i = 0, len = keys.length; i < len; i += 1) { key = keys[i]; to[key] = from[key]; } return to; } Syntax = { AssignmentExpression: 'AssignmentExpression', AssignmentPattern: 'AssignmentPattern', ArrayExpression: 'ArrayExpression', ArrayPattern: 'ArrayPattern', ArrowFunctionExpression: 'ArrowFunctionExpression', AwaitExpression: 'AwaitExpression', // CAUTION: It's deferred to ES7. 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', ExportAllDeclaration: 'ExportAllDeclaration', ExportDefaultDeclaration: 'ExportDefaultDeclaration', ExportNamedDeclaration: 'ExportNamedDeclaration', 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', MetaProperty: 'MetaProperty', MethodDefinition: 'MethodDefinition', ModuleSpecifier: 'ModuleSpecifier', NewExpression: 'NewExpression', ObjectExpression: 'ObjectExpression', ObjectPattern: 'ObjectPattern', Program: 'Program', Property: 'Property', RestElement: 'RestElement', ReturnStatement: 'ReturnStatement', SequenceExpression: 'SequenceExpression', SpreadElement: 'SpreadElement', Super: 'Super', 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'], AssignmentPattern: ['left', 'right'], ArrayExpression: ['elements'], ArrayPattern: ['elements'], ArrowFunctionExpression: ['params', 'body'], AwaitExpression: ['argument'], // CAUTION: It's deferred to ES7. BlockStatement: ['body'], BinaryExpression: ['left', 'right'], BreakStatement: ['label'], CallExpression: ['callee', 'arguments'], CatchClause: ['param', 'body'], ClassBody: ['body'], ClassDeclaration: ['id', 'superClass', 'body'], ClassExpression: ['id', 'superClass', 'body'], 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: [], ExportAllDeclaration: ['source'], ExportDefaultDeclaration: ['declaration'], ExportNamedDeclaration: ['declaration', 'specifiers', 'source'], ExportSpecifier: ['exported', 'local'], ExpressionStatement: ['expression'], ForStatement: ['init', 'test', 'update', 'body'], ForInStatement: ['left', 'right', 'body'], ForOfStatement: ['left', 'right', 'body'], FunctionDeclaration: ['id', 'params', 'body'], FunctionExpression: ['id', 'params', 'body'], GeneratorExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7. Identifier: [], IfStatement: ['test', 'consequent', 'alternate'], ImportDeclaration: ['specifiers', 'source'], ImportDefaultSpecifier: ['local'], ImportNamespaceSpecifier: ['local'], ImportSpecifier: ['imported', 'local'], Literal: [], LabeledStatement: ['label', 'body'], LogicalExpression: ['left', 'right'], MemberExpression: ['object', 'property'], MetaProperty: ['meta', 'property'], MethodDefinition: ['key', 'value'], ModuleSpecifier: [], NewExpression: ['callee', 'arguments'], ObjectExpression: ['properties'], ObjectPattern: ['properties'], Program: ['body'], Property: ['key', 'value'], RestElement: [ 'argument' ], ReturnStatement: ['argument'], SequenceExpression: ['expressions'], SpreadElement: ['argument'], Super: [], SwitchStatement: ['discriminant', 'cases'], SwitchCase: ['test', 'consequent'], TaggedTemplateExpression: ['tag', 'quasi'], TemplateElement: [], TemplateLiteral: ['quasis', 'expressions'], ThisExpression: [], ThrowStatement: ['argument'], TryStatement: ['block', 'handler', '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 = null; if (visitor.fallback === 'iteration') { this.__fallback = objectKeys; } else if (typeof visitor.fallback === 'function') { this.__fallback = visitor.fallback; } 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 = node.type || element.wrap; candidates = this.__keys[nodeType]; if (!candidates) { if (this.__fallback) { candidates = this.__fallback(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) { var worklist, leavelist, node, nodeType, target, element, current, current2, candidates, candidate, sentinel, outer, key; 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; } } } } 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 = node.type || element.wrap; candidates = this.__keys[nodeType]; if (!candidates) { if (this.__fallback) { candidates = this.__fallback(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 = require('./package.json').version; exports.Syntax = Syntax; exports.traverse = traverse; exports.replace = replace; exports.attachComments = attachComments; exports.VisitorKeys = VisitorKeys; exports.VisitorOption = VisitorOption; exports.Controller = Controller; exports.cloneEnvironment = function () { return clone({}); }; return exports; }(exports)); /* vim: set sw=4 ts=4 et tw=80 : */ estraverse-4.2.0/gulpfile.js000066400000000000000000000053461267036617700160740ustar00rootroot00000000000000/* Copyright (C) 2014 Yusuke Suzuki 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 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. */ 'use strict'; var gulp = require('gulp'), git = require('gulp-git'), bump = require('gulp-bump'), filter = require('gulp-filter'), tagVersion = require('gulp-tag-version'); var TEST = [ 'test/*.js' ]; var POWERED = [ 'powered-test/*.js' ]; var SOURCE = [ 'src/**/*.js' ]; /** * Bumping version number and tagging the repository with it. * Please read http://semver.org/ * * You can use the commands * * gulp patch # makes v0.1.0 -> v0.1.1 * gulp feature # makes v0.1.1 -> v0.2.0 * gulp release # makes v0.2.1 -> v1.0.0 * * To bump the version numbers accordingly after you did a patch, * introduced a feature or made a backwards-incompatible release. */ function inc(importance) { // get all the files to bump version in return gulp.src(['./package.json']) // bump the version number in those files .pipe(bump({type: importance})) // save it back to filesystem .pipe(gulp.dest('./')) // commit the changed version number .pipe(git.commit('Bumps package version')) // read only one file to get the version number .pipe(filter('package.json')) // **tag it in the repository** .pipe(tagVersion({ prefix: '' })); } gulp.task('patch', [ ], function () { return inc('patch'); }) gulp.task('minor', [ ], function () { return inc('minor'); }) gulp.task('major', [ ], function () { return inc('major'); }) estraverse-4.2.0/package.json000066400000000000000000000017701267036617700162120ustar00rootroot00000000000000{ "name": "estraverse", "description": "ECMAScript JS AST traversal functions", "homepage": "https://github.com/estools/estraverse", "main": "estraverse.js", "version": "4.2.0", "engines": { "node": ">=0.10.0" }, "maintainers": [ { "name": "Yusuke Suzuki", "email": "utatane.tea@gmail.com", "web": "http://github.com/Constellation" } ], "repository": { "type": "git", "url": "http://github.com/estools/estraverse.git" }, "devDependencies": { "babel-preset-es2015": "^6.3.13", "babel-register": "^6.3.13", "chai": "^2.1.1", "espree": "^1.11.0", "gulp": "^3.8.10", "gulp-bump": "^0.2.2", "gulp-filter": "^2.0.0", "gulp-git": "^1.0.1", "gulp-tag-version": "^1.2.1", "jshint": "^2.5.6", "mocha": "^2.1.0" }, "license": "BSD-2-Clause", "scripts": { "test": "npm run-script lint && npm run-script unit-test", "lint": "jshint estraverse.js", "unit-test": "mocha --compilers js:babel-register" } } estraverse-4.2.0/test/000077500000000000000000000000001267036617700146765ustar00rootroot00000000000000estraverse-4.2.0/test/checkDump.js000066400000000000000000000031201267036617700171330ustar00rootroot00000000000000// Copyright (C) 2013 Yusuke Suzuki // // 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 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. import { expect } from 'chai'; export default function checkDump(dump, expected) { expect(normalize(expected)).to.be.equal(normalize(dump)); } function normalize(dump) { return dump .trim() .split('\n') .map(line => line.trim()) .join('\n'); } estraverse-4.2.0/test/controller.js000066400000000000000000000047021267036617700174220ustar00rootroot00000000000000// Copyright (C) 2013 Yusuke Suzuki // // 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 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. import { Controller } from '../'; import Dumper from './dumper'; import checkDump from './checkDump'; describe('controller', function() { it('traverse', function() { const controller = new Controller(); const dumper = new Dumper(); const tree = { type: 'ObjectExpression', properties: [{ key: { type: 'Identifier', name: 'a' }, value: { type: 'Identifier', name: 'a' } }] }; controller.traverse(tree, { enter(node) { dumper.log(`enter - ${node.type}`); }, leave(node) { dumper.log(`leave - ${node.type}`); } }); checkDump(dumper.result(), ` enter - ObjectExpression enter - undefined enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - undefined leave - ObjectExpression `); }); }); estraverse-4.2.0/test/dumper.js000066400000000000000000000040331267036617700165300ustar00rootroot00000000000000// Copyright (C) 2013 Yusuke Suzuki // // 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 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. import { traverse, VisitorOption } from '..'; export default class Dumper { constructor() { this.logs = []; } log(str) { this.logs.push(str); } result() { return this.logs.join('\n'); } static dump(tree, keys, fallback) { const dumper = new Dumper(); traverse(tree, { enter(node) { dumper.log(`enter - ${node.type}`); return VisitorOption[node.$enter]; }, leave(node) { dumper.log(`leave - ${node.type}`); return VisitorOption[node.$leave]; }, keys, fallback }); return dumper.result(); } }; estraverse-4.2.0/test/es6.js000066400000000000000000000260671267036617700157440ustar00rootroot00000000000000// -*- coding: utf-8 -*- // Copyright (C) 2015 Yusuke Suzuki // // 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 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. import Dumper from './dumper'; import checkDump from './checkDump'; import { parse as esprima } from './third_party/esprima'; import { parse as espree } from 'espree'; function classDeclaration() { const program = esprima(` class Hello { }; `); return program.body[0]; } describe('rest expression', function() { it('argument', function() { const tree = { type: 'RestElement', argument: { type: 'Identifier', name: 'hello' } }; checkDump(Dumper.dump(tree), ` enter - RestElement enter - Identifier leave - Identifier leave - RestElement `); }); }); describe('class', function() { it('declaration#1', function() { const tree = esprima(` class Hello extends World { method() { } }; `); checkDump(Dumper.dump(tree), ` enter - Program enter - ClassDeclaration enter - Identifier leave - Identifier enter - Identifier leave - Identifier enter - ClassBody enter - MethodDefinition enter - Identifier leave - Identifier enter - FunctionExpression enter - BlockStatement leave - BlockStatement leave - FunctionExpression leave - MethodDefinition leave - ClassBody leave - ClassDeclaration enter - EmptyStatement leave - EmptyStatement leave - Program `); }); it('declaration#2', function() { const tree = esprima(` class Hello extends ok() { method() { } }; `); checkDump(Dumper.dump(tree), ` enter - Program enter - ClassDeclaration enter - Identifier leave - Identifier enter - CallExpression enter - Identifier leave - Identifier leave - CallExpression enter - ClassBody enter - MethodDefinition enter - Identifier leave - Identifier enter - FunctionExpression enter - BlockStatement leave - BlockStatement leave - FunctionExpression leave - MethodDefinition leave - ClassBody leave - ClassDeclaration enter - EmptyStatement leave - EmptyStatement leave - Program `); }); }); describe('export', function() { it('named declaration #1', function() { const tree = { type: 'ExportNamedDeclaration', declaration: { type: 'VariableDeclaration', declarations: [{ type: 'VariableDeclarator', id: { type: 'Identifier', name: 'hello' }, init: { type: 'Literal', value: 6 } }] } }; checkDump(Dumper.dump(tree), ` enter - ExportNamedDeclaration enter - VariableDeclaration enter - VariableDeclarator enter - Identifier leave - Identifier enter - Literal leave - Literal leave - VariableDeclarator leave - VariableDeclaration leave - ExportNamedDeclaration `); }); it('named declaration #2', function() { const tree = { type: 'ExportNamedDeclaration', declaration: null, specifiers: [{ type: 'ExportSpecifier', exported: { type: 'Identifier', name: 'foo' }, local: { type: 'Identifier', name: 'bar' } }], source: { type: 'Literal', value: 'hello' } }; checkDump(Dumper.dump(tree), ` enter - ExportNamedDeclaration enter - ExportSpecifier enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - ExportSpecifier enter - Literal leave - Literal leave - ExportNamedDeclaration `); }); it('all declaration #1', function() { const tree = { type: 'ExportAllDeclaration', source: { type: 'Literal', value: 'hello' } }; checkDump(Dumper.dump(tree), ` enter - ExportAllDeclaration enter - Literal leave - Literal leave - ExportAllDeclaration `); }); it('default declaration #1', function() { const tree = { type: 'ExportDefaultDeclaration', declaration: classDeclaration() }; checkDump(Dumper.dump(tree), ` enter - ExportDefaultDeclaration enter - ClassDeclaration enter - Identifier leave - Identifier enter - ClassBody leave - ClassBody leave - ClassDeclaration leave - ExportDefaultDeclaration `); }); it('default declaration #1', function() { const tree = { type: 'ExportDefaultDeclaration', declaration: classDeclaration() }; checkDump(Dumper.dump(tree), ` enter - ExportDefaultDeclaration enter - ClassDeclaration enter - Identifier leave - Identifier enter - ClassBody leave - ClassBody leave - ClassDeclaration leave - ExportDefaultDeclaration `); }); }); describe('import', function() { it('default specifier #1', function() { const tree = espree(`import Cocoa from 'rabbit-house'`, { ecmaFeatures: { modules: true } }); checkDump(Dumper.dump(tree), ` enter - Program enter - ImportDeclaration enter - ImportDefaultSpecifier enter - Identifier leave - Identifier leave - ImportDefaultSpecifier enter - Literal leave - Literal leave - ImportDeclaration leave - Program `); }); it('named specifier #1', function() { const tree = espree(`import {Cocoa, Cappuccino as Chino} from 'rabbit-house'`, { ecmaFeatures: { modules: true } }); checkDump(Dumper.dump(tree), ` enter - Program enter - ImportDeclaration enter - ImportSpecifier enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - ImportSpecifier enter - ImportSpecifier enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - ImportSpecifier enter - Literal leave - Literal leave - ImportDeclaration leave - Program `); }); it('namespace specifier #1', function() { const tree = espree(`import * as RabbitHouse from 'rabbit-house'`, { ecmaFeatures: { modules: true } }); checkDump(Dumper.dump(tree), ` enter - Program enter - ImportDeclaration enter - ImportNamespaceSpecifier enter - Identifier leave - Identifier leave - ImportNamespaceSpecifier enter - Literal leave - Literal leave - ImportDeclaration leave - Program `); }); }); describe('pattern', function() { it('assignment pattern#1', function() { const tree = { type: 'AssignmentPattern', left: { type: 'Identifier', name: 'hello' }, right: { type: 'Literal', value: 'world' } }; checkDump(Dumper.dump(tree), ` enter - AssignmentPattern enter - Identifier leave - Identifier enter - Literal leave - Literal leave - AssignmentPattern `); }); }); describe('super', function() { it('super expression#1', function() { const tree = {type: 'Super'}; checkDump(Dumper.dump(tree), ` enter - Super leave - Super `); }); }); describe('meta property', function() { it('MetaProperty in constructor #1', function() { const tree = { type: 'UnaryExpression', operator: 'typeof', prefix: true, argument: { type: 'MetaProperty', meta: { type: 'Identifier', name: 'new' }, property: { type: 'Identifier', name: 'target' } } }; checkDump(Dumper.dump(tree), ` enter - UnaryExpression enter - MetaProperty enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - MetaProperty leave - UnaryExpression `); }); }); // vim: set sw=4 ts=4 et tw=80 : estraverse-4.2.0/test/replace.js000066400000000000000000000353431267036617700166570ustar00rootroot00000000000000// Copyright (C) 2014 Ingvar Stepanyan // // 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 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. import { replace, VisitorOption } from '..'; import { expect } from 'chai'; describe('replace', function() { it('can simplify expressions', function() { let tree = { type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'BinaryExpression', operator: '*', left: { type: 'Literal', value: 2 }, right: { type: 'Literal', value: 3 } } }] }; tree = replace(tree, { enter(node) { if (node.type === 'BinaryExpression' && node.left.type === 'Literal' && node.right.type === 'Literal') { return { type: 'Literal', value: eval(JSON.stringify(node.left.value) + node.operator + JSON.stringify(node.right.value)) }; } } }); expect(tree).to.be.eql({ type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'Literal', value: 6 } }] }); }); it('can remove nodes', function() { let tree = { type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'ArrayExpression', elements: [ {type: 'Literal', value: 1}, {type: 'Literal', value: 2}, {type: 'Literal', value: 3}, {type: 'Literal', value: 4} ] } }] }; tree = replace(tree, { enter(node) { if (node.type === 'Identifier' && node.name === 'a') { this.remove(); } if (node.type === 'Literal' && node.value === 2) { return VisitorOption.Remove; } } }); expect(tree).to.be.eql({ type: 'ObjectExpression', properties: [{ type: 'Property', key: null, value: { type: 'ArrayExpression', elements: [ {type: 'Literal', value: 1}, {type: 'Literal', value: 3}, {type: 'Literal', value: 4} ] } }] }); }); it('can remove all nodes in an array in enter phase', function() { let tree = { type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'ArrayExpression', elements: [ {type: 'Literal', value: 1}, {type: 'Literal', value: 2}, {type: 'Literal', value: 3}, {type: 'Literal', value: 4} ] } }] }; tree = replace(tree, { enter(node) { if (node.type === 'Literal') { return this.remove(); } } }); expect(tree).to.be.eql({ type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'ArrayExpression', elements: [] } }] }); tree = { type: 'FunctionExpression', id: { type: 'Identifier', name: 'foo' }, params: [], defaults: [], body: { type: 'BlockStatement', body: [{ type: 'ExpressionStatement', expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'debug' }, arguments: [{ type: 'Literal', value: 'foo', raw: '"foo"' }] } }, { type: 'ExpressionStatement', expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'debug' }, arguments: [{ type: 'Literal', value: 'bar', raw: '"bar"' }] } }] }, rest: null, generator: false, expression: false }; tree = replace(tree, { enter(node) { if (node.type === 'ExpressionStatement' && node.expression.type === 'CallExpression' && node.expression.callee.name === 'debug') { return this.remove(); } } }); expect(tree).to.be.eql({ type: 'FunctionExpression', id: { type: 'Identifier', name: 'foo' }, params: [], defaults: [], body: { type: 'BlockStatement', body: [] }, rest: null, generator: false, expression: false }); }); it('can remove all nodes in an array in leave phase', function() { let tree = { type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'ArrayExpression', elements: [ {type: 'Literal', value: 1}, {type: 'Literal', value: 2}, {type: 'Literal', value: 3}, {type: 'Literal', value: 4} ] } }] }; tree = replace(tree, { leave(node) { if (node.type === 'Literal') { return this.remove(); } } }); expect(tree).to.be.eql({ type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'ArrayExpression', elements: [] } }] }); tree = { type: 'FunctionExpression', id: { type: 'Identifier', name: 'foo' }, params: [], defaults: [], body: { type: 'BlockStatement', body: [{ type: 'ExpressionStatement', expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'debug' }, arguments: [{ type: 'Literal', value: 'foo', raw: '"foo"' }] } }, { type: 'ExpressionStatement', expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'debug' }, arguments: [{ type: 'Literal', value: 'bar', raw: '"bar"' }] } }] }, rest: null, generator: false, expression: false }; tree = replace(tree, { leave(node) { if (node.type === 'ExpressionStatement' && node.expression.type === 'CallExpression' && node.expression.callee.name === 'debug') { this.remove(); } } }); expect(tree).to.be.eql({ type: 'FunctionExpression', id: { type: 'Identifier', name: 'foo' }, params: [], defaults: [], body: { type: 'BlockStatement', body: [] }, rest: null, generator: false, expression: false }); }); it('respects skip', function() { let tree = { type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'BinaryExpression', operator: '*', left: { type: 'Literal', value: 2 }, right: { type: 'Literal', value: 3 } } }] }; const old = JSON.parse(JSON.stringify(tree)); tree = replace(tree, { enter(node) { switch (node.type) { case 'ObjectExpression': return VisitorOption.Skip; case 'BinaryExpression': return VisitorOption.Remove; } } }); expect(tree).to.be.eql(old); }); it('can remove unknown nodes', function() { let tree = { type: 'XXXExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'ArrayExpression', elements: [ {type: 'Literal', value: 1}, {type: 'Literal', value: 2}, {type: 'Literal', value: 3}, {type: 'Literal', value: 4} ] } }] }; tree = replace(tree, { enter(node) { if (node.type === 'Identifier' && node.name === 'a') { this.remove(); } if (node.type === 'Literal' && node.value === 2) { return VisitorOption.Remove; } }, fallback: 'iteration' }); expect(tree).to.be.eql({ type: 'XXXExpression', properties: [{ type: 'Property', key: null, value: { type: 'ArrayExpression', elements: [ {type: 'Literal', value: 1}, {type: 'Literal', value: 3}, {type: 'Literal', value: 4} ] } }] }); }); it('throw unkown node type error when unknown nodes', function() { const tree = { type: 'XXXExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'ArrayExpression', elements: [ {type: 'Literal', value: 1}, {type: 'Literal', value: 2}, {type: 'Literal', value: 3}, {type: 'Literal', value: 4} ] } }] }; expect(() => replace(tree, { enter(node) { if (node.type === 'Identifier' && node.name === 'a') { this.remove(); } if (node.type === 'Literal' && node.value === 2) { return VisitorOption.Remove; } } }) ).to.throw('Unknown node type XXXExpression.'); }); }); estraverse-4.2.0/test/third_party/000077500000000000000000000000001267036617700172275ustar00rootroot00000000000000estraverse-4.2.0/test/third_party/esprima.js000066400000000000000000005255071267036617700212430ustar00rootroot00000000000000/* Copyright (C) 2013 Ariya Hidayat Copyright (C) 2013 Thaddee Tyl Copyright (C) 2012 Ariya Hidayat Copyright (C) 2012 Mathias Bynens Copyright (C) 2012 Joost-Wim Boekesteijn Copyright (C) 2012 Kris Kowal Copyright (C) 2012 Yusuke Suzuki Copyright (C) 2012 Arpad Borsos Copyright (C) 2011 Ariya Hidayat 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 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. */ (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, ClassPropertyType, 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, Template: 10 }; TokenName = {}; TokenName[Token.BooleanLiteral] = 'Boolean'; TokenName[Token.EOF] = ''; 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 = { ArrayExpression: 'ArrayExpression', ArrayPattern: 'ArrayPattern', ArrowFunctionExpression: 'ArrowFunctionExpression', AssignmentExpression: 'AssignmentExpression', BinaryExpression: 'BinaryExpression', BlockStatement: 'BlockStatement', BreakStatement: 'BreakStatement', CallExpression: 'CallExpression', CatchClause: 'CatchClause', ClassBody: 'ClassBody', ClassDeclaration: 'ClassDeclaration', ClassExpression: 'ClassExpression', ComprehensionBlock: 'ComprehensionBlock', ComprehensionExpression: 'ComprehensionExpression', ConditionalExpression: 'ConditionalExpression', ContinueStatement: 'ContinueStatement', DebuggerStatement: 'DebuggerStatement', DoWhileStatement: 'DoWhileStatement', EmptyStatement: 'EmptyStatement', ExportDeclaration: 'ExportDeclaration', ExportBatchSpecifier: 'ExportBatchSpecifier', ExportSpecifier: 'ExportSpecifier', ExpressionStatement: 'ExpressionStatement', ForInStatement: 'ForInStatement', ForOfStatement: 'ForOfStatement', ForStatement: 'ForStatement', FunctionDeclaration: 'FunctionDeclaration', FunctionExpression: 'FunctionExpression', Identifier: 'Identifier', IfStatement: 'IfStatement', ImportDeclaration: 'ImportDeclaration', ImportDefaultSpecifier: 'ImportDefaultSpecifier', ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', ImportSpecifier: 'ImportSpecifier', LabeledStatement: 'LabeledStatement', Literal: 'Literal', LogicalExpression: 'LogicalExpression', MemberExpression: 'MemberExpression', MethodDefinition: 'MethodDefinition', NewExpression: 'NewExpression', ObjectExpression: 'ObjectExpression', ObjectPattern: 'ObjectPattern', Program: 'Program', Property: 'Property', ReturnStatement: 'ReturnStatement', SequenceExpression: 'SequenceExpression', SpreadElement: 'SpreadElement', SwitchCase: 'SwitchCase', SwitchStatement: 'SwitchStatement', 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' }; PropertyKind = { Data: 1, Get: 2, Set: 4 }; ClassPropertyType = { 'static': 'static', prototype: 'prototype' }; // 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', UnexpectedTemplate: 'Unexpected quasi %0', 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', InvalidLHSInFormalsList: 'Invalid left-hand side in formals list', 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', IllegalDuplicateClassProperty: 'Illegal duplicate property in class definition', IllegalClassConstructorProperty: 'Illegal constructor property in class definition', IllegalReturn: 'Illegal return statement', IllegalYield: 'Illegal yield expression', IllegalSpread: 'Illegal spread element', 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', ParameterAfterRestParameter: 'Rest parameter must be final parameter of an argument list', DefaultRestParameter: 'Rest parameter can not have a default value', ElementAfterSpreadElement: 'Spread must be the final element of an element list', ObjectPatternAsRestParameter: 'Invalid rest parameter', ObjectPatternAsSpread: 'Invalid spread argument', 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', MissingFromClause: 'Missing from clause', NoAsAfterImportNamespace: 'Missing as after import *', InvalidModuleSpecifier: 'Invalid module specifier', IllegalImportDeclaration: 'Illegal import declaration', IllegalExportDeclaration: 'Illegal export declaration', NoUninitializedConst: 'Const must be initialized', ComprehensionRequiresBlock: 'Comprehension must have at least one block', ComprehensionError: 'Comprehension Error', EachNotAllowed: 'Each is not supported' }; // 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-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'), 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\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]') }; // 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 StringMap() { this.$data = {}; } StringMap.prototype.get = function (key) { key = '$' + key; return this.$data[key]; }; StringMap.prototype.set = function (key, value) { key = '$' + key; this.$data[key] = value; return this; }; StringMap.prototype.has = function (key) { key = '$' + key; return Object.prototype.hasOwnProperty.call(this.$data, key); }; StringMap.prototype.delete = function (key) { key = '$' + key; return delete this.$data[key]; }; 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 === 32) || // space (ch === 9) || // tab (ch === 0xB) || (ch === 0xC) || (ch === 0xA0) || (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0); } // 7.3 Line Terminators function isLineTerminator(ch) { return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029); } // 7.6 Identifier Names and Identifiers function isIdentifierStart(ch) { return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) (ch >= 65 && ch <= 90) || // A..Z (ch >= 97 && ch <= 122) || // a..z (ch === 92) || // \ (backslash) ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch))); } function isIdentifierPart(ch) { return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) (ch >= 65 && ch <= 90) || // A..Z (ch >= 97 && ch <= 122) || // a..z (ch >= 48 && ch <= 57) || // 0..9 (ch === 92) || // \ (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' is only treated as a keyword in strict mode. // 'let' is 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 === '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; 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() { var start, loc, ch, comment; start = index - 2; loc = { start: { line: lineNumber, column: index - lineStart - 2 } }; while (index < length) { ch = source.charCodeAt(index); ++index; if (isLineTerminator(ch)) { if (extra.comments) { comment = source.slice(start + 2, 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 + 2, 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 === 13 && source.charCodeAt(index + 1) === 10) { ++index; } ++lineNumber; ++index; lineStart = index; if (index >= length) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } } else if (ch === 42) { // Block comment ends with '*/' (char #42, char #47). if (source.charCodeAt(index + 1) === 47) { ++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; while (index < length) { ch = source.charCodeAt(index); if (isWhiteSpace(ch)) { ++index; } else if (isLineTerminator(ch)) { ++index; if (ch === 13 && source.charCodeAt(index) === 10) { ++index; } ++lineNumber; lineStart = index; } else if (ch === 47) { // 47 is '/' ch = source.charCodeAt(index + 1); if (ch === 47) { ++index; ++index; skipSingleLineComment(); } else if (ch === 42) { // 42 is '*' ++index; ++index; skipMultiLineComment(); } 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 scanUnicodeCodePointEscape() { var ch, code, cu1, cu2; ch = source[index]; code = 0; // At least, one hex digit is required. if (ch === '}') { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } while (index < length) { ch = source[index++]; if (!isHexDigit(ch)) { break; } code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); } if (code > 0x10FFFF || ch !== '}') { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } // UTF-16 Encoding if (code <= 0xFFFF) { return String.fromCharCode(code); } cu1 = ((code - 0x10000) >> 10) + 0xD800; cu2 = ((code - 0x10000) & 1023) + 0xDC00; return String.fromCharCode(cu1, cu2); } function getEscapedIdentifier() { var ch, id; ch = source.charCodeAt(index++); id = String.fromCharCode(ch); // '\u' (char #92, char #117) denotes an escaped character. if (ch === 92) { if (source.charCodeAt(index) !== 117) { 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' (char #92, char #117) denotes an escaped character. if (ch === 92) { id = id.substr(0, id.length - 1); if (source.charCodeAt(index) !== 117) { 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 === 92) { // Blackslash (char #92) 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 (char #92) starts an escaped character. id = (source.charCodeAt(index) === 92) ? 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, range: [start, 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 40: // ( open bracket case 41: // ) close bracket case 59: // ; semicolon case 44: // , comma case 123: // { open curly brace case 125: // } close curly brace case 91: // [ case 93: // ] case 58: // : case 63: // ? case 126: // ~ ++index; if (extra.tokenize) { if (code === 40) { extra.openParenToken = extra.tokens.length; } else if (code === 123) { extra.openCurlyToken = extra.tokens.length; } } return { type: Token.Punctuator, value: String.fromCharCode(code), lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; default: code2 = source.charCodeAt(index + 1); // '=' (char #61) marks an assignment or comparison operator. if (code2 === 61) { switch (code) { case 37: // % case 38: // & case 42: // *: case 43: // + case 45: // - case 47: // / case 60: // < case 62: // > case 94: // ^ case 124: // | index += 2; return { type: Token.Punctuator, value: String.fromCharCode(code) + String.fromCharCode(code2), lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; case 33: // ! case 61: // = index += 2; // !== and === if (source.charCodeAt(index) === 61) { ++index; } return { type: Token.Punctuator, value: source.slice(start, index), lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; default: break; } } break; } // Peek more characters. ch2 = source[index + 1]; ch3 = source[index + 2]; ch4 = source[index + 3]; // 4-character punctuator: >>>= if (ch1 === '>' && ch2 === '>' && ch3 === '>') { if (ch4 === '=') { index += 4; return { type: Token.Punctuator, value: '>>>=', lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } } // 3-character punctuators: === !== >>> <<= >>= if (ch1 === '>' && ch2 === '>' && ch3 === '>') { index += 3; return { type: Token.Punctuator, value: '>>>', lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } if (ch1 === '<' && ch2 === '<' && ch3 === '=') { index += 3; return { type: Token.Punctuator, value: '<<=', lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } if (ch1 === '>' && ch2 === '>' && ch3 === '=') { index += 3; return { type: Token.Punctuator, value: '>>=', lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } if (ch1 === '.' && ch2 === '.' && ch3 === '.') { index += 3; return { type: Token.Punctuator, value: '...', lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } // Other 2-character punctuators: ++ -- << >> && || if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) { index += 2; return { type: Token.Punctuator, value: ch1 + ch2, lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } if (ch1 === '=' && ch2 === '>') { index += 2; return { type: Token.Punctuator, value: '=>', lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { ++index; return { type: Token.Punctuator, value: ch1, lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } if (ch1 === '.') { ++index; return { type: Token.Punctuator, value: ch1, lineNumber: lineNumber, lineStart: lineStart, range: [start, 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, range: [start, index] }; } function scanBinaryLiteral(start) { var ch, number; number = ''; while (index < length) { ch = source[index]; if (ch !== '0' && ch !== '1') { break; } number += source[index++]; } if (number.length === 0) { // only 0b or 0B throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } if (index < length) { ch = source.charCodeAt(index); /* istanbul ignore else */ if (isIdentifierStart(ch) || isDecimalDigit(ch)) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } } return { type: Token.NumericLiteral, value: parseInt(number, 2), lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } function scanOctalLiteral(prefix, start) { var number, octal; if (isOctalDigit(prefix)) { octal = true; number = '0' + source[index++]; } else { octal = false; ++index; number = ''; } while (index < length) { if (!isOctalDigit(source[index])) { break; } number += source[index++]; } if (!octal && number.length === 0) { // only 0o or 0O throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } return { type: Token.NumericLiteral, value: parseInt(number, 8), octal: octal, lineNumber: lineNumber, lineStart: lineStart, range: [start, 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'. // Octal number in ES6 starts with '0o'. // Binary number in ES6 starts with '0b'. if (number === '0') { if (ch === 'x' || ch === 'X') { ++index; return scanHexLiteral(start); } if (ch === 'b' || ch === 'B') { ++index; return scanBinaryLiteral(start); } if (ch === 'o' || ch === 'O' || isOctalDigit(ch)) { return scanOctalLiteral(ch, 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, range: [start, index] }; } // 7.8.4 String Literals function scanStringLiteral() { var str = '', quote, start, ch, code, unescaped, restore, octal = false; 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 'n': str += '\n'; break; case 'r': str += '\r'; break; case 't': str += '\t'; break; case 'u': case 'x': if (source[index] === '{') { ++index; str += scanUnicodeCodePointEscape(); } else { restore = index; unescaped = scanHexEscape(ch); if (unescaped) { str += unescaped; } else { index = restore; str += ch; } } 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; } /* istanbul ignore else */ 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, lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } function scanTemplate() { var cooked = '', ch, start, terminated, tail, restore, unescaped, code, octal; terminated = false; tail = false; start = index; ++index; while (index < length) { ch = source[index++]; if (ch === '`') { tail = true; terminated = true; break; } else if (ch === '$') { if (source[index] === '{') { ++index; terminated = true; break; } cooked += ch; } else if (ch === '\\') { ch = source[index++]; if (!isLineTerminator(ch.charCodeAt(0))) { switch (ch) { case 'n': cooked += '\n'; break; case 'r': cooked += '\r'; break; case 't': cooked += '\t'; break; case 'u': case 'x': if (source[index] === '{') { ++index; cooked += scanUnicodeCodePointEscape(); } else { restore = index; unescaped = scanHexEscape(ch); if (unescaped) { cooked += unescaped; } else { index = restore; cooked += ch; } } break; case 'b': cooked += '\b'; break; case 'f': cooked += '\f'; break; case 'v': cooked += '\v'; break; default: if (isOctalDigit(ch)) { code = '01234567'.indexOf(ch); // \0 is not octal escape sequence if (code !== 0) { octal = true; } /* istanbul ignore else */ 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++]); } } cooked += String.fromCharCode(code); } else { cooked += ch; } break; } } else { ++lineNumber; if (ch === '\r' && source[index] === '\n') { ++index; } lineStart = index; } } else if (isLineTerminator(ch.charCodeAt(0))) { ++lineNumber; if (ch === '\r' && source[index] === '\n') { ++index; } lineStart = index; cooked += '\n'; } else { cooked += ch; } } if (!terminated) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } return { type: Token.Template, value: { cooked: cooked, raw: source.slice(start + 1, index - ((tail) ? 1 : 2)) }, tail: tail, octal: octal, lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } function scanTemplateElement(option) { var startsWith, template; lookahead = null; skipComment(); startsWith = (option.head) ? '`' : '}'; if (source[index] !== startsWith) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } template = scanTemplate(); peek(); return template; } function testRegExp(pattern, flags) { var tmp = pattern, value; if (flags.indexOf('u') >= 0) { // Replace each astral symbol and every Unicode code point // escape sequence with a single ASCII symbol to avoid throwing on // regular expressions that are only valid in combination with the // `/u` flag. // Note: replacing with the ASCII symbol `x` might cause false // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a // perfectly valid pattern that is equivalent to `[a-b]`, but it // would be replaced by `[x-b]` which throws an error. tmp = tmp .replace(/\\u\{([0-9a-fA-F]+)\}/g, function ($0, $1) { if (parseInt($1, 16) <= 0x10FFFF) { return 'x'; } throwError({}, Messages.InvalidRegExp); }) .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, 'x'); } // First, detect invalid regular expressions. try { value = new RegExp(tmp); } catch (e) { throwError({}, Messages.InvalidRegExp); } // Return a regular expression object for this pattern-flag pair, or // `null` in case the current environment doesn't support the flags it // uses. try { return new RegExp(pattern, flags); } catch (exception) { return null; } } 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, 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, regex: { pattern: body.value, flags: flags.value }, lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } return { literal: body.literal + flags.literal, value: value, regex: { pattern: body.value, flags: flags.value }, range: [start, index] }; } 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 scanRegExp(); } if (prevToken.type === 'Punctuator') { 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 scanRegExp(); } 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 scanRegExp(); } } 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 scanRegExp(); } return scanRegExp(); } if (prevToken.type === 'Keyword' && prevToken.value !== 'this') { return scanRegExp(); } return scanPunctuator(); } function advance() { var ch; skipComment(); if (index >= length) { return { type: Token.EOF, lineNumber: lineNumber, lineStart: lineStart, range: [index, index] }; } ch = source.charCodeAt(index); // Very common: ( and ) and ; if (ch === 40 || ch === 41 || ch === 58) { return scanPunctuator(); } // String literal starts with single quote (#39) or double quote (#34). if (ch === 39 || ch === 34) { return scanStringLiteral(); } if (ch === 96) { return scanTemplate(); } if (isIdentifierStart(ch)) { return scanIdentifier(); } // Dot (.) char #46 can also start a floating-point number, hence the need // to check the next character. if (ch === 46) { if (isDecimalDigit(source.charCodeAt(index + 1))) { return scanNumericLiteral(); } return scanPunctuator(); } if (isDecimalDigit(ch)) { return scanNumericLiteral(); } // Slash (/) char #47 can also start a regex. if (extra.tokenize && ch === 47) { return advanceSlash(); } return scanPunctuator(); } function lex() { var token; token = lookahead; index = token.range[1]; lineNumber = token.lineNumber; lineStart = token.lineStart; lookahead = advance(); index = token.range[1]; lineNumber = token.lineNumber; lineStart = token.lineStart; return token; } function peek() { var pos, line, start; pos = index; line = lineNumber; start = lineStart; lookahead = advance(); index = pos; lineNumber = line; lineStart = start; } function lookahead2() { var adv, pos, line, start, result; // If we are collecting the tokens, don't grab the next one yet. /* istanbul ignore next */ adv = (typeof extra.advance === 'function') ? extra.advance : advance; pos = index; line = lineNumber; start = lineStart; // Scan for the next immediate token. /* istanbul ignore if */ if (lookahead === null) { lookahead = adv(); } index = lookahead.range[1]; lineNumber = lookahead.lineNumber; lineStart = lookahead.lineStart; // Grab the token right after. result = adv(); index = pos; lineNumber = line; lineStart = start; return result; } function markerCreate() { if (!extra.loc && !extra.range) { return undefined; } skipComment(); return {offset: index, line: lineNumber, col: index - lineStart}; } function processComment(node) { var lastChild, trailingComments, bottomRight = extra.bottomRightStack, last = bottomRight[bottomRight.length - 1]; if (node.type === Syntax.Program) { /* istanbul ignore else */ 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 (last && last.trailingComments && last.trailingComments[0].range[0] >= node.range[1]) { trailingComments = last.trailingComments; delete last.trailingComments; } } // Eating the stack. if (last) { while (last && last.range[0] >= node.range[0]) { lastChild = last; last = bottomRight.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; } bottomRight.push(node); } function markerApply(marker, node) { if (extra.range) { node.range = [marker.offset, index]; } if (extra.loc) { node.loc = { start: { line: marker.line, column: marker.col }, end: { line: lineNumber, column: index - lineStart } }; node = delegate.postProcess(node); } if (extra.attachComment) { processComment(node); } return node; } SyntaxTreeDelegate = { name: 'SyntaxTree', postProcess: function (node) { 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 }; }, createForOfStatement: function (left, right, body) { return { type: Syntax.ForOfStatement, left: left, right: right, body: body }; }, createFunctionDeclaration: function (id, params, defaults, body, rest, generator, expression) { return { type: Syntax.FunctionDeclaration, id: id, params: params, defaults: defaults, body: body, rest: rest, generator: generator, expression: expression }; }, createFunctionExpression: function (id, params, defaults, body, rest, generator, expression) { return { type: Syntax.FunctionExpression, id: id, params: params, defaults: defaults, body: body, rest: rest, generator: generator, expression: expression }; }, 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) { var object = { type: Syntax.Literal, value: token.value, raw: source.slice(token.range[0], token.range[1]) }; if (token.regex) { object.regex = token.regex; } return object; }, 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, method, shorthand, computed) { return { type: Syntax.Property, key: key, value: value, kind: kind, method: method, shorthand: shorthand, computed: computed }; }, 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 }; }, createTemplateElement: function (value, tail) { return { type: Syntax.TemplateElement, value: value, tail: tail }; }, createTemplateLiteral: function (quasis, expressions) { return { type: Syntax.TemplateLiteral, quasis: quasis, expressions: expressions }; }, createSpreadElement: function (argument) { return { type: Syntax.SpreadElement, argument: argument }; }, createTaggedTemplateExpression: function (tag, quasi) { return { type: Syntax.TaggedTemplateExpression, tag: tag, quasi: quasi }; }, createArrowFunctionExpression: function (params, defaults, body, rest, expression) { return { type: Syntax.ArrowFunctionExpression, id: null, params: params, defaults: defaults, body: body, rest: rest, generator: false, expression: expression }; }, createMethodDefinition: function (propertyType, kind, key, value, computed) { return { type: Syntax.MethodDefinition, key: key, value: value, kind: kind, 'static': propertyType === ClassPropertyType.static, computed: computed }; }, createClassBody: function (body) { return { type: Syntax.ClassBody, body: body }; }, createClassExpression: function (id, superClass, body) { return { type: Syntax.ClassExpression, id: id, superClass: superClass, body: body }; }, createClassDeclaration: function (id, superClass, body) { return { type: Syntax.ClassDeclaration, id: id, superClass: superClass, body: body }; }, createExportSpecifier: function (id, name) { return { type: Syntax.ExportSpecifier, id: id, name: name }; }, createExportBatchSpecifier: function () { return { type: Syntax.ExportBatchSpecifier }; }, createImportDefaultSpecifier: function (id) { return { type: Syntax.ImportDefaultSpecifier, id: id }; }, createImportNamespaceSpecifier: function (id) { return { type: Syntax.ImportNamespaceSpecifier, id: id }; }, createExportDeclaration: function (isDefault, declaration, specifiers, src) { return { type: Syntax.ExportDeclaration, 'default': !!isDefault, declaration: declaration, specifiers: specifiers, source: src }; }, createImportSpecifier: function (id, name) { return { type: Syntax.ImportSpecifier, id: id, name: name }; }, createImportDeclaration: function (specifiers, src) { return { type: Syntax.ImportDeclaration, specifiers: specifiers, source: src }; }, createYieldExpression: function (argument, dlg) { return { type: Syntax.YieldExpression, argument: argument, delegate: dlg }; }, createComprehensionExpression: function (filter, blocks, body) { return { type: Syntax.ComprehensionExpression, filter: filter, blocks: blocks, 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, idx) { assert(idx < args.length, 'Message reference must be in range'); return args[idx]; } ); if (typeof token.lineNumber === 'number') { error = new Error('Line ' + token.lineNumber + ': ' + msg); error.index = token.range[0]; error.lineNumber = token.lineNumber; error.column = token.range[0] - 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); } if (token.type === Token.Template) { throwError(token, Messages.UnexpectedTemplate, token.value.raw); } // 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 matches the specified contextual keyword function matchContextualKeyword(keyword) { return lookahead.type === Token.Identifier && 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, oldIndex = index, oldLineNumber = lineNumber, oldLineStart = lineStart, oldLookahead = lookahead; // Catch the very common case first: immediately a semicolon (char #59). if (source.charCodeAt(index) === 59) { lex(); return; } line = lineNumber; skipComment(); if (lineNumber !== line) { index = oldIndex; lineNumber = oldLineNumber; lineStart = oldLineStart; lookahead = oldLookahead; return; } if (match(';')) { lex(); 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; } function isAssignableLeftHandSide(expr) { return isLeftHandSide(expr) || expr.type === Syntax.ObjectPattern || expr.type === Syntax.ArrayPattern; } // 11.1.4 Array Initialiser function parseArrayInitialiser() { var elements = [], blocks = [], filter = null, tmp, possiblecomprehension = true, marker = markerCreate(); expect('['); while (!match(']')) { if (lookahead.value === 'for' && lookahead.type === Token.Keyword) { if (!possiblecomprehension) { throwError({}, Messages.ComprehensionError); } matchKeyword('for'); tmp = parseForStatement({ignoreBody: true}); tmp.of = tmp.type === Syntax.ForOfStatement; tmp.type = Syntax.ComprehensionBlock; if (tmp.left.kind) { // can't be let or const throwError({}, Messages.ComprehensionError); } blocks.push(tmp); } else if (lookahead.value === 'if' && lookahead.type === Token.Keyword) { if (!possiblecomprehension) { throwError({}, Messages.ComprehensionError); } expectKeyword('if'); expect('('); filter = parseExpression(); expect(')'); } else if (lookahead.value === ',' && lookahead.type === Token.Punctuator) { possiblecomprehension = false; // no longer allowed. lex(); elements.push(null); } else { tmp = parseSpreadOrAssignmentExpression(); elements.push(tmp); if (tmp && tmp.type === Syntax.SpreadElement) { if (!match(']')) { throwError({}, Messages.ElementAfterSpreadElement); } } else if (!(match(']') || matchKeyword('for') || matchKeyword('if'))) { expect(','); // this lexes. possiblecomprehension = false; } } } expect(']'); if (filter && !blocks.length) { throwError({}, Messages.ComprehensionRequiresBlock); } if (blocks.length) { if (elements.length !== 1) { throwError({}, Messages.ComprehensionError); } return markerApply(marker, delegate.createComprehensionExpression(filter, blocks, elements[0])); } return markerApply(marker, delegate.createArrayExpression(elements)); } // 11.1.5 Object Initialiser function parsePropertyFunction(options) { var previousStrict, previousYieldAllowed, params, defaults, body, marker = markerCreate(); previousStrict = strict; previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = options.generator; params = options.params || []; defaults = options.defaults || []; body = parseConciseBody(); if (options.name && strict && isRestrictedWord(params[0].name)) { throwErrorTolerant(options.name, Messages.StrictParamName); } strict = previousStrict; state.yieldAllowed = previousYieldAllowed; return markerApply(marker, delegate.createFunctionExpression( null, params, defaults, body, options.rest || null, options.generator, body.type !== Syntax.BlockStatement )); } function parsePropertyMethodFunction(options) { var previousStrict, tmp, method; previousStrict = strict; strict = true; tmp = parseParams(); if (tmp.stricted) { throwErrorTolerant(tmp.stricted, tmp.message); } method = parsePropertyFunction({ params: tmp.params, defaults: tmp.defaults, rest: tmp.rest, generator: options.generator }); strict = previousStrict; return method; } function parseObjectPropertyKey() { var marker = markerCreate(), token = lex(), propertyKey, result; // 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 markerApply(marker, delegate.createLiteral(token)); } if (token.type === Token.Punctuator && token.value === '[') { // For computed properties we should skip the [ and ], and // capture in marker only the assignment expression itself. marker = markerCreate(); propertyKey = parseAssignmentExpression(); result = markerApply(marker, propertyKey); expect(']'); return result; } return markerApply(marker, delegate.createIdentifier(token.value)); } function parseObjectProperty() { var token, key, id, param, computed, marker = markerCreate(); token = lookahead; computed = (token.value === '[' && token.type === Token.Punctuator); if (token.type === Token.Identifier || computed) { id = parseObjectPropertyKey(); // Property Assignment: Getter and Setter. if (token.value === 'get' && !(match(':') || match('('))) { computed = (lookahead.value === '['); key = parseObjectPropertyKey(); expect('('); expect(')'); return markerApply(marker, delegate.createProperty('get', key, parsePropertyFunction({ generator: false }), false, false, computed)); } if (token.value === 'set' && !(match(':') || match('('))) { computed = (lookahead.value === '['); key = parseObjectPropertyKey(); expect('('); token = lookahead; param = [ parseVariableIdentifier() ]; expect(')'); return markerApply(marker, delegate.createProperty('set', key, parsePropertyFunction({ params: param, generator: false, name: token }), false, false, computed)); } if (match(':')) { lex(); return markerApply(marker, delegate.createProperty('init', id, parseAssignmentExpression(), false, false, computed)); } if (match('(')) { return markerApply(marker, delegate.createProperty('init', id, parsePropertyMethodFunction({ generator: false }), true, false, computed)); } if (computed) { // Computed properties can only be used with full notation. throwUnexpected(lookahead); } return markerApply(marker, delegate.createProperty('init', id, id, false, true, false)); } if (token.type === Token.EOF || token.type === Token.Punctuator) { if (!match('*')) { throwUnexpected(token); } lex(); computed = (lookahead.type === Token.Punctuator && lookahead.value === '['); id = parseObjectPropertyKey(); if (!match('(')) { throwUnexpected(lex()); } return markerApply(marker, delegate.createProperty('init', id, parsePropertyMethodFunction({ generator: true }), true, false, computed)); } key = parseObjectPropertyKey(); if (match(':')) { lex(); return markerApply(marker, delegate.createProperty('init', key, parseAssignmentExpression(), false, false, false)); } if (match('(')) { return markerApply(marker, delegate.createProperty('init', key, parsePropertyMethodFunction({ generator: false }), true, false, false)); } throwUnexpected(lex()); } function getFieldName(key) { var toString = String; if (key.type === Syntax.Identifier) { return key.name; } return toString(key.value); } function parseObjectInitialiser() { var properties = [], property, name, kind, storedKind, map = new StringMap(), marker = markerCreate(); expect('{'); while (!match('}')) { property = parseObjectProperty(); if (!property.computed) { name = getFieldName(property.key); kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set; if (map.has(name)) { storedKind = map.get(name); if (storedKind === 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 (storedKind & kind) { throwErrorTolerant({}, Messages.AccessorGetSet); } } map.set(name, storedKind | kind); } else { map.set(name, kind); } } properties.push(property); if (!match('}')) { expect(','); } } expect('}'); return markerApply(marker, delegate.createObjectExpression(properties)); } function parseTemplateElement(option) { var marker = markerCreate(), token = scanTemplateElement(option); if (strict && token.octal) { throwError(token, Messages.StrictOctalLiteral); } return markerApply(marker, delegate.createTemplateElement({ raw: token.value.raw, cooked: token.value.cooked }, token.tail)); } function parseTemplateLiteral() { var quasi, quasis, expressions, marker = markerCreate(); quasi = parseTemplateElement({ head: true }); quasis = [ quasi ]; expressions = []; while (!quasi.tail) { expressions.push(parseExpression()); quasi = parseTemplateElement({ head: false }); quasis.push(quasi); } return markerApply(marker, delegate.createTemplateLiteral(quasis, expressions)); } // 11.1.6 The Grouping Operator function parseGroupExpression() { var expr; expect('('); ++state.parenthesizedCount; expr = parseExpression(); expect(')'); return expr; } // 11.1 Primary Expressions function parsePrimaryExpression() { var marker, type, token, expr; type = lookahead.type; if (type === Token.Identifier) { marker = markerCreate(); return markerApply(marker, delegate.createIdentifier(lex().value)); } if (type === Token.StringLiteral || type === Token.NumericLiteral) { if (strict && lookahead.octal) { throwErrorTolerant(lookahead, Messages.StrictOctalLiteral); } marker = markerCreate(); return markerApply(marker, delegate.createLiteral(lex())); } if (type === Token.Keyword) { if (matchKeyword('this')) { marker = markerCreate(); lex(); return markerApply(marker, delegate.createThisExpression()); } if (matchKeyword('function')) { return parseFunctionExpression(); } if (matchKeyword('class')) { return parseClassExpression(); } if (matchKeyword('super')) { marker = markerCreate(); lex(); return markerApply(marker, delegate.createIdentifier('super')); } } if (type === Token.BooleanLiteral) { marker = markerCreate(); token = lex(); token.value = (token.value === 'true'); return markerApply(marker, delegate.createLiteral(token)); } if (type === Token.NullLiteral) { marker = markerCreate(); token = lex(); token.value = null; return markerApply(marker, delegate.createLiteral(token)); } if (match('[')) { return parseArrayInitialiser(); } if (match('{')) { return parseObjectInitialiser(); } if (match('(')) { return parseGroupExpression(); } if (match('/') || match('/=')) { marker = markerCreate(); expr = delegate.createLiteral(scanRegExp()); peek(); return markerApply(marker, expr); } if (type === Token.Template) { return parseTemplateLiteral(); } throwUnexpected(lex()); } // 11.2 Left-Hand-Side Expressions function parseArguments() { var args = [], arg; expect('('); if (!match(')')) { while (index < length) { arg = parseSpreadOrAssignmentExpression(); args.push(arg); if (match(')')) { break; } else if (arg.type === Syntax.SpreadElement) { throwError({}, Messages.ElementAfterSpreadElement); } expect(','); } } expect(')'); return args; } function parseSpreadOrAssignmentExpression() { if (match('...')) { var marker = markerCreate(); lex(); return markerApply(marker, delegate.createSpreadElement(parseAssignmentExpression())); } return parseAssignmentExpression(); } function parseNonComputedProperty() { var marker = markerCreate(), token = lex(); if (!isIdentifierName(token)) { throwUnexpected(token); } return markerApply(marker, delegate.createIdentifier(token.value)); } function parseNonComputedMember() { expect('.'); return parseNonComputedProperty(); } function parseComputedMember() { var expr; expect('['); expr = parseExpression(); expect(']'); return expr; } function parseNewExpression() { var callee, args, marker = markerCreate(); expectKeyword('new'); callee = parseLeftHandSideExpression(); args = match('(') ? parseArguments() : []; return markerApply(marker, delegate.createNewExpression(callee, args)); } function parseLeftHandSideExpressionAllowCall() { var expr, args, marker = markerCreate(); expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); while (match('.') || match('[') || match('(') || lookahead.type === Token.Template) { if (match('(')) { args = parseArguments(); expr = markerApply(marker, delegate.createCallExpression(expr, args)); } else if (match('[')) { expr = markerApply(marker, delegate.createMemberExpression('[', expr, parseComputedMember())); } else if (match('.')) { expr = markerApply(marker, delegate.createMemberExpression('.', expr, parseNonComputedMember())); } else { expr = markerApply(marker, delegate.createTaggedTemplateExpression(expr, parseTemplateLiteral())); } } return expr; } function parseLeftHandSideExpression() { var expr, marker = markerCreate(); expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); while (match('.') || match('[') || lookahead.type === Token.Template) { if (match('[')) { expr = markerApply(marker, delegate.createMemberExpression('[', expr, parseComputedMember())); } else if (match('.')) { expr = markerApply(marker, delegate.createMemberExpression('.', expr, parseNonComputedMember())); } else { expr = markerApply(marker, delegate.createTaggedTemplateExpression(expr, parseTemplateLiteral())); } } return expr; } // 11.3 Postfix Expressions function parsePostfixExpression() { var marker = markerCreate(), expr = parseLeftHandSideExpressionAllowCall(), token; if (lookahead.type !== Token.Punctuator) { return expr; } 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)) { throwError({}, Messages.InvalidLHSInAssignment); } token = lex(); expr = markerApply(marker, delegate.createPostfixExpression(token.value, expr)); } return expr; } // 11.4 Unary Operators function parseUnaryExpression() { var marker, token, expr; if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { return parsePostfixExpression(); } if (match('++') || match('--')) { marker = markerCreate(); 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)) { throwError({}, Messages.InvalidLHSInAssignment); } return markerApply(marker, delegate.createUnaryExpression(token.value, expr)); } if (match('+') || match('-') || match('~') || match('!')) { marker = markerCreate(); token = lex(); expr = parseUnaryExpression(); return markerApply(marker, delegate.createUnaryExpression(token.value, expr)); } if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { marker = markerCreate(); token = lex(); expr = parseUnaryExpression(); expr = markerApply(marker, delegate.createUnaryExpression(token.value, expr)); if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { throwErrorTolerant({}, Messages.StrictDelete); } return expr; } return parsePostfixExpression(); } 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 expr, token, prec, previousAllowIn, stack, right, operator, left, i, marker, markers; previousAllowIn = state.allowIn; state.allowIn = true; marker = markerCreate(); left = parseUnaryExpression(); token = lookahead; prec = binaryPrecedence(token, previousAllowIn); if (prec === 0) { return left; } token.prec = prec; lex(); markers = [marker, markerCreate()]; right = parseUnaryExpression(); stack = [left, token, right]; while ((prec = binaryPrecedence(lookahead, previousAllowIn)) > 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.pop(); markerApply(marker, expr); stack.push(expr); markers.push(marker); } // Shift. token = lex(); token.prec = prec; stack.push(token); markers.push(markerCreate()); expr = parseUnaryExpression(); stack.push(expr); } state.allowIn = previousAllowIn; // 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(); markerApply(marker, expr); } return expr; } // 11.12 Conditional Operator function parseConditionalExpression() { var expr, previousAllowIn, consequent, alternate, marker = markerCreate(); expr = parseBinaryExpression(); if (match('?')) { lex(); previousAllowIn = state.allowIn; state.allowIn = true; consequent = parseAssignmentExpression(); state.allowIn = previousAllowIn; expect(':'); alternate = parseAssignmentExpression(); expr = markerApply(marker, delegate.createConditionalExpression(expr, consequent, alternate)); } return expr; } // 11.13 Assignment Operators // 12.14.5 AssignmentPattern function reinterpretAsAssignmentBindingPattern(expr) { var i, len, property, element; if (expr.type === Syntax.ObjectExpression) { expr.type = Syntax.ObjectPattern; for (i = 0, len = expr.properties.length; i < len; i += 1) { property = expr.properties[i]; if (property.kind !== 'init') { throwError({}, Messages.InvalidLHSInAssignment); } reinterpretAsAssignmentBindingPattern(property.value); } } else if (expr.type === Syntax.ArrayExpression) { expr.type = Syntax.ArrayPattern; for (i = 0, len = expr.elements.length; i < len; i += 1) { element = expr.elements[i]; /* istanbul ignore else */ if (element) { reinterpretAsAssignmentBindingPattern(element); } } } else if (expr.type === Syntax.Identifier) { if (isRestrictedWord(expr.name)) { throwError({}, Messages.InvalidLHSInAssignment); } } else if (expr.type === Syntax.SpreadElement) { reinterpretAsAssignmentBindingPattern(expr.argument); if (expr.argument.type === Syntax.ObjectPattern) { throwError({}, Messages.ObjectPatternAsSpread); } } else { /* istanbul ignore else */ if (expr.type !== Syntax.MemberExpression && expr.type !== Syntax.CallExpression && expr.type !== Syntax.NewExpression) { throwError({}, Messages.InvalidLHSInAssignment); } } } // 13.2.3 BindingPattern function reinterpretAsDestructuredParameter(options, expr) { var i, len, property, element; if (expr.type === Syntax.ObjectExpression) { expr.type = Syntax.ObjectPattern; for (i = 0, len = expr.properties.length; i < len; i += 1) { property = expr.properties[i]; if (property.kind !== 'init') { throwError({}, Messages.InvalidLHSInFormalsList); } reinterpretAsDestructuredParameter(options, property.value); } } else if (expr.type === Syntax.ArrayExpression) { expr.type = Syntax.ArrayPattern; for (i = 0, len = expr.elements.length; i < len; i += 1) { element = expr.elements[i]; if (element) { reinterpretAsDestructuredParameter(options, element); } } } else if (expr.type === Syntax.Identifier) { validateParam(options, expr, expr.name); } else if (expr.type === Syntax.SpreadElement) { // BindingRestElement only allows BindingIdentifier if (expr.argument.type !== Syntax.Identifier) { throwError({}, Messages.InvalidLHSInFormalsList); } validateParam(options, expr.argument, expr.argument.name); } else { throwError({}, Messages.InvalidLHSInFormalsList); } } function reinterpretAsCoverFormalsList(expressions) { var i, len, param, params, defaults, defaultCount, options, rest; params = []; defaults = []; defaultCount = 0; rest = null; options = { paramSet: new StringMap() }; for (i = 0, len = expressions.length; i < len; i += 1) { param = expressions[i]; if (param.type === Syntax.Identifier) { params.push(param); defaults.push(null); validateParam(options, param, param.name); } else if (param.type === Syntax.ObjectExpression || param.type === Syntax.ArrayExpression) { reinterpretAsDestructuredParameter(options, param); params.push(param); defaults.push(null); } else if (param.type === Syntax.SpreadElement) { assert(i === len - 1, 'It is guaranteed that SpreadElement is last element by parseExpression'); if (param.argument.type !== Syntax.Identifier) { throwError({}, Messages.InvalidLHSInFormalsList); } reinterpretAsDestructuredParameter(options, param.argument); rest = param.argument; } else if (param.type === Syntax.AssignmentExpression) { params.push(param.left); defaults.push(param.right); ++defaultCount; validateParam(options, param.left, param.left.name); } else { return null; } } if (options.message === Messages.StrictParamDupe) { throwError( strict ? options.stricted : options.firstRestricted, options.message ); } if (defaultCount === 0) { defaults = []; } return { params: params, defaults: defaults, rest: rest, stricted: options.stricted, firstRestricted: options.firstRestricted, message: options.message }; } function parseArrowFunctionExpression(options, marker) { var previousStrict, previousYieldAllowed, body; expect('=>'); previousStrict = strict; previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = false; body = parseConciseBody(); if (strict && options.firstRestricted) { throwError(options.firstRestricted, options.message); } if (strict && options.stricted) { throwErrorTolerant(options.stricted, options.message); } strict = previousStrict; state.yieldAllowed = previousYieldAllowed; return markerApply(marker, delegate.createArrowFunctionExpression( options.params, options.defaults, body, options.rest, body.type !== Syntax.BlockStatement )); } function parseAssignmentExpression() { var marker, expr, token, params, oldParenthesizedCount, startsWithParen = false; // Note that 'yield' is treated as a keyword in strict mode, but a // contextual keyword (identifier) in non-strict mode, so we need // to use matchKeyword and matchContextualKeyword appropriately. if ((state.yieldAllowed && matchContextualKeyword('yield')) || (strict && matchKeyword('yield'))) { return parseYieldExpression(); } oldParenthesizedCount = state.parenthesizedCount; marker = markerCreate(); if (match('(')) { token = lookahead2(); if ((token.type === Token.Punctuator && token.value === ')') || token.value === '...') { params = parseParams(); if (!match('=>')) { throwUnexpected(lex()); } return parseArrowFunctionExpression(params, marker); } startsWithParen = true; } token = lookahead; expr = parseConditionalExpression(); if (match('=>') && (state.parenthesizedCount === oldParenthesizedCount || state.parenthesizedCount === (oldParenthesizedCount + 1))) { if (expr.type === Syntax.Identifier) { params = reinterpretAsCoverFormalsList([ expr ]); } else if (expr.type === Syntax.AssignmentExpression || expr.type === Syntax.ArrayExpression || expr.type === Syntax.ObjectExpression) { if (!startsWithParen) { throwUnexpected(lex()); } params = reinterpretAsCoverFormalsList([ expr ]); } else if (expr.type === Syntax.SequenceExpression) { params = reinterpretAsCoverFormalsList(expr.expressions); } if (params) { return parseArrowFunctionExpression(params, marker); } } if (matchAssign()) { // 11.13.1 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { throwErrorTolerant(token, Messages.StrictLHSAssignment); } // ES.next draf 11.13 Runtime Semantics step 1 if (match('=') && (expr.type === Syntax.ObjectExpression || expr.type === Syntax.ArrayExpression)) { reinterpretAsAssignmentBindingPattern(expr); } else if (!isLeftHandSide(expr)) { throwError({}, Messages.InvalidLHSInAssignment); } expr = markerApply(marker, delegate.createAssignmentExpression(lex().value, expr, parseAssignmentExpression())); } return expr; } // 11.14 Comma Operator function parseExpression() { var marker, expr, expressions, sequence, spreadFound; marker = markerCreate(); expr = parseAssignmentExpression(); expressions = [ expr ]; if (match(',')) { while (index < length) { if (!match(',')) { break; } lex(); expr = parseSpreadOrAssignmentExpression(); expressions.push(expr); if (expr.type === Syntax.SpreadElement) { spreadFound = true; if (!match(')')) { throwError({}, Messages.ElementAfterSpreadElement); } break; } } sequence = markerApply(marker, delegate.createSequenceExpression(expressions)); } if (spreadFound && lookahead2().value !== '=>') { throwError({}, Messages.IllegalSpread); } return sequence || 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, marker = markerCreate(); expect('{'); block = parseStatementList(); expect('}'); return markerApply(marker, delegate.createBlockStatement(block)); } // 12.2 Variable Statement function parseVariableIdentifier() { var marker = markerCreate(), token = lex(); if (token.type !== Token.Identifier) { throwUnexpected(token); } return markerApply(marker, delegate.createIdentifier(token.value)); } function parseVariableDeclaration(kind) { var id, marker = markerCreate(), init = null; if (match('{')) { id = parseObjectInitialiser(); reinterpretAsAssignmentBindingPattern(id); } else if (match('[')) { id = parseArrayInitialiser(); reinterpretAsAssignmentBindingPattern(id); } else { /* istanbul ignore next */ id = state.allowKeyword ? parseNonComputedProperty() : parseVariableIdentifier(); // 12.2.1 if (strict && isRestrictedWord(id.name)) { throwErrorTolerant({}, Messages.StrictVarName); } } if (kind === 'const') { if (!match('=')) { throwError({}, Messages.NoUninitializedConst); } expect('='); init = parseAssignmentExpression(); } else if (match('=')) { lex(); init = parseAssignmentExpression(); } return markerApply(marker, delegate.createVariableDeclarator(id, init)); } function parseVariableDeclarationList(kind) { var list = []; do { list.push(parseVariableDeclaration(kind)); if (!match(',')) { break; } lex(); } while (index < length); return list; } function parseVariableStatement() { var declarations, marker = markerCreate(); expectKeyword('var'); declarations = parseVariableDeclarationList(); consumeSemicolon(); return markerApply(marker, 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, marker = markerCreate(); expectKeyword(kind); declarations = parseVariableDeclarationList(kind); consumeSemicolon(); return markerApply(marker, delegate.createVariableDeclaration(declarations, kind)); } // people.mozilla.org/~jorendorff/es6-draft.html function parseModuleSpecifier() { var marker = markerCreate(), specifier; if (lookahead.type !== Token.StringLiteral) { throwError({}, Messages.InvalidModuleSpecifier); } specifier = delegate.createLiteral(lex()); return markerApply(marker, specifier); } function parseExportBatchSpecifier() { var marker = markerCreate(); expect('*'); return markerApply(marker, delegate.createExportBatchSpecifier()); } function parseExportSpecifier() { var id, name = null, marker = markerCreate(); if (matchKeyword('default')) { lex(); id = markerApply(marker, delegate.createIdentifier('default')); // export {default} from "something"; } else { id = parseVariableIdentifier(); } if (matchContextualKeyword('as')) { lex(); name = parseNonComputedProperty(); } return markerApply(marker, delegate.createExportSpecifier(id, name)); } function parseExportDeclaration() { var declaration = null, possibleIdentifierToken, sourceElement, isExportFromIdentifier, src = null, specifiers = [], marker = markerCreate(); expectKeyword('export'); if (matchKeyword('default')) { // covers: // export default ... lex(); if (matchKeyword('function') || matchKeyword('class')) { possibleIdentifierToken = lookahead2(); if (isIdentifierName(possibleIdentifierToken)) { // covers: // export default function foo () {} // export default class foo {} sourceElement = parseSourceElement(); return markerApply(marker, delegate.createExportDeclaration(true, sourceElement, [sourceElement.id], null)); } // covers: // export default function () {} // export default class {} switch (lookahead.value) { case 'class': return markerApply(marker, delegate.createExportDeclaration(true, parseClassExpression(), [], null)); case 'function': return markerApply(marker, delegate.createExportDeclaration(true, parseFunctionExpression(), [], null)); } } if (matchContextualKeyword('from')) { throwError({}, Messages.UnexpectedToken, lookahead.value); } // covers: // export default {}; // export default []; if (match('{')) { declaration = parseObjectInitialiser(); } else if (match('[')) { declaration = parseArrayInitialiser(); } else { declaration = parseAssignmentExpression(); } consumeSemicolon(); return markerApply(marker, delegate.createExportDeclaration(true, declaration, [], null)); } // non-default export if (lookahead.type === Token.Keyword) { // covers: // export var f = 1; switch (lookahead.value) { case 'let': case 'const': case 'var': case 'class': case 'function': return markerApply(marker, delegate.createExportDeclaration(false, parseSourceElement(), specifiers, null)); } } if (match('*')) { // covers: // export * from "foo"; specifiers.push(parseExportBatchSpecifier()); if (!matchContextualKeyword('from')) { throwError({}, lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); } lex(); src = parseModuleSpecifier(); consumeSemicolon(); return markerApply(marker, delegate.createExportDeclaration(false, null, specifiers, src)); } expect('{'); if (!match('}')) { do { isExportFromIdentifier = isExportFromIdentifier || matchKeyword('default'); specifiers.push(parseExportSpecifier()); } while (match(',') && lex()); } expect('}'); if (matchContextualKeyword('from')) { // covering: // export {default} from "foo"; // export {foo} from "foo"; lex(); src = parseModuleSpecifier(); consumeSemicolon(); } else if (isExportFromIdentifier) { // covering: // export {default}; // missing fromClause throwError({}, lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); } else { // cover // export {foo}; consumeSemicolon(); } return markerApply(marker, delegate.createExportDeclaration(false, declaration, specifiers, src)); } function parseImportSpecifier() { // import {} ...; var id, name = null, marker = markerCreate(); id = parseNonComputedProperty(); if (matchContextualKeyword('as')) { lex(); name = parseVariableIdentifier(); } return markerApply(marker, delegate.createImportSpecifier(id, name)); } function parseNamedImports() { var specifiers = []; // {foo, bar as bas} expect('{'); if (!match('}')) { do { specifiers.push(parseImportSpecifier()); } while (match(',') && lex()); } expect('}'); return specifiers; } function parseImportDefaultSpecifier() { // import ...; var id, marker = markerCreate(); id = parseNonComputedProperty(); return markerApply(marker, delegate.createImportDefaultSpecifier(id)); } function parseImportNamespaceSpecifier() { // import <* as foo> ...; var id, marker = markerCreate(); expect('*'); if (!matchContextualKeyword('as')) { throwError({}, Messages.NoAsAfterImportNamespace); } lex(); id = parseNonComputedProperty(); return markerApply(marker, delegate.createImportNamespaceSpecifier(id)); } function parseImportDeclaration() { var specifiers, src, marker = markerCreate(); expectKeyword('import'); specifiers = []; if (lookahead.type === Token.StringLiteral) { // covers: // import "foo"; src = parseModuleSpecifier(); consumeSemicolon(); return markerApply(marker, delegate.createImportDeclaration(specifiers, src)); } if (!matchKeyword('default') && isIdentifierName(lookahead)) { // covers: // import foo // import foo, ... specifiers.push(parseImportDefaultSpecifier()); if (match(',')) { lex(); } } if (match('*')) { // covers: // import foo, * as foo // import * as foo specifiers.push(parseImportNamespaceSpecifier()); } else if (match('{')) { // covers: // import foo, {bar} // import {bar} specifiers = specifiers.concat(parseNamedImports()); } if (!matchContextualKeyword('from')) { throwError({}, lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); } lex(); src = parseModuleSpecifier(); consumeSemicolon(); return markerApply(marker, delegate.createImportDeclaration(specifiers, src)); } // 12.3 Empty Statement function parseEmptyStatement() { var marker = markerCreate(); expect(';'); return markerApply(marker, delegate.createEmptyStatement()); } // 12.4 Expression Statement function parseExpressionStatement() { var marker = markerCreate(), expr = parseExpression(); consumeSemicolon(); return markerApply(marker, delegate.createExpressionStatement(expr)); } // 12.5 If statement function parseIfStatement() { var test, consequent, alternate, marker = markerCreate(); expectKeyword('if'); expect('('); test = parseExpression(); expect(')'); consequent = parseStatement(); if (matchKeyword('else')) { lex(); alternate = parseStatement(); } else { alternate = null; } return markerApply(marker, delegate.createIfStatement(test, consequent, alternate)); } // 12.6 Iteration Statements function parseDoWhileStatement() { var body, test, oldInIteration, marker = markerCreate(); expectKeyword('do'); oldInIteration = state.inIteration; state.inIteration = true; body = parseStatement(); state.inIteration = oldInIteration; expectKeyword('while'); expect('('); test = parseExpression(); expect(')'); if (match(';')) { lex(); } return markerApply(marker, delegate.createDoWhileStatement(body, test)); } function parseWhileStatement() { var test, body, oldInIteration, marker = markerCreate(); expectKeyword('while'); expect('('); test = parseExpression(); expect(')'); oldInIteration = state.inIteration; state.inIteration = true; body = parseStatement(); state.inIteration = oldInIteration; return markerApply(marker, delegate.createWhileStatement(test, body)); } function parseForVariableDeclaration() { var marker = markerCreate(), token = lex(), declarations = parseVariableDeclarationList(); return markerApply(marker, delegate.createVariableDeclaration(declarations, token.value)); } function parseForStatement(opts) { var init, test, update, left, right, body, operator, oldInIteration, marker = markerCreate(); init = test = update = null; expectKeyword('for'); // http://wiki.ecmascript.org/doku.php?id=proposals:iterators_and_generators&s=each if (matchContextualKeyword('each')) { throwError({}, Messages.EachNotAllowed); } expect('('); if (match(';')) { lex(); } else { if (matchKeyword('var') || matchKeyword('let') || matchKeyword('const')) { state.allowIn = false; init = parseForVariableDeclaration(); state.allowIn = true; if (init.declarations.length === 1) { if (matchKeyword('in') || matchContextualKeyword('of')) { operator = lookahead; if (!((operator.value === 'in' || init.kind !== 'var') && init.declarations[0].init)) { lex(); left = init; right = parseExpression(); init = null; } } } } else { state.allowIn = false; init = parseExpression(); state.allowIn = true; if (matchContextualKeyword('of')) { operator = lex(); left = init; right = parseExpression(); init = null; } else if (matchKeyword('in')) { // LeftHandSideExpression if (!isAssignableLeftHandSide(init)) { throwError({}, Messages.InvalidLHSInForIn); } operator = 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; if (!(opts !== undefined && opts.ignoreBody)) { body = parseStatement(); } state.inIteration = oldInIteration; if (typeof left === 'undefined') { return markerApply(marker, delegate.createForStatement(init, test, update, body)); } if (operator.value === 'in') { return markerApply(marker, delegate.createForInStatement(left, right, body)); } return markerApply(marker, delegate.createForOfStatement(left, right, body)); } // 12.7 The continue statement function parseContinueStatement() { var label = null, marker = markerCreate(); expectKeyword('continue'); // Optimize the most common form: 'continue;'. if (source.charCodeAt(index) === 59) { lex(); if (!state.inIteration) { throwError({}, Messages.IllegalContinue); } return markerApply(marker, delegate.createContinueStatement(null)); } if (peekLineTerminator()) { if (!state.inIteration) { throwError({}, Messages.IllegalContinue); } return markerApply(marker, delegate.createContinueStatement(null)); } if (lookahead.type === Token.Identifier) { label = parseVariableIdentifier(); if (!state.labelSet.has(label.name)) { throwError({}, Messages.UnknownLabel, label.name); } } consumeSemicolon(); if (label === null && !state.inIteration) { throwError({}, Messages.IllegalContinue); } return markerApply(marker, delegate.createContinueStatement(label)); } // 12.8 The break statement function parseBreakStatement() { var label = null, marker = markerCreate(); expectKeyword('break'); // Catch the very common case first: immediately a semicolon (char #59). if (source.charCodeAt(index) === 59) { lex(); if (!(state.inIteration || state.inSwitch)) { throwError({}, Messages.IllegalBreak); } return markerApply(marker, delegate.createBreakStatement(null)); } if (peekLineTerminator()) { if (!(state.inIteration || state.inSwitch)) { throwError({}, Messages.IllegalBreak); } return markerApply(marker, delegate.createBreakStatement(null)); } if (lookahead.type === Token.Identifier) { label = parseVariableIdentifier(); if (!state.labelSet.has(label.name)) { throwError({}, Messages.UnknownLabel, label.name); } } consumeSemicolon(); if (label === null && !(state.inIteration || state.inSwitch)) { throwError({}, Messages.IllegalBreak); } return markerApply(marker, delegate.createBreakStatement(label)); } // 12.9 The return statement function parseReturnStatement() { var argument = null, marker = markerCreate(); expectKeyword('return'); if (!state.inFunctionBody) { throwErrorTolerant({}, Messages.IllegalReturn); } // 'return' followed by a space and an identifier is very common. if (source.charCodeAt(index) === 32) { if (isIdentifierStart(source.charCodeAt(index + 1))) { argument = parseExpression(); consumeSemicolon(); return markerApply(marker, delegate.createReturnStatement(argument)); } } if (peekLineTerminator()) { return markerApply(marker, delegate.createReturnStatement(null)); } if (!match(';')) { if (!match('}') && lookahead.type !== Token.EOF) { argument = parseExpression(); } } consumeSemicolon(); return markerApply(marker, delegate.createReturnStatement(argument)); } // 12.10 The with statement function parseWithStatement() { var object, body, marker = markerCreate(); if (strict) { throwErrorTolerant({}, Messages.StrictModeWith); } expectKeyword('with'); expect('('); object = parseExpression(); expect(')'); body = parseStatement(); return markerApply(marker, delegate.createWithStatement(object, body)); } // 12.10 The swith statement function parseSwitchCase() { var test, consequent = [], sourceElement, marker = markerCreate(); if (matchKeyword('default')) { lex(); test = null; } else { expectKeyword('case'); test = parseExpression(); } expect(':'); while (index < length) { if (match('}') || matchKeyword('default') || matchKeyword('case')) { break; } sourceElement = parseSourceElement(); if (typeof sourceElement === 'undefined') { break; } consequent.push(sourceElement); } return markerApply(marker, delegate.createSwitchCase(test, consequent)); } function parseSwitchStatement() { var discriminant, cases, clause, oldInSwitch, defaultFound, marker = markerCreate(); expectKeyword('switch'); expect('('); discriminant = parseExpression(); expect(')'); expect('{'); cases = []; if (match('}')) { lex(); return markerApply(marker, 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 markerApply(marker, delegate.createSwitchStatement(discriminant, cases)); } // 12.13 The throw statement function parseThrowStatement() { var argument, marker = markerCreate(); expectKeyword('throw'); if (peekLineTerminator()) { throwError({}, Messages.NewlineAfterThrow); } argument = parseExpression(); consumeSemicolon(); return markerApply(marker, delegate.createThrowStatement(argument)); } // 12.14 The try statement function parseCatchClause() { var param, body, marker = markerCreate(); expectKeyword('catch'); expect('('); if (match(')')) { throwUnexpected(lookahead); } param = parseExpression(); // 12.14.1 if (strict && param.type === Syntax.Identifier && isRestrictedWord(param.name)) { throwErrorTolerant({}, Messages.StrictCatchVariable); } expect(')'); body = parseBlock(); return markerApply(marker, delegate.createCatchClause(param, body)); } function parseTryStatement() { var block, handlers = [], finalizer = null, marker = markerCreate(); 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 markerApply(marker, delegate.createTryStatement(block, [], handlers, finalizer)); } // 12.15 The debugger statement function parseDebuggerStatement() { var marker = markerCreate(); expectKeyword('debugger'); consumeSemicolon(); return markerApply(marker, delegate.createDebuggerStatement()); } // 12 Statements function parseStatement() { var type = lookahead.type, marker, expr, labeledBody; if (type === Token.EOF) { throwUnexpected(lookahead); } if (type === Token.Punctuator) { switch (lookahead.value) { case ';': return parseEmptyStatement(); case '{': return parseBlock(); case '(': return parseExpressionStatement(); default: break; } } if (type === Token.Keyword) { switch (lookahead.value) { case 'break': return parseBreakStatement(); case 'continue': return parseContinueStatement(); case 'debugger': return parseDebuggerStatement(); case 'do': return parseDoWhileStatement(); case 'for': return parseForStatement(); case 'function': return parseFunctionDeclaration(); case 'class': return parseClassDeclaration(); case 'if': return parseIfStatement(); case 'return': return parseReturnStatement(); case 'switch': return parseSwitchStatement(); case 'throw': return parseThrowStatement(); case 'try': return parseTryStatement(); case 'var': return parseVariableStatement(); case 'while': return parseWhileStatement(); case 'with': return parseWithStatement(); default: break; } } marker = markerCreate(); expr = parseExpression(); // 12.12 Labelled Statements if ((expr.type === Syntax.Identifier) && match(':')) { lex(); if (state.labelSet.has(expr.name)) { throwError({}, Messages.Redeclaration, 'Label', expr.name); } state.labelSet.set(expr.name, true); labeledBody = parseStatement(); state.labelSet.delete(expr.name); return markerApply(marker, delegate.createLabeledStatement(expr, labeledBody)); } consumeSemicolon(); return markerApply(marker, delegate.createExpressionStatement(expr)); } // 13 Function Definition function parseConciseBody() { if (match('{')) { return parseFunctionSourceElements(); } return parseAssignmentExpression(); } function parseFunctionSourceElements() { var sourceElement, sourceElements = [], token, directive, firstRestricted, oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesizedCount, marker = markerCreate(); 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.range[0] + 1, token.range[1] - 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; oldParenthesizedCount = state.parenthesizedCount; state.labelSet = new StringMap(); state.inIteration = false; state.inSwitch = false; state.inFunctionBody = true; state.parenthesizedCount = 0; 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; state.parenthesizedCount = oldParenthesizedCount; return markerApply(marker, delegate.createBlockStatement(sourceElements)); } function validateParam(options, param, name) { if (strict) { if (isRestrictedWord(name)) { options.stricted = param; options.message = Messages.StrictParamName; } if (options.paramSet.has(name)) { options.stricted = param; options.message = Messages.StrictParamDupe; } } else if (!options.firstRestricted) { if (isRestrictedWord(name)) { options.firstRestricted = param; options.message = Messages.StrictParamName; } else if (isStrictModeReservedWord(name)) { options.firstRestricted = param; options.message = Messages.StrictReservedWord; } else if (options.paramSet.has(name)) { options.firstRestricted = param; options.message = Messages.StrictParamDupe; } } options.paramSet.set(name, true); } function parseParam(options) { var token, rest, param, def; token = lookahead; if (token.value === '...') { token = lex(); rest = true; } if (match('[')) { param = parseArrayInitialiser(); reinterpretAsDestructuredParameter(options, param); } else if (match('{')) { if (rest) { throwError({}, Messages.ObjectPatternAsRestParameter); } param = parseObjectInitialiser(); reinterpretAsDestructuredParameter(options, param); } else { param = parseVariableIdentifier(); validateParam(options, token, token.value); } if (match('=')) { if (rest) { throwErrorTolerant(lookahead, Messages.DefaultRestParameter); } lex(); def = parseAssignmentExpression(); ++options.defaultCount; } if (rest) { if (!match(')')) { throwError({}, Messages.ParameterAfterRestParameter); } options.rest = param; return false; } options.params.push(param); options.defaults.push(def); return !match(')'); } function parseParams(firstRestricted) { var options, marker = markerCreate(); options = { params: [], defaultCount: 0, defaults: [], rest: null, firstRestricted: firstRestricted }; expect('('); if (!match(')')) { options.paramSet = new StringMap(); while (index < length) { if (!parseParam(options)) { break; } expect(','); } } expect(')'); if (options.defaultCount === 0) { options.defaults = []; } return markerApply(marker, options); } function parseFunctionDeclaration() { var id, body, token, tmp, firstRestricted, message, previousStrict, previousYieldAllowed, generator, marker = markerCreate(); expectKeyword('function'); generator = false; if (match('*')) { lex(); generator = true; } 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); firstRestricted = tmp.firstRestricted; if (tmp.message) { message = tmp.message; } previousStrict = strict; previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = generator; body = parseFunctionSourceElements(); if (strict && firstRestricted) { throwError(firstRestricted, message); } if (strict && tmp.stricted) { throwErrorTolerant(tmp.stricted, message); } strict = previousStrict; state.yieldAllowed = previousYieldAllowed; return markerApply(marker, delegate.createFunctionDeclaration(id, tmp.params, tmp.defaults, body, tmp.rest, generator, false)); } function parseFunctionExpression() { var token, id = null, firstRestricted, message, tmp, body, previousStrict, previousYieldAllowed, generator, marker = markerCreate(); expectKeyword('function'); generator = false; if (match('*')) { lex(); generator = true; } 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); firstRestricted = tmp.firstRestricted; if (tmp.message) { message = tmp.message; } previousStrict = strict; previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = generator; body = parseFunctionSourceElements(); if (strict && firstRestricted) { throwError(firstRestricted, message); } if (strict && tmp.stricted) { throwErrorTolerant(tmp.stricted, message); } strict = previousStrict; state.yieldAllowed = previousYieldAllowed; return markerApply(marker, delegate.createFunctionExpression(id, tmp.params, tmp.defaults, body, tmp.rest, generator, false)); } function parseYieldExpression() { var yieldToken, delegateFlag, expr, marker = markerCreate(); yieldToken = lex(); assert(yieldToken.value === 'yield', 'Called parseYieldExpression with non-yield lookahead.'); if (!state.yieldAllowed) { throwErrorTolerant({}, Messages.IllegalYield); } delegateFlag = false; if (match('*')) { lex(); delegateFlag = true; } expr = parseAssignmentExpression(); return markerApply(marker, delegate.createYieldExpression(expr, delegateFlag)); } // 14 Functions and classes // 14.1 Functions is defined above (13 in ES5) // 14.2 Arrow Functions Definitions is defined in (7.3 assignments) // 14.3 Method Definitions // 14.3.7 function specialMethod(methodDefinition) { return methodDefinition.kind === 'get' || methodDefinition.kind === 'set' || methodDefinition.value.generator; } function parseMethodDefinition() { var token, key, param, propType, computed, marker = markerCreate(); if (lookahead.value === 'static') { propType = ClassPropertyType.static; lex(); } else { propType = ClassPropertyType.prototype; } if (match('*')) { lex(); computed = (lookahead.value === '['); return markerApply(marker, delegate.createMethodDefinition( propType, '', parseObjectPropertyKey(), parsePropertyMethodFunction({ generator: true }), computed )); } token = lookahead; key = parseObjectPropertyKey(); if (token.value === 'get' && !match('(')) { computed = (lookahead.value === '['); key = parseObjectPropertyKey(); expect('('); expect(')'); return markerApply(marker, delegate.createMethodDefinition( propType, 'get', key, parsePropertyFunction({ generator: false }), computed )); } if (token.value === 'set' && !match('(')) { computed = (lookahead.value === '['); key = parseObjectPropertyKey(); expect('('); token = lookahead; param = [ parseVariableIdentifier() ]; expect(')'); return markerApply(marker, delegate.createMethodDefinition( propType, 'set', key, parsePropertyFunction({ params: param, generator: false, name: token }), computed )); } computed = (token.value === '['); return markerApply(marker, delegate.createMethodDefinition( propType, '', key, parsePropertyMethodFunction({ generator: false }), computed )); } // 14.5 Class Definitions function parseClassElement() { if (match(';')) { lex(); } else { return parseMethodDefinition(); } } function parseClassBody() { var classElement, classElements = [], existingProps = {}, marker = markerCreate(), propName, propType; existingProps[ClassPropertyType.static] = new StringMap(); existingProps[ClassPropertyType.prototype] = new StringMap(); expect('{'); while (index < length) { if (match('}')) { break; } classElement = parseClassElement(existingProps); if (typeof classElement !== 'undefined') { classElements.push(classElement); propName = !classElement.computed && getFieldName(classElement.key); if (propName !== false) { propType = classElement.static ? ClassPropertyType.static : ClassPropertyType.prototype; if (propName === 'constructor' && !classElement.static) { if (specialMethod(classElement)) { throwError(classElement, Messages.IllegalClassConstructorProperty); } if (existingProps[ClassPropertyType.prototype].has('constructor')) { throwError(classElement.key, Messages.IllegalDuplicateClassProperty); } } existingProps[propType].set(propName, true); } } } expect('}'); return markerApply(marker, delegate.createClassBody(classElements)); } function parseClassExpression() { var id, previousYieldAllowed, superClass = null, marker = markerCreate(); expectKeyword('class'); if (!matchKeyword('extends') && !match('{')) { id = parseVariableIdentifier(); } if (matchKeyword('extends')) { expectKeyword('extends'); previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = false; superClass = parseAssignmentExpression(); state.yieldAllowed = previousYieldAllowed; } return markerApply(marker, delegate.createClassExpression(id, superClass, parseClassBody())); } function parseClassDeclaration() { var id, previousYieldAllowed, superClass = null, marker = markerCreate(); expectKeyword('class'); id = parseVariableIdentifier(); if (matchKeyword('extends')) { expectKeyword('extends'); previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = false; superClass = parseAssignmentExpression(); state.yieldAllowed = previousYieldAllowed; } return markerApply(marker, delegate.createClassDeclaration(id, superClass, parseClassBody())); } // 15 Program function parseSourceElement() { if (lookahead.type === Token.Keyword) { switch (lookahead.value) { case 'const': case 'let': return parseConstLetDeclaration(lookahead.value); case 'function': return parseFunctionDeclaration(); case 'export': throwErrorTolerant({}, Messages.IllegalExportDeclaration); return parseExportDeclaration(); case 'import': throwErrorTolerant({}, Messages.IllegalImportDeclaration); return parseImportDeclaration(); default: return parseStatement(); } } if (lookahead.type !== Token.EOF) { return parseStatement(); } } function parseProgramElement() { if (extra.isModule && lookahead.type === Token.Keyword) { switch (lookahead.value) { case 'export': return parseExportDeclaration(); case 'import': return parseImportDeclaration(); } } return parseSourceElement(); } function parseProgramElements() { var sourceElement, sourceElements = [], token, directive, firstRestricted; while (index < length) { token = lookahead; if (token.type !== Token.StringLiteral) { break; } sourceElement = parseProgramElement(); sourceElements.push(sourceElement); if (sourceElement.expression.type !== Syntax.Literal) { // this is not directive break; } directive = source.slice(token.range[0] + 1, token.range[1] - 1); if (directive === 'use strict') { strict = true; if (firstRestricted) { throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral); } } else { if (!firstRestricted && token.octal) { firstRestricted = token; } } } while (index < length) { sourceElement = parseProgramElement(); if (typeof sourceElement === 'undefined') { break; } sourceElements.push(sourceElement); } return sourceElements; } function parseProgram() { var body, marker = markerCreate(); strict = !!extra.isModule; peek(); body = parseProgramElements(); return markerApply(marker, delegate.createProgram(body)); } function collectToken() { var loc, token, range, value, entry; skipComment(); loc = { start: { line: lineNumber, column: index - lineStart } }; token = extra.advance(); loc.end = { line: lineNumber, column: index - lineStart }; if (token.type !== Token.EOF) { range = [token.range[0], token.range[1]]; value = source.slice(token.range[0], token.range[1]); entry = { type: TokenName[token.type], value: value, range: range, loc: loc }; if (token.regex) { entry.regex = { pattern: token.regex.pattern, flags: token.regex.flags }; } extra.tokens.push(entry); } return token; } function collectRegex() { var pos, loc, regex, token; skipComment(); pos = index; loc = { start: { line: lineNumber, column: index - lineStart } }; regex = extra.scanRegExp(); loc.end = { line: lineNumber, column: index - lineStart }; if (!extra.tokenize) { /* istanbul ignore next */ // 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, regex: regex.regex, range: [pos, index], loc: loc }); } return regex; } 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 (entry.regex) { token.regex = { pattern: entry.regex.pattern, flags: entry.regex.flags }; } if (extra.range) { token.range = entry.range; } if (extra.loc) { token.loc = entry.loc; } tokens.push(token); } extra.tokens = tokens; } function patch() { if (typeof extra.tokens !== 'undefined') { extra.advance = advance; extra.scanRegExp = scanRegExp; advance = collectToken; scanRegExp = collectRegex; } } function unpatch() { if (typeof extra.scanRegExp === 'function') { advance = extra.advance; scanRegExp = extra.scanRegExp; } } // This is used to modify the delegate. function extend(object, properties) { var entry, result = {}; for (entry in object) { /* istanbul ignore else */ if (object.hasOwnProperty(entry)) { result[entry] = object[entry]; } } for (entry in properties) { /* istanbul ignore else */ if (properties.hasOwnProperty(entry)) { result[entry] = properties[entry]; } } return result; } 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 = { allowKeyword: true, allowIn: true, labelSet: new StringMap(), 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 = []; } patch(); 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 { unpatch(); 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 = { allowKeyword: false, allowIn: true, labelSet: new StringMap(), parenthesizedCount: 0, inFunctionBody: false, inIteration: false, inSwitch: false, lastCommentStart: -1, yieldAllowed: false }; 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) { delegate = extend(delegate, { 'postProcess': function (node) { node.loc.source = toString(options.source); return node; } }); } if (options.sourceType === 'module') { extra.isModule = true; } 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 = []; } } patch(); 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 { unpatch(); extra = {}; } return program; } // Sync with *.json manifests. exports.version = '1.1.0-dev-harmony'; 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 : */ estraverse-4.2.0/test/traverse.js000066400000000000000000000370561267036617700171020ustar00rootroot00000000000000// Copyright (C) 2013 Yusuke Suzuki // // 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 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. import Dumper from './dumper'; import checkDump from './checkDump'; import { expect } from 'chai'; import { traverse } from '..'; describe('object expression', function() { it('properties', function() { const tree = { type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'Identifier', name: 'a' } }] }; checkDump(Dumper.dump(tree), ` enter - ObjectExpression enter - Property enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - Property leave - ObjectExpression `); }); it('properties without type', function() { const tree = { type: 'ObjectExpression', properties: [{ key: { type: 'Identifier', name: 'a' }, value: { type: 'Identifier', name: 'a' } }] }; checkDump(Dumper.dump(tree), ` enter - ObjectExpression enter - undefined enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - undefined leave - ObjectExpression `); }); it('properties with custom type', function() { const tree = { type: 'ObjectExpression', properties: [{ type: 'CustomProperty', foo: { type: 'Identifier', name: 'a' } }] }; checkDump(Dumper.dump(tree, {CustomProperty: ['foo']}), ` enter - ObjectExpression enter - CustomProperty enter - Identifier leave - Identifier leave - CustomProperty leave - ObjectExpression `); }); it('skip and break', function() { const tree = { type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { $enter: 'Skip', type: 'ObjectExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'Identifier', name: 'a' } }] }, $leave: 'Break' }] }; checkDump(Dumper.dump(tree), ` enter - ObjectExpression enter - Property enter - Identifier leave - Identifier enter - ObjectExpression leave - ObjectExpression leave - Property `); }); }); describe('object pattern', function() { it('properties', function() { const tree = { type: 'ObjectPattern', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'Identifier', name: 'a' } }] }; checkDump(Dumper.dump(tree), ` enter - ObjectPattern enter - Property enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - Property leave - ObjectPattern `); }); it('properties without type', function() { const tree = { type: 'ObjectPattern', properties: [{ key: { type: 'Identifier', name: 'a' }, value: { type: 'Identifier', name: 'a' } }] }; checkDump(Dumper.dump(tree), ` enter - ObjectPattern enter - undefined enter - Identifier leave - Identifier enter - Identifier leave - Identifier leave - undefined leave - ObjectPattern `); }); }); describe('try statement', function() { it('old interface', function() { const tree = { type: 'TryStatement', handler: { type: 'BlockStatement', body: [] }, finalizer: { type: 'BlockStatement', body: [] } }; checkDump(Dumper.dump(tree), ` enter - TryStatement enter - BlockStatement leave - BlockStatement enter - BlockStatement leave - BlockStatement leave - TryStatement `); }); it('new interface', function() { const tree = { type: 'TryStatement', handler: { type: 'BlockStatement', body: [] }, finalizer: { type: 'BlockStatement', body: [] } }; checkDump(Dumper.dump(tree), ` enter - TryStatement enter - BlockStatement leave - BlockStatement enter - BlockStatement leave - BlockStatement leave - TryStatement `); }); }); describe('arrow function expression', function() { it('traverse', function() { const tree = { type: 'ArrowFunctionExpression', params: [ { type: 'AssignmentPattern', left: { type: 'Identifier', name: 'a' }, right: { type: 'Literal', value: 20 } }, { type: 'RestElement', argument: { type: 'Identifier', name: 'rest' } } ], body: { type: 'BlockStatement', body: [] } }; checkDump(Dumper.dump(tree), ` enter - ArrowFunctionExpression enter - AssignmentPattern enter - Identifier leave - Identifier enter - Literal leave - Literal leave - AssignmentPattern enter - RestElement enter - Identifier leave - Identifier leave - RestElement enter - BlockStatement leave - BlockStatement leave - ArrowFunctionExpression `); }); }); describe('function expression', function() { it('traverse', function() { const tree = { type: 'FunctionExpression', params: [ { type: 'AssignmentPattern', left: { type: 'Identifier', name: 'a' }, right: { type: 'Literal', value: 20 } }, { type: 'RestElement', argument: { type: 'Identifier', name: 'rest' } } ], body: { type: 'BlockStatement', body: [] } }; checkDump(Dumper.dump(tree), ` enter - FunctionExpression enter - AssignmentPattern enter - Identifier leave - Identifier enter - Literal leave - Literal leave - AssignmentPattern enter - RestElement enter - Identifier leave - Identifier leave - RestElement enter - BlockStatement leave - BlockStatement leave - FunctionExpression `); }); }); describe('function declaration', function() { it('traverse', function() { const tree = { type: 'FunctionDeclaration', id: { type: 'Identifier', name: 'decl' }, params: [ { type: 'AssignmentPattern', left: { type: 'Identifier', name: 'a' }, right: { type: 'Literal', value: 20 } }, { type: 'RestElement', argument: { type: 'Identifier', name: 'rest' } } ], body: { type: 'BlockStatement', body: [] } }; checkDump(Dumper.dump(tree), ` enter - FunctionDeclaration enter - Identifier leave - Identifier enter - AssignmentPattern enter - Identifier leave - Identifier enter - Literal leave - Literal leave - AssignmentPattern enter - RestElement enter - Identifier leave - Identifier leave - RestElement enter - BlockStatement leave - BlockStatement leave - FunctionDeclaration `); }); }); describe('extending keys', function() { it('traverse', function() { const tree = { type: 'TestStatement', id: { type: 'Identifier', name: 'decl' }, params: [{ type: 'Identifier', name: 'a' }], defaults: [{ type: 'Literal', value: 20 }], rest: { type: 'Identifier', name: 'rest' }, body: { type: 'BlockStatement', body: [] } }; const result = Dumper.dump(tree, { TestStatement: ['id', 'params', 'defaults', 'rest', 'body'] }); checkDump(result, ` enter - TestStatement enter - Identifier leave - Identifier enter - Identifier leave - Identifier enter - Literal leave - Literal enter - Identifier leave - Identifier enter - BlockStatement leave - BlockStatement leave - TestStatement `); }); }); describe('no listed keys fallback', function() { it('traverse', function() { const tree = { type: 'TestStatement', id: { type: 'Identifier', name: 'decl' }, params: [{ type: 'Identifier', name: 'a' }], defaults: [{ type: 'Literal', value: 20 }], rest: { type: 'Identifier', name: 'rest' }, body: { type: 'BlockStatement', body: [] } }; checkDump(Dumper.dump(tree, null, 'iteration'), ` enter - TestStatement enter - Identifier leave - Identifier enter - Identifier leave - Identifier enter - Literal leave - Literal enter - Identifier leave - Identifier enter - BlockStatement leave - BlockStatement leave - TestStatement `); }); it('traverse with fallback function', function() { const tree = { type: 'TestStatement', id: { type: 'Identifier', name: 'decl' }, params: [{ type: 'Identifier', name: 'a' }], defaults: [{ type: 'Literal', value: 20 }], rest: { type: 'Identifier', name: 'rest' }, body: { type: 'BlockStatement', body: [] } }; function filterKeys(node) { return Object.keys(node).filter(key => key !== 'id'); } checkDump(Dumper.dump(tree, null, filterKeys), ` enter - TestStatement enter - Identifier leave - Identifier enter - Literal leave - Literal enter - Identifier leave - Identifier enter - BlockStatement leave - BlockStatement leave - TestStatement `); }); it('throw unknown node type error when unknown nodes', function() { const tree = { type: 'XXXExpression', properties: [{ type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'ArrayExpression', elements: [ {type: 'Literal', value: 1}, {type: 'Literal', value: 2}, {type: 'Literal', value: 3}, {type: 'Literal', value: 4} ] } }] }; expect( () => traverse(tree, { enter(node) {} }) ).to.throw('Unknown node type XXXExpression.'); }); }); estraverse-4.2.0/test/type.js000066400000000000000000000041471267036617700162230ustar00rootroot00000000000000// -*- coding: utf-8 -*- // Copyright (C) 2014 Yusuke Suzuki // // 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 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. import { expect } from 'chai'; import { traverse } from '..'; describe('type API', function() { it('for Property', function() { const property = { type: 'Property', key: { type: 'Identifier', name: 'a' }, value: { type: 'Identifier', name: 'a' } }; const tree = { type: 'ObjectExpression', properties: [property] }; return traverse(tree, { enter(node) { if (node === property) { expect(this.type()).to.be.equal('Property'); } } }); }); }); // vim: set sw=4 ts=4 et tw=80 :