题目描述
1.对文法进行LL(1)判别,若不是LL(1)型文法,则进行等价变换。构造预测分析表。
2.编程实现预测分析表
分析
在拿到我们自己的文法后,首先要对自己的文法进行LL(1)型判别,判别的方法有以下两种。
1.判断是否有左递归
2.判断是否有左公因子
- 左递归和左公因子的是啥东西将会在下面进行解释
如果你的文法中有左递归或左公因子,就可以说明你的文法并不是 LL(1)型 文法,需要进行等价变换。
左递归
递归是什么意思呢? 递归在程序设计语言中就是:有一个函数名字为fn(),在这个函数的内部调用了自己的函数,例如下面的代码。
void fn()
{
fn()
}
这段代码就用到了递归,从这段代码可以看出,如果不加一个限制条件,那么该段代码将会进入一个死循环,没有终止。这肯定会导致系统的崩溃,在文法中也一样。
在文法中,左递归分为直接左递归和间接左递归
直接左递归
我们来看这样一组文法
在这个文法中,箭头的左边和右边都有A,这就说明左边的A可以一直推导出右边的A,如果不加限定条件,则进入死循环。
这里需要注意,只有A在最左边的时候才是左递归,如果A -> aA | b,则不为左递归,因为在预测分析中,每次替换的都是最左边的那个非终结符
如果你的文法中存在类似的情况,则说明需要进行等价变换。方法如下。
等价变换的结果就是得到消除该文法的左递归,但是由消除左递归后的文法推出来的式子必须与消除前一致。
例如上述文法
该文发的右边部分有一个符号’|
',这个符号的是或的意思,也就是说A既可以推出Aα,也可以推出β,如果不使该文法陷入死循环,那么该文法的最后一个推导一定是A -> β,因为只有进行到A -> β,推导才会结束,因为推导时,只能选择Aα和β这两者中的一个,没有被选择的将会被抛弃。
上面的文法,最后一定得到的是下面这个式子
所以说等价变换得到的文法,最后也应该可以推出来βα…α这个式子。
消除直接左递归的方法
因为由A -> Aα | β 这个式子得出的最终推导结果为βα…α,所以消除左递归的文法也能得到βα…α,由βα…α可以看出,在结果中一定有一个β,并且β的位置排在第一个,然后后面紧跟着n个α,从这可以看出有两部分,一部分是β的,一部分是n个α的,然后给大家看一下消除左递归等价变换的例子。应该可以看明白,很简单。
A -> Aα | β //这是原来的文法不用管他,下面的是方法
A -> β A'
A' -> α A' | e //因为输入打不出来伊布西龙,这里用e代替
这俩就是等价变换后的文法
A -> β A'
A' -> α A' | e
其实这里的α和β就是一个标记,他可以表示1个或多个字母,例如下面的这几个例子
消除前
消除后
消除前
消除后
消除直接左递归的一般形式
间接左递归
直接左递归是进行一步推导时就会面对递归的问题,而间接左递归就是进行两步以上推到时会出现左递归的问题。例如
S -> Aa | b
A -> Ac | S d | e
这里就出现了间接左递归,大家看第二个式子中出现了Sd
,则将S展开写会出现以下情况
S -> Aa
-> Sda
第二行出现了递归,所以这种情况称为间接左递归,那么如何消除呢?下面说方法
1.将S的定义带入到A的式子中
2.消除A的左递归
// 1.将S的定义带入到A的式子中,得
A → A c | A a d | b d | ε
// 2.消除A的左递归,得
A → b d A’ | A’
A’ → c A’ | a d A’ | ε
这样就将间接左递归消除完毕了
左公因子
左公因子是啥呢?其实就是咱们数学中学的公因式,比如 ab + ac + ad = a(b+c+d)这里得a就是公因子。那么在文法中是下面的这种情况。
S -> aAd | aBe
看到没,aAd和aBe这俩都有a,所以a是它俩的公因子,但是在文法里我们叫它左公因子。
如何让消除呢?很简单,和数学一样,就是提取公因式,下面是例子。
S -> aAd | aBe
将a提出来
S -> aS'
S' -> Ad | Be
这样我们就将文法改造好了。
预测分析表
预测分析表是代码里比较重要的一项,大家一定要写对,然后再去调程序啊!!!
下面来说一说预测分析表怎么求吧
1.求First集和Follow集
2.求SELECT集
3.根据SELECT集构造分析表
FIRST集和FOLLOW集
点我即可进入first集和follow集的网站
进入网站之后点击它
进入之后,输入你等价变换后的文法,点击提交即可。
注意:输入的时候一定要注意,每个字符直接都要有空格,括号一定要为英文状态下的括号,否则会出错
提交后得到first集和follow集
SELECT集
SELECT集是根据FIRST集和FOLLOW集求出来的,所以大家一定要先得出正确的文法,然后得出正确的FIRST集和FOLLOW集,进而求得SELECT集。下面以如下表所示的FIRST集和FOLLOW集来演示如何求SELECT集。
下表所示FIRST集和FOLLOW集的文法是
G:
M -> HM'
M' -> aHM' | ε
H -> bH' | ( M )
H' -> ( M ) | ε
标识符 | FIRST集 | FOLLOW集 |
---|---|---|
M | b ( | $ ) |
M’ | a ε | $ ) |
H | b ( | a |
H’ | ( ε | a |
我们在求SELECT集之前需要将产生式中的|
去掉,也就是说把|
的左边和右边全都单独的写成一个式子,例如上边的文法去掉|
后变成如下文法
M -> HM'
M' -> aHM'
M' -> ε
H -> bH'
H -> ( M )
H' -> ( M )
H' -> ε
为了解说方便,我直接将本题的SELECT给到大家
(1)M -> HM' SELECT(1) = { b ( }
(2)M' -> aHM' SELECT(2) = { a }
(3)M' -> ε SELECT(3) = { $ ) }
(4)H -> bH' SELECT(4) = { b }
(5)H -> ( M ) SELECT(5) = { ( }
(6)H' -> ( M ) SELECT(6) = { ( }
(7)H' -> ε SELECT(7) = { a }
**注意序号!!!**
三个规则
1.如果->
右边的第一字符是小写字母或者任意的符号,如a,b,左括号(,右括号),加号+,星号*, 那么直接将它放入对应的SELECT集中,例如(2)、(4)、(5)、(6)
2.如果->
右边只有ε,那么将->
左边的字母的FOLLOW集中的所有东西(去查表)放入那一行的SELECT集中,如(3)、(7)。M’的FOLLOW集是$ 和 ),所以将$和)放入SELLECT(3)中。
3.如果->
右边的第一个字符是大写字母,则将改大写字母的FIRST集中的所有东西,放入那一行的SELLECT集中。如(1),H的FIRST集为b和(,所以将b和(,放入SELLECT(1)中
构造预测分析表
这张表是由上边求得的SELECT集转化而来的,至于如何转化的大家应该一目了然,这里就不详细说了,大致意思就是先将SELECT集中的字符写到输入符号下方,然后把->
左边的那些大写字母写到非终结符那一列,然后一行一行填表即可。比如我们先看第一行(非终结符为M的这一行),去上边->
左边字母为M的式子
(1)M -> HM' SELECT(1) = { b ( }
先看SELECT(1),里边有 b 和 ( ,意思就是,当非终结符M遇到输入符号为 b 或( 时,他的产生是为
M -> HM’ 所以将M -> HM’ 填入预测分析表中M那一行的 b 列和 ( 列。其余的做法都一样。很简单!!!
如何改代码呢
打开老师提供的代码,我们需要以下这些地方。
1.先将你的文法中的|
去掉,如何去掉,上面已经说过。
2.找到这个地方
VT[20] = {这里改成你自己的文法中的终结符(终结符就是一些小写字母或者一些符号)}
VN[20] = {这里改成你自己的文法中的非终结符(大写字母)}
把结束符号改成你自己的,原来的是#,其实#或$都可以,不改也无所谓。
4.
将这部分改成你自己的
注意:如果你的文法中有像H’这样的内容,需要将H’用别的字母来替换,改成W或者Z啥的都可以
将这个也改为你自己的,这个地方是根据预测分析表改的[ ][ ]这个指的是第几行第几列
根据自己的表填写即可。
大家如何有任何疑问都可以直接联系我或者在评论区留言