You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
4.4 KiB
JavaScript
150 lines
4.4 KiB
JavaScript
'use strict';
|
|
|
|
const chalk = require("chalk");
|
|
const repeatString = require("repeat-string");
|
|
const createEventEmitter = require("create-event-emitter");
|
|
|
|
const findNestedProperties = require("./find-nested-properties");
|
|
|
|
module.exports = function createWalker(tree, logging = false) {
|
|
let properties = findNestedProperties(tree);
|
|
|
|
return createEventEmitter({
|
|
tree: tree,
|
|
done: false,
|
|
currentLevel: 0,
|
|
currentProperty: properties[0],
|
|
currentPropertyIndex: 0,
|
|
currentStack: [ tree ],
|
|
currentIndex: 0,
|
|
currentNode: tree,
|
|
properties: [ null ],
|
|
indexes: [ null ],
|
|
propertyIndexes: [ null ],
|
|
stacks: [ [tree] ],
|
|
nodes: [ tree ], // FIXME: This might be redundant?
|
|
_padding: function _padding() {
|
|
return repeatString(" ", this.currentLevel);
|
|
},
|
|
_debugStatus: function () {
|
|
return `(level: ${this.currentLevel}, property: ${this.currentProperty.property}, index: ${this.currentIndex})`;
|
|
},
|
|
getPath: function getPath() {
|
|
let path = new Array(this.currentLevel * 2);
|
|
|
|
for (let i = 1; i <= this.currentLevel; i++) {
|
|
path[(i-1) * 2] = this.properties[i][this.propertyIndexes[i]].property;
|
|
path[(i-1) * 2 + 1] = this.indexes[i];
|
|
}
|
|
|
|
return path;
|
|
},
|
|
step: function step() {
|
|
if (this.done !== true) {
|
|
let node = this.currentStack[this.currentIndex];
|
|
|
|
if (logging) {
|
|
console.log(this._padding(), chalk.bold.red(`Node encountered: ${node.type}`), this._debugStatus());
|
|
}
|
|
|
|
this.emit("enterNode", node);
|
|
|
|
this.levelDown(node);
|
|
} else {
|
|
throw new Error("Walker reached end");
|
|
}
|
|
},
|
|
nextIndex: function nextIndex() {
|
|
this.emit("exitNode", this.currentStack[this.currentIndex]);
|
|
|
|
if (this.currentIndex < this.currentStack.length - 1) {
|
|
this.updateIndex(this.currentIndex + 1);
|
|
|
|
if (logging) {
|
|
console.log(this._padding(), chalk.bold.blue(`New index: ${this.currentIndex}`), this._debugStatus());
|
|
}
|
|
} else {
|
|
this.nextProperty();
|
|
}
|
|
},
|
|
nextProperty: function nextProperty() {
|
|
if (this.currentPropertyIndex < this.properties[this.currentLevel].length - 1) {
|
|
this.updateIndex(0);
|
|
this.updatePropertyIndex(this.currentPropertyIndex + 1);
|
|
|
|
if (logging) {
|
|
console.log(this._padding(), chalk.bold.yellow(`New property: ${this.currentProperty.property}`), this._debugStatus());
|
|
console.log(this._padding(), chalk.bold.blue(`New index: ${this.currentIndex}`), this._debugStatus());
|
|
}
|
|
} else {
|
|
this.levelUp();
|
|
}
|
|
},
|
|
levelUp: function levelUp() {
|
|
if (this.currentLevel > 1) {
|
|
this.currentLevel -= 1;
|
|
|
|
this.currentNode = this.nodes[this.currentLevel];
|
|
|
|
this.updateIndex(this.indexes[this.currentLevel]);
|
|
this.updatePropertyIndex(this.propertyIndexes[this.currentLevel]);
|
|
|
|
if (logging) {
|
|
console.log(this._padding(), chalk.bold.green(`New level: ${this.currentLevel}`), this._debugStatus());
|
|
}
|
|
|
|
this.nextIndex();
|
|
} else {
|
|
this.emit("exitNode", tree);
|
|
this.done = true;
|
|
}
|
|
},
|
|
levelDown: function levelDown(node) {
|
|
let nestedProperties = findNestedProperties(node);
|
|
|
|
if (nestedProperties.length > 0) {
|
|
this.currentLevel += 1;
|
|
|
|
this.nodes[this.currentLevel] = node;
|
|
this.currentNode = node;
|
|
|
|
this.properties[this.currentLevel] = nestedProperties;
|
|
|
|
this.updateIndex(0);
|
|
this.updatePropertyIndex(0);
|
|
|
|
if (logging) {
|
|
console.log(this._padding(), chalk.bold.green(`New level: ${this.currentLevel}`), this._debugStatus());
|
|
console.log(this._padding(), chalk.bold.yellow(`New property: ${this.currentProperty.property}`), this._debugStatus());
|
|
console.log(this._padding(), chalk.bold.blue(`New index: ${this.currentIndex}`), this._debugStatus());
|
|
}
|
|
} else {
|
|
this.nextIndex();
|
|
}
|
|
},
|
|
updateIndex: function updateIndex(index) {
|
|
this.currentIndex = index;
|
|
this.indexes[this.currentLevel] = index;
|
|
},
|
|
updatePropertyIndex: function updatePropertyIndex(index) {
|
|
this.currentPropertyIndex = index;
|
|
this.propertyIndexes[this.currentLevel] = index;
|
|
|
|
this.currentProperty = this.properties[this.currentLevel][index];
|
|
|
|
let newStack;
|
|
|
|
if (this.currentProperty.type === "array") {
|
|
newStack = this.currentNode[this.currentProperty.property];
|
|
} else if (this.currentProperty.type === "node") {
|
|
newStack = [ this.currentNode[this.currentProperty.property] ];
|
|
} else {
|
|
throw new Error("Unrecognized property type");
|
|
}
|
|
|
|
this.stacks[this.currentLevel] = newStack;
|
|
this.currentStack = newStack;
|
|
}
|
|
});
|
|
};
|