一、自底向上语法分析
- 一个自底向上语法分析过程对应于为一个输入串构造语法分析树的过程,它从叶子节点开始,逐步向上到达根节点。即把输入串归约成文法的开始符号。
二、规约
-
若一个子串和某个产生式的右部匹配,则用该产生式的左部符号代替这个子串。
-
归约可以看成是推导的逆过程
三、句柄
-
句型的句柄是和某产生式右部匹配的子串,并且,把它归约成该产生式左部的非终结符代表了最右推导过程的逆过程的一步。
-
该段文字中蓝色部分就是句柄
-
如果说文法存在二义性,那么句柄可能不一致
四、用栈实现移进——归约语法分析
- 移进——归约语法分析是自底向上语法分析的一种形式。
- 使用栈来保存文法符号,并用一个输入缓冲区来存放将要进行语法分析的其余符号。
- 移进——归约语法分析的四种动作:
- 移进:把下一个输入符号移进栈
- 归约:分析器知道句柄的右端已在栈顶,然后确定句柄的左端在栈中的位置,再决定用什么样的非终结符代替句柄
- 接受:分析器宣告分析成功
- 报错:分析器发现语法错误,调用错误恢复例程
下面是一个例子
五、id1 * id + id的归约语法分析
关于这个流程,大家可以仔细关注一下,通过这个流程,应该可以大致明白移进——规约的过程
六、移进——归约分析的冲突
七、LR(0)分析表的构造
LR(0)分析表的构造过程涉及构建一个表格,用于指导编译器在分析源代码时确定下一步的动作。这个表格是基于LR(0)自动机,它是对文法的一个抽象表示,用于处理语法分析过程中的不确定性和冲突。以下是LR(0)分析表构造的基本步骤:
-
定义文法:首先,需要有一个形式化的上下文无关文法,它描述了源代码的语法结构。
-
拓广文法:在原始文法的基础上,添加一个起始符号S’和一个产生式S’ → S,其中S是原始文法的开始符号。拓广文法用于处理输入结束的情况。
-
构建项目集族:项目是一个形如A → α.β的产生式,其中A是一个非终结符,α和β是终结符和非终结符的序列,点(.)表示当前分析的位置。项目集族是一系列的项目集,每个项目集包含一组项目。项目集族通过合并那些可以通过推导关系相互到达的项目集来构建。
-
计算闭包:对于每个项目集,计算其闭包,即包含所有可以通过零次或多次应用产生式的右侧推导到达的项目。闭包确保每个项目集都包含所有可能的状态。
-
构建ACTION表:ACTION表是一个二维表,其行代表项目集(状态),列代表输入符号(终结符和$,表示输入结束)。ACTION表的每个条目指定了一个动作,例如移进==(shift)、规约(reduce)或接受(accept)。==
-
构建GOTO表:GOTO表也是一个二维表,其行代表项目集(状态),列代表非终结符。GOTO表的每个条目指定了在遇到非终结符时应转移到的下一个状态。
-
处理冲突:在构建ACTION和GOTO表时,可能会遇到冲突,即对于同一状态和输入符号,有多个可能的动作。LR(0)文法要求没有移进-规约冲突和规约-规约冲突,否则文法不是LR(0)文法。
-
填充分析表:使用ACTION和GOTO表的信息填充LR(0)分析表。分析表的每一行对应一个项目集(状态),每一列对应一个输入符号。
-
验证分析表:最后,验证分析表是否正确地反映了文法的语法结构,确保所有合法的输入序列都可以通过分析表正确地被接受,并且所有非法的输入序列都被正确地拒绝。
-
在实际操作中,由于构造LR(0)分析表可能涉及复杂的算法和大量的手动工作,通常使用自动化工具如编译器生成器(如GCC的GCCXML,ANTLR,或者Yacc/Bison等)来生成分析表。这些工具能够处理文法分析中的细节,生成高效且正确的代码。
百读不如一做,下面是一个例子,读懂了,就知道该怎么做了
求表达式的自动机
关键步骤讲解,本题就是,我们先添加一个其实符号S’,之后就按照要求不断扩展,不断读取,写出这个DFA就可以了
写出增广文法,如果点后是一个非终结符,就要将该非终结符的所有移进状态加进去,对应上文的4,之后只剩终结符了,就不用添加了,然后再不断读取后面的一个符号,即将·移动到下一个字符的后面,根据字符的不同分类
S->bBB
S->·bBB 移进
S->bBB· 归约
S->b·BB 待约
S->bB·B 待约
根据文法和自动机来填写LR(0)语法分析表,根据上一个结果的自动机的状态,action中写终结符和$,goto中写非终结符,有三种选择,移进s,归约r,接受acc,一般情况下,状态1对应的 $要写上acc,如果有归约的状态就写上ri
SLR(1)和LR(0)的语法分析表的区别就是,SLR(1)仅在FOLLOW集下写ri,而LR(0)是在action整行写下ri
八、项目闭包集
项目集闭包
项目集闭包的概念是在编译原理的LR(0)分析表构造中引入的。在LR(0)分析中,一个项目是一个文法产生式和一个点的组合,形如A -> α.β,其中A是非终结符,α和β是符号串(可以是终结符或非终结符),点.表示当前分析的位置。项目集是一个项目的集合,而项目集的闭包则包含了所有可以从初始项目集通过零次或多次应用产生式的右侧推导而到达的项目。
计算项目集闭包的过程包括:
- 遍历项目集中的每个项目。
- 对于每个项目,检查点后面的符号(非终结符)的所有产生式。
- 如果这个符号有非ε产生式,就生成新的项目并添加到闭包中。
- 如果这个符号有ε产生式,也生成新的项目并添加到闭包中。
- 重复这个过程,直到没有新的项目可以添加到闭包中。
项目集的闭包在构造LR(0)分析表时非常重要,因为它帮助确定在给定状态下分析器可以进行的所有可能的动作。通过计算闭包,可以确保分析表涵盖了所有必要的项目,从而正确地进行语法分析。
项目集闭包的概念是LR(0)分析表构造中的一个重要概念。在编译器设计中,特别是当构建语法分析器时,项目集闭包用于确定给定状态下分析器可以进行的所有可能动作。项目集闭包包含了所有可以通过零次或多次应用产生式的右侧推导而到达的项目。
一个项目通常表示为一个产生式,其中点(.)表示当前分析的位置。例如,对于产生式A -> αBβ,A -> α.Bβ表示Bβ尚未被分析的部分。项目集闭包计算过程中,会检查每个项目的点后面的符号,如果这是一个非终结符(即另一个语法规则左部的符号),则将其所有可能的产生式添加到闭包中。如果这个非终结符有ε-产生式(即它可以产生空字符串),则将相应的项目也添加到闭包中。
这个过程一直进行,直到没有新的项目可以添加到闭包中为止。最终得到的闭包项目集代表了在当前状态下,分析器可以进行的所有可能动作。这对于构建有效的语法分析器至关重要,因为它决定了分析器如何根据当前输入和已识别的语法结构来推进分析过程。
简而言之,项目集闭包是一个包含了所有可达项目的集合,这些项目代表了分析器在不同状态下可以进行的所有可能动作。它是编译器设计中语法分析器构建的一个关键步骤。