ANTLRで生成したパーサーで構文解析

前回の続きです。


JavaParser.csとJavaLexer.csを使ってみましょう。
まずはVisualStudioでC#のコンソールアプリケーションプロジェクトを作成してください。


JavaParser.csと同じ階層に作成するとパーサを作り直したときeclipseに消されてしまうので気をつけてください。
今回はJavaParser.csのある階層に1つディレクトリを作りその中にプロジェクトを作成しました。


パースするにもANTLRのランタイムが必要なので以下のページのDOT-NET-runtime-3.1.3.zipをダウンロードしてください。
http://www.antlr.org/download/CSharp
zipを解凍しておきます。


次にこれをC#プロジェクトに追加します。
ソリューションエクスプローラの参照設定を右クリックし、参照の追加でzipに含まれていたすべてのdllを選択し追加してください。
Antlr3.Runtime.dll Antlr3.Utility.dll antlr.runtime.dll StringTemplate.dll


JavaParser.cs JavaLexer.csも追加します。
このとき次の手順を踏むとソースをコピーせずプロジェクトに追加できます。
プロジェクトを右クリックし[追加]、[既存の項目]と進み、先の2つの.csを選択し、追加ボタンの右の三角形をクリックし、リンクとして追加を選択します。
ソリューションエクスプローラ上で2つのファイルのアイコンに矢印が付いていればコピーでなく参照として追加されたことがわかります。



ここでパースするコードを書こうと思っていたのですが、いくつかコンパイルエラーになってしまいます。
.gファイルを修正しましょう。

上の方にある@lexer::membersを以下のように修正してください

@lexer::members{
  protected bool enumIsKeyword = true;
  protected bool assertIsKeyword = true;
}

これはJavaLexer.csクラスのメンバとしてそのまま出力されます。
ですのでjavaの組み込み型であるbooleanでなくC#の組み込み型であるboolへの直しておきましょう。


続いて、Treeの実装と.gに書かれたTreeを使ったコードがかみ合わずエラーとなるので修正します。

$t1.getLine()や$t2.getLine()などは$t1.Line $t2.Lineとします。
C#ではLineがメソッドでなくプロパティとして提供されています。
同様にgetCharPositionInLine()はCharPositionInLineに直します。


最後にパースするコードを書きましょう。

//ファイルの読み込み
string filename = @"test.java";
Antlr.Runtime.ANTLRFileStream fs = new Antlr.Runtime.ANTLRFileStream(filename);

もしくは

Antlr.Runtime.ANTLRStringStream fs = new Antlr.Runtime.ANTLRStringStream(@"
class Main{
    public static void Main(String[] arg){
        int test = 0;
        System.out.println(test);
    }
}
");

として直接ソースを書きます。
ANTLRFileStreamもANTLRStringStreamもICharStreamのサブクラスですのでどちらでもかまいません。

残りのコードは以下のようになります。

JavaLexer lex = new JavaLexer(fs);
Antlr.Runtime.CommonTokenStream tokens = new Antlr.Runtime.CommonTokenStream(lex);
JavaParser parse = new JavaParser(tokens);
parse.compilationUnit();

ここでcompilationUnit()は構文のルールの中でももっとも最上位の物です。
このメソッドの戻り値から構文木を取得することが出来ます。

ここで意味でなく構文が無効となるソースを書いてどのようになるか試してみましょう。
たとえばclassをclasとすると構文解析に失敗します。
line 2:0 no viable alternative at input 'clas'
とコンソールに出力されると思います。

問題なく構文解析された場合はコンソールへの出力がありません。


今回は構文解析のみですのでここで終わりです。
コンパイラなどの作成には構文木を使用するようです。その辺はおいおい勉強して行こうと思います。