三、语法分析

语法分析是编译原理中的核心部分,也是最难的部分。这部分的内容最多,涉及的概念也很多。

文法的概念

文法通常表示成四元组:G[S] = (VT,VN,S,ξ)
VT:终结符号集
VN:非终结符号集
S:文法开始符号,是一个特殊的非终结符号集
ξ:产生式的集合

比较抽象的几个概念:可以先不用理解,在后续的学习中再反过来看就好理解了
产生式:定义语法实体的一种书写规则,类似P->a
终结符是不可再分的符号,非终结符是可以再分的类和集合。
也就是非终结符仍然存在由其推得的产生式:T→…
而终结符不存在由其推得的产生式。

用一个例子来帮助理解产生式:
构造标识符的产生文法(由字母开头的数字字母串):
L→a | b | c | … | z
D→0 | 1 | … | 9
产生数字或者字母:
T→L | D
产生一连串的T:
S → T | ST
最后是产生以字母开头的任意长度的数字字母串:
I→L | LS

句型:从开始符S出发经过0步或有限步推导得到的符号串,其中还含有非终结符。
句子:从开始符S出发经过1步或有限步推导得到的符号串,只含终结符,因为只有全部为终结符的时候才表示推出了一种符合的结果。
文法 G[S] 产生的全部句子记为 L[G]

文法的分类

这一部分只要了解就好。

0型文法

α→β的形式,α中含有至少一个非终结符,其实就是产生式的定义,因此0型文法是限制最少的文法,所有的文法都是0型文法

1型文法

相对于0型文法增加了产生式右侧字符长度不小于左边的限制。

2型文法

A→α,其中A∈VN,α∈(VT∪VN)*
也就是左边只能有一个非终结符

3型文法

A→α 或 A→αB 的形式,其中A,B∈VN,α∈VT*
也可以是A→α 或 A→Bα 的形式

四类文法的区别如下:
(1)1型文法中不允许存在A→ε,而23型可以
(2)0、1型文法产生式左部可以存在含有终结符的符号串或是两个以上的非终结符,而2、3型文法左部只能是一个非终结符号。
(3)2、3型是上下文无关文法,后续的学习中主要使用2、3型文法

此外,将正规式写成上下文无关文法也是需要掌握的内容。

推导与语法树

说明:在此后的讨论中用大写字母表示非终结符,用小写字母表示终结符,用希腊字母表示文法符号串

推导与短语

规范推导:每次都对最左或最右的非终结符进行推导
几个术语:短语、句柄、素短语。
由于不好解释,还是看语法树比较好理解。

语法树和二义性

由于一种文法可以推导出多个句子,因此语法树是针对一个句子来画的。
文法的二义性:如果一个句子存在两种不同的最左或最右推导(有两种语法树的画法),那就存在二义性。
如果要消除文法的二义性可以通过:

  • 改变一些运算符的优先性
  • 添加新的非终结符

书上的一个例子来解释语法树比较形象:
有如下文法
在这里插入图片描述

句型 (T + T) * i 对应的语法树为:
在这里插入图片描述
理解了语法树的概念后,再去理解短语,句柄这些概念就比较容易了:

  • 短语:子树的末端结点(即树叶)组成的符号串是相对于子树根的短语。
  • 直接短语:简单子树的末端结点组成的符号串是相对于简单子树根的直接短语。
  • 句柄:最左简单子树的末端结点组成的符号串为句柄。
  • 素短语:子树的末端结点组成的符号串含终结符,且在该子树中不再有含有终结符的更小子树。

下面是一个例子:
在这里插入图片描述

自顶向下的语法分析

自顶向下的语法分析是从文法的开始符出发并寻找一种推导序列,使得这个推导序列恰为输入的符号串。
JavaCC所使用的分析方法就是自顶向下。

递归下降分析法

不确定的分析: 不确定的递归下降分析法实际上就是一个不断试探直到分析出输入串的过程,分析失败就会回溯,直到分析成功或无法分析为止。因此这种方法的效率很低,实际很少使用,故不多介绍。
确定的递归下降分析: 为了提高分析的效率,就要减少回溯,那么就要求文法满足以下的条件:
(1)文法不含左递归,也就是不存在A→A…这样的产生式
(2)当产生式右端有多个候选式时,要求首字符集合两两不相交,也就是走一步就能确定接下来的分析路线,不用回溯。

接下来介绍如何消除左递归和回溯:

  • 消除左递归:
    要消除文法全部的左递归比较困难,这里只要掌握消除产生式中的左递归即可。方法就是引入一个新的非终结符。
    例如:A→Aα | β
    通过这个式子可以知道A产生的是一个β开头,后面全是α的符号串βααα…
    要消除左递归,首先引入新的非终结符A’
    A’的作用是负责产生任意多的α
    那么可以写为:A’→αA’ | ε
    最后文法就改写为:
    A→βA’
    A’→αA’ | ε
  • 消除回溯:
    发生回溯的原因就是存在公共的前缀因子,比如产生式:A→αβ1 | αβ2
    消除的方法将左因子提取出来,然后用一个新的非终结符来表示右侧字符串
    比如产生式:
    A→aAB | a | b
    经过消除后的产生式为:
    A→aA’ | b
    A’→AB | ε

LL(1)分析法

LL(1)分析法又称预测分析法,是一种不带回溯的自顶向下的分析法。LL(1)的含义:第一个L表明从左向右扫描输入串,第二个L表明分析过程中使用最左推导。1表明看一个符号就能确定如何推导(不用回溯)

LL(1)在进行分析的时候有一个输入串和分析栈,分析栈中一开始压入#和开始符号,然后指向输入串的指针开始从左向右移动,如果栈顶的符号可以和输入串当前的符号可以进行推导,就取出进行推导后再压入,只到出现匹配,就将其弹出。
在这里插入图片描述

LL(1)分析法中主要有以下概念需要了解:

  • 分析表:是一个矩阵,规定了一个非终结符在遇到一个符号时该用哪条产生式进行推导,行为非终结符,列为终结符或界符#(结束标志),M[A,a]的内容为非终结符A遇到终结符a时的推导方式。
    对于如下文法(简单算术表达式)的分析表:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • FIRST集:
    α是文法G[S]的任一符号串,则FIRST(α)就是α的所有可能推导的开头终结符或可能的ε。
    构造方法:
    在这里插入图片描述

  • FOLLOW集
    S是文法G[S]的开始符号,如果S经过任意步推得…A的形式,则FOLLOW(A)就是所有句型中紧跟在A之后的终结符或#。
    构造方法:
    在这里插入图片描述

  • 构造分析表:
    在这里插入图片描述

FIRST集在构造分析表时的作用比较容易理解,这里解释一下FOLLOW集的用处:
使用FOLLOW集的情况是ε∈FIRST(A)(或者文法中存在A→ε的产生式),则对任何属于FOLLOW(A)的终结符b,都将A→ε加入到M[A,b]中。
由于A不能推导出右侧为b的产生式,因此就用A→ε将A消除,让b与A左侧的符号进行推导匹配。

自底向上的语法分析

与自顶向下的语法分析相比,自底向上的语法分析无需消除左递归和回溯,一些二义性文法也可以采用自底向上的语法分析,因此该方法的适用性更广。
原理就是从左自右扫描输入串,自底向上进行分析 好像说了一句废话
简单来说就是“移进-归约”,只讲概念比较抽象,不妨用一个例子来进行解释:

  • 有一个文法G[S]:
           S→aAbB
           A→c | Ac
           B→d
    我们要对输入串accbd进行分析,判断其是否为该文法的一个句子。
    分析过程中有一个分析栈,初始时分析栈中只有有一个#号,相应的在输入串的末尾也加一个#号,#号的作用是判断边界。然后不断移进(将输入串从左到右压入分析栈中),在移进的过程中去对照文法产生式的右部,查看是否有匹配的,将其进行归约(比如根据产生式A→c | Ac,c可以规约成A),如果最后分析栈中剩下的是#S,那么分析成功,说明该输入串为该文法的一个句子。
  • 下面来模拟一下分析过程:
    在这里插入图片描述

可以看到自底向上的语法分析是将输入串来还原成最初状态,而自顶向下的语法分析是不断试探来构造出输入串,一个是有根据地还原,一个是穷举式地构造,因此前者的效率要明显高得多。

下面来介绍一下基于自底向上的几种分析方法

算符优先分析法

跟名字一样,这种分析方法特别适合分析程序语言中的各种表达式,并且容易用手工实现
在用这种方法进行分析前需要规定各种运算符之间的优先级
其次,算符优先文法首先得是算符文法,即产生式右部不存在两个相邻的非终结符(类似···QR···这样的形式,QR为非终结符),否则会使得两个运算符之间的操作数存在不确定性。也就是在两个可能会相继出现的终结符之间最多只能有一个非终结符。

对于任何一对终结符a、b,我们说:
(1)a等于b 当且仅当文法G中含有形如P→ ···ab···或P→···aQb···的产生式;
从产生式中可以看出a和b会同时被规约,因此优先级一样

(2)a小于b 当且仅当 G中含有形如P→···aR···的产生式,而R(+=>)b···或R(+=>)Qb···;
从产生式中可以看出b会先被规约,因此b的优先级比a高

(3)a大于b 当且仅当 G中含有形如P→···Rb···的产生式,而R(+=>)···a或R(+=>)···aQ;
从产生式中可以看出a会先被规约,因此a的优先级比b高

为了找出所有满足优先级关系的终结符对,这里要引入两个集合:
在这里插入图片描述
求FIRSTVT集的方法:
(1)若有产生式P→a··· 或 P→Qa···,则a∈FIRSTVT(P)
(2)若有产生式P→Q···,则FIRSTVT(Q)⊂FIRSTVT(P)

求LASTVT集的方法:
(1)若有产生式P→···a 或 P→···aQ,则a∈LASTVT(P)
(2)若有产生式P→···Q,则LASTVT(Q)⊂LASTVT(P)

构造优先级关系表的方法如下:
(1)对形如R→···ab··· 或 R→···aQb···的产生式,有a等于b
(2)对形如R→···aP··· 的产生式,有b∈FIRSTVT(P),则a小于b
(3)对形如R→···Pb··· 的产生式,有a∈LASTVT(P),则a大于b

结合优先级关系表的构造方法以及优先级的定义,FIRSTVT集和LASTVT集的作用就很好理解了。

LR(0)分析法

LR分析法是一种自底向上进行规范规约的语法分析方法,这里的LR分别指自左向右扫描和自底向上规约。相比LL和算符优先文法,LR分析法的限制要少得多,但是手工构建分析表比较困难,需要借助自动生成分析器的工具。

LR(0)分析表

先看一个例子(看看分析表长啥样):
在这里插入图片描述
ACTION表 每一列对应一个终结符,# 符用于表示结束(也有用 $ 符号的),acc 表示分析成功,表中的 si 表示将状态 i 移入分析栈,s 就是 shift 的缩写, ri 表示用第i个产生式进行规约, GOTO表 的每一列对应一个非终结符,表示规约后应该转移到哪个状态。
状态0对应的是初始状态,栈中初始压入一个#和一个状态0,再输入串的右侧也会添一个#。

如何使用分析表进行分析可以参考下图(图片来源:哈工大编译原理慕课)
在这里插入图片描述
可以看到其实分析过程是很简单的,关键的点就在于如何构造分析表。

从上图可以看到一个简易的文法生成的分析表就已经不小了,如果是一个更复杂的文法,用手工进行分析表的构造将会是一件非常困难的事,下面来介绍几个重要的概念:

  • 活前缀
    前缀是一个字符串的任意首部,abc的前缀有:ε、a、ab、abc
    而活前缀是规范推导所得到的句型中,不含有句柄之后符号串的前缀。
    再进行分析工作时,栈里的文法符号应该构成活前缀,否则表示存在语法错误。

  • 项目
    产生式的右部任意位置添加一个圆点,构成一个项目
    产生式 A→aBc对应的4个项目:
    A → • aBc A → a • Bc A → aB • c A → aBc •
    对应的NFA为
    在这里插入图片描述
    因此一个产生式右部有k个符号的话,就对应了k+1个项目

  • 项目集闭包
    等价项目的集合。
    如果将所有项目都作为自动机的状态的话,将会过于庞大,因此将一些等价项目合并为一个状态。
    项目集I的闭包CLOSURE(I):
    (1)I的任何项目都属于CLOSURE(I)
    (2)若A→α•Bβ 或 A→•Bβ属于CLOSURE(I),对于任何B→γ,将项目
    B→•γ添加至CLOSURE(I)

  • 拓广文法(增广文法)
    看图理解:
    在这里插入图片描述
    引入了一个新的开始符号S’,目的是让开始符号只出现在一个产生式的左侧,便于分析器进行接收。
    同时将产生式右部用 | 连接的部分进行了拆分。

最后用一张图来演示分析表的构造过程:
在这里插入图片描述
通过看状态456可以发现,一行里的规约动作都是一样的,但对于某些文法来说,不同的输入串可能要采取不同规约动作,因此就可能会产生冲突。

下图就是一种冲突的方式:

在这里插入图片描述
LR(0)产生冲突的原因在于不会根据后面的符号来决定动作。

SLR分析法

SLR的全称是Simple LR,Simple是因为可以通过FOLLOW集来化解冲突。
当省略后面的括号时,默认k=1,因此SLR=SLR(1)。
在分析表构造算法中,LR会对所有符号进行规约动作,而SLR只对FOLLOW集中的符号进行规约动作,这是SLR的改进之处。其他的构造算法二者是一样的。
但是仅仅利用FOLLOW集来化解冲突还是不够的。因为这只是一个必要条件,而非充分条件。也就是扩大了规约的范围,虽然避免了不合理规约,却没有避免错误规约。
在这里插入图片描述

LR(1)分析法

LR(1)分析法在SLR的基础上进一步增加了在不同位置,要求有不同的后继符号才可以进行规约的限制。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

LALR

对LR(1)分析表进行优化,将“同心”的项目集合并起来,从而减少项目集的个数。
同心项目: 除去搜索符之后的两个项目集如果相同,则称其同心

四种文法分析方法的比较

在这里插入图片描述

二义性文法的应用

首先,二义文法绝不是LR文法。
二义性文法的构造比较简单,产生式更少。
二义性文法一般可以用SLR分析法进行分析,比如可以通过规定算符优先级可以解决冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值