基本概念
语法分析的地位:语法分析是编译程序的核心功能之一。
语法分析的作用:语法分析的作用是识别由词法分析给出的单词符号串是否是给定文法的正确句子。
语法分析的方法:可以分为自顶向下分析方法和自底向上分析方法两大类。
- 自顶向下分析方法:从文法的开始符号出发推导出与输入的单词符号完全匹配的句子。
确定分析和不确定分析:
- 确定分析:每一步推导所使用的产生式都是唯一确定的。
- 不确定分析:每一步推导可能存在多个产生式。
开始符号集FIRST:
- 针对对象:开始符号集是针对一个指定的字符串而言的。
- 集合元素:指定字符串的所有可能开头的终结符构成的集合。
- 特殊情况:如果一个字符串可以推导出空串ε,则ε也是开始符号集中的元素。
后跟符号集FOLLOW:
- 针对对象:后跟符号集是针对一个特定的非终结符而言的。
- 集合元素:指定非终结符后面可以紧接着的终结符构成的集合。
- 特殊情况:如果该非终结符可以作为结尾,那么结束符#也是后跟符号集中的元素。
选择符号集SELECT:
- 针对对象:选择符号集是针对一个特定的产生式而言的。
- 集合元素:使用指定产生式可以下一步选择进行替换的终结符构成的集合。
LL(1)文法:
- LL(1)文法的特点:能够使用确定的自顶向下分析技术。
- 文法含义:第一个L表示从左到右扫描输入串;第二个L表示分析过程使用最左推导;1表示只需要向右看一个符号就知道选择哪一个产生式进行下一步推导。
- 判定充要条件:
- 首先判定该文法是否是上下文无关文法:要求文法中每一个产生式的左部都是一个非终结符。
- 将文法中的所有产生式按照左部相同的原则分为多个类。
- 对每个类别中的产生式,判断产生式的SELECT集是否存在交集,如果均不存在交集则说明该文法是LL(1)文法。
求解算法
FIRST集合求解
FIRST集合是针对一个字符串而言,可以采用如下的方式进行求解。初始状态下FIRST集合为空。
- 如果该字符串以终结符开头或为空串,那么就将该终结符(空串)放入FIRST集合中。
- 如果该字符串以非终结符开头,那么使用以这个非终结符作为左部的产生式的右部来对该非终结符进行替换,得到若干个新的产生式。
- 如果还有未处理的产生式,则重复上述步骤进行处理,直到所有的产生式都已经处理完成。
下面通过举例的方法来说明如何求解FIRST集合。
对于本题中的第一个产生式S→AB
,我们需要对其右部AB求FIRST集合。
首先,AB字符串的开头是非终结符A,因此需要使用以A作为左部的产生式进行替换。本题中以A为左部的产生式为A→ε
和A→b
,因此进行替换后的结果为S→B
和S→bB
。
对于S→bB
,其以终结符b开头,因此将b放入FIRST集合中。
对于S→B
,其以非终结符B开头,因此使用以B作为左部的产生式进行替换。本题中以B作为左部的产生式为B→ε
和B→aD
,则替换后的产生式为S→ε
和S→aD
。
对于S→ε
,其推导出空串,因此将ε放入FIRST集合中。
对于S→aD
,其以终结符a开头,因此将a放入FIRST集合中。
此时已经没有未经处理的产生式,因此FIRST集合计算结束,为{b,ε,a}
。
FOLLOW集合求解
FOLLOW集合是针对一个非终结符而言,可以采用如下的方式求解:初始状态下FOLLOW集为空。
- 如果该非终结符是开始符号,则将终止符#放入该非终结符的FOLLOW集中。
- 查看所有右部包含该非终结符的产生式:
- 如果这些产生式中,紧邻该非终结符的右边有字符串,那么就把这个字符串的FIRST集合中除了空串之外的所有元素加入到FOLLOW集合中。如果该字符串可以推出空串,那么就将该产生式左部的FOLLOW集合中的所有元素放入当前非终结符的FOLLOW集合中。
- 如果这些产生式中,紧邻该非终结符的右边没有字符串,则将该产生式左部的FOLLOW集合中的所有元素放入当前非终结符的FOLLOW集合中。
下面通过举例的方法来说明如何求FOLLOW集合。初始状态下的FOLLOW集为空。
对于本题中的非终结符S,由于其是开始符号,因此将终止符#放入FOLLOW集中。接着,找出所有产生式中右部包含S的那些,本题中只有D→aS
。这里,由于S右边没有字符串,因此将该产生式左部D的FOLLOW集中的所有元素放入非终结符S的FOLLOW集合中。
下面,就需要递归地计算非终结符D的FOLLOW集合。
对于本题中的非终结符D,右部包含D的产生式有两个,分别是B→aD
和C→AD
。对于这两个产生式,D的右边均没有字符串,因此D的FOLLOW集合即B的FOLLOW集合和C的FOLLOW集合的并集。因此,下面需要继续递归地求解B的FOLLOW集和C的FOLLOW集合。
对于本题中的非终结符B,右部包含B的产生式只有一个,就是S→AB
。这里B右边没有字符串,因此B的FOLLOW集合就是S的FOLLOW集。
对于本题中的非终结符C,右部包含C的产生式只有S→bC
这一个。这里C右边没有字符串,因此C的FOLLOW集合就说S的FOLLOW集合。
所以,D的FOLLOW集合就是S的FOLLOW集合,即FOLLOW(S)=FOLLOW(S)∪{#},求解得FOLLOW(S)=FOLLOW(B)=FOLLOW(C)=FOLLOW(D)={#}
。
由此可见,求解一个非终结符的FOLLOW集合,往往需要借助其他非终结符的FOLLOW集合。
SELECT集合求解
SELECT集合是针对一个产生式而言,可以采用如下的方式求解:
首先,需要划分为两种情况:该产生式右部可以推出空串和不能推出空串的情况。
1.如果一个产生式右部不能推出空串,那么这个产生式的SELECT集合就说是其右部的FIRST集合。
2.如果一个产生式右部可以推出空串,那么这个产生式的SELECT集合就需要求出其左部的FOLLOW集和右部的FIRST集合,两者取并集后从并集中减去空串ε。
所以,求解一个产生式的SELECT集问题就转换为求一个字符串的FISRT集合和一个非终结符的FOLLOW集合的过程,因此只需要如何求解FIRST集合和FOLLOW集合即可。
LL(1)文法判定
下面正式进入求解过程,首先明确一点,判定一个文法是否是LL(1)文法,需要使用到的工具是LL(1)文法判别的充要条件。
第一步,判定该文法是否是上下文无关文法。由于本题中的文法的所有产生式的左部都是一个非终结符,因此该文法是上下文无关文法。
第二步,将产生式按照相同左部的原则进行归类。例如,将S→AB和S→bC归为一类,A→ε和A→b归为一类,其他以此类推。
第三步,对于文法的每一个产生式,计算SELECT集合。这一步是LL(1)文法判别中最复杂的一个步骤。
第四步:判定相同左部的不同产生式的SELECT集合是否存在交集。如果均不存在交集,那么该文法就是LL(1)文法。
下面通过一道例题进行说明:
给定文法如下,请判定其是否是LL(1)文法。
S→AB
S→bC
A→ε
A→b
B→ε
B→aD
C→AD
C→b
D→aS
D→c
详细解析:
-
判定是否为上下文无关文法:经过检验,所有产生式的左部都是一个非终结符,因此是上下文无关文法。
-
将产生式进行归类:将S→AB和S→bC归为一类,A→ε和A→b归为一类,以此类推。
-
计算每个产生式的SELECT集合:
- SELECT(S→AB)= {b,a,#}
- SELECT(S→bC)={b}
- SELECT(A→ε)={a,c,#}
- SELECT(A→b)={b}
- SELECT(B→ε)={#}
- SELECT(B→aD)={a}
- SELECT(C→AD)={a,b,c}
- SELECT(C→b)={b}
- SELECT(D→aS)={a}
- SELECT(D→c)={c}
-
判断同一类产生式的SELECT集是否相交:本题中,同类SELECT集合均不相交,因此该文法是LL(1)文法。