c2rb

c2rb.rb

Ruby の使い方をひととおり覚えると、色々なプログラムを作ってみたくなります。しかし、ネット上のアルゴリズム関係の記事は多くは C 言語で記述してあるので Ruby に翻訳しなくてはなりません。そこで、C 言語のリストを Ruby に変換する c2rb.rb スクリプトを作ってみました。正規表現を使って一行ずつ変換するので完全自動と言うわけにはいかず、手作業による手直しが必要ですが、原本の C 言語のリストに合わせて仕様を簡単に変更できるので便利です。c2rb.rb のリストは次のようになります。

#!/usr/bin/ruby

ARGF.each do |line|

# preprocessor

  line.sub!(/#define\s+(\w+)\s+(\w+).*$/, '\1 = \2')
  line.sub!(/\/\*(.*)\*\//, '#\1')

# functions

  if line.sub!(/^\s*(void|int|long|float|double)\s*(\w+)\s*\(/,'def \2(')
    line.gsub!(/(\w+)\[\w*\]?/,'\1')
  end

# variables

  if line =~ /^(\s*)(int|long|float|double)\b/
    space = $1
    line.sub!(/(int|long|float|double)/,"")
    line.gsub!(/(\w+)\[\w*\]?/,'\1 = []')
    while (line =~ /(\b\w+\b\s*=\s*[\[\]\w]+)/)
      puts space + $1
      line = $'
    end
    line = ""
  end

# main()

  line.sub!(/^.*\bmain\b.*\(.*\).*$/,"begin")

# types

  line.gsub!(/\((void|int|long|float|double)\)/,"")
  line.gsub!(/\b(void|int|long|float|double)\b/,"")

# "for" statement

  line.gsub!(/for\s.*(\w+)\s*=\s*(\w+).*(\w+)\s*<=\s*(\w+).*\+\+.*?\)(.*);$/, 'for \1 in \2..\4;\5; end')
  line.gsub!(/for\s.*(\w+)\s*=\s*(\w+).*(\w+)\s*<\s*(\w+).*\+\+.*?\)(.*);$/, 'for \1 in \2...\4;\5; end')
  line.gsub!(/for\s.*(\w+)\s*=\s*(\w+).*(\w+)\s*<=\s*(\w+).*\+\+.*$/, 'for \1 in \2..\4')
  line.gsub!(/for\s.*(\w+)\s*=\s*(\w+).*(\w+)\s*<\s*(\w+).*\+\+.*$/, 'for \1 in \2...\4')
  line.gsub!(/for\s+\(\s*;\s*;\s*\)/,"while (true)")

# "if" statement

  line.gsub!(/(if\s.*?\))(.*)\selse\s(.*);$/,'\1;\2; else;\2; end')
  line.gsub!(/(if\s.*?\))(.*);$/,'\1;\2; end')

# delimiter

  line.gsub!(/;$/,"")

# parenthesis

  line.gsub!(/\{/,"")
  line.gsub!(/\}/,"end")

# increment

  line.gsub!(/(\b\w\b)\+\+/,'\1 += 1')
  line.gsub!(/(\b\w\b)--/,'\1 -= 1')

# scanf

  line.gsub!(/scanf\s*\(\s*"%d", &(\w|\w\[\w\])\s*\)/,'\1 = gets.to_i')

# Math functions

  line.gsub!(/\bsqrt\b/,"Math::sqrt")
  puts line
end

試しに奥村晴彦さんの「C言語による最新アルゴリズム事典」技術評論社、1991年、のなかの corrcoef.c を Ruby のスクリプトに変換してみましょう。まず corrcoef.c を c2rb.rb を使って corrcoef.txt に変換します。

$ ruby c2rb.rb corrcoef.c > corrcoef.txt

corrcoef.txt を手作業で実行可能なものに仕上げたのが corrcoef.rb です。

corrcoef.rb の実行例は次のようになります。

$ ruby corrcoef.rb
1, 2
3, 4
^D
データの件数 2
標準偏差 1.41421 1.41421  相関係数 1
標準偏差 1.41421 1.41421  相関係数 1
標準偏差 1.41421 1.41421  相関係数 1

comment.rb

複数行にわたる C 言語のコメントはワンパスでは処理できないので、コメントだけを処理するフィルター comment.rb を作りました。使い方は次のように c2rb.rb の出力をパイプします。

$ ruby c2rb.rb corrcoef.c | ruby comment.rb > corrcoef.rb

corrcoef.rb が一発で動作することはまずないので、ruby -c corrcoef.rb で文法チェックをしながらバグを潰していきます。パースエラーがでてくる場合は if, for 文の多様な書き方に c2rb が追従できていないことが多いです。if, for 文周辺を修正して下さい。リストをきちんとパースする完全自動の C->Ruby コンバータがあるとうれしいのですが。まあ、お手軽版は改造しやすいので勘弁していただくとして。

実際に C から Ruby への翻訳を行ってみると、一対一の変換とはいかないのが分かります。C 言語のポインタは Ruby では意識的に採用されていませんし、引数への関数渡しにはクロージャを使う必要があります。また、static 変数はクラス変数にする必要があるなど結構工夫が必要です。その分、発想の転換を色々と楽しむことができます。