ANTLRで生成されたASTをC#でウォーキングするためのチュートリアルを知っている人はいますか?私が見つけた最も近いものはthisですが、それはあまり役に立ちません。C#でANTLR ASTを歩くチュートリアル?
私の目標は、私が作業しているドメイン固有の言語に基づいて生成しているツリーを歩き回り、生成されたC#コードを出力するためにツリーを使用することです。
ANTLR ASTをトラバースする方法の明確な例を提供するJavaベースのチュートリアルも役立ちます。
ANTLRで生成されたASTをC#でウォーキングするためのチュートリアルを知っている人はいますか?私が見つけた最も近いものはthisですが、それはあまり役に立ちません。C#でANTLR ASTを歩くチュートリアル?
私の目標は、私が作業しているドメイン固有の言語に基づいて生成しているツリーを歩き回り、生成されたC#コードを出力するためにツリーを使用することです。
ANTLR ASTをトラバースする方法の明確な例を提供するJavaベースのチュートリアルも役立ちます。
Manuel Abadia's articleの最後に例を適用してこれを把握することができました。
ここに私のバージョンがあります。これは、解析されたコードをC#に変換するために使用されています。 これらは、手順は次のとおりです。
CommonTree
に変換します。ツリートラバースノードのリテラル文字列を取得するには、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;
....
}
}
通常、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 <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)
することは一般的にすべてが、現在のタイプに応じて再帰的に行われていますノードなど
あなたはTreeParserの記述に目を通すべきです。ツリーをはるかに簡単に解釈する作業を行うことができます。 ANTLR 2.Xについて
ANTLR 3.xのためにhttp://www.antlr2.org/doc/sor.html はhttp://www.antlr.org/wiki/display/ANTLR3/Tree+construction(Javaベースのパーサーとツリーパーサの一例)を参照してください参照
私は(実際にはありません)似た何かをしたと私はTreeParserになってしまいました。
また、ANTLR本を購入することをお勧めします。私はそれがどんなWebリソースよりも貴重であることが分かった。それはすべての答えを持っていないかもしれませんが、それは確かに基本的に役立ちます。
C#のための木文法のいずれかのサンプルコード? – Kiquenet