Python SyntaxError Programming を解析してみた

yoshiori
2010-12-23

こんにちはこんにちは!!
id:Yoshioriです.
sym - JPerl Advent Calendar 2010 ……濃すぎですよね><
「記号プログラミングを考え出したりする人達は凄すぎ>< ついていけない><」
みたいに考えちゃってる人はいませんか? 僕は考えちゃってました>< でも,それはもったいないです!!!
そんな僕みたいな一般人でも,書かれた記号プログラミングを解析するという楽しみがある事に気が付きました.
なので今日はそんな「記号プログラミングを解析する」のを実際にどうやるのかを書いてみたいと思います.
題材は id:TAKESAKO「Python SyntaxError Programming」です.
良かったら読みながら手元で iPython などで実際に同じように実行していただくとさらに楽しめるかと思います.

早速読んでみる

#!/usr/bin/python
_=-~-~(()>[]);_,__=_*_,_**_-~-~_;___='%'+`SyntaxError`[_];exec''+___*(__+~-~_)%(_*_^-~__,_*_-~__,~__*__/~_,~-~_*~_,_-__^~-~_*_,-~-~__,~-_*_,_/_^~_*~_,_-~-~_*_,_-~-~_*_,_|~-~_*~_,_+__+__/_,__,~_&_*_,__+_|_*_,_-__^~-~_*_,__^~-_*_,_|~-~_*~_,~-~_*~_,-~__,-~-~__)

これで,
「Hello, Python!」
と表示されます.凄すぎてどうなってるのか全然わかりません><
で,頑張って解析してみました.

まずは,文ごとに分ける

; で一行にされているので,まずは改行して見やすくしてみます.

_=-~-~(()>[]);
_,__=_*_,_**_-~-~_;
___='%'+`SyntaxError`[_];
exec''+___*(__+~-~_)%(_*_^-~__,_*_-~__,~__*__/~_,~-~_*~_,_-__^~-~_*_,-~-~__,~-_*_,_/_^~_*~_,_-~-~_*_,_-~-~_*_,_|~-~_*~_,_+__+__/_,__,~_&_*_,__+_|_*_,_-__^~-~_*_,__^~-_*_,_|~-~_*~_,~-~_*~_,-~__,-~-~__)

まったく見やすくならないですね!! やりましたね!!

まずは1行目を解析してみる

_=-~-~(()>[]);

まず,_ に代入をしているのはわかります.なので右辺を解析してみます.

-~-~(()>[]);

まずは,- も ~ も演算子なので確固のついている () > [] が先に処理されます.で,空のタプルと空のリストを比較しています.
iPython で実行してみると

>>> () > []
True

となります.
ちなみに

>>> () < []
False
>>> () == []
False
>>> () is []
False
>>> () in []
False

という結果になります.これは Python の決め事で,昔の Guido は何でも比較出来るのが正しいと考えてたみたいです.(Python3.0 では比較出来ないみたいです.)
で,話しを戻すと

-~-~True;

になっているわけですね.
~ 記号でビット反転されています.ビット反転が良くわからなかったのですが,ここid:nishiohirokazu に教えてもらってわかりました!!
で,True の数値表現は 1 なので

>>> int(True)
1
>>> ~1
-2
>>> --2
2
>>> ~2
-3
>>> --3
3

と,言う事で _ に 3 を代入している事がわかりました.

2行目を解析してみる

こうなってくると,2行目が見えてきました.

_,__=_*_,_**_-~-~_;

また代入をしているのですが,「,」 が両辺にあるので多項代入だとわかります.右辺をまずはさきほど求めた 3 でおきかえてみます.

_,__=3*3,3**3-~-~3;

になります.

_,__=3*3,3**3-~-~3;

計算すると

_,__=9,32;

になります.
これで,この時点でそれぞれ変数に
_ = 9
__ = 32
が,入っている事がわかります.

3行目を解析してみる

___='%'+`SyntaxError`[_];

まずは代入なので右辺を見ていきます.判明している _ に 9 を入れてみます.

___='%'+`SyntaxError`[9];

バッククートは括れば評価結果を取得できるので SyntaxError を評価すると(これも Python3.0 では repr 関数に置きかわりました)

>>> `SyntaxError`
"<type 'exceptions.SyntaxError'>"

と,文字列を取得できます.

___='%'+"<type 'exceptions.SyntaxError'>"[9];

で,その文字列の配列の9要素目なので「c」が取得できます.

___='%'+'c';
___='%c';

結果として
___ = '%c'
が入っている事になります.

4行目を解析してみる

exec''+___*(__+~-~_)%(_*_^-~__,_*_-~__,~__*__/~_,~-~_*~_,_-__^~-~_*_,-~-~__,~-_*_,_/_^~_*~_,_-~-~_*_,_-~-~_*_,_|~-~_*~_,_+__+__/_,__,~_&_*_,__+_|_*_,_-__^~-~_*_,__^~-_*_,_|~-~_*~_,~-~_*~_,-~__,-~-~__)

ここまでで,
_ = 9
__ = 32
___ = '%c'
が判明しているので,すべて置き換えてみます.

exec''+%c*(32+~-~9)%(9*9^-~32,9*9-~32,~32*32/~9,~-~9*~9,9-32^~-~9*9,-~-~32,~-9*9,9/9^~9*~9,9-~-~9*9,9-~-~9*9,9|~-~9*~9,9+32+32/9,32,~9&9*9,32+9|9*9,9-32^~-~9*9,32^~-9*9,9|~-~9*~9,~-~9*~9,-~32,-~-~32)

あとは計算してみると……

exec''+'%c'*(21)%(112,114,105,110,116,34,72,101,108,108,111,44,32,80,121,116,104,111,110,33,34)

となります.

exec''+'%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c'%(112,114,105,110,116,34,72,101,108,108,111,44,32,80,121,116,104,111,110,33,34)

と,文字コードから文字列を作りだしていて……

exec''+'print"Hello, Python!"'

となっていて
「Hello, Python!」
と表示されます.
以上,解析結果としては,id:TAKESAKO が変態すぐるという事がわかりましたね><
みなさんも是非「記号プログラミングを解析する」という楽しみ方もしてみてください!!
そして解析出来たら僕に教えてください><