遍历阶段Traverse

305 阅读1分钟

所谓遍历阶段本质上就是对于抽象语法树的一个深度遍历过程。 这一小节就是要实现两个功能

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);
}