10.2.1.3. 将 CFG 转换为 NPDA
注意:这不是我们之前使用的构造方法。这种方法适用于任何 CFG,即使是那些不属于 GNF 的 CFG。
想法:要用 CFG 推导一个字符串,从起始符号开始,反复应用产生式规则,直到推导出该字符串为止。为了用 NPDA 模拟这个过程,开始时把起始符号压到栈上。每当要应用产生式规则 \(A \rightarrow w\) 时,变量 \(A\) 应该在栈顶。弹出(或替换) \(A\) ,并把该规则的右端 \(w\) 压到栈上。每当栈顶是终结符时,如果它与输入字符串的下一个符号匹配,则把它从栈中弹出。如果不匹配,那么这个字符串不属于该文法的语言。如果从起始符号 \(S\) 开始,可以应用替换规则,匹配输入字符串中的所有终结符并清空栈,那么这个字符串就属于该语言。
构造出的 NPDA:
三个状态: \(s, q, f\)
照常,在状态 \(s\) 开始
把 \(S\) 压入栈中,进入 \(q\)
所有重写规则都在状态 \(q\) 中:
如果重写规则的左端在栈顶,则用重写规则的右端替换它,并保持在状态 \(q\)
\(q\) 中识别终结符的附加规则:
读取输入符号,弹出输入符号,保持在状态 \(q\)
从栈中弹出 \(z\) ,进入 \(f\) ,接受
10.2.1.5. 通用的分析例程
想法:要用适当的右端替换栈顶的变量,使用向前看符号和左端在 LL 分析表中查找右端。(LL[,] 就是分析表。):
push(S)
read(symbol) 获取向前看符号
while 栈非空 do
case 栈顶元素为
终结符:
if 栈顶元素 == symbol
then { pop(); read(symbol) } 弹出终结符并获取下一个向前看符号
else
error
非终结符:
if LL[栈顶元素,symbol] <> error
then { pop(), 弹出左部
push(LL[栈顶元素,symbol]) } 压入右部
else
error
end case
end while
if symbol <> $, then error
备注
对前面的例子,尝试以下追踪:
分析字符串: aabbb
分析字符串: b
我们将使用 FIRST 和 FOLLOW 这两个函数来帮助计算分析表。
10.2.1.6. 构造 LL 分析表 LL[rows,cols]
对每条规则 \(A \rightarrow w\)
对 FIRST(w) 中的每个 a
把 w 加入 LL[A,a]
如果 \(\lambda\) 在 FIRST(w) 中
对 FOLLOW(A) 中的每个 \(b\) ,把 \(w\) 加入 LL[A,b]
其中 \(b \in T \cup \{\$\}\)
每个未定义的表项都是错误。
注释(Comments):
有些 CFL 没有 LL(k) 分析器
有些语言,某些文法有 LL(k) 分析器,而另一些文法没有。