refactor: rewrite FastPath class (#6225)
parent
ff7bc1c008
commit
e068c318b4
|
@ -1,33 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const assert = require("assert");
|
|
||||||
|
|
||||||
function FastPath(value) {
|
|
||||||
assert.ok(this instanceof FastPath);
|
|
||||||
this.stack = [value];
|
|
||||||
}
|
|
||||||
|
|
||||||
// The name of the current property is always the penultimate element of
|
|
||||||
// this.stack, and always a String.
|
|
||||||
FastPath.prototype.getName = function getName() {
|
|
||||||
const s = this.stack;
|
|
||||||
const len = s.length;
|
|
||||||
if (len > 1) {
|
|
||||||
return s[len - 2];
|
|
||||||
}
|
|
||||||
// Since the name is always a string, null is a safe sentinel value to
|
|
||||||
// return if we do not know the name of the (root) value.
|
|
||||||
/* istanbul ignore next */
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The value of the current property is always the final element of
|
|
||||||
// this.stack.
|
|
||||||
FastPath.prototype.getValue = function getValue() {
|
|
||||||
const s = this.stack;
|
|
||||||
return s[s.length - 1];
|
|
||||||
};
|
|
||||||
|
|
||||||
function getNodeHelper(path, count) {
|
function getNodeHelper(path, count) {
|
||||||
const stackIndex = getNodeStackIndexHelper(path.stack, count);
|
const stackIndex = getNodeStackIndexHelper(path.stack, count);
|
||||||
return stackIndex === -1 ? null : path.stack[stackIndex];
|
return stackIndex === -1 ? null : path.stack[stackIndex];
|
||||||
|
@ -43,99 +15,119 @@ function getNodeStackIndexHelper(stack, count) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FastPath.prototype.getNode = function getNode(count) {
|
class FastPath {
|
||||||
return getNodeHelper(this, ~~count);
|
constructor(value) {
|
||||||
};
|
this.stack = [value];
|
||||||
|
|
||||||
FastPath.prototype.getParentNode = function getParentNode(count) {
|
|
||||||
return getNodeHelper(this, ~~count + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Temporarily push properties named by string arguments given after the
|
|
||||||
// callback function onto this.stack, then call the callback with a
|
|
||||||
// reference to this (modified) FastPath object. Note that the stack will
|
|
||||||
// be restored to its original state after the callback is finished, so it
|
|
||||||
// is probably a mistake to retain a reference to the path.
|
|
||||||
FastPath.prototype.call = function call(callback /*, name1, name2, ... */) {
|
|
||||||
const s = this.stack;
|
|
||||||
const origLen = s.length;
|
|
||||||
let value = s[origLen - 1];
|
|
||||||
const argc = arguments.length;
|
|
||||||
for (let i = 1; i < argc; ++i) {
|
|
||||||
const name = arguments[i];
|
|
||||||
value = value[name];
|
|
||||||
s.push(name, value);
|
|
||||||
}
|
|
||||||
const result = callback(this);
|
|
||||||
s.length = origLen;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
FastPath.prototype.callParent = function callParent(callback, count) {
|
|
||||||
const stackIndex = getNodeStackIndexHelper(this.stack, ~~count + 1);
|
|
||||||
const parentValues = this.stack.splice(stackIndex + 1);
|
|
||||||
const result = callback(this);
|
|
||||||
Array.prototype.push.apply(this.stack, parentValues);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Similar to FastPath.prototype.call, except that the value obtained by
|
|
||||||
// accessing this.getValue()[name1][name2]... should be array-like. The
|
|
||||||
// callback will be called with a reference to this path object for each
|
|
||||||
// element of the array.
|
|
||||||
FastPath.prototype.each = function each(callback /*, name1, name2, ... */) {
|
|
||||||
const s = this.stack;
|
|
||||||
const origLen = s.length;
|
|
||||||
let value = s[origLen - 1];
|
|
||||||
const argc = arguments.length;
|
|
||||||
|
|
||||||
for (let i = 1; i < argc; ++i) {
|
|
||||||
const name = arguments[i];
|
|
||||||
value = value[name];
|
|
||||||
s.push(name, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < value.length; ++i) {
|
// The name of the current property is always the penultimate element of
|
||||||
if (i in value) {
|
// this.stack, and always a String.
|
||||||
s.push(i, value[i]);
|
getName() {
|
||||||
// If the callback needs to know the value of i, call
|
const s = this.stack;
|
||||||
// path.getName(), assuming path is the parameter name.
|
const { length } = s;
|
||||||
callback(this);
|
if (length > 1) {
|
||||||
s.length -= 2;
|
return s[length - 2];
|
||||||
}
|
}
|
||||||
|
// Since the name is always a string, null is a safe sentinel value to
|
||||||
|
// return if we do not know the name of the (root) value.
|
||||||
|
/* istanbul ignore next */
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.length = origLen;
|
// The value of the current property is always the final element of
|
||||||
};
|
// this.stack.
|
||||||
|
getValue() {
|
||||||
// Similar to FastPath.prototype.each, except that the results of the
|
const s = this.stack;
|
||||||
// callback function invocations are stored in an array and returned at
|
return s[s.length - 1];
|
||||||
// the end of the iteration.
|
|
||||||
FastPath.prototype.map = function map(callback /*, name1, name2, ... */) {
|
|
||||||
const s = this.stack;
|
|
||||||
const origLen = s.length;
|
|
||||||
let value = s[origLen - 1];
|
|
||||||
const argc = arguments.length;
|
|
||||||
|
|
||||||
for (let i = 1; i < argc; ++i) {
|
|
||||||
const name = arguments[i];
|
|
||||||
value = value[name];
|
|
||||||
s.push(name, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = new Array(value.length);
|
getNode(count) {
|
||||||
|
return getNodeHelper(this, ~~count);
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < value.length; ++i) {
|
getParentNode(count) {
|
||||||
if (i in value) {
|
return getNodeHelper(this, ~~count + 1);
|
||||||
s.push(i, value[i]);
|
}
|
||||||
result[i] = callback(this, i);
|
|
||||||
s.length -= 2;
|
// Temporarily push properties named by string arguments given after the
|
||||||
|
// callback function onto this.stack, then call the callback with a
|
||||||
|
// reference to this (modified) FastPath object. Note that the stack will
|
||||||
|
// be restored to its original state after the callback is finished, so it
|
||||||
|
// is probably a mistake to retain a reference to the path.
|
||||||
|
call(callback, ...names) {
|
||||||
|
const s = this.stack;
|
||||||
|
const origLen = s.length;
|
||||||
|
let value = s[origLen - 1];
|
||||||
|
for (const name of names) {
|
||||||
|
value = value[name];
|
||||||
|
s.push(name, value);
|
||||||
}
|
}
|
||||||
|
const result = callback(this);
|
||||||
|
s.length = origLen;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.length = origLen;
|
callParent(callback, count) {
|
||||||
|
const stackIndex = getNodeStackIndexHelper(this.stack, ~~count + 1);
|
||||||
|
const parentValues = this.stack.splice(stackIndex + 1);
|
||||||
|
const result = callback(this);
|
||||||
|
this.stack.push(...parentValues);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
// Similar to FastPath.prototype.call, except that the value obtained by
|
||||||
};
|
// accessing this.getValue()[name1][name2]... should be array-like. The
|
||||||
|
// callback will be called with a reference to this path object for each
|
||||||
|
// element of the array.
|
||||||
|
each(callback, ...names) {
|
||||||
|
const s = this.stack;
|
||||||
|
const origLen = s.length;
|
||||||
|
let value = s[origLen - 1];
|
||||||
|
for (const name of names) {
|
||||||
|
value = value[name];
|
||||||
|
s.push(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < value.length; ++i) {
|
||||||
|
if (i in value) {
|
||||||
|
s.push(i, value[i]);
|
||||||
|
// If the callback needs to know the value of i, call
|
||||||
|
// path.getName(), assuming path is the parameter name.
|
||||||
|
callback(this);
|
||||||
|
s.length -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.length = origLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar to FastPath.prototype.each, except that the results of the
|
||||||
|
// callback function invocations are stored in an array and returned at
|
||||||
|
// the end of the iteration.
|
||||||
|
map(callback, ...names) {
|
||||||
|
const s = this.stack;
|
||||||
|
const origLen = s.length;
|
||||||
|
let value = s[origLen - 1];
|
||||||
|
|
||||||
|
for (const name of names) {
|
||||||
|
value = value[name];
|
||||||
|
s.push(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = new Array(value.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < value.length; ++i) {
|
||||||
|
if (i in value) {
|
||||||
|
s.push(i, value[i]);
|
||||||
|
result[i] = callback(this, i);
|
||||||
|
s.length -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.length = origLen;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = FastPath;
|
module.exports = FastPath;
|
||||||
|
|
Loading…
Reference in New Issue