java 在C#中运行ANTLR AST的教程?

nvbavucw  于 2023-05-15  发布在  Java
关注(0)|答案(4)|浏览(185)

有人知道在C#中使用ANTLR生成的AST的教程吗?我能找到的最接近的是this,但它不是很有帮助。
我的目标是遍历基于我正在研究的领域特定语言生成的树,并使用树输出生成的C#代码。
基于Java的教程也会很有帮助--任何提供了如何遍历ANTLRAST的清晰示例的教程。

oyxsuwqo

oyxsuwqo1#

我通过修改Manuel Abadia's article结尾的例子来解决这个问题。
这是我的版本,我碰巧正在使用它来将解析后的代码转换为C#。具体步骤如下:
1.用你的输入示例化一个ANTLRStringStream或子类(可以是文件或字符串)。
1.示例化生成的lexer,传入该字符串流。
1.使用lexer示例化一个令牌流。
1.使用该标记流示例化解析器。
1.从解析器获取顶级值,并将其转换为CommonTree
1.遍历树:
要获取节点的文本,请使用node.Text。要获取节点的令牌名称,请使用node.Token.Text
请注意,node.Token.Text只会在没有对应字符串的虚构令牌时给予令牌的实际名称。如果它是一个真实的的令牌,那么node.Token.Text将返回它的字符串。
例如,如果您的语法中有以下内容:

tokens { PROGRAM, FUNCDEC }

EQUALS : '==';
ASSIGN : '=';

然后,您将从node.Token.Text的相应访问中获得"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;

      ....
    }
}
cvxl0en2

cvxl0en22#

通常,您使用递归遍历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 <predicate> <if-true> [<if-false>])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)

一般来说,一切都是递归完成的,这取决于当前节点的类型等。

pb3s4cty

pb3s4cty3#

你应该考虑写一个TreeParser;它可以使解释树的工作简单得多。
对于ANTLR 2.x,请参见http://www.antlr2.org/doc/sor.html对于ANTLR 3.x,请参见http://www.antlr.org/wiki/display/ANTLR3/Tree+construction(基于Java的解析器和树解析器示例)

kuarbcqp

kuarbcqp4#

我做了类似的事情(但不是真的),最后得到了一个TreeParser。
我也建议买ANTLR的书。它比任何网络资源都更有价值。它可能没有所有的答案,但它肯定有助于与基础。

相关问题