Brainf*ckを書く
c言語っぽいコードをBrainfuckに変換するものを作っていたので、Brainfuckでコード書いた時のことをまとめておきます。
はじめに
計算元に変更がないコーディングをする。
次の例を見てみましょう。
+++++>++++++++++< //5,10を定義 [>+<-] //5を10に移動
こうすると元から用意されていた5,10はことごとく変更が加えられてしまいます。こうならないように事前にコピーしてから実装したほうがあとでミスが少なくなります。
+++++>++++++++++< //5,10を定義 [>>>+<<<-]>>>[<<<+>>+>-]<<< //5を&3に移動,&3から&0,&2に値を移動 >[>>+<<-]>>[<<+>+>-]<< //10を&3に移動,&3から&1,&2に値を移動
これなら&0,&1に計算元の変数を残しつつ、&2に計算結果を残すことが出来ます。
新しく変数を使うときは0にしておく
[-]
を領域に挿入するだけです。何故かといえば前使って値が変わっていたりすると事故ります。
先程の例で言えば、計算結果は予め初期化する必要があるので
+++++>++++++++++>[-]<< //5,10を定義,&2を初期化 [>>>+<<<-]>>>[<<<+>>+>-]<<< //5を&3に移動,&3から&0,&2に値を移動 >[>>+<<-]>>[<<+>+>-]<< //10を&3に移動,&3から&1,&2に値を移動
これだけのことです。では解説していきます。
文字の出力
[-]//初期化 ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ +++++.//65 = 'A' [-]
ASCIIコード表片手にやってください。簡単ですね。
繰り返し
あらかじめループさせたい数値を別のアドレスにセットしてループ内で引いていきます。
先程の例もよく65回やるのを省いたりバイアスかけるために
>++++++[-<++++++++++>]< +++++.
みたいなコードを見かけます。
int i = 6; char c = 0; while(i){ i--; c++; c++; c++; c++; c++; c++; c++; c++; c++; c++; } c++; c++; c++; c++; c++; putchar(c);
こんなかんじになります。
値のコピー
一時メモリに移してからコピー元とコピー先に移動します。
+++>[-]>[-]<< //&0:コピー元 &1:コピー先 &2:一時メモリ [>>+<<-] //&0 -> &2 >>[<<+>+>-]<< // &0,&1 <- &2
a -> tmp //aは0になる a,b <- tmp//tmpは0になる
足し算
++>+++>[-]>[-]<<< // &0:値1 &1:値2 &2:計算結果 &3:一時メモリ [>>>+<<<-]>>>[<<<+>>+>-]<<< //&0の値を&2に加算 >[>>+<<-]>>[<<+>+>-]<<< //&1の値を&2に加算
最初の例と同じです。コピーすることを基本にするといまいち追いにくいのでこれからは一部省きます。
引き算
足し算とほぼ同じ
++>+++>[-]>[-]<<< // &0:値1 &1:値2 &2:計算結果 &3:一時メモリ [>>>+<<<-]>>>[<<<+>>+>-]<<< //&0の値を&2にコピー >[>>+<<-]>>[<<+>->-]<<< //&1の値を&2から減算
最初の値を入れておいてもう一方の値は引いてやれば終わりです。
掛け算
「値をコピーして足す」という足し算のプロセスを繰り返します。
++>+++>[-]>[-]>[-]<<<< // &0:値1 &1:値2 &2:計算結果 &3,4:一時メモリ >[>>>+<<<-]>>>[<<<+>>+>-]<<<< // 値2を&4にコピー(ループカウンタ) >>>>[-<<<< //&4が0になるまで繰り返す [>>>+<<<-]>>>[<<<+>>+>-]<<< //値1を&2に加算 >>>>]<<<<
加算動作を値2だけ繰り返せば掛け算になります。
while文
[/* do */]
[
に入る前に計算結果のアドレスに移動しましょう。
if文
elseに突入するかのフラグを用意して対応します。
>+< //&0:条件 &1:else突入フラグ [ >[-]< //else突入フラグを折る /* true statement */ ] >[ //else文 [-]< //フラグおる /* false statement*/ > ]<
このようにします。複数のelse文に対応させるならその分だけフラグを用意して
>>>>>[-]+>[-]+>[-]+><<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<<<<<+>>>>>><<<<<<<<+>>>>>>>>-][-]<<<<<<<<>>[[-]<<>>>>>>[-]>[-]>[-]><<<<<<<<<>>>>>[[-]<<<<</* True */>>>>>]<<<<<>>]<<>[>>>>>>>+<<<<<<<-]<>>>>>>>>[<<<<<+>>>>><<<<<<<+>>>>>>>-][-]<<<<<<<<>>>[[-]<<<>>>>>>>[-]>[-]><<<<<<<<<>>>>>>[[-]<<<<<</* Else 1 */>>>>>>]<<<<<<>>>]<<<>>[>>>>>>+<<<<<<-]<<>>>>>>>>[<<<<+>>>><<<<<<+>>>>>>-][-]<<<<<<<<>>>>[[-]<<<<>>>>>>>>[-]><<<<<<<<<>>>>>>>[[-]<<<<<<</* Else 2 */>>>>>>>]<<<<<<<>>>>]<<<<
こんなかんじで処理します。もうだるいのでかなり投げやりですが[-]>[-]>[-]<<<
の部分で最初のifに入ったら他のelseに入るフラグを折る+条件分の実行のために変数とフラグ両方で検証(前の例はifに限ってフラグがない)するようにします。
for文
初期化文とインクリメントする文を付加してwhileを生成します。(省略)
そろそろ手でやるのがだるくなってきましたので適当にまとめます。
割り算(剰余)
掛け算の要領で引いていく。引くループのたびに結果をインクリメント。もし引く対象が0になった場合、割る数から現在のカウンタを引けば剰余が求まる。
if文等は作ったものを再利用してもよし。
比較
両者の値を引いていって0になった時点での大小関係をif
数値の出力
'0'
を足せばよし。itoa的な処理は10を代入した変数を順番に引いていって0になったら一つ上の桁の変数から1を引くみたいなダウンカウンタを構成する。
今回何をしていたかというと
cっぽいコードからbfのコードを生成するためにIEnumerable<char>
いっぱいのコードを書いてました。
条件文、比較分、制御構文が使えるので変数のアドレスさえ自前で用意してやれば
FizzBuzzっぽい何かが定義できました。
めでたしめでたし。