変態なif式

exuberant-ctagsはとても高速にコードをscanしてくれるので便利だ。さすがC言語で書かれただけある。Rubyにも対応している。rcodetoolsのrct-meth-argsも同様にTAGSファイルを吐いてくれる。両者の違いは速度と正確さのトレードオフである。exuberant-ctagsの方は高速な分、動的に生成されたメソッドのタグは取ってくれない。一方rct-meth-argsは実際にRubyで実行しているのできわめて正確だが、Rubyで書かれているだけ*1に速度で大幅に見劣りするし、副作用の問題もある。規模やプログラミングスタイルに応じてうまく使い分けていきたい。
exuberant-ctagsのruby.cはif式とif修飾子の違いを検出できていない。そのため、スコープを取り違えてしまい、if修飾子が使われると以後のメソッドのクラス名を取得してくれなくなるという実用上重大な欠陥がある。それをなんとかするべくif式とif修飾子の違いを簡単に見分けるアルゴリズムはないものかと模索している。

とりあえずRubyの文法を見てあらゆるケースのif式を考えてみる。列挙してみると長年Rubyを使っていてもいまだ知らない組み合わせがあってなかなかおもしろい。if trueをunless falseに変えても同様の議論になる。

a=9
if true; 1 end                       # => 1
!if true; 1 end                       # => false
(if true;1 end)                         # => 1
# op_assign
a+=(if true; 1 end)                       # => 10
a=0
a..if true; 1 end                         # => 0..1
a...if true; 1 end                         # => 0...1
a+if true; 1 end                           # => 1
a-if true; 1 end                           # => -1
a*if true; 1 end                           # => 0
a/if true; 1 end                           # => 0
a%if true; 1 end                           # => 0
a**if true; 1 end                           # => 0
a|if true; 1 end                           # => 1
a^if true; 1 end                           # => 1
a&if true; 1 end                           # => 0
a<=>if true; 1 end                           # => -1
a>if true; 1 end                           # => false
a>=if true; 1 end                           # => false
a<if true; 1 end                           # => true
a<=if true; 1 end                           # => true
a==if true; 1 end                           # => false
a===if true; 1 end                           # => false
a!=if true; 1 end                           # => true
a=~if true; 1 end                           # => false
(a)!~if true; 1 end                           # => true
!if true; 1 end                           # => false
~if true; 1 end                           # => -2
a<<if true; 1 end                         # => 0
a>>if true; 1 end                         # => 0
a&&if true; 1 end                         # => 1
a||if true; 1 end                         # => 0
defined?(if true; 1 end)                         # => "expression"
# if
unless if true; 1 end; 1 end                            # => nil
1 and if true; 1 end                            # => 1
1 or if true; 1 end                            # => 1
not if true; 1 end                             # => false
# elsif
case 1; when if true; 1 end: 2 end # => 2
while if true; false end;end       # => nil
until if true; true end;end       # => nil
begin raise;rescue Exception; 2 end # => 2
begin raise;rescue(if true; Exception end); 2 end # => 2
{1=>if true; 1 end}                               # => {1=>1}
x=*if true; [9,8] end                            # => [9, 8]
[7].collect(&if true; lambda{|x| x*2} end)       # => [14]

*1:Rubyのset_trace_funcはやばいほど遅い…