我试图理解LL解析算法中最左边的派生。这个link从生成视角解释了它。即它显示了如何遵循最左边的派生以从一组规则生成特定的标记序列。
但我正在考虑相反的方向。给定一个令牌流和一组语法规则,如何找到 正确的步骤以通过最左边的推导应用一组规则?
让我们继续使用上述链接中的以下语法:
给定的令牌序列是:1 2 3
一种方法是:
1 2 3
-> D D D
-> N D D (rewrite the *left-most* D to N according to the rule N->D.)
-> N D (rewrite the *left-most* N D to N according to the rule N->N D.)
-> N (same as above.)
但是还有其他方法可以应用语法规则:
1 2 3 - > D D D - > N D D - > N N D - > N N N
OR
1 2 3 - > D D D - > N D D - > N N D - > N N
但只有第一个推导最终会出现在一个非终端中。
随着令牌序列长度的增加,可以有更多方法。我认为推断出正确的推导步骤,需要2个先决条件:
在给出这两个之后,找到导出步骤的算法是什么?我们是否必须将最终结果设为单非终端?
答案 0 :(得分:1)
LL解析的一般过程包括:
预测堆栈顶部语法符号的生成(如果该符号是非终端),并将该符号替换为生产的右侧。
使用下一个输入符号匹配堆栈顶部语法符号,同时丢弃它们。
匹配操作没有问题,但预测可能需要一个oracle。然而,出于解释的目的,只要其起作用,进行预测的机制是无关紧要的。例如,对于某个小整数k
,每个可能的k
输入符号序列只与最多一个可能的生成一致,在这种情况下,您可以使用查找表。在这种情况下,我们说语法是LL(k)
。但你可以使用任何机制,包括魔法。只需要预测总是准确的。
在此算法的任何步骤中,部分派生的字符串是附加堆栈的消耗输入。最初没有消耗输入,并且堆栈仅由起始符号组成,因此部分派生的字符串(已应用0个派生)。由于消耗的输入仅由终端组成,并且算法只修改了堆栈的顶部(第一个)元素,因此很明显,部分派生的字符串系列构成了最左边的派生。
如果解析成功,将消耗整个输入并且堆栈将为空,因此解析将导致从起始符号输入的最左侧。
以下是您的示例的完整解析:
Consumed Unconsumed Partial Production
Input Stack input derivation or other action
-------- ----- ---------- ---------- ---------------
N 1 2 3 N N → N D
N D 1 2 3 N D N → N D
N D D 1 2 3 N D D N → D
D D D 1 2 3 D D D D → 1
1 D D 1 2 3 1 D D -- match --
1 D D 2 3 1 D D D → 2
1 2 D 2 3 1 2 D -- match --
1 2 D 3 1 2 D D → 3
1 2 3 3 1 2 3 -- match --
1 2 3 -- -- 1 2 3 -- success --
如果您阅读了最后两列,则可以看到从N
开始到1 2 3
结束的派生过程。在此示例中,只能使用魔法进行预测,因为任何 k 的规则N → N D
不是LL( k );使用右递归规则N → D N
将允许LL(2)决策过程(例如,如果至少有两个未消耗的输入令牌,则使用N → D N
;否则N → D
“。 )
您尝试制作的图表(以1 2 3
开头,以N
结尾)是自下而上解析。使用LR算法的自下而上解析对应于最右边的派生,但是需要向后读取派生,因为它以起始符号结束。