編譯原理語法分析編譯
⑴ 【編譯原理】第四章:語法分析
從分析樹的根節點到葉節點方向構造分析樹。
即從開始符號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) 。
⑵ 編譯原理 什麼是語義分析
在編譯原理中,語法規則和詞法規則不同之處在於:規則主要識別單詞,而語法主要識別多個單片語成的句子。詞法分析和詞法分析程序:詞法分析階段是編譯過程的第一個階段。這個階段的任務是從左到右一個字元一個字元地讀入源程序,即對構成源程序的字元流進行掃描然後根據構詞規則識別單詞(也稱單詞符號或符號)。詞法分析程序實現這個任務。詞法分析程序可以使用lex等工具自動生成。語法分析(Syntax analysis或Parsing)和語法分析程序(Parser) 語法分析是編譯過程的一個邏輯階段。語法分析的任務是在詞法分析的基礎上將單詞序列組合成各類語法短語,如「程序」,「語句」,「表達式」等等.語法分析程序判斷源程序在結構上是否正確.源程序的結構由上下文無關文法描述.語義分析(Syntax analysis) 語義分析是編譯過程的一個邏輯階段. 語義分析的任務是對結構上正確的源程序進行上下文有關性質的審查, 進行類型審查.語義分析將審查類型並報告錯誤:不能在表達式中使用一個數組變數,賦值語句的右端和左端的類型不匹配.
⑶ 如何通俗易懂地解釋編譯原理中語法分析的過程
語法分析(Syntax analysis或Parsing)和語法分析程序(Parser)
語法分析是編譯過程的一個邏輯階段。語法分析的任務是在詞法分析的基礎上將單詞序列組合成各類語法短語,如「程序」,「語句」,「表達式」等等.語法分析程序判斷源程序在結構上是否正確.源程序的結構由上下文無關文法描述.
⑷ 編譯原理筆記17:自下而上語法分析(4)LR(0)、SLR(1) 分析表的構造
(移進項目就是指圓點右邊是終結符的項目,規約項目指的就是圓點在右部最右端的項目)
LR(0) 文法可以直接通過識別活前綴的 DFA 來構造 LR 分析表
假定 C = {I 0 , I 1 , ... , I n } (aka. LR(0) 項目規范族、DFA 狀態集)
首先為文法產生式進行編號,拓廣文法的產生式要標記為 0(這里就是後面分析表中 rj 的產生式編號 j 的由來)
然後令每個項目集 I k 的下標 k 作為分析器的狀態(行首),包含 S' → .S 的集合下標為分析器的初態(也就是 DFA 的初態,一般都是 0 )。
下面用一個例子來說明 ACTION、GOTO 子表的構造:
SLR(1) 為解決沖突提出了一個簡單的方法:通過識別活前綴的 DFA 和【簡單向前看一個終結符】構造 SLR(1) 分析表。
如果我們的識別活前綴的 DFA 中存在移進-規約沖突、規約-規約沖突,都可以嘗試使用這個方法來解決沖突。(這里說【嘗試】,當然是因為 SLR 也只能解決一部分問題,並不是萬能的靈丹妙葯。。)
這里,我們拿前面那個 LR(0) 解決不了的文法來舉例
該文法不是 LR(0) 文法,但是是 SLR(1) 文法。
觀察上圖 DFA 中的狀態2,想像當我們的自動機正處於這個狀態:次棧頂已經規約為 T 了,棧頂也是當前的狀態 2 ,而當前剩餘輸入為 *。
如果這個自動機不會【往前多看一步】的話,那麼對處於這個狀態的自動機來說,看起來狀態 2 中的移進項目和規約項目都是可選的。這就是移進-規約沖突。
想要解決這個沖突,就輪到【往前多看一步】上場了——把當前剩餘輸入考慮進來,輔助進行項目的選擇:
對其他的沖突也使用同樣的方法進行判斷。
這種沖突性動作的解決辦法叫做 SLR(1) 解決辦法
准備工作部分,與 LR(0) 分析表的構造差不多:同樣使用每個項目集的狀態編號作為分析器的狀態編號,也就同樣用作行下標;同樣使用拓廣文法產生式作為 0 號產生式。
填表也和 LR(0) 類似,唯一的不同體現在對規約項的處理方法上:如果當前狀態有項目 A → α.aβ 和 A → α. ,而次棧頂此時是 α 且讀寫頭讀到的是 a,那麼當且僅當 a∈FOLLOW(A) 時,我們才會用 A → α 對 α 進行規約。
如果構造出來的表的每個入口都不含多重定義(也就是如上圖中表格那樣的,每個格子裡面最多隻有一個動作),那麼該表就是該文法的 SLR(1) 表,這個文法就是 SLR(1) 文法。使用 SLR(1) 表的分析器叫做一個 SLR(1) 分析器。
任意的二義文法都不能構造出 SLR(1) 分析表
例:懸空 else
例:
這里的 L 可以理解為左值,R 可以理解為右值
經過計算可以確定其 DFA 如下圖所示。
在 狀態4 中,由於 "=" 同時存在於 FOLLOW(L) 與 FOLLOW(R) 中,因此該狀態內存在移進-規約沖突,故該文法不是 SLR(1) 文法。
這樣的非二義文法可以通過增加向前看終結符的個數來解決沖突(比如LL(2)、LR(2))但這會讓問題更加復雜,故一般不採用。而二義文法無論向前看多少個終結符都無法解決二義性。
⑸ 編譯原理全部的名詞解釋
書上有別那麼懶!.
編譯過程的六個階段:詞法分析,語法分析,語義分析,中間代碼生成,代碼優化,目標代碼生成
解釋程序:把某種語言的源程序轉換成等價的另一種語言程序——目標語言程序,然後再執行目標程序.解釋方式是接受某高級語言的一個語句輸入,進行解釋並控制計算機執行,馬上得到這句的執行結果,然後再接受下一句.
編譯程序:就是指這樣一種程序,通過它能夠將用高級語言編寫的源程序轉換成與之在邏輯上等價的低級語言形式的目標程序(機器語言程序或匯編語言程序).
解釋程序和編譯程序的根本區別:是否生成目標代碼
句子的二義性(這里的二義性是指語法結構上的.):文法G[S]的一個句子如果能找到兩種不同的最左推導(或最右推導),或者存在兩棵不同的語法樹,則稱這個句子是二義性的.
文法的二義性:一個文法如果包含二義性的句子,則這個文法是二義文法,否則是無二義文法.
LL(1)的含義:(LL(1)文法是無二義的; LL(1)文法不含左遞歸)
第1個L:從左到右掃描輸入串 第2個L:生成的是最左推導
1 :向右看1個輸入符號便可決定選擇哪個產生式
某些非LL(1)文法到LL(1)文法的等價變換: 1. 提取公因子 2. 消除左遞歸
文法符號的屬性:單詞的含義,即與文法符號相關的一些信息.如,類型、值、存儲地址等.
一個屬性文法(attribute grammar)是一個三元組A=(G, V, F)
G:上下文無關文法.
V:屬性的有窮集.每個屬性與文法的一個終結符或非終結符相連.屬性與變數一樣,可以進行計算和傳遞.
F:關於屬性的斷言或謂詞(一組屬性的計算規則)的有窮集.斷言或語義規則與一個產生式相聯,只引用該產生式左端或右端的終結符或非終結符相聯的屬性.
綜合屬性:若產生式左部的單非終結符A的屬性值由右部各非終結符的屬性值決定,則A的屬性稱為綜合屬
繼承屬性:若產生式右部符號B的屬性值是根據左部非終結符的屬性值或者右部其它符號的屬性值決定的,則B的屬性為繼承屬性.
(1)非終結符既可有綜合屬性也可有繼承屬性,但文法開始符號沒有繼承屬性.
(2) 終結符只有綜合屬性,沒有繼承屬性,它們由詞法程序提供.
在計算時: 綜合屬性沿屬性語法樹向上傳遞;繼承屬性沿屬性語法樹向下傳遞.
語法制導翻譯:是指在語法分析過程中,完成附加在所使用的產生式上的語義規則描述的動作.
語法制導翻譯實現:對單詞符號串進行語法分析,構造語法分析樹,然後根據需要構造屬性依賴圖,遍歷語法樹並在語法樹的各結點處按語義規則進行計算.
中間代碼(中間語言)
1、是復雜性介於源程序語言和機器語言的一種表示形式.
2、一般,快速編譯程序直接生成目標代碼.
3、為了使編譯程序結構在邏輯上更為簡單明確,常採用中間代碼,這樣可以將與機器相關的某些實現細節置於代碼生成階段仔細處理,並且可以在中間代碼一級進行優化工作,使得代碼優化比較容易實現.
何謂中間代碼:源程序的一種內部表示,不依賴目標機的結構,易於代碼的機械生成.
為何要轉換成中間代碼:(1)邏輯結構清楚;利於不同目標機上實現同一種語言.
(2)便於移植,便於修改,便於進行與機器無關的優化.
中間代碼的幾種形式:逆波蘭記號 ,三元式和樹形表示 ,四元式
符號表的一般形式:一張符號表的的組成包括兩項,即名字欄和信息欄.
信息欄包含許多子欄和標志位,用來記錄相應名字和種種不同屬性,名字欄也稱主欄.主欄的內容稱為關鍵字(key word).
符號表的功能:(1)收集符號屬性 (2) 上下文語義的合法性檢查的依據: 檢查標識符屬性在上下文中的一致性和合法性.(3)作為目標代碼生成階段地址分配的依據
符號的主要屬性及作用:
1. 符號名 2. 符號的類型 (整型、實型、字元串型等))3. 符號的存儲類別(公共、私有)
4. 符號的作用域及可視性 (全局、局部) 5. 符號變數的存儲分配信息 (靜態存儲區、動態存儲區)
存儲分配方案策略:靜態存儲分配;動態存儲分配:棧式、 堆式.
靜態存儲分配
1、基本策略
在編譯時就安排好目標程序運行時的全部數據空間,並能確定每個數據項的單元地址.
2、適用的分配對象:子程序的目標代碼段;全局數據目標(全局變數)
3、靜態存儲分配的要求:不允許遞歸調用,不含有可變數組.
FORTRAN程序是段結構,不允許遞歸,數據名大小、性質固定. 是典型的靜態分配
動態存儲分配
1、如果一個程序設計語言允許遞歸過程、可變數組或允許用戶自由申請和釋放空間,那麼,就需要採用動態存儲管理技術.
2、兩種動態存儲分配方式:棧式,堆式
棧式動態存儲分配
分配策略:將整個程序的數據空間設計為一個棧.
【例】在具有遞歸結構的語言程序中,每當調用一個過程時,它所需的數據空間就分配在棧頂,每當過程工作結束時就釋放這部分空間.
過程所需的數據空間包括兩部分
一部分是生存期在本過程這次活動中的數據對象.如局部變數、參數單元、臨時變數等;
另一部分則是用以管理過程活動的記錄信息(連接數據).
活動記錄(AR)
一個過程的一次執行所需要的信息使用一個連續的存儲區來管理,這個區 (塊)叫做一個活動記錄.
構成
1、臨時工作單元;2、局部變數;3、機器狀態信息;4、存取鏈;
5、控制鏈;6、實參;7、返回地址
什麼是代碼優化
所謂優化,就是對代碼進行等價變換,使得變換後的代碼運行結果與變換前代碼運行結果相同,而運行速度加快或佔用存儲空間減少.
優化原則:等價原則:經過優化後不應改變程序運行的結果.
有效原則:使優化後所產生的目標代碼運行時間較短,佔用的存儲空間較小.
合算原則:以盡可能低的代價取得較好的優化效果.
常見的優化技術
(1) 刪除多餘運算(刪除公共子表達式) (2) 代碼外提 +刪除歸納變數+ (3)強度削弱; (4)變換循環控制條件 (5)合並已知量與復寫傳播 (6)刪除無用賦值
基本塊定義
程序中只有一個入口和一個出口的一段順序執行的語句序列,稱為程序的一個基本塊.
給我分數啊.
⑹ 編譯原理筆記7:語法分析(1)語法分析器的任務、語法錯誤的處理
語法分析器的兩項主要任務,分別:
源程序中的錯誤可以分為詞法/語法錯誤、語義錯誤兩類。前者主要形式是命名不合法、關鍵字書寫錯誤、語法結構有問題(比如缺分號、該配對的東西不配對)等;後者則可分為靜態/動態兩種,靜態例如類型使用錯誤、參數使用錯誤等,動態語義錯誤則是無窮遞歸這類邏輯性的問題。
例如:
緊急恢復:x = a+b+d; // 丟棄掉 b 後的記號,直到遇到 +
短語級恢復: x = a+b; // 加入分號
在寫程序時,要養成減少錯誤的好習慣:每次用變數、參數時,要在使用之前進行初始化,並在直接使用之前檢查一下是否出現值為空等問題,防止出現不可預知的錯誤
⑺ 請問編譯原理中什麼叫完成詞法分析,語法分析
以你說的SQL語句為例,詞法分析是將語句中的單詞流識別出來,比如create table Student 詞法分析是分析出 這句的單詞流是 「create」 「table」 「identifier」(前提是你給它們編號 比如用宏或者枚舉),然後語法分析 是通過單詞流 判斷 非邏輯錯誤 比如 有不能識別的符號 create table後面不是標示符等等 語義分析是分析語句的邏輯關系 比如欄位長度越界什麼的如 vchar(2) 你賦值為「啊啊啊啊啊啊」這種錯誤的識別是語義分析階段完成的 希望能幫到你
⑻ 編譯原理-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(α) 包括空串ε,那麼這個產生式是不是間接左遞歸。
⑼ 編譯原理語法分析編程
#include <iostream>
#include <string>
#include <fstream>
#include <queue>
#include <string.h>
#include <stdio.h>
using namespace std;
enum Datatype { RESERVE_WORD=1,IDENTIFIER=2,DIGIT=3,OPERATOR=4,SEPRATOR=5 };
struct OutputStruct
{
public:
Datatype type;
string value;
};
string operate[]={"sin","cos","pow"};
string KeyWord[]={"main","int","if","char","cout"};
const int MAX_SIZE=255;
char BUFF[MAX_SIZE]; //buffer to contain a char line.
ifstream inFile;
ofstream outFileStream;
queue<OutputStruct> tt;
bool IsKeyWord(string& cs)
{
for(int i=0;i<5;++i)
if(cs==KeyWord[i])
return true; //Exist
return false;
}
void ReadLineAndAnalyze()
{
int strSize=0;
int i;
int errFlag=0;
char ch;
string outStructStr,str;
struct OutputStruct outStruct;
{
i=0;
inFile.getline(BUFF,MAX_SIZE,'\n');
strSize=inFile.gcount();
cout<<BUFF;
do{
str="";
do{
ch=BUFF[i];
i++;
}while(ch==' '||ch==' '||ch=='\n');
switch(ch)
{
case '+':
case '-':
case '*':
case '/':
outStruct.type=OPERATOR;
outStruct.value=ch;
break;
case '=':
case '>':
case '<':
outStructStr=ch;
if(BUFF[i]=='=')
{
outStruct.type=OPERATOR;
outStructStr+=BUFF[i];
outStruct.value=outStructStr;
i++;
}
else
{
outStruct.type=OPERATOR;
outStruct.value=ch;
};
break;
case ',':
case ';':
case '{':
case '}':
case '(':
case ')':
case '[':
case ']':
case '\"':
outStruct.type=SEPRATOR;
outStruct.value=ch;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
outStructStr+=ch;
while(BUFF[i]>='0'&&BUFF[i]<='9'||BUFF[i]=='.')
{
outStructStr+=BUFF[i];
i++;
}//while
outStruct.type=DIGIT;
outStruct.value=outStructStr;
break;
default:
if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')
{
outStructStr+=ch;
while(BUFF[i]>='a'&&BUFF[i]<='z'||BUFF[i]>='A'&&BUFF[i]<='Z')
{
outStructStr+=BUFF[i];
i++;
}//while
if(IsKeyWord(outStructStr))
{
outStruct.type=RESERVE_WORD;
outStruct.value=outStructStr;
}
else
{
outStruct.type=IDENTIFIER;
outStruct.value=outStructStr;
}
break;
}
else
errFlag=1;
}//switch;
if(!errFlag)
tt.push(outStruct);
errFlag=0;
outStructStr="";
}while(i<strSize-1);
}//while(i<MAX_SIZE&&!inFile.eof());//do_while
return;
}
float F();
float T();
float E();
float S();
float F()
{
float ret;
if((tt.front().type==IDENTIFIER)||(tt.front().type==DIGIT))
{
ret=atof(tt.front().value.c_str());
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
return ret;
}
if(tt.front().value=="(")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
ret=E();
if(tt.front().value==")")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
return ret;
}
else
{
cout<<"\b ----ERROR! "<<tt.front().value<<" 缺少右括弧"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit(0);
}
}
else
{
cout<<"\b ----ERROR! "<<tt.front().value<<" 缺少因子"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit(0);
}
}
float T()
{
float i,j;
i=F();
if(tt.front().value=="*")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
j=T();
return i*j;
}
else if(tt.front().value=="/")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
j=T();
if(abs(j)<0.0000001)
{
cout<<"\b ----ERROR! 除數為零!"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit(0);
}
return i/j;
}
return i;
}
float E()
{
float i,j;
i=T();
if(tt.front().value=="+")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
j=E();
i=i+j;
}
else if(tt.front().value=="-")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
j=E();
i=i-j;
}
if(tt.front().value==";"||tt.front().type==OPERATOR||tt.front().value==")")
return i;
else
{
cout<<"\b ----ERROR! "<<tt.front().value<<" 缺少運算符"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit (0);
}
}
float S()
{
float i;
i=E();
if(tt.front().value==";")
{
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
tt.pop();
return i;
}
cout<<"\b ----ERROR! "<<tt.front().value<<" 缺少左括弧"<<endl;
cout<<"Press \"enter\" to modify the data file!";
getchar();
system("notepad data.txt");
exit(0);
}
void GrammaAnalize()
{
float i;
if(tt.empty())
{
cout<<"END"<<endl;exit(0);
}
i=S();
cout<<"\b="<<i<<endl;
}
int main()
{
inFile.open("data.txt");
if(!inFile)
{
cout<<"打開源文件失敗!";
return 1;
}
while(!inFile.eof())
{
ReadLineAndAnalyze();
GrammaAnalize();
}
inFile.close();
return 0;
}