自上而下的语法分析方法(LL分析法)
概述:
- 语法分析的地位:编译程序的核心部分
- 任务:词法分析出来的单词序列是否是给定文法的句子
- 理论:上下文无关文法和下推自动机
- 方式:自上而下的语法分析(推导)和自下而上的语法分析(规约)
下推自动机:
-
下推自动机模型:
- pda和fa的模型相比,多了一个下推栈
- 决定pda的动作因素:当前状态,读头指向的符号,下推栈栈顶符号
- 当输入串读完下推栈为空,到达终态
-
下推自动机定义:
当处于q状态,不需要读入任何字符,就可以将栈顶符号A转化为w(欧米伽),就是推导的过程,
当处于q状态,读入一个字符a,栈顶符号为a时,这个过程就是匹配。-
例子:
通过文法,写出相应的推导步骤
当状态为q,读入字符为a,栈顶字符为a,就将两个字符相消,
当状态为q,栈顶字符为S,读入任何字符都不能和其匹配,所以读入一个空串,使得S可以变成c或者aSb
当输入带上存放的是aacbb#,栈中存放S:
读头指向a,栈顶为S,进行推导
读头指向a,栈顶为a,进行匹配
读头指向a,栈顶为S,进行推导
……
问题出现:任何进行备选式的选择,可以通过回溯的方法,先对一个候选式进行推导,当出现不能推导和匹配就进行回溯操作,当进行一次推导,将当前栈情况,输入带情况和选择式记录下来,当出现了匹配错误,通过对上一级的回溯来达到匹配。
-
-
一般方法:
- 基本构成:
- 基本构成:
-
算法:
回溯的缺点:- 文法存在左递归,语法分析会无限循环
- 当一个文法P->Pa;当P出现在栈顶,那么就会推导到Pa,P进行了递归
- 时间和空间的浪费
- 无法准确的指出语法错误
- 推导具有盲目性
- 文法存在左递归,语法分析会无限循环
-
不带回溯的自上而下分析算法:
- 消除左递归
- 什么是左递归:P-一步或多步推导->Pa
- 消除左递归:
-
消除直接左递归:
通过推导可知,由直接左递归的式子的最左一个字符一定是终结符β,可以使用一个右递归式子替换。
E产生直接左递归,化为E产生TE‘ E’产生+TE‘或者空串
T产生左递归,化为T产生FT’ T’产生*FT‘或者空串 -
消除间接左递归
-
此算法需要不包含形如P->P的产生式,也不包含以空串为右部的产生式的文法
-
出现直接左递归消除
-
若产生式右部最左符号是非终结符,且其序号大于左部的非终结符,不处理
-
小于左部的非终结符,将这个非终结符的右部串取代,然后消除新的直接左递归
-
最后两句的解释:当出现了一个产生式的右部的非终结符已经被消除过了左递归将将其替换,否则不替换。
-
- 消除回溯:
- 根据读头下面的符号来选择候选式,设立候选式的首符集,当栈顶为非终结符,将这个非终结符的各个候选式的首符集和读头下面的符号比较,选出适合该读头下符号的首符集对应的候选式。因为进行了提取各个候选式的左公因子,可以避免出现一个多个候选式存在同一个首符集。
- 需要求候选式的终结首符集合
- 首符为终结符
- 首符为非终结符
- 非终结符推出终结符
- 推出空串
- 空串
- 替换情况:
- 当出现第四种情况,需要进行提取公因子
通过这样的方式可以将所有非终结符变为两两不相交
- 消除左递归
预测分析程序与LL(1)文法:
- 带预测分析的PDA:
- 使用一个预测分析表,它是预测分析程序分析的主要依据
- 使用一个二维数组,M[A,a]
- 行标A是非终结符(栈顶可能出现),列标a是终结符(读头可能出现)
- 表中存在的是对应的候选式或出错标志
- 当栈顶出现A,读头出现a时,选取的候选式
- 预测分析程序描述:
X为栈顶符号,读入符号为a- X=# and a=#:识别成功,退出
- X=a且X!=#:匹配
- X!=a:错误
- X∈VN,查找预测分析表M,出栈X,入栈对应表达式,输出带记下产生式的标志
- 求终结首符集:
求文法中每个文法符号的首符集:- 定义:
- 代码分析:
- 刚开始First(X)为空,repeat相当于一个while循环
- 首先,递归求Yj的去空终结首符集,
- 例如,求Yj的去空终结首符集,并入Fisrt(x)中,然后判断这个Yj的终结首符集中是否存在空串,存在空串,则Yj可以为空,则有可能Yj不存在,再进行下一个查询,
- 直到Yj终结首符集为非空或者j=k
- 当j=k时,则(Y1,Y2,Y3……)都可以为空串,则X可能为空串,所以加入空串作为首符集
- 例子:
first(E)=first(T)
first(E’)={+,空串}
first(T)=first(F)
first(T’)={*,空串}
first(F)={i,(}
first(E)=first(T)=first(F)={i,(}
- 定义:
- 求随符集
- 当出现,读头下面为字符a,在栈顶出现FE……,F可以推出bc | 空串,E可以推出a,但是怎么才能然语法分析程序知道,F使用空串后,可以用E匹配到a呢,因此出现了follow(F)集
- 例子:
- 当出现,读头下面为字符a,在栈顶出现FE……,F可以推出bc | 空串,E可以推出a,但是怎么才能然语法分析程序知道,F使用空串后,可以用E匹配到a呢,因此出现了follow(F)集
- 构造预测分析表
-
基本思想:
解释:当读入符号为栈顶非终结符的一个产生式的first集,就将这个产生式写入对应的框中,如果这个产生式可以产生空串,则需要判断这个读头字符是否属于随符集,当属于,需要将栈顶符号产生空串写入对应框中。 -
构造算法:
-
例子
分析:- 求出各个产生式右侧的first集
- 画出部分的预测分析表
- 查看E的first集,得知,当栈顶出现E,读头出现 ( 时,可以由E替换得到,需要替换为TE‘
- 查看E的first集,得知,当栈顶出现E,读头出现 i 时,可以由E替换得到,需要替换为TE‘
- 查看E’的first集,得知,当栈顶出现E,读头出现 + 时,可以由E’替换得到,需要替换为+TE‘
- 查看E’的first集,得知,当栈顶出现E‘时,可以将E’使用空串替换,当E‘被空串替换时,就需要使用E’的随符集,随符集中跟随E’的是 )和 # 因此将空串填入)和 # 的位置
-
全过程:
- 文法化简,消除P->P的产生式
- 消除左递归
- 提取最左公因子,FIRST集两两不相交
- FIRST集出现空串,求FOLLOW集
- 填写预测分析表
-
- LL(1)文法:
- 定义:若文法G的预测分析表M中不含有多重定义项,那么G就是LL(1)文法
- 二维数组的元素,每空只能填写一个
- LL(1)文法是无二义性的,二义文法一定不是LL(1)文法
- LL:从左到右扫描输入串,采用最左推导的方式
- 1:表示需要向前看一个输入符号
- 证明过程:
- 解读:
当A推出 a | p,则,A需要在自己这一行中填写这两个产生式,当这两个产生式的FIRST集不同,即不相交,则一个空内不会出现两个产生式的情况;同样,当 p 可以推出空串,则A可以为空,需要使用FOLLOW(A)来比较,这时,当a的FIRST集和A的FOLLOW集没有相交部分,那么他们就不会出现一个元素有两种推出结果的情况