編譯原理語法分析
❶ 編譯原理中語法分析的作用是什麼
語法分析是搞清楚語言含義的必要條件,只有語法搞清楚了,語句表達的意思才能得到准確理解,才能得到正確實現。
❷ 編譯原理筆記7:語法分析(1)語法分析器的任務、語法錯誤的處理
語法分析器的兩項主要任務,分別:
源程序中的錯誤可以分為詞法/語法錯誤、語義錯誤兩類。前者主要形式是命名不合法、關鍵字書寫錯誤、語法結構有問題(比如缺分號、該配對的東西不配對)等;後者則可分為靜態/動態兩種,靜態例如類型使用錯誤、參數使用錯誤等,動態語義錯誤則是無窮遞歸這類邏輯性的問題。
例如:
緊急恢復:x = a+b+d; // 丟棄掉 b 後的記號,直到遇到 +
短語級恢復: x = a+b; // 加入分號
在寫程序時,要養成減少錯誤的好習慣:每次用變數、參數時,要在使用之前進行初始化,並在直接使用之前檢查一下是否出現值為空等問題,防止出現不可預知的錯誤
❸ 編譯原理語法分析器程序設計,用C語言或C++,哪裡有這個程序
1.文法簡略,沒有實現的部分,可以在此文法的基礎上進行擴充,本程序的採用自頂向下的LL(1)文法。
2.可以自動實現求First
集和
Follow
集。
3.處終結符外(有些硬編碼的成分),終結符的文法可以自定義,也就是說讀者可以自定義文法。
4.為方便理解,C語言的文法描述寫成中文。
5.程序將詞法分析和語法分析結合起來,詞法分析的結果作為語法分析的輸入。
6.最終結果在控制台顯示的有:詞法分析、First集、Follow集、Select集,在preciateResult.txt
中寫入了語法分析結果,在preciateTable.txt
中寫入了預測分析表。
7.文法的詞素之間必須有空格分開。
❹ 如何通俗易懂地解釋編譯原理中語法分析的過程
分成詞法分析,語法分析(LL演算法,遞歸下降演算法,LR演算法),語義分析,運行時環境,中間代碼,代碼生成,代碼優化這些部分。其實現在很多編譯原理的教材都是按照85,86出版的那本龍書來安排教學內容的,所以那本龍書的內容格式幾乎成了現在編譯原理教材的定式,包括國內的教材也是如此。一般來說,大學裡面的本科教學是不可能把上面的所有部分都認真講完的,而是比較偏重於前面幾個部分。像代碼優化那部分東西,就像個無底洞一樣,如果要認真講,就是單獨開一個學期的課也不可能講得清楚。所以,一般對於本科生,對詞法分析和語法分析掌握要求就相對要高一點了。
詞法分析相對來說比較簡單。可能是詞法分析程序本身實現起來很簡單吧,很多沒有學過編譯原理的人也同樣可以寫出各種各樣的詞法分析程序。不過編譯原理在講解詞法分析的時候,重點把正則表達式和自動機原理加了進來,然後以一種十分標準的方式來講解詞法分析程序的產生。這樣的做法道理很明顯,就是要讓詞法分析從程序上升到理論的地步。
語法分析部分就比較麻煩一點了。現在一般有兩種語法分析演算法,LL自頂向下演算法和LR自底向上演算法。LL演算法還好說,到了LR演算法的時候,困難就來了。很多自學編譯原理的都是遇到LR演算法的理解成問題後就放棄了自學。其實這些東西都是只要大家理解就可以了,又不是像詞法分析那樣非得自己寫出來才算真正的會。像LR演算法的語法分析器,一般都是用工具Yacc來生成,實踐中完全沒有比較自己來實現。對於LL演算法中特殊的遞歸下降演算法,因為其實踐十分簡單,那麼就應該要求每個學生都能自己寫。當然,現在也有不少好的LL演算法的語法分析器,不過要是換在非C平台,比如Java,Delphi,你不能運用YACC工具了,那麼你就只有自己來寫語法分析器。
❺ 編譯原理中詞法分析和語法分析的任務分別是什麼
在編譯原理中,語法規則和詞法規則不同之處在於:規則主要識別單詞,而語法主要識別多個單片語成的句子。
詞法分析和詞法分析程序:
詞法分析階段是編譯過程的第一個階段。這個階段的任務是從左到右一個字元一個字元地讀入源程序,即對構成源程序的字元流進行掃描然後根據構詞規則識別單詞(也稱單詞符號或符號)。詞法分析程序實現這個任務。詞法分析程序可以使用lex等工具自動生成。
語法分析(Syntax analysis或Parsing)和語法分析程序(Parser)
語法分析是編譯過程的一個邏輯階段。語法分析的任務是在詞法分析的基礎上將單詞序列組合成各類語法短語,如「程序」,「語句」,「表達式」等等.語法分析程序判斷源程序在結構上是否正確.源程序的結構由上下文無關文法描述.
語義分析(Syntax analysis)
語義分析是編譯過程的一個邏輯階段. 語義分析的任務是對結構上正確的源程序進行上下文有關性質的審查, 進行類型審查.語義分析將審查類型並報告錯誤:不能在表達式中使用一個數組變數,賦值語句的右端和左端的類型不匹配.
❻ 【編譯原理】第四章:語法分析
從分析樹的根節點到葉節點方向構造分析樹。
即從開始符號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) 。
❼ 編譯原理語法分析有哪幾種方法
語法分析有自上而下和自下而上兩種分析方法
其中
自上而下:遞歸下降,LL(1)
自下而上:LR(0),SLR(1),LR(1),LALR(1)
❽ 編譯原理筆記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 中,給表達式加括弧:
❾ 編譯原理-LL1文法詳細講解
我們知道2型文法( CFG ),它的每個產生式類型都是 α→β ,其中 α ∈ VN , β ∈ (VN∪VT)*。
例如, 一個表達式的文法:
最終推導出 id + (id + id) 的句子,那麼它的推導過程就會構成一顆樹,即 CFG 分析樹:
從分析樹可以看出,我們從文法開始符號起,不斷地利用產生式的右部替換產生式左部的非終結符,最終推導出我們想要的句子。這種方式我們稱為自頂向下分析法。
從文法開始符號起,不斷用非終結符的候選式(即產生式)替換當前句型中的非終結符,最終得到相應的句子。
在每一步推導過程中,我們需要做兩個選擇:
因為一個句型中,可能存在多個非終結符,我們就不確定選擇那一個非終結符進行替換。
對於這種情況,我們就需要做強制規定,每次都選擇句型中第一個非終結符進行替換(或者每次都選擇句型中最後一個非終結符進行替換)。
自頂向下的語法分析採用最左推導方式,即總是選擇每個句型的最左非終結符進行替換。
最終的結果是要推導出一個特定句子(例如 id + (id + id) )。
我們將特定句子看成一個輸入字元串,而每一個非終結符對應一個處理方法,這個處理方法用來匹配輸入字元串的部分,演算法如下:
方法解析:
這種方式稱為遞歸下降分析( Recursive-Descent Parsing ):
當選擇的候選式不正確,就需要回溯( backtracking ),重新選擇候選式,進行下一次嘗試匹配。因為要不斷的回溯,導致分析效率比較低。
這種方式叫做預測分析( Predictive Parsing ):
要實現預測分析,我們必須保證從文法開始符號起,每一個推導過程中,當前句型最左非終結符 A 對於當前輸入字元 a ,只能得到唯一的 A 候選式。
根據上面的解決方法,我們首先想到,如果非終結符 A 的候選式只有一個以終結符 a 開頭候選式不就行了么。
進而我們可以得出,如果一個非終結符 A ,它的候選式都是以終結符開頭,並且這些終結符都各不相同,那麼本身就符合預測分析了。
這就是S_文法,滿足下面兩個條件:
例子:
這就是一個典型的S_文法,它的每一個非終結符遇到任一終結符得到候選式是確定的。如 S -> aA | bAB , 只有遇到終結符 a 和 b 的時候,才能返回 S 的候選式,遇到其他終結符時,直接報錯,匹配不成功。
雖然S_文法可以實現預測分析,但是從它的定義上看,S_文法不支持空產生式(ε產生式),極大地限制了它的應用。
什麼是空產生式(ε產生式)?
例子
這里 A 有了空產生式,那麼 S 的產生式組 S -> aA | bAB ,就可以是 a | bB ,這樣 a , bb , bc 就變成這個文法 G 的新句子了。
根據預測分析的定義,非終結符對於任一終結符得到的產生式是確定的,要麼能獲取唯一的產生式,要麼不匹配直接報錯。
那麼空產生式何時被選擇呢?
由此可以引入非終結符 A 的後繼符號集的概念:
定義: 由文法 G 推導出來的所有句型,可以出現在非終結符 A 後邊的終結符 a 的集合,就是這個非終結符 A 的後繼符號集,記為 FOLLOW(A) 。
因此對於 A -> ε 空產生式,只要遇到非終結符 A 的後繼符號集中的字元,可以選擇這個空產生式。
那麼對於 A -> a 這樣的產生式,只要遇到終結符 a 就可以選擇了。
由此我們引入的產生式可選集概念:
定義: 在進行推導時,選用非終結符 A 一個產生式 A→β 對應的輸入符號的集合,記為 SELECT(A→β)
因為預測分析要求非終結符 A 對於輸入字元 a ,只能得到唯一的 A 候選式。
那麼對於一個文法 G 的所有產生式組,要求有相同左部的產生式,它們的可選集不相交。
在 S_文法基礎上,我們允許有空產生式,但是要做限制:
將上面例子中的文法改造:
但是q_文法的產生式不能是非終結符打頭,這就限制了其應用,因此引入LL(1)文法。
LL(1)文法允許產生式的右部首字元是非終結符,那麼怎麼得到這個產生式可選集。
我們知道對於產生式:
定義: 給定一個文法符號串 α , α 的 串首終結符集 FIRST(α) 被定義為可以從 α 推導出的所有串首終結符構成的集合。
定義已經了解清楚了,那麼該如何求呢?
例如一個文法符號串 BCDe , 其中 B C D 都是非終結符, e 是終結符。
因此對於一個文法符號串 X1X2 … Xn ,求解 串首終結符集 FIRST(X1X2 … Xn) 演算法:
但是這里有一個關鍵點,如何求非終結符的串首終結符集?
因此對於一個非終結符 A , 求解 串首終結符集 FIRST(A) 演算法:
這里大家可能有個疑惑,怎麼能將 FIRST(Bβ) 添加到 FIRST(A) 中,如果問文法符號串 Bβ 中包含非終結符 A ,就產生了循環調用的情況,該怎麼辦?
對於 串首終結符集 ,我想大家疑惑的點就是,串首終結符集到底是針對 文法符號串 的,還是針對 非終結符 的,這個容易弄混。
其實我們應該知道, 非終結符 本身就屬於一個特殊的 文法符號串 。
而求解 文法符號串 的串首終結符集,其實就是要知道文法符號串中每個字元的串首終結符集:
上面章節我們知道了,對於非終結符 A 的 後繼符號集 :
就是由文法 G 推導出來的所有句型,可以出現在非終結符 A 後邊的終結符的集合,記為 FOLLOW(A) 。
仔細想一下,什麼樣的終結符可以出現在非終結符 A 後面,應該是在產生式中就位於 A 後面的終結符。例如 S -> Aa ,那麼終結符 a 肯定屬於 FOLLOW(A) 。
因此求非終結符 A 的 後繼符號集 演算法:
如果非終結符 A 是產生式結尾,那麼說明這個產生式左部非終結符後面能出現的終結符,也都可以出現在非終結符 A 後面。
我們可以求出 LL(1) 文法中每個產生式可選集:
根據產生式可選集,我們可以構建一個預測分析表,表中的每一行都是一個非終結符,表中的每一列都是一個終結符,包括結束符號 $ ,而表中的值就是產生式。
這樣進行語法推導的時候,非終結符遇到當前輸入字元,就可以從預測分析表中獲取對應的產生式了。
有了預測分析表,我們就可以進行預測分析了,具體流程:
可以這么理解:
我們知道要實現預測分析,要求相同左部的產生式,它們的可選集是不相交。
但是有的文法結構不符合這個要求,要進行改造。
如果相同左部的多個產生式有共同前綴,那麼它們的可選集必然相交。
例如:
那麼如何進行改造呢?
其實很簡單,進行如下轉換:
如此文法的相同左部的產生式,它們的可選集是不相交,符合現預測分析。
這種改造方法稱為 提取公因子演算法 。
當我們自頂向下的語法分析時,就需要採用最左推導方式。
而這個時候,如果產生式左部和產生式右部首字元一樣(即A→Aα),那麼推導就可能陷入無限循環。
例如:
因此對於:
文法中不能包含這兩種形式,不然最左推導就沒辦法進行。
例如:
它能夠推導出如下:
你會驚奇的發現,它能推導出 b 和 (a)* (即由 0 個 a 或者無數個 a 生成的文法符號串)。其實就可以改造成:
因此消除 直接左遞歸 演算法的一般形式:
例如:
消除間接左遞歸的方法就是直接帶入消除,即
消除間接左遞歸演算法:
這個演算法看起來描述很多,其實理解起來很簡單:
思考 : 我們通過 Ai -> Ajβ 來判斷是不是間接左遞歸,那如果有產生式 Ai -> BAjβ 且 B -> ε ,那麼它是不是間接左遞歸呢?
間接地我們可以推出如果一個產生式 Ai -> αAjβ 且 FIRST(α) 包括空串ε,那麼這個產生式是不是間接左遞歸。