require 'enumerator'
class Befunge93
def self.load_and_run(filename)
self.new(File.read(filename)).run
end
def initialize(text)
rows = text.split(/[\r\n]+/)
@hgt = rows.size
@wid = rows.max { |a, b| a.size <=> b.size }.size
@wid = [ @wid, 80 ].max
@code = rows.map { |row| row + " " * (@wid - row.size) }
@pc, @dir = [0, 0], :east
@stack = []
@stringmode = false
@commentmode = false
@debugger = []
end
def run
loop do
if @commentmode
if curr == ?=
@commentmode = false
end
step
next
end
if @stringmode
case curr
when ?"
@stringmode = false
else
push curr
end
else
case curr
when ?0..?9
push (curr - ?0)
when ?+, ?-, ?*, ?/, ?%
b, a = pop, pop
push a.send(curr.to_sym, b)
when ?!
push (pop.zero? ? 1 : 0)
when ?`
b, a = pop, pop
push (a > b ? 1 : 0)
when ?>
@dir = :east
when ?<
@dir = :west
when ?^
@dir = :north
when ?v
@dir = :south
when ??
@dir = [:east, :west, :north, :south][rand(4)]
when ?=
@commentmode = true
when ?_
@dir = pop.zero? ? :east : :west
when ?|
@dir = pop.zero? ? :south : :north
when ?"
@stringmode = true
when ?:
push top
when ?\\
a = pop
b = pop
push a
push b
when ?$
pop
when ?.
print pop, " "
when ?,
print pop.chr
when ?#
step
when ?h
print "Enter befunge instructions or just <Enter> s: show stack, d: dump, h: comes back in here\n> "
@debugger = $stdin.gets.chomp.enum_for( :each_byte ).to_a
when ?g
r, c = pop, pop
push @code[r][c]
when ?p
r, c, v = pop, pop, pop
@code += (@code.size..r).map{ " "*@wid }
@code[r][c] = v
when ?s
p @stack
when ?&
print "int?> "
push gets.to_i
when ?~
print "chr?> "
push gets[0]
when ?@
break
when ?d
dump
end
end
step
end
rescue Exception => e
$stderr.puts e.message
$stderr.puts e.backtrace
dump
end
private
def curr
@debugger.empty? ?
@code[ @pc[0] ][ @pc[1] ] :
@debugger.shift
end
def dump
x = @code[@pc.first][@pc.last]
x = case x
when 32
"H"
else
x.chr
end
puts @code[0...@pc.first] +
[ @code[@pc.first][0...@pc.last] + "\e[1;31m#{x}\e[0;0m" + @code[@pc.first][@pc.last.succ..-1] ] +
@code[@pc.first.succ..-1]
puts "-"*80
p( { :pc => @pc, :stack => @stack } )
end
def step
case @dir
when :north
@pc[0] = (@pc[0] - 1 + @hgt) % @hgt
when :south
@pc[0] = (@pc[0] + 1) % @hgt
when :east
@pc[1] = (@pc[1] + 1) % @wid
when :west
@pc[1] = (@pc[1] - 1 + @wid) % @wid
end
end
def push(val)
@stack.push(val)
end
def pop
@stack.pop || 0
end
def top
@stack.last || 0
end
end
if __FILE__ == $0
Befunge93.load_and_run(ARGV.shift)
end