JavaScript - 什么是抽象语法树(AST)

抽象语法树(Abstract Syntax Trees),简称AST,如果您正在编写代码,那么 AST 很可能已经参与了您的开发流程。它们为您的开发流程的许多部分提供动力。 有些人可能在编译器的上下文中听说过它们,但它们被用于各种工具中。 即使您不编写通用开发工具,AST 也可能是您工具带中的有用工具。 在这篇文章中,我们将讨论什么是 AST,它们在哪里使用以及如何利用它们。

什么是AST

抽象语法树或 AST 是代码的树型数据结构表示。 它们是编译器工作方式的基本部分。 当编译器转换某些代码时,基本上有以下步骤:

  1. 词法分析
  2. 语法分析
  3. 代码生成

词法分析又名标记化

在此步骤中,您编写的代码将被转换为一组描述代码不同部分的标记。 这与基本语法突出显示使用的方法基本相同。 这些标记令牌不了解事物如何组合在一起,并且仅关注文件的组件。

你可以想象这就像你将一个文本分解成单词。 您可能能够区分标点符号、动词、名词、数字等,但在这个阶段,您对句子的组成部分或句子如何组合没有任何更深入的了解。

语法分析又名解析

这是我们将标记列表转换为抽象语法树的步骤。 它将我们的标记转换为表示代码实际结构的树。 以前在标记中我们只有一对 (),现在我们知道它是函数调用、函数定义、分组还是其他东西。

这里的等价物是将我们的单词列表转换为表示诸如句子之类的数据结构,某个名词在句子中扮演什么角色,或者我们是否在列表中。

另一个可以与之比较的例子是 DOM。 上一步只是将 HTML 分解为“标签”和“文本”,而这一步将生成表示为 DOM 树的层次结构。

需要注意的一件事是没有“单一”的 AST 格式。 它们可能会有所不同,这取决于您要转换为 AST 的语言以及您用于解析的工具。 在 JavaScript 中,一个通用标准是 ESTree,但您会看到不同的工具可能会添加不同的属性。

一般来说,AST 是一种树结构,其中每个节点至少有一个类型来指定它所代表的内容。

代码生成

此步骤本身可以是多个步骤。 一旦我们有了抽象语法树,我们就可以操作它,也可以将它“打印”到不同类型的代码中。 使用 AST 操作代码比直接在代码上作为文本或标记列表执行这些操作更安全。

操纵文本总是很危险的; 它显示最少的上下文。 如果您曾经尝试使用字符串替换或正则表达式来操作文本,您可能会注意到很容易出错。而且不容易调试。

甚至操纵令牌也不容易。 虽然我们可能知道变量是什么,但如果我们想重命名它,我们将无法深入了解变量的范围或可能与之冲突的任何变量。

AST 提供了有关代码结构的足够信息,我们可以更有信心地对其进行修改。 例如,我们可以确定变量的声明位置,并确切地知道由于树结构而影响程序的哪个部分。

一旦我们操纵了树,我们就可以打印树以输出任何预期的代码输出。 例如,如果我们要构建一个像 TypeScript 编译器这样的编译器,我们会输出 JavaScript,而另一个编译器可能会输出机器代码。

同样,使用 AST 更容易实现这一点,因为相同结构的不同输出可能具有不同的格式。 使用更线性的输入(如文本或标记列表)生成输出会相当困难。

如何处理 AST?

理论涵盖了哪些实际生活中的 AST 用例? 我们讨论了编译器,但我们并不是整天都在构建编译器。

AST 的用例很广泛,通常可以分为三个总体操作:读取、修改和打印。 它们是一种添加剂,这意味着如果您正在打印 AST,那么您以前也阅读过 AST 并对其进行修改的可能性很高。 但我们将介绍每个主要关注一个用例的示例。

读取/遍历 AST

从技术上讲,使用 AST 的第一步是解析文本以创建 AST,但在大多数情况下,提供解析步骤的库也提供了一种遍历 AST 的方法。遍历 AST 意味着访问树的不同节点以获取细节或执行操作。

最常见的用例之一是 linting。 例如,ESLint 使用 espree 生成 AST,如果您想编写任何自定义规则,您将根据不同的 AST 节点编写这些规则。 ESLint 文档有大量关于如何构建自定义规则、插件和格式化程序的文档。

修改/转换 AST

如前所述,与将代码修改为标记或原始字符串相比,拥有 AST 使修改所述树更容易、更安全。您可能想要使用 AST 修改某些代码的原因有很多种。

例如,Babel 修改 AST 以向下编译新功能或将 JSX 转换为函数调用。例如,当您编译 React 或 Preact 代码时会发生这种情况。

另一个用例是捆绑代码。在模块的世界中,捆绑代码通常比将文件附加在一起要复杂得多。更好地了解各个文件的结构可以更轻松地合并这些文件并在必要时调整导入和函数调用。如果您查看 webpack、parcel 或 rollup 等工具的代码库,您会发现它们都使用 AST 作为其捆绑工作流程的一部分。

打印 AST

在大多数情况下,打印和修改 AST 是齐头并进的,因为您必须输出刚刚修改的 AST。 但是,虽然像 recast 这样的一些库明确专注于以与原始代码样式相同的代码样式打印 AST,但也有各种用例,您希望以不同的方式显式打印您的 AST。

例如,Prettier 使用 AST 根据您的配置重新格式化您的代码,而无需更改代码的内容/含义。 他们这样做的方式是将您的代码转换为完全与格式无关的 AST,然后根据您的规则重写它。

其他常见的用例是用不同的目标语言打印代码或构建自己的缩小工具。

您可以使用几种不同的工具来打印 AST,例如 escodegen 或 astring。 您还可以根据您的用例构建自己的格式化程序,或者为 Prettier 构建一个插件。

最后:

虽然 AST 可能是大多数开发人员每天都不会使用的东西,但我相信了解它对今后的工作会有帮助。感谢阅读。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章