前文概述:
前阵子弄完词法分析后,这一周开始语法分析的课程。
语法分析:在计算机科学和语言学中,语法分析(英语:syntactic analysis,也叫 parsing)是根据某种给定的形式文法对由单词序列(如英语单词序列)构成的输入文本进行分析并确定其语法结构的一种过程。
(摘自维基百科)
其实学习语法分析,并不是一上来就看其算法应该怎么做,而是需要一些概念进行铺垫。
语法分析方式:
语法分析方式分为两种,一种是自顶向下分析,一种是自底向上分析。这篇博文要讲的是自顶向下分析。
自顶向下分析法:
概念1:推导方式
在推导过程中,有最左推导和最右推导。
最左推导:每次选择最左非终结符进行替换。
最右推导:每次选择最右非终结符进行替换。
而自顶向下选择的是最左推导。
概念2:回溯
回溯现象:同一个非终结符的多个候选式存在共同前缀,将导致回溯现象。
比如:A —> aBd | aCd。那么这个将会产生回溯,因为假如选了aBd这个之后,程序发现不对,那么就得重新回来,选择aCd这个,这样子是不好的,效率很低。
既然这样的话,如何消除回溯现象呢?
A —> aBd | aCd
还是以这个为例,我们可以改造成下面这个:
A —> aD
D —> Bd | Cd
也就是通过改写产生式来推迟决定,等读入了足够多的输入,获得足够信息后再做出正确的决定。
概念3:LL(k)文法:
预测分析:
预测分析是递归下降分析技术的一个特例,通过在输入中向前看固定个数(通常为1)符号来选择正确的产生式。
LL(k)文法:
可以对某些文法构造出向前看k个输入符号的预测分析。
文法转换:
并不是所有的文法都适用于自顶向下的分析,我们有时候需要对文法进行改造,使之适合。
概念4:左递归
如果一个文法中有一个非终结符A使得对某个字符串a存在一个推导:A —> *Aa,那么这个文法就是左递归的。
如:
A —> Aa
E —> E + T | E - T
左递归分为两种:
直接左递归:
如:
A —> Aa | b
E —> E + T | E - T
A和E都可以直接推出A和E。
那么如何消除左递归呢?
如A —> Aa | b
则其r = ba*。
令 A —> bC, C —> aC | e。(这里e代表空字符串)。
间接左递归:
如:
S —> Aa | b
A —> Ac | Sd | e (这里e代表空字符串,下同)
上面那个可以推出:
S —> Aa —> Sda,即经过两步及两步以上推出了左递归。
那么如何消除呢?
还是以上面为例:
将S的定义代入A的产生式得:
A —> Ac | Aad | bd | e
消除A的直接左递归:
A —> bd | C
C —> cC | adC | e
LL(1)文法:
在了解LL(1)文法之前,我们需要来了解一下以下几个概念:
FIRST(X)集:
可以从X推导出的所有串首终结符构成的集合,如果式空串e,也要加入。
如:A —> bd | C C —> cC | adC | e
那么FIRST(A) = {b, c, a, e}。
这个应该不是很难理解?那就过了。
FOLLOW(A)集:
可能在某个句型中紧跟在A后面的终结符a的集合。如果A是某个句型的最右符号,则将结束符“#"添加到FOLLOW(A)中。
如何求解:
①设S为文法中开始符号,把{#}加入FOLLOW(S)中(这里“#” 为句子括号)。
②若A→αBβ是一个产生式,则把FIRST(β)的非空元素加入FOLLOW(B)中。如果β能够推导出ε则把FOLLOW(A)也加入FOLLOW(B)中。
③反复使用②直到每个非终结符的FOLLOW集不再增大为止。
例子可以看一下这篇文章
SELECT(A —> b):产生式的可选集
产生式 A —> b的可选集是指可以选用该产生式进行推导时对应的输入符号的集合,记为SELECT(A —> b)。
例如:
SELECT(A —> aB) = {a}.
SELECT(A —> e) = FOLLOW(A).
LL(1) 文法:
当同个非终结符推出的SELECT集不相交时,则是LL(1)文法。
如SELECT(A —> aB) = {a}. SELECT(A —> e) = FOLLOW(A).演示a和FOLLOW不相交的话,那么A —> aB,A —> e构成的文法就是LL(1)文法。
LL(1) 文法分析方法:
递归的预测分析法:
在递归下降分析中,编写没有给非终结符对应的过程时,根据预测分析表进行产生式的选择。
非递归的预测分析法:
不需要为每一个非终结符编写递归下降过程,而是根据预测分析表构造一个自动机,也叫驱动的预测分析。
一般写语法分析器代码就是依照上面其中一中方法来。
总结:
上面讲的其实是一个大概,其中的细节并没有深挖,推荐这个教程,老师讲的很不错。看了几遍应该就可以了。