編譯器(compiler):閱讀以某一種語言(源語言)編寫的程式,并把該程式翻譯成為一個等價的、用另一種語言(目智語言)編寫的程式。編譯器的重要任務之一是報告它在翻譯過程中發現的源程式中的錯誤。
解釋器(interpreter):另一種常見的語言處理器。它并不通過翻譯的方式生成目标程式。從使用者的角度看,解釋器直接利用使用者提供的輸入執行源程式中指定的操作。
在把使用者輸入映射成輸出的過程中,由一個編譯器産生的機器語言目标程式通常比一個解釋器快很多。然而,解釋器的錯誤診斷效果通常比編譯器好,因為它逐個語句地執行源程式。

一個源程式可能被分割成多個子產品,并存放在獨立的檔案中。把源程式聚合在一起的任務有時會由一個被稱為預處理器(preprocessor)的程式獨立完成。預處理器還負責把那些成為宏的縮寫形式轉換為源語言的語句。
然後,将經過預處理的源程式作為輸入傳遞給一個編譯器。編譯器可能産生一個彙編語言程式作為其輸出,因為彙編語言比較容易輸出和調試。接着,這個彙編語言程式由稱為彙編器(assembler)的程式進行處理,并生成可重定位的機器代碼。
大型程式經常被分成多個部分進行編譯,是以,可重定位的機器代碼有必要和其他可重定位的目标檔案以及庫檔案連接配接到一起,形成真正在機器上運作的代碼。一個檔案中的代碼可能指向另一個檔案中的位置,而連結器(linker)能夠解決外部記憶體位址的問題。最後,加載器(loader)把所有的可執行目标檔案放到記憶體中執行。
分析(analysis)部分部分把源程式分解成為多個組成要素,并在這些要素之上加上文法結構。然後它使用這個結構來建立該源程式的一個中間表示。分析部分會收集有關源程式的資訊,并把資訊存放在一個成為**符号表(symbol table)的資料結構中。符号表和中間表示形式一起傳送給綜合部分。
綜合(synthesis)部分根據中間表示和符号表中的資訊來構造使用者期待的目标程式。分析部分經常被稱為編譯器的前端(front end),而綜合部分被稱為後端(back end)。
編譯過程順序執行了一組步驟(phase)。每個步驟把源程式的一種表示形式轉換成另一種表示形式。一個典型的把編譯程式分解成為多個步驟的方式如下圖所示。在實踐中,多個不走可能被組合在一起,而這些組合在一起的中間步驟之間的中間表示不需要明确地構造出來。存放整個源程式的資訊的符号表可由編譯器的各個步驟使用。
編譯器的第一個步驟成為詞法分析(lexical analysis)或掃描(scanning)。詞法分析器讀物組成源程式的字元流,并且将它們組織成為有意義的詞素(lexeme)的序列。對于每個詞素,詞法分析器産生如下形式的詞法單元(token)作為輸出:
<code><token-name, attribute-value></code>
第一個分量token-name是一個由詞法分析步驟使用的抽象符号,而二個分量attribute-value指向符号表中關于這個詞法單元的條目。符号表條目的資訊會被語義分析和代碼生成步驟使用。
比如,指派語句:
<code>position = initial + rate * 60</code>
經過詞法分析之後被表示成如下的詞法單元序列
<code><id, 1> < = > <id, 2> < + > <id, 3> < * > <60></code>
編譯器的第二個步驟稱為文法分析(syntax analysis)或解析(parsing)。文法分析器使用由詞法分析器生成的各個詞法單元的第一個分量來建立樹形的中間表示。該中間表示給出了詞法分析産生的詞法單元流的文法結構。一個常用的表示方法是文法樹(syntax tree),樹中的每個内部結點表示一個運算,而該結點的子節點表示該運算的分量。
語義分析器(semantic analyzer)使用文法樹和符号表中的資訊來檢查源程式是否和語言定義的語意一緻。它同時也收集類型資訊,并把這些資訊存放在文法樹或符号表中,以便在随後的中間代碼生成過程中使用。
語意分析的一個重要部分是類型檢查(type checking)。編譯器檢查每個運算符是否具有比對的運算分量。
程式設計語言可能允許某些類型轉換,這被稱為自動類型轉換(coercion)。
在把一個源程式翻譯成目标代碼的過程中,一個編譯器可能構造出一個或多個中間表示。這些中間表示可以有多種形式。文法樹是一種中間表示形式,它們通常在文法分析和語義分析中使用。該中間表示應該具有兩個重要的性質:它應該易于生成,且能夠被輕松地翻譯為目标機器上的語言。
機器無關的代碼優化步驟試圖改進中間代碼,以便更好的生成目标代碼。“更好”通常意味着更快,但是也有可能會有其他目标,如更短的或能耗更低的目标代碼。
代碼生成器以源程式的中間表示形式作為輸入,并把它映射到目智語言。如果目智語言是機器代碼,那麼就必須為程式使用的每個變量選擇寄存器或記憶體位置。然後,中間指令被翻譯成為能夠完成相同任務的機器指令序列。
編譯器的重要功能之一是記錄源程式中使用的變量的名字,并收集和每個名字的各種屬性有關的資訊。這些屬性可以提供一個名字和存儲配置設定、它的類型、作用域等資訊。對于過程名字,這些資訊還包括:它的參數數量和類型、每個參數的傳遞方法(比如傳值或傳引用)以及傳回類型。
符号表資料結構為每個變量名字建立了一個記錄條目。記錄的字段就是名字的各個屬性。這個資料結構應該允許編譯器迅速查找到每個名字的記錄,并向記錄中快速存放和擷取記錄中的資料。
前面關于步驟的讨論講的是一個編譯器的邏輯組成方式。在一個特定的實作中,多個步驟的活動可以被組合成一趟(pass)。每趟讀入一個輸入檔案并産生一個輸出檔案。
一些常用的編譯器構造工具包括:
文法分析器的生成器:根據一個程式設計語言的文法描述自動生成文法分析器。
掃描器的生成器:可以根據一個語言的文法單元的正規表達式描述生成詞法分析器。
文法制導的翻譯引擎:可以生成一組用于周遊分析樹并生成中間代碼的例程。
代碼生成器的生成器:依據一組關于如何把中間語言的每個運算翻譯成為目标機上的機器語言的規則,生成一個代碼生成器。
資料流分析引擎:可以幫助收集資料流資訊,即程式中的值如何從程式的一個部分傳遞到另一部分。資料流分析是代碼優化的一個重要部分。
編譯器構造工具集:提供了可用于構造編譯器的不同階段的例程的完整集合。
語言處理器:一個內建的的軟體開發環境,其中包括很多種類的語言處理器,比如編譯器、解釋器、彙編器、連接配接器、加載器、調試器以及程式概要提取工具。
編譯器的步驟:一個編譯器的運作需要一系列的步驟,每個步驟把源程式從一個中間表示轉換成為另一個中間表示。
機器語言和彙編語言:機器語言是第一代程式設計語言,然後是彙編語言。使用這些語言進行程式設計既費時,又容易出錯。
編譯器設計中的模組化:編譯器設計是理論對實踐有很大影響的領域之一。已知在編譯器設計中有用的模型包括自動機、文法、正規表達式、樹形結構和很多其他理論概念。
代碼優化:雖然代碼不能真正達到最優化,但提高代碼效率的科學既複雜又非常重要。它是編譯技術研究的一個主要部分。
進階語言:随着時間的流逝,程式設計語言擔負了越來越多的原先由程式員負責的任務,比如記憶體管理、類型一緻性檢查或代碼的并發執行。
編譯器和計算機體系結構:編譯器技術影響了計算機的體系結構,同時也收到體系結構發展的影響。體系結構中的很多現代創新都依賴于編譯器能夠從源程式中抽取出有效利用硬體能力的機會。
軟體生産率和軟體安全性:使得編譯器能夠優化代碼的技術同樣能夠用于多種不同的程式分析任務。這些任務既包括探測常見的程式錯誤,也包括發現程式可能會受到已被黑客們發現的多種入侵方式之一的傷害。
作用域規則:一個<code>x</code>的聲明的作用域是一段上下文,在此上下文中對<code>x</code>的使用指向聲明。如果僅僅通過閱讀某個語言的程式就可以确定其作用域,那麼這個語言就使用了靜态作用域,或者說詞法作用域。否則這個語言就使用了動态作用域。
環境:名字和記憶體位置關聯,然後再和值相關聯。這個情況可以使用環境和狀态來描述,其中環境把名字映射成為存儲位置,而狀态把位置映射到它的值。
塊結構:允許語句塊互相嵌套的語言成為塊結構的語言,假設一個塊中有一個<code>x</code>的聲明<code>d</code>,而嵌套于這個塊中的塊<code>b</code>中有一個對名字<code>x</code>的使用。如果這兩個塊之間沒有其他聲明了<code>x</code>的塊,那麼這個<code>x</code>的使用位于<code>d</code>的作用域内。
參數傳遞:參數可以通過值或引用的方式從調用過程傳遞給被調用過程。當通過值傳遞方式傳遞大型對象時,實際被傳遞的值是指向這些對象本身的引用。這樣就變成了一個高效的引用調用。
别名:當參數被以引用傳遞方式(高效地)傳遞時,兩個形式參數可能會指向同一個對象。這會造成一個變量的修改改變了另一個變量的值。