I think this is about as good as it's gonna get, but just for kicks, let's do one more round of iterations.
As before, it's a very simple substitution from Brainfuck to Ruby, with fancy things like joining duplicate lines into one and indentation.
Some things that bug me:
- The regex (
/\++|-+|<+|>+|[.,\[\]]/
) feels like it can be simplified, but I can't figure out how. - I feel like the
map
can be simplified, but I can't figure out how. - The constant repetition of
#{' ' * indent_level}
bugs me, but I can't see how to get rid of it. indent_level
being declared outside of the scope ofeach
feels like an anti-pattern, but AFAICT there's no better way to do it.
input_file, output_file = ARGV
code = IO.read(input_file).delete('^+-<>.,[]')
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|
case string[0]
when '+' #String of "+"
next "#{' ' * indent_level}data[pointer] += #{string.length}"
when '-' #String of "-"
next "#{' ' * indent_level}data[pointer] -= #{string.length}"
when '<' #String of "<"
next "#{' ' * indent_level}pointer -= #{string.length}"
when '>' #String of ">"
next "#{' ' * indent_level}pointer += #{string.length}"
when '[' #Single "["
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 ']' #Single "]"
indent_level -= 1
next "#{' ' * indent_level}end"
when ',' #Single ","
next "#{' ' * indent_level}data[pointer] = $stdin.readbyte"
when '.' #Single "."
next "#{' ' * indent_level}putc data[pointer]"
end
end.each { |line| output.puts(line) }
end