AST语法树飞速入门

90 阅读3分钟

什么是AST?

AST(Abstract Syntax Tree,抽象语法树)是源代码的抽象语法结构的树状表示,它以树形结构表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

如何查看AST

astexplorer.net/

为什么需要AST?

  • 代码分析​​:编译器/解释器理解代码结构的基础
  • 代码转换​​:Babel等工具进行代码转换的中间表示
  • ​​代码优化​​:在保持语义不变的前提下优化代码结构
  • 代码生成​​:从AST生成目标代码(如JS、机器码)
  • 静态分析​​:代码检查、类型检查等

AST基本使用

基本结构

const num = 1举例,我们可以看到 num 的声明很清晰的展现在了AST语法树中,其中包括:类型、位置、名称、值等,所以我们也可以将 AST语法树 看做代码最详细的描述,其中包含了计算机需要理解的一切信息

{
        "type": "VariableDeclaration",
        "start": 0,
        "end": 13,
        "loc": {...},
        "declarations": [
          {
            "type": "VariableDeclarator",
            "start": 6,
            "end": 11,
            "loc": {...},
            "id": {
              "type": "Identifier",
              "start": 6,
              "end": 9,
              "loc": {...},
              "name": "num"
            },
            "init": {
              "type": "NumericLiteral",
              "start": 12,
              "end": 13,
              "loc": {...},
              "extra": {
                "rawValue": 1,
                "raw": "1"
              },
              "value": 1
            }
          }
        ],
        "kind": "const"
      }

AST 常见节点类型

以下是 JavaScript/TypeScript AST 中常见的节点类型及其说明:

节点类型示例代码说明
Programconst a = 1;整个程序的根节点
VariableDeclarationlet a = 1;变量声明节点
VariableDeclaratora = 1变量声明中的声明器
Identifiera标识符(变量名、函数名等)
Literal1, "hello"字面量(值)
BinaryExpressiona + b二元运算表达式
AssignmentExpressiona = b赋值表达式
MemberExpressionobj.property成员访问表达式
CallExpressionfn()函数调用表达式
FunctionDeclarationfunction fn() {}函数声明
ArrowFunctionExpression() => {}箭头函数表达式
BlockStatement{ ... }代码块
ExpressionStatementa++;表达式语句
IfStatementif (a) {}if 条件语句
ForStatementfor (let i=0; i<10; i++) {}for 循环语句
WhileStatementwhile (true) {}while 循环语句
ReturnStatementreturn a;return 语句
ObjectExpression{ a: 1 }对象字面量
ArrayExpression[1, 2]数组字面量
Propertya: 1对象属性
TemplateLiteral`Hello ${name}`模板字符串
JSXElement<div></div>JSX 元素
JSXAttribute<div className="app" />JSX 属性
ImportDeclarationimport React from 'react'import 声明
ExportDefaultDeclarationexport default App默认导出声明
ClassDeclarationclass A {}类声明
NewExpressionnew Date()new 表达式
ThisExpressionthisthis 表达式
SpreadElement[...arr]展开运算符
TypeAnnotation: number (TypeScript)类型注解
TSTypeAliasDeclarationtype A = string (TypeScript)类型别名声明

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const t = require('@babel/types');
const code = `
function example() {
  console.log('Original code');
}
`;

const ast = parser.parse(code);

traverse(ast, {
  FunctionDeclaration(path) {
    // 在函数体开头添加 console.log
    const newStatement = t.expressionStatement(
      t.callExpression(
        t.memberExpression(t.identifier('console'), t.identifier('log')),
        [t.stringLiteral('Added at top')]
      )
    );
    
    path.get('body').unshiftContainer('body', newStatement);
  }
});

const output = generator(ast).code;
console.log(output);
/*
输出:
function example() {
  console.log("Added at top");
  console.log('Original code');
}
*/

const code = `const a = 1, b = 2;`;

const ast = parser.parse(code);

// 删除特定变量声明
traverse(ast, {
  VariableDeclarator(path) {
    if (path.node.id.name === 'a') {
      path.remove();
    }
  }
});

console.log(generator(ast).code);
// 输出: const b = 2;



const code = `const message = 'Hello';`;

// 1. 解析为AST
const ast = parser.parse(code);

// 2. 遍历并修改
traverse(ast, {
  StringLiteral(path) {
    // 修改所有字符串值为'Modified'
    path.node.value = 'Modified';
  }
});

// 3. 生成代码
const output = generator(ast).code;
console.log(output); // 输出: const message = "Modified";

const code = `
	const a = 1;
	function foo() { return 'bar'; }
`;

const ast = parser.parse(code);

// 查询所有变量声明
traverse(ast, {
  VariableDeclaration(path) {
    console.log('找到变量声明:', path.node.kind);
    path.node.declarations.forEach(decl => {
      console.log('变量名:', decl.id.name);
    });
  }
});

// 查询特定函数
traverse(ast, {
  FunctionDeclaration(path) {
    if (path.node.id.name === 'foo') {
      console.log('找到foo函数:', path.node);
    }
  }
});

包裹

// 原始代码
const code = `const a = 1;`;

// 1. 解析为 AST
const ast = parser.parse(code);

// 2. 遍历并转换
traverse(ast, {
  VariableDeclaration(path) {
    // 创建新的函数声明
    const functionDeclaration = t.functionDeclaration(
      t.identifier('b'), // 函数名
      [], // 参数列表
      t.blockStatement([
        // 将原变量声明放入函数体
        path.node
      ])
    );
    
    // 替换原变量声明为函数声明
    path.replaceWith(functionDeclaration);
  }
});

// 3. 生成转换后的代码
const output = generator(ast).code;
console.log(output);
// 输出: function b() { const a = 1; }