HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Brainf**k to Ruby converter -- v2

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
converterbrainfruby

Problem

Previous iteration.

You know, I think this is the fastest I've ever pushed out an update to anything. This is Version 2 of my Brainfk to Ruby converter, and the generated code looks... Well, like Brainfk, 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/cases 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 +)|([.,\[\]])/)
      .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 & 2

Solution

You're correct that nesting a case in an elsif ladder is a bit clunky here, but the two things that jump out at me as making the code difficult to comprehend are: 1) we have to know everything that's going on with the Regexp passed to scan() in order to figure out the intent of the if clauses, and 2) the variable string is not a String, but an Array.

You can remove the capture groups from your Regexp to just get an Array of Strings back from scan() (instead of an Array of Arrays of Strings), and figure out what exactly was matched by looking at the strings themselves - which means you don't need a nested case anymore, and makes it a little more obvious what scan() is doing without having to actually parse its argument.

Like this:

# example.rb
code = '--+++.,,'
code.scan(/\++|\-+|[.,]/).map { |str|
  case str[0]
  when '+'
    "Plus signs: #{str.length}"
  when '-'
    "Minus signs: #{str.length}"
  when '.'
    'Dot!'
  when ','
    'Comma!'
  end
}.each {|ll| puts ll}


Produces:

$ ruby example.rb
Minus signs: 2
Plus signs: 3
Dot!
Comma!
Comma!

Code Snippets

# example.rb
code = '--+++.,,'
code.scan(/\++|\-+|[.,]/).map { |str|
  case str[0]
  when '+'
    "Plus signs: #{str.length}"
  when '-'
    "Minus signs: #{str.length}"
  when '.'
    'Dot!'
  when ','
    'Comma!'
  end
}.each {|ll| puts ll}

Context

StackExchange Code Review Q#94520, answer score: 8

Revisions (0)

No revisions yet.