将解析树转换为AST

时间:2017-02-23 08:15:46

标签: parsing compiler-construction context-free-grammar

让我先提出问题:我是否可以将实现此特定语法的解析树转换为AST。

我得到了这个语法来构建一个解析树:

literal := INTEGER | FLOAT | TRUE | FALSE .

designator := IDENTIFIER { "[" expression0 "]" } .

op0 := ">=" | "<=" | "!=" | "==" | ">" | "<" .
op1 := "+" | "-" | "or" .
op2 := "*" | "/" | "and" .

expression0 := expression1 [ op0 expression1 ] .
expression1 := expression2 { op1  expression2 } .
expression2 := expression3 { op2 expression3 } .
expression3 := "not" expression3
       | "(" expression0 ")"
       | designator
       | call-expression
       | literal .

对于这个特例:

func main() : void {
    let a = 1 + 2 + 3 + 4;
}

我的解析器将生成(部分)解析树

            EXPRESSION1
                EXPRESSION2
                  EXPRESSION3
                    LITERAL
                      INTEGER(1)(lineNum:2, charPos:10)
                OP1
                  ADD(lineNum:2, charPos:12)
                EXPRESSION2
                  EXPRESSION3
                    LITERAL
                      INTEGER(2)(lineNum:2, charPos:14)
                OP1
                  ADD(lineNum:2, charPos:16)
                EXPRESSION2
                  EXPRESSION3
                    LITERAL
                      INTEGER(3)(lineNum:2, charPos:18)
                OP1
                  ADD(lineNum:2, charPos:20)
                EXPRESSION2
                  EXPRESSION3
                    LITERAL
                      INTEGER(4)(lineNum:2, charPos:22)

注意EXPRESSION1下的这些树枝是如何进行的:

EXPRESSION2 + EXPRESSION2 + EXPRESSION2 + EXPRESSION2

运算符+与其两个操作数不对应。因此在我看来,在AST转换中,我无法通过简单地拉起运算符来替换非终端EXPRESSION1来获得一个有助于生成3地址IR代码的AST。

为了实现这个目标,我为这种语言编写的语法将是这样的

expression1 := expression2 | expression1 + expression2  (1)
expression2 := expression3 | expression2 * expression3  (2)
expression3 := literal                                  (3)

其中EXPRESSION1下的分支仅为

EXPRESSION1 + EXPRESSION2

然而,这个语法不是LL(1),因为| FIRST(表达式2)| = | {literal,+} | &GT; 1.

这引出了一个问题:(1)转换这个解析树最优雅和最简单的方法是什么? (2)我的解析树的构造完全浪费了这个语法的时间,我本来应该开始编写AST了吗?

1 个答案:

答案 0 :(得分:2)

我相信你希望产生一个AST:

     ADD
     /  \
    1   ADD
        /  \  
       2   ADD
           / \
          3   4

但是你可能注意到你的解析树实际上是一个平面列表,并不代表一个简单的起点,即单个传递组操作符及其操作数。无论如何编写更高级的解析器,识别树结构和应用语法减少并不是一项简单的任务。

因此,在开始这样做之前,您可能希望考虑一些现有的解析器生成器,如yacc或ANTLR。可能您需要重写语法以创建以运算符为中心的规则,而不是将它们视为递归实用程序。不是经典LL(1)的语法可能不是一个大问题,因为现代生成器(如ANTLR)可以处理具有更大前瞻,谓词等的LL(k)语法,并且只是绕过该类型的问题。

如果您仍然坚持编码,请手动考虑使用堆栈存储符号,并在收集表达式后将其转换为AST节点,但这又不是一项简单的任务。