Created
December 7, 2016 14:06
-
-
Save dividedharmony/036d65a476891431c1311efe88958ffc to your computer and use it in GitHub Desktop.
PerfectMind is a Brainf**k compiler in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# 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