由于识别表达式的工作已经委托给其他的分析器去做了,因此这一阶段需要关注的产生式其实很少,它们是:
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中的链表内插入对应的对象进去就行了。