先决条件 – 自顶向下解析器的分类
预测解析是递归下降解析的一种特殊形式,不需要回溯,因此可以预测使用哪些乘积来替换输入字符串。
非递归预测解析或表驱动也称为 LL(1) 解析器。此解析器遵循最左派生 (LMD)。
LL(1):
here, first L is for Left to Right scanning of inputs,
the second L is for left most derivation procedure,
1 = Number of Look Ahead Symbols
预测解析期间的主要问题是确定要应用于非终结符的产生式。
这个非递归解析器在解析表中查找要应用的乘积。 LL(1) 解析器具有以下组件:
(1) buffer: an input buffer which contains the string to be passed
(2) stack: a pushdown stack which contains a sequence of grammar symbols
(3) A parsing table: a 2d array M[A, a]
where
A->non-terminal, a->terminal or $
(4) output stream:
end of the stack and an end of the input symbols are both denoted with $
非递归预测解析算法:
主要概念 -> 借助 FIRST() 和 FOLLOW() 集,可以仅使用避免递归调用的堆栈来完成此解析。
对于每个规则,语法 G 中的 A->x:
- 对于包含在 FIRST(A) 中的每个终端 ‘a’,如果 x 派生 ‘a’ 作为第一个符号,则将 A->x 添加到解析表中的 M[A, a]。
- 如果 FIRST(A) 包含 FOLLOW(A) 中每个终端 ‘b’ 的空产生式,则将此产生式 (A->null) 添加到解析表中的 M[A, b]。
程序,流程:
- 一开始,下推栈保存着文法 G 的起始符号。
- 在每一步,一个符号 X 从堆栈中弹出:
如果 X 是一个终端,那么它与前瞻匹配并且前瞻前进一步,
如果 X 是非终结符,则使用前瞻和解析表(实现 FIRST 集)选择产生式并将其右侧推入堆栈。 - 此过程重复进行,直到堆栈和输入字符串变为空(空)。
表驱动解析算法:
输入:字符串w 和 G 的解析表 M。
tos 栈顶 Stack[tos++] <-$ Stack[tos++] <-Start Symbol token <-next_token() X <-Stack[tos] 如果 X 是终端或 $ then if X = token 然后弹出 X token是 token() 的下一个 else error() else /* X 是非终结符 */ 如果 M[x, token] = X -> y1y2…yk then pop x push else error() X Stack[tos] 直到 X = $
// 非递归解析器模型图:
所以根据给定的图非递归解析算法。
输入:输入字符串’w’ 和语法 G 的解析表(’M’)。
输出:如果 w 在 L(G) 中,则 w 的 LMD;否则错误指示。
设置输入指针指向字符串$的第一个符号;重复设X为栈指针指向的符号, a为输入指针所指向的符号;如果 X 是终端或 $ 那么如果 X=a 然后从堆栈中弹出 X 并增加输入指针; else error() end if else /*if X 是非终结符 */ if 然后开始从堆栈中弹出 X;推进入堆栈,Y1 在顶部;输出生产 end else error() end if end if until X=$ /* 栈为空 */
示例:考虑随后的 LL(1) 语法:
S -> A
S -> ( S * A)
A -> id
现在让我们解析给定的输入:
( id * id )
解析表:
- row-> 对于每个非终结符,
- column-> 用于每个终端(包括特殊终端)。
此表的每个单元格最多包含给定语法的一条规则:
现在让我们看看使用算法,解析器如何使用这个解析表来处理给定的输入。
程序:
解析器因此结束,因为它的堆栈和输入流上都只剩下“$”。在这种情况下,解析器报告它已接受输入字符串并将以下规则列表写入输出流:
S -> ( S * A),
S -> A,
A -> id,
A -> id
这确实是输入字符串的 LMD 规则列表,即:
S -> ( S * A ) -> ( A * A ) -> ( id * A ) -> ( id * id )