バグ修正 というか仕様くらいよもう Befunge

全然読んでなかった(爆)
http://catseye.tc/projects/befunge93/doc/befunge93.html

A Befunge-93 program is treated as an 80x25 torus

なんて馬鹿なんだおいら。というわけで修正しました。

   def load_program(prog)
      @prog   = prog.split("\n")
      @width  = 80
      @height = 25
      @prog   = @prog.map {|l| l.ljust(@width)} + ["".ljust(@width)] * (25 - @prog.length)
   end

いわもとさんのご指摘、

Befunge-93ではコード領域は80x25と決まっているので、初めから80x25分確保してしまってもいいのかも知れません。

をみて、一瞬「width_max , height_max で torus 。putで毎回最大値チェックして torus範囲が変わる」というbefunge改を連想してしまったのだけれど、それは面倒。これでいいや。

追記:
バカしてしまった。なんて天然教育的配慮人間なんだろう僕って。上のままでは "".ljust(*)は全部同じものをさしてしまう。うーん、別インスタンスにするうまい方法がわからない。

   def load_program(prog)
      @prog   = prog.split("\n")
      @width  = 80
      @height = 25
      (25-@prog.length).times {|i| @prog += [""] }
      @prog   = @prog.map {|l| l.ljust(@width)}
   end

あんまりかっこいい実装ではないけど、とりいそぎ修正。

追記2:
さらにこうしたほうが良い気がしてきた。前のままだと""の中とか Stringになってしまうので演算とか出来ない。

120c120
<       @stack.push  read_raw(Point.new(x,y))
---
>       @stack.push  read_raw(Point.new(x,y)).ord
126,127c126,127
<       v = @stack.pop.chr
<       write_raw(Point.new(x,y), v) 
---
>       v = @stack.pop
>       write_raw(Point.new(x,y), v.chr) 
147c147
<          @stack.push(c)
---
>          @stack.push(c.ord)
165c165
<          when ?~              then @stack.push STDIN.getc
---
>          when ?~              then @stack.push STDIN.getc.ord

全体に反映済。


全体:

#!/usr/bin/env ruby

# for <=1.8.6 compatibility
# added by K.Iwamoto
unless String.method_defined? :ord
   class String
      def ord
	 self[0]
      end
   end
end

# for <=1.8.6 compatibility
# added by K.Iwamoto
unless Integer.method_defined? :ord
   class Integer
      def ord
	 self
      end
   end
end

