當爲另一種語言構建解釋器時,通常建議創建一個基於堆棧的虛擬機來解釋實際解釋器生成的字節碼。然後解釋器將由兩部分組成:翻譯器,它將虛擬機的高級語言指令轉換爲字節碼,以及虛擬機本身。替代解釋器的基於堆棧的虛擬機
我的問題是:解釋型語言有什麼替代方案?例如,是否可以(並且實際)跳過虛擬機,並使用C中的函數實現所有指令?在某種程度上,在我看來這應該是可能的,但也許你最終會實現一些最簡單的虛擬機,以實現更復雜的功能。還有其他選擇嗎?
當爲另一種語言構建解釋器時,通常建議創建一個基於堆棧的虛擬機來解釋實際解釋器生成的字節碼。然後解釋器將由兩部分組成:翻譯器,它將虛擬機的高級語言指令轉換爲字節碼,以及虛擬機本身。替代解釋器的基於堆棧的虛擬機
我的問題是:解釋型語言有什麼替代方案?例如,是否可以(並且實際)跳過虛擬機,並使用C中的函數實現所有指令?在某種程度上,在我看來這應該是可能的,但也許你最終會實現一些最簡單的虛擬機,以實現更復雜的功能。還有其他選擇嗎?
建議製作基於堆棧的VM,因爲它們製作起來很簡單。
VM的另一種常見類型是基於寄存器的,其中值存儲在寄存器而不是堆棧中。
解釋器和虛擬機還有許多其他變體。你可以有一個生成解析樹的編譯器和解釋這些的解釋器(但是如果它是用遞歸函數實現的,它可能會認爲它仍然是一個基於堆棧的虛擬機)。
它也並不少見做出的編譯器,而不是產生某種機器代碼(虛擬機或真機),以另一種語言生成代碼。對於這些類型的編譯器來說,C是一種常見的目標語言,因爲C語言及其編譯器無處不在。但是你沒有VM或解釋器了,你只需要一個編譯器/翻譯器。
你的建議是有點可能的。 C並沒有真正讓你操縱堆棧,當你調用一個函數時,它並不知道它周圍的局部變量,所以你需要在堆上分配一塊內存來保留一些虛假的「堆棧」空間「用於腳本語言的本地變量,並將其傳遞給每個函數(或將其填充到全局線程中)。你還需要一個用於腳本語言函數調用的棧的基指針。
一旦你這樣做了,你已經完成了大部分的工作來獲得基於棧的語言。所以你可以做其餘的事情。要爲此使用實際的堆棧和基址指針,必須將其下拉到機器語言級別。
如果你的語言是基於寄存器的,它仍然需要一個堆棧來訪問局部變量(它只是少用一次),但你不要用它作爲指令參數。如果我可能犯罪簡化,基於3地址註冊的虛擬機就是基於堆棧的虛擬機的特例。
字節碼解釋器的另一種方法是讓指令包含指令ID,然後將其用作函數指針數組索引,每個函數指針實現一條指令。
顯然這樣做會影響性能。如果你的指令足夠簡單,你可以通過直接在機器代碼中實現它們來節省CPU週期,並且可以忽略函數調用的開銷(通常可以忽略),甚至可以使用真正的堆棧而不是假的堆棧。
這一切都取決於您的需求。對於大多數情況,特別是如果這是你的第一個解析器/解釋器/虛擬機,我建議使用一組函數指針和一個假堆棧。這很簡單,不難調試,並且在現代機器上足夠快。您隨後可以隨時進入並編寫一個優化版本,以改變事情。
E.g.一種方法是爲函數調用生成足夠的機器代碼,然後在生成的機器代碼中插入一個指向這種函數的指針。所以每個腳本都會成爲一個編譯代碼塊,但是您不必編寫完整的編譯器。從那裏開始,你可以通過爲它們生成彙編程序來改進個別的關鍵指令,將不常用的東西作爲函數。這有點改善了代碼的局部性,這是一個微小的優化,可以提供幫助。但只是其中之一。
哦,大約一個月前,我在博客上一個如何使從一個初學者的角度編譯器(以及字節碼解釋器),它可能會有所幫助:http://orangejuiceliberationfront.com/how-to-write-a-compiler/
您可以直接發出線程代碼,跳繩一個「解釋者」部分。 –
@ SK-邏輯:這不會消除解釋器,它只是使它微不足道。 –
@IraBaxter,帶有直接線程代碼,沒有解釋器,它是100%的機器代碼。 –