mirror of
				https://gitea.com/actions/setup-python.git
				synced 2025-10-31 09:08:07 +07:00 
			
		
		
		
	
		
			
				
	
	
		
			850 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			850 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
|   Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
 | |
|   Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
 | |
| 
 | |
|   Redistribution and use in source and binary forms, with or without
 | |
|   modification, are permitted provided that the following conditions are met:
 | |
| 
 | |
|     * Redistributions of source code must retain the above copyright
 | |
|       notice, this list of conditions and the following disclaimer.
 | |
|     * Redistributions in binary form must reproduce the above copyright
 | |
|       notice, this list of conditions and the following disclaimer in the
 | |
|       documentation and/or other materials provided with the distribution.
 | |
| 
 | |
|   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | |
|   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|   ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 | |
|   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | |
|   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | |
|   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | |
|   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | |
|   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| */
 | |
| /*jslint vars:false, bitwise:true*/
 | |
| /*jshint indent:4*/
 | |
| /*global exports:true*/
 | |
| (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 : */
 |