上一节我们经过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
}