@rightcapital/phpdoc-parser
Version:
TypeScript version of PHPDoc parser with support for intersection types and generics
217 lines (216 loc) • 9.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeTraverser = void 0;
const base_node_1 = require("./base-node");
const const_expr_node_1 = require("./const-expr/const-expr-node");
const php_doc_child_node_1 = require("./php-doc/php-doc-child-node");
const php_doc_tag_value_node_1 = require("./php-doc/php-doc-tag-value-node");
const type_node_1 = require("./type/type-node");
class NodeTraverser {
constructor(visitors) {
this.visitors = visitors;
}
traverse(nodes) {
this.stopTraversal = false;
for (const visitor of this.visitors) {
const returned = visitor.beforeTraverse(nodes);
if (returned !== null) {
nodes = returned;
}
}
nodes = this.traverseArray(nodes);
for (const visitor of this.visitors) {
const returned = visitor.afterTraverse(nodes);
if (returned !== null) {
nodes = returned;
}
}
return nodes;
}
traverseNode(node) {
const subNodeNames = Object.keys(node);
for (const name of subNodeNames) {
if (name === 'attributes') {
continue;
}
let subNode = node[name];
if (Array.isArray(subNode)) {
subNode = this.traverseArray(subNode);
if (this.stopTraversal) {
break;
}
}
else if (subNode instanceof base_node_1.BaseNode) {
let traverseChildren = true;
let breakVisitorIndex = null;
for (let visitorIndex = 0; visitorIndex < this.visitors.length; ++visitorIndex) {
const visitor = this.visitors[visitorIndex];
const enterNodeResult = visitor.enterNode(subNode);
if (enterNodeResult === null) {
continue;
}
switch (enterNodeResult) {
case 1:
traverseChildren = false;
break;
case 4:
traverseChildren = false;
breakVisitorIndex = visitorIndex;
break;
case 2:
this.stopTraversal = true;
break;
default:
if (enterNodeResult instanceof base_node_1.BaseNode) {
this.ensureReplacementReasonable(subNode, enterNodeResult);
subNode = enterNodeResult;
}
else {
throw new Error(`enterNode() returned invalid value of type ${typeof enterNodeResult}: value=${JSON.stringify(enterNodeResult, undefined, 2)}`);
}
}
if (this.stopTraversal) {
break;
}
}
if (traverseChildren) {
subNode = this.traverseNode(subNode);
if (this.stopTraversal) {
break;
}
}
for (let visitorIndex = 0; visitorIndex < this.visitors.length; ++visitorIndex) {
const visitor = this.visitors[visitorIndex];
const leaveNodeResult = visitor.leaveNode(subNode);
if (leaveNodeResult != null) {
if (leaveNodeResult instanceof base_node_1.BaseNode) {
this.ensureReplacementReasonable(subNode, leaveNodeResult);
subNode = leaveNodeResult;
}
else if (leaveNodeResult === 2) {
this.stopTraversal = true;
break;
}
else if (Array.isArray(leaveNodeResult)) {
throw new Error('leaveNode() may only return an array ' +
'if the parent structure is an array');
}
else {
throw new Error(`leaveNode() returned invalid value of type ${typeof leaveNodeResult}`);
}
}
if (breakVisitorIndex === visitorIndex) {
break;
}
}
}
node[name] = subNode;
}
return node;
}
traverseArray(nodes) {
const doNodes = [];
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
if (node instanceof base_node_1.BaseNode) {
let traverseChildren = true;
let breakVisitorIndex = null;
for (let visitorIndex = 0; visitorIndex < this.visitors.length; visitorIndex++) {
const visitor = this.visitors[visitorIndex];
const returnVal = visitor.enterNode(node);
if (returnVal === null) {
continue;
}
if (returnVal instanceof base_node_1.BaseNode) {
this.ensureReplacementReasonable(node, returnVal);
node = returnVal;
}
else if (Array.isArray(returnVal)) {
doNodes.push([i, returnVal]);
continue;
}
else if (returnVal === 3) {
doNodes.push([i, []]);
continue;
}
else if (returnVal === 1) {
traverseChildren = false;
}
else if (returnVal === 4) {
traverseChildren = false;
breakVisitorIndex = visitorIndex;
break;
}
else if (returnVal === 2) {
this.stopTraversal = true;
break;
}
else {
throw new Error(`enterNode returned invalid value of type ${typeof returnVal}`);
}
}
if (traverseChildren) {
node = this.traverseNode(node);
if (this.stopTraversal) {
break;
}
}
for (let visitorIndex = 0; visitorIndex < this.visitors.length; visitorIndex++) {
const visitor = this.visitors[visitorIndex];
const returnVal = visitor.leaveNode(node);
if (returnVal !== null) {
if (returnVal instanceof base_node_1.BaseNode) {
this.ensureReplacementReasonable(node, returnVal);
node = returnVal;
}
else if (Array.isArray(returnVal)) {
doNodes.push([i, returnVal]);
break;
}
else if (returnVal === 3) {
doNodes.push([i, []]);
break;
}
else if (returnVal === 2) {
this.stopTraversal = true;
break;
}
else {
throw new Error(`leaveNode returned invalid value of type ${typeof returnVal}`);
}
}
if (breakVisitorIndex === visitorIndex) {
break;
}
}
}
else if (Array.isArray(node)) {
throw new Error('Invalid node structure: Contains nested arrays');
}
}
if (doNodes.length > 0) {
while (doNodes.length > 0) {
const [i, replace] = doNodes.pop();
nodes = [...nodes.slice(0, i), ...replace, ...nodes.slice(i + 1)];
}
}
return nodes;
}
ensureReplacementReasonable(old, newNode) {
if (old instanceof type_node_1.TypeNode && !(newNode instanceof type_node_1.TypeNode)) {
throw new Error(`Trying to replace TypeNode with ${newNode.constructor.name}`);
}
if (old instanceof const_expr_node_1.ConstExprNode && !(newNode instanceof const_expr_node_1.ConstExprNode)) {
throw new Error(`Trying to replace ConstExprNode with ${newNode.constructor.name}`);
}
if (old instanceof php_doc_child_node_1.PhpDocChildNode &&
!(newNode instanceof php_doc_child_node_1.PhpDocChildNode)) {
throw new Error(`Trying to replace PhpDocChildNode with ${newNode.constructor.name}`);
}
if (old instanceof php_doc_tag_value_node_1.PhpDocTagValueNode &&
!(newNode instanceof php_doc_tag_value_node_1.PhpDocTagValueNode)) {
throw new Error(`Trying to replace PhpDocTagValueNode with ${newNode.constructor.name}`);
}
}
}
exports.NodeTraverser = NodeTraverser;