perlcompile - Perl コンパイラ・トランスレータの解説
Perl は常にコンパイラを持っています: ソースコードは内部コード (構文木) に 変換され、実行する前に最適化されます。 バージョン 5.005 以降、Perl は最適化された構文木 (B
) を 調査できる能力があるモジュールとともに配布されており、 このツールは Perl からネイティブな実行ファイルにコンパイルできる C のコードに 変換するモジュールのような様々な便利なユーティリティを記述するのに 用いられています。
B
モジュールは構文木にアクセスすることを可能にし、その他のモジュール (「バックエンド」"back ends") はその構文木を利用します。 また、人間がいくらか読みやすく出力するものもあります。 その他の用途としてはサブルーチンの繋がりや変数がどこで使用されているかなどの クロスリファレンスを生成するのに用いられています。 また疑わしい構造に関してコードのチェックにも用いられています。 もう一つのバックエンドとしてコードを整頓し美しく読みやすい Perl ソースとして 再出力します。
元々の目的が Perl プログラムと等価な C 言語のコードを提供し、ネイティブな 実行可能ファイルにすることであったので、本来何もコンパイルはしないのですが、 現在 B
モジュールは「コンパイラ」として知られています。 このコンパイラは正確に言うとトランスレータやインスペクタに近いですが、 人々は Perl にインスペクタツールではなくコンパイラオプションを 持ってほしいと思っています。 あなたには何ができますか?
この文書は Perl コンパイラについて解説しています: Perl コンパイラを 構成しているモジュール、バックエンドモジュールの使い方、そしてそこに どのような問題があるかです。
コンパイラのバックエンドは B::
以下に入っていて、(あなたや、コンパイラ 利用者が使うであろう) フロントエンドは O モジュールです。
以下は知っておくべき重要なバックエンドのリストで、それぞれ 0 (今後 実装するためのアウトラインの段階) から10 (もしバグが あったらとても驚きます) のステータス番号は作業進行度を表しています:
あなたのコードのに疑わしい部分があれば警告します。 ステータス: 6 (適切に動作しますが、限られたエリアしかチェックしません)。
フォーマットが一貫しているか注意しながらの Perl ソースの再構成を行います。 ステータス: 8 (ほぼ正確に動作しますが、いくつかの不明瞭なものは 欠けています)。
変数やサブルーチンの宣言と使用を報告します。 ステータス: 8 (よく動きますが、まだいくつかの根深いバグがあります)。
以下の節ではどのようにコンパイラのバックエンドを利用するかを解説します。 これらはおよそ成熟度の順に並んでいるので、最も安定していて 検証されているバックエンドを最初に解説し、最も実験的で不完全な バックエンドを最後に解説します。
O モジュールは -c フラグを Perl に渡すことで自動的に有効になり、 Perl はコードを実行せずにコンパイルだけを行います。 これが全てのバックエンドが以下のように表示する理由です:
myperlprogram syntax OK
すべての出力に先立ってこのような表示がなされます。
クロスリファレンスを作成するバックエンド (B::Xref) は変数の宣言や サブルーチンの使用法などの分析を行ったレポートを作成します。 例えば、以下は Perl に同梱されている pod2man プログラムのレポートです:
Subroutine clear_noremap
Package (lexical)
$ready_to_print i1069, 1079
Package main
$& 1086
$. 1086
$0 1086
$1 1087
$2 1085, 1085
$3 1085, 1085
$ARGV 1086
%HTML_Escapes 1085, 1085
これは clear_noremap
で用いられている変数を表示しています。 $ready_to_print
という変数は my()(レキシカル) 変数で、1069 行目で (my() で宣言されて)導入され、1079 行目で使われています。 main パッケージの $&
という変数は 1086 行目で使われている、などです。
行番号は以下の接頭辞を伴って出力されます:
レキシカル変数が最初に (my() で宣言されて) 導入された。
サブルーチンまたはメソッド呼び出し。
サブルーチンが定義された。
フォーマットが定義された。
クロスリファレンスを作成するにあたり最も有用なオプションとしてレポートを 複数のファイルに分割して保存するというものがあります。 たとえば myperlprogram のレポートと report ファイルに保存するには:
$ perl -MO=Xref,-oreport myperlprogram
Deparse バックエンドはあなたの Perl ソースが Perl コンパイラにどのように パースされたかを表示します。 この出力は読みやすいように整形することができます。 基本的な使用法は以下の通りです:
$ perl -MO=Deparse myperlprogram
出力を見ると、Perl がコードをどのように整形すべきか 分かっていないことが分かるでしょう。 あなたはコードのブロックごとに自分で改行することになるでしょう。 しかし、その作業は以下の一行野郎で可能です:
$ perl -MO=Deparse -e '$op=shift||die "usage: $0
code [...]";chomp(@ARGV=<>)unless@ARGV; for(@ARGV){$was=$_;eval$op;
die$@ if$@; rename$was,$_ unless$was eq $_}'
-e syntax OK
$op = shift @ARGV || die("usage: $0 code [...]");
chomp(@ARGV = <ARGV>) unless @ARGV;
foreach $_ (@ARGV) {
$was = $_;
eval $op;
die $@ if $@;
rename $was, $_ unless $was eq $_;
}
逆コンパイラは生成するコードに対するオプションを持っています。 例えば、インデントの量を (上述のような) 4 から 2 に変更できます:
$ perl -MO=Deparse,-si2 myperlprogram
-p オプションは普通省略可能なところにかっこを挿入します:
$ perl -MO=Deparse -e 'print "Hello, world\n"'
-e syntax OK
print "Hello, world\n";
$ perl -MO=Deparse,-p -e 'print "Hello, world\n"'
-e syntax OK
print("Hello, world\n");
その他のフォーマットオプションは B::Deparse を参照してください。
Lint バックエンド (B::Lint) は良くないスタイルのプログラムを調査します。 あるプログラマの間違ったスタイルは他のプログラマの学習を促進します; よってどのような事について警告するかオプションで設定できます。
スタイルチェッカーをソースコードに対して実行するには:
$ perl -MO=Lint myperlprogram
コンテキストのチェックと未定義のサブルーチンを無効にするには:
$ perl -MO=Lint,-context,-undefined-subs myperlprogram
その他のオプションについては B::Lint を参照してください。
このモジュールは Perl プログラム内の機構を内省する (Java の用語では「リフレクションする」) モジュールで、Perl プログラムが その内部を調べられるようにします。 バックエンドモジュールはコンパイルされた構文木にアクセスする機能を提供します。 バックエンドモジュールのユーザーは B に関する経験は問われません。
このモジュールはコンパイラのバックエンドのフロントエンドモジュールです。 通常では以下のような感じで利用します:
$ perl -MO=Deparse myperlprogram
これは use O 'Deparse'
があなたのプログラムに含まれているようにします。
このモジュールは簡潔な (しかし完全な) Perl 構文木を表示します。 このモジュールの出力は B::Terse や B::Debug よりもカスタマイズ可能です (またそれらをエミュレートすることも可能です)。 このモジュールはバックエンドを書いている人や、Perl の内部構造を学びたい人に 有用です。 平均的なプログラマには役に立たないでしょう。
このモジュールは Perl 構文木を詳細に STDOUT にダンプします。 このモジュールはバックエンドを書いている人や Perl の内部構造を学びたい人に 有用です。 平均的なプログラマには役に立たないでしょう。
このモジュールは Perl コードのコンパイルされた構文木を提供します。 このモジュールは他人の書いたコードをデバッグ再構成しようとしている人に 役立つでしょう。 また自分のコードを綺麗に出力するのにも役立ちます。 使用法の詳細は "The Decompiling Back End" を参照してください。
このモジュールはコンパイルされたソースコードを分析します; これは 一部の人は難色を示すものの、警告を出すほど悪いものではないものです。 例えば、配列を scalar(@array)
することなくスカラコンテキストとして 使用するというのは Lint が識別できるものです。 使用法の詳細は "The Lint Back End" を参照してください。
このモジュールは関数内およびファイル内で使用されている my() で 宣言された変数を表示します。 myperlprogram 内で定義された mysub() サブルーチン内で使用されている my() で宣言された変数を取得するには以下のようにします:
$ perl -MO=Showlex,mysub myperlprogram
myperlprogram 内で使用されている my() で宣言された変数を取得するには 以下のようにします:
$ perl -MO=Showlex myperlprogram
[BROKEN]
このモジュールは構文木の内容を出力しますが、B::Debug より得られる 情報は少ないです。 比較のための例として print "Hello, world."
について B::Debug は 96 行もの 情報を出力しますが、B::Terse は 6 行しか出力しません。
このモジュールは自分のプログラムのバックエンドを書いている人及び Perl の 内部について学ぼうとしている人に役立つでしょう。 平均的なプログラマには役に立たないでしょう。
このモジュールは変数、サブルーチン、プログラム内で使用されたフォーマット、 モジュールのロードそれぞれの場所についてレポートを出力します。 使用法の詳細は "The Cross Referencing Back End" を参照してください。
BEGIN{} ブロックはコードがコンパイルされる段階で実行されてしまいます。 ファイルを開いたり、データベース接続を初期化したりといった、 BEGIN{} 内での外部状態の初期化は正しく動作しません。 これに対応するに、Perl には INIT{} ブロック構文があります; この構文はあなたのコードがコンパイルされたあと、実行される前に 処理されます。 実行される順序: BEGIN{} ブロック、(コンパイラのバックエンドを用いて状態を 保存できます) 、INIT{}ブロック、コードの実行、END{}ブロック。
このドキュメントは Nathan Torkington によって書かれ、現在は perl5-porters のメーリングリスト perl5-porters@perl.org によって 保守されています。