语法分析:自上而下分析

概述

本节将介绍编译程序构造中的一些典型的语法分析方法。语法分析器的功能,自上而下分析面临的问题,LL(1)分析法

语法分析器的功能

语法分析是编译过程的核心部分。它的任务是在词法分析识别出单词符号串的基础上,分析并判定程序的语法结构是否符合语法规则。

语言的语法结构是用上下文无关文法描述的。因此,语法分析器的工作本质上就是按文法的产生式,识别输入符号串是否为一个句子。这里所说的输入串是指由单词符号(文法的终结符)组成的有限序列。对一个文法,当给你一串(终结)符号时,怎样知道它是不是该文法的一个句子(“程序”)呢?这就要判断,看是否能从文法的开始符号出发推导出这个输入串,或者,从概念上讲,就是要建立一棵与输入串相匹配的语法分析树。
按照语法分析树的建立方法,我们可以粗略地把语法分析办法分成两类,一类是自上而下分析法,另一类是自下而上分析法。本章主要介绍自上而下分析法,下一章我们将介绍自下而上分析法。

自上而下分析面临的问题

现在来讨论自上而下的语法分析方法。顾名思义,自上而下就是从文法的开始符号出发,向下推导,推出句子。我们首先将简单地介绍自上而下分析的一般方法。这种方法是带“回溯”的。下一节,将着重讨论一种广为使用的不带回溯的递归子程序(递归下降)分析方法。
自上而下分析的主旨是,对任何输入串,试图用一切可能的办法,从文法开始符号(根结)出发,自上而下地为输入串建立一棵语法树。或者说,为输入串寻找一个最左推导。这种分析过程本质上是一种试探过程,是反复使用不同产生式谋求匹配输入串的过程。
实现这种自上而下的带回溯试探法的一个简单途径是让每个非终结符对应一个递归子程序。每个这种子程序可作为一个布尔过程。一旦发现它的某个候选与输入串相匹配,就用这个候选去扩展语法树,并返回“真”值;否则,保持原来的语法树和IP值不变,并返回“假”值。
上述这种自上而下分析法存在许多困难和缺点。
首先是文法的左递归性问题。一个文法是含有左递归的,如果存在非终结符P
PPa
含有左递归的文法将使上述的自上而下的分析过程陷入无限循环。即,当试图用P去匹配输入串时,我们会发现,在没有识别任何输入符号的情况下,又得重新要求P去进行新的匹配。因此,使用自上而下分析法必须消除文法的左递归性。
其次,由于回溯,就碰到一大堆麻烦事情。如果我们走了一大段错路,最后必须回头,那么,就应把已经做的一大堆语义工作(指中间代码产生工作和各种表格的簿记工作)推倒重来。这些事情既麻烦又费时间,所以,最好应设法消除回溯。
第三,在上述的自上而下分析过程中,当一个非终结符用某一候选匹配成功时,这种成功可能仅是暂时的。例如,就文法(4.1)而言,考虑输入串x**y。若对A首先使用第二个候选式,A将成功地把它的唯一子结匹配于输入串的第二个符号。但S的第三个子结y与第三个输入符号不匹配。因而,导致了无法识别输入串x**y是一个句子的事实。然而,若A首先使用它的第一个候选**,则整个输入串即可获得成功分析。这意味着,A首先使用第二个候选所得的成功匹配是虚假的。由于这种虚假现象,我们需要更复杂的回溯技术。一般说,要消除虚假匹配是很困难的。但若从最长的候选开始匹配,虚假匹配的现象就会减少一些。
第四,当最终报告分析不成功时,我们难于知道输入串中出错的确切位置。
最后,由于带回溯的自上而下分析实际上采用了一种穷尽一切可能的试探法,因此,效率很低,代价极高。严重的低效使得这种分析法只有理论意义,而在实践上价值不大。
后面,我们将集中讨论不带回溯的自上而下分析法。

LL(1)分析法

左递归消除

直接消除见诸于产生式中的左递归是比较容易的。假定关于非终结符P的规则为

P→Pa | b

其中b不以P开头。那么,我们可以把P的规则改写为如下的非直接左递归形式:

P→bP¢

P¢→aP¢|e ,e为空字)

