PERLCOMPILE(7) | Perl Programmers Reference Guide | PERLCOMPILE(7) |
perlcompile - 關於 Perl 編譯器和翻譯器的介紹
Perl 一直是有一個編譯器的:你的源文件會被編譯成一種內部格式(一種語法分析樹),並且在運行前還會被優化。從5.005版本起,Perl 在發行時就帶有一個模塊可以檢查優化過的語法分析樹(該模塊稱作B模塊("B")),它被用來編寫許多有用的功能,包括一個可以將你的Perl轉成C源代碼的模塊,這樣再編譯後就可以得到一個可執行的文件了。
"B" 模塊提供了訪問語法分析樹的方法, 其它的一些模塊(“後端”)則對這個樹進行操作。一些把它(語法樹)以字節碼的形式輸出,還有以C源代碼形式的輸出的,後者以半可讀的文本形式輸出的。另一些遍歷整棵語法樹以建立一個關於所使用的子程序,格式及變量的交叉引用表。還有另外一些檢查你的代碼,看看有沒有模棱兩可的構造。另一些則重新將語法樹導出成Perl代碼,可以起代碼美化或是消除混亂的代碼的作用。
因爲 "B" 模塊的最初目的是提供一種能將Perl程序轉爲對應C代碼的方法,接着就能把它變成可執行文件了,所以 "B" 模塊和它的那些後端模塊就被認爲是“編譯器”了,即使它們實際上沒有做任何編譯方面的事。這個編譯器的各個部分精確的說應該是個“翻譯器”,或者一個“檢視器”,但是用Perl的人們想要一個“編譯選項”而不是一個叫做“檢視器”的小玩藝。你能怎麼辦呢?
這篇文章的主要內容是講Perl編譯器的用法:它包含的模塊,怎樣使用那些最重要的後端模塊,它們有什麼問題,如何讓它們工作。
Layout 佈局
編譯器的後端放在 "B::" 裏面,而前端(就是你,編譯器的使用者,有時候要與之交互的)是 O 模塊。一些後端(如 "B::C"))提供了一些程序(如perlcc)來隱藏模塊的複雜性。
這裏是一些值得知道的重要後端,並附有它們目前的狀態,用0到10的整數表示。(狀態0表示目前該部分功能只是有一個框架,還沒有實現;狀態10則表示如果還有Bug的話,我們會感到很奇怪的):
接下來的部分介紹怎樣使用各種各樣的編譯器後端。介紹的順序按照後端的成熟程度排列,所以最爲穩定的,經過了驗證的後端會最先介紹,還在試驗中和沒有完成的後端就放到後面描述了。
O模塊默認讓 -c 開關有效,這防止Perl在編譯完代碼後運行程序。這也是爲什麼所有的後端在產生任何輸出前都會打印一句:
myperlprogram syntax OK
The Cross Referencing Back End 交叉引用後端
交叉引用後端(B::Xref)生成一個關於你的程序的報表,把各個申明以及子程序,變量(包括格式)的使用情況存入文件中去。舉例來說,這有一段摘自對pod2man程序分析後生成的報表(該程序是Perl自帶的一個例程):
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行被引入( 原文用的詞是introduced,也就是在 my() 中第一次被定義的意思 ),然後在第1079行該變量被使用了。從主包(main package)中來的變量 $& 又在第1086行被使用, 等等。
行號前面可能會有一個字母作爲前綴,它們的意思是:
交叉引用中最爲有用的選項就是把報表存入不同的文件,例如要把關於 myperlprogram 的報表存入文件 report 中:
$ perl -MO=Xref,-oreport myperlprogram
The Decompiling Back End 反編譯後端
反編譯後端將把你的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 後端
lint 後端 (B::Lint) 檢察程序中不好的程序風格。一個程序認爲的不好風格可能對另外一個程序員來說是用起來很有效的工具,所以有選項讓你設定哪些東東將會受到檢查。
要運行一個風格檢查器檢察你的代碼:
$ perl -MO=Lint myperlprogram
要取消對上下文和沒有定義的子程序的檢查:
$ perl -MO=Lint,-context,-undefined-subs myperlprogram
要知道更多的選項信息,請看
B::Lint
The Simple C Back End 簡化的C後端
這個模塊用來把你的Perl程序的內部編譯狀態存儲到一個C代碼文件中去,而生成的C代碼就可以被特定平臺上的C編譯器轉換成一個可執行文件了。最後的程序還會和Perl解釋器的庫文件靜態鏈接起來,所以它不會節省你的磁盤空間(除非你的Perl是用共享的庫文件創建的)或是程序大小,然而,另一方面,程序啓動起來會快一些。
"perlcc" 工具缺省是生成以下的可執行文件。
perlcc myperlprogram.pl
The Bytecode Back End 字節碼後端
這個模塊只有在你能夠找到一種方法來裝入並運行它生成的字節碼時纔會顯得有用。ByteLoader模塊提供了這項功能。
要把Perl轉換成可執行的字節碼,你可以使用 "perlcc" 的 "-B" 開關:
perlcc -B myperlprogram.pl
字節碼是和機器類型無關的,所以一旦你編譯了一個模塊或是程序,它就可以像Perl源代碼一樣具有可移植性。(假設那個模塊或者程序的使用者有一個足夠新的Perl解釋器來對字節碼進行解碼)
有一些選項用來控制要生成的字節碼的性質和關於優化方面的參數,要知道這些選項的詳細情況,請參考
B::Bytecode
The Optimized C Back End 優化的C後端
優化的C後端按照語法樹中運行期代碼的路徑將你的Perl程序轉換成等效的(但是被優化了的)C代碼文件。這個C程序會直接對Perl的數據結構進行操作,而且也會鏈接Perl的解釋器的庫文件,以支持 eval(), "s///e", "require" 等等。
"perlcc" 工具使用 -O 開關生成這種可執行文件。要編譯一個Perl程序(以".pl" 或者".p" 結尾):
perlcc -O myperlprogram.pl
從Perl模塊創建一個共享庫文件(以 ".pm" 結尾):
perlcc -O Myperlmodule.pm
知道更多,請參考 perlcc 和 B::CC.
$ perl -MO=Deparse myperlprogram
這與在這個Perl程序中使用 "use O 'Deparse'" 相同。
$ perl -MO=Showlex,mysub myperlprogram
要得到一份關於 my() 中的變量在文件myperlprogram中的使用情況的列表:
$ perl -MO=Showlex myperlprogram
[BROKEN]
這個模塊對正在編寫自己的後端程序,或正在深入Perl內部機制的人們來說是非常有用的。對普通程序員來說則沒什麼用。
簡單 C 後端目前只保存以字符和數字命名的類型說明
優化的 C 後端會爲一些不該爲之輸出的模塊(比如說 DirHandle)輸出代碼。而且它不太可能正確地處理正在執行的子程序外部的goto語句(goto &sub is OK)。目前 "goto LABEL" 語句在這個後端中完全不會工作。他還會生成讓C 編譯器頭痛無比的巨大的初始化函數。如果把這個初始化函數分割開是能得到比目前更好的效果的。另外的問題包括:處理無符號的數學問題時不能正確工作;一些操作碼如果按照默認的操作碼機制處理也會有非正常的結果。
BEGIN{} 塊會在編譯你的代碼的時候被執行。所有的在BEGIN{} 中初始化的外部狀態,如打開的文件,初始的數據庫連結等等,會有不正確的表現。爲了解決這個問題,Perl中又提供了一個 INIT{} 塊來對應程序編譯之後,正式運行之前要執行的那段代碼。執行的順序是:BEGIN{}, (後端編譯程序可能這時會保存狀態), INIT{}, 程序運行, END{}。
這篇文章最初是由 Nathan Torkington 編寫,現在由郵件列表(perl5-porters@perl.org.)維護
郭銳(sunny65535) <sunny65535@263.net>
跋
本頁面中文版由中文
man 手冊頁計劃提供。
中文 man
手冊頁計劃:https://github.com/man-pages-zh/manpages-zh
2003-11-25 | perl v5.8.3 |