上下文无关文法

上下文无关文法
上下文无关文法

第三部分上下文无关语言和下推自动机

前面介绍的有限自动机是计算的初级模型,它所接受的正规语言不太关心字符串自身的结构。上下文无关文法(CFL)是一种简单的描述语法规则的递归方法,语言中的字符串由这些规则产生。所有的正规语言都能用上下文无关文法描述,它也可以描述非正规语言。上下文无关文法描述的语法规则更复杂多变,可以在相当大的程度上,描述高级程序设计语言的语法和其他一些形式语言。

类似正则语言对应的抽象机模型是有限自动机,CFL也有对应的抽象机模型。CFL对应的计算模型是在有限自动机的基础上增加存储空间得到,并被设想成无限空间(对应有限自动机的有限空间),采用了一种简单的管理模式,栈(stack),这种新的计算模型(或抽象机)称为下推自动机(pushdown automata),下推是栈最典型的操作。有必要在下推自动机中保留非确定性,确定型下推自动机不能接受所有的CFL,但给定一个CFG,容易构造一个相应的非确定型下推自动机,它在识别字符串过程中的移动模拟了文法的推导过程,这个过程称为分析(parse)。分析不是一定需要下推自动机来完成。

CFL仍然不够通用,不能包括所有有意义的、或有用的形式语言。采用类似第五章的技术,我们将给出一些不是CFL的简单例子,这些技术也用于解决与CFL相关的判定问题。

6 上下文无关文法

6.1 上下文无关文法的定义

为了描述我们在第二部分考察的各种语言,包括一些非正则语言,我们引入一种语言的递归定义方法,称为文法。文法与我们熟悉的语言的语法描述相近,是描述语言和分析语言的有力工具。

问题:文法的形式化定义似乎可以模仿有限自动机,比如5元组或6元组之类。

例子6.1 正如我们在例子2.16中所见,字母表{a, b}上的回文语言pal可以用下面的递归方法描述:

1.Λ, a, b∈pal

2.对每个S∈pal,aSa和bSb也属于pal

3.pal中不包含其他字符串

如果将上面的符号S看成一个变量,代表了所有我们希望计算(比如某种递归算法)的pal 的元素,那么上面的规则1和规则2可以非正式地重新表述如下:

1.S的值可以是Λ, a, b

2.每个S可以写成aSa或bSb的形式

如果我们用→表示“可以取值为”,则可以写出下面的式子:

S→aSa→abSba→abΛba=abba

上面的产生过程可以总结成下面的两组产生式(或称规则):

S→a | b | Λ

S→aSa | bSb

符号“|”表示“或”的含义。上式的含义是aSa或bSb,而不是a或b,即连接运算的优先级高于“|”。我们使用的这套术语中,小写字母a和b表示终结符,大写字母S表示非终结符,或称变量。总共有5条规则,或产生式(production)。符号S是非终结符,也是起始终结

符,即我们生成字符串的起始符号是S,然后不断利用规则替换符号串中的非终结符,直到最终得到一个不含非终结符的符号串,就生成了规则所定义的语言的一个字符串。

例子2中的产生式具有除起始符S外的多个非终结符,我们设想S表示了语言中任意的字符串,其他非终结符表示了其他辅助性的字符串类型,他们可用来方便地生成S表示的字符串。

例子6.2 我们要构造一个生成所有在字母表{a, b}上的非回文字符串的文法,那样的字符串可以描述如下:从字符串的两端开始比较,也许能够发现一些相同的字符对,但最终能够发现一对不同的字符。对于前一种情况,我们可以借用回文语言的产生式:

S→aSa | bSb

如果加入产生式

S→a | b | Λ

则这种左右匹配的形式将体现在整个字符串上,为了中断这种左右匹配的情况,即体现上面提到的第二种情况,我们引入新的非终结符,比如D,表示那些左右两个端点上的字符不同的字符串。且所有符合D的字符串也符合S,因此有S→D。

非终结符的定义比较简单,它唯一的条件是左右两个端点的字符不相同,中间的字符串可以是任意的,我们用非终结符A表示任意的字符串,则有D→aAb | bAa。

A表示任意的字符串,因此A的产生式更简单了,不用添加新的非终结符来简化问题,它的产生式是,A→Λ | aA | bA。

我们把上面三个非终结符的产生式写在一起,就得到了描述所规定语言的产生式集:

S→aSa | bSb | D

D→aAb | bAa

A→Λ | aA | bA

因此一个完整的非回文字符串“abbaaba”的产生过程是,

S→aSa→abSba→abDba→abbAaba→abbaAaba→abbaΛaba→abbaaba

定义6.1 上下文无关文法(context-free grammar, CFG)是一个4元组G=(V, ∑, S, P),其中,V和∑是不相交的有限集,S∈V,P是一组有限的产生式规则,形如A→α,其中A∈V,且α∈(V?∑)*。

V的元素称为非终结符(或变量),∑的元素称为终结符,S是一个特殊的非终结符,称为起始符,P的元素称为语法规则,或产生式。

设G=(V, ∑, S, P)是一个CFG,我们将符号→保留给P的产生式专用,符号?G用于表示字符串的产生过程的每一步,如α?Gβ表示字符串β能够通过替换α中的某些变量(根据P定义的产生式来替换)得到,即如果有

α=α1Aα2且A→γ∈P,则β=α1γα2。

这里能够看到,我们命名为上下文无关(context-free)的含义,即我们在利用产生式规则,用一组符号替换某个非终结符时,与非终结符的上下文无关(此处,A的替换与α1和α2无关),替换是无限制的。

在没有多个文法出现,很清楚用到文法G是什么的情况下,推导符号?G可以简写成?。多步的推导可以写成?*G,即如果存在一组符号串,α=α0、α1、…、αk=β,每个后者都是前者的直接推导,则称为α可以多步推导出β,记为α?*Gβ,简写成α?*β。

定义6.2 G=(V, ∑, S, P)是一个CFG,则G产生的语言是所有可由G产生的字符串组成的集合,即L(G)={x∈∑* | S?*G x}。一个语言L是上下文无关语言(context-free language, CFL),当且仅当存在一个CFG G,使得L=L(G)。

此处可以把文法设想成类似自动机的抽象模型,则一个语言L是CFL当且仅当存在一个CFG G接受它(或识别它),类似前面正则语言与有限自动机的关系,接受(或识别)的含义是两方面的,一方面凡是L中的字符串都能由G产生,另一方面,凡是不属于L的字符串都不能由G产生。前面的例子,能够比较容易地说明这两方面的限制,下面的例子则不是很明显。

例子6.3 考虑语言L={x∈{0, 1}* | n0(x)=n1(x)},其中n i(x)表示数字i在x中的出现次数(即含有相同数目0和1的语言)。写出生成L的CFG。

分析:既然CFG的本质是一个递归定义,那么类似例子6.1,我们可以先发现归纳基础,然后找到归纳推理。显然Λ∈L,另外如果存在一个字符串x∈L,那么得到更长的属于L的字符串的两个方法是,0x1和1x0,即分别在两端添加相同数量的0和1。(当然,还有很多生成方法,比如x01,x10,或在x中插入相同数量的0和1),这样得到三个产生式:S→Λ | 0S1 | 1S0

显然,还遗漏了一些字符串,如0110。我们注意到L中任意两个元素的连接仍然属于L,因此可以增加一个产生式,S→SS。与前面的三个产生式合并,我们得到一个CFG G如下,S→Λ | 0S1 | 1S0 | SS

显然G产生的字符串都满足0和1数目相等这个条件,即L(G)?L,现在只要证明L?L(G)。

令d(x)=n0(x)-n1(x)。则字符串x∈L,当且仅当d(x)=0。因此只需证明每个满足d(x)=0的x,都有x∈L(G)。我们用数学归纳法来证明L?L(G)。归纳对象是字符串的长度|x|。

1.归纳基础,|x|=0且d(x)=0,则x∈L(G),这是显然的,因为此时x=Λ,而Λ可以由产生

式S→Λ得到。

2.归纳推理,设k>=0,每个满足|y|<=k,d(y)=0的字符串y都属于L(G)。要证明每个长

度等于k+1且d(x)=0的字符串x也属于L(G)。分情况讨论如下:

a)如果x以0开始,以1结尾,则可以写成x=0y1,且d(y)=0,根据归纳假设y∈L(G),

即存在一组推导,S?*G y。因此对于x,存在推导,S?0S1?*0y1?x。

b)如果x以1开始,以0结尾,类似a)的处理,能够得到从S到x的推导。

c)如果x以0开始,且以0结尾。则x的长度至少为2,设x=0y0。现在考察x的所

有前缀z的d(z),其中

d(0)=1,d(0y)=d(0y0)-d(0)=-1,

而d(z)随着z的长度增1,至多增加1或减少1,而当前缀从0变化到0y时,d(z)

从1变化到-1,因此存在某个长于0短于0y的前缀z,使得d(z)=0,则x=zw,显

然d(w)=0,由于z、w的长度都<=k,因此z、w都属于L(G),分别存在从S出发

到z和w的推导。为了得到从S出发到x的推导,首先用产生式S→SS。

d)如果x以1开始,且以1结尾,类似c)的处理。

例子6.4 考查语言L={x∈{0, 1}* | n0(x)≠n1(x)},写出它的CFG。

分析:例子6.3看起来与本例关系很大,但实际上没有什么帮助,我们在第8章,将详细解释为什么一个语言的CFG并不能帮助发现它的补集语言的CFG。

L中的字符串可以分为两大类,一类含有的0的个数多于1,另一类含有1的个数多于0,前者组成集合L0,后者组成L1。如果能够发现L0和L1的CFG,由于L= L0?L1,就能够很容易找到L的CFG。

类似以前的做法,我们尽力找到语言L0的递归特征。显然0∈L0,且如果x∈L0,则0x和x0也属于L0,由此得到三个产生式:

S→0 | S0 | 0S

我们无法保证L0中的字符串连接1仍然属于L0,但是如果两个L0中的字符串连接后再连接一个1能够保证新字符串仍然属于L0,因此得到另外三个产生式:

S→1SS | SS1 | S1S

合并这两组产生式就得到了生成L0的CFG G0如下,

S→0 | S0 | 0S | 1SS | SS1 | S1S

类似前面,我们只要证明L0?L(G0)。令d(x)=n0(x)-n1(x),要证明凡是d(x)>0的x都能由G0产生,对x的长度应用数学归纳法。

1.归纳基础,显然|x|=1且d(x)>0时,即x=0,x可由S→0推导,属于L(G0)。

2.归纳推理,设k>=0,对每个|x|<=k且d(x)>0的x都属于L(G0),要证明每个|x|<=k且

d(x)>0的x也属于L(G0)。分情况讨论,此处仅讨论x=0y0的情况(即以0开始和结尾

的情况),

a)x仅由0组成,易证。

b)x至少含有一个1。则x=w1z,现在证明d(w)>0且d(z)>0。设x含有n个1,n>=1,

对每个1=

分两种情况1)不存在wi,使得d(wi)<=0,即所有的d(wi)>0,则d(wn)>0,令

w=wn,z=zn,显然d(z)>0,得证;2)存在一个wi,使得d(w i)<=0,设i=m是第

一个d(wi)<0的前缀,由于x以0开始,一定有d(w1)>0,且d(wm-1)=1>0,令

w=wm-1,z=zm-1,易证d(zm-1)>0,因此得证。

其他两种情况,x以1开始,以1结尾证明略。

类似方法能够得到生成L1的产生式,我们用A表示生成L0的产生式,B表示生成L1的产生式,那么生成语言L的全部文法是:

S→A | B

A→0 | 0A | 1AA | AA1 | A1A

B→1 | 1B | 0BB | BB0 | B0B

注意其中的第一个规则,它恰如其分地表示了合集的含义。

6.2 更多地例子,包括一些熟悉的语言

例子6.5 我们前面已经提到了在计算机科学和其他领域应用很广泛的书写代数表达式的语言。一般地,我们允许任意的标识符和数字常数出现在表达式中,为了简化问题,我们规定只有一个标识符(字母a)、4种两项运算符(+、-、*、/)和左括号、右括号。我们用变量A表示这个简单的表达式语言。它可以嵌入到复杂的表达式语言中。

一个容易发现的递归现象是,如果存在两个表达式,那么可以利用运算符和括号,连接它们构成新的表达式。显然,除了单个标识符a,其他表达式都是通过这个递归过程构造的,因此我们得到下面的文法:

S→S+S | S-S | S*S | S/S | (S) | a

表达式a+(a*a)/a-a的推导过程如下,

S?S-S?S+S-S?a+S-S?a+S/S-S?a+(S)/S-S?a+(S*S)/S-S?a+(a*S)/S-S?...?a+(a*a)/a-a 还存在其他一些推导过程,如

S?S/S?S+S/S?a+S/S?a+(S)/S?a+(S*S)/S?a+(a*S)/S?a+(a*a)/S?a+(a*a)/S-S?a+(a*a)/ a-S?a+(a*a)/a-a

显然,第一种推导比第二种更自然,它符合了通常的运算符的优先级和计算次序。比如,上述表达式通常的计算次序如下:

1.计算a*a,记为A

2.计算A/a,记为B

3.计算a+B,记为C

4.计算C-a

尽管第二种推导不符合通常的表达式结构,或表达式语义,但整个推导是符合文法规定的,是一个合法的推导。一个可能的结论是本例给出的CFG不是最合理的,它没有体现出运算符和括号的优先级,另外好的CFG对每个字符串仅仅提供一种推导过程(如果忽略次序不同的一些

简单替换),在6.4节我们将回到这个问题,它称为CFG的歧义。

例子6.6 上面的例子类似例子3.5和3.6,仅仅描述了程序设计语言(比如C和Pascal)的某个部分,本例将显示,CFG可用来描述程序设计语言的整个语法结构。

C语言有两大类语法结构,包括条件声明()和循环声明()等等,因此有,

→... | | | ...

→if ()

→for (; ; )

...

可以类似构造多个声明连接而成的复合声明,以及等等。

例子6.7 高级程序设计语言的一个大的优点是写出的程序与英文陈述很接近,既然我们可以用CFG去描述高级程序设计语言,那么可不可以用来描述英语呢?

对于一些简单的英语语法,容易找到它的CFG,比如最基本、最典型的英语陈述句的结构是, ,因此可以构造出如下的产生式,

可以进一步构造生成右部三个非终结符的产生式。写出一套合理的、具有广泛性、符合常用语法习惯的英语规则并不困难,且规则的数目也可以不是很多。困难的地方是防止产生不合语法,或合乎语法,然而不合语义,没有人会使用的英语句子。下面的产生式就是一个例子,

→John | Jane

→reminded

|

→himself | herself

上面文法能够产生很多不“正确”的英语句子,如“John reminded herself”,“Jane reminded himself”。可以通过增加文法的复杂性(更多的非终结符和更多的产生式)来消除这种不正确的推导,比如修改产生式为,

而对于例如“Jane reminded Jane”还需要其他消除方法,因为句子“Jane reminded John”是一个好的英语句子。要想区别这两个句子,需要上下文信息,因此本章讨论的CFG无法很深入、精细地刻画自然语言的一些细微特征。

6.3 CFL的合并、连接和Kleene*运算

例子6.4我们构造了一个CFG,它生成的语言是另外两种语言的合集,而且找到了另外两种语言对应的CFG。例子6.4揭示的方法和处理连接和Kleene*运算的相应方法构成了下面定理的基础。

定理6.1 L1和L2是两个CFL,则语言L1?L2、L1L2和L1*也是CFL。

证明:我们用构造法证明。假设生成L1和L2的文法分别是G1=(V1, ∑, S1, P1)和G2=(V2, ∑, S2, P2),以此为基础,分别构造生成上面三种新语言的CFG。

首先假设V1?V2=φ(否则可以通过改名来到达目的),设生成L1?L2的文法Gu=(Vu, ∑, Su, Pu),其中,Su是不属于V1和V2的新增非终结符,Vu=V1?V2?{Su},Pu=P1?P2?{Su→S1 | S2}。

现在证明L(G u)=L1?L2。一方面,任取x∈L1=L(G1),则S1?*x,又由于存在产生式Su?S1,因此Su?*x。类似地,任取x∈L1,也有S?*x。因此L1?L2?L(Gu)。另一方面,任取x∈L(Gu),则存在S?*x,则存在S1?*x或S2?*x,因此L(Gu)?L1?L2。得证。

类似处理连接运算,文法Gc=(Vc, ∑, Sc, Pc),其中Sc是不属于V1和V2的新增非终结符。定义Vc=V1?V2?{Sc},Pc=P1?P2?{Sc→S1S2}。任给x∈L1L2,则x=x1x2,x1∈L1,x2∈L2。因此有S?S1S2?*x1S2?*x1x2=x,即L1L2?L(Gc)。任给x∈L(Gc),即S?*x,则有S1S2?*x,由于V1和V2不相交,则存在x1x2=x,满足S1?*x1,S2?*x2,因此L(Gc)?L1L2。

文法G*=(V, ∑, S, P)生成语言L1*,其中,S是不属于V的新增非终结符。语言L1*所含字符串x的形式是x=x1x2...xk,其中每个xi∈L1,如果能够S连续地产生k个S1,则S能够推导出x,因此得到下面的产生式,S→S1S | Λ,而P=P1?{S→S1S | Λ}。显然,L1*?L(G1*)。任给x∈L(G1*),则存在S?*x,而S推导的第一步一定是S?S1k,因此x∈L(G1)k?L(G1)*。得证。

下面的例子说明了证明的第一步消除V1和V2中的同名是很重要的。比如有两个CFG如下:

S1→XA, X→c, A→a

S2→XB, X→d, B→b

此处非终结符X出现在两个文法中,如果不改名将带来混乱。如S?S1?XA?dA?da,而事实上S1无法推导出da。

推论6.1 每个正则语言都是CFL。

证明:根据正则语言的递归定义(定义3.1),每个正则语言是以三种简单的字符(φ、{Λ}、{a})为基础,多次施加三种运算(合并、连接、Kleeen*)得到。显然三种简单的字符组成的语言是CFL,又根据定理6.1,三种运算保持了CFL的性质,因此根据结构归纳法,命题得证。

例子6.8 语言L是正则表达式,(011+1)*(01)*,表示的正则语言,写出它对应的CFG。

分析:定理6.1的证明过程给出了发现一个CFL的CFG的方法。分别考虑(011+1)*和(01)*。而(011+1)*可进一步转化成考虑(011+1),得到A→011 | 1,然后加上Kleene*运算,得到,B→AB | Λ

类似地,对应正则表达式(01)*的产生式是,

C→DC | Λ, D→01

最后应用连接运算,得到语言L对应的CFG是

S→BC

B→AB | Λ

A→011 | 1

C→DC | Λ

D→01

最后引入的符号S是非终结符,构造过程中引入的符号是辅助非终结符。

例子6.9 语言L={0i1j0k | j>i+k}的CFG。

分析:一个直观的观察是L的每个字符串都是三部分连接而成的:0i、1j、0k。因此似乎可以分别构造三部分语言的CFG,然后通过连接运算构造整个语言的CFG。这种方法是错误的,因为本例语言的三部分是相互关联的,而不是相互独立的,定理6.1揭示的方法仅仅用在相互独立的两个CFG之间的运算。

可行的方法是,将本例中具有关系的参数i、j、k转换成相互无关的参数。由于j>i+k,不妨令j=i+k+m,m>0。则0i1j0k=0i1i1m1k0k。此处的三个参数i、m、k相互独立,因此语言L=L1L2L3,其中,

L1={0i1i | i>=0}

L2={1m | m>0}

L3={1k0k | k>=0}

先分别构造语言L1、L2、L3的CFG,然后利用连接运算构造L的CFG。L1和L3类似,L2易于构造。发现L1的递归性质,Λ∈L1,对每个x∈L1,都有0x1∈L1。因此得到L1的CFG,A→0A1 | Λ。类似地,L3的CFG是C→1C0 | Λ。L2的CFG是B→1B | 1(注意,不是B→Λ,因为L2的字符串长度至少为1)。

