Befunge
Wikipediaで見つけた、二次元すごろくプログラミング言語。言語使用を読んだら、思ったよりシンプルだったので作ってみました。
この言語、非常に面白いです。
無限ループ
左上から始まり、右向きに移動して、「<」に来ると、左向きに進みます。
左端に到達すると、今度は右から出てきて、「^」に到達します。
今度は上に進み、「>」→「v」→「<」→「^」→「>」→「v」→「<」→「^」って、無限にループします。
<^ v>
Hello World!を表示
さっきより複雑です。
コマンドの詳細はWikipediaを参照してください。Befunge
矢印の順番に進んで、Hに到達した時点でスタックには、[数値の0,!,d,l,r,o,W,<スペース>,o,l,l,e,H]、このようにデータが積まれてています。その後、スタックの0が出てくるまで、ループして一文字ずつ出力して終了します。
v @_ v >0"!dlroW"v v :# < >" ,olleH" v ^ <
インタプリタのソースコードは以下です。
こんな感じで実行すれば動きます。-dオプションをつけると、いろいろ表示されるかも。
ruby befunge.rb {Befungeのソースファイル}
class Befunge E = [1,0] W = [-1,0] S = [0,1] N = [0,-1] def initialize reset init_cmd end def reset @point = [0,0] @direction = E @stack = [] @skip = false @map = nil @height = nil @width = nil @cmd = {} @result = "" init_cmd end def pop @stack.pop end def push x @stack.push x end def init_cmd @cmd["<"] = proc{|c|@direction=W} @cmd[">"] = proc{|c|@direction=E} @cmd["^"] = proc{|c|@direction=N} @cmd["v"] = proc{|c|@direction=S} @cmd["_"] = proc{|c|@direction=(pop==0)?E: W} @cmd["|"] = proc{|c|@direction=(pop==0)?S: N} @cmd["?"] = proc{|c|@direction=[N,E,W,S][rand(4)]} @cmd[" "] = proc{|c| } @cmd["#"] = proc{|c|@skip=true} @cmd["@"] = proc{|c|exit!} @cmd["\""]= proc{|c|@str_mode=true} @cmd["&"] = proc{|c|push STDIN.getc.to_i} @cmd["~"] = proc{|c|push STDIN.getc} @cmd["."] = proc{|c|x=pop;STD_OUT.print x;STDOUT.flush;@result<<x.to_s} @cmd[","] = proc{|c|x=pop;STDOUT.print x.chr;STDOUT.flush;@result<<x.chr} @cmd["+"] = proc{|c|y,x=pop,pop;push x+y} @cmd["-"] = proc{|c|y,x=pop,pop;push x-y} @cmd["*"] = proc{|c|y,x=pop,pop;push x*y} @cmd["/"] = proc{|c|y,x=pop,pop;push x/y} @cmd["%"] = proc{|c|y,x=pop,pop;push x%y} @cmd["/"] = proc{|c|y,x=pop,pop;push x/y} @cmd["`"] = proc{|c|y,x=pop,pop;push(x>y ? 1: 0)} @cmd["!"] = proc{|c|push pop==0?1:0} @cmd[":"] = proc{|c|x=pop;push x;push x} @cmd["\\"]= proc{|c|y,x=pop,pop;push x;push x} @cmd["$"] = proc{|c|pop} @cmd["g"] = proc{|c|y,x=pop,pop;push @map[y][x][0]} @cmd["p"] = proc{|c|y,x,v=pop,pop,pop;@map[y][x]=v.chr} end def read_map(src) lines = src.map{|line|line.chomp} max_len = lines.max{|x,y|x.size<=>y.size}.size @map = lines.map{|line| sprintf("%-*s", max_len, line).split(//)} @height = @map.size @width = max_len end def move() old_x, old_y = @point dir_x, dir_y = @direction x = (old_x + dir_x) y = (old_y + dir_y) y = (y + @height) % @height x = (x + @width) % @width if $DEBUG print "#{[old_x,old_y].inspect} => #{[x,y].inspect}\n" end @point = [x, y] end def cur_ch x,y = @point @map[y][x] end def do_loop while true ch = cur_ch yield ch move end end def eval(src) reset read_map(src) do_loop{|ch| if $DEBUG print_map print "command=\"#{ch}\"\n" print "pos=(#{@point[0]},#{@point[1]}) " + "direction=(#{@direction[0]},#{@direction[1]}) " + "result=[#{@result}]\n" print "stack=#{@stack.inspect}\n" print "\n\n\n" end if @str_mode if ch == "\"" @str_mode = false else push ch[0] end elsif @skip @skip = false elsif ch =~ /\d/ push ch.to_i else @cmd[ch].call(ch) end } end def print_map @map.each do |line| line.each do |ch| print ch end print "\n" end end end b = Befunge.new b.eval ARGF.read
ループとHelloWorld以外で確認したプログラムは以下です。
5の階乗を求める
5 100p:v v *g00:_00g.@ >00p1-:^"
ここののっているプログラムで使っていないコマンドはテストしていないからうまく動かないかも。