这种形式和原来的形式是等价的,也就是说,从P推出的符号串是相同的。
如何消除一个文法的一切左递归呢?虽然困难不少,但仍有可能。如果一个文法不含回路(形如PP的推导),也不含以e为右部的产生式,那么,执行下述算法将保证消除左递归(但改写后的文法可能含有以e为右部的产生式)。
消除左递归算法:

  1. 把文法G的所有非终结符按任一种顺序排列成P1,P2,…,Pn;按此顺序执行;
  2. FOR i:=1 TO n DO
    BEGIN
    FOR j:=1 TO i-1 DO
    把形如Pi→Pjg的规则改写成
    Pi→d1g|d2g|…|dkg。其中Pj→d1|d2|…|dk是关于Pj的所有规则;
    消除关于Pi规则的直接左递归性
    END
  3. 化简由2所得的文法。即去除那些从开始符号出发永远无法到达的非终结符的产生规则。

消除回溯、提左因子

欲构造行之有效的自上而下分析器,必须消除回溯。为了消除回溯就必须保证:对文法的任何非终结符,当要它去匹配输入串时,能够根据它所面临的输入符号准确地指派它的一个候选去执行任务,并且此候选的工作结果应是确信无疑的。也就是说,若此候选获得成功匹配,那么,这种匹配决不会是虚假的;若此候选无法完成匹配任务,则任何其它候选也肯定无法完成。换句话说,假定现在轮到非终结符A去执行匹配(或称识别)任务,A共有n个候选a1,a2,…,an,即A→a1 | a2 | … |an。A所面临的第一个输入符号为a,如果A能够根据不同的输入符号指派相应的候选ai作为全权代表去执行任务,那就肯定无需回溯了。在这里A已不再是让某个候选去试探性地执行任务,而是根据所面临的输入符号a准确地指派唯一的一个候选。其次,被指派候选的工作成败完全代表了A。

前面已经说过,欲实行自上而下分析,文法不得含有左递归。令G是一个不含左递归的文法,对G的所有非终结符的每个候选a定义它的终结首符集FIRST(a)为:

FIRST(a)={a | aa…, aÎV T}

特别是,若ae,则规定eÎFIRST(a)。换句话说,FIRST(a)是a的所有可能推导的开头终结符或可能的e。如果非终结符A的所有候选首符集两两不相交,即A的任何两个不同候选a i和a j

FIRST(ai)∩FIRST(a j)=f

那么,当要求A匹配输入串时,A就能根据它所面临的第一个输入符号a,准确地指派某一个候选前去执行任务。这个候选就是那个终结首符集含a的a。
应该指出,许多文法都存在那样的非终结符,它的所有候选的终结首符集并非两两不相交的。例如,通常关于条件句的产生式

语句®if 条件 then 语句 else 语句
     | if 条件 then 语句

就是这样一种情形。

如何把一个文法改造成任何非终结符的所有候选首符集两两不相交呢?其办法是,提取公共左因子。例如,假定关于A的规则是

A→db 1 | db 2 | …| db n | g 1 | g 2 | … g m (其中,每个g 不以d开头)

那么,可以把这些规则改写成

A→dA¢ | g 1 | g 2 | … | g m

A¢→b 1 | b 2 | … | b n

经过反复提取左因子,就能够把每个非终结符(包括新引进者)的所有候选首符集变成为两两不相交。我们为此付出的代价是,大量引进新的非终结符和e-产生式。

LL(1)分析条件

当一个文法不含左递归,并且满足每个非终结的所有候选首符集两两不相交的条件,我们可以找出满足构造不带回溯的自上而下分析的文法条件:

  1. 文法不含左递归,
  2. 对于文法中每一个非终结符A的各个产生式的候选首符集两两不相交。即,若
    A→a 1|a 2|…|an

    FIRST(a i)∩FIRST(a j)=f(i¹j)
  3. 对文法中的每个非终结符A,若它存在某个候选首符集包含e,则
    FIRST(A)∩FOLLOW(A)=f

    如果一个文法G满足以上条件,则称该文法G为LL(1)文法

这里,LL(1)中的第一个L表示从左到右扫描输入串,第二个L表示最左推导,1表示分析时每一步只需向前查看一个符号。
对于一个LL(1)文法,可以对其输入串进行有效的无回溯的自上而下分析。假设要用非终结符A进行匹配,面临的输入符号为a,A的所有产生式为

A→a 1 | a 2 | … | an

1. 若aÎFIRST(a i),则指派a i去执行匹配任务;
2. 若a不属于任何一个候选首符集,则:
(1) 若e属于某个FIRST(ai )且aÎFOLLOW(A),则让A与e自动匹配。
(2) 否则,a的出现是一种语法错误。
根据LL(1)文法的条件,每一步这样的工作都是确信无疑的。

递归下降分析程序构造

当一个文法满足LL(1)条件时,我们就可以为它构造一个不带回溯的自上而下分析程序,这个分析程序是由一组递归过程组成的,每个过程对应文法的一个非终结符。这样的一个分析程序称为递归下降分析器。如果用某种高级语言写出所有递归过程,那就可以用这个语言的编译系统来产生整个的分析程序。例如,考虑文法,它的每个非终结符所对应的递归过程列于如下图1。其中,ADVANCE是指把输入串指示器IP调至指向下一个输入符号;SYM是指IP当前所指的那个输入符号;ERROR为出错诊察处理程序。
对于图1的递归子程序,我们假定在开始工作前,输入串指示器IP指向第一个输入符号。当每个子程序工作完毕之后,IP总是指向下一个未处理的符号。请注意递归子程序E¢,我们知道,关于E¢的规则是

E¢→+TE¢|e

即E¢只有两个候选。第一个候选的开头终结符为+,第二个候选为e。这就是说,当E¢面临输入符号+时就令第一个候选进入工作,当面临任何其它输入符号时,E¢就自动认为获得了匹配(这时,更精确的做法是判断该输入符号是否属于FOLLOW(E¢))。递归过程E¢就是根据这一原则设计的。同理,关于T¢的过程也是如此。

     PROCEDURE  E;             PROCEDURE  T;
     BEGIN                                   BEGIN
       T;E¢                                    F;T¢
         END;                                    END
         PROCEDURE  E¢;            PROCEDURE  T¢;
         IF SYM=‘+’ THEN               IF SYM=‘*’ THEN
         BEGIN                                    BEGIN
                 ADVANCE;            ADVANCE;
        T;E¢                                  F;T¢
               END                                  END;
    PROCEDURE  F;
     IF SYM=‘i’ THEN ADVANCE
     ELSE
               IF SYM=‘(’ THEN
                       BEGIN
                               ADVANCE;
                               E;
                               IF SYM=‘)’ THEN ADVANCE
                               ELSE ERROR
                  END
               ELSE ERROR;

图1 递归子程序

在前面的上下文无关文法产生式(或称巴科斯范式)中我们只用到了两个元符号“→”和“|”。下面我们扩充几个元语言符号:

  1. 用花括号{a}表示闭包运算a*。
  2. 用表示a可任意重复0次至n次,。
  3. 用方括号{a}_1^0表示,即表示a的出现可有可无(等价于a|e)。

引入上述元符号的文法亦称扩充的巴科斯范式。
例如,通常的“实数”可定义为:

  decimal→ [sign]integer.{digit}[exponent]
  exponent→E[sign]integer
  integer→digit{digit}
  sign→+|-

用这种定义系统来描述语法的好处是,直观易懂,便于表示左递归消去和因子提取。对于构造自上而下分析器来说,采用这种定义系统描述文法显然是非常可取的。

预测分析程序

预测分析程序工作过程

预测分析表是一个M[A,a]形式的矩阵。其中,A为非终结符,a是终结符或‘#’(注意,‘#’不是文法的终结符,我们总把它当成输入串的结束符。虽然它不是文法的一部分,但假定它的存在将有助于简化分析算法的描述)。矩阵元素M[A,a]中存放着一条关于A的产生式,指出当A面临输入符号a时所应采用的候选。M[A,a]中也可能存放一个“出错标志”,指出A根本不该面临输入符号a。

栈STACK用于存放文法符号。分析开始时,栈底先放一个‘#’,然后,放进文法开始符号。同时,假定输入串之后也总有一个‘#’,标志输入串结束。
预测分析程序的总控程序在任何时候都是按STACK栈顶符号X和当前的输入符号a行事的,如图4.4。对于任何(X,a),总控程序每次都执行下述三种可能的动作之一:
1. 若X=a=‘#’,则宣布分析成功,停止分析过程。
2. 若X=a ¹‘#’,则把X从STACK栈顶逐出,让a指向下一个输入符号。
3. 若X是一个非终结符,则查看分析表M。若M[A,a]中存放着关于X的一个产生式,那么,首先把X逐出STACK栈顶,然后,把产生式的右部符号串按反序一一推进STACK栈(若右部符号为e,则意味不推什么东西进栈)。在把产生式的右部符号推进栈的同时应做这个产生式相应的语义动作(目前暂且不管)。若M[A,a]中存放着“出错标志”,则调用出错诊察程序ERROR。

预测分析程序的总控程序略微形式一点的描述是:
BEGIN
首先把‘#’然后把文法开始符号推进STACK栈;
把第一个输入符号读进a;
FLAG:=TRUE;
WHILE FLAG DO
BEGIN
把STACK栈顶符号上托出去并放在X中;
IF XÎVT THEN
IF X= a THEN 把下一输入符号读进a
ELSE ERROR
ELSE IF X=‘#’ THEN
IF X=a THEN FLAG:=FALSE ELSE ERROR
ELSE IF M[A,a]={X→X1X2…Xk}THEN
把Xk,Xk-1,…,X1一一推进STACK栈
/* 若X1X2…Xk=e,不推什么进栈 */
ELSE ERROR
END OF WHILE;
STOP /分析成功,过程完毕/
END

预测分析表的构造

下面,我们介绍对于任给的文法G,如何构造它的预测分析表M[A,a]。为了构造预测分析表M,我们需要先构造与文法G有关的集合FIRST和FOLLOW。

首先,我们来讨论如何对每一个文法符号XÎVT∪VN构造FIRST(X)。其办法是,连续使用下面的规则,直至每个集合FIRST不再增大为止:
1. 若XÎVT,则FIRST(X)={X}。
2. 若XÎVN,且有产生式X→a…,则把a加入到FIRST(X)中;若X→e也是一条产生式,则把e也加到FIRST(X)中。
3. 若X→Y…是一个产生式且YÎVN,则把FIRST(Y)中的所有非e-元素都加到FIRST(X)中;若X→Y1Y2…Yk是一个产生式,Y1,…,Yi-1都是非终结符,而且,对于任何j,1£j£i-1,FIRST(Yj)都含有e(即Y1…Yi-1e), 则把FIRST(Yi)中的所有非e-元素都加到FIRST(X)中;特别是,若所有的FIRST(Yj)均含有e,j=1,2,…,k,则把e加到FIRST(X)中。

现在,我们能够对文法G的任何符号串a=X1X2…Xn构造集合FIRST(a)。首先,置FIRST(a)=FIRST(X1){e};若对任何1£j£i-1,eÎFIRST(Xj),则把FIRST(Xi){e}加至FIRST(a)中;特别是,若所有的FIRST(Xj)均含有e,1£j£n,则把e也加至FIRST(a)中。显然,若a=e则FIRST(a)={e}。

对于文法G的每个非终结符A构造FOLLOW(A)的办法是,连续使用下面的规则,直至每个FOLLOW不再增大为止:

  1. 对于文法的开始符号S,置#于FOLLOW(S)中;
  2. 若A→aBb是一个产生式,则把FIRST(b){e}加至FOLLOW(B)中;
  3. 若A→aB是一个产生式,或A®aBb是一个产生式而be(即eÎFIRST(b)),则把FOLLOW(A)加至FOLLOW(B)中。

在对文法G的每个非终结符A及其任意候选a都构造出FIRST(a)和FOLLOW(A)之后,我们现在可以用它们来构造G的分析表M[A,a]。构造分析表算法的思想背景是很简单的。例如,假定A→a是一个产生式,a ÎFIRST(a)。那么,当A呈现于STACK栈之顶且a是当前的输入符号时,a应被当作是A唯一合适的全权代表。因此,M[A,a]中应放进产生式A→a。当a=e或ae时,如果当前面临的输入符号a(可能是终结符或‘#’)属于FOLLOW(A),那么,A→a就认为已自动得到匹配,因而,应把A®a放在M[A,a]中。根据这个思想背景,构造分析表M的算法是:

  1. 对文法G的每个产生式A→a执行第2步和第3步;
  2. 对每个终结符a ÎFIRST(a),把A→a加至M[A,a]中;
  3. 若eÎFIRST(a),则对任何bÎFOLLOW(A)把A→a加至M[A,b]中。
  4. 把所有无定义的M[A,a]标上“出错标志”。

上述算法可应用于任何文法G以构造它的分析表M。但对于某些文法,有些M[A,a]可能持有若干个产生式,或者说有些M[A,a]可能是多重定义的。如果G是左递归或二义的,那么,M至少含有一个多重定义入口。因此,消除左递归和提取左因子将有助于获得无多重定义的分析表M。
可以证明,一个文法G的预测分析表M不含多重定义入口,当且仅当该文法为LL(1)的

小结

通过这一章的学习,我们了解了自上而下分析法的基本思想,学习了递归下降分析法的基本方法:如消除左递归、消除回溯、构造递归下降子程序。学习了预测分析方法,掌握了预测分析表的构造方法、LL(1)文法的定义。在下一章中,我们将讨论自下而上语法分析方法。

典型题解

例题1按照乔姆斯基(Chomsky)对文法的分类,指出下述文法的所属类型,并给出所描述的语言。
(a) S → Be
B → eC | Af
A → Ae | e
C → Cf
D → fDA

(b) A → ε| aB
B → Ab | a

(c) S → abcA
S → Aabc
A →ε
Aa → Sa
cA → cS

解题思路:
这类问题主要是考察考生对Chomsky形式语言体系的掌握情况,因此,首先应当理解各种类型文法的特征,注意各种类型文法对产生式形式上的限制。本题的另一个考察的知识点是文法的一些基本概念,如推导、句子、语言等。

解答:
(a)    该文法是上下文无关文法(2型),从D → fDA可以看出该文法不是正规文法。它所描述的语言是L={eife | i≥1}∪{ efie | i≥1}
(b)    该文法是上下文无关文法(2型),从两个产生式可以看出,该文法既不是右线性文法,也不是左线性文法。它所描述的语言是L={anbm | n,m≥0且 (n=m 或 n-m=2)}
(c)    该文法是上下文有关文法(1型),从产生式Aa → Sa可以看出,该文法不是上下文无关文法。它所描述的语言是L={(abc)m | n>0}

注意:(a)文法描述的语言是文法推导的句子的全体,该文法推导的句子的过程中不会引用非终结符D。

例题2给出文法G(S)
S → aSb | P
P → bPc | bQc
Q → Qa | a
1) 它是Chomsky哪一型文法?
2) 它生成的语言是什么?

解题思路:
注意到S推出的串的形式是aiPbi(i≥0),而P推出的串的形式是bjQcj(j≥1), Q推出的串的形式是ak(k≥1)。

解答:
1) 该文法是Chomsky2型文法,即上下文无关文法。
2) 它生成的语言是L={aibjakcjbi | i≥0,j≥1,k≥1}

例题3写一个文法G,使得L(G)={ anbman|n,m³0}。

解题思路:
写出语言的文法是检查考生形式抽象的能力。解答这类问题,首先应当仔细研究语言的结构特点,通常这些语言具有形式上的对称性和字符数目上的相关性等特点,这些特性可以用文法的递归定义来实现。

解答:所求文法是:
G(S):
S ® aSa | B
B ® bB| e

例题4 将文法G(S)改写成等价的正规文法。
G(S):
S → dAB
A → aA | a
B → Bb |ε

解题思路:
对于这类题目,首先求出文法描述的正规语言,写出相应的正规式,在此基础上构造相应的DFA,最后把DFA的状态转换成文法的非终结符,就能够写出等价的正规文法了。

解答:该文法描述的语言是daibj(i>0,j≥0),对应的DFA是:

相应的正规文法是:
G(S):
S → dA
A → aB
B → aB | bC |ε
C → bC |ε

注意: 把DFA的转换成正规文法时,终态对应的非终结符应当有ε候选式(如B,C)。

例题5 按指定类型,给出语言的文法
(a) L={aibj|j>i≥1} 的上下文无关文法
(b) 字母表Σ={a,b}上的同时只有奇数个a和奇数个b的所有串的集合的正规文法
(c) 有相同个数的a和b组成的句子的无二义文法

解题思路:
给出语言的文法可以从多个角度进行思考,如分解语言的结构,利用有限自动机,找出语言的递归或递推特性等。
语言(a) L={aibj|j>i≥1},实际上可以看成aibibj-i的形式,而aibi可以由A → aAb | ab规则来描述,bj-i可以由B → bB | b规则来描述。

语言(b)的描述可以借助有限自动机的思想,非终结符A、B、C、S分别表示下面四种状态:

识别了偶数个a和偶数个b的状态
识别了奇数个a和偶数个b的状态
识别了偶数个a和奇数个b的状态
识别了奇数个a和奇数个b的状态
文法规则只需要描述这些非终结符之间的推导关系,即状态之间的转换关系。
语言(c)的描述可以采用递归的思想,写出相应的无二义文法。

