什么是抽象语法树?

在计算机科学中,抽象语法和抽象语法树其实是源代码的抽象语法结构的树状表现形式 在线编辑器

我们常用的浏览器就是通过将js代码转化为抽象语法树来进行下一步的分析等其他操作。所以将js转化为抽象语法树更利于程序的分析。

如图:

如上图中的变量声明语句,转化为AST之后就是右图的样子。

先来分析一下左图:

var 是一个关键字

AST是一个定义者

= 是Equal 等号的叫法有很多形式,在后面我们还会看到

“is tree” 是一个字符串

;就是 Semicoion

再来对应一下右图:

首先一段代码转化成的抽象语法树是一个对象,该对象会有一个顶级的type属性'Program',第二个属性是body是一个数组。

body数组中存放的每一项都是一个对象,里面包含了所有的对于该语句的描述信息

type:描述该语句的类型 --变量声明语句
kind:变量声明的关键字 -- var
declaration: 声明的内容数组,里面的每一项也是一个对象type: 描述该语句的类型 id: 描述变量名称的对象type:定义name: 是变量的名字init: 初始化变量值得对象type: 类型value: 值 "is tree" 不带引号row: "\"is tree"\" 带引号
复制代码

抽象语法树有哪些用途?

代码语法的检查,代码风格的检查,代码的格式化,代码的高亮,代码错误提示,代码自动补全等等

如:JSLint、JSHint 对代码错误或风格的检查,发现一些潜在的错误 IDE的错误提示,格式化,高亮,自动补全等等 代码的混淆压缩 如:UglifyJS2等

优化变更代码,改变代码结构达到想要的结构

代码打包工具webpack,rollup等等 CommonJS、AMD、CMD、UMD等代码规范之间的转化 CoffeeScript、TypeScript、JSX等转化为原生Javascript

通过什么工具或库来实现源码转化为抽象语法树?

那就是javascript Parser 解析器,他会把js源码转化为抽象的语法树。

浏览器会把js源码通过解析器转化为抽象语法树,再进一步转化为字节码或直接生成机器码

一般来说每一个js引擎都会有自己的抽象语法树格式,chrome的v8引擎,firefox的SpiderMonkey 引擎等等,MDN提供了详细SpiderMonkey AST format的详细说明,算是业界的标准。(SpiderMonkey是Mozilla项目的一部分,是一个用C语言实现的JavaScript脚本引擎,为了在SpiderMonkey中运行JavaScript代码,应用程序必须有三个要素:JSRuntime,JSContext和全局对象。)

常用的javascript Parser

esprima

traceur

acorn

shift

我们主要拿esprima来举一个例子

安装

 npm install esprima estraverse escodegen -S
复制代码

esprima 涉及三个库名称和功能如下:

esprima 把源码转化为抽象语法树

let esprima = require('esprima'); // 引入esprimalet jsOrigin = 'function eat(){};'; // 定义一个js源码let AST = esprima.parse(jsOrigin); // 通过esprima.parse将js源码转化为一个抽象语法树console.log(AST); // 打印生成的抽象语法树/*Script {type: 'Program',// 顶级的type属性body: [ FunctionDeclaration {type: 'FunctionDeclaration', // js源码的类型--是一个函数声明id: [Identifier],params: [],body: [BlockStatement],generator: false, // 是不是generator函数expression: false, // 是不是一个表达式async: false // 是不是一个异步函数},EmptyStatement { type: 'EmptyStatement' } ],sourceType: 'script' }*/复制代码

estraverse 遍历并更新抽象语法树

在介绍用法之前我们先来npm上看一下这个库,这个库的下载量居然500多万,而且没有README说明文档,是不是很牛掰!

在举例子之前我们要遍历抽象语法树,首先我们要先了解一下他的遍历顺利

 let estraverse = require('estraverse');estraverse.traverse(AST, {enter(node){console.log('enter', node.type)if(node.type === 'Identifier') {node.name += '_enter'}},leave(node){console.log('leave', node.type)if(node.type === 'Identifier') {node.name += '_leave'}}})// enter Program// enter FunctionDeclaration// enter Identifier// leave Identifier// enter BlockStatement// leave BlockStatement// leave FunctionDeclaration// enter EmptyStatement// leave EmptyStatement// leave Program复制代码

