yharaさんトークショー

yharaさんの

Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~

Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~

の発売を記念してジュンク堂大阪本店にて行われたトークショー。結構人が集まっていた。
内容としては前半奇妙な本紹介で、後半奇妙な松江紹介が行われた。このネタで本を出すアタリが流石のyharaさんで、このネタで出版しようと思うところが流石のUSU-YAさんなのかな、と良く知りもしないのに夢想してみた。

僕はbrainf*ckとgrassくらいしか知らなかった。Befungeとか面白かった。
仕様とか全然読んでないのだけれど、この本読んであんまり面白くって感動したので勢いでrubyで書いてみた。

                  v          
>v"Hello World!!"0<          
.:                     
^_>@
quartz:pts/3% ruby befunge.rb test.bf
Hello World!!

いいっぽいけど '*'とかわかんね。
うーん。抜け仕様だらけだ。後でちゃんと仕様読んでみよう。
追記:
というか'*'とかは単純に演算子だよね。きっと。追加してみた。

  ",,,"v
v      <
>123456789++++++++..v
v..********123456789<          
>123456789--------..v 
@ .////////123456789<          

かな。

45,362880,-27,0

なんのことはない。というか仕様嫁、とかセルフツッコミ。

ソース

class VM
  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

  def read
    @prog[@curpos.y][@curpos.x].chr
  end

  def initialize(prog)
    @stack = []
    @curpos = Point.new(0,0)
    @dir = RIGHT
    @state = MODE_NORMAL
    @debug = false
    load_program(prog)
  end

  def load_program(prog)
    @prog = prog.split("\n")
    @width  = @prog.map {|l| l.length}.max
    @height = @prog.length
  end

  def doubleQuote
    @state = (MODE_STRING+MODE_NORMAL) - @state
  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

  def step(n=1)
    case c = read 
    when '"'
      doubleQuote
    when 'v','<','>','^'
      @dir = DIR[c]
    when ':'
      @stack.push @stack.last
    when '.'
      print @stack.pop
    when '@'
      return nil
    when '_'
      if @stack.pop == 0
	@dir = RIGHT
      else 
	@dir = LEFT
      end
    when '|'
      if @stack.pop == 0
	@dir = UP
      else 
	@dir = DOWN
      end
    else
      if @state == MODE_STRING
        @stack.push(c)
      elsif ("0".."9").include?(c)
	@stack.push(c.to_i)
      end
    end
    if @debug
      STDERR.puts "pos: #{@curpos} mnemonic: #{read} dir: #{@dir} state: #{@state}"
    end
    nextpos
  end

  def debugenable
    @debug = true
  end
end

vm = VM.new(File.open(ARGV[0]).read)
#vm.debugenable

while vm.step
end

追加分

*** old.rb      2008-12-22 17:39:54.000000000 +0900
--- bef.rb      2008-12-22 17:39:18.000000000 +0900
***************
*** 25,30 ****
--- 25,37 ----
    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},
+   }
+ 
    def read
      @prog[@curpos.y][@curpos.x].chr
    end
***************
*** 85,90 ****
--- 92,100 ----
        else 
        @dir = DOWN
        end
+     when '+', '-', '*', '/'
+       res = OP[c].call(@stack.pop, @stack.pop)
+       @stack.push(res)
      else
        if @state == MODE_STRING
          @stack.push(c)