对于非终结符 A ,若其所有产生式为: A -> u1 | u2 | … | un ,则 First(A) 为 First(u1), First(u2), … , First(un) 的并集。
在 LL(1) 解析过程中,假设栈顶为非终结符 X ,且此时读入了一个终结符 a 。如果 a 在某个 ui 的首字符集合 First(ui) 中,且 First(u1), First(u2), … , First(un) 互不相交,那么此时只能挑选 X -> ui 来进行展开了,否则将无法和 a 匹配上。
例如,对上一节中的例子: S -> aS | bS | c , aS, bS, c 的首字符集合分别为 {a}, {b}, {c} ,当栈顶为 S 时,若读入的是 a ,则应选择产生式 S -> aS ,将栈顶的 S 替换成 aS ,才可以和 a 匹配上,若读入的是 b 或 c ,则应选择 S -> bS 或 S -> c 。
当 First(u1), First(u2), … , First(un) 有相交的情况时怎么办?如 S -> aS | a | c 。此时就不能使用 LL(1) 法了,因为当栈顶的 S 遇到 a 时,无法判断出应按 S -> aS 展开,还是按 S -> a 展开。因此,含左递归的语法是不能使用 LL(1) 法来解析的,因为一个含左递归的语法(如:A -> Aa | c)中,必然存在相交的现象。
如果一种语法可以用 LL(1) 法来解析,则称此语法为一种 LL(1) 语法( LL(1) grammar ) ,LL(1) 语法需要的特性将在本章第 4 节的最后讲。
如果 a 不在任何 ui 的首字符集合中呢?此时要分两种情况考虑:
情况 A : 没有任何 First(ui) 含有 ε ,也就是 X 不能用空串代替,此情况表明最终句子不符合语法,因为用任何一个产生式 X -> ui 展开都不可能和 a 匹配上。
那么什么是后继字符集合?
后继字符集合可以看成所有可以合法的站在非终结符 A 的后面的终结符(可能包括结束符 $ 、但不包括 ε )的集合。
因此,当栈顶为 X ,读入的符号为 a , a 不在任何 First(ui) 中,且 X => ε 的时候,那么 a 必须是 X 的后继字符才能保证最终句子是一个符合语法的句子。
若一个符号串 u = X1 X2 … Xn ,则 First(u) 的计算步骤如下:
(1) 置 i = 1 ;
(2) 若 i == n + 1,则将 ε 加入 First(u) ,终止计算;
(3) 若 Xi 是终结符,则将 Xi 加入 First(u) ,终止计算;
一个语法中所有非终结符的 follow set 的计算步骤如下:
(1) 将 $ 加入到 Follow(S) 中, S 为起始符号, $ 为结束符 EOF ;
(2) 对每条形如 A -> u B v 的产生式,将 First(v) - ε 加入到 Follow(B) ;
(3) 对每条形如 A -> u B 的产生式,或 A -> u B v 的产生式(其中 First(v) 含 ε ),将 Follow(A) 加入到 Follow(B) 。
以下为一个计算 first set 和 follow set 的实例,语法为:
- First(C) = {b, ε}
- First(B') = {a, ε}
- First(B) = {c}
- First(S) = {b, a, c}
- Follow(S) = {$}
- Follow(B') = {$}
- Follow(C) = {a, $}
- Follow(A) = {c, b, a, $}