Skip to content

Instantly share code, notes, and snippets.

@kuzux
Created April 14, 2009 12:47
Show Gist options
  • Save kuzux/95165 to your computer and use it in GitHub Desktop.
Save kuzux/95165 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
class Brainfuck
# Ruby Brainfuck interpreter
# (C) Scott Olson <scott@scott-olson.org>
# MIT License
attr_accessor :script, :stdin, :stdout
@@allowed = '<>+-.,[]#'
# script is the brainfuck program
# stdin and stdout are the input and output, respectively
# they can be String, IO, or something else that accepts putc/getc
def initialize(script, stdout=nil, stdin=nil)
@script = script
@stdout = stdout || ''
@stdin = stdin || ''
end
def run
ptr = 0
stack = [0]
script = @script.split('')
script_ptr = 0
while script_ptr < script.length
debug_msg = "%0#{script.length.to_s.length}i: (#{ptr}=#{stack[ptr]}) #{script[script_ptr]}" % script_ptr
case script[script_ptr]
when '>'
ptr += 1
stack[ptr] ||= 0 # set the cell to 0 if it is nil
when '<'
ptr -= 1
stack[ptr] ||= 0 # set the cell to 0 if it is nil
when '+'
stack[ptr] += 1
when '-'
stack[ptr] -= 1
when '.'
debug_msg << " outputting #{stack[ptr].chr.inspect}"
case @stdout
when String
@stdout << stack[ptr]
else
@stdout.putc stack[ptr]
end
when ','
case @stdin
when String
stack[ptr] = (/^1\.8/ === RUBY_VERSION ? @stdin.first[0] : @stdin.first.ord) # grab first char as int
@stdin[0] = '' # remove the char from the string
else
stack[ptr] = @stdin.getc
stack[ptr] ||= 0 # our EOF if @stdin is at the end
end
when '['
came_from = script_ptr # never forget where you came from
# this code jumps to the next ']' if our current byte is 0
script_ptr += script[script_ptr..-1].index(']') if stack[ptr] == 0
when ']'
raise "unmatched ] encountered" unless defined? :came_from
script_ptr = came_from - 1 # go back to the start of the loop
when '#'
debug_msg << " dumping info for debugging purposes\n"
stack_dump = []
stack.each_with_index do |v,i|
stack_dump << "*#{i}=#{v}" && next if ptr == i # put a * next to the cell the pointer is at
stack_dump << "#{i}=#{v}"
end
debug_msg << "[" << stack_dump.join(', ') << "]\n"
else # not a brainfuck character; treated as a comment
comment ||= [script_ptr]
comment[1] = script_ptr
end
if @@allowed.include?(script[script_ptr]) and comment
debug_msg = ("%0#{script.length.to_s.length}i-%0#{script.length.to_s.length}i: comments\n" % [comment[0], comment[1]]) + debug_msg
comment = nil
end
script_ptr += 1
$stderr.puts debug_msg if @@debug unless comment
end
end
@@debug = false
def self.debug=(val)
@@debug = val
end
end
class BSP
def call(env)
#Brainfuck.debug = true
req = Rack::Request.new env
headers = {"Content-Type" => "text/html"}
filename = env["PATH_INFO"][1..-1]
input = req.params.map{|k,v|"#{k}=#{v}"}.join("&")
output = ""
Brainfuck.new(File.read(filename), output, input).run
headers["Content-Length"] = output.length.to_s
[200,headers,output]
end
end
run BSP.new
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment