Brainf*ck

 

はじめに

なんか、Brainf*ckのページでも作ってみようかなーなんて。
いっしょに楽しく学びましょう。

言語の特徴

命令が8つしかない。以下はWikipediaからの引用。

  1. > ポインタをインクリメントする。ポインタをptrとすると、C言語の「ptr++;」に相当する。
  2. < ポインタをデクリメントする。C言語の「ptr--;」に相当。
  3. + ポインタが指す値をインクリメントする。C言語の「(*ptr)++;」に相当。
  4. - ポインタが指す値をデクリメントする。C言語の「(*ptr)--;」に相当。
  5. . ポインタが指す値を出力する。C言語の「putchar(*ptr);」に相当。
  6. , 1バイトを入力してポインタが指す値に代入する。C言語の「*ptr=getchar();」に相当。
  7. [ ポインタが指す値が0なら、対応する ] までジャンプする。C言語の「while(*ptr){」に相当。
  8. ] ポインタが指す値が0でないなら、対応する [ にジャンプする。C言語の「}」に相当。

処理系

コンパイラ

The Brainfuck Archiveの、 impl/compilers/BF2C.c を保存します。

BF2C.cをCのコンパイラでコンパイルすると、BrainfuckのソースをCのソースに変換してくれるプログラムができあがるので、
Cに変換されたソースをもう一度Cコンパイラでコンパイルして実行してください。
こんな感じ。

 program.bf ー(BF2C.exe)→ program.c ー(Cコンパイラ)→ program.exe

インタプリタ

UNIX系OSでは、impl/interp/ 以下にインタプリタもあるようなので、そちらを使って #!/usr/bin/bfi などとした方がラクでしょう。

http://4mhz.de/bfdev.html こんなんあったわよ

Webインタプリタ

Cをコンパイルするのが面倒だとか、メモリの中身や実行途中の様子を観察したい場合は、Webインタプリタが便利です。

ONLINE BrainF*ck interpreter for JavaScript

断り書き

以下、このコーナーでは「メモリに先頭からa, b, c, d, … と値がはいっており、ポインタは先頭から3番を指している」という状態を、

 [a][b][c][d]…
     ↑

と書くことにします。

また、メモリのn番目を指したいとき、矢印の横に (n) と表記します。
例えば「メモリの99番目から順に e, f, g, h, … と値がはいっており、ポインタは先頭から100番目を指している」という状態を、

 …[e][f][g][h]…
    ↑(100)

と表します。
(追記してくださる方はこのルールに従うか、独自の書き方をする場合はその見方を書いてくださるとありがたいです。)

Hello, World!

さて、WikipediaのHello worldプログラムの一覧に、こんなコードが載っております。

 +++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.
 ------------.<++++++++.--------.+++.------.--------.>+.

これを実行するとたしかに Hello, world! と表示されますが、最初はわけがわからないのでちょっと解説を。

まず一つ目の +++++++++ ですが、これは次の [ ] ループのためのループ回数の設定です。これを念頭に置いてコードを読むと良く分かります。

次に [ がありますが、今ポインタの内容は最初に9まで増やしたので、0ではありません。従って ] までジャンプはせずに、中に入ります。
そして >++++++++>+++++++++++>+++++<<<- が実行され、メモリの内容は

 [8][8][11][5][0][0]…
 ↑

となります。

[ ] ループはメモリの先頭のが0になるまで(つまり9回)繰り返され、ループが終わった時点でメモリの内容は

 [0][72][99][45][0][0]…
 ↑

となります。

あとは勝手に横から書き換えてみる。

  • 先ず、"Hello"の出力について。
    • 右にポインタを移動して72(H の文字コード)を出力。
    • もう一度右に移動すると99(c の文字コード)が入っているので文字コードを2足して101(e)を出力。
    • 7足して108(l)を2回出力。
    • 3足して111(o)を出力。
  • 次に、", "の出力について。
    • 一つ右にポインタを移動すると45(- の文字コード)が入っているので1引いて44(,)を出力。
    • 12引いて32( (空白))を出力。
  • 更に、"world"(Worldにあらず)の出力について。
    • 一つ左にポインタを戻し、8足して119(w)を出力。
    • 8引いて111(o)を出力。
    • 3足して114(r)を出力。
    • 6引いて108(l)を出力。
    • 8引いて100(d)を出力。
  • 最後に、"!"(感嘆符)の出力について。
    • 一つ右にポインタを移動してから1足して、33(!)を出力。


この段階でメモリの内容は

 [0][72][100][33][0][0]…
                ↑

となります。

if構文

続くとか言っておきながら続く気配がないので勝手に横から書いてみる。

入力した文字が「@」かどうか判別する。

>,<++++++++[>--------<-]>>+<[>]>[~~@の場合>>]<<<[~~@ではない場合[-]]

では先頭から説明していきます。

 >,

 [0][s][0][0][0]
     ↑

