语法分析基础

编译的第一步是词法分析,第二步是语法分析。词法分析产生的结果是标识符,第二步是判断源程序文法是否合法。例如if{...}就是不合法的文法,但是他产生的标识符都是正确的。

工作原理: 通过上下文无关文法产生的文法式,识别输入字符串是否是一个句子。从左至右每次读入一个字符进行规则匹配。

问题及解决

左递归

例如

css
规则为P->Pa
字符串为a
起始非终结符为P
读入a后将栈中的P展开为Pa,然后因为是从左往右匹配因此读入P,展开为Paa,如此递归,得到死循环

消除递归的方法:

  • 消除直接左递归

经过这样的转换之后产生的语言没有改变,但是消除了直接的左递归

再将它一般化得:

AAα1|Aα2||Aαm|β1|β2||βnAβ1A|β2A||βnAAα1A|α2A||αmA|ε

通过上面的方法消除了直接左递归,但是还可能经过若干次转换之后又形成了左递归,例如

将A代入就形成了左递归

消除左递归的一般算法

条件: 不含回路(P=>P),不含以ε为右部的产生式

  1. 将文法中所有规则按任意一种顺序排列成P1,P2,P3按此顺序执行
  2. sqf
    for i=1 to n do
    for k=1 to i-1 do //i-1而不是n
    Pi中包含Pk的规则全部代入
    消除Pi中的左递归
  3. 化简前面得到的文法,去除那些由起始符号永远无法到达的非终结符产生的规则

代入时Q代入的是经过第一次循环产生的R而不是最开始的R。化简时将从S无法到达的规则删除

回溯

如上图,如果此时读入i可以同时进入两个状态,需要读入更多的字符才可以判断具体是哪个状态,这样需要占据大量资源并且容易导致程序错误。

解决方法:

其中δ是这些规则中相同的部分

通过反复提取左因子,我们可以将所有非终结符的所有First集变为两两不相交,但是回引入大量的新字符.

First(α)是所有α推导的可能的开头终结符或ε

如果A->α1|α2|, 且First(alphai)First(αj)=,则我们可以唯一的将字符推给某个模式,这样就无需回溯。

LL(1)

LL(1)标识从左向右推导,每次只看一个字符。

要构造不带回溯的自顶向下分析的文法(LL(1)文法)条件:

  • 文法不含左递归(验证方法为使用消除左递归算法运行一遍,如果和原来相比没有变化则不含左递归)
  • 每一个非终结符的各个产生式的First集两两不想交,即A->α1|α2|, First(alphai)First(αj)=
  • 对于每个非终结符A,若它的First包含ε,则First(A) Follow(A) =

使用LL(1)进行匹配的模式为:

  1. 如果要使用非终结符A进行匹配,输入符号为a,A的所有产生式为α1|α2|
  2. 若a First(αi),则将αi压入栈中进行下一步分析
  3. 如果没有任何一个First匹配成功,但是存在ε,则使用Follow(A)进行判断,如果Follow(A)中有a,则将A弹出栈(将A弹出然后再将ε压入)
  4. 如果都没匹配上,这说明有语法错误

构造First集算法

应用如下规则,直到First不再增大为止:

  • 如果XVT(终结符集合),则First(X) = {X}
  • 如果XVN(非终结符集合),且有产生式X->a…,则将a假如First(X)中,如果X->ε也是一个产生式,则将ε加入First(X)中
  • 如果X->Y1Y2YN,首先将First(Y1) - {ε}添加进First(x),如果First(Y1)中包含ε,则将First(Y2) - {ε)添加进First(X)中。依次类推,如果推导最后First(YN)也包含ε,则将ε添加进First(X)

构造Follow集算法

Follow是非终结符后随的终结符号的集合

构造规则:

  • 对于开始符号S,将#(结束符)添加到Follow(S)中
  • 若A->aBβ是一个产生式(β可以是终结符也可以是非终结符),将First(β) - {ε)添加到Follow(B)中
  • 若A->αB是一个产生式,或A->αBβεFirst(β),则将Follow(A)添加到Follow(B)中

对于第二条规则很好理解,如果B后面是一个终结符,那么符合定义。如果是非终结符,那么First(A)就是第一个符号。而对于第二个则是由于它是这个式子中的最后一个字符,因此B的Follow集就是A的Follow集

例如E的Follow集由于第一条,因此包含Follow(E)

分析表的构造方法

分析表就是将根据First集和Follow集构造一张表判断读入当前字符后下一个状态是什么。

构造方法:

  • 对于每个A->α(如A->Ba, A->c可以,A->Bc | a不能,它属于两条规则)执行如下步骤
    • 对于每个a First(α),将A->α填入M[A, a]位置
      • εFirst(α),则对于任何b Follow(A),将A>ε填入M[A, b]中
      • 对于所有无定义的M[A, a]标上出错标志

例: