乐趣区

AST-抽象语法树-学习

Reference

AST 与前端工程化实战
AST 抽象语法树——最基础的 javascript 重点知识,99% 的人根本不了解
13 个示例快速入门 JS 抽象语法树

AST Explorer

https://astexplorer.net/

概念

JavaScript 解析:
  • 词法分析(Lexical Analysis):把 JavaScript 代码(字符串)的字符流(char stream)按照 ECMAScript 标准转换为记号流(token stream)。
  • 语法分析(Syntactic Analysis):将词法单元流转换成一个由元素逐级嵌套组成的语法结构树,AST

Demo

代码:

var AST = “is Tree”;

词法分析 => 记号流

Keyword: var

Identifier: AST

Punctuator: =

String: “is Tree”

Punctuator: ;

语法分析 => AST

AST 工具:Recast

解析器(recast.parse): code => AST
Code Case 1
const recast = require("recast");
const code =
  `
  function add(a, b) {
    return a +
      // 有什么奇怪的东西混进来了
      b
  }
  `
const ast = recast.parse(code);
const add  = ast.program.body[0]

console.log(add)
Output Of Case 1
{
    "type": "FunctionDeclaration",
    "id": {
        "type": "Identifier",
        "name": "add"
    },
    "params": [
        {
            "type": "Identifier",
            "name": "a"
        },
        {
            "type": "Identifier",
            "name": "b"
        }
    ],
    "body": {
        "type": "BlockStatement",
        "body": [
            {
                "type": "ReturnStatement",
                "argument": {
                    "type": "BinaryExpression",
                    "operator": "+",
                    "left": {
                        "type": "Identifier",
                        "name": "a"
                    },
                    "right": {
                        "type": "Identifier",
                        "name": "b",
                        "comments": [
                            {
                                "type": "Line",
                                "value": "有什么奇怪的东西混进来了",
                                "loc": {},
                                "leading": true,
                                "trailing": false
                            }
                        ]
                    }
                }
            }
        ]
    },
    "generator": false,
    "expression": false,
    "async": false
}
制作模具(recast.types.builders):AST => code
Code Case 2

在 Code Case 1 代码后加入以下代码,对 ast 进行重新组装:

  • 将 add 方法改为箭头函数
  • 增加 squareSum 计算平方和方法
// 引入变量声明,变量符号,函数声明三种“模具”const { 
    variableDeclaration, 
    variableDeclarator, 
    identifier: id, 
    arrowFunctionExpression, 
    binaryExpression,
    blockStatement,
    returnStatement
 } = recast.types.builders

// 将准备好的组件置入模具,并组装回原来的 ast 对象。// 将 add 方法改为箭头函数
ast.program.body[0] = variableDeclaration("const", [
  variableDeclarator(add.id, arrowFunctionExpression(
    add.params,
    binaryExpression('+', ...add.params)
  ))
]);

// 新增 squareSum 计算平方和方法
ast.program.body.push(variableDeclaration('var', [variableDeclarator(id('squareSum'), arrowFunctionExpression([id('a'), id('b')],
        blockStatement([
            variableDeclaration('let', [variableDeclarator(id('c'), binaryExpression('*', id('a'), id('a'))),
              variableDeclarator(id('d'), binaryExpression('*', id('b'), id('b')))]),
            returnStatement(binaryExpression('+', id('c'), id('d')))
        ])
        
    ))
]))

// 将 AST 对象重新转回可以阅读的代码
const output = recast.print(ast).code;

console.log(output)
Output Of Case 2
const add = (a, b) => a + b;

var squareSum = (a, b) => {
  let c = a * a, d = b * b;
  return c + d;
};

树节点遍历(recast.types.visit)

recast.visit(ast, {visitExpressionStatement: function(path) {const { node} = path;
    },
    visitBlockStatement(path) {// do something here}
});

应用场景

  1. 解释器和编译器
  2. 静态代码分析(抽离重复代码,判断代码相似性)
  3. 代码转换

  4. 代码格式化
退出移动版