読者です 読者をやめる 読者になる 読者になる

2bfコマンド作った 〜 入力文字列を出力するBrainfuckのコードを吐くコマンド

C unix

標準入力から入ってきた文字列を出力するような、Brainfuckのコードを吐くコマンド、2bfを作りました。

インストール方法

 $ git clone https://github.com/itchyny/2bf
 $ cd ./2bf
 $ autoreconf -i
 $ ./configure
 $ make
 $ sudo make install

使い方

 $ echo -n "Hello, world" | 2bf
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.
 $ echo -n "こんにちは、世界。" | 2bf
+++++++++++++[>+++++++++++++++++>++++++++++>+++++++++++++>++++++++++++<<<<-]>++++++.>-.>>---------.<<<.>+.>>.<<<.>-.>++.<<.>.>----------.<<.>.>++++++++++++++.<<.>-.+.<+.>>+++++++++.>+++.<<<+++.>>>-.---------.<<<----.>-.++.

ここで、私たちは出力コードが合っているかどうか確かめなければいけません。
そこで、2bfと同じように、標準入力から入ってくる文字列をBrainfuckのコードとして実行するコマンドbfも作りました。

インストール方法

 $ git clone https://github.com/itchyny/bf
 $ cd ./bf
 $ autoreconf -i
 $ ./configure
 $ make
 $ sudo make install

使い方

 $ echo "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------." | bf
Hello, world
 $ echo "+++++++++++++[>+++++++++++++++++>++++++++++>+++++++++++++>++++++++++++<<<<-]>++++++.>-.>>---------.<<<.>+.>>.<<<.>-.>++.<<.>.>----------.<<.>.>++++++++++++++.<<.>-.+.<+.>>+++++++++.>+++.<<<+++.>>>-.---------.<<<----.>-.++." | bf
こんにちは、世界。


bfも2bfも、どちらも標準入力→変換→標準出力なので、パイプでつなげると「Brainfuckのコードを吐くBrainfuckのコードを吐くBrainfuckのコードを実行して実行して実行することができます。

 $ echo -n "Hello, world" | 2bf
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.
 $ echo -n "Hello, world" | 2bf | bf
Hello, world
 $ echo -n "Hello, world" | 2bf | bf | 2bf
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.
 $ echo -n "Hello, world" | 2bf | bf | 2bf | bf
Hello, world
 $ echo -n "Hello, world" | 2bf | bf | 2bf | bf | 2bf
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.
 $ echo -n "Hello, world" | 2bf | bf | 2bf | bf | 2bf | 2bf
++++++++[>+++++>+++++++++++>++++++++<<<-]>+++.........>+++.>--.<<........>>.<<...........>>.<<.....>>--...<<++.>++.>++.<<+.>>.<<---..+++.---.......+++..---...+++.>>.<<-.+.-............+.>>--.<<---........+++.-........+.---...+++.-......+.-........+.<++++++++++.
 $ echo -n "Hello, world" | 2bf | bf | 2bf | bf | 2bf | 2bf | 2bf
++++++++[>+++++>+++++++++++>++++++++<<<-]>+++........>+++.>--.<<.....>>.<<...........>>.<<........>>--...<<++.>++.>++.<<--...+++.........>>.<<---...+++.>>.<<-..+.>>--..<<........>>++..<<.>>--..<<...........>>++..<<.>>--..<<.....>>++..<<-..+...>>--..<<---..+++.>>++.<<---..+++.>>.<<---..+++.>>--..<<---.+++.>>++..<<.>>--..<<-...+..---...+++.-...+.......---...+++..-...+...---...+++.>>++..<<.>>--..<<-.+.---.+++.-.+............---.+++.>>++..<<-..+.>>--..<<-...+........---...+++.-.+........---.+++.-...+...---...+++.-.+......---.+++.-.+........---.+++.>>.<<---..........+++.<++++++++++.
 $ echo -n "Hello, world" | 2bf | bf | 2bf | bf | 2bf | 2bf | 2bf | bf
++++++++[>+++++>+++++++++++>++++++++<<<-]>+++.........>+++.>--.<<........>>.<<...........>>.<<.....>>--...<<++.>++.>++.<<+.>>.<<---..+++.---.......+++..---...+++.>>.<<-.+.-............+.>>--.<<---........+++.-........+.---...+++.-......+.-........+.<++++++++++.
 $ echo -n "Hello, world" | 2bf | bf | 2bf | bf | 2bf | 2bf | 2bf | bf | bf
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.
 $ echo -n "Hello, world" | 2bf | bf | 2bf | bf | 2bf | 2bf | 2bf | bf | bf | bf
