B::Deparse - perl コードを生成するPerlコンパイラバックエンド

目次


名前

B::Deparse - perl コードを生成するPerlコンパイラバックエンド


概要

perl -MO=Deparse[,-d][,-fFILE][,-p][,-q][,-l] [,-sLETTERS][,-xLEVEL] prog.pl


説明

B::Deparse is a backend module for the Perl compiler that generates perl source code, based on the internal compiled structure that perl itself creates after parsing a program. The output of B::Deparse won't be exactly the same as the original source, since perl doesn't keep track of comments or whitespace, and there isn't a one-to-one correspondence between perl's syntactical constructions and their compiled form, but it will often be close. When you use the -p option, the output also includes parentheses even when they are not required by precedence, which can make it easy to see if perl is parsing your expressions the way you intended.

While B::Deparse goes to some lengths to try to figure out what your original program was doing, some parts of the language can still trip it up; it still fails even on some parts of Perl's own test suite. If you encounter a failure other than the most common ones described in the BUGS section below, you can help contribute to B::Deparse's ongoing development by submitting a bug report with a small example.


オプション

他のコンパイラバックエンドのオプションと同じように, '-MO=Deparse' の後に直接空白を入れずにコンマで区切って指定します.

-d

(データが定数として現れた際に)Data::Dumperを使って出力を行います. このオプションを指定しないときには B::Deparse は自分で持っている簡単な 手続きを使います. 今のところ, 奇妙な浮動小数点数など多くの点では ビルトインのものの方が優れていますが, いくつかのデータ(共有や自己参照を 行う複雑な構造等)に限ると Data::Dumper の方が優れています.

-fFILE

通常 B::Deparse はプログラムのメインコードと同じファイルに含まれる すべての関数をdeparseします. 他のファイルで定義されている関数を含める ためには-fにファイル名を指定します. -fは複数回指定して複数個の ファイルをを含めることもできます. (多くの場合これを使うことはない でしょう.) このオプションは2パラメータの #line ディレクティブの スコープ内で定義された関数を含めるためにも使えます.

-l

元のコードでの行とファイルを元に '#line' 宣言を出力に加えます.

-p

括弧を多めに出力します. このオプションを指定しないときには B::Deparse は元のプログラム中で必要な箇所にしか括弧を生成しません. -p を 指定すると(ほとんど)使って問題ない箇所すべてに括弧を生成します. これはLISPに慣れているとか, perl がどのようにパースしたのかを見たい ときには便利でしょう. 次のような入力があったとすると,

    if ($var & 0x7f == 65) {print "Gimme an A!"}
    print ($which ? $a : $b), "\n";
    $name = $ENV{USER} or "Bob";

B::Deparse,-p は次のように出力します.

    if (($var & 0)) {
        print('Gimme an A!')
    };
    (print(($which ? $a : $b)), '???');
    (($name = $ENV{'USER'}) or '???')

そしてこれはおそらく意図したものではないでしょう ('???'は perlが最適化で省略したサインです).

-P

プロトタイプチェックを無効にします. このオプションを加えるとすべての 呼び出しはプロトタイプが無かったかのようにdeparseします. いいかえると, 次の文

    perl -MO=Deparse,-P -e 'sub foo (\@) { 1 } foo @x'

は次の出力となります.

    sub foo (\@) {
	1;
    }
    &foo(\@x);

パラメータが実際どのように foo に渡されるのかが明瞭になります.

-q

ダブルクオート文字列を対応する連結(concatenation), uc, ucfirst, lc, lcfirst, quotemeta, join の組み合わせに展開します. 例えば, 次の例では

    print "Hello, $world, @ladies, \u$gentlemen\E, \u\L$me!";

