racc 1.2.6のcparseで無限ループ発生せずで、parser.rbとcparseの参照するテーブルの内容が異なるのではないかという予想を立てていましたが、またしてもこの予想は外れていました.
cparseはC言語によるrubyの拡張ライブラリとして実装されてており、参照するテーブルは同一でした.
拡張ライブラリ側で参照する際に、参照する位置がずれたりしないかとも考えましたが、拡張ライブラリのデバッグ用出力を有効にして、parse.rbとcparser.cの両者の出力内容を比較した結果、文法エラーになるトークンを読み込んで、エラーと判定し、エラー回復モードに入るまではまったく同じでした。
違いは、cparse.cがエラー処理として、(仮想的な)エラートークンをシフトするのに対し、parser.rbは文法エラーになるトークンをそのままシフトすることでした。
振り出しに戻ってしまったのですが、よくよくcparse.cを調べてみると、エラー回復モード中に、エラートークンを読み込む処理をする処理がか書かれていました。
それに対して、parser.rbには、該当する処理が存在しませんでした.
そこで、parser.rbに必要な処理を追加すのが、以下に示すパッチです.
ただし、cparse.cでは、(エラー回復モード中のエラー処理には)goto文で飛んできて、(エラー処理が終われば)goto文で(飛んできた所の次の行に)戻っているため、parser.rbにどう書くのが適切かは自信がないです。
また、tecsgenに同梱されているテストケースを実行すると、@racc_state(パーサ内部の状態遷移を管理する配列)がnilになる場合があり、とりあえず動作させるために、@racc_stateがnliの場合には、処理を打ちきるという修正を加えました.
上記の事情を踏まえた上での、ruby 1.8.7に同梱されたraccのparser.rbに対するパッチを以下に示します.
これにより、cparseでなくparser.rbで実行した時でも、テストケースを全て実行できました。
すくなくとも無限ループにはなりませんでした。
#Linuxのruby1.8.7での実行結果です.
#もともとの発端になったJRubyではまだ試していません.
Index: parser.rb
===================================================================
--- parser.rb (リビジョン 23907)
+++ parser.rb (作業コピー)
@@ -115,6 +115,10 @@
catch(:racc_end_parse) {
while true
+#addeb by komianmi
+ break unless @racc_state[-1]
+ break if @racc_state.length < 1
+#end
if i = action_pointer[@racc_state[-1]]
if @racc_read_next
if @racc_t != 0 # not EOF
@@ -294,6 +298,47 @@
racc_e_pop @racc_state, @racc_tstack, @racc_vstack
end
end
+#added by kominami
+ # shiftreduce error token
+ if act > 0 and act < shift_n
+ #
+ # shift
+ #
+ @racc_vstack.push @racc_val
+ @racc_state.push act
+ @racc_read_next = true
+ if @yydebug
+ @racc_tstack.push @racc_t
+ racc_shift 1, @racc_tstack, @racc_vstack
+ end
+ elsif (act < 0 && act > -(reduce_n))
+ #
+ # reduce
+ #
+ code = catch(:racc_jump) {
+ @racc_state.push _racc_do_reduce(arg, act)
+ false
+ }
+ if code
+ case code
+ when 1 # yyerror
+ @racc_user_yyerror = true # user_yyerror
+ return -reduce_n
+ when 2 # yyaccept
+ return shift_n
+ else
+ raise '[Racc Bug] unknown jump code'
+ end
+ end
+
+ elsif (act == shift_n)
+ racc_accept if yydebug
+ return vstack[0]
+
+ else
+ rb_raise(RaccBug, "[Racc Bug] unknown act value %ld", act);
+ end
+#end
return act
else

“raccのparser.rbへのとりあえずのパッチ作成” への1件の返信