由於識別表達式的工作已經委託給其他的分析器去做了,因此這一階段需要關注的產生式其實很少,它們是:
Jerry -> BasicBlock <END>
BasicBlock -> ε
BasicBlock -> Sentence BasicBlock
Sentence -> <EOS>
Sentence -> IfElseBranch
Sentence -> WhileLoop
Sentence -> Declaration
Sentence -> <IO> Assignment <EOS>
Sentence -> Assignment <EOS>
Sentence -> <BREAK> <EOS>
Sentence -> <LBRACE> BasicBlock <RBRACE>
IfElseBranch -> <IF> <LPARENT> Assignment <RPARENT> BasicBlock ElseBlock
ElseBlock -> <ELSE> BasicBlock
ElseBlock -> ε
WhileLoop -> <WHILE> <LPARENT> Assignment <RPARENT> BasicBlock
Declaration -> <TYPE> VariableRegister <EOS>
VariableRegister -> VariableRegister <COMMA> Variable Initialization
VariableRegister -> Variable Initialization
Initialization -> <ASSIGN> Assignment
Initialization -> ε
產生式比一般習題中出現的還是要多,不過進行LR分析比之前要輕鬆得多。首先是狀態0和狀態1:
-------------------------------
狀態0
Jerry -> · BasicBlock <END>
BasicBlock -> ·
BasicBlock -> · Sentence BasicBlock
Sentence -> · <EOS>
Sentence -> · IfElseBranch
Sentence -> · WhileLoop
Sentence -> · Declaration
Sentence -> · <IO> Assignment <EOS>
Sentence -> · Assignment <EOS> # 注a
Sentence -> · <BREAK> <EOS>
Sentence -> · <LBRACE> BasicBlock <RBRACE>
IfElseBranch -> · <IF> <LPARENT> Assignment <RPARENT> BasicBlock ElseBlock
WhileLoop -> · <WHILE> <LPARENT> Assignment <RPARENT> BasicBlock
Declaration -> · <TYPE> VariableRegister <EOS>
-------------------------------
狀態1 # 一旦接受到<END>就 Accept 的項目
Jerry -> BasicBlock · <END>
-------------------------------
注a:因爲Assignment使用OperationAnalyser來分析,所以這個項目並不衍生出其他項目。並且,在狀態0中,只要遇到First(Assignment)集合中的東西,就拉一個OperationAnalyser到分析器棧頂,然後轉過去。這種先讀入符號再變換分析器的過程稱之爲延遲的變換,對應的,有立即變換,即一旦步入某個狀態,立即更改分析器,比如在這個狀態下:
IfElseBranch -> <IF> <LPARENT> · Assignment <RPARENT> BasicBlock ElseBlock
毫無疑問,因爲已經沒有別的選了,接下來只可能是Assignment,所以這裏可以立即變換分析器。
狀態0既是一個規約狀態,也是一個待移進狀態。如果下一個符號是First(Sentence)集合中的符號,那麼就繼續分析,如果是Follow(BasicBlock)就規約。這裏
First(Sentence) = {EOS, WHILE, IF, INTEGER_TYPE, REAL_TYPE, READ, WRITE, BREAK, LBRACE}
U First(Assignment)
Follow(BasicBlock) = {ELSE, END, RBRACE}
兩家是井水不犯河水,因此這個衝突可以用SLR(1)方法解決。
另外,這個狀態1看起來有點怪怪的,這是因爲在Jerry中引入了一個特殊的符號END所致,而END只會出現在輸入結尾,因此可以忽略它,這樣狀態1就跟書上的別無二致了。
#############################################
爲了遠離序號式命名帶來的晦澀和難於記憶,以後的狀態採取另一種命名法,對於主項目對應的產生式左部有多個產生式對應的(如Sentence、VariableRegister等都有多個產生式與之對應),該狀態命名採取這種方式:
主項目對應產生式的左部名稱 _ 主項目右部符號名稱序列(當然不全是大寫,可以採用駱駝命名法) _ 點號的位置
如狀態
Sentence -> <IO> Assignment · <EOS>
可以命名爲“狀態Sentence_IOAssignmentEoS_2”,點號在第二個符號之後,因此後面的數字爲2。對於主項目產生式左部在產生式集合中僅一次作爲左部出現的,不會導致歧義,因此直接這樣命名:
產生式的左部名稱 _ 點號位置
如狀態
IfElseBranch -> <IF> <LPARENT> · Assignment <RPARENT> BasicBlock ElseBlock
可以命名爲“狀態IfElseBranch_2”。
#############################################
在繼續之前,強烈建議你拿出一張草稿紙在上面畫畫,特別是對於整個LR狀態集合中最亂的狀態1
狀態1移進一個Sentence非終結符就轉移到這個狀態
狀態BasicBlock_SentenceBasicBlock_1
BasicBlock -> Sentence · BasicBlock
BasicBlock -> ·
BasicBlock -> · Sentence BasicBlock
Sentence -> · <EOS>
Sentence -> · IfElseBranch
Sentence -> · WhileLoop
Sentence -> · Declaration
Sentence -> · <IO> Assignment <EOS>
Sentence -> · Assignment <EOS> # 注a
Sentence -> · <BREAK> <EOS>
Sentence -> · <LBRACE> BasicBlock <RBRACE>
IfElseBranch -> · <IF> <LPARENT> Assignment <RPARENT> BasicBlock ElseBlock
WhileLoop -> · <WHILE> <LPARENT> Assignment <RPARENT> BasicBlock
Declaration -> · <TYPE> VariableRegister <EOS>
------------------------------
這個狀態的SR衝突解決跟狀態1相同。它移進一個BasicBlock之後變爲狀態
狀態BasicBlock_SentenceBasicBlock_2
BasicBlock -> Sentence BasicBlock ·
------------------------------
接下來還是狀態1遇到某些終結符作轉移的目標狀態:
遇EOS轉狀態Sentence_EoS_1,該狀態遇任何符號都規約
Sentence -> <EOS> ·
------------------------------
遇IF轉狀態IfElseBranch_1
IfElseBranch -> <IF> · <LPARENT> Assignment <RPARENT> BasicBlock ElseBlock
------------------------------
遇WHILE轉狀態WhileLoop_1
WhileLoop -> <WHILE> · <LPARENT> Assignment <RPARENT> BasicBlock
------------------------------
遇Assignment則Goto到狀態Sentence_AssignmentEoS_1
Sentence -> Assignment · <EOS>
------------------------------
遇BREAK轉狀態Sentence_BreakEoS_1
Sentence -> <BREAK> · <EOS>
------------------------------
遇LBRACE轉狀態Sentence_LBraceBasicBlockRBrace_1
Sentence -> <LBRACE> · BasicBlock <RBRACE>
BasicBlock -> ·
BasicBlock -> · Sentence BasicBlock
Sentence -> · <EOS>
Sentence -> · IfElseBranch
Sentence -> · WhileLoop
Sentence -> · Declaration
Sentence -> · <IO> Assignment <EOS>
Sentence -> · Assignment <EOS> # 注a
Sentence -> · <BREAK> <EOS>
Sentence -> · <LBRACE> BasicBlock <RBRACE>
IfElseBranch -> · <IF> <LPARENT> Assignment <RPARENT> BasicBlock ElseBlock
WhileLoop -> · <WHILE> <LPARENT> Assignment <RPARENT> BasicBlock
Declaration -> · <TYPE> VariableRegister <EOS>
------------------------------
遇INTEGER_TYPE或REAL_TYPE轉狀態Declaration_1
Declaration -> <TYPE> · VariableRegister <EOS>
VariableRegister -> · VariableRegister <COMMA> Variable Initialization
VariableRegister -> · Variable Initialization # 注b
------------------------------
注b:Variable也會扔給另一個分析器去分析,因此該狀態會立即變換分析器。
寫到這裏,這一篇已經很長了,並且大部分的工作都很瑣碎無趣。因此接下來之列出狀態名和轉移關係,這些東西只作爲具體實現的參考。
狀態1遇READ或WRITE轉狀態Sentence_IOAssignmentEoS_1
狀態Sentence_IOAssignmentEoS_1遇Assignment則Goto狀態Sentence_IOAssignmentEoS_2
狀態Sentence_IOAssignmentEoS_2遇EOS轉狀態Sentence_IOAssignmentEoS_3
狀態Sentence_IOAssignmentEoS_3遇任何符號都規約
狀態IfElseBranch_1遇LPARENT轉狀態IfElseBranch_2
狀態IfElseBranch_2遇Assignment則Goto狀態IfElseBranch_3
狀態IfElseBranch_3遇RPARENT轉狀態IfElseBranch_4
狀態IfElseBranch_4遇BasicBlock則Goto狀態IfElseBranch_5 # 注c,解釋在文章最後
狀態IfElseBranch_5遇ELSE轉狀態ElseBlock_1
狀態IfElseBranch_5遇First(Sentence)規約 ElseBlock -> ε 然後Goto狀態 IfElseBranch_6
狀態IfElseBranch_5遇ElseBlock則Goto狀態IfElseBranch_6
狀態IfElseBranch_6遇任何符號都規約
狀態ElseBlock_1遇BasicBlock則Goto狀態ElseBlock_2
狀態ElseBlock_2遇任何符號都規約
狀態WhileLoop_1遇LPARENT轉狀態WhileLoop_2
狀態WhileLoop_2遇Assignment則Goto狀狀態WhileLoop_3
狀態WhileLoop_3遇RPARENT轉狀態WhileLoop_4
狀態WhileLoop_4遇BasicBlock則Goto狀態WhileLoop_5
狀態WhileLoop_5遇任何符號都規約
狀態Sentence_AssignmentEoS_1遇EOS轉狀態Sentence_AssignmentEoS_2
狀態Sentence_AssignmentEoS_2遇任何符號都規約
狀態Sentence_BreakEoS_1遇EOS轉狀態Sentence_BreakEoS_2
狀態Sentence_BreakEoS_2遇任何符號都規約
狀態Sentence_LBraceBasicBlockRBrace_1遇BasicBlock則Goto狀態Sentence_LBraceBasicBlockRBrace_2
狀態Sentence_LBraceBasicBlockRBrace_2遇RBrace轉狀態Sentence_LBraceBasicBlockRBrace_3
狀態Sentence_LBraceBasicBlockRBrace_3遇任何符號都規約
狀態Declaration_1遇Variable則Goto狀態VariableRegister_VariableInitialization_1
狀態VariableRegister_VariableInitialization_1遇ASSIGN轉狀態Initialization_AssignAssignment_1
狀態Initialization_AssignAssignment_1遇Assignment則Goto狀態Initialization_AssignAssignment_2
狀態Initialization_AssignAssignment_2遇任意符號都規約
狀態VariableRegister_VariableInitialization_1遇COMMA或EOS規約Initialization -> ε
狀態VariableRegister_VariableInitialization_1遇Initialization則Goto
狀態VariableRegister_VariableInitialization_2
狀態VariableRegister_VariableInitialization_2遇任意符號都規約
狀態Declaration_1遇VariableRegister則Goto狀態DeclarationVariableRegister # 注d,解釋在文章最後
狀態DeclarationVariableRegister遇COMMA轉狀態Declaration_1
狀態DeclarationVariableRegister遇EOS轉狀態Declaration_3
狀態Declaration_3遇到任何符號都規約
注c:遇到BasicBlock似乎是一件很麻煩的事情,只要那個小點打在這傢伙前面,那就會惹來一大堆項目;不過從另一方面考慮,凡是遇到BasicBlock——狀態BasicBlock_SentenceBasicBlock_1除外——就變換分析器,準確地說,是弄一個新的LRAnalyser放到分析器棧棧頂,然後繼續。這樣可以省很多LR狀態的。
注d:首先,從形式上,這個叫做DeclarationVariableRegister的狀態包含這麼幾個項目:
Declaration -> <TYPE> VariableRegister · <EOS>
VariableRegister -> VariableRegister · <COMMA> Variable Initialization
所以它的名字看起來很詭異。然而,問題在於如果真這樣了,那看起來一個VariableRegister至多導出2個Variable Initialization,這顯然是不科學的。原因在於,實際上狀態Declaration_1
Declaration -> <TYPE> · VariableRegister <EOS>
VariableRegister -> · VariableRegister <COMMA> Variable Initialization
VariableRegister -> · Variable Initialization
是一個項目數量任意多的狀態(注意,項目VariableRegister -> · VariableRegister <COMMA> Variable Initialization這是個左遞歸項目)。因此,有些狀態上面甚至並沒有列舉出來。解決這個問題的方法是對左遞歸產生式導致的缺陷視而不見,在實現的時候,每當規約一次
VariableRegister -> Variable Initialization
就在對應的DeclarationNode中的鏈表內插入對應的對象進去就行了。