问题描述
假设我有语法
S -> a b | a T
T -> a
很明显,语法接受{aa,ab}。但是我很困惑LR解析如何解析单词“ a b”。天真的,它可以这样工作,方法是将第一个“ a”减少为T,然后卡住或不得不回溯。
a a => T a => stuck or backtrack
LR解析器如何知道它不应该将第一个“ a”减少为T?
解决方法
在这种情况下,解析器将永远不会尝试减少T
,因为生产T → a
并未处于从S
到达的任何状态。初始状态包含以下项目:
S → • a b
S → • a T
,在此状态下,唯一可能的动作是带有标记a
的移位动作。由于a
实际上是下一个输入字符,因此我们进行了转移到项目集为
S → a • b
S → a • T
T → • a
此状态也没有减少动作,它有两个不同的移位动作:一个在b
上,另一个在a
上。由于下一个输入是b
,因此将执行该移位操作,从而导致项目集为
S → a b •
其中只有一个减少项。
一个更有趣的情况是语法非常相似
S → a b
S → T a
T → a
在此,初始状态的项目集确实包含T
的生产:
S → • a b
S → • T a
T → • a
在初始状态下唯一可用的动作仍然是移a
,但现在进行移位之后,我们发现自己处于项集为的状态:
S → a • b
T → a •
,现在我们有两个可能的操作:b
的移位和T → a
的减小。在这里,解析器需要使用其功能来展望未来的令牌(假设它是LR(1)
解析器)。
但是要这样做,我们需要做一些小的调整。在构造解析自动机之前,始终对语法进行“增强”。增强语法通过添加唯一的输入结束字符(也可以参与超前检查)来添加对输入结束的显式识别。增强语法为:
S'→ S $
S → a b
S → T a
T → a
在此语法中,我们可以看到非终结符T
后面只能加上符号a
,并且此事实被编码到状态转换表中,其中项集中的每个项实际上是带有一组可能的先行注释。 (在表的构建过程中,所有项目都带有注释,但是在计算法律诉讼时,仅考虑减少的先行;减少是•末尾的项目。)
使用此修改,移a
后到达的项目集为:
S → a • b
T → a • [ lookahead: { a } ]
和前瞻性使您完全清楚应选择哪个操作。如果下一个输入是b
,则将其移位。如果下一个输入是a
,则会减少T
。
这种精度在LR(0)解析中不可用,其中不使用超前。正是由于您提到的原因,修改后的语法不是LR(0):它无法决定是否减少T
。这个问题经常出现,这就是为什么LR(0)在实践中很少有用的原因。
给出示例输入“ a b” ...读取“ a”后,解析器处于以下各项的状态:
S -> a . b
S -> a . T
T -> . a
由于这些项目在生产的最后都没有点号,因此该状态不要求执行任何减少动作。唯一可用的操作是移位,因此解析器将读取下一个符号。无需提前。