抽象语法树构造和遍历

时间:2011-05-27 18:15:26

标签: language-agnostic traversal abstract-syntax-tree construction

我不清楚抽象语法树的结构。要在AST代表的程序源中“向下(向前)”,你是在最顶层的节点上走,还是走下去?例如,示例程序

a = 1
b = 2
c = 3
d = 4
e = 5

导致AST看起来像这样: enter image description here

或者: enter image description here

在第一个中,在main node上“右”将推进你完成程序,但在第二个中,只需按照每个节点上的next指针执行相同的操作。 / p>

似乎第二个更正确,因为您不需要像第一个节点那样具有可能极长的指针数组的特殊节点类型。虽然,当你进入for循环和if分支和更复杂的事情时,我可以看到第二个变得比第一个变得复杂。

5 个答案:

答案 0 :(得分:5)

第一种表示是更典型的表示,尽管第二种表示与树的构造兼容,作为递归数据结构,可以在实现平台功能而非命令时使用。

考虑:

enter image description here

这是你的第一个例子,除了缩短和“主”节点(一个概念性的稻草人)更恰当地命名为“块”,以反映命令式编程中包含一系列语句的“块”的公共构造语言。不同种类的节点具有不同种类的子节点,并且有时这些子节点包括其顺序很重要的辅助节点的集合,如“块”的情况。例如,数组初始化可能会产生同样的结果:

int[] arr = {1, 2}

考虑如何在语法树中表示它:

enter image description here

这里,array-literal-type节点还有多个相同类型的子节点,其顺序很重要。

答案 1 :(得分:4)

  

在第一个位置,“正确”   在主节点上将推进你   通过该计划,但在第二个   一个简单地跟随下一个指针   在每个节点上也会这样做。

     

似乎是第二个   更正确,因为你不需要   类似于特殊节点类型的东西   可能非常长   第一个指针数组   节点

我几乎总是喜欢第一种方法,我认为当你不需要维护指向下一个节点的指针时,你会发现构建AST要容易得多。

我认为通常更容易让所有对象都来自公共基类,类似于:

abstract class Expr { }

class Block : Expr
{
    Expr[] Statements { get; set; }
    public Block(Expr[] statements) { ... }
}

class Assign : Expr
{
    Var Variable { get; set; }
    Expr Expression { get; set; }
    public Assign(Var variable, Expr expression) { ... }
}

class Var : Expr
{
    string Name { get; set; }
    public Variable(string name) { ... }
}

class Int : Expr
{
    int Value { get; set; }
    public Int(int value) { ... }
}

产生的AST如下:

Expr program =
    new Block(new Expr[]
        {
            new Assign(new Var("a"), new Int(1)),
            new Assign(new Var("b"), new Int(2)),
            new Assign(new Var("c"), new Int(3)),
            new Assign(new Var("d"), new Int(4)),
            new Assign(new Var("e"), new Int(5)),
        });

答案 2 :(得分:1)

这取决于语言。在C中,您必须使用第一个表单来捕获块的概念,因为块具有可变范围:

{
    {
        int a = 1;
    }
    // a doesn't exist here
}

变量范围将是您称为“主节点”的属性。

答案 3 :(得分:1)

我认为你的第一个版本更有意义,原因有两个。

首先,第一个更清楚地演示了程序的“嵌套性”,并且也明确地实现为根树(这是树的通常概念)。

第二个也是更重要的原因是你的“主节点”实际上可能是一个“分支节点”(例如),它可能只是一个更大的AST中的另一个节点。通过这种方式,您可以以递归的方式查看AST,其中每个AST都是具有其他AST的节点。这使得第一个设计更简单,更通用,更均匀。

答案 4 :(得分:0)

建议:在处理树数据结构时,wheter是与编译器相关的AST或其他种类,总是使用单个“根”节点,它可以帮助您执行操作并获得更多控制:

class ASTTreeNode {
  bool isRoot() {...}

  string display() { ... }  
  // ...
}

void main ()
{
  ASTTreeNode MyRoot = new ASTTreeNode();

  // ...

  // prints the root node, plus each subnode recursively
  MyRoot.Show();
}

干杯。