raccのエラー回復モードは頑張りすぎ?

昨日の続きで、TECSジェネレータでの文法定義と、CDLファイルでのエラーになる書き方を調べています。

今回も途中報告です。

前回に書いたerror-7.cdlは、以下の文法の記述に誤りがある場合のテスト-ケースでした.

文法定義

celltype
: CELLTYPE celltype_name ‘{‘ celltype_statement_list ‘}’ ‘;’

error-7.cdlの内容

celltype TaskMain {
/* attributeの定義が空でならsyntax error */
/*    attr {

x = 0;

};
*/
};

これはコメントを無視すると、以下と同等です。

celltyope TaskMain {};

文法定義と比較すると、celltype_statement_listという要素が不足しているため、文法エラーとなります。

実際、raccの生成したパーザはこの文法エラーを認識して、on_error()を呼び出しています.

ここで、on_error()とは、raccを用いてパーザを作成する人が、エラー処理を行うために自由に定義できるメソッドです。

on_error()でパース自体を中止することもできます。

また中止せずに、raccが生成したパーザにエラー回復モードで処理を続行させることができます。

エラー回復モードは、文法エラーが見つかった箇所を、エラーだとは認識しつつも、そこに正しい記述があったものとみなして、パース処理をつづけることです。

今回の文法定義の場合を例にとると、celltype_statement_listが存在するべきところに、「}」があったため、エラーとなります.

この後エラー回復モードで処理を続けようとして、「}」をcelltype_statement_listだとみなします。

文法では、この後に「}」と「;」がこの順番に記述されていなければなりません。

しかし、入力としては「;」しか存在しません。

raccは入力の終わりを$endというトークンとして扱います。

しかし、これが来ても、文法エラーが解消されません.パーザは次の入力を求めますが、実際の入力は終わっているので、$endしか渡してもらえません.

結局ここで無限ループが発生してしまいます。

実は、同じエラー内容でも以下の場合は、パーザは文法エラーを報告し、正常に終了します.

(文法としてはエラーになるが、正常終了する場合の記述)

celltyope TaskMain {}};

最初の「}」が来た時点でエラーと判断されますが、エラー回復モードになっても「}」、「;」と続くため、celltypeの文法定義に合致し、エラー回復モードは終了します。その後入力の終わり$endが来て、パーザは正常終了します。

エラー回復モードが(期待するより)頑張りすぎているのかもしれません.

ただし、入力の終わりがきたら必ず打ち切るようにするには、入力の終わりを特別扱いし、どんな状態であっても終了するルーチンをつくるか、どの状態からでも入力の終わりが来たら終了する状態遷移テーブルを作成する必要があると思います。

後、raccが手本にしたyaccの仕様がどうだったのか、確認したほうがいいかな。

raccの動作が、yaccの仕様の通りであれば、on_error()で対処するのがbetterだと思います.

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は、文法テーブルの指定通りに動作してると思います。

TECSとは

TECS(TOPPERS Embedded Component System)とはTOPPERSプロジェクトにて開発された、組込み向けコンポーネントシステムです.

5月のESECの時に公開され、6月のTOPPERSカンファレンスのテーマにもなっていました。

TOPPERSプロジェクトのサイトの「TECSとは」というページで、ソースもダウンロードできます。

その他の場所でもCodeZineの連載記事(「【第1回】TOPPERSプロジェクトとTECS」、「【第2回】TECSによるアプリケーション開 」、「【第3回】コンポーネント記述言語TECS CDL」、「【第4回】コンポーネントの実装コード 」)や、「組込みプレス Vol.15」の「OSSの組込みコンポーネントシステム TECSを使ってみよう」などの記事を読むことできます。

公式サイト以外にも、いつのまにか「TECS 開発ブログ」という開発者のブログが始まっています。

などと書いている私も、TOPPERSプロジェクトのコンポーネント仕様WGに参加しており、開発者の一員です.

今後はこのブログで、TECSも含めてRTOSとか、プログラミングについて書いていこうと思っています.