class VM
   class Stack < Array
      def pop
	 v = super
	 v ? v: 0
      end

      # added by K.Iwamoto
      def last
	 empty? ? 0 : super
      end
   end

   class Point 
      attr_accessor :x, :y
      def initialize(x,y)
         @x = x
         @y = y
      end
      def to_s
         "(#{x} , #{y})"
      end
   end

   attr_reader :dir
   UP = 0
   DOWN =  1
   LEFT =  2
   RIGHT = 3
   DIR = {'>'=>RIGHT, '<'=>LEFT, '^'=>UP, 'v'=>DOWN}

   attr_reader :state
   MODE_NORMAL = 0
   MODE_STRING = 1

   attr_reader :curpos
   attr_reader :stack
   attr_reader :prog

   OP = {
      '+' => Proc.new { |x,y| x + y },
      '-' => Proc.new { |x,y| x - y },
      '*' => Proc.new { |x,y| x * y },
      '/' => Proc.new { |x,y| x / y },
      '%' => Proc.new { |x,y| x % y }
   }

   def read_raw(p)
      @prog[p.y][p.x]
   end

   def write_raw(p, v)
      @prog[p.y][p.x] = v
   end

   def read
      read_raw(@curpos)
   end

   def initialize(prog=nil)
      @stack = Stack.new
      @curpos = Point.new(0,0)
      @dir = RIGHT
      @state = MODE_NORMAL
      @debug = false
      if prog then load_program(prog) end
   end

   def load_program(prog)
      @prog   = prog.split("\n")
      @width  = 80
      @height = 25
      (25-@prog.length).times {|i| @prog += [""] }
      @prog   = @prog.map {|l| l.ljust(@width)}
   end

   def nextpos
      case @dir
      when RIGHT
         @curpos.x = (@curpos.x+1) % @width
      when LEFT
         @curpos.x = (@curpos.x-1) % @width
      when UP
         @curpos.y = (@curpos.y-1) % @height
      when DOWN
         @curpos.y = (@curpos.y+1) % @height
      end
   end

   # utils for isrns
   def doublequote
      @state = (MODE_STRING+MODE_NORMAL) - @state
   end

   def changedir(pred, oneway, anotherway)
      @dir = (pred == 0)? oneway : anotherway
   end

   def swaplast2
      t = @stack.pop
      @stack.push(t, @stack.pop)
   end

   def alu(op)
      y = @stack.pop
      x = @stack.pop
      @stack.push OP[op].call(x,y)
   end

   def cmp
      y = @stack.pop
      x = @stack.pop
      @stack.push  x > y ? 1 : 0
   end
   
   def negate
      @stack.push  @stack.pop == 1? 0 : 1
   end

   def get
      y = @stack.pop
      x = @stack.pop
      @stack.push  read_raw(Point.new(x,y)).ord
   end

   def put
      y = @stack.pop
      x = @stack.pop
      v = @stack.pop
      write_raw(Point.new(x,y), v.chr) 
   end

   def show_program
      if @debug
	 STDERR.puts @prog.join("-\n")
      end
   end


   # main dispatcher
   def step(n=1)
      c = read

      if @debug
         STDERR.print "pos: #{@curpos} mnemonic: #{c.chr} "
         STDERR.puts  "dir: #{@dir} stack: #{@stack.join(',')}"
      end

      if @state == MODE_STRING && c != ?"
         @stack.push(c.ord)
      else 
         case c

            #control
         when ?v,?<,?>,?^     then @dir = DIR[c.chr]
         when ?_              then changedir(@stack.pop, RIGHT, LEFT)
         when ?|              then changedir(@stack.pop,  DOWN, UP  )
         when ??	      then @dir = rand(4)
         when ?#	      then nextpos    # through!
         when ?@              then return false

            #literal
         when ?0 .. ?9        then @stack.push c.chr.to_i
         when ?"              then doublequote

            #I/O
         when ?&              then @stack.push STDIN.readline.to_i
         when ?~              then @stack.push STDIN.getc.ord
         when ?.              then print @stack.pop
         when ?,              then print @stack.pop.chr

            #arith & logic 
         when ?+,?-,?*,?/,?%  then alu(c.chr)
         when ?`              then cmp
         when ?!	      then negate

            #stack manip
         when ?:              then @stack.push @stack.last
         when ?\\	      then swaplast2
         when ?$	      then @stack.pop   #just discard

            #mem manip
         when ?g	      then get
         when ?p              then put

	    #debug
	 when ?=	      then show_program

         end
      end
      nextpos
   end

   def debugenable
      @debug = true
   end
end


vm = VM.new

if ARGV[0] == "-d"
   ARGV.shift
   vm.debugenable
end

begin
    vm.load_program(File.open(ARGV[0]).read)
rescue 
    puts "#{$0}: file not found"
    exit
end

while vm.step
end

debug専用の変な命令 '='を入れてしまった。 -d 時に = を読むと全プログラムスペースを表示する。

"egoh@...."44p45p46p47p48pv
0123v              =      <
pos: (0 , 0) mnemonic: " dir: 3 stack: 
pos: (1 , 0) mnemonic: e dir: 3 stack: 
pos: (2 , 0) mnemonic: g dir: 3 stack: e
pos: (3 , 0) mnemonic: o dir: 3 stack: e,g
pos: (4 , 0) mnemonic: h dir: 3 stack: e,g,o
pos: (5 , 0) mnemonic: @ dir: 3 stack: e,g,o,h
pos: (6 , 0) mnemonic: . dir: 3 stack: e,g,o,h,@
pos: (7 , 0) mnemonic: . dir: 3 stack: e,g,o,h,@,.
pos: (8 , 0) mnemonic: . dir: 3 stack: e,g,o,h,@,.,.
pos: (9 , 0) mnemonic: . dir: 3 stack: e,g,o,h,@,.,.,.
pos: (10 , 0) mnemonic: " dir: 3 stack: e,g,o,h,@,.,.,.,.
pos: (11 , 0) mnemonic: 4 dir: 3 stack: e,g,o,h,@,.,.,.,.
pos: (12 , 0) mnemonic: 4 dir: 3 stack: e,g,o,h,@,.,.,.,.,4
pos: (13 , 0) mnemonic: p dir: 3 stack: e,g,o,h,@,.,.,.,.,4,4
pos: (14 , 0) mnemonic: 4 dir: 3 stack: e,g,o,h,@,.,.,.
pos: (15 , 0) mnemonic: 5 dir: 3 stack: e,g,o,h,@,.,.,.,4
pos: (16 , 0) mnemonic: p dir: 3 stack: e,g,o,h,@,.,.,.,4,5
pos: (17 , 0) mnemonic: 4 dir: 3 stack: e,g,o,h,@,.,.
pos: (18 , 0) mnemonic: 6 dir: 3 stack: e,g,o,h,@,.,.,4
pos: (19 , 0) mnemonic: p dir: 3 stack: e,g,o,h,@,.,.,4,6
pos: (20 , 0) mnemonic: 4 dir: 3 stack: e,g,o,h,@,.
pos: (21 , 0) mnemonic: 7 dir: 3 stack: e,g,o,h,@,.,4
pos: (22 , 0) mnemonic: p dir: 3 stack: e,g,o,h,@,.,4,7
pos: (23 , 0) mnemonic: 4 dir: 3 stack: e,g,o,h,@
pos: (24 , 0) mnemonic: 8 dir: 3 stack: e,g,o,h,@,4
pos: (25 , 0) mnemonic: p dir: 3 stack: e,g,o,h,@,4,8
pos: (26 , 0) mnemonic: v dir: 3 stack: e,g,o,h
pos: (26 , 1) mnemonic: < dir: 1 stack: e,g,o,h
pos: (25 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (24 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (23 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (22 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (21 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (20 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (19 , 1) mnemonic: = dir: 2 stack: e,g,o,h
"egoh@...."44p45p46p47p48pv                                                     -
0123v              =      <                                                     -
                                                                                -
                                                                                -
    .                                                                           -
    .                                                                           -
    .                                                                           -
    .                                                                           -
    @                                                                           -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                -
                                                                                
pos: (18 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (17 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (16 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (15 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (14 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (13 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (12 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (11 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (10 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (9 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (8 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (7 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (6 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (5 , 1) mnemonic:   dir: 2 stack: e,g,o,h
pos: (4 , 1) mnemonic: v dir: 2 stack: e,g,o,h
pos: (4 , 2) mnemonic:   dir: 1 stack: e,g,o,h
pos: (4 , 3) mnemonic:   dir: 1 stack: e,g,o,h
pos: (4 , 4) mnemonic: . dir: 1 stack: e,g,o,h
pos: (4 , 5) mnemonic: . dir: 1 stack: e,g,o
pos: (4 , 6) mnemonic: . dir: 1 stack: e,g
pos: (4 , 7) mnemonic: . dir: 1 stack: e
pos: (4 , 8) mnemonic: @ dir: 1 stack: 
hoge