Hello, world

はい、元に戻りましたね。
2bfの出力は、「それなりに」短いコードであって、残念ながら「最も短い」コードとは限りません。
ある文字列を吐く「最も短い」Brainfuckのコードを求める問題は、簡単には解けない問題だと思います。



bfも2bfもマルチバイト文字も正しく扱えます。 (端末の文字コードに従います。コマンド自体は何のコード変換も行いません)

 $ echo -n "ヾ(。>﹏<。)ノ゙" | 2bf
+++++++++++[>+++++++++++++++++++++>++++++++++++>+++++++++++++++++>++++>+++++++++++++++>++++++<<<<<<-]>----.>-.>+++.>----.<<<++++++++++++.>>-.>>----.>----.<<<<<.>>----.<++++++++++++.>>>>--.<<<<<.>>++++.>>.<+.<<<.>>+.<------.<.>>.>>---.
 $ echo -n "ヾ(。>﹏<。)ノ゙" | 2bf | bf
ヾ(。>﹏<。)ノ゙


更に、(NULL文字を含む)バイナリーファイルだって扱えます。

 $ 2bf < 適当なpdfファイル.pdf | bf > out.pdf
 $ diff 適当なpdfファイル.pdf out.pdf
 $


無限長の入力も扱えます。

 $ yes 'Hello, world!' | 2bf
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.<<<++++++++++.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.--------.+++.------.--------.>+.<<<.>.>+.+++++++..+++.>+++++++++++.------------.<++++++++.---- (永遠に続く)
 $ yes 'Hello, world!' | 2bf | bf
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
(永遠に続く)


もちろん、入力がランダムでも大丈夫。 (入力がダンラムだと、出力したコードが正しいかどうか確かめるというわけにはいきませんが…)

 $ 2bf < /dev/random
+++++++++++[>++++++++++++++++>++++++++++++>+++>++++++++++++++++++++>++++++++>+++++++++++++++++++++>++++<<<<<<<-]>++.>-----.>-----.>>>>--.<<<<<<----------.<+++++++++++++.>>>>>>>+++++++++.<<<++.>++.>>+++++++++.<<----.<<<<-------------------.>>>-----------------.<<<++++++++++++++++++++++++.>>>>>>++.<<<<<.>>--------------.<<.>>>>-----.<<+++++++++++++.>+++++++++++++.<<-----.<+++++.>>>>>+++++++++.<--------.>----.<<+.<<<+.------------------.>>>>>++++++.<<<<<<+++++.>>>>++++.<<++.>>>+++++.<<<++++++++++++++++++++.<<++++++++++++.>>------------.+++++++++.>>>>>.<+++++++++++++++.<++++++++++.>>+++++.<<<----.>>---------.<<<<<<<+++++++++.>>+++++++++++++++++++++++.>>>>+++++++++++.<<<+++++.>>>>>-----.<<-.<<--------.<<<--------------------.>>+++.<<----------.>>>>++++.<+++++++++++++++++++++++.>>>---------.<<<<<<+++++++++++++++.>>>---------.<<<+++++++++.<++++++++++++.>>>>>>+++.<<<<<++++++++.>>>+++++++++++++++.>>>>>++++++++++++++++[>++++++++++++++++<-]<<<<<<<<<--.>>>>.>++++++++.>>>+++++++++.<<<<<<++++++.>>--- (永遠に続く)
 $ 2bf < /dev/random | bf
