【问题标题】:Abstract syntax tree construction and traversal抽象语法树的构造和遍历
【发布时间】:2011-09-03 14:40:44
【问题描述】:

我不清楚抽象语法树的结构。要在 AST 所代表的程序源中“向下(前进)”,您是直接在最顶部的节点上,还是向下?例如,示例程序是否会

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

生成如下所示的 AST:

或者这个:

在第一个中,在 main node 上“向右”移动将引导您完成程序,但在第二个中,只需跟随每个节点上的 next 指针即可。

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

    • 对不起,我不懂“二进制主节点”,从你的 Lisp 示例中我也无法真正理解(可能是因为我从未使用过 Lisp)。你能详细说明一下吗?
    • @Seth:那就不要介意类似 Lisp 的例子。我已经发布了一个类似 C 的示例,说明为什么需要第一个表单。
    • 所以你必须使用一个带有 n 个子节点的节点作为根节点(保存主程序和东西),还有什么?我不知道 AST 被用来显示块范围或其他什么。
    • @Seth:AST 代表程序的句法结构。句法/词法范围是其中的一部分。
    【解决方案2】:

    建议:在处理树形数据结构时,无论是编译器相关的AST还是其他类型,始终使用单个“根”节点,它可以帮助您执行操作并拥有更多控制权:

    class ASTTreeNode {
      bool isRoot() {...}
    
      string display() { ... }  
      // ...
    }
    
    void main ()
    {
      ASTTreeNode MyRoot = new ASTTreeNode();
    
      // ...
    
      // prints the root node, plus each subnode recursively
      MyRoot.Show();
    }
    

    干杯。

    【讨论】:

      【解决方案3】:

      出于几个原因,我相信您的第一个版本更有意义。

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

      第二个也是更重要的原因是您的“主节点”实际上可能是一个“分支节点”(例如),它可以只是更大 AST 中的另一个节点。这样,您的 AST 可以在递归意义上查看,其中每个 AST 都是一个节点,其他 AST 作为它的子节点。这使得第一个的设计更简单、更通用且非常均匀。

      【讨论】:

        【解决方案4】:

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

        考虑:

        这是您的第一个示例,除了缩短并且使用“主”节点(概念上的稻草人)更恰当地命名为“块”,以反映命令式编程中包含一系列语句的“块”的常见构造语言。不同种类的节点有不同种类的子节点,有时这些子节点包括顺序很重要的子节点的集合,例如“块”。例如,数组初始化也可能出现同样的情况:

        int[] arr = {1, 2}
        

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

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

        【讨论】:

          【解决方案5】:

          在第一个中,“正确” 在主节点上会推进你 通过程序,但在第二 一个简单地跟随下一个指针 在每个节点上都会做同样的事情。

          好像是第二个 更正确,因为你不需要 类似于特殊节点类型的东西 具有潜在的极长 第一个指针数组 节点

          我几乎总是更喜欢第一种方法,而且我认为当您不需要维护指向下一个节点的指针时,您会发现构建 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)),
                  });
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多