Skip to content

Instantly share code, notes, and snippets.

@dividedharmony
Created December 7, 2016 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dividedharmony/036d65a476891431c1311efe88958ffc to your computer and use it in GitHub Desktop.
Save dividedharmony/036d65a476891431c1311efe88958ffc to your computer and use it in GitHub Desktop.
PerfectMind is a Brainf**k compiler in Ruby
##
# Instructions from https://www.codewars.com/kata/my-smallest-code-interpreter-aka-brainf-star-star-k/train/ruby
#
# Inspired from real-world Brainf**k, we want to create an interpreter of that
# language which will support the following instructions
# (the machine memory or 'data' should behave like a potentially infinite array of bytes, initialized to 0):
#
# > increment the data pointer (to point to the next cell to the right).
# < decrement the data pointer (to point to the next cell to the left).
# + increment (increase by one, truncate overflow: 255 + 1 = 0) the byte at the data pointer.
# - decrement (decrease by one, treat as unsigned byte: 0 - 1 = 255 ) the byte at the data pointer.
# . output the byte at the data pointer.
# , accept one byte of input, storing its value in the byte at the data pointer.
# [ if the byte at the data pointer is zero, then instead of moving the instruction pointer forward to the next command, jump it forward to the command after the matching ] command.
# ] if the byte at the data pointer is nonzero, then instead of moving the instruction pointer forward to the next command, jump it back to the command after the matching [ command.
class PerfectMind
attr_reader :input_array, :output_array, :memory_stack, :command_tape
def initialize(command_string, input_string)
@command_tape = CommandTape.new(command_string.split(''), self)
@input_array = input_string.bytes
@output_array = []
@memory_stack = MemoryStack.new
end
def interpret
@interpretation ||= calculate_interpretation
end
private
def calculate_interpretation
until command_tape.finished?
command_tape.next
end
output_array.pack('c*')
end
class MemoryStack
attr_reader :stack
attr_accessor :pointer
def initialize
@stack = Hash.new(0)
@pointer = 0
end
def increment_pointer
@pointer += 1
end
def decrement_pointer
@pointer -= 1
end
def value_at_pointer
stack[pointer]
end
def value_at_pointer=(value)
stack[pointer] = value
end
def increment_value_at_pointer
stack[pointer] = overflow(stack[pointer] + 1)
end
def decrement_value_at_pointer
stack[pointer] = overflow(stack[pointer] - 1)
end
private
def overflow(value)
value % 256
end
end
# End MemoryStack
class CommandTape
COMMAND_COMPILATION = {
'>' => :forward,
'<' => :backward,
'+' => :increment,
'-' => :decrement,
'.' => :output,
',' => :input,
'[' => :begin_loop,
']' => :end_loop
}.freeze
attr_reader :loop_stack, :perfect_mind, :command_array, :index
def initialize(command_array, perfect_mind)
@loop_stack = []
@perfect_mind = perfect_mind
@command_array = process_end_loops(command_array.reject { |command| !COMMAND_COMPILATION.has_key?(command) })
@index = 0
end
def finished?
index >= command_array.length
end
def next
command = command_array[index]
if command.is_a? Fixnum
@index = command
else
send(COMMAND_COMPILATION[command])
end
end
def forward
perfect_mind.memory_stack.increment_pointer
@index += 1
end
def backward
perfect_mind.memory_stack.decrement_pointer
@index += 1
end
def increment
perfect_mind.memory_stack.increment_value_at_pointer
@index += 1
end
def decrement
perfect_mind.memory_stack.decrement_value_at_pointer
@index += 1
end
def input
perfect_mind.memory_stack.value_at_pointer = (perfect_mind.input_array.shift || 0)
@index += 1
end
def output
perfect_mind.output_array << perfect_mind.memory_stack.value_at_pointer
@index += 1
end
def begin_loop
if skip_loop?
@index = command_array.index(@index) + 1
else
@index += 1
end
end
def skip_loop?
perfect_mind.memory_stack.value_at_pointer == 0
end
# There's no need for an end loop
def process_end_loops(array)
array.each_with_index.map do |command, index|
if(command == '[')
loop_stack.push(index)
command
elsif(command == ']')
loop_stack.pop
else
command
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment