我对AST是什么有一个大致的了解,但是我想知道如何构造一个。
如果获得语法和分析树,该如何构建AST?
如果得到语法和表达方式,该怎么办?
Answers:
好吧,首先,语法用于从表达式构造解析树。因此,如果您已经有一个分析树,则不需要语法。
根据解析器的工作量,解析表达式构成的结果树可能已经是抽象语法树。或者它可能是一个简单的解析树,需要第二遍才能构造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和解析器生成代码。(代码生成器适用于大多数流行语言,也有一些不流行的语言。)
构造解析器的方式在很大程度上取决于您使用哪种语言。在Haskell中编写解析器的方式与在C语言中编写的方式完全不同。
这是一个教程,向您展示如何构建自己的递归下降解析器。
Coco是用于多种语言的解析器生成器,还附带有关如何入门的文档。
我将从一般角度回答此问题,而无需尝试谈论词法分析器和解析器。
语法分析树包含非上下文符号,这些符号是上下文无关文法的一部分,并显示了产生链以最终或非递归地获得由终端符号组成的字符串。因此,当您拥有语法分析树时,就不需要语法了-您可以从语法分析树中得出语法。
AST不包含任何非终结符。它仅包含符号。
例:
E
|
E + T
| |
T M * M
| | |
M a b
|
a
这是show的非常快速的版本a+a*b
。请注意,抽象语法树的解释方式取决于树的优先级,您执行的遍历类型(有序,前序,后序)。这将是您编写到搜索树中的通用功能。但是,通常来说,该解析树的AST可能如下所示:
+
| |
a *
| |
a b
答案在回答“如何构建AST”问题上并不令人满意。
此文章 存档从该系列手工艺口译,回答它整齐,易于初学者。完成实施。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
之所以称其为“递归下降”,是因为当语法规则直接或间接指向自身时,即转化为递归方法调用。
旁注:
之所以称其为“递归血统”,是因为它沿语法进行。令人困惑的是,当谈论“高”和“低”优先级时,我们也隐喻地使用了方向,但是方向却相反。在自上而下的解析器中,您首先会到达优先级最低的表达式,因为它们可能进而包含优先级更高的子表达式。自上而下的语法规则按优先级从高到低的顺序排列。
CS人士确实需要聚在一起,弄清楚他们的隐喻。甚至不要让我开始了解堆栈应该朝哪个方向发展。