乐趣区

关于ast:AST语法树增删改查

AST 是 Abstract Syntax Tree 的缩写,即“形象语法树”. 它是以树状的模式体现编程语言的语法结构. webpack 打包 JS 代码的时候,webpack 会在咱们的原有代码根底上新增一些代码,例如咱们能够在打包 JS 代码的时候将高级代码转为低级代码,就是通过 AST 语法树来实现的
AST 在线生成地址
babel 插件查看应用地址

AST 生成过程由源码 -> 词法剖析 -> 语法分析 -> 形象语法树
例如
let a = 1 + 2
词法剖析

  • 从左至右一个字符一个字符地读入源程序,从中辨认出一个一个“单词”“符号”等 读出来的就是一般的字符,没有任何编程语言的函数
  • 将剖析之后后果保留在一个词法单元数组中
单词 单词 符号 数字 符号 数字
let a = 1 + 2
[{"type": "word", value: "let"},
  {"type": "word", value: "a"},
  {"type": "Punctuator", value: "="},
  {"type": "Numberic", value: "1"},
  {"type": "Punctuator", value: "+"},
  {"type": "Numberic", value: "2"},
]

之后进入词法剖析
语法分析
将单词序列组合成各类的语法短语

关键字 标识符 赋值运算符 字面量 二元运算符 字面量
let a = 1 + 2
[{
  "type": "VariableDecLaration", 
  "content": {{"type": "kind", "value": "let"},  // kind 示意是什么类型的申明
    {"type": "Identifier", "value": "a"},  // Identifier 示意是标识符
    {"type": "init", "value": "="},  // 示意初始值的表达式
    {"type": "Literal", "value": "1"},  // Literal 示意是一个字面量
    {"type": "operator", "value": "+"},  // operator 示意是一个二元运算符
    {"type": "Literal", "value": "2"},
  } 
}]

形象语法树

 "program": {
    "type": "Program",
    "start": 0,
    "end": 13,
    "loc": {
      "start": {
        "line": 1,
        "column": 0
      },
      "end": {
        "line": 1,
        "column": 13
      }
    },
    "sourceType": "module",
    "interpreter": null,
    "body": [
      {
        "type": "VariableDeclaration",
        "start": 0,
        "end": 13,
        "loc": {
          "start": {
            "line": 1,
            "column": 0
          },
          "end": {
            "line": 1,
            "column": 13
          }
        },
        "declarations": [  // 这里是数组,示意能够同时申明多个变量
          {
            "type": "VariableDeclarator",
            "start": 4,
            "end": 13,
            "loc": {
              "start": {
                "line": 1,
                "column": 4
              },
              "end": {
                "line": 1,
                "column": 13
              }
            },
            "id": {
              "type": "Identifier",
              "start": 4,
              "end": 5,
              "loc": {
                "start": {
                  "line": 1,
                  "column": 4
                },
                "end": {
                  "line": 1,
                  "column": 5
                },
                "identifierName": "a"
              },
              "name": "a"
            },
            "init": {
              "type": "BinaryExpression",
              "start": 8,
              "end": 13,
              "loc": {
                "start": {
                  "line": 1,
                  "column": 8
                },
                "end": {
                  "line": 1,
                  "column": 13
                }
              },
              "left": {
                "type": "NumericLiteral",
                "start": 8,
                "end": 9,
                "loc": {
                  "start": {
                    "line": 1,
                    "column": 8
                  },
                  "end": {
                    "line": 1,
                    "column": 9
                  }
                },
                "extra": {
                  "rawValue": 1,
                  "raw": "1"
                },
                "value": 1
              },
              "operator": "+",
              "right": {
                "type": "NumericLiteral",
                "start": 12,
                "end": 13,
                "loc": {
                  "start": {
                    "line": 1,
                    "column": 12
                  },
                  "end": {
                    "line": 1,
                    "column": 13
                  }
                },
                "extra": {
                  "rawValue": 2,
                  "raw": "2"
                },
                "value": 2
              }
            }
          }
        ],
        "kind": "let"
      }
    ],
    "directives": []}

程序示例

package.json依赖

    "@babel/generator": "^7.11.6",  // 转换 AST 构造
    "@babel/parser": "^7.11.5",  // 拆解 AST 树结构
    "@babel/traverse": "^7.11.5", 
    "@babel/types": "^7.11.5",

@babel/parse 生成 AST

import * as parser from '@babel/parser'

const code = `let a = 1 + 2`
const ast = parser.parse(code)
console.log(ast)

批改语法树

通过 babel 的 traverse 模块遍历节点,找到对应节点后,通过 babel 中的 generator 模块来转换语法树为代码

import * as parser from '@babel/parser'
import traverse from "@babel/traverse"
import generator from '@babel/generator'

const code = `let val = 1 + 2`
const ast = parser.parse(code)
console.log(ast)

// traverse 办法能够遍历所有的语法树结点
traverse(ast, {enter(path) {  // 这个 path 会找到所有的 node
    if (path.node.type == 'Identifier') {
      path.node.name = 'modify'
      path.stop()}
  }
})

const ret = generator(ast)
console.log(ret)

这里会把 val 批改为 modify

创立语法树

通过@babel/types 模块创立语法树节点而后 push 到 body 中就能够实现语法树的手动创立,

import * as parser from '@babel/parser'
import traverse from "@babel/traverse"
import generator from '@babel/generator'
import * as t from '@babel/types'

let code = ``
let ast = parser.parse(code)

let left = t.NumericLiteral(1)
let right = t.NumericLiteral(2)
let init = t.binaryExpression("+", left, right)

let id = t.identifier("add")
let variable = t.variableDeclarator(id, init)
let declaration = t.variableDeclaration('let', [variable])
ast.program.body.push(declaration)


// 转换为 code 
let genCode = generator(ast)
console.log(genCode.code)
删除语法树节点

想要删除语法节点的外围就是先遍历找到所有的节点,通过 @babel/traverse 来实现,找到每个节点之后就能够通过具体的办法来实现增删改查操作

  • NodePath 罕用的属性
    • node:获取以后节点
    • parent:父节点
    • parentPath:父 path
    • scope:作用域
    • context:上下文
  • NodePath 罕用的办法
    • get:以后节点
    • findParent:向父节点搜查节点
    • getSibling:获取兄弟节点
    • replaceWith:用 AST 节点替换该节点
    • replaceWithMultiple:用多个 AST 节点替换该节点
    • insertBefore:在节点前插入节点
    • insertAfter:在节点后插入节点
    • remove: 删除节点
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default  
const generator = require('@babel/generator').default

let code = `
  console.log('jake')
  let sum = 1 + 2 
  let minus = 2 - 1
  console.log("tom")
`
let ast = parser.parse(code)
console.log(ast)

// traverse 遍历语法树的
traverse(ast, {Identifier(path) {if (path.node.name == 'sum') {path.parentPath.remove()
    }
  }
})

console.log(generator(ast))

删除了 sum 之后

退出移动版