AST(抽象语法树,AST)抽象语法树,可以把代码转译成语法树的表现形式
例如下面的代码:
var a = 3;a + 5AST抽象出来的树结构:
代表的是根例程
(二项式)
可以到查看AST的解析结果
编译器过程
大多数编译器的工作过程可以分为三部分:
安装来理解编译的过程:
npm install esprima estraverse escodegenconst esprima = require('esprima')const estraverse = require('estraverse')const escodegen = require('escodegen')let code = `var a = 3`// Parse(解析)let ast = esprima.parseScript(code);//Transform(转换)estraverse.traverse(ast, {enter(node) {console.log("enter",node.type);},leave(node) {console.log("leave",node.type);}});// Generate(代码生成)const result = escodegen.generate(ast);Babel对于AST的遍历是深度优先遍历,对于AST上的每一个分支Babel都会先向下遍历走到尽头,然后再向上遍历退出刚遍历过的例程,然后寻找下一个分支。
AST对语法树的遍历是深度优先遍历,所以会先向下遍历走到尽头,然后再向上遍历退出刚遍历过的队列,寻找下一个分支ast树ast树,所以遍历的过程中控制台会打印下面的信息:
enter Programenter VariableDeclarationenter VariableDeclaratorenter Identifierleave Identifierenter Literalleave Literalleave VariableDeclaratorleave VariableDeclarationleave Program通过type的判断我们可以修改变量的值:
estraverse.traverse(ast, {enter(node) {if(node.type === "Literal"){node.value = "change";}}});// var a = "change";babel插件
来看下babel是如何工作的,首先通过npm安装@babel/core和babel-types:
npm install @babel/core我们知道babel能编译es6代码,例如最基础的const和箭头函数:
// es2015 的 const 和 arrow functionconst add = (a, b) => a + b;// Babel 转译后var add = function add(a, b) {return a + b;};我们可以到查看生成的语法树:
{"type": "Program","body": [{"type": "VariableDeclaration", // 变量声明"declarations": [ // 具体声明{"type": "VariableDeclarator", // 变量声明"id": {"type": "Identifier", // 标识符(最基础的)"name": "add" // 函数名},"init": {"type": "ArrowFunctionExpression", // 箭头函数"id": null,"expression": true,"generator": false,"params": [ // 参数{"type": "Identifier","name": "a"},{"type": "Identifier","name": "b"}],"body": { // 函数体"type": "BinaryExpression", // 二项式"left": { // 二项式左边"type": "Identifier","name": "a"},"operator": "+", // 二项式运算符"right": { // 二项式右边"type": "Identifier","name": "b"}}}}],"kind": "const"}],"sourceType": "module"}通过代码模拟一下:
const babel = require('babel-core');const t = require('babel-types');let code = `let add = (a, b)=>{return a+b}`;let ArrowPlugins = {visitor: {ArrowFunctionExpression(path) {let { node } = path;let body = node.body;let params = node.params;let r = t.functionExpression(null, params, body, false, false);path.replaceWith(r);}}}let result = babel.transform(code, {plugins: [ArrowPlugins]})console.log(result.code);我们可以在访问者中捕获到匹配的type,在某些函数里面替换箭头函数。
类转换
const babel = require("@babel/core");const typs = require("@babel/types");const code = `class Animal {constructor(name){this.name = name}getName(){return this.name}}`const classPlugins = {visitor:{ClassDeclaration(path){let node = path.node;let body = node.body.body;let id = node.id;let params = node.params;let methods = body.map(method=>{if(method.kind === "constructor"){return typs.functionDeclaration(id, method.params, method.body)}else{// Animal.prototypelet left = typs.memberExpression(id,typs.identifier("prototype"));// Animal.prototype.getNameleft = typs.memberExpression(left,method.key);let right = typs.functionExpression(null,method.params,method.body);return typs.assignmentExpression("=",left,right);}})path.replaceWithMultiple(methods);}}}const result = babel.transform(code, {plugins: [classPlugins]})console.log(result.code)导入转换
const babel = require('@babel/core');const types = require('@babel/types');const code = `import antd,{Button} from "antd"`;const importPlugin = {visitor: {ImportDeclaration(path) {let node = path.nodelet specifiers = node.specifiersif (!(specifiers.length == 1 &&types.isImportDefaultSpecifier(specifiers[0]))) {specifiers = specifiers.map((specifier) => {let local = types.importDefaultSpecifier(specifier.local);if (types.isImportDefaultSpecifier(specifier)) {return types.importDeclaration([local],types.stringLiteral(node.source.value))} else {return types.importDeclaration([local],types.stringLiteral(node.source.value+"/lib/"+specifier.local.name))}});path.replaceWithMultiple(specifiers)}},},}const result = babel.transform(code, {plugins: [importPlugin],});console.log(result.code)参考链接
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。