AST(Abstract Syntax Tree)
通俗的理解抽象地将代码根据 源代码语法 生成对应的树状结构。
在js中,js引擎会将代码转换成AST,解释器根据AST生成字节码,提供给计算机。
js还有优化编译器, 它可以通过AST将代码直接转化为机器码。
应用
- IDE工具的提示,语法高亮、代码补全
- jsLint、jsHint对代码的错误/风格检测
- webpack、rollrollup进行代码打包,确定模块关系
- typeScript、jsx转化为原生的js
ast的parser有很多,如babelon7、acorn、esformatter等,webpack中有使用到acorn快速获取模块关系,babel中的parser也可以解析ast。往往项目中会babel和loader中babel解析多次语法树,而
webpack的loader通常会给留出链尾来接受一个ast字符的返回。不同不解析器ast格式上会有一些不同,但逻辑是一样的。
整个解析过程
- 分词:将代码分割为最小语法单元组
- 语法解析: 在分词(字符)基础上分析语法单元间的关系
js中的语法单元
- 关键字: var、let、const
- 标识符:if、else || 内置(自定义)的常量
- 运算符:+、-、*、/、%.....
- 数字/字符串
- 注释
- 空格:连续空格、suo
- 其它:括号、分号、冒号.....
var a = 123;
在通过acorn解析得到, AST在线查看
image.png
语法解析过程
根据源码语法递归确定语法单元组之间的关系。
function fn(){
var a = 1;
return a + 2;
}
fn();
在通过acorn解析得到JSON
{
"type": "Program",
"start": 0,
"end": 51,
"body": [
{
"type": "FunctionDeclaration", // 函数声明
"start": 0,
"end": 45,
"id": {
"type": "Identifier", // 标识符号类型
"start": 9,
"end": 11,
"name": "fn" // 函数 标识符号fn
},
"expression": false,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement", // 节点块
"start": 13,
"end": 45,
"body": [
{
"type": "VariableDeclaration", // 变量声明
"start": 16,
"end": 26,
"declarations": [
{
"type": "VariableDeclarator", // 变量声明
"start": 20,
"end": 25,
"id": {
"type": "Identifier", // 变量标识符a
"start": 20,
"end": 21,
"name": "a"
},
"init": { // 该标识符初始化的值
"type": "Literal",
"start": 24,
"end": 25,
"value": 1,
"raw": "1"
}
}
],
"kind": "var" // 类型标识符号
},
{
"type": "ReturnStatement", // 函数返回
"start": 30,
"end": 43,
"argument": {
"type": "BinaryExpression", // 二元表达式
"start": 37,
"end": 42,
"left": {
"type": "Identifier",
"start": 37,
"end": 38,
"name": "a"
},
"operator": "+",
"right": {
"type": "Literal",
"start": 41,
"end": 42,
"value": 2,
"raw": "2"
}
}
}
]
}
},
{
"type": "ExpressionStatement",
"start": 46,
"end": 51,
"expression": {
"type": "CallExpression",
"start": 46,
"end": 50,
"callee": {
"type": "Identifier",
"start": 46,
"end": 48,
"name": "fn"
},
"arguments": []
}
}
],
"sourceType": "module"
}
应用AST定制console
babelon7的解析下做加工
import { generator, parser, traverse, type as t } from "babel";
function compile(code) {
1. 解析拿到树
const ast = parser.parse(code);
2. 遍历树节点,过滤相应节点修改
const visitor = {
CallExpression(path) {
const { callee } = path.code;
if(t.isMemberExpression(callee) &&
callee.name === "console" && callee.property === "log") {
cons functionPath = path.findParent(p => p.isFunctionDeclaration())
if(functionPath && functionPath.node.id.name){
path.node.arguments.unShift(t.stringLiteral(`[${functionPath.node.id.name}]`))
}
}
}
}
traverse.default(ast, visitor)
3. 将语法树的json对象转化为字符串返回
return gernerator.default(ast, {}, code);
}
const code = `
function fn () { console.log("code log") }
`
compile(code); // fucntion fn() { console.log("[fn]", "code log") }