解釋器
邊翻譯邊執行的翻譯程序
解釋器是能夠執行用其他計算機語言編寫的程序的系統軟體,它是一種翻譯程序。它的執行方式是一邊翻譯一邊執行,因此其執行效率一般偏低,但是解釋器的實現較為簡單,而且編寫源程序的高級語言可以使用更加靈活和富於表現力的語法。
解釋器
在開始之前有必要再次強調:下面介紹的解釋器是一個源代碼解釋器。也就是說,解釋器在執行時,每次讀入一條語句,並且根據這條語句執行特定的操作;然後再讀入下一條語句,依次類推。這與偽代碼解釋器是有所區別的,例如早期的Java運行時系統。兩者的區別在於:源代碼解釋器直接對程序的源代碼解釋執行;而偽代碼解釋器先將程序的源代碼轉化為某種與機器無關的中間代碼,然後再執行中間代碼。相比之下,源代碼解釋器更易於創建,並且不需要一個獨立的編譯過程。
Small BASIC解釋器包括兩個主要的子系統:一個是表達式解析器,負責處理數字錶達式;另一個是解釋器,負責程序的實際執行。對於前者,可採用本書第二章所介紹的表達式解析器。但是在這裡做了某些改進,使得解析器能夠解析包含在程序語句中的數字錶達式,而不是只能解析孤立的表達式。
解釋器子系統和解析器子系統包含在同一個解釋器類中,該類名為SBasic。儘管從理論上講可以使用兩個獨立的類:一個包含解釋器,另一個包含表達式解析器;但是將兩者用同一個類來實現的代效率會更高,因為表達式解析器和解釋器的代碼是密不可分的。例如,兩個子系統都操作保存著程序代碼的同一個字元數組。如果將它們分別安排在兩個類中,將會增加可觀的額外開銷,並導致性能上的損失和功能上的重複。此外,由於程序解釋的任務繁重,而解析表達式只是其中的一部分,因此將整個解釋機制包含在單個類中是很有意義的。
解釋器執行時,每次從程序的源代碼中讀入一個標識符。如果讀入的是關鍵字,解釋器就按照該關鍵字的要求執行規定的操作。舉例來說,當解釋器讀入一個PRINT后,它將列印PRINT之後的字元;當讀入一個GOSUB時,它就執行指定的子程序。在到達程序的結尾之前,這個過程將反覆進行。可以看到,解釋器只是簡單地執行程序指定的動作。
解釋器運行程序的方法有:
1.直接運行高級編程語言(如 Shell 自帶的解釋器)
2.轉換高級編程語言碼到一些有效率的位元組碼 (Bytecode),並運行這些位元組碼
3.以解釋器包含的編譯器對高級語言編譯,並指示處理器運行編譯后的程序 (例如:JIT)
Perl,Python,MATLAB,與Ruby是屬於第二種方法,而UCSD Pascal則是屬於第三種方式。在轉譯的過程中,這組高級語言所寫成的程序仍然維持在源代碼的格式(或某種中繼語言的格式),而程序本身所指涉的動作或行為則由解釋器來表現。
使用解釋器來運行程序會比直接運行編譯過的機器碼來得慢,但是相對的這個直譯的行為會比編譯再運行來得快。這在程序開發的雛型化階段和只是撰寫試驗性的代碼時尤其來得重要,因為這個“編輯-直譯-除錯”的循環通常比“編輯-編譯-運行-除錯”的循環來得省時許多。
在解釋器上運行程序比直接運行編譯過的代碼來得慢,是因為解釋器每次都必須去分析並轉譯它所運行到的程序行,而編譯過的程序就只是直接運行。這個在運行時的分析被稱為"直譯式的成本"。在解釋器中,變數的訪問也是比較慢的,因為每次要訪問變數的時候它都必須找出該變數實際存儲的位置,而不像編譯過的程序在編譯的時候就決定好了變數的位置了。
在使用解釋器來達到較快的開發速度和使用編譯器來達到較快的運行進度之間是有許多妥協的。有些系統(例如有一些LISP)允許直譯和編譯的代碼互相調用並共享變數。這意味著一旦一個子程序在解釋器中被測試併除錯過之後,它就可以被編譯以獲得較快的運行進度。許多解釋器並不像其名稱所說的那樣運行原始代碼,反而是把原始代碼轉換成更壓縮的內部格式。舉例來說,有些BASIC的解釋器會把keywords取代成可以用來在jump table中找出相對應指令的單一byte符號。解釋器也可以使用如同編譯器一般的文字分析器(lexical analyzer)和語法分析器(parser)然後再轉譯產生出來的抽象語法樹(abstract syntax tree)。
可攜性佳,直譯式程序相較於編譯式程序有較佳的可攜性,可以容易的在不同軟硬體平台上運行。而編譯式程序經過編譯后的程序則只限定於運行在開發環境平台。