KEIS BLOGは株式会社ケイズ・ソフトウェアが運営しています。

KEIS BLOG

Introduction to Antlr!


shimoda01

こんにちは。最近、 Airbnb を使って東京近郊の家に泊まって開発合宿なんて面白そうって思って何も行動を起こしていない下田です。アイデアはいいと思うんですけどねぇ。ところで、今回は Antlr について説明したいと思います。開発者は University of San Francisco の教授で意外と気さくそうな人ですが(ビデオ見る限り)完全に空気ですね。面白いライブラリですが、 Antlr を使う現場はそうないと思います。でも、実は、 Antlr でどういったことが出来るのか知ると意外と現場で使える場面はあると思います。Antlr を簡単にいえば、正規表現の組み合わせてグループを作ってさらにそれを階層化できて、そのグループ単位に処理が書けます。基本的には、オレオレ言語を作るときに用いられますが、例えば、特定のルールのフォーマットのテキストを読み込んで処理をするといったタスクを手軽に処理することが出来ます。TSV(タブ区切り)などは、そこまで複雑なフォーマットでないため、 Antlr を使う必要はありませんが、もう少し高度な場合はまさに Antlr が使えます。では、どんな感じか見て行きましょう。

$ wget http://www.antlr.org/download/antlr-4.5.1-complete.jar
$ alias antlr4='java -cp "./*" org.antlr.v4.Tool'
$ alias grun='java -cp "./:./*" org.antlr.v4.gui.TestRig'

そして、 Hello.g4 というファイルを作っていきます。

grammar Hello;
r : 'hello' ID ;
ID : [a-z]+ ;
WS : [ \t\r\n]+ -> skip ;

正規表現が分かる方ならなんとなくつかめると思います。一番最初の ‘r’ のグループから始まります。 ‘hello’ の固定文字の後、 ID のグループが始まる感じです。これで、グループと階層を表現していきます。コンパイルして実際に表示してみます。

antlr Hello.g4
javac -cp antlr-4.5.1-complete.jar *.java

うまくいくとこんな感じで沢山のファイルが出来ると思います。

antlr-4.5.1-complete.jar
Hello.g4
HelloLexer.java
HelloLexer.tokens
HelloParser.java
Hello.tokens
HelloListener.java
HelloBaseListener.java
HelloBaseListener.class
HelloListener.class
HelloParser$RContext.class
HelloParser.class
HelloLexer.class

次に、’hello world’ と入力してどう表示されるか見ていきます。

$ grun Hello r -gui
hello world
^D

shimoda02

さきほど、 ‘r’ のグループの後に ID のグループが始まると言いましたが、実は大文字で定義されたグループは、 ‘hello’ の固定文字と同様の扱いになるため、グループ化されません。なので、 ‘hello’ も同様に以下のように記述することが出来ます。

grammar Hello;
r : HELLO ID ;
HELLO : 'hello' ;
ID : [a-z]+ ;
WS : [ \t\r\n]+ -> skip ;

では、以下のように定義するとどうなるでしょう。

grammar Hello;
r : hello_keyword ID ;
hello_keyword : 'hello' ;
ID : [a-z]+ ;
WS : [ \t\r\n]+ -> skip ;

shimoda03

想定通りですね。ちなみに、 ‘WS’ グループは、おまじないです。Python や 固定長ファイルを処理する以外のケースで、空白の数で処理が左右されることはないので、空白は無視するようにしています。なんとなくどう動作しているかつかめたのではないでしょうか。
ちなみに、 CSV を処理するようなタスクをやったことがある方はかなりいると思います。でも実は、 CSV の処理ってスクラッチから書くと難しいですよね。例えば、 Excel から出力した CSV も処理しなきゃいけなくなると途端にコード量が増えます。しかし、そんな時に、 Antlr が使いこなせれば、 Parser がたった数行で書けるんです。素晴らしい世の中になりましたね。これで、 Mr. Parr に足を向けて寝れなくなりますね。

grammar CSV;

file: hdr row+ ;
hdr : row ;
row : field (',' field)* '\r'? '\n' ;

field
    : TEXT
    | STRING
    |
    ;

TEXT   : ~[,\n\r"]+ ;
STRING : '"' ('""'|~'"')* '"' ; // quote-quote is an escaped quote

では、どのように定義されているか見て行きましょう。まずは、’file’ がエントリーポイントです。そこから、ヘッダーの定義の後に、実データが続く構成となっています。実際、ヘッダーも実データとフォーマットが同等なので、 hdr : row となっているように別名定義されているだけです。行データですが、 ‘field’ グループがカンマ区切りで続き、最後に改行コードが定義されています。こちらの定義であれば、 Mac/Linux/Windows の環境のファイルをサポートできます。通常の CSV であれば、ダブルクォートを扱うことがないので問題無いですが、 Excel などで出力すると、ダブルクォートが多用されますが、そういった場合でも、簡単な正規表現で定義できます。このように単純な正規表現で再帰が出来るのが、 Antlr の強みでもあります。

いかがだったでしょうか。簡単な正規表現の組み合わせで構造化することができました。次回はもう少し複雑な文法を処理していきたいと思います。

【関連記事】
Splunkに株価を取り込んでみた ft. Fujikawa