JRubyでもtecsgenのテストケースが通るのを確認しました

raccのparser.rbへのとりあえずのパッチ作成に載せた試作パッチを、Linux(Fedora 7)とcgywin(1.5.25)上でJRubyで実行し、tecsgenの配布物に含まれるテストケースが通ることを確認しました。

Javaは、JDK 1.6 update14、JRubyは1.3.1を使用しました。

ただし、それぞれの箇所でいささか工夫が必要でした。

*Linux
tecsgenがCのヘッダファイルを読み込む(typedefで定義された型名や構造体タグ名を認識するため)時に、C処理系の独自の予約語が存在すると、tecsgenはそれを認識で来ません。
これによるパース失敗を避けるため、独自の予約語を読み込み前に削除しています。
具体的にはtecsgenの内部でCプリプロセッサ(あるいはC処理系のCプリプロセス処理機能)を呼び出し、空文字列に置換してから、ヘッダファイルを読み込みます。
具体的にはtecsgenのコマンドラインオプション-Dで空文字列に置き換える文字を指定します。
ただし、この指定が”-D__extension__”とされていたため、Linuxのgcc(4.1.3)では”1″に置き換えられて、コンパイルエラーになりました。
#cygwinではこの指定でも問題が起きませんでした。
#現在公開されているtecsgenは、主にcygwinで動作確認されています。

Linuxでは”-D__extension__=”とする必要がありました。

*cygwin
cygwinでJRubyを実行する場合、環境変数RUBYLIBへのパス名の設定方法に工夫が必要でした。
この工夫は、Rubyの場合は必要ありませんでした。

cygwinでフルパスで指定する場合、/cygdrive/c/のように
ドライブ名を/cygdrive/ドライブ名という指定をすることが
可能です。
しかし、この形式で指定した場合、rubyのスクリプトで
クラスFileのexpand_pathメソッドでフルパスに変換した
場合、
C:/cygdrive/c
と、先頭にドライブ名をつけたにも関わらず、/cygdrive/c
もつけたままであるため、存在しないパスになってしまいました。
これは、cygwinの標準のrubyとか、cygwin中から呼び出した
mswin32版rubyでは、経験しなかったことです。
どこが原因かまでは調べていませんが、cygwin上で、JRubyを
使ってtecsgen.rbを実行して、”ファイルが見つからない”
というようなエラーが発生した場合は、環境変数RUBYLIBの値を
確認してください。
もし、”/cygdrive/c”のような形式であれば、この部分をとり
さったパス名を指定してください。
以下をコマンドラインで実行してもよいと思います。

export RUBYLIB=`echo $PWD | sed ‘s/^\/cygdrive\/.//’`

tecsgen の Exerb版, Jruby 版の障害について

TECS 開発ブログの記事「tecsgen の Exerb版, Jruby 版の障害 (06/21)」、「tecsgen の Exerb版, Jruby 版の障害(続き) (06/24) 」 において、TECSジェネレータの障害が報告されています.

TECSジェネレータとは、TECSにおいてコンポーネントの定義、組み合わせを指定したファイルであるCDLファイル(拡張子が「.cdl」であるテキストファイル)から、コンポーネントを実現する部分のCソースコードを自動生成します.また、コンポーネントが提供する機能(これは個々のコンポーネントの作成者がコーディングする必要があります)のCソースの雛形も生成します.

このTECSジェネレータは、Rubyで書かれています。

より具体的に言うと、CDL(Component Description Language)という言語をパースするためにRubyでかかれたパーザジェネレータRaccを用いています.

これがJavaで実装されたRuby、JRubyで期待した動作をしない(Out Of Memoryが発生する)という報告があったことが発端でした。

この問題に対して、私も調べて見ました。

今回は、Linux(Fedora 7)で行いました。

1.まず、問題を再現させるために、Rubyの標準ライブラリのRaccそのままではなく、一部手を加えたものを用いま す。

cp /usr/lib/ruby/1.8/racc/parser.rb ~/lib/ruby/racc

export RUBYLIB=~/lib/ruby/racc:$RUBYLIB

~/lib/ruby/racc/parser.rbの40行め、モジュールRaccの中のクラスParserに対する機能追加の部分で、以下が四杯するように変更する

require ‘racc/cparse’ -> require ‘xracc/cparse’

2.Raccのデバッグ機能を用いるため、デバッグ用の出力を行う、デバッグ用パーザを生成します。

TECSジェネレータのMakefileにはデバッグ用パーザを生成する定義がありますが、通常のターゲットでは生成されません。そのため、以下のようにターゲットを指定します。

make yydebug

3.問題が発生するテストケースのCDLファイルが存在するディレクトリに移動し、TECSジェネレータ(tecsgen)のオプション-yを指定して実行する。

tecsgen -y error-7.idl  2>&1 | tee  error-7-2.txt

オプション-yはTECSジェネレータにRaccのデバッグ用パーザを読み込むことを指定します。

また、デバッグがしやすいように、~/lib/ruby/racc/parser.rbにデバッグ用出力を追加しておきます.

このようしてTECSジェネレータを実行させて見ました.

結果は以下のとおりです.

1) tok=CELLTYPE
[ 0 45 ]
2) tok=IDENTIFIER
[ 0 45 101 ]
reduce  IDENTIFIER(2) –> celltype_name(214)
[ 0 45 102 ]
3) tok={
[ 0 45 102 149 ]
4) tok=}
[ 0 45 102 149 214 ]
reduce  “}”(76) –> celltype_statement(219)
[ 0 45 102 149 229 ]
reduce  celltype_statement(219) –> specified_celltype_statement(218)
[ 0 45 102 149 228 ]
reduce  specified_celltype_statement(218) –> celltype_statement_list(215)
[ 0 45 102 149 223 ]
6) tok=;
[ 0 45 102 149 223 214 ]
7) tok=nil
[ 0 45 102 149 223 214 ]

(これ以降、以下の部分の繰り返し)

reduce  $end(0) –> celltype_statement(219)
[ 0 45 102 149 223 229 ]
reduce  celltype_statement(219) –> specified_celltype_statement(218)
[ 0 45 102 149 223 347 ]
reduce  celltype_statement_list(215) specified_celltype_statement(218) –> celltype_statement_list(215)
[ 0 45 102 149 223 ]
shift   $end
[ 0 45 102 149 223 214 ]
error-7.cdlは文法エラーの場合のテストケースです。

本来トークン3とトークン4の間にセルタイプの定義が存在すべきですが、それがない場合のテストをするものです。

ですから、トークン3の次にトークン4が来た時点で構文エラーになるべきはずです。

しかし、逆にトークン4をセルタイプの定義と(パーザが)判断しているため、その後のトークン(また、ファイルの終わりに到達しても)が来ても無限ルールに陥ってしまっているのではないでしょうか。

racc/parserとrac/cparseの違いは、まだ調べていません。

同じ文法テーブルを使用しているなら、racc/cparseでも無限ルールに陥っても不思議ではありません.

現段階の仮説の一つは、racc/cparseが(パーザの)入力の終わりを特別扱いして、処理を打ちきっているのではないかいうことです。

でもこの仮説が真の場合でも、racc/cparseの方がが予期せぬ入力に対して柔軟な対応をしてくれるということでしかありません。

私の見る限り、racc/parserは、文法テーブルの指定通りに動作してると思います。