AST抽象语法树

12/18/2021 AST

# 什么是 AST

  • AST 是 Abstract Syntax Tree 的缩写,即 “抽象语法树”
  • 它是以树状的形式表现编程语言的语法结构
  • 只要你想修改别人的源代码,或者你想把你的源代码放到别人的代码中执行,抽象语法树是有用的。
  • 可以理解为是将不可直接看到的语句,以具象的方式展示出来(树状的结构)
  • 我们不会自己来拆解语法树,这里使用的 babel 里的相应插件
  • 如何使用?首先要看懂语法树,知道它里面有什么?
  • 在线生成工具: astexplorer.net (opens new window) @babel/parser Tree

# JS语法转化成AST抽象语法树的例子

# let name = 'xm'

file: File{
    type: File,
    // 初始到结尾有几个字符,不太重要
    start: 0,
    end: 18,
    ...
    // 如果要在树里面加东西,program是入口
    program: Program{
        type: Program, //很重要
        body: { // 所有的内容都存在body里面
            VariableDeclaration: { // 变量定义语句
            // 在这个语法树上有很多的关键字:声明变量、运算符、标识符.....这些语法格式在我们的树中都有一个对应  let num = 1
                declarations: [
                    VariableDeclarator: {
                        // 初始化,字符串字面量
                        init: StringLiteral {
                            type : 'StringLiteral',
                            // 初始结束
                            start: 11,
                            end: 15,
                            // 值
                            value: 'xm'
                        }
                    }
                ],
                kind: 'let' //类型
            }
        }
    }
}
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

# let sum = 66 + 88

...
init: BinaryExpression {
    type : 'BinaryExpression',
    // 初始结束
    start: 10,
    end: 17,
    // 标记的66
    left: NumericLiteral,
    // 操作符
    operatior: '+',
    // 标记的88
    right: NumericLiteral
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 有了这个树之后我们就可以对它进行增删改查(DOM节点操作)
  • 只不过这里改完之后操作的只是语法树,并不是我们实际的代码,所以.....(将AST转为代码)

# 从源码到AST抽象语法树的生成过程

源码--词法分析--语法分析--抽象语法树

  • 抽象语法树在生成之前:
    • 词法分析:读出代码里的 符号 + 字符 + 单词(它和JS语法还不相干)
    • 语法分析:将读出来的字符转成符合 JS 语法的 语法单句(一个单句就是一个 type,一个单句里面会有很多节点内容)

示例:let a = 66 + 88

# 词法分析

  • 从左至右一个字符一个字符地读入源程序, 从中识别出一个一个 “单词”“符号”等 读出来的就是普通的字符,没有任何编程语言的函数
  • 将分析之后结果保存在一个数组中,我们称之为词法单元
单词 单词 符号 数字 符号 数字
let a = 66 + 88
[
  {"type": "word", value: "let"},
  {"type": "word", value: "a"},
  {"type": "Punctuator", value: "="},
  {"type": "Numberic", value: "66"},
  {"type": "Punctuator", value: "+"},
  {"type": "Numberic", value: "88"},
]
1
2
3
4
5
6
7
8

# 语法分析

在词法分析的基础上根据当前编程语言的语法,将单词序列组合成各类的语法短语

关键字 标识符 赋值运算符 字面量 二元运算符 字面量
let a = 66 + 88
[{
  "type": "VariableDecLaration", 
  "content": {
    {"type": "kind", "value": "let"},  // kind 表示是什么类型的声明
    {"type": "Identifier", "value": "a"},  // Identifier 表示是标识符
    {"type": "init", "value": "="},  // 表示初始值的表达式
    {"type": "Literal", "value": "66"},  // Literal 表示是一个字面量
    {"type": "operator", "value": "+"},  // operator 表示是一个二元运算符
    {"type": "Literal", "value": "88"},
  } 
}]
1
2
3
4
5
6
7
8
9
10
11

# 生成抽象语法树

let a = 66 + 88

let tree = {
  "type": "Program", 
  "start": 0, 
  "end": 18, 
  "body": [{
    "type": "VariableDeclaration", 
    "kind": "let", 
    "start": 0, 
    "end": 18, 
    "declarations": [{ // 这里是数组,表示可以同时声明多个变量
      "type": "VariableDeclaration", 
      "start": 4
      "end": 17, 
      "id": {
        "type": "Identifier",
        "start": 4, 
        "end": 7,
        "name": "a"
        }, 
      "init": {
        "type": "BinaryExpression", 
        "start": 10, 
        "end": 17,
        "left": {
          "type": "Literal",
          "start": 10,
          "end": 13,
          "value": 10,
          "raw": 10 
        },
        "operator": "+",
        "right": {
          "type": "Literal",
          "start": 16,
          "end": 18,
          "value": 66,
          "raw": 66    
        }
      }
    }]
  }],
  "sourceType": "module"
}
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

# 抽象语法树作用

做一些大型框架或者第三方工具的时候,那么 AST 将是你的不二选择,例如: babel, webpack, JD Taro, uni-app 等

在利用 webpack 打包 JS 代码的时候,webpack 会在我们的原有代码基础上新增一些代码, 例如我们可以在打包JS 代码的时候将高级代码转为低级代码,就是通过 AST 语法树来完成的

把 JS 代码转换成 AST 其实就是将源代码的每一个组成部分拆解出来放至树中,拆解过程是非常复杂的, 所以借助于第三方模块来帮实现拆解, 这里使用 @babel/parse模块

更新时间: 2021-12-18 20:56