因此连接三部分,得到最终的CFG G=(V, ∑, S, P),其中,V={S, A, B, C},∑={0, 1},P={S→ABC, A→0A1 | Λ, B→1B | 1, C→1C0 | Λ}。

字符串01402=(01)(1)(1202)的推导过程如下,

S?ABC?0A1BC?0Λ1BC?011C?0111C0?01111C00?01111Λ00?0111100

6.4 推导树和歧义

对于一个自然语言,比如英语,理解它的句子从理解它的语法结构开始,即了解句子是怎样根据语言的句法规则产生的。给定一个CFG和一个它所生成的字符串x,知道了x的推导过程有助于正确理解x的含义。一个展示推导过程(或推导结构)的自然的方法是画出推导树或分析树。树的根部是文法的起始非终结符,它是推导的起点。树的内部节点对应文法的一个非终结符,比如A,A的子节点对应形如A→α的产生式的右部α的每个符号。对于形如A→Λ的产生式,标记为A的内部节点的子节点只有一个,即Λ。

以例子 6.5文法为例,S→S+S | S-S | S*S | S/S | (S) | a,存在一个字符串的推导,S?S-S?S*S-S?a*S-S?a*a-S?a*a-a,它对应的推导树如图6-1a所示。另一个字符串的推导,S?S-S?S-S/S?...?a-a/a的推导树如图6-1b。

这两个代数表达式常常用表达式树(expression trees)表示,表达式树是一个二叉树,它的叶节点是标识符或常量,内部节点是运算符(参见图6-2)。表达式树显示了表达式的结构和推导过程,本节提出的推导树类似于这种表达式树。

在一个CFL的字符串的完整推导树上,根节点对应文法的起始非终结符,叶节点(或称终端节点)对应终结符或Λ。有时我们也用推导树表示起于某个普通非终结符的推导结构,或部分推导过程,这种推导树的根节点不一定是起始非终结符,叶节点也不一定是终结符。

推导的每一步都是用某个产生式的右部代替左部的非终结符,每个推导都由一组这样的替换按照一定顺序组成。替换的顺序很重要,因此推导S?S+S?a+S?a+a和S?S+S?S+a?a+a 是不同的推导。这种差异是在细节上,当得到S+S时,前者选取了最左非终结符,后者选取了最右非终结符。两种推导对应的推导树是完全一样的,因此可以认为推导过程中的细微的次序差异是不重要的。推导树完全反映了推导中用到的产生式,但不关心推导中用到的临时节点,或某些产生式的应用次序,这些次序也与字符串的语法结构无关,因此对应相同推导树的推导都认为是相同的推导。

另一个比较两个推导的方法是将推导的过程标准化,比如采用最左原则,即每次替换最左(或第一个)非终结符。如果两个遵循最左原则的推导是不同的,那么认为是“本质”不同的推导。

实际上,最左原则和推导树原则是两个相当的判定标准。一方面,对应不同推导树的最左推导过程是显然不同的。另一方面,对应不同最左推导过程的推导树也是不同的。比如设下面的推导是第一次出现差异的情况,

xAβ?xα1β

xAβ?xα2β

其中,x是终结符组成的字符串,A是非终结符,且α1≠α2,因此体现在推导树则一定不同。

现在定义,一个字符串具有多个(超过1个)推导树当且仅当具有多个最左推导。注意我们也可以采用最右原则,关键是消除一些细节上的差异。

我们发现许多CFG生成的字符串具有多个“本质”不同的生成办法。

定义6.3 CFG G是歧义的(ambiguous)当且仅当存在一个x∈L(G)具有多个推导树(或最左推导过程)。

显然,上面定义的歧义很接近我们日常应用的自然语言句子的歧义。比如一个记者用到的标题“Disabled Fly to See Carter”,如果它出现在美国第39任总统当政时期,对应的推导是:S→ ...。但通常更多的解释是:S→ ...。此处,正确理解一个新闻标题的关键是选择相应的语法推导。

例子6.10 回到例子6.5给出的代数表达式的CFG,我们考察了字符串a+(a*a)/a-a具有的本质不同的推导过程。

分析:本例的CFG形如,S→S+S | S-S | S*S | S/S | (s) | a,看一个简单的例子“a+a+a”,存在两个不同的最左推导:

S?S+S?a+S?a+S+S?a+a+S?a+a+a

S?S+S?S+S+S?a+S+S?a+a+S?a+a+a

它们对应的推导树分别见图6-3a和图6-3b。如果结合具体的代数运算符含义,两者最终含义是相同的,前者等同于a+(a+a),后者等同于(a+a)+a。可见括号具有消除歧义的作用。

从例子6.10容易看到,凡是具有形如A→AαA的产生式的CFG都是有歧义的。然而有很多种引入歧义的方式,有时很难发现并排除它们。

例子6.11 程序设计语言歧义的一个标准的例子是“dangling else”,考虑下面的产生式:→ if () |

if () else |

现在考虑声明:if (expr1) if (expr2) f(); else g();

根据上面的产生式,可以有两个推导树,一种将else看成与第一个if相关,另一种将else 看成与第二个if相关。参见图6-4a和图6-4b。在C语言中,为了消除歧义,常常引入括号,如,if (expr1) {if (expr2) f();} else g();

if (expr1) {if (expr2) f(); else g();}

或者修改文法,如

|

→if () else |

→if () |

if () else

目前我们无法证明为什么新文法消除了歧义,但可以直观地解释。生成的都是if-else 匹配的情况,生成的是if-else不匹配的情况。而且出现在else之前的都是,因此不匹配的情况出现在else之后,即else总是与最接近的if匹配。

程序设计语言Modula-2使用了类似括号的方法消除歧义,

→ IF THEN END |

IF THEN

ELSE END |

在上面文法下,图6-4a和图6-4b对应的字符串分别是

IF A1 THEN IF A2 THEN S1 END ELSE S2 END

IF A1 THEN IF A2 THEN S1 ELSE S2 END END

6.5 一个无歧义的代数表达式

尽管有些CFL是“内在”歧义的,即只能由有歧义的CFG生成,但通常意义的歧义是针对文法而言,而不是语言。如果一个CFG是歧义的,常常可能存在(也是我们希望发现的)一个与其相当的非歧义的CFG,本节我们消除例子6.5给出的代数表达式的文法的歧义。

为了简化问题,我们仅仅使用两个运算符“+”和“*”,得到产生式如下,

S→S+S | S*S | (S) | a

如果消除了这两个运算符的歧义,能够类似地消除“-”和“/”的歧义。正如前面讲到,产生式S→S+S能够带来歧义,我们需要消除这种类型的产生式,同时保持各种运算符的优先级,比如*的优先级高于+,且位于前面的+优先级高于后面的+。新文法G1的产生式如下,S→S+T | T

T→T*F | F

F→(S) | a

需要证明两个方面:1)G1与G相当;2)G1没有歧义。为了证明方便,G1中的起始符号改写成S1。

定理6.2 文法G的产生式是S→S+S | S*S | (S) | a,文法G1的产生式是

S1→S1+T | T

T→T*F | F

F→(S1) | a

则L(G)=L(G1)。

证明:首先证明L(G1)?L(G)。对属于L(G1)的字符串x的长度使用数学归纳法。

1.归纳基础,x=a时,显然x∈L(G)

2.归纳推理,设k>=1,每个属于L(G1)、长度<=k的字符串y也属于L(G),要证明如果

x∈L(G1)且|x|=k+1,则x∈L(G)。显然G1推导x的第一步是下面三种情况之一,

S1?S1+T

S1?T?T*F

S1?T?(S1)

如果是情况一,则x=y+z,S1?*y,T?*z,由于S1?*T,因此也有S1?*z,由于y

和z的长度都<=k,因此y和z都属于L(G),而文法G有产生式S→S+S,因此S?*x

成立,即x∈L(G)。情况二和情况三可类似说明。

然后证明L(G)?L(G1),仍然对|x|使用数学归纳法。归纳基础显然。设k>=1,对每个y∈L(G)且|y|<=k,y∈L(G1)也成立,要证明如果x∈L(G)且|x|=k+1,则x∈L(G1)。设x在G上的第一步推导是S?(S),对应x=(y),则|y|

设第一步推导为S?S+S的情况。则x=y+z,y和z都属于L(G1),如果能够得到T?*z,那么根据S1?S1+T,就能得到S1?*x。设x=x1+x2+...+x n,x i∈L(G1),n是这种形式的最大值,因此所有的x i都是由T而不是S1推导得到(如果是S1,则n不是最大值),令y=x1+x2+...+x n-1,z=x n,

即T?*z,得证。

类似证明第一步推导为S?S*S的情况。设x= x1*x2*...*x n,n是这种形式的最大值,则一定有F?*x n,根据S1?T?T*F,得证。

为了证明文法的无歧义性,关键是说明括号的无歧义性。参见练习5.11。

定义6.4 一个字符串的左括号和右括号是平衡的,指的是,它的左括号和右括号的数目相当,而且每个前缀的左括号数目不少于右括号。左括号的配偶(mate)指的是,从该做括号起到与其平衡的右括号。

平衡字符串的每个左括号都有一个配偶。从G1生成的所有字符串都是平衡的。设x∈L(G1),如果能够说明x每步都只有一个最左替换,就证明了G1的无歧义性。

定理6.3 CFG G1的产生式如下,

S1→S1+T | T

T→T*F | F

F→(S1) | a

则G1无歧义。

证明:我们证明L(G1)的每个字符串x都只有一个最左推导。对|x|使用数学归纳法。可以证明一个更普遍的情况,即任何一个从S1、T、F推导出来的字符串x都只有一个最左推导。

1.归纳基础,|x|=1,x=a,显然成立

2.归纳推理,设k>=1,任何一个从S1、T、F推导出来的长度<=k的字符串y都只有最

左推导,要证明对于|x|=k+1的x也成立。分情况讨论:1)设x至少有一个“+”不在

括号中,且设此处的“+”是最右一个不在括号内的“+”号,则任何从S1到x的最左

推导都是下面的形式,S1?S1+T?*y+T?*y+z,y和z都只有一个最左推导,因此x

也只有一个最左推导;2)设x没有一个“+”不在括号内,但有“*”不在括号内,设

