Babel(抽象语法树,又称AST)
文章:https://juejin.im/post/5a9315e46fb9a0633a711f25
https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md
- 你了解过Babel吗?
了解过抽象语法树,又称AST,有学习过,也写过一个基于AST的乞丐版模板引擎,先是词法解析token,然后生产抽象语法树,然后更改抽象语法树,当然这是插件做的事情,最后根据新的AST生成代码。
- 写过Babel插件吗
没有,只是看过相关文档
- 如果让你写一个插件,你能写的出来吗?
应该可以吧...
遂卒....
开玩笑的,既然提到了,又没回答上来什么,哎哟我这暴脾气,一想到今晚就睡不着,连夜把它撸了。
那么我们来从零写个插件吧。
写一个预计算简单表达式的插件
预览
Before:
const result = 1 + 2 + 3 + 4 + 5;
After:
const result = 15;
以上的例子可能大家不会经常遇到,因为傻x才会这么写,但是有可能你会这么写
setTimeout(function(){
// do something
}, 1000 * 2) // 插件要做的事,就是把 1000 * 2 替换成 2000
前提条件
开工
再写代码之前,你需要明白Babel它的原理,简单点说: Babel解析成AST,然后插件更改AST,最后由Babel输出代码
那么Babel的插件模块需要你暴露一个function,function内返回visitor
module.export = function(babel){
return {
visitor:{
}
}
}
visitor是对各类型的AST节点做处理的地方,那么我们怎么知道Babel生成了的AST有哪些节点呢?
很简单,你可以把Babel转换的结果打印出来,或者这里有传送门: AST explorer
这里我们看到 const result = 1 + 2中的1 + 1是一个BinaryExpression节点,那么在visitor中,我们就处理这个节点
var babel = require('babel-core');
var t = require('babel-types');
const visitor = {
BinaryExpression(path) {
const node = path.node;
let result;
// 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {
// 根据不同的操作符作运算
switch (node.operator) {
case "+":
result = node.left.value + node.right.value;
break
case "-":
result = node.left.value - node.right.value;
break;
case "*":
result = node.left.value * node.right.value;
break;
case "/":
result = node.left.value / node.right.value;
break;
case "**":
let i = right;
while (--i) {
result = result || node.left.value;
result = result - node.left.value;
}
break;
default:
}
}
// 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number字面量
path.replaceWith(t.numericLiteral(result));
}
}
};
module.exports = function (babel) {
return {
visitor
};
}
插件写好了,我们运行下插件试试
const babel = require("babel-core");
const result = babel.transform("const result = 1 + 2;",{
plugins:[
require("./index")
]
});
console.log(result.code); // const result = 3;
与预期一致,那么转换 const result = 1 + 2 + 3 + 4 + 5;呢?
结果是: const result = 3 + 3 + 4 + 5;
这就奇怪了,为什么只计算了1 + 2之后,就没有继续往下运算了吗?
我们看一下这个表达式的AST树
你会发现Babel解析成表达式里面再嵌套表达式。
表达式( 表达式( 表达式( 表达式(1 + 2) + 3) + 4) + 5)
而我们的判断条件并不符合所有的,只符合1 + 2
// 判断表达式两边,是否都是数字
if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {}
那么我们得改一改
第一次计算1 + 2之后,我们会得到这样的表达式
表达式( 表达式( 表达式(3+ 3) + 4) + 5)
其中 3 + 3又符合了我们的条件, 我们通过向上递归的方式遍历父级节点
// 如果上面的运算有结果的话
if (result !== undefined) {
// 把表达式节点替换成number字面量
path.replaceWith(t.numericLiteral(result));
let parentPath = path.parentPath;
// 向上遍历父级节点
parentPath && visitor.BinaryExpression.call(this, parentPath);
}
到这里,我们就得出了结果 const result = 15;
那么其他运算呢:
const result = 100 + 10 - 50 >>> const result = 60;
const result = (100 / 2) + 50 >>> const result = 100;
const result = (((100 / 2) + 50 * 2) / 50) ** 2 >>> const result = 9;
完结
到这里,已经向你大概的讲解了,如果编写一个Babel插件,再也不怕面试官问我答不出什么了哈...
你以为这就完了吗?
并没有
如果转换这样呢: const result = 0.1 + 0.2;
预期肯定是0.3, 但是实际上,Javascript有浮点计算误差,得出的结果是0.30000000000000004
那是不是这个插件就没卵用?
这就需要你去矫正浮点运算误差了,可以使用Big.js;
比如: result = node.left.value + node.right.value; 改成 result = +new Big(node.left.value).plus(node.right.value);
你以为完了吗? 这个插件还可以做很多
比如: Math.PI * 2 >>> 6.283185307179586
比如: Math.pow(2,2) >>> 4
...
...
最后上项目地址: github.com/axetroy/bab…
作者:Axetroy
链接:https://juejin.im/post/5a9315e46fb9a0633a711f25
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Babel(抽象语法树,又称AST)的更多相关文章
- 五分钟了解抽象语法树(AST)babel是如何转换的?
抽象语法树 什么是抽象语法树? It is a hierarchical program representation that presents source code structure acco ...
- JavaScript的工作原理:解析、抽象语法树(AST)+ 提升编译速度5个技巧
这是专门探索 JavaScript 及其所构建的组件的系列文章的第 14 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...
- 新抽象语法树(AST)给 PHP7 带来的变化
本文大部分内容参照 AST 的 RFC 文档而成:https://wiki.php.net/rfc/abstract_syntax_tree,为了易于理解从源文档中节选部分进行介绍. 我的官方群点击此 ...
- 抽象语法树(AST)
AST描述 在计算机科学中,抽象语法树(AST)或语法树是用编程语言编写的源代码的抽象语法结构的树表示.树的每个节点表示在源代码中出现的构造.语法是“抽象的”,因为它不代表真实语法中出现的每个细节,而 ...
- 理解Babel是如何编译JS代码的及理解抽象语法树(AST)
Babel是如何编译JS代码的及理解抽象语法树(AST) 1. Babel的作用是? 很多浏览器目前还不支持ES6的代码,但是我们可以通过Babel将ES6的代码转译成ES5代码,让所有的浏览器都 ...
- AST抽象语法树
抽象语法树简介 (一)简介 抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并 ...
- javascript编写一个简单的编译器(理解抽象语法树AST)
javascript编写一个简单的编译器(理解抽象语法树AST) 编译器 是一种接收一段代码,然后把它转成一些其他一种机制.我们现在来做一个在一张纸上画出一条线,那么我们画出一条线需要定义的条件如下: ...
- 从零写一个编译器(九):语义分析之构造抽象语法树(AST)
项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...
- AST抽象语法树 Javascript版
在javascript世界中,你可以认为抽象语法树(AST)是最底层. 再往下,就是关于转换和编译的"黑魔法"领域了. 现在,我们拆解一个简单的add函数 function add ...
随机推荐
- Yii框架的原代码
http://www.digpage.com/app_struct.html#index-php
- join()方法跟踪
#join方法跟踪java.lang.Thread.join() 进入线程的join方法,实际上线程thread是实现的 runnable接口 class Thread implements Runn ...
- Java之Set的使用场景
2.Set使用场景 API介绍: java.util.Set接口和java.util.List接口一样,同样继承自Collection接口, 它与Collection接口中的方法基本一致,并没有对Co ...
- 题解 P1130 【红牌】
逆推dp经典题目:数字三角形的折叠版 为什么这么说? 因为我们会发现:除了每一次都特判一下是否转换行号以外,剩下的思想没什么不同. 没看题目的看这里 先定义: n,m是步骤数目,小组数目 work[i ...
- 学习Spring Boot:(十四)spring-shiro的密码加密
前言 前面配置了怎么使用 shiro ,这次研究下怎么使用spring shiro的密码加密,并且需要在新增.更新用户的时候,实现生成盐,加密后的密码进行入库操作. 正文 配置凭证匹配器 @Bean ...
- 【BZOJ1078】[SCOI2008]斜堆(性质题)
[BZOJ1078][SCOI2008]斜堆(性质题) 题面 BZOJ 洛谷 题解 考虑一下这道题目的性质吧.思考一下最后插入进来的数是什么样子的.首先因为它是最后插入进来的,所以一定是比某个数小,然 ...
- 解题:HNOI 2014 世界树
题面 首先建虚树 DFS求虚树上每个点所属的点和到它所属点的距离,然后在=考虑虚树所有的边(对应原树一条链).如果两个端点所属节点不同就倍增出分界点统计答案,否则不用管(之后会统计到的):注意根节点特 ...
- D. Recovering BST Codeforces Round #505 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final)
http://codeforces.com/contest/1025/problem/D 树 dp 优化 f[x][y][0]=f[x][z][1] & f[z+1][y][0] ( gcd( ...
- jsoncpp的安装与使用示例
安装: 生成静态库 生成静态库: 第一步:生成目标文件: g++ -g -Wall -c json_reader.cpp json_value.cpp json_writer.cpp -I. -I.. ...
- bzoj千题计划246:bzoj2242: [SDOI2011]计算器
http://www.lydsy.com/JudgeOnline/problem.php?id=2242 #include<map> #include<cmath> #incl ...