前回紹介した自作Brainfuck系言語の「INSTEP」を使って、Quineを書く話
・Quineってなんや
自身のソースコードと完全に同じ文字列を出力するプログラムのこと。Wikipediaを参照されたし。ただし、入力を受け取るものや空文字列はQuineとはみなさない。
・Quineを書く一般的方針
一般的な話をする。抽象的すぎるが。
Quineは大きく分けて2つの部分(A、Bとする)に分けられる。
- Aは、Bそのものの文字列である。
- Bは、A+B (= 自身のソースコード)を出力するプログラムである。
ここで気を付けたいのは、Bは、Aに全く依存してはいけないということである。なぜなら、AはBに依存しているので、無限ループになってしまうからである。
つまり、Bは、Aだけから「Aそのものの文字列」を生成しなければいけない。
(なるほどわからん)
・実際に書いていきたい
前回紹介したINSTEPは、本家Brainfuckの ,
以外の命令を使えるので、本家BrainfuckでQuineならINSTEPでも当然Quineになる。しかしそれだと面白くないので、今回はINSTEPで追加した左ビットシフト・右ビットシフト命令 ^
・v
を使うという条件で書いてみる。
・方針
A部分には、データ領域にB部分の文字列データを書き込むプログラムを記述する。今回は単純に文字コードをそのまま書き込むようにする。例えば、ソースコード上の文字+
を表現するために、文字コード43 = 101011
をデータ領域に書き込む。すなわち、A部分では+^^+^^+^+>
と書くことになる。
B部分では、データ領域に書き込まれた情報からA部分を表すデータを錬成する。たとえばデータ領域に43
があったら、それを+^^+^^+^+>
に変換する機構を書くことになる。具体的な処理としては、下位ビットから順に値が1か0かを判別し、1なら +^
を、0なら ^
のみを書き込むという形になる。
・B部分からつくってイク
B部分のソースコードはこうなる。
>>>>>>-<<<<<<
[
[>>+>+>+>+<<<<<-]
+^^^
[
>>>v^
[-<->]
<[>>[>]+^^+^^+^+[<]<-]
>>v
[
[>]+^^+^+^+^+^[<]
>[<<<+>>>-]
]
<<<
[>+>+>+<<<-]
<-
]
>>>>>[>]+^+^+^+^+^[<]
<<<<<
]
>>>>>>[>]<+
[-.<+]<[.<]
1行目の>>>>>>-<<<<<<
は、A領域とB領域の境界の値として-1
を設定する。間に5バイトの空白を入れるのは、ここが今から説明する作業を行う領域になるからだ。
以降、図を使って説明していくことにする。例えば、A領域の右端のデータが43
だったとすると、現時点でのデータ領域は図のようになる。↓p
はデータポインタを表す。また、便宜上、図中で左からnバイト目を{n}
と書く。例えばここでは{7} = -1
である。
3行目の[>>+>+>+>+<<<<<-]
で、{1}
のデータを{3}
~{6}
の4か所にコピーする。Brainfuckの命令は破壊的な変更をするので、大量にコピーを取っておく必要がある。
4行目の+^^^
で、{1}
の値を8に設定する。これはカウンターとしてはたらき、何ビット目を処理しているかを示す。
6行目の>>>v^
で、{4}
の下位1ビットが切り捨てられる。
7行目の[-<->]
で、{3}
から{4}
を減算する。
8行目の<[>>[>]+^^+^^+^+[<]<-]
で、もし減算の結果が1だった場合(= 元のデータの下位1ビットが1だった場合)には、右端まで行って43
を書き込んで戻って来る。
9行目の>>v
で、{5}
を右ビットシフトする。その結果が0でない場合は、右端まで行って94
を書き込んで戻って来る。
戻ってきたら、12行目の>[<<<+>>>-]
で{5}
の値を{2}
に移す。
15行目の[>+>+>+<<<-]
で、{2}
の値を{3}
~{5}
にコピーする。そして、16行目の<-
で、{1}
のカウンターの値を1減らす。
ここまででループが1周したので、カウンターが0になるまで計8回、同じこと(6~16行目)を繰り返す。ループが終わると、18行目の>>>>>[>]+^+^+^+^+^[<]
で右端に62
を書き込んで戻って来る。
これで1バイト分の処理が完了したので、あとはデータ領域の端に到達するまでひたすらに同じこと(3~19行目)を繰り返す。
そして最終的には、A領域とB領域の境界の-1を挟んで 右側にはA部分を表すデータが、左側にはB部分を表すデータが書き込まれた状態になる。ただし、A領域の右端から読み取ったデータをB領域の左端から書き込んでいるので、B領域のデータは右から順に書き出していく必要がある。で、A領域も右端から書き出せるように書き込むA部分のデータ順を逆順にしてやると、B領域からA領域まで一気に書き出せるようになる。
というわけで、21行目の>>>>>>[>]<+
でデータ領域の右端まで移動し、22行目の[-.<+]<[.<]
で書き出していけばB部分のプログラムは終わりである。
・A部分をつくろう
方針のところで述べたように、B部分のソースコードのそれぞれの文字を、その文字コードを入力する命令に置き換えることでA部分が完成する。また前述の通りA部分のデータを逆順にして格納する必要がある。
B部分の文字数はざっと170字といったところなので手作業で出来ないこともないのだろうが、こういう単純な作業は自動化したほうが楽でいい。Python3を使った。
import re
rawcode="""
>>>>>>-<<<<<<
[
[>>+>+>+>+<<<<<-]
+^^^
[
>>>v^
[-<->]
<[>>[>]+^^+^^+^+[<]<-]
>>v
[
[>]+^^+^+^+^+^[<]
>[<<<+>>>-]
]
<<<
[>+>+>+<<<-]
<-
]
>>>>>[>]+^+^+^+^+^[<]
<<<<<
]
>>>>>>[>]<+
[-.<+]<[.<]
"""
purified=re.sub(r"[^]^v<>+.[-]","",rawcode)
rev=purified[::-1]
token=["+","-",".","<",">","[","]","^","v"]
repl=[
"+^^+^^+^+",
"+^^+^+^^+",
"+^^+^+^+^",
"+^+^+^+^^",
"+^+^+^+^+^",
"+^^+^+^^+^+",
"+^^+^+^+^^+",
"+^^+^+^+^+^",
"+^+^+^^+^+^"
]
res=""
for c in rev:
for i in range(9):
if token[i]==c:
res+=(">"+repl[i])
print(res)
28行目でRegexを使って関係ない文字を全部無視している。30行目でスライスを使って文字列をひっくり返し、あとはforで各文字に対して置換するだけである。
出来上がったA部分はこちら。
>+^^+^+^+^^+>+^+^+^+^^>+^^+^+^+^>+^^+^+^^+^+>+^+^+^+^^>+^^+^+^+^^+>+^^+^^+^+>+^+^+^+^^>+^^+^+^+^>+^^+^+^^+>+^^+^+^^+^+>+^^+^^+^+>+^+^+^+^^>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^+^^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^+^+^^+>+^+^+^+^^>+^^+^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^^>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^+^+^^+>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^+^^+^+>+^+^+^+^+^>+^^+^+^+^^+>+^+^+^+^^>+^^+^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+^+>+^^+^+^^+^+>+^+^+^^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^^>+^^+^+^+^^+>+^+^+^+^^>+^^+^+^^+^+>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^^>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+>+^+^+^+^^>+^^+^+^^+>+^^+^+^^+^+>+^^+^+^+^+^>+^+^+^^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^^+^+>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^^+^+>+^^+^+^^+^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^+^^+>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^
実に1843文字である。ヒエ~ッ。
これとさっきのB部分をそのままつなぐと、晴れてQuineとなる。
>+^^+^+^+^^+>+^+^+^+^^>+^^+^+^+^>+^^+^+^^+^+>+^+^+^+^^>+^^+^+^+^^+>+^^+^^+^+>+^+^+^+^^>+^^+^+^+^>+^^+^+^^+>+^^+^+^^+^+>+^^+^^+^+>+^+^+^+^^>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^+^^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^+^+^^+>+^+^+^+^^>+^^+^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^^>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^+^+^^+>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^+^^+^+>+^+^+^+^+^>+^^+^+^+^^+>+^+^+^+^^>+^^+^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+^+>+^^+^+^^+^+>+^+^+^^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^^>+^^+^+^+^^+>+^+^+^+^^>+^^+^+^^+^+>+^^+^^+^+>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^^+^+>+^+^+^+^^>+^^+^+^+^^+>+^+^+^+^+^>+^^+^+^^+>+^+^+^+^^>+^^+^+^^+>+^^+^+^^+^+>+^^+^+^+^+^>+^+^+^^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^^+^+>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^+^+^+^>+^^+^^+^+>+^^+^+^+^^+>+^^+^+^^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^^+^^+^+>+^+^+^+^+^>+^+^+^+^+^>+^^+^+^^+^+>+^^+^+^^+^+>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^+^+^+^^>+^^+^+^^+>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>+^+^+^+^+^>>>>>>-<<<<<<[[>>+>+>+>+<<<<<-]+^^^[>>>v^[-<->]<[>>[>]+^^+^^+^+[<]<-]>>v[[>]+^^+^+^+^+^[<]>[<<<+>>>-]]<<<[>+>+>+<<<-]<-]>>>>>[>]+^+^+^+^+^[<]<<<<<]>>>>>>[>]<+[-.<+]<[.<]
実に2012文字のQuineの完成である。
もっと短くできるという方は挑戦してみてください。