解答:
(a) 所求的文法是G(S):
S → AB
A → aAb | ab
B → bB | b
(b) 所求的文法是G(S):
S → aC | bB
A → bC | aB |ε
B → aA | bS
C → aS | bA
(c) 所求的文法是G(S):
S→aBS|bAS|aB|bA
B→aBB|b
A→bAA|a

例题6 写一个上下文无关文法,使其语言是能被5整除且不以0开头的无符号整数的集合。(如{5,10,15,…})

解题思路:
能被5整除的数从形式上看,是以0,5结尾的数字串。题目要求的不以0开头,注意0不是该语言的句子。

解答:所求文法为:
G(S):
S → M F | 5
F → 5 | 0
N → 1 |2 | 3 | 4 | 5 | 6 | 7 | 8| 9
D → N | 0
M → M D | N
其中, S代表能被5整除且不以0开头的无符号整数;
F代表可以出现在个位上的数字;
D代表所有数字;
N代表所有非零数字;
M代表所有不以零开头的数字串;

例题7
写一个文法使其语言为L(G)={ anbm| 2n>m≥n≥1}
解题思路:
b的个数大于或等于a的个数,但又比a的个数的2倍要少。这是一种类型的问题,一般在两个或多个字符的数量上做文章,对于这类问题,有一种固定的问题求解方法。以本题为例,b的个数在a的个数的一倍和两倍之间,那么就存在两个边界:一倍和两倍,我们就分别为它们写出两个产生式:
1.S —> aSb
2.S —> aSbb
此时可以看到,用产生式1扩展时所产生的a和b的个数相等,而用产生式2扩展时所产生的b的个数是a的个数的两倍,如果同时使用两个产生式进行扩展,那b的个数将在a的个数的一倍和两倍之间,满足了这个前提之后,再用另一个产生式(S —> ab)来保证边界条件(2n>m、m≥n和n≥1)就可以了。如果边界条件是2n≥m>n≥1,则可以用产生式S —> abb来满足。

解答: S —> aSb | aSbb | ab

例题8试简述二义性概念。
解答:如果一个文法存在某个句子对应两颗不同的语法树,则说这个文法是二义的。如果一个语言是二义的,当且仅当它不存在无二义性的文法。文法的二义性与语言的二义性是两个不同的概念。例如,对于某种语言L来说,可能存在两个文法G和G’,有L(G)=L(G’)=L,但文法G是二义的,而G’是无二义的,这时,语言L并不是二义的。

例题9文法G的产生式集为{S → S+S |S*S | i | (S)},对于输入串i+i*i:
1) 给出一个推导;
2) 画出一棵语法树;
3) 文法G是否是二义性的,请证明你的结论?

解题思路:
这类题目,重点考察推导、语法树和二义性等基本概念。要证明一个文法是二义性的,只要找出该文法的一个句子,说明该句子有两种不同的最左推导或最右推导,或者有两棵不同的语法树。
解答:
1) SÞS+SÞi+SÞ i+S*S Þ i+i*S Þ i+i*i
2) i+i*i 的语法树
3) 文法G是二义性的。考虑句子i+i*i,除了语法树外,还有另一棵语法树,所以文法G是二义性的。

例题10已知文法G=({S},{a},{S → SaS, S →ε},S)
1) 该文法是否是二义性文法,为什么?
2) 该文法是否是OPG(算符优先文法)文法,为什么?
3) 该文法是否是LL(1)文法,为什么?
4) 该文法是否是SLR(1)文法,为什么?

解题思路:
本题和核心是判断文法的二义性,同时必须掌握OPG(算符优先文法)文法、 LL(1)文法和SLR(1)文法和二义性文法的关系,并根据它们之间的关系判断文法的性质。
解答:考虑该文法的句子aa,我们有下面两个不同的最左推导:
SÞSaSÞSaSaSÞaSaSÞaaSÞaa
SÞSaSÞaSÞaSaSÞaaSÞaa
所以该文法是二义性的。因为OPG(算符优先文法)文法, LL(1)文法和SLR(1)文法一定不是二义性文法,所以,该文法不是OPG(算符优先文法)文法、 LL(1)文法和SLR(1)文法。

