自顶向下分析的算法思想
- 语法分析:给定文法G和句子s,回答s是否能够从G推导出来?
- 基本算法思想:从G的开始符号出发,随意推导出某个句子t,比较t和s。
- 若 t == s,则回答是
- 若 t != s, 则不一定不是,需要回溯后重新进行推导出句子t',判断t'是否等于s。如果不等于则继续重新推导......
- 因为这是从开始符号出发推出句子,因此称为自顶向下分析。
- 对应于分析树自顶向下的构造顺序。
举个例子
首先我们给定文法G如下:
S -> N V N
N -> s
| t
| g
| w
V -> e
| d
需要推导出句子s如下:
g d w
我们首先进行分一次随意的推导,过程如下:
S -> N V N
-> s V N
-> s e N
-> s e t
结果明显与句子s不符。那么需要对其进行重新推导:
S -> N V N
-> g V N
-> g d N
-> g d w
这样,得到的句子与需要推导出的句子s一致,那么返回是,结束推导。
为了提高这部分的推导结果匹配的效率,我们通常会在得到一个终结符的时候就将新得到的终结符与所需要匹配的句子里面相应的字符进行比较,而不是让全部的终结符都生成以后再进行全部的比较。举个例子,就是 N V N 的第一位 N 进行推导得到了终结符后就与句子s的第一位进行匹配,如果不是相应的g就回溯,选择下一个 N 的推导结果。
对于该算法的代码实现如下:
tokens[]; // all tokens
i = 0;
stack = [S] // S 是开始符号
while(stack[] != [])
if(stack[top] is a terminal t)
if(t == tokens[i++])
pop();
else
backtrack();
else if(stack[top] is a nonterminal T)
pop();
push(the next right hand side of T) // 逆序压栈
比如在上一个例子中,
tokens[] = [g, d, w]
stack = [S]
top = S (nonterminal)
stack = [N2, V, N1]
top = N1 (nonterminal)
stack = [N2, V, s]
top = s (terminal, 但是不匹配 tokens[0])
stack = [N2, V, t]
....
stack = [N2, V, g]
...
算法的讨论
- 算法需要用到回溯
- 给分析效率带来问题
- 而就这部分而言(就所有部分),编译器必须高效
- 编译上千万行的内核等程序
- 因此,实际上我们需要线性时间的算法
- 避免回溯
- 引出递归下降分析算法和LL(1)分析算法
重新思考示例
- 用前看符号避免回溯
- 先观察输入串s中当前正在进行匹配的最前面的字符。
原文链接:
- 编译原理 - 网易云课堂