Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/ruby
# vim: sw=2 ts=2 ft=ruby expandtab tw=0 nu syn=on:
# file: matthew.rb

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]+/)                              # split input into rows
   @hgt = rows.size                                          # find program height
   @wid = rows.max { |a, b| a.size <=> b.size }.size         # find program width
   @wid = [ @wid, 80 ].max
   @code = rows.map { |row| row + " " * (@wid - row.size) }  # pad rows, store code (r, c)
   @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     # what about underflow?
         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]    # Ruby 1.8, maybe not 1.9?
       when ?@
         break
       when ?d
   dump
       end
     end
     step    # move program counter
   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#