You know, I think this is the fastest I've ever pushed out an update to anything. This is Version 2 of my Brainf**k to Ruby converter, and the generated code looks... Well, like Brainf**k, converted directly to Ruby, with no attempt at making it more readable.
I'm looking for any tips on making things more idiomatic, both in the generator and generated code. The nest if
/case
s really bug me, but I'm not quite sure how to get rid of them, especially since just two characters are blindly replaced. I'd also like advice on making it run faster.
bf_to_ruby.rb
input_file, output_file = ARGV
code = IO.read(input_file).tr('^+-<>.,[]', '')
open(output_file, File::CREAT | File::WRONLY) do |output|
output.puts <<-END.gsub(/^[ \t]*\||\s*#@.*$/, '')
|#!/usr/bin/env ruby
|class Mem < Hash #@ `Hash` because it's more memory-efficient and allows negative values.
| def initialize; super(0); end
| def []=(i, val); super(i, val & 255); end
|end
|data = Mem.new
|pointer = 0
END
indent_level = 0
code.scan(/(\++)|(\-+)|(<+)|(>+)|([.,\[\]])/)
.map do |string|
if string[0]
next "#{' ' * indent_level}data[pointer] += #{string[0].length}"
elsif string[1]
next "#{' ' * indent_level}data[pointer] -= #{string[1].length}"
elsif string[2]
next "#{' ' * indent_level}pointer -= #{string[2].length}"
elsif string[3]
next "#{' ' * indent_level}pointer += #{string[3].length}"
elsif string[4]
case string[4]
when '['
ret = "#{' ' * indent_level}until data[pointer] == 0"
indent_level += 1
next ret #Split it so that it's clear that indent is increased *after* the line
when ']'
indent_level -= 1
next "#{' ' * indent_level}end"
when ','
next "#{' ' * indent_level}data[pointer] = $stdin.readbyte"
when '.'
next "#{' ' * indent_level}putc data[pointer]"
end
end
end.each { |line| output.puts(line) }
end
Demo
Input:
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
Output:
#!/usr/bin/env ruby class Mem < Hash def initialize; super(0); end def []=(i, val); super(i, val & 255); end end data = Mem.new pointer = 0 data[pointer] += 8 until data[pointer] == 0 pointer += 1 data[pointer] += 4 until data[pointer] == 0 pointer += 1 data[pointer] += 2 pointer += 1 data[pointer] += 3 pointer += 1 data[pointer] += 3 pointer += 1 data[pointer] += 1 pointer -= 4 data[pointer] -= 1 end pointer += 1 data[pointer] += 1 pointer += 1 data[pointer] += 1 pointer += 1 data[pointer] -= 1 pointer += 2 data[pointer] += 1 until data[pointer] == 0 pointer -= 1 end pointer -= 1 data[pointer] -= 1 end pointer += 2 putc data[pointer] pointer += 1 data[pointer] -= 3 putc data[pointer] data[pointer] += 7 putc data[pointer] putc data[pointer] data[pointer] += 3 putc data[pointer] pointer += 2 putc data[pointer] pointer -= 1 data[pointer] -= 1 putc data[pointer] pointer -= 1 putc data[pointer] data[pointer] += 3 putc data[pointer] data[pointer] -= 6 putc data[pointer] data[pointer] -= 8 putc data[pointer] pointer += 2 data[pointer] += 1 putc data[pointer] pointer += 1 data[pointer] += 2 putc data[pointer]
You'll notice that the generated code is nearly the same as the last version's generated code, but with a lot of duplicate lines merged and indenting.