B::Deparse - Perl 編譯器後端,用於產生 Perl 程式碼
perl -MO=Deparse[,-d][,-fFILE][,-p][,-q][,-l] [,-sLETTERS][,-xLEVEL] prog.pl
B::Deparse 是 Perl 編譯器的一個後端模組,用於根據 perl 在剖析程式後建立的內部編譯結構來產生 perl 原始碼。B::Deparse 的輸出不會與原始碼完全相同,因為 perl 沒有追蹤註解或空白,而且 perl 的語法結構與其編譯形式之間並非一對一的對應,但通常會很接近。當您使用 -p 選項時,輸出也會包含括號,即使優先順序不需要括號,這可以讓您輕鬆查看 perl 是否按照您的預期剖析您的表達式。
雖然 B::Deparse 盡力找出您的原始程式在做什麼,但語言的某些部分仍然會讓它出錯;它甚至會在 Perl 自身測試套件的某些部分失敗。如果您遇到 BUGS 部分中所述的最常見錯誤以外的失敗,您可以提交一個包含小範例的錯誤報告,以協助 B::Deparse 的持續開發。
與所有編譯器後端選項一樣,這些選項必須緊接在「-MO=Deparse」之後,以逗號分隔,但不要有任何空白。
使用 Data::Dumper 輸出資料值(當它們顯示為常數時)。沒有這個選項,B::Deparse 會使用一些自己的簡單常式來達到相同目的。目前,Data::Dumper 對於某些類型的資料(例如具有共用和自我參考的複雜結構)比較好,而內建常式對於其他類型的資料(例如奇數浮點值)比較好。
通常,B::Deparse 會對程式的 main 程式碼和在同一個檔案中定義的所有子常式進行反編譯。若要包含在其他檔案中定義的子常式,請傳遞 -f 選項和檔案名稱。您可以傳遞 -f 選項多次,以包含多個次要檔案。(大多數時候您根本不需要使用它。)您也可以使用這個選項來包含在具有兩個參數的 #line 指令範圍中定義的子常式。
根據原始程式碼的行和檔案位置,將「#line」宣告新增到輸出中。
列印額外的括號。沒有這個選項,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 最佳化了常數值)。
停用原型檢查。使用此選項後,所有函式呼叫都會被解析,就像沒有為它們定義原型一樣。換句話說,
perl -MO=Deparse,-P -e 'sub foo (\@) { 1 } foo @x'
會列印
sub foo (\@) {
1;
}
&foo(\@x);
清楚說明參數實際上是如何傳遞給 foo
的。
將雙引號字串展開成對應的串接、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 的值轉換為字串。
調整 B::Deparse 輸出的樣式。這些字母應直接接在「s」之後,沒有空格或標點符號。下列選項可用
縮排 elsif
、else
和 continue
區塊。例如,列印
if (...) {
...
} else {
...
}
而不是
if (...) {
...
}
else {
...
}
預設值是不縮排。
將行縮排為 NUMBER 個欄位的倍數。預設值為 4 個欄位。
每 8 個欄位的縮排使用一個 tab。預設值僅使用空格。例如,如果樣式選項為 -si4T,縮排 3 次的行將在前面加上一個 tab 和四個空格;如果選項為 -si8T,同一行將在前面加上三個 tab。
列印 STRING,作為無法判斷的常數的值,因為它已被最佳化而移除(助記符:當常數用於 void 內容時,就會發生這種情況)。字串的結尾以句點標示。字串應為有效的 perl 表達式,通常為常數。請注意,除非它是數字,否則可能需要加上引號,而且在命令列中,引號需要受到 shell 的保護。一些慣用的值包括 0、1、42、''、'foo' 和 'Useless use of constant omitted'(可能需要 -sv"'Useless use of constant omitted'." 或類似字串,具體取決於您的 shell)。預設值為 '???'。如果您在需要時使用 B::Deparse 對模組或其他檔案進行處理,則不應使用會評估為 false 的值,因為在將檔案編譯為主程式時,模組結尾的慣用 true 常數會處於 void 內容中。
將慣例語法結構擴充為等效結構,以顯示其內部運作。LEVEL 應為數字,數值越高表示擴充程度越大。與 -q 相同,這實際上涉及關閉 B::Deparse 正常運作中的特殊案例。
如果 LEVEL 至少為 3,for
迴圈將轉換為等效的 while 迴圈,並帶有 continue 區塊;例如
for ($i = 0; $i < 10; ++$i) {
print $i;
}
轉換為
$i = 0;
while ($i < 10) {
print $i;
} continue {
++$i
}
請注意,在某些情況下,此轉換無法完美地轉回原始程式碼 -- 例如,如果迴圈的初始化程式宣告 my 變數,則迴圈外部將不會具有正確的範圍。
如果 LEVEL 至少為 5,use
宣告將轉換為包含對 require
和 import
呼叫的 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 將轉換為巢狀三元運算子,而 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 程式以逐個子程式的方式使用。
$deparse = B::Deparse->new(OPTIONS)
建立一個物件,以儲存解譯運算的狀態和任何選項。選項與命令列中可以提供的選項相同(請參閱 "OPTIONS");在 -MO=Deparse 之後以逗號分隔的選項應提供為個別字串。
$deparse->ambient_pragmas(strict => 'all', '$[' => $[);
子程式的編譯可能會受到幾個編譯器指令(pragmas)的影響。這些指令是
use strict;
use warnings;
指定給特殊變數 $[
use integer;
use bytes;
use utf8;
use re;
通常,如果您對在這些指令其中一個或多個指令存在的情況下編譯的子程式使用 B::Deparse,輸出將包含開啟適當指令的陳述式。因此,如果您接著編譯由 coderef2text 傳回的程式碼,其行為將與您解譯的子程式相同。
不過,您可能知道您打算在特定內容中使用結果,其中一些實用規則已在範圍內。在此情況下,您可以使用 ambient_pragmas 方法來描述您希望做出的假設。
目前並非所有選項都有任何有用的效果。請參閱 "BUGS" 以取得更多詳細資訊。
它接受的參數為
採用一個字串,可能包含數個以空白分隔的值。特殊值 "all" 和 "none" 的意思正如您所預期的那樣。
$deparse->ambient_pragmas(strict => 'subs refs');
採用一個數字,陣列基底 $[ 的值。已過時:不能是非零。
如果值為 true,則假設適當的實用規則在環境範圍內,否則則否。
採用一個字串,可能包含以空白分隔的值清單。值 "all" 和 "none" 是特殊的。在此處傳遞陣列參考也是允許的。
$deparser->ambient_pragmas(re => 'eval');
採用一個字串,可能包含以空白分隔的值清單。值 "all" 和 "none" 再次是特殊的。在此處傳遞陣列參考也是允許的。
$deparser->ambient_pragmas(warnings => [qw[void io]]);
如果其中一個值是字串 "FATAL",則該清單中的所有警告都將被視為致命,就像使用 warnings 實用規則本身一樣。如果您需要指定某些警告是致命的,而其他警告僅被啟用,您可以傳遞 warnings 參數兩次
$deparser->ambient_pragmas(
warnings => 'all',
warnings => [FATAL => qw/void io/],
);
請參閱 warnings 以取得有關詞彙警告的更多資訊。
這兩個參數用於以特殊變數 $^H 和 ${^WARNING_BITS} 所使用的格式指定環境實用規則。
它們主要存在,以便您可以撰寫類似這樣的程式碼
{ 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 + $[
); }
它指定環境實用規則完全是呼叫點範圍內的那些實用規則。
此參數用於指定儲存在特殊雜湊 %^H 中的環境實用規則。
$body = $deparse->coderef2text(\&func)
$body = $deparse->coderef2text(sub ($$) { ... })
傳回子常式的本體來源碼(區塊,選擇性地加上括號中的原型),給定子常式的參考。因為子常式可以沒有名稱,或有多個名稱,此方法不會傳回完整的子常式定義——如果你想評估結果,你應加上「sub subname」或「sub」作為匿名函式建構式。除非子常式定義在 main:: 套件中,否則程式碼將包含套件宣告。
唯一完全支援的實用程式為:use warnings
、use strict
、use bytes
、use integer
和 use feature
。
除了上面列出的,我們目前無法保證 B::Deparse 會在程式正確的點產生實用程式。(特別是,區塊開頭的實用程式通常會出現在區塊開始之前。)由於實用程式的效果通常是詞彙範圍的,這表示實用程式會影響程式中不同於輸入檔案的部分。
事實上,上述是一個更一般問題的特定實例:我們無法保證在完全正確的地方產生 BEGIN 區塊或 use
宣告。因此,如果你使用影響編譯的模組(例如,透過覆寫關鍵字、重載常數或其他),則輸出程式碼可能無法按預期運作。
有些常數不論是否使用 -d 都無法正確列印。例如,B::Deparse 和 Data::Dumper 都不知道如何正確列印雙值標量,如下所示
use constant E2BIG => ($!=7); $y = E2BIG; print $y, 0+$y;
use constant H => { "#" => 1 }; H->{"#"};
使用來源過濾的輸入檔案可能無法解析為可執行程式碼,因為它仍會包含來源過濾模組的 use 宣告,即使產生的程式碼已經是普通的 Perl,不應再過濾一次。
最佳化的陳述句會呈現為「???」。這包括具有編譯時間副作用的陳述句,例如模糊的
my $x if 0;
因此,無法正確解析。
foreach my $i (@_) { 0 }
=>
foreach my $i (@_) { '???' }
在子常式外部範圍宣告的詞彙(my)變數會出現在 coderef2text 輸出文字中,作為套件變數。這是一個棘手的問題,因為 perl 沒有本機功能可以參考在不同範圍內定義的詞彙變數,儘管 PadWalker 是一個好的開始。
另請參閱 Data::Dump::Streamer,它結合了 B::Deparse 和 PadWalker 來正確序列化封閉。
非 ASCII 平台(EBCDIC)上可能還有更多錯誤。
Stephen McCamant <smcc@CSUA.Berkeley.EDU>,基於 Malcolm Beattie <mbeattie@sable.ox.ac.uk> 的早期版本,並由 Gisle Aas、James Duncan、Albert Dvornik、Robin Houston、Dave Mitchell、Hugo van der Sanden、Gurusamy Sarathy、Nick Ing-Simmons 和 Rafael Garcia-Suarez 共同編寫。