通过上面节点类型的打印结果我们不难看出,我们的抽象语法树的每个节点被访问了2次,一次是进入的时候,一次是离开的时候,我们可以通过下面的图来更加清楚的理解抽象语法树的遍历顺序

看完遍历顺序之后,我们看到代码中的判断条件 如果是变量名的话,第一次进入访问时对这个变量的名称做了一次修改,当离开的时候也做了一次修改。那接下来我们要验证 抽象语法树种的这个节点的变量名称 是否修改成功了呢?我们有两种方案,方案一:直接打印抽象语法树,这个非常简单再这里就你介绍了。方案二: 我们将现有的抽象语法树转化成源码看一下变量名是否变成功 这样就一目了然了。那怎么将我们的抽象语法树还原成源码呢?这就要引入我们的第三个库了 escodegen

escodegen 将抽象语法树还原成js源码

let escodegen = require('escodegen');let originReback = escodegen.generate(AST);console.log(originReback);// function eat_enter_leave() {};复制代码

通过上面还原回来的源码我们看到变量名称确实被更改了。

接下来我们来探索一下如何用抽象语法树来将箭头函数转化为普通的函数

我们都知道es6语法转es5的语法我们用的是babel,让我们接下来就看一下 babel是如何将箭头函数转化为普通函数的。

第一步需要使用babel的两个插件,babel-core 核心模块 babel-types 类型模块
npm i babel-core babel-types -S复制代码

第一步:我们先来对比普通函数和箭头函数的抽象语法树,通过对比找出其中的不同之处,然后在节点可以复用的前提下,尽可能少的改变一下不同的地方,从而成功的将箭头函数转化为普通函数。

我们以这个箭头函数为例:

let sum = (a,b) => a+b; ------>var sum = function sum(a, b) {return a + b;};
复制代码

如上图所示,普通函数和箭头函数的AST的不同在于init,所以我们现在要做的是将箭头函数的arrowFunctionExpression 转换为FunctionExpression

利用babel-types生成新的部分的AST语法树,替换原有的。如果创建某个节点的语法树,那就在下面的网址上,需要哪个节点就搜哪个节点 babel-types

  // babel 核心库,用来实现核心的转换引擎const babel = require('babel-core');// 实现类型转化 生成AST节点const types = require('babel-types');let code = 'let sum = (a,b) => a+b;';let es5Code = function (a,b) {return a+b;};// babel 转化采用的是访问者模式Visitor 对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同// 这个访问者可以对特定的类型的节点进行处理let visitor = {ArrowFunctionExpression(path) {// 如果这个节点是箭头函数的节点的话,我们在这里进行处理替换工作// 1.复用params参数let params = path.node.params;let blockStatement = types.blockStatement([types.returnStatement(path.node.body)])let func = types.functionExpression(null, params, blockStatement, false,false);path.replaceWith(func)}};let arrayPlugin = {visitor};// babel内部先把代码转化成AST,然后进行遍历let result = babel.transform(code, {plugins: [arrayPlugin]});console.log(result.code);// let sum = function (a, b) {//     return a + b;// };复制代码

我们写一个babel的预计算插件

let code = `const result = 1000 * 60 * 60 * 24`;//let code = `const result = 1000 * 60`;let babel = require('babel-core');let types = require('babel-types');//预计算let visitor = {BinaryExpression(path){let node = path.node;if(!isNaN(node.left.value)&&!isNaN(node.right.value)){let result = eval(node.left.value+node.operator+node.right.value);result =  types.numericLiteral(result);path.replaceWith(result);//如果此表达式的父亲也是一个表达式的话,需要递归计算if(path.parentPath.node.type == 'BinaryExpression'){visitor.BinaryExpression.call(null,path.parentPath);}}}}let r = babel.transform(code,{plugins:[{visitor}]});console.log(r.code);复制代码

