【梳理】编译原理与实践 第三章 上下文无关语法与语法分析(docx)

编译原理

知 识 梳 理

(第一版)

建议先修课程:离散数学、C / C++、数据结构、汇编语言、计算机组成原理。
配套教材:
Kenneth C. Louden Compiler Construction: Principles and Practice


链接:https://pan.baidu.com/s/1SG8BRZnMM56NjSPR4cLcgQ
提取码:0000


三 上下文无关语法与语法分析

编程语言的语法,通常是由上下文无关语法(context-free grammar,上下文无关文法,CFG)的语法规则,以一种与用正则表达式给出被扫描程序识别的标记的词法结构很相像的方式给出的。实际上,CFG使用的命名方式与运算和正则表达式非常相似。主要的不同是,CFG是递归的。例如,if语句允许在自身继续嵌套if语句。而这是正则表达式不允许的。允许使用递归形式,使得编程语言能表示的结构数量比仅使用正则表达式的情况极大增加。
3.1 语法分析过程
通常,语法分析器会逐个读入标记,逐步进行语法分析。在1-pass编译器中,语法分析与其它步骤,包括代码生成,是合并到一起的,所以1-pass编译器并不会显式构建语法树。但更常见的是,编译器的pass数超过1,后续的部件会将语法树作为输入。
语法树的每个结点的结构经常是可变的,以便节省空间。
相比词法分析器(扫描器),语法分析器需要面对的一个困难是:错误处理。扫描器遇到非法标记时,只需生成一条错误信息。某种程度上说,它把困难丢给了语法分析器。语法分析器不仅需要报告错误,还需要从错误状态恢复过来,继续分析语法,以便找到尽量多的错误。有时候,语法分析器会进行错误修复,在此过程推断出一种可能的修正后的代码版本。错误恢复的另一个重要方面是:要给出有意义的错误消息。这一点并不容易,因为完全有可能直到错误真正发生以后才可能检测到该错误。
3.2 上下文无关语法
编程语言的语法结构是上下文无关语法。这种语法的细则很像使用正则式的语言的词法结构,但上下文无关语法还包括递归的规则。
我们用简单的带有加减乘的整数算术表达式作为例子。这些表达式可以这样描述:
exp→exp⁡op exp⁡〖 | (exp) |〗 number
op→+ | - |*
与数字的正则表达式对比:
number=digit digit""
digit=0|1|2|3|4|5|6|7|8|9
在基本正则表达式的规则中,有三种运算:选择、连接和重复。此外还需要等号来定义正则表达式的名称。我们用斜体表示正则表达式的名称,而正体表示正则表达式中包含的实际字符。
表示语法规则时,我们也采用类似的记法。不过,并没有表示重复的符号("
" )了。另外,我们还使用右箭头→而不是等号来为语法规则定义名称。这是因为在语法分析中可能涉及递归,名称不能简单地与定义互换了。
语法规则也会使用正则表达式。
John Backus和Peter Naur首先使用与这个例子类似的方式来描述Algol60语言,因此这种形式的语法规则也称Backus-Naur形式(BN形式,Backus-Naur form,BNF)。

与正则表达式一样,语法规则也在字母表上定义。在正则表达式中,字母表中的符号一般是字符;但在语法规则中,一般是代表字符串的标记。标记为固定符号时(比如保留字while和特殊符号+、:=),我们写成代码字体;当标记代表标识符和数字时,我们写成斜体,这与标记代表正则表达式的名称的情况一致。

给定字母表,上下文无关语法规则的BNF形式包括一串符号。第一个符号是结构名称,第二个符号是元符号→。之后则是一连串的符号,每个这些符号或都在字母表中存在,或为结构名称,或为元符号|。
在非正式术语中,BNF语法规则按如下形式解释:结构名称在箭头左侧。结构包括箭头右侧由竖线分割开来的表达式中的一个。符号序列和每个这样的选择(choice)的结构名称定义了结构的设计布局(layout)。例如:
exp→exp⁡op exp⁡〖 | (exp) |〗 number
op→+ | - |*
第一条规则定义了一个表达式结构,名称为exp,该结构包含三条表达式中的一条:由一个运算符连接的两条表达式、最外层带一对括号的表达式,或一个数字。第二条规则定义了一个运算符可以是+、-或*。
在这里使用的元符号和其它约定(convention,或称规范)虽然与常见的很相似,但并不存在对这些规定的通用标准。元符号→也可写作=、:和::=。如果要将语法规则写成纯文本文件,还需要把用斜体写的名称用别的方法表示。常见的手段是将名称两端加上尖括号⟨⟩,或将斜体表示的标记名写成大写。所以,上例也可能被写成
exp∷=⟨exp⟩ ⟨op⟩ ⟨exp⟩ | (⟨exp⟩) | NUMBER
⟨op⟩∷=+|-|*

我们再介绍另外两种记法。
有时候向BNF记法中引入括号作为元符号是有用的。例如,上述两条规则也可以写成
exp→exp(+ ┤|-| )exp | “(” exp") " | number
在这条规则中,括号是必要的,以便将运算符的选择与右侧的表达式分开。
如果括号作为元符号,则需要将元符号括号与作为标记的括号区分开。将括号加上双引号,就能将括号识别为标记。
在BNF中,括号并不是绝对必要的,因为将括号部分分离为一条新的语法规则是可能的。事实上,如果我们允许相同的名称在箭头左侧出现多次,由|给出的选择运算在语法规则中也不必要。例如,可以将上例写为
exp→exp⁡op exp
exp→(exp)
exp→number
op→+
op→-
op→ *
然而,通常都将选择运算的表达式写在同一条语法规则里,并令每个结构名称在箭头左侧只出现一次。
有时候我们想给一些语法规则更简单的记号。我们会用大写字母表示结构名称,小写字母表示单个标记符号(常常只是一个字符)。在不致混淆时,上述例子可以写为
E→E O E | ( E ) | n
O→+ | - |

上下文无关语法通过定义好的语法规则给出语法上合法的标记符号串的集合。例如,算术表达式
(34-3)42
是合法的,因为它对应
(number-number)number
其中,number标记也有自己的结构,由扫描器决定。这个串是合法的表达式,它的每部分都对应这两条语法规则:
exp→exp⁡op exp⁡〖 | (exp) |〗 number
op→+ | - |

如下的串
(34-3
42
就不是合法的表达式,因为左括号没有对应的右括号,第二条规则要求表达式的最外层具有成对的括号。

语法规则通过推导(derivation)来确定标记串是否合法。推导,就是在语法规则右侧(right-hand side)用选择运算替换结构名称。一次推导从单个结构名称开始,以一串标记(即句子,sentence)结束。推导的每一步使用一条语法规则进行一次替换。
例如,对表达式(34-3)42,推导的每一步是这样的(用刚才的例子作为语法规则):
█(&exp⇒exp" op " exp &&[exp→exp" op " exp]@&⇒exp" op number " &&[exp→" number" ]@&⇒exp
"number " &&[op→ ]@&⇒(exp)“number " &&[exp→(exp)]@&⇒(exp” op " exp)" number " &&[exp→exp" op " exp]@&⇒(“exp op number” )“number " &&[exp→” number" ]" " @&⇒(exp-“number” )"number " &&[op→-]@&⇒(“number” -“number” )“number " &&[exp→"number” ])
推导的步骤使用了不同的箭头,与语法规则中的元符号→不同。这是因为语法规则与推导是不同的:语法规则是一种定义,而推导是通过替换进行构造。

通过推导获得的全部标记串的集合,就是表达式语法定义的语言(language defined by the grammar)。它包括全部语法上合法的表达式。这记作
L(G)={s ┤| exp⇒"* " s}
G代表表达式语法;s代表任意的标记串(句子)ÿ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值