此处的“*”是最右一个不在括号内的“*”号,则所有的最左推导中一定存在下面的

一步最左推导步骤,S1?T?T*F?*y*F?*y*z。y和z都只有一个最左推导,因此x

也只有一个最左推导;3)设x的所有“+”和“*”都在括号内,则最初的推导一定是

S1?T?F?(S1),即x=(y),而y只有一个最左推导,因此x也只有一个最左推导。6.6 简化形式和规范形式

歧义是CFG一个不好的性质,本节继续讨论一些直观的方法改善CFG的质量,首先删除一些特定类型(带来麻烦)的产生式,然后将产生式写成标准化的形式,即规范形式。

我们从删除空产生式(Λ-production,形如A→Λ)和单一产生式(一个非终结符被另一个非终结符代替,形如A→B)开始。用一个例子来说明它的作用,文法G上存在一个推导α?β,如果G没有空产生式,则β的长度不小于α,如果G没有单一产生式,则β的长度大于α。如果l 和t分别表示当前字符串的长度和字符串中终结符的个数,则l+t的值每步推导至少增加1,起始终结符S的l+t值为1,长度为k的字符串的l+t值为2k,因此从S到x的推导步骤至多2k-1步。有了推导的上限,就可以用枚举的方法解决一些问题,比如|x|=k,则尝试所有不超过2k-1步的推导过程,考察是否某个推导得到了字符串x,尽管这不是一个高效的方法,但至少提供了一个方法判定x能够被G生成。

显然,如果Λ在语言L(G)之中,则不能消除所有空产生式。我们可以将问题局限在讨论不含Λ的语言和它对应的文法G。

例子6.12 G是一个具有下面产生式的文法

S→ABCBCDA

A→CD

B→Cb

C→a | Λ

D→bD | Λ

分析:显然不能仅仅扔掉空产生式。比如如果扔掉D→Λ,则推导中的D将无法再消除,则文法无法生成任何字符串。因此需要添加一些产生式弥补空产生式的缺失。我们将S→ABCBCDA改写成S→ABC1BC2DA,其中的C1、C2和D都可能取空值或非空值,如果C 和D的空产生式被删除了,则至少需要下面一组产生式来弥补,

S→ABBC2DA | ABC1BDA | ABC1BC2A | ABBDA | ABBC2A | ABC1BA | ABBA

其他非终结符的产生式类似处理。考察非终结符A,尽管没有直接带有空产生式,但可以间接推导出Λ,因此S的产生式应该改写成S→A1BC1BC2DA2来处理。则需要添加的产生式是通过删除集合{A1, A2, C1, C2, D}的某个子集得到,共需增加31个产生式(本身不用再加入了)。

类似的方法处理其他现有的产生式,如果产生式X→α的α含有某个能够推导出Λ的非终结符,则应该加入所有的X→α’,α’来自α中删除部分那些可能推导出Λ的非终结符。这个过程可能产生新的空产生式或重复产生式(形如X→X),这两种产生式忽略不加入。

本例最终消除空产生式的文法共有40个产生式,除了前面提到的32个起始符的产生式,还有下面8个,

A→CD | C | D

B→Cb | b

C→a

D→bD | b

例子6.12蕴含了一个通用的处理方法,为了更系统、更形式化地说明问题,我们先递归定义可空非终结符(即能够推导出Λ的非终结符,或称可空变量),然后给出识别所有可空非终结符的算法。

定义6.5 CFG G=(V, ∑, S, P)的可空非终结符递归定义如下:

1.任何具有空产生式的非终结符,如A→Λ中的A;

2.如果产生式A→B1B2...B n的每个B i都是可空非终结符,则A也是可空非终结符;

3.仅由1和2得到的非终结符。

算法FindNull(发现CFG(V, ∑, S, P)的可空非终结符)

N0={A∈V | P 含有产生式A→Λ};

i=0;

do {

i=i+1;

N i=N i-1?{A | P含有产生式A→α且α∈N i-1* };

while N i≠N i-1

Ni包含了所有的可空非终结符。

定义6.5定义的可空非终结符A等价于A?*Λ,由此可以发现其他直观的算法,参见练习2.50。对于例子6.12,算法的N0={C, D},N1={A, C, D},且最后输出结果是N1。

算法6.1 给定一个CFG G=(V, ∑, S, P),构造不含空产生式的CFG G1={V, ∑, S, P1}。

1.初始化P1=P

2.使用算法FindNull找到V的所有可空非终结符

3.对每个P中的产生式A→α,删除α中的一个或多个可空非终结符,得到的新产生式,

加入到P1中。

4.删除P1中的所有空产生式和重复产生式。

定理6.4 给定一个CFG G=(V, ∑, S, P),G1是算法6.1处理得到的CFG,则G1没有空产生式,且L(G1)=L(G)-{Λ}。

证明:G1没有空产生式是显然的。为了证明L(G1)=L(G)-{Λ},我们证明一个更通用的结论。对每个A∈V和x∈∑*,下面的式子都成立。

A?G*x当且仅当A?G1*x

我们引入一个新的记号,A?G k x,含义是从A到x的推导有k步。首先证明对任意的n>=1,

如果A?G n x,则A?G1*x。对n使用数学归纳法。

1.归纳基础,n=1,即A?G1x,则P中存在A→x,x是非空的,因此P1中也有A→x,则

A?G11x,A?G1*x。

2.归纳推理,n=k时所有|x|<=k的x满足上式。要证明对|x|=k+1的x也满足上式。设A?G*x

的第一步是A→X1X2...X n,对应x=x1x2...x n。其中X i=x i或X i?G<=k x i,如果所有x i≠Λ,显然用到的产生式也在G1中,因此A?G1*x,如果某个x i=Λ,由于A→X1X2...X i-1X i+1...X n 在G1中,则上式仍然成立。

现在证明问题的另一面,任给n>=1,如果A?G1n x,则A?G*x。类似可证。

显然,消除文法的空产生式,需要添加大量新的产生式(很类似,消除FA的空转移需要添加大量新的转移)。于是存在一个问题,添加的新产生式是否带来了新的不好的性质。部分的回答是算法6.1不会带来歧义,即如果原来文法G是非歧义的,则处理后的文法G1也是非歧义的。证明并不困难,参见练习6.38。

单一产生式的消除类似空产生式的消除。比如要删除A→B(更普遍地,A?*B),就要对所有B→α,添加A→α。为了简化讨论,我们消除单一产生式的工作基础是无空产生式的文法。下面先定义A可推导集(A-derivable)。

1.如果存在产生式A→B,则B是A可推导的;

2.如果存在产生式C→B,且C是A可推导的,B≠A,则B是A可推导的;

3.仅包含1和2产生的非终结符。

算法6.2 给定一个没有空产生式的CFG G=(V, ∑, S, P),构造一个没有单一产生式的文法G1=(V, ∑, S, P1)。

1.初始化P1=P

2.对每个A∈V,发现A的可推导集

3.对A的可推导集的每个元素B,如果存在B→α,则添加产生式A→α到P1

4.删除P1中的所有单一产生式

定理6.5 设G是没有空产生式CFG,G1是算法6.2得到的文法,则G1没有单一产生式,且L(G1)=L(G)。

证明:略,参见练习6.37。

值得指出的是,如果文法G是无歧义的,则文法G1也是无歧义的。

例子6.13 G是生成代数表达式的文法,它的产生式如下:

S→S+T | T

T→T*F | F

F→(S) | a

则S的可推导集是{T, F},T的可推导集是{F}。根据算法6.2的第3步,加入产生式S→T*F | (S) | a和T→(S) | a,然后删除单一产生式,最后的产生式集合如下,

S→S+T | T*F | (S) | a

T→T*F | (S) | a

F→(S) | a

除了删除一些“不好”的产生式,如空产生式和单一产生式,对产生式的格式添加更多的限制也是有用的。已经提出了多种产生式的“规范形式”,本节介绍其中的一种,Chomsky范式。

定义 6.6 一个CFG的每个产生式符合下面两个形式中的一种,则称为Chomsky范式(Chomsky normal form, CNF):A→BC和A→a。其中,A、B、C是非终结符,a是终结符。

将一个文法G转换成CNF需要三个步骤。第一步应用算法6.1和算法6.2得到没有空产生式和单一产生式的文法G1,L(G1)=L(G)-{Λ};第二步构造新的文法G2,它的产生式只有下面两种形式:A→B1B2...B k和A→a,其中,A、B i是非终结符,a是终结符,L(G2)=L(G1)。从G1构造G2很简单,给每个终结符a引入一个相应的非终结符X a,添加产生式X a→a,原产生式中的a都替换成X a(除了A→a这类产生式)。文法G2已经很接近Chomsky范式了,产生式的右部要么全是非终结符,要么是单个终结符。第三步,将G2中右部长度超过2的产生式用多个产生式替换。

定理6.6 对于每个CFG G都存在一个符合Chomsky范式的CFG G’,使得L(G’)=L(G)-{Λ}。

证明:略。

例子6.14 CFG G的产生式如下:

S→AACD

A→aAb | Λ

C→aC | a

D→aDa | bDb | Λ

构造G对应的符合Chomsky范式的文法G’。

分析:

1.删除空产生式,得到

S→AACD | ACD | AAC | CD | AC | C

A→aAb | ab

C→aC | a

D→aDa | bDb | aa | bb

2.删除单一产生式,增加S→aC | a,删除S→C。

3.增加产生式的限制,不允许非终结符和终结符在产生式右部混杂出现,得到

S→AACD | ACD | AAC | CD | AC | XaC | a

A→XaAXb | XbXb

C→XaC | a

D→XaDXa | XbDXb | XaXb | XbXb

Xa→a

Xb→b

4.转换成CNF,得到

S→AT1 T1→AT2 T2→CD

S→AU1 U1→CD

S→AV1 V1→AC

S→CD | AC | XaC | a

A→XaW1 W1→Axb

A→XaXb

C→XaC | a

D→XaY1 Y1→Dxa

D→XbZ1 Z1→DXb

D→XaXb | XbXa

Xa→a Xb→b

--------------END-------------

注意文法(grammar)的定义。

定义6.1 上下文无关文法(context-free grammar,CFG)是一个4元组G=(V, ∑, S, P),其中:V是变量集,又称非终结符集

∑是字母表,又称终结符集

S∈V,称开始符

P是文法规则集(或称产生式),形如A→α,A∈V,α∈(V?∑)*

我们保留符号→给P中的规则,而字符串的推导使用符号?。如α?β,表示α中某些字符串根据P中某个规则被替换,从而得到β。如:

α=α1Aα2

β=α1γα2

A→γ是P中的一个规则。这里可以看到上下文无关的含义,即P中的规则在应用中与上下文无关,更确切地说,非终结符A在推导中,与上下文无关。

?*的定义:α?*β,或者α=β,或者存在一个推导链α0 α1 ... αk,αi?αi+1,且α0=α,αk=β。定义6.2 CFG G产生的语言是:L(G)={x | S?*x}

由CFG产生的语言就是CFL。

例子6.3 d(0)=0 d(0y)=-1,因此必定存在一个点,使得前缀d(z)=0。类似连续函数的中值定理。例子6.4 CFL的补集可能不是CFL。

S→0 | S0 | 0S | 1SS | SS1 | S1S

要证明两个语言相等,就要证明互为子集,因此是两个方向的证明。可以用结构归纳法证明S 满足性质n0(x)>n1(x),反向证明较难。

6.2 更多例子,包括一些熟悉的语言

例子6.5 代数表达式

例子6.6 C语言语法

例子6.7 英语的部分语法

6.3 CFL的合并、连接和Kleene*运算

定理6.1 如果L1和L2是上下文无关文法,则语言L1?L2、L1L2、L1*也是上下文无关文法。证明:用构造法。如果G1=(V1, ∑, S1, P1)和G2=(V2, ∑, S2, P2),设V1?V2=φ。

L1?L2:Gu=(Vu, ∑, Su, Pu),Vu=V1?V2?{Su},Pu=P1?P2?{Su→S1|S2}

L1L2:Gc=(Vc, ∑, Sc, Pc),Vu=V1?V2?{Sc},Pu=P1?P2?{Sc→S1S2}

L1*:G*={V, ∑, S, P},V=V1?{S},P=P1?{S→S1S|Λ}

推论6.1 每个正规语言都是上下文无关语言。

根据正规语言的递归定义使用结构归纳法。

定理6.1也给出了发现语言的CFG的方法,例子6.8,(011+1)*(01)*,逐步分解生成文法。

例子6.9 的巧妙分解,注意这里没有一种形式化的方法。

6.4 推导树和歧义

对于英语这样的自然语言,理解一个句子首先要理解它的语法结构,即句子是如何用语法规则推导出来的。一个自然的展示这个推导过程的方式是画推导树(derivation tree)或称分析树(parse tree)。树的根是开始推导的起点,树中每个节点对应一个非终结符,它的儿节点就是相应规则的右部,

表达式树。

推导过程可以是多样的,因为同一时有多个非终结符,可以任意选择扩展非终结符的次序,而最终的结果一般是相同的。为了避免不必要的多样,可以规定每次选择最左的非终结符扩展,称为最左扩展。如果一个字符串也多个不同的最左扩展,则字符串在本质上具有不同的推导树。

定义6.3 如果存在一个字符串x∈L(G),有多个不同的推导树,则称CFG G有歧义。

例子6.10 A→AαA类型的歧义。

例子6.11 dangling else(悬挂)问题。

括号在消除歧义中的作用。

6.5 一个关于代数表达式的非歧义的CFG

尽管有些CFL是“内在歧义”,即它们只能由歧义文法产生,但多数情况下,歧义来自文法,而不是语言本身。因此我们可能为某些歧义文法找到生成能力相当的非歧义文法。

180页提供了消解符合结合律的歧义规则的方法,但我们需要更通用的形式化方法。

定理6.2 CFG G的产生式是:S→S+S | S*S | (S) | a,G1的产生式是:

S1→S1+T|T

T→T*F|F

F→(S1)|a

则L(G)=L(G1)。

(有些荒唐,这也称为一个定理,叫一个例子更合适。)

两个问题:1)如何判断一个CFG是否是歧义的?2)如何找到非歧义的相当CFG?要求用普适的形式化方法。本节研究了一个个案。

