先决条件 — 构建 LL(1) 解析表。
LL(1)解析是编译器设计的语法分析阶段的一种自顶向下的解析方法。 LL(1) 解析所需的组件是输入字符串、堆栈、给定语法的解析表和解析器。在这里,我们讨论一个解析器,它确定是否可以从给定的语法(或解析表)生成给定的字符串。
令给定文法为 G = (V, T, S, P)
其中V-变量符号集、T-终端符号集、S-起始符号、P-产生集。
LL(1) 解析器算法:
Input- 1. stack = S //stack 最初只包含 S。
2.输入字符串=w$
其中 S 是语法的开始符号, w 是字符串, $ 用于字符串。
3. PT 是给定语法的解析表,形式为矩阵或二维数组。
输出 –确定给定的字符串可以由给定的语法(解析表)产生,否则会产生错误。
脚步:
1. while(stack is not empty) {
// initially it is S
2. A = top symbol of stack;
//initially it is first symbol in string, it can be $ also
3. r = next input symbol of given string;
4. if (A∈T or A==$) {
5. if(A==r){
6. pop A from stack;
7. remove r from input;
8. }
9. else
10. ERROR();
11. }
12. else if (A∈V) {
13. if(PT[A,r]= A⇢B1B2....Bk) {
14. pop A from stack;
// B1 on top of stack at final of this step
15. push Bk,Bk-1......B1 on stack
16. }
17. else if (PT[A,r] = error())
18. error();
19. }
20. }
// if parser terminate without error()
// then given string can generated by given parsing table.
时间复杂度
众所周知,语言的文法大小是有限的。例如,有限数量的变量、终端和产生式。如果文法是有限的,那么它的 LL(1) 解析表也是大小为 O(V*T) 的有限。让
- p是所有产生式的 RHS 中字符串的最大长度,并且
- l是给定字符串的长度,
l比p大很多。 if算法第 4 行的块总是运行 O(1) 时间。 else if算法中第 12 行的块将O(|P|*p)作为单个下一个输入符号的上限。 while 循环可以运行超过l次,但我们已经考虑了O(|P|*p) 中单个下一个输入符号的重复while 循环。所以总的时间复杂度为
T(l) = O(l)*O(|P|*p)
= O(l*|P|*p)
= O(l) { as l >>>|P|*p }
该算法的时间复杂度是输入字符串的长度顺序。
与上下文无关语言(CFL)的比较:
LL(1) 语法中的语言是 CFL 的适当子集。使用 CYK 算法,我们可以找到给定上下文无关文法 (CFG) 的字符串的成员资格。 CYK 花费O(l 3 )时间进行 CFG 的成员资格测试。但是对于 LL(1) 语法,我们可以使用上述算法在O(l)时间内进行成员资格测试,这是线性的。如果我们知道给定的 CFG 是 LL(1) 语法,那么使用 LL(1) 解析器进行解析而不是 CYK 算法。
例子 –
令文法G = (V, T, S’, P ) 为
S' → S$
S → xYzS | a
Y → xYz | y
此语法的解析表( PT )
a | x | y | z | $ | |
S’ | S’ → S$ | S’ → S$ | error | error | error |
S | S → a | S → xYzS | error | error | error |
Y | error | Y → xYz | Y → y | error | error |
Let string1 = xxyzza,
我们必须用这个字符串添加$ ,
我们将使用上面的解析算法,流程示意图:
对于string1,我们得到了一个空栈,while 循环或算法没有错误地终止。因此, string1属于给定语法G 的语言。
Let string2 = xxyzzz,
和上面一样,我们将使用算法来解析string2,这是图
对于string2 ,在上图的最后阶段,堆栈的顶部是S并且字符串的下一个输入符号是z ,但在PT[S,z] = error 中。算法因错误而终止。因此, string2不在语法G的语言中。