Reference
AST 与前端工程化实战
AST抽象语法树——最基础的javascript重点知识,99%的人根本不了解
13 个示例快速入门 JS 抽象语法树
AST Explorer
https://astexplorer.net/
概念
JavaScript解析:
- 词法分析(Lexical Analysis) :把JavaScript代码(字符串)的字符流(char stream)按照ECMAScript标准转换为记号流(token stream)。
- 语法分析(Syntactic Analysis):将词法单元流转换成一个由元素逐级嵌套组成的语法结构树,AST
Demo
代码:
var AST = "is Tree";
词法分析 => 记号流
Keyword: varIdentifier: AST
Punctuator: =
String: "is Tree"
Punctuator: ;
语法分析 => AST
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
}
});
应用场景
- 解释器和编译器
- 静态代码分析(抽离重复代码,判断代码相似性)
- 代码转换
- 代码格式化