我试图解析正负小数。
number(N) ::= pnumber(N1).
number(N) ::= nnumber(N1).
number(N) ::= pnumber(N1) DOT pnumber(N2).
number(N) ::= nnumber(N1) DOT pnumber(N2).
pnumber(N) ::= NUMBER(N1).
nnumber(N) ::= MINUS NUMBER(N1).
包含前两个规则会产生转换/减少冲突,但我不知道如何编写语法以便不会发生冲突。 我正在使用Lemon解析器。
编辑:来自.out文件的冲突
State 79:
(56) number ::= nnumber *
number ::= nnumber * DOT pnumber
DOT shift 39
DOT reduce 56 ** Parsing conflict **
{default} reduce 56 number ::= nnumber
State 80:
(55) number ::= pnumber *
number ::= pnumber * DOT pnumber
DOT shift 40
DOT reduce 55 ** Parsing conflict **
{default} reduce 55 number ::= pnumber
State 39:
number ::= nnumber DOT * pnumber
pnumber ::= * NUMBER
NUMBER shift-reduce 59 pnumber ::= NUMBER
pnumber shift-reduce 58 number ::= nnumber DOT pnumber
State 40:
number ::= pnumber DOT * pnumber
pnumber ::= * NUMBER
NUMBER shift-reduce 59 pnumber ::= NUMBER
pnumber shift-reduce 57 number ::= pnumber DOT pnumber
编辑2:导致问题的最小语法
start ::= prog.
prog ::= rule.
rule ::= REVERSE_IMPLICATION body DOT.
body ::= bodydef.
body ::= body CONJUNCTION bodydef.
bodydef ::= literal.
literal ::= variable.
variable ::= number.
number ::= pnumber.
number ::= nnumber.
number ::= pnumber DOT pnumber.
number ::= nnumber DOT pnumber.
pnumber ::= NUMBER.
nnumber ::= MINUS NUMBER.
答案 0 :(得分:2)
您显示的冲突表明number
非终端使用的方式存在问题,而不是number
本身。
基本问题是,在看到pnumber
或nnumber
后,当前瞻的下一个标记是DOT
时,它无法决定是否应该结束number
{减少,因此DOT
是 number
之后的其他非终端的一部分,或者DOT
应该是作为number
的一部分处理(移位以便稍后可以减少一个p / nnumber DOT pnumber规则。)
因此,为了诊断问题,您需要在右侧的任何地方显示使用number
的所有规则(并且递归地使用任何其他使用这些规则的规则'右边的非终端。)
请注意,仅发布一个语法片段很少有用,因为LR解析器构建过程在很大程度上取决于语法中其他地方使用规则的上下文...
因此,问题在于您需要使用双令牌前瞻来区分(真实)DOT
number
中的literal
和结尾处的DOT
rule
。
简单的解决方法是让词法分析器处理它 - 词法分析器可以很容易地进行少量前瞻,因此你可以将REAL_NUMBER
识别为NUMBER
的一个独特的非终端(可能仍然如果没有-
,那么您最终会以
number ::= NUMBER | MINUS NUMBER | REAL_NUMBER | MINUS REAL_NUMBER
通过分解语法来消除冲突要困难得多,但可以做到。
通常,要重构语法以消除先行冲突,您需要找出表明冲突的规则(此处为rule
和number
)并重构事物以将它们组合成规则具有共同的前缀,直到你足够消除歧义。
首先,我假设除了number
之外还有其他规则可以出现在这里,否则我们就可以消除所有干预规则。
variable ::= number | name
我们希望移动number
规则" up"在语法中将rule
与DOT
放在同一个地方。所以当我们以number
结尾时,我们需要将包含规则拆分为特殊情况。我们添加后缀以表示与原始规则对应的规则,所有版本以number
分割结束
variable ::= number | variable_n
variable_n ::= name
...并传播" up"
literal ::= number | literal_n
literal_n ::= variable_n
......再次
bodydef ::= number | bodydef_n
bodydef_n := literal_n
......再次
body ::= number | body_n
body := body CONJUNCTION number
body_n ::= bodydef_n
body_n ::= body CONJUNCTION bodydef_n
请注意,当你向上移动时,你需要拆分越来越多的规则,所以这个过程可能会夸大语法。但是,在您重构的rhs结尾处仅使用 的规则最终只需要_n
版本,因此您不一定要规则数量加倍。
......最后一步
rule ::= REVERSE_IMPLICATION body_n DOT
rule ::= REVERSE_IMPLICATION number DOT
rule ::= REVERSE_IMPLICATION body CONJUNCTION number DOT
现在你在所有相同的地方都有DOT,所以展开number
规则:
rule ::= REVERSE_IMPLICATION body_n DOT
rule ::= REVERSE_IMPLICATION integer DOT
rule ::= REVERSE_IMPLICATION integer DOT pnumber DOT
rule ::= REVERSE_IMPLICATION body CONJUNCTION integer DOT
rule ::= REVERSE_IMPLICATION body CONJUNCTION integer DOT pnumber DOT
并且shift-reduce冲突消失了,因为规则具有共同的前缀,直到超过所需的前瞻以确定使用哪个。 我通过添加
减少了最终扩展中的规则数量integer ::= pnumber | nnumber
答案 1 :(得分:0)
您必须声明DOT
运算符令牌与%left
或%right
的关联性。
或者,另一个想法是放弃这种中间减少。语法中的一个显而易见的特征是数字增长DOT
后跟一个数字。这可以通过一条规则来捕获:
number : number DOT NUMBER
后跟DOT
后跟NUMBER
令牌的数字仍然是一个数字。
此规则并不要求DOT
声明关联性,因为没有歧义;规则纯粹是左递归的,DOT
的右手是终端令牌。当状态机此时,解析器必须将堆栈顶部减少到number
,然后转换DOT
:
number : number DOT NUMBER
您在这里解析的语言是常规的;它可以通过正则表达式进行解析而无需任何递归。这就是为什么规则中既包含左右递归又需要声明关联性的规则,这些规则在某种程度上是一个很大的锤子"。