我有一个像这样的规则的语法
A -> pq
B -> pr
Block -> { Astar Bstar }
Astar -> Astar A
| epsilon
Bstar -> Bstar B
| epsilon
有没有办法把这个语法变成LALR(1)?根据我的结论,如果解析器在块内看到p
,则会发生转换/引出冲突。
答案 0 :(得分:3)
您的语言是常规语言,相当于正则表达式\{(pq)*(pr)*\}
。问题是,任何简单的语法转换都需要两个字符的前瞻,才能看到q
之后是r
还是p
现在您已将此标记为yacc
和parsing
,因此不清楚您是否正在寻找“如何使用yacc处理此问题”或“理论答案”的实际答案“是否存在LALR(1)这种语言的语法。“
对于实际答案,解决方案就是你试图 - 将A
和B
的识别移动到词法分析器中,在那里识别字符序列pq
和{{1 }}作为标记pr
和A
。由于lex / flex使用DFA并回溯到最长匹配的令牌,因此这里的任意前瞻都没有问题。
对于理论答案,您需要转换语法以消除前瞻性的需要。有问题的构造是B
(大括号无关紧要),相当于(pq)*(pr)*
,它表示如下语法:
p(qp)*(rp)*r | p(qp)*q | epsilon
另一种方法是重构Block -> { p Astar Bstar r }
| { p Astar q }
| { }
A -> q p
B -> r p
Astar -> Astar A | epsilon
Bstar -> Bstar B | epsilon
规则,使它们与空字符串不匹配:
star
为同一种语言提供两种非常不同的LALR(1)语法。
答案 1 :(得分:2)
让我们首先看看你为什么会遇到LALR(1)冲突,然后看看我们是否可以重写语法使其成为LALR(1)。
要了解为什么语法不是LALR(1),让我们从计算语法的LR(1)配置集开始:
(1)
S' -> .Block [$]
Block -> .{ Astar Bstar } [$]
(2)
S' -> Block. [$]
(3)
Block -> { . Astar Bstar } [$]
Astar -> .Astar A [ }p ]
Astar -> .epsilon [ }p ]
(4)
Block -> { Astar . Bstar } [$]
Astar -> Astar .A [ }p]
A -> .pq [}p]
Bstar -> .epsilon [ }p ]
Bstar -> . Bstar B [ }p ]
此时,我们可以停止,因为我们在符号p上的状态(4)中有一个移位/缩小的混淆:你是否移动A -> .pq [ {p ]
的p,还是减少BStar -> .epsilon [ }p ]
?由于LR(1)语法中存在移位/减少冲突,因此语法根本不是LR(1),这意味着它不可能是LALR(1)(因为每个LALR(1)语法也是LR (1)语法)。
从根本上说,问题是当解析器看到p
时,它无法判断它是否正在查看A
的开头(意味着它需要移动它)或者是否存在不再是A
了,它正在查看B
的开头(意味着它需要减少Bstar -> epsilon
)。
要解决这个问题,让我们看看如果我们进行一些小调整会发生什么。我们遇到的问题是解析器需要在看到p
是否要移位或减少时立即确定。如果我们通过查看p
然后跟进后续角色给予时间推迟决定,该怎么办?要做到这一点,让我们通过重写
Bstar -> epsilon
Bstar -> Bstar B
作为
Bstar -> epsilon
Bstar -> B Bstar
现在,解析器在决定做什么之前会查看更多令牌。如果它正在查看pq
,它知道它没有查看与B相关的任何内容。如果它看到pr
,它就知道它在看B,因此可以开始进行第二种类型的制作。如果我们这样做,让我们看看LR(1)州会发生什么:
(1)
S' -> .Block [$]
Block -> .{ Astar Bstar } [$]
(2)
S' -> Block. [$]
(3)
Block -> { . Astar Bstar } [$]
Astar -> .Astar A [ }p ]
Astar -> .epsilon [ }p ]
(4)
Block -> { Astar . Bstar } [$]
Astar -> Astar .A [ }p]
A -> .pq [}p]
Bstar -> .epsilon [ } ]
Bstar -> . B Bstar [ } ]
B -> .pr [}]
(5)
Block -> { Astar Bstar . } [$]
(6)
Block -> { Astar Bstar } . [$]
(7)
A -> p.q [}p]
B -> p.r [}]
(8)
A -> .pq [}p]
(9)
B -> pr. [}]
(10)
Bstar -> B . Bstar [ } ]
Bstar -> . B Bstar [ } ]
B -> .pr [}]
(11)
B -> p.r [}]
请注意,我们原来的shift / reduce冲突已经消失,这个新语法根本不再有任何转移/减少冲突。此外,由于没有任何具有相同核心的状态对,上述状态集也是我们在LALR(1)表中将具有的状态集。因此,上面的语法确实是LALR(1),我们根本没有改变语法的含义。
希望这有帮助!