跳轉到內容

鸚鵡虛擬機器/鸚鵡編譯器工具

來自 Wikibooks,開放世界中的開放書籍

鸚鵡編譯器工具

[編輯 | 編輯原始碼]

本書的第一部分介紹了鸚鵡平臺的一些基礎知識,以及鸚鵡為其他高階語言提供的各種功能。需要注意的是,鸚鵡提供的功能和能力超出了大多數單個語言的需求。這是因為鸚鵡的目標是成為一個支援多種高階動態程式語言的平臺,每種語言都具有不同的功能集。這些程式語言中的一些最新版本,例如 Perl 6 和 Python 3000,都計劃了非常有趣的功能集,這些功能集無法被任何其他現有的直譯器或虛擬機器平臺很好地支援。

到目前為止,PIR 和鸚鵡程式設計一直處於相對較低的級別,但鸚鵡的目標是支援高階語言。為了促進這一目標,鸚鵡提供了編譯器設計人員可以用來快速輕鬆地建立下一代語言(如 Python 3000 和 Perl 6)需要的高階語言功能的工具。這些工具統稱為鸚鵡編譯器工具 (PCT)。PCT 是一套工具,人們可以使用它們在鸚鵡平臺上快速輕鬆地實現新的程式語言。我們將在本章以及後續的一些章節中討論它們。

解析和編譯:鸚鵡的工作原理

[編輯 | 編輯原始碼]

鸚鵡被設計成一個高度模組化的系統。這意味著許多元件可以根據需要進行互換。其中一些更改需要在編譯時指定,但其他更改可以在執行時執行。

將程式輸入鸚鵡需要經過多個步驟。以下是這些步驟的簡要概述

解析器和詞法分析器
鸚鵡的第一階段是解析器和詞法分析器(詞法分析器是“詞法分析儀”的簡稱)。我們將在本章後面以及未來的章節中更詳細地討論這些元件的操作。解析器和詞法分析器讀取 PIR 或 PASM 中的輸入程式碼,並將其轉換為稱為抽象語法樹 (AST) 的資料表示形式。AST 是一種以對計算機非常易於處理的方式表示程式指令的方法。
編譯器
編譯器單元將 AST 中的資訊轉換為鸚鵡位元組碼格式。位元組碼是一組二進位制機器語言指令。從這裡,鸚鵡可以直接執行位元組碼,或者可以將位元組碼儲存到磁碟並在以後執行它。
最佳化器
最佳化器獲取生成的位元組碼,並嘗試使其更小、更快、更高效。經過正確最佳化的位元組碼通常比未最佳化的位元組碼執行速度更快。
JIT 編譯器
JIT 是“Just In Time”的縮寫,JIT 編譯器嘗試將鸚鵡位元組碼轉換為本地機器程式碼。這通常會帶來很大的速度提升,但高度依賴於平臺,並且目前在任何系統上都無法執行。
直譯器
一旦程式被轉換為位元組碼,該位元組碼就會載入到直譯器中,並在其中執行。

這只是對這些元件的非常簡要的概述,我們將在後面的章節中更詳細地討論它們。然而,值得在這裡注意的是,許多這些元件都是模組化的,如果要使用不同的元件,可以將其替換掉。例如,如果您已經為特定語言編寫了一個解析器,則無需使用 PCT 重寫解析器,而是可以將您現有的解析器載入到鸚鵡中。當然,您可能需要進行修改以確保您的自定義解析器輸出正確的 AST,但這與從頭開始完全重寫語言解析器相比,是一個很小的代價。

PCT 設計流程

[編輯 | 編輯原始碼]

PCT 包括建立新程式語言編譯器所需的一些工具和設計步驟。以下是建立新編譯器所需的一些步驟的簡要介紹

  1. 建立語言外殼
  2. 建立語法檔案
  3. 建立語法動作檔案
  4. 建立必要的類、內建函式和 PMC
  5. 建立驅動程式

建立編譯器後,可以使用它來執行用高階語言編寫的程式。以下是執行編譯器涉及的一些步驟

  1. 將語法編譯成鸚鵡抽象語法樹 (PAST)
  2. 將 PAST 編譯成鸚鵡最佳化語法樹 (POST)
  3. 將 POST 編譯成鸚鵡位元組碼 (PBC) 或 PIR
  4. 在鸚鵡上執行 PBC 或 PIR

