共计 8694 个字符,预计需要花费 22 分钟才能阅读完成。
AST 形象语法树
why
支流我的项目插件的用处: javascript 转译、代码压缩、css 预处理、eslint、prettier 等都建设在 AST 的根底上。
what
according to the grammar of a programming language, each AST node corresponds to an item of a source code.(依据编程语言的语法,每个 AST 节点对应一个源代码项。)
demo
链接地址:astexplorer.net
AST 解析工具
js 语法
function square(n) {return n * n;}
ast 语法树
// Parser acorn-8.0.1 | |
{ | |
"type": "Program", | |
"start": 0, | |
"end": 38, | |
"body": [ | |
{ | |
"type": "FunctionDeclaration", | |
"start": 0, | |
"end": 38, | |
"id": { | |
"type": "Identifier", | |
"start": 9, | |
"end": 15, | |
"name": "square" | |
}, | |
"expression": false, | |
"generator": false, | |
"async": false, | |
"params": [ | |
{ | |
"type": "Identifier", | |
"start": 16, | |
"end": 17, | |
"name": "n" | |
} | |
], | |
"body": { | |
"type": "BlockStatement", | |
"start": 19, | |
"end": 38, | |
"body": [ | |
{ | |
"type": "ReturnStatement", | |
"start": 23, | |
"end": 36, | |
"argument": { | |
"type": "BinaryExpression", | |
"start": 30, | |
"end": 35, | |
"left": { | |
"type": "Identifier", | |
"start": 30, | |
"end": 31, | |
"name": "n" | |
}, | |
"operator": "*", | |
"right": { | |
"type": "Identifier", | |
"start": 34, | |
"end": 35, | |
"name": "n" | |
} | |
} | |
} | |
] | |
} | |
} | |
], | |
"sourceType": "module" | |
} |
从纯文本中失去 AST(通过编译器)
- 词法剖析
scanner。它读取咱们的代码,而后把他们依照预约的规定合并成一个个的标识(tokens). 同时,它会移除空白符,正文等。最初,整个代码将被宰割进一个 tokens 列表(或者说一维数组)。当词法剖析源代码的时候,它会一个一个字母的读取代码。当它遇到空格,操作符,或者特殊符号的时候,它会认为一个会话曾经实现了。
- 语法解析,也叫解析器
它将词法剖析进去的数组转化成树形的表达形式。同时验证语法,语法错误,抛出语法错误。
当生成树的时候,解析器会删除一些没必要的标识 tokens(比方不残缺的括号),因而 AST 不是 100% 与源码匹配,但咱们曾经可能晓得如何解决了。题外话,解析器 100% 笼罩所有代码构造生成树叫做 CST(具体语法树)
更多编译器常识
the-super-tiny-compiler- 仓库地址
将 Lisp 转化为 C 语言
LangSandbox- 仓库地址
发明本人的语言,并将它编译成 C 语言或者机器语言,最初运行它。
第三方库生成 AST
重点介绍 Babylon
Babylon
Babylon is a JavaScript parser used in Babel.Support for JSX, Flow, Typescript.
babel
babel 是一个 javascript 编译器。宏观来说,它分为 3 个阶段运行代码:解析(parsing),转译(transforming),生成(generation)。咱们能够给 babel 一些 javascript 代码,它批改代码而后生成新的代码返回。过程即创立 AST,遍历树,批改 tokens,最初从 AST 中生成最新的代码。
babel 解析生成
1、应用 babylon 解析代码生成语法树
import * as babylon from "babylon"; | |
const code = ` | |
const abc = 5; | |
`; | |
const ast = babylon.parse(code); |
生成树后果:
{ | |
"type": "File", | |
"start": 0, | |
"end": 18, | |
"loc": { | |
"start": { | |
"line": 1, | |
"column": 0 | |
}, | |
"end": { | |
"line": 3, | |
"column": 0 | |
} | |
}, | |
"program": { | |
"type": "Program", | |
"start": 0, | |
"end": 18, | |
"loc": { | |
"start": { | |
"line": 1, | |
"column": 0 | |
}, | |
"end": { | |
"line": 3, | |
"column": 0 | |
} | |
}, | |
"sourceType": "script", | |
"body": [ | |
{ | |
"type": "VariableDeclaration", | |
"start": 3, | |
"end": 17, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 2 | |
}, | |
"end": { | |
"line": 2, | |
"column": 16 | |
} | |
}, | |
"declarations": [ | |
{ | |
"type": "VariableDeclarator", | |
"start": 9, | |
"end": 16, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 8 | |
}, | |
"end": { | |
"line": 2, | |
"column": 15 | |
} | |
}, | |
"id": { | |
"type": "Identifier", | |
"start": 9, | |
"end": 12, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 8 | |
}, | |
"end": { | |
"line": 2, | |
"column": 11 | |
}, | |
"identifierName": "abc" | |
}, | |
"name": "abc" | |
}, | |
"init": { | |
"type": "NumericLiteral", | |
"start": 15, | |
"end": 16, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 14 | |
}, | |
"end": { | |
"line": 2, | |
"column": 15 | |
} | |
}, | |
"extra": { | |
"rawValue": 5, | |
"raw": "5" | |
}, | |
"value": 5 | |
} | |
} | |
], | |
"kind": "const" | |
} | |
], | |
"directives": []}, | |
"comments": [], | |
"tokens": [ | |
{ | |
"type": { | |
"label": "const", | |
"keyword": "const", | |
"beforeExpr": false, | |
"startsExpr": false, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"value": "const", | |
"start": 3, | |
"end": 8, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 2 | |
}, | |
"end": { | |
"line": 2, | |
"column": 7 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": "name", | |
"beforeExpr": false, | |
"startsExpr": true, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null | |
}, | |
"value": "abc", | |
"start": 9, | |
"end": 12, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 8 | |
}, | |
"end": { | |
"line": 2, | |
"column": 11 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": "=", | |
"beforeExpr": true, | |
"startsExpr": false, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": true, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"value": "=", | |
"start": 13, | |
"end": 14, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 12 | |
}, | |
"end": { | |
"line": 2, | |
"column": 13 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": "num", | |
"beforeExpr": false, | |
"startsExpr": true, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"value": 5, | |
"start": 15, | |
"end": 16, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 14 | |
}, | |
"end": { | |
"line": 2, | |
"column": 15 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": ";", | |
"beforeExpr": true, | |
"startsExpr": false, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"start": 16, | |
"end": 17, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 15 | |
}, | |
"end": { | |
"line": 2, | |
"column": 16 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": "eof", | |
"beforeExpr": false, | |
"startsExpr": false, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"start": 18, | |
"end": 18, | |
"loc": { | |
"start": { | |
"line": 3, | |
"column": 0 | |
}, | |
"end": { | |
"line": 3, | |
"column": 0 | |
} | |
} | |
} | |
] | |
} |
2、应用 babel 的转换器 transforming 语法树语法
import traverse from "babel-traverse"; | |
traverse(ast, {enter(path) {if (path.node.type === "Identifier") { | |
path.node.name = path.node.name | |
.split("") | |
.reverse() | |
.join(""); | |
} | |
} | |
}); |
{ | |
"type": "File", | |
"start": 0, | |
"end": 18, | |
"loc": { | |
"start": { | |
"line": 1, | |
"column": 0 | |
}, | |
"end": { | |
"line": 3, | |
"column": 0 | |
} | |
}, | |
"program": { | |
"type": "Program", | |
"start": 0, | |
"end": 18, | |
"loc": { | |
"start": { | |
"line": 1, | |
"column": 0 | |
}, | |
"end": { | |
"line": 3, | |
"column": 0 | |
} | |
}, | |
"sourceType": "script", | |
"body": [ | |
{ | |
"type": "VariableDeclaration", | |
"start": 3, | |
"end": 17, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 2 | |
}, | |
"end": { | |
"line": 2, | |
"column": 16 | |
} | |
}, | |
"declarations": [ | |
{ | |
"type": "VariableDeclarator", | |
"start": 9, | |
"end": 16, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 8 | |
}, | |
"end": { | |
"line": 2, | |
"column": 15 | |
} | |
}, | |
"id": { | |
"type": "Identifier", | |
"start": 9, | |
"end": 12, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 8 | |
}, | |
"end": { | |
"line": 2, | |
"column": 11 | |
}, | |
"identifierName": "abc" | |
}, | |
"name": "cba" | |
}, | |
"init": { | |
"type": "NumericLiteral", | |
"start": 15, | |
"end": 16, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 14 | |
}, | |
"end": { | |
"line": 2, | |
"column": 15 | |
} | |
}, | |
"extra": { | |
"rawValue": 5, | |
"raw": "5" | |
}, | |
"value": 5 | |
} | |
} | |
], | |
"kind": "const" | |
} | |
], | |
"directives": []}, | |
"comments": [], | |
"tokens": [ | |
{ | |
"type": { | |
"label": "const", | |
"keyword": "const", | |
"beforeExpr": false, | |
"startsExpr": false, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"value": "const", | |
"start": 3, | |
"end": 8, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 2 | |
}, | |
"end": { | |
"line": 2, | |
"column": 7 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": "name", | |
"beforeExpr": false, | |
"startsExpr": true, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null | |
}, | |
"value": "abc", | |
"start": 9, | |
"end": 12, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 8 | |
}, | |
"end": { | |
"line": 2, | |
"column": 11 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": "=", | |
"beforeExpr": true, | |
"startsExpr": false, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": true, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"value": "=", | |
"start": 13, | |
"end": 14, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 12 | |
}, | |
"end": { | |
"line": 2, | |
"column": 13 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": "num", | |
"beforeExpr": false, | |
"startsExpr": true, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"value": 5, | |
"start": 15, | |
"end": 16, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 14 | |
}, | |
"end": { | |
"line": 2, | |
"column": 15 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": ";", | |
"beforeExpr": true, | |
"startsExpr": false, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"start": 16, | |
"end": 17, | |
"loc": { | |
"start": { | |
"line": 2, | |
"column": 15 | |
}, | |
"end": { | |
"line": 2, | |
"column": 16 | |
} | |
} | |
}, | |
{ | |
"type": { | |
"label": "eof", | |
"beforeExpr": false, | |
"startsExpr": false, | |
"rightAssociative": false, | |
"isLoop": false, | |
"isAssign": false, | |
"prefix": false, | |
"postfix": false, | |
"binop": null, | |
"updateContext": null | |
}, | |
"start": 18, | |
"end": 18, | |
"loc": { | |
"start": { | |
"line": 3, | |
"column": 0 | |
}, | |
"end": { | |
"line": 3, | |
"column": 0 | |
} | |
} | |
} | |
] | |
} |
3、应用 babel 的生成器 generator 代码
import generate from "@babel/generator"; | |
const newCode = generate(ast).code; | |
// newCode => const cba = 5; |
babel 插件制作(babel-plugins)
在上述步骤中,第一步(解析)和第三步(生成)有 babel 解决。
当开发 babel-plugin 插件的时候,咱们只须要形容转化你的 AST 节点的 ”visitors” 就能够了。
// my-babel-plugin.js | |
module.exports = function() { | |
return { | |
visitor: {Identifier(path) { | |
const name = path.node.name; | |
console.log(name); | |
path.node.name = name | |
.split("") | |
.reverse() | |
.join(""); | |
} | |
} | |
}; | |
}; | |
// 在 babel.config.js 中注册插件,重启我的项目能力失效 | |
// plugins: ["./src/plugins/mybabelplugin.js"] |
学习 Babel 插件制作 -Babel-handbook
中文插件手册
主动代码重构工具,神器 JSCodeshift
例如说你想要替换掉所有的老掉牙的匿名函数, 把他们变成 Lambda 表达式(箭头函数)。
// transform | |
load().then(function(response)) {return response.data;} | |
// to | |
load().then(response => response.data) |
上述操作代码编辑器可能没方法这么做,因为这并不是简略的查找替换操作。这时候 jscodeshift 就能够应用了。
如果你想创立主动把你的代码从旧的框架迁徙到新的框架,这就是一种很 nice 的形式。
jscodeshift
jscodeshift 是一个工具包,用于在多个 JavaScript 或 TypeScript 文件上运行 codemods。
react-codemod
This repository contains a collection of codemod scripts for use with JSCodeshift that help update React APIs.
此存储库蕴含一组 codemod 脚本,用于 jscodeshift,用于更新 React api。
Prettier
// transform | |
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis()),isThereSeriouselyAnotherOne()); | |
// to | |
foo {reallyLongArg(), | |
omgSoManyParameters(), | |
IShouldRefactorThis(), | |
isThereSeriouselyAnotherOne()}; | |
// Prettier 格式化咱们的代码。它调整长句,整顿空格,括号等。 |
《A prettier printer》
Finally
js2flowchart 在线转化预览地址
js2flowchart 仓库地址
它将 js 代码转化生成 svg 流程图
这是一个很好的例子,因为它向你展示了你,当你领有 AST 时,能够做任何你想要做的事。把 AST 转回成字符串代码并不是必要的,你能够通过它画一个流程图,或者其它你想要的货色。
js2flowchart 应用场景是什么呢?通过流程图,你能够解释你的代码,或者给你代码写文档;通过可视化的解释学习其他人的代码;通过简略的 js 语法,为每个处理过程简略的形容创立流程图。
你也能够在代码中应用它,或者通过 CLI,你只须要指向你想生成 SVG 的文件就行。而且,还有 VS Code 插件(链接在我的项目 readme 中)
首先,解析代码成 AST,而后,咱们遍历 AST 并且生成另一颗树,我称之为工作流树。它删除很多不重要的额 tokens,然而将要害块放在一起,如函数、循环、条件等。再之后,咱们遍历工作流树并且创立形态树。每个形态树的节点蕴含可视化类型、地位、在树中的连贯等信息。最初一步,咱们遍历所有的形态,生成对应的 SVG,合并所有的 SVG 到一个文件中.
后续会继续更新,学习中。。。