Brainf**k to Ruby converter, written in Ruby -- v1.0 I've seen a lot of Brainf**k interpreters in various languages. I decided that actually interpreting Brainf**k is too hard, so instead of that, I wrote a 'compiler' in Ruby that directly transcribes BF to Ruby code. This is Version 1.0, and I'm looking for any tips on how to make it more efficient.

There are a few things that really bug me:


replacements feels like it's messed up somehow, but I can't put my finger on how.
$stdin.readbyte feels entirely too long, but I can't find an alternative that's shorter.
Should I be using $stdin at all?




input_file = $ARGV[0]
output_file = $ARGV[1]
start = &lt;&lt;-END.gsub(/[ \t]*\|/, '')
  |# Automatically generated by bf_to_ruby.rb
  |# Source available at GitHub
  |data = Hash.new(0)
  |pointer = 0
END

replacements = [
    { replacing: '+', replacement: 'data[pointer] += 1'              },
    { replacing: '-', replacement: 'data[pointer] -= 1'              },
    { replacing: '&gt;', replacement: 'pointer += 1'                    },
    { replacing: '&lt;', replacement: 'pointer -= 1'                    },
    { replacing: '.', replacement: 'putc data[pointer]'              },
    { replacing: ',', replacement: 'data[pointer] = $stdin.readbyte' },
    { replacing: '[', replacement: 'until data[pointer] == 0'        },
    { replacing: ']', replacement: 'end'                             },
]

output = open(output_file, File::CREAT | File::WRONLY)
output.puts(start)

open(input_file, File::RDONLY) do |input|
  input.each_char do |char|
    replacements.each do |data|
      if data[:replacing] == char
        output.puts(data[:replacement])
      end
    end
  end
end


Demo

Input:


++++++++[&gt;++++[&gt;++&gt;+++&gt;+++&gt;+&lt;&lt;&lt;&lt;-]&gt;+&gt;+&gt;-&gt;&gt;+[&lt;]&lt;-]&gt;&gt;.&gt;---.+++++++..+++.&gt;&gt;.&lt;-.&lt;.+++.------.--------.&gt;&gt;+.&gt;++.



Output:




# Automatically generated by bf_to_ruby.rb
# Source available at GitHub
data = Hash.new(0)
pointer = 0
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
until data[pointer] == 0
pointer += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
until data[pointer] == 0
pointer += 1
data[pointer] += 1
data[pointer] += 1
pointer += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
pointer += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
pointer += 1
data[pointer] += 1
pointer -= 1
pointer -= 1
pointer -= 1
pointer -= 1
data[pointer] -= 1
end
pointer += 1
data[pointer] += 1
pointer += 1
data[pointer] += 1
pointer += 1
data[pointer] -= 1
pointer += 1
pointer += 1
data[pointer] += 1
until data[pointer] == 0
pointer -= 1
end
pointer -= 1
data[pointer] -= 1
end
pointer += 1
pointer += 1
$stdout.putc data[pointer]
pointer += 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
$stdout.putc data[pointer]
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
$stdout.putc data[pointer]
$stdout.putc data[pointer]
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
$stdout.putc data[pointer]
pointer += 1
pointer += 1
$stdout.putc data[pointer]
pointer -= 1
data[pointer] -= 1
$stdout.putc data[pointer]
pointer -= 1
$stdout.putc data[pointer]
data[pointer] += 1
data[pointer] += 1
data[pointer] += 1
$stdout.putc data[pointer]
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
$stdout.putc data[pointer]
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
data[pointer] -= 1
$stdout.putc data[pointer]
pointer += 1
pointer += 1
data[pointer] += 1
$stdout.putc data[pointer]
pointer += 1
data[pointer] += 1
data[pointer] += 1
$stdout.putc data[pointer]



Both, when run, output:


Hello World!
You should open the input file before the output file, so that if the input file is unreadable, no file is created at all.  File opening should be done using blocks for automatic closure and cleanup.

This is too complicated:


replacements.each do |data|
  if data[:replacing] == char
    output.puts(data[:replacement])
  end
end



replacements would be better as a Hash:

replacements = {
    '+' =&gt; 'data[pointer] += 1',
    '-' =&gt; 'data[pointer] -= 1',
    '&gt;' =&gt; 'pointer += 1',
    '&lt;' =&gt; 'pointer -= 1',
    '.' =&gt; 'putc data[pointer]',
    ',' =&gt; 'data[pointer] = $stdin.readbyte',
    '[' =&gt; 'until data[pointer] == 0',
    ']' =&gt; 'end',
}

open(input_file, File::RDONLY) do |input|
  open(output_file, File::CREAT | File::WRONLY) do |output|
    output.puts(start)
    output.puts(input.each_char.map { |c| replacements[c] }.compact.join("\n"))
  end
end


One big puts should be faster than many indivual puts.