Python Bytecode短歌
64 00 00 0c 04 04 04 17 04 03 17 02 04 03 17 04 03 03 04 03 03 13 04 04 14 04 17 18 17 0b 53
空関数のco_codeをこれで置き換えて実行すると、2014と表示されます。
詳しい説明は後で。
味わい
04を「ど」03を「ろ」17を「あ」と発音するなら
64 00 00 0c ど ど ど あ ど ろ あ 02 ど ろ あ ど ろ ろ ど ろ ろ 13 ど ど 14 ど あ 18 あ 0b 53
となる。もうちょっと工夫の余地がある。
難しいポイント
変数や関数呼び出しを使うためにはバイトコード部ではなく定数テーブルに名前の文字列を積んで、そのインデックスを指定してLOAD命令を発行する必要がある。短歌以外の部分に意味のある文字列を色々書き込むと「31文字で完結」という短歌の良さが失われるので避けたい。だから変数も関数も使ってはいけない。
定数を使うのはもちろん定数テーブルから読み込む必要がある。上記と同様にそこに書き込むことはやりたくない。ただ、実は空の関数でも定数テーブルには「None」が積まれている。唯一使える定数だ。
定数ロードの命令は3バイト消費するので不用意に使うとあっという間に31文字を使い切る。というわけで冒頭部に1回だけ使う。冒頭の64 00 00が「LOAD_CONST 0」である。
NoneにUNARY_NOT(0c)を使うことでboolへの変換が行われて、結果がTrueになる。Trueに対してBINARY_ADD(17)などの算術演算を使うことでintegerへの変換(True→1)が行われる。これによって数値を作るための種を生み出している。
PythonのVMはスタックマシン。「04(ど)」はスタック先頭を複製する命令DUP_TOP。「03(ろ)」はスタック頭3個を回転する命令ROT_THREE。「17(あ)」がスタックトップ2つを加算して戻す命令BINARY_ADD。
BPT(Byte per Tanka)についての考察
Python Bytecode短歌においてのBPT上限について考察する
上5バイトは上記のコードと同じ、次の7バイトで以下のようにすることで残りがNOPでも618文字出力できる。
ADD, # 2 DUP, # 2 2 ADD, # 4 DUP, # 4 4 POW, # 256 DUP, # 256 256 POW, # 256^256 (about 616 digits)