ANTLRとは

パーサジェネレータ。ANTLRメタ言語からLexer, Parser, Tree-Parserを生成させることができる。Javaで動く。ターゲット言語はC#,C++,Java,PythonANTLR 3.0は開発中で、ANTLRメタ言語の仕様が変わり、ターゲット言語にC,Objective C, Ruby, LISP, Perl6, PHP, Oberonが加わるが、リリースはまだ先になりそう。日本語の情報がほとんどない。

ANTLR Parser Generator
http://www.antlr.org/

ANTLRのインストール

環境

環境は以下の通りとする。

gijだとうまくいかなかったので、SunのサイトからJDKをダウンロードしてインストールしておく必要がある。

ビルド

以下の手順で行った。

$ cd antlr-2.7.7
$ ./configure --disable-csharp --disable-python
...
$ make
...

私が使用している環境ではC#とPaythonをインストールしていないので、上記のようにconfigureの実行時に無効にした。

テスト

以下の手順で行った。

$ cd examples
$ make
...

*.gファイルからパーサソースを生成し、コンパイルして実行する。途中でWarningは多く発生するが、エラーがなければビルドは問題なく終了したことが確認できる。

Getting Started with ANTLR

言語定義ファイルの作成

実際に文法を定義して、パーサを生成してみる。

以下のページを参考にした。

Getting Started with ANTLR
http://www.antlr.org/doc/getting-started.html

以下の内容を記述したファイルを作成する。仮にファイル名はt.gとする。

class P extends Parser;

startRule
    :   n:NAME
        {System.out.println("Hi there, "+n.getText());}
    ;

class L extends Lexer;

// one-or-more letters followed by a newline
NAME:   ( 'a'..'z'|'A'..'Z' )+ NEWLINE
    ;

NEWLINE
    :   '\r' '\n'   // DOS
    |   '\n'        // UNIX
    ;

環境変数CLASSPATHの設定

ANTLRを起動する前に、環境変数CLASSPATHを設定する。

$ export CLASSPATH=<install dir>/antlr-2.7.7/antlr.jar:$CLASSPATH

にはantlr-2.7.7をインストールしたパスを設定する。

ANTLRの起動

以下のコマンドを実行する。先ほど作成した言語定義ファイルは、カレントディレクトリにあるものとする。

$ java antlr.Tool t.g
ANTLR Parser Generator   Version 2.7.7 (2006-11-01)   1989-2005
$ ls
L.java  L.smap  P.java  P.smap  PTokenTypes.java  PTokenTypes.txt  t.g
$

パーサの起動プログラムを作成

以下のファイルを作成する。ファイル名をMain.javaとする。

import java.io.*;

class Main {
    public static void main(String[] args) {
        try {
            L lexer = new L(new DataInputStream(System.in));
            P parser = new P(lexer);
            parser.startRule();
        } catch(Exception e) {
            System.err.println("exception: "+e);
        }
    }
}

コンパイル

以下のようにコンパイルを実行する。

$ javac *.java
...

               ^^^^^^^^^^^^^^^^^^^^^^^
The import antlr.SemanticException is never used
----------
17 problems (17 warnings)$ ls
L.class  L.smap      Main.java  P.java  PTokenTypes.class  PTokenTypes.txt
L.java   Main.class  P.class    P.smap  PTokenTypes.java   t.g
$

Warningメッセージがたくさんでてくるが、とりあえずclassファイルは生成される。

実行

いよいよ実行してみる。入力待ちになっているので、"takane"と打ち込んでリターンキーを押すとメッセージが表示されて終了する。

$ java Main
takane
Hi there, takane

$

以下のようなメッセージが表示されるならば、CLASSPATHにカレントディレクトリを追加する。

$ java Main
Exception in thread "main" java.lang.NoClassDefFoundError: Main
   at gnu.java.lang.MainThread.run(libgcj.so.7rh)
