LR 語法分析器

2021-04-26 12:53:39 字數 3292 閱讀 4653

lr語法分析器算是基本完成了,只需要乙個文法定義檔案(.syntax),就可以進行對應語言的語法分析,最後形成語法樹。詞法分析是固定的,採用c++的詞法定義。以後將加入動態的詞法分析。

壓縮包中檔案的描述:

lrtable.exe 是用文法定義檔案(syntax檔案)生成lr動作表檔案(action檔案),使用方法在 1_build_action.bat 檔案和 tableop.txt 檔案中有。直接執行 1_build_action.bat 會自動生成compile.script.syntax 檔案對應的動作表,並儲存為 script.action 。

lrparser.exe 是語法分析器,執行時必須傳入syntax檔案和對應的action檔案,這兩個檔案必須是對應的,否則執行結果不可預料。執行停止後可以使用 「?」 命令來查詢支援哪些 command。例如用 result 命令來列印分析後的語法樹。

*.scp 檔案是例子**,用來給 lrparser 分析的。

tt.txt 是乙個錯誤的例子**,測試 lrparser 的錯誤恢復。

tableop.txt 是 lrtable.exe 的命令序列,自動生成動作表,並儲存退出。供 1_build_action.bat 使用。

compile.script.syntax 是我的指令碼的文法定義。我的指令碼直譯器的語法分析是硬編碼的。該文法定義是乙個老版本的定義,還不支援object、new、以及匿名函式。因此 object.scp 用這個文法定義以及對應的動作表分析會出錯。

compile.script2.syntax 是最新的文法定義,支援object、new、以及匿名函式,可以正確分析 object.scp 檔案。

再要更改語言,就只用更改 syntax 檔案即可,無需再更改 lrparser.exe,這就是我的語法分析器的強大之處。

ps:

2、syntax檔案中,每個非終結符的定義最後都必須留至少乙個空行,否則無法正確分析。

3、syntax檔案中,nil、identifier 和 literal 是特殊終結符,本別指代【空】、識別符號和常量。他們的定義應該由詞法分析給出,但是目前詞法分析是硬編碼的,所以看不到他們的定義。

identifier 就是以字母或者下劃線開頭,字母數字下劃線組成的記號

nil 是空,不匹配任何符號

literal 就是 數字開頭的記號(數字常量)或者引號括起來的記號(字串常量)

關鍵字由單引號括起來,文法中稱之為終結符。

4、syntax檔案中,圓括號()代表optional(可有可無)。

補充1:文法分析法的分類

語法分析分為自頂向下和自底向上兩種。自頂向下分析法有 遞迴下降、ll 等。ll大致相當於遞迴下降的非遞迴版本。自頂向下分析的思路是分析器讀入輸入序列的前幾個記號,就必須對後續序列的形制進行猜測,否則就會出錯。例如:

stat -> 'if' exp 'then' stat 'else' stat 'endif'

stat -> 'while' exp 'do' stat 'endwhile'

如果讀入記號為'if'分析器就可以大膽猜測後續序列採用第乙個產生式進行分析,如果分析不下去,那只能說輸入的**錯了,可以進行錯誤修復。

如果是:

stat -> 'if' exp 'then' stat 'else' stat 'endif'

stat -> 'if' exp 'then' stat 'endif'

光讀入第乙個'if'是沒有辦法確定的,分析器必須讀如若干個記號,直到遇到'else'或者'endif'我才能確定。這就是自頂向下的麻煩所在:分析器很難知道讀入多少個記號才能確定用哪乙個產生式來處理。

自底向上分析法有移動歸約、lr系列(slr、lalr、lr)等。自底向上的思想是:分析器不對後續序列採用何種產生式進行猜測,只對已經讀入的序列負責。已經讀入的符號在堆疊裡擺成個什麼形制(堆疊狀態),就按照對應產生式來歸約。

