在C#中使用ANTLR ASTs的教程?

是否有人知道在C#中使用ANTLR生成的AST的教程? 我能找到的最接近的是这个 ,但它并不是非常有用。

我的目标是根据我正在使用的特定于域的语言遍历我正在生成的树,并使用树来输出生成的C#代码。

基于Java的教程也很有帮助 – 任何提供如何遍历ANTLR AST的明确示例的东西。

我设法通过调整曼努埃尔阿巴迪亚文章末尾的例子来解决这个问题。

这是我的版本,我碰巧用它将解析后的代码转换为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 : '='; 

然后,您将从"FUNCDEC"的相应访问中获得"PROGRAM""FUNCDEC""==""="

您可以在下面看到我的部分示例,或者您可以浏览完整版本 。


 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; .... } } 

通常,您使用递归来执行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

[])处理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) 

通常,一切都是递归完成的,具体取决于当前节点的类型等。

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

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

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

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