抽象语法树入门到放弃?

抽象语法树(Abstract syntax tree AST)在计算机科学中,抽象语法和抽象语法树其实是源代码的抽象语法结构的树状表现形式

为什么是抽象的?

前端工程化,离不了 AST

虽然AST是编译原理中的基础内容,但在大前端时代,它有很广的应用。

比如我们的babel、eslint、代码格式化、代码自动补全、代码压缩、JSX 甚至 Typescript 都是在 AST 上进行操作的。

初识 AST

AST 其实就是一个树状结构,

这里给出一个更好理解 AST 的工具 click

var a = 'hello world'

以上代码转换后:

这个结构包含了很多属性。VariableDeclaration 变量声明

FunctionDeclaration 函数声明

Expression 表达式

ThisExpression this表达式

你可以在文档里找到大部分属性 文档

可以在ES标准里找到所有属性 文档

AST 应用

应用AST,首先得先把 JS 代码转成 AST,然后用 文档中的 API 去修改树,修改完成后,再解析树并输出 JS 代码。

以下以 recast 举例。

recast 是 Javascript 解析器,提供了AST接口。

安装npm install recast

新建 parse.js1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22const recast = require('recast');

const code =

`

function add(a, b) {return a + b}

// hello

`

const ast = recast.parse(code);

const add = ast.program.body[0];

const {variableDeclaration, variableDeclarator, functionExpression} = recast.types.builders;

ast.program.body[0] = variableDeclaration("var", [

variableDeclarator(add.id, functionExpression(

null,

add.params,

add.body

))

])

const output = recast.prettyPrint(ast, { tabWidth: 2 }).code

console.log(output)

以上代码将 code 中的函数定义改成函数表达式

运行node parse.js

命令行读取文件

recast 提供了 run 方法,来让我们在命令行里可以拿到js文件中的代码,像:

map.js1

2

3

4

5

6

7

8

9

10

11

12

13

14function (){

}

function b(){

}

a();

b();

let a = 2;

let b = 3;

let c = 4;

c = a + b;

b = c + a;

a = c + b;

parse.js1

2

3

4const recast = require("recast");

recast.run(function(ast, printSource){

console.log(recast.print(ast).code)

})

命令行输入node parse demo.js

这样,我们就可以在 parse.js 内部,操作 demo.js 的语法树

树的遍历

如果我们想对代码里的所有函数、表达式或者命名进行批量操作。 recast 提供了 recast.visit 方法进行节点树的遍历。

比如以下代码是遍历 ExpressionStatement,只要文档中能查到的,都可以遍历对应的格式。1

2

3

4

5

6

7

8

9

10

11// map.js

const recast = require('recast')

recast.run(function(ast, printSource){

recast.visit(ast, {

visitExpressionStatement: function({node}){

console.log(node)

return false

}

});

});

很久之前的一个需求

在之前TOH还负责丁当商城(单入口Vue项目)的时候,xusy 给了我这么一个需求。在以前因为某种原因,引入了 Jquery ,但只用了 $.extend 方法,现在需要去除Jquery,重写这个方法。举个简单的例子。

当我重写完相关函数后,代码大概是这个样子。

https://gitlab.dxy.net/f2e/dxy_mall/commit/0770c8f9e531fc58727e99dce650d0bd4c2568cb1

2

3

4

5

6

7

8

9

10

11

12

13import { extend } from 'jquery'

/*

改成

*/

import { extend } from 'utils/common'

import $ from 'jquery'

$.extend()

/*

改成

*/

import { extend } from 'jquery'

extend()

当时的做法是全局检索关键字 jquery,然后逐一去修改替换,如果需要修改的文件很多的话,一个美好的下午就没了。

当然,你可以直接检索 import { extend } from 'jquery' 然后一键替换,这样会有问题,比如有个人这样写代码import { extend} from 'jquery',那就匹配不到了……

还有的人这么写代码1

2

3import { extend}

from

'jquery'

用 AST 怎么做呢?1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49var fs = require('fs')

const recast = require("recast");

const { literal, importSpecifier, identifier, callExpression} = recast.types.builders;

fs.readdir('./codes', function(err, files){

files.map((fileName) => {

const fileUrl = `./codes/${fileName}`

console.log(fileUrl)

fs.readFile(fileUrl, function(err, data){

// 读取文件失败/错误

if (err) {

throw err;

}

const code = data.toString()

const ast = recast.parse(code);

recast.visit(ast, {

visitImportDeclaration: function(path){

console.log('-----------')

if('jquery' === path.node.source.value) {

path.node.specifiers[0] = importSpecifier(identifier('extend'))

console.log(path.node.specifiers)

path.node.source = literal('utils/extend')

}

return false

},

visitExpressionStatement: function(path){

console.log('++++++')

if(

path.node.expression.callee.object.name === '$' &&

path.node.expression.callee.property.name === 'extend'

) {

path.node.expression = callExpression(identifier('extend'), path.node.expression.arguments)

}

// console.log(memberExpression(identifier(''),identifier('extend'), false))

return false

}

});

const output = recast.prettyPrint(ast, { tabWidth: 2 }).code

fs.writeFile(fileUrl, output, {}, function(){

console.log(`wirte ${fileName} OK!`);

})

});

})

console.log(files)

})

