什么是抽象语法树?

什么是树?

树是一种数据结构,具有以下特点

  • 具有层级关系
  • 有单一父节点
  • 等等

什么是语法树?

代码字符串通过词法分析、语法分析、转为出来的用来表示代码结构的树状结构

计算机的"抽象"是什么概念?

在计算机中抽象跟现实中不一样,现实中的抽象表示的模糊的概念,但是计算机里面"抽象"可以理解为本质,就是这个东西的本质,比如可以将类看作是对象的一种抽象。所以抽象语法树就是拿到语法树的本质,所以这一步会将空格,换行之类不影响语法的东西给丢弃掉,方便编译器和代码分析工具来理解个操作代码(Babel和ESLINT等工具都是来操作抽象语法树的)

抽象语法树是怎么来的?

我们的代码本质上是一串字符串,但是计算机并不认识这个字符串,所以需要解析器通过各种操作与转化,将这串字符串转为编译器可以识别的语言。

以 let a = 1 + 2; 这段js代码为例

  • 解析器做的事情:解析器(Parser) = 词法分析 + 语法分析 的组合,生成抽象语法树(AST)
  • 编译器做的事情:编译器(Compiler) = 包含解析器,还包括后续的优化、代码生成等步骤,拿到 AST 后,可能会优化成出 a = 3;这样的代码,然后生成底层的机器码或字节码,做这些操作
  1. 词法分析

首先会对这串代码字符进行一系列的词法分析,把一串字符切分成一个个 Token,形成token流,这一步会判断个关键字,符号,数字,标识符等等,然后还会记录位置,方便进行报错,下图可以看到转出来的token流

代码层面是这样

js 复制代码
[
  { type: "Keyword", value: "let" },
  { type: "Identifier", value: "a" },
  { type: "Punctuator", value: "=" },
  { type: "Numeric", value: "1" },
  { type: "Punctuator", value: "+" },
  { type: "Numeric", value: "2" },
  { type: "Punctuator", value: ";" }
]
  1. 语法分析

词法分析结束后会生成对应的token流,然后再根据token流进行语法分析,将对应的token流转为语法树。 大致转成下面这样

js 复制代码
{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "kind": "let",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": { "type": "Identifier", "name": "a" },
          "init": {
            "type": "BinaryExpression",
            "operator": "+",
            "left": { "type": "Literal", "value": 1 },
            "right": { "type": "Literal", "value": 2 }
          }
        }
      ]
    }
  ]
}
  1. 抽象
  • 在计算机中抽象跟现实中不一样,现实中的抽象表示的模糊的概念,但是计算机里面"抽象"可以理解为本质,就是这个东西的本质,比如可以将类看作是对象的一种抽象。
  • 所以这里的抽象就是拿到代码的本质,所以这一步会将空格,换行之类不影响语法的东西给丢弃掉,方便编译器和代码分析工具来理解个操作代码(Babel和Eslint等工具都是来操作抽象语法树的)
  1. 常见的js解析器
  • Acorn → 轻量级 JS parser(ESLint、Rollup 用)

  • Espree → ESLint 默认 parser(基于 Acorn)

  • Babel Parser → Babel 的解析器,支持最新语法

  • Esprima → 早期常用解析器

  • 以babel为例,生成的AST

