Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Compiler construction 为具有多行的代码创建抽象语法树_Compiler Construction_Abstract Syntax Tree_Yacc - Fatal编程技术网

Compiler construction 为具有多行的代码创建抽象语法树

Compiler construction 为具有多行的代码创建抽象语法树,compiler-construction,abstract-syntax-tree,yacc,Compiler Construction,Abstract Syntax Tree,Yacc,从多行代码创建AST的一般做法是什么?例如,如果我正在编写一个翻译器将代码从一种语言翻译成另一种语言,我会遇到一组如下语句: x = 2 f = k->o a = 1+2*3 我可以在这里成功地为每一行代码创建AST。现在,在必须生成翻译后的代码时,如果代码是n行长的,那么使用一个AST而不是n个AST会更好吗?如果是这样的话,单个AST是如何形成的?首先,您可能不应该再考虑“代码行”了。解析器的输入是一个令牌流。你有一个lexer,不是吗?:-) AST跨越许多语句是完全正常的。实际上

从多行代码创建AST的一般做法是什么?例如,如果我正在编写一个翻译器将代码从一种语言翻译成另一种语言,我会遇到一组如下语句:

x = 2
f = k->o
a = 1+2*3

我可以在这里成功地为每一行代码创建AST。现在,在必须生成翻译后的代码时,如果代码是n行长的,那么使用一个AST而不是n个AST会更好吗?如果是这样的话,单个AST是如何形成的?

首先,您可能不应该再考虑“代码行”了。解析器的输入是一个令牌流。你有一个lexer,不是吗?:-)

AST跨越许多语句是完全正常的。实际上,它通常会跨越整个编译单元,例如单个函数甚至整个模块(读:源文件)

例如,考虑这个程序:

fun f(x, y) {
    z = x + y;
    if (z > 3) {
        return z; 
    } else {
        return g(x);
    }
}

fun g(x) {
    return x * 2;
}
编译器可能会构建一个类似以下内容的AST(请忽略细节,关注一般结构):


为整个源文件构建AST的好处是,可以轻松地对代码执行多个逻辑传递(例如,为了解析前向引用)。当然,缺点是AST可能会变得非常大。

我建议您使用一个由较小的AST组成的AST。并使用递归遍历它们。对于树的设计,您可以选择任意一侧。但是我会用右边来表示每一行,用左边来表示下一行代码。把它画出来。我假设您知道如何解析assignNode和exprNode,并显示更大的图片

x=2

f=k->o

a=1+2*3

所以,对于这一点,你有

                                   [AssignNode]
                          left                      right
                      [AssignNode]                  [x=2]
              left                   right  
           [AssignNode]             [f=k->o]
     left                right
    [Null]            a = [ExprNode]

                           [1]  +   [2*3]
遍历将从向右开始,直到到达null。一旦我们点击null,返回节点并向左走

因为这是一个由更小的组成的Ast。每次我们点击一个新的节点/Ast,我们都会进行同样的遍历,然后向右再向左

给我们 向右走,得到x=2 然后点击null return并向左走。 到达指定节点 向右走,得到f=k->o 然后点击null return并向左走。 获取分配节点 右转获取一个=ExprNode 右转并解析ExprNode返回 并完成节点返回 然后向左走。
左为空。

如果您愿意,可以将单个语句AST组合成块或复合语句AST,而块或复合语句AST又可以与其他子树组合成函数/方法树,一直到完整的程序树(一个AST代表整个程序)。其思想是在层次结构的顶部有一个AST。当你不想调用某个方法时,你就在上面的AST上调用它,递归将完成剩下的工作。使用一个名为“sequential_statement”的树节点来实现这一点,左子节点由一个语句组成,右子节点是另一个“sequential_statement”节点或伪“end of list”节点。
                                   [AssignNode]
                          left                      right
                      [AssignNode]                  [x=2]
              left                   right  
           [AssignNode]             [f=k->o]
     left                right
    [Null]            a = [ExprNode]

                           [1]  +   [2*3]