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 中就可以实现语法树的手动创建,
AST语法树增删改查_第1张图片

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之后
image.png

你可能感兴趣的:(ast前端)