以上就是我对抽象语法树的理解,有什么不正确的地方,恳求斧正。

不要厌烦熟悉的事物,每天都进步一点;不要畏惧陌生的事物,每天都学习一点;

抽象语法树 Abstract syntax tree相关推荐

  1. python 抽象语法树_抽象语法树(Abstract Syntax Tree)

    一般来说,程序中的一段源代码在执行之前会经历下面三个步骤 1 分词/词法分析 这个过程会将由字符组成的字符串分解成有意义的代码快,这些代码块被称为词法单元.例如 var a = 4:会被分解成 var ...

  2. 语法树与抽象语法树(parse tree abstract syntax tree)

    1 语法树(parse tree): 是在parsing阶段,derivation的图像化表示,parser tree focus on grammar的actual implemment,包括像wh ...

  3. 抽象语法树 -Abstract Syntax Tree

    什么是抽象语法树? 是源代码结构的一种抽象表示,以树状的形式表现编程语言的语法结构.树上的每个节点都表示源代码中的一种结构. 拆分成语法树 拆解一个简单的add函数 function add(a, b ...

  4. js 数组 实现 完全树_JavaScript的工作原理:解析、抽象语法树(AST)+ 提升编译速度5个技巧

    摘要: JS的"编译原理". 原文:JavaScript的工作原理:解析.抽象语法树(AST)+ 提升编译速度5个技巧 作者:前端小智 Fundebug经授权转载,版权归原作者所有 ...

  5. JavaScript 是如何工作的:解析、抽象语法树(AST)+ 提升编译速度5个技巧

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 14 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  6. 从零写一个编译器(九):语义分析之构造抽象语法树(AST)

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...

  7. 详解AST抽象语法树

    浅谈 AST 先来看一下把一个简单的函数转换成AST之后的样子. // 简单函数 function square(n) {return n * n; }// 转换后的AST {type: " ...

  8. php7 ast,PHP7 的抽象语法树(AST)带来的变化

    什么是抽象语法树? 抽象语法树(abstract syntax tree,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并不会表示 ...

  9. python树代码_浅析AST抽象语法树及Python代码实现

    在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码.树上的每个 ...

最新文章

  1. HDU.4903.The only survival(组合 计数)
  2. 灰帽黑客:正义黑客的道德规范、渗透测试、攻击方法和漏洞分析技术(第3版)
  3. BZOJ-3505-数三角形-CQOI2014
  4. hadoop 源码编译---Win7(64位)
  5. java nio epoll_Java NIO 选择器(Selector)的内部实现(poll epoll)
  6. mysql如何让表建立连接吗_MySQL 表与表之间建立关系
  7. xshell 6 连接debian系统拒绝了密码_Xshell中操作Linux的常用命令,你知道几个?
  8. android判断主线程_android中从子线程切换到主线程,但是显得代码很臃肿,请教大牛是怎么自定义的?...
  9. python只读模式可以打印嘛_只读python属性?无法打印对象
  10. python剪刀石头布_如何用python写剪刀石头布
  11. ESXi日志分析网站第五版v5.4
  12. ASP.NET Core 2.1 : 十.升级现有Core2.0 项目到2.1
  13. 注册flash.ocx inno setup (转)
  14. 算法与数据结构全阶班-左程云版系列目录
  15. rtl8201以太网卡调试
  16. 如何在数据库mysql中储存图片
  17. 大内存PDF文件转Word免费之道
  18. 安卓CTS官方文档之兼容性测试套件简介
  19. 非线性规划 - 数学建模
  20. Java 使用XmlUtil解析Xml

热门文章

  1. Activity的管理类
  2. sea.js学习网址和书籍
  3. Oculus和虚拟现实的无限可能
  4. Javascript学习------内部对象
  5. Jackson 框架,轻易转换JSON
  6. AppUpdater 的一个问题
  7. ViewPager一屏显示多个item,及边缘滑动事件优化
  8. .classpath文件
  9. List去重复——多个复杂字段判断去重
  10. 中国联通:联通集团正研究混改 具体实施方案在讨论中