由于识别表达式的工作已经委托给其他的分析器去做了,因此这一阶段需要关注的产生式其实很少,它们是:
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中的链表内插入对应的对象进去就行了。
分享到:
相关推荐
在描述中提到的"加上框架代码,构造出LR1语法分析程序",意味着LR1生成器不仅生成状态转换表,还会提供一些基础的框架代码,这些代码可以与状态转换表结合,用于构建完整的LR1解析器。框架代码通常包括输入符号的...
LR(0)分析是编译器设计中的一个重要概念,它是一种自底向上的语法分析方法。LR(0)分析表的构建是理解编译器如何处理输入程序的关键步骤。在这个过程中,我们首先需要理解什么是LR分析,特别是LR(0)分析。 LR分析,...
LR(1) 语法分析编译器项目集构造课程设计 LR(1) 语法分析编译器项目集构造是编译器设计中的一种重要技术,用于实现语法分析。下面是该技术的详细知识点: 一、LR(1) 语法分析器的设计目的和要求 LR(1) 语法分析器...
LR语法分析器是一种用于解析上下文无关文法的高效工具,广泛应用于编译器设计中。LR分析器从左向右扫描输入字符串,并自底向上地进行语法分析,旨在判断输入字符串是否符合文法的语句。以下是关于LR分析器设计的一些...
编译原理是研究编译器构建的理论基础,其中自底向上的语法分析,特别是LR分析,是编译器设计的重要部分。本文将深入探讨LR分析的基本概念、工作原理以及如何使用C++来实现一个LR分析器,并附带实验报告。 **LR分析*...
LR分析是一种在编译原理中广泛使用的自底向上语法分析技术,主要应用于解析程序源代码。LR分析器根据文法规则推导出输入符号串是否符合文法,从而判断其是否为合法的程序结构。本篇文章将深入探讨LR分析方法,特别是...
1. **状态集**: LR(1)分析器通过构造一系列的状态集来进行语法分析,每个状态集包含一组项目。 2. **项目**: 在构造状态集时使用的基本单元,用于表示文法中某个规则的部分匹配状态。 3. **动作表(Action Table)**...
LR(0)语法分析器是一种在编译原理中常见的解析技术,主要应用于处理上下文无关文法。这个程序是用C语言编写的,因此具备高度的可移植性和效率。 LR(0)分析器的核心思想是基于状态转移表,通过自底向上的方式逐层解析...
每个LR状态都包含一个项目集(Item Set),项目集是一组文法规则,每个规则的指针指向下一个待处理的部分。例如,对于规则"A → BC",一个项目可能是"A → B·C",表示我们已经看到"A"并期待"B"。当我们遇到"B"时,...
LR0语法分析程序生成器是一种自动化工具,它用于解析给定的上下文无关文法(Context-Free Grammar, CFG),并生成相应的LR0分析器状态转换表。这个过程是编译器设计中的一个重要环节,因为LR0分析器是编译器前端的...
LR语法分析器的全称是“左到右扫描,右部优先”的语法分析器,其工作原理是自左向右读取输入符号串,并通过一套状态转移表(通常称为LR分析表)来决定如何进行下一步操作。这个过程涉及到了文法、状态、动作和移进/...
LR(0)方法是编译原理中用于语法分析的一种重要技术,主要应用于自底向上的解析过程。这个程序设计的任务是构建并使用LR(0)分析表来解析符合特定语法规则的输入串。LR(0)分析器的构建基于状态机模型,它通过一系列的...
LR1语法分析器是编译原理领域中的一个重要工具,它基于经典的 LR(1) 分析方法,用于解析符合特定上下文无关文法的输入字符串。本项目提供了Java语言实现的LR1语法分析器,适用于学习和实践编译器设计课程的学生。在...
在编译原理中,LR0(Left-to-Right, Shift-Reduce, 0-follow set)是一种广泛应用的自底向上的语法分析方法。本项目提供的“LR0语法分析程序”是基于C++实现的,适用于理解和实践编译器设计中的语法分析阶段。下面将...
《编译原理上机源代码LR语法分析器》 编译原理是计算机科学中的核心领域,涉及语言处理、解析技术等方面。LR语法分析是编译器设计中的一个重要环节,用于将高级语言转换为机器可执行的指令。这篇实验报告详细介绍了...
LR(LALR或LR(k))语法分析是一种在编译原理中广泛使用的解析技术,主要用于将源代码转换为可执行代码。LR分析器基于上下文无关语言的文法,能够处理复杂的语法结构。本资源包含的是LR语法分析的源代码,对于学习...
LR(1)语法分析器是一种在编译原理中广泛使用的解析技术,主要用于处理上下文无关文法。这种分析器能够处理更复杂的语言结构,而不仅仅是简单的左递归或右递归表达式。以下是对LR(1)语法分析器及其相关知识点的详细...
在Java中实现LR语法分析器,首先需要对编译原理中的核心概念有深入理解,包括正规表达式、上下文无关文法(CFG)、状态机、项集、分析表等。LR分析器的核心是LR分析表,这个表包含了每个状态下的动作(Shift、Reduce...
在实现一个LR(0)语法分析器时,首先需要对输入的文法进行处理,构造出LR(0)项目集,然后生成动作表和.goto表。在图形界面中,用户可以输入文法,分析器根据文法自动构建解析表,并提供解析功能。用户还可以查看解析...
LR语法分析器是编译器设计中的一种重要工具,用于解析程序源代码,将其转化为中间形式,以便后续的编译阶段进行处理。本实验报告将深入探讨LR(0)语法分析器的工作原理及其应用。 LR分析,全称为“Left-to-Right,...