L›føˇà1¿OVG€?_Z*ì9øUØLoö⁄¿AÕÔ˛``/∞∑5±…√dÏ´›÷˝`π‡Ö¶ÒQ∏›ÌfX˙ø^}D Nπ3 ÷ıiEèh4I/ñûB΃≥¥Ú?=w=U‘;ÚÎóòE«!sÇèL©⁄z«”Mp˝¶$N ,∑ ÔŒ-èB¨Båæ¶<4›f≠%ZìríÛàqô“^g˝„1B6«Û°vÏCfi=”fiyK§¨Y«ÕídçpRl6ÿR/¬;íç‡kË,∑P·Ä<#ˇT•‘3u2#ã9Ô–˘¬SÃiíaH‘ë˛L-º‹7cÜ'ÜPȈ)<∞B¡…à3-A©úUY˝j2ı«”/$ÅûMKEB;1Ú…æVÕ–LZ™E#˚´ ãv)M%—®ÎdðfiîªJÍ<.ñKŸ∑[f¬{û3/è≤⁄Z¬Z€Ï’|•”˙¯p˜¥£°ºöéÔ»Iÿw“/n?÷fs0‰[S7∑èŒÅ!I)—5GÃ9¨ƒQ_Ûm† (永遠に続く)

Brainfuck楽しいよ!╭( ・ㅂ・)و ̑̑ グッ!

 $ echo "Brainfuck楽しいよ!╭( ・ㅂ・)و ̑̑ グッ!" | 2bf
++++++++++++[>++++++>++++++++++>+++++++++++++++++++>++++++++++++++>++++++++++++++++>+++++++++++>+++>++++++++++++++++++++<<<<<<<<-]>------.>------.-----------------.++++++++.+++++.--------.+++++++++++++++.------------------.++++++++.>++.>---.>---.<<---.>>>---.<<--------------.<.>>>.+++.<<<.>>>--.++++++.>>-.<<<-.>-------.<<<-.>--.>---------------.>>++++.--------.>.<<<++++++++++++++++.<++++++++++++++++.<+.>>>++++.---.>>.<<<.<.>>>+++++++++.<<<<----------.>>>++++++.>---------.<<<<-------------.>>>+++++++++.<<<.>>>.>.>.<<<.-----.>>>.<<<++++++.<-------.>>>>.<<<-.--------------.>>>.<<<+++++++++++++.>----------------.<<<<<<++++++++++.
 $ echo "Brainfuck楽しいよ!╭( ・ㅂ・)و ̑̑ グッ!" | 2bf | bf
Brainfuck楽しいよ!╭( ・ㅂ・)و ̑̑ グッ!

Brainfuck☆*:.。. o(≧▽≦)o .。.:*☆超楽しいwwww

 $ echo "Brainfuck☆*:.。. o(≧▽≦)o .。.:*☆超楽しいwwww" | 2bf
+++++++++++[>++++++>++++++++++>+++++++++++++++++++++>++++++++++++++>++++>+++++++++++++++++>+++++++++++++++>+++<<<<<<<<-]>.>++++.-----------------.++++++++.+++++.--------.+++++++++++++++.------------------.++++++++.>-----.>--.------------------.>--.<<<<--------.>>>>++++.<<+++++++++++++.>>>++.>----.<<.>>>-.<<<<<<++++.>>>------.<<-------------.>+++.>>>++++++.<<<<.>+++++++++++++.>>.<<<.>-------------.>>>-.<<+.<<<.>>>>>>.<<<+++++.<<+++++++++++++.>>>.>-----.<<.<<<<.>>>>----.<<-------------.>>>>---------.<<<---.<++++++.>>>-------.<<-.<--.>>>>+++++++++++++.<+++++++.<<<---.>----.>>>--------------.<<<<.>.+++.<<++++++++....<<++++++++++.
 $ echo "Brainfuck☆*:.。. o(≧▽≦)o .。.:*☆超楽しいwwww" | 2bf | bf
Brainfuck☆*:.。. o(≧▽≦)o .。.:*☆超楽しいwwww


まとめ

  • 2bfは標準入力の文字列を出力するようなBrainfuckのコードを吐くコマンド
  • bfは標準入力の文字列をBrainfuckのコードとして実行するコマンド
  • 2bfは「それなりに」小さなBrainfuckのコードを出力する
  • 2bfもbfも100行程度のC言語で書かれていて、./configure && make && make installでインストール
  • echo "ほげ" | 2bf | bf は常に「ほげ」 (ほげは何でも良い)
  • echo "ほげ" | 2bf | 2bf | bf | bf も常に「ほげ」 (ほげは何でも良い)
  • echo "ほげ" | 2bf | 2bf | 2bf | bf | bf | bf も常に「ほげ」 (ほげは何でも良い)
  • 2bfもbfも使用するメモリーは一定
  • 2bfもbfも無限長の入力を扱える
  • ただし、bfはループ [...] の長さが一定以上だと扱えない。(エラーを吐いて終了する)
  • 2bfもbfもバイナリーを扱える。2bf < バイナリーファイル | bf > 出力 は常に元のファイルと一致する