例题11 生成L={ albmclanbn| l³0,m³1,n³2}这种语言的文法是什么?它是Chomsky哪一型文法?

解答:所求文法是G(S)
S → AC
A → aAc | B
B → bB | b
C → aCb | ab
它是Chomsky 2型文法,即上下文无关文法。

例题12文法G(S):
S → aSPQ | abQ
QP → PQ
bP → bb
bQ → bc
cQ → cc
它是Chomsky哪一型文法?它生成的语言是什么?

解答:从规则形式上可以看出,文法G是Chomsky 1型文法,即上下文有关文法。它生成的语言是L={ anbncn | n³1}

例题13给出下列术语的严格定义:
1) 上下文无关文法 2) LL(1)文法

解答:
上下文无关文法的定义:
形式上说,一个上下文无关文法G是一个四元式( VT VN ,S,P),其中
VT 是一个非空有限集,它的每个元素称为终结符号;
VN 是一个非空有限集,它的每个元素称为非终结符号, VT VN =f;
S是一个非终结符号,称为开始符号;
P是一个产生式集合(有限), 每个产生式的形式是P®a, 其中,PÎ VN , aÎ( VTVN )*。开始符号S至少必须在某个产生式的左部出现一次。

LL(1)文法的定义:
如果一个文法G满足下面的条件,则称该文法G为LL(1)文法:
1. 文法不含左递归,
2. 对于文法中每一个非终结符A的各个产生式的候选首符集两两不相交。即,若A→a1|a2|…|an
则 FIRST(ai)∩FIRST(aj)=f (i¹j)
3. 对文法中的每个非终结符A,若它存在某个候选首符集包含e,则
FIRST(A)∩FOLLOW(A)=f

例题14给出文法G1(S)
S → aSb | P
P → bPc | bQc
Q → Qa | a
消除文法的左递归、提取左公共因子后是不是LL(1)文法?请证实。

本章练习

  1. 考虑下面文法G1:
    S→a | Ù | (T)
    T→T, S | S
    (1) 消去G1的左递归。然后,对每个非终结符,写出不带回溯的递归子程序。
    (2) 经改写后的文法是否是LL(1)的?给出它的预测分析表。

  2. 对下面的文法G
    E→TE¢
    E¢→+E | e
    T→FT¢
    T¢→T | e
    F→PF¢
    F¢→*F¢ | e
    P→ (E) | a | b | Ù
    (1) 计算这个文法的每个非终结符的FIRST和FOLLOW。
    (2) 证明这个文法是LL(1)的。
    (3) 构造它的预测分析表。
    (4) 构造它的递归下降分析程序。

  3. 下面文法中,哪些是LL(1)的,说明理由。

    (1) S→Abc
    A→a | e
    B→b | e

    (2) S→Ab
    A→a | B | e

    (3) S→ABBA
    A→a | e
    B→b | e

    (4) S→aSe | B
    B→bBe | C
    C→cCe | d

  4. 对下面文法
    Expr→-Expr
    Expr→ (Expr)|Var ExprTail
    ExprTail→-Expr | e
    Var→id VarTail
    VarTail→ (Expr) | e
    (1) 构造LL(1)分析表。
    (2) 给出对句子id–id((id))的分析过程。

  5. 把下面文法改写为LL(1)的:
    Declist→Declist; Decl | Decl
    Decl→IdList:Type
    IdList→Idlist, id | id
    Type→ScalarType | array (ScalarTypeList) of Type
    ScalarType→id | Bound..Bound
    Bound→Sign IntLiteral | id
    Sign→+ | - | e
    ScalarTypeList→ScalarTypeList, ScalarType | ScalarType

参考文献

[1]Alfred V.Aho, Ravi Sethi, Jeffrey D. Ullman. Compilers: Principles, Techniques, and Tools. Acldison-Wesley Publishing Company, 1986.
[2]A.V.Aho, J.D. Ullman. Principles of Compiler Descign, Addison=Wesley, 1977.
[3]Arthur B.Pyster. Compiler Design and Constuction. Van Nostrand Reihold Company, 1980.
[4]陈火旺,钱家骅,孙永强. 程序设计语言编译原理. 北京:国防工业出版社,1984.
[5]王兵山,吴兵,形式语言. 国防科技大学出版社,1988.
[6]霍普克罗夫特,厄尔曼. 形式语言及其与自动机的关系. 科学出版社,1979.

你可能感兴趣的:(【Compilers,Principles】)