UNPKG

@rightcapital/phpdoc-parser

Version:

TypeScript version of PHPDoc parser with support for intersection types and generics

217 lines (216 loc) • 9.5 kB
"use strict"; 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;