『壹』 編譯原理學了有什麼用
對大多數人來說,學過編譯原理,應該可以知道對於很多代碼的優化,編譯器其實可以做好,不需要自己寫代碼的時候杞人憂天。在通用、局部的優化上,甚至編譯器往往做得比程序員好。
大概率會意識到編譯原理背後的故事,也許會沉迷在某個方向,也許還會樂於看一些奇妙的parser構建方式。
大概還可能會去學習類型系統,發現形式化的故事似乎在很多方面都有對應的版本,而後,他們也許會嘗試走向研究,去挑戰目前都沒有好好解決的代碼優化問題,也許會走向應用,用起LLVM,在上面加個target,支持一些新硬體,做個新語言的前端等。
編譯原理是計算機專業的一門重要專業課,旨在介紹編譯程序構造的一般原理和基本方法。內容包括語言和文法、詞法分析、語法分析、語法制導翻譯、中間代碼生成、存儲管理、代碼優化和目標代碼生成。 編譯原理是計算機專業設置的一門重要的專業課程。
編譯原理課程是計算機相關專業學生的必修課程和高等學校培養計算機專業人才的基礎及核心課程,同時也是計算機專業課程中最難及最挑戰學習能力的課程之一。編譯原理課程內容主要是原理性質,高度抽象。
編譯可以分為五個基本步驟:詞法分析、語法分析、語義分析及中間代碼的生成、優化、目標代碼的生成。這是每個編譯器都必須的基本步驟和流程, 從源頭輸入高級語言源程序輸出目標語言代碼。
1、詞法分析
詞法分析器是通過詞法分析程序對構成源程序的字元串從左到右的掃描, 逐個字元地讀, 識別出每個單詞符號, 識別出的符號一般以二元式形式輸出, 即包含符號種類的編碼和該符號的值。
詞法分析器一般以函數的形式存在, 供語法分析器調用。當然也可以一個獨立的詞法分析器程序存在。完成詞法分析任務的程序稱為詞法分析程序或詞法分析器或掃描器。
2、語法分析
語法分析是編譯過程的第二個階段。這階段的任務是在詞法分析的基礎上將識別出的單詞符號序列組合成各類語法短語, 如「語句」, 「表達式」等.語法分析程序的主要步驟是判斷源程序語句是否符合定義的語法規則, 在語法結構上是否正確。
而一個語法規則又稱為文法, 喬姆斯基將文法根據施加不同的限制分為0型、1型、2型、3型文法, 0型文法又稱短語文法, 1型稱為上下文有關文法, 2型稱為上下文無關文法, 3型文法稱為正規文法, 限制條件依次遞增。
3、語義分析
詞法分析注重的是每個單詞是否合法, 以及這個單詞屬於語言中的哪些部分。語法分析的上下文無關文法注重的是輸入語句是否可以依據文法匹配產生式。
那麼, 語義分析就是要了解各個語法單位之間的關系是否合法。實際應用中就是對結構上正確的源程序進行上下文有關性質的審查, 進行類型審查等。
4、中間代碼生成與優化
在進行了語法分析和語義分析階段的工作之後, 有的編譯程序將源程序變成一種內部表示形式, 這種內部表示形式叫做中間語言或中間表示或中間代碼。
所謂「中間代碼」是一種結構簡單、含義明確的記號系統, 這種記號系統復雜性介於源程序語言和機器語言之間, 容易將它翻譯成目標代碼。另外, 還可以在中間代碼一級進行與機器無關的優化。
5、目標代碼的生成
根據優化後的中間代碼, 可生成有效的目標代碼。而通常編譯器將其翻譯為匯編代碼, 此時還需要將匯編代碼經匯編器匯編為目標機器的機器語言。
6、出錯處理
編譯的各個階段都有可能發現源碼中的錯誤, 尤其是語法分析階段可能會發現大量的錯誤, 因此編譯器需要做出錯處理, 報告錯誤類型及錯誤位置等信息。
『貳』 編譯原理中語法分析的作用是什麼
語法分析是搞清楚語言含義的必要條件,只有語法搞清楚了,語句表達的意思才能得到准確理解,才能得到正確實現。
『叄』 編譯原理筆記7:語法分析(1)語法分析器的任務、語法錯誤的處理
語法分析器的兩項主要任務,分別:
源程序中的錯誤可以分為詞法/語法錯誤、語義錯誤兩類。前者主要形式是命名不合虛悄雹法、關鍵字書寫錯誤、語法結構有問題(比如缺分號、該配對的東西不配對)等;後者則可分為靜態/動態兩種,靜態例如類型使用錯誤、參數使用錯誤等,動態語義錯誤則是無窮遞歸這類邏輯性的問題。
例如:
緊急恢復:x = a+b+d; // 丟運芹棄掉 b 後的記號,直到遇到 +
短語級恢復: x = a+b; // 加入分號
在寫程序時,要養成減少錯誤的好習慣:每次用變數、參數時,要在使用之前進行初始化,並在差帆直接使用之前檢查一下是否出現值為空等問題,防止出現不可預知的錯誤
『肆』 【編譯原理】第四章:語法分析
從分析樹的根節點到葉節點方向構造分析樹。
即從開始符號S推導出詞串w的過程。
例:
總是選擇每個句型的 最左非終結符 進行替換。
總是選擇每個句型的 最右非終結符 進行替換。
在自底向上的分析中,總是採用 最左規約 的方式,因此把 最左規約 稱為 規范規約 ,對應的 最右推導 稱為 規范推導 。
最左推導、最右推導具有唯一性。
自頂向下的語法分析採用最左推導方試,總是選擇每個句型的 最左非終結符 進行替換。
由一組 過程 組成,每一個過程對應一個 非終結符 。
從文法開始符號S開始,遞歸調用文法中的其他非終結符,最終掃描整個輸入串,完成分析。
如果其間有不唯一的產生式,就可能需要退回上一步重新嘗試的情況,稱為 回溯 。
預測分析 是 遞歸下降分析 技術的一個特例,通過輸入中向前看固定個數的符號選擇正確的產生式。
如果一個文法可以構造出向前看k個符號的預測分析器,稱為LL(k)文法 。
預測分析不需要回溯,具有確定性。
含有 形式產生式的文法稱為是 直接左遞歸 的。
如果一個文法中有一個非終結符A使得對某個串存在推導 ,那麼這個文法是 左遞歸 的。其中,經過兩步或以上推導產生的左遞歸,稱為 間接左遞歸 的。
左遞歸會使遞歸下降分析器陷入無限循環。
文法
即
該文法是直接左遞歸的,會陷入無限循環。
將以上文法轉換為:
即可消除左遞歸。事實上,這個過程把左遞歸轉換成了右遞歸。
消除直接左遞歸的一般形式
使用代入法。
對於一個文法,通過改寫產生式來 推遲決定 ,等獲得足夠多的輸入信息再做正確的決定。
例:文法:
可以改寫為:
從文法的開始符號S開始,每一步推導根據當前句型的最左非終結符A和當前輸入符號α,選擇正確的A-產生式。為保證分析的確定性,選出的候選式必須是唯一的。
S_文法(簡單的確定型文法)
可能在某個舉行中緊跟在A後面的終結符a的集合,記為 FOLLOW(A) 。
如果A是某個句型的最右符號,則將結束符「 $ 」添加到FOLLOW(A)中。
例:文法:
中,FOLLOW(B) = {a, c}
產生式 的可選集是指可以選用該產生式進行推導時對應的輸入符號的集合,記為 SELECT(A->β) 。
例如
SELECT(A -> aβ)={a}
SELECT(A -> aβ | bγ)={a, b}
SELECT(A -> ε)=FOLLOW(A)
q_文法
文法符號串α串首終結符的集合,記作 FIRST(A) 。
『伍』 編譯原理筆記9:語法分析樹、語法樹、二義性的消除
語法分析樹和語法樹不是一種東西 。習慣上,我們把前者叫做「具體語法樹」,其能夠體現推導的過程;後者叫做「抽象語法樹」,其不體現過程,只關心最後的結果。
語法分析樹是語言推導過程的圖形化表示方法。這種表示方法反映了語言的實質以及語言的推導過程。
定義:對於 CFG G 的句型,分析樹被定義為具有下述性質的一棵樹:
推導,有最左推導和最右推導,這兩種推導方式在推導過程中的分析樹可能不同,但因最終得到的句子是相同的,所以最終的分析樹是一樣的。
分析樹能反映句型的推導過程,也能反映句型的結構。然而實際上,我們往往不關心推導的過程,而只關心推導的結果。因此,我們要對 分析樹 進行改造,得到 語法樹 。語法樹中全是終結符,沒有非終結符。而且語法樹中沒有括弧
定義:
說白了,語法樹這玩意,就一句話: 葉子全是操作數,內部全是操作符 ,樹里沒有非終結符也不能有括弧。
語法樹要表達的東西,是操作符(運算)作用於操作數(運算對象)
舉倆例子吧:
【例】: -(id+id) 的語法樹:
【例】:-id+id 的語法樹:
顯然,我們從上面這兩個語法樹中,直接就能觀察出來它們的運算順序。
【例】:句型 if C then s1 else s2
二義性問題:一個句子可能對應多於一棵語法樹。
【例】: 設文法 G: E → E+E | E*E | (E) | -E | id
則,句子 id+id*id、id+id+id 可能的分析樹有:
在該例中,雖然 id+id+id 的 「+」 的結合性無論左右都不會影響結果。但萬一,萬一「+」的含義變成了「減法」,那麼左結合和右結合就會引起很大的問題了。
我們在這里講的「二義性」的「義」並非語義——我們現在在學習的內容是「語法分析器」,尚未到需要研究語言背後含義的階段。
我們現在講的「二義性」指的是一個句子對應多種分析樹。
二義性的體現,是文法對同一句子有不止一棵分析樹。這種問題由【句子產生過程中的某些推導有多於一種選擇】引起。懸空 else 問題就可以很好地體現這種【超過一種選擇】帶來的二義性問題,示例如下。
看下面這么個例子。。
(其實,我感覺這個其實比較像是「說話大喘氣」帶來的理解歧義問題。。。)上面的產生式中並沒體現出來該咋算分一塊,所以兩種完全不同的句子結構都是合法的。
二義性問題是有救的,大概有以下這三種辦法:
這些辦法的核心,其實都是將優先順序和結合性說明白。
核心:把優先順序和結合性說明白
既然要說明白,那就不能讓一個非終結符可以直接在當次推導中能推出會帶來優先順序和結合性歧義的東西。(對分析樹的一個內部節點,不會有出現在其下面的分支是相同的非終結符的情況。如果有得選,那就有得歧義了。沒得選才能確定地一路走到黑)
改寫為非二義文法的二義文法大概有下面這幾個特點:
改寫的關鍵步驟:
【例】改寫下面的二義文法為非二義文法。圖右側是要達成的優先順序和結合性
改寫的核心其實就兩句話:
所以能夠得到非終結符與運算的對應關系(因為不同的運算有不同的優先順序,我們想要引入多個優先順序就要引入多個新的非終結符。這樣每個非終結符就可以負責一個優先順序的運算符號,也就是說新的非終結符是與運算有關系的了。因此這里搞出來了「對應關系」四個字)如下:
優先順序由低到高分別是 +、 、-,而距離開始符號越近,優先順序越低。因此在這里的排序也可以+ -順序。每個符號對應一層的非終結符。根據所需要的結合性,則可確定是左遞歸還是右遞歸,以確定新的產生式長什麼樣子
【例】:規定優先順序和結合性,寫出改寫的非二義文法
我們已經掌握了一種叫做【改寫】的工具,能讓我們消除二義性。接下來我們就要用這個工具來嘗試搞搞懸空 else 問題!
懸空 else 問題出現的原因是 then 數量多於 else,讓 else 有多個可以結合的 then。在二義文法中,由於選哪兩個 then、else 配對都可以,故會引起出現二義的情況。在這里,我們規定 else 右結合,即與左邊最靠近的 then 結合。
為改寫此文法,可以將 S 分為完全匹配(MS)和不完全匹配(UMS)兩類。在 MS 中體現 then、else 個數相等即匹配且右結合;在UMS 中 then、else 不匹配,體現 else 右結合。
【例】:用改寫後的文法寫一個條件語句
經過檢查,無法再根據文法寫出其他分析樹,故已經消除了二義性
雖然二義文法會導致二義性,但是其並非一無是處。其有兩個顯著的優點:
在 Yacc 中,我們可以直接指定優先順序、結合性而無需自己重寫文法。
left 表示左結合,right 表示右結合。越往下的算符優先順序越高。
嗯就這么簡單。。。
我們其實可以把語言本身定義成沒有優先順序和結合性的。。然後所有的優先、結合都交由括弧進行控制,哪個先算就加括弧。把一個過程的結束用明確的標志標記出來。
比如在 Ada 中:
在 Pascal 中,給表達式加括弧: