所谓遍历阶段本质上就是对于抽象语法树的一个深度遍历过程。 这一小节就是要实现两个功能
1.首先我们需要遍历上一节生成的ast树
2.引入visitor模式,可以使用户对当前节点进行增删改查。
那么对于第一个功能,这里展开说一下:
一开始我们会拿到当前的根节点,然后接下来去遍历,这里需要知道的是根节点的孩子节点都是存在body里面的,然后当遇到number类型比如2这种节点的时候是没有子节点的,那后续就不用管了。但是当我们遇到表达式这种子节点的时候,还得继续去遍历。那这就是深度优先搜索的一个整体逻辑
import { ChildNode, NodeTypes, RootNode, CallExpressionNode } from "./ast";
export function traverser(rootNode: RootNode) {
// 遍历树 深度优先搜索
function traverArray(array: ChildNode[]) {
array.forEach((node) => {
traverNode(node);
});
}
function traverNode(node: RootNode | ChildNode) {
switch (node.type) {
case NodeTypes.Program:
traverArray(node.body);
break;
case NodeTypes.CallExpression:
traverArray(node.params);
break;
case NodeTypes.NumberLiteral:
break;
default:
break;
}
}
traverNode(rootNode);
}
那接下来我们就来说说引入visitor模式。 那一开始肯定是进入enter模式,其余的就不多说了,详细请看以下
import { ChildNode, NodeTypes, RootNode, CallExpressionNode } from "./ast";
type ParentNode = RootNode | CallExpressionNode | undefined;
type MethodFn = (node: RootNode | ChildNode, parent: ParentNode) => void;
interface VisitorOption {
enter: MethodFn;
exit?: MethodFn;
}
export interface Visitor {
Program?: VisitorOption;
NumberLiteral?: VisitorOption;
CallExpression?: VisitorOption;
StringLiteral?: VisitorOption;
}
export function traverser(rootNode: RootNode, visitor: Visitor) {
// 遍历树 深度优先搜索
function traverArray(array: ChildNode[], parent: ParentNode) {
array.forEach((node) => {
traverNode(node, parent);
});
}
function traverNode(node: RootNode | ChildNode, parent?: ParentNode) {
// enter
const methods = visitor[node.type];
if (methods) {
methods.enter(node, parent);
}
switch (node.type) {
case NodeTypes.Program:
traverArray(node.body, node);
break;
case NodeTypes.CallExpression:
traverArray(node.params, node);
break;
case NodeTypes.NumberLiteral:
break;
default:
break;
}
// exit
if (methods && methods.exit) {
methods.exit(node, parent);
}
}
traverNode(rootNode);
}