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 }});
应用场景
- 解释器和编译器
- 静态代码分析(抽离重复代码,判断代码相似性)
- 代码转换
- 代码格式化