2009-05-20 45 views
21

有没有人知道在C#中使用ANTLR生成的AST的教程?我能找到的最接近的是this,但这不是非常有帮助。教程在C#中走ANTLR ASTs?

我的目标是根据我正在处理的领域特定语言生成树,并使用这些树来输出生成的C#代码。

基于Java的教程也会有所帮助 - 任何可以提供有关如何遍历ANTLR ASTs的清晰示例的东西都可以。

回答

19

我设法通过修改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 : '='; 

然后你会得到"PROGRAM""FUNCDEC""==",并且"="node.Token.Text相应的访问。

你可以在下面看到我的例子的一部分,或者你可以浏览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; 

     .... 
    } 
} 
8

正常情况下,您可以使用递归遍历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)) 

该种处理的是高度依赖于语言的语义。例如,处理一个IF声明,结构(IF <predicate> <if-true> [<if-false>]),对堆栈机,如JVM或CLR生成代码时,可能看起来有点像这样:

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) 

一般来说一切都完成递归根据当前的类型节点等

+0

树语法的为C#任何示例代码? – Kiquenet 2010-08-24 08:34:01

0

我没有类似的东西(但不是真的),我结束了一个TreeParser。

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