AST(Abstract Syntax Tree) 抽象语法树

254 阅读3分钟

AST(Abstract Syntax Tree) 抽象语法树

为什么要谈 AST?

如果你查看目前任何主流的项目中的devDependencies,会发现前些年的不计其数的插件诞生。比如,babel、css 预处理器、eslint、prettier 等。有很多 js 模块我们不会在生产环境用到,但是它们在我们的开发过程中充当着重要的角色。所有的上述工具都是建立在 AST 之上。

什么是 AST?

It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.

它是根据编程语言的语法来展示源码结构的分层程序表示,每个 AST 节点都对应着源码中的一部分(item 不知道怎么翻译比较好,暂时翻译为部分)。

让我们看个例子:

Alt text

其中的重点是,从一段代码,我们可以得到一个树形结构的数据。代码中的每一部分都和树中的节点匹配。

如何从一段代码得到 AST?

Alt text

幸运的是,我们并不需要过一遍从高级语言代码到二进制的所有步骤。我们只对词法分析(Lexical Analysis)和语法分析(Syntax Analysis)感兴趣,而这两步也是从代码生成 AST 过程中最重要的部分。

词法分析(Lexical Analysis)

第一步,词法分析。

词法分析器(或者叫扫描器——scanner)会读取代码,然后使用定义好的规则生成 token。同时,它也会移除空白字符,注释等。最后,整个代码会被分离成一系列的 token。

Alt text

我们可以看到一个 token 就是一个对象,内部有值和类型的信息。

词法分析器会一个字符一个字符扫描代码,然后当它遇到了空白字符、操作符以及特殊字符等时,它会判断这个词已经结束了。

语法分析(Syntax Analysis)

第二步,语法分析。

语法分析器(或者叫解析器——parser)会接受词法分析生成的一系列 token,然后会将它们转化为树形结构,最后会验证语言语法,如果有语法错误的话,会抛出语法错误。

Alt text

当生成一棵树时,一些解析器会省略不必要的 token(比如重复的括号)。所以生成的抽象语法树可能并不是 100%匹配代码的,不过了解这个机制存在就好,不用太过深究。另一方面,也存在「具体语法树(Concrete Syntax Tree)」,它是 100%匹配代码的。

AST playground

然后介绍一个可以在线生成 AST 的网站——astexplorer,它同时也支持了很多除了 JS 之外的语言。

image.png

Babel

Babel 是一个 JS 解析器(compiler)。

Babel 是一个主要用来将 ECMAScript 2015+ 代码转换为向后兼容当前和更老版本浏览器或者环境代码的工具链。

主要功能

  • 转换语法。
  • polyfill 目标环境缺失的功能。
  • 源码转换(codemods)。
  • 还有更多其他功能(可以去官网查看哦~)

可插拔(Pluggable)

**Babel 是由插件组成的(Babel is built out of Pluggins)。**使用现有的或者自己写的插件可以组成自己的语法转换流水线。

你可以利用上面说到的astexplorer.net随时随地的创建插件,或者使用generator-babel-plugin来生成插件模版。

简单插件的例子:

// A plugin is just a function
// 一个插件就是一个函数
export default function ({ types: t }) {
  return {
    visitor: {
      Identifier(path) {
        let name = path.node.name; // 反转每个节点的名称: JavaScript -> tpircSavaJ
        path.node.name = name.split("").reverse().join("");
      },
    },
  };
}

左上:转换之前的代码 右上:生成的语法树 左下:自定义的反转名称的插件 右下:转换的结果

image.png

可调试(Debuggable)

支持 Source map,它可以让你很方便的调试编译后的代码。

Source map 是一种将编译后的代码映射回为未构建状态的代码的方式。