如何构造抽象语法树

时间:2009-11-12 11:24:12

标签: abstract-syntax-tree

我对AST是什么有一个大概,但我想知道如何构建一个。

如果给你一个语法和一个解析树,你如何构建AST?

如果给你一个语法和表达,你怎么做?

3 个答案:

答案 0 :(得分:42)

好吧,首先,语法用于从表达式构造一个解析树。因此,如果您已经有一个解析树,则不需要语法。

根据解析器的工作量,通过解析表达式形成的结果树可能已经是一个抽象语法树。或者它可能是一个简单的解析树,需要第二遍来构建ast。

要从语法和表达式构造解析树,首先必须将语法转换为工作代码。通常,您将工作拆分为一个tokenizer,它将表示表达式的输入流拆分为一个标记列表,一个解析器获取标记列表并从中构造一个解析树\ ast。

因此表达式1 + 2*(3+4)可能会被拆分为这样的标记列表:

1 - int
+ - add_operator
2 - int
* - mul_operator
( - lparen
3 - int
+ - add_operator
4 - int
) - rparen

第一列是实际文本值。第二个代表令牌类型。这些标记被输入到解析器中,该解析器是根据您的语法构建的,并识别标记并构建解析树。

那么,如何编写词法标记器和实际的解析器呢?你可以手动滚动自己。或者,更常见的是,使用解析器生成器,如coco或antlr或lex / yacc。这些工具描述了您的语法并为tokenzier和parser生成代码。 (代码生成器适用于大多数流行语言,也有一些不受欢迎的语言。)

如何构建解析器在很大程度上取决于您使用的语言。你如何在Haskell中编写一个解析器与你在C中的编写完全不同。

答案 1 :(得分:3)

我将从一般的角度回答这个问题,而不是试图谈论词法分析器和解析器。

解析树包含作为无上下文语法一部分的非终端符号,并显示生成链以获得由终端符号组成的字符串,无论是递归还是非。因此,当你拥有解析树时,你不需要语法 - 你可以从解析树中派生出语法。

AST不包含任何非终端符号。它只包含符号。

示例:

 E
 |
 E + T
 |   |
 T   M * M
 |   |   |
 M   a   b
 |
 a

这是显示a+a*b的快速版本。请注意,解释抽象语法树的方式取决于树的优先级,您执行的遍历类型(按顺序,预订,后顺序)这将是您在搜索树中编码的一般功能。但是,通常,该解析树的AST可能如下所示:

   +
 |   |
 a   * 
    | |
    a b

答案 2 :(得分:1)

答案在回答“如何构建AST”方面并不令人满意。

这是articlearchived the series Crafting Interpreters,为初学者提供了简洁方便的答案。完成实施。 Stackoverflow不是链接的地方,所以我将复制要点。


递归下降解析

有一整套解析技术,其名称似乎主要是“ L”和“ R”的组合-LL(k),LR(1),LALR-以及解析器组合器,Earley解析器等更多奇特的野兽,调车场算法和packrat解析。对于我们的第一个口译员来说,一种技术绰绰有余:递归血统。

递归下降是构建解析器的最简单方法,不需要使用复杂的解析器生成器工具,例如Yacc,Bison或ANTLR。您只需要简单的手写代码。不过,不要被它的简单性所迷惑。递归下降解析器快速,健壮,并且可以支持复杂的错误处理。实际上,GCC,V8(Chrome中的JavaScript VM),Roslyn(用C#编写的C#编译器)和许多其他重量级生产语言实现都使用递归下降。踢屁股。

它被认为是自上而下的解析器,因为它从最高或最外面的语法规则(此处为表达式)开始,一直向下进入嵌套的子表达式,最后到达语法树的叶子。这与像LR这样的自底向上解析器相反,后者从主表达式开始并将它们组合成越来越大的语法块。

递归下降解析器是将语法规则直接转换为命令式代码的字面意思。每个规则成为一个功能。规则的主体翻译为大致如下的代码:

Grammar notation    Code representation
Terminal            Code to match and consume a token
NonterminalCall     to that rule’s function
|                   if or switch statement
* or +              while or for loop
?                   if statement

之所以称为“递归下降”,是因为当语法规则直接或间接指向自身时,会转化为递归方法调用。


旁注:

之所以称为“递归血统”,是因为它沿语法进行。令人困惑的是,当谈论“高”和“低”优先级时,我们也隐喻地使用了方向,但是方向却相反。在自上而下的解析器中,您首先会到达优先级最低的表达式,因为它们可能进而包含优先级更高的子表达式。 自上而下的语法规则按优先级从高到低的顺序。

Original image link

parsing expressions direction

CS人士确实需要聚在一起,弄清楚他们的隐喻。甚至不要让我开始了解堆栈应该朝哪个方向发展。