抽象语法树(AST)
核心提示:抽象语法树是编程语言中一个极其重要的概念,弄清楚它有助于后续深入理解编译原理、代码转译、babel 等技术。
一、概念拆解
当我们遇到一个难以理解的专业术语时,最简单的方式就是拆词。针对"抽象语法树",我们可以拆解为三个部分:
- 抽象
- 语法
- 树
接下来我们要做的就是针对这三个词逐一击破,只要把这三个词搞懂了,那么抽象语法树整体的概念也就能够理解了。
二、树(Tree)
2.1 什么是树
树实际上是一种数据结构。
我们都知道计算机是用来处理数据的,处理数据的第一步就是先要将数据存储进去。那么存储数据的方式就有多种多样。
现实生活中的例子:比如我们有一个书柜(计算机)放 10 本书(数据),那么我放置这 10 本书的方式是多种多样的,我可以横着放、竖着放、斜着放。
所谓数据结构,实际上就是数据(书)在计算机(书柜)中组织和管理的一种方式。根据不同的场景,使用合适的数据结构能够帮助我们高效地对数据进行访问和操作。
2.2 数据结构的分类
数据结构从大类上分类,可以分为两大类:
1. 线性数据结构
数据以线性的方式来进行存储,这种结构又被称之为序列,每个数据在序列中最多只有一个前驱和后驱数据。
常见的线性数据结构:
| 类型 | 特点 | 典型应用 |
|---|---|---|
| 数组 | 连续存储空间中的固定大小集合,允许通过索引快速访问元素 | 需要频繁查找的场景 |
| 链表 | 由节点组成的线性集合,每个节点包含数据和指向下一个节点的指针 | 需要频繁插入删除的场景 |
| 栈 | 遵循**后进先出(LIFO)**原则,操作都在栈顶进行 | 函数调用、撤销操作 |
| 队列 | 遵循**先进先出(FIFO)**原则,一端添加、另一端移除 | 任务队列、消息队列 |
2. 非线性数据结构
数据之间的存储和关系不是线性的。
常见的非线性数据结构:
| 类型 | 特点 | 典型应用 |
|---|---|---|
| 树 | 分层结构,有根节点,其余节点按层级组织,每个节点可有多个子节点 | DOM树、文件系统 |
| 图 | 由顶点(节点)和边组成,边连接顶点,可有向或无向 | 社交网络、地图导航 |
2.3 没有完美的数据结构
核心原则:没有一种数据结构是完美的。假设有那么一种完美的数据结构,那么其他数据结构就没有存在的意义了。
数组 vs 链表的对比:
-
数组:在内存中是一段连续的地址
- ✅ 查找效率高:通过下标可直接定位
- ❌ 插入/删除效率低:需要移动后续元素
-
链表:在内存中不连续存储,通过 next 字段指向下一个节点
- ✅ 插入/删除效率高:只需修改指针
- ❌ 查找效率低:需要遍历
2.4 树结构的优势
树这种非线性的数据结构在解决某些问题的时候,具有以下优点:
- 层次关系:自然表示数据之间的层次关系,如文件系统目录结构、组织结构、语法分析树等。
- 搜索效率 :对于二叉搜索树、AVL树、红黑树等,搜索、插入和删除操作的时间复杂度通常为 O(log n),比线性数据结构高得多。
- 动态数据集合:可以方便地添加、删除和重新组织节点,适合动态变化的数据集合。
- 有序存储:在有序树结构中,数据按一定顺序组织,可在 O(log n) 时间内完成查找最大值、最小值等操作。
- 空间优化:例如字典树(Trie)可存储大量字符串,公共前缀只存储一次,有效节省空间。
- 分治策略:树结构天然适应分治策略,可将复杂问题分解为较小的子问题并递归求解。
2.5 树的应用
正因为"树"这种数据结构有上述优点,所以你在很多地方都能看到它的身影:
- DOM 树
- CSSOM 树
- Vue 模板树
- 语法树(我们重点讨论的内容)
三、语法树
3.1 什么是语法树
语法树:简单来讲,就是将我们所写的代码转为树的结构。
3.2 从代码到 Token
假设有如下代码:
javascript
var a = 42;
var b = 5;
function addA(d) {
return a + d;
}
var c = addA(2) + b;
对于编译器或解释器来说,它们看不懂这些代码,这就是一段连续的字符串:
javascript
'var a = 42;var b = 5;function addA(d) {return a + d;}var c = addA(2) + b;'
编译器或解释器会对代码进行整体扫描分析,分析出来:
- 哪些是关键字(Keyword)
- 哪些是标识符(Identifier)
- 哪些是运算符(Punctuator)
形成一个一个的 token(最小的不可再拆分的单位)。
上面代码分析出的 token 序列:
Keyword(var) Identifier(a) Punctuator(=) Numeric(42) Punctuator(;)
Keyword(var) Identifier(b) Punctuator(=) Numeric(5) Punctuator(;)
Keyword(function) Identifier(addA) Punctuator(() Identifier(d) Punctuator())
Punctuator({) Keyword(return) Identifier(a) Punctuator(+) Identifier(d)
Punctuator(;) Punctuator(}) Keyword(var) Identifier(c) Punctuator(=)
Identifier(addA) Punctuator(() Numeric(2) Punctuator()) Punctuator(+)
Identifier(b) Punctuator(;)

