estraverse
Advanced tools
Comparing version 0.0.4 to 1.0.0
@@ -25,5 +25,5 @@ /* | ||
*/ | ||
/*jslint bitwise:true */ | ||
/*global exports:true, define:true, window:true */ | ||
/*jslint vars:false*/ | ||
/*jshint indent:4*/ | ||
/*global exports:true, define:true, window:true*/ | ||
(function (factory) { | ||
@@ -154,62 +154,168 @@ 'use strict'; | ||
function traverse(top, visitor) { | ||
var worklist, leavelist, node, nodeType, ret, current, current2, candidates, candidate, marker = {}; | ||
function Reference(parent, key) { | ||
this.parent = parent; | ||
this.key = key; | ||
} | ||
worklist = [ top ]; | ||
leavelist = [ null ]; | ||
Reference.prototype.replace = function replace(node) { | ||
this.parent[this.key] = node; | ||
}; | ||
function Element(node, path, wrap, ref) { | ||
this.node = node; | ||
this.path = path; | ||
this.wrap = wrap; | ||
this.ref = ref; | ||
} | ||
function Controller(root, visitor) { | ||
this.visitor = visitor; | ||
this.root = root; | ||
this.__worklist = []; | ||
this.__leavelist = []; | ||
this.__current = null; | ||
this.__state = null; | ||
} | ||
// API: | ||
// return property path from root to current node | ||
Controller.prototype.path = function path() { | ||
var i, iz, result, element; | ||
// first node is sentinel | ||
result = []; | ||
for (i = 1, iz = this.__leavelist.length; i < iz; ++i) { | ||
element = this.__leavelist[i]; | ||
result.push(element.path); | ||
} | ||
result.push(this.__current.path); | ||
return '$' + result.join('.'); | ||
}; | ||
// 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; | ||
previous = this.__current; | ||
this.__current = element; | ||
this.__state = null; | ||
result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node); | ||
this.__current = previous; | ||
return result; | ||
}; | ||
Controller.prototype.notify = function notify(flag) { | ||
this.__state = flag; | ||
}; | ||
Controller.prototype.skip = function () { | ||
this.notify(VisitorOption.Skip); | ||
}; | ||
Controller.prototype['break'] = function () { | ||
this.notify(VisitorOption.Break); | ||
}; | ||
function traverse(root, visitor) { | ||
var worklist, | ||
leavelist, | ||
element, | ||
node, | ||
nodeType, | ||
ret, | ||
key, | ||
current, | ||
current2, | ||
candidates, | ||
candidate, | ||
sentinel, | ||
controller; | ||
sentinel = {}; | ||
controller = new Controller(root, visitor); | ||
// reference | ||
worklist = controller.__worklist; | ||
leavelist = controller.__leavelist; | ||
// initialize | ||
worklist.push(new Element(root, '', null, null)); | ||
leavelist.push(new Element(null, '', null, null)); | ||
while (worklist.length) { | ||
node = worklist.pop(); | ||
nodeType = node.type; | ||
element = worklist.pop(); | ||
if (node === marker) { | ||
node = leavelist.pop(); | ||
if (visitor.leave) { | ||
ret = visitor.leave(node, leavelist[leavelist.length - 1]); | ||
} else { | ||
ret = undefined; | ||
} | ||
if (ret === VisitorOption.Break) { | ||
if (element === sentinel) { | ||
element = leavelist.pop(); | ||
ret = (visitor.leave) ? controller.__execute(visitor.leave, element) : null; | ||
if (controller.__state === VisitorOption.Break || ret === VisitorOption.Break) { | ||
return; | ||
} | ||
} else if (node) { | ||
if (wrappers.hasOwnProperty(nodeType)) { | ||
node = node.node; | ||
nodeType = wrappers[nodeType]; | ||
} | ||
continue; | ||
} | ||
if (visitor.enter) { | ||
ret = visitor.enter(node, leavelist[leavelist.length - 1]); | ||
} else { | ||
ret = undefined; | ||
} | ||
if (element.node) { | ||
if (ret === VisitorOption.Break) { | ||
ret = (visitor.enter) ? controller.__execute(visitor.enter, element) : null; | ||
if (controller.__state === VisitorOption.Break || ret === VisitorOption.Break) { | ||
return; | ||
} | ||
worklist.push(marker); | ||
leavelist.push(node); | ||
worklist.push(sentinel); | ||
leavelist.push(element); | ||
if (ret !== VisitorOption.Skip) { | ||
candidates = VisitorKeys[nodeType]; | ||
current = candidates.length; | ||
while ((current -= 1) >= 0) { | ||
candidate = node[candidates[current]]; | ||
if (candidate) { | ||
if (isArray(candidate)) { | ||
current2 = candidate.length; | ||
while ((current2 -= 1) >= 0) { | ||
if (candidate[current2]) { | ||
if(nodeType === Syntax.ObjectExpression && 'properties' === candidates[current] && null == candidates[current].type) { | ||
worklist.push({type: 'PropertyWrapper', node: candidate[current2]}); | ||
} else { | ||
worklist.push(candidate[current2]); | ||
} | ||
} | ||
} | ||
} else { | ||
worklist.push(candidate); | ||
} | ||
if (controller.__state === VisitorOption.Skip || ret === VisitorOption.Skip) { | ||
continue; | ||
} | ||
node = element.node; | ||
nodeType = element.wrap || node.type; | ||
candidates = VisitorKeys[nodeType]; | ||
current = candidates.length; | ||
while ((current -= 1) >= 0) { | ||
key = candidates[current]; | ||
candidate = node[key]; | ||
if (!candidate) { | ||
continue; | ||
} | ||
if (!isArray(candidate)) { | ||
worklist.push(new Element(candidate, key, null, null)); | ||
continue; | ||
} | ||
current2 = candidate.length; | ||
while ((current2 -= 1) >= 0) { | ||
if (!candidate[current2]) { | ||
continue; | ||
} | ||
if (nodeType === Syntax.ObjectExpression && 'properties' === candidates[current] && null == candidates[current].type) { | ||
element = new Element(candidate[current2], key + '[' + current2 + ']', 'Property', null); | ||
} else { | ||
element = new Element(candidate[current2], key + '[' + current2 + ']', null, null); | ||
} | ||
worklist.push(element); | ||
} | ||
@@ -221,84 +327,112 @@ } | ||
function replace(top, visitor) { | ||
var worklist, leavelist, node, nodeType, target, tuple, ret, current, current2, candidates, candidate, marker = {}, result; | ||
function replace(root, visitor) { | ||
var worklist, | ||
leavelist, | ||
node, | ||
nodeType, | ||
target, | ||
element, | ||
current, | ||
current2, | ||
candidates, | ||
candidate, | ||
sentinel, | ||
controller, | ||
outer, | ||
key; | ||
result = { | ||
top: top | ||
}; | ||
sentinel = {}; | ||
controller = new Controller(root, visitor); | ||
tuple = [ top, result, 'top' ]; | ||
worklist = [ tuple ]; | ||
leavelist = [ tuple ]; | ||
// reference | ||
worklist = controller.__worklist; | ||
leavelist = controller.__leavelist; | ||
function notify(v) { | ||
ret = v; | ||
} | ||
// initialize | ||
outer = { | ||
root: root | ||
}; | ||
element = new Element(root, '', null, new Reference(outer, 'root')); | ||
worklist.push(element); | ||
leavelist.push(element); | ||
while (worklist.length) { | ||
tuple = worklist.pop(); | ||
element = worklist.pop(); | ||
if (tuple === marker) { | ||
tuple = leavelist.pop(); | ||
ret = undefined; | ||
if (element === sentinel) { | ||
element = leavelist.pop(); | ||
if (visitor.leave) { | ||
node = tuple[0]; | ||
target = visitor.leave(tuple[0], leavelist[leavelist.length - 1][0], notify); | ||
target = controller.__execute(visitor.leave, element); | ||
// node may be replaced with null, | ||
// so distinguish between undefined and null in this place | ||
if (target !== undefined) { | ||
node = target; | ||
// replace | ||
element.ref.replace(target); | ||
} | ||
tuple[1][tuple[2]] = node; | ||
} | ||
if (ret === VisitorOption.Break) { | ||
return result.top; | ||
if (controller.__state === VisitorOption.Break) { | ||
return outer.root; | ||
} | ||
} else if (tuple[0]) { | ||
ret = undefined; | ||
node = tuple[0]; | ||
continue; | ||
} | ||
nodeType = node.type; | ||
if (wrappers.hasOwnProperty(nodeType)) { | ||
tuple[0] = node = node.node; | ||
nodeType = wrappers[nodeType]; | ||
if (visitor.enter) { | ||
target = controller.__execute(visitor.enter, element); | ||
// node may be replaced with null, | ||
// so distinguish between undefined and null in this place | ||
if (target !== undefined) { | ||
// replace | ||
element.ref.replace(target); | ||
element.node = target; | ||
} | ||
} | ||
if (visitor.enter) { | ||
target = visitor.enter(tuple[0], leavelist[leavelist.length - 1][0], notify); | ||
if (target !== undefined) { | ||
node = target; | ||
} | ||
tuple[1][tuple[2]] = node; | ||
tuple[0] = node; | ||
if (controller.__state === VisitorOption.Break) { | ||
return outer.root; | ||
} | ||
// node may be null | ||
node = element.node; | ||
if (!node) { | ||
continue; | ||
} | ||
worklist.push(sentinel); | ||
leavelist.push(element); | ||
if (controller.__state === VisitorOption.Skip) { | ||
continue; | ||
} | ||
nodeType = element.wrap || node.type; | ||
candidates = VisitorKeys[nodeType]; | ||
current = candidates.length; | ||
while ((current -= 1) >= 0) { | ||
key = candidates[current]; | ||
candidate = node[key]; | ||
if (!candidate) { | ||
continue; | ||
} | ||
if (ret === VisitorOption.Break) { | ||
return result.top; | ||
if (!isArray(candidate)) { | ||
worklist.push(new Element(candidate, key, null, new Reference(node, key))); | ||
continue; | ||
} | ||
if (tuple[0]) { | ||
worklist.push(marker); | ||
leavelist.push(tuple); | ||
if (ret !== VisitorOption.Skip) { | ||
candidates = VisitorKeys[nodeType]; | ||
current = candidates.length; | ||
while ((current -= 1) >= 0) { | ||
candidate = node[candidates[current]]; | ||
if (candidate) { | ||
if (isArray(candidate)) { | ||
current2 = candidate.length; | ||
while ((current2 -= 1) >= 0) { | ||
if (candidate[current2]) { | ||
if(nodeType === Syntax.ObjectExpression && 'properties' === candidates[current] && null == candidates[current].type) { | ||
worklist.push([{type: 'PropertyWrapper', node: candidate[current2]}, candidate, current2]); | ||
} else { | ||
worklist.push([candidate[current2], candidate, current2]); | ||
} | ||
} | ||
} | ||
} else { | ||
worklist.push([candidate, node, candidates[current]]); | ||
} | ||
} | ||
} | ||
current2 = candidate.length; | ||
while ((current2 -= 1) >= 0) { | ||
if (!candidate[current2]) { | ||
continue; | ||
} | ||
if (nodeType === Syntax.ObjectExpression && 'properties' === candidates[current] && null == candidates[current].type) { | ||
element = new Element(candidate[current2], key + '[' + current2 + ']', 'Property', new Reference(candidate, current2)); | ||
} else { | ||
element = new Element(candidate[current2], key + '[' + current2 + ']', null, new Reference(candidate, current2)); | ||
} | ||
worklist.push(element); | ||
} | ||
@@ -308,6 +442,6 @@ } | ||
return result.top; | ||
return outer.root; | ||
} | ||
exports.version = '0.0.4'; | ||
exports.version = '1.0.0'; | ||
exports.Syntax = Syntax; | ||
@@ -318,3 +452,4 @@ exports.traverse = traverse; | ||
exports.VisitorOption = VisitorOption; | ||
exports.Controller = Controller; | ||
})); | ||
/* vim: set sw=4 ts=4 et tw=80 : */ |
@@ -6,3 +6,3 @@ { | ||
"main": "estraverse.js", | ||
"version": "0.0.4", | ||
"version": "1.0.0", | ||
"engines": { | ||
@@ -21,2 +21,3 @@ "node": ">=0.4.0" | ||
"devDependencies": { | ||
"jshint": "~1.1.0", | ||
"mocha": "*", | ||
@@ -30,4 +31,5 @@ "chai": "*" | ||
"scripts": { | ||
"test": "./node_modules/.bin/mocha" | ||
"test": "npm run-script lint", | ||
"lint": "node_modules/.bin/jshint estraverse.js" | ||
} | ||
} |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
20330
6
439
1
3