require "./parser.cr" class Brainfuck::Program @mem_size : Int32 def initialize(@parser : Parser, mem_size : Int32?) @mem_size = mem_size || DEFAULT_TAPE_SIZE end def interpret code = @parser.parse jumps = find_jumps(code) code_ptr = 0 memory = [0_u8] * @mem_size mem_ptr = 0 while code_ptr < code.size case code[code_ptr] when Opcode::PtrRight mem_ptr += 1 when Opcode::PtrLeft mem_ptr -= 1 when Opcode::Inc begin memory[mem_ptr] += 1_u8 rescue OverflowError memory[mem_ptr] = 0_u8 end when Opcode::Dec begin memory[mem_ptr] -= 1_u8 rescue OverflowError memory[mem_ptr] = 0_u8 end when Opcode::Out print memory[mem_ptr].chr when Opcode::In c = STDIN.read_char memory[mem_ptr] = c.nil? ? 0_u8 : c.ord.to_u8 when Opcode::LoopStart code_ptr = jumps[code_ptr] if memory[mem_ptr] == 0_u8 when Opcode::LoopEnd code_ptr = jumps[code_ptr] unless memory[mem_ptr] == 0_u8 end raise MemoryError.new("Too far left") if mem_ptr < 0 raise MemoryError.new("Too far right") if mem_ptr >= memory.size code_ptr += 1 end end def compile raise Util::NoCompilerAvailableError.new("No available compiler for brainfuck") end # assumes valid code private def find_jumps(code : Array(Opcode)) : Hash(Int32, Int32) jumps = Hash(Int32, Int32).new stack = [] of Int32 code.each_index do |i| if code[i] == Opcode::LoopStart stack.push(i) end if code[i] == Opcode::LoopEnd jumps[i] = stack.pop jumps[jumps[i]] = i end end return jumps end end