vimスクリプトで BrainF*ck - 再実装編
BFの8命令すべてに対応したよ
昨日のエントリーで不完全だった「ジャンプ命令」というか
[ と ] に対応してなかった件、対応してみた。
実装方針も、チューリングな感じにした。かな?
vimスクリプトはこんな感じ。
bf.vim という名前で。
$ cat bf.vim " define let s:GT = 1 " > let s:LT = 2 " < let s:PLUS = 3 " + let s:MINUS = 4 " - let s:DOT = 5 " . let s:CAMMA = 6 " , let s:LB = 7 " [ let s:RB = 8 " ] let s:UNKNOWN = -1 " X " variables let s:lineno = 0 let s:lastno = 0 let s:instruction = "" " instruction memory let s:ip = 0 " instruction pointer let s:stack = [] " stack memory let s:stack_size = 256 let s:sp = 0 " stack pointer let s:ax = 0 " register ax function! s:BFInit() let s:instruction = "" let s:stack = [] let s:ip = 0 let s:sp = 0 let s:ax = 0 " load instruction while s:lineno <= s:lastno let s:instruction = s:instruction . getline(s:lineno) let s:lineno = s:lineno + 1 endwhile " clear stack let l:i = 0 while l:i < s:stack_size call add(s:stack, 0) let l:i = l:i + 1 endwhile endfunction function! s:BFForward() " > let s:ip = s:ip + 1 if s:sp >= s:stack_size - 1 echo "Oops! out of range" return endif let s:sp = s:sp + 1 endfunction function! s:BFBack() " < let s:ip = s:ip + 1 if !s:sp echo "Oops! out of range" return endif let s:sp = s:sp - 1 endfunction function! s:BFIncr() " + let s:ip = s:ip + 1 let s:stack[s:sp] = s:stack[s:sp] + 1 endfunction function! s:BFDecr() " - let s:ip = s:ip + 1 let s:stack[s:sp] = s:stack[s:sp] - 1 endfunction function! s:BFPutChar() " . let s:ip = s:ip + 1 echo nr2char(s:stack[s:sp]) endfunction function! s:BFGetChar() " , let s:ip = s:ip + 1 let l:char = input('input char : ') let s:stack[s:sp] = char2nr(l:char) endfunction function! s:BFWhile() " [ if s:stack[s:sp] == 0 let l:dst = stridx(s:instruction, "]", s:ip) if l:dst == -1 echo "syntax error : ] not found" return endif let s:ax = 0 let s:ip = l:dst + 1 else let s:ax = s:ip let s:ip = s:ip + 1 endif endfunction function! s:BFEndWhile() " ] let s:ip = s:ax endfunction " ----------------------------------- function! s:BFDebug() echo "ip : " . s:ip echo "*ip : " . s:instruction[s:ip] echo "instruction length : " . strlen(s:instruction) echo "sp : " . s:sp echo "*sp : " . s:stack[s:sp] echo "stack depth : " . len(s:stack) echo "ax : " . s:ax endfunction function! s:BFLex() let l:char = strpart(s:instruction, s:ip, 1) if l:char == ">" return s:GT elseif l:char == "<" return s:LT elseif l:char == "+" return s:PLUS elseif l:char == "-" return s:MINUS elseif l:char == "." return s:DOT elseif l:char == "," return s:CAMMA elseif l:char == "[" return s:LB elseif l:char == "]" return s:RB else return s:UNKNOWN endif endfunction " -------------------------------------- function! BFMain() range let s:lineno = a:firstline let s:lastno = a:lastline execute s:BFInit() while 1 let l:ch = s:BFLex() if l:ch == s:GT execute s:BFForward() elseif l:ch == s:LT execute s:BFBack() elseif l:ch == s:PLUS execute s:BFIncr() elseif l:ch == s:MINUS execute s:BFDecr() elseif l:ch == s:DOT execute s:BFPutChar() elseif l:ch == s:CAMMA execute s:BFGetChar() elseif l:ch == s:LB execute s:BFWhile() elseif l:ch == s:RB execute s:BFEndWhile() else break endif endwhile endfunction
hello.bf を用意する
Hello, World! を出力するソースを用意します。
昨日のエントリーにも書いたけど、再掲。
$ cat hello.bf +++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-. ------------.<++++++++.--------.+++.------.--------.>+.
BrainF*ckを実行!
じゃ、vimで BrainF*ckを実行しまっす。
vimでソースファイルを開いて
$ vim hello.bf
次のように実行すると...
:%call BFMain()
Hello, World!
でましたー。
H e l l o , w o r l d ! Press ENTER or type command to continue
今回の実装
今回は、わりとチューリングっぽく
s:instruction をテープに、s:ipを磁気ヘッドにみたてた動きにしました。
s:stack でデータをごにょごにょして
スタックポインタを s:sp ってことで。
空行があっても、止まらなくしてます。
ツッコミどころ
で、やっぱりツッコミどころは満載な感じです><
[ と ] は復帰アドレスというかジャンプ先を格納するために
汎用レジスタっぽく、s:ax を用意したのだけど
axにしてるあたりが中途半端なんですが><
それから、[ と ]の処理は
本来、レジスタじゃなくて、スタックにしないといけないけど
ま、[ と ] はネストしないでね、ということで、とりあえず。
あー、このへんの実装がやっぱ中途半端><
あと、トークンを用意してみたものの
ステートマシンとか用意してないし
先読みとかする必要もないから、あんま意味ねーし、、
ていうか、メソッド、みたいなところでトークンを使わずに
じかに "]" を使っててかえって汚い&バグのもと。
嗚呼、見れば見るほど中途半端。
関数名もあんまよくない。
あと、リエントラントな設計じゃない?とかもあるか。
あと、出力文字列用にプールを用意しといて
終了時に文字列プールを flushするようにすれば
きれいに Hello, World! が出るのだけど。
vimスクリプトを習得する、というのが目的ではあるのだけど
んー、どうかなぁ。
いいのかなぁ、こんなので。
つづく、かもしれない...。
追記
しかも、World じゃなくて world が出るプログラムだったよ><