前提条件— LL(1)解析表的构造。
LL(1)解析是编译器设计的语法分析阶段中的自上而下的解析方法。 LL(1)解析所需的组件是输入字符串,堆栈,给定语法的解析表和解析器。在这里,我们讨论一个解析器,该解析器确定是否可以从给定的语法(或解析表)生成给定的字符串。
让给定的语法是G =(V,T,S,P)
其中V变量符号集,T端子符号集,S-起始符号,P-生产集。
LL(1)解析器算法:
输入-1 . stack = S //堆栈最初仅包含S。
2.输入字符串= w $
其中S是语法的开始符号,w是给定的字符串,$是字符串。
3. PT是矩阵或2D数组形式的给定语法的解析表。
输出-确定给定的字符串可以由给定的语法(解析表)生成,否则,将产生错误。
脚步:
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)时间。否则,如果算法中第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的语言。