【问题标题】:Tutorial for walking ANTLR ASTs in C#?在 C# 中行走 ANTLR AST 的教程?
【发布时间】:2009-05-20 10:30:40
【问题描述】:

有人知道在 C# 中遍历 ANTLR 生成的 AST 的教程吗?我能找到的最接近的是this,但它并不是很有帮助。

我的目标是遍历基于我正在研究的领域特定语言生成的树,并使用这些树来输出生成的 C# 代码。

基于 Java 的教程也会有所帮助 - 任何提供如何遍历 ANTLR AST 的清晰示例的任何内容。

【问题讨论】:

    标签: c# java parsing antlr


    【解决方案1】:

    我设法通过修改Manuel Abadia's article 末尾的示例来解决这个问题。

    这是我的版本,我碰巧用它来将解析的代码转换为 C#。 这些是步骤:

    1. 使用您的输入实例化 ANTLRStringStream 或子类(可以是文件或字符串)。
    2. 实例化您生成的词法分析器,并传入该字符串流。
    3. 使用词法分析器实例化令牌流。
    4. 使用该令牌流实例化您的解析器。
    5. 从解析器中获取顶级值,并将其转换为CommonTree
    6. 遍历树:

    要获取节点的文字文本,请使用node.Text。 要获取节点的令牌名称,请使用node.Token.Text

    请注意,node.Token.Text 只会在它是没有相应字符串的虚构令牌时为您提供令牌的实际名称。如果它是一个真正的令牌,那么node.Token.Text 将返回它的字符串。

    例如,如果您的语法中有以下内容:

    tokens { PROGRAM, FUNCDEC }
    
    EQUALS : '==';
    ASSIGN : '=';
    

    然后你会从node.Token.Text的对应访问中得到"PROGRAM""FUNCDEC""==""="

    您可以在下面查看我的部分示例,也可以浏览full version


    public static string Convert(string input)
    {
        ANTLRStringStream sStream = new ANTLRStringStream(input);
        MyGrammarLexer lexer = new MyGrammarLexer(sStream);
    
        CommonTokenStream tStream = new CommonTokenStream(lexer);
    
        MyGrammarParser parser = new MyGrammarParser (tStream);
        MyGrammarParser.program_return parserResult = parser.program();
    
        CommonTree ast = (CommonTree)parserResult.Tree;
    
        Print(ast);
        string output = header + body + footer;
    
        return output;
    }
    
    public static void PrintChildren(CT ast)
    {
        PrintChildren(ast, " ", true);
    }
    
    public static void PrintChildren(CT ast, string delim, bool final)
    {
        if (ast.Children == null)
        {
            return;
        }
    
        int num = ast.Children.Count;
    
        for (int i = 0; i < num; ++i)
        {
            CT d = (CT)(ast.Children[i]);
            Print(d);
            if (final || i < num - 1)
            {
                body += delim;
            }
        }
    }
    
    public static void Print(CommonTree ast)
    {
        switch (ast.Token.Text)
        {
            case "PROGRAM":
                //body += header;
                PrintChildren(ast);
                //body += footer;
                break;
            case "GLOBALS":
                body += "\r\n\r\n// GLOBALS\r\n";
                PrintChildren(ast);
                break;
            case "GLOBAL":
                body += "public static ";
                PrintChildren(ast);
                body += ";\r\n";
                break;
    
          ....
        }
    }
    

    【讨论】:

      【解决方案2】:

      通常,您使用递归遍历 AST,并根据节点的类型执行不同的操作。如果您使用多态树节点(即树中不同节点的不同子类),则访问者模式中的双重调度可能是合适的;但是,这对于 Antlr 来说通常不是很方便。

      在伪代码中,行走通常看起来像这样:

      func processTree(t)
          case t.Type of
              FOO: processFoo t
              BAR: processBar t
          end
      
      // a post-order process
      func processFoo(foo)
          // visit children
          for (i = 0; i < foo.ChildCount; ++i)
              processTree(foo.GetChild(i))
          // visit node
          do_stuff(foo.getText())
      
      // a pre-order process
      func processBoo(bar)
          // visit node
          do_stuff(bar.getText())
          // visit children
          for (i = 0; i < foo.ChildCount; ++i)
              processTree(foo.GetChild(i))
      

      处理的种类高度依赖于语言的语义。例如,在为 JVM 或 CLR 等堆栈机器生成代码时,处理具有 (IF &lt;predicate&gt; &lt;if-true&gt; [&lt;if-false&gt;]) 结构的 IF 语句可能看起来像这样:

      func processIf(n)
          predicate = n.GetChild(0)
          processExpr(predicate) // get predicate value on stack
          falseLabel = createLabel()
          genCode(JUMP_IF_FALSE, falseLabel) // JUMP_IF_FALSE is called brfalse in CLR,
                                             // ifeq in JVM
          if_true = n.GetChild(1)
          processStmt(if_true)
          if_false = n.ChildCount > 2 ? n.GetChild(2) : null
          if (if_false != null)
              doneLabel = createLabel()
              genCode(JUMP, doneLabel)
          markLabel(falseLabel)
          if (if_false != null)
              processStmt(if_false) // if-false branch
              markLabel(doneLabel)
      

      一般情况下,一切都是根据当前节点的类型等递归完成的。

      【讨论】:

        【解决方案3】:

        您应该考虑编写 TreeParser;它可以使解释树的工作变得更加简单。

        对于 ANTLR 2.x,请参阅 http://www.antlr2.org/doc/sor.html 对于 ANTLR 3.x,请参阅 http://www.antlr.org/wiki/display/ANTLR3/Tree+construction(基于 java 的解析器和树解析器示例)

        【讨论】:

          【解决方案4】:

          我做了类似的事情(但不是真的),最终得到了一个 TreeParser。

          我还建议购买 ANTLR 书。我发现它比任何网络资源都更有价值。它可能没有所有的答案,但它肯定有助于基础知识。

          【讨论】:

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