#!/usr/bin/env ruby

require "thread"

if ARGV[0] == "-d"
	$debug = true
	ARGV.shift
end

class PQueue < Queue
	def inspect
		"(queue:%08x)" % __id__
	end
	def chr
		"(queue:%08x)" % __id__
	end
end

$world = File.read(ARGV[0]).split(/\n/)
$width = $world.map {|l| l.size }.max
$height = $world.size

def exec(x, y, dx, dy, s)
	loop do
		y = $height - 1 if y < 0
		y = 0 if $height <= y
		x = $width - 1 if x < 0
		x = 0 if $width <= x

		inst = ($world[y] || [])[x] || ?\s

		if $debug || inst == ?d
			a = [Thread.current.__id__, inst.chr, x, y, s.inspect, s.map {|c| c.chr rescue c.inspect }.inspect]
			puts "tid:%08x, %s@(%2d, %2d) stack:%s   %s" % a
		end

		case inst
		when ?"
			loop do
				x += dx; y += dy
				y = $height - 1 if y < 0
				y = 0 if $height <= y
				x = $width - 1 if x < 0
				x = 0 if $width <= x
				chr = ($world[y] || [])[x]
				break if chr == ?"
				s << chr if chr
			end
		when ?> then dx, dy =  1,  0
		when ?< then dx, dy = -1,  0
		when ?^ then dx, dy =  0, -1
		when ?v then dx, dy =  0,  1
		when ?+ then a = s.pop; b = s.pop; s << (b + a)
		when ?- then a = s.pop; b = s.pop; s << (b - a)
		when ?* then a = s.pop; b = s.pop; s << (b * a)
		when ?/ then a = s.pop; b = s.pop; s << (b / a)
		when ?% then a = s.pop; b = s.pop; s << (b % a)
		when ?` then a = s.pop; b = s.pop; s << (b > a ? 1 : 0)
		when ?\\ then a = s.pop; b = s.pop; s << a << b
		when ?$ then s.pop
		when ?# then x += dx; y += dy
		when ?: then s << s.last

		when ?|
			t = Thread.new(x, y, dx, dy, s.dup) do |x2, y2, dx2, dy2, s2|
				dx2, dy2 = -dy2, dx2
				exec(x2 + dx2, y2 + dy2, dx2, dy2, s2)
			end
			$threads << t
			dx, dy = dy, -dx
		when ?& then s << PQueue.new
		when ?? then s.pop.pop.each {|a| s << a }
		when ?! then a = ((0...s.pop).map { s.pop }).reverse; s.pop << a; break
		when ?_ then (x += dx; y += dy) if s.pop == 0
		when ?G then n = s.pop; s << s[-1 - n]
		else
			if ?0 <= inst && inst <= ?9 then
				s << inst - ?0
			end
		end
		x += dx; y += dy
	end
end

stdio = Object.new
class << stdio
	def <<(x)
		v, r = x
		exit 0 if v < 0
		putc(v)
		r << [] if r
	end
	def pop
		[STDIN.getc || -1]
	end
	def chr
		"(stdio)"
	end
	def inspect
		"(stdio)"
	end
end

Thread.abort_on_exception = true
$threads = []

exec(0, 0, 1, 0, [stdio])
until $threads.empty?
	$threads.pop.join
end

