Befunge
前回勢いで書いてしまった奴は全然イケテナカッタみたいで、仕様は洩れまくり。コンマとピリオドの違いもちゃんと実装していなかったし、文字列指定"の動作も間違っていて、文字列中に befunge文法要素が出てしまうとそっちを実行してしまうひどいもんだった。いくら速攻とは家ひどすぎる。修正してみた。
#なぜかバグってるほうをアップしてしまった。再度修正
これで
"@"00pv ^ <
sikibu:befunge kuro$ ./befunge.rb -d selfwrite.bf pos: (0 , 0) mnemonic: " dir: 3 stack: pos: (1 , 0) mnemonic: @ dir: 3 stack: pos: (2 , 0) mnemonic: " dir: 3 stack: 64 pos: (3 , 0) mnemonic: 0 dir: 3 stack: 64 pos: (4 , 0) mnemonic: 0 dir: 3 stack: 64,0 pos: (5 , 0) mnemonic: p dir: 3 stack: 64,0,0 pos: (6 , 0) mnemonic: v dir: 3 stack: pos: (6 , 1) mnemonic: < dir: 1 stack: pos: (5 , 1) mnemonic: dir: 2 stack: pos: (4 , 1) mnemonic: dir: 2 stack: pos: (3 , 1) mnemonic: dir: 2 stack: pos: (2 , 1) mnemonic: dir: 2 stack: pos: (1 , 1) mnemonic: dir: 2 stack: pos: (0 , 1) mnemonic: ^ dir: 2 stack: pos: (0 , 0) mnemonic: @ dir: 0 stack:
のように自己書き換えや、普通にユーザー入力も
&&+.25*,@
sikibu:befunge kuro$ ./befunge.rb add.bf 123 321 444
おk。
カウンタを作ってこんなことも
25*66+8p 0v > 1 + v , : g 6 8 : + : ` ^ @_ HELLO WORLD!!
できる。
sikibu:befunge kuro$ ./befunge.rb hello.bf HELLO WORLD!
残りの仕様も実装完了。こんなもんかな。全然テストしてないけど・・・
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 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 = [] @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 = @prog.map {|l| l.length}.max @height = @prog.length 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(@stack.pop,t) 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)) end def put y = @stack.pop x = @stack.pop v = @stack.pop.chr write_raw(Point.new(x,y), v) 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) else case c #control when ?v,?<,?>,?^ then @dir = DIR[c.chr] when ?_ then changedir(@stack.pop, RIGHT, LEFT) when ?| then changedir(@stack.pop, UP, DOWN) 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 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 end end nextpos end def debugenable @debug = true end end vm = VM.new if ARGV[0] == "-d" ARGV.shift vm.debugenable end vm.load_program(File.open(ARGV[0]).read) while vm.step end