次のように出力されます.

    print 'Hello, ' . $world . ', ' . join($", @ladies) . ', '
          . ucfirst($gentlemen) . ', ' . ucfirst(lc $me . '!');

展開された形式はperlがその様な構成を内部で行っている表現です. この オプションは実際には通常B::Deparseが行っている逆変換でなくなります. 言い換えると, $x = "$y"$x = $y と同一ではありません. 前者は代入の前に $y を文字列に変換しています.

-sLETTERS

B::Deparse の出力のスタイルをいじります. LETTERS は 's' の直後に続く必要があります. スペースや句読点は必要ありません. 以下のオプションが使用できます:

C

elsif, else, continue ブロックを近くに配置します. 例えば, 次のコード

    if (...) {
         ...
    } else {
         ...
    }

が次のコードの代わりに出力されます.

    if (...) {
         ...
    }
    else {
         ...
    }

デフォルトではこのオプションは無効です.

iNUMBER

インデントを NUMBER カラム毎に並べます. デフォルトは 4 です.

T

インデント8カラム毎にタブを使用します. デフォルトではスペースのみ を使用します(訳注:つまりはこのオプションはデフォルトでは無効). 例えば スタイルオプションに -si4T を使用すると, 3回インデントが行われた 行では1つのタブと4つsのスペースが生成されます. オプションに -si8T を使用すると同じ行で3つのタブが生成されます.

vSTRING.

最適化で取り除かれて確定できない定数値を STRING で表示します. (覚え方: これは定数がvoidコンテキストで使われたときに発生します.) 文字列の終わりはピリオドでマークします. この文字列は正しいperlの式, 一般的には定数である必要があります. 数字以外を指定する場合には クオートが必要になる点に, そしてコマンドラインでのクオートは シェルからエスケープする必要がある点に注意してください. よくある値としては 0, 1, 42, '', 'foo', そして 'Useless use of constant omitted' ('使われない定数が省略されました'. この指定を行うには -sv"'Useless use of constant omitted'." の様な 感じで指定する必要があるでしょう). デフォルトは '???' です. もしモジュールやrequireしている他のファイルでB::Deparseを使うのなら, 偽として評価される値を使うべきではありません. 慣習的にモジュールの 末尾におかれる真(true)として評価される定数値はメインプログラムとして コンパイルされるときにはvoidコンテキストでの評価となるためです.

-xLEVEL

よく使われる構文を内部構造に近い等価なもので展開します. LEVEL は 数字である必要があり, 大きい値ほど多くに展開されます. -q の用に, B::Deparse の通常の操作の中において特殊な状況を無効にする必要が実際には生じる こともあります.

LEVEL が3あると, for ループはcontinueブロックを伴うwhileループに 変換されます. 例えば次の文は

    for ($i = 0; $i < 10; ++$i) {
        print $i;
    }

次のようになります.

    $i = 0;
    while ($i < 10) {
        print $i;
    } continue {
        ++$i
    }

いくつかのケースでは, この変換は完全にはソースコードには戻らないことが あります. 例えばループ初期化でmy変数を宣言した場合, ループの外側に 対応するスコープは持ちません.

LEVEL が5あると, use 宣言が requireimport を呼び出す BEGIN ブロックに変換されます. 例えば次のコードは

    use strict 'refs';

次のようになります.

    sub BEGIN {
        require strict;
        do {
            'strict'->import('refs')
        };
    }

LEVELが7あると, if文が&&, ?:, do {} を用いた等価な 式に変換されます. 例えば次の文は

    print 'hi' if $nice;
    if ($nice) {
        print 'hi';
    }
    if ($nice) {
        print 'hi';
    } else {
        print 'bye';
    }

次のようになります.

    $nice and print 'hi';
    $nice and do { print 'hi' };
    $nice ? do { print 'hi' } : do { print 'bye' };

elsifの長い列はネストした3項演算になるますが, B::Deparse では 今のところこれをきれいにインデントする方法をしりません.


B::Deparse をモジュールとして使う

概要

    use B::Deparse;
    $deparse = B::Deparse->new("-p", "-sC");
    $body = $deparse->coderef2text(\&func);
    eval "sub func $body"; # the inverse operation

説明

B::Deparse は他のperlプログラムからsub-by-subの原理で使うこともできます.

new

    $deparse = B::Deparse->new(OPTIONS)

deparse操作の状態とオプションを殻のするためのオブジェクトを作成します. オプションはコマンドラインで与えるものと同一です("OPTIONS"参照). -MO=Deparseの後にコンマで区切って与えていたオプションは, バラバラの文字列として与えます. -uの様ないくつかのオプションは 1つの関数としては用をなさないので渡さないでください. (訳注:-uってなに?)

ambient_pragmas

    $deparse->ambient_pragmas(strict => 'all', '$[' => $[);

関数のコンパイル時にはいくつかのコンパイラディレクティブ, pragma の影響下にあります. それらには以下のものがあります:

通常, 1つ以上のこれらのプラグマの環境下でコンパイルされた 関数に対して B::Deparse を使用するのなら, 出力には対応する ディレクティブを有効にする文が含められます. そのため coderef2text が返したコードをコンパイルすると, deparse した関数と同じ振る舞いを 持つようになります.

しかし, 結果をいくつかのプラグマで既に有効になっている特定の コンテキストで使うつもりであるのなら, ambient_pragmas メソッドを使って環境を仮定すること替えできます.

すべてのオプションが意味のある効果を持っているわけでは ありません. "BUGS" を参照してください.

使用できるパラメータは以下の通りです:

strict

文字列を取ります. 通常空白で区切られたいくつかの値からなります. "all" と "none" は特殊な値で, その意味は見た目の通りです.

    $deparse->ambient_pragmas(strict => 'subs refs');
$[

数字を取ります. この値は配列の基準添字 $[ の値です.

bytes
utf8
integer

値が真であれば対応するプラグマが環境スコープに仮定されます. 真でなければされません.

re

Takes a string, possibly containing a whitespace-separated list of values. The values "all" and "none" are special. It's also permissible to pass an array reference here.

    $deparser->ambient_pragmas(re => 'eval');
warnings

Takes a string, possibly containing a whitespace-separated list of values. The values "all" and "none" are special, again. It's also permissible to pass an array reference here.

    $deparser->ambient_pragmas(warnings => [qw[void io]]);

If one of the values is the string "FATAL", then all the warnings in that list will be considered fatal, just as with the warnings pragma itself. Should you need to specify that some warnings are fatal, and others are merely enabled, you can pass the warnings parameter twice:

    $deparser->ambient_pragmas(
	warnings => 'all',
	warnings => [FATAL => qw/void io/],
    );

See perllexwarn [CPAN] for more information about lexical warnings.

hint_bits
warning_bits

These two parameters are used to specify the ambient pragmas in the format used by the special variables $^H and ${^WARNING_BITS}.

They exist principally so that you can write code like:

    { my ($hint_bits, $warning_bits);
    BEGIN {($hint_bits, $warning_bits) = ($^H, ${^WARNING_BITS})}
    $deparser->ambient_pragmas (
	hint_bits    => $hint_bits,
	warning_bits => $warning_bits,
	'$['         => 0 + $[
    ); }

which specifies that the ambient pragmas are exactly those which are in scope at the point of calling.

coderef2text

    $body = $deparse->coderef2text(\&func)
    $body = $deparse->coderef2text(sub ($$) { ... })

与えられた関数のリファレンスに対応する関数の本体(1つのブロック. 括弧にプロトタイプがつくこともあります)のソースコードを返します. 関数は名前を持たないことも, 2つ以上の名前を持つこともあるため この復帰値は関数名をつけず, 完全な形での関数定義にはなりません. もし結果をevalしたいときには "sub subname " や無名関数であれば "sub "を前につけてください. 関数が main:: パッケージ以外で 定義されているのなら, コードにはパッケージ宣言も含まれているでしょう.


バグ


著者

Stephen McCamant <smcc@CSUA.Berkeley.EDU>, based on an earlier version by Malcolm Beattie <mbeattie@sable.ox.ac.uk>, with contributions from Gisle Aas, James Duncan, Albert Dvornik, Robin Houston, Dave Mitchell, Hugo van der Sanden, Gurusamy Sarathy, Nick Ing-Simmons, and Rafael Garcia-Suarez.

B::Deparse - perl コードを生成するPerlコンパイラバックエンド

索引

B::Deparse - perl コードを生成するPerlコンパイラバックエンド