转化阶段Transformation

69 阅读1分钟

上一节我们经过traverse遍历阶段对于生成的抽象语法树进行深度遍历节点,接下来我们将进行转化阶段(Transformation)对某些节点进行修改。 这里我们可以将我们现在需要生成的ast的形式与之前的ast进行一个对比

  const originalAST: RootNode = {
    type: NodeTypes.Program,
    body: [
      {
        type: NodeTypes.CallExpression,
        name: "add",
        params: [
          {
            type: NodeTypes.NumberLiteral,
            value: "2",
          },
          {
            type: NodeTypes.CallExpression,
            name: "subtract",
            params: [
              {
                type: NodeTypes.NumberLiteral,
                value: "4",
              },
              {
                type: NodeTypes.NumberLiteral,
                value: "2",
              },
            ],
          },
        ],
      },
    ],
  };

这里再来看一下我们需要生成的新的ast树的形式

const transformedAST = {
    type: NodeTypes.Program,
    body: [
      {
        type: "ExpressionStatement",
        expression: {
          type: "CallExpression",
          callee: {
            type: "Identifier",
            name: "add",
          },
          arguments: [
            {
              type: "NumberLiteral",
              value: "2",
            },
            {
              type: "CallExpression",
              callee: {
                type: "Identifier",
                name: "subtract",
              },
              arguments: [
                {
                  type: "NumberLiteral",
                  value: "4",
                },
                {
                  type: "NumberLiteral",
                  value: "2",
                },
              ],
            },
          ],
        },
      },
    ],
  };

那我们通过这里的对比可以看到,基本上是差不多的,只不过结构发生了一点点的改变。那我们就来开始生成新的ast树吧。

这里有一个注意的点,就是新的ast树是需要有一个ExpressionStatement进行包裹的,那什么时候会生成这个呢?那就是当我们遇到表达式的时候,当这个表达式的父级不是表达式的话,我们就需要生成ExpressionStatement来进行包裹。

这里还有一个需要注意的点是,如何将新生成的ast结构存储到newAst里面,那这里我们通过创建context去指向这个新的ast的body里面。

import { RootNode, NodeTypes } from "./ast";
import { traverser } from "./traverser";
export function transform(ast: RootNode) {
    const newAst = {
        type: NodeTypes.Program,
        body: []
    }
    // 创建一个新的字段将旧的ast给新的
    ast.context = newAst.body
    traverser(ast, {
        CallExpression: {
            enter(node, parent) {
                if (node.type === NodeTypes.CallExpression) {
                    let expression: any = {
                        type: "CallExpression",
                        callee: {
                            type: "Identifier",
                            name: node.name
                        },
                        arguments: [],
                    }
                    node.context = expression.arguments;
                    if (parent?.type !== NodeTypes.CallExpression) {
                        expression = {
                            type: "ExpressionStatement",
                            expression
                        }
                    }
                    parent?.context?.push(expression)

                }
            }
        },
        NumberLiteral: {
            enter(node, parent) {
                if (node.type === NodeTypes.NumberLiteral) {
                    const numberNode: any = {
                        type: "NumberLiteral",
                        value: node.value
                    };
                    parent?.context?.push(numberNode)
                }
            }
        }
    })
    return newAst
}