移動歸約適合手動編碼,比如a+b/2,總是看符號,然後比較符號的優先順序。/比+優先順序高,就先歸約/再歸約+。移動規約和lr系列的關係就好比是遞迴下降和ll的關係一樣。

slr僅僅依據當前堆疊狀態來歸約,slr動作表裡面的條目數就是所有可能的堆疊狀態數。這樣歸約有個問題,例如a+b/2會被分析為((a+b)/2),因為讀完a+b時,剛好可以歸約,於是就歸約了,完全沒有考慮後面的除號(/)優先順序更高。

lalr和lr是依據當前讀入的字元和當前堆疊狀態來歸約。那麼a+b雖然可以歸約,但是當前讀入字元/是不允許a+b歸約的,得繼續往前讀,直到看到b/2再開始歸約,變為a+(b/2)=>(a+(b/2))。

lalr 是簡化了的lr(教科書都是先介紹lr,再介紹lalr)。lr動作表的每一條包含乙個記號和乙個堆疊狀態,這兩條規定了乙個動作,這樣的動作錶比slr 動作表大許多。lalr把lr動作表按照堆疊狀態分組,每一組裡面有乙個堆疊狀態和若干個記號,意思是:我遇到這若干個記號,而當前堆疊狀態恰好是那個狀 態,我就採取對應動作。因此lalr的動作表和slr動作表一樣大,還可以像lr一樣依據讀入記號來歸約。但是由於合併了動作,會導致有些lr可以分析的 lalr無法分析。比如某檔案按照lr分析狀態轉移序列可以是1-4,4-6,6-3或者1-3,3-7,7-2。lalr將3、4合併,6、7合併,那 麼就變成1-34,34-67,67-?,問號處是跳到34,還是2?

所以lr是所有分析法中最強大的。

補充2:關於 lrtable 生成的動作表

lrtable.exe生成的動作表是規範lr動作表,像乙個資料庫一樣,包含四個字段:set(也叫state)、in、action、param,每一行後面還有對action和param的注釋。

in欄位裡面的值,除了#表示檔案結尾外,其他符號都是syntax檔案中出現的。

action的意思是:

0=error,沒有使用。

1=push,將[in]移入記號棧,將[param]移入狀態棧

2=reduct,從記號棧中移出n個記號,進行歸約,歸約後作為當前讀入記號,不立刻放入堆疊;並且從狀態棧中彈出n個狀態。param是乙個數字,表示第幾個產生式,n就是這個產生式左邊的符號數。

3=custom,沒有使用

4=accept,動作和reduct一樣。

當前堆疊狀態就是狀態棧的棧頂元素。在教科書上,符號站和狀態棧是乙個棧,符號和狀態交替存放。我為了避免異型陣列,就分成兩個棧,執行中保證兩個堆疊數目一致即可。

其實可以簡化一下,將reduct和accept的引數直接設定為n,也就是說param不是產生式,而直接是n,這樣我可以不必讀入syntax檔案。我當初是想有利於報錯和錯誤恢復。現在的錯誤恢復沒有用到產生式。

lrparser最難的地方就是錯誤恢復。要對可能的錯誤進行搜尋。找到乙個較好的修復方案。

LR語法分析器程式設計

include include include include struct code val const char p const char tnt i etf lr分析表列的字元 const int m 9 0表示出錯,s4用4表示。acc用99表示 r2用 2表示 int col char 列...

編譯原理 語法分析3 LR分析器

語法分析,lr分析器 import pandas as pd data slr pd.dataframe data,index 0 1 2 3 4 5 6 7 8 9 10 11 slr分析表 grammer e e t e t t t f t f f e f id 文法的各個產生式 stk 0 用...

LALR語法分析器

lalr分析器 是一種規範lr分析方法的簡化形式。它可以對上下無關文法進行語法分析。lalr即 l ook ahead lr 其中,look ahead為 向前看 l代表對輸入進行從左到右的檢查,r代表反向構造出最右推導序列。lalr分析器 可以根據一種程式語言的正式語法的 產生式而對一段文字程式輸...