cgramの出力チェック(1)
cgramは実質C to Cのコンバータなので、入力Cプログラムと出力Cプログラムで比較することによって、検証を行う。実際にサンプルCプログラムを作成して比較してみる。
元のCプログラム
static int g1 = 0;
typedef long mylong;
static mylong z;
typedef char *String;
String p, lineptr[100], alloc(int);
int strcmp(String, String);
/*
コメント
*/
int a; /* コメント */
int b; /*
コメント
*/ int c;
enum hoge { AAA, BBB, CCC};
enum aho {
DDD,
EEE,
FFF,
};cgramによる出力
static int g1 = 0 ;
typedef long mylong ;
static mylong z ;
typedef char * String ;
String p , lineptr [ 100 ] , alloc ( int ) ;
int strcmp ( String , String ) ;
int a ;
int b ;
int c ;
enum hoge { AAA , BBB , CCC } ;
enum aho {
DDD ,
EEE ,
FFF
} ; このことから、出力側のCプログラムには以下のことが言えそうだ。
- 文法的に問題のない範囲でスペースが挿入される
- 注釈行は削除される
- {}内の不要な最後の","を省略する
したがって、単純にdiffをとるだけでは無理そうだ。そこで、このテスト専用の期待値比較プログラムを作ってみる。次回に続く。
cgramのテスト
ANTLRのC言語定義ファイルであるcgramをテストしてみる。
テストデータ
cgram/testsでmakeを実行すると、エラーになる。
$ make
java -classpath .:..:../examples:../grammars Test test.c
Exception in thread "main" java.lang.NoClassDefFoundError: Test
at java.lang.Class.initializeClass(libgcj.so.7rh)
Caused by: java.lang.ClassNotFoundException: antlr.TokenStream not found in gnu.gcj.runtime.SystemClassLoader{urls=[file:./,file:../,file:../examples/,file:../grammars/], 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 java.lang.Class.initializeClass(libgcj.so.7rh)
make: *** [test] エラー 1
$原因は、-classpathにantlr.jarが入っていないからである。環境変数CLASSPATHにantlr.jarを加えても、動作しない。
ただし、環境変数CLASSPATHにantlr.jarを加え、以下のように必要なパスを追加すると、makeを使わずに動作させることができる。
$ export CLASSPATH=.:../examples:../grammars:$CLASSPATH $ java Test 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 ) ; $
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;
}
*/
$
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の起動
以下のコマンドを実行する。先ほど作成した言語定義ファイルは、カレントディレクトリにあるものとする。
$ 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)
| > |
ANTLRのインストール
環境
環境は以下の通りとする。
- Fedora Core 6
- bash
- JDK 1.6.0_01
- ターゲット言語はJavaとC++
gijだとうまくいかなかったので、SunのサイトからJDKをダウンロードしてインストールしておく必要がある。
ビルド
以下の手順で行った。
$ cd antlr-2.7.7 $ ./configure --disable-csharp --disable-python ... $ make ...
私が使用している環境ではC#とPaythonをインストールしていないので、上記のようにconfigureの実行時に無効にした。
テスト
以下の手順で行った。
$ cd examples $ make ...
*.gファイルからパーサソースを生成し、コンパイルして実行する。途中でWarningは多く発生するが、エラーがなければビルドは問題なく終了したことが確認できる。
はじめまして
とりあえず登録してみました。主に個人的な技術メモを中心に使用していく予定です。