這應該可以讓您大致瞭解建立編譯器需要做什麼以及編譯器如何工作。我們將在本章以及本節接下來的幾章中詳細闡述每個步驟。

建立語言外殼

[編輯 | 編輯原始碼]

新的語言外殼包含許多元件。有我們提到的語法和動作檔案,但您還需要一個驅動程式來建立 HLLCompiler 物件並啟動編譯。此外,如果您想要任何內建函式或類,則需要編寫它們。為了簡化整個過程,您需要有一個 makefile 來處理編寫語言的所有構建步驟。

幸運的是,有一個工具可以簡化此過程,即mk_language_shell.plmk_language_shell.pl 是一個 Perl 5 程式,它建立建立新語言編譯器所需的所有必要檔案,並用一些有用的預設程式碼填充這些檔案。它位於鸚鵡根資料夾中的tools/dev/資料夾中。要從您的 shell 執行此程式,請轉到鸚鵡根資料夾並鍵入

tools/dev/mk_language_shell.pl <LANGUAGE_NAME> <PATH>

這裡,<LANGUAGE_NAME> 是您新語言的名稱,<PATH> 是您希望將其儲存到的目錄。按照慣例,所有語言專案都儲存在languages/目錄中。使用此目錄可以使其他構建工具更容易找到它。

例如,如果我們要建立一個名為“mylanguage”的新語言,我們可以編寫

tools/dev/mk_language_shell.pl mylanguage languages/mylanguage

這將建立所有必要的檔案,包括語言專案的 makefile。請注意,隨著時間的推移,許多這些預設檔案(包括 makefile)都需要編輯或修改。您可以作為練習開啟 makefile 並檢視事物是如何構建的。如果您以前從未見過 makefile,那麼這是一個瞭解它們是什麼以及它們如何工作的機會。

語法和動作

[編輯 | 編輯原始碼]
注意:PGE 實際上是Perl 6 語法引擎。Perl 6 編譯器呼叫 PGE 來處理 Perl 6 原始碼中的語法規則。

語法,通常是具有“.pg”副檔名的檔案,使用鸚鵡語法引擎 (PGE) 進行編譯。PGE 是鸚鵡的 Perl 6 規則引擎的實現。PGE 使用遞迴下降解析器,儘管某些元件(如表示式)可以使用自下而上的解析器來提高效率。如果您閱讀過關於編譯器構造的書,這應該對您有所幫助。如果沒有,解析器的詳細資訊此時並不特別重要。

不幸的是,在我們進一步深入之前,我們需要介紹一些術語。熟悉語法和解析器的人可以跳過本節。其他所有人應該嘗試通讀它,因為它是有價值的相關資訊。

一個稱為詞法分析器的工具讀取輸入檔案並將文字塊轉換為稱為“標記”的內容。然後,解析器將標記排列成特定的模式,稱為“規則”。當規則成功應用於一組輸入標記時,則稱該規則“匹配”輸入。將標記視為句子中的一個詞。單獨一個詞可能沒有太多意義。但是,如果您將多個詞組合成一個句子,則意圖就會變得清晰。解析器將一組標記組合在一起,並嘗試將其形成一個“句子”或已知模式。如果找到有效的標記模式,則解析器成功。

在解析過程的每個步驟中,解析器都會從詞法分析器接收一個標記。如果解析器擁有足夠的標記來構成一個有效的模式,則解析成功。如果它沒有足夠的資訊來形成有效的模式,它會請求下一個標記並再次嘗試。大型模式被劃分為較小的模式。標記組合成小的模式,小的模式組合成更大的標記。最終,整個程式碼檔案將被簡化為單個模式,解析器退出。

在每個步驟中,解析器可以選擇使用標記中的資訊執行操作。解析器將特定操作與不同的標記型別相關聯。對開括號標記執行的操作將與對閉括號標記執行的操作不同。在PGE的情況下,操作是函式,通常用PIR或NQP編寫,這些函式建立PAST節點。PAST節點儲存在一個表示輸入的大樹中。這稱為解析樹。當解析器到達最終匹配併成功時,解析樹將傳遞給工具包的後續階段進行處理,並最終轉換為Parrot位元組碼。