json 复制代码
 {
  "type": "File",
  "start": 0,
  "end": 14,
  "loc": {
    "start": {
      "line": 1,
      "column": 0,
      "index": 0
    },
    "end": {
      "line": 1,
      "column": 14,        
      "index": 14
    }
  },
  "errors": [],
  "program": {
    "type": "Program",     
    "start": 0,
    "end": 14,
    "loc": {
      "start": {
        "line": 1,
        "column": 0,       
        "index": 0
      },
      "end": {
        "line": 1,
        "column": 14,      
        "index": 14        
      }
    },
    "sourceType": "module",
    "interpreter": null,   
    "body": [
      {
        "type": "VariableDeclaration",
        "start": 0,
        "end": 14,
        "loc": {
          "start": {
            "line": 1,
            "column": 0,
            "index": 0
          },
          "end": {
            "line": 1,
            "column": 14,
            "index": 14
          }
        },
        "declarations": [
          {
            "type": "VariableDeclarator",
            "start": 4,
            "end": 13,
            "loc": {
              "start": {
                "line": 1,
                "column": 4,
                "index": 4
              },
              "end": {
                "line": 1,
                "column": 13,
                "index": 13
              }
            },
            "id": {
              "type": "Identifier",
              "start": 4,
              "end": 5,
              "loc": {
                "start": {
                  "line": 1,
                  "column": 4,
                  "index": 4
                },
                "end": {
                  "line": 1,
                  "column": 5,
                  "index": 5
                },
                "identifierName": "a"
              },
              "name": "a"
            },
            "init": {
              "type": "BinaryExpression",
              "start": 8,
              "end": 13,
              "loc": {
                "start": {
                  "line": 1,
                  "column": 8,
                  "index": 8
                },
                "end": {
                  "line": 1,
                  "column": 13,
                  "index": 13
                }
              },
              "left": {
                "type": "NumericLiteral",
                "start": 8,
                "end": 9,
                "loc": {
                  "start": {
                    "line": 1,
                    "column": 8,
                    "index": 8
                  },
                  "end": {
                    "line": 1,
                    "column": 9,
                    "index": 9
                  }
                },
                "extra": {
                  "rawValue": 1,
                  "raw": "1"
                },
                "value": 1
              },
              "operator": "+",
              "right": {
                "type": "NumericLiteral",
                "start": 12,
                "end": 13,
                "loc": {
                  "start": {
                    "line": 1,
                    "column": 12,
                    "index": 12
                  },
                  "end": {
                    "line": 1,
                    "column": 13,
                    "index": 13
                  }
                },
                "extra": {
                  "rawValue": 2,
                  "raw": "2"
                },
                "value": 2
              }
            }
          }
        ],
        "kind": "let"
      }
    ],
    "directives": [],
    "extra": {
      "topLevelAwait": false
    }
  },
  "comments": []
}
  1. 常见编译器
  • Babel 将 ES6+/TypeScript/JSX 代码转译为 ES5 或更低版本的 JavaScript。
  • TypeScript Compiler (tsc) 将 TypeScript 代码编译成 JavaScript,并支持类型检查。
  • babel的过程
js 复制代码
import { generate } from '@babel/generator';
import { parse } from '@babel/parser';

const ast = parse("let a = 1 + 2;", { sourceType: "module" }) //生成AST
// babel还会做一些列的其它事情,访问,修改ast,然后转化出修改的ast
const { code,map } = generate(ast) //再重新生成代码
console.log(code,map)
相关推荐
醉方休8 小时前
React Fiber 风格任务调度库
前端·javascript·react.js
北辰alk9 小时前
React Intl 全方位解析:为你的 React 应用注入国际化灵魂
前端
李白白i单身版9 小时前
前端VUE项目实现静默打印,无需用户手动确认
前端
bysking9 小时前
【29 - git bisect】git bisect 命令进行二分定位,排查异常commit bysking
前端
华仔啊9 小时前
摸鱼神器!前端大佬私藏的 11 个 JS 神级 API,复制粘贴就能用,效率翻倍
前端·javascript
一枚前端小能手9 小时前
🔥 React Hooks又让我重新渲染了999次!这些坑你踩过几个?
前端
念念不忘 必有回响9 小时前
js设计模式-状态模式
javascript·设计模式·状态模式
我的写法有点潮9 小时前
Scss 的四种导入方式你都知道吗
前端·css
薄荷糖__9 小时前
(二)模块化:ES Module使用原理,包管理工具npm
前端·面试
云飞云共享云桌面9 小时前
SolidWorks对电脑的硬件配置要求具体有哪些
java·服务器·前端·网络·数据库