Reference

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

AST Explorer

https://astexplorer.net/

概念

JavaScript解析

preview

Demo

代码:

var AST = "is Tree";

词法分析 => 记号流

Keyword: var

Identifier: AST

Punctuator: =

String: "is Tree"

Punctuator: ;

语法分析 => AST

image-20190827143034622

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. 代码转换

    image-20190827140720601

  4. 代码格式化

spoonysnail
80 声望1 粉丝