如前所述,在Parrot上實現新的語言被分解成幾個部分。

  • 使用Perl 6語法規則編寫語法檔案。
  • 使用NQP編寫語法操作檔案。
  • 用PIR編寫驅動程式。
  • 使用PIR(或C,用於PMC)編寫內建函式、類和PMC。

一旦你建立了你的語言外殼,所有這些檔案都將為你生成。你只需要在必要的檔案中填寫你的語法和操作,編寫其餘必要的內建程式碼,你應該就可以擁有一個工作的編譯器。一旦你修改了這些檔案以執行你需要的操作,還有一個額外的可選步驟你需要執行。

  • 編寫一系列測試模組以驗證你的語言是否正常執行。

我們稍後將討論測試和測試框架。我們將在接下來的幾章中討論編寫解析器和操作檔案。

驅動程式

[編輯 | 編輯原始碼]

驅動程式是編譯器的主入口點,它需要執行許多工。驅動程式的第一個也是最重要的任務是為相關的高階語言建立一個編譯器物件,並將命令列引數傳遞給該編譯器物件。編譯器物件是一個HLLCompiler物件,HLLCompiler類包含解析命令列引數和初始化編譯器所需的所有方法。有關HLLCompiler類的更多資訊,請參見附錄

驅動程式有許多工。以下是這些任務,沒有特定的順序。

  1. 指定一個:main函式,該函式啟動程式。
  2. 為給定的高階語言(HLL)建立一個HLLCompiler物件。
  3. 向HLLCompiler物件指定任何其他詳細資訊,以在解析階段之前更改編譯器的操作。
  4. 包含語言執行所需的類庫和內建函式。對於大多數語言,這將至少包括一個庫載入例程,該例程能夠將其他庫載入到Parrot中以供HLL及其編寫的程式使用。
  5. 宣告將與解析器一起使用或將由HLL程式使用的任何全域性變數。

除了這些之外,語言設計者可能希望在主驅動程式中執行其他任務。

獲取幫助

[編輯 | 編輯原始碼]

在編寫新的語言編譯器時,你可以去許多地方獲取幫助。Parrot儲存庫包含所有當前的Parrot文件,以POD格式。Perl 5程式設計師會熟悉POD,但其他使用者可能不熟悉。POD是一種簡單的文件格式,在Perl程式碼中被視為多行註釋。像pod2html這樣的特殊程式可以用於將POD檔案轉換為其他檔案型別以進行展示,例如HTML。

languages/目錄中有很多語言。如果你正在嘗試為你的語言實現某個特定功能,很有可能你可以找到另一個語言如何實現該功能的現有示例。一個非常有用的工具,尤其是在構建PAST節點樹或用PIR編寫函式時,是Parrot的--target=指令。此指令允許你指定輸出轉儲格式。例如,如果你進入languages/perl6/目錄,你可以輸入以下內容:

../../parrot perl6.pbc --target=pir

此命令將輸出你輸入的任何Perl 6指令的PIR。這些選項適用於Parrot,因此所有語言都將使用它們,而不僅僅是Perl 6。以下是一些你可能想嘗試的其他目標。

  • pir:列印程式碼生成的PIR結果。
  • pasm:列印PASM程式碼結果。
  • past:列印從程式碼生成的PAST節點樹。
  • parse:列印程式碼的解析樹。

嘗試所有這些,看看使用不同的語言可以獲得什麼樣的結果。

如果你已經在POD文件和現有程式碼示例中尋找幫助,那麼可能是時候找到一個真正的人來詢問了。Parrot開發者和愛好者聚集在#parrot(irc.perl.org)聊天室。Perl 6開發者和愛好者聚集在#perl6(freenode)聊天室。

其他資源和聯絡方式可在http://www.parrotcode.org/resources.html找到。


上一頁 鸚鵡虛擬機器 下一頁
Parrot偵錯程式 Parrot語法引擎
華夏公益教科書