オリジナルのPyPyを手に入れよう。 ~~PyPy Advent Calendar 11日目~~

PyPy Advent Calendar 11日目の担当として[twitter:@yanolab]が書きます。

PyPyとは

PyPyにはややこしいことに、以下の2つの意味があります。公式に別々の名前で呼ぶように呼びかけているみたいです。

1番目をPyPyと呼んで、2番目をRPython Toolchainなんて呼ぶみたいです。

PyPyとRPython Toolchainについての基本的な情報については、[twitter:@ymotongpoo] さんがPyPy Advent Calendar 6日目としてFAQを翻訳されたので、そちらを参照されるのがよいかと思います。

本記事のテーマ

みなさん、PyPyはビルドしたことありますか?おそらくほとんどの人がNoと答えると思います。なぜなら・・・・PyPyのビルドにはものすごーーーく時間がかかります。さらに、ビルドには多量(4GB以上を推奨)のメモリも必要です。ノートパソコンでは絶望的です。以前、2GBのCore2Duoのviaoでビルドを試みたところ、6時間近くかかったあげく、エラーで失敗しました。バキバキと、ものすごい勢いで心が折れます。粉砕されます。ですので、PyPyのビルドには覚悟が必要です(ぉぃ
しかし、プログラマたるもの自分で使う道具(プログラミング言語)にはお気に入りも存在するはずですし、厳選して使っているはずです。職人ともなれば自分で自分の道具をカスタマイズし、自分仕様にして使うなんてことはざらのはずです。
そこで、今回はPyPyをカスタマイズして、自分独自のPyPy(Python)を作ろうじゃないかというテーマで記事を書きます。

準備

まずはレポジトリをローカルに持ってきましょう。

hg clone https://bitbucket.org/pypyja/pypy

上記のアドレスは日本のPyPy使いが利用しているPyPyのレポジトリです。翻訳したドキュメント等をコミットしています。

本題

今回はPythonrubyでおなじみのunless文を追加してみます。
unless文とは、if文の条件が偽の時のみ実行される構文です。要は"if not 条件"と同意です。

unless False:
  print "False"

unless True:
  print "True"
else:
  print "False"

val = 1 unless True else 0
print val

の様に使います。他にもunless elif elseも使えたりします。

RPython ToolchainによるPythonの実装はpypy/interpreter以下に存在します。ですので、unless文を追加する場合はここを編集します。
Pythonの文法はpypy/interpreter/pyparser/data/Grammer2.7に存在します。ここにunless文を追加すれば、字句解析は既存のフレームワークがやってくれるので、pypy/interpreter/astcompiler/astbuilder.pyの構文木を生成する処理に手を加えれば見事完成です。
と、これが正攻法なのですが、今回はちょっとしたチートをおこないますw
(め、めんどくさいからってわけじゃないんだから!シ、シンプルな方法を選んだんだよ!)
上にも書いたようにunless文はif notと同意なので、トークンを切り出すと時にunlessをif notに置き換えてしまいます。字句解析はpypy/interpreter/pyparser/pytokenizer.pyで行っているので、ここに処理を追加します。追加する処理は下記の通りです。

たったこれだけです。もしくは下記のレポジトリをクローンしてもOKです。

hg clone https://bitbucket.org/yanolab/pypy

やっていることは、字句解析処理でトークンに切り出したときに、unlessを見つけたらトークン一覧に追加する前に、解析行のunlessをif notに置き換えてから、もう一度if notのところの処理をやり直させているだけです。これでunlessがif notに置き換わります。if not自体はPythonで元から使えるので他に処理はいりません!なんと楽なことでしょう。
あとはPyPyをビルドするだけでunless文を追加した独自のPython処理系が手に入ります。では早速ビルドしてみましょう!と言いたいところですが、絶対にやめましょうw何時間もかけてビルドして失敗したらたまりません。
PyPyではtestコードを書くことを激しく推奨していますので、pypy/interpreter/test以下にテストコードを書いて実装するのが一番です。
また、以外と便利なのがpy.pyです。PyPyにはPythonで動くPyPy実装があります。それがpy.pyです。実行は至って簡単で、

python pypy/bin/py.py

と実行するだけです。最初にC言語ファイルのビルドが走りますが、PyPyのビルドに比べたら圧倒的に短時間で済みます。このインタプリタで動作を確認したらいよいよビルドです。(testは残念ながらはしょりますw)

PyPyのビルド方法に関しては公式ドキュメントがあるのでそこを参照するといいでしょう。

ubuntuであれば下記のパッケージをインストールしておけばOKです。

sudo apt-get install libffi-dev libssl-dev libncurses-dev libexpat-dev libbz2-dev zlib1g-dev

準備ができたら下記のコマンドを実行します。

cd pypy/translator/goal
python translate.py -Ojit targetpypystandalone.py

後は延々とマンデルブロが表示されるので終わるのを待ちます。ビルド中はひたすら他の作業に打ち込むといいでしょう。
ちなみにRPythonの解析はシングルスレッドで動くのでコアは1個しか使いません。なので、デュアルコアとかクアッドコアよりもシングルで速いCPUの方がPyPyのビルドは速く終わると思います。解析が終わったあとのC言語ファイルのビルドはコア数で動くのでコア数が多い方がお得です。解析だけCPUの速いマシンで行って、C言語ファイルのビルドは別マシンでとかいうのもありかもしれませんね。

以上で今回のエントリは終わりです。

(本当はあと、独自ビルトインモジュールの作り方と独自ビルトイン関数の作り方、基底クラスのいじり方と書こうと思っていましたが、長くなりすぎるのでまたいつかの機会に。)

次は[twitter:@kozo2]さんの担当です!よろしくお願いします。

enjoy your pypy and python life!