仮に入力された文字コードをsとしておきます。一番左のメモリは次に使うので空けています。

 <++++++++[>--------<-]>

先ほど空けた一番左のメモリに戻って8を足し0になるまで1を引いて右のメモリから8を引きます。ループが終わり>が実行されると

 [0][s-64][0][0][0]
     ↑

となります。

 >+<

 [0][s-64][1][0][0]
     ↑

 [>] sが64(@)の時、s-64=0なので、

 [0][0][1][0][0]
    ↑

なにも起きません。 sが64以外(@ではない)時、s-64≠0なので、

 [0][s-64][1][0][0]
                ↑(4)

結果2つ右にポインタが移動します。

 >

さらに一つ右に移動するので、 sが64の時

 [0][s-64][1][0][0]
             ↑

sが64以外の時

 [0][s-64][1][0][0]
                    ↑(5)

ポインタが指す値はs==64?1:0となりました。

 [~~@の場合>>]

ここで[ ]が来ると、sが64ではない時何も起こらず sが64の時だけ中が実行されます。 文字を出力する、という文を書く場合はメモリの(1)~(5)を弄ったり変更しないように注意してください。[]内の最後に>>を付けてるのはポインタを(5)に移動させる、という意味です。 これは、sが64ではない時、つまり[]内を実行しない場合の結果とポインタが同じ位置に持ってこれるのと、さらに、ポインタ(5)の値は0のはずなのでループを一回に抜けることができます。

 [0][s-64][1][0][0]
                    ↑(5)

sが64かどうかにかかわらず結果こうなります。 さらに

 <<<

 [0][s-64][1][0][0]
       ↑(1)

sが64ではない時、ポインタが指す値は0ではないので

 [~~@ではない場合[-]]

[]内を実行できます。C言語等のelse {~~}に相当します。[]内の最後に[-]を付けているのは、ループを抜けるためにメモリの値を0にする、という意味です。ですので場合によっては必要ないかもしれません。

if構文その2

上同様、入力値が@(ASCII:64(10))かどうかを判定する。

 ,>++++++++[-<-------->]<[#@以外の処理>>-<]>+[#@の時の処理]

説明は省略。

FizzBuzz

++++++[->++++> >+>+>-<<<<<]>[<++++> >+++>++++> >+++>+ 
++++>+++++> > > > > >++> >++<<<<<<<<<<<<<<-]<++++>+++ 
>-->+++>-> >--->++> > >+++++[->++>++<<]<<<<<<<<<<[->- 
[> > > > > > >]>[<+++>.>.> > > >..> > >+<]<<<<<-[> > > >]>[<+ 
++++>.>.>..> > >+<]> > > >+<-[<<<]<[[-<<+> >]> > >+>+<<<<< 
<[-> >+>+>-<<<<]<]>>[[-]<]>[> > >[>.<<.<<<]<[.<<<<]>]>.<<<< 
<<<<<<<] 

Brainf*ck系言語

命令置換系

Brainf*ckの性質上、 [ や . などを別の文字列と置き換えることによって、 簡単に別の言語(俺言語)ができてしまう。ここではその一部を紹介しておこう。

  • プログラミング言語「長門有希
    • …,「」および任意の命令区切りを使ってプログラムする言語。
    • 例えば 「………。…。…。…。…。…。…。…。…。…………。……長門有希」こんな風に。
    • でも実態はただの BrainF*ck なのでまともなものはできそうにない。
  • neko mimi Fu**
    • ネコミミ!ネコミミモードキス…したくなっちゃった…おにいさま私のしもべーや・く・そ・く・よフルフルフルムーン  キスキス…
  • Misa
    • みさくら語でかける言語。
    • →や「などを命令セットに使ってるので非常に自由な文章でかけるのが特徴。
    • 一見ただの日本語文章のようだが、実行すると…?的な暗号用途に使えるかもしれない。

置換表

Brainfuckneko mimi Fu**Misaプログラミング言語「長門有希」あえぎ声記述言語北斗脳裂斬C言語
>ネコミミ!> → 〜 ー………ああッptr++;
<ネコミミモード< ← ★ ☆…………んんッ!ptr--;
+おにいさま+ あ ぁ お ぉあッ(*ptr)++;
-私のしもべー- っ ッ……んッ(*ptr)--;
.や・く・そ・く・よ. !………………イク!イっちゃう!おまえはもう死んでいるputchar(*ptr);
,フルフルフルムーン, ?……………入ってるの感じちゃうぅ!*ptr=getchar();
[キスキス…[ 「 『してぇ!お願い!while(*ptr){
]キス…したくなっちゃった…] 」 』やめないでぇ!}
*プログラミング言語「長門有希」は各命令を。等で区切る必要がある。

仕様拡張系

Brainf*ckでは無理な(或いは膨大な処理が必要になる)基礎処理を追加したり、独自拡張言語も存在している。

  • BrainCrash
    • bit演算が命令に加えられたもの。
    • 0バイトで"Hello, world!"が出力できる言語でもある。

参考資料