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
-
1\$\begingroup\$ BTW: this is a compiler, not an interpreter. \$\endgroup\$Jörg W Mittag– Jörg W Mittag2015年07月31日 11:19:02 +00:00Commented Jul 31, 2015 at 11:19
1 Answer 1
I'm not 100% sure this works in Ruby, but can you use constants with string interpolation? This string is repeated a lot. I doubt it will ever change, but if it does, it would be nice to only have to change it once.
"#{' ' * indent_level}data[pointer]"
Could be #{' ' * indent_level}#{DATA}
.
-
\$\begingroup\$ I'm not quite sure what you mean by "can you use constants with string interpolation?" \$\endgroup\$anon– anon2015年06月24日 23:19:52 +00:00Commented Jun 24, 2015 at 23:19
-
\$\begingroup\$ Can you inject a constant string value into another string using interpolation? I'm sure you can. I'm just not sure you can. \$\endgroup\$RubberDuck– RubberDuck2015年06月24日 23:21:17 +00:00Commented Jun 24, 2015 at 23:21
-
\$\begingroup\$ Oh, like
"#{'test'} test"
? Yes, that's possible. I don't see why you'd do it though. \$\endgroup\$anon– anon2015年06月24日 23:22:01 +00:00Commented Jun 24, 2015 at 23:22 -
2\$\begingroup\$ Because this string (
data[pointer]
) is repeated several times. If you would ever have a reason to change it. You'd only have to change it once. \$\endgroup\$RubberDuck– RubberDuck2015年06月24日 23:23:20 +00:00Commented Jun 24, 2015 at 23:23 -
\$\begingroup\$ And when would I ever need to change it? \$\endgroup\$anon– anon2015年06月25日 01:13:35 +00:00Commented Jun 25, 2015 at 1:13