在进行语法分析的过程中,我们需要根据文法构建语法分析树。如果分析树构建成功则说明语法正确,反之语法错误。因此,如何构建一棵语法分析树非常重要
构建语法分析树有两种方式,一种是自上而下的分析方法(由根部开始构建语法分析树),另一种是自下而上的分析方法。本文采用的是自上而下的分析方法
自上而下的分析方法存在一个问题:文法中存在左递归怎么办?
什么是左递归?假设存在一个文法E → E+E
,可能会存在以下的推导过程
$$ E→E+E→E+E+E→E+E+E+E→... $$
没完没了,陷入左递归状态,因此我们需要消除左递归
左递归的类型有以下两种:
直接左递归
- 例如 $A→A\alpha|\beta$
间接左递归
- 例如 $A→^+A\alpha$
消除直接左递归
直接左递归较为直观,以上文提到的$A→A\alpha|\beta$为例,我们对他进行推导
$$ A→A\alpha→A\alpha\alpha→A\alpha\alpha\alpha→...→\beta \alpha...\alpha $$
可以看出,此文法生成的串一定以$\beta$开头,后面跟随若干个$\alpha$,且$\alpha$的个数可以为0
因此我们可以定义一个新的非终结符$A'$,且新的文法满足以下条件
- 令$\alpha$可以重复,且不产生左递归(即使用右递归)
- 让A的产生式以$\beta$开头(消除原有的左递归)
因此新的文法如下
$$ \begin{align*} A&→\beta A\\ A'&→\alpha A'|\epsilon \end{align*} $$
事实上,消除左递归的过程就是把左递归转换成了右递归
再举个例子,消除下面文法的左递归
$$ \begin{align*} E&→E+T|T\\ T&→T*F|F\\ F&→(E)|id \end{align*} $$
仔细观察上述文法,发现只有产生式1和产生式2有左递归。对于产生式1,可以将+T
看成一个整体$\alpha$,T
看成是$\beta$,于是产生式1就可以看成是
$$ E→E\alpha|\beta $$
根据第一个例子中消除左递归的方式,产生式1消除左递归后变为
$$ \begin{align*} E&→\beta E'\\ E'&→\alpha E'|\epsilon \end{align*} $$
再将$\alpha,\beta$换回原来的符号
$$ \begin{align*} E&→TE'\\ E'&→+TE'|\epsilon \end{align*} $$
同理,对于产生式2,将*F
看成一个整体$\alpha$,F
看成是$\beta$,于是产生式2消除左递归后变为
$$ \begin{align*} T&→FT'\\ T'&→*FT'|\epsilon \end{align*} $$
消除直接左递归的一般形式
如果某条产生式具有直接左递归,例如
$$ A→A\alpha_1|A\alpha_2|...|A\alpha_n|\beta_1|\beta_2|...|\beta_m $$
其中,$\alpha_i\neq \epsilon$且$\beta$不以$A$开头
我们将其转换成不含左递归的产生式
$$ \begin{align*} A&→\beta_1A'|\beta_2A'|...|\beta_mA'\\ A'&→\alpha_1A'|\alpha_2A'|...|\alpha_nA'|\epsilon \end{align*} $$
消除左递归是要付出代价的,引进了一些非终结符和$\epsilon$产生式
消除间接左递归
例如以下文法
$$ \begin{align*} S&→Aa|b\\ A&→Ac|Sd|\epsilon \end{align*} $$
随便推导一下
$$ S→Aa→Sda $$
可以发现推导了两步就出现了左递归。消除间接左递归的方法也很简单,对于上面的例子
- 首先将S的定义带入A的产生式,得
$$ A→Ac|Aad|bd|\epsilon $$
- 消除A产生式的直接左递归,得
$$ \begin{align*} A→bdA'|A'\\ A'→cA'|adA'|\epsilon \end{align*} $$
消除间接左递归的方法总结下来,分以下3步
- 将递归链上的非终结符逐级带入产生直接左递归
- 消除直接左递归
- 删除多余文法
消除左递归算法
- 输入:不含循环推倒(即形如$A→^+A$的推导)和$\epsilon$产生式的文法G
- 输出:等价的无左递归文法
- 方法:
“A→Aα→AAα→AAAα→...→βα...α”这里应该是“A→Aα→Aαα→Aααα→...→βα...α”吧
是的,已修正,谢谢@(大拇指)