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').defaultlet 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
之后