LoginSignup
4
4

More than 5 years have passed since last update.

Zephirの中身を少し眺めたメモ

Last updated at Posted at 2017-04-20

PHPのサブセットみたいな言語(*.zep)からPHPの拡張モジュール(Cソースコード経由で最終的には*.soになる)を出力するZephirという言語があります。PHPフレームワークPhalconの実装言語としても有名ですね。

このZephirがどうやって*.zepを解釈して*.cを吐き出してるのか気になったので確認してみました。

Zephir入門

Zephirのインストールは「Installation — Zephir 0.9.4 documentation」に沿って行いました。

ドキュメント通りにインストールするとなぜかrootを要求されますが、install-nosudoを使えば$HOME/bin/以下にzephirコマンドがインストールされます。

チュートリアルに沿って下記のように*.zepファイルを作成します。

$ zephir init utils
$ cd utils
$ vi utils/greeting.zep
greeting.zep
namespace Utils;

class Greeting
{

    public static function say()
    {
        var_dump("hello world!");
    }

}

では、さっそくビルドしてみましょう。

$ zephir build

これでエクステンションutils.soがビルドされます。また、sudo権限があれば勝手にPHP拡張モジュールのインストールまでやってくれます。

途中経過で作られたCソースコードext/utils/greeting.zep.cの中身を見てみましょう。

PHP_METHOD(Utils_Greeting, say) {

        zval _0;
        zval *this_ptr = getThis();

        ZVAL_UNDEF(&_0);

        ZEPHIR_MM_GROW();

        ZEPHIR_INIT_VAR(&_0);
        ZEPHIR_INIT_NVAR(&_0);
        ZVAL_STRING(&_0, "hello world!");
        zephir_var_dump(&_0 TSRMLS_CC);
        ZEPHIR_MM_RESTORE();

}

独自マクロだらけですが、確かにPHP拡張モジュールっぽいCソースコードが出力されています。

コンパイラの本体を探す

さて、上記Cソースコードが出力されるまでの処理を追ってみましょう。

zephirコマンドの中身はシェルスクリプトで、最終的にPHPスクリプトcompiler.phpを呼び出しています。その後の処理でビルド専用のPHP拡張モジュールzephir_parser.soを指定してPHPを起動するようになっており、構文解析などはこの拡張モジュール内で実装されていることがわかります。

逆に言うと、zephirコマンドはZephirコンパイル用のPHP拡張に依存しているため、PHPのバージョンが変わるとZephir自体の再インストールが必要になります。

字句解析・構文解析の本体

Zephirの字句解析・構文解析の本体は下記ファイルです。

字句解析にはre2cを、構文解析にはLEMONを利用しています。

*.reファイルを見るとZephirの対応しているトークンが確認できます。

たとえば、下記の定義からZephirでは浮動小数点数の指数形式(1e10のような形式)をサポートしていないことがわかります。

                DOUBLE = ([\-]?[0-9]+[\.][0-9]+);
                DOUBLE {
                        token->opcode = ZEPHIR_T_DOUBLE;
                        token->value = estrndup(start, YYCURSOR - start);
                        token->len = YYCURSOR - start;
                        s->active_char += (YYCURSOR - start);
                        q = YYCURSOR;
                        return 0;
                }

構文解析機本体はruntime/base.czephir_parse_program()です。zephir_get_token()1で字句解析器からトークンを取り出し、zephir_()2で構文解析を行っています。構文解析の結果、*.zep ファイルに対応する構文木がPHPの配列の形で(!)帰ってきます。

Cソースコードの生成

構文木からCソースコードを出力する部分はピュアPHPで記述されています。Library/CompilerFile.phppreCompile()が処理の本体になります。

そこから先の処理はあまり追っていませんが、Library/以下が構文ごとにCソースコードを出力するような処理に対応していると思います。

これだけでもすごい量ですが、自動生成されるCコードから呼び出されるPHP APIのラッパー(Zephir Kernel)もかなりの実装量です。

どういうモチベーションでこんなことをする気になったのか気になりますね。

LLVMベースのJITコンパイル実装の夢

runtime/以下のソースコードを眺めると、LLVMに関係しそうな関数呼び出しがチラホラ存在します。

zephir.c
        if (!ZEPHIRT_GLOBAL(module)) {

                ZEPHIRT_GLOBAL(module) = LLVMModuleCreateWithName("zephir");
                ZEPHIRT_GLOBAL(builder) = LLVMCreateBuilder();

                LLVMInitializeNativeTarget();
                LLVMLinkInJIT();

                if (LLVMCreateExecutionEngineForModule(&ZEPHIRT_GLOBAL(engine), ZEPHIRT_GLOBAL(module), &msg) == 1) {
                        fprintf(stderr, "%s\n", msg);
                        LLVMDisposeMessage(msg);
                        return;
                }

                ZEPHIRT_GLOBAL(pass_manager) =  LLVMCreateFunctionPassManagerForModule(ZEPHIRT_GLOBAL(module));
                LLVMAddTargetData(LLVMGetExecutionEngineTargetData(ZEPHIRT_GLOBAL(engine)), ZEPHIRT_GLOBAL(pass_manager));
        }

これは*.zepのJITコンパイル実装のようです。どうやらZephirはZend Engine上で動くスクリプト言語としての野望も持っているようですね。開発状況としては実験段階のようですが、ロマンを感じます。

まとめ

ZephirはPHPに文法が近いという話を聞いていたので、どうせ言語といっても文字列置換程度かと想像していたんですが、思ったより言語っぽい実装になっていたのは意外でした。

個人的にはLEMONの存在を知ったのが収穫でした。小さいパーサーを作るときに便利かもしれません。


  1. re2cが生成する関数。 

  2. LEMONが生成する関数。変な関数名になっているのはコード生成時にこのプレフィックスを指定したため。 

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4