定义6.4 左右括号平衡,左括号的配对

定理6.3 证明G1是无歧义文法(183页)

6.6 简化形式和规范形式

空产生式,形如A→Λ

单元产生式,形如A→B

如果CFG中没有这两种规则,则每次推导至少长度不减小,且只有当一个非终结符被一个终结符代替时,长度才不变,其他情况则一定增长。因此如果一个字符串长度为l,且终结符个数为t,则至多要经过l+t步得到(从开始符算起)。因此一个长度为k的终结符字符串,至多需要2k步(更准确地,2k-1步)。因此如果判定一个长度为k的字符串x是否在一个CFL中,枚举所有推导步数不超过2k-1的字符串,然后比较是否与x相同。

定义6.5 可空变量(nullable variable 递归定义)

如果有A→Λ,则A是可空变量

如果A→B1B2...Bn并且B1、B2、...、Bn都是可空变量,则A是可空变量

没有其他变量是可空变量

实质上,可空变量就是A?*Λ。

算法FindNull 发现CFG的所有可空变量。

算法6.1 给定CFG G=(V, ∑, S, P),构造没有空产生式的CFG G1={V, ∑, S, P1}。

定理6.4 G1由算法6.1得到,L(G1)=L(G)-{Λ}

很直观的东西却要搞出很繁琐的证明(数学归纳法),令学生反感,失去兴趣。

如果G是非歧义的,则G1也是非歧义的。

定义A-可推导

算法6.2 给定CFG G=(V, ∑, S, P),构造没有单元产生式的CFG G1={V, ∑, S, P1}。

定理6.5 G1由算法6.2得到,L(G1)=L(G)

类似算法6.1,如果G是非歧义的,则G1也是非歧义的。

定义6.6 一个CFG符合Chomsky范型(CNF),如果产生式只有下面两种形式:

A→BC

A→a

将一个CFG G转换成CNF一般要三个步骤:

应用算法6.1和6.2,得到无空产生式和单元产生式的规则集。

转换成下面两种产生式:A→B1B2...Bk和A→a。

转换成CNF。

上下文无关文法

第三部分上下文无关语言和下推自动机 前面介绍的有限自动机是计算的初级模型,它所接受的正规语言不太关心字符串自身的结构。上下文无关文法(CFL)是一种简单的描述语法规则的递归方法,语言中的字符串由这些规则产生。所有的正规语言都能用上下文无关文法描述,它也可以描述非正规语言。上下文无关文法描述的语法规则更复杂多变,可以在相当大的程度上,描述高级程序设计语言的语法和其他一些形式语言。 类似正则语言对应的抽象机模型是有限自动机,CFL也有对应的抽象机模型。CFL对应的计算模型是在有限自动机的基础上增加存储空间得到,并被设想成无限空间(对应有限自动机的有限空间),采用了一种简单的管理模式,栈(stack),这种新的计算模型(或抽象机)称为下推自动机(pushdown automata),下推是栈最典型的操作。有必要在下推自动机中保留非确定性,确定型下推自动机不能接受所有的CFL,但给定一个CFG,容易构造一个相应的非确定型下推自动机,它在识别字符串过程中的移动模拟了文法的推导过程,这个过程称为分析(parse)。分析不是一定需要下推自动机来完成。 CFL仍然不够通用,不能包括所有有意义的、或有用的形式语言。采用类似第五章的技术,我们将给出一些不是CFL的简单例子,这些技术也用于解决与CFL相关的判定问题。 6 上下文无关文法 6.1 上下文无关文法的定义 为了描述我们在第二部分考察的各种语言,包括一些非正则语言,我们引入一种语言的递归定义方法,称为文法。文法与我们熟悉的语言的语法描述相近,是描述语言和分析语言的有力工具。 问题:文法的形式化定义似乎可以模仿有限自动机,比如5元组或6元组之类。 例子6.1 正如我们在例子2.16中所见,字母表{a, b}上的回文语言pal可以用下面的递归方法描述: 1.Λ, a, b∈pal 2.对每个S∈pal,aSa和bSb也属于pal 3.pal中不包含其他字符串 如果将上面的符号S看成一个变量,代表了所有我们希望计算(比如某种递归算法)的pal 的元素,那么上面的规则1和规则2可以非正式地重新表述如下: 1.S的值可以是Λ, a, b 2.每个S可以写成aSa或bSb的形式 如果我们用→表示“可以取值为”,则可以写出下面的式子: S→aSa→abSba→abΛba=abba 上面的产生过程可以总结成下面的两组产生式(或称规则): S→a | b | Λ S→aSa | bSb 符号“|”表示“或”的含义。上式的含义是aSa或bSb,而不是a或b,即连接运算的优先级高于“|”。我们使用的这套术语中,小写字母a和b表示终结符,大写字母S表示非终结符,或称变量。总共有5条规则,或产生式(production)。符号S是非终结符,也是起始终结

编译原理 上下文无关文法 语法分析习题(附答案)_东华大学 姚励

