小結-9.19
編譯器與解釋器:
1)編譯器的重要任務之一是報告在編譯過程中發現的源程序中的錯誤。倘若目標程序是一個可執行的程序,那麽它將可以被調用。(這意味著目標程序很可能不可在機器上直接執行)
2)解釋器是另一種常見的語言處理器,它並不通過編譯的方式生成目標程序。解釋器直接解釋源程序,執行相關的操作。
VS:
從運行時間上來講,從用戶輸入到處理得到輸出結果,執行編譯後的目標程序比用解釋器解釋源程序要快許多。
從交互與動態性上來講,解釋器,由於是一條一條解釋執行的,所以它的錯誤診斷效果比編譯的效果要好。
Java 混合編譯器,結合了編譯器和解釋器。Java的源程序首先經編譯器被編譯為字節碼(中間表示形式),然後虛擬機對字節碼加以解釋執行。
即時編譯器:有些Java編譯器,會把字節碼編譯為機器語言,然後在執行程序。
除了編譯器外,語言處理系統還有:
預處理器:聚合多個源程序,翻譯宏指令;
匯編器:處理編譯器生成的匯編語言目標程序,生成可重定位的機器代碼。
鏈接器:連接多個可重定位的目標文件和庫文件,處理外部內存地址;
加載器:將所有可執行文件放在內存中執行。
一個源程序可能被分為多個模塊,並存於獨立的文件中。把源程序聚合在一起的工作有時會由預處理器擔任,同時,它還會把被稱為宏的縮寫形式轉換為語言的的語句。
然後傳到編譯器中,產生一個匯編語言程序作為輸出,因為,匯編語言比較容易輸出和測試;接著,這個程序傳至匯編器中,匯編器進行處理,生成可重定位的機器代碼。
大型程序經常被分為幾個部分進行編譯,因此可重定位的機器代碼有必要和其他可重定位的文件以及庫文件連接到一起,形成真正在機器上運行的代碼;一個文件中的代碼可能指向另一個文件的位置,而鏈接器可以解決外部內存地址問題。
繼續介紹編譯器~~~
編譯器能夠把源程序轉換為在語義上等價的目標出現。映射過程分為兩個部分:分析部分和綜合部分。
1)分析部分(前端):把源程序分解為多個組成要素,並在這些要素上加上語法結構,用建立的語法結構創建源程序的中間表示形式,檢查並報告語法和語義錯誤,收集源程序的有關信息,並把他們放在符號表中,把中間表示形式和符號表一起傳送給綜合部分。
分析部分分為三個階段/步驟:詞法分析、語法分析、語義分析;
2)綜合部份(後端):根據符號表和中間表示形式創建用戶期待的目標代碼。
綜合部分分為三個階段/步驟:中間代碼產生、代碼優化、生成機器代碼;
前端與源語言有關,與機器語言無關;後端與源語言無關,與機器語言有關。
綜上:編譯過程分為六個/五個階段/步驟:
每個步驟把源語言的一種表示形式轉換為另一種。
如圖:
註:語義分析和中間代碼生成可以合為一個步驟。原因是中間代碼質量不夠高,且編譯器具有一定的可移植性。
下面是六個部分的詳細說明:
1)詞法分析(掃描):任務是讀入組成源程序的字符流,將它們組織成為有意義的詞素序列。輸出:對每一個詞素,語法分析器輸出一個詞法單元,形式:(token-name:attribute-value),token-name是在語法分析步驟使用的抽象符號,attribute-value是在語義分析和代碼生成步驟使用的這個詞法單元對應的條目。
詞素:語音和語義的最小單位,詞法單元的形式(token-name:attribute-value)可以理解為(單詞種別/名字,單詞符號的屬性值),
嗯,舉個例子:
position = inital+rate * 60
position 是一個詞素,被映射為<id,1> 其中id 是標識符 position 的抽象符號,而1 指向 position對用的條目。一個標識符對應的符號表條目應該存放和它有關的信息,比如它的名字和類型。
同理,= 是一個詞素,被映射為 <=>,因為這個詞法單元不需要屬性值,所以我們省略了第二個分量;initial被銀蛇為 <id,2>,+被映射為<+>,rate被映射為<id,3>,*被映射為<*>,60被映射為<60>;
我們常常把這些詞法單元的名字稱為終結符號,因為它們在程序設計語言(PDL)中是以終結符號的形式出現的。若詞法單元有屬性值,那麽這個值就是一個指向符號表的指針,符號表中包含了該詞法單元的附加信息,雖然這些附加信息與文法並無關系。
2)語法分析(解析/分析):任務是使用詞法分析產生的詞法單元的第一個分量創建樹形的中間表示。該中間表示給出了詞法分析產生的詞法單元流的語法結構,常用的表示法是語法樹,內部結點表示運算,相應的子結點表示運算的分量。後續步驟使用這個語法樹分析源程序,產生目標程序。描述語法結構的工具有上下文無關法(CFG).
上下文無關法:描述詞法單元的形成,任何一個詞法單元與其所處的環境無關。
3)語義分析:任務是使用符號表和語法樹的信息來檢查源程序是否和語言定義的語法一致。收集類型信息,把這些信息存放在符號表或語法樹中,一邊在隨後的中間代碼生成過程中使用。語義分析的重要部分是類型檢查,檢查每個運算符是否具有匹配的運算分量,自動進行類型轉換(下圖的 inttofloat)。
4)中間代碼生成:中間表示形式應該具有兩個重要的性質:易於生成,能被輕易地翻譯為目標機器的語言。它與源語言和機器無關。
中間代碼生成器的輸出是三地址代碼序列。形式 X=Y op Z ,每個分量都像一個寄存器。
關於三地址分量,有幾點:第一,每個三地址賦值的右部最多有一個運算符,因此這些指令確定了運算順序。第二,編譯器應該生成一個臨時名字以存放一個三地址指令計算得到的值。第三,有些三地址指令的運算分量少於三個(下圖的 id1=t3啊)。
5)代碼優化:分為與機器無關和有關的優化。
機器無關的代碼優化:試圖改進中間代碼,以便生成更好的目標代碼。
更好更快,更短,空間占用更少,能耗更低;
機器相關的優化。
優化編譯器:不同編譯器的代碼優化工作量差別很大優化工作做得最多的編譯器,即“優化編譯器”
6)代碼生成:任務:以源程序的中間表示形式作為輸入,並把它映射到目標語言如果目標語言是機器代碼,就必須為程序使用的每個變量選擇寄存器或內存位置。
代碼生成的一個重要方面是合理分配寄存器以存放變量的值。
編譯器在中間代碼生成或目標代碼生成階段做出有關存儲分配的決定運行時刻的存儲組織方法依賴於被編譯的語言。
符號表(symbol table)
的作用:記錄源程序中使用的變量名字,存儲每個名字的各種屬性有關的信息,變量的存儲分配、類型、作用域,過程的參數個數和類型,參數的傳遞方法、返回類型等。
符號表是編譯器中的重要數據結構
變量名表:為每個變量名創建一個記錄條目,記錄的字段就是名字的各個屬性。
趟(pass)/“遍”:
編譯步驟是編譯器的邏輯組織方式,在特定實現中,可以將多個步驟的活動組合成一趟,每趟讀入一個輸入文件並產生一個輸出文件。
所謂趟(遍)就是對源程序或源程序的中間結果從頭到尾掃描一次,並作有關的加工處理,生成新的中間結果或目標程序。
典型的組織方式
前端作為一趟,優化作為一個可選的趟,後端作為一趟。
未完,提醒一下自己,哈哈哈。
小結-9.19