我想解析一组表达式:R[3]C
,R[2]C
,R[3]C-R[2]C
......我无法解决冲突......
以下是lexer.mll
的一部分:
rule token = parse
| 'R' { R }
| 'C' { C }
| "RC" { RC }
| ['0'-'9']+ as lxm { INTEGER (int_of_string lxm) }
| '+' { PLUS }
| '-' { MINUS }
| '[' { LBRACKET }
| ']' { RBRACKET }
| eof { EOF }
...
parser.mly
的一部分:
main:
e_expression EOF { $1 };
e_expression:
| ec = e_cell { EE_rc (Rc.Cell ec) }
| e_expression MINUS e_expression { EE_string_EEL ("MINUS", [$1; $3]) }
e_cell:
| R LBRACKET r = index RBRACKET C c = index { (Rc.I_relative r, Rc.I_absolute c) }
| R LBRACKET r = index RBRACKET C { (Rc.I_relative r, Rc.I_relative 0) }
index:
| INTEGER { $1 }
| MINUS INTEGER { Printf.printf "%n\n" 8; 0 - $2 }
此代码奇怪地不适用于R[3]C-R[2]C
,这里是parser.conflicts,我无法理解。
如果我对| R LBRACKET r = index RBRACKET C c = index ...
中的第e_cell
行注释,则代码可以解析R[3]C-R[2]C
,其中3
和2
为index
, `R[3]C
和R[2]C
为e_cell
,R[3]C-R[2]C
为e_expression
。
有人可以帮忙吗?
答案 0 :(得分:1)
你的语法不是LALR(1)
。事实上,它甚至不是LR(1)
。
考虑以下两个有效的e_expression
:
R[1]C-R[2]C
R[1]C-1-R[2]C
在第一种情况下,在我们转移C
后,我们将达到以下条件:
R [ index ] C -R[2]C
然后我们希望它减少:
e_cell -R[2]C
再次减少到
e_expression -R[2]C
然后
e_expression - e_expression
在第二种情况下,我们将:
R [ index ] C -1-R[2]C
然后
R [ index ] C - 1-R[2]C
R [ index ] C index -R[2]C
e_cell -R[2]C
(此时,我们已经达到了与第一个输入类似的位置,所以我将省略下一步。)
因此,在我们转移C
之后,前瞻是-
,我们需要:
将R [ index ] C
缩减为e_cell
或
转移-
,提供R [ index ] C -
如果没有更多的前瞻,我们无法分辨:以下令牌必须是R
(案例1)或INTEGER
(案例2)。
所以我们可以说语法是LALR(2),除了相对于减号存在另一个shift-reduce冲突,这使得语法模糊不清,因此对于任何k都不是LALR(k)。你可能已经使用运算符优先级声明来处理这个,但是以防万一:
假设您已达到:
e_expression - e_expression
,前瞻是-
。现在它可以将e_expression - e_expression
缩减为e_expression
,然后移动-
,从而产生:
e_expression -
或者它可以简单地移动-
:
e_expression - e_expression -
无论我们阅读多少前向上下文,都无法在这两者之间做出决定,因为它们都会导致有效的解析。第一个解析将使-
为左关联,第二个解析为右关联。
如果您没有使用优先级声明解决此问题,则可以选择以下其中一项,而不是e_expression: e_expression MINUS e_expression
:
e_expression: e_cell MINUS e_expression
e_expression: e_expression MINUS e_cell
现在,如何解决原来的问题:)
最简单的解决方案是,如果-
中的-1
只能被视为负整数的一部分,那就是让词法分析器处理它。然后解析器在MINUS
中看不到R[-1]C-1
,因此它不会尝试减少R[-1]C
。
另一个解决方案是使用GLR解析器(显然有一个用于OCaml,但我对此一无所知)。
最后,有可能在给定LR(2)语法的情况下机械地创建LR(1)语法,以及提取原始分析树的机制。由此产生的语法通常是臃肿和手工编写的痛苦,但翻译可以自动完成。不幸的是,我不知道有任何OCaml工具可以做到这一点。基本思想是将每个非终端分成一组对,成为新的非终端。您可以轻松地将所有现有规则扩展到新的非终结符集中。现在,由于每个非终结符实际上都包含一个先行标记,因此单标记预测相当于原始语言中的双标记预测。
答案 1 :(得分:0)
所以问题似乎是当它在]之后看到“ - ”标记时,解析器不确定它是在制作索引,还是在分隔两个表达式。
即。当解析器到达R [3] C-时,它不确定是否需要等待INTEGER来完成e_cell并减少,或者现在减少并开始处理另一个e_expression。
解决这个问题的最佳方法可能是将负整数代码移动到词法分析器中。我没有方便的ocamllex安装,但我认为正在改变
['0'-'9']+
到
'-'? ['0'-'9']+
可以工作,然后从索引中删除负整数大小写(显然这会导致Printf语句出现问题,但是你可以使内部逻辑更复杂以解决这个问题。