Ruby の予約語に endif やら endfor やらを追加するパッチ
前のエントリで、Ruby では複文の終わりを表すのが end しかないことを問題にし、それに対して endif や endfor を予約語に追加することを解決策として示した。
で、言うだけでなくて実際にパッチを作ってみた。
mismatched-end-1.0.0.tar.gz (追記: RubyForge.org に移動)
(追記2: arton氏によるWindows用バイナリ。多謝)
http://arton.no-ip.info/data/asr/kwruby.zip
このパッチを当てると、以下の予約語が追加される。意味については説明不要だろう。
- endif
- endunless
- endfor
- endwhile
- enduntil
- endcase
- endbegin
- enddo
- enddef
- endclass
- endmodule
インストール方法は次の通り。コンパイルには、gperf (keywors から lex.c を生成する) と bison (parse.y から parse.c を生成する) が必要なことに注意。
$ tar xzf ruby-1.8.6-p114.tar.gz $ cd ruby-1.8.6-p114/ $ patch -p1 < /tmp/end-mismatch-patch/ruby-1.8.6/patch.diff $ ./configure --prefix=/usr/local $ make $ make test $ sudo cp ruby /usr/local/bin/myruby
実際の例を見てみる。たとえば次のスクリプトでは、10 行目が end ではなく enb になっている。
test1.rb:
001: module Foo 002: class Bar 003: def example(arg) 004: i = 0 005: for item in arg 006: if item 007: p item 008: end 009: end 010: enb # error 011: end 012: end
これをふつうの Ruby で実行すると、12 行目に文法エラーがあり、'end' がくるはずなのにファイルの終わりが来てる (unexpected $end, expecting kEND) と報告される。しかし、これだと実際のエラーがどこにあるのかはわからない。
$ ruby test1.rb test1.rb:12: syntax error, unexpected $end, expecting kEND
ここで end のかわりに、たとえば 9 行目を endfor に、また 11 行目を endclass に変えてみる。
test2.rb:
001: module Foo 002: class Bar 003: def example(arg) 004: i = 0 005: for item in arg 006: if item 007: p item 008: end 009: endfor 010: enb # error 011: endclass 012: end
これを実行してみると、9 行目に問題はなく、11 行目に問題があることがわかる。つまり、9 行目から 11 行目の間に問題があると推測できる。またエラーメッセージも expecting kEND or kENDDEF とあるので、enddef が見つからない、つまり def に対応した end が見つからないということがすぐに分かる。
$ myruby test2.rb test2.rb:11: syntax error, unexpected kENDCLASS, expecting kEND or kENDDEF
これだけの情報があれば、どこが間違っているかを特定するのは簡単だろう。
また、このパッチは eRuby ファイルが複雑な場合に特に便利だ。eRuby ファイルは手軽なのがいいのだが、HTML が複雑になるとすっごーく読みづらくなる。しかし endif や endfor が使えると、end がどの if や for と関連しているかがよくわかるようになる。
<% if @list %> <table> <% for item in @list %> <tr> <% if !item.name %> <td cols="2">-</td> <% elsif item.email %> <td><a href="mailto:<%=item.email%>"><%=h item.name %></a></td> <td><a href="mailto:<%=item.email%>"><%=h item.email %></a></td> <% else %> <td><%= item.name %></td> <td>-</td> <% endif %> </tr> <% endfor %> </table> <% else %> <p>Not found.</p> <% endif %>
ということで、Ruby の mismatched-end 問題で悩んでいるひとがいたら、このパッチを試してみればいいと思うよ。エディタのあやふやな自動インデントに頼った方法とは大違いだから!