作 业 一、选择题 1.、程序中出现的错误常数 3.14.15属于__(A)__。 (A) 语法错误 (B) 词法错误 (C) 语义错误 (D) 警告错误 2、表达式α0 αn 代表____(B)____ 。 (A) 直接推导 (B) 广义推导 (C) 推导 (D) 间接推导 3、文法),},,,{)},(,,*,,({2P +=E F T E i G 其中:产生式P 为: i E F F F T T T T E E |)(|*|→→+→ 则句型T+T*i+F 中的句柄是__(A)___。 (A) T (B) i (C) T*i (D) i+F 4、文法b B a aA A AS AB S →→→,|,|与下列哪个正规式等价__(B)___。 (A)+b aa * (B)b aa * (C)*)(ab (D) *)(ab a ; 第二题:(清华大学年考研试题)已知文法G[S]为: S → dAB A → aA | a B → Bb | ε G[S]产生的语言是什么?(请用自然语言或表达式描述语言特征) 答案:da +b * 第三题:(1) 构造一个文法G ,使得:L(G)={a 2m b m |m>0} (2) 构造一个文法G ,使得:L(G)={a n #b n | n>0} (3) 写出以0开头的8进制无符号整数的文法。

答案:(1) S→aaSb | aab (2) S→aSb | a#b (3) S→0 N N→DN | D D→0|1|2|3|4|5|6|7 四、有文法G[S]: S → a | ( T ) |ε T →T,S | S (1)请给出句子(a,(a,a))的最左、最右推导。 (2)请给出句子(a,(a,a))的短语、直接短语和句柄。答案:(1)最左推导: S=>(T) =>(T,S) =>(S,S) =>(a,S) =>(a,(T)) =>(a,(T,S)) =>(a,(S,S)) =>(a,(a,S)) =>(a,(a,a)) 最右推导: S=>(T) =>(T,S) =>(T,(T)) =>(T,(T,S)) =>(T,(T,a)) =>(T,(S,a)) =>(T,(a,a)) =>(S,(a,a)) =>(a,(a,a))

计算机理论导引实验报告2-上下文无关文法(CFG)

HUNAN UNIVERSITY 计算理论导引实验报告 题目:上下文无关文法(CFG)学生姓名: 学生学号: 专业班级:计算机科学与技术2班 上课老师: 实验日期:2014-1-5

一、实验目的 (2) 二、实验内容.......................................................................................... 错误!未定义书签。 三、实验代码.......................................................................................... 错误!未定义书签。 四、测试数据以及运行结果 (9) 五、实验感想 (13)

一、实验目的 1、掌握上下文无关文法概念。 2、掌握用动态规划算法验证某个字符串w是否属于某上下文无关文法。 二、实验内容 对于任意给定的一个上下文无关文法,并对任意字符串w, 用动态规划算法判断是否有w∈L(G)。 编写一个算法/程序,对于给定的输入,可以在多项式时间内判定ACFG。 三、实验代码 #include // 第一类规则,即规则右边只含有两个变元 class Regular_1 { public: int left; int right_1; int right_2; }; // 第二类规则,即规则右边只含有一个终结符或者空 class Regular_2 { public: int left; int right; }; // 表格类,用来存放中间数据 class Table { public: int size; // 表格的行和列的数量,与输入长度相同 int num_v; // 表格中每个单元格最多含有的数量大小,与cfg的变元数量相同 int ***value; // 用来存放数据的三元数组 Table(int num_v,int num_w); // 构造函数,参数指定输入字符串的长度以及cfg变元的数量 ~Table(); // 析构函数 void SetValue(int i,int j,int num); // 向表格第i行j列追加数据num bool CheckValue(int i,int j,int num); // 检查表格第i行j列是否含有数据num,含有则返回true,否则返回false void Print(); // 打印表格的内容

编译原理 第二章习题答案

第2章习题解答 1.文法G[S]为: S->Ac|aB A->ab B->bc 写出L(G[S])的全部元素。 [答案] S=>Ac=>abc 或S=>aB=>abc 所以L(G[S])={abc} ============================================== 2. 文法G[N]为: N->D|ND D->0|1|2|3|4|5|6|7|8|9 G[N]的语言是什么? [答案] G[N]的语言是V+。V={0,1,2,3,4,5,6,7,8,9} N=>ND=>NDD.... =>NDDDD...D=>D......D =============================================== 3.已知文法G[S]: S→dAB A→aA|a B→ε|bB 问:相应的正规式是什么?G[S]能否改写成为等价的正规文法?[答案] 正规式是daa*b*;

相应的正规文法为(由自动机化简来): G[S]:S→dA A→a|aB B→aB|a|b|bC C→bC|b 也可为(观察得来):G[S]:S→dA A→a|aA|aB B→bB|ε ===================================================================== ========== 4.已知文法G[Z]: Z->aZb|ab 写出L(G[Z])的全部元素。 [答案] Z=>aZb=>aaZbb=>aaa..Z...bbb=> aaa..ab...bbb L(G[Z])={a n b n|n>=1} ===================================================================== ========= 5.给出语言{a n b n c m|n>=1,m>=0}的上下文无关文法。 [分析] 本题难度不大,主要是考上下文无关文法的基本概念。上下文无关文法的基本定义是:A->β,A∈Vn,β∈(Vn∪Vt)*,注意关键问题是保证a n b n的成立,即“a与b的个数要相等”,为此,可以用一条形如A->aAb|ab的产生式即可解决。 [答案] 构造上下文无关文法如下: S->AB|A A->aAb|ab B->Bc|c [扩展]

上下文无关的文法生成器

1 引言 几乎所有程序设计语言都是通过上下文无关文法来定义的。一个原因在于它们拥有足够强的表达力来表示大多数程序设计语言的语法,而且从另外一方面,上下文无关文法又足够简单,使得我们可以构造有效的分析算法来检验一个给定字符串是否是由某个上下文无关文法产生的。具体的例子可以参见LR分析器和LL分析器. 2 定义 上下文无关文法(英语:context-free grammar,缩写为CFG),在计算机科学中,若 一个形式文法G = (V, Σ, P, S) 的产生式规则都取如下的形式:A -> α,则谓之。其中A∈V ,α∈(V∪Σ)*。上下文无关文法取名为“上下文无关”的原因就是因为字符 A 总可以被字符串α自由替换,而无需考虑字符 A 出现的上下文。一个形式语言是上下文无关的,如果它是由上下文无关文法生成的. 正则语言和正则表达式,一个正则表达式可以用来表示一个句子集合(正则语言),且每个正则表达式都可以构造出有限状态自动机来判断任意的句子是否属于这个句子集合。因此,用正则表达式来表示正则语言是精确的、可操作的。 那么,可以用正则表达式来表示程序语言(比如 C 语言)所代表的句子集合吗? 很遗憾,答案是否定的。正则表达式毕竟太简单了,无法来表示程序语言这样复杂级别的句子集合。 为了表示程序语言的句子集合,需要采用表达能力更强大的工具——上下文无关语 法(context-free grammar)。 下面以一个简单的例子来说明什么是上下文无关语法,假设有一种非常非常原始的语言,我们把它成为 X 语言,它的句子只有:主语谓语宾语这样一种结构,主语中 只有我、你、他三个词,谓语中只有吃一个词.宾语中只有饭、菜两个词。我们 把它的语法写出下面这样的形式: 语句 -> 主语谓语宾语 主语 -> 我 主语 -> 你 主语 -> 他 谓语 -> 吃 宾语 -> 饭 宾语 -> 菜 可以看出, X 语言总共只有 6 个句子: { 我吃饭, 我吃菜, ..., 他吃菜 }。也就是说,我们可以用上面这个语法来表示 X 语言这个集合,我们可以从第一行的“语句 -> 主语谓语宾语” 开始,分别将主、谓、宾替换成可用的词,从而将所有满足语法的句子都推导出来。对于任意一个句子,我们也可以将句子和此语法来对比,判断它是否属于满足 X 语言。 上面这个语法中的每一行形如“语句 -> 主语谓语宾语” 的式子称为产生式(production)。 产生式左侧的符号(语句、主语、谓语和宾语)称为非终结符(nonterminal), 代表可以继续扩展或产生的符号,也就是说,当在某条产生式的右边遇到了此非终结符的时候,总是可以用本产生式的右边来替换这个非终结符。 而“我、你、他、吃、饭、菜” 这些符号是 X 语言中的词,无法再产生新的符号了,称为终结符(terminal)。终结符只能出现在产生式的右边,非终结符则左边和右边都可以出现。

上下文无关语言和非上下文无关语言

8 上下文无关语言和非上下文无关语言 8.1 上下文无关语言的泵引理 从第6章到第7章,我们给出了两种描述CFL的模型,CFG和PDA。这两种模型都没有提供直接、明确的方法来判断一个形式语言不是CFL。然而,正如例子6.7对自然语言的一个简单考察,我们发现CFG存在描述能力的局限。本节中,我们精确定义和讨论CFL的一个性质,它类似于正则语言的泵引理。利用这个性质能够发现许多不是CFL的语言。 正则语言的泵引理基于这样的事实,如果一个足够长的输入字符串x导致FA在状态转移中,到达某个状态超过一次,即接受路径上存在回路,根据回路容易将x分成三部分,u是回路之前的字符串,v是回路的字符串,w是回路后的字符串,那么在回路上的多次重复,得到的新的字符串也应该被FA接受,即对任意的m>=0,uv m w被FA接受。如果我们用CFG生成(而不是PDA移动)CFL,容易得到类似的观察。设CFG G的一个推导出现同一个非终结符的嵌套重复,如下面的形式, S?*vAz?*vwAyz?*vwxyz 其中,v, w, x, y, z∈∑*。推导过程中,出现了A?*wAy,我们可以多次重复这个推导过程,如 S?*vAz?*vwAyz?*vw2Ay2z?*vw3Ay3z?*...?*vw m Ay m z 又由于A?*x,因此所有这类字符串vxz, vwxyz, vw2xy2z, ..., vw m xy m z都输入语言L(G)。 为了将上面的观察总结成CFL的泵引理,我们必须说明对于足够长的字符串的推导过程中都会出现非终结符的嵌套重复。同时我们也尽量发现分解得到的5个子串:v, w, x, y, z,的一些性质。这类似于我们处理正则语言的泵引理。 在6.6节,我们证明了所有的CFG产生式都可以改写成Chomsky范式,而不会影响CFG 接受语言的能力(唯一的影响是不能接受空字符,由于此处仅仅关心足够长的字符串,因此这个影响可以忽略)。因此我们的讨论可以局限在Chomsky范式(CNF)表示的CFG,显然这类文法得到的推导树都是二叉树,即每个父节点至多有两个子节点。 我们先定义几个与二叉树相关的概念。路径是一串节点组成的序列,前后节点之间有父子关系;路径的长度是路径包含的节点的个数;二叉树的高度是最长路径的长度。由于非终结符数目有限,如果推导树足够高,那么存在一个路径,某个非终结符在该路径上出现了两次,即出现了前面提到的嵌套重复现象。 引理8.1 任给h>=1,如果二叉树的叶结点个数>2h-1,那么该二叉树的高度>h。 证明:这是一个数学问题。我们用数学归纳法证明它的逆反命题:如果二叉树的高度<=h,那么二叉树的叶节点个数<=2h-1。 1.归纳基础,h=1,此时二叉树只有一个根节点,它也是唯一的叶结点,命题成立。 2.归纳推理,设k>=1时,命题成立。要证明k+1时,命题也成立。设二叉树T的高度为 k+1,则它的根节点的两个子树(以子节点为跟的二叉树)的叶节点数都<=2k-1,因此T 的叶节点数<=2k-1+2k-1=2k。得证。 定理8.1 G=(V, ∑, S, P)是形式为CNF的CFG,共有p个非终结符,则对每个长度>=2p+1的属于L(G)的字符串,都能找到5个字符串v, w, x, y, z满足下面的条件: u=vwxyz |wy|>0 |wxy|<=2p+1 vw m xy m z∈L(G), ?m>=0 证明:根据引理8.1,任何一个生成u的推导树高度>=p+2,即至少存在一个长度>=p+2的路径,不妨设d是其中的一个,显然d的最底部是一个终结符,其上的符号都是非终结符,共

3、上下文无关语言练习

第3章、上下文无关语言习题解答 - 练习 3.1 回忆一下例3.3中给出的CFG G 4。为方便起见,用一个字母重新命名它的变元如下: E →E +T|T T →T ×E|F F →(E )|a 给出下述字符串的语法分析树和派生。 a. a b. a+a c. a+a+a d. ((a)) 答: a. E T F a ??? b. E E T T F F a a a ?+?+?+?+ c. E E T E T F T F a F a a a a a ?+?++?++?++?++ d. ()()()(())(())(())(())E T F E T F E T F a ????????? 3.2 a. 利用语言A={a m b n c n | m,n ≥0}和B={a n b n c m | m,n ≥0}以及例3.20(语言B={a n b n c n | n ≥0}不是上下文无关的),证明上下文无关语言在交的运算下不封闭。 b. 利用(a)和DeMorgan 律(定理1.10),证明上下文无关语言在补运算下不封闭。 证明: a.先说明A,B 均为上下文无关文法,对A 构造CFG C 1 S →aS|T|ε T →bTc|ε //生成b n c n 对B,构造CFG C 2 S →Sc|R|ε R →aRb |ε //生成a n b n 由此知 A,B 均为上下文无关语言。 由例3.20, A ∩B={a n b n c n |n ≥0}(假设m ≤n )不是上下文无关语言,所以上下文无关语言在交的运算下不封闭。

b. 用反证法。 假设CFL 在补运算下封闭,则对于(a)中上下文无关语言A,B ,A ,B 也为CFL 。因为CFL 对并运算封闭,所以A B U 也为CFL ,进而知道A B U 为CFL 。由DeMorgan 定律A B A B =U I ,得出A B I 是CFL 。 这与(a)的结论矛盾,所以CFL 对补运算不封闭。 3.3 设上下文无关文法G : R →XRX|S S →aTb|bTa T →XTX|X|ε X →a|b 回答下述问题: a. G 的变元和终结符是什么?起始变元是哪个? 答:变元是:R ,X ,S ,T ;起始变元是R 。终结符是:a ,b ,ε b. 给出L(G)中的三个字符串。答:ab ,ba ,aab 。 c. 给出不在L(G)中的三个字符串。答:a ,b ,ε。 d. 是真是假:T aba ?。答:假 e. 是真是假:* T aba ?。答:真 f. 是真是假:T T ?。答:假 g. 是真是假:* T T ?。答:假 h. 是真是假:* XXX aba ?。答:真 i. 是真是假:* X aba ?。答:假 j. 是真是假:* T XX ?。答:真 k. 是真是假:* T XXX ?。答:真 l. 是真是假:*X ε?。答:假 m. 用普通的语言描述L(G):

上下文无关文法与语言

第 5 章上下文无关文法及语言 现在我们把注意力从正则语言转移到另外一大类语言上来,它们叫做“上下文无关语言”。这个语言类有着自然、递归的表示方法,这种表示方法叫做“上下文无关文法”。从1960年以来,上下文无关文法一直在编译技术中扮演着重要的角色。它们能够把分析器(一类用来在编译过程中发掘源程序结构的程序)的实现从一种费时的、不通用方式的设计工作转变成为一种能够很快完成的工作。近年来,上下文无关文法也被用来描述文档格式:XML(eXtensible Markup Language 可扩展标记语言)中使用的DTD(Document-Type Definition 文档类型定义)就是用来描述Web上的信息交换格式的。 在本章中,我们将首先介绍上下文无关文法的表示方法,然后将介绍怎样用文法来定义语言。我们将会讨论到“语法分析树”──对一个文法处在它所表示的语言的字符串中结构的图形描述。语法分析树是对一个编程语言的语法分析器的产物,也是通常用来获得程序结构的途径。 上下文无关语言还有另外一种等价的自动机表示叫做“下推自动机”。我们将在第6章介绍下推自动机。虽然它不如有穷自动机重要,但仍然要介绍它,原因是作为一种语言的定义机制来说,它跟上下文无关文法具有等价性,后面在第7章研究如何判定上下文无关语言以及研究上下文无关语言的封闭性时,这种等价性是非常有用的。 5.1 上下文无关文法 这一章的内容将从非形式化地介绍上下文无关文法的表示法开始。形式化的定义会在读者了解到这些文法的一些重要的能力之后给出。届时我们将会说明怎样形式地定义一个文法,并将介绍一种叫作“推导”的过程:它能够决定在一个文法的语言中到底有哪些串。 5.1.1一个非形式化的例子 下面来考虑一个“回文(palindrome)”的语言。“回文”是指正向和反向读起来都一样的串,比如otto或者madamimadam(“Madam, I’m Adam,”引自Eve在Eden的花园里听到的第一句话)。换句话说,串w是一个回文当且仅当w = w R。考虑简单些的情况——只需要描述字母表{0, 1}上的回文,这个语言包括像0110,11011这样的串,也包括空串ε,但不包括像011或0101这样的串。 很容易验证这个0,1上的回文语言L pal不是正则语言,要做到这点只需要使用泵引理即可:假设L pal是一个正则语言,令n是与其相关的常数,考虑回文串w = 0n10n,如果L pal 是正则的,那么我们就能够把w写为w = xyz,其中y由第一组0中的若干个(但至少一个)构成。接下来,如果L pal是正则的话那么xz也应该在L pal里。然而由于xz的两端的0的个数不同,进而可知它不可能是回文串,由此得出的矛盾可以推翻前面关于L pal是正则语言的假设。 对于什么样的0,1串在L pal里,有一个自然的、递归的定义,它从一个最基本的显然属于L pal中的串开始,接着利用一个最直观的思想:如果一个串在L pal里,那么它开头和结尾

上下文无关文法与上下文有关文法

上下文无关文法 形式语言理论中一种重要的变换文法,用来描述上下文无关语言,在乔姆斯基分层中称为2型文法。由于程序设计语言的语法基本上都是上下文无关文法,因此应用十分广泛。 简介 上下文无关文法(Context-Free Grammar, CFG) 在计算机科学中,G = (N, Σ, P, S) 的产生式规则都取如下的形式:V -> w,则称之为上下文无关的,其中V∈N ,w∈(N∪Σ)* 。上下文无关文法取名为“上下文无关”的原因就是因为字符V 总可以被字串w 自由替换,而无需考虑字符V 出现的上下文。一个形式语言是上下文无关的,如果它是由上下文无关文法生成的﹙条目上下文无关语言﹚。 来定义的。另一方面,上下文无关文法又足够简单,使得我们可以构造有效的分析算法来检验一个给定字串是否是由某个上下文无关文法产生的。例子可以参见LR 分析器和LL 分析器。

BNF ﹙巴克斯-诺尔范式﹚经常用来表达上下文无关文法。 文法规则使用相似的表示法。名字用斜体表示(但它是一种不同的字体,所以可与正则表达式相区分)。竖线仍表示作为选择的元符号。并置也用作一种标准运算。但是这里没有重复的元符号(如正则表达式中的星号*),稍后还会再讲到它。表示法中的另一个差别是现在用箭头符号“→”代替了等号来表示名字的定义。这是由于现在的名字不能简单地由其定义取代,而需要更为复杂的定义过程来表示,这是由定义的递归本质决定的。

文法规则是定义在一个字母表或符号集之上。在正则表达式中,这些符号通常就是字符,而在文法规则中,符号通常是表示字符串的记号。我们利用C中的枚举类型定义了在扫描程序中的记号;为了避免涉及到 特定实现语言(例如C 记号。此时的记号就是一个固定的符号,如同在保留字while 中或诸如+或: =这样的特殊符号一样,对于作为表示多于一个串的标识符和数的记号来说,代码字体为斜体,这就同假设这个记号是正则表达式的名字(这是它经常的表示)一样。 上下文无关文法的却利用了与正则表达式中极为类似的命名惯例和运算,二 recursive)。 例子 例子1一个简单的上下文无关文法的例子是:S -> aSb | ε 。这个文法产生了语言{anbn : n ≥ 0} 。不难证明这个语言不是正规的。 例子2 这个例子可以产生变量x,y,z 的算术表达式: S -> T + S | T - S | T T -> T * T | T / T | ( S ) | x | y | z 例如字串"( x + y ) * x - z * y / ( x + x )" 就可以用这个文法来产生。 例子3 字母表{a,b} 上 a 和 b 数目不相等的所有字串可以由下述文法产生: S -> U | V U -> T aU | T aT

基于上下文无关文法的句法分析

基于上下文无关文法的句法分析
常宝宝 北京大学计算语言学研究所 chbb@https://www.360docs.net/doc/179423959.html,

句法分析导引
以“词”为单位的分析技术
词语切分(segmentation, tokenization) 形态分析(morphological analysis, lemmatization, stemming) 词类标注(part-of-speech tagging) ……
以“句”为单位的分析技术
句法分析(syntactic parsing) ……
以“篇”为单位的分析技术
指代分析(anaphora resolution) ……
句法分析关心句子的组成规律(词如何组成句子?)

句法学(syntax)
In linguistics, syntax is the study of the rules, or "patterned relations", that govern the way the words in a sentence come together. It concerns how different words are combined into clauses, which, in turn, are combined into sentences. From Wikipedia, the free encyclopedia 语言学中研究句子组成规则的学科是句法学

句子成分分析
句子是词的线性序列,但词和词之间结合的松紧程度 并不一样。
今天 上午 我们 有 两 节 课
句子在构造上具有层次性,较小的成分还可以进一步 组成较大的成分。
今天 上午 我们 有 两 节 课 | | | | | | | | |
不同性质的成分可以有不同的句法功能和分布,可以 区分成不同的类型。
名词短语 (可以充当主谓结构中的主语、述宾结构中的宾语等) 两节课 动词短语 (可以充当主谓结构中的谓语等)

上下文无关文法

上下文无关文法 百科名片 形式语言理论中一种重要的变换文法,用来描述上下文无关语言,在乔姆斯基分层中称为2型文法。由于程序设计语言的语法基本上都是上下文无关文法,因此应用十分广泛。 目录[隐藏] 简介 例子 范式 同态映射下的性质 文法形式和文法的相似性 文法的二义性 子文法类 [编辑本段] 简介 上下文无关文法(Content-Free Grammar, CFG) 在计算机科学中,若一个形式文法G = (N, Σ, P, S) 的 上下文无关文法 产生式规则都取如下的形式:V -> w,则称之为上下文无关的,其中V∈N ,w∈(N ∪Σ)* 。上下文无关文法取名为“上下文无关”的原因就是因为字符V 总可以被字串w 自由替换,而无需考虑字符V 出现的上下文。一个形式语言是上下文无关的,如果它是由上下文无关文法生成的﹙条目上下文无关语言﹚。 上下文无关文法重要的原因在于它们拥有足够强的表达力来表示大多数程序设计语言的语法;实际上,几乎所有程序设计语言都是通

上下文无关文法 过上下文无关文法来定义的。另一方面,上下文无关文法又足够简单,使得我们可以构造有效的分析算法来检验一个给定字串是否是由某个上下文无关文法产生的。例子可以参见LR 分析器和LL 分析器。 BNF ﹙巴克斯-诺尔范式﹚经常用来表达上下文无关文法。 文法规则使用相似的表示法。名字用斜体表示(但它是一种不同的 上下文无关文法 字体,所以可与正则表达式相区分)。竖线仍表示作为选择的元符号。并置也用作一种标准运算。但是这里没有重复的元符号(如正则表达式中的星号*),稍后还会再讲到它。表示法中的另一个差别是现在用箭头符号“→”代替了等号来表示名字的定义。这是由于现在的名字不能简单地由其定义取代,而需要更为复杂的定义过程来表示,这是由定义的递归本质决定的。 同正则表达式类似,文法规则是定义在一个字母表或

《编译原理》考试试题及答案

《编译原理》考试试题及答案(附录) 一、判断题: 1.一个上下文无关文法的开始符,可以是终结符或非终结符。 ( X ) 2.一个句型的直接短语是唯一的。 ( X ) 3.已经证明文法的二义性是可判定的。( X ) 4.每个基本块可用一个DAG表示。(√) 5.每个过程的活动记录的体积在编译时可静态确定。(√) 6.2型文法一定是3型文法。( x ) 7.一个句型一定句子。 ( X ) 8.算符优先分析法每次都是对句柄进行归约。 (应是最左素短语) ( X ) 9.采用三元式实现三地址代码时,不利于对中间代码进行优化。(√) 10.编译过程中,语法分析器的任务是分析单词是怎样构成的。 ( x ) 11.一个优先表一定存在相应的优先函数。 ( x ) 12.目标代码生成时,应考虑如何充分利用计算机的寄存器的问题。 ( ) 13.递归下降分析法是一种自下而上分析法。 ( ) 14.并不是每个文法都能改写成LL(1)文法。 ( ) 15.每个基本块只有一个入口和一个出口。 ( ) 16.一个LL(1)文法一定是无二义的。 ( ) 17.逆波兰法表示的表达试亦称前缀式。 ( ) 18.目标代码生成时,应考虑如何充分利用计算机的寄存器的问题。 ( ) 19.正规文法产生的语言都可以用上下文无关文法来描述。 ( ) 20.一个优先表一定存在相应的优先函数。 ( ) 21.3型文法一定是2型文法。 ( ) 22.如果一个文法存在某个句子对应两棵不同的语法树,则文法是二义性的。 ( ) 二、填空题: 1.( 最右推导 )称为规范推导。 2.编译过程可分为(词法分析),(语法分析),(语义分析和中间代码生成),(代码优化)和(目 标代码生成)五个阶段。 3.如果一个文法存在某个句子对应两棵不同的语法树,则称这个文法是()。 4.从功能上说,程序语言的语句大体可分为()语句和()语句两大类。 5.语法分析器的输入是(),其输出是()。 6.扫描器的任务是从()中识别出一个个()。 7.符号表中的信息栏中登记了每个名字的有关的性质,如()等等。 8.一个过程相应的DISPLAY表的内容为()。 9.一个句型的最左直接短语称为句型的()。 10.常用的两种动态存贮分配办法是()动态分配和()动态分配。 11.一个名字的属性包括( )和( )。 12.常用的参数传递方式有(),()和()。 13.根据优化所涉及的程序范围,可将优化分成为(),()和()三个级别。 14.语法分析的方法大致可分为两类,一类是()分析法,另一类是()分析法。 15.预测分析程序是使用一张()和一个()进行联合控制的。 16.常用的参数传递方式有(),()和()。 17.一张转换图只包含有限个状态,其中有一个被认为是()态;而且实际上至少要有一个()态。

上下文无关文法的基本概念

上下文无关文法的基本概念 定义:上下文无关文法G是一个四元组G = (N,T,P,S),其中 N是非终结符的有限集合; T是终结符或单词的有限集合,它与N不相交; P是形如A →α的产生式的有限集合,其中A∈N,α∈V﹡,V=T∪N S是N中的区分符号,称为开始符号或句子符号。V中的符号称为文法符号,包括终结符和非终结符。 特殊情况:A →ε空产生式 例如,if语句结构的文法产生式表示: stmt →if expr then stmt else stmt Backus - Naur范式(Backus-Naur form)或BNF文法 符号的使用约定: 符号是终结符: 字母表中比较靠前的小写字母,如a,b,c等。 操作符,如+、-等。 标点符号,如括号、逗号等。 数字0,1, (9) 黑体串,如id、if等。 符号的使用约定: 下列符号是非终结符: 字母表中比较靠前的大写字母,如A、B、C等。 字母S,它常常代表开始符号。 小写斜体名字,如expr、stmt等。 字母表中比较靠后的大写字母,如X、Y、Z等,表示文法符号,也就是说,可以是非终结符也可以是终结符。 符号的使用约定: 字母表中比较靠后的小写字母,如u,v,…,z等,表示终结符号的串。 小写希腊字母,如α、β、γ等,表示文法符号的串。因此,一个通用产生式可以写作A →α,箭头左边(产生式左部)是一个非终结符A,箭头右边是文法符号串(产生式右部)。 符号的使用约定: 如果A →α1、A →α2、…、A →αk 是所有以A为左部的产生式(称为A产生式),则可以把它们写成A →α1|α2|…|αk,我们将α1、α2、…、αk称为A的候选式。除非另有说明,否则第一个产生式左部的符号是开始符号。 例1考虑下面的关于简单算术表达式的文法,非终结符为<表达式>和<运算符>,终结符有ID,+,-,*,/,↑,(,)。产生式有 <表达式> → <表达式> <运算符> <表达式> <表达式> → (<表达式>) <表达式> → - <表达式> <表达式> →ID <运算符> → + <运算符> → - <运算符> → * <运算符> → / <运算符> →↑

第二章编译原理习题

例题2.1 是非题 1.因名字都是用标识符表示的,故名字与标识符没有区别。() 2.正规文法产生的语言都可以用上下文无关文法来描述。() 3.上下文无关文法比正规文法具有更强的描述能力。() 分析 1.标识符是高级语言中定义的字符串,一般是以英文字母开头(包括大小写字母)的,由数字、字母和一些特殊字符(如下划线等)组成的一定长度(不同的高级语言对标识符的长度的规定不同)的字符串,它只是一个标志,没有其它含义。名字是用标识符表示的,但名字不仅仅是一个字符串,它还具有属性和值,就象一个人的名字不仅仅是一个符号,对于认识这个人的人来说,它还代表着这个人,因此本题错。 2.乔姆斯基把文法分成4种类型,从4种文法的形式化描述来看,它们的差别主要在对规则形式的约束上。0型文法的规则形式是:每个产生式α→β都满 足,α∈(V N ∪V T )*且至少含有一个非终结符,β∈(V N ∪V T )*,0型文法也叫短语 文法。1型文法的规则形式是:每个产生式α→β均满足|α|≤|β|,仅仅S→ε例外,且S不得出现在任何产生式的右部,1型文法也称上下文有关文法,从两种文法的规则形式来看,1型文法对规则形式的约束比0型文法强,1型文法能描述的语言0型文法也能描述,而0型文法能描述的语言1型文法不一定能够描述,1 型文法是0型文法的特例。2型文法的规则形式是A→β,其中A∈V N ,β∈(V N ∪ V T )*,2型文法也称上下文无关文法,分析2型文法的规则形式不难发现,2型文法是1型文法的特例,也是0型文法的特例。3型文法的规则形式是A→αB或 A→α,其中α∈,A、B∈V N ,3型文法也称正规文法,可以看出3型文法是前面3种文法的特例。从上面的分析可以,正规文法是上下文无关文法的特例,可以用正规文法描述的语言,其正规文法描述的形式也是上下文无关文法的描述形式,即可以用上下文无关文法描述,因此本题对。 3.上下文无关文法是2型文法,正则文法是3型文法,从上题的分析可以看出,3型文法是2型文法的特例,3型文法可以描述的语言都可以用2型文法来描述,而2型文法可以描述的语言则不一定能用3型文法来描述。即2型文法比3型文法的描述能力强,因此本题对。 例题2.2 填空题 1.程序语言是由()和()两方面定义的。 3.文法G所产生的句子的全体的(),将它记为()

相关文档
最新文档