一个简单的例子看懂抽象语法树的魔力
栏目: JavaScript · 发布时间: 5年前
内容简介:在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。上面这是百度百科的定义,一如既往的让人摸不着头脑。我们总结一下百度百科的定义:
在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
上面这是百度百科的定义,一如既往的让人摸不着头脑。
我们总结一下百度百科的定义:
- 抽象语法树是一颗树
- 树的每个节点都表示了源代码的一种 语法结构
上面这段总结有几个关键词:抽象语法树、树、节点、源代码、语法结构,其中语法结构是比较难理解的,那么什么是语法结构呢?
举几个简单的例子:
//变量声明 var a = 1; 复制代码
//循环 while(true){ console.log(1); } 复制代码
//判断 if(true){ console.log(1); } 复制代码
//函数声明 function a(){ console.log(1); } 复制代码
以上这些例子是js的语法声明(statement),这种声明就可以看成js的语法结构! 也就是说抽象语法树的每个节点都在描述这种结构。
比如说一个节点是变量声明,那么这个节点的子节点都会去描述变量声明的具体内容:变量名是什么,变量是什么类型,变量的初始值是什么等等。
就是这样一个一个的声明,构成了抽象语法树。
代码执行的三个步骤
从js程序到机器可执行的机器码需要经历两个阶段:
- 语法检查
- 编译运行
语法检查又分为语法分析和词法分析,所以分成三个步骤就是:
- 词法分析
- 语法分析
- 编译运行
这里先简单介绍下每个阶段都干了什么活:
第一步:词法分析,也叫做扫描scanner。它读取我们的代码,然后把它们按照预定的规则合并成一个个的标识Tokens(type 和 value )。这个阶段,它会移除空白符,注释等。最后,整个代码将被分割进一个Tokens列表(一个一维数组)。
第二步:语法分析,也叫做解析器。它会将词法分析出来的Token数组转化成树形的表达形式。同时,验证语法,语法如果有错的话,抛出语法错误。
第三步:编译阶段,也叫编译器。这个阶段会处理AST,生成机器可执行的机械码。
词法分析
先以一个简单的例子看下token序列长什么样
var a = 1; 复制代码
他的token长这样
其实就是一个一维数组,里面有一些对象用于描述每个单词。
我整理了下常见的type:
- Keyword (关键词)
- Identifier (标识符)
- Punctuator (标点符号)
- Numberic(数字)
- String (字符串)
- Boolean(布尔)
- Null(空值)
语法分析
词法分析由源代码生成了Token序列,语法分析则是由Token序列生成了抽象语法树。
还是看上一个例子:
var a = 1; 复制代码
他的抽象语法树长这样:
1、先看最外层的三个属性
- type(表示是一段程序代码)
- body(代码的具体内容)
- sourceType(表示语言的种类)
2、再看body里面的具体内容,body是一个数组,这是因为程序可能有多个内容块(statement),每个内容块用一个对象表示。
3、再看每个内容块的内容
- type(表示这个内容块是干什么的)
- declarations(乘装变量内容的块,可以看到这个块也是一个数组,因为变量声明可能生命多个,所以一个生命对应一个对象 例如 var a=1,b=2;) kind(关键字)
4、再看declarations里面对象里面的内容
- type (声明的类型是个变量)
- id(表示变量名)
- init(表示为这个变量设置的初值)
上面提到statement,statement有很多类型,比如说变量声明,函数定义,if语句,while循环,等都是一个statement,大家如果想看更多的类型,点击这里。
一个超级简单的例子
好了,说了这么多,终于要写代码了。
这个例子实现的功能:
- 将源代码中的 == 变成 ===
- 将源代码中的 var 变成 let
- 将源代码中的 console注释掉
这个例子用到的工具:
- Esprima (将源代码转化为ast)
- Estraverse(遍历语法树)
- Escodegen(讲语法书反编译为js代码)
初始化一个项目
npm init 复制代码
安装用到的依赖包
npm install esprima estraverse escodegen --save 复制代码
新建index.js入口文件 和 originCode.js 源代码文件
在 originCode.js 中输入要转换的源代码
function fun() { var opt = 1; console.log(1); if (opt == 1) { console.log(2); } } 复制代码
在 index.js 中实现我们的功能
let fs = require('fs'); const esprima = require('esprima');//将JS代码转化为语法树模块 const estraverse = require('estraverse');//JS语法树遍历各节点 const escodegen = require('escodegen');//将JS语法树反编译成js代码模块 /** * 由源代码得到抽象语法树 */ function getAst(jsFile) { let jsCode; return new Promise((resolve)=>{ fs.readFile(jsFile, (error, data) => { jsCode = data.toString(); resolve(esprima.parseScript(jsCode)); }); }); } /** * 设置全等 */ function setEqual(node) { if (node.operator === '==') { node.operator = '==='; } } /** * 删除console */ function delConsole(node) { if (node.type === 'ExpressionStatement' && node.expression.type === 'CallExpression' && node.expression.callee.object.name==='console') { node.expression.callee.object.name = '//console'; } } /** * 把var变成let */ function setLet(node){ if(node.kind === 'var'){ node.kind = 'let'; } } /** * 遍历语法树 */ function travel(ast){ estraverse.traverse(ast, { enter: (node) => { setEqual(node); setLet(node); delConsole(node); } }); } /** * 生成文件 */ function writeCode(file,data) { fs.writeFile(file,data,(error)=>{ console.log(error); }); } /** * 入口函数 */ function main(){ let file = './originCode.js'; let distFile = './distCode.js'; getAst(file).then(function(jsCode) { travel(jsCode); // 删掉 console , 通过parseScript在生成一变ast去掉注释的内容 // let distCode = escodegen.generate( esprima.parseScript( escodegen.generate(jsCode))); // 注释 console let distCode = escodegen.generate(jsCode); console.log('distcode',distCode); writeCode(distFile,distCode); }); } main(); 复制代码
然后运行我们的项目
node index.js 复制代码
distCode.js的内容已经变成我们想要的了
function fun() { let opt = 1; //console.log(1); if (opt === 1) { //console.log(2); } } 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring Data的魔力
- 2018 Gartner魔力象限“大裁员”
- 预测《权游》角色生死,AI算法魔力何在?
- Gartner云端盘点,浅谈2017IaaS魔力象限
- 阿里、百度、腾讯都选择 Flink,它到底有什么魔力?
- AMAZ LINUX 2 + QB + GD 挂 PT 魔力
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。