Skip to content

Instantly share code, notes, and snippets.

@5thWall
Created July 20, 2015 04:07
Show Gist options
  • Save 5thWall/94f7a1c19dc10ff07186 to your computer and use it in GitHub Desktop.
Save 5thWall/94f7a1c19dc10ff07186 to your computer and use it in GitHub Desktop.
Implementation of a BrainF*** interpreter
require 'memory'
require 'tape'
class BrainLuckMachine
attr_accessor :tape, :input, :output, :memory
def initialize(code, input)
@tape = Tape.new(code)
@input = input.codepoints
@output = ''
@memory = Memory.new
end
def run
loop do
cmd = tape.incr_tape
return output if cmd == :EOF
send(cmd)
end
end
private
def incr_data_ptr
memory.incr_ptr
end
def decr_data_ptr
memory.decr_ptr
end
def incr_data
memory.incr
end
def decr_data
memory.decr
end
def put_output
output << memory.current.chr
end
def save_input
memory.save(input.first)
self.input = input.drop(1)
end
def jump_forward
tape.jump_forward if memory.zero?
end
def jump_back
tape.jump_back unless memory.zero?
end
end
class Memory
attr_reader :memory, :ptr
def initialize
@memory = []
@ptr = 0
end
def incr_ptr
@ptr += 1
memory[ptr] ||= 0
end
def decr_ptr
if ptr < 0
@ptr = 0
memory.insert(0, 0)
else
@ptr -= 1
end
end
def incr
memory[ptr] += 1
memory[ptr] = 0 if current > 255
end
def decr
memory[ptr] -= 1
memory[ptr] = 255 if current < 0
end
def save(input)
memory[ptr] = input
end
def current
memory[ptr]
end
def zero?
current.zero?
end
end
class Tape
COMMANDS = {
'>' => :incr_data_ptr,
'<' => :decr_data_ptr,
'+' => :incr_data,
'-' => :decr_data,
'.' => :put_output,
',' => :save_input,
'[' => :jump_forward,
']' => :jump_back
}
attr_reader :code, :ptr
def initialize(code)
@code = code.chars.each_with_index.map do |char, i|
COMMANDS.fetch(char) do |_|
fail "Syntax Error at #{i}: Could not parse '#{char}'"
end
end
@ptr = -1
end
def cur_cmd
return :EOF if ptr < 0
code[ptr] || :EOF
end
def incr_tape
@ptr += 1
cur_cmd
end
def decr_tape
@ptr -= 1
cur_cmd
end
def jump_forward(count = 0)
cmd = incr_tape
fail "EOF Reached Unexpectedly" if cmd == :EOF
return cmd if count == 0 && cmd == :jump_back
count += 1 if cmd == :jump_forward
count -= 1 if cmd == :jump_back
jump_forward(count)
end
def jump_back(count = 0)
cmd = decr_tape
fail "BOF Reached Unexpectedly" if cmd == :EOF
return cmd if count == 0 && cmd == :jump_forward
count -= 1 if cmd == :jump_forward
count += 1 if cmd == :jump_back
jump_back(count)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment