《编译原理》-3.上下文无关文法及分析


分析的任务是确定程序的语法,或称作结构,也正是这个原因,它又被称为语法分析(syntax analysis)。

程序设计语言的语法通常由上下文无关的文法规则给出,其方式同扫描程序识别的由正则表达式提供的记号的词法结构相类似。

上下文无关文法的确利用了与正则表达式中极为类似的命名惯例和运算。二者的主要区别 在于上下文无关文法的规则是递归的(r e c u r s i v e)

用作语言语义结构的数据结构现在也必须是递归的,而不再是线性的(如图用于词法和记号中的一样了),经常使用的机构是树,称作分析树或者语法树。

3.1 分析过程

分析程序的任务是从由扫描程序产生的记号中确定程序的语法结构,以及或隐式或显式地构造出表示该结构的分析树或者语法树。

因此,可以把分析程序看作一个函数,该函数把由扫描程序产生的记号序列作为输入,并产生语法树作为它的输出:
记号序列—(分析程序)—>语法树

通常来说,记号序列不是显式输入参数,但是当分析过程需要下一个记号时,分析程序就调用诸如 getToken的扫描程序过程以从输入中获得它。

在分析程序汇总有一个比在扫描程序中更为复杂的问题,这就是对于错误的处理,在处理程序中,如果遇到一个字符不是正规记号的一部分,那么它只需要产生一个出错记号并消耗掉这个讨厌的字符即可,但对于分析程序而言,它必须不仅报告一个出错信息,还必须从错误状态恢复并继续分析(尽可能多的去找到错误)。分许程序有时会执行错误修复(error repair),此时它从提交给它的非正确版本中推断出一个可能正确的代码版本。错误恢复的一个尤为重要的方面是有意义的错误信息报告以及在 尽可能接近真正错误时继续分析下去。由于要到错误真正地已经发生了分析程序才会发现它, 所以做到这一点并不简单。由于错误恢复技术依赖于所使用的特定分析算法,所以本章先就不 学习它了。

3.2 上下文无关文法

上下文无关文法说明程序设计语言的语法结构,除了上下文无关文法涉及到了递归规则之外,这样的说明与使用正则表达式的词法结构的说明十分类似。例如在一个运算中,就可以使用带有加法,减法和乘法的简单整型算数表达式。
这些表达式可由下面的文法给出:
在这里插入图片描述

3.2.1 与正则表达式的比较

在第2章中为n u m b e r给出的正则表达式规则如下所示:
number = digit digit *
digit = 0|1|2|3|4|5|6|7|8|9

与上面上下文无关文法的样本作比较,基本正则表达式有三种运算:选择,并置和重复,此外还可以用等号来表示正则表达式的含义,此时名字用斜体书写表示与真正的字符序列的区别。

文法规则使用相似的表达法。名字用斜体表示,竖线仍表示选择,并置也仍然为一种标准运算,但是这里没有重复的元符号(也就是正则表达式里的*)。另一个差别是现在用箭头符号取代了正则表达式中的等号来表示名字的定义。。这是由于现在的名字不能简单地由其定义取代, 而需要更为复杂的定义过程来表示,这是由定义的递归本质决定的 。这种格式的文法常称为BNF文法。

3.2.2 上下文无关文法规则的说明

类似于正则表达式,文法规则是定义在一个字母或符号集合上的,在正则表达式中,这些符号通常是字符。而文法规则中,符号通常是字符串的记号。

假设有一个字母表,BNF中的上下文无关文法规则是由符号串组成,第一个符号是结构名字,第二个符号是元字符->,这个符号之后是一个符号串,该串的每个符号都是字母表中的一个符号(即一个结构的名字)或是元符号|。

在非正式术语中,对BNF的文法规则解释如下,规则定义了在箭头左边名字的机构,这个结构被定义为由被竖线分隔开的选择右边的一个选项组成,每个选项中的符号序列和机构名字定义了结构的布局。

两个问题:
在BNF的元符号中使用括号有时很有用,这同括号可在正则表达式中重新安排优先权很相似。

例如:exp → exp ( “+” | “-” | “*”) exp | “(“e x p”)” | number

在这个规则中,括号很有必要,它用于将箭头右侧表达式之间的算符选择组合在一起,这是因为并置优于选择。因此,下面的规则就有了不同的含义:
exp → exp “+” | “-” | “*” exp | “(“e x p”)” | number

注意:当将括号包含为一个元符号时,就有必要区分括号记号和元符号,这一点是通过将括号记号放在引号中做到的,这同正则表达式一样。

由于经常可将括号中的部分分隔成新的文法规则,所以在 B N F中的括号并不像元符号一样 缺之不可。实际上如果允许在箭头左边的相同名字可出现任意次,那么由竖线元符号给出的选 择运算在文法规则中也不是一定要有的。

例如,简单的表达式文法可写作:
exp → exp op exp
exp → (e x p)
exp → n u m b e r
op → +
op → -
op → *

然而,通常把文法规则写成每个结构的所有选择都可在一个规则中列出来,而且每个结构 名字在箭头左边只出现一次。

有时我们需要为说明简便性而给出一些用简短表示法写出的文法规则的示例。在这些情形 中,应将结构名字大写,并小写单个的记号符号(它们经常仅仅是一个字符);因此,按这种 速记方法可将简单的表达式文法写作:
E → E O E|( E ) | n
O → + | - | *

有时当正在将字符如同记号一样使用时,且无需使用代码字体来书写它们,则也可简化表 示法如下:
E → E O E|( E ) | a
O → + | - | *

3.2.3 推导及由文法定义的语言

现在讨论文法规则如何确定一种“语言”或者是记号的正规串集。

上下文无关文法规则确定了为由规则定义的结构的几号符号符合语法的船机。例如。算法表达式
(34-4)*42
与7个记号的正规串相对应
(number - number)* number

其中number记号具有由扫描程序确定的结构且串本身也是一个正规的表达式,这是因为每一个部分都与有文法规则
exp → exp op exp | (e x p) | number
op → + | - | *
给出的选择对应,另一方面,串( 3 4 - 3 * 4 2 就不是正规的表达式,因为缺少右括号与之匹配,且exp 的文法规则中的第2个选择要求括号需要成对生成。

文法规则通过推导确定记号符号的正规串。推导(d e r i v a t i o n)是在文法规则的右边进行选 择的一个结构名字替换序列。推导以一个结构名字开始并以记号符号串结束。在推导的每一个 步骤中,使用来自文法规则的选择每一次生成一个替换。

例如:
在这里插入图片描述
注意推导步骤中的箭头跟文法规则中的箭头不同。

由推导从exp符号中得到的所有记号符号的串集是被表达式的文法定义的语言。这个语言包含了所有合乎语法的表达式,可以符号表示为:
在这里插入图片描述
G表示表达式文法,s代表记号符号的任意数组串,而符号⇒*表示由如前所述的替换序列组成的推导(星号用作指示步骤的序列,这与在正则表达式 中指示重复很相像)。由于它们通过推导“产生” L (G)中的串,文法规则因此有时也称作产 生式(p r o d u c t i o n)

除非特别指出不是,在各种情况下都假定在文法规则中,第 1个列出的就是这个最普通的结构 (在上下文无关文法的数学理论中,这个结构称作开始符号( start symbol))

**结构名也称作非终结符(n o n t e r m i n a l)。相反地,由于字母表中的符号终结推导,所 以它们被称作终结符(t e r m i n a l)。**因为终结符通常是编译应用程序中的记号,所以这两个名字 在使用时是基本同义的。终结符和非终结符经常都被认作是符号

3.3 分析树与抽象语法树

3.3.1 分析树

推导为构造来自一个初始的非终结符的特定终结符的串提供了一个方法,就是推导并唯一地表示出他们所构造的机构。总而言之,对同一个串可以由多个推导。

例如上面我们推导过的(number-number)*number,下面我们给出另一种推导,两种推导唯一的不同是顺序不同。 而这其实是一个很表面的差别,为了把它表示的更清楚,我们需要表示出终结符串的结构,而这些终止符串将推导的主要特征抽取出来,同时却将表面的差别按顺序分解开,这样的表示法就是树结构,称作分析树。
在这里插入图片描述
与推导相对应的分析树(parse tree)是一个作了标记的树,其中内部的节点由非终结符标 出,树叶节点由终结符标出,每个内部节点的子节点都表示推导的一个步骤中的相关非终结符 的替换。

以下是一个简单的示例,推导:
e x p ⇒ exp op exp
⇒ number op exp
⇒ number + e x p
⇒ number + number

与下列分析树对应:
在这里插入图片描述
推导中的第 1步对应于根节点的 3个孩子。第 2步对应于根下最左边的 e x p的单个 n u m b e r孩子,后面的两步与上面的类似。通过将分析树中内部节点编号可将这个对应表示得 更清楚一些,编号采用在相应的推导中,与其相关的非终结符被取代的步骤编号。因此,如果 如下给前一个推导编号:
(1) e x p ⇒ exp op exp
( 2 ) ⇒ number op exp
( 3 ) ⇒ number + e x p
( 4 ) ⇒ number + n u m b e r

可相应地将分析树的内部节点编号如下:
在这里插入图片描述
在这里插入图片描述
同理,不同的推导也会产生不同的分析树(编号不同)。

一般而言,分析树可与许多推导相对应,所有这些推导都表示与终结符的被分析串相同的 基础结构,但是仍有可能找出那个与分析树唯一相关的推导。最左推导( leftmost derivation) 是指它的每一步中最左的非终结符都要被替换的推导。相应地,最右推导( r i g h t m o s t d e r i v a t i o n)则是指它的每一步中最右的非终结符都要被替换的推导。最左推导和与其相关的分 析树的内部节点的前序编号相对应;而最右推导则和后序编号相对应。

3.3.2 抽象语法树

分析树是表述记号串结构的一种十分有用的表示法。在分析树,记号表现为分析树的树叶(自左至右),而分析树内部节点则是推导的各个步骤(按某种顺序),但是,分析树却包括了比纯粹为编译生成可执行所需要更多的信息。

考虑3+4的分析树:
在这里插入图片描述
我们已经讨论了如何用这个树来显示每一个number记号的真是数值。语法引导的转换原则说明了:如同分析树所表示的一样,串3+4的含义或语义应与其语法结构直接相关。在这种情况下,语法引导的转换原则意味着咋分析树表示中应加上数值3和数值4。实际上可以将这个树看作:根代表两个孩子exp子树的数值相加。而另一方面,每个子树又代表它的每个number孩子的值,但是还有一种更简单的方法能够表示与这相同的信息。如下所示:
在这里插入图片描述
这里的根节点仅是被它所表示的运算标出,而叶子节点由其值标出(不是number记号)。类似地,(34-3)*42的分析树也可以用下面的树简单表示出来。
在这里插入图片描述
在这个树中,括号记号实际已经消失了,但是它仍准确地表达着从34减去3,再乘以42的语义内容。

这种树是真正的源代码记号序列的抽象表示。虽然不能从其中重新得到记号序列(不同于分析树),但是它们却包含了转换所需要的所有信息,而且比分析树效率更高。这样的树叫做抽象语法树(abstract syntax tree)或简称为语法树(syntax tree)。分析程序可以通过一个分析树表示所有的步骤,但通常只能构造出一个抽象的语法树。

3.4 二义性

3.4.1 二义性文法

分析树和语法树唯一地表达着语法结构,它与最左和最右推导一样,但并不是对于所有推导都可以。不幸的是,文法有可能允许一个串有多个分析树,例如:
exp → exp op exp | ( exp ) | n u m b e r
op → + | - | *
和串34-3*42,这个串有两个不同的分析树:
在这里插入图片描述
也就对应了两个不同的语法树:
在这里插入图片描述
可以生成带有两个不同分析树的串的文法称为二义性文法。这个文法不能准确地指出程序的语法结构(即使是完全正确正规串本身),所以它是分析程序表示的一个很严重的问题。在某种意义上,二义性文法就像是一个非确定的自动机,两个不同的路径都可以接受到相同的串,但因为缺少合适的算法,又不能构造为唯一的确定自动机。

所以我们必须将二义性文法认为是一种语言语法的不完善说明,而且应该想办法避免。幸运的是,在后面介绍的标准分析算法的测试中二义性文法不能通过测试,而且也开发出了标准技术体 系来解决在程序设计语言中遇到的典型二义性。

解决二义性问题的两个方法

1.设置一个规则,该规则可以在每个二义性情况下,指出那个二义性情况是正确的,这样的规则称为消除二义性规则。这样做的用处在于:它无需修改文法就可以消除二义性,缺点是语言的语法结构再也不能由文法单独提供了。

2.将文法改变成一个强制正确分析树的构造的格式,这样就可以解决二义性。

两种方法都要求先确定正确的树。

在前面的两个语法树中,哪一个是串 3 4 - 3 * 4 2的正确解释呢?第1个树通过把减法节点作 为乘法节点的孩子,指出可将这个表达式等于:先做减法 ( 3 4-3 = 3 1 ),然后再做乘法(3142 = 1 3 0 2 )。相反地,第2个树指出先做乘法(342 =126)然后再做减法( 3 4-1 2 6 =-9 2 )。选择哪个树取 决于我们认为哪一个计算是正确的。人们认为乘法比减法优先( p r e c e d e n c e)。

通常地,乘法和 除法比加法和减法优先。

为了去除在这个简单表达式文法中的二义性,现在可以只需设置消除二义性规则,它建立 了3个运算相互之间的优先关系。其标准解决办法是给予加法和减法相同的优先权,而乘法和 除法则有高一级的优先权

不幸的是,这个规则仍然不能完全地消除文法中的二义性,例如34-3-42,这个串也可能有两个语法树。
在这里插入图片描述
因此,要求在一个消除二义性的规则,他处理每一个加法,减法和乘法的结合性。这个消除二义性的规则通常是规定它们为左结合,它确实消除了简单表达式文法中其他的二义性问题。

因为在表达式中不允许有超过一个算符的序列,有时需要规定运算是非结合性的

例如,可以将简单表达式文法用下面的格式写出:
在这里插入图片描述在这种情况下,34-3-42甚至34-342的表达式都是正规的,但是它们必须带上括号,例如(34-3)-42和34-(342),这样的弯曲括号表达式,就没有必要说明其结合性或优先权了。

我们不再讨论消除二义性的规则,现在来看一下重写文法以消除二义性的方法。请注意,必须找到无需改变正被识别的基本串的办法(正如完全括号表达式的示例所做的一样)

3.4.2 优先权和结合性

为了处理文法中的运算优先权问题,就必须把具有相同优先权的算符归纳在一组中,并为每一 种优先权规定不同的规则。例如,可把乘法比加法和减法优先添加到简单表达式文法,如下所示:

exp → exp addop exp | t e r m
addop → + | -
term → term mulop term | f a c t o r
mulop → *
factor → ( e x p ) | n u m b e r

在这个文法中,乘法被归为term中,而加法则归于exp中,由于exp的基本情况是term,也就是说加法和减法将更加接近根部,也就接受了更低的优先级。 这样将算符放在不同的优先权级别中的办法 是在语法说明中使用B N F的一个标准方法。这种分组称作优先级联(precedence cascade)。

简单算术表达式的最后一种文法仍未指出算符的结合性而且仍有二义性。它的原因在于算 符两边的递归都允许每一边匹配推导(因此也在分析树和语法树)中的算符重复。解决方法是 用基本情况代替递归,强制重复算符匹配一边的递归。
这样将规则 exp → exp addop exp | t e r m

替换为 exp → exp addop term | t e r m

使得加法和减法左结合,而 exp → term addop exp | t e r m 却使得它们右结合。

换而言之,左递归规则使得它的算符在左边结合,而右递归规则使得它们 在右边结合。

为了消除简单算术表达式B N F规则中的二义性,重写规则使得所有的运算都左结合:
exp → exp addop term | t e r m
addop → + |-
term → term mulop factor | f a c t o r
mulop → *
factor → ( exp ) | n u m b e r
在这里插入图片描述

3.4.3 悬挂else问题

考虑例3 . 4中的文法:
statement → if-stmt | o t h e r
if-stmt → i f ( e x p ) s t a t e m e n t
| i f ( exp ) statement e l s e s t a t e m e n t
exp → 0 | 1

由于可选的else的影响,这个文法有二义性,为了看清他,可以考虑下面的串:
if(0)if(1)other else other

这个串有两个分析树
在这里插入图片描述

哪一个是正确的取决于将单个的else部分与第1个或第2个if语句结合,第1个分析树将e l s e部分 与第1个i f语句结合;第 2个分析树将它与第 2个i f语句结合。这种二义性称作悬挂 e l s e问题 (dangling else problem)。

同样也有两种方法:
一是设置相关规则
二是常用的就近原则,else与最近的if组成一个完整的判断条件。

3.4.4 无关紧要的二义性

有些文法有时可能会产生二义性,但是并不影响结果,叫做无关紧要的二义性,例如a+b+c=a+(b+c);此时我们无需关注到底是哪种结合性。

3.5 扩展的表示法:EBNF和语法图

这一部分将对两种扩展表示法进行讲解;

3.5.1 EBNF表示法

重复和可选的结构在程序设计语言中极为普通,因此在 B N F文法规则中也是一样的。所以 当看到B N F表示法有时扩展到包括了用于这两个情况的特殊表示法时,也不应感到惊奇。这些 扩展包含了称作扩展的B N F(extended BNF)或E B N F的表示法。

首先考虑重复情况,如在语句序列中。我们已看到重复是由文法规则中的递归表达,并可 能使用了左递归或右递归,它们由一般规则
A → Aα|β (左递归)和 A → αA |β (右递归)指出,其中α和β是终结符和非终结符的任意串,且在第一个规则中β不以A开始,在第二个规则中β不以A结束。

重复有可能使用与正则表达式所用相同的表示法,即:型号*。则两个规则可被写作非递归规则
A→β α和A→αβ

相反的,EBNF选择用花括号表示重复,上述规则可以写为:
A→β{α}和A→{α}β

E B N F中的可选结构可通过前后用方括号[. . .]表示出来。

3.5.2 语法图

用作可视地表示E B N F规则的图形表示法称作语法图( syntax diagram)。它们是由表示终 结符和非终结符的方框、表示序列和选择的带箭头的线,以及每一个表示文法规则定义该非终 结符的图表的非终结符标记组成。圆形框和椭圆形框用来指出图中的终结符,而方形框和矩形 框则用来指出非终结符。

在这里插入图片描述
语法图是从E B N F而并非B N F中写出来的,所以需要用图来表示重复和可选结构。给出下 面的重复:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.6 上下文无关语言的形式特性(略)

3.7 TINY语言的语法(略)

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值