主流项目插件的用途: javascript转译、代码压缩、css预处理、eslint、prettier等都创建在AST的基础上。
according to the grammar of a programming language, each AST node corresponds to an item of a source code.(根据编程语言的语法,每一个AST节点对应一个源代码项。)
连接地址:astexplorer.net
AST解析工具
js语法javascript
function square(n) { return n * n; }
ast语法树css
// 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" }
scanner。它读取咱们的代码,而后把他们按照预约的规则合并成一个个的标识(tokens).同时,它会移除空白符,注释等。最后,整个代码将被分割进一个tokens列表(或者说一维数组)。当词法分析源代码的时候,它会一个一个字母的读取代码。当它遇到空格,操做符,或者特殊符号的时候,它会认为一个会话已经完成了。html
它将词法分析出来的数组转化成树形的表达形式。同时验证语法,语法错误,抛出语法错误。
当生成树的时候,解析器会删除一些不必的标识tokens(好比不完整的括号),所以AST不是100%与源码匹配,但咱们已经可以知道如何处理了。题外话,解析器100%覆盖全部代码结构生成树叫作CST(具体语法树)java
the-super-tiny-compiler-仓库地址node
将Lisp转化为C语言
LangSandbox-仓库地址react
创造本身的语言,并将它编译成C语言或者机器语言,最后运行它。
第三方库生成ASTgit
Babylon
Babylon is a JavaScript parser used in Babel.Support for JSX, Flow, Typescript.github
babel是一个javascript编译器。宏观来讲,它分为3个阶段运行代码:解析(parsing),转译(transforming),生成(generation)。咱们能够给babel一些javascript代码,它修改代码而后生成新的代码返回。过程即建立AST,遍历树,修改tokens,最后从AST中生成最新的代码。express
一、使用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 } } } ] }
二、使用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 } } } ] }
三、使用babel的生成器generator代码
import generate from "@babel/generator"; const newCode = generate(ast).code; // newCode => const cba = 5;
在上述步骤中,第一步(解析)和第三步(生成)有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
中文插件手册
例如说你想要替换掉全部的老掉牙的匿名函数, 把他们变成Lambda表达式(箭头函数)。
// transform load().then(function(response)) { return response.data; } // to load().then(response => response.data)
上述操做代码编辑器可能没办法这么作,由于这并非简单的查找替换操做。这时候jscodeshift就可使用了。
若是你想建立自动把你的代码从旧的框架迁移到新的框架,这就是一种很nice的方式。
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。
// transform foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis()),isThereSeriouselyAnotherOne()); // to foo { reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouselyAnotherOne() }; // Prettier 格式化咱们的代码。它调整长句,整理空格,括号等。
js2flowchart在线转化预览地址
js2flowchart仓库地址
它将js代码转化生成svg流程图
这是一个很好的例子,由于它向你展示了你,当你拥有AST时,能够作任何你想要作的事。把AST转回成字符串代码并非必要的,你能够经过它画一个流程图,或者其它你想要的东西。
js2flowchart使用场景是什么呢?经过流程图,你能够解释你的代码,或者给你代码写文档;经过可视化的解释学习其余人的代码;经过简单的js语法,为每一个处理过程简单的描述建立流程图。
你也能够在代码中使用它,或者经过CLI,你只须要指向你想生成SVG的文件就行。并且,还有VS Code插件(连接在项目readme中)
首先,解析代码成AST,而后,咱们遍历AST而且生成另外一颗树,我称之为工做流树。它删除不少不重要的额tokens,可是将关键块放在一块儿,如函数、循环、条件等。再以后,咱们遍历工做流树而且建立形状树。每一个形状树的节点包含可视化类型、位置、在树中的链接等信息。最后一步,咱们遍历全部的形状,生成对应的SVG,合并全部的SVG到一个文件中.后续会持续更新,学习中。。。