什么是抽象语法树?

什么是树?

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

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

什么是语法树?

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

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

在计算机中抽象跟现实中不一样,现实中的抽象表示的模糊的概念,但是计算机里面"抽象"可以理解为本质,就是这个东西的本质,比如可以将类看作是对象的一种抽象。所以抽象语法树就是拿到语法树的本质,所以这一步会将空格,换行之类不影响语法的东西给丢弃掉,方便编译器和代码分析工具来理解个操作代码(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)
相关推荐
来恩10038 分钟前
jQuery选择器
前端·javascript·jquery
前端繁华如梦10 分钟前
树上挂苹果还是挂玻璃球?Three.js 程序化果实的完整实现指南
前端·javascript
墨痕诉清风17 分钟前
Web浏览器客户端检测网站网络健康(代码)
前端·网络·测试工具
IMPYLH20 分钟前
Linux 的 wc 命令
linux·运维·服务器·前端·bash
happybasic37 分钟前
Python库升级标准流程~
linux·前端·python
川冰ICE42 分钟前
前端工程化深度实战:从Webpack5到Vite5的构建工具演进与选型决策
前端
CDwenhuohuo43 分钟前
优惠券组件直接用 uview plus
前端·javascript·vue.js
用户74090472362751 小时前
我用 curl 排查了一次 OpenAI-compatible API 连接失败:401、403、404 分别怎么定位
前端
kft13141 小时前
XSS深度剖析:从弹窗到持久化窃取Cookie
前端·web安全·xss·安全测试
烬羽1 小时前
《前端三权分立:HTML、CSS、JS为什么不能“乱搞”》
前端