结束

php ast 抽象语法树,抽象语法树(AST)相关推荐

  1. mysql 语法树_Inception 语法树打印(15)

    目前,Inception已经支持大部分MySQL语句了,但是有一点不足之处是,规则都是固定的,虽然可以通过设置参数来修改是不是可以跳过这些检查,但除了检查语法错误之外,其它可配置的检查始终是一个固定在 ...

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

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

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

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

  4. 抽象语法树(AST)

    抽象语法树(AST) 最近在做一个类JAVA语言的编译器,整个开发过程,用抽象语法树(Abstract SyntaxTree,AST)作为程序的一种中间表示,所以首先就要学会建立相对应源代码的AST和 ...

  5. html转换成抽象语法树,五分钟了解抽象语法树(AST)babel是如何转换的?

    抽象语法树 什么是抽象语法树? It is a hierarchical program representation that presents source code structure acco ...

  6. js最小化浏览器_「译」解析、抽象语法树(ast) +如何最小化解析时间的5个技巧...

    前言 该系列课程会在本周陆续更新完毕,主要讲解的都是工作中可能会遇到的真实开发中比较重要的问题以及相应的解决方法.通过本系列的课程学习,希望能对你日常的工作带来些许变化.当然,欢迎大家关注我,我将持续 ...

  7. java抽象语法树_抽象语法树(AST)

    抽象语法树(AST) 最近在做一个类JAVA语言的编译器,整个开发过程,用抽象语法树(Abstract SyntaxTree,AST)作为程序的一种中间表示,所以首先就要学会建立相对应源代码的AST和 ...

  8. php ast 抽象语法树,AST抽象语法树的基本思想

    AST抽象语法树的基本思想 前言 AST概述 AST结构 AST解析 转换 生成 前言 在阅读java ORM框架spring data jpa的源码时,发现Hibernate(spring data ...

  9. java抽象语法树_抽象语法树AST的全面解析(一)

    Javac编译概述 将.java源文件编译成.class文件,这一步大致可以分为3个过程: 1.把所有的源文件解析成语法树,输入到编译器的符号表: 2.注解处理器的注解处理过程: 3.分析语法树并生成 ...

最新文章

  1. java 使用new新建一个对象时的操作过程
  2. ADS与RealView MDK
  3. Java 18 正式发布
  4. 经典PID控制算法用C语言实现!
  5. 腾讯天衍实验室主任郑冶枫
  6. 图解YU12、I420、YV12、NV12、NV21、YUV420P、YUV420SP、YUV422P、YUV444P的区别
  7. 【Scratch】青少年蓝桥杯_每日一题_6.01_画春花
  8. Python 网络爬虫笔记11 -- Scrapy 实战
  9. C++实现插入排序(附完整源码)
  10. OAuth2.0在项目中的应用
  11. 三角形中惊现叛徒!自己胖的像个球,却能成就世界上最快的赛车引擎......
  12. 【渝粤题库】国家开放大学2021春2246社会工作概论题目
  13. STM32启动文件——startup_stm32f10x_hd.s
  14. 21 PP配置-生产计划-定义计划订单编号范围
  15. oracle库锁表处理,oracle 数据库锁表处理 ORA-00031
  16. 【JAVA程序设计】(C00003)基于springboot+mybatis的在线购物商城系统
  17. linux定时器原理
  18. 我的世界java平台缺少证书_解决https安全证书缺少的问题
  19. 如何准备全国计算机二级Python,二级Python考试技巧
  20. android打开系统文件怎么打开方式,Android调用系统应用打开任意文件

热门文章

  1. boost中静态库编译没有-fPIC选项的问题解决方案
  2. 五种傅里叶变换的分析(FT、FS、DTFT、DFT、DFS)(基于MATLAB)
  3. 信创产业专题:突破核心技术,信创扬帆起航(2021)
  4. linux复制粘贴命令
  5. HTTP各个状态码的含义
  6. 每个游戏开发者都需要知道的游戏网络知识
  7. Linux常用信息显示命令
  8. html5标题标语,励志宣传标语(精选70句)
  9. iOS高仿国美、二次元应用、点赞喷射动画、电影筛选页等源码
  10. 如何选择学生护眼灯?适合学生的柔和护眼台灯