Caused by: java.lang.ClassNotFoundException: Main not found in gnu.gcj.runtime.SystemClassLoader{urls=[file:.../antlr-2.7.7/antlr.jar], parent=gnu.gcj.runtime.ExtensionClassLoader{urls=[], parent=null}}
   at java.net.URLClassLoader.findClass(libgcj.so.7rh)
   at gnu.gcj.runtime.SystemClassLoader.findClass(libgcj.so.7rh)
   at java.lang.ClassLoader.loadClass(libgcj.so.7rh)
   at java.lang.ClassLoader.loadClass(libgcj.so.7rh)
   at gnu.java.lang.MainThread.run(libgcj.so.7rh)

環境変数CLASSPATHにカレントディレクトリを追加。

>

$ export CLASSPATH=.:$CLASSPATH
|

C言語パーサ

cgramのダウンロード

ANTLRのページhttp://www.antlr.org/ を色々と調べてみると、いくつかの文法ファイルをダウンロードできる。今回は、そのうちのC言語パーサ(cgram)のビルドを行う。環境設定については、すでに説明したものを利用する。

参考:http://d.hatena.ne.jp/takaneh/20070507#1178533339

以下のページからcgramをダウンロードする。

Files in diredtory /grammer/cgram
http://www.antlr.org/grammar/cgram

ダウンロードするファイルはcgram.tgz。

cgramのビルド

tgzファイルを展開してmakeする。

$ tar xvfz cgram.tgz
...
$ cd cgram
$ ls
announce.txt  examples/  grammars/  license.txt  readme.txt  tests/  todo.txt
$ cd grammars
$ ls
CSymbolTable.java  GnuCTreeParser.g              StdCParser.g
CToken.java        LineObject.java               TNode.java
GnuCEmitter.g      Makefile                      TNodeFactory.java
GnuCParser.g       PreprocessorInfoChannel.java
$ make
...
The serializable class TNode does not declare a static final serialVersionUID field of type long
----------
18 problems (1 error, 17 warnings)make: *** [StdCParser.class] Error 255
$

実はそのままではエラーが発生する。以下の箇所をコメントアウトするとよい。

StdCParser.g
    913 {
    914 //        import CToken;          // コメントアウト
    915         import java.io.*;
    916 //        import LineObject;      // コメントアウト
    917         import antlr.*;
    918 }

GnuCParser.g
    614 {
    615 //        import CToken;          // コメントアウト
    616         import java.io.*;
    617 //        import LineObject;      // コメントアウト
    618         import antlr.*;
    619 }

TNode.java
      9 //import CToken;                  // コメントアウト

上記の変更後、再びmakeを実行するとエラーが発生しないはず。ただし、相変わらずWarningは大量に出る。

実行

ディレクトリexamplesに移動し、CLASSPATHを設定してmakeする。
以下の例は、サンプルCプログラムを読み込み、出力する。

$ cd ../examples
$ export CLASSPATH=../grammars:$CLASSPATH
$ make
...
4 problems (4 warnings) $ ls
GNUCTokenTypes.txt@  Test.class  TestLex.class  TestThrough.class
Makefile             Test.java   TestLex.java   TestThrough.java
$ java Test ../tests/test.c
static int g1 = 0 ; 
typedef long mylong ; 
static mylong z ; 

typedef int Length ; 
Length len , maxlen ; 
Length * lengths [ ] ; 

typedef char * String ; 
String p , lineptr [ 100 ] , alloc ( int ) ; 
int strcmp ( String , String ) ; 

ちなみに元のファイルを見てみると、注釈行が削除されている。

$ cat ../tests/test.c
static int g1 = 0;
typedef long mylong;
static mylong z;

typedef int Length;
Length len, maxlen;
Length *lengths[];

typedef char *String;
String p, lineptr[100], alloc(int);
int strcmp(String, String);
/*
typedef struct tnode *Treeptr;

typedef struct tnode {
    char *word;
    int count;
    Treeptr left;
    Treeptr right;
} Treenode;

Treeptr talloc(void) {
    return (Treeptr) malloc(sizeof(Treenode));
}
           
int fun(int a, mylong b) {
    mylong c;
    return a+b;
}
*/

$