はじめてのANTLR

昨日、ちょっとふれたANTLRですが、

ANTLR, ANother Tool for Language Recognition, (formerly PCCTS) is a language tool that provides a framework for constructing recognizers, compilers, and translators from grammatical descriptions containing Java, C#, or C++ actions.

つまりlexとyaccを合わせたようなツールです。基本的にはJava向けっぽいのですが、少し前からC#もサポートされるよーになったみたいなので、ちょっと弄ってみました。

使い方は、

  1. 文法を用意する(.gファイル)
  2. ターゲットとなる言語向けのソースへ変換(Java/C++/C#)
  3. ターゲット言語でコンパイル

という流れになります。

では、サンプルとして簡単な文法ファイルを見てみます。

// test.g
header 
{
  using System.Text;
}

options
{
  language = "CSharp";
}

// Lexer
class IntAndIDLexer extends Lexer;

INT : ('0'..'9')+ 
  ;

ID  : ('a'..'z')+ 
  ;

COMMA 
  : ',' 
  ;

NEWLINE 
  : '\r' '\n'
  ;

// Parser
class SeriesParser extends Parser;

series
{ 
  int n = 1; 
}
  : element (COMMA element {++n;})* NEWLINE 
{ 
  Console.WriteLine("{0} elements.", n); 
}
  ;

element: INT | ID ;

・header
C#の場合は、using指定などを行います。

・options
ANTLRのデフォルト出力はJavaなので、C#を明示的に指定しています。

・IntAndIDLexer
Lexerから継承していることからわかるように字句解析クラスとなります。IntAndIDLexerがC#から見えるクラス名になります。
ここではINT(0〜9の羅列)とID(a〜zの羅列)、COMMA(コンマ)、NEWLINE(改行)を定義しています。

・SeriesParser
Parserから継承していますので、もちろん構文解析クラスです。seriesに対するルールを定義していますが、C#から見た場合seriesはSeriasParserのメソッドになっています。で、seriesですが、まず最初のブロックは宣言部になります。ここでは変数nを用意しています。続いてルールですが、

element (COMMA element {++n;})* NEWLINE

elementの後、,(コンマ) elementが0回以上繰り返して、改行となっています。また、{++n;}はアクションでマッチするたびに、++nが実行されます。最後のブロックは、ルールが受理された時に実行されるアクションで、ここでは要素の個数を表示しています。また、突然出てきたelementは

element: INT | ID;

からわかるように、INTもしくはIDとなっています。
次に文法ファイルをC#のコードへ変換します。

$ cantlr test.g

上記コマンドを実行すると、次のようなファイルが生成されます。

  • IntAndIDLexer.cs
  • IntAndIDLexerTokenTypes.cs
  • SeriesParser.cs

後は、これらを呼び出すコードを用意すればおしまいです。

using System;
using antlr;

class TheClass {
  public static void Main() {
    IntAndIDLexer lexer = new IntAndIDLexer(
        new ByteBuffer(Console.OpenStandardInput()));
    SeriesParser parser = new SeriesParser(lexer);
    parser.series();
  }
}

先ほどのファイルとこの呼び出し用コードを一緒にコンパイルします。

$ csc /t:exe /r:antlr.runtime.dll *.cs

これで完成。プログラムを実行し、数字や文字をコンマ区切りで入力して改行すると要素数が表示されるはずです。