22

C#でANTLRで生成されたASTを歩くためのチュートリアルを知っている人はいますか?私が見つけた最も近いものはこれですが、それはひどく役に立ちません。

私の目標は、作業中のドメイン固有言語に基づいて生成しているツリーをウォークスルーし、ツリーを使用して生成されたC#コードを出力することです。

Javaベースのチュートリアルも役立ちます-ANTLRASTをトラバースする方法の明確な例を提供するものなら何でも。

4

4 に答える 4

20

Manuel Abadiaの記事の最後にある例を採用することで、これを理解することができました。

これが私のバージョンです。これは、解析されたコードを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

以下の私の例の一部を見ることができます、またはあなたは完全なバージョンを閲覧することができます。


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;

      ....
    }
}
于 2009-05-29T19:06:09.693 に答える
8

通常、再帰を使用してASTをウォークし、ノードの種類に基づいてさまざまなアクションを実行します。ポリモーフィックツリーノード(つまり、ツリー内のノードごとに異なるサブクラス)を使用している場合は、Visitorパターンでのダブルディスパッチが適切な場合があります。ただし、これは通常、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 <predicate> <if-true> [<if-false>])、次のようになります。

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)

通常、現在のノードのタイプなどに応じて、すべてが再帰的に実行されます。

于 2009-05-20T13:29:32.307 に答える
1

TreeParserの作成を検討する必要があります。これにより、ツリーの解釈がはるかに簡単になります。

ANTLR 2.xについては、http: //www.antlr2.org/doc/sor.htmlを参照してください 。ANTLR3.xについては、 http ://www.antlr.org/wiki/display/ANTLR3/Tree+construction (javaベース)を参照してください。パーサーとツリーパーサーの例)

于 2009-05-20T14:17:07.420 に答える
0

私は似たようなことをしましたが(実際にはそうではありませんでした)、TreeParserに行き着きました。

また、ANTLRの本を購入することをお勧めします。私はそれがどのウェブリソースよりも価値があることを発見しました。それはすべての答えを持っているわけではないかもしれませんが、それは確かに基本に役立ちます。

于 2009-05-20T14:24:43.740 に答える