こんにちは、高木です。
ここのところPHPでC言語の前処理をする話題を連載しています。今回のその一貫なのですが、これまでとはちょっと違うテーマになります。
PHPで前処理して生成したC言語のソースコードは、ほとんどの場合、前処理前のソースコードとは行番号がずれてしまいます。一発でコンパイルが成功して、完動すれば問題ないのですが、コンパイルエラーや警告が発生したり、assertマクロでエラーになったりすると、表示される行番号もファイル名も前処理語のものになるので不便です。
そこで、何とかして元のソースコードの行番号とファイル名を表示させることができないか、考えてみました。最初は、PHPの閉じタグに遭遇するたびにコールバックされる関数を登録できないか探したのですが、どうも無理そうです。しかたがないので、いったん前処理前のソースコードを字句解析して、?>の直後に#line指令を挿入することにしました。
次のコードがそのためのスクリプトです。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?php function get_source_code(string $path) : string { if (!file_exists($path)) return NULL; $contents = file_get_contents($path); $tokens = token_get_all($contents); $code = ''; foreach ($tokens as $token) { if (!is_array($token)) { $code .= $token; } else { switch ($token[0]) { case T_CLOSE_TAG: $line = $token[2]; $code .= '?>' . PHP_EOL; if (strpos($token[1], PHP_EOL) !== false) ++$line; $code .= sprintf("#line %d\n", $line); break; case T_FILE: $code .= "\"$path\""; break; default: $code .= $token[1]; break; } } } return $code; } ?> |
このスクリプトでは、前処理前のスースコードのパスを渡すと、PHPの閉じタグ(?>)の直後に#line指令を挿入することで、元のソースコードの行番号とファイル名に設定しています。それと同時に、__FILE__が書かれた箇所をパス名を表す文字列とすり替えています。T_CLOSE_TAGというのが閉じたタグ、T_FILEというのが__FILE__に相当します。
get_source_code関数は処理した結果のソースコードを文字列として返します。あとは、その文字列をいったんファイルに保存してからPHPにかけてもいいですし、呼び出し元でeval関数などを使って処理してもいいでしょう。
#include指令で取り込むファイルについても、今回と同様の処理を再帰的に行う必要があります。これについては、また別の機会に書きたいと思います。