3.3 从 Token 到语法树
最终,会采用"树"这种数据结构来存储上面的 token 数据,形成一颗语法树。
在线可视化工具 :https://www.jointjs.com/demos/abstract-syntax-tree
你可以将自己的源码放上去,看到对应源码所生成的语法树。
四、抽象
4.1 计算机科学中的"抽象"
重要区分:在计算机科学里面的"抽象"这个词和现实生活中"抽象"这个词的含义是不太相同的。
- 现实生活中:"抽象"往往指"很模糊"的意思。
- 计算机科学中 :抽象是一种思维方式,具体指的是从一个具体事物中提取出本质特征、概念和规律,忽略不相关的细节。
这实际上是一种非常重要的方式,通过这种方式,我们可以将某个复杂的问题分解成更简单的、更纯粹的小问题,从而帮助我们更容易地解决复杂问题。
4.2 抽象在语法树中的体现
明白了抽象的概念之后,我们再来看抽象语法树。
在将源代码转换为树结构的时候,只会关注代码的结构和语法,会忽略具体的字符、空格、换行这些表达细节。像这些不重要的表达细节,在形成树结构的时候通通会被丢弃掉。
五、抽象语法树(AST)
5.1 定义
抽象语法树,是编程语言中一种树形的数据结构,用于表示源代码的语法结构。
5.2 核心特征
在 AST 中:
- 每个节点代表源代码中的一个语法元素(如变量、表达式、语句等)
- 描述了这些元素之间的层次关系
- 采用抽象思想:只关注代码的结构和语法,忽略具体的字符、空格、换行等表达细节
5.3 价值与意义
通过这种抽象表示,我们可以:
- ✅ 更方便地理解、分析和操作源代码
- ✅ 无需直接处理文本格式的代码
- ✅ 简化代码处理过程
- ✅ 提高代码操作的精确性和可扩展性
- ✅ 实现高效的代码优化和转换算法
5.4 应用领域
抽象语法树在以下领域具有广泛的应用:
- 编译器和解释器设计
- 代码分析(静态代码检查、代码审计)
- 代码转换(Babel、TypeScript 编译)
- 代码优化
- 代码生成
六、总结
| 概念 | 核心含义 |
|---|---|
| 树 | 一种分层、非线性的数据结构,擅长表示层次关系 |
| 语法树 | 将代码转为树形结构的表示 |
| 抽象 | 忽略无关细节,只保留本质特征 |
| AST | 结合了树的结构、语法的表达、抽象的思想,是代码的结构化表示 |
一句话概括:抽象语法树就是忽略代码中的无关细节(空格、换行等),只保留代码的结构和语法信息,用树形数据结构来表示源代码。