移位-歸約剖析(shift-reduce parsing)
想像輸送帶旁有位工人,身旁有張小桌。物件一次一個地沿著帶子下來。工人只有兩種動作:把下一個物件從帶子拿到桌上(那是移位),或注意到桌上最後幾個物件構成一個已知的組件,便把它們夾合成一個完成的零件(那是歸約)。移位-歸約剖析正是把這套規矩套用在詞符與文法規則上,以堆疊當作那張桌子。
剖析器維護一個堆疊,並由左到右讀輸入。移位把下一個輸入詞符壓入堆疊。歸約查看堆疊頂端,發現頂端符號匹配某產生式 A -> alpha 的右側,便彈出那些符號,並在原處壓入 A。當整個輸入被消耗完、且堆疊恰好只剩起始符號時,剖析成功。對 num + num 用 E -> E + E | num,剖析器移位 num、歸約為 E、移位 +、移位 num、歸約為 E,再把 E + E 歸約為 E 並接受。每一步的難題是移位還是歸約、以及該用哪條規則歸約,這正是 LR 剖析器用一台狀態機與一張表自動化的決定。
移位-歸約是所有由下而上剖析的引擎,而 LR 家族(LR(0)、SLR、LALR、LR(1))只是計算「何時移位、何時歸約」的不同方式。當文法使得這個選擇從可得資訊中確實無法決定時,建表器會回報移位-歸約衝突或歸約-歸約衝突,文法作者再以優先序與結合性宣告,或藉改寫文法來化解。yacc 與 bison 等工具產生的正是這種剖析器。
在 E -> E + E | num 下對 num + num 的追蹤,左邊是堆疊:[] 移位 -> [num] 歸約 -> [E] 移位 -> [E +] 移位 -> [E + num] 歸約 -> [E + E] 歸約 -> [E]。輸入已空,堆疊為起始符號 E:接受。
只有兩種動作,移位與歸約,驅動堆疊由詞符一路上推到起始符號。
移位-歸約剖析是通用的由下而上機制;LR 剖析則是系統化決定每一步要移位或歸約的方法。衝突意味著文法照寫無法告訴剖析器該做哪個動作。