Brainfuckとは
Brainfuckはプログラミング言語の一つで、特に難解プログラミング言語というのに分類されてます。ジョーク的なプログラミング言語で、チューリング完全なのでC言語と計算能力としては同等です。では、その内容を紹介します。
構成
Brainfuckは簡単に次の要素で構成されます。
- Brainfuckプログラム
- バイトの配列
- インストラクションポインタ(プログラムカウンタのようなもの)
- データポインタ(配列の位置を指すポインタ)
- 入力と出力のバイトストリーム
ただし、以降の説明でデータポインタのことを「ポインタ」と言うことにします。
命令
Brainfuckプログラムは次の8つの命令のみで作られます。
-
>
ポインタをインクリメントする。 -
<
ポインタをデクリメントする。 -
+
ポインタの指す先をインクリメントする。 -
-
ポインタの指す先をデクリメントする。 -
.
ポインタの指す先をASCII文字として出力する。 -
,
入力から1バイトのみをASCII文字として読み、そのコードをポインタの指す先に代入する。 -
[
ポインタの指す先が0なら対応する次の]
までポインタをジャンプさせる。 -
]
ポインタの指す先が0でないなら対応する一つ前の[
までポインタをジャンプさせる。
上から順により簡単に言うと、1を足す、1を引く、次のバイトに移動、一つ前のバイトに移動、出力、入力、繰り返しの開始、繰り返しの終わり
といった感じです。これらの命令はここのBrainfuck実行環境を見ると非常に理解しやすいかと思います。
プログラム例
<プログラム①>
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.+.+.>++++++++++.
<実行結果①>
ABC(改行)
最初の状態ではバイト列はすべて0で初期化されており、かつポインタはバイト列の先頭を指しています。
プログラムの先頭65個の+
でバイト列の先頭に65を代入しています。
次にポインタの位置をそのままにして.
で出力を命令しています。
65はASCIIコードで"A"ですから、"A"が出力されます。
そしてポインタの位置をそのままに+.+.
で66="B" , 67="C"を出力し、>
でポインタ次のバイトに進めます。
+
を10個使って10を代入し出力します。10はASCIIコードで改行にあたります。
<プログラム②>
+++++++++++++[>+++++>+++++>+++++<<<-]>.>+.>++.
<実行結果>
ABC
まず先頭に13を代入します。これは繰り返しのカウンタとして使います。
[]
の中で2,3,4番目のバイトに5を足して、ポインタをバイト列の先頭まで戻して1を引きます。
これで[]
の中身を13回繰り返すことになり、2,3,4番目のバイトに65がそれぞれ入っていることになります。
ループを抜け出した状態でポインタは先頭にあるため一つ進めて出力、一つ進めて1足して出力、一つ進めて2足して出力をすることで"ABC"を出力しています。
インタプリタを作る
簡単な仕様なのでC言語で十分Brainfuckの実行を再現するインタプリタのようなものが作れます。ということで実際に作ってみました。ソースコードはこれです。
https://github.com/yakishamo/BFI/blob/main/main.c
コマンドライン引数としてBrainfuckのコードを渡すも良し、渡さなければプログラム中で貼り付けることになります。
Brainfuckの仕様では、命令以外の文字は無視して次の命令に進むとされていますが、それでは不便なので上記の8つの記号以外が読み込まれた場合はエラーを吐いてプログラムが死にます。
大まかな流れとしては、コマンドライン引数が与えられているか見て、なければ入力待ち状態にして、Brainfuckプログラムを配列に格納できたらwhile文でswitch文をグルグル回していきます。
実際にBrainfuckしてみました。
$.\Brainfuck.exe
input:: +++++++++++++[>+++++>+++++>+++++<<<-]>.>+.>++.
ABC
成功しました。
まとめのようなもの
BrainfuckのインタプリタはC言語であれば比較的簡単に作ることが出来ました。他の言語でインタプリタを作ってみても、言語によって処理速度が違ったりして面白いかもしれません。また、Brainfuckのように難読化を目指したプログラミング言語を自分で考えてみるのも良さそうです。(自分は考えようとしましたがいいアイデアが浮かびませんでした。)
追記(2021/12/06)
[
と]
の処理で、自分のインタプリタだと2重のループに対応できないことを指摘され、自分でも確認したのでこれの修正を後日行います。