我正在自学为编程语言编写解释器,并且我已经阅读了抽象语法树。我知道它们是什么,但我看不到它们的用途。
为什么 AST 有用?
使用 AST 的主要好处是您将解析和验证逻辑与实现部分分开。作为 AST 实现的解释器确实更容易理解和维护。如果您在解析一些奇怪的语法时遇到问题,请查看 AST 解析器,如果一段代码没有产生预期的结果,那么您可以查看解释 AST 的代码。
另一个很大的优势是当您的语法需要“前瞻”时,例如,如果您的语法允许在定义子例程之前使用它,那么在使用 AST 时验证子例程的存在是微不足道的 - 使用“即时”解析器。
它们代表代码的逻辑/语法,它自然是一棵树而不是行列表,不会陷入具体的语法问题,例如您放置星号的位置。
然后可以从后端的 POV 以更一致和更方便的方式操作逻辑,这与我们编写具体语法的方式可能(并且对于除 Lisps 之外的所有内容)非常不同。
您需要“语法树”来表示大多数编程语言的结构,以便对包含编程语言文本的文档进行分析或转换。(你可以通过我的简历看到一些精彩的例子)。
该树是抽象的 (AST) 还是具体的 (CST) 取决于品味、便利性和工程汗水。术语 CST 专门用于描述使用语法解构源代码时的解析派生树;它通常包含许多具体语法的树元素,例如语句终止符分号。AST 用于表示“比 CST 更简单的东西”,例如,省略分号树节点,因为它们不会对程序分析产生太大影响,因此编写处理 AST 的分析器比在科技委。理解这一点的更好方法是认识到 AST 通常与 CST 同构,也就是说,您应该能够从中重新生成 CST。如果你想变身源文本并重新生成它,那么 CST 通常是更好的选择,因为它从原始程序中丢失的信息更少(我的奇特示例使用这种方法)。
我认为您会发现关于抽象与具体语法树的 SO 讨论非常有帮助。
通常,您会将代码解析为某种形式的 AST,它可能或多或少是一个正式的模型。所以我认为 Kirk Woll 上面的评论所要表达的意思是,当你解析语言时,你经常使用解析器来创建某种类型的数据模型,即你正在阅读的原始内容,通常以树的方式组织. 因此,根据该定义,除非您正在做一个非常简单的翻译,否则很难避免 AST。
我经常使用 ANTLR 来解析复杂的语言,在这种情况下,AST 的含义稍微更具体。ANTLR 有一种方便的方法,可以使用非常简单的操作在解析器语法中生成 AST。然后,您为此 AST 编写一个更简单的解析器,您可以像您正在处理的语言的更简单版本一样对其进行操作。构建两个解析器的额外工作是否是净收益取决于语言复杂性以及解析后您打算如何使用它。
您可能想看的一本关于该主题的好书是 ANTLR 作者 Terrence Parr 的“语言实现模式”。他非常彻底地解决了这个话题。也就是说,在我开始使用 AST 之前,我并没有真正得到它们,所以(像往常一样)是理解它们的最佳方式。
迟到的问题,但我想我会添加一些东西。您实际上不必构建 AST。可以在解析源代码时直接发出指令。在这种情况下,解析语法中隐含了 AST。对于简单的语言,尤其是动态类型的语言,这是一个非常好的策略。对于更复杂的语言或需要进一步分析源代码的地方,AST 可能非常有用。例如,如果你的语言是静态类型的,即你的变量是用固定类型声明的,那么 AST 可以用来检查你没有给变量分配错误的类型。例如,将字符串分配给声明为保存整数的变量将是错误的,这可以使用 AST 更方便地捕获。
此外,正如其他人所提到的,AST 提供了语法分析和代码生成之间的清晰分离,并使代码更加模块化。